@elizaos/plugin-imessage 2.0.3-beta.2 → 2.0.3-beta.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.
Files changed (58) hide show
  1. package/dist/accounts.d.ts +135 -0
  2. package/dist/accounts.d.ts.map +1 -0
  3. package/dist/accounts.js +209 -0
  4. package/dist/accounts.js.map +1 -0
  5. package/dist/api/bluebubbles-routes.d.ts +10 -0
  6. package/dist/api/bluebubbles-routes.d.ts.map +1 -0
  7. package/dist/api/bluebubbles-routes.js +132 -0
  8. package/dist/api/bluebubbles-routes.js.map +1 -0
  9. package/dist/api/imessage-routes.d.ts +68 -0
  10. package/dist/api/imessage-routes.d.ts.map +1 -0
  11. package/dist/api/imessage-routes.js +228 -0
  12. package/dist/api/imessage-routes.js.map +1 -0
  13. package/dist/chatdb-reader.d.ts +240 -0
  14. package/dist/chatdb-reader.d.ts.map +1 -0
  15. package/dist/chatdb-reader.js +667 -0
  16. package/dist/chatdb-reader.js.map +1 -0
  17. package/dist/config.d.ts +60 -0
  18. package/dist/config.d.ts.map +1 -0
  19. package/dist/config.js +8 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/connector-account-provider.d.ts +18 -0
  22. package/dist/connector-account-provider.d.ts.map +1 -0
  23. package/dist/connector-account-provider.js +83 -0
  24. package/dist/connector-account-provider.js.map +1 -0
  25. package/dist/contacts-reader.d.ts +141 -0
  26. package/dist/contacts-reader.d.ts.map +1 -0
  27. package/dist/contacts-reader.js +359 -0
  28. package/dist/contacts-reader.js.map +1 -0
  29. package/dist/data-routes.d.ts +21 -0
  30. package/dist/data-routes.d.ts.map +1 -0
  31. package/dist/data-routes.js +280 -0
  32. package/dist/data-routes.js.map +1 -0
  33. package/dist/index.d.ts +24 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +83 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/providers/index.d.ts +4 -0
  38. package/dist/providers/index.d.ts.map +1 -0
  39. package/dist/providers/index.js +5 -0
  40. package/dist/providers/index.js.map +1 -0
  41. package/dist/rpc.d.ts +206 -0
  42. package/dist/rpc.d.ts.map +1 -0
  43. package/dist/rpc.js +393 -0
  44. package/dist/rpc.js.map +1 -0
  45. package/dist/service.d.ts +264 -0
  46. package/dist/service.d.ts.map +1 -0
  47. package/dist/service.js +1705 -0
  48. package/dist/service.js.map +1 -0
  49. package/dist/setup-routes.d.ts +26 -0
  50. package/dist/setup-routes.d.ts.map +1 -0
  51. package/dist/setup-routes.js +139 -0
  52. package/dist/setup-routes.js.map +1 -0
  53. package/dist/types.d.ts +192 -0
  54. package/dist/types.d.ts.map +1 -0
  55. package/dist/types.js +138 -0
  56. package/dist/types.js.map +1 -0
  57. package/package.json +4 -3
  58. package/registry-entry.json +103 -0
@@ -0,0 +1,359 @@
1
+ /**
2
+ * macOS Contacts reader for @elizaos/plugin-imessage.
3
+ *
4
+ * Incoming iMessages arrive tagged with a raw handle — a phone number in
5
+ * E.164 form (`+15551234567`) or an email address. Raw handles are ugly
6
+ * to read and make the agent's replies feel impersonal. This module
7
+ * resolves each handle to the real display name from the user's Apple
8
+ * Contacts so the agent sees "Mom" or "Alex Chen" instead of a string
9
+ * of digits.
10
+ *
11
+ * ---
12
+ *
13
+ * Backend: **CNContactStore** through the shared native macOS dylib. This
14
+ * keeps the feature aligned with the macOS Contacts privacy grant and avoids
15
+ * asking for Automation access to the Contacts app.
16
+ *
17
+ * The service calls this lazily when it actually needs name resolution or
18
+ * contact CRUD. Contacts rarely change mid-session, so the iMessage service
19
+ * caches the returned map for v1.
20
+ *
21
+ * Graceful degradation: if Contacts is not authorized, or returns no
22
+ * rows, or the native bridge fails for any other reason, the reader returns
23
+ * an empty map. The service treats that as "handles remain anonymous"
24
+ * and proceeds normally — no crash, no hard failure.
25
+ */
26
+ import { existsSync } from "node:fs";
27
+ import path from "node:path";
28
+ import { logger } from "@elizaos/core";
29
+ const NATIVE_DYLIB_CANDIDATES = [
30
+ process.env.ELIZA_NATIVE_PERMISSIONS_DYLIB ?? "",
31
+ "../../../packages/app-core/platforms/electrobun/src/libMacWindowEffects.dylib",
32
+ ].filter(Boolean);
33
+ let nativeContactsBridge;
34
+ let lastContactsFailure = null;
35
+ export function getLastContactsFailure() {
36
+ return lastContactsFailure;
37
+ }
38
+ function cStringBuffer(value) {
39
+ const bytes = Buffer.from(value, "utf8");
40
+ const buffer = Buffer.alloc(bytes.byteLength + 1);
41
+ bytes.copy(buffer);
42
+ return buffer;
43
+ }
44
+ async function loadNativeContactsBridge() {
45
+ if (nativeContactsBridge !== undefined)
46
+ return nativeContactsBridge;
47
+ nativeContactsBridge = null;
48
+ if (process.platform !== "darwin")
49
+ return null;
50
+ for (const candidate of NATIVE_DYLIB_CANDIDATES) {
51
+ const dylibPath = path.isAbsolute(candidate)
52
+ ? candidate
53
+ : path.resolve(import.meta.dir, candidate);
54
+ if (!existsSync(dylibPath))
55
+ continue;
56
+ try {
57
+ const { CString, FFIType, dlopen, ptr } = await import("bun:ffi");
58
+ const lib = dlopen(dylibPath, {
59
+ loadContactsJson: { args: [], returns: FFIType.ptr },
60
+ listAllContactsJson: { args: [], returns: FFIType.ptr },
61
+ addContactJson: { args: [FFIType.ptr], returns: FFIType.ptr },
62
+ updateContactJson: {
63
+ args: [FFIType.ptr, FFIType.ptr],
64
+ returns: FFIType.ptr,
65
+ },
66
+ deleteContactJson: { args: [FFIType.ptr], returns: FFIType.ptr },
67
+ freeNativeCString: { args: [FFIType.ptr], returns: FFIType.void },
68
+ });
69
+ const takeNativeString = (value) => {
70
+ if (!value)
71
+ return null;
72
+ try {
73
+ return new CString(value).toString();
74
+ }
75
+ finally {
76
+ lib.symbols.freeNativeCString(value);
77
+ }
78
+ };
79
+ nativeContactsBridge = {
80
+ loadContacts() {
81
+ return takeNativeString(lib.symbols.loadContactsJson());
82
+ },
83
+ listAllContacts() {
84
+ return takeNativeString(lib.symbols.listAllContactsJson());
85
+ },
86
+ addContact(payloadJson) {
87
+ const payload = cStringBuffer(payloadJson);
88
+ return takeNativeString(lib.symbols.addContactJson(ptr(payload)));
89
+ },
90
+ updateContact(personId, payloadJson) {
91
+ const id = cStringBuffer(personId);
92
+ const payload = cStringBuffer(payloadJson);
93
+ return takeNativeString(lib.symbols.updateContactJson(ptr(id), ptr(payload)));
94
+ },
95
+ deleteContact(personId) {
96
+ const id = cStringBuffer(personId);
97
+ return takeNativeString(lib.symbols.deleteContactJson(ptr(id)));
98
+ },
99
+ };
100
+ return nativeContactsBridge;
101
+ }
102
+ catch (error) {
103
+ logger.warn(`[imessage] Failed to load native Contacts bridge from ${dylibPath}: ${error instanceof Error ? error.message : String(error)}`);
104
+ }
105
+ }
106
+ return null;
107
+ }
108
+ function parseNativeContactsResponse(raw) {
109
+ if (!raw) {
110
+ return {
111
+ ok: false,
112
+ error: "native_error",
113
+ message: "Native Contacts bridge returned no response.",
114
+ };
115
+ }
116
+ try {
117
+ const parsed = JSON.parse(raw);
118
+ return {
119
+ ok: parsed.ok === true,
120
+ error: typeof parsed.error === "string" ? parsed.error : undefined,
121
+ id: typeof parsed.id === "string" ? parsed.id : undefined,
122
+ message: typeof parsed.message === "string" ? parsed.message : undefined,
123
+ contacts: Array.isArray(parsed.contacts) ? parsed.contacts : undefined,
124
+ };
125
+ }
126
+ catch {
127
+ return {
128
+ ok: false,
129
+ error: "native_error",
130
+ message: "Native Contacts bridge returned invalid JSON.",
131
+ };
132
+ }
133
+ }
134
+ function isRecord(value) {
135
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
136
+ }
137
+ function stringField(record, key) {
138
+ const value = record[key];
139
+ return typeof value === "string" ? value : "";
140
+ }
141
+ /**
142
+ * Normalize a handle to the canonical form used as a key in the
143
+ * ContactsMap. Strips whitespace, parentheses, hyphens, and dots from
144
+ * phone numbers and lowercases emails. Leaves a leading `+` in place.
145
+ */
146
+ export function normalizeContactHandle(raw) {
147
+ const trimmed = raw.trim();
148
+ if (!trimmed)
149
+ return "";
150
+ // Email: lowercase
151
+ if (trimmed.includes("@")) {
152
+ return trimmed.toLowerCase();
153
+ }
154
+ // Phone: strip formatting characters, preserve leading +
155
+ const hasPlus = trimmed.startsWith("+");
156
+ const digitsOnly = trimmed.replace(/[^\d]/g, "");
157
+ return hasPlus ? `+${digitsOnly}` : digitsOnly;
158
+ }
159
+ /**
160
+ * Parse legacy tab-delimited contact fixture output into a ContactsMap.
161
+ * Exported so tests can exercise normalization without a live address book.
162
+ *
163
+ * Input format per line: `kind\thandle\tname`.
164
+ * Empty lines are skipped. Lines with fewer than 3 fields are skipped.
165
+ * Empty handles are skipped. Duplicate handles keep the first entry.
166
+ */
167
+ export function parseContactsOutput(raw) {
168
+ const map = new Map();
169
+ if (!raw.trim())
170
+ return map;
171
+ for (const line of raw.split("\n")) {
172
+ const trimmed = line.trim();
173
+ if (!trimmed)
174
+ continue;
175
+ const fields = trimmed.split("\t");
176
+ if (fields.length < 3)
177
+ continue;
178
+ const [_kind, handle, name] = fields;
179
+ if (!handle || !name)
180
+ continue;
181
+ const normalized = normalizeContactHandle(handle);
182
+ if (!normalized)
183
+ continue;
184
+ if (map.has(normalized))
185
+ continue;
186
+ map.set(normalized, { name: name.trim() });
187
+ }
188
+ return map;
189
+ }
190
+ function contactsMapFromNativeRows(rows) {
191
+ const map = new Map();
192
+ for (const row of rows ?? []) {
193
+ if (!isRecord(row))
194
+ continue;
195
+ const handle = stringField(row, "handle");
196
+ const name = stringField(row, "name");
197
+ if (!handle || !name)
198
+ continue;
199
+ const normalized = normalizeContactHandle(handle);
200
+ if (!normalized)
201
+ continue;
202
+ if (map.has(normalized))
203
+ continue;
204
+ map.set(normalized, { name: name.trim() });
205
+ }
206
+ return map;
207
+ }
208
+ /**
209
+ * Read Apple Contacts through CNContactStore and return a ContactsMap. Returns
210
+ * an empty map (with a warning log) on any failure — most commonly, the
211
+ * user hasn't authorized Contacts access yet.
212
+ */
213
+ export async function loadContacts() {
214
+ const bridge = await loadNativeContactsBridge();
215
+ if (!bridge) {
216
+ lastContactsFailure = "bridge_unavailable";
217
+ logger.warn("[imessage] Native Contacts bridge unavailable. Inbound messages will use raw handles.");
218
+ return new Map();
219
+ }
220
+ const response = parseNativeContactsResponse(bridge.loadContacts());
221
+ if (response.ok) {
222
+ lastContactsFailure = null;
223
+ const map = contactsMapFromNativeRows(response.contacts);
224
+ logger.info(`[imessage] Contacts loaded: ${map.size} handle(s) resolved from Apple Contacts`);
225
+ return map;
226
+ }
227
+ if (response.error === "permission") {
228
+ lastContactsFailure = "permission";
229
+ logger.warn("[imessage] Contacts access not authorized. Inbound messages will use raw handles until Contacts access is granted.");
230
+ }
231
+ else {
232
+ lastContactsFailure = "native_error";
233
+ logger.warn(`[imessage] Failed to load Apple Contacts data: ${response.message ?? response.error ?? "unknown error"}. Inbound messages will use raw handles instead of names.`);
234
+ }
235
+ return new Map();
236
+ }
237
+ function labeledValuesFromNativeRows(rows) {
238
+ if (!Array.isArray(rows))
239
+ return [];
240
+ return rows.flatMap((entry) => {
241
+ if (!isRecord(entry))
242
+ return [];
243
+ const value = stringField(entry, "value");
244
+ if (!value)
245
+ return [];
246
+ const label = stringField(entry, "label");
247
+ return [{ label: label || null, value }];
248
+ });
249
+ }
250
+ function fullContactsFromNativeRows(rows) {
251
+ return (rows ?? []).flatMap((entry) => {
252
+ if (!isRecord(entry))
253
+ return [];
254
+ const id = stringField(entry, "id");
255
+ if (!id)
256
+ return [];
257
+ return [
258
+ {
259
+ id,
260
+ name: stringField(entry, "name"),
261
+ firstName: stringField(entry, "firstName") || null,
262
+ lastName: stringField(entry, "lastName") || null,
263
+ phones: labeledValuesFromNativeRows(entry.phones),
264
+ emails: labeledValuesFromNativeRows(entry.emails),
265
+ },
266
+ ];
267
+ });
268
+ }
269
+ /**
270
+ * List every contact in the user's address book as a full `FullContact`
271
+ * record. Returns an empty array on any failure (permission denied,
272
+ * native bridge error, etc.) with a warning log.
273
+ */
274
+ export async function listAllContacts() {
275
+ const bridge = await loadNativeContactsBridge();
276
+ if (!bridge) {
277
+ lastContactsFailure = "bridge_unavailable";
278
+ logger.warn("[imessage] listAllContacts failed: native bridge unavailable");
279
+ return [];
280
+ }
281
+ const response = parseNativeContactsResponse(bridge.listAllContacts());
282
+ if (response.ok) {
283
+ lastContactsFailure = null;
284
+ return fullContactsFromNativeRows(response.contacts);
285
+ }
286
+ lastContactsFailure = response.error === "permission" ? "permission" : "native_error";
287
+ logger.warn(`[imessage] listAllContacts failed: ${response.message ?? response.error ?? "unknown error"}`);
288
+ return [];
289
+ }
290
+ /**
291
+ * Create a new Apple Contacts record. Returns the new person's id on
292
+ * success, or null on failure (permission denied, validation, etc.).
293
+ *
294
+ * Requires the Contacts privacy grant.
295
+ */
296
+ export async function addContact(input) {
297
+ const bridge = await loadNativeContactsBridge();
298
+ if (!bridge) {
299
+ lastContactsFailure = "bridge_unavailable";
300
+ logger.warn("[imessage] addContact failed: native bridge unavailable");
301
+ return null;
302
+ }
303
+ const response = parseNativeContactsResponse(bridge.addContact(JSON.stringify(input)));
304
+ if (response.ok && response.id) {
305
+ lastContactsFailure = null;
306
+ logger.info(`[imessage] Contact created: ${response.id}`);
307
+ return response.id;
308
+ }
309
+ lastContactsFailure = response.error === "permission" ? "permission" : "native_error";
310
+ logger.warn(`[imessage] addContact failed: ${response.message ?? response.error ?? "unknown error"}`);
311
+ return null;
312
+ }
313
+ export async function updateContact(personId, patch) {
314
+ if (patch.firstName === undefined &&
315
+ patch.lastName === undefined &&
316
+ (patch.addPhones?.length ?? 0) === 0 &&
317
+ (patch.removePhones?.length ?? 0) === 0 &&
318
+ (patch.addEmails?.length ?? 0) === 0 &&
319
+ (patch.removeEmails?.length ?? 0) === 0) {
320
+ return true;
321
+ }
322
+ const bridge = await loadNativeContactsBridge();
323
+ if (!bridge) {
324
+ lastContactsFailure = "bridge_unavailable";
325
+ logger.warn(`[imessage] updateContact failed for ${personId}: native bridge unavailable`);
326
+ return false;
327
+ }
328
+ const response = parseNativeContactsResponse(bridge.updateContact(personId, JSON.stringify(patch)));
329
+ if (response.ok) {
330
+ lastContactsFailure = null;
331
+ logger.info(`[imessage] Contact updated: ${personId}`);
332
+ return true;
333
+ }
334
+ lastContactsFailure = response.error === "permission" ? "permission" : "native_error";
335
+ logger.warn(`[imessage] updateContact failed for ${personId}: ${response.message ?? response.error ?? "unknown error"}`);
336
+ return false;
337
+ }
338
+ /**
339
+ * Delete a contact by Apple Contacts id. Requires the Contacts privacy grant.
340
+ * Returns false on any failure (not found, permission denied, etc.).
341
+ */
342
+ export async function deleteContact(personId) {
343
+ const bridge = await loadNativeContactsBridge();
344
+ if (!bridge) {
345
+ lastContactsFailure = "bridge_unavailable";
346
+ logger.warn(`[imessage] deleteContact failed for ${personId}: native bridge unavailable`);
347
+ return false;
348
+ }
349
+ const response = parseNativeContactsResponse(bridge.deleteContact(personId));
350
+ if (response.ok) {
351
+ lastContactsFailure = null;
352
+ logger.info(`[imessage] Contact deleted: ${personId}`);
353
+ return true;
354
+ }
355
+ lastContactsFailure = response.error === "permission" ? "permission" : "native_error";
356
+ logger.warn(`[imessage] deleteContact failed for ${personId}: ${response.message ?? response.error ?? "unknown error"}`);
357
+ return false;
358
+ }
359
+ //# sourceMappingURL=contacts-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contacts-reader.js","sourceRoot":"","sources":["../src/contacts-reader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * iMessage connector data routes.
3
+ *
4
+ * Post-setup CRUD against a working iMessage service. These live under
5
+ * `/api/imessage/` (not `/api/setup/imessage/`) since they are not part
6
+ * of the pairing/setup state machine.
7
+ *
8
+ * GET /api/imessage/messages recent messages (optional chatId/limit)
9
+ * POST /api/imessage/messages send a message
10
+ * GET /api/imessage/chats list chats
11
+ * GET /api/imessage/contacts list contacts
12
+ * POST /api/imessage/contacts create a contact
13
+ * PATCH /api/imessage/contacts/:id update a contact
14
+ * DELETE /api/imessage/contacts/:id delete a contact
15
+ *
16
+ * Registered with `rawPath: true` so they mount at their canonical paths
17
+ * without the plugin-name prefix.
18
+ */
19
+ import type { Route } from "@elizaos/core";
20
+ export declare const imessageDataRoutes: Route[];
21
+ //# sourceMappingURL=data-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-routes.d.ts","sourceRoot":"","sources":["../src/data-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAiB,KAAK,EAA+B,MAAM,eAAe,CAAC;AAmavF,eAAO,MAAM,kBAAkB,EAAE,KAAK,EA2CrC,CAAC"}
@@ -0,0 +1,280 @@
1
+ /**
2
+ * iMessage connector data routes.
3
+ *
4
+ * Post-setup CRUD against a working iMessage service. These live under
5
+ * `/api/imessage/` (not `/api/setup/imessage/`) since they are not part
6
+ * of the pairing/setup state machine.
7
+ *
8
+ * GET /api/imessage/messages recent messages (optional chatId/limit)
9
+ * POST /api/imessage/messages send a message
10
+ * GET /api/imessage/chats list chats
11
+ * GET /api/imessage/contacts list contacts
12
+ * POST /api/imessage/contacts create a contact
13
+ * PATCH /api/imessage/contacts/:id update a contact
14
+ * DELETE /api/imessage/contacts/:id delete a contact
15
+ *
16
+ * Registered with `rawPath: true` so they mount at their canonical paths
17
+ * without the plugin-name prefix.
18
+ */
19
+ function setupError(code, message) {
20
+ return { error: { code, message } };
21
+ }
22
+ const IMESSAGE_SERVICE_NAME = "imessage";
23
+ function resolveService(runtime) {
24
+ const raw = runtime.getService(IMESSAGE_SERVICE_NAME);
25
+ return raw ?? null;
26
+ }
27
+ /**
28
+ * Extract the `:id` segment from a contact path like
29
+ * `/api/imessage/contacts/ABCD-EFGH-...`. Returns null if the path
30
+ * doesn't match.
31
+ */
32
+ function parseContactId(pathname) {
33
+ const prefix = "/api/imessage/contacts/";
34
+ if (!pathname.startsWith(prefix))
35
+ return null;
36
+ const rest = pathname.slice(prefix.length);
37
+ if (!rest)
38
+ return null;
39
+ return decodeURIComponent(rest);
40
+ }
41
+ // ── GET /api/imessage/messages?limit=N ──────────────────────────────
42
+ async function handleMessages(req, res, runtime) {
43
+ const service = resolveService(runtime);
44
+ if (!service) {
45
+ res.status(503).json(setupError("service_unavailable", "imessage service not registered"));
46
+ return;
47
+ }
48
+ const url = new URL(req.url ?? "/api/imessage/messages", "http://localhost");
49
+ const limitParam = url.searchParams.get("limit");
50
+ const limit = Math.min(Math.max(1, Number.parseInt(limitParam ?? "50", 10) || 50), 500);
51
+ const chatId = url.searchParams.get("chatId")?.trim() || undefined;
52
+ try {
53
+ const messages = typeof service.getMessages === "function"
54
+ ? await service.getMessages({ chatId, limit })
55
+ : await service.getRecentMessages(limit);
56
+ res.status(200).json({ messages, count: messages.length });
57
+ }
58
+ catch (err) {
59
+ res
60
+ .status(500)
61
+ .json(setupError("internal_error", `failed to read messages: ${err instanceof Error ? err.message : String(err)}`));
62
+ }
63
+ }
64
+ // ── POST /api/imessage/messages ────────────────────────────────────
65
+ async function handleSendMessage(req, res, runtime) {
66
+ const service = resolveService(runtime);
67
+ if (!service) {
68
+ res.status(503).json(setupError("service_unavailable", "imessage service not registered"));
69
+ return;
70
+ }
71
+ const body = req.body ?? {};
72
+ const to = body.to?.trim() || "";
73
+ const chatId = body.chatId?.trim() || "";
74
+ const text = body.text?.trim() || "";
75
+ const mediaUrl = body.mediaUrl?.trim() || undefined;
76
+ if (!to && !chatId) {
77
+ res.status(400).json(setupError("bad_request", "either to or chatId is required"));
78
+ return;
79
+ }
80
+ if (!text && !mediaUrl) {
81
+ res.status(400).json(setupError("bad_request", "either text or mediaUrl is required"));
82
+ return;
83
+ }
84
+ try {
85
+ const result = await service.sendMessage(chatId ? `chat_id:${chatId}` : to, text, {
86
+ ...(mediaUrl ? { mediaUrl } : {}),
87
+ ...(typeof body.maxBytes === "number" ? { maxBytes: body.maxBytes } : {}),
88
+ });
89
+ if (!result.success) {
90
+ res.status(500).json(setupError("internal_error", result.error ?? "failed to send iMessage"));
91
+ return;
92
+ }
93
+ res.status(200).json(result);
94
+ }
95
+ catch (err) {
96
+ res
97
+ .status(500)
98
+ .json(setupError("internal_error", `sendMessage threw: ${err instanceof Error ? err.message : String(err)}`));
99
+ }
100
+ }
101
+ // ── GET /api/imessage/chats ─────────────────────────────────────────
102
+ async function handleChats(_req, res, runtime) {
103
+ const service = resolveService(runtime);
104
+ if (!service) {
105
+ res.status(503).json(setupError("service_unavailable", "imessage service not registered"));
106
+ return;
107
+ }
108
+ try {
109
+ const chats = await service.getChats();
110
+ res.status(200).json({ chats, count: chats.length });
111
+ }
112
+ catch (err) {
113
+ res
114
+ .status(500)
115
+ .json(setupError("internal_error", `failed to read chats: ${err instanceof Error ? err.message : String(err)}`));
116
+ }
117
+ }
118
+ // ── GET /api/imessage/contacts ──────────────────────────────────────
119
+ async function handleListContacts(_req, res, runtime) {
120
+ const service = resolveService(runtime);
121
+ if (!service) {
122
+ res.status(503).json(setupError("service_unavailable", "imessage service not registered"));
123
+ return;
124
+ }
125
+ try {
126
+ const contacts = await service.listAllContacts();
127
+ res.status(200).json({ contacts, count: contacts.length });
128
+ }
129
+ catch (err) {
130
+ res
131
+ .status(500)
132
+ .json(setupError("internal_error", `failed to read contacts: ${err instanceof Error ? err.message : String(err)}`));
133
+ }
134
+ }
135
+ // ── POST /api/imessage/contacts ─────────────────────────────────────
136
+ async function handleCreateContact(req, res, runtime) {
137
+ const service = resolveService(runtime);
138
+ if (!service) {
139
+ res.status(503).json(setupError("service_unavailable", "imessage service not registered"));
140
+ return;
141
+ }
142
+ const body = req.body ?? {};
143
+ if (!body.firstName && !body.lastName && !body.phones?.length && !body.emails?.length) {
144
+ res
145
+ .status(400)
146
+ .json(setupError("bad_request", "at least one of firstName, lastName, phones, or emails is required"));
147
+ return;
148
+ }
149
+ try {
150
+ const id = await service.addContact({
151
+ firstName: body.firstName,
152
+ lastName: body.lastName,
153
+ phones: body.phones,
154
+ emails: body.emails,
155
+ });
156
+ if (!id) {
157
+ res
158
+ .status(500)
159
+ .json(setupError("internal_error", "contact creation failed — see server logs. Common cause: Contacts write permission not granted yet."));
160
+ return;
161
+ }
162
+ res.status(201).json({ id, created: true });
163
+ }
164
+ catch (err) {
165
+ res
166
+ .status(500)
167
+ .json(setupError("internal_error", `addContact threw: ${err instanceof Error ? err.message : String(err)}`));
168
+ }
169
+ }
170
+ // ── PATCH /api/imessage/contacts/:id ────────────────────────────────
171
+ async function handleUpdateContact(req, res, runtime) {
172
+ const pathname = req.url ?? "";
173
+ const id = parseContactId(pathname.split("?")[0]);
174
+ if (!id) {
175
+ res.status(400).json(setupError("bad_request", "contact id is required in the path"));
176
+ return;
177
+ }
178
+ const service = resolveService(runtime);
179
+ if (!service) {
180
+ res.status(503).json(setupError("service_unavailable", "imessage service not registered"));
181
+ return;
182
+ }
183
+ const body = req.body ?? {};
184
+ try {
185
+ const ok = await service.updateContact(id, {
186
+ firstName: body.firstName,
187
+ lastName: body.lastName,
188
+ addPhones: body.addPhones,
189
+ removePhones: body.removePhones,
190
+ addEmails: body.addEmails,
191
+ removeEmails: body.removeEmails,
192
+ });
193
+ if (!ok) {
194
+ res
195
+ .status(500)
196
+ .json(setupError("internal_error", "contact update failed — see server logs. Contact may not exist, or write permission may be denied."));
197
+ return;
198
+ }
199
+ res.status(200).json({ id, updated: true });
200
+ }
201
+ catch (err) {
202
+ res
203
+ .status(500)
204
+ .json(setupError("internal_error", `updateContact threw: ${err instanceof Error ? err.message : String(err)}`));
205
+ }
206
+ }
207
+ // ── DELETE /api/imessage/contacts/:id ───────────────────────────────
208
+ async function handleDeleteContact(req, res, runtime) {
209
+ const pathname = req.url ?? "";
210
+ const id = parseContactId(pathname.split("?")[0]);
211
+ if (!id) {
212
+ res.status(400).json(setupError("bad_request", "contact id is required in the path"));
213
+ return;
214
+ }
215
+ const service = resolveService(runtime);
216
+ if (!service) {
217
+ res.status(503).json(setupError("service_unavailable", "imessage service not registered"));
218
+ return;
219
+ }
220
+ try {
221
+ const ok = await service.deleteContact(id);
222
+ if (!ok) {
223
+ res
224
+ .status(500)
225
+ .json(setupError("internal_error", "contact delete failed — see server logs. Contact may not exist, or write permission may be denied."));
226
+ return;
227
+ }
228
+ res.status(200).json({ id, deleted: true });
229
+ }
230
+ catch (err) {
231
+ res
232
+ .status(500)
233
+ .json(setupError("internal_error", `deleteContact threw: ${err instanceof Error ? err.message : String(err)}`));
234
+ }
235
+ }
236
+ export const imessageDataRoutes = [
237
+ {
238
+ type: "GET",
239
+ path: "/api/imessage/messages",
240
+ handler: handleMessages,
241
+ rawPath: true,
242
+ },
243
+ {
244
+ type: "POST",
245
+ path: "/api/imessage/messages",
246
+ handler: handleSendMessage,
247
+ rawPath: true,
248
+ },
249
+ {
250
+ type: "GET",
251
+ path: "/api/imessage/chats",
252
+ handler: handleChats,
253
+ rawPath: true,
254
+ },
255
+ {
256
+ type: "GET",
257
+ path: "/api/imessage/contacts",
258
+ handler: handleListContacts,
259
+ rawPath: true,
260
+ },
261
+ {
262
+ type: "POST",
263
+ path: "/api/imessage/contacts",
264
+ handler: handleCreateContact,
265
+ rawPath: true,
266
+ },
267
+ {
268
+ type: "PATCH",
269
+ path: "/api/imessage/contacts/:id",
270
+ handler: handleUpdateContact,
271
+ rawPath: true,
272
+ },
273
+ {
274
+ type: "DELETE",
275
+ path: "/api/imessage/contacts/:id",
276
+ handler: handleDeleteContact,
277
+ rawPath: true,
278
+ },
279
+ ];
280
+ //# sourceMappingURL=data-routes.js.map