frontmcp 0.12.2 → 1.0.0-beta.2
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 +1 -1
- package/package.json +6 -5
- package/src/commands/build/bundler.js +1 -1
- package/src/commands/build/bundler.js.map +1 -1
- package/src/commands/build/exec/cli-runtime/cli-bundler.d.ts +17 -0
- package/src/commands/build/exec/cli-runtime/cli-bundler.js +75 -0
- package/src/commands/build/exec/cli-runtime/cli-bundler.js.map +1 -0
- package/src/commands/build/exec/cli-runtime/credential-store.d.ts +22 -0
- package/src/commands/build/exec/cli-runtime/credential-store.js +140 -0
- package/src/commands/build/exec/cli-runtime/credential-store.js.map +1 -0
- package/src/commands/build/exec/cli-runtime/daemon-client.d.ts +16 -0
- package/src/commands/build/exec/cli-runtime/daemon-client.js +169 -0
- package/src/commands/build/exec/cli-runtime/daemon-client.js.map +1 -0
- package/src/commands/build/exec/cli-runtime/generate-cli-entry.d.ts +37 -0
- package/src/commands/build/exec/cli-runtime/generate-cli-entry.js +1287 -0
- package/src/commands/build/exec/cli-runtime/generate-cli-entry.js.map +1 -0
- package/src/commands/build/exec/cli-runtime/index.d.ts +9 -0
- package/src/commands/build/exec/cli-runtime/index.js +32 -0
- package/src/commands/build/exec/cli-runtime/index.js.map +1 -0
- package/src/commands/build/exec/cli-runtime/oauth-helper.d.ts +9 -0
- package/src/commands/build/exec/cli-runtime/oauth-helper.js +224 -0
- package/src/commands/build/exec/cli-runtime/oauth-helper.js.map +1 -0
- package/src/commands/build/exec/cli-runtime/output-formatter.d.ts +46 -0
- package/src/commands/build/exec/cli-runtime/output-formatter.js +168 -0
- package/src/commands/build/exec/cli-runtime/output-formatter.js.map +1 -0
- package/src/commands/build/exec/cli-runtime/schema-extractor.d.ts +57 -0
- package/src/commands/build/exec/cli-runtime/schema-extractor.js +129 -0
- package/src/commands/build/exec/cli-runtime/schema-extractor.js.map +1 -0
- package/src/commands/build/exec/cli-runtime/schema-to-commander.d.ts +38 -0
- package/src/commands/build/exec/cli-runtime/schema-to-commander.js +172 -0
- package/src/commands/build/exec/cli-runtime/schema-to-commander.js.map +1 -0
- package/src/commands/build/exec/cli-runtime/session-manager.d.ts +16 -0
- package/src/commands/build/exec/cli-runtime/session-manager.js +122 -0
- package/src/commands/build/exec/cli-runtime/session-manager.js.map +1 -0
- package/src/commands/build/exec/config.d.ts +25 -0
- package/src/commands/build/exec/config.js +0 -1
- package/src/commands/build/exec/config.js.map +1 -1
- package/src/commands/build/exec/esbuild-bundler.d.ts +4 -1
- package/src/commands/build/exec/esbuild-bundler.js +28 -9
- package/src/commands/build/exec/esbuild-bundler.js.map +1 -1
- package/src/commands/build/exec/index.d.ts +7 -2
- package/src/commands/build/exec/index.js +159 -9
- package/src/commands/build/exec/index.js.map +1 -1
- package/src/commands/build/exec/manifest.d.ts +14 -0
- package/src/commands/build/exec/manifest.js.map +1 -1
- package/src/commands/build/exec/runner-script.d.ts +1 -1
- package/src/commands/build/exec/runner-script.js +48 -5
- package/src/commands/build/exec/runner-script.js.map +1 -1
- package/src/commands/build/exec/sea-builder.d.ts +18 -0
- package/src/commands/build/exec/sea-builder.js +81 -0
- package/src/commands/build/exec/sea-builder.js.map +1 -0
- package/src/commands/build/exec/setup.js +0 -2
- package/src/commands/build/exec/setup.js.map +1 -1
- package/src/commands/build/index.d.ts +1 -1
- package/src/commands/build/index.js +3 -3
- package/src/commands/build/index.js.map +1 -1
- package/src/commands/build/register.d.ts +2 -0
- package/src/commands/build/register.js +21 -0
- package/src/commands/build/register.js.map +1 -0
- package/src/commands/{dev.d.ts → dev/dev.d.ts} +1 -1
- package/src/commands/{dev.js → dev/dev.js} +3 -3
- package/src/commands/dev/dev.js.map +1 -0
- package/src/commands/{doctor.js → dev/doctor.js} +5 -4
- package/src/commands/dev/doctor.js.map +1 -0
- package/src/commands/{inspector.js → dev/inspector.js} +1 -1
- package/src/commands/dev/inspector.js.map +1 -0
- package/src/commands/dev/register.d.ts +2 -0
- package/src/commands/dev/register.js +49 -0
- package/src/commands/dev/register.js.map +1 -0
- package/src/commands/{test.d.ts → dev/test.d.ts} +1 -1
- package/src/commands/{test.js → dev/test.js} +5 -5
- package/src/commands/dev/test.js.map +1 -0
- package/src/commands/{configure.d.ts → package/configure.d.ts} +1 -1
- package/src/commands/{configure.js → package/configure.js} +3 -3
- package/src/commands/package/configure.js.map +1 -0
- package/src/commands/package/esm-update.d.ts +19 -0
- package/src/commands/package/esm-update.js +93 -0
- package/src/commands/package/esm-update.js.map +1 -0
- package/src/commands/{install/index.d.ts → package/install.d.ts} +1 -1
- package/src/commands/{install/index.js → package/install.js} +8 -5
- package/src/commands/package/install.js.map +1 -0
- package/src/commands/{install → package}/questionnaire.js +2 -2
- package/src/commands/{install → package}/questionnaire.js.map +1 -1
- package/src/commands/package/register.d.ts +2 -0
- package/src/commands/package/register.js +35 -0
- package/src/commands/package/register.js.map +1 -0
- package/src/commands/{install → package}/registry.js +8 -6
- package/src/commands/package/registry.js.map +1 -0
- package/src/commands/{install → package}/sources/git.js +8 -1
- package/src/commands/package/sources/git.js.map +1 -0
- package/src/commands/{install → package}/sources/local.js.map +1 -1
- package/src/commands/{install → package}/sources/npm.js.map +1 -1
- package/src/commands/{install → package}/types.d.ts +7 -1
- package/src/commands/{install → package}/types.js +4 -0
- package/src/commands/package/types.js.map +1 -0
- package/src/commands/{uninstall.d.ts → package/uninstall.d.ts} +1 -1
- package/src/commands/{uninstall.js → package/uninstall.js} +2 -2
- package/src/commands/package/uninstall.js.map +1 -0
- package/src/{pm/pm.format.d.ts → commands/pm/format.d.ts} +1 -1
- package/src/{pm/pm.format.js → commands/pm/format.js} +2 -2
- package/src/commands/pm/format.js.map +1 -0
- package/src/{pm/pm.health.js → commands/pm/health.js} +1 -1
- package/src/commands/pm/health.js.map +1 -0
- package/src/commands/pm/index.d.ts +9 -0
- package/src/{pm → commands/pm}/index.js +32 -32
- package/src/commands/pm/index.js.map +1 -0
- package/src/commands/{list.d.ts → pm/list.d.ts} +1 -1
- package/src/commands/{list.js → pm/list.js} +3 -3
- package/src/commands/pm/list.js.map +1 -0
- package/src/{pm/pm.logs.js → commands/pm/log-utils.js} +9 -9
- package/src/commands/pm/log-utils.js.map +1 -0
- package/src/commands/{logs.d.ts → pm/logs.d.ts} +1 -1
- package/src/commands/{logs.js → pm/logs.js} +6 -6
- package/src/commands/pm/logs.js.map +1 -0
- package/src/{pm/pm.manager.d.ts → commands/pm/manager.d.ts} +1 -1
- package/src/{pm/pm.manager.js → commands/pm/manager.js} +21 -21
- package/src/commands/pm/manager.js.map +1 -0
- package/src/{pm/pm.paths.d.ts → commands/pm/paths.d.ts} +1 -0
- package/src/{pm/pm.paths.js → commands/pm/paths.js} +2 -1
- package/src/commands/pm/paths.js.map +1 -0
- package/src/{pm/pm.pidfile.d.ts → commands/pm/pidfile.d.ts} +1 -1
- package/src/{pm/pm.pidfile.js → commands/pm/pidfile.js} +8 -8
- package/src/commands/pm/pidfile.js.map +1 -0
- package/src/commands/pm/register.d.ts +2 -0
- package/src/commands/pm/register.js +83 -0
- package/src/commands/pm/register.js.map +1 -0
- package/src/commands/{restart.d.ts → pm/restart.d.ts} +1 -1
- package/src/commands/{restart.js → pm/restart.js} +4 -4
- package/src/commands/pm/restart.js.map +1 -0
- package/src/{pm/pm.service.d.ts → commands/pm/service-gen.d.ts} +1 -1
- package/src/{pm/pm.service.js → commands/pm/service-gen.js} +5 -5
- package/src/commands/pm/service-gen.js.map +1 -0
- package/src/commands/{service.d.ts → pm/service.d.ts} +1 -1
- package/src/commands/{service.js → pm/service.js} +6 -6
- package/src/commands/pm/service.js.map +1 -0
- package/src/commands/{socket.d.ts → pm/socket.d.ts} +1 -1
- package/src/commands/{socket.js → pm/socket.js} +3 -3
- package/src/commands/pm/socket.js.map +1 -0
- package/src/{pm/pm.spawn.d.ts → commands/pm/spawn.d.ts} +1 -1
- package/src/{pm/pm.spawn.js → commands/pm/spawn.js} +15 -15
- package/src/commands/pm/spawn.js.map +1 -0
- package/src/commands/{start.d.ts → pm/start.d.ts} +1 -1
- package/src/commands/{start.js → pm/start.js} +6 -6
- package/src/commands/pm/start.js.map +1 -0
- package/src/commands/{status.d.ts → pm/status.d.ts} +1 -1
- package/src/commands/{status.js → pm/status.js} +5 -5
- package/src/commands/pm/status.js.map +1 -0
- package/src/commands/{stop.d.ts → pm/stop.d.ts} +1 -1
- package/src/commands/{stop.js → pm/stop.js} +3 -3
- package/src/commands/pm/stop.js.map +1 -0
- package/src/{pm/pm.types.js → commands/pm/types.js} +1 -1
- package/src/commands/pm/types.js.map +1 -0
- package/src/commands/{create.js → scaffold/create.js} +91 -20
- package/src/commands/scaffold/create.js.map +1 -0
- package/src/commands/scaffold/register.d.ts +2 -0
- package/src/commands/scaffold/register.js +28 -0
- package/src/commands/scaffold/register.js.map +1 -0
- package/src/{args.d.ts → core/args.d.ts} +7 -1
- package/src/{args.js → core/args.js} +8 -0
- package/src/core/args.js.map +1 -0
- package/src/core/bridge.d.ts +9 -0
- package/src/core/bridge.js +75 -0
- package/src/core/bridge.js.map +1 -0
- package/src/core/cli.d.ts +8 -0
- package/src/core/cli.js +23 -0
- package/src/core/cli.js.map +1 -0
- package/src/core/colors.js.map +1 -0
- package/src/core/help.d.ts +7 -0
- package/src/core/help.js +83 -0
- package/src/core/help.js.map +1 -0
- package/src/core/index.d.ts +7 -0
- package/src/core/index.js +22 -0
- package/src/core/index.js.map +1 -0
- package/src/core/program.d.ts +2 -0
- package/src/core/program.js +23 -0
- package/src/core/program.js.map +1 -0
- package/src/core/tsconfig.js.map +1 -0
- package/src/{version.js → core/version.js} +1 -1
- package/src/core/version.js.map +1 -0
- package/src/index.d.ts +1 -1
- package/src/index.js +2 -5
- package/src/index.js.map +1 -1
- package/src/{utils → shared}/env.js +2 -2
- package/src/shared/env.js.map +1 -0
- package/src/{utils → shared}/fs.js +2 -2
- package/src/shared/fs.js.map +1 -0
- package/src/shared/index.d.ts +3 -0
- package/src/shared/index.js +14 -0
- package/src/shared/index.js.map +1 -0
- package/src/shared/prompts.js.map +1 -0
- package/src/args.js.map +0 -1
- package/src/cli.d.ts +0 -5
- package/src/cli.js +0 -241
- package/src/cli.js.map +0 -1
- package/src/colors.js.map +0 -1
- package/src/commands/configure.js.map +0 -1
- package/src/commands/create.js.map +0 -1
- package/src/commands/dev.js.map +0 -1
- package/src/commands/doctor.js.map +0 -1
- package/src/commands/inspector.js.map +0 -1
- package/src/commands/install/index.js.map +0 -1
- package/src/commands/install/registry.js.map +0 -1
- package/src/commands/install/sources/git.js.map +0 -1
- package/src/commands/install/types.js.map +0 -1
- package/src/commands/list.js.map +0 -1
- package/src/commands/logs.js.map +0 -1
- package/src/commands/restart.js.map +0 -1
- package/src/commands/service.js.map +0 -1
- package/src/commands/socket.js.map +0 -1
- package/src/commands/start.js.map +0 -1
- package/src/commands/status.js.map +0 -1
- package/src/commands/stop.js.map +0 -1
- package/src/commands/template.d.ts +0 -13
- package/src/commands/template.js +0 -193
- package/src/commands/template.js.map +0 -1
- package/src/commands/test.js.map +0 -1
- package/src/commands/uninstall.js.map +0 -1
- package/src/pm/index.d.ts +0 -9
- package/src/pm/index.js.map +0 -1
- package/src/pm/pm.format.js.map +0 -1
- package/src/pm/pm.health.js.map +0 -1
- package/src/pm/pm.logs.js.map +0 -1
- package/src/pm/pm.manager.js.map +0 -1
- package/src/pm/pm.paths.js.map +0 -1
- package/src/pm/pm.pidfile.js.map +0 -1
- package/src/pm/pm.service.js.map +0 -1
- package/src/pm/pm.spawn.js.map +0 -1
- package/src/pm/pm.types.js.map +0 -1
- package/src/templates/3rd-party-integration/src/http-client.d.ts +0 -20
- package/src/templates/3rd-party-integration/src/http-client.js +0 -105
- package/src/templates/3rd-party-integration/src/http-client.js.map +0 -1
- package/src/templates/3rd-party-integration/src/mcp-http-types.d.ts +0 -63
- package/src/templates/3rd-party-integration/src/mcp-http-types.js +0 -52
- package/src/templates/3rd-party-integration/src/mcp-http-types.js.map +0 -1
- package/src/templates/3rd-party-integration/src/tools/example.list.d.ts +0 -45
- package/src/templates/3rd-party-integration/src/tools/example.list.js +0 -85
- package/src/templates/3rd-party-integration/src/tools/example.list.js.map +0 -1
- package/src/tsconfig.js.map +0 -1
- package/src/utils/env.js.map +0 -1
- package/src/utils/fs.js.map +0 -1
- package/src/utils/prompts.js.map +0 -1
- package/src/version.js.map +0 -1
- /package/src/commands/{doctor.d.ts → dev/doctor.d.ts} +0 -0
- /package/src/commands/{inspector.d.ts → dev/inspector.d.ts} +0 -0
- /package/src/commands/{install → package}/questionnaire.d.ts +0 -0
- /package/src/commands/{install → package}/registry.d.ts +0 -0
- /package/src/commands/{install → package}/sources/git.d.ts +0 -0
- /package/src/commands/{install → package}/sources/local.d.ts +0 -0
- /package/src/commands/{install → package}/sources/local.js +0 -0
- /package/src/commands/{install → package}/sources/npm.d.ts +0 -0
- /package/src/commands/{install → package}/sources/npm.js +0 -0
- /package/src/{pm/pm.health.d.ts → commands/pm/health.d.ts} +0 -0
- /package/src/{pm/pm.logs.d.ts → commands/pm/log-utils.d.ts} +0 -0
- /package/src/{pm/pm.types.d.ts → commands/pm/types.d.ts} +0 -0
- /package/src/commands/{create.d.ts → scaffold/create.d.ts} +0 -0
- /package/src/{colors.d.ts → core/colors.d.ts} +0 -0
- /package/src/{colors.js → core/colors.js} +0 -0
- /package/src/{tsconfig.d.ts → core/tsconfig.d.ts} +0 -0
- /package/src/{tsconfig.js → core/tsconfig.js} +0 -0
- /package/src/{version.d.ts → core/version.d.ts} +0 -0
- /package/src/{utils → shared}/env.d.ts +0 -0
- /package/src/{utils → shared}/fs.d.ts +0 -0
- /package/src/{utils → shared}/prompts.d.ts +0 -0
- /package/src/{utils → shared}/prompts.js +0 -0
|
@@ -0,0 +1,1287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Generates the CLI entry point TypeScript/JavaScript source code.
|
|
4
|
+
* This creates a commander.js-based CLI where each MCP tool is a subcommand.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.RESERVED_COMMANDS = void 0;
|
|
8
|
+
exports.resolveToolCommandName = resolveToolCommandName;
|
|
9
|
+
exports.generateCliEntry = generateCliEntry;
|
|
10
|
+
exports.extractTemplateParams = extractTemplateParams;
|
|
11
|
+
const schema_extractor_1 = require("./schema-extractor");
|
|
12
|
+
const schema_to_commander_1 = require("./schema-to-commander");
|
|
13
|
+
exports.RESERVED_COMMANDS = new Set([
|
|
14
|
+
'resource', 'template', 'prompt', 'subscribe',
|
|
15
|
+
'login', 'logout', 'connect', 'serve', 'daemon',
|
|
16
|
+
'doctor', 'install', 'uninstall', 'sessions', 'help', 'version',
|
|
17
|
+
'skills', 'job', 'workflow',
|
|
18
|
+
]);
|
|
19
|
+
/**
|
|
20
|
+
* Resolve tool command name, appending '-tool' suffix if it conflicts with a built-in command.
|
|
21
|
+
* Returns { cmdName, wasRenamed } so the caller can log a warning at build time.
|
|
22
|
+
*/
|
|
23
|
+
function resolveToolCommandName(toolName) {
|
|
24
|
+
const cmdName = (0, schema_to_commander_1.camelToKebab)(toolName).replace(/_/g, '-');
|
|
25
|
+
if (exports.RESERVED_COMMANDS.has(cmdName)) {
|
|
26
|
+
return { cmdName: `${cmdName}-tool`, wasRenamed: true };
|
|
27
|
+
}
|
|
28
|
+
return { cmdName, wasRenamed: false };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Generate the CLI entry source code (CJS module).
|
|
32
|
+
*/
|
|
33
|
+
function generateCliEntry(options) {
|
|
34
|
+
const { appName, appVersion, description, serverBundleFilename, outputDefault, schema, excludeTools, oauthConfig, authRequired, } = options;
|
|
35
|
+
const capabilities = schema.capabilities;
|
|
36
|
+
const filteredTools = schema.tools.filter((t) => !excludeTools.includes(t.name) && !schema_extractor_1.SYSTEM_TOOL_NAMES.has(t.name));
|
|
37
|
+
const selfContained = !!options.selfContained;
|
|
38
|
+
const sections = [
|
|
39
|
+
generateHeader(appName, appVersion, description, serverBundleFilename, outputDefault, authRequired, capabilities, oauthConfig, selfContained),
|
|
40
|
+
generateToolCommands(filteredTools, appName),
|
|
41
|
+
generateResourceCommands(schema),
|
|
42
|
+
generateTemplateCommands(schema.resourceTemplates),
|
|
43
|
+
generatePromptCommands(schema.prompts),
|
|
44
|
+
capabilities.skills ? generateSkillsCommands() : '',
|
|
45
|
+
capabilities.jobs ? generateJobCommands(schema.jobs) : '',
|
|
46
|
+
capabilities.workflows ? generateWorkflowCommands() : '',
|
|
47
|
+
generateSubscribeCommands(),
|
|
48
|
+
...(authRequired ? [
|
|
49
|
+
generateLoginCommand(appName, oauthConfig),
|
|
50
|
+
generateLogoutCommand(appName),
|
|
51
|
+
generateSessionCommands(),
|
|
52
|
+
] : []),
|
|
53
|
+
generateServeCommand(serverBundleFilename),
|
|
54
|
+
generateDoctorCommand(appName, options.nativeDeps),
|
|
55
|
+
generateInstallCommand(appName, options.nativeDeps, selfContained),
|
|
56
|
+
generateDaemonCommands(appName, serverBundleFilename),
|
|
57
|
+
generateFooter(),
|
|
58
|
+
];
|
|
59
|
+
return sections.filter(Boolean).join('\n\n');
|
|
60
|
+
}
|
|
61
|
+
function generateHeader(appName, appVersion, description, serverBundleFilename, outputDefault, authRequired, capabilities, oauthConfig, selfContained) {
|
|
62
|
+
const hasOAuth = !!oauthConfig;
|
|
63
|
+
// Build the group routing map dynamically
|
|
64
|
+
const skillsRouting = capabilities.skills ? `\n else if (name === 'skills') groups['Skills'].push(sub);` : '';
|
|
65
|
+
const jobsRouting = capabilities.jobs ? `\n else if (name === 'job') groups['Jobs'].push(sub);` : '';
|
|
66
|
+
const workflowsRouting = capabilities.workflows ? `\n else if (name === 'workflow') groups['Workflows'].push(sub);` : '';
|
|
67
|
+
const authRouting = authRequired ? `\n else if (['login', 'logout', 'sessions', 'connect'].indexOf(name) !== -1) groups['Auth'].push(sub);` : '';
|
|
68
|
+
// Build the groups object dynamically
|
|
69
|
+
const groupEntries = [
|
|
70
|
+
` 'Tools': []`,
|
|
71
|
+
` 'Resources & Prompts': []`,
|
|
72
|
+
];
|
|
73
|
+
if (capabilities.skills)
|
|
74
|
+
groupEntries.push(` 'Skills': []`);
|
|
75
|
+
if (capabilities.jobs)
|
|
76
|
+
groupEntries.push(` 'Jobs': []`);
|
|
77
|
+
if (capabilities.workflows)
|
|
78
|
+
groupEntries.push(` 'Workflows': []`);
|
|
79
|
+
if (authRequired)
|
|
80
|
+
groupEntries.push(` 'Auth': []`);
|
|
81
|
+
groupEntries.push(` 'Subscriptions': []`);
|
|
82
|
+
groupEntries.push(` 'System': []`);
|
|
83
|
+
return `'use strict';
|
|
84
|
+
|
|
85
|
+
var { Command, Option } = require('commander');
|
|
86
|
+
var path = require('path');
|
|
87
|
+
var fs = require('fs');
|
|
88
|
+
var os = require('os');
|
|
89
|
+
var fmt = require('./output-formatter');
|
|
90
|
+
${authRequired ? "var sessions = require('./session-manager');\nvar creds = require('./credential-store');" : ''}
|
|
91
|
+
${hasOAuth ? "var oauthHelper = require('./oauth-helper');" : ''}
|
|
92
|
+
|
|
93
|
+
var APP_NAME = ${JSON.stringify(appName)};
|
|
94
|
+
var SCRIPT_DIR = __dirname;
|
|
95
|
+
var FRONTMCP_HOME = process.env.FRONTMCP_HOME || path.join(os.homedir(), '.frontmcp');
|
|
96
|
+
${selfContained
|
|
97
|
+
? `// Self-contained: server bundle and SDK are inlined by esbuild
|
|
98
|
+
var SERVER_BUNDLE = '../${serverBundleFilename}';`
|
|
99
|
+
: `var SERVER_BUNDLE = path.join(SCRIPT_DIR, ${JSON.stringify(serverBundleFilename)});`}
|
|
100
|
+
|
|
101
|
+
var _client = null;
|
|
102
|
+
async function getClient() {
|
|
103
|
+
if (_client) return _client;
|
|
104
|
+
|
|
105
|
+
// Try daemon first — Unix socket HTTP (~5-15ms vs ~420ms in-process)
|
|
106
|
+
var socketPath = path.join(FRONTMCP_HOME, 'sockets', APP_NAME + '.sock');
|
|
107
|
+
if (fs.existsSync(socketPath)) {
|
|
108
|
+
try {
|
|
109
|
+
var daemonClient = require('./daemon-client');
|
|
110
|
+
var dc = daemonClient.createDaemonClient(socketPath);
|
|
111
|
+
await dc.ping();
|
|
112
|
+
_client = dc;
|
|
113
|
+
return _client;
|
|
114
|
+
} catch (_) { /* daemon not available, fall through */ }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Fallback: in-process connect (with CLI mode for faster init)
|
|
118
|
+
var mod = require(${selfContained ? `'../${serverBundleFilename}'` : 'SERVER_BUNDLE'});
|
|
119
|
+
var configOrClass = mod.default || mod;
|
|
120
|
+
var sdk = require('@frontmcp/sdk');
|
|
121
|
+
var connect = sdk.connect || sdk.direct.connect;${authRequired ? `
|
|
122
|
+
var sessionName = sessions.getActiveSessionName();
|
|
123
|
+
var store = creds.createCredentialStore();
|
|
124
|
+
var credBlob = await store.get(sessionName);
|
|
125
|
+
var connectOpts = credBlob ? { authToken: credBlob.token, mode: 'cli' } : { mode: 'cli' };
|
|
126
|
+
_client = await connect(configOrClass, connectOpts);` : `
|
|
127
|
+
_client = await connect(configOrClass, { mode: 'cli' });`}
|
|
128
|
+
return _client;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
var program = new Command();
|
|
132
|
+
program
|
|
133
|
+
.name(${JSON.stringify(appName)})
|
|
134
|
+
.version(${JSON.stringify(appVersion)})
|
|
135
|
+
.description(${JSON.stringify(description || `${appName} CLI`)})
|
|
136
|
+
.option('--output <mode>', 'Output format: text or json', ${JSON.stringify(outputDefault)});
|
|
137
|
+
|
|
138
|
+
program.configureHelp({
|
|
139
|
+
sortSubcommands: false,
|
|
140
|
+
formatHelp: function(cmd, helper) {
|
|
141
|
+
var groups = {
|
|
142
|
+
${groupEntries.join(',\n')}
|
|
143
|
+
};
|
|
144
|
+
var toolCmdNames = cmd._toolCommandNames || [];
|
|
145
|
+
cmd.commands.forEach(function(sub) {
|
|
146
|
+
var name = sub.name();
|
|
147
|
+
if (toolCmdNames.indexOf(name) !== -1) groups['Tools'].push(sub);
|
|
148
|
+
else if (['resource', 'template', 'prompt'].indexOf(name) !== -1) groups['Resources & Prompts'].push(sub);${skillsRouting}${jobsRouting}${workflowsRouting}${authRouting}
|
|
149
|
+
else if (name === 'subscribe') groups['Subscriptions'].push(sub);
|
|
150
|
+
else groups['System'].push(sub);
|
|
151
|
+
});
|
|
152
|
+
var termWidth = helper.padWidth(cmd, helper);
|
|
153
|
+
var lines = [];
|
|
154
|
+
lines.push('Usage: ' + helper.commandUsage(cmd));
|
|
155
|
+
lines.push('');
|
|
156
|
+
var desc = helper.commandDescription(cmd);
|
|
157
|
+
if (desc) { lines.push(desc); lines.push(''); }
|
|
158
|
+
var globalOpts = helper.formatHelp ? helper.visibleOptions(cmd) : [];
|
|
159
|
+
if (globalOpts.length > 0) {
|
|
160
|
+
lines.push('Options:');
|
|
161
|
+
globalOpts.forEach(function(opt) {
|
|
162
|
+
lines.push(' ' + helper.optionTerm(opt).padEnd(termWidth) + ' ' + helper.optionDescription(opt));
|
|
163
|
+
});
|
|
164
|
+
lines.push('');
|
|
165
|
+
}
|
|
166
|
+
Object.keys(groups).forEach(function(groupName) {
|
|
167
|
+
var cmds = groups[groupName];
|
|
168
|
+
if (cmds.length === 0) return;
|
|
169
|
+
lines.push(groupName + ':');
|
|
170
|
+
cmds.forEach(function(sub) {
|
|
171
|
+
lines.push(' ' + helper.subcommandTerm(sub).padEnd(termWidth) + ' ' + helper.subcommandDescription(sub));
|
|
172
|
+
});
|
|
173
|
+
lines.push('');
|
|
174
|
+
});
|
|
175
|
+
return lines.join('\\n');
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
program.action(function() { program.outputHelp(); });`;
|
|
180
|
+
}
|
|
181
|
+
function generateToolCommands(tools, appName) {
|
|
182
|
+
if (tools.length === 0)
|
|
183
|
+
return '// No tools extracted\nprogram._toolCommandNames = [];';
|
|
184
|
+
const cmdNames = [];
|
|
185
|
+
const commands = tools.map((tool) => {
|
|
186
|
+
const { cmdName } = resolveToolCommandName(tool.name);
|
|
187
|
+
cmdNames.push(cmdName);
|
|
188
|
+
const { options } = (0, schema_to_commander_1.schemaToCommander)(tool.inputSchema);
|
|
189
|
+
const optionLines = options.map((o) => ` ${(0, schema_to_commander_1.generateOptionCode)(o)}`).join('\n');
|
|
190
|
+
return `program
|
|
191
|
+
.command(${JSON.stringify(cmdName)})
|
|
192
|
+
.description(${JSON.stringify(tool.description)})
|
|
193
|
+
${optionLines}
|
|
194
|
+
.action(async function(opts) {
|
|
195
|
+
try {
|
|
196
|
+
var client = await getClient();
|
|
197
|
+
var args = {};
|
|
198
|
+
var rawOpts = this.opts();
|
|
199
|
+
${generateArgMapping(tool)}
|
|
200
|
+
var result = await client.callTool(${JSON.stringify(tool.name)}, args);
|
|
201
|
+
var mode = program.opts().output || ${JSON.stringify('text')};
|
|
202
|
+
console.log(fmt.formatToolResult(result, mode));
|
|
203
|
+
} catch (err) {
|
|
204
|
+
var meta = err && err._meta ? err._meta : (err && err.data && err.data._meta ? err.data._meta : null);
|
|
205
|
+
if (meta && meta.authorization_required) {
|
|
206
|
+
console.error('Authorization required' + (meta.app ? ' for ' + meta.app : ''));
|
|
207
|
+
if (meta.auth_url) console.error('Authorize at: ' + meta.auth_url);
|
|
208
|
+
console.error('Or run: ' + ${JSON.stringify(appName)} + ' login');
|
|
209
|
+
} else {
|
|
210
|
+
console.error('Error:', err.message || err);
|
|
211
|
+
}
|
|
212
|
+
process.exitCode = 1;
|
|
213
|
+
}
|
|
214
|
+
});`;
|
|
215
|
+
});
|
|
216
|
+
return `program._toolCommandNames = ${JSON.stringify(cmdNames)};\n\n${commands.join('\n\n')}`;
|
|
217
|
+
}
|
|
218
|
+
function generateArgMapping(tool) {
|
|
219
|
+
const props = tool.inputSchema.properties;
|
|
220
|
+
if (!props)
|
|
221
|
+
return '';
|
|
222
|
+
const mappings = Object.keys(props).map((propName) => {
|
|
223
|
+
const propSchema = props[propName];
|
|
224
|
+
const kebab = (0, schema_to_commander_1.camelToKebab)(propName);
|
|
225
|
+
// Commander converts kebab-case flags to camelCase in opts()
|
|
226
|
+
const camel = kebabToCamel(kebab);
|
|
227
|
+
// Resolve type for object detection
|
|
228
|
+
let propType = propSchema?.type;
|
|
229
|
+
if (Array.isArray(propType)) {
|
|
230
|
+
propType = propType.find((t) => t !== 'null') || propType[0];
|
|
231
|
+
}
|
|
232
|
+
if (propType === 'object') {
|
|
233
|
+
return `if (rawOpts[${JSON.stringify(camel)}] !== undefined) {
|
|
234
|
+
try { args[${JSON.stringify(propName)}] = JSON.parse(rawOpts[${JSON.stringify(camel)}]); }
|
|
235
|
+
catch (_jsonErr) { console.error('Invalid JSON for --${kebab}'); process.exitCode = 1; return; }
|
|
236
|
+
}`;
|
|
237
|
+
}
|
|
238
|
+
return `if (rawOpts[${JSON.stringify(camel)}] !== undefined) args[${JSON.stringify(propName)}] = rawOpts[${JSON.stringify(camel)}];`;
|
|
239
|
+
});
|
|
240
|
+
return mappings.join('\n ');
|
|
241
|
+
}
|
|
242
|
+
function generateJobArgMapping(inputSchema) {
|
|
243
|
+
const props = inputSchema.properties;
|
|
244
|
+
if (!props)
|
|
245
|
+
return '';
|
|
246
|
+
const mappings = Object.keys(props).map((propName) => {
|
|
247
|
+
const propSchema = props[propName];
|
|
248
|
+
const kebab = (0, schema_to_commander_1.camelToKebab)(propName);
|
|
249
|
+
const camel = kebabToCamel(kebab);
|
|
250
|
+
let propType = propSchema?.type;
|
|
251
|
+
if (Array.isArray(propType)) {
|
|
252
|
+
propType = propType.find((t) => t !== 'null') || propType[0];
|
|
253
|
+
}
|
|
254
|
+
if (propType === 'object') {
|
|
255
|
+
return `if (rawOpts[${JSON.stringify(camel)}] !== undefined) {
|
|
256
|
+
try { input[${JSON.stringify(propName)}] = JSON.parse(rawOpts[${JSON.stringify(camel)}]); }
|
|
257
|
+
catch (_jsonErr) { console.error('Invalid JSON for --${kebab}'); process.exitCode = 1; return; }
|
|
258
|
+
}`;
|
|
259
|
+
}
|
|
260
|
+
return `if (rawOpts[${JSON.stringify(camel)}] !== undefined) input[${JSON.stringify(propName)}] = rawOpts[${JSON.stringify(camel)}];`;
|
|
261
|
+
});
|
|
262
|
+
return mappings.join('\n ');
|
|
263
|
+
}
|
|
264
|
+
function generateResourceCommands(_schema) {
|
|
265
|
+
return `var resourceCmd = program.command('resource').description('Resource operations');
|
|
266
|
+
|
|
267
|
+
resourceCmd
|
|
268
|
+
.command('list')
|
|
269
|
+
.description('List available resources')
|
|
270
|
+
.action(async function() {
|
|
271
|
+
try {
|
|
272
|
+
var client = await getClient();
|
|
273
|
+
var result = await client.listResources();
|
|
274
|
+
var mode = program.opts().output || 'text';
|
|
275
|
+
if (mode === 'json') {
|
|
276
|
+
console.log(JSON.stringify(result, null, 2));
|
|
277
|
+
} else {
|
|
278
|
+
var resources = result.resources || [];
|
|
279
|
+
if (resources.length === 0) { console.log('No resources available.'); return; }
|
|
280
|
+
resources.forEach(function(r) {
|
|
281
|
+
console.log(' ' + r.uri + (r.description ? ' - ' + r.description : ''));
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
} catch (err) {
|
|
285
|
+
console.error('Error:', err.message || err);
|
|
286
|
+
process.exitCode = 1;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
resourceCmd
|
|
291
|
+
.command('read <uri>')
|
|
292
|
+
.description('Read a resource by URI')
|
|
293
|
+
.action(async function(uri) {
|
|
294
|
+
try {
|
|
295
|
+
var client = await getClient();
|
|
296
|
+
var result = await client.readResource(uri);
|
|
297
|
+
var mode = program.opts().output || 'text';
|
|
298
|
+
console.log(fmt.formatResourceResult(result, mode));
|
|
299
|
+
} catch (err) {
|
|
300
|
+
console.error('Error:', err.message || err);
|
|
301
|
+
process.exitCode = 1;
|
|
302
|
+
}
|
|
303
|
+
});`;
|
|
304
|
+
}
|
|
305
|
+
function generateTemplateCommands(templates) {
|
|
306
|
+
if (!templates || templates.length === 0)
|
|
307
|
+
return '// No resource templates extracted';
|
|
308
|
+
const subcommands = templates.map((tmpl) => {
|
|
309
|
+
const cmdName = (0, schema_to_commander_1.camelToKebab)(tmpl.name).replace(/_/g, '-');
|
|
310
|
+
// Extract {param} placeholders from URI template
|
|
311
|
+
const paramNames = extractTemplateParams(tmpl.uriTemplate);
|
|
312
|
+
const optionLines = paramNames
|
|
313
|
+
.map((p) => ` .requiredOption('--${(0, schema_to_commander_1.camelToKebab)(p)} <value>', 'Template parameter: ${p}')`)
|
|
314
|
+
.join('\n');
|
|
315
|
+
const paramMapping = paramNames
|
|
316
|
+
.map((p) => {
|
|
317
|
+
const camel = kebabToCamel((0, schema_to_commander_1.camelToKebab)(p));
|
|
318
|
+
return `uri = uri.replace('{${p}}', encodeURIComponent(rawOpts[${JSON.stringify(camel)}]));`;
|
|
319
|
+
})
|
|
320
|
+
.join('\n ');
|
|
321
|
+
return `templateCmd
|
|
322
|
+
.command(${JSON.stringify(cmdName)})
|
|
323
|
+
.description(${JSON.stringify(tmpl.description || `Read resource from template: ${tmpl.uriTemplate}`)})
|
|
324
|
+
${optionLines}
|
|
325
|
+
.action(async function(opts) {
|
|
326
|
+
try {
|
|
327
|
+
var client = await getClient();
|
|
328
|
+
var rawOpts = this.opts();
|
|
329
|
+
var uri = ${JSON.stringify(tmpl.uriTemplate)};
|
|
330
|
+
${paramMapping}
|
|
331
|
+
var result = await client.readResource(uri);
|
|
332
|
+
var mode = program.opts().output || 'text';
|
|
333
|
+
console.log(fmt.formatResourceResult(result, mode));
|
|
334
|
+
} catch (err) {
|
|
335
|
+
console.error('Error:', err.message || err);
|
|
336
|
+
process.exitCode = 1;
|
|
337
|
+
}
|
|
338
|
+
});`;
|
|
339
|
+
});
|
|
340
|
+
return `var templateCmd = program.command('template').description('Resource template operations');
|
|
341
|
+
|
|
342
|
+
templateCmd
|
|
343
|
+
.command('list')
|
|
344
|
+
.description('List available resource templates')
|
|
345
|
+
.action(async function() {
|
|
346
|
+
try {
|
|
347
|
+
var client = await getClient();
|
|
348
|
+
var result = await client.listResourceTemplates();
|
|
349
|
+
var mode = program.opts().output || 'text';
|
|
350
|
+
if (mode === 'json') {
|
|
351
|
+
console.log(JSON.stringify(result, null, 2));
|
|
352
|
+
} else {
|
|
353
|
+
var templates = result.resourceTemplates || [];
|
|
354
|
+
if (templates.length === 0) { console.log('No resource templates available.'); return; }
|
|
355
|
+
templates.forEach(function(t) {
|
|
356
|
+
console.log(' ' + t.uriTemplate + (t.description ? ' - ' + t.description : ''));
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
} catch (err) {
|
|
360
|
+
console.error('Error:', err.message || err);
|
|
361
|
+
process.exitCode = 1;
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
${subcommands.join('\n\n')}`;
|
|
366
|
+
}
|
|
367
|
+
function generatePromptCommands(prompts) {
|
|
368
|
+
const subcommands = prompts.map((prompt) => {
|
|
369
|
+
const cmdName = (0, schema_to_commander_1.camelToKebab)(prompt.name).replace(/_/g, '-');
|
|
370
|
+
const argOptions = (prompt.arguments || [])
|
|
371
|
+
.map((a) => {
|
|
372
|
+
const flag = `--${(0, schema_to_commander_1.camelToKebab)(a.name)} <value>`;
|
|
373
|
+
const desc = a.description || '';
|
|
374
|
+
return a.required
|
|
375
|
+
? ` .requiredOption('${flag}', '${escapeStr(desc)}')`
|
|
376
|
+
: ` .option('${flag}', '${escapeStr(desc)}')`;
|
|
377
|
+
})
|
|
378
|
+
.join('\n');
|
|
379
|
+
return `promptCmd
|
|
380
|
+
.command(${JSON.stringify(cmdName)})
|
|
381
|
+
.description(${JSON.stringify(prompt.description || '')})
|
|
382
|
+
${argOptions}
|
|
383
|
+
.action(async function(opts) {
|
|
384
|
+
try {
|
|
385
|
+
var client = await getClient();
|
|
386
|
+
var args = {};
|
|
387
|
+
var rawOpts = this.opts();
|
|
388
|
+
${(prompt.arguments || []).map((a) => {
|
|
389
|
+
const camel = kebabToCamel((0, schema_to_commander_1.camelToKebab)(a.name));
|
|
390
|
+
return `if (rawOpts[${JSON.stringify(camel)}] !== undefined) args[${JSON.stringify(a.name)}] = rawOpts[${JSON.stringify(camel)}];`;
|
|
391
|
+
}).join('\n ')}
|
|
392
|
+
var result = await client.getPrompt(${JSON.stringify(prompt.name)}, args);
|
|
393
|
+
var mode = program.opts().output || 'text';
|
|
394
|
+
console.log(fmt.formatPromptResult(result, mode));
|
|
395
|
+
} catch (err) {
|
|
396
|
+
console.error('Error:', err.message || err);
|
|
397
|
+
process.exitCode = 1;
|
|
398
|
+
}
|
|
399
|
+
});`;
|
|
400
|
+
});
|
|
401
|
+
return `var promptCmd = program.command('prompt').description('Prompt operations');
|
|
402
|
+
|
|
403
|
+
promptCmd
|
|
404
|
+
.command('list')
|
|
405
|
+
.description('List available prompts')
|
|
406
|
+
.action(async function() {
|
|
407
|
+
try {
|
|
408
|
+
var client = await getClient();
|
|
409
|
+
var result = await client.listPrompts();
|
|
410
|
+
var mode = program.opts().output || 'text';
|
|
411
|
+
if (mode === 'json') {
|
|
412
|
+
console.log(JSON.stringify(result, null, 2));
|
|
413
|
+
} else {
|
|
414
|
+
var prompts = result.prompts || [];
|
|
415
|
+
if (prompts.length === 0) { console.log('No prompts available.'); return; }
|
|
416
|
+
prompts.forEach(function(p) {
|
|
417
|
+
console.log(' ' + p.name + (p.description ? ' - ' + p.description : ''));
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
} catch (err) {
|
|
421
|
+
console.error('Error:', err.message || err);
|
|
422
|
+
process.exitCode = 1;
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
${subcommands.join('\n\n')}`;
|
|
427
|
+
}
|
|
428
|
+
function generateSkillsCommands() {
|
|
429
|
+
return `var skillsCmd = program.command('skills').description('Skill operations');
|
|
430
|
+
|
|
431
|
+
skillsCmd
|
|
432
|
+
.command('search [query]')
|
|
433
|
+
.description('Search for skills')
|
|
434
|
+
.action(async function(query) {
|
|
435
|
+
try {
|
|
436
|
+
var client = await getClient();
|
|
437
|
+
var result = await client.searchSkills(query || '');
|
|
438
|
+
var mode = program.opts().output || 'text';
|
|
439
|
+
if (mode === 'json') {
|
|
440
|
+
console.log(JSON.stringify(result, null, 2));
|
|
441
|
+
} else {
|
|
442
|
+
var skills = result.skills || result || [];
|
|
443
|
+
if (Array.isArray(skills) && skills.length === 0) { console.log('No skills found.'); return; }
|
|
444
|
+
if (Array.isArray(skills)) {
|
|
445
|
+
skills.forEach(function(s) {
|
|
446
|
+
console.log(' ' + (s.name || s.id || JSON.stringify(s)));
|
|
447
|
+
});
|
|
448
|
+
} else {
|
|
449
|
+
console.log(JSON.stringify(result, null, 2));
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
} catch (err) {
|
|
453
|
+
console.error('Error:', err.message || err);
|
|
454
|
+
process.exitCode = 1;
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
skillsCmd
|
|
459
|
+
.command('load <ids...>')
|
|
460
|
+
.description('Load skills by ID')
|
|
461
|
+
.action(async function(ids) {
|
|
462
|
+
try {
|
|
463
|
+
var client = await getClient();
|
|
464
|
+
var result = await client.loadSkills(ids);
|
|
465
|
+
var mode = program.opts().output || 'text';
|
|
466
|
+
if (mode === 'json') {
|
|
467
|
+
console.log(JSON.stringify(result, null, 2));
|
|
468
|
+
} else {
|
|
469
|
+
console.log('Loaded ' + ids.length + ' skill(s).');
|
|
470
|
+
if (result && typeof result === 'object') {
|
|
471
|
+
console.log(JSON.stringify(result, null, 2));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
} catch (err) {
|
|
475
|
+
console.error('Error:', err.message || err);
|
|
476
|
+
process.exitCode = 1;
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
skillsCmd
|
|
481
|
+
.command('list')
|
|
482
|
+
.description('List available skills')
|
|
483
|
+
.action(async function() {
|
|
484
|
+
try {
|
|
485
|
+
var client = await getClient();
|
|
486
|
+
var result = await client.listSkills();
|
|
487
|
+
var mode = program.opts().output || 'text';
|
|
488
|
+
if (mode === 'json') {
|
|
489
|
+
console.log(JSON.stringify(result, null, 2));
|
|
490
|
+
} else {
|
|
491
|
+
var skills = result.skills || result || [];
|
|
492
|
+
if (Array.isArray(skills) && skills.length === 0) { console.log('No skills available.'); return; }
|
|
493
|
+
if (Array.isArray(skills)) {
|
|
494
|
+
skills.forEach(function(s) {
|
|
495
|
+
console.log(' ' + (s.name || s.id || JSON.stringify(s)));
|
|
496
|
+
});
|
|
497
|
+
} else {
|
|
498
|
+
console.log(JSON.stringify(result, null, 2));
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
} catch (err) {
|
|
502
|
+
console.error('Error:', err.message || err);
|
|
503
|
+
process.exitCode = 1;
|
|
504
|
+
}
|
|
505
|
+
});`;
|
|
506
|
+
}
|
|
507
|
+
function generateJobCommands(jobs) {
|
|
508
|
+
// Generate typed 'run' subcommands for each known job
|
|
509
|
+
const runSubcommands = jobs.map((job) => {
|
|
510
|
+
const jobCmdName = (0, schema_to_commander_1.camelToKebab)(job.name).replace(/_/g, '-');
|
|
511
|
+
if (job.inputSchema) {
|
|
512
|
+
const { options } = (0, schema_to_commander_1.schemaToCommander)(job.inputSchema);
|
|
513
|
+
const optionLines = options.map((o) => ` ${(0, schema_to_commander_1.generateOptionCode)(o)}`).join('\n');
|
|
514
|
+
const argMapping = generateJobArgMapping(job.inputSchema);
|
|
515
|
+
return `jobRunCmd
|
|
516
|
+
.command(${JSON.stringify(jobCmdName)})
|
|
517
|
+
.description(${JSON.stringify(job.description || `Run the ${job.name} job`)})
|
|
518
|
+
${optionLines}
|
|
519
|
+
.option('--background', 'Run in background mode')
|
|
520
|
+
.action(async function(opts) {
|
|
521
|
+
try {
|
|
522
|
+
var client = await getClient();
|
|
523
|
+
var input = {};
|
|
524
|
+
var rawOpts = this.opts();
|
|
525
|
+
${argMapping}
|
|
526
|
+
var result = await client.executeJob(${JSON.stringify(job.name)}, input, { background: !!rawOpts.background });
|
|
527
|
+
var mode = program.opts().output || 'text';
|
|
528
|
+
if (mode === 'json') {
|
|
529
|
+
console.log(JSON.stringify(result, null, 2));
|
|
530
|
+
} else {
|
|
531
|
+
if (rawOpts.background && result && result.runId) {
|
|
532
|
+
console.log('Job started. Run ID: ' + result.runId);
|
|
533
|
+
} else {
|
|
534
|
+
console.log(JSON.stringify(result, null, 2));
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
} catch (err) {
|
|
538
|
+
console.error('Error:', err.message || err);
|
|
539
|
+
process.exitCode = 1;
|
|
540
|
+
}
|
|
541
|
+
});`;
|
|
542
|
+
}
|
|
543
|
+
// No inputSchema — fall back to generic --input <json>
|
|
544
|
+
return `jobRunCmd
|
|
545
|
+
.command(${JSON.stringify(jobCmdName)})
|
|
546
|
+
.description(${JSON.stringify(job.description || `Run the ${job.name} job`)})
|
|
547
|
+
.option('--input <json>', 'Job input as JSON string')
|
|
548
|
+
.option('--background', 'Run in background mode')
|
|
549
|
+
.action(async function(opts) {
|
|
550
|
+
try {
|
|
551
|
+
var client = await getClient();
|
|
552
|
+
var input = {};
|
|
553
|
+
if (opts.input) {
|
|
554
|
+
try { input = JSON.parse(opts.input); }
|
|
555
|
+
catch (_) { console.error('Invalid JSON for --input'); process.exitCode = 1; return; }
|
|
556
|
+
}
|
|
557
|
+
var result = await client.executeJob(${JSON.stringify(job.name)}, input, { background: !!opts.background });
|
|
558
|
+
var mode = program.opts().output || 'text';
|
|
559
|
+
if (mode === 'json') {
|
|
560
|
+
console.log(JSON.stringify(result, null, 2));
|
|
561
|
+
} else {
|
|
562
|
+
if (opts.background && result && result.runId) {
|
|
563
|
+
console.log('Job started. Run ID: ' + result.runId);
|
|
564
|
+
} else {
|
|
565
|
+
console.log(JSON.stringify(result, null, 2));
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
} catch (err) {
|
|
569
|
+
console.error('Error:', err.message || err);
|
|
570
|
+
process.exitCode = 1;
|
|
571
|
+
}
|
|
572
|
+
});`;
|
|
573
|
+
});
|
|
574
|
+
// Generic fallback 'run' for jobs not known at build time
|
|
575
|
+
const genericRun = `jobRunCmd
|
|
576
|
+
.command('_run <name>')
|
|
577
|
+
.description('Run a job by name (generic)')
|
|
578
|
+
.option('--input <json>', 'Job input as JSON string')
|
|
579
|
+
.option('--background', 'Run in background mode')
|
|
580
|
+
.action(async function(name, opts) {
|
|
581
|
+
try {
|
|
582
|
+
var client = await getClient();
|
|
583
|
+
var input = {};
|
|
584
|
+
if (opts.input) {
|
|
585
|
+
try { input = JSON.parse(opts.input); }
|
|
586
|
+
catch (_) { console.error('Invalid JSON for --input'); process.exitCode = 1; return; }
|
|
587
|
+
}
|
|
588
|
+
var result = await client.executeJob(name, input, { background: !!opts.background });
|
|
589
|
+
var mode = program.opts().output || 'text';
|
|
590
|
+
if (mode === 'json') {
|
|
591
|
+
console.log(JSON.stringify(result, null, 2));
|
|
592
|
+
} else {
|
|
593
|
+
if (opts.background && result && result.runId) {
|
|
594
|
+
console.log('Job started. Run ID: ' + result.runId);
|
|
595
|
+
} else {
|
|
596
|
+
console.log(JSON.stringify(result, null, 2));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
} catch (err) {
|
|
600
|
+
console.error('Error:', err.message || err);
|
|
601
|
+
process.exitCode = 1;
|
|
602
|
+
}
|
|
603
|
+
});`;
|
|
604
|
+
return `var jobCmd = program.command('job').description('Job operations');
|
|
605
|
+
|
|
606
|
+
jobCmd
|
|
607
|
+
.command('list')
|
|
608
|
+
.description('List available jobs')
|
|
609
|
+
.action(async function() {
|
|
610
|
+
try {
|
|
611
|
+
var client = await getClient();
|
|
612
|
+
var result = await client.listJobs();
|
|
613
|
+
var mode = program.opts().output || 'text';
|
|
614
|
+
if (mode === 'json') {
|
|
615
|
+
console.log(JSON.stringify(result, null, 2));
|
|
616
|
+
} else {
|
|
617
|
+
var jobs = result.jobs || result || [];
|
|
618
|
+
if (Array.isArray(jobs) && jobs.length === 0) { console.log('No jobs available.'); return; }
|
|
619
|
+
if (Array.isArray(jobs)) {
|
|
620
|
+
jobs.forEach(function(j) {
|
|
621
|
+
console.log(' ' + (j.name || j.id || JSON.stringify(j)));
|
|
622
|
+
});
|
|
623
|
+
} else {
|
|
624
|
+
console.log(JSON.stringify(result, null, 2));
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
} catch (err) {
|
|
628
|
+
console.error('Error:', err.message || err);
|
|
629
|
+
process.exitCode = 1;
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
var jobRunCmd = jobCmd.command('run').description('Run a job');
|
|
634
|
+
|
|
635
|
+
${runSubcommands.join('\n\n')}
|
|
636
|
+
|
|
637
|
+
${jobs.length > 0 ? genericRun : `jobRunCmd
|
|
638
|
+
.argument('<name>', 'Job name')
|
|
639
|
+
.option('--input <json>', 'Job input as JSON string')
|
|
640
|
+
.option('--background', 'Run in background mode')
|
|
641
|
+
.action(async function(name, opts) {
|
|
642
|
+
try {
|
|
643
|
+
var client = await getClient();
|
|
644
|
+
var input = {};
|
|
645
|
+
if (opts.input) {
|
|
646
|
+
try { input = JSON.parse(opts.input); }
|
|
647
|
+
catch (_) { console.error('Invalid JSON for --input'); process.exitCode = 1; return; }
|
|
648
|
+
}
|
|
649
|
+
var result = await client.executeJob(name, input, { background: !!opts.background });
|
|
650
|
+
var mode = program.opts().output || 'text';
|
|
651
|
+
if (mode === 'json') {
|
|
652
|
+
console.log(JSON.stringify(result, null, 2));
|
|
653
|
+
} else {
|
|
654
|
+
if (opts.background && result && result.runId) {
|
|
655
|
+
console.log('Job started. Run ID: ' + result.runId);
|
|
656
|
+
} else {
|
|
657
|
+
console.log(JSON.stringify(result, null, 2));
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
} catch (err) {
|
|
661
|
+
console.error('Error:', err.message || err);
|
|
662
|
+
process.exitCode = 1;
|
|
663
|
+
}
|
|
664
|
+
});`}
|
|
665
|
+
|
|
666
|
+
jobCmd
|
|
667
|
+
.command('status <runId>')
|
|
668
|
+
.description('Get the status of a job run')
|
|
669
|
+
.action(async function(runId) {
|
|
670
|
+
try {
|
|
671
|
+
var client = await getClient();
|
|
672
|
+
var result = await client.getJobStatus(runId);
|
|
673
|
+
var mode = program.opts().output || 'text';
|
|
674
|
+
if (mode === 'json') {
|
|
675
|
+
console.log(JSON.stringify(result, null, 2));
|
|
676
|
+
} else {
|
|
677
|
+
console.log('Status: ' + (result.status || JSON.stringify(result)));
|
|
678
|
+
}
|
|
679
|
+
} catch (err) {
|
|
680
|
+
console.error('Error:', err.message || err);
|
|
681
|
+
process.exitCode = 1;
|
|
682
|
+
}
|
|
683
|
+
});`;
|
|
684
|
+
}
|
|
685
|
+
function generateWorkflowCommands() {
|
|
686
|
+
return `var workflowCmd = program.command('workflow').description('Workflow operations');
|
|
687
|
+
|
|
688
|
+
workflowCmd
|
|
689
|
+
.command('list')
|
|
690
|
+
.description('List available workflows')
|
|
691
|
+
.action(async function() {
|
|
692
|
+
try {
|
|
693
|
+
var client = await getClient();
|
|
694
|
+
var result = await client.listWorkflows();
|
|
695
|
+
var mode = program.opts().output || 'text';
|
|
696
|
+
if (mode === 'json') {
|
|
697
|
+
console.log(JSON.stringify(result, null, 2));
|
|
698
|
+
} else {
|
|
699
|
+
var workflows = result.workflows || result || [];
|
|
700
|
+
if (Array.isArray(workflows) && workflows.length === 0) { console.log('No workflows available.'); return; }
|
|
701
|
+
if (Array.isArray(workflows)) {
|
|
702
|
+
workflows.forEach(function(w) {
|
|
703
|
+
console.log(' ' + (w.name || w.id || JSON.stringify(w)));
|
|
704
|
+
});
|
|
705
|
+
} else {
|
|
706
|
+
console.log(JSON.stringify(result, null, 2));
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
} catch (err) {
|
|
710
|
+
console.error('Error:', err.message || err);
|
|
711
|
+
process.exitCode = 1;
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
workflowCmd
|
|
716
|
+
.command('run <name>')
|
|
717
|
+
.description('Run a workflow by name')
|
|
718
|
+
.option('--input <json>', 'Workflow input as JSON string')
|
|
719
|
+
.option('--background', 'Run in background mode')
|
|
720
|
+
.action(async function(name, opts) {
|
|
721
|
+
try {
|
|
722
|
+
var client = await getClient();
|
|
723
|
+
var input = {};
|
|
724
|
+
if (opts.input) {
|
|
725
|
+
try { input = JSON.parse(opts.input); }
|
|
726
|
+
catch (_) { console.error('Invalid JSON for --input'); process.exitCode = 1; return; }
|
|
727
|
+
}
|
|
728
|
+
var result = await client.executeWorkflow(name, input, { background: !!opts.background });
|
|
729
|
+
var mode = program.opts().output || 'text';
|
|
730
|
+
if (mode === 'json') {
|
|
731
|
+
console.log(JSON.stringify(result, null, 2));
|
|
732
|
+
} else {
|
|
733
|
+
if (opts.background && result && result.runId) {
|
|
734
|
+
console.log('Workflow started. Run ID: ' + result.runId);
|
|
735
|
+
} else {
|
|
736
|
+
console.log(JSON.stringify(result, null, 2));
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
} catch (err) {
|
|
740
|
+
console.error('Error:', err.message || err);
|
|
741
|
+
process.exitCode = 1;
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
workflowCmd
|
|
746
|
+
.command('status <runId>')
|
|
747
|
+
.description('Get the status of a workflow run')
|
|
748
|
+
.action(async function(runId) {
|
|
749
|
+
try {
|
|
750
|
+
var client = await getClient();
|
|
751
|
+
var result = await client.getWorkflowStatus(runId);
|
|
752
|
+
var mode = program.opts().output || 'text';
|
|
753
|
+
if (mode === 'json') {
|
|
754
|
+
console.log(JSON.stringify(result, null, 2));
|
|
755
|
+
} else {
|
|
756
|
+
console.log('Status: ' + (result.status || JSON.stringify(result)));
|
|
757
|
+
}
|
|
758
|
+
} catch (err) {
|
|
759
|
+
console.error('Error:', err.message || err);
|
|
760
|
+
process.exitCode = 1;
|
|
761
|
+
}
|
|
762
|
+
});`;
|
|
763
|
+
}
|
|
764
|
+
function generateSubscribeCommands() {
|
|
765
|
+
return `var subscribeCmd = program.command('subscribe').description('Subscribe to updates');
|
|
766
|
+
|
|
767
|
+
subscribeCmd
|
|
768
|
+
.command('resource <uri>')
|
|
769
|
+
.description('Stream resource updates (Ctrl+C to stop)')
|
|
770
|
+
.action(async function(uri) {
|
|
771
|
+
try {
|
|
772
|
+
var client = await getClient();
|
|
773
|
+
await client.subscribeResource(uri);
|
|
774
|
+
var mode = program.opts().output || 'text';
|
|
775
|
+
console.log('Subscribed to resource: ' + uri);
|
|
776
|
+
console.log('Waiting for updates... (Ctrl+C to stop)\\n');
|
|
777
|
+
client.onResourceUpdated(function(uri) {
|
|
778
|
+
console.log(fmt.formatSubscriptionEvent({ type: 'resource_updated', uri: uri, timestamp: new Date().toISOString() }, mode));
|
|
779
|
+
});
|
|
780
|
+
process.on('SIGINT', async function() {
|
|
781
|
+
console.log('\\nUnsubscribing...');
|
|
782
|
+
try { await client.unsubscribeResource(uri); } catch (_) { /* ok */ }
|
|
783
|
+
process.exit(0);
|
|
784
|
+
});
|
|
785
|
+
// Keep process alive
|
|
786
|
+
await new Promise(function() {});
|
|
787
|
+
} catch (err) {
|
|
788
|
+
console.error('Error:', err.message || err);
|
|
789
|
+
process.exitCode = 1;
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
subscribeCmd
|
|
794
|
+
.command('notification <name>')
|
|
795
|
+
.description('Stream notifications (Ctrl+C to stop)')
|
|
796
|
+
.action(async function(name) {
|
|
797
|
+
try {
|
|
798
|
+
var client = await getClient();
|
|
799
|
+
var mode = program.opts().output || 'text';
|
|
800
|
+
console.log('Listening for notification: ' + name);
|
|
801
|
+
console.log('Waiting for events... (Ctrl+C to stop)\\n');
|
|
802
|
+
client.onNotification(function(notification) {
|
|
803
|
+
if (notification.method === name || name === '*') {
|
|
804
|
+
console.log(fmt.formatSubscriptionEvent({ type: 'notification', method: notification.method, params: notification.params, timestamp: new Date().toISOString() }, mode));
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
process.on('SIGINT', function() {
|
|
808
|
+
console.log('\\nStopping...');
|
|
809
|
+
process.exit(0);
|
|
810
|
+
});
|
|
811
|
+
// Keep process alive
|
|
812
|
+
await new Promise(function() {});
|
|
813
|
+
} catch (err) {
|
|
814
|
+
console.error('Error:', err.message || err);
|
|
815
|
+
process.exitCode = 1;
|
|
816
|
+
}
|
|
817
|
+
});`;
|
|
818
|
+
}
|
|
819
|
+
function generateLoginCommand(appName, oauthConfig) {
|
|
820
|
+
const serverUrl = oauthConfig?.serverUrl || '';
|
|
821
|
+
const clientId = oauthConfig?.clientId || appName;
|
|
822
|
+
const defaultScope = oauthConfig?.defaultScope || '';
|
|
823
|
+
const portStart = oauthConfig?.portRange?.[0] ?? 17830;
|
|
824
|
+
const portEnd = oauthConfig?.portRange?.[1] ?? 17850;
|
|
825
|
+
const timeout = oauthConfig?.timeout ?? 120000;
|
|
826
|
+
return `program
|
|
827
|
+
.command('login')
|
|
828
|
+
.description('Authenticate via OAuth')
|
|
829
|
+
.option('--server <url>', 'Server URL for OAuth'${serverUrl ? `, ${JSON.stringify(serverUrl)}` : ''})
|
|
830
|
+
.option('--session <name>', 'Session name', 'default')
|
|
831
|
+
.option('--scope <scopes>', 'OAuth scopes'${defaultScope ? `, ${JSON.stringify(defaultScope)}` : ''})
|
|
832
|
+
.option('--no-browser', 'Print URL instead of opening browser')
|
|
833
|
+
.action(async function(opts) {
|
|
834
|
+
var serverUrl = opts.server || process.env.FRONTMCP_SERVER_URL || ${JSON.stringify(serverUrl)};
|
|
835
|
+
if (!serverUrl) {
|
|
836
|
+
console.error('Server URL required. Use --server <url> or set FRONTMCP_SERVER_URL.');
|
|
837
|
+
process.exitCode = 1;
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
try {
|
|
841
|
+
var oauthHelper = require('./oauth-helper');
|
|
842
|
+
var result = await oauthHelper.startOAuthLogin({
|
|
843
|
+
serverUrl: serverUrl,
|
|
844
|
+
clientId: ${JSON.stringify(clientId)},
|
|
845
|
+
scope: opts.scope || ${JSON.stringify(defaultScope)},
|
|
846
|
+
portStart: ${portStart},
|
|
847
|
+
portEnd: ${portEnd},
|
|
848
|
+
timeout: ${timeout},
|
|
849
|
+
noBrowser: !opts.browser
|
|
850
|
+
});
|
|
851
|
+
var sessionName = opts.session || 'default';
|
|
852
|
+
var store = creds.createCredentialStore();
|
|
853
|
+
await store.set(sessionName, result);
|
|
854
|
+
sessions.getOrCreateSession(sessionName);
|
|
855
|
+
console.log('Logged in successfully. Session: ' + sessionName);
|
|
856
|
+
} catch (err) {
|
|
857
|
+
console.error('Login failed:', err.message || err);
|
|
858
|
+
process.exitCode = 1;
|
|
859
|
+
}
|
|
860
|
+
});`;
|
|
861
|
+
}
|
|
862
|
+
function generateLogoutCommand(_appName) {
|
|
863
|
+
return `program
|
|
864
|
+
.command('logout')
|
|
865
|
+
.description('Clear stored credentials')
|
|
866
|
+
.option('--session <name>', 'Session to log out')
|
|
867
|
+
.option('--all', 'Log out of all sessions')
|
|
868
|
+
.action(async function(opts) {
|
|
869
|
+
var store = creds.createCredentialStore();
|
|
870
|
+
if (opts.all) {
|
|
871
|
+
var allSessions = await store.list();
|
|
872
|
+
for (var i = 0; i < allSessions.length; i++) {
|
|
873
|
+
await store.delete(allSessions[i]);
|
|
874
|
+
}
|
|
875
|
+
console.log('Logged out of ' + allSessions.length + ' session(s).');
|
|
876
|
+
} else {
|
|
877
|
+
var sessionName = opts.session || sessions.getActiveSessionName();
|
|
878
|
+
await store.delete(sessionName);
|
|
879
|
+
console.log('Logged out of session: ' + sessionName);
|
|
880
|
+
}
|
|
881
|
+
});`;
|
|
882
|
+
}
|
|
883
|
+
function generateSessionCommands() {
|
|
884
|
+
return `var sessionsCmd = program.command('sessions').description('Session management');
|
|
885
|
+
|
|
886
|
+
sessionsCmd
|
|
887
|
+
.command('list')
|
|
888
|
+
.description('List all sessions')
|
|
889
|
+
.action(function() {
|
|
890
|
+
var list = sessions.listSessions();
|
|
891
|
+
if (list.length === 0) { console.log('No sessions.'); return; }
|
|
892
|
+
list.forEach(function(s) {
|
|
893
|
+
var marker = s.isActive ? ' (active)' : '';
|
|
894
|
+
console.log(' ' + s.name + marker + ' - last used: ' + s.lastUsedAt);
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
sessionsCmd
|
|
899
|
+
.command('switch <name>')
|
|
900
|
+
.description('Switch to a named session')
|
|
901
|
+
.action(function(name) {
|
|
902
|
+
sessions.switchSession(name);
|
|
903
|
+
console.log('Switched to session: ' + name);
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
sessionsCmd
|
|
907
|
+
.command('delete <name>')
|
|
908
|
+
.description('Delete a session')
|
|
909
|
+
.action(async function(name) {
|
|
910
|
+
var store = creds.createCredentialStore();
|
|
911
|
+
await store.delete(name);
|
|
912
|
+
sessions.deleteSession(name);
|
|
913
|
+
console.log('Deleted session: ' + name);
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
program
|
|
917
|
+
.command('connect')
|
|
918
|
+
.description('Authenticate and store credentials')
|
|
919
|
+
.option('--session <name>', 'Session name', 'default')
|
|
920
|
+
.option('--token <token>', 'Auth token (or pass via stdin)')
|
|
921
|
+
.action(async function(opts) {
|
|
922
|
+
var sessionName = opts.session || 'default';
|
|
923
|
+
var token = opts.token;
|
|
924
|
+
if (!token) {
|
|
925
|
+
console.log('Usage: ' + program.name() + ' connect --token <your-token>');
|
|
926
|
+
console.log(' Or pipe token: echo "tok_xxx" | ' + program.name() + ' connect');
|
|
927
|
+
process.exitCode = 1;
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
var store = creds.createCredentialStore();
|
|
931
|
+
await store.set(sessionName, { token: token });
|
|
932
|
+
sessions.getOrCreateSession(sessionName);
|
|
933
|
+
console.log('Credentials stored for session: ' + sessionName);
|
|
934
|
+
});`;
|
|
935
|
+
}
|
|
936
|
+
function generateServeCommand(serverBundleFilename) {
|
|
937
|
+
return `program
|
|
938
|
+
.command('serve')
|
|
939
|
+
.description('Start the HTTP/SSE server')
|
|
940
|
+
.option('-p, --port <port>', 'Port number', function(v) { return parseInt(v, 10); })
|
|
941
|
+
.action(async function(opts) {
|
|
942
|
+
var mod = require(path.join(SCRIPT_DIR, ${JSON.stringify(serverBundleFilename)}));
|
|
943
|
+
if (opts.port) process.env.PORT = String(opts.port);
|
|
944
|
+
// The server bundle should self-start when required
|
|
945
|
+
if (typeof mod.start === 'function') await mod.start();
|
|
946
|
+
else if (typeof mod.default?.start === 'function') await mod.default.start();
|
|
947
|
+
});`;
|
|
948
|
+
}
|
|
949
|
+
function generateDoctorCommand(appName, nativeDeps) {
|
|
950
|
+
const checks = [];
|
|
951
|
+
if (nativeDeps.brew?.length) {
|
|
952
|
+
for (const pkg of nativeDeps.brew) {
|
|
953
|
+
checks.push(` { name: ${JSON.stringify(pkg)}, type: 'brew', check: 'brew list ${pkg}' }`);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
if (nativeDeps.apt?.length) {
|
|
957
|
+
for (const pkg of nativeDeps.apt) {
|
|
958
|
+
checks.push(` { name: ${JSON.stringify(pkg)}, type: 'apt', check: 'dpkg -l ${pkg}' }`);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
if (nativeDeps.npm?.length) {
|
|
962
|
+
for (const pkg of nativeDeps.npm) {
|
|
963
|
+
checks.push(` { name: ${JSON.stringify(pkg)}, type: 'npm', check: 'npm ls ${pkg}' }`);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return `program
|
|
967
|
+
.command('doctor')
|
|
968
|
+
.description('Check system dependencies and configuration')
|
|
969
|
+
.option('--fix', 'Attempt to install missing dependencies')
|
|
970
|
+
.action(async function(opts) {
|
|
971
|
+
var exec = require('child_process').execSync;
|
|
972
|
+
var ok = true;
|
|
973
|
+
|
|
974
|
+
// Check Node.js version
|
|
975
|
+
var nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
|
|
976
|
+
if (nodeMajor >= 22) {
|
|
977
|
+
console.log(' [ok] Node.js v' + process.versions.node);
|
|
978
|
+
} else {
|
|
979
|
+
console.log(' [!!] Node.js v' + process.versions.node + ' (>=22 required)');
|
|
980
|
+
ok = false;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
var deps = [
|
|
984
|
+
${checks.join(',\n')}
|
|
985
|
+
];
|
|
986
|
+
|
|
987
|
+
for (var i = 0; i < deps.length; i++) {
|
|
988
|
+
var dep = deps[i];
|
|
989
|
+
try {
|
|
990
|
+
exec(dep.check, { stdio: 'ignore' });
|
|
991
|
+
console.log(' [ok] ' + dep.name + ' (' + dep.type + ')');
|
|
992
|
+
} catch (_) {
|
|
993
|
+
console.log(' [!!] ' + dep.name + ' (' + dep.type + ') - not found');
|
|
994
|
+
ok = false;
|
|
995
|
+
if (opts.fix) {
|
|
996
|
+
try {
|
|
997
|
+
var installCmd = dep.type === 'brew' ? 'brew install ' + dep.name
|
|
998
|
+
: dep.type === 'apt' ? 'sudo apt-get install -y ' + dep.name
|
|
999
|
+
: 'npm install ' + dep.name;
|
|
1000
|
+
console.log(' Installing: ' + installCmd);
|
|
1001
|
+
exec(installCmd, { stdio: 'inherit' });
|
|
1002
|
+
console.log(' [ok] Installed ' + dep.name);
|
|
1003
|
+
} catch (e) {
|
|
1004
|
+
console.log(' [!!] Failed to install ' + dep.name);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// Check FRONTMCP_HOME directory
|
|
1011
|
+
var fs = require('fs');
|
|
1012
|
+
var appDir = require('path').join(FRONTMCP_HOME, 'apps', ${JSON.stringify(appName)});
|
|
1013
|
+
if (fs.existsSync(appDir)) {
|
|
1014
|
+
console.log(' [ok] App directory: ' + appDir);
|
|
1015
|
+
} else {
|
|
1016
|
+
console.log(' [!!] App directory not found: ' + appDir);
|
|
1017
|
+
ok = false;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
if (ok) console.log('\\nAll checks passed.');
|
|
1021
|
+
else {
|
|
1022
|
+
console.log('\\nSome checks failed.' + (opts.fix ? '' : ' Run with --fix to attempt repairs.'));
|
|
1023
|
+
process.exitCode = 1;
|
|
1024
|
+
}
|
|
1025
|
+
});`;
|
|
1026
|
+
}
|
|
1027
|
+
function generateInstallCommand(appName, nativeDeps, selfContained) {
|
|
1028
|
+
const depEntries = [];
|
|
1029
|
+
if (nativeDeps.brew?.length) {
|
|
1030
|
+
for (const pkg of nativeDeps.brew) {
|
|
1031
|
+
depEntries.push(` { name: ${JSON.stringify(pkg)}, type: 'brew', install: 'brew install ${pkg}', check: 'brew list ${pkg}' }`);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
if (nativeDeps.npm?.length) {
|
|
1035
|
+
for (const pkg of nativeDeps.npm) {
|
|
1036
|
+
depEntries.push(` { name: ${JSON.stringify(pkg)}, type: 'npm', install: 'npm install ${pkg}', check: 'npm ls ${pkg}' }`);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
return `program
|
|
1040
|
+
.command('install')
|
|
1041
|
+
.description('Install to ~/.frontmcp/ and set up dependencies')
|
|
1042
|
+
.option('--prefix <path>', 'Installation prefix directory')
|
|
1043
|
+
.option('--bin-dir <path>', 'Directory for symlink (default: ~/.local/bin or /usr/local/bin)')
|
|
1044
|
+
.action(async function(opts) {
|
|
1045
|
+
var fs = require('fs');
|
|
1046
|
+
var pathMod = require('path');
|
|
1047
|
+
var os = require('os');
|
|
1048
|
+
var exec = require('child_process').execSync;
|
|
1049
|
+
var installBase = opts.prefix || FRONTMCP_HOME;
|
|
1050
|
+
var appDir = pathMod.join(installBase, 'apps', ${JSON.stringify(appName)});
|
|
1051
|
+
var dirs = ['', '/data', '/sessions', '/credentials'].map(function(s) { return appDir + s; });
|
|
1052
|
+
|
|
1053
|
+
console.log('Installing ${appName}...');
|
|
1054
|
+
dirs.forEach(function(d) { fs.mkdirSync(d, { recursive: true }); });
|
|
1055
|
+
|
|
1056
|
+
// Copy bundle files
|
|
1057
|
+
var files = fs.readdirSync(SCRIPT_DIR).filter(function(f) {
|
|
1058
|
+
return f.endsWith('.js') || f.endsWith('.json')${selfContained ? " || f.endsWith('-bin')" : ''};
|
|
1059
|
+
});
|
|
1060
|
+
files.forEach(function(f) {
|
|
1061
|
+
fs.copyFileSync(pathMod.join(SCRIPT_DIR, f), pathMod.join(appDir, f));
|
|
1062
|
+
});
|
|
1063
|
+
console.log(' Copied ' + files.length + ' files to ' + appDir);
|
|
1064
|
+
|
|
1065
|
+
// Install native deps
|
|
1066
|
+
var deps = [
|
|
1067
|
+
${depEntries.join(',\n')}
|
|
1068
|
+
];
|
|
1069
|
+
for (var i = 0; i < deps.length; i++) {
|
|
1070
|
+
var dep = deps[i];
|
|
1071
|
+
try { exec(dep.check, { stdio: 'ignore' }); }
|
|
1072
|
+
catch (_) {
|
|
1073
|
+
console.log(' [' + (i + 1) + '/' + deps.length + '] Installing ' + dep.name + ' via ' + dep.type + '...');
|
|
1074
|
+
try { exec(dep.install, { stdio: 'inherit' }); }
|
|
1075
|
+
catch (e) { console.log(' Warning: Failed to install ' + dep.name); }
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// Set execute permission on the entry point
|
|
1080
|
+
var entryFile = pathMod.join(appDir, ${JSON.stringify(selfContained ? `${appName}-cli-bin` : `${appName}-cli.bundle.js`)});
|
|
1081
|
+
try { fs.chmodSync(entryFile, 0o755); } catch (_) { /* ok */ }
|
|
1082
|
+
|
|
1083
|
+
// Create symlink
|
|
1084
|
+
var binDirs = opts.binDir ? [opts.binDir] : ['/usr/local/bin', pathMod.join(os.homedir(), '.local', 'bin')];
|
|
1085
|
+
var linked = false;
|
|
1086
|
+
for (var j = 0; j < binDirs.length && !linked; j++) {
|
|
1087
|
+
try {
|
|
1088
|
+
fs.mkdirSync(binDirs[j], { recursive: true });
|
|
1089
|
+
var linkPath = pathMod.join(binDirs[j], ${JSON.stringify(appName)});
|
|
1090
|
+
try { fs.unlinkSync(linkPath); } catch (_) { /* ok */ }
|
|
1091
|
+
fs.symlinkSync(entryFile, linkPath);
|
|
1092
|
+
console.log(' Symlinked: ' + linkPath);
|
|
1093
|
+
linked = true;
|
|
1094
|
+
} catch (_) { /* try next */ }
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
console.log('\\nInstalled. Run: ${appName} --help');
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
program
|
|
1101
|
+
.command('uninstall')
|
|
1102
|
+
.description('Remove from ~/.frontmcp/ and clean up')
|
|
1103
|
+
.option('--prefix <path>', 'Installation prefix directory')
|
|
1104
|
+
.option('--bin-dir <path>', 'Directory where symlink was created')
|
|
1105
|
+
.action(async function(opts) {
|
|
1106
|
+
var fs = require('fs');
|
|
1107
|
+
var pathMod = require('path');
|
|
1108
|
+
var os = require('os');
|
|
1109
|
+
var uninstallBase = opts.prefix || FRONTMCP_HOME;
|
|
1110
|
+
var appDir = pathMod.join(uninstallBase, 'apps', ${JSON.stringify(appName)});
|
|
1111
|
+
|
|
1112
|
+
// Remove credentials (if auth is enabled)
|
|
1113
|
+
if (typeof creds !== 'undefined') {
|
|
1114
|
+
var store = creds.createCredentialStore();
|
|
1115
|
+
var credSessions = await store.list();
|
|
1116
|
+
for (var i = 0; i < credSessions.length; i++) {
|
|
1117
|
+
await store.delete(credSessions[i]);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// Remove symlink
|
|
1122
|
+
var binDirs = opts.binDir ? [opts.binDir] : ['/usr/local/bin', pathMod.join(os.homedir(), '.local', 'bin')];
|
|
1123
|
+
binDirs.forEach(function(d) {
|
|
1124
|
+
try { fs.unlinkSync(pathMod.join(d, ${JSON.stringify(appName)})); } catch (_) { /* ok */ }
|
|
1125
|
+
});
|
|
1126
|
+
|
|
1127
|
+
// Remove app directory
|
|
1128
|
+
fs.rmSync(appDir, { recursive: true, force: true });
|
|
1129
|
+
console.log('Uninstalled ${appName}.');
|
|
1130
|
+
});`;
|
|
1131
|
+
}
|
|
1132
|
+
function generateDaemonCommands(appName, serverBundleFilename) {
|
|
1133
|
+
return `var daemonCmd = program.command('daemon').description('Daemon management');
|
|
1134
|
+
|
|
1135
|
+
daemonCmd
|
|
1136
|
+
.command('start')
|
|
1137
|
+
.description('Start as a background daemon (Unix socket)')
|
|
1138
|
+
.option('--idle-timeout <ms>', 'Auto-stop after idle period (ms, 0 to disable)', function(v) { return parseInt(v, 10); }, 300000)
|
|
1139
|
+
.action(async function(opts) {
|
|
1140
|
+
var { spawn } = require('child_process');
|
|
1141
|
+
var pathMod = require('path');
|
|
1142
|
+
var pidDir = pathMod.join(FRONTMCP_HOME, 'pids');
|
|
1143
|
+
var logDir = pathMod.join(FRONTMCP_HOME, 'logs');
|
|
1144
|
+
var socketDir = pathMod.join(FRONTMCP_HOME, 'sockets');
|
|
1145
|
+
fs.mkdirSync(pidDir, { recursive: true });
|
|
1146
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
1147
|
+
fs.mkdirSync(socketDir, { recursive: true });
|
|
1148
|
+
|
|
1149
|
+
var socketPath = pathMod.join(socketDir, ${JSON.stringify(appName)} + '.sock');
|
|
1150
|
+
|
|
1151
|
+
// Clean up stale socket file
|
|
1152
|
+
try { fs.unlinkSync(socketPath); } catch (_) { /* ok */ }
|
|
1153
|
+
|
|
1154
|
+
// Check if already running
|
|
1155
|
+
var pidPath = pathMod.join(pidDir, ${JSON.stringify(appName)} + '.pid');
|
|
1156
|
+
try {
|
|
1157
|
+
var existing = JSON.parse(fs.readFileSync(pidPath, 'utf8'));
|
|
1158
|
+
process.kill(existing.pid, 0);
|
|
1159
|
+
console.log('Daemon already running (PID: ' + existing.pid + ').');
|
|
1160
|
+
return;
|
|
1161
|
+
} catch (_) { /* not running, proceed */ }
|
|
1162
|
+
|
|
1163
|
+
var env = Object.assign({}, process.env, {
|
|
1164
|
+
FRONTMCP_DAEMON_SOCKET: socketPath,
|
|
1165
|
+
FRONTMCP_DAEMON_IDLE_TIMEOUT: String(opts.idleTimeout)
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
|
+
var logPath = pathMod.join(logDir, ${JSON.stringify(appName)} + '.log');
|
|
1169
|
+
var out = fs.openSync(logPath, 'a');
|
|
1170
|
+
var err = fs.openSync(logPath, 'a');
|
|
1171
|
+
|
|
1172
|
+
// Start the daemon using runUnixSocket via a small wrapper script
|
|
1173
|
+
// Always use absolute path for the server bundle (SCRIPT_DIR resolves to __dirname at runtime)
|
|
1174
|
+
var serverBundlePath = pathMod.join(SCRIPT_DIR, ${JSON.stringify(serverBundleFilename)});
|
|
1175
|
+
var daemonScript = 'var mod = require(' + JSON.stringify(serverBundlePath) + ');' +
|
|
1176
|
+
'var sdk = require("@frontmcp/sdk");' +
|
|
1177
|
+
'var FrontMcpInstance = sdk.FrontMcpInstance || sdk.default.FrontMcpInstance;' +
|
|
1178
|
+
'var config = mod.default || mod;' +
|
|
1179
|
+
'FrontMcpInstance.runUnixSocket(Object.assign({}, config, { socketPath: ' + JSON.stringify(socketPath) + ' }))' +
|
|
1180
|
+
'.then(function() { console.log("Daemon listening on " + ' + JSON.stringify(socketPath) + '); })' +
|
|
1181
|
+
'.catch(function(e) { console.error("Daemon failed:", e); process.exit(1); });';
|
|
1182
|
+
|
|
1183
|
+
var child = spawn('node', ['-e', daemonScript], {
|
|
1184
|
+
detached: true,
|
|
1185
|
+
stdio: ['ignore', out, err],
|
|
1186
|
+
env: env
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
fs.writeFileSync(pidPath, JSON.stringify({
|
|
1190
|
+
pid: child.pid,
|
|
1191
|
+
socketPath: socketPath,
|
|
1192
|
+
startedAt: new Date().toISOString()
|
|
1193
|
+
}));
|
|
1194
|
+
child.unref();
|
|
1195
|
+
|
|
1196
|
+
// Wait for socket file to appear (max 5s)
|
|
1197
|
+
var waited = 0;
|
|
1198
|
+
while (!fs.existsSync(socketPath) && waited < 5000) {
|
|
1199
|
+
await new Promise(function(r) { setTimeout(r, 100); });
|
|
1200
|
+
waited += 100;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
if (fs.existsSync(socketPath)) {
|
|
1204
|
+
console.log('Daemon started (PID: ' + child.pid + '). Socket: ' + socketPath);
|
|
1205
|
+
console.log('Logs: ' + logPath);
|
|
1206
|
+
} else {
|
|
1207
|
+
console.log('Daemon started (PID: ' + child.pid + ') but socket not yet available.');
|
|
1208
|
+
console.log('Check logs: ' + logPath);
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
|
|
1212
|
+
daemonCmd
|
|
1213
|
+
.command('stop')
|
|
1214
|
+
.description('Stop the daemon')
|
|
1215
|
+
.action(function() {
|
|
1216
|
+
var pathMod = require('path');
|
|
1217
|
+
var pidPath = pathMod.join(FRONTMCP_HOME, 'pids', ${JSON.stringify(appName)} + '.pid');
|
|
1218
|
+
try {
|
|
1219
|
+
var data = JSON.parse(fs.readFileSync(pidPath, 'utf8'));
|
|
1220
|
+
process.kill(data.pid, 'SIGTERM');
|
|
1221
|
+
fs.unlinkSync(pidPath);
|
|
1222
|
+
// Clean up socket file
|
|
1223
|
+
if (data.socketPath) {
|
|
1224
|
+
try { fs.unlinkSync(data.socketPath); } catch (_) { /* ok */ }
|
|
1225
|
+
}
|
|
1226
|
+
console.log('Daemon stopped (PID: ' + data.pid + ').');
|
|
1227
|
+
} catch (e) {
|
|
1228
|
+
console.log('No running daemon found.');
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
daemonCmd
|
|
1233
|
+
.command('status')
|
|
1234
|
+
.description('Check daemon status')
|
|
1235
|
+
.action(function() {
|
|
1236
|
+
var pathMod = require('path');
|
|
1237
|
+
var pidPath = pathMod.join(FRONTMCP_HOME, 'pids', ${JSON.stringify(appName)} + '.pid');
|
|
1238
|
+
try {
|
|
1239
|
+
var data = JSON.parse(fs.readFileSync(pidPath, 'utf8'));
|
|
1240
|
+
try {
|
|
1241
|
+
process.kill(data.pid, 0);
|
|
1242
|
+
var socketStatus = data.socketPath && fs.existsSync(data.socketPath) ? ', socket: active' : '';
|
|
1243
|
+
console.log('Running (PID: ' + data.pid + ', started: ' + data.startedAt + socketStatus + ')');
|
|
1244
|
+
} catch (_) {
|
|
1245
|
+
console.log('Not running (stale PID file).');
|
|
1246
|
+
fs.unlinkSync(pidPath);
|
|
1247
|
+
}
|
|
1248
|
+
} catch (_) { console.log('Not running.'); }
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
daemonCmd
|
|
1252
|
+
.command('logs')
|
|
1253
|
+
.description('Tail daemon logs')
|
|
1254
|
+
.option('-n, --lines <n>', 'Number of lines', function(v) { return parseInt(v, 10); }, 50)
|
|
1255
|
+
.action(function(opts) {
|
|
1256
|
+
var pathMod = require('path');
|
|
1257
|
+
var logPath = pathMod.join(FRONTMCP_HOME, 'logs', ${JSON.stringify(appName)} + '.log');
|
|
1258
|
+
try {
|
|
1259
|
+
var content = fs.readFileSync(logPath, 'utf8');
|
|
1260
|
+
var lines = content.split('\\n');
|
|
1261
|
+
var start = Math.max(0, lines.length - opts.lines);
|
|
1262
|
+
console.log(lines.slice(start).join('\\n'));
|
|
1263
|
+
} catch (_) { console.log('No logs found.'); }
|
|
1264
|
+
});`;
|
|
1265
|
+
}
|
|
1266
|
+
function generateFooter() {
|
|
1267
|
+
return `program.parseAsync(process.argv).catch(function(err) {
|
|
1268
|
+
console.error('Fatal:', err.message || err);
|
|
1269
|
+
process.exit(1);
|
|
1270
|
+
});`;
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Extract {param} placeholders from a URI template string.
|
|
1274
|
+
*/
|
|
1275
|
+
function extractTemplateParams(uriTemplate) {
|
|
1276
|
+
const matches = uriTemplate.match(/\{([^}]+)\}/g);
|
|
1277
|
+
if (!matches)
|
|
1278
|
+
return [];
|
|
1279
|
+
return matches.map((m) => m.slice(1, -1));
|
|
1280
|
+
}
|
|
1281
|
+
function kebabToCamel(str) {
|
|
1282
|
+
return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1283
|
+
}
|
|
1284
|
+
function escapeStr(s) {
|
|
1285
|
+
return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
1286
|
+
}
|
|
1287
|
+
//# sourceMappingURL=generate-cli-entry.js.map
|