@soyeht/soyeht 0.1.1 → 0.1.2

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 CHANGED
@@ -7,7 +7,7 @@ Channel plugin for connecting the Soyeht Flutter mobile app to an OpenClaw gatew
7
7
  After this package is published to npm, install it on the OpenClaw host with:
8
8
 
9
9
  ```bash
10
- openclaw plugins install @soyeht/soyeht@0.1.1 --pin
10
+ openclaw plugins install @soyeht/soyeht@0.1.2 --pin
11
11
  openclaw plugins enable soyeht
12
12
  ```
13
13
 
@@ -5,7 +5,7 @@
5
5
  ],
6
6
  "name": "Soyeht",
7
7
  "description": "Channel plugin for the Soyeht Flutter mobile app",
8
- "version": "0.1.1",
8
+ "version": "0.1.2",
9
9
  "configSchema": {
10
10
  "type": "object",
11
11
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soyeht/soyeht",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "OpenClaw channel plugin for the Soyeht Flutter mobile app",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -38,9 +38,6 @@
38
38
  "prepublishOnly": "npm run validate && npm run pack:check",
39
39
  "version": "node scripts/sync-plugin-manifest-version.mjs"
40
40
  },
41
- "dependencies": {
42
- "@sinclair/typebox": "^0.34.0"
43
- },
44
41
  "devDependencies": {
45
42
  "@types/node": "^25.3.5",
46
43
  "typescript": "^5.7.0",
package/src/types.ts CHANGED
@@ -1,190 +1,390 @@
1
- import { Type, type Static } from "@sinclair/typebox";
1
+ type JsonSchema = Record<string, unknown>;
2
2
 
3
3
  // ---------------------------------------------------------------------------
4
4
  // Account config schema (per-account settings under channels.soyeht.accounts.*)
5
5
  // ---------------------------------------------------------------------------
6
6
 
7
- export const SoyehtAccountConfigSchema = Type.Object({
8
- enabled: Type.Optional(Type.Boolean()),
9
- backendBaseUrl: Type.Optional(Type.String()),
10
- pluginAuthToken: Type.Optional(Type.String()),
11
- allowProactive: Type.Optional(Type.Boolean()),
12
- audio: Type.Optional(
13
- Type.Object({
14
- transcribeInbound: Type.Optional(Type.Boolean()),
15
- ttsOutbound: Type.Optional(Type.Boolean()),
16
- }),
17
- ),
18
- files: Type.Optional(
19
- Type.Object({
20
- acceptInbound: Type.Optional(Type.Boolean()),
21
- maxBytes: Type.Optional(Type.Number()),
22
- }, { additionalProperties: false }),
23
- ),
24
- security: Type.Optional(
25
- Type.Object({
26
- enabled: Type.Optional(Type.Boolean()),
27
- timestampToleranceMs: Type.Optional(Type.Number()),
28
- dhRatchetIntervalMessages: Type.Optional(Type.Number()),
29
- dhRatchetIntervalMs: Type.Optional(Type.Number()),
30
- sessionMaxAgeMs: Type.Optional(Type.Number()),
31
- rateLimit: Type.Optional(
32
- Type.Object({
33
- maxRequests: Type.Optional(Type.Number()),
34
- windowMs: Type.Optional(Type.Number()),
35
- }, { additionalProperties: false }),
36
- ),
37
- }, { additionalProperties: false }),
38
- ),
39
- }, { additionalProperties: false });
40
-
41
- export type SoyehtAccountConfig = Static<typeof SoyehtAccountConfigSchema>;
42
-
43
- export const SoyehtChannelConfigSchema = Type.Object({
44
- accounts: Type.Optional(Type.Record(Type.String(), SoyehtAccountConfigSchema)),
45
- }, { additionalProperties: false });
46
-
47
- export type SoyehtChannelConfig = Static<typeof SoyehtChannelConfigSchema>;
7
+ export type SoyehtAccountConfig = {
8
+ enabled?: boolean;
9
+ backendBaseUrl?: string;
10
+ pluginAuthToken?: string;
11
+ allowProactive?: boolean;
12
+ audio?: {
13
+ transcribeInbound?: boolean;
14
+ ttsOutbound?: boolean;
15
+ };
16
+ files?: {
17
+ acceptInbound?: boolean;
18
+ maxBytes?: number;
19
+ };
20
+ security?: {
21
+ enabled?: boolean;
22
+ timestampToleranceMs?: number;
23
+ dhRatchetIntervalMessages?: number;
24
+ dhRatchetIntervalMs?: number;
25
+ sessionMaxAgeMs?: number;
26
+ rateLimit?: {
27
+ maxRequests?: number;
28
+ windowMs?: number;
29
+ };
30
+ };
31
+ };
32
+
33
+ export const SoyehtAccountConfigSchema: JsonSchema = {
34
+ type: "object",
35
+ additionalProperties: false,
36
+ properties: {
37
+ enabled: { type: "boolean" },
38
+ backendBaseUrl: { type: "string" },
39
+ pluginAuthToken: { type: "string" },
40
+ allowProactive: { type: "boolean" },
41
+ audio: {
42
+ type: "object",
43
+ additionalProperties: false,
44
+ properties: {
45
+ transcribeInbound: { type: "boolean" },
46
+ ttsOutbound: { type: "boolean" },
47
+ },
48
+ },
49
+ files: {
50
+ type: "object",
51
+ additionalProperties: false,
52
+ properties: {
53
+ acceptInbound: { type: "boolean" },
54
+ maxBytes: { type: "number" },
55
+ },
56
+ },
57
+ security: {
58
+ type: "object",
59
+ additionalProperties: false,
60
+ properties: {
61
+ enabled: { type: "boolean" },
62
+ timestampToleranceMs: { type: "number" },
63
+ dhRatchetIntervalMessages: { type: "number" },
64
+ dhRatchetIntervalMs: { type: "number" },
65
+ sessionMaxAgeMs: { type: "number" },
66
+ rateLimit: {
67
+ type: "object",
68
+ additionalProperties: false,
69
+ properties: {
70
+ maxRequests: { type: "number" },
71
+ windowMs: { type: "number" },
72
+ },
73
+ },
74
+ },
75
+ },
76
+ },
77
+ };
78
+
79
+ export type SoyehtChannelConfig = {
80
+ accounts?: Record<string, SoyehtAccountConfig>;
81
+ };
82
+
83
+ export const SoyehtChannelConfigSchema: JsonSchema = {
84
+ type: "object",
85
+ additionalProperties: false,
86
+ properties: {
87
+ accounts: {
88
+ type: "object",
89
+ additionalProperties: SoyehtAccountConfigSchema,
90
+ },
91
+ },
92
+ };
48
93
 
49
94
  // ---------------------------------------------------------------------------
50
95
  // Outbound message payloads
51
96
  // ---------------------------------------------------------------------------
52
97
 
53
- export const TextMessagePayloadSchema = Type.Object({
54
- contentType: Type.Literal("text"),
55
- text: Type.String({ minLength: 1 }),
56
- });
57
-
58
- export type TextMessagePayload = Static<typeof TextMessagePayloadSchema>;
59
-
60
- export const AudioMessagePayloadSchema = Type.Object({
61
- contentType: Type.Literal("audio"),
62
- renderStyle: Type.Literal("voice_note"),
63
- mimeType: Type.String(),
64
- filename: Type.String(),
65
- durationMs: Type.Optional(Type.Number()),
66
- url: Type.String(),
67
- transcript: Type.Optional(Type.String()),
68
- });
69
-
70
- export type AudioMessagePayload = Static<typeof AudioMessagePayloadSchema>;
71
-
72
- export const FileMessagePayloadSchema = Type.Object({
73
- contentType: Type.Literal("file"),
74
- mimeType: Type.String(),
75
- filename: Type.String(),
76
- sizeBytes: Type.Optional(Type.Number()),
77
- url: Type.String(),
78
- });
79
-
80
- export type FileMessagePayload = Static<typeof FileMessagePayloadSchema>;
98
+ export type TextMessagePayload = {
99
+ contentType: "text";
100
+ text: string;
101
+ };
102
+
103
+ export const TextMessagePayloadSchema: JsonSchema = {
104
+ type: "object",
105
+ additionalProperties: false,
106
+ required: ["contentType", "text"],
107
+ properties: {
108
+ contentType: { const: "text" },
109
+ text: { type: "string", minLength: 1 },
110
+ },
111
+ };
112
+
113
+ export type AudioMessagePayload = {
114
+ contentType: "audio";
115
+ renderStyle: "voice_note";
116
+ mimeType: string;
117
+ filename: string;
118
+ durationMs?: number;
119
+ url: string;
120
+ transcript?: string;
121
+ };
122
+
123
+ export const AudioMessagePayloadSchema: JsonSchema = {
124
+ type: "object",
125
+ additionalProperties: false,
126
+ required: ["contentType", "renderStyle", "mimeType", "filename", "url"],
127
+ properties: {
128
+ contentType: { const: "audio" },
129
+ renderStyle: { const: "voice_note" },
130
+ mimeType: { type: "string" },
131
+ filename: { type: "string" },
132
+ durationMs: { type: "number" },
133
+ url: { type: "string" },
134
+ transcript: { type: "string" },
135
+ },
136
+ };
137
+
138
+ export type FileMessagePayload = {
139
+ contentType: "file";
140
+ mimeType: string;
141
+ filename: string;
142
+ sizeBytes?: number;
143
+ url: string;
144
+ };
145
+
146
+ export const FileMessagePayloadSchema: JsonSchema = {
147
+ type: "object",
148
+ additionalProperties: false,
149
+ required: ["contentType", "mimeType", "filename", "url"],
150
+ properties: {
151
+ contentType: { const: "file" },
152
+ mimeType: { type: "string" },
153
+ filename: { type: "string" },
154
+ sizeBytes: { type: "number" },
155
+ url: { type: "string" },
156
+ },
157
+ };
81
158
 
82
159
  // ---------------------------------------------------------------------------
83
160
  // Outbound envelope (wraps any payload for backend delivery)
84
161
  // ---------------------------------------------------------------------------
85
162
 
86
- export const OutboundEnvelopeSchema = Type.Object({
87
- accountId: Type.String(),
88
- sessionId: Type.String(),
89
- deliveryId: Type.String(),
90
- timestamp: Type.Number(),
91
- message: Type.Union([
92
- TextMessagePayloadSchema,
93
- AudioMessagePayloadSchema,
94
- FileMessagePayloadSchema,
95
- ]),
96
- });
97
-
98
- export type OutboundEnvelope = Static<typeof OutboundEnvelopeSchema>;
163
+ export type OutboundEnvelope = {
164
+ accountId: string;
165
+ sessionId: string;
166
+ deliveryId: string;
167
+ timestamp: number;
168
+ message: TextMessagePayload | AudioMessagePayload | FileMessagePayload;
169
+ };
170
+
171
+ export const OutboundEnvelopeSchema: JsonSchema = {
172
+ type: "object",
173
+ additionalProperties: false,
174
+ required: ["accountId", "sessionId", "deliveryId", "timestamp", "message"],
175
+ properties: {
176
+ accountId: { type: "string" },
177
+ sessionId: { type: "string" },
178
+ deliveryId: { type: "string" },
179
+ timestamp: { type: "number" },
180
+ message: {
181
+ oneOf: [
182
+ TextMessagePayloadSchema,
183
+ AudioMessagePayloadSchema,
184
+ FileMessagePayloadSchema,
185
+ ],
186
+ },
187
+ },
188
+ };
99
189
 
100
190
  // ---------------------------------------------------------------------------
101
191
  // Delivery ack (webhook response from backend)
102
192
  // ---------------------------------------------------------------------------
103
193
 
104
- export const DeliveryAckSchema = Type.Object({
105
- deliveryId: Type.String(),
106
- status: Type.Union([Type.Literal("delivered"), Type.Literal("failed")]),
107
- error: Type.Optional(Type.String()),
108
- });
109
-
110
- export type DeliveryAck = Static<typeof DeliveryAckSchema>;
194
+ export type DeliveryAck = {
195
+ deliveryId: string;
196
+ status: "delivered" | "failed";
197
+ error?: string;
198
+ };
199
+
200
+ export const DeliveryAckSchema: JsonSchema = {
201
+ type: "object",
202
+ additionalProperties: false,
203
+ required: ["deliveryId", "status"],
204
+ properties: {
205
+ deliveryId: { type: "string" },
206
+ status: { enum: ["delivered", "failed"] },
207
+ error: { type: "string" },
208
+ },
209
+ };
111
210
 
112
211
  // ---------------------------------------------------------------------------
113
212
  // Security schemas
114
213
  // ---------------------------------------------------------------------------
115
214
 
116
- export const HandshakeInitV2Schema = Type.Object({
117
- version: Type.Literal(2),
118
- accountId: Type.String(),
119
- appEphemeralKey: Type.String(), // X25519 pub raw b64url
120
- nonce: Type.String(),
121
- timestamp: Type.Number(),
122
- });
123
-
124
- export type HandshakeInitV2 = Static<typeof HandshakeInitV2Schema>;
125
-
126
- export const HandshakeResponseV2Schema = Type.Object({
127
- version: Type.Literal(2),
128
- phase: Type.Literal("init"),
129
- complete: Type.Boolean(),
130
- pluginEphemeralKey: Type.String(), // X25519 pub raw b64url
131
- nonce: Type.String(),
132
- timestamp: Type.Number(),
133
- serverTimestamp: Type.Number(),
134
- challengeExpiresAt: Type.Number(),
135
- expiresAt: Type.Number(),
136
- pluginSignature: Type.String(), // Ed25519 sig over transcript, b64url
137
- });
138
-
139
- export type HandshakeResponseV2 = Static<typeof HandshakeResponseV2Schema>;
140
-
141
- export const EnvelopeV2Schema = Type.Object({
142
- v: Type.Literal(2),
143
- accountId: Type.String(),
144
- direction: Type.Union([Type.Literal("plugin_to_app"), Type.Literal("app_to_plugin")]),
145
- counter: Type.Number(),
146
- timestamp: Type.Number(),
147
- dhRatchetKey: Type.Optional(Type.String()),
148
- ciphertext: Type.String(),
149
- iv: Type.String(),
150
- tag: Type.String(),
151
- });
152
-
153
- export type EnvelopeV2Type = Static<typeof EnvelopeV2Schema>;
154
-
155
- export const PairRequestSchema = Type.Object({
156
- accountId: Type.String(),
157
- pairingToken: Type.String(),
158
- appIdentityKey: Type.String(), // Ed25519 pub raw b64url
159
- appDhKey: Type.String(), // X25519 pub raw b64url
160
- appSignature: Type.String(), // Ed25519 signature over pairing proof transcript
161
- });
162
-
163
- export type PairRequest = Static<typeof PairRequestSchema>;
164
-
165
- export const PairingQrPayloadSchema = Type.Object({
166
- version: Type.Literal(1),
167
- type: Type.Literal("soyeht_pairing_qr"),
168
- accountId: Type.String(),
169
- pairingToken: Type.String(),
170
- expiresAt: Type.Number(),
171
- allowOverwrite: Type.Boolean(),
172
- pluginIdentityKey: Type.String(),
173
- pluginDhKey: Type.String(),
174
- fingerprint: Type.String(),
175
- signature: Type.String(),
176
- });
177
-
178
- export type PairingQrPayload = Static<typeof PairingQrPayloadSchema>;
179
-
180
- export const HandshakeFinishV2Schema = Type.Object({
181
- version: Type.Literal(2),
182
- accountId: Type.String(),
183
- nonce: Type.String(),
184
- appSignature: Type.String(),
185
- });
186
-
187
- export type HandshakeFinishV2 = Static<typeof HandshakeFinishV2Schema>;
215
+ export type HandshakeInitV2 = {
216
+ version: 2;
217
+ accountId: string;
218
+ appEphemeralKey: string;
219
+ nonce: string;
220
+ timestamp: number;
221
+ };
222
+
223
+ export const HandshakeInitV2Schema: JsonSchema = {
224
+ type: "object",
225
+ additionalProperties: false,
226
+ required: ["version", "accountId", "appEphemeralKey", "nonce", "timestamp"],
227
+ properties: {
228
+ version: { const: 2 },
229
+ accountId: { type: "string" },
230
+ appEphemeralKey: { type: "string" },
231
+ nonce: { type: "string" },
232
+ timestamp: { type: "number" },
233
+ },
234
+ };
235
+
236
+ export type HandshakeResponseV2 = {
237
+ version: 2;
238
+ phase: "init";
239
+ complete: boolean;
240
+ pluginEphemeralKey: string;
241
+ nonce: string;
242
+ timestamp: number;
243
+ serverTimestamp: number;
244
+ challengeExpiresAt: number;
245
+ expiresAt: number;
246
+ pluginSignature: string;
247
+ };
248
+
249
+ export const HandshakeResponseV2Schema: JsonSchema = {
250
+ type: "object",
251
+ additionalProperties: false,
252
+ required: [
253
+ "version",
254
+ "phase",
255
+ "complete",
256
+ "pluginEphemeralKey",
257
+ "nonce",
258
+ "timestamp",
259
+ "serverTimestamp",
260
+ "challengeExpiresAt",
261
+ "expiresAt",
262
+ "pluginSignature",
263
+ ],
264
+ properties: {
265
+ version: { const: 2 },
266
+ phase: { const: "init" },
267
+ complete: { type: "boolean" },
268
+ pluginEphemeralKey: { type: "string" },
269
+ nonce: { type: "string" },
270
+ timestamp: { type: "number" },
271
+ serverTimestamp: { type: "number" },
272
+ challengeExpiresAt: { type: "number" },
273
+ expiresAt: { type: "number" },
274
+ pluginSignature: { type: "string" },
275
+ },
276
+ };
277
+
278
+ export type EnvelopeV2Type = {
279
+ v: 2;
280
+ accountId: string;
281
+ direction: "plugin_to_app" | "app_to_plugin";
282
+ counter: number;
283
+ timestamp: number;
284
+ dhRatchetKey?: string;
285
+ ciphertext: string;
286
+ iv: string;
287
+ tag: string;
288
+ };
289
+
290
+ export const EnvelopeV2Schema: JsonSchema = {
291
+ type: "object",
292
+ additionalProperties: false,
293
+ required: ["v", "accountId", "direction", "counter", "timestamp", "ciphertext", "iv", "tag"],
294
+ properties: {
295
+ v: { const: 2 },
296
+ accountId: { type: "string" },
297
+ direction: { enum: ["plugin_to_app", "app_to_plugin"] },
298
+ counter: { type: "number" },
299
+ timestamp: { type: "number" },
300
+ dhRatchetKey: { type: "string" },
301
+ ciphertext: { type: "string" },
302
+ iv: { type: "string" },
303
+ tag: { type: "string" },
304
+ },
305
+ };
306
+
307
+ export type PairRequest = {
308
+ accountId: string;
309
+ pairingToken: string;
310
+ appIdentityKey: string;
311
+ appDhKey: string;
312
+ appSignature: string;
313
+ };
314
+
315
+ export const PairRequestSchema: JsonSchema = {
316
+ type: "object",
317
+ additionalProperties: false,
318
+ required: ["accountId", "pairingToken", "appIdentityKey", "appDhKey", "appSignature"],
319
+ properties: {
320
+ accountId: { type: "string" },
321
+ pairingToken: { type: "string" },
322
+ appIdentityKey: { type: "string" },
323
+ appDhKey: { type: "string" },
324
+ appSignature: { type: "string" },
325
+ },
326
+ };
327
+
328
+ export type PairingQrPayload = {
329
+ version: 1;
330
+ type: "soyeht_pairing_qr";
331
+ accountId: string;
332
+ pairingToken: string;
333
+ expiresAt: number;
334
+ allowOverwrite: boolean;
335
+ pluginIdentityKey: string;
336
+ pluginDhKey: string;
337
+ fingerprint: string;
338
+ signature: string;
339
+ };
340
+
341
+ export const PairingQrPayloadSchema: JsonSchema = {
342
+ type: "object",
343
+ additionalProperties: false,
344
+ required: [
345
+ "version",
346
+ "type",
347
+ "accountId",
348
+ "pairingToken",
349
+ "expiresAt",
350
+ "allowOverwrite",
351
+ "pluginIdentityKey",
352
+ "pluginDhKey",
353
+ "fingerprint",
354
+ "signature",
355
+ ],
356
+ properties: {
357
+ version: { const: 1 },
358
+ type: { const: "soyeht_pairing_qr" },
359
+ accountId: { type: "string" },
360
+ pairingToken: { type: "string" },
361
+ expiresAt: { type: "number" },
362
+ allowOverwrite: { type: "boolean" },
363
+ pluginIdentityKey: { type: "string" },
364
+ pluginDhKey: { type: "string" },
365
+ fingerprint: { type: "string" },
366
+ signature: { type: "string" },
367
+ },
368
+ };
369
+
370
+ export type HandshakeFinishV2 = {
371
+ version: 2;
372
+ accountId: string;
373
+ nonce: string;
374
+ appSignature: string;
375
+ };
376
+
377
+ export const HandshakeFinishV2Schema: JsonSchema = {
378
+ type: "object",
379
+ additionalProperties: false,
380
+ required: ["version", "accountId", "nonce", "appSignature"],
381
+ properties: {
382
+ version: { const: 2 },
383
+ accountId: { type: "string" },
384
+ nonce: { type: "string" },
385
+ appSignature: { type: "string" },
386
+ },
387
+ };
188
388
 
189
389
  // ---------------------------------------------------------------------------
190
390
  // Channel capabilities
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const PLUGIN_VERSION = "0.1.1";
1
+ export const PLUGIN_VERSION = "0.1.2";