@plosson/agentio 0.4.2 → 0.4.4
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 +4 -4
- package/package.json +3 -1
- package/src/auth/oauth.ts +22 -2
- package/src/commands/gateway.ts +259 -0
- package/src/commands/gcal.ts +383 -0
- package/src/commands/gsheets.ts +365 -0
- package/src/commands/gtasks.ts +326 -0
- package/src/commands/status.ts +85 -0
- package/src/commands/telegram.ts +209 -1
- package/src/commands/update.ts +2 -2
- package/src/commands/whatsapp.ts +853 -0
- package/src/config/config-manager.ts +1 -1
- package/src/gateway/adapters/telegram.ts +357 -0
- package/src/gateway/adapters/types.ts +147 -0
- package/src/gateway/adapters/whatsapp-auth.ts +172 -0
- package/src/gateway/adapters/whatsapp.ts +723 -0
- package/src/gateway/api.ts +791 -0
- package/src/gateway/client.ts +402 -0
- package/src/gateway/daemon.ts +461 -0
- package/src/gateway/store.ts +637 -0
- package/src/gateway/types.ts +325 -0
- package/src/gateway/webhook.ts +109 -0
- package/src/index.ts +34 -16
- package/src/polyfills.ts +10 -0
- package/src/services/gcal/client.ts +380 -0
- package/src/services/gsheets/client.ts +362 -0
- package/src/services/gtasks/client.ts +301 -0
- package/src/types/config.ts +37 -1
- package/src/types/gcal.ts +135 -0
- package/src/types/gsheets.ts +81 -0
- package/src/types/gtasks.ts +58 -0
- package/src/types/qrcode-terminal.d.ts +8 -0
- package/src/types/whatsapp.ts +116 -0
- package/src/utils/output.ts +586 -0
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ jobs:
|
|
|
34
34
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
35
35
|
steps:
|
|
36
36
|
- uses: actions/checkout@v4
|
|
37
|
-
- run: curl -LsSf https://agentio.
|
|
37
|
+
- run: curl -LsSf https://agentio.me/install | sh
|
|
38
38
|
- run: npm install -g @anthropic-ai/claude-code
|
|
39
39
|
- run: agentio config import && agentio claude install
|
|
40
40
|
- run: claude -p "$(cat prompt.md)" --max-turns 30 --dangerously-skip-permissions
|
|
@@ -53,12 +53,12 @@ See [`examples/daily-briefing/`](./examples/daily-briefing) for the complete wor
|
|
|
53
53
|
|
|
54
54
|
**macOS / Linux:**
|
|
55
55
|
```bash
|
|
56
|
-
curl -LsSf https://agentio.
|
|
56
|
+
curl -LsSf https://agentio.me/install | sh
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
**Windows (PowerShell):**
|
|
60
60
|
```powershell
|
|
61
|
-
iwr -useb https://agentio.
|
|
61
|
+
iwr -useb https://agentio.me/install.ps1 | iex
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
<details>
|
|
@@ -279,7 +279,7 @@ jobs:
|
|
|
279
279
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
280
280
|
steps:
|
|
281
281
|
- uses: actions/checkout@v4
|
|
282
|
-
- run: curl -LsSf https://agentio.
|
|
282
|
+
- run: curl -LsSf https://agentio.me/install | sh
|
|
283
283
|
- run: npm install -g @anthropic-ai/claude-code
|
|
284
284
|
- run: agentio config import && agentio claude install
|
|
285
285
|
- run: claude -p "$(cat prompt.md)" --max-turns 30 --dangerously-skip-permissions
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plosson/agentio",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "CLI for LLM agents to interact with communication and tracking services",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,9 +47,11 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@inquirer/prompts": "^8.2.0",
|
|
50
|
+
"@whiskeysockets/baileys": "^7.0.0-rc.9",
|
|
50
51
|
"commander": "^14.0.2",
|
|
51
52
|
"googleapis": "^169.0.0",
|
|
52
53
|
"libsodium-wrappers": "^0.8.1",
|
|
54
|
+
"qrcode-terminal": "^0.12.0",
|
|
53
55
|
"rss-parser": "^3.13.0"
|
|
54
56
|
}
|
|
55
57
|
}
|
package/src/auth/oauth.ts
CHANGED
|
@@ -34,15 +34,35 @@ const GDRIVE_FULL_SCOPES = [
|
|
|
34
34
|
'https://www.googleapis.com/auth/userinfo.email', // get email for profile naming
|
|
35
35
|
];
|
|
36
36
|
|
|
37
|
-
const
|
|
37
|
+
const GCAL_SCOPES = [
|
|
38
|
+
'https://www.googleapis.com/auth/calendar', // full access to calendars
|
|
39
|
+
'https://www.googleapis.com/auth/userinfo.email', // get email for profile naming
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const GTASKS_SCOPES = [
|
|
43
|
+
'https://www.googleapis.com/auth/tasks', // full access to tasks
|
|
44
|
+
'https://www.googleapis.com/auth/userinfo.email', // get email for profile naming
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const GSHEETS_SCOPES = [
|
|
48
|
+
'https://www.googleapis.com/auth/spreadsheets', // full access to spreadsheets
|
|
49
|
+
'https://www.googleapis.com/auth/drive.file', // create/access files created by this app
|
|
50
|
+
'https://www.googleapis.com/auth/drive.readonly', // list spreadsheets, export, copy
|
|
51
|
+
'https://www.googleapis.com/auth/userinfo.email', // get email for profile naming
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const SCOPES: Record<'gmail' | 'gchat' | 'gdocs' | 'gdrive-readonly' | 'gdrive-full' | 'gcal' | 'gtasks' | 'gsheets', string[]> = {
|
|
38
55
|
gmail: GMAIL_SCOPES,
|
|
39
56
|
gchat: GCHAT_SCOPES,
|
|
40
57
|
gdocs: GDOCS_SCOPES,
|
|
41
58
|
'gdrive-readonly': GDRIVE_READONLY_SCOPES,
|
|
42
59
|
'gdrive-full': GDRIVE_FULL_SCOPES,
|
|
60
|
+
gcal: GCAL_SCOPES,
|
|
61
|
+
gtasks: GTASKS_SCOPES,
|
|
62
|
+
gsheets: GSHEETS_SCOPES,
|
|
43
63
|
};
|
|
44
64
|
|
|
45
|
-
export type OAuthService = 'gmail' | 'gchat' | 'gdocs' | 'gdrive-readonly' | 'gdrive-full';
|
|
65
|
+
export type OAuthService = 'gmail' | 'gchat' | 'gdocs' | 'gdrive-readonly' | 'gdrive-full' | 'gcal' | 'gtasks' | 'gsheets';
|
|
46
66
|
|
|
47
67
|
export async function performOAuthFlow(
|
|
48
68
|
service: OAuthService
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { randomBytes } from 'crypto';
|
|
4
|
+
import { handleError, CliError } from '../utils/errors';
|
|
5
|
+
import { loadConfig, saveConfig } from '../config/config-manager';
|
|
6
|
+
import { startDaemon, stopDaemon, getDaemonStatus, reloadDaemon, LOG_FILE } from '../gateway/daemon';
|
|
7
|
+
import { exportWhatsAppAuthState, importWhatsAppAuthState } from '../gateway/store';
|
|
8
|
+
import type { Config } from '../types/config';
|
|
9
|
+
|
|
10
|
+
export function registerGatewayCommands(program: Command): void {
|
|
11
|
+
const gateway = program
|
|
12
|
+
.command('gateway')
|
|
13
|
+
.description('Gateway daemon management');
|
|
14
|
+
|
|
15
|
+
gateway
|
|
16
|
+
.command('start')
|
|
17
|
+
.description('Start the gateway daemon')
|
|
18
|
+
.option('--foreground', 'Run in foreground (don\'t daemonize)')
|
|
19
|
+
.action(async (options) => {
|
|
20
|
+
try {
|
|
21
|
+
await startDaemon({ foreground: options.foreground });
|
|
22
|
+
} catch (error) {
|
|
23
|
+
handleError(error);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
gateway
|
|
28
|
+
.command('stop')
|
|
29
|
+
.description('Stop the gateway daemon')
|
|
30
|
+
.action(async () => {
|
|
31
|
+
try {
|
|
32
|
+
await stopDaemon();
|
|
33
|
+
} catch (error) {
|
|
34
|
+
handleError(error);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
gateway
|
|
39
|
+
.command('status')
|
|
40
|
+
.description('Show gateway daemon status')
|
|
41
|
+
.action(async () => {
|
|
42
|
+
try {
|
|
43
|
+
const status = await getDaemonStatus();
|
|
44
|
+
|
|
45
|
+
if (!status.running) {
|
|
46
|
+
console.log('Gateway: stopped');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log(`Gateway: running (PID ${status.pid})`);
|
|
51
|
+
|
|
52
|
+
if (status.adapters && status.adapters.length > 0) {
|
|
53
|
+
console.log('\nConnected adapters:');
|
|
54
|
+
for (const adapter of status.adapters) {
|
|
55
|
+
const statusIcon = adapter.connected ? '✓' : '✗';
|
|
56
|
+
console.log(` ${statusIcon} ${adapter.service}:${adapter.profile}`);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
console.log('\nNo adapters connected');
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
handleError(error);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
gateway
|
|
67
|
+
.command('reload')
|
|
68
|
+
.description('Reload gateway configuration')
|
|
69
|
+
.action(async () => {
|
|
70
|
+
try {
|
|
71
|
+
await reloadDaemon();
|
|
72
|
+
} catch (error) {
|
|
73
|
+
handleError(error);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
gateway
|
|
78
|
+
.command('logs')
|
|
79
|
+
.description('View gateway logs')
|
|
80
|
+
.option('--follow, -f', 'Follow log output')
|
|
81
|
+
.option('--lines <n>', 'Number of lines to show', '50')
|
|
82
|
+
.action(async (options) => {
|
|
83
|
+
try {
|
|
84
|
+
if (!existsSync(LOG_FILE)) {
|
|
85
|
+
console.log('No log file found');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (options.follow) {
|
|
90
|
+
// Tail -f equivalent
|
|
91
|
+
const tailProcess = Bun.spawn(['tail', '-f', LOG_FILE], {
|
|
92
|
+
stdout: 'inherit',
|
|
93
|
+
stderr: 'inherit',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
process.on('SIGINT', () => {
|
|
97
|
+
tailProcess.kill();
|
|
98
|
+
process.exit(0);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await tailProcess.exited;
|
|
102
|
+
} else {
|
|
103
|
+
// Read last N lines
|
|
104
|
+
const lines = parseInt(options.lines, 10) || 50;
|
|
105
|
+
const tailProcess = Bun.spawn(['tail', `-${lines}`, LOG_FILE], {
|
|
106
|
+
stdout: 'inherit',
|
|
107
|
+
stderr: 'inherit',
|
|
108
|
+
});
|
|
109
|
+
await tailProcess.exited;
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
handleError(error);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Profile subcommands
|
|
117
|
+
const profile = gateway.command('profile').description('Manage gateway identity');
|
|
118
|
+
|
|
119
|
+
profile
|
|
120
|
+
.command('add')
|
|
121
|
+
.description('Create gateway identity with secret key')
|
|
122
|
+
.option('--name <name>', 'Gateway name', 'default')
|
|
123
|
+
.option('--secret <secret>', 'Use specific secret (otherwise auto-generated)')
|
|
124
|
+
.action(async (options) => {
|
|
125
|
+
try {
|
|
126
|
+
const config = await loadConfig() as Config;
|
|
127
|
+
|
|
128
|
+
if (config.gateway?.name) {
|
|
129
|
+
throw new CliError('INVALID_PARAMS', `Gateway already configured: ${config.gateway.name}`, 'Use "gateway profile remove" first to reconfigure');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Generate or use provided secret
|
|
133
|
+
const secret = options.secret || `gw_${randomBytes(24).toString('base64url')}`;
|
|
134
|
+
|
|
135
|
+
config.gateway = {
|
|
136
|
+
...config.gateway,
|
|
137
|
+
name: options.name,
|
|
138
|
+
secret,
|
|
139
|
+
api: {
|
|
140
|
+
...config.gateway?.api,
|
|
141
|
+
secret, // Also set as API secret
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
await saveConfig(config);
|
|
146
|
+
|
|
147
|
+
console.log(`Gateway profile created`);
|
|
148
|
+
console.log(` Name: ${options.name}`);
|
|
149
|
+
console.log(` Secret: ${secret}`);
|
|
150
|
+
console.log(`\nThis secret will be included in "agentio config export"`);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
handleError(error);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
profile
|
|
157
|
+
.command('list')
|
|
158
|
+
.description('Show gateway identity')
|
|
159
|
+
.action(async () => {
|
|
160
|
+
try {
|
|
161
|
+
const config = await loadConfig() as Config;
|
|
162
|
+
|
|
163
|
+
if (!config.gateway?.name) {
|
|
164
|
+
console.log('No gateway configured');
|
|
165
|
+
console.log('Run: agentio gateway profile add --name <name>');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
console.log('Gateway Profile');
|
|
170
|
+
console.log(` Name: ${config.gateway.name}`);
|
|
171
|
+
console.log(` Secret: ${config.gateway.secret ? config.gateway.secret.slice(0, 10) + '...' : '(not set)'}`);
|
|
172
|
+
console.log(` API Port: ${config.gateway.api?.port || 7890}`);
|
|
173
|
+
console.log(` API Host: ${config.gateway.api?.host || '127.0.0.1'}`);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
handleError(error);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
profile
|
|
180
|
+
.command('remove')
|
|
181
|
+
.description('Remove gateway identity')
|
|
182
|
+
.action(async () => {
|
|
183
|
+
try {
|
|
184
|
+
const config = await loadConfig() as Config;
|
|
185
|
+
|
|
186
|
+
if (!config.gateway?.name) {
|
|
187
|
+
console.log('No gateway configured');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const name = config.gateway.name;
|
|
192
|
+
delete config.gateway.name;
|
|
193
|
+
delete config.gateway.secret;
|
|
194
|
+
if (config.gateway.api) {
|
|
195
|
+
delete config.gateway.api.secret;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
await saveConfig(config);
|
|
199
|
+
console.log(`Gateway profile "${name}" removed`);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
handleError(error);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Teleport command
|
|
206
|
+
gateway
|
|
207
|
+
.command('teleport')
|
|
208
|
+
.description('Transfer auth state to remote gateway')
|
|
209
|
+
.argument('<url>', 'Remote gateway URL (e.g., https://my-gateway.com)')
|
|
210
|
+
.option('--service <service>', 'Service to teleport (default: all)', 'all')
|
|
211
|
+
.action(async (url: string, options) => {
|
|
212
|
+
try {
|
|
213
|
+
const config = await loadConfig() as Config;
|
|
214
|
+
|
|
215
|
+
if (!config.gateway?.secret) {
|
|
216
|
+
throw new CliError('CONFIG_ERROR', 'No gateway secret configured', 'Run: agentio gateway profile add');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
console.log(`Teleporting to ${url}...`);
|
|
220
|
+
|
|
221
|
+
// Export WhatsApp auth state
|
|
222
|
+
if (options.service === 'all' || options.service === 'whatsapp') {
|
|
223
|
+
const profiles = config.profiles.whatsapp || [];
|
|
224
|
+
|
|
225
|
+
for (const profile of profiles) {
|
|
226
|
+
console.log(` Exporting whatsapp:${profile}...`);
|
|
227
|
+
const authState = await exportWhatsAppAuthState(profile);
|
|
228
|
+
|
|
229
|
+
if (!authState) {
|
|
230
|
+
console.log(` No auth state found, skipping`);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Send to remote gateway
|
|
235
|
+
const response = await fetch(`${url}/import/whatsapp/${encodeURIComponent(profile)}`, {
|
|
236
|
+
method: 'POST',
|
|
237
|
+
headers: {
|
|
238
|
+
'Content-Type': 'application/json',
|
|
239
|
+
'Authorization': `Bearer ${config.gateway.secret}`,
|
|
240
|
+
},
|
|
241
|
+
body: JSON.stringify(authState),
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (!response.ok) {
|
|
245
|
+
const error = await response.text();
|
|
246
|
+
throw new CliError('API_ERROR', `Failed to import to remote: ${error}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
console.log(` Transferred successfully`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
console.log('\nTeleport complete!');
|
|
254
|
+
console.log('You can now stop the local gateway and use the remote one.');
|
|
255
|
+
} catch (error) {
|
|
256
|
+
handleError(error);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|