@dbx-tools/tunnel 0.6.65 → 0.6.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssU0FBUyxNQUFNLG9CQUFvQixDQUFDO0FBQ2hELE9BQU8sS0FBSyxTQUFTLE1BQU0scUJBQXFCLENBQUM7QUFDakQsT0FBTyxLQUFLLEdBQUcsTUFBTSxjQUFjLENBQUM7QUFDcEMsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUM1QyxPQUFPLEtBQUssV0FBVyxNQUFNLHNCQUFzQixDQUFDO0FBQ3BELE9BQU8sS0FBSyxHQUFHLE1BQU0sY0FBYyxDQUFDO0FBQ3BDLE9BQU8sS0FBSyxNQUFNLE1BQU0saUJBQWlCLENBQUM7QUFDMUMsT0FBTyxLQUFLLEtBQUssTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QyxPQUFPLEtBQUssU0FBUyxNQUFNLHFCQUFxQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxRQUFRLE1BQU0sb0JBQW9CLENBQUM7QUFDL0MsT0FBTyxLQUFLLFVBQVUsTUFBTSxzQkFBc0IsQ0FBQztBQUVuRCxPQUFPLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQUUsV0FBVyxFQUFFLGVBQWUsRUFBRSxZQUFZLEVBQUUsY0FBYyxFQUFFLGtCQUFrQixFQUFFLGlCQUFpQixFQUFFLFlBQVksRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUM1TSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTVDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRzlFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFekMsT0FBTyxFQUFFLGNBQWMsRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUczRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0ICogYXMgYWxsb3dsaXN0IGZyb20gXCIuL3NyYy9hbGxvd2xpc3QudHNcIjtcbmV4cG9ydCAqIGFzIGNvZGVFbWFpbCBmcm9tIFwiLi9zcmMvY29kZS1lbWFpbC50c1wiO1xuZXhwb3J0ICogYXMgZW52IGZyb20gXCIuL3NyYy9lbnYudHNcIjtcbmV4cG9ydCAqIGFzIGdhdGUgZnJvbSBcIi4vc3JjL2dhdGUudHNcIjtcbmV4cG9ydCAqIGFzIGhlYWRlcnMgZnJvbSBcIi4vc3JjL2hlYWRlcnMudHNcIjtcbmV4cG9ydCAqIGFzIGludGVyY2VwdG9yIGZyb20gXCIuL3NyYy9pbnRlcmNlcHRvci50c1wiO1xuZXhwb3J0ICogYXMgb3RwIGZyb20gXCIuL3NyYy9vdHAudHNcIjtcbmV4cG9ydCAqIGFzIHBsdWdpbiBmcm9tIFwiLi9zcmMvcGx1Z2luLnRzXCI7XG5leHBvcnQgKiBhcyBwb3J0ciBmcm9tIFwiLi9zcmMvcG9ydHIudHNcIjtcbmV4cG9ydCAqIGFzIHJhdGVMaW1pdCBmcm9tIFwiLi9zcmMvcmF0ZS1saW1pdC50c1wiO1xuZXhwb3J0ICogYXMgc2VuZENvZGUgZnJvbSBcIi4vc3JjL3NlbmQtY29kZS50c1wiO1xuZXhwb3J0ICogYXMgc2lnbmluZ0tleSBmcm9tIFwiLi9zcmMvc2lnbmluZy1rZXkudHNcIjtcbmV4cG9ydCB0eXBlIHsgQ29kZUNvcHkgfSBmcm9tIFwiLi9zcmMvY29kZS1lbWFpbC50c1wiO1xuZXhwb3J0IHsgQUxMT1dfRU5WLCBTVUJKRUNUX0VOViwgQlJBTkRfTkFNRV9FTlYsIE1FU1NBR0VfRU5WLCBTRVNTSU9OX1RUTF9FTlYsIENPREVfVFRMX0VOViwgSldUX1NFQ1JFVF9FTlYsIFNFU1NJT05fQ1VUT0ZGX0VOViwgUFVCTElDX0RPTUFJTl9FTlYsIElOU0VDVVJFX0VOViwgRk9SV0FSRF9IRUFERVJTX0VOViB9IGZyb20gXCIuL3NyYy9lbnYudHNcIjtcbmV4cG9ydCB7IEFVVEhfUFJFRklYIH0gZnJvbSBcIi4vc3JjL2dhdGUudHNcIjtcbmV4cG9ydCB0eXBlIHsgR2F0ZU9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvZ2F0ZS50c1wiO1xuZXhwb3J0IHsgUFJPVEVDVEVEX0hFQURFUlMsIERFRkFVTFRfRk9SV0FSRF9IRUFERVJTIH0gZnJvbSBcIi4vc3JjL2hlYWRlcnMudHNcIjtcbmV4cG9ydCB0eXBlIHsgSGVhZGVyUG9saWN5IH0gZnJvbSBcIi4vc3JjL2hlYWRlcnMudHNcIjtcbmV4cG9ydCB0eXBlIHsgVHVubmVsSW50ZXJjZXB0b3JPcHRpb25zIH0gZnJvbSBcIi4vc3JjL2ludGVyY2VwdG9yLnRzXCI7XG5leHBvcnQgeyBDb2RlU3RvcmUgfSBmcm9tIFwiLi9zcmMvb3RwLnRzXCI7XG5leHBvcnQgdHlwZSB7IFZlcmlmeU91dGNvbWUgfSBmcm9tIFwiLi9zcmMvb3RwLnRzXCI7XG5leHBvcnQgeyBBdXRoR2F0ZVBsdWdpbiwgYXV0aEdhdGUgfSBmcm9tIFwiLi9zcmMvcGx1Z2luLnRzXCI7XG5leHBvcnQgdHlwZSB7IEF1dGhHYXRlQ29uZmlnLCBTZW5kQ29kZU9wdGlvbnMsIFJlc29sdmVkQXV0aEdhdGVDb25maWcsIEF1dGhHYXRlQXBpIH0gZnJvbSBcIi4vc3JjL3BsdWdpbi50c1wiO1xuZXhwb3J0IHR5cGUgeyBQb3J0ckNvbmZpZyB9IGZyb20gXCIuL3NyYy9wb3J0ci50c1wiO1xuZXhwb3J0IHsgUmF0ZUxpbWl0ZXIgfSBmcm9tIFwiLi9zcmMvcmF0ZS1saW1pdC50c1wiO1xuZXhwb3J0IHsgS0VZX1RUTF9TRUNPTkRTIH0gZnJvbSBcIi4vc3JjL3NpZ25pbmcta2V5LnRzXCI7XG5leHBvcnQgdHlwZSB7IFNpZ25pbmdLZXkgfSBmcm9tIFwiLi9zcmMvc2lnbmluZy1rZXkudHNcIjtcbiJdfQ==
23
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssU0FBUyxNQUFNLG9CQUFvQixDQUFDO0FBQ2hELE9BQU8sS0FBSyxTQUFTLE1BQU0scUJBQXFCLENBQUM7QUFDakQsT0FBTyxLQUFLLEdBQUcsTUFBTSxjQUFjLENBQUM7QUFDcEMsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUM1QyxPQUFPLEtBQUssV0FBVyxNQUFNLHNCQUFzQixDQUFDO0FBQ3BELE9BQU8sS0FBSyxHQUFHLE1BQU0sY0FBYyxDQUFDO0FBQ3BDLE9BQU8sS0FBSyxNQUFNLE1BQU0saUJBQWlCLENBQUM7QUFDMUMsT0FBTyxLQUFLLEtBQUssTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QyxPQUFPLEtBQUssU0FBUyxNQUFNLHFCQUFxQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxRQUFRLE1BQU0sb0JBQW9CLENBQUM7QUFDL0MsT0FBTyxLQUFLLFVBQVUsTUFBTSxzQkFBc0IsQ0FBQztBQUVuRCxPQUFPLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQUUsV0FBVyxFQUFFLGVBQWUsRUFBRSxZQUFZLEVBQUUsY0FBYyxFQUFFLGtCQUFrQixFQUFFLGlCQUFpQixFQUFFLFlBQVksRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUM1TSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTVDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRzlFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFekMsT0FBTyxFQUFFLGNBQWMsRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUczRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0ICogYXMgYWxsb3dsaXN0IGZyb20gXCIuL3NyYy9hbGxvd2xpc3QudHNcIjtcbmV4cG9ydCAqIGFzIGNvZGVFbWFpbCBmcm9tIFwiLi9zcmMvY29kZS1lbWFpbC50c1wiO1xuZXhwb3J0ICogYXMgZW52IGZyb20gXCIuL3NyYy9lbnYudHNcIjtcbmV4cG9ydCAqIGFzIGdhdGUgZnJvbSBcIi4vc3JjL2dhdGUudHNcIjtcbmV4cG9ydCAqIGFzIGhlYWRlcnMgZnJvbSBcIi4vc3JjL2hlYWRlcnMudHNcIjtcbmV4cG9ydCAqIGFzIGludGVyY2VwdG9yIGZyb20gXCIuL3NyYy9pbnRlcmNlcHRvci50c1wiO1xuZXhwb3J0ICogYXMgb3RwIGZyb20gXCIuL3NyYy9vdHAudHNcIjtcbmV4cG9ydCAqIGFzIHBsdWdpbiBmcm9tIFwiLi9zcmMvcGx1Z2luLnRzXCI7XG5leHBvcnQgKiBhcyBwb3J0ciBmcm9tIFwiLi9zcmMvcG9ydHIudHNcIjtcbmV4cG9ydCAqIGFzIHJhdGVMaW1pdCBmcm9tIFwiLi9zcmMvcmF0ZS1saW1pdC50c1wiO1xuZXhwb3J0ICogYXMgc2VuZENvZGUgZnJvbSBcIi4vc3JjL3NlbmQtY29kZS50c1wiO1xuZXhwb3J0ICogYXMgc2lnbmluZ0tleSBmcm9tIFwiLi9zcmMvc2lnbmluZy1rZXkudHNcIjtcbmV4cG9ydCB0eXBlIHsgQ29kZUNvcHkgfSBmcm9tIFwiLi9zcmMvY29kZS1lbWFpbC50c1wiO1xuZXhwb3J0IHsgQUxMT1dfRU5WLCBTVUJKRUNUX0VOViwgQlJBTkRfTkFNRV9FTlYsIE1FU1NBR0VfRU5WLCBTRVNTSU9OX1RUTF9FTlYsIENPREVfVFRMX0VOViwgSldUX1NFQ1JFVF9FTlYsIFNFU1NJT05fQ1VUT0ZGX0VOViwgUFVCTElDX0RPTUFJTl9FTlYsIElOU0VDVVJFX0VOViwgRk9SV0FSRF9IRUFERVJTX0VOViB9IGZyb20gXCIuL3NyYy9lbnYudHNcIjtcbmV4cG9ydCB7IEFVVEhfUFJFRklYIH0gZnJvbSBcIi4vc3JjL2dhdGUudHNcIjtcbmV4cG9ydCB0eXBlIHsgR2F0ZU9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvZ2F0ZS50c1wiO1xuZXhwb3J0IHsgUFJPVEVDVEVEX0hFQURFUlMsIERFRkFVTFRfRk9SV0FSRF9IRUFERVJTIH0gZnJvbSBcIi4vc3JjL2hlYWRlcnMudHNcIjtcbmV4cG9ydCB0eXBlIHsgSGVhZGVyUG9saWN5IH0gZnJvbSBcIi4vc3JjL2hlYWRlcnMudHNcIjtcbmV4cG9ydCB0eXBlIHsgVHVubmVsSW50ZXJjZXB0b3JPcHRpb25zIH0gZnJvbSBcIi4vc3JjL2ludGVyY2VwdG9yLnRzXCI7XG5leHBvcnQgeyBDb2RlU3RvcmUgfSBmcm9tIFwiLi9zcmMvb3RwLnRzXCI7XG5leHBvcnQgdHlwZSB7IFZlcmlmeU91dGNvbWUgfSBmcm9tIFwiLi9zcmMvb3RwLnRzXCI7XG5leHBvcnQgeyBBdXRoR2F0ZVBsdWdpbiwgYXV0aEdhdGUgfSBmcm9tIFwiLi9zcmMvcGx1Z2luLnRzXCI7XG5leHBvcnQgdHlwZSB7IEF1dGhHYXRlQ29uZmlnLCBTZW5kQ29kZU9wdGlvbnMsIFJlc29sdmVkQXV0aEdhdGVDb25maWcsIEF1dGhHYXRlQXBpIH0gZnJvbSBcIi4vc3JjL3BsdWdpbi50c1wiO1xuZXhwb3J0IHR5cGUgeyBQb3J0ckluc3RhbGxPcHRpb25zLCBQb3J0ckNvbmZpZyB9IGZyb20gXCIuL3NyYy9wb3J0ci50c1wiO1xuZXhwb3J0IHsgUmF0ZUxpbWl0ZXIgfSBmcm9tIFwiLi9zcmMvcmF0ZS1saW1pdC50c1wiO1xuZXhwb3J0IHsgS0VZX1RUTF9TRUNPTkRTIH0gZnJvbSBcIi4vc3JjL3NpZ25pbmcta2V5LnRzXCI7XG5leHBvcnQgdHlwZSB7IFNpZ25pbmdLZXkgfSBmcm9tIFwiLi9zcmMvc2lnbmluZy1rZXkudHNcIjtcbiJdfQ==
@@ -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
- * 1. applies the workspace host the context computed to `process.env`
11
- * (`DATABRICKS_HOST`), so portr and any later SDK call agree on the workspace;
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.
@@ -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
- * 1. applies the workspace host the context computed to `process.env`
11
- * (`DATABRICKS_HOST`), so portr and any later SDK call agree on the workspace;
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
- // 3. Install + launch portr, then bind it: the app and portr now share a fate.
71
- const portrEnv = installPortr();
72
- writePortrConfig(portrConfig, portrEnv);
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJjZXB0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW50ZXJjZXB0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdCRztBQUdILE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxrQkFBa0IsRUFBRSxVQUFVLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFNUYsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0FBZ0JoRCxxRkFBcUY7QUFDckYsU0FBUyxpQkFBaUIsQ0FBQyxJQUFhO0lBQ3RDLE9BQU8sSUFBSSxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLElBQUksQ0FBQztBQUMxRSxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUFDLFVBQW9DLEVBQUU7SUFDdEUsT0FBTyxDQUFDLEdBQXVCLEVBQVEsRUFBRTtRQUN2Qyw2RUFBNkU7UUFDN0UsOEVBQThFO1FBQzlFLDZEQUE2RDtRQUM3RCxJQUFJLEdBQUcsQ0FBQyxHQUFHLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDM0IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLEtBQUssR0FBRyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUM7UUFDekQsQ0FBQztRQUVELGdGQUFnRjtRQUNoRixnRkFBZ0Y7UUFDaEYscURBQXFEO1FBQ3JELE1BQU0sSUFBSSxHQUFHLGlCQUFpQixDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QyxNQUFNLFdBQVcsR0FBRyxrQkFBa0IsQ0FBQztZQUNyQyxZQUFZLEVBQUUsT0FBTyxDQUFDLFlBQVk7WUFDbEMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxTQUFTO1lBQzVCLElBQUk7U0FDTCxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDakIsTUFBTSxDQUFDLElBQUksQ0FBQyw0RUFBNEUsQ0FBQyxDQUFDO1lBQzFGLE9BQU87UUFDVCxDQUFDO1FBRUQsK0VBQStFO1FBQy9FLE1BQU0sUUFBUSxHQUFHLFlBQVksRUFBRSxDQUFDO1FBQ2hDLGdCQUFnQixDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUN4QyxNQUFNLEtBQUssR0FBRyxVQUFVLENBQUMsV0FBVyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ2hELEdBQUcsQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFdkIsK0VBQStFO1FBQy9FLDJEQUEyRDtRQUMzRCxHQUFHLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxHQUFHLEVBQUU7WUFDL0IsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNO2dCQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDM0MsQ0FBQyxDQUFDLENBQUM7UUFFSCxNQUFNLENBQUMsSUFBSSxDQUFDLDJCQUEyQixJQUFJLEtBQUssV0FBVyxDQUFDLFNBQVMsSUFBSSxXQUFXLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUNsRyxDQUFDLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBgdHVubmVsSW50ZXJjZXB0b3IoKWAgLSB0aGUge0BsaW5rIEludGVyY2VwdG9yfSB0aGF0IGZyb250cyBhbiBhcHAgd2l0aCBhIHB1YmxpY1xuICogcG9ydHIgdHVubmVsLCBjb25zdW1pbmcgdGhlIHtAbGluayBJbnRlcmNlcHRvckNvbnRleHR9IGBAZGJ4LXRvb2xzL2FwcGtpdGAnc1xuICogYGNyZWF0ZUFwcGAgaGFuZHMgaXQuXG4gKlxuICogVGhpcyBpcyB0aGUgaW4tcHJvY2VzcyByZXBsYWNlbWVudCBmb3IgdGhlIG9sZCBgZGJ4dC10dW5uZWwgLS0gPGNtZD5gIHdyYXBwZXIuXG4gKiBJbnN0ZWFkIG9mIHRoZSB0dW5uZWwgYmVpbmcgdGhlIG1haW4gcHJvY2VzcyB0aGF0IHNwYXducyB0aGUgYXBwIGFzIGEgY2hpbGQsIHRoZVxuICogQVBQIGlzIHRoZSBtYWluIHByb2Nlc3MgYW5kIGhhbmRzIHRoaXMgaW50ZXJjZXB0b3IgaXRzIGNvbnRleHQuIFRoZSBpbnRlcmNlcHRvcjpcbiAqXG4gKiAgIDEuIGFwcGxpZXMgdGhlIHdvcmtzcGFjZSBob3N0IHRoZSBjb250ZXh0IGNvbXB1dGVkIHRvIGBwcm9jZXNzLmVudmBcbiAqICAgICAgKGBEQVRBQlJJQ0tTX0hPU1RgKSwgc28gcG9ydHIgYW5kIGFueSBsYXRlciBTREsgY2FsbCBhZ3JlZSBvbiB0aGUgd29ya3NwYWNlO1xuICogICAyLiBpbnN0YWxscyArIGxhdW5jaGVzIHBvcnRyLCBwb2ludGVkIGF0IHRoZSBhcHAncyBQVUJMSUMgcG9ydDtcbiAqICAgMy4gYGJpbmRQcm9jZXNzYGVzIHBvcnRyIHNvIHRoZSBhcHAgYW5kIHRoZSB0dW5uZWwgbGl2ZSBhbmQgZGllIGFzIG9uZSAtXG4gKiAgICAgIHNpZ25hbHMgcGFzcyB0aHJvdWdoIGFuZCBlaXRoZXIgZGVhdGggdGVhcnMgdGhlIHBhaXIgZG93biAodGhlXG4gKiAgICAgIGNvbmN1cnJlbnRseS1zdHlsZSBzdXBlcnZpc2lvbiB0aGF0IHVzZWQgdG8gYmUgYHN1cGVydmlzZUV4aXRgKTtcbiAqICAgNC4gcmVnaXN0ZXJzIGFuIEFwcEtpdCBgc2h1dGRvd25gIGxpZmVjeWNsZSBoYW5kbGVyIHNvIGFuIG9yZGVybHkgYXBwIHNodXRkb3duXG4gKiAgICAgIGFsc28gc3RvcHMgcG9ydHIuXG4gKlxuICogVGhlIGVtYWlsLU9UUCBHQVRFIGlzIGEgc2VwYXJhdGUgY29uY2VybjogaXQgaXMgdGhlIGBhdXRoR2F0ZWAgQXBwS2l0IHBsdWdpbixcbiAqIHdoaWNoIHJlZ2lzdGVycyB0aGUgbG9naW4gcm91dGVzICsgYSBnYXRpbmcgbWlkZGxld2FyZSBvbiB0aGUgYXBwJ3Mgb3duIHNlcnZlci5cbiAqIFJlZ2lzdGVyIGl0IGluIHRoZSBhcHAncyBgcGx1Z2luc2AgZm9yIGdhdGVkIHRyYWZmaWMuIFRoaXMgaW50ZXJjZXB0b3IgaXMgb25seVxuICogdGhlIHBvcnRyIGhhbGYgLSBcInVwZGF0ZSB0aGUgaG9zdCwgYmluZCBwb3J0clwiIC0gdGhlIHNtYWxsZXN0IHVzZWZ1bCB1bml0LlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgdHlwZSB7IEludGVyY2VwdG9yLCBJbnRlcmNlcHRvckNvbnRleHQgfSBmcm9tIFwiQGRieC10b29scy9hcHBraXRcIjtcbmltcG9ydCB7IGxvZywgb2JqZWN0IH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IGluc3RhbGxQb3J0ciwgcmVzb2x2ZVBvcnRyQ29uZmlnLCBzdGFydFBvcnRyLCB3cml0ZVBvcnRyQ29uZmlnIH0gZnJvbSBcIi4vcG9ydHIudHNcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcInR1bm5lbDppbnRlcmNlcHRvclwiKTtcblxuLyoqIE9wdGlvbnMgZm9yIHtAbGluayB0dW5uZWxJbnRlcmNlcHRvcn0gKGVhY2ggYWxzbyByZXNvbHZhYmxlIGZyb20gZW52KS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgVHVubmVsSW50ZXJjZXB0b3JPcHRpb25zIHtcbiAgLyoqIHBvcnRyIGA8c3ViZG9tYWluPi48c2VydmVyPmAgdG8gc2VydmUgb24uIEVudiBgVFVOTkVMX1BVQkxJQ19ET01BSU5gLiAqL1xuICBwdWJsaWNEb21haW4/OiBzdHJpbmc7XG4gIC8qKiBwb3J0ciBzdWJkb21haW4gKGVsc2UgZGVyaXZlZCBmcm9tIHtAbGluayBwdWJsaWNEb21haW59KS4gKi9cbiAgc3ViZG9tYWluPzogc3RyaW5nO1xuICAvKipcbiAgICogVGhlIFBVQkxJQyBwb3J0IHBvcnRyIGZvcndhcmRzIHRvIC0gdGhlIHBvcnQgdGhlIGFwcCBpdHNlbGYgbGlzdGVucyBvbi5cbiAgICogRGVmYXVsdHMgdG8gdGhlIERhdGFicmlja3MgQXBwcyBydW50aW1lIGNvbnRyYWN0IGBEQVRBQlJJQ0tTX0FQUF9QT1JUYFxuICAgKiAodGhlbiBgODAwMGApLCB3aGljaCBpcyB0aGUgcG9ydCB0aGUgcGxhdGZvcm0gcm91dGVzIHRvLlxuICAgKi9cbiAgcG9ydD86IG51bWJlcjtcbn1cblxuLyoqIFJlc29sdmUgdGhlIHB1YmxpYyBwb3J0IHBvcnRyIHNob3VsZCB0YXJnZXQ6IGV4cGxpY2l0LCBlbHNlIHRoZSBBcHBzIGNvbnRyYWN0LiAqL1xuZnVuY3Rpb24gcmVzb2x2ZVB1YmxpY1BvcnQocG9ydD86IG51bWJlcik6IG51bWJlciB7XG4gIHJldHVybiBwb3J0ID8/IG9iamVjdC50b051bWJlcihwcm9jZXNzLmVudi5EQVRBQlJJQ0tTX0FQUF9QT1JUKSA/PyA4MDAwO1xufVxuXG4vKipcbiAqIEJ1aWxkIHRoZSB0dW5uZWwge0BsaW5rIEludGVyY2VwdG9yfS4gUGFzcyBpdCB0byBgY3JlYXRlQXBwKHsgaW50ZXJjZXB0b3IgfSlgLlxuICpcbiAqIEEgbm8tb3AgKGxvZ3MgYW5kIHJldHVybnMpIHdoZW4gbm8gcG9ydHIgdHVubmVsIGlzIGNvbmZpZ3VyZWQgLSBubyBgUE9SVFJfVE9LRU5gXG4gKiBvciBubyByZXNvbHZhYmxlIGA8c3ViZG9tYWluPi48c2VydmVyPmAgLSBzbyBhbiBhcHAgY2FuIHJlZ2lzdGVyIGl0XG4gKiB1bmNvbmRpdGlvbmFsbHkgYW5kIG9ubHkgYWN0dWFsbHkgdHVubmVscyB3aGVyZSB0aGUgZGVwbG95bWVudCB3aXJlZCBwb3J0ci5cbiAqXG4gKiBAZXhhbXBsZVxuICogaW1wb3J0IHsgY3JlYXRlQXBwIH0gZnJvbSBcIkBkYngtdG9vbHMvYXBwa2l0XCI7XG4gKiBpbXBvcnQgeyB0dW5uZWxJbnRlcmNlcHRvciB9IGZyb20gXCJAZGJ4LXRvb2xzL3R1bm5lbFwiO1xuICpcbiAqIGF3YWl0IGNyZWF0ZUFwcCh7XG4gKiAgIHBsdWdpbnM6IFtzZXJ2ZXIoeyBob3N0LCBzdGF0aWNQYXRoIH0pXSxcbiAqICAgaW50ZXJjZXB0b3I6IHR1bm5lbEludGVyY2VwdG9yKCksXG4gKiB9KTtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHR1bm5lbEludGVyY2VwdG9yKG9wdGlvbnM6IFR1bm5lbEludGVyY2VwdG9yT3B0aW9ucyA9IHt9KTogSW50ZXJjZXB0b3Ige1xuICByZXR1cm4gKGN0eDogSW50ZXJjZXB0b3JDb250ZXh0KTogdm9pZCA9PiB7XG4gICAgLy8gMS4gQXBwbHkgdGhlIGNvbXB1dGVkIHdvcmtzcGFjZSBob3N0IHNvIHBvcnRyICsgdGhlIFNESyBhZ3JlZS4gVGhlIGNvbnRleHRcbiAgICAvLyAgICBhbHJlYWR5IHJlc29sdmVkIGl0IChmcm9tIHRoZSBlbnYgLyBhdXRvLWNvbmZpZyk7IHNldCBpdCBvbmx5IHdoZW4gaXQgaXNcbiAgICAvLyAgICBrbm93biBhbmQgbm90IGFscmVhZHkgcHJlc2VudCwgc28gYW4gZXhwbGljaXQgZW52IHdpbnMuXG4gICAgaWYgKGN0eC5lbnYuZGF0YWJyaWNrc0hvc3QpIHtcbiAgICAgIHByb2Nlc3MuZW52LkRBVEFCUklDS1NfSE9TVCA/Pz0gY3R4LmVudi5kYXRhYnJpY2tzSG9zdDtcbiAgICB9XG5cbiAgICAvLyAyLiBSZXNvbHZlIHBvcnRyIHdpcmluZy4gTm8gdG9rZW4gLyBkb21haW4gLT4gbm8gdHVubmVsOyBsZWF2ZSB0aGUgYXBwIGFsb25lLlxuICAgIC8vICAgIGByZXNvbHZlUG9ydHJDb25maWdgIGFscmVhZHkgZmFsbHMgYmFjayB0byBUVU5ORUxfUFVCTElDX0RPTUFJTiBpdHNlbGYsIHNvXG4gICAgLy8gICAgdGhlIGV4cGxpY2l0IG9wdGlvbiBpcyBwYXNzZWQgc3RyYWlnaHQgdGhyb3VnaC5cbiAgICBjb25zdCBwb3J0ID0gcmVzb2x2ZVB1YmxpY1BvcnQob3B0aW9ucy5wb3J0KTtcbiAgICBjb25zdCBwb3J0ckNvbmZpZyA9IHJlc29sdmVQb3J0ckNvbmZpZyh7XG4gICAgICBwdWJsaWNEb21haW46IG9wdGlvbnMucHVibGljRG9tYWluLFxuICAgICAgc3ViZG9tYWluOiBvcHRpb25zLnN1YmRvbWFpbixcbiAgICAgIHBvcnQsXG4gICAgfSk7XG4gICAgaWYgKCFwb3J0ckNvbmZpZykge1xuICAgICAgbG9nZ2VyLmluZm8oXCJubyBQT1JUUl9UT0tFTi9UVU5ORUxfUFVCTElDX0RPTUFJTiAtIHRoZSBhcHAgcnVucyB3aXRob3V0IGEgcHVibGljIHR1bm5lbFwiKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyAzLiBJbnN0YWxsICsgbGF1bmNoIHBvcnRyLCB0aGVuIGJpbmQgaXQ6IHRoZSBhcHAgYW5kIHBvcnRyIG5vdyBzaGFyZSBhIGZhdGUuXG4gICAgY29uc3QgcG9ydHJFbnYgPSBpbnN0YWxsUG9ydHIoKTtcbiAgICB3cml0ZVBvcnRyQ29uZmlnKHBvcnRyQ29uZmlnLCBwb3J0ckVudik7XG4gICAgY29uc3QgcG9ydHIgPSBzdGFydFBvcnRyKHBvcnRyQ29uZmlnLCBwb3J0ckVudik7XG4gICAgY3R4LmJpbmRQcm9jZXNzKHBvcnRyKTtcblxuICAgIC8vIDQuIEFuIG9yZGVybHkgQXBwS2l0IHNodXRkb3duIGFsc28gc3RvcHMgcG9ydHIgKGJlbHQtYW5kLXN1c3BlbmRlcnMgd2l0aCB0aGVcbiAgICAvLyAgICBzaWduYWwgcGFzcy10aHJvdWdoIGBiaW5kUHJvY2Vzc2AgYWxyZWFkeSBpbnN0YWxsZWQpLlxuICAgIGN0eC5vbkxpZmVjeWNsZShcInNodXRkb3duXCIsICgpID0+IHtcbiAgICAgIGlmICghcG9ydHIua2lsbGVkKSBwb3J0ci5raWxsKFwiU0lHVEVSTVwiKTtcbiAgICB9KTtcblxuICAgIGxvZ2dlci5pbmZvKGB0dW5uZWwgYm91bmQ6IHBvcnRyIC0+IDoke3BvcnR9ICgke3BvcnRyQ29uZmlnLnN1YmRvbWFpbn0uJHtwb3J0ckNvbmZpZy5zZXJ2ZXJ9KWApO1xuICB9O1xufVxuIl19
69
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJjZXB0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW50ZXJjZXB0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRztBQUdILE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxrQkFBa0IsRUFBRSxVQUFVLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFNUYsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0FBZ0JoRCxxRkFBcUY7QUFDckYsU0FBUyxpQkFBaUIsQ0FBQyxJQUFhO0lBQ3RDLE9BQU8sSUFBSSxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLElBQUksQ0FBQztBQUMxRSxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUFDLFVBQW9DLEVBQUU7SUFDdEUsT0FBTyxLQUFLLEVBQUUsR0FBdUIsRUFBaUIsRUFBRTtRQUN0RCxJQUFJLEdBQUcsQ0FBQyxHQUFHLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDM0IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLEtBQUssR0FBRyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUM7UUFDekQsQ0FBQztRQUVELE1BQU0sSUFBSSxHQUFHLGlCQUFpQixDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QyxNQUFNLFdBQVcsR0FBRyxrQkFBa0IsQ0FBQztZQUNyQyxZQUFZLEVBQUUsT0FBTyxDQUFDLFlBQVk7WUFDbEMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxTQUFTO1lBQzVCLElBQUk7U0FDTCxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDakIsTUFBTSxDQUFDLElBQUksQ0FBQyw0RUFBNEUsQ0FBQyxDQUFDO1lBQzFGLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxRQUFRLEdBQUcsTUFBTSxZQUFZLEVBQUUsQ0FBQztRQUN0QyxNQUFNLGdCQUFnQixDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUM5QyxNQUFNLEtBQUssR0FBRyxNQUFNLFVBQVUsQ0FBQyxXQUFXLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDdEQsR0FBRyxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUV2QixHQUFHLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxHQUFHLEVBQUU7WUFDL0IsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNO2dCQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDM0MsQ0FBQyxDQUFDLENBQUM7UUFFSCxNQUFNLENBQUMsSUFBSSxDQUFDLDJCQUEyQixJQUFJLEtBQUssV0FBVyxDQUFDLFNBQVMsSUFBSSxXQUFXLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUNsRyxDQUFDLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBgdHVubmVsSW50ZXJjZXB0b3IoKWAgLSB0aGUge0BsaW5rIEludGVyY2VwdG9yfSB0aGF0IGZyb250cyBhbiBhcHAgd2l0aCBhIHB1YmxpY1xuICogcG9ydHIgdHVubmVsLCBjb25zdW1pbmcgdGhlIHtAbGluayBJbnRlcmNlcHRvckNvbnRleHR9IGBAZGJ4LXRvb2xzL2FwcGtpdGAnc1xuICogYGNyZWF0ZUFwcGAgaGFuZHMgaXQuXG4gKlxuICogVGhpcyBpcyB0aGUgaW4tcHJvY2VzcyByZXBsYWNlbWVudCBmb3IgdGhlIG9sZCBgZGJ4dC10dW5uZWwgLS0gPGNtZD5gIHdyYXBwZXIuXG4gKiBJbnN0ZWFkIG9mIHRoZSB0dW5uZWwgYmVpbmcgdGhlIG1haW4gcHJvY2VzcyB0aGF0IHNwYXducyB0aGUgYXBwIGFzIGEgY2hpbGQsIHRoZVxuICogQVBQIGlzIHRoZSBtYWluIHByb2Nlc3MgYW5kIGhhbmRzIHRoaXMgaW50ZXJjZXB0b3IgaXRzIGNvbnRleHQuIFRoZSBpbnRlcmNlcHRvclxuICogYXBwbGllcyB0aGUgY29tcHV0ZWQgd29ya3NwYWNlIGhvc3QsIGluc3RhbGxzIGFuZCBsYXVuY2hlcyBwb3J0ciBvbiB0aGUgYXBwJ3NcbiAqIHB1YmxpYyBwb3J0LCBiaW5kcyB0aGUgY2hpbGQgcHJvY2VzcyB0byB0aGUgYXBwLCBhbmQgc3RvcHMgcG9ydHIgZHVyaW5nIGFuXG4gKiBvcmRlcmx5IEFwcEtpdCBzaHV0ZG93bi5cbiAqXG4gKiBUaGUgZW1haWwtT1RQIEdBVEUgaXMgYSBzZXBhcmF0ZSBjb25jZXJuOiBpdCBpcyB0aGUgYGF1dGhHYXRlYCBBcHBLaXQgcGx1Z2luLFxuICogd2hpY2ggcmVnaXN0ZXJzIHRoZSBsb2dpbiByb3V0ZXMgKyBhIGdhdGluZyBtaWRkbGV3YXJlIG9uIHRoZSBhcHAncyBvd24gc2VydmVyLlxuICogUmVnaXN0ZXIgaXQgaW4gdGhlIGFwcCdzIGBwbHVnaW5zYCBmb3IgZ2F0ZWQgdHJhZmZpYy4gVGhpcyBpbnRlcmNlcHRvciBpcyBvbmx5XG4gKiB0aGUgcG9ydHIgaGFsZiAtIFwidXBkYXRlIHRoZSBob3N0LCBiaW5kIHBvcnRyXCIgLSB0aGUgc21hbGxlc3QgdXNlZnVsIHVuaXQuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB0eXBlIHsgSW50ZXJjZXB0b3IsIEludGVyY2VwdG9yQ29udGV4dCB9IGZyb20gXCJAZGJ4LXRvb2xzL2FwcGtpdFwiO1xuaW1wb3J0IHsgbG9nLCBvYmplY3QgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgaW5zdGFsbFBvcnRyLCByZXNvbHZlUG9ydHJDb25maWcsIHN0YXJ0UG9ydHIsIHdyaXRlUG9ydHJDb25maWcgfSBmcm9tIFwiLi9wb3J0ci50c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwidHVubmVsOmludGVyY2VwdG9yXCIpO1xuXG4vKiogT3B0aW9ucyBmb3Ige0BsaW5rIHR1bm5lbEludGVyY2VwdG9yfSAoZWFjaCBhbHNvIHJlc29sdmFibGUgZnJvbSBlbnYpLiAqL1xuZXhwb3J0IGludGVyZmFjZSBUdW5uZWxJbnRlcmNlcHRvck9wdGlvbnMge1xuICAvKiogcG9ydHIgYDxzdWJkb21haW4+LjxzZXJ2ZXI+YCB0byBzZXJ2ZSBvbi4gRW52IGBUVU5ORUxfUFVCTElDX0RPTUFJTmAuICovXG4gIHB1YmxpY0RvbWFpbj86IHN0cmluZztcbiAgLyoqIHBvcnRyIHN1YmRvbWFpbiAoZWxzZSBkZXJpdmVkIGZyb20ge0BsaW5rIHB1YmxpY0RvbWFpbn0pLiAqL1xuICBzdWJkb21haW4/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBUaGUgUFVCTElDIHBvcnQgcG9ydHIgZm9yd2FyZHMgdG8gLSB0aGUgcG9ydCB0aGUgYXBwIGl0c2VsZiBsaXN0ZW5zIG9uLlxuICAgKiBEZWZhdWx0cyB0byB0aGUgRGF0YWJyaWNrcyBBcHBzIHJ1bnRpbWUgY29udHJhY3QgYERBVEFCUklDS1NfQVBQX1BPUlRgXG4gICAqICh0aGVuIGA4MDAwYCksIHdoaWNoIGlzIHRoZSBwb3J0IHRoZSBwbGF0Zm9ybSByb3V0ZXMgdG8uXG4gICAqL1xuICBwb3J0PzogbnVtYmVyO1xufVxuXG4vKiogUmVzb2x2ZSB0aGUgcHVibGljIHBvcnQgcG9ydHIgc2hvdWxkIHRhcmdldDogZXhwbGljaXQsIGVsc2UgdGhlIEFwcHMgY29udHJhY3QuICovXG5mdW5jdGlvbiByZXNvbHZlUHVibGljUG9ydChwb3J0PzogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIHBvcnQgPz8gb2JqZWN0LnRvTnVtYmVyKHByb2Nlc3MuZW52LkRBVEFCUklDS1NfQVBQX1BPUlQpID8/IDgwMDA7XG59XG5cbi8qKlxuICogQnVpbGQgdGhlIHR1bm5lbCB7QGxpbmsgSW50ZXJjZXB0b3J9LiBQYXNzIGl0IHRvIGBjcmVhdGVBcHAoeyBpbnRlcmNlcHRvciB9KWAuXG4gKlxuICogQSBuby1vcCAobG9ncyBhbmQgcmV0dXJucykgd2hlbiBubyBwb3J0ciB0dW5uZWwgaXMgY29uZmlndXJlZCAtIG5vIGBQT1JUUl9UT0tFTmBcbiAqIG9yIG5vIHJlc29sdmFibGUgYDxzdWJkb21haW4+LjxzZXJ2ZXI+YCAtIHNvIGFuIGFwcCBjYW4gcmVnaXN0ZXIgaXRcbiAqIHVuY29uZGl0aW9uYWxseSBhbmQgb25seSBhY3R1YWxseSB0dW5uZWxzIHdoZXJlIHRoZSBkZXBsb3ltZW50IHdpcmVkIHBvcnRyLlxuICpcbiAqIEBleGFtcGxlXG4gKiBpbXBvcnQgeyBjcmVhdGVBcHAgfSBmcm9tIFwiQGRieC10b29scy9hcHBraXRcIjtcbiAqIGltcG9ydCB7IHR1bm5lbEludGVyY2VwdG9yIH0gZnJvbSBcIkBkYngtdG9vbHMvdHVubmVsXCI7XG4gKlxuICogYXdhaXQgY3JlYXRlQXBwKHtcbiAqICAgcGx1Z2luczogW3NlcnZlcih7IGhvc3QsIHN0YXRpY1BhdGggfSldLFxuICogICBpbnRlcmNlcHRvcjogdHVubmVsSW50ZXJjZXB0b3IoKSxcbiAqIH0pO1xuICovXG5leHBvcnQgZnVuY3Rpb24gdHVubmVsSW50ZXJjZXB0b3Iob3B0aW9uczogVHVubmVsSW50ZXJjZXB0b3JPcHRpb25zID0ge30pOiBJbnRlcmNlcHRvciB7XG4gIHJldHVybiBhc3luYyAoY3R4OiBJbnRlcmNlcHRvckNvbnRleHQpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBpZiAoY3R4LmVudi5kYXRhYnJpY2tzSG9zdCkge1xuICAgICAgcHJvY2Vzcy5lbnYuREFUQUJSSUNLU19IT1NUID8/PSBjdHguZW52LmRhdGFicmlja3NIb3N0O1xuICAgIH1cblxuICAgIGNvbnN0IHBvcnQgPSByZXNvbHZlUHVibGljUG9ydChvcHRpb25zLnBvcnQpO1xuICAgIGNvbnN0IHBvcnRyQ29uZmlnID0gcmVzb2x2ZVBvcnRyQ29uZmlnKHtcbiAgICAgIHB1YmxpY0RvbWFpbjogb3B0aW9ucy5wdWJsaWNEb21haW4sXG4gICAgICBzdWJkb21haW46IG9wdGlvbnMuc3ViZG9tYWluLFxuICAgICAgcG9ydCxcbiAgICB9KTtcbiAgICBpZiAoIXBvcnRyQ29uZmlnKSB7XG4gICAgICBsb2dnZXIuaW5mbyhcIm5vIFBPUlRSX1RPS0VOL1RVTk5FTF9QVUJMSUNfRE9NQUlOIC0gdGhlIGFwcCBydW5zIHdpdGhvdXQgYSBwdWJsaWMgdHVubmVsXCIpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHBvcnRyRW52ID0gYXdhaXQgaW5zdGFsbFBvcnRyKCk7XG4gICAgYXdhaXQgd3JpdGVQb3J0ckNvbmZpZyhwb3J0ckNvbmZpZywgcG9ydHJFbnYpO1xuICAgIGNvbnN0IHBvcnRyID0gYXdhaXQgc3RhcnRQb3J0cihwb3J0ckNvbmZpZywgcG9ydHJFbnYpO1xuICAgIGN0eC5iaW5kUHJvY2Vzcyhwb3J0cik7XG5cbiAgICBjdHgub25MaWZlY3ljbGUoXCJzaHV0ZG93blwiLCAoKSA9PiB7XG4gICAgICBpZiAoIXBvcnRyLmtpbGxlZCkgcG9ydHIua2lsbChcIlNJR1RFUk1cIik7XG4gICAgfSk7XG5cbiAgICBsb2dnZXIuaW5mbyhgdHVubmVsIGJvdW5kOiBwb3J0ciAtPiA6JHtwb3J0fSAoJHtwb3J0ckNvbmZpZy5zdWJkb21haW59LiR7cG9ydHJDb25maWcuc2VydmVyfSlgKTtcbiAgfTtcbn1cbiJdfQ==
@@ -1,16 +1,20 @@
1
1
  /**
2
- * portr install + config + launch for the tunnel CLI.
2
+ * portr install, config, and launch support for the tunnel runtime.
3
3
  *
4
- * Some hosts (Databricks Apps among them) mount the container's `$HOME` read-only
5
- * on cold start, so the portr binary and its config are placed under a writable,
6
- * cwd-rooted `.home` unconditionally - it costs nothing where `$HOME` is writable.
7
- * The install is idempotent (the installer skips when the on-PATH binary is
8
- * current). The config is rendered from `TUNNEL_PUBLIC_DOMAIN` (`<subdomain>.<server>`)
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
- /** Install portr (idempotent) into the cwd-rooted home and return the child env. */
34
- export declare function installPortr(): NodeJS.ProcessEnv;
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, env: NodeJS.ProcessEnv): void;
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, env: NodeJS.ProcessEnv): ReturnType<typeof spawn>;
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,26 @@
1
1
  /**
2
- * portr install + config + launch for the tunnel CLI.
2
+ * portr install, config, and launch support for the tunnel runtime.
3
3
  *
4
- * Some hosts (Databricks Apps among them) mount the container's `$HOME` read-only
5
- * on cold start, so the portr binary and its config are placed under a writable,
6
- * cwd-rooted `.home` unconditionally - it costs nothing where `$HOME` is writable.
7
- * The install is idempotent (the installer skips when the on-PATH binary is
8
- * current). The config is rendered from `TUNNEL_PUBLIC_DOMAIN` (`<subdomain>.<server>`)
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 { spawn, spawnSync } from "node:child_process";
14
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
12
+ import { execFile, spawn } from "node:child_process";
13
+ import { 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 PORTR_MIN_VERSION = "v1.0.15";
23
+ const execFileAsync = promisify(execFile);
19
24
  /**
20
25
  * Resolve portr config from `TUNNEL_PUBLIC_DOMAIN` + `PORTR_TOKEN`, or an
21
26
  * explicit `subdomain`. The domain is `<subdomain>.<server>` (e.g.
@@ -39,34 +44,54 @@ export function resolvePortrConfig(opts) {
39
44
  return undefined;
40
45
  return { subdomain, server, token, port: opts.port };
41
46
  }
42
- /** The writable home portr installs + configures under (Apps `$HOME` is read-only). */
43
- function portrHome() {
44
- const home = join(process.cwd(), ".home");
45
- mkdirSync(join(home, ".portr", "bin"), { recursive: true });
46
- return home;
47
+ /** GitHub release asset name for the current or supplied OS/architecture. */
48
+ export function portrAssetName(version, platform = process.platform, arch = process.arch) {
49
+ const osName = platform === "darwin" ? "Darwin" : platform === "linux" ? "Linux" : undefined;
50
+ const archName = arch === "arm64" ? "arm64" : arch === "x64" ? "x86_64" : undefined;
51
+ if (!osName || !archName) {
52
+ throw new Error(`portr has no supported release asset for ${platform}/${arch}`);
53
+ }
54
+ return `portr_${version}_${osName}_${archName}.zip`;
55
+ }
56
+ async function portrDownloadUrl() {
57
+ const releaseResponse = await fetch(PORTR_LATEST_RELEASE_URL, {
58
+ headers: {
59
+ accept: "application/vnd.github+json",
60
+ "user-agent": "@dbx-tools/tunnel",
61
+ },
62
+ });
63
+ if (!releaseResponse.ok) {
64
+ throw new Error(`portr release lookup failed (${releaseResponse.status})`);
65
+ }
66
+ const release = (await releaseResponse.json());
67
+ const version = release.tag_name.replace(/^v/, "");
68
+ const assetName = portrAssetName(version);
69
+ const asset = release.assets.find((candidate) => candidate.name === assetName);
70
+ if (!asset)
71
+ throw new Error(`portr release ${release.tag_name} has no ${assetName}`);
72
+ logger.info("installing portr", { version, asset: assetName });
73
+ return asset.browser_download_url;
47
74
  }
48
- /** Install portr (idempotent) into the cwd-rooted home and return the child env. */
49
- export function installPortr() {
50
- const home = portrHome();
51
- const env = {
75
+ /** Install portr when absent and return the environment used by its process. */
76
+ export async function installPortr(options = {}) {
77
+ const homeDir = options.homeDir ?? os.homedir();
78
+ const context = await bin.ensure("portr", portrDownloadUrl, {
79
+ autoUnpackage: true,
80
+ homeDir,
81
+ minVersion: PORTR_MIN_VERSION,
82
+ selector: ({ source }) => join(source, "portr"),
83
+ });
84
+ return {
52
85
  ...process.env,
53
- HOME: home,
86
+ HOME: homeDir,
54
87
  PORTR_AUTO_ADD_PATH: "no",
55
- PATH: [join(home, ".portr", "bin"), process.env.PATH ?? ""].join(delimiter),
88
+ PATH: [context.binDir, process.env.PATH ?? ""].join(delimiter),
56
89
  };
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
90
  }
66
91
  /** Render `~/.portr/config.yaml` for the resolved tunnel. */
67
- export function writePortrConfig(config, env) {
68
- const path = join(env.HOME, ".portr", "config.yaml");
69
- writeFileSync(path, [
92
+ export async function writePortrConfig(config, childEnv) {
93
+ const path = join(childEnv.HOME ?? os.homedir(), ".portr", "config.yaml");
94
+ await writeFile(path, [
70
95
  `server_url: ${config.server}`,
71
96
  `ssh_url: ${config.server}:4444`,
72
97
  `secret_key: ${config.token}`,
@@ -80,14 +105,10 @@ export function writePortrConfig(config, env) {
80
105
  ].join("\n"));
81
106
  }
82
107
  /** Launch `portr start` as a child process (caller supervises + kills it). */
83
- export function startPortr(config, env) {
108
+ export async function startPortr(config, childEnv) {
84
109
  // Reclaim the subdomain from any portr left by a previous boot in this container.
85
- spawnSync("pkill", ["-x", "portr"], { stdio: "ignore" });
110
+ await execFileAsync("pkill", ["-x", "portr"]).catch(() => undefined);
86
111
  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"));
112
+ return spawn("portr", ["start"], { env: childEnv, stdio: "inherit" });
92
113
  }
93
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9ydHIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcG9ydHIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7O0dBV0c7QUFFSCxPQUFPLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUMvRCxPQUFPLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUM1QyxPQUFPLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ2xELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUU3QyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0FBVTFDOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLGtCQUFrQixDQUFDLElBS2xDO0lBQ0MsbUVBQW1FO0lBQ25FLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxhQUFhLENBQUMsQ0FBQztJQUNwRCxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztJQUNoRSxJQUFJLENBQUMsS0FBSztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQzdCLElBQUksU0FBUyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDL0IsSUFBSSxNQUEwQixDQUFDO0lBQy9CLElBQUksTUFBTSxFQUFFLENBQUM7UUFDWCxTQUFTLEtBQUssTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNuQyxNQUFNLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ2pELENBQUM7SUFDRCxNQUFNLEtBQUssR0FBRyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxTQUFTLENBQUM7SUFDakQsSUFBSSxDQUFDLFNBQVMsSUFBSSxDQUFDLE1BQU0sSUFBSSxNQUFNLEtBQUssTUFBTTtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ2pFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO0FBQ3ZELENBQUM7QUFFRCx1RkFBdUY7QUFDdkYsU0FBUyxTQUFTO0lBQ2hCLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDMUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxFQUFFLEtBQUssQ0FBQyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDNUQsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDO0FBRUQsb0ZBQW9GO0FBQ3BGLE1BQU0sVUFBVSxZQUFZO0lBQzFCLE1BQU0sSUFBSSxHQUFHLFNBQVMsRUFBRSxDQUFDO0lBQ3pCLE1BQU0sR0FBRyxHQUFzQjtRQUM3QixHQUFHLE9BQU8sQ0FBQyxHQUFHO1FBQ2QsSUFBSSxFQUFFLElBQUk7UUFDVixtQkFBbUIsRUFBRSxJQUFJO1FBQ3pCLElBQUksRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxFQUFFLEtBQUssQ0FBQyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7S0FDNUUsQ0FBQztJQUNGLE1BQU0sQ0FBQyxJQUFJLENBQUMsK0JBQStCLENBQUMsQ0FBQztJQUM3QyxNQUFNLEdBQUcsR0FBRyxTQUFTLENBQUMsTUFBTSxFQUFFLENBQUMsSUFBSSxFQUFFLDBDQUEwQyxDQUFDLEVBQUU7UUFDaEYsR0FBRztRQUNILEtBQUssRUFBRSxTQUFTO0tBQ2pCLENBQUMsQ0FBQztJQUNILElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO0lBQzlELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQztBQUVELDZEQUE2RDtBQUM3RCxNQUFNLFVBQVUsZ0JBQWdCLENBQUMsTUFBbUIsRUFBRSxHQUFzQjtJQUMxRSxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUssRUFBRSxRQUFRLEVBQUUsYUFBYSxDQUFDLENBQUM7SUFDdEQsYUFBYSxDQUNYLElBQUksRUFDSjtRQUNFLGVBQWUsTUFBTSxDQUFDLE1BQU0sRUFBRTtRQUM5QixZQUFZLE1BQU0sQ0FBQyxNQUFNLE9BQU87UUFDaEMsZUFBZSxNQUFNLENBQUMsS0FBSyxFQUFFO1FBQzdCLHlCQUF5QjtRQUN6QixtQkFBbUI7UUFDbkIsVUFBVTtRQUNWLGFBQWEsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUMvQixrQkFBa0IsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUNwQyxhQUFhLE1BQU0sQ0FBQyxJQUFJLEVBQUU7UUFDMUIsRUFBRTtLQUNILENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUNiLENBQUM7QUFDSixDQUFDO0FBRUQsOEVBQThFO0FBQzlFLE1BQU0sVUFBVSxVQUFVLENBQUMsTUFBbUIsRUFBRSxHQUFzQjtJQUNwRSxrRkFBa0Y7SUFDbEYsU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsRUFBRSxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDO0lBQ3pELE1BQU0sQ0FBQyxJQUFJLENBQUMsMkJBQTJCLE1BQU0sQ0FBQyxTQUFTLElBQUksTUFBTSxDQUFDLE1BQU0sUUFBUSxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUMvRixPQUFPLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQztBQUM5RCxDQUFDO0FBRUQsOEVBQThFO0FBQzlFLE1BQU0sVUFBVSxjQUFjLENBQUMsR0FBc0I7SUFDbkQsT0FBTyxVQUFVLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFLLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0FBQy9ELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIHBvcnRyIGluc3RhbGwgKyBjb25maWcgKyBsYXVuY2ggZm9yIHRoZSB0dW5uZWwgQ0xJLlxuICpcbiAqIFNvbWUgaG9zdHMgKERhdGFicmlja3MgQXBwcyBhbW9uZyB0aGVtKSBtb3VudCB0aGUgY29udGFpbmVyJ3MgYCRIT01FYCByZWFkLW9ubHlcbiAqIG9uIGNvbGQgc3RhcnQsIHNvIHRoZSBwb3J0ciBiaW5hcnkgYW5kIGl0cyBjb25maWcgYXJlIHBsYWNlZCB1bmRlciBhIHdyaXRhYmxlLFxuICogY3dkLXJvb3RlZCBgLmhvbWVgIHVuY29uZGl0aW9uYWxseSAtIGl0IGNvc3RzIG5vdGhpbmcgd2hlcmUgYCRIT01FYCBpcyB3cml0YWJsZS5cbiAqIFRoZSBpbnN0YWxsIGlzIGlkZW1wb3RlbnQgKHRoZSBpbnN0YWxsZXIgc2tpcHMgd2hlbiB0aGUgb24tUEFUSCBiaW5hcnkgaXNcbiAqIGN1cnJlbnQpLiBUaGUgY29uZmlnIGlzIHJlbmRlcmVkIGZyb20gYFRVTk5FTF9QVUJMSUNfRE9NQUlOYCAoYDxzdWJkb21haW4+LjxzZXJ2ZXI+YClcbiAqICsgYFBPUlRSX1RPS0VOYCBhbmQgcG9pbnRzIHBvcnRyIGF0IHRoZSBQVUJMSUMgcG9ydCAodGhlIHByb3h5IGxpc3RlbnMgdGhlcmUpLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBzcGF3biwgc3Bhd25TeW5jIH0gZnJvbSBcIm5vZGU6Y2hpbGRfcHJvY2Vzc1wiO1xuaW1wb3J0IHsgZXhpc3RzU3luYywgbWtkaXJTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSBcIm5vZGU6ZnNcIjtcbmltcG9ydCB7IGRlbGltaXRlciwgam9pbiB9IGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB7IGVudiwgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IFBVQkxJQ19ET01BSU5fRU5WIH0gZnJvbSBcIi4vZW52LnRzXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJ0dW5uZWw6cG9ydHJcIik7XG5cbi8qKiBSZXNvbHZlZCBwb3J0ciB3aXJpbmcsIG9yIGB1bmRlZmluZWRgIHdoZW4gbm8gdHVubmVsIGlzIGNvbmZpZ3VyZWQuICovXG5leHBvcnQgaW50ZXJmYWNlIFBvcnRyQ29uZmlnIHtcbiAgc3ViZG9tYWluOiBzdHJpbmc7XG4gIHNlcnZlcjogc3RyaW5nO1xuICB0b2tlbjogc3RyaW5nO1xuICBwb3J0OiBudW1iZXI7XG59XG5cbi8qKlxuICogUmVzb2x2ZSBwb3J0ciBjb25maWcgZnJvbSBgVFVOTkVMX1BVQkxJQ19ET01BSU5gICsgYFBPUlRSX1RPS0VOYCwgb3IgYW5cbiAqIGV4cGxpY2l0IGBzdWJkb21haW5gLiBUaGUgZG9tYWluIGlzIGA8c3ViZG9tYWluPi48c2VydmVyPmAgKGUuZy5cbiAqIGBkZW1vLmFwcHMuZGJ4LnRvb2xzYCkuIFJldHVybnMgYHVuZGVmaW5lZGAgKG5vIHR1bm5lbCkgd2hlbiB0aGUgdG9rZW4gb3IgYVxuICogdXNhYmxlIGRvbWFpbiBpcyBhYnNlbnQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlUG9ydHJDb25maWcob3B0czoge1xuICBwdWJsaWNEb21haW4/OiBzdHJpbmc7XG4gIHN1YmRvbWFpbj86IHN0cmluZztcbiAgdG9rZW4/OiBzdHJpbmc7XG4gIHBvcnQ6IG51bWJlcjtcbn0pOiBQb3J0ckNvbmZpZyB8IHVuZGVmaW5lZCB7XG4gIC8vIFBPUlRSXyogaXMgdXBzdHJlYW0gcG9ydHIncyBvd24gbmFtZXNwYWNlLCBzbyBpdCBrZWVwcyBpdHMgbmFtZS5cbiAgY29uc3QgdG9rZW4gPSBlbnYuc3RyaW5nKG9wdHMudG9rZW4sIFwiUE9SVFJfVE9LRU5cIik7XG4gIGNvbnN0IGRvbWFpbiA9IGVudi5zdHJpbmcob3B0cy5wdWJsaWNEb21haW4sIFBVQkxJQ19ET01BSU5fRU5WKTtcbiAgaWYgKCF0b2tlbikgcmV0dXJuIHVuZGVmaW5lZDtcbiAgbGV0IHN1YmRvbWFpbiA9IG9wdHMuc3ViZG9tYWluO1xuICBsZXQgc2VydmVyOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGlmIChkb21haW4pIHtcbiAgICBzdWJkb21haW4gPz89IGRvbWFpbi5zcGxpdChcIi5cIilbMF07XG4gICAgc2VydmVyID0gZG9tYWluLnNsaWNlKGRvbWFpbi5pbmRleE9mKFwiLlwiKSArIDEpO1xuICB9XG4gIHNlcnZlciA/Pz0gZW52LnRleHQoXCJQT1JUUl9TRVJWRVJcIikgPz8gdW5kZWZpbmVkO1xuICBpZiAoIXN1YmRvbWFpbiB8fCAhc2VydmVyIHx8IHNlcnZlciA9PT0gZG9tYWluKSByZXR1cm4gdW5kZWZpbmVkO1xuICByZXR1cm4geyBzdWJkb21haW4sIHNlcnZlciwgdG9rZW4sIHBvcnQ6IG9wdHMucG9ydCB9O1xufVxuXG4vKiogVGhlIHdyaXRhYmxlIGhvbWUgcG9ydHIgaW5zdGFsbHMgKyBjb25maWd1cmVzIHVuZGVyIChBcHBzIGAkSE9NRWAgaXMgcmVhZC1vbmx5KS4gKi9cbmZ1bmN0aW9uIHBvcnRySG9tZSgpOiBzdHJpbmcge1xuICBjb25zdCBob21lID0gam9pbihwcm9jZXNzLmN3ZCgpLCBcIi5ob21lXCIpO1xuICBta2RpclN5bmMoam9pbihob21lLCBcIi5wb3J0clwiLCBcImJpblwiKSwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gIHJldHVybiBob21lO1xufVxuXG4vKiogSW5zdGFsbCBwb3J0ciAoaWRlbXBvdGVudCkgaW50byB0aGUgY3dkLXJvb3RlZCBob21lIGFuZCByZXR1cm4gdGhlIGNoaWxkIGVudi4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbnN0YWxsUG9ydHIoKTogTm9kZUpTLlByb2Nlc3NFbnYge1xuICBjb25zdCBob21lID0gcG9ydHJIb21lKCk7XG4gIGNvbnN0IGVudjogTm9kZUpTLlByb2Nlc3NFbnYgPSB7XG4gICAgLi4ucHJvY2Vzcy5lbnYsXG4gICAgSE9NRTogaG9tZSxcbiAgICBQT1JUUl9BVVRPX0FERF9QQVRIOiBcIm5vXCIsXG4gICAgUEFUSDogW2pvaW4oaG9tZSwgXCIucG9ydHJcIiwgXCJiaW5cIiksIHByb2Nlc3MuZW52LlBBVEggPz8gXCJcIl0uam9pbihkZWxpbWl0ZXIpLFxuICB9O1xuICBsb2dnZXIuaW5mbyhcImluc3RhbGxpbmcgcG9ydHIgKGlkZW1wb3RlbnQpXCIpO1xuICBjb25zdCByZXMgPSBzcGF3blN5bmMoXCJiYXNoXCIsIFtcIi1jXCIsIFwiY3VybCAtc1NmIGh0dHBzOi8vaW5zdGFsbC5wb3J0ci5kZXYgfCBzaFwiXSwge1xuICAgIGVudixcbiAgICBzdGRpbzogXCJpbmhlcml0XCIsXG4gIH0pO1xuICBpZiAocmVzLnN0YXR1cyAhPT0gMCkgdGhyb3cgbmV3IEVycm9yKFwicG9ydHIgaW5zdGFsbCBmYWlsZWRcIik7XG4gIHJldHVybiBlbnY7XG59XG5cbi8qKiBSZW5kZXIgYH4vLnBvcnRyL2NvbmZpZy55YW1sYCBmb3IgdGhlIHJlc29sdmVkIHR1bm5lbC4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3cml0ZVBvcnRyQ29uZmlnKGNvbmZpZzogUG9ydHJDb25maWcsIGVudjogTm9kZUpTLlByb2Nlc3NFbnYpOiB2b2lkIHtcbiAgY29uc3QgcGF0aCA9IGpvaW4oZW52LkhPTUUhLCBcIi5wb3J0clwiLCBcImNvbmZpZy55YW1sXCIpO1xuICB3cml0ZUZpbGVTeW5jKFxuICAgIHBhdGgsXG4gICAgW1xuICAgICAgYHNlcnZlcl91cmw6ICR7Y29uZmlnLnNlcnZlcn1gLFxuICAgICAgYHNzaF91cmw6ICR7Y29uZmlnLnNlcnZlcn06NDQ0NGAsXG4gICAgICBgc2VjcmV0X2tleTogJHtjb25maWcudG9rZW59YCxcbiAgICAgIFwiZGlzYWJsZV9kYXNoYm9hcmQ6IHRydWVcIixcbiAgICAgIFwiZGlzYWJsZV90dWk6IHRydWVcIixcbiAgICAgIFwidHVubmVsczpcIixcbiAgICAgIGAgIC0gbmFtZTogJHtjb25maWcuc3ViZG9tYWlufWAsXG4gICAgICBgICAgIHN1YmRvbWFpbjogJHtjb25maWcuc3ViZG9tYWlufWAsXG4gICAgICBgICAgIHBvcnQ6ICR7Y29uZmlnLnBvcnR9YCxcbiAgICAgIFwiXCIsXG4gICAgXS5qb2luKFwiXFxuXCIpLFxuICApO1xufVxuXG4vKiogTGF1bmNoIGBwb3J0ciBzdGFydGAgYXMgYSBjaGlsZCBwcm9jZXNzIChjYWxsZXIgc3VwZXJ2aXNlcyArIGtpbGxzIGl0KS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzdGFydFBvcnRyKGNvbmZpZzogUG9ydHJDb25maWcsIGVudjogTm9kZUpTLlByb2Nlc3NFbnYpOiBSZXR1cm5UeXBlPHR5cGVvZiBzcGF3bj4ge1xuICAvLyBSZWNsYWltIHRoZSBzdWJkb21haW4gZnJvbSBhbnkgcG9ydHIgbGVmdCBieSBhIHByZXZpb3VzIGJvb3QgaW4gdGhpcyBjb250YWluZXIuXG4gIHNwYXduU3luYyhcInBraWxsXCIsIFtcIi14XCIsIFwicG9ydHJcIl0sIHsgc3RkaW86IFwiaWdub3JlXCIgfSk7XG4gIGxvZ2dlci5pbmZvKGBwb3J0ciB0dW5uZWxpbmcgaHR0cHM6Ly8ke2NvbmZpZy5zdWJkb21haW59LiR7Y29uZmlnLnNlcnZlcn0gLT4gOiR7Y29uZmlnLnBvcnR9YCk7XG4gIHJldHVybiBzcGF3bihcInBvcnRyXCIsIFtcInN0YXJ0XCJdLCB7IGVudiwgc3RkaW86IFwiaW5oZXJpdFwiIH0pO1xufVxuXG4vKiogVHJ1ZSB3aGVuIHRoZSBpbnN0YWxsZWQgcG9ydHIgYmluYXJ5IHBhdGggZXhpc3RzIChwb3N0LWluc3RhbGwgc2FuaXR5KS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwb3J0ckluc3RhbGxlZChlbnY6IE5vZGVKUy5Qcm9jZXNzRW52KTogYm9vbGVhbiB7XG4gIHJldHVybiBleGlzdHNTeW5jKGpvaW4oZW52LkhPTUUhLCBcIi5wb3J0clwiLCBcImJpblwiLCBcInBvcnRyXCIpKTtcbn1cbiJdfQ==
114
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9ydHIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcG9ydHIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7R0FVRztBQUNILE9BQU8sRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDckQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzdDLE9BQU8sRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUN6QixPQUFPLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUM1QyxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBRXRDLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN0QyxPQUFPLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ2xELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUU3QyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0FBQzFDLE1BQU0sd0JBQXdCLEdBQUcsOERBQThELENBQUM7QUFDaEcsTUFBTSxpQkFBaUIsR0FBRyxTQUFTLENBQUM7QUFDcEMsTUFBTSxhQUFhLEdBQUcsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBcUIxQzs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxrQkFBa0IsQ0FBQyxJQUtsQztJQUNDLG1FQUFtRTtJQUNuRSxNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsYUFBYSxDQUFDLENBQUM7SUFDcEQsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFLGlCQUFpQixDQUFDLENBQUM7SUFDaEUsSUFBSSxDQUFDLEtBQUs7UUFBRSxPQUFPLFNBQVMs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LFNBQVMsRUFBRTtRQUMvQixrQkFBa0IsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUNwQyxhQUFhLE1BQU0sQ0FBQyxJQUFJLEVBQUU7UUFDMUIsRUFBRTtLQUNILENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUNiLENBQUM7QUFDSixDQUFDO0FBRUQsOEVBQThFO0FBQzlFLE1BQU0sQ0FBQyxLQUFLLFVBQVUsVUFBVSxDQUM5QixNQUFtQixFQUNuQixRQUEyQjtJQUUzQixrRkFBa0Y7SUFDbEYsTUFBTSxhQUFhLENBQUMsT0FBTyxFQUFFLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBRXJFLE1BQU0sQ0FBQyxJQUFJLENBQUMsMkJBQTJCLE1BQU0sQ0FBQyxTQUFTLElBQUksTUFBTSxDQUFDLE1BQU0sUUFBUSxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUMvRixPQUFPLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7QUFDeEUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogcG9ydHIgaW5zdGFsbCwgY29uZmlnLCBhbmQgbGF1bmNoIHN1cHBvcnQgZm9yIHRoZSB0dW5uZWwgcnVudGltZS5cbiAqXG4gKiBUaGUgYmluYXJ5IGFuZCBjb25maWcgdXNlIHBvcnRyJ3MgY29udmVudGlvbmFsIGAkSE9NRS8ucG9ydHJgIHRyZWUuIFRoZVxuICogaW5zdGFsbCBpcyBpZGVtcG90ZW50ICh0aGUgaW5zdGFsbGVyIHNraXBzIGFuIGV4aXN0aW5nIGV4ZWN1dGFibGUpLiBUaGVcbiAqIGNvbmZpZyBpcyByZW5kZXJlZCBmcm9tIGBUVU5ORUxfUFVCTElDX0RPTUFJTmBcbiAqIChgPHN1YmRvbWFpbj4uPHNlcnZlcj5gKSArIGBQT1JUUl9UT0tFTmAgYW5kIHBvaW50cyBwb3J0ciBhdCB0aGUgUFVCTElDIHBvcnRcbiAqICh0aGUgcHJveHkgbGlzdGVucyB0aGVyZSkuXG4gKlxuICogQG1vZHVsZVxuICovXG5pbXBvcnQgeyBleGVjRmlsZSwgc3Bhd24gfSBmcm9tIFwibm9kZTpjaGlsZF9wcm9jZXNzXCI7XG5pbXBvcnQgeyB3cml0ZUZpbGUgfSBmcm9tIFwibm9kZTpmcy9wcm9taXNlc1wiO1xuaW1wb3J0IG9zIGZyb20gXCJub2RlOm9zXCI7XG5pbXBvcnQgeyBkZWxpbWl0ZXIsIGpvaW4gfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgeyBwcm9taXNpZnkgfSBmcm9tIFwibm9kZTp1dGlsXCI7XG5cbmltcG9ydCB7IGJpbiB9IGZyb20gXCJAZGJ4LXRvb2xzL2NvcmVcIjtcbmltcG9ydCB7IGVudiwgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IFBVQkxJQ19ET01BSU5fRU5WIH0gZnJvbSBcIi4vZW52LnRzXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJ0dW5uZWw6cG9ydHJcIik7XG5jb25zdCBQT1JUUl9MQVRFU1RfUkVMRUFTRV9VUkwgPSBcImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvYW1hbHNoYWppL3BvcnRyL3JlbGVhc2VzL2xhdGVzdFwiO1xuY29uc3QgUE9SVFJfTUlOX1ZFUlNJT04gPSBcInYxLjAuMTVcIjtcbmNvbnN0IGV4ZWNGaWxlQXN5bmMgPSBwcm9taXNpZnkoZXhlY0ZpbGUpO1xuXG5pbnRlcmZhY2UgUG9ydHJSZWxlYXNlIHtcbiAgdGFnX25hbWU6IHN0cmluZztcbiAgYXNzZXRzOiBBcnJheTx7IG5hbWU6IHN0cmluZzsgYnJvd3Nlcl9kb3dubG9hZF91cmw6IHN0cmluZyB9Pjtcbn1cblxuLyoqIE9wdGlvbnMgZm9yIGluc3RhbGxpbmcgdGhlIHBvcnRyIGV4ZWN1dGFibGUuICovXG5leHBvcnQgaW50ZXJmYWNlIFBvcnRySW5zdGFsbE9wdGlvbnMge1xuICAvKiogSG9tZSBkaXJlY3RvcnkgY29udGFpbmluZyBgLnBvcnRyYDsgZGVmYXVsdHMgdG8gdGhlIE9TIGhvbWUgZGlyZWN0b3J5LiAqL1xuICBob21lRGlyPzogc3RyaW5nO1xufVxuXG4vKiogUmVzb2x2ZWQgcG9ydHIgd2lyaW5nLCBvciBgdW5kZWZpbmVkYCB3aGVuIG5vIHR1bm5lbCBpcyBjb25maWd1cmVkLiAqL1xuZXhwb3J0IGludGVyZmFjZSBQb3J0ckNvbmZpZyB7XG4gIHN1YmRvbWFpbjogc3RyaW5nO1xuICBzZXJ2ZXI6IHN0cmluZztcbiAgdG9rZW46IHN0cmluZztcbiAgcG9ydDogbnVtYmVyO1xufVxuXG4vKipcbiAqIFJlc29sdmUgcG9ydHIgY29uZmlnIGZyb20gYFRVTk5FTF9QVUJMSUNfRE9NQUlOYCArIGBQT1JUUl9UT0tFTmAsIG9yIGFuXG4gKiBleHBsaWNpdCBgc3ViZG9tYWluYC4gVGhlIGRvbWFpbiBpcyBgPHN1YmRvbWFpbj4uPHNlcnZlcj5gIChlLmcuXG4gKiBgZGVtby5hcHBzLmRieC50b29sc2ApLiBSZXR1cm5zIGB1bmRlZmluZWRgIChubyB0dW5uZWwpIHdoZW4gdGhlIHRva2VuIG9yIGFcbiAqIHVzYWJsZSBkb21haW4gaXMgYWJzZW50LlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZVBvcnRyQ29uZmlnKG9wdHM6IHtcbiAgcHVibGljRG9tYWluPzogc3RyaW5nO1xuICBzdWJkb21haW4/OiBzdHJpbmc7XG4gIHRva2VuPzogc3RyaW5nO1xuICBwb3J0OiBudW1iZXI7XG59KTogUG9ydHJDb25maWcgfCB1bmRlZmluZWQge1xuICAvLyBQT1JUUl8qIGlzIHVwc3RyZWFtIHBvcnRyJ3Mgb3duIG5hbWVzcGFjZSwgc28gaXQga2VlcHMgaXRzIG5hbWUuXG4gIGNvbnN0IHRva2VuID0gZW52LnN0cmluZyhvcHRzLnRva2VuLCBcIlBPUlRSX1RPS0VOXCIpO1xuICBjb25zdCBkb21haW4gPSBlbnYuc3RyaW5nKG9wdHMucHVibGljRG9tYWluLCBQVUJMSUNfRE9NQUlOX0VOVik7XG4gIGlmICghdG9rZW4pIHJldHVybiB1bmRlZmluZWQ7XG4gIGxldCBzdWJkb21haW4gPSBvcHRzLnN1YmRvbWFpbjtcbiAgbGV0IHNlcnZlcjogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBpZiAoZG9tYWluKSB7XG4gICAgc3ViZG9tYWluID8/PSBkb21haW4uc3BsaXQoXCIuXCIpWzBdO1xuICAgIHNlcnZlciA9IGRvbWFpbi5zbGljZShkb21haW4uaW5kZXhPZihcIi5cIikgKyAxKTtcbiAgfVxuICBzZXJ2ZXIgPz89IGVudi50ZXh0KFwiUE9SVFJfU0VSVkVSXCIpID8/IHVuZGVmaW5lZDtcbiAgaWYgKCFzdWJkb21haW4gfHwgIXNlcnZlciB8fCBzZXJ2ZXIgPT09IGRvbWFpbikgcmV0dXJuIHVuZGVmaW5lZDtcbiAgcmV0dXJuIHsgc3ViZG9tYWluLCBzZXJ2ZXIsIHRva2VuLCBwb3J0OiBvcHRzLnBvcnQgfTtcbn1cblxuLyoqIEdpdEh1YiByZWxlYXNlIGFzc2V0IG5hbWUgZm9yIHRoZSBjdXJyZW50IG9yIHN1cHBsaWVkIE9TL2FyY2hpdGVjdHVyZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwb3J0ckFzc2V0TmFtZShcbiAgdmVyc2lvbjogc3RyaW5nLFxuICBwbGF0Zm9ybTogTm9kZUpTLlBsYXRmb3JtID0gcHJvY2Vzcy5wbGF0Zm9ybSxcbiAgYXJjaDogc3RyaW5nID0gcHJvY2Vzcy5hcmNoLFxuKTogc3RyaW5nIHtcbiAgY29uc3Qgb3NOYW1lID0gcGxhdGZvcm0gPT09IFwiZGFyd2luXCIgPyBcIkRhcndpblwiIDogcGxhdGZvcm0gPT09IFwibGludXhcIiA/IFwiTGludXhcIiA6IHVuZGVmaW5lZDtcbiAgY29uc3QgYXJjaE5hbWUgPSBhcmNoID09PSBcImFybTY0XCIgPyBcImFybTY0XCIgOiBhcmNoID09PSBcIng2NFwiID8gXCJ4ODZfNjRcIiA6IHVuZGVmaW5lZDtcbiAgaWYgKCFvc05hbWUgfHwgIWFyY2hOYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBwb3J0ciBoYXMgbm8gc3VwcG9ydGVkIHJlbGVhc2UgYXNzZXQgZm9yICR7cGxhdGZvcm19LyR7YXJjaH1gKTtcbiAgfVxuICByZXR1cm4gYHBvcnRyXyR7dmVyc2lvbn1fJHtvc05hbWV9XyR7YXJjaE5hbWV9LnppcGA7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHBvcnRyRG93bmxvYWRVcmwoKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3QgcmVsZWFzZVJlc3BvbnNlID0gYXdhaXQgZmV0Y2goUE9SVFJfTEFURVNUX1JFTEVBU0VfVVJMLCB7XG4gICAgaGVhZGVyczoge1xuICAgICAgYWNjZXB0OiBcImFwcGxpY2F0aW9uL3ZuZC5naXRodWIranNvblwiLFxuICAgICAgXCJ1c2VyLWFnZW50XCI6IFwiQGRieC10b29scy90dW5uZWxcIixcbiAgICB9LFxuICB9KTtcbiAgaWYgKCFyZWxlYXNlUmVzcG9uc2Uub2spIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYHBvcnRyIHJlbGVhc2UgbG9va3VwIGZhaWxlZCAoJHtyZWxlYXNlUmVzcG9uc2Uuc3RhdHVzfSlgKTtcbiAgfVxuICBjb25zdCByZWxlYXNlID0gKGF3YWl0IHJlbGVhc2VSZXNwb25zZS5qc29uKCkpIGFzIFBvcnRyUmVsZWFzZTtcbiAgY29uc3QgdmVyc2lvbiA9IHJlbGVhc2UudGFnX25hbWUucmVwbGFjZSgvXnYvLCBcIlwiKTtcblxuICBjb25zdCBhc3NldE5hbWUgPSBwb3J0ckFzc2V0TmFtZSh2ZXJzaW9uKTtcbiAgY29uc3QgYXNzZXQgPSByZWxlYXNlLmFzc2V0cy5maW5kKChjYW5kaWRhdGUpID0+IGNhbmRpZGF0ZS5uYW1lID09PSBhc3NldE5hbWUpO1xuICBpZiAoIWFzc2V0KSB0aHJvdyBuZXcgRXJyb3IoYHBvcnRyIHJlbGVhc2UgJHtyZWxlYXNlLnRhZ19uYW1lfSBoYXMgbm8gJHthc3NldE5hbWV9YCk7XG5cbiAgbG9nZ2VyLmluZm8oXCJpbnN0YWxsaW5nIHBvcnRyXCIsIHsgdmVyc2lvbiwgYXNzZXQ6IGFzc2V0TmFtZSB9KTtcbiAgcmV0dXJuIGFzc2V0LmJyb3dzZXJfZG93bmxvYWRfdXJsO1xufVxuXG4vKiogSW5zdGFsbCBwb3J0ciB3aGVuIGFic2VudCBhbmQgcmV0dXJuIHRoZSBlbnZpcm9ubWVudCB1c2VkIGJ5IGl0cyBwcm9jZXNzLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluc3RhbGxQb3J0cihvcHRpb25zOiBQb3J0ckluc3RhbGxPcHRpb25zID0ge30pOiBQcm9taXNlPE5vZGVKUy5Qcm9jZXNzRW52PiB7XG4gIGNvbnN0IGhvbWVEaXIgPSBvcHRpb25zLmhvbWVEaXIgPz8gb3MuaG9tZWRpcigpO1xuICBjb25zdCBjb250ZXh0ID0gYXdhaXQgYmluLmVuc3VyZShcInBvcnRyXCIsIHBvcnRyRG93bmxvYWRVcmwsIHtcbiAgICBhdXRvVW5wYWNrYWdlOiB0cnVlLFxuICAgIGhvbWVEaXIsXG4gICAgbWluVmVyc2lvbjogUE9SVFJfTUlOX1ZFUlNJT04sXG4gICAgc2VsZWN0b3I6ICh7IHNvdXJjZSB9KSA9PiBqb2luKHNvdXJjZSwgXCJwb3J0clwiKSxcbiAgfSk7XG4gIHJldHVybiB7XG4gICAgLi4ucHJvY2Vzcy5lbnYsXG4gICAgSE9NRTogaG9tZURpcixcbiAgICBQT1JUUl9BVVRPX0FERF9QQVRIOiBcIm5vXCIsXG4gICAgUEFUSDogW2NvbnRleHQuYmluRGlyLCBwcm9jZXNzLmVudi5QQVRIID8/IFwiXCJdLmpvaW4oZGVsaW1pdGVyKSxcbiAgfTtcbn1cblxuLyoqIFJlbmRlciBgfi8ucG9ydHIvY29uZmlnLnlhbWxgIGZvciB0aGUgcmVzb2x2ZWQgdHVubmVsLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdyaXRlUG9ydHJDb25maWcoXG4gIGNvbmZpZzogUG9ydHJDb25maWcsXG4gIGNoaWxkRW52OiBOb2RlSlMuUHJvY2Vzc0Vudixcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBwYXRoID0gam9pbihjaGlsZEVudi5IT01FID8/IG9zLmhvbWVkaXIoKSwgXCIucG9ydHJcIiwgXCJjb25maWcueWFtbFwiKTtcbiAgYXdhaXQgd3JpdGVGaWxlKFxuICAgIHBhdGgsXG4gICAgW1xuICAgICAgYHNlcnZlcl91cmw6ICR7Y29uZmlnLnNlcnZlcn1gLFxuICAgICAgYHNzaF91cmw6ICR7Y29uZmlnLnNlcnZlcn06NDQ0NGAsXG4gICAgICBgc2VjcmV0X2tleTogJHtjb25maWcudG9rZW59YCxcbiAgICAgIFwiZGlzYWJsZV9kYXNoYm9hcmQ6IHRydWVcIixcbiAgICAgIFwiZGlzYWJsZV90dWk6IHRydWVcIixcbiAgICAgIFwidHVubmVsczpcIixcbiAgICAgIGAgIC0gbmFtZTogJHtjb25maWcuc3ViZG9tYWlufWAsXG4gICAgICBgICAgIHN1YmRvbWFpbjogJHtjb25maWcuc3ViZG9tYWlufWAsXG4gICAgICBgICAgIHBvcnQ6ICR7Y29uZmlnLnBvcnR9YCxcbiAgICAgIFwiXCIsXG4gICAgXS5qb2luKFwiXFxuXCIpLFxuICApO1xufVxuXG4vKiogTGF1bmNoIGBwb3J0ciBzdGFydGAgYXMgYSBjaGlsZCBwcm9jZXNzIChjYWxsZXIgc3VwZXJ2aXNlcyArIGtpbGxzIGl0KS4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydFBvcnRyKFxuICBjb25maWc6IFBvcnRyQ29uZmlnLFxuICBjaGlsZEVudjogTm9kZUpTLlByb2Nlc3NFbnYsXG4pOiBQcm9taXNlPFJldHVyblR5cGU8dHlwZW9mIHNwYXduPj4ge1xuICAvLyBSZWNsYWltIHRoZSBzdWJkb21haW4gZnJvbSBhbnkgcG9ydHIgbGVmdCBieSBhIHByZXZpb3VzIGJvb3QgaW4gdGhpcyBjb250YWluZXIuXG4gIGF3YWl0IGV4ZWNGaWxlQXN5bmMoXCJwa2lsbFwiLCBbXCIteFwiLCBcInBvcnRyXCJdKS5jYXRjaCgoKSA9PiB1bmRlZmluZWQpO1xuXG4gIGxvZ2dlci5pbmZvKGBwb3J0ciB0dW5uZWxpbmcgaHR0cHM6Ly8ke2NvbmZpZy5zdWJkb21haW59LiR7Y29uZmlnLnNlcnZlcn0gLT4gOiR7Y29uZmlnLnBvcnR9YCk7XG4gIHJldHVybiBzcGF3bihcInBvcnRyXCIsIFtcInN0YXJ0XCJdLCB7IGVudjogY2hpbGRFbnYsIHN0ZGlvOiBcImluaGVyaXRcIiB9KTtcbn1cbiJdfQ==
package/package.json CHANGED
@@ -18,19 +18,20 @@
18
18
  "projen": "projen"
19
19
  },
20
20
  "devDependencies": {
21
- "@dbx-tools/email": "0.6.65",
21
+ "@dbx-tools/email": "0.6.67",
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.65"
27
+ "@dbx-tools/email": "0.6.67"
28
28
  },
29
29
  "dependencies": {
30
30
  "@databricks/appkit": "^0.43.0",
31
- "@dbx-tools/appkit": "0.6.65",
32
- "@dbx-tools/shared-core": "0.6.65",
33
- "@dbx-tools/shared-email": "0.6.65",
31
+ "@dbx-tools/appkit": "0.6.67",
32
+ "@dbx-tools/core": "0.6.67",
33
+ "@dbx-tools/shared-core": "0.6.67",
34
+ "@dbx-tools/shared-email": "0.6.67",
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.65",
51
+ "version": "0.6.67",
51
52
  "types": "./lib/index.d.ts",
52
53
  "type": "module",
53
54
  "exports": {
@@ -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
- * 1. applies the workspace host the context computed to `process.env`
11
- * (`DATABRICKS_HOST`), so portr and any later SDK call agree on the workspace;
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
- // 3. Install + launch portr, then bind it: the app and portr now share a fate.
92
- const portrEnv = installPortr();
93
- writePortrConfig(portrConfig, portrEnv);
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,39 @@
1
1
  /**
2
- * portr install + config + launch for the tunnel CLI.
2
+ * portr install, config, and launch support for the tunnel runtime.
3
3
  *
4
- * Some hosts (Databricks Apps among them) mount the container's `$HOME` read-only
5
- * on cold start, so the portr binary and its config are placed under a writable,
6
- * cwd-rooted `.home` unconditionally - it costs nothing where `$HOME` is writable.
7
- * The install is idempotent (the installer skips when the on-PATH binary is
8
- * current). The config is rendered from `TUNNEL_PUBLIC_DOMAIN` (`<subdomain>.<server>`)
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 { spawn, spawnSync } from "node:child_process";
15
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
12
+ import { execFile, spawn } from "node:child_process";
13
+ import { 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 PORTR_MIN_VERSION = "v1.0.15";
25
+ const execFileAsync = promisify(execFile);
26
+
27
+ interface PortrRelease {
28
+ tag_name: string;
29
+ assets: Array<{ name: string; browser_download_url: string }>;
30
+ }
31
+
32
+ /** Options for installing the portr executable. */
33
+ export interface PortrInstallOptions {
34
+ /** Home directory containing `.portr`; defaults to the OS home directory. */
35
+ homeDir?: string;
36
+ }
21
37
 
22
38
  /** Resolved portr wiring, or `undefined` when no tunnel is configured. */
23
39
  export interface PortrConfig {
@@ -54,35 +70,65 @@ export function resolvePortrConfig(opts: {
54
70
  return { subdomain, server, token, port: opts.port };
55
71
  }
56
72
 
57
- /** The writable home portr installs + configures under (Apps `$HOME` is read-only). */
58
- function portrHome(): string {
59
- const home = join(process.cwd(), ".home");
60
- mkdirSync(join(home, ".portr", "bin"), { recursive: true });
61
- return home;
73
+ /** GitHub release asset name for the current or supplied OS/architecture. */
74
+ export function portrAssetName(
75
+ version: string,
76
+ platform: NodeJS.Platform = process.platform,
77
+ arch: string = process.arch,
78
+ ): string {
79
+ const osName = platform === "darwin" ? "Darwin" : platform === "linux" ? "Linux" : undefined;
80
+ const archName = arch === "arm64" ? "arm64" : arch === "x64" ? "x86_64" : undefined;
81
+ if (!osName || !archName) {
82
+ throw new Error(`portr has no supported release asset for ${platform}/${arch}`);
83
+ }
84
+ return `portr_${version}_${osName}_${archName}.zip`;
85
+ }
86
+
87
+ async function portrDownloadUrl(): Promise<string> {
88
+ const releaseResponse = await fetch(PORTR_LATEST_RELEASE_URL, {
89
+ headers: {
90
+ accept: "application/vnd.github+json",
91
+ "user-agent": "@dbx-tools/tunnel",
92
+ },
93
+ });
94
+ if (!releaseResponse.ok) {
95
+ throw new Error(`portr release lookup failed (${releaseResponse.status})`);
96
+ }
97
+ const release = (await releaseResponse.json()) as PortrRelease;
98
+ const version = release.tag_name.replace(/^v/, "");
99
+
100
+ const assetName = portrAssetName(version);
101
+ const asset = release.assets.find((candidate) => candidate.name === assetName);
102
+ if (!asset) throw new Error(`portr release ${release.tag_name} has no ${assetName}`);
103
+
104
+ logger.info("installing portr", { version, asset: assetName });
105
+ return asset.browser_download_url;
62
106
  }
63
107
 
64
- /** Install portr (idempotent) into the cwd-rooted home and return the child env. */
65
- export function installPortr(): NodeJS.ProcessEnv {
66
- const home = portrHome();
67
- const env: NodeJS.ProcessEnv = {
108
+ /** Install portr when absent and return the environment used by its process. */
109
+ export async function installPortr(options: PortrInstallOptions = {}): Promise<NodeJS.ProcessEnv> {
110
+ const homeDir = options.homeDir ?? os.homedir();
111
+ const context = await bin.ensure("portr", portrDownloadUrl, {
112
+ autoUnpackage: true,
113
+ homeDir,
114
+ minVersion: PORTR_MIN_VERSION,
115
+ selector: ({ source }) => join(source, "portr"),
116
+ });
117
+ return {
68
118
  ...process.env,
69
- HOME: home,
119
+ HOME: homeDir,
70
120
  PORTR_AUTO_ADD_PATH: "no",
71
- PATH: [join(home, ".portr", "bin"), process.env.PATH ?? ""].join(delimiter),
121
+ PATH: [context.binDir, process.env.PATH ?? ""].join(delimiter),
72
122
  };
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
123
  }
81
124
 
82
125
  /** Render `~/.portr/config.yaml` for the resolved tunnel. */
83
- export function writePortrConfig(config: PortrConfig, env: NodeJS.ProcessEnv): void {
84
- const path = join(env.HOME!, ".portr", "config.yaml");
85
- writeFileSync(
126
+ export async function writePortrConfig(
127
+ config: PortrConfig,
128
+ childEnv: NodeJS.ProcessEnv,
129
+ ): Promise<void> {
130
+ const path = join(childEnv.HOME ?? os.homedir(), ".portr", "config.yaml");
131
+ await writeFile(
86
132
  path,
87
133
  [
88
134
  `server_url: ${config.server}`,
@@ -100,14 +146,13 @@ export function writePortrConfig(config: PortrConfig, env: NodeJS.ProcessEnv): v
100
146
  }
101
147
 
102
148
  /** Launch `portr start` as a child process (caller supervises + kills it). */
103
- export function startPortr(config: PortrConfig, env: NodeJS.ProcessEnv): ReturnType<typeof spawn> {
149
+ export async function startPortr(
150
+ config: PortrConfig,
151
+ childEnv: NodeJS.ProcessEnv,
152
+ ): Promise<ReturnType<typeof spawn>> {
104
153
  // Reclaim the subdomain from any portr left by a previous boot in this container.
105
- spawnSync("pkill", ["-x", "portr"], { stdio: "ignore" });
106
- logger.info(`portr tunneling https://${config.subdomain}.${config.server} -> :${config.port}`);
107
- return spawn("portr", ["start"], { env, stdio: "inherit" });
108
- }
154
+ await execFileAsync("pkill", ["-x", "portr"]).catch(() => undefined);
109
155
 
110
- /** True when the installed portr binary path exists (post-install sanity). */
111
- export function portrInstalled(env: NodeJS.ProcessEnv): boolean {
112
- return existsSync(join(env.HOME!, ".portr", "bin", "portr"));
156
+ logger.info(`portr tunneling https://${config.subdomain}.${config.server} -> :${config.port}`);
157
+ return spawn("portr", ["start"], { env: childEnv, stdio: "inherit" });
113
158
  }