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 +99 -21
- package/bin/ankimcp.js +0 -0
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +31 -17
- package/dist/cli.js.map +1 -1
- package/dist/main-http.js +15 -1
- package/dist/main-http.js.map +1 -1
- package/dist/services/ngrok.service.d.ts +15 -0
- package/dist/services/ngrok.service.js +120 -0
- package/dist/services/ngrok.service.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -2
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
|
-
**
|
|
81
|
+
**Configuration - Choose one method:**
|
|
82
82
|
|
|
83
|
-
|
|
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.
|
|
113
|
-
3.
|
|
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
|
-
**
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
#
|
|
157
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
163
|
-
#
|
|
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.
|
|
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
|
-
$
|
|
44
|
-
$
|
|
45
|
-
$
|
|
46
|
-
$
|
|
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
|
-
$
|
|
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
|
-
|
|
62
|
-
1.
|
|
63
|
-
2.
|
|
64
|
-
3.
|
|
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.
|
|
95
|
-
2.
|
|
96
|
-
|
|
97
|
-
|
|
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":";;;;;
|
|
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
|
-
|
|
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);
|
package/dist/main-http.js.map
CHANGED
|
@@ -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;
|
|
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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"}
|