@folotoy/folotoy-openclaw-plugin 0.5.2 → 0.5.4
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/README.md +73 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -1
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/__tests__/test-message.mjs +190 -0
- package/src/index.ts +18 -1
package/README.md
CHANGED
|
@@ -10,10 +10,20 @@ FoloToy Toy <──MQTT──> FoloToy MQTT Broker <──MQTT──> Plugin
|
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
13
|
+
Interactive install (scan QR code to pair your toy):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx @folotoy/folotoy-openclaw-plugin install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install manually:
|
|
20
|
+
|
|
13
21
|
```bash
|
|
14
22
|
openclaw plugins install @folotoy/folotoy-openclaw-plugin
|
|
15
23
|
```
|
|
16
24
|
|
|
25
|
+
The interactive installer will pair your toy, write the config, and automatically restart the OpenClaw gateway.
|
|
26
|
+
|
|
17
27
|
For local development:
|
|
18
28
|
|
|
19
29
|
```bash
|
|
@@ -71,8 +81,9 @@ Example `openclaw.json`:
|
|
|
71
81
|
Inbound and outbound use separate topics:
|
|
72
82
|
|
|
73
83
|
```
|
|
74
|
-
Inbound
|
|
75
|
-
Outbound
|
|
84
|
+
Inbound (Toy → Plugin): /openapi/folotoy/{sn}/thing/command/call
|
|
85
|
+
Outbound (Plugin → Toy): /openapi/folotoy/{sn}/thing/command/callAck
|
|
86
|
+
Notification (Plugin → Toy): /openapi/folotoy/{sn}/thing/event/post
|
|
76
87
|
```
|
|
77
88
|
|
|
78
89
|
The plugin connects with an `openapi:` prefix on the `clientId` to distinguish itself from the toy's own connection:
|
|
@@ -134,6 +145,22 @@ Finish message (`is_finished: true`, empty content):
|
|
|
134
145
|
|
|
135
146
|
`msgId` starts at 1 per session and auto-increments. `recording_id` is passed through from the inbound message.
|
|
136
147
|
|
|
148
|
+
**Plugin → Toy (notification)**
|
|
149
|
+
|
|
150
|
+
Proactive messages (e.g., timer reminders) use the `event/post` topic with a different identifier and payload format:
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"msgId": 1,
|
|
155
|
+
"identifier": "send_notification",
|
|
156
|
+
"outParams": {
|
|
157
|
+
"text": "Time to drink water!"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Notifications are triggered via the OpenClaw `--deliver` mechanism (see [Testing Notifications](#testing-notifications)).
|
|
163
|
+
|
|
137
164
|
## Environments
|
|
138
165
|
|
|
139
166
|
| Environment | MQTT Host | Port |
|
|
@@ -158,6 +185,25 @@ Immediately sends a transitional reply (e.g., "Let me think...") upon receiving
|
|
|
158
185
|
|
|
159
186
|
MQTT connection failures trigger automatic reconnection with exponential backoff (1s → 60s cap), resetting on success.
|
|
160
187
|
|
|
188
|
+
### Auto-restart Gateway
|
|
189
|
+
|
|
190
|
+
After interactive installation (`npx @folotoy/folotoy-openclaw-plugin install`), the plugin automatically restarts the OpenClaw gateway so changes take effect immediately.
|
|
191
|
+
|
|
192
|
+
## Testing Notifications
|
|
193
|
+
|
|
194
|
+
To send a proactive notification to the toy via OpenClaw:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
openclaw agent --agent main \
|
|
198
|
+
--message "你的通知内容" \
|
|
199
|
+
--deliver \
|
|
200
|
+
--reply-channel folotoy \
|
|
201
|
+
--reply-account default \
|
|
202
|
+
--reply-to <toy_sn>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
This triggers the `outbound.sendText` path, which publishes a `send_notification` message to the `event/post` MQTT topic. The toy will receive and play the notification.
|
|
206
|
+
|
|
161
207
|
## Development
|
|
162
208
|
|
|
163
209
|
```bash
|
|
@@ -166,6 +212,31 @@ pnpm test
|
|
|
166
212
|
pnpm build
|
|
167
213
|
```
|
|
168
214
|
|
|
215
|
+
### MQTT Integration Test
|
|
216
|
+
|
|
217
|
+
A manual integration test script simulates a FoloToy toy by connecting to the MQTT broker, sending a message, and verifying the response format.
|
|
218
|
+
|
|
219
|
+
Configure `.env`:
|
|
220
|
+
|
|
221
|
+
```env
|
|
222
|
+
FOLOTOY_TOY_SN=your-toy-sn
|
|
223
|
+
FOLOTOY_TOY_KEY=your-toy-key
|
|
224
|
+
FOLOTOY_MQTT_HOST=192.168.10.100
|
|
225
|
+
FOLOTOY_MQTT_PORT=1883
|
|
226
|
+
FOLOTOY_TEST_MQTT_USERNAME=testuser # optional, defaults to TOY_SN
|
|
227
|
+
FOLOTOY_TEST_MQTT_PASSWORD=testpassword # optional, defaults to TOY_KEY
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Run:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Chat test — verify soothing ack, AI reply, finish message
|
|
234
|
+
node --env-file=.env src/__tests__/test-message.mjs chat "你好"
|
|
235
|
+
|
|
236
|
+
# Reminder test — verify reply + wait for notification on event/post topic
|
|
237
|
+
node --env-file=.env src/__tests__/test-message.mjs reminder
|
|
238
|
+
```
|
|
239
|
+
|
|
169
240
|
## License
|
|
170
241
|
|
|
171
242
|
MIT
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAgC,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAgC,MAAM,0BAA0B,CAAA;AA0Q/F,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;;;EAczF;yBAEe,KAAK,iBAAiB;AAAtC,wBAGC"}
|
package/dist/index.js
CHANGED
|
@@ -105,9 +105,12 @@ const folotoyChannel = {
|
|
|
105
105
|
Body: text,
|
|
106
106
|
From: credentials.toy_sn,
|
|
107
107
|
To: credentials.toy_sn,
|
|
108
|
-
SessionKey: `folotoy-${accountId}-${credentials.toy_sn}`,
|
|
108
|
+
SessionKey: `agent:main:folotoy-${accountId}-${credentials.toy_sn}`,
|
|
109
109
|
AccountId: accountId,
|
|
110
110
|
Provider: 'folotoy',
|
|
111
|
+
Surface: 'folotoy',
|
|
112
|
+
OriginatingChannel: 'folotoy',
|
|
113
|
+
OriginatingTo: credentials.toy_sn,
|
|
111
114
|
});
|
|
112
115
|
// dispatch, optionally summarize, then send finish message
|
|
113
116
|
void (async () => {
|
|
@@ -193,8 +196,23 @@ const folotoyChannel = {
|
|
|
193
196
|
// cleanup handled by abortSignal listener in startAccount
|
|
194
197
|
},
|
|
195
198
|
},
|
|
199
|
+
messaging: {
|
|
200
|
+
targetResolver: {
|
|
201
|
+
looksLikeId: () => true,
|
|
202
|
+
hint: 'FoloToy toy SN (e.g. 1051db8d0d0c)',
|
|
203
|
+
},
|
|
204
|
+
},
|
|
196
205
|
outbound: {
|
|
197
206
|
deliveryMode: 'direct',
|
|
207
|
+
resolveTarget: ({ to, cfg }) => {
|
|
208
|
+
if (to)
|
|
209
|
+
return { ok: true, to };
|
|
210
|
+
const folotoy = cfg
|
|
211
|
+
?.channels?.folotoy;
|
|
212
|
+
if (folotoy?.toy_sn)
|
|
213
|
+
return { ok: true, to: folotoy.toy_sn };
|
|
214
|
+
return { ok: false, error: new Error('No toy_sn configured for FoloToy') };
|
|
215
|
+
},
|
|
198
216
|
sendText: async ({ text, accountId }) => {
|
|
199
217
|
const key = accountId ?? 'default';
|
|
200
218
|
const entry = activeClients.get(key);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AAC/H,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAsB1I,8CAA8C;AAC9C,MAAM,aAAa,GAAG,IAAI,GAAG,EAAqE,CAAA;AAElG,iDAAiD;AACjD,IAAI,QAA+C,CAAA;AAEnD,MAAM,cAAc,GAAqC;IACvD,EAAE,EAAE,SAAS;IACb,IAAI,EAAE;QACJ,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,SAAS;QAChB,cAAc,EAAE,SAAS;QACzB,QAAQ,EAAE,mBAAmB;QAC7B,KAAK,EAAE,qDAAqD;KAC7D;IACD,YAAY,EAAE;QACZ,SAAS,EAAE,CAAC,QAAQ,CAAC;KACtB;IACD,YAAY,EAAE;QACZ,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE;gBACpE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC1B,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC3B,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,wBAAwB,EAAE;gBAC9D,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC3B,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE;gBACzD,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE;gBACzD,eAAe,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,uBAAuB,EAAE;gBACtE,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,yBAAyB,EAAE;aAC1E;SACF;QACD,OAAO,EAAE;YACP,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE;YAC5B,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE;YAC3B,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE;YAC9C,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,yBAAyB,EAAE;YACrE,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE;YAC9C,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE;YACjE,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE;YACjC,eAAe,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE;YAC5C,iBAAiB,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE;SACvD;KACF;IACD,MAAM,EAAE;QACN,cAAc,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,MAAM,OAAO,GAAI,GAAgF;iBAC9F,QAAQ,EAAE,OAAO,CAAA;YACpB,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACnC,CAAC;QACD,cAAc,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE;YAClC,MAAM,OAAO,GAAI,GAAgF;iBAC9F,QAAQ,EAAE,OAAO,CAAA;YACpB,OAAO,OAAO,IAAK,EAAwB,CAAA;QAC7C,CAAC;KACF;IACD,OAAO,EAAE;QACP,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC1B,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,GAAG,CAAA;YAEzE,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,GAAG,EAAE,IAAI,EAAE,CAAC,yDAAyD,CAAC,CAAA;gBACtE,OAAM;YACR,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACpB,GAAG,EAAE,IAAI,EAAE,CAAC,kDAAkD,CAAC,CAAA;gBAC/D,OAAM;YACR,CAAC;YAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAA;YAC9C,GAAG,EAAE,IAAI,EAAE,CAAC,6BAA6B,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAA;YAC3F,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,UAAU,CAAC,CAAA;YACxD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;YAC9D,MAAM,YAAY,GAAG,iBAAiB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YAC1D,MAAM,aAAa,GAAG,kBAAkB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YAE5D,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAA;YAClF,GAAG,EAAE,IAAI,EAAE,CAAC,2CAA2C,YAAY,EAAE,CAAC,CAAA;YAEtE,MAAM,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE;gBACrC,IAAI,GAAG;oBAAE,GAAG,EAAE,KAAK,EAAE,CAAC,wBAAwB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;YAC9D,CAAC,CAAC,CAAA;YAEF,MAAM,cAAc,GAAG,OAAO,CAAC,eAAe,IAAI,uBAAuB,CAAA;YACzE,MAAM,eAAe,GAAG,OAAO,CAAC,iBAAiB,IAAI,yBAAyB,CAAA;YAE9E,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;gBACvC,IAAI,GAAmB,CAAA;gBACvB,IAAI,CAAC;oBACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAmB,CAAA;gBACxD,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAM;gBACR,CAAC;gBACD,IAAI,GAAG,CAAC,UAAU,KAAK,YAAY,IAAI,OAAO,GAAG,CAAC,WAAW,EAAE,IAAI,KAAK,QAAQ;oBAAE,OAAM;gBAExF,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,GAAG,GAAG,CAAA;gBAC1D,IAAI,KAAK,GAAG,CAAC,CAAA;gBAEb,uEAAuE;gBACvE,oCAAoC;gBACpC,MAAM,MAAM,GAAoB;oBAC9B,KAAK;oBACL,UAAU,EAAE,aAAa;oBACzB,SAAS,EAAE,EAAE,OAAO,EAAE,iBAAiB,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE;iBACzF,CAAA;gBACD,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;gBAErD,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,sBAAsB,CAAC;oBAC7D,IAAI,EAAE,IAAI;oBACV,IAAI,EAAE,WAAW,CAAC,MAAM;oBACxB,EAAE,EAAE,WAAW,CAAC,MAAM;oBACtB,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MQTT integration test — simulates a FoloToy toy.
|
|
3
|
+
*
|
|
4
|
+
* Modes:
|
|
5
|
+
* chat — send a message, verify outbound reply format (soothing + reply + finish)
|
|
6
|
+
* reminder — send a reminder request, verify reply, wait for notification delivery
|
|
7
|
+
*
|
|
8
|
+
* Configuration (via .env or environment variables):
|
|
9
|
+
* FOLOTOY_TOY_SN — toy serial number
|
|
10
|
+
* FOLOTOY_TOY_KEY — toy key (MQTT password)
|
|
11
|
+
* FOLOTOY_MQTT_HOST — MQTT broker host
|
|
12
|
+
* FOLOTOY_MQTT_PORT — MQTT broker port (default 1883)
|
|
13
|
+
* FOLOTOY_TEST_MQTT_USERNAME — MQTT username for test client (default: FOLOTOY_TOY_SN)
|
|
14
|
+
* FOLOTOY_TEST_MQTT_PASSWORD — MQTT password for test client (default: FOLOTOY_TOY_KEY)
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* node --env-file=.env test-mqtt.mjs chat "你好"
|
|
18
|
+
* node --env-file=.env test-mqtt.mjs reminder
|
|
19
|
+
* node --env-file=.env test-mqtt.mjs # defaults to: chat "你好"
|
|
20
|
+
*/
|
|
21
|
+
import mqtt from 'mqtt'
|
|
22
|
+
|
|
23
|
+
const SN = process.env.FOLOTOY_TOY_SN
|
|
24
|
+
const KEY = process.env.FOLOTOY_TOY_KEY
|
|
25
|
+
const HOST = process.env.FOLOTOY_MQTT_HOST
|
|
26
|
+
const PORT = Number(process.env.FOLOTOY_MQTT_PORT || 1883)
|
|
27
|
+
|
|
28
|
+
if (!SN || !HOST) {
|
|
29
|
+
console.error('Missing FOLOTOY_TOY_SN or FOLOTOY_MQTT_HOST. Set them in .env or as env vars.')
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Parse args
|
|
34
|
+
const args = process.argv.slice(2)
|
|
35
|
+
let mode = 'chat'
|
|
36
|
+
let message = '你好'
|
|
37
|
+
|
|
38
|
+
if (args[0] === 'chat') {
|
|
39
|
+
mode = 'chat'
|
|
40
|
+
message = args[1] || '你好'
|
|
41
|
+
} else if (args[0] === 'reminder') {
|
|
42
|
+
mode = 'reminder'
|
|
43
|
+
message = args[1] || '1分钟后提醒我站起来'
|
|
44
|
+
} else if (args[0]) {
|
|
45
|
+
message = args[0]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const inboundTopic = `/openapi/folotoy/${SN}/thing/command/call`
|
|
49
|
+
const outboundTopic = `/openapi/folotoy/${SN}/thing/command/callAck`
|
|
50
|
+
const notificationTopic = `/openapi/folotoy/${SN}/thing/event/post`
|
|
51
|
+
|
|
52
|
+
// ── Validation state ──
|
|
53
|
+
const errors = []
|
|
54
|
+
let gotSoothing = false
|
|
55
|
+
let gotReply = false
|
|
56
|
+
let gotFinish = false
|
|
57
|
+
let gotNotification = false
|
|
58
|
+
|
|
59
|
+
function validateOutboundMessage(msg) {
|
|
60
|
+
if (msg.identifier !== 'chat_output') {
|
|
61
|
+
errors.push(`Expected identifier "chat_output", got "${msg.identifier}"`)
|
|
62
|
+
}
|
|
63
|
+
const p = msg.outParams
|
|
64
|
+
if (typeof p?.recording_id !== 'number') {
|
|
65
|
+
errors.push(`Missing or invalid recording_id in outbound message (order=${p?.order})`)
|
|
66
|
+
}
|
|
67
|
+
if (typeof p?.order !== 'number') {
|
|
68
|
+
errors.push('Missing or invalid order in outbound message')
|
|
69
|
+
}
|
|
70
|
+
if (typeof p?.is_finished !== 'boolean') {
|
|
71
|
+
errors.push(`Missing or invalid is_finished in outbound message (order=${p?.order})`)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function validateNotificationMessage(msg) {
|
|
76
|
+
if (msg.identifier !== 'send_notification') {
|
|
77
|
+
errors.push(`Expected notification identifier "send_notification", got "${msg.identifier}"`)
|
|
78
|
+
}
|
|
79
|
+
if (typeof msg.outParams?.text !== 'string' || !msg.outParams.text) {
|
|
80
|
+
errors.push('Notification missing outParams.text')
|
|
81
|
+
}
|
|
82
|
+
if ('recording_id' in (msg.outParams || {})) {
|
|
83
|
+
errors.push('Notification should not contain recording_id')
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function printSummary() {
|
|
88
|
+
console.log('\n' + '='.repeat(60))
|
|
89
|
+
console.log(`TEST SUMMARY (mode: ${mode})`)
|
|
90
|
+
console.log('='.repeat(60))
|
|
91
|
+
|
|
92
|
+
const checks = [
|
|
93
|
+
['Soothing ack (order=1)', gotSoothing],
|
|
94
|
+
['AI reply (order=2+)', gotReply],
|
|
95
|
+
['Finish message (is_finished=true)', gotFinish],
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
for (const [label, ok] of checks) {
|
|
99
|
+
console.log(` ${ok ? '✅' : '❌'} ${label}`)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (mode === 'reminder') {
|
|
103
|
+
console.log(` ${gotNotification ? '✅' : 'ℹ️ '} Notification on event/post${gotNotification ? '' : ' (not received within timeout — may arrive later)'}`)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (errors.length > 0) {
|
|
107
|
+
console.log('\nErrors:')
|
|
108
|
+
for (const e of errors) console.log(` ❌ ${e}`)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const allPassed = checks.every(([, ok]) => ok) && errors.length === 0
|
|
112
|
+
console.log(`\n${allPassed ? '✅ ALL CHECKS PASSED' : '❌ SOME CHECKS FAILED'}`)
|
|
113
|
+
return allPassed
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function done() {
|
|
117
|
+
const passed = printSummary()
|
|
118
|
+
client.end()
|
|
119
|
+
process.exit(passed ? 0 : 1)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── MQTT ──
|
|
123
|
+
const MQTT_USERNAME = process.env.FOLOTOY_TEST_MQTT_USERNAME || SN
|
|
124
|
+
const MQTT_PASSWORD = process.env.FOLOTOY_TEST_MQTT_PASSWORD || KEY
|
|
125
|
+
|
|
126
|
+
const client = mqtt.connect(`mqtt://${HOST}:${PORT}`, {
|
|
127
|
+
clientId: `test-toy-${Date.now()}`,
|
|
128
|
+
username: MQTT_USERNAME,
|
|
129
|
+
password: MQTT_PASSWORD,
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
client.on('connect', () => {
|
|
133
|
+
console.log(`[connected] ${HOST}:${PORT} as ${SN}`)
|
|
134
|
+
console.log(`[mode] ${mode}`)
|
|
135
|
+
console.log(`[message] ${message}`)
|
|
136
|
+
|
|
137
|
+
client.subscribe([outboundTopic, notificationTopic], (err) => {
|
|
138
|
+
if (err) {
|
|
139
|
+
console.error('[subscribe error]', err.message)
|
|
140
|
+
process.exit(1)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const msg = {
|
|
144
|
+
msgId: 1,
|
|
145
|
+
identifier: 'chat_input',
|
|
146
|
+
inputParams: { text: message, recording_id: 999 },
|
|
147
|
+
}
|
|
148
|
+
console.log(`\n[publish] ${inboundTopic}`)
|
|
149
|
+
console.log(JSON.stringify(msg, null, 2))
|
|
150
|
+
client.publish(inboundTopic, JSON.stringify(msg))
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
client.on('message', (topic, payload) => {
|
|
155
|
+
let msg
|
|
156
|
+
try { msg = JSON.parse(payload.toString()) } catch { return }
|
|
157
|
+
|
|
158
|
+
if (topic === outboundTopic) {
|
|
159
|
+
const p = msg.outParams
|
|
160
|
+
const label = p?.is_finished ? 'FINISH' : `REPLY(order=${p?.order})`
|
|
161
|
+
console.log(`\n[OUTBOUND ${label}] ${p?.content || '(empty)'}`)
|
|
162
|
+
validateOutboundMessage(msg)
|
|
163
|
+
|
|
164
|
+
if (p?.order === 1 && !p?.is_finished) gotSoothing = true
|
|
165
|
+
if (p?.order >= 2 && !p?.is_finished && p?.content) gotReply = true
|
|
166
|
+
if (p?.is_finished) {
|
|
167
|
+
gotFinish = true
|
|
168
|
+
if (mode === 'chat') {
|
|
169
|
+
setTimeout(done, 500)
|
|
170
|
+
} else {
|
|
171
|
+
console.log('\n[waiting up to 90s for notification on event/post topic...]')
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (topic === notificationTopic) {
|
|
177
|
+
console.log(`\n[NOTIFICATION] ${msg.outParams?.text || JSON.stringify(msg)}`)
|
|
178
|
+
validateNotificationMessage(msg)
|
|
179
|
+
gotNotification = true
|
|
180
|
+
setTimeout(done, 500)
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
client.on('error', (err) => {
|
|
185
|
+
console.error('[mqtt error]', err.message)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
// Timeout: chat 30s, reminder 120s
|
|
189
|
+
const timeoutMs = mode === 'reminder' ? 120_000 : 30_000
|
|
190
|
+
setTimeout(done, timeoutMs)
|
package/src/index.ts
CHANGED
|
@@ -136,9 +136,12 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
136
136
|
Body: text,
|
|
137
137
|
From: credentials.toy_sn,
|
|
138
138
|
To: credentials.toy_sn,
|
|
139
|
-
SessionKey: `folotoy-${accountId}-${credentials.toy_sn}`,
|
|
139
|
+
SessionKey: `agent:main:folotoy-${accountId}-${credentials.toy_sn}`,
|
|
140
140
|
AccountId: accountId,
|
|
141
141
|
Provider: 'folotoy',
|
|
142
|
+
Surface: 'folotoy',
|
|
143
|
+
OriginatingChannel: 'folotoy',
|
|
144
|
+
OriginatingTo: credentials.toy_sn,
|
|
142
145
|
})
|
|
143
146
|
|
|
144
147
|
// dispatch, optionally summarize, then send finish message
|
|
@@ -227,8 +230,22 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
227
230
|
},
|
|
228
231
|
},
|
|
229
232
|
|
|
233
|
+
messaging: {
|
|
234
|
+
targetResolver: {
|
|
235
|
+
looksLikeId: () => true,
|
|
236
|
+
hint: 'FoloToy toy SN (e.g. 1051db8d0d0c)',
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
|
|
230
240
|
outbound: {
|
|
231
241
|
deliveryMode: 'direct',
|
|
242
|
+
resolveTarget: ({ to, cfg }) => {
|
|
243
|
+
if (to) return { ok: true, to }
|
|
244
|
+
const folotoy = (cfg as Record<string, unknown> & { channels?: { folotoy?: FlatChannelConfig } })
|
|
245
|
+
?.channels?.folotoy
|
|
246
|
+
if (folotoy?.toy_sn) return { ok: true, to: folotoy.toy_sn }
|
|
247
|
+
return { ok: false, error: new Error('No toy_sn configured for FoloToy') }
|
|
248
|
+
},
|
|
232
249
|
sendText: async ({ text, accountId }) => {
|
|
233
250
|
const key = accountId ?? 'default'
|
|
234
251
|
const entry = activeClients.get(key)
|