@boltic/cli 1.0.38 → 1.0.40

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # ⚡ Boltic CLI
2
2
 
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.**
3
+ > **Professional CLI for interacting with the Boltic platform — create, manage, and publish integrations, serverless functions, workflows, MCPs, and more with enterprise-grade features and a seamless developer experience.**
4
4
 
5
5
  [![NPM Version](https://img.shields.io/npm/v/@boltic/cli)](https://www.npmjs.com/package/@boltic/cli)
6
6
  [![GitHub Repo](https://img.shields.io/badge/GitHub-Repo-blue?logo=github)](https://github.com/bolticio/cli)
@@ -29,6 +29,7 @@
29
29
  - [🔐 Authentication](#-authentication)
30
30
  - [🧩 Integration Management](#-integration-management)
31
31
  - [🧠 MCP](#-mcp-model-context-protocol)
32
+ - [⚡ Serverless Functions](#-serverless-functions)
32
33
  - [📚 Command Reference](#-command-reference)
33
34
  - [🛠️ Development Workflow](#️-development-workflow)
34
35
  - [🔧 Configuration](#-configuration)
@@ -44,6 +45,7 @@
44
45
 
45
46
  - 🔐 **Secure Authentication** - Enterprise-grade token management with secure storage
46
47
  - 🚀 **Rapid Development** - Create workflows, integrations, and more in minutes, not hours
48
+ - ⚡ **Serverless Functions** - Deploy functions in Node.js, Python, Golang, or Java with local testing
47
49
  - 📦 **Smart Project Management** - Automated folder structure and configuration
48
50
  - 🔄 **Real-time Synchronization** - Instant sync with Boltic Cloud platform
49
51
  - 🎯 **Type-safe Development** - Support for Workflow Activities and Triggers
@@ -69,11 +71,14 @@ boltic login
69
71
  # Create your first integration
70
72
  boltic integration create
71
73
 
72
- # Sync your changes
73
- boltic integration sync
74
+ # Or create a serverless function
75
+ boltic serverless create --type blueprint --name my-api --language nodejs
74
76
 
75
- # Submit for review
76
- boltic integration submit
77
+ # Test locally
78
+ boltic serverless test
79
+
80
+ # Deploy to Boltic Cloud
81
+ boltic serverless publish
77
82
  ```
78
83
 
79
84
  ---
@@ -299,6 +304,107 @@ boltic mcp setup https://mcp.boltic.io/sse my-boltic --client claude
299
304
 
300
305
  ---
301
306
 
307
+ ## ⚡ Serverless Functions
308
+
309
+ Create, test, and deploy serverless functions on the Boltic platform with support for multiple languages and deployment types.
310
+
311
+ ### Supported Languages
312
+
313
+ | Language | Version | Handler |
314
+ |----------|---------|---------|
315
+ | Node.js | 20 | `handler.handler` |
316
+ | Python | 3 | `index.handler` |
317
+ | Golang | 1.22 | `Handler` |
318
+ | Java | 17 | `Handler` |
319
+
320
+ ### Deployment Types
321
+
322
+ | Type | Description |
323
+ |------|-------------|
324
+ | **Blueprint** | Write code directly in the CLI-generated project |
325
+ | **Git** | Deploy from a Git repository |
326
+ | **Container** | Deploy a Docker container |
327
+
328
+ ### Creating Serverless Functions
329
+
330
+ ```bash
331
+ # Interactive mode (prompts for all options)
332
+ boltic serverless create
333
+
334
+ # Create a blueprint serverless function
335
+ boltic serverless create --type blueprint --name my-api --language nodejs
336
+
337
+ # Create a git-based serverless function
338
+ boltic serverless create --type git --name my-git-func --language python
339
+
340
+ # Create a container-based serverless function
341
+ boltic serverless create --type container --name my-container
342
+
343
+ # Specify custom directory
344
+ boltic serverless create --type blueprint --name my-function --language python --directory ./projects
345
+ ```
346
+
347
+ #### Generated Project Structure
348
+
349
+ ```
350
+ my-serverless/
351
+ ├── boltic.yaml # Configuration file with serverless settings
352
+ ├── handler.js # Handler file (Node.js) or index.py (Python), etc.
353
+ └── package.json # Dependencies (for Node.js projects)
354
+ ```
355
+
356
+ ### Testing Locally
357
+
358
+ ```bash
359
+ # Auto-detect language and run on default port (8080)
360
+ boltic serverless test
361
+
362
+ # Specify custom port
363
+ boltic serverless test --port 3000
364
+
365
+ # Test from specific directory
366
+ boltic serverless test --directory ./my-function
367
+ ```
368
+
369
+ ### Publishing
370
+
371
+ ```bash
372
+ # Publish from current directory
373
+ boltic serverless publish
374
+
375
+ # Publish from specific directory
376
+ boltic serverless publish --directory ./my-function
377
+ ```
378
+
379
+ ### Pulling Existing Functions
380
+
381
+ ```bash
382
+ # Pull a serverless function (interactive selection)
383
+ boltic serverless pull
384
+
385
+ # Pull to a specific path
386
+ boltic serverless pull --path ./projects
387
+ ```
388
+
389
+ ### Listing Functions
390
+
391
+ ```bash
392
+ # List all serverless functions
393
+ boltic serverless list
394
+ ```
395
+
396
+ ### Checking Status
397
+
398
+ ```bash
399
+ # Check status (interactive selection)
400
+ boltic serverless status
401
+
402
+ # Check status by name
403
+ boltic serverless status --name my-function
404
+ ```
405
+
406
+ ---
407
+
302
408
  ## 📚 Command Reference
303
409
 
304
410
  ### Core Commands
@@ -329,6 +435,18 @@ boltic mcp setup https://mcp.boltic.io/sse my-boltic --client claude
329
435
  | `boltic mcp help` | Show help for MCP sub-commands | |
330
436
  | `boltic mcp setup` | Configure an MCP server for a specific client| `--client <name>` `--name <alias>`|
331
437
 
438
+ ### Serverless Commands
439
+
440
+ | Command | Description | Options |
441
+ | ---------------------------- | ------------------------------------ | ---------------------------------------------------- |
442
+ | `boltic serverless create` | Create a new serverless function | `--type, -t` `--name, -n` `--language, -l` `--directory, -d` |
443
+ | `boltic serverless test` | Test serverless function locally | `--port, -p` `--language, -l` `--directory, -d` |
444
+ | `boltic serverless publish` | Publish/deploy serverless function | `--directory, -d` |
445
+ | `boltic serverless pull` | Pull existing serverless function | `--path` |
446
+ | `boltic serverless list` | List all serverless functions | |
447
+ | `boltic serverless status` | Show status of a serverless function | `--name, -n` |
448
+ | `boltic serverless help` | Show help for serverless commands | |
449
+
332
450
  ### Help and Documentation
333
451
 
334
452
  ```bash
@@ -0,0 +1,174 @@
1
+ import axios from "axios";
2
+ import FormData from "form-data";
3
+ import fs from "fs";
4
+ import https from "https";
5
+ import { handleError } from "../helper/error.js";
6
+ import { logApi } from "../helper/verbose.js";
7
+
8
+ const getHttpsAgentForUrl = (baseUrl) => {
9
+ try {
10
+ const host = new URL(baseUrl).hostname;
11
+ if (
12
+ host.endsWith("fcz0.de") ||
13
+ host.endsWith("uat.fcz0.de") ||
14
+ host.endsWith("fyndx1.de") ||
15
+ process.env.BOLTCI_INSECURE_TLS === "true"
16
+ ) {
17
+ return new https.Agent({ rejectUnauthorized: false });
18
+ }
19
+ } catch (_) {
20
+ // ignore URL parse errors and fall back to default agent
21
+ }
22
+ return undefined;
23
+ };
24
+
25
+ const listAllServerless = async (
26
+ apiUrl,
27
+ token,
28
+ accountId,
29
+ session,
30
+ query = null
31
+ ) => {
32
+ if (!token || !session || !accountId) {
33
+ console.error(
34
+ "\x1b[31mError:\x1b[0m Authentication credentials are required."
35
+ );
36
+ console.log("\n🔹 Please log in first using:");
37
+ console.log("\x1b[32m$ boltic login\x1b[0m\n");
38
+ process.exit(1); // Exit the CLI with an error code
39
+ }
40
+ try {
41
+ const params = {
42
+ page: 1,
43
+ limit: 999,
44
+ sortBy: "CreatedAt",
45
+ sortOrder: "desc",
46
+ };
47
+
48
+ // Add query parameter if provided
49
+ if (query) {
50
+ params.q = query;
51
+ }
52
+
53
+ const axiosOptions = {
54
+ method: "get",
55
+ url: `${apiUrl}/service/panel/serverless/v1.0/apps`,
56
+ params,
57
+ headers: {
58
+ "Content-Type": "application/json",
59
+ Authorization: `Bearer ${token}`,
60
+ Cookie: session,
61
+ },
62
+ httpsAgent: getHttpsAgentForUrl(apiUrl),
63
+ };
64
+
65
+ const response = await axios(axiosOptions);
66
+ logApi(axiosOptions.method, axiosOptions.url, response.status);
67
+ return response.data.data;
68
+ } catch (error) {
69
+ handleError(error);
70
+ }
71
+ };
72
+
73
+ const pullServerless = async (apiUrl, token, accountId, session, id) => {
74
+ if (!token || !session || !accountId) {
75
+ console.error(
76
+ "\x1b[31mError:\x1b[0m Authentication credentials are required."
77
+ );
78
+ console.log("\n🔹 Please log in first using:");
79
+ console.log("\x1b[32m$ boltic login\x1b[0m\n");
80
+ process.exit(1); // Exit the CLI with an error code
81
+ }
82
+ try {
83
+ const response = await axios({
84
+ method: "get",
85
+ url: `${apiUrl}/service/panel/serverless/v1.0/apps/${id}`,
86
+ headers: {
87
+ "Content-Type": "application/json",
88
+ Authorization: `Bearer ${token}`,
89
+ Cookie: session,
90
+ },
91
+ httpsAgent: getHttpsAgentForUrl(apiUrl),
92
+ });
93
+ return response?.data;
94
+ } catch (error) {
95
+ handleError(error);
96
+ }
97
+ };
98
+
99
+ const publishServerless = async (apiUrl, token, session, payload) => {
100
+ if (!token || !session) {
101
+ console.error(
102
+ "\x1b[31mError:\x1b[0m Authentication credentials are required."
103
+ );
104
+ console.log("\n🔹 Please log in first using:");
105
+ console.log("\x1b[32m$ boltic login\x1b[0m\n");
106
+ process.exit(1);
107
+ }
108
+
109
+ try {
110
+ const axiosOptions = {
111
+ method: "post",
112
+ url: `https://asia-south1.api.boltic.io/service/panel/serverless/v1.0/apps`,
113
+ headers: {
114
+ "Content-Type": "application/json",
115
+ Authorization: `Bearer ${token}`,
116
+ Cookie: session,
117
+ },
118
+ data: payload,
119
+ httpsAgent: getHttpsAgentForUrl(apiUrl),
120
+ };
121
+
122
+ const response = await axios(axiosOptions);
123
+ logApi(axiosOptions.method, axiosOptions.url, response.status);
124
+ return response.data;
125
+ } catch (error) {
126
+ handleError(error);
127
+ return null;
128
+ }
129
+ };
130
+
131
+ const updateServerless = async (
132
+ apiUrl,
133
+ token,
134
+ session,
135
+ serverlessId,
136
+ payload
137
+ ) => {
138
+ if (!token || !session) {
139
+ console.error(
140
+ "\x1b[31mError:\x1b[0m Authentication credentials are required."
141
+ );
142
+ console.log("\n🔹 Please log in first using:");
143
+ console.log("\x1b[32m$ boltic login\x1b[0m\n");
144
+ process.exit(1);
145
+ }
146
+
147
+ try {
148
+ const axiosOptions = {
149
+ method: "put",
150
+ url: `https://asia-south1.api.boltic.io/service/panel/serverless/v1.0/apps/${serverlessId}`,
151
+ headers: {
152
+ "Content-Type": "application/json",
153
+ Authorization: `Bearer ${token}`,
154
+ Cookie: session,
155
+ },
156
+ data: payload,
157
+ httpsAgent: getHttpsAgentForUrl(apiUrl),
158
+ };
159
+
160
+ const response = await axios(axiosOptions);
161
+ logApi(axiosOptions.method, axiosOptions.url, response.status);
162
+ return response.data;
163
+ } catch (error) {
164
+ handleError(error);
165
+ return null;
166
+ }
167
+ };
168
+
169
+ export {
170
+ listAllServerless,
171
+ pullServerless,
172
+ publishServerless,
173
+ updateServerless,
174
+ };
package/cli.js CHANGED
@@ -6,6 +6,7 @@ import EnvironmentCommands from "./commands/env.js";
6
6
  import IntegrationCommands from "./commands/integration.js";
7
7
  import AuthCommands from "./commands/login.js";
8
8
  import McpCommands from "./commands/mcp.js";
9
+ import ServerlessCommands from "./commands/serverless.js";
9
10
 
10
11
  // Create a CLI module with functional approach
11
12
  import { findSimilarCommands } from "./helper/command-suggestions.js";
@@ -17,9 +18,69 @@ const createCLI = (consoleUrl, apiUrl, serviceName, env) => {
17
18
  login: {
18
19
  description: "Authenticate the user and save access token",
19
20
  action: async (args) => {
20
- // Support PAT-based login via flag: `boltic login --pat`
21
- if (args.includes("--pat")) {
22
- await AuthCommands.handlePatLogin();
21
+ // Support PAT-based login via flags, e.g.:
22
+ // boltic login --pat XXXXX --account_id YYYYYY
23
+ // boltic login --pat=XXXXX --account-id=YYYYYY
24
+ let patFromArg;
25
+ let accountIdFromArg;
26
+ let hasPatFlag = false;
27
+ let hasAccountIdFlag = false;
28
+
29
+ for (let i = 0; i < args.length; i++) {
30
+ const arg = args[i];
31
+
32
+ if (arg === "--pat") {
33
+ hasPatFlag = true;
34
+ if (
35
+ i + 1 < args.length &&
36
+ !args[i + 1].startsWith("--")
37
+ ) {
38
+ patFromArg = args[i + 1];
39
+ i++;
40
+ }
41
+ continue;
42
+ }
43
+
44
+ if (arg === "--account_id" || arg === "--account-id") {
45
+ hasAccountIdFlag = true;
46
+ if (
47
+ i + 1 < args.length &&
48
+ !args[i + 1].startsWith("--")
49
+ ) {
50
+ accountIdFromArg = args[i + 1];
51
+ i++;
52
+ }
53
+ continue;
54
+ }
55
+
56
+ if (arg.startsWith("--pat=")) {
57
+ hasPatFlag = true;
58
+ patFromArg = arg.split("=")[1];
59
+ continue;
60
+ }
61
+
62
+ if (
63
+ arg.startsWith("--account_id=") ||
64
+ arg.startsWith("--account-id=")
65
+ ) {
66
+ hasAccountIdFlag = true;
67
+ accountIdFromArg = arg.split("=")[1];
68
+ continue;
69
+ }
70
+ }
71
+
72
+ // If any PAT-related flag is present, delegate to PAT login handler.
73
+ // `handlePatLogin` will decide whether to prompt based on which values are provided.
74
+ if (
75
+ hasPatFlag ||
76
+ hasAccountIdFlag ||
77
+ patFromArg ||
78
+ accountIdFromArg
79
+ ) {
80
+ await AuthCommands.handlePatLogin(
81
+ patFromArg,
82
+ accountIdFromArg
83
+ );
23
84
  return;
24
85
  }
25
86
 
@@ -50,6 +111,10 @@ const createCLI = (consoleUrl, apiUrl, serviceName, env) => {
50
111
  description: "Display the version of the CLI.",
51
112
  action: () => showVersion(),
52
113
  },
114
+ serverless: {
115
+ description: "Manage serverless (create, list, test)",
116
+ action: (args) => handleServerless(args),
117
+ },
53
118
  };
54
119
 
55
120
  return {
@@ -175,6 +240,9 @@ async function handleMcp(args) {
175
240
  await McpCommands.execute(args);
176
241
  }
177
242
 
243
+ async function handleServerless(args) {
244
+ await ServerlessCommands.execute(args);
245
+ }
178
246
  async function showVersion() {
179
247
  let version = "1.0.0";
180
248
  try {
package/commands/login.js CHANGED
@@ -29,6 +29,55 @@ const execute = async (args) => {
29
29
  return;
30
30
  }
31
31
 
32
+ // Special handling for `boltic login` to support PAT-based login via flags:
33
+ // boltic login --pat XXXXX --account_id YYYYYY
34
+ if (subCommand === "login") {
35
+ const options = args.slice(1);
36
+ let patFromArg;
37
+ let accountIdFromArg;
38
+
39
+ for (let i = 0; i < options.length; i++) {
40
+ const arg = options[i];
41
+
42
+ if (arg === "--pat" && i + 1 < options.length) {
43
+ patFromArg = options[i + 1];
44
+ i++;
45
+ continue;
46
+ }
47
+
48
+ if (
49
+ (arg === "--account_id" || arg === "--account-id") &&
50
+ i + 1 < options.length
51
+ ) {
52
+ accountIdFromArg = options[i + 1];
53
+ i++;
54
+ continue;
55
+ }
56
+
57
+ if (arg.startsWith("--pat=")) {
58
+ patFromArg = arg.split("=")[1];
59
+ continue;
60
+ }
61
+
62
+ if (
63
+ arg.startsWith("--account_id=") ||
64
+ arg.startsWith("--account-id=")
65
+ ) {
66
+ accountIdFromArg = arg.split("=")[1];
67
+ continue;
68
+ }
69
+ }
70
+
71
+ // If PAT flags are provided, use PAT-based login. Otherwise, fall back to browser-based login.
72
+ if (patFromArg || accountIdFromArg) {
73
+ await handlePatLogin(patFromArg, accountIdFromArg);
74
+ return;
75
+ }
76
+
77
+ await handleLogin();
78
+ return;
79
+ }
80
+
32
81
  await commands[subCommand].action(args.slice(1));
33
82
  };
34
83
 
@@ -179,31 +228,51 @@ async function handleLogin() {
179
228
 
180
229
  // Handle PAT-based login command
181
230
  async function handlePatLogin(patFromArg, accountIdFromArg) {
182
- let pat = patFromArg;
183
- let accountId = accountIdFromArg;
231
+ let pat = patFromArg && patFromArg.trim();
232
+ let accountId = accountIdFromArg && accountIdFromArg.trim();
233
+
234
+ // If both values are provided via CLI flags, do not prompt at all.
235
+ if (pat && accountId) {
236
+ try {
237
+ await storeSecret("pat", pat);
238
+ await storeSecret("account_id", accountId);
239
+ console.log(
240
+ chalk.green(
241
+ "\n✅ PAT token and Account ID stored securely. They will be used for future organization-related requests.\n"
242
+ )
243
+ );
244
+ } catch (error) {
245
+ console.error(
246
+ chalk.red(
247
+ `\n❌ Failed to store PAT credentials: ${error.message || error}\n`
248
+ )
249
+ );
250
+ }
251
+ return;
252
+ }
184
253
 
185
254
  if (!pat) {
186
255
  console.log(chalk.cyan("\n🔐 Personal Access Token (PAT) login\n"));
187
- pat = await askQuestion("Enter your PAT token: ");
256
+ pat = (await askQuestion("Enter your PAT token: ")).trim();
188
257
  }
189
258
 
190
- if (!pat || !pat.trim()) {
259
+ if (!pat) {
191
260
  console.log(chalk.red("\n❌ PAT token cannot be empty.\n"));
192
261
  return;
193
262
  }
194
263
 
195
264
  if (!accountId) {
196
- accountId = await askQuestion("Enter your Account ID: ");
265
+ accountId = (await askQuestion("Enter your Account ID: ")).trim();
197
266
  }
198
267
 
199
- if (!accountId || !accountId.trim()) {
268
+ if (!accountId) {
200
269
  console.log(chalk.red("\n❌ Account ID cannot be empty.\n"));
201
270
  return;
202
271
  }
203
272
 
204
273
  try {
205
- await storeSecret("pat", pat.trim());
206
- await storeSecret("account_id", accountId.trim());
274
+ await storeSecret("pat", pat);
275
+ await storeSecret("account_id", accountId);
207
276
  console.log(
208
277
  chalk.green(
209
278
  "\n✅ PAT token and Account ID stored securely. They will be used for future organization-related requests.\n"