@marcfargas/go-easy 0.0.1 → 0.3.0
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/CHANGELOG.md +90 -0
- package/LICENSE +21 -0
- package/README.md +224 -0
- package/dist/auth-flow.d.ts +50 -0
- package/dist/auth-flow.d.ts.map +1 -0
- package/dist/auth-flow.js +219 -0
- package/dist/auth-flow.js.map +1 -0
- package/dist/auth-server.d.ts +18 -0
- package/dist/auth-server.d.ts.map +1 -0
- package/dist/auth-server.js +327 -0
- package/dist/auth-server.js.map +1 -0
- package/dist/auth-store.d.ts +81 -0
- package/dist/auth-store.d.ts.map +1 -0
- package/dist/auth-store.js +185 -0
- package/dist/auth-store.js.map +1 -0
- package/dist/auth.d.ts +47 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +131 -0
- package/dist/auth.js.map +1 -0
- package/dist/bin/calendar.d.ts +17 -0
- package/dist/bin/calendar.d.ts.map +1 -0
- package/dist/bin/calendar.js +224 -0
- package/dist/bin/calendar.js.map +1 -0
- package/dist/bin/drive.d.ts +18 -0
- package/dist/bin/drive.d.ts.map +1 -0
- package/dist/bin/drive.js +205 -0
- package/dist/bin/drive.js.map +1 -0
- package/dist/bin/easy.d.ts +11 -0
- package/dist/bin/easy.d.ts.map +1 -0
- package/dist/bin/easy.js +140 -0
- package/dist/bin/easy.js.map +1 -0
- package/dist/bin/gmail.d.ts +25 -0
- package/dist/bin/gmail.d.ts.map +1 -0
- package/dist/bin/gmail.js +243 -0
- package/dist/bin/gmail.js.map +1 -0
- package/dist/bin/tasks.d.ts +17 -0
- package/dist/bin/tasks.d.ts.map +1 -0
- package/dist/bin/tasks.js +190 -0
- package/dist/bin/tasks.js.map +1 -0
- package/dist/calendar/helpers.d.ts +35 -0
- package/dist/calendar/helpers.d.ts.map +1 -0
- package/dist/calendar/helpers.js +178 -0
- package/dist/calendar/helpers.js.map +1 -0
- package/dist/calendar/index.d.ts +64 -0
- package/dist/calendar/index.d.ts.map +1 -0
- package/dist/calendar/index.js +210 -0
- package/dist/calendar/index.js.map +1 -0
- package/dist/calendar/types.d.ts +191 -0
- package/dist/calendar/types.d.ts.map +1 -0
- package/dist/calendar/types.js +12 -0
- package/dist/calendar/types.js.map +1 -0
- package/dist/drive/helpers.d.ts +22 -0
- package/dist/drive/helpers.d.ts.map +1 -0
- package/dist/drive/helpers.js +85 -0
- package/dist/drive/helpers.js.map +1 -0
- package/dist/drive/index.d.ts +114 -0
- package/dist/drive/index.d.ts.map +1 -0
- package/dist/drive/index.js +418 -0
- package/dist/drive/index.js.map +1 -0
- package/dist/drive/types.d.ts +91 -0
- package/dist/drive/types.d.ts.map +1 -0
- package/dist/drive/types.js +5 -0
- package/dist/drive/types.js.map +1 -0
- package/dist/errors.d.ts +58 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +73 -0
- package/dist/errors.js.map +1 -0
- package/dist/gmail/helpers.d.ts +59 -0
- package/dist/gmail/helpers.d.ts.map +1 -0
- package/dist/gmail/helpers.js +308 -0
- package/dist/gmail/helpers.js.map +1 -0
- package/dist/gmail/index.d.ts +95 -0
- package/dist/gmail/index.d.ts.map +1 -0
- package/dist/gmail/index.js +465 -0
- package/dist/gmail/index.js.map +1 -0
- package/dist/gmail/markdown.d.ts +22 -0
- package/dist/gmail/markdown.d.ts.map +1 -0
- package/dist/gmail/markdown.js +30 -0
- package/dist/gmail/markdown.js.map +1 -0
- package/dist/gmail/types.d.ts +154 -0
- package/dist/gmail/types.d.ts.map +1 -0
- package/dist/gmail/types.js +5 -0
- package/dist/gmail/types.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/safety.d.ts +58 -0
- package/dist/safety.d.ts.map +1 -0
- package/dist/safety.js +61 -0
- package/dist/safety.js.map +1 -0
- package/dist/scopes.d.ts +16 -0
- package/dist/scopes.d.ts.map +1 -0
- package/dist/scopes.js +28 -0
- package/dist/scopes.js.map +1 -0
- package/dist/tasks/helpers.d.ts +10 -0
- package/dist/tasks/helpers.d.ts.map +1 -0
- package/dist/tasks/helpers.js +33 -0
- package/dist/tasks/helpers.js.map +1 -0
- package/dist/tasks/index.d.ts +63 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +253 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/tasks/types.d.ts +79 -0
- package/dist/tasks/types.d.ts.map +1 -0
- package/dist/tasks/types.js +5 -0
- package/dist/tasks/types.js.map +1 -0
- package/package.json +73 -4
- package/skills/go-easy/SKILL.md +146 -0
- package/skills/go-easy/calendar.md +366 -0
- package/skills/go-easy/drive.md +309 -0
- package/skills/go-easy/gmail.md +478 -0
- package/skills/go-easy/tasks.md +260 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @marcfargas/go-easy
|
|
2
|
+
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`8cf8524`](https://github.com/marcfargas/go-easy/commit/8cf85243659e1151b896a69f5405534062a82899) Thanks [@marcfargas](https://github.com/marcfargas)! - Beta release — own auth system, Google Tasks, reply CLI, file-based body flags, Markdown emails.
|
|
8
|
+
|
|
9
|
+
### Auth (BREAKING)
|
|
10
|
+
|
|
11
|
+
go-easy now owns its own OAuth2 tokens at `~/.go-easy/` instead of reading from legacy CLI stores (`~/.gmcli`, `~/.gdcli`, `~/.gccli`).
|
|
12
|
+
|
|
13
|
+
- `npx go-easy auth add <email>` — agent-compatible two-phase OAuth flow (start → poll)
|
|
14
|
+
- `npx go-easy auth list` — list configured accounts and scopes
|
|
15
|
+
- `npx go-easy auth remove <email> --confirm` — remove an account
|
|
16
|
+
- One combined token per account covers Gmail + Drive + Calendar + Tasks
|
|
17
|
+
- Specific error codes with `fix` field: `AUTH_NO_ACCOUNT`, `AUTH_MISSING_SCOPE`, `AUTH_TOKEN_REVOKED`, `AUTH_NO_CREDENTIALS`
|
|
18
|
+
|
|
19
|
+
### Google Tasks (NEW)
|
|
20
|
+
|
|
21
|
+
New service module and CLI for Google Tasks API:
|
|
22
|
+
|
|
23
|
+
- `npx go-tasks <account> lists` — list task lists
|
|
24
|
+
- `npx go-tasks <account> tasks <listId>` — list tasks with pagination
|
|
25
|
+
- `npx go-tasks <account> get/add/update/complete/move/delete` — full CRUD
|
|
26
|
+
- `npx go-tasks <account> create-list/delete-list/clear` — list management
|
|
27
|
+
- Subtask support via `--parent` flag
|
|
28
|
+
- Library: `@marcfargas/go-easy/tasks` export
|
|
29
|
+
- Requires re-auth for existing accounts (`npx go-easy auth add <email>`)
|
|
30
|
+
|
|
31
|
+
### Gmail CLI
|
|
32
|
+
|
|
33
|
+
- Add `reply` command — reply and reply-all with `--reply-all` flag (DESTRUCTIVE, requires `--confirm`)
|
|
34
|
+
- Add `--in-reply-to` flag for `draft` command (thread association)
|
|
35
|
+
- Add `--cc` and `--bcc` flags for `draft` and `send` commands
|
|
36
|
+
- Add `--page-token` for `search` and `drafts` pagination
|
|
37
|
+
|
|
38
|
+
### Body Flags (BREAKING)
|
|
39
|
+
|
|
40
|
+
Replace inline `--body`, `--html`, `--md` flags with file-based alternatives:
|
|
41
|
+
|
|
42
|
+
- `--body-text-file=<path>` — read plain text body from UTF-8 file
|
|
43
|
+
- `--body-html-file=<path>` — read HTML body from UTF-8 file
|
|
44
|
+
- `--body-md-file=<path>` — read Markdown body from file (auto-converted to HTML)
|
|
45
|
+
|
|
46
|
+
This eliminates shell escaping, encoding, and multiline issues for agent use.
|
|
47
|
+
|
|
48
|
+
### Markdown Email Support
|
|
49
|
+
|
|
50
|
+
- New `markdown` option on `send`, `reply`, `forward`, and `createDraft`
|
|
51
|
+
- Auto-converts Markdown to email-safe HTML with inline styles
|
|
52
|
+
- GFM support: tables, strikethrough, code blocks, links, lists
|
|
53
|
+
- `markdownToHtml()` helper exported from `@marcfargas/go-easy/gmail`
|
|
54
|
+
|
|
55
|
+
### Forward Improvements
|
|
56
|
+
|
|
57
|
+
- Forward creates a draft by default (WRITE, no safety gate)
|
|
58
|
+
- `--send-now --confirm` to send immediately (DESTRUCTIVE)
|
|
59
|
+
- Attachment filtering: `--include=name` and `--exclude=name` (substring match)
|
|
60
|
+
- `--no-thread` to break out of the original thread
|
|
61
|
+
- Body content appears above the forwarded message
|
|
62
|
+
|
|
63
|
+
### Calendar
|
|
64
|
+
|
|
65
|
+
- Support all event types: working location, out-of-office, focus time, birthday
|
|
66
|
+
- `--page-token` for events pagination
|
|
67
|
+
- Fix `update` command: use PATCH instead of PUT to prevent data loss on partial updates
|
|
68
|
+
|
|
69
|
+
### Drive
|
|
70
|
+
|
|
71
|
+
- `--page-token` for `ls` and `search` pagination
|
|
72
|
+
|
|
73
|
+
### Fixes
|
|
74
|
+
|
|
75
|
+
- RFC 2047 encode Subject headers with non-ASCII characters
|
|
76
|
+
- Fix forward threading (keep in original thread by default)
|
|
77
|
+
- Fix auth HTML pages: add `<meta charset="utf-8">` for emoji rendering
|
|
78
|
+
|
|
79
|
+
## 0.2.0
|
|
80
|
+
|
|
81
|
+
### Minor Changes
|
|
82
|
+
|
|
83
|
+
- [`5f424e1`](https://github.com/marcfargas/go-easy/commit/5f424e16c3c9971c2be196725a5d9c1d7e88633b) Thanks [@marcfargas](https://github.com/marcfargas)! - Initial release — Gmail, Drive & Calendar APIs for AI agents and humans.
|
|
84
|
+
|
|
85
|
+
- Gmail: search, getMessage, getThread, send, reply, forward, createDraft, sendDraft, listDrafts, listLabels, batchModifyLabels, getAttachmentContent, getProfile
|
|
86
|
+
- Drive: listFiles, searchFiles, getFile, downloadFile, exportFile, uploadFile, createFolder, moveFile, renameFile, copyFile, trashFile, listPermissions, shareFile, unshareFile
|
|
87
|
+
- Calendar: listCalendars, listEvents, getEvent, createEvent, updateEvent, deleteEvent, queryFreeBusy
|
|
88
|
+
- Gateway CLIs: go-gmail, go-drive, go-calendar (JSON output, --confirm safety)
|
|
89
|
+
- Safety model: READ/WRITE/DESTRUCTIVE operation classification
|
|
90
|
+
- Auth: multi-account OAuth2 with per-service token stores
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marc Fargas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# go-easy 🟢
|
|
2
|
+
|
|
3
|
+
> Google APIs made easy — Gmail, Drive & Calendar. For AI agents and humans.
|
|
4
|
+
|
|
5
|
+
Thin TypeScript wrappers over Google's individual `@googleapis/*` packages with:
|
|
6
|
+
- **Own auth** — unified OAuth2 with combined tokens, agent-compatible two-phase flow
|
|
7
|
+
- **Agent-friendly types** — structured `GmailMessage`, `DriveFile`, `CalendarEvent`
|
|
8
|
+
- **Safety guards** — destructive operations (send, share, delete) require explicit confirmation
|
|
9
|
+
- **JSON gateways** — CLI tools that always output structured JSON
|
|
10
|
+
- **File-based body** — email bodies read from files, not CLI args (no shell escaping issues)
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# As a library
|
|
16
|
+
npm install @marcfargas/go-easy
|
|
17
|
+
|
|
18
|
+
# As CLI tools (no install needed)
|
|
19
|
+
npx go-gmail you@example.com search "is:unread"
|
|
20
|
+
npx go-drive you@example.com ls
|
|
21
|
+
npx go-calendar you@example.com events primary
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Requires **Node.js ≥ 20**.
|
|
25
|
+
|
|
26
|
+
## Auth Setup
|
|
27
|
+
|
|
28
|
+
go-easy manages its own OAuth2 tokens in `~/.go-easy/`.
|
|
29
|
+
|
|
30
|
+
### Prerequisites
|
|
31
|
+
|
|
32
|
+
1. Create a project in [Google Cloud Console](https://console.cloud.google.com/)
|
|
33
|
+
2. Enable the Gmail, Drive, and Calendar APIs
|
|
34
|
+
3. Create OAuth2 credentials (Desktop application type)
|
|
35
|
+
4. Save credentials to `~/.go-easy/credentials.json`:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"clientId": "YOUR_CLIENT_ID.apps.googleusercontent.com",
|
|
40
|
+
"clientSecret": "YOUR_CLIENT_SECRET"
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Add an account
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx go-easy auth add you@example.com
|
|
48
|
+
# → { "status": "started", "authUrl": "https://accounts.google.com/..." }
|
|
49
|
+
# Open the URL, authorize, then poll:
|
|
50
|
+
npx go-easy auth add you@example.com
|
|
51
|
+
# → { "status": "complete", "email": "you@example.com", "scopes": ["gmail", "drive", "calendar"] }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
One combined token covers Gmail + Drive + Calendar. The flow is agent-compatible — two separate CLI calls (start + poll), no streaming stdout needed.
|
|
55
|
+
|
|
56
|
+
### Manage accounts
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx go-easy auth list # List configured accounts
|
|
60
|
+
npx go-easy auth add you@example.com # Add or upgrade account
|
|
61
|
+
npx go-easy auth remove you@example.com --confirm # Remove account
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { getAuth } from '@marcfargas/go-easy/auth';
|
|
68
|
+
import { search, send } from '@marcfargas/go-easy/gmail';
|
|
69
|
+
import { setSafetyContext } from '@marcfargas/go-easy';
|
|
70
|
+
|
|
71
|
+
const auth = await getAuth('gmail', 'you@example.com');
|
|
72
|
+
|
|
73
|
+
// Search (READ — no safety gate)
|
|
74
|
+
const results = await search(auth, { query: 'is:unread from:client' });
|
|
75
|
+
console.log(results.items);
|
|
76
|
+
|
|
77
|
+
// Send (DESTRUCTIVE — requires safety context)
|
|
78
|
+
setSafetyContext({
|
|
79
|
+
confirm: async (op) => {
|
|
80
|
+
console.log(`⚠️ ${op.description}`);
|
|
81
|
+
return true; // or prompt the user
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
await send(auth, {
|
|
86
|
+
to: 'client@example.com',
|
|
87
|
+
subject: 'Invoice attached',
|
|
88
|
+
markdown: '# Invoice\n\nPlease find attached.',
|
|
89
|
+
attachments: ['./invoice.pdf'],
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Gateway CLIs
|
|
94
|
+
|
|
95
|
+
All gateway CLIs output JSON to stdout and work via `npx`:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Gmail
|
|
99
|
+
npx go-gmail you@example.com search "is:unread" --max=10
|
|
100
|
+
npx go-gmail you@example.com get <messageId>
|
|
101
|
+
npx go-gmail you@example.com reply <messageId> --body-text-file=reply.txt --confirm
|
|
102
|
+
npx go-gmail you@example.com send --to=x@y.com --subject="Hi" --body-text-file=body.txt --confirm
|
|
103
|
+
|
|
104
|
+
# Drive
|
|
105
|
+
npx go-drive you@example.com ls
|
|
106
|
+
npx go-drive you@example.com search "quarterly report"
|
|
107
|
+
npx go-drive you@example.com upload ./file.pdf --folder=<folderId>
|
|
108
|
+
|
|
109
|
+
# Calendar
|
|
110
|
+
npx go-calendar you@example.com events primary --from=2026-02-01T00:00:00Z
|
|
111
|
+
npx go-calendar you@example.com create primary --summary="Meeting" --start=... --end=...
|
|
112
|
+
npx go-calendar you@example.com freebusy primary --from=... --to=...
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Body content is always read from files (`--body-text-file`, `--body-html-file`, `--body-md-file`), never passed inline.
|
|
116
|
+
|
|
117
|
+
Destructive operations require `--confirm`. Without it, they show what *would* happen and exit with code 2.
|
|
118
|
+
|
|
119
|
+
## Services
|
|
120
|
+
|
|
121
|
+
| Service | Module | Gateway | Status |
|
|
122
|
+
|---------|--------|---------|--------|
|
|
123
|
+
| Gmail | `@marcfargas/go-easy/gmail` | `npx go-gmail` | ✅ Ready |
|
|
124
|
+
| Drive | `@marcfargas/go-easy/drive` | `npx go-drive` | ✅ Ready |
|
|
125
|
+
| Calendar | `@marcfargas/go-easy/calendar` | `npx go-calendar` | ✅ Ready |
|
|
126
|
+
|
|
127
|
+
### Gmail
|
|
128
|
+
|
|
129
|
+
| Function | Safety | Description |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| `search` | READ | Search messages by Gmail query |
|
|
132
|
+
| `getMessage` | READ | Get a single message with parsed fields |
|
|
133
|
+
| `getThread` | READ | Get a full conversation thread |
|
|
134
|
+
| `listLabels` | READ | List all labels |
|
|
135
|
+
| `getAttachmentContent` | READ | Download an attachment as Buffer |
|
|
136
|
+
| `getProfile` | READ | Get the authenticated email address |
|
|
137
|
+
| `createDraft` | WRITE | Create a draft (no send) |
|
|
138
|
+
| `listDrafts` | READ | List existing drafts |
|
|
139
|
+
| `batchModifyLabels` | WRITE | Add/remove labels on multiple messages |
|
|
140
|
+
| `markdownToHtml` | — | Convert Markdown to email-safe HTML |
|
|
141
|
+
| `send` | ⚠️ DESTRUCTIVE | Send a new email (supports `markdown` option) |
|
|
142
|
+
| `reply` | ⚠️ DESTRUCTIVE | Reply / reply-all to a message |
|
|
143
|
+
| `forward` | WRITE / ⚠️ DESTRUCTIVE | Forward as draft (default) or send (`sendNow`). Attachment filtering. |
|
|
144
|
+
| `sendDraft` | ⚠️ DESTRUCTIVE | Send an existing draft |
|
|
145
|
+
|
|
146
|
+
### Drive
|
|
147
|
+
|
|
148
|
+
| Function | Safety | Description |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `listFiles` | READ | List folder contents or query by metadata |
|
|
151
|
+
| `searchFiles` | READ | Full-text search inside file contents |
|
|
152
|
+
| `getFile` | READ | Get file metadata by ID |
|
|
153
|
+
| `downloadFile` | READ | Download binary files as Buffer |
|
|
154
|
+
| `exportFile` | READ | Export Workspace files (Docs → pdf/docx, Sheets → xlsx/csv, etc.) |
|
|
155
|
+
| `listPermissions` | READ | List sharing permissions on a file |
|
|
156
|
+
| `uploadFile` | WRITE | Upload a local file |
|
|
157
|
+
| `createFolder` | WRITE | Create a folder |
|
|
158
|
+
| `moveFile` | WRITE | Move a file to a different folder |
|
|
159
|
+
| `renameFile` | WRITE | Rename a file |
|
|
160
|
+
| `copyFile` | WRITE | Copy a file |
|
|
161
|
+
| `trashFile` | ⚠️ DESTRUCTIVE | Trash a file |
|
|
162
|
+
| `shareFile` | ⚠️ DESTRUCTIVE* | Share a file (*public sharing only; user/group is WRITE) |
|
|
163
|
+
| `unshareFile` | ⚠️ DESTRUCTIVE | Remove a sharing permission |
|
|
164
|
+
|
|
165
|
+
### Calendar
|
|
166
|
+
|
|
167
|
+
| Function | Safety | Description |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `listCalendars` | READ | List all calendars for the account |
|
|
170
|
+
| `listEvents` | READ | List events with time range, search, pagination |
|
|
171
|
+
| `getEvent` | READ | Get a single event by ID |
|
|
172
|
+
| `queryFreeBusy` | READ | Check availability across calendars |
|
|
173
|
+
| `createEvent` | WRITE | Create an event (with attendees, all-day, location, OOO, focus time) |
|
|
174
|
+
| `updateEvent` | WRITE | Update an existing event (full replace) |
|
|
175
|
+
| `deleteEvent` | ⚠️ DESTRUCTIVE | Delete an event (warns about attendee cancellation) |
|
|
176
|
+
|
|
177
|
+
## Safety Model
|
|
178
|
+
|
|
179
|
+
Operations are classified into three levels:
|
|
180
|
+
|
|
181
|
+
| Level | Gate | Examples |
|
|
182
|
+
|-------|------|----------|
|
|
183
|
+
| **READ** | None | search, getMessage, listLabels |
|
|
184
|
+
| **WRITE** | Logged | createDraft, batchModifyLabels, upload |
|
|
185
|
+
| **DESTRUCTIVE** | Blocked unless confirmed | send, reply, forward, share, delete |
|
|
186
|
+
|
|
187
|
+
Set up a `SafetyContext` at startup to handle confirmation prompts.
|
|
188
|
+
Without one, all destructive operations are blocked by default.
|
|
189
|
+
|
|
190
|
+
## Module Structure
|
|
191
|
+
|
|
192
|
+
go-easy uses **subpath exports** — import only what you need:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
import { getAuth } from '@marcfargas/go-easy/auth';
|
|
196
|
+
import { search, send } from '@marcfargas/go-easy/gmail';
|
|
197
|
+
import { listFiles, uploadFile } from '@marcfargas/go-easy/drive';
|
|
198
|
+
import { listEvents, createEvent } from '@marcfargas/go-easy/calendar';
|
|
199
|
+
import { setSafetyContext } from '@marcfargas/go-easy';
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
| Import path | What's in it |
|
|
203
|
+
|---|---|
|
|
204
|
+
| `@marcfargas/go-easy` | Safety context, errors, plus `gmail`/`drive`/`calendar` as namespaces |
|
|
205
|
+
| `@marcfargas/go-easy/auth` | `getAuth`, `listAccounts`, `listAllAccounts`, `clearAuthCache` |
|
|
206
|
+
| `@marcfargas/go-easy/auth-store` | `readAccountStore`, `writeAccountStore`, `findAccount`, etc. |
|
|
207
|
+
| `@marcfargas/go-easy/scopes` | `SCOPES`, `ALL_SCOPES`, `scopeToService` |
|
|
208
|
+
| `@marcfargas/go-easy/gmail` | All Gmail operations |
|
|
209
|
+
| `@marcfargas/go-easy/drive` | All Drive operations |
|
|
210
|
+
| `@marcfargas/go-easy/calendar` | All Calendar operations |
|
|
211
|
+
|
|
212
|
+
## Development
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
npm install # install deps
|
|
216
|
+
npm run build # compile TypeScript
|
|
217
|
+
npm test # run tests (vitest)
|
|
218
|
+
npm run lint # type-check without emitting
|
|
219
|
+
npm run dev # watch mode
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
MIT
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth-flow — Two-phase OAuth flow for agent-compatible auth.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 (start): Spawn background auth server, return URL.
|
|
5
|
+
* Phase 2 (poll): Check pending file for completion status.
|
|
6
|
+
*
|
|
7
|
+
* The agent calls `npx go-easy auth add <email>` which:
|
|
8
|
+
* - On first call: starts the server, returns { status: "started", authUrl }
|
|
9
|
+
* - On subsequent calls: polls and returns current status
|
|
10
|
+
* - When user completes auth: returns { status: "complete" }
|
|
11
|
+
*/
|
|
12
|
+
export type AuthFlowStatus = {
|
|
13
|
+
status: 'started';
|
|
14
|
+
authUrl: string;
|
|
15
|
+
expiresIn: number;
|
|
16
|
+
} | {
|
|
17
|
+
status: 'waiting';
|
|
18
|
+
authUrl: string;
|
|
19
|
+
expiresIn: number;
|
|
20
|
+
} | {
|
|
21
|
+
status: 'complete';
|
|
22
|
+
email: string;
|
|
23
|
+
scopes: string[];
|
|
24
|
+
} | {
|
|
25
|
+
status: 'partial';
|
|
26
|
+
email: string;
|
|
27
|
+
grantedScopes: string[];
|
|
28
|
+
missingScopes: string[];
|
|
29
|
+
message: string;
|
|
30
|
+
} | {
|
|
31
|
+
status: 'denied';
|
|
32
|
+
message: string;
|
|
33
|
+
} | {
|
|
34
|
+
status: 'expired';
|
|
35
|
+
message: string;
|
|
36
|
+
} | {
|
|
37
|
+
status: 'error';
|
|
38
|
+
message: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Start or poll the auth flow for an email.
|
|
42
|
+
*
|
|
43
|
+
* This is the single entry point — handles all states:
|
|
44
|
+
* - No session → start new auth server
|
|
45
|
+
* - Pending session with live server → return waiting/started
|
|
46
|
+
* - Completed session → return result, clean up
|
|
47
|
+
* - Stale session (dead pid) → clean up, restart
|
|
48
|
+
*/
|
|
49
|
+
export declare function authAdd(email: string): Promise<AuthFlowStatus>;
|
|
50
|
+
//# sourceMappingURL=auth-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-flow.d.ts","sourceRoot":"","sources":["../src/auth-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAiBH,MAAM,MAAM,cAAc,GACtB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GACvD;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvG;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAmBzC;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAyGpE"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth-flow — Two-phase OAuth flow for agent-compatible auth.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 (start): Spawn background auth server, return URL.
|
|
5
|
+
* Phase 2 (poll): Check pending file for completion status.
|
|
6
|
+
*
|
|
7
|
+
* The agent calls `npx go-easy auth add <email>` which:
|
|
8
|
+
* - On first call: starts the server, returns { status: "started", authUrl }
|
|
9
|
+
* - On subsequent calls: polls and returns current status
|
|
10
|
+
* - When user completes auth: returns { status: "complete" }
|
|
11
|
+
*/
|
|
12
|
+
import { readFile, unlink, stat } from 'node:fs/promises';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { spawn } from 'node:child_process';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { getPendingDir, readAccountStore, readCredentials, findAccount, } from './auth-store.js';
|
|
17
|
+
import { ALL_SCOPES, scopeToService } from './scopes.js';
|
|
18
|
+
import { AuthError } from './errors.js';
|
|
19
|
+
// ─── Public API ────────────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Start or poll the auth flow for an email.
|
|
22
|
+
*
|
|
23
|
+
* This is the single entry point — handles all states:
|
|
24
|
+
* - No session → start new auth server
|
|
25
|
+
* - Pending session with live server → return waiting/started
|
|
26
|
+
* - Completed session → return result, clean up
|
|
27
|
+
* - Stale session (dead pid) → clean up, restart
|
|
28
|
+
*/
|
|
29
|
+
export async function authAdd(email) {
|
|
30
|
+
// Check if already fully configured
|
|
31
|
+
const store = await readAccountStore();
|
|
32
|
+
if (store) {
|
|
33
|
+
const account = findAccount(store, email);
|
|
34
|
+
if (account?.tokens.combined) {
|
|
35
|
+
const hasAll = ALL_SCOPES.every((s) => account.tokens.combined.scopes.includes(s));
|
|
36
|
+
if (hasAll) {
|
|
37
|
+
// Clean up any stale pending file
|
|
38
|
+
await cleanupPending(email);
|
|
39
|
+
return {
|
|
40
|
+
status: 'complete',
|
|
41
|
+
email: account.email,
|
|
42
|
+
scopes: account.tokens.combined.scopes.map((s) => scopeToService(s) ?? s),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Check credentials exist
|
|
48
|
+
const creds = await readCredentials();
|
|
49
|
+
if (!creds) {
|
|
50
|
+
throw new AuthError('AUTH_NO_CREDENTIALS', {
|
|
51
|
+
message: 'OAuth client credentials not found at ~/.go-easy/credentials.json',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// Check for pending session
|
|
55
|
+
const pending = await readPending(email);
|
|
56
|
+
if (pending) {
|
|
57
|
+
// Terminal states: return and clean up
|
|
58
|
+
if (pending.status === 'complete') {
|
|
59
|
+
await cleanupPending(email);
|
|
60
|
+
return {
|
|
61
|
+
status: 'complete',
|
|
62
|
+
email: pending.email ?? email,
|
|
63
|
+
scopes: pending.scopes ?? [],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (pending.status === 'partial') {
|
|
67
|
+
await cleanupPending(email);
|
|
68
|
+
return {
|
|
69
|
+
status: 'partial',
|
|
70
|
+
email: pending.email ?? email,
|
|
71
|
+
grantedScopes: pending.grantedScopes ?? [],
|
|
72
|
+
missingScopes: pending.missingScopes ?? [],
|
|
73
|
+
message: pending.message ?? 'Partial authorization',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (pending.status === 'denied') {
|
|
77
|
+
await cleanupPending(email);
|
|
78
|
+
return {
|
|
79
|
+
status: 'denied',
|
|
80
|
+
message: pending.message ?? 'User declined authorization',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (pending.status === 'expired') {
|
|
84
|
+
await cleanupPending(email);
|
|
85
|
+
return {
|
|
86
|
+
status: 'expired',
|
|
87
|
+
message: pending.message ?? 'Authorization timed out',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (pending.status === 'error') {
|
|
91
|
+
await cleanupPending(email);
|
|
92
|
+
return {
|
|
93
|
+
status: 'error',
|
|
94
|
+
message: pending.message ?? 'Unknown error during authorization',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Active session (waiting/started)
|
|
98
|
+
if (pending.status === 'waiting' || pending.status === 'started') {
|
|
99
|
+
// Check if the server process is still alive
|
|
100
|
+
if (pending.pid && isProcessAlive(pending.pid)) {
|
|
101
|
+
// Check if expired by time
|
|
102
|
+
if (pending.expiresAt && new Date(pending.expiresAt) < new Date()) {
|
|
103
|
+
await cleanupPending(email);
|
|
104
|
+
return {
|
|
105
|
+
status: 'expired',
|
|
106
|
+
message: 'Authorization timed out',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// Still waiting — return current state
|
|
110
|
+
const expiresIn = pending.expiresAt
|
|
111
|
+
? Math.max(0, Math.floor((new Date(pending.expiresAt).getTime() - Date.now()) / 1000))
|
|
112
|
+
: 300;
|
|
113
|
+
return {
|
|
114
|
+
status: 'waiting',
|
|
115
|
+
authUrl: pending.authUrl ?? '',
|
|
116
|
+
expiresIn,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// PID is dead — stale session, clean up and restart
|
|
120
|
+
await cleanupPending(email);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Start new auth flow
|
|
124
|
+
return startAuthServer(email);
|
|
125
|
+
}
|
|
126
|
+
// ─── Internal ──────────────────────────────────────────────
|
|
127
|
+
function pendingFilePath(email) {
|
|
128
|
+
return join(getPendingDir(), `${email.toLowerCase().trim()}.json`);
|
|
129
|
+
}
|
|
130
|
+
async function readPending(email) {
|
|
131
|
+
try {
|
|
132
|
+
const raw = await readFile(pendingFilePath(email), 'utf-8');
|
|
133
|
+
return JSON.parse(raw);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function cleanupPending(email) {
|
|
140
|
+
try {
|
|
141
|
+
await unlink(pendingFilePath(email));
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Already deleted or never existed
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function isProcessAlive(pid) {
|
|
148
|
+
try {
|
|
149
|
+
process.kill(pid, 0); // Signal 0 = just check existence
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Spawn the auth-server as a fully detached background process.
|
|
158
|
+
*
|
|
159
|
+
* Uses stdio: 'ignore' so the child has no pipe ties to the parent.
|
|
160
|
+
* The child writes its startup info to the pending/<email>.json file.
|
|
161
|
+
* We poll that file briefly to get the authUrl.
|
|
162
|
+
*/
|
|
163
|
+
async function startAuthServer(email) {
|
|
164
|
+
// Resolve the auth-server script path
|
|
165
|
+
const thisDir = typeof __dirname !== 'undefined'
|
|
166
|
+
? __dirname
|
|
167
|
+
: fileURLToPath(new URL('.', import.meta.url));
|
|
168
|
+
const serverScript = join(thisDir, 'auth-server.js');
|
|
169
|
+
// Verify the script exists
|
|
170
|
+
try {
|
|
171
|
+
await stat(serverScript);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
throw new AuthError('AUTH_ERROR', {
|
|
175
|
+
message: `Auth server script not found: ${serverScript}`,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
// Clean up any old pending file first
|
|
179
|
+
await cleanupPending(email);
|
|
180
|
+
// Spawn fully detached — no pipes, child survives parent exit
|
|
181
|
+
const child = spawn(process.execPath, // node.exe
|
|
182
|
+
[serverScript, email.toLowerCase().trim(), '0'], {
|
|
183
|
+
detached: true,
|
|
184
|
+
stdio: 'ignore',
|
|
185
|
+
windowsHide: true,
|
|
186
|
+
});
|
|
187
|
+
child.unref();
|
|
188
|
+
// Poll the pending file for the server's startup info
|
|
189
|
+
const maxWait = 10_000; // 10s
|
|
190
|
+
const pollInterval = 100; // 100ms
|
|
191
|
+
const start = Date.now();
|
|
192
|
+
while (Date.now() - start < maxWait) {
|
|
193
|
+
await sleep(pollInterval);
|
|
194
|
+
const pending = await readPending(email);
|
|
195
|
+
if (pending && pending.authUrl) {
|
|
196
|
+
return {
|
|
197
|
+
status: 'started',
|
|
198
|
+
authUrl: pending.authUrl,
|
|
199
|
+
expiresIn: pending.expiresAt
|
|
200
|
+
? Math.max(0, Math.floor((new Date(pending.expiresAt).getTime() - Date.now()) / 1000))
|
|
201
|
+
: 300,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
// If the server wrote an error/terminal state, return it
|
|
205
|
+
if (pending && pending.status === 'error') {
|
|
206
|
+
return {
|
|
207
|
+
status: 'error',
|
|
208
|
+
message: pending.message ?? 'Auth server failed to start',
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
throw new AuthError('AUTH_ERROR', {
|
|
213
|
+
message: 'Auth server did not start within 10 seconds',
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
function sleep(ms) {
|
|
217
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=auth-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-flow.js","sourceRoot":"","sources":["../src/auth-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* auth-server — Background loopback OAuth callback server.
|
|
4
|
+
*
|
|
5
|
+
* Spawned as a detached child by auth-flow.ts.
|
|
6
|
+
* Listens on 127.0.0.1:<port>, waits for Google OAuth callback,
|
|
7
|
+
* exchanges code for token, writes result to pending/<email>.json,
|
|
8
|
+
* updates accounts.json.
|
|
9
|
+
*
|
|
10
|
+
* Communicates with parent via:
|
|
11
|
+
* 1. stdout: JSON line with { port, pid, authUrl } (parent reads this, then detaches)
|
|
12
|
+
* 2. pending/<email>.json file (poll target for subsequent CLI calls)
|
|
13
|
+
*
|
|
14
|
+
* Usage (not meant to be called directly):
|
|
15
|
+
* node auth-server.js <email> <port>
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=auth-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-server.d.ts","sourceRoot":"","sources":["../src/auth-server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG"}
|