@cartanova/qgrid-cli 1.0.1 → 1.0.2
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/bundle/src/application/qgrid/oauth.ts +181 -0
- package/bundle/src/application/qgrid/pool.ts +132 -0
- package/bundle/src/application/qgrid/qgrid.frame.ts +275 -0
- package/bundle/src/application/qgrid/qgrid.types.ts +117 -0
- package/bundle/src/application/qgrid/worker.ts +234 -0
- package/bundle/src/application/queries.generated.ts +59 -0
- package/bundle/src/application/request-log/request-log.model.ts +155 -0
- package/bundle/src/application/request-log/request-log.types.ts +15 -0
- package/bundle/src/application/sonamu.generated.http +187 -0
- package/bundle/src/application/sonamu.generated.sso.ts +66 -0
- package/bundle/src/application/sonamu.generated.ts +135 -0
- package/bundle/src/application/token/token.model.ts +160 -0
- package/bundle/src/application/token/token.types.ts +26 -0
- package/bundle/src/i18n/en.ts +59 -0
- package/bundle/src/i18n/ko.ts +59 -0
- package/bundle/src/i18n/sd.generated.ts +384 -0
- package/bundle/src/index.ts +6 -0
- package/bundle/src/sonamu.config.ts +248 -0
- package/bundle/src/testing/fixture.ts +6 -0
- package/bundle/src/testing/global.ts +6 -0
- package/bundle/src/testing/setup-mocks.ts +44 -0
- package/bundle/src/typings/fastify.d.ts +8 -0
- package/bundle/src/typings/sonamu.d.ts +17 -0
- package/bundle/src/utils/subset-loaders.ts +11 -0
- package/package.json +2 -2
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker — Claude CLI stream-json 프로세스 하나를 관리.
|
|
3
|
+
*
|
|
4
|
+
* 기존 SocratsAI ClaudeCliService에서 추출 + 리팩터:
|
|
5
|
+
* - 토큰/모델을 생성자에서 주입
|
|
6
|
+
* - env allowlist (PATH, HOME, TMPDIR, CLAUDE_CODE_OAUTH_TOKEN만)
|
|
7
|
+
* - callCount 추적 + maxCalls 도달 시 프로세스 재활용
|
|
8
|
+
* - QuotaError/TimeoutError/ProcessError 구분
|
|
9
|
+
*/
|
|
10
|
+
import { type ChildProcess, spawn } from "node:child_process";
|
|
11
|
+
import { mkdirSync } from "node:fs";
|
|
12
|
+
|
|
13
|
+
import type { CliResult, QueryInput } from "./qgrid.types";
|
|
14
|
+
import { maskToken, ProcessError, QuotaError, TimeoutError } from "./qgrid.types";
|
|
15
|
+
|
|
16
|
+
type PendingRequest = {
|
|
17
|
+
resolve: (value: CliResult) => void;
|
|
18
|
+
reject: (reason: Error) => void;
|
|
19
|
+
timer: ReturnType<typeof setTimeout>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type WorkerConfig = {
|
|
23
|
+
token: string;
|
|
24
|
+
model: string;
|
|
25
|
+
timeout: number;
|
|
26
|
+
cwd: string;
|
|
27
|
+
maxCalls: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export class Worker {
|
|
31
|
+
tokenId: string;
|
|
32
|
+
config: WorkerConfig;
|
|
33
|
+
process: ChildProcess | null = null;
|
|
34
|
+
buffer = "";
|
|
35
|
+
pending: PendingRequest | null = null;
|
|
36
|
+
sessionId: string | null = null;
|
|
37
|
+
queue: Array<() => void> = [];
|
|
38
|
+
cwdCreated = false;
|
|
39
|
+
callCount = 0;
|
|
40
|
+
needsRecycle = false;
|
|
41
|
+
|
|
42
|
+
constructor(config: WorkerConfig) {
|
|
43
|
+
this.config = config;
|
|
44
|
+
this.tokenId = config.token;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getQueueDepth(): number {
|
|
48
|
+
return this.queue.length + (this.pending ? 1 : 0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
rejectPending(err: Error): void {
|
|
52
|
+
if (!this.pending) return;
|
|
53
|
+
this.pending.reject(err);
|
|
54
|
+
clearTimeout(this.pending.timer);
|
|
55
|
+
this.pending = null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
ensureProcess(): ChildProcess {
|
|
59
|
+
if (this.process && this.process.exitCode === null && !this.needsRecycle) {
|
|
60
|
+
return this.process;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this.process && this.process.exitCode === null) {
|
|
64
|
+
this.process.kill();
|
|
65
|
+
this.process = null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const env: NodeJS.ProcessEnv = {
|
|
69
|
+
PATH: process.env.PATH,
|
|
70
|
+
HOME: process.env.HOME,
|
|
71
|
+
TMPDIR: process.env.TMPDIR,
|
|
72
|
+
CLAUDE_CODE_OAUTH_TOKEN: this.config.token,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const { cwd } = this.config;
|
|
76
|
+
if (!this.cwdCreated) {
|
|
77
|
+
mkdirSync(cwd, { recursive: true });
|
|
78
|
+
this.cwdCreated = true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const child = spawn(
|
|
82
|
+
"claude",
|
|
83
|
+
[
|
|
84
|
+
"-p",
|
|
85
|
+
"--input-format",
|
|
86
|
+
"stream-json",
|
|
87
|
+
"--output-format",
|
|
88
|
+
"stream-json",
|
|
89
|
+
"--verbose",
|
|
90
|
+
"--max-turns",
|
|
91
|
+
"1",
|
|
92
|
+
"--model",
|
|
93
|
+
this.config.model,
|
|
94
|
+
],
|
|
95
|
+
{ stdio: ["pipe", "pipe", "ignore"], env, cwd },
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
child.stdout?.on("data", (d: Buffer) => {
|
|
99
|
+
this.buffer += d.toString();
|
|
100
|
+
this.processBuffer();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
child.on("close", () => {
|
|
104
|
+
this.process = null;
|
|
105
|
+
this.rejectPending(
|
|
106
|
+
new ProcessError(`CLI process closed (token: ${maskToken(this.tokenId)})`),
|
|
107
|
+
);
|
|
108
|
+
this.drainQueue();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
child.on("error", (err) => {
|
|
112
|
+
this.process = null;
|
|
113
|
+
this.rejectPending(
|
|
114
|
+
new ProcessError(`CLI process error: ${err.message} (token: ${maskToken(this.tokenId)})`),
|
|
115
|
+
);
|
|
116
|
+
this.drainQueue();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
this.process = child;
|
|
120
|
+
this.buffer = "";
|
|
121
|
+
this.sessionId = null;
|
|
122
|
+
this.callCount = 0;
|
|
123
|
+
this.needsRecycle = false;
|
|
124
|
+
return child;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
processBuffer(): void {
|
|
128
|
+
const lines = this.buffer.split("\n");
|
|
129
|
+
this.buffer = lines.pop() ?? "";
|
|
130
|
+
|
|
131
|
+
lines
|
|
132
|
+
.filter((line) => line.trim() !== "")
|
|
133
|
+
.forEach((line) => {
|
|
134
|
+
try {
|
|
135
|
+
const j = JSON.parse(line);
|
|
136
|
+
|
|
137
|
+
if (j.type === "system" && j.subtype === "init" && j.session_id) {
|
|
138
|
+
this.sessionId = j.session_id;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (j.type === "result" && this.pending) {
|
|
142
|
+
let text: string = j.result ?? "";
|
|
143
|
+
text = text.replace(/^```(?:json)?\s*\n?/i, "").replace(/\n?```\s*$/i, "");
|
|
144
|
+
|
|
145
|
+
if (text.startsWith("You've hit")) {
|
|
146
|
+
this.pending.reject(
|
|
147
|
+
new QuotaError(`Quota exhausted (token: ${maskToken(this.tokenId)})`),
|
|
148
|
+
);
|
|
149
|
+
clearTimeout(this.pending.timer);
|
|
150
|
+
this.pending = null;
|
|
151
|
+
this.drainQueue();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const u = j.usage ?? {};
|
|
156
|
+
this.pending.resolve({
|
|
157
|
+
text,
|
|
158
|
+
usage: {
|
|
159
|
+
input_tokens: u.input_tokens ?? 0,
|
|
160
|
+
output_tokens: u.output_tokens ?? 0,
|
|
161
|
+
cache_creation_input_tokens: u.cache_creation_input_tokens ?? 0,
|
|
162
|
+
cache_read_input_tokens: u.cache_read_input_tokens ?? 0,
|
|
163
|
+
},
|
|
164
|
+
durationMs: j.duration_ms ?? 0,
|
|
165
|
+
costUsd: j.total_cost_usd ?? 0,
|
|
166
|
+
});
|
|
167
|
+
clearTimeout(this.pending.timer);
|
|
168
|
+
this.pending = null;
|
|
169
|
+
this.drainQueue();
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
// skip non-json lines
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
drainQueue(): void {
|
|
178
|
+
if (this.pending) return;
|
|
179
|
+
const next = this.queue.shift();
|
|
180
|
+
if (next) next();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async query(input: QueryInput, timeoutMs?: number): Promise<CliResult> {
|
|
184
|
+
if (this.pending) {
|
|
185
|
+
await new Promise<void>((resolve) => {
|
|
186
|
+
this.queue.push(resolve);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const child = this.ensureProcess();
|
|
191
|
+
const timeout = timeoutMs ?? this.config.timeout;
|
|
192
|
+
|
|
193
|
+
this.callCount++;
|
|
194
|
+
if (this.callCount >= this.config.maxCalls) {
|
|
195
|
+
this.needsRecycle = true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const prompt = input.system ? `${input.system}\n\n${input.prompt}` : input.prompt;
|
|
199
|
+
|
|
200
|
+
return new Promise<CliResult>((resolve, reject) => {
|
|
201
|
+
const timer = setTimeout(() => {
|
|
202
|
+
this.pending = null;
|
|
203
|
+
reject(
|
|
204
|
+
new TimeoutError(`Timeout after ${timeout / 1000}s (token: ${maskToken(this.tokenId)})`),
|
|
205
|
+
);
|
|
206
|
+
this.kill();
|
|
207
|
+
}, timeout);
|
|
208
|
+
|
|
209
|
+
this.pending = { resolve, reject, timer };
|
|
210
|
+
|
|
211
|
+
const msg = JSON.stringify({
|
|
212
|
+
type: "user",
|
|
213
|
+
message: { role: "user", content: prompt },
|
|
214
|
+
session_id: this.sessionId ?? "default",
|
|
215
|
+
parent_tool_use_id: null,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
child.stdin?.write(`${msg}\n`);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
kill(): void {
|
|
223
|
+
if (this.process && this.process.exitCode === null) {
|
|
224
|
+
this.process.kill();
|
|
225
|
+
}
|
|
226
|
+
this.process = null;
|
|
227
|
+
this.buffer = "";
|
|
228
|
+
this.sessionId = null;
|
|
229
|
+
this.callCount = 0;
|
|
230
|
+
this.needsRecycle = false;
|
|
231
|
+
this.rejectPending(new ProcessError(`Worker killed (token: ${maskToken(this.tokenId)})`));
|
|
232
|
+
this.drainQueue();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @generated
|
|
3
|
+
* 직접 수정하지 마세요.
|
|
4
|
+
*/
|
|
5
|
+
/* oxlint-disable */
|
|
6
|
+
|
|
7
|
+
import type { SSRQuery } from "sonamu/ssr";
|
|
8
|
+
|
|
9
|
+
// SSRQuery 헬퍼 함수
|
|
10
|
+
function createSSRQuery(
|
|
11
|
+
modelName: string,
|
|
12
|
+
methodName: string,
|
|
13
|
+
params: any[],
|
|
14
|
+
serviceKey: [string, string],
|
|
15
|
+
): SSRQuery {
|
|
16
|
+
return { modelName, methodName, params, serviceKey, __brand: "SSRQuery" } as SSRQuery;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
import { RequestLogListParams } from "./request-log/request-log.types";
|
|
20
|
+
import { TokenSubsetKey, RequestLogSubsetKey } from "./sonamu.generated";
|
|
21
|
+
import { TokenListParams } from "./token/token.types";
|
|
22
|
+
|
|
23
|
+
export namespace TokenService {
|
|
24
|
+
export const getToken = <T extends TokenSubsetKey>(subset: T, id: number): SSRQuery =>
|
|
25
|
+
createSSRQuery("TokenModel", "findById", [subset, id], ["Token", "getToken"]);
|
|
26
|
+
|
|
27
|
+
export const getTokens = <T extends TokenSubsetKey, LP extends TokenListParams>(
|
|
28
|
+
subset: T,
|
|
29
|
+
rawParams?: LP,
|
|
30
|
+
): SSRQuery =>
|
|
31
|
+
createSSRQuery("TokenModel", "findMany", [subset, rawParams], ["Token", "getTokens"]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export namespace RequestLogService {
|
|
35
|
+
export const getRequestLog = <T extends RequestLogSubsetKey>(subset: T, id: number): SSRQuery =>
|
|
36
|
+
createSSRQuery("RequestLogModel", "findById", [subset, id], ["RequestLog", "getRequestLog"]);
|
|
37
|
+
|
|
38
|
+
export const getRequestLogs = <T extends RequestLogSubsetKey, LP extends RequestLogListParams>(
|
|
39
|
+
subset: T,
|
|
40
|
+
rawParams?: LP,
|
|
41
|
+
): SSRQuery =>
|
|
42
|
+
createSSRQuery(
|
|
43
|
+
"RequestLogModel",
|
|
44
|
+
"findMany",
|
|
45
|
+
[subset, rawParams],
|
|
46
|
+
["RequestLog", "getRequestLogs"],
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export namespace QgridService {
|
|
51
|
+
export const stats = (): SSRQuery =>
|
|
52
|
+
createSSRQuery("QgridFrame", "stats", [], ["Qgrid", "stats"]);
|
|
53
|
+
|
|
54
|
+
export const usage = (tokenName?: string): SSRQuery =>
|
|
55
|
+
createSSRQuery("QgridFrame", "usage", [tokenName], ["Qgrid", "usage"]);
|
|
56
|
+
|
|
57
|
+
export const health = (): SSRQuery =>
|
|
58
|
+
createSSRQuery("QgridFrame", "health", [], ["Qgrid", "health"]);
|
|
59
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
api,
|
|
3
|
+
asArray,
|
|
4
|
+
BadRequestException,
|
|
5
|
+
BaseModelClass,
|
|
6
|
+
exhaustive,
|
|
7
|
+
type ListResult,
|
|
8
|
+
NotFoundException,
|
|
9
|
+
} from "sonamu";
|
|
10
|
+
|
|
11
|
+
import { SD } from "../../i18n/sd.generated";
|
|
12
|
+
import type { RequestLogSubsetKey, RequestLogSubsetMapping } from "../sonamu.generated";
|
|
13
|
+
import { requestLogLoaderQueries, requestLogSubsetQueries } from "../sonamu.generated.sso";
|
|
14
|
+
import type { RequestLogListParams, RequestLogSaveParams } from "./request-log.types";
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
RequestLog Model
|
|
18
|
+
*/
|
|
19
|
+
class RequestLogModelClass extends BaseModelClass<
|
|
20
|
+
RequestLogSubsetKey,
|
|
21
|
+
RequestLogSubsetMapping,
|
|
22
|
+
typeof requestLogSubsetQueries,
|
|
23
|
+
typeof requestLogLoaderQueries
|
|
24
|
+
> {
|
|
25
|
+
constructor() {
|
|
26
|
+
super("RequestLog", requestLogSubsetQueries, requestLogLoaderQueries);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@api({ httpMethod: "GET", clients: ["axios", "tanstack-query"], resourceName: "RequestLog" })
|
|
30
|
+
async findById<T extends RequestLogSubsetKey>(
|
|
31
|
+
subset: T,
|
|
32
|
+
id: number,
|
|
33
|
+
): Promise<RequestLogSubsetMapping[T]> {
|
|
34
|
+
const { rows } = await this.findMany(subset, {
|
|
35
|
+
id,
|
|
36
|
+
num: 1,
|
|
37
|
+
page: 1,
|
|
38
|
+
});
|
|
39
|
+
if (!rows[0]) {
|
|
40
|
+
throw new NotFoundException(SD("error.entityNotFound")("RequestLog", id));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return rows[0];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async findOne<T extends RequestLogSubsetKey>(
|
|
47
|
+
subset: T,
|
|
48
|
+
listParams: RequestLogListParams,
|
|
49
|
+
): Promise<RequestLogSubsetMapping[T] | null> {
|
|
50
|
+
const { rows } = await this.findMany(subset, {
|
|
51
|
+
...listParams,
|
|
52
|
+
num: 1,
|
|
53
|
+
page: 1,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return rows[0] ?? null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@api({ httpMethod: "GET", clients: ["axios", "tanstack-query"], resourceName: "RequestLogs" })
|
|
60
|
+
async findMany<T extends RequestLogSubsetKey, LP extends RequestLogListParams>(
|
|
61
|
+
subset: T,
|
|
62
|
+
rawParams?: LP,
|
|
63
|
+
): Promise<ListResult<LP, RequestLogSubsetMapping[T]>> {
|
|
64
|
+
// params with defaults
|
|
65
|
+
const params = {
|
|
66
|
+
num: 24,
|
|
67
|
+
page: 1,
|
|
68
|
+
search: "id" as const,
|
|
69
|
+
orderBy: "id-desc" as const,
|
|
70
|
+
...rawParams,
|
|
71
|
+
} satisfies RequestLogListParams;
|
|
72
|
+
|
|
73
|
+
// build queries
|
|
74
|
+
const { qb, onSubset: _ } = this.getSubsetQueries(subset);
|
|
75
|
+
|
|
76
|
+
// id
|
|
77
|
+
if (params.id) {
|
|
78
|
+
qb.whereIn("request_logs.id", asArray(params.id));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// token_name filter (toFilter)
|
|
82
|
+
if (params.token_name) {
|
|
83
|
+
qb.where("request_logs.token_name", params.token_name);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// search-keyword
|
|
87
|
+
if (params.search && params.keyword && params.keyword.length > 0) {
|
|
88
|
+
if (params.search === "id") {
|
|
89
|
+
qb.where("request_logs.id", Number(params.keyword));
|
|
90
|
+
} else if (params.search === "token_name") {
|
|
91
|
+
qb.where("request_logs.token_name", "like", `%${params.keyword}%`);
|
|
92
|
+
} else if (params.search === "query") {
|
|
93
|
+
qb.where("request_logs.query", "like", `%${params.keyword}%`);
|
|
94
|
+
} else {
|
|
95
|
+
throw new BadRequestException(SD("error.unknownSearchField")(params.search));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// orderBy
|
|
100
|
+
if (params.orderBy) {
|
|
101
|
+
// default orderBy
|
|
102
|
+
if (params.orderBy === "id-desc") {
|
|
103
|
+
qb.orderBy("request_logs.id", "desc");
|
|
104
|
+
} else {
|
|
105
|
+
exhaustive(params.orderBy);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const enhancers = this.createEnhancers({
|
|
110
|
+
A: (row) => ({
|
|
111
|
+
...row,
|
|
112
|
+
// 서브셋별로 virtual 필드 계산로직 추가
|
|
113
|
+
}),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return this.executeSubsetQuery({
|
|
117
|
+
subset,
|
|
118
|
+
qb,
|
|
119
|
+
params,
|
|
120
|
+
enhancers,
|
|
121
|
+
debug: false,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@api({ httpMethod: "POST", clients: ["axios", "tanstack-mutation"] })
|
|
126
|
+
async save(spa: RequestLogSaveParams[]): Promise<number[]> {
|
|
127
|
+
const wdb = this.getPuri("w");
|
|
128
|
+
|
|
129
|
+
// register
|
|
130
|
+
spa.forEach((sp) => {
|
|
131
|
+
wdb.ubRegister("request_logs", sp);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// transaction
|
|
135
|
+
return wdb.transaction(async (trx) => {
|
|
136
|
+
const ids = await trx.ubUpsert("request_logs");
|
|
137
|
+
|
|
138
|
+
return ids;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@api({ httpMethod: "POST", clients: ["axios", "tanstack-mutation"] })
|
|
143
|
+
async del(ids: number[]): Promise<number> {
|
|
144
|
+
const wdb = this.getPuri("w");
|
|
145
|
+
|
|
146
|
+
// transaction
|
|
147
|
+
await wdb.transaction(async (trx) => {
|
|
148
|
+
return trx.table("request_logs").whereIn("request_logs.id", ids).delete();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return ids.length;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const RequestLogModel = new RequestLogModelClass();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { RequestLogBaseListParams, RequestLogBaseSchema } from "../sonamu.generated";
|
|
4
|
+
|
|
5
|
+
// RequestLog - ListParams
|
|
6
|
+
export const RequestLogListParams = RequestLogBaseListParams;
|
|
7
|
+
export type RequestLogListParams = z.infer<typeof RequestLogListParams>;
|
|
8
|
+
|
|
9
|
+
// RequestLog - SaveParams
|
|
10
|
+
export const RequestLogSaveParams = RequestLogBaseSchema.partial({
|
|
11
|
+
id: true,
|
|
12
|
+
created_at: true,
|
|
13
|
+
token_name: true,
|
|
14
|
+
});
|
|
15
|
+
export type RequestLogSaveParams = z.infer<typeof RequestLogSaveParams>;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# @generated
|
|
2
|
+
# 직접 수정하지 마세요.
|
|
3
|
+
|
|
4
|
+
GET {{baseUrl}}/api/token/findById
|
|
5
|
+
?subset=A
|
|
6
|
+
&id=0
|
|
7
|
+
Content-Type: application/json
|
|
8
|
+
|
|
9
|
+
###
|
|
10
|
+
|
|
11
|
+
GET {{baseUrl}}/api/token/findMany
|
|
12
|
+
?subset=A
|
|
13
|
+
&rawParams[num]=24
|
|
14
|
+
&rawParams[page]=1
|
|
15
|
+
&rawParams[search]=id
|
|
16
|
+
&rawParams[keyword]=KEYWORD
|
|
17
|
+
&rawParams[orderBy]=id-desc
|
|
18
|
+
&rawParams[queryMode]=both
|
|
19
|
+
&rawParams[id]=0
|
|
20
|
+
&rawParams[sonamuFilter]=unknown-custom
|
|
21
|
+
&rawParams[token]=TOKEN
|
|
22
|
+
Content-Type: application/json
|
|
23
|
+
|
|
24
|
+
###
|
|
25
|
+
|
|
26
|
+
POST {{baseUrl}}/api/token/save
|
|
27
|
+
Content-Type: application/json
|
|
28
|
+
|
|
29
|
+
{
|
|
30
|
+
"spa": [
|
|
31
|
+
{
|
|
32
|
+
"id": 0,
|
|
33
|
+
"created_at": "2000-01-01",
|
|
34
|
+
"token": "TOKEN",
|
|
35
|
+
"name": "NAME",
|
|
36
|
+
"refresh_token": null,
|
|
37
|
+
"expires_at": null,
|
|
38
|
+
"account_uuid": null,
|
|
39
|
+
"active": false
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
###
|
|
45
|
+
|
|
46
|
+
POST {{baseUrl}}/api/token/del
|
|
47
|
+
Content-Type: application/json
|
|
48
|
+
|
|
49
|
+
{
|
|
50
|
+
"ids": [
|
|
51
|
+
0
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
###
|
|
56
|
+
|
|
57
|
+
GET {{baseUrl}}/api/requestLog/findById
|
|
58
|
+
?subset=A
|
|
59
|
+
&id=0
|
|
60
|
+
Content-Type: application/json
|
|
61
|
+
|
|
62
|
+
###
|
|
63
|
+
|
|
64
|
+
GET {{baseUrl}}/api/requestLog/findMany
|
|
65
|
+
?subset=A
|
|
66
|
+
&rawParams[num]=24
|
|
67
|
+
&rawParams[page]=1
|
|
68
|
+
&rawParams[search]=id
|
|
69
|
+
&rawParams[keyword]=KEYWORD
|
|
70
|
+
&rawParams[orderBy]=id-desc
|
|
71
|
+
&rawParams[queryMode]=both
|
|
72
|
+
&rawParams[id]=0
|
|
73
|
+
&rawParams[sonamuFilter]=unknown-custom
|
|
74
|
+
&rawParams[token_name]=TOKEN_NAME
|
|
75
|
+
Content-Type: application/json
|
|
76
|
+
|
|
77
|
+
###
|
|
78
|
+
|
|
79
|
+
POST {{baseUrl}}/api/requestLog/save
|
|
80
|
+
Content-Type: application/json
|
|
81
|
+
|
|
82
|
+
{
|
|
83
|
+
"spa": [
|
|
84
|
+
{
|
|
85
|
+
"id": 0,
|
|
86
|
+
"created_at": "2000-01-01",
|
|
87
|
+
"token_name": "TOKEN_NAME",
|
|
88
|
+
"query": "QUERY",
|
|
89
|
+
"response": "RESPONSE",
|
|
90
|
+
"input_tokens": 0,
|
|
91
|
+
"output_tokens": 0,
|
|
92
|
+
"cache_read_tokens": 0,
|
|
93
|
+
"cache_creation_tokens": 0,
|
|
94
|
+
"duration_ms": 0
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
###
|
|
100
|
+
|
|
101
|
+
POST {{baseUrl}}/api/requestLog/del
|
|
102
|
+
Content-Type: application/json
|
|
103
|
+
|
|
104
|
+
{
|
|
105
|
+
"ids": [
|
|
106
|
+
0
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
###
|
|
111
|
+
|
|
112
|
+
POST {{baseUrl}}/api/qgrid/query
|
|
113
|
+
Content-Type: application/json
|
|
114
|
+
|
|
115
|
+
{
|
|
116
|
+
"prompt": "PROMPT",
|
|
117
|
+
"system": "SYSTEM",
|
|
118
|
+
"timeout": 0
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
###
|
|
122
|
+
|
|
123
|
+
GET {{baseUrl}}/api/qgrid/stats
|
|
124
|
+
?
|
|
125
|
+
Content-Type: application/json
|
|
126
|
+
|
|
127
|
+
###
|
|
128
|
+
|
|
129
|
+
POST {{baseUrl}}/api/qgrid/addToken
|
|
130
|
+
Content-Type: application/json
|
|
131
|
+
|
|
132
|
+
{
|
|
133
|
+
"token": "TOKEN",
|
|
134
|
+
"name": "NAME",
|
|
135
|
+
"refreshToken": "REFRESHTOKEN"
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
###
|
|
139
|
+
|
|
140
|
+
POST {{baseUrl}}/api/qgrid/updateToken
|
|
141
|
+
Content-Type: application/json
|
|
142
|
+
|
|
143
|
+
{
|
|
144
|
+
"token": "TOKEN",
|
|
145
|
+
"name": "NAME",
|
|
146
|
+
"newToken": "NEWTOKEN",
|
|
147
|
+
"refreshToken": "REFRESHTOKEN"
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
###
|
|
151
|
+
|
|
152
|
+
POST {{baseUrl}}/api/qgrid/removeToken
|
|
153
|
+
Content-Type: application/json
|
|
154
|
+
|
|
155
|
+
{
|
|
156
|
+
"token": "TOKEN"
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
###
|
|
160
|
+
|
|
161
|
+
POST {{baseUrl}}/api/qgrid/toggleToken
|
|
162
|
+
Content-Type: application/json
|
|
163
|
+
|
|
164
|
+
{
|
|
165
|
+
"id": 0
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
###
|
|
169
|
+
|
|
170
|
+
POST {{baseUrl}}/api/qgrid/oauthStart
|
|
171
|
+
Content-Type: application/json
|
|
172
|
+
|
|
173
|
+
{
|
|
174
|
+
"name": "NAME"
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
###
|
|
178
|
+
|
|
179
|
+
GET {{baseUrl}}/api/qgrid/usage
|
|
180
|
+
?tokenName=TOKENNAME
|
|
181
|
+
Content-Type: application/json
|
|
182
|
+
|
|
183
|
+
###
|
|
184
|
+
|
|
185
|
+
GET {{baseUrl}}/api/qgrid/health
|
|
186
|
+
?
|
|
187
|
+
Content-Type: application/json
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @generated
|
|
3
|
+
* 직접 수정하지 마세요.
|
|
4
|
+
*/
|
|
5
|
+
import { PuriWrapper, DatabaseSchemaExtend, PuriLoaderQueries } from "sonamu";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
RequestLogSubsetKey,
|
|
9
|
+
TokenSubsetKey,
|
|
10
|
+
RequestLogBaseSchema,
|
|
11
|
+
TokenBaseSchema,
|
|
12
|
+
} from "./sonamu.generated";
|
|
13
|
+
|
|
14
|
+
// SubsetQuery: RequestLog
|
|
15
|
+
export const requestLogSubsetQueries = {
|
|
16
|
+
A: (qbWrapper: PuriWrapper<DatabaseSchemaExtend>) => {
|
|
17
|
+
return qbWrapper.from("request_logs").select({
|
|
18
|
+
id: "request_logs.id",
|
|
19
|
+
created_at: "request_logs.created_at",
|
|
20
|
+
token_name: "request_logs.token_name",
|
|
21
|
+
query: "request_logs.query",
|
|
22
|
+
response: "request_logs.response",
|
|
23
|
+
input_tokens: "request_logs.input_tokens",
|
|
24
|
+
output_tokens: "request_logs.output_tokens",
|
|
25
|
+
cache_read_tokens: "request_logs.cache_read_tokens",
|
|
26
|
+
cache_creation_tokens: "request_logs.cache_creation_tokens",
|
|
27
|
+
duration_ms: "request_logs.duration_ms",
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// LoaderQuery: RequestLog
|
|
33
|
+
export const requestLogLoaderQueries = {
|
|
34
|
+
A: [],
|
|
35
|
+
} as const satisfies PuriLoaderQueries<RequestLogSubsetKey>;
|
|
36
|
+
|
|
37
|
+
// SubsetQuery: Token
|
|
38
|
+
export const tokenSubsetQueries = {
|
|
39
|
+
A: (qbWrapper: PuriWrapper<DatabaseSchemaExtend>) => {
|
|
40
|
+
return qbWrapper.from("tokens").select({
|
|
41
|
+
id: "tokens.id",
|
|
42
|
+
created_at: "tokens.created_at",
|
|
43
|
+
token: "tokens.token",
|
|
44
|
+
name: "tokens.name",
|
|
45
|
+
refresh_token: "tokens.refresh_token",
|
|
46
|
+
expires_at: "tokens.expires_at",
|
|
47
|
+
account_uuid: "tokens.account_uuid",
|
|
48
|
+
active: "tokens.active",
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// LoaderQuery: Token
|
|
54
|
+
export const tokenLoaderQueries = {
|
|
55
|
+
A: [],
|
|
56
|
+
} as const satisfies PuriLoaderQueries<TokenSubsetKey>;
|
|
57
|
+
|
|
58
|
+
// DatabaseSchema
|
|
59
|
+
declare module "sonamu" {
|
|
60
|
+
export interface DatabaseSchemaExtend {
|
|
61
|
+
request_logs: RequestLogBaseSchema;
|
|
62
|
+
tokens: TokenBaseSchema;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface DatabaseForeignKeys {}
|
|
66
|
+
}
|