@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 CHANGED
@@ -1,41 +1,38 @@
1
- # @vox-ai-app/vox-integrations
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
- Requires macOS. Each integration needs specific system permissions granted by the user.
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/vox-integrations
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 | Contents |
18
- | --------------------------------------------- | ----------------------------- |
19
- | `@vox-ai-app/vox-integrations` | All exports |
20
- | `@vox-ai-app/vox-integrations/defs` | All tool definitions |
21
- | `@vox-ai-app/vox-integrations/mail` | Mail functions |
22
- | `@vox-ai-app/vox-integrations/screen` | Screen capture + control |
23
- | `@vox-ai-app/vox-integrations/screen/capture` | Capture only |
24
- | `@vox-ai-app/vox-integrations/screen/control` | Control only |
25
- | `@vox-ai-app/vox-integrations/screen/queue` | Session acquire/release |
26
- | `@vox-ai-app/vox-integrations/imessage` | iMessage data, reply, service |
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/vox-integrations/defs'
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/vox-integrations/screen'
62
- import { acquireScreen, releaseScreen } from '@vox-ai-app/vox-integrations/screen/queue'
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/vox-integrations/defs'
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/vox-integrations/imessage'
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/vox-integrations/imessage'
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/vox-integrations/defs'
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.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/defs/index.js",
12
- "./defs/mail": "./src/defs/mail.js",
13
- "./defs/screen": "./src/defs/screen.js",
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.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, '\\n')}", visible:true, sender:senderAddr}`
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, '\\n')}", visible:true}`
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, '\\n')
16
+ const bodyEsc = esc(body).replace(/\n/g, '" & return & "')
17
17
  const lines = ['tell application "Mail"']
18
18
  if (account) {
19
19
  lines.push(