@dbx-tools/tunnel 0.6.64 → 0.6.66
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 +13 -0
- package/index.ts +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/src/interceptor.d.ts +4 -10
- package/lib/src/interceptor.js +9 -24
- package/lib/src/portr.d.ts +17 -13
- package/lib/src/portr.js +62 -39
- package/package.json +7 -6
- package/src/interceptor.ts +8 -23
- package/src/portr.ts +86 -39
package/README.md
CHANGED
|
@@ -355,6 +355,19 @@ the cookie, because that request was never going to be gated. So the same runnin
|
|
|
355
355
|
app shows the OTP screen to a portr visitor and no login at all to a browser on
|
|
356
356
|
`localhost` or the platform front door.
|
|
357
357
|
|
|
358
|
+
## Verify The Live Portr Install
|
|
359
|
+
|
|
360
|
+
The normal test task skips the networked portr release download. Run the
|
|
361
|
+
integration test explicitly when validating GitHub release discovery, archive
|
|
362
|
+
selection, executable installation, and the downloaded CLI:
|
|
363
|
+
|
|
364
|
+
```sh
|
|
365
|
+
RUN_LIVE_INSTALL_TEST=1 bun test packages/node/tunnel/test/portr-install.integration.test.ts
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
The test installs into an isolated temporary home, runs `portr --version`, logs
|
|
369
|
+
the selected release and executable path, then removes the temporary directory.
|
|
370
|
+
|
|
358
371
|
## Modules
|
|
359
372
|
|
|
360
373
|
- `interceptor` - `tunnelInterceptor()`, the `createApp` interceptor that applies
|
package/index.ts
CHANGED
|
@@ -25,7 +25,7 @@ export { CodeStore } from "./src/otp.ts";
|
|
|
25
25
|
export type { VerifyOutcome } from "./src/otp.ts";
|
|
26
26
|
export { AuthGatePlugin, authGate } from "./src/plugin.ts";
|
|
27
27
|
export type { AuthGateConfig, SendCodeOptions, ResolvedAuthGateConfig, AuthGateApi } from "./src/plugin.ts";
|
|
28
|
-
export type { PortrConfig } from "./src/portr.ts";
|
|
28
|
+
export type { PortrInstallOptions, PortrConfig } from "./src/portr.ts";
|
|
29
29
|
export { RateLimiter } from "./src/rate-limit.ts";
|
|
30
30
|
export { KEY_TTL_SECONDS } from "./src/signing-key.ts";
|
|
31
31
|
export type { SigningKey } from "./src/signing-key.ts";
|
package/lib/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export { CodeStore } from "./src/otp.ts";
|
|
|
21
21
|
export type { VerifyOutcome } from "./src/otp.ts";
|
|
22
22
|
export { AuthGatePlugin, authGate } from "./src/plugin.ts";
|
|
23
23
|
export type { AuthGateConfig, SendCodeOptions, ResolvedAuthGateConfig, AuthGateApi } from "./src/plugin.ts";
|
|
24
|
-
export type { PortrConfig } from "./src/portr.ts";
|
|
24
|
+
export type { PortrInstallOptions, PortrConfig } from "./src/portr.ts";
|
|
25
25
|
export { RateLimiter } from "./src/rate-limit.ts";
|
|
26
26
|
export { KEY_TTL_SECONDS } from "./src/signing-key.ts";
|
|
27
27
|
export type { SigningKey } from "./src/signing-key.ts";
|
package/lib/index.js
CHANGED
|
@@ -20,4 +20,4 @@ export { CodeStore } from "./src/otp.js";
|
|
|
20
20
|
export { AuthGatePlugin, authGate } from "./src/plugin.js";
|
|
21
21
|
export { RateLimiter } from "./src/rate-limit.js";
|
|
22
22
|
export { KEY_TTL_SECONDS } from "./src/signing-key.js";
|
|
23
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssU0FBUyxNQUFNLG9CQUFvQixDQUFDO0FBQ2hELE9BQU8sS0FBSyxTQUFTLE1BQU0scUJBQXFCLENBQUM7QUFDakQsT0FBTyxLQUFLLEdBQUcsTUFBTSxjQUFjLENBQUM7QUFDcEMsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUM1QyxPQUFPLEtBQUssV0FBVyxNQUFNLHNCQUFzQixDQUFDO0FBQ3BELE9BQU8sS0FBSyxHQUFHLE1BQU0sY0FBYyxDQUFDO0FBQ3BDLE9BQU8sS0FBSyxNQUFNLE1BQU0saUJBQWlCLENBQUM7QUFDMUMsT0FBTyxLQUFLLEtBQUssTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QyxPQUFPLEtBQUssU0FBUyxNQUFNLHFCQUFxQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxRQUFRLE1BQU0sb0JBQW9CLENBQUM7QUFDL0MsT0FBTyxLQUFLLFVBQVUsTUFBTSxzQkFBc0IsQ0FBQztBQUVuRCxPQUFPLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQUUsV0FBVyxFQUFFLGVBQWUsRUFBRSxZQUFZLEVBQUUsY0FBYyxFQUFFLGtCQUFrQixFQUFFLGlCQUFpQixFQUFFLFlBQVksRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUM1TSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTVDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRzlFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFekMsT0FBTyxFQUFFLGNBQWMsRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUczRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0ICogYXMgYWxsb3dsaXN0IGZyb20gXCIuL3NyYy9hbGxvd2xpc3QudHNcIjtcbmV4cG9ydCAqIGFzIGNvZGVFbWFpbCBmcm9tIFwiLi9zcmMvY29kZS1lbWFpbC50c1wiO1xuZXhwb3J0ICogYXMgZW52IGZyb20gXCIuL3NyYy9lbnYudHNcIjtcbmV4cG9ydCAqIGFzIGdhdGUgZnJvbSBcIi4vc3JjL2dhdGUudHNcIjtcbmV4cG9ydCAqIGFzIGhlYWRlcnMgZnJvbSBcIi4vc3JjL2hlYWRlcnMudHNcIjtcbmV4cG9ydCAqIGFzIGludGVyY2VwdG9yIGZyb20gXCIuL3NyYy9pbnRlcmNlcHRvci50c1wiO1xuZXhwb3J0ICogYXMgb3RwIGZyb20gXCIuL3NyYy9vdHAudHNcIjtcbmV4cG9ydCAqIGFzIHBsdWdpbiBmcm9tIFwiLi9zcmMvcGx1Z2luLnRzXCI7XG5leHBvcnQgKiBhcyBwb3J0ciBmcm9tIFwiLi9zcmMvcG9ydHIudHNcIjtcbmV4cG9ydCAqIGFzIHJhdGVMaW1pdCBmcm9tIFwiLi9zcmMvcmF0ZS1saW1pdC50c1wiO1xuZXhwb3J0ICogYXMgc2VuZENvZGUgZnJvbSBcIi4vc3JjL3NlbmQtY29kZS50c1wiO1xuZXhwb3J0ICogYXMgc2lnbmluZ0tleSBmcm9tIFwiLi9zcmMvc2lnbmluZy1rZXkudHNcIjtcbmV4cG9ydCB0eXBlIHsgQ29kZUNvcHkgfSBmcm9tIFwiLi9zcmMvY29kZS1lbWFpbC50c1wiO1xuZXhwb3J0IHsgQUxMT1dfRU5WLCBTVUJKRUNUX0VOViwgQlJBTkRfTkFNRV9FTlYsIE1FU1NBR0VfRU5WLCBTRVNTSU9OX1RUTF9FTlYsIENPREVfVFRMX0VOViwgSldUX1NFQ1JFVF9FTlYsIFNFU1NJT05fQ1VUT0ZGX0VOViwgUFVCTElDX0RPTUFJTl9FTlYsIElOU0VDVVJFX0VOViwgRk9SV0FSRF9IRUFERVJTX0VOViB9IGZyb20gXCIuL3NyYy9lbnYudHNcIjtcbmV4cG9ydCB7IEFVVEhfUFJFRklYIH0gZnJvbSBcIi4vc3JjL2dhdGUudHNcIjtcbmV4cG9ydCB0eXBlIHsgR2F0ZU9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvZ2F0ZS50c1wiO1xuZXhwb3J0IHsgUFJPVEVDVEVEX0hFQURFUlMsIERFRkFVTFRfRk9SV0FSRF9IRUFERVJTIH0gZnJvbSBcIi4vc3JjL2hlYWRlcnMudHNcIjtcbmV4cG9ydCB0eXBlIHsgSGVhZGVyUG9saWN5IH0gZnJvbSBcIi4vc3JjL2hlYWRlcnMudHNcIjtcbmV4cG9ydCB0eXBlIHsgVHVubmVsSW50ZXJjZXB0b3JPcHRpb25zIH0gZnJvbSBcIi4vc3JjL2ludGVyY2VwdG9yLnRzXCI7XG5leHBvcnQgeyBDb2RlU3RvcmUgfSBmcm9tIFwiLi9zcmMvb3RwLnRzXCI7XG5leHBvcnQgdHlwZSB7IFZlcmlmeU91dGNvbWUgfSBmcm9tIFwiLi9zcmMvb3RwLnRzXCI7XG5leHBvcnQgeyBBdXRoR2F0ZVBsdWdpbiwgYXV0aEdhdGUgfSBmcm9tIFwiLi9zcmMvcGx1Z2luLnRzXCI7XG5leHBvcnQgdHlwZSB7IEF1dGhHYXRlQ29uZmlnLCBTZW5kQ29kZU9wdGlvbnMsIFJlc29sdmVkQXV0aEdhdGVDb25maWcsIEF1dGhHYXRlQXBpIH0gZnJvbSBcIi4vc3JjL3BsdWdpbi50c1wiO1xuZXhwb3J0IHR5cGUgeyBQb3J0ckluc3RhbGxPcHRpb25zLCBQb3J0ckNvbmZpZyB9IGZyb20gXCIuL3NyYy9wb3J0ci50c1wiO1xuZXhwb3J0IHsgUmF0ZUxpbWl0ZXIgfSBmcm9tIFwiLi9zcmMvcmF0ZS1saW1pdC50c1wiO1xuZXhwb3J0IHsgS0VZX1RUTF9TRUNPTkRTIH0gZnJvbSBcIi4vc3JjL3NpZ25pbmcta2V5LnRzXCI7XG5leHBvcnQgdHlwZSB7IFNpZ25pbmdLZXkgfSBmcm9tIFwiLi9zcmMvc2lnbmluZy1rZXkudHNcIjtcbiJdfQ==
|
package/lib/src/interceptor.d.ts
CHANGED
|
@@ -5,16 +5,10 @@
|
|
|
5
5
|
*
|
|
6
6
|
* This is the in-process replacement for the old `dbxt-tunnel -- <cmd>` wrapper.
|
|
7
7
|
* Instead of the tunnel being the main process that spawns the app as a child, the
|
|
8
|
-
* APP is the main process and hands this interceptor its context. The interceptor
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 2. installs + launches portr, pointed at the app's PUBLIC port;
|
|
13
|
-
* 3. `bindProcess`es portr so the app and the tunnel live and die as one -
|
|
14
|
-
* signals pass through and either death tears the pair down (the
|
|
15
|
-
* concurrently-style supervision that used to be `superviseExit`);
|
|
16
|
-
* 4. registers an AppKit `shutdown` lifecycle handler so an orderly app shutdown
|
|
17
|
-
* also stops portr.
|
|
8
|
+
* APP is the main process and hands this interceptor its context. The interceptor
|
|
9
|
+
* applies the computed workspace host, installs and launches portr on the app's
|
|
10
|
+
* public port, binds the child process to the app, and stops portr during an
|
|
11
|
+
* orderly AppKit shutdown.
|
|
18
12
|
*
|
|
19
13
|
* The email-OTP GATE is a separate concern: it is the `authGate` AppKit plugin,
|
|
20
14
|
* which registers the login routes + a gating middleware on the app's own server.
|
package/lib/src/interceptor.js
CHANGED
|
@@ -5,16 +5,10 @@
|
|
|
5
5
|
*
|
|
6
6
|
* This is the in-process replacement for the old `dbxt-tunnel -- <cmd>` wrapper.
|
|
7
7
|
* Instead of the tunnel being the main process that spawns the app as a child, the
|
|
8
|
-
* APP is the main process and hands this interceptor its context. The interceptor
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 2. installs + launches portr, pointed at the app's PUBLIC port;
|
|
13
|
-
* 3. `bindProcess`es portr so the app and the tunnel live and die as one -
|
|
14
|
-
* signals pass through and either death tears the pair down (the
|
|
15
|
-
* concurrently-style supervision that used to be `superviseExit`);
|
|
16
|
-
* 4. registers an AppKit `shutdown` lifecycle handler so an orderly app shutdown
|
|
17
|
-
* also stops portr.
|
|
8
|
+
* APP is the main process and hands this interceptor its context. The interceptor
|
|
9
|
+
* applies the computed workspace host, installs and launches portr on the app's
|
|
10
|
+
* public port, binds the child process to the app, and stops portr during an
|
|
11
|
+
* orderly AppKit shutdown.
|
|
18
12
|
*
|
|
19
13
|
* The email-OTP GATE is a separate concern: it is the `authGate` AppKit plugin,
|
|
20
14
|
* which registers the login routes + a gating middleware on the app's own server.
|
|
@@ -47,16 +41,10 @@ function resolvePublicPort(port) {
|
|
|
47
41
|
* });
|
|
48
42
|
*/
|
|
49
43
|
export function tunnelInterceptor(options = {}) {
|
|
50
|
-
return (ctx) => {
|
|
51
|
-
// 1. Apply the computed workspace host so portr + the SDK agree. The context
|
|
52
|
-
// already resolved it (from the env / auto-config); set it only when it is
|
|
53
|
-
// known and not already present, so an explicit env wins.
|
|
44
|
+
return async (ctx) => {
|
|
54
45
|
if (ctx.env.databricksHost) {
|
|
55
46
|
process.env.DATABRICKS_HOST ??= ctx.env.databricksHost;
|
|
56
47
|
}
|
|
57
|
-
// 2. Resolve portr wiring. No token / domain -> no tunnel; leave the app alone.
|
|
58
|
-
// `resolvePortrConfig` already falls back to TUNNEL_PUBLIC_DOMAIN itself, so
|
|
59
|
-
// the explicit option is passed straight through.
|
|
60
48
|
const port = resolvePublicPort(options.port);
|
|
61
49
|
const portrConfig = resolvePortrConfig({
|
|
62
50
|
publicDomain: options.publicDomain,
|
|
@@ -67,13 +55,10 @@ export function tunnelInterceptor(options = {}) {
|
|
|
67
55
|
logger.info("no PORTR_TOKEN/TUNNEL_PUBLIC_DOMAIN - the app runs without a public tunnel");
|
|
68
56
|
return;
|
|
69
57
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const portr = startPortr(portrConfig, portrEnv);
|
|
58
|
+
const portrEnv = await installPortr();
|
|
59
|
+
await writePortrConfig(portrConfig, portrEnv);
|
|
60
|
+
const portr = await startPortr(portrConfig, portrEnv);
|
|
74
61
|
ctx.bindProcess(portr);
|
|
75
|
-
// 4. An orderly AppKit shutdown also stops portr (belt-and-suspenders with the
|
|
76
|
-
// signal pass-through `bindProcess` already installed).
|
|
77
62
|
ctx.onLifecycle("shutdown", () => {
|
|
78
63
|
if (!portr.killed)
|
|
79
64
|
portr.kill("SIGTERM");
|
|
@@ -81,4 +66,4 @@ export function tunnelInterceptor(options = {}) {
|
|
|
81
66
|
logger.info(`tunnel bound: portr -> :${port} (${portrConfig.subdomain}.${portrConfig.server})`);
|
|
82
67
|
};
|
|
83
68
|
}
|
|
84
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
69
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJjZXB0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW50ZXJjZXB0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRztBQUdILE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxrQkFBa0IsRUFBRSxVQUFVLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFNUYsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0FBZ0JoRCxxRkFBcUY7QUFDckYsU0FBUyxpQkFBaUIsQ0FBQyxJQUFhO0lBQ3RDLE9BQU8sSUFBSSxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLElBQUksQ0FBQztBQUMxRSxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUFDLFVBQW9DLEVBQUU7SUFDdEUsT0FBTyxLQUFLLEVBQUUsR0FBdUIsRUFBaUIsRUFBRTtRQUN0RCxJQUFJLEdBQUcsQ0FBQyxHQUFHLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDM0IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLEtBQUssR0FBRyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUM7UUFDekQsQ0FBQztRQUVELE1BQU0sSUFBSSxHQUFHLGlCQUFpQixDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QyxNQUFNLFdBQVcsR0FBRyxrQkFBa0IsQ0FBQztZQUNyQyxZQUFZLEVBQUUsT0FBTyxDQUFDLFlBQVk7WUFDbEMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxTQUFTO1lBQzVCLElBQUk7U0FDTCxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDakIsTUFBTSxDQUFDLElBQUksQ0FBQyw0RUFBNEUsQ0FBQyxDQUFDO1lBQzFGLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxRQUFRLEdBQUcsTUFBTSxZQUFZLEVBQUUsQ0FBQztRQUN0QyxNQUFNLGdCQUFnQixDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUM5QyxNQUFNLEtBQUssR0FBRyxNQUFNLFVBQVUsQ0FBQyxXQUFXLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDdEQsR0FBRyxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUV2QixHQUFHLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxHQUFHLEVBQUU7WUFDL0IsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNO2dCQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDM0MsQ0FBQyxDQUFDLENBQUM7UUFFSCxNQUFNLENBQUMsSUFBSSxDQUFDLDJCQUEyQixJQUFJLEtBQUssV0FBVyxDQUFDLFNBQVMsSUFBSSxXQUFXLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUNsRyxDQUFDLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBgdHVubmVsSW50ZXJjZXB0b3IoKWAgLSB0aGUge0BsaW5rIEludGVyY2VwdG9yfSB0aGF0IGZyb250cyBhbiBhcHAgd2l0aCBhIHB1YmxpY1xuICogcG9ydHIgdHVubmVsLCBjb25zdW1pbmcgdGhlIHtAbGluayBJbnRlcmNlcHRvckNvbnRleHR9IGBAZGJ4LXRvb2xzL2FwcGtpdGAnc1xuICogYGNyZWF0ZUFwcGAgaGFuZHMgaXQuXG4gKlxuICogVGhpcyBpcyB0aGUgaW4tcHJvY2VzcyByZXBsYWNlbWVudCBmb3IgdGhlIG9sZCBgZGJ4dC10dW5uZWwgLS0gPGNtZD5gIHdyYXBwZXIuXG4gKiBJbnN0ZWFkIG9mIHRoZSB0dW5uZWwgYmVpbmcgdGhlIG1haW4gcHJvY2VzcyB0aGF0IHNwYXducyB0aGUgYXBwIGFzIGEgY2hpbGQsIHRoZVxuICogQVBQIGlzIHRoZSBtYWluIHByb2Nlc3MgYW5kIGhhbmRzIHRoaXMgaW50ZXJjZXB0b3IgaXRzIGNvbnRleHQuIFRoZSBpbnRlcmNlcHRvclxuICogYXBwbGllcyB0aGUgY29tcHV0ZWQgd29ya3NwYWNlIGhvc3QsIGluc3RhbGxzIGFuZCBsYXVuY2hlcyBwb3J0ciBvbiB0aGUgYXBwJ3NcbiAqIHB1YmxpYyBwb3J0LCBiaW5kcyB0aGUgY2hpbGQgcHJvY2VzcyB0byB0aGUgYXBwLCBhbmQgc3RvcHMgcG9ydHIgZHVyaW5nIGFuXG4gKiBvcmRlcmx5IEFwcEtpdCBzaHV0ZG93bi5cbiAqXG4gKiBUaGUgZW1haWwtT1RQIEdBVEUgaXMgYSBzZXBhcmF0ZSBjb25jZXJuOiBpdCBpcyB0aGUgYGF1dGhHYXRlYCBBcHBLaXQgcGx1Z2luLFxuICogd2hpY2ggcmVnaXN0ZXJzIHRoZSBsb2dpbiByb3V0ZXMgKyBhIGdhdGluZyBtaWRkbGV3YXJlIG9uIHRoZSBhcHAncyBvd24gc2VydmVyLlxuICogUmVnaXN0ZXIgaXQgaW4gdGhlIGFwcCdzIGBwbHVnaW5zYCBmb3IgZ2F0ZWQgdHJhZmZpYy4gVGhpcyBpbnRlcmNlcHRvciBpcyBvbmx5XG4gKiB0aGUgcG9ydHIgaGFsZiAtIFwidXBkYXRlIHRoZSBob3N0LCBiaW5kIHBvcnRyXCIgLSB0aGUgc21hbGxlc3QgdXNlZnVsIHVuaXQuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB0eXBlIHsgSW50ZXJjZXB0b3IsIEludGVyY2VwdG9yQ29udGV4dCB9IGZyb20gXCJAZGJ4LXRvb2xzL2FwcGtpdFwiO1xuaW1wb3J0IHsgbG9nLCBvYmplY3QgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgaW5zdGFsbFBvcnRyLCByZXNvbHZlUG9ydHJDb25maWcsIHN0YXJ0UG9ydHIsIHdyaXRlUG9ydHJDb25maWcgfSBmcm9tIFwiLi9wb3J0ci50c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwidHVubmVsOmludGVyY2VwdG9yXCIpO1xuXG4vKiogT3B0aW9ucyBmb3Ige0BsaW5rIHR1bm5lbEludGVyY2VwdG9yfSAoZWFjaCBhbHNvIHJlc29sdmFibGUgZnJvbSBlbnYpLiAqL1xuZXhwb3J0IGludGVyZmFjZSBUdW5uZWxJbnRlcmNlcHRvck9wdGlvbnMge1xuICAvKiogcG9ydHIgYDxzdWJkb21haW4+LjxzZXJ2ZXI+YCB0byBzZXJ2ZSBvbi4gRW52IGBUVU5ORUxfUFVCTElDX0RPTUFJTmAuICovXG4gIHB1YmxpY0RvbWFpbj86IHN0cmluZztcbiAgLyoqIHBvcnRyIHN1YmRvbWFpbiAoZWxzZSBkZXJpdmVkIGZyb20ge0BsaW5rIHB1YmxpY0RvbWFpbn0pLiAqL1xuICBzdWJkb21haW4/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBUaGUgUFVCTElDIHBvcnQgcG9ydHIgZm9yd2FyZHMgdG8gLSB0aGUgcG9ydCB0aGUgYXBwIGl0c2VsZiBsaXN0ZW5zIG9uLlxuICAgKiBEZWZhdWx0cyB0byB0aGUgRGF0YWJyaWNrcyBBcHBzIHJ1bnRpbWUgY29udHJhY3QgYERBVEFCUklDS1NfQVBQX1BPUlRgXG4gICAqICh0aGVuIGA4MDAwYCksIHdoaWNoIGlzIHRoZSBwb3J0IHRoZSBwbGF0Zm9ybSByb3V0ZXMgdG8uXG4gICAqL1xuICBwb3J0PzogbnVtYmVyO1xufVxuXG4vKiogUmVzb2x2ZSB0aGUgcHVibGljIHBvcnQgcG9ydHIgc2hvdWxkIHRhcmdldDogZXhwbGljaXQsIGVsc2UgdGhlIEFwcHMgY29udHJhY3QuICovXG5mdW5jdGlvbiByZXNvbHZlUHVibGljUG9ydChwb3J0PzogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIHBvcnQgPz8gb2JqZWN0LnRvTnVtYmVyKHByb2Nlc3MuZW52LkRBVEFCUklDS1NfQVBQX1BPUlQpID8/IDgwMDA7XG59XG5cbi8qKlxuICogQnVpbGQgdGhlIHR1bm5lbCB7QGxpbmsgSW50ZXJjZXB0b3J9LiBQYXNzIGl0IHRvIGBjcmVhdGVBcHAoeyBpbnRlcmNlcHRvciB9KWAuXG4gKlxuICogQSBuby1vcCAobG9ncyBhbmQgcmV0dXJucykgd2hlbiBubyBwb3J0ciB0dW5uZWwgaXMgY29uZmlndXJlZCAtIG5vIGBQT1JUUl9UT0tFTmBcbiAqIG9yIG5vIHJlc29sdmFibGUgYDxzdWJkb21haW4+LjxzZXJ2ZXI+YCAtIHNvIGFuIGFwcCBjYW4gcmVnaXN0ZXIgaXRcbiAqIHVuY29uZGl0aW9uYWxseSBhbmQgb25seSBhY3R1YWxseSB0dW5uZWxzIHdoZXJlIHRoZSBkZXBsb3ltZW50IHdpcmVkIHBvcnRyLlxuICpcbiAqIEBleGFtcGxlXG4gKiBpbXBvcnQgeyBjcmVhdGVBcHAgfSBmcm9tIFwiQGRieC10b29scy9hcHBraXRcIjtcbiAqIGltcG9ydCB7IHR1bm5lbEludGVyY2VwdG9yIH0gZnJvbSBcIkBkYngtdG9vbHMvdHVubmVsXCI7XG4gKlxuICogYXdhaXQgY3JlYXRlQXBwKHtcbiAqICAgcGx1Z2luczogW3NlcnZlcih7IGhvc3QsIHN0YXRpY1BhdGggfSldLFxuICogICBpbnRlcmNlcHRvcjogdHVubmVsSW50ZXJjZXB0b3IoKSxcbiAqIH0pO1xuICovXG5leHBvcnQgZnVuY3Rpb24gdHVubmVsSW50ZXJjZXB0b3Iob3B0aW9uczogVHVubmVsSW50ZXJjZXB0b3JPcHRpb25zID0ge30pOiBJbnRlcmNlcHRvciB7XG4gIHJldHVybiBhc3luYyAoY3R4OiBJbnRlcmNlcHRvckNvbnRleHQpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBpZiAoY3R4LmVudi5kYXRhYnJpY2tzSG9zdCkge1xuICAgICAgcHJvY2Vzcy5lbnYuREFUQUJSSUNLU19IT1NUID8/PSBjdHguZW52LmRhdGFicmlja3NIb3N0O1xuICAgIH1cblxuICAgIGNvbnN0IHBvcnQgPSByZXNvbHZlUHVibGljUG9ydChvcHRpb25zLnBvcnQpO1xuICAgIGNvbnN0IHBvcnRyQ29uZmlnID0gcmVzb2x2ZVBvcnRyQ29uZmlnKHtcbiAgICAgIHB1YmxpY0RvbWFpbjogb3B0aW9ucy5wdWJsaWNEb21haW4sXG4gICAgICBzdWJkb21haW46IG9wdGlvbnMuc3ViZG9tYWluLFxuICAgICAgcG9ydCxcbiAgICB9KTtcbiAgICBpZiAoIXBvcnRyQ29uZmlnKSB7XG4gICAgICBsb2dnZXIuaW5mbyhcIm5vIFBPUlRSX1RPS0VOL1RVTk5FTF9QVUJMSUNfRE9NQUlOIC0gdGhlIGFwcCBydW5zIHdpdGhvdXQgYSBwdWJsaWMgdHVubmVsXCIpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHBvcnRyRW52ID0gYXdhaXQgaW5zdGFsbFBvcnRyKCk7XG4gICAgYXdhaXQgd3JpdGVQb3J0ckNvbmZpZyhwb3J0ckNvbmZpZywgcG9ydHJFbnYpO1xuICAgIGNvbnN0IHBvcnRyID0gYXdhaXQgc3RhcnRQb3J0cihwb3J0ckNvbmZpZywgcG9ydHJFbnYpO1xuICAgIGN0eC5iaW5kUHJvY2Vzcyhwb3J0cik7XG5cbiAgICBjdHgub25MaWZlY3ljbGUoXCJzaHV0ZG93blwiLCAoKSA9PiB7XG4gICAgICBpZiAoIXBvcnRyLmtpbGxlZCkgcG9ydHIua2lsbChcIlNJR1RFUk1cIik7XG4gICAgfSk7XG5cbiAgICBsb2dnZXIuaW5mbyhgdHVubmVsIGJvdW5kOiBwb3J0ciAtPiA6JHtwb3J0fSAoJHtwb3J0ckNvbmZpZy5zdWJkb21haW59LiR7cG9ydHJDb25maWcuc2VydmVyfSlgKTtcbiAgfTtcbn1cbiJdfQ==
|
package/lib/src/portr.d.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* portr install
|
|
2
|
+
* portr install, config, and launch support for the tunnel runtime.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* + `PORTR_TOKEN` and points portr at the PUBLIC port (the proxy listens there).
|
|
4
|
+
* The binary and config use portr's conventional `$HOME/.portr` tree. The
|
|
5
|
+
* install is idempotent (the installer skips an existing executable). The
|
|
6
|
+
* config is rendered from `TUNNEL_PUBLIC_DOMAIN`
|
|
7
|
+
* (`<subdomain>.<server>`) + `PORTR_TOKEN` and points portr at the PUBLIC port
|
|
8
|
+
* (the proxy listens there).
|
|
10
9
|
*
|
|
11
10
|
* @module
|
|
12
11
|
*/
|
|
13
12
|
import { spawn } from "node:child_process";
|
|
13
|
+
/** Options for installing the portr executable. */
|
|
14
|
+
export interface PortrInstallOptions {
|
|
15
|
+
/** Home directory containing `.portr`; defaults to the OS home directory. */
|
|
16
|
+
homeDir?: string;
|
|
17
|
+
}
|
|
14
18
|
/** Resolved portr wiring, or `undefined` when no tunnel is configured. */
|
|
15
19
|
export interface PortrConfig {
|
|
16
20
|
subdomain: string;
|
|
@@ -30,11 +34,11 @@ export declare function resolvePortrConfig(opts: {
|
|
|
30
34
|
token?: string;
|
|
31
35
|
port: number;
|
|
32
36
|
}): PortrConfig | undefined;
|
|
33
|
-
/**
|
|
34
|
-
export declare function
|
|
37
|
+
/** GitHub release asset name for the current or supplied OS/architecture. */
|
|
38
|
+
export declare function portrAssetName(version: string, platform?: NodeJS.Platform, arch?: string): string;
|
|
39
|
+
/** Install portr when absent and return the environment used by its process. */
|
|
40
|
+
export declare function installPortr(options?: PortrInstallOptions): Promise<NodeJS.ProcessEnv>;
|
|
35
41
|
/** Render `~/.portr/config.yaml` for the resolved tunnel. */
|
|
36
|
-
export declare function writePortrConfig(config: PortrConfig,
|
|
42
|
+
export declare function writePortrConfig(config: PortrConfig, childEnv: NodeJS.ProcessEnv): Promise<void>;
|
|
37
43
|
/** Launch `portr start` as a child process (caller supervises + kills it). */
|
|
38
|
-
export declare function startPortr(config: PortrConfig,
|
|
39
|
-
/** True when the installed portr binary path exists (post-install sanity). */
|
|
40
|
-
export declare function portrInstalled(env: NodeJS.ProcessEnv): boolean;
|
|
44
|
+
export declare function startPortr(config: PortrConfig, childEnv: NodeJS.ProcessEnv): Promise<ReturnType<typeof spawn>>;
|
package/lib/src/portr.js
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* portr install
|
|
2
|
+
* portr install, config, and launch support for the tunnel runtime.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* + `PORTR_TOKEN` and points portr at the PUBLIC port (the proxy listens there).
|
|
4
|
+
* The binary and config use portr's conventional `$HOME/.portr` tree. The
|
|
5
|
+
* install is idempotent (the installer skips an existing executable). The
|
|
6
|
+
* config is rendered from `TUNNEL_PUBLIC_DOMAIN`
|
|
7
|
+
* (`<subdomain>.<server>`) + `PORTR_TOKEN` and points portr at the PUBLIC port
|
|
8
|
+
* (the proxy listens there).
|
|
10
9
|
*
|
|
11
10
|
* @module
|
|
12
11
|
*/
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
12
|
+
import { execFile, spawn } from "node:child_process";
|
|
13
|
+
import { chmod, writeFile } from "node:fs/promises";
|
|
14
|
+
import os from "node:os";
|
|
15
15
|
import { delimiter, join } from "node:path";
|
|
16
|
+
import { promisify } from "node:util";
|
|
17
|
+
import { bin } from "@dbx-tools/core";
|
|
16
18
|
import { env, log } from "@dbx-tools/shared-core";
|
|
17
19
|
import { PUBLIC_DOMAIN_ENV } from "./env.js";
|
|
18
20
|
const logger = log.logger("tunnel:portr");
|
|
21
|
+
const PORTR_LATEST_RELEASE_URL = "https://api.github.com/repos/amalshaji/portr/releases/latest";
|
|
22
|
+
const execFileAsync = promisify(execFile);
|
|
19
23
|
/**
|
|
20
24
|
* Resolve portr config from `TUNNEL_PUBLIC_DOMAIN` + `PORTR_TOKEN`, or an
|
|
21
25
|
* explicit `subdomain`. The domain is `<subdomain>.<server>` (e.g.
|
|
@@ -39,34 +43,57 @@ export function resolvePortrConfig(opts) {
|
|
|
39
43
|
return undefined;
|
|
40
44
|
return { subdomain, server, token, port: opts.port };
|
|
41
45
|
}
|
|
42
|
-
/**
|
|
43
|
-
function
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
/** GitHub release asset name for the current or supplied OS/architecture. */
|
|
47
|
+
export function portrAssetName(version, platform = process.platform, arch = process.arch) {
|
|
48
|
+
const osName = platform === "darwin" ? "Darwin" : platform === "linux" ? "Linux" : undefined;
|
|
49
|
+
const archName = arch === "arm64" ? "arm64" : arch === "x64" ? "x86_64" : undefined;
|
|
50
|
+
if (!osName || !archName) {
|
|
51
|
+
throw new Error(`portr has no supported release asset for ${platform}/${arch}`);
|
|
52
|
+
}
|
|
53
|
+
return `portr_${version}_${osName}_${archName}.zip`;
|
|
54
|
+
}
|
|
55
|
+
async function portrDownloadUrl() {
|
|
56
|
+
const releaseResponse = await fetch(PORTR_LATEST_RELEASE_URL, {
|
|
57
|
+
headers: {
|
|
58
|
+
accept: "application/vnd.github+json",
|
|
59
|
+
"user-agent": "@dbx-tools/tunnel",
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
if (!releaseResponse.ok) {
|
|
63
|
+
throw new Error(`portr release lookup failed (${releaseResponse.status})`);
|
|
64
|
+
}
|
|
65
|
+
const release = (await releaseResponse.json());
|
|
66
|
+
const version = release.tag_name.replace(/^v/, "");
|
|
67
|
+
const assetName = portrAssetName(version);
|
|
68
|
+
const asset = release.assets.find((candidate) => candidate.name === assetName);
|
|
69
|
+
if (!asset)
|
|
70
|
+
throw new Error(`portr release ${release.tag_name} has no ${assetName}`);
|
|
71
|
+
logger.info("installing portr", { version, asset: assetName });
|
|
72
|
+
return asset.browser_download_url;
|
|
47
73
|
}
|
|
48
|
-
/** Install portr
|
|
49
|
-
export function installPortr() {
|
|
50
|
-
const
|
|
51
|
-
const
|
|
74
|
+
/** Install portr when absent and return the environment used by its process. */
|
|
75
|
+
export async function installPortr(options = {}) {
|
|
76
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
77
|
+
const context = await bin.ensure("portr", portrDownloadUrl, {
|
|
78
|
+
autoUnpackage: true,
|
|
79
|
+
homeDir,
|
|
80
|
+
selector: async ({ source }) => {
|
|
81
|
+
const selected = join(source, "portr");
|
|
82
|
+
await chmod(selected, 0o755);
|
|
83
|
+
return selected;
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
52
87
|
...process.env,
|
|
53
|
-
HOME:
|
|
88
|
+
HOME: homeDir,
|
|
54
89
|
PORTR_AUTO_ADD_PATH: "no",
|
|
55
|
-
PATH: [
|
|
90
|
+
PATH: [context.binDir, process.env.PATH ?? ""].join(delimiter),
|
|
56
91
|
};
|
|
57
|
-
logger.info("installing portr (idempotent)");
|
|
58
|
-
const res = spawnSync("bash", ["-c", "curl -sSf https://install.portr.dev | sh"], {
|
|
59
|
-
env,
|
|
60
|
-
stdio: "inherit",
|
|
61
|
-
});
|
|
62
|
-
if (res.status !== 0)
|
|
63
|
-
throw new Error("portr install failed");
|
|
64
|
-
return env;
|
|
65
92
|
}
|
|
66
93
|
/** Render `~/.portr/config.yaml` for the resolved tunnel. */
|
|
67
|
-
export function writePortrConfig(config,
|
|
68
|
-
const path = join(
|
|
69
|
-
|
|
94
|
+
export async function writePortrConfig(config, childEnv) {
|
|
95
|
+
const path = join(childEnv.HOME ?? os.homedir(), ".portr", "config.yaml");
|
|
96
|
+
await writeFile(path, [
|
|
70
97
|
`server_url: ${config.server}`,
|
|
71
98
|
`ssh_url: ${config.server}:4444`,
|
|
72
99
|
`secret_key: ${config.token}`,
|
|
@@ -80,14 +107,10 @@ export function writePortrConfig(config, env) {
|
|
|
80
107
|
].join("\n"));
|
|
81
108
|
}
|
|
82
109
|
/** Launch `portr start` as a child process (caller supervises + kills it). */
|
|
83
|
-
export function startPortr(config,
|
|
110
|
+
export async function startPortr(config, childEnv) {
|
|
84
111
|
// Reclaim the subdomain from any portr left by a previous boot in this container.
|
|
85
|
-
|
|
112
|
+
await execFileAsync("pkill", ["-x", "portr"]).catch(() => undefined);
|
|
86
113
|
logger.info(`portr tunneling https://${config.subdomain}.${config.server} -> :${config.port}`);
|
|
87
|
-
return spawn("portr", ["start"], { env, stdio: "inherit" });
|
|
88
|
-
}
|
|
89
|
-
/** True when the installed portr binary path exists (post-install sanity). */
|
|
90
|
-
export function portrInstalled(env) {
|
|
91
|
-
return existsSync(join(env.HOME, ".portr", "bin", "portr"));
|
|
114
|
+
return spawn("portr", ["start"], { env: childEnv, stdio: "inherit" });
|
|
92
115
|
}
|
|
93
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9ydHIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcG9ydHIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7O0dBV0c7QUFFSCxPQUFPLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUMvRCxPQUFPLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUM1QyxPQUFPLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ2xELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUU3QyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0FBVTFDOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLGtCQUFrQixDQUFDLElBS2xDO0lBQ0MsbUVBQW1FO0lBQ25FLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxhQUFhLENBQUMsQ0FBQztJQUNwRCxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztJQUNoRSxJQUFJLENBQUMsS0FBSztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQzdCLElBQUksU0FBUyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDL0IsSUFBSSxNQUEwQixDQUFDO0lBQy9CLElBQUksTUFBTSxFQUFFLENBQUM7UUFDWCxTQUFTLEtBQUssTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNuQyxNQUFNLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ2pELENBQUM7SUFDRCxNQUFNLEtBQUssR0FBRyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxTQUFTLENBQUM7SUFDakQsSUFBSSxDQUFDLFNBQVMsSUFBSSxDQUFDLE1BQU0sSUFBSSxNQUFNLEtBQUssTUFBTTtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ2pFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO0FBQ3ZELENBQUM7QUFFRCx1RkFBdUY7QUFDdkYsU0FBUyxTQUFTO0lBQ2hCLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDMUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxFQUFFLEtBQUssQ0FBQyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDNUQsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDO0FBRUQsb0ZBQW9GO0FBQ3BGLE1BQU0sVUFBVSxZQUFZO0lBQzFCLE1BQU0sSUFBSSxHQUFHLFNBQVMsRUFBRSxDQUFDO0lBQ3pCLE1BQU0sR0FBRyxHQUFzQjtRQUM3QixHQUFHLE9BQU8sQ0FBQyxHQUFHO1FBQ2QsSUFBSSxFQUFFLElBQUk7UUFDVixtQkFBbUIsRUFBRSxJQUFJO1FBQ3pCLElBQUksRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxFQUFFLEtBQUssQ0FBQyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7S0FDNUUsQ0FBQztJQUNGLE1BQU0sQ0FBQyxJQUFJLENBQUMsK0JBQStCLENBQUMsQ0FBQztJQUM3QyxNQUFNLEdBQUcsR0FBRyxTQUFTLENBQUMsTUFBTSxFQUFFLENBQUMsSUFBSSxFQUFFLDBDQUEwQyxDQUFDLEVBQUU7UUFDaEYsR0FBRztRQUNILEtBQUssRUFBRSxTQUFTO0tBQ2pCLENBQUMsQ0FBQztJQUNILElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO0lBQzlELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQztBQUVELDZEQUE2RDtBQUM3RCxNQUFNLFVBQVUsZ0JBQWdCLENBQUMsTUFBbUIsRUFBRSxHQUFzQjtJQUMxRSxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUssRUFBRSxRQUFRLEVBQUUsYUFBYSxDQUFDLENBQUM7SUFDdEQsYUFBYSxDQUNYLElBQUksRUFDSjtRQUNFLGVBQWUsTUFBTSxDQUFDLE1BQU0sRUFBRTtRQUM5QixZQUFZLE1BQU0sQ0FBQyxNQUFNLE9BQU87UUFDaEMsZUFBZSxNQUFNLENBQUMsS0FBSyxFQUFFO1FBQzdCLHlCQUF5QjtRQUN6QixtQkFBbUI7UUFDbkIsVUFBVTtRQUNWLGFBQWEsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUMvQixrQkFBa0IsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUNwQyxhQUFhLE1BQU0sQ0FBQyxJQUFJLEVBQUU7UUFDMUIsRUFBRTtLQUNILENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUNiLENBQUM7QUFDSixDQUFDO0FBRUQsOEVBQThFO0FBQzlFLE1BQU0sVUFBVSxVQUFVLENBQUMsTUFBbUIsRUFBRSxHQUFzQjtJQUNwRSxrRkFBa0Y7SUFDbEYsU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsRUFBRSxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDO0lBQ3pELE1BQU0sQ0FBQyxJQUFJLENBQUMsMkJBQTJCLE1BQU0sQ0FBQyxTQUFTLElBQUksTUFBTSxDQUFDLE1BQU0sUUFBUSxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUMvRixPQUFPLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQztBQUM5RCxDQUFDO0FBRUQsOEVBQThFO0FBQzlFLE1BQU0sVUFBVSxjQUFjLENBQUMsR0FBc0I7SUFDbkQsT0FBTyxVQUFVLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFLLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0FBQy9ELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIHBvcnRyIGluc3RhbGwgKyBjb25maWcgKyBsYXVuY2ggZm9yIHRoZSB0dW5uZWwgQ0xJLlxuICpcbiAqIFNvbWUgaG9zdHMgKERhdGFicmlja3MgQXBwcyBhbW9uZyB0aGVtKSBtb3VudCB0aGUgY29udGFpbmVyJ3MgYCRIT01FYCByZWFkLW9ubHlcbiAqIG9uIGNvbGQgc3RhcnQsIHNvIHRoZSBwb3J0ciBiaW5hcnkgYW5kIGl0cyBjb25maWcgYXJlIHBsYWNlZCB1bmRlciBhIHdyaXRhYmxlLFxuICogY3dkLXJvb3RlZCBgLmhvbWVgIHVuY29uZGl0aW9uYWxseSAtIGl0IGNvc3RzIG5vdGhpbmcgd2hlcmUgYCRIT01FYCBpcyB3cml0YWJsZS5cbiAqIFRoZSBpbnN0YWxsIGlzIGlkZW1wb3RlbnQgKHRoZSBpbnN0YWxsZXIgc2tpcHMgd2hlbiB0aGUgb24tUEFUSCBiaW5hcnkgaXNcbiAqIGN1cnJlbnQpLiBUaGUgY29uZmlnIGlzIHJlbmRlcmVkIGZyb20gYFRVTk5FTF9QVUJMSUNfRE9NQUlOYCAoYDxzdWJkb21haW4+LjxzZXJ2ZXI+YClcbiAqICsgYFBPUlRSX1RPS0VOYCBhbmQgcG9pbnRzIHBvcnRyIGF0IHRoZSBQVUJMSUMgcG9ydCAodGhlIHByb3h5IGxpc3RlbnMgdGhlcmUpLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBzcGF3biwgc3Bhd25TeW5jIH0gZnJvbSBcIm5vZGU6Y2hpbGRfcHJvY2Vzc1wiO1xuaW1wb3J0IHsgZXhpc3RzU3luYywgbWtkaXJTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSBcIm5vZGU6ZnNcIjtcbmltcG9ydCB7IGRlbGltaXRlciwgam9pbiB9IGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB7IGVudiwgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IFBVQkxJQ19ET01BSU5fRU5WIH0gZnJvbSBcIi4vZW52LnRzXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJ0dW5uZWw6cG9ydHJcIik7XG5cbi8qKiBSZXNvbHZlZCBwb3J0ciB3aXJpbmcsIG9yIGB1bmRlZmluZWRgIHdoZW4gbm8gdHVubmVsIGlzIGNvbmZpZ3VyZWQuICovXG5leHBvcnQgaW50ZXJmYWNlIFBvcnRyQ29uZmlnIHtcbiAgc3ViZG9tYWluOiBzdHJpbmc7XG4gIHNlcnZlcjogc3RyaW5nO1xuICB0b2tlbjogc3RyaW5nO1xuICBwb3J0OiBudW1iZXI7XG59XG5cbi8qKlxuICogUmVzb2x2ZSBwb3J0ciBjb25maWcgZnJvbSBgVFVOTkVMX1BVQkxJQ19ET01BSU5gICsgYFBPUlRSX1RPS0VOYCwgb3IgYW5cbiAqIGV4cGxpY2l0IGBzdWJkb21haW5gLiBUaGUgZG9tYWluIGlzIGA8c3ViZG9tYWluPi48c2VydmVyPmAgKGUuZy5cbiAqIGBkZW1vLmFwcHMuZGJ4LnRvb2xzYCkuIFJldHVybnMgYHVuZGVmaW5lZGAgKG5vIHR1bm5lbCkgd2hlbiB0aGUgdG9rZW4gb3IgYVxuICogdXNhYmxlIGRvbWFpbiBpcyBhYnNlbnQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlUG9ydHJDb25maWcob3B0czoge1xuICBwdWJsaWNEb21haW4/OiBzdHJpbmc7XG4gIHN1YmRvbWFpbj86IHN0cmluZztcbiAgdG9rZW4/OiBzdHJpbmc7XG4gIHBvcnQ6IG51bWJlcjtcbn0pOiBQb3J0ckNvbmZpZyB8IHVuZGVmaW5lZCB7XG4gIC8vIFBPUlRSXyogaXMgdXBzdHJlYW0gcG9ydHIncyBvd24gbmFtZXNwYWNlLCBzbyBpdCBrZWVwcyBpdHMgbmFtZS5cbiAgY29uc3QgdG9rZW4gPSBlbnYuc3RyaW5nKG9wdHMudG9rZW4sIFwiUE9SVFJfVE9LRU5cIik7XG4gIGNvbnN0IGRvbWFpbiA9IGVudi5zdHJpbmcob3B0cy5wdWJsaWNEb21haW4sIFBVQkxJQ19ET01BSU5fRU5WKTtcbiAgaWYgKCF0b2tlbikgcmV0dXJuIHVuZGVmaW5lZDtcbiAgbGV0IHN1YmRvbWFpbiA9IG9wdHMuc3ViZG9tYWluO1xuICBsZXQgc2VydmVyOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGlmIChkb21haW4pIHtcbiAgICBzdWJkb21haW4gPz89IGRvbWFpbi5zcGxpdChcIi5cIilbMF07XG4gICAgc2VydmVyID0gZG9tYWluLnNsaWNlKGRvbWFpbi5pbmRleE9mKFwiLlwiKSArIDEpO1xuICB9XG4gIHNlcnZlciA/Pz0gZW52LnRleHQoXCJQT1JUUl9TRVJWRVJcIikgPz8gdW5kZWZpbmVkO1xuICBpZiAoIXN1YmRvbWFpbiB8fCAhc2VydmVyIHx8IHNlcnZlciA9PT0gZG9tYWluKSByZXR1cm4gdW5kZWZpbmVkO1xuICByZXR1cm4geyBzdWJkb21haW4sIHNlcnZlciwgdG9rZW4sIHBvcnQ6IG9wdHMucG9ydCB9O1xufVxuXG4vKiogVGhlIHdyaXRhYmxlIGhvbWUgcG9ydHIgaW5zdGFsbHMgKyBjb25maWd1cmVzIHVuZGVyIChBcHBzIGAkSE9NRWAgaXMgcmVhZC1vbmx5KS4gKi9cbmZ1bmN0aW9uIHBvcnRySG9tZSgpOiBzdHJpbmcge1xuICBjb25zdCBob21lID0gam9pbihwcm9jZXNzLmN3ZCgpLCBcIi5ob21lXCIpO1xuICBta2RpclN5bmMoam9pbihob21lLCBcIi5wb3J0clwiLCBcImJpblwiKSwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gIHJldHVybiBob21lO1xufVxuXG4vKiogSW5zdGFsbCBwb3J0ciAoaWRlbXBvdGVudCkgaW50byB0aGUgY3dkLXJvb3RlZCBob21lIGFuZCByZXR1cm4gdGhlIGNoaWxkIGVudi4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbnN0YWxsUG9ydHIoKTogTm9kZUpTLlByb2Nlc3NFbnYge1xuICBjb25zdCBob21lID0gcG9ydHJIb21lKCk7XG4gIGNvbnN0IGVudjogTm9kZUpTLlByb2Nlc3NFbnYgPSB7XG4gICAgLi4ucHJvY2Vzcy5lbnYsXG4gICAgSE9NRTogaG9tZSxcbiAgICBQT1JUUl9BVVRPX0FERF9QQVRIOiBcIm5vXCIsXG4gICAgUEFUSDogW2pvaW4oaG9tZSwgXCIucG9ydHJcIiwgXCJiaW5cIiksIHByb2Nlc3MuZW52LlBBVEggPz8gXCJcIl0uam9pbihkZWxpbWl0ZXIpLFxuICB9O1xuICBsb2dnZXIuaW5mbyhcImluc3RhbGxpbmcgcG9ydHIgKGlkZW1wb3RlbnQpXCIpO1xuICBjb25zdCByZXMgPSBzcGF3blN5bmMoXCJiYXNoXCIsIFtcIi1jXCIsIFwiY3VybCAtc1NmIGh0dHBzOi8vaW5zdGFsbC5wb3J0ci5kZXYgfCBzaFwiXSwge1xuICAgIGVudixcbiAgICBzdGRpbzogXCJpbmhlcml0XCIsXG4gIH0pO1xuICBpZiAocmVzLnN0YXR1cyAhPT0gMCkgdGhyb3cgbmV3IEVycm9yKFwicG9ydHIgaW5zdGFsbCBmYWlsZWRcIik7XG4gIHJldHVybiBlbnY7XG59XG5cbi8qKiBSZW5kZXIgYH4vLnBvcnRyL2NvbmZpZy55YW1sYCBmb3IgdGhlIHJlc29sdmVkIHR1bm5lbC4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3cml0ZVBvcnRyQ29uZmlnKGNvbmZpZzogUG9ydHJDb25maWcsIGVudjogTm9kZUpTLlByb2Nlc3NFbnYpOiB2b2lkIHtcbiAgY29uc3QgcGF0aCA9IGpvaW4oZW52LkhPTUUhLCBcIi5wb3J0clwiLCBcImNvbmZpZy55YW1sXCIpO1xuICB3cml0ZUZpbGVTeW5jKFxuICAgIHBhdGgsXG4gICAgW1xuICAgICAgYHNlcnZlcl91cmw6ICR7Y29uZmlnLnNlcnZlcn1gLFxuICAgICAgYHNzaF91cmw6ICR7Y29uZmlnLnNlcnZlcn06NDQ0NGAsXG4gICAgICBgc2VjcmV0X2tleTogJHtjb25maWcudG9rZW59YCxcbiAgICAgIFwiZGlzYWJsZV9kYXNoYm9hcmQ6IHRydWVcIixcbiAgICAgIFwiZGlzYWJsZV90dWk6IHRydWVcIixcbiAgICAgIFwidHVubmVsczpcIixcbiAgICAgIGAgIC0gbmFtZTogJHtjb25maWcuc3ViZG9tYWlufWAsXG4gICAgICBgICAgIHN1YmRvbWFpbjogJHtjb25maWcuc3ViZG9tYWlufWAsXG4gICAgICBgICAgIHBvcnQ6ICR7Y29uZmlnLnBvcnR9YCxcbiAgICAgIFwiXCIsXG4gICAgXS5qb2luKFwiXFxuXCIpLFxuICApO1xufVxuXG4vKiogTGF1bmNoIGBwb3J0ciBzdGFydGAgYXMgYSBjaGlsZCBwcm9jZXNzIChjYWxsZXIgc3VwZXJ2aXNlcyArIGtpbGxzIGl0KS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzdGFydFBvcnRyKGNvbmZpZzogUG9ydHJDb25maWcsIGVudjogTm9kZUpTLlByb2Nlc3NFbnYpOiBSZXR1cm5UeXBlPHR5cGVvZiBzcGF3bj4ge1xuICAvLyBSZWNsYWltIHRoZSBzdWJkb21haW4gZnJvbSBhbnkgcG9ydHIgbGVmdCBieSBhIHByZXZpb3VzIGJvb3QgaW4gdGhpcyBjb250YWluZXIuXG4gIHNwYXduU3luYyhcInBraWxsXCIsIFtcIi14XCIsIFwicG9ydHJcIl0sIHsgc3RkaW86IFwiaWdub3JlXCIgfSk7XG4gIGxvZ2dlci5pbmZvKGBwb3J0ciB0dW5uZWxpbmcgaHR0cHM6Ly8ke2NvbmZpZy5zdWJkb21haW59LiR7Y29uZmlnLnNlcnZlcn0gLT4gOiR7Y29uZmlnLnBvcnR9YCk7XG4gIHJldHVybiBzcGF3bihcInBvcnRyXCIsIFtcInN0YXJ0XCJdLCB7IGVudiwgc3RkaW86IFwiaW5oZXJpdFwiIH0pO1xufVxuXG4vKiogVHJ1ZSB3aGVuIHRoZSBpbnN0YWxsZWQgcG9ydHIgYmluYXJ5IHBhdGggZXhpc3RzIChwb3N0LWluc3RhbGwgc2FuaXR5KS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwb3J0ckluc3RhbGxlZChlbnY6IE5vZGVKUy5Qcm9jZXNzRW52KTogYm9vbGVhbiB7XG4gIHJldHVybiBleGlzdHNTeW5jKGpvaW4oZW52LkhPTUUhLCBcIi5wb3J0clwiLCBcImJpblwiLCBcInBvcnRyXCIpKTtcbn1cbiJdfQ==
|
|
116
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9ydHIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcG9ydHIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7R0FVRztBQUNILE9BQU8sRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDckQsT0FBTyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNwRCxPQUFPLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDekIsT0FBTyxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDNUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUV0QyxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDdEMsT0FBTyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFFN0MsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsQ0FBQztBQUMxQyxNQUFNLHdCQUF3QixHQUFHLDhEQUE4RCxDQUFDO0FBQ2hHLE1BQU0sYUFBYSxHQUFHLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQztBQXFCMUM7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsa0JBQWtCLENBQUMsSUFLbEM7SUFDQyxtRUFBbUU7SUFDbkUsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLGFBQWEsQ0FBQyxDQUFDO0lBQ3BELE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO0lBQ2hFLElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFDN0IsSUFBSSxTQUFTLEdB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O1FBQzdCLHlCQUF5QjtRQUN6QixtQkFBbUI7UUFDbkIsVUFBVTtRQUNWLGFBQWEsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUMvQixrQkFBa0IsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUNwQyxhQUFhLE1BQU0sQ0FBQyxJQUFJLEVBQUU7UUFDMUIsRUFBRTtLQUNILENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUNiLENBQUM7QUFDSixDQUFDO0FBRUQsOEVBQThFO0FBQzlFLE1BQU0sQ0FBQyxLQUFLLFVBQVUsVUFBVSxDQUM5QixNQUFtQixFQUNuQixRQUEyQjtJQUUzQixrRkFBa0Y7SUFDbEYsTUFBTSxhQUFhLENBQUMsT0FBTyxFQUFFLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBRXJFLE1BQU0sQ0FBQyxJQUFJLENBQUMsMkJBQTJCLE1BQU0sQ0FBQyxTQUFTLElBQUksTUFBTSxDQUFDLE1BQU0sUUFBUSxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUMvRixPQUFPLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7QUFDeEUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogcG9ydHIgaW5zdGFsbCwgY29uZmlnLCBhbmQgbGF1bmNoIHN1cHBvcnQgZm9yIHRoZSB0dW5uZWwgcnVudGltZS5cbiAqXG4gKiBUaGUgYmluYXJ5IGFuZCBjb25maWcgdXNlIHBvcnRyJ3MgY29udmVudGlvbmFsIGAkSE9NRS8ucG9ydHJgIHRyZWUuIFRoZVxuICogaW5zdGFsbCBpcyBpZGVtcG90ZW50ICh0aGUgaW5zdGFsbGVyIHNraXBzIGFuIGV4aXN0aW5nIGV4ZWN1dGFibGUpLiBUaGVcbiAqIGNvbmZpZyBpcyByZW5kZXJlZCBmcm9tIGBUVU5ORUxfUFVCTElDX0RPTUFJTmBcbiAqIChgPHN1YmRvbWFpbj4uPHNlcnZlcj5gKSArIGBQT1JUUl9UT0tFTmAgYW5kIHBvaW50cyBwb3J0ciBhdCB0aGUgUFVCTElDIHBvcnRcbiAqICh0aGUgcHJveHkgbGlzdGVucyB0aGVyZSkuXG4gKlxuICogQG1vZHVsZVxuICovXG5pbXBvcnQgeyBleGVjRmlsZSwgc3Bhd24gfSBmcm9tIFwibm9kZTpjaGlsZF9wcm9jZXNzXCI7XG5pbXBvcnQgeyBjaG1vZCwgd3JpdGVGaWxlIH0gZnJvbSBcIm5vZGU6ZnMvcHJvbWlzZXNcIjtcbmltcG9ydCBvcyBmcm9tIFwibm9kZTpvc1wiO1xuaW1wb3J0IHsgZGVsaW1pdGVyLCBqb2luIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuaW1wb3J0IHsgcHJvbWlzaWZ5IH0gZnJvbSBcIm5vZGU6dXRpbFwiO1xuXG5pbXBvcnQgeyBiaW4gfSBmcm9tIFwiQGRieC10b29scy9jb3JlXCI7XG5pbXBvcnQgeyBlbnYsIGxvZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyBQVUJMSUNfRE9NQUlOX0VOViB9IGZyb20gXCIuL2Vudi50c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwidHVubmVsOnBvcnRyXCIpO1xuY29uc3QgUE9SVFJfTEFURVNUX1JFTEVBU0VfVVJMID0gXCJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2FtYWxzaGFqaS9wb3J0ci9yZWxlYXNlcy9sYXRlc3RcIjtcbmNvbnN0IGV4ZWNGaWxlQXN5bmMgPSBwcm9taXNpZnkoZXhlY0ZpbGUpO1xuXG5pbnRlcmZhY2UgUG9ydHJSZWxlYXNlIHtcbiAgdGFnX25hbWU6IHN0cmluZztcbiAgYXNzZXRzOiBBcnJheTx7IG5hbWU6IHN0cmluZzsgYnJvd3Nlcl9kb3dubG9hZF91cmw6IHN0cmluZyB9Pjtcbn1cblxuLyoqIE9wdGlvbnMgZm9yIGluc3RhbGxpbmcgdGhlIHBvcnRyIGV4ZWN1dGFibGUuICovXG5leHBvcnQgaW50ZXJmYWNlIFBvcnRySW5zdGFsbE9wdGlvbnMge1xuICAvKiogSG9tZSBkaXJlY3RvcnkgY29udGFpbmluZyBgLnBvcnRyYDsgZGVmYXVsdHMgdG8gdGhlIE9TIGhvbWUgZGlyZWN0b3J5LiAqL1xuICBob21lRGlyPzogc3RyaW5nO1xufVxuXG4vKiogUmVzb2x2ZWQgcG9ydHIgd2lyaW5nLCBvciBgdW5kZWZpbmVkYCB3aGVuIG5vIHR1bm5lbCBpcyBjb25maWd1cmVkLiAqL1xuZXhwb3J0IGludGVyZmFjZSBQb3J0ckNvbmZpZyB7XG4gIHN1YmRvbWFpbjogc3RyaW5nO1xuICBzZXJ2ZXI6IHN0cmluZztcbiAgdG9rZW46IHN0cmluZztcbiAgcG9ydDogbnVtYmVyO1xufVxuXG4vKipcbiAqIFJlc29sdmUgcG9ydHIgY29uZmlnIGZyb20gYFRVTk5FTF9QVUJMSUNfRE9NQUlOYCArIGBQT1JUUl9UT0tFTmAsIG9yIGFuXG4gKiBleHBsaWNpdCBgc3ViZG9tYWluYC4gVGhlIGRvbWFpbiBpcyBgPHN1YmRvbWFpbj4uPHNlcnZlcj5gIChlLmcuXG4gKiBgZGVtby5hcHBzLmRieC50b29sc2ApLiBSZXR1cm5zIGB1bmRlZmluZWRgIChubyB0dW5uZWwpIHdoZW4gdGhlIHRva2VuIG9yIGFcbiAqIHVzYWJsZSBkb21haW4gaXMgYWJzZW50LlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZVBvcnRyQ29uZmlnKG9wdHM6IHtcbiAgcHVibGljRG9tYWluPzogc3RyaW5nO1xuICBzdWJkb21haW4/OiBzdHJpbmc7XG4gIHRva2VuPzogc3RyaW5nO1xuICBwb3J0OiBudW1iZXI7XG59KTogUG9ydHJDb25maWcgfCB1bmRlZmluZWQge1xuICAvLyBQT1JUUl8qIGlzIHVwc3RyZWFtIHBvcnRyJ3Mgb3duIG5hbWVzcGFjZSwgc28gaXQga2VlcHMgaXRzIG5hbWUuXG4gIGNvbnN0IHRva2VuID0gZW52LnN0cmluZyhvcHRzLnRva2VuLCBcIlBPUlRSX1RPS0VOXCIpO1xuICBjb25zdCBkb21haW4gPSBlbnYuc3RyaW5nKG9wdHMucHVibGljRG9tYWluLCBQVUJMSUNfRE9NQUlOX0VOVik7XG4gIGlmICghdG9rZW4pIHJldHVybiB1bmRlZmluZWQ7XG4gIGxldCBzdWJkb21haW4gPSBvcHRzLnN1YmRvbWFpbjtcbiAgbGV0IHNlcnZlcjogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBpZiAoZG9tYWluKSB7XG4gICAgc3ViZG9tYWluID8/PSBkb21haW4uc3BsaXQoXCIuXCIpWzBdO1xuICAgIHNlcnZlciA9IGRvbWFpbi5zbGljZShkb21haW4uaW5kZXhPZihcIi5cIikgKyAxKTtcbiAgfVxuICBzZXJ2ZXIgPz89IGVudi50ZXh0KFwiUE9SVFJfU0VSVkVSXCIpID8/IHVuZGVmaW5lZDtcbiAgaWYgKCFzdWJkb21haW4gfHwgIXNlcnZlciB8fCBzZXJ2ZXIgPT09IGRvbWFpbikgcmV0dXJuIHVuZGVmaW5lZDtcbiAgcmV0dXJuIHsgc3ViZG9tYWluLCBzZXJ2ZXIsIHRva2VuLCBwb3J0OiBvcHRzLnBvcnQgfTtcbn1cblxuLyoqIEdpdEh1YiByZWxlYXNlIGFzc2V0IG5hbWUgZm9yIHRoZSBjdXJyZW50IG9yIHN1cHBsaWVkIE9TL2FyY2hpdGVjdHVyZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwb3J0ckFzc2V0TmFtZShcbiAgdmVyc2lvbjogc3RyaW5nLFxuICBwbGF0Zm9ybTogTm9kZUpTLlBsYXRmb3JtID0gcHJvY2Vzcy5wbGF0Zm9ybSxcbiAgYXJjaDogc3RyaW5nID0gcHJvY2Vzcy5hcmNoLFxuKTogc3RyaW5nIHtcbiAgY29uc3Qgb3NOYW1lID0gcGxhdGZvcm0gPT09IFwiZGFyd2luXCIgPyBcIkRhcndpblwiIDogcGxhdGZvcm0gPT09IFwibGludXhcIiA/IFwiTGludXhcIiA6IHVuZGVmaW5lZDtcbiAgY29uc3QgYXJjaE5hbWUgPSBhcmNoID09PSBcImFybTY0XCIgPyBcImFybTY0XCIgOiBhcmNoID09PSBcIng2NFwiID8gXCJ4ODZfNjRcIiA6IHVuZGVmaW5lZDtcbiAgaWYgKCFvc05hbWUgfHwgIWFyY2hOYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBwb3J0ciBoYXMgbm8gc3VwcG9ydGVkIHJlbGVhc2UgYXNzZXQgZm9yICR7cGxhdGZvcm19LyR7YXJjaH1gKTtcbiAgfVxuICByZXR1cm4gYHBvcnRyXyR7dmVyc2lvbn1fJHtvc05hbWV9XyR7YXJjaE5hbWV9LnppcGA7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHBvcnRyRG93bmxvYWRVcmwoKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3QgcmVsZWFzZVJlc3BvbnNlID0gYXdhaXQgZmV0Y2goUE9SVFJfTEFURVNUX1JFTEVBU0VfVVJMLCB7XG4gICAgaGVhZGVyczoge1xuICAgICAgYWNjZXB0OiBcImFwcGxpY2F0aW9uL3ZuZC5naXRodWIranNvblwiLFxuICAgICAgXCJ1c2VyLWFnZW50XCI6IFwiQGRieC10b29scy90dW5uZWxcIixcbiAgICB9LFxuICB9KTtcbiAgaWYgKCFyZWxlYXNlUmVzcG9uc2Uub2spIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYHBvcnRyIHJlbGVhc2UgbG9va3VwIGZhaWxlZCAoJHtyZWxlYXNlUmVzcG9uc2Uuc3RhdHVzfSlgKTtcbiAgfVxuICBjb25zdCByZWxlYXNlID0gKGF3YWl0IHJlbGVhc2VSZXNwb25zZS5qc29uKCkpIGFzIFBvcnRyUmVsZWFzZTtcbiAgY29uc3QgdmVyc2lvbiA9IHJlbGVhc2UudGFnX25hbWUucmVwbGFjZSgvXnYvLCBcIlwiKTtcblxuICBjb25zdCBhc3NldE5hbWUgPSBwb3J0ckFzc2V0TmFtZSh2ZXJzaW9uKTtcbiAgY29uc3QgYXNzZXQgPSByZWxlYXNlLmFzc2V0cy5maW5kKChjYW5kaWRhdGUpID0+IGNhbmRpZGF0ZS5uYW1lID09PSBhc3NldE5hbWUpO1xuICBpZiAoIWFzc2V0KSB0aHJvdyBuZXcgRXJyb3IoYHBvcnRyIHJlbGVhc2UgJHtyZWxlYXNlLnRhZ19uYW1lfSBoYXMgbm8gJHthc3NldE5hbWV9YCk7XG5cbiAgbG9nZ2VyLmluZm8oXCJpbnN0YWxsaW5nIHBvcnRyXCIsIHsgdmVyc2lvbiwgYXNzZXQ6IGFzc2V0TmFtZSB9KTtcbiAgcmV0dXJuIGFzc2V0LmJyb3dzZXJfZG93bmxvYWRfdXJsO1xufVxuXG4vKiogSW5zdGFsbCBwb3J0ciB3aGVuIGFic2VudCBhbmQgcmV0dXJuIHRoZSBlbnZpcm9ubWVudCB1c2VkIGJ5IGl0cyBwcm9jZXNzLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluc3RhbGxQb3J0cihvcHRpb25zOiBQb3J0ckluc3RhbGxPcHRpb25zID0ge30pOiBQcm9taXNlPE5vZGVKUy5Qcm9jZXNzRW52PiB7XG4gIGNvbnN0IGhvbWVEaXIgPSBvcHRpb25zLmhvbWVEaXIgPz8gb3MuaG9tZWRpcigpO1xuICBjb25zdCBjb250ZXh0ID0gYXdhaXQgYmluLmVuc3VyZShcInBvcnRyXCIsIHBvcnRyRG93bmxvYWRVcmwsIHtcbiAgICBhdXRvVW5wYWNrYWdlOiB0cnVlLFxuICAgIGhvbWVEaXIsXG4gICAgc2VsZWN0b3I6IGFzeW5jICh7IHNvdXJjZSB9KSA9PiB7XG4gICAgICBjb25zdCBzZWxlY3RlZCA9IGpvaW4oc291cmNlLCBcInBvcnRyXCIpO1xuICAgICAgYXdhaXQgY2htb2Qoc2VsZWN0ZWQsIDBvNzU1KTtcbiAgICAgIHJldHVybiBzZWxlY3RlZDtcbiAgICB9LFxuICB9KTtcbiAgcmV0dXJuIHtcbiAgICAuLi5wcm9jZXNzLmVudixcbiAgICBIT01FOiBob21lRGlyLFxuICAgIFBPUlRSX0FVVE9fQUREX1BBVEg6IFwibm9cIixcbiAgICBQQVRIOiBbY29udGV4dC5iaW5EaXIsIHByb2Nlc3MuZW52LlBBVEggPz8gXCJcIl0uam9pbihkZWxpbWl0ZXIpLFxuICB9O1xufVxuXG4vKiogUmVuZGVyIGB+Ly5wb3J0ci9jb25maWcueWFtbGAgZm9yIHRoZSByZXNvbHZlZCB0dW5uZWwuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gd3JpdGVQb3J0ckNvbmZpZyhcbiAgY29uZmlnOiBQb3J0ckNvbmZpZyxcbiAgY2hpbGRFbnY6IE5vZGVKUy5Qcm9jZXNzRW52LFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHBhdGggPSBqb2luKGNoaWxkRW52LkhPTUUgPz8gb3MuaG9tZWRpcigpLCBcIi5wb3J0clwiLCBcImNvbmZpZy55YW1sXCIpO1xuICBhd2FpdCB3cml0ZUZpbGUoXG4gICAgcGF0aCxcbiAgICBbXG4gICAgICBgc2VydmVyX3VybDogJHtjb25maWcuc2VydmVyfWAsXG4gICAgICBgc3NoX3VybDogJHtjb25maWcuc2VydmVyfTo0NDQ0YCxcbiAgICAgIGBzZWNyZXRfa2V5OiAke2NvbmZpZy50b2tlbn1gLFxuICAgICAgXCJkaXNhYmxlX2Rhc2hib2FyZDogdHJ1ZVwiLFxuICAgICAgXCJkaXNhYmxlX3R1aTogdHJ1ZVwiLFxuICAgICAgXCJ0dW5uZWxzOlwiLFxuICAgICAgYCAgLSBuYW1lOiAke2NvbmZpZy5zdWJkb21haW59YCxcbiAgICAgIGAgICAgc3ViZG9tYWluOiAke2NvbmZpZy5zdWJkb21haW59YCxcbiAgICAgIGAgICAgcG9ydDogJHtjb25maWcucG9ydH1gLFxuICAgICAgXCJcIixcbiAgICBdLmpvaW4oXCJcXG5cIiksXG4gICk7XG59XG5cbi8qKiBMYXVuY2ggYHBvcnRyIHN0YXJ0YCBhcyBhIGNoaWxkIHByb2Nlc3MgKGNhbGxlciBzdXBlcnZpc2VzICsga2lsbHMgaXQpLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0UG9ydHIoXG4gIGNvbmZpZzogUG9ydHJDb25maWcsXG4gIGNoaWxkRW52OiBOb2RlSlMuUHJvY2Vzc0Vudixcbik6IFByb21pc2U8UmV0dXJuVHlwZTx0eXBlb2Ygc3Bhd24+PiB7XG4gIC8vIFJlY2xhaW0gdGhlIHN1YmRvbWFpbiBmcm9tIGFueSBwb3J0ciBsZWZ0IGJ5IGEgcHJldmlvdXMgYm9vdCBpbiB0aGlzIGNvbnRhaW5lci5cbiAgYXdhaXQgZXhlY0ZpbGVBc3luYyhcInBraWxsXCIsIFtcIi14XCIsIFwicG9ydHJcIl0pLmNhdGNoKCgpID0+IHVuZGVmaW5lZCk7XG5cbiAgbG9nZ2VyLmluZm8oYHBvcnRyIHR1bm5lbGluZyBodHRwczovLyR7Y29uZmlnLnN1YmRvbWFpbn0uJHtjb25maWcuc2VydmVyfSAtPiA6JHtjb25maWcucG9ydH1gKTtcbiAgcmV0dXJuIHNwYXduKFwicG9ydHJcIiwgW1wic3RhcnRcIl0sIHsgZW52OiBjaGlsZEVudiwgc3RkaW86IFwiaW5oZXJpdFwiIH0pO1xufVxuIl19
|
package/package.json
CHANGED
|
@@ -18,19 +18,20 @@
|
|
|
18
18
|
"projen": "projen"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@dbx-tools/email": "0.6.
|
|
21
|
+
"@dbx-tools/email": "0.6.66",
|
|
22
22
|
"@types/bun": "^1.3.14",
|
|
23
23
|
"@types/node": "^24.6.0",
|
|
24
24
|
"typescript": "^5.9.3"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"@dbx-tools/email": "0.6.
|
|
27
|
+
"@dbx-tools/email": "0.6.66"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@databricks/appkit": "^0.43.0",
|
|
31
|
-
"@dbx-tools/appkit": "0.6.
|
|
32
|
-
"@dbx-tools/
|
|
33
|
-
"@dbx-tools/shared-
|
|
31
|
+
"@dbx-tools/appkit": "0.6.66",
|
|
32
|
+
"@dbx-tools/core": "0.6.66",
|
|
33
|
+
"@dbx-tools/shared-core": "0.6.66",
|
|
34
|
+
"@dbx-tools/shared-email": "0.6.66",
|
|
34
35
|
"jose": "^6.2.3"
|
|
35
36
|
},
|
|
36
37
|
"main": "./lib/index.js",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
"./package.json": "./package.json"
|
|
48
49
|
}
|
|
49
50
|
},
|
|
50
|
-
"version": "0.6.
|
|
51
|
+
"version": "0.6.66",
|
|
51
52
|
"types": "./lib/index.d.ts",
|
|
52
53
|
"type": "module",
|
|
53
54
|
"exports": {
|
package/src/interceptor.ts
CHANGED
|
@@ -5,16 +5,10 @@
|
|
|
5
5
|
*
|
|
6
6
|
* This is the in-process replacement for the old `dbxt-tunnel -- <cmd>` wrapper.
|
|
7
7
|
* Instead of the tunnel being the main process that spawns the app as a child, the
|
|
8
|
-
* APP is the main process and hands this interceptor its context. The interceptor
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 2. installs + launches portr, pointed at the app's PUBLIC port;
|
|
13
|
-
* 3. `bindProcess`es portr so the app and the tunnel live and die as one -
|
|
14
|
-
* signals pass through and either death tears the pair down (the
|
|
15
|
-
* concurrently-style supervision that used to be `superviseExit`);
|
|
16
|
-
* 4. registers an AppKit `shutdown` lifecycle handler so an orderly app shutdown
|
|
17
|
-
* also stops portr.
|
|
8
|
+
* APP is the main process and hands this interceptor its context. The interceptor
|
|
9
|
+
* applies the computed workspace host, installs and launches portr on the app's
|
|
10
|
+
* public port, binds the child process to the app, and stops portr during an
|
|
11
|
+
* orderly AppKit shutdown.
|
|
18
12
|
*
|
|
19
13
|
* The email-OTP GATE is a separate concern: it is the `authGate` AppKit plugin,
|
|
20
14
|
* which registers the login routes + a gating middleware on the app's own server.
|
|
@@ -66,17 +60,11 @@ function resolvePublicPort(port?: number): number {
|
|
|
66
60
|
* });
|
|
67
61
|
*/
|
|
68
62
|
export function tunnelInterceptor(options: TunnelInterceptorOptions = {}): Interceptor {
|
|
69
|
-
return (ctx: InterceptorContext): void => {
|
|
70
|
-
// 1. Apply the computed workspace host so portr + the SDK agree. The context
|
|
71
|
-
// already resolved it (from the env / auto-config); set it only when it is
|
|
72
|
-
// known and not already present, so an explicit env wins.
|
|
63
|
+
return async (ctx: InterceptorContext): Promise<void> => {
|
|
73
64
|
if (ctx.env.databricksHost) {
|
|
74
65
|
process.env.DATABRICKS_HOST ??= ctx.env.databricksHost;
|
|
75
66
|
}
|
|
76
67
|
|
|
77
|
-
// 2. Resolve portr wiring. No token / domain -> no tunnel; leave the app alone.
|
|
78
|
-
// `resolvePortrConfig` already falls back to TUNNEL_PUBLIC_DOMAIN itself, so
|
|
79
|
-
// the explicit option is passed straight through.
|
|
80
68
|
const port = resolvePublicPort(options.port);
|
|
81
69
|
const portrConfig = resolvePortrConfig({
|
|
82
70
|
publicDomain: options.publicDomain,
|
|
@@ -88,14 +76,11 @@ export function tunnelInterceptor(options: TunnelInterceptorOptions = {}): Inter
|
|
|
88
76
|
return;
|
|
89
77
|
}
|
|
90
78
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const portr = startPortr(portrConfig, portrEnv);
|
|
79
|
+
const portrEnv = await installPortr();
|
|
80
|
+
await writePortrConfig(portrConfig, portrEnv);
|
|
81
|
+
const portr = await startPortr(portrConfig, portrEnv);
|
|
95
82
|
ctx.bindProcess(portr);
|
|
96
83
|
|
|
97
|
-
// 4. An orderly AppKit shutdown also stops portr (belt-and-suspenders with the
|
|
98
|
-
// signal pass-through `bindProcess` already installed).
|
|
99
84
|
ctx.onLifecycle("shutdown", () => {
|
|
100
85
|
if (!portr.killed) portr.kill("SIGTERM");
|
|
101
86
|
});
|
package/src/portr.ts
CHANGED
|
@@ -1,23 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* portr install
|
|
2
|
+
* portr install, config, and launch support for the tunnel runtime.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* + `PORTR_TOKEN` and points portr at the PUBLIC port (the proxy listens there).
|
|
4
|
+
* The binary and config use portr's conventional `$HOME/.portr` tree. The
|
|
5
|
+
* install is idempotent (the installer skips an existing executable). The
|
|
6
|
+
* config is rendered from `TUNNEL_PUBLIC_DOMAIN`
|
|
7
|
+
* (`<subdomain>.<server>`) + `PORTR_TOKEN` and points portr at the PUBLIC port
|
|
8
|
+
* (the proxy listens there).
|
|
10
9
|
*
|
|
11
10
|
* @module
|
|
12
11
|
*/
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
import
|
|
12
|
+
import { execFile, spawn } from "node:child_process";
|
|
13
|
+
import { chmod, writeFile } from "node:fs/promises";
|
|
14
|
+
import os from "node:os";
|
|
16
15
|
import { delimiter, join } from "node:path";
|
|
16
|
+
import { promisify } from "node:util";
|
|
17
|
+
|
|
18
|
+
import { bin } from "@dbx-tools/core";
|
|
17
19
|
import { env, log } from "@dbx-tools/shared-core";
|
|
18
20
|
import { PUBLIC_DOMAIN_ENV } from "./env.ts";
|
|
19
21
|
|
|
20
22
|
const logger = log.logger("tunnel:portr");
|
|
23
|
+
const PORTR_LATEST_RELEASE_URL = "https://api.github.com/repos/amalshaji/portr/releases/latest";
|
|
24
|
+
const execFileAsync = promisify(execFile);
|
|
25
|
+
|
|
26
|
+
interface PortrRelease {
|
|
27
|
+
tag_name: string;
|
|
28
|
+
assets: Array<{ name: string; browser_download_url: string }>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Options for installing the portr executable. */
|
|
32
|
+
export interface PortrInstallOptions {
|
|
33
|
+
/** Home directory containing `.portr`; defaults to the OS home directory. */
|
|
34
|
+
homeDir?: string;
|
|
35
|
+
}
|
|
21
36
|
|
|
22
37
|
/** Resolved portr wiring, or `undefined` when no tunnel is configured. */
|
|
23
38
|
export interface PortrConfig {
|
|
@@ -54,35 +69,68 @@ export function resolvePortrConfig(opts: {
|
|
|
54
69
|
return { subdomain, server, token, port: opts.port };
|
|
55
70
|
}
|
|
56
71
|
|
|
57
|
-
/**
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
/** GitHub release asset name for the current or supplied OS/architecture. */
|
|
73
|
+
export function portrAssetName(
|
|
74
|
+
version: string,
|
|
75
|
+
platform: NodeJS.Platform = process.platform,
|
|
76
|
+
arch: string = process.arch,
|
|
77
|
+
): string {
|
|
78
|
+
const osName = platform === "darwin" ? "Darwin" : platform === "linux" ? "Linux" : undefined;
|
|
79
|
+
const archName = arch === "arm64" ? "arm64" : arch === "x64" ? "x86_64" : undefined;
|
|
80
|
+
if (!osName || !archName) {
|
|
81
|
+
throw new Error(`portr has no supported release asset for ${platform}/${arch}`);
|
|
82
|
+
}
|
|
83
|
+
return `portr_${version}_${osName}_${archName}.zip`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function portrDownloadUrl(): Promise<string> {
|
|
87
|
+
const releaseResponse = await fetch(PORTR_LATEST_RELEASE_URL, {
|
|
88
|
+
headers: {
|
|
89
|
+
accept: "application/vnd.github+json",
|
|
90
|
+
"user-agent": "@dbx-tools/tunnel",
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
if (!releaseResponse.ok) {
|
|
94
|
+
throw new Error(`portr release lookup failed (${releaseResponse.status})`);
|
|
95
|
+
}
|
|
96
|
+
const release = (await releaseResponse.json()) as PortrRelease;
|
|
97
|
+
const version = release.tag_name.replace(/^v/, "");
|
|
98
|
+
|
|
99
|
+
const assetName = portrAssetName(version);
|
|
100
|
+
const asset = release.assets.find((candidate) => candidate.name === assetName);
|
|
101
|
+
if (!asset) throw new Error(`portr release ${release.tag_name} has no ${assetName}`);
|
|
102
|
+
|
|
103
|
+
logger.info("installing portr", { version, asset: assetName });
|
|
104
|
+
return asset.browser_download_url;
|
|
62
105
|
}
|
|
63
106
|
|
|
64
|
-
/** Install portr
|
|
65
|
-
export function installPortr(): NodeJS.ProcessEnv {
|
|
66
|
-
const
|
|
67
|
-
const
|
|
107
|
+
/** Install portr when absent and return the environment used by its process. */
|
|
108
|
+
export async function installPortr(options: PortrInstallOptions = {}): Promise<NodeJS.ProcessEnv> {
|
|
109
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
110
|
+
const context = await bin.ensure("portr", portrDownloadUrl, {
|
|
111
|
+
autoUnpackage: true,
|
|
112
|
+
homeDir,
|
|
113
|
+
selector: async ({ source }) => {
|
|
114
|
+
const selected = join(source, "portr");
|
|
115
|
+
await chmod(selected, 0o755);
|
|
116
|
+
return selected;
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
68
120
|
...process.env,
|
|
69
|
-
HOME:
|
|
121
|
+
HOME: homeDir,
|
|
70
122
|
PORTR_AUTO_ADD_PATH: "no",
|
|
71
|
-
PATH: [
|
|
123
|
+
PATH: [context.binDir, process.env.PATH ?? ""].join(delimiter),
|
|
72
124
|
};
|
|
73
|
-
logger.info("installing portr (idempotent)");
|
|
74
|
-
const res = spawnSync("bash", ["-c", "curl -sSf https://install.portr.dev | sh"], {
|
|
75
|
-
env,
|
|
76
|
-
stdio: "inherit",
|
|
77
|
-
});
|
|
78
|
-
if (res.status !== 0) throw new Error("portr install failed");
|
|
79
|
-
return env;
|
|
80
125
|
}
|
|
81
126
|
|
|
82
127
|
/** Render `~/.portr/config.yaml` for the resolved tunnel. */
|
|
83
|
-
export function writePortrConfig(
|
|
84
|
-
|
|
85
|
-
|
|
128
|
+
export async function writePortrConfig(
|
|
129
|
+
config: PortrConfig,
|
|
130
|
+
childEnv: NodeJS.ProcessEnv,
|
|
131
|
+
): Promise<void> {
|
|
132
|
+
const path = join(childEnv.HOME ?? os.homedir(), ".portr", "config.yaml");
|
|
133
|
+
await writeFile(
|
|
86
134
|
path,
|
|
87
135
|
[
|
|
88
136
|
`server_url: ${config.server}`,
|
|
@@ -100,14 +148,13 @@ export function writePortrConfig(config: PortrConfig, env: NodeJS.ProcessEnv): v
|
|
|
100
148
|
}
|
|
101
149
|
|
|
102
150
|
/** Launch `portr start` as a child process (caller supervises + kills it). */
|
|
103
|
-
export function startPortr(
|
|
151
|
+
export async function startPortr(
|
|
152
|
+
config: PortrConfig,
|
|
153
|
+
childEnv: NodeJS.ProcessEnv,
|
|
154
|
+
): Promise<ReturnType<typeof spawn>> {
|
|
104
155
|
// Reclaim the subdomain from any portr left by a previous boot in this container.
|
|
105
|
-
|
|
106
|
-
logger.info(`portr tunneling https://${config.subdomain}.${config.server} -> :${config.port}`);
|
|
107
|
-
return spawn("portr", ["start"], { env, stdio: "inherit" });
|
|
108
|
-
}
|
|
156
|
+
await execFileAsync("pkill", ["-x", "portr"]).catch(() => undefined);
|
|
109
157
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return existsSync(join(env.HOME!, ".portr", "bin", "portr"));
|
|
158
|
+
logger.info(`portr tunneling https://${config.subdomain}.${config.server} -> :${config.port}`);
|
|
159
|
+
return spawn("portr", ["start"], { env: childEnv, stdio: "inherit" });
|
|
113
160
|
}
|