devframe 0.8.0 → 0.8.1
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/dist/adapters/build.d.mts +1 -1
- package/dist/adapters/cac.d.mts +1 -1
- package/dist/adapters/cac.mjs +1 -1
- package/dist/adapters/cli.d.mts +1 -1
- package/dist/adapters/cli.mjs +1 -1
- package/dist/adapters/dev.d.mts +2 -2
- package/dist/adapters/dev.mjs +1 -1
- package/dist/adapters/embedded.d.mts +1 -1
- package/dist/adapters/mcp.d.mts +11 -5
- package/dist/adapters/mcp.mjs +1 -1
- package/dist/{cac-B-R_tDGJ.mjs → cac-B1LM3zit.mjs} +1 -1
- package/dist/cli/main.mjs +2 -1
- package/dist/client/index.d.mts +37 -8
- package/dist/client/index.mjs +41 -15
- package/dist/constants.d.mts +13 -6
- package/dist/constants.mjs +13 -6
- package/dist/{context-C9Cgm1hP.mjs → context-CQefP2jR.mjs} +2 -1
- package/dist/{context-7BbaIUSI.d.mts → context-DFzmxCLa.d.mts} +1 -1
- package/dist/{revoke-BtQDKTp7.mjs → crypto-token-XCqTSMg9.mjs} +1 -35
- package/dist/{dev-D4M7Veo7.mjs → dev-BSHFZGZr.mjs} +2 -2
- package/dist/{devframe-Dsjn_Xtq.d.mts → devframe-qgCKL683.d.mts} +10 -1
- package/dist/{fetch-BZyK4v6W.mjs → fetch-CK0S253E.mjs} +5 -4
- package/dist/helpers/vite.d.mts +15 -14
- package/dist/helpers/vite.mjs +7 -7
- package/dist/{http-BghOqfTD.mjs → http-Dvgxujcu.mjs} +1 -1
- package/dist/{index-CeBbry0R.d.mts → index-B7RgLw--.d.mts} +11 -6
- package/dist/index.d.mts +2 -2
- package/dist/node/auth.d.mts +2 -2
- package/dist/node/auth.mjs +2 -2
- package/dist/node/hub-internals.d.mts +2 -2
- package/dist/node/hub-internals.mjs +1 -1
- package/dist/node/index.d.mts +23 -3
- package/dist/node/index.mjs +3 -3
- package/dist/recipes/common-rpc-functions.d.mts +1 -1
- package/dist/recipes/interactive-auth.d.mts +2 -2
- package/dist/recipes/interactive-auth.mjs +11 -3
- package/dist/revoke-DZbJ7Cl0.mjs +35 -0
- package/dist/rpc/transports/ws-server.d.mts +2 -2
- package/dist/rpc/transports/ws-server.mjs +49 -2
- package/dist/{server-CcAPxuoT.mjs → server-CXaDDUl0.mjs} +1 -1
- package/dist/{server-VLQJouOO.d.mts → server-E9z4ZWbd.d.mts} +4 -3
- package/dist/{state-WX3HT5a3.mjs → state-BeUHDDjk.mjs} +13 -6
- package/dist/types/index.d.mts +2 -2
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/shared-state.d.mts +1 -1
- package/dist/utils/streaming-channel.d.mts +1 -1
- package/dist/{ws-server-D_Vjtums.d.mts → ws-server-J-zkiOyp.d.mts} +23 -2
- package/package.json +1 -1
- package/skills/devframe/SKILL.md +3 -2
package/dist/adapters/cac.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ft as InferCliFlags, It as defineCliFlags, Lt as parseCliFlags, Pt as CliFlagsSchema, r as DevframeDefinition } from "../devframe-
|
|
1
|
+
import { Ft as InferCliFlags, It as defineCliFlags, Lt as parseCliFlags, Pt as CliFlagsSchema, r as DevframeDefinition } from "../devframe-qgCKL683.mjs";
|
|
2
2
|
import { CAC } from "cac";
|
|
3
3
|
import { H3 } from "h3";
|
|
4
4
|
//#region src/adapters/cac.d.ts
|
package/dist/adapters/cac.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as defineCliFlags, r as parseCliFlags, t as createCac } from "../cac-
|
|
1
|
+
import { n as defineCliFlags, r as parseCliFlags, t as createCac } from "../cac-B1LM3zit.mjs";
|
|
2
2
|
export { createCac, defineCliFlags, parseCliFlags };
|
package/dist/adapters/cli.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ft as InferCliFlags, It as defineCliFlags, Lt as parseCliFlags, Pt as CliFlagsSchema } from "../devframe-
|
|
1
|
+
import { Ft as InferCliFlags, It as defineCliFlags, Lt as parseCliFlags, Pt as CliFlagsSchema } from "../devframe-qgCKL683.mjs";
|
|
2
2
|
import { CacHandle, CreateCacOptions, createCac } from "./cac.mjs";
|
|
3
3
|
//#region src/adapters/cli.d.ts
|
|
4
4
|
/** @deprecated Use `createCac` from `devframe/adapters/cac` instead. */
|
package/dist/adapters/cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as defineCliFlags, r as parseCliFlags, t as createCac } from "../cac-
|
|
1
|
+
import { n as defineCliFlags, r as parseCliFlags, t as createCac } from "../cac-B1LM3zit.mjs";
|
|
2
2
|
//#region src/adapters/cli.ts
|
|
3
3
|
/** @deprecated Use `createCac` from `devframe/adapters/cac` instead. */
|
|
4
4
|
const createCli = createCac;
|
package/dist/adapters/dev.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as ConnectionMeta, d as McpRouteOptions, p as DevframeAuthHandler, r as DevframeDefinition, u as DevframeWsOptions } from "../devframe-
|
|
2
|
-
import { n as StartedServer } from "../server-
|
|
1
|
+
import { _ as ConnectionMeta, d as McpRouteOptions, p as DevframeAuthHandler, r as DevframeDefinition, u as DevframeWsOptions } from "../devframe-qgCKL683.mjs";
|
|
2
|
+
import { n as StartedServer } from "../server-E9z4ZWbd.mjs";
|
|
3
3
|
import { H3 } from "h3";
|
|
4
4
|
//#region src/adapters/dev.d.ts
|
|
5
5
|
interface CreateDevServerOptions {
|
package/dist/adapters/dev.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as resolveDevServerPort, r as resolveMcpConnectionMeta, t as createDevServer } from "../dev-
|
|
1
|
+
import { n as resolveDevServerPort, r as resolveMcpConnectionMeta, t as createDevServer } from "../dev-BSHFZGZr.mjs";
|
|
2
2
|
export { createDevServer, resolveDevServerPort, resolveMcpConnectionMeta };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as DevframeNodeContext, r as DevframeDefinition } from "../devframe-
|
|
1
|
+
import { b as DevframeNodeContext, r as DevframeDefinition } from "../devframe-qgCKL683.mjs";
|
|
2
2
|
//#region src/adapters/embedded.d.ts
|
|
3
3
|
interface CreateEmbeddedOptions {
|
|
4
4
|
/** Target context the devframe is registered into. Required. */
|
package/dist/adapters/mcp.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as DevframeNodeContext, r as DevframeDefinition } from "../devframe-
|
|
1
|
+
import { b as DevframeNodeContext, r as DevframeDefinition } from "../devframe-qgCKL683.mjs";
|
|
2
2
|
import "@modelcontextprotocol/server";
|
|
3
3
|
//#region src/adapters/mcp/build-server.d.ts
|
|
4
4
|
interface CreateMcpServerOptions {
|
|
@@ -47,7 +47,12 @@ interface CreateMcpFetchHandlerOptions {
|
|
|
47
47
|
exposeSharedState: boolean | ((key: string) => boolean);
|
|
48
48
|
/**
|
|
49
49
|
* Origin allow-list beyond the loopback default. `false` disables the
|
|
50
|
-
* origin gate entirely. Default: loopback-only
|
|
50
|
+
* origin gate entirely. Default: loopback-only.
|
|
51
|
+
*
|
|
52
|
+
* Unlike the WS transport, the MCP route does **not** allow `Origin`-less
|
|
53
|
+
* requests: a route-based endpoint is reachable by any local process, so a
|
|
54
|
+
* request must carry an `Origin` that passes the gate. Native clients
|
|
55
|
+
* (e.g. `devframe connect`) send their loopback origin explicitly.
|
|
51
56
|
*/
|
|
52
57
|
allowedOrigins?: readonly string[] | false;
|
|
53
58
|
}
|
|
@@ -71,9 +76,10 @@ interface McpFetchHandler {
|
|
|
71
76
|
* and MCP server (built from the shared, live `ctx` via
|
|
72
77
|
* `buildMcpServerFromContext`), correlated by the `Mcp-Session-Id` header: an
|
|
73
78
|
* `initialize` POST spins up a session; later requests route to it; a `DELETE`
|
|
74
|
-
* (or client disconnect) tears it down. The origin gate
|
|
75
|
-
* loopback-default DNS-rebinding protection
|
|
76
|
-
*
|
|
79
|
+
* (or client disconnect) tears it down. The origin gate guards every request:
|
|
80
|
+
* loopback-default DNS-rebinding protection that — unlike the WS upgrade's
|
|
81
|
+
* `isAllowedOrigin` — also rejects `Origin`-less requests, so a route-based
|
|
82
|
+
* endpoint isn't reachable by an arbitrary local process.
|
|
77
83
|
*
|
|
78
84
|
* @experimental
|
|
79
85
|
*/
|
package/dist/adapters/mcp.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as createMcpServer, t as createMcpFetchHandler } from "../fetch-
|
|
1
|
+
import { n as createMcpServer, t as createMcpFetchHandler } from "../fetch-CK0S253E.mjs";
|
|
2
2
|
export { createMcpFetchHandler, createMcpServer };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as colors } from "./diagnostics-reporter-CsIG85Q5.mjs";
|
|
2
2
|
import { createBuild } from "./adapters/build.mjs";
|
|
3
|
-
import { n as resolveDevServerPort, t as createDevServer } from "./dev-
|
|
3
|
+
import { n as resolveDevServerPort, t as createDevServer } from "./dev-BSHFZGZr.mjs";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import cac$1 from "cac";
|
|
6
6
|
//#region src/adapters/flags.ts
|
package/dist/cli/main.mjs
CHANGED
|
@@ -200,7 +200,8 @@ async function call(sdk, options, args) {
|
|
|
200
200
|
});
|
|
201
201
|
}
|
|
202
202
|
async function withInstanceClient(sdk, url, fn) {
|
|
203
|
-
const
|
|
203
|
+
const origin = new URL(url).origin;
|
|
204
|
+
const transport = new sdk.StreamableHTTPClientTransport(new URL(url), { requestInit: { headers: { origin } } });
|
|
204
205
|
const client = new sdk.Client({
|
|
205
206
|
name: "devframe-connect",
|
|
206
207
|
version: "0.0.0"
|
package/dist/client/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as DevframeRpcServerFunctions, F as ScopedSharedStates, I as SettingsForNamespace, J as RpcSharedStateHost, N as ScopedRpcFn, O as DevframeSettings, P as ScopedServerFunctions, Q as DevframeRpcClientFunctions, _ as ConnectionMeta, at as StreamReader, ht as SharedState, jt as EventEmitter, ot as StreamSink, q as RpcSharedStateGetOptions } from "../devframe-
|
|
1
|
+
import { $ as DevframeRpcServerFunctions, F as ScopedSharedStates, I as SettingsForNamespace, J as RpcSharedStateHost, N as ScopedRpcFn, O as DevframeSettings, P as ScopedServerFunctions, Q as DevframeRpcClientFunctions, _ as ConnectionMeta, at as StreamReader, ht as SharedState, jt as EventEmitter, ot as StreamSink, q as RpcSharedStateGetOptions } from "../devframe-qgCKL683.mjs";
|
|
2
2
|
import { _ as RpcFunctionDefinition, w as RpcFunctionsCollector } from "../types-CnJSgRVa.mjs";
|
|
3
3
|
import { E as RpcCacheOptions, T as RpcCacheManager } from "../index-Dbw1p5ch.mjs";
|
|
4
4
|
import { t as WsRpcChannelOptions } from "../ws-client-B6Tnr0ic.mjs";
|
|
@@ -26,6 +26,12 @@ interface SetupDevframeConnectionOptions {
|
|
|
26
26
|
/** Override the locally stored auth token. */
|
|
27
27
|
authToken?: string;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Allow an external viewer to connect by registering its browser origin with
|
|
31
|
+
* the Devframe host. Returns `false` if the host did not provide an origin
|
|
32
|
+
* registration token.
|
|
33
|
+
*/
|
|
34
|
+
declare function registerDevframeViewerOrigin(connection: DevframeConnection, origin?: any): Promise<boolean>;
|
|
29
35
|
/**
|
|
30
36
|
* Return connection information previously prepared in this window or an
|
|
31
37
|
* accessible parent window.
|
|
@@ -377,15 +383,16 @@ declare function getDevframeRpcClient(options?: DevframeRpcClientOptions): Promi
|
|
|
377
383
|
//#endregion
|
|
378
384
|
//#region src/client/otp.d.ts
|
|
379
385
|
/**
|
|
380
|
-
* Read a one-time authentication code (OTP) from the current page URL's
|
|
381
|
-
*
|
|
386
|
+
* Read a one-time authentication code (OTP) from the current page URL's
|
|
387
|
+
* fragment, without side effects. Returns `undefined` when the parameter is
|
|
388
|
+
* absent.
|
|
382
389
|
*/
|
|
383
390
|
declare function readOtpFromUrl(param?: string): string | undefined;
|
|
384
391
|
/**
|
|
385
|
-
* Read the one-time code from the page URL and remove it from the
|
|
386
|
-
* (and the current history entry), so the single-use code isn't
|
|
387
|
-
* URL, browser history, or a `Referer`. Returns the code, or
|
|
388
|
-
* absent.
|
|
392
|
+
* Read the one-time code from the page URL fragment and remove it from the
|
|
393
|
+
* address bar (and the current history entry), so the single-use code isn't
|
|
394
|
+
* left in the URL, browser history, or a `Referer`. Returns the code, or
|
|
395
|
+
* `undefined` when absent.
|
|
389
396
|
*/
|
|
390
397
|
declare function consumeOtpFromUrl(param?: string): string | undefined;
|
|
391
398
|
/**
|
|
@@ -402,6 +409,28 @@ declare function authenticateWithUrlOtp(rpc: Pick<DevframeRpcClient, 'isTrusted'
|
|
|
402
409
|
param?: string;
|
|
403
410
|
}): Promise<boolean>;
|
|
404
411
|
//#endregion
|
|
412
|
+
//#region src/client/rpc-ws.d.ts
|
|
413
|
+
/** Minimal subset of `window.location` needed to resolve a WS URL. */
|
|
414
|
+
interface WsUrlLocation {
|
|
415
|
+
protocol: string;
|
|
416
|
+
host: string;
|
|
417
|
+
hostname: string;
|
|
418
|
+
href: string;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Resolve a {@link ConnectionMeta.websocket} descriptor into a concrete
|
|
422
|
+
* `ws(s)://` URL.
|
|
423
|
+
*
|
|
424
|
+
* The object / relative-path forms connect to the page's own origin (only the
|
|
425
|
+
* `http`→`ws` protocol swap is applied), resolving the path against where
|
|
426
|
+
* `__connection.json` was loaded. This is deliberately host-agnostic so the
|
|
427
|
+
* connection survives a reverse proxy that changes the domain or port — the
|
|
428
|
+
* client trusts its own location, never a server-baked hostname. An explicit
|
|
429
|
+
* `port`/`host` (or a full `ws(s)://` URL string) opts into a cross-origin
|
|
430
|
+
* endpoint, e.g. a side-car server on its own port.
|
|
431
|
+
*/
|
|
432
|
+
declare function resolveWsUrl(websocket: ConnectionMeta['websocket'], metaBaseUrl: string, loc: WsUrlLocation): string;
|
|
433
|
+
//#endregion
|
|
405
434
|
//#region src/client/settings.d.ts
|
|
406
435
|
/**
|
|
407
436
|
* Build the client-side `settings` surface for a scope namespace. Mirrors
|
|
@@ -412,4 +441,4 @@ declare function createClientSettings<T extends Record<string, any> = Record<str
|
|
|
412
441
|
//#region src/client/index.d.ts
|
|
413
442
|
declare const connectDevframe: typeof getDevframeRpcClient;
|
|
414
443
|
//#endregion
|
|
415
|
-
export { DevframeClientRpcHost, DevframeConnection, DevframeConnectionError, DevframeConnectionErrorKind, DevframeConnectionStatus, DevframeRpcClient, DevframeRpcClientCall, DevframeRpcClientCallEvent, DevframeRpcClientCallOptional, DevframeRpcClientMode, DevframeRpcClientOptions, DevframeRpcContext, DevframeScopedClientContext, DevframeScopedClientRpc, DevframeScopedClientStreamingHost, RpcClientEvents, RpcStreamingClientHost, SetupDevframeConnectionOptions, StreamingSubscribeOptions, authenticateWithUrlOtp, connectDevframe, consumeOtpFromUrl, createClientSettings, createRpcStreamingClientHost, createScopedClientContext, getDevframeConnection, getDevframeRpcClient, isCallableStatus, readOtpFromUrl, setupDevframeConnection };
|
|
444
|
+
export { DevframeClientRpcHost, DevframeConnection, DevframeConnectionError, DevframeConnectionErrorKind, DevframeConnectionStatus, DevframeRpcClient, DevframeRpcClientCall, DevframeRpcClientCallEvent, DevframeRpcClientCallOptional, DevframeRpcClientMode, DevframeRpcClientOptions, DevframeRpcContext, DevframeScopedClientContext, DevframeScopedClientRpc, DevframeScopedClientStreamingHost, RpcClientEvents, RpcStreamingClientHost, SetupDevframeConnectionOptions, StreamingSubscribeOptions, type WsUrlLocation, authenticateWithUrlOtp, connectDevframe, consumeOtpFromUrl, createClientSettings, createRpcStreamingClientHost, createScopedClientContext, getDevframeConnection, getDevframeRpcClient, isCallableStatus, readOtpFromUrl, registerDevframeViewerOrigin, resolveWsUrl, setupDevframeConnection };
|
package/dist/client/index.mjs
CHANGED
|
@@ -20,11 +20,14 @@ const DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
|
|
|
20
20
|
const DEVFRAME_CONNECTION_KEY = "__DEVFRAME_CONNECTION__";
|
|
21
21
|
const DEVFRAME_RPC_DUMP_MANIFEST_FILENAME = "__rpc-dump/index.json";
|
|
22
22
|
/**
|
|
23
|
-
* Page-URL
|
|
24
|
-
* "magic link" auth. A host can print a link like
|
|
25
|
-
* the client reads the code, exchanges it for a
|
|
26
|
-
* from the URL.
|
|
27
|
-
*
|
|
23
|
+
* Page-URL **fragment** parameter carrying a one-time authentication code (OTP)
|
|
24
|
+
* for "magic link" auth. A host can print a link like
|
|
25
|
+
* `<origin>/#devframe_otp=<code>`; the client reads the code, exchanges it for a
|
|
26
|
+
* token, and strips the parameter from the URL. The code rides the fragment
|
|
27
|
+
* (never the query string) so the browser never transmits it to the server,
|
|
28
|
+
* keeping it out of access logs and `Referer` headers. See `buildOtpAuthUrl`
|
|
29
|
+
* (node) and the `authenticateWithUrlOtp` / `consumeOtpFromUrl` client utilities
|
|
30
|
+
* (or `connectDevframe`'s `otpParam`).
|
|
28
31
|
*/
|
|
29
32
|
const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
|
|
30
33
|
/**
|
|
@@ -35,6 +38,10 @@ const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
|
|
|
35
38
|
* trusted before its own `anonymous:devframe:auth` handshake call arrives.
|
|
36
39
|
*/
|
|
37
40
|
const DEVFRAME_AUTH_TOKEN_QUERY_PARAM = "devframe_auth_token";
|
|
41
|
+
/** External viewer origin requested during connection bootstrap. */
|
|
42
|
+
const DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM = "devframe_viewer_origin";
|
|
43
|
+
/** Token that authorizes an external viewer origin registration. */
|
|
44
|
+
const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
|
|
38
45
|
//#endregion
|
|
39
46
|
//#region src/utils/diagnostics-reporter.ts
|
|
40
47
|
const formatAnsi = ansiFormatter(colors);
|
|
@@ -428,6 +435,21 @@ function storeAuthToken(token) {
|
|
|
428
435
|
}
|
|
429
436
|
//#endregion
|
|
430
437
|
//#region src/client/connection.ts
|
|
438
|
+
/**
|
|
439
|
+
* Allow an external viewer to connect by registering its browser origin with
|
|
440
|
+
* the Devframe host. Returns `false` if the host did not provide an origin
|
|
441
|
+
* registration token.
|
|
442
|
+
*/
|
|
443
|
+
async function registerDevframeViewerOrigin(connection, origin = globalThis.location?.origin) {
|
|
444
|
+
const token = connection.connectionMeta.viewerOriginToken;
|
|
445
|
+
if (!token || !origin) return false;
|
|
446
|
+
const url = new URL(connection.metaBaseUrl);
|
|
447
|
+
url.searchParams.set(DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, origin);
|
|
448
|
+
url.searchParams.set(DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, token);
|
|
449
|
+
const response = await fetch(url, { cache: "no-store" });
|
|
450
|
+
if (!response.ok) throw new Error(`Failed to register external viewer origin (${response.status}).`);
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
431
453
|
function resolveMetaBaseUrl(baseURL) {
|
|
432
454
|
const metaPath = withBase(DEVFRAME_CONNECTION_META_FILENAME, baseURL);
|
|
433
455
|
try {
|
|
@@ -530,12 +552,14 @@ function isCallableStatus(status) {
|
|
|
530
552
|
//#endregion
|
|
531
553
|
//#region src/client/otp.ts
|
|
532
554
|
/**
|
|
533
|
-
* Read a one-time authentication code (OTP) from the current page URL's
|
|
534
|
-
*
|
|
555
|
+
* Read a one-time authentication code (OTP) from the current page URL's
|
|
556
|
+
* fragment, without side effects. Returns `undefined` when the parameter is
|
|
557
|
+
* absent.
|
|
535
558
|
*/
|
|
536
559
|
function readOtpFromUrl(param = DEVFRAME_OTP_URL_PARAM) {
|
|
537
560
|
try {
|
|
538
|
-
|
|
561
|
+
const hash = globalThis.location?.hash?.replace(/^#/, "") ?? "";
|
|
562
|
+
return new URLSearchParams(hash).get(param) || void 0;
|
|
539
563
|
} catch {
|
|
540
564
|
return;
|
|
541
565
|
}
|
|
@@ -543,16 +567,18 @@ function readOtpFromUrl(param = DEVFRAME_OTP_URL_PARAM) {
|
|
|
543
567
|
function stripParamFromUrl(param) {
|
|
544
568
|
try {
|
|
545
569
|
const url = new URL(globalThis.location.href);
|
|
546
|
-
|
|
547
|
-
|
|
570
|
+
const fragment = new URLSearchParams(url.hash.replace(/^#/, ""));
|
|
571
|
+
if (!fragment.has(param)) return;
|
|
572
|
+
fragment.delete(param);
|
|
573
|
+
url.hash = fragment.toString();
|
|
548
574
|
globalThis.history?.replaceState(globalThis.history.state, "", url.href);
|
|
549
575
|
} catch {}
|
|
550
576
|
}
|
|
551
577
|
/**
|
|
552
|
-
* Read the one-time code from the page URL and remove it from the
|
|
553
|
-
* (and the current history entry), so the single-use code isn't
|
|
554
|
-
* URL, browser history, or a `Referer`. Returns the code, or
|
|
555
|
-
* absent.
|
|
578
|
+
* Read the one-time code from the page URL fragment and remove it from the
|
|
579
|
+
* address bar (and the current history entry), so the single-use code isn't
|
|
580
|
+
* left in the URL, browser history, or a `Referer`. Returns the code, or
|
|
581
|
+
* `undefined` when absent.
|
|
556
582
|
*/
|
|
557
583
|
function consumeOtpFromUrl(param = DEVFRAME_OTP_URL_PARAM) {
|
|
558
584
|
const code = readOtpFromUrl(param);
|
|
@@ -2588,4 +2614,4 @@ async function getDevframeRpcClient(options = {}) {
|
|
|
2588
2614
|
//#region src/client/index.ts
|
|
2589
2615
|
const connectDevframe = getDevframeRpcClient;
|
|
2590
2616
|
//#endregion
|
|
2591
|
-
export { DevframeConnectionError, authenticateWithUrlOtp, connectDevframe, consumeOtpFromUrl, createClientSettings, createRpcStreamingClientHost, createScopedClientContext, getDevframeConnection, getDevframeRpcClient, isCallableStatus, readOtpFromUrl, setupDevframeConnection };
|
|
2617
|
+
export { DevframeConnectionError, authenticateWithUrlOtp, connectDevframe, consumeOtpFromUrl, createClientSettings, createRpcStreamingClientHost, createScopedClientContext, getDevframeConnection, getDevframeRpcClient, isCallableStatus, readOtpFromUrl, registerDevframeViewerOrigin, resolveWsUrl, setupDevframeConnection };
|
package/dist/constants.d.mts
CHANGED
|
@@ -35,11 +35,14 @@ declare const DEVFRAME_RPC_DUMP_DIRNAME = "__rpc-dump";
|
|
|
35
35
|
*/
|
|
36
36
|
declare const REMOTE_CONNECTION_KEY = "devframe-remote-connection";
|
|
37
37
|
/**
|
|
38
|
-
* Page-URL
|
|
39
|
-
* "magic link" auth. A host can print a link like
|
|
40
|
-
* the client reads the code, exchanges it for a
|
|
41
|
-
* from the URL.
|
|
42
|
-
*
|
|
38
|
+
* Page-URL **fragment** parameter carrying a one-time authentication code (OTP)
|
|
39
|
+
* for "magic link" auth. A host can print a link like
|
|
40
|
+
* `<origin>/#devframe_otp=<code>`; the client reads the code, exchanges it for a
|
|
41
|
+
* token, and strips the parameter from the URL. The code rides the fragment
|
|
42
|
+
* (never the query string) so the browser never transmits it to the server,
|
|
43
|
+
* keeping it out of access logs and `Referer` headers. See `buildOtpAuthUrl`
|
|
44
|
+
* (node) and the `authenticateWithUrlOtp` / `consumeOtpFromUrl` client utilities
|
|
45
|
+
* (or `connectDevframe`'s `otpParam`).
|
|
43
46
|
*/
|
|
44
47
|
declare const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
|
|
45
48
|
/**
|
|
@@ -50,6 +53,10 @@ declare const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
|
|
|
50
53
|
* trusted before its own `anonymous:devframe:auth` handshake call arrives.
|
|
51
54
|
*/
|
|
52
55
|
declare const DEVFRAME_AUTH_TOKEN_QUERY_PARAM = "devframe_auth_token";
|
|
56
|
+
/** External viewer origin requested during connection bootstrap. */
|
|
57
|
+
declare const DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM = "devframe_viewer_origin";
|
|
58
|
+
/** Token that authorizes an external viewer origin registration. */
|
|
59
|
+
declare const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
|
|
53
60
|
/**
|
|
54
61
|
* Prefix that marks an RPC method as callable before a connection is
|
|
55
62
|
* trusted. This is the *only* rule the pre-trust gate applies — there is no
|
|
@@ -66,4 +73,4 @@ declare const ANONYMOUS_RPC_PREFIX = "anonymous:";
|
|
|
66
73
|
*/
|
|
67
74
|
declare function isAnonymousRpcMethod(name: string): boolean;
|
|
68
75
|
//#endregion
|
|
69
|
-
export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DIRNAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_MOUNT_PATH, DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
|
|
76
|
+
export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DIRNAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_MOUNT_PATH, DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
|
package/dist/constants.mjs
CHANGED
|
@@ -35,11 +35,14 @@ const DEVFRAME_RPC_DUMP_DIRNAME = "__rpc-dump";
|
|
|
35
35
|
*/
|
|
36
36
|
const REMOTE_CONNECTION_KEY = "devframe-remote-connection";
|
|
37
37
|
/**
|
|
38
|
-
* Page-URL
|
|
39
|
-
* "magic link" auth. A host can print a link like
|
|
40
|
-
* the client reads the code, exchanges it for a
|
|
41
|
-
* from the URL.
|
|
42
|
-
*
|
|
38
|
+
* Page-URL **fragment** parameter carrying a one-time authentication code (OTP)
|
|
39
|
+
* for "magic link" auth. A host can print a link like
|
|
40
|
+
* `<origin>/#devframe_otp=<code>`; the client reads the code, exchanges it for a
|
|
41
|
+
* token, and strips the parameter from the URL. The code rides the fragment
|
|
42
|
+
* (never the query string) so the browser never transmits it to the server,
|
|
43
|
+
* keeping it out of access logs and `Referer` headers. See `buildOtpAuthUrl`
|
|
44
|
+
* (node) and the `authenticateWithUrlOtp` / `consumeOtpFromUrl` client utilities
|
|
45
|
+
* (or `connectDevframe`'s `otpParam`).
|
|
43
46
|
*/
|
|
44
47
|
const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
|
|
45
48
|
/**
|
|
@@ -50,6 +53,10 @@ const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
|
|
|
50
53
|
* trusted before its own `anonymous:devframe:auth` handshake call arrives.
|
|
51
54
|
*/
|
|
52
55
|
const DEVFRAME_AUTH_TOKEN_QUERY_PARAM = "devframe_auth_token";
|
|
56
|
+
/** External viewer origin requested during connection bootstrap. */
|
|
57
|
+
const DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM = "devframe_viewer_origin";
|
|
58
|
+
/** Token that authorizes an external viewer origin registration. */
|
|
59
|
+
const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
|
|
53
60
|
/**
|
|
54
61
|
* Prefix that marks an RPC method as callable before a connection is
|
|
55
62
|
* trusted. This is the *only* rule the pre-trust gate applies — there is no
|
|
@@ -68,4 +75,4 @@ function isAnonymousRpcMethod(name) {
|
|
|
68
75
|
return name.startsWith(ANONYMOUS_RPC_PREFIX);
|
|
69
76
|
}
|
|
70
77
|
//#endregion
|
|
71
|
-
export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DIRNAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_MOUNT_PATH, DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
|
|
78
|
+
export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DIRNAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_MOUNT_PATH, DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { n as randomToken } from "./crypto-token-XCqTSMg9.mjs";
|
|
1
2
|
import { t as createStorage } from "./storage-Dzoc3NVC.mjs";
|
|
2
|
-
import {
|
|
3
|
+
import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-DZbJ7Cl0.mjs";
|
|
3
4
|
import { join } from "pathe";
|
|
4
5
|
//#region src/node/hub-internals/context.ts
|
|
5
6
|
const internalContextMap = /* @__PURE__ */ new WeakMap();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as DevframeNodeContext, ht as SharedState } from "./devframe-
|
|
1
|
+
import { b as DevframeNodeContext, ht as SharedState } from "./devframe-qgCKL683.mjs";
|
|
2
2
|
//#region src/node/hub-internals/context.d.ts
|
|
3
3
|
interface InternalAnonymousAuthStorage {
|
|
4
4
|
trusted: Record<string, {
|
|
@@ -42,38 +42,4 @@ function timingSafeEqual(a, b) {
|
|
|
42
42
|
return mismatch === 0;
|
|
43
43
|
}
|
|
44
44
|
//#endregion
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Flip `isTrusted` to false on any live WS clients connected with `token`
|
|
48
|
-
* and broadcast the `auth:revoked` event so they can react.
|
|
49
|
-
*
|
|
50
|
-
* Shared between persisted-auth revocation and remote-dock token revocation.
|
|
51
|
-
*/
|
|
52
|
-
async function revokeActiveConnectionsForToken(context, token) {
|
|
53
|
-
const rpcHost = context.rpc;
|
|
54
|
-
if (!rpcHost?._rpcGroup) return;
|
|
55
|
-
const affectedSessionIds = /* @__PURE__ */ new Set();
|
|
56
|
-
for (const client of rpcHost._rpcGroup.clients) if (client.$meta.clientAuthToken === token) {
|
|
57
|
-
affectedSessionIds.add(client.$meta.id);
|
|
58
|
-
client.$meta.isTrusted = false;
|
|
59
|
-
client.$meta.clientAuthToken = void 0;
|
|
60
|
-
}
|
|
61
|
-
if (affectedSessionIds.size === 0) return;
|
|
62
|
-
await rpcHost.broadcast({
|
|
63
|
-
method: "devframe:auth:revoked",
|
|
64
|
-
args: [],
|
|
65
|
-
filter: (client) => affectedSessionIds.has(client.$meta.id)
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Revoke an auth token: remove from storage and notify all connected clients
|
|
70
|
-
* using this token that they are no longer trusted.
|
|
71
|
-
*/
|
|
72
|
-
async function revokeAuthToken(context, storage, token) {
|
|
73
|
-
storage.mutate((state) => {
|
|
74
|
-
delete state.trusted[token];
|
|
75
|
-
});
|
|
76
|
-
await revokeActiveConnectionsForToken(context, token);
|
|
77
|
-
}
|
|
78
|
-
//#endregion
|
|
79
|
-
export { timingSafeEqual as a, randomToken as i, revokeAuthToken as n, randomDigits as r, revokeActiveConnectionsForToken as t };
|
|
45
|
+
export { randomToken as n, timingSafeEqual as r, randomDigits as t };
|
|
@@ -2,7 +2,7 @@ import { DEVFRAME_CONNECTION_META_FILENAME } from "./constants.mjs";
|
|
|
2
2
|
import { n as createHostContext, t as createH3DevframeHost } from "./host-h3-Kz7t5Xab.mjs";
|
|
3
3
|
import { t as diagnostics } from "./diagnostics-B5-qHeqD.mjs";
|
|
4
4
|
import { r as registerDevframeInstance } from "./instance-registry-D6fxYu36.mjs";
|
|
5
|
-
import { i as normalizeHttpServerUrl, t as startHttpAndWs } from "./server-
|
|
5
|
+
import { i as normalizeHttpServerUrl, t as startHttpAndWs } from "./server-CXaDDUl0.mjs";
|
|
6
6
|
import { n as resolveBasePath, t as normalizeBasePath } from "./_shared-bWRzeSa0.mjs";
|
|
7
7
|
import { t as open } from "./open-Deb5xmIT.mjs";
|
|
8
8
|
import { mountStaticHandler } from "./utils/serve-static.mjs";
|
|
@@ -295,7 +295,7 @@ async function createDevServer(def, options = {}) {
|
|
|
295
295
|
const mcpPath = joinURL(basePath, mcpRoute);
|
|
296
296
|
let mountMcpHttp;
|
|
297
297
|
try {
|
|
298
|
-
({mountMcpHttp} = await import("./http-
|
|
298
|
+
({mountMcpHttp} = await import("./http-Dvgxujcu.mjs"));
|
|
299
299
|
} catch (error) {
|
|
300
300
|
const reason = error instanceof Error ? error.message : String(error);
|
|
301
301
|
throw diagnostics.DF0017({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as RpcFunctionDefinition, v as RpcFunctionDefinitionAny } from "./types-CnJSgRVa.mjs";
|
|
2
2
|
import { w as RpcFunctionsCollectorBase } from "./index-Dbw1p5ch.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { n as DevframeNodeRpcSessionMeta } from "./ws-server-J-zkiOyp.mjs";
|
|
4
4
|
import { CAC } from "cac";
|
|
5
5
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
6
6
|
import { Peer } from "crossws";
|
|
@@ -1414,6 +1414,15 @@ interface ConnectionMeta {
|
|
|
1414
1414
|
* > from a publicly reachable static `__connection.json`.
|
|
1415
1415
|
*/
|
|
1416
1416
|
authToken?: string;
|
|
1417
|
+
/**
|
|
1418
|
+
* Session-scoped token that lets a trusted external viewer register its
|
|
1419
|
+
* browser origin before opening the WebSocket. The host includes it in the
|
|
1420
|
+
* connection metadata, which the viewer obtains through the host page.
|
|
1421
|
+
*
|
|
1422
|
+
* Treat this as a bearer credential. Keep connection metadata containing the
|
|
1423
|
+
* token same-origin until the requesting origin has been verified.
|
|
1424
|
+
*/
|
|
1425
|
+
viewerOriginToken?: string;
|
|
1417
1426
|
}
|
|
1418
1427
|
//#endregion
|
|
1419
1428
|
//#region src/types/utils.d.ts
|
|
@@ -457,9 +457,10 @@ function parseResourceUri(uri) {
|
|
|
457
457
|
* and MCP server (built from the shared, live `ctx` via
|
|
458
458
|
* `buildMcpServerFromContext`), correlated by the `Mcp-Session-Id` header: an
|
|
459
459
|
* `initialize` POST spins up a session; later requests route to it; a `DELETE`
|
|
460
|
-
* (or client disconnect) tears it down. The origin gate
|
|
461
|
-
* loopback-default DNS-rebinding protection
|
|
462
|
-
*
|
|
460
|
+
* (or client disconnect) tears it down. The origin gate guards every request:
|
|
461
|
+
* loopback-default DNS-rebinding protection that — unlike the WS upgrade's
|
|
462
|
+
* `isAllowedOrigin` — also rejects `Origin`-less requests, so a route-based
|
|
463
|
+
* endpoint isn't reachable by an arbitrary local process.
|
|
463
464
|
*
|
|
464
465
|
* @experimental
|
|
465
466
|
*/
|
|
@@ -503,7 +504,7 @@ function createMcpFetchHandler(ctx, options) {
|
|
|
503
504
|
}
|
|
504
505
|
async function handle(req) {
|
|
505
506
|
const origin = req.headers.get("origin") ?? void 0;
|
|
506
|
-
if (allowedOrigins !== false && !isAllowedOrigin(origin, allowedOrigins ?? [])) return new Response("Forbidden: origin
|
|
507
|
+
if (allowedOrigins !== false && (origin === void 0 || !isAllowedOrigin(origin, allowedOrigins ?? []))) return new Response("Forbidden: origin required", { status: 403 });
|
|
507
508
|
const sessionId = req.headers.get("mcp-session-id") ?? void 0;
|
|
508
509
|
let session = sessionId ? sessions.get(sessionId) : void 0;
|
|
509
510
|
if (!session && req.method === "POST") {
|
package/dist/helpers/vite.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as McpRouteOptions, p as DevframeAuthHandler, r as DevframeDefinition } from "../devframe-
|
|
1
|
+
import { d as McpRouteOptions, p as DevframeAuthHandler, r as DevframeDefinition } from "../devframe-qgCKL683.mjs";
|
|
2
2
|
//#region src/helpers/vite.d.ts
|
|
3
3
|
interface ViteDevBridgeOptions {
|
|
4
4
|
/**
|
|
@@ -31,16 +31,17 @@ interface ViteDevBridgeOptions {
|
|
|
31
31
|
flags?: Record<string, unknown>;
|
|
32
32
|
};
|
|
33
33
|
/**
|
|
34
|
-
* Whether the bridged devframe runs its own auth gate.
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
34
|
+
* Whether the bridged devframe runs its own auth gate. The side-car RPC
|
|
35
|
+
* server is reachable by anything that can open its socket, so it **gates by
|
|
36
|
+
* default**: when unset, authentication resolves through `createDevServer`
|
|
37
|
+
* (devframe's interactive OTP gate unless the definition's `cli.auth` opts
|
|
38
|
+
* out), and the side-car prints its code/link banner to stdout. Pass a
|
|
39
|
+
* {@link DevframeAuthHandler} to install a custom scheme, or `false` to opt
|
|
40
|
+
* out for a single-user localhost host that owns the trust boundary another
|
|
41
|
+
* way. Only applies in bridge mode (`devMiddleware`); the static-mount mode
|
|
41
42
|
* starts no RPC server.
|
|
42
43
|
*
|
|
43
|
-
* @default
|
|
44
|
+
* @default gated (devframe's interactive OTP, unless `cli.auth` opts out)
|
|
44
45
|
*/
|
|
45
46
|
auth?: boolean | DevframeAuthHandler;
|
|
46
47
|
/**
|
|
@@ -84,11 +85,11 @@ interface DevframeVitePlugin {
|
|
|
84
85
|
* host-served SPA can discover the WS endpoint via
|
|
85
86
|
* {@link connectDevframe}.
|
|
86
87
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
88
|
+
* The side-car RPC server **gates by default** (devframe's interactive OTP
|
|
89
|
+
* unless the definition's `cli.auth` opts out), printing its code/link banner
|
|
90
|
+
* to stdout, so a bridged devframe isn't silently reachable by anything that
|
|
91
|
+
* can open its socket. Pass `options.auth: false` to opt out for a single-user
|
|
92
|
+
* localhost host, or a {@link DevframeAuthHandler} for a custom scheme.
|
|
92
93
|
*
|
|
93
94
|
* Use bridge mode when integrating with frameworks that own the SPA
|
|
94
95
|
* (Nuxt, Astro, SolidStart, plain Vite apps). For the all-in-one
|
package/dist/helpers/vite.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_WS_ROUTE } from "../constan
|
|
|
2
2
|
import { t as diagnostics } from "../diagnostics-B5-qHeqD.mjs";
|
|
3
3
|
import { n as resolveBasePath, t as normalizeBasePath } from "../_shared-bWRzeSa0.mjs";
|
|
4
4
|
import { serveStaticNodeMiddleware } from "../utils/serve-static.mjs";
|
|
5
|
-
import { n as resolveDevServerPort, r as resolveMcpConnectionMeta, t as createDevServer } from "../dev-
|
|
5
|
+
import { n as resolveDevServerPort, r as resolveMcpConnectionMeta, t as createDevServer } from "../dev-BSHFZGZr.mjs";
|
|
6
6
|
import { resolve } from "pathe";
|
|
7
7
|
//#region src/helpers/vite.ts
|
|
8
8
|
/**
|
|
@@ -19,11 +19,11 @@ import { resolve } from "pathe";
|
|
|
19
19
|
* host-served SPA can discover the WS endpoint via
|
|
20
20
|
* {@link connectDevframe}.
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
22
|
+
* The side-car RPC server **gates by default** (devframe's interactive OTP
|
|
23
|
+
* unless the definition's `cli.auth` opts out), printing its code/link banner
|
|
24
|
+
* to stdout, so a bridged devframe isn't silently reachable by anything that
|
|
25
|
+
* can open its socket. Pass `options.auth: false` to opt out for a single-user
|
|
26
|
+
* localhost host, or a {@link DevframeAuthHandler} for a custom scheme.
|
|
27
27
|
*
|
|
28
28
|
* Use bridge mode when integrating with frameworks that own the SPA
|
|
29
29
|
* (Nuxt, Astro, SolidStart, plain Vite apps). For the all-in-one
|
|
@@ -58,7 +58,7 @@ function viteDevBridge(d, options = {}) {
|
|
|
58
58
|
port,
|
|
59
59
|
flags: mw.flags,
|
|
60
60
|
openBrowser: false,
|
|
61
|
-
auth: options.auth
|
|
61
|
+
auth: options.auth,
|
|
62
62
|
mcp: options.mcp
|
|
63
63
|
});
|
|
64
64
|
} catch (e) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { W as DevframeNodeRpcSession, b as DevframeNodeContext, ht as SharedState } from "./devframe-
|
|
2
|
-
import { n as InternalAnonymousAuthStorage } from "./context-
|
|
1
|
+
import { W as DevframeNodeRpcSession, b as DevframeNodeContext, ht as SharedState } from "./devframe-qgCKL683.mjs";
|
|
2
|
+
import { n as InternalAnonymousAuthStorage } from "./context-DFzmxCLa.mjs";
|
|
3
3
|
//#region src/node/auth/revoke.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Flip `isTrusted` to false on any live WS clients connected with `token`
|
|
@@ -28,10 +28,15 @@ declare function getTempAuthCode(): string;
|
|
|
28
28
|
*/
|
|
29
29
|
declare function refreshTempAuthCode(): string;
|
|
30
30
|
/**
|
|
31
|
-
* Build a "magic link" authentication URL that embeds a one-time code (OTP)
|
|
32
|
-
*
|
|
33
|
-
* it on startup (devframe stays headless, so the host prints its own
|
|
34
|
-
* Defaults to the current code; the link is subject to the same TTL.
|
|
31
|
+
* Build a "magic link" authentication URL that embeds a one-time code (OTP) in
|
|
32
|
+
* the URL **fragment**. Opening it authenticates the client without typing —
|
|
33
|
+
* print it on startup (devframe stays headless, so the host prints its own
|
|
34
|
+
* banner). Defaults to the current code; the link is subject to the same TTL.
|
|
35
|
+
*
|
|
36
|
+
* The code rides the fragment (`#devframe_otp=…`), not the query string, so it
|
|
37
|
+
* is never sent to the server, written to an access log, or leaked in a
|
|
38
|
+
* `Referer` header — the browser client reads it locally (see
|
|
39
|
+
* `consumeOtpFromUrl`). Any existing fragment parameters are preserved.
|
|
35
40
|
*/
|
|
36
41
|
declare function buildOtpAuthUrl(baseUrl: string, code?: string): string;
|
|
37
42
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { $ as DevframeRpcServerFunctions, A as DevframeSettingsStore, At as DevframeAgentHostEvents, B as DevframeDefineDiagnosticsOptions, C as DevframeServicesHost, Ct as AgentResourceContent, D as DevframeScopedStreamingHost, Dt as AgentToolProvider, E as DevframeScopedNodeRpc, Et as AgentToolInput, F as ScopedSharedStates, G as RpcBroadcastOptions, H as DevframeDiagnosticsHost, I as SettingsForNamespace, J as RpcSharedStateHost, K as RpcFunctionsHost, L as DevframeViewHost, M as ScopedClientFunctions, Mt as EventUnsubscribe, N as ScopedRpcFn, Nt as EventsMap, O as DevframeSettings, Ot as AgentToolProviderHandle, P as ScopedServerFunctions, Q as DevframeRpcClientFunctions, R as DevframeHost, S as DevframeServiceOf, St as AgentResource, T as DevframeScopedNodeContext, Tt as AgentTool, U as DevframeDiagnosticsLogger, V as DevframeDiagnosticsDefinition, W as DevframeNodeRpcSession, X as RpcStreamingChannelOptions, Y as RpcStreamingChannel, Z as RpcStreamingHost, _ as ConnectionMeta, a as DevframeDockDefaults, b as DevframeNodeContext, bt as AgentHandle, c as DevframeSetupInfo, d as McpRouteOptions, et as DevframeRpcSharedStates, f as defineDevframe, g as Thenable, h as PartialWithoutId, i as DevframeDeploymentKind, j as ScopedBroadcastOptions, jt as EventEmitter, k as DevframeSettingsRegistry, kt as DevframeAgentHost, l as DevframeSpaOptions, m as EntriesToObject, n as DevframeCliOptions, o as DevframeDuplicationStrategy, q as RpcSharedStateGetOptions, r as DevframeDefinition, s as DevframeRuntime, t as DevframeBrowserContext, u as DevframeWsOptions, v as ConnectionMetaWebsocket, w as DevframeServicesRegistry, wt as AgentResourceInput, x as DevframeServiceId, xt as AgentManifest, y as DevframeCapabilities, z as DevframeStorageScope } from "./devframe-
|
|
1
|
+
import { $ as DevframeRpcServerFunctions, A as DevframeSettingsStore, At as DevframeAgentHostEvents, B as DevframeDefineDiagnosticsOptions, C as DevframeServicesHost, Ct as AgentResourceContent, D as DevframeScopedStreamingHost, Dt as AgentToolProvider, E as DevframeScopedNodeRpc, Et as AgentToolInput, F as ScopedSharedStates, G as RpcBroadcastOptions, H as DevframeDiagnosticsHost, I as SettingsForNamespace, J as RpcSharedStateHost, K as RpcFunctionsHost, L as DevframeViewHost, M as ScopedClientFunctions, Mt as EventUnsubscribe, N as ScopedRpcFn, Nt as EventsMap, O as DevframeSettings, Ot as AgentToolProviderHandle, P as ScopedServerFunctions, Q as DevframeRpcClientFunctions, R as DevframeHost, S as DevframeServiceOf, St as AgentResource, T as DevframeScopedNodeContext, Tt as AgentTool, U as DevframeDiagnosticsLogger, V as DevframeDiagnosticsDefinition, W as DevframeNodeRpcSession, X as RpcStreamingChannelOptions, Y as RpcStreamingChannel, Z as RpcStreamingHost, _ as ConnectionMeta, a as DevframeDockDefaults, b as DevframeNodeContext, bt as AgentHandle, c as DevframeSetupInfo, d as McpRouteOptions, et as DevframeRpcSharedStates, f as defineDevframe, g as Thenable, h as PartialWithoutId, i as DevframeDeploymentKind, j as ScopedBroadcastOptions, jt as EventEmitter, k as DevframeSettingsRegistry, kt as DevframeAgentHost, l as DevframeSpaOptions, m as EntriesToObject, n as DevframeCliOptions, o as DevframeDuplicationStrategy, q as RpcSharedStateGetOptions, r as DevframeDefinition, s as DevframeRuntime, t as DevframeBrowserContext, u as DevframeWsOptions, v as ConnectionMetaWebsocket, w as DevframeServicesRegistry, wt as AgentResourceInput, x as DevframeServiceId, xt as AgentManifest, y as DevframeCapabilities, z as DevframeStorageScope } from "./devframe-qgCKL683.mjs";
|
|
2
2
|
import { C as RpcFunctionType, T as RpcReturnSchema, _ as RpcFunctionDefinition, g as RpcFunctionAgentOptions, i as RpcArgsSchema } from "./types-CnJSgRVa.mjs";
|
|
3
3
|
import "./index-Dbw1p5ch.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { n as DevframeNodeRpcSessionMeta } from "./ws-server-J-zkiOyp.mjs";
|
|
5
5
|
//#region src/define.d.ts
|
|
6
6
|
declare const defineRpcFunction: <NAME extends string, TYPE extends RpcFunctionType, ARGS extends any[], RETURN = void, const AS extends RpcArgsSchema | undefined = undefined, const RS extends RpcReturnSchema | undefined = undefined>(definition: RpcFunctionDefinition<NAME, TYPE, ARGS, RETURN, AS, RS, DevframeNodeContext>) => RpcFunctionDefinition<NAME, TYPE, ARGS, RETURN, AS, RS, DevframeNodeContext>;
|
|
7
7
|
//#endregion
|
package/dist/node/auth.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { p as DevframeAuthHandler } from "../devframe-
|
|
2
|
-
import { a as verifyAuthToken, i as refreshTempAuthCode, n as exchangeTempAuthCode, o as revokeActiveConnectionsForToken, r as getTempAuthCode, s as revokeAuthToken, t as buildOtpAuthUrl } from "../index-
|
|
1
|
+
import { p as DevframeAuthHandler } from "../devframe-qgCKL683.mjs";
|
|
2
|
+
import { a as verifyAuthToken, i as refreshTempAuthCode, n as exchangeTempAuthCode, o as revokeActiveConnectionsForToken, r as getTempAuthCode, s as revokeAuthToken, t as buildOtpAuthUrl } from "../index-B7RgLw--.mjs";
|
|
3
3
|
export { DevframeAuthHandler, buildOtpAuthUrl, exchangeTempAuthCode, getTempAuthCode, refreshTempAuthCode, revokeActiveConnectionsForToken, revokeAuthToken, verifyAuthToken };
|
package/dist/node/auth.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "../revoke-
|
|
2
|
-
import { a as verifyAuthToken, i as refreshTempAuthCode, n as exchangeTempAuthCode, r as getTempAuthCode, t as buildOtpAuthUrl } from "../state-
|
|
1
|
+
import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "../revoke-DZbJ7Cl0.mjs";
|
|
2
|
+
import { a as verifyAuthToken, i as refreshTempAuthCode, n as exchangeTempAuthCode, r as getTempAuthCode, t as buildOtpAuthUrl } from "../state-BeUHDDjk.mjs";
|
|
3
3
|
export { buildOtpAuthUrl, exchangeTempAuthCode, getTempAuthCode, refreshTempAuthCode, revokeActiveConnectionsForToken, revokeAuthToken, verifyAuthToken };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as DevframeDeploymentKind, r as DevframeDefinition } from "../devframe-
|
|
2
|
-
import { a as internalContextMap, i as getInternalContext, n as InternalAnonymousAuthStorage, r as RemoteTokenRecord, t as DevframeInternalContext } from "../context-
|
|
1
|
+
import { i as DevframeDeploymentKind, r as DevframeDefinition } from "../devframe-qgCKL683.mjs";
|
|
2
|
+
import { a as internalContextMap, i as getInternalContext, n as InternalAnonymousAuthStorage, r as RemoteTokenRecord, t as DevframeInternalContext } from "../context-DFzmxCLa.mjs";
|
|
3
3
|
//#region src/adapters/_shared.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Resolve the mount base path for a devframe's SPA. Hosted adapters
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as internalContextMap, t as getInternalContext } from "../context-
|
|
1
|
+
import { n as internalContextMap, t as getInternalContext } from "../context-CQefP2jR.mjs";
|
|
2
2
|
import { n as resolveBasePath, t as normalizeBasePath } from "../_shared-bWRzeSa0.mjs";
|
|
3
3
|
export { getInternalContext, internalContextMap, normalizeBasePath, resolveBasePath };
|
package/dist/node/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { At as DevframeAgentHostEvents, C as DevframeServicesHost, Ct as AgentResourceContent, Dt as AgentToolProvider, Et as AgentToolInput, H as DevframeDiagnosticsHost$1, J as RpcSharedStateHost, K as RpcFunctionsHost, L as DevframeViewHost$1, O as DevframeSettings, Ot as AgentToolProviderHandle, R as DevframeHost, S as DevframeServiceOf, St as AgentResource, T as DevframeScopedNodeContext, Tt as AgentTool, U as DevframeDiagnosticsLogger, Z as RpcStreamingHost, b as DevframeNodeContext, bt as AgentHandle, ht as SharedState, jt as EventEmitter, kt as DevframeAgentHost$1, wt as AgentResourceInput, x as DevframeServiceId, xt as AgentManifest } from "../devframe-
|
|
1
|
+
import { At as DevframeAgentHostEvents, C as DevframeServicesHost, Ct as AgentResourceContent, Dt as AgentToolProvider, Et as AgentToolInput, H as DevframeDiagnosticsHost$1, J as RpcSharedStateHost, K as RpcFunctionsHost, L as DevframeViewHost$1, O as DevframeSettings, Ot as AgentToolProviderHandle, R as DevframeHost, S as DevframeServiceOf, St as AgentResource, T as DevframeScopedNodeContext, Tt as AgentTool, U as DevframeDiagnosticsLogger, Z as RpcStreamingHost, b as DevframeNodeContext, bt as AgentHandle, ht as SharedState, jt as EventEmitter, kt as DevframeAgentHost$1, wt as AgentResourceInput, x as DevframeServiceId, xt as AgentManifest } from "../devframe-qgCKL683.mjs";
|
|
2
2
|
import { v as RpcFunctionDefinitionAny } from "../types-CnJSgRVa.mjs";
|
|
3
3
|
import "../index-Dbw1p5ch.mjs";
|
|
4
|
-
import { n as StartedServer, r as startHttpAndWs, t as StartHttpAndWsOptions } from "../server-
|
|
4
|
+
import { n as StartedServer, r as startHttpAndWs, t as StartHttpAndWsOptions } from "../server-E9z4ZWbd.mjs";
|
|
5
5
|
import { BirpcGroup } from "birpc";
|
|
6
6
|
//#region src/node/agent-args.d.ts
|
|
7
7
|
/**
|
|
@@ -232,6 +232,26 @@ interface DevframeInstanceRegistration {
|
|
|
232
232
|
declare function registerDevframeInstance(record: DevframeInstanceRecord, options?: {
|
|
233
233
|
instancesDir?: string;
|
|
234
234
|
}): DevframeInstanceRegistration;
|
|
235
|
+
/**
|
|
236
|
+
* Read the registry and split records into live and dead by probing each
|
|
237
|
+
* one's `__connection.json`, deleting dead records (prune-on-read). Live
|
|
238
|
+
* records carry the dialable origin the probe confirmed (a `localhost`
|
|
239
|
+
* record may come back as `127.0.0.1` / `[::1]`).
|
|
240
|
+
*
|
|
241
|
+
* A liveness probe only proves *something* answers on the record's port, so
|
|
242
|
+
* records left behind by killed processes shadow the server currently bound
|
|
243
|
+
* there: per `(port, basePath)` only the newest record survives, older
|
|
244
|
+
* ghosts are pruned with the dead.
|
|
245
|
+
*
|
|
246
|
+
* @experimental
|
|
247
|
+
*/
|
|
248
|
+
declare function listLiveDevframeInstances(options?: {
|
|
249
|
+
instancesDir?: string;
|
|
250
|
+
timeoutMs?: number;
|
|
251
|
+
}): Promise<{
|
|
252
|
+
live: DevframeInstanceRecord[];
|
|
253
|
+
pruned: DevframeInstanceRecord[];
|
|
254
|
+
}>;
|
|
235
255
|
//#endregion
|
|
236
256
|
//#region src/node/rpc-shared-state.d.ts
|
|
237
257
|
declare function createRpcSharedStateServerHost(rpc: RpcFunctionsHost): RpcSharedStateHost;
|
|
@@ -278,4 +298,4 @@ declare function toDialableHost(host: string): string;
|
|
|
278
298
|
declare function formatHostForUrl(host: string): string;
|
|
279
299
|
declare function normalizeHttpServerUrl(host: string, port: number | string): string;
|
|
280
300
|
//#endregion
|
|
281
|
-
export { AgentArgsFallback, CreateH3DevframeHostOptions, CreateHostContextOptions, CreateStorageOptions, DevframeAgentHost, DevframeDiagnosticsHost, type DevframeInstanceRecord, type DevframeInstanceRegistration, DevframeServicesHostImpl, DevframeViewHost, type RpcFunctionsHost, StartHttpAndWsOptions, StartedServer, coerceAgentPositionalArgs, createH3DevframeHost, createHostContext, createNodeSettings, createRpcSharedStateServerHost, createRpcStreamingServerHost, createScopedNodeContext, createStorage, formatHostForUrl, isObject, normalizeHttpServerUrl, registerDevframeInstance, startHttpAndWs, toDialableHost };
|
|
301
|
+
export { AgentArgsFallback, CreateH3DevframeHostOptions, CreateHostContextOptions, CreateStorageOptions, DevframeAgentHost, DevframeDiagnosticsHost, type DevframeInstanceRecord, type DevframeInstanceRegistration, DevframeServicesHostImpl, DevframeViewHost, type RpcFunctionsHost, StartHttpAndWsOptions, StartedServer, coerceAgentPositionalArgs, createH3DevframeHost, createHostContext, createNodeSettings, createRpcSharedStateServerHost, createRpcStreamingServerHost, createScopedNodeContext, createStorage, formatHostForUrl, isObject, listLiveDevframeInstances, normalizeHttpServerUrl, registerDevframeInstance, startHttpAndWs, toDialableHost };
|
package/dist/node/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as DevframeViewHost, c as createRpcSharedStateServerHost, d as coerceAgentPositionalArgs, i as createNodeSettings, l as DevframeDiagnosticsHost, n as createHostContext, o as DevframeServicesHostImpl, r as createScopedNodeContext, s as createRpcStreamingServerHost, t as createH3DevframeHost, u as DevframeAgentHost } from "../host-h3-Kz7t5Xab.mjs";
|
|
2
2
|
import { t as createStorage } from "../storage-Dzoc3NVC.mjs";
|
|
3
|
-
import { r as registerDevframeInstance } from "../instance-registry-D6fxYu36.mjs";
|
|
4
|
-
import { a as toDialableHost, i as normalizeHttpServerUrl, n as formatHostForUrl, r as isObject, t as startHttpAndWs } from "../server-
|
|
5
|
-
export { DevframeAgentHost, DevframeDiagnosticsHost, DevframeServicesHostImpl, DevframeViewHost, coerceAgentPositionalArgs, createH3DevframeHost, createHostContext, createNodeSettings, createRpcSharedStateServerHost, createRpcStreamingServerHost, createScopedNodeContext, createStorage, formatHostForUrl, isObject, normalizeHttpServerUrl, registerDevframeInstance, startHttpAndWs, toDialableHost };
|
|
3
|
+
import { r as registerDevframeInstance, t as listLiveDevframeInstances } from "../instance-registry-D6fxYu36.mjs";
|
|
4
|
+
import { a as toDialableHost, i as normalizeHttpServerUrl, n as formatHostForUrl, r as isObject, t as startHttpAndWs } from "../server-CXaDDUl0.mjs";
|
|
5
|
+
export { DevframeAgentHost, DevframeDiagnosticsHost, DevframeServicesHostImpl, DevframeViewHost, coerceAgentPositionalArgs, createH3DevframeHost, createHostContext, createNodeSettings, createRpcSharedStateServerHost, createRpcStreamingServerHost, createScopedNodeContext, createStorage, formatHostForUrl, isObject, listLiveDevframeInstances, normalizeHttpServerUrl, registerDevframeInstance, startHttpAndWs, toDialableHost };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "../devframe-
|
|
1
|
+
import "../devframe-qgCKL683.mjs";
|
|
2
2
|
import { E as Thenable, S as RpcFunctionSetupResult, c as RpcDump, g as RpcFunctionAgentOptions } from "../types-CnJSgRVa.mjs";
|
|
3
3
|
import "../index-Dbw1p5ch.mjs";
|
|
4
4
|
import { t as SimpleSchema } from "../simple-schema-BDzLeJDk.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { b as DevframeNodeContext, p as DevframeAuthHandler } from "../devframe-
|
|
2
|
-
import "../index-
|
|
1
|
+
import { b as DevframeNodeContext, p as DevframeAuthHandler } from "../devframe-qgCKL683.mjs";
|
|
2
|
+
import "../index-B7RgLw--.mjs";
|
|
3
3
|
//#region src/recipes/interactive-auth.d.ts
|
|
4
4
|
interface CreateInteractiveAuthOptions {
|
|
5
5
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { n as colors } from "../diagnostics-reporter-CsIG85Q5.mjs";
|
|
2
2
|
import { isAnonymousRpcMethod } from "../constants.mjs";
|
|
3
3
|
import { n as defineRpcFunction } from "../define-BLWPsH6y.mjs";
|
|
4
|
-
import { t as getInternalContext } from "../context-
|
|
5
|
-
import { a as verifyAuthToken, n as exchangeTempAuthCode, r as getTempAuthCode, t as buildOtpAuthUrl } from "../state-
|
|
4
|
+
import { t as getInternalContext } from "../context-CQefP2jR.mjs";
|
|
5
|
+
import { a as verifyAuthToken, n as exchangeTempAuthCode, r as getTempAuthCode, t as buildOtpAuthUrl } from "../state-BeUHDDjk.mjs";
|
|
6
6
|
import { t as s } from "../simple-schema-DQPZrAaZ.mjs";
|
|
7
7
|
//#region src/recipes/interactive-auth.ts
|
|
8
8
|
function defaultBanner(info) {
|
|
@@ -107,16 +107,24 @@ function createInteractiveAuth(context, options = {}) {
|
|
|
107
107
|
}
|
|
108
108
|
function onConnect(peer, session) {
|
|
109
109
|
let token;
|
|
110
|
+
let requestOrigin;
|
|
110
111
|
try {
|
|
111
112
|
token = new URL(peer.request?.url ?? "", "http://localhost").searchParams.get("devframe_auth_token") ?? void 0;
|
|
112
113
|
} catch {}
|
|
114
|
+
try {
|
|
115
|
+
requestOrigin = peer.request?.headers?.get?.("origin") ?? void 0;
|
|
116
|
+
} catch {}
|
|
113
117
|
if (!token) return;
|
|
114
118
|
if (isStaticToken(token)) {
|
|
115
119
|
session.meta.clientAuthToken = token;
|
|
116
120
|
session.meta.isTrusted = true;
|
|
117
121
|
return;
|
|
118
122
|
}
|
|
119
|
-
verifyAuthToken(token, session, storage);
|
|
123
|
+
if (verifyAuthToken(token, session, storage)) return;
|
|
124
|
+
if (internal.isRemoteTokenTrusted(token, requestOrigin)) {
|
|
125
|
+
session.meta.clientAuthToken = token;
|
|
126
|
+
session.meta.isTrusted = true;
|
|
127
|
+
}
|
|
120
128
|
}
|
|
121
129
|
function buildOpenUrl(url) {
|
|
122
130
|
return buildOtpAuthUrl(url);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/node/auth/revoke.ts
|
|
2
|
+
/**
|
|
3
|
+
* Flip `isTrusted` to false on any live WS clients connected with `token`
|
|
4
|
+
* and broadcast the `auth:revoked` event so they can react.
|
|
5
|
+
*
|
|
6
|
+
* Shared between persisted-auth revocation and remote-dock token revocation.
|
|
7
|
+
*/
|
|
8
|
+
async function revokeActiveConnectionsForToken(context, token) {
|
|
9
|
+
const rpcHost = context.rpc;
|
|
10
|
+
if (!rpcHost?._rpcGroup) return;
|
|
11
|
+
const affectedSessionIds = /* @__PURE__ */ new Set();
|
|
12
|
+
for (const client of rpcHost._rpcGroup.clients) if (client.$meta.clientAuthToken === token) {
|
|
13
|
+
affectedSessionIds.add(client.$meta.id);
|
|
14
|
+
client.$meta.isTrusted = false;
|
|
15
|
+
client.$meta.clientAuthToken = void 0;
|
|
16
|
+
}
|
|
17
|
+
if (affectedSessionIds.size === 0) return;
|
|
18
|
+
await rpcHost.broadcast({
|
|
19
|
+
method: "devframe:auth:revoked",
|
|
20
|
+
args: [],
|
|
21
|
+
filter: (client) => affectedSessionIds.has(client.$meta.id)
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Revoke an auth token: remove from storage and notify all connected clients
|
|
26
|
+
* using this token that they are no longer trusted.
|
|
27
|
+
*/
|
|
28
|
+
async function revokeAuthToken(context, storage, token) {
|
|
29
|
+
storage.mutate((state) => {
|
|
30
|
+
delete state.trusted[token];
|
|
31
|
+
});
|
|
32
|
+
await revokeActiveConnectionsForToken(context, token);
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { revokeAuthToken as n, revokeActiveConnectionsForToken as t };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as isAllowedOrigin, i as
|
|
2
|
-
export { DevframeNodeRpcSessionMeta, WsRpcTransport, WsRpcTransportOptions, attachWsRpcTransport, isAllowedOrigin, isLoopbackHostname };
|
|
1
|
+
import { a as WsRpcTransportOptions, c as isAllowedOrigin, i as WsRpcTransport, l as isLoopbackHostname, n as DevframeNodeRpcSessionMeta, o as attachWsRpcTransport, r as WsOriginRegistry, s as createWsOriginRegistry, t as CreateWsOriginRegistryOptions } from "../../ws-server-J-zkiOyp.mjs";
|
|
2
|
+
export { CreateWsOriginRegistryOptions, DevframeNodeRpcSessionMeta, WsOriginRegistry, WsRpcTransport, WsRpcTransportOptions, attachWsRpcTransport, createWsOriginRegistry, isAllowedOrigin, isLoopbackHostname };
|
|
@@ -1,9 +1,52 @@
|
|
|
1
|
+
import "../../constants.mjs";
|
|
1
2
|
import { n as strictJsonStringify } from "../../serialization-C8Mnw9hK.mjs";
|
|
2
3
|
import { n as structuredCloneStringify, t as structuredCloneParse } from "../../structured-clone-CbAV5rFI.mjs";
|
|
4
|
+
import { n as randomToken, r as timingSafeEqual } from "../../crypto-token-XCqTSMg9.mjs";
|
|
3
5
|
import { createServer } from "node:http";
|
|
4
6
|
import { createServer as createServer$1 } from "node:https";
|
|
5
7
|
import crossws from "crossws/adapters/node";
|
|
6
8
|
//#region src/rpc/transports/ws-server.ts
|
|
9
|
+
/**
|
|
10
|
+
* Create a live, token-protected origin allowlist for external browser
|
|
11
|
+
* viewers. Pass it to {@link WsRpcTransportOptions.allowedOrigins}, then use
|
|
12
|
+
* `registerFromUrl()` in the connection metadata handler to authorize a
|
|
13
|
+
* viewer without sharing a mutable array or disabling DNS-rebinding protection.
|
|
14
|
+
*/
|
|
15
|
+
function createWsOriginRegistry(options = {}) {
|
|
16
|
+
const token = randomToken();
|
|
17
|
+
const origins = new Set(options.allowedOrigins ?? []);
|
|
18
|
+
function normalizeOrigin(origin) {
|
|
19
|
+
if (!origin) return;
|
|
20
|
+
try {
|
|
21
|
+
const url = new URL(origin);
|
|
22
|
+
const normalized = url.origin === "null" ? `${url.protocol}//${url.host}` : url.origin;
|
|
23
|
+
return origin === normalized ? normalized : void 0;
|
|
24
|
+
} catch {}
|
|
25
|
+
}
|
|
26
|
+
function registerOrigin(origin, candidateToken) {
|
|
27
|
+
const normalized = normalizeOrigin(origin);
|
|
28
|
+
if (!normalized || !candidateToken || !timingSafeEqual(token, candidateToken)) return false;
|
|
29
|
+
if (options.validateOrigin && !options.validateOrigin(normalized)) return false;
|
|
30
|
+
origins.add(normalized);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
token,
|
|
35
|
+
registerFromUrl(url) {
|
|
36
|
+
let parsed;
|
|
37
|
+
try {
|
|
38
|
+
parsed = new URL(url, "http://localhost");
|
|
39
|
+
} catch {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const origin = parsed.searchParams.get("devframe_viewer_origin") ?? void 0;
|
|
43
|
+
return registerOrigin(origin, parsed.searchParams.get("devframe_viewer_origin_token") ?? void 0) ? origin : void 0;
|
|
44
|
+
},
|
|
45
|
+
isAllowed(origin) {
|
|
46
|
+
return isAllowedOrigin(origin, [...origins]);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
7
50
|
let sessionId = 0;
|
|
8
51
|
const EMPTY_DEFS = /* @__PURE__ */ new Map();
|
|
9
52
|
function NOOP() {}
|
|
@@ -32,6 +75,9 @@ function isAllowedOrigin(origin, allowedOrigins) {
|
|
|
32
75
|
return false;
|
|
33
76
|
}
|
|
34
77
|
}
|
|
78
|
+
function isWsOriginRegistry(value) {
|
|
79
|
+
return !!value && !Array.isArray(value);
|
|
80
|
+
}
|
|
35
81
|
/**
|
|
36
82
|
* Route `upgrade` events on a server to the crossws adapter, optionally
|
|
37
83
|
* filtered to a single `path`. Non-matching requests are left untouched so
|
|
@@ -55,7 +101,8 @@ function routeUpgrades(server, ws, path, destroyUnmatched, allowedOrigins) {
|
|
|
55
101
|
return;
|
|
56
102
|
}
|
|
57
103
|
}
|
|
58
|
-
|
|
104
|
+
const originAllowed = isWsOriginRegistry(allowedOrigins) ? allowedOrigins.isAllowed(req.headers.origin) : isAllowedOrigin(req.headers.origin, allowedOrigins || []);
|
|
105
|
+
if (allowedOrigins !== false && !originAllowed) {
|
|
59
106
|
socket.write("HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n");
|
|
60
107
|
socket.destroy();
|
|
61
108
|
return;
|
|
@@ -164,4 +211,4 @@ function attachWsRpcTransport(rpcGroup, options = {}) {
|
|
|
164
211
|
};
|
|
165
212
|
}
|
|
166
213
|
//#endregion
|
|
167
|
-
export { attachWsRpcTransport, isAllowedOrigin, isLoopbackHostname };
|
|
214
|
+
export { attachWsRpcTransport, createWsOriginRegistry, isAllowedOrigin, isLoopbackHostname };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRpcServer } from "./rpc/server.mjs";
|
|
2
2
|
import { attachWsRpcTransport } from "./rpc/transports/ws-server.mjs";
|
|
3
3
|
import { t as diagnostics } from "./diagnostics-B5-qHeqD.mjs";
|
|
4
|
-
import { t as getInternalContext } from "./context-
|
|
4
|
+
import { t as getInternalContext } from "./context-CQefP2jR.mjs";
|
|
5
5
|
import { createServer } from "node:http";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
7
|
import { H3, toNodeHandler } from "h3";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { $ as DevframeRpcServerFunctions, Q as DevframeRpcClientFunctions, W as DevframeNodeRpcSession, _ as ConnectionMeta, b as DevframeNodeContext, p as DevframeAuthHandler } from "./devframe-
|
|
2
|
-
import "./
|
|
1
|
+
import { $ as DevframeRpcServerFunctions, Q as DevframeRpcClientFunctions, W as DevframeNodeRpcSession, _ as ConnectionMeta, b as DevframeNodeContext, p as DevframeAuthHandler } from "./devframe-qgCKL683.mjs";
|
|
2
|
+
import { r as WsOriginRegistry } from "./ws-server-J-zkiOyp.mjs";
|
|
3
|
+
import "./index-B7RgLw--.mjs";
|
|
3
4
|
import { Peer } from "crossws";
|
|
4
5
|
import { BirpcGroup, EventOptions } from "birpc";
|
|
5
6
|
import { NodeAdapter } from "crossws/adapters/node";
|
|
@@ -91,7 +92,7 @@ interface StartHttpAndWsOptions {
|
|
|
91
92
|
* from another host. Pass `false` to disable origin checking entirely
|
|
92
93
|
* (not recommended). Default: loopback-only.
|
|
93
94
|
*/
|
|
94
|
-
allowedOrigins?: readonly string[] | false;
|
|
95
|
+
allowedOrigins?: readonly string[] | WsOriginRegistry | false;
|
|
95
96
|
/**
|
|
96
97
|
* Called once the WS server is bound so callers can mount static
|
|
97
98
|
* handlers whose origin depends on the resolved port, or print their
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEVFRAME_OTP_URL_PARAM } from "./constants.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { n as randomToken, r as timingSafeEqual, t as randomDigits } from "./crypto-token-XCqTSMg9.mjs";
|
|
3
3
|
//#region src/node/auth/state.ts
|
|
4
4
|
/** Number of decimal digits in a human-typed one-time authentication code. */
|
|
5
5
|
const TEMP_AUTH_CODE_LENGTH = 6;
|
|
@@ -37,14 +37,21 @@ function refreshTempAuthCode() {
|
|
|
37
37
|
return tempAuthCode;
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
* Build a "magic link" authentication URL that embeds a one-time code (OTP)
|
|
41
|
-
*
|
|
42
|
-
* it on startup (devframe stays headless, so the host prints its own
|
|
43
|
-
* Defaults to the current code; the link is subject to the same TTL.
|
|
40
|
+
* Build a "magic link" authentication URL that embeds a one-time code (OTP) in
|
|
41
|
+
* the URL **fragment**. Opening it authenticates the client without typing —
|
|
42
|
+
* print it on startup (devframe stays headless, so the host prints its own
|
|
43
|
+
* banner). Defaults to the current code; the link is subject to the same TTL.
|
|
44
|
+
*
|
|
45
|
+
* The code rides the fragment (`#devframe_otp=…`), not the query string, so it
|
|
46
|
+
* is never sent to the server, written to an access log, or leaked in a
|
|
47
|
+
* `Referer` header — the browser client reads it locally (see
|
|
48
|
+
* `consumeOtpFromUrl`). Any existing fragment parameters are preserved.
|
|
44
49
|
*/
|
|
45
50
|
function buildOtpAuthUrl(baseUrl, code = tempAuthCode) {
|
|
46
51
|
const url = new URL(baseUrl);
|
|
47
|
-
url.
|
|
52
|
+
const fragment = new URLSearchParams(url.hash.replace(/^#/, ""));
|
|
53
|
+
fragment.set(DEVFRAME_OTP_URL_PARAM, code);
|
|
54
|
+
url.hash = fragment.toString();
|
|
48
55
|
return url.href;
|
|
49
56
|
}
|
|
50
57
|
/**
|
package/dist/types/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as DevframeRpcServerFunctions, A as DevframeSettingsStore, At as DevframeAgentHostEvents, B as DevframeDefineDiagnosticsOptions, C as DevframeServicesHost, Ct as AgentResourceContent, D as DevframeScopedStreamingHost, Dt as AgentToolProvider, E as DevframeScopedNodeRpc, Et as AgentToolInput, F as ScopedSharedStates, G as RpcBroadcastOptions, H as DevframeDiagnosticsHost, I as SettingsForNamespace, J as RpcSharedStateHost, K as RpcFunctionsHost, L as DevframeViewHost, M as ScopedClientFunctions, Mt as EventUnsubscribe, N as ScopedRpcFn, Nt as EventsMap, O as DevframeSettings, Ot as AgentToolProviderHandle, P as ScopedServerFunctions, Q as DevframeRpcClientFunctions, R as DevframeHost, S as DevframeServiceOf, St as AgentResource, T as DevframeScopedNodeContext, Tt as AgentTool, U as DevframeDiagnosticsLogger, V as DevframeDiagnosticsDefinition, W as DevframeNodeRpcSession, X as RpcStreamingChannelOptions, Y as RpcStreamingChannel, Z as RpcStreamingHost, _ as ConnectionMeta, a as DevframeDockDefaults, b as DevframeNodeContext, bt as AgentHandle, c as DevframeSetupInfo, d as McpRouteOptions, et as DevframeRpcSharedStates, f as defineDevframe, g as Thenable, h as PartialWithoutId, i as DevframeDeploymentKind, j as ScopedBroadcastOptions, jt as EventEmitter, k as DevframeSettingsRegistry, kt as DevframeAgentHost, l as DevframeSpaOptions, m as EntriesToObject, n as DevframeCliOptions, o as DevframeDuplicationStrategy, q as RpcSharedStateGetOptions, r as DevframeDefinition, s as DevframeRuntime, t as DevframeBrowserContext, u as DevframeWsOptions, v as ConnectionMetaWebsocket, w as DevframeServicesRegistry, wt as AgentResourceInput, x as DevframeServiceId, xt as AgentManifest, y as DevframeCapabilities, z as DevframeStorageScope } from "../devframe-
|
|
1
|
+
import { $ as DevframeRpcServerFunctions, A as DevframeSettingsStore, At as DevframeAgentHostEvents, B as DevframeDefineDiagnosticsOptions, C as DevframeServicesHost, Ct as AgentResourceContent, D as DevframeScopedStreamingHost, Dt as AgentToolProvider, E as DevframeScopedNodeRpc, Et as AgentToolInput, F as ScopedSharedStates, G as RpcBroadcastOptions, H as DevframeDiagnosticsHost, I as SettingsForNamespace, J as RpcSharedStateHost, K as RpcFunctionsHost, L as DevframeViewHost, M as ScopedClientFunctions, Mt as EventUnsubscribe, N as ScopedRpcFn, Nt as EventsMap, O as DevframeSettings, Ot as AgentToolProviderHandle, P as ScopedServerFunctions, Q as DevframeRpcClientFunctions, R as DevframeHost, S as DevframeServiceOf, St as AgentResource, T as DevframeScopedNodeContext, Tt as AgentTool, U as DevframeDiagnosticsLogger, V as DevframeDiagnosticsDefinition, W as DevframeNodeRpcSession, X as RpcStreamingChannelOptions, Y as RpcStreamingChannel, Z as RpcStreamingHost, _ as ConnectionMeta, a as DevframeDockDefaults, b as DevframeNodeContext, bt as AgentHandle, c as DevframeSetupInfo, d as McpRouteOptions, et as DevframeRpcSharedStates, f as defineDevframe, g as Thenable, h as PartialWithoutId, i as DevframeDeploymentKind, j as ScopedBroadcastOptions, jt as EventEmitter, k as DevframeSettingsRegistry, kt as DevframeAgentHost, l as DevframeSpaOptions, m as EntriesToObject, n as DevframeCliOptions, o as DevframeDuplicationStrategy, q as RpcSharedStateGetOptions, r as DevframeDefinition, s as DevframeRuntime, t as DevframeBrowserContext, u as DevframeWsOptions, v as ConnectionMetaWebsocket, w as DevframeServicesRegistry, wt as AgentResourceInput, x as DevframeServiceId, xt as AgentManifest, y as DevframeCapabilities, z as DevframeStorageScope } from "../devframe-qgCKL683.mjs";
|
|
2
2
|
import { g as RpcFunctionAgentOptions } from "../types-CnJSgRVa.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { n as DevframeNodeRpcSessionMeta } from "../ws-server-J-zkiOyp.mjs";
|
|
4
4
|
export { AgentHandle, AgentManifest, AgentResource, AgentResourceContent, AgentResourceInput, AgentTool, AgentToolInput, AgentToolProvider, AgentToolProviderHandle, ConnectionMeta, ConnectionMetaWebsocket, DevframeAgentHost, DevframeAgentHostEvents, DevframeBrowserContext, DevframeCapabilities, DevframeCliOptions, DevframeDefineDiagnosticsOptions, DevframeDefinition, DevframeDeploymentKind, DevframeDiagnosticsDefinition, DevframeDiagnosticsHost, DevframeDiagnosticsLogger, DevframeDockDefaults, DevframeDuplicationStrategy, DevframeHost, DevframeNodeContext, DevframeNodeRpcSession, type DevframeNodeRpcSessionMeta, DevframeRpcClientFunctions, DevframeRpcServerFunctions, DevframeRpcSharedStates, DevframeRuntime, DevframeScopedNodeContext, DevframeScopedNodeRpc, DevframeScopedStreamingHost, DevframeServiceId, DevframeServiceOf, DevframeServicesHost, DevframeServicesRegistry, DevframeSettings, DevframeSettingsRegistry, DevframeSettingsStore, DevframeSetupInfo, DevframeSpaOptions, DevframeStorageScope, DevframeViewHost, DevframeWsOptions, EntriesToObject, EventEmitter, EventUnsubscribe, EventsMap, McpRouteOptions, PartialWithoutId, RpcBroadcastOptions, type RpcFunctionAgentOptions, RpcFunctionsHost, RpcSharedStateGetOptions, RpcSharedStateHost, RpcStreamingChannel, RpcStreamingChannelOptions, RpcStreamingHost, ScopedBroadcastOptions, ScopedClientFunctions, ScopedRpcFn, ScopedServerFunctions, ScopedSharedStates, SettingsForNamespace, Thenable, defineDevframe };
|
package/dist/utils/events.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _t as SharedStateOptions, dt as ImmutableArray, ft as ImmutableMap, gt as SharedStateEvents, ht as SharedState, mt as ImmutableSet, pt as ImmutableObject, ut as Immutable, vt as SharedStatePatch, yt as createSharedState } from "../devframe-
|
|
1
|
+
import { _t as SharedStateOptions, dt as ImmutableArray, ft as ImmutableMap, gt as SharedStateEvents, ht as SharedState, mt as ImmutableSet, pt as ImmutableObject, ut as Immutable, vt as SharedStatePatch, yt as createSharedState } from "../devframe-qgCKL683.mjs";
|
|
2
2
|
export { Immutable, ImmutableArray, ImmutableMap, ImmutableObject, ImmutableSet, SharedState, SharedStateEvents, SharedStateOptions, SharedStatePatch, createSharedState };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { at as StreamReader, ct as createStreamReader, it as StreamErrorPayload, lt as createStreamSink, nt as CreateStreamReaderOptions, ot as StreamSink, rt as CreateStreamSinkOptions, st as StreamSinkEvents, tt as BufferedChunk } from "../devframe-
|
|
1
|
+
import { at as StreamReader, ct as createStreamReader, it as StreamErrorPayload, lt as createStreamSink, nt as CreateStreamReaderOptions, ot as StreamSink, rt as CreateStreamSinkOptions, st as StreamSinkEvents, tt as BufferedChunk } from "../devframe-qgCKL683.mjs";
|
|
2
2
|
export { BufferedChunk, CreateStreamReaderOptions, CreateStreamSinkOptions, StreamErrorPayload, StreamReader, StreamSink, StreamSinkEvents, createStreamReader, createStreamSink };
|
|
@@ -62,7 +62,7 @@ interface WsRpcTransportOptions {
|
|
|
62
62
|
* Pass `false` to disable origin checking entirely (not recommended).
|
|
63
63
|
* Default: loopback-only.
|
|
64
64
|
*/
|
|
65
|
-
allowedOrigins?: readonly string[] | false;
|
|
65
|
+
allowedOrigins?: readonly string[] | WsOriginRegistry | false;
|
|
66
66
|
/**
|
|
67
67
|
* RPC function definitions, used by the per-call wire serializer to
|
|
68
68
|
* dispatch between strict-JSON and structured-clone encoding based
|
|
@@ -79,6 +79,27 @@ interface WsRpcTransportOptions {
|
|
|
79
79
|
/** Override the default per-call deserializer. Most callers should leave this unset. */
|
|
80
80
|
deserialize?: ChannelOptions['deserialize'];
|
|
81
81
|
}
|
|
82
|
+
interface CreateWsOriginRegistryOptions {
|
|
83
|
+
/** Origins allowed before any external viewers are registered. */
|
|
84
|
+
allowedOrigins?: readonly string[];
|
|
85
|
+
/** Additional validation to run after the registration token is verified. */
|
|
86
|
+
validateOrigin?: (origin: string) => boolean;
|
|
87
|
+
}
|
|
88
|
+
interface WsOriginRegistry {
|
|
89
|
+
/** Registration token to include in connection metadata. */
|
|
90
|
+
readonly token: string;
|
|
91
|
+
/** Read and register an origin from a connection bootstrap URL. */
|
|
92
|
+
registerFromUrl: (url: string) => string | undefined;
|
|
93
|
+
/** Check whether an origin is currently allowed. */
|
|
94
|
+
isAllowed: (origin: string | undefined) => boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Create a live, token-protected origin allowlist for external browser
|
|
98
|
+
* viewers. Pass it to {@link WsRpcTransportOptions.allowedOrigins}, then use
|
|
99
|
+
* `registerFromUrl()` in the connection metadata handler to authorize a
|
|
100
|
+
* viewer without sharing a mutable array or disabling DNS-rebinding protection.
|
|
101
|
+
*/
|
|
102
|
+
declare function createWsOriginRegistry(options?: CreateWsOriginRegistryOptions): WsOriginRegistry;
|
|
82
103
|
interface WsRpcTransport {
|
|
83
104
|
/**
|
|
84
105
|
* The crossws node adapter driving the socket — exposes the connected
|
|
@@ -115,4 +136,4 @@ declare function isAllowedOrigin(origin: string | undefined, allowedOrigins: rea
|
|
|
115
136
|
*/
|
|
116
137
|
declare function attachWsRpcTransport<ClientFunctions extends object, ServerFunctions extends object>(rpcGroup: BirpcGroup<ClientFunctions, ServerFunctions, false>, options?: WsRpcTransportOptions): WsRpcTransport;
|
|
117
138
|
//#endregion
|
|
118
|
-
export {
|
|
139
|
+
export { WsRpcTransportOptions as a, isAllowedOrigin as c, WsRpcTransport as i, isLoopbackHostname as l, DevframeNodeRpcSessionMeta as n, attachWsRpcTransport as o, WsOriginRegistry as r, createWsOriginRegistry as s, CreateWsOriginRegistryOptions as t };
|
package/package.json
CHANGED
package/skills/devframe/SKILL.md
CHANGED
|
@@ -569,10 +569,11 @@ RPC handlers run with the full privileges of the host process, so the boundary t
|
|
|
569
569
|
- **`auth` defaults to `true`** — dev-mode connections must authenticate before calls are accepted. Devframe ships the node primitives (`exchangeTempAuthCode`, `verifyAuthToken` in `devframe/node/auth`); the host adapter (e.g. Vite DevTools) provides the interactive `devframe:anonymous:auth` + `devframe:auth:exchange` handlers and auth UI.
|
|
570
570
|
- **`auth: false` trusts every reachable connection.** Use it only for single-user `localhost` tools. Never combine it with a non-loopback bind host, a tunnel, or a shared/CI environment. The default bind host is already `localhost`.
|
|
571
571
|
- **Authentication** exchanges a 6-digit one-time code (shown in the developer's terminal) for a node-issued bearer token via `requestTrustWithCode(code)`. The code is single-use, expires in 5 min, compared in constant time, and rotates after repeated failures — show it only in the terminal, never over the network.
|
|
572
|
-
- **Magic-link (optional):** print `buildOtpAuthUrl(origin)` — `<origin
|
|
572
|
+
- **Magic-link (optional):** print `buildOtpAuthUrl(origin)` — `<origin>/#devframe_otp=<code>`. The code rides the URL **fragment**, which the browser never sends to the server, so it stays out of access logs and `Referer`. `connectDevframe` reads the code, exchanges it, and strips it from the URL. Integrations can opt out (`otpParam: false`) and drive it via the exposed `authenticateWithUrlOtp(rpc)` / `consumeOtpFromUrl()` client utilities. Only the single-use code rides the URL, never the bearer; treat the printed link like the code itself. The standalone CLI's `--open` does this automatically via `DevframeAuthHandler.buildOpenUrl` — the launched tab already carries the OTP, no prompt needed.
|
|
573
573
|
- **Tokens are secrets.** The bearer token rides the WS URL (`?devframe_auth_token=…`) — serve over `wss://`/`https://` beyond loopback. Never log the token or code, never bake them into build output. Revoke via `revokeAuthToken(...)`; clients drop to untrusted on `devframe:auth:revoked`.
|
|
574
574
|
- **Authorize handlers.** Any trusted client can call any registered function — validate inputs, and mark state-changing functions `type: 'destructive'` so MCP/agent clients prompt first.
|
|
575
|
-
- **Origin-lock remote docks** (`originLock
|
|
575
|
+
- **Origin-lock remote docks** (`originLock`, on by default) so a dock's session token is honored only on a connection whose `Origin` matches the dock — the connect-time gate enforces it.
|
|
576
|
+
- **The MCP route requires an Origin.** The route-based MCP server (`cli.mcp`, `viteDevBridge`/Next handler `mcp`, `createMcpFetchHandler`) rejects `Origin`-less requests — a request must carry a loopback (or allow-listed) `Origin`, so it isn't reachable by an arbitrary local process. `devframe connect` sends each instance's own loopback origin automatically.
|
|
576
577
|
|
|
577
578
|
See [Security](https://devfra.me/security) for the full reference.
|
|
578
579
|
|