@brantrusnak/openclaw-omadeus 1.0.0

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/src/types.ts ADDED
@@ -0,0 +1,186 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Omadeus config shape (stored under channels.omadeus in OpenClaw config)
3
+ // ---------------------------------------------------------------------------
4
+
5
+ export type OmadeusChannelConfig = {
6
+ enabled?: boolean;
7
+ casUrl?: string;
8
+ maestroUrl?: string;
9
+ email?: string;
10
+ password?: string;
11
+ organizationId?: number;
12
+ /** Cached Omadeus session JWT obtained during onboarding/startup. */
13
+ sessionToken?: string;
14
+ /** Selected member reference ID (account) used by onboarding. */
15
+ selectedMemberReferenceId?: number;
16
+ /** Selected channel metadata used for inbound filtering. */
17
+ selectedChannelViewId?: number;
18
+ selectedChannelTitle?: string;
19
+ selectedChannelPrivateRoomId?: number;
20
+ selectedChannelPublicRoomId?: number;
21
+ };
22
+
23
+ export type ResolvedOmadeusAccount = {
24
+ accountId: string;
25
+ name?: string;
26
+ enabled: boolean;
27
+ config: OmadeusChannelConfig;
28
+ casUrl: string;
29
+ maestroUrl: string;
30
+ email: string;
31
+ password: string;
32
+ organizationId: number;
33
+ sessionToken?: string;
34
+ /** "none" if neither config/env credentials nor cached session token exist */
35
+ credentialSource: "config" | "env" | "session" | "none";
36
+ };
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // CAS auth types
40
+ // ---------------------------------------------------------------------------
41
+
42
+ export type CasTokenResponse = {
43
+ token?: string;
44
+ };
45
+
46
+ export type CasAuthorizationCodeResponse = {
47
+ authorizationCode?: string;
48
+ code?: string;
49
+ };
50
+
51
+ export type OmadeusSessionTokenResponse = {
52
+ token: string;
53
+ };
54
+
55
+ export type OmadeusOrganization = {
56
+ id: number;
57
+ title: string;
58
+ plan: string;
59
+ membersCount: number;
60
+ createdAt: string;
61
+ };
62
+
63
+ export type OmadeusOrganizationMember = {
64
+ referenceId: number;
65
+ id: number;
66
+ firstName?: string;
67
+ lastName?: string;
68
+ title?: string;
69
+ email?: string;
70
+ isSystem?: boolean;
71
+ };
72
+
73
+ export type OmadeusChannelView = {
74
+ id: number;
75
+ title: string;
76
+ type?: string;
77
+ privateRoomId?: number | null;
78
+ publicRoomId?: number | null;
79
+ privateRoomTitle?: string | null;
80
+ publicRoomTitle?: string | null;
81
+ };
82
+
83
+ // ---------------------------------------------------------------------------
84
+ // JWT decoded payload (only fields we need)
85
+ // ---------------------------------------------------------------------------
86
+
87
+ export type OmadeusJwtPayload = {
88
+ id: number;
89
+ email: string;
90
+ firstName?: string;
91
+ lastName?: string;
92
+ title?: string;
93
+ referenceId: number;
94
+ sessionId: string;
95
+ organizationId: number;
96
+ roles: string[];
97
+ exp: number;
98
+ };
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // Jaguar socket message (chat — DMs, nugget rooms, task rooms, etc.)
102
+ // ---------------------------------------------------------------------------
103
+
104
+ export type OmadeusSubscribableType =
105
+ | "direct"
106
+ | "channel"
107
+ | "nugget"
108
+ | "project"
109
+ | "sprint"
110
+ | "release"
111
+ | "summary"
112
+ | "client"
113
+ | "folder"
114
+ | (string & {});
115
+ export type OmadeusSubscribableKind =
116
+ | "task"
117
+ | "direct"
118
+ | "channel"
119
+ | "nugget"
120
+ | "project"
121
+ | "sprint"
122
+ | "release"
123
+ | "summary"
124
+ | "client"
125
+ | "folder"
126
+ | (string & {});
127
+
128
+ export type OmadeusMessage = {
129
+ id: number;
130
+ temporaryId?: string;
131
+ type: "message";
132
+ roomId: number;
133
+ senderId: number;
134
+ senderReferenceId: number;
135
+ organizationId: number;
136
+ body: string;
137
+ roomName: string | null;
138
+ subscribableType: OmadeusSubscribableType;
139
+ subscribableKind: OmadeusSubscribableKind;
140
+ createdAtTimestamp: number;
141
+ mimetype: string;
142
+ filename: string | null;
143
+ fileLength: number | null;
144
+ duration: number | null;
145
+ details: string | null;
146
+ replyRootId: number | null;
147
+ attachmentUrl: string | null;
148
+ speechFileUrl: string | null;
149
+ reactions: Record<string, unknown>;
150
+ threadRoomId: number | null;
151
+ replyTo: unknown | null;
152
+ createdAt: string;
153
+ removedAt: string | null;
154
+ metadata: unknown | null;
155
+ isMute: boolean;
156
+ isSeen: boolean;
157
+ };
158
+
159
+ /** Parsed details.rawMessage field for @mention detection. */
160
+ export type OmadeusMessageDetails = {
161
+ rawMessage?: string;
162
+ };
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // Dolphin socket events (task/data — assignments, updates, etc.)
166
+ // ---------------------------------------------------------------------------
167
+
168
+ export type DolphinSocketEvent = Record<string, unknown>;
169
+
170
+ // ---------------------------------------------------------------------------
171
+ // Inbound message (normalized for OpenClaw)
172
+ // ---------------------------------------------------------------------------
173
+
174
+ export type OmadeusInboundMessage = {
175
+ /** Jaguar message id (used for reactions, replies, etc.). */
176
+ messageId: number;
177
+ from: string;
178
+ fromReferenceId: number;
179
+ content: string;
180
+ roomId: number;
181
+ roomName: string | null;
182
+ subscribableType: OmadeusSubscribableType;
183
+ subscribableKind: OmadeusSubscribableKind;
184
+ isMention: boolean;
185
+ timestamp: number;
186
+ };
@@ -0,0 +1,62 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import type { OmadeusTokenManager } from "../token.js";
3
+
4
+ export type OmadeusApiOptions = {
5
+ maestroUrl: string;
6
+ tokenManager: OmadeusTokenManager;
7
+ };
8
+
9
+ export function authHeaders(token: string): Record<string, string> {
10
+ return {
11
+ Authorization: `Bearer ${token}`,
12
+ "Content-Type": "application/json",
13
+ };
14
+ }
15
+
16
+ export async function apiFetch(
17
+ opts: OmadeusApiOptions,
18
+ path: string,
19
+ init?: RequestInit,
20
+ ): Promise<Response> {
21
+ const token = opts.tokenManager.getToken();
22
+ if (!token) throw new Error("Omadeus: not authenticated");
23
+ const url = `${opts.maestroUrl}${path}`;
24
+ try {
25
+ return await fetch(url, {
26
+ ...init,
27
+ headers: { ...authHeaders(token), ...(init?.headers as Record<string, string>) },
28
+ });
29
+ } catch (err) {
30
+ const message = err instanceof Error ? err.message : String(err);
31
+ throw new Error(`Omadeus API request to ${url} failed: ${message}`);
32
+ }
33
+ }
34
+
35
+ function withApiPrefix(prefix: string, path: string): string {
36
+ if (!path) return prefix;
37
+ if (path.startsWith("/")) return `${prefix}${path}`;
38
+ return `${prefix}/${path}`;
39
+ }
40
+
41
+ const JAGUAR_PREFIX = "/jaguar/apiv1";
42
+ const DOLPHIN_PREFIX = "/dolphin/apiv1";
43
+
44
+ export async function jaguarFetch(
45
+ opts: OmadeusApiOptions,
46
+ path: string,
47
+ init?: RequestInit,
48
+ ): Promise<Response> {
49
+ return apiFetch(opts, withApiPrefix(JAGUAR_PREFIX, path), init);
50
+ }
51
+
52
+ export async function dolphinFetch(
53
+ opts: OmadeusApiOptions,
54
+ path: string,
55
+ init?: RequestInit,
56
+ ): Promise<Response> {
57
+ return apiFetch(opts, withApiPrefix(DOLPHIN_PREFIX, path), init);
58
+ }
59
+
60
+ export function generateTemporaryId(): string {
61
+ return `_${randomUUID().replace(/-/g, "").slice(0, 10)}`;
62
+ }
@@ -0,0 +1,17 @@
1
+ import type { OmadeusJwtPayload } from "../types.js";
2
+
3
+ /** Decode the payload portion of a JWT without verifying the signature. */
4
+ export function decodeJwtPayload(token: string): OmadeusJwtPayload {
5
+ const parts = token.split(".");
6
+ if (parts.length !== 3) {
7
+ throw new Error("Invalid JWT: expected 3 parts");
8
+ }
9
+ const payload = Buffer.from(parts[1]!, "base64url").toString("utf-8");
10
+ return JSON.parse(payload) as OmadeusJwtPayload;
11
+ }
12
+
13
+ /** Returns ms until the token expires (negative = already expired). */
14
+ export function tokenExpiresInMs(token: string): number {
15
+ const { exp } = decodeJwtPayload(token);
16
+ return exp * 1000 - Date.now();
17
+ }