Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

181 lines
5.3 KiB
JavaScript
Executable File

#!/usr/bin/env node
import { spawn } from "node:child_process";
import { createInterface } from "node:readline";
import { fileURLToPath } from "node:url";
const provider = option("--provider", "all");
const timeoutMs = Number(option("--timeout-ms", "30000"));
if (!new Set(["all", "codex", "claude"]).has(provider)) {
throw new Error("--provider must be all, codex, or claude");
}
const results = {};
if (provider !== "claude") results.codex = await safely(probeCodex);
if (provider !== "codex") results.claude = await safely(probeClaude);
process.stdout.write(`${JSON.stringify(results, null, 2)}\n`);
if (Object.values(results).some((result) => !result.ok)) process.exitCode = 1;
async function probeCodex() {
const child = spawn(
process.env.CODEX_EXECUTABLE || "codex",
["-c", 'service_tier="fast"', "app-server"],
{ cwd: process.cwd(), env: process.env, stdio: ["pipe", "pipe", "ignore"] }
);
const lines = createInterface({ input: child.stdout });
const pending = new Map();
let nextId = 1;
lines.on("line", (line) => {
let message;
try {
message = JSON.parse(line);
} catch {
return;
}
const waiter = pending.get(message.id);
if (!waiter) return;
pending.delete(message.id);
if (message.error)
waiter.reject(new Error("Codex JSON-RPC request failed"));
else waiter.resolve(message.result);
});
const call = (method, params) => {
const id = nextId++;
const response = new Promise((resolve, reject) => {
pending.set(id, { resolve, reject });
});
child.stdin.write(`${JSON.stringify({ id, method, params })}\n`);
return bounded(response, `${method} timed out`);
};
try {
await call("initialize", {
clientInfo: {
name: "tutti-provider-usage-smoke",
title: "Tutti Provider Usage Smoke",
version: "0.1.0"
},
capabilities: { experimentalApi: true }
});
child.stdin.write(`${JSON.stringify({ method: "initialized" })}\n`);
const response = await call("account/rateLimits/read", null);
const limits = object(response?.rateLimits) || object(response);
return {
protocol: "codex-app-server",
method: "account/rateLimits/read",
planType: scalar(limits?.planType),
primary: windowSummary(limits?.primary),
secondary: windowSummary(limits?.secondary)
};
} finally {
lines.close();
child.stdin.end();
setTimeout(() => child.kill("SIGTERM"), 500).unref();
}
}
async function probeClaude() {
const child = spawn(
process.execPath,
[
"--experimental-strip-types",
fileURLToPath(new URL("../src/main.ts", import.meta.url))
],
{ cwd: process.cwd(), env: process.env, stdio: ["pipe", "pipe", "ignore"] }
);
const lines = createInterface({ input: child.stdout });
const response = new Promise((resolve, reject) => {
lines.on("line", (line) => {
let event;
try {
event = JSON.parse(line);
} catch {
return;
}
if (event.id !== "usage-smoke") return;
if (event.type === "error")
reject(new Error("Claude usage probe failed"));
else resolve(event.payload);
});
child.once("error", reject);
});
child.stdin.end(
`${JSON.stringify({
version: 10,
id: "usage-smoke",
type: "probe_usage",
payload: { cwd: process.cwd(), env: {} }
})}\n`
);
try {
const usage = await bounded(response, "Claude SDK get_usage timed out");
return {
protocol: "claude-agent-sdk-sidecar",
method: "get_usage",
subscriptionType: scalar(usage?.subscriptionType),
rateLimitsAvailable: usage?.rateLimitsAvailable === true,
windows: Object.fromEntries(
["five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet"]
.filter((key) => usage?.rateLimits?.[key])
.map((key) => [key, windowSummary(usage.rateLimits[key])])
)
};
} finally {
lines.close();
child.kill("SIGTERM");
}
}
function windowSummary(value) {
const item = object(value);
if (!item) return null;
return {
utilization: number(item.utilization ?? item.usedPercent),
resetsAt: scalar(item.resets_at ?? item.resetsAt),
windowDurationMinutes: number(item.windowDurationMins)
};
}
async function safely(probe) {
const startedAt = Date.now();
try {
const result = await probe();
return { ok: true, durationMs: Date.now() - startedAt, ...result };
} catch (error) {
return {
ok: false,
durationMs: Date.now() - startedAt,
error:
error instanceof Error ? error.message.slice(0, 500) : "unknown error"
};
}
}
function bounded(promise, message) {
let timer;
return Promise.race([
promise,
new Promise((_, reject) => {
timer = setTimeout(() => reject(new Error(message)), timeoutMs);
})
]).finally(() => clearTimeout(timer));
}
function object(value) {
return value && typeof value === "object" && !Array.isArray(value)
? value
: null;
}
function scalar(value) {
return ["string", "number", "boolean"].includes(typeof value) ? value : null;
}
function number(value) {
return typeof value === "number" && Number.isFinite(value) ? value : null;
}
function option(name, fallback) {
const index = process.argv.indexOf(name);
return index >= 0 ? process.argv[index + 1] || fallback : fallback;
}