@ccusage/mcp 0.0.1 → 17.0.1
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/LICENSE +21 -0
- package/README.md +74 -7
- package/dist/index.d.ts +2 -0
- package/dist/index.js +541 -0
- package/package.json +39 -7
- package/CLAUDE.md +0 -107
- package/bun.lock +0 -29
- package/index.ts +0 -1
- package/tsconfig.json +0 -29
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ryoppippi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,15 +1,82 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/public/logo.svg" alt="ccusage logo" width="256" height="256">
|
|
3
|
+
<h1>@ccusage/mcp</h1>
|
|
4
|
+
</div>
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="https://socket.dev/api/npm/package/@ccusage/mcp"><img src="https://socket.dev/api/badge/npm/package/@ccusage/mcp" alt="Socket Badge" /></a>
|
|
8
|
+
<a href="https://npmjs.com/package/@ccusage/mcp"><img src="https://img.shields.io/npm/v/@ccusage/mcp?color=yellow" alt="npm version" /></a>
|
|
9
|
+
<a href="https://tanstack.com/stats/npm?packageGroups=%5B%7B%22packages%22:%5B%7B%22name%22:%22@ccusage/mcp%22%7D%5D%7D%5D&range=30-days&transform=none&binType=daily&showDataMode=all&height=400"><img src="https://img.shields.io/npm/dy/@ccusage/mcp" alt="NPM Downloads" /></a>
|
|
10
|
+
<a href="https://packagephobia.com/result?p=@ccusage/mcp"><img src="https://packagephobia.com/badge?p=@ccusage/mcp" alt="install size" /></a>
|
|
11
|
+
<a href="https://deepwiki.com/ryoppippi/ccusage"><img src="https://img.shields.io/badge/DeepWiki-ryoppippi%2Fccusage-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXCGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==" alt="DeepWiki"></a>
|
|
12
|
+
<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<div align="center">
|
|
16
|
+
<img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/public/mcp-claude-desktop.avif" alt="Claude Desktop MCP integration screenshot" width="640">
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
> MCP (Model Context Protocol) server implementation for ccusage - provides Claude Code usage data through the MCP protocol.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
4
22
|
|
|
5
23
|
```bash
|
|
6
|
-
|
|
24
|
+
# Using bunx (recommended for speed)
|
|
25
|
+
bunx @ccusage/mcp@latest
|
|
26
|
+
|
|
27
|
+
# Using npx
|
|
28
|
+
npx @ccusage/mcp@latest
|
|
29
|
+
|
|
30
|
+
# Start with HTTP transport
|
|
31
|
+
bunx @ccusage/mcp@latest -- --type http --port 8080
|
|
7
32
|
```
|
|
8
33
|
|
|
9
|
-
|
|
34
|
+
## Integrations
|
|
10
35
|
|
|
11
|
-
|
|
12
|
-
|
|
36
|
+
### Claude Desktop Integration
|
|
37
|
+
|
|
38
|
+
Add to your Claude Desktop MCP configuration:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"ccusage": {
|
|
44
|
+
"command": "npx",
|
|
45
|
+
"args": ["@ccusage/mcp@latest"],
|
|
46
|
+
"type": "stdio"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
13
50
|
```
|
|
14
51
|
|
|
15
|
-
|
|
52
|
+
### Claude Code
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
claude mcp add ccusage npx -- @ccusage/mcp@latest
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Documentation
|
|
59
|
+
|
|
60
|
+
For full documentation, visit **[ccusage.com/guide/mcp-server](https://ccusage.com/guide/mcp-server)**
|
|
61
|
+
|
|
62
|
+
## Sponsors
|
|
63
|
+
|
|
64
|
+
### Featured Sponsor
|
|
65
|
+
|
|
66
|
+
Check out [ccusage: The Claude Code cost scorecard that went viral](https://www.youtube.com/watch?v=Ak6qpQ5qdgk)
|
|
67
|
+
|
|
68
|
+
<p align="center">
|
|
69
|
+
<a href="https://www.youtube.com/watch?v=Ak6qpQ5qdgk">
|
|
70
|
+
<img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/public/ccusage_thumbnail.png" alt="ccusage: The Claude Code cost scorecard that went viral" width="600">
|
|
71
|
+
</a>
|
|
72
|
+
</p>
|
|
73
|
+
|
|
74
|
+
<p align="center">
|
|
75
|
+
<a href="https://github.com/sponsors/ryoppippi">
|
|
76
|
+
<img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg">
|
|
77
|
+
</a>
|
|
78
|
+
</p>
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
MIT © [@ryoppippi](https://github.com/ryoppippi)
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import process$1 from "node:process";
|
|
4
|
+
import { serve } from "@hono/node-server";
|
|
5
|
+
import { getClaudePaths } from "ccusage/data-loader";
|
|
6
|
+
import { logger } from "ccusage/logger";
|
|
7
|
+
import { cli, define } from "gunshi";
|
|
8
|
+
import { StreamableHTTPTransport } from "@hono/mcp";
|
|
9
|
+
import "@modelcontextprotocol/sdk/client/index.js";
|
|
10
|
+
import "@modelcontextprotocol/sdk/inMemory.js";
|
|
11
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
+
import a from "node:fs/promises";
|
|
14
|
+
import c from "node:path";
|
|
15
|
+
import b from "node:fs";
|
|
16
|
+
import F from "node:os";
|
|
17
|
+
import { Hono } from "hono/tiny";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import spawn, { SubprocessError } from "nano-spawn";
|
|
20
|
+
var name = "@ccusage/mcp";
|
|
21
|
+
var version = "17.0.1";
|
|
22
|
+
var description = "MCP server implementation for ccusage data";
|
|
23
|
+
var d = Object.defineProperty;
|
|
24
|
+
var n = (s, t) => d(s, "name", {
|
|
25
|
+
value: t,
|
|
26
|
+
configurable: !0
|
|
27
|
+
});
|
|
28
|
+
typeof Symbol.asyncDispose != "symbol" && Object.defineProperty(Symbol, "asyncDispose", {
|
|
29
|
+
configurable: !1,
|
|
30
|
+
enumerable: !1,
|
|
31
|
+
writable: !1,
|
|
32
|
+
value: Symbol.for("asyncDispose")
|
|
33
|
+
});
|
|
34
|
+
var P = class {
|
|
35
|
+
static {
|
|
36
|
+
n(this, "FsFixture");
|
|
37
|
+
}
|
|
38
|
+
path;
|
|
39
|
+
constructor(t) {
|
|
40
|
+
this.path = t;
|
|
41
|
+
}
|
|
42
|
+
getPath(...t) {
|
|
43
|
+
return c.join(this.path, ...t);
|
|
44
|
+
}
|
|
45
|
+
exists(t = "") {
|
|
46
|
+
return a.access(this.getPath(t)).then(() => !0, () => !1);
|
|
47
|
+
}
|
|
48
|
+
rm(t = "") {
|
|
49
|
+
return a.rm(this.getPath(t), {
|
|
50
|
+
recursive: !0,
|
|
51
|
+
force: !0
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
cp(t, r, i) {
|
|
55
|
+
return r ? r.endsWith(c.sep) && (r += c.basename(t)) : r = c.basename(t), a.cp(t, this.getPath(r), i);
|
|
56
|
+
}
|
|
57
|
+
mkdir(t) {
|
|
58
|
+
return a.mkdir(this.getPath(t), { recursive: !0 });
|
|
59
|
+
}
|
|
60
|
+
writeFile(t, r) {
|
|
61
|
+
return a.writeFile(this.getPath(t), r);
|
|
62
|
+
}
|
|
63
|
+
writeJson(t, r) {
|
|
64
|
+
return this.writeFile(t, JSON.stringify(r, null, 2));
|
|
65
|
+
}
|
|
66
|
+
readFile(t, r) {
|
|
67
|
+
return a.readFile(this.getPath(t), r);
|
|
68
|
+
}
|
|
69
|
+
async [Symbol.asyncDispose]() {
|
|
70
|
+
await this.rm();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const v = b.realpathSync(F.tmpdir()), D = `fs-fixture-${Date.now()}-${process.pid}`;
|
|
74
|
+
let m = 0;
|
|
75
|
+
const j = n(() => (m += 1, m), "getId");
|
|
76
|
+
var u = class {
|
|
77
|
+
static {
|
|
78
|
+
n(this, "Path");
|
|
79
|
+
}
|
|
80
|
+
path;
|
|
81
|
+
constructor(t) {
|
|
82
|
+
this.path = t;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
var f = class extends u {
|
|
86
|
+
static {
|
|
87
|
+
n(this, "Directory");
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var y = class extends u {
|
|
91
|
+
static {
|
|
92
|
+
n(this, "File");
|
|
93
|
+
}
|
|
94
|
+
content;
|
|
95
|
+
constructor(t, r) {
|
|
96
|
+
super(t), this.content = r;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var l = class {
|
|
100
|
+
static {
|
|
101
|
+
n(this, "Symlink");
|
|
102
|
+
}
|
|
103
|
+
target;
|
|
104
|
+
type;
|
|
105
|
+
path;
|
|
106
|
+
constructor(t, r) {
|
|
107
|
+
this.target = t, this.type = r;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const w = n((s, t, r) => {
|
|
111
|
+
const i = [];
|
|
112
|
+
for (const p in s) {
|
|
113
|
+
if (!Object.hasOwn(s, p)) continue;
|
|
114
|
+
const e = c.join(t, p);
|
|
115
|
+
let o = s[p];
|
|
116
|
+
if (typeof o == "function") {
|
|
117
|
+
const g = Object.assign(Object.create(r), { filePath: e }), h = o(g);
|
|
118
|
+
if (h instanceof l) {
|
|
119
|
+
h.path = e, i.push(h);
|
|
120
|
+
continue;
|
|
121
|
+
} else o = h;
|
|
122
|
+
}
|
|
123
|
+
typeof o == "string" ? i.push(new y(e, o)) : i.push(new f(e), ...w(o, e, r));
|
|
124
|
+
}
|
|
125
|
+
return i;
|
|
126
|
+
}, "flattenFileTree");
|
|
127
|
+
n(async (s, t) => {
|
|
128
|
+
const r = t?.tempDir ? c.resolve(t.tempDir) : v, i = c.join(r, `${D}-${j()}/`);
|
|
129
|
+
if (await a.mkdir(i, { recursive: !0 }), s) {
|
|
130
|
+
if (typeof s == "string") await a.cp(s, i, {
|
|
131
|
+
recursive: !0,
|
|
132
|
+
filter: t?.templateFilter
|
|
133
|
+
});
|
|
134
|
+
else if (typeof s == "object") {
|
|
135
|
+
const p = {
|
|
136
|
+
fixturePath: i,
|
|
137
|
+
getPath: n((...e) => c.join(i, ...e), "getPath"),
|
|
138
|
+
symlink: n((e, o) => new l(e, o), "symlink")
|
|
139
|
+
};
|
|
140
|
+
await Promise.all(w(s, i, p).map(async (e) => {
|
|
141
|
+
e instanceof f ? await a.mkdir(e.path, { recursive: !0 }) : e instanceof l ? (await a.mkdir(c.dirname(e.path), { recursive: !0 }), await a.symlink(e.target, e.path, e.type)) : e instanceof y && (await a.mkdir(c.dirname(e.path), { recursive: !0 }), await a.writeFile(e.path, e.content));
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return new P(i);
|
|
146
|
+
}, "createFixture");
|
|
147
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
148
|
+
function resolveBinaryPath(packageName, binName) {
|
|
149
|
+
let packageJsonPath;
|
|
150
|
+
try {
|
|
151
|
+
packageJsonPath = nodeRequire.resolve(`${packageName}/package.json`);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
throw new Error(`Unable to resolve ${packageName}. Install the package alongside @ccusage/mcp to enable ${packageName} tools.`, { cause: error });
|
|
154
|
+
}
|
|
155
|
+
const packageJson = nodeRequire(packageJsonPath);
|
|
156
|
+
const binField = packageJson.bin ?? packageJson.publishConfig?.bin;
|
|
157
|
+
let binRelative;
|
|
158
|
+
if (typeof binField === "string") binRelative = binField;
|
|
159
|
+
else if (binField != null && typeof binField === "object") binRelative = binName != null && binName !== "" ? binField[binName] : Object.values(binField)[0];
|
|
160
|
+
if (binRelative == null) throw new Error(`Unable to locate ${binName ?? packageName} binary entry in ${packageName}/package.json`);
|
|
161
|
+
const packageDir = c.dirname(packageJsonPath);
|
|
162
|
+
return c.resolve(packageDir, binRelative);
|
|
163
|
+
}
|
|
164
|
+
function createCliInvocation(entryPath) {
|
|
165
|
+
if (entryPath.endsWith(".ts")) return {
|
|
166
|
+
executable: "bun",
|
|
167
|
+
prefixArgs: [entryPath]
|
|
168
|
+
};
|
|
169
|
+
return {
|
|
170
|
+
executable: process$1.execPath,
|
|
171
|
+
prefixArgs: [entryPath]
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async function executeCliCommand(executable, args, env) {
|
|
175
|
+
try {
|
|
176
|
+
const result = await spawn(executable, args, { env: {
|
|
177
|
+
...process$1.env,
|
|
178
|
+
FORCE_COLOR: "0",
|
|
179
|
+
...env
|
|
180
|
+
} });
|
|
181
|
+
const output = (result.stdout ?? result.output ?? "").trim();
|
|
182
|
+
if (output === "") throw new Error("CLI command returned empty output");
|
|
183
|
+
return output;
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (error instanceof SubprocessError) {
|
|
186
|
+
const message = (error.stderr ?? error.stdout ?? error.output ?? error.message).trim();
|
|
187
|
+
throw new Error(message);
|
|
188
|
+
}
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const filterDateSchema = z.string().regex(/^\d{8}$/, "Date must be in YYYYMMDD format");
|
|
193
|
+
const ccusageParametersShape = {
|
|
194
|
+
since: filterDateSchema.optional(),
|
|
195
|
+
until: filterDateSchema.optional(),
|
|
196
|
+
mode: z.enum([
|
|
197
|
+
"auto",
|
|
198
|
+
"calculate",
|
|
199
|
+
"display"
|
|
200
|
+
]).default("auto").optional(),
|
|
201
|
+
timezone: z.string().optional(),
|
|
202
|
+
locale: z.string().optional()
|
|
203
|
+
};
|
|
204
|
+
const ccusageParametersSchema = z.object(ccusageParametersShape);
|
|
205
|
+
let cachedCcusageInvocation = null;
|
|
206
|
+
function getCcusageInvocation() {
|
|
207
|
+
if (cachedCcusageInvocation != null) return cachedCcusageInvocation;
|
|
208
|
+
const entryPath = resolveBinaryPath("ccusage", "ccusage");
|
|
209
|
+
cachedCcusageInvocation = createCliInvocation(entryPath);
|
|
210
|
+
return cachedCcusageInvocation;
|
|
211
|
+
}
|
|
212
|
+
async function runCcusageCliJson(command, parameters, claudePath) {
|
|
213
|
+
const { executable, prefixArgs } = getCcusageInvocation();
|
|
214
|
+
const cliArgs = [
|
|
215
|
+
...prefixArgs,
|
|
216
|
+
command,
|
|
217
|
+
"--json"
|
|
218
|
+
];
|
|
219
|
+
const since = parameters.since;
|
|
220
|
+
if (since != null && since !== "") cliArgs.push("--since", since);
|
|
221
|
+
const until = parameters.until;
|
|
222
|
+
if (until != null && until !== "") cliArgs.push("--until", until);
|
|
223
|
+
const mode = parameters.mode;
|
|
224
|
+
if (mode != null && mode !== "auto") cliArgs.push("--mode", mode);
|
|
225
|
+
const timezone = parameters.timezone;
|
|
226
|
+
if (timezone != null && timezone !== "") cliArgs.push("--timezone", timezone);
|
|
227
|
+
const locale = parameters.locale;
|
|
228
|
+
if (locale != null && locale !== "") cliArgs.push("--locale", locale);
|
|
229
|
+
return executeCliCommand(executable, cliArgs, { CLAUDE_CONFIG_DIR: claudePath });
|
|
230
|
+
}
|
|
231
|
+
async function getCcusageDaily(parameters, claudePath) {
|
|
232
|
+
try {
|
|
233
|
+
const raw = await runCcusageCliJson("daily", parameters, claudePath);
|
|
234
|
+
const parsed = JSON.parse(raw);
|
|
235
|
+
if (Array.isArray(parsed) && parsed.length === 0) return {
|
|
236
|
+
daily: [],
|
|
237
|
+
totals: {}
|
|
238
|
+
};
|
|
239
|
+
return parsed;
|
|
240
|
+
} catch {
|
|
241
|
+
return {
|
|
242
|
+
daily: [],
|
|
243
|
+
totals: {}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async function getCcusageMonthly(parameters, claudePath) {
|
|
248
|
+
try {
|
|
249
|
+
const raw = await runCcusageCliJson("monthly", parameters, claudePath);
|
|
250
|
+
const parsed = JSON.parse(raw);
|
|
251
|
+
if (Array.isArray(parsed) && parsed.length === 0) return {
|
|
252
|
+
monthly: [],
|
|
253
|
+
totals: {}
|
|
254
|
+
};
|
|
255
|
+
return parsed;
|
|
256
|
+
} catch {
|
|
257
|
+
return {
|
|
258
|
+
monthly: [],
|
|
259
|
+
totals: {}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async function getCcusageSession(parameters, claudePath) {
|
|
264
|
+
try {
|
|
265
|
+
const raw = await runCcusageCliJson("session", parameters, claudePath);
|
|
266
|
+
const parsed = JSON.parse(raw);
|
|
267
|
+
if (Array.isArray(parsed) && parsed.length === 0) return {
|
|
268
|
+
sessions: [],
|
|
269
|
+
totals: {}
|
|
270
|
+
};
|
|
271
|
+
return parsed;
|
|
272
|
+
} catch {
|
|
273
|
+
return {
|
|
274
|
+
sessions: [],
|
|
275
|
+
totals: {}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async function getCcusageBlocks(parameters, claudePath) {
|
|
280
|
+
try {
|
|
281
|
+
const raw = await runCcusageCliJson("blocks", parameters, claudePath);
|
|
282
|
+
const parsed = JSON.parse(raw);
|
|
283
|
+
if (Array.isArray(parsed) && parsed.length === 0) return { blocks: [] };
|
|
284
|
+
return parsed;
|
|
285
|
+
} catch {
|
|
286
|
+
return { blocks: [] };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const codexModelUsageSchema = z.object({
|
|
290
|
+
inputTokens: z.number(),
|
|
291
|
+
cachedInputTokens: z.number(),
|
|
292
|
+
outputTokens: z.number(),
|
|
293
|
+
reasoningOutputTokens: z.number(),
|
|
294
|
+
totalTokens: z.number(),
|
|
295
|
+
isFallback: z.boolean().optional()
|
|
296
|
+
});
|
|
297
|
+
const codexTotalsSchema = z.object({
|
|
298
|
+
inputTokens: z.number(),
|
|
299
|
+
cachedInputTokens: z.number(),
|
|
300
|
+
outputTokens: z.number(),
|
|
301
|
+
reasoningOutputTokens: z.number(),
|
|
302
|
+
totalTokens: z.number(),
|
|
303
|
+
costUSD: z.number()
|
|
304
|
+
});
|
|
305
|
+
const codexDailyRowSchema = z.object({
|
|
306
|
+
date: z.string(),
|
|
307
|
+
inputTokens: z.number(),
|
|
308
|
+
cachedInputTokens: z.number(),
|
|
309
|
+
outputTokens: z.number(),
|
|
310
|
+
reasoningOutputTokens: z.number(),
|
|
311
|
+
totalTokens: z.number(),
|
|
312
|
+
costUSD: z.number(),
|
|
313
|
+
models: z.record(codexModelUsageSchema)
|
|
314
|
+
});
|
|
315
|
+
const codexMonthlyRowSchema = z.object({
|
|
316
|
+
month: z.string(),
|
|
317
|
+
inputTokens: z.number(),
|
|
318
|
+
cachedInputTokens: z.number(),
|
|
319
|
+
outputTokens: z.number(),
|
|
320
|
+
reasoningOutputTokens: z.number(),
|
|
321
|
+
totalTokens: z.number(),
|
|
322
|
+
costUSD: z.number(),
|
|
323
|
+
models: z.record(codexModelUsageSchema)
|
|
324
|
+
});
|
|
325
|
+
const codexDailyResponseSchema = z.object({
|
|
326
|
+
daily: z.array(codexDailyRowSchema),
|
|
327
|
+
totals: codexTotalsSchema.nullable()
|
|
328
|
+
});
|
|
329
|
+
const codexMonthlyResponseSchema = z.object({
|
|
330
|
+
monthly: z.array(codexMonthlyRowSchema),
|
|
331
|
+
totals: codexTotalsSchema.nullable()
|
|
332
|
+
});
|
|
333
|
+
const codexParametersShape = {
|
|
334
|
+
since: z.string().optional(),
|
|
335
|
+
until: z.string().optional(),
|
|
336
|
+
timezone: z.string().optional(),
|
|
337
|
+
locale: z.string().optional(),
|
|
338
|
+
offline: z.boolean().optional()
|
|
339
|
+
};
|
|
340
|
+
const codexParametersSchema = z.object(codexParametersShape);
|
|
341
|
+
let cachedCodexInvocation = null;
|
|
342
|
+
function getCodexInvocation() {
|
|
343
|
+
if (cachedCodexInvocation != null) return cachedCodexInvocation;
|
|
344
|
+
const entryPath = resolveBinaryPath("@ccusage/codex", "ccusage-codex");
|
|
345
|
+
cachedCodexInvocation = createCliInvocation(entryPath);
|
|
346
|
+
return cachedCodexInvocation;
|
|
347
|
+
}
|
|
348
|
+
async function runCodexCliJson(command, parameters) {
|
|
349
|
+
const { executable, prefixArgs } = getCodexInvocation();
|
|
350
|
+
const cliArgs = [
|
|
351
|
+
...prefixArgs,
|
|
352
|
+
command,
|
|
353
|
+
"--json"
|
|
354
|
+
];
|
|
355
|
+
const since = parameters.since;
|
|
356
|
+
if (since != null && since !== "") cliArgs.push("--since", since);
|
|
357
|
+
const until = parameters.until;
|
|
358
|
+
if (until != null && until !== "") cliArgs.push("--until", until);
|
|
359
|
+
const timezone = parameters.timezone;
|
|
360
|
+
if (timezone != null && timezone !== "") cliArgs.push("--timezone", timezone);
|
|
361
|
+
const locale = parameters.locale;
|
|
362
|
+
if (locale != null && locale !== "") cliArgs.push("--locale", locale);
|
|
363
|
+
if (parameters.offline === true) cliArgs.push("--offline");
|
|
364
|
+
else if (parameters.offline === false) cliArgs.push("--no-offline");
|
|
365
|
+
return executeCliCommand(executable, cliArgs, {});
|
|
366
|
+
}
|
|
367
|
+
async function getCodexDaily(parameters) {
|
|
368
|
+
const raw = await runCodexCliJson("daily", parameters);
|
|
369
|
+
return codexDailyResponseSchema.parse(JSON.parse(raw));
|
|
370
|
+
}
|
|
371
|
+
async function getCodexMonthly(parameters) {
|
|
372
|
+
const raw = await runCodexCliJson("monthly", parameters);
|
|
373
|
+
return codexMonthlyResponseSchema.parse(JSON.parse(raw));
|
|
374
|
+
}
|
|
375
|
+
function defaultOptions() {
|
|
376
|
+
const paths = getClaudePaths();
|
|
377
|
+
if (paths.length === 0) throw new Error("No valid Claude path found. Ensure getClaudePaths() returns at least one valid path.");
|
|
378
|
+
return { claudePath: paths[0] };
|
|
379
|
+
}
|
|
380
|
+
function createMcpServer(options) {
|
|
381
|
+
const server = new McpServer({
|
|
382
|
+
name,
|
|
383
|
+
version
|
|
384
|
+
});
|
|
385
|
+
const { claudePath = "" } = options ?? defaultOptions();
|
|
386
|
+
if (claudePath === "") throw new Error("Claude path is required");
|
|
387
|
+
server.registerTool("daily", {
|
|
388
|
+
description: "Show usage report grouped by date",
|
|
389
|
+
inputSchema: ccusageParametersShape
|
|
390
|
+
}, async (args) => {
|
|
391
|
+
const parameters = ccusageParametersSchema.parse(args);
|
|
392
|
+
const jsonOutput = await getCcusageDaily(parameters, claudePath);
|
|
393
|
+
return { content: [{
|
|
394
|
+
type: "text",
|
|
395
|
+
text: JSON.stringify(jsonOutput, null, 2)
|
|
396
|
+
}] };
|
|
397
|
+
});
|
|
398
|
+
server.registerTool("session", {
|
|
399
|
+
description: "Show usage report grouped by conversation session",
|
|
400
|
+
inputSchema: ccusageParametersShape
|
|
401
|
+
}, async (args) => {
|
|
402
|
+
const parameters = ccusageParametersSchema.parse(args);
|
|
403
|
+
const jsonOutput = await getCcusageSession(parameters, claudePath);
|
|
404
|
+
return { content: [{
|
|
405
|
+
type: "text",
|
|
406
|
+
text: JSON.stringify(jsonOutput, null, 2)
|
|
407
|
+
}] };
|
|
408
|
+
});
|
|
409
|
+
server.registerTool("monthly", {
|
|
410
|
+
description: "Show usage report grouped by month",
|
|
411
|
+
inputSchema: ccusageParametersShape
|
|
412
|
+
}, async (args) => {
|
|
413
|
+
const parameters = ccusageParametersSchema.parse(args);
|
|
414
|
+
const jsonOutput = await getCcusageMonthly(parameters, claudePath);
|
|
415
|
+
return { content: [{
|
|
416
|
+
type: "text",
|
|
417
|
+
text: JSON.stringify(jsonOutput, null, 2)
|
|
418
|
+
}] };
|
|
419
|
+
});
|
|
420
|
+
server.registerTool("blocks", {
|
|
421
|
+
description: "Show usage report grouped by session billing blocks",
|
|
422
|
+
inputSchema: ccusageParametersShape
|
|
423
|
+
}, async (args) => {
|
|
424
|
+
const parameters = ccusageParametersSchema.parse(args);
|
|
425
|
+
const jsonOutput = await getCcusageBlocks(parameters, claudePath);
|
|
426
|
+
return { content: [{
|
|
427
|
+
type: "text",
|
|
428
|
+
text: JSON.stringify(jsonOutput, null, 2)
|
|
429
|
+
}] };
|
|
430
|
+
});
|
|
431
|
+
server.registerTool("codex-daily", {
|
|
432
|
+
description: "Show Codex usage grouped by day",
|
|
433
|
+
inputSchema: codexParametersShape
|
|
434
|
+
}, async (args) => {
|
|
435
|
+
const parameters = codexParametersSchema.parse(args);
|
|
436
|
+
const codexDaily = await getCodexDaily(parameters);
|
|
437
|
+
return { content: [{
|
|
438
|
+
type: "text",
|
|
439
|
+
text: JSON.stringify(codexDaily, null, 2)
|
|
440
|
+
}] };
|
|
441
|
+
});
|
|
442
|
+
server.registerTool("codex-monthly", {
|
|
443
|
+
description: "Show Codex usage grouped by month",
|
|
444
|
+
inputSchema: codexParametersShape
|
|
445
|
+
}, async (args) => {
|
|
446
|
+
const parameters = codexParametersSchema.parse(args);
|
|
447
|
+
const codexMonthly = await getCodexMonthly(parameters);
|
|
448
|
+
return { content: [{
|
|
449
|
+
type: "text",
|
|
450
|
+
text: JSON.stringify(codexMonthly, null, 2)
|
|
451
|
+
}] };
|
|
452
|
+
});
|
|
453
|
+
return server;
|
|
454
|
+
}
|
|
455
|
+
async function startMcpServerStdio(server) {
|
|
456
|
+
const transport = new StdioServerTransport();
|
|
457
|
+
await server.connect(transport);
|
|
458
|
+
}
|
|
459
|
+
function createMcpHttpApp(options) {
|
|
460
|
+
const app = new Hono();
|
|
461
|
+
const mcpServer = createMcpServer(options ?? defaultOptions());
|
|
462
|
+
app.all("/", async (c$1) => {
|
|
463
|
+
const transport = new StreamableHTTPTransport();
|
|
464
|
+
await mcpServer.connect(transport);
|
|
465
|
+
return transport.handleRequest(c$1);
|
|
466
|
+
});
|
|
467
|
+
return app;
|
|
468
|
+
}
|
|
469
|
+
const MCP_DEFAULT_PORT = 8080;
|
|
470
|
+
const mcpCommand = define({
|
|
471
|
+
name: "mcp",
|
|
472
|
+
description: "Start MCP server with usage reporting tools",
|
|
473
|
+
args: {
|
|
474
|
+
mode: {
|
|
475
|
+
type: "enum",
|
|
476
|
+
short: "m",
|
|
477
|
+
description: "Cost calculation mode for usage reports",
|
|
478
|
+
choices: [
|
|
479
|
+
"auto",
|
|
480
|
+
"calculate",
|
|
481
|
+
"display"
|
|
482
|
+
],
|
|
483
|
+
default: "auto"
|
|
484
|
+
},
|
|
485
|
+
type: {
|
|
486
|
+
type: "enum",
|
|
487
|
+
short: "t",
|
|
488
|
+
description: "Transport type for MCP server",
|
|
489
|
+
choices: ["stdio", "http"],
|
|
490
|
+
default: "stdio"
|
|
491
|
+
},
|
|
492
|
+
port: {
|
|
493
|
+
type: "number",
|
|
494
|
+
short: "p",
|
|
495
|
+
description: `Port for HTTP transport (default: ${MCP_DEFAULT_PORT})`,
|
|
496
|
+
default: MCP_DEFAULT_PORT
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
async run(ctx) {
|
|
500
|
+
const { type: mcpType, mode, port } = ctx.values;
|
|
501
|
+
if (mcpType === "stdio") logger.level = 0;
|
|
502
|
+
const paths = getClaudePaths();
|
|
503
|
+
if (paths.length === 0) {
|
|
504
|
+
logger.error("No valid Claude data directory found");
|
|
505
|
+
throw new Error("No valid Claude data directory found");
|
|
506
|
+
}
|
|
507
|
+
const options = {
|
|
508
|
+
claudePath: paths.at(0),
|
|
509
|
+
mode
|
|
510
|
+
};
|
|
511
|
+
switch (mcpType) {
|
|
512
|
+
case "stdio": {
|
|
513
|
+
const server = createMcpServer(options);
|
|
514
|
+
await startMcpServerStdio(server);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
case "http": {
|
|
518
|
+
const app = createMcpHttpApp(options);
|
|
519
|
+
serve({
|
|
520
|
+
fetch: app.fetch,
|
|
521
|
+
port
|
|
522
|
+
});
|
|
523
|
+
logger.info(`MCP server is running on http://localhost:${port}`);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
default: throw new Error(`Unsupported MCP type: ${mcpType}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
async function runCli(argv = process$1.argv.slice(2)) {
|
|
531
|
+
let args = argv;
|
|
532
|
+
if (args[0] === "ccusage-mcp") args = args.slice(1);
|
|
533
|
+
await cli(args, mcpCommand, {
|
|
534
|
+
name,
|
|
535
|
+
version,
|
|
536
|
+
description,
|
|
537
|
+
subCommands: /* @__PURE__ */ new Map()
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
runCli();
|
|
541
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,12 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ccusage/mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
3
|
+
"version": "17.0.1",
|
|
4
|
+
"description": "MCP server implementation for ccusage data",
|
|
5
|
+
"homepage": "https://github.com/ryoppippi/ccusage#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/ryoppippi/ccusage/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/ryoppippi/ccusage.git"
|
|
12
|
+
},
|
|
13
|
+
"funding": "https://github.com/ryoppippi/ccusage?sponsor=1",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "ryoppippi",
|
|
5
16
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
17
|
+
"exports": {
|
|
18
|
+
".": "./dist/index.js",
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"module": "./dist/index.js",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"bin": {
|
|
25
|
+
"ccusage-mcp": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@hono/mcp": "^0.1.4",
|
|
32
|
+
"@hono/node-server": "^1.19.2",
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.17.3",
|
|
34
|
+
"gunshi": "^0.26.3",
|
|
35
|
+
"hono": "^4.9.2",
|
|
36
|
+
"nano-spawn": "^1.0.2",
|
|
37
|
+
"zod": "^3.25.67",
|
|
38
|
+
"@ccusage/codex": "17.0.1",
|
|
39
|
+
"ccusage": "17.0.1"
|
|
8
40
|
},
|
|
9
|
-
"
|
|
10
|
-
"
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20.19.4"
|
|
11
43
|
}
|
|
12
|
-
}
|
|
44
|
+
}
|
package/CLAUDE.md
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
|
|
3
|
-
Default to using Bun instead of Node.js.
|
|
4
|
-
|
|
5
|
-
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
|
6
|
-
- Use `bun test` instead of `jest` or `vitest`
|
|
7
|
-
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
|
8
|
-
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
|
9
|
-
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
|
10
|
-
- Bun automatically loads .env, so don't use dotenv.
|
|
11
|
-
|
|
12
|
-
## APIs
|
|
13
|
-
|
|
14
|
-
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
|
15
|
-
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
|
16
|
-
- `Bun.redis` for Redis. Don't use `ioredis`.
|
|
17
|
-
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
|
18
|
-
- `WebSocket` is built-in. Don't use `ws`.
|
|
19
|
-
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
|
20
|
-
- Bun.$`ls` instead of execa.
|
|
21
|
-
|
|
22
|
-
## Testing
|
|
23
|
-
|
|
24
|
-
Use `bun test` to run tests.
|
|
25
|
-
|
|
26
|
-
```ts#index.test.ts
|
|
27
|
-
import { test, expect } from "bun:test";
|
|
28
|
-
|
|
29
|
-
test("hello world", () => {
|
|
30
|
-
expect(1).toBe(1);
|
|
31
|
-
});
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Frontend
|
|
35
|
-
|
|
36
|
-
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
|
37
|
-
|
|
38
|
-
Server:
|
|
39
|
-
|
|
40
|
-
```ts#index.ts
|
|
41
|
-
import index from "./index.html"
|
|
42
|
-
|
|
43
|
-
Bun.serve({
|
|
44
|
-
routes: {
|
|
45
|
-
"/": index,
|
|
46
|
-
"/api/users/:id": {
|
|
47
|
-
GET: (req) => {
|
|
48
|
-
return new Response(JSON.stringify({ id: req.params.id }));
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
// optional websocket support
|
|
53
|
-
websocket: {
|
|
54
|
-
open: (ws) => {
|
|
55
|
-
ws.send("Hello, world!");
|
|
56
|
-
},
|
|
57
|
-
message: (ws, message) => {
|
|
58
|
-
ws.send(message);
|
|
59
|
-
},
|
|
60
|
-
close: (ws) => {
|
|
61
|
-
// handle close
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
development: {
|
|
65
|
-
hmr: true,
|
|
66
|
-
console: true,
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
|
72
|
-
|
|
73
|
-
```html#index.html
|
|
74
|
-
<html>
|
|
75
|
-
<body>
|
|
76
|
-
<h1>Hello, world!</h1>
|
|
77
|
-
<script type="module" src="./frontend.tsx"></script>
|
|
78
|
-
</body>
|
|
79
|
-
</html>
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
With the following `frontend.tsx`:
|
|
83
|
-
|
|
84
|
-
```tsx#frontend.tsx
|
|
85
|
-
import React from "react";
|
|
86
|
-
|
|
87
|
-
// import .css files directly and it works
|
|
88
|
-
import './index.css';
|
|
89
|
-
|
|
90
|
-
import { createRoot } from "react-dom/client";
|
|
91
|
-
|
|
92
|
-
const root = createRoot(document.body);
|
|
93
|
-
|
|
94
|
-
export default function Frontend() {
|
|
95
|
-
return <h1>Hello, world!</h1>;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
root.render(<Frontend />);
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Then, run index.ts
|
|
102
|
-
|
|
103
|
-
```sh
|
|
104
|
-
bun --hot ./index.ts
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
|
package/bun.lock
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"lockfileVersion": 1,
|
|
3
|
-
"workspaces": {
|
|
4
|
-
"": {
|
|
5
|
-
"name": "mk",
|
|
6
|
-
"devDependencies": {
|
|
7
|
-
"@types/bun": "latest",
|
|
8
|
-
},
|
|
9
|
-
"peerDependencies": {
|
|
10
|
-
"typescript": "^5",
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
"packages": {
|
|
15
|
-
"@types/bun": ["@types/bun@1.2.20", "", { "dependencies": { "bun-types": "1.2.20" } }, "sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA=="],
|
|
16
|
-
|
|
17
|
-
"@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="],
|
|
18
|
-
|
|
19
|
-
"@types/react": ["@types/react@19.1.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg=="],
|
|
20
|
-
|
|
21
|
-
"bun-types": ["bun-types@1.2.20", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-pxTnQYOrKvdOwyiyd/7sMt9yFOenN004Y6O4lCcCUoKVej48FS5cvTw9geRaEcB9TsDZaJKAxPTVvi8tFsVuXA=="],
|
|
22
|
-
|
|
23
|
-
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
|
24
|
-
|
|
25
|
-
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
|
26
|
-
|
|
27
|
-
"undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
|
|
28
|
-
}
|
|
29
|
-
}
|
package/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
console.log("hello ccusage");
|
package/tsconfig.json
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
// Environment setup & latest features
|
|
4
|
-
"lib": ["ESNext"],
|
|
5
|
-
"target": "ESNext",
|
|
6
|
-
"module": "Preserve",
|
|
7
|
-
"moduleDetection": "force",
|
|
8
|
-
"jsx": "react-jsx",
|
|
9
|
-
"allowJs": true,
|
|
10
|
-
|
|
11
|
-
// Bundler mode
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"verbatimModuleSyntax": true,
|
|
15
|
-
"noEmit": true,
|
|
16
|
-
|
|
17
|
-
// Best practices
|
|
18
|
-
"strict": true,
|
|
19
|
-
"skipLibCheck": true,
|
|
20
|
-
"noFallthroughCasesInSwitch": true,
|
|
21
|
-
"noUncheckedIndexedAccess": true,
|
|
22
|
-
"noImplicitOverride": true,
|
|
23
|
-
|
|
24
|
-
// Some stricter flags (disabled by default)
|
|
25
|
-
"noUnusedLocals": false,
|
|
26
|
-
"noUnusedParameters": false,
|
|
27
|
-
"noPropertyAccessFromIndexSignature": false
|
|
28
|
-
}
|
|
29
|
-
}
|