ai-spend-agent 0.7.1 → 0.7.3
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/index.js +5 -8
- package/dist/statuslineRuntime.js +205 -9
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { mkdir, readFile, rm, stat } from "node:fs/promises";
|
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
-
import { analyzeSpend, attributeUsageRecords, buildUsageGlance, buildActivitySnapshot, loadContextHealth, detectLocalCredentials, detectLocalPlans, redactSecrets, readSafeStateText, invalidateConnectedSpendTrustReceipt, resolveSafeScanRoot, resolveSafeStateDirectory, subscriptionPlans, unsafeScanRootReason, selectProviderFinancialHeadlineRecords, writeSafeStateText, verifyConnectedSpendTrustReceipt, verifyConnectedSourceRegistryTrustReceipt, writeConnectedSpendTrustReceipt, loadDeadContext, sampleDeadContext, latestObservedWorkingDirectory, downgradeSampleUsageEvidence, isBundledSampleUsage, loadLocalAgentUsage, loadLocalAgentFinancialUsage, loadSampleUsageData, parseUsageRecord, scanLocalUsageSignals, buildMissingSourcePrompts, confirmMapping, createProviderConnectorStub, createLocalFolderSourceRegistry, createScanAuditLog, fetchProviderUsageRecords, addApprovedSource, normalizeSourceRegistry, downgradeUntrustedSourceRegistryClaims, buildSourceStatuses, slugifySourceId, financialEvidenceForRecords, formatSourceStatuses, readActivitySnapshot, recordActivitySnapshotRefreshFailure, sourceStatusDefinitions, writeActivitySnapshot } from "@agent-finops/core";
|
|
7
|
+
import { analyzeSpend, attributeUsageRecords, buildUsageGlance, buildActivitySnapshot, loadContextHealth, detectLocalCredentials, detectLocalPlans, redactSecrets, readSafeStateText, invalidateConnectedSpendTrustReceipt, resolveSafeScanRoot, resolveSafeStateDirectory, subscriptionPlans, unsafeScanRootReason, selectProviderFinancialHeadlineRecords, writeSafeStateText, verifyConnectedSpendTrustReceipt, verifyConnectedSourceRegistryTrustReceipt, writeConnectedSpendTrustReceipt, loadDeadContext, sampleDeadContext, latestObservedWorkingDirectory, downgradeSampleUsageEvidence, isBundledSampleUsage, loadLocalAgentUsage, loadLocalAgentFinancialUsage, localAgentFormatDescriptors, localAgentFormatLabel, loadSampleUsageData, parseUsageRecord, scanLocalUsageSignals, buildMissingSourcePrompts, confirmMapping, createProviderConnectorStub, createLocalFolderSourceRegistry, createScanAuditLog, fetchProviderUsageRecords, addApprovedSource, normalizeSourceRegistry, downgradeUntrustedSourceRegistryClaims, buildSourceStatuses, slugifySourceId, financialEvidenceForRecords, formatSourceStatuses, readActivitySnapshot, recordActivitySnapshotRefreshFailure, sourceStatusDefinitions, writeActivitySnapshot } from "@agent-finops/core";
|
|
8
8
|
import { StatuslineInstallerError, installClaudeStatusline, uninstallClaudeStatusline } from "./statuslineInstaller.js";
|
|
9
9
|
import { readStatuslineCache, renderStatusline } from "./statuslineRuntime.js";
|
|
10
10
|
import { generateActionPlanMarkdown, generateApplyArtifactMarkdown, generateDemoPackageMarkdown, generateHtmlReport, generateMarkdownReport, generatePlainEnglishSummary, generatePolicyConfigDraftMarkdown, generateReportCardCaption, generateReportCardSvg, generateVerificationPlanMarkdown, groupByDimensions } from "@agent-finops/report";
|
|
@@ -556,7 +556,7 @@ async function doctorSourcesCommand(args) {
|
|
|
556
556
|
localError = sanitizeSecretishError(error instanceof Error ? error.message : String(error));
|
|
557
557
|
}
|
|
558
558
|
const checkedAt = now.toISOString();
|
|
559
|
-
for (const id of
|
|
559
|
+
for (const { id } of localAgentFormatDescriptors) {
|
|
560
560
|
const records = (localLogs?.records ?? []).filter((record) => record.agentId === id);
|
|
561
561
|
const evidence = financialEvidenceForRecords(records);
|
|
562
562
|
const scan = localLogs?.sourceScans.find((entry) => entry.agent === id);
|
|
@@ -704,12 +704,12 @@ function localAgentDiagnosticSummary(diagnostics) {
|
|
|
704
704
|
const unsupportedCount = unsupported
|
|
705
705
|
.reduce((total, diagnostic) => total + diagnostic.count, 0);
|
|
706
706
|
if (unsupportedCount > 0) {
|
|
707
|
-
messages.push(`${unsupportedCount} ${unsupported[0].agent
|
|
707
|
+
messages.push(`${unsupportedCount} ${localAgentFormatLabel(unsupported[0].agent)} token snapshot(s) lacked the input/output components required for pricing.`);
|
|
708
708
|
}
|
|
709
709
|
const malformedCount = malformed
|
|
710
710
|
.reduce((total, diagnostic) => total + diagnostic.count, 0);
|
|
711
711
|
if (malformedCount > 0) {
|
|
712
|
-
messages.push(`${malformedCount} malformed JSONL line(s) were skipped in ${malformed[0].agent
|
|
712
|
+
messages.push(`${malformedCount} malformed JSONL line(s) were skipped in ${localAgentFormatLabel(malformed[0].agent)} transcripts.`);
|
|
713
713
|
}
|
|
714
714
|
return messages.length > 0 ? messages.join(" ") : undefined;
|
|
715
715
|
}
|
|
@@ -1529,10 +1529,7 @@ function formatInitReceipt(input) {
|
|
|
1529
1529
|
return `${plan.agent}: ${known} (${plan.billing})`;
|
|
1530
1530
|
}).join("; ")
|
|
1531
1531
|
: "none detected (billing mode remains unresolved)";
|
|
1532
|
-
const sourceLines = (input.logs?.sourceScans ??
|
|
1533
|
-
emptyInitSourceScan("claude-code"),
|
|
1534
|
-
emptyInitSourceScan("codex")
|
|
1535
|
-
]).map((scan) => {
|
|
1532
|
+
const sourceLines = (input.logs?.sourceScans ?? localAgentFormatDescriptors.map((descriptor) => (emptyInitSourceScan(descriptor.id)))).map((scan) => {
|
|
1536
1533
|
const agentRecords = records.filter((record) => record.agentId === scan.agent);
|
|
1537
1534
|
const priced = agentRecords.filter((record) => typeof record.amountUsd === "number").length;
|
|
1538
1535
|
const skipped = scan.filesSkippedBeforeWindow ?? 0;
|
|
@@ -155,9 +155,18 @@ export function renderStatusline(result, options = {}) {
|
|
|
155
155
|
if (overage) {
|
|
156
156
|
// Billed overage is the sole compact paid-alert bridge and must survive
|
|
157
157
|
// narrow layouts. It never derives from plan pressure.
|
|
158
|
+
if (hasMultipleSubscribedAgents(snapshot)) {
|
|
159
|
+
return assembleMultiSubscriptionOverageLine(snapshot, segments, overage, freshness, tier, columns, now);
|
|
160
|
+
}
|
|
158
161
|
return assembleOverageLine(segments, overage, freshness, columns);
|
|
159
162
|
}
|
|
163
|
+
if (snapshot.mode === "subscription" && hasMultipleSubscribedAgents(snapshot)) {
|
|
164
|
+
return assembleMultiSubscriptionLine(snapshot, freshness, tier, columns, now, options.timeZone);
|
|
165
|
+
}
|
|
160
166
|
if (snapshot.mode === "mixed") {
|
|
167
|
+
if (hasMultipleSubscribedAgents(snapshot)) {
|
|
168
|
+
return assembleMultiSubscriptionMixedLine(snapshot, segments, freshness, tier, columns, now, options.timeZone);
|
|
169
|
+
}
|
|
161
170
|
return assembleMixedLine(snapshot, segments, freshness, tier, columns, now, options.timeZone);
|
|
162
171
|
}
|
|
163
172
|
return assembleLine(segments, freshness, columns, overage);
|
|
@@ -256,6 +265,9 @@ function renderMetered(snapshot, tier) {
|
|
|
256
265
|
return rendered;
|
|
257
266
|
}
|
|
258
267
|
function renderSubscription(snapshot, tier, now, timeZone) {
|
|
268
|
+
if (hasMultipleSubscribedAgents(snapshot)) {
|
|
269
|
+
return renderMultiSubscriptionSegments(snapshot, tier, now, timeZone);
|
|
270
|
+
}
|
|
259
271
|
const agent = selectSubscriptionAgent(snapshot, now);
|
|
260
272
|
if (!agent)
|
|
261
273
|
return ["subscription detected", "runway not reported"];
|
|
@@ -276,6 +288,11 @@ function renderSubscription(snapshot, tier, now, timeZone) {
|
|
|
276
288
|
return segments.length > 0 ? segments : ["subscription detected"];
|
|
277
289
|
}
|
|
278
290
|
function renderMixed(snapshot, tier, now, timeZone) {
|
|
291
|
+
if (hasMultipleSubscribedAgents(snapshot)) {
|
|
292
|
+
const segments = renderMultiSubscriptionSegments(snapshot, tier, now, timeZone);
|
|
293
|
+
appendMeteredSevenDaySegment(snapshot, segments);
|
|
294
|
+
return segments.length > 0 ? segments : ["mixed billing", "amounts unavailable"];
|
|
295
|
+
}
|
|
279
296
|
const agent = selectSubscriptionAgent(snapshot, now);
|
|
280
297
|
const limits = agent ? activeLimits(agent, now) : [];
|
|
281
298
|
const selectedLimits = tier === "full"
|
|
@@ -284,6 +301,42 @@ function renderMixed(snapshot, tier, now, timeZone) {
|
|
|
284
301
|
const segments = selectedLimits.map((limit) => formatLimit(limit, now, timeZone));
|
|
285
302
|
if (limits.length === 0 && tier !== "minimal")
|
|
286
303
|
segments.push("runway not reported");
|
|
304
|
+
appendMeteredSevenDaySegment(snapshot, segments);
|
|
305
|
+
const value = agent?.apiEquivalent.sevenDays;
|
|
306
|
+
if (value?.amountUsd !== null && value?.amountUsd !== undefined &&
|
|
307
|
+
value.financialEvidence === "estimated") {
|
|
308
|
+
segments.push(`sub ~${formatUsd(value.amountUsd)} 7d value`);
|
|
309
|
+
}
|
|
310
|
+
if (agent?.pressure === "extra_usage_credits_exhausted" && tier !== "minimal") {
|
|
311
|
+
segments.push("plan pressure");
|
|
312
|
+
}
|
|
313
|
+
return segments.length > 0 ? segments : ["mixed billing", "amounts unavailable"];
|
|
314
|
+
}
|
|
315
|
+
function renderMultiSubscriptionSegments(snapshot, tier, now, timeZone) {
|
|
316
|
+
const entries = orderedSubscriptionLimits(snapshot, now);
|
|
317
|
+
const selected = tier === "full" ? entries : entries.slice(0, 1);
|
|
318
|
+
const segments = selected.map(({ agent, limit }) => tier === "full"
|
|
319
|
+
? formatAttributedLimit(agent, limit, now, timeZone)
|
|
320
|
+
: formatAttributedLimitCompact(agent, limit));
|
|
321
|
+
if (entries.length === 0 && tier !== "minimal") {
|
|
322
|
+
segments.push("subscription detected", "runway not reported");
|
|
323
|
+
}
|
|
324
|
+
const agents = orderedSubscriptionAgents(snapshot, now);
|
|
325
|
+
const valueAgents = tier === "full" ? agents : agents.slice(0, 1);
|
|
326
|
+
for (const agent of valueAgents) {
|
|
327
|
+
const value = agent.apiEquivalent.sevenDays;
|
|
328
|
+
if (value.amountUsd !== null && value.financialEvidence === "estimated") {
|
|
329
|
+
segments.push(`${subscriptionAgentLabel(agent)} ~${formatUsd(value.amountUsd)} 7d value`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (tier !== "minimal") {
|
|
333
|
+
for (const agent of agents.filter(({ pressure }) => pressure === "extra_usage_credits_exhausted")) {
|
|
334
|
+
segments.push(`${subscriptionAgentLabel(agent)} plan pressure`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return segments.length > 0 ? segments : ["subscription detected"];
|
|
338
|
+
}
|
|
339
|
+
function appendMeteredSevenDaySegment(snapshot, segments) {
|
|
287
340
|
const billed = snapshot.metered?.providerBilled.sevenDays;
|
|
288
341
|
const estimated = snapshot.metered?.apiEquivalent.sevenDays;
|
|
289
342
|
if (billed?.amountUsd !== null && billed?.amountUsd !== undefined &&
|
|
@@ -294,15 +347,6 @@ function renderMixed(snapshot, tier, now, timeZone) {
|
|
|
294
347
|
estimated.financialEvidence === "estimated") {
|
|
295
348
|
segments.push(`metered ~${formatUsd(estimated.amountUsd)} 7d`);
|
|
296
349
|
}
|
|
297
|
-
const value = agent?.apiEquivalent.sevenDays;
|
|
298
|
-
if (value?.amountUsd !== null && value?.amountUsd !== undefined &&
|
|
299
|
-
value.financialEvidence === "estimated") {
|
|
300
|
-
segments.push(`sub ~${formatUsd(value.amountUsd)} 7d value`);
|
|
301
|
-
}
|
|
302
|
-
if (agent?.pressure === "extra_usage_credits_exhausted" && tier !== "minimal") {
|
|
303
|
-
segments.push("plan pressure");
|
|
304
|
-
}
|
|
305
|
-
return segments.length > 0 ? segments : ["mixed billing", "amounts unavailable"];
|
|
306
350
|
}
|
|
307
351
|
function renderUnresolved(snapshot, tier) {
|
|
308
352
|
const value = snapshot.unresolved?.apiEquivalent.sevenDays;
|
|
@@ -324,6 +368,56 @@ function selectSubscriptionAgent(snapshot, now) {
|
|
|
324
368
|
return left.agent.localeCompare(right.agent);
|
|
325
369
|
})[0];
|
|
326
370
|
}
|
|
371
|
+
function hasMultipleSubscribedAgents(snapshot) {
|
|
372
|
+
return (snapshot.subscription?.agents.length ?? 0) > 1;
|
|
373
|
+
}
|
|
374
|
+
function orderedSubscriptionLimits(snapshot, now) {
|
|
375
|
+
return (snapshot.subscription?.agents ?? [])
|
|
376
|
+
.flatMap((agent) => activeLimits(agent, now).map((limit) => ({ agent, limit })))
|
|
377
|
+
.sort(compareSubscriptionLimitEntries);
|
|
378
|
+
}
|
|
379
|
+
function primarySubscriptionLimits(snapshot, now) {
|
|
380
|
+
return (snapshot.subscription?.agents ?? [])
|
|
381
|
+
.flatMap((agent) => {
|
|
382
|
+
const limit = [...activeLimits(agent, now)].sort(compareUrgency)[0];
|
|
383
|
+
return limit ? [{ agent, limit }] : [];
|
|
384
|
+
})
|
|
385
|
+
.sort(compareSubscriptionLimitEntries);
|
|
386
|
+
}
|
|
387
|
+
function compareSubscriptionLimitEntries(left, right) {
|
|
388
|
+
const urgency = compareUrgency(left.limit, right.limit);
|
|
389
|
+
if (urgency !== 0)
|
|
390
|
+
return urgency;
|
|
391
|
+
const observationDifference = Date.parse(right.limit.observedAt) - Date.parse(left.limit.observedAt);
|
|
392
|
+
if (observationDifference !== 0)
|
|
393
|
+
return observationDifference;
|
|
394
|
+
return left.agent.agent.localeCompare(right.agent.agent);
|
|
395
|
+
}
|
|
396
|
+
function orderedSubscriptionAgents(snapshot, now) {
|
|
397
|
+
return [...(snapshot.subscription?.agents ?? [])].sort((left, right) => {
|
|
398
|
+
const leftUrgent = [...activeLimits(left, now)].sort(compareUrgency)[0];
|
|
399
|
+
const rightUrgent = [...activeLimits(right, now)].sort(compareUrgency)[0];
|
|
400
|
+
if (leftUrgent && rightUrgent) {
|
|
401
|
+
const urgency = compareUrgency(leftUrgent, rightUrgent);
|
|
402
|
+
if (urgency !== 0)
|
|
403
|
+
return urgency;
|
|
404
|
+
}
|
|
405
|
+
else if (leftUrgent) {
|
|
406
|
+
return -1;
|
|
407
|
+
}
|
|
408
|
+
else if (rightUrgent) {
|
|
409
|
+
return 1;
|
|
410
|
+
}
|
|
411
|
+
const latestDifference = latestLimitObservation(right, now) - latestLimitObservation(left, now);
|
|
412
|
+
if (latestDifference !== 0)
|
|
413
|
+
return latestDifference;
|
|
414
|
+
const recordsDifference = right.apiEquivalent.sevenDays.recordCount -
|
|
415
|
+
left.apiEquivalent.sevenDays.recordCount;
|
|
416
|
+
if (recordsDifference !== 0)
|
|
417
|
+
return recordsDifference;
|
|
418
|
+
return left.agent.localeCompare(right.agent);
|
|
419
|
+
});
|
|
420
|
+
}
|
|
327
421
|
function latestLimitObservation(agent, now) {
|
|
328
422
|
return Math.max(0, ...activeLimits(agent, now).map((limit) => Date.parse(limit.observedAt)));
|
|
329
423
|
}
|
|
@@ -347,6 +441,24 @@ function formatLimit(limit, now, timeZone) {
|
|
|
347
441
|
const reset = formatReset(limit, now, timeZone);
|
|
348
442
|
return `${label} ${formatPercent(limit.remainingPercent)}% left ↻${reset}`;
|
|
349
443
|
}
|
|
444
|
+
function subscriptionAgentLabel(agent) {
|
|
445
|
+
return agent.agent === "claude-code" ? "claude" : "codex";
|
|
446
|
+
}
|
|
447
|
+
function formatAttributedLimit(agent, limit, now, timeZone) {
|
|
448
|
+
const label = limit.kind === "five-hour" ? "5h" : "week";
|
|
449
|
+
return `${subscriptionAgentLabel(agent)} ${label} ${formatPercent(limit.remainingPercent)}% ↻${formatReset(limit, now, timeZone)}`;
|
|
450
|
+
}
|
|
451
|
+
function formatAttributedLimitCompact(agent, limit) {
|
|
452
|
+
const label = limit.kind === "five-hour" ? "5h" : "wk";
|
|
453
|
+
return `${subscriptionAgentLabel(agent)} ${label} ${formatPercent(limit.remainingPercent)}%`;
|
|
454
|
+
}
|
|
455
|
+
function formatAttributedValue(agent, compact = false) {
|
|
456
|
+
const value = agent.apiEquivalent.sevenDays;
|
|
457
|
+
if (value.amountUsd === null || value.financialEvidence !== "estimated")
|
|
458
|
+
return undefined;
|
|
459
|
+
const window = compact ? "/7d" : " 7d";
|
|
460
|
+
return `${subscriptionAgentLabel(agent)} ~${formatUsd(value.amountUsd)}${window} value`;
|
|
461
|
+
}
|
|
350
462
|
function formatReset(limit, now, timeZone) {
|
|
351
463
|
const reset = new Date(limit.resetsAt);
|
|
352
464
|
const zone = validTimeZone(timeZone) ? timeZone : undefined;
|
|
@@ -444,6 +556,21 @@ function assembleOverageLine(segments, overage, freshness, columns) {
|
|
|
444
556
|
["billed"]
|
|
445
557
|
], columns);
|
|
446
558
|
}
|
|
559
|
+
function assembleMultiSubscriptionOverageLine(snapshot, fullSegments, overage, freshness, tier, columns, now) {
|
|
560
|
+
const primary = primarySubscriptionLimits(snapshot, now);
|
|
561
|
+
const compactPrimary = primary.map(({ agent, limit }) => formatAttributedLimitCompact(agent, limit));
|
|
562
|
+
const urgent = compactPrimary[0];
|
|
563
|
+
const candidates = [];
|
|
564
|
+
if (tier === "full")
|
|
565
|
+
candidates.push(["aibill", ...fullSegments, overage, freshness]);
|
|
566
|
+
if (tier !== "minimal") {
|
|
567
|
+
candidates.push(["aibill", ...compactPrimary, overage, freshness]);
|
|
568
|
+
}
|
|
569
|
+
if (urgent)
|
|
570
|
+
candidates.push(["aibill", urgent, overage, freshness]);
|
|
571
|
+
candidates.push(["aibill", overage, freshness], ["aibill", overage], [overage], ["OVERAGE billed"], ["billed"]);
|
|
572
|
+
return firstFittingLine(candidates, columns);
|
|
573
|
+
}
|
|
447
574
|
function assembleMixedLine(snapshot, fullSegments, freshness, tier, columns, now, timeZone) {
|
|
448
575
|
const agent = selectSubscriptionAgent(snapshot, now);
|
|
449
576
|
const urgent = agent ? [...activeLimits(agent, now)].sort(compareUrgency)[0] : undefined;
|
|
@@ -486,6 +613,75 @@ function assembleMixedLine(snapshot, fullSegments, freshness, tier, columns, now
|
|
|
486
613
|
candidates.push(["mix", runway, meteredClear, shortFreshness], ["mix", runway.replace("↻", ""), meteredClear], ["mix", meteredClear], ["mix", metered], ["mix"]);
|
|
487
614
|
return firstFittingLine(candidates, columns);
|
|
488
615
|
}
|
|
616
|
+
function assembleMultiSubscriptionLine(snapshot, freshness, tier, columns, now, timeZone) {
|
|
617
|
+
const entries = orderedSubscriptionLimits(snapshot, now);
|
|
618
|
+
const primary = primarySubscriptionLimits(snapshot, now);
|
|
619
|
+
const agents = orderedSubscriptionAgents(snapshot, now);
|
|
620
|
+
const fullLimits = entries.map(({ agent, limit }) => formatAttributedLimit(agent, limit, now, timeZone));
|
|
621
|
+
const compactLimits = entries.map(({ agent, limit }) => formatAttributedLimitCompact(agent, limit));
|
|
622
|
+
const compactPrimary = primary.map(({ agent, limit }) => formatAttributedLimitCompact(agent, limit));
|
|
623
|
+
const fullValues = agents.flatMap((agent) => formatAttributedValue(agent) ?? []);
|
|
624
|
+
const compactValues = agents.flatMap((agent) => formatAttributedValue(agent, true) ?? []);
|
|
625
|
+
const pressure = agents
|
|
626
|
+
.filter((agent) => agent.pressure === "extra_usage_credits_exhausted")
|
|
627
|
+
.map((agent) => `${subscriptionAgentLabel(agent)} plan pressure`);
|
|
628
|
+
const noRunway = entries.length === 0 ? ["subscription detected", "runway not reported"] : [];
|
|
629
|
+
const urgent = compactPrimary[0];
|
|
630
|
+
const urgentValue = agents[0] ? formatAttributedValue(agents[0], true) : undefined;
|
|
631
|
+
const candidates = [];
|
|
632
|
+
if (tier === "full") {
|
|
633
|
+
candidates.push(["aibill", ...noRunway, ...fullLimits, ...fullValues, ...pressure, freshness]);
|
|
634
|
+
}
|
|
635
|
+
if (tier !== "minimal") {
|
|
636
|
+
candidates.push(["aibill", ...noRunway, ...compactLimits, ...compactValues, ...pressure, freshness], ["aibill", ...noRunway, ...compactLimits, freshness], ["aibill", ...noRunway, ...compactPrimary, ...compactValues, freshness], ["aibill", ...noRunway, ...compactPrimary, freshness]);
|
|
637
|
+
}
|
|
638
|
+
if (urgent) {
|
|
639
|
+
if (urgentValue)
|
|
640
|
+
candidates.push(["aibill", urgent, urgentValue, freshness]);
|
|
641
|
+
candidates.push(["aibill", urgent, freshness], ["aibill", urgent], [urgent]);
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
candidates.push(["aibill", "runway n/r", freshness], ["runway n/r", freshness], ["aibill", ...noRunway, ...compactValues, freshness], ["aibill", ...compactValues, freshness], ["aibill", "subscription detected", freshness], ["subscription detected"]);
|
|
645
|
+
}
|
|
646
|
+
return firstFittingLine(candidates, columns);
|
|
647
|
+
}
|
|
648
|
+
function assembleMultiSubscriptionMixedLine(snapshot, fullSegments, freshness, tier, columns, now, timeZone) {
|
|
649
|
+
const entries = orderedSubscriptionLimits(snapshot, now);
|
|
650
|
+
const primary = primarySubscriptionLimits(snapshot, now);
|
|
651
|
+
const agents = orderedSubscriptionAgents(snapshot, now);
|
|
652
|
+
const compactLimits = entries.map(({ agent, limit }) => formatAttributedLimitCompact(agent, limit));
|
|
653
|
+
const compactPrimary = primary.map(({ agent, limit }) => formatAttributedLimitCompact(agent, limit));
|
|
654
|
+
const compactValues = agents.flatMap((agent) => formatAttributedValue(agent, true) ?? []);
|
|
655
|
+
const noRunway = entries.length === 0 ? ["subscription detected", "runway not reported"] : [];
|
|
656
|
+
const urgent = compactPrimary[0];
|
|
657
|
+
const urgentValue = agents[0] ? formatAttributedValue(agents[0], true) : undefined;
|
|
658
|
+
const meteredBilled = snapshot.metered?.providerBilled.sevenDays;
|
|
659
|
+
const meteredEstimated = snapshot.metered?.apiEquivalent.sevenDays;
|
|
660
|
+
const metered = meteredBilled?.amountUsd !== null && meteredBilled?.amountUsd !== undefined &&
|
|
661
|
+
meteredBilled.financialEvidence === "verified"
|
|
662
|
+
? `metered ${formatBilledUsd(meteredBilled.amountUsd)}/7d billed`
|
|
663
|
+
: meteredEstimated?.amountUsd !== null && meteredEstimated?.amountUsd !== undefined &&
|
|
664
|
+
meteredEstimated.financialEvidence === "estimated"
|
|
665
|
+
? `metered ~${formatUsd(meteredEstimated.amountUsd)}/7d`
|
|
666
|
+
: "metered n/r";
|
|
667
|
+
const shortFreshness = compactFreshness(freshness);
|
|
668
|
+
const candidates = [];
|
|
669
|
+
if (tier === "full")
|
|
670
|
+
candidates.push(["aibill", ...fullSegments, freshness]);
|
|
671
|
+
if (tier !== "minimal") {
|
|
672
|
+
candidates.push(["aibill", "mix", ...noRunway, ...compactLimits, metered, ...compactValues, freshness], ["aibill", "mix", ...noRunway, ...compactPrimary, metered, freshness]);
|
|
673
|
+
}
|
|
674
|
+
if (urgent) {
|
|
675
|
+
if (urgentValue)
|
|
676
|
+
candidates.push(["aibill", "mix", urgent, metered, urgentValue, shortFreshness]);
|
|
677
|
+
candidates.push(["aibill", "mix", urgent, metered, shortFreshness], ["mix", urgent, metered], ["aibill", urgent, shortFreshness], ["aibill", urgent], [urgent]);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
candidates.push(["aibill", "mix", ...noRunway, metered, freshness], ["aibill", ...noRunway, freshness], ["aibill", "mix", "runway n/r", metered, shortFreshness], ["mix", "runway n/r", metered, shortFreshness], ["mix", "runway n/r", shortFreshness]);
|
|
681
|
+
}
|
|
682
|
+
candidates.push(["mix", metered, shortFreshness], ["mix", metered], ["mix"]);
|
|
683
|
+
return firstFittingLine(candidates, columns);
|
|
684
|
+
}
|
|
489
685
|
function formatLimitCompact(limit) {
|
|
490
686
|
const label = limit.kind === "five-hour" ? "5h" : "wk";
|
|
491
687
|
return `${label} ${formatPercent(limit.remainingPercent)}% left`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-spend-agent",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Local-first financial accountability CLI for Claude Code and Codex work, cost evidence, attribution, runway, provenance, and Context Health.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"prepack": "npm run build"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@agent-finops/core": "0.7.
|
|
58
|
-
"@agent-finops/report": "0.7.
|
|
57
|
+
"@agent-finops/core": "0.7.3",
|
|
58
|
+
"@agent-finops/report": "0.7.3",
|
|
59
59
|
"yocto-spinner": "^1.2.0"
|
|
60
60
|
}
|
|
61
61
|
}
|