@plosson/agentio 0.1.26 → 0.1.28
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 +213 -166
- package/package.json +1 -1
- package/src/auth/jira-oauth.ts +5 -23
- package/src/commands/config.ts +33 -12
- package/src/commands/discourse.ts +209 -0
- package/src/commands/gchat.ts +8 -49
- package/src/commands/gmail.ts +37 -58
- package/src/commands/jira.ts +12 -53
- package/src/commands/slack.ts +8 -49
- package/src/commands/status.ts +164 -0
- package/src/commands/telegram.ts +19 -60
- package/src/config/config-manager.ts +17 -1
- package/src/config/credentials.ts +2 -2
- package/src/index.ts +4 -0
- package/src/services/discourse/client.ts +287 -0
- package/src/services/gchat/client.ts +23 -1
- package/src/services/gmail/client.ts +145 -44
- package/src/services/jira/client.ts +44 -1
- package/src/services/slack/client.ts +9 -1
- package/src/services/telegram/client.ts +14 -1
- package/src/types/config.ts +3 -1
- package/src/types/discourse.ts +62 -0
- package/src/types/gmail.ts +1 -0
- package/src/types/service.ts +21 -0
- package/src/utils/output.ts +76 -0
- package/src/utils/profile-commands.ts +90 -0
package/README.md
CHANGED
|
@@ -1,8 +1,55 @@
|
|
|
1
1
|
# agentio
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Run LLM agent workflows in GitHub Actions. No servers. No Zapier. Just cron.**
|
|
4
|
+
|
|
5
|
+
agentio is a CLI that lets LLM agents interact with Gmail, Slack, JIRA, Telegram, Google Chat, and RSS feeds. Designed for CI/CD pipelines and scheduled automation.
|
|
6
|
+
|
|
7
|
+
## Why agentio?
|
|
8
|
+
|
|
9
|
+
You want your AI agent to:
|
|
10
|
+
- Send a daily Slack summary of unread emails
|
|
11
|
+
- Monitor RSS feeds and post to Telegram
|
|
12
|
+
- Update JIRA tickets based on email threads
|
|
13
|
+
- Run on a schedule, without managing servers
|
|
14
|
+
|
|
15
|
+
**agentio makes this trivial.** Authenticate once locally, export your config as a single encrypted file, and run anywhere — GitHub Actions, GitLab CI, or any CI/CD platform.
|
|
16
|
+
|
|
17
|
+
## Quick Example
|
|
18
|
+
|
|
19
|
+
A scheduled workflow that reads your emails, has Claude summarize them, and posts to Slack:
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
# .github/workflows/daily-briefing.yml
|
|
23
|
+
name: Daily Briefing
|
|
24
|
+
on:
|
|
25
|
+
schedule:
|
|
26
|
+
- cron: '0 7 * * 1-5'
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
briefing:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
env:
|
|
32
|
+
AGENTIO_CONFIG: ${{ secrets.AGENTIO_CONFIG }}
|
|
33
|
+
AGENTIO_KEY: ${{ secrets.AGENTIO_KEY }}
|
|
34
|
+
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- run: curl -LsSf https://agentio.work/install | sh
|
|
38
|
+
- run: npm install -g @anthropic-ai/claude-code
|
|
39
|
+
- run: agentio config import && agentio claude install
|
|
40
|
+
- run: claude -p "$(cat prompt.md)" --max-turns 30 --dangerously-skip-permissions
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```markdown
|
|
44
|
+
# prompt.md
|
|
45
|
+
Fetch my unread emails from the last 24 hours using agentio-gmail.
|
|
46
|
+
Summarize them by urgency and sender.
|
|
47
|
+
Post the summary to Slack using agentio-slack.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
See [`examples/daily-briefing/`](./examples/daily-briefing) for the complete working example.
|
|
4
51
|
|
|
5
|
-
##
|
|
52
|
+
## Install
|
|
6
53
|
|
|
7
54
|
**macOS / Linux:**
|
|
8
55
|
```bash
|
|
@@ -14,149 +61,154 @@ curl -LsSf https://agentio.work/install | sh
|
|
|
14
61
|
iwr -useb https://agentio.work/install.ps1 | iex
|
|
15
62
|
```
|
|
16
63
|
|
|
17
|
-
## Update
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
agentio update
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Alternative Installation Methods
|
|
24
|
-
|
|
25
64
|
<details>
|
|
26
|
-
<summary>Homebrew
|
|
65
|
+
<summary><strong>Other methods</strong> (Homebrew, Scoop, npm)</summary>
|
|
27
66
|
|
|
67
|
+
**Homebrew (macOS/Linux):**
|
|
28
68
|
```bash
|
|
29
69
|
brew tap plosson/agentio
|
|
30
70
|
brew install agentio
|
|
31
71
|
```
|
|
32
|
-
</details>
|
|
33
|
-
|
|
34
|
-
<details>
|
|
35
|
-
<summary>Scoop (Windows)</summary>
|
|
36
72
|
|
|
73
|
+
**Scoop (Windows):**
|
|
37
74
|
```powershell
|
|
38
75
|
scoop bucket add agentio https://github.com/plosson/scoop-agentio
|
|
39
76
|
scoop install agentio
|
|
40
77
|
```
|
|
41
|
-
</details>
|
|
42
|
-
|
|
43
|
-
<details>
|
|
44
|
-
<summary>npm / bun</summary>
|
|
45
78
|
|
|
79
|
+
**npm / bun:**
|
|
46
80
|
```bash
|
|
47
|
-
# Run directly
|
|
48
|
-
bunx @plosson/agentio --help
|
|
49
81
|
npx @plosson/agentio --help
|
|
50
|
-
|
|
51
|
-
# Global install
|
|
52
|
-
bun add -g @plosson/agentio
|
|
82
|
+
# or global install
|
|
53
83
|
npm install -g @plosson/agentio
|
|
54
84
|
```
|
|
85
|
+
|
|
55
86
|
</details>
|
|
56
87
|
|
|
57
|
-
|
|
58
|
-
<summary>Direct binary download</summary>
|
|
88
|
+
## Setup: Authenticate Once, Run Anywhere
|
|
59
89
|
|
|
60
|
-
|
|
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>
|
|
90
|
+
### 1. Authenticate locally
|
|
65
91
|
|
|
66
|
-
|
|
92
|
+
```bash
|
|
93
|
+
# Add your Gmail account (opens browser for OAuth)
|
|
94
|
+
agentio gmail profile add
|
|
67
95
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
| Gmail | Available | `list`, `get`, `search`, `send`, `reply`, `archive`, `mark`, `attachment`, `export` |
|
|
71
|
-
| Telegram | Available | `send` |
|
|
72
|
-
| Google Chat | Available | `send`, `list`, `get` |
|
|
73
|
-
| Slack | Available | `send` |
|
|
74
|
-
| JIRA | Available | `projects`, `search`, `get`, `comment`, `transitions`, `transition` |
|
|
75
|
-
| Linear | Planned | - |
|
|
96
|
+
# Add Slack webhook
|
|
97
|
+
agentio slack profile add
|
|
76
98
|
|
|
77
|
-
|
|
99
|
+
# Add any other services you need...
|
|
100
|
+
```
|
|
78
101
|
|
|
79
|
-
###
|
|
102
|
+
### 2. Export your config
|
|
80
103
|
|
|
81
104
|
```bash
|
|
82
|
-
|
|
83
|
-
|
|
105
|
+
agentio config export
|
|
106
|
+
# Outputs:
|
|
107
|
+
# ✓ Exported to agentio.config
|
|
108
|
+
# ✓ Encryption key: abc123...
|
|
109
|
+
#
|
|
110
|
+
# Store BOTH in your CI/CD secrets!
|
|
111
|
+
```
|
|
84
112
|
|
|
85
|
-
|
|
86
|
-
agentio gmail list --limit 10
|
|
113
|
+
All credentials are encrypted with AES-256-GCM. The export file is useless without the key.
|
|
87
114
|
|
|
88
|
-
|
|
89
|
-
agentio gmail search --query "from:boss@company.com is:unread"
|
|
115
|
+
**Note:** GitHub secrets are limited to 48KB per secret. Only add the profiles you need for your workflow to keep the exported config small.
|
|
90
116
|
|
|
91
|
-
|
|
92
|
-
agentio gmail get <message-id>
|
|
117
|
+
### 3. Add to GitHub Secrets
|
|
93
118
|
|
|
94
|
-
|
|
95
|
-
|
|
119
|
+
| Secret | Value |
|
|
120
|
+
|--------|-------|
|
|
121
|
+
| `AGENTIO_CONFIG` | Base64-encoded contents of `agentio.config` |
|
|
122
|
+
| `AGENTIO_KEY` | The encryption key from export |
|
|
96
123
|
|
|
97
|
-
|
|
98
|
-
|
|
124
|
+
```bash
|
|
125
|
+
# Get base64 for GitHub secret
|
|
126
|
+
cat agentio.config | base64
|
|
127
|
+
```
|
|
99
128
|
|
|
100
|
-
|
|
101
|
-
agentio gmail attachment <message-id>
|
|
102
|
-
agentio gmail attachment <message-id> --name "document.pdf" --output ./downloads
|
|
129
|
+
### 4. Use in your workflow
|
|
103
130
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
131
|
+
```yaml
|
|
132
|
+
env:
|
|
133
|
+
AGENTIO_CONFIG: ${{ secrets.AGENTIO_CONFIG }}
|
|
134
|
+
AGENTIO_KEY: ${{ secrets.AGENTIO_KEY }}
|
|
135
|
+
|
|
136
|
+
steps:
|
|
137
|
+
- run: agentio config import # Auto-detects env vars
|
|
138
|
+
- run: agentio gmail list --limit 10
|
|
107
139
|
```
|
|
108
140
|
|
|
109
|
-
|
|
141
|
+
Done. Your agent can now access all your services securely in CI/CD.
|
|
110
142
|
|
|
111
|
-
|
|
112
|
-
# Set up bot profile (interactive wizard)
|
|
113
|
-
agentio telegram profile add
|
|
143
|
+
## Services
|
|
114
144
|
|
|
115
|
-
|
|
116
|
-
|
|
145
|
+
| Service | Auth | Commands |
|
|
146
|
+
|---------|------|----------|
|
|
147
|
+
| **Gmail** | OAuth | `list`, `get`, `search`, `send`, `reply`, `archive`, `mark`, `attachment`, `export` |
|
|
148
|
+
| **Slack** | Webhook | `send` |
|
|
149
|
+
| **Telegram** | Bot Token | `send` |
|
|
150
|
+
| **Google Chat** | Webhook/OAuth | `send`, `list`, `get` |
|
|
151
|
+
| **JIRA** | OAuth | `projects`, `search`, `get`, `comment`, `transitions`, `transition` |
|
|
152
|
+
| **Discourse** | API Key | `list`, `get`, `categories` |
|
|
153
|
+
| **RSS** | None | `articles`, `get`, `info` |
|
|
117
154
|
|
|
118
|
-
|
|
119
|
-
agentio telegram send --parse-mode markdown "**Bold** and _italic_"
|
|
120
|
-
```
|
|
155
|
+
## Usage Examples
|
|
121
156
|
|
|
122
|
-
|
|
157
|
+
<details>
|
|
158
|
+
<summary><strong>Gmail</strong></summary>
|
|
123
159
|
|
|
124
160
|
```bash
|
|
125
|
-
#
|
|
126
|
-
agentio
|
|
161
|
+
# List recent emails
|
|
162
|
+
agentio gmail list --limit 10
|
|
163
|
+
|
|
164
|
+
# Search
|
|
165
|
+
agentio gmail search --query "from:boss@company.com is:unread"
|
|
127
166
|
|
|
128
|
-
#
|
|
129
|
-
agentio
|
|
167
|
+
# Get specific email
|
|
168
|
+
agentio gmail get <message-id>
|
|
130
169
|
|
|
131
|
-
# Send
|
|
132
|
-
agentio
|
|
170
|
+
# Send (body from stdin works great with LLMs)
|
|
171
|
+
echo "Generated by my agent" | agentio gmail send --to user@example.com --subject "Daily Report"
|
|
133
172
|
|
|
134
|
-
#
|
|
135
|
-
agentio
|
|
173
|
+
# Download attachments
|
|
174
|
+
agentio gmail attachment <message-id> --output ./downloads
|
|
136
175
|
|
|
137
|
-
#
|
|
138
|
-
agentio
|
|
176
|
+
# Export as PDF
|
|
177
|
+
agentio gmail export <message-id> --output email.pdf
|
|
139
178
|
```
|
|
140
179
|
|
|
141
|
-
|
|
180
|
+
</details>
|
|
142
181
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
agentio slack profile add
|
|
182
|
+
<details>
|
|
183
|
+
<summary><strong>Slack</strong></summary>
|
|
146
184
|
|
|
185
|
+
```bash
|
|
147
186
|
# Send message
|
|
148
|
-
agentio slack send "
|
|
187
|
+
agentio slack send "Deployment complete ✓"
|
|
149
188
|
|
|
150
|
-
# Send Block Kit message
|
|
189
|
+
# Send rich Block Kit message
|
|
151
190
|
agentio slack send --json blocks.json
|
|
152
191
|
```
|
|
153
192
|
|
|
154
|
-
|
|
193
|
+
</details>
|
|
194
|
+
|
|
195
|
+
<details>
|
|
196
|
+
<summary><strong>Telegram</strong></summary>
|
|
155
197
|
|
|
156
198
|
```bash
|
|
157
|
-
#
|
|
158
|
-
agentio
|
|
199
|
+
# Send to channel
|
|
200
|
+
agentio telegram send "Alert: New items found"
|
|
201
|
+
|
|
202
|
+
# With markdown
|
|
203
|
+
agentio telegram send --parse-mode markdown "**Bold** and _italic_"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
</details>
|
|
207
|
+
|
|
208
|
+
<details>
|
|
209
|
+
<summary><strong>JIRA</strong></summary>
|
|
159
210
|
|
|
211
|
+
```bash
|
|
160
212
|
# List projects
|
|
161
213
|
agentio jira projects
|
|
162
214
|
|
|
@@ -167,115 +219,110 @@ agentio jira search --jql "assignee = currentUser() AND status != Done"
|
|
|
167
219
|
# Get issue details
|
|
168
220
|
agentio jira get PROJ-123
|
|
169
221
|
|
|
170
|
-
# Add
|
|
171
|
-
agentio jira comment PROJ-123 "
|
|
172
|
-
|
|
173
|
-
# View available transitions
|
|
174
|
-
agentio jira transitions PROJ-123
|
|
222
|
+
# Add comment
|
|
223
|
+
agentio jira comment PROJ-123 "Automated update from CI"
|
|
175
224
|
|
|
176
|
-
# Transition
|
|
225
|
+
# Transition issue
|
|
226
|
+
agentio jira transitions PROJ-123 # see available transitions
|
|
177
227
|
agentio jira transition PROJ-123 <transition-id>
|
|
178
228
|
```
|
|
179
229
|
|
|
180
|
-
|
|
230
|
+
</details>
|
|
181
231
|
|
|
182
|
-
|
|
232
|
+
<details>
|
|
233
|
+
<summary><strong>RSS</strong></summary>
|
|
183
234
|
|
|
184
235
|
```bash
|
|
185
|
-
#
|
|
186
|
-
agentio
|
|
187
|
-
agentio gmail profile add --profile personal
|
|
236
|
+
# List articles (auto-discovers feed URL)
|
|
237
|
+
agentio rss articles https://simonwillison.net --limit 10
|
|
188
238
|
|
|
189
|
-
#
|
|
190
|
-
agentio
|
|
239
|
+
# Filter by date
|
|
240
|
+
agentio rss articles https://blog.example.com --since 2025-01-01
|
|
241
|
+
|
|
242
|
+
# Get full article
|
|
243
|
+
agentio rss get https://simonwillison.net <article-url>
|
|
191
244
|
```
|
|
192
245
|
|
|
193
|
-
|
|
246
|
+
</details>
|
|
194
247
|
|
|
195
|
-
|
|
248
|
+
## Multi-Profile Support
|
|
196
249
|
|
|
197
|
-
|
|
250
|
+
Manage multiple accounts per service:
|
|
198
251
|
|
|
199
252
|
```bash
|
|
200
|
-
#
|
|
201
|
-
agentio
|
|
202
|
-
|
|
203
|
-
# Or install from a full GitHub URL
|
|
204
|
-
agentio claude plugin install https://github.com/plosson/agentio
|
|
205
|
-
|
|
206
|
-
# Install to a specific directory
|
|
207
|
-
agentio claude plugin install plosson/agentio -d ~/myproject
|
|
208
|
-
|
|
209
|
-
# Install only specific components (skills, commands, hooks, agents)
|
|
210
|
-
agentio claude plugin install plosson/agentio --skills
|
|
211
|
-
agentio claude plugin install plosson/agentio --agents
|
|
212
|
-
|
|
213
|
-
# Force reinstall if already exists
|
|
214
|
-
agentio claude plugin install plosson/agentio -f
|
|
253
|
+
# Add named profiles
|
|
254
|
+
agentio gmail profile add --profile work
|
|
255
|
+
agentio gmail profile add --profile personal
|
|
215
256
|
|
|
216
|
-
#
|
|
217
|
-
agentio
|
|
257
|
+
# Use specific profile
|
|
258
|
+
agentio gmail list --profile work
|
|
218
259
|
```
|
|
219
260
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
261
|
+
## Workflow Examples
|
|
262
|
+
|
|
263
|
+
The [`examples/`](./examples) folder contains ready-to-use workflows. Here's how it works:
|
|
264
|
+
|
|
265
|
+
### Daily Email Briefing → Slack
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
268
|
+
name: Daily Briefing
|
|
269
|
+
on:
|
|
270
|
+
schedule:
|
|
271
|
+
- cron: '0 7 * * 1-5' # Weekdays at 7 AM UTC
|
|
272
|
+
|
|
273
|
+
jobs:
|
|
274
|
+
briefing:
|
|
275
|
+
runs-on: ubuntu-latest
|
|
276
|
+
env:
|
|
277
|
+
AGENTIO_CONFIG: ${{ secrets.AGENTIO_CONFIG }}
|
|
278
|
+
AGENTIO_KEY: ${{ secrets.AGENTIO_KEY }}
|
|
279
|
+
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
280
|
+
steps:
|
|
281
|
+
- uses: actions/checkout@v4
|
|
282
|
+
- run: curl -LsSf https://agentio.work/install | sh
|
|
283
|
+
- run: npm install -g @anthropic-ai/claude-code
|
|
284
|
+
- run: agentio config import && agentio claude install
|
|
285
|
+
- run: claude -p "$(cat prompt.md)" --max-turns 30 --dangerously-skip-permissions
|
|
230
286
|
```
|
|
231
287
|
|
|
232
|
-
|
|
288
|
+
The magic is in `prompt.md` — Claude reads your emails via the agentio plugin, analyzes them, and posts a summary to Slack:
|
|
233
289
|
|
|
234
|
-
|
|
290
|
+
```markdown
|
|
291
|
+
# Daily Email Briefing
|
|
235
292
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
293
|
+
1. Fetch unread emails from the last 24 hours using agentio-gmail
|
|
294
|
+
2. Categorize by urgency: Urgent / Important / FYI
|
|
295
|
+
3. Generate a morning briefing with one-line summaries
|
|
296
|
+
4. Send to Slack using agentio-slack
|
|
239
297
|
```
|
|
240
298
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
## Design
|
|
244
|
-
|
|
245
|
-
agentio is designed for LLM consumption:
|
|
246
|
-
|
|
247
|
-
- **Structured output**: Human-readable text output optimized for LLM parsing
|
|
248
|
-
- **Clear errors**: Error messages written to stderr with suggestions
|
|
249
|
-
- **Stdin support**: Pipe content to commands that accept body text
|
|
250
|
-
- **Multi-profile**: Manage multiple accounts per service
|
|
251
|
-
|
|
252
|
-
## Configuration
|
|
299
|
+
See [`examples/daily-briefing/`](./examples/daily-briefing) for the complete working example.
|
|
253
300
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
- `config.json` - Profile names and defaults
|
|
257
|
-
- `tokens.enc` - Encrypted credentials (AES-256-GCM)
|
|
301
|
+
## Claude Code Integration
|
|
258
302
|
|
|
259
|
-
|
|
303
|
+
agentio includes skills for [Claude Code](https://claude.ai/download) that let Claude directly read your email, post to Slack, or query JIRA during conversations.
|
|
260
304
|
|
|
261
|
-
|
|
305
|
+
**Install skills:**
|
|
262
306
|
|
|
263
307
|
```bash
|
|
264
|
-
|
|
265
|
-
agentio
|
|
308
|
+
agentio claude install https://github.com/plosson/agentio # marketplace
|
|
309
|
+
agentio claude install agentio-gmail@agentio # Gmail skill
|
|
310
|
+
agentio claude install agentio-jira@agentio # JIRA skill
|
|
311
|
+
```
|
|
266
312
|
|
|
267
|
-
|
|
268
|
-
agentio config export --output backup.config
|
|
313
|
+
**Then in Claude Code:**
|
|
269
314
|
|
|
270
|
-
|
|
271
|
-
agentio config import agentio.config --key <encryption-key>
|
|
315
|
+
> "Summarize my unread emails and post a summary to Slack"
|
|
272
316
|
|
|
273
|
-
|
|
274
|
-
AGENTIO_KEY=<key> agentio config import agentio.config
|
|
317
|
+
Claude uses the installed skills to fetch emails and send the summary — no manual commands needed.
|
|
275
318
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
319
|
+
## Design Principles
|
|
320
|
+
|
|
321
|
+
- **Structured output** — Text output optimized for LLM parsing
|
|
322
|
+
- **Stdin support** — Pipe content to commands (`echo "msg" | agentio slack send`)
|
|
323
|
+
- **Single config export** — One encrypted file + key = full portability
|
|
324
|
+
- **Multi-profile** — Multiple accounts per service
|
|
325
|
+
- **No runtime dependencies** — Single binary, runs anywhere
|
|
279
326
|
|
|
280
327
|
## License
|
|
281
328
|
|
package/package.json
CHANGED
package/src/auth/jira-oauth.ts
CHANGED
|
@@ -9,11 +9,11 @@ const ATLASSIAN_RESOURCES_URL = 'https://api.atlassian.com/oauth/token/accessibl
|
|
|
9
9
|
const JIRA_SCOPES = [
|
|
10
10
|
'read:jira-work', // Read projects, issues
|
|
11
11
|
'write:jira-work', // Add comments, change status
|
|
12
|
+
'read:me', // Read current user info
|
|
12
13
|
'offline_access', // Get refresh tokens
|
|
13
14
|
];
|
|
14
15
|
|
|
15
|
-
const
|
|
16
|
-
const PORT_RANGE_END = 3010;
|
|
16
|
+
const OAUTH_PORT = 9999;
|
|
17
17
|
|
|
18
18
|
export interface JiraOAuthResult {
|
|
19
19
|
accessToken: string;
|
|
@@ -31,23 +31,6 @@ export interface AtlassianSite {
|
|
|
31
31
|
avatarUrl?: string;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
async function findAvailablePort(): Promise<number> {
|
|
35
|
-
for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port++) {
|
|
36
|
-
try {
|
|
37
|
-
await new Promise<void>((resolve, reject) => {
|
|
38
|
-
const server = createServer();
|
|
39
|
-
server.listen(port, () => {
|
|
40
|
-
server.close(() => resolve());
|
|
41
|
-
});
|
|
42
|
-
server.on('error', reject);
|
|
43
|
-
});
|
|
44
|
-
return port;
|
|
45
|
-
} catch {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
throw new Error(`No available port found in range ${PORT_RANGE_START}-${PORT_RANGE_END}`);
|
|
50
|
-
}
|
|
51
34
|
|
|
52
35
|
async function exchangeCodeForTokens(
|
|
53
36
|
code: string,
|
|
@@ -130,8 +113,7 @@ export async function refreshJiraToken(
|
|
|
130
113
|
export async function performJiraOAuthFlow(
|
|
131
114
|
selectSite?: (sites: AtlassianSite[]) => Promise<AtlassianSite>
|
|
132
115
|
): Promise<JiraOAuthResult> {
|
|
133
|
-
const
|
|
134
|
-
const redirectUri = `http://localhost:${port}/callback`;
|
|
116
|
+
const redirectUri = `http://localhost:${OAUTH_PORT}/callback`;
|
|
135
117
|
|
|
136
118
|
const state = Math.random().toString(36).substring(2);
|
|
137
119
|
const authUrl = new URL(ATLASSIAN_AUTH_URL);
|
|
@@ -152,7 +134,7 @@ export async function performJiraOAuthFlow(
|
|
|
152
134
|
}, 5 * 60 * 1000);
|
|
153
135
|
|
|
154
136
|
server = createServer(async (req, res) => {
|
|
155
|
-
const url = new URL(req.url || '', `http://localhost:${
|
|
137
|
+
const url = new URL(req.url || '', `http://localhost:${OAUTH_PORT}`);
|
|
156
138
|
|
|
157
139
|
if (url.pathname !== '/callback') {
|
|
158
140
|
res.writeHead(404);
|
|
@@ -230,7 +212,7 @@ export async function performJiraOAuthFlow(
|
|
|
230
212
|
}
|
|
231
213
|
});
|
|
232
214
|
|
|
233
|
-
server.listen(
|
|
215
|
+
server.listen(OAUTH_PORT, () => {
|
|
234
216
|
console.error(`\nOpening browser for Atlassian authorization...`);
|
|
235
217
|
console.error(`If browser doesn't open, visit:\n${authUrl.toString()}\n`);
|
|
236
218
|
|
package/src/commands/config.ts
CHANGED
|
@@ -112,8 +112,8 @@ export function registerConfigCommands(program: Command): void {
|
|
|
112
112
|
|
|
113
113
|
config
|
|
114
114
|
.command('import')
|
|
115
|
-
.description('Import configuration and credentials from an encrypted file')
|
|
116
|
-
.argument('
|
|
115
|
+
.description('Import configuration and credentials from an encrypted file or environment variables')
|
|
116
|
+
.argument('[file]', 'Path to the encrypted configuration file (optional if AGENTIO_CONFIG env var is set)')
|
|
117
117
|
.option('--key <key>', 'Encryption key (64 hex characters). Falls back to AGENTIO_KEY env var')
|
|
118
118
|
.option('--merge', 'Merge with existing configuration instead of replacing')
|
|
119
119
|
.action(async (file, options) => {
|
|
@@ -137,26 +137,47 @@ export function registerConfigCommands(program: Command): void {
|
|
|
137
137
|
);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (
|
|
140
|
+
let encryptedContent: string;
|
|
141
|
+
|
|
142
|
+
if (file) {
|
|
143
|
+
// Read from file
|
|
144
|
+
const filePath = file.startsWith('/') ? file : join(process.cwd(), file);
|
|
145
|
+
if (!existsSync(filePath)) {
|
|
146
|
+
throw new CliError(
|
|
147
|
+
'NOT_FOUND',
|
|
148
|
+
`File not found: ${filePath}`,
|
|
149
|
+
'Provide a valid path to the exported configuration file'
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
encryptedContent = await readFile(filePath, 'utf-8');
|
|
153
|
+
} else if (process.env.AGENTIO_CONFIG) {
|
|
154
|
+
// Decode from AGENTIO_CONFIG environment variable (base64-encoded)
|
|
155
|
+
try {
|
|
156
|
+
encryptedContent = Buffer.from(process.env.AGENTIO_CONFIG, 'base64').toString('utf-8');
|
|
157
|
+
} catch {
|
|
158
|
+
throw new CliError(
|
|
159
|
+
'INVALID_PARAMS',
|
|
160
|
+
'Failed to decode AGENTIO_CONFIG',
|
|
161
|
+
'AGENTIO_CONFIG must be a valid base64-encoded string'
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
143
165
|
throw new CliError(
|
|
144
|
-
'
|
|
145
|
-
|
|
146
|
-
'Provide a
|
|
166
|
+
'INVALID_PARAMS',
|
|
167
|
+
'No configuration source provided',
|
|
168
|
+
'Provide a file path or set AGENTIO_CONFIG environment variable (base64-encoded)'
|
|
147
169
|
);
|
|
148
170
|
}
|
|
149
171
|
|
|
150
|
-
//
|
|
151
|
-
const encryptedContent = await readFile(filePath, 'utf-8');
|
|
172
|
+
// Parse the encrypted content
|
|
152
173
|
let encrypted: { iv: string; tag: string; data: string };
|
|
153
174
|
try {
|
|
154
175
|
encrypted = JSON.parse(encryptedContent);
|
|
155
176
|
} catch {
|
|
156
177
|
throw new CliError(
|
|
157
178
|
'INVALID_PARAMS',
|
|
158
|
-
'Invalid
|
|
159
|
-
'The
|
|
179
|
+
'Invalid configuration format',
|
|
180
|
+
'The configuration does not appear to be a valid agentio export'
|
|
160
181
|
);
|
|
161
182
|
}
|
|
162
183
|
|