anki-mcp-http 0.7.0 → 0.8.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
@@ -78,9 +78,9 @@ Want to use Anki with MCP clients like **Cursor IDE**, **Cline**, or **Zed Edito
78
78
  - [Zed Editor](https://zed.dev/) - Fast, modern code editor
79
79
  - Other MCP clients that support STDIO transport
80
80
 
81
- **Basic Configuration:**
81
+ **Configuration - Choose one method:**
82
82
 
83
- Add this to your MCP client's configuration file:
83
+ **Method 1: Using npx (recommended - no installation needed)**
84
84
 
85
85
  ```json
86
86
  {
@@ -96,6 +96,28 @@ Add this to your MCP client's configuration file:
96
96
  }
97
97
  ```
98
98
 
99
+ **Method 2: Using global installation**
100
+
101
+ First, install globally:
102
+ ```bash
103
+ npm install -g anki-mcp-http
104
+ ```
105
+
106
+ Then configure:
107
+ ```json
108
+ {
109
+ "mcpServers": {
110
+ "anki-mcp": {
111
+ "command": "anki-mcp-http",
112
+ "args": ["--stdio"],
113
+ "env": {
114
+ "ANKI_CONNECT_URL": "http://localhost:8765"
115
+ }
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
99
121
  **Configuration file locations:**
100
122
  - **Cursor IDE**: `~/.cursor/mcp.json` (macOS/Linux) or `%USERPROFILE%\.cursor\mcp.json` (Windows)
101
123
  - **Cline**: Accessible via settings UI in VS Code
@@ -109,21 +131,48 @@ Want to use Anki with ChatGPT or Claude.ai in your browser? This mode lets you c
109
131
 
110
132
  **How it works (simple explanation):**
111
133
  1. You run a small server on your computer (where Anki is installed)
112
- 2. You use [ngrok](https://ngrok.com) to create a public URL that forwards to your local server
113
- 3. You share that URL with ChatGPT or Claude.ai
134
+ 2. Use the built-in `--ngrok` flag to automatically create a public tunnel URL
135
+ 3. Share that URL with ChatGPT or Claude.ai
114
136
  4. Now the AI can talk to your Anki through the internet!
115
137
 
116
- **Setup:**
138
+ **New in v0.8.0:** Integrated ngrok support with the `--ngrok` flag - no need to run ngrok separately!
139
+
140
+ **Setup - Choose one method:**
141
+
142
+ **Method 1: Using npx (recommended - no installation needed)**
117
143
 
118
144
  ```bash
119
- # Quick start (using npx - no installation needed)
145
+ # Quick start
120
146
  npx anki-mcp-http
121
147
 
148
+ # With ngrok tunnel (recommended for web-based AI)
149
+ npx anki-mcp-http --ngrok
150
+
122
151
  # With custom options
123
152
  npx anki-mcp-http --port 8080 --host 0.0.0.0
124
153
  npx anki-mcp-http --anki-connect http://localhost:8765
154
+ ```
125
155
 
126
- # Or install from source
156
+ **Method 2: Using global installation**
157
+
158
+ ```bash
159
+ # Install once
160
+ npm install -g anki-mcp-http
161
+
162
+ # Run the server
163
+ anki-mcp-http
164
+
165
+ # With ngrok tunnel (recommended for web-based AI)
166
+ anki-mcp-http --ngrok
167
+
168
+ # With custom options
169
+ anki-mcp-http --port 8080 --host 0.0.0.0
170
+ anki-mcp-http --anki-connect http://localhost:8765
171
+ ```
172
+
173
+ **Method 3: Install from source (for development)**
174
+
175
+ ```bash
127
176
  npm install
128
177
  npm run build
129
178
  npm run start:prod:http
@@ -132,37 +181,66 @@ npm run start:prod:http
132
181
  **CLI Options:**
133
182
 
134
183
  ```bash
135
- ankimcp [options]
184
+ anki-mcp-http [options]
136
185
 
137
186
  Options:
138
187
  --stdio Run in STDIO mode (for MCP clients)
139
188
  -p, --port <port> Port to listen on (HTTP mode, default: 3000)
140
189
  -h, --host <host> Host to bind to (HTTP mode, default: 127.0.0.1)
141
190
  -a, --anki-connect <url> AnkiConnect URL (default: http://localhost:8765)
191
+ --ngrok Start ngrok tunnel (requires global ngrok installation)
142
192
  --help Show help message
143
193
 
144
- Examples - HTTP Mode:
145
- ankimcp # Use all defaults (HTTP mode)
146
- ankimcp --port 8080 # Custom port
147
- ankimcp --host 0.0.0.0 # Listen on all network interfaces
148
-
149
- Examples - STDIO Mode:
150
- ankimcp --stdio # For use with npx in MCP clients
194
+ Usage with npx (no installation needed):
195
+ npx anki-mcp-http # HTTP mode
196
+ npx anki-mcp-http --port 8080 # Custom port
197
+ npx anki-mcp-http --stdio # STDIO mode
198
+ npx anki-mcp-http --ngrok # HTTP mode with ngrok tunnel
199
+
200
+ Usage with global installation:
201
+ npm install -g anki-mcp-http # Install once
202
+ anki-mcp-http # HTTP mode
203
+ anki-mcp-http --port 8080 # Custom port
204
+ anki-mcp-http --stdio # STDIO mode
205
+ anki-mcp-http --ngrok # HTTP mode with ngrok tunnel
151
206
  ```
152
207
 
153
208
  **Using with ngrok:**
154
209
 
210
+ **Method 1: Integrated (Recommended - One Command)**
211
+
155
212
  ```bash
156
- # 1. Start ankimcp
157
- npx anki-mcp-http
213
+ # One-time setup (if you haven't already):
214
+ npm install -g ngrok
215
+ ngrok config add-authtoken <your-token> # Get token from https://dashboard.ngrok.com
216
+
217
+ # Start server with ngrok tunnel in one command:
218
+ anki-mcp-http --ngrok
219
+
220
+ # The tunnel URL will be displayed in the startup banner
221
+ # Example output:
222
+ # 🌐 Ngrok tunnel: https://abc123.ngrok-free.app
223
+ ```
224
+
225
+ **Method 2: Manual (Two Terminals)**
158
226
 
159
- # 2. In another terminal, create a tunnel
227
+ ```bash
228
+ # Terminal 1: Start the server
229
+ anki-mcp-http
230
+
231
+ # Terminal 2: Create tunnel
160
232
  ngrok http 3000
161
233
 
162
- # 3. Copy the ngrok URL (looks like: https://abc123.ngrok.io)
163
- # 4. Share this URL with your AI assistant
234
+ # Copy the ngrok URL (looks like: https://abc123.ngrok-free.app)
235
+ # Share this URL with your AI assistant
164
236
  ```
165
237
 
238
+ **Benefits of `--ngrok` flag:**
239
+ - āœ… One command instead of two terminals
240
+ - āœ… Automatic cleanup when you press Ctrl+C
241
+ - āœ… URL displayed directly in the startup banner
242
+ - āœ… Works with custom ports: `anki-mcp-http --port 8080 --ngrok`
243
+
166
244
  **Security note:** Anyone with your ngrok URL can access your Anki, so keep that URL private!
167
245
 
168
246
  ### Option 4: Manual Installation from Source (Local Mode)
@@ -601,7 +679,7 @@ This project follows [Semantic Versioning](https://semver.org/) with a pre-1.0 d
601
679
  - Will be released when the API is stable and tested
602
680
  - Breaking changes will require major version bumps (2.0.0, etc.)
603
681
 
604
- **Current Status**: `0.6.0` - Active beta development. New features include the `twenty_rules` prompt for evidence-based flashcard creation, media file management, and improved prompt system. APIs may change based on feedback and testing.
682
+ **Current Status**: `0.8.0` - Active beta development. New features include integrated ngrok tunneling (`--ngrok` flag), the `twenty_rules` prompt for evidence-based flashcard creation, media file management, and improved prompt system. APIs may change based on feedback and testing.
605
683
 
606
684
  ## Similar Projects
607
685
 
package/bin/ankimcp.js CHANGED
File without changes
package/dist/cli.d.ts CHANGED
@@ -2,7 +2,8 @@ export interface CliOptions {
2
2
  port: number;
3
3
  host: string;
4
4
  ankiConnect: string;
5
+ ngrok: boolean;
5
6
  }
6
7
  export declare function checkForUpdates(): void;
7
8
  export declare function parseCliArgs(): CliOptions;
8
- export declare function displayStartupBanner(options: CliOptions): void;
9
+ export declare function displayStartupBanner(options: CliOptions, ngrokUrl?: string): void;
package/dist/cli.js CHANGED
@@ -34,19 +34,25 @@ function parseCliArgs() {
34
34
  .option('-p, --port <number>', 'Port to listen on (HTTP mode)', '3000')
35
35
  .option('-h, --host <address>', 'Host to bind to (HTTP mode)', '127.0.0.1')
36
36
  .option('-a, --anki-connect <url>', 'AnkiConnect URL', 'http://localhost:8765')
37
+ .option('--ngrok', 'Start ngrok tunnel (requires global ngrok installation)')
37
38
  .addHelpText('after', `
38
39
  Transport Modes:
39
40
  HTTP Mode (default): For web-based AI assistants (ChatGPT, Claude.ai)
40
41
  STDIO Mode: For desktop MCP clients (Cursor, Cline, Zed)
41
42
 
42
43
  Examples - HTTP Mode:
43
- $ ankimcp # Use defaults
44
- $ ankimcp --port 8080 # Custom port
45
- $ ankimcp --host 0.0.0.0 --port 3000 # Listen on all interfaces
46
- $ ankimcp --anki-connect http://localhost:8765
44
+ $ anki-mcp-http # Use defaults
45
+ $ anki-mcp-http --port 8080 # Custom port
46
+ $ anki-mcp-http --host 0.0.0.0 --port 3000 # Listen on all interfaces
47
+ $ anki-mcp-http --anki-connect http://localhost:8765
48
+
49
+ Examples - HTTP Mode with Ngrok:
50
+ $ anki-mcp-http --ngrok # Start with ngrok tunnel
51
+ $ anki-mcp-http --port 8080 --ngrok # Custom port + ngrok
52
+ $ anki-mcp-http --host 0.0.0.0 --ngrok # Public host + ngrok
47
53
 
48
54
  Examples - STDIO Mode:
49
- $ ankimcp --stdio # For use with npx in MCP clients
55
+ $ anki-mcp-http --stdio # For use with npx in MCP clients
50
56
 
51
57
  # MCP client configuration (Cursor, Cline, Zed, etc.):
52
58
  {
@@ -58,10 +64,11 @@ Examples - STDIO Mode:
58
64
  }
59
65
  }
60
66
 
61
- Usage with ngrok (HTTP mode only):
62
- 1. Start ankimcp in one terminal
63
- 2. In another terminal: ngrok http 3000
64
- 3. Share the ngrok URL with your AI assistant
67
+ Ngrok Setup (one-time):
68
+ 1. Install: npm install -g ngrok
69
+ 2. Get auth token from: https://dashboard.ngrok.com/get-started/your-authtoken
70
+ 3. Setup: ngrok config add-authtoken <your-token>
71
+ 4. Run: anki-mcp-http --ngrok
65
72
  `);
66
73
  program.parse();
67
74
  const options = program.opts();
@@ -69,9 +76,10 @@ Usage with ngrok (HTTP mode only):
69
76
  port: parseInt(options.port.toString(), 10),
70
77
  host: options.host,
71
78
  ankiConnect: options.ankiConnect,
79
+ ngrok: options.ngrok || false,
72
80
  };
73
81
  }
74
- function displayStartupBanner(options) {
82
+ function displayStartupBanner(options, ngrokUrl) {
75
83
  const version = getVersion();
76
84
  const title = `AnkiMCP HTTP Server v${version}`;
77
85
  const padding = Math.floor((64 - title.length) / 2);
@@ -82,19 +90,25 @@ function displayStartupBanner(options) {
82
90
  ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
83
91
 
84
92
  šŸš€ Server running on: http://${options.host}:${options.port}
85
- šŸ”Œ AnkiConnect URL: ${options.ankiConnect}
93
+ šŸ”Œ AnkiConnect URL: ${options.ankiConnect}${ngrokUrl ? `\n🌐 Ngrok tunnel: ${ngrokUrl}` : ''}
86
94
 
87
95
  Configuration:
88
96
  • Port: ${options.port} (override: --port 8080)
89
97
  • Host: ${options.host} (override: --host 0.0.0.0)
90
98
  • AnkiConnect: ${options.ankiConnect}
91
- (override: --anki-connect http://localhost:8765)
92
-
99
+ (override: --anki-connect http://localhost:8765)${ngrokUrl ? `\n • Ngrok tunnel: ${ngrokUrl}\n • Ngrok dashboard: http://localhost:4040` : ''}
100
+ ${!ngrokUrl
101
+ ? `
93
102
  Usage with ngrok:
94
- 1. In another terminal: ngrok http ${options.port}
95
- 2. Share the ngrok URL with your AI assistant
96
-
97
- Run 'ankimcp --help' for more options.
103
+ 1. Install: npm install -g ngrok
104
+ 2. Setup: ngrok config add-authtoken <your-token>
105
+ 3. Run: anki-mcp-http --ngrok
106
+ `
107
+ : `
108
+ Share this URL with your AI assistant:
109
+ ${ngrokUrl}
110
+ `}
111
+ Run 'anki-mcp-http --help' for more options.
98
112
  `);
99
113
  }
100
114
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;AAyBA,0CAEC;AAED,oCA4DC;AAED,oDA0BC;AArHD,yCAAoC;AACpC,2BAAkC;AAClC,+BAA4B;AAC5B,sEAA6C;AAQ7C,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CACf,IAAA,iBAAY,EAAC,IAAA,WAAI,EAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAC1D,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;IACrD,CAAC;AACH,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,cAAc,EAAE,CAAC,OAAO,CAAC;AAClC,CAAC;AAED,SAAgB,eAAe;IAC7B,IAAA,yBAAc,EAAC,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AACrD,CAAC;AAED,SAAgB,YAAY;IAC1B,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,SAAS,CAAC;SACf,WAAW,CAAC,yDAAyD,CAAC;SACtE,OAAO,CAAC,UAAU,EAAE,CAAC;SACrB,MAAM,CACL,SAAS,EACT,6DAA6D,CAC9D;SACA,MAAM,CAAC,qBAAqB,EAAE,+BAA+B,EAAE,MAAM,CAAC;SACtE,MAAM,CAAC,sBAAsB,EAAE,6BAA6B,EAAE,WAAW,CAAC;SAC1E,MAAM,CACL,0BAA0B,EAC1B,iBAAiB,EACjB,uBAAuB,CACxB;SACA,WAAW,CACV,OAAO,EACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BL,CACI,CAAC;IAEJ,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAc,CAAC;IAE3C,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC;QAC3C,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,WAAW,EAAE,OAAO,CAAC,WAAW;KACjC,CAAC;AACJ,CAAC;AAED,SAAgB,oBAAoB,CAAC,OAAmB;IACtD,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,wBAAwB,OAAO,EAAE,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAE1F,OAAO,CAAC,GAAG,CAAC;;GAEX,WAAW;;;+BAGiB,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI;wBACnC,OAAO,CAAC,WAAW;;;0BAGjB,OAAO,CAAC,IAAI;0BACZ,OAAO,CAAC,IAAI;0BACZ,OAAO,CAAC,WAAW;;;;uCAIN,OAAO,CAAC,IAAI;;;;CAIlD,CAAC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;AA0BA,0CAEC;AAED,oCAuEC;AAED,oDAqCC;AA5ID,yCAAoC;AACpC,2BAAkC;AAClC,+BAA4B;AAC5B,sEAA6C;AAS7C,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CACf,IAAA,iBAAY,EAAC,IAAA,WAAI,EAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAC1D,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;IACrD,CAAC;AACH,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,cAAc,EAAE,CAAC,OAAO,CAAC;AAClC,CAAC;AAED,SAAgB,eAAe;IAC7B,IAAA,yBAAc,EAAC,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AACrD,CAAC;AAED,SAAgB,YAAY;IAC1B,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,SAAS,CAAC;SACf,WAAW,CAAC,yDAAyD,CAAC;SACtE,OAAO,CAAC,UAAU,EAAE,CAAC;SACrB,MAAM,CACL,SAAS,EACT,6DAA6D,CAC9D;SACA,MAAM,CAAC,qBAAqB,EAAE,+BAA+B,EAAE,MAAM,CAAC;SACtE,MAAM,CAAC,sBAAsB,EAAE,6BAA6B,EAAE,WAAW,CAAC;SAC1E,MAAM,CACL,0BAA0B,EAC1B,iBAAiB,EACjB,uBAAuB,CACxB;SACA,MAAM,CACL,SAAS,EACT,yDAAyD,CAC1D;SACA,WAAW,CACV,OAAO,EACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCL,CACI,CAAC;IAEJ,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAc,CAAC;IAE3C,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC;QAC3C,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,KAAK;KAC9B,CAAC;AACJ,CAAC;AAED,SAAgB,oBAAoB,CAClC,OAAmB,EACnB,QAAiB;IAEjB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,wBAAwB,OAAO,EAAE,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAE1F,OAAO,CAAC,GAAG,CAAC;;GAEX,WAAW;;;+BAGiB,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI;wBACnC,OAAO,CAAC,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE;;;0BAGzE,OAAO,CAAC,IAAI;0BACZ,OAAO,CAAC,IAAI;0BACZ,OAAO,CAAC,WAAW;0EAC6B,QAAQ,CAAC,CAAC,CAAC,6BAA6B,QAAQ,iDAAiD,CAAC,CAAC,CAAC,EAAE;EAE5K,CAAC,QAAQ;QACP,CAAC,CAAC;;;;;CAKP;QACK,CAAC,CAAC;;IAEJ,QAAQ;CAEV;;CAED,CAAC,CAAC;AACH,CAAC"}
package/dist/main-http.js CHANGED
@@ -5,6 +5,7 @@ const app_module_1 = require("./app.module");
5
5
  const bootstrap_1 = require("./bootstrap");
6
6
  const origin_validation_guard_1 = require("./http/guards/origin-validation.guard");
7
7
  const cli_1 = require("./cli");
8
+ const ngrok_service_1 = require("./services/ngrok.service");
8
9
  async function bootstrap() {
9
10
  (0, cli_1.checkForUpdates)();
10
11
  const options = (0, cli_1.parseCliArgs)();
@@ -19,7 +20,20 @@ async function bootstrap() {
19
20
  });
20
21
  app.useGlobalGuards(new origin_validation_guard_1.OriginValidationGuard());
21
22
  await app.listen(options.port, options.host);
22
- (0, cli_1.displayStartupBanner)(options);
23
+ let ngrokUrl;
24
+ if (options.ngrok) {
25
+ try {
26
+ const ngrokService = new ngrok_service_1.NgrokService();
27
+ const tunnelInfo = await ngrokService.start(options.port);
28
+ ngrokUrl = tunnelInfo.publicUrl;
29
+ }
30
+ catch (err) {
31
+ console.error('\nāŒ Failed to start ngrok:');
32
+ console.error(err instanceof Error ? err.message : String(err));
33
+ console.error('\nServer is still running locally without tunnel.\n');
34
+ }
35
+ }
36
+ (0, cli_1.displayStartupBanner)(options, ngrokUrl);
23
37
  }
24
38
  bootstrap().catch((err) => {
25
39
  console.error('Failed to start MCP HTTP server:', err);
@@ -1 +1 @@
1
- {"version":3,"file":"main-http.js","sourceRoot":"","sources":["../src/main-http.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AACzC,2CAAoE;AACpE,mFAA8E;AAC9E,+BAIe;AAEf,KAAK,UAAU,SAAS;IAEtB,IAAA,qBAAe,GAAE,CAAC;IAElB,MAAM,OAAO,GAAG,IAAA,kBAAY,GAAE,CAAC;IAG/B,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IAGnD,MAAM,UAAU,GAAG,IAAA,4BAAgB,EAAC,CAAC,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,IAAA,+BAAmB,EAAC,UAAU,CAAC,CAAC;IAGtD,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,OAAO,EAAE,EAAE;QACxD,MAAM,EAAE,aAAa;QACrB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;IAGH,GAAG,CAAC,eAAe,CAAC,IAAI,+CAAqB,EAAE,CAAC,CAAC;IAEjD,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAG7C,IAAA,0BAAoB,EAAC,OAAO,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACxB,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAC;IACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"main-http.js","sourceRoot":"","sources":["../src/main-http.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AACzC,2CAAoE;AACpE,mFAA8E;AAC9E,+BAIe;AACf,4DAAwD;AAExD,KAAK,UAAU,SAAS;IAEtB,IAAA,qBAAe,GAAE,CAAC;IAElB,MAAM,OAAO,GAAG,IAAA,kBAAY,GAAE,CAAC;IAG/B,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAChC,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IAGnD,MAAM,UAAU,GAAG,IAAA,4BAAgB,EAAC,CAAC,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,IAAA,+BAAmB,EAAC,UAAU,CAAC,CAAC;IAGtD,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,OAAO,EAAE,EAAE;QACxD,MAAM,EAAE,aAAa;QACrB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;IAGH,GAAG,CAAC,eAAe,CAAC,IAAI,+CAAqB,EAAE,CAAC,CAAC;IAEjD,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAG7C,IAAI,QAA4B,CAAC;IACjC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,4BAAY,EAAE,CAAC;YACxC,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC1D,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;YAC5C,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAChE,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAGD,IAAA,0BAAoB,EAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACxB,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAC;IACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ export interface NgrokTunnelInfo {
2
+ publicUrl: string;
3
+ apiUrl: string;
4
+ }
5
+ export declare class NgrokService {
6
+ private process;
7
+ private cleanupHandlersRegistered;
8
+ start(port: number): Promise<NgrokTunnelInfo>;
9
+ private isNgrokInstalled;
10
+ private waitForNgrok;
11
+ private getTunnelUrl;
12
+ private registerCleanupHandlers;
13
+ private cleanup;
14
+ getWebInterfaceUrl(): string;
15
+ }
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.NgrokService = void 0;
7
+ const child_process_1 = require("child_process");
8
+ const http_1 = __importDefault(require("http"));
9
+ class NgrokService {
10
+ process = null;
11
+ cleanupHandlersRegistered = false;
12
+ async start(port) {
13
+ if (!(await this.isNgrokInstalled())) {
14
+ throw new Error('ngrok is not installed.\n' +
15
+ 'Install it with: npm install -g ngrok\n' +
16
+ 'Or download from: https://ngrok.com/download\n' +
17
+ 'Then setup auth: ngrok config add-authtoken <your-token>');
18
+ }
19
+ this.process = (0, child_process_1.spawn)('ngrok', ['http', port.toString()], {
20
+ stdio: 'pipe',
21
+ });
22
+ if (!this.cleanupHandlersRegistered) {
23
+ this.registerCleanupHandlers();
24
+ this.cleanupHandlersRegistered = true;
25
+ }
26
+ this.process.on('error', (err) => {
27
+ throw new Error(`Failed to start ngrok: ${err.message}`);
28
+ });
29
+ this.process.on('exit', (code, signal) => {
30
+ if (code !== 0 && code !== null) {
31
+ console.error(`ngrok exited with code ${code}`);
32
+ }
33
+ });
34
+ await this.waitForNgrok();
35
+ const publicUrl = await this.getTunnelUrl();
36
+ return {
37
+ publicUrl,
38
+ apiUrl: 'http://localhost:4040',
39
+ };
40
+ }
41
+ async isNgrokInstalled() {
42
+ return new Promise((resolve) => {
43
+ const check = (0, child_process_1.spawn)('which', ['ngrok']);
44
+ check.on('close', (code) => resolve(code === 0));
45
+ check.on('error', () => resolve(false));
46
+ });
47
+ }
48
+ async waitForNgrok(maxAttempts = 20, delayMs = 500) {
49
+ for (let i = 0; i < maxAttempts; i++) {
50
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
51
+ try {
52
+ await this.getTunnelUrl();
53
+ return;
54
+ }
55
+ catch {
56
+ }
57
+ }
58
+ throw new Error('ngrok failed to start in time.\n' +
59
+ 'Make sure you have configured your auth token:\n' +
60
+ 'ngrok config add-authtoken <your-token>');
61
+ }
62
+ async getTunnelUrl() {
63
+ return new Promise((resolve, reject) => {
64
+ http_1.default
65
+ .get('http://localhost:4040/api/tunnels', (res) => {
66
+ let data = '';
67
+ res.on('data', (chunk) => (data += chunk));
68
+ res.on('end', () => {
69
+ try {
70
+ const response = JSON.parse(data);
71
+ const tunnel = response.tunnels?.find((t) => t.proto === 'https');
72
+ const publicUrl = tunnel?.public_url;
73
+ if (publicUrl) {
74
+ resolve(publicUrl);
75
+ }
76
+ else {
77
+ reject(new Error('No HTTPS tunnel found in ngrok API response'));
78
+ }
79
+ }
80
+ catch (err) {
81
+ reject(new Error(`Failed to parse ngrok API response: ${err}`));
82
+ }
83
+ });
84
+ })
85
+ .on('error', (err) => {
86
+ reject(new Error(`Failed to connect to ngrok API: ${err.message}`));
87
+ });
88
+ });
89
+ }
90
+ registerCleanupHandlers() {
91
+ process.on('SIGINT', () => {
92
+ console.log('\n\nšŸ›‘ Shutting down ngrok tunnel...');
93
+ this.cleanup();
94
+ process.exit(0);
95
+ });
96
+ process.on('SIGTERM', () => {
97
+ this.cleanup();
98
+ process.exit(0);
99
+ });
100
+ process.on('exit', () => {
101
+ this.cleanup();
102
+ });
103
+ process.on('uncaughtException', (err) => {
104
+ console.error('Uncaught exception:', err);
105
+ this.cleanup();
106
+ process.exit(1);
107
+ });
108
+ }
109
+ cleanup() {
110
+ if (this.process && !this.process.killed) {
111
+ this.process.kill('SIGTERM');
112
+ this.process = null;
113
+ }
114
+ }
115
+ getWebInterfaceUrl() {
116
+ return 'http://localhost:4040';
117
+ }
118
+ }
119
+ exports.NgrokService = NgrokService;
120
+ //# sourceMappingURL=ngrok.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngrok.service.js","sourceRoot":"","sources":["../../src/services/ngrok.service.ts"],"names":[],"mappings":"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"}