gencow 0.1.103 → 0.1.104

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
@@ -2148,6 +2148,7 @@ ${BOLD}Examples:${RESET}
2148
2148
  method: "POST",
2149
2149
  headers: {
2150
2150
  "Content-Type": "application/octet-stream",
2151
+ "X-Deploy-Local-Dir": process.cwd(),
2151
2152
  },
2152
2153
  body: bundleBuffer,
2153
2154
  });
@@ -2202,6 +2203,7 @@ ${BOLD}Examples:${RESET}
2202
2203
  method: "POST",
2203
2204
  headers: {
2204
2205
  "Content-Type": "application/octet-stream",
2206
+ "X-Deploy-Local-Dir": process.cwd(),
2205
2207
  },
2206
2208
  body: retryBuffer,
2207
2209
  });
@@ -2610,7 +2612,7 @@ ${BOLD}Examples:${RESET}
2610
2612
 
2611
2613
  const backendDeployRes = await platformFetch(creds, `/platform/apps/${appId}/deploy?env=${envTarget}`, {
2612
2614
  method: "POST",
2613
- headers: { "Content-Type": "application/octet-stream" },
2615
+ headers: { "Content-Type": "application/octet-stream", "X-Deploy-Local-Dir": process.cwd() },
2614
2616
  body: backendBuffer,
2615
2617
  });
2616
2618
 
@@ -2693,7 +2695,7 @@ ${BOLD}Examples:${RESET}
2693
2695
 
2694
2696
  const deployRes = await platformFetch(creds, `/platform/apps/${appId}/deploy-static`, {
2695
2697
  method: "POST",
2696
- headers: { "Content-Type": "application/octet-stream" },
2698
+ headers: { "Content-Type": "application/octet-stream", "X-Deploy-Local-Dir": process.cwd() },
2697
2699
  body: bundleBuffer,
2698
2700
  });
2699
2701
 
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.104",
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);