atris 2.1.0 → 2.2.1
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/AGENT.md +35 -0
- package/AGENTS.md +46 -0
- package/GETTING_STARTED.md +2 -2
- package/PERSONA.md +5 -1
- package/README.md +16 -8
- package/atris/AGENTS.md +25 -0
- package/atris/GEMINI.md +8 -0
- package/atris/GETTING_STARTED.md +2 -2
- package/atris/atris.md +4 -3
- package/atris/features/README.md +41 -15
- package/atris/policies/LESSONS.md +1 -0
- package/atris/skills/README.md +45 -14
- package/atris/skills/atris/SKILL.md +7 -0
- package/atris/skills/autopilot/SKILL.md +9 -4
- package/atris/skills/autopilot/atris-autopilot.md +71 -0
- package/atris/skills/autopilot/hooks/stop-hook.sh +79 -0
- package/atris/skills/backend/SKILL.md +6 -1
- package/atris/skills/calendar/SKILL.md +301 -0
- package/atris/skills/clawhub/atris/SKILL.md +121 -0
- package/atris/skills/copy-editor/SKILL.md +470 -0
- package/atris/skills/design/SKILL.md +5 -1
- package/atris/skills/drive/SKILL.md +333 -0
- package/atris/skills/email-agent/SKILL.md +376 -0
- package/atris/skills/memory/SKILL.md +8 -0
- package/atris/skills/meta/SKILL.md +4 -0
- package/atris/skills/skill-improver/SKILL.md +147 -0
- package/atris/skills/writing/SKILL.md +3 -0
- package/atris/team/brainstormer.md +1 -0
- package/atris/team/executor.md +27 -0
- package/atris/team/launcher.md +1 -0
- package/atris/team/navigator.md +44 -5
- package/atris/team/validator.md +44 -3
- package/atris.md +37 -1
- package/bin/atris.js +58 -5
- package/commands/auth.js +24 -4
- package/commands/init.js +140 -17
- package/commands/integrations.js +330 -0
- package/commands/skill.js +496 -0
- package/commands/status.js +9 -1
- package/commands/sync.js +64 -19
- package/commands/workflow.js +7 -0
- package/package.json +4 -2
- package/utils/auth.js +33 -0
- package/commands/stubs.txt +0 -10
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: drive
|
|
3
|
+
description: Google Drive integration via AtrisOS API. Browse, search, read, upload files and work with Google Sheets. Use when user asks about Drive, files, docs, sheets, or spreadsheets.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
tags:
|
|
6
|
+
- drive
|
|
7
|
+
- backend
|
|
8
|
+
- google-drive
|
|
9
|
+
- sheets
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Drive Agent
|
|
13
|
+
|
|
14
|
+
> Drop this in `~/.claude/skills/drive/SKILL.md` and Claude Code becomes your Google Drive assistant.
|
|
15
|
+
|
|
16
|
+
## Bootstrap (ALWAYS Run First)
|
|
17
|
+
|
|
18
|
+
Before any Drive operation, run this bootstrap to ensure everything is set up:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
#!/bin/bash
|
|
22
|
+
set -e
|
|
23
|
+
|
|
24
|
+
# 1. Check if atris CLI is installed
|
|
25
|
+
if ! command -v atris &> /dev/null; then
|
|
26
|
+
echo "Installing atris CLI..."
|
|
27
|
+
npm install -g atris
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# 2. Check if logged in to AtrisOS
|
|
31
|
+
if [ ! -f ~/.atris/credentials.json ]; then
|
|
32
|
+
echo "Not logged in to AtrisOS."
|
|
33
|
+
echo ""
|
|
34
|
+
echo "Option 1 (interactive): Run 'atris login' and follow prompts"
|
|
35
|
+
echo "Option 2 (non-interactive): Get token from https://atris.ai/auth/cli"
|
|
36
|
+
echo " Then run: atris login --token YOUR_TOKEN"
|
|
37
|
+
echo ""
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# 3. Extract token
|
|
42
|
+
if command -v node &> /dev/null; then
|
|
43
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
44
|
+
elif command -v python3 &> /dev/null; then
|
|
45
|
+
TOKEN=$(python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.atris/credentials.json')))['token'])")
|
|
46
|
+
elif command -v jq &> /dev/null; then
|
|
47
|
+
TOKEN=$(jq -r '.token' ~/.atris/credentials.json)
|
|
48
|
+
else
|
|
49
|
+
echo "Error: Need node, python3, or jq to read credentials"
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# 4. Check Google Drive connection status
|
|
54
|
+
STATUS=$(curl -s "https://api.atris.ai/api/integrations/google-drive/status" \
|
|
55
|
+
-H "Authorization: Bearer $TOKEN")
|
|
56
|
+
|
|
57
|
+
if echo "$STATUS" | grep -q "Token expired\|Not authenticated"; then
|
|
58
|
+
echo "Token expired. Please re-authenticate:"
|
|
59
|
+
echo " Run: atris login --force"
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if command -v node &> /dev/null; then
|
|
64
|
+
CONNECTED=$(node -e "try{console.log(JSON.parse('$STATUS').connected||false)}catch(e){console.log(false)}")
|
|
65
|
+
elif command -v python3 &> /dev/null; then
|
|
66
|
+
CONNECTED=$(echo "$STATUS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('connected', False))")
|
|
67
|
+
else
|
|
68
|
+
CONNECTED=$(echo "$STATUS" | jq -r '.connected // false')
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [ "$CONNECTED" != "true" ] && [ "$CONNECTED" != "True" ]; then
|
|
72
|
+
echo "Google Drive not connected. Getting authorization URL..."
|
|
73
|
+
AUTH=$(curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/start" \
|
|
74
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
75
|
+
-H "Content-Type: application/json" \
|
|
76
|
+
-d '{}')
|
|
77
|
+
|
|
78
|
+
if command -v node &> /dev/null; then
|
|
79
|
+
URL=$(node -e "try{console.log(JSON.parse('$AUTH').auth_url||'')}catch(e){console.log('')}")
|
|
80
|
+
elif command -v python3 &> /dev/null; then
|
|
81
|
+
URL=$(echo "$AUTH" | python3 -c "import sys,json; print(json.load(sys.stdin).get('auth_url', ''))")
|
|
82
|
+
else
|
|
83
|
+
URL=$(echo "$AUTH" | jq -r '.auth_url // empty')
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
echo ""
|
|
87
|
+
echo "Open this URL to connect your Google Drive:"
|
|
88
|
+
echo "$URL"
|
|
89
|
+
echo ""
|
|
90
|
+
echo "After authorizing, run your command again."
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
echo "Ready. Google Drive is connected."
|
|
95
|
+
export ATRIS_TOKEN="$TOKEN"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## API Reference
|
|
101
|
+
|
|
102
|
+
Base: `https://api.atris.ai/api/integrations`
|
|
103
|
+
|
|
104
|
+
All requests require: `-H "Authorization: Bearer $TOKEN"`
|
|
105
|
+
|
|
106
|
+
### Get Token (after bootstrap)
|
|
107
|
+
```bash
|
|
108
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### List Files
|
|
112
|
+
```bash
|
|
113
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/files?page_size=20" \
|
|
114
|
+
-H "Authorization: Bearer $TOKEN"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**List files in a folder:**
|
|
118
|
+
```bash
|
|
119
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/files?folder_id=FOLDER_ID&page_size=20" \
|
|
120
|
+
-H "Authorization: Bearer $TOKEN"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Search Files
|
|
124
|
+
```bash
|
|
125
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/search?q=quarterly+report&page_size=20" \
|
|
126
|
+
-H "Authorization: Bearer $TOKEN"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Simple queries search by file name. For advanced queries, use Drive query syntax:
|
|
130
|
+
- `name contains 'budget'` — name search
|
|
131
|
+
- `mimeType = 'application/vnd.google-apps.spreadsheet'` — only sheets
|
|
132
|
+
- `mimeType = 'application/vnd.google-apps.document'` — only docs
|
|
133
|
+
- `modifiedTime > '2026-01-01'` — recently modified
|
|
134
|
+
|
|
135
|
+
### Get File Metadata
|
|
136
|
+
```bash
|
|
137
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/files/{file_id}" \
|
|
138
|
+
-H "Authorization: Bearer $TOKEN"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Download File
|
|
142
|
+
```bash
|
|
143
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/files/{file_id}/download" \
|
|
144
|
+
-H "Authorization: Bearer $TOKEN"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Returns base64-encoded content. For Google Docs/Sheets/Slides, use export instead.
|
|
148
|
+
|
|
149
|
+
### Export Google Docs/Sheets/Slides
|
|
150
|
+
```bash
|
|
151
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/files/{file_id}/export?mime_type=text/plain" \
|
|
152
|
+
-H "Authorization: Bearer $TOKEN"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Export formats:**
|
|
156
|
+
- `text/plain` — plain text (default, good for Docs)
|
|
157
|
+
- `text/html` — HTML
|
|
158
|
+
- `application/pdf` — PDF
|
|
159
|
+
- `text/csv` — CSV (for Sheets)
|
|
160
|
+
|
|
161
|
+
### Upload File
|
|
162
|
+
```bash
|
|
163
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/files" \
|
|
164
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
165
|
+
-H "Content-Type: application/json" \
|
|
166
|
+
-d '{
|
|
167
|
+
"name": "notes.txt",
|
|
168
|
+
"content": "File content here",
|
|
169
|
+
"mime_type": "text/plain"
|
|
170
|
+
}'
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Upload to a specific folder:**
|
|
174
|
+
```bash
|
|
175
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/files" \
|
|
176
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
177
|
+
-H "Content-Type: application/json" \
|
|
178
|
+
-d '{
|
|
179
|
+
"name": "report.txt",
|
|
180
|
+
"content": "File content here",
|
|
181
|
+
"mime_type": "text/plain",
|
|
182
|
+
"folder_id": "FOLDER_ID"
|
|
183
|
+
}'
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Google Sheets
|
|
189
|
+
|
|
190
|
+
Full read/write access to Google Sheets.
|
|
191
|
+
|
|
192
|
+
### Get Spreadsheet Info
|
|
193
|
+
```bash
|
|
194
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/sheets/{spreadsheet_id}" \
|
|
195
|
+
-H "Authorization: Bearer $TOKEN"
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Returns sheet names, title, and metadata.
|
|
199
|
+
|
|
200
|
+
### Read Cells
|
|
201
|
+
```bash
|
|
202
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/sheets/{spreadsheet_id}/values?range=Sheet1" \
|
|
203
|
+
-H "Authorization: Bearer $TOKEN"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Range uses A1 notation:**
|
|
207
|
+
- `Sheet1` — entire sheet
|
|
208
|
+
- `Sheet1!A1:D10` — specific range
|
|
209
|
+
- `Sheet1!A:A` — entire column A
|
|
210
|
+
- `Sheet1!1:1` — entire row 1
|
|
211
|
+
|
|
212
|
+
### Update Cells
|
|
213
|
+
```bash
|
|
214
|
+
curl -s -X PUT "https://api.atris.ai/api/integrations/google-drive/sheets/{spreadsheet_id}/values" \
|
|
215
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
216
|
+
-H "Content-Type: application/json" \
|
|
217
|
+
-d '{
|
|
218
|
+
"range": "Sheet1!A1:B2",
|
|
219
|
+
"values": [
|
|
220
|
+
["Name", "Score"],
|
|
221
|
+
["Alice", 95]
|
|
222
|
+
]
|
|
223
|
+
}'
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Append Rows
|
|
227
|
+
```bash
|
|
228
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/sheets/{spreadsheet_id}/append" \
|
|
229
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
230
|
+
-H "Content-Type: application/json" \
|
|
231
|
+
-d '{
|
|
232
|
+
"range": "Sheet1",
|
|
233
|
+
"values": [
|
|
234
|
+
["Bob", 88],
|
|
235
|
+
["Carol", 92]
|
|
236
|
+
]
|
|
237
|
+
}'
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Workflows
|
|
243
|
+
|
|
244
|
+
### "Find a file in my Drive"
|
|
245
|
+
1. Run bootstrap
|
|
246
|
+
2. Search: `GET /google-drive/search?q=QUERY`
|
|
247
|
+
3. Display: name, type, modified date for each result
|
|
248
|
+
|
|
249
|
+
### "Read a Google Doc"
|
|
250
|
+
1. Run bootstrap
|
|
251
|
+
2. Search for the doc: `GET /google-drive/search?q=DOC_NAME`
|
|
252
|
+
3. Export as text: `GET /google-drive/files/{id}/export?mime_type=text/plain`
|
|
253
|
+
4. Display content
|
|
254
|
+
|
|
255
|
+
### "Read a spreadsheet"
|
|
256
|
+
1. Run bootstrap
|
|
257
|
+
2. Search for the sheet: `GET /google-drive/search?q=SHEET_NAME`
|
|
258
|
+
3. Get sheet info: `GET /google-drive/sheets/{id}` (to see sheet names)
|
|
259
|
+
4. Read values: `GET /google-drive/sheets/{id}/values?range=Sheet1`
|
|
260
|
+
5. Display as a table
|
|
261
|
+
|
|
262
|
+
### "Add rows to a spreadsheet"
|
|
263
|
+
1. Run bootstrap
|
|
264
|
+
2. Find the sheet
|
|
265
|
+
3. Read current data to understand the columns: `GET /google-drive/sheets/{id}/values?range=Sheet1!1:1`
|
|
266
|
+
4. **Show user what will be appended, get approval**
|
|
267
|
+
5. Append: `POST /google-drive/sheets/{id}/append`
|
|
268
|
+
|
|
269
|
+
### "Upload a file to Drive"
|
|
270
|
+
1. Run bootstrap
|
|
271
|
+
2. Read the local file content
|
|
272
|
+
3. **Confirm with user**: "Upload {filename} to Drive?"
|
|
273
|
+
4. Upload: `POST /google-drive/files` with `{name, content, mime_type}`
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Error Handling
|
|
278
|
+
|
|
279
|
+
| Error | Meaning | Solution |
|
|
280
|
+
|-------|---------|----------|
|
|
281
|
+
| `Token expired` | AtrisOS session expired | Run `atris login` |
|
|
282
|
+
| `Google Drive not connected` | OAuth not completed | Re-run bootstrap |
|
|
283
|
+
| `401 Unauthorized` | Invalid/expired token | Run `atris login` |
|
|
284
|
+
| `400 Drive not connected` | No Drive credentials | Complete OAuth via bootstrap |
|
|
285
|
+
| `429 Rate limited` | Too many requests | Wait 60s, retry |
|
|
286
|
+
| `Invalid grant` | Google revoked access | Re-connect via bootstrap |
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Security Model
|
|
291
|
+
|
|
292
|
+
1. **Local token** (`~/.atris/credentials.json`): Your AtrisOS auth token, stored locally with 600 permissions.
|
|
293
|
+
2. **Drive credentials**: Google Drive refresh token is stored **server-side** in AtrisOS encrypted vault.
|
|
294
|
+
3. **Access control**: AtrisOS API enforces that you can only access your own Drive.
|
|
295
|
+
4. **OAuth scopes**: Only requests necessary Drive permissions (read, write files).
|
|
296
|
+
5. **HTTPS only**: All API communication encrypted in transit.
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Quick Reference
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# Setup (one time)
|
|
304
|
+
npm install -g atris && atris login
|
|
305
|
+
|
|
306
|
+
# Get token
|
|
307
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
308
|
+
|
|
309
|
+
# Check connection
|
|
310
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/status" -H "Authorization: Bearer $TOKEN"
|
|
311
|
+
|
|
312
|
+
# List files
|
|
313
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/files" -H "Authorization: Bearer $TOKEN"
|
|
314
|
+
|
|
315
|
+
# Search files
|
|
316
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/search?q=budget" -H "Authorization: Bearer $TOKEN"
|
|
317
|
+
|
|
318
|
+
# Read a Google Doc as text
|
|
319
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/files/{file_id}/export?mime_type=text/plain" -H "Authorization: Bearer $TOKEN"
|
|
320
|
+
|
|
321
|
+
# Read a spreadsheet
|
|
322
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/sheets/{id}/values?range=Sheet1" -H "Authorization: Bearer $TOKEN"
|
|
323
|
+
|
|
324
|
+
# Append rows to a sheet
|
|
325
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/sheets/{id}/append" \
|
|
326
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
327
|
+
-d '{"range":"Sheet1","values":[["Alice",95]]}'
|
|
328
|
+
|
|
329
|
+
# Upload a file
|
|
330
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/files" \
|
|
331
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
332
|
+
-d '{"name":"notes.txt","content":"Hello world","mime_type":"text/plain"}'
|
|
333
|
+
```
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: email-agent
|
|
3
|
+
description: Gmail integration via AtrisOS API. Read, send, archive emails. Use when user asks about email, inbox, or wants to send/check messages.
|
|
4
|
+
version: 1.1.0
|
|
5
|
+
tags:
|
|
6
|
+
- email-agent
|
|
7
|
+
- backend
|
|
8
|
+
- email
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Email Agent
|
|
12
|
+
|
|
13
|
+
> Drop this in `~/.claude/skills/email-agent/SKILL.md` and Claude Code becomes your email assistant.
|
|
14
|
+
|
|
15
|
+
## Bootstrap (ALWAYS Run First)
|
|
16
|
+
|
|
17
|
+
Before any email operation, run this bootstrap to ensure everything is set up:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
#!/bin/bash
|
|
21
|
+
set -e
|
|
22
|
+
|
|
23
|
+
# 1. Check if atris CLI is installed
|
|
24
|
+
if ! command -v atris &> /dev/null; then
|
|
25
|
+
echo "Installing atris CLI..."
|
|
26
|
+
npm install -g atris
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# 2. Check if logged in to AtrisOS
|
|
30
|
+
if [ ! -f ~/.atris/credentials.json ]; then
|
|
31
|
+
echo "Not logged in to AtrisOS."
|
|
32
|
+
echo ""
|
|
33
|
+
echo "Option 1 (interactive): Run 'atris login' and follow prompts"
|
|
34
|
+
echo "Option 2 (non-interactive): Get token from https://atris.ai/auth/cli"
|
|
35
|
+
echo " Then run: atris login --token YOUR_TOKEN"
|
|
36
|
+
echo ""
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# 3. Extract token (try node first, then python3, then jq)
|
|
41
|
+
if command -v node &> /dev/null; then
|
|
42
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
43
|
+
elif command -v python3 &> /dev/null; then
|
|
44
|
+
TOKEN=$(python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.atris/credentials.json')))['token'])")
|
|
45
|
+
elif command -v jq &> /dev/null; then
|
|
46
|
+
TOKEN=$(jq -r '.token' ~/.atris/credentials.json)
|
|
47
|
+
else
|
|
48
|
+
echo "Error: Need node, python3, or jq to read credentials"
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# 4. Check Gmail connection status (also validates token)
|
|
53
|
+
STATUS=$(curl -s "https://api.atris.ai/api/integrations/gmail/status" \
|
|
54
|
+
-H "Authorization: Bearer $TOKEN")
|
|
55
|
+
|
|
56
|
+
# Check for token expiry
|
|
57
|
+
if echo "$STATUS" | grep -q "Token expired\|Not authenticated"; then
|
|
58
|
+
echo "Token expired. Please re-authenticate:"
|
|
59
|
+
echo " Run: atris login --force"
|
|
60
|
+
echo " Or get new token from: https://atris.ai/auth/cli"
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Parse connected status
|
|
65
|
+
if command -v node &> /dev/null; then
|
|
66
|
+
CONNECTED=$(node -e "console.log(JSON.parse('$STATUS').connected || false)")
|
|
67
|
+
elif command -v python3 &> /dev/null; then
|
|
68
|
+
CONNECTED=$(echo "$STATUS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('connected', False))")
|
|
69
|
+
else
|
|
70
|
+
CONNECTED=$(echo "$STATUS" | jq -r '.connected // false')
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
if [ "$CONNECTED" != "true" ] && [ "$CONNECTED" != "True" ]; then
|
|
74
|
+
echo "Gmail not connected. Getting authorization URL..."
|
|
75
|
+
AUTH=$(curl -s -X POST "https://api.atris.ai/api/integrations/gmail/start" \
|
|
76
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
77
|
+
-H "Content-Type: application/json" \
|
|
78
|
+
-d '{}')
|
|
79
|
+
|
|
80
|
+
if command -v node &> /dev/null; then
|
|
81
|
+
URL=$(node -e "console.log(JSON.parse('$AUTH').auth_url || '')")
|
|
82
|
+
elif command -v python3 &> /dev/null; then
|
|
83
|
+
URL=$(echo "$AUTH" | python3 -c "import sys,json; print(json.load(sys.stdin).get('auth_url', ''))")
|
|
84
|
+
else
|
|
85
|
+
URL=$(echo "$AUTH" | jq -r '.auth_url // empty')
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
echo ""
|
|
89
|
+
echo "Open this URL to connect your Gmail:"
|
|
90
|
+
echo "$URL"
|
|
91
|
+
echo ""
|
|
92
|
+
echo "After authorizing, run your email command again."
|
|
93
|
+
exit 0
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
echo "Ready. Gmail is connected."
|
|
97
|
+
export ATRIS_TOKEN="$TOKEN"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Important**: Run this script ONCE before email operations. If it exits with instructions, follow them, then run again.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## API Reference
|
|
105
|
+
|
|
106
|
+
Base: `https://api.atris.ai/api/integrations/gmail`
|
|
107
|
+
|
|
108
|
+
All requests require: `-H "Authorization: Bearer $TOKEN"`
|
|
109
|
+
|
|
110
|
+
### Get Token (after bootstrap)
|
|
111
|
+
```bash
|
|
112
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### List Emails
|
|
116
|
+
```bash
|
|
117
|
+
curl -s "https://api.atris.ai/api/integrations/gmail/messages?query=in:inbox&max_results=20" \
|
|
118
|
+
-H "Authorization: Bearer $TOKEN"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Query syntax** (Gmail search):
|
|
122
|
+
- `in:inbox` — inbox only
|
|
123
|
+
- `in:inbox newer_than:1d` — today's emails
|
|
124
|
+
- `is:unread` — unread only
|
|
125
|
+
- `from:someone@example.com` — from specific sender
|
|
126
|
+
- `subject:invoice` — subject contains word
|
|
127
|
+
- `has:attachment` — emails with attachments
|
|
128
|
+
|
|
129
|
+
### Read Single Email
|
|
130
|
+
```bash
|
|
131
|
+
curl -s "https://api.atris.ai/api/integrations/gmail/messages/{message_id}" \
|
|
132
|
+
-H "Authorization: Bearer $TOKEN"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Send Email
|
|
136
|
+
```bash
|
|
137
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
|
|
138
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
139
|
+
-H "Content-Type: application/json" \
|
|
140
|
+
-d '{
|
|
141
|
+
"to": "recipient@example.com",
|
|
142
|
+
"subject": "Subject line",
|
|
143
|
+
"body": "Email body text"
|
|
144
|
+
}'
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**With CC/BCC:**
|
|
148
|
+
```bash
|
|
149
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
|
|
150
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
151
|
+
-H "Content-Type: application/json" \
|
|
152
|
+
-d '{
|
|
153
|
+
"to": "recipient@example.com",
|
|
154
|
+
"cc": "copy@example.com",
|
|
155
|
+
"bcc": ["hidden1@example.com", "hidden2@example.com"],
|
|
156
|
+
"subject": "Subject line",
|
|
157
|
+
"body": "Email body text"
|
|
158
|
+
}'
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**With attachments:**
|
|
162
|
+
```bash
|
|
163
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
|
|
164
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
165
|
+
-H "Content-Type: application/json" \
|
|
166
|
+
-d '{
|
|
167
|
+
"to": "recipient@example.com",
|
|
168
|
+
"subject": "With attachment",
|
|
169
|
+
"body": "See attached.",
|
|
170
|
+
"attachments": [{"filename": "report.txt", "content": "base64-encoded-content", "mime_type": "text/plain"}]
|
|
171
|
+
}'
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Drafts
|
|
175
|
+
|
|
176
|
+
**List drafts:**
|
|
177
|
+
```bash
|
|
178
|
+
curl -s "https://api.atris.ai/api/integrations/gmail/drafts?max_results=20" \
|
|
179
|
+
-H "Authorization: Bearer $TOKEN"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Read a draft:**
|
|
183
|
+
```bash
|
|
184
|
+
curl -s "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}" \
|
|
185
|
+
-H "Authorization: Bearer $TOKEN"
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Create a draft:**
|
|
189
|
+
```bash
|
|
190
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts" \
|
|
191
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
192
|
+
-H "Content-Type: application/json" \
|
|
193
|
+
-d '{
|
|
194
|
+
"to": "recipient@example.com",
|
|
195
|
+
"subject": "Subject line",
|
|
196
|
+
"body": "Draft body text"
|
|
197
|
+
}'
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Supports same fields as send: `cc`, `bcc`, `attachments`, plus `thread_id` to attach to an existing thread.
|
|
201
|
+
|
|
202
|
+
**Update a draft:**
|
|
203
|
+
```bash
|
|
204
|
+
curl -s -X PUT "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}" \
|
|
205
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
206
|
+
-H "Content-Type: application/json" \
|
|
207
|
+
-d '{
|
|
208
|
+
"to": "recipient@example.com",
|
|
209
|
+
"subject": "Updated subject",
|
|
210
|
+
"body": "Updated body"
|
|
211
|
+
}'
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Send a draft:**
|
|
215
|
+
```bash
|
|
216
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}/send" \
|
|
217
|
+
-H "Authorization: Bearer $TOKEN"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Delete a draft:**
|
|
221
|
+
```bash
|
|
222
|
+
curl -s -X DELETE "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}" \
|
|
223
|
+
-H "Authorization: Bearer $TOKEN"
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Archive Email
|
|
227
|
+
```bash
|
|
228
|
+
# Single message
|
|
229
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/archive" \
|
|
230
|
+
-H "Authorization: Bearer $TOKEN"
|
|
231
|
+
|
|
232
|
+
# Batch archive
|
|
233
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/batch-archive" \
|
|
234
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
235
|
+
-H "Content-Type: application/json" \
|
|
236
|
+
-d '{"message_ids": ["id1", "id2", "id3"]}'
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Check Status
|
|
240
|
+
```bash
|
|
241
|
+
curl -s "https://api.atris.ai/api/integrations/gmail/status" \
|
|
242
|
+
-H "Authorization: Bearer $TOKEN"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Disconnect Gmail
|
|
246
|
+
```bash
|
|
247
|
+
curl -s -X DELETE "https://api.atris.ai/api/integrations/gmail" \
|
|
248
|
+
-H "Authorization: Bearer $TOKEN"
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Workflows
|
|
254
|
+
|
|
255
|
+
### "Check my emails"
|
|
256
|
+
1. Run bootstrap
|
|
257
|
+
2. List messages: `GET /messages?query=in:inbox%20newer_than:1d&max_results=20`
|
|
258
|
+
3. Display: sender, subject, snippet for each
|
|
259
|
+
|
|
260
|
+
### "Send email to X about Y"
|
|
261
|
+
1. Run bootstrap
|
|
262
|
+
2. Draft email content
|
|
263
|
+
3. **Show user the draft for approval**
|
|
264
|
+
4. On approval: `POST /send` with `{to, subject, body}`
|
|
265
|
+
5. Confirm: "Email sent!"
|
|
266
|
+
|
|
267
|
+
### "Clean up my inbox"
|
|
268
|
+
1. Run bootstrap
|
|
269
|
+
2. List: `GET /messages?query=in:inbox&max_results=50`
|
|
270
|
+
3. Identify archivable emails (see rules below)
|
|
271
|
+
4. **Show user what will be archived, get approval**
|
|
272
|
+
5. Batch archive: `POST /batch-archive`
|
|
273
|
+
|
|
274
|
+
### "Show my drafts"
|
|
275
|
+
1. Run bootstrap
|
|
276
|
+
2. List drafts: `GET /gmail/drafts?max_results=20`
|
|
277
|
+
3. Display: to, subject, snippet for each
|
|
278
|
+
|
|
279
|
+
### "Draft an email to X about Y"
|
|
280
|
+
1. Run bootstrap
|
|
281
|
+
2. Compose email content
|
|
282
|
+
3. **Show user the draft for review**
|
|
283
|
+
4. On approval: `POST /gmail/drafts` with `{to, subject, body}`
|
|
284
|
+
5. Confirm: "Draft saved! You can find it in Gmail."
|
|
285
|
+
|
|
286
|
+
### "Send draft about X"
|
|
287
|
+
1. Run bootstrap
|
|
288
|
+
2. List drafts: `GET /gmail/drafts`
|
|
289
|
+
3. Find matching draft by subject/recipient
|
|
290
|
+
4. **Show user the draft content, confirm they want to send it**
|
|
291
|
+
5. Send: `POST /gmail/drafts/{draft_id}/send`
|
|
292
|
+
|
|
293
|
+
### "Archive all from [sender]"
|
|
294
|
+
1. Run bootstrap
|
|
295
|
+
2. Search: `GET /messages?query=from:{sender}`
|
|
296
|
+
3. Collect message IDs
|
|
297
|
+
4. **Confirm with user**: "Found N emails from {sender}. Archive all?"
|
|
298
|
+
5. Batch archive
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Auto-Archive Rules
|
|
303
|
+
|
|
304
|
+
**Safe to suggest archiving:**
|
|
305
|
+
- From: `noreply@`, `notifications@`, `newsletter@`, `no-reply@`
|
|
306
|
+
- Subject contains: digest, newsletter, notification, weekly update, daily summary
|
|
307
|
+
- Marketing: promotional, unsubscribe link present
|
|
308
|
+
|
|
309
|
+
**NEVER auto-archive (always keep):**
|
|
310
|
+
- Subject contains: invoice, receipt, payment, urgent, action required, password, verification, security
|
|
311
|
+
- From known contacts (check if user has replied to them)
|
|
312
|
+
- Flagged/starred messages
|
|
313
|
+
|
|
314
|
+
**Always ask before archiving.** Never archive without explicit user approval.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Error Handling
|
|
319
|
+
|
|
320
|
+
| Error | Meaning | Solution |
|
|
321
|
+
|-------|---------|----------|
|
|
322
|
+
| `Token expired` | AtrisOS session expired | Run `atris login` |
|
|
323
|
+
| `Gmail not connected` | OAuth not completed | Re-run bootstrap, complete OAuth flow |
|
|
324
|
+
| `401 Unauthorized` | Invalid/expired token | Run `atris login` |
|
|
325
|
+
| `400 Gmail not connected` | No Gmail credentials | Complete OAuth via bootstrap |
|
|
326
|
+
| `429 Rate limited` | Too many requests | Wait 60s, retry |
|
|
327
|
+
| `Invalid grant` | Google revoked access | Re-connect Gmail via bootstrap |
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Security Model
|
|
332
|
+
|
|
333
|
+
1. **Local token** (`~/.atris/credentials.json`): Your AtrisOS auth token, stored locally with 600 permissions. Same model as AWS CLI, GitHub CLI.
|
|
334
|
+
|
|
335
|
+
2. **Gmail credentials**: Your Gmail refresh token is stored **server-side** in AtrisOS encrypted vault. Never stored on your local machine.
|
|
336
|
+
|
|
337
|
+
3. **Access control**: AtrisOS API enforces that you can only access your own email. No cross-user access possible.
|
|
338
|
+
|
|
339
|
+
4. **OAuth scopes**: Only requests necessary Gmail permissions (read, send, modify labels).
|
|
340
|
+
|
|
341
|
+
5. **HTTPS only**: All API communication encrypted in transit.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Quick Reference
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
# Setup (one time)
|
|
349
|
+
npm install -g atris && atris login
|
|
350
|
+
|
|
351
|
+
# Get token
|
|
352
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
353
|
+
|
|
354
|
+
# Check connection
|
|
355
|
+
curl -s "https://api.atris.ai/api/integrations/gmail/status" -H "Authorization: Bearer $TOKEN"
|
|
356
|
+
|
|
357
|
+
# List inbox
|
|
358
|
+
curl -s "https://api.atris.ai/api/integrations/gmail/messages?query=in:inbox&max_results=10" -H "Authorization: Bearer $TOKEN"
|
|
359
|
+
|
|
360
|
+
# Send email
|
|
361
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
|
|
362
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
363
|
+
-d '{"to":"email@example.com","subject":"Hi","body":"Hello!"}'
|
|
364
|
+
|
|
365
|
+
# List drafts
|
|
366
|
+
curl -s "https://api.atris.ai/api/integrations/gmail/drafts" -H "Authorization: Bearer $TOKEN"
|
|
367
|
+
|
|
368
|
+
# Create draft
|
|
369
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts" \
|
|
370
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
371
|
+
-d '{"to":"email@example.com","subject":"Hi","body":"Draft text"}'
|
|
372
|
+
|
|
373
|
+
# Send a draft
|
|
374
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}/send" \
|
|
375
|
+
-H "Authorization: Bearer $TOKEN"
|
|
376
|
+
```
|