@kairyou/agent-tools 0.3.0 → 0.4.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 +56 -39
- package/README.zh-CN.md +54 -40
- package/dist/statusline/claude-statusline.mjs +355 -0
- package/dist/usage/cli.mjs +25 -0
- package/dist/usage/codex-hook.mjs +144 -0
- package/dist/usage/core.mjs +1054 -0
- package/dist/usage/opencode-plugin.mjs +80 -0
- package/dist/usage/opencode-tui.mjs +46 -0
- package/integrations/statusline/claude-statusline.mjs +32 -3
- package/integrations/usage/core.mjs +30 -1
- package/package.json +5 -5
- package/scripts/build.mjs +65 -0
- package/scripts/install.mjs +26 -15
- package/scripts/build-vision.mjs +0 -35
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// integrations/usage/cli.mjs
|
|
4
|
+
import { queryAgentProviderUsage } from "./core.mjs";
|
|
5
|
+
function parseAgent(argv) {
|
|
6
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
7
|
+
const arg = argv[index];
|
|
8
|
+
if (arg === "--agent" && argv[index + 1]) return argv[index + 1];
|
|
9
|
+
if (arg.startsWith("--agent=")) return arg.slice("--agent=".length);
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
var agent = parseAgent(process.argv.slice(2));
|
|
14
|
+
if (agent !== "claude" && agent !== "codex") {
|
|
15
|
+
process.stderr.write(agent ? `Unsupported agent: ${agent}
|
|
16
|
+
` : "Missing --agent <claude|codex>\n");
|
|
17
|
+
process.exitCode = 2;
|
|
18
|
+
} else {
|
|
19
|
+
try {
|
|
20
|
+
const result = await queryAgentProviderUsage(agent);
|
|
21
|
+
if (result?.text) process.stdout.write(`${result.text}
|
|
22
|
+
`);
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// integrations/usage/codex-hook.mjs
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
var SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
var AGENT_TOOLS_HOME = process.env.AGENT_TOOLS_HOME || path.resolve(SCRIPT_DIR, "..", "..");
|
|
11
|
+
var USAGE_SCRIPT = path.join(SCRIPT_DIR, "core.mjs");
|
|
12
|
+
var LOG_PATH = path.join(AGENT_TOOLS_HOME, "logs", "usage-hook.log");
|
|
13
|
+
var TIMEOUT_MS = Number(process.env.AGENT_TOOLS_USAGE_HOOK_TIMEOUT_MS || 4500);
|
|
14
|
+
var MAX_LOG_BYTES = Number(process.env.AGENT_TOOLS_USAGE_HOOK_LOG_BYTES || 256 * 1024);
|
|
15
|
+
var KEEP_LOG_BYTES = 128 * 1024;
|
|
16
|
+
function hookOut(message) {
|
|
17
|
+
const payload = { continue: true };
|
|
18
|
+
if (message) payload.systemMessage = message;
|
|
19
|
+
process.stdout.write(`${JSON.stringify(payload)}
|
|
20
|
+
`);
|
|
21
|
+
}
|
|
22
|
+
function preview(text) {
|
|
23
|
+
return String(text || "").replace(/\s+/g, " ").trim().slice(0, 500);
|
|
24
|
+
}
|
|
25
|
+
function logFailure(event) {
|
|
26
|
+
try {
|
|
27
|
+
fs.mkdirSync(path.dirname(LOG_PATH), { recursive: true });
|
|
28
|
+
rotateLogIfNeeded();
|
|
29
|
+
fs.appendFileSync(
|
|
30
|
+
LOG_PATH,
|
|
31
|
+
`${JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), ...event })}
|
|
32
|
+
`
|
|
33
|
+
);
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function rotateLogIfNeeded() {
|
|
38
|
+
const maxBytes = Number.isFinite(MAX_LOG_BYTES) && MAX_LOG_BYTES > 0 ? MAX_LOG_BYTES : 256 * 1024;
|
|
39
|
+
if (!fs.existsSync(LOG_PATH)) return;
|
|
40
|
+
const stat = fs.statSync(LOG_PATH);
|
|
41
|
+
if (stat.size <= maxBytes) return;
|
|
42
|
+
const keepBytes = Math.min(KEEP_LOG_BYTES, Math.floor(maxBytes / 2));
|
|
43
|
+
const fd = fs.openSync(LOG_PATH, "r");
|
|
44
|
+
try {
|
|
45
|
+
const buffer = Buffer.alloc(keepBytes);
|
|
46
|
+
fs.readSync(fd, buffer, 0, keepBytes, Math.max(0, stat.size - keepBytes));
|
|
47
|
+
fs.writeFileSync(
|
|
48
|
+
LOG_PATH,
|
|
49
|
+
`${JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), reason: "log rotated", previousBytes: stat.size })}
|
|
50
|
+
` + buffer.toString("utf8").replace(/^[^\n]*\n?/, "")
|
|
51
|
+
);
|
|
52
|
+
} finally {
|
|
53
|
+
fs.closeSync(fd);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function failureMessage() {
|
|
57
|
+
return `API usage hook failed; see ${LOG_PATH.replace(/\\/g, "/")}`;
|
|
58
|
+
}
|
|
59
|
+
function parseHookJson(stdout) {
|
|
60
|
+
const text = stdout.trim();
|
|
61
|
+
if (!text) return { continue: true };
|
|
62
|
+
return JSON.parse(text);
|
|
63
|
+
}
|
|
64
|
+
async function runUsageScript() {
|
|
65
|
+
if (!fs.existsSync(USAGE_SCRIPT)) {
|
|
66
|
+
logFailure({ reason: "missing usage script", usageScript: USAGE_SCRIPT });
|
|
67
|
+
hookOut(failureMessage());
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const result = await new Promise((resolve) => {
|
|
71
|
+
const child = spawn(process.execPath, [USAGE_SCRIPT, "hook", "--agent", "codex"], {
|
|
72
|
+
cwd: process.cwd(),
|
|
73
|
+
env: process.env,
|
|
74
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
75
|
+
windowsHide: true
|
|
76
|
+
});
|
|
77
|
+
let stdout = "";
|
|
78
|
+
let stderr = "";
|
|
79
|
+
let settled = false;
|
|
80
|
+
const timeout = setTimeout(() => {
|
|
81
|
+
if (settled) return;
|
|
82
|
+
settled = true;
|
|
83
|
+
child.kill();
|
|
84
|
+
resolve({ status: null, signal: "timeout", stdout, stderr });
|
|
85
|
+
}, Number.isFinite(TIMEOUT_MS) && TIMEOUT_MS > 0 ? TIMEOUT_MS : 4500);
|
|
86
|
+
child.stdout.on("data", (chunk) => {
|
|
87
|
+
stdout += chunk;
|
|
88
|
+
});
|
|
89
|
+
child.stderr.on("data", (chunk) => {
|
|
90
|
+
stderr += chunk;
|
|
91
|
+
});
|
|
92
|
+
child.on("error", (error) => {
|
|
93
|
+
if (settled) return;
|
|
94
|
+
settled = true;
|
|
95
|
+
clearTimeout(timeout);
|
|
96
|
+
resolve({ status: null, error, stdout, stderr });
|
|
97
|
+
});
|
|
98
|
+
child.on("exit", (status, signal) => {
|
|
99
|
+
if (settled) return;
|
|
100
|
+
settled = true;
|
|
101
|
+
clearTimeout(timeout);
|
|
102
|
+
resolve({ status, signal, stdout, stderr });
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
if (result.status !== 0) {
|
|
106
|
+
logFailure({
|
|
107
|
+
reason: "usage script exited non-zero",
|
|
108
|
+
status: result.status,
|
|
109
|
+
signal: result.signal || "",
|
|
110
|
+
error: result.error?.message || "",
|
|
111
|
+
stdout: preview(result.stdout),
|
|
112
|
+
stderr: preview(result.stderr),
|
|
113
|
+
usageScript: USAGE_SCRIPT,
|
|
114
|
+
node: process.version,
|
|
115
|
+
platform: `${process.platform} ${os.release()}`
|
|
116
|
+
});
|
|
117
|
+
hookOut(failureMessage());
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const payload = parseHookJson(result.stdout);
|
|
122
|
+
process.stdout.write(`${JSON.stringify(payload)}
|
|
123
|
+
`);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
logFailure({
|
|
126
|
+
reason: "usage script returned invalid hook JSON",
|
|
127
|
+
error: error.message,
|
|
128
|
+
stdout: preview(result.stdout),
|
|
129
|
+
stderr: preview(result.stderr),
|
|
130
|
+
usageScript: USAGE_SCRIPT
|
|
131
|
+
});
|
|
132
|
+
hookOut(failureMessage());
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
await runUsageScript();
|
|
137
|
+
} catch (error) {
|
|
138
|
+
logFailure({
|
|
139
|
+
reason: "wrapper exception",
|
|
140
|
+
error: error?.stack || error?.message || String(error),
|
|
141
|
+
usageScript: USAGE_SCRIPT
|
|
142
|
+
});
|
|
143
|
+
hookOut(failureMessage());
|
|
144
|
+
}
|