mojulo 0.0.0 → 0.1.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.
- package/README.md +53 -4
- package/lib/audit-logger-new.js +11 -0
- package/lib/auth/gate.js +25 -0
- package/lib/auth/service.js +17 -0
- package/lib/auth/session.js +63 -0
- package/lib/builder/chat-processor.js +607 -0
- package/lib/builder/composer-bridge.js +82 -0
- package/lib/builder/evaluator.js +159 -0
- package/lib/builder/executor.js +252 -0
- package/lib/builder/index.js +48 -0
- package/lib/builder/session.js +248 -0
- package/lib/builder/system-prompt.js +422 -0
- package/lib/builder/tone-presets.js +75 -0
- package/lib/builder/tool-executors.js +1418 -0
- package/lib/builder/tools.js +338 -0
- package/lib/builder/validators.js +239 -0
- package/lib/composer/composer.js +225 -0
- package/lib/composer/index.js +40 -0
- package/lib/composer/protocols/00_base.txt +19 -0
- package/lib/composer/protocols/01_knowledge.txt +9 -0
- package/lib/composer/protocols/02_form-gathering.txt +32 -0
- package/lib/composer/protocols/03_appointments.txt +16 -0
- package/lib/composer/protocols/04_triage.txt +15 -0
- package/lib/composer/protocols/05_optical-read.txt +22 -0
- package/lib/composer/response-builder.js +98 -0
- package/lib/config-builder.js +650 -0
- package/lib/db/ids.js +10 -0
- package/lib/db/index.js +179 -0
- package/lib/db/repositories/apiKeys.js +72 -0
- package/lib/db/repositories/auditLogs.js +12 -0
- package/lib/db/repositories/botSpaces.js +12 -0
- package/lib/db/repositories/builderSessions.js +312 -0
- package/lib/db/repositories/deploymentEvents.js +12 -0
- package/lib/db/repositories/deployments.js +385 -0
- package/lib/db/repositories/documents.js +68 -0
- package/lib/db/repositories/mcpJobs.js +84 -0
- package/lib/deployers/bot-fleet.js +110 -0
- package/lib/deployers/bot-proxy.js +72 -0
- package/lib/deployers/build.js +89 -0
- package/lib/deployers/cloud-deploy.js +310 -0
- package/lib/deployers/docker.js +439 -0
- package/lib/deployers/fly.js +432 -0
- package/lib/deployers/index.js +38 -0
- package/lib/deployment-auth.js +36 -0
- package/lib/document-parser.js +171 -0
- package/lib/embedder/chunker.js +93 -0
- package/lib/embedder/local.js +101 -0
- package/lib/embedder/preview-rag.js +93 -0
- package/lib/envelope-schema.js +54 -0
- package/lib/fleet/scoped-sql.js +342 -0
- package/lib/form-schema-config/base.js +135 -0
- package/lib/form-schema-config/index.js +286 -0
- package/lib/form-schema-config/locales/af-ZA.js +153 -0
- package/lib/form-schema-config/locales/ar-AE.js +142 -0
- package/lib/form-schema-config/locales/ar-SA.js +164 -0
- package/lib/form-schema-config/locales/de-DE.js +152 -0
- package/lib/form-schema-config/locales/en-AU.js +161 -0
- package/lib/form-schema-config/locales/en-CA.js +115 -0
- package/lib/form-schema-config/locales/en-GB.js +132 -0
- package/lib/form-schema-config/locales/en-IN.js +219 -0
- package/lib/form-schema-config/locales/en-MY.js +171 -0
- package/lib/form-schema-config/locales/en-NG.js +198 -0
- package/lib/form-schema-config/locales/en-PH.js +186 -0
- package/lib/form-schema-config/locales/en-SG.js +153 -0
- package/lib/form-schema-config/locales/en-US.js +138 -0
- package/lib/form-schema-config/locales/es-ES.js +171 -0
- package/lib/form-schema-config/locales/es-MX.js +193 -0
- package/lib/form-schema-config/locales/fr-CA.js +138 -0
- package/lib/form-schema-config/locales/fr-FR.js +155 -0
- package/lib/form-schema-config/locales/hi-IN.js +219 -0
- package/lib/form-schema-config/locales/it-IT.js +157 -0
- package/lib/form-schema-config/locales/ja-JP.js +169 -0
- package/lib/form-schema-config/locales/ko-KR.js +140 -0
- package/lib/form-schema-config/locales/nl-NL.js +149 -0
- package/lib/form-schema-config/locales/pt-BR.js +168 -0
- package/lib/form-schema-config/locales/zh-CN.js +172 -0
- package/lib/form-schema-config/locales/zh-HK.js +142 -0
- package/lib/form-structure-schema.js +191 -0
- package/lib/llm-providers.js +828 -0
- package/lib/markdown.js +197 -0
- package/lib/mcp/catalysts/appointment-to-calendar.md +84 -0
- package/lib/mcp/catalysts/conversations-to-channel-digest.md +104 -0
- package/lib/mcp/catalysts/document-extract-to-store.md +92 -0
- package/lib/mcp/catalysts/knowledge-gap-miner.md +96 -0
- package/lib/mcp/catalysts/loader.js +144 -0
- package/lib/mcp/catalysts/qualify-lead-to-crm.md +83 -0
- package/lib/mcp/catalysts/scan-conversations-for-signal.md +92 -0
- package/lib/mcp/catalysts/submission-to-ticket.md +83 -0
- package/lib/mcp/catalysts/submissions-to-warehouse.md +103 -0
- package/lib/mcp/catalysts/weekly-submissions-digest.md +82 -0
- package/lib/mcp/jobs.js +64 -0
- package/lib/mcp/server.js +184 -0
- package/lib/mcp/session-binding.js +130 -0
- package/lib/mcp/tools/build.js +123 -0
- package/lib/mcp/tools/catalysts.js +477 -0
- package/lib/mcp/tools/context.js +325 -0
- package/lib/mcp/tools/fleet.js +391 -0
- package/lib/mcp/tools/jobs-tools.js +240 -0
- package/lib/mcp/tools/operate.js +314 -0
- package/lib/preview/build-preview-config.js +136 -0
- package/lib/rate-limiter.js +11 -0
- package/lib/resolve-api-key.js +142 -0
- package/lib/storage/index.js +40 -0
- package/messages/de.json +2136 -0
- package/messages/en.json +2136 -0
- package/messages/es.json +2136 -0
- package/messages/fr.json +2136 -0
- package/messages/it.json +2136 -0
- package/messages/ja.json +2136 -0
- package/messages/ko.json +2136 -0
- package/messages/nl.json +2136 -0
- package/messages/pl.json +2136 -0
- package/messages/pt.json +2136 -0
- package/messages/ru.json +2136 -0
- package/messages/uk.json +2136 -0
- package/messages/zh.json +2136 -0
- package/package.json +61 -5
- package/scripts/mcp-config.mjs +162 -0
- package/scripts/mcp-stdio-loader.mjs +42 -0
- package/scripts/mcp-stdio.mjs +108 -0
- package/scripts/mojulo-paths.mjs +48 -0
package/README.md
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Mojulo
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
building self-hosted bots from inside Claude Code or Claude Desktop.
|
|
3
|
+
**MCP server for building self-hosted chatbots from inside Claude.** Describe the bot you want — Mojulo compiles it into a portable Docker artifact you own. Conversations live in the bot's own SQLite, hash-chained turn by turn. The MCP surface composes alongside your other MCPs (Drive, Gmail, your CRM), so the build/deploy/operate loop runs entirely inside a Claude session.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
## Quickstart
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 1. Wire mojulo into Claude (Claude Code or Claude Desktop)
|
|
9
|
+
claude mcp add mojulo --command "npx -y mojulo"
|
|
10
|
+
|
|
11
|
+
# 2. Configure at least one LLM provider key
|
|
12
|
+
npx -y mojulo-config set anthropic sk-ant-...
|
|
13
|
+
|
|
14
|
+
# 3. In a Claude session, ask:
|
|
15
|
+
# "build me a triage bot for my dental practice"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Compiled bots land in `~/.mojulo/data/artifacts/`. Run them with `docker compose up`, or set a Fly token (`mojulo-config set fly fo1_...`) and ask Claude to deploy to the cloud.
|
|
19
|
+
|
|
20
|
+
On first connect, Claude calls `forward_context` to read mojulo's glossary, lifecycle, and tool index — so the session orients itself before doing anything.
|
|
21
|
+
|
|
22
|
+
## Tools at a glance
|
|
23
|
+
|
|
24
|
+
- **Build** — `infer_intent`, `generate_*`, `save_modular_bot`. Describe a bot in free text; the tools sequence themselves into a compiled zip.
|
|
25
|
+
- **Operate** — `get_deployment`, `query_conversations`, `get_conversation`, `query_submissions`, `verify_chain`. Read what each connected bot has captured. Transcript content never leaves the bot's SQLite — these tools proxy through.
|
|
26
|
+
- **Fleet** — `fleet_query_conversations`, `fleet_analytics_summary`, `verify_fleet_chains`. Cross-bot rollups; same posture, just batched.
|
|
27
|
+
- **Catalysts** — `list_catalysts`, `recommend_catalysts`, `get_catalyst`. Curated workflow recipes Claude turns into local skills in your `.claude/skills/`.
|
|
28
|
+
|
|
29
|
+
## Catalysts shipped
|
|
30
|
+
|
|
31
|
+
`qualify-lead-to-crm` · `appointment-to-calendar` · `submission-to-ticket` · `submissions-to-warehouse` · `document-extract-to-store` · `scan-conversations-for-signal` · `knowledge-gap-miner` · `weekly-submissions-digest` · `conversations-to-channel-digest`
|
|
32
|
+
|
|
33
|
+
Claude reads one, binds it to a destination MCP you already have installed, and writes a local skill. The catalyst is the nucleation point; the resulting skill is yours.
|
|
34
|
+
|
|
35
|
+
## Dashboard
|
|
36
|
+
|
|
37
|
+
There's a browser dashboard (Settings UI, in-app builder, wizard) but it's not shipped in this npm package yet — clone the repo to run it:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
git clone https://github.com/zombico/mojulo.git
|
|
41
|
+
cd mojulo/control && cp .env.example .env && npm install && npm run dev
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The MCP surface in the npm package covers the build/deploy/operate loop end-to-end without it.
|
|
45
|
+
|
|
46
|
+
## More
|
|
47
|
+
|
|
48
|
+
- Full repo and docs: <https://github.com/zombico/mojulo>
|
|
49
|
+
- Architecture: [ARCHITECTURE.md](https://github.com/zombico/mojulo/blob/main/ARCHITECTURE.md)
|
|
50
|
+
- MCP integration: [docs/mcp-integration.md](https://github.com/zombico/mojulo/blob/main/docs/mcp-integration.md)
|
|
51
|
+
- Catalysts: [docs/catalysts.md](https://github.com/zombico/mojulo/blob/main/docs/catalysts.md)
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
[Apache License 2.0](https://github.com/zombico/mojulo/blob/main/LICENSE)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Lite writes audit events to stdout for transparency during local ops.
|
|
2
|
+
// Fleet-wide audit trails live in the Full product.
|
|
3
|
+
|
|
4
|
+
export async function auditLog(event, _request) {
|
|
5
|
+
if (process.env.MOJULO_LITE_AUDIT_QUIET === '1') return;
|
|
6
|
+
try {
|
|
7
|
+
console.log('[audit]', JSON.stringify({ ts: Date.now(), ...event }));
|
|
8
|
+
} catch {
|
|
9
|
+
// ignore
|
|
10
|
+
}
|
|
11
|
+
}
|
package/lib/auth/gate.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { redirect } from 'next/navigation';
|
|
2
|
+
import { ApiKeyRepository } from '@/lib/db/repositories/apiKeys';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* requireLLMKey
|
|
6
|
+
*
|
|
7
|
+
* Gate for pages that depend on an LLM provider key being configured.
|
|
8
|
+
* Mojulo-Lite's builders (wizard + chat builder) all call out to the user's LLM
|
|
9
|
+
* (to compose RAG summaries, generate forms, infer intent, etc.), so they
|
|
10
|
+
* are unusable until a key has been saved on /settings.
|
|
11
|
+
*
|
|
12
|
+
* Use from any server component:
|
|
13
|
+
*
|
|
14
|
+
* export default async function Page() {
|
|
15
|
+
* await requireLLMKey();
|
|
16
|
+
* // ...render
|
|
17
|
+
* }
|
|
18
|
+
*/
|
|
19
|
+
export async function requireLLMKey() {
|
|
20
|
+
const keys = await ApiKeyRepository.findByUserId('local');
|
|
21
|
+
if (!keys || keys.length === 0) {
|
|
22
|
+
redirect('/settings?gate=no-key');
|
|
23
|
+
}
|
|
24
|
+
return keys;
|
|
25
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Single-user auth stub for Mojulo-Lite.
|
|
2
|
+
// Every request is treated as the same local operator. The user object shape
|
|
3
|
+
// matches what the builder/deploy code expects (id + email).
|
|
4
|
+
|
|
5
|
+
const LOCAL_USER = {
|
|
6
|
+
id: 'local',
|
|
7
|
+
email: 'local@mojulo-lite',
|
|
8
|
+
name: 'Local Operator',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export async function getCurrentUser() {
|
|
12
|
+
return LOCAL_USER;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function requireAuth() {
|
|
16
|
+
return LOCAL_USER;
|
|
17
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// HMAC-signed session token, used by middleware.js + /api/auth/*.
|
|
2
|
+
// The token is `<exp>.<base64url(hmac(password, exp))>`. Signing with the
|
|
3
|
+
// password itself means rotating CONTROL_PLANE_PASSWORD invalidates every
|
|
4
|
+
// outstanding session with no extra bookkeeping.
|
|
5
|
+
//
|
|
6
|
+
// Web Crypto only — middleware runs on the Edge runtime.
|
|
7
|
+
|
|
8
|
+
const enc = new TextEncoder();
|
|
9
|
+
|
|
10
|
+
export const SESSION_COOKIE = 'mojulo_session';
|
|
11
|
+
export const SESSION_TTL_SECONDS = 60 * 60 * 24 * 7;
|
|
12
|
+
|
|
13
|
+
export function isAuthEnabled() {
|
|
14
|
+
return !!(process.env.CONTROL_PLANE_USER && process.env.CONTROL_PLANE_PASSWORD);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function hmacKey(password) {
|
|
18
|
+
return crypto.subtle.importKey(
|
|
19
|
+
'raw',
|
|
20
|
+
enc.encode(password),
|
|
21
|
+
{ name: 'HMAC', hash: 'SHA-256' },
|
|
22
|
+
false,
|
|
23
|
+
['sign', 'verify'],
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function b64urlEncode(buf) {
|
|
28
|
+
const bytes = new Uint8Array(buf);
|
|
29
|
+
let bin = '';
|
|
30
|
+
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
|
31
|
+
return btoa(bin).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function b64urlDecode(s) {
|
|
35
|
+
const pad = s.length % 4 === 0 ? '' : '='.repeat(4 - (s.length % 4));
|
|
36
|
+
const bin = atob(s.replaceAll('-', '+').replaceAll('_', '/') + pad);
|
|
37
|
+
const buf = new Uint8Array(bin.length);
|
|
38
|
+
for (let i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);
|
|
39
|
+
return buf.buffer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function createSessionToken(password, ttlSeconds = SESSION_TTL_SECONDS) {
|
|
43
|
+
const exp = Math.floor(Date.now() / 1000) + ttlSeconds;
|
|
44
|
+
const key = await hmacKey(password);
|
|
45
|
+
const sig = await crypto.subtle.sign('HMAC', key, enc.encode(String(exp)));
|
|
46
|
+
return `${exp}.${b64urlEncode(sig)}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function verifySessionToken(token, password) {
|
|
50
|
+
if (!token || typeof token !== 'string') return false;
|
|
51
|
+
const dot = token.indexOf('.');
|
|
52
|
+
if (dot === -1) return false;
|
|
53
|
+
const expStr = token.slice(0, dot);
|
|
54
|
+
const sigStr = token.slice(dot + 1);
|
|
55
|
+
const exp = Number.parseInt(expStr, 10);
|
|
56
|
+
if (!Number.isFinite(exp) || exp < Math.floor(Date.now() / 1000)) return false;
|
|
57
|
+
try {
|
|
58
|
+
const key = await hmacKey(password);
|
|
59
|
+
return await crypto.subtle.verify('HMAC', key, b64urlDecode(sigStr), enc.encode(expStr));
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|