pdauth 1.0.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.
Files changed (60) hide show
  1. package/PROXY-SPEC.md +159 -0
  2. package/README.md +142 -0
  3. package/bin/ga.mjs +329 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +88 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/apps.d.ts +8 -0
  9. package/dist/commands/apps.d.ts.map +1 -0
  10. package/dist/commands/apps.js +76 -0
  11. package/dist/commands/apps.js.map +1 -0
  12. package/dist/commands/config.d.ts +5 -0
  13. package/dist/commands/config.d.ts.map +1 -0
  14. package/dist/commands/config.js +58 -0
  15. package/dist/commands/config.js.map +1 -0
  16. package/dist/commands/connect.d.ts +6 -0
  17. package/dist/commands/connect.d.ts.map +1 -0
  18. package/dist/commands/connect.js +67 -0
  19. package/dist/commands/connect.js.map +1 -0
  20. package/dist/commands/index.d.ts +7 -0
  21. package/dist/commands/index.d.ts.map +1 -0
  22. package/dist/commands/index.js +7 -0
  23. package/dist/commands/index.js.map +1 -0
  24. package/dist/commands/proxy.d.ts +12 -0
  25. package/dist/commands/proxy.d.ts.map +1 -0
  26. package/dist/commands/proxy.js +127 -0
  27. package/dist/commands/proxy.js.map +1 -0
  28. package/dist/commands/status.d.ts +10 -0
  29. package/dist/commands/status.d.ts.map +1 -0
  30. package/dist/commands/status.js +117 -0
  31. package/dist/commands/status.js.map +1 -0
  32. package/dist/commands/tools.d.ts +10 -0
  33. package/dist/commands/tools.d.ts.map +1 -0
  34. package/dist/commands/tools.js +123 -0
  35. package/dist/commands/tools.js.map +1 -0
  36. package/dist/config.d.ts +21 -0
  37. package/dist/config.d.ts.map +1 -0
  38. package/dist/config.js +59 -0
  39. package/dist/config.js.map +1 -0
  40. package/dist/index.d.ts +4 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +6 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/pipedream.d.ts +52 -0
  45. package/dist/pipedream.d.ts.map +1 -0
  46. package/dist/pipedream.js +142 -0
  47. package/dist/pipedream.js.map +1 -0
  48. package/package.json +46 -0
  49. package/src/cli.ts +111 -0
  50. package/src/commands/apps.ts +88 -0
  51. package/src/commands/config.ts +68 -0
  52. package/src/commands/connect.ts +76 -0
  53. package/src/commands/index.ts +6 -0
  54. package/src/commands/proxy.ts +155 -0
  55. package/src/commands/status.ts +143 -0
  56. package/src/commands/tools.ts +150 -0
  57. package/src/config.ts +78 -0
  58. package/src/index.ts +6 -0
  59. package/src/pipedream.ts +216 -0
  60. package/tsconfig.json +20 -0
package/PROXY-SPEC.md ADDED
@@ -0,0 +1,159 @@
1
+ # pdauth proxy Command Specification
2
+
3
+ ## Overview
4
+
5
+ Add a `proxy` command to pdauth that allows making authenticated HTTP requests to any Pipedream-connected API using the SDK's built-in proxy feature.
6
+
7
+ ## SDK Already Has Proxy
8
+
9
+ The `@pipedream/sdk` has a `client.proxy` object with these methods:
10
+ - `client.proxy.get(request)`
11
+ - `client.proxy.post(request)`
12
+ - `client.proxy.put(request)`
13
+ - `client.proxy.delete(request)`
14
+ - `client.proxy.patch(request)`
15
+
16
+ Each takes:
17
+ ```typescript
18
+ {
19
+ url: string; // Target API URL (relative for dynamic domain apps)
20
+ externalUserId: string; // User ID
21
+ accountId: string; // Account ID (e.g., "apn_1234567")
22
+ params?: Record<string, string>; // Query params
23
+ headers?: Record<string, string>; // Custom headers
24
+ body?: Record<string, unknown>; // Request body (POST/PUT/PATCH)
25
+ }
26
+ ```
27
+
28
+ ## Command Interface
29
+
30
+ ```bash
31
+ pdauth proxy <app> <path> [options]
32
+
33
+ Arguments:
34
+ app - App slug (e.g., zoho_mail, slack, notion)
35
+ path - API path (e.g., /api/accounts, /api/accounts/123/folders)
36
+
37
+ Options:
38
+ -X, --method <method> HTTP method (GET|POST|PUT|DELETE|PATCH) [default: GET]
39
+ -u, --user <userId> External user ID [default: from config]
40
+ -d, --data <json> Request body as JSON string
41
+ -H, --header <header> Custom header (can be repeated: -H "X-Foo: bar" -H "X-Baz: qux")
42
+ -q, --query <param> Query parameter (can be repeated: -q "limit=10" -q "offset=0")
43
+ -j, --json Output as JSON (default: pretty print)
44
+ -v, --verbose Show request details
45
+ ```
46
+
47
+ ## Examples
48
+
49
+ ```bash
50
+ # GET request
51
+ pdauth proxy zoho_mail /api/accounts --user telegram:5439689035
52
+
53
+ # GET with query params
54
+ pdauth proxy zoho_mail /api/accounts/123/folders -q "limit=50" --user telegram:5439689035
55
+
56
+ # POST with JSON body
57
+ pdauth proxy zoho_mail /api/accounts/123/folders \
58
+ -X POST \
59
+ -d '{"folderName": "Projects"}' \
60
+ --user telegram:5439689035
61
+
62
+ # PUT with headers
63
+ pdauth proxy slack /api/chat.postMessage \
64
+ -X POST \
65
+ -d '{"channel": "C123", "text": "Hello"}' \
66
+ -H "Content-Type: application/json" \
67
+ --user telegram:5439689035
68
+ ```
69
+
70
+ ## Implementation Steps
71
+
72
+ 1. Create `src/commands/proxy.ts`:
73
+ - Import getClient from pipedream.ts
74
+ - Import listAccounts to find account ID for app + user
75
+ - Parse CLI arguments
76
+ - Call appropriate proxy method (get/post/put/delete/patch)
77
+ - Output response
78
+
79
+ 2. Add to `src/commands/index.ts`:
80
+ - Export registerProxyCommand
81
+
82
+ 3. Add to `src/cli.ts`:
83
+ - Register the proxy command
84
+
85
+ ## Code Structure
86
+
87
+ ```typescript
88
+ // src/commands/proxy.ts
89
+ import { Command } from 'commander';
90
+ import { getClient, listAccounts } from '../pipedream.js';
91
+
92
+ export function registerProxyCommand(program: Command): void {
93
+ program
94
+ .command('proxy <app> <path>')
95
+ .description('Make authenticated API request via Pipedream proxy')
96
+ .option('-X, --method <method>', 'HTTP method', 'GET')
97
+ .option('-u, --user <userId>', 'External user ID')
98
+ .option('-d, --data <json>', 'Request body JSON')
99
+ .option('-H, --header <header>', 'Custom header', collect, [])
100
+ .option('-q, --query <param>', 'Query parameter', collect, [])
101
+ .option('-j, --json', 'JSON output')
102
+ .option('-v, --verbose', 'Verbose output')
103
+ .action(async (app, path, options) => {
104
+ // 1. Get account ID for app + user
105
+ const accounts = await listAccounts(options.user);
106
+ const account = accounts.find(a => a.app.nameSlug === app);
107
+ if (!account) throw new Error(`No ${app} account connected for user`);
108
+
109
+ // 2. Build request
110
+ const client = getClient();
111
+ const request = {
112
+ url: path, // relative path for dynamic domain apps
113
+ externalUserId: options.user,
114
+ accountId: account.id,
115
+ params: parseParams(options.query),
116
+ headers: parseHeaders(options.header),
117
+ body: options.data ? JSON.parse(options.data) : undefined,
118
+ };
119
+
120
+ // 3. Call proxy
121
+ const method = options.method.toLowerCase();
122
+ const response = await client.proxy[method](request);
123
+
124
+ // 4. Output
125
+ console.log(options.json ? JSON.stringify(response) : response);
126
+ });
127
+ }
128
+
129
+ function collect(value: string, previous: string[]) {
130
+ return previous.concat([value]);
131
+ }
132
+
133
+ function parseParams(params: string[]): Record<string, string> {
134
+ const result: Record<string, string> = {};
135
+ for (const p of params) {
136
+ const [key, ...rest] = p.split('=');
137
+ result[key] = rest.join('=');
138
+ }
139
+ return result;
140
+ }
141
+
142
+ function parseHeaders(headers: string[]): Record<string, string> {
143
+ const result: Record<string, string> = {};
144
+ for (const h of headers) {
145
+ const [key, ...rest] = h.split(':');
146
+ result[key.trim()] = rest.join(':').trim();
147
+ }
148
+ return result;
149
+ }
150
+ ```
151
+
152
+ ## Testing
153
+
154
+ ```bash
155
+ # After implementation, test with:
156
+ pdauth proxy zoho_mail /api/accounts --user telegram:5439689035 -v
157
+
158
+ # Expected: List of Zoho Mail accounts
159
+ ```
package/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # šŸ” pdauth
2
+
3
+ **Pipedream OAuth CLI** — Give AI agents access to 2500+ APIs via dynamic OAuth
4
+
5
+ ```
6
+ Agent: "I need Spotify access to manage your playlist"
7
+ ↓
8
+ $ pdauth connect spotify
9
+ ↓
10
+ šŸ”— https://pipedream.com/_static/connect.html?token=ctok_xxx&app=spotify
11
+ ↓
12
+ User clicks → OAuth flow → Done
13
+ ↓
14
+ Agent can now call Spotify tools via MCP
15
+ ```
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install -g pdauth
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ### 1. Configure Pipedream credentials
26
+
27
+ Get your credentials at https://pipedream.com/settings/api
28
+
29
+ ```bash
30
+ pdauth config
31
+ ```
32
+
33
+ Or set directly:
34
+ ```bash
35
+ pdauth config --set clientId=YOUR_CLIENT_ID
36
+ pdauth config --set clientSecret=YOUR_CLIENT_SECRET
37
+ pdauth config --set projectId=YOUR_PROJECT_ID
38
+ ```
39
+
40
+ ### 2. Generate OAuth link for any app
41
+
42
+ ```bash
43
+ # Generate link for Spotify
44
+ pdauth connect spotify
45
+
46
+ # Generate for a specific user
47
+ pdauth connect google_sheets --user pedro@example.com
48
+
49
+ # Copy to clipboard
50
+ pdauth connect slack --copy
51
+ ```
52
+
53
+ ### 3. Check connection status
54
+
55
+ ```bash
56
+ # Show connected accounts
57
+ pdauth status
58
+
59
+ # Show all users
60
+ pdauth status --all
61
+
62
+ # JSON output for scripts
63
+ pdauth status --json
64
+ ```
65
+
66
+ ### 4. Use connected tools
67
+
68
+ ```bash
69
+ # List available tools for an app
70
+ pdauth tools slack
71
+
72
+ # Call a tool
73
+ pdauth call slack.send_message channel=general text="Hello from AI!"
74
+
75
+ # Call with JSON args
76
+ pdauth call notion.create_page --args '{"title": "New Page", "parent_id": "..."}'
77
+ ```
78
+
79
+ ## Commands
80
+
81
+ | Command | Description |
82
+ |---------|-------------|
83
+ | `pdauth config` | Configure Pipedream credentials |
84
+ | `pdauth apps [--search <query>]` | List available apps (2500+) |
85
+ | `pdauth app <slug>` | Get info about an app |
86
+ | `pdauth connect <app>` | Generate OAuth link |
87
+ | `pdauth status` | List connected accounts |
88
+ | `pdauth disconnect <app>` | Revoke app access |
89
+ | `pdauth tools <app>` | List MCP tools for an app |
90
+ | `pdauth call <app.tool>` | Invoke an MCP tool |
91
+
92
+ ## Options
93
+
94
+ All commands support:
95
+ - `-u, --user <id>` — Specify user ID (default: "default")
96
+ - `-j, --json` — Output as JSON
97
+
98
+ ## How It Works
99
+
100
+ 1. **You run** `pdauth connect <app>` → generates a secure OAuth link
101
+ 2. **User clicks** the link → Pipedream handles OAuth flow
102
+ 3. **Credentials stored** securely in Pipedream (not locally)
103
+ 4. **Agent calls tools** via MCP using `pdauth call` or direct MCP
104
+
105
+ Powered by [Pipedream Connect](https://pipedream.com/docs/connect) — managed OAuth for 2500+ APIs.
106
+
107
+ ## For AI Agents (OpenClaw)
108
+
109
+ This CLI is designed for AI agents to request and use OAuth access dynamically:
110
+
111
+ ```
112
+ User: "Add this song to my Spotify playlist"
113
+
114
+ Agent: I need Spotify access. Here's a link to authorize:
115
+ https://pipedream.com/_static/connect.html?token=...
116
+
117
+ User: *clicks, authorizes*
118
+
119
+ Agent: *calls pdauth call spotify.add_to_playlist ...*
120
+ āœ“ Added "Song Name" to your playlist!
121
+ ```
122
+
123
+ ## Environment Variables
124
+
125
+ Instead of `pdauth config`, you can use environment variables:
126
+
127
+ ```bash
128
+ export PIPEDREAM_CLIENT_ID=your_client_id
129
+ export PIPEDREAM_CLIENT_SECRET=your_client_secret
130
+ export PIPEDREAM_PROJECT_ID=your_project_id
131
+ export PIPEDREAM_ENVIRONMENT=development # or production
132
+ ```
133
+
134
+ ## Links
135
+
136
+ - [Pipedream MCP](https://mcp.pipedream.com) — Browse all available apps
137
+ - [Pipedream Connect Docs](https://pipedream.com/docs/connect)
138
+ - [OpenClaw](https://openclaw.ai) — AI agent platform
139
+
140
+ ## License
141
+
142
+ MIT Ā© Versatly
package/bin/ga.mjs ADDED
@@ -0,0 +1,329 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ga - Google Analytics CLI powered by pdauth
5
+ *
6
+ * Usage:
7
+ * ga properties List all properties
8
+ * ga report <property> 30-day overview
9
+ * ga traffic <property> Traffic sources breakdown
10
+ * ga devices <property> Device breakdown
11
+ * ga pages <property> Top pages
12
+ * ga compare <property> Compare periods
13
+ * ga franchise All Hale franchises overview
14
+ */
15
+
16
+ import { execSync } from 'child_process';
17
+
18
+ const USER = 'telegram:5439689035';
19
+
20
+ function pdauthCall(instruction) {
21
+ const args = JSON.stringify({ instruction });
22
+ const cmd = `pdauth call google_analytics.google_analytics-run-report-in-ga4 --user ${USER} --args '${args}' --json`;
23
+
24
+ try {
25
+ const result = execSync(cmd, { encoding: 'utf-8', timeout: 120000 });
26
+ const parsed = JSON.parse(result);
27
+ return parsed?.content?.[0]?.text || 'No data';
28
+ } catch (e) {
29
+ return `Error: ${e.message}`;
30
+ }
31
+ }
32
+
33
+ function printHeader(title) {
34
+ console.log('\n' + '═'.repeat(60));
35
+ console.log(` šŸ“Š ${title}`);
36
+ console.log('═'.repeat(60) + '\n');
37
+ }
38
+
39
+ import { writeFileSync, existsSync, readFileSync } from 'fs';
40
+ import { join } from 'path';
41
+
42
+ const ALERTS_FILE = join(process.env.HOME, '.config/pdauth/ga-alerts.json');
43
+
44
+ function loadAlerts() {
45
+ if (existsSync(ALERTS_FILE)) {
46
+ return JSON.parse(readFileSync(ALERTS_FILE, 'utf-8'));
47
+ }
48
+ return { alerts: [] };
49
+ }
50
+
51
+ function saveAlerts(data) {
52
+ writeFileSync(ALERTS_FILE, JSON.stringify(data, null, 2));
53
+ }
54
+
55
+ const commands = {
56
+ properties: () => {
57
+ printHeader('Google Analytics Properties');
58
+ const result = pdauthCall('List ALL available Google Analytics properties I have access to. Show property names and IDs in a clean list format.');
59
+ console.log(result);
60
+ },
61
+
62
+ report: (property) => {
63
+ if (!property) {
64
+ console.log('Usage: ga report <property-name-or-id>');
65
+ process.exit(1);
66
+ }
67
+ printHeader(`30-Day Report: ${property}`);
68
+ const result = pdauthCall(`For property "${property}", get a comprehensive 30-day report showing: total users, new users, sessions, page views, and average session duration. Format as a clean summary with numbers.`);
69
+ console.log(result);
70
+ },
71
+
72
+ traffic: (property) => {
73
+ if (!property) {
74
+ console.log('Usage: ga traffic <property-name-or-id>');
75
+ process.exit(1);
76
+ }
77
+ printHeader(`Traffic Sources: ${property}`);
78
+ const result = pdauthCall(`For property "${property}", get sessions breakdown by traffic source (sessionSource) for the last 30 days. Show top 15 sources with users, sessions, and pageviews. Format as a table.`);
79
+ console.log(result);
80
+ },
81
+
82
+ devices: (property) => {
83
+ if (!property) {
84
+ console.log('Usage: ga devices <property-name-or-id>');
85
+ process.exit(1);
86
+ }
87
+ printHeader(`Device Breakdown: ${property}`);
88
+ const result = pdauthCall(`For property "${property}", get sessions breakdown by device category (desktop, mobile, tablet) for the last 30 days. Show users, sessions, and percentage for each.`);
89
+ console.log(result);
90
+ },
91
+
92
+ pages: (property) => {
93
+ if (!property) {
94
+ console.log('Usage: ga pages <property-name-or-id>');
95
+ process.exit(1);
96
+ }
97
+ printHeader(`Top Pages: ${property}`);
98
+ const result = pdauthCall(`For property "${property}", get the top 20 pages by pageviews for the last 30 days. Show page path, pageviews, and users.`);
99
+ console.log(result);
100
+ },
101
+
102
+ countries: (property) => {
103
+ if (!property) {
104
+ console.log('Usage: ga countries <property-name-or-id>');
105
+ process.exit(1);
106
+ }
107
+ printHeader(`Countries: ${property}`);
108
+ const result = pdauthCall(`For property "${property}", get sessions breakdown by country for the last 30 days. Show top 15 countries with users and sessions.`);
109
+ console.log(result);
110
+ },
111
+
112
+ compare: (property) => {
113
+ if (!property) {
114
+ console.log('Usage: ga compare <property-name-or-id>');
115
+ process.exit(1);
116
+ }
117
+ printHeader(`Period Comparison: ${property}`);
118
+ const result = pdauthCall(`For property "${property}", compare the last 30 days vs the previous 30 days. Show users, sessions, and pageviews for both periods with percentage change.`);
119
+ console.log(result);
120
+ },
121
+
122
+ franchise: () => {
123
+ printHeader('Hale Pet Door Franchise Overview');
124
+ console.log('Fetching data for all franchises (this may take a moment)...\n');
125
+
126
+ const franchises = [
127
+ { name: 'Hale Pet Door (Main)', id: '494080031' },
128
+ { name: 'HPD Orlando', id: '494128845' },
129
+ { name: 'Denver', id: '398496582' },
130
+ { name: 'Atlanta', id: '393738911' },
131
+ { name: 'San Antonio', id: '392790632' },
132
+ { name: 'Twin Cities', id: '392331022' },
133
+ { name: 'Boise', id: '393448653' },
134
+ ];
135
+
136
+ const result = pdauthCall(`Get a quick summary for these GA4 properties showing total users and sessions for the last 30 days: ${franchises.map(f => `${f.name} (ID: ${f.id})`).join(', ')}. Format as a comparison table.`);
137
+ console.log(result);
138
+ },
139
+
140
+ export: (property) => {
141
+ if (!property) {
142
+ console.log('Usage: ga export <property-name-or-id> [filename]');
143
+ process.exit(1);
144
+ }
145
+ const filename = `ga-export-${Date.now()}.csv`;
146
+ printHeader(`Exporting: ${property}`);
147
+
148
+ const result = pdauthCall(`For property "${property}", get the last 30 days data with: date, users, sessions, pageviews, bounceRate. Format the output as CSV with headers. One row per day.`);
149
+
150
+ // Try to extract CSV-like content
151
+ console.log('Raw data:\n');
152
+ console.log(result);
153
+
154
+ // Save to file
155
+ const filepath = join(process.cwd(), filename);
156
+ writeFileSync(filepath, result);
157
+ console.log(`\nāœ… Saved to: ${filepath}`);
158
+ },
159
+
160
+ 'export-all': () => {
161
+ printHeader('Exporting All Properties');
162
+ const timestamp = new Date().toISOString().split('T')[0];
163
+ const filename = `ga-franchise-export-${timestamp}.csv`;
164
+
165
+ const result = pdauthCall(`For ALL Hale Pet Door properties I have access to, get total users and sessions for the last 30 days. Format as CSV with columns: property_name, property_id, users, sessions. Include all 22 properties.`);
166
+
167
+ console.log(result);
168
+
169
+ const filepath = join(process.cwd(), filename);
170
+ writeFileSync(filepath, result);
171
+ console.log(`\nāœ… Saved to: ${filepath}`);
172
+ },
173
+
174
+ 'alert-add': (args) => {
175
+ const parts = args.split(' ');
176
+ if (parts.length < 3) {
177
+ console.log('Usage: ga alert-add <property> <metric> <threshold>');
178
+ console.log('Example: ga alert-add 494080031 users-drop 20%');
179
+ console.log('Example: ga alert-add "Hale Pet Door" sessions-below 100');
180
+ process.exit(1);
181
+ }
182
+
183
+ const threshold = parts.pop();
184
+ const metric = parts.pop();
185
+ const property = parts.join(' ');
186
+
187
+ const data = loadAlerts();
188
+ const alert = {
189
+ id: Date.now(),
190
+ property,
191
+ metric,
192
+ threshold,
193
+ created: new Date().toISOString(),
194
+ enabled: true
195
+ };
196
+ data.alerts.push(alert);
197
+ saveAlerts(data);
198
+
199
+ printHeader('Alert Created');
200
+ console.log(` Property: ${property}`);
201
+ console.log(` Metric: ${metric}`);
202
+ console.log(` Threshold: ${threshold}`);
203
+ console.log(` ID: ${alert.id}`);
204
+ console.log('\nāœ… Alert saved! Run "ga alert-check" to test.');
205
+ },
206
+
207
+ 'alert-list': () => {
208
+ printHeader('Configured Alerts');
209
+ const data = loadAlerts();
210
+
211
+ if (data.alerts.length === 0) {
212
+ console.log('No alerts configured.');
213
+ console.log('Run: ga alert-add <property> <metric> <threshold>');
214
+ return;
215
+ }
216
+
217
+ data.alerts.forEach((alert, i) => {
218
+ const status = alert.enabled ? 'āœ…' : 'āŒ';
219
+ console.log(`${i + 1}. ${status} ${alert.property}`);
220
+ console.log(` Metric: ${alert.metric} | Threshold: ${alert.threshold}`);
221
+ console.log(` ID: ${alert.id}`);
222
+ console.log('');
223
+ });
224
+ },
225
+
226
+ 'alert-check': () => {
227
+ printHeader('Checking Alerts');
228
+ const data = loadAlerts();
229
+
230
+ if (data.alerts.length === 0) {
231
+ console.log('No alerts to check.');
232
+ return;
233
+ }
234
+
235
+ data.alerts.filter(a => a.enabled).forEach(alert => {
236
+ console.log(`\nšŸ” Checking: ${alert.property} (${alert.metric} ${alert.threshold})`);
237
+
238
+ const result = pdauthCall(`For property "${alert.property}", check if ${alert.metric} has crossed threshold ${alert.threshold} in the last 7 days compared to previous 7 days. Give a brief YES/NO answer with the actual numbers.`);
239
+
240
+ console.log(result);
241
+ });
242
+ },
243
+
244
+ 'alert-remove': (id) => {
245
+ if (!id) {
246
+ console.log('Usage: ga alert-remove <alert-id>');
247
+ process.exit(1);
248
+ }
249
+
250
+ const data = loadAlerts();
251
+ const before = data.alerts.length;
252
+ data.alerts = data.alerts.filter(a => String(a.id) !== String(id));
253
+
254
+ if (data.alerts.length === before) {
255
+ console.log(`Alert ${id} not found.`);
256
+ process.exit(1);
257
+ }
258
+
259
+ saveAlerts(data);
260
+ console.log(`āœ… Alert ${id} removed.`);
261
+ },
262
+
263
+ realtime: (property) => {
264
+ if (!property) {
265
+ console.log('Usage: ga realtime <property>');
266
+ process.exit(1);
267
+ }
268
+ printHeader(`Realtime: ${property}`);
269
+ console.log('āš ļø Note: GA4 realtime requires special API access.');
270
+ const result = pdauthCall(`For property "${property}", get the most recent data available - today's users and sessions if possible, otherwise the last 24 hours.`);
271
+ console.log(result);
272
+ },
273
+
274
+ help: () => {
275
+ console.log(`
276
+ šŸ“Š ga - Google Analytics CLI
277
+
278
+ USAGE:
279
+ ga <command> [property]
280
+
281
+ COMMANDS:
282
+ properties List all available properties
283
+ report <prop> 30-day overview (users, sessions, pageviews)
284
+ traffic <prop> Traffic sources breakdown
285
+ devices <prop> Desktop vs Mobile vs Tablet
286
+ pages <prop> Top pages by views
287
+ countries <prop> Geographic breakdown
288
+ compare <prop> This month vs last month
289
+ franchise Hale Pet Door network overview
290
+ realtime <prop> Today/last 24h snapshot
291
+
292
+ EXPORT:
293
+ export <prop> Export 30 days to CSV
294
+ export-all Export all franchises to CSV
295
+
296
+ ALERTS:
297
+ alert-add <prop> <metric> <threshold> Add alert
298
+ alert-list List all alerts
299
+ alert-check Check all alerts now
300
+ alert-remove <id> Remove an alert
301
+
302
+ EXAMPLES:
303
+ ga properties
304
+ ga report "Hale Pet Door"
305
+ ga traffic 494080031
306
+ ga franchise
307
+ ga export "HPD-Orlando"
308
+ ga alert-add "Hale Pet Door" users-drop 20%
309
+ ga alert-check
310
+
311
+ PROPERTY:
312
+ Can be name ("Hale Pet Door") or ID (494080031)
313
+ `);
314
+ }
315
+ };
316
+
317
+ // Parse args
318
+ const [,, command, ...rest] = process.argv;
319
+ const property = rest.join(' ');
320
+
321
+ if (!command || command === 'help' || command === '--help' || command === '-h') {
322
+ commands.help();
323
+ } else if (commands[command]) {
324
+ commands[command](property);
325
+ } else {
326
+ console.log(`Unknown command: ${command}`);
327
+ console.log('Run "ga help" for usage');
328
+ process.exit(1);
329
+ }
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":""}