@brantrusnak/openclaw-omadeus 1.0.2 → 1.0.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 +15 -61
- package/dist/_virtual/_rolldown/runtime.js +4 -0
- package/dist/api.js +5 -0
- package/dist/index.js +14 -0
- package/dist/runtime-api.js +15 -0
- package/dist/setup-entry.js +7 -0
- package/dist/src/allowed-reaction-emojis.js +21 -0
- package/dist/src/api/auth.api.js +118 -0
- package/dist/src/api/channel.api.js +23 -0
- package/dist/src/api/message.api.js +76 -0
- package/dist/src/api/nugget.api.js +127 -0
- package/dist/src/auth.js +30 -0
- package/dist/src/channel.js +626 -0
- package/dist/src/config.js +52 -0
- package/dist/src/defaults.js +5 -0
- package/dist/src/inbound-policy.js +205 -0
- package/dist/src/inbound.js +97 -0
- package/dist/src/member-resolve.js +53 -0
- package/dist/src/message-handler.js +262 -0
- package/dist/src/nugget-lookup.js +140 -0
- package/dist/src/onboarding.js +357 -0
- package/dist/src/outbound.js +17 -0
- package/dist/src/reply-dispatcher.js +46 -0
- package/dist/src/runtime.js +5 -0
- package/dist/src/setup-core.js +46 -0
- package/dist/src/setup-surface.js +2 -0
- package/dist/src/socket/dolphin.socket.js +18 -0
- package/dist/src/socket/jaguar.socket.js +22 -0
- package/dist/src/socket/socket.js +153 -0
- package/dist/src/store.js +13 -0
- package/dist/src/token.js +84 -0
- package/dist/src/types.js +15 -0
- package/dist/src/utils/http.util.js +43 -0
- package/dist/src/utils/jwt.util.js +15 -0
- package/package.json +10 -3
- package/src/api/auth.api.ts +27 -7
- package/src/channel.ts +127 -238
- package/src/member-resolve.ts +1 -1
- package/src/onboarding.ts +117 -163
- package/src/setup-core.ts +10 -1
- package/src/socket/socket.ts +24 -11
package/src/onboarding.ts
CHANGED
|
@@ -8,29 +8,16 @@ import { listMemberChannelViews } from "./api/channel.api.js";
|
|
|
8
8
|
import { authenticate } from "./auth.js";
|
|
9
9
|
import { getOmadeusChannelConfig, resolveOmadeusAccount } from "./config.js";
|
|
10
10
|
import { OMADEUS_CAS_URL, OMADEUS_MAESTRO_URL } from "./defaults.js";
|
|
11
|
+
import { formatMemberLabel } from "./member-resolve.js";
|
|
11
12
|
import type {
|
|
12
13
|
OmadeusChannelConfig,
|
|
13
14
|
OmadeusChannelView,
|
|
14
15
|
OmadeusInboundEntityKind,
|
|
15
16
|
OmadeusOrganizationMember,
|
|
16
17
|
} from "./types.js";
|
|
18
|
+
import { OMADEUS_INBOUND_ENTITY_KINDS } from "./types.js";
|
|
17
19
|
|
|
18
20
|
const channel = "omadeus" as const;
|
|
19
|
-
const DONE = "__done__";
|
|
20
|
-
const ALL_ENTITY_KINDS: OmadeusInboundEntityKind[] = [
|
|
21
|
-
"task",
|
|
22
|
-
"nugget",
|
|
23
|
-
"project",
|
|
24
|
-
"release",
|
|
25
|
-
"sprint",
|
|
26
|
-
"summary",
|
|
27
|
-
"client",
|
|
28
|
-
"folder",
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
type CoreConfig = OpenClawConfig & {
|
|
32
|
-
channels?: { omadeus?: OmadeusChannelConfig };
|
|
33
|
-
};
|
|
34
21
|
|
|
35
22
|
type SelectOption = {
|
|
36
23
|
value: string;
|
|
@@ -38,23 +25,20 @@ type SelectOption = {
|
|
|
38
25
|
hint?: string;
|
|
39
26
|
};
|
|
40
27
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
function getOmadeusSection(cfg: OpenClawConfig): OmadeusChannelConfig | undefined {
|
|
57
|
-
return getOmadeusChannelConfig(cfg as CoreConfig);
|
|
28
|
+
function formatAuthError(err: unknown): string {
|
|
29
|
+
if (!(err instanceof Error)) return String(err);
|
|
30
|
+
const parts = [err.message];
|
|
31
|
+
const { cause } = err;
|
|
32
|
+
if (cause instanceof Error) {
|
|
33
|
+
parts.push(cause.message);
|
|
34
|
+
const code = (cause as Error & { code?: unknown }).code;
|
|
35
|
+
if (typeof code === "string" && code) {
|
|
36
|
+
parts.push(`(${code})`);
|
|
37
|
+
}
|
|
38
|
+
} else if (typeof cause === "string" && cause.trim()) {
|
|
39
|
+
parts.push(cause);
|
|
40
|
+
}
|
|
41
|
+
return parts.join(" — ");
|
|
58
42
|
}
|
|
59
43
|
|
|
60
44
|
async function noteOmadeusAuthHelp(prompter: WizardPrompter): Promise<void> {
|
|
@@ -80,35 +64,32 @@ async function promptOrganizationId(params: {
|
|
|
80
64
|
}): Promise<number> {
|
|
81
65
|
const { prompter, maestroUrl, email, existing } = params;
|
|
82
66
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
);
|
|
93
|
-
return orgs[0]!.id;
|
|
94
|
-
}
|
|
95
|
-
const choice = await prompter.select({
|
|
96
|
-
message: "Select organization",
|
|
97
|
-
options: orgs.map((org) => ({
|
|
98
|
-
value: String(org.id),
|
|
99
|
-
label: `${org.title} (${org.membersCount} members)`,
|
|
100
|
-
hint: `ID: ${org.id}`,
|
|
101
|
-
})),
|
|
102
|
-
initialValue: existing ? String(existing) : String(orgs[0]!.id),
|
|
103
|
-
});
|
|
104
|
-
return Number(choice);
|
|
67
|
+
try {
|
|
68
|
+
const orgs = await listOrganizations({ maestroUrl, email });
|
|
69
|
+
if (orgs.length > 0) {
|
|
70
|
+
if (orgs.length === 1) {
|
|
71
|
+
await prompter.note(
|
|
72
|
+
`Found organization: ${orgs[0]!.title} (${orgs[0]!.id})`,
|
|
73
|
+
"Omadeus organization",
|
|
74
|
+
);
|
|
75
|
+
return orgs[0]!.id;
|
|
105
76
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
77
|
+
const choice = await prompter.select({
|
|
78
|
+
message: "Select organization",
|
|
79
|
+
options: orgs.map((org) => ({
|
|
80
|
+
value: String(org.id),
|
|
81
|
+
label: `${org.title} (${org.membersCount} members)`,
|
|
82
|
+
hint: `ID: ${org.id}`,
|
|
83
|
+
})),
|
|
84
|
+
initialValue: existing ? String(existing) : String(orgs[0]!.id),
|
|
85
|
+
});
|
|
86
|
+
return Number(choice);
|
|
111
87
|
}
|
|
88
|
+
} catch {
|
|
89
|
+
await prompter.note(
|
|
90
|
+
"Could not fetch organizations from the API. Enter the ID manually.",
|
|
91
|
+
"Omadeus organization",
|
|
92
|
+
);
|
|
112
93
|
}
|
|
113
94
|
|
|
114
95
|
const raw = await prompter.text({
|
|
@@ -140,8 +121,21 @@ async function promptChannelSelection(params: {
|
|
|
140
121
|
take: 100,
|
|
141
122
|
});
|
|
142
123
|
if (channels.length === 0) {
|
|
143
|
-
|
|
124
|
+
await prompter.note(
|
|
125
|
+
"No channels found for this account. Channel listening will stay disabled.",
|
|
126
|
+
"Omadeus channels",
|
|
127
|
+
);
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const listenToChannels = await prompter.confirm({
|
|
132
|
+
message: "Listen for messages in Omadeus channels?",
|
|
133
|
+
initialValue: (existingChannelViewIds?.length ?? 0) > 0,
|
|
134
|
+
});
|
|
135
|
+
if (!listenToChannels) {
|
|
136
|
+
return [];
|
|
144
137
|
}
|
|
138
|
+
|
|
145
139
|
const selected = await promptMultiSelect({
|
|
146
140
|
prompter,
|
|
147
141
|
message: "Which channels should OpenClaw listen to?",
|
|
@@ -155,27 +149,9 @@ async function promptChannelSelection(params: {
|
|
|
155
149
|
initialValues:
|
|
156
150
|
existingChannelViewIds && existingChannelViewIds.length > 0
|
|
157
151
|
? existingChannelViewIds.map(String)
|
|
158
|
-
:
|
|
152
|
+
: undefined,
|
|
159
153
|
});
|
|
160
|
-
|
|
161
|
-
if (chosen.length === 0) {
|
|
162
|
-
throw new Error("At least one channel must be selected.");
|
|
163
|
-
}
|
|
164
|
-
return chosen;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function memberLabel(member: OmadeusOrganizationMember): string {
|
|
168
|
-
const fullName = `${member.firstName ?? ""} ${member.lastName ?? ""}`.trim();
|
|
169
|
-
if (member.title?.trim()) {
|
|
170
|
-
return member.title.trim();
|
|
171
|
-
}
|
|
172
|
-
if (fullName) {
|
|
173
|
-
return fullName;
|
|
174
|
-
}
|
|
175
|
-
if (member.email?.trim()) {
|
|
176
|
-
return member.email.trim();
|
|
177
|
-
}
|
|
178
|
-
return `Member ${member.referenceId}`;
|
|
154
|
+
return channels.filter((item) => selected.includes(String(item.id)));
|
|
179
155
|
}
|
|
180
156
|
|
|
181
157
|
function memberHint(member: OmadeusOrganizationMember): string | undefined {
|
|
@@ -190,40 +166,11 @@ async function promptMultiSelect(params: {
|
|
|
190
166
|
options: SelectOption[];
|
|
191
167
|
initialValues?: string[];
|
|
192
168
|
}): Promise<string[]> {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
options: params.options,
|
|
199
|
-
initialValues: params.initialValues,
|
|
200
|
-
initialValue: params.initialValues,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const selected = new Set(params.initialValues ?? []);
|
|
205
|
-
while (true) {
|
|
206
|
-
const next = await params.prompter.select({
|
|
207
|
-
message: `${params.message} (${selected.size} selected)`,
|
|
208
|
-
options: [
|
|
209
|
-
{ value: DONE, label: selected.size > 0 ? "Done" : "Done (select none)" },
|
|
210
|
-
...params.options.map((option) => ({
|
|
211
|
-
...option,
|
|
212
|
-
label: selected.has(option.value) ? `[selected] ${option.label}` : option.label,
|
|
213
|
-
})),
|
|
214
|
-
],
|
|
215
|
-
initialValue: DONE,
|
|
216
|
-
});
|
|
217
|
-
const value = String(next);
|
|
218
|
-
if (value === DONE) {
|
|
219
|
-
return [...selected];
|
|
220
|
-
}
|
|
221
|
-
if (selected.has(value)) {
|
|
222
|
-
selected.delete(value);
|
|
223
|
-
} else {
|
|
224
|
-
selected.add(value);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
169
|
+
return params.prompter.multiselect({
|
|
170
|
+
message: params.message,
|
|
171
|
+
options: params.options,
|
|
172
|
+
initialValues: params.initialValues,
|
|
173
|
+
});
|
|
227
174
|
}
|
|
228
175
|
|
|
229
176
|
async function loadSelectableMembers(params: {
|
|
@@ -241,13 +188,13 @@ async function loadSelectableMembers(params: {
|
|
|
241
188
|
})
|
|
242
189
|
)
|
|
243
190
|
.filter((member) => member.isSystem !== true && !excluded.has(member.referenceId))
|
|
244
|
-
.sort((a, b) =>
|
|
191
|
+
.sort((a, b) => formatMemberLabel(a).localeCompare(formatMemberLabel(b)));
|
|
245
192
|
}
|
|
246
193
|
|
|
247
194
|
function memberOptions(members: OmadeusOrganizationMember[]): SelectOption[] {
|
|
248
195
|
return members.map((member) => ({
|
|
249
196
|
value: String(member.referenceId),
|
|
250
|
-
label:
|
|
197
|
+
label: formatMemberLabel(member),
|
|
251
198
|
hint: memberHint(member),
|
|
252
199
|
}));
|
|
253
200
|
}
|
|
@@ -258,6 +205,27 @@ function readReferenceIds(values: string[]): number[] {
|
|
|
258
205
|
.filter((value) => Number.isInteger(value) && value > 0);
|
|
259
206
|
}
|
|
260
207
|
|
|
208
|
+
async function promptCredentials(
|
|
209
|
+
prompter: WizardPrompter,
|
|
210
|
+
existing: { email?: string; password?: string },
|
|
211
|
+
): Promise<{ email: string; password: string }> {
|
|
212
|
+
const email = String(
|
|
213
|
+
await prompter.text({
|
|
214
|
+
message: "Omadeus username (email)",
|
|
215
|
+
initialValue: existing.email,
|
|
216
|
+
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
|
|
217
|
+
}),
|
|
218
|
+
).trim();
|
|
219
|
+
const password = String(
|
|
220
|
+
await prompter.text({
|
|
221
|
+
message: "Omadeus password",
|
|
222
|
+
sensitive: true,
|
|
223
|
+
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
|
|
224
|
+
}),
|
|
225
|
+
).trim();
|
|
226
|
+
return { email, password };
|
|
227
|
+
}
|
|
228
|
+
|
|
261
229
|
async function promptSenderAllowlist(params: {
|
|
262
230
|
prompter: WizardPrompter;
|
|
263
231
|
message: string;
|
|
@@ -277,7 +245,7 @@ async function promptSenderAllowlist(params: {
|
|
|
277
245
|
],
|
|
278
246
|
initialValue: existingReferenceIds && existingReferenceIds.length > 0 ? "specific" : "all",
|
|
279
247
|
});
|
|
280
|
-
if (
|
|
248
|
+
if (mode === "all") {
|
|
281
249
|
return undefined;
|
|
282
250
|
}
|
|
283
251
|
|
|
@@ -297,16 +265,17 @@ async function promptEntityKindSelection(params: {
|
|
|
297
265
|
const selected = await promptMultiSelect({
|
|
298
266
|
prompter: params.prompter,
|
|
299
267
|
message: "Which entity room types should OpenClaw listen to?",
|
|
300
|
-
options:
|
|
268
|
+
options: OMADEUS_INBOUND_ENTITY_KINDS.map((kind) => ({
|
|
301
269
|
value: kind,
|
|
302
270
|
label: kind,
|
|
303
271
|
})),
|
|
304
272
|
initialValues:
|
|
305
273
|
params.existingKinds && params.existingKinds.length > 0
|
|
306
274
|
? params.existingKinds
|
|
307
|
-
:
|
|
275
|
+
: [...OMADEUS_INBOUND_ENTITY_KINDS],
|
|
308
276
|
});
|
|
309
|
-
|
|
277
|
+
const selectedSet = new Set(selected);
|
|
278
|
+
return OMADEUS_INBOUND_ENTITY_KINDS.filter((kind) => selectedSet.has(kind));
|
|
310
279
|
}
|
|
311
280
|
|
|
312
281
|
export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
@@ -343,7 +312,7 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
343
312
|
credentials: [],
|
|
344
313
|
finalize: async ({ cfg, prompter }) => {
|
|
345
314
|
const account = resolveOmadeusAccount({ cfg });
|
|
346
|
-
const section =
|
|
315
|
+
const section = getOmadeusChannelConfig(cfg) ?? {};
|
|
347
316
|
let next = cfg;
|
|
348
317
|
|
|
349
318
|
if (account.credentialSource === "none") {
|
|
@@ -356,21 +325,10 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
356
325
|
const casUrl = OMADEUS_CAS_URL;
|
|
357
326
|
const maestroUrl = OMADEUS_MAESTRO_URL;
|
|
358
327
|
|
|
359
|
-
let email =
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
|
|
364
|
-
}),
|
|
365
|
-
).trim();
|
|
366
|
-
|
|
367
|
-
let password = String(
|
|
368
|
-
await prompter.text({
|
|
369
|
-
message: "Omadeus password",
|
|
370
|
-
initialValue: section.password ?? envPassword,
|
|
371
|
-
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
|
|
372
|
-
}),
|
|
373
|
-
).trim();
|
|
328
|
+
let { email, password } = await promptCredentials(prompter, {
|
|
329
|
+
email: section.email ?? envEmail,
|
|
330
|
+
password: section.password ?? envPassword,
|
|
331
|
+
});
|
|
374
332
|
|
|
375
333
|
const organizationId = await promptOrganizationId({
|
|
376
334
|
prompter,
|
|
@@ -379,7 +337,6 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
379
337
|
existing: section.organizationId,
|
|
380
338
|
});
|
|
381
339
|
|
|
382
|
-
// Verify the full auth flow before saving
|
|
383
340
|
let sessionToken: string | undefined;
|
|
384
341
|
let selfReferenceId: number | undefined;
|
|
385
342
|
while (true) {
|
|
@@ -396,8 +353,10 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
396
353
|
await prompter.note(`Authenticated as ${payload.email}`, "Omadeus authentication");
|
|
397
354
|
break;
|
|
398
355
|
} catch (err) {
|
|
399
|
-
|
|
400
|
-
|
|
356
|
+
await prompter.note(
|
|
357
|
+
`Authentication failed: ${formatAuthError(err)}`,
|
|
358
|
+
"Omadeus authentication",
|
|
359
|
+
);
|
|
401
360
|
const retry = await prompter.confirm({
|
|
402
361
|
message: "Re-enter email/password and try again?",
|
|
403
362
|
initialValue: true,
|
|
@@ -409,20 +368,7 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
409
368
|
);
|
|
410
369
|
break;
|
|
411
370
|
}
|
|
412
|
-
email =
|
|
413
|
-
await prompter.text({
|
|
414
|
-
message: "Omadeus email",
|
|
415
|
-
initialValue: email,
|
|
416
|
-
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
|
|
417
|
-
}),
|
|
418
|
-
).trim();
|
|
419
|
-
password = String(
|
|
420
|
-
await prompter.text({
|
|
421
|
-
message: "Omadeus password",
|
|
422
|
-
initialValue: password,
|
|
423
|
-
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
|
|
424
|
-
}),
|
|
425
|
-
).trim();
|
|
371
|
+
({ email, password } = await promptCredentials(prompter, { email, password }));
|
|
426
372
|
}
|
|
427
373
|
}
|
|
428
374
|
|
|
@@ -457,12 +403,15 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
457
403
|
existingChannelViewIds: existingInbound?.channels?.allowedChannelViewIds,
|
|
458
404
|
});
|
|
459
405
|
|
|
460
|
-
const channelSenderIds =
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
406
|
+
const channelSenderIds =
|
|
407
|
+
selectedChannels.length > 0
|
|
408
|
+
? await promptSenderAllowlist({
|
|
409
|
+
prompter,
|
|
410
|
+
message: "Which users can trigger OpenClaw from allowed channels?",
|
|
411
|
+
members,
|
|
412
|
+
existingReferenceIds: existingInbound?.channels?.allowedSenderReferenceIds,
|
|
413
|
+
})
|
|
414
|
+
: undefined;
|
|
466
415
|
|
|
467
416
|
const entityKinds = await promptEntityKindSelection({
|
|
468
417
|
prompter,
|
|
@@ -495,11 +444,16 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
495
444
|
const entityKindSummary =
|
|
496
445
|
entityKinds.length > 0 ? entityKinds.join(", ") : "none (entity rooms disabled)";
|
|
497
446
|
|
|
447
|
+
const channelSummary =
|
|
448
|
+
selectedChannels.length > 0
|
|
449
|
+
? `- Channels "${channelTitles}": rooms ${channelRoomIds.join(", ") || "(no room ids)"} from ${senderSummary(channelSenderIds)}; @mention not required in those rooms.`
|
|
450
|
+
: "- Channels: disabled (none selected).";
|
|
451
|
+
|
|
498
452
|
await prompter.note(
|
|
499
453
|
[
|
|
500
454
|
`Inbound policy (Jaguar chat):`,
|
|
501
455
|
`- Direct messages: enabled for ${senderSummary(directSenderIds)} (no @mention required).`,
|
|
502
|
-
|
|
456
|
+
channelSummary,
|
|
503
457
|
`- Entity rooms (${entityKindSummary}): ${senderSummary(entitySenderIds)}; @mention required.`,
|
|
504
458
|
].join("\n"),
|
|
505
459
|
"Omadeus inbound policy",
|
|
@@ -516,7 +470,7 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
516
470
|
email,
|
|
517
471
|
password,
|
|
518
472
|
organizationId,
|
|
519
|
-
|
|
473
|
+
sessionToken,
|
|
520
474
|
inbound: {
|
|
521
475
|
version: 1,
|
|
522
476
|
direct: {
|
|
@@ -525,7 +479,7 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
525
479
|
requireMention: "never",
|
|
526
480
|
},
|
|
527
481
|
channels: {
|
|
528
|
-
enabled:
|
|
482
|
+
enabled: selectedChannels.length > 0,
|
|
529
483
|
allowedRoomIds: channelRoomIds,
|
|
530
484
|
allowedChannelViewIds: channelViewIds,
|
|
531
485
|
...(channelSenderIds ? { allowedSenderReferenceIds: channelSenderIds } : {}),
|
|
@@ -548,7 +502,7 @@ export const omadeusSetupWizard: ChannelSetupWizard = {
|
|
|
548
502
|
...cfg,
|
|
549
503
|
channels: {
|
|
550
504
|
...cfg.channels,
|
|
551
|
-
omadeus: { ...
|
|
505
|
+
omadeus: { ...getOmadeusChannelConfig(cfg), enabled: false },
|
|
552
506
|
},
|
|
553
507
|
}),
|
|
554
508
|
};
|
package/src/setup-core.ts
CHANGED
|
@@ -35,12 +35,21 @@ export const omadeusSetupAdapter: ChannelSetupAdapter = {
|
|
|
35
35
|
const password = input.password?.trim() || undefined;
|
|
36
36
|
const organizationId = readSetupNumberField(rawInput, "organizationId");
|
|
37
37
|
|
|
38
|
+
const channelsRecord = cfg.channels as Record<string, unknown> | undefined;
|
|
39
|
+
const omadeusExisting = channelsRecord?.["omadeus"];
|
|
40
|
+
const omadeusPrevious =
|
|
41
|
+
omadeusExisting !== null &&
|
|
42
|
+
typeof omadeusExisting === "object" &&
|
|
43
|
+
!Array.isArray(omadeusExisting)
|
|
44
|
+
? (omadeusExisting as Record<string, unknown>)
|
|
45
|
+
: {};
|
|
46
|
+
|
|
38
47
|
return {
|
|
39
48
|
...cfg,
|
|
40
49
|
channels: {
|
|
41
50
|
...cfg.channels,
|
|
42
51
|
omadeus: {
|
|
43
|
-
...
|
|
52
|
+
...omadeusPrevious,
|
|
44
53
|
enabled: true,
|
|
45
54
|
...(casUrl ? { casUrl } : {}),
|
|
46
55
|
...(maestroUrl ? { maestroUrl } : {}),
|
package/src/socket/socket.ts
CHANGED
|
@@ -31,10 +31,12 @@ const HEARTBEAT_MISSED_MAX = 5;
|
|
|
31
31
|
const KEEP_ALIVE_CONTENT = "keep-alive";
|
|
32
32
|
const KEEP_ALIVE_ACTION = "answer";
|
|
33
33
|
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
function isServerKeepAlive(data: Record<string, unknown>): boolean {
|
|
35
|
+
return (data as { content?: unknown }).content === KEEP_ALIVE_CONTENT;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isClientKeepAlive(data: Record<string, unknown>): boolean {
|
|
39
|
+
return (data as { data?: unknown }).data === KEEP_ALIVE_CONTENT;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export function createOmadeusSocketClient(opts: OmadeusSocketOptions): OmadeusSocketClient {
|
|
@@ -87,7 +89,7 @@ export function createOmadeusSocketClient(opts: OmadeusSocketOptions): OmadeusSo
|
|
|
87
89
|
return;
|
|
88
90
|
}
|
|
89
91
|
heartbeatMissCount += 1;
|
|
90
|
-
|
|
92
|
+
sendKeepAliveFrame();
|
|
91
93
|
|
|
92
94
|
if (heartbeatMissCount >= HEARTBEAT_MISSED_MAX) {
|
|
93
95
|
log?.warn(
|
|
@@ -104,6 +106,12 @@ export function createOmadeusSocketClient(opts: OmadeusSocketOptions): OmadeusSo
|
|
|
104
106
|
}, HEARTBEAT_INTERVAL_MS);
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
function sendKeepAliveFrame() {
|
|
110
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
111
|
+
ws.send(JSON.stringify({ data: KEEP_ALIVE_CONTENT, action: KEEP_ALIVE_ACTION }));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
107
115
|
function connect() {
|
|
108
116
|
if (ws) {
|
|
109
117
|
ws.removeAllListeners();
|
|
@@ -144,19 +152,24 @@ export function createOmadeusSocketClient(opts: OmadeusSocketOptions): OmadeusSo
|
|
|
144
152
|
const data = JSON.parse(String(raw)) as Record<string, unknown>;
|
|
145
153
|
|
|
146
154
|
const action = (data as { action?: unknown }).action;
|
|
147
|
-
if (
|
|
155
|
+
if (isServerKeepAlive(data) && action === KEEP_ALIVE_ACTION) {
|
|
148
156
|
resetHeartbeat();
|
|
149
157
|
return;
|
|
150
158
|
}
|
|
151
159
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
160
|
+
if (isClientKeepAlive(data) && action === KEEP_ALIVE_ACTION) {
|
|
161
|
+
resetHeartbeat();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// If backend sends a heartbeat ping, answer it immediately.
|
|
166
|
+
if (isServerKeepAlive(data) && action === "heartbeat") {
|
|
167
|
+
resetHeartbeat();
|
|
168
|
+
sendKeepAliveFrame();
|
|
157
169
|
return;
|
|
158
170
|
}
|
|
159
171
|
|
|
172
|
+
resetHeartbeat();
|
|
160
173
|
onEvent?.(data);
|
|
161
174
|
} catch {
|
|
162
175
|
log?.warn(`${logPrefix} unparseable message: ${String(raw).slice(0, 200)}`);
|