shabti 2.7.0 → 2.9.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 +14 -2
- package/package.json +1 -1
- package/src/commands/config.js +45 -1
- package/src/commands/health.js +104 -0
- package/src/index.js +2 -0
package/README.md
CHANGED
|
@@ -12,13 +12,24 @@ npm install -g shabti
|
|
|
12
12
|
|
|
13
13
|
### Prerequisites
|
|
14
14
|
|
|
15
|
-
Shabti requires a running Qdrant instance for vector storage
|
|
15
|
+
Shabti requires a running Qdrant instance for vector storage.
|
|
16
|
+
|
|
17
|
+
**Option 1: Docker**
|
|
16
18
|
|
|
17
19
|
```bash
|
|
18
20
|
docker run -d --name qdrant -p 6333:6333 -p 6334:6334 qdrant/qdrant
|
|
19
21
|
```
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
**Option 2: Native binary (no Docker)**
|
|
24
|
+
|
|
25
|
+
Download the binary for your platform from [Qdrant releases](https://github.com/qdrant/qdrant/releases) and run it directly.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Check for locally installed qdrant
|
|
29
|
+
shabti config setup --detect
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Verify the connection:**
|
|
22
33
|
|
|
23
34
|
```bash
|
|
24
35
|
shabti config setup --check
|
|
@@ -56,6 +67,7 @@ shabti status
|
|
|
56
67
|
| `snapshot` | Manage storage snapshots |
|
|
57
68
|
| `config` | Manage configuration |
|
|
58
69
|
| `gc` | Garbage collect expired entries |
|
|
70
|
+
| `health` | Run health checks on engine |
|
|
59
71
|
| `a2a` | Start A2A protocol server |
|
|
60
72
|
| `chat` | Interactive chat with OpenAI |
|
|
61
73
|
| `mcp-config` | Print MCP server configuration JSON |
|
package/package.json
CHANGED
package/src/commands/config.js
CHANGED
|
@@ -49,22 +49,66 @@ export function registerConfig(program) {
|
|
|
49
49
|
.command("setup")
|
|
50
50
|
.description("Show Qdrant setup instructions")
|
|
51
51
|
.option("--check", "Test connection to Qdrant")
|
|
52
|
+
.option("--detect", "Detect locally installed qdrant binary")
|
|
52
53
|
.action(async (opts) => {
|
|
53
54
|
const config = loadConfig();
|
|
54
55
|
heading("Qdrant Setup");
|
|
55
56
|
console.log();
|
|
56
57
|
console.log(" shabti requires a running Qdrant instance for vector storage.");
|
|
57
58
|
console.log();
|
|
58
|
-
|
|
59
|
+
|
|
60
|
+
// Option 1: Docker
|
|
61
|
+
console.log(chalk.cyan(" Option 1: Docker"));
|
|
59
62
|
console.log();
|
|
60
63
|
console.log(` docker run -d --name qdrant -p 6333:6333 -p 6334:6334 qdrant/qdrant`);
|
|
61
64
|
console.log();
|
|
65
|
+
|
|
66
|
+
// Option 2: Native binary
|
|
67
|
+
console.log(chalk.cyan(" Option 2: Native binary (no Docker required)"));
|
|
68
|
+
console.log();
|
|
69
|
+
console.log(
|
|
70
|
+
` Download from: ${chalk.underline("https://github.com/qdrant/qdrant/releases")}`,
|
|
71
|
+
);
|
|
72
|
+
console.log();
|
|
73
|
+
const plat = process.platform;
|
|
74
|
+
if (plat === "linux") {
|
|
75
|
+
console.log(" Linux:");
|
|
76
|
+
console.log(
|
|
77
|
+
" wget https://github.com/qdrant/qdrant/releases/latest/download/qdrant-x86_64-unknown-linux-gnu.tar.gz",
|
|
78
|
+
);
|
|
79
|
+
console.log(" tar xzf qdrant-x86_64-unknown-linux-gnu.tar.gz");
|
|
80
|
+
console.log(" ./qdrant");
|
|
81
|
+
} else if (plat === "darwin") {
|
|
82
|
+
console.log(" macOS:");
|
|
83
|
+
console.log(" Download the macOS binary from the releases page,");
|
|
84
|
+
console.log(" or build from source: cargo install qdrant");
|
|
85
|
+
} else if (plat === "win32") {
|
|
86
|
+
console.log(" Windows:");
|
|
87
|
+
console.log(" Download qdrant.exe from the releases page and run it.");
|
|
88
|
+
}
|
|
89
|
+
console.log();
|
|
90
|
+
|
|
62
91
|
console.log(` Current Qdrant URL: ${chalk.yellow(config.qdrant_url)}`);
|
|
63
92
|
console.log();
|
|
64
93
|
console.log(" To change the URL:");
|
|
65
94
|
console.log(` shabti config set qdrant_url ${chalk.dim("<url>")}`);
|
|
66
95
|
console.log();
|
|
67
96
|
|
|
97
|
+
if (opts.detect) {
|
|
98
|
+
const { execFileSync } = await import("node:child_process");
|
|
99
|
+
const cmd = plat === "win32" ? "where" : "which";
|
|
100
|
+
try {
|
|
101
|
+
const result = execFileSync(cmd, ["qdrant"], {
|
|
102
|
+
encoding: "utf8",
|
|
103
|
+
timeout: 5000,
|
|
104
|
+
}).trim();
|
|
105
|
+
success(`Qdrant binary found at: ${result}`);
|
|
106
|
+
} catch (_) {
|
|
107
|
+
console.log(chalk.yellow(" Qdrant binary not found in PATH."));
|
|
108
|
+
console.log(" Download it from https://github.com/qdrant/qdrant/releases");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
68
112
|
if (opts.check) {
|
|
69
113
|
const restUrl = config.qdrant_url.replace(":6334", ":6333");
|
|
70
114
|
try {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { createEngine, loadConfig } from "../core/engine.js";
|
|
3
|
+
import { heading, success, warn } from "../utils/style.js";
|
|
4
|
+
|
|
5
|
+
export function registerHealth(program) {
|
|
6
|
+
program
|
|
7
|
+
.command("health")
|
|
8
|
+
.description("Run health checks on the shabti engine")
|
|
9
|
+
.option("-j, --json", "Output as JSON")
|
|
10
|
+
.action(async (opts) => {
|
|
11
|
+
const checks = [];
|
|
12
|
+
|
|
13
|
+
// 1. Qdrant connectivity
|
|
14
|
+
const config = loadConfig();
|
|
15
|
+
const qdrantUrl = config.qdrant_url.replace(/:\d+$/, ":6333");
|
|
16
|
+
try {
|
|
17
|
+
const res = await fetch(`${qdrantUrl}/healthz`, {
|
|
18
|
+
signal: AbortSignal.timeout(3000),
|
|
19
|
+
});
|
|
20
|
+
checks.push({
|
|
21
|
+
name: "qdrant",
|
|
22
|
+
status: res.ok ? "ok" : "degraded",
|
|
23
|
+
message: res.ok ? "Reachable" : `HTTP ${res.status}`,
|
|
24
|
+
});
|
|
25
|
+
} catch (err) {
|
|
26
|
+
checks.push({
|
|
27
|
+
name: "qdrant",
|
|
28
|
+
status: "error",
|
|
29
|
+
message: err.message,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 2. Engine initialization
|
|
34
|
+
let engine = null;
|
|
35
|
+
try {
|
|
36
|
+
engine = createEngine();
|
|
37
|
+
const status = engine.status();
|
|
38
|
+
checks.push({
|
|
39
|
+
name: "engine",
|
|
40
|
+
status: "ok",
|
|
41
|
+
message: `${status.entryCount} entries, tier: ${status.tier}`,
|
|
42
|
+
});
|
|
43
|
+
} catch (err) {
|
|
44
|
+
checks.push({
|
|
45
|
+
name: "engine",
|
|
46
|
+
status: "error",
|
|
47
|
+
message: err.message,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 3. Embedding model
|
|
52
|
+
if (engine) {
|
|
53
|
+
try {
|
|
54
|
+
const modelId = engine.modelId();
|
|
55
|
+
checks.push({
|
|
56
|
+
name: "embedding",
|
|
57
|
+
status: "ok",
|
|
58
|
+
message: modelId,
|
|
59
|
+
});
|
|
60
|
+
} catch (err) {
|
|
61
|
+
checks.push({
|
|
62
|
+
name: "embedding",
|
|
63
|
+
status: "error",
|
|
64
|
+
message: err.message,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const allOk = checks.every((c) => c.status === "ok");
|
|
70
|
+
|
|
71
|
+
if (opts.json) {
|
|
72
|
+
console.log(JSON.stringify({ healthy: allOk, checks }, null, 2));
|
|
73
|
+
} else {
|
|
74
|
+
heading("Health Check");
|
|
75
|
+
console.log();
|
|
76
|
+
for (const check of checks) {
|
|
77
|
+
const icon =
|
|
78
|
+
check.status === "ok"
|
|
79
|
+
? chalk.green("✓")
|
|
80
|
+
: check.status === "degraded"
|
|
81
|
+
? chalk.yellow("⚠")
|
|
82
|
+
: chalk.red("✗");
|
|
83
|
+
console.log(` ${icon} ${chalk.cyan(check.name.padEnd(12))} ${check.message}`);
|
|
84
|
+
}
|
|
85
|
+
console.log();
|
|
86
|
+
if (allOk) {
|
|
87
|
+
success("All checks passed");
|
|
88
|
+
} else {
|
|
89
|
+
warn("Some checks failed");
|
|
90
|
+
}
|
|
91
|
+
console.log();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (engine) {
|
|
95
|
+
try {
|
|
96
|
+
await engine.shutdown();
|
|
97
|
+
} catch (_) {
|
|
98
|
+
// best-effort
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!allOk) process.exitCode = 1;
|
|
103
|
+
});
|
|
104
|
+
}
|
package/src/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { registerExport } from "./commands/export.js";
|
|
|
16
16
|
import { registerImport } from "./commands/import.js";
|
|
17
17
|
import { registerStore } from "./commands/store.js";
|
|
18
18
|
import { registerDelete } from "./commands/delete.js";
|
|
19
|
+
import { registerHealth } from "./commands/health.js";
|
|
19
20
|
|
|
20
21
|
const { version } = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
21
22
|
|
|
@@ -86,6 +87,7 @@ function buildProgram() {
|
|
|
86
87
|
registerImport(program);
|
|
87
88
|
registerStore(program);
|
|
88
89
|
registerDelete(program);
|
|
90
|
+
registerHealth(program);
|
|
89
91
|
|
|
90
92
|
program
|
|
91
93
|
.command("gc")
|