perplexity-user-mcp 0.8.39 → 0.8.44
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 +5 -0
- package/dist/attachments.d.ts +10 -20
- package/dist/browser-window.d.ts +1 -0
- package/dist/cf-warmup.d.ts +32 -0
- package/dist/checks/browser.d.ts +12 -100
- package/dist/checks/config.d.ts +25 -91
- package/dist/checks/ide.d.ts +31 -89
- package/dist/checks/mcp.d.ts +12 -61
- package/dist/checks/native-deps.d.ts +46 -131
- package/dist/checks/network.d.ts +13 -71
- package/dist/checks/probe.d.ts +20 -92
- package/dist/checks/profiles.d.ts +13 -99
- package/dist/checks/profiles.mjs +35 -0
- package/dist/checks/runtime.d.ts +24 -89
- package/dist/checks/vault.d.ts +13 -142
- package/dist/checks/vault.mjs +6 -11
- package/dist/{chunk-T6ARJK2P.mjs → chunk-2B5OQUXR.mjs} +6 -6
- package/dist/{chunk-2FPGJKCA.mjs → chunk-2EE7MNP2.mjs} +2 -2
- package/dist/{chunk-NJX4RBO6.mjs → chunk-2OVLCZHU.mjs} +28 -3
- package/dist/{chunk-WDIW33DA.mjs → chunk-3LUO5ATM.mjs} +1 -1
- package/dist/{chunk-B65IJQZJ.mjs → chunk-6E6XTHTG.mjs} +1 -1
- package/dist/{chunk-S677V2JU.mjs → chunk-C5I7KXHK.mjs} +32 -2
- package/dist/{chunk-TDXETAQT.mjs → chunk-DKEJZ4FI.mjs} +1 -1
- package/dist/{chunk-U7QPUNRH.mjs → chunk-DXR6EEZH.mjs} +26 -7
- package/dist/{chunk-HJIXH6CL.mjs → chunk-E3GRJXXJ.mjs} +2 -0
- package/dist/{chunk-RK4EBZJ3.mjs → chunk-E75J42W5.mjs} +11 -8
- package/dist/{chunk-452DK6OS.mjs → chunk-FNHYUE22.mjs} +2 -2
- package/dist/{chunk-D254EFYB.mjs → chunk-GBI2U336.mjs} +1 -1
- package/dist/chunk-GPUGKWXH.mjs +17 -0
- package/dist/{chunk-HNSPNCFH.mjs → chunk-KSNV3ZVY.mjs} +1 -1
- package/dist/{chunk-XTRJSV72.mjs → chunk-LGH5BSUY.mjs} +1 -1
- package/dist/{chunk-KJFX2ZXR.mjs → chunk-NMKNEEZB.mjs} +1 -1
- package/dist/{chunk-FKQ3HP4Q.mjs → chunk-TIWHN4IW.mjs} +1 -1
- package/dist/{chunk-V4U3JM4R.mjs → chunk-TSLRTZYR.mjs} +1 -1
- package/dist/{chunk-DQQISMYN.mjs → chunk-V4LHDNWJ.mjs} +2 -2
- package/dist/{chunk-C3HPFFTD.mjs → chunk-WHVJ724K.mjs} +84 -44
- package/dist/cli.d.ts +14 -1298
- package/dist/cli.mjs +35 -27
- package/dist/client.d.ts +27 -24
- package/dist/client.mjs +6 -6
- package/dist/cloud-sync.d.ts +65 -42
- package/dist/cloud-sync.mjs +8 -8
- package/dist/config.d.ts +35 -39
- package/dist/config.mjs +3 -3
- package/dist/cookie-jar.d.ts +77 -0
- package/dist/daemon/attach.d.ts +10 -11
- package/dist/daemon/attach.mjs +19 -17
- package/dist/daemon/audit.d.ts +5 -7
- package/dist/daemon/audit.mjs +2 -2
- package/dist/daemon/client-http.d.ts +10 -16
- package/dist/daemon/client-http.mjs +17 -17
- package/dist/daemon/index.d.ts +17 -14
- package/dist/daemon/index.mjs +18 -18
- package/dist/daemon/install-tunnel.d.ts +8 -34
- package/dist/daemon/install-tunnel.mjs +2 -2
- package/dist/daemon/launcher.d.ts +24 -29
- package/dist/daemon/launcher.mjs +16 -16
- package/dist/daemon/local-tokens.d.ts +23 -0
- package/dist/daemon/lockfile.d.ts +10 -12
- package/dist/daemon/lockfile.mjs +2 -2
- package/dist/daemon/oauth-consent-cache.d.ts +86 -0
- package/dist/daemon/oauth-provider.d.ts +132 -0
- package/dist/daemon/public-pages.d.ts +9 -0
- package/dist/daemon/security.d.ts +52 -0
- package/dist/daemon/server.d.ts +12 -83
- package/dist/daemon/server.mjs +11 -11
- package/dist/daemon/token.d.ts +7 -9
- package/dist/daemon/token.mjs +2 -2
- package/dist/daemon/tunnel-providers/cloudflared-named-setup.d.ts +140 -0
- package/dist/daemon/tunnel-providers/cloudflared-named.d.ts +45 -0
- package/dist/daemon/tunnel-providers/cloudflared-quick.d.ts +8 -0
- package/dist/daemon/tunnel-providers/index.d.ts +16 -327
- package/dist/daemon/tunnel-providers/index.mjs +3 -3
- package/dist/daemon/tunnel-providers/ngrok-config.d.ts +18 -0
- package/dist/daemon/tunnel-providers/ngrok.d.ts +68 -0
- package/dist/daemon/tunnel-providers/types.d.ts +56 -0
- package/dist/daemon/tunnel.d.ts +5 -7
- package/dist/debug-tracer.d.ts +2 -0
- package/dist/doctor-report.d.ts +17 -22
- package/dist/doctor.d.ts +12 -44
- package/dist/doctor.mjs +2 -2
- package/dist/export.d.ts +11 -18
- package/dist/export.mjs +4 -4
- package/dist/format.d.ts +52 -0
- package/dist/fs-utils.d.ts +8 -0
- package/dist/health-check.d.ts +1 -108
- package/dist/health-check.mjs +3 -3
- package/dist/history-store.d.ts +29 -65
- package/dist/history-store.mjs +2 -2
- package/dist/impit-login-runner.d.ts +1 -469
- package/dist/impit-login-runner.mjs +4 -4
- package/dist/index.d.ts +25 -149
- package/dist/index.mjs +22 -20
- package/dist/is-main-module.d.ts +9 -0
- package/dist/login-runner.d.ts +1 -333
- package/dist/login-runner.mjs +13 -13
- package/dist/login.d.ts +5 -0
- package/dist/logout.d.ts +2 -28
- package/dist/logout.mjs +3 -2
- package/dist/manual-login-runner.d.ts +1 -150
- package/dist/manual-login-runner.mjs +11 -11
- package/dist/{native-deps-IE4B55EL.mjs → native-deps-FCSYDL4W.mjs} +4 -4
- package/dist/native-deps.d.ts +36 -0
- package/dist/package-version.d.ts +1 -0
- package/dist/profiles.d.ts +41 -41
- package/dist/profiles.mjs +1 -1
- package/dist/prompts.d.ts +2 -0
- package/dist/redact.d.ts +14 -142
- package/dist/refresh.d.ts +11 -16
- package/dist/refresh.mjs +4 -4
- package/dist/reinit-watcher.d.ts +15 -24
- package/dist/reinit-watcher.mjs +2 -2
- package/dist/resources.d.ts +5 -0
- package/dist/safe-write.d.ts +16 -0
- package/dist/session-metadata.d.ts +45 -0
- package/dist/tool-config.d.ts +10 -0
- package/dist/tools.d.ts +23 -0
- package/dist/tty-prompt.d.ts +18 -34
- package/dist/vault.d.ts +114 -34
- package/dist/vault.mjs +6 -4
- package/dist/viewer-detect.d.ts +2 -4
- package/dist/viewers.d.ts +13 -18
- package/dist/viewers.mjs +1 -1
- package/package.json +3 -3
- package/dist/cloud-sync.d-Cqt6y18U.d.ts +0 -42
- package/dist/doctor.d-CXmUqOXX.d.ts +0 -43
- package/dist/history-store.d-BzjBF2m3.d.ts +0 -65
- package/dist/native-deps-BNThFHxa.d.ts +0 -175
- package/dist/profiles.d-DqS1oZWr.d.ts +0 -41
- package/dist/session-metadata-B9aV_n5g.d.ts +0 -148
- package/dist/vault.d-BSJWDLhp.d.ts +0 -37
- package/dist/viewer-detect.d-HWGnyFAA.d.ts +0 -4
- package/dist/viewers.d-BGCK6sw6.d.ts +0 -10
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth consent cache — remembers per-(clientId, redirectUri, resource)
|
|
3
|
+
* consents so the user isn't prompted every time Claude Desktop / Cursor /
|
|
4
|
+
* Cline refreshes an access token (which happens on a ~1h cycle).
|
|
5
|
+
*
|
|
6
|
+
* Storage: <configDir>/oauth-consent.json, 0600.
|
|
7
|
+
* Entries carry an absolute `expiresAt` (ms since epoch). On each check
|
|
8
|
+
* we lazily prune expired entries. Deleting a record is synchronous and
|
|
9
|
+
* best-effort (if the file is locked we lose the mutation; next write
|
|
10
|
+
* will overwrite it).
|
|
11
|
+
*
|
|
12
|
+
* Key structure: `(clientId, redirectUri, resource)`. After Phase 8.2 H12
|
|
13
|
+
* the OAuth `resource` parameter (RFC 8707) is part of the audience binding,
|
|
14
|
+
* so a consent granted for `https://tunnel-a.example/mcp` must NOT
|
|
15
|
+
* auto-approve a new authorize request for `https://tunnel-b.example/mcp`
|
|
16
|
+
* even though the clientId + redirectUri match. `resource === undefined` is
|
|
17
|
+
* a distinct key from any bound resource string — unbound consents (legacy
|
|
18
|
+
* / loopback clients that don't send the `resource` param) don't
|
|
19
|
+
* auto-approve bound-resource consents and vice-versa.
|
|
20
|
+
*
|
|
21
|
+
* Revoking a consent entry does NOT invalidate already-issued access
|
|
22
|
+
* tokens — those live 1h anyway; revoking the CLIENT (see
|
|
23
|
+
* oauth-provider.revokeClient) is the way to invalidate outstanding
|
|
24
|
+
* tokens. Phase 8.2's dashboard panel composes these two.
|
|
25
|
+
*/
|
|
26
|
+
export interface ConsentEntry {
|
|
27
|
+
clientId: string;
|
|
28
|
+
redirectUri: string;
|
|
29
|
+
approvedAt: string;
|
|
30
|
+
expiresAt: number;
|
|
31
|
+
/**
|
|
32
|
+
* RFC 8707 resource binding. `undefined` = unbound (legacy / loopback
|
|
33
|
+
* clients that don't send the `resource` param at /authorize). Two entries
|
|
34
|
+
* with the same (clientId, redirectUri) but different `resource` values
|
|
35
|
+
* (including undefined-vs-string) are DISTINCT cache keys.
|
|
36
|
+
*/
|
|
37
|
+
resource?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface ConsentCacheOptions {
|
|
40
|
+
cachePath?: string;
|
|
41
|
+
now?: () => number;
|
|
42
|
+
/**
|
|
43
|
+
* Resource the authorize request targets. Must match `ConsentEntry.resource`
|
|
44
|
+
* exactly for a check hit (undefined matches undefined; a string matches
|
|
45
|
+
* the same string; any mismatch is a miss).
|
|
46
|
+
*/
|
|
47
|
+
resource?: string;
|
|
48
|
+
}
|
|
49
|
+
export declare function getConsentCachePath(configDir?: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Record an approval for (clientId, redirectUri, resource). Overwrites any
|
|
52
|
+
* prior entry for the SAME triple only — entries that share clientId +
|
|
53
|
+
* redirectUri but have a different `resource` (including bound-vs-unbound)
|
|
54
|
+
* are preserved as separate consents.
|
|
55
|
+
*/
|
|
56
|
+
export declare function record(clientId: string, redirectUri: string, ttlMs: number, options?: ConsentCacheOptions): ConsentEntry;
|
|
57
|
+
/**
|
|
58
|
+
* Returns true iff a non-expired consent exists for the full triple
|
|
59
|
+
* (clientId, redirectUri, resource). `resource === undefined` is a distinct
|
|
60
|
+
* key from any bound resource string. Expired entries are pruned from disk
|
|
61
|
+
* as a side effect.
|
|
62
|
+
*/
|
|
63
|
+
export declare function check(clientId: string, redirectUri: string, options?: ConsentCacheOptions): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Returns all non-expired consents. Expired entries are pruned on read.
|
|
66
|
+
*/
|
|
67
|
+
export declare function list(options?: ConsentCacheOptions): ConsentEntry[];
|
|
68
|
+
/**
|
|
69
|
+
* Revoke consent entries. Filter hierarchy:
|
|
70
|
+
* - `{}` (no filter) → revoke everything
|
|
71
|
+
* - `{ clientId }` → revoke every entry for that client (all redirects, all resources)
|
|
72
|
+
* - `{ clientId, redirectUri }` → revoke every entry for that (client, redirect) (all resources)
|
|
73
|
+
* - `{ clientId, redirectUri, resource }` → revoke only the exact triple
|
|
74
|
+
* - `{ clientId, resource }` → revoke every entry for that client scoped to the given resource
|
|
75
|
+
*
|
|
76
|
+
* A `resource` filter value of `undefined` means "do not filter by resource"
|
|
77
|
+
* (NOT "match unbound entries"). To revoke only unbound entries for a client,
|
|
78
|
+
* enumerate via `list()` + call revoke per-entry — the filter API deliberately
|
|
79
|
+
* treats `resource === undefined` as "any" to match pre-H12 callers.
|
|
80
|
+
*
|
|
81
|
+
* Returns the number of entries removed.
|
|
82
|
+
*/
|
|
83
|
+
export declare function revoke(options?: ConsentCacheOptions & {
|
|
84
|
+
clientId?: string;
|
|
85
|
+
redirectUri?: string;
|
|
86
|
+
}): number;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.1 authorization-server provider for the Perplexity MCP daemon.
|
|
3
|
+
*
|
|
4
|
+
* Implements the MCP SDK's `OAuthServerProvider` interface. Plugs into
|
|
5
|
+
* `mcpAuthRouter()` to expose `/authorize`, `/token`, `/register`, `/revoke`
|
|
6
|
+
* and the `/.well-known/*` metadata endpoints.
|
|
7
|
+
*
|
|
8
|
+
* Design:
|
|
9
|
+
* - Clients are persisted in `<configDir>/oauth-clients.json` (0600).
|
|
10
|
+
* - Authorization codes are in-memory, 2-min TTL, single-use.
|
|
11
|
+
* - Access tokens are in-memory, 1-hour TTL.
|
|
12
|
+
* - Refresh tokens rotate on each exchange.
|
|
13
|
+
* - `verifyAccessToken` accepts either a valid OAuth access token OR the
|
|
14
|
+
* daemon's static bearer, so existing loopback callers (extension host
|
|
15
|
+
* + CLI) keep working with no changes.
|
|
16
|
+
* - `authorize()` defers to a caller-supplied `requestConsent` callback
|
|
17
|
+
* which the daemon wires to the VS Code extension's modal. Until the
|
|
18
|
+
* user approves or the 2-min timeout elapses, the HTTP response is
|
|
19
|
+
* held open.
|
|
20
|
+
*/
|
|
21
|
+
import type { Response } from "express";
|
|
22
|
+
import type { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js";
|
|
23
|
+
import type { OAuthRegisteredClientsStore } from "@modelcontextprotocol/sdk/server/auth/clients.js";
|
|
24
|
+
import type { AuthorizationParams, OAuthServerProvider } from "@modelcontextprotocol/sdk/server/auth/provider.js";
|
|
25
|
+
import type { OAuthClientInformationFull, OAuthTokenRevocationRequest, OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
26
|
+
import * as consentCache from "./oauth-consent-cache.js";
|
|
27
|
+
export interface OAuthProviderOptions {
|
|
28
|
+
configDir: string;
|
|
29
|
+
/**
|
|
30
|
+
* Invoked when a browser hits `/authorize`. Must resolve to `true`
|
|
31
|
+
* (approve) or `false` (deny). The daemon wires this to the VS Code
|
|
32
|
+
* modal via an SSE-based consent coordinator.
|
|
33
|
+
*
|
|
34
|
+
* `resource` is the RFC 8707 resource the authorize request targets
|
|
35
|
+
* (post-H12). The consent UI MUST display this value so users can spot
|
|
36
|
+
* cross-resource replay attempts. `undefined` means the client did not
|
|
37
|
+
* send a `resource` param (legacy / loopback flow).
|
|
38
|
+
*/
|
|
39
|
+
requestConsent: (info: {
|
|
40
|
+
clientId: string;
|
|
41
|
+
clientName: string;
|
|
42
|
+
redirectUri: string;
|
|
43
|
+
consentId: string;
|
|
44
|
+
resource?: string;
|
|
45
|
+
}) => Promise<boolean>;
|
|
46
|
+
/** Live getter so rotate-token stays supported. */
|
|
47
|
+
getStaticBearer: () => string;
|
|
48
|
+
/**
|
|
49
|
+
* Live getter for the consent-cache TTL in ms. `0` disables the cache
|
|
50
|
+
* (modal fires every time). Read live per-authorize so toggling the
|
|
51
|
+
* setting takes effect on the next request without restarting the
|
|
52
|
+
* provider.
|
|
53
|
+
*/
|
|
54
|
+
getConsentCacheTtlMs?: () => number;
|
|
55
|
+
/**
|
|
56
|
+
* Fires just before the authorize response is sent when the provider
|
|
57
|
+
* decided to auto-approve from the consent cache. server.ts uses this
|
|
58
|
+
* to flip the request's audit tag from `none` to `oauth-cached` so
|
|
59
|
+
* the audit log records the cache hit distinctly.
|
|
60
|
+
*/
|
|
61
|
+
onConsentCacheHit?: (info: {
|
|
62
|
+
clientId: string;
|
|
63
|
+
redirectUri: string;
|
|
64
|
+
res: Response;
|
|
65
|
+
}) => void;
|
|
66
|
+
}
|
|
67
|
+
export interface AuthorizedClientSummary {
|
|
68
|
+
clientId: string;
|
|
69
|
+
clientName?: string;
|
|
70
|
+
registeredAt: number;
|
|
71
|
+
lastUsedAt?: string;
|
|
72
|
+
consentLastApprovedAt?: string;
|
|
73
|
+
activeTokens: number;
|
|
74
|
+
}
|
|
75
|
+
export declare class PerplexityOAuthProvider implements OAuthServerProvider {
|
|
76
|
+
private readonly options;
|
|
77
|
+
private codes;
|
|
78
|
+
private tokens;
|
|
79
|
+
private clients;
|
|
80
|
+
private clientsPath;
|
|
81
|
+
private consentCachePath;
|
|
82
|
+
constructor(options: OAuthProviderOptions);
|
|
83
|
+
get clientsStore(): OAuthRegisteredClientsStore;
|
|
84
|
+
authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response): Promise<void>;
|
|
85
|
+
private issueAuthorizationCode;
|
|
86
|
+
challengeForAuthorizationCode(_client: OAuthClientInformationFull, authorizationCode: string): Promise<string>;
|
|
87
|
+
exchangeAuthorizationCode(client: OAuthClientInformationFull, authorizationCode: string, codeVerifier?: string, redirectUri?: string, resource?: URL | string): Promise<OAuthTokens>;
|
|
88
|
+
exchangeRefreshToken(client: OAuthClientInformationFull, refreshToken: string, scopes?: string[], resource?: URL | string): Promise<OAuthTokens>;
|
|
89
|
+
verifyAccessToken(token: string, source?: "loopback" | "tunnel", expectedResource?: string): Promise<AuthInfo>;
|
|
90
|
+
revokeToken(_client: OAuthClientInformationFull, request: OAuthTokenRevocationRequest): Promise<void>;
|
|
91
|
+
listClients(): AuthorizedClientSummary[];
|
|
92
|
+
revokeClient(clientId: string): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Revoke every registered OAuth client and invalidate all outstanding
|
|
95
|
+
* access/refresh tokens. Returns the count of clients that were removed.
|
|
96
|
+
* Cached consents for every revoked client are also purged so a
|
|
97
|
+
* future registration with a colliding id cannot silently inherit them.
|
|
98
|
+
*/
|
|
99
|
+
revokeAllClients(): number;
|
|
100
|
+
listConsents(): consentCache.ConsentEntry[];
|
|
101
|
+
revokeConsent(clientId?: string, redirectUri?: string): number;
|
|
102
|
+
private issueTokenPair;
|
|
103
|
+
private loadClients;
|
|
104
|
+
private persistClients;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Coordinator for /authorize ↔ /daemon/oauth-consent round-trip.
|
|
108
|
+
*
|
|
109
|
+
* `request()` starts a new pending consent and returns a Promise that resolves
|
|
110
|
+
* when the extension host POSTs `/daemon/oauth-consent` with the matching id.
|
|
111
|
+
* Times out after the given ms (denying by default).
|
|
112
|
+
*/
|
|
113
|
+
export declare class ConsentCoordinator {
|
|
114
|
+
private pending;
|
|
115
|
+
request(options: {
|
|
116
|
+
id: string;
|
|
117
|
+
clientId: string;
|
|
118
|
+
clientName: string;
|
|
119
|
+
redirectUri: string;
|
|
120
|
+
resource?: string;
|
|
121
|
+
timeoutMs: number;
|
|
122
|
+
onRequest: () => void;
|
|
123
|
+
}): Promise<boolean>;
|
|
124
|
+
resolve(id: string, approved: boolean): boolean;
|
|
125
|
+
list(): Array<{
|
|
126
|
+
id: string;
|
|
127
|
+
clientId: string;
|
|
128
|
+
clientName: string;
|
|
129
|
+
redirectUri: string;
|
|
130
|
+
resource?: string;
|
|
131
|
+
}>;
|
|
132
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unauthenticated pages served on the daemon's HTTP surface.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately minimal — no version, uptime, or tool-list leakage. The homepage
|
|
5
|
+
* is what a human lands on if they hit the tunnel URL in a browser. Everything
|
|
6
|
+
* actionable lives in the VS Code dashboard behind bearer auth.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getHomepageHtml(): string;
|
|
9
|
+
export declare function getRobotsTxt(): string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security middleware for the daemon HTTP surface.
|
|
3
|
+
*
|
|
4
|
+
* Ships in Phase 6a — everything runs BEFORE the bearer check so that both
|
|
5
|
+
* authenticated and unauthenticated requests are rate-limited / blocklisted.
|
|
6
|
+
*
|
|
7
|
+
* Components:
|
|
8
|
+
* 1. IP/UA extraction into req._pplx (typed).
|
|
9
|
+
* 2. Per-IP rate limit for unauthenticated requests (credentials often missing
|
|
10
|
+
* from /register, /authorize, public pages — bearer-rate-limit kicks in
|
|
11
|
+
* once authenticated).
|
|
12
|
+
* 3. Per-bearer rate limit (60 rpm default). Loopback-sourced requests exempt.
|
|
13
|
+
* 4. User-Agent blocklist — known scanners.
|
|
14
|
+
* 5. 401-burst tripwire — 20 401s in 60s on the tunnel fires a callback so
|
|
15
|
+
* the launcher can auto-disable the tunnel.
|
|
16
|
+
* 6. Slow-401 — every 401 response is delayed a fixed 150ms to defeat
|
|
17
|
+
* bearer-brute-force timing probes.
|
|
18
|
+
*/
|
|
19
|
+
export interface SecurityOptions {
|
|
20
|
+
ratelimitRpm?: number;
|
|
21
|
+
tripwireWindowMs?: number;
|
|
22
|
+
tripwireThreshold?: number;
|
|
23
|
+
slow401Ms?: number;
|
|
24
|
+
uaBlocklist?: RegExp[];
|
|
25
|
+
onTripwireTriggered?: (info: {
|
|
26
|
+
source: "loopback" | "tunnel";
|
|
27
|
+
failures: number;
|
|
28
|
+
windowMs: number;
|
|
29
|
+
ip: string | null;
|
|
30
|
+
}) => void | Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export interface SecurityMiddlewareResult {
|
|
33
|
+
middleware: (req: any, res: any, next: any) => void;
|
|
34
|
+
record401: (info: {
|
|
35
|
+
source: "loopback" | "tunnel";
|
|
36
|
+
ip: string | null;
|
|
37
|
+
}) => void;
|
|
38
|
+
snapshot: () => SecurityDiagnostics;
|
|
39
|
+
}
|
|
40
|
+
export interface SecurityDiagnostics {
|
|
41
|
+
tripwireFailures: number;
|
|
42
|
+
tripwireWindowMs: number;
|
|
43
|
+
tripwireThreshold: number;
|
|
44
|
+
rateLimitedBearers: number;
|
|
45
|
+
blockedUas: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create the security middleware stack. Call once per daemon instance.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createSecurity(options?: SecurityOptions): SecurityMiddlewareResult;
|
|
51
|
+
/** Reset the tripwire latch — called after an auto-disable so future attacks re-trip. */
|
|
52
|
+
export declare function resetTripwire(security: SecurityMiddlewareResult): void;
|
package/dist/daemon/server.d.ts
CHANGED
|
@@ -1,87 +1,17 @@
|
|
|
1
|
-
import { RequestLike } from
|
|
2
|
-
import { PerplexityClient } from
|
|
3
|
-
import { readAuditTail } from
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import
|
|
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
|
-
|
|
1
|
+
import { type RequestLike } from "express";
|
|
2
|
+
import { PerplexityClient } from "../client.js";
|
|
3
|
+
import { readAuditTail } from "./audit.js";
|
|
4
|
+
import { type AuthorizedClientSummary } from "./oauth-provider.js";
|
|
5
|
+
import type { ConsentEntry } from "./oauth-consent-cache.js";
|
|
6
|
+
import { type DaemonTokenRecord } from "./token.js";
|
|
77
7
|
type EventPayload = Record<string, unknown>;
|
|
78
|
-
interface DaemonTunnelHealth {
|
|
8
|
+
export interface DaemonTunnelHealth {
|
|
79
9
|
status: "disabled" | "starting" | "enabled" | "crashed";
|
|
80
10
|
url: string | null;
|
|
81
11
|
pid?: number | null;
|
|
82
12
|
error?: string | null;
|
|
83
13
|
}
|
|
84
|
-
interface StartDaemonServerOptions {
|
|
14
|
+
export interface StartDaemonServerOptions {
|
|
85
15
|
host?: string;
|
|
86
16
|
port?: number;
|
|
87
17
|
uuid?: string;
|
|
@@ -120,7 +50,7 @@ interface StartDaemonServerOptions {
|
|
|
120
50
|
/** When tunnel is enabled we advertise this as the OAuth issuer. */
|
|
121
51
|
getTunnelUrl?: () => string | null;
|
|
122
52
|
}
|
|
123
|
-
interface StartedDaemonServer {
|
|
53
|
+
export interface StartedDaemonServer {
|
|
124
54
|
host: string;
|
|
125
55
|
port: number;
|
|
126
56
|
url: string;
|
|
@@ -147,13 +77,12 @@ interface StartedDaemonServer {
|
|
|
147
77
|
redirectUri?: string;
|
|
148
78
|
}) => number;
|
|
149
79
|
}
|
|
150
|
-
declare function startDaemonServer(options?: StartDaemonServerOptions): Promise<StartedDaemonServer>;
|
|
80
|
+
export declare function startDaemonServer(options?: StartDaemonServerOptions): Promise<StartedDaemonServer>;
|
|
151
81
|
/**
|
|
152
82
|
* Canonicalize the expected resource identifier (RFC 8707) for this
|
|
153
83
|
* request. Returns `<scheme>://<host>/mcp` with no trailing slash so the
|
|
154
84
|
* PRM endpoint, the /authorize→/token binding, and verifyAccessToken's
|
|
155
85
|
* expectedResource all agree on a single form.
|
|
156
86
|
*/
|
|
157
|
-
declare function resolveRequestResource(req: RequestLike, fallback?: URL): string;
|
|
158
|
-
|
|
159
|
-
export { type DaemonTunnelHealth, type StartDaemonServerOptions, type StartedDaemonServer, resolveRequestResource, startDaemonServer };
|
|
87
|
+
export declare function resolveRequestResource(req: RequestLike, fallback?: URL): string;
|
|
88
|
+
export {};
|
package/dist/daemon/server.mjs
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveRequestResource,
|
|
3
3
|
startDaemonServer
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
12
|
-
import "../chunk-
|
|
13
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-2B5OQUXR.mjs";
|
|
5
|
+
import "../chunk-TSLRTZYR.mjs";
|
|
6
|
+
import "../chunk-DKEJZ4FI.mjs";
|
|
7
|
+
import "../chunk-2EE7MNP2.mjs";
|
|
8
|
+
import "../chunk-WHVJ724K.mjs";
|
|
9
|
+
import "../chunk-V4LHDNWJ.mjs";
|
|
10
|
+
import "../chunk-6E6XTHTG.mjs";
|
|
11
|
+
import "../chunk-GBI2U336.mjs";
|
|
12
|
+
import "../chunk-DXR6EEZH.mjs";
|
|
13
|
+
import "../chunk-C5I7KXHK.mjs";
|
|
14
14
|
import "../chunk-MTDFKNXX.mjs";
|
|
15
|
-
import "../chunk-
|
|
15
|
+
import "../chunk-E3GRJXXJ.mjs";
|
|
16
16
|
import "../chunk-4UEJOM6W.mjs";
|
|
17
17
|
export {
|
|
18
18
|
resolveRequestResource,
|
package/dist/daemon/token.d.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
interface DaemonTokenRecord {
|
|
1
|
+
export interface DaemonTokenRecord {
|
|
2
2
|
bearerToken: string;
|
|
3
3
|
version: number;
|
|
4
4
|
createdAt: string;
|
|
5
5
|
rotatedAt: string;
|
|
6
6
|
}
|
|
7
|
-
interface TokenOptions {
|
|
7
|
+
export interface TokenOptions {
|
|
8
8
|
tokenPath?: string;
|
|
9
9
|
now?: () => string;
|
|
10
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 };
|
|
11
|
+
export declare function getTokenPath(configDir?: string): string;
|
|
12
|
+
export declare function generateBearerToken(): string;
|
|
13
|
+
export declare function readToken(options?: TokenOptions): DaemonTokenRecord | null;
|
|
14
|
+
export declare function ensureToken(options?: TokenOptions): DaemonTokenRecord;
|
|
15
|
+
export declare function rotateToken(options?: TokenOptions): DaemonTokenRecord;
|
package/dist/daemon/token.mjs
CHANGED
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
getTokenPath,
|
|
5
5
|
readToken,
|
|
6
6
|
rotateToken
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-DKEJZ4FI.mjs";
|
|
8
8
|
import "../chunk-MTDFKNXX.mjs";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-E3GRJXXJ.mjs";
|
|
10
10
|
import "../chunk-4UEJOM6W.mjs";
|
|
11
11
|
export {
|
|
12
12
|
ensureToken,
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflared named-tunnel setup helpers.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the `cloudflared` CLI to drive a persistent (named) tunnel. Named
|
|
5
|
+
* tunnels require an origin cert (`~/.cloudflared/cert.pem`), a tunnel UUID
|
|
6
|
+
* + credentials file, and a YAML config that maps a hostname -> local port.
|
|
7
|
+
*
|
|
8
|
+
* This module ships the setup primitives only; provider registration lives in
|
|
9
|
+
* cloudflared-named.ts (Phase 8.4.2). The dashboard/CLI call these helpers
|
|
10
|
+
* during the one-time setup flow.
|
|
11
|
+
*/
|
|
12
|
+
import { spawn as nodeSpawn } from "node:child_process";
|
|
13
|
+
/**
|
|
14
|
+
* Minimal spawn signature we rely on — matches the three-arg overload of
|
|
15
|
+
* `node:child_process.spawn` used for piping stdio and collecting output.
|
|
16
|
+
* Declared as a type alias so tests can inject a fake implementation.
|
|
17
|
+
*/
|
|
18
|
+
type SpawnFn = typeof nodeSpawn;
|
|
19
|
+
export interface CloudflaredLoginResult {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
certPath: string;
|
|
22
|
+
stderr?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface NamedTunnelSummary {
|
|
25
|
+
/** cloudflared's tunnel UUID. */
|
|
26
|
+
uuid: string;
|
|
27
|
+
/** Human-readable name. */
|
|
28
|
+
name: string;
|
|
29
|
+
createdAt?: string;
|
|
30
|
+
connections?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface CreatedTunnel extends NamedTunnelSummary {
|
|
33
|
+
/** Path to the credentials JSON cloudflared wrote. */
|
|
34
|
+
credentialsPath: string;
|
|
35
|
+
}
|
|
36
|
+
export interface NamedTunnelConfig {
|
|
37
|
+
uuid: string;
|
|
38
|
+
/** e.g. "mcp.example.com" */
|
|
39
|
+
hostname: string;
|
|
40
|
+
/** Local daemon HTTP port. */
|
|
41
|
+
port: number;
|
|
42
|
+
/** Full path to the written .yml. */
|
|
43
|
+
configPath: string;
|
|
44
|
+
credentialsPath: string;
|
|
45
|
+
}
|
|
46
|
+
export interface DeletedNamedTunnel {
|
|
47
|
+
uuid: string;
|
|
48
|
+
}
|
|
49
|
+
export type DeleteNamedTunnelFailureReason = "active-connections" | "unknown";
|
|
50
|
+
export declare class DeleteNamedTunnelError extends Error {
|
|
51
|
+
readonly reason: DeleteNamedTunnelFailureReason;
|
|
52
|
+
constructor(message: string, reason: DeleteNamedTunnelFailureReason);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Runs `cloudflared tunnel login`. Opens a browser. Blocks until the cert
|
|
56
|
+
* lands at `~/.cloudflared/cert.pem` (or throws on timeout / abort). The
|
|
57
|
+
* login subprocess is best-effort terminated on resolve/reject.
|
|
58
|
+
*/
|
|
59
|
+
export declare function runCloudflaredLogin(options: {
|
|
60
|
+
configDir: string;
|
|
61
|
+
binaryPath?: string;
|
|
62
|
+
signal?: AbortSignal;
|
|
63
|
+
timeoutMs?: number;
|
|
64
|
+
/** Override the cert watch location (defaults to `$HOME/.cloudflared/cert.pem`). */
|
|
65
|
+
certPath?: string;
|
|
66
|
+
/**
|
|
67
|
+
* When true, pipe the cloudflared child's stderr AND stdout to the parent
|
|
68
|
+
* process's stderr so CLI users see the "open this URL in your browser"
|
|
69
|
+
* prompt (some cloudflared builds emit it on stdout, others on stderr).
|
|
70
|
+
* Never forwards to parent stdout — the CLI reserves stdout for --json
|
|
71
|
+
* machine-readable output. Default false (test hermeticity + dashboard
|
|
72
|
+
* flow that wraps output in notifications instead).
|
|
73
|
+
*/
|
|
74
|
+
forwardOutput?: boolean;
|
|
75
|
+
/** Test-only dependency injection seam. */
|
|
76
|
+
dependencies?: {
|
|
77
|
+
spawn?: SpawnFn;
|
|
78
|
+
};
|
|
79
|
+
}): Promise<CloudflaredLoginResult>;
|
|
80
|
+
/**
|
|
81
|
+
* Runs `cloudflared tunnel list --output=json` and parses. Returns [] on
|
|
82
|
+
* "no tunnels" (exit 0 + empty list). Throws on binary/cert problems.
|
|
83
|
+
*/
|
|
84
|
+
export declare function listNamedTunnels(options: {
|
|
85
|
+
configDir: string;
|
|
86
|
+
binaryPath?: string;
|
|
87
|
+
dependencies?: {
|
|
88
|
+
spawn?: SpawnFn;
|
|
89
|
+
};
|
|
90
|
+
}): Promise<NamedTunnelSummary[]>;
|
|
91
|
+
/**
|
|
92
|
+
* Runs `cloudflared tunnel create <name>`, parses the UUID + credentials
|
|
93
|
+
* path out of stdout, then runs `cloudflared tunnel route dns <uuid>
|
|
94
|
+
* <hostname>` to install the CNAME record.
|
|
95
|
+
*/
|
|
96
|
+
export declare function createNamedTunnel(options: {
|
|
97
|
+
configDir: string;
|
|
98
|
+
name: string;
|
|
99
|
+
hostname: string;
|
|
100
|
+
binaryPath?: string;
|
|
101
|
+
signal?: AbortSignal;
|
|
102
|
+
dependencies?: {
|
|
103
|
+
spawn?: SpawnFn;
|
|
104
|
+
};
|
|
105
|
+
}): Promise<CreatedTunnel>;
|
|
106
|
+
/**
|
|
107
|
+
* Deletes a remote Cloudflare named tunnel. This is intentionally separate
|
|
108
|
+
* from clearNamedTunnelConfig(): remote delete can fail even with --force
|
|
109
|
+
* when DNS still routes traffic and active connections keep the tunnel alive.
|
|
110
|
+
*/
|
|
111
|
+
export declare function deleteNamedTunnel(options: {
|
|
112
|
+
configDir: string;
|
|
113
|
+
uuid: string;
|
|
114
|
+
binaryPath?: string;
|
|
115
|
+
signal?: AbortSignal;
|
|
116
|
+
dependencies?: {
|
|
117
|
+
spawn?: SpawnFn;
|
|
118
|
+
};
|
|
119
|
+
}): Promise<DeletedNamedTunnel>;
|
|
120
|
+
export declare function isActiveConnectionDeleteFailure(output: string): boolean;
|
|
121
|
+
export declare function getNamedTunnelConfigPath(configDir: string): string;
|
|
122
|
+
/**
|
|
123
|
+
* Writes `<configDir>/cloudflared-named.yml` describing the tunnel ->
|
|
124
|
+
* localhost mapping. Uses the temp-file + rename pattern from ngrok-config.ts
|
|
125
|
+
* and locks file mode to 0600 (POSIX) / user-only ACL (Windows).
|
|
126
|
+
*/
|
|
127
|
+
export declare function writeTunnelConfig(options: {
|
|
128
|
+
configDir: string;
|
|
129
|
+
uuid: string;
|
|
130
|
+
hostname: string;
|
|
131
|
+
port: number;
|
|
132
|
+
credentialsPath: string;
|
|
133
|
+
}): NamedTunnelConfig;
|
|
134
|
+
/**
|
|
135
|
+
* Reads + validates the config written by writeTunnelConfig. Returns null
|
|
136
|
+
* if absent or malformed.
|
|
137
|
+
*/
|
|
138
|
+
export declare function readNamedTunnelConfig(configDir: string): NamedTunnelConfig | null;
|
|
139
|
+
export declare function clearNamedTunnelConfig(configDir: string): boolean;
|
|
140
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflared named-tunnel provider.
|
|
3
|
+
*
|
|
4
|
+
* Runs a persistent Cloudflare tunnel that routes a user-owned hostname
|
|
5
|
+
* (e.g. https://mcp.example.com) to the local daemon HTTP port. Unlike
|
|
6
|
+
* cloudflared-quick, the URL is stable across restarts.
|
|
7
|
+
*
|
|
8
|
+
* Setup (one-time, handled by 8.4.3 UI + 8.4.4 CLI) produces:
|
|
9
|
+
* - ~/.cloudflared/cert.pem (origin cert from `cloudflared login`)
|
|
10
|
+
* - ~/.cloudflared/<uuid>.json (tunnel credentials, written by create)
|
|
11
|
+
* - <configDir>/cloudflared-named.yml (managed ingress config we serialize)
|
|
12
|
+
*
|
|
13
|
+
* Port-drift rewrite (load-bearing): the managed YAML embeds the daemon's
|
|
14
|
+
* loopback port. The daemon picks a fresh OS-assigned port on most restarts,
|
|
15
|
+
* so the persisted port is almost always stale by the time start() runs. We
|
|
16
|
+
* rewrite the managed YAML with the current port on every start() — idempotent
|
|
17
|
+
* atomic writes are cheap, forgetting the rewrite routes cloudflared to a
|
|
18
|
+
* dead port.
|
|
19
|
+
*
|
|
20
|
+
* The managed YAML is treated as provider-owned — hand-edits to add extra
|
|
21
|
+
* ingress rules WILL be silently dropped on the next start() because we
|
|
22
|
+
* serialize only the four canonical keys (tunnel / credentials-file /
|
|
23
|
+
* hostname / service). Warning on drift is deferred to 8.4.3.
|
|
24
|
+
*/
|
|
25
|
+
import { spawn as nodeSpawn } from "node:child_process";
|
|
26
|
+
import type { TunnelProvider } from "./types.js";
|
|
27
|
+
type SpawnFn = typeof nodeSpawn;
|
|
28
|
+
interface ProviderDependencies {
|
|
29
|
+
spawn?: SpawnFn;
|
|
30
|
+
homedir?: () => string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build a provider bound to a specific dependency set. The exported
|
|
34
|
+
* singleton uses node defaults; tests construct a one-off via
|
|
35
|
+
* createCloudflaredNamedProvider({ dependencies: { spawn: fakeSpawn } }).
|
|
36
|
+
*/
|
|
37
|
+
export declare function createCloudflaredNamedProvider(options?: {
|
|
38
|
+
dependencies?: ProviderDependencies;
|
|
39
|
+
}): TunnelProvider;
|
|
40
|
+
/**
|
|
41
|
+
* Default singleton wired to node's real spawn. Registered in the provider
|
|
42
|
+
* registry; tests use createCloudflaredNamedProvider() for DI.
|
|
43
|
+
*/
|
|
44
|
+
export declare const cloudflaredNamedProvider: TunnelProvider;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Quick Tunnel provider — wraps the existing binary-based flow.
|
|
3
|
+
*
|
|
4
|
+
* Ephemeral subdomain on *.trycloudflare.com. Zero configuration required
|
|
5
|
+
* beyond installing the binary (handled via `daemon install-tunnel`).
|
|
6
|
+
*/
|
|
7
|
+
import type { TunnelProvider } from "./types.js";
|
|
8
|
+
export declare const cloudflaredQuickProvider: TunnelProvider;
|