@agentick/connector-imessage 0.5.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/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # @agentick/connector-imessage
2
+
3
+ iMessage platform adapter for the Agentick connector system. macOS only.
4
+
5
+ Polls `~/Library/Messages/chat.db` for incoming messages and sends responses
6
+ via AppleScript through Messages.app.
7
+
8
+ ## Install
9
+
10
+ ```sh
11
+ pnpm add @agentick/connector-imessage
12
+ ```
13
+
14
+ Requires Node 22+ (`node:sqlite` built-in).
15
+
16
+ ## Prerequisites
17
+
18
+ Grant **Full Disk Access** to your terminal application in System Settings >
19
+ Privacy & Security > Full Disk Access. Without this, the agent cannot read
20
+ `chat.db`.
21
+
22
+ ## Usage
23
+
24
+ ```typescript
25
+ import { createConnector } from "@agentick/connector";
26
+ import { IMessagePlatform } from "@agentick/connector-imessage";
27
+
28
+ const connector = createConnector(
29
+ client,
30
+ new IMessagePlatform({
31
+ handle: "+15551234567",
32
+ }),
33
+ {
34
+ sessionId: "main",
35
+ contentPolicy: "summarized",
36
+ deliveryStrategy: "on-idle",
37
+ },
38
+ );
39
+
40
+ await connector.start();
41
+ ```
42
+
43
+ ## Options
44
+
45
+ ```typescript
46
+ interface IMessageConnectorOptions {
47
+ handle: string;
48
+ pollIntervalMs?: number;
49
+ sendDelay?: number;
50
+ dbPath?: string;
51
+ }
52
+ ```
53
+
54
+ **`handle`** — Phone number (with country code) or email address to watch.
55
+
56
+ **`pollIntervalMs`** — How often to poll `chat.db`. Default: 2000ms.
57
+
58
+ **`sendDelay`** — Delay between sending multiple messages to avoid rate
59
+ limiting by Messages.app. Default: 500ms.
60
+
61
+ **`dbPath`** — Custom path to `chat.db` (for testing).
62
+
63
+ ## How It Works
64
+
65
+ **Inbound**: Polls `chat.db` using `node:sqlite`. Tracks a ROWID watermark so
66
+ each poll only returns new messages. Filters by handle and `is_from_me = 0`.
67
+ Poll errors (e.g., `SQLITE_BUSY` when Messages.app holds a lock) are caught
68
+ and retried on the next interval.
69
+
70
+ **Outbound**: Sends via `osascript` driving Messages.app. Text is escaped to
71
+ prevent AppleScript injection.
72
+
73
+ **Confirmations**: Text-based only. Sends a prompt like "Allow shell to
74
+ execute? Reply yes/no" and parses the next inbound message as the response.
75
+ Natural language is supported — "yes but only in /tmp" is approved with the
76
+ full text as reason.
77
+
78
+ ## Recommended Config
79
+
80
+ ```typescript
81
+ {
82
+ contentPolicy: "summarized", // clean summaries, no raw tool blocks
83
+ deliveryStrategy: "on-idle", // one polished message per execution
84
+ }
85
+ ```
86
+
87
+ iMessage works best with `"on-idle"` delivery — one complete, well-composed
88
+ message rather than a stream of fragments.
89
+
90
+ ## Exports
91
+
92
+ ```typescript
93
+ export { IMessagePlatform, type IMessageConnectorOptions } from "./imessage-platform.js";
94
+ export { IMessageDB, type IMessageRow } from "./imessage-db.js";
95
+ export { sendIMessage, buildAppleScript } from "./imessage-send.js";
96
+ ```
@@ -0,0 +1,32 @@
1
+ export interface IMessageRow {
2
+ rowid: number;
3
+ text: string;
4
+ date: number;
5
+ is_from_me: number;
6
+ handle_id: string;
7
+ }
8
+ /**
9
+ * Polls the iMessage chat.db for new incoming messages.
10
+ *
11
+ * Uses node:sqlite (Node 22+) to read the Messages database directly.
12
+ * Tracks a ROWID watermark so each poll only returns new messages.
13
+ *
14
+ * Requires Full Disk Access in System Settings > Privacy & Security
15
+ * for the terminal application running the agent.
16
+ */
17
+ export declare class IMessageDB {
18
+ private _db;
19
+ private _watermark;
20
+ private readonly _handle;
21
+ private readonly _dbPath;
22
+ constructor(handle: string, dbPath?: string);
23
+ open(): void;
24
+ close(): void;
25
+ /**
26
+ * Poll for new incoming messages from the configured handle.
27
+ * Returns messages with ROWID > watermark, then advances watermark.
28
+ */
29
+ poll(): IMessageRow[];
30
+ private _getMaxRowId;
31
+ }
32
+ //# sourceMappingURL=imessage-db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imessage-db.d.ts","sourceRoot":"","sources":["../src/imessage-db.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAK3C,IAAI,IAAI,IAAI;IAMZ,KAAK,IAAI,IAAI;IAKb;;;OAGG;IACH,IAAI,IAAI,WAAW,EAAE;IA6BrB,OAAO,CAAC,YAAY;CAMrB"}
@@ -0,0 +1,68 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ /**
5
+ * Polls the iMessage chat.db for new incoming messages.
6
+ *
7
+ * Uses node:sqlite (Node 22+) to read the Messages database directly.
8
+ * Tracks a ROWID watermark so each poll only returns new messages.
9
+ *
10
+ * Requires Full Disk Access in System Settings > Privacy & Security
11
+ * for the terminal application running the agent.
12
+ */
13
+ export class IMessageDB {
14
+ _db = null;
15
+ _watermark = 0;
16
+ _handle;
17
+ _dbPath;
18
+ constructor(handle, dbPath) {
19
+ this._handle = handle;
20
+ this._dbPath = dbPath ?? join(homedir(), "Library/Messages/chat.db");
21
+ }
22
+ open() {
23
+ this._db = new DatabaseSync(this._dbPath, { open: true, readOnly: true });
24
+ // Initialize watermark to current max ROWID so we only get new messages
25
+ this._watermark = this._getMaxRowId();
26
+ }
27
+ close() {
28
+ this._db?.close();
29
+ this._db = null;
30
+ }
31
+ /**
32
+ * Poll for new incoming messages from the configured handle.
33
+ * Returns messages with ROWID > watermark, then advances watermark.
34
+ */
35
+ poll() {
36
+ if (!this._db)
37
+ return [];
38
+ const stmt = this._db.prepare(`
39
+ SELECT
40
+ m.ROWID as rowid,
41
+ m.text as text,
42
+ m.date as date,
43
+ m.is_from_me as is_from_me,
44
+ h.id as handle_id
45
+ FROM message m
46
+ JOIN handle h ON m.handle_id = h.ROWID
47
+ WHERE h.id = ?
48
+ AND m.ROWID > ?
49
+ AND m.is_from_me = 0
50
+ AND m.text IS NOT NULL
51
+ AND m.text != ''
52
+ ORDER BY m.ROWID ASC
53
+ `);
54
+ const rows = stmt.all(this._handle, this._watermark);
55
+ if (rows.length > 0) {
56
+ this._watermark = rows[rows.length - 1].rowid;
57
+ }
58
+ return rows;
59
+ }
60
+ _getMaxRowId() {
61
+ if (!this._db)
62
+ return 0;
63
+ const stmt = this._db.prepare("SELECT MAX(ROWID) as max_id FROM message");
64
+ const result = stmt.get();
65
+ return result?.max_id ?? 0;
66
+ }
67
+ }
68
+ //# sourceMappingURL=imessage-db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imessage-db.js","sourceRoot":"","sources":["../src/imessage-db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAUjC;;;;;;;;GAQG;AACH,MAAM,OAAO,UAAU;IACb,GAAG,GAAwB,IAAI,CAAC;IAChC,UAAU,GAAG,CAAC,CAAC;IACN,OAAO,CAAS;IAChB,OAAO,CAAS;IAEjC,YAAY,MAAc,EAAE,MAAe;QACzC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,0BAA0B,CAAC,CAAC;IACvE,CAAC;IAED,IAAI;QACF,IAAI,CAAC,GAAG,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAS,CAAC,CAAC;QACjF,wEAAwE;QACxE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;IACxC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;IAClB,CAAC;IAED;;;OAGG;IACH,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAC;QAEzB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;KAe7B,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAA6B,CAAC;QAEjF,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;QAChD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,IAAI,CAAC,GAAG;YAAE,OAAO,CAAC,CAAC;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAA2C,CAAC;QACnE,OAAO,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC;IAC7B,CAAC;CACF"}
@@ -0,0 +1,43 @@
1
+ import type { ConnectorPlatform, ConnectorBridge, ConnectorStatus } from "@agentick/connector";
2
+ export interface IMessageConnectorOptions {
3
+ /** Phone number or email to watch for messages. */
4
+ handle: string;
5
+ /** How often to poll chat.db, in milliseconds. Default: 2000. */
6
+ pollIntervalMs?: number;
7
+ /** Delay between sending multiple messages, in milliseconds. Default: 500. */
8
+ sendDelay?: number;
9
+ /** Custom path to chat.db (for testing). */
10
+ dbPath?: string;
11
+ }
12
+ /**
13
+ * iMessage platform adapter for the Agentick connector system.
14
+ * macOS only.
15
+ *
16
+ * Polls ~/Library/Messages/chat.db for incoming messages and sends
17
+ * responses via AppleScript -> Messages.app.
18
+ *
19
+ * Recommended config:
20
+ * - deliveryStrategy: "on-idle"
21
+ * - contentPolicy: "summarized"
22
+ * - renderMode: "message"
23
+ */
24
+ export declare class IMessagePlatform implements ConnectorPlatform {
25
+ private readonly _handle;
26
+ private readonly _pollIntervalMs;
27
+ private readonly _sendDelay;
28
+ private readonly _dbPath?;
29
+ private _db;
30
+ private _pollTimer;
31
+ private _bridge;
32
+ private _status;
33
+ private _pendingConfirmation;
34
+ constructor(options: IMessageConnectorOptions);
35
+ get status(): ConnectorStatus;
36
+ start(bridge: ConnectorBridge): Promise<void>;
37
+ stop(): Promise<void>;
38
+ private _scheduleNextPoll;
39
+ private _poll;
40
+ private _handleDelivery;
41
+ private _handleConfirmation;
42
+ }
43
+ //# sourceMappingURL=imessage-platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imessage-platform.d.ts","sourceRoot":"","sources":["../src/imessage-platform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EAEf,eAAe,EAChB,MAAM,qBAAqB,CAAC;AAM7B,MAAM,WAAW,wBAAwB;IACvC,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAElC,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,OAAO,CAAmC;IAElD,OAAO,CAAC,oBAAoB,CAEZ;gBAEJ,OAAO,EAAE,wBAAwB;IAO7C,IAAI,MAAM,IAAI,eAAe,CAE5B;IAEK,KAAK,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB7C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,KAAK;YA0BC,eAAe;YAaf,mBAAmB;CASlC"}
@@ -0,0 +1,115 @@
1
+ import { extractText, parseTextConfirmation, formatConfirmationMessage } from "@agentick/connector";
2
+ import { IMessageDB } from "./imessage-db.js";
3
+ import { sendIMessage } from "./imessage-send.js";
4
+ /**
5
+ * iMessage platform adapter for the Agentick connector system.
6
+ * macOS only.
7
+ *
8
+ * Polls ~/Library/Messages/chat.db for incoming messages and sends
9
+ * responses via AppleScript -> Messages.app.
10
+ *
11
+ * Recommended config:
12
+ * - deliveryStrategy: "on-idle"
13
+ * - contentPolicy: "summarized"
14
+ * - renderMode: "message"
15
+ */
16
+ export class IMessagePlatform {
17
+ _handle;
18
+ _pollIntervalMs;
19
+ _sendDelay;
20
+ _dbPath;
21
+ _db = null;
22
+ _pollTimer = null;
23
+ _bridge = null;
24
+ _status = "disconnected";
25
+ _pendingConfirmation = null;
26
+ constructor(options) {
27
+ this._handle = options.handle;
28
+ this._pollIntervalMs = options.pollIntervalMs ?? 2000;
29
+ this._sendDelay = options.sendDelay ?? 500;
30
+ this._dbPath = options.dbPath;
31
+ }
32
+ get status() {
33
+ return this._status;
34
+ }
35
+ async start(bridge) {
36
+ this._bridge = bridge;
37
+ this._status = "connecting";
38
+ bridge.reportStatus("connecting");
39
+ this._db = new IMessageDB(this._handle, this._dbPath);
40
+ this._db.open();
41
+ bridge.onDeliver((output) => {
42
+ return this._handleDelivery(output);
43
+ });
44
+ bridge.onConfirmation((request, respond) => {
45
+ this._handleConfirmation(request, respond).catch((err) => {
46
+ console.error("iMessage confirmation error:", err);
47
+ });
48
+ });
49
+ this._status = "connected";
50
+ bridge.reportStatus("connected");
51
+ this._scheduleNextPoll();
52
+ }
53
+ async stop() {
54
+ if (this._pollTimer) {
55
+ clearTimeout(this._pollTimer);
56
+ this._pollTimer = null;
57
+ }
58
+ this._db?.close();
59
+ this._db = null;
60
+ this._bridge = null;
61
+ this._status = "disconnected";
62
+ this._pendingConfirmation = null;
63
+ }
64
+ // --- Private ---
65
+ _scheduleNextPoll() {
66
+ this._pollTimer = setTimeout(() => {
67
+ this._poll();
68
+ if (this._bridge)
69
+ this._scheduleNextPoll();
70
+ }, this._pollIntervalMs);
71
+ }
72
+ _poll() {
73
+ if (!this._db || !this._bridge)
74
+ return;
75
+ let messages;
76
+ try {
77
+ messages = this._db.poll();
78
+ }
79
+ catch (err) {
80
+ // chat.db may be locked by Messages.app — log and retry next interval
81
+ console.error("iMessage poll error (will retry):", err);
82
+ return;
83
+ }
84
+ for (const msg of messages) {
85
+ const text = msg.text;
86
+ if (this._pendingConfirmation) {
87
+ const { respond } = this._pendingConfirmation;
88
+ this._pendingConfirmation = null;
89
+ respond(parseTextConfirmation(text));
90
+ continue;
91
+ }
92
+ this._bridge.send(text);
93
+ }
94
+ }
95
+ async _handleDelivery(output) {
96
+ for (const message of output.messages) {
97
+ const text = extractText(message.content, "\n\n");
98
+ if (!text)
99
+ continue;
100
+ await sendIMessage(this._handle, text);
101
+ if (this._sendDelay > 0) {
102
+ await sleep(this._sendDelay);
103
+ }
104
+ }
105
+ }
106
+ async _handleConfirmation(request, respond) {
107
+ this._pendingConfirmation = { respond };
108
+ const msg = formatConfirmationMessage(request);
109
+ await sendIMessage(this._handle, `${msg}\n\nReply yes/no (or explain what you'd like instead)`);
110
+ }
111
+ }
112
+ function sleep(ms) {
113
+ return new Promise((resolve) => setTimeout(resolve, ms));
114
+ }
115
+ //# sourceMappingURL=imessage-platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imessage-platform.js","sourceRoot":"","sources":["../src/imessage-platform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Send an iMessage via AppleScript.
3
+ *
4
+ * Uses `osascript` to drive Messages.app. The handle can be a phone
5
+ * number (with country code) or an email address.
6
+ */
7
+ export declare function sendIMessage(handle: string, text: string): Promise<void>;
8
+ /**
9
+ * Build the AppleScript to send an iMessage.
10
+ *
11
+ * The message text is escaped to prevent AppleScript injection:
12
+ * - Backslashes are doubled
13
+ * - Double quotes are escaped
14
+ */
15
+ export declare function buildAppleScript(handle: string, text: string): string;
16
+ //# sourceMappingURL=imessage-send.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imessage-send.d.ts","sourceRoot":"","sources":["../src/imessage-send.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYxE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAWrE"}
@@ -0,0 +1,39 @@
1
+ import { execFile } from "node:child_process";
2
+ /**
3
+ * Send an iMessage via AppleScript.
4
+ *
5
+ * Uses `osascript` to drive Messages.app. The handle can be a phone
6
+ * number (with country code) or an email address.
7
+ */
8
+ export function sendIMessage(handle, text) {
9
+ return new Promise((resolve, reject) => {
10
+ const script = buildAppleScript(handle, text);
11
+ execFile("osascript", ["-e", script], (error, _stdout, stderr) => {
12
+ if (error) {
13
+ reject(new Error(`Failed to send iMessage: ${stderr || error.message}`));
14
+ }
15
+ else {
16
+ resolve();
17
+ }
18
+ });
19
+ });
20
+ }
21
+ /**
22
+ * Build the AppleScript to send an iMessage.
23
+ *
24
+ * The message text is escaped to prevent AppleScript injection:
25
+ * - Backslashes are doubled
26
+ * - Double quotes are escaped
27
+ */
28
+ export function buildAppleScript(handle, text) {
29
+ const escapedText = text.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
30
+ const escapedHandle = handle.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
31
+ return [
32
+ 'tell application "Messages"',
33
+ " set targetService to 1st service whose service type = iMessage",
34
+ ` set targetBuddy to buddy "${escapedHandle}" of targetService`,
35
+ ` send "${escapedText}" to targetBuddy`,
36
+ "end tell",
37
+ ].join("\n");
38
+ }
39
+ //# sourceMappingURL=imessage-send.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imessage-send.js","sourceRoot":"","sources":["../src/imessage-send.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,IAAY;IACvD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAE9C,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;YAC/D,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC3E,CAAC;iBAAM,CAAC;gBACN,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,IAAY;IAC3D,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrE,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAEzE,OAAO;QACL,6BAA6B;QAC7B,kEAAkE;QAClE,+BAA+B,aAAa,oBAAoB;QAChE,WAAW,WAAW,kBAAkB;QACxC,UAAU;KACX,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { IMessagePlatform, type IMessageConnectorOptions } from "./imessage-platform.js";
2
+ export { IMessageDB, type IMessageRow } from "./imessage-db.js";
3
+ export { sendIMessage, buildAppleScript } from "./imessage-send.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAEzF,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { IMessagePlatform } from "./imessage-platform.js";
2
+ export { IMessageDB } from "./imessage-db.js";
3
+ export { sendIMessage, buildAppleScript } from "./imessage-send.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAiC,MAAM,wBAAwB,CAAC;AAEzF,OAAO,EAAE,UAAU,EAAoB,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@agentick/connector-imessage",
3
+ "version": "0.5.0",
4
+ "description": "iMessage connector for Agentick — bridge iMessage to agent sessions (macOS only)",
5
+ "keywords": [
6
+ "agent",
7
+ "ai",
8
+ "connector",
9
+ "imessage",
10
+ "macos"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "Ryan Lindgren",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/agenticklabs/agentick.git",
17
+ "directory": "packages/connector-imessage"
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "os": [
23
+ "darwin"
24
+ ],
25
+ "type": "module",
26
+ "main": "src/index.ts",
27
+ "exports": {
28
+ ".": "./src/index.ts"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "import": "./dist/index.js"
36
+ }
37
+ },
38
+ "main": "./dist/index.js",
39
+ "types": "./dist/index.d.ts"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.build.json",
43
+ "test": "echo \"Tests run from workspace root\"",
44
+ "typecheck": "tsc -p tsconfig.build.json --noEmit",
45
+ "lint": "oxlint src/",
46
+ "format:check": "oxfmt --check src/",
47
+ "clean": "rm -rf dist tsconfig.build.tsbuildinfo",
48
+ "prepublishOnly": "pnpm build",
49
+ "dev": "tsc --watch"
50
+ },
51
+ "dependencies": {
52
+ "@agentick/connector": "workspace:*",
53
+ "@agentick/shared": "workspace:*"
54
+ },
55
+ "devDependencies": {
56
+ "typescript": "^5.8.3"
57
+ }
58
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { IMessagePlatform, type IMessageConnectorOptions } from "./imessage-platform.js";
2
+
3
+ export { IMessageDB, type IMessageRow } from "./imessage-db.js";
4
+ export { sendIMessage, buildAppleScript } from "./imessage-send.js";