@smcllns/gmail 0.1.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/README.md +164 -0
- package/dist/account-storage.d.ts +22 -0
- package/dist/account-storage.d.ts.map +1 -0
- package/dist/account-storage.js +87 -0
- package/dist/account-storage.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +539 -0
- package/dist/cli.js.map +1 -0
- package/dist/gmail-oauth-flow.d.ts +13 -0
- package/dist/gmail-oauth-flow.d.ts.map +1 -0
- package/dist/gmail-oauth-flow.js +142 -0
- package/dist/gmail-oauth-flow.js.map +1 -0
- package/dist/gmail-service.d.ts +101 -0
- package/dist/gmail-service.d.ts.map +1 -0
- package/dist/gmail-service.js +477 -0
- package/dist/gmail-service.js.map +1 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# @smcllns/gmail
|
|
2
|
+
|
|
3
|
+
A fork of [@mariozechner/gmcli](https://github.com/badlogic/gmcli) designed for use with Claude Code and other AI coding agents.
|
|
4
|
+
|
|
5
|
+
## Why This Fork?
|
|
6
|
+
|
|
7
|
+
The original gmcli is excellent but requests full Gmail access (`mail.google.com` scope). This fork:
|
|
8
|
+
|
|
9
|
+
1. **Narrows OAuth scopes** for safer agent usage:
|
|
10
|
+
- `gmail.readonly` - Read messages, threads, settings
|
|
11
|
+
- `gmail.labels` - Create, update, delete labels
|
|
12
|
+
- No send/compose permissions at the API level
|
|
13
|
+
|
|
14
|
+
2. **Restricts dangerous operations** in the CLI:
|
|
15
|
+
- `send`, `drafts create`, `drafts send`, `drafts delete` are blocked
|
|
16
|
+
- Returns guidance directing users to the Gmail web interface
|
|
17
|
+
- Prevents AI agents from sending emails without human review
|
|
18
|
+
|
|
19
|
+
3. **Simplifies CLI for agent usage**:
|
|
20
|
+
- Renamed binary from `gmcli` to `gmail`
|
|
21
|
+
- Default account config so commands don't require email prefix
|
|
22
|
+
- Usage: `bunx @smcllns/gmail <command>` or `npx @smcllns/gmail <command>`
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -g @smcllns/gmail
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or use directly with bunx/npx:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bunx @smcllns/gmail <command>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Setup
|
|
37
|
+
|
|
38
|
+
### 1. Create OAuth Credentials
|
|
39
|
+
|
|
40
|
+
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
41
|
+
2. Create a new project (or select existing)
|
|
42
|
+
3. Enable the Gmail API
|
|
43
|
+
4. Go to "APIs & Services" → "Credentials"
|
|
44
|
+
5. Create "OAuth client ID" → "Desktop app"
|
|
45
|
+
6. Download the credentials JSON file
|
|
46
|
+
|
|
47
|
+
### 2. Configure the CLI
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Set credentials (once per machine)
|
|
51
|
+
gmail accounts credentials ~/Downloads/credentials.json
|
|
52
|
+
|
|
53
|
+
# Add your Gmail account
|
|
54
|
+
gmail accounts add you@gmail.com
|
|
55
|
+
|
|
56
|
+
# The first account is automatically set as default
|
|
57
|
+
# Or set manually:
|
|
58
|
+
gmail config default you@gmail.com
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
gmail - Gmail CLI for Claude Code agents
|
|
65
|
+
|
|
66
|
+
USAGE
|
|
67
|
+
|
|
68
|
+
gmail accounts <action> Account management
|
|
69
|
+
gmail config <action> Configuration management
|
|
70
|
+
gmail <command> [options] Gmail operations (uses default account)
|
|
71
|
+
gmail --account <email> <command> Gmail operations with specific account
|
|
72
|
+
|
|
73
|
+
ACCOUNT COMMANDS
|
|
74
|
+
|
|
75
|
+
gmail accounts credentials <file.json> Set OAuth credentials (once)
|
|
76
|
+
gmail accounts list List configured accounts
|
|
77
|
+
gmail accounts add <email> [--manual] Add account (--manual for browserless OAuth)
|
|
78
|
+
gmail accounts remove <email> Remove account
|
|
79
|
+
|
|
80
|
+
CONFIG COMMANDS
|
|
81
|
+
|
|
82
|
+
gmail config default <email> Set default account for all commands
|
|
83
|
+
gmail config show Show current configuration
|
|
84
|
+
|
|
85
|
+
GMAIL COMMANDS
|
|
86
|
+
|
|
87
|
+
gmail search <query> [--max N] [--page TOKEN]
|
|
88
|
+
Search threads using Gmail query syntax.
|
|
89
|
+
Returns: thread ID, date, sender, subject, labels.
|
|
90
|
+
|
|
91
|
+
Query examples:
|
|
92
|
+
in:inbox, in:sent, in:drafts, in:trash
|
|
93
|
+
is:unread, is:starred, is:important
|
|
94
|
+
from:sender@example.com, to:recipient@example.com
|
|
95
|
+
subject:keyword, has:attachment, filename:pdf
|
|
96
|
+
after:2024/01/01, before:2024/12/31
|
|
97
|
+
label:Work, label:UNREAD
|
|
98
|
+
Combine: "in:inbox is:unread from:boss@company.com"
|
|
99
|
+
|
|
100
|
+
gmail thread <threadId> [--download]
|
|
101
|
+
Get thread with all messages.
|
|
102
|
+
Shows: Message-ID, headers, body, attachments.
|
|
103
|
+
--download saves attachments to ~/.gmail-cli/attachments/
|
|
104
|
+
|
|
105
|
+
gmail labels list
|
|
106
|
+
List all labels with ID, name, and type.
|
|
107
|
+
|
|
108
|
+
gmail labels <threadIds...> [--add L] [--remove L]
|
|
109
|
+
Modify labels on threads (comma-separated for multiple).
|
|
110
|
+
Accepts label names or IDs (names are case-insensitive).
|
|
111
|
+
System labels: INBOX, UNREAD, STARRED, IMPORTANT, TRASH, SPAM
|
|
112
|
+
|
|
113
|
+
gmail drafts list
|
|
114
|
+
List all drafts. Returns: draft ID, message ID.
|
|
115
|
+
|
|
116
|
+
gmail drafts get <draftId> [--download]
|
|
117
|
+
View draft with attachments.
|
|
118
|
+
--download saves attachments to ~/.gmail-cli/attachments/
|
|
119
|
+
|
|
120
|
+
gmail url <threadIds...>
|
|
121
|
+
Generate Gmail web URLs for threads.
|
|
122
|
+
Uses canonical URL format with email parameter.
|
|
123
|
+
|
|
124
|
+
RESTRICTED OPERATIONS
|
|
125
|
+
|
|
126
|
+
gmail send - Returns guidance to use Gmail web interface
|
|
127
|
+
gmail drafts create - Returns guidance to use Gmail web interface
|
|
128
|
+
gmail drafts send - Returns guidance to use Gmail web interface
|
|
129
|
+
gmail drafts delete - Returns guidance to use Gmail web interface
|
|
130
|
+
|
|
131
|
+
EXAMPLES
|
|
132
|
+
|
|
133
|
+
gmail search "in:inbox is:unread"
|
|
134
|
+
gmail search "from:boss@company.com" --max 50
|
|
135
|
+
gmail thread 19aea1f2f3532db5
|
|
136
|
+
gmail thread 19aea1f2f3532db5 --download
|
|
137
|
+
gmail labels list
|
|
138
|
+
gmail labels abc123 --add Work --remove UNREAD
|
|
139
|
+
gmail url 19aea1f2f3532db5
|
|
140
|
+
|
|
141
|
+
DATA STORAGE
|
|
142
|
+
|
|
143
|
+
~/.gmail-cli/credentials.json OAuth client credentials
|
|
144
|
+
~/.gmail-cli/accounts.json Account tokens
|
|
145
|
+
~/.gmail-cli/config.json CLI configuration (default account)
|
|
146
|
+
~/.gmail-cli/attachments/ Downloaded attachments
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Key Differences from Upstream
|
|
150
|
+
|
|
151
|
+
| Feature | @mariozechner/gmcli | @smcllns/gmail |
|
|
152
|
+
|---------|---------------------|----------------|
|
|
153
|
+
| OAuth scopes | Full access (`mail.google.com`) | Read-only + labels |
|
|
154
|
+
| Send email | ✅ Supported | ❌ Blocked (returns guidance) |
|
|
155
|
+
| Create drafts | ✅ Supported | ❌ Blocked |
|
|
156
|
+
| Send drafts | ✅ Supported | ❌ Blocked |
|
|
157
|
+
| Delete drafts | ✅ Supported | ❌ Blocked |
|
|
158
|
+
| Binary name | `gmcli` | `gmail` |
|
|
159
|
+
| Default account | Not supported | ✅ `gmail config default` |
|
|
160
|
+
| Data directory | `~/.gmcli/` | `~/.gmail-cli/` |
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
MIT
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { EmailAccount } from "./types.js";
|
|
2
|
+
export declare class AccountStorage {
|
|
3
|
+
private accounts;
|
|
4
|
+
constructor();
|
|
5
|
+
private ensureConfigDir;
|
|
6
|
+
private loadAccounts;
|
|
7
|
+
private saveAccounts;
|
|
8
|
+
addAccount(account: EmailAccount): void;
|
|
9
|
+
getAccount(email: string): EmailAccount | undefined;
|
|
10
|
+
getAllAccounts(): EmailAccount[];
|
|
11
|
+
deleteAccount(email: string): boolean;
|
|
12
|
+
hasAccount(email: string): boolean;
|
|
13
|
+
setCredentials(clientId: string, clientSecret: string): void;
|
|
14
|
+
getCredentials(): {
|
|
15
|
+
clientId: string;
|
|
16
|
+
clientSecret: string;
|
|
17
|
+
} | null;
|
|
18
|
+
setDefaultAccount(email: string): void;
|
|
19
|
+
getDefaultAccount(): string | null;
|
|
20
|
+
private loadConfig;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=account-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-storage.d.ts","sourceRoot":"","sources":["../src/account-storage.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAO/C,qBAAa,cAAc;IAC1B,OAAO,CAAC,QAAQ,CAAwC;;IAOxD,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,YAAY;IAIpB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAKvC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAInD,cAAc,IAAI,YAAY,EAAE;IAIhC,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAMrC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIlC,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAI5D,cAAc,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IASnE,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAMtC,iBAAiB,IAAI,MAAM,GAAG,IAAI;IAKlC,OAAO,CAAC,UAAU;CAQlB"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
const CONFIG_DIR = path.join(os.homedir(), ".gmail-cli");
|
|
5
|
+
const ACCOUNTS_FILE = path.join(CONFIG_DIR, "accounts.json");
|
|
6
|
+
const CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
|
|
7
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
8
|
+
export class AccountStorage {
|
|
9
|
+
accounts = new Map();
|
|
10
|
+
constructor() {
|
|
11
|
+
this.ensureConfigDir();
|
|
12
|
+
this.loadAccounts();
|
|
13
|
+
}
|
|
14
|
+
ensureConfigDir() {
|
|
15
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
16
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
loadAccounts() {
|
|
20
|
+
if (fs.existsSync(ACCOUNTS_FILE)) {
|
|
21
|
+
try {
|
|
22
|
+
const data = JSON.parse(fs.readFileSync(ACCOUNTS_FILE, "utf8"));
|
|
23
|
+
for (const account of data) {
|
|
24
|
+
this.accounts.set(account.email, account);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Ignore
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
saveAccounts() {
|
|
33
|
+
fs.writeFileSync(ACCOUNTS_FILE, JSON.stringify(Array.from(this.accounts.values()), null, 2));
|
|
34
|
+
}
|
|
35
|
+
addAccount(account) {
|
|
36
|
+
this.accounts.set(account.email, account);
|
|
37
|
+
this.saveAccounts();
|
|
38
|
+
}
|
|
39
|
+
getAccount(email) {
|
|
40
|
+
return this.accounts.get(email);
|
|
41
|
+
}
|
|
42
|
+
getAllAccounts() {
|
|
43
|
+
return Array.from(this.accounts.values());
|
|
44
|
+
}
|
|
45
|
+
deleteAccount(email) {
|
|
46
|
+
const deleted = this.accounts.delete(email);
|
|
47
|
+
if (deleted)
|
|
48
|
+
this.saveAccounts();
|
|
49
|
+
return deleted;
|
|
50
|
+
}
|
|
51
|
+
hasAccount(email) {
|
|
52
|
+
return this.accounts.has(email);
|
|
53
|
+
}
|
|
54
|
+
setCredentials(clientId, clientSecret) {
|
|
55
|
+
fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify({ clientId, clientSecret }, null, 2));
|
|
56
|
+
}
|
|
57
|
+
getCredentials() {
|
|
58
|
+
if (!fs.existsSync(CREDENTIALS_FILE))
|
|
59
|
+
return null;
|
|
60
|
+
try {
|
|
61
|
+
return JSON.parse(fs.readFileSync(CREDENTIALS_FILE, "utf8"));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
setDefaultAccount(email) {
|
|
68
|
+
const config = this.loadConfig();
|
|
69
|
+
config.defaultAccount = email;
|
|
70
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
71
|
+
}
|
|
72
|
+
getDefaultAccount() {
|
|
73
|
+
const config = this.loadConfig();
|
|
74
|
+
return config.defaultAccount || null;
|
|
75
|
+
}
|
|
76
|
+
loadConfig() {
|
|
77
|
+
if (!fs.existsSync(CONFIG_FILE))
|
|
78
|
+
return {};
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8"));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return {};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=account-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-storage.js","sourceRoot":"","sources":["../src/account-storage.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|