chron-mcp 0.1.19 → 0.1.24
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 +895 -185
- package/dist/index.js +263 -61
- 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 join9(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 = join9;
|
|
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 readFileSync6 = (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: readFileSync6,
|
|
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: readFileSync6 } = 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 = readFileSync6(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 = readFileSync6(LDD_PATH);
|
|
2972
2972
|
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
|
|
2973
2973
|
if (versionMatch) {
|
|
2974
2974
|
cachedVersionFilesystem = versionMatch[1];
|
|
@@ -2995,33 +2995,33 @@ var require_detect_libc = __commonJS({
|
|
|
2995
2995
|
}
|
|
2996
2996
|
return null;
|
|
2997
2997
|
};
|
|
2998
|
-
var
|
|
2999
|
-
let
|
|
2998
|
+
var version4 = async () => {
|
|
2999
|
+
let version5 = null;
|
|
3000
3000
|
if (isLinux()) {
|
|
3001
|
-
|
|
3002
|
-
if (!
|
|
3003
|
-
|
|
3001
|
+
version5 = await versionFromFilesystem();
|
|
3002
|
+
if (!version5) {
|
|
3003
|
+
version5 = versionFromReport();
|
|
3004
3004
|
}
|
|
3005
|
-
if (!
|
|
3005
|
+
if (!version5) {
|
|
3006
3006
|
const out = await safeCommand();
|
|
3007
|
-
|
|
3007
|
+
version5 = versionFromCommand(out);
|
|
3008
3008
|
}
|
|
3009
3009
|
}
|
|
3010
|
-
return
|
|
3010
|
+
return version5;
|
|
3011
3011
|
};
|
|
3012
3012
|
var versionSync = () => {
|
|
3013
|
-
let
|
|
3013
|
+
let version5 = null;
|
|
3014
3014
|
if (isLinux()) {
|
|
3015
|
-
|
|
3016
|
-
if (!
|
|
3017
|
-
|
|
3015
|
+
version5 = versionFromFilesystemSync();
|
|
3016
|
+
if (!version5) {
|
|
3017
|
+
version5 = versionFromReport();
|
|
3018
3018
|
}
|
|
3019
|
-
if (!
|
|
3019
|
+
if (!version5) {
|
|
3020
3020
|
const out = safeCommandSync();
|
|
3021
|
-
|
|
3021
|
+
version5 = versionFromCommand(out);
|
|
3022
3022
|
}
|
|
3023
3023
|
}
|
|
3024
|
-
return
|
|
3024
|
+
return version5;
|
|
3025
3025
|
};
|
|
3026
3026
|
module2.exports = {
|
|
3027
3027
|
GLIBC,
|
|
@@ -3030,7 +3030,7 @@ var require_detect_libc = __commonJS({
|
|
|
3030
3030
|
familySync,
|
|
3031
3031
|
isNonGlibcLinux,
|
|
3032
3032
|
isNonGlibcLinuxSync,
|
|
3033
|
-
version:
|
|
3033
|
+
version: version4,
|
|
3034
3034
|
versionSync
|
|
3035
3035
|
};
|
|
3036
3036
|
}
|
|
@@ -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: createHash6 } = 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 = createHash6("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: createHash6 } = require("crypto");
|
|
7233
7233
|
var extension2 = require_extension();
|
|
7234
7234
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
7235
7235
|
var subprotocol2 = require_subprotocol();
|
|
@@ -7429,7 +7429,7 @@ var require_websocket_server = __commonJS({
|
|
|
7429
7429
|
socket.on("error", socketOnError);
|
|
7430
7430
|
const key = req.headers["sec-websocket-key"];
|
|
7431
7431
|
const upgrade = req.headers.upgrade;
|
|
7432
|
-
const
|
|
7432
|
+
const version4 = +req.headers["sec-websocket-version"];
|
|
7433
7433
|
if (req.method !== "GET") {
|
|
7434
7434
|
const message = "Invalid HTTP method";
|
|
7435
7435
|
abortHandshakeOrEmitwsClientError(this, req, socket, 405, message);
|
|
@@ -7445,7 +7445,7 @@ var require_websocket_server = __commonJS({
|
|
|
7445
7445
|
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
|
7446
7446
|
return;
|
|
7447
7447
|
}
|
|
7448
|
-
if (
|
|
7448
|
+
if (version4 !== 13 && version4 !== 8) {
|
|
7449
7449
|
const message = "Missing or invalid Sec-WebSocket-Version header";
|
|
7450
7450
|
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message, {
|
|
7451
7451
|
"Sec-WebSocket-Version": "13, 8"
|
|
@@ -7489,7 +7489,7 @@ var require_websocket_server = __commonJS({
|
|
|
7489
7489
|
}
|
|
7490
7490
|
if (this.options.verifyClient) {
|
|
7491
7491
|
const info = {
|
|
7492
|
-
origin: req.headers[`${
|
|
7492
|
+
origin: req.headers[`${version4 === 8 ? "sec-websocket-origin" : "origin"}`],
|
|
7493
7493
|
secure: !!(req.socket.authorized || req.socket.encrypted),
|
|
7494
7494
|
req
|
|
7495
7495
|
};
|
|
@@ -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 = createHash6("sha1").update(key + GUID).digest("base64");
|
|
7542
7542
|
const headers = [
|
|
7543
7543
|
"HTTP/1.1 101 Switching Protocols",
|
|
7544
7544
|
"Upgrade: websocket",
|
|
@@ -11306,7 +11306,7 @@ var init_lib_esm = __esm({
|
|
|
11306
11306
|
});
|
|
11307
11307
|
|
|
11308
11308
|
// node_modules/@libsql/client/lib-esm/hrana.js
|
|
11309
|
-
async function executeHranaBatch(mode,
|
|
11309
|
+
async function executeHranaBatch(mode, version4, batch, hranaStmts, disableForeignKeys = false) {
|
|
11310
11310
|
if (disableForeignKeys) {
|
|
11311
11311
|
batch.step().run("PRAGMA foreign_keys=off");
|
|
11312
11312
|
}
|
|
@@ -11315,7 +11315,7 @@ async function executeHranaBatch(mode, version3, batch, hranaStmts, disableForei
|
|
|
11315
11315
|
let lastStep = beginStep;
|
|
11316
11316
|
const stmtPromises = hranaStmts.map((hranaStmt) => {
|
|
11317
11317
|
const stmtStep = batch.step().condition(BatchCond.ok(lastStep));
|
|
11318
|
-
if (
|
|
11318
|
+
if (version4 >= 3) {
|
|
11319
11319
|
stmtStep.condition(BatchCond.not(BatchCond.isAutocommit(batch)));
|
|
11320
11320
|
}
|
|
11321
11321
|
const stmtPromise = stmtStep.query(hranaStmt);
|
|
@@ -11323,7 +11323,7 @@ async function executeHranaBatch(mode, version3, batch, hranaStmts, disableForei
|
|
|
11323
11323
|
return stmtPromise;
|
|
11324
11324
|
});
|
|
11325
11325
|
const commitStep = batch.step().condition(BatchCond.ok(lastStep));
|
|
11326
|
-
if (
|
|
11326
|
+
if (version4 >= 3) {
|
|
11327
11327
|
commitStep.condition(BatchCond.not(BatchCond.isAutocommit(batch)));
|
|
11328
11328
|
}
|
|
11329
11329
|
const commitPromise = commitStep.run("COMMIT");
|
|
@@ -11426,9 +11426,9 @@ var init_hrana = __esm({
|
|
|
11426
11426
|
// BEGIN statement yet.
|
|
11427
11427
|
#started;
|
|
11428
11428
|
/** @private */
|
|
11429
|
-
constructor(mode,
|
|
11429
|
+
constructor(mode, version4) {
|
|
11430
11430
|
this.#mode = mode;
|
|
11431
|
-
this.#version =
|
|
11431
|
+
this.#version = version4;
|
|
11432
11432
|
this.#started = void 0;
|
|
11433
11433
|
}
|
|
11434
11434
|
execute(stmt) {
|
|
@@ -11859,10 +11859,10 @@ var init_ws = __esm({
|
|
|
11859
11859
|
return stmt;
|
|
11860
11860
|
});
|
|
11861
11861
|
const hranaStmts = normalizedStmts.map(stmtToHrana);
|
|
11862
|
-
const
|
|
11862
|
+
const version4 = await streamState.conn.client.getVersion();
|
|
11863
11863
|
streamState.conn.sqlCache.apply(hranaStmts);
|
|
11864
|
-
const batch = streamState.stream.batch(
|
|
11865
|
-
const resultsPromise = executeHranaBatch(mode,
|
|
11864
|
+
const batch = streamState.stream.batch(version4 >= 3);
|
|
11865
|
+
const resultsPromise = executeHranaBatch(mode, version4, batch, hranaStmts);
|
|
11866
11866
|
const results = await resultsPromise;
|
|
11867
11867
|
return results;
|
|
11868
11868
|
} catch (e) {
|
|
@@ -11877,9 +11877,9 @@ var init_ws = __esm({
|
|
|
11877
11877
|
const streamState = await this.#openStream();
|
|
11878
11878
|
try {
|
|
11879
11879
|
const hranaStmts = stmts.map(stmtToHrana);
|
|
11880
|
-
const
|
|
11881
|
-
const batch = streamState.stream.batch(
|
|
11882
|
-
const resultsPromise = executeHranaBatch("deferred",
|
|
11880
|
+
const version4 = await streamState.conn.client.getVersion();
|
|
11881
|
+
const batch = streamState.stream.batch(version4 >= 3);
|
|
11882
|
+
const resultsPromise = executeHranaBatch("deferred", version4, batch, hranaStmts, true);
|
|
11883
11883
|
const results = await resultsPromise;
|
|
11884
11884
|
return results;
|
|
11885
11885
|
} catch (e) {
|
|
@@ -11893,8 +11893,8 @@ var init_ws = __esm({
|
|
|
11893
11893
|
return this.limit(async () => {
|
|
11894
11894
|
const streamState = await this.#openStream();
|
|
11895
11895
|
try {
|
|
11896
|
-
const
|
|
11897
|
-
return new WsTransaction(this, streamState, mode,
|
|
11896
|
+
const version4 = await streamState.conn.client.getVersion();
|
|
11897
|
+
return new WsTransaction(this, streamState, mode, version4);
|
|
11898
11898
|
} catch (e) {
|
|
11899
11899
|
this._closeStream(streamState);
|
|
11900
11900
|
throw mapHranaError(e);
|
|
@@ -12001,8 +12001,8 @@ var init_ws = __esm({
|
|
|
12001
12001
|
this.#futureConnState = void 0;
|
|
12002
12002
|
}
|
|
12003
12003
|
const next = this.#openConn();
|
|
12004
|
-
const
|
|
12005
|
-
next.useSqlCache =
|
|
12004
|
+
const version4 = await next.client.getVersion();
|
|
12005
|
+
next.useSqlCache = version4 >= 2;
|
|
12006
12006
|
if (next.useSqlCache) {
|
|
12007
12007
|
next.sqlCache.capacity = sqlCacheCapacity;
|
|
12008
12008
|
}
|
|
@@ -12034,8 +12034,8 @@ var init_ws = __esm({
|
|
|
12034
12034
|
#client;
|
|
12035
12035
|
#streamState;
|
|
12036
12036
|
/** @private */
|
|
12037
|
-
constructor(client, state, mode,
|
|
12038
|
-
super(mode,
|
|
12037
|
+
constructor(client, state, mode, version4) {
|
|
12038
|
+
super(mode, version4);
|
|
12039
12039
|
this.#client = client;
|
|
12040
12040
|
this.#streamState = state;
|
|
12041
12041
|
}
|
|
@@ -12152,14 +12152,14 @@ var init_http = __esm({
|
|
|
12152
12152
|
return stmt;
|
|
12153
12153
|
});
|
|
12154
12154
|
const hranaStmts = normalizedStmts.map(stmtToHrana);
|
|
12155
|
-
const
|
|
12155
|
+
const version4 = await this.#client.getVersion();
|
|
12156
12156
|
let resultsPromise;
|
|
12157
12157
|
const stream = this.#client.openStream();
|
|
12158
12158
|
try {
|
|
12159
12159
|
const sqlCache = new SqlCache(stream, sqlCacheCapacity2);
|
|
12160
12160
|
sqlCache.apply(hranaStmts);
|
|
12161
12161
|
const batch = stream.batch(false);
|
|
12162
|
-
resultsPromise = executeHranaBatch(mode,
|
|
12162
|
+
resultsPromise = executeHranaBatch(mode, version4, batch, hranaStmts);
|
|
12163
12163
|
} finally {
|
|
12164
12164
|
stream.closeGracefully();
|
|
12165
12165
|
}
|
|
@@ -12174,12 +12174,12 @@ var init_http = __esm({
|
|
|
12174
12174
|
return this.limit(async () => {
|
|
12175
12175
|
try {
|
|
12176
12176
|
const hranaStmts = stmts.map(stmtToHrana);
|
|
12177
|
-
const
|
|
12177
|
+
const version4 = await this.#client.getVersion();
|
|
12178
12178
|
let resultsPromise;
|
|
12179
12179
|
const stream = this.#client.openStream();
|
|
12180
12180
|
try {
|
|
12181
12181
|
const batch = stream.batch(false);
|
|
12182
|
-
resultsPromise = executeHranaBatch("deferred",
|
|
12182
|
+
resultsPromise = executeHranaBatch("deferred", version4, batch, hranaStmts, true);
|
|
12183
12183
|
} finally {
|
|
12184
12184
|
stream.closeGracefully();
|
|
12185
12185
|
}
|
|
@@ -12193,8 +12193,8 @@ var init_http = __esm({
|
|
|
12193
12193
|
async transaction(mode = "write") {
|
|
12194
12194
|
return this.limit(async () => {
|
|
12195
12195
|
try {
|
|
12196
|
-
const
|
|
12197
|
-
return new HttpTransaction(this.#client.openStream(), mode,
|
|
12196
|
+
const version4 = await this.#client.getVersion();
|
|
12197
|
+
return new HttpTransaction(this.#client.openStream(), mode, version4);
|
|
12198
12198
|
} catch (e) {
|
|
12199
12199
|
throw mapHranaError(e);
|
|
12200
12200
|
}
|
|
@@ -12240,8 +12240,8 @@ var init_http = __esm({
|
|
|
12240
12240
|
#stream;
|
|
12241
12241
|
#sqlCache;
|
|
12242
12242
|
/** @private */
|
|
12243
|
-
constructor(stream, mode,
|
|
12244
|
-
super(mode,
|
|
12243
|
+
constructor(stream, mode, version4) {
|
|
12244
|
+
super(mode, version4);
|
|
12245
12245
|
this.#stream = stream;
|
|
12246
12246
|
this.#sqlCache = new SqlCache(stream, sqlCacheCapacity2);
|
|
12247
12247
|
}
|
|
@@ -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((join9) => join9.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((join9) => join9.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,10 @@ 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"),
|
|
16547
|
+
metadata: text("metadata")
|
|
16545
16548
|
});
|
|
16546
16549
|
messages = sqliteTable("messages", {
|
|
16547
16550
|
id: text("id").primaryKey(),
|
|
@@ -16602,6 +16605,15 @@ async function initDb(dbPath2) {
|
|
|
16602
16605
|
if (!sessCols.includes("external_ref")) {
|
|
16603
16606
|
await client.execute("ALTER TABLE sessions ADD COLUMN external_ref TEXT");
|
|
16604
16607
|
}
|
|
16608
|
+
if (!sessCols.includes("public_key")) {
|
|
16609
|
+
await client.execute("ALTER TABLE sessions ADD COLUMN public_key TEXT");
|
|
16610
|
+
}
|
|
16611
|
+
if (!sessCols.includes("signature")) {
|
|
16612
|
+
await client.execute("ALTER TABLE sessions ADD COLUMN signature TEXT");
|
|
16613
|
+
}
|
|
16614
|
+
if (!sessCols.includes("metadata")) {
|
|
16615
|
+
await client.execute("ALTER TABLE sessions ADD COLUMN metadata TEXT");
|
|
16616
|
+
}
|
|
16605
16617
|
return drizzle(client, { schema: schema_exports });
|
|
16606
16618
|
}
|
|
16607
16619
|
var import_os, import_fs, import_path, CREATE_SQL;
|
|
@@ -16622,7 +16634,10 @@ var init_db2 = __esm({
|
|
|
16622
16634
|
created_at TEXT NOT NULL,
|
|
16623
16635
|
updated_at TEXT NOT NULL,
|
|
16624
16636
|
parent_session_id TEXT,
|
|
16625
|
-
external_ref TEXT
|
|
16637
|
+
external_ref TEXT,
|
|
16638
|
+
public_key TEXT,
|
|
16639
|
+
signature TEXT,
|
|
16640
|
+
metadata TEXT
|
|
16626
16641
|
)`,
|
|
16627
16642
|
`CREATE TABLE IF NOT EXISTS messages (
|
|
16628
16643
|
id TEXT PRIMARY KEY,
|
|
@@ -16668,16 +16683,23 @@ function fmtTimestamp(iso) {
|
|
|
16668
16683
|
const tz = !m[3] || m[3] === "Z" ? "+00:00" : m[3];
|
|
16669
16684
|
return `${m[1]} ${m[2]} ${tz}`;
|
|
16670
16685
|
}
|
|
16671
|
-
async function printList(limit) {
|
|
16686
|
+
async function printList(limit, refFilter) {
|
|
16672
16687
|
const db = await initDb();
|
|
16673
|
-
|
|
16688
|
+
let rows = await db.select({
|
|
16674
16689
|
id: sessions.id,
|
|
16675
16690
|
title: sessions.title,
|
|
16676
16691
|
updated_at: sessions.updated_at,
|
|
16692
|
+
external_ref: sessions.external_ref,
|
|
16677
16693
|
message_count: sql`count(${messages.id})`
|
|
16678
|
-
}).from(sessions).leftJoin(messages, eq(messages.session_id, sessions.id)).groupBy(sessions.id).orderBy(desc(sessions.updated_at)).limit(limit);
|
|
16694
|
+
}).from(sessions).leftJoin(messages, eq(messages.session_id, sessions.id)).groupBy(sessions.id).orderBy(desc(sessions.updated_at)).limit(refFilter ? 1e4 : limit);
|
|
16695
|
+
if (refFilter) {
|
|
16696
|
+
rows = rows.filter((r) => r.external_ref?.startsWith(refFilter) ?? false);
|
|
16697
|
+
rows = rows.slice(0, limit);
|
|
16698
|
+
}
|
|
16679
16699
|
if (rows.length === 0) {
|
|
16680
|
-
|
|
16700
|
+
const msg = refFilter ? `No sessions linked to ref: ${refFilter}
|
|
16701
|
+
` : "No sessions found.\n";
|
|
16702
|
+
process.stdout.write(msg);
|
|
16681
16703
|
return;
|
|
16682
16704
|
}
|
|
16683
16705
|
process.stdout.write("\n");
|
|
@@ -16685,8 +16707,9 @@ async function printList(limit) {
|
|
|
16685
16707
|
const date = fmtDate(row.updated_at);
|
|
16686
16708
|
const prefix = row.id.slice(0, 8);
|
|
16687
16709
|
const count = String(row.message_count).padStart(3);
|
|
16710
|
+
const ref = row.external_ref ? ` ${DIM}[${row.external_ref}]${RESET}` : "";
|
|
16688
16711
|
process.stdout.write(
|
|
16689
|
-
` ${DIM}${date}${RESET} ${CYAN}${prefix}${RESET} ${BOLD}${count} msgs${RESET} ${row.title}
|
|
16712
|
+
` ${DIM}${date}${RESET} ${CYAN}${prefix}${RESET} ${BOLD}${count} msgs${RESET} ${row.title}${ref}
|
|
16690
16713
|
`
|
|
16691
16714
|
);
|
|
16692
16715
|
}
|
|
@@ -16716,25 +16739,66 @@ ${BOLD}${session.title}${RESET}
|
|
|
16716
16739
|
`);
|
|
16717
16740
|
for (const msg of msgs) {
|
|
16718
16741
|
const ts = fmtTimestamp(msg.created_at);
|
|
16719
|
-
|
|
16720
|
-
|
|
16742
|
+
if (msg.event_type === "code_change") {
|
|
16743
|
+
try {
|
|
16744
|
+
const payload = JSON.parse(msg.content);
|
|
16745
|
+
const opColor = payload.operation === "delete" ? YELLOW : payload.operation === "create" ? GREEN : CYAN;
|
|
16746
|
+
process.stdout.write(`${DIM}[${ts}]${RESET} ${BOLD}${MAGENTA}code_change${RESET} ${opColor}${payload.operation ?? ""}${RESET} ${payload.file_path ?? ""}
|
|
16747
|
+
`);
|
|
16748
|
+
if (payload.diff) {
|
|
16749
|
+
const lines = payload.diff.split("\n").slice(0, 20);
|
|
16750
|
+
for (const line of lines) {
|
|
16751
|
+
const color = line.startsWith("+") ? GREEN : line.startsWith("-") ? YELLOW : DIM;
|
|
16752
|
+
process.stdout.write(`${color}${line}${RESET}
|
|
16753
|
+
`);
|
|
16754
|
+
}
|
|
16755
|
+
const total = payload.diff.split("\n").length;
|
|
16756
|
+
if (total > 20)
|
|
16757
|
+
process.stdout.write(`${DIM} \u2026 ${total - 20} more lines${RESET}
|
|
16721
16758
|
`);
|
|
16722
|
-
|
|
16759
|
+
}
|
|
16760
|
+
process.stdout.write("\n");
|
|
16761
|
+
} catch {
|
|
16762
|
+
process.stdout.write(`${DIM}[${ts}]${RESET} ${BOLD}${MAGENTA}code_change${RESET}
|
|
16763
|
+
${msg.content}
|
|
16764
|
+
|
|
16765
|
+
`);
|
|
16766
|
+
}
|
|
16767
|
+
} else if (msg.event_type === "tool_call" || msg.event_type === "tool_result") {
|
|
16768
|
+
try {
|
|
16769
|
+
const payload = JSON.parse(msg.content);
|
|
16770
|
+
const label = msg.event_type === "tool_call" ? `${BOLD}${CYAN}tool_call${RESET} ${payload.tool_name ?? ""}` : `${BOLD}${DIM}tool_result${RESET} id:${payload.tool_call_id?.slice(0, 8) ?? ""}`;
|
|
16771
|
+
process.stdout.write(`${DIM}[${ts}]${RESET} ${label}
|
|
16723
16772
|
|
|
16724
16773
|
`);
|
|
16774
|
+
} catch {
|
|
16775
|
+
process.stdout.write(`${DIM}[${ts}]${RESET} ${BOLD}${CYAN}${msg.event_type}${RESET}
|
|
16776
|
+
${msg.content}
|
|
16777
|
+
|
|
16778
|
+
`);
|
|
16779
|
+
}
|
|
16780
|
+
} else {
|
|
16781
|
+
const roleColor = msg.role === "user" ? YELLOW : CYAN;
|
|
16782
|
+
process.stdout.write(`${DIM}[${ts}]${RESET} ${BOLD}${roleColor}${msg.role}${RESET}
|
|
16783
|
+
`);
|
|
16784
|
+
process.stdout.write(`${msg.content}
|
|
16785
|
+
|
|
16786
|
+
`);
|
|
16787
|
+
}
|
|
16725
16788
|
}
|
|
16726
16789
|
}
|
|
16727
16790
|
async function runHistory(args2) {
|
|
16728
16791
|
const limitArg = args2.find((a) => a.startsWith("--limit="));
|
|
16729
16792
|
const limit = limitArg ? parseInt(limitArg.split("=")[1], 10) : 20;
|
|
16793
|
+
const refArg = args2.find((a) => a.startsWith("--ref="))?.split("=").slice(1).join("=");
|
|
16730
16794
|
const positional = args2.filter((a) => !a.startsWith("--"));
|
|
16731
16795
|
if (positional.length === 0) {
|
|
16732
|
-
await printList(limit);
|
|
16796
|
+
await printList(limit, refArg);
|
|
16733
16797
|
} else {
|
|
16734
16798
|
await printDetail(positional[0]);
|
|
16735
16799
|
}
|
|
16736
16800
|
}
|
|
16737
|
-
var RESET, BOLD, DIM, CYAN, YELLOW;
|
|
16801
|
+
var RESET, BOLD, DIM, CYAN, YELLOW, GREEN, MAGENTA;
|
|
16738
16802
|
var init_history = __esm({
|
|
16739
16803
|
"src/cli/history.ts"() {
|
|
16740
16804
|
"use strict";
|
|
@@ -16746,6 +16810,8 @@ var init_history = __esm({
|
|
|
16746
16810
|
DIM = "\x1B[2m";
|
|
16747
16811
|
CYAN = "\x1B[36m";
|
|
16748
16812
|
YELLOW = "\x1B[33m";
|
|
16813
|
+
GREEN = "\x1B[32m";
|
|
16814
|
+
MAGENTA = "\x1B[35m";
|
|
16749
16815
|
}
|
|
16750
16816
|
});
|
|
16751
16817
|
|
|
@@ -16824,14 +16890,14 @@ async function queryIntegrity(db) {
|
|
|
16824
16890
|
unchained++;
|
|
16825
16891
|
continue;
|
|
16826
16892
|
}
|
|
16827
|
-
let
|
|
16893
|
+
let ok2 = true;
|
|
16828
16894
|
for (let i = 1; i < chained.length; i++) {
|
|
16829
16895
|
if (chained[i].prev_hash !== chained[i - 1].content_hash) {
|
|
16830
|
-
|
|
16896
|
+
ok2 = false;
|
|
16831
16897
|
break;
|
|
16832
16898
|
}
|
|
16833
16899
|
}
|
|
16834
|
-
if (
|
|
16900
|
+
if (ok2)
|
|
16835
16901
|
intact++;
|
|
16836
16902
|
else {
|
|
16837
16903
|
broken++;
|
|
@@ -17127,15 +17193,15 @@ function parseSince(arg) {
|
|
|
17127
17193
|
}
|
|
17128
17194
|
throw new Error(`Invalid --since value: "${arg}". Use 7d, 30d, or YYYY-MM-DD.`);
|
|
17129
17195
|
}
|
|
17130
|
-
async function buildReport(db,
|
|
17196
|
+
async function buildReport(db, cutoffDate2) {
|
|
17131
17197
|
const allSessions = await db.select({
|
|
17132
17198
|
id: sessions.id,
|
|
17133
17199
|
ai_tool: sessions.ai_tool,
|
|
17134
17200
|
updated_at: sessions.updated_at
|
|
17135
17201
|
}).from(sessions);
|
|
17136
|
-
const filtered =
|
|
17202
|
+
const filtered = cutoffDate2 ? allSessions.filter((s) => s.updated_at >= cutoffDate2) : allSessions;
|
|
17137
17203
|
const dateTo = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
17138
|
-
const dateFrom =
|
|
17204
|
+
const dateFrom = cutoffDate2 ?? (filtered.length > 0 ? filtered.map((s) => s.updated_at).sort()[0].slice(0, 10) : dateTo);
|
|
17139
17205
|
if (filtered.length === 0) {
|
|
17140
17206
|
return { dateFrom, dateTo, sessionCount: 0, providerCounts: {}, userMessages: 0, aiMessages: 0, secretsTotal: 0, byType: [] };
|
|
17141
17207
|
}
|
|
@@ -17203,10 +17269,10 @@ async function runReport(args2) {
|
|
|
17203
17269
|
return;
|
|
17204
17270
|
}
|
|
17205
17271
|
const sinceArg = args2.find((a) => a.startsWith("--since="))?.slice("--since=".length);
|
|
17206
|
-
let
|
|
17272
|
+
let cutoffDate2 = null;
|
|
17207
17273
|
if (sinceArg) {
|
|
17208
17274
|
try {
|
|
17209
|
-
|
|
17275
|
+
cutoffDate2 = parseSince(sinceArg);
|
|
17210
17276
|
} catch (err) {
|
|
17211
17277
|
process.stderr.write(`${err instanceof Error ? err.message : String(err)}
|
|
17212
17278
|
`);
|
|
@@ -17214,7 +17280,7 @@ async function runReport(args2) {
|
|
|
17214
17280
|
}
|
|
17215
17281
|
}
|
|
17216
17282
|
const db = await initDb();
|
|
17217
|
-
const data = await buildReport(db,
|
|
17283
|
+
const data = await buildReport(db, cutoffDate2);
|
|
17218
17284
|
printReport(data);
|
|
17219
17285
|
}
|
|
17220
17286
|
var RESET2, BOLD2;
|
|
@@ -17229,6 +17295,271 @@ var init_report = __esm({
|
|
|
17229
17295
|
}
|
|
17230
17296
|
});
|
|
17231
17297
|
|
|
17298
|
+
// src/utils/signing.ts
|
|
17299
|
+
function keysDir() {
|
|
17300
|
+
return (0, import_path2.join)((0, import_os3.homedir)(), ".chron", "keys");
|
|
17301
|
+
}
|
|
17302
|
+
function privKeyPath(sessionId) {
|
|
17303
|
+
return (0, import_path2.join)(keysDir(), `${sessionId}.key`);
|
|
17304
|
+
}
|
|
17305
|
+
function sessionDigest(sessionId, finalContentHash, messageCount, firstCreatedAt) {
|
|
17306
|
+
const input = `${sessionId}|${finalContentHash}|${messageCount}|${firstCreatedAt}`;
|
|
17307
|
+
return (0, import_crypto2.createHash)("sha256").update(input).digest();
|
|
17308
|
+
}
|
|
17309
|
+
function signSession(sessionId, finalContentHash, messageCount, firstCreatedAt) {
|
|
17310
|
+
const privPath = privKeyPath(sessionId);
|
|
17311
|
+
if (!(0, import_fs3.existsSync)(privPath)) {
|
|
17312
|
+
throw new Error(`Private key not found: ${privPath}`);
|
|
17313
|
+
}
|
|
17314
|
+
const privateKey = (0, import_fs3.readFileSync)(privPath, "utf8");
|
|
17315
|
+
const digest = sessionDigest(sessionId, finalContentHash, messageCount, firstCreatedAt);
|
|
17316
|
+
const sig = (0, import_crypto2.sign)(null, digest, privateKey);
|
|
17317
|
+
return sig.toString("base64");
|
|
17318
|
+
}
|
|
17319
|
+
function verifySignature(publicKeyPem, signatureB64, sessionId, finalContentHash, messageCount, firstCreatedAt) {
|
|
17320
|
+
try {
|
|
17321
|
+
const digest = sessionDigest(sessionId, finalContentHash, messageCount, firstCreatedAt);
|
|
17322
|
+
const sigBuf = Buffer.from(signatureB64, "base64");
|
|
17323
|
+
return (0, import_crypto2.verify)(null, digest, publicKeyPem, sigBuf);
|
|
17324
|
+
} catch {
|
|
17325
|
+
return false;
|
|
17326
|
+
}
|
|
17327
|
+
}
|
|
17328
|
+
function signBuffer(sessionId, data) {
|
|
17329
|
+
const privateKey = (0, import_fs3.readFileSync)(privKeyPath(sessionId), "utf8");
|
|
17330
|
+
const digest = (0, import_crypto2.createHash)("sha256").update(data).digest();
|
|
17331
|
+
const sig = (0, import_crypto2.sign)(null, digest, privateKey);
|
|
17332
|
+
return sig.toString("base64");
|
|
17333
|
+
}
|
|
17334
|
+
function verifyBufferSignature(publicKeyPem, signatureB64, data) {
|
|
17335
|
+
try {
|
|
17336
|
+
const digest = (0, import_crypto2.createHash)("sha256").update(data).digest();
|
|
17337
|
+
const sigBuf = Buffer.from(signatureB64, "base64");
|
|
17338
|
+
return (0, import_crypto2.verify)(null, digest, publicKeyPem, sigBuf);
|
|
17339
|
+
} catch {
|
|
17340
|
+
return false;
|
|
17341
|
+
}
|
|
17342
|
+
}
|
|
17343
|
+
var import_crypto2, import_fs3, import_path2, import_os3;
|
|
17344
|
+
var init_signing = __esm({
|
|
17345
|
+
"src/utils/signing.ts"() {
|
|
17346
|
+
"use strict";
|
|
17347
|
+
import_crypto2 = require("crypto");
|
|
17348
|
+
import_fs3 = require("fs");
|
|
17349
|
+
import_path2 = require("path");
|
|
17350
|
+
import_os3 = require("os");
|
|
17351
|
+
}
|
|
17352
|
+
});
|
|
17353
|
+
|
|
17354
|
+
// package.json
|
|
17355
|
+
var require_package = __commonJS({
|
|
17356
|
+
"package.json"(exports2, module2) {
|
|
17357
|
+
module2.exports = {
|
|
17358
|
+
name: "chron-mcp",
|
|
17359
|
+
version: "0.1.24",
|
|
17360
|
+
mcpName: "io.github.sirinivask/chron",
|
|
17361
|
+
description: "Audit-grade timestamped logs for every AI conversation",
|
|
17362
|
+
repository: {
|
|
17363
|
+
type: "git",
|
|
17364
|
+
url: "https://github.com/sirinivask/chron.git"
|
|
17365
|
+
},
|
|
17366
|
+
main: "dist/index.js",
|
|
17367
|
+
vitest: {
|
|
17368
|
+
include: [
|
|
17369
|
+
"tests/**/*.test.ts"
|
|
17370
|
+
],
|
|
17371
|
+
globals: true
|
|
17372
|
+
},
|
|
17373
|
+
bin: {
|
|
17374
|
+
"chron-mcp": "dist/index.js",
|
|
17375
|
+
chron: "dist/cli/index.js"
|
|
17376
|
+
},
|
|
17377
|
+
files: [
|
|
17378
|
+
"dist",
|
|
17379
|
+
"skills",
|
|
17380
|
+
".claude-plugin",
|
|
17381
|
+
"assets",
|
|
17382
|
+
"dashboards",
|
|
17383
|
+
"README.md"
|
|
17384
|
+
],
|
|
17385
|
+
engines: {
|
|
17386
|
+
node: ">=18"
|
|
17387
|
+
},
|
|
17388
|
+
scripts: {
|
|
17389
|
+
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",
|
|
17390
|
+
"build:cli": "npx esbuild src/cli/index.ts --bundle --format=cjs --outfile=dist/cli/index.js --platform=node && chmod +x dist/cli/index.js",
|
|
17391
|
+
typecheck: "tsc --noEmit",
|
|
17392
|
+
dev: "tsc --watch",
|
|
17393
|
+
start: "node dist/index.js",
|
|
17394
|
+
test: "vitest run",
|
|
17395
|
+
"test:watch": "vitest",
|
|
17396
|
+
prepublishOnly: "npm test && npm run build"
|
|
17397
|
+
},
|
|
17398
|
+
keywords: [
|
|
17399
|
+
"mcp",
|
|
17400
|
+
"ai",
|
|
17401
|
+
"audit",
|
|
17402
|
+
"logging",
|
|
17403
|
+
"claude",
|
|
17404
|
+
"cursor"
|
|
17405
|
+
],
|
|
17406
|
+
license: "SEE LICENSE IN LICENSE",
|
|
17407
|
+
dependencies: {
|
|
17408
|
+
"@libsql/client": "^0.17.3",
|
|
17409
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
17410
|
+
"drizzle-orm": "^0.45.2",
|
|
17411
|
+
express: "^4.18.2",
|
|
17412
|
+
uuid: "^11.1.1",
|
|
17413
|
+
zod: "^3.22.4"
|
|
17414
|
+
},
|
|
17415
|
+
devDependencies: {
|
|
17416
|
+
"@types/express": "^4.17.21",
|
|
17417
|
+
"@types/node": "^20.0.0",
|
|
17418
|
+
"@types/uuid": "^9.0.0",
|
|
17419
|
+
"drizzle-kit": "^0.20.14",
|
|
17420
|
+
typescript: "^5.4.5",
|
|
17421
|
+
vitest: "^1.4.0"
|
|
17422
|
+
}
|
|
17423
|
+
};
|
|
17424
|
+
}
|
|
17425
|
+
});
|
|
17426
|
+
|
|
17427
|
+
// src/cli/export-bundle.ts
|
|
17428
|
+
var export_bundle_exports = {};
|
|
17429
|
+
__export(export_bundle_exports, {
|
|
17430
|
+
runExportBundle: () => runExportBundle
|
|
17431
|
+
});
|
|
17432
|
+
function sha256file(filePath) {
|
|
17433
|
+
const content = (0, import_fs4.readFileSync)(filePath);
|
|
17434
|
+
return (0, import_crypto3.createHash)("sha256").update(content).digest("hex");
|
|
17435
|
+
}
|
|
17436
|
+
function parseSince2(s) {
|
|
17437
|
+
if (s.endsWith("d")) {
|
|
17438
|
+
const days = parseInt(s, 10);
|
|
17439
|
+
const d = /* @__PURE__ */ new Date();
|
|
17440
|
+
d.setDate(d.getDate() - days);
|
|
17441
|
+
return d.toISOString().slice(0, 10);
|
|
17442
|
+
}
|
|
17443
|
+
return s;
|
|
17444
|
+
}
|
|
17445
|
+
async function runExportBundle(args2) {
|
|
17446
|
+
const sessionArg = args2.find((a) => a.startsWith("--session="))?.split("=").slice(1).join("=");
|
|
17447
|
+
const sinceArg = args2.find((a) => a.startsWith("--since="))?.split("=").slice(1).join("=");
|
|
17448
|
+
const outputPath = args2.find((a) => a.startsWith("--output="))?.split("=").slice(1).join("=") ?? "bundle.chron.tar.gz";
|
|
17449
|
+
const db = await initDb();
|
|
17450
|
+
const allSessions = await db.select().from(sessions);
|
|
17451
|
+
let filtered = allSessions;
|
|
17452
|
+
if (sessionArg) {
|
|
17453
|
+
filtered = allSessions.filter((s) => s.id.startsWith(sessionArg));
|
|
17454
|
+
if (filtered.length === 0) {
|
|
17455
|
+
process.stderr.write(`Session not found: ${sessionArg}
|
|
17456
|
+
`);
|
|
17457
|
+
process.exit(1);
|
|
17458
|
+
}
|
|
17459
|
+
}
|
|
17460
|
+
if (sinceArg) {
|
|
17461
|
+
const cutoff = parseSince2(sinceArg);
|
|
17462
|
+
filtered = filtered.filter((s) => s.created_at >= cutoff);
|
|
17463
|
+
}
|
|
17464
|
+
if (filtered.length === 0) {
|
|
17465
|
+
process.stderr.write("No sessions matched the filter.\n");
|
|
17466
|
+
process.exit(1);
|
|
17467
|
+
}
|
|
17468
|
+
const sessionIds = filtered.map((s) => s.id);
|
|
17469
|
+
const tempDir = (0, import_path3.join)((0, import_os4.tmpdir)(), `chron-bundle-${Date.now()}`);
|
|
17470
|
+
(0, import_fs4.mkdirSync)(tempDir, { recursive: true });
|
|
17471
|
+
const pubkeysDir = (0, import_path3.join)(tempDir, "pubkeys");
|
|
17472
|
+
(0, import_fs4.mkdirSync)(pubkeysDir);
|
|
17473
|
+
try {
|
|
17474
|
+
(0, import_fs4.writeFileSync)((0, import_path3.join)(tempDir, "sessions.json"), JSON.stringify(filtered, null, 2));
|
|
17475
|
+
const msgsPath = (0, import_path3.join)(tempDir, "messages.jsonl");
|
|
17476
|
+
const msgsStream = (0, import_fs4.createWriteStream)(msgsPath);
|
|
17477
|
+
for (const sid of sessionIds) {
|
|
17478
|
+
const msgs = await db.select().from(messages).where(eq(messages.session_id, sid)).orderBy(asc(messages.created_at), asc(sql`rowid`));
|
|
17479
|
+
for (const m of msgs)
|
|
17480
|
+
msgsStream.write(JSON.stringify(m) + "\n");
|
|
17481
|
+
}
|
|
17482
|
+
await new Promise((resolve) => msgsStream.end(resolve));
|
|
17483
|
+
const secretsPath = (0, import_path3.join)(tempDir, "secrets.jsonl");
|
|
17484
|
+
const secretsStream = (0, import_fs4.createWriteStream)(secretsPath);
|
|
17485
|
+
for (const sid of sessionIds) {
|
|
17486
|
+
const secs = await db.select().from(secrets_detected).where(eq(secrets_detected.session_id, sid));
|
|
17487
|
+
for (const s of secs)
|
|
17488
|
+
secretsStream.write(JSON.stringify(s) + "\n");
|
|
17489
|
+
}
|
|
17490
|
+
await new Promise((resolve) => secretsStream.end(resolve));
|
|
17491
|
+
for (const s of filtered) {
|
|
17492
|
+
if (s.public_key) {
|
|
17493
|
+
(0, import_fs4.writeFileSync)((0, import_path3.join)(pubkeysDir, `${s.id}.pub`), s.public_key);
|
|
17494
|
+
}
|
|
17495
|
+
}
|
|
17496
|
+
const fileHashes = {
|
|
17497
|
+
"sessions.json": sha256file((0, import_path3.join)(tempDir, "sessions.json")),
|
|
17498
|
+
"messages.jsonl": sha256file(msgsPath),
|
|
17499
|
+
"secrets.jsonl": sha256file(secretsPath)
|
|
17500
|
+
};
|
|
17501
|
+
for (const s of filtered) {
|
|
17502
|
+
if (s.public_key) {
|
|
17503
|
+
fileHashes[`pubkeys/${s.id}.pub`] = sha256file((0, import_path3.join)(pubkeysDir, `${s.id}.pub`));
|
|
17504
|
+
}
|
|
17505
|
+
}
|
|
17506
|
+
const manifest = {
|
|
17507
|
+
chron_bundle: "v1",
|
|
17508
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17509
|
+
chron_version: import_package.version,
|
|
17510
|
+
machine_id: (0, import_os4.hostname)(),
|
|
17511
|
+
sessions: sessionIds,
|
|
17512
|
+
files: fileHashes
|
|
17513
|
+
};
|
|
17514
|
+
const manifestBytes = Buffer.from(JSON.stringify(manifest, null, 2));
|
|
17515
|
+
(0, import_fs4.writeFileSync)((0, import_path3.join)(tempDir, "manifest.json"), manifestBytes);
|
|
17516
|
+
let signed = false;
|
|
17517
|
+
if (filtered.length === 1) {
|
|
17518
|
+
const s = filtered[0];
|
|
17519
|
+
if (s.public_key && (0, import_fs4.existsSync)(privKeyPath(s.id))) {
|
|
17520
|
+
try {
|
|
17521
|
+
const sig = signBuffer(s.id, manifestBytes);
|
|
17522
|
+
(0, import_fs4.writeFileSync)((0, import_path3.join)(tempDir, "manifest.sig"), JSON.stringify({
|
|
17523
|
+
chron_sig: "v1",
|
|
17524
|
+
session_id: s.id,
|
|
17525
|
+
algorithm: "Ed25519-SHA256",
|
|
17526
|
+
signature: sig
|
|
17527
|
+
}, null, 2));
|
|
17528
|
+
signed = true;
|
|
17529
|
+
} catch {
|
|
17530
|
+
}
|
|
17531
|
+
}
|
|
17532
|
+
}
|
|
17533
|
+
const absOutput = outputPath.startsWith("/") ? outputPath : (0, import_path3.join)(process.cwd(), outputPath);
|
|
17534
|
+
(0, import_child_process.execSync)(`tar -czf "${absOutput}" -C "${tempDir}" .`);
|
|
17535
|
+
const sessionWord = filtered.length === 1 ? "session" : "sessions";
|
|
17536
|
+
process.stdout.write(`Bundle: ${absOutput}
|
|
17537
|
+
`);
|
|
17538
|
+
process.stdout.write(` ${filtered.length} ${sessionWord} | signed: ${signed ? "yes (Ed25519)" : "no"}
|
|
17539
|
+
`);
|
|
17540
|
+
process.stdout.write(` Verify: chron verify --bundle="${absOutput}"
|
|
17541
|
+
`);
|
|
17542
|
+
} finally {
|
|
17543
|
+
(0, import_fs4.rmSync)(tempDir, { recursive: true, force: true });
|
|
17544
|
+
}
|
|
17545
|
+
}
|
|
17546
|
+
var import_fs4, import_path3, import_os4, import_crypto3, import_child_process, import_package;
|
|
17547
|
+
var init_export_bundle = __esm({
|
|
17548
|
+
"src/cli/export-bundle.ts"() {
|
|
17549
|
+
"use strict";
|
|
17550
|
+
import_fs4 = require("fs");
|
|
17551
|
+
import_path3 = require("path");
|
|
17552
|
+
import_os4 = require("os");
|
|
17553
|
+
import_crypto3 = require("crypto");
|
|
17554
|
+
import_child_process = require("child_process");
|
|
17555
|
+
init_drizzle_orm();
|
|
17556
|
+
init_db2();
|
|
17557
|
+
init_schema();
|
|
17558
|
+
init_signing();
|
|
17559
|
+
import_package = __toESM(require_package());
|
|
17560
|
+
}
|
|
17561
|
+
});
|
|
17562
|
+
|
|
17232
17563
|
// src/cli/export.ts
|
|
17233
17564
|
var export_exports = {};
|
|
17234
17565
|
__export(export_exports, {
|
|
@@ -17242,9 +17573,16 @@ function fmtTimestamp2(iso) {
|
|
|
17242
17573
|
return `${m[1]} ${m[2]} ${tz}`;
|
|
17243
17574
|
}
|
|
17244
17575
|
async function runExport(args2) {
|
|
17576
|
+
if (args2.includes("--signed")) {
|
|
17577
|
+
const { runExportBundle: runExportBundle2 } = await Promise.resolve().then(() => (init_export_bundle(), export_bundle_exports));
|
|
17578
|
+
await runExportBundle2(args2.filter((a) => a !== "--signed"));
|
|
17579
|
+
return;
|
|
17580
|
+
}
|
|
17245
17581
|
const prefix = args2.find((a) => !a.startsWith("--"));
|
|
17246
17582
|
if (!prefix) {
|
|
17247
|
-
process.stderr.write(
|
|
17583
|
+
process.stderr.write(
|
|
17584
|
+
"Usage: chron export <session-id-prefix>\n chron export --signed [--session=<id>] [--since=<range>] [--output=<file>]\n"
|
|
17585
|
+
);
|
|
17248
17586
|
process.exit(1);
|
|
17249
17587
|
}
|
|
17250
17588
|
const db = await initDb();
|
|
@@ -17302,7 +17640,7 @@ __export(settings_exports, {
|
|
|
17302
17640
|
runSettings: () => runSettings
|
|
17303
17641
|
});
|
|
17304
17642
|
function dbPath() {
|
|
17305
|
-
return process.env.CHRON_DB_PATH ?? (0,
|
|
17643
|
+
return process.env.CHRON_DB_PATH ?? (0, import_path4.join)((0, import_os5.homedir)(), ".chron", "chron.db");
|
|
17306
17644
|
}
|
|
17307
17645
|
async function runSettings(_args) {
|
|
17308
17646
|
process.stdout.write(`
|
|
@@ -17316,12 +17654,12 @@ ${BOLD3}Chron Settings${RESET3}
|
|
|
17316
17654
|
|
|
17317
17655
|
`);
|
|
17318
17656
|
}
|
|
17319
|
-
var
|
|
17657
|
+
var import_os5, import_path4, RESET3, BOLD3, DIM2, CYAN2;
|
|
17320
17658
|
var init_settings = __esm({
|
|
17321
17659
|
"src/cli/settings.ts"() {
|
|
17322
17660
|
"use strict";
|
|
17323
|
-
|
|
17324
|
-
|
|
17661
|
+
import_os5 = require("os");
|
|
17662
|
+
import_path4 = require("path");
|
|
17325
17663
|
RESET3 = "\x1B[0m";
|
|
17326
17664
|
BOLD3 = "\x1B[1m";
|
|
17327
17665
|
DIM2 = "\x1B[2m";
|
|
@@ -17414,79 +17752,6 @@ var init_secrets = __esm({
|
|
|
17414
17752
|
}
|
|
17415
17753
|
});
|
|
17416
17754
|
|
|
17417
|
-
// package.json
|
|
17418
|
-
var require_package = __commonJS({
|
|
17419
|
-
"package.json"(exports2, module2) {
|
|
17420
|
-
module2.exports = {
|
|
17421
|
-
name: "chron-mcp",
|
|
17422
|
-
version: "0.1.19",
|
|
17423
|
-
mcpName: "io.github.sirinivask/chron",
|
|
17424
|
-
description: "Audit-grade timestamped logs for every AI conversation",
|
|
17425
|
-
repository: {
|
|
17426
|
-
type: "git",
|
|
17427
|
-
url: "https://github.com/sirinivask/chron.git"
|
|
17428
|
-
},
|
|
17429
|
-
main: "dist/index.js",
|
|
17430
|
-
vitest: {
|
|
17431
|
-
include: [
|
|
17432
|
-
"tests/**/*.test.ts"
|
|
17433
|
-
],
|
|
17434
|
-
globals: true
|
|
17435
|
-
},
|
|
17436
|
-
bin: {
|
|
17437
|
-
"chron-mcp": "dist/index.js",
|
|
17438
|
-
chron: "dist/cli/index.js"
|
|
17439
|
-
},
|
|
17440
|
-
files: [
|
|
17441
|
-
"dist",
|
|
17442
|
-
"skills",
|
|
17443
|
-
".claude-plugin",
|
|
17444
|
-
"assets",
|
|
17445
|
-
"dashboards",
|
|
17446
|
-
"README.md"
|
|
17447
|
-
],
|
|
17448
|
-
engines: {
|
|
17449
|
-
node: ">=18"
|
|
17450
|
-
},
|
|
17451
|
-
scripts: {
|
|
17452
|
-
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",
|
|
17453
|
-
"build:cli": "npx esbuild src/cli/index.ts --bundle --format=cjs --outfile=dist/cli/index.js --platform=node && chmod +x dist/cli/index.js",
|
|
17454
|
-
typecheck: "tsc --noEmit",
|
|
17455
|
-
dev: "tsc --watch",
|
|
17456
|
-
start: "node dist/index.js",
|
|
17457
|
-
test: "vitest run",
|
|
17458
|
-
"test:watch": "vitest",
|
|
17459
|
-
prepublishOnly: "npm test && npm run build"
|
|
17460
|
-
},
|
|
17461
|
-
keywords: [
|
|
17462
|
-
"mcp",
|
|
17463
|
-
"ai",
|
|
17464
|
-
"audit",
|
|
17465
|
-
"logging",
|
|
17466
|
-
"claude",
|
|
17467
|
-
"cursor"
|
|
17468
|
-
],
|
|
17469
|
-
license: "SEE LICENSE IN LICENSE",
|
|
17470
|
-
dependencies: {
|
|
17471
|
-
"@libsql/client": "^0.17.3",
|
|
17472
|
-
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
17473
|
-
"drizzle-orm": "^0.45.2",
|
|
17474
|
-
express: "^4.18.2",
|
|
17475
|
-
uuid: "^11.1.1",
|
|
17476
|
-
zod: "^3.22.4"
|
|
17477
|
-
},
|
|
17478
|
-
devDependencies: {
|
|
17479
|
-
"@types/express": "^4.17.21",
|
|
17480
|
-
"@types/node": "^20.0.0",
|
|
17481
|
-
"@types/uuid": "^9.0.0",
|
|
17482
|
-
"drizzle-kit": "^0.20.14",
|
|
17483
|
-
typescript: "^5.4.5",
|
|
17484
|
-
vitest: "^1.4.0"
|
|
17485
|
-
}
|
|
17486
|
-
};
|
|
17487
|
-
}
|
|
17488
|
-
});
|
|
17489
|
-
|
|
17490
17755
|
// src/cli/connect.ts
|
|
17491
17756
|
var connect_exports = {};
|
|
17492
17757
|
__export(connect_exports, {
|
|
@@ -17496,26 +17761,26 @@ function prompt(rl, question) {
|
|
|
17496
17761
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
17497
17762
|
}
|
|
17498
17763
|
function configPath() {
|
|
17499
|
-
return (0,
|
|
17764
|
+
return (0, import_path5.join)((0, import_os6.homedir)(), ".chron", "config.json");
|
|
17500
17765
|
}
|
|
17501
17766
|
function loadConfig() {
|
|
17502
17767
|
try {
|
|
17503
|
-
return JSON.parse((0,
|
|
17768
|
+
return JSON.parse((0, import_fs5.readFileSync)(configPath(), "utf8"));
|
|
17504
17769
|
} catch {
|
|
17505
17770
|
return {};
|
|
17506
17771
|
}
|
|
17507
17772
|
}
|
|
17508
17773
|
function saveConfig(data) {
|
|
17509
|
-
const dir = (0,
|
|
17510
|
-
(0,
|
|
17511
|
-
(0,
|
|
17774
|
+
const dir = (0, import_path5.join)((0, import_os6.homedir)(), ".chron");
|
|
17775
|
+
(0, import_fs5.mkdirSync)(dir, { recursive: true });
|
|
17776
|
+
(0, import_fs5.writeFileSync)(configPath(), JSON.stringify(data, null, 2), "utf8");
|
|
17512
17777
|
}
|
|
17513
17778
|
function patchClaudeJson(vars) {
|
|
17514
|
-
const path = (0,
|
|
17515
|
-
if (!(0,
|
|
17779
|
+
const path = (0, import_path5.join)((0, import_os6.homedir)(), ".claude.json");
|
|
17780
|
+
if (!(0, import_fs5.existsSync)(path))
|
|
17516
17781
|
return false;
|
|
17517
17782
|
try {
|
|
17518
|
-
const raw = (0,
|
|
17783
|
+
const raw = (0, import_fs5.readFileSync)(path, "utf8");
|
|
17519
17784
|
const doc = JSON.parse(raw);
|
|
17520
17785
|
const servers = doc.mcpServers ?? {};
|
|
17521
17786
|
const chron = servers.chron ?? {};
|
|
@@ -17524,7 +17789,7 @@ function patchClaudeJson(vars) {
|
|
|
17524
17789
|
chron.env = { ...chron.env ?? {}, ...vars };
|
|
17525
17790
|
servers.chron = chron;
|
|
17526
17791
|
doc.mcpServers = servers;
|
|
17527
|
-
(0,
|
|
17792
|
+
(0, import_fs5.writeFileSync)(path, JSON.stringify(doc, null, 2), "utf8");
|
|
17528
17793
|
return true;
|
|
17529
17794
|
} catch {
|
|
17530
17795
|
return false;
|
|
@@ -17578,7 +17843,7 @@ ${DIM4}Sending test event...${RESET5} `);
|
|
|
17578
17843
|
}
|
|
17579
17844
|
}]
|
|
17580
17845
|
}]);
|
|
17581
|
-
let
|
|
17846
|
+
let ok2 = false;
|
|
17582
17847
|
let statusCode = 0;
|
|
17583
17848
|
try {
|
|
17584
17849
|
const res = await fetch(url, {
|
|
@@ -17590,7 +17855,7 @@ ${DIM4}Sending test event...${RESET5} `);
|
|
|
17590
17855
|
body: testPayload
|
|
17591
17856
|
});
|
|
17592
17857
|
statusCode = res.status;
|
|
17593
|
-
|
|
17858
|
+
ok2 = res.ok;
|
|
17594
17859
|
} catch (e) {
|
|
17595
17860
|
process.stdout.write(`${RED}failed${RESET5}
|
|
17596
17861
|
`);
|
|
@@ -17599,7 +17864,7 @@ ${DIM4}Sending test event...${RESET5} `);
|
|
|
17599
17864
|
`);
|
|
17600
17865
|
process.exit(1);
|
|
17601
17866
|
}
|
|
17602
|
-
if (!
|
|
17867
|
+
if (!ok2) {
|
|
17603
17868
|
process.stdout.write(`${RED}failed (HTTP ${statusCode})${RESET5}
|
|
17604
17869
|
`);
|
|
17605
17870
|
process.stderr.write(` ${RED}Check your URL and token, then try again.${RESET5}
|
|
@@ -17607,13 +17872,13 @@ ${DIM4}Sending test event...${RESET5} `);
|
|
|
17607
17872
|
`);
|
|
17608
17873
|
process.exit(1);
|
|
17609
17874
|
}
|
|
17610
|
-
process.stdout.write(`${
|
|
17875
|
+
process.stdout.write(`${GREEN2}OK${RESET5}
|
|
17611
17876
|
|
|
17612
17877
|
`);
|
|
17613
17878
|
const config = loadConfig();
|
|
17614
17879
|
config.logscale = { url, connected_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
17615
17880
|
saveConfig(config);
|
|
17616
|
-
process.stdout.write(`${
|
|
17881
|
+
process.stdout.write(`${GREEN2}${BOLD5}Connected!${RESET5} Test event appeared in LogScale.
|
|
17617
17882
|
|
|
17618
17883
|
`);
|
|
17619
17884
|
process.stdout.write(`${BOLD5}Add these env vars to your MCP client config:${RESET5}
|
|
@@ -17722,7 +17987,7 @@ ${DIM4}Sending test event...${RESET5} `);
|
|
|
17722
17987
|
`);
|
|
17723
17988
|
process.exit(1);
|
|
17724
17989
|
}
|
|
17725
|
-
process.stdout.write(`${
|
|
17990
|
+
process.stdout.write(`${GREEN2}OK${RESET5}
|
|
17726
17991
|
|
|
17727
17992
|
`);
|
|
17728
17993
|
} catch (e) {
|
|
@@ -17740,7 +18005,7 @@ ${DIM4}Sending test event...${RESET5} `);
|
|
|
17740
18005
|
if (isLocalhost(url))
|
|
17741
18006
|
splunkVars.CHRON_SPLUNK_INSECURE = "1";
|
|
17742
18007
|
patchClaudeJson(splunkVars);
|
|
17743
|
-
process.stdout.write(`${
|
|
18008
|
+
process.stdout.write(`${GREEN2}${BOLD5}Connected!${RESET5} Splunk is receiving chron events.
|
|
17744
18009
|
|
|
17745
18010
|
`);
|
|
17746
18011
|
process.stdout.write(`${DIM4}Config saved to ~/.chron/config.json \u2014 events will flow immediately in all running sessions.${RESET5}
|
|
@@ -17829,7 +18094,7 @@ ${DIM4}Authenticating with Azure AD...${RESET5} `);
|
|
|
17829
18094
|
}
|
|
17830
18095
|
const tokenData = await tokenRes.json();
|
|
17831
18096
|
accessToken = tokenData.access_token;
|
|
17832
|
-
process.stdout.write(`${
|
|
18097
|
+
process.stdout.write(`${GREEN2}OK${RESET5}
|
|
17833
18098
|
`);
|
|
17834
18099
|
} catch (e) {
|
|
17835
18100
|
process.stdout.write(`${RED}failed${RESET5}
|
|
@@ -17871,7 +18136,7 @@ ${DIM4}Authenticating with Azure AD...${RESET5} `);
|
|
|
17871
18136
|
`);
|
|
17872
18137
|
process.exit(1);
|
|
17873
18138
|
}
|
|
17874
|
-
process.stdout.write(`${
|
|
18139
|
+
process.stdout.write(`${GREEN2}OK${RESET5}
|
|
17875
18140
|
|
|
17876
18141
|
`);
|
|
17877
18142
|
} catch (e) {
|
|
@@ -17893,7 +18158,7 @@ ${DIM4}Authenticating with Azure AD...${RESET5} `);
|
|
|
17893
18158
|
CHRON_SENTINEL_DCR_ID: dcrId,
|
|
17894
18159
|
CHRON_SENTINEL_STREAM: stream
|
|
17895
18160
|
});
|
|
17896
|
-
process.stdout.write(`${
|
|
18161
|
+
process.stdout.write(`${GREEN2}${BOLD5}Connected!${RESET5} Test event sent to Sentinel workspace.
|
|
17897
18162
|
|
|
17898
18163
|
`);
|
|
17899
18164
|
process.stdout.write(`${DIM4}Config saved to ~/.chron/config.json \u2014 events will flow immediately in all running sessions.${RESET5}
|
|
@@ -17929,19 +18194,19 @@ Integrations:
|
|
|
17929
18194
|
}
|
|
17930
18195
|
}
|
|
17931
18196
|
}
|
|
17932
|
-
var import_readline,
|
|
18197
|
+
var import_readline, import_os6, import_path5, import_fs5, RESET5, BOLD5, DIM4, CYAN4, GREEN2, RED, YELLOW3;
|
|
17933
18198
|
var init_connect = __esm({
|
|
17934
18199
|
"src/cli/connect.ts"() {
|
|
17935
18200
|
"use strict";
|
|
17936
18201
|
import_readline = require("readline");
|
|
17937
|
-
|
|
17938
|
-
|
|
17939
|
-
|
|
18202
|
+
import_os6 = require("os");
|
|
18203
|
+
import_path5 = require("path");
|
|
18204
|
+
import_fs5 = require("fs");
|
|
17940
18205
|
RESET5 = "\x1B[0m";
|
|
17941
18206
|
BOLD5 = "\x1B[1m";
|
|
17942
18207
|
DIM4 = "\x1B[2m";
|
|
17943
18208
|
CYAN4 = "\x1B[36m";
|
|
17944
|
-
|
|
18209
|
+
GREEN2 = "\x1B[32m";
|
|
17945
18210
|
RED = "\x1B[31m";
|
|
17946
18211
|
YELLOW3 = "\x1B[33m";
|
|
17947
18212
|
}
|
|
@@ -18037,7 +18302,7 @@ ${BOLD6}Session Summary${RESET6}
|
|
|
18037
18302
|
process.stdout.write(` Duration ${stats.duration_minutes}m
|
|
18038
18303
|
|
|
18039
18304
|
`);
|
|
18040
|
-
process.stdout.write(`${BOLD6}Secrets touched${RESET6} ${secrets.length === 0 ? `${
|
|
18305
|
+
process.stdout.write(`${BOLD6}Secrets touched${RESET6} ${secrets.length === 0 ? `${GREEN3}none${RESET6}` : `${RED2}${secrets.length} detection(s)${RESET6}`}
|
|
18041
18306
|
`);
|
|
18042
18307
|
for (const s of secrets) {
|
|
18043
18308
|
process.stdout.write(` ${RED2}[${s.type}]${RESET6} ${DIM5}${s.masked_value}${RESET6}
|
|
@@ -18091,7 +18356,7 @@ async function runSummary(args2) {
|
|
|
18091
18356
|
printSummary(data);
|
|
18092
18357
|
}
|
|
18093
18358
|
}
|
|
18094
|
-
var RESET6, BOLD6, DIM5, CYAN5,
|
|
18359
|
+
var RESET6, BOLD6, DIM5, CYAN5, GREEN3, YELLOW4, RED2, MUTATION_PATTERNS, PROD_PATTERNS;
|
|
18095
18360
|
var init_summary = __esm({
|
|
18096
18361
|
"src/cli/summary.ts"() {
|
|
18097
18362
|
"use strict";
|
|
@@ -18102,7 +18367,7 @@ var init_summary = __esm({
|
|
|
18102
18367
|
BOLD6 = "\x1B[1m";
|
|
18103
18368
|
DIM5 = "\x1B[2m";
|
|
18104
18369
|
CYAN5 = "\x1B[36m";
|
|
18105
|
-
|
|
18370
|
+
GREEN3 = "\x1B[32m";
|
|
18106
18371
|
YELLOW4 = "\x1B[33m";
|
|
18107
18372
|
RED2 = "\x1B[31m";
|
|
18108
18373
|
MUTATION_PATTERNS = [
|
|
@@ -18118,6 +18383,419 @@ var init_summary = __esm({
|
|
|
18118
18383
|
}
|
|
18119
18384
|
});
|
|
18120
18385
|
|
|
18386
|
+
// src/cli/prune.ts
|
|
18387
|
+
var prune_exports = {};
|
|
18388
|
+
__export(prune_exports, {
|
|
18389
|
+
runPrune: () => runPrune
|
|
18390
|
+
});
|
|
18391
|
+
function configRetentionDays() {
|
|
18392
|
+
const configPath2 = (0, import_path6.join)((0, import_os7.homedir)(), ".chron", "config.json");
|
|
18393
|
+
if (!(0, import_fs6.existsSync)(configPath2))
|
|
18394
|
+
return null;
|
|
18395
|
+
try {
|
|
18396
|
+
const cfg = JSON.parse((0, import_fs6.readFileSync)(configPath2, "utf8"));
|
|
18397
|
+
const v = Number(cfg.retention_days);
|
|
18398
|
+
return isNaN(v) ? null : v;
|
|
18399
|
+
} catch {
|
|
18400
|
+
return null;
|
|
18401
|
+
}
|
|
18402
|
+
}
|
|
18403
|
+
function cutoffDate(days) {
|
|
18404
|
+
const d = /* @__PURE__ */ new Date();
|
|
18405
|
+
d.setDate(d.getDate() - days);
|
|
18406
|
+
return d.toISOString().slice(0, 10);
|
|
18407
|
+
}
|
|
18408
|
+
async function runPrune(args2) {
|
|
18409
|
+
const dryRun = args2.includes("--dry-run");
|
|
18410
|
+
const confirm = args2.includes("--confirm");
|
|
18411
|
+
const olderArg = args2.find((a) => a.startsWith("--older-than="))?.split("=")[1];
|
|
18412
|
+
if (!dryRun && !confirm) {
|
|
18413
|
+
process.stderr.write(
|
|
18414
|
+
"Usage: chron prune --older-than=<n>d [--dry-run | --confirm]\n\n --dry-run Show what would be deleted without deleting\n --confirm Actually delete (required for real deletion)\n\nExample: chron prune --older-than=90d --dry-run\n"
|
|
18415
|
+
);
|
|
18416
|
+
process.exit(1);
|
|
18417
|
+
}
|
|
18418
|
+
let days;
|
|
18419
|
+
if (olderArg) {
|
|
18420
|
+
days = parseInt(olderArg, 10);
|
|
18421
|
+
if (isNaN(days) || days <= 0) {
|
|
18422
|
+
process.stderr.write(`Invalid --older-than value: ${olderArg} (expected e.g. 90d)
|
|
18423
|
+
`);
|
|
18424
|
+
process.exit(1);
|
|
18425
|
+
}
|
|
18426
|
+
} else {
|
|
18427
|
+
const cfg = configRetentionDays();
|
|
18428
|
+
if (!cfg) {
|
|
18429
|
+
process.stderr.write(
|
|
18430
|
+
"No --older-than specified and no retention_days in ~/.chron/config.json\n"
|
|
18431
|
+
);
|
|
18432
|
+
process.exit(1);
|
|
18433
|
+
}
|
|
18434
|
+
days = cfg;
|
|
18435
|
+
process.stdout.write(`Using retention_days=${days} from config
|
|
18436
|
+
`);
|
|
18437
|
+
}
|
|
18438
|
+
const cutoff = cutoffDate(days);
|
|
18439
|
+
const db = await initDb();
|
|
18440
|
+
const stale = await db.select({
|
|
18441
|
+
id: sessions.id,
|
|
18442
|
+
title: sessions.title,
|
|
18443
|
+
updated_at: sessions.updated_at,
|
|
18444
|
+
msg_count: sql`count(${messages.id})`
|
|
18445
|
+
}).from(sessions).leftJoin(messages, sql`${messages.session_id} = ${sessions.id}`).where(lt(sessions.updated_at, cutoff)).groupBy(sessions.id);
|
|
18446
|
+
if (stale.length === 0) {
|
|
18447
|
+
process.stdout.write(`No sessions older than ${days}d (cutoff: ${cutoff}).
|
|
18448
|
+
`);
|
|
18449
|
+
return;
|
|
18450
|
+
}
|
|
18451
|
+
const totalMsgs = stale.reduce((s, r) => s + Number(r.msg_count), 0);
|
|
18452
|
+
process.stdout.write(`
|
|
18453
|
+
Sessions to prune (last active before ${cutoff}):
|
|
18454
|
+
|
|
18455
|
+
`);
|
|
18456
|
+
for (const row of stale) {
|
|
18457
|
+
process.stdout.write(` ${row.updated_at.slice(0, 10)} ${row.id.slice(0, 8)} ${row.msg_count} msgs ${row.title}
|
|
18458
|
+
`);
|
|
18459
|
+
}
|
|
18460
|
+
process.stdout.write(`
|
|
18461
|
+
Total: ${stale.length} session(s), ${totalMsgs} message(s)
|
|
18462
|
+
|
|
18463
|
+
`);
|
|
18464
|
+
if (dryRun) {
|
|
18465
|
+
process.stdout.write("Dry run \u2014 nothing deleted. Re-run with --confirm to delete.\n");
|
|
18466
|
+
return;
|
|
18467
|
+
}
|
|
18468
|
+
for (const row of stale) {
|
|
18469
|
+
await db.delete(sessions).where(sql`${sessions.id} = ${row.id}`);
|
|
18470
|
+
}
|
|
18471
|
+
process.stdout.write(`Pruned ${stale.length} session(s) and ${totalMsgs} message(s).
|
|
18472
|
+
`);
|
|
18473
|
+
}
|
|
18474
|
+
var import_fs6, import_path6, import_os7;
|
|
18475
|
+
var init_prune = __esm({
|
|
18476
|
+
"src/cli/prune.ts"() {
|
|
18477
|
+
"use strict";
|
|
18478
|
+
init_drizzle_orm();
|
|
18479
|
+
import_fs6 = require("fs");
|
|
18480
|
+
import_path6 = require("path");
|
|
18481
|
+
import_os7 = require("os");
|
|
18482
|
+
init_db2();
|
|
18483
|
+
init_schema();
|
|
18484
|
+
}
|
|
18485
|
+
});
|
|
18486
|
+
|
|
18487
|
+
// src/cli/sign.ts
|
|
18488
|
+
var sign_exports = {};
|
|
18489
|
+
__export(sign_exports, {
|
|
18490
|
+
runSign: () => runSign
|
|
18491
|
+
});
|
|
18492
|
+
async function runSign(args2) {
|
|
18493
|
+
const prefix = args2[0];
|
|
18494
|
+
if (!prefix) {
|
|
18495
|
+
process.stderr.write("Usage: chron sign <session-id-prefix>\n");
|
|
18496
|
+
process.exit(1);
|
|
18497
|
+
}
|
|
18498
|
+
const db = await initDb();
|
|
18499
|
+
const allSessions = await db.select().from(sessions);
|
|
18500
|
+
const session = allSessions.find((s) => s.id.startsWith(prefix));
|
|
18501
|
+
if (!session) {
|
|
18502
|
+
process.stderr.write(`Session not found: ${prefix}
|
|
18503
|
+
`);
|
|
18504
|
+
process.exit(1);
|
|
18505
|
+
}
|
|
18506
|
+
if (!session.public_key) {
|
|
18507
|
+
process.stderr.write(`Session ${session.id.slice(0, 8)} has no public key \u2014 was it created before v0.1.19?
|
|
18508
|
+
`);
|
|
18509
|
+
process.exit(1);
|
|
18510
|
+
}
|
|
18511
|
+
if (!(0, import_fs8.existsSync)(privKeyPath(session.id))) {
|
|
18512
|
+
process.stderr.write(`Private key not found: ${privKeyPath(session.id)}
|
|
18513
|
+
`);
|
|
18514
|
+
process.stderr.write("The key is stored on the machine where the session was created.\n");
|
|
18515
|
+
process.exit(1);
|
|
18516
|
+
}
|
|
18517
|
+
const [countRow] = await db.select({
|
|
18518
|
+
count: sql`count(*)`,
|
|
18519
|
+
first_created_at: sql`min(${messages.created_at})`
|
|
18520
|
+
}).from(messages).where(eq(messages.session_id, session.id));
|
|
18521
|
+
const messageCount = countRow?.count ?? 0;
|
|
18522
|
+
const firstCreatedAt = countRow?.first_created_at ?? "";
|
|
18523
|
+
if (messageCount === 0) {
|
|
18524
|
+
process.stderr.write("Session has no messages \u2014 nothing to sign.\n");
|
|
18525
|
+
process.exit(1);
|
|
18526
|
+
}
|
|
18527
|
+
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);
|
|
18528
|
+
const finalContentHash = lastMsg[lastMsg.length - 1]?.content_hash ?? "";
|
|
18529
|
+
if (!finalContentHash) {
|
|
18530
|
+
process.stderr.write("Session has no hash chain \u2014 cannot sign.\n");
|
|
18531
|
+
process.exit(1);
|
|
18532
|
+
}
|
|
18533
|
+
const signature = signSession(session.id, finalContentHash, messageCount, firstCreatedAt);
|
|
18534
|
+
await db.update(sessions).set({ signature }).where(eq(sessions.id, session.id));
|
|
18535
|
+
const sigData = {
|
|
18536
|
+
chron_signature: "v1",
|
|
18537
|
+
session_id: session.id,
|
|
18538
|
+
session_title: session.title,
|
|
18539
|
+
public_key_pem: session.public_key,
|
|
18540
|
+
message_count: messageCount,
|
|
18541
|
+
first_message_at: firstCreatedAt,
|
|
18542
|
+
final_content_hash: finalContentHash,
|
|
18543
|
+
signature,
|
|
18544
|
+
signed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
18545
|
+
};
|
|
18546
|
+
const sigFile = (0, import_path7.join)(process.cwd(), `${session.id.slice(0, 8)}.chron.sig`);
|
|
18547
|
+
(0, import_fs7.writeFileSync)(sigFile, JSON.stringify(sigData, null, 2));
|
|
18548
|
+
process.stdout.write(`Signed session ${session.id.slice(0, 8)}
|
|
18549
|
+
`);
|
|
18550
|
+
process.stdout.write(` messages : ${messageCount}
|
|
18551
|
+
`);
|
|
18552
|
+
process.stdout.write(` final hash: ${finalContentHash.slice(0, 16)}\u2026
|
|
18553
|
+
`);
|
|
18554
|
+
process.stdout.write(` sig file : ${sigFile}
|
|
18555
|
+
`);
|
|
18556
|
+
}
|
|
18557
|
+
var import_fs7, import_path7, import_fs8;
|
|
18558
|
+
var init_sign = __esm({
|
|
18559
|
+
"src/cli/sign.ts"() {
|
|
18560
|
+
"use strict";
|
|
18561
|
+
init_drizzle_orm();
|
|
18562
|
+
import_fs7 = require("fs");
|
|
18563
|
+
import_path7 = require("path");
|
|
18564
|
+
init_db2();
|
|
18565
|
+
init_schema();
|
|
18566
|
+
init_signing();
|
|
18567
|
+
import_fs8 = require("fs");
|
|
18568
|
+
}
|
|
18569
|
+
});
|
|
18570
|
+
|
|
18571
|
+
// src/utils/hash.ts
|
|
18572
|
+
function computeContentHash(sessionId, role, content, createdAt, prevHash, eventType) {
|
|
18573
|
+
const base = `${sessionId}|${role}|${content}|${createdAt}|${prevHash ?? ""}`;
|
|
18574
|
+
const input = eventType != null ? `${base}|${eventType}` : base;
|
|
18575
|
+
return (0, import_crypto4.createHash)("sha256").update(input).digest("hex");
|
|
18576
|
+
}
|
|
18577
|
+
var import_crypto4;
|
|
18578
|
+
var init_hash = __esm({
|
|
18579
|
+
"src/utils/hash.ts"() {
|
|
18580
|
+
"use strict";
|
|
18581
|
+
import_crypto4 = require("crypto");
|
|
18582
|
+
}
|
|
18583
|
+
});
|
|
18584
|
+
|
|
18585
|
+
// src/cli/verify.ts
|
|
18586
|
+
var verify_exports = {};
|
|
18587
|
+
__export(verify_exports, {
|
|
18588
|
+
runVerify: () => runVerify
|
|
18589
|
+
});
|
|
18590
|
+
function ok(msg) {
|
|
18591
|
+
process.stdout.write(` ${GREEN4}\u2713${RESET7} ${msg}
|
|
18592
|
+
`);
|
|
18593
|
+
}
|
|
18594
|
+
function fail(msg) {
|
|
18595
|
+
process.stdout.write(` ${RED3}\u2717${RESET7} ${msg}
|
|
18596
|
+
`);
|
|
18597
|
+
}
|
|
18598
|
+
function warn(msg) {
|
|
18599
|
+
process.stdout.write(` ${YELLOW5}!${RESET7} ${msg}
|
|
18600
|
+
`);
|
|
18601
|
+
}
|
|
18602
|
+
async function runVerifyBundle(bundlePath) {
|
|
18603
|
+
process.stdout.write(`
|
|
18604
|
+
${BOLD7}Verifying bundle${RESET7} ${bundlePath}
|
|
18605
|
+
|
|
18606
|
+
`);
|
|
18607
|
+
const tempDir = (0, import_path8.join)((0, import_os8.tmpdir)(), `chron-verify-${Date.now()}`);
|
|
18608
|
+
(0, import_fs9.mkdirSync)(tempDir, { recursive: true });
|
|
18609
|
+
try {
|
|
18610
|
+
(0, import_child_process2.execSync)(`tar -xzf "${bundlePath}" -C "${tempDir}"`, { stdio: "pipe" });
|
|
18611
|
+
} catch {
|
|
18612
|
+
process.stderr.write(`Failed to extract bundle: ${bundlePath}
|
|
18613
|
+
`);
|
|
18614
|
+
(0, import_fs9.rmSync)(tempDir, { recursive: true, force: true });
|
|
18615
|
+
process.exit(1);
|
|
18616
|
+
}
|
|
18617
|
+
let allOk = true;
|
|
18618
|
+
try {
|
|
18619
|
+
const manifestPath = (0, import_path8.join)(tempDir, "manifest.json");
|
|
18620
|
+
const manifestBytes = (0, import_fs9.readFileSync)(manifestPath);
|
|
18621
|
+
const manifest = JSON.parse(manifestBytes.toString());
|
|
18622
|
+
process.stdout.write(`${BOLD7}Manifest${RESET7} (${manifest.sessions.length} session(s), generated ${manifest.generated_at})
|
|
18623
|
+
`);
|
|
18624
|
+
process.stdout.write(`
|
|
18625
|
+
${BOLD7}File integrity${RESET7}
|
|
18626
|
+
`);
|
|
18627
|
+
for (const [rel, expectedHash] of Object.entries(manifest.files)) {
|
|
18628
|
+
const filePath = (0, import_path8.join)(tempDir, rel);
|
|
18629
|
+
try {
|
|
18630
|
+
const content = (0, import_fs9.readFileSync)(filePath);
|
|
18631
|
+
const actual = (0, import_fs9.createHash)("sha256").update(content).digest("hex");
|
|
18632
|
+
if (actual === expectedHash) {
|
|
18633
|
+
ok(rel);
|
|
18634
|
+
} else {
|
|
18635
|
+
fail(`${rel} \u2014 SHA256 mismatch (tampered)`);
|
|
18636
|
+
allOk = false;
|
|
18637
|
+
}
|
|
18638
|
+
} catch {
|
|
18639
|
+
fail(`${rel} \u2014 file missing from bundle`);
|
|
18640
|
+
allOk = false;
|
|
18641
|
+
}
|
|
18642
|
+
}
|
|
18643
|
+
process.stdout.write(`
|
|
18644
|
+
${BOLD7}Signature${RESET7}
|
|
18645
|
+
`);
|
|
18646
|
+
const sigPath = (0, import_path8.join)(tempDir, "manifest.sig");
|
|
18647
|
+
let sigFile = null;
|
|
18648
|
+
try {
|
|
18649
|
+
sigFile = JSON.parse((0, import_fs9.readFileSync)(sigPath).toString());
|
|
18650
|
+
} catch {
|
|
18651
|
+
warn("No manifest.sig \u2014 bundle was not signed");
|
|
18652
|
+
}
|
|
18653
|
+
if (sigFile?.session_id && sigFile?.signature) {
|
|
18654
|
+
const pubKeyPath = (0, import_path8.join)(tempDir, "pubkeys", `${sigFile.session_id}.pub`);
|
|
18655
|
+
try {
|
|
18656
|
+
const pubKey = (0, import_fs9.readFileSync)(pubKeyPath, "utf8");
|
|
18657
|
+
const valid = verifyBufferSignature(pubKey, sigFile.signature, manifestBytes);
|
|
18658
|
+
if (valid) {
|
|
18659
|
+
ok(`Ed25519 signature valid (session ${sigFile.session_id.slice(0, 8)})`);
|
|
18660
|
+
} else {
|
|
18661
|
+
fail("Ed25519 signature INVALID \u2014 manifest was altered after signing");
|
|
18662
|
+
allOk = false;
|
|
18663
|
+
}
|
|
18664
|
+
} catch {
|
|
18665
|
+
fail(`Public key not found for session ${sigFile.session_id.slice(0, 8)}`);
|
|
18666
|
+
allOk = false;
|
|
18667
|
+
}
|
|
18668
|
+
}
|
|
18669
|
+
process.stdout.write("\n");
|
|
18670
|
+
} finally {
|
|
18671
|
+
(0, import_fs9.rmSync)(tempDir, { recursive: true, force: true });
|
|
18672
|
+
}
|
|
18673
|
+
process.exit(allOk ? 0 : 1);
|
|
18674
|
+
}
|
|
18675
|
+
async function runVerify(args2) {
|
|
18676
|
+
const bundleArg = args2.find((a) => a.startsWith("--bundle="))?.split("=").slice(1).join("=");
|
|
18677
|
+
if (bundleArg) {
|
|
18678
|
+
await runVerifyBundle(bundleArg);
|
|
18679
|
+
return;
|
|
18680
|
+
}
|
|
18681
|
+
const prefix = args2.find((a) => !a.startsWith("--"));
|
|
18682
|
+
if (!prefix) {
|
|
18683
|
+
process.stderr.write(
|
|
18684
|
+
"Usage: chron verify <session-id-prefix>\n chron verify --bundle=<bundle.chron.tar.gz>\n"
|
|
18685
|
+
);
|
|
18686
|
+
process.exit(1);
|
|
18687
|
+
}
|
|
18688
|
+
const db = await initDb();
|
|
18689
|
+
const allSessions = await db.select().from(sessions);
|
|
18690
|
+
const session = allSessions.find((s) => s.id.startsWith(prefix));
|
|
18691
|
+
if (!session) {
|
|
18692
|
+
process.stderr.write(`Session not found: ${prefix}
|
|
18693
|
+
`);
|
|
18694
|
+
process.exit(1);
|
|
18695
|
+
}
|
|
18696
|
+
process.stdout.write(`
|
|
18697
|
+
${BOLD7}Verifying session ${session.id.slice(0, 8)}${RESET7} \u2014 ${session.title}
|
|
18698
|
+
|
|
18699
|
+
`);
|
|
18700
|
+
const rows = await db.select().from(messages).where(eq(messages.session_id, session.id)).orderBy(asc(messages.created_at), asc(sql`rowid`));
|
|
18701
|
+
process.stdout.write(`${BOLD7}Hash chain${RESET7} (${rows.length} messages)
|
|
18702
|
+
`);
|
|
18703
|
+
const chained = rows.filter((r) => r.content_hash !== null);
|
|
18704
|
+
let chainOk = true;
|
|
18705
|
+
if (chained.length === 0) {
|
|
18706
|
+
warn("No chained messages \u2014 session pre-dates hash chaining");
|
|
18707
|
+
} else {
|
|
18708
|
+
for (let i = 0; i < chained.length; i++) {
|
|
18709
|
+
const row = chained[i];
|
|
18710
|
+
const expectedPrev = i === 0 ? null : chained[i - 1].content_hash;
|
|
18711
|
+
if (row.prev_hash !== expectedPrev) {
|
|
18712
|
+
fail(`Row ${i + 1} (${row.id.slice(0, 8)}): prev_hash mismatch \u2014 chain broken`);
|
|
18713
|
+
chainOk = false;
|
|
18714
|
+
break;
|
|
18715
|
+
}
|
|
18716
|
+
const expected = computeContentHash(row.session_id, row.role, row.content, row.created_at, row.prev_hash, row.event_type ?? void 0);
|
|
18717
|
+
if (row.content_hash !== expected) {
|
|
18718
|
+
fail(`Row ${i + 1} (${row.id.slice(0, 8)}): content_hash mismatch \u2014 row tampered`);
|
|
18719
|
+
chainOk = false;
|
|
18720
|
+
break;
|
|
18721
|
+
}
|
|
18722
|
+
}
|
|
18723
|
+
if (chainOk)
|
|
18724
|
+
ok(`All ${chained.length} hashes valid`);
|
|
18725
|
+
}
|
|
18726
|
+
process.stdout.write(`
|
|
18727
|
+
${BOLD7}Clock attestation${RESET7}
|
|
18728
|
+
`);
|
|
18729
|
+
if (session.metadata) {
|
|
18730
|
+
try {
|
|
18731
|
+
const meta = JSON.parse(session.metadata);
|
|
18732
|
+
const status = meta.clock_sync_status ?? "unknown";
|
|
18733
|
+
if (status === "synchronized") {
|
|
18734
|
+
const offset = meta.clock_offset_ms !== void 0 ? ` (offset: ${meta.clock_offset_ms > 0 ? "+" : ""}${meta.clock_offset_ms}ms)` : "";
|
|
18735
|
+
ok(`Clock synchronized via NTP${offset}`);
|
|
18736
|
+
} else if (status === "not_synchronized") {
|
|
18737
|
+
fail(`Clock NOT synchronized with NTP \u2014 timing analysis unreliable`);
|
|
18738
|
+
} else {
|
|
18739
|
+
warn("Clock sync status unknown (NTP check failed or offline)");
|
|
18740
|
+
}
|
|
18741
|
+
} catch {
|
|
18742
|
+
warn("Could not parse session metadata");
|
|
18743
|
+
}
|
|
18744
|
+
} else {
|
|
18745
|
+
warn("No clock attestation \u2014 session created before NTP support (v0.1.24)");
|
|
18746
|
+
}
|
|
18747
|
+
process.stdout.write(`
|
|
18748
|
+
${BOLD7}Signature${RESET7}
|
|
18749
|
+
`);
|
|
18750
|
+
if (!session.public_key) {
|
|
18751
|
+
warn("No public key \u2014 session was created before signing support (v0.1.19)");
|
|
18752
|
+
} else if (!session.signature) {
|
|
18753
|
+
warn("Not yet signed \u2014 run: chron sign " + session.id.slice(0, 8));
|
|
18754
|
+
} else {
|
|
18755
|
+
const finalHash = chained[chained.length - 1]?.content_hash ?? "";
|
|
18756
|
+
const firstCreatedAt = rows[0]?.created_at ?? "";
|
|
18757
|
+
const sigValid = verifySignature(
|
|
18758
|
+
session.public_key,
|
|
18759
|
+
session.signature,
|
|
18760
|
+
session.id,
|
|
18761
|
+
finalHash,
|
|
18762
|
+
rows.length,
|
|
18763
|
+
firstCreatedAt
|
|
18764
|
+
);
|
|
18765
|
+
if (sigValid) {
|
|
18766
|
+
ok("Ed25519 signature valid");
|
|
18767
|
+
process.stdout.write(` ${DIM6}public key: ${session.public_key.split("\n")[1]?.slice(0, 40)}\u2026${RESET7}
|
|
18768
|
+
`);
|
|
18769
|
+
} else {
|
|
18770
|
+
fail("Ed25519 signature INVALID \u2014 session data may have been altered after signing");
|
|
18771
|
+
}
|
|
18772
|
+
}
|
|
18773
|
+
process.stdout.write("\n");
|
|
18774
|
+
const allOk = chainOk && (session.signature ? true : true);
|
|
18775
|
+
process.exit(allOk ? 0 : 1);
|
|
18776
|
+
}
|
|
18777
|
+
var import_fs9, import_path8, import_os8, import_child_process2, RESET7, BOLD7, GREEN4, RED3, DIM6, YELLOW5;
|
|
18778
|
+
var init_verify = __esm({
|
|
18779
|
+
"src/cli/verify.ts"() {
|
|
18780
|
+
"use strict";
|
|
18781
|
+
init_drizzle_orm();
|
|
18782
|
+
import_fs9 = require("fs");
|
|
18783
|
+
import_path8 = require("path");
|
|
18784
|
+
import_os8 = require("os");
|
|
18785
|
+
import_child_process2 = require("child_process");
|
|
18786
|
+
init_db2();
|
|
18787
|
+
init_schema();
|
|
18788
|
+
init_hash();
|
|
18789
|
+
init_signing();
|
|
18790
|
+
RESET7 = "\x1B[0m";
|
|
18791
|
+
BOLD7 = "\x1B[1m";
|
|
18792
|
+
GREEN4 = "\x1B[32m";
|
|
18793
|
+
RED3 = "\x1B[31m";
|
|
18794
|
+
DIM6 = "\x1B[2m";
|
|
18795
|
+
YELLOW5 = "\x1B[33m";
|
|
18796
|
+
}
|
|
18797
|
+
});
|
|
18798
|
+
|
|
18121
18799
|
// src/cli/index.ts
|
|
18122
18800
|
var [, , command, ...args] = process.argv;
|
|
18123
18801
|
async function main() {
|
|
@@ -18157,6 +18835,21 @@ async function main() {
|
|
|
18157
18835
|
await runSummary2(args);
|
|
18158
18836
|
break;
|
|
18159
18837
|
}
|
|
18838
|
+
case "prune": {
|
|
18839
|
+
const { runPrune: runPrune2 } = await Promise.resolve().then(() => (init_prune(), prune_exports));
|
|
18840
|
+
await runPrune2(args);
|
|
18841
|
+
break;
|
|
18842
|
+
}
|
|
18843
|
+
case "sign": {
|
|
18844
|
+
const { runSign: runSign2 } = await Promise.resolve().then(() => (init_sign(), sign_exports));
|
|
18845
|
+
await runSign2(args);
|
|
18846
|
+
break;
|
|
18847
|
+
}
|
|
18848
|
+
case "verify": {
|
|
18849
|
+
const { runVerify: runVerify2 } = await Promise.resolve().then(() => (init_verify(), verify_exports));
|
|
18850
|
+
await runVerify2(args);
|
|
18851
|
+
break;
|
|
18852
|
+
}
|
|
18160
18853
|
default: {
|
|
18161
18854
|
const name = command ? `Unknown command: ${command}
|
|
18162
18855
|
|
|
@@ -18172,9 +18865,13 @@ Commands:
|
|
|
18172
18865
|
settings View current configuration
|
|
18173
18866
|
connect Connect to a SIEM integration (crowdstrike, sentinel, splunk)
|
|
18174
18867
|
summary Structured summary of a session (timeline, mutations, secrets)
|
|
18868
|
+
sign Sign a session with its Ed25519 key \u2014 produces a .chron.sig file
|
|
18869
|
+
verify Verify a session's hash chain and Ed25519 signature
|
|
18870
|
+
prune Delete sessions older than a retention cutoff
|
|
18175
18871
|
|
|
18176
18872
|
Options (history):
|
|
18177
18873
|
--limit=<n> Max sessions to show (default: 20)
|
|
18874
|
+
--ref=<value> Filter by external_ref prefix (e.g. --ref=jira: or --ref=jira:ENG-123)
|
|
18178
18875
|
<id-prefix> Show full log for the session with this ID prefix
|
|
18179
18876
|
|
|
18180
18877
|
Options (report):
|
|
@@ -18182,8 +18879,21 @@ Options (report):
|
|
|
18182
18879
|
--format=soc2 Generate SOC 2 HTML evidence package
|
|
18183
18880
|
--output=<file> Output file for --format=soc2 (default: soc2-report.html)
|
|
18184
18881
|
|
|
18185
|
-
Options (export
|
|
18186
|
-
<id-prefix>
|
|
18882
|
+
Options (export):
|
|
18883
|
+
<id-prefix> Markdown export for a single session
|
|
18884
|
+
--signed Tamper-evident bundle (JSONL + manifest + Ed25519 sig)
|
|
18885
|
+
--session=<id> Filter bundle to a single session
|
|
18886
|
+
--since=<range> Filter bundle by date: 7d, 30d, or YYYY-MM-DD
|
|
18887
|
+
--output=<file> Output path (default: bundle.chron.tar.gz)
|
|
18888
|
+
|
|
18889
|
+
Options (verify):
|
|
18890
|
+
<id-prefix> Verify a session's hash chain + Ed25519 signature
|
|
18891
|
+
--bundle=<file> Verify a signed bundle offline (no DB needed)
|
|
18892
|
+
|
|
18893
|
+
Options (prune):
|
|
18894
|
+
--older-than=<n>d Cutoff in days (falls back to retention_days in config)
|
|
18895
|
+
--dry-run Show what would be deleted without deleting
|
|
18896
|
+
--confirm Required flag to actually delete
|
|
18187
18897
|
`
|
|
18188
18898
|
);
|
|
18189
18899
|
process.exit(command ? 1 : 0);
|