@ai-chat.email/cli 5.2.5

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/LICENSE.md ADDED
@@ -0,0 +1 @@
1
+ © MailPilot & DOSAYGO. All rights reserved. Use is subject to the Legal Agreements outlined here: https://mailpilot.chat/terms.html
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # @mailpilot/cli
2
+
3
+ Email AI agents like you email humans.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i -g @mailpilot/cli
9
+ ```
10
+
11
+ Or via shell:
12
+
13
+ ```bash
14
+ # macOS / Linux
15
+ curl -fsSL mailpilot.chat/install.sh | bash
16
+
17
+ # Windows (PowerShell)
18
+ irm mailpilot.chat/install.ps1 | iex
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```bash
24
+ mailpilot --auth # Authenticate with your license
25
+ mailpilot # Start the TUI
26
+ ```
27
+
28
+ ## How it works
29
+
30
+ This package is a thin wrapper that downloads the native MailPilot binary for your platform on first run. Updates are downloaded automatically when available.
31
+
32
+ The binary is cached in `node_modules/@mailpilot/cli/.binary/`.
33
+
34
+ ## Supported Platforms
35
+
36
+ - macOS (Apple Silicon / arm64)
37
+ - Linux (x64)
38
+ - Windows (x64)
39
+
40
+ ## Links
41
+
42
+ - [Website](https://mailpilot.chat)
43
+ - [GitHub](https://github.com/MailPilotHQ/MailPilot)
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env node
2
+ const { spawn, execSync } = require('child_process');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const https = require('https');
6
+ const os = require('os');
7
+
8
+ const GITHUB_REPO = 'MailPilotBlack/MailPilotBlack';
9
+ const BINARY_DIR = path.join(__dirname, '..', '.binary');
10
+ const CACHE_FILE = path.join(BINARY_DIR, '.version-cache');
11
+ const CACHE_TTL = 10800; // 3 hours in seconds
12
+
13
+ function getPlatformInfo() {
14
+ const platform = os.platform();
15
+ const arch = os.arch();
16
+
17
+ let osName, archName, ext = '';
18
+
19
+ switch (platform) {
20
+ case 'darwin':
21
+ osName = 'macos';
22
+ break;
23
+ case 'linux':
24
+ osName = 'linux';
25
+ break;
26
+ case 'win32':
27
+ osName = 'windows';
28
+ ext = '.exe';
29
+ break;
30
+ default:
31
+ console.error(`Unsupported platform: ${platform}`);
32
+ process.exit(1);
33
+ }
34
+
35
+ switch (arch) {
36
+ case 'arm64':
37
+ archName = 'arm64';
38
+ break;
39
+ case 'x64':
40
+ archName = 'x64';
41
+ break;
42
+ default:
43
+ console.error(`Unsupported architecture: ${arch}`);
44
+ process.exit(1);
45
+ }
46
+
47
+ // Validate supported combinations
48
+ if (osName === 'macos' && archName !== 'arm64') {
49
+ console.error('Only Apple Silicon (arm64) is supported on macOS.');
50
+ process.exit(1);
51
+ }
52
+ if (osName === 'linux' && archName !== 'x64') {
53
+ console.error('Only x64 is supported on Linux.');
54
+ process.exit(1);
55
+ }
56
+ if (osName === 'windows' && archName !== 'x64') {
57
+ console.error('Only x64 is supported on Windows.');
58
+ process.exit(1);
59
+ }
60
+
61
+ return { osName, archName, ext };
62
+ }
63
+
64
+ function getBinaryPath() {
65
+ const { osName, archName, ext } = getPlatformInfo();
66
+ const binaryName = `mailpilot-${osName}-${archName}${ext}`;
67
+ return path.join(BINARY_DIR, binaryName);
68
+ }
69
+
70
+ function getLatestVersion() {
71
+ return new Promise((resolve, reject) => {
72
+ // Check cache first
73
+ if (fs.existsSync(CACHE_FILE)) {
74
+ try {
75
+ const stat = fs.statSync(CACHE_FILE);
76
+ const cacheAge = (Date.now() - stat.mtimeMs) / 1000;
77
+ if (cacheAge < CACHE_TTL) {
78
+ const cached = fs.readFileSync(CACHE_FILE, 'utf8').trim();
79
+ if (cached) {
80
+ resolve(cached);
81
+ return;
82
+ }
83
+ }
84
+ } catch {
85
+ // Cache read failed, fetch from API
86
+ }
87
+ }
88
+
89
+ const url = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
90
+ const req = https.get(url, { headers: { 'User-Agent': 'mailpilot-cli' }, timeout: 5000 }, (res) => {
91
+ if (res.statusCode === 302 || res.statusCode === 301) {
92
+ // Follow redirect
93
+ https.get(res.headers.location, { headers: { 'User-Agent': 'mailpilot-cli' }, timeout: 5000 }, (res2) => {
94
+ let data = '';
95
+ res2.on('data', chunk => data += chunk);
96
+ res2.on('end', () => {
97
+ try {
98
+ const json = JSON.parse(data);
99
+ const version = json.tag_name;
100
+ if (version) {
101
+ // Update cache
102
+ try {
103
+ if (!fs.existsSync(BINARY_DIR)) fs.mkdirSync(BINARY_DIR, { recursive: true });
104
+ fs.writeFileSync(CACHE_FILE, version);
105
+ } catch {}
106
+ }
107
+ resolve(version);
108
+ } catch (e) {
109
+ reject(e);
110
+ }
111
+ });
112
+ }).on('error', reject);
113
+ return;
114
+ }
115
+ let data = '';
116
+ res.on('data', chunk => data += chunk);
117
+ res.on('end', () => {
118
+ try {
119
+ const json = JSON.parse(data);
120
+ const version = json.tag_name;
121
+ if (version) {
122
+ // Update cache
123
+ try {
124
+ if (!fs.existsSync(BINARY_DIR)) fs.mkdirSync(BINARY_DIR, { recursive: true });
125
+ fs.writeFileSync(CACHE_FILE, version);
126
+ } catch {}
127
+ }
128
+ resolve(version);
129
+ } catch (e) {
130
+ reject(e);
131
+ }
132
+ });
133
+ });
134
+ req.on('error', reject);
135
+ req.on('timeout', () => {
136
+ req.destroy();
137
+ reject(new Error('Request timed out'));
138
+ });
139
+ });
140
+ }
141
+
142
+ function getCurrentVersion(binaryPath) {
143
+ if (!fs.existsSync(binaryPath)) return null;
144
+ try {
145
+ const result = execSync(`"${binaryPath}" --version`, { encoding: 'utf8', timeout: 5000 });
146
+ const match = result.match(/v?\d+\.\d+\.\d+/);
147
+ return match ? match[0].replace(/^v/, '') : null; // Always strip v prefix for comparison
148
+ } catch {
149
+ return null;
150
+ }
151
+ }
152
+
153
+ function normalizeVersion(v) {
154
+ return v ? v.replace(/^v/, '') : null;
155
+ }
156
+
157
+ function downloadBinary(version) {
158
+ return new Promise((resolve, reject) => {
159
+ const { osName, archName, ext } = getPlatformInfo();
160
+ const assetName = `mailpilot-${osName}-${archName}${ext}`;
161
+ const url = `https://github.com/${GITHUB_REPO}/releases/download/${version}/${assetName}`;
162
+
163
+ if (!fs.existsSync(BINARY_DIR)) {
164
+ fs.mkdirSync(BINARY_DIR, { recursive: true });
165
+ }
166
+
167
+ const binaryPath = path.join(BINARY_DIR, assetName);
168
+ const tempPath = binaryPath + '.tmp';
169
+ const oldPath = binaryPath + '.old';
170
+ const file = fs.createWriteStream(tempPath);
171
+
172
+ function download(downloadUrl) {
173
+ https.get(downloadUrl, (res) => {
174
+ if (res.statusCode === 302 || res.statusCode === 301) {
175
+ download(res.headers.location);
176
+ return;
177
+ }
178
+ if (res.statusCode !== 200) {
179
+ reject(new Error(`Download failed: HTTP ${res.statusCode}`));
180
+ return;
181
+ }
182
+ res.pipe(file);
183
+ file.on('finish', () => {
184
+ file.close();
185
+
186
+ // Safe atomic update: rename old → move new → cleanup old
187
+ try {
188
+ if (process.platform !== 'win32') {
189
+ fs.chmodSync(tempPath, 0o755);
190
+ }
191
+
192
+ // Rename existing binary to .old (keeps inode for running process)
193
+ if (fs.existsSync(binaryPath)) {
194
+ try {
195
+ fs.renameSync(binaryPath, oldPath);
196
+ } catch (e) {
197
+ // Binary might be locked (running) on Windows
198
+ console.error('Binary in use, update pending for next run.');
199
+ // Leave .tmp for next run to pick up
200
+ resolve(binaryPath);
201
+ return;
202
+ }
203
+ }
204
+
205
+ // Move new binary into place
206
+ fs.renameSync(tempPath, binaryPath);
207
+
208
+ // Clean up old binary after short delay
209
+ if (fs.existsSync(oldPath)) {
210
+ setTimeout(() => {
211
+ try { fs.unlinkSync(oldPath); } catch {}
212
+ }, 5000);
213
+ }
214
+
215
+ resolve(binaryPath);
216
+ } catch (err) {
217
+ reject(err);
218
+ }
219
+ });
220
+ }).on('error', (err) => {
221
+ fs.unlink(tempPath, () => {});
222
+ reject(err);
223
+ });
224
+ }
225
+
226
+ download(url);
227
+ });
228
+ }
229
+
230
+ function detectInstalledAgents() {
231
+ const agents = [];
232
+ const { execSync } = require('child_process');
233
+
234
+ const checkCommand = (cmd) => {
235
+ try {
236
+ if (process.platform === 'win32') {
237
+ execSync(`where ${cmd}`, { stdio: 'ignore' });
238
+ } else {
239
+ execSync(`command -v ${cmd}`, { stdio: 'ignore' });
240
+ }
241
+ return true;
242
+ } catch {
243
+ return false;
244
+ }
245
+ };
246
+
247
+ if (checkCommand('claude')) agents.push('claude');
248
+ if (checkCommand('copilot') || checkCommand('github-copilot-cli')) agents.push('copilot');
249
+ if (checkCommand('codex')) agents.push('codex');
250
+ if (checkCommand('gemini')) agents.push('gemini');
251
+ if (checkCommand('opencode')) agents.push('opencode');
252
+
253
+ return agents;
254
+ }
255
+
256
+ function showFirstRunInstructions() {
257
+ console.error('');
258
+ console.error('mailpilot is in your PATH.');
259
+ console.error('');
260
+ console.error('Run: mailpilot --auth');
261
+ console.error('');
262
+ console.error('Then run:');
263
+
264
+ const agents = detectInstalledAgents();
265
+ if (agents.length > 0) {
266
+ agents.forEach(agent => console.error(` mailpilot ${agent}`));
267
+ } else {
268
+ // No agents detected, show common examples
269
+ console.error(' mailpilot claude');
270
+ console.error(' mailpilot copilot');
271
+ console.error(' mailpilot codex');
272
+ }
273
+ console.error('');
274
+ console.error('Tip: Get your agent working on a task first, then Ctrl+G to hand off.');
275
+ console.error(' Stay in touch over email while your agent continues independently.');
276
+ }
277
+
278
+ async function main() {
279
+ const binaryPath = getBinaryPath();
280
+ const tempPath = binaryPath + '.tmp';
281
+
282
+ // Check for pending update from previous locked run
283
+ if (fs.existsSync(tempPath) && !fs.existsSync(binaryPath)) {
284
+ try {
285
+ fs.renameSync(tempPath, binaryPath);
286
+ if (process.platform !== 'win32') {
287
+ fs.chmodSync(binaryPath, 0o755);
288
+ }
289
+ } catch {}
290
+ }
291
+
292
+ const currentVersion = getCurrentVersion(binaryPath);
293
+
294
+ let needsDownload = !currentVersion;
295
+ let latestVersion = null;
296
+
297
+ // Check for updates if binary exists
298
+ if (currentVersion) {
299
+ try {
300
+ latestVersion = await getLatestVersion();
301
+ const latestNorm = normalizeVersion(latestVersion);
302
+ if (latestNorm && latestNorm !== currentVersion) {
303
+ needsDownload = true;
304
+ }
305
+ } catch {
306
+ // Couldn't check for updates, use existing binary
307
+ }
308
+ } else {
309
+ // No binary, must download
310
+ try {
311
+ latestVersion = await getLatestVersion();
312
+ } catch (err) {
313
+ console.error('Failed to fetch latest version:', err.message);
314
+ process.exit(1);
315
+ }
316
+ }
317
+
318
+ if (needsDownload) {
319
+ const version = latestVersion || ('v' + currentVersion);
320
+ console.error(currentVersion ? `Updating to ${version}...` : 'Installing MailPilot...');
321
+ try {
322
+ await downloadBinary(version);
323
+
324
+ // Show first-run instructions only on fresh install (not update)
325
+ if (!currentVersion) {
326
+ showFirstRunInstructions();
327
+ }
328
+ } catch (err) {
329
+ console.error('Download failed:', err.message);
330
+ if (currentVersion) {
331
+ console.error('Using existing version.');
332
+ } else {
333
+ process.exit(1);
334
+ }
335
+ }
336
+ }
337
+
338
+ // Run the binary
339
+ const env = { ...process.env };
340
+ if (!env.MAILPILOT_ORIGINAL_CWD) {
341
+ env.MAILPILOT_ORIGINAL_CWD = process.env.PWD || process.cwd();
342
+ }
343
+ const child = spawn(binaryPath, process.argv.slice(2), {
344
+ stdio: 'inherit',
345
+ windowsHide: false,
346
+ env
347
+ });
348
+
349
+ child.on('error', (err) => {
350
+ console.error('Failed to start mailpilot:', err.message);
351
+ process.exit(1);
352
+ });
353
+
354
+ child.on('exit', (code) => {
355
+ process.exit(code || 0);
356
+ });
357
+ }
358
+
359
+ main();
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@ai-chat.email/cli",
3
+ "version": "5.2.5",
4
+ "description": "AI Chat Email CLI - Email AI agents like you email humans",
5
+ "author": "DOSAYGO Corporation",
6
+ "license": "SEE LICENSE IN LICENSE.md",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/AI-Chat-Email/AI-Chat-Email"
10
+ },
11
+ "homepage": "https://ai-chat.email",
12
+ "bin": {
13
+ "aice": "bin/aice.js"
14
+ },
15
+ "files": [
16
+ "bin/"
17
+ ],
18
+ "engines": {
19
+ "node": ">=22"
20
+ },
21
+ "keywords": [
22
+ "aice",
23
+ "chat",
24
+ "mailpilot",
25
+ "ai",
26
+ "agents",
27
+ "email",
28
+ "cli",
29
+ "coding",
30
+ "automation"
31
+ ]
32
+ }