@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 +29 -43
- package/package.json +2 -2
- package/src/commands/update.ts +303 -0
- package/src/index.ts +5 -1
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
|
-
##
|
|
5
|
+
## Quick Install
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
**Homebrew (recommended):**
|
|
7
|
+
**macOS / Linux:**
|
|
10
8
|
```bash
|
|
11
|
-
|
|
12
|
-
brew install agentio
|
|
9
|
+
curl -LsSf https://agentio.work/install | sh
|
|
13
10
|
```
|
|
14
11
|
|
|
15
|
-
**
|
|
16
|
-
```
|
|
17
|
-
|
|
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
|
-
|
|
17
|
+
## Update
|
|
27
18
|
|
|
28
|
-
**Debian/Ubuntu (.deb):**
|
|
29
19
|
```bash
|
|
30
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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();
|