@romiluz/clawmongo 0.1.0-rc.3 → 0.1.0-rc.4
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/dist/cli/branding.js +129 -0
- package/dist/cli/commands/init.js +173 -0
- package/dist/cli/commands/registry.js +15 -4
- package/dist/main.js +11 -2
- package/package.json +1 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClawMongo CLI Branding
|
|
3
|
+
*
|
|
4
|
+
* ASCII art logo, colors, and branding elements.
|
|
5
|
+
*
|
|
6
|
+
* @module cli/branding
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* ClawMongo ASCII art logo.
|
|
10
|
+
*/
|
|
11
|
+
export const LOGO = `
|
|
12
|
+
\x1b[36m _____ _ __ __
|
|
13
|
+
/ ____| | | \\/ |
|
|
14
|
+
| | | | __ ___ __| \\ / | ___ _ __ __ _ ___
|
|
15
|
+
| | | |/ _\` \\ \\ /\\ / /| |\\/| |/ _ \\| '_ \\ / _\` |/ _ \\
|
|
16
|
+
| |____| | (_| |\\ V V / | | | | (_) | | | | (_| | (_) |
|
|
17
|
+
\\_____|_|\\__,_| \\_/\\_/ |_| |_|\\___/|_| |_|\\__, |\\___/
|
|
18
|
+
__/ |
|
|
19
|
+
|___/ \x1b[0m`;
|
|
20
|
+
/**
|
|
21
|
+
* Tagline displayed under the logo.
|
|
22
|
+
*/
|
|
23
|
+
export const TAGLINE = "\x1b[33m MongoDB is the brain, OpenClaw is the heart.\x1b[0m";
|
|
24
|
+
/**
|
|
25
|
+
* Short tagline for compact displays.
|
|
26
|
+
*/
|
|
27
|
+
export const TAGLINE_SHORT = "\x1b[2mMongoDB-native agentic AI assistant\x1b[0m";
|
|
28
|
+
/**
|
|
29
|
+
* Version banner with logo.
|
|
30
|
+
*/
|
|
31
|
+
export function getVersionBanner(version) {
|
|
32
|
+
return `${LOGO}
|
|
33
|
+
${TAGLINE}
|
|
34
|
+
|
|
35
|
+
\x1b[2mVersion ${version}\x1b[0m
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Welcome message for first-run experience.
|
|
40
|
+
*/
|
|
41
|
+
export function getWelcomeMessage(version) {
|
|
42
|
+
return `${LOGO}
|
|
43
|
+
${TAGLINE}
|
|
44
|
+
|
|
45
|
+
\x1b[32m✨ Welcome to ClawMongo v${version}!\x1b[0m
|
|
46
|
+
|
|
47
|
+
ClawMongo is a MongoDB-native fork of OpenClaw that leverages
|
|
48
|
+
MongoDB's capabilities (Atlas Search, Vector Search, \$rankFusion)
|
|
49
|
+
while maintaining full parity with OpenClaw's features.
|
|
50
|
+
|
|
51
|
+
\x1b[36m📚 Quick Start:\x1b[0m
|
|
52
|
+
|
|
53
|
+
\x1b[33mclawmongo init\x1b[0m Set up your configuration
|
|
54
|
+
\x1b[33mclawmongo chat\x1b[0m Start interactive chat
|
|
55
|
+
\x1b[33mclawmongo send\x1b[0m Send a one-shot message
|
|
56
|
+
\x1b[33mclawmongo health\x1b[0m Check system status
|
|
57
|
+
|
|
58
|
+
\x1b[36m🔧 Configuration:\x1b[0m
|
|
59
|
+
|
|
60
|
+
Set these environment variables:
|
|
61
|
+
|
|
62
|
+
\x1b[2mANTHROPIC_API_KEY\x1b[0m Your Anthropic API key
|
|
63
|
+
\x1b[2mCLAWMONGO_MONGODB_URI\x1b[0m MongoDB connection string (optional)
|
|
64
|
+
\x1b[2mVOYAGE_API_KEY\x1b[0m Voyage AI key for embeddings (optional)
|
|
65
|
+
|
|
66
|
+
\x1b[36m📖 Documentation:\x1b[0m
|
|
67
|
+
|
|
68
|
+
https://github.com/romiluz13/ClawMongo
|
|
69
|
+
|
|
70
|
+
\x1b[2mRun 'clawmongo --help' for all available commands.\x1b[0m
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Compact header for command output.
|
|
75
|
+
*/
|
|
76
|
+
export function getCompactHeader(version) {
|
|
77
|
+
return `\x1b[36m🐾 ClawMongo\x1b[0m v${version}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Success message with emoji.
|
|
81
|
+
*/
|
|
82
|
+
export function successMessage(text) {
|
|
83
|
+
return `\x1b[32m✓\x1b[0m ${text}`;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Error message with emoji.
|
|
87
|
+
*/
|
|
88
|
+
export function errorMessage(text) {
|
|
89
|
+
return `\x1b[31m✗\x1b[0m ${text}`;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Warning message with emoji.
|
|
93
|
+
*/
|
|
94
|
+
export function warnMessage(text) {
|
|
95
|
+
return `\x1b[33m⚠\x1b[0m ${text}`;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Info message with emoji.
|
|
99
|
+
*/
|
|
100
|
+
export function infoMessage(text) {
|
|
101
|
+
return `\x1b[36mℹ\x1b[0m ${text}`;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Step indicator for multi-step processes.
|
|
105
|
+
*/
|
|
106
|
+
export function stepIndicator(current, total, text) {
|
|
107
|
+
return `\x1b[36m[${current}/${total}]\x1b[0m ${text}`;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Progress bar.
|
|
111
|
+
*/
|
|
112
|
+
export function progressBar(percent, width = 30) {
|
|
113
|
+
const filled = Math.round((percent / 100) * width);
|
|
114
|
+
const empty = width - filled;
|
|
115
|
+
const bar = "\x1b[32m" + "█".repeat(filled) + "\x1b[2m" + "░".repeat(empty) + "\x1b[0m";
|
|
116
|
+
return `${bar} ${percent.toFixed(0)}%`;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Box drawing for important messages.
|
|
120
|
+
*/
|
|
121
|
+
export function boxMessage(title, lines) {
|
|
122
|
+
const maxLen = Math.max(title.length, ...lines.map(l => l.length));
|
|
123
|
+
const top = `┌${"─".repeat(maxLen + 2)}┐`;
|
|
124
|
+
const titleLine = `│ \x1b[1m${title.padEnd(maxLen)}\x1b[0m │`;
|
|
125
|
+
const sep = `├${"─".repeat(maxLen + 2)}┤`;
|
|
126
|
+
const content = lines.map(l => `│ ${l.padEnd(maxLen)} │`).join("\n");
|
|
127
|
+
const bottom = `└${"─".repeat(maxLen + 2)}┘`;
|
|
128
|
+
return `${top}\n${titleLine}\n${sep}\n${content}\n${bottom}`;
|
|
129
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClawMongo Init Command
|
|
3
|
+
*
|
|
4
|
+
* Interactive setup wizard for first-time configuration.
|
|
5
|
+
*
|
|
6
|
+
* @module cli/commands/init
|
|
7
|
+
*/
|
|
8
|
+
import * as readline from "readline";
|
|
9
|
+
import * as fs from "fs";
|
|
10
|
+
import * as path from "path";
|
|
11
|
+
import { ExitCode } from "./types.js";
|
|
12
|
+
import { createOutput } from "./output.js";
|
|
13
|
+
import { LOGO, TAGLINE, successMessage, errorMessage, stepIndicator, boxMessage } from "../branding.js";
|
|
14
|
+
/**
|
|
15
|
+
* Prompt user for input with optional default.
|
|
16
|
+
*/
|
|
17
|
+
async function prompt(rl, question, defaultValue, isSecret = false) {
|
|
18
|
+
const defaultHint = defaultValue ? ` \x1b[2m(${isSecret ? "****" : defaultValue})\x1b[0m` : "";
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
rl.question(` ${question}${defaultHint}: `, (answer) => {
|
|
21
|
+
resolve(answer.trim() || defaultValue || "");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Prompt for yes/no confirmation.
|
|
27
|
+
*/
|
|
28
|
+
async function confirm(rl, question, defaultYes = true) {
|
|
29
|
+
const hint = defaultYes ? "[Y/n]" : "[y/N]";
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
rl.question(` ${question} ${hint}: `, (answer) => {
|
|
32
|
+
const a = answer.trim().toLowerCase();
|
|
33
|
+
if (a === "")
|
|
34
|
+
resolve(defaultYes);
|
|
35
|
+
else
|
|
36
|
+
resolve(a === "y" || a === "yes");
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Select from a list of options using arrow keys.
|
|
42
|
+
*/
|
|
43
|
+
async function select(rl, question, options, defaultIndex = 0) {
|
|
44
|
+
console.log(`\n ${question}\n`);
|
|
45
|
+
let selectedIndex = defaultIndex;
|
|
46
|
+
// For simplicity, use numbered selection (arrow keys require raw mode)
|
|
47
|
+
options.forEach((opt, i) => {
|
|
48
|
+
const marker = i === defaultIndex ? "\x1b[36m→\x1b[0m" : " ";
|
|
49
|
+
console.log(` ${marker} ${i + 1}. ${opt.label}`);
|
|
50
|
+
});
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
rl.question(`\n Enter number (1-${options.length}): `, (answer) => {
|
|
53
|
+
const num = parseInt(answer.trim(), 10);
|
|
54
|
+
if (num >= 1 && num <= options.length) {
|
|
55
|
+
const selected = options[num - 1];
|
|
56
|
+
resolve(selected ? selected.value : options[0]?.value ?? "");
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const defaultOpt = options[defaultIndex];
|
|
60
|
+
resolve(defaultOpt ? defaultOpt.value : options[0]?.value ?? "");
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Write config to .env file.
|
|
67
|
+
*/
|
|
68
|
+
function writeEnvFile(config, filePath) {
|
|
69
|
+
const lines = [
|
|
70
|
+
"# ClawMongo Configuration",
|
|
71
|
+
"# Generated by 'clawmongo init'",
|
|
72
|
+
"",
|
|
73
|
+
];
|
|
74
|
+
if (config.anthropicApiKey) {
|
|
75
|
+
lines.push(`ANTHROPIC_API_KEY=${config.anthropicApiKey}`);
|
|
76
|
+
}
|
|
77
|
+
if (config.mongodbUri) {
|
|
78
|
+
lines.push(`CLAWMONGO_MONGODB_URI=${config.mongodbUri}`);
|
|
79
|
+
}
|
|
80
|
+
if (config.voyageApiKey) {
|
|
81
|
+
lines.push(`VOYAGE_API_KEY=${config.voyageApiKey}`);
|
|
82
|
+
}
|
|
83
|
+
if (config.openaiApiKey) {
|
|
84
|
+
lines.push(`OPENAI_API_KEY=${config.openaiApiKey}`);
|
|
85
|
+
}
|
|
86
|
+
lines.push("");
|
|
87
|
+
fs.writeFileSync(filePath, lines.join("\n"));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Init command - interactive setup wizard.
|
|
91
|
+
*/
|
|
92
|
+
export const initCommand = {
|
|
93
|
+
name: "init",
|
|
94
|
+
aliases: ["setup", "configure"],
|
|
95
|
+
description: "Interactive setup wizard",
|
|
96
|
+
usage: "clawmongo init [--force]",
|
|
97
|
+
async execute(ctx) {
|
|
98
|
+
const out = createOutput({ json: ctx.json });
|
|
99
|
+
const envPath = path.join(process.cwd(), ".env");
|
|
100
|
+
// Check if .env already exists
|
|
101
|
+
if (fs.existsSync(envPath) && !ctx.flags.force) {
|
|
102
|
+
out.writeln(errorMessage(".env file already exists. Use --force to overwrite."));
|
|
103
|
+
return ExitCode.ERROR;
|
|
104
|
+
}
|
|
105
|
+
// Show welcome banner
|
|
106
|
+
console.log(LOGO);
|
|
107
|
+
console.log(TAGLINE);
|
|
108
|
+
console.log("\n \x1b[1m✨ Welcome to ClawMongo Setup!\x1b[0m\n");
|
|
109
|
+
console.log(" This wizard will help you configure ClawMongo.");
|
|
110
|
+
console.log(" Press Enter to accept defaults, or type your values.\n");
|
|
111
|
+
const rl = readline.createInterface({
|
|
112
|
+
input: process.stdin,
|
|
113
|
+
output: process.stdout
|
|
114
|
+
});
|
|
115
|
+
const config = {};
|
|
116
|
+
try {
|
|
117
|
+
// Step 1: Anthropic API Key
|
|
118
|
+
console.log(stepIndicator(1, 4, "\x1b[1mAnthropic API Key\x1b[0m"));
|
|
119
|
+
console.log(" \x1b[2mRequired for Claude AI. Get yours at https://console.anthropic.com\x1b[0m\n");
|
|
120
|
+
config.anthropicApiKey = await prompt(rl, "ANTHROPIC_API_KEY", process.env.ANTHROPIC_API_KEY, true);
|
|
121
|
+
// Step 2: MongoDB URI
|
|
122
|
+
console.log("\n" + stepIndicator(2, 4, "\x1b[1mMongoDB Connection\x1b[0m"));
|
|
123
|
+
console.log(" \x1b[2mOptional. Enables persistent storage, Atlas Search, and vector search.\x1b[0m\n");
|
|
124
|
+
const useMongo = await confirm(rl, "Configure MongoDB?", true);
|
|
125
|
+
if (useMongo) {
|
|
126
|
+
config.mongodbUri = await prompt(rl, "CLAWMONGO_MONGODB_URI", process.env.CLAWMONGO_MONGODB_URI);
|
|
127
|
+
}
|
|
128
|
+
// Step 3: Embedding Provider
|
|
129
|
+
console.log("\n" + stepIndicator(3, 4, "\x1b[1mEmbedding Provider\x1b[0m"));
|
|
130
|
+
console.log(" \x1b[2mOptional. Enables semantic search and RAG capabilities.\x1b[0m\n");
|
|
131
|
+
const embeddingProvider = await select(rl, "Select embedding provider:", [
|
|
132
|
+
{ value: "none", label: "None (skip for now)" },
|
|
133
|
+
{ value: "voyage", label: "Voyage AI (recommended)" },
|
|
134
|
+
{ value: "openai", label: "OpenAI" }
|
|
135
|
+
], 0);
|
|
136
|
+
if (embeddingProvider === "voyage") {
|
|
137
|
+
config.voyageApiKey = await prompt(rl, "VOYAGE_API_KEY", process.env.VOYAGE_API_KEY, true);
|
|
138
|
+
}
|
|
139
|
+
else if (embeddingProvider === "openai") {
|
|
140
|
+
config.openaiApiKey = await prompt(rl, "OPENAI_API_KEY", process.env.OPENAI_API_KEY, true);
|
|
141
|
+
}
|
|
142
|
+
// Step 4: Confirm and write
|
|
143
|
+
console.log("\n" + stepIndicator(4, 4, "\x1b[1mConfirm Configuration\x1b[0m\n"));
|
|
144
|
+
const summary = [
|
|
145
|
+
`Anthropic API Key: ${config.anthropicApiKey ? "✓ Set" : "✗ Not set"}`,
|
|
146
|
+
`MongoDB URI: ${config.mongodbUri ? "✓ Set" : "✗ Not set"}`,
|
|
147
|
+
`Voyage API Key: ${config.voyageApiKey ? "✓ Set" : "✗ Not set"}`,
|
|
148
|
+
`OpenAI API Key: ${config.openaiApiKey ? "✓ Set" : "✗ Not set"}`
|
|
149
|
+
];
|
|
150
|
+
console.log(boxMessage("Configuration Summary", summary));
|
|
151
|
+
console.log("");
|
|
152
|
+
const shouldWrite = await confirm(rl, "Write configuration to .env?", true);
|
|
153
|
+
if (shouldWrite) {
|
|
154
|
+
writeEnvFile(config, envPath);
|
|
155
|
+
console.log("\n" + successMessage(`Configuration saved to ${envPath}`));
|
|
156
|
+
console.log("\n \x1b[36m🚀 You're all set! Try these commands:\x1b[0m\n");
|
|
157
|
+
console.log(" \x1b[33mclawmongo health\x1b[0m Check system status");
|
|
158
|
+
console.log(" \x1b[33mclawmongo chat\x1b[0m Start interactive chat");
|
|
159
|
+
console.log(" \x1b[33mclawmongo send\x1b[0m Send a message\n");
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
console.log("\n" + errorMessage("Setup cancelled."));
|
|
163
|
+
}
|
|
164
|
+
rl.close();
|
|
165
|
+
return ExitCode.SUCCESS;
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
rl.close();
|
|
169
|
+
out.error(err instanceof Error ? err.message : String(err));
|
|
170
|
+
return ExitCode.ERROR;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
@@ -74,15 +74,26 @@ export function generateHelp(command, prefix = "") {
|
|
|
74
74
|
export function generateGlobalHelp(registry, version) {
|
|
75
75
|
const commands = registry.list();
|
|
76
76
|
const lines = [];
|
|
77
|
-
|
|
78
|
-
lines.push("
|
|
77
|
+
// ASCII Logo
|
|
78
|
+
lines.push("\x1b[36m _____ _ __ __ ");
|
|
79
|
+
lines.push(" / ____| | | \\/ | ");
|
|
80
|
+
lines.push(" | | | | __ ___ __| \\ / | ___ _ __ __ _ ___ ");
|
|
81
|
+
lines.push(" | | | |/ _` \\ \\ /\\ / /| |\\/| |/ _ \\| '_ \\ / _` |/ _ \\ ");
|
|
82
|
+
lines.push(" | |____| | (_| |\\ V V / | | | | (_) | | | | (_| | (_) |");
|
|
83
|
+
lines.push(" \\_____|_|\\__,_| \\_/\\_/ |_| |_|\\___/|_| |_|\\__, |\\___/ ");
|
|
84
|
+
lines.push(" __/ | ");
|
|
85
|
+
lines.push(" |___/ \x1b[0m");
|
|
86
|
+
lines.push("");
|
|
87
|
+
lines.push("\x1b[33m MongoDB is the brain, OpenClaw is the heart.\x1b[0m");
|
|
88
|
+
lines.push("");
|
|
89
|
+
lines.push(` \x1b[2mVersion ${version}\x1b[0m`);
|
|
79
90
|
lines.push("");
|
|
80
91
|
lines.push("Usage: clawmongo <command> [options]");
|
|
81
92
|
lines.push("");
|
|
82
93
|
lines.push("Commands:");
|
|
83
94
|
for (const cmd of commands) {
|
|
84
95
|
const aliasStr = cmd.aliases ? ` (${cmd.aliases.join(", ")})` : "";
|
|
85
|
-
lines.push(` ${cmd.name.padEnd(12)}${aliasStr.padEnd(8)} ${cmd.description}`);
|
|
96
|
+
lines.push(` \x1b[33m${cmd.name.padEnd(12)}\x1b[0m${aliasStr.padEnd(8)} ${cmd.description}`);
|
|
86
97
|
}
|
|
87
98
|
lines.push("");
|
|
88
99
|
lines.push("Options:");
|
|
@@ -91,6 +102,6 @@ export function generateGlobalHelp(registry, version) {
|
|
|
91
102
|
lines.push(" --json Output as JSON");
|
|
92
103
|
lines.push(" -V, --verbose Verbose output");
|
|
93
104
|
lines.push("");
|
|
94
|
-
lines.push("
|
|
105
|
+
lines.push("\x1b[2mRun 'clawmongo <command> --help' for command-specific help.\x1b[0m");
|
|
95
106
|
return lines.join("\n");
|
|
96
107
|
}
|
package/dist/main.js
CHANGED
|
@@ -19,13 +19,17 @@ import { cronCommand } from "./cli/commands/cron.js";
|
|
|
19
19
|
import { backupCommand } from "./cli/commands/backup.js";
|
|
20
20
|
import { securityCommand } from "./cli/commands/security.js";
|
|
21
21
|
import { benchmarkCommand } from "./cli/commands/benchmark.js";
|
|
22
|
-
|
|
22
|
+
import { initCommand } from "./cli/commands/init.js";
|
|
23
|
+
// Import branding
|
|
24
|
+
import { getVersionBanner, getWelcomeMessage } from "./cli/branding.js";
|
|
25
|
+
const VERSION = "0.1.0-rc.4";
|
|
23
26
|
/**
|
|
24
27
|
* Build the command registry.
|
|
25
28
|
*/
|
|
26
29
|
function buildRegistry() {
|
|
27
30
|
const registry = createRegistry();
|
|
28
31
|
// Register all commands
|
|
32
|
+
registry.register(initCommand); // Setup wizard first
|
|
29
33
|
registry.register(healthCommand);
|
|
30
34
|
registry.register(statusCommand);
|
|
31
35
|
registry.register(doctorCommand);
|
|
@@ -51,7 +55,12 @@ async function main() {
|
|
|
51
55
|
const out = createOutput({ json: hasFlag(parsed.flags, "json") });
|
|
52
56
|
// Handle --version
|
|
53
57
|
if (hasFlag(parsed.flags, "version") || hasFlag(parsed.flags, "v")) {
|
|
54
|
-
out.writeln(
|
|
58
|
+
out.writeln(getVersionBanner(VERSION));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Handle no command - show welcome message
|
|
62
|
+
if (!parsed.command && !hasFlag(parsed.flags, "help")) {
|
|
63
|
+
out.writeln(getWelcomeMessage(VERSION));
|
|
55
64
|
return;
|
|
56
65
|
}
|
|
57
66
|
// Handle --help with no command
|