@anonymilyhq/cli 1.0.4 → 1.1.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/README.md CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  The Anonymily CLI connects to your live webhook endpoint on `api.anonymily.com` via a Server-Sent Events (SSE) stream and forwards every incoming request — with the exact same method, headers, query params, and body — to a port on your local machine.
18
18
 
19
- No tunnels, no sign-ups required to start. Just one command.
19
+ **No tunnels. No signup. No account required.** Just one command.
20
20
 
21
21
  > **Pro tip:** Create a free account at [anonymily.com](https://anonymily.com) and claim your endpoint to keep the same URL across sessions and unlock 500 requests / 30-day history retention with a Pro subscription.
22
22
 
@@ -38,6 +38,8 @@ npm install -g @anonymilyhq/cli
38
38
 
39
39
  ## Quick Start
40
40
 
41
+ **No account or signup required** — works immediately with free tier (50 requests, 24h retention).
42
+
41
43
  ```bash
42
44
  # Start listening — generates a random endpoint ID automatically
43
45
  npx @anonymilyhq/cli listen 3000
@@ -45,6 +47,7 @@ npx @anonymilyhq/cli listen 3000
45
47
  # Output:
46
48
  # 🚀 Anonymily CLI is running!
47
49
  # Forwarding: https://api.anonymily.com/h/xk92bzte ➔ http://127.0.0.1:3000
50
+ # Tier: 🆓 FREE | Storage: 0/50 | Retention: 24 hours
48
51
  # Waiting for requests to arrive...
49
52
  ```
50
53
 
@@ -109,6 +112,35 @@ export PRO_API_KEY=<your-pro-api-key>
109
112
 
110
113
  ---
111
114
 
115
+ ### `feedback <rating> [message]`
116
+
117
+ Submit feedback about the Anonymily CLI directly from your terminal.
118
+
119
+ ```bash
120
+ anonymily feedback <rating> [message]
121
+ ```
122
+
123
+ | Parameter | Description |
124
+ |-----------|-------------|
125
+ | `<rating>` | **Required.** Rating from 1-5 (1=Poor, 5=Excellent) |
126
+ | `[message]` | Optional feedback message |
127
+
128
+ **Examples:**
129
+ ```bash
130
+ # Quick rating
131
+ anonymily feedback 5
132
+
133
+ # Rating with message
134
+ anonymily feedback 4 "Great tool! Would love to see request filtering."
135
+
136
+ # Detailed feedback
137
+ anonymily feedback 5 "The SSE reconnection works perfectly. No issues so far!"
138
+ ```
139
+
140
+ Your feedback helps us improve Anonymily. All submissions are anonymous unless you're logged in via the web dashboard.
141
+
142
+ ---
143
+
112
144
  ## Account-Based Pro (Recommended)
113
145
 
114
146
  The modern way to get Pro limits is via an **account subscription** at [anonymily.com/upgrade](https://anonymily.com/upgrade). This gives your entire account Pro limits (₹750/month) — no per-hook configuration needed.
@@ -203,6 +235,10 @@ npx @anonymilyhq/cli listen 4000
203
235
 
204
236
  ---
205
237
 
238
+ ## Changelog
239
+
240
+ See [CHANGELOG.md](CHANGELOG.md) for a detailed history of changes.
241
+
206
242
  ## Contributing
207
243
 
208
244
  Issues and pull requests are welcome. Open an issue first to discuss any significant changes.
package/bin/cli.js CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  ProApiError,
13
13
  } from '../lib/proRegister.js';
14
14
  import { logger } from '../lib/logger.js';
15
+ import { submitFeedback } from '../lib/feedback.js';
15
16
 
16
17
  // Point this to your live production backend URL
17
18
  const API_URL = process.env.ANONYMILY_API_URL || 'https://api.anonymily.com';
@@ -19,7 +20,7 @@ const API_URL = process.env.ANONYMILY_API_URL || 'https://api.anonymily.com';
19
20
  program
20
21
  .name('anonymily')
21
22
  .description('Forward webhooks from Anonymily directly to your local machine.')
22
- .version('1.0.3');
23
+ .version('1.1.0');
23
24
 
24
25
  // ---------------------------------------------------------------------------
25
26
  // listen
@@ -89,5 +90,37 @@ configCmd
89
90
  );
90
91
  });
91
92
 
93
+ // ---------------------------------------------------------------------------
94
+ // feedback
95
+ // ---------------------------------------------------------------------------
96
+ program
97
+ .command('feedback')
98
+ .description('Submit feedback about Anonymily CLI')
99
+ .argument('<rating>', 'Rating from 1-5 (1=Poor, 5=Excellent)')
100
+ .argument('[message]', 'Optional feedback message')
101
+ .action(async (rating, message) => {
102
+ const numRating = parseInt(rating, 10);
103
+
104
+ if (isNaN(numRating) || numRating < 1 || numRating > 5) {
105
+ logger.error('Rating must be a number between 1 and 5.');
106
+ process.exit(1);
107
+ }
108
+
109
+ logger.dim('Submitting feedback...');
110
+ const result = await submitFeedback(API_URL, numRating, message);
111
+
112
+ if (result.success) {
113
+ logger.success(
114
+ `\n✅ Thank you for your feedback! (ID: ${result.id || 'N/A'})\n`,
115
+ );
116
+ logger.raw(
117
+ 'Your input helps us improve Anonymily. 🙏\n',
118
+ );
119
+ } else {
120
+ logger.error('Failed to submit feedback. Please try again later.');
121
+ process.exit(1);
122
+ }
123
+ });
124
+
92
125
  program.parse();
93
126
 
@@ -0,0 +1,36 @@
1
+ import { logger } from './logger.js';
2
+
3
+ /**
4
+ * Submit feedback to the Anonymily backend.
5
+ *
6
+ * @param {string} apiUrl - Base API URL
7
+ * @param {number} rating - Rating from 1-5
8
+ * @param {string} message - Optional feedback message
9
+ * @returns {Promise<{success: boolean, id?: string}>}
10
+ */
11
+ export async function submitFeedback(apiUrl, rating, message = '') {
12
+ try {
13
+ const response = await fetch(`${apiUrl}/feedback`, {
14
+ method: 'POST',
15
+ headers: {
16
+ 'Content-Type': 'application/json',
17
+ },
18
+ body: JSON.stringify({
19
+ rating,
20
+ message: message || undefined,
21
+ pagePath: 'cli',
22
+ }),
23
+ });
24
+
25
+ if (!response.ok) {
26
+ const errorText = await response.text();
27
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
28
+ }
29
+
30
+ const result = await response.json();
31
+ return result;
32
+ } catch (error) {
33
+ logger.error(`Failed to submit feedback: ${error.message}`);
34
+ return { success: false };
35
+ }
36
+ }
package/lib/forwarder.js CHANGED
@@ -33,16 +33,53 @@ function ensureContentType(headers, body) {
33
33
  return headers;
34
34
  }
35
35
 
36
- export function startForwarding(apiUrl, hookId, port) {
36
+ /**
37
+ * Fetch tier information for a hook ID from the backend.
38
+ */
39
+ async function fetchTierInfo(apiUrl, hookId) {
40
+ try {
41
+ const response = await fetch(`${apiUrl}/history/${hookId}`);
42
+ if (!response.ok) return null;
43
+
44
+ const data = await response.json();
45
+ return {
46
+ tier: data.tier || 'free',
47
+ requestCount: data.requests?.length || 0,
48
+ maxRequests: data.tier === 'pro' ? 500 : 50,
49
+ retention: data.tier === 'pro' ? '30 days' : '24 hours',
50
+ };
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ export async function startForwarding(apiUrl, hookId, port) {
37
57
  const webhookUrl = `${apiUrl}/h/${hookId}`;
38
58
  const localUrl = `http://127.0.0.1:${port}`;
39
59
 
40
60
  logger.success(pc.bold(`\n🚀 Anonymily CLI is running!`));
41
61
  logger.raw(`\nForwarding: ${pc.cyan(webhookUrl)} ➔ ${pc.cyan(localUrl)}`);
42
- logger.dim(`Waiting for requests to arrive...\n`);
62
+
63
+ // Fetch and display tier information
64
+ const tierInfo = await fetchTierInfo(apiUrl, hookId);
65
+ if (tierInfo) {
66
+ const tierColor = tierInfo.tier === 'pro' ? pc.green : pc.yellow;
67
+ const tierBadge = tierInfo.tier === 'pro' ? '✨ PRO' : '🆓 FREE';
68
+ logger.raw(`Tier: ${tierColor(pc.bold(tierBadge))} | Storage: ${tierInfo.requestCount}/${tierInfo.maxRequests} | Retention: ${tierInfo.retention}`);
69
+
70
+ if (tierInfo.tier === 'free') {
71
+ logger.dim(`\n💡 Upgrade to Pro for 500 requests + 30-day retention at https://anonymily.com/upgrade`);
72
+ }
73
+ }
74
+
75
+ logger.dim(`Waiting for requests to arrive...`);
76
+ logger.dim(`💬 Enjoying the CLI? Run 'anonymily feedback 5' to let us know!\n`);
43
77
 
44
78
  const es = new EventSourceConstructor(`${apiUrl}/stream/${hookId}`);
45
79
 
80
+ let requestCount = tierInfo?.requestCount || 0;
81
+ const maxRequests = tierInfo?.maxRequests || 50;
82
+
46
83
  es.onmessage = async (event) => {
47
84
  try {
48
85
  const reqData = JSON.parse(event.data);
@@ -61,7 +98,12 @@ export function startForwarding(apiUrl, hookId, port) {
61
98
  const queryString = new URLSearchParams(query || {}).toString();
62
99
  const fullLocalUrl = queryString ? `${localUrl}?${queryString}` : localUrl;
63
100
 
64
- logger.warn(`[${new Date().toLocaleTimeString()}] Incoming ${method} request...`);
101
+ // Increment request count and show progress
102
+ requestCount++;
103
+ const progress = `[${requestCount}/${maxRequests}]`;
104
+ const progressColor = requestCount > maxRequests * 0.8 ? pc.red : pc.dim;
105
+
106
+ logger.warn(`[${new Date().toLocaleTimeString()}] ⚡ Incoming ${method} request ${progressColor(progress)}...`);
65
107
 
66
108
  const response = await fetch(fullLocalUrl, {
67
109
  method,
@@ -71,6 +113,13 @@ export function startForwarding(apiUrl, hookId, port) {
71
113
 
72
114
  const statusColor = response.ok ? pc.green : pc.red;
73
115
  logger.raw(` └─ Forwarded to localhost | Status: ${statusColor(response.status)}\n`);
116
+
117
+ // Warn when approaching tier limit
118
+ if (requestCount === maxRequests) {
119
+ logger.warn(`⚠️ Tier limit reached (${maxRequests} requests). Older requests will be purged.\n`);
120
+ } else if (requestCount === Math.floor(maxRequests * 0.9)) {
121
+ logger.warn(`⚠️ 90% of tier limit reached. Consider upgrading to Pro for 500 requests.\n`);
122
+ }
74
123
  } catch (err) {
75
124
  logger.error(` └─ Error forwarding: ${err.message}`);
76
125
  logger.dim(` Make sure your local server is actually running on port ${port}\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anonymilyhq/cli",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "CLI for Anonymily platform",
5
5
  "type": "module",
6
6
  "bin": {