claude-finops 0.13.0 → 0.14.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/README.md +77 -3
- package/config/settings.json +9 -0
- package/finops/analytics.py +78 -2
- package/finops/api.py +130 -4
- package/finops/cloud.py +51 -3
- package/finops/guard.py +412 -0
- package/finops/integrate.py +78 -0
- package/finops/jev.py +175 -0
- package/package.json +1 -1
- package/run.py +16 -12
- package/web/app.js +830 -59
- package/web/guide/budgets.png +0 -0
- package/web/styles.css +71 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-finops",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Local FinOps dashboard for Claude Code: what you used, what it cost, why it cost that much, and what to change. Reads your own transcripts, no API key, no data leaves the machine.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"claude-finops": "bin/claude-finops.js"
|
package/run.py
CHANGED
|
@@ -154,6 +154,8 @@ HELP = """Claude FinOps Command Center
|
|
|
154
154
|
claude-finops --no-update-check skip the once-a-day npm version check
|
|
155
155
|
claude-finops --install-hook retired no-op (prints a message, changes nothing)
|
|
156
156
|
claude-finops --install-statusline show model, context % and 5-hour limit in your statusline
|
|
157
|
+
claude-finops --install-guard warn near, and ask at, your per-session token budget
|
|
158
|
+
claude-finops --uninstall-guard remove the session guard hook
|
|
157
159
|
claude-finops --help this message
|
|
158
160
|
|
|
159
161
|
Environment:
|
|
@@ -241,7 +243,6 @@ def keys():
|
|
|
241
243
|
def set_key(pid=None):
|
|
242
244
|
"""Prompt for a key and store it 0600. The value is never echoed or logged."""
|
|
243
245
|
import getpass
|
|
244
|
-
import json
|
|
245
246
|
from finops.paths import SECRETS_PATH
|
|
246
247
|
provs = _providers()
|
|
247
248
|
if pid not in provs:
|
|
@@ -256,23 +257,21 @@ def set_key(pid=None):
|
|
|
256
257
|
p = provs[pid]
|
|
257
258
|
print(f"{p['name']}: paste the key (input stays hidden), or press Enter to remove it.")
|
|
258
259
|
value = getpass.getpass("key: ").strip()
|
|
260
|
+
from finops.cloud import save_key
|
|
259
261
|
try:
|
|
260
|
-
|
|
261
|
-
except
|
|
262
|
-
|
|
263
|
-
if value:
|
|
264
|
-
data[p["field"]] = value
|
|
265
|
-
else:
|
|
266
|
-
data.pop(p["field"], None)
|
|
267
|
-
fd = os.open(SECRETS_PATH, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
268
|
-
with os.fdopen(fd, "w") as fh:
|
|
269
|
-
json.dump(data, fh, indent=2)
|
|
262
|
+
save_key(pid, value)
|
|
263
|
+
except ValueError as e:
|
|
264
|
+
sys.exit(str(e))
|
|
270
265
|
print(f"{'Stored' if value else 'Removed'} — {SECRETS_PATH}")
|
|
271
|
-
print("
|
|
266
|
+
print("The dashboard picks it up on the next Refresh; no restart needed.")
|
|
272
267
|
|
|
273
268
|
|
|
274
269
|
def main():
|
|
275
270
|
args = sys.argv[1:]
|
|
271
|
+
# Runs before every tool call in Claude Code: skip setup, print only its JSON answer.
|
|
272
|
+
if "--guard" in args:
|
|
273
|
+
from finops.guard import main as guard
|
|
274
|
+
return sys.exit(guard())
|
|
276
275
|
os.chdir(ROOT)
|
|
277
276
|
ensure_dirs()
|
|
278
277
|
migrate()
|
|
@@ -294,6 +293,11 @@ def main():
|
|
|
294
293
|
if "--install-hook" in args or "--uninstall-hook" in args:
|
|
295
294
|
from finops.integrate import install_hook
|
|
296
295
|
return install_hook(remove="--uninstall-hook" in args)
|
|
296
|
+
if "--install-guard" in args or "--uninstall-guard" in args:
|
|
297
|
+
from finops.integrate import install_guard
|
|
298
|
+
r = install_guard(remove="--uninstall-guard" in args)
|
|
299
|
+
print(r["message"])
|
|
300
|
+
return sys.exit(0 if r["ok"] else 1)
|
|
297
301
|
if "--install-statusline" in args or "--uninstall-statusline" in args:
|
|
298
302
|
from finops.integrate import install_statusline
|
|
299
303
|
r = install_statusline(remove="--uninstall-statusline" in args)
|