File size: 4,036 Bytes
1f21206 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | /**
* Status REST API
*
* GET /api/status โ ๅฅๅบทๆฃๆฅ
* GET /api/status/diagnostics โ ็ณป็ป่ฏๆญไฟกๆฏ
* GET /api/status/usage โ Token ็จ้๏ผๅฝๅไผ่ฏ็ดฏ่ฎก๏ผ
* GET /api/status/user โ ็จๆทไฟกๆฏ
*/
import * as os from 'os'
import * as path from 'path'
import * as fs from 'fs/promises'
import { ApiError, errorResponse } from '../middleware/errorHandler.js'
// ๆๅกๅจๅฏๅจๆถ้ด๏ผ็จไบ่ฎก็ฎ uptime๏ผ
const startedAt = Date.now()
// ไผ่ฏ็บงๅซ็ token ็จ้็ดฏ่ฎก๏ผ่ฟ็จ็ๅฝๅจๆๅ
๏ผ
const usage = {
totalInputTokens: 0,
totalOutputTokens: 0,
totalCost: 0,
}
/** ไพๅค้จ็ดฏๅ token ็จ้ */
export function addUsage(input: number, output: number, cost: number) {
usage.totalInputTokens += input
usage.totalOutputTokens += output
usage.totalCost += cost
}
/** ้็ฝฎ็จ้๏ผๆต่ฏ็จ๏ผ */
export function resetUsage() {
usage.totalInputTokens = 0
usage.totalOutputTokens = 0
usage.totalCost = 0
}
// โโโ Router โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
export async function handleStatusApi(
req: Request,
_url: URL,
segments: string[],
): Promise<Response> {
try {
if (req.method !== 'GET') {
throw new ApiError(405, `Method ${req.method} not allowed`, 'METHOD_NOT_ALLOWED')
}
const sub = segments[2] // 'diagnostics' | 'usage' | 'user' | undefined
switch (sub) {
case undefined:
return handleHealthCheck()
case 'diagnostics':
return handleDiagnostics()
case 'usage':
return handleUsage()
case 'user':
return await handleUser()
default:
throw ApiError.notFound(`Unknown status endpoint: ${sub}`)
}
} catch (error) {
return errorResponse(error)
}
}
// โโโ Handlers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function handleHealthCheck(): Response {
return Response.json({
status: 'ok',
version: getVersion(),
uptime: Date.now() - startedAt,
})
}
function handleDiagnostics(): Response {
return Response.json({
nodeVersion: process.version,
bunVersion: typeof Bun !== 'undefined' ? Bun.version : 'N/A',
platform: process.platform,
arch: process.arch,
configDir: getConfigDir(),
memory: {
rss: process.memoryUsage.rss(),
heapUsed: process.memoryUsage().heapUsed,
heapTotal: process.memoryUsage().heapTotal,
},
})
}
function handleUsage(): Response {
return Response.json({
totalInputTokens: usage.totalInputTokens,
totalOutputTokens: usage.totalOutputTokens,
totalCost: usage.totalCost,
})
}
async function handleUser(): Promise<Response> {
const configDir = getConfigDir()
const projects = await discoverProjects(configDir)
return Response.json({
configDir,
projects,
})
}
// โโโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function getConfigDir(): string {
return process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude')
}
function getVersion(): string {
// ไป package.json ็ version ๅญๆฎต่ฏปๅ๏ผๅ้ๅฐ็ฏๅขๅ้ๆ unknown
return process.env.APP_VERSION || '999.0.0-local'
}
/**
* ๆซๆ configDir ไธ็ projects ็ฎๅฝ๏ผ่ฟๅๅทฒ็ฅ็้กน็ฎ่ทฏๅพๅ่กจใ
* ๅฆๆ็ฎๅฝไธๅญๅจ๏ผ่ฟๅ็ฉบๆฐ็ปใ
*/
async function discoverProjects(configDir: string): Promise<string[]> {
const projectsDir = path.join(configDir, 'projects')
try {
const entries = await fs.readdir(projectsDir)
return entries.filter((e) => !e.startsWith('.'))
} catch {
return []
}
}
|