@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.
- package/LICENSE +21 -0
- package/README.md +690 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/setup-entry.d.ts +3 -0
- package/dist/setup-entry.js +7 -0
- package/dist/setup-entry.js.map +1 -0
- package/dist/src/channel.d.ts +10 -0
- package/dist/src/channel.js +444 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/config.d.ts +125 -0
- package/dist/src/config.js +146 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/message.d.ts +51 -0
- package/dist/src/message.js +145 -0
- package/dist/src/message.js.map +1 -0
- package/dist/src/mi/account.d.ts +5 -0
- package/dist/src/mi/account.js +162 -0
- package/dist/src/mi/account.js.map +1 -0
- package/dist/src/mi/common.d.ts +15 -0
- package/dist/src/mi/common.js +80 -0
- package/dist/src/mi/common.js.map +1 -0
- package/dist/src/mi/index.d.ts +4 -0
- package/dist/src/mi/index.js +10 -0
- package/dist/src/mi/index.js.map +1 -0
- package/dist/src/mi/mina.d.ts +66 -0
- package/dist/src/mi/mina.js +225 -0
- package/dist/src/mi/mina.js.map +1 -0
- package/dist/src/mi/miot.d.ts +35 -0
- package/dist/src/mi/miot.js +168 -0
- package/dist/src/mi/miot.js.map +1 -0
- package/dist/src/mi/typing.d.ts +90 -0
- package/dist/src/mi/typing.js +1 -0
- package/dist/src/mi/typing.js.map +1 -0
- package/dist/src/onboarding.d.ts +5 -0
- package/dist/src/onboarding.js +118 -0
- package/dist/src/onboarding.js.map +1 -0
- package/dist/src/openclaw-plugin-sdk.d.d.ts +185 -0
- package/dist/src/openclaw-plugin-sdk.d.js +1 -0
- package/dist/src/openclaw-plugin-sdk.d.js.map +1 -0
- package/dist/src/outbound.d.ts +5 -0
- package/dist/src/outbound.js +108 -0
- package/dist/src/outbound.js.map +1 -0
- package/dist/src/runtime.d.ts +6 -0
- package/dist/src/runtime.js +15 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/service.d.ts +70 -0
- package/dist/src/service.js +200 -0
- package/dist/src/service.js.map +1 -0
- package/dist/src/speaker.d.ts +62 -0
- package/dist/src/speaker.js +211 -0
- package/dist/src/speaker.js.map +1 -0
- package/dist/src/tts/mimo.d.ts +50 -0
- package/dist/src/tts/mimo.js +214 -0
- package/dist/src/tts/mimo.js.map +1 -0
- package/dist/src/types.d.ts +30 -0
- package/dist/src/types.js +1 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/codec.d.ts +31 -0
- package/dist/src/utils/codec.js +144 -0
- package/dist/src/utils/codec.js.map +1 -0
- package/dist/src/utils/debug.d.ts +10 -0
- package/dist/src/utils/debug.js +15 -0
- package/dist/src/utils/debug.js.map +1 -0
- package/dist/src/utils/hash.d.ts +40 -0
- package/dist/src/utils/hash.js +75 -0
- package/dist/src/utils/hash.js.map +1 -0
- package/dist/src/utils/http.d.ts +24 -0
- package/dist/src/utils/http.js +151 -0
- package/dist/src/utils/http.js.map +1 -0
- package/dist/src/utils/index.d.ts +6 -0
- package/dist/src/utils/index.js +10 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/io.d.ts +26 -0
- package/dist/src/utils/io.js +53 -0
- package/dist/src/utils/io.js.map +1 -0
- package/dist/src/utils/parse.d.ts +26 -0
- package/dist/src/utils/parse.js +51 -0
- package/dist/src/utils/parse.js.map +1 -0
- package/index.ts +26 -0
- package/openclaw.plugin.json +344 -0
- package/package.json +106 -0
- package/setup-entry.ts +12 -0
- package/skills/migpt-volume/SKILL.md +182 -0
- package/skills/migpt-volume/index.ts +50 -0
- package/src/channel.ts +519 -0
- package/src/config.ts +299 -0
- package/src/message.ts +186 -0
- package/src/mi/account.ts +184 -0
- package/src/mi/common.ts +105 -0
- package/src/mi/index.ts +4 -0
- package/src/mi/mina.ts +261 -0
- package/src/mi/miot.ts +193 -0
- package/src/mi/typing.ts +93 -0
- package/src/onboarding.ts +136 -0
- package/src/openclaw-plugin-sdk.d.ts +185 -0
- package/src/outbound.ts +137 -0
- package/src/runtime.ts +14 -0
- package/src/service.ts +246 -0
- package/src/speaker.ts +264 -0
- package/src/tts/mimo.ts +300 -0
- package/src/types.ts +34 -0
- package/src/utils/codec.ts +206 -0
- package/src/utils/debug.ts +16 -0
- package/src/utils/hash.ts +104 -0
- package/src/utils/http.ts +193 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/io.ts +68 -0
- package/src/utils/parse.ts +64 -0
- package/tsconfig.json +25 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { OpenClawPluginApi } from 'openclaw/plugin-sdk';
|
|
2
|
+
export { miGPTPlugin } from './src/channel.js';
|
|
3
|
+
export { getMiGPTRuntime, setMiGPTRuntime } from './src/runtime.js';
|
|
4
|
+
export { miGPTOnboardingAdapter } from './src/onboarding.js';
|
|
5
|
+
export { MiService, MiServiceConfig } from './src/service.js';
|
|
6
|
+
export { IMessage, MiMessage } from './src/message.js';
|
|
7
|
+
export { MiSpeaker } from './src/speaker.js';
|
|
8
|
+
export { ExtendedOpenClawConfig, MiGPTAccountConfig, MiGPTConfig, ResolvedMiAccount, applyMiAccountConfig, deleteMiAccount, formatMiAllowFrom, listMiAccountIds, resolveDefaultMiAccountId, resolveMiAccount, resolveMiAllowFrom, setMiAccountEnabled } from './src/config.js';
|
|
9
|
+
export { MiDevice, MiMessageEvent } from './src/types.js';
|
|
10
|
+
import './src/mi/mina.js';
|
|
11
|
+
import './src/mi/typing.js';
|
|
12
|
+
import './src/mi/miot.js';
|
|
13
|
+
import './src/tts/mimo.js';
|
|
14
|
+
|
|
15
|
+
declare const plugin: {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
configSchema: any;
|
|
20
|
+
register(api: OpenClawPluginApi): void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export { plugin as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
2
|
+
import { miGPTPlugin } from "./src/channel.js";
|
|
3
|
+
import { setMiGPTRuntime } from "./src/runtime.js";
|
|
4
|
+
const plugin = {
|
|
5
|
+
id: "migpt-claw",
|
|
6
|
+
name: "MiGPT",
|
|
7
|
+
description: "\u5C0F\u7C73\u5C0F\u7231\u97F3\u7BB1 OpenClaw Channel \u63D2\u4EF6",
|
|
8
|
+
configSchema: emptyPluginConfigSchema(),
|
|
9
|
+
register(api) {
|
|
10
|
+
setMiGPTRuntime(api.runtime);
|
|
11
|
+
api.registerChannel({ plugin: miGPTPlugin });
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var index_default = plugin;
|
|
15
|
+
import { miGPTPlugin as miGPTPlugin2 } from "./src/channel.js";
|
|
16
|
+
import { setMiGPTRuntime as setMiGPTRuntime2, getMiGPTRuntime } from "./src/runtime.js";
|
|
17
|
+
import { miGPTOnboardingAdapter } from "./src/onboarding.js";
|
|
18
|
+
import { MiService } from "./src/service.js";
|
|
19
|
+
import { MiMessage } from "./src/message.js";
|
|
20
|
+
import { MiSpeaker } from "./src/speaker.js";
|
|
21
|
+
export * from "./src/config.js";
|
|
22
|
+
export * from "./src/types.js";
|
|
23
|
+
export {
|
|
24
|
+
MiMessage,
|
|
25
|
+
MiService,
|
|
26
|
+
MiSpeaker,
|
|
27
|
+
index_default as default,
|
|
28
|
+
getMiGPTRuntime,
|
|
29
|
+
miGPTOnboardingAdapter,
|
|
30
|
+
miGPTPlugin2 as miGPTPlugin,
|
|
31
|
+
setMiGPTRuntime2 as setMiGPTRuntime
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../index.ts"],"sourcesContent":["import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport { emptyPluginConfigSchema } from 'openclaw/plugin-sdk';\nimport { miGPTPlugin } from './src/channel.js';\nimport { setMiGPTRuntime } from './src/runtime.js';\n\nconst plugin = {\n id: 'migpt-claw',\n name: 'MiGPT',\n description: '小米小爱音箱 OpenClaw Channel 插件',\n configSchema: emptyPluginConfigSchema(),\n register(api: OpenClawPluginApi) {\n setMiGPTRuntime(api.runtime);\n api.registerChannel({ plugin: miGPTPlugin });\n },\n};\n\nexport default plugin;\n\nexport { miGPTPlugin } from './src/channel.js';\nexport { setMiGPTRuntime, getMiGPTRuntime } from './src/runtime.js';\nexport { miGPTOnboardingAdapter } from './src/onboarding.js';\nexport { MiService, type MiServiceConfig } from './src/service.js';\nexport { MiMessage, type IMessage } from './src/message.js';\nexport { MiSpeaker } from './src/speaker.js';\nexport * from './src/config.js';\nexport * from './src/types.js';\n"],"mappings":"AACA,SAAS,+BAA+B;AACxC,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAEhC,MAAM,SAAS;AAAA,EACb,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,cAAc,wBAAwB;AAAA,EACtC,SAAS,KAAwB;AAC/B,oBAAgB,IAAI,OAAO;AAC3B,QAAI,gBAAgB,EAAE,QAAQ,YAAY,CAAC;AAAA,EAC7C;AACF;AAEA,IAAO,gBAAQ;AAEf,SAAS,eAAAA,oBAAmB;AAC5B,SAAS,mBAAAC,kBAAiB,uBAAuB;AACjD,SAAS,8BAA8B;AACvC,SAAS,iBAAuC;AAChD,SAAS,iBAAgC;AACzC,SAAS,iBAAiB;AAC1B,cAAc;AACd,cAAc;","names":["miGPTPlugin","setMiGPTRuntime"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
|
|
2
|
+
import { miGPTPlugin } from "./src/channel.js";
|
|
3
|
+
var setup_entry_default = defineSetupPluginEntry(miGPTPlugin);
|
|
4
|
+
export {
|
|
5
|
+
setup_entry_default as default
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=setup-entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../setup-entry.ts"],"sourcesContent":["/**\n * setup-entry.ts — 轻量级 Setup 入口\n *\n * OpenClaw 在以下场景仅加载此文件(不加载完整 index.ts):\n * - 频道已禁用但需要 setup 界面(onboarding、config repair)\n * - 频道已启用但未配置\n * - 延迟加载模式下,gateway 启动前仅需 setup 表面\n */\nimport { defineSetupPluginEntry } from 'openclaw/plugin-sdk/channel-core';\nimport { miGPTPlugin } from './src/channel.js';\n\nexport default defineSetupPluginEntry(miGPTPlugin);\n"],"mappings":"AAQA,SAAS,8BAA8B;AACvC,SAAS,mBAAmB;AAE5B,IAAO,sBAAQ,uBAAuB,WAAW;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ChannelPlugin } from 'openclaw/plugin-sdk';
|
|
2
|
+
import { ResolvedMiAccount } from './config.js';
|
|
3
|
+
import './service.js';
|
|
4
|
+
import './mi/mina.js';
|
|
5
|
+
import './mi/typing.js';
|
|
6
|
+
import './mi/miot.js';
|
|
7
|
+
|
|
8
|
+
declare const miGPTPlugin: ChannelPlugin<ResolvedMiAccount>;
|
|
9
|
+
|
|
10
|
+
export { miGPTPlugin };
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk";
|
|
2
|
+
import {
|
|
3
|
+
resolveMiAccount,
|
|
4
|
+
listMiAccountIds,
|
|
5
|
+
resolveDefaultMiAccountId,
|
|
6
|
+
setMiAccountEnabled,
|
|
7
|
+
deleteMiAccount,
|
|
8
|
+
resolveMiAllowFrom,
|
|
9
|
+
formatMiAllowFrom
|
|
10
|
+
} from "./config.js";
|
|
11
|
+
import { miOutbound } from "./outbound.js";
|
|
12
|
+
import { miGPTOnboardingAdapter } from "./onboarding.js";
|
|
13
|
+
import { MiService } from "./service.js";
|
|
14
|
+
import { MiMessage } from "./message.js";
|
|
15
|
+
import { sleep } from "./utils/parse.js";
|
|
16
|
+
import { Debugger } from "./utils/debug.js";
|
|
17
|
+
import { MiSpeaker } from "./speaker.js";
|
|
18
|
+
import { getMiGPTRuntime } from "./runtime.js";
|
|
19
|
+
import { MiMoTTS } from "./tts/mimo.js";
|
|
20
|
+
const meta = {
|
|
21
|
+
id: "migpt",
|
|
22
|
+
label: "MiGPT",
|
|
23
|
+
selectionLabel: "\u5C0F\u7C73\u97F3\u7BB1 (MiGPT)",
|
|
24
|
+
docsPath: "/channels/migpt",
|
|
25
|
+
docsLabel: "migpt",
|
|
26
|
+
blurb: "\u5C0F\u7C73\u5C0F\u7231\u97F3\u7BB1\u8BED\u97F3\u5BF9\u8BDD\u3002",
|
|
27
|
+
aliases: ["xiaomi", "mico"],
|
|
28
|
+
order: 60
|
|
29
|
+
};
|
|
30
|
+
const miGPTPlugin = {
|
|
31
|
+
id: "migpt",
|
|
32
|
+
meta: {
|
|
33
|
+
...meta
|
|
34
|
+
},
|
|
35
|
+
capabilities: {
|
|
36
|
+
chatTypes: ["direct"],
|
|
37
|
+
polls: false,
|
|
38
|
+
threads: false,
|
|
39
|
+
media: true,
|
|
40
|
+
reactions: false,
|
|
41
|
+
edit: false,
|
|
42
|
+
reply: false,
|
|
43
|
+
blockStreaming: false
|
|
44
|
+
},
|
|
45
|
+
reload: { configPrefixes: ["channels.migpt"] },
|
|
46
|
+
onboarding: miGPTOnboardingAdapter,
|
|
47
|
+
// 新增:Agent Prompt 配置,用于定制 AI 在音箱场景下的行为规范
|
|
48
|
+
agentPrompt: {
|
|
49
|
+
description: "\u97F3\u7BB1\u64AD\u62A5\u89C4\u8303",
|
|
50
|
+
getConfig: (cfg) => {
|
|
51
|
+
const migptCfg = cfg.channels?.migpt;
|
|
52
|
+
return {
|
|
53
|
+
enabled: true,
|
|
54
|
+
systemPrompt: migptCfg?.systemPrompt
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
applyConfig: (cfg, updates) => {
|
|
58
|
+
const nextCfg = { ...cfg };
|
|
59
|
+
const nextMigpt = { ...nextCfg.channels?.migpt };
|
|
60
|
+
if (updates.systemPrompt !== void 0) {
|
|
61
|
+
nextMigpt.systemPrompt = updates.systemPrompt;
|
|
62
|
+
}
|
|
63
|
+
nextCfg.channels = { ...nextCfg.channels, migpt: nextMigpt };
|
|
64
|
+
return nextCfg;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
config: {
|
|
68
|
+
listAccountIds: (cfg) => listMiAccountIds(cfg),
|
|
69
|
+
resolveAccount: (cfg, accountId) => resolveMiAccount(cfg, accountId),
|
|
70
|
+
defaultAccountId: (cfg) => resolveDefaultMiAccountId(cfg),
|
|
71
|
+
setAccountEnabled: ({ cfg, accountId, enabled }) => setMiAccountEnabled(cfg, accountId, enabled),
|
|
72
|
+
deleteAccount: ({ cfg, accountId }) => deleteMiAccount(cfg, accountId),
|
|
73
|
+
isConfigured: (account) => account.configured,
|
|
74
|
+
describeAccount: (account) => ({
|
|
75
|
+
accountId: account.accountId,
|
|
76
|
+
enabled: account.enabled,
|
|
77
|
+
configured: account.configured,
|
|
78
|
+
name: account.name,
|
|
79
|
+
devices: account.devices
|
|
80
|
+
}),
|
|
81
|
+
resolveAllowFrom: ({ cfg, accountId }) => resolveMiAllowFrom(cfg, accountId),
|
|
82
|
+
formatAllowFrom: ({ allowFrom }) => formatMiAllowFrom(allowFrom)
|
|
83
|
+
},
|
|
84
|
+
setup: {
|
|
85
|
+
resolveAccountId: ({ accountId }) => accountId?.trim().toLowerCase() || DEFAULT_ACCOUNT_ID,
|
|
86
|
+
applyAccountConfig: ({ cfg, accountId, input }) => {
|
|
87
|
+
const migptCfg = cfg.channels?.migpt ?? {};
|
|
88
|
+
const accountConfig = {
|
|
89
|
+
userId: input.userId,
|
|
90
|
+
password: input.password,
|
|
91
|
+
passToken: input.passToken,
|
|
92
|
+
devices: input.devices,
|
|
93
|
+
enabled: true
|
|
94
|
+
};
|
|
95
|
+
const isDefault = !accountId || accountId === DEFAULT_ACCOUNT_ID;
|
|
96
|
+
if (isDefault) {
|
|
97
|
+
return {
|
|
98
|
+
...cfg,
|
|
99
|
+
channels: {
|
|
100
|
+
...cfg.channels,
|
|
101
|
+
migpt: {
|
|
102
|
+
...migptCfg,
|
|
103
|
+
...accountConfig
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
...cfg,
|
|
110
|
+
channels: {
|
|
111
|
+
...cfg.channels,
|
|
112
|
+
migpt: {
|
|
113
|
+
...migptCfg,
|
|
114
|
+
accounts: {
|
|
115
|
+
...migptCfg.accounts,
|
|
116
|
+
[accountId]: accountConfig
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
validateInput: ({ input }) => {
|
|
123
|
+
if (!input.userId) {
|
|
124
|
+
return "\u5C0F\u7C73 ID (userId) \u662F\u5FC5\u9700\u7684";
|
|
125
|
+
}
|
|
126
|
+
if (!input.passToken && !input.password) {
|
|
127
|
+
return "\u9700\u8981\u63D0\u4F9B passToken \u6216 password";
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
messaging: {
|
|
133
|
+
normalizeTarget: (target) => {
|
|
134
|
+
let id = target.replace(/^migpt:/i, "");
|
|
135
|
+
if (id.trim()) {
|
|
136
|
+
return { ok: true, to: id.trim() };
|
|
137
|
+
}
|
|
138
|
+
return { ok: false, error: "Invalid target format" };
|
|
139
|
+
},
|
|
140
|
+
targetResolver: {
|
|
141
|
+
looksLikeId: (id) => {
|
|
142
|
+
return id.length > 0 && id.length < 100;
|
|
143
|
+
},
|
|
144
|
+
hint: "MiGPT \u76EE\u6807\u683C\u5F0F\uFF1A\u8BBE\u5907\u540D\u79F0\uFF08\u5982\uFF1A\u5BA2\u5385\u97F3\u7BB1\uFF09"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
outbound: miOutbound,
|
|
148
|
+
gateway: {
|
|
149
|
+
startAccount: async (ctx) => {
|
|
150
|
+
const { account, abortSignal, log, cfg } = ctx;
|
|
151
|
+
log?.info(`[migpt:${account.accountId}] Starting gateway`);
|
|
152
|
+
if (!account.configured) {
|
|
153
|
+
log?.error(`[migpt:${account.accountId}] Account not configured`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const devices = account.devices;
|
|
157
|
+
if (devices.length === 0) {
|
|
158
|
+
log?.error(`[migpt:${account.accountId}] No devices configured`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const mimoConfig = cfg.channels?.migpt?.mimo;
|
|
162
|
+
if (mimoConfig?.apiKey) {
|
|
163
|
+
await MiSpeaker.cleanupMiMoTTS();
|
|
164
|
+
const mimoTTS = new MiMoTTS({
|
|
165
|
+
apiKey: mimoConfig.apiKey,
|
|
166
|
+
baseUrl: mimoConfig.baseUrl,
|
|
167
|
+
model: mimoConfig.model,
|
|
168
|
+
voice: mimoConfig.voice,
|
|
169
|
+
style: mimoConfig.style,
|
|
170
|
+
stream: mimoConfig.stream,
|
|
171
|
+
port: mimoConfig.port,
|
|
172
|
+
host: mimoConfig.host
|
|
173
|
+
});
|
|
174
|
+
await mimoTTS.init();
|
|
175
|
+
await MiSpeaker.setMiMoTTS(mimoTTS);
|
|
176
|
+
log?.info(`[migpt:${account.accountId}] MiMo TTS \u5DF2\u542F\u7528 (server: ${mimoTTS.serverUrl})`);
|
|
177
|
+
}
|
|
178
|
+
const keepAliveTimeout = cfg.channels?.migpt?.keepAliveTimeout ?? 30;
|
|
179
|
+
let keepAlive = cfg.channels?.migpt?.keepAlive ?? false;
|
|
180
|
+
let keepAliveTimer = null;
|
|
181
|
+
const resetKeepAliveTimer = () => {
|
|
182
|
+
if (keepAliveTimer) clearTimeout(keepAliveTimer);
|
|
183
|
+
if (keepAlive) {
|
|
184
|
+
keepAliveTimer = setTimeout(() => {
|
|
185
|
+
keepAlive = false;
|
|
186
|
+
log?.info(`[migpt:${account.accountId}] \u6301\u7EED\u5BF9\u8BDD\u8D85\u65F6 (${keepAliveTimeout}s)\uFF0C\u9000\u51FA`);
|
|
187
|
+
}, keepAliveTimeout * 1e3);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const enterKeepAlive = () => {
|
|
191
|
+
keepAlive = true;
|
|
192
|
+
resetKeepAliveTimer();
|
|
193
|
+
log?.info(`[migpt:${account.accountId}] \u8FDB\u5165\u6301\u7EED\u5BF9\u8BDD\u6A21\u5F0F`);
|
|
194
|
+
};
|
|
195
|
+
const exitKeepAlive = () => {
|
|
196
|
+
keepAlive = false;
|
|
197
|
+
if (keepAliveTimer) {
|
|
198
|
+
clearTimeout(keepAliveTimer);
|
|
199
|
+
keepAliveTimer = null;
|
|
200
|
+
}
|
|
201
|
+
log?.info(`[migpt:${account.accountId}] \u9000\u51FA\u6301\u7EED\u5BF9\u8BDD\u6A21\u5F0F`);
|
|
202
|
+
};
|
|
203
|
+
const devicePromises = devices.map(async (deviceName) => {
|
|
204
|
+
log?.info(`[migpt:${account.accountId}] Starting poller for device: ${deviceName}`);
|
|
205
|
+
const initSuccess = await MiService.init({
|
|
206
|
+
...account.config,
|
|
207
|
+
announceOnStart: account.config.announceOnStart ?? cfg.channels?.migpt?.announceOnStart,
|
|
208
|
+
startupMessage: account.config.startupMessage ?? cfg.channels?.migpt?.startupMessage
|
|
209
|
+
}, deviceName);
|
|
210
|
+
if (!initSuccess) {
|
|
211
|
+
log?.error(`[migpt:${account.accountId}] Failed to initialize device: ${deviceName}`);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
Debugger.debug = account.config.debug ?? false;
|
|
215
|
+
ctx.setStatus({
|
|
216
|
+
...ctx.getStatus(),
|
|
217
|
+
running: true,
|
|
218
|
+
connected: true,
|
|
219
|
+
lastConnectedAt: Date.now()
|
|
220
|
+
});
|
|
221
|
+
const heartbeat = cfg.channels?.migpt?.heartbeat ?? 1e3;
|
|
222
|
+
const enterKeywords = cfg.channels?.migpt?.keepAliveEnterKeywords ?? ["\u6253\u5F00\u8FDE\u7EED\u5BF9\u8BDD", "\u8FDB\u5165\u6301\u7EED\u5BF9\u8BDD", "\u5F00\u542F\u6301\u7EED\u5BF9\u8BDD", "\u6301\u7EED\u5BF9\u8BDD\u6A21\u5F0F"];
|
|
223
|
+
const exitKeywords = cfg.channels?.migpt?.keepAliveExitKeywords ?? ["\u5173\u95ED\u8FDE\u7EED\u5BF9\u8BDD", "\u9000\u51FA\u6301\u7EED\u5BF9\u8BDD", "\u9000\u51FA\u6301\u7EED\u5BF9\u8BDD\u6A21\u5F0F", "\u518D\u89C1"];
|
|
224
|
+
while (!abortSignal.aborted) {
|
|
225
|
+
try {
|
|
226
|
+
const msg = await MiMessage.fetchNextMessage(deviceName);
|
|
227
|
+
if (msg) {
|
|
228
|
+
log?.info(`[migpt:${account.accountId}] Received message from ${deviceName}: ${msg.text.slice(0, 50)}...`);
|
|
229
|
+
if (enterKeywords.some((kw) => msg.text.includes(kw))) {
|
|
230
|
+
enterKeepAlive();
|
|
231
|
+
const enterResult = await MiSpeaker.play({ text: "\u5DF2\u8FDB\u5165\u6301\u7EED\u5BF9\u8BDD\u6A21\u5F0F" });
|
|
232
|
+
const waitMs = enterResult.duration ? Math.ceil(enterResult.duration * 1e3) + 200 : 4e3;
|
|
233
|
+
log?.info(`[migpt:${account.accountId}] \u7B49\u5F85\u97F3\u9891\u64AD\u653E\u5B8C\u6210: ${waitMs}ms`);
|
|
234
|
+
await sleep(waitMs);
|
|
235
|
+
await MiService.wakeUp();
|
|
236
|
+
log?.info(`[migpt:${account.accountId}] \u6301\u7EED\u5BF9\u8BDD\uFF1A\u5DF2\u5524\u9192\u97F3\u7BB1\u7B49\u5F85\u4E0B\u4E00\u53E5`);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (exitKeywords.some((kw) => msg.text.includes(kw))) {
|
|
240
|
+
exitKeepAlive();
|
|
241
|
+
await MiSpeaker.play({ text: "\u5DF2\u9000\u51FA\u6301\u7EED\u5BF9\u8BDD\u6A21\u5F0F" });
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const keepAliveKeywords = [...enterKeywords, ...exitKeywords];
|
|
245
|
+
if (keepAliveKeywords.some((kw) => msg.text.includes(kw))) {
|
|
246
|
+
log?.info(`[migpt:${account.accountId}] \u8C41\u514D\u6301\u7EED\u5BF9\u8BDD\u6307\u4EE4: ${msg.text.slice(0, 30)}...`);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const hardwareControlVerbs = cfg.channels?.migpt?.hardwareControlVerbs ?? [
|
|
250
|
+
"\u64AD\u653E",
|
|
251
|
+
"\u6253\u5F00",
|
|
252
|
+
"\u5173\u95ED",
|
|
253
|
+
"\u6682\u505C",
|
|
254
|
+
"\u7EE7\u7EED",
|
|
255
|
+
"\u505C\u6B62",
|
|
256
|
+
"\u5207\u6362",
|
|
257
|
+
"\u5F00\u542F",
|
|
258
|
+
"\u5173\u6389",
|
|
259
|
+
"\u5F00",
|
|
260
|
+
"\u5173",
|
|
261
|
+
"\u542F\u52A8",
|
|
262
|
+
"\u8C03\u8282",
|
|
263
|
+
"\u8C03\u9AD8",
|
|
264
|
+
"\u8C03\u4F4E",
|
|
265
|
+
"\u8C03\u5927",
|
|
266
|
+
"\u8C03\u5C0F",
|
|
267
|
+
"\u8C03\u4EAE",
|
|
268
|
+
"\u8C03\u6697",
|
|
269
|
+
"\u589E\u5927",
|
|
270
|
+
"\u51CF\u5C0F",
|
|
271
|
+
"\u8BBE\u7F6E",
|
|
272
|
+
"\u8C03\u5230",
|
|
273
|
+
"\u5BFC\u822A",
|
|
274
|
+
"\u62E8\u6253",
|
|
275
|
+
"\u6253\u7535\u8BDD",
|
|
276
|
+
"\u547C\u53EB",
|
|
277
|
+
"\u53D1\u77ED\u4FE1",
|
|
278
|
+
"\u53D1\u6D88\u606F"
|
|
279
|
+
];
|
|
280
|
+
const hardwareControlPattern = new RegExp(
|
|
281
|
+
`^((${hardwareControlVerbs.join("|")})|.+(${hardwareControlVerbs.join("|")})).+`
|
|
282
|
+
);
|
|
283
|
+
const isHardwareControl = hardwareControlPattern.test(msg.text.trim());
|
|
284
|
+
if (isHardwareControl) {
|
|
285
|
+
log?.info(`[migpt:${account.accountId}] \u8DF3\u8FC7\u786C\u4EF6\u63A7\u5236\u6307\u4EE4: ${msg.text.slice(0, 30)}...`);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
await MiSpeaker.pause();
|
|
290
|
+
} catch {
|
|
291
|
+
}
|
|
292
|
+
if (keepAlive) {
|
|
293
|
+
resetKeepAliveTimer();
|
|
294
|
+
}
|
|
295
|
+
const pluginRuntime = getMiGPTRuntime();
|
|
296
|
+
pluginRuntime.channel.activity.record({
|
|
297
|
+
channel: "migpt",
|
|
298
|
+
accountId: account.accountId,
|
|
299
|
+
direction: "inbound"
|
|
300
|
+
});
|
|
301
|
+
const fromAddress = `migpt:${deviceName}`;
|
|
302
|
+
const toAddress = `migpt:${account.accountId}`;
|
|
303
|
+
const sessionKey = `${account.accountId}:${deviceName}`;
|
|
304
|
+
const systemPrompts = [];
|
|
305
|
+
if (account.config.systemPrompt) {
|
|
306
|
+
systemPrompts.push(account.config.systemPrompt);
|
|
307
|
+
}
|
|
308
|
+
const globalSystemPrompt = cfg.channels?.migpt?.systemPrompt;
|
|
309
|
+
if (globalSystemPrompt && globalSystemPrompt !== account.config.systemPrompt) {
|
|
310
|
+
systemPrompts.push(globalSystemPrompt);
|
|
311
|
+
}
|
|
312
|
+
const envelopeOptions = pluginRuntime.channel.reply.resolveEnvelopeFormatOptions(cfg);
|
|
313
|
+
const body = pluginRuntime.channel.reply.formatInboundEnvelope({
|
|
314
|
+
Body: msg.text,
|
|
315
|
+
BodyForAgent: msg.text,
|
|
316
|
+
From: fromAddress,
|
|
317
|
+
To: toAddress,
|
|
318
|
+
SessionKey: sessionKey,
|
|
319
|
+
ChatType: "direct",
|
|
320
|
+
SenderId: deviceName,
|
|
321
|
+
SenderName: deviceName,
|
|
322
|
+
Provider: "migpt",
|
|
323
|
+
Surface: "migpt",
|
|
324
|
+
MessageSid: `${deviceName}-${msg.timestamp}`,
|
|
325
|
+
Timestamp: msg.timestamp,
|
|
326
|
+
OriginatingChannel: "migpt",
|
|
327
|
+
envelopeOptions
|
|
328
|
+
});
|
|
329
|
+
const DEFAULT_SPEAKER_PROMPT = `\u4F60\u662F\u5C0F\u7C73\u667A\u80FD\u97F3\u7BB1\u52A9\u624B\uFF0C\u7528\u7B80\u77ED\u53E3\u8BED\u56DE\u590D\u3002\u89C4\u5219\uFF1A\u666E\u901A\u5BF9\u8BDD\u56DE\u590D 50 \u5B57\u4EE5\u5185\uFF0C\u8BB2\u6545\u4E8B\u6216\u67E5\u8D44\u6599\u53EF\u4EE5\u9002\u5F53\u653E\u5BBD\u9650\u5236\u4E0D\u8D85\u8FC7 200 \u5B57\uFF1B\u4E0D\u7528 URL/\u4EE3\u7801/emoji/markdown \u683C\u5F0F\u3002`;
|
|
330
|
+
const contextInfo = `\u4F60\u6B63\u5728\u901A\u8FC7\u5C0F\u7C73\u97F3\u7BB1\u4E0E\u7528\u6237\u5BF9\u8BDD\u3002
|
|
331
|
+
|
|
332
|
+
\u3010\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u3011
|
|
333
|
+
- \u8BBE\u5907\uFF1A${deviceName}
|
|
334
|
+
- \u7528\u6237\uFF1A${deviceName}
|
|
335
|
+
- \u6D88\u606F ID: ${deviceName}-${msg.timestamp}
|
|
336
|
+
- \u5F53\u524D\u65F6\u95F4\uFF1A${new Date(msg.timestamp).toLocaleString("zh-CN")}`;
|
|
337
|
+
const agentBody = systemPrompts.length > 0 ? `${contextInfo}
|
|
338
|
+
|
|
339
|
+
${systemPrompts.join("\n\n")}
|
|
340
|
+
|
|
341
|
+
${msg.text}` : `${contextInfo}
|
|
342
|
+
|
|
343
|
+
${DEFAULT_SPEAKER_PROMPT}
|
|
344
|
+
|
|
345
|
+
${msg.text}`;
|
|
346
|
+
const ctxPayload = pluginRuntime.channel.reply.finalizeInboundContext({
|
|
347
|
+
Body: body,
|
|
348
|
+
BodyForAgent: agentBody,
|
|
349
|
+
RawBody: msg.text,
|
|
350
|
+
CommandBody: msg.text,
|
|
351
|
+
From: fromAddress,
|
|
352
|
+
To: toAddress,
|
|
353
|
+
SessionKey: sessionKey,
|
|
354
|
+
AccountId: account.accountId,
|
|
355
|
+
ChatType: "direct",
|
|
356
|
+
SenderId: deviceName,
|
|
357
|
+
SenderName: deviceName,
|
|
358
|
+
Provider: "migpt",
|
|
359
|
+
Surface: "migpt",
|
|
360
|
+
MessageSid: `${deviceName}-${msg.timestamp}`,
|
|
361
|
+
Timestamp: msg.timestamp,
|
|
362
|
+
OriginatingChannel: "migpt",
|
|
363
|
+
OriginatingTo: toAddress,
|
|
364
|
+
CommandAuthorized: true
|
|
365
|
+
});
|
|
366
|
+
await pluginRuntime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
367
|
+
ctx: ctxPayload,
|
|
368
|
+
cfg,
|
|
369
|
+
dispatcherOptions: {
|
|
370
|
+
responsePrefix: "",
|
|
371
|
+
deliver: async (payload, info) => {
|
|
372
|
+
log?.info(`[migpt:${account.accountId}] deliver called, kind: ${info.kind}`);
|
|
373
|
+
if (payload.text) {
|
|
374
|
+
const playResult = await MiSpeaker.play({ text: payload.text });
|
|
375
|
+
const exitKeywordsInReply = ["\u9000\u51FA", "\u5173\u95ED", "\u518D\u89C1", "\u5DF2\u9000\u51FA", "\u5DF2\u5173\u95ED"];
|
|
376
|
+
if (exitKeywordsInReply.some((kw) => payload.text.includes(kw))) {
|
|
377
|
+
exitKeepAlive();
|
|
378
|
+
log?.info(`[migpt:${account.accountId}] AI \u56DE\u590D\u5305\u542B\u9000\u51FA\u5173\u952E\u8BCD\uFF0C\u9000\u51FA\u6301\u7EED\u5BF9\u8BDD`);
|
|
379
|
+
}
|
|
380
|
+
if (keepAlive) {
|
|
381
|
+
const waitMs = playResult.duration ? Math.ceil(playResult.duration * 1e3) + 200 : 2e3;
|
|
382
|
+
log?.info(`[migpt:${account.accountId}] \u7B49\u5F85\u97F3\u9891\u64AD\u653E\u5B8C\u6210: ${waitMs}ms`);
|
|
383
|
+
await sleep(waitMs);
|
|
384
|
+
await MiService.wakeUp();
|
|
385
|
+
log?.info(`[migpt:${account.accountId}] \u6301\u7EED\u5BF9\u8BDD\uFF1A\u5DF2\u5524\u9192\u97F3\u7BB1\u7B49\u5F85\u4E0B\u4E00\u53E5`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
} catch (err) {
|
|
393
|
+
log?.error(`[migpt:${account.accountId}] Error polling messages: ${err.message}`);
|
|
394
|
+
ctx.setStatus({
|
|
395
|
+
...ctx.getStatus(),
|
|
396
|
+
lastError: err.message
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
await sleep(heartbeat);
|
|
400
|
+
}
|
|
401
|
+
log?.info(`[migpt:${account.accountId}] Stopping poller for device: ${deviceName}`);
|
|
402
|
+
});
|
|
403
|
+
await Promise.all(devicePromises);
|
|
404
|
+
if (keepAliveTimer) {
|
|
405
|
+
clearTimeout(keepAliveTimer);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
status: {
|
|
410
|
+
defaultRuntime: {
|
|
411
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
412
|
+
running: false,
|
|
413
|
+
connected: false,
|
|
414
|
+
lastConnectedAt: null,
|
|
415
|
+
lastError: null,
|
|
416
|
+
lastInboundAt: null,
|
|
417
|
+
lastOutboundAt: null
|
|
418
|
+
},
|
|
419
|
+
buildChannelSummary: ({ snapshot }) => ({
|
|
420
|
+
configured: snapshot.configured ?? false,
|
|
421
|
+
running: snapshot.running ?? false,
|
|
422
|
+
connected: snapshot.connected ?? false,
|
|
423
|
+
lastConnectedAt: snapshot.lastConnectedAt ?? null,
|
|
424
|
+
lastError: snapshot.lastError ?? null
|
|
425
|
+
}),
|
|
426
|
+
buildAccountSnapshot: ({ account, runtime }) => ({
|
|
427
|
+
accountId: account?.accountId ?? DEFAULT_ACCOUNT_ID,
|
|
428
|
+
name: account?.name,
|
|
429
|
+
enabled: account?.enabled ?? false,
|
|
430
|
+
configured: Boolean(account?.configured),
|
|
431
|
+
devices: account?.devices,
|
|
432
|
+
running: runtime?.running ?? false,
|
|
433
|
+
connected: runtime?.connected ?? false,
|
|
434
|
+
lastConnectedAt: runtime?.lastConnectedAt ?? null,
|
|
435
|
+
lastError: runtime?.lastError ?? null,
|
|
436
|
+
lastInboundAt: runtime?.lastInboundAt ?? null,
|
|
437
|
+
lastOutboundAt: runtime?.lastOutboundAt ?? null
|
|
438
|
+
})
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
export {
|
|
442
|
+
miGPTPlugin
|
|
443
|
+
};
|
|
444
|
+
//# sourceMappingURL=channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/channel.ts"],"sourcesContent":["import type { ChannelPlugin } from 'openclaw/plugin-sdk';\nimport { DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk';\nimport type { ResolvedMiAccount, ExtendedOpenClawConfig } from './types.js';\nimport {\n resolveMiAccount,\n listMiAccountIds,\n resolveDefaultMiAccountId,\n setMiAccountEnabled,\n deleteMiAccount,\n resolveMiAllowFrom,\n formatMiAllowFrom,\n} from './config.js';\nimport { miOutbound } from './outbound.js';\nimport { miGPTOnboardingAdapter } from './onboarding.js';\nimport { MiService } from './service.js';\nimport { MiMessage } from './message.js';\nimport { sleep } from './utils/parse.js';\nimport { Debugger } from './utils/debug.js';\nimport { MiSpeaker } from './speaker.js';\nimport { getMiGPTRuntime } from './runtime.js';\nimport { MiMoTTS } from './tts/mimo.js';\n\nconst meta = {\n id: 'migpt',\n label: 'MiGPT',\n selectionLabel: '小米音箱 (MiGPT)',\n docsPath: '/channels/migpt',\n docsLabel: 'migpt',\n blurb: '小米小爱音箱语音对话。',\n aliases: ['xiaomi', 'mico'],\n order: 60,\n};\n\nexport const miGPTPlugin: ChannelPlugin<ResolvedMiAccount> = {\n id: 'migpt',\n meta: {\n ...meta,\n },\n capabilities: {\n chatTypes: ['direct'],\n polls: false,\n threads: false,\n media: true,\n reactions: false,\n edit: false,\n reply: false,\n blockStreaming: false,\n },\n reload: { configPrefixes: ['channels.migpt'] },\n onboarding: miGPTOnboardingAdapter,\n\n // 新增:Agent Prompt 配置,用于定制 AI 在音箱场景下的行为规范\n agentPrompt: {\n description: '音箱播报规范',\n getConfig: (cfg: any) => {\n const migptCfg = cfg.channels?.migpt;\n return {\n enabled: true,\n systemPrompt: migptCfg?.systemPrompt,\n };\n },\n applyConfig: (cfg: any, updates: any) => {\n const nextCfg = { ...cfg } as ExtendedOpenClawConfig;\n const nextMigpt = { ...nextCfg.channels?.migpt };\n if (updates.systemPrompt !== undefined) {\n nextMigpt.systemPrompt = updates.systemPrompt;\n }\n nextCfg.channels = { ...nextCfg.channels, migpt: nextMigpt };\n return nextCfg;\n },\n },\n\n config: {\n listAccountIds: (cfg) => listMiAccountIds(cfg as unknown as ExtendedOpenClawConfig),\n resolveAccount: (cfg, accountId) =>\n resolveMiAccount(cfg as unknown as ExtendedOpenClawConfig, accountId),\n defaultAccountId: (cfg) => resolveDefaultMiAccountId(cfg as unknown as ExtendedOpenClawConfig),\n setAccountEnabled: ({ cfg, accountId, enabled }) =>\n setMiAccountEnabled(cfg as unknown as ExtendedOpenClawConfig, accountId, enabled),\n deleteAccount: ({ cfg, accountId }) =>\n deleteMiAccount(cfg as unknown as ExtendedOpenClawConfig, accountId),\n isConfigured: (account) => account.configured,\n describeAccount: (account) => ({\n accountId: account.accountId,\n enabled: account.enabled,\n configured: account.configured,\n name: account.name,\n devices: account.devices,\n }),\n resolveAllowFrom: ({ cfg, accountId }: { cfg: any; accountId?: string }) =>\n resolveMiAllowFrom(cfg as unknown as ExtendedOpenClawConfig, accountId),\n formatAllowFrom: ({ allowFrom }: { allowFrom: Array<string | number> }) => formatMiAllowFrom(allowFrom),\n },\n\n setup: {\n resolveAccountId: ({ accountId }: { accountId?: string }) => accountId?.trim().toLowerCase() || DEFAULT_ACCOUNT_ID,\n applyAccountConfig: ({ cfg, accountId, input }: { cfg: any; accountId?: string; input: any }) => {\n const migptCfg = cfg.channels?.migpt ?? {};\n const accountConfig = {\n userId: input.userId,\n password: input.password,\n passToken: input.passToken,\n devices: input.devices,\n enabled: true,\n };\n\n const isDefault = !accountId || accountId === DEFAULT_ACCOUNT_ID;\n\n if (isDefault) {\n return {\n ...cfg,\n channels: {\n ...cfg.channels,\n migpt: {\n ...migptCfg,\n ...accountConfig,\n },\n },\n } as ExtendedOpenClawConfig;\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 } as ExtendedOpenClawConfig;\n },\n validateInput: ({ input }: { input: any }) => {\n if (!input.userId) {\n return '小米 ID (userId) 是必需的';\n }\n if (!input.passToken && !input.password) {\n return '需要提供 passToken 或 password';\n }\n return null;\n },\n },\n\n messaging: {\n normalizeTarget: (target: string) => {\n // 支持格式:migpt:客厅音箱 或 客厅音箱\n let id = target.replace(/^migpt:/i, '');\n if (id.trim()) {\n return { ok: true, to: id.trim() };\n }\n return { ok: false, error: 'Invalid target format' };\n },\n targetResolver: {\n looksLikeId: (id: string): boolean => {\n // 简单的启发式判断:非空字符串\n return id.length > 0 && id.length < 100;\n },\n hint: 'MiGPT 目标格式:设备名称(如:客厅音箱)',\n },\n },\n\n outbound: miOutbound,\n\n gateway: {\n startAccount: async (ctx) => {\n const { account, abortSignal, log, cfg } = ctx;\n\n log?.info(`[migpt:${account.accountId}] Starting gateway`);\n\n if (!account.configured) {\n log?.error(`[migpt:${account.accountId}] Account not configured`);\n return;\n }\n\n // 获取设备列表\n const devices = account.devices;\n if (devices.length === 0) {\n log?.error(`[migpt:${account.accountId}] No devices configured`);\n return;\n }\n\n // ============ MiMo TTS 初始化(如果配置了) ============\n const mimoConfig = (cfg as any).channels?.migpt?.mimo;\n if (mimoConfig?.apiKey) {\n // 清理旧实例,防止端口泄漏\n await MiSpeaker.cleanupMiMoTTS();\n\n const mimoTTS = new MiMoTTS({\n apiKey: mimoConfig.apiKey,\n baseUrl: mimoConfig.baseUrl,\n model: mimoConfig.model,\n voice: mimoConfig.voice,\n style: mimoConfig.style,\n stream: mimoConfig.stream,\n port: mimoConfig.port,\n host: mimoConfig.host,\n });\n await mimoTTS.init();\n await MiSpeaker.setMiMoTTS(mimoTTS);\n log?.info(`[migpt:${account.accountId}] MiMo TTS 已启用 (server: ${mimoTTS.serverUrl})`);\n }\n\n // ============ 持续对话状态 ============\n const keepAliveTimeout = (cfg as any).channels?.migpt?.keepAliveTimeout ?? 30;\n let keepAlive = (cfg as any).channels?.migpt?.keepAlive ?? false;\n let keepAliveTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * 重置持续对话超时计时器\n * 超时后自动退出持续对话模式\n */\n const resetKeepAliveTimer = () => {\n if (keepAliveTimer) clearTimeout(keepAliveTimer);\n if (keepAlive) {\n keepAliveTimer = setTimeout(() => {\n keepAlive = false;\n log?.info(`[migpt:${account.accountId}] 持续对话超时 (${keepAliveTimeout}s),退出`);\n }, keepAliveTimeout * 1000);\n }\n };\n\n /**\n * 进入持续对话模式\n */\n const enterKeepAlive = () => {\n keepAlive = true;\n resetKeepAliveTimer();\n log?.info(`[migpt:${account.accountId}] 进入持续对话模式`);\n };\n\n /**\n * 退出持续对话模式\n */\n const exitKeepAlive = () => {\n keepAlive = false;\n if (keepAliveTimer) {\n clearTimeout(keepAliveTimer);\n keepAliveTimer = null;\n }\n log?.info(`[migpt:${account.accountId}] 退出持续对话模式`);\n };\n\n // 为每个设备启动轮询\n const devicePromises = devices.map(async (deviceName: string) => {\n log?.info(`[migpt:${account.accountId}] Starting poller for device: ${deviceName}`);\n\n // 初始化服务(传递启动播报配置)\n const initSuccess = await MiService.init({\n ...account.config,\n announceOnStart: account.config.announceOnStart ?? cfg.channels?.migpt?.announceOnStart,\n startupMessage: account.config.startupMessage ?? cfg.channels?.migpt?.startupMessage,\n }, deviceName);\n if (!initSuccess) {\n log?.error(`[migpt:${account.accountId}] Failed to initialize device: ${deviceName}`);\n return;\n }\n\n // 设置调试模式和音箱控制方式\n Debugger.debug = account.config.debug ?? false;\n\n // 更新状态\n ctx.setStatus({\n ...ctx.getStatus(),\n running: true,\n connected: true,\n lastConnectedAt: Date.now(),\n });\n\n // 获取轮询间隔\n const heartbeat = cfg.channels?.migpt?.heartbeat ?? 1000;\n\n // 持续对话唤醒关键词\n const enterKeywords: string[] = (cfg as any).channels?.migpt?.keepAliveEnterKeywords ?? ['打开连续对话', '进入持续对话', '开启持续对话', '持续对话模式'];\n const exitKeywords: string[] = (cfg as any).channels?.migpt?.keepAliveExitKeywords ?? ['关闭连续对话', '退出持续对话', '退出持续对话模式', '再见'];\n\n // 轮询消息\n while (!abortSignal.aborted) {\n try {\n const msg = await MiMessage.fetchNextMessage(deviceName);\n if (msg) {\n log?.info(`[migpt:${account.accountId}] Received message from ${deviceName}: ${msg.text.slice(0, 50)}...`);\n\n // ============ 持续对话关键词检测 ============\n if (enterKeywords.some(kw => msg.text.includes(kw))) {\n enterKeepAlive();\n const enterResult = await MiSpeaker.play({ text: '已进入持续对话模式' });\n // 等音频播放完成后再唤醒,确保语音完整播放\n const waitMs = enterResult.duration ? Math.ceil(enterResult.duration * 1000) + 200 : 4000;\n log?.info(`[migpt:${account.accountId}] 等待音频播放完成: ${waitMs}ms`);\n await sleep(waitMs);\n await MiService.wakeUp();\n log?.info(`[migpt:${account.accountId}] 持续对话:已唤醒音箱等待下一句`);\n continue;\n }\n if (exitKeywords.some(kw => msg.text.includes(kw))) {\n exitKeepAlive();\n await MiSpeaker.play({ text: '已退出持续对话模式' });\n continue;\n }\n\n // ============ 硬件控制指令过滤 ============\n // 这些指令应该由小爱原生处理,不发送给 openclaw\n \n // 豁免:持续对话控制指令\n const keepAliveKeywords = [...enterKeywords, ...exitKeywords];\n if (keepAliveKeywords.some(kw => msg.text.includes(kw))) {\n log?.info(`[migpt:${account.accountId}] 豁免持续对话指令: ${msg.text.slice(0, 30)}...`);\n continue;\n }\n \n // 从配置读取硬件控制动词关键词,默认值包含常见动词\n const hardwareControlVerbs: string[] = (cfg as any).channels?.migpt?.hardwareControlVerbs ?? [\n '播放', '打开', '关闭', '暂停', '继续', '停止', '切换',\n '开启', '关掉', '开', '关', '启动', '调节',\n '调高', '调低', '调大', '调小', '调亮', '调暗', '增大', '减小', '设置', '调到',\n '导航', '拨打', '打电话', '呼叫', '发短信', '发消息',\n ];\n \n // 构建正则表达式:支持两种模式\n // 模式1:动词关键词 + 内容(如\"打开灯\"、\"播放音乐\")\n // 模式2:任意内容 + 动词关键词 + 内容(如\"大白调小一些\"、\"吸顶灯调亮一些\")\n const hardwareControlPattern = new RegExp(\n `^((${hardwareControlVerbs.join('|')})|.+(${hardwareControlVerbs.join('|')})).+`\n );\n \n const isHardwareControl = hardwareControlPattern.test(msg.text.trim());\n if (isHardwareControl) {\n log?.info(`[migpt:${account.accountId}] 跳过硬件控制指令: ${msg.text.slice(0, 30)}...`);\n continue;\n }\n\n // ============ 抢答抑制:立即暂停小爱原生回复 ============\n try {\n await MiSpeaker.pause();\n } catch {\n // 忽略暂停失败\n }\n\n // 重置持续对话计时器\n if (keepAlive) {\n resetKeepAliveTimer();\n }\n\n // 记录活动\n const pluginRuntime = getMiGPTRuntime();\n pluginRuntime.channel.activity.record({\n channel: 'migpt',\n accountId: account.accountId,\n direction: 'inbound',\n });\n\n // 构建路由\n const fromAddress = `migpt:${deviceName}`;\n const toAddress = `migpt:${account.accountId}`;\n const sessionKey = `${account.accountId}:${deviceName}`;\n\n // ============ 系统提示词注入 ============\n // 收集系统提示词(账户级别 + 全局)\n const systemPrompts: string[] = [];\n \n // 账户级别的 systemPrompt\n if (account.config.systemPrompt) {\n systemPrompts.push(account.config.systemPrompt);\n }\n \n // 全局 systemPrompt\n const globalSystemPrompt = (cfg as any).channels?.migpt?.systemPrompt;\n if (globalSystemPrompt && globalSystemPrompt !== account.config.systemPrompt) {\n systemPrompts.push(globalSystemPrompt);\n }\n\n // 构建消息体\n const envelopeOptions = pluginRuntime.channel.reply.resolveEnvelopeFormatOptions(cfg);\n const body = pluginRuntime.channel.reply.formatInboundEnvelope({\n Body: msg.text,\n BodyForAgent: msg.text,\n From: fromAddress,\n To: toAddress,\n SessionKey: sessionKey,\n ChatType: 'direct',\n SenderId: deviceName,\n SenderName: deviceName,\n Provider: 'migpt',\n Surface: 'migpt',\n MessageSid: `${deviceName}-${msg.timestamp}`,\n Timestamp: msg.timestamp,\n OriginatingChannel: 'migpt',\n envelopeOptions,\n });\n\n // 默认的音箱场景提示词(如果没有配置 systemPrompt)\n const DEFAULT_SPEAKER_PROMPT = `你是小米智能音箱助手,用简短口语回复。规则:普通对话回复 50 字以内,讲故事或查资料可以适当放宽限制不超过 200 字;不用 URL/代码/emoji/markdown 格式。`;\n\n // 构建 AI 看到的完整上下文\n const contextInfo = `你正在通过小米音箱与用户对话。\n\n【会话上下文】\n- 设备:${deviceName}\n- 用户:${deviceName}\n- 消息 ID: ${deviceName}-${msg.timestamp}\n- 当前时间:${new Date(msg.timestamp).toLocaleString('zh-CN')}`;\n\n // BodyForAgent: AI 实际看到的完整上下文(动态数据 + 系统提示 + 用户输入)\n const agentBody = systemPrompts.length > 0\n ? `${contextInfo}\\n\\n${systemPrompts.join(\"\\n\\n\")}\\n\\n${msg.text}`\n : `${contextInfo}\\n\\n${DEFAULT_SPEAKER_PROMPT}\\n\\n${msg.text}`;\n\n // 构建上下文\n const ctxPayload = pluginRuntime.channel.reply.finalizeInboundContext({\n Body: body,\n BodyForAgent: agentBody,\n RawBody: msg.text,\n CommandBody: msg.text,\n From: fromAddress,\n To: toAddress,\n SessionKey: sessionKey,\n AccountId: account.accountId,\n ChatType: 'direct',\n SenderId: deviceName,\n SenderName: deviceName,\n Provider: 'migpt',\n Surface: 'migpt',\n MessageSid: `${deviceName}-${msg.timestamp}`,\n Timestamp: msg.timestamp,\n OriginatingChannel: 'migpt',\n OriginatingTo: toAddress,\n CommandAuthorized: true,\n });\n\n // 分派消息到 OpenClaw\n await pluginRuntime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({\n ctx: ctxPayload,\n cfg,\n dispatcherOptions: {\n responsePrefix: '',\n deliver: async (payload: { text?: string; mediaUrls?: string[]; mediaUrl?: string }, info: { kind: string }) => {\n log?.info(`[migpt:${account.accountId}] deliver called, kind: ${info.kind}`);\n if (payload.text) {\n const playResult = await MiSpeaker.play({ text: payload.text });\n\n // ============ 检测 AI 回复是否包含退出关键词 ============\n const exitKeywordsInReply = ['退出', '关闭', '再见', '已退出', '已关闭'];\n if (exitKeywordsInReply.some(kw => payload.text!.includes(kw))) {\n exitKeepAlive();\n log?.info(`[migpt:${account.accountId}] AI 回复包含退出关键词,退出持续对话`);\n }\n\n // ============ 持续对话:AI 回复后重新唤醒音箱 ============\n if (keepAlive) {\n // 等音频播放完成后再唤醒,确保语音完整播放\n const waitMs = playResult.duration ? Math.ceil(playResult.duration * 1000) + 200 : 2000;\n log?.info(`[migpt:${account.accountId}] 等待音频播放完成: ${waitMs}ms`);\n await sleep(waitMs);\n await MiService.wakeUp();\n log?.info(`[migpt:${account.accountId}] 持续对话:已唤醒音箱等待下一句`);\n }\n }\n },\n },\n });\n }\n } catch (err: any) {\n log?.error(`[migpt:${account.accountId}] Error polling messages: ${err.message}`);\n ctx.setStatus({\n ...ctx.getStatus(),\n lastError: err.message,\n });\n }\n\n await sleep(heartbeat);\n }\n\n log?.info(`[migpt:${account.accountId}] Stopping poller for device: ${deviceName}`);\n });\n\n await Promise.all(devicePromises);\n\n // 清理持续对话计时器\n if (keepAliveTimer) {\n clearTimeout(keepAliveTimer);\n }\n },\n },\n\n status: {\n defaultRuntime: {\n accountId: DEFAULT_ACCOUNT_ID,\n running: false,\n connected: false,\n lastConnectedAt: null,\n lastError: null,\n lastInboundAt: null,\n lastOutboundAt: null,\n },\n buildChannelSummary: ({ snapshot }: { snapshot: any }) => ({\n configured: snapshot.configured ?? false,\n running: snapshot.running ?? false,\n connected: snapshot.connected ?? false,\n lastConnectedAt: snapshot.lastConnectedAt ?? null,\n lastError: snapshot.lastError ?? null,\n }),\n buildAccountSnapshot: ({ account, runtime }: { account: any; runtime: any }) => ({\n accountId: account?.accountId ?? DEFAULT_ACCOUNT_ID,\n name: account?.name,\n enabled: account?.enabled ?? false,\n configured: Boolean(account?.configured),\n devices: account?.devices,\n running: runtime?.running ?? false,\n connected: runtime?.connected ?? false,\n lastConnectedAt: runtime?.lastConnectedAt ?? null,\n lastError: runtime?.lastError ?? null,\n lastInboundAt: runtime?.lastInboundAt ?? null,\n lastOutboundAt: runtime?.lastOutboundAt ?? null,\n }),\n },\n};\n"],"mappings":"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","names":[]}
|