spectrum-ts 0.4.0 → 0.6.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.
@@ -3,14 +3,17 @@ import {
3
3
  } from "../../chunk-HXM64ENV.js";
4
4
  import {
5
5
  asAttachment,
6
+ asContact,
6
7
  asCustom,
8
+ fromVCard,
7
9
  mergeStreams,
8
- stream
9
- } from "../../chunk-5XW4CAWS.js";
10
+ stream,
11
+ toVCard
12
+ } from "../../chunk-UZWRB3FZ.js";
10
13
  import {
11
14
  asText,
12
15
  definePlatform
13
- } from "../../chunk-XEEDIGVK.js";
16
+ } from "../../chunk-XZTTLPHE.js";
14
17
 
15
18
  // src/providers/imessage/index.ts
16
19
  import { createClient as createClient2, directChat } from "@photon-ai/advanced-imessage";
@@ -104,41 +107,74 @@ async function disposeCloudAuth(clients) {
104
107
 
105
108
  // src/providers/imessage/local.ts
106
109
  import { createReadStream } from "fs";
107
- import { unlink, writeFile } from "fs/promises";
110
+ import { mkdtemp, rm, writeFile } from "fs/promises";
108
111
  import { tmpdir } from "os";
109
- import { join } from "path";
112
+ import { basename, join } from "path";
110
113
  import { Readable } from "stream";
111
114
  import {
112
115
  readAttachmentBytes
113
116
  } from "@photon-ai/imessage-kit";
117
+ var toSendResult = (result) => {
118
+ if (!result.message?.id) {
119
+ throw new Error(
120
+ "iMessage local send did not return a message id \u2014 track upstream in @photon-ai/imessage-kit"
121
+ );
122
+ }
123
+ return { id: result.message.id, timestamp: result.sentAt };
124
+ };
114
125
  var DEFAULT_ATTACHMENT_NAME = "attachment";
126
+ var VCARD_MIME_TYPES = /* @__PURE__ */ new Set([
127
+ "text/vcard",
128
+ "text/x-vcard",
129
+ "text/directory",
130
+ "application/vcard",
131
+ "application/x-vcard"
132
+ ]);
133
+ var normalizeMimeType = (mimeType) => (mimeType.split(";")[0] ?? "").trim().toLowerCase();
134
+ var isVCardAttachment = (mimeType, fileName) => {
135
+ if (mimeType && VCARD_MIME_TYPES.has(normalizeMimeType(mimeType))) {
136
+ return true;
137
+ }
138
+ return Boolean(fileName?.toLowerCase().endsWith(".vcf"));
139
+ };
115
140
  var toSpace = (message) => ({
116
141
  id: message.chatId,
117
142
  type: message.chatKind === "group" ? "group" : "dm"
118
143
  });
119
- var toMessages = (message) => {
144
+ var toAttachmentContent = (att) => {
145
+ const { localPath } = att;
146
+ return asAttachment({
147
+ name: att.fileName ?? DEFAULT_ATTACHMENT_NAME,
148
+ mimeType: att.mimeType,
149
+ size: att.sizeBytes,
150
+ read: () => readAttachmentBytes(att),
151
+ stream: localPath ? async () => Readable.toWeb(
152
+ createReadStream(localPath)
153
+ ) : void 0
154
+ });
155
+ };
156
+ var toVCardContent = async (att) => {
157
+ try {
158
+ const buf = await readAttachmentBytes(att);
159
+ return asContact(fromVCard(buf.toString("utf8")));
160
+ } catch {
161
+ return toAttachmentContent(att);
162
+ }
163
+ };
164
+ var toMessages = async (message) => {
120
165
  const base = {
121
166
  sender: { id: message.participant ?? "" },
122
167
  space: toSpace(message),
123
168
  timestamp: message.createdAt
124
169
  };
125
170
  if (message.attachments.length > 0) {
126
- return message.attachments.map((att) => {
127
- const { localPath } = att;
128
- return {
171
+ return Promise.all(
172
+ message.attachments.map(async (att) => ({
129
173
  ...base,
130
174
  id: `${message.id}:${att.id}`,
131
- content: asAttachment({
132
- name: att.fileName ?? DEFAULT_ATTACHMENT_NAME,
133
- mimeType: att.mimeType,
134
- size: att.sizeBytes,
135
- read: () => readAttachmentBytes(att),
136
- stream: localPath ? async () => Readable.toWeb(
137
- createReadStream(localPath)
138
- ) : void 0
139
- })
140
- };
141
- });
175
+ content: isVCardAttachment(att.mimeType, att.fileName) ? await toVCardContent(att) : toAttachmentContent(att)
176
+ }))
177
+ );
142
178
  }
143
179
  return [
144
180
  {
@@ -149,37 +185,60 @@ var toMessages = (message) => {
149
185
  ];
150
186
  };
151
187
  var messages = (client) => stream((emit, end) => {
188
+ let lastPromise = Promise.resolve();
152
189
  client.startWatching({
153
190
  onMessage: (message) => {
154
- try {
155
- for (const m of toMessages(message)) {
191
+ if (message.isFromMe) {
192
+ return;
193
+ }
194
+ lastPromise = lastPromise.then(() => toMessages(message)).then((ms) => {
195
+ for (const m of ms) {
156
196
  emit(m);
157
197
  }
158
- } catch (error) {
159
- end(error);
160
- }
198
+ }).catch((error) => end(error));
161
199
  }
162
200
  });
163
201
  return () => client.stopWatching();
164
202
  });
203
+ var vcardFileName = (content) => {
204
+ const base = content.name?.formatted ?? content.user?.id ?? "contact";
205
+ return `${base.replace(/[^a-zA-Z0-9_\-.]/g, "_")}.vcf`;
206
+ };
207
+ var sendTempFile = async (client, spaceId, name, data) => {
208
+ const safeName = basename(name) || DEFAULT_ATTACHMENT_NAME;
209
+ const dir = await mkdtemp(join(tmpdir(), "spectrum-"));
210
+ const tmp = join(dir, safeName);
211
+ await writeFile(tmp, data);
212
+ try {
213
+ return await client.send(spaceId, { attachments: [tmp] });
214
+ } finally {
215
+ await rm(dir, { recursive: true, force: true }).catch(() => {
216
+ });
217
+ }
218
+ };
165
219
  var send = async (client, spaceId, content) => {
166
220
  switch (content.type) {
167
221
  case "text":
168
- await client.send(spaceId, content.text);
169
- break;
170
- case "attachment": {
171
- const tmp = join(tmpdir(), `spectrum-${Date.now()}-${content.name}`);
172
- await writeFile(tmp, await content.read());
173
- try {
174
- await client.send(spaceId, { attachments: [tmp] });
175
- } finally {
176
- await unlink(tmp).catch(() => {
177
- });
178
- }
179
- break;
222
+ return toSendResult(await client.send(spaceId, content.text));
223
+ case "attachment":
224
+ return toSendResult(
225
+ await sendTempFile(client, spaceId, content.name, await content.read())
226
+ );
227
+ case "contact": {
228
+ const vcf = await toVCard(content);
229
+ return toSendResult(
230
+ await sendTempFile(
231
+ client,
232
+ spaceId,
233
+ vcardFileName(content),
234
+ Buffer.from(vcf, "utf8")
235
+ )
236
+ );
180
237
  }
181
238
  default:
182
- break;
239
+ throw new Error(
240
+ `Unsupported iMessage local content type: ${content.type}`
241
+ );
183
242
  }
184
243
  };
185
244
 
@@ -189,6 +248,148 @@ import {
189
248
  messageGuid,
190
249
  Reaction
191
250
  } from "@photon-ai/advanced-imessage";
251
+
252
+ // src/utils/audio.ts
253
+ import { spawn } from "child_process";
254
+ import { mkdtemp as mkdtemp2, readFile, rm as rm2, writeFile as writeFile2 } from "fs/promises";
255
+ import { tmpdir as tmpdir2 } from "os";
256
+ import { join as join2 } from "path";
257
+ var M4A_BRANDS = /* @__PURE__ */ new Set([
258
+ "M4A ",
259
+ "M4B ",
260
+ "M4P ",
261
+ "mp42",
262
+ "mp41",
263
+ "isom",
264
+ "iso2"
265
+ ]);
266
+ var M4A_MIME_TYPES = /* @__PURE__ */ new Set([
267
+ "audio/mp4",
268
+ "audio/mp4a-latm",
269
+ "audio/x-m4a",
270
+ "audio/aac",
271
+ "audio/aacp"
272
+ ]);
273
+ var FFMPEG_MISSING_MESSAGE = "voice content: input is not m4a/aac and ffmpeg is unavailable. Install `ffmpeg-static` or ensure `ffmpeg` is on PATH.";
274
+ var isM4a = (buffer) => {
275
+ if (buffer.length < 12) {
276
+ return false;
277
+ }
278
+ if (buffer.toString("ascii", 4, 8) !== "ftyp") {
279
+ return false;
280
+ }
281
+ return M4A_BRANDS.has(buffer.toString("ascii", 8, 12));
282
+ };
283
+ var isM4aMimeType = (mimeType) => M4A_MIME_TYPES.has(mimeType.toLowerCase());
284
+ var cachedFfmpegPath;
285
+ var tryStaticBinary = async () => {
286
+ try {
287
+ const mod = await import("ffmpeg-static");
288
+ return mod.default ?? void 0;
289
+ } catch {
290
+ return void 0;
291
+ }
292
+ };
293
+ var resolveFfmpegPath = async () => {
294
+ if (cachedFfmpegPath) {
295
+ return cachedFfmpegPath;
296
+ }
297
+ cachedFfmpegPath = await tryStaticBinary() ?? "ffmpeg";
298
+ return cachedFfmpegPath;
299
+ };
300
+ var collectStream = (stream2) => {
301
+ if (!stream2) {
302
+ return Promise.resolve("");
303
+ }
304
+ return new Promise((resolve, reject) => {
305
+ const chunks = [];
306
+ stream2.on("data", (chunk) => {
307
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
308
+ });
309
+ stream2.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
310
+ stream2.on("error", reject);
311
+ });
312
+ };
313
+ var isMissingBinaryError = (err) => err?.code === "ENOENT";
314
+ var runFfmpeg = (ffmpegPath, args) => {
315
+ const proc = spawn(ffmpegPath, args, { stdio: ["ignore", "ignore", "pipe"] });
316
+ const stderr = collectStream(proc.stderr);
317
+ const exit = new Promise((resolve, reject) => {
318
+ proc.on(
319
+ "error",
320
+ (err) => reject(
321
+ isMissingBinaryError(err) ? new Error(FFMPEG_MISSING_MESSAGE) : err
322
+ )
323
+ );
324
+ proc.on("exit", (code) => resolve(code ?? -1));
325
+ });
326
+ return Promise.all([exit, stderr]).then(([code, text]) => ({
327
+ code,
328
+ stderr: text
329
+ }));
330
+ };
331
+ var DURATION_PATTERN = /Duration:\s*(\d+):(\d{2}):(\d{2})(?:\.(\d{1,3}))?/;
332
+ var parseDuration = (stderr) => {
333
+ const match = stderr.match(DURATION_PATTERN);
334
+ if (!match) {
335
+ return void 0;
336
+ }
337
+ const [, hh, mm, ss, frac] = match;
338
+ const seconds = Number(hh) * 3600 + Number(mm) * 60 + Number(ss) + Number(`0.${frac ?? 0}`);
339
+ return Number.isFinite(seconds) ? seconds : void 0;
340
+ };
341
+ var transcodeToM4a = async (buffer) => {
342
+ const ffmpeg = await resolveFfmpegPath();
343
+ const dir = await mkdtemp2(join2(tmpdir2(), "spectrum-voice-"));
344
+ const inPath = join2(dir, "in");
345
+ const outPath = join2(dir, "out.m4a");
346
+ try {
347
+ await writeFile2(inPath, buffer);
348
+ const { code, stderr } = await runFfmpeg(ffmpeg, [
349
+ "-y",
350
+ "-i",
351
+ inPath,
352
+ "-f",
353
+ "ipod",
354
+ "-c:a",
355
+ "aac",
356
+ outPath
357
+ ]);
358
+ if (code !== 0) {
359
+ throw new Error(`ffmpeg conversion failed (exit ${code}): ${stderr}`);
360
+ }
361
+ const out = await readFile(outPath);
362
+ return { buffer: out, duration: parseDuration(stderr) };
363
+ } finally {
364
+ await rm2(dir, { recursive: true, force: true }).catch(() => {
365
+ });
366
+ }
367
+ };
368
+ var ensureM4a = async (buffer, mimeType) => {
369
+ if (isM4aMimeType(mimeType) || isM4a(buffer)) {
370
+ return { buffer };
371
+ }
372
+ return transcodeToM4a(buffer);
373
+ };
374
+
375
+ // src/providers/imessage/remote.ts
376
+ var toSendResult2 = (receipt) => ({
377
+ id: receipt.guid,
378
+ timestamp: /* @__PURE__ */ new Date()
379
+ });
380
+ var VCARD_MIME_TYPES2 = /* @__PURE__ */ new Set([
381
+ "text/vcard",
382
+ "text/x-vcard",
383
+ "text/directory",
384
+ "application/vcard",
385
+ "application/x-vcard"
386
+ ]);
387
+ var isVCardAttachment2 = (mimeType, fileName) => {
388
+ if (mimeType && VCARD_MIME_TYPES2.has(mimeType.toLowerCase())) {
389
+ return true;
390
+ }
391
+ return Boolean(fileName?.toLowerCase().endsWith(".vcf"));
392
+ };
192
393
  var TAPBACK_NAMES = new Set(
193
394
  Object.values(Reaction).filter((r) => r !== "emoji" && r !== "sticker")
194
395
  );
@@ -200,21 +401,32 @@ var baseMessage = (event) => ({
200
401
  },
201
402
  timestamp: event.timestamp
202
403
  });
203
- var toMessages2 = (client, event) => {
404
+ var toAttachmentContent2 = (client, info) => asAttachment({
405
+ name: info.fileName,
406
+ mimeType: info.mimeType,
407
+ size: info.totalBytes,
408
+ read: async () => Buffer.from(await client.attachments.downloadBuffer(info.guid)),
409
+ stream: async () => client.attachments.download(info.guid).stream
410
+ });
411
+ var toVCardContent2 = async (client, info) => {
412
+ try {
413
+ const buf = Buffer.from(await client.attachments.downloadBuffer(info.guid));
414
+ return asContact(fromVCard(buf.toString("utf8")));
415
+ } catch {
416
+ return toAttachmentContent2(client, info);
417
+ }
418
+ };
419
+ var toMessages2 = async (client, event) => {
204
420
  const base = baseMessage(event);
205
421
  const messageGuidStr = event.message.guid;
206
422
  if (event.message.attachments.length > 0) {
207
- return event.message.attachments.map((info) => ({
208
- ...base,
209
- id: `${messageGuidStr}:${info.guid}`,
210
- content: asAttachment({
211
- name: info.fileName,
212
- mimeType: info.mimeType,
213
- size: info.totalBytes,
214
- read: async () => Buffer.from(await client.attachments.downloadBuffer(info.guid)),
215
- stream: async () => client.attachments.download(info.guid).stream
216
- })
217
- }));
423
+ return Promise.all(
424
+ event.message.attachments.map(async (info) => ({
425
+ ...base,
426
+ id: `${messageGuidStr}:${info.guid}`,
427
+ content: isVCardAttachment2(info.mimeType, info.fileName) ? await toVCardContent2(client, info) : toAttachmentContent2(client, info)
428
+ }))
429
+ );
218
430
  }
219
431
  const text = event.message.text;
220
432
  return [
@@ -231,7 +443,10 @@ var clientStream = (client) => {
231
443
  (async () => {
232
444
  try {
233
445
  for await (const event of sub) {
234
- for (const message of toMessages2(client, event)) {
446
+ if (event.message.isFromMe) {
447
+ continue;
448
+ }
449
+ for (const message of await toMessages2(client, event)) {
235
450
  emit(message);
236
451
  }
237
452
  }
@@ -243,6 +458,20 @@ var clientStream = (client) => {
243
458
  return () => sub.close();
244
459
  });
245
460
  };
461
+ var sendVCardAttachment = (remote, name, vcf) => remote.attachments.upload({
462
+ data: Buffer.from(vcf, "utf8"),
463
+ fileName: name,
464
+ mimeType: "text/vcard"
465
+ });
466
+ var vcardFileName2 = (contact) => {
467
+ const base = contact.name?.formatted ?? contact.user?.id ?? "contact";
468
+ return `${base.replace(/[^a-zA-Z0-9_\-.]/g, "_")}.vcf`;
469
+ };
470
+ var sendContactAttachment = async (remote, content) => {
471
+ const vcf = await toVCard(content);
472
+ const upload = await sendVCardAttachment(remote, vcardFileName2(content), vcf);
473
+ return upload.guid;
474
+ };
246
475
  var messages2 = (clients) => mergeStreams(clients.map(clientStream));
247
476
  var startTyping = async (clients, spaceId) => {
248
477
  const remote = clients[0];
@@ -261,22 +490,44 @@ var stopTyping = async (clients, spaceId) => {
261
490
  var send2 = async (clients, spaceId, content) => {
262
491
  const remote = clients[0];
263
492
  if (!remote) {
264
- return;
493
+ throw new Error("No remote iMessage client available");
265
494
  }
495
+ const chat = chatGuid(spaceId);
266
496
  switch (content.type) {
267
497
  case "text":
268
- await remote.messages.send(chatGuid(spaceId), content.text);
269
- break;
498
+ return toSendResult2(await remote.messages.send(chat, content.text));
270
499
  case "attachment": {
271
500
  const attachment = await remote.attachments.upload({
272
501
  data: await content.read(),
273
502
  fileName: content.name,
274
503
  mimeType: content.mimeType
275
504
  });
276
- await remote.messages.send(chatGuid(spaceId), "", {
277
- attachment: attachment.guid
505
+ return toSendResult2(
506
+ await remote.messages.send(chat, "", {
507
+ attachment: attachment.guid
508
+ })
509
+ );
510
+ }
511
+ case "contact": {
512
+ const attachment = await sendContactAttachment(remote, content);
513
+ return toSendResult2(await remote.messages.send(chat, "", { attachment }));
514
+ }
515
+ case "voice": {
516
+ const { buffer } = await ensureM4a(
517
+ await content.read(),
518
+ content.mimeType
519
+ );
520
+ const attachment = await remote.attachments.upload({
521
+ data: buffer,
522
+ fileName: content.name ?? "voice.m4a",
523
+ mimeType: "audio/x-m4a"
278
524
  });
279
- break;
525
+ return toSendResult2(
526
+ await remote.messages.send(chat, "", {
527
+ attachment: attachment.guid,
528
+ audioMessage: true
529
+ })
530
+ );
280
531
  }
281
532
  default:
282
533
  throw new Error(`Unsupported iMessage content type: ${content.type}`);
@@ -285,30 +536,70 @@ var send2 = async (clients, spaceId, content) => {
285
536
  var replyToMessage = async (clients, spaceId, msgId, content) => {
286
537
  const remote = clients[0];
287
538
  if (!remote) {
288
- return;
539
+ throw new Error("No remote iMessage client available");
289
540
  }
290
541
  const chat = chatGuid(spaceId);
291
542
  const replyTo = messageGuid(msgId);
292
543
  switch (content.type) {
293
544
  case "text":
294
- await remote.messages.send(chat, content.text, { replyTo });
295
- break;
545
+ return toSendResult2(
546
+ await remote.messages.send(chat, content.text, { replyTo })
547
+ );
296
548
  case "attachment": {
297
549
  const attachment = await remote.attachments.upload({
298
550
  data: await content.read(),
299
551
  fileName: content.name,
300
552
  mimeType: content.mimeType
301
553
  });
302
- await remote.messages.send(chat, "", {
303
- attachment: attachment.guid,
304
- replyTo
554
+ return toSendResult2(
555
+ await remote.messages.send(chat, "", {
556
+ attachment: attachment.guid,
557
+ replyTo
558
+ })
559
+ );
560
+ }
561
+ case "contact": {
562
+ const attachment = await sendContactAttachment(remote, content);
563
+ return toSendResult2(
564
+ await remote.messages.send(chat, "", { attachment, replyTo })
565
+ );
566
+ }
567
+ case "voice": {
568
+ const { buffer } = await ensureM4a(
569
+ await content.read(),
570
+ content.mimeType
571
+ );
572
+ const attachment = await remote.attachments.upload({
573
+ data: buffer,
574
+ fileName: content.name ?? "voice.m4a",
575
+ mimeType: "audio/x-m4a"
305
576
  });
306
- break;
577
+ return toSendResult2(
578
+ await remote.messages.send(chat, "", {
579
+ attachment: attachment.guid,
580
+ audioMessage: true,
581
+ replyTo
582
+ })
583
+ );
307
584
  }
308
585
  default:
309
586
  throw new Error(`Unsupported iMessage content type: ${content.type}`);
310
587
  }
311
588
  };
589
+ var editMessage = async (clients, spaceId, msgId, content) => {
590
+ if (content.type !== "text") {
591
+ throw new Error("iMessage only supports editing text content");
592
+ }
593
+ const remote = clients[0];
594
+ if (!remote) {
595
+ throw new Error("No remote iMessage client available");
596
+ }
597
+ await remote.messages.edit(
598
+ chatGuid(spaceId),
599
+ messageGuid(msgId),
600
+ content.text
601
+ );
602
+ };
312
603
  var reactToMessage = async (clients, spaceId, msgId, reaction) => {
313
604
  const remote = clients[0];
314
605
  if (!remote) {
@@ -420,10 +711,9 @@ var imessage = definePlatform("iMessage", {
420
711
  actions: {
421
712
  send: async ({ space, content, client }) => {
422
713
  if (isLocal(client)) {
423
- await send(client, space.id, content);
424
- } else {
425
- await send2(client, space.id, content);
714
+ return await send(client, space.id, content);
426
715
  }
716
+ return await send2(client, space.id, content);
427
717
  },
428
718
  startTyping: async ({ space, client }) => {
429
719
  if (isLocal(client)) {
@@ -445,9 +735,19 @@ var imessage = definePlatform("iMessage", {
445
735
  },
446
736
  replyToMessage: async ({ space, messageId, content, client }) => {
447
737
  if (isLocal(client)) {
448
- return;
738
+ throw new Error(
739
+ "iMessage local mode does not support replying to messages"
740
+ );
741
+ }
742
+ return await replyToMessage(client, space.id, messageId, content);
743
+ },
744
+ editMessage: async ({ space, messageId, content, client }) => {
745
+ if (isLocal(client)) {
746
+ throw new Error(
747
+ "iMessage local mode does not support editing messages"
748
+ );
449
749
  }
450
- await replyToMessage(client, space.id, messageId, content);
750
+ await editMessage(client, space.id, messageId, content);
451
751
  }
452
752
  }
453
753
  });
@@ -1,4 +1,4 @@
1
- import { d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-BdWMydUJ.js';
1
+ import { d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-DZMHfgYQ.js';
2
2
  import * as node_readline from 'node:readline';
3
3
  import z__default from 'zod';
4
4
  import 'hotscript';
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  definePlatform
3
- } from "../../chunk-XEEDIGVK.js";
3
+ } from "../../chunk-XZTTLPHE.js";
4
4
 
5
5
  // src/providers/terminal/index.ts
6
6
  import { createInterface } from "readline";
@@ -49,9 +49,13 @@ var terminal = definePlatform("terminal", {
49
49
  },
50
50
  actions: {
51
51
  send: async ({ content }) => {
52
- if (content.type === "text") {
53
- console.log(content.text);
52
+ if (content.type !== "text") {
53
+ throw new Error(
54
+ `Terminal provider only supports text content, got "${content.type}"`
55
+ );
54
56
  }
57
+ console.log(content.text);
58
+ return { id: crypto.randomUUID(), timestamp: /* @__PURE__ */ new Date() };
55
59
  }
56
60
  }
57
61
  });
@@ -1,7 +1,7 @@
1
1
  import { M as ManagedStream } from '../../stream-DGy4geUK.js';
2
2
  import * as z from 'zod';
3
3
  import z__default from 'zod';
4
- import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-BdWMydUJ.js';
4
+ import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-DZMHfgYQ.js';
5
5
  import * as zod_v4_core from 'zod/v4/core';
6
6
  import { WhatsAppClient } from '@photon-ai/whatsapp-business';
7
7
  import 'hotscript';