tokenoscopy 0.3.0 → 0.3.2

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.
@@ -1,7 +1,9 @@
1
+ import { readFileSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ import { z } from 'zod';
5
+
1
6
  // src/auth-mode.ts
2
- import { readFileSync } from "fs";
3
- import { homedir } from "os";
4
- import { join } from "path";
5
7
  function keyWasRejected(key, cfg) {
6
8
  const rejected = cfg?.customApiKeyResponses?.rejected ?? [];
7
9
  return rejected.includes(key.slice(-20));
@@ -157,9 +159,6 @@ function redactValue(value) {
157
159
  }
158
160
  return value;
159
161
  }
160
-
161
- // ../shared/src/events.ts
162
- import { z } from "zod";
163
162
  var agentProductSchema = z.enum([
164
163
  "claude_code",
165
164
  "codex",
@@ -266,12 +265,12 @@ var sessionSummarySchema = z.object({
266
265
  /** Count of transcript records the parser did not recognize (drift signal). */
267
266
  unknown_record_count: z.number().int().nonnegative().default(0)
268
267
  });
269
- var flushUploadSchema = z.object({
268
+ z.object({
270
269
  schema_version: z.literal(1),
271
270
  summary: sessionSummarySchema,
272
271
  events: z.array(sessionEventSchema)
273
272
  });
274
- var hookEventSchema = z.object({
273
+ z.object({
275
274
  hook_event_name: z.string(),
276
275
  session_id: z.string().min(8),
277
276
  transcript_path: z.string().optional(),
@@ -288,14 +287,14 @@ var hookEventSchema = z.object({
288
287
  agent_id: z.string().optional(),
289
288
  agent_type: z.string().optional()
290
289
  }).passthrough();
291
- var guardCheckRequestSchema = z.object({
290
+ z.object({
292
291
  session_id: z.string().min(8),
293
292
  tool_name: z.string(),
294
293
  cwd: z.string().optional(),
295
294
  git_repo: z.string().optional()
296
295
  });
297
296
  var guardDecisionSchema = z.enum(["allow", "ask", "deny"]);
298
- var guardCheckResponseSchema = z.object({
297
+ z.object({
299
298
  decision: guardDecisionSchema,
300
299
  reason: z.string().optional(),
301
300
  budget: z.object({
@@ -357,20 +356,6 @@ function estimateCostUsd(model, usage) {
357
356
  return Math.round(cost * 1e6) / 1e6;
358
357
  }
359
358
 
360
- // ../shared/src/diagnose.ts
361
- var DIAG = {
362
- /** A session counts as live if it produced an event this recently. */
363
- LIVE_WINDOW_MS: 15 * 6e4,
364
- RUNAWAY_MIN_COST: 10,
365
- RUNAWAY_CTX_TOKENS: 4e5,
366
- RUNAWAY_CYCLE: 40,
367
- RUNAWAY_REPEAT: 10,
368
- BLOAT_CTX_TOKENS: 2e5,
369
- BLOAT_MIN_COST: 5,
370
- CYCLE_MIN: 25,
371
- REPEAT_MIN: 5
372
- };
373
-
374
359
  // ../shared/src/guard-contract.ts
375
360
  var GUARD_TIMEOUT_MS = 1500;
376
361
  var ALLOW_CACHE_MS = 15e3;
@@ -734,19 +719,4 @@ function parseTranscript(jsonl) {
734
719
  return { summary, events, warnings };
735
720
  }
736
721
 
737
- export {
738
- classifyAuth,
739
- readClaudeJson,
740
- detectAuth,
741
- detectAuthMode,
742
- costIsRealMoney,
743
- redactText,
744
- redactValue,
745
- GUARD_TIMEOUT_MS,
746
- ALLOW_CACHE_MS,
747
- naturalDedupKey,
748
- truncatePayload,
749
- withRelPath,
750
- repoLabelFromCwd,
751
- parseTranscript
752
- };
722
+ export { ALLOW_CACHE_MS, GUARD_TIMEOUT_MS, classifyAuth, costIsRealMoney, detectAuth, detectAuthMode, naturalDedupKey, parseTranscript, readClaudeJson, redactText, redactValue, repoLabelFromCwd, truncatePayload, withRelPath };
package/dist/cli.js CHANGED
@@ -1,25 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- ALLOW_CACHE_MS,
4
- GUARD_TIMEOUT_MS,
5
- detectAuth,
6
- detectAuthMode,
7
- naturalDedupKey,
8
- parseTranscript,
9
- redactValue,
10
- truncatePayload,
11
- withRelPath
12
- } from "./chunk-L62TVAWB.js";
2
+ import { detectAuth, GUARD_TIMEOUT_MS, parseTranscript, detectAuthMode, withRelPath, redactValue, ALLOW_CACHE_MS, truncatePayload, naturalDedupKey } from './chunk-AZYPMFOF.js';
3
+ import { existsSync, readFileSync, readdirSync, statSync, watch, copyFileSync, writeFileSync, mkdirSync } from 'fs';
4
+ import { homedir, userInfo, hostname, tmpdir } from 'os';
5
+ import { join } from 'path';
6
+ import { execFileSync } from 'child_process';
7
+ import { createInterface } from 'readline/promises';
13
8
 
14
- // src/commands/doctor.ts
15
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
16
- import { homedir as homedir2 } from "os";
17
- import { join as join2 } from "path";
18
-
19
- // src/config.ts
20
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
21
- import { homedir } from "os";
22
- import { join } from "path";
23
9
  var DEFAULT_API_URL = "https://tokenoscopy.com";
24
10
  function configDir() {
25
11
  return process.env.TOKENOSCOPY_CONFIG_DIR ?? join(homedir(), ".config", "tokenoscopy");
@@ -59,10 +45,10 @@ async function runDoctor() {
59
45
  out("\u2717 no config \u2014 run `npx tokenoscopy init`");
60
46
  failures++;
61
47
  }
62
- const settingsPath = join2(homedir2(), ".claude", "settings.json");
63
- if (existsSync2(settingsPath)) {
48
+ const settingsPath = join(homedir(), ".claude", "settings.json");
49
+ if (existsSync(settingsPath)) {
64
50
  try {
65
- const settings = JSON.parse(readFileSync2(settingsPath, "utf8"));
51
+ const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
66
52
  const raw = JSON.stringify(settings.hooks ?? {});
67
53
  const hasHttp = raw.includes("/api/ingest/hook");
68
54
  const hasFlush = raw.includes("tokenoscopy flush");
@@ -87,8 +73,8 @@ async function runDoctor() {
87
73
  }[auth.mode];
88
74
  out(`${auth.mode === "unknown" ? "\u26A0" : "\u2713"} billing lane: ${laneLabel}`);
89
75
  out(` evidence: ${auth.evidence}`);
90
- const projectsDir = join2(homedir2(), ".claude", "projects");
91
- if (existsSync2(projectsDir)) {
76
+ const projectsDir = join(homedir(), ".claude", "projects");
77
+ if (existsSync(projectsDir)) {
92
78
  out(`\u2713 transcript directory readable (${projectsDir})`);
93
79
  } else {
94
80
  out(`\u26A0 no transcript directory yet (${projectsDir}) \u2014 appears after the first session`);
@@ -131,13 +117,6 @@ async function runDoctor() {
131
117
  ${failures} problem(s) found.`);
132
118
  return failures === 0 ? 0 : 1;
133
119
  }
134
-
135
- // src/commands/flush.ts
136
- import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
137
-
138
- // src/identity.ts
139
- import { execFileSync } from "child_process";
140
- import { hostname, userInfo } from "os";
141
120
  function git(args, cwd) {
142
121
  try {
143
122
  const out = execFileSync("git", args, {
@@ -274,14 +253,14 @@ async function runFlush(args) {
274
253
  }
275
254
  }
276
255
  }
277
- if (!transcriptPath || !existsSync3(transcriptPath)) {
256
+ if (!transcriptPath || !existsSync(transcriptPath)) {
278
257
  process.stderr.write("tokenoscopy flush: no transcript to flush\n");
279
258
  return 0;
280
259
  }
281
260
  let upload;
282
261
  try {
283
262
  const { summary, events, warnings } = parseTranscript(
284
- readFileSync3(transcriptPath, "utf8")
263
+ readFileSync(transcriptPath, "utf8")
285
264
  );
286
265
  const identity = detectIdentity(summary.cwd);
287
266
  summary.actor.machine_id = identity.machine;
@@ -351,18 +330,13 @@ async function runFlush(args) {
351
330
  }
352
331
  return 0;
353
332
  }
354
-
355
- // src/commands/guard.ts
356
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
357
- import { tmpdir } from "os";
358
- import { join as join3 } from "path";
359
333
  function cachePath(sessionId) {
360
334
  const safe = sessionId.replace(/[^a-zA-Z0-9-]/g, "").slice(0, 64);
361
- return join3(tmpdir(), `tokenoscopy-guard-${safe}.json`);
335
+ return join(tmpdir(), `tokenoscopy-guard-${safe}.json`);
362
336
  }
363
337
  function readAllowCache(sessionId) {
364
338
  try {
365
- const raw = JSON.parse(readFileSync4(cachePath(sessionId), "utf8"));
339
+ const raw = JSON.parse(readFileSync(cachePath(sessionId), "utf8"));
366
340
  return typeof raw.until === "number" && raw.until > Date.now();
367
341
  } catch {
368
342
  return false;
@@ -370,7 +344,7 @@ function readAllowCache(sessionId) {
370
344
  }
371
345
  function writeAllowCache(sessionId) {
372
346
  try {
373
- writeFileSync2(
347
+ writeFileSync(
374
348
  cachePath(sessionId),
375
349
  JSON.stringify({ until: Date.now() + ALLOW_CACHE_MS })
376
350
  );
@@ -447,13 +421,6 @@ async function runGuard() {
447
421
  return 0;
448
422
  }
449
423
  }
450
-
451
- // src/commands/init.ts
452
- import { execFileSync as execFileSync2 } from "child_process";
453
- import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3, copyFileSync } from "fs";
454
- import { homedir as homedir3 } from "os";
455
- import { join as join4 } from "path";
456
- import { createInterface } from "readline/promises";
457
424
  var MARKER = "tokenoscopy";
458
425
  function isOurs(group) {
459
426
  return JSON.stringify(group).toLowerCase().includes(MARKER);
@@ -462,7 +429,7 @@ function resolveInvocation() {
462
429
  const override = process.env.TOKENOSCOPY_BIN?.trim();
463
430
  if (override) return override;
464
431
  try {
465
- const found = execFileSync2("command", ["-v", "tokenoscopy"], {
432
+ const found = execFileSync("command", ["-v", "tokenoscopy"], {
466
433
  encoding: "utf8",
467
434
  shell: "/bin/sh",
468
435
  timeout: 2e3,
@@ -553,11 +520,11 @@ async function runInit(args) {
553
520
  }
554
521
  const config = { apiUrl, key };
555
522
  saveConfig(config);
556
- const settingsPath = flags.get("--project") ? join4(process.cwd(), ".claude", "settings.json") : join4(homedir3(), ".claude", "settings.json");
523
+ const settingsPath = flags.get("--project") ? join(process.cwd(), ".claude", "settings.json") : join(homedir(), ".claude", "settings.json");
557
524
  let settings = {};
558
- if (existsSync4(settingsPath)) {
525
+ if (existsSync(settingsPath)) {
559
526
  try {
560
- settings = JSON.parse(readFileSync5(settingsPath, "utf8"));
527
+ settings = JSON.parse(readFileSync(settingsPath, "utf8"));
561
528
  copyFileSync(settingsPath, `${settingsPath}.tokenoscopy-backup`);
562
529
  } catch {
563
530
  process.stderr.write(`\u2717 ${settingsPath} exists but isn't valid JSON \u2014 fix it first.
@@ -574,12 +541,12 @@ async function runInit(args) {
574
541
  `);
575
542
  return 0;
576
543
  }
577
- const dir = join4(settingsPath, "..");
578
- if (!existsSync4(dir)) {
579
- const { mkdirSync: mkdirSync2 } = await import("fs");
544
+ const dir = join(settingsPath, "..");
545
+ if (!existsSync(dir)) {
546
+ const { mkdirSync: mkdirSync2 } = await import('fs');
580
547
  mkdirSync2(dir, { recursive: true });
581
548
  }
582
- writeFileSync3(settingsPath, `${JSON.stringify(settings, null, 2)}
549
+ writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
583
550
  `);
584
551
  process.stderr.write(
585
552
  [
@@ -592,15 +559,10 @@ async function runInit(args) {
592
559
  );
593
560
  return 0;
594
561
  }
595
-
596
- // src/commands/tail.ts
597
- import { watch, existsSync as existsSync5, readdirSync, statSync } from "fs";
598
- import { homedir as homedir4 } from "os";
599
- import { join as join5 } from "path";
600
562
  var DEBOUNCE_MS = 3e3;
601
563
  async function runTail(args) {
602
- const projectsDir = process.env.TOKENOSCOPY_PROJECTS_DIR ?? join5(homedir4(), ".claude", "projects");
603
- if (!existsSync5(projectsDir)) {
564
+ const projectsDir = process.env.TOKENOSCOPY_PROJECTS_DIR ?? join(homedir(), ".claude", "projects");
565
+ if (!existsSync(projectsDir)) {
604
566
  process.stderr.write(`tokenoscopy tail: ${projectsDir} does not exist
605
567
  `);
606
568
  return 1;
@@ -612,7 +574,7 @@ async function runTail(args) {
612
574
  const cutoff = Date.now() - days * 24 * 3600 * 1e3;
613
575
  let count = 0;
614
576
  for (const dir of readdirSync(projectsDir)) {
615
- const full = join5(projectsDir, dir);
577
+ const full = join(projectsDir, dir);
616
578
  let entries;
617
579
  try {
618
580
  entries = readdirSync(full);
@@ -621,7 +583,7 @@ async function runTail(args) {
621
583
  }
622
584
  for (const f of entries) {
623
585
  if (!f.endsWith(".jsonl")) continue;
624
- const p = join5(full, f);
586
+ const p = join(full, f);
625
587
  try {
626
588
  if (statSync(p).mtimeMs >= cutoff) {
627
589
  await runFlush(["--transcript", p]);
@@ -640,14 +602,14 @@ async function runTail(args) {
640
602
  const pending = /* @__PURE__ */ new Map();
641
603
  watch(projectsDir, { recursive: true }, (_event, filename) => {
642
604
  if (!filename || !filename.endsWith(".jsonl")) return;
643
- const full = join5(projectsDir, filename);
605
+ const full = join(projectsDir, filename);
644
606
  const existing = pending.get(full);
645
607
  if (existing) clearTimeout(existing);
646
608
  pending.set(
647
609
  full,
648
610
  setTimeout(() => {
649
611
  pending.delete(full);
650
- if (existsSync5(full)) {
612
+ if (existsSync(full)) {
651
613
  void runFlush(["--transcript", full]);
652
614
  }
653
615
  }, DEBOUNCE_MS)
package/dist/index.js CHANGED
@@ -1,22 +1 @@
1
- import {
2
- classifyAuth,
3
- costIsRealMoney,
4
- detectAuth,
5
- detectAuthMode,
6
- parseTranscript,
7
- readClaudeJson,
8
- redactText,
9
- redactValue,
10
- repoLabelFromCwd
11
- } from "./chunk-L62TVAWB.js";
12
- export {
13
- classifyAuth,
14
- costIsRealMoney,
15
- detectAuth,
16
- detectAuthMode,
17
- parseTranscript,
18
- readClaudeJson,
19
- redactText,
20
- redactValue,
21
- repoLabelFromCwd
22
- };
1
+ export { classifyAuth, costIsRealMoney, detectAuth, detectAuthMode, parseTranscript, readClaudeJson, redactText, redactValue, repoLabelFromCwd } from './chunk-AZYPMFOF.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokenoscopy",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Flight recorder & spend control for coding-agent fleets. Records Claude Code sessions (hooks + transcripts), replays them, and enforces budgets.",
5
5
  "license": "MIT",
6
6
  "type": "module",