amalfa 1.0.22 ā 1.0.23
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/package.json +1 -1
- package/src/cli/enhance-commands.ts +81 -0
- package/src/cli/list-scripts.ts +67 -0
- package/src/cli/sonar-chat.ts +95 -0
- package/src/cli.ts +346 -92
- package/src/config/defaults.ts +135 -26
- package/src/config/scripts-registry.json +72 -0
- package/src/daemon/index.ts +41 -24
- package/src/daemon/sonar-agent.ts +774 -0
- package/src/mcp/index.ts +114 -8
- package/src/resonance/db.ts +13 -0
- package/src/utils/DaemonManager.ts +68 -4
- package/src/utils/ServiceLifecycle.ts +195 -208
- package/src/utils/StatsTracker.ts +8 -4
- package/src/utils/ollama-discovery.ts +190 -0
- package/src/utils/sonar-client.ts +294 -0
- package/src/utils/ZombieDefense.ts +0 -258
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amalfa",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23",
|
|
4
4
|
"description": "Local-first knowledge graph engine for AI agents. Transforms markdown into searchable memory with MCP protocol.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/pjsvis/amalfa#readme",
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { DaemonManager } from "../utils/DaemonManager";
|
|
2
|
+
// import { getLogger } from "../utils/Logger";
|
|
3
|
+
|
|
4
|
+
// const log = getLogger("CLI:Enhance");
|
|
5
|
+
|
|
6
|
+
export async function cmdEnhance(args: string[]) {
|
|
7
|
+
const manager = new DaemonManager();
|
|
8
|
+
const status = await manager.checkSonarAgent();
|
|
9
|
+
|
|
10
|
+
if (!status.running) {
|
|
11
|
+
console.error("ā Sonar Agent is not running.");
|
|
12
|
+
console.error(" Please start it first: amalfa sonar start");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const BASE_URL = `http://localhost:${status.port}`;
|
|
17
|
+
|
|
18
|
+
// Parse arguments
|
|
19
|
+
const batchIdx = args.indexOf("--batch");
|
|
20
|
+
const docIdx = args.indexOf("--doc");
|
|
21
|
+
const limitIdx = args.indexOf("--limit");
|
|
22
|
+
|
|
23
|
+
if (batchIdx !== -1) {
|
|
24
|
+
// Batch Mode
|
|
25
|
+
let limit = 50;
|
|
26
|
+
if (limitIdx !== -1) {
|
|
27
|
+
const limitArg = args[limitIdx + 1];
|
|
28
|
+
if (limitArg !== undefined) {
|
|
29
|
+
limit = parseInt(limitArg, 10);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log(`š Starting batch enhancement (Limit: ${limit})...`);
|
|
34
|
+
try {
|
|
35
|
+
const res = await fetch(`${BASE_URL}/metadata/batch`, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: { "Content-Type": "application/json" },
|
|
38
|
+
body: JSON.stringify({ limit }),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
42
|
+
|
|
43
|
+
const result = (await res.json()) as {
|
|
44
|
+
processed: number;
|
|
45
|
+
errors: number;
|
|
46
|
+
};
|
|
47
|
+
console.log(`ā
Batch complete:`);
|
|
48
|
+
console.log(` Processed: ${result.processed}`);
|
|
49
|
+
console.log(` Errors: ${result.errors}`);
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.error("ā Batch enhancement failed:", e);
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (docIdx !== -1 && args[docIdx + 1]) {
|
|
57
|
+
// Single Doc Mode
|
|
58
|
+
const docId = args[docIdx + 1];
|
|
59
|
+
console.log(`š Enhancing document: ${docId}...`);
|
|
60
|
+
try {
|
|
61
|
+
const res = await fetch(`${BASE_URL}/metadata/enhance`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: { "Content-Type": "application/json" },
|
|
64
|
+
body: JSON.stringify({ docId }),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
68
|
+
|
|
69
|
+
const result = await res.json();
|
|
70
|
+
console.log("ā
Enhancement successful!");
|
|
71
|
+
console.log(JSON.stringify(result, null, 2));
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.error("ā Enhancement failed:", e);
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log("Usage:");
|
|
79
|
+
console.log(" amalfa enhance --batch [--limit <n>]");
|
|
80
|
+
console.log(" amalfa enhance --doc <id>");
|
|
81
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
7
|
+
// Registry is now in src/config, relative to src/cli is ../config
|
|
8
|
+
const REGISTRY_PATH = join(__dirname, "../config/scripts-registry.json");
|
|
9
|
+
|
|
10
|
+
interface ScriptEntry {
|
|
11
|
+
path: string;
|
|
12
|
+
command: string;
|
|
13
|
+
description: string;
|
|
14
|
+
category: string;
|
|
15
|
+
type: "user" | "dev";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function loadRegistry(): ScriptEntry[] {
|
|
19
|
+
if (!existsSync(REGISTRY_PATH)) {
|
|
20
|
+
console.error(`ā Registry not found at: ${REGISTRY_PATH}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
return JSON.parse(readFileSync(REGISTRY_PATH, "utf-8"));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function printScripts() {
|
|
27
|
+
const scripts = loadRegistry();
|
|
28
|
+
const grouped: Record<string, ScriptEntry[]> = {};
|
|
29
|
+
|
|
30
|
+
// Detect environment
|
|
31
|
+
// If "scripts" folder exists in root, we are likely in the repo (Dev Mode)
|
|
32
|
+
// Logic: __dirname is src/cli/. Root is ../..
|
|
33
|
+
const rootDir = join(__dirname, "../../");
|
|
34
|
+
const scriptsDir = join(rootDir, "scripts");
|
|
35
|
+
const isDevMode = existsSync(scriptsDir);
|
|
36
|
+
|
|
37
|
+
// Group by category, filtering if needed
|
|
38
|
+
for (const s of scripts) {
|
|
39
|
+
if (!isDevMode && s.type === "dev") continue;
|
|
40
|
+
|
|
41
|
+
if (!grouped[s.category]) grouped[s.category] = [];
|
|
42
|
+
grouped[s.category]?.push(s);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log("\nš AMALFA Command Registry\n");
|
|
46
|
+
if (isDevMode) {
|
|
47
|
+
console.log("š ļø Development Mode Detected (showing all repo scripts)\n");
|
|
48
|
+
} else {
|
|
49
|
+
console.log("š¦ Production Mode (showing user commands)\n");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const categories = Object.keys(grouped).sort();
|
|
53
|
+
|
|
54
|
+
for (const cat of categories) {
|
|
55
|
+
console.log(`[${cat.toUpperCase()}]`);
|
|
56
|
+
const catScripts = grouped[cat];
|
|
57
|
+
if (catScripts) {
|
|
58
|
+
for (const script of catScripts) {
|
|
59
|
+
console.log(` $ ${script.command}`);
|
|
60
|
+
console.log(` ${script.description}`);
|
|
61
|
+
console.log("");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
printScripts();
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// import { Command } from "commander";
|
|
2
|
+
import { createInterface } from "readline";
|
|
3
|
+
import { DaemonManager } from "../utils/DaemonManager";
|
|
4
|
+
|
|
5
|
+
export async function chatLoop() {
|
|
6
|
+
const manager = new DaemonManager();
|
|
7
|
+
const status = await manager.checkSonarAgent();
|
|
8
|
+
|
|
9
|
+
if (!status.running) {
|
|
10
|
+
console.log(
|
|
11
|
+
"ā Sonar Agent is not running. Start it with: amalfa sonar start",
|
|
12
|
+
);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const BASE_URL = `http://localhost:${status.port}`;
|
|
17
|
+
let sessionId: string | undefined;
|
|
18
|
+
|
|
19
|
+
console.log(`š¬ AMALFA Corpus Assistant (${status.activeModel || "Sonar"})`);
|
|
20
|
+
console.log(" Type 'exit' or 'quit' to leave.\n");
|
|
21
|
+
|
|
22
|
+
const rl = createInterface({
|
|
23
|
+
input: process.stdin,
|
|
24
|
+
output: process.stdout,
|
|
25
|
+
prompt: "You > ",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
rl.prompt();
|
|
29
|
+
|
|
30
|
+
rl.on("line", async (line) => {
|
|
31
|
+
const input = line.trim();
|
|
32
|
+
if (["exit", "quit"].includes(input.toLowerCase())) {
|
|
33
|
+
rl.close();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!input) {
|
|
38
|
+
rl.prompt();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let timer: Timer | undefined;
|
|
43
|
+
try {
|
|
44
|
+
const start = Date.now();
|
|
45
|
+
process.stdout.write("š¤ Thinking... (0s)");
|
|
46
|
+
|
|
47
|
+
timer = setInterval(() => {
|
|
48
|
+
const elapsed = Math.floor((Date.now() - start) / 1000);
|
|
49
|
+
process.stdout.clearLine(0);
|
|
50
|
+
process.stdout.cursorTo(0);
|
|
51
|
+
process.stdout.write(`š¤ Thinking... (${elapsed}s)`);
|
|
52
|
+
}, 1000);
|
|
53
|
+
|
|
54
|
+
const res = await fetch(`${BASE_URL}/chat`, {
|
|
55
|
+
method: "POST",
|
|
56
|
+
headers: { "Content-Type": "application/json" },
|
|
57
|
+
body: JSON.stringify({ message: input, sessionId }),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
clearInterval(timer);
|
|
61
|
+
|
|
62
|
+
if (!res.ok) {
|
|
63
|
+
throw new Error(`API Error: ${res.statusText}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const data = (await res.json()) as {
|
|
67
|
+
message: { content: string };
|
|
68
|
+
sessionId: string;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Update session ID if new
|
|
72
|
+
if (!sessionId) {
|
|
73
|
+
sessionId = data.sessionId;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Clear "Thinking..." line
|
|
77
|
+
process.stdout.clearLine(0);
|
|
78
|
+
process.stdout.cursorTo(0);
|
|
79
|
+
|
|
80
|
+
console.log(`Sonar > ${data.message.content}\n`);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
clearInterval(timer);
|
|
83
|
+
process.stdout.clearLine(0);
|
|
84
|
+
process.stdout.cursorTo(0);
|
|
85
|
+
console.error(
|
|
86
|
+
`ā Error: ${e instanceof Error ? e.message : String(e)}\n`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
rl.prompt();
|
|
91
|
+
}).on("close", () => {
|
|
92
|
+
console.log("\nš Goodbye!");
|
|
93
|
+
process.exit(0);
|
|
94
|
+
});
|
|
95
|
+
}
|