chron-mcp 0.1.12 → 0.1.13
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/.claude-plugin/plugin.json +4 -4
- package/README.md +5 -2
- package/dist/cli/index.js +17091 -0
- package/dist/index.js +68361 -84
- package/package.json +7 -4
- package/skills/chron.skill.md +3 -3
- package/dist/db/index.d.ts +0 -5
- package/dist/db/index.js +0 -91
- package/dist/db/schema.d.ts +0 -242
- package/dist/db/schema.js +0 -20
- package/dist/http/middleware.d.ts +0 -2
- package/dist/http/middleware.js +0 -13
- package/dist/http/server.d.ts +0 -3
- package/dist/http/server.js +0 -54
- package/dist/index.d.ts +0 -2
- package/dist/server.d.ts +0 -3
- package/dist/server.js +0 -42
- package/dist/setup.d.ts +0 -8
- package/dist/setup.js +0 -118
- package/dist/tools/messages.d.ts +0 -35
- package/dist/tools/messages.js +0 -95
- package/dist/tools/sessions.d.ts +0 -27
- package/dist/tools/sessions.js +0 -93
- package/dist/tools/verify.d.ts +0 -9
- package/dist/tools/verify.js +0 -49
- package/dist/utils/hash.d.ts +0 -1
- package/dist/utils/hash.js +0 -9
- package/dist/utils/time.d.ts +0 -1
- package/dist/utils/time.js +0 -19
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chron-mcp",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"mcpName": "io.github.
|
|
3
|
+
"version": "0.1.13",
|
|
4
|
+
"mcpName": "io.github.sirinivask/chron",
|
|
5
5
|
"description": "Audit-grade timestamped logs for every AI conversation",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"globals": true
|
|
16
16
|
},
|
|
17
17
|
"bin": {
|
|
18
|
-
"chron-mcp": "dist/index.js"
|
|
18
|
+
"chron-mcp": "dist/index.js",
|
|
19
|
+
"chron": "dist/cli/index.js"
|
|
19
20
|
},
|
|
20
21
|
"files": [
|
|
21
22
|
"dist",
|
|
@@ -28,7 +29,9 @@
|
|
|
28
29
|
"node": ">=18"
|
|
29
30
|
},
|
|
30
31
|
"scripts": {
|
|
31
|
-
"build": "
|
|
32
|
+
"build": "npx esbuild src/index.ts --bundle --format=cjs --outfile=dist/index.js --platform=node && chmod +x dist/index.js && npx esbuild src/cli/index.ts --bundle --format=cjs --outfile=dist/cli/index.js --platform=node && chmod +x dist/cli/index.js",
|
|
33
|
+
"build:cli": "npx esbuild src/cli/index.ts --bundle --format=cjs --outfile=dist/cli/index.js --platform=node && chmod +x dist/cli/index.js",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
32
35
|
"dev": "tsc --watch",
|
|
33
36
|
"start": "node dist/index.js",
|
|
34
37
|
"test": "vitest run",
|
package/skills/chron.skill.md
CHANGED
|
@@ -6,10 +6,10 @@ Chron creates an audit trail of every AI conversation. Every message is logged w
|
|
|
6
6
|
|
|
7
7
|
## Session Start
|
|
8
8
|
|
|
9
|
-
Call `
|
|
9
|
+
Call `init_session` with the session title and `ai_tool`. It creates or resumes a session and returns `session_id`, `message_count`, `first_message_at`, and recent `messages` in a single call — no separate `get_session_history` needed.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
> "Resuming session: [N messages] since [
|
|
11
|
+
If `message_count > 0`, display:
|
|
12
|
+
> "Resuming session: [N messages] since [first_message_at]"
|
|
13
13
|
|
|
14
14
|
## Every Exchange
|
|
15
15
|
|
package/dist/db/index.d.ts
DELETED
package/dist/db/index.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.initDb = initDb;
|
|
37
|
-
const client_1 = require("@libsql/client");
|
|
38
|
-
const libsql_1 = require("drizzle-orm/libsql");
|
|
39
|
-
const os_1 = require("os");
|
|
40
|
-
const fs_1 = require("fs");
|
|
41
|
-
const path_1 = require("path");
|
|
42
|
-
const schema = __importStar(require("./schema"));
|
|
43
|
-
function getDbPath() {
|
|
44
|
-
const envPath = process.env.CHRON_DB_PATH;
|
|
45
|
-
if (envPath)
|
|
46
|
-
return envPath;
|
|
47
|
-
return (0, path_1.join)((0, os_1.homedir)(), '.chron', 'chron.db');
|
|
48
|
-
}
|
|
49
|
-
const CREATE_SQL = [
|
|
50
|
-
`CREATE TABLE IF NOT EXISTS sessions (
|
|
51
|
-
id TEXT PRIMARY KEY,
|
|
52
|
-
title TEXT NOT NULL UNIQUE,
|
|
53
|
-
ai_tool TEXT,
|
|
54
|
-
created_at TEXT NOT NULL,
|
|
55
|
-
updated_at TEXT NOT NULL
|
|
56
|
-
)`,
|
|
57
|
-
`CREATE TABLE IF NOT EXISTS messages (
|
|
58
|
-
id TEXT PRIMARY KEY,
|
|
59
|
-
session_id TEXT NOT NULL,
|
|
60
|
-
role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
|
|
61
|
-
content TEXT NOT NULL,
|
|
62
|
-
created_at TEXT NOT NULL,
|
|
63
|
-
prev_hash TEXT,
|
|
64
|
-
content_hash TEXT,
|
|
65
|
-
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
66
|
-
)`,
|
|
67
|
-
`CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_title ON sessions(title)`,
|
|
68
|
-
`CREATE INDEX IF NOT EXISTS idx_messages_session_id ON messages(session_id)`,
|
|
69
|
-
];
|
|
70
|
-
async function initDb(dbPath) {
|
|
71
|
-
const path = dbPath ?? getDbPath();
|
|
72
|
-
if (!path.startsWith(':')) {
|
|
73
|
-
(0, fs_1.mkdirSync)((0, path_1.dirname)(path), { recursive: true });
|
|
74
|
-
}
|
|
75
|
-
const client = (0, client_1.createClient)({ url: path.startsWith(':') ? path : `file:${path}` });
|
|
76
|
-
await client.execute('PRAGMA journal_mode = WAL');
|
|
77
|
-
await client.execute('PRAGMA foreign_keys = ON');
|
|
78
|
-
for (const sql of CREATE_SQL) {
|
|
79
|
-
await client.execute(sql);
|
|
80
|
-
}
|
|
81
|
-
// Migrate existing messages tables that predate hash chaining columns
|
|
82
|
-
const tableInfo = await client.execute('PRAGMA table_info(messages)');
|
|
83
|
-
const columns = tableInfo.rows.map((r) => r[1]);
|
|
84
|
-
if (!columns.includes('prev_hash')) {
|
|
85
|
-
await client.execute('ALTER TABLE messages ADD COLUMN prev_hash TEXT');
|
|
86
|
-
}
|
|
87
|
-
if (!columns.includes('content_hash')) {
|
|
88
|
-
await client.execute('ALTER TABLE messages ADD COLUMN content_hash TEXT');
|
|
89
|
-
}
|
|
90
|
-
return (0, libsql_1.drizzle)(client, { schema });
|
|
91
|
-
}
|
package/dist/db/schema.d.ts
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
export declare const sessions: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
2
|
-
name: "sessions";
|
|
3
|
-
schema: undefined;
|
|
4
|
-
columns: {
|
|
5
|
-
id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
6
|
-
name: "id";
|
|
7
|
-
tableName: "sessions";
|
|
8
|
-
dataType: "string";
|
|
9
|
-
columnType: "SQLiteText";
|
|
10
|
-
data: string;
|
|
11
|
-
driverParam: string;
|
|
12
|
-
notNull: true;
|
|
13
|
-
hasDefault: false;
|
|
14
|
-
isPrimaryKey: true;
|
|
15
|
-
isAutoincrement: false;
|
|
16
|
-
hasRuntimeDefault: false;
|
|
17
|
-
enumValues: [string, ...string[]];
|
|
18
|
-
baseColumn: never;
|
|
19
|
-
identity: undefined;
|
|
20
|
-
generated: undefined;
|
|
21
|
-
}, {}, {
|
|
22
|
-
length: number | undefined;
|
|
23
|
-
}>;
|
|
24
|
-
title: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
25
|
-
name: "title";
|
|
26
|
-
tableName: "sessions";
|
|
27
|
-
dataType: "string";
|
|
28
|
-
columnType: "SQLiteText";
|
|
29
|
-
data: string;
|
|
30
|
-
driverParam: string;
|
|
31
|
-
notNull: true;
|
|
32
|
-
hasDefault: false;
|
|
33
|
-
isPrimaryKey: false;
|
|
34
|
-
isAutoincrement: false;
|
|
35
|
-
hasRuntimeDefault: false;
|
|
36
|
-
enumValues: [string, ...string[]];
|
|
37
|
-
baseColumn: never;
|
|
38
|
-
identity: undefined;
|
|
39
|
-
generated: undefined;
|
|
40
|
-
}, {}, {
|
|
41
|
-
length: number | undefined;
|
|
42
|
-
}>;
|
|
43
|
-
ai_tool: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
44
|
-
name: "ai_tool";
|
|
45
|
-
tableName: "sessions";
|
|
46
|
-
dataType: "string";
|
|
47
|
-
columnType: "SQLiteText";
|
|
48
|
-
data: string;
|
|
49
|
-
driverParam: string;
|
|
50
|
-
notNull: false;
|
|
51
|
-
hasDefault: false;
|
|
52
|
-
isPrimaryKey: false;
|
|
53
|
-
isAutoincrement: false;
|
|
54
|
-
hasRuntimeDefault: false;
|
|
55
|
-
enumValues: [string, ...string[]];
|
|
56
|
-
baseColumn: never;
|
|
57
|
-
identity: undefined;
|
|
58
|
-
generated: undefined;
|
|
59
|
-
}, {}, {
|
|
60
|
-
length: number | undefined;
|
|
61
|
-
}>;
|
|
62
|
-
created_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
63
|
-
name: "created_at";
|
|
64
|
-
tableName: "sessions";
|
|
65
|
-
dataType: "string";
|
|
66
|
-
columnType: "SQLiteText";
|
|
67
|
-
data: string;
|
|
68
|
-
driverParam: string;
|
|
69
|
-
notNull: true;
|
|
70
|
-
hasDefault: false;
|
|
71
|
-
isPrimaryKey: false;
|
|
72
|
-
isAutoincrement: false;
|
|
73
|
-
hasRuntimeDefault: false;
|
|
74
|
-
enumValues: [string, ...string[]];
|
|
75
|
-
baseColumn: never;
|
|
76
|
-
identity: undefined;
|
|
77
|
-
generated: undefined;
|
|
78
|
-
}, {}, {
|
|
79
|
-
length: number | undefined;
|
|
80
|
-
}>;
|
|
81
|
-
updated_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
82
|
-
name: "updated_at";
|
|
83
|
-
tableName: "sessions";
|
|
84
|
-
dataType: "string";
|
|
85
|
-
columnType: "SQLiteText";
|
|
86
|
-
data: string;
|
|
87
|
-
driverParam: string;
|
|
88
|
-
notNull: true;
|
|
89
|
-
hasDefault: false;
|
|
90
|
-
isPrimaryKey: false;
|
|
91
|
-
isAutoincrement: false;
|
|
92
|
-
hasRuntimeDefault: false;
|
|
93
|
-
enumValues: [string, ...string[]];
|
|
94
|
-
baseColumn: never;
|
|
95
|
-
identity: undefined;
|
|
96
|
-
generated: undefined;
|
|
97
|
-
}, {}, {
|
|
98
|
-
length: number | undefined;
|
|
99
|
-
}>;
|
|
100
|
-
};
|
|
101
|
-
dialect: "sqlite";
|
|
102
|
-
}>;
|
|
103
|
-
export declare const messages: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
104
|
-
name: "messages";
|
|
105
|
-
schema: undefined;
|
|
106
|
-
columns: {
|
|
107
|
-
id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
108
|
-
name: "id";
|
|
109
|
-
tableName: "messages";
|
|
110
|
-
dataType: "string";
|
|
111
|
-
columnType: "SQLiteText";
|
|
112
|
-
data: string;
|
|
113
|
-
driverParam: string;
|
|
114
|
-
notNull: true;
|
|
115
|
-
hasDefault: false;
|
|
116
|
-
isPrimaryKey: true;
|
|
117
|
-
isAutoincrement: false;
|
|
118
|
-
hasRuntimeDefault: false;
|
|
119
|
-
enumValues: [string, ...string[]];
|
|
120
|
-
baseColumn: never;
|
|
121
|
-
identity: undefined;
|
|
122
|
-
generated: undefined;
|
|
123
|
-
}, {}, {
|
|
124
|
-
length: number | undefined;
|
|
125
|
-
}>;
|
|
126
|
-
session_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
127
|
-
name: "session_id";
|
|
128
|
-
tableName: "messages";
|
|
129
|
-
dataType: "string";
|
|
130
|
-
columnType: "SQLiteText";
|
|
131
|
-
data: string;
|
|
132
|
-
driverParam: string;
|
|
133
|
-
notNull: true;
|
|
134
|
-
hasDefault: false;
|
|
135
|
-
isPrimaryKey: false;
|
|
136
|
-
isAutoincrement: false;
|
|
137
|
-
hasRuntimeDefault: false;
|
|
138
|
-
enumValues: [string, ...string[]];
|
|
139
|
-
baseColumn: never;
|
|
140
|
-
identity: undefined;
|
|
141
|
-
generated: undefined;
|
|
142
|
-
}, {}, {
|
|
143
|
-
length: number | undefined;
|
|
144
|
-
}>;
|
|
145
|
-
role: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
146
|
-
name: "role";
|
|
147
|
-
tableName: "messages";
|
|
148
|
-
dataType: "string";
|
|
149
|
-
columnType: "SQLiteText";
|
|
150
|
-
data: "user" | "assistant";
|
|
151
|
-
driverParam: string;
|
|
152
|
-
notNull: true;
|
|
153
|
-
hasDefault: false;
|
|
154
|
-
isPrimaryKey: false;
|
|
155
|
-
isAutoincrement: false;
|
|
156
|
-
hasRuntimeDefault: false;
|
|
157
|
-
enumValues: ["user", "assistant"];
|
|
158
|
-
baseColumn: never;
|
|
159
|
-
identity: undefined;
|
|
160
|
-
generated: undefined;
|
|
161
|
-
}, {}, {
|
|
162
|
-
length: number | undefined;
|
|
163
|
-
}>;
|
|
164
|
-
content: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
165
|
-
name: "content";
|
|
166
|
-
tableName: "messages";
|
|
167
|
-
dataType: "string";
|
|
168
|
-
columnType: "SQLiteText";
|
|
169
|
-
data: string;
|
|
170
|
-
driverParam: string;
|
|
171
|
-
notNull: true;
|
|
172
|
-
hasDefault: false;
|
|
173
|
-
isPrimaryKey: false;
|
|
174
|
-
isAutoincrement: false;
|
|
175
|
-
hasRuntimeDefault: false;
|
|
176
|
-
enumValues: [string, ...string[]];
|
|
177
|
-
baseColumn: never;
|
|
178
|
-
identity: undefined;
|
|
179
|
-
generated: undefined;
|
|
180
|
-
}, {}, {
|
|
181
|
-
length: number | undefined;
|
|
182
|
-
}>;
|
|
183
|
-
created_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
184
|
-
name: "created_at";
|
|
185
|
-
tableName: "messages";
|
|
186
|
-
dataType: "string";
|
|
187
|
-
columnType: "SQLiteText";
|
|
188
|
-
data: string;
|
|
189
|
-
driverParam: string;
|
|
190
|
-
notNull: true;
|
|
191
|
-
hasDefault: false;
|
|
192
|
-
isPrimaryKey: false;
|
|
193
|
-
isAutoincrement: false;
|
|
194
|
-
hasRuntimeDefault: false;
|
|
195
|
-
enumValues: [string, ...string[]];
|
|
196
|
-
baseColumn: never;
|
|
197
|
-
identity: undefined;
|
|
198
|
-
generated: undefined;
|
|
199
|
-
}, {}, {
|
|
200
|
-
length: number | undefined;
|
|
201
|
-
}>;
|
|
202
|
-
prev_hash: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
203
|
-
name: "prev_hash";
|
|
204
|
-
tableName: "messages";
|
|
205
|
-
dataType: "string";
|
|
206
|
-
columnType: "SQLiteText";
|
|
207
|
-
data: string;
|
|
208
|
-
driverParam: string;
|
|
209
|
-
notNull: false;
|
|
210
|
-
hasDefault: false;
|
|
211
|
-
isPrimaryKey: false;
|
|
212
|
-
isAutoincrement: false;
|
|
213
|
-
hasRuntimeDefault: false;
|
|
214
|
-
enumValues: [string, ...string[]];
|
|
215
|
-
baseColumn: never;
|
|
216
|
-
identity: undefined;
|
|
217
|
-
generated: undefined;
|
|
218
|
-
}, {}, {
|
|
219
|
-
length: number | undefined;
|
|
220
|
-
}>;
|
|
221
|
-
content_hash: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
222
|
-
name: "content_hash";
|
|
223
|
-
tableName: "messages";
|
|
224
|
-
dataType: "string";
|
|
225
|
-
columnType: "SQLiteText";
|
|
226
|
-
data: string;
|
|
227
|
-
driverParam: string;
|
|
228
|
-
notNull: false;
|
|
229
|
-
hasDefault: false;
|
|
230
|
-
isPrimaryKey: false;
|
|
231
|
-
isAutoincrement: false;
|
|
232
|
-
hasRuntimeDefault: false;
|
|
233
|
-
enumValues: [string, ...string[]];
|
|
234
|
-
baseColumn: never;
|
|
235
|
-
identity: undefined;
|
|
236
|
-
generated: undefined;
|
|
237
|
-
}, {}, {
|
|
238
|
-
length: number | undefined;
|
|
239
|
-
}>;
|
|
240
|
-
};
|
|
241
|
-
dialect: "sqlite";
|
|
242
|
-
}>;
|
package/dist/db/schema.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.messages = exports.sessions = void 0;
|
|
4
|
-
const sqlite_core_1 = require("drizzle-orm/sqlite-core");
|
|
5
|
-
exports.sessions = (0, sqlite_core_1.sqliteTable)('sessions', {
|
|
6
|
-
id: (0, sqlite_core_1.text)('id').primaryKey(),
|
|
7
|
-
title: (0, sqlite_core_1.text)('title').notNull().unique(),
|
|
8
|
-
ai_tool: (0, sqlite_core_1.text)('ai_tool'),
|
|
9
|
-
created_at: (0, sqlite_core_1.text)('created_at').notNull(),
|
|
10
|
-
updated_at: (0, sqlite_core_1.text)('updated_at').notNull(),
|
|
11
|
-
});
|
|
12
|
-
exports.messages = (0, sqlite_core_1.sqliteTable)('messages', {
|
|
13
|
-
id: (0, sqlite_core_1.text)('id').primaryKey(),
|
|
14
|
-
session_id: (0, sqlite_core_1.text)('session_id').notNull().references(() => exports.sessions.id),
|
|
15
|
-
role: (0, sqlite_core_1.text)('role', { enum: ['user', 'assistant'] }).notNull(),
|
|
16
|
-
content: (0, sqlite_core_1.text)('content').notNull(),
|
|
17
|
-
created_at: (0, sqlite_core_1.text)('created_at').notNull(),
|
|
18
|
-
prev_hash: (0, sqlite_core_1.text)('prev_hash'),
|
|
19
|
-
content_hash: (0, sqlite_core_1.text)('content_hash'),
|
|
20
|
-
});
|
package/dist/http/middleware.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.apiKeyMiddleware = apiKeyMiddleware;
|
|
4
|
-
function apiKeyMiddleware(apiKey) {
|
|
5
|
-
return (req, res, next) => {
|
|
6
|
-
const header = req.headers.authorization;
|
|
7
|
-
if (!header || header !== `Bearer ${apiKey}`) {
|
|
8
|
-
res.status(401).end();
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
next();
|
|
12
|
-
};
|
|
13
|
-
}
|
package/dist/http/server.d.ts
DELETED
package/dist/http/server.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.startHttpServer = startHttpServer;
|
|
7
|
-
const express_1 = __importDefault(require("express"));
|
|
8
|
-
const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js");
|
|
9
|
-
const middleware_1 = require("./middleware");
|
|
10
|
-
const server_1 = require("../server");
|
|
11
|
-
async function startHttpServer(server, db) {
|
|
12
|
-
const app = (0, express_1.default)();
|
|
13
|
-
app.use(express_1.default.json({ limit: '1mb' }));
|
|
14
|
-
const apiKey = process.env.CHRON_API_KEY;
|
|
15
|
-
if (apiKey) {
|
|
16
|
-
app.use((0, middleware_1.apiKeyMiddleware)(apiKey));
|
|
17
|
-
}
|
|
18
|
-
const transports = new Map();
|
|
19
|
-
app.get('/sse', async (req, res) => {
|
|
20
|
-
try {
|
|
21
|
-
const instanceServer = (0, server_1.createServer)(db);
|
|
22
|
-
const transport = new sse_js_1.SSEServerTransport('/messages', res);
|
|
23
|
-
transports.set(transport.sessionId, { transport, server: instanceServer });
|
|
24
|
-
transport.onclose = () => transports.delete(transport.sessionId);
|
|
25
|
-
await instanceServer.connect(transport);
|
|
26
|
-
}
|
|
27
|
-
catch (err) {
|
|
28
|
-
process.stderr.write(`SSE error: ${err}\n`);
|
|
29
|
-
if (!res.headersSent)
|
|
30
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
app.post('/messages', async (req, res) => {
|
|
34
|
-
try {
|
|
35
|
-
const sessionId = req.query.sessionId;
|
|
36
|
-
const entry = transports.get(sessionId);
|
|
37
|
-
if (!entry) {
|
|
38
|
-
res.status(404).json({ error: 'Session not found' });
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
await entry.transport.handlePostMessage(req, res);
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
process.stderr.write(`Messages error: ${err}\n`);
|
|
45
|
-
if (!res.headersSent)
|
|
46
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
const port = parseInt(process.env.PORT ?? '3001', 10);
|
|
50
|
-
if (isNaN(port))
|
|
51
|
-
throw new Error(`Invalid PORT value: ${process.env.PORT}`);
|
|
52
|
-
await new Promise(resolve => app.listen(port, resolve));
|
|
53
|
-
process.stderr.write(`Chron HTTP server listening on port ${port}\n`);
|
|
54
|
-
}
|
package/dist/index.d.ts
DELETED
package/dist/server.d.ts
DELETED
package/dist/server.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createServer = createServer;
|
|
4
|
-
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
5
|
-
const zod_1 = require("zod");
|
|
6
|
-
const sessions_1 = require("./tools/sessions");
|
|
7
|
-
const messages_1 = require("./tools/messages");
|
|
8
|
-
const verify_1 = require("./tools/verify");
|
|
9
|
-
const roleEnum = zod_1.z.enum(['user', 'assistant']);
|
|
10
|
-
function createServer(db) {
|
|
11
|
-
const server = new mcp_js_1.McpServer({
|
|
12
|
-
name: 'chron',
|
|
13
|
-
version: '0.1.7',
|
|
14
|
-
});
|
|
15
|
-
// @ts-expect-error — MCP SDK deep generic type instantiation limit hit; runtime types are correct
|
|
16
|
-
server.tool('start_session', 'Create a new audit session or resume an existing one by title. Call this at the start of every conversation.', {
|
|
17
|
-
title: zod_1.z.string().describe('Descriptive session title, e.g. "Contract review — 2026-05-08"'),
|
|
18
|
-
ai_tool: zod_1.z.string().optional().describe('AI tool name: "claude", "cursor", "windsurf", etc.'),
|
|
19
|
-
}, (0, sessions_1.startSession)(db));
|
|
20
|
-
server.tool('log_message', 'Record a single message (user or assistant) with the current local datetime and timezone offset. Call before responding for user messages, and before sending for assistant messages.', {
|
|
21
|
-
session_id: zod_1.z.string().describe('Session ID returned by start_session'),
|
|
22
|
-
role: roleEnum,
|
|
23
|
-
content: zod_1.z.string().describe('Full message text'),
|
|
24
|
-
}, (0, messages_1.logMessage)(db));
|
|
25
|
-
server.tool('log_exchange', 'Record a user+assistant exchange from historical or batch imports only. Do NOT use for live conversations — both timestamps are captured at the same instant with no real gap. For live sessions always call log_message twice: once for the user message, once for the assistant response.', {
|
|
26
|
-
session_id: zod_1.z.string().describe('Session ID returned by start_session'),
|
|
27
|
-
user_content: zod_1.z.string().describe('The exact user message'),
|
|
28
|
-
assistant_content: zod_1.z.string().describe('The exact assistant response'),
|
|
29
|
-
}, (0, messages_1.logExchange)(db));
|
|
30
|
-
// @ts-expect-error — MCP SDK deep generic type instantiation limit hit; runtime types are correct
|
|
31
|
-
server.tool('list_sessions', 'List all audit sessions ordered by most recently active. Returns id, title, ai_tool, message_count, created_at, updated_at.', {
|
|
32
|
-
limit: zod_1.z.number().int().positive().optional().describe('Return only the most recent N sessions'),
|
|
33
|
-
}, (0, sessions_1.listSessions)(db));
|
|
34
|
-
server.tool('get_session_history', 'Retrieve the full timestamped audit log for a session, oldest first.', {
|
|
35
|
-
session_id: zod_1.z.string(),
|
|
36
|
-
limit: zod_1.z.number().int().positive().optional().describe('Return only the most recent N messages'),
|
|
37
|
-
}, (0, sessions_1.getSessionHistory)(db));
|
|
38
|
-
server.tool('verify_session', 'Verify the tamper-evident hash chain for a session. Returns valid=true if no rows were edited after logging, or the first broken link if tampering is detected.', {
|
|
39
|
-
session_id: zod_1.z.string().describe('Session ID to verify'),
|
|
40
|
-
}, (0, verify_1.verifySession)(db));
|
|
41
|
-
return server;
|
|
42
|
-
}
|
package/dist/setup.d.ts
DELETED
package/dist/setup.js
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.runSetup = runSetup;
|
|
4
|
-
const fs_1 = require("fs");
|
|
5
|
-
const os_1 = require("os");
|
|
6
|
-
const path_1 = require("path");
|
|
7
|
-
const child_process_1 = require("child_process");
|
|
8
|
-
const CHRON_ENTRY = {
|
|
9
|
-
command: 'npx',
|
|
10
|
-
args: ['-y', 'chron-mcp'],
|
|
11
|
-
};
|
|
12
|
-
function configPath(...parts) {
|
|
13
|
-
return (0, path_1.join)((0, os_1.homedir)(), ...parts);
|
|
14
|
-
}
|
|
15
|
-
const TOOLS = [
|
|
16
|
-
{
|
|
17
|
-
name: 'Claude Desktop',
|
|
18
|
-
path: process.platform === 'win32'
|
|
19
|
-
? (0, path_1.join)(process.env.APPDATA ?? '', 'Claude', 'claude_desktop_config.json')
|
|
20
|
-
: configPath('Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: 'Cursor',
|
|
24
|
-
path: process.platform === 'win32'
|
|
25
|
-
? (0, path_1.join)(process.env.APPDATA ?? '', 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'mcp.json')
|
|
26
|
-
: configPath('.cursor', 'mcp.json'),
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: 'Windsurf',
|
|
30
|
-
path: configPath('.codeium', 'windsurf', 'mcp_config.json'),
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
name: 'Claude Code',
|
|
34
|
-
path: configPath('.claude', 'settings.json'),
|
|
35
|
-
},
|
|
36
|
-
];
|
|
37
|
-
function readJson(filePath) {
|
|
38
|
-
if (!(0, fs_1.existsSync)(filePath))
|
|
39
|
-
return {};
|
|
40
|
-
try {
|
|
41
|
-
return JSON.parse((0, fs_1.readFileSync)(filePath, 'utf8'));
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
return {};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function writeJson(filePath, data) {
|
|
48
|
-
(0, fs_1.mkdirSync)((0, path_1.dirname)(filePath), { recursive: true });
|
|
49
|
-
(0, fs_1.writeFileSync)(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
50
|
-
}
|
|
51
|
-
function configureTool(name, filePath) {
|
|
52
|
-
// Skip if the app isn't installed (config dir doesn't exist and it's not Claude Code)
|
|
53
|
-
const dir = (0, path_1.dirname)(filePath);
|
|
54
|
-
if (!(0, fs_1.existsSync)(dir) && name !== 'Claude Code') {
|
|
55
|
-
return { tool: name, status: 'skipped' };
|
|
56
|
-
}
|
|
57
|
-
try {
|
|
58
|
-
const config = readJson(filePath);
|
|
59
|
-
if (!config.mcpServers)
|
|
60
|
-
config.mcpServers = {};
|
|
61
|
-
if (config.mcpServers.chron) {
|
|
62
|
-
return { tool: name, status: 'already', path: filePath };
|
|
63
|
-
}
|
|
64
|
-
config.mcpServers.chron = CHRON_ENTRY;
|
|
65
|
-
writeJson(filePath, config);
|
|
66
|
-
return { tool: name, status: 'added', path: filePath };
|
|
67
|
-
}
|
|
68
|
-
catch (err) {
|
|
69
|
-
return { tool: name, status: 'error', error: err.message };
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function configureClaudeCode() {
|
|
73
|
-
// Try `claude mcp add` first — it's the official way
|
|
74
|
-
try {
|
|
75
|
-
(0, child_process_1.execSync)('claude mcp add chron -- npx -y chron-mcp', { stdio: 'pipe' });
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
// Fall back to writing settings.json directly
|
|
79
|
-
const result = configureTool('Claude Code', (0, path_1.join)((0, os_1.homedir)(), '.claude', 'settings.json'));
|
|
80
|
-
if (result.status === 'error')
|
|
81
|
-
return result;
|
|
82
|
-
}
|
|
83
|
-
// Also install the SessionStart hook so Claude auto-logs without manual tool calls
|
|
84
|
-
installClaudeCodeHook();
|
|
85
|
-
return { tool: 'Claude Code', status: 'added' };
|
|
86
|
-
}
|
|
87
|
-
function installClaudeCodeHook() {
|
|
88
|
-
// Copy skill file to ~/.chron/ so the hook can cat it
|
|
89
|
-
const skillSrc = (0, path_1.join)(__dirname, '..', 'skills', 'chron.skill.md');
|
|
90
|
-
const skillDst = (0, path_1.join)((0, os_1.homedir)(), '.chron', 'chron.skill.md');
|
|
91
|
-
if ((0, fs_1.existsSync)(skillSrc)) {
|
|
92
|
-
(0, fs_1.mkdirSync)((0, path_1.dirname)(skillDst), { recursive: true });
|
|
93
|
-
(0, fs_1.copyFileSync)(skillSrc, skillDst);
|
|
94
|
-
}
|
|
95
|
-
const settingsPath = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'settings.json');
|
|
96
|
-
const settings = readJson(settingsPath);
|
|
97
|
-
if (!settings.hooks)
|
|
98
|
-
settings.hooks = {};
|
|
99
|
-
if (!settings.hooks.SessionStart)
|
|
100
|
-
settings.hooks.SessionStart = [];
|
|
101
|
-
const alreadyInstalled = settings.hooks.SessionStart.some((h) => Array.isArray(h.hooks) && h.hooks.some((c) => c.command?.includes('chron')));
|
|
102
|
-
if (!alreadyInstalled) {
|
|
103
|
-
settings.hooks.SessionStart.push({
|
|
104
|
-
hooks: [{ type: 'command', command: 'cat ~/.chron/chron.skill.md' }],
|
|
105
|
-
});
|
|
106
|
-
writeJson(settingsPath, settings);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
async function runSetup() {
|
|
110
|
-
const results = [];
|
|
111
|
-
for (const tool of TOOLS) {
|
|
112
|
-
const result = tool.name === 'Claude Code'
|
|
113
|
-
? configureClaudeCode()
|
|
114
|
-
: configureTool(tool.name, tool.path);
|
|
115
|
-
results.push(result);
|
|
116
|
-
}
|
|
117
|
-
return results.filter(r => r.status !== 'skipped');
|
|
118
|
-
}
|