@soederpop/luca 0.0.34 → 0.0.35
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/bun.lock +0 -1
- package/docs/README.md +1 -1
- package/docs/TABLE-OF-CONTENTS.md +0 -1
- package/docs/bootstrap/SKILL.md +1 -1
- package/package.json +1 -2
- package/scripts/examples/using-assistant-with-mcp.ts +2 -7
- package/scripts/test-linux-binary.sh +80 -0
- package/src/bootstrap/generated.ts +2 -92
- package/src/cli/build-info.ts +2 -2
- package/src/introspection/generated.agi.ts +628 -887
- package/src/introspection/generated.node.ts +780 -1039
- package/src/introspection/generated.web.ts +1 -1
- package/src/node/container.ts +0 -5
- package/src/python/generated.ts +1 -1
- package/src/scaffolds/generated.ts +1 -1
- package/docs/examples/port-exposer.md +0 -89
- package/src/node/features/port-exposer.ts +0 -351
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { setBuildTimeData, setContainerBuildTimeData } from './index.js';
|
|
2
2
|
|
|
3
3
|
// Auto-generated introspection registry data
|
|
4
|
-
// Generated at: 2026-03-
|
|
4
|
+
// Generated at: 2026-03-27T03:29:26.528Z
|
|
5
5
|
|
|
6
6
|
setBuildTimeData('features.containerLink', {
|
|
7
7
|
"id": "features.containerLink",
|
package/src/node/container.ts
CHANGED
|
@@ -34,7 +34,6 @@ import "./features/json-tree";
|
|
|
34
34
|
import "./features/networking";
|
|
35
35
|
import "./features/os";
|
|
36
36
|
import "./features/package-finder";
|
|
37
|
-
import "./features/port-exposer";
|
|
38
37
|
import "./features/python";
|
|
39
38
|
import "./features/proc";
|
|
40
39
|
import "./features/repl";
|
|
@@ -87,7 +86,6 @@ import type { Vault } from "./features/vault";
|
|
|
87
86
|
import type { VM } from "./features/vm";
|
|
88
87
|
import type { YAML } from "./features/yaml";
|
|
89
88
|
import type { YamlTree } from "./features/yaml-tree";
|
|
90
|
-
import type { PortExposer } from "./features/port-exposer";
|
|
91
89
|
import type { Docker } from './features/docker';
|
|
92
90
|
import type { Runpod } from './features/runpod';
|
|
93
91
|
import type { SecureShell } from './features/secure-shell';
|
|
@@ -127,7 +125,6 @@ export {
|
|
|
127
125
|
type DiskCache,
|
|
128
126
|
type Vault,
|
|
129
127
|
type Downloader,
|
|
130
|
-
type PortExposer,
|
|
131
128
|
type Docker,
|
|
132
129
|
type Runpod,
|
|
133
130
|
type SecureShell,
|
|
@@ -193,7 +190,6 @@ export interface NodeFeatures extends AvailableFeatures {
|
|
|
193
190
|
jsonTree: typeof JsonTree;
|
|
194
191
|
downloader: typeof Downloader;
|
|
195
192
|
python: typeof Python;
|
|
196
|
-
portExposer: typeof PortExposer;
|
|
197
193
|
runpod: typeof Runpod;
|
|
198
194
|
secureShell: typeof SecureShell;
|
|
199
195
|
ink: typeof Ink;
|
|
@@ -282,7 +278,6 @@ export class NodeContainer<
|
|
|
282
278
|
diskCache?: DiskCache;
|
|
283
279
|
vault?: Vault;
|
|
284
280
|
python?: Python;
|
|
285
|
-
portExposer?: PortExposer;
|
|
286
281
|
ink?: Ink;
|
|
287
282
|
telegram?: Telegram;
|
|
288
283
|
opener?: Opener;
|
package/src/python/generated.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Auto-generated scaffold and MCP readme content
|
|
2
|
-
// Generated at: 2026-03-
|
|
2
|
+
// Generated at: 2026-03-27T03:29:27.470Z
|
|
3
3
|
// Source: docs/scaffolds/*.md, docs/examples/assistant/, and docs/mcp/readme.md
|
|
4
4
|
//
|
|
5
5
|
// Do not edit manually. Run: luca build-scaffolds
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "Port Exposer"
|
|
3
|
-
tags: [portExposer, ngrok, networking, tunnel]
|
|
4
|
-
lastTested: null
|
|
5
|
-
lastTestPassed: null
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# portExposer
|
|
9
|
-
|
|
10
|
-
Exposes local HTTP services via ngrok with SSL-enabled public URLs. Useful for development, testing webhooks, and sharing local services with external consumers.
|
|
11
|
-
|
|
12
|
-
## Overview
|
|
13
|
-
|
|
14
|
-
The `portExposer` feature creates an ngrok tunnel from a local port to a public HTTPS URL. It supports custom subdomains, regional endpoints, basic auth, and OAuth (features that require a paid ngrok plan). Requires ngrok to be installed or available as a dependency, and optionally an auth token for premium features.
|
|
15
|
-
|
|
16
|
-
## Enabling the Feature
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
const exposer = container.feature('portExposer', {
|
|
20
|
-
port: 3000,
|
|
21
|
-
enable: true
|
|
22
|
-
})
|
|
23
|
-
console.log('Port Exposer enabled:', exposer.state.get('enabled'))
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Exploring the API
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
const docs = container.features.describe('portExposer')
|
|
30
|
-
console.log(docs)
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Checking Connection State
|
|
34
|
-
|
|
35
|
-
```ts
|
|
36
|
-
const exposer = container.feature('portExposer', { port: 3000 })
|
|
37
|
-
console.log('Connected:', exposer.isConnected())
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Exposing a Port
|
|
41
|
-
|
|
42
|
-
Create a tunnel and get the public URL.
|
|
43
|
-
|
|
44
|
-
```ts skip
|
|
45
|
-
const url = await exposer.expose()
|
|
46
|
-
console.log('Public URL:', url)
|
|
47
|
-
console.log('Connected:', exposer.isConnected())
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
The returned URL is an HTTPS endpoint that forwards traffic to `localhost:3000`. The tunnel remains active until `close()` is called or the process exits.
|
|
51
|
-
|
|
52
|
-
## Getting Connection Info
|
|
53
|
-
|
|
54
|
-
Retrieve a snapshot of the current tunnel state.
|
|
55
|
-
|
|
56
|
-
```ts skip
|
|
57
|
-
await exposer.expose()
|
|
58
|
-
const info = exposer.getConnectionInfo()
|
|
59
|
-
console.log('Public URL:', info.publicUrl)
|
|
60
|
-
console.log('Local port:', info.localPort)
|
|
61
|
-
console.log('Connected at:', info.connectedAt)
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Reconnecting with New Options
|
|
65
|
-
|
|
66
|
-
Close the existing tunnel and re-expose with different settings.
|
|
67
|
-
|
|
68
|
-
```ts skip
|
|
69
|
-
const url1 = await exposer.expose()
|
|
70
|
-
console.log('First URL:', url1)
|
|
71
|
-
|
|
72
|
-
const url2 = await exposer.reconnect({ port: 8080 })
|
|
73
|
-
console.log('New URL (port 8080):', url2)
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
The `reconnect` method calls `close()` internally, merges the new options, then calls `expose()` again.
|
|
77
|
-
|
|
78
|
-
## Closing the Tunnel
|
|
79
|
-
|
|
80
|
-
```ts skip
|
|
81
|
-
await exposer.close()
|
|
82
|
-
console.log('Tunnel closed:', !exposer.isConnected())
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Calling `close()` when no tunnel is active is a safe no-op. The `disable()` method also closes the tunnel before disabling the feature.
|
|
86
|
-
|
|
87
|
-
## Summary
|
|
88
|
-
|
|
89
|
-
The `portExposer` feature wraps ngrok to expose local ports as public HTTPS endpoints. It supports connection lifecycle management, reconnection with new options, and event-driven notifications for tunnel state changes. Requires ngrok to be installed.
|
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
|
|
3
|
-
import * as ngrok from '@ngrok/ngrok'
|
|
4
|
-
import { Feature } from '../../feature.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Zod schema for the Port Exposer feature state
|
|
8
|
-
*/
|
|
9
|
-
export const PortExposerStateSchema = FeatureStateSchema.extend({
|
|
10
|
-
/** Whether ngrok is currently connected */
|
|
11
|
-
connected: z.boolean().describe('Whether ngrok is currently connected'),
|
|
12
|
-
/** The public URL provided by ngrok */
|
|
13
|
-
publicUrl: z.string().optional().describe('The public URL provided by ngrok'),
|
|
14
|
-
/** The local port being exposed */
|
|
15
|
-
localPort: z.number().optional().describe('The local port being exposed'),
|
|
16
|
-
/** Ngrok session information */
|
|
17
|
-
sessionInfo: z.object({
|
|
18
|
-
authToken: z.string().optional().describe('Ngrok authentication token'),
|
|
19
|
-
region: z.string().optional().describe('Ngrok region'),
|
|
20
|
-
subdomain: z.string().optional().describe('Ngrok subdomain'),
|
|
21
|
-
}).optional().describe('Ngrok session information'),
|
|
22
|
-
/** Connection timestamp */
|
|
23
|
-
connectedAt: z.coerce.date().optional().describe('Timestamp when the connection was established'),
|
|
24
|
-
/** Any error that occurred */
|
|
25
|
-
lastError: z.string().optional().describe('Last error message from an ngrok operation'),
|
|
26
|
-
})
|
|
27
|
-
export type PortExposerState = z.infer<typeof PortExposerStateSchema>
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Zod schema for Port Exposer feature options
|
|
31
|
-
*/
|
|
32
|
-
export const PortExposerOptionsSchema = FeatureOptionsSchema.extend({
|
|
33
|
-
/** Local port to expose */
|
|
34
|
-
port: z.number().optional().describe('Local port to expose'),
|
|
35
|
-
/** Optional ngrok auth token for premium features */
|
|
36
|
-
authToken: z.string().optional().describe('Ngrok auth token for premium features'),
|
|
37
|
-
/** Preferred region (us, eu, ap, au, sa, jp, in) */
|
|
38
|
-
region: z.string().optional().describe('Preferred ngrok region (us, eu, ap, au, sa, jp, in)'),
|
|
39
|
-
/** Custom subdomain (requires paid plan) */
|
|
40
|
-
subdomain: z.string().optional().describe('Custom subdomain (requires paid plan)'),
|
|
41
|
-
/** Domain to use (requires paid plan) */
|
|
42
|
-
domain: z.string().optional().describe('Domain to use (requires paid plan)'),
|
|
43
|
-
/** Basic auth for the tunnel */
|
|
44
|
-
basicAuth: z.string().optional().describe('Basic auth credentials for the tunnel'),
|
|
45
|
-
/** OAuth provider for authentication */
|
|
46
|
-
oauth: z.string().optional().describe('OAuth provider for authentication'),
|
|
47
|
-
/** Additional ngrok configuration */
|
|
48
|
-
config: z.any().describe('Additional ngrok configuration'),
|
|
49
|
-
})
|
|
50
|
-
export type PortExposerOptions = z.infer<typeof PortExposerOptionsSchema>
|
|
51
|
-
|
|
52
|
-
export const PortExposerEventsSchema = FeatureEventsSchema.extend({
|
|
53
|
-
exposed: z.tuple([z.object({
|
|
54
|
-
publicUrl: z.string().optional().describe('The public ngrok URL'),
|
|
55
|
-
localPort: z.number().describe('The local port being exposed'),
|
|
56
|
-
}).describe('Exposure details')]).describe('When a local port is successfully exposed via ngrok'),
|
|
57
|
-
closed: z.tuple([]).describe('When the ngrok tunnel is closed'),
|
|
58
|
-
error: z.tuple([z.any().describe('The error object')]).describe('When an ngrok operation fails'),
|
|
59
|
-
}).describe('Port Exposer events')
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Port Exposer Feature
|
|
63
|
-
*
|
|
64
|
-
* Exposes local HTTP services via ngrok with SSL-enabled public URLs.
|
|
65
|
-
* Perfect for development, testing, and sharing local services securely.
|
|
66
|
-
*
|
|
67
|
-
* Features:
|
|
68
|
-
* - SSL-enabled public URLs for local services
|
|
69
|
-
* - Custom subdomains and domains (with paid plans)
|
|
70
|
-
* - Authentication options (basic auth, OAuth)
|
|
71
|
-
* - Regional endpoint selection
|
|
72
|
-
* - Connection state management
|
|
73
|
-
*
|
|
74
|
-
* @example
|
|
75
|
-
* ```typescript
|
|
76
|
-
* // Basic usage
|
|
77
|
-
* const exposer = container.feature('portExposer', { port: 3000 })
|
|
78
|
-
* const url = await exposer.expose()
|
|
79
|
-
* console.log(`Service available at: ${url}`)
|
|
80
|
-
*
|
|
81
|
-
* // With custom subdomain
|
|
82
|
-
* const exposer = container.feature('portExposer', {
|
|
83
|
-
* port: 8080,
|
|
84
|
-
* subdomain: 'my-app',
|
|
85
|
-
* authToken: 'your-ngrok-token'
|
|
86
|
-
* })
|
|
87
|
-
* ```
|
|
88
|
-
*/
|
|
89
|
-
export class PortExposer extends Feature<PortExposerState, PortExposerOptions> {
|
|
90
|
-
static override shortcut = 'portExposer' as const
|
|
91
|
-
static override stateSchema = PortExposerStateSchema
|
|
92
|
-
static override optionsSchema = PortExposerOptionsSchema
|
|
93
|
-
static override eventsSchema = PortExposerEventsSchema
|
|
94
|
-
static { Feature.register(this, 'portExposer') }
|
|
95
|
-
|
|
96
|
-
private ngrokListener?: ngrok.Listener
|
|
97
|
-
|
|
98
|
-
override get initialState(): PortExposerState {
|
|
99
|
-
return {
|
|
100
|
-
...super.initialState,
|
|
101
|
-
connected: false
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Expose the local port via ngrok.
|
|
107
|
-
*
|
|
108
|
-
* Creates an ngrok tunnel to the specified local port and returns
|
|
109
|
-
* the SSL-enabled public URL. Emits `exposed` on success or `error` on failure.
|
|
110
|
-
*
|
|
111
|
-
* @param port - Optional port override; falls back to `options.port`
|
|
112
|
-
* @returns Promise resolving to the public URL string
|
|
113
|
-
* @throws {Error} When no port is specified in options or parameter
|
|
114
|
-
*
|
|
115
|
-
* @example
|
|
116
|
-
* ```typescript
|
|
117
|
-
* const exposer = container.feature('portExposer', { port: 3000 })
|
|
118
|
-
* const url = await exposer.expose()
|
|
119
|
-
* console.log(`Public URL: ${url}`)
|
|
120
|
-
*
|
|
121
|
-
* // Override port at call time
|
|
122
|
-
* const url2 = await exposer.expose(8080)
|
|
123
|
-
* ```
|
|
124
|
-
*/
|
|
125
|
-
async expose(port?: number): Promise<string> {
|
|
126
|
-
const targetPort = port || this.options.port
|
|
127
|
-
|
|
128
|
-
if (!targetPort) {
|
|
129
|
-
throw new Error('Port must be specified either in options or as parameter')
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
// Set up ngrok configuration
|
|
134
|
-
const config: any = {
|
|
135
|
-
addr: targetPort,
|
|
136
|
-
...this.options.config
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Add optional configuration
|
|
140
|
-
if (this.options.authToken) {
|
|
141
|
-
config.authtoken = this.options.authToken
|
|
142
|
-
}
|
|
143
|
-
if (this.options.region) {
|
|
144
|
-
config.region = this.options.region
|
|
145
|
-
}
|
|
146
|
-
if (this.options.subdomain) {
|
|
147
|
-
config.subdomain = this.options.subdomain
|
|
148
|
-
}
|
|
149
|
-
if (this.options.domain) {
|
|
150
|
-
config.domain = this.options.domain
|
|
151
|
-
}
|
|
152
|
-
if (this.options.basicAuth) {
|
|
153
|
-
config.basic_auth = this.options.basicAuth
|
|
154
|
-
}
|
|
155
|
-
if (this.options.oauth) {
|
|
156
|
-
config.oauth = this.options.oauth
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Start ngrok listener
|
|
160
|
-
this.ngrokListener = await ngrok.forward(config)
|
|
161
|
-
const publicUrl = this.ngrokListener.url() || undefined
|
|
162
|
-
|
|
163
|
-
// Update state
|
|
164
|
-
this.setState({
|
|
165
|
-
connected: true,
|
|
166
|
-
publicUrl,
|
|
167
|
-
localPort: targetPort,
|
|
168
|
-
sessionInfo: {
|
|
169
|
-
authToken: this.options.authToken,
|
|
170
|
-
region: this.options.region,
|
|
171
|
-
subdomain: this.options.subdomain
|
|
172
|
-
},
|
|
173
|
-
connectedAt: new Date(),
|
|
174
|
-
lastError: undefined
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
this.emit('exposed', { publicUrl, localPort: targetPort })
|
|
178
|
-
|
|
179
|
-
return publicUrl!
|
|
180
|
-
|
|
181
|
-
} catch (error) {
|
|
182
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
183
|
-
|
|
184
|
-
this.setState({
|
|
185
|
-
connected: false,
|
|
186
|
-
lastError: errorMessage
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
this.emit('error', error)
|
|
190
|
-
throw error
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Stop exposing the port and close the ngrok tunnel.
|
|
196
|
-
*
|
|
197
|
-
* Tears down the ngrok listener, resets connection state, and emits `closed`.
|
|
198
|
-
* Safe to call when no tunnel is active (no-op).
|
|
199
|
-
*
|
|
200
|
-
* @returns Promise that resolves when the tunnel is fully closed
|
|
201
|
-
* @throws {Error} When the ngrok listener fails to close
|
|
202
|
-
*
|
|
203
|
-
* @example
|
|
204
|
-
* ```typescript
|
|
205
|
-
* const exposer = container.feature('portExposer', { port: 3000 })
|
|
206
|
-
* await exposer.expose()
|
|
207
|
-
* // ... later
|
|
208
|
-
* await exposer.close()
|
|
209
|
-
* console.log(exposer.isConnected()) // false
|
|
210
|
-
* ```
|
|
211
|
-
*/
|
|
212
|
-
async close(): Promise<void> {
|
|
213
|
-
if (this.ngrokListener) {
|
|
214
|
-
try {
|
|
215
|
-
await this.ngrokListener.close()
|
|
216
|
-
this.ngrokListener = undefined
|
|
217
|
-
|
|
218
|
-
this.setState({
|
|
219
|
-
connected: false,
|
|
220
|
-
publicUrl: undefined,
|
|
221
|
-
localPort: undefined,
|
|
222
|
-
connectedAt: undefined
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
this.emit('closed')
|
|
226
|
-
} catch (error) {
|
|
227
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
228
|
-
this.setState({ lastError: errorMessage })
|
|
229
|
-
this.emit('error', error)
|
|
230
|
-
throw error
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Get the current public URL if connected.
|
|
237
|
-
*
|
|
238
|
-
* Returns the live URL from the ngrok listener, or `undefined` if no tunnel is active.
|
|
239
|
-
*
|
|
240
|
-
* @returns The public HTTPS URL string, or undefined when disconnected
|
|
241
|
-
*
|
|
242
|
-
* @example
|
|
243
|
-
* ```typescript
|
|
244
|
-
* const exposer = container.feature('portExposer', { port: 3000 })
|
|
245
|
-
* await exposer.expose()
|
|
246
|
-
* console.log(exposer.getPublicUrl()) // 'https://abc123.ngrok.io'
|
|
247
|
-
* ```
|
|
248
|
-
*/
|
|
249
|
-
getPublicUrl(): string | undefined {
|
|
250
|
-
return this.ngrokListener?.url() || undefined
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Check if the ngrok tunnel is currently connected.
|
|
255
|
-
*
|
|
256
|
-
* @returns `true` when an active tunnel exists, `false` otherwise
|
|
257
|
-
*
|
|
258
|
-
* @example
|
|
259
|
-
* ```typescript
|
|
260
|
-
* const exposer = container.feature('portExposer', { port: 3000 })
|
|
261
|
-
* console.log(exposer.isConnected()) // false
|
|
262
|
-
* await exposer.expose()
|
|
263
|
-
* console.log(exposer.isConnected()) // true
|
|
264
|
-
* ```
|
|
265
|
-
*/
|
|
266
|
-
isConnected(): boolean {
|
|
267
|
-
return this.state.get('connected') ?? false
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Get a snapshot of the current connection information.
|
|
272
|
-
*
|
|
273
|
-
* Returns an object with the tunnel's connected status, public URL,
|
|
274
|
-
* local port, connection timestamp, and session metadata.
|
|
275
|
-
*
|
|
276
|
-
* @returns An object containing `connected`, `publicUrl`, `localPort`, `connectedAt`, and `sessionInfo`
|
|
277
|
-
*
|
|
278
|
-
* @example
|
|
279
|
-
* ```typescript
|
|
280
|
-
* const exposer = container.feature('portExposer', { port: 3000 })
|
|
281
|
-
* await exposer.expose()
|
|
282
|
-
* const info = exposer.getConnectionInfo()
|
|
283
|
-
* console.log(info.publicUrl, info.localPort, info.connectedAt)
|
|
284
|
-
* ```
|
|
285
|
-
*/
|
|
286
|
-
getConnectionInfo() {
|
|
287
|
-
const state = this.state.current
|
|
288
|
-
return {
|
|
289
|
-
connected: state.connected,
|
|
290
|
-
publicUrl: state.publicUrl,
|
|
291
|
-
localPort: state.localPort,
|
|
292
|
-
connectedAt: state.connectedAt,
|
|
293
|
-
sessionInfo: state.sessionInfo
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Close the existing tunnel and re-expose with optionally updated options.
|
|
299
|
-
*
|
|
300
|
-
* Calls `close()` first, merges any new options, then calls `expose()`.
|
|
301
|
-
*
|
|
302
|
-
* @param newOptions - Optional partial options to merge before reconnecting
|
|
303
|
-
* @returns Promise resolving to the new public URL string
|
|
304
|
-
*
|
|
305
|
-
* @example
|
|
306
|
-
* ```typescript
|
|
307
|
-
* const exposer = container.feature('portExposer', { port: 3000 })
|
|
308
|
-
* await exposer.expose()
|
|
309
|
-
* // Switch to a different port
|
|
310
|
-
* const newUrl = await exposer.reconnect({ port: 8080 })
|
|
311
|
-
* ```
|
|
312
|
-
*/
|
|
313
|
-
async reconnect(newOptions?: Partial<PortExposerOptions>): Promise<string> {
|
|
314
|
-
await this.close()
|
|
315
|
-
|
|
316
|
-
if (newOptions) {
|
|
317
|
-
Object.assign(this.options, newOptions)
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return this.expose()
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Disable the feature, ensuring the ngrok tunnel is closed first.
|
|
325
|
-
*
|
|
326
|
-
* Overrides the base `disable()` to guarantee that the tunnel is
|
|
327
|
-
* torn down before the feature is marked as disabled.
|
|
328
|
-
*
|
|
329
|
-
* @returns This PortExposer instance
|
|
330
|
-
*
|
|
331
|
-
* @example
|
|
332
|
-
* ```typescript
|
|
333
|
-
* const exposer = container.feature('portExposer', { port: 3000 })
|
|
334
|
-
* await exposer.expose()
|
|
335
|
-
* await exposer.disable()
|
|
336
|
-
* ```
|
|
337
|
-
*/
|
|
338
|
-
async disable(): Promise<this> {
|
|
339
|
-
await this.close()
|
|
340
|
-
return this
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
export default PortExposer
|
|
345
|
-
// Module augmentation for type safety
|
|
346
|
-
declare module '../../feature.js' {
|
|
347
|
-
interface AvailableFeatures {
|
|
348
|
-
portExposer: typeof PortExposer
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|