@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 +96 -0
- package/dist/imessage-db.d.ts +32 -0
- package/dist/imessage-db.d.ts.map +1 -0
- package/dist/imessage-db.js +68 -0
- package/dist/imessage-db.js.map +1 -0
- package/dist/imessage-platform.d.ts +43 -0
- package/dist/imessage-platform.d.ts.map +1 -0
- package/dist/imessage-platform.js +115 -0
- package/dist/imessage-platform.js.map +1 -0
- package/dist/imessage-send.d.ts +16 -0
- package/dist/imessage-send.d.ts.map +1 -0
- package/dist/imessage-send.js +39 -0
- package/dist/imessage-send.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
- package/src/index.ts +4 -0
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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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