perplexity-user-mcp 0.8.36

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.
Files changed (125) hide show
  1. package/README.md +192 -0
  2. package/dist/attachments.d.ts +20 -0
  3. package/dist/attachments.mjs +43 -0
  4. package/dist/checks/browser.d.ts +100 -0
  5. package/dist/checks/browser.mjs +89 -0
  6. package/dist/checks/config.d.ts +91 -0
  7. package/dist/checks/config.mjs +88 -0
  8. package/dist/checks/ide.d.ts +89 -0
  9. package/dist/checks/ide.mjs +80 -0
  10. package/dist/checks/mcp.d.ts +61 -0
  11. package/dist/checks/mcp.mjs +56 -0
  12. package/dist/checks/native-deps.d.ts +131 -0
  13. package/dist/checks/native-deps.mjs +115 -0
  14. package/dist/checks/network.d.ts +71 -0
  15. package/dist/checks/network.mjs +70 -0
  16. package/dist/checks/probe.d.ts +93 -0
  17. package/dist/checks/probe.mjs +82 -0
  18. package/dist/checks/profiles.d.ts +99 -0
  19. package/dist/checks/profiles.mjs +90 -0
  20. package/dist/checks/runtime.d.ts +89 -0
  21. package/dist/checks/runtime.mjs +90 -0
  22. package/dist/checks/vault.d.ts +101 -0
  23. package/dist/checks/vault.mjs +90 -0
  24. package/dist/chunk-3B276PGG.mjs +115 -0
  25. package/dist/chunk-4UEJOM6W.mjs +9 -0
  26. package/dist/chunk-6EP2BLTV.mjs +205 -0
  27. package/dist/chunk-6YMQVLFX.mjs +146 -0
  28. package/dist/chunk-7JL36EBH.mjs +118 -0
  29. package/dist/chunk-DPGMKSSA.mjs +57 -0
  30. package/dist/chunk-H4BUAPPO.mjs +1950 -0
  31. package/dist/chunk-HMKLWVXB.mjs +109 -0
  32. package/dist/chunk-HTUAQRKH.mjs +125 -0
  33. package/dist/chunk-HU5B4FXS.mjs +139 -0
  34. package/dist/chunk-KCXM2M4B.mjs +1006 -0
  35. package/dist/chunk-LKJMLGFP.mjs +237 -0
  36. package/dist/chunk-LZPLNZ5U.mjs +67 -0
  37. package/dist/chunk-MTDFKNXX.mjs +19 -0
  38. package/dist/chunk-OF4DMAPJ.mjs +511 -0
  39. package/dist/chunk-PE23RMXY.mjs +43 -0
  40. package/dist/chunk-Q2VY4R5F.mjs +175 -0
  41. package/dist/chunk-S5VD7WTU.mjs +2540 -0
  42. package/dist/chunk-SVPRB62V.mjs +106 -0
  43. package/dist/chunk-TQLCLE4L.mjs +345 -0
  44. package/dist/chunk-U3DGFLXZ.mjs +43 -0
  45. package/dist/chunk-X45O6YD3.mjs +688 -0
  46. package/dist/chunk-XKSWCEGI.mjs +168 -0
  47. package/dist/chunk-Z7DAACGZ.mjs +534 -0
  48. package/dist/chunk-ZQFUZPLO.mjs +257 -0
  49. package/dist/cli.d.ts +952 -0
  50. package/dist/cli.mjs +827 -0
  51. package/dist/client.d.ts +355 -0
  52. package/dist/client.mjs +27 -0
  53. package/dist/cloud-sync.d-Cqt6y18U.d.ts +42 -0
  54. package/dist/cloud-sync.d.ts +42 -0
  55. package/dist/cloud-sync.mjs +17 -0
  56. package/dist/config.d.ts +186 -0
  57. package/dist/config.mjs +54 -0
  58. package/dist/daemon/attach.d.ts +36 -0
  59. package/dist/daemon/attach.mjs +25 -0
  60. package/dist/daemon/audit.d.ts +23 -0
  61. package/dist/daemon/audit.mjs +12 -0
  62. package/dist/daemon/client-http.d.ts +42 -0
  63. package/dist/daemon/client-http.mjs +29 -0
  64. package/dist/daemon/index.d.ts +14 -0
  65. package/dist/daemon/index.mjs +110 -0
  66. package/dist/daemon/install-tunnel.d.ts +46 -0
  67. package/dist/daemon/install-tunnel.mjs +14 -0
  68. package/dist/daemon/launcher.d.ts +163 -0
  69. package/dist/daemon/launcher.mjs +50 -0
  70. package/dist/daemon/lockfile.d.ts +29 -0
  71. package/dist/daemon/lockfile.mjs +18 -0
  72. package/dist/daemon/server.d.ts +159 -0
  73. package/dist/daemon/server.mjs +20 -0
  74. package/dist/daemon/token.d.ts +17 -0
  75. package/dist/daemon/token.mjs +17 -0
  76. package/dist/daemon/tunnel-providers/index.d.ts +330 -0
  77. package/dist/daemon/tunnel-providers/index.mjs +57 -0
  78. package/dist/daemon/tunnel.d.ts +23 -0
  79. package/dist/daemon/tunnel.mjs +9 -0
  80. package/dist/doctor-report.d.ts +24 -0
  81. package/dist/doctor-report.mjs +14 -0
  82. package/dist/doctor.d-CXmUqOXX.d.ts +43 -0
  83. package/dist/doctor.d.ts +44 -0
  84. package/dist/doctor.mjs +16 -0
  85. package/dist/export.d.ts +19 -0
  86. package/dist/export.mjs +15 -0
  87. package/dist/health-check.d.ts +108 -0
  88. package/dist/health-check.mjs +92 -0
  89. package/dist/history-store.d-BzjBF2m3.d.ts +65 -0
  90. package/dist/history-store.d.ts +65 -0
  91. package/dist/history-store.mjs +48 -0
  92. package/dist/impit-login-runner.d.ts +469 -0
  93. package/dist/impit-login-runner.mjs +685 -0
  94. package/dist/index.d.ts +159 -0
  95. package/dist/index.mjs +236 -0
  96. package/dist/login-runner.d.ts +333 -0
  97. package/dist/login-runner.mjs +320 -0
  98. package/dist/logout.d.ts +28 -0
  99. package/dist/logout.mjs +45 -0
  100. package/dist/manual-login-runner.d.ts +150 -0
  101. package/dist/manual-login-runner.mjs +146 -0
  102. package/dist/native-deps-BNThFHxa.d.ts +175 -0
  103. package/dist/native-deps-YNKXITRY.mjs +139 -0
  104. package/dist/profiles.d-DqS1oZWr.d.ts +41 -0
  105. package/dist/profiles.d.ts +41 -0
  106. package/dist/profiles.mjs +33 -0
  107. package/dist/redact.d.ts +159 -0
  108. package/dist/redact.mjs +11 -0
  109. package/dist/refresh.d.ts +118 -0
  110. package/dist/refresh.mjs +21 -0
  111. package/dist/reinit-watcher.d.ts +15 -0
  112. package/dist/reinit-watcher.mjs +8 -0
  113. package/dist/session-metadata-B9aV_n5g.d.ts +148 -0
  114. package/dist/tty-prompt.d.ts +44 -0
  115. package/dist/tty-prompt.mjs +39 -0
  116. package/dist/vault.d-BtRSLZiM.d.ts +8 -0
  117. package/dist/vault.d.ts +37 -0
  118. package/dist/vault.mjs +21 -0
  119. package/dist/viewer-detect.d-HWGnyFAA.d.ts +4 -0
  120. package/dist/viewer-detect.d.ts +4 -0
  121. package/dist/viewer-detect.mjs +37 -0
  122. package/dist/viewers.d-BGCK6sw6.d.ts +10 -0
  123. package/dist/viewers.d.ts +18 -0
  124. package/dist/viewers.mjs +122 -0
  125. package/package.json +152 -0
@@ -0,0 +1,159 @@
1
+ import { RequestLike } from 'express';
2
+ import { PerplexityClient } from '../client.js';
3
+ import { readAuditTail } from './audit.js';
4
+ import { DaemonTokenRecord } from './token.js';
5
+ import '../config.js';
6
+ import 'patchright';
7
+
8
+ /**
9
+ * OAuth consent cache — remembers per-(clientId, redirectUri, resource)
10
+ * consents so the user isn't prompted every time Claude Desktop / Cursor /
11
+ * Cline refreshes an access token (which happens on a ~1h cycle).
12
+ *
13
+ * Storage: <configDir>/oauth-consent.json, 0600.
14
+ * Entries carry an absolute `expiresAt` (ms since epoch). On each check
15
+ * we lazily prune expired entries. Deleting a record is synchronous and
16
+ * best-effort (if the file is locked we lose the mutation; next write
17
+ * will overwrite it).
18
+ *
19
+ * Key structure: `(clientId, redirectUri, resource)`. After Phase 8.2 H12
20
+ * the OAuth `resource` parameter (RFC 8707) is part of the audience binding,
21
+ * so a consent granted for `https://tunnel-a.example/mcp` must NOT
22
+ * auto-approve a new authorize request for `https://tunnel-b.example/mcp`
23
+ * even though the clientId + redirectUri match. `resource === undefined` is
24
+ * a distinct key from any bound resource string — unbound consents (legacy
25
+ * / loopback clients that don't send the `resource` param) don't
26
+ * auto-approve bound-resource consents and vice-versa.
27
+ *
28
+ * Revoking a consent entry does NOT invalidate already-issued access
29
+ * tokens — those live 1h anyway; revoking the CLIENT (see
30
+ * oauth-provider.revokeClient) is the way to invalidate outstanding
31
+ * tokens. Phase 8.2's dashboard panel composes these two.
32
+ */
33
+ interface ConsentEntry {
34
+ clientId: string;
35
+ redirectUri: string;
36
+ approvedAt: string;
37
+ expiresAt: number;
38
+ /**
39
+ * RFC 8707 resource binding. `undefined` = unbound (legacy / loopback
40
+ * clients that don't send the `resource` param at /authorize). Two entries
41
+ * with the same (clientId, redirectUri) but different `resource` values
42
+ * (including undefined-vs-string) are DISTINCT cache keys.
43
+ */
44
+ resource?: string;
45
+ }
46
+
47
+ /**
48
+ * OAuth 2.1 authorization-server provider for the Perplexity MCP daemon.
49
+ *
50
+ * Implements the MCP SDK's `OAuthServerProvider` interface. Plugs into
51
+ * `mcpAuthRouter()` to expose `/authorize`, `/token`, `/register`, `/revoke`
52
+ * and the `/.well-known/*` metadata endpoints.
53
+ *
54
+ * Design:
55
+ * - Clients are persisted in `<configDir>/oauth-clients.json` (0600).
56
+ * - Authorization codes are in-memory, 2-min TTL, single-use.
57
+ * - Access tokens are in-memory, 1-hour TTL.
58
+ * - Refresh tokens rotate on each exchange.
59
+ * - `verifyAccessToken` accepts either a valid OAuth access token OR the
60
+ * daemon's static bearer, so existing loopback callers (extension host
61
+ * + CLI) keep working with no changes.
62
+ * - `authorize()` defers to a caller-supplied `requestConsent` callback
63
+ * which the daemon wires to the VS Code extension's modal. Until the
64
+ * user approves or the 2-min timeout elapses, the HTTP response is
65
+ * held open.
66
+ */
67
+
68
+ interface AuthorizedClientSummary {
69
+ clientId: string;
70
+ clientName?: string;
71
+ registeredAt: number;
72
+ lastUsedAt?: string;
73
+ consentLastApprovedAt?: string;
74
+ activeTokens: number;
75
+ }
76
+
77
+ type EventPayload = Record<string, unknown>;
78
+ interface DaemonTunnelHealth {
79
+ status: "disabled" | "starting" | "enabled" | "crashed";
80
+ url: string | null;
81
+ pid?: number | null;
82
+ error?: string | null;
83
+ }
84
+ interface StartDaemonServerOptions {
85
+ host?: string;
86
+ port?: number;
87
+ uuid?: string;
88
+ version?: string;
89
+ bearerToken?: string;
90
+ configDir?: string;
91
+ createClient?: () => PerplexityClient;
92
+ onShutdown?: () => Promise<void> | void;
93
+ onTokenRotated?: (token: DaemonTokenRecord) => Promise<void> | void;
94
+ getTunnelState?: () => DaemonTunnelHealth;
95
+ onEnableTunnel?: () => Promise<void> | void;
96
+ onDisableTunnel?: () => Promise<void> | void;
97
+ onTunnelAutoDisable?: (info: {
98
+ failures: number;
99
+ windowMs: number;
100
+ }) => Promise<void> | void;
101
+ /**
102
+ * Called when an MCP client hits `/authorize` and we need the local user
103
+ * to approve the consent. Host (the VS Code extension) resolves true to
104
+ * approve, false to deny. Called with a fresh consent id that the host
105
+ * posts back to `/daemon/oauth-consent` with its decision.
106
+ */
107
+ onOAuthConsentRequest?: (info: {
108
+ consentId: string;
109
+ clientId: string;
110
+ clientName: string;
111
+ redirectUri: string;
112
+ /**
113
+ * RFC 8707 resource the authorize request is targeting. `undefined` when
114
+ * the client did not include a `resource` param (legacy / loopback).
115
+ * The extension-host modal MUST display this when present so users can
116
+ * spot cross-resource replay attempts at consent time.
117
+ */
118
+ resource?: string;
119
+ }) => Promise<void> | void;
120
+ /** When tunnel is enabled we advertise this as the OAuth issuer. */
121
+ getTunnelUrl?: () => string | null;
122
+ }
123
+ interface StartedDaemonServer {
124
+ host: string;
125
+ port: number;
126
+ url: string;
127
+ bearerToken: string;
128
+ auditPath: string;
129
+ tokenPath: string;
130
+ close: () => Promise<void>;
131
+ publishEvent: (event: string, payload: EventPayload) => void;
132
+ getHealth: () => Record<string, unknown>;
133
+ readAuditTail: (limit?: number) => ReturnType<typeof readAuditTail>;
134
+ /** Returns registered OAuth clients with their current token counts. */
135
+ listOAuthClients: () => AuthorizedClientSummary[];
136
+ /** Deletes an OAuth client and all its outstanding tokens. */
137
+ revokeOAuthClient: (clientId: string) => boolean;
138
+ /** Deletes every registered OAuth client and invalidates all outstanding tokens. */
139
+ revokeAllOAuthClients: () => number;
140
+ /** Extension host resolves a pending /authorize consent. */
141
+ resolveOAuthConsent: (consentId: string, approved: boolean) => boolean;
142
+ /** Live non-expired cached consents. */
143
+ listOAuthConsents: () => ConsentEntry[];
144
+ /** Revoke cached consents. No args → revoke all. */
145
+ revokeOAuthConsents: (filter?: {
146
+ clientId?: string;
147
+ redirectUri?: string;
148
+ }) => number;
149
+ }
150
+ declare function startDaemonServer(options?: StartDaemonServerOptions): Promise<StartedDaemonServer>;
151
+ /**
152
+ * Canonicalize the expected resource identifier (RFC 8707) for this
153
+ * request. Returns `<scheme>://<host>/mcp` with no trailing slash so the
154
+ * PRM endpoint, the /authorize→/token binding, and verifyAccessToken's
155
+ * expectedResource all agree on a single form.
156
+ */
157
+ declare function resolveRequestResource(req: RequestLike, fallback?: URL): string;
158
+
159
+ export { type DaemonTunnelHealth, type StartDaemonServerOptions, type StartedDaemonServer, resolveRequestResource, startDaemonServer };
@@ -0,0 +1,20 @@
1
+ import {
2
+ resolveRequestResource,
3
+ startDaemonServer
4
+ } from "../chunk-S5VD7WTU.mjs";
5
+ import "../chunk-PE23RMXY.mjs";
6
+ import "../chunk-HTUAQRKH.mjs";
7
+ import "../chunk-Q2VY4R5F.mjs";
8
+ import "../chunk-H4BUAPPO.mjs";
9
+ import "../chunk-Z7DAACGZ.mjs";
10
+ import "../chunk-OF4DMAPJ.mjs";
11
+ import "../chunk-LZPLNZ5U.mjs";
12
+ import "../chunk-LKJMLGFP.mjs";
13
+ import "../chunk-TQLCLE4L.mjs";
14
+ import "../chunk-MTDFKNXX.mjs";
15
+ import "../chunk-XKSWCEGI.mjs";
16
+ import "../chunk-4UEJOM6W.mjs";
17
+ export {
18
+ resolveRequestResource,
19
+ startDaemonServer
20
+ };
@@ -0,0 +1,17 @@
1
+ interface DaemonTokenRecord {
2
+ bearerToken: string;
3
+ version: number;
4
+ createdAt: string;
5
+ rotatedAt: string;
6
+ }
7
+ interface TokenOptions {
8
+ tokenPath?: string;
9
+ now?: () => string;
10
+ }
11
+ declare function getTokenPath(configDir?: string): string;
12
+ declare function generateBearerToken(): string;
13
+ declare function readToken(options?: TokenOptions): DaemonTokenRecord | null;
14
+ declare function ensureToken(options?: TokenOptions): DaemonTokenRecord;
15
+ declare function rotateToken(options?: TokenOptions): DaemonTokenRecord;
16
+
17
+ export { type DaemonTokenRecord, type TokenOptions, ensureToken, generateBearerToken, getTokenPath, readToken, rotateToken };
@@ -0,0 +1,17 @@
1
+ import {
2
+ ensureToken,
3
+ generateBearerToken,
4
+ getTokenPath,
5
+ readToken,
6
+ rotateToken
7
+ } from "../chunk-HTUAQRKH.mjs";
8
+ import "../chunk-MTDFKNXX.mjs";
9
+ import "../chunk-XKSWCEGI.mjs";
10
+ import "../chunk-4UEJOM6W.mjs";
11
+ export {
12
+ ensureToken,
13
+ generateBearerToken,
14
+ getTokenPath,
15
+ readToken,
16
+ rotateToken
17
+ };
@@ -0,0 +1,330 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { TunnelState, StartedTunnel } from '../tunnel.js';
3
+
4
+ type TunnelProviderId = "cf-quick" | "ngrok" | "cf-named";
5
+ interface TunnelProviderStartOptions {
6
+ port: number;
7
+ configDir: string;
8
+ onStateChange: (state: TunnelState) => void;
9
+ }
10
+ interface SetupCheck {
11
+ ready: boolean;
12
+ /** User-facing reason the provider isn't ready (e.g. "ngrok authtoken not set"). */
13
+ reason?: string;
14
+ /** Optional action hint the dashboard can surface.
15
+ *
16
+ * Kinds:
17
+ * - "open-url": UI opens `url` in the default browser.
18
+ * - "input-authtoken": UI surfaces a text input and posts
19
+ * `daemon:set-ngrok-authtoken` back.
20
+ * - "install-binary": UI runs the known install-binary flow
21
+ * (`daemon:install-cloudflared` for cf-named / cf-quick today).
22
+ * - "run-command": UI dispatches the webview message type identified
23
+ * by `command` (e.g. `"cf-named-login"` → `daemon:cf-named-login`).
24
+ * Preferred over "open-url" when the action has no URL and needs
25
+ * a modal-confirmed host-side handler.
26
+ */
27
+ action?: {
28
+ label: string;
29
+ kind: "open-url" | "input-authtoken" | "install-binary" | "run-command";
30
+ url?: string;
31
+ /** Opaque identifier for the command to run (used with kind: "run-command"). */
32
+ command?: string;
33
+ };
34
+ }
35
+ interface TunnelProvider {
36
+ readonly id: TunnelProviderId;
37
+ readonly displayName: string;
38
+ /** Brief description surfaced next to the provider in the dashboard picker. */
39
+ readonly description: string;
40
+ /**
41
+ * Returns whether the provider has everything it needs to start a tunnel.
42
+ * Called before start(); the dashboard also calls this to render a setup widget.
43
+ */
44
+ isSetupComplete(configDir: string): Promise<SetupCheck>;
45
+ /**
46
+ * Start the tunnel. Must resolve once the public URL is known OR reject if
47
+ * setup is missing / start fails. onStateChange fires on every state
48
+ * transition including "starting" and "enabled".
49
+ */
50
+ start(options: TunnelProviderStartOptions): Promise<StartedTunnel>;
51
+ }
52
+ interface TunnelProviderStatus {
53
+ id: TunnelProviderId;
54
+ displayName: string;
55
+ description: string;
56
+ setup: SetupCheck;
57
+ isActive: boolean;
58
+ }
59
+
60
+ /**
61
+ * Cloudflared named-tunnel provider.
62
+ *
63
+ * Runs a persistent Cloudflare tunnel that routes a user-owned hostname
64
+ * (e.g. https://mcp.example.com) to the local daemon HTTP port. Unlike
65
+ * cloudflared-quick, the URL is stable across restarts.
66
+ *
67
+ * Setup (one-time, handled by 8.4.3 UI + 8.4.4 CLI) produces:
68
+ * - ~/.cloudflared/cert.pem (origin cert from `cloudflared login`)
69
+ * - ~/.cloudflared/<uuid>.json (tunnel credentials, written by create)
70
+ * - <configDir>/cloudflared-named.yml (managed ingress config we serialize)
71
+ *
72
+ * Port-drift rewrite (load-bearing): the managed YAML embeds the daemon's
73
+ * loopback port. The daemon picks a fresh OS-assigned port on most restarts,
74
+ * so the persisted port is almost always stale by the time start() runs. We
75
+ * rewrite the managed YAML with the current port on every start() — idempotent
76
+ * atomic writes are cheap, forgetting the rewrite routes cloudflared to a
77
+ * dead port.
78
+ *
79
+ * The managed YAML is treated as provider-owned — hand-edits to add extra
80
+ * ingress rules WILL be silently dropped on the next start() because we
81
+ * serialize only the four canonical keys (tunnel / credentials-file /
82
+ * hostname / service). Warning on drift is deferred to 8.4.3.
83
+ */
84
+
85
+ type SpawnFn$1 = typeof spawn;
86
+ interface ProviderDependencies {
87
+ spawn?: SpawnFn$1;
88
+ homedir?: () => string;
89
+ }
90
+ /**
91
+ * Build a provider bound to a specific dependency set. The exported
92
+ * singleton uses node defaults; tests construct a one-off via
93
+ * createCloudflaredNamedProvider({ dependencies: { spawn: fakeSpawn } }).
94
+ */
95
+ declare function createCloudflaredNamedProvider(options?: {
96
+ dependencies?: ProviderDependencies;
97
+ }): TunnelProvider;
98
+
99
+ /**
100
+ * ngrok tunnel provider.
101
+ *
102
+ * Uses the official `@ngrok/ngrok` NAPI binding — the tunnel runs in-process
103
+ * so there's no binary to download and no child process to manage. The ngrok
104
+ * account authtoken and (optional) reserved domain live in <configDir>/ngrok.json.
105
+ *
106
+ * Free-tier ngrok includes one reserved static domain (yourname.ngrok-free.app)
107
+ * which persists across daemon restarts; callers who leave `domain` unset get
108
+ * an ephemeral URL that changes on each start.
109
+ *
110
+ * NOTE ON LAZY NATIVE LOADING (0.8.6):
111
+ * `@ngrok/ngrok` ships platform-specific NAPI subpackages
112
+ * (`@ngrok/ngrok-linux-x64-gnu`, `@ngrok/ngrok-win32-x64-msvc`, …) that are
113
+ * resolved at module-load time. If the VSIX was packaged on a different OS
114
+ * than the one activating the extension, the required subpackage may be
115
+ * missing and `require("@ngrok/ngrok")` throws MODULE_NOT_FOUND. That used to
116
+ * crash extension activation because the provider registry statically
117
+ * imported this file. We now defer loading the NAPI binding until a caller
118
+ * actually needs it (start / kill), and surface a domain-specific
119
+ * `NgrokNativeMissingError` so the dashboard can show a useful message.
120
+ */
121
+
122
+ /**
123
+ * Error thrown when the `@ngrok/ngrok` native subpackage for the current
124
+ * platform/arch isn't installed. Callers (dashboard / CLI) should surface the
125
+ * message to the user instead of letting a raw MODULE_NOT_FOUND propagate.
126
+ */
127
+ declare class NgrokNativeMissingError extends Error {
128
+ readonly platform: string;
129
+ readonly arch: string;
130
+ readonly cause?: unknown;
131
+ constructor(cause: unknown);
132
+ }
133
+ interface NgrokModule {
134
+ forward: (options: Record<string, unknown>) => Promise<NgrokListener>;
135
+ kill?: () => Promise<void> | void;
136
+ }
137
+ interface NgrokListener {
138
+ url: () => string | undefined | null;
139
+ close: () => Promise<void> | void;
140
+ }
141
+ /**
142
+ * Lazily import `@ngrok/ngrok`. Cached on first success. On
143
+ * MODULE_NOT_FOUND for either the umbrella package or its platform subpackage,
144
+ * throws `NgrokNativeMissingError`; every other error propagates as-is so we
145
+ * don't swallow genuine bugs.
146
+ */
147
+ declare function loadNgrokNative(): Promise<NgrokModule>;
148
+ /**
149
+ * Probe whether the native binding would load on this platform, without
150
+ * actually keeping it cached. Used by `listTunnelProviderStatuses` so we can
151
+ * surface a `native-missing` setup reason without crashing.
152
+ */
153
+ declare function isNgrokNativeAvailable(): Promise<{
154
+ available: true;
155
+ } | {
156
+ available: false;
157
+ error: NgrokNativeMissingError;
158
+ }>;
159
+
160
+ /**
161
+ * Persistence for ngrok credentials.
162
+ *
163
+ * Stored at `<configDir>/ngrok.json` with file mode 0600 (POSIX) or
164
+ * user-only ACL (Windows). Mirrors the token.ts safety pattern.
165
+ */
166
+ interface NgrokSettings {
167
+ authtoken: string;
168
+ domain?: string;
169
+ updatedAt: string;
170
+ }
171
+ declare function getNgrokConfigPath(configDir: string): string;
172
+ declare function readNgrokSettings(configDir: string): NgrokSettings | null;
173
+ declare function writeNgrokSettings(configDir: string, next: {
174
+ authtoken?: string;
175
+ domain?: string | null;
176
+ }): NgrokSettings;
177
+ declare function clearNgrokSettings(configDir: string): void;
178
+
179
+ /**
180
+ * Cloudflared named-tunnel setup helpers.
181
+ *
182
+ * Wraps the `cloudflared` CLI to drive a persistent (named) tunnel. Named
183
+ * tunnels require an origin cert (`~/.cloudflared/cert.pem`), a tunnel UUID
184
+ * + credentials file, and a YAML config that maps a hostname -> local port.
185
+ *
186
+ * This module ships the setup primitives only; provider registration lives in
187
+ * cloudflared-named.ts (Phase 8.4.2). The dashboard/CLI call these helpers
188
+ * during the one-time setup flow.
189
+ */
190
+
191
+ /**
192
+ * Minimal spawn signature we rely on — matches the three-arg overload of
193
+ * `node:child_process.spawn` used for piping stdio and collecting output.
194
+ * Declared as a type alias so tests can inject a fake implementation.
195
+ */
196
+ type SpawnFn = typeof spawn;
197
+ interface CloudflaredLoginResult {
198
+ ok: boolean;
199
+ certPath: string;
200
+ stderr?: string;
201
+ }
202
+ interface NamedTunnelSummary {
203
+ /** cloudflared's tunnel UUID. */
204
+ uuid: string;
205
+ /** Human-readable name. */
206
+ name: string;
207
+ createdAt?: string;
208
+ connections?: number;
209
+ }
210
+ interface CreatedTunnel extends NamedTunnelSummary {
211
+ /** Path to the credentials JSON cloudflared wrote. */
212
+ credentialsPath: string;
213
+ }
214
+ interface NamedTunnelConfig {
215
+ uuid: string;
216
+ /** e.g. "mcp.example.com" */
217
+ hostname: string;
218
+ /** Local daemon HTTP port. */
219
+ port: number;
220
+ /** Full path to the written .yml. */
221
+ configPath: string;
222
+ credentialsPath: string;
223
+ }
224
+ interface DeletedNamedTunnel {
225
+ uuid: string;
226
+ }
227
+ type DeleteNamedTunnelFailureReason = "active-connections" | "unknown";
228
+ declare class DeleteNamedTunnelError extends Error {
229
+ readonly reason: DeleteNamedTunnelFailureReason;
230
+ constructor(message: string, reason: DeleteNamedTunnelFailureReason);
231
+ }
232
+ /**
233
+ * Runs `cloudflared tunnel login`. Opens a browser. Blocks until the cert
234
+ * lands at `~/.cloudflared/cert.pem` (or throws on timeout / abort). The
235
+ * login subprocess is best-effort terminated on resolve/reject.
236
+ */
237
+ declare function runCloudflaredLogin(options: {
238
+ configDir: string;
239
+ binaryPath?: string;
240
+ signal?: AbortSignal;
241
+ timeoutMs?: number;
242
+ /** Override the cert watch location (defaults to `$HOME/.cloudflared/cert.pem`). */
243
+ certPath?: string;
244
+ /**
245
+ * When true, pipe the cloudflared child's stderr AND stdout to the parent
246
+ * process's stderr so CLI users see the "open this URL in your browser"
247
+ * prompt (some cloudflared builds emit it on stdout, others on stderr).
248
+ * Never forwards to parent stdout — the CLI reserves stdout for --json
249
+ * machine-readable output. Default false (test hermeticity + dashboard
250
+ * flow that wraps output in notifications instead).
251
+ */
252
+ forwardOutput?: boolean;
253
+ /** Test-only dependency injection seam. */
254
+ dependencies?: {
255
+ spawn?: SpawnFn;
256
+ };
257
+ }): Promise<CloudflaredLoginResult>;
258
+ /**
259
+ * Runs `cloudflared tunnel list --output=json` and parses. Returns [] on
260
+ * "no tunnels" (exit 0 + empty list). Throws on binary/cert problems.
261
+ */
262
+ declare function listNamedTunnels(options: {
263
+ configDir: string;
264
+ binaryPath?: string;
265
+ dependencies?: {
266
+ spawn?: SpawnFn;
267
+ };
268
+ }): Promise<NamedTunnelSummary[]>;
269
+ /**
270
+ * Runs `cloudflared tunnel create <name>`, parses the UUID + credentials
271
+ * path out of stdout, then runs `cloudflared tunnel route dns <uuid>
272
+ * <hostname>` to install the CNAME record.
273
+ */
274
+ declare function createNamedTunnel(options: {
275
+ configDir: string;
276
+ name: string;
277
+ hostname: string;
278
+ binaryPath?: string;
279
+ signal?: AbortSignal;
280
+ dependencies?: {
281
+ spawn?: SpawnFn;
282
+ };
283
+ }): Promise<CreatedTunnel>;
284
+ /**
285
+ * Deletes a remote Cloudflare named tunnel. This is intentionally separate
286
+ * from clearNamedTunnelConfig(): remote delete can fail even with --force
287
+ * when DNS still routes traffic and active connections keep the tunnel alive.
288
+ */
289
+ declare function deleteNamedTunnel(options: {
290
+ configDir: string;
291
+ uuid: string;
292
+ binaryPath?: string;
293
+ signal?: AbortSignal;
294
+ dependencies?: {
295
+ spawn?: SpawnFn;
296
+ };
297
+ }): Promise<DeletedNamedTunnel>;
298
+ declare function isActiveConnectionDeleteFailure(output: string): boolean;
299
+ declare function getNamedTunnelConfigPath(configDir: string): string;
300
+ /**
301
+ * Writes `<configDir>/cloudflared-named.yml` describing the tunnel ->
302
+ * localhost mapping. Uses the temp-file + rename pattern from ngrok-config.ts
303
+ * and locks file mode to 0600 (POSIX) / user-only ACL (Windows).
304
+ */
305
+ declare function writeTunnelConfig(options: {
306
+ configDir: string;
307
+ uuid: string;
308
+ hostname: string;
309
+ port: number;
310
+ credentialsPath: string;
311
+ }): NamedTunnelConfig;
312
+ /**
313
+ * Reads + validates the config written by writeTunnelConfig. Returns null
314
+ * if absent or malformed.
315
+ */
316
+ declare function readNamedTunnelConfig(configDir: string): NamedTunnelConfig | null;
317
+ declare function clearNamedTunnelConfig(configDir: string): boolean;
318
+
319
+ declare function getTunnelProvider(id: TunnelProviderId): TunnelProvider;
320
+ declare function listTunnelProviders(): TunnelProvider[];
321
+ interface TunnelSettings {
322
+ activeProvider: TunnelProviderId;
323
+ updatedAt: string;
324
+ }
325
+ declare function getTunnelSettingsPath(configDir: string): string;
326
+ declare function readTunnelSettings(configDir: string): TunnelSettings;
327
+ declare function writeTunnelSettings(configDir: string, patch: Partial<TunnelSettings>): TunnelSettings;
328
+ declare function listTunnelProviderStatuses(configDir: string): Promise<TunnelProviderStatus[]>;
329
+
330
+ export { type CloudflaredLoginResult, type CreatedTunnel, DeleteNamedTunnelError, type DeleteNamedTunnelFailureReason, type DeletedNamedTunnel, type NamedTunnelConfig, type NamedTunnelSummary, NgrokNativeMissingError, type NgrokSettings, type SetupCheck, type TunnelProvider, type TunnelProviderId, type TunnelProviderStatus, type TunnelSettings, clearNamedTunnelConfig, clearNgrokSettings, createCloudflaredNamedProvider, createNamedTunnel, deleteNamedTunnel, getNamedTunnelConfigPath, getNgrokConfigPath, getTunnelProvider, getTunnelSettingsPath, isActiveConnectionDeleteFailure, isNgrokNativeAvailable, listNamedTunnels, listTunnelProviderStatuses, listTunnelProviders, loadNgrokNative, readNamedTunnelConfig, readNgrokSettings, readTunnelSettings, runCloudflaredLogin, writeNgrokSettings, writeTunnelConfig, writeTunnelSettings };
@@ -0,0 +1,57 @@
1
+ import {
2
+ DeleteNamedTunnelError,
3
+ NgrokNativeMissingError,
4
+ clearNamedTunnelConfig,
5
+ clearNgrokSettings,
6
+ createCloudflaredNamedProvider,
7
+ createNamedTunnel,
8
+ deleteNamedTunnel,
9
+ getNamedTunnelConfigPath,
10
+ getNgrokConfigPath,
11
+ getTunnelProvider,
12
+ getTunnelSettingsPath,
13
+ isActiveConnectionDeleteFailure,
14
+ isNgrokNativeAvailable,
15
+ listNamedTunnels,
16
+ listTunnelProviderStatuses,
17
+ listTunnelProviders,
18
+ loadNgrokNative,
19
+ readNamedTunnelConfig,
20
+ readNgrokSettings,
21
+ readTunnelSettings,
22
+ runCloudflaredLogin,
23
+ writeNgrokSettings,
24
+ writeTunnelConfig,
25
+ writeTunnelSettings
26
+ } from "../../chunk-KCXM2M4B.mjs";
27
+ import "../../chunk-6YMQVLFX.mjs";
28
+ import "../../chunk-3B276PGG.mjs";
29
+ import "../../chunk-MTDFKNXX.mjs";
30
+ import "../../chunk-XKSWCEGI.mjs";
31
+ import "../../chunk-4UEJOM6W.mjs";
32
+ export {
33
+ DeleteNamedTunnelError,
34
+ NgrokNativeMissingError,
35
+ clearNamedTunnelConfig,
36
+ clearNgrokSettings,
37
+ createCloudflaredNamedProvider,
38
+ createNamedTunnel,
39
+ deleteNamedTunnel,
40
+ getNamedTunnelConfigPath,
41
+ getNgrokConfigPath,
42
+ getTunnelProvider,
43
+ getTunnelSettingsPath,
44
+ isActiveConnectionDeleteFailure,
45
+ isNgrokNativeAvailable,
46
+ listNamedTunnels,
47
+ listTunnelProviderStatuses,
48
+ listTunnelProviders,
49
+ loadNgrokNative,
50
+ readNamedTunnelConfig,
51
+ readNgrokSettings,
52
+ readTunnelSettings,
53
+ runCloudflaredLogin,
54
+ writeNgrokSettings,
55
+ writeTunnelConfig,
56
+ writeTunnelSettings
57
+ };
@@ -0,0 +1,23 @@
1
+ interface TunnelState {
2
+ status: "starting" | "enabled" | "disabled" | "crashed";
3
+ url: string | null;
4
+ pid: number | null;
5
+ error?: string | null;
6
+ }
7
+ interface StartTunnelOptions {
8
+ command: string;
9
+ args?: string[];
10
+ port: number;
11
+ env?: NodeJS.ProcessEnv;
12
+ onStateChange?: (state: TunnelState) => void;
13
+ }
14
+ interface StartedTunnel {
15
+ pid: number;
16
+ waitUntilReady: Promise<string>;
17
+ stop: () => Promise<void>;
18
+ getState: () => TunnelState;
19
+ }
20
+ declare function startTunnel(options: StartTunnelOptions): StartedTunnel;
21
+ declare function extractTunnelUrl(line: string): string | null;
22
+
23
+ export { type StartTunnelOptions, type StartedTunnel, type TunnelState, extractTunnelUrl, startTunnel };
@@ -0,0 +1,9 @@
1
+ import {
2
+ extractTunnelUrl,
3
+ startTunnel
4
+ } from "../chunk-6YMQVLFX.mjs";
5
+ import "../chunk-4UEJOM6W.mjs";
6
+ export {
7
+ extractTunnelUrl,
8
+ startTunnel
9
+ };
@@ -0,0 +1,24 @@
1
+ import { DoctorReport, DoctorCategory } from './doctor.d-CXmUqOXX.js';
2
+
3
+ declare function buildIssueBody(input: {
4
+ report: DoctorReport;
5
+ stderrTail: string;
6
+ extVersion: string;
7
+ nodeVersion: string;
8
+ os: string;
9
+ activeTier?: string | null;
10
+ }): string;
11
+
12
+ declare function redactIssueBody(md: string): string;
13
+
14
+ declare function decideTransport(input: { bodyBytes: number }): "inline" | "file";
15
+
16
+ declare function buildIssueUrl(input: {
17
+ owner: string;
18
+ repo: string;
19
+ category: DoctorCategory | string;
20
+ check: string;
21
+ body: string;
22
+ }): string;
23
+
24
+ export { buildIssueBody, buildIssueUrl, decideTransport, redactIssueBody };
@@ -0,0 +1,14 @@
1
+ import {
2
+ buildIssueBody,
3
+ buildIssueUrl,
4
+ decideTransport,
5
+ redactIssueBody
6
+ } from "./chunk-DPGMKSSA.mjs";
7
+ import "./chunk-HMKLWVXB.mjs";
8
+ import "./chunk-4UEJOM6W.mjs";
9
+ export {
10
+ buildIssueBody,
11
+ buildIssueUrl,
12
+ decideTransport,
13
+ redactIssueBody
14
+ };