inboxd 1.0.0 → 1.0.2

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.
@@ -0,0 +1,27 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ contents: read
12
+ id-token: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - uses: actions/setup-node@v4
17
+ with:
18
+ node-version: '20'
19
+ registry-url: 'https://registry.npmjs.org'
20
+
21
+ - run: npm ci
22
+
23
+ - run: npm test
24
+
25
+ - run: npm publish --provenance --access public
26
+ env:
27
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/CLAUDE.md ADDED
@@ -0,0 +1,73 @@
1
+ # inboxd
2
+
3
+ CLI tool for Gmail monitoring with multi-account support and macOS notifications.
4
+
5
+ ## Quick Reference
6
+
7
+ ```bash
8
+ npm test # Run tests
9
+ npm run test:watch # Watch mode
10
+ inbox setup # First-time setup wizard
11
+ inbox auth -a <name> # Add account
12
+ inbox summary # Check all inboxes
13
+ inbox check -q # Background check
14
+ inbox install-service # Install launchd service (macOS only)
15
+ ```
16
+
17
+ ## Architecture
18
+
19
+ ```
20
+ src/
21
+ ├── cli.js # Entry point, command definitions (commander)
22
+ ├── gmail-auth.js # OAuth2 flow, token storage, multi-account management
23
+ ├── gmail-monitor.js # Gmail API: fetch, count, trash, restore
24
+ ├── state.js # Tracks seen emails per account
25
+ ├── deletion-log.js # Logs deleted emails for restore capability
26
+ └── notifier.js # macOS notifications (node-notifier)
27
+
28
+ tests/ # Vitest tests with mocked Google APIs
29
+ __mocks__/ # Manual mocks for googleapis, @google-cloud/local-auth
30
+ ```
31
+
32
+ ## Tech Stack
33
+
34
+ - **Runtime**: Node.js 18+ (CommonJS)
35
+ - **Gmail API**: `googleapis`, `@google-cloud/local-auth`
36
+ - **CLI**: `commander`
37
+ - **UI**: `chalk`, `boxen` (ESM packages, loaded via dynamic import)
38
+ - **Notifications**: `node-notifier`
39
+ - **Testing**: `vitest`
40
+
41
+ ## Data Storage
42
+
43
+ All user data lives in `~/.config/inboxd/`:
44
+
45
+ | File | Content |
46
+ |------|---------|
47
+ | `credentials.json` | OAuth client ID/secret from Google Cloud |
48
+ | `accounts.json` | `[{ name, email }]` for each linked account |
49
+ | `token-<name>.json` | OAuth refresh/access tokens |
50
+ | `state-<name>.json` | `{ seenEmailIds, lastCheck, lastNotifiedAt }` |
51
+ | `deletion-log.json` | Audit log for deleted emails |
52
+
53
+ ## Code Patterns
54
+
55
+ - **CommonJS** for all source files
56
+ - **Dynamic import** for ESM-only deps (`chalk`, `boxen`)
57
+ - **Functional style**, no classes
58
+ - **Retry with backoff** in `gmail-monitor.js` for API calls
59
+ - **Silent fail** for missing config files (returns defaults)
60
+
61
+ ## Key Behaviors
62
+
63
+ - `inbox setup` guides first-time users through credentials and auth
64
+ - `inbox check` marks emails as seen after notifying
65
+ - `inbox delete` logs to `deletion-log.json` before trashing
66
+ - `inbox restore` moves from Trash to Inbox, removes log entry
67
+ - `install-service` generates launchd plist dynamically (macOS only, warns on other platforms)
68
+
69
+ ## OAuth Notes
70
+
71
+ - Gmail API requires OAuth consent screen with test users for external accounts
72
+ - Tokens auto-refresh; delete token file to force re-auth
73
+ - Credentials can be in project root (dev) or `~/.config/inboxd/` (installed)
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Paredes
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,159 @@
1
+ # inboxd
2
+
3
+ [![npm version](https://img.shields.io/npm/v/inboxd.svg)](https://www.npmjs.com/package/inboxd)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A CLI tool for monitoring Gmail inboxes with multi-account support and macOS notifications.
7
+
8
+ ## Features
9
+
10
+ - **Multi-account support** - Monitor multiple Gmail accounts from one command
11
+ - **macOS notifications** - Get notified when new emails arrive
12
+ - **Background monitoring** - Run as a launchd service
13
+ - **Delete & restore** - Safely trash emails with undo capability
14
+ - **AI-ready output** - JSON mode for integration with AI agents
15
+ - **Interactive setup** - Guided wizard for first-time configuration
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install -g inboxd
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ Run the interactive setup wizard:
26
+
27
+ ```bash
28
+ inbox setup
29
+ ```
30
+
31
+ The wizard will guide you through:
32
+ 1. Opening Google Cloud Console to create OAuth credentials
33
+ 2. Validating and installing your credentials file
34
+ 3. Authenticating your first Gmail account
35
+
36
+ That's it! You're ready to go.
37
+
38
+ ## Manual Setup
39
+
40
+ If you prefer to set up manually:
41
+
42
+ ### 1. Get Gmail API Credentials
43
+
44
+ 1. Go to [Google Cloud Console](https://console.cloud.google.com/)
45
+ 2. Create a project and enable the **Gmail API**
46
+ 3. Configure OAuth consent screen (add yourself as test user)
47
+ 4. Create OAuth credentials (Desktop app)
48
+ 5. Download and save as `~/.config/inboxd/credentials.json`:
49
+
50
+ ```bash
51
+ mkdir -p ~/.config/inboxd
52
+ mv ~/Downloads/client_secret_*.json ~/.config/inboxd/credentials.json
53
+ ```
54
+
55
+ ### 2. Authenticate
56
+
57
+ ```bash
58
+ inbox auth --account personal
59
+ ```
60
+
61
+ ### 3. Check Your Inbox
62
+
63
+ ```bash
64
+ inbox summary
65
+ ```
66
+
67
+ ## Commands
68
+
69
+ | Command | Description |
70
+ |---------|-------------|
71
+ | `inbox setup` | Interactive setup wizard |
72
+ | `inbox auth -a <name>` | Authenticate a Gmail account |
73
+ | `inbox accounts` | List configured accounts |
74
+ | `inbox summary` | Show inbox summary for all accounts |
75
+ | `inbox check` | Check for new emails + send notifications |
76
+ | `inbox check -q` | Silent check (for background use) |
77
+ | `inbox delete --ids <ids>` | Move emails to trash |
78
+ | `inbox restore --last 1` | Restore last deleted email |
79
+ | `inbox deletion-log` | View deletion history |
80
+ | `inbox logout --all` | Remove all accounts |
81
+ | `inbox install-service` | Install background monitoring (macOS) |
82
+
83
+ ## Configuration
84
+
85
+ All configuration is stored in `~/.config/inboxd/`:
86
+
87
+ | File | Purpose |
88
+ |------|---------|
89
+ | `credentials.json` | Your OAuth client credentials |
90
+ | `accounts.json` | List of configured accounts |
91
+ | `token-<account>.json` | OAuth tokens per account |
92
+ | `state-<account>.json` | Seen email tracking per account |
93
+ | `deletion-log.json` | Record of deleted emails |
94
+
95
+ ## Background Monitoring
96
+
97
+ Install as a macOS launchd service to check for new emails periodically:
98
+
99
+ ```bash
100
+ # Install with default 5-minute interval
101
+ inbox install-service
102
+
103
+ # Or customize the interval
104
+ inbox install-service --interval 10
105
+ ```
106
+
107
+ Manage the service:
108
+
109
+ ```bash
110
+ # Check status
111
+ launchctl list | grep inboxd
112
+
113
+ # View logs
114
+ tail -f /tmp/inboxd.log
115
+
116
+ # Stop service
117
+ launchctl unload ~/Library/LaunchAgents/com.danielparedes.inboxd.plist
118
+ ```
119
+
120
+ **Note:** `install-service` is macOS-only. For Linux, use cron:
121
+ ```bash
122
+ */5 * * * * /path/to/node /path/to/inbox check --quiet
123
+ ```
124
+
125
+ ## JSON Output
126
+
127
+ For AI agent integration, use the `--json` flag:
128
+
129
+ ```bash
130
+ inbox summary --json
131
+ ```
132
+
133
+ Or use the `analyze` command for structured output:
134
+
135
+ ```bash
136
+ inbox analyze --count 20
137
+ ```
138
+
139
+ ## Troubleshooting
140
+
141
+ **"credentials.json not found"**
142
+ Run `inbox setup` or manually download OAuth credentials from Google Cloud Console and save to `~/.config/inboxd/credentials.json`.
143
+
144
+ **"Token expired"**
145
+ Delete the token file and re-authenticate:
146
+ ```bash
147
+ rm ~/.config/inboxd/token-<account>.json
148
+ inbox auth -a <account>
149
+ ```
150
+
151
+ **No notifications appearing**
152
+ Check macOS notification settings for Terminal/Node.js in System Preferences > Notifications.
153
+
154
+ **"install-service is only supported on macOS"**
155
+ The launchd service is macOS-specific. For other platforms, set up a cron job or scheduled task manually.
156
+
157
+ ## License
158
+
159
+ MIT
@@ -0,0 +1,11 @@
1
+ const { vi } = require('vitest');
2
+
3
+ module.exports = {
4
+ authenticate: vi.fn().mockResolvedValue({
5
+ credentials: {
6
+ refresh_token: 'mock-refresh-token',
7
+ access_token: 'mock-access-token',
8
+ expiry_date: Date.now() + 3600000
9
+ }
10
+ })
11
+ };
@@ -0,0 +1,42 @@
1
+ const { vi } = require('vitest');
2
+
3
+ const mockGmail = {
4
+ users: {
5
+ messages: {
6
+ list: vi.fn().mockResolvedValue({ data: { messages: [] } }),
7
+ get: vi.fn().mockResolvedValue({
8
+ data: {
9
+ id: '123',
10
+ threadId: 'thread123',
11
+ labelIds: ['INBOX'],
12
+ snippet: 'snippet',
13
+ payload: { headers: [] }
14
+ }
15
+ }),
16
+ trash: vi.fn().mockResolvedValue({ data: { id: '123', labelIds: ['TRASH'] } }),
17
+ untrash: vi.fn().mockResolvedValue({ data: { id: '123', labelIds: ['INBOX'] } }),
18
+ modify: vi.fn().mockResolvedValue({ data: { id: '123' } }),
19
+ },
20
+ labels: {
21
+ get: vi.fn().mockResolvedValue({ data: { messagesUnread: 5 } }),
22
+ list: vi.fn().mockResolvedValue({ data: { labels: [] } }),
23
+ },
24
+ getProfile: vi.fn().mockResolvedValue({ data: { emailAddress: 'test@example.com' } }),
25
+ }
26
+ };
27
+
28
+ const mockAuth = {
29
+ fromJSON: vi.fn().mockReturnValue({}),
30
+ OAuth2: vi.fn().mockImplementation(() => ({
31
+ generateAuthUrl: vi.fn().mockReturnValue('http://mock-auth-url'),
32
+ getToken: vi.fn().mockResolvedValue({ tokens: {} }),
33
+ setCredentials: vi.fn(),
34
+ })),
35
+ };
36
+
37
+ module.exports = {
38
+ google: {
39
+ gmail: vi.fn().mockReturnValue(mockGmail),
40
+ auth: mockAuth,
41
+ }
42
+ };
@@ -0,0 +1,75 @@
1
+ /** @type {import('eslint').Linter.Config[]} */
2
+ module.exports = [
3
+ {
4
+ files: ['src/**/*.js'],
5
+ languageOptions: {
6
+ ecmaVersion: 2022,
7
+ sourceType: 'commonjs',
8
+ globals: {
9
+ // Node.js globals
10
+ require: 'readonly',
11
+ module: 'readonly',
12
+ exports: 'readonly',
13
+ __dirname: 'readonly',
14
+ __filename: 'readonly',
15
+ process: 'readonly',
16
+ console: 'readonly',
17
+ Buffer: 'readonly',
18
+ setTimeout: 'readonly',
19
+ clearTimeout: 'readonly',
20
+ setInterval: 'readonly',
21
+ clearInterval: 'readonly',
22
+ },
23
+ },
24
+ rules: {
25
+ // Possible errors
26
+ 'no-undef': 'error',
27
+ 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }],
28
+ 'no-constant-condition': 'error',
29
+ 'no-dupe-keys': 'error',
30
+ 'no-duplicate-case': 'error',
31
+ 'no-empty': ['error', { allowEmptyCatch: true }],
32
+ 'no-extra-semi': 'error',
33
+ 'no-unreachable': 'error',
34
+
35
+ // Best practices
36
+ 'eqeqeq': ['error', 'always', { null: 'ignore' }],
37
+ 'no-eval': 'error',
38
+ 'no-implied-eval': 'error',
39
+ 'no-return-await': 'error',
40
+ 'no-throw-literal': 'error',
41
+ 'prefer-promise-reject-errors': 'error',
42
+
43
+ // Style (minimal)
44
+ 'no-trailing-spaces': 'error',
45
+ 'semi': ['error', 'always'],
46
+ },
47
+ },
48
+ {
49
+ // Test files use ESM via vitest
50
+ files: ['tests/**/*.js', '**/*.test.js'],
51
+ languageOptions: {
52
+ ecmaVersion: 2022,
53
+ sourceType: 'module',
54
+ globals: {
55
+ describe: 'readonly',
56
+ it: 'readonly',
57
+ expect: 'readonly',
58
+ beforeEach: 'readonly',
59
+ afterEach: 'readonly',
60
+ beforeAll: 'readonly',
61
+ afterAll: 'readonly',
62
+ vi: 'readonly',
63
+ jest: 'readonly',
64
+ console: 'readonly',
65
+ process: 'readonly',
66
+ },
67
+ },
68
+ rules: {
69
+ 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }],
70
+ },
71
+ },
72
+ {
73
+ ignores: ['node_modules/**', 'coverage/**', 'dist/**', '__mocks__/**'],
74
+ },
75
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inboxd",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "CLI assistant for Gmail monitoring with multi-account support and AI-ready JSON output",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
@@ -10,9 +10,17 @@
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/dparedesi/inboxd.git"
12
12
  },
13
+ "bugs": {
14
+ "url": "https://github.com/dparedesi/inboxd/issues"
15
+ },
16
+ "homepage": "https://github.com/dparedesi/inboxd#readme",
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
13
20
  "scripts": {
14
21
  "test": "vitest run",
15
22
  "test:watch": "vitest",
23
+ "lint": "eslint src tests",
16
24
  "inbox": "node src/cli.js"
17
25
  },
18
26
  "keywords": [
@@ -35,9 +43,11 @@
35
43
  "chalk": "^5.6.2",
36
44
  "commander": "^14.0.2",
37
45
  "googleapis": "^169.0.0",
38
- "node-notifier": "^10.0.1"
46
+ "node-notifier": "^10.0.1",
47
+ "open": "^10.1.0"
39
48
  },
40
49
  "devDependencies": {
50
+ "eslint": "^9.39.2",
41
51
  "vitest": "^4.0.16"
42
52
  }
43
53
  }