@vox-ai-app/integrations 1.0.0 → 1.0.2
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 +23 -26
- package/package.json +5 -6
- package/src/imessage/mac/reply.js +26 -0
- package/src/imessage/mac/service.js +0 -16
- package/src/mail/manage/mac/index.js +5 -5
- package/src/mail/send/mac/index.js +2 -2
package/README.md
CHANGED
|
@@ -1,41 +1,38 @@
|
|
|
1
|
-
# @vox-ai-app/
|
|
1
|
+
# @vox-ai-app/integrations
|
|
2
2
|
|
|
3
3
|
macOS system integrations for Vox: Apple Mail, Screen control, and iMessage. Each integration ships with tool implementations and LLM tool definitions.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- Initial release.## [1.0.0] - 2026-03-24- `openDb` / `closeDb` lifecycle.- WAL journal mode, foreign keys, auto-schema creation.- Initial SQLite-based persistence: conversations, messages, tasks, task activity.### Added## [1.0.2] - 2026-04-01- `checkpointId` type consistency.- `getAllSettings` return type (now returns object instead of array).### Fixed- Added migration system (`src/migrations/runner.js` + `001_initial_schema.js`).- Added new exports: `./tools`, `./settings`, `./mcp-servers`, `./schedules`, `./tool-secrets`, `./patterns`, `./vectors`.- Updated exports map: all repo modules now resolve to `./src/repos/*.js`.- Moved 9 repository files from flat `src/` into `src/repos/` subdirectory.### ChangedRequires macOS. Each integration needs specific system permissions granted by the user.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
npm install @vox-ai-app/
|
|
10
|
+
npm install @vox-ai-app/integrations
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Peer dependency: `electron >= 28`
|
|
14
14
|
|
|
15
15
|
## Exports
|
|
16
16
|
|
|
17
|
-
| Export
|
|
18
|
-
|
|
|
19
|
-
| `@vox-ai-app/
|
|
20
|
-
| `@vox-ai-app/
|
|
21
|
-
| `@vox-ai-app/
|
|
22
|
-
| `@vox-ai-app/
|
|
23
|
-
| `@vox-ai-app/
|
|
24
|
-
| `@vox-ai-app/
|
|
25
|
-
| `@vox-ai-app/
|
|
26
|
-
| `@vox-ai-app/
|
|
17
|
+
| Export | Contents |
|
|
18
|
+
| ----------------------------------------- | ----------------------------- |
|
|
19
|
+
| `@vox-ai-app/integrations` | All exports |
|
|
20
|
+
| `@vox-ai-app/integrations/defs/mail` | Mail tool definitions |
|
|
21
|
+
| `@vox-ai-app/integrations/defs/screen` | Screen tool definitions |
|
|
22
|
+
| `@vox-ai-app/integrations/defs/imessage` | iMessage tool definitions |
|
|
23
|
+
| `@vox-ai-app/integrations/mail` | Mail functions |
|
|
24
|
+
| `@vox-ai-app/integrations/screen` | Screen capture + control |
|
|
25
|
+
| `@vox-ai-app/integrations/screen/capture` | Capture only |
|
|
26
|
+
| `@vox-ai-app/integrations/screen/control` | Control only |
|
|
27
|
+
| `@vox-ai-app/integrations/screen/queue` | Session acquire/release |
|
|
28
|
+
| `@vox-ai-app/integrations/imessage` | iMessage data, reply, service |
|
|
27
29
|
|
|
28
30
|
## Mail
|
|
29
31
|
|
|
30
32
|
Requires **Automation permission** for Mail (System Settings → Privacy & Security → Automation).
|
|
31
33
|
|
|
32
34
|
```js
|
|
33
|
-
import {
|
|
34
|
-
sendEmail,
|
|
35
|
-
readEmails,
|
|
36
|
-
searchContacts,
|
|
37
|
-
replyToEmail
|
|
38
|
-
} from '@vox-ai-app/vox-integrations/mail'
|
|
35
|
+
import { sendEmail, readEmails, searchContacts, replyToEmail } from '@vox-ai-app/integrations/mail'
|
|
39
36
|
|
|
40
37
|
const emails = await readEmails({ account: 'Work', folder: 'INBOX', limit: 20 })
|
|
41
38
|
await sendEmail({ to: 'user@example.com', subject: 'Hi', body: 'Hello.' })
|
|
@@ -45,7 +42,7 @@ await replyToEmail({ messageId: '...', body: 'Thanks!' })
|
|
|
45
42
|
Tool definitions:
|
|
46
43
|
|
|
47
44
|
```js
|
|
48
|
-
import { MAIL_TOOL_DEFINITIONS } from '@vox-ai-app/
|
|
45
|
+
import { MAIL_TOOL_DEFINITIONS } from '@vox-ai-app/integrations/defs/mail'
|
|
49
46
|
```
|
|
50
47
|
|
|
51
48
|
## Screen
|
|
@@ -58,8 +55,8 @@ import {
|
|
|
58
55
|
clickAt,
|
|
59
56
|
typeText,
|
|
60
57
|
getUiElements
|
|
61
|
-
} from '@vox-ai-app/
|
|
62
|
-
import { acquireScreen, releaseScreen } from '@vox-ai-app/
|
|
58
|
+
} from '@vox-ai-app/integrations/screen'
|
|
59
|
+
import { acquireScreen, releaseScreen } from '@vox-ai-app/integrations/screen/queue'
|
|
63
60
|
|
|
64
61
|
const session = await acquireScreen()
|
|
65
62
|
try {
|
|
@@ -74,7 +71,7 @@ try {
|
|
|
74
71
|
Tool definitions:
|
|
75
72
|
|
|
76
73
|
```js
|
|
77
|
-
import { SCREEN_TOOL_DEFINITIONS } from '@vox-ai-app/
|
|
74
|
+
import { SCREEN_TOOL_DEFINITIONS } from '@vox-ai-app/integrations/defs/screen'
|
|
78
75
|
```
|
|
79
76
|
|
|
80
77
|
## iMessage
|
|
@@ -84,7 +81,7 @@ Requires **Full Disk Access** (System Settings → Privacy & Security → Full D
|
|
|
84
81
|
### Tool use (read conversations, send messages)
|
|
85
82
|
|
|
86
83
|
```js
|
|
87
|
-
import { listConversations, listContacts, sendReply } from '@vox-ai-app/
|
|
84
|
+
import { listConversations, listContacts, sendReply } from '@vox-ai-app/integrations/imessage'
|
|
88
85
|
|
|
89
86
|
const conversations = listConversations()
|
|
90
87
|
const contacts = listContacts()
|
|
@@ -94,7 +91,7 @@ await sendReply('+15551234567', 'Hello from Vox!')
|
|
|
94
91
|
### Gateway service (AI replies to incoming iMessages)
|
|
95
92
|
|
|
96
93
|
```js
|
|
97
|
-
import { createIMessageService } from '@vox-ai-app/
|
|
94
|
+
import { createIMessageService } from '@vox-ai-app/integrations/imessage'
|
|
98
95
|
|
|
99
96
|
const svc = createIMessageService({
|
|
100
97
|
logger,
|
|
@@ -117,7 +114,7 @@ svc.start('my-passphrase')
|
|
|
117
114
|
Tool definitions:
|
|
118
115
|
|
|
119
116
|
```js
|
|
120
|
-
import { IMESSAGE_TOOL_DEFINITIONS } from '@vox-ai-app/
|
|
117
|
+
import { IMESSAGE_TOOL_DEFINITIONS } from '@vox-ai-app/integrations/defs/imessage'
|
|
121
118
|
```
|
|
122
119
|
|
|
123
120
|
## License
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vox-ai-app/integrations",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "macOS integrations (Mail, Screen, iMessage) for Vox",
|
|
@@ -8,10 +8,9 @@
|
|
|
8
8
|
"private": false,
|
|
9
9
|
"exports": {
|
|
10
10
|
".": "./src/index.js",
|
|
11
|
-
"./defs": "./src/
|
|
12
|
-
"./defs/
|
|
13
|
-
"./defs/
|
|
14
|
-
"./defs/imessage": "./src/defs/imessage.js",
|
|
11
|
+
"./defs/mail": "./src/mail/def.js",
|
|
12
|
+
"./defs/screen": "./src/screen/def.js",
|
|
13
|
+
"./defs/imessage": "./src/imessage/def.js",
|
|
15
14
|
"./mail": "./src/mail/index.js",
|
|
16
15
|
"./screen": "./src/screen/index.js",
|
|
17
16
|
"./screen/capture": "./src/screen/capture/index.js",
|
|
@@ -36,7 +35,7 @@
|
|
|
36
35
|
"electron": ">=28.0.0"
|
|
37
36
|
},
|
|
38
37
|
"dependencies": {
|
|
39
|
-
"@vox-ai-app/tools": "^1.0.
|
|
38
|
+
"@vox-ai-app/tools": "^1.0.1",
|
|
40
39
|
"better-sqlite3": "^12.0.0"
|
|
41
40
|
}
|
|
42
41
|
}
|
|
@@ -3,9 +3,24 @@ import os from 'os'
|
|
|
3
3
|
import path from 'path'
|
|
4
4
|
import { promisify } from 'util'
|
|
5
5
|
import { exec } from 'child_process'
|
|
6
|
+
import { shell } from 'electron'
|
|
6
7
|
|
|
7
8
|
const execAsync = promisify(exec)
|
|
8
9
|
|
|
10
|
+
const isAutomationDeniedError = (err) => {
|
|
11
|
+
const msg = String(err?.message || err?.stderr || '').toLowerCase()
|
|
12
|
+
return (
|
|
13
|
+
msg.includes('not allowed to send apple events') ||
|
|
14
|
+
msg.includes('apple event handler failed') ||
|
|
15
|
+
msg.includes('-1743') ||
|
|
16
|
+
msg.includes('access not allowed')
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const openMessagesAutomationSettings = () => {
|
|
21
|
+
shell.openExternal('x-apple.systempreferences:com.apple.preference.security?Privacy_Automation')
|
|
22
|
+
}
|
|
23
|
+
|
|
9
24
|
const writeTmp = async (content, ext) => {
|
|
10
25
|
const file = path.join(os.tmpdir(), `vox_ims_${Date.now()}.${ext}`)
|
|
11
26
|
await fs.writeFile(file, content, 'utf8')
|
|
@@ -62,6 +77,17 @@ end tell`
|
|
|
62
77
|
try {
|
|
63
78
|
await execAsync(`osascript "${scriptFile}"`, { timeout: 15_000 })
|
|
64
79
|
console.info('[imessage] Reply sent to', handle, 'files:', filePaths.length)
|
|
80
|
+
} catch (err) {
|
|
81
|
+
if (isAutomationDeniedError(err)) {
|
|
82
|
+
openMessagesAutomationSettings()
|
|
83
|
+
throw Object.assign(
|
|
84
|
+
new Error(
|
|
85
|
+
'Vox needs permission to control Messages. Please grant it in System Settings → Privacy & Security → Automation → Vox → Messages.'
|
|
86
|
+
),
|
|
87
|
+
{ code: 'IMESSAGE_AUTOMATION_REQUIRED' }
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
throw err
|
|
65
91
|
} finally {
|
|
66
92
|
await fs.unlink(scriptFile).catch(() => {})
|
|
67
93
|
}
|
|
@@ -12,22 +12,6 @@ const REPLY_TIMEOUT_MS = 90_000
|
|
|
12
12
|
const FDA_ERROR_MESSAGE =
|
|
13
13
|
'Vox needs Full Disk Access to read Messages. Opening System Settings → Privacy & Security → Full Disk Access — please enable it for Vox and try again.'
|
|
14
14
|
|
|
15
|
-
/**
|
|
16
|
-
* Creates a self-contained iMessage watcher service.
|
|
17
|
-
*
|
|
18
|
-
* @param {object} opts
|
|
19
|
-
* @param {(text: string, handle: string) => Promise<string|null>} opts.onMessage
|
|
20
|
-
* Called when a passphrase-matched message arrives. Must return the AI reply text (or null to skip reply).
|
|
21
|
-
* @param {(text: string, handle: string) => void} [opts.onTranscript]
|
|
22
|
-
* Optional: called before onMessage, useful for emitting transcript events upstream.
|
|
23
|
-
* @param {() => void} [opts.onOpenSettings]
|
|
24
|
-
* Optional: called when Full Disk Access is required. Default opens nothing.
|
|
25
|
-
* @param {{ info: Function, warn: Function, error: Function }} [opts.logger]
|
|
26
|
-
* Optional: defaults to console.
|
|
27
|
-
* @param {number} [opts.pollIntervalMs]
|
|
28
|
-
* Poll interval in ms. Default 3000.
|
|
29
|
-
* @returns {{ start, stop, getPassphrase, listConversations, listContacts, openSettings }}
|
|
30
|
-
*/
|
|
31
15
|
export const createIMessageService = ({
|
|
32
16
|
onMessage,
|
|
33
17
|
onTranscript,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import { ensureAppleMailConfigured } from '../../shared/index.js'
|
|
10
10
|
|
|
11
11
|
const runAs = async (script, signal) => {
|
|
12
|
-
await ensureAppleMailConfigured()
|
|
12
|
+
await ensureAppleMailConfigured(signal)
|
|
13
13
|
const scriptFile = await writeTempScript(script, 'scpt')
|
|
14
14
|
try {
|
|
15
15
|
const { stdout } = await execAbortable(
|
|
@@ -53,7 +53,7 @@ export const replyToEmailMac = async (
|
|
|
53
53
|
{ messageId, body, replyAll = false, account },
|
|
54
54
|
{ signal } = {}
|
|
55
55
|
) => {
|
|
56
|
-
const bodyEsc = esc(body)
|
|
56
|
+
const bodyEsc = esc(body).replace(/\n/g, '" & return & "')
|
|
57
57
|
const replyCmd = replyAll ? 'reply theMsg with reply to all' : 'reply theMsg'
|
|
58
58
|
const senderLines = account
|
|
59
59
|
? [
|
|
@@ -83,7 +83,7 @@ export const replyToEmailMac = async (
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
export const forwardEmailMac = async ({ messageId, to, body = '', account }, { signal } = {}) => {
|
|
86
|
-
const bodyEsc = esc(body)
|
|
86
|
+
const bodyEsc = esc(body).replace(/\n/g, '" & return & "')
|
|
87
87
|
const senderLines = account
|
|
88
88
|
? [
|
|
89
89
|
` set acct to first account whose name contains "${esc(account)}"`,
|
|
@@ -205,11 +205,11 @@ export const createDraftMac = async (
|
|
|
205
205
|
` set acct to first account whose name contains "${esc(account)}"`,
|
|
206
206
|
' set addressesList to email addresses of acct',
|
|
207
207
|
' set senderAddr to item 1 of addressesList',
|
|
208
|
-
` set msg to make new outgoing message with properties {subject:"${esc(subject)}", content:"${esc(body).replace(/\n/g, '
|
|
208
|
+
` set msg to make new outgoing message with properties {subject:"${esc(subject)}", content:"${esc(body).replace(/\n/g, '" & return & "')}", visible:true, sender:senderAddr}`
|
|
209
209
|
)
|
|
210
210
|
} else {
|
|
211
211
|
lines.push(
|
|
212
|
-
` set msg to make new outgoing message with properties {subject:"${esc(subject)}", content:"${esc(body).replace(/\n/g, '
|
|
212
|
+
` set msg to make new outgoing message with properties {subject:"${esc(subject)}", content:"${esc(body).replace(/\n/g, '" & return & "')}", visible:true}`
|
|
213
213
|
)
|
|
214
214
|
}
|
|
215
215
|
lines.push(' tell msg')
|
|
@@ -9,11 +9,11 @@ import {
|
|
|
9
9
|
} from '@vox-ai-app/tools/exec'
|
|
10
10
|
import { ensureAppleMailConfigured } from '../../shared/index.js'
|
|
11
11
|
export const sendEmailMac = async (
|
|
12
|
-
{ to, cc, bcc, subject, body, attachments, account },
|
|
12
|
+
{ to, cc = [], bcc = [], subject, body, attachments = [], account },
|
|
13
13
|
{ signal } = {}
|
|
14
14
|
) => {
|
|
15
15
|
await ensureAppleMailConfigured(signal)
|
|
16
|
-
const bodyEsc = esc(body).replace(/\n/g, '
|
|
16
|
+
const bodyEsc = esc(body).replace(/\n/g, '" & return & "')
|
|
17
17
|
const lines = ['tell application "Mail"']
|
|
18
18
|
if (account) {
|
|
19
19
|
lines.push(
|