oomi-ai 0.2.7 → 0.2.12

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.
@@ -32,8 +32,22 @@ function parseAccounts(rawAccounts) {
32
32
  return accounts;
33
33
  }
34
34
 
35
+ function extractChannelConfig(cfg = {}) {
36
+ if (!cfg || typeof cfg !== 'object') return {};
37
+ if (cfg.channels && typeof cfg.channels === 'object' && cfg.channels[CHANNEL_ID] && typeof cfg.channels[CHANNEL_ID] === 'object') {
38
+ return cfg.channels[CHANNEL_ID];
39
+ }
40
+ if (cfg[CHANNEL_ID] && typeof cfg[CHANNEL_ID] === 'object') {
41
+ return cfg[CHANNEL_ID];
42
+ }
43
+ if (cfg.accounts && typeof cfg.accounts === 'object') {
44
+ return cfg;
45
+ }
46
+ return {};
47
+ }
48
+
35
49
  function normalizeConfig(cfg = {}) {
36
- const channelConfig = cfg?.channels?.[CHANNEL_ID] || {};
50
+ const channelConfig = extractChannelConfig(cfg);
37
51
  const configuredAccounts = parseAccounts(channelConfig.accounts);
38
52
  const accountIds = Object.keys(configuredAccounts);
39
53
  const defaultAccountId = toString(channelConfig.defaultAccountId, accountIds[0] || 'default');
@@ -125,6 +139,45 @@ function extractUserId(payload) {
125
139
  return '';
126
140
  }
127
141
 
142
+ function nextMessageId() {
143
+ return `oomi_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
144
+ }
145
+
146
+ function extractMessageId(payload) {
147
+ const candidates = [
148
+ payload?.messageId,
149
+ payload?.id,
150
+ payload?.requestId,
151
+ payload?.idempotencyKey,
152
+ payload?.metadata?.messageId,
153
+ payload?.metadata?.idempotencyKey,
154
+ ];
155
+
156
+ for (const candidate of candidates) {
157
+ const value = toString(candidate);
158
+ if (value) return value;
159
+ }
160
+
161
+ return nextMessageId();
162
+ }
163
+
164
+ function extractCorrelationId(payload) {
165
+ const candidates = [
166
+ payload?.correlationId,
167
+ payload?.metadata?.correlationId,
168
+ payload?.requestId,
169
+ payload?.messageId,
170
+ payload?.id,
171
+ ];
172
+
173
+ for (const candidate of candidates) {
174
+ const value = toString(candidate);
175
+ if (value) return value;
176
+ }
177
+
178
+ return '';
179
+ }
180
+
128
181
  async function postJson({ url, token, body, timeoutMs }) {
129
182
  const controller = new AbortController();
130
183
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
@@ -221,12 +274,16 @@ const oomiChannelPlugin = {
221
274
  const conversationKey = extractConversationKey(payload);
222
275
  const userId = extractUserId(payload);
223
276
  const sessionKey = toString(payload?.sessionKey || payload?.metadata?.sessionKey, account.defaultSessionKey);
277
+ const messageId = extractMessageId(payload);
278
+ const correlationId = extractCorrelationId(payload);
224
279
 
225
280
  const response = await postJson({
226
281
  url: `${account.backendUrl}/v1/channel/plugin/messages`,
227
282
  token: account.deviceToken,
228
283
  timeoutMs: account.requestTimeoutMs,
229
284
  body: {
285
+ messageId,
286
+ correlationId,
230
287
  conversationKey,
231
288
  userId,
232
289
  sessionKey,
@@ -234,15 +291,18 @@ const oomiChannelPlugin = {
234
291
  source: 'openclaw.channel',
235
292
  metadata: {
236
293
  accountId: resolvedAccountId,
294
+ correlationId,
237
295
  },
238
296
  },
239
297
  });
240
298
 
241
299
  if (!response.ok) {
242
300
  const reason = toString(response.payload?.error, `status ${response.status}`);
301
+ const code = toString(response.payload?.errorCode);
243
302
  return {
244
303
  ok: false,
245
- error: `oomi plugin message publish failed: ${reason}`,
304
+ error: `oomi plugin message publish failed: ${reason}${code ? ` (code=${code})` : ''}`,
305
+ code,
246
306
  };
247
307
  }
248
308
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oomi-ai",
3
- "version": "0.2.7",
3
+ "version": "0.2.12",
4
4
  "description": "Oomi CLI for OpenClaw setup",
5
5
  "bin": {
6
6
  "oomi": "bin/oomi-ai.js"
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "scripts": {
49
49
  "check": "node --check bin/oomi-ai.js",
50
- "test": "node --test test/sessionBridgeState.test.mjs"
50
+ "test": "node --test test/*.test.mjs"
51
51
  },
52
52
  "dependencies": {
53
53
  "ws": "^8.19.0"
@@ -26,10 +26,14 @@ If the user asks to connect OpenClaw to Oomi voice/web:
26
26
  5. Configure channel account (`channels.oomi.accounts.default`) from:
27
27
  - `oomi openclaw plugin --show-secrets --backend-url https://api.oomi.ai`
28
28
  6. Restart OpenClaw.
29
- 7. If user provides an app URL (for example "Connect yourself to Oomi. Use app URL <URL>."):
29
+ 7. Ensure exactly one bridge worker is running (singleton handler):
30
+ - `oomi openclaw bridge ensure --detach`
31
+ - If stale/disconnected: `oomi openclaw bridge restart --detach`
32
+ - On macOS, prefer supervised mode: `oomi openclaw bridge service install`
33
+ 8. If user provides an app URL (for example "Connect yourself to Oomi. Use app URL <URL>."):
30
34
  - Use that URL directly in the pair command.
31
35
  - Example: `oomi openclaw pair --app-url <URL> --no-start`
32
- 8. Return this exact result format to the user:
36
+ 9. Return this exact result format to the user:
33
37
  - `Oomi Connect Ready`
34
38
  - `Auth Link: ...`
35
39
  - No extra narrative text before or after those lines.