@soyeht/soyeht 0.2.7 → 0.2.9
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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/config.ts +30 -4
- package/src/http.ts +163 -12
- package/src/index.ts +1 -1
- package/src/openclaw-plugin-sdk.d.ts +100 -0
- package/src/outbound-queue.ts +11 -0
- package/src/types.ts +46 -40
- package/src/version.ts +1 -1
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -71,14 +71,32 @@ function readAccountsSection(
|
|
|
71
71
|
return section?.["accounts"] as Record<string, unknown> | undefined;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Read flat config from `channels.soyeht.*` (all keys except "accounts").
|
|
76
|
+
* Used as defaults when no explicit account entry exists.
|
|
77
|
+
*/
|
|
78
|
+
function readFlatConfig(cfg: OpenClawConfig): Record<string, unknown> {
|
|
79
|
+
const section = readConfigSection(cfg);
|
|
80
|
+
if (!section) return {};
|
|
81
|
+
const flat: Record<string, unknown> = {};
|
|
82
|
+
for (const [key, value] of Object.entries(section)) {
|
|
83
|
+
if (key !== "accounts") {
|
|
84
|
+
flat[key] = value;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return flat;
|
|
88
|
+
}
|
|
89
|
+
|
|
74
90
|
function readAccountConfig(
|
|
75
91
|
cfg: OpenClawConfig,
|
|
76
92
|
accountId: string,
|
|
77
93
|
): Record<string, unknown> {
|
|
94
|
+
const flat = readFlatConfig(cfg);
|
|
78
95
|
const accounts = readAccountsSection(cfg);
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
96
|
+
const entry = accounts?.[accountId];
|
|
97
|
+
const accountRaw = entry && typeof entry === "object" ? (entry as Record<string, unknown>) : {};
|
|
98
|
+
// Merge: flat config as defaults, account-specific overrides
|
|
99
|
+
return { ...flat, ...accountRaw };
|
|
82
100
|
}
|
|
83
101
|
|
|
84
102
|
// ---------------------------------------------------------------------------
|
|
@@ -199,8 +217,16 @@ export function resolveSoyehtAccount(
|
|
|
199
217
|
|
|
200
218
|
export function listSoyehtAccountIds(cfg: OpenClawConfig): string[] {
|
|
201
219
|
const accounts = readAccountsSection(cfg);
|
|
202
|
-
|
|
220
|
+
const ids = listConfiguredAccountIds({
|
|
203
221
|
accounts: accounts as Record<string, unknown> | undefined,
|
|
204
222
|
normalizeAccountId,
|
|
205
223
|
});
|
|
224
|
+
// If no explicit accounts but flat config exists, treat as "default" account
|
|
225
|
+
if (ids.length === 0) {
|
|
226
|
+
const flat = readFlatConfig(cfg);
|
|
227
|
+
if (Object.keys(flat).length > 0) {
|
|
228
|
+
return [DEFAULT_ACCOUNT_ID];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return ids;
|
|
206
232
|
}
|
package/src/http.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { OpenClawPluginApi, PluginRuntimeChannel } from "openclaw/plugin-sdk";
|
|
3
3
|
import { normalizeAccountId, resolveSoyehtAccount } from "./config.js";
|
|
4
|
-
import { decryptEnvelopeV2, validateEnvelopeV2, type EnvelopeV2 } from "./envelope-v2.js";
|
|
4
|
+
import { decryptEnvelopeV2, encryptEnvelopeV2, validateEnvelopeV2, type EnvelopeV2 } from "./envelope-v2.js";
|
|
5
|
+
import { buildOutboundEnvelope } from "./outbound.js";
|
|
5
6
|
import { cloneRatchetSession, zeroBuffer, type RatchetState } from "./ratchet.js";
|
|
6
7
|
import type { SecurityV2Deps } from "./service.js";
|
|
7
8
|
import { PLUGIN_VERSION } from "./version.js";
|
|
@@ -125,9 +126,23 @@ export function processInboundEnvelope(
|
|
|
125
126
|
// GET /soyeht/health
|
|
126
127
|
// ---------------------------------------------------------------------------
|
|
127
128
|
|
|
128
|
-
export function healthHandler(_api: OpenClawPluginApi) {
|
|
129
|
+
export function healthHandler(_api: OpenClawPluginApi, v2deps?: SecurityV2Deps) {
|
|
129
130
|
return async (_req: IncomingMessage, res: ServerResponse) => {
|
|
130
|
-
|
|
131
|
+
const ready = v2deps?.ready ?? false;
|
|
132
|
+
const identityLoaded = Boolean(v2deps?.identity);
|
|
133
|
+
const activeSessions = v2deps?.sessions.size ?? 0;
|
|
134
|
+
const pairedPeers = v2deps?.peers.size ?? 0;
|
|
135
|
+
const queueStats = v2deps?.outboundQueue.stats() ?? { accounts: 0, totalMessages: 0 };
|
|
136
|
+
|
|
137
|
+
sendJson(res, ready ? 200 : 503, {
|
|
138
|
+
ok: ready,
|
|
139
|
+
plugin: "soyeht",
|
|
140
|
+
version: PLUGIN_VERSION,
|
|
141
|
+
identity: identityLoaded,
|
|
142
|
+
peers: pairedPeers,
|
|
143
|
+
sessions: activeSessions,
|
|
144
|
+
queue: queueStats,
|
|
145
|
+
});
|
|
131
146
|
};
|
|
132
147
|
}
|
|
133
148
|
|
|
@@ -236,15 +251,38 @@ export function inboundHandler(
|
|
|
236
251
|
return;
|
|
237
252
|
}
|
|
238
253
|
|
|
239
|
-
|
|
240
|
-
// The exact API depends on the OpenClaw runtime (e.g. api.channel.pushInbound()).
|
|
241
|
-
// For now the message is decrypted and the session is updated;
|
|
242
|
-
// wiring to the agent conversation will be done when the runtime API is known.
|
|
243
|
-
api.logger.info("[soyeht] Inbound message received (direct mode)", {
|
|
244
|
-
accountId: result.accountId,
|
|
245
|
-
});
|
|
254
|
+
const { plaintext, accountId } = result;
|
|
246
255
|
|
|
247
|
-
|
|
256
|
+
// Parse the decrypted message (EnvelopeV2 plaintext is JSON with contentType/text)
|
|
257
|
+
let messageText: string;
|
|
258
|
+
try {
|
|
259
|
+
const parsed = JSON.parse(plaintext);
|
|
260
|
+
messageText = parsed.text ?? parsed.body ?? plaintext;
|
|
261
|
+
} catch {
|
|
262
|
+
messageText = plaintext;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
api.logger.info("[soyeht] Inbound message received (direct mode)", { accountId });
|
|
266
|
+
|
|
267
|
+
// Respond 200 immediately — agent dispatch happens in background
|
|
268
|
+
sendJson(res, 200, { ok: true, received: true, accountId });
|
|
269
|
+
|
|
270
|
+
// Fire-and-forget: dispatch to agent pipeline
|
|
271
|
+
const channelRuntime = (api.runtime as Record<string, unknown>).channel as
|
|
272
|
+
| PluginRuntimeChannel
|
|
273
|
+
| undefined;
|
|
274
|
+
if (channelRuntime?.routing && channelRuntime?.reply) {
|
|
275
|
+
dispatchToAgent(api, v2deps, channelRuntime, accountId, messageText).catch((err) => {
|
|
276
|
+
api.logger.error("[soyeht] Agent dispatch failed", {
|
|
277
|
+
accountId,
|
|
278
|
+
error: err instanceof Error ? err.message : String(err),
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
} else {
|
|
282
|
+
api.logger.warn(
|
|
283
|
+
"[soyeht] channelRuntime not available — message decrypted but not delivered to agent",
|
|
284
|
+
);
|
|
285
|
+
}
|
|
248
286
|
} catch (err) {
|
|
249
287
|
const message = err instanceof Error ? err.message : "unknown_error";
|
|
250
288
|
sendJson(res, 400, { ok: false, error: message });
|
|
@@ -252,6 +290,119 @@ export function inboundHandler(
|
|
|
252
290
|
};
|
|
253
291
|
}
|
|
254
292
|
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
// Dispatch inbound message to OpenClaw agent pipeline (fire-and-forget)
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
async function dispatchToAgent(
|
|
298
|
+
api: OpenClawPluginApi,
|
|
299
|
+
v2deps: SecurityV2Deps,
|
|
300
|
+
channelRuntime: PluginRuntimeChannel,
|
|
301
|
+
accountId: string,
|
|
302
|
+
messageText: string,
|
|
303
|
+
): Promise<void> {
|
|
304
|
+
const cfg = await api.runtime.config.loadConfig();
|
|
305
|
+
const account = resolveSoyehtAccount(cfg, accountId);
|
|
306
|
+
|
|
307
|
+
const route = channelRuntime.routing.resolveAgentRoute({
|
|
308
|
+
cfg,
|
|
309
|
+
channel: "soyeht",
|
|
310
|
+
accountId: account.accountId,
|
|
311
|
+
peer: { kind: "direct", id: accountId },
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
const sessionCfg = (cfg as Record<string, unknown>).session as
|
|
315
|
+
| Record<string, unknown>
|
|
316
|
+
| undefined;
|
|
317
|
+
const storePath = channelRuntime.session.resolveStorePath(
|
|
318
|
+
sessionCfg?.store as string | undefined,
|
|
319
|
+
{ agentId: route.agentId },
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
const envelopeOptions = channelRuntime.reply.resolveEnvelopeFormatOptions(cfg);
|
|
323
|
+
const previousTimestamp = channelRuntime.session.readSessionUpdatedAt({
|
|
324
|
+
storePath,
|
|
325
|
+
sessionKey: route.sessionKey,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const body = channelRuntime.reply.formatAgentEnvelope({
|
|
329
|
+
channel: "Soyeht",
|
|
330
|
+
from: `soyeht:${accountId}`,
|
|
331
|
+
timestamp: Date.now(),
|
|
332
|
+
previousTimestamp,
|
|
333
|
+
envelope: envelopeOptions,
|
|
334
|
+
body: messageText,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const ctxPayload = channelRuntime.reply.finalizeInboundContext({
|
|
338
|
+
Body: body,
|
|
339
|
+
BodyForAgent: messageText,
|
|
340
|
+
RawBody: messageText,
|
|
341
|
+
From: `soyeht:${accountId}`,
|
|
342
|
+
To: `soyeht:${accountId}`,
|
|
343
|
+
SessionKey: route.sessionKey,
|
|
344
|
+
AccountId: route.accountId,
|
|
345
|
+
ChatType: "direct",
|
|
346
|
+
ConversationLabel: `soyeht:${accountId}`,
|
|
347
|
+
SenderName: accountId,
|
|
348
|
+
SenderId: accountId,
|
|
349
|
+
CommandAuthorized: true,
|
|
350
|
+
Provider: "soyeht",
|
|
351
|
+
Surface: "soyeht",
|
|
352
|
+
OriginatingChannel: "soyeht",
|
|
353
|
+
OriginatingTo: `soyeht:${accountId}`,
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
await channelRuntime.session.recordInboundSession({
|
|
357
|
+
storePath,
|
|
358
|
+
sessionKey: (ctxPayload as Record<string, unknown>).SessionKey as string ?? route.sessionKey,
|
|
359
|
+
ctx: ctxPayload,
|
|
360
|
+
onRecordError: (err: unknown) => {
|
|
361
|
+
api.logger.error("[soyeht] Failed updating session meta", { error: String(err) });
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
await channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
366
|
+
ctx: ctxPayload,
|
|
367
|
+
cfg,
|
|
368
|
+
dispatcherOptions: {
|
|
369
|
+
deliver: async (payload: { text?: string; mediaUrl?: string }) => {
|
|
370
|
+
const text = payload.text;
|
|
371
|
+
if (!text) return;
|
|
372
|
+
|
|
373
|
+
const ratchetSession = v2deps.sessions.get(accountId);
|
|
374
|
+
if (!ratchetSession) {
|
|
375
|
+
api.logger.warn("[soyeht] No ratchet session for reply delivery", { accountId });
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const envelope = buildOutboundEnvelope(accountId, accountId, {
|
|
380
|
+
contentType: "text",
|
|
381
|
+
text,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const { envelope: v2env, updatedSession } = encryptEnvelopeV2({
|
|
385
|
+
session: ratchetSession,
|
|
386
|
+
accountId,
|
|
387
|
+
plaintext: JSON.stringify(envelope),
|
|
388
|
+
dhRatchetCfg: {
|
|
389
|
+
intervalMessages: account.security.dhRatchetIntervalMessages,
|
|
390
|
+
intervalMs: account.security.dhRatchetIntervalMs,
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
v2deps.sessions.set(accountId, updatedSession);
|
|
394
|
+
v2deps.outboundQueue.enqueue(accountId, v2env);
|
|
395
|
+
},
|
|
396
|
+
onError: (err: unknown, info: { kind: string }) => {
|
|
397
|
+
api.logger.error(`[soyeht] ${info.kind} reply failed`, {
|
|
398
|
+
accountId,
|
|
399
|
+
error: err instanceof Error ? err.message : String(err),
|
|
400
|
+
});
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
255
406
|
// ---------------------------------------------------------------------------
|
|
256
407
|
// GET /soyeht/events/:accountId (SSE — plugin → app outbound stream)
|
|
257
408
|
// ---------------------------------------------------------------------------
|
package/src/index.ts
CHANGED
|
@@ -104,7 +104,7 @@ const soyehtPlugin: OpenClawPluginDefinition = {
|
|
|
104
104
|
api.registerHttpRoute({
|
|
105
105
|
path: "/soyeht/health",
|
|
106
106
|
auth: "plugin",
|
|
107
|
-
handler: healthHandler(api),
|
|
107
|
+
handler: healthHandler(api, v2deps),
|
|
108
108
|
});
|
|
109
109
|
api.registerHttpRoute({
|
|
110
110
|
path: "/soyeht/webhook/deliver",
|
|
@@ -140,6 +140,105 @@ declare module "openclaw/plugin-sdk" {
|
|
|
140
140
|
sampleRate?: number;
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
+
// ---- Channel runtime types (subset used by soyeht inbound dispatch) ----
|
|
144
|
+
|
|
145
|
+
export type ResolvedAgentRoute = {
|
|
146
|
+
agentId: string;
|
|
147
|
+
channel: string;
|
|
148
|
+
accountId: string;
|
|
149
|
+
sessionKey: string;
|
|
150
|
+
mainSessionKey: string;
|
|
151
|
+
lastRoutePolicy: "main" | "session";
|
|
152
|
+
matchedBy: string;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export type EnvelopeFormatOptions = {
|
|
156
|
+
timezone?: string;
|
|
157
|
+
includeTimestamp?: boolean;
|
|
158
|
+
includeElapsed?: boolean;
|
|
159
|
+
userTimezone?: string;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export type ReplyPayload = {
|
|
163
|
+
text?: string;
|
|
164
|
+
mediaUrl?: string;
|
|
165
|
+
mediaUrls?: string[];
|
|
166
|
+
isError?: boolean;
|
|
167
|
+
isReasoning?: boolean;
|
|
168
|
+
[key: string]: unknown;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export type ReplyDispatchKind = "tool" | "block" | "final";
|
|
172
|
+
|
|
173
|
+
export type PluginRuntimeChannel = {
|
|
174
|
+
routing: {
|
|
175
|
+
resolveAgentRoute: (params: {
|
|
176
|
+
cfg: OpenClawConfig;
|
|
177
|
+
channel: string;
|
|
178
|
+
accountId?: string | null;
|
|
179
|
+
peer?: { kind: ChatType; id: string } | null;
|
|
180
|
+
parentPeer?: { kind: ChatType; id: string } | null;
|
|
181
|
+
guildId?: string | null;
|
|
182
|
+
teamId?: string | null;
|
|
183
|
+
memberRoleIds?: string[];
|
|
184
|
+
}) => ResolvedAgentRoute;
|
|
185
|
+
buildAgentSessionKey: (...args: unknown[]) => string;
|
|
186
|
+
};
|
|
187
|
+
reply: {
|
|
188
|
+
finalizeInboundContext: <T extends Record<string, unknown>>(
|
|
189
|
+
ctx: T,
|
|
190
|
+
opts?: Record<string, unknown>,
|
|
191
|
+
) => T;
|
|
192
|
+
formatAgentEnvelope: (params: {
|
|
193
|
+
channel: string;
|
|
194
|
+
from?: string;
|
|
195
|
+
timestamp?: number | Date;
|
|
196
|
+
previousTimestamp?: number | Date;
|
|
197
|
+
envelope?: EnvelopeFormatOptions;
|
|
198
|
+
body: string;
|
|
199
|
+
host?: string;
|
|
200
|
+
ip?: string;
|
|
201
|
+
}) => string;
|
|
202
|
+
resolveEnvelopeFormatOptions: (cfg?: OpenClawConfig) => EnvelopeFormatOptions;
|
|
203
|
+
dispatchReplyWithBufferedBlockDispatcher: (params: {
|
|
204
|
+
ctx: Record<string, unknown>;
|
|
205
|
+
cfg: OpenClawConfig;
|
|
206
|
+
dispatcherOptions: {
|
|
207
|
+
deliver: (
|
|
208
|
+
payload: ReplyPayload,
|
|
209
|
+
info?: { kind: ReplyDispatchKind },
|
|
210
|
+
) => Promise<void>;
|
|
211
|
+
onError?: (err: unknown, info: { kind: string }) => void;
|
|
212
|
+
[key: string]: unknown;
|
|
213
|
+
};
|
|
214
|
+
replyOptions?: Record<string, unknown>;
|
|
215
|
+
}) => Promise<unknown>;
|
|
216
|
+
dispatchReplyFromConfig: (...args: unknown[]) => Promise<unknown>;
|
|
217
|
+
[key: string]: unknown;
|
|
218
|
+
};
|
|
219
|
+
session: {
|
|
220
|
+
resolveStorePath: (store?: string, opts?: { agentId?: string }) => string;
|
|
221
|
+
readSessionUpdatedAt: (params: {
|
|
222
|
+
storePath: string;
|
|
223
|
+
sessionKey: string;
|
|
224
|
+
}) => number | undefined;
|
|
225
|
+
recordInboundSession: (params: {
|
|
226
|
+
storePath: string;
|
|
227
|
+
sessionKey: string;
|
|
228
|
+
ctx: Record<string, unknown>;
|
|
229
|
+
onRecordError: (err: unknown) => void;
|
|
230
|
+
[key: string]: unknown;
|
|
231
|
+
}) => Promise<void>;
|
|
232
|
+
[key: string]: unknown;
|
|
233
|
+
};
|
|
234
|
+
activity: {
|
|
235
|
+
record: (...args: unknown[]) => void;
|
|
236
|
+
[key: string]: unknown;
|
|
237
|
+
};
|
|
238
|
+
text: { [key: string]: unknown };
|
|
239
|
+
[key: string]: unknown;
|
|
240
|
+
};
|
|
241
|
+
|
|
143
242
|
export type PluginRuntime = {
|
|
144
243
|
config: {
|
|
145
244
|
loadConfig: () => Promise<OpenClawConfig>;
|
|
@@ -160,6 +259,7 @@ declare module "openclaw/plugin-sdk" {
|
|
|
160
259
|
prefsPath?: string;
|
|
161
260
|
}) => Promise<TtsTelephonyResult>;
|
|
162
261
|
};
|
|
262
|
+
channel?: PluginRuntimeChannel;
|
|
163
263
|
[key: string]: unknown;
|
|
164
264
|
};
|
|
165
265
|
|
package/src/outbound-queue.ts
CHANGED
|
@@ -41,6 +41,8 @@ export type OutboundQueue = {
|
|
|
41
41
|
prune(): number;
|
|
42
42
|
clear(): void;
|
|
43
43
|
|
|
44
|
+
stats(): { accounts: number; totalMessages: number };
|
|
45
|
+
|
|
44
46
|
// Stream token management (for SSE auth)
|
|
45
47
|
createStreamToken(accountId: string, expiresAt: number): string;
|
|
46
48
|
validateStreamToken(token: string): StreamTokenInfo | null;
|
|
@@ -217,12 +219,21 @@ export function createOutboundQueue(opts: OutboundQueueOptions = {}): OutboundQu
|
|
|
217
219
|
}
|
|
218
220
|
}
|
|
219
221
|
|
|
222
|
+
function stats(): { accounts: number; totalMessages: number } {
|
|
223
|
+
let totalMessages = 0;
|
|
224
|
+
for (const q of queues.values()) {
|
|
225
|
+
totalMessages += q.length;
|
|
226
|
+
}
|
|
227
|
+
return { accounts: queues.size, totalMessages };
|
|
228
|
+
}
|
|
229
|
+
|
|
220
230
|
return {
|
|
221
231
|
enqueue,
|
|
222
232
|
subscribe,
|
|
223
233
|
hasSubscribers,
|
|
224
234
|
prune,
|
|
225
235
|
clear,
|
|
236
|
+
stats,
|
|
226
237
|
createStreamToken,
|
|
227
238
|
validateStreamToken,
|
|
228
239
|
revokeStreamTokensForAccount,
|
package/src/types.ts
CHANGED
|
@@ -31,54 +31,57 @@ export type SoyehtAccountConfig = {
|
|
|
31
31
|
};
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
transcribeInbound: { type: "boolean" },
|
|
48
|
-
ttsOutbound: { type: "boolean" },
|
|
49
|
-
},
|
|
34
|
+
// Shared properties object — reused at account level and channel top level
|
|
35
|
+
const accountConfigProperties: Record<string, unknown> = {
|
|
36
|
+
enabled: { type: "boolean" },
|
|
37
|
+
backendBaseUrl: { type: "string" },
|
|
38
|
+
pluginAuthToken: { type: "string" },
|
|
39
|
+
gatewayUrl: { type: "string" },
|
|
40
|
+
allowProactive: { type: "boolean" },
|
|
41
|
+
audio: {
|
|
42
|
+
type: "object",
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
properties: {
|
|
45
|
+
transcribeInbound: { type: "boolean" },
|
|
46
|
+
ttsOutbound: { type: "boolean" },
|
|
50
47
|
},
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
},
|
|
48
|
+
},
|
|
49
|
+
files: {
|
|
50
|
+
type: "object",
|
|
51
|
+
additionalProperties: false,
|
|
52
|
+
properties: {
|
|
53
|
+
acceptInbound: { type: "boolean" },
|
|
54
|
+
maxBytes: { type: "number" },
|
|
58
55
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
},
|
|
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" },
|
|
75
72
|
},
|
|
76
73
|
},
|
|
77
74
|
},
|
|
78
75
|
},
|
|
79
76
|
};
|
|
80
77
|
|
|
81
|
-
export
|
|
78
|
+
export const SoyehtAccountConfigSchema: JsonSchema = {
|
|
79
|
+
type: "object",
|
|
80
|
+
additionalProperties: false,
|
|
81
|
+
properties: accountConfigProperties,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export type SoyehtChannelConfig = SoyehtAccountConfig & {
|
|
82
85
|
accounts?: Record<string, SoyehtAccountConfig>;
|
|
83
86
|
};
|
|
84
87
|
|
|
@@ -86,6 +89,9 @@ export const SoyehtChannelConfigSchema: JsonSchema = {
|
|
|
86
89
|
type: "object",
|
|
87
90
|
additionalProperties: false,
|
|
88
91
|
properties: {
|
|
92
|
+
// Top-level account fields (flat config shorthand for single-account setups)
|
|
93
|
+
...accountConfigProperties,
|
|
94
|
+
// Named accounts (multi-account support)
|
|
89
95
|
accounts: {
|
|
90
96
|
type: "object",
|
|
91
97
|
additionalProperties: SoyehtAccountConfigSchema,
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PLUGIN_VERSION = "0.2.
|
|
1
|
+
export const PLUGIN_VERSION = "0.2.9";
|