gencow 0.1.110 → 0.1.112
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/core/index.js +15 -10
- package/lib/__tests__/readme-codegen.test.ts +18 -2
- package/lib/readme-codegen.mjs +23 -9
- package/package.json +1 -1
- package/server/index.js +124 -13
- package/server/index.js.map +2 -2
- package/templates/ai-chat/ai.ts +76 -15
- package/templates/ai.ts +76 -15
- package/templates/fullstack/ai.ts +76 -15
package/core/index.js
CHANGED
|
@@ -2027,7 +2027,8 @@ function crud(table, options) {
|
|
|
2027
2027
|
const countResult = await db.select({ count: drizzleCount() }).from(anyTable).where(whereClause);
|
|
2028
2028
|
return { data, total: Number(countResult[0]?.count ?? 0) };
|
|
2029
2029
|
}
|
|
2030
|
-
const
|
|
2030
|
+
const enabledMethods = new Set(options?.methods ?? ["list", "get", "create", "update", "remove"]);
|
|
2031
|
+
const listDef = !enabledMethods.has("list") ? void 0 : query(`${prefix}.list`, {
|
|
2031
2032
|
public: isPublic,
|
|
2032
2033
|
args: {
|
|
2033
2034
|
page: v.optional(v.number()),
|
|
@@ -2058,7 +2059,7 @@ function crud(table, options) {
|
|
|
2058
2059
|
};
|
|
2059
2060
|
}
|
|
2060
2061
|
});
|
|
2061
|
-
const getDef = query(`${prefix}.get`, {
|
|
2062
|
+
const getDef = !enabledMethods.has("get") ? void 0 : query(`${prefix}.get`, {
|
|
2062
2063
|
public: isPublic,
|
|
2063
2064
|
args: { id: idValidator },
|
|
2064
2065
|
handler: async (ctx, args) => {
|
|
@@ -2072,7 +2073,7 @@ function crud(table, options) {
|
|
|
2072
2073
|
return result ?? null;
|
|
2073
2074
|
}
|
|
2074
2075
|
});
|
|
2075
|
-
const createDef = mutation(`${prefix}.create`, {
|
|
2076
|
+
const createDef = !enabledMethods.has("create") ? void 0 : mutation(`${prefix}.create`, {
|
|
2076
2077
|
public: isPublic,
|
|
2077
2078
|
invalidates: [],
|
|
2078
2079
|
handler: async (ctx, args) => {
|
|
@@ -2085,14 +2086,14 @@ function crud(table, options) {
|
|
|
2085
2086
|
insertData = await options.hooks.beforeCreate(insertData);
|
|
2086
2087
|
}
|
|
2087
2088
|
const [result] = await ctx.db.insert(anyTable).values(insertData).returning();
|
|
2088
|
-
if (useRealtime) {
|
|
2089
|
+
if (useRealtime && enabledMethods.has("list")) {
|
|
2089
2090
|
const listResult = await fetchListWithTotal(ctx.db);
|
|
2090
2091
|
ctx.realtime.emit(`${prefix}.list`, listResult);
|
|
2091
2092
|
}
|
|
2092
2093
|
return result;
|
|
2093
2094
|
}
|
|
2094
2095
|
});
|
|
2095
|
-
const updateDef = mutation(`${prefix}.update`, {
|
|
2096
|
+
const updateDef = !enabledMethods.has("update") ? void 0 : mutation(`${prefix}.update`, {
|
|
2096
2097
|
public: isPublic,
|
|
2097
2098
|
invalidates: [],
|
|
2098
2099
|
handler: async (ctx, args) => {
|
|
@@ -2107,14 +2108,18 @@ function crud(table, options) {
|
|
|
2107
2108
|
}
|
|
2108
2109
|
const [result] = await ctx.db.update(anyTable).set(updateData).where(eq(pk, id)).returning();
|
|
2109
2110
|
if (useRealtime) {
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2111
|
+
if (enabledMethods.has("list")) {
|
|
2112
|
+
const listResult = await fetchListWithTotal(ctx.db);
|
|
2113
|
+
ctx.realtime.emit(`${prefix}.list`, listResult);
|
|
2114
|
+
}
|
|
2115
|
+
if (enabledMethods.has("get")) {
|
|
2116
|
+
ctx.realtime.emit(`${prefix}.get`, result);
|
|
2117
|
+
}
|
|
2113
2118
|
}
|
|
2114
2119
|
return result;
|
|
2115
2120
|
}
|
|
2116
2121
|
});
|
|
2117
|
-
const removeDef = mutation(`${prefix}.remove`, {
|
|
2122
|
+
const removeDef = !enabledMethods.has("remove") ? void 0 : mutation(`${prefix}.remove`, {
|
|
2118
2123
|
public: isPublic,
|
|
2119
2124
|
invalidates: [],
|
|
2120
2125
|
handler: async (ctx, args) => {
|
|
@@ -2125,7 +2130,7 @@ function crud(table, options) {
|
|
|
2125
2130
|
} else {
|
|
2126
2131
|
await ctx.db.delete(anyTable).where(eq(pk, args.id));
|
|
2127
2132
|
}
|
|
2128
|
-
if (useRealtime) {
|
|
2133
|
+
if (useRealtime && enabledMethods.has("list")) {
|
|
2129
2134
|
const listResult = await fetchListWithTotal(ctx.db);
|
|
2130
2135
|
ctx.realtime.emit(`${prefix}.list`, listResult);
|
|
2131
2136
|
}
|
|
@@ -223,8 +223,24 @@ describe("buildAiPrompt — AI Vibe-Coding 프롬프트", () => {
|
|
|
223
223
|
|
|
224
224
|
it("mutation 제한 경고 포함", () => {
|
|
225
225
|
const md = buildAiPrompt(SIMPLE_API, ["tasks"]);
|
|
226
|
-
expect(md).toContain("mutation
|
|
227
|
-
expect(md).toContain("
|
|
226
|
+
expect(md).toContain("mutation: 30초");
|
|
227
|
+
expect(md).toContain("FUNCTION_TIMEOUT");
|
|
228
|
+
expect(md).toContain("별도 mutation으로 분리");
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("mutation 간 공유 로직 패턴 포함", () => {
|
|
232
|
+
const md = buildAiPrompt(SIMPLE_API, ["tasks"]);
|
|
233
|
+
expect(md).toContain("mutation 간 로직 공유");
|
|
234
|
+
expect(md).toContain("일반 async 함수로 분리");
|
|
235
|
+
expect(md).toContain("mutation은 래핑만");
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("AI 우회 금지 목록 포함", () => {
|
|
239
|
+
const md = buildAiPrompt(SIMPLE_API, ["tasks"]);
|
|
240
|
+
expect(md).toContain("절대 금지");
|
|
241
|
+
expect(md).toContain("openai-direct.ts");
|
|
242
|
+
expect(md).toContain("ai.ts 수정 또는 우회 금지");
|
|
243
|
+
expect(md).toContain("SDK 직접 설치/사용 금지");
|
|
228
244
|
});
|
|
229
245
|
|
|
230
246
|
it("크론 잡 사용법 포함", () => {
|
package/lib/readme-codegen.mjs
CHANGED
|
@@ -300,15 +300,23 @@ export function buildAiPrompt(apiObj, namespaces) {
|
|
|
300
300
|
md += `- gencow/index.ts의 re-export는 export * as moduleName from "./moduleName" 패턴을 써.\n`;
|
|
301
301
|
md += ` Module, Mod 같은 접미사를 붙이지 마.\n`;
|
|
302
302
|
md += `\n`;
|
|
303
|
-
md += `⚠️ mutation
|
|
304
|
-
md += `-
|
|
303
|
+
md += `⚠️ mutation 제한 (타임아웃):\n`;
|
|
304
|
+
md += `- query: 30초, mutation: 30초, cron: 10분. 이 시간을 초과하면 FUNCTION_TIMEOUT 에러.\n`;
|
|
305
305
|
md += `- ✅ 1순위: 각 단계를 별도 mutation으로 분리하고, 프론트엔드에서 순차 호출해.\n`;
|
|
306
306
|
md += ` 예: 크롤링(Step1) → 필터링(Step2) → 요약(Step3) 각각 별도 mutation으로 분리.\n`;
|
|
307
307
|
md += ` 프론트엔드: await crawl(); await filter(); await summarize();\n`;
|
|
308
|
-
md += `- ⚠️ 보조: ctx.scheduler.runAfter(0, "module.nextStep", { sessionId }) — 로컬 dev에서만 안정적. 클라우드에서는
|
|
309
|
-
md +=
|
|
310
|
-
md +=
|
|
311
|
-
md +=
|
|
308
|
+
md += `- ⚠️ 보조: ctx.scheduler.runAfter(0, "module.nextStep", { sessionId }) — 로컬 dev에서만 안정적. 클라우드에서는 앱이 idle 상태로 전환 시 타이머가 소멸돼.\n`;
|
|
309
|
+
md += `\n`;
|
|
310
|
+
md += `⚠️ mutation 간 로직 공유:\n`;
|
|
311
|
+
md += `- mutation().handler()를 다른 mutation에서 직접 호출하면 안 돼.\n`;
|
|
312
|
+
md += `- ✅ 공유 로직은 일반 async 함수로 분리하고, 각 mutation에서 래핑해:\n`;
|
|
313
|
+
md += ` // 공유 함수 (일반 async 함수)\n`;
|
|
314
|
+
md += ` async function filterBatch(ctx, ids) { /* 실제 로직 */ }\n`;
|
|
315
|
+
md += ` // mutation은 래핑만\n`;
|
|
316
|
+
md += ` export const filter = mutation("pipeline.filter", {\n`;
|
|
317
|
+
md += ` handler: async (ctx, args) => filterBatch(ctx, args.ids),\n`;
|
|
318
|
+
md += ` });\n`;
|
|
319
|
+
md += `- ❌ fetch("/api/mutation") self-fetch 패턴은 사용하지 마.\n`;
|
|
312
320
|
|
|
313
321
|
md += `\n`;
|
|
314
322
|
md += `크론 잡 (예약 작업):\n`;
|
|
@@ -373,12 +381,18 @@ export function buildAiPrompt(apiObj, namespaces) {
|
|
|
373
381
|
md += ` const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));\n`;
|
|
374
382
|
md += ` const hex = [...new Uint8Array(hash)].map(b => b.toString(16).padStart(2, "0")).join("");\n`;
|
|
375
383
|
md += `- 직접 구현한 simpleHash 같은 32비트 해시는 충돌 위험이 있으니 SHA-256을 사용해.\n`;
|
|
376
|
-
md +=
|
|
384
|
+
md += `⚠️ AI 호출 규칙 (반드시 준수):\n`;
|
|
385
|
+
md += `- ✅ AI 호출은 반드시 import { ai } from "./ai"를 사용해:\n`;
|
|
377
386
|
md += ` import { ai } from "./ai";\n`;
|
|
378
387
|
md += ` const result = await ai.chat({ messages: [{ role: "user", content: "안녕" }] });\n`;
|
|
379
|
-
md += ` ❌ import OpenAI from "openai"; → 시크릿 관리, 비용 추적, 보안 문제 발생\n`;
|
|
380
|
-
md += ` ❌ OpenAI/Anthropic SDK 직접 설치 금지\n`;
|
|
381
388
|
md += ` ✅ ai.chat()은 로컬(OPENAI_API_KEY)과 클라우드(Gencow 프록시) 자동 전환\n`;
|
|
389
|
+
md += `- ❌ 절대 금지:\n`;
|
|
390
|
+
md += ` - import OpenAI from "openai" — SDK 직접 설치/사용 금지\n`;
|
|
391
|
+
md += ` - fetch("https://api.openai.com/...") — API 직접 호출 금지\n`;
|
|
392
|
+
md += ` - openai-direct.ts, ai-wrapper.ts 같은 별도 래퍼 파일 생성 금지\n`;
|
|
393
|
+
md += ` - ai.ts 수정 또는 우회 금지 (프록시 우회 시 크레딧 차감 불가 + 장애 위험)\n`;
|
|
394
|
+
md += `- AI 에러 발생 시: .env의 OPENAI_API_KEY 확인 또는 npx gencow add AI로 재설치\n`;
|
|
395
|
+
md += `- JSON 응답 필요 시: ai.generateObject({ schema: z.object({...}), prompt: "..." })\n`;
|
|
382
396
|
md += `- AI 컴포넌트가 없으면: gencow add AI 로 설치해.\n`;
|
|
383
397
|
md += `- RAG가 필요하면: gencow add RAG (AI 자동 포함).\n`;
|
|
384
398
|
md += `- 메모리가 필요하면: gencow add Memory.\n`;
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -2076,7 +2076,8 @@ function crud(table, options) {
|
|
|
2076
2076
|
const countResult = await db.select({ count: drizzleCount() }).from(anyTable).where(whereClause);
|
|
2077
2077
|
return { data, total: Number(countResult[0]?.count ?? 0) };
|
|
2078
2078
|
}
|
|
2079
|
-
const
|
|
2079
|
+
const enabledMethods = new Set(options?.methods ?? ["list", "get", "create", "update", "remove"]);
|
|
2080
|
+
const listDef = !enabledMethods.has("list") ? void 0 : query(`${prefix}.list`, {
|
|
2080
2081
|
public: isPublic,
|
|
2081
2082
|
args: {
|
|
2082
2083
|
page: v.optional(v.number()),
|
|
@@ -2107,7 +2108,7 @@ function crud(table, options) {
|
|
|
2107
2108
|
};
|
|
2108
2109
|
}
|
|
2109
2110
|
});
|
|
2110
|
-
const getDef = query(`${prefix}.get`, {
|
|
2111
|
+
const getDef = !enabledMethods.has("get") ? void 0 : query(`${prefix}.get`, {
|
|
2111
2112
|
public: isPublic,
|
|
2112
2113
|
args: { id: idValidator },
|
|
2113
2114
|
handler: async (ctx, args) => {
|
|
@@ -2121,7 +2122,7 @@ function crud(table, options) {
|
|
|
2121
2122
|
return result ?? null;
|
|
2122
2123
|
}
|
|
2123
2124
|
});
|
|
2124
|
-
const createDef = mutation(`${prefix}.create`, {
|
|
2125
|
+
const createDef = !enabledMethods.has("create") ? void 0 : mutation(`${prefix}.create`, {
|
|
2125
2126
|
public: isPublic,
|
|
2126
2127
|
invalidates: [],
|
|
2127
2128
|
handler: async (ctx, args) => {
|
|
@@ -2134,14 +2135,14 @@ function crud(table, options) {
|
|
|
2134
2135
|
insertData = await options.hooks.beforeCreate(insertData);
|
|
2135
2136
|
}
|
|
2136
2137
|
const [result] = await ctx.db.insert(anyTable).values(insertData).returning();
|
|
2137
|
-
if (useRealtime) {
|
|
2138
|
+
if (useRealtime && enabledMethods.has("list")) {
|
|
2138
2139
|
const listResult = await fetchListWithTotal(ctx.db);
|
|
2139
2140
|
ctx.realtime.emit(`${prefix}.list`, listResult);
|
|
2140
2141
|
}
|
|
2141
2142
|
return result;
|
|
2142
2143
|
}
|
|
2143
2144
|
});
|
|
2144
|
-
const updateDef = mutation(`${prefix}.update`, {
|
|
2145
|
+
const updateDef = !enabledMethods.has("update") ? void 0 : mutation(`${prefix}.update`, {
|
|
2145
2146
|
public: isPublic,
|
|
2146
2147
|
invalidates: [],
|
|
2147
2148
|
handler: async (ctx, args) => {
|
|
@@ -2156,14 +2157,18 @@ function crud(table, options) {
|
|
|
2156
2157
|
}
|
|
2157
2158
|
const [result] = await ctx.db.update(anyTable).set(updateData).where(eq(pk, id)).returning();
|
|
2158
2159
|
if (useRealtime) {
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2160
|
+
if (enabledMethods.has("list")) {
|
|
2161
|
+
const listResult = await fetchListWithTotal(ctx.db);
|
|
2162
|
+
ctx.realtime.emit(`${prefix}.list`, listResult);
|
|
2163
|
+
}
|
|
2164
|
+
if (enabledMethods.has("get")) {
|
|
2165
|
+
ctx.realtime.emit(`${prefix}.get`, result);
|
|
2166
|
+
}
|
|
2162
2167
|
}
|
|
2163
2168
|
return result;
|
|
2164
2169
|
}
|
|
2165
2170
|
});
|
|
2166
|
-
const removeDef = mutation(`${prefix}.remove`, {
|
|
2171
|
+
const removeDef = !enabledMethods.has("remove") ? void 0 : mutation(`${prefix}.remove`, {
|
|
2167
2172
|
public: isPublic,
|
|
2168
2173
|
invalidates: [],
|
|
2169
2174
|
handler: async (ctx, args) => {
|
|
@@ -2174,7 +2179,7 @@ function crud(table, options) {
|
|
|
2174
2179
|
} else {
|
|
2175
2180
|
await ctx.db.delete(anyTable).where(eq(pk, args.id));
|
|
2176
2181
|
}
|
|
2177
|
-
if (useRealtime) {
|
|
2182
|
+
if (useRealtime && enabledMethods.has("list")) {
|
|
2178
2183
|
const listResult = await fetchListWithTotal(ctx.db);
|
|
2179
2184
|
ctx.realtime.emit(`${prefix}.list`, listResult);
|
|
2180
2185
|
}
|
|
@@ -81506,6 +81511,110 @@ function findTsFiles(dir) {
|
|
|
81506
81511
|
}
|
|
81507
81512
|
return results;
|
|
81508
81513
|
}
|
|
81514
|
+
function auditAIBypass(functionsDir) {
|
|
81515
|
+
const issues = [];
|
|
81516
|
+
try {
|
|
81517
|
+
const fs2 = __require("fs");
|
|
81518
|
+
const path2 = __require("path");
|
|
81519
|
+
const files = findTsFiles(functionsDir);
|
|
81520
|
+
const SUSPECT_FILENAMES = /* @__PURE__ */ new Set([
|
|
81521
|
+
"openai-direct.ts",
|
|
81522
|
+
"openai-direct.tsx",
|
|
81523
|
+
"ai-wrapper.ts",
|
|
81524
|
+
"ai-wrapper.tsx",
|
|
81525
|
+
"ai-helper.ts",
|
|
81526
|
+
"ai-helper.tsx",
|
|
81527
|
+
"ai-client.ts",
|
|
81528
|
+
"ai-client.tsx",
|
|
81529
|
+
"gpt.ts",
|
|
81530
|
+
"gpt.tsx",
|
|
81531
|
+
"openai.ts",
|
|
81532
|
+
"openai.tsx",
|
|
81533
|
+
"llm.ts",
|
|
81534
|
+
"llm.tsx"
|
|
81535
|
+
]);
|
|
81536
|
+
const DIRECT_OPENAI_FETCH = /fetch\s*\(\s*[`"']https?:\/\/api\.openai\.com/;
|
|
81537
|
+
const DIRECT_OPENAI_CONSTRUCTOR = /new\s+OpenAI\s*\(/;
|
|
81538
|
+
const INLINE_AI_FUNC = /(?:async\s+)?function\s+(?:callGPT|callOpenAI|callAI|fetchGPT|aiCall|directAI)\s*\(/;
|
|
81539
|
+
const OPENAI_IMPORT = /(?:import|require)\s*\(?.*['"]openai['"]/;
|
|
81540
|
+
for (const file3 of files) {
|
|
81541
|
+
const basename = path2.basename(file3);
|
|
81542
|
+
const relPath = path2.relative(functionsDir, file3);
|
|
81543
|
+
if (basename === "ai.ts") continue;
|
|
81544
|
+
if (SUSPECT_FILENAMES.has(basename)) {
|
|
81545
|
+
issues.push({
|
|
81546
|
+
file: relPath,
|
|
81547
|
+
line: 0,
|
|
81548
|
+
snippet: `\uD30C\uC77C\uBA85 "${basename}" \u2014 AI \uC6B0\uD68C \uB798\uD37C \uC758\uC2EC`,
|
|
81549
|
+
type: "suspect-file"
|
|
81550
|
+
});
|
|
81551
|
+
}
|
|
81552
|
+
const content = fs2.readFileSync(file3, "utf-8");
|
|
81553
|
+
const lines = content.split("\n");
|
|
81554
|
+
for (let i = 0; i < lines.length; i++) {
|
|
81555
|
+
const line = lines[i];
|
|
81556
|
+
const trimmed = line.trim();
|
|
81557
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) continue;
|
|
81558
|
+
if (DIRECT_OPENAI_FETCH.test(line)) {
|
|
81559
|
+
issues.push({
|
|
81560
|
+
file: relPath,
|
|
81561
|
+
line: i + 1,
|
|
81562
|
+
snippet: trimmed.slice(0, 100),
|
|
81563
|
+
type: "direct-fetch"
|
|
81564
|
+
});
|
|
81565
|
+
}
|
|
81566
|
+
if (DIRECT_OPENAI_CONSTRUCTOR.test(line)) {
|
|
81567
|
+
issues.push({
|
|
81568
|
+
file: relPath,
|
|
81569
|
+
line: i + 1,
|
|
81570
|
+
snippet: trimmed.slice(0, 100),
|
|
81571
|
+
type: "openai-constructor"
|
|
81572
|
+
});
|
|
81573
|
+
}
|
|
81574
|
+
if (INLINE_AI_FUNC.test(line)) {
|
|
81575
|
+
issues.push({
|
|
81576
|
+
file: relPath,
|
|
81577
|
+
line: i + 1,
|
|
81578
|
+
snippet: trimmed.slice(0, 100),
|
|
81579
|
+
type: "inline-ai-func"
|
|
81580
|
+
});
|
|
81581
|
+
}
|
|
81582
|
+
if (OPENAI_IMPORT.test(line)) {
|
|
81583
|
+
issues.push({
|
|
81584
|
+
file: relPath,
|
|
81585
|
+
line: i + 1,
|
|
81586
|
+
snippet: trimmed.slice(0, 100),
|
|
81587
|
+
type: "openai-import"
|
|
81588
|
+
});
|
|
81589
|
+
}
|
|
81590
|
+
}
|
|
81591
|
+
}
|
|
81592
|
+
} catch {
|
|
81593
|
+
}
|
|
81594
|
+
return { issues };
|
|
81595
|
+
}
|
|
81596
|
+
function printAIBypassReport(result) {
|
|
81597
|
+
if (result.issues.length === 0) return;
|
|
81598
|
+
const typeLabels = {
|
|
81599
|
+
"suspect-file": "\u{1F5C2}\uFE0F AI \uC6B0\uD68C \uB798\uD37C \uD30C\uC77C",
|
|
81600
|
+
"direct-fetch": "\u{1F534} OpenAI API \uC9C1\uC811 \uD638\uCD9C",
|
|
81601
|
+
"openai-constructor": "\u{1F534} OpenAI \uC0DD\uC131\uC790 \uC9C1\uC811 \uC0AC\uC6A9",
|
|
81602
|
+
"inline-ai-func": "\u26A0\uFE0F \uC778\uB77C\uC778 AI \uD568\uC218 \uC815\uC758",
|
|
81603
|
+
"openai-import": "\u26A0\uFE0F openai \uD328\uD0A4\uC9C0 \uC9C1\uC811 import"
|
|
81604
|
+
};
|
|
81605
|
+
console.log(`
|
|
81606
|
+
[auditor] \u{1F916} AI \uC6B0\uD68C \uD328\uD134 \uAC10\uC9C0 (${result.issues.length}\uAC74):`);
|
|
81607
|
+
for (const issue3 of result.issues) {
|
|
81608
|
+
const label = typeLabels[issue3.type] || "\u26A0\uFE0F \uAE30\uD0C0";
|
|
81609
|
+
const loc = issue3.line > 0 ? `${issue3.file}:${issue3.line}` : issue3.file;
|
|
81610
|
+
console.log(`[auditor] ${label} \u2014 ${loc}`);
|
|
81611
|
+
console.log(`[auditor] ${issue3.snippet}`);
|
|
81612
|
+
}
|
|
81613
|
+
console.log(`[auditor] \u2192 AI \uD638\uCD9C\uC740 \uBC18\uB4DC\uC2DC import { ai } from "./ai" \uB97C \uC0AC\uC6A9\uD558\uC138\uC694.`);
|
|
81614
|
+
console.log(`[auditor] \u2192 ai.ts\uB294 \uB85C\uCEEC/\uD074\uB77C\uC6B0\uB4DC \uC790\uB3D9 \uC804\uD658\uC744 \uC9C0\uC6D0\uD569\uB2C8\uB2E4.`);
|
|
81615
|
+
console.log(`[auditor] \u2192 fetch()\uB85C OpenAI\uB97C \uC9C1\uC811 \uD638\uCD9C\uD558\uBA74 \uD074\uB77C\uC6B0\uB4DC\uC5D0\uC11C \uACFC\uAE08\uC774 \uC548 \uB429\uB2C8\uB2E4.
|
|
81616
|
+
`);
|
|
81617
|
+
}
|
|
81509
81618
|
|
|
81510
81619
|
// ../server/src/admin.ts
|
|
81511
81620
|
init_src();
|
|
@@ -82244,7 +82353,7 @@ async function executeWithTimeout(fn, type, functionName) {
|
|
|
82244
82353
|
new Promise((_, reject) => {
|
|
82245
82354
|
controller.signal.addEventListener("abort", () => {
|
|
82246
82355
|
const err = new Error(
|
|
82247
|
-
`Function "${functionName}" exceeded ${timeoutMs / 1e3}s time limit.
|
|
82356
|
+
`Function "${functionName}" exceeded ${timeoutMs / 1e3}s time limit. Split into smaller mutations and call them sequentially from the frontend. Limits: query 30s, mutation 30s, httpAction 5min, cron 10min.`
|
|
82248
82357
|
);
|
|
82249
82358
|
err.code = "FUNCTION_TIMEOUT";
|
|
82250
82359
|
reject(err);
|
|
@@ -82390,6 +82499,8 @@ async function main() {
|
|
|
82390
82499
|
}
|
|
82391
82500
|
const selfFetchResult = auditSelfFetch(functionsPath);
|
|
82392
82501
|
printSelfFetchReport(selfFetchResult);
|
|
82502
|
+
const aiBypassResult = auditAIBypass(functionsPath);
|
|
82503
|
+
printAIBypassReport(aiBypassResult);
|
|
82393
82504
|
const projectDir = resolve6(functionsPath, "..");
|
|
82394
82505
|
const frontendResult = auditFrontendAntiPatterns(projectDir);
|
|
82395
82506
|
printFrontendAntiPatternReport(frontendResult);
|
|
@@ -83373,10 +83484,10 @@ async function main() {
|
|
|
83373
83484
|
} : void 0),
|
|
83374
83485
|
scheduler: scheduler ?? {
|
|
83375
83486
|
runAfter: () => {
|
|
83376
|
-
throw new Error("Scheduler not initialized");
|
|
83487
|
+
throw new Error("Scheduler not initialized \u2014 add gencow/crons.ts to enable. Note: In BaaS mode, scheduler.runAfter() is sleep-unsafe (timers are lost when app goes idle). Recommended: split into separate mutations and call sequentially from frontend.");
|
|
83377
83488
|
},
|
|
83378
83489
|
runAt: () => {
|
|
83379
|
-
throw new Error("Scheduler not initialized");
|
|
83490
|
+
throw new Error("Scheduler not initialized \u2014 add gencow/crons.ts to enable. Note: In BaaS mode, scheduler.runAt() is sleep-unsafe.");
|
|
83380
83491
|
},
|
|
83381
83492
|
cancel: () => false,
|
|
83382
83493
|
cron: () => {
|