agent-usage-all-in-one 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2406,6 +2406,20 @@ var init_catalog = __esm({
2406
2406
  billingDomain: { id: "code-assist-subscription", displayName: "Gemini Code Assist" }
2407
2407
  },
2408
2408
  officialCredentialPaths: [".gemini/antigravity-cli", ".gemini/antigravity"]
2409
+ },
2410
+ {
2411
+ id: "cursor",
2412
+ displayName: "Cursor",
2413
+ command: "agent",
2414
+ permissionDescription: "Read the official Agent CLI /usage screen and account email. Connecting starts agent on refresh and may leave Cursor session files under ~/.cursor; credentials stay in Cursor.",
2415
+ credentialOwner: "official-client",
2416
+ experimental: true,
2417
+ expectedCoverage: ["quota"],
2418
+ target: {
2419
+ provider: { id: "cursor", displayName: "Cursor" },
2420
+ billingDomain: { id: "cursor-subscription", displayName: "Cursor subscription" }
2421
+ },
2422
+ officialCredentialPaths: [".cursor"]
2409
2423
  }
2410
2424
  ];
2411
2425
  }
@@ -2418,6 +2432,12 @@ function clampPercent(value) {
2418
2432
  }
2419
2433
  return Math.min(100, Math.max(0, value));
2420
2434
  }
2435
+ function percentFromUsedAndLimit(usedAmount, limitAmount) {
2436
+ if (usedAmount === null || usedAmount === void 0 || limitAmount === null || limitAmount === void 0 || !Number.isFinite(usedAmount) || !Number.isFinite(limitAmount) || limitAmount <= 0) {
2437
+ return null;
2438
+ }
2439
+ return clampPercent(usedAmount / limitAmount * 100);
2440
+ }
2421
2441
  var init_quota_normalization = __esm({
2422
2442
  "src/core/quota-normalization.ts"() {
2423
2443
  "use strict";
@@ -4014,7 +4034,28 @@ function isGrokOfficialModel(model) {
4014
4034
  }
4015
4035
  return false;
4016
4036
  }
4017
- function resolveGrokBillingDomain(model, customEndpoints) {
4037
+ function grokModelFamilyKey(model) {
4038
+ if (!model) return null;
4039
+ let normalized = model.trim().toLowerCase();
4040
+ if (!normalized) return null;
4041
+ if (normalized === "grk" || normalized.startsWith("grk-") || normalized.startsWith("grk/")) {
4042
+ normalized = `grok${normalized.slice(3)}`;
4043
+ }
4044
+ for (; ; ) {
4045
+ const stripped = normalized.replace(/-(high|medium|low|xhigh|build|latest)$/, "");
4046
+ if (stripped === normalized) return normalized;
4047
+ normalized = stripped;
4048
+ }
4049
+ }
4050
+ function resolveGrokBillingDomain(model, customEndpoints, selectedModel) {
4051
+ const selectedKey = selectedModel?.trim().toLowerCase() ?? "";
4052
+ if (selectedKey && customEndpoints?.has(selectedKey)) {
4053
+ const usageFamily = grokModelFamilyKey(model);
4054
+ const selectedFamily = grokModelFamilyKey(selectedModel);
4055
+ if (usageFamily && selectedFamily && usageFamily === selectedFamily) {
4056
+ return customEndpoints.get(selectedKey);
4057
+ }
4058
+ }
4018
4059
  if (!model) return "grok-build-subscription";
4019
4060
  const normalized = model.trim().toLowerCase();
4020
4061
  if (customEndpoints?.has(normalized)) {
@@ -4317,7 +4358,7 @@ function isParsedTranscriptRecord(value) {
4317
4358
  ];
4318
4359
  const cacheWriteTokenBreakdown = asObject(observation.cacheWriteTokenBreakdown);
4319
4360
  const tokenSemantics = asObject(observation.tokenSemantics);
4320
- const structurallyValid = typeof record.dedupeKey === "string" && (record.reportedCostUsd === null || finiteNonNegative(record.reportedCostUsd) !== null) && typeof observation.id === "string" && typeof observation.billingDomainId === "string" && (observation.model === null || typeof observation.model === "string") && (observation.sessionId === void 0 || observation.sessionId === null || typeof observation.sessionId === "string") && typeof observation.observedAt === "string" && Number.isFinite(Date.parse(observation.observedAt)) && requiredTokenFields.every((field) => nonNegativeSafeInteger(observation[field])) && optionalTokenFields.every(
4361
+ const structurallyValid = typeof record.dedupeKey === "string" && (record.grokSelectedModel === void 0 || record.grokSelectedModel === null || typeof record.grokSelectedModel === "string") && (record.reportedCostUsd === null || finiteNonNegative(record.reportedCostUsd) !== null) && typeof observation.id === "string" && typeof observation.billingDomainId === "string" && (observation.model === null || typeof observation.model === "string") && (observation.sessionId === void 0 || observation.sessionId === null || typeof observation.sessionId === "string") && typeof observation.observedAt === "string" && Number.isFinite(Date.parse(observation.observedAt)) && requiredTokenFields.every((field) => nonNegativeSafeInteger(observation[field])) && optionalTokenFields.every(
4321
4362
  (field) => observation[field] === void 0 || observation[field] === null || nonNegativeSafeInteger(observation[field])
4322
4363
  ) && (observation.cacheWriteTokenBreakdown === void 0 || observation.cacheWriteTokenBreakdown === null || cacheWriteTokenBreakdown !== null && nonNegativeSafeInteger(cacheWriteTokenBreakdown.fiveMinute) && nonNegativeSafeInteger(cacheWriteTokenBreakdown.oneHour)) && (observation.tokenSemantics === void 0 || tokenSemantics !== null && ["included-in-output", "separate"].includes(String(tokenSemantics.reasoning)) && ["included-in-input", "separate"].includes(String(tokenSemantics.cacheRead)) && ["included-in-input", "separate"].includes(String(tokenSemantics.cacheWrite))) && (observation.modelAttribution === void 0 || ["known", "unclassified"].includes(String(observation.modelAttribution))) && (observation.timePrecision === void 0 || ["event", "hour", "day", "billing-period", "unknown"].includes(
4323
4364
  String(observation.timePrecision)
@@ -4335,12 +4376,19 @@ function isParsedTranscriptRecord(value) {
4335
4376
  function nonNegativeSafeInteger(value) {
4336
4377
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
4337
4378
  }
4338
- function parseGrokTranscriptLine(line, grokCustomModels) {
4379
+ function grokSelectedModelFromUpdate(update) {
4380
+ const meta = asObject(update._meta);
4381
+ return string(update.model) ?? string(meta?.modelId);
4382
+ }
4383
+ function parseGrokTranscriptLine(line, grokCustomModels, grokState) {
4339
4384
  const record = parseObject(line);
4340
4385
  const params = asObject(record?.params);
4341
4386
  const update = asObject(params?.update);
4387
+ if (!record || !params || !update) return [];
4388
+ const selected = grokSelectedModelFromUpdate(update);
4389
+ if (selected) grokState.selectedModel = selected;
4342
4390
  const usage = asObject(update?.usage);
4343
- if (!record || !params || !update || !usage || update.sessionUpdate !== "turn_completed") {
4391
+ if (!usage || update.sessionUpdate !== "turn_completed") {
4344
4392
  return [];
4345
4393
  }
4346
4394
  const meta = asObject(params._meta);
@@ -4379,9 +4427,14 @@ function parseGrokTranscriptLine(line, grokCustomModels) {
4379
4427
  const dedupeKey = `${sessionId}:${promptId ?? stableId(line)}:${model}`;
4380
4428
  const directCost = grokCostUsd(totals.costUsdTicks);
4381
4429
  const reportedCostUsd = directCost ?? (remainingCost !== null && untickedTokens > 0 ? remainingCost * (grokRecordedTokens(totals) / untickedTokens) : null);
4382
- const billingDomainId = resolveGrokBillingDomain(model, grokCustomModels);
4430
+ const billingDomainId = resolveGrokBillingDomain(
4431
+ model,
4432
+ grokCustomModels,
4433
+ grokState.selectedModel
4434
+ );
4383
4435
  return {
4384
4436
  dedupeKey,
4437
+ grokSelectedModel: grokState.selectedModel,
4385
4438
  observation: {
4386
4439
  id: `grok-transcript:${stableId(dedupeKey)}`,
4387
4440
  billingDomainId,
@@ -4512,8 +4565,8 @@ function isForkedCodexSession(payload) {
4512
4565
  if (string(payload.forked_from_id)) return true;
4513
4566
  const source = asObject(payload.source);
4514
4567
  const subagent = asObject(source?.subagent);
4515
- const spawn7 = asObject(subagent?.thread_spawn);
4516
- return string(spawn7?.parent_thread_id) !== null;
4568
+ const spawn9 = asObject(subagent?.thread_spawn);
4569
+ return string(spawn9?.parent_thread_id) !== null;
4517
4570
  }
4518
4571
  async function listTranscriptFiles(roots, cutoff) {
4519
4572
  const files = [];
@@ -4704,7 +4757,7 @@ function compact(value) {
4704
4757
  function stableId(value) {
4705
4758
  return createHash2("sha256").update(value).digest("hex").slice(0, 24);
4706
4759
  }
4707
- var DSH_PRIMARY_BILLING_DOMAIN_ID, DSH_SESSION_FORMAT_VERSION, DSH_COMPRESSED_SUFFIX, DSH_LINE_HINTS, LocalTranscriptUsageClient;
4760
+ var DSH_PRIMARY_BILLING_DOMAIN_ID, DSH_SESSION_FORMAT_VERSION, DSH_COMPRESSED_SUFFIX, DSH_LINE_HINTS, TRANSCRIPT_CACHE_VERSION, LocalTranscriptUsageClient;
4708
4761
  var init_local_transcript_usage_client = __esm({
4709
4762
  "src/server/local-transcript-usage-client.ts"() {
4710
4763
  "use strict";
@@ -4715,6 +4768,7 @@ var init_local_transcript_usage_client = __esm({
4715
4768
  DSH_SESSION_FORMAT_VERSION = 0;
4716
4769
  DSH_COMPRESSED_SUFFIX = ".jsonl.zstd";
4717
4770
  DSH_LINE_HINTS = ['"usage"', '"request/context"', '"type":"session"'];
4771
+ TRANSCRIPT_CACHE_VERSION = 2;
4718
4772
  LocalTranscriptUsageClient = class {
4719
4773
  #provider;
4720
4774
  #roots;
@@ -4797,7 +4851,8 @@ var init_local_transcript_usage_client = __esm({
4797
4851
  const remapped = cached.records.map((r) => {
4798
4852
  const expectedDomain = resolveGrokBillingDomain(
4799
4853
  r.observation.model,
4800
- this.#grokCustomModels
4854
+ this.#grokCustomModels,
4855
+ r.grokSelectedModel
4801
4856
  );
4802
4857
  if (r.observation.billingDomainId !== expectedDomain) {
4803
4858
  return {
@@ -4826,9 +4881,10 @@ var init_local_transcript_usage_client = __esm({
4826
4881
  model: "",
4827
4882
  unsupportedVersion: false
4828
4883
  };
4884
+ const grokState = { selectedModel: null };
4829
4885
  try {
4830
4886
  for await (const line of readTranscriptLines(file.path)) {
4831
- records.push(...this.#parseLine(line, codexState, dshState));
4887
+ records.push(...this.#parseLine(line, codexState, dshState, grokState));
4832
4888
  if (dshState.unsupportedVersion) {
4833
4889
  return { records: [], complete: false, unsupportedFormat: true };
4834
4890
  }
@@ -4844,7 +4900,7 @@ var init_local_transcript_usage_client = __esm({
4844
4900
  });
4845
4901
  return { records, complete: true, unsupportedFormat: false };
4846
4902
  }
4847
- #parseLine(line, codexState, dshState) {
4903
+ #parseLine(line, codexState, dshState, grokState) {
4848
4904
  if (this.#provider === "claude-code") {
4849
4905
  return line.includes('"usage"') ? compact(parseClaudeTranscriptLine(line)) : [];
4850
4906
  }
@@ -4852,7 +4908,10 @@ var init_local_transcript_usage_client = __esm({
4852
4908
  if (this.#provider === "dsh") {
4853
4909
  return DSH_LINE_HINTS.some((hint) => line.includes(hint)) ? parseDshTranscriptLine(line, dshState) : [];
4854
4910
  }
4855
- return line.includes('"turn_completed"') ? parseGrokTranscriptLine(line, this.#grokCustomModels) : [];
4911
+ if (line.includes('"turn_completed"') || line.includes('"modelId"') || line.includes('"current_mode_update"')) {
4912
+ return parseGrokTranscriptLine(line, this.#grokCustomModels, grokState);
4913
+ }
4914
+ return [];
4856
4915
  }
4857
4916
  async #loadCache() {
4858
4917
  if (this.#cacheLoaded) return;
@@ -4860,7 +4919,7 @@ var init_local_transcript_usage_client = __esm({
4860
4919
  if (!this.#cachePath) return;
4861
4920
  try {
4862
4921
  const stored = JSON.parse(await readFile3(this.#cachePath, "utf8"));
4863
- if (stored.version !== 1 || !Array.isArray(stored.files)) return;
4922
+ if (stored.version !== TRANSCRIPT_CACHE_VERSION || !Array.isArray(stored.files)) return;
4864
4923
  for (const file of stored.files) {
4865
4924
  if (!isCachedTranscriptFile(file)) continue;
4866
4925
  if (file.recordsDigest !== stableId(JSON.stringify(file.records))) continue;
@@ -4876,7 +4935,10 @@ var init_local_transcript_usage_client = __esm({
4876
4935
  const temporary = `${this.#cachePath}.${process.pid}.tmp`;
4877
4936
  await writeFile2(
4878
4937
  temporary,
4879
- JSON.stringify({ version: 1, files: [...this.#fileCache.values()] }),
4938
+ JSON.stringify({
4939
+ version: TRANSCRIPT_CACHE_VERSION,
4940
+ files: [...this.#fileCache.values()]
4941
+ }),
4880
4942
  { mode: 384 }
4881
4943
  );
4882
4944
  await rename2(temporary, this.#cachePath);
@@ -5274,11 +5336,473 @@ var init_antigravity_connector = __esm({
5274
5336
  }
5275
5337
  });
5276
5338
 
5339
+ // src/connectors/cursor/cursor-about-client.ts
5340
+ import { spawn as spawn3 } from "child_process";
5341
+ function runCaptured(command, arguments_, options) {
5342
+ return new Promise((resolve5, reject) => {
5343
+ const child = spawn3(command, arguments_, {
5344
+ env: options.env,
5345
+ stdio: ["ignore", "pipe", "pipe"]
5346
+ });
5347
+ let stdout = "";
5348
+ const timer = setTimeout(() => {
5349
+ child.kill();
5350
+ reject(new Error("agent about timed out"));
5351
+ }, options.timeoutMs);
5352
+ child.stdout.setEncoding("utf8");
5353
+ child.stdout.on("data", (chunk) => {
5354
+ stdout += chunk;
5355
+ });
5356
+ child.once("error", (error) => {
5357
+ clearTimeout(timer);
5358
+ reject(error);
5359
+ });
5360
+ child.once("exit", (code) => {
5361
+ clearTimeout(timer);
5362
+ if (code === 0) resolve5(stdout);
5363
+ else reject(new Error(`agent about exited ${String(code)}`));
5364
+ });
5365
+ });
5366
+ }
5367
+ var CursorAboutAccountClient;
5368
+ var init_cursor_about_client = __esm({
5369
+ "src/connectors/cursor/cursor-about-client.ts"() {
5370
+ "use strict";
5371
+ CursorAboutAccountClient = class {
5372
+ #command;
5373
+ #timeoutMs;
5374
+ #environment;
5375
+ #runCommand;
5376
+ constructor(options = {}) {
5377
+ this.#command = options.command ?? "agent";
5378
+ this.#timeoutMs = options.timeoutMs ?? 8e3;
5379
+ this.#environment = {
5380
+ ...options.environment ?? process.env,
5381
+ LC_ALL: "en_US.UTF-8",
5382
+ LANG: "en_US.UTF-8"
5383
+ };
5384
+ this.#runCommand = options.runCommand ?? runCaptured;
5385
+ }
5386
+ async readAccountIdentifier() {
5387
+ if (!/^[a-zA-Z0-9_./-]+$/.test(this.#command)) return null;
5388
+ try {
5389
+ const stdout = await this.#runCommand(this.#command, ["about", "--format", "json"], {
5390
+ env: this.#environment,
5391
+ timeoutMs: this.#timeoutMs
5392
+ });
5393
+ const parsed = JSON.parse(stdout);
5394
+ return typeof parsed.userEmail === "string" && parsed.userEmail.trim() ? parsed.userEmail.trim() : null;
5395
+ } catch {
5396
+ return null;
5397
+ }
5398
+ }
5399
+ };
5400
+ }
5401
+ });
5402
+
5403
+ // src/connectors/cursor/cursor-connector.ts
5404
+ function mapUsage(usage, observedAt) {
5405
+ if (usage.kind === "unmetered") return [];
5406
+ const buckets = [
5407
+ {
5408
+ id: "included",
5409
+ billingDomainId: CURSOR_BILLING_DOMAIN_ID,
5410
+ label: "Included",
5411
+ usedPercent: clampPercent(usage.included.usedPercent),
5412
+ resetsAt: null,
5413
+ resetLabel: usage.resetLabel,
5414
+ authority: "official-client",
5415
+ observedAt,
5416
+ scope: "account-wide"
5417
+ }
5418
+ ];
5419
+ if (usage.onDemand?.kind === "fixed") {
5420
+ buckets.push({
5421
+ id: "on-demand",
5422
+ billingDomainId: CURSOR_BILLING_DOMAIN_ID,
5423
+ label: "On-Demand",
5424
+ usedPercent: usage.onDemand.usedPercent,
5425
+ usedAmount: usage.onDemand.usedAmount,
5426
+ limitAmount: usage.onDemand.limitAmount,
5427
+ limitCurrency: usage.onDemand.currency,
5428
+ resetsAt: null,
5429
+ resetLabel: usage.resetLabel,
5430
+ authority: "official-client",
5431
+ observedAt,
5432
+ scope: "account-wide",
5433
+ fallbackStatus: "enabled"
5434
+ });
5435
+ } else if (usage.onDemand?.kind === "unlimited") {
5436
+ buckets.push({
5437
+ id: "on-demand",
5438
+ billingDomainId: CURSOR_BILLING_DOMAIN_ID,
5439
+ label: "On-Demand",
5440
+ usedPercent: null,
5441
+ usedAmount: usage.onDemand.usedAmount,
5442
+ limitCurrency: usage.onDemand.currency,
5443
+ resetsAt: null,
5444
+ resetLabel: usage.resetLabel,
5445
+ authority: "official-client",
5446
+ observedAt,
5447
+ scope: "account-wide",
5448
+ fallbackStatus: "enabled"
5449
+ });
5450
+ } else if (usage.onDemand?.kind === "disabled") {
5451
+ buckets.push({
5452
+ id: "on-demand",
5453
+ billingDomainId: CURSOR_BILLING_DOMAIN_ID,
5454
+ label: "On-Demand",
5455
+ usedPercent: null,
5456
+ resetsAt: null,
5457
+ resetLabel: usage.resetLabel,
5458
+ authority: "official-client",
5459
+ observedAt,
5460
+ scope: "account-wide",
5461
+ fallbackStatus: "disabled"
5462
+ });
5463
+ }
5464
+ return buckets;
5465
+ }
5466
+ function safeFailure8(error) {
5467
+ if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
5468
+ return { code: error.code, message: error.message, recovery: error.recovery };
5469
+ }
5470
+ return {
5471
+ code: "cursor-quota-adapter-failed",
5472
+ message: "Cursor subscription quota is unavailable.",
5473
+ recovery: "Open agent, run /usage, then retry."
5474
+ };
5475
+ }
5476
+ var CURSOR_BILLING_DOMAIN_ID, CURSOR_BILLING_DOMAIN_NAME, CursorConnector;
5477
+ var init_cursor_connector = __esm({
5478
+ "src/connectors/cursor/cursor-connector.ts"() {
5479
+ "use strict";
5480
+ init_quota_normalization();
5481
+ CURSOR_BILLING_DOMAIN_ID = "cursor-subscription";
5482
+ CURSOR_BILLING_DOMAIN_NAME = "Cursor subscription";
5483
+ CursorConnector = class {
5484
+ id = "cursor";
5485
+ displayName = "Cursor";
5486
+ consentId = "cursor";
5487
+ #quotaClient;
5488
+ #accountClient;
5489
+ #clock;
5490
+ constructor(options) {
5491
+ this.#quotaClient = options.quotaClient;
5492
+ this.#accountClient = options.accountClient;
5493
+ this.#clock = options.clock ?? (() => /* @__PURE__ */ new Date());
5494
+ }
5495
+ async collect() {
5496
+ const observedAt = this.#clock().toISOString();
5497
+ const warnings = [];
5498
+ let quotaBuckets = [];
5499
+ let completeQuotaBillingDomainIds;
5500
+ try {
5501
+ const usage = await this.#quotaClient.readUsage();
5502
+ completeQuotaBillingDomainIds = [CURSOR_BILLING_DOMAIN_ID];
5503
+ quotaBuckets = mapUsage(usage, observedAt);
5504
+ } catch (error) {
5505
+ warnings.push(safeFailure8(error));
5506
+ }
5507
+ let accountIdentifier = null;
5508
+ if (this.#accountClient) {
5509
+ try {
5510
+ accountIdentifier = await this.#accountClient.readAccountIdentifier();
5511
+ } catch {
5512
+ accountIdentifier = null;
5513
+ }
5514
+ }
5515
+ return {
5516
+ provider: {
5517
+ id: this.id,
5518
+ displayName: this.displayName,
5519
+ accountIdentifier
5520
+ },
5521
+ billingDomains: [{ id: CURSOR_BILLING_DOMAIN_ID, displayName: CURSOR_BILLING_DOMAIN_NAME }],
5522
+ quotaBuckets,
5523
+ completeQuotaBillingDomainIds,
5524
+ usage: [],
5525
+ costs: [],
5526
+ warnings,
5527
+ observedAt
5528
+ };
5529
+ }
5530
+ };
5531
+ }
5532
+ });
5533
+
5534
+ // src/connectors/cursor/cursor-usage-screen-client.ts
5535
+ import { spawn as spawn4 } from "child_process";
5536
+ import { mkdtemp, rm, writeFile as writeFile3 } from "fs/promises";
5537
+ import { tmpdir } from "os";
5538
+ import { join as join5 } from "path";
5539
+ function parseCursorUsageScreen(text) {
5540
+ const normalized = collapseVerticalLetters(stripTerminalControls2(text));
5541
+ const lines = normalized.split(/\r?\n/).map((line) => collapseSpaces(line)).filter(Boolean);
5542
+ const body = `${lines.join("\n")}
5543
+ ${collapseSpaces(normalized)}`;
5544
+ if (NOT_LOGGED_IN.test(body)) {
5545
+ throw new CursorUsageAdapterError(
5546
+ "cursor-not-logged-in",
5547
+ "Cursor Agent CLI is not signed in.",
5548
+ "Run agent login, then retry."
5549
+ );
5550
+ }
5551
+ if (UNMETERED_PLAN.test(body) || SPEND_CHART.test(body)) {
5552
+ return { kind: "unmetered" };
5553
+ }
5554
+ const included = findIncludedPercent(lines);
5555
+ if (included === null) {
5556
+ if (STANDARD_TABLE.test(body) || UNMETERED_PLAN.test(body) || SPEND_CHART.test(body)) {
5557
+ return { kind: "unmetered" };
5558
+ }
5559
+ throw new CursorUsageAdapterError(
5560
+ "cursor-usage-unparsed",
5561
+ "Cursor /usage did not print Included usage.",
5562
+ "Open agent, run /usage, then retry after updating Cursor Agent CLI."
5563
+ );
5564
+ }
5565
+ return {
5566
+ kind: "meters",
5567
+ included: { usedPercent: included },
5568
+ onDemand: parseOnDemand(lines),
5569
+ resetLabel: lines.find((line) => RESET_LINE.test(line)) ?? null
5570
+ };
5571
+ }
5572
+ function findIncludedPercent(lines) {
5573
+ for (const line of lines) {
5574
+ const compact2 = line.match(/^Included:\s*(\d+(?:\.\d+)?)%\s+used/i);
5575
+ if (compact2) return clampPercent(Number(compact2[1]));
5576
+ const table = line.match(/^Included\s+(\d+(?:\.\d+)?)%\s+used/i);
5577
+ if (table) return clampPercent(Number(table[1]));
5578
+ }
5579
+ return null;
5580
+ }
5581
+ function parseOnDemand(lines) {
5582
+ const joined = lines.join("\n");
5583
+ if (/On-demand usage is off/i.test(joined) || /On-Demand:\s*Disabled/i.test(joined)) {
5584
+ return { kind: "disabled" };
5585
+ }
5586
+ if (/On-demand limit unavailable/i.test(joined)) {
5587
+ return null;
5588
+ }
5589
+ for (const line of lines) {
5590
+ const pair = line.match(/On-Demand[:\s]+(\$[\d,]+(?:\.\d+)?)\s*\/\s*(\$[\d,]+(?:\.\d+)?)/i) ?? line.match(/^On-Demand\s+(\$[\d,]+(?:\.\d+)?)\s*\/\s*(\$[\d,]+(?:\.\d+)?)/i);
5591
+ if (pair) {
5592
+ const usedAmount = parseUsd(pair[1]);
5593
+ const limitAmount = parseUsd(pair[2]);
5594
+ const usedPercent = percentFromUsedAndLimit(usedAmount, limitAmount);
5595
+ if (usedAmount === null || limitAmount === null || usedPercent === null) continue;
5596
+ return { kind: "fixed", usedAmount, limitAmount, currency: "USD", usedPercent };
5597
+ }
5598
+ const unlimited = line.match(/On-Demand[:\s]+(\$[\d,]+(?:\.\d+)?)(?:\s*$)/i);
5599
+ if (unlimited && /No monthly limit|No personal limit/i.test(joined)) {
5600
+ const usedAmount = parseUsd(unlimited[1]);
5601
+ if (usedAmount === null) continue;
5602
+ return { kind: "unlimited", usedAmount, currency: "USD" };
5603
+ }
5604
+ }
5605
+ return null;
5606
+ }
5607
+ function parseUsd(value) {
5608
+ const amount = Number(value.replace(/[$,]/g, ""));
5609
+ return Number.isFinite(amount) ? amount : null;
5610
+ }
5611
+ function collapseSpaces(line) {
5612
+ return line.replace(/\s+/g, " ").trim();
5613
+ }
5614
+ function collapseVerticalLetters(text) {
5615
+ const lines = text.split(/\r?\n/);
5616
+ const collapsed = [];
5617
+ let run = "";
5618
+ for (const line of lines) {
5619
+ const trimmed = line.trim();
5620
+ if (trimmed.length === 1 && /[\w%$.:/-]/.test(trimmed)) {
5621
+ run += trimmed;
5622
+ continue;
5623
+ }
5624
+ if (run) {
5625
+ collapsed.push(run);
5626
+ run = "";
5627
+ }
5628
+ collapsed.push(line);
5629
+ }
5630
+ if (run) collapsed.push(run);
5631
+ return collapsed.join("\n");
5632
+ }
5633
+ function stripTerminalControls2(value) {
5634
+ return value.replace(/\u001B\][^\u0007]*(?:\u0007|\u001B\\)/g, "").replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, "").replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "");
5635
+ }
5636
+ function cursorUsageEnvironment(environment) {
5637
+ return {
5638
+ ...environment,
5639
+ LC_ALL: "en_US.UTF-8",
5640
+ LANG: "en_US.UTF-8",
5641
+ INK_SCREEN_READER: "true"
5642
+ };
5643
+ }
5644
+ async function runCursorUsagePtySession(session) {
5645
+ const driver = join5(session.cwd, "run-cursor-usage-pty.py");
5646
+ await writeFile3(driver, CURSOR_USAGE_PTY_DRIVER, { mode: 448 });
5647
+ return new Promise((resolve5, reject) => {
5648
+ const child = spawn4("python3", [driver, session.command], {
5649
+ cwd: session.cwd,
5650
+ env: {
5651
+ ...session.env,
5652
+ TERM: session.env.TERM ?? "xterm-256color",
5653
+ COLUMNS: "120",
5654
+ LINES: "40"
5655
+ },
5656
+ stdio: ["ignore", "pipe", "pipe"]
5657
+ });
5658
+ let output = "";
5659
+ let stderr = "";
5660
+ const timer = setTimeout(() => {
5661
+ child.kill();
5662
+ reject(
5663
+ new CursorUsageAdapterError(
5664
+ "cursor-usage-timeout",
5665
+ "Cursor Agent CLI did not return the usage screen in time.",
5666
+ "Open agent, run /usage, then retry."
5667
+ )
5668
+ );
5669
+ }, session.timeoutMs);
5670
+ child.stdout.setEncoding("utf8");
5671
+ child.stdout.on("data", (chunk) => {
5672
+ output += chunk;
5673
+ });
5674
+ child.stderr.setEncoding("utf8");
5675
+ child.stderr.on("data", (chunk) => {
5676
+ stderr += chunk;
5677
+ });
5678
+ child.once("error", (error) => {
5679
+ clearTimeout(timer);
5680
+ reject(error);
5681
+ });
5682
+ child.once("exit", (code) => {
5683
+ clearTimeout(timer);
5684
+ if (output.trim()) resolve5(output);
5685
+ else reject(new Error(stderr.trim() || `Cursor usage driver exited ${String(code)}`));
5686
+ });
5687
+ });
5688
+ }
5689
+ function unavailableError3(cause) {
5690
+ return new CursorUsageAdapterError(
5691
+ "cursor-quota-adapter-failed",
5692
+ "Cursor subscription quota is unavailable.",
5693
+ "Open agent, run /usage, then retry.",
5694
+ cause instanceof Error ? { cause } : void 0
5695
+ );
5696
+ }
5697
+ var CursorUsageAdapterError, NOT_LOGGED_IN, UNMETERED_PLAN, SPEND_CHART, STANDARD_TABLE, RESET_LINE, ScreenReaderCursorQuotaClient, CURSOR_USAGE_PTY_DRIVER;
5698
+ var init_cursor_usage_screen_client = __esm({
5699
+ "src/connectors/cursor/cursor-usage-screen-client.ts"() {
5700
+ "use strict";
5701
+ init_quota_normalization();
5702
+ CursorUsageAdapterError = class extends Error {
5703
+ code;
5704
+ recovery;
5705
+ constructor(code, message, recovery, options) {
5706
+ super(message, options);
5707
+ this.name = "CursorUsageAdapterError";
5708
+ this.code = code;
5709
+ this.recovery = recovery;
5710
+ }
5711
+ };
5712
+ NOT_LOGGED_IN = /Not logged in\. Run \/login first/i;
5713
+ UNMETERED_PLAN = /Usage details are not available for this plan in the CLI/i;
5714
+ SPEND_CHART = /Personal spend for the current billing period/i;
5715
+ STANDARD_TABLE = /Monthly plan and on-demand usage/i;
5716
+ RESET_LINE = /^(Resets\s+.+)$/i;
5717
+ ScreenReaderCursorQuotaClient = class {
5718
+ #command;
5719
+ #timeoutMs;
5720
+ #environment;
5721
+ #runSession;
5722
+ constructor(options = {}) {
5723
+ this.#command = options.command ?? "agent";
5724
+ this.#timeoutMs = options.timeoutMs ?? 24e3;
5725
+ this.#environment = cursorUsageEnvironment(options.environment ?? process.env);
5726
+ this.#runSession = options.runSession ?? runCursorUsagePtySession;
5727
+ }
5728
+ async readUsage() {
5729
+ if (!/^[a-zA-Z0-9_./-]+$/.test(this.#command)) {
5730
+ throw unavailableError3(new Error("Unsafe Cursor command path"));
5731
+ }
5732
+ const cwd = await mkdtemp(join5(tmpdir(), "agent-usage-cursor-"));
5733
+ try {
5734
+ const output = await this.#runSession({
5735
+ command: this.#command,
5736
+ cwd,
5737
+ env: this.#environment,
5738
+ timeoutMs: this.#timeoutMs
5739
+ });
5740
+ if (NOT_LOGGED_IN.test(output)) {
5741
+ throw new CursorUsageAdapterError(
5742
+ "cursor-not-logged-in",
5743
+ "Cursor Agent CLI is not signed in.",
5744
+ "Run agent login, then retry."
5745
+ );
5746
+ }
5747
+ return parseCursorUsageScreen(output);
5748
+ } catch (error) {
5749
+ if (error instanceof CursorUsageAdapterError) throw error;
5750
+ throw unavailableError3(error);
5751
+ } finally {
5752
+ await rm(cwd, { recursive: true, force: true });
5753
+ }
5754
+ }
5755
+ };
5756
+ CURSOR_USAGE_PTY_DRIVER = `#!/usr/bin/env python3
5757
+ import os, pty, select, sys, time
5758
+ def collect(fd, seconds):
5759
+ data = b""
5760
+ deadline = time.time() + seconds
5761
+ while time.time() < deadline:
5762
+ ready, _, _ = select.select([fd], [], [], max(0.0, deadline - time.time()))
5763
+ if not ready:
5764
+ continue
5765
+ try:
5766
+ chunk = os.read(fd, 8192)
5767
+ except OSError:
5768
+ break
5769
+ if not chunk:
5770
+ break
5771
+ data += chunk
5772
+ return data
5773
+ def write_keys(fd, payload):
5774
+ try:
5775
+ os.write(fd, payload)
5776
+ return True
5777
+ except OSError:
5778
+ return False
5779
+ command = sys.argv[1]
5780
+ pid, fd = pty.fork()
5781
+ if pid == 0:
5782
+ os.execvp(command, [command, "--trust", "--mode", "ask"])
5783
+ captured = collect(fd, 2.5)
5784
+ if write_keys(fd, b"/"):
5785
+ captured += collect(fd, 0.6)
5786
+ if write_keys(fd, b"usage"):
5787
+ captured += collect(fd, 1.6)
5788
+ if write_keys(fd, bytes([13])):
5789
+ captured += collect(fd, 10.0)
5790
+ if write_keys(fd, bytes([27])):
5791
+ captured += collect(fd, 0.5)
5792
+ try:
5793
+ os.kill(pid, 9)
5794
+ except OSError:
5795
+ pass
5796
+ sys.stdout.buffer.write(captured)
5797
+ `;
5798
+ }
5799
+ });
5800
+
5277
5801
  // src/server/antigravity-sqlite-usage-client.ts
5278
5802
  import { createHash as createHash3 } from "crypto";
5279
- import { readdir, readFile as readFile4, rename as rename3, stat as stat2, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
5803
+ import { readdir, readFile as readFile4, rename as rename3, stat as stat2, writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
5280
5804
  import { createRequire as createRequire2 } from "module";
5281
- import { dirname as dirname2, join as join5 } from "path";
5805
+ import { dirname as dirname2, join as join6 } from "path";
5282
5806
  import protobuf from "protobufjs";
5283
5807
  function parseAntigravityDatabase(dbPath, conversationId, fallbackMtimeMs) {
5284
5808
  const db = new DatabaseSync2(dbPath, { readOnly: true });
@@ -5552,7 +6076,7 @@ var init_antigravity_sqlite_usage_client = __esm({
5552
6076
  } catch {
5553
6077
  continue;
5554
6078
  }
5555
- const summariesPath = join5(root, "conversation_summaries.db");
6079
+ const summariesPath = join6(root, "conversation_summaries.db");
5556
6080
  const candidateIds = /* @__PURE__ */ new Set();
5557
6081
  let summariesLoaded = false;
5558
6082
  try {
@@ -5575,7 +6099,7 @@ var init_antigravity_sqlite_usage_client = __esm({
5575
6099
  }
5576
6100
  } catch {
5577
6101
  }
5578
- const convDir = join5(root, "conversations");
6102
+ const convDir = join6(root, "conversations");
5579
6103
  try {
5580
6104
  const entries = await readdir(convDir);
5581
6105
  for (const entry of entries) {
@@ -5584,7 +6108,7 @@ var init_antigravity_sqlite_usage_client = __esm({
5584
6108
  if (summariesLoaded && candidateIds.size > 0 && !candidateIds.has(convId)) {
5585
6109
  continue;
5586
6110
  }
5587
- const fullPath = join5(convDir, entry);
6111
+ const fullPath = join6(convDir, entry);
5588
6112
  if (seenDbPaths.has(fullPath)) continue;
5589
6113
  seenDbPaths.add(fullPath);
5590
6114
  try {
@@ -5645,7 +6169,7 @@ var init_antigravity_sqlite_usage_client = __esm({
5645
6169
  try {
5646
6170
  await mkdir3(dirname2(this.#cachePath), { recursive: true, mode: 448 });
5647
6171
  const temporary = `${this.#cachePath}.${process.pid}.tmp`;
5648
- await writeFile3(
6172
+ await writeFile4(
5649
6173
  temporary,
5650
6174
  JSON.stringify({ version: 1, files: [...this.#fileCache.values()] }),
5651
6175
  { mode: 384 }
@@ -5773,10 +6297,10 @@ var init_grok_telemetry = __esm({
5773
6297
  });
5774
6298
 
5775
6299
  // src/connectors/grok-build/stdio-grok-billing-client.ts
5776
- import { spawn as spawn3 } from "child_process";
6300
+ import { spawn as spawn5 } from "child_process";
5777
6301
  import { open } from "fs/promises";
5778
6302
  import { homedir as homedir3 } from "os";
5779
- import { join as join6 } from "path";
6303
+ import { join as join7 } from "path";
5780
6304
  import { z as z7 } from "zod";
5781
6305
  function billingIsFresh(billing, now) {
5782
6306
  if (!billing.sourceObservedAt) return false;
@@ -5797,7 +6321,7 @@ async function refreshBillingLogViaDashboard(command, timeoutMs) {
5797
6321
  "expect eof"
5798
6322
  ].join("\n");
5799
6323
  await new Promise((resolve5) => {
5800
- const dashboard = spawn3("/usr/bin/expect", ["-c", expectScript2], {
6324
+ const dashboard = spawn5("/usr/bin/expect", ["-c", expectScript2], {
5801
6325
  env: {
5802
6326
  ...process.env,
5803
6327
  AGENT_USAGE_GROK_REFRESH_COMMAND: command,
@@ -5864,7 +6388,7 @@ async function readLogTail(path) {
5864
6388
  await handle.close();
5865
6389
  }
5866
6390
  }
5867
- function unavailableError3(cause) {
6391
+ function unavailableError4(cause) {
5868
6392
  return new GrokBillingAdapterError(
5869
6393
  "grok-client-unavailable",
5870
6394
  "Grok Build billing capability is unavailable.",
@@ -5897,11 +6421,11 @@ var init_stdio_grok_billing_client = __esm({
5897
6421
  constructor(options = {}) {
5898
6422
  this.#command = options.command ?? "grok";
5899
6423
  this.#timeoutMs = options.timeoutMs ?? 8e3;
5900
- this.#spawnProcess = options.spawnProcess ?? ((command, arguments_) => spawn3(command, arguments_, {
6424
+ this.#spawnProcess = options.spawnProcess ?? ((command, arguments_) => spawn5(command, arguments_, {
5901
6425
  stdio: ["pipe", "pipe", "pipe"]
5902
6426
  }));
5903
6427
  this.#readUnifiedLog = options.readUnifiedLog ?? (() => readLogTail(
5904
- join6(process.env.GROK_HOME ?? join6(homedir3(), ".grok"), "logs", "unified.jsonl")
6428
+ join7(process.env.GROK_HOME ?? join7(homedir3(), ".grok"), "logs", "unified.jsonl")
5905
6429
  ));
5906
6430
  this.#refreshBillingLog = options.refreshBillingLog ?? (options.readUnifiedLog ? async () => void 0 : () => refreshBillingLogViaDashboard(this.#command, this.#timeoutMs));
5907
6431
  this.#clock = options.clock ?? (() => /* @__PURE__ */ new Date());
@@ -5913,7 +6437,7 @@ var init_stdio_grok_billing_client = __esm({
5913
6437
  } catch (error) {
5914
6438
  const fallback = await this.#readFreshLogFallback();
5915
6439
  if (fallback) return fallback;
5916
- throw unavailableError3(error);
6440
+ throw unavailableError4(error);
5917
6441
  }
5918
6442
  process2.stderr.resume();
5919
6443
  const peer = new JsonLinePeer2(process2, this.#timeoutMs);
@@ -5962,7 +6486,7 @@ var init_stdio_grok_billing_client = __esm({
5962
6486
  { cause: error }
5963
6487
  );
5964
6488
  }
5965
- throw unavailableError3(error);
6489
+ throw unavailableError4(error);
5966
6490
  } finally {
5967
6491
  peer.close();
5968
6492
  }
@@ -6006,7 +6530,7 @@ var init_stdio_grok_billing_client = __esm({
6006
6530
  this.#timeoutMs = timeoutMs;
6007
6531
  process2.stdout.setEncoding("utf8");
6008
6532
  process2.stdout.on("data", (chunk) => this.#consume(chunk));
6009
- process2.once("error", (error) => this.#fail(unavailableError3(error)));
6533
+ process2.once("error", (error) => this.#fail(unavailableError4(error)));
6010
6534
  process2.once("exit", (code, signal) => {
6011
6535
  if (this.#closed) return;
6012
6536
  this.#fail(
@@ -6044,7 +6568,7 @@ var init_stdio_grok_billing_client = __esm({
6044
6568
  this.#closed = true;
6045
6569
  for (const pending of this.#pending.values()) {
6046
6570
  clearTimeout(pending.timeout);
6047
- pending.reject(unavailableError3(new Error("Grok ACP connection closed")));
6571
+ pending.reject(unavailableError4(new Error("Grok ACP connection closed")));
6048
6572
  }
6049
6573
  this.#pending.clear();
6050
6574
  this.#process.kill();
@@ -6272,7 +6796,7 @@ function endpointFailure(error, label) {
6272
6796
  recovery: "Check network access and xAI status, then retry."
6273
6797
  };
6274
6798
  }
6275
- function safeFailure8(error) {
6799
+ function safeFailure9(error) {
6276
6800
  if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
6277
6801
  return { code: error.code, message: error.message, recovery: error.recovery };
6278
6802
  }
@@ -6527,7 +7051,7 @@ var init_xai_api_connector = __esm({
6527
7051
  costs: [],
6528
7052
  balances: [],
6529
7053
  invoices: [],
6530
- warnings: [safeFailure8(error)],
7054
+ warnings: [safeFailure9(error)],
6531
7055
  observedAt
6532
7056
  };
6533
7057
  }
@@ -6624,10 +7148,10 @@ var init_ecb_exchange_rate_provider = __esm({
6624
7148
  });
6625
7149
 
6626
7150
  // src/server/keychain-secret-store.ts
6627
- import { spawn as spawn4 } from "child_process";
7151
+ import { spawn as spawn6 } from "child_process";
6628
7152
  async function runSecurityCommand(arguments_, input) {
6629
7153
  return await new Promise((resolveResult, reject) => {
6630
- const child = spawn4("/usr/bin/security", arguments_, {
7154
+ const child = spawn6("/usr/bin/security", arguments_, {
6631
7155
  stdio: ["pipe", "pipe", "ignore"]
6632
7156
  });
6633
7157
  let output = "";
@@ -6698,7 +7222,7 @@ var init_keychain_secret_store = __esm({
6698
7222
  });
6699
7223
 
6700
7224
  // src/server/macos-notifier.ts
6701
- import { spawn as spawn5 } from "child_process";
7225
+ import { spawn as spawn7 } from "child_process";
6702
7226
  var MacOsNotifier;
6703
7227
  var init_macos_notifier = __esm({
6704
7228
  "src/server/macos-notifier.ts"() {
@@ -6706,7 +7230,7 @@ var init_macos_notifier = __esm({
6706
7230
  MacOsNotifier = class {
6707
7231
  async notify(event) {
6708
7232
  await new Promise((resolve5, reject) => {
6709
- const child = spawn5(
7233
+ const child = spawn7(
6710
7234
  "/usr/bin/osascript",
6711
7235
  [
6712
7236
  "-e",
@@ -6726,8 +7250,8 @@ var init_macos_notifier = __esm({
6726
7250
  });
6727
7251
 
6728
7252
  // src/server/macos-start-at-login-manager.ts
6729
- import { mkdir as mkdir4, rm, writeFile as writeFile4 } from "fs/promises";
6730
- import { dirname as dirname3, isAbsolute, join as join7 } from "path";
7253
+ import { mkdir as mkdir4, rm as rm2, writeFile as writeFile5 } from "fs/promises";
7254
+ import { dirname as dirname3, isAbsolute, join as join8 } from "path";
6731
7255
  function launchAgentPlist(options) {
6732
7256
  const label = options.label ?? LABEL;
6733
7257
  const arguments_ = [
@@ -6779,15 +7303,15 @@ var init_macos_start_at_login_manager = __esm({
6779
7303
  const label = validateLaunchAgentLabel(options.label ?? LABEL);
6780
7304
  if (options.nodeImport) validateNodeImport(options.nodeImport);
6781
7305
  this.#options = { ...options, label };
6782
- this.#path = join7(options.userHome, "Library", "LaunchAgents", `${label}.plist`);
7306
+ this.#path = join8(options.userHome, "Library", "LaunchAgents", `${label}.plist`);
6783
7307
  }
6784
7308
  async setEnabled(enabled) {
6785
7309
  if (!enabled) {
6786
- await rm(this.#path, { force: true });
7310
+ await rm2(this.#path, { force: true });
6787
7311
  return;
6788
7312
  }
6789
7313
  await mkdir4(dirname3(this.#path), { recursive: true, mode: 448 });
6790
- await writeFile4(this.#path, launchAgentPlist(this.#options), { mode: 384 });
7314
+ await writeFile5(this.#path, launchAgentPlist(this.#options), { mode: 384 });
6791
7315
  }
6792
7316
  async isEnabled() {
6793
7317
  try {
@@ -6904,7 +7428,7 @@ var init_otlp_protobuf = __esm({
6904
7428
  // src/server/local-server.ts
6905
7429
  import { createReadStream as createReadStream2, existsSync, statSync } from "fs";
6906
7430
  import { createServer } from "http";
6907
- import { extname, join as join8, normalize, resolve } from "path";
7431
+ import { extname, join as join9, normalize, resolve } from "path";
6908
7432
  import { randomBytes } from "crypto";
6909
7433
  import { z as z9 } from "zod";
6910
7434
  async function startLocalServer(options) {
@@ -7270,8 +7794,8 @@ async function serveStatic(response, pathname, staticDirectory) {
7270
7794
  const root = resolve(staticDirectory);
7271
7795
  const requested = pathname === "/" ? "index.html" : pathname.replace(/^\/+/, "");
7272
7796
  const normalized = normalize(requested);
7273
- const candidate = resolve(join8(root, normalized));
7274
- const indexFile = join8(root, "index.html");
7797
+ const candidate = resolve(join9(root, normalized));
7798
+ const indexFile = join9(root, "index.html");
7275
7799
  const file = candidate.startsWith(`${root}/`) && existsSync(candidate) && statSync(candidate).isFile() ? candidate : indexFile;
7276
7800
  if (!existsSync(file)) {
7277
7801
  sendText(response, 404, "Dashboard assets are not built");
@@ -7382,7 +7906,7 @@ var init_local_server = __esm({
7382
7906
  // src/server/path-discovery-probe.ts
7383
7907
  import { constants } from "fs";
7384
7908
  import { access } from "fs/promises";
7385
- import { delimiter, isAbsolute as isAbsolute2, join as join9, resolve as resolve2 } from "path";
7909
+ import { delimiter, isAbsolute as isAbsolute2, join as join10, resolve as resolve2 } from "path";
7386
7910
  async function isExecutable(path) {
7387
7911
  try {
7388
7912
  await access(path, constants.X_OK);
@@ -7428,7 +7952,7 @@ var init_path_discovery_probe = __esm({
7428
7952
  async #findExecutable(command) {
7429
7953
  if (command.includes("/")) return await isExecutable(command) ? resolve2(command) : null;
7430
7954
  for (const directory of this.#path.split(delimiter).filter(Boolean)) {
7431
- const candidate = join9(directory, command);
7955
+ const candidate = join10(directory, command);
7432
7956
  if (await isExecutable(candidate)) return candidate;
7433
7957
  }
7434
7958
  return null;
@@ -7622,8 +8146,10 @@ function mapQuotaRow(row) {
7622
8146
  billingDomainId: row.billing_domain_id,
7623
8147
  label: row.label,
7624
8148
  usedPercent: clampPercent(row.used_percent),
8149
+ usedAmount: row.used_amount,
7625
8150
  windowDurationMinutes: row.window_duration_minutes,
7626
8151
  resetsAt: row.resets_at,
8152
+ resetLabel: row.reset_label,
7627
8153
  authority: row.authority,
7628
8154
  observedAt: row.observed_at,
7629
8155
  scope: row.scope ?? void 0,
@@ -8034,7 +8560,11 @@ function allDomainHistories(providers) {
8034
8560
  provider,
8035
8561
  domain,
8036
8562
  history: domain.history,
8037
- includedInHeadline: provider.id === "dsh" || provider.id === "grok" && domain.id !== "xai-api" ? true : domain.id === provider.summaryBillingDomainId
8563
+ includedInHeadline: billingDomainIncludedInHeadline(
8564
+ provider.id,
8565
+ domain.id,
8566
+ provider.summaryBillingDomainId
8567
+ )
8038
8568
  }))
8039
8569
  );
8040
8570
  }
@@ -8797,6 +9327,11 @@ function tokenCoverage(tokens, excludedObservationCount = 0) {
8797
9327
  );
8798
9328
  return scopes.includes("this-mac") || temporalities.includes("delta") ? "partial" : "complete";
8799
9329
  }
9330
+ function billingDomainIncludedInHeadline(providerId, billingDomainId, summaryBillingDomainId) {
9331
+ if (providerId === "dsh") return true;
9332
+ if (providerId === "grok" && billingDomainId !== "xai-api") return true;
9333
+ return summaryBillingDomainId !== null && billingDomainId === summaryBillingDomainId;
9334
+ }
8800
9335
  function selectSummaryBillingDomainId(providerId, domains) {
8801
9336
  if (providerId === "grok") {
8802
9337
  return domains.find((domain) => domain.id === "grok-build-subscription")?.id ?? domains[0]?.id ?? null;
@@ -8926,16 +9461,18 @@ var init_sqlite_usage_repository = __esm({
8926
9461
  }
8927
9462
  const quotaStatement = this.#database.prepare(
8928
9463
  `INSERT INTO quota_buckets (
8929
- provider_id, id, billing_domain_id, label, used_percent, window_duration_minutes,
8930
- resets_at, authority, observed_at,
9464
+ provider_id, id, billing_domain_id, label, used_percent, used_amount,
9465
+ window_duration_minutes, resets_at, reset_label, authority, observed_at,
8931
9466
  scope, status, limit_amount, limit_currency, fallback_status
8932
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9467
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
8933
9468
  ON CONFLICT(provider_id, id) DO UPDATE SET
8934
9469
  billing_domain_id = excluded.billing_domain_id,
8935
9470
  label = excluded.label,
8936
9471
  used_percent = excluded.used_percent,
9472
+ used_amount = excluded.used_amount,
8937
9473
  window_duration_minutes = excluded.window_duration_minutes,
8938
9474
  resets_at = excluded.resets_at,
9475
+ reset_label = excluded.reset_label,
8939
9476
  authority = excluded.authority,
8940
9477
  observed_at = excluded.observed_at,
8941
9478
  scope = excluded.scope,
@@ -8945,19 +9482,22 @@ var init_sqlite_usage_repository = __esm({
8945
9482
  fallback_status = excluded.fallback_status`
8946
9483
  );
8947
9484
  const reportedQuotaDomains = [
8948
- ...new Set(snapshot2.quotaBuckets.map((bucket) => bucket.billingDomainId))
9485
+ .../* @__PURE__ */ new Set([
9486
+ ...snapshot2.completeQuotaBillingDomainIds ?? [],
9487
+ ...snapshot2.quotaBuckets.map((bucket) => bucket.billingDomainId)
9488
+ ])
8949
9489
  ];
8950
9490
  if (reportedQuotaDomains.length > 0) {
8951
- const retiredQuotaStatement = this.#database.prepare(
8952
- `DELETE FROM quota_buckets
8953
- WHERE provider_id = ? AND billing_domain_id = ? AND id NOT IN (${snapshot2.quotaBuckets.map(() => "?").join(", ")})`
8954
- );
8955
9491
  for (const billingDomainId of reportedQuotaDomains) {
8956
- retiredQuotaStatement.run(
8957
- snapshot2.provider.id,
8958
- billingDomainId,
8959
- ...snapshot2.quotaBuckets.map((bucket) => bucket.id)
8960
- );
9492
+ const ids = snapshot2.quotaBuckets.filter((bucket) => bucket.billingDomainId === billingDomainId).map((bucket) => bucket.id);
9493
+ if (ids.length === 0) {
9494
+ this.#database.prepare(`DELETE FROM quota_buckets WHERE provider_id = ? AND billing_domain_id = ?`).run(snapshot2.provider.id, billingDomainId);
9495
+ continue;
9496
+ }
9497
+ this.#database.prepare(
9498
+ `DELETE FROM quota_buckets
9499
+ WHERE provider_id = ? AND billing_domain_id = ? AND id NOT IN (${ids.map(() => "?").join(", ")})`
9500
+ ).run(snapshot2.provider.id, billingDomainId, ...ids);
8961
9501
  }
8962
9502
  }
8963
9503
  for (const bucket of snapshot2.quotaBuckets) {
@@ -8968,8 +9508,10 @@ var init_sqlite_usage_repository = __esm({
8968
9508
  bucket.billingDomainId,
8969
9509
  bucket.label,
8970
9510
  clampedUsedPercent,
9511
+ bucket.usedAmount ?? null,
8971
9512
  bucket.windowDurationMinutes ?? null,
8972
9513
  bucket.resetsAt,
9514
+ bucket.resetLabel ?? null,
8973
9515
  bucket.authority,
8974
9516
  snapshot2.observedAt,
8975
9517
  bucket.scope ?? null,
@@ -9422,11 +9964,15 @@ var init_sqlite_usage_repository = __esm({
9422
9964
  FROM billing_domains WHERE provider_id = ? ORDER BY id`
9423
9965
  ).all(provider.id);
9424
9966
  const summaryBillingDomainId = selectSummaryBillingDomainId(provider.id, domains);
9425
- if (!summaryBillingDomainId) continue;
9426
- headlineDomains.set(`${provider.id}:${summaryBillingDomainId}`, {
9427
- displayName: provider.display_name,
9428
- domainId: summaryBillingDomainId
9429
- });
9967
+ for (const domain of domains) {
9968
+ if (!billingDomainIncludedInHeadline(provider.id, domain.id, summaryBillingDomainId)) {
9969
+ continue;
9970
+ }
9971
+ headlineDomains.set(`${provider.id}:${domain.id}`, {
9972
+ displayName: provider.display_name,
9973
+ domainId: domain.id
9974
+ });
9975
+ }
9430
9976
  }
9431
9977
  const totals = /* @__PURE__ */ new Map();
9432
9978
  const addContribution = (date, providerId, displayName, recordedTokens2) => {
@@ -9959,8 +10505,8 @@ var init_sqlite_usage_repository = __esm({
9959
10505
  }
9960
10506
  #getBillingDomainOverview(providerId, domain, now, query, forecasts, forecastCoverage, degradedDiagnostic, legacyProviderFailure = null) {
9961
10507
  const quotaRows = this.#database.prepare(
9962
- `SELECT id, billing_domain_id, label, used_percent, window_duration_minutes, resets_at,
9963
- authority, observed_at,
10508
+ `SELECT id, billing_domain_id, label, used_percent, used_amount, window_duration_minutes,
10509
+ resets_at, reset_label, authority, observed_at,
9964
10510
  scope, status, limit_amount, limit_currency, fallback_status
9965
10511
  FROM quota_buckets WHERE provider_id = ? AND billing_domain_id = ? ORDER BY id`
9966
10512
  ).all(providerId, domain.id);
@@ -10393,9 +10939,11 @@ var init_sqlite_usage_repository = __esm({
10393
10939
  observed_at TEXT NOT NULL,
10394
10940
  scope TEXT,
10395
10941
  status TEXT,
10942
+ used_amount REAL,
10396
10943
  limit_amount REAL,
10397
10944
  limit_currency TEXT,
10398
10945
  fallback_status TEXT,
10946
+ reset_label TEXT,
10399
10947
  total_tokens INTEGER NOT NULL DEFAULT 0,
10400
10948
  PRIMARY KEY (provider_id, id),
10401
10949
  FOREIGN KEY (provider_id, billing_domain_id)
@@ -10744,9 +11292,11 @@ var init_sqlite_usage_repository = __esm({
10744
11292
  ["window_duration_minutes", "INTEGER"],
10745
11293
  ["scope", "TEXT"],
10746
11294
  ["status", "TEXT"],
11295
+ ["used_amount", "REAL"],
10747
11296
  ["limit_amount", "REAL"],
10748
11297
  ["limit_currency", "TEXT"],
10749
- ["fallback_status", "TEXT"]
11298
+ ["fallback_status", "TEXT"],
11299
+ ["reset_label", "TEXT"]
10750
11300
  ]) {
10751
11301
  if (!quotaColumns.some((column) => column.name === name)) {
10752
11302
  this.#database.exec(`ALTER TABLE quota_buckets ADD COLUMN ${name} ${type}`);
@@ -10763,14 +11313,14 @@ __export(runtime_exports, {
10763
11313
  runDaemon: () => runDaemon
10764
11314
  });
10765
11315
  import { existsSync as existsSync2 } from "fs";
10766
- import { mkdir as mkdir5, rm as rm2 } from "fs/promises";
11316
+ import { mkdir as mkdir5, rm as rm3 } from "fs/promises";
10767
11317
  import { homedir as homedir4 } from "os";
10768
- import { isAbsolute as isAbsolute3, join as join10, resolve as resolve3 } from "path";
11318
+ import { isAbsolute as isAbsolute3, join as join11, resolve as resolve3 } from "path";
10769
11319
  import { fileURLToPath } from "url";
10770
11320
  async function runDaemon(home) {
10771
11321
  await mkdir5(home, { recursive: true, mode: 448 });
10772
11322
  const demoEnabled = process.env.AGENT_USAGE_DEMO === "1";
10773
- const repository = new SqliteUsageRepository(join10(home, "usage.sqlite"), {
11323
+ const repository = new SqliteUsageRepository(join11(home, "usage.sqlite"), {
10774
11324
  hideDemoProvider: !demoEnabled
10775
11325
  });
10776
11326
  const keychainService = process.env.AGENT_USAGE_KEYCHAIN_SERVICE;
@@ -10809,11 +11359,15 @@ async function runDaemon(home) {
10809
11359
  new AntigravityConnector({
10810
11360
  historyClient: new AntigravitySqliteUsageClient({
10811
11361
  roots: [
10812
- join10(homedir4(), ".gemini/antigravity-cli"),
10813
- join10(homedir4(), ".gemini/antigravity")
11362
+ join11(homedir4(), ".gemini/antigravity-cli"),
11363
+ join11(homedir4(), ".gemini/antigravity")
10814
11364
  ],
10815
- cachePath: join10(home, "antigravity-usage-cache.json")
11365
+ cachePath: join11(home, "antigravity-usage-cache.json")
10816
11366
  })
11367
+ }),
11368
+ new CursorConnector({
11369
+ quotaClient: new ScreenReaderCursorQuotaClient(),
11370
+ accountClient: new CursorAboutAccountClient()
10817
11371
  })
10818
11372
  ],
10819
11373
  connectorDefinitions: defaultConnectorDefinitions,
@@ -10845,10 +11399,14 @@ async function runDaemon(home) {
10845
11399
  "grok",
10846
11400
  "xai-api",
10847
11401
  "dsh",
10848
- "antigravity"
11402
+ "antigravity",
11403
+ "cursor"
10849
11404
  ].map((id) => [
10850
11405
  id,
10851
- { minimumIntervalMs: 5 * 60 * 1e3, timeoutMs: id === "claude-code" ? 25e3 : 2e4 }
11406
+ {
11407
+ minimumIntervalMs: 5 * 60 * 1e3,
11408
+ timeoutMs: id === "claude-code" || id === "cursor" ? 25e3 : 2e4
11409
+ }
10852
11410
  ])
10853
11411
  ),
10854
11412
  telemetryIngestors: [
@@ -10897,7 +11455,7 @@ async function runDaemon(home) {
10897
11455
  scheduler.stop();
10898
11456
  await server.close();
10899
11457
  repository.close();
10900
- await rm2(join10(home, "daemon.json"), { force: true });
11458
+ await rm3(join11(home, "daemon.json"), { force: true });
10901
11459
  resolveStop();
10902
11460
  };
10903
11461
  process.once("SIGTERM", () => void stop());
@@ -10908,27 +11466,27 @@ function localTranscriptClient(provider, applicationHome) {
10908
11466
  return new LocalTranscriptUsageClient({
10909
11467
  provider,
10910
11468
  roots: localTranscriptRoots(provider),
10911
- cachePath: join10(applicationHome, "cache", `${provider}-transcripts.json`)
11469
+ cachePath: join11(applicationHome, "cache", `${provider}-transcripts.json`)
10912
11470
  });
10913
11471
  }
10914
11472
  function localTranscriptRoots(provider) {
10915
11473
  if (provider === "codex") {
10916
11474
  const home = resolveHomeOverride(process.env.CODEX_HOME, ".codex");
10917
- return [join10(home, "sessions"), join10(home, "archived_sessions")];
11475
+ return [join11(home, "sessions"), join11(home, "archived_sessions")];
10918
11476
  }
10919
11477
  if (provider === "claude-code") {
10920
- return [join10(resolveHomeOverride(process.env.CLAUDE_CONFIG_DIR, ".claude"), "projects")];
11478
+ return [join11(resolveHomeOverride(process.env.CLAUDE_CONFIG_DIR, ".claude"), "projects")];
10921
11479
  }
10922
11480
  if (provider === "dsh") {
10923
- return [join10(resolveHomeOverride(process.env.DSH_HOME, ".dsh"), "sessions")];
11481
+ return [join11(resolveHomeOverride(process.env.DSH_HOME, ".dsh"), "sessions")];
10924
11482
  }
10925
- return [join10(resolveHomeOverride(process.env.GROK_HOME, ".grok"), "sessions")];
11483
+ return [join11(resolveHomeOverride(process.env.GROK_HOME, ".grok"), "sessions")];
10926
11484
  }
10927
11485
  function resolveHomeOverride(value, fallback) {
10928
11486
  const configured = value?.trim();
10929
- if (!configured) return join10(homedir4(), fallback);
11487
+ if (!configured) return join11(homedir4(), fallback);
10930
11488
  if (configured === "~") return homedir4();
10931
- if (configured.startsWith("~/")) return join10(homedir4(), configured.slice(2));
11489
+ if (configured.startsWith("~/")) return join11(homedir4(), configured.slice(2));
10932
11490
  return isAbsolute3(configured) ? configured : resolve3(configured);
10933
11491
  }
10934
11492
  function createDemoConnector() {
@@ -10974,7 +11532,7 @@ function locateStaticDirectory() {
10974
11532
  fileURLToPath(new URL("./web", import.meta.url)),
10975
11533
  fileURLToPath(new URL("../../dist/web", import.meta.url))
10976
11534
  ];
10977
- return candidates.find((candidate) => existsSync2(join10(candidate, "index.html")));
11535
+ return candidates.find((candidate) => existsSync2(join11(candidate, "index.html")));
10978
11536
  }
10979
11537
  var init_runtime = __esm({
10980
11538
  "src/server/runtime.ts"() {
@@ -10993,6 +11551,9 @@ var init_runtime = __esm({
10993
11551
  init_opencode_go_connector();
10994
11552
  init_dsh_connector();
10995
11553
  init_antigravity_connector();
11554
+ init_cursor_about_client();
11555
+ init_cursor_connector();
11556
+ init_cursor_usage_screen_client();
10996
11557
  init_antigravity_sqlite_usage_client();
10997
11558
  init_grok_build_connector();
10998
11559
  init_grok_telemetry();
@@ -11012,14 +11573,73 @@ var init_runtime = __esm({
11012
11573
  });
11013
11574
 
11014
11575
  // src/cli.ts
11015
- init_redaction();
11016
- init_daemon_state();
11017
- import { spawn as spawn6 } from "child_process";
11576
+ import { spawn as spawn8 } from "child_process";
11018
11577
  import { connect as tcpConnect } from "net";
11019
11578
  import { homedir as homedir5 } from "os";
11020
- import { join as join11, resolve as resolve4 } from "path";
11579
+ import { join as join12, resolve as resolve4 } from "path";
11021
11580
  import { Command } from "commander";
11022
11581
  import open2 from "open";
11582
+
11583
+ // src/lib/quota-bucket-display.ts
11584
+ function formatQuotaMoney(amount, currency, locale) {
11585
+ try {
11586
+ return new Intl.NumberFormat(locale, {
11587
+ style: "currency",
11588
+ currency,
11589
+ minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,
11590
+ maximumFractionDigits: 2
11591
+ }).format(amount);
11592
+ } catch {
11593
+ const digits = Number.isInteger(amount) ? amount.toString() : amount.toFixed(2);
11594
+ return `${currency} ${digits}`;
11595
+ }
11596
+ }
11597
+ function quotaBucketDisplay(bucket, locale, copy) {
11598
+ const resetText = bucket.resetLabel?.trim() || null;
11599
+ if (bucket.fallbackStatus === "disabled") {
11600
+ return {
11601
+ kind: "disabled",
11602
+ primary: copy.onDemandOff,
11603
+ progressPercent: null,
11604
+ resetText
11605
+ };
11606
+ }
11607
+ const currency = bucket.limitCurrency ?? "USD";
11608
+ if (bucket.usedAmount !== null && bucket.usedAmount !== void 0 && bucket.limitAmount !== null && bucket.limitAmount !== void 0) {
11609
+ return {
11610
+ kind: "amount",
11611
+ primary: `${formatQuotaMoney(bucket.usedAmount, currency, locale)} / ${formatQuotaMoney(bucket.limitAmount, currency, locale)}`,
11612
+ progressPercent: bucket.usedPercent,
11613
+ resetText
11614
+ };
11615
+ }
11616
+ if (bucket.usedAmount !== null && bucket.usedAmount !== void 0) {
11617
+ return {
11618
+ kind: "amount",
11619
+ primary: `${formatQuotaMoney(bucket.usedAmount, currency, locale)} \xB7 ${copy.noMonthlyLimit}`,
11620
+ progressPercent: null,
11621
+ resetText
11622
+ };
11623
+ }
11624
+ if (bucket.usedPercent !== null) {
11625
+ return {
11626
+ kind: "percent",
11627
+ primary: `${bucket.usedPercent}% ${copy.used}`,
11628
+ progressPercent: bucket.usedPercent,
11629
+ resetText
11630
+ };
11631
+ }
11632
+ return {
11633
+ kind: "unavailable",
11634
+ primary: copy.unavailable,
11635
+ progressPercent: null,
11636
+ resetText
11637
+ };
11638
+ }
11639
+
11640
+ // src/cli.ts
11641
+ init_redaction();
11642
+ init_daemon_state();
11023
11643
  var program = new Command();
11024
11644
  program.name("agent-usage").description("View local coding-agent quota, token, and cost usage").option("--home <directory>", "application data directory", defaultHome()).option("--no-open", "start the dashboard without opening a browser");
11025
11645
  program.command("status").description("print the current provider overview").option("--json", "emit machine-readable JSON").option("--window <window>", "history window: 24h, 7d, or 30d", "24h").action(async (options) => {
@@ -11331,7 +11951,7 @@ async function ensureDaemon(home) {
11331
11951
  return await requireDaemonState(home);
11332
11952
  } catch {
11333
11953
  process.stderr.write("Agent Usage: starting local web service\u2026\n");
11334
- const child = spawn6(
11954
+ const child = spawn8(
11335
11955
  process.execPath,
11336
11956
  [...process.execArgv, process.argv[1], "--home", home, "serve"],
11337
11957
  {
@@ -11398,7 +12018,7 @@ function formatOverview(overview) {
11398
12018
  ` : "";
11399
12019
  return `${summary}${recommendation}${overview.providers.map((provider) => {
11400
12020
  const quota = provider.quotaBuckets.map(
11401
- (bucket) => `${bucket.label}: ${bucket.usedPercent ?? "?"}% used (${formatEvidence(
12021
+ (bucket) => `${bucket.label}: ${formatQuotaBucket(bucket)} (${formatEvidence(
11402
12022
  bucket.authority,
11403
12023
  bucket.observedAt ?? provider.freshness.lastSuccessAt
11404
12024
  )})`
@@ -11458,6 +12078,15 @@ function formatTokenEvidence(evidence) {
11458
12078
  function formatEvidence(authority, observedAt) {
11459
12079
  return `source ${authority} at ${observedAt ?? "unknown time"}`;
11460
12080
  }
12081
+ function formatQuotaBucket(bucket) {
12082
+ const display = quotaBucketDisplay(bucket, "en", {
12083
+ used: "used",
12084
+ unavailable: "unavailable",
12085
+ noMonthlyLimit: "No monthly limit",
12086
+ onDemandOff: "On-demand usage is off"
12087
+ });
12088
+ return display.resetText ? `${display.primary}; ${display.resetText}` : display.primary;
12089
+ }
11461
12090
  function formatDoctor(report) {
11462
12091
  const providerLines = report.providers.map(
11463
12092
  (provider) => provider.status === "degraded" ? `${provider.displayName} \u2014 degraded${provider.health.errorCode ? ` (${provider.health.errorCode})` : ""}: ${provider.health.recovery ?? "Run refresh and inspect connector diagnostics."}` : `${provider.displayName} \u2014 healthy`
@@ -11473,6 +12102,6 @@ function formatDoctor(report) {
11473
12102
  ].join("\n");
11474
12103
  }
11475
12104
  function defaultHome() {
11476
- return join11(homedir5(), "Library", "Application Support", "Agent Usage");
12105
+ return join12(homedir5(), "Library", "Application Support", "Agent Usage");
11477
12106
  }
11478
12107
  //# sourceMappingURL=cli.js.map