@boltic/cli 1.0.27-1 → 1.0.27
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 +38 -14
- package/cli.js +9 -0
- package/commands/integration.js +3 -45
- package/commands/mcp.js +374 -0
- package/helper/folder.js +2 -28
- package/helper/validation.js +6 -78
- package/package.json +2 -1
- package/helper/markdown.js +0 -32
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ⚡ Boltic CLI
|
|
2
2
|
|
|
3
|
-
> **Professional CLI
|
|
3
|
+
> **Professional CLI for interacting with the Boltic platform — create, manage, and publish integrations, workflows, MCPs, and more with enterprise-grade features and a seamless developer experience.**
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@boltic/cli)
|
|
6
6
|
[](https://github.com/bolticio/cli)
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|

|
|
15
15
|
|
|
16
|
-
**Streamline your
|
|
16
|
+
**Streamline your developer workflow on the Boltic platform**
|
|
17
17
|
|
|
18
18
|
[Features](#-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [Documentation](#-documentation) • [Contributing](#-contributing)
|
|
19
19
|
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
- [📦 Installation](#-installation)
|
|
29
29
|
- [🔐 Authentication](#-authentication)
|
|
30
30
|
- [🧩 Integration Management](#-integration-management)
|
|
31
|
+
- [🧠 MCP](#-mcp-model-context-protocol)
|
|
31
32
|
- [📚 Command Reference](#-command-reference)
|
|
32
33
|
- [🛠️ Development Workflow](#️-development-workflow)
|
|
33
34
|
- [🔧 Configuration](#-configuration)
|
|
@@ -42,12 +43,12 @@
|
|
|
42
43
|
## ✨ Features
|
|
43
44
|
|
|
44
45
|
- 🔐 **Secure Authentication** - Enterprise-grade token management with secure storage
|
|
45
|
-
- 🚀 **Rapid
|
|
46
|
+
- 🚀 **Rapid Development** - Create workflows, integrations, and more in minutes, not hours
|
|
46
47
|
- 📦 **Smart Project Management** - Automated folder structure and configuration
|
|
47
48
|
- 🔄 **Real-time Synchronization** - Instant sync with Boltic Cloud platform
|
|
48
49
|
- 🎯 **Type-safe Development** - Support for Workflow Activities and Triggers
|
|
49
50
|
- 🎨 **Rich Interactive UI** - Beautiful command-line interface with progress indicators
|
|
50
|
-
- 📊 **Comprehensive Validation** - Built-in validation for
|
|
51
|
+
- 📊 **Comprehensive Validation** - Built-in validation for platform resources and components
|
|
51
52
|
- 🔧 **Developer Experience** - Hot reload, debugging tools, and comprehensive error handling
|
|
52
53
|
- 🌐 **Multi-platform Support** - Works seamlessly on Windows, macOS, and Linux
|
|
53
54
|
- 📈 **Version Control Integration** - Git-friendly workflow with proper ignore patterns
|
|
@@ -62,7 +63,7 @@ Get up and running with Boltic CLI in under 2 minutes:
|
|
|
62
63
|
# Install Boltic CLI globally
|
|
63
64
|
npm install -g @boltic/cli
|
|
64
65
|
|
|
65
|
-
# Authenticate with your Boltic account
|
|
66
|
+
# Authenticate with your Boltic account (if required for commands)
|
|
66
67
|
boltic login
|
|
67
68
|
|
|
68
69
|
# Create your first integration
|
|
@@ -225,9 +226,7 @@ my-integration/
|
|
|
225
226
|
│ ├── base.json
|
|
226
227
|
│ └── webhook.json
|
|
227
228
|
├── Authentication.mdx
|
|
228
|
-
├──
|
|
229
|
-
│ ├── integration.mdx
|
|
230
|
-
│ └── trigger.mdx
|
|
229
|
+
├── Documentation.mdx
|
|
231
230
|
└── spec.json
|
|
232
231
|
```
|
|
233
232
|
|
|
@@ -239,11 +238,9 @@ my-integration/
|
|
|
239
238
|
- **`base.json`** - Base integration configuration and parameters
|
|
240
239
|
- **`webhook.json`** - Webhook configuration (for trigger integrations)
|
|
241
240
|
|
|
242
|
-
|
|
243
|
-
-
|
|
244
|
-
-
|
|
245
|
-
- `documentation/trigger.mdx` - Trigger documentation (required when trigger_type is "CloudTrigger")
|
|
246
|
-
- `spec.json` - Integration specification and metadata
|
|
241
|
+
- **`Authentication.mdx`** - Authentication documentation in Markdown format
|
|
242
|
+
- **`Documentation.mdx`** - General integration documentation
|
|
243
|
+
- **`spec.json`** - Integration specification and metadata
|
|
247
244
|
|
|
248
245
|
### Managing Existing Integrations
|
|
249
246
|
|
|
@@ -278,6 +275,26 @@ boltic integration submit
|
|
|
278
275
|
|
|
279
276
|
---
|
|
280
277
|
|
|
278
|
+
## 🔌 MCP Management
|
|
279
|
+
|
|
280
|
+
The CLI includes utilities to set up MCP connections for popular clients. This helps your AI tools connect to Boltic services via STDIO or Streamable HTTP.
|
|
281
|
+
|
|
282
|
+
### Usage
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# Basic: configure MCP for Claude Desktop (default client)
|
|
286
|
+
boltic mcp setup https://mcp.boltic.io/sse
|
|
287
|
+
|
|
288
|
+
# Specify client and a friendly name
|
|
289
|
+
boltic mcp setup https://mcp.boltic.io/sse my-boltic --client claude
|
|
290
|
+
|
|
291
|
+
# Supported clients (examples):
|
|
292
|
+
# claude, cline, roocode, windsurf, cursor, vscode, vscode-insiders,
|
|
293
|
+
# witsy, enconvo, boltai, amazon-bedrock, amazonq, librechat, gemini-cli
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
281
298
|
## 📚 Command Reference
|
|
282
299
|
|
|
283
300
|
### Core Commands
|
|
@@ -301,6 +318,13 @@ boltic integration submit
|
|
|
301
318
|
| `boltic integration status` | Check integration status | Interactive selection |
|
|
302
319
|
| `boltic integration help` | Show integration help | |
|
|
303
320
|
|
|
321
|
+
### MCP Commands
|
|
322
|
+
|
|
323
|
+
| Command | Description | Options |
|
|
324
|
+
| ------------------ | -------------------------------------------- | --------------------------------- |
|
|
325
|
+
| `boltic mcp help` | Show help for MCP sub-commands | |
|
|
326
|
+
| `boltic mcp setup` | Configure an MCP server for a specific client| `--client <name>` `--name <alias>`|
|
|
327
|
+
|
|
304
328
|
### Help and Documentation
|
|
305
329
|
|
|
306
330
|
```bash
|
|
@@ -423,7 +447,7 @@ boltic --verbose login
|
|
|
423
447
|
|
|
424
448
|
```bash
|
|
425
449
|
# Verify project structure
|
|
426
|
-
ls -la # Should show: schemas/, Authentication.mdx
|
|
450
|
+
ls -la # Should show: schemas/, Authentication.mdx, Documentation.mdx, spec.json
|
|
427
451
|
|
|
428
452
|
# Check file permissions
|
|
429
453
|
ls -la schemas/
|
package/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import path from "path";
|
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import EnvironmentCommands from "./commands/env.js";
|
|
6
6
|
import IntegrationCommands from "./commands/integration.js";
|
|
7
|
+
import McpCommands from "./commands/mcp.js";
|
|
7
8
|
import AuthCommands from "./commands/login.js";
|
|
8
9
|
|
|
9
10
|
// Create a CLI module with functional approach
|
|
@@ -22,6 +23,10 @@ const createCLI = (consoleUrl, apiUrl, serviceName, env) => {
|
|
|
22
23
|
description: "Manage integrations (create, list)",
|
|
23
24
|
action: (args) => handleIntegration(args),
|
|
24
25
|
},
|
|
26
|
+
mcp: {
|
|
27
|
+
description: "Manage MCPs clients and servers",
|
|
28
|
+
action: (args) => handleMcp(args),
|
|
29
|
+
},
|
|
25
30
|
logout: {
|
|
26
31
|
description: "Logout and clear access token",
|
|
27
32
|
action: AuthCommands.handleLogout,
|
|
@@ -157,6 +162,10 @@ async function handleEnvironment(args) {
|
|
|
157
162
|
await EnvironmentCommands.execute(args);
|
|
158
163
|
}
|
|
159
164
|
|
|
165
|
+
async function handleMcp(args) {
|
|
166
|
+
await McpCommands.execute(args);
|
|
167
|
+
}
|
|
168
|
+
|
|
160
169
|
async function showVersion() {
|
|
161
170
|
let version = "1.0.0";
|
|
162
171
|
try {
|
package/commands/integration.js
CHANGED
|
@@ -114,16 +114,6 @@ async function readSchemaFiles(currentDir) {
|
|
|
114
114
|
// Read documentation files
|
|
115
115
|
const authDocPath = path.join(currentDir, "Authentication.mdx");
|
|
116
116
|
const generalDocPath = path.join(currentDir, "Documentation.mdx");
|
|
117
|
-
const docsIntegrationPath = path.join(
|
|
118
|
-
currentDir,
|
|
119
|
-
"documentation",
|
|
120
|
-
"integration.mdx"
|
|
121
|
-
);
|
|
122
|
-
const docsTriggerPath = path.join(
|
|
123
|
-
currentDir,
|
|
124
|
-
"documentation",
|
|
125
|
-
"trigger.mdx"
|
|
126
|
-
);
|
|
127
117
|
|
|
128
118
|
if (fs.existsSync(authDocPath)) {
|
|
129
119
|
schemas.authentication_documentation = fs.readFileSync(
|
|
@@ -132,22 +122,8 @@ async function readSchemaFiles(currentDir) {
|
|
|
132
122
|
);
|
|
133
123
|
}
|
|
134
124
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (fs.existsSync(docsIntegrationPath)) {
|
|
138
|
-
documentation.integration = fs.readFileSync(
|
|
139
|
-
docsIntegrationPath,
|
|
140
|
-
"utf8"
|
|
141
|
-
);
|
|
142
|
-
} else if (fs.existsSync(generalDocPath)) {
|
|
143
|
-
// Fallback to legacy Documentation.mdx
|
|
144
|
-
documentation.integration = fs.readFileSync(generalDocPath, "utf8");
|
|
145
|
-
}
|
|
146
|
-
if (fs.existsSync(docsTriggerPath)) {
|
|
147
|
-
documentation.trigger = fs.readFileSync(docsTriggerPath, "utf8");
|
|
148
|
-
}
|
|
149
|
-
if (Object.keys(documentation).length > 0) {
|
|
150
|
-
schemas.documentation = documentation;
|
|
125
|
+
if (fs.existsSync(generalDocPath)) {
|
|
126
|
+
schemas.documentation = fs.readFileSync(generalDocPath, "utf8");
|
|
151
127
|
}
|
|
152
128
|
|
|
153
129
|
return schemas;
|
|
@@ -1115,25 +1091,7 @@ async function handleStatus() {
|
|
|
1115
1091
|
|
|
1116
1092
|
if (integration.documentation) {
|
|
1117
1093
|
console.log(chalk.cyan("\nDocumentation:"));
|
|
1118
|
-
|
|
1119
|
-
typeof integration.documentation === "object" &&
|
|
1120
|
-
integration.documentation !== null
|
|
1121
|
-
) {
|
|
1122
|
-
if (integration.documentation.integration) {
|
|
1123
|
-
console.log(
|
|
1124
|
-
chalk.dim("integration.mdx:\n") +
|
|
1125
|
-
integration.documentation.integration
|
|
1126
|
-
);
|
|
1127
|
-
}
|
|
1128
|
-
if (integration.documentation.trigger) {
|
|
1129
|
-
console.log(
|
|
1130
|
-
chalk.dim("\ntrigger.mdx:\n") +
|
|
1131
|
-
integration.documentation.trigger
|
|
1132
|
-
);
|
|
1133
|
-
}
|
|
1134
|
-
} else {
|
|
1135
|
-
console.log(integration.documentation);
|
|
1136
|
-
}
|
|
1094
|
+
console.log(integration.documentation);
|
|
1137
1095
|
}
|
|
1138
1096
|
} catch (error) {
|
|
1139
1097
|
if (
|
package/commands/mcp.js
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import yaml from "js-yaml";
|
|
6
|
+
import { execFileSync } from "child_process";
|
|
7
|
+
|
|
8
|
+
// Types removed for CommonJS conversion
|
|
9
|
+
|
|
10
|
+
const commands = {
|
|
11
|
+
setup: {
|
|
12
|
+
description: "Setup command for various MCP Clients",
|
|
13
|
+
action: handleSetup,
|
|
14
|
+
},
|
|
15
|
+
help: {
|
|
16
|
+
description: "Show help for mcp commands",
|
|
17
|
+
action: showHelp,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Execute the MCP command
|
|
22
|
+
const execute = async (args) => {
|
|
23
|
+
const subCommand = args[0];
|
|
24
|
+
|
|
25
|
+
if (!subCommand) {
|
|
26
|
+
showHelp();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!commands[subCommand]) {
|
|
31
|
+
console.log(chalk.red("Unknown or missing mcp sub-command.\n"));
|
|
32
|
+
showHelp();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const commandObj = commands[subCommand];
|
|
37
|
+
await commandObj.action(args.slice(1));
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function showHelp() {
|
|
41
|
+
console.log(chalk.cyan("\nMCP Commands:\n"));
|
|
42
|
+
Object.entries(commands).forEach(([cmd, details]) => {
|
|
43
|
+
console.log(chalk.bold(`${cmd}`) + ` - ${details.description}`);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Handle setup subcommand: setup <url> [name] --client <client>
|
|
48
|
+
async function handleSetup(args) {
|
|
49
|
+
// Parse positional args
|
|
50
|
+
const url = args[0];
|
|
51
|
+
let name = args[1] && !args[1].startsWith("--") ? args[1] : undefined;
|
|
52
|
+
|
|
53
|
+
// Parse flags: --client, --name
|
|
54
|
+
let client = "claude";
|
|
55
|
+
for (let i = 0; i < args.length; i++) {
|
|
56
|
+
const token = args[i];
|
|
57
|
+
if (
|
|
58
|
+
token === "--client" &&
|
|
59
|
+
args[i + 1] &&
|
|
60
|
+
!args[i + 1].startsWith("--")
|
|
61
|
+
) {
|
|
62
|
+
client = args[i + 1];
|
|
63
|
+
i++;
|
|
64
|
+
} else if (token.startsWith("--client=")) {
|
|
65
|
+
client = token.split("=")[1];
|
|
66
|
+
} else if (
|
|
67
|
+
token === "--name" &&
|
|
68
|
+
args[i + 1] &&
|
|
69
|
+
!args[i + 1].startsWith("--")
|
|
70
|
+
) {
|
|
71
|
+
name = args[i + 1];
|
|
72
|
+
i++;
|
|
73
|
+
} else if (token.startsWith("--name=")) {
|
|
74
|
+
name = token.split("=")[1];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!url) {
|
|
79
|
+
console.log(
|
|
80
|
+
chalk.red(
|
|
81
|
+
"❌ URL is required. Usage: boltic mcp setup <url> [name] --client <client>"
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const newKey =
|
|
88
|
+
name || url.split("/").slice(3).join("/").replace(/\//g, "-");
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
console.log(chalk.cyan("📝 Configuration Details:"));
|
|
92
|
+
console.log(` URL: ${chalk.green(url)}`);
|
|
93
|
+
console.log(` Client: ${chalk.green(client)}`);
|
|
94
|
+
console.log(` Name: ${chalk.green(newKey)}\n`);
|
|
95
|
+
|
|
96
|
+
const mcpUrl = url;
|
|
97
|
+
const command = `composio --sse "${mcpUrl}"`;
|
|
98
|
+
|
|
99
|
+
console.log(chalk.cyan("💾 Saving configurations..."));
|
|
100
|
+
|
|
101
|
+
saveMcpConfig(url, client, newKey, mcpUrl, command);
|
|
102
|
+
|
|
103
|
+
console.log(
|
|
104
|
+
chalk.cyan(
|
|
105
|
+
`\n🚀 All done! Please restart ${client} for changes to take effect\n`
|
|
106
|
+
)
|
|
107
|
+
);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.log(chalk.red("\n❌ Error occurred while setting up MCP:"));
|
|
110
|
+
console.log(
|
|
111
|
+
chalk.red(
|
|
112
|
+
` ${error && error.message ? error.message : String(error)}`
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
console.log(
|
|
116
|
+
chalk.yellow(
|
|
117
|
+
"\nPlease try again or contact support if the issue persists.\n"
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function saveMcpConfig(url, clientType, name, mcpUrl, command) {
|
|
124
|
+
const config = {
|
|
125
|
+
command: "npx",
|
|
126
|
+
args: ["-y", "mcp-remote", mcpUrl],
|
|
127
|
+
env: {
|
|
128
|
+
npm_config_yes: "true",
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const sseConfig = {
|
|
133
|
+
url: mcpUrl,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const homeDir = os.homedir();
|
|
137
|
+
|
|
138
|
+
const platformPaths = {
|
|
139
|
+
win32: {
|
|
140
|
+
baseDir:
|
|
141
|
+
process.env.APPDATA || path.join(homeDir, "AppData", "Roaming"),
|
|
142
|
+
vscodePath: path.join("Code", "User", "globalStorage"),
|
|
143
|
+
},
|
|
144
|
+
darwin: {
|
|
145
|
+
baseDir: path.join(homeDir, "Library", "Application Support"),
|
|
146
|
+
vscodePath: path.join("Code", "User", "globalStorage"),
|
|
147
|
+
},
|
|
148
|
+
linux: {
|
|
149
|
+
baseDir:
|
|
150
|
+
process.env.XDG_CONFIG_HOME || path.join(homeDir, ".config"),
|
|
151
|
+
vscodePath: path.join("Code/User/globalStorage"),
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const platform = process.platform;
|
|
156
|
+
|
|
157
|
+
// Check if platform is supported
|
|
158
|
+
if (!platformPaths[platform]) {
|
|
159
|
+
console.log(chalk.red(`\n❌ Platform ${platform} is not supported.`));
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const { baseDir } = platformPaths[platform];
|
|
164
|
+
|
|
165
|
+
const defaultClaudePath = path.join(
|
|
166
|
+
baseDir,
|
|
167
|
+
"Claude",
|
|
168
|
+
"claude_desktop_config.json"
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// Define client paths using the platform-specific base directories
|
|
172
|
+
const clientPaths = {
|
|
173
|
+
claude: {
|
|
174
|
+
type: "file",
|
|
175
|
+
path: defaultClaudePath,
|
|
176
|
+
},
|
|
177
|
+
cline: {
|
|
178
|
+
type: "file",
|
|
179
|
+
path: path.join(
|
|
180
|
+
baseDir,
|
|
181
|
+
platformPaths[platform].vscodePath,
|
|
182
|
+
"saoudrizwan.claude-dev",
|
|
183
|
+
"settings",
|
|
184
|
+
"cline_mcp_settings.json"
|
|
185
|
+
),
|
|
186
|
+
},
|
|
187
|
+
roocode: {
|
|
188
|
+
type: "file",
|
|
189
|
+
path: path.join(
|
|
190
|
+
baseDir,
|
|
191
|
+
platformPaths[platform].vscodePath,
|
|
192
|
+
"rooveterinaryinc.roo-cline",
|
|
193
|
+
"settings",
|
|
194
|
+
"mcp_settings.json"
|
|
195
|
+
),
|
|
196
|
+
},
|
|
197
|
+
windsurf: {
|
|
198
|
+
type: "file",
|
|
199
|
+
path: path.join(homeDir, ".codeium", "windsurf", "mcp_config.json"),
|
|
200
|
+
},
|
|
201
|
+
witsy: {
|
|
202
|
+
type: "file",
|
|
203
|
+
path: path.join(baseDir, "Witsy", "settings.json"),
|
|
204
|
+
},
|
|
205
|
+
enconvo: {
|
|
206
|
+
type: "file",
|
|
207
|
+
path: path.join(homeDir, ".config", "enconvo", "mcp_config.json"),
|
|
208
|
+
},
|
|
209
|
+
cursor: {
|
|
210
|
+
type: "file",
|
|
211
|
+
path: path.join(homeDir, ".cursor", "mcp.json"),
|
|
212
|
+
},
|
|
213
|
+
vscode: {
|
|
214
|
+
type: "command",
|
|
215
|
+
command: process.platform === "win32" ? "code.cmd" : "code",
|
|
216
|
+
},
|
|
217
|
+
"vscode-insiders": {
|
|
218
|
+
type: "command",
|
|
219
|
+
command:
|
|
220
|
+
process.platform === "win32"
|
|
221
|
+
? "code-insiders.cmd"
|
|
222
|
+
: "code-insiders",
|
|
223
|
+
},
|
|
224
|
+
boltai: {
|
|
225
|
+
type: "file",
|
|
226
|
+
path: path.join(homeDir, ".boltai", "mcp.json"),
|
|
227
|
+
},
|
|
228
|
+
"amazon-bedrock": {
|
|
229
|
+
type: "file",
|
|
230
|
+
path: path.join(
|
|
231
|
+
homeDir,
|
|
232
|
+
"Amazon Bedrock Client",
|
|
233
|
+
"mcp_config.json"
|
|
234
|
+
),
|
|
235
|
+
},
|
|
236
|
+
amazonq: {
|
|
237
|
+
type: "file",
|
|
238
|
+
path: path.join(homeDir, ".aws", "amazonq", "mcp.json"),
|
|
239
|
+
},
|
|
240
|
+
librechat: {
|
|
241
|
+
type: "yaml",
|
|
242
|
+
path: path.join(homeDir, "LibreChat", "librechat.yaml"),
|
|
243
|
+
},
|
|
244
|
+
"gemini-cli": {
|
|
245
|
+
type: "file",
|
|
246
|
+
path: path.join(homeDir, ".gemini", "settings.json"),
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
if (!clientPaths[clientType]) {
|
|
250
|
+
console.log(chalk.red(`\n❌ Client ${clientType} is not supported.`));
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const clientConfig = clientPaths[clientType];
|
|
255
|
+
const newKey =
|
|
256
|
+
name || url.split("/").slice(3).join("/").replace(/\//g, "-");
|
|
257
|
+
|
|
258
|
+
if (clientConfig.type === "command") {
|
|
259
|
+
handleCommandClient(clientConfig, newKey, config);
|
|
260
|
+
} else if (clientConfig.type === "yaml") {
|
|
261
|
+
handleYamlClient(clientConfig, newKey, config);
|
|
262
|
+
} else {
|
|
263
|
+
handleFileClient(clientConfig, newKey, config, mcpUrl);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function handleCommandClient(clientConfig, serverName, config) {
|
|
268
|
+
if (!clientConfig.command) {
|
|
269
|
+
throw new Error("Command not specified for command-type client");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const args = [];
|
|
273
|
+
args.push("--add-mcp", JSON.stringify({ ...config, name: serverName }));
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
const output = execFileSync(clientConfig.command, args);
|
|
277
|
+
console.log(
|
|
278
|
+
chalk.green(
|
|
279
|
+
`✅ Configuration added via ${clientConfig.command}: ${output.toString()}`
|
|
280
|
+
)
|
|
281
|
+
);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if (error && error.code === "ENOENT") {
|
|
284
|
+
throw new Error(
|
|
285
|
+
`Command '${clientConfig.command}' not found. Make sure ${clientConfig.command} is installed and on your PATH`
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function handleYamlClient(clientConfig, serverName, config) {
|
|
293
|
+
if (!clientConfig.path) {
|
|
294
|
+
throw new Error("Path not specified for YAML client");
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const configDir = path.dirname(clientConfig.path);
|
|
298
|
+
if (!fs.existsSync(configDir)) {
|
|
299
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
let existingYaml = {};
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
if (fs.existsSync(clientConfig.path)) {
|
|
306
|
+
const originalContent = fs.readFileSync(clientConfig.path, "utf8");
|
|
307
|
+
existingYaml = yaml.load(originalContent) || {};
|
|
308
|
+
}
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.log(chalk.yellow("⚠️ Creating new YAML config file"));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Initialize mcpServers if it doesn't exist
|
|
314
|
+
if (!existingYaml.mcpServers) {
|
|
315
|
+
existingYaml.mcpServers = {};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Add the new server
|
|
319
|
+
existingYaml.mcpServers[serverName] = config;
|
|
320
|
+
|
|
321
|
+
// Write the updated YAML
|
|
322
|
+
const yamlContent = yaml.dump(existingYaml, {
|
|
323
|
+
indent: 2,
|
|
324
|
+
lineWidth: -1,
|
|
325
|
+
noRefs: true,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
fs.writeFileSync(clientConfig.path, yamlContent);
|
|
329
|
+
console.log(chalk.green(`✅ Configuration saved to: ${clientConfig.path}`));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function handleFileClient(clientConfig, serverName, config, mcpUrl) {
|
|
333
|
+
if (!clientConfig.path) {
|
|
334
|
+
throw new Error("Path not specified for file client");
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const configDir = path.dirname(clientConfig.path);
|
|
338
|
+
if (!fs.existsSync(configDir)) {
|
|
339
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
let existingConfig = { mcpServers: {} };
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
if (fs.existsSync(clientConfig.path)) {
|
|
346
|
+
existingConfig = JSON.parse(
|
|
347
|
+
fs.readFileSync(clientConfig.path, "utf8")
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.log(chalk.yellow("⚠️ Creating new config file"));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Ensure mcpServers exists
|
|
355
|
+
if (!existingConfig.mcpServers) existingConfig.mcpServers = {};
|
|
356
|
+
|
|
357
|
+
// Special handling for Cursor which uses SSE configuration
|
|
358
|
+
if (clientConfig.path?.includes(".cursor")) {
|
|
359
|
+
const sseConfig = {
|
|
360
|
+
url: mcpUrl,
|
|
361
|
+
};
|
|
362
|
+
existingConfig.mcpServers[serverName] = sseConfig;
|
|
363
|
+
} else {
|
|
364
|
+
existingConfig.mcpServers[serverName] = config;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
fs.writeFileSync(
|
|
368
|
+
clientConfig.path,
|
|
369
|
+
JSON.stringify(existingConfig, null, 2)
|
|
370
|
+
);
|
|
371
|
+
console.log(chalk.green(`✅ Configuration saved to: ${clientConfig.path}`));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export default { execute };
|
package/helper/folder.js
CHANGED
|
@@ -47,9 +47,6 @@ export const createIntegrationFolderStructure = async (
|
|
|
47
47
|
const resourcesDir = path.join(schemasDir, "resources");
|
|
48
48
|
fs.mkdirSync(resourcesDir, { recursive: true });
|
|
49
49
|
|
|
50
|
-
const docsDir = path.join(integrationDir, "documentation");
|
|
51
|
-
fs.mkdirSync(docsDir, { recursive: true });
|
|
52
|
-
|
|
53
50
|
// Create template files
|
|
54
51
|
const files = {
|
|
55
52
|
"schemas/resources/resource1.json": JSON.stringify(resource1, null, 4),
|
|
@@ -72,13 +69,7 @@ export const createIntegrationFolderStructure = async (
|
|
|
72
69
|
...(create_catalogue && {
|
|
73
70
|
"Authentication.mdx": `# ${name} Authentication`,
|
|
74
71
|
}),
|
|
75
|
-
|
|
76
|
-
...(!isEmpty(activity_type) && {
|
|
77
|
-
"documentation/integration.mdx": `# ${name} Integration`,
|
|
78
|
-
}),
|
|
79
|
-
...(trigger_type === "CloudTrigger" && {
|
|
80
|
-
"documentation/trigger.mdx": `# ${name} Trigger`,
|
|
81
|
-
}),
|
|
72
|
+
"Documentation.mdx": `# ${name} Documentation`,
|
|
82
73
|
};
|
|
83
74
|
|
|
84
75
|
// Create all files
|
|
@@ -156,14 +147,7 @@ export const createExistingIntegrationsFolder = async (payload) => {
|
|
|
156
147
|
const resourcesDir = path.join(schemasDir, "resources");
|
|
157
148
|
fs.mkdirSync(resourcesDir, { recursive: true });
|
|
158
149
|
|
|
159
|
-
const docsDir = path.join(integrationDir, "documentation");
|
|
160
|
-
fs.mkdirSync(docsDir, { recursive: true });
|
|
161
|
-
|
|
162
150
|
const authentication_documentation = authentication?.documentation || "";
|
|
163
|
-
const isDocumentationObject =
|
|
164
|
-
documentation &&
|
|
165
|
-
typeof documentation === "object" &&
|
|
166
|
-
!Array.isArray(documentation);
|
|
167
151
|
|
|
168
152
|
// Define files and content
|
|
169
153
|
const files = {
|
|
@@ -184,17 +168,7 @@ export const createExistingIntegrationsFolder = async (payload) => {
|
|
|
184
168
|
...(authentication && {
|
|
185
169
|
"Authentication.mdx": authentication_documentation || "",
|
|
186
170
|
}),
|
|
187
|
-
|
|
188
|
-
"documentation/integration.mdx": isDocumentationObject
|
|
189
|
-
? documentation?.integration || `# ${name} Integration`
|
|
190
|
-
: (typeof documentation === "string" && documentation) ||
|
|
191
|
-
`# ${name} Integration`,
|
|
192
|
-
}),
|
|
193
|
-
...(trigger_type === "CloudTrigger" && {
|
|
194
|
-
"documentation/trigger.mdx": isDocumentationObject
|
|
195
|
-
? documentation?.trigger || `# ${name} Trigger`
|
|
196
|
-
: `# ${name} Trigger`,
|
|
197
|
-
}),
|
|
171
|
+
"Documentation.mdx": documentation || "",
|
|
198
172
|
};
|
|
199
173
|
|
|
200
174
|
// Write resource files
|
package/helper/validation.js
CHANGED
|
@@ -2,7 +2,6 @@ import fs from "fs";
|
|
|
2
2
|
import isEmpty from "lodash.isempty";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import * as componentSchemas from "../templates/component-schemas.js";
|
|
5
|
-
import { validateMarkdownFile } from "./markdown.js";
|
|
6
5
|
|
|
7
6
|
const validateOptionObject = (options, fieldName, fileLabel, errors) => {
|
|
8
7
|
options.forEach((opt, index) => {
|
|
@@ -326,82 +325,9 @@ const validateComponentSchemas = (schemas, errors, filename = "") => {
|
|
|
326
325
|
// INDIVIDUAL VALIDATORS
|
|
327
326
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
328
327
|
|
|
329
|
-
const validateDocumentation = (
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
"documentation",
|
|
333
|
-
"integration.mdx"
|
|
334
|
-
);
|
|
335
|
-
const docsTriggerPath = path.join(
|
|
336
|
-
currentDir,
|
|
337
|
-
"documentation",
|
|
338
|
-
"trigger.mdx"
|
|
339
|
-
);
|
|
340
|
-
const legacyDocPath = path.join(currentDir, "Documentation.mdx");
|
|
341
|
-
|
|
342
|
-
const requiresIntegrationDoc = spec && !isEmpty(spec.activity_type);
|
|
343
|
-
const requiresTriggerDoc = spec && spec.trigger_type === "CloudTrigger";
|
|
344
|
-
|
|
345
|
-
// Backward-compatibility: if nothing specific is required by spec,
|
|
346
|
-
// ensure at least legacy Documentation.mdx or new docs exist
|
|
347
|
-
const hasLegacy = fs.existsSync(legacyDocPath);
|
|
348
|
-
const hasNewIntegration = fs.existsSync(docsIntegrationPath);
|
|
349
|
-
const hasNewTrigger = fs.existsSync(docsTriggerPath);
|
|
350
|
-
|
|
351
|
-
if (!requiresIntegrationDoc && !requiresTriggerDoc) {
|
|
352
|
-
if (!hasLegacy && !hasNewIntegration && !hasNewTrigger) {
|
|
353
|
-
errors.add('"Documentation.mdx" not found in the root directory.');
|
|
354
|
-
return; // No further checks needed
|
|
355
|
-
}
|
|
356
|
-
// If files exist, validate whatever is present
|
|
357
|
-
if (hasLegacy) {
|
|
358
|
-
validateMarkdownFile(legacyDocPath, errors, "Documentation.mdx");
|
|
359
|
-
}
|
|
360
|
-
if (hasNewIntegration) {
|
|
361
|
-
validateMarkdownFile(
|
|
362
|
-
docsIntegrationPath,
|
|
363
|
-
errors,
|
|
364
|
-
"documentation/integration.mdx"
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
if (hasNewTrigger) {
|
|
368
|
-
validateMarkdownFile(
|
|
369
|
-
docsTriggerPath,
|
|
370
|
-
errors,
|
|
371
|
-
"documentation/trigger.mdx"
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (requiresIntegrationDoc) {
|
|
378
|
-
const hasNew = validateMarkdownFile(
|
|
379
|
-
docsIntegrationPath,
|
|
380
|
-
errors,
|
|
381
|
-
"documentation/integration.mdx"
|
|
382
|
-
);
|
|
383
|
-
const hasLegacyValidated = validateMarkdownFile(
|
|
384
|
-
legacyDocPath,
|
|
385
|
-
errors,
|
|
386
|
-
"Documentation.mdx"
|
|
387
|
-
);
|
|
388
|
-
if (!hasNew && !hasLegacyValidated) {
|
|
389
|
-
// Maintain backward-compatible error message expected by tests
|
|
390
|
-
errors.add('"Documentation.mdx" not found in the root directory.');
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
if (requiresTriggerDoc) {
|
|
395
|
-
const hasTrigger = validateMarkdownFile(
|
|
396
|
-
docsTriggerPath,
|
|
397
|
-
errors,
|
|
398
|
-
"documentation/trigger.mdx"
|
|
399
|
-
);
|
|
400
|
-
if (!hasTrigger) {
|
|
401
|
-
errors.add(
|
|
402
|
-
'"documentation/trigger.mdx" not found when trigger_type is "CloudTrigger".'
|
|
403
|
-
);
|
|
404
|
-
}
|
|
328
|
+
const validateDocumentation = (docPath, errors) => {
|
|
329
|
+
if (!fs.existsSync(docPath)) {
|
|
330
|
+
errors.add(`"Documentation.mdx" not found in the root directory.`);
|
|
405
331
|
}
|
|
406
332
|
};
|
|
407
333
|
|
|
@@ -599,11 +525,13 @@ export const validateIntegrationSchemas = (currentDir) => {
|
|
|
599
525
|
spec: path.join(currentDir, "spec.json"),
|
|
600
526
|
webhook: path.join(currentDir, "schemas", "webhook.json"),
|
|
601
527
|
authentication: path.join(currentDir, "schemas", "authentication.json"),
|
|
528
|
+
documentation: path.join(currentDir, "Documentation.mdx"),
|
|
602
529
|
};
|
|
603
530
|
|
|
604
531
|
// Step-by-step validation
|
|
532
|
+
validateDocumentation(paths.documentation, errors);
|
|
533
|
+
|
|
605
534
|
const spec = validateSpec(paths.spec, errors);
|
|
606
|
-
validateDocumentation(currentDir, spec, errors);
|
|
607
535
|
validateWebhook(paths.webhook, spec, errors);
|
|
608
536
|
validateAuthentication(paths.authentication, errors);
|
|
609
537
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boltic/cli",
|
|
3
|
-
"version": "1.0.27
|
|
3
|
+
"version": "1.0.27",
|
|
4
4
|
"description": "A powerful CLI tool for managing Boltic Workflow integrations - create, sync, test, and publish integrations with ease",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -107,6 +107,7 @@
|
|
|
107
107
|
"@inquirer/prompts": "^7.8.0",
|
|
108
108
|
"axios": "^1.11.0",
|
|
109
109
|
"chalk": "^5.5.0",
|
|
110
|
+
"js-yaml": "^4.1.0",
|
|
110
111
|
"keytar": "^7.9.0",
|
|
111
112
|
"lodash.isempty": "^4.4.0",
|
|
112
113
|
"open": "^10.2.0",
|
package/helper/markdown.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
|
|
3
|
-
const hasBalancedCodeFences = (content) => {
|
|
4
|
-
const fenceCount = (content.match(/```/g) || []).length;
|
|
5
|
-
return fenceCount % 2 === 0;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export const validateMarkdownString = (content, fileLabel, errors) => {
|
|
9
|
-
const label = fileLabel || "markdown";
|
|
10
|
-
if (!content || !content.trim()) {
|
|
11
|
-
errors.add(`"${label}" is empty.`);
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
// Keep checks minimal to avoid false positives in CLI usage
|
|
15
|
-
if (!hasBalancedCodeFences(content)) {
|
|
16
|
-
errors.add('"' + label + '" has unbalanced code fences (```).');
|
|
17
|
-
}
|
|
18
|
-
return true;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const validateMarkdownFile = (filePath, errors, label) => {
|
|
22
|
-
try {
|
|
23
|
-
if (!fs.existsSync(filePath)) return false;
|
|
24
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
25
|
-
return validateMarkdownString(content, label || filePath, errors);
|
|
26
|
-
} catch (e) {
|
|
27
|
-
errors.add(
|
|
28
|
-
`Failed to read markdown file "${label || filePath}": ${e.message}`
|
|
29
|
-
);
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
};
|