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.
- package/README.md +192 -0
- package/dist/attachments.d.ts +20 -0
- package/dist/attachments.mjs +43 -0
- package/dist/checks/browser.d.ts +100 -0
- package/dist/checks/browser.mjs +89 -0
- package/dist/checks/config.d.ts +91 -0
- package/dist/checks/config.mjs +88 -0
- package/dist/checks/ide.d.ts +89 -0
- package/dist/checks/ide.mjs +80 -0
- package/dist/checks/mcp.d.ts +61 -0
- package/dist/checks/mcp.mjs +56 -0
- package/dist/checks/native-deps.d.ts +131 -0
- package/dist/checks/native-deps.mjs +115 -0
- package/dist/checks/network.d.ts +71 -0
- package/dist/checks/network.mjs +70 -0
- package/dist/checks/probe.d.ts +93 -0
- package/dist/checks/probe.mjs +82 -0
- package/dist/checks/profiles.d.ts +99 -0
- package/dist/checks/profiles.mjs +90 -0
- package/dist/checks/runtime.d.ts +89 -0
- package/dist/checks/runtime.mjs +90 -0
- package/dist/checks/vault.d.ts +101 -0
- package/dist/checks/vault.mjs +90 -0
- package/dist/chunk-3B276PGG.mjs +115 -0
- package/dist/chunk-4UEJOM6W.mjs +9 -0
- package/dist/chunk-6EP2BLTV.mjs +205 -0
- package/dist/chunk-6YMQVLFX.mjs +146 -0
- package/dist/chunk-7JL36EBH.mjs +118 -0
- package/dist/chunk-DPGMKSSA.mjs +57 -0
- package/dist/chunk-H4BUAPPO.mjs +1950 -0
- package/dist/chunk-HMKLWVXB.mjs +109 -0
- package/dist/chunk-HTUAQRKH.mjs +125 -0
- package/dist/chunk-HU5B4FXS.mjs +139 -0
- package/dist/chunk-KCXM2M4B.mjs +1006 -0
- package/dist/chunk-LKJMLGFP.mjs +237 -0
- package/dist/chunk-LZPLNZ5U.mjs +67 -0
- package/dist/chunk-MTDFKNXX.mjs +19 -0
- package/dist/chunk-OF4DMAPJ.mjs +511 -0
- package/dist/chunk-PE23RMXY.mjs +43 -0
- package/dist/chunk-Q2VY4R5F.mjs +175 -0
- package/dist/chunk-S5VD7WTU.mjs +2540 -0
- package/dist/chunk-SVPRB62V.mjs +106 -0
- package/dist/chunk-TQLCLE4L.mjs +345 -0
- package/dist/chunk-U3DGFLXZ.mjs +43 -0
- package/dist/chunk-X45O6YD3.mjs +688 -0
- package/dist/chunk-XKSWCEGI.mjs +168 -0
- package/dist/chunk-Z7DAACGZ.mjs +534 -0
- package/dist/chunk-ZQFUZPLO.mjs +257 -0
- package/dist/cli.d.ts +952 -0
- package/dist/cli.mjs +827 -0
- package/dist/client.d.ts +355 -0
- package/dist/client.mjs +27 -0
- package/dist/cloud-sync.d-Cqt6y18U.d.ts +42 -0
- package/dist/cloud-sync.d.ts +42 -0
- package/dist/cloud-sync.mjs +17 -0
- package/dist/config.d.ts +186 -0
- package/dist/config.mjs +54 -0
- package/dist/daemon/attach.d.ts +36 -0
- package/dist/daemon/attach.mjs +25 -0
- package/dist/daemon/audit.d.ts +23 -0
- package/dist/daemon/audit.mjs +12 -0
- package/dist/daemon/client-http.d.ts +42 -0
- package/dist/daemon/client-http.mjs +29 -0
- package/dist/daemon/index.d.ts +14 -0
- package/dist/daemon/index.mjs +110 -0
- package/dist/daemon/install-tunnel.d.ts +46 -0
- package/dist/daemon/install-tunnel.mjs +14 -0
- package/dist/daemon/launcher.d.ts +163 -0
- package/dist/daemon/launcher.mjs +50 -0
- package/dist/daemon/lockfile.d.ts +29 -0
- package/dist/daemon/lockfile.mjs +18 -0
- package/dist/daemon/server.d.ts +159 -0
- package/dist/daemon/server.mjs +20 -0
- package/dist/daemon/token.d.ts +17 -0
- package/dist/daemon/token.mjs +17 -0
- package/dist/daemon/tunnel-providers/index.d.ts +330 -0
- package/dist/daemon/tunnel-providers/index.mjs +57 -0
- package/dist/daemon/tunnel.d.ts +23 -0
- package/dist/daemon/tunnel.mjs +9 -0
- package/dist/doctor-report.d.ts +24 -0
- package/dist/doctor-report.mjs +14 -0
- package/dist/doctor.d-CXmUqOXX.d.ts +43 -0
- package/dist/doctor.d.ts +44 -0
- package/dist/doctor.mjs +16 -0
- package/dist/export.d.ts +19 -0
- package/dist/export.mjs +15 -0
- package/dist/health-check.d.ts +108 -0
- package/dist/health-check.mjs +92 -0
- package/dist/history-store.d-BzjBF2m3.d.ts +65 -0
- package/dist/history-store.d.ts +65 -0
- package/dist/history-store.mjs +48 -0
- package/dist/impit-login-runner.d.ts +469 -0
- package/dist/impit-login-runner.mjs +685 -0
- package/dist/index.d.ts +159 -0
- package/dist/index.mjs +236 -0
- package/dist/login-runner.d.ts +333 -0
- package/dist/login-runner.mjs +320 -0
- package/dist/logout.d.ts +28 -0
- package/dist/logout.mjs +45 -0
- package/dist/manual-login-runner.d.ts +150 -0
- package/dist/manual-login-runner.mjs +146 -0
- package/dist/native-deps-BNThFHxa.d.ts +175 -0
- package/dist/native-deps-YNKXITRY.mjs +139 -0
- package/dist/profiles.d-DqS1oZWr.d.ts +41 -0
- package/dist/profiles.d.ts +41 -0
- package/dist/profiles.mjs +33 -0
- package/dist/redact.d.ts +159 -0
- package/dist/redact.mjs +11 -0
- package/dist/refresh.d.ts +118 -0
- package/dist/refresh.mjs +21 -0
- package/dist/reinit-watcher.d.ts +15 -0
- package/dist/reinit-watcher.mjs +8 -0
- package/dist/session-metadata-B9aV_n5g.d.ts +148 -0
- package/dist/tty-prompt.d.ts +44 -0
- package/dist/tty-prompt.mjs +39 -0
- package/dist/vault.d-BtRSLZiM.d.ts +8 -0
- package/dist/vault.d.ts +37 -0
- package/dist/vault.mjs +21 -0
- package/dist/viewer-detect.d-HWGnyFAA.d.ts +4 -0
- package/dist/viewer-detect.d.ts +4 -0
- package/dist/viewer-detect.mjs +37 -0
- package/dist/viewers.d-BGCK6sw6.d.ts +10 -0
- package/dist/viewers.d.ts +18 -0
- package/dist/viewers.mjs +122 -0
- 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,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
|
+
};
|