@protoboxai/cli 1.0.0

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.
@@ -0,0 +1,209 @@
1
+ import Conf from "conf";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { existsSync, mkdirSync } from "node:fs";
5
+ const DEFAULT_CONFIG = {
6
+ baseUrl: "https://platform.protobox.ai",
7
+ defaultFormat: "table"
8
+ };
9
+ function getConfigDir() {
10
+ return join(homedir(), ".protobox");
11
+ }
12
+ function ensureConfigDir() {
13
+ const configDir = getConfigDir();
14
+ if (!existsSync(configDir)) {
15
+ mkdirSync(configDir, { recursive: true });
16
+ }
17
+ }
18
+ class ConfigStore {
19
+ store;
20
+ constructor() {
21
+ ensureConfigDir();
22
+ this.store = new Conf({
23
+ projectName: "protobox",
24
+ cwd: getConfigDir(),
25
+ configName: "config",
26
+ defaults: DEFAULT_CONFIG,
27
+ schema: {
28
+ apiKey: { type: "string" },
29
+ jwtToken: { type: "string" },
30
+ refreshToken: { type: "string" },
31
+ baseUrl: { type: "string", default: DEFAULT_CONFIG.baseUrl },
32
+ server: { type: "string" },
33
+ deployment: { type: "string" },
34
+ apiPrefix: { type: "string" },
35
+ workspaceId: { type: "string" },
36
+ appUrl: { type: "string" },
37
+ defaultFormat: {
38
+ type: "string",
39
+ enum: ["table", "json"],
40
+ default: "table"
41
+ }
42
+ }
43
+ });
44
+ }
45
+ /**
46
+ * Get a configuration value
47
+ * Supports dot notation for nested values
48
+ */
49
+ get(key) {
50
+ return this.store.get(key);
51
+ }
52
+ /**
53
+ * Set a configuration value
54
+ */
55
+ set(key, value) {
56
+ this.store.set(key, value);
57
+ }
58
+ /**
59
+ * Delete a configuration value
60
+ */
61
+ delete(key) {
62
+ this.store.delete(key);
63
+ }
64
+ /**
65
+ * Get all configuration values
66
+ */
67
+ getAll() {
68
+ return this.store.store;
69
+ }
70
+ /**
71
+ * Check if a key exists in the configuration
72
+ */
73
+ has(key) {
74
+ return this.store.has(key);
75
+ }
76
+ /**
77
+ * Clear all configuration
78
+ */
79
+ clear() {
80
+ this.store.clear();
81
+ }
82
+ /**
83
+ * Get the path to the config file
84
+ */
85
+ getPath() {
86
+ return this.store.path;
87
+ }
88
+ /**
89
+ * Get API key with environment variable fallback
90
+ * Priority: CHANL_API_KEY env var > config file
91
+ */
92
+ getApiKey() {
93
+ return process.env["PROTOBOX_API_KEY"] || process.env["CHANL_API_KEY"] || this.get("apiKey");
94
+ }
95
+ /**
96
+ * Get JWT token from config
97
+ */
98
+ getJwtToken() {
99
+ return this.get("jwtToken");
100
+ }
101
+ /**
102
+ * Get refresh token from config
103
+ */
104
+ getRefreshToken() {
105
+ return this.get("refreshToken");
106
+ }
107
+ /**
108
+ * Get base URL with environment variable fallback
109
+ * Priority: CHANL_BASE_URL env var > config file > default
110
+ */
111
+ getBaseUrl() {
112
+ return process.env["PROTOBOX_BASE_URL"] || process.env["CHANL_BASE_URL"] || this.get("server") || this.get("baseUrl");
113
+ }
114
+ getDeployment() {
115
+ const d = process.env["CHANL_DEPLOYMENT"] || this.get("deployment");
116
+ if (d === "local" || d === "cloud") return d;
117
+ const base = this.getBaseUrl();
118
+ if (base.includes("localhost:18005") || base.includes("127.0.0.1:18005")) {
119
+ return "local";
120
+ }
121
+ return void 0;
122
+ }
123
+ getApiPrefix() {
124
+ const fromEnv = process.env["CHANL_API_PREFIX"];
125
+ if (fromEnv !== void 0) return fromEnv;
126
+ return this.get("apiPrefix");
127
+ }
128
+ /**
129
+ * Get the app URL for browser-based flows (e.g., CLI auth)
130
+ * Priority: CHANL_APP_URL env var > config file > derived from baseUrl
131
+ */
132
+ getAppUrl() {
133
+ if (process.env["PROTOBOX_APP_URL"]) return process.env["PROTOBOX_APP_URL"];
134
+ if (process.env["CHANL_APP_URL"]) return process.env["CHANL_APP_URL"];
135
+ const stored = this.get("appUrl");
136
+ if (stored) return stored;
137
+ const baseUrl = this.getBaseUrl();
138
+ if (baseUrl.includes("localhost")) return "http://localhost:3014";
139
+ return "https://app.protobox.ai";
140
+ }
141
+ /**
142
+ * Get workspace ID with environment variable fallback
143
+ * Priority: CHANL_WORKSPACE_ID env var > config file
144
+ */
145
+ getWorkspaceId() {
146
+ return process.env["PROTOBOX_WORKSPACE_ID"] || process.env["CHANL_WORKSPACE_ID"] || this.get("workspaceId");
147
+ }
148
+ /**
149
+ * Check if user is authenticated (has API key or JWT token)
150
+ */
151
+ isAuthenticated() {
152
+ return !!(this.getApiKey() || this.getJwtToken());
153
+ }
154
+ /**
155
+ * Get the auth method in use
156
+ */
157
+ getAuthMethod() {
158
+ if (this.getApiKey()) return "api-key";
159
+ if (this.getJwtToken()) return "jwt";
160
+ return null;
161
+ }
162
+ /**
163
+ * Set API key in config
164
+ */
165
+ setApiKey(apiKey) {
166
+ this.set("apiKey", apiKey);
167
+ }
168
+ /**
169
+ * Set JWT tokens in config (from browser login)
170
+ */
171
+ setJwtAuth(jwtToken, refreshToken) {
172
+ this.set("jwtToken", jwtToken);
173
+ this.set("refreshToken", refreshToken);
174
+ this.delete("apiKey");
175
+ }
176
+ /**
177
+ * Remove all auth credentials from config (logout)
178
+ */
179
+ clearAuth() {
180
+ this.delete("apiKey");
181
+ this.delete("jwtToken");
182
+ this.delete("refreshToken");
183
+ }
184
+ /**
185
+ * Remove API key from config (logout)
186
+ */
187
+ removeApiKey() {
188
+ this.delete("apiKey");
189
+ }
190
+ /**
191
+ * Set workspace ID in config
192
+ */
193
+ setWorkspaceId(workspaceId) {
194
+ this.set("workspaceId", workspaceId);
195
+ }
196
+ /**
197
+ * Set base URL in config
198
+ */
199
+ setBaseUrl(baseUrl) {
200
+ this.set("baseUrl", baseUrl);
201
+ }
202
+ }
203
+ const configStore = new ConfigStore();
204
+ export {
205
+ ConfigStore,
206
+ configStore,
207
+ getConfigDir
208
+ };
209
+ //# sourceMappingURL=config-store.js.map
@@ -0,0 +1,221 @@
1
+ import chalk from "chalk";
2
+ let globalOptions = {};
3
+ function setOutputOptions(options) {
4
+ globalOptions = { ...globalOptions, ...options };
5
+ }
6
+ function getOutputOptions() {
7
+ return globalOptions;
8
+ }
9
+ function isJsonOutput() {
10
+ return globalOptions.json === true;
11
+ }
12
+ function printJson(data) {
13
+ console.log(JSON.stringify(data, null, 2));
14
+ }
15
+ function printSuccess(message) {
16
+ if (isJsonOutput()) return;
17
+ console.log(chalk.green("\u2713"), message);
18
+ }
19
+ function printError(message, details) {
20
+ if (isJsonOutput()) {
21
+ printJson({ error: message, details });
22
+ return;
23
+ }
24
+ console.error(chalk.red("\u2717"), chalk.red("Error:"), message);
25
+ if (details) {
26
+ console.error(" ", chalk.dim(details));
27
+ }
28
+ }
29
+ function printWarning(message) {
30
+ if (isJsonOutput()) return;
31
+ console.log(chalk.yellow("!"), chalk.yellow("Warning:"), message);
32
+ }
33
+ function printInfo(message) {
34
+ if (isJsonOutput()) return;
35
+ console.log(chalk.blue("\u2139"), message);
36
+ }
37
+ function printLabel(label, value) {
38
+ if (isJsonOutput()) return;
39
+ console.log(chalk.dim(label + ":"), value);
40
+ }
41
+ function printHeader(title) {
42
+ if (isJsonOutput()) return;
43
+ console.log();
44
+ console.log(chalk.bold(title));
45
+ console.log(chalk.dim("\u2500".repeat(title.length)));
46
+ }
47
+ function printBlank() {
48
+ if (isJsonOutput()) return;
49
+ console.log();
50
+ }
51
+ function printTable(columns, rows) {
52
+ if (isJsonOutput()) {
53
+ printJson(rows);
54
+ return;
55
+ }
56
+ if (rows.length === 0) {
57
+ console.log(chalk.dim("No data to display"));
58
+ return;
59
+ }
60
+ const widths = columns.map((col) => {
61
+ const headerWidth = col.header.length;
62
+ const maxDataWidth = rows.reduce((max, row) => {
63
+ const value = col.format ? col.format(row[col.key]) : String(row[col.key] ?? "");
64
+ return Math.max(max, value.length);
65
+ }, 0);
66
+ return col.width ?? Math.max(headerWidth, maxDataWidth);
67
+ });
68
+ const headerLine = columns.map((col, i) => padString(col.header, widths[i] ?? 0, col.align)).join(" ");
69
+ console.log(chalk.bold(headerLine));
70
+ console.log(
71
+ chalk.dim(
72
+ widths.map((w) => "\u2500".repeat(w)).join("\u2500\u2500")
73
+ )
74
+ );
75
+ for (const row of rows) {
76
+ const rowLine = columns.map((col, i) => {
77
+ const value = col.format ? col.format(row[col.key]) : String(row[col.key] ?? "");
78
+ return padString(value, widths[i] ?? 0, col.align);
79
+ }).join(" ");
80
+ console.log(rowLine);
81
+ }
82
+ }
83
+ function padString(str, width, align = "left") {
84
+ const strLen = stripAnsi(str).length;
85
+ const padding = Math.max(0, width - strLen);
86
+ switch (align) {
87
+ case "right":
88
+ return " ".repeat(padding) + str;
89
+ case "center":
90
+ const leftPad = Math.floor(padding / 2);
91
+ const rightPad = padding - leftPad;
92
+ return " ".repeat(leftPad) + str + " ".repeat(rightPad);
93
+ default:
94
+ return str + " ".repeat(padding);
95
+ }
96
+ }
97
+ function stripAnsi(str) {
98
+ return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
99
+ }
100
+ function formatBoolean(value) {
101
+ return value ? chalk.green("\u2713") : chalk.dim("\u2717");
102
+ }
103
+ function formatRelativeDate(dateStr) {
104
+ if (!dateStr) return chalk.dim("Never");
105
+ const date = typeof dateStr === "string" ? new Date(dateStr) : dateStr;
106
+ const now = /* @__PURE__ */ new Date();
107
+ const diffMs = now.getTime() - date.getTime();
108
+ const diffSecs = Math.floor(diffMs / 1e3);
109
+ const diffMins = Math.floor(diffSecs / 60);
110
+ const diffHours = Math.floor(diffMins / 60);
111
+ const diffDays = Math.floor(diffHours / 24);
112
+ if (diffSecs < 60) return "Just now";
113
+ if (diffMins < 60) return `${diffMins} minute${diffMins > 1 ? "s" : ""} ago`;
114
+ if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? "s" : ""} ago`;
115
+ if (diffDays < 7) return `${diffDays} day${diffDays > 1 ? "s" : ""} ago`;
116
+ return date.toLocaleDateString();
117
+ }
118
+ function truncate(str, maxLength) {
119
+ if (str.length <= maxLength) return str;
120
+ return str.slice(0, maxLength - 1) + "\u2026";
121
+ }
122
+ function maskString(str, showFirst = 4, showLast = 4) {
123
+ if (str.length <= showFirst + showLast) {
124
+ return "*".repeat(str.length);
125
+ }
126
+ const first = str.slice(0, showFirst);
127
+ const last = str.slice(-showLast);
128
+ const masked = "*".repeat(Math.min(8, str.length - showFirst - showLast));
129
+ return `${first}${masked}${last}`;
130
+ }
131
+ function formatDuration(ms) {
132
+ if (ms === void 0) return chalk.dim("\u2014");
133
+ if (ms < 1e3) return `${ms}ms`;
134
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
135
+ return `${(ms / 6e4).toFixed(1)}m`;
136
+ }
137
+ function formatStatus(status) {
138
+ const statusColors = {
139
+ success: chalk.green,
140
+ completed: chalk.green,
141
+ active: chalk.green,
142
+ enabled: chalk.green,
143
+ failed: chalk.red,
144
+ error: chalk.red,
145
+ disabled: chalk.red,
146
+ pending: chalk.yellow,
147
+ running: chalk.blue,
148
+ in_progress: chalk.blue
149
+ };
150
+ const colorFn = statusColors[status.toLowerCase()] ?? chalk.white;
151
+ return colorFn(status);
152
+ }
153
+ function formatDate(dateStr) {
154
+ if (!dateStr) return chalk.dim("\u2014");
155
+ const date = typeof dateStr === "string" ? new Date(dateStr) : dateStr;
156
+ return date.toLocaleDateString("en-US", {
157
+ month: "short",
158
+ day: "numeric",
159
+ year: "numeric",
160
+ hour: "2-digit",
161
+ minute: "2-digit"
162
+ });
163
+ }
164
+ function printSimpleTable(headers, rows) {
165
+ if (isJsonOutput()) {
166
+ const data = rows.map((row) => {
167
+ const obj = {};
168
+ headers.forEach((h, i) => {
169
+ obj[h] = row[i] ?? "";
170
+ });
171
+ return obj;
172
+ });
173
+ printJson(data);
174
+ return;
175
+ }
176
+ if (rows.length === 0) {
177
+ console.log(chalk.dim("No data to display"));
178
+ return;
179
+ }
180
+ const widths = headers.map((header, i) => {
181
+ const maxDataWidth = rows.reduce((max, row) => {
182
+ const value = stripAnsi(row[i] ?? "");
183
+ return Math.max(max, value.length);
184
+ }, 0);
185
+ return Math.max(header.length, maxDataWidth);
186
+ });
187
+ const headerLine = headers.map((h, i) => padString(h, widths[i] ?? 0)).join(" ");
188
+ console.log(chalk.bold(headerLine));
189
+ console.log(
190
+ chalk.dim(
191
+ widths.map((w) => "\u2500".repeat(w)).join("\u2500\u2500")
192
+ )
193
+ );
194
+ for (const row of rows) {
195
+ const rowLine = row.map((cell, i) => padString(cell, widths[i] ?? 0)).join(" ");
196
+ console.log(rowLine);
197
+ }
198
+ }
199
+ export {
200
+ formatBoolean,
201
+ formatDate,
202
+ formatDuration,
203
+ formatRelativeDate,
204
+ formatStatus,
205
+ getOutputOptions,
206
+ isJsonOutput,
207
+ maskString,
208
+ printBlank,
209
+ printError,
210
+ printHeader,
211
+ printInfo,
212
+ printJson,
213
+ printLabel,
214
+ printSimpleTable,
215
+ printSuccess,
216
+ printTable,
217
+ printWarning,
218
+ setOutputOptions,
219
+ truncate
220
+ };
221
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1,46 @@
1
+ import { ChanlSDK } from "@protoboxai/sdk";
2
+ import { configStore } from "./config-store.js";
3
+ import { printError } from "./output.js";
4
+ function createSdk() {
5
+ const apiKey = configStore.getApiKey();
6
+ const jwtToken = configStore.getJwtToken();
7
+ const baseUrl = configStore.getBaseUrl();
8
+ if (!apiKey && !jwtToken) {
9
+ printError("Not authenticated", "Run 'chanl login' to authenticate");
10
+ process.exitCode = 1;
11
+ return null;
12
+ }
13
+ const deployment = configStore.getDeployment();
14
+ const apiPrefix = configStore.getApiPrefix();
15
+ if (apiKey) {
16
+ if (apiKey.startsWith("eyJ")) {
17
+ return new ChanlSDK({
18
+ jwtToken: apiKey,
19
+ baseUrl,
20
+ deployment,
21
+ apiPrefix
22
+ });
23
+ }
24
+ return new ChanlSDK({ apiKey, baseUrl, deployment, apiPrefix });
25
+ }
26
+ return new ChanlSDK({
27
+ jwtToken,
28
+ baseUrl,
29
+ deployment,
30
+ apiPrefix
31
+ });
32
+ }
33
+ function getWorkspaceId() {
34
+ const workspaceId = configStore.getWorkspaceId();
35
+ if (!workspaceId) {
36
+ printError("No workspace selected", "Run 'chanl config set workspaceId <id>' to select a workspace");
37
+ process.exitCode = 1;
38
+ return null;
39
+ }
40
+ return workspaceId;
41
+ }
42
+ export {
43
+ createSdk,
44
+ getWorkspaceId
45
+ };
46
+ //# sourceMappingURL=sdk-factory.js.map
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@protoboxai/cli",
3
+ "version": "1.0.0",
4
+ "description": "Command-line interface for Protobox — MCP, tools, knowledge, agents",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "protobox": "./bin/protobox.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsup",
12
+ "dev": "tsup --watch",
13
+ "clean": "rimraf dist",
14
+ "prebuild": "npm run clean",
15
+ "test": "vitest run",
16
+ "test:watch": "vitest",
17
+ "lint": "eslint src/**/*.ts",
18
+ "format": "prettier --write src/**/*.ts",
19
+ "typecheck": "tsc --noEmit"
20
+ },
21
+ "keywords": [
22
+ "protobox",
23
+ "cli",
24
+ "mcp",
25
+ "ai-agents",
26
+ "api",
27
+ "command-line"
28
+ ],
29
+ "author": "Protobox",
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "@protoboxai/sdk": "workspace:*",
33
+ "@inquirer/prompts": "^7.10.1",
34
+ "@modelcontextprotocol/sdk": "^1.25.1",
35
+ "chalk": "^5.3.0",
36
+ "commander": "^12.1.0",
37
+ "conf": "^13.0.1",
38
+ "inquirer": "^12.2.0",
39
+ "open": "^11.0.0",
40
+ "ora": "^8.1.1"
41
+ },
42
+ "devDependencies": {
43
+ "@types/inquirer": "^9.0.7",
44
+ "@types/node": "^22.10.2",
45
+ "rimraf": "^6.0.1",
46
+ "tsup": "^8.3.5",
47
+ "typescript": "^5.7.2",
48
+ "vitest": "^2.1.8"
49
+ },
50
+ "files": [
51
+ "dist/**/*.js",
52
+ "dist/**/*.d.ts",
53
+ "README.md"
54
+ ],
55
+ "engines": {
56
+ "node": ">=18.0.0"
57
+ },
58
+ "publishConfig": {
59
+ "registry": "https://registry.npmjs.org",
60
+ "access": "public"
61
+ }
62
+ }