ai-cc-router 0.2.6 → 0.2.7

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.
@@ -4,7 +4,6 @@ import { fileURLToPath } from "url";
4
4
  import { join, dirname } from "path";
5
5
  import chalk from "chalk";
6
6
  import { detectPlatform } from "../utils/platform.js";
7
- import { showTelemetryDisclosureIfNeeded } from "../utils/telemetry.js";
8
7
  const execFileAsync = promisify(execFile);
9
8
  // Resolve the path to the compiled CLI entry point
10
9
  const __filename = fileURLToPath(import.meta.url);
@@ -19,9 +18,6 @@ export function registerService(program) {
19
18
  .description("Register cc-router to start automatically on system boot")
20
19
  .action(async () => {
21
20
  console.log(chalk.cyan("\nInstalling cc-router as a system service...\n"));
22
- // Show telemetry disclosure once before the service takes over — after
23
- // this point output goes to PM2 logs, not the user's terminal.
24
- showTelemetryDisclosureIfNeeded();
25
21
  // 1. Verify PM2 is installed
26
22
  const pm2Version = await getPm2Version();
27
23
  if (!pm2Version) {
@@ -13,7 +13,7 @@ import { DEFAULT_RATE_LIMITS } from "../proxy/types.js";
13
13
  import { existsSync } from "fs";
14
14
  import { checkMitmproxyInstalled, isCaCertInstalled, generateCaCert, installCaCert, writeAddonScript, getNetworkExtensionStatus, openNetworkExtensionSettings, } from "../interceptor/mitmproxy-manager.js";
15
15
  import { printDesktopSupportExplainer, printNetworkExtensionInstructions } from "./cmd-client.js";
16
- import { trackEvent, showTelemetryDisclosureIfNeeded } from "../utils/telemetry.js";
16
+ import { trackEvent } from "../utils/telemetry.js";
17
17
  const execFileAsync = promisify(execFile);
18
18
  // ─── Public registration ──────────────────────────────────────────────────────
19
19
  export function registerSetup(program) {
@@ -213,7 +213,6 @@ async function runSetupWizard({ addMode }) {
213
213
  console.log(chalk.bold(`\n${"━".repeat(40)}\n Saving\n${"━".repeat(40)}\n`));
214
214
  saveAccounts(merged);
215
215
  console.log(chalk.green(` ✓ ${merged.length} account(s) saved to ~/.cc-router/accounts.json`));
216
- showTelemetryDisclosureIfNeeded();
217
216
  void trackEvent("setup_completed", { account_count: merged.length });
218
217
  // ─── Post-setup interactive flow ─────────────────────────────────────────
219
218
  await runPostSetupFlow(merged.length);
@@ -2,7 +2,6 @@ import { execFile } from "child_process";
2
2
  import { promisify } from "util";
3
3
  import chalk from "chalk";
4
4
  import { PROXY_PORT, LITELLM_PORT, ACCOUNTS_PATH } from "../config/paths.js";
5
- import { showTelemetryDisclosureIfNeeded } from "../utils/telemetry.js";
6
5
  const execFileAsync = promisify(execFile);
7
6
  export function registerStart(program) {
8
7
  program
@@ -14,9 +13,6 @@ export function registerStart(program) {
14
13
  .option("--accounts <path>", "Path to accounts.json", ACCOUNTS_PATH)
15
14
  .action(async (opts) => {
16
15
  if (opts.daemon) {
17
- // Show telemetry disclosure in the user's terminal before handing off
18
- // to PM2 — once the daemon starts, its stdout goes to PM2 logs.
19
- showTelemetryDisclosureIfNeeded();
20
16
  await startDaemon();
21
17
  return;
22
18
  }
@@ -7,7 +7,6 @@ function defaultState() {
7
7
  enabled: true,
8
8
  installId: randomUUID(),
9
9
  firstRunAt: new Date().toISOString(),
10
- disclosureShown: false,
11
10
  };
12
11
  }
13
12
  // Read the telemetry state, creating and persisting a fresh one on first run.
@@ -25,9 +24,8 @@ export function loadTelemetryState() {
25
24
  enabled: raw.enabled ?? true,
26
25
  installId: raw.installId ?? randomUUID(),
27
26
  firstRunAt: raw.firstRunAt ?? new Date().toISOString(),
28
- disclosureShown: raw.disclosureShown ?? false,
29
27
  };
30
- if (!raw.installId || raw.disclosureShown === undefined) {
28
+ if (!raw.installId) {
31
29
  writeTelemetryState(state);
32
30
  }
33
31
  return state;
@@ -6,7 +6,7 @@ import { TokenPool } from "./token-pool.js";
6
6
  import { needsRefresh, refreshAccountToken, saveAccounts, startRefreshLoop } from "./token-refresher.js";
7
7
  import { loadAccounts, accountsFileExists, readAccountsFromPath, readConfig } from "../config/manager.js";
8
8
  import { checkForUpdate, performUpdate, restartSelf } from "../utils/self-update.js";
9
- import { trackEvent, startHeartbeat, showTelemetryDisclosureIfNeeded } from "../utils/telemetry.js";
9
+ import { trackEvent, startHeartbeat } from "../utils/telemetry.js";
10
10
  import { loadTelemetryState } from "../config/telemetry.js";
11
11
  import { logRoute, logError, logStartup } from "./logger.js";
12
12
  import { stats } from "./stats.js";
@@ -386,10 +386,7 @@ export async function startServer(opts = {}) {
386
386
  if (autoUpdate)
387
387
  console.log(chalk.gray(" Auto-update: enabled (patch/minor)"));
388
388
  // Anonymous telemetry — fire-and-forget, never blocks proxy startup.
389
- // Show the disclosure on the very first start (covers existing users
390
- // upgrading from versions before telemetry existed) before sending anything.
391
389
  try {
392
- showTelemetryDisclosureIfNeeded();
393
390
  const telemetryState = loadTelemetryState();
394
391
  // First-run detection: if the install is brand new, emit app_started too
395
392
  const firstRunAge = Date.now() - new Date(telemetryState.firstRunAt).getTime();
@@ -1,6 +1,5 @@
1
1
  import os from "os";
2
- import chalk from "chalk";
3
- import { isTelemetryEnabled, loadTelemetryState, writeTelemetryState } from "../config/telemetry.js";
2
+ import { isTelemetryEnabled, loadTelemetryState } from "../config/telemetry.js";
4
3
  import { detectPlatform } from "./platform.js";
5
4
  import { getCurrentVersion } from "./self-update.js";
6
5
  // ─── Aptabase configuration ──────────────────────────────────────────────────
@@ -19,10 +18,12 @@ function getOsName() {
19
18
  }
20
19
  function getLocale() {
21
20
  try {
22
- return Intl.DateTimeFormat().resolvedOptions().locale;
21
+ // Aptabase limits locale to 10 characters — truncate extended subtags
22
+ const raw = Intl.DateTimeFormat().resolvedOptions().locale;
23
+ return raw.length <= 10 ? raw : raw.slice(0, 10);
23
24
  }
24
25
  catch {
25
- return process.env["LANG"]?.split(".")[0] ?? "unknown";
26
+ return process.env["LANG"]?.split(".")[0]?.slice(0, 10) ?? "unknown";
26
27
  }
27
28
  }
28
29
  function getSystemProps() {
@@ -37,11 +38,14 @@ function getSystemProps() {
37
38
  sdkVersion: `cc-router@${getCurrentVersion()}`,
38
39
  };
39
40
  }
40
- // Session ID: <installId>-<epoch-hours>. This groups events that belong to the
41
- // same "session" (proxy run) without leaking any timing precision finer than 1h.
41
+ // Session ID groups events from the same install within an hourly window,
42
+ // without leaking timing precision finer than 1h.
43
+ // Aptabase limits sessionId to 36 characters. A UUID with dashes is already 36,
44
+ // so we strip dashes and take the first 24 hex chars + epochHours (~6-7 digits).
42
45
  function getSessionId(installId) {
43
46
  const epochHours = Math.floor(Date.now() / 3_600_000);
44
- return `${installId}-${epochHours}`;
47
+ const shortId = installId.replace(/-/g, "").slice(0, 24);
48
+ return `${shortId}${epochHours}`;
45
49
  }
46
50
  // ─── Public API ──────────────────────────────────────────────────────────────
47
51
  // Fire-and-forget: never throws, never blocks the caller. If telemetry is
@@ -85,33 +89,3 @@ export function startHeartbeat(accountCount) {
85
89
  }, 6 * 60 * 60 * 1000);
86
90
  timer.unref();
87
91
  }
88
- // One-time disclosure shown the very first time CC-Router runs after install
89
- // or upgrade. Idempotent — gated by telemetry.disclosureShown so it's safe to
90
- // call from multiple entry points (setup wizard, foreground start, daemon
91
- // start, service install). Returns true if the disclosure was just shown.
92
- export function showTelemetryDisclosureIfNeeded() {
93
- try {
94
- const state = loadTelemetryState();
95
- if (state.disclosureShown)
96
- return false;
97
- console.log();
98
- console.log(chalk.dim("─".repeat(60)));
99
- console.log(chalk.bold(" Anonymous usage analytics"));
100
- console.log();
101
- console.log(" CC-Router sends anonymous lifecycle events (version, OS,");
102
- console.log(" startup, heartbeat) to help us understand usage and prioritize");
103
- console.log(" improvements. No IPs, no tokens, no prompts, no request content.");
104
- console.log();
105
- console.log(` Disable: ${chalk.cyan("cc-router telemetry off")}`);
106
- console.log(` Or set: ${chalk.cyan("DO_NOT_TRACK=1")} | ${chalk.cyan("CC_ROUTER_TELEMETRY=0")}`);
107
- console.log(` Source: ${chalk.dim("src/utils/telemetry.ts")}`);
108
- console.log(chalk.dim("─".repeat(60)));
109
- console.log();
110
- state.disclosureShown = true;
111
- writeTelemetryState(state);
112
- return true;
113
- }
114
- catch {
115
- return false;
116
- }
117
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-cc-router",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Round-robin proxy for Claude Max OAuth tokens — use multiple Claude Max accounts with Claude Code",
5
5
  "type": "module",
6
6
  "bin": {