ai-spend-agent 0.1.5 → 0.2.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/index.js +55 -2
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { realpathSync } from "node:fs";
|
|
|
3
3
|
import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, extname, join, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
-
import { analyzeSpend, attributeUsageRecords, detectLocalCredentials, redactSecrets, unsafeScanRootReason, loadDeadContext, sampleDeadContext, loadLocalAgentUsage, loadSampleUsageData, scanLocalUsageSignals, buildMissingSourcePrompts, confirmMapping, createProviderConnectorStub, createLocalFolderSourceRegistry, createScanAuditLog, fetchProviderUsageRecords, addApprovedSource, slugifySourceId } from "@agent-finops/core";
|
|
6
|
+
import { analyzeSpend, attributeUsageRecords, detectLocalCredentials, detectLocalPlans, redactSecrets, subscriptionPlans, unsafeScanRootReason, loadDeadContext, sampleDeadContext, loadLocalAgentUsage, loadSampleUsageData, scanLocalUsageSignals, buildMissingSourcePrompts, confirmMapping, createProviderConnectorStub, createLocalFolderSourceRegistry, createScanAuditLog, fetchProviderUsageRecords, addApprovedSource, slugifySourceId } from "@agent-finops/core";
|
|
7
7
|
import { generateActionPlanMarkdown, generateApplyArtifactMarkdown, generateDemoPackageMarkdown, generateHtmlReport, generateMarkdownReport, generatePlainEnglishSummary, generatePolicyConfigDraftMarkdown, generateReportCardCaption, generateReportCardSvg, generateVerificationPlanMarkdown, groupByDimensions } from "@agent-finops/report";
|
|
8
8
|
export async function runCli(argv = process.argv.slice(2)) {
|
|
9
9
|
if (argv.includes("--help") || argv.includes("-h") || argv[0] === "help") {
|
|
@@ -70,6 +70,27 @@ async function quickstartCommand(args) {
|
|
|
70
70
|
const summary = analyzeSpend(records);
|
|
71
71
|
const groupBy = args.groupBy ?? "model";
|
|
72
72
|
const color = args.noColor ? false : undefined;
|
|
73
|
+
// Persona: --plan override wins; otherwise read the plans the coding agents
|
|
74
|
+
// themselves persisted locally (read-only, whitelisted fields, no network).
|
|
75
|
+
let detectedPlans;
|
|
76
|
+
if (args.plan) {
|
|
77
|
+
const override = planOverrideFromFlag(args.plan);
|
|
78
|
+
if (!override) {
|
|
79
|
+
return {
|
|
80
|
+
exitCode: 1,
|
|
81
|
+
stdout: "",
|
|
82
|
+
stderr: `Unknown --plan "${args.plan}". Valid plans: ${subscriptionPlans.map((plan) => plan.id).join(", ")}`
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
detectedPlans = [override];
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
detectedPlans = await detectLocalPlans({
|
|
89
|
+
// Env overrides keep tests (and unusual installs) isolated from $HOME.
|
|
90
|
+
claudeConfigPath: process.env.AI_SPEND_CLAUDE_CONFIG,
|
|
91
|
+
codexAuthPath: process.env.AI_SPEND_CODEX_AUTH
|
|
92
|
+
}).catch(() => []);
|
|
93
|
+
}
|
|
73
94
|
// Surface auto-detected credentials so the user knows their next 2-min step,
|
|
74
95
|
// without ever printing a raw secret.
|
|
75
96
|
const detection = await detectLocalCredentials({ cwd: resolve(args.path) });
|
|
@@ -102,7 +123,8 @@ async function quickstartCommand(args) {
|
|
|
102
123
|
color,
|
|
103
124
|
mode,
|
|
104
125
|
nextSteps,
|
|
105
|
-
deadContext
|
|
126
|
+
deadContext,
|
|
127
|
+
detectedPlans
|
|
106
128
|
});
|
|
107
129
|
const header = [` ${dataModeBanner(mode)}`, ...warnings.map((warning) => ` ! ${warning}`)].join("\n");
|
|
108
130
|
return ok(`${header}\n${summaryText}`);
|
|
@@ -190,6 +212,13 @@ async function doctorCommand(args) {
|
|
|
190
212
|
const hasLogs = claudeFound || codexFound;
|
|
191
213
|
const detection = await detectLocalCredentials({ cwd: rootPath }).catch(() => ({ credentials: [] }));
|
|
192
214
|
const providerRefs = detection.credentials.map((credential) => `${credential.provider} (${credential.hint})`);
|
|
215
|
+
const plans = await detectLocalPlans({
|
|
216
|
+
claudeConfigPath: process.env.AI_SPEND_CLAUDE_CONFIG,
|
|
217
|
+
codexAuthPath: process.env.AI_SPEND_CODEX_AUTH
|
|
218
|
+
}).catch(() => []);
|
|
219
|
+
const planLine = plans.length > 0
|
|
220
|
+
? plans.map((plan) => `${plan.planLabel} (${plan.agent}, ${plan.billing === "api_key" ? "pay per token" : plan.billing})`).join(", ")
|
|
221
|
+
: "none detected (use --plan to declare one)";
|
|
193
222
|
const warnings = [];
|
|
194
223
|
if (stateMode === "sample")
|
|
195
224
|
warnings.push("sample state present — it will be shown as DEMO and cannot mask real logs; run `ai-spend-agent reset` to clear it");
|
|
@@ -215,6 +244,7 @@ async function doctorCommand(args) {
|
|
|
215
244
|
`Claude Code logs: ${claudeFound ? "found" : "not found"}`,
|
|
216
245
|
`Codex logs: ${codexFound ? "found" : "not found"}`,
|
|
217
246
|
`provider env references: ${providerRefs.length > 0 ? providerRefs.join(", ") : "none detected"}`,
|
|
247
|
+
`subscription plans: ${planLine}`,
|
|
218
248
|
"redaction policy: secrets are never printed or persisted",
|
|
219
249
|
"plan check: available (subscription vs API-rate math)",
|
|
220
250
|
`data mode you'll get now: ${predictedMode}`,
|
|
@@ -663,6 +693,20 @@ async function connectCommand(args) {
|
|
|
663
693
|
lines.push(`missing: ${source.fieldsMissing.join(", ")}`);
|
|
664
694
|
return ok(lines.join("\n"));
|
|
665
695
|
}
|
|
696
|
+
/** Map a --plan id to a synthetic DetectedPlan (explicit user override). */
|
|
697
|
+
function planOverrideFromFlag(planId) {
|
|
698
|
+
const plan = subscriptionPlans.find((candidate) => candidate.id === planId);
|
|
699
|
+
if (!plan)
|
|
700
|
+
return undefined;
|
|
701
|
+
return {
|
|
702
|
+
agent: plan.agent,
|
|
703
|
+
provider: plan.provider,
|
|
704
|
+
planId: plan.id,
|
|
705
|
+
planLabel: plan.name,
|
|
706
|
+
billing: "subscription",
|
|
707
|
+
source: "--plan override"
|
|
708
|
+
};
|
|
709
|
+
}
|
|
666
710
|
function describeOrigin(credential) {
|
|
667
711
|
if (credential.origin === "process_env")
|
|
668
712
|
return "your shell environment";
|
|
@@ -927,6 +971,14 @@ function parseArgs(argv) {
|
|
|
927
971
|
parsed.ignoreState = true;
|
|
928
972
|
continue;
|
|
929
973
|
}
|
|
974
|
+
if (arg === "--plan") {
|
|
975
|
+
const next = rest[index + 1];
|
|
976
|
+
if (next) {
|
|
977
|
+
parsed.plan = next;
|
|
978
|
+
index += 1;
|
|
979
|
+
}
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
930
982
|
if (arg === "--group-by") {
|
|
931
983
|
const next = rest[index + 1];
|
|
932
984
|
if (isGroupByDimension(next)) {
|
|
@@ -1203,6 +1255,7 @@ function helpText() {
|
|
|
1203
1255
|
"Run with no command for an instant, zero-key demo:",
|
|
1204
1256
|
" ai-spend-agent Show where your AI money goes (sample/auto-detected data)",
|
|
1205
1257
|
" ai-spend-agent --group-by agent Drill down by source|model|client|project|agent|user|workspace|apiKey",
|
|
1258
|
+
" ai-spend-agent --plan <id> Declare your plan when auto-detection can't (claude-max-5x|claude-max-20x|claude-pro|chatgpt-plus|chatgpt-pro)",
|
|
1206
1259
|
"",
|
|
1207
1260
|
"Connect your real spend (cost data is ADMIN/owner-gated):",
|
|
1208
1261
|
" ai-spend-agent connect openai Self-serve in ~2 min with an org-owner Admin key",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-spend-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Your AI spend in one view, in 90 seconds — local-first CLI for OpenAI, Anthropic, Cursor, Copilot + Claude Code/Codex session logs, with a ranked savings cut list, and flags the tools your agent loads but never uses (dead context).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"prepack": "npm run build"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@agent-finops/core": "0.
|
|
53
|
-
"@agent-finops/report": "0.
|
|
52
|
+
"@agent-finops/core": "0.2.0",
|
|
53
|
+
"@agent-finops/report": "0.2.0",
|
|
54
54
|
"yocto-spinner": "^1.2.0"
|
|
55
55
|
}
|
|
56
56
|
}
|