spect8-mcp 0.1.4 → 0.2.1

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.
@@ -8,18 +8,18 @@ export interface QueuedRow {
8
8
  attempts: number;
9
9
  }
10
10
  /**
11
- * Durable, append-only FIFO backed by SQLite. Used when the ingest endpoint is
12
- * unreachable so no captured event is lost across crashes or network outages.
13
- *
14
- * Rows are keyed by auto-incremented id to preserve ordering; `dequeue()`
15
- * returns the oldest unsent rows and callers call `ack()` after a successful
16
- * POST. On retry storms we also track `attempts` so pathological rows can be
17
- * drained manually by an operator.
11
+ * Durable, pure-JS FIFO buffer. Used when the ingest endpoint is
12
+ * unreachable so no captured event is lost. Backed by a simple JSON file
13
+ * to avoid native dependencies like SQLite which break on Windows/Node 24.
18
14
  */
19
15
  export declare class OfflineQueue {
16
+ private readonly dbPath;
20
17
  private readonly logger;
21
- private readonly db;
18
+ private rows;
19
+ private nextId;
22
20
  constructor(dbPath: string, logger: Logger);
21
+ private load;
22
+ private save;
23
23
  enqueueMany(kind: QueueKind, payloads: string[]): void;
24
24
  dequeue(kind: QueueKind, limit: number): QueuedRow[];
25
25
  ack(ids: number[]): void;
@@ -1 +1 @@
1
- {"version":3,"file":"offline_queue.d.ts","sourceRoot":"","sources":["../../src/transport/offline_queue.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,aAAa,CAAC;AAEhD,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,qBAAa,YAAY;IAGK,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;gBAE3B,MAAM,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM;IAmB3D,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAgBtD,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE;IASpD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI;IAQxB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI;IAUzB,IAAI,IAAI,MAAM;IAOd,KAAK,IAAI,IAAI;CAGd"}
1
+ {"version":3,"file":"offline_queue.d.ts","sourceRoot":"","sources":["../../src/transport/offline_queue.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,aAAa,CAAC;AAEhD,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IAIX,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAU,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHpE,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,MAAM,CAAa;gBAEE,MAAM,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM;IAI5E,OAAO,CAAC,IAAI;IAqBZ,OAAO,CAAC,IAAI;IAYZ,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAetD,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE;IAMpD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI;IAOxB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI;IAWzB,IAAI,IAAI,MAAM;IAId,KAAK,IAAI,IAAI;CAGd"}
@@ -1,83 +1,95 @@
1
- import Database from "better-sqlite3";
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
2
2
  import { dirname } from "node:path";
3
- import { mkdirSync, existsSync } from "node:fs";
4
3
  /**
5
- * Durable, append-only FIFO backed by SQLite. Used when the ingest endpoint is
6
- * unreachable so no captured event is lost across crashes or network outages.
7
- *
8
- * Rows are keyed by auto-incremented id to preserve ordering; `dequeue()`
9
- * returns the oldest unsent rows and callers call `ack()` after a successful
10
- * POST. On retry storms we also track `attempts` so pathological rows can be
11
- * drained manually by an operator.
4
+ * Durable, pure-JS FIFO buffer. Used when the ingest endpoint is
5
+ * unreachable so no captured event is lost. Backed by a simple JSON file
6
+ * to avoid native dependencies like SQLite which break on Windows/Node 24.
12
7
  */
13
8
  export class OfflineQueue {
9
+ dbPath;
14
10
  logger;
15
- db;
11
+ rows = [];
12
+ nextId = 1;
16
13
  constructor(dbPath, logger) {
14
+ this.dbPath = dbPath;
17
15
  this.logger = logger;
18
- if (!existsSync(dirname(dbPath))) {
19
- mkdirSync(dirname(dbPath), { recursive: true });
16
+ this.load();
17
+ }
18
+ load() {
19
+ if (!existsSync(this.dbPath)) {
20
+ if (!existsSync(dirname(this.dbPath))) {
21
+ mkdirSync(dirname(this.dbPath), { recursive: true });
22
+ }
23
+ this.save();
24
+ return;
25
+ }
26
+ try {
27
+ const raw = readFileSync(this.dbPath, "utf8");
28
+ const data = JSON.parse(raw);
29
+ this.rows = data.rows || [];
30
+ this.nextId = data.nextId || 1;
31
+ }
32
+ catch (err) {
33
+ this.logger.error("Failed to load offline queue", err.message);
34
+ this.rows = [];
35
+ this.nextId = 1;
36
+ }
37
+ }
38
+ save() {
39
+ try {
40
+ const data = {
41
+ rows: this.rows,
42
+ nextId: this.nextId
43
+ };
44
+ writeFileSync(this.dbPath, JSON.stringify(data, null, 2), "utf8");
45
+ }
46
+ catch (err) {
47
+ this.logger.error("Failed to save offline queue", err.message);
20
48
  }
21
- this.db = new Database(dbPath);
22
- this.db.pragma("journal_mode = WAL");
23
- this.db.pragma("synchronous = NORMAL");
24
- this.db.exec(`
25
- CREATE TABLE IF NOT EXISTS spect8_queue (
26
- id INTEGER PRIMARY KEY AUTOINCREMENT,
27
- kind TEXT NOT NULL,
28
- payload TEXT NOT NULL,
29
- created_at_ms INTEGER NOT NULL,
30
- attempts INTEGER NOT NULL DEFAULT 0
31
- );
32
- CREATE INDEX IF NOT EXISTS ix_spect8_queue_kind ON spect8_queue(kind, id);
33
- `);
34
49
  }
35
50
  enqueueMany(kind, payloads) {
36
51
  if (payloads.length === 0)
37
52
  return;
38
- const stmt = this.db.prepare("INSERT INTO spect8_queue(kind, payload, created_at_ms) VALUES (?, ?, ?)");
39
53
  const now = Date.now();
40
- const tx = this.db.transaction((rows) => {
41
- for (const p of rows)
42
- stmt.run(kind, p, now);
43
- });
44
- try {
45
- tx(payloads);
46
- }
47
- catch (err) {
48
- this.logger.error("offline queue enqueue failed", err.message);
54
+ for (const p of payloads) {
55
+ this.rows.push({
56
+ id: this.nextId++,
57
+ kind,
58
+ payload: p,
59
+ created_at_ms: now,
60
+ attempts: 0
61
+ });
49
62
  }
63
+ this.save();
50
64
  }
51
65
  dequeue(kind, limit) {
52
- const rows = this.db
53
- .prepare("SELECT id, kind, payload, created_at_ms, attempts FROM spect8_queue WHERE kind = ? ORDER BY id LIMIT ?")
54
- .all(kind, limit);
55
- return rows;
66
+ return this.rows
67
+ .filter(r => r.kind === kind)
68
+ .slice(0, limit);
56
69
  }
57
70
  ack(ids) {
58
71
  if (ids.length === 0)
59
72
  return;
60
- const placeholders = ids.map(() => "?").join(",");
61
- this.db
62
- .prepare(`DELETE FROM spect8_queue WHERE id IN (${placeholders})`)
63
- .run(...ids);
73
+ const set = new Set(ids);
74
+ this.rows = this.rows.filter(r => !set.has(r.id));
75
+ this.save();
64
76
  }
65
77
  bump(ids) {
66
78
  if (ids.length === 0)
67
79
  return;
68
- const placeholders = ids.map(() => "?").join(",");
69
- this.db
70
- .prepare(`UPDATE spect8_queue SET attempts = attempts + 1 WHERE id IN (${placeholders})`)
71
- .run(...ids);
80
+ const set = new Set(ids);
81
+ for (const r of this.rows) {
82
+ if (set.has(r.id)) {
83
+ r.attempts++;
84
+ }
85
+ }
86
+ this.save();
72
87
  }
73
88
  size() {
74
- const row = this.db
75
- .prepare("SELECT COUNT(*) AS n FROM spect8_queue")
76
- .get();
77
- return row.n;
89
+ return this.rows.length;
78
90
  }
79
91
  close() {
80
- this.db.close();
92
+ this.save();
81
93
  }
82
94
  }
83
95
  //# sourceMappingURL=offline_queue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"offline_queue.js","sourceRoot":"","sources":["../../src/transport/offline_queue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"offline_queue.js","sourceRoot":"","sources":["../../src/transport/offline_queue.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spect8-mcp",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "description": "Spect8 local MCP server: captures tool events + token usage from Cursor and Claude Code, posts HMAC-signed batches to the Spect8 ingest API.",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",
@@ -12,9 +12,11 @@
12
12
  "files": [
13
13
  "dist",
14
14
  "examples",
15
+ "scripts",
15
16
  "README.md"
16
17
  ],
17
18
  "scripts": {
19
+ "preinstall": "node scripts/preinstall.js",
18
20
  "build": "tsc -p tsconfig.json",
19
21
  "dev": "tsc -w -p tsconfig.json",
20
22
  "start": "node dist/server.js",
@@ -25,13 +27,11 @@
25
27
  },
26
28
  "dependencies": {
27
29
  "@modelcontextprotocol/sdk": "^1.0.0",
28
- "better-sqlite3": "^11.5.0",
29
30
  "chokidar": "^3.6.0",
30
31
  "undici": "^6.20.0",
31
32
  "zod": "^3.23.8"
32
33
  },
33
34
  "devDependencies": {
34
- "@types/better-sqlite3": "^7.6.11",
35
35
  "@types/node": "^22.0.0",
36
36
  "@typescript-eslint/eslint-plugin": "^7.18.0",
37
37
  "@typescript-eslint/parser": "^7.18.0",
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Pre-install check for Spect8 MCP.
5
+ * Ensures the environment is ready and provides clear, human-readable
6
+ * guidance if any common issues are detected.
7
+ */
8
+
9
+ const nodeVersion = process.versions.node;
10
+ const majorVersion = parseInt(nodeVersion.split('.')[0], 10);
11
+ const isWindows = process.platform === 'win32';
12
+
13
+ console.log('--- Spect8 Environment Check ---');
14
+
15
+ if (majorVersion < 20) {
16
+ console.error('❌ Error: Node.js 20 or higher is required.');
17
+ console.error(` Your current version is: ${nodeVersion}`);
18
+ console.error(' Please upgrade at: https://nodejs.org');
19
+ process.exit(1);
20
+ }
21
+
22
+ if (isWindows && majorVersion >= 24) {
23
+ console.warn('⚠️ Notice: You are using Node 24 on Windows.');
24
+ console.warn(' This version is very new. We have removed all native dependencies');
25
+ console.warn(' to ensure it works, but if you encounter issues, Node 22 LTS is recommended.');
26
+ }
27
+
28
+ console.log('✅ Environment compatible. Proceeding with installation...');
29
+ console.log('--------------------------------');