chron-mcp 0.1.19 → 0.1.20
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 +1 -1
- package/dist/cli/index.js +293 -23
- package/dist/index.js +134 -51
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1184,7 +1184,7 @@ var init_sql = __esm({
|
|
|
1184
1184
|
return new SQL([new StringChunk(str)]);
|
|
1185
1185
|
}
|
|
1186
1186
|
sql2.raw = raw;
|
|
1187
|
-
function
|
|
1187
|
+
function join6(chunks, separator) {
|
|
1188
1188
|
const result = [];
|
|
1189
1189
|
for (const [i, chunk] of chunks.entries()) {
|
|
1190
1190
|
if (i > 0 && separator !== void 0) {
|
|
@@ -1194,7 +1194,7 @@ var init_sql = __esm({
|
|
|
1194
1194
|
}
|
|
1195
1195
|
return new SQL(result);
|
|
1196
1196
|
}
|
|
1197
|
-
sql2.join =
|
|
1197
|
+
sql2.join = join6;
|
|
1198
1198
|
function identifier(value) {
|
|
1199
1199
|
return new Name(value);
|
|
1200
1200
|
}
|
|
@@ -2806,7 +2806,7 @@ var require_filesystem = __commonJS({
|
|
|
2806
2806
|
"use strict";
|
|
2807
2807
|
var fs = require("fs");
|
|
2808
2808
|
var LDD_PATH = "/usr/bin/ldd";
|
|
2809
|
-
var
|
|
2809
|
+
var readFileSync3 = (path) => fs.readFileSync(path, "utf-8");
|
|
2810
2810
|
var readFile = (path) => new Promise((resolve, reject) => {
|
|
2811
2811
|
fs.readFile(path, "utf-8", (err, data) => {
|
|
2812
2812
|
if (err) {
|
|
@@ -2818,7 +2818,7 @@ var require_filesystem = __commonJS({
|
|
|
2818
2818
|
});
|
|
2819
2819
|
module2.exports = {
|
|
2820
2820
|
LDD_PATH,
|
|
2821
|
-
readFileSync:
|
|
2821
|
+
readFileSync: readFileSync3,
|
|
2822
2822
|
readFile
|
|
2823
2823
|
};
|
|
2824
2824
|
}
|
|
@@ -2830,7 +2830,7 @@ var require_detect_libc = __commonJS({
|
|
|
2830
2830
|
"use strict";
|
|
2831
2831
|
var childProcess = require("child_process");
|
|
2832
2832
|
var { isLinux, getReport } = require_process();
|
|
2833
|
-
var { LDD_PATH, readFile, readFileSync:
|
|
2833
|
+
var { LDD_PATH, readFile, readFileSync: readFileSync3 } = require_filesystem();
|
|
2834
2834
|
var cachedFamilyFilesystem;
|
|
2835
2835
|
var cachedVersionFilesystem;
|
|
2836
2836
|
var command2 = "getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true";
|
|
@@ -2911,7 +2911,7 @@ var require_detect_libc = __commonJS({
|
|
|
2911
2911
|
}
|
|
2912
2912
|
cachedFamilyFilesystem = null;
|
|
2913
2913
|
try {
|
|
2914
|
-
const lddContent =
|
|
2914
|
+
const lddContent = readFileSync3(LDD_PATH);
|
|
2915
2915
|
cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
|
|
2916
2916
|
} catch (e) {
|
|
2917
2917
|
}
|
|
@@ -2968,7 +2968,7 @@ var require_detect_libc = __commonJS({
|
|
|
2968
2968
|
}
|
|
2969
2969
|
cachedVersionFilesystem = null;
|
|
2970
2970
|
try {
|
|
2971
|
-
const lddContent =
|
|
2971
|
+
const lddContent = readFileSync3(LDD_PATH);
|
|
2972
2972
|
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
|
|
2973
2973
|
if (versionMatch) {
|
|
2974
2974
|
cachedVersionFilesystem = versionMatch[1];
|
|
@@ -6162,7 +6162,7 @@ var require_websocket = __commonJS({
|
|
|
6162
6162
|
var http = require("http");
|
|
6163
6163
|
var net = require("net");
|
|
6164
6164
|
var tls = require("tls");
|
|
6165
|
-
var { randomBytes, createHash:
|
|
6165
|
+
var { randomBytes, createHash: createHash4 } = require("crypto");
|
|
6166
6166
|
var { Duplex, Readable } = require("stream");
|
|
6167
6167
|
var { URL: URL2 } = require("url");
|
|
6168
6168
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -6842,7 +6842,7 @@ var require_websocket = __commonJS({
|
|
|
6842
6842
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
6843
6843
|
return;
|
|
6844
6844
|
}
|
|
6845
|
-
const digest =
|
|
6845
|
+
const digest = createHash4("sha1").update(key + GUID).digest("base64");
|
|
6846
6846
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
6847
6847
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
6848
6848
|
return;
|
|
@@ -7229,7 +7229,7 @@ var require_websocket_server = __commonJS({
|
|
|
7229
7229
|
var EventEmitter = require("events");
|
|
7230
7230
|
var http = require("http");
|
|
7231
7231
|
var { Duplex } = require("stream");
|
|
7232
|
-
var { createHash:
|
|
7232
|
+
var { createHash: createHash4 } = require("crypto");
|
|
7233
7233
|
var extension2 = require_extension();
|
|
7234
7234
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
7235
7235
|
var subprotocol2 = require_subprotocol();
|
|
@@ -7538,7 +7538,7 @@ var require_websocket_server = __commonJS({
|
|
|
7538
7538
|
}
|
|
7539
7539
|
if (this._state > RUNNING)
|
|
7540
7540
|
return abortHandshake(socket, 503);
|
|
7541
|
-
const digest =
|
|
7541
|
+
const digest = createHash4("sha1").update(key + GUID).digest("base64");
|
|
7542
7542
|
const headers = [
|
|
7543
7543
|
"HTTP/1.1 101 Switching Protocols",
|
|
7544
7544
|
"Upgrade: websocket",
|
|
@@ -14252,7 +14252,7 @@ var init_select2 = __esm({
|
|
|
14252
14252
|
const tableName = getTableLikeName(table);
|
|
14253
14253
|
for (const item of extractUsedTable(table))
|
|
14254
14254
|
this.usedTables.add(item);
|
|
14255
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
14255
|
+
if (typeof tableName === "string" && this.config.joins?.some((join6) => join6.alias === tableName)) {
|
|
14256
14256
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
14257
14257
|
}
|
|
14258
14258
|
if (!this.isPartialSelect) {
|
|
@@ -15141,7 +15141,7 @@ var init_update = __esm({
|
|
|
15141
15141
|
createJoin(joinType) {
|
|
15142
15142
|
return (table, on) => {
|
|
15143
15143
|
const tableName = getTableLikeName(table);
|
|
15144
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
15144
|
+
if (typeof tableName === "string" && this.config.joins.some((join6) => join6.alias === tableName)) {
|
|
15145
15145
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
15146
15146
|
}
|
|
15147
15147
|
if (typeof on === "function") {
|
|
@@ -16541,7 +16541,9 @@ var init_schema = __esm({
|
|
|
16541
16541
|
created_at: text("created_at").notNull(),
|
|
16542
16542
|
updated_at: text("updated_at").notNull(),
|
|
16543
16543
|
parent_session_id: text("parent_session_id"),
|
|
16544
|
-
external_ref: text("external_ref")
|
|
16544
|
+
external_ref: text("external_ref"),
|
|
16545
|
+
public_key: text("public_key"),
|
|
16546
|
+
signature: text("signature")
|
|
16545
16547
|
});
|
|
16546
16548
|
messages = sqliteTable("messages", {
|
|
16547
16549
|
id: text("id").primaryKey(),
|
|
@@ -16602,6 +16604,12 @@ async function initDb(dbPath2) {
|
|
|
16602
16604
|
if (!sessCols.includes("external_ref")) {
|
|
16603
16605
|
await client.execute("ALTER TABLE sessions ADD COLUMN external_ref TEXT");
|
|
16604
16606
|
}
|
|
16607
|
+
if (!sessCols.includes("public_key")) {
|
|
16608
|
+
await client.execute("ALTER TABLE sessions ADD COLUMN public_key TEXT");
|
|
16609
|
+
}
|
|
16610
|
+
if (!sessCols.includes("signature")) {
|
|
16611
|
+
await client.execute("ALTER TABLE sessions ADD COLUMN signature TEXT");
|
|
16612
|
+
}
|
|
16605
16613
|
return drizzle(client, { schema: schema_exports });
|
|
16606
16614
|
}
|
|
16607
16615
|
var import_os, import_fs, import_path, CREATE_SQL;
|
|
@@ -16622,7 +16630,9 @@ var init_db2 = __esm({
|
|
|
16622
16630
|
created_at TEXT NOT NULL,
|
|
16623
16631
|
updated_at TEXT NOT NULL,
|
|
16624
16632
|
parent_session_id TEXT,
|
|
16625
|
-
external_ref TEXT
|
|
16633
|
+
external_ref TEXT,
|
|
16634
|
+
public_key TEXT,
|
|
16635
|
+
signature TEXT
|
|
16626
16636
|
)`,
|
|
16627
16637
|
`CREATE TABLE IF NOT EXISTS messages (
|
|
16628
16638
|
id TEXT PRIMARY KEY,
|
|
@@ -16824,14 +16834,14 @@ async function queryIntegrity(db) {
|
|
|
16824
16834
|
unchained++;
|
|
16825
16835
|
continue;
|
|
16826
16836
|
}
|
|
16827
|
-
let
|
|
16837
|
+
let ok2 = true;
|
|
16828
16838
|
for (let i = 1; i < chained.length; i++) {
|
|
16829
16839
|
if (chained[i].prev_hash !== chained[i - 1].content_hash) {
|
|
16830
|
-
|
|
16840
|
+
ok2 = false;
|
|
16831
16841
|
break;
|
|
16832
16842
|
}
|
|
16833
16843
|
}
|
|
16834
|
-
if (
|
|
16844
|
+
if (ok2)
|
|
16835
16845
|
intact++;
|
|
16836
16846
|
else {
|
|
16837
16847
|
broken++;
|
|
@@ -17419,7 +17429,7 @@ var require_package = __commonJS({
|
|
|
17419
17429
|
"package.json"(exports2, module2) {
|
|
17420
17430
|
module2.exports = {
|
|
17421
17431
|
name: "chron-mcp",
|
|
17422
|
-
version: "0.1.
|
|
17432
|
+
version: "0.1.20",
|
|
17423
17433
|
mcpName: "io.github.sirinivask/chron",
|
|
17424
17434
|
description: "Audit-grade timestamped logs for every AI conversation",
|
|
17425
17435
|
repository: {
|
|
@@ -17578,7 +17588,7 @@ ${DIM4}Sending test event...${RESET5} `);
|
|
|
17578
17588
|
}
|
|
17579
17589
|
}]
|
|
17580
17590
|
}]);
|
|
17581
|
-
let
|
|
17591
|
+
let ok2 = false;
|
|
17582
17592
|
let statusCode = 0;
|
|
17583
17593
|
try {
|
|
17584
17594
|
const res = await fetch(url, {
|
|
@@ -17590,7 +17600,7 @@ ${DIM4}Sending test event...${RESET5} `);
|
|
|
17590
17600
|
body: testPayload
|
|
17591
17601
|
});
|
|
17592
17602
|
statusCode = res.status;
|
|
17593
|
-
|
|
17603
|
+
ok2 = res.ok;
|
|
17594
17604
|
} catch (e) {
|
|
17595
17605
|
process.stdout.write(`${RED}failed${RESET5}
|
|
17596
17606
|
`);
|
|
@@ -17599,7 +17609,7 @@ ${DIM4}Sending test event...${RESET5} `);
|
|
|
17599
17609
|
`);
|
|
17600
17610
|
process.exit(1);
|
|
17601
17611
|
}
|
|
17602
|
-
if (!
|
|
17612
|
+
if (!ok2) {
|
|
17603
17613
|
process.stdout.write(`${RED}failed (HTTP ${statusCode})${RESET5}
|
|
17604
17614
|
`);
|
|
17605
17615
|
process.stderr.write(` ${RED}Check your URL and token, then try again.${RESET5}
|
|
@@ -18118,6 +18128,254 @@ var init_summary = __esm({
|
|
|
18118
18128
|
}
|
|
18119
18129
|
});
|
|
18120
18130
|
|
|
18131
|
+
// src/utils/signing.ts
|
|
18132
|
+
function keysDir() {
|
|
18133
|
+
return (0, import_path4.join)((0, import_os5.homedir)(), ".chron", "keys");
|
|
18134
|
+
}
|
|
18135
|
+
function privKeyPath(sessionId) {
|
|
18136
|
+
return (0, import_path4.join)(keysDir(), `${sessionId}.key`);
|
|
18137
|
+
}
|
|
18138
|
+
function sessionDigest(sessionId, finalContentHash, messageCount, firstCreatedAt) {
|
|
18139
|
+
const input = `${sessionId}|${finalContentHash}|${messageCount}|${firstCreatedAt}`;
|
|
18140
|
+
return (0, import_crypto2.createHash)("sha256").update(input).digest();
|
|
18141
|
+
}
|
|
18142
|
+
function signSession(sessionId, finalContentHash, messageCount, firstCreatedAt) {
|
|
18143
|
+
const privPath = privKeyPath(sessionId);
|
|
18144
|
+
if (!(0, import_fs4.existsSync)(privPath)) {
|
|
18145
|
+
throw new Error(`Private key not found: ${privPath}`);
|
|
18146
|
+
}
|
|
18147
|
+
const privateKey = (0, import_fs4.readFileSync)(privPath, "utf8");
|
|
18148
|
+
const digest = sessionDigest(sessionId, finalContentHash, messageCount, firstCreatedAt);
|
|
18149
|
+
const sig = (0, import_crypto2.sign)(null, digest, privateKey);
|
|
18150
|
+
return sig.toString("base64");
|
|
18151
|
+
}
|
|
18152
|
+
function verifySignature(publicKeyPem, signatureB64, sessionId, finalContentHash, messageCount, firstCreatedAt) {
|
|
18153
|
+
try {
|
|
18154
|
+
const digest = sessionDigest(sessionId, finalContentHash, messageCount, firstCreatedAt);
|
|
18155
|
+
const sigBuf = Buffer.from(signatureB64, "base64");
|
|
18156
|
+
return (0, import_crypto2.verify)(null, digest, publicKeyPem, sigBuf);
|
|
18157
|
+
} catch {
|
|
18158
|
+
return false;
|
|
18159
|
+
}
|
|
18160
|
+
}
|
|
18161
|
+
var import_crypto2, import_fs4, import_path4, import_os5;
|
|
18162
|
+
var init_signing = __esm({
|
|
18163
|
+
"src/utils/signing.ts"() {
|
|
18164
|
+
"use strict";
|
|
18165
|
+
import_crypto2 = require("crypto");
|
|
18166
|
+
import_fs4 = require("fs");
|
|
18167
|
+
import_path4 = require("path");
|
|
18168
|
+
import_os5 = require("os");
|
|
18169
|
+
}
|
|
18170
|
+
});
|
|
18171
|
+
|
|
18172
|
+
// src/cli/sign.ts
|
|
18173
|
+
var sign_exports = {};
|
|
18174
|
+
__export(sign_exports, {
|
|
18175
|
+
runSign: () => runSign
|
|
18176
|
+
});
|
|
18177
|
+
async function runSign(args2) {
|
|
18178
|
+
const prefix = args2[0];
|
|
18179
|
+
if (!prefix) {
|
|
18180
|
+
process.stderr.write("Usage: chron sign <session-id-prefix>\n");
|
|
18181
|
+
process.exit(1);
|
|
18182
|
+
}
|
|
18183
|
+
const db = await initDb();
|
|
18184
|
+
const allSessions = await db.select().from(sessions);
|
|
18185
|
+
const session = allSessions.find((s) => s.id.startsWith(prefix));
|
|
18186
|
+
if (!session) {
|
|
18187
|
+
process.stderr.write(`Session not found: ${prefix}
|
|
18188
|
+
`);
|
|
18189
|
+
process.exit(1);
|
|
18190
|
+
}
|
|
18191
|
+
if (!session.public_key) {
|
|
18192
|
+
process.stderr.write(`Session ${session.id.slice(0, 8)} has no public key \u2014 was it created before v0.1.19?
|
|
18193
|
+
`);
|
|
18194
|
+
process.exit(1);
|
|
18195
|
+
}
|
|
18196
|
+
if (!(0, import_fs6.existsSync)(privKeyPath(session.id))) {
|
|
18197
|
+
process.stderr.write(`Private key not found: ${privKeyPath(session.id)}
|
|
18198
|
+
`);
|
|
18199
|
+
process.stderr.write("The key is stored on the machine where the session was created.\n");
|
|
18200
|
+
process.exit(1);
|
|
18201
|
+
}
|
|
18202
|
+
const [countRow] = await db.select({
|
|
18203
|
+
count: sql`count(*)`,
|
|
18204
|
+
first_created_at: sql`min(${messages.created_at})`
|
|
18205
|
+
}).from(messages).where(eq(messages.session_id, session.id));
|
|
18206
|
+
const messageCount = countRow?.count ?? 0;
|
|
18207
|
+
const firstCreatedAt = countRow?.first_created_at ?? "";
|
|
18208
|
+
if (messageCount === 0) {
|
|
18209
|
+
process.stderr.write("Session has no messages \u2014 nothing to sign.\n");
|
|
18210
|
+
process.exit(1);
|
|
18211
|
+
}
|
|
18212
|
+
const lastMsg = await db.select({ content_hash: messages.content_hash }).from(messages).where(eq(messages.session_id, session.id)).orderBy(asc(sql`rowid`)).limit(1e3);
|
|
18213
|
+
const finalContentHash = lastMsg[lastMsg.length - 1]?.content_hash ?? "";
|
|
18214
|
+
if (!finalContentHash) {
|
|
18215
|
+
process.stderr.write("Session has no hash chain \u2014 cannot sign.\n");
|
|
18216
|
+
process.exit(1);
|
|
18217
|
+
}
|
|
18218
|
+
const signature = signSession(session.id, finalContentHash, messageCount, firstCreatedAt);
|
|
18219
|
+
await db.update(sessions).set({ signature }).where(eq(sessions.id, session.id));
|
|
18220
|
+
const sigData = {
|
|
18221
|
+
chron_signature: "v1",
|
|
18222
|
+
session_id: session.id,
|
|
18223
|
+
session_title: session.title,
|
|
18224
|
+
public_key_pem: session.public_key,
|
|
18225
|
+
message_count: messageCount,
|
|
18226
|
+
first_message_at: firstCreatedAt,
|
|
18227
|
+
final_content_hash: finalContentHash,
|
|
18228
|
+
signature,
|
|
18229
|
+
signed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
18230
|
+
};
|
|
18231
|
+
const sigFile = (0, import_path5.join)(process.cwd(), `${session.id.slice(0, 8)}.chron.sig`);
|
|
18232
|
+
(0, import_fs5.writeFileSync)(sigFile, JSON.stringify(sigData, null, 2));
|
|
18233
|
+
process.stdout.write(`Signed session ${session.id.slice(0, 8)}
|
|
18234
|
+
`);
|
|
18235
|
+
process.stdout.write(` messages : ${messageCount}
|
|
18236
|
+
`);
|
|
18237
|
+
process.stdout.write(` final hash: ${finalContentHash.slice(0, 16)}\u2026
|
|
18238
|
+
`);
|
|
18239
|
+
process.stdout.write(` sig file : ${sigFile}
|
|
18240
|
+
`);
|
|
18241
|
+
}
|
|
18242
|
+
var import_fs5, import_path5, import_fs6;
|
|
18243
|
+
var init_sign = __esm({
|
|
18244
|
+
"src/cli/sign.ts"() {
|
|
18245
|
+
"use strict";
|
|
18246
|
+
init_drizzle_orm();
|
|
18247
|
+
import_fs5 = require("fs");
|
|
18248
|
+
import_path5 = require("path");
|
|
18249
|
+
init_db2();
|
|
18250
|
+
init_schema();
|
|
18251
|
+
init_signing();
|
|
18252
|
+
import_fs6 = require("fs");
|
|
18253
|
+
}
|
|
18254
|
+
});
|
|
18255
|
+
|
|
18256
|
+
// src/utils/hash.ts
|
|
18257
|
+
function computeContentHash(sessionId, role, content, createdAt, prevHash, eventType) {
|
|
18258
|
+
const base = `${sessionId}|${role}|${content}|${createdAt}|${prevHash ?? ""}`;
|
|
18259
|
+
const input = eventType != null ? `${base}|${eventType}` : base;
|
|
18260
|
+
return (0, import_crypto3.createHash)("sha256").update(input).digest("hex");
|
|
18261
|
+
}
|
|
18262
|
+
var import_crypto3;
|
|
18263
|
+
var init_hash = __esm({
|
|
18264
|
+
"src/utils/hash.ts"() {
|
|
18265
|
+
"use strict";
|
|
18266
|
+
import_crypto3 = require("crypto");
|
|
18267
|
+
}
|
|
18268
|
+
});
|
|
18269
|
+
|
|
18270
|
+
// src/cli/verify.ts
|
|
18271
|
+
var verify_exports = {};
|
|
18272
|
+
__export(verify_exports, {
|
|
18273
|
+
runVerify: () => runVerify
|
|
18274
|
+
});
|
|
18275
|
+
function ok(msg) {
|
|
18276
|
+
process.stdout.write(` ${GREEN3}\u2713${RESET7} ${msg}
|
|
18277
|
+
`);
|
|
18278
|
+
}
|
|
18279
|
+
function fail(msg) {
|
|
18280
|
+
process.stdout.write(` ${RED3}\u2717${RESET7} ${msg}
|
|
18281
|
+
`);
|
|
18282
|
+
}
|
|
18283
|
+
function warn(msg) {
|
|
18284
|
+
process.stdout.write(` ${YELLOW5}!${RESET7} ${msg}
|
|
18285
|
+
`);
|
|
18286
|
+
}
|
|
18287
|
+
async function runVerify(args2) {
|
|
18288
|
+
const prefix = args2[0];
|
|
18289
|
+
if (!prefix) {
|
|
18290
|
+
process.stderr.write("Usage: chron verify <session-id-prefix>\n");
|
|
18291
|
+
process.exit(1);
|
|
18292
|
+
}
|
|
18293
|
+
const db = await initDb();
|
|
18294
|
+
const allSessions = await db.select().from(sessions);
|
|
18295
|
+
const session = allSessions.find((s) => s.id.startsWith(prefix));
|
|
18296
|
+
if (!session) {
|
|
18297
|
+
process.stderr.write(`Session not found: ${prefix}
|
|
18298
|
+
`);
|
|
18299
|
+
process.exit(1);
|
|
18300
|
+
}
|
|
18301
|
+
process.stdout.write(`
|
|
18302
|
+
${BOLD7}Verifying session ${session.id.slice(0, 8)}${RESET7} \u2014 ${session.title}
|
|
18303
|
+
|
|
18304
|
+
`);
|
|
18305
|
+
const rows = await db.select().from(messages).where(eq(messages.session_id, session.id)).orderBy(asc(messages.created_at), asc(sql`rowid`));
|
|
18306
|
+
process.stdout.write(`${BOLD7}Hash chain${RESET7} (${rows.length} messages)
|
|
18307
|
+
`);
|
|
18308
|
+
const chained = rows.filter((r) => r.content_hash !== null);
|
|
18309
|
+
let chainOk = true;
|
|
18310
|
+
if (chained.length === 0) {
|
|
18311
|
+
warn("No chained messages \u2014 session pre-dates hash chaining");
|
|
18312
|
+
} else {
|
|
18313
|
+
for (let i = 0; i < chained.length; i++) {
|
|
18314
|
+
const row = chained[i];
|
|
18315
|
+
const expectedPrev = i === 0 ? null : chained[i - 1].content_hash;
|
|
18316
|
+
if (row.prev_hash !== expectedPrev) {
|
|
18317
|
+
fail(`Row ${i + 1} (${row.id.slice(0, 8)}): prev_hash mismatch \u2014 chain broken`);
|
|
18318
|
+
chainOk = false;
|
|
18319
|
+
break;
|
|
18320
|
+
}
|
|
18321
|
+
const expected = computeContentHash(row.session_id, row.role, row.content, row.created_at, row.prev_hash, row.event_type ?? void 0);
|
|
18322
|
+
if (row.content_hash !== expected) {
|
|
18323
|
+
fail(`Row ${i + 1} (${row.id.slice(0, 8)}): content_hash mismatch \u2014 row tampered`);
|
|
18324
|
+
chainOk = false;
|
|
18325
|
+
break;
|
|
18326
|
+
}
|
|
18327
|
+
}
|
|
18328
|
+
if (chainOk)
|
|
18329
|
+
ok(`All ${chained.length} hashes valid`);
|
|
18330
|
+
}
|
|
18331
|
+
process.stdout.write(`
|
|
18332
|
+
${BOLD7}Signature${RESET7}
|
|
18333
|
+
`);
|
|
18334
|
+
if (!session.public_key) {
|
|
18335
|
+
warn("No public key \u2014 session was created before signing support (v0.1.19)");
|
|
18336
|
+
} else if (!session.signature) {
|
|
18337
|
+
warn("Not yet signed \u2014 run: chron sign " + session.id.slice(0, 8));
|
|
18338
|
+
} else {
|
|
18339
|
+
const finalHash = chained[chained.length - 1]?.content_hash ?? "";
|
|
18340
|
+
const firstCreatedAt = rows[0]?.created_at ?? "";
|
|
18341
|
+
const sigValid = verifySignature(
|
|
18342
|
+
session.public_key,
|
|
18343
|
+
session.signature,
|
|
18344
|
+
session.id,
|
|
18345
|
+
finalHash,
|
|
18346
|
+
rows.length,
|
|
18347
|
+
firstCreatedAt
|
|
18348
|
+
);
|
|
18349
|
+
if (sigValid) {
|
|
18350
|
+
ok("Ed25519 signature valid");
|
|
18351
|
+
process.stdout.write(` ${DIM6}public key: ${session.public_key.split("\n")[1]?.slice(0, 40)}\u2026${RESET7}
|
|
18352
|
+
`);
|
|
18353
|
+
} else {
|
|
18354
|
+
fail("Ed25519 signature INVALID \u2014 session data may have been altered after signing");
|
|
18355
|
+
}
|
|
18356
|
+
}
|
|
18357
|
+
process.stdout.write("\n");
|
|
18358
|
+
const allOk = chainOk && (session.signature ? true : true);
|
|
18359
|
+
process.exit(allOk ? 0 : 1);
|
|
18360
|
+
}
|
|
18361
|
+
var RESET7, BOLD7, GREEN3, RED3, DIM6, YELLOW5;
|
|
18362
|
+
var init_verify = __esm({
|
|
18363
|
+
"src/cli/verify.ts"() {
|
|
18364
|
+
"use strict";
|
|
18365
|
+
init_drizzle_orm();
|
|
18366
|
+
init_db2();
|
|
18367
|
+
init_schema();
|
|
18368
|
+
init_hash();
|
|
18369
|
+
init_signing();
|
|
18370
|
+
RESET7 = "\x1B[0m";
|
|
18371
|
+
BOLD7 = "\x1B[1m";
|
|
18372
|
+
GREEN3 = "\x1B[32m";
|
|
18373
|
+
RED3 = "\x1B[31m";
|
|
18374
|
+
DIM6 = "\x1B[2m";
|
|
18375
|
+
YELLOW5 = "\x1B[33m";
|
|
18376
|
+
}
|
|
18377
|
+
});
|
|
18378
|
+
|
|
18121
18379
|
// src/cli/index.ts
|
|
18122
18380
|
var [, , command, ...args] = process.argv;
|
|
18123
18381
|
async function main() {
|
|
@@ -18157,6 +18415,16 @@ async function main() {
|
|
|
18157
18415
|
await runSummary2(args);
|
|
18158
18416
|
break;
|
|
18159
18417
|
}
|
|
18418
|
+
case "sign": {
|
|
18419
|
+
const { runSign: runSign2 } = await Promise.resolve().then(() => (init_sign(), sign_exports));
|
|
18420
|
+
await runSign2(args);
|
|
18421
|
+
break;
|
|
18422
|
+
}
|
|
18423
|
+
case "verify": {
|
|
18424
|
+
const { runVerify: runVerify2 } = await Promise.resolve().then(() => (init_verify(), verify_exports));
|
|
18425
|
+
await runVerify2(args);
|
|
18426
|
+
break;
|
|
18427
|
+
}
|
|
18160
18428
|
default: {
|
|
18161
18429
|
const name = command ? `Unknown command: ${command}
|
|
18162
18430
|
|
|
@@ -18172,6 +18440,8 @@ Commands:
|
|
|
18172
18440
|
settings View current configuration
|
|
18173
18441
|
connect Connect to a SIEM integration (crowdstrike, sentinel, splunk)
|
|
18174
18442
|
summary Structured summary of a session (timeline, mutations, secrets)
|
|
18443
|
+
sign Sign a session with its Ed25519 key \u2014 produces a .chron.sig file
|
|
18444
|
+
verify Verify a session's hash chain and Ed25519 signature
|
|
18175
18445
|
|
|
18176
18446
|
Options (history):
|
|
18177
18447
|
--limit=<n> Max sessions to show (default: 20)
|
|
@@ -18182,7 +18452,7 @@ Options (report):
|
|
|
18182
18452
|
--format=soc2 Generate SOC 2 HTML evidence package
|
|
18183
18453
|
--output=<file> Output file for --format=soc2 (default: soc2-report.html)
|
|
18184
18454
|
|
|
18185
|
-
Options (export / secrets):
|
|
18455
|
+
Options (export / secrets / sign / verify):
|
|
18186
18456
|
<id-prefix> Scope to a single session
|
|
18187
18457
|
`
|
|
18188
18458
|
);
|
package/dist/index.js
CHANGED
|
@@ -6890,7 +6890,7 @@ var require_filesystem = __commonJS({
|
|
|
6890
6890
|
"use strict";
|
|
6891
6891
|
var fs = require("fs");
|
|
6892
6892
|
var LDD_PATH = "/usr/bin/ldd";
|
|
6893
|
-
var
|
|
6893
|
+
var readFileSync4 = (path) => fs.readFileSync(path, "utf-8");
|
|
6894
6894
|
var readFile = (path) => new Promise((resolve, reject) => {
|
|
6895
6895
|
fs.readFile(path, "utf-8", (err, data) => {
|
|
6896
6896
|
if (err) {
|
|
@@ -6902,7 +6902,7 @@ var require_filesystem = __commonJS({
|
|
|
6902
6902
|
});
|
|
6903
6903
|
module2.exports = {
|
|
6904
6904
|
LDD_PATH,
|
|
6905
|
-
readFileSync:
|
|
6905
|
+
readFileSync: readFileSync4,
|
|
6906
6906
|
readFile
|
|
6907
6907
|
};
|
|
6908
6908
|
}
|
|
@@ -6914,7 +6914,7 @@ var require_detect_libc = __commonJS({
|
|
|
6914
6914
|
"use strict";
|
|
6915
6915
|
var childProcess = require("child_process");
|
|
6916
6916
|
var { isLinux, getReport } = require_process();
|
|
6917
|
-
var { LDD_PATH, readFile, readFileSync:
|
|
6917
|
+
var { LDD_PATH, readFile, readFileSync: readFileSync4 } = require_filesystem();
|
|
6918
6918
|
var cachedFamilyFilesystem;
|
|
6919
6919
|
var cachedVersionFilesystem;
|
|
6920
6920
|
var command = "getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true";
|
|
@@ -6995,7 +6995,7 @@ var require_detect_libc = __commonJS({
|
|
|
6995
6995
|
}
|
|
6996
6996
|
cachedFamilyFilesystem = null;
|
|
6997
6997
|
try {
|
|
6998
|
-
const lddContent =
|
|
6998
|
+
const lddContent = readFileSync4(LDD_PATH);
|
|
6999
6999
|
cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
|
|
7000
7000
|
} catch (e) {
|
|
7001
7001
|
}
|
|
@@ -7052,7 +7052,7 @@ var require_detect_libc = __commonJS({
|
|
|
7052
7052
|
}
|
|
7053
7053
|
cachedVersionFilesystem = null;
|
|
7054
7054
|
try {
|
|
7055
|
-
const lddContent =
|
|
7055
|
+
const lddContent = readFileSync4(LDD_PATH);
|
|
7056
7056
|
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
|
|
7057
7057
|
if (versionMatch) {
|
|
7058
7058
|
cachedVersionFilesystem = versionMatch[1];
|
|
@@ -10246,7 +10246,7 @@ var require_websocket = __commonJS({
|
|
|
10246
10246
|
var http = require("http");
|
|
10247
10247
|
var net = require("net");
|
|
10248
10248
|
var tls = require("tls");
|
|
10249
|
-
var { randomBytes, createHash:
|
|
10249
|
+
var { randomBytes, createHash: createHash4 } = require("crypto");
|
|
10250
10250
|
var { Duplex, Readable } = require("stream");
|
|
10251
10251
|
var { URL: URL3 } = require("url");
|
|
10252
10252
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -10926,7 +10926,7 @@ var require_websocket = __commonJS({
|
|
|
10926
10926
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
10927
10927
|
return;
|
|
10928
10928
|
}
|
|
10929
|
-
const digest =
|
|
10929
|
+
const digest = createHash4("sha1").update(key + GUID).digest("base64");
|
|
10930
10930
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
10931
10931
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
10932
10932
|
return;
|
|
@@ -11313,7 +11313,7 @@ var require_websocket_server = __commonJS({
|
|
|
11313
11313
|
var EventEmitter = require("events");
|
|
11314
11314
|
var http = require("http");
|
|
11315
11315
|
var { Duplex } = require("stream");
|
|
11316
|
-
var { createHash:
|
|
11316
|
+
var { createHash: createHash4 } = require("crypto");
|
|
11317
11317
|
var extension2 = require_extension();
|
|
11318
11318
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
11319
11319
|
var subprotocol2 = require_subprotocol();
|
|
@@ -11622,7 +11622,7 @@ var require_websocket_server = __commonJS({
|
|
|
11622
11622
|
}
|
|
11623
11623
|
if (this._state > RUNNING)
|
|
11624
11624
|
return abortHandshake(socket, 503);
|
|
11625
|
-
const digest =
|
|
11625
|
+
const digest = createHash4("sha1").update(key + GUID).digest("base64");
|
|
11626
11626
|
const headers = [
|
|
11627
11627
|
"HTTP/1.1 101 Switching Protocols",
|
|
11628
11628
|
"Upgrade: websocket",
|
|
@@ -17521,7 +17521,7 @@ var init_sql2 = __esm({
|
|
|
17521
17521
|
return new SQL([new StringChunk(str)]);
|
|
17522
17522
|
}
|
|
17523
17523
|
sql2.raw = raw;
|
|
17524
|
-
function
|
|
17524
|
+
function join6(chunks, separator) {
|
|
17525
17525
|
const result = [];
|
|
17526
17526
|
for (const [i, chunk] of chunks.entries()) {
|
|
17527
17527
|
if (i > 0 && separator !== void 0) {
|
|
@@ -17531,7 +17531,7 @@ var init_sql2 = __esm({
|
|
|
17531
17531
|
}
|
|
17532
17532
|
return new SQL(result);
|
|
17533
17533
|
}
|
|
17534
|
-
sql2.join =
|
|
17534
|
+
sql2.join = join6;
|
|
17535
17535
|
function identifier(value) {
|
|
17536
17536
|
return new Name(value);
|
|
17537
17537
|
}
|
|
@@ -20462,7 +20462,7 @@ var init_select2 = __esm({
|
|
|
20462
20462
|
const tableName = getTableLikeName(table);
|
|
20463
20463
|
for (const item of extractUsedTable(table))
|
|
20464
20464
|
this.usedTables.add(item);
|
|
20465
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
20465
|
+
if (typeof tableName === "string" && this.config.joins?.some((join6) => join6.alias === tableName)) {
|
|
20466
20466
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
20467
20467
|
}
|
|
20468
20468
|
if (!this.isPartialSelect) {
|
|
@@ -21351,7 +21351,7 @@ var init_update = __esm({
|
|
|
21351
21351
|
createJoin(joinType) {
|
|
21352
21352
|
return (table, on) => {
|
|
21353
21353
|
const tableName = getTableLikeName(table);
|
|
21354
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
21354
|
+
if (typeof tableName === "string" && this.config.joins.some((join6) => join6.alias === tableName)) {
|
|
21355
21355
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
21356
21356
|
}
|
|
21357
21357
|
if (typeof on === "function") {
|
|
@@ -22751,7 +22751,9 @@ var init_schema = __esm({
|
|
|
22751
22751
|
created_at: text("created_at").notNull(),
|
|
22752
22752
|
updated_at: text("updated_at").notNull(),
|
|
22753
22753
|
parent_session_id: text("parent_session_id"),
|
|
22754
|
-
external_ref: text("external_ref")
|
|
22754
|
+
external_ref: text("external_ref"),
|
|
22755
|
+
public_key: text("public_key"),
|
|
22756
|
+
signature: text("signature")
|
|
22755
22757
|
});
|
|
22756
22758
|
messages = sqliteTable("messages", {
|
|
22757
22759
|
id: text("id").primaryKey(),
|
|
@@ -22812,6 +22814,12 @@ async function initDb(dbPath) {
|
|
|
22812
22814
|
if (!sessCols.includes("external_ref")) {
|
|
22813
22815
|
await client.execute("ALTER TABLE sessions ADD COLUMN external_ref TEXT");
|
|
22814
22816
|
}
|
|
22817
|
+
if (!sessCols.includes("public_key")) {
|
|
22818
|
+
await client.execute("ALTER TABLE sessions ADD COLUMN public_key TEXT");
|
|
22819
|
+
}
|
|
22820
|
+
if (!sessCols.includes("signature")) {
|
|
22821
|
+
await client.execute("ALTER TABLE sessions ADD COLUMN signature TEXT");
|
|
22822
|
+
}
|
|
22815
22823
|
return drizzle(client, { schema: schema_exports });
|
|
22816
22824
|
}
|
|
22817
22825
|
var import_os, import_fs, import_path, CREATE_SQL;
|
|
@@ -22832,7 +22840,9 @@ var init_db2 = __esm({
|
|
|
22832
22840
|
created_at TEXT NOT NULL,
|
|
22833
22841
|
updated_at TEXT NOT NULL,
|
|
22834
22842
|
parent_session_id TEXT,
|
|
22835
|
-
external_ref TEXT
|
|
22843
|
+
external_ref TEXT,
|
|
22844
|
+
public_key TEXT,
|
|
22845
|
+
signature TEXT
|
|
22836
22846
|
)`,
|
|
22837
22847
|
`CREATE TABLE IF NOT EXISTS messages (
|
|
22838
22848
|
id TEXT PRIMARY KEY,
|
|
@@ -38464,7 +38474,7 @@ var init_time = __esm({
|
|
|
38464
38474
|
var version4;
|
|
38465
38475
|
var init_package = __esm({
|
|
38466
38476
|
"package.json"() {
|
|
38467
|
-
version4 = "0.1.
|
|
38477
|
+
version4 = "0.1.20";
|
|
38468
38478
|
}
|
|
38469
38479
|
});
|
|
38470
38480
|
|
|
@@ -38708,12 +38718,62 @@ var init_relay = __esm({
|
|
|
38708
38718
|
}
|
|
38709
38719
|
});
|
|
38710
38720
|
|
|
38721
|
+
// src/utils/signing.ts
|
|
38722
|
+
function keysDir() {
|
|
38723
|
+
return (0, import_path3.join)((0, import_os3.homedir)(), ".chron", "keys");
|
|
38724
|
+
}
|
|
38725
|
+
function privKeyPath(sessionId) {
|
|
38726
|
+
return (0, import_path3.join)(keysDir(), `${sessionId}.key`);
|
|
38727
|
+
}
|
|
38728
|
+
function pubKeyPath(sessionId) {
|
|
38729
|
+
return (0, import_path3.join)(keysDir(), `${sessionId}.pub`);
|
|
38730
|
+
}
|
|
38731
|
+
function generateSessionKeypair(sessionId) {
|
|
38732
|
+
const { privateKey, publicKey } = (0, import_crypto4.generateKeyPairSync)("ed25519", {
|
|
38733
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
38734
|
+
publicKeyEncoding: { type: "spki", format: "pem" }
|
|
38735
|
+
});
|
|
38736
|
+
const dir = keysDir();
|
|
38737
|
+
(0, import_fs3.mkdirSync)(dir, { recursive: true });
|
|
38738
|
+
(0, import_fs3.writeFileSync)(privKeyPath(sessionId), privateKey, { mode: 384 });
|
|
38739
|
+
(0, import_fs3.writeFileSync)(pubKeyPath(sessionId), publicKey);
|
|
38740
|
+
return publicKey;
|
|
38741
|
+
}
|
|
38742
|
+
function sessionDigest(sessionId, finalContentHash, messageCount, firstCreatedAt) {
|
|
38743
|
+
const input = `${sessionId}|${finalContentHash}|${messageCount}|${firstCreatedAt}`;
|
|
38744
|
+
return (0, import_crypto4.createHash)("sha256").update(input).digest();
|
|
38745
|
+
}
|
|
38746
|
+
function verifySignature(publicKeyPem, signatureB64, sessionId, finalContentHash, messageCount, firstCreatedAt) {
|
|
38747
|
+
try {
|
|
38748
|
+
const digest = sessionDigest(sessionId, finalContentHash, messageCount, firstCreatedAt);
|
|
38749
|
+
const sigBuf = Buffer.from(signatureB64, "base64");
|
|
38750
|
+
return (0, import_crypto4.verify)(null, digest, publicKeyPem, sigBuf);
|
|
38751
|
+
} catch {
|
|
38752
|
+
return false;
|
|
38753
|
+
}
|
|
38754
|
+
}
|
|
38755
|
+
var import_crypto4, import_fs3, import_path3, import_os3;
|
|
38756
|
+
var init_signing = __esm({
|
|
38757
|
+
"src/utils/signing.ts"() {
|
|
38758
|
+
"use strict";
|
|
38759
|
+
import_crypto4 = require("crypto");
|
|
38760
|
+
import_fs3 = require("fs");
|
|
38761
|
+
import_path3 = require("path");
|
|
38762
|
+
import_os3 = require("os");
|
|
38763
|
+
}
|
|
38764
|
+
});
|
|
38765
|
+
|
|
38711
38766
|
// src/tools/sessions.ts
|
|
38712
38767
|
function startSession(db) {
|
|
38713
38768
|
return async (args) => {
|
|
38714
38769
|
const id = v4_default();
|
|
38715
38770
|
const now = localISOString();
|
|
38716
38771
|
try {
|
|
38772
|
+
let publicKey = null;
|
|
38773
|
+
try {
|
|
38774
|
+
publicKey = generateSessionKeypair(id);
|
|
38775
|
+
} catch {
|
|
38776
|
+
}
|
|
38717
38777
|
await db.insert(sessions).values({
|
|
38718
38778
|
id,
|
|
38719
38779
|
title: args.title,
|
|
@@ -38721,7 +38781,8 @@ function startSession(db) {
|
|
|
38721
38781
|
created_at: now,
|
|
38722
38782
|
updated_at: now,
|
|
38723
38783
|
parent_session_id: args.parent_session_id ?? null,
|
|
38724
|
-
external_ref: args.external_ref ?? null
|
|
38784
|
+
external_ref: args.external_ref ?? null,
|
|
38785
|
+
public_key: publicKey
|
|
38725
38786
|
});
|
|
38726
38787
|
emitEvent({ event_type: "session_started", timestamp: now, session: { id_prefix: id.slice(0, 8), ai_tool: args.ai_tool ?? null } });
|
|
38727
38788
|
return {
|
|
@@ -38756,6 +38817,11 @@ function initSession(db) {
|
|
|
38756
38817
|
let created;
|
|
38757
38818
|
let ai_tool;
|
|
38758
38819
|
try {
|
|
38820
|
+
let publicKey = null;
|
|
38821
|
+
try {
|
|
38822
|
+
publicKey = generateSessionKeypair(id);
|
|
38823
|
+
} catch {
|
|
38824
|
+
}
|
|
38759
38825
|
await db.insert(sessions).values({
|
|
38760
38826
|
id,
|
|
38761
38827
|
title: args.title,
|
|
@@ -38763,7 +38829,8 @@ function initSession(db) {
|
|
|
38763
38829
|
created_at: now,
|
|
38764
38830
|
updated_at: now,
|
|
38765
38831
|
parent_session_id: args.parent_session_id ?? null,
|
|
38766
|
-
external_ref: args.external_ref ?? null
|
|
38832
|
+
external_ref: args.external_ref ?? null,
|
|
38833
|
+
public_key: publicKey
|
|
38767
38834
|
});
|
|
38768
38835
|
session_id = id;
|
|
38769
38836
|
created = true;
|
|
@@ -38864,6 +38931,7 @@ var init_sessions = __esm({
|
|
|
38864
38931
|
init_schema();
|
|
38865
38932
|
init_time();
|
|
38866
38933
|
init_relay();
|
|
38934
|
+
init_signing();
|
|
38867
38935
|
}
|
|
38868
38936
|
});
|
|
38869
38937
|
|
|
@@ -38871,13 +38939,13 @@ var init_sessions = __esm({
|
|
|
38871
38939
|
function computeContentHash(sessionId, role, content, createdAt, prevHash, eventType) {
|
|
38872
38940
|
const base = `${sessionId}|${role}|${content}|${createdAt}|${prevHash ?? ""}`;
|
|
38873
38941
|
const input = eventType != null ? `${base}|${eventType}` : base;
|
|
38874
|
-
return (0,
|
|
38942
|
+
return (0, import_crypto5.createHash)("sha256").update(input).digest("hex");
|
|
38875
38943
|
}
|
|
38876
|
-
var
|
|
38944
|
+
var import_crypto5;
|
|
38877
38945
|
var init_hash = __esm({
|
|
38878
38946
|
"src/utils/hash.ts"() {
|
|
38879
38947
|
"use strict";
|
|
38880
|
-
|
|
38948
|
+
import_crypto5 = require("crypto");
|
|
38881
38949
|
}
|
|
38882
38950
|
});
|
|
38883
38951
|
|
|
@@ -39345,6 +39413,7 @@ var init_messages = __esm({
|
|
|
39345
39413
|
// src/tools/verify.ts
|
|
39346
39414
|
function verifySession(db) {
|
|
39347
39415
|
return async (args) => {
|
|
39416
|
+
const [sessionRow] = await db.select().from(sessions).where(eq(sessions.id, args.session_id)).limit(1);
|
|
39348
39417
|
const rows = await db.select().from(messages).where(eq(messages.session_id, args.session_id)).orderBy(asc(messages.created_at), asc(sql`rowid`));
|
|
39349
39418
|
const chained = rows.filter((r) => r.content_hash !== null);
|
|
39350
39419
|
if (chained.length === 0) {
|
|
@@ -39376,10 +39445,23 @@ function verifySession(db) {
|
|
|
39376
39445
|
};
|
|
39377
39446
|
}
|
|
39378
39447
|
}
|
|
39448
|
+
let signature_valid = "unsigned";
|
|
39449
|
+
if (sessionRow?.public_key && sessionRow?.signature) {
|
|
39450
|
+
const finalHash = chained[chained.length - 1]?.content_hash ?? "";
|
|
39451
|
+
const firstCreatedAt = rows[0]?.created_at ?? "";
|
|
39452
|
+
signature_valid = verifySignature(
|
|
39453
|
+
sessionRow.public_key,
|
|
39454
|
+
sessionRow.signature,
|
|
39455
|
+
args.session_id,
|
|
39456
|
+
finalHash,
|
|
39457
|
+
rows.length,
|
|
39458
|
+
firstCreatedAt
|
|
39459
|
+
);
|
|
39460
|
+
}
|
|
39379
39461
|
return {
|
|
39380
39462
|
content: [{
|
|
39381
39463
|
type: "text",
|
|
39382
|
-
text: JSON.stringify({ valid: true, messages: rows.length, chained: chained.length })
|
|
39464
|
+
text: JSON.stringify({ valid: true, messages: rows.length, chained: chained.length, signature_valid })
|
|
39383
39465
|
}]
|
|
39384
39466
|
};
|
|
39385
39467
|
};
|
|
@@ -39390,6 +39472,7 @@ var init_verify = __esm({
|
|
|
39390
39472
|
init_drizzle_orm();
|
|
39391
39473
|
init_schema();
|
|
39392
39474
|
init_hash();
|
|
39475
|
+
init_signing();
|
|
39393
39476
|
}
|
|
39394
39477
|
});
|
|
39395
39478
|
|
|
@@ -39793,24 +39876,24 @@ __export(setup_exports, {
|
|
|
39793
39876
|
runSetup: () => runSetup
|
|
39794
39877
|
});
|
|
39795
39878
|
function configPath(...parts) {
|
|
39796
|
-
return (0,
|
|
39879
|
+
return (0, import_path4.join)((0, import_os4.homedir)(), ...parts);
|
|
39797
39880
|
}
|
|
39798
39881
|
function readJson(filePath) {
|
|
39799
|
-
if (!(0,
|
|
39882
|
+
if (!(0, import_fs4.existsSync)(filePath))
|
|
39800
39883
|
return {};
|
|
39801
39884
|
try {
|
|
39802
|
-
return JSON.parse((0,
|
|
39885
|
+
return JSON.parse((0, import_fs4.readFileSync)(filePath, "utf8"));
|
|
39803
39886
|
} catch {
|
|
39804
39887
|
return {};
|
|
39805
39888
|
}
|
|
39806
39889
|
}
|
|
39807
39890
|
function writeJson(filePath, data) {
|
|
39808
|
-
(0,
|
|
39809
|
-
(0,
|
|
39891
|
+
(0, import_fs4.mkdirSync)((0, import_path4.dirname)(filePath), { recursive: true });
|
|
39892
|
+
(0, import_fs4.writeFileSync)(filePath, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
39810
39893
|
}
|
|
39811
39894
|
function configureTool(name, filePath) {
|
|
39812
|
-
const dir = (0,
|
|
39813
|
-
if (!(0,
|
|
39895
|
+
const dir = (0, import_path4.dirname)(filePath);
|
|
39896
|
+
if (!(0, import_fs4.existsSync)(dir) && name !== "Claude Code") {
|
|
39814
39897
|
return { tool: name, status: "skipped" };
|
|
39815
39898
|
}
|
|
39816
39899
|
try {
|
|
@@ -39831,7 +39914,7 @@ function configureClaudeCode() {
|
|
|
39831
39914
|
try {
|
|
39832
39915
|
(0, import_child_process.execSync)("claude mcp add chron -- npx -y chron-mcp", { stdio: "pipe" });
|
|
39833
39916
|
} catch {
|
|
39834
|
-
const result = configureTool("Claude Code", (0,
|
|
39917
|
+
const result = configureTool("Claude Code", (0, import_path4.join)((0, import_os4.homedir)(), ".claude", "settings.json"));
|
|
39835
39918
|
if (result.status === "error")
|
|
39836
39919
|
return result;
|
|
39837
39920
|
}
|
|
@@ -39839,13 +39922,13 @@ function configureClaudeCode() {
|
|
|
39839
39922
|
return { tool: "Claude Code", status: "added" };
|
|
39840
39923
|
}
|
|
39841
39924
|
function installClaudeCodeHook() {
|
|
39842
|
-
const skillSrc = (0,
|
|
39843
|
-
const skillDst = (0,
|
|
39844
|
-
if ((0,
|
|
39845
|
-
(0,
|
|
39846
|
-
(0,
|
|
39925
|
+
const skillSrc = (0, import_path4.join)(__dirname, "..", "skills", "chron.skill.md");
|
|
39926
|
+
const skillDst = (0, import_path4.join)((0, import_os4.homedir)(), ".chron", "chron.skill.md");
|
|
39927
|
+
if ((0, import_fs4.existsSync)(skillSrc)) {
|
|
39928
|
+
(0, import_fs4.mkdirSync)((0, import_path4.dirname)(skillDst), { recursive: true });
|
|
39929
|
+
(0, import_fs4.copyFileSync)(skillSrc, skillDst);
|
|
39847
39930
|
}
|
|
39848
|
-
const settingsPath = (0,
|
|
39931
|
+
const settingsPath = (0, import_path4.join)((0, import_os4.homedir)(), ".claude", "settings.json");
|
|
39849
39932
|
const settings = readJson(settingsPath);
|
|
39850
39933
|
if (!settings.hooks)
|
|
39851
39934
|
settings.hooks = {};
|
|
@@ -39869,13 +39952,13 @@ async function runSetup() {
|
|
|
39869
39952
|
}
|
|
39870
39953
|
return results.filter((r) => r.status !== "skipped");
|
|
39871
39954
|
}
|
|
39872
|
-
var
|
|
39955
|
+
var import_fs4, import_os4, import_path4, import_child_process, CHRON_ENTRY, TOOLS;
|
|
39873
39956
|
var init_setup = __esm({
|
|
39874
39957
|
"src/setup.ts"() {
|
|
39875
39958
|
"use strict";
|
|
39876
|
-
|
|
39877
|
-
|
|
39878
|
-
|
|
39959
|
+
import_fs4 = require("fs");
|
|
39960
|
+
import_os4 = require("os");
|
|
39961
|
+
import_path4 = require("path");
|
|
39879
39962
|
import_child_process = require("child_process");
|
|
39880
39963
|
CHRON_ENTRY = {
|
|
39881
39964
|
command: "npx",
|
|
@@ -39884,11 +39967,11 @@ var init_setup = __esm({
|
|
|
39884
39967
|
TOOLS = [
|
|
39885
39968
|
{
|
|
39886
39969
|
name: "Claude Desktop",
|
|
39887
|
-
path: process.platform === "win32" ? (0,
|
|
39970
|
+
path: process.platform === "win32" ? (0, import_path4.join)(process.env.APPDATA ?? "", "Claude", "claude_desktop_config.json") : configPath("Library", "Application Support", "Claude", "claude_desktop_config.json")
|
|
39888
39971
|
},
|
|
39889
39972
|
{
|
|
39890
39973
|
name: "Cursor",
|
|
39891
|
-
path: process.platform === "win32" ? (0,
|
|
39974
|
+
path: process.platform === "win32" ? (0, import_path4.join)(process.env.APPDATA ?? "", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json") : configPath(".cursor", "mcp.json")
|
|
39892
39975
|
},
|
|
39893
39976
|
{
|
|
39894
39977
|
name: "Windsurf",
|
|
@@ -60076,7 +60159,7 @@ var require_view = __commonJS({
|
|
|
60076
60159
|
var dirname3 = path.dirname;
|
|
60077
60160
|
var basename = path.basename;
|
|
60078
60161
|
var extname = path.extname;
|
|
60079
|
-
var
|
|
60162
|
+
var join6 = path.join;
|
|
60080
60163
|
var resolve = path.resolve;
|
|
60081
60164
|
module2.exports = View2;
|
|
60082
60165
|
function View2(name, options) {
|
|
@@ -60124,12 +60207,12 @@ var require_view = __commonJS({
|
|
|
60124
60207
|
};
|
|
60125
60208
|
View2.prototype.resolve = function resolve2(dir, file) {
|
|
60126
60209
|
var ext = this.ext;
|
|
60127
|
-
var path2 =
|
|
60210
|
+
var path2 = join6(dir, file);
|
|
60128
60211
|
var stat = tryStat(path2);
|
|
60129
60212
|
if (stat && stat.isFile()) {
|
|
60130
60213
|
return path2;
|
|
60131
60214
|
}
|
|
60132
|
-
path2 =
|
|
60215
|
+
path2 = join6(dir, basename(file, ext), "index" + ext);
|
|
60133
60216
|
stat = tryStat(path2);
|
|
60134
60217
|
if (stat && stat.isFile()) {
|
|
60135
60218
|
return path2;
|
|
@@ -61196,7 +61279,7 @@ var require_send = __commonJS({
|
|
|
61196
61279
|
var Stream2 = require("stream");
|
|
61197
61280
|
var util2 = require("util");
|
|
61198
61281
|
var extname = path.extname;
|
|
61199
|
-
var
|
|
61282
|
+
var join6 = path.join;
|
|
61200
61283
|
var normalize = path.normalize;
|
|
61201
61284
|
var resolve = path.resolve;
|
|
61202
61285
|
var sep = path.sep;
|
|
@@ -61415,7 +61498,7 @@ var require_send = __commonJS({
|
|
|
61415
61498
|
return res;
|
|
61416
61499
|
}
|
|
61417
61500
|
parts = path2.split(sep);
|
|
61418
|
-
path2 = normalize(
|
|
61501
|
+
path2 = normalize(join6(root, path2));
|
|
61419
61502
|
} else {
|
|
61420
61503
|
if (UP_PATH_REGEXP.test(path2)) {
|
|
61421
61504
|
debug('malicious path "%s"', path2);
|
|
@@ -61556,7 +61639,7 @@ var require_send = __commonJS({
|
|
|
61556
61639
|
return self.onStatError(err);
|
|
61557
61640
|
return self.error(404);
|
|
61558
61641
|
}
|
|
61559
|
-
var p =
|
|
61642
|
+
var p = join6(path2, self._index[i]);
|
|
61560
61643
|
debug('stat "%s"', p);
|
|
61561
61644
|
fs.stat(p, function(err2, stat) {
|
|
61562
61645
|
if (err2)
|
|
@@ -69004,8 +69087,8 @@ var StdioServerTransport = class {
|
|
|
69004
69087
|
};
|
|
69005
69088
|
|
|
69006
69089
|
// src/index.ts
|
|
69007
|
-
var
|
|
69008
|
-
var
|
|
69090
|
+
var import_os5 = require("os");
|
|
69091
|
+
var import_path5 = require("path");
|
|
69009
69092
|
init_db2();
|
|
69010
69093
|
init_server3();
|
|
69011
69094
|
init_package();
|
|
@@ -69015,7 +69098,7 @@ if (process.argv[2] === "--version" || process.argv[2] === "-v") {
|
|
|
69015
69098
|
process.exit(0);
|
|
69016
69099
|
}
|
|
69017
69100
|
async function main() {
|
|
69018
|
-
const dbPath = process.env.CHRON_DB_PATH ?? (0,
|
|
69101
|
+
const dbPath = process.env.CHRON_DB_PATH ?? (0, import_path5.join)((0, import_os5.homedir)(), ".chron", "chron.db");
|
|
69019
69102
|
if (process.stdin.isTTY) {
|
|
69020
69103
|
process.stdout.write(`chron-mcp ${version4}
|
|
69021
69104
|
|