@happyvertical/smrt-app-cli 0.43.6 → 0.43.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -0
- package/dist/bin/smrt-app.d.ts +8 -0
- package/dist/bin/smrt-app.js +18 -0
- package/dist/bin/smrt-mcp-bridge.js +2 -1
- package/dist/bridge-Crwjtcyt.js +2 -0
- package/dist/{config-B6arU8x7.js → config-CuTFiGxX.js} +24 -3
- package/dist/index.d.ts +32 -0
- package/dist/index.js +3 -1033
- package/dist/smrt-app.d.ts +1 -0
- package/dist/src-DCo67mp8.js +1143 -0
- package/package.json +4 -3
- package/dist/bridge-DqndDLee.js +0 -2
package/README.md
CHANGED
|
@@ -17,6 +17,45 @@ pnpm add @happyvertical/smrt-app-cli
|
|
|
17
17
|
|
|
18
18
|
Node.js 24.18 or newer is required.
|
|
19
19
|
|
|
20
|
+
The published package also exposes a configuration-driven `smrt-app` binary.
|
|
21
|
+
It lets policy runners and app operators use the canonical CLI outside a
|
|
22
|
+
consumer repository without maintaining a branded wrapper:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
SMRT_APP_NAME=work \
|
|
26
|
+
SMRT_APP_ENV_PREFIX=WORK \
|
|
27
|
+
SMRT_APP_CONFIG_DIR=happyvertical-work \
|
|
28
|
+
SMRT_APP_DEFAULT_SERVER_URL=https://work.example \
|
|
29
|
+
pnpm dlx --package @happyvertical/smrt-app-cli@X.Y.Z \
|
|
30
|
+
smrt-app -- auth status
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Pin the exact published package version in automation. The four application
|
|
34
|
+
settings are required and are non-secret; command arguments after `--` are
|
|
35
|
+
forwarded unchanged to `createAppCli`. The same values may be passed as
|
|
36
|
+
`--name`, `--env-prefix`, `--config-dir`, and `--default-server-url` before the
|
|
37
|
+
delimiter. Invalid or incomplete configuration fails before the CLI performs
|
|
38
|
+
network or credential access. The executable also applies the HTTPS/loopback
|
|
39
|
+
policy to the effective server URL selected from app-specific environment or
|
|
40
|
+
persisted configuration, and to command-level server overrides.
|
|
41
|
+
|
|
42
|
+
To run the same CLI's stdio MCP bridge, configure its explicit identity and
|
|
43
|
+
select bridge mode:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
SMRT_APP_NAME=work \
|
|
47
|
+
SMRT_APP_ENV_PREFIX=WORK \
|
|
48
|
+
SMRT_APP_CONFIG_DIR=happyvertical-work \
|
|
49
|
+
SMRT_APP_DEFAULT_SERVER_URL=https://work.example \
|
|
50
|
+
SMRT_APP_MCP_SERVER_NAME=work-mcp \
|
|
51
|
+
SMRT_APP_MCP_SERVER_VERSION=1.0.0 \
|
|
52
|
+
smrt-app --stdio-mcp
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`SMRT_APP_*` is reserved for non-secret executable configuration. Runtime
|
|
56
|
+
credentials continue to use the app-specific environment/config resolution
|
|
57
|
+
implemented by `createAppCli`; never pass bearer tokens as executable options.
|
|
58
|
+
|
|
20
59
|
## Quick start
|
|
21
60
|
|
|
22
61
|
```ts
|
|
@@ -114,6 +153,8 @@ await cli.startMcpBridge({ name: 'acme-mcp', version: '1.0.0' });
|
|
|
114
153
|
The package also ships the generic `smrt-mcp-bridge` binary. The remote app
|
|
115
154
|
surface is typically mounted with
|
|
116
155
|
[`@happyvertical/smrt-app-mcp`](../smrt-app-mcp/README.md).
|
|
156
|
+
The binary rejects non-loopback plaintext HTTP origins before resolving or
|
|
157
|
+
sending stored credentials; use HTTPS for remote application servers.
|
|
117
158
|
|
|
118
159
|
The bridge canonicalizes its `tools/list` catalog by tool name and emits a
|
|
119
160
|
one-day `private` cache lifetime. The catalog is tied to the configured local
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `smrt-app` — configuration-driven executable for a published SMRT app CLI.
|
|
3
|
+
*
|
|
4
|
+
* The wrapper accepts non-secret identity before `--`; everything after `--`
|
|
5
|
+
* is forwarded unchanged to the canonical `createAppCli` command dispatcher.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=smrt-app.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { i as runAppCliExecutable } from "../src-DCo67mp8.js";
|
|
3
|
+
//#region src/bin/smrt-app.ts
|
|
4
|
+
/**
|
|
5
|
+
* `smrt-app` — configuration-driven executable for a published SMRT app CLI.
|
|
6
|
+
*
|
|
7
|
+
* The wrapper accepts non-secret identity before `--`; everything after `--`
|
|
8
|
+
* is forwarded unchanged to the canonical `createAppCli` command dispatcher.
|
|
9
|
+
*/
|
|
10
|
+
try {
|
|
11
|
+
await runAppCliExecutable();
|
|
12
|
+
} catch (error) {
|
|
13
|
+
const message = error instanceof Error ? error.message : "Unknown error.";
|
|
14
|
+
process.stderr.write(`smrt-app: ${message}\n`);
|
|
15
|
+
process.exitCode = 2;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { m as runMcpStdioBridge } from "../config-CuTFiGxX.js";
|
|
3
3
|
//#region src/bin/smrt-mcp-bridge.ts
|
|
4
4
|
/**
|
|
5
5
|
* `smrt-mcp-bridge` — generic stdio MCP bridge.
|
|
@@ -39,6 +39,7 @@ await runMcpStdioBridge({
|
|
|
39
39
|
envPrefix,
|
|
40
40
|
appSlug: readArg("app-slug") ?? process.env.SMRT_MCP_APP_SLUG,
|
|
41
41
|
defaultServerUrl: readArg("default-server-url") ?? process.env.SMRT_MCP_DEFAULT_SERVER_URL,
|
|
42
|
+
requireSecureServerUrl: true,
|
|
42
43
|
serverInfo: {
|
|
43
44
|
name: readArg("name") ?? process.env.SMRT_MCP_SERVER_NAME ?? "smrt-app-mcp",
|
|
44
45
|
version: readArg("version") ?? process.env.SMRT_MCP_SERVER_VERSION ?? "0.0.0"
|
|
@@ -159,6 +159,27 @@ async function runMcpStdioBridge(options) {
|
|
|
159
159
|
* @packageDocumentation
|
|
160
160
|
*/
|
|
161
161
|
var DEFAULT_LOCAL_SERVER = "http://localhost:5173";
|
|
162
|
+
/** Enforce the executable-safe server URL policy without echoing URL values. */
|
|
163
|
+
function assertSecureServerUrl(value) {
|
|
164
|
+
let url;
|
|
165
|
+
try {
|
|
166
|
+
url = new URL(value);
|
|
167
|
+
} catch {
|
|
168
|
+
throw new Error("Invalid server URL configuration.");
|
|
169
|
+
}
|
|
170
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash) throw new Error("Invalid server URL configuration.");
|
|
171
|
+
const localHosts = /* @__PURE__ */ new Set([
|
|
172
|
+
"localhost",
|
|
173
|
+
"127.0.0.1",
|
|
174
|
+
"[::1]"
|
|
175
|
+
]);
|
|
176
|
+
if (url.protocol === "http:" && !localHosts.has(url.hostname)) throw new Error("Invalid server URL configuration.");
|
|
177
|
+
}
|
|
178
|
+
function normalizeServerUrl(context, value) {
|
|
179
|
+
const normalized = value.replace(/\/+$/u, "");
|
|
180
|
+
if (context.requireSecureServerUrl) assertSecureServerUrl(normalized);
|
|
181
|
+
return normalized;
|
|
182
|
+
}
|
|
162
183
|
function configFilePath(context) {
|
|
163
184
|
const override = process.env[`${context.envPrefix}_CLI_CONFIG`];
|
|
164
185
|
if (override) return override;
|
|
@@ -201,7 +222,7 @@ async function saveCliConfig(context, config) {
|
|
|
201
222
|
/** Resolve the server URL: env var → config file → `defaultServerUrl`. */
|
|
202
223
|
async function getServerUrl(context, config) {
|
|
203
224
|
const resolved = config ?? await loadCliConfig(context);
|
|
204
|
-
return (process.env[`${context.envPrefix}_SERVER_URL`] ?? resolved.serverUrl ?? context.defaultServerUrl ?? DEFAULT_LOCAL_SERVER)
|
|
225
|
+
return normalizeServerUrl(context, process.env[`${context.envPrefix}_SERVER_URL`] ?? resolved.serverUrl ?? context.defaultServerUrl ?? DEFAULT_LOCAL_SERVER);
|
|
205
226
|
}
|
|
206
227
|
/** Resolve a bearer token only when it is bound to the exact target server. */
|
|
207
228
|
async function getStoredToken(context, config, serverUrl) {
|
|
@@ -271,7 +292,7 @@ async function requestJson(context, path, init = {}, options = {}) {
|
|
|
271
292
|
*/
|
|
272
293
|
async function requestJsonResult(context, path, init = {}, options = {}) {
|
|
273
294
|
const config = options.loadedConfig ?? await loadCliConfig(context);
|
|
274
|
-
const serverUrl = (options.serverUrl ?? await getServerUrl(context, config))
|
|
295
|
+
const serverUrl = normalizeServerUrl(context, options.serverUrl ?? await getServerUrl(context, config));
|
|
275
296
|
const token = await getStoredToken(context, config, serverUrl);
|
|
276
297
|
const headers = new Headers(init.headers);
|
|
277
298
|
if (options.requireAuth && options.auth !== false && !token) return failure(401, {
|
|
@@ -432,4 +453,4 @@ async function readBodyWithCap(response, maxBytes) {
|
|
|
432
453
|
return new TextDecoder().decode(Buffer.concat(chunks.map((c) => Buffer.from(c))));
|
|
433
454
|
}
|
|
434
455
|
//#endregion
|
|
435
|
-
export {
|
|
456
|
+
export { getStoredToken as a, requestJson as c, saveCliConfig as d, createMcpStdioBridge as f, toMcpTransportError as h, getServerUrl as i, requestJsonResult as l, runMcpStdioBridge as m, assertSecureServerUrl as n, loadCliConfig as o, formatMcpCallResult as p, clearStoredToken as r, redactTransportValue as s, AppCliRequestError as t, saveAuth as u };
|
package/dist/index.d.ts
CHANGED
|
@@ -43,6 +43,16 @@ export declare interface AppCliContext {
|
|
|
43
43
|
stderr: NodeJS.WriteStream;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
export declare interface AppCliExecutableConfig {
|
|
47
|
+
cliOptions: Pick<CreateAppCliOptions, 'name' | 'envPrefix' | 'configDir' | 'defaultServerUrl' | 'requireSecureServerUrl'>;
|
|
48
|
+
argv: string[];
|
|
49
|
+
mcp?: {
|
|
50
|
+
name: string;
|
|
51
|
+
version: string;
|
|
52
|
+
};
|
|
53
|
+
stdioMcp: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
46
56
|
/** Error compatibility wrapper returned by the legacy throwing helper. */
|
|
47
57
|
export declare class AppCliRequestError extends Error {
|
|
48
58
|
readonly status: number;
|
|
@@ -79,6 +89,9 @@ declare interface AppResultMetadata {
|
|
|
79
89
|
expectedVersion?: DeclaredActionField;
|
|
80
90
|
}
|
|
81
91
|
|
|
92
|
+
/** Enforce the executable-safe server URL policy without echoing URL values. */
|
|
93
|
+
export declare function assertSecureServerUrl(value: string): void;
|
|
94
|
+
|
|
82
95
|
export declare function buildFlagParser(schema: Record<string, unknown> | undefined, options?: ParserOptions): BuildParserResult;
|
|
83
96
|
|
|
84
97
|
export declare interface BuildParserResult {
|
|
@@ -134,6 +147,11 @@ export declare interface CliConfigContext {
|
|
|
134
147
|
appSlug?: string;
|
|
135
148
|
/** Fallback server URL if neither env nor config sets one. */
|
|
136
149
|
defaultServerUrl?: string;
|
|
150
|
+
/**
|
|
151
|
+
* Require HTTPS for every resolved or request-level server URL, while still
|
|
152
|
+
* permitting loopback HTTP for local development.
|
|
153
|
+
*/
|
|
154
|
+
requireSecureServerUrl?: boolean;
|
|
137
155
|
}
|
|
138
156
|
|
|
139
157
|
export declare type CliResource = CliResource_2;
|
|
@@ -194,6 +212,8 @@ export declare interface CreateAppCliOptions {
|
|
|
194
212
|
configDir?: string;
|
|
195
213
|
/** Baked-in default server URL when neither env nor config sets one. */
|
|
196
214
|
defaultServerUrl?: string;
|
|
215
|
+
/** Enforce HTTPS or loopback HTTP for every effective server URL. */
|
|
216
|
+
requireSecureServerUrl?: boolean;
|
|
197
217
|
/**
|
|
198
218
|
* App-specific commands. Dispatched BEFORE built-ins (`auth`,
|
|
199
219
|
* `resources`, `mcp`) and before the resource-slug dispatcher, so an
|
|
@@ -393,6 +413,9 @@ declare interface OutputOptions {
|
|
|
393
413
|
stdoutIsTty?: boolean;
|
|
394
414
|
}
|
|
395
415
|
|
|
416
|
+
/** Parse and validate the generic executable's non-secret configuration. */
|
|
417
|
+
export declare function parseAppCliExecutableConfig(argv: string[], env?: NodeJS.ProcessEnv): AppCliExecutableConfig;
|
|
418
|
+
|
|
396
419
|
export declare interface ParsedArgs {
|
|
397
420
|
/** Body to send (JSON serialised, for POST/PUT/PATCH). May be empty. */
|
|
398
421
|
body: Record<string, unknown>;
|
|
@@ -511,6 +534,15 @@ declare interface ResourceListResponseBody {
|
|
|
511
534
|
artifact?: DiscoveryConformanceArtifact;
|
|
512
535
|
}
|
|
513
536
|
|
|
537
|
+
/** Create the canonical app CLI and execute its forwarded command or bridge. */
|
|
538
|
+
export declare function runAppCliExecutable(options?: RunAppCliExecutableOptions): Promise<void>;
|
|
539
|
+
|
|
540
|
+
export declare interface RunAppCliExecutableOptions {
|
|
541
|
+
argv?: string[];
|
|
542
|
+
env?: NodeJS.ProcessEnv;
|
|
543
|
+
createCli?: (options: CreateAppCliOptions) => AppCli;
|
|
544
|
+
}
|
|
545
|
+
|
|
514
546
|
/**
|
|
515
547
|
* One-call entry point — start the factory-owned stdio bridge. The returned
|
|
516
548
|
* promise resolves once the stdio listener is installed.
|