@plosson/agentio 0.1.27 → 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 +207 -176
- 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 +12 -51
- package/src/commands/gchat.ts +8 -49
- package/src/commands/gmail.ts +10 -51
- 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 +2 -0
- package/src/services/discourse/client.ts +16 -2
- package/src/services/gchat/client.ts +23 -1
- package/src/services/gmail/client.ts +16 -1
- 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/service.ts +21 -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
|
+
```
|
|
4
49
|
|
|
5
|
-
|
|
50
|
+
See [`examples/daily-briefing/`](./examples/daily-briefing) for the complete working example.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
6
53
|
|
|
7
54
|
**macOS / Linux:**
|
|
8
55
|
```bash
|
|
@@ -14,150 +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
|
-
| RSS | Available | `articles`, `get`, `info` |
|
|
76
|
-
| Linear | Planned | - |
|
|
96
|
+
# Add Slack webhook
|
|
97
|
+
agentio slack profile add
|
|
77
98
|
|
|
78
|
-
|
|
99
|
+
# Add any other services you need...
|
|
100
|
+
```
|
|
79
101
|
|
|
80
|
-
###
|
|
102
|
+
### 2. Export your config
|
|
81
103
|
|
|
82
104
|
```bash
|
|
83
|
-
|
|
84
|
-
|
|
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
|
+
```
|
|
85
112
|
|
|
86
|
-
|
|
87
|
-
agentio gmail list --limit 10
|
|
113
|
+
All credentials are encrypted with AES-256-GCM. The export file is useless without the key.
|
|
88
114
|
|
|
89
|
-
|
|
90
|
-
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.
|
|
91
116
|
|
|
92
|
-
|
|
93
|
-
|
|
117
|
+
### 3. Add to GitHub Secrets
|
|
118
|
+
|
|
119
|
+
| Secret | Value |
|
|
120
|
+
|--------|-------|
|
|
121
|
+
| `AGENTIO_CONFIG` | Base64-encoded contents of `agentio.config` |
|
|
122
|
+
| `AGENTIO_KEY` | The encryption key from export |
|
|
94
123
|
|
|
95
|
-
|
|
96
|
-
|
|
124
|
+
```bash
|
|
125
|
+
# Get base64 for GitHub secret
|
|
126
|
+
cat agentio.config | base64
|
|
127
|
+
```
|
|
97
128
|
|
|
98
|
-
|
|
99
|
-
echo "Message body" | agentio gmail send --to user@example.com --subject "Hello"
|
|
129
|
+
### 4. Use in your workflow
|
|
100
130
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
131
|
+
```yaml
|
|
132
|
+
env:
|
|
133
|
+
AGENTIO_CONFIG: ${{ secrets.AGENTIO_CONFIG }}
|
|
134
|
+
AGENTIO_KEY: ${{ secrets.AGENTIO_KEY }}
|
|
104
135
|
|
|
105
|
-
|
|
106
|
-
agentio
|
|
107
|
-
agentio gmail
|
|
136
|
+
steps:
|
|
137
|
+
- run: agentio config import # Auto-detects env vars
|
|
138
|
+
- run: agentio gmail list --limit 10
|
|
108
139
|
```
|
|
109
140
|
|
|
110
|
-
|
|
141
|
+
Done. Your agent can now access all your services securely in CI/CD.
|
|
111
142
|
|
|
112
|
-
|
|
113
|
-
# Set up bot profile (interactive wizard)
|
|
114
|
-
agentio telegram profile add
|
|
143
|
+
## Services
|
|
115
144
|
|
|
116
|
-
|
|
117
|
-
|
|
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` |
|
|
118
154
|
|
|
119
|
-
|
|
120
|
-
agentio telegram send --parse-mode markdown "**Bold** and _italic_"
|
|
121
|
-
```
|
|
155
|
+
## Usage Examples
|
|
122
156
|
|
|
123
|
-
|
|
157
|
+
<details>
|
|
158
|
+
<summary><strong>Gmail</strong></summary>
|
|
124
159
|
|
|
125
160
|
```bash
|
|
126
|
-
#
|
|
127
|
-
agentio
|
|
161
|
+
# List recent emails
|
|
162
|
+
agentio gmail list --limit 10
|
|
128
163
|
|
|
129
|
-
#
|
|
130
|
-
agentio
|
|
164
|
+
# Search
|
|
165
|
+
agentio gmail search --query "from:boss@company.com is:unread"
|
|
131
166
|
|
|
132
|
-
#
|
|
133
|
-
agentio
|
|
167
|
+
# Get specific email
|
|
168
|
+
agentio gmail get <message-id>
|
|
134
169
|
|
|
135
|
-
#
|
|
136
|
-
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"
|
|
172
|
+
|
|
173
|
+
# Download attachments
|
|
174
|
+
agentio gmail attachment <message-id> --output ./downloads
|
|
137
175
|
|
|
138
|
-
#
|
|
139
|
-
agentio
|
|
176
|
+
# Export as PDF
|
|
177
|
+
agentio gmail export <message-id> --output email.pdf
|
|
140
178
|
```
|
|
141
179
|
|
|
142
|
-
|
|
180
|
+
</details>
|
|
143
181
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
agentio slack profile add
|
|
182
|
+
<details>
|
|
183
|
+
<summary><strong>Slack</strong></summary>
|
|
147
184
|
|
|
185
|
+
```bash
|
|
148
186
|
# Send message
|
|
149
|
-
agentio slack send "
|
|
187
|
+
agentio slack send "Deployment complete ✓"
|
|
150
188
|
|
|
151
|
-
# Send Block Kit message
|
|
189
|
+
# Send rich Block Kit message
|
|
152
190
|
agentio slack send --json blocks.json
|
|
153
191
|
```
|
|
154
192
|
|
|
155
|
-
|
|
193
|
+
</details>
|
|
194
|
+
|
|
195
|
+
<details>
|
|
196
|
+
<summary><strong>Telegram</strong></summary>
|
|
156
197
|
|
|
157
198
|
```bash
|
|
158
|
-
#
|
|
159
|
-
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
|
+
```
|
|
160
205
|
|
|
206
|
+
</details>
|
|
207
|
+
|
|
208
|
+
<details>
|
|
209
|
+
<summary><strong>JIRA</strong></summary>
|
|
210
|
+
|
|
211
|
+
```bash
|
|
161
212
|
# List projects
|
|
162
213
|
agentio jira projects
|
|
163
214
|
|
|
@@ -168,39 +219,38 @@ agentio jira search --jql "assignee = currentUser() AND status != Done"
|
|
|
168
219
|
# Get issue details
|
|
169
220
|
agentio jira get PROJ-123
|
|
170
221
|
|
|
171
|
-
# Add
|
|
172
|
-
agentio jira comment PROJ-123 "
|
|
173
|
-
|
|
174
|
-
# View available transitions
|
|
175
|
-
agentio jira transitions PROJ-123
|
|
222
|
+
# Add comment
|
|
223
|
+
agentio jira comment PROJ-123 "Automated update from CI"
|
|
176
224
|
|
|
177
|
-
# Transition
|
|
225
|
+
# Transition issue
|
|
226
|
+
agentio jira transitions PROJ-123 # see available transitions
|
|
178
227
|
agentio jira transition PROJ-123 <transition-id>
|
|
179
228
|
```
|
|
180
229
|
|
|
181
|
-
|
|
230
|
+
</details>
|
|
231
|
+
|
|
232
|
+
<details>
|
|
233
|
+
<summary><strong>RSS</strong></summary>
|
|
182
234
|
|
|
183
235
|
```bash
|
|
184
|
-
# List articles
|
|
185
|
-
agentio rss articles https://simonwillison.net
|
|
186
|
-
agentio rss articles https://steipete.me --limit 5
|
|
236
|
+
# List articles (auto-discovers feed URL)
|
|
237
|
+
agentio rss articles https://simonwillison.net --limit 10
|
|
187
238
|
|
|
188
239
|
# Filter by date
|
|
189
|
-
agentio rss articles https://blog.
|
|
240
|
+
agentio rss articles https://blog.example.com --since 2025-01-01
|
|
190
241
|
|
|
191
|
-
# Get
|
|
192
|
-
agentio rss info https://kau.sh
|
|
193
|
-
|
|
194
|
-
# Get a specific article
|
|
242
|
+
# Get full article
|
|
195
243
|
agentio rss get https://simonwillison.net <article-url>
|
|
196
244
|
```
|
|
197
245
|
|
|
246
|
+
</details>
|
|
247
|
+
|
|
198
248
|
## Multi-Profile Support
|
|
199
249
|
|
|
200
|
-
|
|
250
|
+
Manage multiple accounts per service:
|
|
201
251
|
|
|
202
252
|
```bash
|
|
203
|
-
# Add profiles
|
|
253
|
+
# Add named profiles
|
|
204
254
|
agentio gmail profile add --profile work
|
|
205
255
|
agentio gmail profile add --profile personal
|
|
206
256
|
|
|
@@ -208,90 +258,71 @@ agentio gmail profile add --profile personal
|
|
|
208
258
|
agentio gmail list --profile work
|
|
209
259
|
```
|
|
210
260
|
|
|
211
|
-
##
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
###
|
|
216
|
-
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
|
226
286
|
```
|
|
227
287
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
```bash
|
|
231
|
-
# List marketplaces and plugins from agentio.json
|
|
232
|
-
agentio claude list
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
agentio claude update https://github.com/plosson/agentio
|
|
290
|
+
```markdown
|
|
291
|
+
# Daily Email Briefing
|
|
237
292
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
|
241
297
|
```
|
|
242
298
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
Projects can define marketplaces and plugins in an `agentio.json` file:
|
|
299
|
+
See [`examples/daily-briefing/`](./examples/daily-briefing) for the complete working example.
|
|
246
300
|
|
|
247
|
-
|
|
248
|
-
{
|
|
249
|
-
"marketplaces": [
|
|
250
|
-
"https://github.com/plosson/agentio"
|
|
251
|
-
],
|
|
252
|
-
"plugins": [
|
|
253
|
-
"agentio-gmail@agentio",
|
|
254
|
-
"agentio-rss@agentio"
|
|
255
|
-
]
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
## Design
|
|
260
|
-
|
|
261
|
-
agentio is designed for LLM consumption:
|
|
262
|
-
|
|
263
|
-
- **Structured output**: Human-readable text output optimized for LLM parsing
|
|
264
|
-
- **Clear errors**: Error messages written to stderr with suggestions
|
|
265
|
-
- **Stdin support**: Pipe content to commands that accept body text
|
|
266
|
-
- **Multi-profile**: Manage multiple accounts per service
|
|
267
|
-
|
|
268
|
-
## Configuration
|
|
269
|
-
|
|
270
|
-
Configuration is stored in `~/.config/agentio/`:
|
|
271
|
-
|
|
272
|
-
- `config.json` - Profile names and defaults
|
|
273
|
-
- `tokens.enc` - Encrypted credentials (AES-256-GCM)
|
|
301
|
+
## Claude Code Integration
|
|
274
302
|
|
|
275
|
-
|
|
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.
|
|
276
304
|
|
|
277
|
-
|
|
305
|
+
**Install skills:**
|
|
278
306
|
|
|
279
307
|
```bash
|
|
280
|
-
|
|
281
|
-
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
|
+
```
|
|
282
312
|
|
|
283
|
-
|
|
284
|
-
agentio config export --output backup.config
|
|
313
|
+
**Then in Claude Code:**
|
|
285
314
|
|
|
286
|
-
|
|
287
|
-
agentio config import agentio.config --key <encryption-key>
|
|
315
|
+
> "Summarize my unread emails and post a summary to Slack"
|
|
288
316
|
|
|
289
|
-
|
|
290
|
-
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.
|
|
291
318
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
|
295
326
|
|
|
296
327
|
## License
|
|
297
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
|
|