gencow 0.1.103 → 0.1.105

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/bin/gencow.mjs CHANGED
@@ -191,6 +191,20 @@ function isStandaloneProject() {
191
191
  return existsSync(resolve(process.cwd(), "gencow.json"));
192
192
  }
193
193
 
194
+ /**
195
+ * drizzle-kit CLI 명령어 빌드 — 로컬 바이너리 우선 사용.
196
+ * pnpm 모노레포에서 npx가 esbuild 네이티브 바이너리를 잘못 resolve하여
197
+ * "Host version X does not match binary version Y" 에러 방지.
198
+ * 로컬 ./node_modules/.bin/drizzle-kit이 있으면 직접 실행, 없으면 npx fallback.
199
+ */
200
+ function _drizzleKitCmd(subcmd) {
201
+ const localBin = resolve(process.cwd(), "node_modules/.bin/drizzle-kit");
202
+ if (existsSync(localBin)) {
203
+ return `"${localBin}" ${subcmd}`;
204
+ }
205
+ return `npx drizzle-kit ${subcmd}`;
206
+ }
207
+
194
208
  function findServerRoot() {
195
209
  // 1st: monorepo structure (walk up from CWD to find packages/server)
196
210
  let dir = process.cwd();
@@ -1251,7 +1265,7 @@ ${hasPrompt ? `
1251
1265
  });
1252
1266
  } else {
1253
1267
  // Standalone: use npx drizzle-kit directly
1254
- execSync("npx drizzle-kit generate", {
1268
+ execSync(_drizzleKitCmd("generate"), {
1255
1269
  cwd: process.cwd(),
1256
1270
  env: genEnv,
1257
1271
  stdio: "inherit", // 프롬프트 패스스루 (rename 등)
@@ -1435,7 +1449,7 @@ ${hasPrompt ? `
1435
1449
  // 🆕 로컬 drizzle-kit generate 실행 (프롬프트 패스스루)
1436
1450
  info("스키마 마이그레이션 생성 중...");
1437
1451
  try {
1438
- execSync("npx drizzle-kit generate", {
1452
+ execSync(_drizzleKitCmd("generate"), {
1439
1453
  cwd: process.cwd(),
1440
1454
  stdio: "inherit",
1441
1455
  });
@@ -1503,7 +1517,7 @@ ${hasPrompt ? `
1503
1517
  runInServer("pnpm db:generate", buildEnv(config));
1504
1518
  } else {
1505
1519
  // Standalone: npx drizzle-kit generate (프롬프트 패스스루)
1506
- execSync("npx drizzle-kit generate", {
1520
+ execSync(_drizzleKitCmd("generate"), {
1507
1521
  cwd: process.cwd(),
1508
1522
  env: buildEnv(config),
1509
1523
  stdio: "inherit",
@@ -1983,7 +1997,7 @@ ${BOLD}Examples:${RESET}
1983
1997
  });
1984
1998
  } else {
1985
1999
  // Standalone: npx drizzle-kit generate (프롬프트 패스스루 — rename 감지 대화형 포함)
1986
- execGen("npx drizzle-kit generate", {
2000
+ execGen(_drizzleKitCmd("generate"), {
1987
2001
  cwd: process.cwd(),
1988
2002
  stdio: "inherit",
1989
2003
  });
@@ -2148,6 +2162,7 @@ ${BOLD}Examples:${RESET}
2148
2162
  method: "POST",
2149
2163
  headers: {
2150
2164
  "Content-Type": "application/octet-stream",
2165
+ "X-Deploy-Local-Dir": process.cwd(),
2151
2166
  },
2152
2167
  body: bundleBuffer,
2153
2168
  });
@@ -2202,6 +2217,7 @@ ${BOLD}Examples:${RESET}
2202
2217
  method: "POST",
2203
2218
  headers: {
2204
2219
  "Content-Type": "application/octet-stream",
2220
+ "X-Deploy-Local-Dir": process.cwd(),
2205
2221
  },
2206
2222
  body: retryBuffer,
2207
2223
  });
@@ -2491,7 +2507,7 @@ ${BOLD}Examples:${RESET}
2491
2507
  if (existsSync(schemaPath)) {
2492
2508
  info("스키마 마이그레이션 생성 중...");
2493
2509
  try {
2494
- execSync("npx drizzle-kit generate", {
2510
+ execSync(_drizzleKitCmd("generate"), {
2495
2511
  cwd: backendRoot,
2496
2512
  stdio: "inherit", // ← 프롬프트 패스스루!
2497
2513
  });
@@ -2610,7 +2626,7 @@ ${BOLD}Examples:${RESET}
2610
2626
 
2611
2627
  const backendDeployRes = await platformFetch(creds, `/platform/apps/${appId}/deploy?env=${envTarget}`, {
2612
2628
  method: "POST",
2613
- headers: { "Content-Type": "application/octet-stream" },
2629
+ headers: { "Content-Type": "application/octet-stream", "X-Deploy-Local-Dir": process.cwd() },
2614
2630
  body: backendBuffer,
2615
2631
  });
2616
2632
 
@@ -2693,7 +2709,7 @@ ${BOLD}Examples:${RESET}
2693
2709
 
2694
2710
  const deployRes = await platformFetch(creds, `/platform/apps/${appId}/deploy-static`, {
2695
2711
  method: "POST",
2696
- headers: { "Content-Type": "application/octet-stream" },
2712
+ headers: { "Content-Type": "application/octet-stream", "X-Deploy-Local-Dir": process.cwd() },
2697
2713
  body: bundleBuffer,
2698
2714
  });
2699
2715
 
@@ -3955,7 +3971,7 @@ process.exit(0);
3955
3971
  }
3956
3972
  try {
3957
3973
  const { execSync } = await import("child_process");
3958
- execSync("npx drizzle-kit generate", {
3974
+ execSync(_drizzleKitCmd("generate"), {
3959
3975
  cwd: process.cwd(),
3960
3976
  stdio: "inherit",
3961
3977
  });
package/core/index.js CHANGED
@@ -1665,7 +1665,12 @@ function createScheduler() {
1665
1665
  actions.set(name, handler);
1666
1666
  },
1667
1667
  async executeAction(name, args) {
1668
- await executeAction(name, args);
1668
+ try {
1669
+ await executeAction(name, args);
1670
+ } catch (error) {
1671
+ const msg = error instanceof Error ? error.message : String(error);
1672
+ console.error(`[scheduler] executeAction("${name}") failed: ${msg}`);
1673
+ }
1669
1674
  }
1670
1675
  };
1671
1676
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gencow",
3
- "version": "0.1.103",
3
+ "version": "0.1.105",
4
4
  "description": "Gencow — AI Backend Engine",
5
5
  "type": "module",
6
6
  "bin": {
package/server/index.js CHANGED
@@ -1673,7 +1673,12 @@ function createScheduler() {
1673
1673
  actions.set(name21, handler);
1674
1674
  },
1675
1675
  async executeAction(name21, args) {
1676
- await executeAction(name21, args);
1676
+ try {
1677
+ await executeAction(name21, args);
1678
+ } catch (error95) {
1679
+ const msg = error95 instanceof Error ? error95.message : String(error95);
1680
+ console.error(`[scheduler] executeAction("${name21}") failed: ${msg}`);
1681
+ }
1677
1682
  }
1678
1683
  };
1679
1684
  }
@@ -82259,10 +82264,6 @@ if (IS_BAAS) {
82259
82264
  const contentLength = c.res.headers.get("content-length");
82260
82265
  if (contentLength) {
82261
82266
  runtimeBuffer.add("bandwidth_bytes", parseInt(contentLength, 10));
82262
- } else if (c.res.body) {
82263
- const cloned = c.res.clone();
82264
- const buf = await cloned.arrayBuffer();
82265
- runtimeBuffer.add("bandwidth_bytes", buf.byteLength);
82266
82267
  }
82267
82268
  } catch {
82268
82269
  }
@@ -82637,25 +82638,19 @@ async function main() {
82637
82638
  // @ts-ignore
82638
82639
  duplex: "half"
82639
82640
  });
82641
+ const buf = await proxyRes.arrayBuffer();
82640
82642
  if (appName) {
82641
- try {
82642
- const cl = proxyRes.headers.get("content-length");
82643
- if (cl) {
82644
- addProxyMetric(appName, parseInt(cl, 10));
82645
- } else if (proxyRes.body) {
82646
- const buf = await proxyRes.arrayBuffer();
82647
- addProxyMetric(appName, buf.byteLength);
82648
- return new Response(buf, {
82649
- status: proxyRes.status,
82650
- headers: proxyRes.headers
82651
- });
82652
- }
82653
- } catch {
82654
- }
82643
+ addProxyMetric(appName, buf.byteLength);
82655
82644
  }
82656
- return new Response(proxyRes.body, {
82645
+ const resHeaders = new Headers(proxyRes.headers);
82646
+ if (buf.byteLength > 0) {
82647
+ resHeaders.set("content-length", String(buf.byteLength));
82648
+ } else {
82649
+ resHeaders.delete("content-length");
82650
+ }
82651
+ return new Response(buf, {
82657
82652
  status: proxyRes.status,
82658
- headers: proxyRes.headers
82653
+ headers: resHeaders
82659
82654
  });
82660
82655
  }
82661
82656
  async function serveStaticFile(c, dataDir, pathname, appName) {
@@ -82771,6 +82766,22 @@ async function main() {
82771
82766
  if (sleepingApps.length > 0) {
82772
82767
  console.log(`[platform] Registered ${sleepingApps.length} sleeping app(s) into memory for future wake \u2713`);
82773
82768
  }
82769
+ const allAppsForCronSync = [...runningApps, ...sleepingApps];
82770
+ if (allAppsForCronSync.length > 0) {
82771
+ const { syncCronJobs: _syncCronJobs } = await import(resolve5(functionsPath, "../src/provisioner.ts"));
82772
+ let cronSyncCount = 0;
82773
+ for (const row of allAppsForCronSync) {
82774
+ try {
82775
+ await _syncCronJobs(row.id, row.name, row.dataDir);
82776
+ cronSyncCount++;
82777
+ } catch (e) {
82778
+ console.warn(`[cron] ${row.name}: boot-time cron sync failed:`, e.message);
82779
+ }
82780
+ }
82781
+ if (cronSyncCount > 0) {
82782
+ console.log(`[platform] Boot-time cron sync complete for ${cronSyncCount}/${allAppsForCronSync.length} app(s) \u2713`);
82783
+ }
82784
+ }
82774
82785
  } catch (e) {
82775
82786
  const msg = e instanceof Error ? e.message : String(e);
82776
82787
  console.error("[platform] Auto-recovery failed:", msg);
@@ -82808,15 +82819,27 @@ async function main() {
82808
82819
  try {
82809
82820
  const { canSleep: _canSleep, sleepApp: _sleepApp, getLastAccess: _getLastAccess, getAppMeta: _getAppMeta } = await import(resolve5(functionsPath, "../src/provisioner.ts"));
82810
82821
  const metaMap = _getAppMeta();
82822
+ const allApps = await db.select({ name: appsTable.name, status: appsTable.status }).from(appsTable);
82823
+ const dbStatusMap = new Map(allApps.map((a) => [a.name, a.status]));
82811
82824
  for (const [appName] of metaMap) {
82812
82825
  if (sleepCount >= MAX_SLEEP_PER_CYCLE) break;
82826
+ const dbStatus = dbStatusMap.get(appName);
82827
+ if (dbStatus === "sleeping") {
82828
+ console.log(`[sleep] SWEEP: ${appName} is "sleeping" in DB but active in memory \u2014 enforcing sleep...`);
82829
+ try {
82830
+ await _sleepApp(appName);
82831
+ sleepCount++;
82832
+ } catch (e) {
82833
+ console.error(`[sleep] Failed to sweep ghost app ${appName}:`, e);
82834
+ }
82835
+ continue;
82836
+ }
82813
82837
  const last = _getLastAccess(appName) || 0;
82814
82838
  const canSlp = _canSleep(appName);
82815
82839
  if (!canSlp) continue;
82816
82840
  const idleMin = Math.floor((Date.now() - last) / 6e4);
82817
82841
  try {
82818
82842
  await _sleepApp(appName);
82819
- await db.update(appsTable).set({ status: "sleeping" }).where(eqOp(appsTable.name, appName));
82820
82843
  console.log(`[sleep] ${appName}: idle ${idleMin}m \u2014 sleeping \u2713`);
82821
82844
  sleepCount++;
82822
82845
  } catch (e) {
@@ -82909,6 +82932,8 @@ async function main() {
82909
82932
  console.error(`[cron] ${label}: failed (${elapsed}s) \u2014 ${errMsg}`);
82910
82933
  }
82911
82934
  }
82935
+ let _cronEmptyCount = 0;
82936
+ const CRON_EMPTY_LOG_INTERVAL = 5;
82912
82937
  setInterval(async () => {
82913
82938
  try {
82914
82939
  const dueJobs = await rawSql(
@@ -82921,15 +82946,31 @@ async function main() {
82921
82946
  ORDER BY cj.next_run_at ASC
82922
82947
  LIMIT 50`
82923
82948
  );
82924
- if (dueJobs.length === 0) return;
82925
- console.log(`[cron] ${dueJobs.length} due cron job(s) found`);
82926
- for (let i = 0; i < dueJobs.length; i += CRON_CONCURRENCY) {
82927
- const batch = dueJobs.slice(i, i + CRON_CONCURRENCY);
82928
- await Promise.allSettled(batch.map((job) => executeCronJob(job)));
82929
- if (i + CRON_CONCURRENCY < dueJobs.length) {
82930
- await new Promise((r) => setTimeout(r, 1e3));
82949
+ if (dueJobs.length === 0) {
82950
+ _cronEmptyCount++;
82951
+ if (_cronEmptyCount % CRON_EMPTY_LOG_INTERVAL === 0) {
82952
+ console.log(`[cron] No due jobs (${_cronEmptyCount} consecutive empty polls)`);
82931
82953
  }
82954
+ return;
82932
82955
  }
82956
+ _cronEmptyCount = 0;
82957
+ console.log(`[cron] ${dueJobs.length} due cron job(s) found`);
82958
+ for (const job of dueJobs) {
82959
+ const nextRun = cronCalculateNextRun2(job.schedule);
82960
+ await rawSql(
82961
+ `UPDATE cron_jobs SET next_run_at = $1 WHERE id = $2`,
82962
+ [nextRun, job.id]
82963
+ );
82964
+ }
82965
+ void (async () => {
82966
+ for (let i = 0; i < dueJobs.length; i += CRON_CONCURRENCY) {
82967
+ const batch = dueJobs.slice(i, i + CRON_CONCURRENCY);
82968
+ await Promise.allSettled(batch.map((job) => executeCronJob(job)));
82969
+ if (i + CRON_CONCURRENCY < dueJobs.length) {
82970
+ await new Promise((r) => setTimeout(r, 1e3));
82971
+ }
82972
+ }
82973
+ })();
82933
82974
  } catch (err) {
82934
82975
  console.error(`[cron] Scheduler error:`, err.message);
82935
82976
  }
@@ -83253,7 +83294,7 @@ async function main() {
83253
83294
  if (!IS_BAAS && Array.isArray(result) && result.length >= 2) {
83254
83295
  checkMixedUserIds(name21, result);
83255
83296
  }
83256
- return c.json(result);
83297
+ return c.json(result ?? null);
83257
83298
  } catch (err) {
83258
83299
  const status = err?.code === "FUNCTION_TIMEOUT" ? 408 : err instanceof GencowValidationError ? 400 : 500;
83259
83300
  return c.json({ error: err.message, ...err?.code ? { code: err.code } : {} }, status);
@@ -83288,7 +83329,7 @@ async function main() {
83288
83329
  name21
83289
83330
  );
83290
83331
  await invalidateQueries(mut.invalidates, ctx);
83291
- return c.json(result, 201);
83332
+ return c.json(result ?? null, 201);
83292
83333
  } catch (err) {
83293
83334
  const status = err?.code === "FUNCTION_TIMEOUT" ? 408 : err instanceof GencowValidationError ? 400 : 500;
83294
83335
  return c.json({ error: err.message, ...err?.code ? { code: err.code } : {} }, status);