@brantrusnak/openclaw-omadeus 1.0.1 → 1.0.3
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 +5 -2
- 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 +115 -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 +363 -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/openclaw.plugin.json +144 -28
- package/package.json +12 -7
- package/src/api/auth.api.ts +0 -29
- package/src/api/channel.api.ts +29 -0
- package/src/api/nugget.api.ts +81 -10
- package/src/channel.ts +136 -247
- package/src/inbound-policy.ts +250 -0
- package/src/inbound.ts +20 -0
- package/src/member-resolve.ts +84 -0
- package/src/message-handler.ts +99 -53
- package/src/nugget-lookup.ts +67 -4
- package/src/onboarding.ts +283 -200
- package/src/setup-core.ts +10 -1
- package/src/socket/socket.ts +24 -11
- package/src/types.ts +77 -7
package/src/channel.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
} from "./api/nugget.api.js";
|
|
29
29
|
import { addMessageReaction, deleteMessage, editMessage } from "./api/message.api.js";
|
|
30
30
|
import {
|
|
31
|
+
getOmadeusChannelConfig,
|
|
31
32
|
listOmadeusAccountIds,
|
|
32
33
|
resolveDefaultOmadeusAccountId,
|
|
33
34
|
resolveOmadeusAccount,
|
|
@@ -44,30 +45,55 @@ import { createJaguarSocketClient, type JaguarSocketClient } from "./socket/jagu
|
|
|
44
45
|
import { createTokenManager, type OmadeusTokenManager } from "./token.js";
|
|
45
46
|
import type { ResolvedOmadeusAccount as Account } from "./types.js";
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
const CHANNEL_ID = "omadeus" as const;
|
|
49
|
+
|
|
50
|
+
const gatewayState: {
|
|
51
|
+
tokenManager: OmadeusTokenManager | null;
|
|
52
|
+
dolphin: DolphinSocketClient | null;
|
|
53
|
+
jaguar: JaguarSocketClient | null;
|
|
54
|
+
} = { tokenManager: null, dolphin: null, jaguar: null };
|
|
55
|
+
|
|
56
|
+
const isUnconfigured = (account: Account) => account.credentialSource === "none";
|
|
57
|
+
|
|
58
|
+
let lastPersistedToken: string | null = null;
|
|
51
59
|
|
|
52
60
|
async function persistSessionToken(token: string): Promise<void> {
|
|
61
|
+
if (lastPersistedToken === token) return;
|
|
53
62
|
const runtime = getOmadeusRuntime();
|
|
54
|
-
const cfg = runtime.config.
|
|
55
|
-
const section = (
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
const cfg = runtime.config.current() as OpenClawConfig;
|
|
64
|
+
const section = getOmadeusChannelConfig(cfg) ?? {};
|
|
65
|
+
if (section.sessionToken === token) {
|
|
66
|
+
lastPersistedToken = token;
|
|
58
67
|
return;
|
|
59
68
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
await runtime.config.mutateConfigFile({
|
|
70
|
+
afterWrite: { mode: "auto" },
|
|
71
|
+
mutate: (draft) => {
|
|
72
|
+
draft.channels = {
|
|
73
|
+
...(draft.channels ?? {}),
|
|
74
|
+
omadeus: {
|
|
75
|
+
...(getOmadeusChannelConfig(draft) ?? {}),
|
|
76
|
+
sessionToken: token,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
69
79
|
},
|
|
70
|
-
}
|
|
80
|
+
});
|
|
81
|
+
lastPersistedToken = token;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function actionError(text: string, error = text) {
|
|
85
|
+
return {
|
|
86
|
+
isError: true as const,
|
|
87
|
+
content: [{ type: "text" as const, text }],
|
|
88
|
+
details: { error },
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function actionOk(payload: Record<string, unknown>) {
|
|
93
|
+
return {
|
|
94
|
+
content: [{ type: "text" as const, text: JSON.stringify({ ok: true, channel: CHANNEL_ID, ...payload }) }],
|
|
95
|
+
details: { ok: true, channel: CHANNEL_ID, ...payload },
|
|
96
|
+
};
|
|
71
97
|
}
|
|
72
98
|
|
|
73
99
|
const omadeusConfigAdapter = createTopLevelChannelConfigAdapter<Account>({
|
|
@@ -83,11 +109,7 @@ const omadeusConfigAdapter = createTopLevelChannelConfigAdapter<Account>({
|
|
|
83
109
|
"password",
|
|
84
110
|
"organizationId",
|
|
85
111
|
"sessionToken",
|
|
86
|
-
"
|
|
87
|
-
"selectedChannelViewId",
|
|
88
|
-
"selectedChannelTitle",
|
|
89
|
-
"selectedChannelPrivateRoomId",
|
|
90
|
-
"selectedChannelPublicRoomId",
|
|
112
|
+
"inbound",
|
|
91
113
|
],
|
|
92
114
|
// Keep adapter contract satisfied even though Omadeus no longer uses DM allowlists.
|
|
93
115
|
resolveAllowFrom: () => [],
|
|
@@ -188,10 +210,10 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
188
210
|
meta: {
|
|
189
211
|
id: "omadeus",
|
|
190
212
|
label: "Omadeus",
|
|
191
|
-
selectionLabel: "Omadeus (WebSocket)",
|
|
192
|
-
docsPath: "
|
|
193
|
-
docsLabel: "
|
|
194
|
-
blurb: "
|
|
213
|
+
selectionLabel: "Omadeus (API + WebSocket)",
|
|
214
|
+
docsPath: "",
|
|
215
|
+
docsLabel: "",
|
|
216
|
+
blurb: "AI-native project management that knows your role, speaks your language, and keeps your team in sync. No noise.",
|
|
195
217
|
},
|
|
196
218
|
capabilities: {
|
|
197
219
|
chatTypes: ["direct", "group"],
|
|
@@ -205,6 +227,9 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
205
227
|
messageToolHints: () => [
|
|
206
228
|
"- Omadeus routing: **send** uses **room id** (`to` / `target`, e.g. `room:117947` or `117947`). **edit**, **delete**, **react** use the Jaguar **message** `id` (`messageId`, or the current inbound message from context).",
|
|
207
229
|
"- Create Omadeus task/nugget: use `action=send` with params `{ op: \"create_task\"|\"create_nugget\", title, description, priority?, stage?, kind?, memberReferenceId?, clientId?, folderId? }`.",
|
|
230
|
+
"- Omadeus **Task** and **Nugget** are distinct product types (Jaguar `subscribableKind`). **Project**, **Sprint**, **Release**, **Folder**, **Client**, **Summary**, etc. also have entity chat rooms. User \"task\" / \"the task\" → map to **this room's** `subscribableKind` (Task vs Nugget vs other), not an OpenClaw background task.",
|
|
231
|
+
"- In Task or Nugget rooms, inbound may include **Dolphin nuggetviews** JSON for this chat's `roomId` — **summarize that** for status questions. The payload may include a **`people`** object (Omadeus member names). Use those for assignees; do not read `referenceId` numbers as names. Do not tell the user to go use the Omadeus app instead of answering from that data or the thread.",
|
|
232
|
+
"- `session_status` / SessionKey: **OpenClaw** gateway only. Use the inbound SessionKey, \"current\", or the hint in **entity** rooms — never a fake `task/<...>` string from a title.",
|
|
208
233
|
`- Reactions only allow these emojis (others are ignored): ${ALLOWED_OMADEUS_REACTION_EMOJI_LIST.join(" ")}`,
|
|
209
234
|
"- Reply in chat with plain text; use the message tool for proactive sends, edits, deletes, or reactions.",
|
|
210
235
|
],
|
|
@@ -213,7 +238,7 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
213
238
|
describeMessageTool: ({ cfg }) => {
|
|
214
239
|
const enabled =
|
|
215
240
|
cfg.channels?.omadeus?.enabled !== false &&
|
|
216
|
-
resolveOmadeusAccount({ cfg })
|
|
241
|
+
!isUnconfigured(resolveOmadeusAccount({ cfg }));
|
|
217
242
|
return {
|
|
218
243
|
actions: enabled ? ["send", "edit", "delete", "react"] : [],
|
|
219
244
|
capabilities: [],
|
|
@@ -221,28 +246,19 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
221
246
|
};
|
|
222
247
|
},
|
|
223
248
|
handleAction: async (ctx) => {
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
if (!
|
|
249
|
+
const account = resolveOmadeusAccount({ cfg: ctx.cfg });
|
|
250
|
+
const apiOpts = () => {
|
|
251
|
+
if (!gatewayState.tokenManager) {
|
|
227
252
|
throw new Error("Omadeus: not connected; gateway must be running with Omadeus enabled.");
|
|
228
253
|
}
|
|
229
|
-
return { maestroUrl: account.maestroUrl, tokenManager:
|
|
254
|
+
return { maestroUrl: account.maestroUrl, tokenManager: gatewayState.tokenManager };
|
|
230
255
|
};
|
|
231
256
|
|
|
232
257
|
if (ctx.action === "send" && isCreateNuggetRequest(ctx.params)) {
|
|
233
258
|
const title = readStringParam(ctx.params, ["title", "subject", "name"]);
|
|
234
259
|
const description = readStringParam(ctx.params, ["description", "details", "body"]);
|
|
235
260
|
if (!title || !description) {
|
|
236
|
-
return
|
|
237
|
-
isError: true,
|
|
238
|
-
content: [
|
|
239
|
-
{
|
|
240
|
-
type: "text" as const,
|
|
241
|
-
text: "Omadeus create task/nugget requires `title` and `description`.",
|
|
242
|
-
},
|
|
243
|
-
],
|
|
244
|
-
details: { error: "Missing title/description." },
|
|
245
|
-
};
|
|
261
|
+
return actionError("Omadeus create task/nugget requires `title` and `description`.", "Missing title/description.");
|
|
246
262
|
}
|
|
247
263
|
|
|
248
264
|
const kind = readNuggetKind(ctx.params);
|
|
@@ -250,25 +266,19 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
250
266
|
const stage = readStringParam(ctx.params, ["stage"]) ?? "Triage";
|
|
251
267
|
const memberReferenceId =
|
|
252
268
|
readNumberParam(ctx.params, ["memberReferenceId", "assigneeReferenceId"]) ??
|
|
253
|
-
|
|
269
|
+
gatewayState.tokenManager?.getPayload().referenceId;
|
|
254
270
|
const clientId = readNumberParam(ctx.params, ["clientId"]) ?? 1;
|
|
255
271
|
const folderId = readNumberParam(ctx.params, ["folderId"]) ?? 1;
|
|
256
272
|
|
|
257
273
|
if (!memberReferenceId) {
|
|
258
|
-
return
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
type: "text" as const,
|
|
263
|
-
text: "Omadeus create task/nugget needs `memberReferenceId` or an active authenticated user.",
|
|
264
|
-
},
|
|
265
|
-
],
|
|
266
|
-
details: { error: "Missing memberReferenceId." },
|
|
267
|
-
};
|
|
274
|
+
return actionError(
|
|
275
|
+
"Omadeus create task/nugget needs `memberReferenceId` or an active authenticated user.",
|
|
276
|
+
"Missing memberReferenceId.",
|
|
277
|
+
);
|
|
268
278
|
}
|
|
269
279
|
|
|
270
280
|
try {
|
|
271
|
-
const created = await createNugget(
|
|
281
|
+
const created = await createNugget(apiOpts(), {
|
|
272
282
|
title,
|
|
273
283
|
description,
|
|
274
284
|
stage,
|
|
@@ -278,39 +288,10 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
278
288
|
clientId,
|
|
279
289
|
folderId,
|
|
280
290
|
});
|
|
281
|
-
|
|
282
|
-
const id = created["id"];
|
|
283
|
-
return {
|
|
284
|
-
content: [
|
|
285
|
-
{
|
|
286
|
-
type: "text" as const,
|
|
287
|
-
text: JSON.stringify({
|
|
288
|
-
ok: true,
|
|
289
|
-
channel: "omadeus",
|
|
290
|
-
action: "create",
|
|
291
|
-
kind,
|
|
292
|
-
number,
|
|
293
|
-
id,
|
|
294
|
-
title,
|
|
295
|
-
}),
|
|
296
|
-
},
|
|
297
|
-
],
|
|
298
|
-
details: {
|
|
299
|
-
ok: true,
|
|
300
|
-
channel: "omadeus",
|
|
301
|
-
action: "create",
|
|
302
|
-
kind,
|
|
303
|
-
number,
|
|
304
|
-
id,
|
|
305
|
-
},
|
|
306
|
-
};
|
|
291
|
+
return actionOk({ action: "create", kind, number: created["number"], id: created["id"], title });
|
|
307
292
|
} catch (err) {
|
|
308
293
|
const msg = err instanceof Error ? err.message : String(err);
|
|
309
|
-
return
|
|
310
|
-
isError: true,
|
|
311
|
-
content: [{ type: "text" as const, text: msg }],
|
|
312
|
-
details: { error: msg },
|
|
313
|
-
};
|
|
294
|
+
return actionError(msg);
|
|
314
295
|
}
|
|
315
296
|
}
|
|
316
297
|
|
|
@@ -322,93 +303,47 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
322
303
|
(typeof ctx.params.content === "string" && ctx.params.content.trim()) ||
|
|
323
304
|
"";
|
|
324
305
|
if (messageId == null) {
|
|
325
|
-
return
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
type: "text" as const,
|
|
330
|
-
text: "Omadeus edit requires `messageId` (Jaguar message id) or current inbound MessageSid.",
|
|
331
|
-
},
|
|
332
|
-
],
|
|
333
|
-
details: { error: "Missing messageId for edit." },
|
|
334
|
-
};
|
|
306
|
+
return actionError(
|
|
307
|
+
"Omadeus edit requires `messageId` (Jaguar message id) or current inbound MessageSid.",
|
|
308
|
+
"Missing messageId for edit.",
|
|
309
|
+
);
|
|
335
310
|
}
|
|
336
311
|
if (!body) {
|
|
337
|
-
return
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
type: "text" as const,
|
|
342
|
-
text: "Omadeus edit requires new text in `message`, `text`, or `content`.",
|
|
343
|
-
},
|
|
344
|
-
],
|
|
345
|
-
details: { error: "Missing body for edit." },
|
|
346
|
-
};
|
|
312
|
+
return actionError(
|
|
313
|
+
"Omadeus edit requires new text in `message`, `text`, or `content`.",
|
|
314
|
+
"Missing body for edit.",
|
|
315
|
+
);
|
|
347
316
|
}
|
|
348
317
|
try {
|
|
349
|
-
await editMessage(
|
|
318
|
+
await editMessage(apiOpts(), { messageId, body });
|
|
350
319
|
} catch (err) {
|
|
351
320
|
const msg = err instanceof Error ? err.message : String(err);
|
|
352
|
-
return
|
|
353
|
-
isError: true,
|
|
354
|
-
content: [{ type: "text" as const, text: msg }],
|
|
355
|
-
details: { error: msg },
|
|
356
|
-
};
|
|
321
|
+
return actionError(msg);
|
|
357
322
|
}
|
|
358
|
-
return {
|
|
359
|
-
content: [
|
|
360
|
-
{
|
|
361
|
-
type: "text" as const,
|
|
362
|
-
text: JSON.stringify({ ok: true, channel: "omadeus", action: "edit", messageId }),
|
|
363
|
-
},
|
|
364
|
-
],
|
|
365
|
-
details: { ok: true, channel: "omadeus", messageId },
|
|
366
|
-
};
|
|
323
|
+
return actionOk({ action: "edit", messageId });
|
|
367
324
|
}
|
|
368
325
|
|
|
369
326
|
if (ctx.action === "delete") {
|
|
370
327
|
const messageId = readReactionMessageId(ctx.params, ctx.toolContext);
|
|
371
328
|
if (messageId == null) {
|
|
372
|
-
return
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
type: "text" as const,
|
|
377
|
-
text: "Omadeus delete requires `messageId` (Jaguar message id) or current inbound MessageSid.",
|
|
378
|
-
},
|
|
379
|
-
],
|
|
380
|
-
details: { error: "Missing messageId for delete." },
|
|
381
|
-
};
|
|
329
|
+
return actionError(
|
|
330
|
+
"Omadeus delete requires `messageId` (Jaguar message id) or current inbound MessageSid.",
|
|
331
|
+
"Missing messageId for delete.",
|
|
332
|
+
);
|
|
382
333
|
}
|
|
383
334
|
try {
|
|
384
|
-
await deleteMessage(
|
|
335
|
+
await deleteMessage(apiOpts(), { messageId });
|
|
385
336
|
} catch (err) {
|
|
386
337
|
const msg = err instanceof Error ? err.message : String(err);
|
|
387
|
-
return
|
|
388
|
-
isError: true,
|
|
389
|
-
content: [{ type: "text" as const, text: msg }],
|
|
390
|
-
details: { error: msg },
|
|
391
|
-
};
|
|
338
|
+
return actionError(msg);
|
|
392
339
|
}
|
|
393
|
-
return {
|
|
394
|
-
content: [
|
|
395
|
-
{
|
|
396
|
-
type: "text" as const,
|
|
397
|
-
text: JSON.stringify({ ok: true, channel: "omadeus", action: "delete", messageId }),
|
|
398
|
-
},
|
|
399
|
-
],
|
|
400
|
-
details: { ok: true, channel: "omadeus", messageId },
|
|
401
|
-
};
|
|
340
|
+
return actionOk({ action: "delete", messageId });
|
|
402
341
|
}
|
|
403
342
|
|
|
404
343
|
if (ctx.action === "react") {
|
|
405
344
|
const emoji = typeof ctx.params.emoji === "string" ? ctx.params.emoji.trim() : "";
|
|
406
345
|
if (!emoji) {
|
|
407
|
-
return
|
|
408
|
-
isError: true,
|
|
409
|
-
content: [{ type: "text" as const, text: "Omadeus react requires `emoji`." }],
|
|
410
|
-
details: { error: "Omadeus react requires emoji." },
|
|
411
|
-
};
|
|
346
|
+
return actionError("Omadeus react requires `emoji`.", "Omadeus react requires emoji.");
|
|
412
347
|
}
|
|
413
348
|
if (!isAllowedOmadeusReactionEmoji(emoji)) {
|
|
414
349
|
return {
|
|
@@ -417,7 +352,7 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
417
352
|
type: "text" as const,
|
|
418
353
|
text: JSON.stringify({
|
|
419
354
|
ok: true,
|
|
420
|
-
channel:
|
|
355
|
+
channel: CHANNEL_ID,
|
|
421
356
|
ignored: true,
|
|
422
357
|
reason: "unsupported_emoji",
|
|
423
358
|
emoji,
|
|
@@ -425,84 +360,42 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
425
360
|
}),
|
|
426
361
|
},
|
|
427
362
|
],
|
|
428
|
-
details: {
|
|
429
|
-
ok: true,
|
|
430
|
-
ignored: true,
|
|
431
|
-
channel: "omadeus",
|
|
432
|
-
},
|
|
363
|
+
details: { ok: true, ignored: true, channel: CHANNEL_ID },
|
|
433
364
|
};
|
|
434
365
|
}
|
|
435
366
|
const messageId = readReactionMessageId(ctx.params, ctx.toolContext);
|
|
436
367
|
if (messageId == null) {
|
|
437
|
-
return
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
type: "text" as const,
|
|
442
|
-
text: "Omadeus react requires `messageId` or a current inbound message id (MessageSid).",
|
|
443
|
-
},
|
|
444
|
-
],
|
|
445
|
-
details: { error: "Missing messageId for reaction." },
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
if (!activeTokenManager) {
|
|
449
|
-
return {
|
|
450
|
-
isError: true,
|
|
451
|
-
content: [
|
|
452
|
-
{
|
|
453
|
-
type: "text" as const,
|
|
454
|
-
text: "Omadeus is not connected; cannot react (gateway must be running).",
|
|
455
|
-
},
|
|
456
|
-
],
|
|
457
|
-
details: { error: "Omadeus not connected." },
|
|
458
|
-
};
|
|
368
|
+
return actionError(
|
|
369
|
+
"Omadeus react requires `messageId` or a current inbound message id (MessageSid).",
|
|
370
|
+
"Missing messageId for reaction.",
|
|
371
|
+
);
|
|
459
372
|
}
|
|
460
373
|
try {
|
|
461
|
-
await addMessageReaction(
|
|
374
|
+
await addMessageReaction(apiOpts(), { messageId, emoji });
|
|
462
375
|
} catch (err) {
|
|
463
376
|
const msg = err instanceof Error ? err.message : String(err);
|
|
464
|
-
return
|
|
465
|
-
isError: true,
|
|
466
|
-
content: [{ type: "text" as const, text: msg }],
|
|
467
|
-
details: { error: msg },
|
|
468
|
-
};
|
|
377
|
+
return actionError(msg);
|
|
469
378
|
}
|
|
470
|
-
return {
|
|
471
|
-
content: [
|
|
472
|
-
{
|
|
473
|
-
type: "text" as const,
|
|
474
|
-
text: JSON.stringify({
|
|
475
|
-
ok: true,
|
|
476
|
-
channel: "omadeus",
|
|
477
|
-
messageId,
|
|
478
|
-
emoji,
|
|
479
|
-
}),
|
|
480
|
-
},
|
|
481
|
-
],
|
|
482
|
-
details: { ok: true, channel: "omadeus", messageId, emoji },
|
|
483
|
-
};
|
|
379
|
+
return actionOk({ messageId, emoji });
|
|
484
380
|
}
|
|
485
|
-
|
|
486
|
-
|
|
381
|
+
|
|
382
|
+
throw new Error(`Unhandled Omadeus action: ${String(ctx.action)}`);
|
|
487
383
|
},
|
|
488
384
|
},
|
|
489
385
|
reload: { configPrefixes: ["channels.omadeus"] },
|
|
490
386
|
setup: omadeusSetupAdapter,
|
|
491
387
|
setupWizard: omadeusSetupWizard,
|
|
492
388
|
|
|
493
|
-
// -------------------------------------------------------------------------
|
|
494
|
-
// Config adapter
|
|
495
|
-
// -------------------------------------------------------------------------
|
|
496
389
|
config: {
|
|
497
390
|
...omadeusConfigAdapter,
|
|
498
|
-
isConfigured: (account) => account
|
|
391
|
+
isConfigured: (account) => !isUnconfigured(account),
|
|
499
392
|
unconfiguredReason: () =>
|
|
500
393
|
"Omadeus requires email, password, and organizationId. Run: openclaw setup omadeus",
|
|
501
394
|
describeAccount: (account) => ({
|
|
502
395
|
accountId: account.accountId,
|
|
503
396
|
name: account.name,
|
|
504
397
|
enabled: account.enabled,
|
|
505
|
-
configured: account
|
|
398
|
+
configured: !isUnconfigured(account),
|
|
506
399
|
credentialSource: account.credentialSource,
|
|
507
400
|
baseUrl: account.maestroUrl,
|
|
508
401
|
}),
|
|
@@ -520,13 +413,13 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
520
413
|
const id = normalizeOmadeusRoomId(input);
|
|
521
414
|
if (!id) {
|
|
522
415
|
const taskIntent = parseTaskChannelTargetIntent(input);
|
|
523
|
-
if (!taskIntent || !
|
|
416
|
+
if (!taskIntent || !gatewayState.tokenManager) {
|
|
524
417
|
return null;
|
|
525
418
|
}
|
|
526
419
|
const roomId = await resolveTaskRoomIdByNumber(
|
|
527
420
|
{
|
|
528
421
|
maestroUrl: resolveOmadeusAccount({ cfg }).maestroUrl,
|
|
529
|
-
tokenManager:
|
|
422
|
+
tokenManager: gatewayState.tokenManager,
|
|
530
423
|
},
|
|
531
424
|
{ nuggetNumber: taskIntent.nuggetNumber },
|
|
532
425
|
);
|
|
@@ -550,18 +443,15 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
550
443
|
},
|
|
551
444
|
},
|
|
552
445
|
|
|
553
|
-
// -------------------------------------------------------------------------
|
|
554
|
-
// Outbound adapter
|
|
555
|
-
// -------------------------------------------------------------------------
|
|
556
446
|
outbound: {
|
|
557
447
|
deliveryMode: "direct",
|
|
558
448
|
textChunkLimit: 4000,
|
|
559
449
|
chunker: (text, limit) => getOmadeusRuntime().channel.text.chunkMarkdownText(text, limit),
|
|
560
450
|
chunkerMode: "markdown",
|
|
561
451
|
...createAttachedChannelResultAdapter({
|
|
562
|
-
channel:
|
|
452
|
+
channel: CHANNEL_ID,
|
|
563
453
|
sendText: async ({ cfg, to, text }) => {
|
|
564
|
-
if (!
|
|
454
|
+
if (!gatewayState.jaguar || !gatewayState.tokenManager) {
|
|
565
455
|
throw new Error("Omadeus: not connected. Is the gateway running with Omadeus enabled?");
|
|
566
456
|
}
|
|
567
457
|
const deps: OutboundDeps = {
|
|
@@ -569,9 +459,9 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
569
459
|
maestroUrl: resolveOmadeusAccount({
|
|
570
460
|
cfg,
|
|
571
461
|
}).maestroUrl,
|
|
572
|
-
tokenManager:
|
|
462
|
+
tokenManager: gatewayState.tokenManager,
|
|
573
463
|
},
|
|
574
|
-
jaguarSocket:
|
|
464
|
+
jaguarSocket: gatewayState.jaguar,
|
|
575
465
|
};
|
|
576
466
|
return await sendOmadeusMessage(deps, { to, text });
|
|
577
467
|
},
|
|
@@ -593,9 +483,6 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
593
483
|
},
|
|
594
484
|
},
|
|
595
485
|
|
|
596
|
-
// -------------------------------------------------------------------------
|
|
597
|
-
// Status adapter
|
|
598
|
-
// -------------------------------------------------------------------------
|
|
599
486
|
status: {
|
|
600
487
|
defaultRuntime: defaultRuntimeState,
|
|
601
488
|
collectStatusIssues: (accounts): ChannelStatusIssue[] =>
|
|
@@ -603,7 +490,7 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
603
490
|
const issues: ChannelStatusIssue[] = [];
|
|
604
491
|
if (entry.enabled !== false && entry.configured !== true) {
|
|
605
492
|
issues.push({
|
|
606
|
-
channel:
|
|
493
|
+
channel: CHANNEL_ID,
|
|
607
494
|
accountId: String(entry.accountId ?? DEFAULT_ACCOUNT_ID),
|
|
608
495
|
kind: "config",
|
|
609
496
|
message: "Omadeus credentials are missing.",
|
|
@@ -626,7 +513,7 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
626
513
|
accountId: account.accountId,
|
|
627
514
|
name: account.name,
|
|
628
515
|
enabled: account.enabled,
|
|
629
|
-
configured: account
|
|
516
|
+
configured: !isUnconfigured(account),
|
|
630
517
|
runtime,
|
|
631
518
|
}),
|
|
632
519
|
baseUrl: account.maestroUrl,
|
|
@@ -636,15 +523,12 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
636
523
|
}),
|
|
637
524
|
},
|
|
638
525
|
|
|
639
|
-
// -------------------------------------------------------------------------
|
|
640
|
-
// Gateway adapter — starts sockets on gateway boot
|
|
641
|
-
// -------------------------------------------------------------------------
|
|
642
526
|
gateway: {
|
|
643
527
|
startAccount: async (ctx) => {
|
|
644
528
|
const { account, cfg, abortSignal } = ctx;
|
|
645
529
|
ctx.log?.info(`[omadeus] starting for org ${account.organizationId}`);
|
|
646
530
|
|
|
647
|
-
if (account
|
|
531
|
+
if (isUnconfigured(account)) {
|
|
648
532
|
ctx.log?.warn("[omadeus] skipping start: credentials not configured");
|
|
649
533
|
ctx.setStatus({
|
|
650
534
|
accountId: account.accountId,
|
|
@@ -666,8 +550,8 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
666
550
|
}
|
|
667
551
|
|
|
668
552
|
const log = ctx.log ?? { info: () => {}, warn: () => {}, error: () => {} };
|
|
553
|
+
let isConnected = false;
|
|
669
554
|
|
|
670
|
-
// Auth
|
|
671
555
|
const tokenManager = createTokenManager({
|
|
672
556
|
casUrl: account.casUrl,
|
|
673
557
|
maestroUrl: account.maestroUrl,
|
|
@@ -697,7 +581,7 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
697
581
|
}
|
|
698
582
|
|
|
699
583
|
tokenManager.startAutoRefresh();
|
|
700
|
-
|
|
584
|
+
gatewayState.tokenManager = tokenManager;
|
|
701
585
|
|
|
702
586
|
const selfReferenceId = tokenManager.getPayload().referenceId;
|
|
703
587
|
|
|
@@ -711,9 +595,9 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
711
595
|
runtime: ctx.runtime,
|
|
712
596
|
log,
|
|
713
597
|
outboundDeps,
|
|
598
|
+
selfReferenceId,
|
|
714
599
|
});
|
|
715
600
|
|
|
716
|
-
// Jaguar socket (chat — DMs, nugget/task/project rooms)
|
|
717
601
|
const jaguar = createJaguarSocketClient({
|
|
718
602
|
maestroUrl: account.maestroUrl,
|
|
719
603
|
tokenManager,
|
|
@@ -742,17 +626,19 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
742
626
|
onOtherEvent: (data) => {
|
|
743
627
|
log.info(`[jaguar] non-message event: ${JSON.stringify(data).slice(0, 120)}`);
|
|
744
628
|
},
|
|
745
|
-
onConnect: () =>
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
connected: true,
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
onDisconnect: () =>
|
|
629
|
+
onConnect: () => {
|
|
630
|
+
if (!isConnected) {
|
|
631
|
+
isConnected = true;
|
|
632
|
+
ctx.setStatus({ accountId: account.accountId, connected: true, lastConnectedAt: Date.now() });
|
|
633
|
+
}
|
|
634
|
+
},
|
|
635
|
+
onDisconnect: () => {
|
|
636
|
+
isConnected = false;
|
|
637
|
+
ctx.setStatus({ accountId: account.accountId, connected: false });
|
|
638
|
+
},
|
|
752
639
|
onError: (err) => ctx.setStatus({ accountId: account.accountId, lastError: err.message }),
|
|
753
640
|
});
|
|
754
641
|
|
|
755
|
-
// Dolphin socket (data — tasks, projects, sprints, releases)
|
|
756
642
|
const dolphin = createDolphinSocketClient({
|
|
757
643
|
maestroUrl: account.maestroUrl,
|
|
758
644
|
tokenManager,
|
|
@@ -761,13 +647,16 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
761
647
|
log.info(`[dolphin] event: ${JSON.stringify(data).slice(0, 120)}`);
|
|
762
648
|
// TODO: handle task assignment/update events as they are discovered
|
|
763
649
|
},
|
|
764
|
-
onConnect: () =>
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
connected: true,
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
onDisconnect: () =>
|
|
650
|
+
onConnect: () => {
|
|
651
|
+
if (!isConnected) {
|
|
652
|
+
isConnected = true;
|
|
653
|
+
ctx.setStatus({ accountId: account.accountId, connected: true, lastConnectedAt: Date.now() });
|
|
654
|
+
}
|
|
655
|
+
},
|
|
656
|
+
onDisconnect: () => {
|
|
657
|
+
isConnected = false;
|
|
658
|
+
ctx.setStatus({ accountId: account.accountId, connected: false });
|
|
659
|
+
},
|
|
771
660
|
onError: (err) => ctx.setStatus({ accountId: account.accountId, lastError: err.message }),
|
|
772
661
|
});
|
|
773
662
|
|
|
@@ -776,8 +665,8 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
776
665
|
|
|
777
666
|
jaguar.connect();
|
|
778
667
|
dolphin.connect();
|
|
779
|
-
|
|
780
|
-
|
|
668
|
+
gatewayState.jaguar = jaguar;
|
|
669
|
+
gatewayState.dolphin = dolphin;
|
|
781
670
|
|
|
782
671
|
ctx.setStatus({
|
|
783
672
|
accountId: account.accountId,
|
|
@@ -792,9 +681,10 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
792
681
|
tokenManager.stopAutoRefresh();
|
|
793
682
|
jaguar.disconnect();
|
|
794
683
|
dolphin.disconnect();
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
684
|
+
gatewayState.tokenManager = null;
|
|
685
|
+
gatewayState.jaguar = null;
|
|
686
|
+
gatewayState.dolphin = null;
|
|
687
|
+
lastPersistedToken = null;
|
|
798
688
|
ctx.setStatus({
|
|
799
689
|
accountId: account.accountId,
|
|
800
690
|
running: false,
|
|
@@ -802,7 +692,6 @@ export const omadeusPlugin: ChannelPlugin<Account> = {
|
|
|
802
692
|
});
|
|
803
693
|
};
|
|
804
694
|
|
|
805
|
-
// Keep this account runner alive until the gateway aborts it.
|
|
806
695
|
await new Promise<void>((resolve) => {
|
|
807
696
|
if (abortSignal.aborted) {
|
|
808
697
|
resolve();
|