@caik.dev/cli 0.1.6 → 0.1.8

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.
Files changed (2) hide show
  1. package/dist/index.js +256 -17
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import { readFileSync as readFileSync9 } from "fs";
5
+ import { readFileSync as readFileSync10 } from "fs";
6
6
  import { fileURLToPath } from "url";
7
- import { dirname as dirname5, join as join9 } from "path";
7
+ import { dirname as dirname5, join as join10 } from "path";
8
8
  import chalk3 from "chalk";
9
9
 
10
10
  // src/errors.ts
@@ -2585,12 +2585,57 @@ function registerKarmaCommand(program2) {
2585
2585
  }
2586
2586
 
2587
2587
  // src/commands/hook.ts
2588
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, existsSync as existsSync11 } from "fs";
2588
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, existsSync as existsSync11, appendFileSync } from "fs";
2589
2589
  import { join as join7 } from "path";
2590
2590
  import { homedir as homedir6 } from "os";
2591
- var PENDING_EVENTS_PATH = join7(homedir6(), ".caik", "pending-events.json");
2591
+ var CAIK_DIR = join7(homedir6(), ".caik");
2592
+ var PENDING_EVENTS_PATH = join7(CAIK_DIR, "pending-events.json");
2593
+ var AUDIT_LOG_PATH = join7(CAIK_DIR, "audit.log");
2592
2594
  var FLUSH_THRESHOLD = 50;
2593
2595
  var API_TIMEOUT_MS = 2e3;
2596
+ function readStdin() {
2597
+ return new Promise((resolve3) => {
2598
+ if (process.stdin.isTTY) {
2599
+ resolve3("");
2600
+ return;
2601
+ }
2602
+ let data = "";
2603
+ process.stdin.setEncoding("utf-8");
2604
+ process.stdin.on("data", (chunk) => {
2605
+ data += chunk;
2606
+ });
2607
+ process.stdin.on("end", () => resolve3(data));
2608
+ setTimeout(() => resolve3(data), 500);
2609
+ });
2610
+ }
2611
+ function parseStdin(raw) {
2612
+ if (!raw.trim()) return null;
2613
+ try {
2614
+ return JSON.parse(raw);
2615
+ } catch {
2616
+ return null;
2617
+ }
2618
+ }
2619
+ function shouldSendTelemetry() {
2620
+ const config = readConfig();
2621
+ const level = config.contributionLevel ?? "contributor";
2622
+ return level !== "none";
2623
+ }
2624
+ function shouldSendToolUse() {
2625
+ const config = readConfig();
2626
+ const level = config.contributionLevel ?? "contributor";
2627
+ return level === "contributor" || level === "collective";
2628
+ }
2629
+ function auditLog(event) {
2630
+ try {
2631
+ if (!existsSync11(CAIK_DIR)) {
2632
+ mkdirSync8(CAIK_DIR, { recursive: true, mode: 448 });
2633
+ }
2634
+ const line = JSON.stringify(event) + "\n";
2635
+ appendFileSync(AUDIT_LOG_PATH, line, "utf-8");
2636
+ } catch {
2637
+ }
2638
+ }
2594
2639
  function readPendingEvents() {
2595
2640
  try {
2596
2641
  if (!existsSync11(PENDING_EVENTS_PATH)) return [];
@@ -2602,9 +2647,8 @@ function readPendingEvents() {
2602
2647
  }
2603
2648
  }
2604
2649
  function writePendingEvents(events) {
2605
- const dir = join7(homedir6(), ".caik");
2606
- if (!existsSync11(dir)) {
2607
- mkdirSync8(dir, { recursive: true, mode: 448 });
2650
+ if (!existsSync11(CAIK_DIR)) {
2651
+ mkdirSync8(CAIK_DIR, { recursive: true, mode: 448 });
2608
2652
  }
2609
2653
  writeFileSync8(PENDING_EVENTS_PATH, JSON.stringify(events, null, 2) + "\n", "utf-8");
2610
2654
  }
@@ -2654,12 +2698,20 @@ function registerHookCommand(program2) {
2654
2698
  const hook = program2.command("hook").description("Platform hook callbacks (observation only, never gates agent actions)");
2655
2699
  hook.command("session-start").description("Log session start event").option("--platform <name>", "Platform name", "claude-code").action(async (opts) => {
2656
2700
  try {
2657
- const client = createClient(program2);
2701
+ if (!shouldSendTelemetry()) {
2702
+ process.exit(0);
2703
+ return;
2704
+ }
2705
+ const raw = await readStdin();
2706
+ const input = parseStdin(raw);
2658
2707
  const event = {
2659
2708
  type: "session_start",
2660
2709
  platform: opts.platform ?? "claude-code",
2710
+ sessionId: input?.session_id,
2661
2711
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2662
2712
  };
2713
+ auditLog(event);
2714
+ const client = createClient(program2);
2663
2715
  await postSingleEventWithTimeout(client, event);
2664
2716
  } catch {
2665
2717
  }
@@ -2667,28 +2719,47 @@ function registerHookCommand(program2) {
2667
2719
  });
2668
2720
  hook.command("session-end").description("Flush pending events and log session end").option("--platform <name>", "Platform name", "claude-code").action(async (opts) => {
2669
2721
  try {
2670
- const client = createClient(program2);
2722
+ if (!shouldSendTelemetry()) {
2723
+ clearPendingEvents();
2724
+ process.exit(0);
2725
+ return;
2726
+ }
2727
+ const raw = await readStdin();
2728
+ const input = parseStdin(raw);
2671
2729
  const pending = readPendingEvents();
2672
2730
  const endEvent = {
2673
2731
  type: "session_end",
2674
2732
  platform: opts.platform ?? "claude-code",
2733
+ sessionId: input?.session_id,
2675
2734
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2676
2735
  };
2677
2736
  pending.push(endEvent);
2737
+ auditLog(endEvent);
2738
+ const client = createClient(program2);
2678
2739
  await postEventsWithTimeout(client, pending);
2679
2740
  } catch {
2680
2741
  }
2681
2742
  process.exit(0);
2682
2743
  });
2683
- hook.command("post-tool-use").description("Buffer a tool-use event").option("--platform <name>", "Platform name", "claude-code").option("--tool <name>", "Tool name").option("--success <bool>", "Whether the tool call succeeded").action(async (opts) => {
2744
+ hook.command("post-tool-use").description("Buffer a tool-use event").option("--platform <name>", "Platform name", "claude-code").option("--tool <name>", "Tool name (fallback if stdin unavailable)").option("--success <bool>", "Success flag (fallback if stdin unavailable)").action(async (opts) => {
2684
2745
  try {
2746
+ if (!shouldSendToolUse()) {
2747
+ process.exit(0);
2748
+ return;
2749
+ }
2750
+ const raw = await readStdin();
2751
+ const input = parseStdin(raw);
2752
+ const toolName = input?.tool_name ?? opts.tool;
2753
+ const success2 = input?.tool_response?.success ?? opts.success === "true" ?? false;
2685
2754
  const event = {
2686
2755
  type: "tool_use",
2687
2756
  platform: opts.platform ?? "claude-code",
2688
- tool: opts.tool,
2689
- success: opts.success === "true" || opts.success === true,
2757
+ sessionId: input?.session_id,
2758
+ tool: toolName,
2759
+ success: Boolean(success2),
2690
2760
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2691
2761
  };
2762
+ auditLog(event);
2692
2763
  const events = bufferEvent(event);
2693
2764
  if (events.length >= FLUSH_THRESHOLD) {
2694
2765
  const client = createClient(program2);
@@ -2700,12 +2771,17 @@ function registerHookCommand(program2) {
2700
2771
  });
2701
2772
  hook.command("cursor-session-start").description("Log Cursor session start event").action(async () => {
2702
2773
  try {
2703
- const client = createClient(program2);
2774
+ if (!shouldSendTelemetry()) {
2775
+ process.exit(0);
2776
+ return;
2777
+ }
2704
2778
  const event = {
2705
2779
  type: "session_start",
2706
2780
  platform: "cursor",
2707
2781
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2708
2782
  };
2783
+ auditLog(event);
2784
+ const client = createClient(program2);
2709
2785
  await postSingleEventWithTimeout(client, event);
2710
2786
  } catch {
2711
2787
  }
@@ -2713,6 +2789,10 @@ function registerHookCommand(program2) {
2713
2789
  });
2714
2790
  hook.command("cursor-mcp-exec").description("Buffer a Cursor MCP tool execution event").option("--server <name>", "MCP server name").option("--tool <name>", "Tool name").action(async (opts) => {
2715
2791
  try {
2792
+ if (!shouldSendToolUse()) {
2793
+ process.exit(0);
2794
+ return;
2795
+ }
2716
2796
  const event = {
2717
2797
  type: "mcp_exec",
2718
2798
  platform: "cursor",
@@ -2720,6 +2800,7 @@ function registerHookCommand(program2) {
2720
2800
  tool: opts.tool,
2721
2801
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2722
2802
  };
2803
+ auditLog(event);
2723
2804
  const events = bufferEvent(event);
2724
2805
  if (events.length >= FLUSH_THRESHOLD) {
2725
2806
  const client = createClient(program2);
@@ -2731,7 +2812,11 @@ function registerHookCommand(program2) {
2731
2812
  });
2732
2813
  hook.command("cursor-session-end").description("Flush pending events and log Cursor session end").action(async () => {
2733
2814
  try {
2734
- const client = createClient(program2);
2815
+ if (!shouldSendTelemetry()) {
2816
+ clearPendingEvents();
2817
+ process.exit(0);
2818
+ return;
2819
+ }
2735
2820
  const pending = readPendingEvents();
2736
2821
  const endEvent = {
2737
2822
  type: "session_end",
@@ -2739,6 +2824,8 @@ function registerHookCommand(program2) {
2739
2824
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2740
2825
  };
2741
2826
  pending.push(endEvent);
2827
+ auditLog(endEvent);
2828
+ const client = createClient(program2);
2742
2829
  await postEventsWithTimeout(client, pending);
2743
2830
  } catch {
2744
2831
  }
@@ -3343,7 +3430,7 @@ function registerUpgradeCommand(program2) {
3343
3430
  });
3344
3431
  spinner.stop();
3345
3432
  console.log(success(`Upgraded to ${update.latest}`));
3346
- } catch (err) {
3433
+ } catch {
3347
3434
  spinner.stop();
3348
3435
  console.log(error("Upgrade failed. Try manually:"));
3349
3436
  console.log(info("npm install -g @caik.dev/cli@latest"));
@@ -3351,13 +3438,164 @@ function registerUpgradeCommand(program2) {
3351
3438
  });
3352
3439
  }
3353
3440
 
3441
+ // src/commands/audit.ts
3442
+ import { readFileSync as readFileSync9, existsSync as existsSync14, writeFileSync as writeFileSync10 } from "fs";
3443
+ import { join as join9 } from "path";
3444
+ import { homedir as homedir8 } from "os";
3445
+ var CAIK_DIR2 = join9(homedir8(), ".caik");
3446
+ var AUDIT_LOG_PATH2 = join9(CAIK_DIR2, "audit.log");
3447
+ var PENDING_EVENTS_PATH2 = join9(CAIK_DIR2, "pending-events.json");
3448
+ function registerAuditCommand(program2) {
3449
+ const cmd = program2.command("audit").description("See exactly what telemetry CAIK collects \u2014 full transparency");
3450
+ cmd.command("log").description("Show recent telemetry events (from local audit log)").option("-n, --lines <count>", "Number of recent events to show", "20").option("--all", "Show all events").action((opts) => {
3451
+ console.log(heading("\nCAIK Telemetry Audit Log"));
3452
+ console.log("\u2550".repeat(50));
3453
+ const config = readConfig();
3454
+ const level = config.contributionLevel ?? "contributor";
3455
+ console.log(info(`Contribution level: ${level}`));
3456
+ console.log(dim(`Log path: ${AUDIT_LOG_PATH2}`));
3457
+ console.log();
3458
+ if (!existsSync14(AUDIT_LOG_PATH2)) {
3459
+ console.log(dim("No events logged yet. Events appear here after your next Claude Code session."));
3460
+ return;
3461
+ }
3462
+ const raw = readFileSync9(AUDIT_LOG_PATH2, "utf-8").trim();
3463
+ if (!raw) {
3464
+ console.log(dim("Audit log is empty."));
3465
+ return;
3466
+ }
3467
+ const lines = raw.split("\n");
3468
+ const limit = opts.all ? lines.length : Math.min(parseInt(opts.lines, 10) || 20, lines.length);
3469
+ const recent = lines.slice(-limit);
3470
+ if (!opts.all && lines.length > limit) {
3471
+ console.log(dim(`Showing last ${limit} of ${lines.length} events. Use --all to see everything.
3472
+ `));
3473
+ }
3474
+ for (const line of recent) {
3475
+ try {
3476
+ const event = JSON.parse(line);
3477
+ const time = new Date(event.timestamp).toLocaleTimeString();
3478
+ const type = event.type.padEnd(14);
3479
+ const platform2 = (event.platform ?? "").padEnd(12);
3480
+ const tool = event.tool ? ` tool=${event.tool}` : "";
3481
+ const ok = event.success !== void 0 ? ` success=${event.success}` : "";
3482
+ const sid = event.sessionId ? ` sid=${event.sessionId.slice(0, 8)}\u2026` : "";
3483
+ console.log(` ${dim(time)} ${type} ${dim(platform2)}${tool}${ok}${sid}`);
3484
+ } catch {
3485
+ console.log(` ${dim(line)}`);
3486
+ }
3487
+ }
3488
+ console.log();
3489
+ console.log(dim("This is everything CAIK sends. No prompts, files, or personal data \u2014 ever."));
3490
+ console.log(dim("Change your level: caik config contribution"));
3491
+ });
3492
+ cmd.command("pending").description("Show events buffered locally (not yet sent to API)").action(() => {
3493
+ console.log(heading("\nPending Events (buffered locally)"));
3494
+ console.log("\u2500".repeat(50));
3495
+ console.log(dim(`Path: ${PENDING_EVENTS_PATH2}
3496
+ `));
3497
+ if (!existsSync14(PENDING_EVENTS_PATH2)) {
3498
+ console.log(dim("No pending events."));
3499
+ return;
3500
+ }
3501
+ try {
3502
+ const raw = readFileSync9(PENDING_EVENTS_PATH2, "utf-8");
3503
+ const events = JSON.parse(raw);
3504
+ if (events.length === 0) {
3505
+ console.log(dim("No pending events."));
3506
+ return;
3507
+ }
3508
+ console.log(info(`${events.length} event(s) buffered, waiting for session end to flush:
3509
+ `));
3510
+ const counts = /* @__PURE__ */ new Map();
3511
+ const tools = /* @__PURE__ */ new Map();
3512
+ let successCount = 0;
3513
+ let failCount = 0;
3514
+ for (const e of events) {
3515
+ counts.set(e.type, (counts.get(e.type) ?? 0) + 1);
3516
+ if (e.tool) tools.set(e.tool, (tools.get(e.tool) ?? 0) + 1);
3517
+ if (e.success === true) successCount++;
3518
+ if (e.success === false) failCount++;
3519
+ }
3520
+ for (const [type, count] of counts) {
3521
+ console.log(` ${type}: ${count}`);
3522
+ }
3523
+ if (tools.size > 0) {
3524
+ console.log();
3525
+ console.log(info("Tools used:"));
3526
+ const sorted = [...tools.entries()].sort((a, b) => b[1] - a[1]);
3527
+ for (const [tool, count] of sorted.slice(0, 10)) {
3528
+ console.log(` ${tool}: ${count}`);
3529
+ }
3530
+ if (sorted.length > 10) {
3531
+ console.log(dim(` \u2026 and ${sorted.length - 10} more`));
3532
+ }
3533
+ }
3534
+ if (successCount + failCount > 0) {
3535
+ console.log();
3536
+ console.log(` ${success(`${successCount} succeeded`)} / ${warn(`${failCount} failed`)}`);
3537
+ }
3538
+ } catch {
3539
+ console.log(warn("Could not parse pending events file."));
3540
+ }
3541
+ });
3542
+ cmd.command("clear").description("Clear the local audit log and pending events").action(() => {
3543
+ if (existsSync14(AUDIT_LOG_PATH2)) {
3544
+ writeFileSync10(AUDIT_LOG_PATH2, "", "utf-8");
3545
+ }
3546
+ if (existsSync14(PENDING_EVENTS_PATH2)) {
3547
+ writeFileSync10(PENDING_EVENTS_PATH2, "[]", "utf-8");
3548
+ }
3549
+ console.log(success("Audit log and pending events cleared."));
3550
+ });
3551
+ cmd.action(() => {
3552
+ const config = readConfig();
3553
+ const level = config.contributionLevel ?? "contributor";
3554
+ console.log(heading("\nCAIK Telemetry Transparency"));
3555
+ console.log("\u2550".repeat(50));
3556
+ console.log();
3557
+ console.log(` Contribution Level: ${level}`);
3558
+ let logCount = 0;
3559
+ if (existsSync14(AUDIT_LOG_PATH2)) {
3560
+ const raw = readFileSync9(AUDIT_LOG_PATH2, "utf-8").trim();
3561
+ if (raw) logCount = raw.split("\n").length;
3562
+ }
3563
+ console.log(` Events Logged: ${logCount}`);
3564
+ let pendingCount = 0;
3565
+ if (existsSync14(PENDING_EVENTS_PATH2)) {
3566
+ try {
3567
+ const parsed = JSON.parse(readFileSync9(PENDING_EVENTS_PATH2, "utf-8"));
3568
+ if (Array.isArray(parsed)) pendingCount = parsed.length;
3569
+ } catch {
3570
+ }
3571
+ }
3572
+ console.log(` Pending (unsent): ${pendingCount}`);
3573
+ console.log(` Audit Log: ${dim(AUDIT_LOG_PATH2)}`);
3574
+ console.log();
3575
+ console.log(heading("What each level sends:"));
3576
+ console.log(` ${dim("none")} Nothing. Directory access only.`);
3577
+ console.log(` ${dim("minimal")} Install/uninstall events (artifact ID + timestamp).`);
3578
+ console.log(` ${dim("contributor")} + Tool-use signals (tool name, success/fail). No content.`);
3579
+ console.log(` ${dim("collective")} + Workflow patterns, stack signals. Anonymized.`);
3580
+ console.log();
3581
+ console.log(dim("NEVER sent: prompts, file contents, conversation text, file paths, personal data."));
3582
+ console.log();
3583
+ console.log(info("Commands:"));
3584
+ console.log(` ${dim("caik audit log")} Show recent events`);
3585
+ console.log(` ${dim("caik audit pending")} Show buffered events`);
3586
+ console.log(` ${dim("caik audit clear")} Clear all local telemetry data`);
3587
+ console.log(` ${dim("caik config contribution")} Change your contribution level`);
3588
+ console.log();
3589
+ });
3590
+ }
3591
+
3354
3592
  // src/index.ts
3355
3593
  var __filename = fileURLToPath(import.meta.url);
3356
3594
  var __dirname = dirname5(__filename);
3357
3595
  var version = "0.0.1";
3358
3596
  try {
3359
- const pkgPath = join9(__dirname, "..", "package.json");
3360
- const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
3597
+ const pkgPath = join10(__dirname, "..", "package.json");
3598
+ const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
3361
3599
  version = pkg.version;
3362
3600
  } catch {
3363
3601
  }
@@ -3378,6 +3616,7 @@ registerKarmaCommand(program);
3378
3616
  registerHookCommand(program);
3379
3617
  registerSetupCommand(program);
3380
3618
  registerUpgradeCommand(program);
3619
+ registerAuditCommand(program);
3381
3620
  program.exitOverride();
3382
3621
  async function main() {
3383
3622
  const updateHint = printUpdateHint();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caik.dev/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "description": "CAIK CLI — Search, install, and publish AI artifacts from your terminal",
6
6
  "keywords": [