kimi-code-usage 0.1.0__py3-none-any.whl
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.
|
File without changes
|
kimi_code_usage/main.py
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import asyncio
|
|
3
|
+
import argparse
|
|
4
|
+
import aiohttp
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from datetime import datetime, timedelta
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
from typing import Any, Mapping, Sequence, cast, Tuple, List
|
|
13
|
+
|
|
14
|
+
# --- i18n ---
|
|
15
|
+
LANG = os.getenv("LANG", "en")
|
|
16
|
+
IS_ZH = "zh" in LANG.lower()
|
|
17
|
+
|
|
18
|
+
L_EN = {
|
|
19
|
+
"title": "Kimi Code Usage",
|
|
20
|
+
"weekly_limit": "Weekly Usage",
|
|
21
|
+
"limit_fallback": "Limit",
|
|
22
|
+
"remaining": "remaining",
|
|
23
|
+
"countdown": "Countdown",
|
|
24
|
+
"reset": "Reset",
|
|
25
|
+
"no_data": "No usage data found.",
|
|
26
|
+
"error_key": "KIMI_API_KEY not found in environment or .env file.",
|
|
27
|
+
"error_api": "API Error",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
L_ZH = {
|
|
31
|
+
"title": "Kimi Code 用量监控",
|
|
32
|
+
"weekly_limit": "周用量限额",
|
|
33
|
+
"limit_fallback": "限额",
|
|
34
|
+
"remaining": "剩余",
|
|
35
|
+
"countdown": "重置倒计时",
|
|
36
|
+
"reset": "重置时间",
|
|
37
|
+
"no_data": "未找到用量数据。",
|
|
38
|
+
"error_key": "未在环境或 .env 文件中找到 KIMI_API_KEY。",
|
|
39
|
+
"error_api": "API 错误",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
L = L_ZH if IS_ZH else L_EN
|
|
43
|
+
|
|
44
|
+
class UsageRow:
|
|
45
|
+
def __init__(self, label: str, used: int, limit: int, reset_at: str = None, countdown: str = None):
|
|
46
|
+
self.label = label
|
|
47
|
+
self.used = used
|
|
48
|
+
self.limit = limit
|
|
49
|
+
self.reset_at = reset_at
|
|
50
|
+
self.countdown = countdown
|
|
51
|
+
|
|
52
|
+
def _to_int(v) -> int | None:
|
|
53
|
+
try: return int(v)
|
|
54
|
+
except (TypeError, ValueError): return None
|
|
55
|
+
|
|
56
|
+
def _get_reset_info(data: Mapping[str, Any]):
|
|
57
|
+
reset_at = data.get("resetTime") or data.get("reset_at") or data.get("reset_time")
|
|
58
|
+
if reset_at:
|
|
59
|
+
try:
|
|
60
|
+
if isinstance(reset_at, (int, float)):
|
|
61
|
+
dt = datetime.fromtimestamp(reset_at)
|
|
62
|
+
else:
|
|
63
|
+
dt = datetime.fromisoformat(reset_at.replace("Z", "+00:00")).astimezone()
|
|
64
|
+
|
|
65
|
+
now = datetime.now(dt.tzinfo) if dt.tzinfo else datetime.now()
|
|
66
|
+
diff = dt - now
|
|
67
|
+
if diff.total_seconds() <= 0: return dt.strftime("%m-%d %H:%M"), "0m"
|
|
68
|
+
days = diff.days
|
|
69
|
+
hours, rem = divmod(diff.seconds, 3600)
|
|
70
|
+
minutes, _ = divmod(rem, 60)
|
|
71
|
+
parts = []
|
|
72
|
+
if days > 0: parts.append(f"{days}d")
|
|
73
|
+
if hours > 0: parts.append(f"{hours}h")
|
|
74
|
+
parts.append(f"{minutes}m")
|
|
75
|
+
return dt.strftime("%m-%d %H:%M"), " ".join(parts)
|
|
76
|
+
except Exception: pass
|
|
77
|
+
|
|
78
|
+
reset_in = _to_int(data.get("reset_in"))
|
|
79
|
+
if reset_in is not None:
|
|
80
|
+
dt = datetime.now() + timedelta(seconds=reset_in)
|
|
81
|
+
hours, rem = divmod(reset_in, 3600)
|
|
82
|
+
minutes, _ = divmod(rem, 60)
|
|
83
|
+
return dt.strftime("%m-%d %H:%M"), f"{hours}h {minutes}m"
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
def _limit_label(item, detail, window, idx) -> str:
|
|
87
|
+
duration = _to_int(window.get("duration"))
|
|
88
|
+
time_unit = str(window.get("time_unit") or "").upper()
|
|
89
|
+
if duration and time_unit:
|
|
90
|
+
if "HOUR" in time_unit: return f"{duration}h {L['limit_fallback']}"
|
|
91
|
+
if "DAY" in time_unit: return f"{duration}d {L['limit_fallback']}"
|
|
92
|
+
return f"{L['limit_fallback']} #{idx + 1}"
|
|
93
|
+
|
|
94
|
+
def _to_usage_row(data, *, default_label) -> UsageRow | None:
|
|
95
|
+
limit = _to_int(data.get("limit") or data.get("limit_amount"))
|
|
96
|
+
used = _to_int(data.get("used") or data.get("used_amount"))
|
|
97
|
+
if used is None:
|
|
98
|
+
remaining = _to_int(data.get("remaining"))
|
|
99
|
+
if remaining is not None and limit is not None:
|
|
100
|
+
used = limit - remaining
|
|
101
|
+
if used is None and limit is None: return None
|
|
102
|
+
reset_at, countdown = _get_reset_info(data) or (None, None)
|
|
103
|
+
return UsageRow(
|
|
104
|
+
label=str(data.get("name") or data.get("title") or data.get("model_name") or default_label),
|
|
105
|
+
used=used or 0,
|
|
106
|
+
limit=limit or 0,
|
|
107
|
+
reset_at=reset_at,
|
|
108
|
+
countdown=countdown,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def _parse_usage_payload(payload):
|
|
112
|
+
summary = None
|
|
113
|
+
limits = []
|
|
114
|
+
|
|
115
|
+
# Check if it's the direct list from /usage or the nested dict from /usages
|
|
116
|
+
data_list = payload.get("data")
|
|
117
|
+
if isinstance(data_list, Sequence):
|
|
118
|
+
# Format: [{"model_name": "all", ...}, {"model_name": "...", ...}]
|
|
119
|
+
for item in data_list:
|
|
120
|
+
label = L["weekly_limit"] if item.get("model_name") == "all" else L["limit_fallback"]
|
|
121
|
+
row = _to_usage_row(item, default_label=label)
|
|
122
|
+
if row:
|
|
123
|
+
if item.get("model_name") == "all": summary = row
|
|
124
|
+
else: limits.append(row)
|
|
125
|
+
else:
|
|
126
|
+
# Original complex structure
|
|
127
|
+
usage = payload.get("usage")
|
|
128
|
+
if isinstance(usage, Mapping):
|
|
129
|
+
summary = _to_usage_row(cast(Mapping, usage), default_label=L["weekly_limit"])
|
|
130
|
+
raw_limits = payload.get("limits")
|
|
131
|
+
if isinstance(raw_limits, Sequence):
|
|
132
|
+
for idx, item in enumerate(raw_limits):
|
|
133
|
+
if not isinstance(item, Mapping): continue
|
|
134
|
+
detail = item.get("detail") if isinstance(item.get("detail"), Mapping) else item
|
|
135
|
+
window = item.get("window") if isinstance(item.get("window"), Mapping) else {}
|
|
136
|
+
row = _to_usage_row(detail, default_label=_limit_label(item, detail, window, idx))
|
|
137
|
+
if row: limits.append(row)
|
|
138
|
+
|
|
139
|
+
return summary, limits
|
|
140
|
+
|
|
141
|
+
def _get_visual_width(s: str) -> int:
|
|
142
|
+
import unicodedata
|
|
143
|
+
width = 0
|
|
144
|
+
for char in s:
|
|
145
|
+
if unicodedata.east_asian_width(char) in ("W", "F", "A"): width += 2
|
|
146
|
+
else: width += 1
|
|
147
|
+
return width
|
|
148
|
+
|
|
149
|
+
def _format_rows(rows: List[UsageRow]) -> Text:
|
|
150
|
+
visual_widths = [_get_visual_width(r.label) for r in rows]
|
|
151
|
+
max_visual_width = max(visual_widths) if visual_widths else 0
|
|
152
|
+
max_visual_width = max(max_visual_width, 6)
|
|
153
|
+
bar_width = 20
|
|
154
|
+
result = Text()
|
|
155
|
+
for i, row in enumerate(rows):
|
|
156
|
+
used_ratio = row.used / row.limit if row.limit > 0 else 0
|
|
157
|
+
remaining_percent = 100 - (used_ratio * 100)
|
|
158
|
+
color = "red" if used_ratio > 0.9 else "yellow" if used_ratio > 0.7 else "green"
|
|
159
|
+
filled = int(used_ratio * bar_width)
|
|
160
|
+
if i > 0: result.append("\n\n")
|
|
161
|
+
|
|
162
|
+
label_v_width = _get_visual_width(row.label)
|
|
163
|
+
padding = " " * (max_visual_width - label_v_width)
|
|
164
|
+
result.append(f"{row.label}{padding} ", style="cyan")
|
|
165
|
+
result.append("█" * filled, style=color)
|
|
166
|
+
result.append("░" * (bar_width - filled))
|
|
167
|
+
result.append(f" {used_ratio * 100:.0f}% {remaining_percent:.0f}% {L['remaining']}", style="bold")
|
|
168
|
+
|
|
169
|
+
meta_parts = []
|
|
170
|
+
if row.countdown: meta_parts.append(f"{L['countdown']}: {row.countdown}")
|
|
171
|
+
if row.reset_at: meta_parts.append(f"{L['reset']}: {row.reset_at}")
|
|
172
|
+
if meta_parts:
|
|
173
|
+
result.append("\n")
|
|
174
|
+
result.append(" ".join(meta_parts), style="dim cyan")
|
|
175
|
+
return result
|
|
176
|
+
|
|
177
|
+
async def get_usage_data(api_key: str, base_url: str) -> Tuple[UsageRow | None, List[UsageRow]]:
|
|
178
|
+
url = base_url.rstrip("/") + "/usages"
|
|
179
|
+
async with aiohttp.ClientSession() as session:
|
|
180
|
+
async with session.get(url, headers={"Authorization": f"Bearer {api_key}"}) as resp:
|
|
181
|
+
if resp.status != 200:
|
|
182
|
+
# Try fallback /usage if /usages fails
|
|
183
|
+
fallback_url = base_url.rstrip("/") + "/usage"
|
|
184
|
+
async with session.get(fallback_url, headers={"Authorization": f"Bearer {api_key}"}) as f_resp:
|
|
185
|
+
if f_resp.status != 200:
|
|
186
|
+
text = await f_resp.text()
|
|
187
|
+
raise Exception(f"{L['error_api']} {f_resp.status}: {text}")
|
|
188
|
+
payload = await f_resp.json()
|
|
189
|
+
else:
|
|
190
|
+
payload = await resp.json()
|
|
191
|
+
|
|
192
|
+
return _parse_usage_payload(payload)
|
|
193
|
+
|
|
194
|
+
async def main():
|
|
195
|
+
load_dotenv()
|
|
196
|
+
parser = argparse.ArgumentParser(description="Kimi Code Usage CLI")
|
|
197
|
+
parser.add_argument("--json", action="store_true")
|
|
198
|
+
parser.add_argument("--plain", action="store_true")
|
|
199
|
+
args = parser.parse_args()
|
|
200
|
+
|
|
201
|
+
api_key = os.getenv("KIMI_API_KEY") or os.getenv("KIMI_CODING_API_KEY")
|
|
202
|
+
base_url = os.getenv("KIMI_BASE_URL", "https://api.kimi.com/coding/v1")
|
|
203
|
+
if not api_key:
|
|
204
|
+
print(f"[Error] {L['error_key']}", file=sys.stderr)
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
summary, limits = await get_usage_data(api_key, base_url)
|
|
209
|
+
rows = ([summary] if summary else []) + limits
|
|
210
|
+
|
|
211
|
+
if args.json:
|
|
212
|
+
print(json.dumps([{"label": r.label, "used": r.used, "limit": r.limit, "reset_at": r.reset_at} for r in rows], ensure_ascii=False))
|
|
213
|
+
elif args.plain:
|
|
214
|
+
for r in rows:
|
|
215
|
+
print(f"{r.label}: {r.used}/{r.limit} ({r.used/r.limit*100:.0f}% used)")
|
|
216
|
+
else:
|
|
217
|
+
console = Console()
|
|
218
|
+
if not rows:
|
|
219
|
+
console.print(Panel(Text(L["no_data"], style="dim"), title=f"[bold]{L['title']}[/bold]"))
|
|
220
|
+
else:
|
|
221
|
+
console.print(Panel(_format_rows(rows), title=f"[bold]{L['title']}[/bold]", expand=False, padding=(1, 2, 0, 2)))
|
|
222
|
+
except Exception as e:
|
|
223
|
+
print(f"[Error] {e}", file=sys.stderr)
|
|
224
|
+
|
|
225
|
+
def run_cli():
|
|
226
|
+
asyncio.run(main())
|
|
227
|
+
|
|
228
|
+
if __name__ == "__main__":
|
|
229
|
+
run_cli()
|
kimi_code_usage/mcp.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from fastmcp import FastMCP
|
|
3
|
+
from .main import get_usage_data, _format_rows, L
|
|
4
|
+
|
|
5
|
+
# Initialize FastMCP for Kimi Code Usage
|
|
6
|
+
mcp = FastMCP("Kimi Code Usage")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@mcp.tool()
|
|
10
|
+
async def get_kimi_usage() -> str:
|
|
11
|
+
"""
|
|
12
|
+
Get the current Kimi Coding Plan API usage and quota limits.
|
|
13
|
+
Returns a formatted summary including used/remaining quota and reset time.
|
|
14
|
+
获取当前 Kimi Coding Plan API 的使用量和配额限制,包括已用量、剩余量和重置时间。
|
|
15
|
+
"""
|
|
16
|
+
api_key = os.getenv("KIMI_API_KEY") or os.getenv("KIMI_CODING_API_KEY")
|
|
17
|
+
base_url = os.getenv("KIMI_BASE_URL", "https://api.kimi.com/coding/v1")
|
|
18
|
+
|
|
19
|
+
if not api_key:
|
|
20
|
+
return f"Error: {L['error_key']}"
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
summary, limits = await get_usage_data(api_key, base_url)
|
|
24
|
+
rows = ([summary] if summary else []) + limits
|
|
25
|
+
|
|
26
|
+
if not rows:
|
|
27
|
+
return L["no_data"]
|
|
28
|
+
|
|
29
|
+
# Return clean plain text — no ANSI codes, friendly for LLMs
|
|
30
|
+
lines = []
|
|
31
|
+
for row in rows:
|
|
32
|
+
used_ratio = row.used / row.limit if row.limit > 0 else 0
|
|
33
|
+
remaining = row.limit - row.used
|
|
34
|
+
remaining_pct = 100 - used_ratio * 100
|
|
35
|
+
line = f"{row.label}: {row.used}/{row.limit} used ({remaining_pct:.0f}% remaining)"
|
|
36
|
+
if row.countdown and row.reset_at:
|
|
37
|
+
line += f" | Reset in {row.countdown} (at {row.reset_at})"
|
|
38
|
+
lines.append(line)
|
|
39
|
+
|
|
40
|
+
return "\n".join(lines)
|
|
41
|
+
|
|
42
|
+
except Exception as e:
|
|
43
|
+
return f"Error fetching Kimi usage: {str(e)}"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def run_mcp():
|
|
47
|
+
mcp.run()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
run_mcp()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kimi-code-usage
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A curated Kimi Coding Plan usage monitor with aesthetic CLI and MCP server.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Golden0Voyager/kimi-code-usage
|
|
6
|
+
Project-URL: Repository, https://github.com/Golden0Voyager/kimi-code-usage
|
|
7
|
+
Project-URL: Issues, https://github.com/Golden0Voyager/kimi-code-usage/issues
|
|
8
|
+
Author-email: Haining Yu <hainingyu@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: curated,kimi,mcp,monitor,moonshot,usage
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: aiohttp>=3.8.0
|
|
13
|
+
Requires-Dist: fastmcp>=0.1.0
|
|
14
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
15
|
+
Requires-Dist: rich>=12.0.0
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img src="vscode-extension/assets/banner.png" width="100%" alt="Kimi Code Usage Banner">
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
# Kimi Code Usage: The Curated Toolchain
|
|
23
|
+
|
|
24
|
+
**Manifesting your AI quota with aesthetic precision across CLI, MCP, and VS Code.**
|
|
25
|
+
**以优雅的姿态,在终端、AI 助手与编辑器中感知你的 AI 额度。**
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
### 🌟 Project Vision | 项目愿景
|
|
30
|
+
|
|
31
|
+
In the era of "Vibecoding," transparency of resources is a prerequisite for flow. **Kimi Code Usage** is a meticulously crafted toolchain — three components, one soul.
|
|
32
|
+
|
|
33
|
+
在"直觉编程"时代,资源的透明度是进入心流状态的前提。**Kimi Code Usage** 是一套精心打磨的工具链 — 三种形态,一个灵魂。
|
|
34
|
+
|
|
35
|
+
**Common Prerequisite:** A [Kimi Coding Plan](https://api.kimi.com/coding/v1) API Key, set as `KIMI_API_KEY` in your environment or `.env` file.
|
|
36
|
+
**统一前提:** 在环境变量或 `.env` 文件中设置 `KIMI_API_KEY`。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
### ⚡ CLI Reporter | 终端报告器
|
|
41
|
+
|
|
42
|
+
> A Rich-rendered panel in your terminal. Zero noise, pure signal.
|
|
43
|
+
> 在你的终端中渲染出带有工业美感的配额面板。
|
|
44
|
+
|
|
45
|
+
**Install & Run:**
|
|
46
|
+
```bash
|
|
47
|
+
pip install kimi-code-usage
|
|
48
|
+
kimi-usage # Aesthetic Rich panel
|
|
49
|
+
kimi-usage --json # Machine-readable JSON
|
|
50
|
+
kimi-usage --plain # Plain text output
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or run instantly without installing:
|
|
54
|
+
```bash
|
|
55
|
+
uvx kimi-code-usage
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
### 🔍 MCP Server | AI 智能体接口
|
|
61
|
+
|
|
62
|
+
> Exposes `get_kimi_usage` to any MCP-compatible AI Agent.
|
|
63
|
+
> 让你的 AI 助手能够主动感知你的额度状态。
|
|
64
|
+
|
|
65
|
+
Compatible with **Claude Code, Cursor, Windsurf, Hermes**, and any MCP-enabled agent.
|
|
66
|
+
|
|
67
|
+
**Add to your MCP config** (e.g., `~/.claude/settings.json`):
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"kimi-code-usage": {
|
|
72
|
+
"command": "uvx",
|
|
73
|
+
"args": ["--from", "kimi-code-usage", "kimi-mcp"],
|
|
74
|
+
"env": {
|
|
75
|
+
"KIMI_API_KEY": "YOUR_KEY"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
> **Note:** The MCP server still needs `--from` because `kimi-mcp` is a separate command from the default `kimi-code-usage` entry point.
|
|
83
|
+
|
|
84
|
+
Then simply ask your AI: *"Check my Kimi quota."* / *"帮我查一下 Kimi 用量。"*
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### 💎 VS Code Extension | 编辑器插件
|
|
89
|
+
|
|
90
|
+
> A sleek status bar indicator with sensory color alerting.
|
|
91
|
+
> 状态栏实时显示剩余百分比,颜色随额度变化而呼吸。
|
|
92
|
+
|
|
93
|
+
**Install:** Search `Kimi Code Usage` in the VS Code Marketplace, or:
|
|
94
|
+
```bash
|
|
95
|
+
code --install-extension HainingYu.kimi-code-usage
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Configure** (`Settings > kimiUsage`):
|
|
99
|
+
|
|
100
|
+
| Setting | Description | Default |
|
|
101
|
+
| :--- | :--- | :--- |
|
|
102
|
+
| `apiKey` | API key (or reads `KIMI_API_KEY` env) | `""` |
|
|
103
|
+
| `refreshInterval` | Auto-refresh in minutes | `5` |
|
|
104
|
+
| `warnPercent` | Yellow caution threshold | `30%` |
|
|
105
|
+
| `criticalPercent` | Red alert threshold | `10%` |
|
|
106
|
+
|
|
107
|
+
**Usage:** Status bar shows `⬡ W:96% 5H:99%`. Hover for details. `Cmd+Shift+P → Kimi: Refresh`.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### 🎨 About the Curator | 关于策展人
|
|
112
|
+
|
|
113
|
+
Crafted with ❤️ by **Haining Yu**, an Art Curator and Vibecoder. This toolchain is part of a curated collection designed to bridge the gap between aesthetic curation and intuitive, AI-powered coding.
|
|
114
|
+
|
|
115
|
+
由 **Haining Yu** 精心打磨。作为一名艺术策展人与 Vibecoder,我将代码视作展览,力求在审美策展与直觉化 AI 编程之间寻找完美的平衡。
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
<p align="center">
|
|
120
|
+
<strong>Enjoy the flow. Stay in the vibe.</strong>
|
|
121
|
+
</p>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
kimi_code_usage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
kimi_code_usage/main.py,sha256=d5Zwq5Beu6Ya-VdEyoBjBdAUJNVOtH69GdPQ4uGFd_s,9078
|
|
3
|
+
kimi_code_usage/mcp.py,sha256=dGV0WyIj7Tr09XSSJ-JEnB8xJtuugxpyub-dtC9XphM,1606
|
|
4
|
+
kimi_code_usage-0.1.0.dist-info/METADATA,sha256=aVbqBKD2al9udnj50iO_QNDRZlZYC5gOlIrtqUvh9JE,4068
|
|
5
|
+
kimi_code_usage-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
6
|
+
kimi_code_usage-0.1.0.dist-info/entry_points.txt,sha256=UdUDvcbag8n65wmubjm22zUDyd31Fti3VanOzH9-ZrM,146
|
|
7
|
+
kimi_code_usage-0.1.0.dist-info/RECORD,,
|