chron-mcp 0.1.13 → 0.1.17

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/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 join3(chunks, separator) {
1187
+ function join4(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 = join3;
1197
+ sql2.join = join4;
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 readFileSync = (path) => fs.readFileSync(path, "utf-8");
2809
+ var readFileSync2 = (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: readFileSync2,
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 } = require_filesystem();
2833
+ var { LDD_PATH, readFile, readFileSync: readFileSync2 } = 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 = readFileSync(LDD_PATH);
2914
+ const lddContent = readFileSync2(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 = readFileSync(LDD_PATH);
2971
+ const lddContent = readFileSync2(LDD_PATH);
2972
2972
  const versionMatch = lddContent.match(RE_GLIBC_VERSION);
2973
2973
  if (versionMatch) {
2974
2974
  cachedVersionFilesystem = versionMatch[1];
@@ -11068,8 +11068,8 @@ var init_stream3 = __esm({
11068
11068
  let promise;
11069
11069
  try {
11070
11070
  const request = createRequest();
11071
- const fetch = this.#fetch;
11072
- promise = fetch(request);
11071
+ const fetch2 = this.#fetch;
11072
+ promise = fetch2(request);
11073
11073
  } catch (error) {
11074
11074
  promise = Promise.reject(error);
11075
11075
  }
@@ -11136,11 +11136,11 @@ var init_stream3 = __esm({
11136
11136
 
11137
11137
  // node_modules/@libsql/hrana-client/lib-esm/http/client.js
11138
11138
  async function findEndpoint(customFetch, clientUrl) {
11139
- const fetch = customFetch;
11139
+ const fetch2 = customFetch;
11140
11140
  for (const endpoint of checkEndpoints) {
11141
11141
  const url = new URL(endpoint.versionPath, clientUrl);
11142
11142
  const request = new Request(url.toString(), { method: "GET" });
11143
- const response = await fetch(request);
11143
+ const response = await fetch2(request);
11144
11144
  await response.arrayBuffer();
11145
11145
  if (response.ok) {
11146
11146
  return endpoint;
@@ -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((join3) => join3.alias === tableName)) {
14255
+ if (typeof tableName === "string" && this.config.joins?.some((join4) => join4.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((join3) => join3.alias === tableName)) {
15144
+ if (typeof tableName === "string" && this.config.joins.some((join4) => join4.alias === tableName)) {
15145
15145
  throw new Error(`Alias "${tableName}" is already used in this query`);
15146
15146
  }
15147
15147
  if (typeof on === "function") {
@@ -17026,6 +17026,547 @@ var init_secrets = __esm({
17026
17026
  }
17027
17027
  });
17028
17028
 
17029
+ // package.json
17030
+ var require_package = __commonJS({
17031
+ "package.json"(exports2, module2) {
17032
+ module2.exports = {
17033
+ name: "chron-mcp",
17034
+ version: "0.1.17",
17035
+ mcpName: "io.github.sirinivask/chron",
17036
+ description: "Audit-grade timestamped logs for every AI conversation",
17037
+ repository: {
17038
+ type: "git",
17039
+ url: "https://github.com/sirinivask/chron.git"
17040
+ },
17041
+ main: "dist/index.js",
17042
+ vitest: {
17043
+ include: [
17044
+ "tests/**/*.test.ts"
17045
+ ],
17046
+ globals: true
17047
+ },
17048
+ bin: {
17049
+ "chron-mcp": "dist/index.js",
17050
+ chron: "dist/cli/index.js"
17051
+ },
17052
+ files: [
17053
+ "dist",
17054
+ "skills",
17055
+ ".claude-plugin",
17056
+ "assets",
17057
+ "dashboards",
17058
+ "README.md"
17059
+ ],
17060
+ engines: {
17061
+ node: ">=18"
17062
+ },
17063
+ scripts: {
17064
+ 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",
17065
+ "build:cli": "npx esbuild src/cli/index.ts --bundle --format=cjs --outfile=dist/cli/index.js --platform=node && chmod +x dist/cli/index.js",
17066
+ typecheck: "tsc --noEmit",
17067
+ dev: "tsc --watch",
17068
+ start: "node dist/index.js",
17069
+ test: "vitest run",
17070
+ "test:watch": "vitest",
17071
+ prepublishOnly: "npm test && npm run build"
17072
+ },
17073
+ keywords: [
17074
+ "mcp",
17075
+ "ai",
17076
+ "audit",
17077
+ "logging",
17078
+ "claude",
17079
+ "cursor"
17080
+ ],
17081
+ license: "SEE LICENSE IN LICENSE",
17082
+ dependencies: {
17083
+ "@libsql/client": "^0.17.3",
17084
+ "@modelcontextprotocol/sdk": "^1.12.0",
17085
+ "drizzle-orm": "^0.45.2",
17086
+ express: "^4.18.2",
17087
+ uuid: "^11.1.1",
17088
+ zod: "^3.22.4"
17089
+ },
17090
+ devDependencies: {
17091
+ "@types/express": "^4.17.21",
17092
+ "@types/node": "^20.0.0",
17093
+ "@types/uuid": "^9.0.0",
17094
+ "drizzle-kit": "^0.20.14",
17095
+ typescript: "^5.4.5",
17096
+ vitest: "^1.4.0"
17097
+ }
17098
+ };
17099
+ }
17100
+ });
17101
+
17102
+ // src/cli/connect.ts
17103
+ var connect_exports = {};
17104
+ __export(connect_exports, {
17105
+ runConnect: () => runConnect
17106
+ });
17107
+ function prompt(rl, question) {
17108
+ return new Promise((resolve) => rl.question(question, resolve));
17109
+ }
17110
+ function configPath() {
17111
+ return (0, import_path3.join)((0, import_os3.homedir)(), ".chron", "config.json");
17112
+ }
17113
+ function loadConfig() {
17114
+ try {
17115
+ return JSON.parse((0, import_fs2.readFileSync)(configPath(), "utf8"));
17116
+ } catch {
17117
+ return {};
17118
+ }
17119
+ }
17120
+ function saveConfig(data) {
17121
+ const dir = (0, import_path3.join)((0, import_os3.homedir)(), ".chron");
17122
+ (0, import_fs2.mkdirSync)(dir, { recursive: true });
17123
+ (0, import_fs2.writeFileSync)(configPath(), JSON.stringify(data, null, 2), "utf8");
17124
+ }
17125
+ function patchClaudeJson(vars) {
17126
+ const path = (0, import_path3.join)((0, import_os3.homedir)(), ".claude.json");
17127
+ if (!(0, import_fs2.existsSync)(path))
17128
+ return false;
17129
+ try {
17130
+ const raw = (0, import_fs2.readFileSync)(path, "utf8");
17131
+ const doc = JSON.parse(raw);
17132
+ const servers = doc.mcpServers ?? {};
17133
+ const chron = servers.chron ?? {};
17134
+ chron.env = { ...chron.env ?? {}, ...vars };
17135
+ servers.chron = chron;
17136
+ doc.mcpServers = servers;
17137
+ (0, import_fs2.writeFileSync)(path, JSON.stringify(doc, null, 2), "utf8");
17138
+ return true;
17139
+ } catch {
17140
+ return false;
17141
+ }
17142
+ }
17143
+ async function connectCrowdStrike() {
17144
+ process.stdout.write(`
17145
+ ${BOLD5}Connect Chron \u2192 CrowdStrike LogScale${RESET5}
17146
+
17147
+ `);
17148
+ process.stdout.write(`${DIM4}Events will be sent directly to your LogScale repository.
17149
+ `);
17150
+ process.stdout.write(`No message content is transmitted \u2014 metadata only.${RESET5}
17151
+
17152
+ `);
17153
+ const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
17154
+ let url;
17155
+ let token;
17156
+ try {
17157
+ url = (await prompt(rl, ` LogScale ingest URL ${DIM4}(e.g. https://cloud.us.humio.com/api/v1/ingest/humio-structured)${RESET5}
17158
+ ${CYAN4}>${RESET5} `)).trim();
17159
+ if (!url.startsWith("https://")) {
17160
+ process.stdout.write(`
17161
+ ${RED}URL must start with https://${RESET5}
17162
+ `);
17163
+ process.exit(1);
17164
+ }
17165
+ token = (await prompt(rl, `
17166
+ LogScale ingest token
17167
+ ${CYAN4}>${RESET5} `)).trim();
17168
+ if (!token) {
17169
+ process.stdout.write(`
17170
+ ${RED}Token cannot be empty${RESET5}
17171
+ `);
17172
+ process.exit(1);
17173
+ }
17174
+ } finally {
17175
+ rl.close();
17176
+ }
17177
+ process.stdout.write(`
17178
+ ${DIM4}Sending test event...${RESET5} `);
17179
+ const testPayload = JSON.stringify([{
17180
+ tags: { host: require("os").hostname(), source: "chron-mcp" },
17181
+ events: [{
17182
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
17183
+ attributes: {
17184
+ event_type: "connection_test",
17185
+ chron_version: require_package().version,
17186
+ os: process.platform,
17187
+ message: "chron connect crowdstrike \u2014 test event"
17188
+ }
17189
+ }]
17190
+ }]);
17191
+ let ok = false;
17192
+ let statusCode = 0;
17193
+ try {
17194
+ const res = await fetch(url, {
17195
+ method: "POST",
17196
+ headers: {
17197
+ "Authorization": `Bearer ${token}`,
17198
+ "Content-Type": "application/json"
17199
+ },
17200
+ body: testPayload
17201
+ });
17202
+ statusCode = res.status;
17203
+ ok = res.ok;
17204
+ } catch (e) {
17205
+ process.stdout.write(`${RED}failed${RESET5}
17206
+ `);
17207
+ process.stderr.write(` ${RED}Error: ${e.message}${RESET5}
17208
+
17209
+ `);
17210
+ process.exit(1);
17211
+ }
17212
+ if (!ok) {
17213
+ process.stdout.write(`${RED}failed (HTTP ${statusCode})${RESET5}
17214
+ `);
17215
+ process.stderr.write(` ${RED}Check your URL and token, then try again.${RESET5}
17216
+
17217
+ `);
17218
+ process.exit(1);
17219
+ }
17220
+ process.stdout.write(`${GREEN}OK${RESET5}
17221
+
17222
+ `);
17223
+ const config = loadConfig();
17224
+ config.logscale = { url, connected_at: (/* @__PURE__ */ new Date()).toISOString() };
17225
+ saveConfig(config);
17226
+ process.stdout.write(`${GREEN}${BOLD5}Connected!${RESET5} Test event appeared in LogScale.
17227
+
17228
+ `);
17229
+ process.stdout.write(`${BOLD5}Add these env vars to your MCP client config:${RESET5}
17230
+
17231
+ `);
17232
+ process.stdout.write(` ${CYAN4}CHRON_LOGSCALE_URL${RESET5} ${url}
17233
+ `);
17234
+ process.stdout.write(` ${CYAN4}CHRON_LOGSCALE_TOKEN${RESET5} ${DIM4}<your-token>${RESET5}
17235
+
17236
+ `);
17237
+ process.stdout.write(`${DIM4}For Claude Code \u2014 add to the "env" block of the chron entry in ~/.claude.json:${RESET5}
17238
+
17239
+ `);
17240
+ process.stdout.write(` ${DIM4}"env": {
17241
+ `);
17242
+ process.stdout.write(` "CHRON_LOGSCALE_URL": "${url}",
17243
+ `);
17244
+ process.stdout.write(` "CHRON_LOGSCALE_TOKEN": "<your-token>"
17245
+ `);
17246
+ process.stdout.write(` }${RESET5}
17247
+
17248
+ `);
17249
+ process.stdout.write(`${DIM4}Connection saved to ~/.chron/config.json${RESET5}
17250
+
17251
+ `);
17252
+ }
17253
+ function isLocalhost(url) {
17254
+ return url.includes("localhost") || url.includes("127.0.0.1") || url.includes("::1");
17255
+ }
17256
+ async function connectSplunk() {
17257
+ process.stdout.write(`
17258
+ ${BOLD5}Connect Chron \u2192 Splunk${RESET5}
17259
+
17260
+ `);
17261
+ process.stdout.write(`${DIM4}Chron will send session telemetry to your Splunk instance via HTTP Event
17262
+ `);
17263
+ process.stdout.write(`Collector (HEC). Message content is never transmitted.${RESET5}
17264
+
17265
+ `);
17266
+ process.stdout.write(`${DIM4}Works with Splunk Enterprise, Splunk Cloud, and local Docker instances.
17267
+ `);
17268
+ process.stdout.write(`See dashboards/splunk/README.md for setup steps.${RESET5}
17269
+
17270
+ `);
17271
+ process.stdout.write(`${DIM4}Note: Splunk 9.x Docker uses HTTPS on port 8088 with a self-signed cert.
17272
+ `);
17273
+ process.stdout.write(`Use https://localhost:8088 \u2014 certificate verification is skipped for localhost.${RESET5}
17274
+
17275
+ `);
17276
+ const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
17277
+ let url, token;
17278
+ try {
17279
+ url = (await prompt(rl, ` Splunk HEC base URL ${DIM4}(e.g. https://localhost:8088 or https://your.splunkcloud.com:8088)${RESET5}
17280
+ ${CYAN4}>${RESET5} `)).trim();
17281
+ token = (await prompt(rl, `
17282
+ HEC token
17283
+ ${CYAN4}>${RESET5} `)).trim();
17284
+ } finally {
17285
+ rl.close();
17286
+ }
17287
+ if (!url.startsWith("http")) {
17288
+ process.stdout.write(`
17289
+ ${RED}URL must start with http:// or https://${RESET5}
17290
+ `);
17291
+ process.exit(1);
17292
+ }
17293
+ if (!token) {
17294
+ process.stdout.write(`
17295
+ ${RED}HEC token cannot be empty${RESET5}
17296
+ `);
17297
+ process.exit(1);
17298
+ }
17299
+ if (isLocalhost(url)) {
17300
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
17301
+ }
17302
+ process.stdout.write(`
17303
+ ${DIM4}Sending test event...${RESET5} `);
17304
+ const hecUrl = `${url.replace(/\/$/, "")}/services/collector/event`;
17305
+ const testPayload = JSON.stringify({
17306
+ time: Date.now() / 1e3,
17307
+ host: require("os").hostname(),
17308
+ source: "chron-mcp",
17309
+ sourcetype: "chron:event",
17310
+ event: {
17311
+ event_type: "connection_test",
17312
+ chron_version: require_package().version,
17313
+ os: process.platform,
17314
+ message: "chron connect splunk \u2014 test event"
17315
+ }
17316
+ });
17317
+ try {
17318
+ const res = await fetch(hecUrl, {
17319
+ method: "POST",
17320
+ headers: {
17321
+ "Authorization": `Splunk ${token}`,
17322
+ "Content-Type": "application/json"
17323
+ },
17324
+ body: testPayload
17325
+ });
17326
+ if (!res.ok) {
17327
+ const err = await res.text();
17328
+ process.stdout.write(`${RED}failed (HTTP ${res.status})${RESET5}
17329
+ `);
17330
+ process.stderr.write(` ${RED}${err}${RESET5}
17331
+
17332
+ `);
17333
+ process.exit(1);
17334
+ }
17335
+ process.stdout.write(`${GREEN}OK${RESET5}
17336
+
17337
+ `);
17338
+ } catch (e) {
17339
+ process.stdout.write(`${RED}failed${RESET5}
17340
+ `);
17341
+ process.stderr.write(` ${RED}Error: ${e.message}${RESET5}
17342
+
17343
+ `);
17344
+ process.exit(1);
17345
+ }
17346
+ const config = loadConfig();
17347
+ config.splunk = { url, token, insecure: isLocalhost(url), connected_at: (/* @__PURE__ */ new Date()).toISOString() };
17348
+ saveConfig(config);
17349
+ const splunkVars = { CHRON_SPLUNK_URL: url, CHRON_SPLUNK_TOKEN: token };
17350
+ if (isLocalhost(url))
17351
+ splunkVars.CHRON_SPLUNK_INSECURE = "1";
17352
+ patchClaudeJson(splunkVars);
17353
+ process.stdout.write(`${GREEN}${BOLD5}Connected!${RESET5} Splunk is receiving chron events.
17354
+
17355
+ `);
17356
+ process.stdout.write(`${DIM4}Config saved to ~/.chron/config.json \u2014 events will flow immediately in all running sessions.${RESET5}
17357
+
17358
+ `);
17359
+ }
17360
+ async function connectSentinel() {
17361
+ process.stdout.write(`
17362
+ ${BOLD5}Connect Chron \u2192 Microsoft Sentinel${RESET5}
17363
+
17364
+ `);
17365
+ process.stdout.write(`${DIM4}Chron will send session telemetry to your Sentinel workspace via the
17366
+ `);
17367
+ process.stdout.write(`Azure Monitor Logs Ingestion API. Message content is never transmitted.${RESET5}
17368
+
17369
+ `);
17370
+ process.stdout.write(`${DIM4}Prerequisites: Azure App Registration, Data Collection Endpoint (DCE),
17371
+ `);
17372
+ process.stdout.write(`and a Data Collection Rule (DCR) pointing to a ChronEvents_CL custom table.
17373
+ `);
17374
+ process.stdout.write(`See dashboards/sentinel/README.md for full setup steps.${RESET5}
17375
+
17376
+ `);
17377
+ const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
17378
+ let tenantId, clientId, clientSecret, dce, dcrId, stream;
17379
+ try {
17380
+ tenantId = (await prompt(rl, ` Azure Tenant ID ${DIM4}(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)${RESET5}
17381
+ ${CYAN4}>${RESET5} `)).trim();
17382
+ clientId = (await prompt(rl, `
17383
+ App Registration Client ID
17384
+ ${CYAN4}>${RESET5} `)).trim();
17385
+ clientSecret = (await prompt(rl, `
17386
+ Client Secret
17387
+ ${CYAN4}>${RESET5} `)).trim();
17388
+ dce = (await prompt(rl, `
17389
+ Data Collection Endpoint URL ${DIM4}(https://...)${RESET5}
17390
+ ${CYAN4}>${RESET5} `)).trim();
17391
+ dcrId = (await prompt(rl, `
17392
+ DCR Immutable ID ${DIM4}(dcr-xxxx)${RESET5}
17393
+ ${CYAN4}>${RESET5} `)).trim();
17394
+ stream = (await prompt(rl, `
17395
+ Stream name ${DIM4}(press Enter for Custom-ChronEvents_CL)${RESET5}
17396
+ ${CYAN4}>${RESET5} `)).trim() || "Custom-ChronEvents_CL";
17397
+ } finally {
17398
+ rl.close();
17399
+ }
17400
+ if (!tenantId || !clientId || !clientSecret) {
17401
+ process.stdout.write(`
17402
+ ${RED}Tenant ID, Client ID, and Client Secret are all required.${RESET5}
17403
+ `);
17404
+ process.exit(1);
17405
+ }
17406
+ if (!dce.startsWith("https://")) {
17407
+ process.stdout.write(`
17408
+ ${RED}DCE URL must start with https://${RESET5}
17409
+ `);
17410
+ process.exit(1);
17411
+ }
17412
+ if (dcrId && !dcrId.startsWith("dcr-")) {
17413
+ process.stdout.write(`
17414
+ ${YELLOW3}Warning: DCR Immutable ID usually starts with "dcr-"${RESET5}
17415
+ `);
17416
+ }
17417
+ process.stdout.write(`
17418
+ ${DIM4}Authenticating with Azure AD...${RESET5} `);
17419
+ let accessToken;
17420
+ try {
17421
+ const tokenRes = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
17422
+ method: "POST",
17423
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
17424
+ body: new URLSearchParams({
17425
+ grant_type: "client_credentials",
17426
+ client_id: clientId,
17427
+ client_secret: clientSecret,
17428
+ scope: "https://monitor.azure.com/.default"
17429
+ }).toString()
17430
+ });
17431
+ if (!tokenRes.ok) {
17432
+ const err = await tokenRes.text();
17433
+ process.stdout.write(`${RED}failed (HTTP ${tokenRes.status})${RESET5}
17434
+ `);
17435
+ process.stderr.write(` ${RED}${err}${RESET5}
17436
+
17437
+ `);
17438
+ process.exit(1);
17439
+ }
17440
+ const tokenData = await tokenRes.json();
17441
+ accessToken = tokenData.access_token;
17442
+ process.stdout.write(`${GREEN}OK${RESET5}
17443
+ `);
17444
+ } catch (e) {
17445
+ process.stdout.write(`${RED}failed${RESET5}
17446
+ `);
17447
+ process.stderr.write(` ${RED}Error: ${e.message}${RESET5}
17448
+
17449
+ `);
17450
+ process.exit(1);
17451
+ }
17452
+ process.stdout.write(`${DIM4}Sending test event...${RESET5} `);
17453
+ const ingestUrl = `${dce}/dataCollectionRules/${dcrId}/streams/${stream}?api-version=2023-01-01`;
17454
+ const testRecord = {
17455
+ TimeGenerated: (/* @__PURE__ */ new Date()).toISOString(),
17456
+ EventType: "connection_test",
17457
+ SessionIdPrefix: "test",
17458
+ AiTool: "",
17459
+ OS: process.platform,
17460
+ ChronVersion: require_package().version,
17461
+ Computer: require("os").hostname(),
17462
+ Role: "",
17463
+ DetectionType: "",
17464
+ MaskedValue: ""
17465
+ };
17466
+ try {
17467
+ const res = await fetch(ingestUrl, {
17468
+ method: "POST",
17469
+ headers: {
17470
+ "Authorization": `Bearer ${accessToken}`,
17471
+ "Content-Type": "application/json"
17472
+ },
17473
+ body: JSON.stringify([testRecord])
17474
+ });
17475
+ if (!res.ok) {
17476
+ const err = await res.text();
17477
+ process.stdout.write(`${RED}failed (HTTP ${res.status})${RESET5}
17478
+ `);
17479
+ process.stderr.write(` ${RED}${err}${RESET5}
17480
+
17481
+ `);
17482
+ process.exit(1);
17483
+ }
17484
+ process.stdout.write(`${GREEN}OK${RESET5}
17485
+
17486
+ `);
17487
+ } catch (e) {
17488
+ process.stdout.write(`${RED}failed${RESET5}
17489
+ `);
17490
+ process.stderr.write(` ${RED}Error: ${e.message}${RESET5}
17491
+
17492
+ `);
17493
+ process.exit(1);
17494
+ }
17495
+ const config = loadConfig();
17496
+ config.sentinel = { dce, dcrId, stream, tenantId, clientId, connected_at: (/* @__PURE__ */ new Date()).toISOString() };
17497
+ saveConfig(config);
17498
+ process.stdout.write(`${GREEN}${BOLD5}Connected!${RESET5} Test event sent to Sentinel workspace.
17499
+
17500
+ `);
17501
+ process.stdout.write(`${BOLD5}Add these env vars to your MCP client config:${RESET5}
17502
+
17503
+ `);
17504
+ process.stdout.write(` ${CYAN4}CHRON_SENTINEL_TENANT_ID${RESET5} ${tenantId}
17505
+ `);
17506
+ process.stdout.write(` ${CYAN4}CHRON_SENTINEL_CLIENT_ID${RESET5} ${clientId}
17507
+ `);
17508
+ process.stdout.write(` ${CYAN4}CHRON_SENTINEL_CLIENT_SECRET${RESET5} ${DIM4}<your-client-secret>${RESET5}
17509
+ `);
17510
+ process.stdout.write(` ${CYAN4}CHRON_SENTINEL_DCE${RESET5} ${dce}
17511
+ `);
17512
+ process.stdout.write(` ${CYAN4}CHRON_SENTINEL_DCR_ID${RESET5} ${dcrId}
17513
+ `);
17514
+ process.stdout.write(` ${CYAN4}CHRON_SENTINEL_STREAM${RESET5} ${stream}
17515
+
17516
+ `);
17517
+ process.stdout.write(`${DIM4}For Claude Code \u2014 add to the "env" block of the chron entry in ~/.claude.json.
17518
+ `);
17519
+ process.stdout.write(`Connection saved to ~/.chron/config.json${RESET5}
17520
+
17521
+ `);
17522
+ }
17523
+ async function runConnect(args2) {
17524
+ const [subcommand] = args2;
17525
+ switch (subcommand) {
17526
+ case "crowdstrike":
17527
+ await connectCrowdStrike();
17528
+ break;
17529
+ case "sentinel":
17530
+ await connectSentinel();
17531
+ break;
17532
+ case "splunk":
17533
+ await connectSplunk();
17534
+ break;
17535
+ default: {
17536
+ const name = subcommand ? `Unknown integration: ${subcommand}
17537
+
17538
+ ` : "";
17539
+ process.stdout.write(
17540
+ `${name}Usage: chron connect <integration>
17541
+
17542
+ Integrations:
17543
+ crowdstrike Connect to CrowdStrike LogScale (direct ingest)
17544
+ sentinel Connect to Microsoft Sentinel (Azure Monitor Logs Ingestion API)
17545
+ splunk Connect to Splunk via HTTP Event Collector (HEC)
17546
+ `
17547
+ );
17548
+ process.exit(subcommand ? 1 : 0);
17549
+ }
17550
+ }
17551
+ }
17552
+ var import_readline, import_os3, import_path3, import_fs2, RESET5, BOLD5, DIM4, CYAN4, GREEN, RED, YELLOW3;
17553
+ var init_connect = __esm({
17554
+ "src/cli/connect.ts"() {
17555
+ "use strict";
17556
+ import_readline = require("readline");
17557
+ import_os3 = require("os");
17558
+ import_path3 = require("path");
17559
+ import_fs2 = require("fs");
17560
+ RESET5 = "\x1B[0m";
17561
+ BOLD5 = "\x1B[1m";
17562
+ DIM4 = "\x1B[2m";
17563
+ CYAN4 = "\x1B[36m";
17564
+ GREEN = "\x1B[32m";
17565
+ RED = "\x1B[31m";
17566
+ YELLOW3 = "\x1B[33m";
17567
+ }
17568
+ });
17569
+
17029
17570
  // src/cli/index.ts
17030
17571
  var [, , command, ...args] = process.argv;
17031
17572
  async function main() {
@@ -17055,6 +17596,11 @@ async function main() {
17055
17596
  await runSecrets2(args);
17056
17597
  break;
17057
17598
  }
17599
+ case "connect": {
17600
+ const { runConnect: runConnect2 } = await Promise.resolve().then(() => (init_connect(), connect_exports));
17601
+ await runConnect2(args);
17602
+ break;
17603
+ }
17058
17604
  default: {
17059
17605
  const name = command ? `Unknown command: ${command}
17060
17606
 
@@ -17068,6 +17614,7 @@ Commands:
17068
17614
  export Export a session as markdown
17069
17615
  secrets List detected secrets across sessions
17070
17616
  settings View current configuration
17617
+ connect Connect to a SIEM integration (crowdstrike, sentinel, splunk)
17071
17618
 
17072
17619
  Options (history):
17073
17620
  --limit=<n> Max sessions to show (default: 20)