@plosson/agentio 0.1.9 → 0.1.10

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
@@ -2,70 +2,46 @@
2
2
 
3
3
  CLI for LLM agents to interact with communication and tracking services.
4
4
 
5
- ## Installation
5
+ ## Quick Install
6
6
 
7
- ### macOS
8
-
9
- **Homebrew (recommended):**
7
+ **macOS / Linux:**
10
8
  ```bash
11
- brew tap plosson/agentio
12
- brew install agentio
9
+ curl -LsSf https://agentio.work/install | sh
13
10
  ```
14
11
 
15
- **Or download binary:**
16
- ```bash
17
- # Apple Silicon
18
- curl -L https://github.com/plosson/agentio/releases/latest/download/agentio-darwin-arm64 -o agentio
19
- chmod +x agentio && sudo mv agentio /usr/local/bin/
20
-
21
- # Intel
22
- curl -L https://github.com/plosson/agentio/releases/latest/download/agentio-darwin-x64 -o agentio
23
- chmod +x agentio && sudo mv agentio /usr/local/bin/
12
+ **Windows (PowerShell):**
13
+ ```powershell
14
+ iwr -useb https://agentio.work/install.ps1 | iex
24
15
  ```
25
16
 
26
- ### Linux
17
+ ## Update
27
18
 
28
- **Debian/Ubuntu (.deb):**
29
19
  ```bash
30
- # x64
31
- curl -LO https://github.com/plosson/agentio/releases/latest/download/agentio_0.1.3_amd64.deb
32
- sudo dpkg -i agentio_0.1.3_amd64.deb
33
-
34
- # ARM64
35
- curl -LO https://github.com/plosson/agentio/releases/latest/download/agentio_0.1.3_arm64.deb
36
- sudo dpkg -i agentio_0.1.3_arm64.deb
20
+ agentio update
37
21
  ```
38
22
 
39
- **Homebrew:**
23
+ ## Alternative Installation Methods
24
+
25
+ <details>
26
+ <summary>Homebrew (macOS/Linux)</summary>
27
+
40
28
  ```bash
41
29
  brew tap plosson/agentio
42
30
  brew install agentio
43
31
  ```
32
+ </details>
44
33
 
45
- **Or download binary:**
46
- ```bash
47
- # x64
48
- curl -L https://github.com/plosson/agentio/releases/latest/download/agentio-linux-x64 -o agentio
49
- chmod +x agentio && sudo mv agentio /usr/local/bin/
50
-
51
- # ARM64
52
- curl -L https://github.com/plosson/agentio/releases/latest/download/agentio-linux-arm64 -o agentio
53
- chmod +x agentio && sudo mv agentio /usr/local/bin/
54
- ```
55
-
56
- ### Windows
34
+ <details>
35
+ <summary>Scoop (Windows)</summary>
57
36
 
58
- **Scoop (recommended):**
59
37
  ```powershell
60
38
  scoop bucket add agentio https://github.com/plosson/scoop-agentio
61
39
  scoop install agentio
62
40
  ```
41
+ </details>
63
42
 
64
- **Or download binary:**
65
-
66
- Download `agentio-windows-x64.exe` from [GitHub Releases](https://github.com/plosson/agentio/releases/latest) and add to your PATH.
67
-
68
- ### npm / bun
43
+ <details>
44
+ <summary>npm / bun</summary>
69
45
 
70
46
  ```bash
71
47
  # Run directly
@@ -76,6 +52,16 @@ npx @plosson/agentio --help
76
52
  bun add -g @plosson/agentio
77
53
  npm install -g @plosson/agentio
78
54
  ```
55
+ </details>
56
+
57
+ <details>
58
+ <summary>Direct binary download</summary>
59
+
60
+ Download from [GitHub Releases](https://github.com/plosson/agentio/releases/latest):
61
+ - macOS: `agentio-darwin-arm64` (Apple Silicon) or `agentio-darwin-x64` (Intel)
62
+ - Linux: `agentio-linux-x64` or `agentio-linux-arm64`
63
+ - Windows: `agentio-windows-x64.exe`
64
+ </details>
79
65
 
80
66
  ## Services
81
67
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plosson/agentio",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "CLI for LLM agents to interact with communication and tracking services",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,7 +34,7 @@
34
34
  "scripts": {
35
35
  "dev": "bun run src/index.ts",
36
36
  "build": "bun build src/index.ts --outdir dist --target node",
37
- "build:native": "bun build src/index.ts --compile --minify --sourcemap --bytecode --outfile dist/agentio",
37
+ "build:native": "bun build src/index.ts --compile --minify --sourcemap --bytecode --define BUILD_VERSION=\"\\\"$(bun -e 'console.log(require(\"./package.json\").version)')\\\"\" --outfile dist/agentio",
38
38
  "typecheck": "tsc --noEmit"
39
39
  },
40
40
  "engines": {
@@ -0,0 +1,303 @@
1
+ import { Command } from 'commander';
2
+ import { createInterface } from 'readline';
3
+ import { CliError, handleError } from '../utils/errors';
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import * as os from 'os';
7
+ // Import package.json - bun will bundle this at compile time
8
+ import pkg from '../../package.json';
9
+
10
+ const GITHUB_REPO = 'plosson/agentio';
11
+
12
+ interface GitHubRelease {
13
+ tag_name: string;
14
+ assets: Array<{
15
+ name: string;
16
+ browser_download_url: string;
17
+ }>;
18
+ }
19
+
20
+ function prompt(question: string): Promise<string> {
21
+ const rl = createInterface({
22
+ input: process.stdin,
23
+ output: process.stderr,
24
+ });
25
+
26
+ return new Promise((resolve) => {
27
+ rl.question(question, (answer) => {
28
+ rl.close();
29
+ resolve(answer.trim());
30
+ });
31
+ });
32
+ }
33
+
34
+ function getCurrentVersion(): string {
35
+ return pkg.version;
36
+ }
37
+
38
+ function getPlatform(): string {
39
+ const platform = os.platform();
40
+ const arch = os.arch();
41
+
42
+ if (platform === 'darwin') {
43
+ return arch === 'arm64' ? 'darwin-arm64' : 'darwin-x64';
44
+ } else if (platform === 'linux') {
45
+ return arch === 'arm64' ? 'linux-arm64' : 'linux-x64';
46
+ } else if (platform === 'win32') {
47
+ return 'windows-x64';
48
+ }
49
+
50
+ throw new CliError('API_ERROR', `Unsupported platform: ${platform}-${arch}`);
51
+ }
52
+
53
+ function getAssetName(platform: string): string {
54
+ if (platform === 'windows-x64') {
55
+ return `agentio-${platform}.exe`;
56
+ }
57
+ return `agentio-${platform}`;
58
+ }
59
+
60
+ function isCompiledBinary(): boolean {
61
+ // In compiled bun binaries, argv[0] is just "bun" (no path)
62
+ // In dev mode, argv[0] is a full path like "/Users/.../bun"
63
+ return process.argv[0] === 'bun' && !process.execPath.endsWith('/bun');
64
+ }
65
+
66
+ function getExecutablePath(): string {
67
+ if (!isCompiledBinary()) {
68
+ throw new CliError(
69
+ 'API_ERROR',
70
+ 'Update command only works with compiled binaries',
71
+ 'In development, use git pull and bun install instead'
72
+ );
73
+ }
74
+ return process.execPath;
75
+ }
76
+
77
+ async function fetchLatestRelease(): Promise<GitHubRelease> {
78
+ const url = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
79
+ const response = await fetch(url, {
80
+ headers: {
81
+ 'Accept': 'application/vnd.github.v3+json',
82
+ 'User-Agent': 'agentio-updater',
83
+ },
84
+ });
85
+
86
+ if (!response.ok) {
87
+ if (response.status === 404) {
88
+ throw new CliError('NOT_FOUND', 'No releases found');
89
+ }
90
+ throw new CliError('API_ERROR', `Failed to fetch release info: ${response.statusText}`);
91
+ }
92
+
93
+ return response.json();
94
+ }
95
+
96
+ function compareVersions(current: string, latest: string): number {
97
+ const parseVersion = (v: string) => v.replace(/^v/, '').split('.').map(Number);
98
+ const currentParts = parseVersion(current);
99
+ const latestParts = parseVersion(latest);
100
+
101
+ for (let i = 0; i < 3; i++) {
102
+ const c = currentParts[i] || 0;
103
+ const l = latestParts[i] || 0;
104
+ if (l > c) return 1;
105
+ if (l < c) return -1;
106
+ }
107
+ return 0;
108
+ }
109
+
110
+ async function downloadBinary(url: string, dest: string): Promise<void> {
111
+ const response = await fetch(url, {
112
+ headers: {
113
+ 'User-Agent': 'agentio-updater',
114
+ },
115
+ });
116
+
117
+ if (!response.ok) {
118
+ throw new CliError('API_ERROR', `Download failed: ${response.statusText}`);
119
+ }
120
+
121
+ const buffer = await response.arrayBuffer();
122
+ fs.writeFileSync(dest, Buffer.from(buffer));
123
+ }
124
+
125
+ function moveFile(src: string, dest: string): void {
126
+ try {
127
+ // Try atomic rename first (fastest, works on same filesystem)
128
+ fs.renameSync(src, dest);
129
+ } catch (err: unknown) {
130
+ // If rename fails (cross-device), fall back to copy+delete
131
+ if (err && typeof err === 'object' && 'code' in err && err.code === 'EXDEV') {
132
+ fs.copyFileSync(src, dest);
133
+ fs.unlinkSync(src);
134
+ } else {
135
+ throw err;
136
+ }
137
+ }
138
+ }
139
+
140
+ async function updateBinary(downloadUrl: string, targetPath: string): Promise<void> {
141
+ const platform = os.platform();
142
+ const isWindows = platform === 'win32';
143
+
144
+ // Download to same directory as target to ensure same filesystem
145
+ const targetDir = path.dirname(targetPath);
146
+ const ext = isWindows ? '.exe' : '';
147
+ const tmpFile = path.join(targetDir, `.agentio-update-${Date.now()}${ext}`);
148
+
149
+ console.error('Downloading update...');
150
+ await downloadBinary(downloadUrl, tmpFile);
151
+
152
+ // Check file was downloaded
153
+ const stats = fs.statSync(tmpFile);
154
+ if (stats.size === 0) {
155
+ fs.unlinkSync(tmpFile);
156
+ throw new CliError('API_ERROR', 'Downloaded file is empty');
157
+ }
158
+
159
+ // Get original permissions
160
+ let originalMode = 0o755;
161
+ try {
162
+ originalMode = fs.statSync(targetPath).mode;
163
+ } catch {
164
+ // Use default if can't read original
165
+ }
166
+
167
+ console.error('Installing update...');
168
+
169
+ if (isWindows) {
170
+ // Windows: cannot delete/overwrite running executable, but CAN rename it
171
+ const backupPath = targetPath + '.old';
172
+ try {
173
+ // Try to remove old backup (may fail if locked from previous update)
174
+ try {
175
+ if (fs.existsSync(backupPath)) {
176
+ fs.unlinkSync(backupPath);
177
+ }
178
+ } catch {
179
+ // If we can't delete old backup, try renaming it instead
180
+ const oldBackup = targetPath + '.old2';
181
+ try {
182
+ if (fs.existsSync(oldBackup)) fs.unlinkSync(oldBackup);
183
+ fs.renameSync(backupPath, oldBackup);
184
+ } catch {
185
+ // Give up on cleanup, proceed anyway
186
+ }
187
+ }
188
+
189
+ // Move current executable to backup
190
+ fs.renameSync(targetPath, backupPath);
191
+
192
+ // Move new executable into place
193
+ moveFile(tmpFile, targetPath);
194
+
195
+ // Try to clean up backup (will likely fail since we're still running)
196
+ try {
197
+ fs.unlinkSync(backupPath);
198
+ } catch {
199
+ // Expected - Windows locks running executables
200
+ }
201
+ } catch (error) {
202
+ // Restore backup if something went wrong
203
+ try {
204
+ if (fs.existsSync(backupPath) && !fs.existsSync(targetPath)) {
205
+ fs.renameSync(backupPath, targetPath);
206
+ }
207
+ if (fs.existsSync(tmpFile)) {
208
+ fs.unlinkSync(tmpFile);
209
+ }
210
+ } catch {
211
+ // Best effort cleanup
212
+ }
213
+ throw error;
214
+ }
215
+ } else {
216
+ // Unix: atomic rename works even while binary is running
217
+ // The running process keeps the old inode open
218
+ fs.chmodSync(tmpFile, originalMode);
219
+ moveFile(tmpFile, targetPath);
220
+ }
221
+ }
222
+
223
+ export function registerUpdateCommand(program: Command): void {
224
+ program
225
+ .command('update')
226
+ .description('Update agentio to the latest version')
227
+ .option('--check', 'Only check for updates, don\'t install')
228
+ .option('--force', 'Force update even if already on latest version')
229
+ .option('-y, --yes', 'Skip confirmation prompt')
230
+ .action(async (options) => {
231
+ try {
232
+ const currentVersion = getCurrentVersion();
233
+ const platform = getPlatform();
234
+ const assetName = getAssetName(platform);
235
+
236
+ console.error(`Current version: ${currentVersion}`);
237
+ console.error(`Platform: ${platform}`);
238
+ console.error('');
239
+ console.error('Checking for updates...');
240
+
241
+ const release = await fetchLatestRelease();
242
+ const latestVersion = release.tag_name.replace(/^v/, '');
243
+
244
+ const comparison = compareVersions(currentVersion, latestVersion);
245
+
246
+ if (comparison === 0 && !options.force) {
247
+ console.log(`Already on the latest version (${currentVersion})`);
248
+ return;
249
+ }
250
+
251
+ if (comparison < 0) {
252
+ console.log(`Current version (${currentVersion}) is newer than latest release (${latestVersion})`);
253
+ if (!options.force) {
254
+ return;
255
+ }
256
+ }
257
+
258
+ console.log(`New version available: ${latestVersion}`);
259
+
260
+ if (options.check) {
261
+ return;
262
+ }
263
+
264
+ // Find the asset for this platform
265
+ const asset = release.assets.find(a => a.name === assetName);
266
+ if (!asset) {
267
+ throw new CliError(
268
+ 'NOT_FOUND',
269
+ `No binary found for ${platform}`,
270
+ `Available assets: ${release.assets.map(a => a.name).join(', ')}`
271
+ );
272
+ }
273
+
274
+ // Confirm update
275
+ if (!options.yes) {
276
+ const answer = await prompt(`Update from ${currentVersion} to ${latestVersion}? [y/N] `);
277
+ if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
278
+ console.error('Update cancelled');
279
+ return;
280
+ }
281
+ }
282
+
283
+ try {
284
+ const execPath = getExecutablePath();
285
+ await updateBinary(asset.browser_download_url, execPath);
286
+ console.log(`Successfully updated to version ${latestVersion}`);
287
+ } catch (error) {
288
+ console.error('');
289
+ console.error('Automatic update failed. You can update manually:');
290
+ console.error('');
291
+ if (os.platform() === 'win32') {
292
+ console.error(' iwr -useb https://agentio.work/install.ps1 | iex');
293
+ } else {
294
+ console.error(' curl -LsSf https://agentio.work/install | sh');
295
+ }
296
+ console.error('');
297
+ throw error;
298
+ }
299
+ } catch (error) {
300
+ handleError(error);
301
+ }
302
+ });
303
+ }
package/src/index.ts CHANGED
@@ -5,18 +5,22 @@ import { registerTelegramCommands } from './commands/telegram';
5
5
  import { registerGChatCommands } from './commands/gchat';
6
6
  import { registerJiraCommands } from './commands/jira';
7
7
  import { registerSlackCommands } from './commands/slack';
8
+ import { registerUpdateCommand } from './commands/update';
9
+
10
+ declare const BUILD_VERSION: string;
8
11
 
9
12
  const program = new Command();
10
13
 
11
14
  program
12
15
  .name('agentio')
13
16
  .description('CLI for LLM agents to interact with communication and tracking services')
14
- .version('0.1.0');
17
+ .version(BUILD_VERSION);
15
18
 
16
19
  registerGmailCommands(program);
17
20
  registerTelegramCommands(program);
18
21
  registerGChatCommands(program);
19
22
  registerJiraCommands(program);
20
23
  registerSlackCommands(program);
24
+ registerUpdateCommand(program);
21
25
 
22
26
  program.parse();