@happyvertical/smrt-app-cli 0.40.48 → 0.40.50
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/AGENTS.md +2 -0
- package/README.md +9 -0
- package/dist/bin/smrt-mcp-bridge.js +1 -1
- package/dist/bridge-D0LJc3mN.js +2 -0
- package/dist/{config-DB4AMYT0.js → config-DvxwoFks.js} +27 -9
- package/dist/index.d.ts +69 -4
- package/dist/index.js +77 -4
- package/package.json +3 -3
- package/dist/bridge-sTKlA6Hz.js +0 -2
package/AGENTS.md
CHANGED
|
@@ -13,6 +13,8 @@ Application CLI support for SMRT-based apps.
|
|
|
13
13
|
- Keep commands app-agnostic; pass app identity through `CliConfigContext`.
|
|
14
14
|
- Treat transient auth polling failures as recoverable until the server-issued expiration window closes.
|
|
15
15
|
- Do not persist tokens, server URLs, or app slugs outside the configured local CLI config path.
|
|
16
|
+
- Bind persisted bearer tokens to the exact issuer emitted by the device flow
|
|
17
|
+
and to the server selected during login. Never reuse them after either changes.
|
|
16
18
|
- Keep command output stream-injectable so tests can assert behavior without writing to the real terminal.
|
|
17
19
|
|
|
18
20
|
## Gotchas
|
package/README.md
CHANGED
|
@@ -90,6 +90,13 @@ surface is typically mounted with
|
|
|
90
90
|
- Device-code login distinguishes pending approval, expiry, transient network
|
|
91
91
|
failure, and hard rejection.
|
|
92
92
|
- Tokens and server configuration stay in the app-specific local config path.
|
|
93
|
+
- Device-flow responses identify their issuer. Tokens are keyed by that exact
|
|
94
|
+
issuer and are not reused when the configured server changes.
|
|
95
|
+
|
|
96
|
+
For standards-based remote MCP OAuth registration, use
|
|
97
|
+
`registerMcpClient()` (Client ID Metadata Document first, executable RFC 7591
|
|
98
|
+
DCR fallback with `application_type`) and follow the
|
|
99
|
+
[remote MCP authorization guide](../../docs/content/architecture/remote-mcp-authorization.md).
|
|
93
100
|
|
|
94
101
|
## Public API
|
|
95
102
|
|
|
@@ -101,6 +108,8 @@ surface is typically mounted with
|
|
|
101
108
|
| `invokeCommand()` | Invoke one generated resource command |
|
|
102
109
|
| `buildFlagParser()` | Convert supported JSON Schema to flags |
|
|
103
110
|
| `loadCliConfig()` / `saveCliConfig()` | Read and write namespaced CLI config |
|
|
111
|
+
| `resolveMcpClientRegistration()` | Inspect the selected Client ID Metadata or DCR path |
|
|
112
|
+
| `registerMcpClient()` | Use Client ID Metadata or execute the DCR fallback |
|
|
104
113
|
|
|
105
114
|
## Development
|
|
106
115
|
|
|
@@ -129,23 +129,41 @@ async function getServerUrl(context, config) {
|
|
|
129
129
|
const resolved = config ?? await loadCliConfig(context);
|
|
130
130
|
return (process.env[`${context.envPrefix}_SERVER_URL`] ?? resolved.serverUrl ?? context.defaultServerUrl ?? DEFAULT_LOCAL_SERVER).replace(/\/+$/u, "");
|
|
131
131
|
}
|
|
132
|
-
/** Resolve
|
|
133
|
-
async function getStoredToken(context, config) {
|
|
132
|
+
/** Resolve a bearer token only when it is bound to the exact target server. */
|
|
133
|
+
async function getStoredToken(context, config, serverUrl) {
|
|
134
134
|
const resolved = config ?? await loadCliConfig(context);
|
|
135
|
-
|
|
135
|
+
const targetServer = (serverUrl ?? await getServerUrl(context, resolved)).replace(/\/+$/u, "");
|
|
136
|
+
const environmentToken = process.env[`${context.envPrefix}_TOKEN`];
|
|
137
|
+
const environmentServer = process.env[`${context.envPrefix}_SERVER_URL`]?.replace(/\/+$/u, "");
|
|
138
|
+
if (environmentToken) return environmentServer === targetServer ? environmentToken : void 0;
|
|
139
|
+
const configuredServer = resolved.serverUrl?.replace(/\/+$/u, "");
|
|
140
|
+
if (!configuredServer || configuredServer !== targetServer) return void 0;
|
|
141
|
+
if (resolved.credentialIssuer) return resolved.tokensByIssuer?.[resolved.credentialIssuer];
|
|
142
|
+
return resolved.token;
|
|
136
143
|
}
|
|
137
144
|
/** Remove the token from the config file (e.g. on logout). */
|
|
138
145
|
async function clearStoredToken(context) {
|
|
139
146
|
const config = await loadCliConfig(context);
|
|
147
|
+
if (config.credentialIssuer && config.tokensByIssuer) {
|
|
148
|
+
delete config.tokensByIssuer[config.credentialIssuer];
|
|
149
|
+
if (Object.keys(config.tokensByIssuer).length === 0) delete config.tokensByIssuer;
|
|
150
|
+
}
|
|
151
|
+
delete config.credentialIssuer;
|
|
140
152
|
delete config.token;
|
|
141
153
|
await saveCliConfig(context, config);
|
|
142
154
|
}
|
|
143
|
-
/** Persist a login
|
|
144
|
-
async function saveAuth(context, serverUrl, token) {
|
|
155
|
+
/** Persist a login with the bearer token keyed by its exact issuer. */
|
|
156
|
+
async function saveAuth(context, serverUrl, token, issuer = serverUrl) {
|
|
157
|
+
const config = await loadCliConfig(context);
|
|
158
|
+
const normalizedServerUrl = serverUrl.replace(/\/+$/u, "");
|
|
159
|
+
if (!issuer.trim()) throw new Error("Credential issuer must not be empty.");
|
|
160
|
+
const exactIssuer = issuer;
|
|
145
161
|
await saveCliConfig(context, {
|
|
146
|
-
...
|
|
147
|
-
|
|
148
|
-
|
|
162
|
+
...config,
|
|
163
|
+
credentialIssuer: exactIssuer,
|
|
164
|
+
serverUrl: normalizedServerUrl,
|
|
165
|
+
token: void 0,
|
|
166
|
+
tokensByIssuer: { [exactIssuer]: token }
|
|
149
167
|
});
|
|
150
168
|
}
|
|
151
169
|
var DEFAULT_MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
@@ -157,7 +175,7 @@ var DEFAULT_MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
|
157
175
|
async function requestJson(context, path, init = {}, options = {}) {
|
|
158
176
|
const config = options.loadedConfig ?? await loadCliConfig(context);
|
|
159
177
|
const serverUrl = (options.serverUrl ?? await getServerUrl(context, config)).replace(/\/+$/u, "");
|
|
160
|
-
const token = await getStoredToken(context, config);
|
|
178
|
+
const token = await getStoredToken(context, config, serverUrl);
|
|
161
179
|
const headers = new Headers(init.headers);
|
|
162
180
|
if (options.requireAuth && options.auth !== false && !token) throw new Error(`Not authenticated. Run \`${context.envPrefix.toLowerCase()} auth login\` first.`);
|
|
163
181
|
if (!headers.has("content-type") && init.body) headers.set("content-type", "application/json");
|
package/dist/index.d.ts
CHANGED
|
@@ -68,8 +68,13 @@ export declare function clearStoredToken(context: CliConfigContext): Promise<voi
|
|
|
68
68
|
|
|
69
69
|
/** Shape stored on disk in the app's CLI config file. */
|
|
70
70
|
export declare interface CliConfig {
|
|
71
|
+
/** Issuer selected for the currently configured server. */
|
|
72
|
+
credentialIssuer?: string;
|
|
71
73
|
serverUrl?: string;
|
|
74
|
+
/** Legacy single-token field. Read only when its stored server still matches. */
|
|
72
75
|
token?: string;
|
|
76
|
+
/** Bearer credentials keyed by their exact issuer identifier. */
|
|
77
|
+
tokensByIssuer?: Record<string, string>;
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
/**
|
|
@@ -166,6 +171,9 @@ export declare interface CreateAppCliOptions {
|
|
|
166
171
|
extraCommands?: AppCliCommand[];
|
|
167
172
|
}
|
|
168
173
|
|
|
174
|
+
/** Build and validate the document hosted at an HTTPS URL used as client_id. */
|
|
175
|
+
export declare function createMcpClientIdMetadataDocument(options: McpClientRegistrationOptions): McpClientIdMetadataDocument;
|
|
176
|
+
|
|
169
177
|
/**
|
|
170
178
|
* Wire up the stdio server. Use `runMcpStdioBridge` for a one-call entry
|
|
171
179
|
* point in `bin/` scripts; this lower-level form is exposed for tests.
|
|
@@ -212,8 +220,8 @@ export declare function findResourceBySlug(response: ResourceListResponse, slug:
|
|
|
212
220
|
/** Resolve the server URL: env var → config file → `defaultServerUrl`. */
|
|
213
221
|
export declare function getServerUrl(context: CliConfigContext, config?: CliConfig): Promise<string>;
|
|
214
222
|
|
|
215
|
-
/** Resolve
|
|
216
|
-
export declare function getStoredToken(context: CliConfigContext, config?: CliConfig): Promise<string | undefined>;
|
|
223
|
+
/** Resolve a bearer token only when it is bound to the exact target server. */
|
|
224
|
+
export declare function getStoredToken(context: CliConfigContext, config?: CliConfig, serverUrl?: string): Promise<string | undefined>;
|
|
217
225
|
|
|
218
226
|
/**
|
|
219
227
|
* Build the URL the CLI should hit for this command, plus the fetch init
|
|
@@ -236,6 +244,41 @@ declare interface InvokeOptions {
|
|
|
236
244
|
/** Read the CLI config file. Missing file → empty config. */
|
|
237
245
|
export declare function loadCliConfig(context: CliConfigContext): Promise<CliConfig>;
|
|
238
246
|
|
|
247
|
+
export declare interface McpClientIdMetadataDocument {
|
|
248
|
+
application_type: OAuthApplicationType;
|
|
249
|
+
client_id: string;
|
|
250
|
+
client_name: string;
|
|
251
|
+
grant_types: ['authorization_code'];
|
|
252
|
+
redirect_uris: string[];
|
|
253
|
+
response_types: ['code'];
|
|
254
|
+
token_endpoint_auth_method: 'none';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export declare type McpClientRegistration = {
|
|
258
|
+
clientId: string;
|
|
259
|
+
kind: 'client_id_metadata_document';
|
|
260
|
+
metadataDocument: McpClientIdMetadataDocument;
|
|
261
|
+
} | {
|
|
262
|
+
endpoint: string;
|
|
263
|
+
kind: 'dynamic_client_registration';
|
|
264
|
+
request: Omit<McpClientIdMetadataDocument, 'client_id'>;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
export declare interface McpClientRegistrationOptions {
|
|
268
|
+
applicationType: OAuthApplicationType;
|
|
269
|
+
/** HTTPS metadata-document URL. Required only when CIMD is advertised. */
|
|
270
|
+
clientId?: string;
|
|
271
|
+
clientName: string;
|
|
272
|
+
redirectUris: string[];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export declare interface McpRegisteredClient {
|
|
276
|
+
clientId: string;
|
|
277
|
+
kind: McpClientRegistration['kind'];
|
|
278
|
+
metadataDocument?: McpClientIdMetadataDocument;
|
|
279
|
+
registrationResponse?: Record<string, unknown>;
|
|
280
|
+
}
|
|
281
|
+
|
|
239
282
|
/**
|
|
240
283
|
* Configuration for the bridge.
|
|
241
284
|
*
|
|
@@ -258,6 +301,14 @@ export declare interface McpStdioBridgeOptions extends CliConfigContext {
|
|
|
258
301
|
fetch?: typeof fetch;
|
|
259
302
|
}
|
|
260
303
|
|
|
304
|
+
/** MCP OAuth client-registration helpers for remote HTTP deployments. */
|
|
305
|
+
export declare type OAuthApplicationType = 'native' | 'web';
|
|
306
|
+
|
|
307
|
+
export declare interface OAuthAuthorizationServerMetadata {
|
|
308
|
+
client_id_metadata_document_supported?: boolean;
|
|
309
|
+
registration_endpoint?: string;
|
|
310
|
+
}
|
|
311
|
+
|
|
261
312
|
declare interface OutputOptions {
|
|
262
313
|
stdout?: NodeJS.WriteStream | Writable;
|
|
263
314
|
stderr?: NodeJS.WriteStream | Writable;
|
|
@@ -281,6 +332,14 @@ declare interface ParserOptions {
|
|
|
281
332
|
positionalOnly?: boolean;
|
|
282
333
|
}
|
|
283
334
|
|
|
335
|
+
/**
|
|
336
|
+
* Complete client registration against discovered authorization-server
|
|
337
|
+
* metadata. A Client ID Metadata Document needs no registration request: the
|
|
338
|
+
* authorization server retrieves it from the HTTPS client_id. The legacy DCR
|
|
339
|
+
* fallback is executed as an RFC 7591 JSON POST.
|
|
340
|
+
*/
|
|
341
|
+
export declare function registerMcpClient(authorizationServer: OAuthAuthorizationServerMetadata, options: McpClientRegistrationOptions, fetchImpl?: typeof fetch): Promise<McpRegisteredClient>;
|
|
342
|
+
|
|
284
343
|
/**
|
|
285
344
|
* Render the response. Returns the desired exit code.
|
|
286
345
|
*/
|
|
@@ -331,6 +390,12 @@ export declare interface RequestJsonOptions {
|
|
|
331
390
|
loadedConfig?: CliConfig;
|
|
332
391
|
}
|
|
333
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Select registration in MCP priority order: Client ID Metadata Documents
|
|
395
|
+
* first, then RFC 7591 DCR as a compatibility fallback.
|
|
396
|
+
*/
|
|
397
|
+
export declare function resolveMcpClientRegistration(authorizationServer: OAuthAuthorizationServerMetadata, options: McpClientRegistrationOptions): McpClientRegistration;
|
|
398
|
+
|
|
334
399
|
export declare type ResourceListResponse = ResourceListResponseBody;
|
|
335
400
|
|
|
336
401
|
declare interface ResourceListResponseBody {
|
|
@@ -348,8 +413,8 @@ declare interface ResourceListResponseBody {
|
|
|
348
413
|
*/
|
|
349
414
|
export declare function runMcpStdioBridge(options: McpStdioBridgeOptions): Promise<void>;
|
|
350
415
|
|
|
351
|
-
/** Persist a login
|
|
352
|
-
export declare function saveAuth(context: CliConfigContext, serverUrl: string, token: string): Promise<void>;
|
|
416
|
+
/** Persist a login with the bearer token keyed by its exact issuer. */
|
|
417
|
+
export declare function saveAuth(context: CliConfigContext, serverUrl: string, token: string, issuer?: string): Promise<void>;
|
|
353
418
|
|
|
354
419
|
/**
|
|
355
420
|
* Write the CLI config to disk with 0600 permissions (the token is a bearer
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as requestJson, c as createMcpStdioBridge, i as loadCliConfig, l as runMcpStdioBridge, n as getServerUrl, o as saveAuth, r as getStoredToken, s as saveCliConfig, t as clearStoredToken } from "./config-
|
|
1
|
+
import { a as requestJson, c as createMcpStdioBridge, i as loadCliConfig, l as runMcpStdioBridge, n as getServerUrl, o as saveAuth, r as getStoredToken, s as saveCliConfig, t as clearStoredToken } from "./config-DvxwoFks.js";
|
|
2
2
|
import { spawn, spawnSync } from "node:child_process";
|
|
3
3
|
//#region src/discovery.ts
|
|
4
4
|
/**
|
|
@@ -91,6 +91,79 @@ function splitPath(s) {
|
|
|
91
91
|
return s.split("/").filter((p) => p.length > 0);
|
|
92
92
|
}
|
|
93
93
|
//#endregion
|
|
94
|
+
//#region src/mcp-oauth.ts
|
|
95
|
+
function createMcpRegistrationRequest(options) {
|
|
96
|
+
if (!options.clientName.trim()) throw new Error("MCP client_name must not be empty.");
|
|
97
|
+
if (options.redirectUris.length === 0) throw new Error("MCP client metadata requires at least one redirect URI.");
|
|
98
|
+
for (const redirectUri of options.redirectUris) new URL(redirectUri);
|
|
99
|
+
return {
|
|
100
|
+
application_type: options.applicationType,
|
|
101
|
+
client_name: options.clientName,
|
|
102
|
+
grant_types: ["authorization_code"],
|
|
103
|
+
redirect_uris: [...options.redirectUris],
|
|
104
|
+
response_types: ["code"],
|
|
105
|
+
token_endpoint_auth_method: "none"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** Build and validate the document hosted at an HTTPS URL used as client_id. */
|
|
109
|
+
function createMcpClientIdMetadataDocument(options) {
|
|
110
|
+
if (!options.clientId) throw new Error("MCP client_id metadata URL is required for CIMD.");
|
|
111
|
+
const clientId = new URL(options.clientId);
|
|
112
|
+
if (clientId.protocol !== "https:" || clientId.pathname === "/") throw new Error("MCP client_id metadata URL must use HTTPS and include a path.");
|
|
113
|
+
if (clientId.search || clientId.hash) throw new Error("MCP client_id metadata URL must not include query or fragment.");
|
|
114
|
+
return {
|
|
115
|
+
...createMcpRegistrationRequest(options),
|
|
116
|
+
client_id: options.clientId
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Select registration in MCP priority order: Client ID Metadata Documents
|
|
121
|
+
* first, then RFC 7591 DCR as a compatibility fallback.
|
|
122
|
+
*/
|
|
123
|
+
function resolveMcpClientRegistration(authorizationServer, options) {
|
|
124
|
+
if (authorizationServer.client_id_metadata_document_supported === true) {
|
|
125
|
+
const metadataDocument = createMcpClientIdMetadataDocument(options);
|
|
126
|
+
return {
|
|
127
|
+
clientId: metadataDocument.client_id,
|
|
128
|
+
kind: "client_id_metadata_document",
|
|
129
|
+
metadataDocument
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (authorizationServer.registration_endpoint) return {
|
|
133
|
+
endpoint: authorizationServer.registration_endpoint,
|
|
134
|
+
kind: "dynamic_client_registration",
|
|
135
|
+
request: createMcpRegistrationRequest(options)
|
|
136
|
+
};
|
|
137
|
+
throw new Error("Authorization server supports neither Client ID Metadata Documents nor dynamic client registration.");
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Complete client registration against discovered authorization-server
|
|
141
|
+
* metadata. A Client ID Metadata Document needs no registration request: the
|
|
142
|
+
* authorization server retrieves it from the HTTPS client_id. The legacy DCR
|
|
143
|
+
* fallback is executed as an RFC 7591 JSON POST.
|
|
144
|
+
*/
|
|
145
|
+
async function registerMcpClient(authorizationServer, options, fetchImpl = fetch) {
|
|
146
|
+
const registration = resolveMcpClientRegistration(authorizationServer, options);
|
|
147
|
+
if (registration.kind === "client_id_metadata_document") return {
|
|
148
|
+
clientId: registration.clientId,
|
|
149
|
+
kind: registration.kind,
|
|
150
|
+
metadataDocument: registration.metadataDocument
|
|
151
|
+
};
|
|
152
|
+
const response = await fetchImpl(registration.endpoint, {
|
|
153
|
+
body: JSON.stringify(registration.request),
|
|
154
|
+
headers: { "content-type": "application/json" },
|
|
155
|
+
method: "POST"
|
|
156
|
+
});
|
|
157
|
+
if (!response.ok) throw new Error(`Dynamic client registration failed: HTTP ${response.status}`);
|
|
158
|
+
const body = await response.json();
|
|
159
|
+
if (typeof body !== "object" || body === null || typeof body.client_id !== "string" || !body.client_id) throw new Error("Dynamic client registration response omitted client_id.");
|
|
160
|
+
return {
|
|
161
|
+
clientId: body.client_id,
|
|
162
|
+
kind: registration.kind,
|
|
163
|
+
registrationResponse: body
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
94
167
|
//#region src/output.ts
|
|
95
168
|
var JSON_BUFFER_LIMIT = 10 * 1024 * 1024;
|
|
96
169
|
/**
|
|
@@ -558,7 +631,7 @@ async function runAuthLogin(options, args) {
|
|
|
558
631
|
serverUrl: targetServer
|
|
559
632
|
});
|
|
560
633
|
if (token.status === "approved" && token.accessToken) {
|
|
561
|
-
await saveAuth(options.context, targetServer, token.accessToken);
|
|
634
|
+
await saveAuth(options.context, targetServer, token.accessToken, start.issuer ?? targetServer);
|
|
562
635
|
stdout.write(`Authenticated to ${targetServer}\n`);
|
|
563
636
|
return;
|
|
564
637
|
}
|
|
@@ -774,7 +847,7 @@ function createAppCli(options) {
|
|
|
774
847
|
return {
|
|
775
848
|
run: (argv) => runCli(context, options, extraByName, argv),
|
|
776
849
|
startMcpBridge: async (serverInfo) => {
|
|
777
|
-
const { runMcpStdioBridge } = await import("./bridge-
|
|
850
|
+
const { runMcpStdioBridge } = await import("./bridge-D0LJc3mN.js");
|
|
778
851
|
await runMcpStdioBridge({
|
|
779
852
|
...context,
|
|
780
853
|
serverInfo: {
|
|
@@ -927,4 +1000,4 @@ function similar(a, b) {
|
|
|
927
1000
|
return true;
|
|
928
1001
|
}
|
|
929
1002
|
//#endregion
|
|
930
|
-
export { buildFlagParser, buildUrl, classifySchema, clearStoredToken, createAppCli, createMcpStdioBridge, fetchResourceList, findCommand, findResourceBySlug, getServerUrl, getStoredToken, invokeCommand, loadCliConfig, renderResponse, requestJson, runMcpStdioBridge, saveAuth, saveCliConfig };
|
|
1003
|
+
export { buildFlagParser, buildUrl, classifySchema, clearStoredToken, createAppCli, createMcpClientIdMetadataDocument, createMcpStdioBridge, fetchResourceList, findCommand, findResourceBySlug, getServerUrl, getStoredToken, invokeCommand, loadCliConfig, registerMcpClient, renderResponse, requestJson, resolveMcpClientRegistration, runMcpStdioBridge, saveAuth, saveCliConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-app-cli",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.50",
|
|
4
4
|
"description": "Reusable CLI factory for SMRT apps — branded `<name> <resource> <command>` CLI + stdio MCP bridge with decorator-driven resource discovery",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
24
|
-
"@happyvertical/smrt-users": "0.40.
|
|
24
|
+
"@happyvertical/smrt-users": "0.40.50"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "24.13.2",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"vite": "8.1.4",
|
|
30
30
|
"vite-plugin-dts": "4.5.4",
|
|
31
31
|
"vitest": "4.1.10",
|
|
32
|
-
"@happyvertical/smrt-core": "0.40.
|
|
32
|
+
"@happyvertical/smrt-core": "0.40.50"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|
|
35
35
|
"node": ">=24.18.0"
|
package/dist/bridge-sTKlA6Hz.js
DELETED