tokensaver-cli 0.3.2__tar.gz → 0.3.4__tar.gz

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.
Files changed (62) hide show
  1. {tokensaver_cli-0.3.2/src/tokensaver_cli.egg-info → tokensaver_cli-0.3.4}/PKG-INFO +6 -1
  2. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/README.md +5 -0
  3. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/__init__.py +1 -1
  4. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/agents.py +31 -2
  5. tokensaver_cli-0.3.4/src/tokensaver_cli/catalog_cmd.py +113 -0
  6. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/claude_plugin/.claude-plugin/plugin.json +3 -3
  7. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/cache.md +19 -0
  8. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/catalog.md +26 -0
  9. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/doctor.md +11 -0
  10. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/flows.md +19 -0
  11. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/help.md +27 -0
  12. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/keys.md +19 -0
  13. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/login.md +26 -0
  14. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/model.md +22 -0
  15. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/models.md +24 -0
  16. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/quota.md +17 -0
  17. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/use.md +28 -0
  18. tokensaver_cli-0.3.4/src/tokensaver_cli/claude_plugin/commands/whoami.md +14 -0
  19. tokensaver_cli-0.3.4/src/tokensaver_cli/flows_cmd.py +90 -0
  20. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/main.py +57 -0
  21. tokensaver_cli-0.3.4/src/tokensaver_cli/models_cmd.py +81 -0
  22. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/profiles.py +9 -1
  23. tokensaver_cli-0.3.4/src/tokensaver_cli/use_cmd.py +74 -0
  24. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4/src/tokensaver_cli.egg-info}/PKG-INFO +6 -1
  25. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli.egg-info/SOURCES.txt +16 -0
  26. tokensaver_cli-0.3.4/tests/test_models_use_cli.py +105 -0
  27. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/tests/test_route_cli.py +34 -0
  28. tokensaver_cli-0.3.2/src/tokensaver_cli/claude_plugin/commands/model.md +0 -29
  29. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/LICENSE +0 -0
  30. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/pyproject.toml +0 -0
  31. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/setup.cfg +0 -0
  32. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/api_client.py +0 -0
  33. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/approve_cmd.py +0 -0
  34. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/auth_cmd.py +0 -0
  35. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/banner.py +0 -0
  36. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/claude_plugin/.claude-plugin/marketplace.json +0 -0
  37. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/claude_plugin/.mcp.json +0 -0
  38. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/claude_plugin/commands/approve.md +0 -0
  39. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/claude_plugin/commands/pipeline.md +0 -0
  40. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/claude_plugin/commands/setup.md +0 -0
  41. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/claude_plugin/commands/status.md +0 -0
  42. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/claude_plugin/commands/unroute.md +0 -0
  43. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/claude_plugin/hooks/hooks.json +0 -0
  44. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/config.py +0 -0
  45. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/credentials.py +0 -0
  46. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/doctor.py +0 -0
  47. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/json_merge.py +0 -0
  48. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/keys_cmd.py +0 -0
  49. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/logo.py +0 -0
  50. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/plugin_install.py +0 -0
  51. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/route_frame.py +0 -0
  52. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/state.py +0 -0
  53. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli/status.py +0 -0
  54. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli.egg-info/dependency_links.txt +0 -0
  55. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli.egg-info/entry_points.txt +0 -0
  56. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli.egg-info/requires.txt +0 -0
  57. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/src/tokensaver_cli.egg-info/top_level.txt +0 -0
  58. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/tests/test_approve_cli.py +0 -0
  59. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/tests/test_auth_cli.py +0 -0
  60. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/tests/test_banner.py +0 -0
  61. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/tests/test_deploy_mode.py +0 -0
  62. {tokensaver_cli-0.3.2 → tokensaver_cli-0.3.4}/tests/test_main_args.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tokensaver-cli
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: TokenSaver CLI — route AI agents (Claude Code, Cursor, Codex) through the control plane
5
5
  Author: TokenSaver
6
6
  License-Expression: MIT
@@ -69,6 +69,10 @@ pip install -e ".[dev]"
69
69
  | `tokensaver logout` | Remove local credentials |
70
70
  | `tokensaver whoami` | Email, plan, key prefix, Flux IA link |
71
71
  | `tokensaver approve [model]` | Approve model in Agent Registry (zero-trust) |
72
+ | `tokensaver models` | List profiles + catalogue models |
73
+ | `tokensaver use <profile\|model>` | Set Claude model (sticky) + approve |
74
+ | `tokensaver flows [--open]` | Flux IA URL + recent runs |
75
+ | `tokensaver catalog …` | List / approve Agent Registry |
72
76
  | `tokensaver keys list` | List API keys |
73
77
  | `tokensaver keys create --name CLI --use` | Create key and set as default |
74
78
  | `tokensaver keys revoke <id>` | Delete a key |
@@ -84,6 +88,7 @@ pip install -e ".[dev]"
84
88
  | `tokensaver doctor [--claude] [--local]` | Health checks |
85
89
  | `tokensaver status [--line\|--hook]` | Route status + Flux IA |
86
90
  | `tokensaver profiles` | cheap / default / strong (plan-aware) |
91
+ | Claude plugin slash | `/tokensaver-router:models\|use\|flows\|quota\|cache\|catalog\|…` |
87
92
 
88
93
  ### Claude Code options
89
94
 
@@ -42,6 +42,10 @@ pip install -e ".[dev]"
42
42
  | `tokensaver logout` | Remove local credentials |
43
43
  | `tokensaver whoami` | Email, plan, key prefix, Flux IA link |
44
44
  | `tokensaver approve [model]` | Approve model in Agent Registry (zero-trust) |
45
+ | `tokensaver models` | List profiles + catalogue models |
46
+ | `tokensaver use <profile\|model>` | Set Claude model (sticky) + approve |
47
+ | `tokensaver flows [--open]` | Flux IA URL + recent runs |
48
+ | `tokensaver catalog …` | List / approve Agent Registry |
45
49
  | `tokensaver keys list` | List API keys |
46
50
  | `tokensaver keys create --name CLI --use` | Create key and set as default |
47
51
  | `tokensaver keys revoke <id>` | Delete a key |
@@ -57,6 +61,7 @@ pip install -e ".[dev]"
57
61
  | `tokensaver doctor [--claude] [--local]` | Health checks |
58
62
  | `tokensaver status [--line\|--hook]` | Route status + Flux IA |
59
63
  | `tokensaver profiles` | cheap / default / strong (plan-aware) |
64
+ | Claude plugin slash | `/tokensaver-router:models\|use\|flows\|quota\|cache\|catalog\|…` |
60
65
 
61
66
  ### Claude Code options
62
67
 
@@ -5,4 +5,4 @@ Published on PyPI as ``tokensaver-cli``; installs the ``tokensaver`` console com
5
5
  No server-side pipeline code — configures local agents to call api.tokensaver.fr.
6
6
  """
7
7
 
8
- __version__ = "0.3.2"
8
+ __version__ = "0.3.4"
@@ -255,6 +255,18 @@ def _restore_routed_files(files: list[RoutedFile], *, strip_if_no_backup: bool =
255
255
  path.unlink(missing_ok=True)
256
256
 
257
257
 
258
+ def _sticky_claude_model() -> str | None:
259
+ """Last model chosen via ``--model`` / ``--profile`` (survives bare ``route --launch``)."""
260
+ rec = load_state().get("claude")
261
+ if not rec or not isinstance(rec.meta, dict):
262
+ return None
263
+ for key in ("sticky_model", "model"):
264
+ value = rec.meta.get(key)
265
+ if isinstance(value, str) and value.strip():
266
+ return value.strip()
267
+ return None
268
+
269
+
258
270
  def route_claude(
259
271
  cfg: RouteConfig,
260
272
  *,
@@ -267,12 +279,25 @@ def route_claude(
267
279
  install_plugin: bool = True,
268
280
  ) -> int:
269
281
  plan_slug = resolve_plan_slug()
282
+ explicit_model = bool(model and model.strip())
283
+ explicit_profile = bool(profile and profile.strip())
284
+ sticky = None if (explicit_model or explicit_profile) else _sticky_claude_model()
270
285
  try:
271
- model_id, profile_name = resolve_model(model=model, profile=profile, plan_slug=plan_slug)
286
+ model_id, profile_name = resolve_model(
287
+ model=model,
288
+ profile=profile,
289
+ plan_slug=plan_slug,
290
+ sticky_model=sticky,
291
+ )
272
292
  except ValueError as exc:
273
293
  print(str(exc), file=sys.stderr)
274
294
  return 2
275
- if is_free_plan(plan_slug) and not (model and model.strip()):
295
+ if sticky and model_id == sticky and not explicit_model and not explicit_profile:
296
+ print(
297
+ f"Keeping previous model {model_id} (set with --model / --profile to change)",
298
+ file=sys.stderr,
299
+ )
300
+ elif is_free_plan(plan_slug) and not explicit_model and not sticky:
276
301
  print(
277
302
  f"Free plan → default model {model_id} (override with --model / --profile)",
278
303
  file=sys.stderr,
@@ -357,15 +382,19 @@ def route_claude(
357
382
  file=sys.stderr,
358
383
  )
359
384
 
385
+ prev_sticky = _sticky_claude_model()
386
+ sticky_model = model_id if (explicit_model or explicit_profile) else (prev_sticky or model_id)
360
387
  meta = {
361
388
  "scope": scope,
362
389
  "model": model_id,
390
+ "sticky_model": sticky_model,
363
391
  "profile": profile_name,
364
392
  "with_fs": fs_ok,
365
393
  "console_url": cfg.console_url,
366
394
  "flows_url": cfg.flows_url(),
367
395
  "plugin_dir": str(plugin_dir) if plugin_dir else None,
368
396
  "deploy_mode": cfg.deploy_mode,
397
+ "plan_slug": plan_slug,
369
398
  }
370
399
  record_route("claude", routed, meta=meta)
371
400
 
@@ -0,0 +1,113 @@
1
+ """Agent Registry helpers from the CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from typing import Any
7
+ from urllib.parse import quote
8
+
9
+ from tokensaver_cli.api_client import ApiError, request_json
10
+ from tokensaver_cli.approve_cmd import _api_base, _auth_headers, approve_catalog_ref
11
+ from tokensaver_cli.credentials import resolve_access_token
12
+
13
+
14
+ def _list_assets(
15
+ api_v1: str,
16
+ *,
17
+ status: str | None = None,
18
+ asset_type: str = "model",
19
+ q: str | None = None,
20
+ limit: int = 50,
21
+ ) -> list[dict[str, Any]]:
22
+ params = [f"type={quote(asset_type)}", f"limit={limit}"]
23
+ if status:
24
+ params.append(f"status={quote(status)}")
25
+ if q:
26
+ params.append(f"q={quote(q)}")
27
+ qs = "&".join(params)
28
+ headers = _auth_headers()
29
+ try:
30
+ data = request_json("GET", f"{api_v1}/sdk/catalog/assets?{qs}", headers=headers)
31
+ except ApiError:
32
+ token = resolve_access_token()
33
+ if not token:
34
+ raise
35
+ data = request_json(
36
+ "GET",
37
+ f"{api_v1}/catalog/assets?{qs}",
38
+ headers={"Authorization": f"Bearer {token}"},
39
+ )
40
+ items = data.get("items") if isinstance(data, dict) else None
41
+ if not isinstance(items, list):
42
+ return []
43
+ return [i for i in items if isinstance(i, dict)]
44
+
45
+
46
+ def run_catalog(
47
+ action: str | None = None,
48
+ ref: str | None = None,
49
+ *,
50
+ force_local: bool = False,
51
+ status: str | None = None,
52
+ ) -> int:
53
+ act = (action or "list").strip().lower()
54
+ api_v1 = _api_base(force_local=force_local)
55
+
56
+ if act in ("approve", "allow"):
57
+ target = (ref or "").strip()
58
+ if not target:
59
+ print("Usage: tokensaver catalog approve <provider/model>", file=sys.stderr)
60
+ return 2
61
+ try:
62
+ asset = approve_catalog_ref(target, force_local=force_local)
63
+ except ApiError as exc:
64
+ print(f"FAIL {exc}", file=sys.stderr)
65
+ return 1
66
+ print(f"OK approved {asset.get('ref')} status={asset.get('status')}")
67
+ return 0
68
+
69
+ if act not in ("list", "pending", "ls"):
70
+ print("Usage: tokensaver catalog [list|pending|approve <ref>]", file=sys.stderr)
71
+ return 2
72
+
73
+ filter_status = status
74
+ if act == "pending" and not filter_status:
75
+ # show both pending buckets
76
+ rows: list[dict[str, Any]] = []
77
+ for st in ("quarantined", "discovered"):
78
+ try:
79
+ rows.extend(_list_assets(api_v1, status=st, asset_type="model"))
80
+ except ApiError as exc:
81
+ print(f"FAIL list catalog — {exc}", file=sys.stderr)
82
+ return 1
83
+ # de-dupe by id
84
+ seen: set[str] = set()
85
+ uniq: list[dict[str, Any]] = []
86
+ for row in rows:
87
+ aid = str(row.get("id") or "")
88
+ if aid and aid in seen:
89
+ continue
90
+ if aid:
91
+ seen.add(aid)
92
+ uniq.append(row)
93
+ rows = uniq
94
+ else:
95
+ try:
96
+ rows = _list_assets(api_v1, status=filter_status, asset_type="model")
97
+ except ApiError as exc:
98
+ print(f"FAIL list catalog — {exc}", file=sys.stderr)
99
+ return 1
100
+
101
+ print("Agent Registry (models)")
102
+ if not rows:
103
+ print(" (empty)")
104
+ return 0
105
+ print(f" {'STATUS':12} REF")
106
+ for row in rows:
107
+ st = str(row.get("status") or "?")[:12]
108
+ ref_s = row.get("ref") or "?"
109
+ print(f" {st:12} {ref_s}")
110
+ print()
111
+ print("Approve: tokensaver catalog approve <ref>")
112
+ print(" or: tokensaver approve <ref>")
113
+ return 0
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "tokensaver-router",
3
3
  "displayName": "TokenSaver Router",
4
- "version": "0.3.2",
5
- "description": "Route Claude Code through TokenSaver — login, model profiles, Agent Registry approve, MCP Trust Gateway, Flux IA traces.",
4
+ "version": "0.3.4",
5
+ "description": "Route Claude Code through TokenSaver — models, use, approve, Flux IA, quotas, cache, catalog, login/keys.",
6
6
  "author": {
7
7
  "name": "TokenSaver",
8
8
  "url": "https://tokensaver.fr"
@@ -10,7 +10,7 @@
10
10
  "homepage": "https://tokensaver.fr/sdk-api",
11
11
  "repository": "https://github.com/tokensaver/tokensaver-platform",
12
12
  "license": "MIT",
13
- "keywords": ["tokensaver", "router", "mcp", "governance", "cost"],
13
+ "keywords": ["tokensaver", "router", "mcp", "governance", "cost", "models"],
14
14
  "commands": ["./commands"],
15
15
  "hooks": "./hooks/hooks.json",
16
16
  "mcpServers": "./.mcp.json"
@@ -0,0 +1,19 @@
1
+ ---
2
+ description: Show TokenSaver cache stats (and optional purge guidance)
3
+ argument-hint: "[stats|purge]"
4
+ ---
5
+
6
+ Use MCP tools:
7
+
8
+ 1. Default / `stats`: call `tokensaver_cache_stats` and paste the result.
9
+ 2. Optional detail: `tokensaver_list_cache_entries` (small limit).
10
+ 3. Only if the user explicitly asks to purge: confirm, then `tokensaver_cache_purge`.
11
+
12
+ Never purge without clear user consent.
13
+
14
+ CLI fallback:
15
+
16
+ ```bash
17
+ tokensaver status
18
+ tokensaver flows
19
+ ```
@@ -0,0 +1,26 @@
1
+ ---
2
+ description: List or approve Agent Registry (catalog) models — fix zero-trust 403
3
+ argument-hint: "[pending|approve <ref>]"
4
+ ---
5
+
6
+ Agent Registry governs which models may egress (zero-trust).
7
+
8
+ If `$ARGUMENTS` starts with `approve`, run:
9
+
10
+ ```bash
11
+ tokensaver catalog approve <ref>
12
+ # or: tokensaver approve <ref>
13
+ ```
14
+
15
+ If `$ARGUMENTS` is `pending` or empty, list pending models:
16
+
17
+ ```bash
18
+ tokensaver catalog pending
19
+ tokensaver catalog list
20
+ ```
21
+
22
+ MCP alternatives:
23
+ - `tokensaver_list_catalog_assets` with `status=quarantined` or `type=model`
24
+ - `tokensaver_approve_catalog_asset` with `ref=…` (creates+approves if missing)
25
+
26
+ After approve, tell the user to retry their message. Related: `/tokensaver-router:use` and `/tokensaver-router:approve`.
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Run TokenSaver doctor checks for Claude routing
3
+ ---
4
+
5
+ ```bash
6
+ tokensaver doctor --claude
7
+ # local stack:
8
+ tokensaver doctor --claude --local
9
+ ```
10
+
11
+ Report which checks failed (API key, Anthropic base URL, /v1/models, MCP). Suggest `tokensaver login`, `tokensaver route claude --local`, or `tokensaver approve --current` as appropriate.
@@ -0,0 +1,19 @@
1
+ ---
2
+ description: Open Flux IA — recent governed runs / pipeline traces
3
+ argument-hint: "[--open]"
4
+ ---
5
+
6
+ Show TokenSaver Flux IA links and recent pipeline runs:
7
+
8
+ ```bash
9
+ tokensaver flows
10
+ # open browser:
11
+ tokensaver flows --open
12
+ ```
13
+
14
+ If MCP tools are available, also call:
15
+ - `tokensaver_list_runs` (limit 5)
16
+ - `tokensaver_get_last_run_detail` when the user wants step-by-step
17
+ - `tokensaver_open_run_console` to deep-link a run
18
+
19
+ Explain that each Claude message through TokenSaver can appear as a governed flow (cache, compression, PII, LLM, MCP). Do not invent token savings — only report what the tools/console show.
@@ -0,0 +1,27 @@
1
+ ---
2
+ description: List TokenSaver Router slash commands and CLI equivalents
3
+ ---
4
+
5
+ Print this catalogue for the user:
6
+
7
+ | Slash | Purpose | CLI |
8
+ |-------|---------|-----|
9
+ | `/tokensaver-router:models` | List profiles + catalogue | `tokensaver models` |
10
+ | `/tokensaver-router:use` | Set model (sticky) + approve | `tokensaver use …` |
11
+ | `/tokensaver-router:model` | Alias of use | `tokensaver use …` |
12
+ | `/tokensaver-router:approve` | Agent Registry approve | `tokensaver approve …` |
13
+ | `/tokensaver-router:catalog` | List/approve registry | `tokensaver catalog …` |
14
+ | `/tokensaver-router:status` | Route statusline | `tokensaver status` |
15
+ | `/tokensaver-router:whoami` | Identity + plan | `tokensaver whoami` |
16
+ | `/tokensaver-router:flows` | Flux IA / recent runs | `tokensaver flows` |
17
+ | `/tokensaver-router:pipeline` | Flux IA pipeline hint | `tokensaver status` |
18
+ | `/tokensaver-router:quota` | Quotas / key overview | MCP `tokensaver_get_api_key_overview` |
19
+ | `/tokensaver-router:cache` | Cache stats | MCP `tokensaver_cache_stats` |
20
+ | `/tokensaver-router:login` | Login / signup | `tokensaver login` |
21
+ | `/tokensaver-router:keys` | API keys | `tokensaver keys …` |
22
+ | `/tokensaver-router:setup` | Full route setup | `tokensaver route claude` |
23
+ | `/tokensaver-router:doctor` | Health checks | `tokensaver doctor --claude` |
24
+ | `/tokensaver-router:unroute` | Restore Claude settings | `tokensaver unroute claude` |
25
+ | `/tokensaver-router:help` | This list | — |
26
+
27
+ Tip: if the current model is blocked (credits / 403), run the **CLI** commands in a terminal — slash prompts need a working model.
@@ -0,0 +1,19 @@
1
+ ---
2
+ description: List / create / revoke TokenSaver API keys
3
+ argument-hint: "[list|create|revoke <id>]"
4
+ ---
5
+
6
+ Requires a login JWT (`tokensaver login`), not key-only import.
7
+
8
+ ```bash
9
+ tokensaver keys list
10
+ tokensaver keys create --name Claude --use
11
+ tokensaver keys revoke <key-id>
12
+ ```
13
+
14
+ If `$ARGUMENTS` is `create`, run create with `--use`.
15
+ If it starts with `revoke`, revoke that id.
16
+ Otherwise list keys.
17
+
18
+ Warn: the plaintext key is shown once at create time — store it securely.
19
+ Related: `/tokensaver-router:whoami`, `/tokensaver-router:login`.
@@ -0,0 +1,26 @@
1
+ ---
2
+ description: Log in / signup to TokenSaver (saves credentials + API key)
3
+ argument-hint: "[--local|--key ts_…]"
4
+ ---
5
+
6
+ CLI-first auth (no dashboard required):
7
+
8
+ ```bash
9
+ tokensaver login
10
+ # local API:
11
+ tokensaver login --local
12
+ # import existing key:
13
+ tokensaver login --key ts_…
14
+ # non-interactive signup example:
15
+ tokensaver login --email you@acme.com --signup -y
16
+ ```
17
+
18
+ Then route Claude:
19
+
20
+ ```bash
21
+ tokensaver route claude --launch
22
+ # or local:
23
+ tokensaver route claude --local --launch
24
+ ```
25
+
26
+ Verify with `tokensaver whoami` and `tokensaver doctor --claude`.
@@ -0,0 +1,22 @@
1
+ ---
2
+ description: Switch TokenSaver routing model or profile (alias of use)
3
+ argument-hint: "<cheap|default|strong|provider/model>"
4
+ ---
5
+
6
+ Alias of `/tokensaver-router:use`. Prefer that command.
7
+
8
+ If `$ARGUMENTS` is set, run:
9
+
10
+ ```bash
11
+ tokensaver use $ARGUMENTS
12
+ tokensaver status --line
13
+ ```
14
+
15
+ If empty, list options first:
16
+
17
+ ```bash
18
+ tokensaver models
19
+ ```
20
+
21
+ Then ask which profile or catalogue id to apply via `tokensaver use …`.
22
+ After switch, approve is handled by `use`; fallback: `tokensaver approve --current`.
@@ -0,0 +1,24 @@
1
+ ---
2
+ description: List TokenSaver profiles and catalogue models available on your plan
3
+ ---
4
+
5
+ Run (shell — works even if the chat model is blocked):
6
+
7
+ ```bash
8
+ tokensaver models
9
+ ```
10
+
11
+ Also show sticky Claude model via:
12
+
13
+ ```bash
14
+ tokensaver status --line
15
+ ```
16
+
17
+ Optional MCP (richer JSON): call `tokensaver_list_models` and paste the ids.
18
+
19
+ Summarize for the user:
20
+ 1. Profiles (`cheap` / `default` / `strong`) for their plan
21
+ 2. Catalogue ids they can pass to `/tokensaver-router:use`
22
+ 3. Current sticky model if any
23
+
24
+ Next step hint: `/tokensaver-router:use <profile|provider/model>`
@@ -0,0 +1,17 @@
1
+ ---
2
+ description: Show TokenSaver API key quotas and usage
3
+ ---
4
+
5
+ Prefer MCP tools (paste ASCII dashboards as-is, do not rewrite into prose):
6
+
7
+ 1. `tokensaver_get_api_key_overview` — full key dashboard (primary)
8
+ 2. Or `tokensaver_get_quota_usage` if the user only asks for quotas
9
+
10
+ If MCP is unavailable, fall back to:
11
+
12
+ ```bash
13
+ tokensaver whoami
14
+ tokensaver doctor
15
+ ```
16
+
17
+ and the Flux IA / console usage pages from `tokensaver status`.
@@ -0,0 +1,28 @@
1
+ ---
2
+ description: Set Claude routing model or profile (sticky) and approve in Agent Registry
3
+ argument-hint: "<cheap|default|strong|provider/model>"
4
+ ---
5
+
6
+ Select a TokenSaver model for Claude Code. Prefer the CLI (updates sticky model + Agent Registry).
7
+
8
+ If `$ARGUMENTS` is empty, run `tokensaver models` and ask the user which id/profile to use.
9
+
10
+ Otherwise run:
11
+
12
+ ```bash
13
+ # Prefer --local when status shows local
14
+ tokensaver use $ARGUMENTS
15
+ # equivalent:
16
+ # tokensaver use cheap
17
+ # tokensaver use openrouter/z-ai/glm-4.7-flash
18
+ ```
19
+
20
+ Then verify:
21
+
22
+ ```bash
23
+ tokensaver status --line
24
+ ```
25
+
26
+ Tell the user to send a **new message** (or restart Claude) so the model takes effect. Statusline must show the chosen model.
27
+
28
+ If approve fails (403), run `/tokensaver-router:approve $ARGUMENTS`.
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: Show TokenSaver identity, plan, API key, and Flux IA link
3
+ ---
4
+
5
+ Run:
6
+
7
+ ```bash
8
+ tokensaver whoami
9
+ # local stack:
10
+ tokensaver whoami --local
11
+ ```
12
+
13
+ Report email, plan (Free / Pro / Enterprise), API host, key prefix, and Flux IA URL.
14
+ Explain that Free plan profiles default to allowlisted models (e.g. GLM 4.7 Flash) while Enterprise defaults to Sonnet/Opus unless a sticky `--model` was set.
@@ -0,0 +1,90 @@
1
+ """Flux IA deep links and recent pipeline runs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ import webbrowser
7
+ from typing import Any
8
+
9
+ from tokensaver_cli.api_client import ApiError, request_json
10
+ from tokensaver_cli.config import RouteConfigError, resolve_route_config
11
+ from tokensaver_cli.status import fetch_recent_flow_id
12
+
13
+
14
+ def _list_recent_runs(cfg, *, limit: int = 5) -> list[dict[str, Any]]:
15
+ url = f"{cfg.api_v1_base}/monitoring/pipelines?days=7"
16
+ try:
17
+ data = request_json(
18
+ "GET",
19
+ url,
20
+ headers={"Authorization": f"Bearer {cfg.api_key}"},
21
+ )
22
+ except ApiError:
23
+ return []
24
+ if not isinstance(data, dict):
25
+ return []
26
+ out: list[dict[str, Any]] = []
27
+ for key in ("recent_runs", "recent_run_ids"):
28
+ items = data.get(key)
29
+ if not isinstance(items, list):
30
+ continue
31
+ for item in items:
32
+ if isinstance(item, str) and item.strip():
33
+ out.append({"id": item.strip()})
34
+ elif isinstance(item, dict):
35
+ out.append(item)
36
+ if len(out) >= limit:
37
+ return out
38
+ # Fallback: dig pipelines buckets
39
+ pipelines = data.get("pipelines") or data.get("by_pipeline") or []
40
+ if isinstance(pipelines, dict):
41
+ pipelines = list(pipelines.values())
42
+ if isinstance(pipelines, list):
43
+ for entry in pipelines:
44
+ if not isinstance(entry, dict):
45
+ continue
46
+ for rid in entry.get("recent_run_ids") or []:
47
+ if isinstance(rid, str) and rid.strip():
48
+ out.append({"id": rid.strip(), "pipeline": entry.get("name") or entry.get("id")})
49
+ if len(out) >= limit:
50
+ return out
51
+ return out[:limit]
52
+
53
+
54
+ def run_flows(
55
+ *,
56
+ force_local: bool = False,
57
+ open_browser: bool = False,
58
+ limit: int = 5,
59
+ ) -> int:
60
+ try:
61
+ cfg = resolve_route_config(force_local=force_local)
62
+ except RouteConfigError as exc:
63
+ print(str(exc), file=sys.stderr)
64
+ return 1
65
+
66
+ flow_id = fetch_recent_flow_id(cfg)
67
+ url = cfg.flows_url(flow_id=flow_id)
68
+ print("TokenSaver Flux IA")
69
+ print(f" Console: {url}")
70
+ if flow_id:
71
+ print(f" Latest: {flow_id}")
72
+ print()
73
+ runs = _list_recent_runs(cfg, limit=limit)
74
+ if runs:
75
+ print(f"Recent runs (up to {limit}):")
76
+ for row in runs:
77
+ rid = row.get("id") or row.get("run_id") or "?"
78
+ extra = row.get("pipeline") or row.get("status") or ""
79
+ suffix = f" {extra}" if extra else ""
80
+ print(f" {rid}{suffix}")
81
+ print()
82
+ else:
83
+ print("No recent runs found (send a Claude message first).")
84
+ print()
85
+
86
+ print("MCP: tokensaver_list_runs · tokensaver_get_last_run_detail · tokensaver_open_run_console")
87
+ if open_browser:
88
+ webbrowser.open(url)
89
+ print("Opened browser.")
90
+ return 0