@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 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,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}