tokencanary 0.1.3 → 0.1.4

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 (27) hide show
  1. package/dist/{chunk-4SHFFLSU.js → chunk-66ONBXHK.js} +2 -2
  2. package/dist/{chunk-GZLLOKQD.js → chunk-DCCFWI4O.js} +2 -2
  3. package/dist/{chunk-GZLLOKQD.js.map → chunk-DCCFWI4O.js.map} +1 -1
  4. package/dist/{chunk-DDYZRAAR.js → chunk-IUASFEXE.js} +2 -2
  5. package/dist/cli.js +10 -10
  6. package/dist/daemon.js +1 -1
  7. package/dist/{install-7Z4UZCDW.js → install-NA7R44O4.js} +29 -20
  8. package/dist/install-NA7R44O4.js.map +1 -0
  9. package/dist/{license-7FP2FILW.js → license-VKOP67LZ.js} +2 -2
  10. package/dist/{logs-XMA4R4ST.js → logs-U4OOGTGV.js} +2 -2
  11. package/dist/{stats-R5F7RE43.js → stats-7NLMKM67.js} +2 -2
  12. package/dist/{status-5RPOTLUD.js → status-2EAJQ5TH.js} +2 -2
  13. package/dist/stop-QJSJTHHO.js +8 -0
  14. package/dist/{uninstall-ZW74DUPS.js → uninstall-WUG7BEBH.js} +3 -3
  15. package/dist/{upgrade-DKFPZWUX.js → upgrade-IR4MHUD2.js} +2 -2
  16. package/package.json +4 -1
  17. package/dist/install-7Z4UZCDW.js.map +0 -1
  18. package/dist/stop-GEAGXYPB.js +0 -8
  19. /package/dist/{chunk-4SHFFLSU.js.map → chunk-66ONBXHK.js.map} +0 -0
  20. /package/dist/{chunk-DDYZRAAR.js.map → chunk-IUASFEXE.js.map} +0 -0
  21. /package/dist/{license-7FP2FILW.js.map → license-VKOP67LZ.js.map} +0 -0
  22. /package/dist/{logs-XMA4R4ST.js.map → logs-U4OOGTGV.js.map} +0 -0
  23. /package/dist/{stats-R5F7RE43.js.map → stats-7NLMKM67.js.map} +0 -0
  24. /package/dist/{status-5RPOTLUD.js.map → status-2EAJQ5TH.js.map} +0 -0
  25. /package/dist/{stop-GEAGXYPB.js.map → stop-QJSJTHHO.js.map} +0 -0
  26. /package/dist/{uninstall-ZW74DUPS.js.map → uninstall-WUG7BEBH.js.map} +0 -0
  27. /package/dist/{upgrade-DKFPZWUX.js.map → upgrade-IR4MHUD2.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getConfigDir
3
- } from "./chunk-GZLLOKQD.js";
3
+ } from "./chunk-DCCFWI4O.js";
4
4
 
5
5
  // src/checks/claude.ts
6
6
  import { execSync } from "child_process";
@@ -87,4 +87,4 @@ export {
87
87
  runDoctor,
88
88
  runAllChecks
89
89
  };
90
- //# sourceMappingURL=chunk-4SHFFLSU.js.map
90
+ //# sourceMappingURL=chunk-66ONBXHK.js.map
@@ -23,7 +23,7 @@ function getStatsPath() {
23
23
  function getConfigPath() {
24
24
  return `${getConfigDir()}/${CONFIG_FILENAME}`;
25
25
  }
26
- var DEFAULT_API_URL = "https://app.tokencanary.com";
26
+ var DEFAULT_API_URL = "https://www.tokencanary.com";
27
27
  function getApiBaseUrl() {
28
28
  return process.env.TOKENCANARY_API_URL ?? DEFAULT_API_URL;
29
29
  }
@@ -227,4 +227,4 @@ export {
227
227
  readConfigSync,
228
228
  getPricePer1kTokens
229
229
  };
230
- //# sourceMappingURL=chunk-GZLLOKQD.js.map
230
+ //# sourceMappingURL=chunk-DCCFWI4O.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../shared/src/constants.ts","../../shared/src/fingerprint.ts","../../shared/src/license.ts","../../shared/src/license-store.ts","../../shared/src/license-api.ts","../../shared/src/session-stats.ts","../../shared/src/config.ts"],"sourcesContent":["/** App name for config/log paths */\nexport const APP_NAME = \"tokencanary\";\n\n/** Default log directory under user home */\nexport function getLogDir(): string {\n const home =\n process.env.HOME ?? process.env.USERPROFILE ?? process.env.TMP ?? \"/tmp\";\n return `${home}/.${APP_NAME}/logs`;\n}\n\n/** Default config directory under user home */\nexport function getConfigDir(): string {\n const home =\n process.env.HOME ?? process.env.USERPROFILE ?? process.env.TMP ?? \"/tmp\";\n return `${home}/.${APP_NAME}`;\n}\n\n/** Trial duration in days */\nexport const TRIAL_DAYS = 30;\n\n/** Offline grace period in days */\nexport const OFFLINE_GRACE_DAYS = 3;\n\n/** Default daemon port for hook HTTP server */\nexport const DEFAULT_DAEMON_PORT = 3847;\n\n/** License file name under config dir */\nexport const LICENSE_FILENAME = \"license.json\";\n\n/** Stats file name under config dir (session stats since daemon start) */\nexport const STATS_FILENAME = \"stats.json\";\n\n/** User config file under config dir (quiet, pricePer1kTokens, etc.) */\nexport const CONFIG_FILENAME = \"config.json\";\n\nexport function getLicensePath(): string {\n return `${getConfigDir()}/${LICENSE_FILENAME}`;\n}\n\nexport function getStatsPath(): string {\n return `${getConfigDir()}/${STATS_FILENAME}`;\n}\n\nexport function getConfigPath(): string {\n return `${getConfigDir()}/${CONFIG_FILENAME}`;\n}\n\n/** Default API/app base URL (override with TOKENCANARY_API_URL). */\nexport const DEFAULT_API_URL = \"https://app.tokencanary.com\";\n\nexport function getApiBaseUrl(): string {\n return process.env.TOKENCANARY_API_URL ?? DEFAULT_API_URL;\n}\n\n/** Upgrade page URL; pass installId to prefill for checkout. */\nexport const DEFAULT_UPGRADE_URL = `${DEFAULT_API_URL}/upgrade`;\n\nexport function getUpgradeUrl(installId?: string): string {\n const base = process.env.TOKENCANARY_UPGRADE_URL ?? DEFAULT_UPGRADE_URL;\n if (!installId) return base;\n const u = new URL(base);\n u.searchParams.set(\"install_id\", installId);\n return u.toString();\n}\n\n/** Hook request timeout in ms (fail open) */\nexport const HOOK_TIMEOUT_MS = 5000;\n","import { createHash } from \"node:crypto\";\nimport { hostname, platform, arch, cpus, networkInterfaces } from \"node:os\";\n\n/**\n * Deterministic machine fingerprint for license binding.\n * Uses hostname, OS, CPU model, and hashed network info (no raw MAC).\n */\nexport function getMachineId(): string {\n const parts: string[] = [];\n try {\n parts.push(hostname());\n } catch {\n parts.push(\"unknown-host\");\n }\n parts.push(platform());\n parts.push(arch());\n try {\n const cpu = cpus()[0];\n parts.push(cpu?.model ?? \"unknown-cpu\");\n } catch {\n parts.push(\"unknown-cpu\");\n }\n try {\n const nets = networkInterfaces();\n const addrs: string[] = [];\n for (const name of Object.keys(nets)) {\n const list = nets[name];\n if (list) {\n for (const iface of list) {\n if (iface.address && !iface.internal) addrs.push(name + \":\" + iface.address);\n }\n }\n }\n addrs.sort();\n if (addrs.length > 0) {\n parts.push(createHash(\"sha256\").update(addrs.join(\",\")).digest(\"hex\").slice(0, 16));\n }\n } catch {\n // skip network\n }\n return createHash(\"sha256\").update(parts.join(\"|\")).digest(\"hex\");\n}\n","import type { LicenseInfo, Plan } from \"./types.js\";\nimport { TRIAL_DAYS, OFFLINE_GRACE_DAYS } from \"./constants.js\";\n\nconst MS_PER_DAY = 86400 * 1000;\n\nexport function createTrialLicense(installId: string, machineId: string): LicenseInfo {\n const now = new Date();\n const expiry = new Date(now.getTime() + TRIAL_DAYS * MS_PER_DAY);\n return {\n installId,\n machineId,\n plan: \"trial\",\n expiry: expiry.toISOString(),\n createdAt: now.toISOString(),\n };\n}\n\n/**\n * Check if license is valid at the given date.\n * - Trial/monthly/yearly: valid if expiry > now.\n * - Offline grace: if lastKnownValid and within OFFLINE_GRACE_DAYS, still valid.\n */\nexport function isLicenseValid(\n license: LicenseInfo | null,\n now: Date = new Date(),\n options?: { lastKnownValid?: Date }\n): boolean {\n if (!license) return false;\n const expiry = new Date(license.expiry);\n if (expiry > now) return true;\n if (options?.lastKnownValid) {\n const graceEnd = new Date(\n options.lastKnownValid.getTime() + OFFLINE_GRACE_DAYS * MS_PER_DAY\n );\n if (now < graceEnd) return true;\n }\n return false;\n}\n\nexport function getPlanDisplay(plan: Plan): string {\n switch (plan) {\n case \"trial\":\n return \"Trial\";\n case \"monthly\":\n return \"Monthly\";\n case \"yearly\":\n return \"Yearly\";\n default:\n return String(plan);\n }\n}\n","import { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { getLicensePath } from \"./constants.js\";\nimport type { LicenseInfo } from \"./types.js\";\n\nexport function readLicenseSync(): LicenseInfo | null {\n const path = getLicensePath();\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, \"utf8\");\n const data = JSON.parse(raw) as LicenseInfo;\n if (\n typeof data.installId === \"string\" &&\n typeof data.machineId === \"string\" &&\n typeof data.plan === \"string\" &&\n typeof data.expiry === \"string\" &&\n typeof data.createdAt === \"string\"\n ) {\n return data;\n }\n } catch {\n // invalid or missing\n }\n return null;\n}\n\nexport function writeLicenseSync(license: LicenseInfo): void {\n const path = getLicensePath();\n writeFileSync(path, JSON.stringify(license, null, 2), \"utf8\");\n}\n","import type { LicenseInfo } from \"./types.js\";\n\n/** Response shape from POST /api/install and POST /api/license/check */\nexport interface LicenseApiResponse {\n install_id: string;\n machine_id: string;\n plan: \"trial\" | \"monthly\" | \"yearly\";\n expiry: string;\n created_at: string;\n}\n\nexport function apiResponseToLicenseInfo(r: LicenseApiResponse): LicenseInfo {\n return {\n installId: r.install_id,\n machineId: r.machine_id,\n plan: r.plan,\n expiry: r.expiry,\n createdAt: r.created_at,\n };\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { getStatsPath, getConfigDir } from \"./constants.js\";\nimport type { SessionStats } from \"./types.js\";\n\nfunction defaultStats(): SessionStats {\n return {\n requests_optimized: 0,\n input_chars: 0,\n output_chars: 0,\n updated_at: new Date().toISOString(),\n };\n}\n\nexport function readSessionStatsSync(): SessionStats | null {\n const path = getStatsPath();\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, \"utf8\");\n const data = JSON.parse(raw) as SessionStats;\n if (\n typeof data.requests_optimized === \"number\" &&\n typeof data.input_chars === \"number\" &&\n typeof data.output_chars === \"number\" &&\n typeof data.updated_at === \"string\"\n ) {\n return data;\n }\n } catch {\n // invalid or missing\n }\n return null;\n}\n\nexport function writeSessionStatsSync(stats: SessionStats): void {\n const dir = getConfigDir();\n mkdirSync(dir, { recursive: true });\n const path = getStatsPath();\n writeFileSync(path, JSON.stringify(stats, null, 2), \"utf8\");\n}\n\n/** Reset stats to zeros (call on daemon start). */\nexport function resetSessionStatsSync(): void {\n writeSessionStatsSync(defaultStats());\n}\n\n/** Add one request's chars to current stats and persist. */\nexport function updateSessionStatsSync(inputChars: number, outputChars: number): void {\n const current = readSessionStatsSync() ?? defaultStats();\n const next: SessionStats = {\n requests_optimized: current.requests_optimized + 1,\n input_chars: current.input_chars + inputChars,\n output_chars: current.output_chars + outputChars,\n updated_at: new Date().toISOString(),\n };\n writeSessionStatsSync(next);\n}\n","import { readFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { getConfigPath, getConfigDir } from \"./constants.js\";\n\n/** User-configurable options (same file used by CLI and daemon). */\nexport interface UserConfig {\n /** When true, do not show in-Claude systemMessage (savings). */\n quiet?: boolean;\n /** Price per 1k tokens (for $ estimate in stats/systemMessage). Default ~$3/1M = 0.003. */\n pricePer1kTokens?: number;\n}\n\nconst DEFAULT_PRICE_PER_1K = 0.003;\n\nexport function readConfigSync(): UserConfig {\n const path = getConfigPath();\n if (!existsSync(path)) return {};\n try {\n const raw = readFileSync(path, \"utf8\");\n const data = JSON.parse(raw) as Record<string, unknown>;\n const config: UserConfig = {};\n if (typeof data.quiet === \"boolean\") config.quiet = data.quiet;\n if (typeof data.pricePer1kTokens === \"number\")\n config.pricePer1kTokens = data.pricePer1kTokens;\n return config;\n } catch {\n return {};\n }\n}\n\nexport function writeConfigSync(config: UserConfig): void {\n const dir = getConfigDir();\n mkdirSync(dir, { recursive: true });\n const path = getConfigPath();\n const existing = readConfigSync();\n const merged = { ...existing, ...config };\n writeFileSync(path, JSON.stringify(merged, null, 2), \"utf8\");\n}\n\n/** Price per 1k tokens for $ estimate (from config or default). */\nexport function getPricePer1kTokens(): number {\n const config = readConfigSync();\n return config.pricePer1kTokens ?? DEFAULT_PRICE_PER_1K;\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","existsSync","existsSync","readFileSync","writeFileSync","readFileSync","existsSync","mkdirSync","writeFileSync","existsSync","readFileSync"]}
1
+ {"version":3,"sources":["../../shared/src/constants.ts","../../shared/src/fingerprint.ts","../../shared/src/license.ts","../../shared/src/license-store.ts","../../shared/src/license-api.ts","../../shared/src/session-stats.ts","../../shared/src/config.ts"],"sourcesContent":["/** App name for config/log paths */\nexport const APP_NAME = \"tokencanary\";\n\n/** Default log directory under user home */\nexport function getLogDir(): string {\n const home =\n process.env.HOME ?? process.env.USERPROFILE ?? process.env.TMP ?? \"/tmp\";\n return `${home}/.${APP_NAME}/logs`;\n}\n\n/** Default config directory under user home */\nexport function getConfigDir(): string {\n const home =\n process.env.HOME ?? process.env.USERPROFILE ?? process.env.TMP ?? \"/tmp\";\n return `${home}/.${APP_NAME}`;\n}\n\n/** Trial duration in days */\nexport const TRIAL_DAYS = 30;\n\n/** Offline grace period in days */\nexport const OFFLINE_GRACE_DAYS = 3;\n\n/** Default daemon port for hook HTTP server */\nexport const DEFAULT_DAEMON_PORT = 3847;\n\n/** License file name under config dir */\nexport const LICENSE_FILENAME = \"license.json\";\n\n/** Stats file name under config dir (session stats since daemon start) */\nexport const STATS_FILENAME = \"stats.json\";\n\n/** User config file under config dir (quiet, pricePer1kTokens, etc.) */\nexport const CONFIG_FILENAME = \"config.json\";\n\nexport function getLicensePath(): string {\n return `${getConfigDir()}/${LICENSE_FILENAME}`;\n}\n\nexport function getStatsPath(): string {\n return `${getConfigDir()}/${STATS_FILENAME}`;\n}\n\nexport function getConfigPath(): string {\n return `${getConfigDir()}/${CONFIG_FILENAME}`;\n}\n\n/** Default API/app base URL (override with TOKENCANARY_API_URL). */\nexport const DEFAULT_API_URL = \"https://www.tokencanary.com\";\n\nexport function getApiBaseUrl(): string {\n return process.env.TOKENCANARY_API_URL ?? DEFAULT_API_URL;\n}\n\n/** Upgrade page URL; pass installId to prefill for checkout. */\nexport const DEFAULT_UPGRADE_URL = `${DEFAULT_API_URL}/upgrade`;\n\nexport function getUpgradeUrl(installId?: string): string {\n const base = process.env.TOKENCANARY_UPGRADE_URL ?? DEFAULT_UPGRADE_URL;\n if (!installId) return base;\n const u = new URL(base);\n u.searchParams.set(\"install_id\", installId);\n return u.toString();\n}\n\n/** Hook request timeout in ms (fail open) */\nexport const HOOK_TIMEOUT_MS = 5000;\n","import { createHash } from \"node:crypto\";\nimport { hostname, platform, arch, cpus, networkInterfaces } from \"node:os\";\n\n/**\n * Deterministic machine fingerprint for license binding.\n * Uses hostname, OS, CPU model, and hashed network info (no raw MAC).\n */\nexport function getMachineId(): string {\n const parts: string[] = [];\n try {\n parts.push(hostname());\n } catch {\n parts.push(\"unknown-host\");\n }\n parts.push(platform());\n parts.push(arch());\n try {\n const cpu = cpus()[0];\n parts.push(cpu?.model ?? \"unknown-cpu\");\n } catch {\n parts.push(\"unknown-cpu\");\n }\n try {\n const nets = networkInterfaces();\n const addrs: string[] = [];\n for (const name of Object.keys(nets)) {\n const list = nets[name];\n if (list) {\n for (const iface of list) {\n if (iface.address && !iface.internal) addrs.push(name + \":\" + iface.address);\n }\n }\n }\n addrs.sort();\n if (addrs.length > 0) {\n parts.push(createHash(\"sha256\").update(addrs.join(\",\")).digest(\"hex\").slice(0, 16));\n }\n } catch {\n // skip network\n }\n return createHash(\"sha256\").update(parts.join(\"|\")).digest(\"hex\");\n}\n","import type { LicenseInfo, Plan } from \"./types.js\";\nimport { TRIAL_DAYS, OFFLINE_GRACE_DAYS } from \"./constants.js\";\n\nconst MS_PER_DAY = 86400 * 1000;\n\nexport function createTrialLicense(installId: string, machineId: string): LicenseInfo {\n const now = new Date();\n const expiry = new Date(now.getTime() + TRIAL_DAYS * MS_PER_DAY);\n return {\n installId,\n machineId,\n plan: \"trial\",\n expiry: expiry.toISOString(),\n createdAt: now.toISOString(),\n };\n}\n\n/**\n * Check if license is valid at the given date.\n * - Trial/monthly/yearly: valid if expiry > now.\n * - Offline grace: if lastKnownValid and within OFFLINE_GRACE_DAYS, still valid.\n */\nexport function isLicenseValid(\n license: LicenseInfo | null,\n now: Date = new Date(),\n options?: { lastKnownValid?: Date }\n): boolean {\n if (!license) return false;\n const expiry = new Date(license.expiry);\n if (expiry > now) return true;\n if (options?.lastKnownValid) {\n const graceEnd = new Date(\n options.lastKnownValid.getTime() + OFFLINE_GRACE_DAYS * MS_PER_DAY\n );\n if (now < graceEnd) return true;\n }\n return false;\n}\n\nexport function getPlanDisplay(plan: Plan): string {\n switch (plan) {\n case \"trial\":\n return \"Trial\";\n case \"monthly\":\n return \"Monthly\";\n case \"yearly\":\n return \"Yearly\";\n default:\n return String(plan);\n }\n}\n","import { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { getLicensePath } from \"./constants.js\";\nimport type { LicenseInfo } from \"./types.js\";\n\nexport function readLicenseSync(): LicenseInfo | null {\n const path = getLicensePath();\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, \"utf8\");\n const data = JSON.parse(raw) as LicenseInfo;\n if (\n typeof data.installId === \"string\" &&\n typeof data.machineId === \"string\" &&\n typeof data.plan === \"string\" &&\n typeof data.expiry === \"string\" &&\n typeof data.createdAt === \"string\"\n ) {\n return data;\n }\n } catch {\n // invalid or missing\n }\n return null;\n}\n\nexport function writeLicenseSync(license: LicenseInfo): void {\n const path = getLicensePath();\n writeFileSync(path, JSON.stringify(license, null, 2), \"utf8\");\n}\n","import type { LicenseInfo } from \"./types.js\";\n\n/** Response shape from POST /api/install and POST /api/license/check */\nexport interface LicenseApiResponse {\n install_id: string;\n machine_id: string;\n plan: \"trial\" | \"monthly\" | \"yearly\";\n expiry: string;\n created_at: string;\n}\n\nexport function apiResponseToLicenseInfo(r: LicenseApiResponse): LicenseInfo {\n return {\n installId: r.install_id,\n machineId: r.machine_id,\n plan: r.plan,\n expiry: r.expiry,\n createdAt: r.created_at,\n };\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { getStatsPath, getConfigDir } from \"./constants.js\";\nimport type { SessionStats } from \"./types.js\";\n\nfunction defaultStats(): SessionStats {\n return {\n requests_optimized: 0,\n input_chars: 0,\n output_chars: 0,\n updated_at: new Date().toISOString(),\n };\n}\n\nexport function readSessionStatsSync(): SessionStats | null {\n const path = getStatsPath();\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, \"utf8\");\n const data = JSON.parse(raw) as SessionStats;\n if (\n typeof data.requests_optimized === \"number\" &&\n typeof data.input_chars === \"number\" &&\n typeof data.output_chars === \"number\" &&\n typeof data.updated_at === \"string\"\n ) {\n return data;\n }\n } catch {\n // invalid or missing\n }\n return null;\n}\n\nexport function writeSessionStatsSync(stats: SessionStats): void {\n const dir = getConfigDir();\n mkdirSync(dir, { recursive: true });\n const path = getStatsPath();\n writeFileSync(path, JSON.stringify(stats, null, 2), \"utf8\");\n}\n\n/** Reset stats to zeros (call on daemon start). */\nexport function resetSessionStatsSync(): void {\n writeSessionStatsSync(defaultStats());\n}\n\n/** Add one request's chars to current stats and persist. */\nexport function updateSessionStatsSync(inputChars: number, outputChars: number): void {\n const current = readSessionStatsSync() ?? defaultStats();\n const next: SessionStats = {\n requests_optimized: current.requests_optimized + 1,\n input_chars: current.input_chars + inputChars,\n output_chars: current.output_chars + outputChars,\n updated_at: new Date().toISOString(),\n };\n writeSessionStatsSync(next);\n}\n","import { readFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { getConfigPath, getConfigDir } from \"./constants.js\";\n\n/** User-configurable options (same file used by CLI and daemon). */\nexport interface UserConfig {\n /** When true, do not show in-Claude systemMessage (savings). */\n quiet?: boolean;\n /** Price per 1k tokens (for $ estimate in stats/systemMessage). Default ~$3/1M = 0.003. */\n pricePer1kTokens?: number;\n}\n\nconst DEFAULT_PRICE_PER_1K = 0.003;\n\nexport function readConfigSync(): UserConfig {\n const path = getConfigPath();\n if (!existsSync(path)) return {};\n try {\n const raw = readFileSync(path, \"utf8\");\n const data = JSON.parse(raw) as Record<string, unknown>;\n const config: UserConfig = {};\n if (typeof data.quiet === \"boolean\") config.quiet = data.quiet;\n if (typeof data.pricePer1kTokens === \"number\")\n config.pricePer1kTokens = data.pricePer1kTokens;\n return config;\n } catch {\n return {};\n }\n}\n\nexport function writeConfigSync(config: UserConfig): void {\n const dir = getConfigDir();\n mkdirSync(dir, { recursive: true });\n const path = getConfigPath();\n const existing = readConfigSync();\n const merged = { ...existing, ...config };\n writeFileSync(path, JSON.stringify(merged, null, 2), \"utf8\");\n}\n\n/** Price per 1k tokens for $ estimate (from config or default). */\nexport function getPricePer1kTokens(): number {\n const config = readConfigSync();\n return config.pricePer1kTokens ?? DEFAULT_PRICE_PER_1K;\n}\n"],"mappings":";AACO,IAAM,WAAW;AAGlB,SAAU,YAAS;AACvB,QAAM,OACJ,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe,QAAQ,IAAI,OAAO;AACpE,SAAO,GAAG,IAAI,KAAK,QAAQ;AAC7B;AAGM,SAAU,eAAY;AAC1B,QAAM,OACJ,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe,QAAQ,IAAI,OAAO;AACpE,SAAO,GAAG,IAAI,KAAK,QAAQ;AAC7B;AAGO,IAAM,aAAa;AAGnB,IAAM,qBAAqB;AAG3B,IAAM,sBAAsB;AAG5B,IAAM,mBAAmB;AAGzB,IAAM,iBAAiB;AAGvB,IAAM,kBAAkB;AAEzB,SAAU,iBAAc;AAC5B,SAAO,GAAG,aAAY,CAAE,IAAI,gBAAgB;AAC9C;AAEM,SAAU,eAAY;AAC1B,SAAO,GAAG,aAAY,CAAE,IAAI,cAAc;AAC5C;AAEM,SAAU,gBAAa;AAC3B,SAAO,GAAG,aAAY,CAAE,IAAI,eAAe;AAC7C;AAGO,IAAM,kBAAkB;AAEzB,SAAU,gBAAa;AAC3B,SAAO,QAAQ,IAAI,uBAAuB;AAC5C;AAGO,IAAM,sBAAsB,GAAG,eAAe;AAE/C,SAAU,cAAc,WAAkB;AAC9C,QAAM,OAAO,QAAQ,IAAI,2BAA2B;AACpD,MAAI,CAAC;AAAW,WAAO;AACvB,QAAM,IAAI,IAAI,IAAI,IAAI;AACtB,IAAE,aAAa,IAAI,cAAc,SAAS;AAC1C,SAAO,EAAE,SAAQ;AACnB;AAGO,IAAM,kBAAkB;;;AClE/B,SAAS,kBAAkB;AAC3B,SAAS,UAAU,UAAU,MAAM,MAAM,yBAAyB;AAM5D,SAAU,eAAY;AAC1B,QAAM,QAAkB,CAAA;AACxB,MAAI;AACF,UAAM,KAAK,SAAQ,CAAE;EACvB,QAAQ;AACN,UAAM,KAAK,cAAc;EAC3B;AACA,QAAM,KAAK,SAAQ,CAAE;AACrB,QAAM,KAAK,KAAI,CAAE;AACjB,MAAI;AACF,UAAM,MAAM,KAAI,EAAG,CAAC;AACpB,UAAM,KAAK,KAAK,SAAS,aAAa;EACxC,QAAQ;AACN,UAAM,KAAK,aAAa;EAC1B;AACA,MAAI;AACF,UAAM,OAAO,kBAAiB;AAC9B,UAAM,QAAkB,CAAA;AACxB,eAAW,QAAQ,OAAO,KAAK,IAAI,GAAG;AACpC,YAAM,OAAO,KAAK,IAAI;AACtB,UAAI,MAAM;AACR,mBAAW,SAAS,MAAM;AACxB,cAAI,MAAM,WAAW,CAAC,MAAM;AAAU,kBAAM,KAAK,OAAO,MAAM,MAAM,OAAO;QAC7E;MACF;IACF;AACA,UAAM,KAAI;AACV,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,KAAK,WAAW,QAAQ,EAAE,OAAO,MAAM,KAAK,GAAG,CAAC,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC;IACpF;EACF,QAAQ;EAER;AACA,SAAO,WAAW,QAAQ,EAAE,OAAO,MAAM,KAAK,GAAG,CAAC,EAAE,OAAO,KAAK;AAClE;;;ACtCA,IAAM,aAAa,QAAQ;AAmBrB,SAAU,eACd,SACA,MAAY,oBAAI,KAAI,GACpB,SAAmC;AAEnC,MAAI,CAAC;AAAS,WAAO;AACrB,QAAM,SAAS,IAAI,KAAK,QAAQ,MAAM;AACtC,MAAI,SAAS;AAAK,WAAO;AACzB,MAAI,SAAS,gBAAgB;AAC3B,UAAM,WAAW,IAAI,KACnB,QAAQ,eAAe,QAAO,IAAK,qBAAqB,UAAU;AAEpE,QAAI,MAAM;AAAU,aAAO;EAC7B;AACA,SAAO;AACT;AAEM,SAAU,eAAe,MAAU;AACvC,UAAQ,MAAM;IACZ,KAAK;AACH,aAAO;IACT,KAAK;AACH,aAAO;IACT,KAAK;AACH,aAAO;IACT;AACE,aAAO,OAAO,IAAI;EACtB;AACF;;;AClDA,SAAS,cAAc,eAAe,kBAAkB;AAIlD,SAAU,kBAAe;AAC7B,QAAM,OAAO,eAAc;AAC3B,MAAI,CAAC,WAAW,IAAI;AAAG,WAAO;AAC9B,MAAI;AACF,UAAM,MAAM,aAAa,MAAM,MAAM;AACrC,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,QACE,OAAO,KAAK,cAAc,YAC1B,OAAO,KAAK,cAAc,YAC1B,OAAO,KAAK,SAAS,YACrB,OAAO,KAAK,WAAW,YACvB,OAAO,KAAK,cAAc,UAC1B;AACA,aAAO;IACT;EACF,QAAQ;EAER;AACA,SAAO;AACT;AAEM,SAAU,iBAAiB,SAAoB;AACnD,QAAM,OAAO,eAAc;AAC3B,gBAAc,MAAM,KAAK,UAAU,SAAS,MAAM,CAAC,GAAG,MAAM;AAC9D;;;ACjBM,SAAU,yBAAyB,GAAqB;AAC5D,SAAO;IACL,WAAW,EAAE;IACb,WAAW,EAAE;IACb,MAAM,EAAE;IACR,QAAQ,EAAE;IACV,WAAW,EAAE;;AAEjB;;;ACnBA,SAAS,gBAAAA,eAAc,iBAAAC,gBAAe,cAAAC,aAAY,iBAAiB;AAInE,SAAS,eAAY;AACnB,SAAO;IACL,oBAAoB;IACpB,aAAa;IACb,cAAc;IACd,aAAY,oBAAI,KAAI,GAAG,YAAW;;AAEtC;AAEM,SAAU,uBAAoB;AAClC,QAAM,OAAO,aAAY;AACzB,MAAI,CAACC,YAAW,IAAI;AAAG,WAAO;AAC9B,MAAI;AACF,UAAM,MAAMC,cAAa,MAAM,MAAM;AACrC,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,QACE,OAAO,KAAK,uBAAuB,YACnC,OAAO,KAAK,gBAAgB,YAC5B,OAAO,KAAK,iBAAiB,YAC7B,OAAO,KAAK,eAAe,UAC3B;AACA,aAAO;IACT;EACF,QAAQ;EAER;AACA,SAAO;AACT;AAEM,SAAU,sBAAsB,OAAmB;AACvD,QAAM,MAAM,aAAY;AACxB,YAAU,KAAK,EAAE,WAAW,KAAI,CAAE;AAClC,QAAM,OAAO,aAAY;AACzB,EAAAC,eAAc,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,MAAM;AAC5D;AAGM,SAAU,wBAAqB;AACnC,wBAAsB,aAAY,CAAE;AACtC;AAGM,SAAU,uBAAuB,YAAoB,aAAmB;AAC5E,QAAM,UAAU,qBAAoB,KAAM,aAAY;AACtD,QAAM,OAAqB;IACzB,oBAAoB,QAAQ,qBAAqB;IACjD,aAAa,QAAQ,cAAc;IACnC,cAAc,QAAQ,eAAe;IACrC,aAAY,oBAAI,KAAI,GAAG,YAAW;;AAEpC,wBAAsB,IAAI;AAC5B;;;ACvDA,SAAS,gBAAAC,eAAc,cAAAC,aAAY,aAAAC,YAAW,iBAAAC,sBAAqB;AAWnE,IAAM,uBAAuB;AAEvB,SAAU,iBAAc;AAC5B,QAAM,OAAO,cAAa;AAC1B,MAAI,CAACC,YAAW,IAAI;AAAG,WAAO,CAAA;AAC9B,MAAI;AACF,UAAM,MAAMC,cAAa,MAAM,MAAM;AACrC,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,UAAM,SAAqB,CAAA;AAC3B,QAAI,OAAO,KAAK,UAAU;AAAW,aAAO,QAAQ,KAAK;AACzD,QAAI,OAAO,KAAK,qBAAqB;AACnC,aAAO,mBAAmB,KAAK;AACjC,WAAO;EACT,QAAQ;AACN,WAAO,CAAA;EACT;AACF;AAYM,SAAU,sBAAmB;AACjC,QAAM,SAAS,eAAc;AAC7B,SAAO,OAAO,oBAAoB;AACpC;","names":["readFileSync","writeFileSync","existsSync","existsSync","readFileSync","writeFileSync","readFileSync","existsSync","mkdirSync","writeFileSync","existsSync","readFileSync"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getConfigDir
3
- } from "./chunk-GZLLOKQD.js";
3
+ } from "./chunk-DCCFWI4O.js";
4
4
 
5
5
  // src/commands/stop.ts
6
6
  import { readFile, unlink } from "fs/promises";
@@ -43,4 +43,4 @@ async function runStopCmd(_args) {
43
43
  export {
44
44
  runStopCmd
45
45
  };
46
- //# sourceMappingURL=chunk-DDYZRAAR.js.map
46
+ //# sourceMappingURL=chunk-IUASFEXE.js.map
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runDoctor
4
- } from "./chunk-4SHFFLSU.js";
5
- import "./chunk-GZLLOKQD.js";
4
+ } from "./chunk-66ONBXHK.js";
5
+ import "./chunk-DCCFWI4O.js";
6
6
 
7
7
  // src/commands/index.ts
8
8
  var COMMANDS = {
@@ -73,35 +73,35 @@ Quick start:
73
73
  `);
74
74
  }
75
75
  async function runInstall(_args) {
76
- const { runInstallCmd } = await import("./install-7Z4UZCDW.js");
76
+ const { runInstallCmd } = await import("./install-NA7R44O4.js");
77
77
  return runInstallCmd(_args);
78
78
  }
79
79
  async function runStop(_args) {
80
- const { runStopCmd } = await import("./stop-GEAGXYPB.js");
80
+ const { runStopCmd } = await import("./stop-QJSJTHHO.js");
81
81
  return runStopCmd(_args);
82
82
  }
83
83
  async function runUninstall(_args) {
84
- const { runUninstallCmd } = await import("./uninstall-ZW74DUPS.js");
84
+ const { runUninstallCmd } = await import("./uninstall-WUG7BEBH.js");
85
85
  return runUninstallCmd(_args);
86
86
  }
87
87
  async function runStatus(_args) {
88
- const { runStatusCmd } = await import("./status-5RPOTLUD.js");
88
+ const { runStatusCmd } = await import("./status-2EAJQ5TH.js");
89
89
  return runStatusCmd(_args);
90
90
  }
91
91
  async function runStats(_args) {
92
- const { runStatsCmd } = await import("./stats-R5F7RE43.js");
92
+ const { runStatsCmd } = await import("./stats-7NLMKM67.js");
93
93
  return runStatsCmd(_args);
94
94
  }
95
95
  async function runLogs(_args) {
96
- const { runLogsCmd } = await import("./logs-XMA4R4ST.js");
96
+ const { runLogsCmd } = await import("./logs-U4OOGTGV.js");
97
97
  return runLogsCmd(_args);
98
98
  }
99
99
  async function runUpgrade(_args) {
100
- const { runUpgradeCmd } = await import("./upgrade-DKFPZWUX.js");
100
+ const { runUpgradeCmd } = await import("./upgrade-IR4MHUD2.js");
101
101
  return runUpgradeCmd(_args);
102
102
  }
103
103
  async function runLicense(_args) {
104
- const { runLicenseCmd } = await import("./license-7FP2FILW.js");
104
+ const { runLicenseCmd } = await import("./license-VKOP67LZ.js");
105
105
  return runLicenseCmd(_args);
106
106
  }
107
107
 
package/dist/daemon.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  readSessionStatsSync,
10
10
  resetSessionStatsSync,
11
11
  updateSessionStatsSync
12
- } from "./chunk-GZLLOKQD.js";
12
+ } from "./chunk-DCCFWI4O.js";
13
13
 
14
14
  // ../daemon/src/index.ts
15
15
  import { fileURLToPath } from "url";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runAllChecks
3
- } from "./chunk-4SHFFLSU.js";
3
+ } from "./chunk-66ONBXHK.js";
4
4
  import {
5
5
  DEFAULT_DAEMON_PORT,
6
6
  apiResponseToLicenseInfo,
@@ -9,7 +9,7 @@ import {
9
9
  getMachineId,
10
10
  readLicenseSync,
11
11
  writeLicenseSync
12
- } from "./chunk-GZLLOKQD.js";
12
+ } from "./chunk-DCCFWI4O.js";
13
13
 
14
14
  // src/commands/install.ts
15
15
  import { randomUUID } from "crypto";
@@ -159,6 +159,7 @@ async function runInstallCmd(_args) {
159
159
  await mkdir(configDir, { recursive: true });
160
160
  let license = readLicenseSync();
161
161
  const isFirstRun = !license;
162
+ let trialSkippedDueToNetwork = false;
162
163
  if (!license) {
163
164
  const installId = randomUUID();
164
165
  const machineId = getMachineId();
@@ -172,24 +173,29 @@ async function runInstallCmd(_args) {
172
173
  });
173
174
  } catch (e) {
174
175
  const msg = e instanceof Error ? e.message : String(e);
175
- console.error("Could not start trial:", msg);
176
- console.error("Check your connection and try again.");
177
- return 1;
176
+ console.warn("Could not start trial:", msg);
177
+ console.warn(`Request was to: ${baseUrl}/api/install`);
178
+ console.warn(
179
+ "If the host cannot be resolved, you may be offline or the server may be unavailable. Setup will continue; run 'tokencanary setup' again when online to start your trial."
180
+ );
181
+ trialSkippedDueToNetwork = true;
178
182
  }
179
- if (!res.ok) {
180
- const text = await res.text();
181
- console.error("Could not start trial. Server returned:", res.status, text || res.statusText);
182
- return 1;
183
- }
184
- let data;
185
- try {
186
- data = await res.json();
187
- } catch {
188
- console.error("Invalid response from server.");
189
- return 1;
183
+ if (!trialSkippedDueToNetwork && res !== void 0) {
184
+ if (!res.ok) {
185
+ const text = await res.text();
186
+ console.error("Could not start trial. Server returned:", res.status, text || res.statusText);
187
+ return 1;
188
+ }
189
+ let data;
190
+ try {
191
+ data = await res.json();
192
+ } catch {
193
+ console.error("Invalid response from server.");
194
+ return 1;
195
+ }
196
+ license = apiResponseToLicenseInfo(data);
197
+ writeLicenseSync(license);
190
198
  }
191
- license = apiResponseToLicenseInfo(data);
192
- writeLicenseSync(license);
193
199
  } else {
194
200
  const baseUrl = getApiBaseUrl();
195
201
  try {
@@ -230,10 +236,13 @@ async function runInstallCmd(_args) {
230
236
  });
231
237
  }
232
238
  console.log("");
233
- if (isFirstRun) {
239
+ if (isFirstRun && license) {
234
240
  console.log("Token Canary trial activated");
235
241
  console.log("Your 30-day trial has started.\n");
236
242
  }
243
+ if (trialSkippedDueToNetwork) {
244
+ console.log("Trial could not be started (server unreachable). Run 'tokencanary setup' again when online to start your 30-day trial.\n");
245
+ }
237
246
  console.log("Setup complete");
238
247
  console.log(` Config: ${hooksPath}`);
239
248
  console.log(" Daemon: started");
@@ -253,4 +262,4 @@ async function runInstallCmd(_args) {
253
262
  export {
254
263
  runInstallCmd
255
264
  };
256
- //# sourceMappingURL=install-7Z4UZCDW.js.map
265
+ //# sourceMappingURL=install-NA7R44O4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/install.ts","../src/daemon-client.ts"],"sourcesContent":["import {\n getConfigDir,\n getMachineId,\n getApiBaseUrl,\n readLicenseSync,\n writeLicenseSync,\n apiResponseToLicenseInfo,\n type LicenseInfo,\n type LicenseApiResponse,\n} from \"@token-canary/shared\";\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { getDaemonPort as getPort, startDaemonProcess } from \"../daemon-client.js\";\nimport { runAllChecks } from \"./doctor.js\";\n\nconst HOOKS_FILENAME = \"hooks.json\";\nconst FORWARDER_FILENAME = \"forward-hook.cjs\";\nconst CLAUDE_SETTINGS_PATH = join(homedir(), \".claude\", \"settings.json\");\n\nconst FORWARDER_SCRIPT = `\"use strict\";\nconst fs = require(\"fs\");\nconst path = require(\"path\");\nconst http = require(\"http\");\nconst https = require(\"https\");\n\nconst body = fs.readFileSync(0, \"utf8\");\nfunction passThrough() { process.stdout.write(body); process.exit(0); }\n\nlet hooks;\ntry {\n const hooksPath = path.join(path.dirname(__filename), \"hooks.json\");\n hooks = JSON.parse(fs.readFileSync(hooksPath, \"utf8\"));\n} catch {\n passThrough();\n}\n\nconst event = process.argv[2];\nconst url = hooks[event];\nif (!url) { passThrough(); }\n\nconst u = new URL(url);\nconst client = u.protocol === \"https:\" ? https : http;\nconst req = client.request(url, { method: \"POST\", headers: { \"Content-Type\": \"application/json\" } }, (res) => {\n if (res.statusCode !== 200) { passThrough(); return; }\n let data = \"\";\n res.on(\"data\", (c) => (data += c));\n res.on(\"end\", () => { process.stdout.write(data); process.exit(0); });\n});\nreq.setTimeout(5000, () => { req.destroy(); });\nreq.on(\"error\", () => { passThrough(); });\nreq.end(body);\n`;\n\n/** Write forwarder script to config dir. Call this before configureClaudeCodeHooks. */\nasync function writeForwarderScript(configDir: string): Promise<{ ok: boolean; path: string }> {\n const forwarderPath = join(configDir, FORWARDER_FILENAME);\n try {\n await writeFile(forwarderPath, FORWARDER_SCRIPT, \"utf8\");\n return { ok: true, path: forwarderPath };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(`Could not write ${forwarderPath}: ${msg}`);\n return { ok: false, path: forwarderPath };\n }\n}\n\n/** Merge Token Canary hooks into ~/.claude/settings.json. Requires forwarder already written. Returns true if written. */\nasync function configureClaudeCodeHooks(\n configDir: string,\n _hooksConfig: Record<string, string>\n): Promise<boolean> {\n const forwarderPath = join(configDir, FORWARDER_FILENAME);\n\n const commandUserPrompt = `node \"${forwarderPath}\" UserPromptSubmit`;\n const commandPreToolUse = `node \"${forwarderPath}\" PreToolUse`;\n\n let existing: Record<string, unknown> = {};\n try {\n const raw = await readFile(CLAUDE_SETTINGS_PATH, \"utf8\");\n existing = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // no file or invalid json\n }\n\n const existingHooks = (existing.hooks as Record<string, unknown[]>) || {};\n const isOurHook = (entry: unknown): boolean => {\n const hooks = typeof entry === \"object\" && entry !== null && \"hooks\" in entry\n ? (entry as { hooks?: unknown[] }).hooks\n : undefined;\n if (!Array.isArray(hooks)) return false;\n return hooks.some((h) => {\n const c = typeof h === \"object\" && h !== null && \"command\" in h\n ? String((h as { command?: string }).command ?? \"\")\n : \"\";\n return c.includes(forwarderPath);\n });\n };\n\n const tokenCanaryHooks: Record<string, unknown[]> = {\n UserPromptSubmit: [{ hooks: [{ type: \"command\", command: commandUserPrompt }] }],\n PreToolUse: [{ matcher: \"*\", hooks: [{ type: \"command\", command: commandPreToolUse }] }],\n };\n\n const merged: Record<string, unknown[]> = {};\n for (const [event, entries] of Object.entries(existingHooks)) {\n const filtered = Array.isArray(entries)\n ? entries.filter((e) => !isOurHook(e))\n : [];\n merged[event] = filtered;\n }\n for (const [event, entries] of Object.entries(tokenCanaryHooks)) {\n merged[event] = [...(merged[event] || []), ...entries];\n }\n existing.hooks = merged;\n\n try {\n const { mkdir: mkdirFs } = await import(\"node:fs/promises\");\n await mkdirFs(join(homedir(), \".claude\"), { recursive: true });\n await writeFile(CLAUDE_SETTINGS_PATH, JSON.stringify(existing, null, 2), \"utf8\");\n return true;\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(`Could not write ${CLAUDE_SETTINGS_PATH}: ${msg}`);\n return false;\n }\n}\n\nfunction printManualClaudeSteps(forwarderPath: string): void {\n console.log(\"\\nAdd the following to the `hooks` key in ~/.claude/settings.json:\");\n console.log(\"(Create the file and ~/.claude if they do not exist.)\\n\");\n console.log(JSON.stringify({\n UserPromptSubmit: [{ hooks: [{ type: \"command\", command: `node \"${forwarderPath}\" UserPromptSubmit` }] }],\n PreToolUse: [{ matcher: \"*\", hooks: [{ type: \"command\", command: `node \"${forwarderPath}\" PreToolUse` }] }],\n }, null, 2));\n console.log(\"\");\n}\n\nexport async function runInstallCmd(_args: string[]): Promise<number> {\n const checks = await runAllChecks();\n const failed = checks.filter((c) => !c.ok);\n if (failed.length > 0) {\n console.log(\"Environment check\\n\");\n for (const c of checks) {\n console.log(` ${c.ok ? \"✓\" : \"✗\"} ${c.name}: ${c.message}`);\n }\n console.log(\"\\nFix the issues above first, then run: tokencanary doctor\");\n return 1;\n }\n\n const configDir = getConfigDir();\n await mkdir(configDir, { recursive: true });\n let license: LicenseInfo | null = readLicenseSync();\n const isFirstRun = !license;\n let trialSkippedDueToNetwork = false;\n if (!license) {\n const installId = randomUUID();\n const machineId = getMachineId();\n const baseUrl = getApiBaseUrl();\n let res: Response | undefined;\n try {\n res = await fetch(`${baseUrl}/api/install`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ install_id: installId, machine_id: machineId }),\n });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.warn(\"Could not start trial:\", msg);\n console.warn(`Request was to: ${baseUrl}/api/install`);\n console.warn(\n \"If the host cannot be resolved, you may be offline or the server may be unavailable. \" +\n \"Setup will continue; run 'tokencanary setup' again when online to start your trial.\"\n );\n trialSkippedDueToNetwork = true;\n }\n if (!trialSkippedDueToNetwork && res !== undefined) {\n if (!res.ok) {\n const text = await res.text();\n console.error(\"Could not start trial. Server returned:\", res.status, text || res.statusText);\n return 1;\n }\n let data: LicenseApiResponse;\n try {\n data = (await res.json()) as LicenseApiResponse;\n } catch {\n console.error(\"Invalid response from server.\");\n return 1;\n }\n license = apiResponseToLicenseInfo(data);\n writeLicenseSync(license);\n }\n } else {\n const baseUrl = getApiBaseUrl();\n try {\n const res = await fetch(`${baseUrl}/api/license/check`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n install_id: license.installId,\n machine_id: license.machineId,\n }),\n });\n if (res.ok) {\n const data = (await res.json()) as Parameters<typeof apiResponseToLicenseInfo>[0];\n license = apiResponseToLicenseInfo(data);\n writeLicenseSync(license);\n }\n } catch {\n // use cached license\n }\n }\n\n const port = getPort();\n const hooksConfig = {\n UserPromptSubmit: `http://127.0.0.1:${port}/hook/UserPromptSubmit`,\n PreToolUse: `http://127.0.0.1:${port}/hook/PreToolUse`,\n };\n const hooksPath = join(configDir, HOOKS_FILENAME);\n await writeFile(hooksPath, JSON.stringify(hooksConfig, null, 2));\n\n const forwarderResult = await writeForwarderScript(configDir);\n if (!forwarderResult.ok) {\n console.error(\"Setup cannot continue without the forwarder script.\");\n console.error(\"Create the file manually and re-run tokencanary setup.\");\n return 1;\n }\n\n const claudeConfigured = await configureClaudeCodeHooks(configDir, hooksConfig);\n\n const child = startDaemonProcess();\n const pidPath = join(configDir, \"daemon.pid\");\n if (child?.pid != null) {\n await writeFile(pidPath, String(child.pid), \"utf8\").catch(() => {});\n }\n\n console.log(\"\");\n if (isFirstRun && license) {\n console.log(\"Token Canary trial activated\");\n console.log(\"Your 30-day trial has started.\\n\");\n }\n if (trialSkippedDueToNetwork) {\n console.log(\"Trial could not be started (server unreachable). Run 'tokencanary setup' again when online to start your 30-day trial.\\n\");\n }\n console.log(\"Setup complete\");\n console.log(` Config: ${hooksPath}`);\n console.log(\" Daemon: started\");\n if (claudeConfigured) {\n console.log(\" Claude Code: hooks configured automatically\");\n console.log(\"\\nYou can start using Claude Code normally; prompts will be optimized.\");\n } else {\n printManualClaudeSteps(forwarderResult.path);\n }\n console.log(\"\\nUseful commands:\");\n console.log(\" tokencanary status — daemon and license status\");\n console.log(\" tokencanary logs — view recent logs\");\n console.log(\" tokencanary upgrade — subscribe when trial ends\");\n console.log(\"\");\n\n return 0;\n}\n","import { spawn } from \"node:child_process\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { DEFAULT_DAEMON_PORT } from \"@token-canary/shared\";\n\n/**\n * Resolve the daemon entry path relative to the running CLI.\n * When published, cli.js and daemon.js live in the same dist/ dir.\n */\nexport function getDaemonEntryPath(): string {\n const cliUrl = import.meta.url;\n const cliPath = fileURLToPath(cliUrl);\n const cliDir = dirname(cliPath);\n return join(cliDir, \"daemon.js\");\n}\n\nexport function startDaemonProcess(): ReturnType<typeof spawn> {\n const entry = getDaemonEntryPath();\n const child = spawn(process.execPath, [entry], {\n stdio: \"ignore\",\n detached: true,\n });\n child.unref();\n child.on(\"error\", (err) => {\n console.error(\"Token Canary daemon failed to start:\", err.message);\n console.error(\"Daemon path:\", entry);\n });\n return child;\n}\n\nexport function getDaemonPort(): number {\n return Number(process.env.TOKENCANARY_PORT) || DEFAULT_DAEMON_PORT;\n}\n"],"mappings":"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","names":["join","join"]}
@@ -6,7 +6,7 @@ import {
6
6
  isLicenseValid,
7
7
  readLicenseSync,
8
8
  writeLicenseSync
9
- } from "./chunk-GZLLOKQD.js";
9
+ } from "./chunk-DCCFWI4O.js";
10
10
 
11
11
  // src/commands/license.ts
12
12
  async function runLicenseCmd(_args) {
@@ -52,4 +52,4 @@ async function runLicenseCmd(_args) {
52
52
  export {
53
53
  runLicenseCmd
54
54
  };
55
- //# sourceMappingURL=license-7FP2FILW.js.map
55
+ //# sourceMappingURL=license-VKOP67LZ.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getLogDir
3
- } from "./chunk-GZLLOKQD.js";
3
+ } from "./chunk-DCCFWI4O.js";
4
4
 
5
5
  // src/commands/logs.ts
6
6
  import { readdir, readFile } from "fs/promises";
@@ -38,4 +38,4 @@ async function runLogsCmd(_args) {
38
38
  export {
39
39
  runLogsCmd
40
40
  };
41
- //# sourceMappingURL=logs-XMA4R4ST.js.map
41
+ //# sourceMappingURL=logs-U4OOGTGV.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getStatsPath,
3
3
  readSessionStatsSync
4
- } from "./chunk-GZLLOKQD.js";
4
+ } from "./chunk-DCCFWI4O.js";
5
5
 
6
6
  // src/commands/stats.ts
7
7
  import { existsSync } from "fs";
@@ -29,4 +29,4 @@ async function runStatsCmd(_args) {
29
29
  export {
30
30
  runStatsCmd
31
31
  };
32
- //# sourceMappingURL=stats-R5F7RE43.js.map
32
+ //# sourceMappingURL=stats-7NLMKM67.js.map
@@ -6,7 +6,7 @@ import {
6
6
  readLicenseSync,
7
7
  readSessionStatsSync,
8
8
  writeLicenseSync
9
- } from "./chunk-GZLLOKQD.js";
9
+ } from "./chunk-DCCFWI4O.js";
10
10
 
11
11
  // src/commands/status.ts
12
12
  async function runStatusCmd(_args) {
@@ -54,4 +54,4 @@ async function runStatusCmd(_args) {
54
54
  export {
55
55
  runStatusCmd
56
56
  };
57
- //# sourceMappingURL=status-5RPOTLUD.js.map
57
+ //# sourceMappingURL=status-2EAJQ5TH.js.map
@@ -0,0 +1,8 @@
1
+ import {
2
+ runStopCmd
3
+ } from "./chunk-IUASFEXE.js";
4
+ import "./chunk-DCCFWI4O.js";
5
+ export {
6
+ runStopCmd
7
+ };
8
+ //# sourceMappingURL=stop-QJSJTHHO.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runStopCmd
3
- } from "./chunk-DDYZRAAR.js";
3
+ } from "./chunk-IUASFEXE.js";
4
4
  import {
5
5
  getConfigDir
6
- } from "./chunk-GZLLOKQD.js";
6
+ } from "./chunk-DCCFWI4O.js";
7
7
 
8
8
  // src/commands/uninstall.ts
9
9
  import { homedir } from "os";
@@ -90,4 +90,4 @@ async function runUninstallCmd(_args) {
90
90
  export {
91
91
  runUninstallCmd
92
92
  };
93
- //# sourceMappingURL=uninstall-ZW74DUPS.js.map
93
+ //# sourceMappingURL=uninstall-WUG7BEBH.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getUpgradeUrl,
3
3
  readLicenseSync
4
- } from "./chunk-GZLLOKQD.js";
4
+ } from "./chunk-DCCFWI4O.js";
5
5
 
6
6
  // src/commands/upgrade.ts
7
7
  import { exec } from "child_process";
@@ -23,4 +23,4 @@ async function runUpgradeCmd(_args) {
23
23
  export {
24
24
  runUpgradeCmd
25
25
  };
26
- //# sourceMappingURL=upgrade-DKFPZWUX.js.map
26
+ //# sourceMappingURL=upgrade-IR4MHUD2.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokencanary",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "description": "CLI and daemon that reduces Claude Code token usage via hooks and local analysis",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -17,5 +17,8 @@
17
17
  ],
18
18
  "engines": {
19
19
  "node": ">=18"
20
+ },
21
+ "scripts": {
22
+ "postinstall": "node -e 'console.log(\" ✓ Token Canary installed successfully.\\n\\n Get started:\\n tokencanary doctor Check your environment\\n tokencanary setup Configure hooks and start the daemon\\n\\n Then use Claude Code as usual — prompts are optimized automatically.\\n\\n Docs: https://www.npmjs.com/package/tokencanary\\n\")'"
20
23
  }
21
24
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/install.ts","../src/daemon-client.ts"],"sourcesContent":["import {\n getConfigDir,\n getMachineId,\n getApiBaseUrl,\n readLicenseSync,\n writeLicenseSync,\n apiResponseToLicenseInfo,\n type LicenseInfo,\n type LicenseApiResponse,\n} from \"@token-canary/shared\";\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { getDaemonPort as getPort, startDaemonProcess } from \"../daemon-client.js\";\nimport { runAllChecks } from \"./doctor.js\";\n\nconst HOOKS_FILENAME = \"hooks.json\";\nconst FORWARDER_FILENAME = \"forward-hook.cjs\";\nconst CLAUDE_SETTINGS_PATH = join(homedir(), \".claude\", \"settings.json\");\n\nconst FORWARDER_SCRIPT = `\"use strict\";\nconst fs = require(\"fs\");\nconst path = require(\"path\");\nconst http = require(\"http\");\nconst https = require(\"https\");\n\nconst body = fs.readFileSync(0, \"utf8\");\nfunction passThrough() { process.stdout.write(body); process.exit(0); }\n\nlet hooks;\ntry {\n const hooksPath = path.join(path.dirname(__filename), \"hooks.json\");\n hooks = JSON.parse(fs.readFileSync(hooksPath, \"utf8\"));\n} catch {\n passThrough();\n}\n\nconst event = process.argv[2];\nconst url = hooks[event];\nif (!url) { passThrough(); }\n\nconst u = new URL(url);\nconst client = u.protocol === \"https:\" ? https : http;\nconst req = client.request(url, { method: \"POST\", headers: { \"Content-Type\": \"application/json\" } }, (res) => {\n if (res.statusCode !== 200) { passThrough(); return; }\n let data = \"\";\n res.on(\"data\", (c) => (data += c));\n res.on(\"end\", () => { process.stdout.write(data); process.exit(0); });\n});\nreq.setTimeout(5000, () => { req.destroy(); });\nreq.on(\"error\", () => { passThrough(); });\nreq.end(body);\n`;\n\n/** Write forwarder script to config dir. Call this before configureClaudeCodeHooks. */\nasync function writeForwarderScript(configDir: string): Promise<{ ok: boolean; path: string }> {\n const forwarderPath = join(configDir, FORWARDER_FILENAME);\n try {\n await writeFile(forwarderPath, FORWARDER_SCRIPT, \"utf8\");\n return { ok: true, path: forwarderPath };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(`Could not write ${forwarderPath}: ${msg}`);\n return { ok: false, path: forwarderPath };\n }\n}\n\n/** Merge Token Canary hooks into ~/.claude/settings.json. Requires forwarder already written. Returns true if written. */\nasync function configureClaudeCodeHooks(\n configDir: string,\n _hooksConfig: Record<string, string>\n): Promise<boolean> {\n const forwarderPath = join(configDir, FORWARDER_FILENAME);\n\n const commandUserPrompt = `node \"${forwarderPath}\" UserPromptSubmit`;\n const commandPreToolUse = `node \"${forwarderPath}\" PreToolUse`;\n\n let existing: Record<string, unknown> = {};\n try {\n const raw = await readFile(CLAUDE_SETTINGS_PATH, \"utf8\");\n existing = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // no file or invalid json\n }\n\n const existingHooks = (existing.hooks as Record<string, unknown[]>) || {};\n const isOurHook = (entry: unknown): boolean => {\n const hooks = typeof entry === \"object\" && entry !== null && \"hooks\" in entry\n ? (entry as { hooks?: unknown[] }).hooks\n : undefined;\n if (!Array.isArray(hooks)) return false;\n return hooks.some((h) => {\n const c = typeof h === \"object\" && h !== null && \"command\" in h\n ? String((h as { command?: string }).command ?? \"\")\n : \"\";\n return c.includes(forwarderPath);\n });\n };\n\n const tokenCanaryHooks: Record<string, unknown[]> = {\n UserPromptSubmit: [{ hooks: [{ type: \"command\", command: commandUserPrompt }] }],\n PreToolUse: [{ matcher: \"*\", hooks: [{ type: \"command\", command: commandPreToolUse }] }],\n };\n\n const merged: Record<string, unknown[]> = {};\n for (const [event, entries] of Object.entries(existingHooks)) {\n const filtered = Array.isArray(entries)\n ? entries.filter((e) => !isOurHook(e))\n : [];\n merged[event] = filtered;\n }\n for (const [event, entries] of Object.entries(tokenCanaryHooks)) {\n merged[event] = [...(merged[event] || []), ...entries];\n }\n existing.hooks = merged;\n\n try {\n const { mkdir: mkdirFs } = await import(\"node:fs/promises\");\n await mkdirFs(join(homedir(), \".claude\"), { recursive: true });\n await writeFile(CLAUDE_SETTINGS_PATH, JSON.stringify(existing, null, 2), \"utf8\");\n return true;\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(`Could not write ${CLAUDE_SETTINGS_PATH}: ${msg}`);\n return false;\n }\n}\n\nfunction printManualClaudeSteps(forwarderPath: string): void {\n console.log(\"\\nAdd the following to the `hooks` key in ~/.claude/settings.json:\");\n console.log(\"(Create the file and ~/.claude if they do not exist.)\\n\");\n console.log(JSON.stringify({\n UserPromptSubmit: [{ hooks: [{ type: \"command\", command: `node \"${forwarderPath}\" UserPromptSubmit` }] }],\n PreToolUse: [{ matcher: \"*\", hooks: [{ type: \"command\", command: `node \"${forwarderPath}\" PreToolUse` }] }],\n }, null, 2));\n console.log(\"\");\n}\n\nexport async function runInstallCmd(_args: string[]): Promise<number> {\n const checks = await runAllChecks();\n const failed = checks.filter((c) => !c.ok);\n if (failed.length > 0) {\n console.log(\"Environment check\\n\");\n for (const c of checks) {\n console.log(` ${c.ok ? \"✓\" : \"✗\"} ${c.name}: ${c.message}`);\n }\n console.log(\"\\nFix the issues above first, then run: tokencanary doctor\");\n return 1;\n }\n\n const configDir = getConfigDir();\n await mkdir(configDir, { recursive: true });\n let license: LicenseInfo | null = readLicenseSync();\n const isFirstRun = !license;\n if (!license) {\n const installId = randomUUID();\n const machineId = getMachineId();\n const baseUrl = getApiBaseUrl();\n let res: Response;\n try {\n res = await fetch(`${baseUrl}/api/install`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ install_id: installId, machine_id: machineId }),\n });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(\"Could not start trial:\", msg);\n console.error(\"Check your connection and try again.\");\n return 1;\n }\n if (!res.ok) {\n const text = await res.text();\n console.error(\"Could not start trial. Server returned:\", res.status, text || res.statusText);\n return 1;\n }\n let data: LicenseApiResponse;\n try {\n data = (await res.json()) as LicenseApiResponse;\n } catch {\n console.error(\"Invalid response from server.\");\n return 1;\n }\n license = apiResponseToLicenseInfo(data);\n writeLicenseSync(license);\n } else {\n const baseUrl = getApiBaseUrl();\n try {\n const res = await fetch(`${baseUrl}/api/license/check`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n install_id: license.installId,\n machine_id: license.machineId,\n }),\n });\n if (res.ok) {\n const data = (await res.json()) as Parameters<typeof apiResponseToLicenseInfo>[0];\n license = apiResponseToLicenseInfo(data);\n writeLicenseSync(license);\n }\n } catch {\n // use cached license\n }\n }\n\n const port = getPort();\n const hooksConfig = {\n UserPromptSubmit: `http://127.0.0.1:${port}/hook/UserPromptSubmit`,\n PreToolUse: `http://127.0.0.1:${port}/hook/PreToolUse`,\n };\n const hooksPath = join(configDir, HOOKS_FILENAME);\n await writeFile(hooksPath, JSON.stringify(hooksConfig, null, 2));\n\n const forwarderResult = await writeForwarderScript(configDir);\n if (!forwarderResult.ok) {\n console.error(\"Setup cannot continue without the forwarder script.\");\n console.error(\"Create the file manually and re-run tokencanary setup.\");\n return 1;\n }\n\n const claudeConfigured = await configureClaudeCodeHooks(configDir, hooksConfig);\n\n const child = startDaemonProcess();\n const pidPath = join(configDir, \"daemon.pid\");\n if (child?.pid != null) {\n await writeFile(pidPath, String(child.pid), \"utf8\").catch(() => {});\n }\n\n console.log(\"\");\n if (isFirstRun) {\n console.log(\"Token Canary trial activated\");\n console.log(\"Your 30-day trial has started.\\n\");\n }\n console.log(\"Setup complete\");\n console.log(` Config: ${hooksPath}`);\n console.log(\" Daemon: started\");\n if (claudeConfigured) {\n console.log(\" Claude Code: hooks configured automatically\");\n console.log(\"\\nYou can start using Claude Code normally; prompts will be optimized.\");\n } else {\n printManualClaudeSteps(forwarderResult.path);\n }\n console.log(\"\\nUseful commands:\");\n console.log(\" tokencanary status — daemon and license status\");\n console.log(\" tokencanary logs — view recent logs\");\n console.log(\" tokencanary upgrade — subscribe when trial ends\");\n console.log(\"\");\n\n return 0;\n}\n","import { spawn } from \"node:child_process\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { DEFAULT_DAEMON_PORT } from \"@token-canary/shared\";\n\n/**\n * Resolve the daemon entry path relative to the running CLI.\n * When published, cli.js and daemon.js live in the same dist/ dir.\n */\nexport function getDaemonEntryPath(): string {\n const cliUrl = import.meta.url;\n const cliPath = fileURLToPath(cliUrl);\n const cliDir = dirname(cliPath);\n return join(cliDir, \"daemon.js\");\n}\n\nexport function startDaemonProcess(): ReturnType<typeof spawn> {\n const entry = getDaemonEntryPath();\n const child = spawn(process.execPath, [entry], {\n stdio: \"ignore\",\n detached: true,\n });\n child.unref();\n child.on(\"error\", (err) => {\n console.error(\"Token Canary daemon failed to start:\", err.message);\n console.error(\"Daemon path:\", entry);\n });\n return child;\n}\n\nexport function getDaemonPort(): number {\n return Number(process.env.TOKENCANARY_PORT) || DEFAULT_DAEMON_PORT;\n}\n"],"mappings":"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","names":["join","join"]}
@@ -1,8 +0,0 @@
1
- import {
2
- runStopCmd
3
- } from "./chunk-DDYZRAAR.js";
4
- import "./chunk-GZLLOKQD.js";
5
- export {
6
- runStopCmd
7
- };
8
- //# sourceMappingURL=stop-GEAGXYPB.js.map