@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,125 @@
1
+ import { OpenClawConfig } from 'openclaw/plugin-sdk';
2
+ import { MiServiceConfig } from './service.js';
3
+ import './mi/mina.js';
4
+ import './mi/typing.js';
5
+ import './mi/miot.js';
6
+
7
+ /**
8
+ * 小米音箱 Channel 配置
9
+ */
10
+ interface MiGPTConfig extends MiServiceConfig {
11
+ /** 启用频道 */
12
+ enabled?: boolean;
13
+ /** 默认账户 ID */
14
+ defaultAccount?: string;
15
+ /** 设备访问策略:pairing/allowlist/open */
16
+ devicePolicy?: 'pairing' | 'allowlist' | 'open';
17
+ /** 允许的设备名称白名单 */
18
+ allowFrom?: Array<string | number>;
19
+ /** 启用流式 TTS */
20
+ streaming?: boolean;
21
+ /** TTS 文本分块长度(汉字) */
22
+ textChunkLimit?: number;
23
+ /** TTS 语速(0.5-2.0) */
24
+ ttsSpeed?: number;
25
+ /** 默认音量(6-100) */
26
+ volume?: number;
27
+ /** 消息轮询间隔(毫秒) */
28
+ heartbeat?: number;
29
+ /** 设备名称列表 */
30
+ devices?: string[];
31
+ /** 账户配置 */
32
+ accounts?: Record<string, MiGPTAccountConfig>;
33
+ /** 音箱控制方式:mina/miot */
34
+ speakerControl?: 'mina' | 'miot';
35
+ /** 系统提示词:用于定制 AI 在音箱场景下的行为规范 */
36
+ systemPrompt?: string;
37
+ /** 启动时是否播报上线文案 */
38
+ announceOnStart?: boolean;
39
+ /** 上线播报文案 */
40
+ startupMessage?: string;
41
+ /** 收到消息时是否回复收到 */
42
+ acknowledgeOnReceive?: boolean;
43
+ /** 收到消息回复文案 */
44
+ receiveMessage?: string;
45
+ }
46
+ /**
47
+ * 账户配置
48
+ */
49
+ interface MiGPTAccountConfig extends MiServiceConfig {
50
+ /** 启用账户 */
51
+ enabled?: boolean;
52
+ /** 账户名称 */
53
+ name?: string;
54
+ /** 设备名称列表 */
55
+ devices?: string[];
56
+ /** 系统提示词:用于定制 AI 在音箱场景下的行为规范 */
57
+ systemPrompt?: string;
58
+ /** 启动时是否播报上线文案 */
59
+ announceOnStart?: boolean;
60
+ /** 上线播报文案 */
61
+ startupMessage?: string;
62
+ /** 收到消息时是否回复收到 */
63
+ acknowledgeOnReceive?: boolean;
64
+ /** 收到消息回复文案 */
65
+ receiveMessage?: string;
66
+ }
67
+ /**
68
+ * 解析后的账户信息
69
+ */
70
+ interface ResolvedMiAccount {
71
+ /** 账户 ID */
72
+ accountId: string;
73
+ /** 启用状态 */
74
+ enabled: boolean;
75
+ /** 是否已配置 */
76
+ configured: boolean;
77
+ /** 账户名称 */
78
+ name?: string;
79
+ /** 设备列表 */
80
+ devices: string[];
81
+ /** 配置详情 */
82
+ config: MiGPTAccountConfig;
83
+ }
84
+ /**
85
+ * OpenClaw 配置类型扩展
86
+ */
87
+ interface ExtendedOpenClawConfig extends OpenClawConfig {
88
+ channels?: {
89
+ migpt?: MiGPTConfig;
90
+ };
91
+ }
92
+ /**
93
+ * 列出所有账户 ID
94
+ */
95
+ declare function listMiAccountIds(cfg: ExtendedOpenClawConfig): string[];
96
+ /**
97
+ * 解析账户配置
98
+ */
99
+ declare function resolveMiAccount(cfg: ExtendedOpenClawConfig, accountId?: string): ResolvedMiAccount;
100
+ /**
101
+ * 获取默认账户 ID
102
+ */
103
+ declare function resolveDefaultMiAccountId(cfg: ExtendedOpenClawConfig): string;
104
+ /**
105
+ * 应用账户配置
106
+ */
107
+ declare function applyMiAccountConfig(cfg: ExtendedOpenClawConfig, accountId: string, updates: Partial<MiGPTAccountConfig>): ExtendedOpenClawConfig;
108
+ /**
109
+ * 设置账户启用状态
110
+ */
111
+ declare function setMiAccountEnabled(cfg: ExtendedOpenClawConfig, accountId: string, enabled: boolean): ExtendedOpenClawConfig;
112
+ /**
113
+ * 删除账户
114
+ */
115
+ declare function deleteMiAccount(cfg: ExtendedOpenClawConfig, accountId: string): ExtendedOpenClawConfig;
116
+ /**
117
+ * 解析允许的设备列表
118
+ */
119
+ declare function resolveMiAllowFrom(cfg: ExtendedOpenClawConfig, _accountId?: string): string[];
120
+ /**
121
+ * 格式化允许的设备列表
122
+ */
123
+ declare function formatMiAllowFrom(allowFrom: Array<string | number>): string[];
124
+
125
+ export { type ExtendedOpenClawConfig, type MiGPTAccountConfig, type MiGPTConfig, type ResolvedMiAccount, applyMiAccountConfig, deleteMiAccount, formatMiAllowFrom, listMiAccountIds, resolveDefaultMiAccountId, resolveMiAccount, resolveMiAllowFrom, setMiAccountEnabled };
@@ -0,0 +1,146 @@
1
+ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk";
2
+ function listMiAccountIds(cfg) {
3
+ const migptCfg = cfg.channels?.migpt;
4
+ if (!migptCfg?.accounts) {
5
+ return [DEFAULT_ACCOUNT_ID];
6
+ }
7
+ return [DEFAULT_ACCOUNT_ID, ...Object.keys(migptCfg.accounts)];
8
+ }
9
+ function resolveMiAccount(cfg, accountId) {
10
+ const id = accountId ?? DEFAULT_ACCOUNT_ID;
11
+ const migptCfg = cfg.channels?.migpt;
12
+ const isDefault = id === DEFAULT_ACCOUNT_ID;
13
+ const accountConfig = isDefault ? { ...migptCfg } : migptCfg?.accounts?.[id] ?? {};
14
+ const mergedConfig = {
15
+ enabled: isDefault ? migptCfg?.enabled : accountConfig.enabled,
16
+ userId: accountConfig.userId ?? migptCfg?.userId,
17
+ password: accountConfig.password ?? migptCfg?.password,
18
+ passToken: accountConfig.passToken ?? migptCfg?.passToken,
19
+ debug: accountConfig.debug ?? migptCfg?.debug,
20
+ timeout: accountConfig.timeout ?? migptCfg?.timeout,
21
+ devices: accountConfig.devices ?? migptCfg?.devices ?? [],
22
+ speakerControl: accountConfig.speakerControl ?? migptCfg?.speakerControl
23
+ };
24
+ const configured = !!(mergedConfig.userId && (mergedConfig.passToken || mergedConfig.password));
25
+ return {
26
+ accountId: id,
27
+ enabled: mergedConfig.enabled ?? false,
28
+ configured,
29
+ name: accountConfig.name ?? (isDefault ? "Default" : id),
30
+ devices: mergedConfig.devices ?? [],
31
+ config: mergedConfig
32
+ };
33
+ }
34
+ function resolveDefaultMiAccountId(cfg) {
35
+ return cfg.channels?.migpt?.defaultAccount ?? DEFAULT_ACCOUNT_ID;
36
+ }
37
+ function applyMiAccountConfig(cfg, accountId, updates) {
38
+ const isDefault = accountId === DEFAULT_ACCOUNT_ID;
39
+ const migptCfg = cfg.channels?.migpt ?? {};
40
+ if (isDefault) {
41
+ return {
42
+ ...cfg,
43
+ channels: {
44
+ ...cfg.channels,
45
+ migpt: {
46
+ ...migptCfg,
47
+ ...updates
48
+ }
49
+ }
50
+ };
51
+ }
52
+ return {
53
+ ...cfg,
54
+ channels: {
55
+ ...cfg.channels,
56
+ migpt: {
57
+ ...migptCfg,
58
+ accounts: {
59
+ ...migptCfg.accounts,
60
+ [accountId]: {
61
+ ...migptCfg.accounts?.[accountId],
62
+ ...updates
63
+ }
64
+ }
65
+ }
66
+ }
67
+ };
68
+ }
69
+ function setMiAccountEnabled(cfg, accountId, enabled) {
70
+ const isDefault = accountId === DEFAULT_ACCOUNT_ID;
71
+ const migptCfg = cfg.channels?.migpt ?? {};
72
+ if (isDefault) {
73
+ return {
74
+ ...cfg,
75
+ channels: {
76
+ ...cfg.channels,
77
+ migpt: {
78
+ ...migptCfg,
79
+ enabled
80
+ }
81
+ }
82
+ };
83
+ }
84
+ return {
85
+ ...cfg,
86
+ channels: {
87
+ ...cfg.channels,
88
+ migpt: {
89
+ ...migptCfg,
90
+ accounts: {
91
+ ...migptCfg.accounts,
92
+ [accountId]: {
93
+ ...migptCfg.accounts?.[accountId],
94
+ enabled
95
+ }
96
+ }
97
+ }
98
+ }
99
+ };
100
+ }
101
+ function deleteMiAccount(cfg, accountId) {
102
+ const isDefault = accountId === DEFAULT_ACCOUNT_ID;
103
+ const migptCfg = cfg.channels?.migpt;
104
+ if (isDefault) {
105
+ const next = { ...cfg };
106
+ const nextChannels = { ...cfg.channels };
107
+ delete nextChannels.migpt;
108
+ if (Object.keys(nextChannels).length > 0) {
109
+ next.channels = nextChannels;
110
+ } else {
111
+ delete next.channels;
112
+ }
113
+ return next;
114
+ }
115
+ const accounts = { ...migptCfg?.accounts };
116
+ delete accounts[accountId];
117
+ return {
118
+ ...cfg,
119
+ channels: {
120
+ ...cfg.channels,
121
+ migpt: {
122
+ ...migptCfg,
123
+ accounts: Object.keys(accounts).length > 0 ? accounts : void 0
124
+ }
125
+ }
126
+ };
127
+ }
128
+ function resolveMiAllowFrom(cfg, _accountId) {
129
+ const migptCfg = cfg.channels?.migpt;
130
+ const allowFrom = migptCfg?.allowFrom ?? [];
131
+ return allowFrom.map((entry) => String(entry).trim()).filter(Boolean);
132
+ }
133
+ function formatMiAllowFrom(allowFrom) {
134
+ return allowFrom.map((entry) => String(entry).trim()).filter(Boolean).map((entry) => entry.toLowerCase());
135
+ }
136
+ export {
137
+ applyMiAccountConfig,
138
+ deleteMiAccount,
139
+ formatMiAllowFrom,
140
+ listMiAccountIds,
141
+ resolveDefaultMiAccountId,
142
+ resolveMiAccount,
143
+ resolveMiAllowFrom,
144
+ setMiAccountEnabled
145
+ };
146
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/config.ts"],"sourcesContent":["import type { OpenClawConfig } from 'openclaw/plugin-sdk';\nimport type { MiServiceConfig } from './service.js';\nimport { DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk';\n\n/**\n * 小米音箱 Channel 配置\n */\nexport interface MiGPTConfig extends MiServiceConfig {\n /** 启用频道 */\n enabled?: boolean;\n /** 默认账户 ID */\n defaultAccount?: string;\n /** 设备访问策略:pairing/allowlist/open */\n devicePolicy?: 'pairing' | 'allowlist' | 'open';\n /** 允许的设备名称白名单 */\n allowFrom?: Array<string | number>;\n /** 启用流式 TTS */\n streaming?: boolean;\n /** TTS 文本分块长度(汉字) */\n textChunkLimit?: number;\n /** TTS 语速(0.5-2.0) */\n ttsSpeed?: number;\n /** 默认音量(6-100) */\n volume?: number;\n /** 消息轮询间隔(毫秒) */\n heartbeat?: number;\n /** 设备名称列表 */\n devices?: string[];\n /** 账户配置 */\n accounts?: Record<string, MiGPTAccountConfig>;\n /** 音箱控制方式:mina/miot */\n speakerControl?: 'mina' | 'miot';\n /** 系统提示词:用于定制 AI 在音箱场景下的行为规范 */\n systemPrompt?: string;\n /** 启动时是否播报上线文案 */\n announceOnStart?: boolean;\n /** 上线播报文案 */\n startupMessage?: string;\n /** 收到消息时是否回复收到 */\n acknowledgeOnReceive?: boolean;\n /** 收到消息回复文案 */\n receiveMessage?: string;\n}\n\n/**\n * 账户配置\n */\nexport interface MiGPTAccountConfig extends MiServiceConfig {\n /** 启用账户 */\n enabled?: boolean;\n /** 账户名称 */\n name?: string;\n /** 设备名称列表 */\n devices?: string[];\n /** 系统提示词:用于定制 AI 在音箱场景下的行为规范 */\n systemPrompt?: string;\n /** 启动时是否播报上线文案 */\n announceOnStart?: boolean;\n /** 上线播报文案 */\n startupMessage?: string;\n /** 收到消息时是否回复收到 */\n acknowledgeOnReceive?: boolean;\n /** 收到消息回复文案 */\n receiveMessage?: string;\n}\n\n/**\n * 解析后的账户信息\n */\nexport interface ResolvedMiAccount {\n /** 账户 ID */\n accountId: string;\n /** 启用状态 */\n enabled: boolean;\n /** 是否已配置 */\n configured: boolean;\n /** 账户名称 */\n name?: string;\n /** 设备列表 */\n devices: string[];\n /** 配置详情 */\n config: MiGPTAccountConfig;\n}\n\n/**\n * OpenClaw 配置类型扩展\n */\nexport interface ExtendedOpenClawConfig extends OpenClawConfig {\n channels?: {\n migpt?: MiGPTConfig;\n };\n}\n\n/**\n * 列出所有账户 ID\n */\nexport function listMiAccountIds(cfg: ExtendedOpenClawConfig): string[] {\n const migptCfg = cfg.channels?.migpt;\n if (!migptCfg?.accounts) {\n return [DEFAULT_ACCOUNT_ID];\n }\n return [DEFAULT_ACCOUNT_ID, ...Object.keys(migptCfg.accounts)];\n}\n\n/**\n * 解析账户配置\n */\nexport function resolveMiAccount(\n cfg: ExtendedOpenClawConfig,\n accountId?: string,\n): ResolvedMiAccount {\n const id = accountId ?? DEFAULT_ACCOUNT_ID;\n const migptCfg = cfg.channels?.migpt;\n const isDefault = id === DEFAULT_ACCOUNT_ID;\n\n // 获取账户特定配置\n const accountConfig = isDefault\n ? { ...migptCfg }\n : migptCfg?.accounts?.[id] ?? {};\n\n // 合并全局和账户特定配置\n const mergedConfig: MiGPTAccountConfig = {\n enabled: isDefault ? migptCfg?.enabled : accountConfig.enabled,\n userId: accountConfig.userId ?? migptCfg?.userId,\n password: accountConfig.password ?? migptCfg?.password,\n passToken: accountConfig.passToken ?? migptCfg?.passToken,\n debug: accountConfig.debug ?? migptCfg?.debug,\n timeout: accountConfig.timeout ?? migptCfg?.timeout,\n devices: accountConfig.devices ?? migptCfg?.devices ?? [],\n speakerControl: accountConfig.speakerControl ?? migptCfg?.speakerControl,\n };\n\n // 检查是否已配置\n const configured = !!(\n mergedConfig.userId &&\n (mergedConfig.passToken || mergedConfig.password)\n );\n\n return {\n accountId: id,\n enabled: mergedConfig.enabled ?? false,\n configured,\n name: accountConfig.name ?? (isDefault ? 'Default' : id),\n devices: mergedConfig.devices ?? [],\n config: mergedConfig,\n };\n}\n\n/**\n * 获取默认账户 ID\n */\nexport function resolveDefaultMiAccountId(cfg: ExtendedOpenClawConfig): string {\n return cfg.channels?.migpt?.defaultAccount ?? DEFAULT_ACCOUNT_ID;\n}\n\n/**\n * 应用账户配置\n */\nexport function applyMiAccountConfig(\n cfg: ExtendedOpenClawConfig,\n accountId: string,\n updates: Partial<MiGPTAccountConfig>,\n): ExtendedOpenClawConfig {\n const isDefault = accountId === DEFAULT_ACCOUNT_ID;\n const migptCfg = cfg.channels?.migpt ?? {};\n\n if (isDefault) {\n return {\n ...cfg,\n channels: {\n ...cfg.channels,\n migpt: {\n ...migptCfg,\n ...updates,\n },\n },\n };\n }\n\n return {\n ...cfg,\n channels: {\n ...cfg.channels,\n migpt: {\n ...migptCfg,\n accounts: {\n ...migptCfg.accounts,\n [accountId]: {\n ...migptCfg.accounts?.[accountId],\n ...updates,\n },\n },\n },\n },\n };\n}\n\n/**\n * 设置账户启用状态\n */\nexport function setMiAccountEnabled(\n cfg: ExtendedOpenClawConfig,\n accountId: string,\n enabled: boolean,\n): ExtendedOpenClawConfig {\n const isDefault = accountId === DEFAULT_ACCOUNT_ID;\n const migptCfg = cfg.channels?.migpt ?? {};\n\n if (isDefault) {\n return {\n ...cfg,\n channels: {\n ...cfg.channels,\n migpt: {\n ...migptCfg,\n enabled,\n },\n },\n };\n }\n\n return {\n ...cfg,\n channels: {\n ...cfg.channels,\n migpt: {\n ...migptCfg,\n accounts: {\n ...migptCfg.accounts,\n [accountId]: {\n ...migptCfg.accounts?.[accountId],\n enabled,\n },\n },\n },\n },\n };\n}\n\n/**\n * 删除账户\n */\nexport function deleteMiAccount(\n cfg: ExtendedOpenClawConfig,\n accountId: string,\n): ExtendedOpenClawConfig {\n const isDefault = accountId === DEFAULT_ACCOUNT_ID;\n const migptCfg = cfg.channels?.migpt;\n\n if (isDefault) {\n // 删除整个 migpt 配置\n const next = { ...cfg } as ExtendedOpenClawConfig;\n const nextChannels = { ...cfg.channels };\n delete (nextChannels as Record<string, unknown>).migpt;\n if (Object.keys(nextChannels).length > 0) {\n next.channels = nextChannels;\n } else {\n delete next.channels;\n }\n return next;\n }\n\n // 删除特定账户\n const accounts = { ...migptCfg?.accounts };\n delete accounts[accountId];\n\n return {\n ...cfg,\n channels: {\n ...cfg.channels,\n migpt: {\n ...migptCfg,\n accounts: Object.keys(accounts).length > 0 ? accounts : undefined,\n },\n },\n };\n}\n\n/**\n * 解析允许的设备列表\n */\nexport function resolveMiAllowFrom(\n cfg: ExtendedOpenClawConfig,\n _accountId?: string,\n): string[] {\n const migptCfg = cfg.channels?.migpt;\n const allowFrom = migptCfg?.allowFrom ?? [];\n return allowFrom.map((entry) => String(entry).trim()).filter(Boolean);\n}\n\n/**\n * 格式化允许的设备列表\n */\nexport function formatMiAllowFrom(allowFrom: Array<string | number>): string[] {\n return allowFrom\n .map((entry) => String(entry).trim())\n .filter(Boolean)\n .map((entry) => entry.toLowerCase());\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,51 @@
1
+ interface IMessage {
2
+ id: string;
3
+ sender: 'user';
4
+ text: string;
5
+ timestamp: number;
6
+ deviceId: string;
7
+ }
8
+ declare class _MiMessage {
9
+ private _lastQueryMsg;
10
+ private _tempQueryMsgs;
11
+ /**
12
+ * 获取下一条消息
13
+ * @param deviceId 设备 ID
14
+ */
15
+ fetchNextMessage(deviceId: string): Promise<IMessage | undefined>;
16
+ /**
17
+ * 拉取第一条消息(初始化)
18
+ */
19
+ private _fetchFirstMessage;
20
+ /**
21
+ * 拉取下一条消息
22
+ */
23
+ private _fetchNextMessage;
24
+ /**
25
+ * 拉取最新的 2 条消息,用于和上一条消息比对是否连续
26
+ */
27
+ private _fetchNext2Messages;
28
+ /**
29
+ * 继续向上拉取其他新消息
30
+ */
31
+ private _fetchNextRemainingMessages;
32
+ /**
33
+ * 读取暂存的消息
34
+ */
35
+ private _fetchNextTempMessage;
36
+ /**
37
+ * 拉取历史消息
38
+ */
39
+ private _fetchHistoryMsgs;
40
+ /**
41
+ * 清除设备消息缓存
42
+ */
43
+ clear(deviceId: string): void;
44
+ /**
45
+ * 清除所有缓存
46
+ */
47
+ clearAll(): void;
48
+ }
49
+ declare const MiMessage: _MiMessage;
50
+
51
+ export { type IMessage, MiMessage };
@@ -0,0 +1,145 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { MiService } from "./service.js";
3
+ import { firstOf, lastOf } from "./utils/parse.js";
4
+ class _MiMessage {
5
+ _lastQueryMsg = {};
6
+ _tempQueryMsgs = {};
7
+ /**
8
+ * 获取下一条消息
9
+ * @param deviceId 设备 ID
10
+ */
11
+ async fetchNextMessage(deviceId) {
12
+ if (!this._lastQueryMsg[deviceId]) {
13
+ return this._fetchFirstMessage(deviceId);
14
+ }
15
+ return this._fetchNextMessage(deviceId);
16
+ }
17
+ /**
18
+ * 拉取第一条消息(初始化)
19
+ */
20
+ async _fetchFirstMessage(deviceId) {
21
+ const msgs = await this._fetchHistoryMsgs(deviceId, {
22
+ limit: 1,
23
+ filterAnswer: false
24
+ });
25
+ this._lastQueryMsg[deviceId] = msgs[0];
26
+ return void 0;
27
+ }
28
+ /**
29
+ * 拉取下一条消息
30
+ */
31
+ async _fetchNextMessage(deviceId) {
32
+ if (this._tempQueryMsgs[deviceId] && this._tempQueryMsgs[deviceId].length > 0) {
33
+ return this._fetchNextTempMessage(deviceId);
34
+ }
35
+ const nextMsg = await this._fetchNext2Messages(deviceId);
36
+ if (nextMsg !== "continue") {
37
+ return nextMsg;
38
+ }
39
+ return this._fetchNextRemainingMessages(deviceId);
40
+ }
41
+ /**
42
+ * 拉取最新的 2 条消息,用于和上一条消息比对是否连续
43
+ */
44
+ async _fetchNext2Messages(deviceId) {
45
+ const msgs = await this._fetchHistoryMsgs(deviceId, { limit: 2 });
46
+ if (msgs.length < 1 || firstOf(msgs).timestamp <= this._lastQueryMsg[deviceId].timestamp) {
47
+ return;
48
+ }
49
+ if (firstOf(msgs).timestamp > this._lastQueryMsg[deviceId].timestamp && (msgs.length === 1 || lastOf(msgs).timestamp <= this._lastQueryMsg[deviceId].timestamp)) {
50
+ this._lastQueryMsg[deviceId] = firstOf(msgs);
51
+ return this._lastQueryMsg[deviceId];
52
+ }
53
+ for (const msg of msgs) {
54
+ if (msg.timestamp > this._lastQueryMsg[deviceId].timestamp) {
55
+ if (!this._tempQueryMsgs[deviceId]) {
56
+ this._tempQueryMsgs[deviceId] = [];
57
+ }
58
+ this._tempQueryMsgs[deviceId].push(msg);
59
+ }
60
+ }
61
+ return "continue";
62
+ }
63
+ /**
64
+ * 继续向上拉取其他新消息
65
+ */
66
+ async _fetchNextRemainingMessages(deviceId, options) {
67
+ let currentPage = 0;
68
+ const { maxPage = 3, pageSize = 10 } = options ?? {};
69
+ while (true) {
70
+ currentPage++;
71
+ if (currentPage > maxPage) {
72
+ return this._fetchNextTempMessage(deviceId);
73
+ }
74
+ const nextTimestamp = lastOf(this._tempQueryMsgs[deviceId]).timestamp;
75
+ const msgs = await this._fetchHistoryMsgs(deviceId, {
76
+ limit: pageSize,
77
+ timestamp: nextTimestamp
78
+ });
79
+ for (const msg of msgs) {
80
+ if (msg.timestamp >= nextTimestamp) {
81
+ } else if (msg.timestamp > this._lastQueryMsg[deviceId].timestamp) {
82
+ this._tempQueryMsgs[deviceId].push(msg);
83
+ } else {
84
+ return this._fetchNextTempMessage(deviceId);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ /**
90
+ * 读取暂存的消息
91
+ */
92
+ _fetchNextTempMessage(deviceId) {
93
+ const nextMsg = this._tempQueryMsgs[deviceId].pop();
94
+ if (nextMsg) {
95
+ this._lastQueryMsg[deviceId] = nextMsg;
96
+ }
97
+ return nextMsg;
98
+ }
99
+ /**
100
+ * 拉取历史消息
101
+ */
102
+ async _fetchHistoryMsgs(deviceId, options) {
103
+ const filterAnswer = options?.filterAnswer ?? true;
104
+ const conversation = await MiService.MiNA?.getConversations({
105
+ limit: options?.limit,
106
+ timestamp: options?.timestamp
107
+ });
108
+ let records = conversation?.records ?? [];
109
+ if (filterAnswer) {
110
+ records = records.filter(
111
+ (e) => ["TTS", "LLM"].includes(e.answers[0]?.type ?? "") && // 过滤 TTS 和 LLM 消息
112
+ e.answers.length === 1
113
+ // 播放音乐时会有 TTS、Audio 两个 Answer
114
+ );
115
+ }
116
+ return records.map((e) => {
117
+ return {
118
+ id: randomUUID(),
119
+ sender: "user",
120
+ text: e.query,
121
+ timestamp: e.time,
122
+ deviceId
123
+ };
124
+ });
125
+ }
126
+ /**
127
+ * 清除设备消息缓存
128
+ */
129
+ clear(deviceId) {
130
+ delete this._lastQueryMsg[deviceId];
131
+ delete this._tempQueryMsgs[deviceId];
132
+ }
133
+ /**
134
+ * 清除所有缓存
135
+ */
136
+ clearAll() {
137
+ this._lastQueryMsg = {};
138
+ this._tempQueryMsgs = {};
139
+ }
140
+ }
141
+ const MiMessage = new _MiMessage();
142
+ export {
143
+ MiMessage
144
+ };
145
+ //# sourceMappingURL=message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/message.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { MiService } from './service.js';\nimport { firstOf, lastOf } from './utils/parse.js';\n\nexport interface IMessage {\n id: string;\n sender: 'user';\n text: string;\n timestamp: number;\n deviceId: string;\n}\n\nclass _MiMessage {\n private _lastQueryMsg: Record<string, IMessage | undefined> = {};\n private _tempQueryMsgs: Record<string, IMessage[]> = {};\n\n /**\n * 获取下一条消息\n * @param deviceId 设备 ID\n */\n async fetchNextMessage(deviceId: string): Promise<IMessage | undefined> {\n if (!this._lastQueryMsg[deviceId]) {\n return this._fetchFirstMessage(deviceId);\n }\n return this._fetchNextMessage(deviceId);\n }\n\n /**\n * 拉取第一条消息(初始化)\n */\n private async _fetchFirstMessage(deviceId: string) {\n const msgs = await this._fetchHistoryMsgs(deviceId, {\n limit: 1,\n filterAnswer: false,\n });\n this._lastQueryMsg[deviceId] = msgs[0];\n return undefined;\n }\n\n /**\n * 拉取下一条消息\n */\n private async _fetchNextMessage(deviceId: string): Promise<IMessage | undefined> {\n if (this._tempQueryMsgs[deviceId] && this._tempQueryMsgs[deviceId].length > 0) {\n // 当前有暂存的新消息(从新到旧),依次处理之\n return this._fetchNextTempMessage(deviceId);\n }\n // 拉取最新的 2 条 msg(用于和上一条消息比对是否连续)\n const nextMsg = await this._fetchNext2Messages(deviceId);\n if (nextMsg !== 'continue') {\n return nextMsg;\n }\n // 继续向上拉取其他新消息\n return this._fetchNextRemainingMessages(deviceId);\n }\n\n /**\n * 拉取最新的 2 条消息,用于和上一条消息比对是否连续\n */\n private async _fetchNext2Messages(deviceId: string): Promise<IMessage | 'continue' | undefined> {\n const msgs = await this._fetchHistoryMsgs(deviceId, { limit: 2 });\n if (msgs.length < 1 || firstOf(msgs)!.timestamp <= this._lastQueryMsg[deviceId]!.timestamp) {\n // 没有拉到新消息\n return;\n }\n if (\n firstOf(msgs)!.timestamp > this._lastQueryMsg[deviceId]!.timestamp &&\n (msgs.length === 1 || lastOf(msgs)!.timestamp <= this._lastQueryMsg[deviceId]!.timestamp)\n ) {\n // 刚好收到一条新消息\n this._lastQueryMsg[deviceId] = firstOf(msgs);\n return this._lastQueryMsg[deviceId];\n }\n // 还有其他新消息,暂存当前的新消息\n for (const msg of msgs) {\n if (msg.timestamp > this._lastQueryMsg[deviceId]!.timestamp) {\n if (!this._tempQueryMsgs[deviceId]) {\n this._tempQueryMsgs[deviceId] = [];\n }\n this._tempQueryMsgs[deviceId].push(msg);\n }\n }\n return 'continue';\n }\n\n /**\n * 继续向上拉取其他新消息\n */\n private async _fetchNextRemainingMessages(deviceId: string, options?: {\n maxPage?: number;\n pageSize?: number;\n }) {\n let currentPage = 0;\n const { maxPage = 3, pageSize = 10 } = options ?? {};\n while (true) {\n currentPage++;\n if (currentPage > maxPage) {\n // 拉取新消息超长,取消拉取\n return this._fetchNextTempMessage(deviceId);\n }\n const nextTimestamp = lastOf(this._tempQueryMsgs[deviceId]!)!.timestamp;\n const msgs = await this._fetchHistoryMsgs(deviceId, {\n limit: pageSize,\n timestamp: nextTimestamp,\n });\n for (const msg of msgs) {\n if (msg.timestamp >= nextTimestamp) {\n // 忽略上一页的消息\n } else if (msg.timestamp > this._lastQueryMsg[deviceId]!.timestamp) {\n // 继续添加新消息\n this._tempQueryMsgs[deviceId].push(msg);\n } else {\n // 拉取到历史消息处\n return this._fetchNextTempMessage(deviceId);\n }\n }\n }\n }\n\n /**\n * 读取暂存的消息\n */\n private _fetchNextTempMessage(deviceId: string): IMessage | undefined {\n const nextMsg = this._tempQueryMsgs[deviceId].pop();\n if (nextMsg) {\n this._lastQueryMsg[deviceId] = nextMsg;\n }\n return nextMsg;\n }\n\n /**\n * 拉取历史消息\n */\n private async _fetchHistoryMsgs(\n deviceId: string,\n options?: {\n limit?: number;\n timestamp?: number;\n filterAnswer?: boolean;\n },\n ): Promise<IMessage[]> {\n const filterAnswer = options?.filterAnswer ?? true;\n const conversation = await MiService.MiNA?.getConversations({\n limit: options?.limit,\n timestamp: options?.timestamp,\n });\n let records = conversation?.records ?? [];\n \n if (filterAnswer) {\n // 过滤有小爱回答的消息\n records = records.filter(\n (e) =>\n ['TTS', 'LLM'].includes(e.answers[0]?.type ?? '') && // 过滤 TTS 和 LLM 消息\n e.answers.length === 1, // 播放音乐时会有 TTS、Audio 两个 Answer\n );\n }\n \n return records.map((e) => {\n return {\n id: randomUUID(),\n sender: 'user',\n text: e.query,\n timestamp: e.time,\n deviceId,\n };\n });\n }\n\n /**\n * 清除设备消息缓存\n */\n clear(deviceId: string) {\n delete this._lastQueryMsg[deviceId];\n delete this._tempQueryMsgs[deviceId];\n }\n\n /**\n * 清除所有缓存\n */\n clearAll() {\n this._lastQueryMsg = {};\n this._tempQueryMsgs = {};\n }\n}\n\nexport const MiMessage = new _MiMessage();\n"],"mappings":"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","names":[]}
@@ -0,0 +1,5 @@
1
+ import { MiAccount } from './typing.js';
2
+
3
+ declare function getAccount(_account: MiAccount): Promise<MiAccount | undefined>;
4
+
5
+ export { getAccount };
@@ -0,0 +1,162 @@
1
+ import { encodeQuery, parseAuthPass } from "../utils/codec.js";
2
+ import { md5, sha1 } from "../utils/hash.js";
3
+ import { Http } from "../utils/http.js";
4
+ import { MiNA } from "./mina.js";
5
+ import { MIoT } from "./miot.js";
6
+ const kLoginAPI = "https://account.xiaomi.com/pass";
7
+ async function getAccount(_account) {
8
+ let account = _account;
9
+ console.log("\u{1F510} \u8BA4\u8BC1\u4FE1\u606F:", {
10
+ userId: account.userId,
11
+ hasPassToken: !!account.passToken,
12
+ hasPassword: !!account.password,
13
+ hasServiceToken: !!account.serviceToken,
14
+ authMode: account.password ? "password" : account.passToken ? "passToken" : "unknown"
15
+ });
16
+ if (account.passToken && account.serviceToken && account.pass?.ssecurity) {
17
+ console.log("\u{1F504} \u5C1D\u8BD5\u4F7F\u7528\u7F13\u5B58\u7684\u767B\u5F55\u6001 (passToken + serviceToken + ssecurity)...");
18
+ account.pass = {
19
+ code: 0,
20
+ passToken: account.passToken,
21
+ ssecurity: account.pass.ssecurity,
22
+ nonce: account.pass.nonce || ""
23
+ };
24
+ let devices;
25
+ if (account.sid === "micoapi") {
26
+ devices = await MiNA.getDevice(account);
27
+ } else if (account.sid === "xiaomiio") {
28
+ devices = await MIoT.getDevice(account);
29
+ } else {
30
+ devices = account;
31
+ }
32
+ if (devices.device) {
33
+ console.log("\u2705 \u4F7F\u7528\u7F13\u5B58\u7684\u767B\u5F55\u6001\u6210\u529F");
34
+ return devices;
35
+ }
36
+ console.log("\u26A0\uFE0F \u7F13\u5B58\u7684\u767B\u5F55\u6001\u5DF2\u5931\u6548\uFF0C\u4F7F\u7528\u5BC6\u7801\u91CD\u65B0\u767B\u5F55...");
37
+ }
38
+ if (account.password) {
39
+ console.log("\u{1F511} \u4F7F\u7528\u5BC6\u7801\u767B\u5F55\uFF08passToken \u4F5C\u4E3A Cookie \u8F85\u52A9\uFF09...");
40
+ } else if (account.passToken) {
41
+ console.log("\u26A0\uFE0F \u4EC5\u6709 passToken \u65E0\u6CD5\u76F4\u63A5\u767B\u5F55\uFF0CpassToken \u9700\u8981\u914D\u5408\u5BC6\u7801\u4F7F\u7528");
42
+ }
43
+ let res = await Http.get(
44
+ `${kLoginAPI}/serviceLogin`,
45
+ { sid: account.sid, _json: true, _locale: "zh_CN" },
46
+ { cookies: _getLoginCookies(account) }
47
+ );
48
+ if (res.isError) {
49
+ console.error("\u274C \u767B\u5F55\u5931\u8D25", res);
50
+ return void 0;
51
+ }
52
+ let pass = parseAuthPass(res);
53
+ console.log("\u{1F4DD} serviceLogin \u54CD\u5E94:", { code: pass.code, description: pass.description, res });
54
+ if (pass.code !== 0) {
55
+ console.log("\u{1F4DD} \u767B\u5F55\u6001\u5931\u6548\uFF0C\u5C1D\u8BD5\u91CD\u65B0\u8BA4\u8BC1...");
56
+ if (!account.password) {
57
+ console.error("\u274C \u7F3A\u5C11\u5BC6\u7801\uFF0C\u65E0\u6CD5\u91CD\u65B0\u767B\u5F55\u3002\u8BF7\u914D\u7F6E password \u5B57\u6BB5\u3002");
58
+ return void 0;
59
+ }
60
+ const data = {
61
+ _json: "true",
62
+ qs: pass.qs,
63
+ sid: account.sid,
64
+ _sign: pass._sign,
65
+ callback: pass.callback,
66
+ user: account.userId,
67
+ hash: md5(account.password).toUpperCase()
68
+ };
69
+ res = await Http.post(`${kLoginAPI}/serviceLoginAuth2`, encodeQuery(data), {
70
+ cookies: _getLoginCookies(account)
71
+ });
72
+ if (res.isError) {
73
+ console.error("\u274C OAuth2 \u767B\u5F55\u5931\u8D25", res);
74
+ return void 0;
75
+ }
76
+ console.log("\u8FD4\u56DE\u7ED3\u679C\uFF1A", res.data);
77
+ pass = parseAuthPass(res);
78
+ console.log("\u{1F4DD} serviceLoginAuth2 \u54CD\u5E94:", {
79
+ code: pass.code,
80
+ hasPassToken: !!pass.passToken,
81
+ hasSsecurity: !!pass.ssecurity,
82
+ description: pass.description
83
+ });
84
+ }
85
+ if (pass.location?.includes("identity/authStart")) {
86
+ console.error("\u274C \u672C\u6B21\u767B\u5F55\u9700\u8981\u9A8C\u8BC1\u7801\uFF0C\u8BF7\u68C0\u67E5 passToken \u662F\u5426\u6B63\u786E");
87
+ console.log("\u{1F4A1} \u5F53\u524D\u4F7F\u7528\u7684 passToken:", account.passToken?.slice(0, 20) + "...");
88
+ return void 0;
89
+ }
90
+ if (!pass.location || !pass.nonce || !pass.ssecurity) {
91
+ console.error("\u274C \u767B\u5F55\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u4F60\u7684\u8D26\u53F7\u5BC6\u7801\u662F\u5426\u6B63\u786E");
92
+ console.log("\u{1F4CB} \u8FD4\u56DE\u6570\u636E:", {
93
+ hasLocation: !!pass.location,
94
+ hasNonce: !!pass.nonce,
95
+ hasSsecurity: !!pass.ssecurity,
96
+ hasPassToken: !!pass.passToken,
97
+ code: pass.code,
98
+ description: pass.description
99
+ });
100
+ return void 0;
101
+ }
102
+ console.log("\u2705 \u767B\u5F55\u6210\u529F\uFF0C\u83B7\u53D6 serviceToken...");
103
+ const serviceToken = await _getServiceToken(pass);
104
+ if (!serviceToken) {
105
+ return void 0;
106
+ }
107
+ console.log("\u2705 \u83B7\u53D6 serviceToken \u6210\u529F", { account, serviceToken });
108
+ account = { ...account, pass, serviceToken };
109
+ console.log("\u{1F4F1} \u6B63\u5728\u83B7\u53D6\u8BBE\u5907\u4FE1\u606F... account.did =", account.did);
110
+ console.log("\u{1F4F1} \u4F7F\u7528\u7684 deviceId:", account.deviceId);
111
+ if (account.sid === "micoapi") {
112
+ account = await MiNA.getDevice(account);
113
+ console.log("\u{1F4F1} MiNA.getDevice \u7ED3\u679C\uFF1Aaccount.device =", account?.device);
114
+ } else if (account.sid === "xiaomiio") {
115
+ account = await MIoT.getDevice(account);
116
+ console.log("\u{1F4F1} MIoT.getDevice \u7ED3\u679C\uFF1Aaccount.device =", account?.device);
117
+ }
118
+ if (account.did && !account.device) {
119
+ console.error(`\u274C \u627E\u4E0D\u5230\u8BBE\u5907\uFF1A${account.did}`);
120
+ console.log(
121
+ "\u{1F41B} \u8BF7\u68C0\u67E5\u4F60\u7684 did \u4E0E\u7C73\u5BB6\u4E2D\u7684\u8BBE\u5907\u540D\u79F0\u662F\u5426\u4E00\u81F4\u3002\u6CE8\u610F\u9519\u522B\u5B57\u3001\u7A7A\u683C\u548C\u5927\u5C0F\u5199\uFF0C\u6BD4\u5982\uFF1A\u97F3\u54CD \u{1F449} \u97F3\u7BB1"
122
+ );
123
+ console.log(
124
+ "\u{1F4A1} \u5EFA\u8BAE\u6253\u5F00 debug \u9009\u9879\uFF0C\u67E5\u770B\u76EE\u6807\u8BBE\u5907\u7684\u771F\u5B9E name\u3001miotDID \u6216 mac \u5730\u5740\uFF0C\u66F4\u65B0 did \u53C2\u6570"
125
+ );
126
+ return void 0;
127
+ }
128
+ console.log("\u2705 \u8BBE\u5907\u4FE1\u606F\u83B7\u53D6\u6210\u529F:", account.device?.name || account.device?.alias);
129
+ return account;
130
+ }
131
+ function _getLoginCookies(account) {
132
+ return {
133
+ userId: account.userId,
134
+ deviceId: account.deviceId,
135
+ passToken: account.pass?.passToken,
136
+ sdkVersion: "3.9"
137
+ };
138
+ }
139
+ async function _getServiceToken(pass) {
140
+ const { location, nonce, ssecurity } = pass ?? {};
141
+ if (!location || !nonce || !ssecurity) {
142
+ console.error("\u274C \u65E0\u6CD5\u83B7\u53D6 serviceToken\uFF0C\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570");
143
+ return void 0;
144
+ }
145
+ const nsec = `nonce=${nonce}&${ssecurity}`;
146
+ const clientSign = sha1(nsec);
147
+ const url = location + "&clientSign=" + encodeURIComponent(clientSign);
148
+ const res = await Http.get(url, {}, { rawResponse: true });
149
+ const cookies = res.headers?.["set-cookie"] ?? [];
150
+ for (const cookie of cookies) {
151
+ const match = cookie.match(/serviceToken=([^;]+)/);
152
+ if (match) {
153
+ return match[1];
154
+ }
155
+ }
156
+ console.error("\u274C \u83B7\u53D6 Mi Service Token \u5931\u8D25");
157
+ return void 0;
158
+ }
159
+ export {
160
+ getAccount
161
+ };
162
+ //# sourceMappingURL=account.js.map