@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 @@
1
+ {"version":3,"sources":["../../../src/mi/miot.ts"],"sourcesContent":["import { jsonEncode } from '../utils/parse.js';\nimport { decodeMIoT, encodeFormData, encodeMIoT } from '../utils/codec.js';\nimport { Http } from '../utils/http.js';\nimport { updateMiAccount } from './common.js';\nimport { Debugger } from '../utils/debug.js';\nimport type { MIoTDevice, MiAccount } from './typing.js';\n\ntype MIoTAccount = MiAccount & { device: MIoTDevice };\n\nexport class MIoT {\n account: MIoTAccount;\n\n constructor(account: MIoTAccount) {\n this.account = account;\n }\n\n static async getDevice(account: MIoTAccount): Promise<MIoTAccount> {\n if (account.sid !== 'xiaomiio') {\n return account;\n }\n const devices = await MIoT.__callMIoT(account, 'POST', '/home/device_list', {\n getVirtualModel: false,\n getHuamiDevices: 0,\n });\n if (Debugger.debug) {\n console.log('🐛 MIoT 设备列表:', jsonEncode(devices, { prettier: true }));\n }\n const device = (devices?.list ?? []).find((e: any) =>\n [e.did, e.name, e.mac].includes(account.did),\n );\n if (device) {\n account.device = device;\n }\n return account;\n }\n\n private static async __callMIoT(\n account: MIoTAccount,\n method: 'GET' | 'POST',\n path: string,\n _data?: any,\n ) {\n const url = `https://api.io.mi.com/app${path}`;\n const config = {\n account,\n setAccount: updateMiAccount(account),\n rawResponse: true,\n validateStatus: () => true,\n headers: {\n 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1',\n 'x-xiaomi-protocal-flag-cli': 'PROTOCAL-HTTP2',\n },\n cookies: {\n userId: account.userId,\n serviceToken: account.serviceToken,\n PassportDeviceId: account.deviceId,\n },\n };\n\n const data = encodeMIoT(path, _data, account.pass!.ssecurity!);\n if (Debugger.debug) {\n console.log('MIoT 请求:', {\n url,\n method,\n cookies: config.cookies,\n body: data,\n });\n }\n\n let res: any;\n if (method === 'GET') {\n res = await Http.get(url, data, config);\n } else {\n // POST body 使用 application/x-www-form-urlencoded 格式\n const formData = encodeFormData(data);\n if (Debugger.debug) {\n console.log('POST body:', formData);\n }\n res = await Http.post(url, formData, {\n ...config,\n headers: {\n ...config.headers,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n });\n }\n \n // 检查错误\n if (res.status === 401) {\n console.error('❌ 401 错误响应:', res.data);\n return undefined;\n }\n \n // MiService 返回的是明文 JSON,直接解析\n if (res.data && typeof res.data === 'object' && res.data.code === 0) {\n return res.data.result;\n }\n \n // 如果是加密字符串,尝试解密\n if (typeof res.data === 'string') {\n try {\n res = await decodeMIoT(\n account.pass!.ssecurity!,\n data._nonce,\n res.data,\n res.headers['miot-content-encoding'] === 'GZIP',\n );\n return res?.result;\n } catch (e) {\n console.error('❌ 解密失败:', e);\n return undefined;\n }\n }\n \n console.error('❌ 未知响应格式:', res);\n return undefined;\n }\n\n private async _callMIoT(method: 'GET' | 'POST', path: string, data?: any) {\n return MIoT.__callMIoT(this.account, method, path, data);\n }\n\n /**\n * 获取 MIoT 设备列表\n */\n async getDevices(getVirtualModel = false, getHuamiDevices = 0) {\n const res = await this._callMIoT('POST', '/home/device_list', {\n getVirtualModel: getVirtualModel,\n getHuamiDevices: getHuamiDevices,\n });\n return res?.list;\n }\n\n /**\n * 获取 MIoT 设备属性值\n */\n async getProperty(scope: number, property: number) {\n const res = await this._callMIoTSpec('prop/get', [\n {\n did: this.account.device.did,\n siid: scope,\n piid: property,\n },\n ]);\n return (res ?? [])?.[0]?.value;\n }\n\n /**\n * 设置 MIoT 设备属性值\n */\n async setProperty(scope: number, property: number, value: any) {\n const res = await this._callMIoTSpec('prop/set', [\n {\n did: this.account.device.did,\n siid: scope,\n piid: property,\n value: value,\n },\n ]);\n return (res ?? [])?.[0]?.code === 0;\n }\n\n /**\n * 调用 MIoT 设备能力指令\n */\n async doAction(scope: number, action: number, args: any = []) {\n const res = await this._callMIoTSpec('action', {\n did: this.account.device.did,\n siid: scope,\n aiid: action,\n in: Array.isArray(args) ? args : [args],\n });\n return res?.code === 0;\n }\n\n /**\n * 调用 MIoT 设备 RPC 指令\n */\n rpc(method: string, params: any, id = 1) {\n return this._callMIoT('POST', `/home/rpc/${this.account.device.did}`, {\n id,\n method,\n params,\n });\n }\n\n private _callMIoTSpec(command: string, params: any, datasource = 2) {\n return this._callMIoT('POST', `/miotspec/${command}`, {\n params,\n datasource,\n });\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * 小米账号类型定义
3
+ */
4
+ interface MiAccount {
5
+ /** 小米 ID(数字) */
6
+ userId?: string;
7
+ /** 密码 */
8
+ password?: string;
9
+ /** 登录凭证 */
10
+ passToken?: string;
11
+ /** 服务类型 */
12
+ sid: 'micoapi' | 'xiaomiio';
13
+ /** 设备 ID */
14
+ deviceId?: string;
15
+ /** 服务 Token */
16
+ serviceToken?: string;
17
+ /** 登录态 */
18
+ pass?: MiPass;
19
+ /** 设备标识(名称/ID/MAC) */
20
+ did?: string;
21
+ /** 设备信息 */
22
+ device?: MiNADevice | MIoTDevice;
23
+ }
24
+ /**
25
+ * 登录态
26
+ */
27
+ interface MiPass {
28
+ code: number;
29
+ qs?: string;
30
+ _sign?: string;
31
+ callback?: string;
32
+ location?: string;
33
+ nonce?: string;
34
+ ssecurity?: string;
35
+ passToken?: string;
36
+ notificationUrl?: string;
37
+ cUserId?: string;
38
+ }
39
+ /**
40
+ * MiNA 设备
41
+ */
42
+ interface MiNADevice {
43
+ deviceID: string;
44
+ deviceId?: string;
45
+ miotDID: string;
46
+ name: string;
47
+ alias: string;
48
+ mac: string;
49
+ hardware: string;
50
+ serialNumber: string;
51
+ rom: string;
52
+ deviceSNProfile?: string;
53
+ extra?: {
54
+ fw_version: string;
55
+ };
56
+ }
57
+ /**
58
+ * MIoT 设备
59
+ */
60
+ interface MIoTDevice {
61
+ did: string;
62
+ name: string;
63
+ mac: string;
64
+ model: string;
65
+ extra?: {
66
+ fw_version: string;
67
+ };
68
+ }
69
+ /**
70
+ * 对话消息
71
+ */
72
+ interface MiConversation {
73
+ query: string;
74
+ time: number;
75
+ answers: Array<{
76
+ type: string;
77
+ tts?: string;
78
+ url?: string;
79
+ }>;
80
+ }
81
+ /**
82
+ * 对话列表
83
+ */
84
+ interface MiConversations {
85
+ records: MiConversation[];
86
+ hasMore: boolean;
87
+ cursor?: number;
88
+ }
89
+
90
+ export type { MIoTDevice, MiAccount, MiConversation, MiConversations, MiNADevice, MiPass };
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=typing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,5 @@
1
+ import { ChannelOnboardingAdapter } from 'openclaw/plugin-sdk';
2
+
3
+ declare const miGPTOnboardingAdapter: ChannelOnboardingAdapter;
4
+
5
+ export { miGPTOnboardingAdapter };
@@ -0,0 +1,118 @@
1
+ import { MiService } from "./service.js";
2
+ const miGPTOnboardingAdapter = {
3
+ async selectAccount({ accounts }) {
4
+ if (accounts.length === 0) {
5
+ return { action: "create" };
6
+ }
7
+ if (accounts.length === 1) {
8
+ return { action: "use", accountId: accounts[0] };
9
+ }
10
+ return { action: "select" };
11
+ },
12
+ async promptCredentials() {
13
+ const answers = {};
14
+ answers.userId = await this.prompt.input({
15
+ message: "\u8BF7\u8F93\u5165\u4F60\u7684\u5C0F\u7C73 ID\uFF08\u6570\u5B57\uFF0C\u5728\u5C0F\u7C73\u8D26\u53F7\u300C\u4E2A\u4EBA\u4FE1\u606F\u300D-\u300C\u5C0F\u7C73 ID\u300D\u67E5\u770B\uFF09:",
16
+ validate: (v) => /^\d+$/.test(v) || "\u5C0F\u7C73 ID \u5FC5\u987B\u662F\u6570\u5B57"
17
+ });
18
+ const usePassToken = await this.prompt.confirm({
19
+ message: "\u662F\u5426\u4F7F\u7528 passToken \u767B\u5F55\uFF1F\uFF08\u63A8\u8350\uFF0C\u53EF\u907F\u514D\u9A8C\u8BC1\u7801\uFF09",
20
+ initial: false
21
+ });
22
+ if (usePassToken) {
23
+ answers.passToken = await this.prompt.password({
24
+ message: "\u8BF7\u8F93\u5165 passToken:",
25
+ validate: (v) => !!v || "passToken \u4E0D\u80FD\u4E3A\u7A7A"
26
+ });
27
+ } else {
28
+ answers.password = await this.prompt.password({
29
+ message: "\u8BF7\u8F93\u5165\u5C0F\u7C73\u8D26\u53F7\u5BC6\u7801:",
30
+ validate: (v) => !!v || "\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A"
31
+ });
32
+ }
33
+ answers.deviceName = await this.prompt.input({
34
+ message: "\u8BF7\u8F93\u5165\u5C0F\u7231\u97F3\u7BB1\u5728\u7C73\u5BB6\u4E2D\u8BBE\u7F6E\u7684\u540D\u79F0\uFF08\u5982\uFF1A\u5BA2\u5385\u97F3\u7BB1\uFF09:",
35
+ validate: (v) => !!v || "\u8BBE\u5907\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A"
36
+ });
37
+ return answers;
38
+ },
39
+ async validateCredentials({ input }) {
40
+ const config = {
41
+ userId: input.userId,
42
+ password: input.password,
43
+ passToken: input.passToken,
44
+ debug: true
45
+ };
46
+ try {
47
+ const devices = await MiService.getDevices(config);
48
+ if (devices.length === 0) {
49
+ return {
50
+ valid: false,
51
+ error: "\u672A\u627E\u5230\u4EFB\u4F55\u8BBE\u5907\uFF0C\u8BF7\u68C0\u67E5\u8D26\u53F7\u51ED\u8BC1\u662F\u5426\u6B63\u786E"
52
+ };
53
+ }
54
+ const deviceName = input.deviceName;
55
+ const matchedDevice = devices.find(
56
+ (d) => d.name.toLowerCase() === deviceName.toLowerCase() || d.did.toLowerCase() === deviceName.toLowerCase()
57
+ );
58
+ if (!matchedDevice) {
59
+ const deviceList = devices.map((d) => d.name).join(", ");
60
+ return {
61
+ valid: false,
62
+ error: `\u672A\u627E\u5230\u8BBE\u5907 "${deviceName}"\u3002\u53EF\u7528\u8BBE\u5907\uFF1A${deviceList}`
63
+ };
64
+ }
65
+ return {
66
+ valid: true,
67
+ data: {
68
+ did: matchedDevice.did
69
+ }
70
+ };
71
+ } catch (err) {
72
+ return {
73
+ valid: false,
74
+ error: err.message || "\u9A8C\u8BC1\u5931\u8D25"
75
+ };
76
+ }
77
+ },
78
+ applyConfig({ cfg, accountId, input, validatedData }) {
79
+ const migptCfg = cfg.channels?.migpt ?? {};
80
+ const accountConfig = {
81
+ userId: input.userId,
82
+ password: input.password,
83
+ passToken: input.passToken,
84
+ devices: [validatedData?.did || input.deviceName],
85
+ enabled: true
86
+ };
87
+ const isDefault = !accountId || accountId === "main";
88
+ if (isDefault) {
89
+ return {
90
+ ...cfg,
91
+ channels: {
92
+ ...cfg.channels,
93
+ migpt: {
94
+ ...migptCfg,
95
+ ...accountConfig
96
+ }
97
+ }
98
+ };
99
+ }
100
+ return {
101
+ ...cfg,
102
+ channels: {
103
+ ...cfg.channels,
104
+ migpt: {
105
+ ...migptCfg,
106
+ accounts: {
107
+ ...migptCfg.accounts,
108
+ [accountId]: accountConfig
109
+ }
110
+ }
111
+ }
112
+ };
113
+ }
114
+ };
115
+ export {
116
+ miGPTOnboardingAdapter
117
+ };
118
+ //# sourceMappingURL=onboarding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/onboarding.ts"],"sourcesContent":["import type { ChannelOnboardingAdapter } from 'openclaw/plugin-sdk';\nimport { MiService } from './service.js';\nimport type { MiServiceConfig } from './service.js';\n\nexport const miGPTOnboardingAdapter: ChannelOnboardingAdapter = {\n async selectAccount({ accounts }: { accounts: string[] }) {\n if (accounts.length === 0) {\n return { action: 'create' };\n }\n if (accounts.length === 1) {\n return { action: 'use', accountId: accounts[0] };\n }\n return { action: 'select' };\n },\n\n async promptCredentials() {\n // 这些方法由 OpenClaw 框架在运行时提供\n const answers: any = {};\n\n answers.userId = await (this as any).prompt.input({\n message: '请输入你的小米 ID(数字,在小米账号「个人信息」-「小米 ID」查看):',\n validate: (v: string) => /^\\d+$/.test(v) || '小米 ID 必须是数字',\n });\n\n const usePassToken = await (this as any).prompt.confirm({\n message: '是否使用 passToken 登录?(推荐,可避免验证码)',\n initial: false,\n });\n\n if (usePassToken) {\n answers.passToken = await (this as any).prompt.password({\n message: '请输入 passToken:',\n validate: (v: string) => !!v || 'passToken 不能为空',\n });\n } else {\n answers.password = await (this as any).prompt.password({\n message: '请输入小米账号密码:',\n validate: (v: string) => !!v || '密码不能为空',\n });\n }\n\n answers.deviceName = await (this as any).prompt.input({\n message: '请输入小爱音箱在米家中设置的名称(如:客厅音箱):',\n validate: (v: string) => !!v || '设备名称不能为空',\n });\n\n return answers;\n },\n\n async validateCredentials({ input }: { input: any }) {\n const config: MiServiceConfig = {\n userId: input.userId,\n password: input.password,\n passToken: input.passToken,\n debug: true,\n };\n\n try {\n const devices = await MiService.getDevices(config);\n \n if (devices.length === 0) {\n return {\n valid: false,\n error: '未找到任何设备,请检查账号凭证是否正确',\n };\n }\n\n const deviceName = input.deviceName;\n const matchedDevice = devices.find(\n (d) => d.name.toLowerCase() === deviceName.toLowerCase() || \n d.did.toLowerCase() === deviceName.toLowerCase()\n );\n\n if (!matchedDevice) {\n const deviceList = devices.map((d) => d.name).join(', ');\n return {\n valid: false,\n error: `未找到设备 \"${deviceName}\"。可用设备:${deviceList}`,\n };\n }\n\n return {\n valid: true,\n data: {\n did: matchedDevice.did,\n },\n };\n } catch (err: any) {\n return {\n valid: false,\n error: err.message || '验证失败',\n };\n }\n },\n\n applyConfig({ cfg, accountId, input, validatedData }: { cfg: any; accountId?: string; input: any; validatedData?: any }) {\n const migptCfg = cfg.channels?.migpt ?? {};\n \n const accountConfig = {\n userId: input.userId,\n password: input.password,\n passToken: input.passToken,\n devices: [validatedData?.did || input.deviceName],\n enabled: true,\n };\n\n const isDefault = !accountId || accountId === 'main';\n\n if (isDefault) {\n return {\n ...cfg,\n channels: {\n ...cfg.channels,\n migpt: {\n ...migptCfg,\n ...accountConfig,\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]: accountConfig,\n },\n },\n },\n };\n },\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,185 @@
1
+ // Type declarations for openclaw/plugin-sdk
2
+ declare module 'openclaw/plugin-sdk/channel-core' {
3
+ export function defineSetupPluginEntry(plugin: any): any;
4
+ }
5
+
6
+ declare module 'openclaw/plugin-sdk' {
7
+ export const DEFAULT_ACCOUNT_ID: string;
8
+
9
+ export function emptyPluginConfigSchema(): any;
10
+
11
+ export interface PluginRuntime {
12
+ channel: {
13
+ activity: {
14
+ record: (opts: { channel: string; accountId: string; direction: 'inbound' | 'outbound' }) => void;
15
+ };
16
+ reply: {
17
+ finalizeInboundContext: (opts: {
18
+ Body: string;
19
+ BodyForAgent?: string;
20
+ RawBody?: string;
21
+ CommandBody?: string;
22
+ From: string;
23
+ To: string;
24
+ SessionKey: string;
25
+ AccountId: string;
26
+ ChatType: 'direct' | 'group';
27
+ SenderId: string;
28
+ SenderName?: string;
29
+ Provider: string;
30
+ Surface: string;
31
+ MessageSid: string;
32
+ Timestamp: number;
33
+ OriginatingChannel: string;
34
+ OriginatingTo: string;
35
+ CommandAuthorized?: boolean;
36
+ MediaPaths?: string[];
37
+ MediaPath?: string;
38
+ MediaTypes?: string[];
39
+ MediaType?: string;
40
+ MediaUrls?: string[];
41
+ MediaUrl?: string;
42
+ ImageMediaTypes?: string[];
43
+ }) => any;
44
+ dispatchReplyWithBufferedBlockDispatcher: (opts: {
45
+ ctx: any;
46
+ cfg: any;
47
+ dispatcherOptions?: {
48
+ responsePrefix?: string;
49
+ deliver?: (payload: { text?: string; mediaUrls?: string[]; mediaUrl?: string }, info: { kind: string }) => Promise<void>;
50
+ };
51
+ }) => Promise<void>;
52
+ resolveEffectiveMessagesConfig: (cfg: any, agentId?: string) => any;
53
+ resolveEnvelopeFormatOptions: (cfg: any) => any;
54
+ formatInboundEnvelope: (opts: {
55
+ Body: string;
56
+ BodyForAgent?: string;
57
+ From: string;
58
+ To: string;
59
+ SessionKey: string;
60
+ ChatType: 'direct' | 'group';
61
+ SenderId: string;
62
+ SenderName?: string;
63
+ Provider: string;
64
+ Surface: string;
65
+ MessageSid: string;
66
+ Timestamp: number;
67
+ OriginatingChannel: string;
68
+ envelopeOptions: any;
69
+ }) => string;
70
+ };
71
+ routing: {
72
+ resolveAgentRoute: (opts: {
73
+ from: string;
74
+ to: string;
75
+ sessionKey: string;
76
+ accountId: string;
77
+ chatType: 'direct' | 'group';
78
+ provider: string;
79
+ }) => any;
80
+ };
81
+ };
82
+ }
83
+
84
+ export interface OpenClawPluginApi {
85
+ runtime: PluginRuntime;
86
+ logger: any;
87
+ registerChannel(options: { plugin: any }): void;
88
+ registerTool(tool: any): void;
89
+ registerGatewayMethod(name: string, handler: any): void;
90
+ registerHttpRoute(handler: any): void;
91
+ registerCli(handler: any, options?: any): void;
92
+ registerCommand(command: any): void;
93
+ registerService(service: any): void;
94
+ registerContextEngine(id: string, factory: any): void;
95
+ registerHook(event: string, handler: any, options?: any): void;
96
+ registerProvider(provider: any): void;
97
+ on(event: string, handler: any, options?: any): void;
98
+ }
99
+
100
+ export interface ChannelPlugin<T = any> {
101
+ id: string;
102
+ meta: {
103
+ id: string;
104
+ label: string;
105
+ selectionLabel: string;
106
+ docsPath?: string;
107
+ docsLabel?: string;
108
+ blurb: string;
109
+ aliases?: string[];
110
+ order?: number;
111
+ };
112
+ capabilities: {
113
+ chatTypes: string[];
114
+ polls?: boolean;
115
+ threads?: boolean;
116
+ media?: boolean;
117
+ reactions?: boolean;
118
+ edit?: boolean;
119
+ reply?: boolean;
120
+ blockStreaming?: boolean;
121
+ };
122
+ reload?: { configPrefixes: string[] };
123
+ onboarding?: ChannelOnboardingAdapter;
124
+ config: {
125
+ listAccountIds: (cfg: any) => string[];
126
+ resolveAccount: (cfg: any, accountId?: string) => T;
127
+ defaultAccountId: (cfg: any) => string;
128
+ setAccountEnabled: (opts: { cfg: any; accountId: string; enabled: boolean }) => any;
129
+ deleteAccount: (opts: { cfg: any; accountId: string }) => any;
130
+ isConfigured: (account: T) => boolean;
131
+ describeAccount: (account: T) => any;
132
+ resolveAllowFrom?: (opts: { cfg: any; accountId?: string }) => string[];
133
+ formatAllowFrom?: (opts: { allowFrom: Array<string | number> }) => string[];
134
+ };
135
+ setup?: {
136
+ resolveAccountId?: (opts: any) => string;
137
+ applyAccountConfig?: (opts: any) => any;
138
+ validateInput?: (opts: any) => string | null;
139
+ applyAccountName?: (opts: any) => any;
140
+ };
141
+ messaging?: {
142
+ normalizeTarget: (target: string) => { ok: boolean; to?: string; error?: string };
143
+ targetResolver: {
144
+ looksLikeId: (id: string) => boolean;
145
+ hint: string;
146
+ };
147
+ };
148
+ outbound: ChannelOutboundAdapter;
149
+ gateway?: {
150
+ startAccount: (ctx: any) => Promise<void>;
151
+ logoutAccount?: (opts: any) => Promise<any>;
152
+ };
153
+ status?: {
154
+ defaultRuntime: any;
155
+ buildChannelSummary?: (opts: any) => any;
156
+ probeAccount?: (opts: any) => Promise<any>;
157
+ buildAccountSnapshot?: (opts: any) => any;
158
+ };
159
+ pairing?: any;
160
+ security?: any;
161
+ groups?: any;
162
+ agentPrompt?: any;
163
+ directory?: any;
164
+ }
165
+
166
+ export interface ChannelOutboundAdapter {
167
+ deliveryMode: string;
168
+ chunker?: (text: string, limit: number) => string[];
169
+ chunkerMode?: string;
170
+ textChunkLimit?: number;
171
+ sendText: (opts: { to: string; text: string; accountId?: string; cfg: any; replyToId?: string }) => Promise<any>;
172
+ sendMedia?: (opts: { to: string; text?: string; mediaUrl: string; accountId?: string; cfg: any; replyToId?: string }) => Promise<any>;
173
+ }
174
+
175
+ export interface ChannelOnboardingAdapter {
176
+ selectAccount?: (opts: any) => Promise<any>;
177
+ promptCredentials?: () => Promise<any>;
178
+ validateCredentials?: (opts: any) => Promise<any>;
179
+ applyConfig?: (opts: any) => any;
180
+ }
181
+
182
+ export interface OpenClawConfig {
183
+ channels?: Record<string, any>;
184
+ }
185
+ }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=openclaw-plugin-sdk.d.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,5 @@
1
+ import { ChannelOutboundAdapter } from 'openclaw/plugin-sdk';
2
+
3
+ declare const miOutbound: ChannelOutboundAdapter;
4
+
5
+ export { miOutbound };
@@ -0,0 +1,108 @@
1
+ import { MiService } from "./service.js";
2
+ import { MiSpeaker } from "./speaker.js";
3
+ import { resolveMiAccount } from "./config.js";
4
+ function chunkText(text, limit) {
5
+ if (text.length <= limit) return [text];
6
+ const chunks = [];
7
+ let remaining = text;
8
+ while (remaining.length > 0) {
9
+ if (remaining.length <= limit) {
10
+ chunks.push(remaining);
11
+ break;
12
+ }
13
+ let splitAt = remaining.lastIndexOf("\n", limit);
14
+ if (splitAt <= 0 || splitAt < limit * 0.5) {
15
+ splitAt = remaining.lastIndexOf(" ", limit);
16
+ }
17
+ if (splitAt <= 0 || splitAt < limit * 0.5) {
18
+ splitAt = limit;
19
+ }
20
+ chunks.push(remaining.slice(0, splitAt));
21
+ remaining = remaining.slice(splitAt).trimStart();
22
+ }
23
+ return chunks;
24
+ }
25
+ const miOutbound = {
26
+ deliveryMode: "direct",
27
+ chunker: chunkText,
28
+ chunkerMode: "plain",
29
+ textChunkLimit: 200,
30
+ sendText: async ({ to, text, accountId, cfg }) => {
31
+ const account = resolveMiAccount(cfg, accountId);
32
+ if (!account.configured) {
33
+ return {
34
+ channel: "migpt",
35
+ error: new Error("Account not configured")
36
+ };
37
+ }
38
+ const initSuccess = await MiService.init(account.config, to);
39
+ if (!initSuccess) {
40
+ return {
41
+ channel: "migpt",
42
+ error: new Error("Failed to initialize MiService")
43
+ };
44
+ }
45
+ const volume = cfg.channels?.migpt?.volume;
46
+ if (volume && volume >= 6 && volume <= 100) {
47
+ await MiSpeaker.setVolume(volume);
48
+ }
49
+ const streaming = cfg.channels?.migpt?.streaming ?? true;
50
+ const chunkLimit = cfg.channels?.migpt?.textChunkLimit ?? 200;
51
+ if (streaming && text.length > chunkLimit) {
52
+ const chunks = chunkText(text, chunkLimit);
53
+ for (const chunk of chunks) {
54
+ const result = await MiSpeaker.play({ text: chunk });
55
+ if (!result.success) {
56
+ return {
57
+ channel: "migpt",
58
+ error: new Error(result.error)
59
+ };
60
+ }
61
+ await new Promise((resolve) => setTimeout(resolve, 200));
62
+ }
63
+ return { channel: "migpt", messageId: Date.now().toString() };
64
+ } else {
65
+ const result = await MiSpeaker.play({ text });
66
+ if (!result.success) {
67
+ return {
68
+ channel: "migpt",
69
+ error: new Error(result.error)
70
+ };
71
+ }
72
+ return { channel: "migpt", messageId: Date.now().toString() };
73
+ }
74
+ },
75
+ sendMedia: async ({ to, text, mediaUrl, accountId, cfg }) => {
76
+ const account = resolveMiAccount(cfg, accountId);
77
+ if (!account.configured) {
78
+ return {
79
+ channel: "migpt",
80
+ error: new Error("Account not configured")
81
+ };
82
+ }
83
+ const initSuccess = await MiService.init(account.config, to);
84
+ if (!initSuccess) {
85
+ return {
86
+ channel: "migpt",
87
+ error: new Error("Failed to initialize MiService")
88
+ };
89
+ }
90
+ if (text?.trim()) {
91
+ await MiSpeaker.play({ text });
92
+ }
93
+ if (mediaUrl) {
94
+ const result = await MiSpeaker.play({ url: mediaUrl });
95
+ if (!result.success) {
96
+ return {
97
+ channel: "migpt",
98
+ error: new Error(result.error)
99
+ };
100
+ }
101
+ }
102
+ return { channel: "migpt", messageId: Date.now().toString() };
103
+ }
104
+ };
105
+ export {
106
+ miOutbound
107
+ };
108
+ //# sourceMappingURL=outbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/outbound.ts"],"sourcesContent":["import type { ChannelOutboundAdapter } from 'openclaw/plugin-sdk';\nimport { MiService } from './service.js';\nimport { MiSpeaker } from './speaker.js';\nimport { resolveMiAccount } from './config.js';\nimport type { ExtendedOpenClawConfig } from './types.js';\n\n/**\n * 文本分块函数\n */\nfunction chunkText(text: string, limit: number): string[] {\n if (text.length <= limit) return [text];\n \n const chunks: string[] = [];\n let remaining = text;\n \n while (remaining.length > 0) {\n if (remaining.length <= limit) {\n chunks.push(remaining);\n break;\n }\n \n // 尝试在换行处分割\n let splitAt = remaining.lastIndexOf('\\n', limit);\n if (splitAt <= 0 || splitAt < limit * 0.5) {\n // 没找到合适的换行,尝试在空格处分割\n splitAt = remaining.lastIndexOf(' ', limit);\n }\n if (splitAt <= 0 || splitAt < limit * 0.5) {\n // 还是没找到,强制在 limit 处分割\n splitAt = limit;\n }\n \n chunks.push(remaining.slice(0, splitAt));\n remaining = remaining.slice(splitAt).trimStart();\n }\n \n return chunks;\n}\n\nexport const miOutbound: ChannelOutboundAdapter = {\n deliveryMode: 'direct',\n chunker: chunkText,\n chunkerMode: 'plain',\n textChunkLimit: 200,\n \n sendText: async ({ to, text, accountId, cfg }) => {\n const account = resolveMiAccount(cfg as unknown as ExtendedOpenClawConfig, accountId);\n \n if (!account.configured) {\n return { \n channel: 'migpt', \n error: new Error('Account not configured') \n };\n }\n\n // 初始化服务\n const initSuccess = await MiService.init(account.config, to);\n if (!initSuccess) {\n return { \n channel: 'migpt', \n error: new Error('Failed to initialize MiService') \n };\n }\n\n // 设置音量(如果配置了)\n const volume = cfg.channels?.migpt?.volume;\n if (volume && volume >= 6 && volume <= 100) {\n await MiSpeaker.setVolume(volume);\n }\n\n // 分块发送(如果启用流式)\n const streaming = cfg.channels?.migpt?.streaming ?? true;\n const chunkLimit = cfg.channels?.migpt?.textChunkLimit ?? 200;\n \n if (streaming && text.length > chunkLimit) {\n const chunks = chunkText(text, chunkLimit);\n for (const chunk of chunks) {\n const result = await MiSpeaker.play({ text: chunk });\n if (!result.success) {\n return { \n channel: 'migpt', \n error: new Error(result.error) \n };\n }\n // 小块之间短暂暂停,避免过于紧凑\n await new Promise(resolve => setTimeout(resolve, 200));\n }\n return { channel: 'migpt', messageId: Date.now().toString() };\n } else {\n const result = await MiSpeaker.play({ text });\n if (!result.success) {\n return { \n channel: 'migpt', \n error: new Error(result.error) \n };\n }\n return { channel: 'migpt', messageId: Date.now().toString() };\n }\n },\n \n sendMedia: async ({ to, text, mediaUrl, accountId, cfg }) => {\n const account = resolveMiAccount(cfg as unknown as ExtendedOpenClawConfig, accountId);\n \n if (!account.configured) {\n return { \n channel: 'migpt', \n error: new Error('Account not configured') \n };\n }\n\n const initSuccess = await MiService.init(account.config, to);\n if (!initSuccess) {\n return { \n channel: 'migpt', \n error: new Error('Failed to initialize MiService') \n };\n }\n\n // 发送文本说明(如果有)\n if (text?.trim()) {\n await MiSpeaker.play({ text });\n }\n\n // 播放音频 URL\n if (mediaUrl) {\n const result = await MiSpeaker.play({ url: mediaUrl });\n if (!result.success) {\n return { \n channel: 'migpt', \n error: new Error(result.error) \n };\n }\n }\n\n return { channel: 'migpt', messageId: Date.now().toString() };\n },\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,6 @@
1
+ import { PluginRuntime } from 'openclaw/plugin-sdk';
2
+
3
+ declare function setMiGPTRuntime(next: PluginRuntime): void;
4
+ declare function getMiGPTRuntime(): PluginRuntime;
5
+
6
+ export { getMiGPTRuntime, setMiGPTRuntime };
@@ -0,0 +1,15 @@
1
+ let runtime = null;
2
+ function setMiGPTRuntime(next) {
3
+ runtime = next;
4
+ }
5
+ function getMiGPTRuntime() {
6
+ if (!runtime) {
7
+ throw new Error("MiGPT runtime not initialized");
8
+ }
9
+ return runtime;
10
+ }
11
+ export {
12
+ getMiGPTRuntime,
13
+ setMiGPTRuntime
14
+ };
15
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/runtime.ts"],"sourcesContent":["import type { PluginRuntime } from \"openclaw/plugin-sdk\";\n\nlet runtime: PluginRuntime | null = null;\n\nexport function setMiGPTRuntime(next: PluginRuntime) {\n runtime = next;\n}\n\nexport function getMiGPTRuntime(): PluginRuntime {\n if (!runtime) {\n throw new Error(\"MiGPT runtime not initialized\");\n }\n return runtime;\n}\n"],"mappings":"AAEA,IAAI,UAAgC;AAE7B,SAAS,gBAAgB,MAAqB;AACnD,YAAU;AACZ;AAEO,SAAS,kBAAiC;AAC/C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,70 @@
1
+ import { MiNA } from './mi/mina.js';
2
+ import { MIoT } from './mi/miot.js';
3
+ import './mi/typing.js';
4
+
5
+ interface MiServiceConfig {
6
+ /** 小米 ID(数字) */
7
+ userId?: string;
8
+ /** 密码 */
9
+ password?: string;
10
+ /** 登录凭证 */
11
+ passToken?: string;
12
+ /** 是否开启调试模式 */
13
+ debug?: boolean;
14
+ /** 网络请求超时时长(毫秒) */
15
+ timeout?: number;
16
+ /** 音箱控制方式:mina/miot */
17
+ speakerControl?: 'mina' | 'miot';
18
+ }
19
+ declare class _MiService {
20
+ MiNA?: MiNA;
21
+ MiOT?: MIoT;
22
+ private _initialized;
23
+ private _initializing;
24
+ private _speakerControl;
25
+ /**
26
+ * 使用 MIoT 发送 TTS 播报
27
+ */
28
+ playWithMiot(text: string): Promise<boolean>;
29
+ /**
30
+ * 使用 MiNA 发送 TTS 播报
31
+ */
32
+ playWithMina(text: string): Promise<boolean>;
33
+ /**
34
+ * 发送 TTS 播报(根据配置选择 MiNA 或 MIoT)
35
+ */
36
+ play(text: string): Promise<boolean>;
37
+ /**
38
+ * 初始化服务
39
+ */
40
+ init(config: MiServiceConfig & {
41
+ announceOnStart?: boolean;
42
+ startupMessage?: string;
43
+ }, did: string): Promise<boolean>;
44
+ /**
45
+ * 获取设备列表
46
+ */
47
+ getDevices(config: MiServiceConfig): Promise<Array<{
48
+ did: string;
49
+ name: string;
50
+ model?: string;
51
+ }>>;
52
+ /**
53
+ * 重新登录
54
+ */
55
+ relogin(config: MiServiceConfig, did: string): Promise<boolean>;
56
+ /**
57
+ * 唤醒小爱音箱(进入监听状态)
58
+ * 使用 MIoT spec 调用唤醒动作 (siid=5, aiid=3)
59
+ * 基于 mi-gpt 项目的实现
60
+ */
61
+ wakeUp(): Promise<boolean>;
62
+ /**
63
+ * 退出监听状态(取消唤醒)
64
+ * 使用 MiNA 暂停使小爱退出监听
65
+ */
66
+ unWakeUp(): Promise<void>;
67
+ }
68
+ declare const MiService: _MiService;
69
+
70
+ export { MiService, type MiServiceConfig };