thingd-cli 0.11.0 → 0.12.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 +20 -9
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +164 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +249 -8
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +7 -1
- package/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +17 -16
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +13 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -132,14 +132,22 @@ thingd mcp-http --path ./thingd.db --driver native --port 8757 --auth-token chan
|
|
|
132
132
|
|
|
133
133
|
### Command Reference
|
|
134
134
|
|
|
135
|
-
**
|
|
135
|
+
**System & Diagnostics**
|
|
136
136
|
```bash
|
|
137
|
-
thingd
|
|
137
|
+
thingd doctor # Run Node, native binding, and remote connectivity diagnostics
|
|
138
|
+
thingd metrics # Get total database counts (objects, events, activeJobs, deadJobs)
|
|
138
139
|
thingd status # Check cluster health (requires --url)
|
|
139
140
|
thingd tools # List available MCP tools (requires --url)
|
|
140
|
-
thingd
|
|
141
|
-
thingd
|
|
142
|
-
|
|
141
|
+
thingd bench rust --smoke # Run Rust SQLite engine smoke benchmarks
|
|
142
|
+
thingd bench rust --count 500 # Run Rust SQLite engine benchmarks with specific run count
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Discovery & Collections**
|
|
146
|
+
```bash
|
|
147
|
+
thingd collections list # List all active collections
|
|
148
|
+
thingd streams list # List all active stream names (alias for events streams)
|
|
149
|
+
thingd events streams # List all active event streams
|
|
150
|
+
thingd queues list-all # List all active queue names
|
|
143
151
|
```
|
|
144
152
|
|
|
145
153
|
**Search**
|
|
@@ -149,10 +157,11 @@ thingd search "my query" [--collection <name>] [--limit <n>]
|
|
|
149
157
|
|
|
150
158
|
**Objects**
|
|
151
159
|
```bash
|
|
152
|
-
thingd objects
|
|
153
|
-
thingd objects put decisions
|
|
154
|
-
thingd objects
|
|
155
|
-
thingd objects
|
|
160
|
+
thingd objects list decisions # List all objects inside a collection
|
|
161
|
+
thingd objects put decisions core --text "msg" # Put object with plain text content
|
|
162
|
+
thingd objects put decisions core --data '{"a":1}' # Put object with arbitrary JSON data
|
|
163
|
+
thingd objects get decisions core # Fetch a specific object by ID
|
|
164
|
+
thingd objects delete decisions core # Delete an object by ID
|
|
156
165
|
```
|
|
157
166
|
|
|
158
167
|
**Events**
|
|
@@ -163,6 +172,7 @@ thingd events list project:thingd
|
|
|
163
172
|
|
|
164
173
|
**Queues**
|
|
165
174
|
```bash
|
|
175
|
+
thingd queues stats embed # View queue statistics (ready, leased, dead jobs)
|
|
166
176
|
thingd queues push embed --payload '{"object":"docs/readme"}'
|
|
167
177
|
thingd queues claim embed
|
|
168
178
|
thingd queues ack embed <jobId>
|
|
@@ -172,3 +182,4 @@ thingd queues dead embed
|
|
|
172
182
|
```
|
|
173
183
|
|
|
174
184
|
|
|
185
|
+
|
package/dist/doctor.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,YAAY,CAAC;AAEhE,wBAAsB,SAAS,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAsMlE"}
|
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
import { resolveConnection } from "./index.js";
|
|
7
|
+
export async function runDoctor(context) {
|
|
8
|
+
context.stderr.write(`\n${pc.bold("thingd doctor")}\n`);
|
|
9
|
+
context.stderr.write(`${pc.dim("Running system diagnostics and connectivity tests...")}\n\n`);
|
|
10
|
+
let healthy = true;
|
|
11
|
+
// 1. Check Node version
|
|
12
|
+
const nodeVersion = process.version;
|
|
13
|
+
const majorVersion = Number.parseInt(nodeVersion.slice(1).split(".")[0] ?? "0", 10);
|
|
14
|
+
if (majorVersion >= 20) {
|
|
15
|
+
context.stderr.write(` ${pc.green("✓")} Node version: ${pc.cyan(nodeVersion)} (OK)\n`);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
healthy = false;
|
|
19
|
+
context.stderr.write(` ${pc.red("×")} Node version: ${pc.yellow(nodeVersion)} (Requires >= v20.x)\n`);
|
|
20
|
+
}
|
|
21
|
+
// 2. Resolve connection options
|
|
22
|
+
const connection = resolveConnection(context);
|
|
23
|
+
// 3. Native Driver Checks
|
|
24
|
+
if (connection.driver === "native") {
|
|
25
|
+
const customPath = process.env.THINGD_NATIVE_PATH;
|
|
26
|
+
if (customPath) {
|
|
27
|
+
if (existsSync(customPath)) {
|
|
28
|
+
try {
|
|
29
|
+
const require = createRequire(import.meta.url);
|
|
30
|
+
const binding = require(customPath);
|
|
31
|
+
if (binding?.NativeThingStore) {
|
|
32
|
+
context.stderr.write(` ${pc.green("✓")} Native Binding: ${pc.cyan("Loaded via THINGD_NATIVE_PATH")} (${pc.dim(customPath)})\n`);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
healthy = false;
|
|
36
|
+
context.stderr.write(` ${pc.red("×")} Native Binding: ${pc.yellow("Loaded but missing NativeThingStore export")} (${pc.dim(customPath)})\n`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
healthy = false;
|
|
41
|
+
context.stderr.write(` ${pc.red("×")} Native Binding: ${pc.yellow(`Failed to load: ${error instanceof Error ? error.message : String(error)}`)} (${pc.dim(customPath)})\n`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
healthy = false;
|
|
46
|
+
context.stderr.write(` ${pc.red("×")} Native Binding: ${pc.yellow("File does not exist")} at THINGD_NATIVE_PATH="${pc.dim(customPath)}"\n`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// Auto-detect sibling binary
|
|
51
|
+
let detectedPath = null;
|
|
52
|
+
try {
|
|
53
|
+
const scriptPath = process.argv[1];
|
|
54
|
+
if (scriptPath) {
|
|
55
|
+
const cliDir = join(resolve(scriptPath), "..", "..");
|
|
56
|
+
const candidates = [
|
|
57
|
+
join(cliDir, "node_modules", "thingd-native", "dist", "thingd_native.node"),
|
|
58
|
+
join(cliDir, "..", "thingd-native", "dist", "thingd_native.node"),
|
|
59
|
+
join(homedir(), "Space/Programming/personal/thingd/packages/thingd-native/dist/thingd_native.node"),
|
|
60
|
+
join(homedir(), "Space/Programming/personal/thingd-cloud/packages/thingd-native/dist/thingd_native.node"),
|
|
61
|
+
];
|
|
62
|
+
for (const candidate of candidates) {
|
|
63
|
+
if (existsSync(candidate)) {
|
|
64
|
+
detectedPath = candidate;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Ignore
|
|
72
|
+
}
|
|
73
|
+
if (detectedPath) {
|
|
74
|
+
try {
|
|
75
|
+
const require = createRequire(import.meta.url);
|
|
76
|
+
const binding = require(detectedPath);
|
|
77
|
+
if (binding?.NativeThingStore) {
|
|
78
|
+
context.stderr.write(` ${pc.green("✓")} Native Binding: ${pc.cyan("Auto-detected and loaded successfully")} (${pc.dim(detectedPath)})\n`);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
healthy = false;
|
|
82
|
+
context.stderr.write(` ${pc.red("×")} Native Binding: ${pc.yellow("Auto-detected but missing NativeThingStore export")} (${pc.dim(detectedPath)})\n`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
healthy = false;
|
|
87
|
+
context.stderr.write(` ${pc.red("×")} Native Binding: ${pc.yellow(`Failed to load auto-detected binding: ${error instanceof Error ? error.message : String(error)}`)} (${pc.dim(detectedPath)})\n`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
healthy = false;
|
|
92
|
+
context.stderr.write(` ${pc.red("×")} Native Binding: ${pc.yellow('Not found. Run "pnpm --filter thingd-native build" or configure THINGD_NATIVE_PATH.')}\n`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
context.stderr.write(` ${pc.dim("○")} Native Binding: Skipped (Using driver: "${connection.driver ?? "memory"}")\n`);
|
|
98
|
+
}
|
|
99
|
+
// 4. Remote Sidecar Reachability & Auth Checks
|
|
100
|
+
if (connection.cloud) {
|
|
101
|
+
const rawUrl = connection.path;
|
|
102
|
+
const isLocal = rawUrl.includes("localhost") || rawUrl.includes("127.0.0.1");
|
|
103
|
+
if (!connection.authToken && !isLocal) {
|
|
104
|
+
context.stderr.write(` ${pc.yellow("⚠")} Auth Token: ${pc.yellow("Missing THINGD_AUTH_TOKEN for remote server (might fail)")}\n`);
|
|
105
|
+
}
|
|
106
|
+
else if (connection.authToken) {
|
|
107
|
+
context.stderr.write(` ${pc.green("✓")} Auth Token: ${pc.cyan("Configured")}\n`);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
context.stderr.write(` ${pc.green("✓")} Auth Token: ${pc.dim("Not required for local sidecar")}\n`);
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const normalizeUrl = (val) => val.startsWith("thingd://") ? `http://${val.slice("thingd://".length)}` : val;
|
|
114
|
+
const targetUrl = new URL(normalizeUrl(rawUrl));
|
|
115
|
+
if (targetUrl.pathname === "/mcp" || targetUrl.pathname === "") {
|
|
116
|
+
targetUrl.pathname = "/healthz";
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
targetUrl.pathname = `${targetUrl.pathname.replace(/\/mcp$/, "")}/healthz`;
|
|
120
|
+
}
|
|
121
|
+
context.stderr.write(` ${pc.dim("○")} Connectivity: Checking reachability to ${pc.cyan(targetUrl.toString())}...\n`);
|
|
122
|
+
const controller = new AbortController();
|
|
123
|
+
const timeoutId = setTimeout(() => controller.abort(), 3000);
|
|
124
|
+
const headers = {};
|
|
125
|
+
if (connection.authToken) {
|
|
126
|
+
headers.Authorization = `Bearer ${connection.authToken}`;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const response = await fetch(targetUrl, {
|
|
130
|
+
signal: controller.signal,
|
|
131
|
+
headers,
|
|
132
|
+
});
|
|
133
|
+
clearTimeout(timeoutId);
|
|
134
|
+
if (response.ok) {
|
|
135
|
+
context.stderr.write(` ${pc.green("✓")} Connectivity: ${pc.cyan("Connected successfully!")} (${pc.dim(`HTTP ${response.status}`)})\n`);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
healthy = false;
|
|
139
|
+
context.stderr.write(` ${pc.red("×")} Connectivity: ${pc.yellow(`Server responded with non-2xx status`)} (${pc.dim(`HTTP ${response.status}`)})\n`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (fetchError) {
|
|
143
|
+
clearTimeout(timeoutId);
|
|
144
|
+
healthy = false;
|
|
145
|
+
context.stderr.write(` ${pc.red("×")} Connectivity: ${pc.yellow(`Failed to connect. Connection refused or timed out.`)} (${pc.dim(fetchError instanceof Error ? fetchError.message : String(fetchError))})\n`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (urlError) {
|
|
149
|
+
healthy = false;
|
|
150
|
+
context.stderr.write(` ${pc.red("×")} Connectivity: ${pc.yellow(`Invalid URL structure: ${urlError instanceof Error ? urlError.message : String(urlError)}`)}\n`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
context.stderr.write(` ${pc.green("✓")} Connectivity: ${pc.cyan("Local SQLite Store")} (${pc.dim(connection.path)})\n`);
|
|
155
|
+
}
|
|
156
|
+
// Final Summary Report
|
|
157
|
+
context.stderr.write("\n");
|
|
158
|
+
if (healthy) {
|
|
159
|
+
context.stderr.write(` ${pc.bold(pc.green("Diagnosis: Everything looks healthy!"))}\n\n`);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
context.stderr.write(` ${pc.bold(pc.yellow("Diagnosis: Some items require attention (see errors above)."))}\n\n`);
|
|
163
|
+
}
|
|
164
|
+
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AASA,OAAO,EAOL,MAAM,EACN,KAAK,YAAY,EAClB,MAAM,QAAQ,CAAC;AAKhB,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAEjD,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,CAAC;AAEF,KAAK,UAAU,GAAG;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AA0DF,wBAAsB,MAAM,CAC1B,IAAI,WAAwB,EAC5B,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAiEjB;AA8jBD,wBAAsB,MAAM,CAC1B,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAcf;AA4BD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,GAAG,iBAAiB,CA2BxE"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,8 @@ import { resolve } from "node:path";
|
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
6
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
7
|
+
import Table from "cli-table3";
|
|
8
|
+
import pc from "picocolors";
|
|
7
9
|
import { ThingD, } from "thingd";
|
|
8
10
|
import { runInteractiveCli } from "./interactive.js";
|
|
9
11
|
import { runMcp } from "./mcp.js";
|
|
@@ -16,24 +18,30 @@ Usage:
|
|
|
16
18
|
thingd status [--url <url>]
|
|
17
19
|
thingd tools --url <url>
|
|
18
20
|
thingd install [--raw] [--claude] [--cursor]
|
|
21
|
+
thingd doctor
|
|
19
22
|
thingd mcp [--path <path>] [--driver <driver>]
|
|
20
23
|
thingd mcp-http [--path <path>] [--driver <driver>] [--host <host>] [--port <port>] [--auth-token <tok>] [--allow-unauthenticated]
|
|
21
24
|
thingd search <query> [--collection <name>] [--limit <n>]
|
|
25
|
+
thingd objects list <collection>
|
|
22
26
|
thingd objects get <collection> <id>
|
|
23
27
|
thingd objects put <collection> <id> --text <text>
|
|
24
28
|
thingd objects put <collection> <id> --data '{"field":"value"}'
|
|
25
29
|
thingd objects delete <collection> <id>
|
|
30
|
+
thingd events streams
|
|
26
31
|
thingd events append <stream> <type> [--text <text>] [--data '{"field":"value"}']
|
|
27
32
|
thingd events list [stream] [--limit <n>]
|
|
28
33
|
thingd collections list
|
|
29
34
|
thingd streams list
|
|
30
35
|
thingd queues list-all
|
|
36
|
+
thingd queues stats <queue>
|
|
31
37
|
thingd queues push <queue> --payload '{"key":"value"}'
|
|
32
38
|
thingd queues claim <queue> [--lease-ms <ms>]
|
|
33
39
|
thingd queues ack <queue> <jobId>
|
|
34
40
|
thingd queues nack <queue> <jobId> [--error <message>] [--delay-ms <ms>]
|
|
35
41
|
thingd queues list <queue> [--limit <n>]
|
|
36
42
|
thingd queues dead <queue> [--limit <n>]
|
|
43
|
+
thingd bench rust --smoke
|
|
44
|
+
thingd bench rust --count <n>
|
|
37
45
|
thingd metrics
|
|
38
46
|
|
|
39
47
|
Options:
|
|
@@ -54,6 +62,7 @@ const BOOLEAN_FLAGS = new Set([
|
|
|
54
62
|
"raw",
|
|
55
63
|
"claude",
|
|
56
64
|
"cursor",
|
|
65
|
+
"smoke",
|
|
57
66
|
]);
|
|
58
67
|
export async function runCli(args = process.argv.slice(2), options = {}) {
|
|
59
68
|
// Auto-detect and set THINGD_NATIVE_PATH if not already set, to allow global execution
|
|
@@ -136,6 +145,15 @@ async function runCommand(context) {
|
|
|
136
145
|
await runInstall(context);
|
|
137
146
|
return;
|
|
138
147
|
}
|
|
148
|
+
if (command === "doctor") {
|
|
149
|
+
const { runDoctor } = await import("./doctor.js");
|
|
150
|
+
await runDoctor(context);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (command === "bench") {
|
|
154
|
+
await runBench(context);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
139
157
|
if (command === "objects") {
|
|
140
158
|
await runObjects(context);
|
|
141
159
|
return;
|
|
@@ -162,6 +180,63 @@ async function runCommand(context) {
|
|
|
162
180
|
}
|
|
163
181
|
throw new Error(`Unknown command: ${command}`);
|
|
164
182
|
}
|
|
183
|
+
async function runBench(context) {
|
|
184
|
+
const target = requiredToken(context.parsed, 1, "benchmark target (rust)");
|
|
185
|
+
if (target !== "rust") {
|
|
186
|
+
throw new Error(`Unsupported benchmark target: ${target}`);
|
|
187
|
+
}
|
|
188
|
+
const isSmoke = hasFlag(context.parsed, "smoke");
|
|
189
|
+
const countStr = stringFlag(context.parsed, "count");
|
|
190
|
+
const count = countStr ? Number.parseInt(countStr, 10) : isSmoke ? 100 : undefined;
|
|
191
|
+
if (count === undefined) {
|
|
192
|
+
throw new Error("bench rust requires --smoke or --count <n>");
|
|
193
|
+
}
|
|
194
|
+
if (Number.isNaN(count) || count <= 0) {
|
|
195
|
+
throw new Error("--count must be a positive integer");
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
const { execSync } = await import("node:child_process");
|
|
199
|
+
try {
|
|
200
|
+
execSync("cargo --version", { stdio: "ignore" });
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
throw new Error("Rust toolchain (cargo) is not installed or not in the PATH. Cannot run Rust benchmarks.");
|
|
204
|
+
}
|
|
205
|
+
context.stderr.write(`\n${pc.bold("Running Rust storage benchmark")} (Count: ${pc.cyan(count)})...\n\n`);
|
|
206
|
+
const { spawn } = await import("node:child_process");
|
|
207
|
+
const child = spawn("cargo", [
|
|
208
|
+
"run",
|
|
209
|
+
"--release",
|
|
210
|
+
"-p",
|
|
211
|
+
"thingd-core",
|
|
212
|
+
"--example",
|
|
213
|
+
"storage_bench",
|
|
214
|
+
"--features",
|
|
215
|
+
"sqlite",
|
|
216
|
+
"--",
|
|
217
|
+
String(count),
|
|
218
|
+
], {
|
|
219
|
+
stdio: "inherit",
|
|
220
|
+
cwd: resolve(resolveCliPath(), "../../../.."),
|
|
221
|
+
});
|
|
222
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
223
|
+
child.on("close", (code) => {
|
|
224
|
+
if (code === 0) {
|
|
225
|
+
resolvePromise();
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
rejectPromise(new Error(`Benchmark failed with exit code: ${code}`));
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
child.on("error", (error) => {
|
|
232
|
+
rejectPromise(error);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
throw new Error(`Failed to run benchmark: ${err instanceof Error ? err.message : String(err)}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
165
240
|
async function runStatus(context) {
|
|
166
241
|
const connection = resolveConnection(context);
|
|
167
242
|
if (!connection.cloud) {
|
|
@@ -233,8 +308,32 @@ async function runSearch(context) {
|
|
|
233
308
|
async function runObjects(context) {
|
|
234
309
|
const action = requiredToken(context.parsed, 1, "objects action");
|
|
235
310
|
const collection = requiredToken(context.parsed, 2, "collection");
|
|
236
|
-
const id = requiredToken(context.parsed, 3, "object id");
|
|
237
311
|
await withDb(context, async (db) => {
|
|
312
|
+
if (action === "list") {
|
|
313
|
+
const objects = await db.listObjects(collection);
|
|
314
|
+
if (context.pretty) {
|
|
315
|
+
const table = new Table({
|
|
316
|
+
head: ["ID", "Version", "Created At", "Updated At", "Data"],
|
|
317
|
+
style: { head: ["green"] },
|
|
318
|
+
});
|
|
319
|
+
for (const obj of objects) {
|
|
320
|
+
const { id, collection: _, createdAt, updatedAt, version, ...data } = obj;
|
|
321
|
+
table.push([
|
|
322
|
+
id,
|
|
323
|
+
String(version),
|
|
324
|
+
createdAt ? new Date(createdAt).toLocaleString() : "",
|
|
325
|
+
updatedAt ? new Date(updatedAt).toLocaleString() : "",
|
|
326
|
+
JSON.stringify(data),
|
|
327
|
+
]);
|
|
328
|
+
}
|
|
329
|
+
context.stdout.write(`${table.toString()}\n`);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
writeJson(context.stdout, objects, false);
|
|
333
|
+
}
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const id = requiredToken(context.parsed, 3, "object id");
|
|
238
337
|
if (action === "get") {
|
|
239
338
|
writeJson(context.stdout, await db.get(collection, id), context.pretty);
|
|
240
339
|
return;
|
|
@@ -254,10 +353,47 @@ async function runObjects(context) {
|
|
|
254
353
|
async function runEvents(context) {
|
|
255
354
|
const action = requiredToken(context.parsed, 1, "events action");
|
|
256
355
|
await withDb(context, async (db) => {
|
|
356
|
+
if (action === "streams") {
|
|
357
|
+
const streams = await db.listStreams();
|
|
358
|
+
if (context.pretty) {
|
|
359
|
+
const table = new Table({
|
|
360
|
+
head: ["Stream Name"],
|
|
361
|
+
style: { head: ["green"] },
|
|
362
|
+
});
|
|
363
|
+
for (const str of streams) {
|
|
364
|
+
table.push([str]);
|
|
365
|
+
}
|
|
366
|
+
context.stdout.write(`${table.toString()}\n`);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
writeJson(context.stdout, streams, false);
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
257
373
|
if (action === "list") {
|
|
258
374
|
const stream = optionalToken(context.parsed, 2);
|
|
259
|
-
const events = await db.events.list(stream);
|
|
260
|
-
|
|
375
|
+
const events = limitItems(await db.events.list(stream), optionalInt(context.parsed, "limit"));
|
|
376
|
+
if (context.pretty) {
|
|
377
|
+
const table = new Table({
|
|
378
|
+
head: ["Event ID", "Stream", "Event Type", "Created At", "Text", "Data"],
|
|
379
|
+
style: { head: ["green"] },
|
|
380
|
+
});
|
|
381
|
+
for (const ev of events) {
|
|
382
|
+
const { id, stream: evStream, type, createdAt, text, ...data } = ev;
|
|
383
|
+
table.push([
|
|
384
|
+
id,
|
|
385
|
+
evStream,
|
|
386
|
+
type,
|
|
387
|
+
createdAt ? new Date(createdAt).toLocaleString() : "",
|
|
388
|
+
text ?? "",
|
|
389
|
+
JSON.stringify(data),
|
|
390
|
+
]);
|
|
391
|
+
}
|
|
392
|
+
context.stdout.write(`${table.toString()}\n`);
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
writeJson(context.stdout, events, false);
|
|
396
|
+
}
|
|
261
397
|
return;
|
|
262
398
|
}
|
|
263
399
|
if (action === "append") {
|
|
@@ -274,7 +410,20 @@ async function runCollections(context) {
|
|
|
274
410
|
const action = requiredToken(context.parsed, 1, "collections action");
|
|
275
411
|
await withDb(context, async (db) => {
|
|
276
412
|
if (action === "list") {
|
|
277
|
-
|
|
413
|
+
const collections = await db.listCollections();
|
|
414
|
+
if (context.pretty) {
|
|
415
|
+
const table = new Table({
|
|
416
|
+
head: ["Collection Name"],
|
|
417
|
+
style: { head: ["green"] },
|
|
418
|
+
});
|
|
419
|
+
for (const col of collections) {
|
|
420
|
+
table.push([col]);
|
|
421
|
+
}
|
|
422
|
+
context.stdout.write(`${table.toString()}\n`);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
writeJson(context.stdout, collections, false);
|
|
426
|
+
}
|
|
278
427
|
return;
|
|
279
428
|
}
|
|
280
429
|
throw new Error(`Unknown collections action: ${action}`);
|
|
@@ -284,7 +433,20 @@ async function runStreams(context) {
|
|
|
284
433
|
const action = requiredToken(context.parsed, 1, "streams action");
|
|
285
434
|
await withDb(context, async (db) => {
|
|
286
435
|
if (action === "list") {
|
|
287
|
-
|
|
436
|
+
const streams = await db.listStreams();
|
|
437
|
+
if (context.pretty) {
|
|
438
|
+
const table = new Table({
|
|
439
|
+
head: ["Stream Name"],
|
|
440
|
+
style: { head: ["green"] },
|
|
441
|
+
});
|
|
442
|
+
for (const str of streams) {
|
|
443
|
+
table.push([str]);
|
|
444
|
+
}
|
|
445
|
+
context.stdout.write(`${table.toString()}\n`);
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
writeJson(context.stdout, streams, false);
|
|
449
|
+
}
|
|
288
450
|
return;
|
|
289
451
|
}
|
|
290
452
|
throw new Error(`Unknown streams action: ${action}`);
|
|
@@ -310,11 +472,50 @@ async function runQueues(context) {
|
|
|
310
472
|
const action = requiredToken(context.parsed, 1, "queues action");
|
|
311
473
|
await withDb(context, async (db) => {
|
|
312
474
|
if (action === "list-all") {
|
|
313
|
-
|
|
475
|
+
const queues = await db.listQueues();
|
|
476
|
+
if (context.pretty) {
|
|
477
|
+
const table = new Table({
|
|
478
|
+
head: ["Queue Name"],
|
|
479
|
+
style: { head: ["green"] },
|
|
480
|
+
});
|
|
481
|
+
for (const q of queues) {
|
|
482
|
+
table.push([q]);
|
|
483
|
+
}
|
|
484
|
+
context.stdout.write(`${table.toString()}\n`);
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
writeJson(context.stdout, queues, false);
|
|
488
|
+
}
|
|
314
489
|
return;
|
|
315
490
|
}
|
|
316
491
|
const queueName = requiredToken(context.parsed, 2, "queue");
|
|
317
492
|
const queue = db.queue(queueName);
|
|
493
|
+
if (action === "stats") {
|
|
494
|
+
const [activeJobs, deadJobs] = await Promise.all([queue.list(), queue.dead()]);
|
|
495
|
+
const totalActive = activeJobs.length;
|
|
496
|
+
const totalDead = deadJobs.length;
|
|
497
|
+
const leasedJobs = activeJobs.filter((job) => job.status === "leased");
|
|
498
|
+
const readyJobs = activeJobs.filter((job) => job.status === "ready");
|
|
499
|
+
const stats = {
|
|
500
|
+
queue: queueName,
|
|
501
|
+
totalActive,
|
|
502
|
+
ready: readyJobs.length,
|
|
503
|
+
leased: leasedJobs.length,
|
|
504
|
+
dead: totalDead,
|
|
505
|
+
};
|
|
506
|
+
if (context.pretty) {
|
|
507
|
+
const table = new Table({
|
|
508
|
+
head: ["Stat Metric", "Value"],
|
|
509
|
+
style: { head: ["green"] },
|
|
510
|
+
});
|
|
511
|
+
table.push(["Queue Name", queueName], ["Ready Jobs", String(readyJobs.length)], ["Leased Jobs", String(leasedJobs.length)], ["Dead Jobs", String(totalDead)], ["Total Active", String(totalActive)]);
|
|
512
|
+
context.stdout.write(`${table.toString()}\n`);
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
writeJson(context.stdout, stats, false);
|
|
516
|
+
}
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
318
519
|
if (action === "push") {
|
|
319
520
|
const payload = parseJsonRecord(requiredFlag(context.parsed, "payload"));
|
|
320
521
|
const options = {
|
|
@@ -345,11 +546,51 @@ async function runQueues(context) {
|
|
|
345
546
|
return;
|
|
346
547
|
}
|
|
347
548
|
if (action === "list") {
|
|
348
|
-
|
|
549
|
+
const jobs = limitItems(await queue.list(), optionalInt(context.parsed, "limit"));
|
|
550
|
+
if (context.pretty) {
|
|
551
|
+
const table = new Table({
|
|
552
|
+
head: ["Job ID", "Status", "Attempts", "Max Attempts", "Available At", "Payload"],
|
|
553
|
+
style: { head: ["green"] },
|
|
554
|
+
});
|
|
555
|
+
for (const job of jobs) {
|
|
556
|
+
table.push([
|
|
557
|
+
job.id,
|
|
558
|
+
job.status,
|
|
559
|
+
String(job.attempts),
|
|
560
|
+
String(job.maxAttempts),
|
|
561
|
+
job.availableAt ? new Date(job.availableAt).toLocaleString() : "",
|
|
562
|
+
JSON.stringify(job.payload),
|
|
563
|
+
]);
|
|
564
|
+
}
|
|
565
|
+
context.stdout.write(`${table.toString()}\n`);
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
writeJson(context.stdout, jobs, false);
|
|
569
|
+
}
|
|
349
570
|
return;
|
|
350
571
|
}
|
|
351
572
|
if (action === "dead") {
|
|
352
|
-
|
|
573
|
+
const jobs = limitItems(await queue.dead(), optionalInt(context.parsed, "limit"));
|
|
574
|
+
if (context.pretty) {
|
|
575
|
+
const table = new Table({
|
|
576
|
+
head: ["Job ID", "Attempts", "Max Attempts", "Dead At", "Last Error", "Payload"],
|
|
577
|
+
style: { head: ["green"] },
|
|
578
|
+
});
|
|
579
|
+
for (const job of jobs) {
|
|
580
|
+
table.push([
|
|
581
|
+
job.id,
|
|
582
|
+
String(job.attempts),
|
|
583
|
+
String(job.maxAttempts),
|
|
584
|
+
job.deadAt ? new Date(job.deadAt).toLocaleString() : "",
|
|
585
|
+
job.lastError ?? "",
|
|
586
|
+
JSON.stringify(job.payload),
|
|
587
|
+
]);
|
|
588
|
+
}
|
|
589
|
+
context.stdout.write(`${table.toString()}\n`);
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
writeJson(context.stdout, jobs, false);
|
|
593
|
+
}
|
|
353
594
|
return;
|
|
354
595
|
}
|
|
355
596
|
throw new Error(`Unknown queues action: ${action}`);
|
package/dist/install.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAwB7C,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAwB7C,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAyInE"}
|
package/dist/install.js
CHANGED
|
@@ -127,9 +127,15 @@ export async function runInstall(context) {
|
|
|
127
127
|
};
|
|
128
128
|
context.stdout.write(`${JSON.stringify(fullConfig, null, 2)}\n`);
|
|
129
129
|
}
|
|
130
|
-
if (
|
|
130
|
+
if (choice === "1") {
|
|
131
131
|
context.stderr.write(`\n Restart Claude Desktop to activate. Cursor activates immediately after pasting.\n\n`);
|
|
132
132
|
}
|
|
133
|
+
else if (choice === "2") {
|
|
134
|
+
context.stderr.write(`\n Restart Claude Desktop to activate.\n\n`);
|
|
135
|
+
}
|
|
136
|
+
else if (choice === "3") {
|
|
137
|
+
context.stderr.write(`\n Cursor activates immediately after pasting.\n\n`);
|
|
138
|
+
}
|
|
133
139
|
}
|
|
134
140
|
function findGlobalBinPath() {
|
|
135
141
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"AA4jDA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA4BvD"}
|
package/dist/interactive.js
CHANGED
|
@@ -229,11 +229,12 @@ async function fetchResources() {
|
|
|
229
229
|
db.listStreams(),
|
|
230
230
|
db.listQueues?.() ?? Promise.resolve([]),
|
|
231
231
|
]);
|
|
232
|
-
totalObjects = isNaN(objCount) || objCount === 0 ? totalObjects : objCount;
|
|
233
|
-
totalEventsCount = isNaN(evtCount) || evtCount === 0 ? totalEventsCount : evtCount;
|
|
232
|
+
totalObjects = Number.isNaN(objCount) || objCount === 0 ? totalObjects : objCount;
|
|
233
|
+
totalEventsCount = Number.isNaN(evtCount) || evtCount === 0 ? totalEventsCount : evtCount;
|
|
234
234
|
totalActiveJobsCount =
|
|
235
|
-
isNaN(activeCount) || activeCount === 0 ? totalActiveJobsCount : activeCount;
|
|
236
|
-
totalDeadJobsCount =
|
|
235
|
+
Number.isNaN(activeCount) || activeCount === 0 ? totalActiveJobsCount : activeCount;
|
|
236
|
+
totalDeadJobsCount =
|
|
237
|
+
Number.isNaN(deadCount) || deadCount === 0 ? totalDeadJobsCount : deadCount;
|
|
237
238
|
collections = nativeCollections.length > 0 ? nativeCollections : ["decisions", "load-test"];
|
|
238
239
|
streams =
|
|
239
240
|
nativeStreams.length > 0
|
|
@@ -712,9 +713,9 @@ function draw() {
|
|
|
712
713
|
else {
|
|
713
714
|
titleStr = ` thingd ${pc.dim("|")} ${driver.toUpperCase()} ${pc.dim("|")} ${dbPath} `;
|
|
714
715
|
}
|
|
715
|
-
buf += pc.inverse(padToWidth(titleStr, W))
|
|
716
|
+
buf += `${pc.inverse(padToWidth(titleStr, W))}\n`;
|
|
716
717
|
// Separator
|
|
717
|
-
buf += pc.dim("─".repeat(sideW)
|
|
718
|
+
buf += `${pc.dim(`${"─".repeat(sideW)}─┬─${"─".repeat(viewW)}`)}\n`;
|
|
718
719
|
// Build Form Lines if active
|
|
719
720
|
if (formState?.active) {
|
|
720
721
|
viewerLines = [`${pc.bgCyan(pc.black(` ${formState.title} `))}`, ""];
|
|
@@ -783,15 +784,15 @@ function draw() {
|
|
|
783
784
|
// Viewer
|
|
784
785
|
const vLine = viewerLines[r + viewerScroll] ?? "";
|
|
785
786
|
const right = fitToWidth(vLine, viewW, false);
|
|
786
|
-
buf += left + pc.dim(" │ ") + right
|
|
787
|
+
buf += `${left + pc.dim(" │ ") + right}\n`;
|
|
787
788
|
}
|
|
788
789
|
// Separator
|
|
789
|
-
buf += pc.dim("─".repeat(sideW)
|
|
790
|
+
buf += `${pc.dim(`${"─".repeat(sideW)}─┴─${"─".repeat(viewW)}`)}\n`;
|
|
790
791
|
// Footer
|
|
791
792
|
let help;
|
|
792
793
|
if (formState?.active) {
|
|
793
794
|
const hasOptions = formState.fields[formState.activeIndex]?.options;
|
|
794
|
-
help = ` ${pc.dim("↑↓")} focus ${hasOptions ? pc.dim("←→")
|
|
795
|
+
help = ` ${pc.dim("↑↓")} focus ${hasOptions ? `${pc.dim("←→")} select ` : ""}${pc.dim("enter")} submit ${pc.dim("ctrl+e")} editor ${pc.dim("esc")} cancel `;
|
|
795
796
|
}
|
|
796
797
|
else if (!connected) {
|
|
797
798
|
help = ` ${pc.dim("↑↓")} nav ${pc.dim("enter")} connect ${pc.dim("q")} quit `;
|
|
@@ -897,7 +898,7 @@ async function launchEditor(f) {
|
|
|
897
898
|
const newContent = fs.readFileSync(tmpFile, "utf-8");
|
|
898
899
|
f.value = newContent.trim();
|
|
899
900
|
}
|
|
900
|
-
catch (
|
|
901
|
+
catch (_e) { }
|
|
901
902
|
if (process.stdin.isTTY)
|
|
902
903
|
process.stdin.setRawMode(true);
|
|
903
904
|
process.stdin.on("keypress", keypressHandler);
|
|
@@ -928,7 +929,7 @@ function parsePayload(str) {
|
|
|
928
929
|
v = true;
|
|
929
930
|
else if (v === "false")
|
|
930
931
|
v = false;
|
|
931
|
-
else if (!isNaN(Number(v)))
|
|
932
|
+
else if (!Number.isNaN(Number(v)))
|
|
932
933
|
v = Number(v);
|
|
933
934
|
}
|
|
934
935
|
obj[k] = v;
|
|
@@ -985,8 +986,8 @@ async function handleCreate(selected) {
|
|
|
985
986
|
try {
|
|
986
987
|
id = crypto.randomUUID();
|
|
987
988
|
}
|
|
988
|
-
catch (
|
|
989
|
-
id =
|
|
989
|
+
catch (_e) {
|
|
990
|
+
id = `obj_${Date.now().toString(36)}${Math.random().toString(36).substring(2)}`;
|
|
990
991
|
}
|
|
991
992
|
}
|
|
992
993
|
const data = parsePayload(vals.payload || "");
|
|
@@ -1109,7 +1110,7 @@ async function handleSearch() {
|
|
|
1109
1110
|
const options = {};
|
|
1110
1111
|
if (limitStr) {
|
|
1111
1112
|
const limit = parseInt(limitStr, 10);
|
|
1112
|
-
if (!isNaN(limit))
|
|
1113
|
+
if (!Number.isNaN(limit))
|
|
1113
1114
|
options.limit = limit;
|
|
1114
1115
|
}
|
|
1115
1116
|
const results = await db.search(query, options);
|
|
@@ -1147,7 +1148,7 @@ async function handleInfo() {
|
|
|
1147
1148
|
const u = new URL(p, urlObj.toString());
|
|
1148
1149
|
const headers = {};
|
|
1149
1150
|
if (authToken)
|
|
1150
|
-
headers
|
|
1151
|
+
headers.Authorization = `Bearer ${authToken}`;
|
|
1151
1152
|
const res = await fetch(u, { headers });
|
|
1152
1153
|
if (!res.ok)
|
|
1153
1154
|
throw new Error(`HTTP ${res.status}`);
|
|
@@ -1232,7 +1233,7 @@ function setupKeypress() {
|
|
|
1232
1233
|
}
|
|
1233
1234
|
else if (key.name === "left" || key.name === "right") {
|
|
1234
1235
|
const f = formState.fields[formState.activeIndex];
|
|
1235
|
-
if (f
|
|
1236
|
+
if (f?.options && f.options.length > 0) {
|
|
1236
1237
|
const currentIndex = f.options.indexOf(f.value);
|
|
1237
1238
|
let nextIndex = key.name === "right" ? currentIndex + 1 : currentIndex - 1;
|
|
1238
1239
|
if (nextIndex < 0)
|
package/dist/mcp/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAC;AAWpB,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;CACvC,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,0BAA+B,GACvC,IAAI,
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAC;AAWpB,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;CACvC,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,0BAA+B,GACvC,IAAI,CA+XN"}
|
package/dist/mcp/tools.js
CHANGED
|
@@ -305,6 +305,19 @@ export function registerThingdTools(server, db, options = {}) {
|
|
|
305
305
|
openWorldHint: false,
|
|
306
306
|
},
|
|
307
307
|
}, async ({ queue }) => jsonResult(await db.queue(queue).dead()));
|
|
308
|
+
server.registerTool("thing_objects_list", {
|
|
309
|
+
title: "List Objects",
|
|
310
|
+
description: "List all thingd objects in a collection.",
|
|
311
|
+
inputSchema: {
|
|
312
|
+
collection: z.string().min(1),
|
|
313
|
+
},
|
|
314
|
+
annotations: {
|
|
315
|
+
readOnlyHint: true,
|
|
316
|
+
destructiveHint: false,
|
|
317
|
+
idempotentHint: true,
|
|
318
|
+
openWorldHint: false,
|
|
319
|
+
},
|
|
320
|
+
}, async ({ collection }) => jsonResult(await db.listObjects(collection)));
|
|
308
321
|
}
|
|
309
322
|
function auditMetadata(actor, source) {
|
|
310
323
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thingd-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Command-line interface, Interactive TUI Dashboard, and MCP server for thingd.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Sayan Mohsin",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"cli-table3": "^0.6.5",
|
|
34
34
|
"picocolors": "^1.1.1",
|
|
35
35
|
"zod": "^4.4.3",
|
|
36
|
-
"thingd": "0.
|
|
36
|
+
"thingd": "0.12.0"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
39
|
"node": ">=20"
|