gencow 0.1.169 → 0.1.170

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.
@@ -20,7 +20,7 @@ export function resolveBackupAppId(restArgs, gencowJson = null) {
20
20
  export function resolveBackupFilePath(restArgs) {
21
21
  for (let i = 0; i < restArgs.length; i++) {
22
22
  const arg = restArgs[i];
23
- if (arg === "--app" || arg === "-a") {
23
+ if (arg === "--app" || arg === "-a" || arg === "--limit" || arg === "--cursor") {
24
24
  i++;
25
25
  continue;
26
26
  }
@@ -45,10 +45,34 @@ export function formatBackupDate(value) {
45
45
  });
46
46
  }
47
47
 
48
+ export function parseBackupListOptions(restArgs) {
49
+ const options = { all: false, cursor: null, limit: 20 };
50
+ for (let i = 0; i < restArgs.length; i++) {
51
+ const arg = restArgs[i];
52
+ if (arg === "--all") {
53
+ options.all = true;
54
+ continue;
55
+ }
56
+ if (arg === "--limit") {
57
+ const next = Number(restArgs[i + 1]);
58
+ if (Number.isFinite(next)) options.limit = Math.min(100, Math.max(1, Math.trunc(next)));
59
+ i++;
60
+ continue;
61
+ }
62
+ if (arg === "--cursor") {
63
+ options.cursor = restArgs[i + 1] || null;
64
+ i++;
65
+ }
66
+ }
67
+ return options;
68
+ }
69
+
48
70
  function renderBackupHelp(logImpl = log, errorImpl = error, subCmd = null) {
49
71
  if (subCmd) errorImpl(`Unknown sub-command: ${subCmd}`);
50
72
  logImpl(`\n ${BOLD}Usage:${RESET}`);
51
- logImpl(` gencow backup list List all backups`);
73
+ logImpl(` gencow backup list List recent backups`);
74
+ logImpl(` gencow backup list --all List all backups by following cursors`);
75
+ logImpl(` gencow backup list --limit 50 List a page with up to 50 backups`);
52
76
  logImpl(` gencow backup create [note] Create a manual backup`);
53
77
  logImpl(` gencow backup restore <id> Restore from backup`);
54
78
  logImpl(` gencow backup restore-file <path> Restore from a downloaded dump file`);
@@ -108,19 +132,39 @@ export function createBackupCommand({
108
132
  switch (subCmd) {
109
133
  case "list":
110
134
  case "ls": {
111
- const response = await rpcQueryImpl(creds, "backup.list", { appName: appId });
112
- if (!response.ok) {
113
- errorImpl(
114
- `Failed to fetch backup list: ${(await response.json().catch(() => ({}))).error || response.statusText}`,
115
- );
116
- return;
135
+ const options = parseBackupListOptions(restArgs);
136
+ const backups = [];
137
+ let data = null;
138
+ let cursor = options.cursor;
139
+ for (let page = 0; page < 1000; page++) {
140
+ const response = await rpcQueryImpl(creds, "backup.list", {
141
+ appName: appId,
142
+ limit: options.limit,
143
+ cursor,
144
+ });
145
+ if (!response.ok) {
146
+ errorImpl(
147
+ `Failed to fetch backup list: ${(await response.json().catch(() => ({}))).error || response.statusText}`,
148
+ );
149
+ return;
150
+ }
151
+ data = await response.json();
152
+ backups.push(...(data.backups || []));
153
+ cursor = data.pageInfo?.nextCursor || null;
154
+ if (options.all && cursor && page === 999) {
155
+ errorImpl(
156
+ "Backup list is too large to fetch with --all. Resume with --cursor from the previous page.",
157
+ );
158
+ return;
159
+ }
160
+ if (!options.all || !cursor) break;
117
161
  }
118
- const data = await response.json();
119
- const backups = data.backups || [];
120
- const limits = data.limits || {};
162
+ const limits = data?.limits || {};
163
+ const summary = data?.summary || {};
164
+ const pageInfo = data?.pageInfo || {};
121
165
 
122
166
  logImpl(
123
- `\n${BOLD}${CYAN}Database Backups${RESET} — ${appId} ${DIM}(Plan: ${data.plan || "free"})${RESET}\n`,
167
+ `\n${BOLD}${CYAN}Database Backups${RESET} — ${appId} ${DIM}(Plan: ${data?.plan || "free"})${RESET}\n`,
124
168
  );
125
169
 
126
170
  if (backups.length === 0) {
@@ -136,6 +180,16 @@ export function createBackupCommand({
136
180
  }
137
181
  }
138
182
 
183
+ const totalCount = summary.totalCount ?? backups.length;
184
+ const pendingCount = summary.pendingCount ?? 0;
185
+ const shownLabel = options.all
186
+ ? `${backups.length}/${totalCount}`
187
+ : `${backups.length} of ${totalCount}`;
188
+ logImpl(`\n ${DIM}Showing: ${shownLabel} | Pending: ${pendingCount}${RESET}`);
189
+ if (!options.all && pageInfo.nextCursor) {
190
+ logImpl(` ${DIM}Next page: gencow backup list --cursor ${pageInfo.nextCursor}${RESET}`);
191
+ logImpl(` ${DIM}Full history: gencow backup list --all${RESET}`);
192
+ }
139
193
  logImpl(
140
194
  `\n ${DIM}Limits: ${limits.maxManualBackups || "?"} manual | ${limits.retentionDays || "?"} days | Auto: ${limits.autoBackupEnabled ? "ON" : "OFF"}${RESET}\n`,
141
195
  );
@@ -209,14 +263,10 @@ export function createBackupCommand({
209
263
  const buffer = await readFileAsync(filePath);
210
264
  const form = new FormData();
211
265
  form.append("file", new Blob([buffer]), filePath.split(/[\\/]/).pop() || "backup.dump");
212
- const response = await platformFetchImpl(
213
- creds,
214
- `/platform/apps/${appId}/backups/restore-file`,
215
- {
216
- method: "POST",
217
- body: form,
218
- },
219
- );
266
+ const response = await platformFetchImpl(creds, `/platform/apps/${appId}/backups/restore-file`, {
267
+ method: "POST",
268
+ body: form,
269
+ });
220
270
  if (!response.ok) {
221
271
  errorImpl(
222
272
  `Restore failed: ${(await response.json?.().catch(() => ({}))).error || response.statusText}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gencow",
3
- "version": "0.1.169",
3
+ "version": "0.1.170",
4
4
  "description": "Gencow — AI Backend Engine",
5
5
  "type": "module",
6
6
  "bin": {
package/server/index.js CHANGED
@@ -109005,9 +109005,9 @@ var IMAGE_DEFAULTS = {
109005
109005
  }
109006
109006
  };
109007
109007
  var PRICING_TIER_POLICY = {
109008
- free: { displayName: "Hobby", maxApps: 5, wsConnections: 50, customDomain: false },
109009
- pro: { displayName: "Legacy Paid", maxApps: 10, wsConnections: 500, customDomain: true },
109010
- scale: { displayName: "Startup", maxApps: 50, wsConnections: 5e3, customDomain: true }
109008
+ free: { displayName: "Hobby", maxApps: 5, wsConnections: 1e3, wsConnectionsPerAppSafety: 500, customDomain: false },
109009
+ pro: { displayName: "Legacy Paid", maxApps: 10, wsConnections: 1e4, wsConnectionsPerAppSafety: 5e3, customDomain: true },
109010
+ scale: { displayName: "Startup", maxApps: 50, wsConnections: 1e5, wsConnectionsPerAppSafety: 25e3, customDomain: true }
109011
109011
  };
109012
109012
  function rowsFromResult6(result2) {
109013
109013
  if (Array.isArray(result2)) return result2;
@@ -109201,6 +109201,7 @@ async function reconcilePricingTierPolicy(params) {
109201
109201
  const existingDisplayName = typeof row.display_name === "string" ? row.display_name : "";
109202
109202
  const { features, changed: featuresChanged } = mergePricingTierFeatureOverrides(row.features, {
109203
109203
  wsConnections: policy.wsConnections,
109204
+ wsConnectionsPerAppSafety: policy.wsConnectionsPerAppSafety,
109204
109205
  customDomain: policy.customDomain
109205
109206
  });
109206
109207
  if (existingMaxApps === policy.maxApps && existingDisplayName === policy.displayName && !featuresChanged) {
@@ -109505,9 +109506,9 @@ async function ensurePlatformSchemaUpgrade(params) {
109505
109506
  )`);
109506
109507
  await params.rawSql(`INSERT INTO pricing_tiers (id, display_name, sort_order, max_apps, monthly_credits, max_storage_bytes, features)
109507
109508
  VALUES
109508
- ('free', 'Hobby', 0, 5, 20000, 1073741824, '{"autoBackup":false,"wsConnections":50,"customDomain":false}'),
109509
- ('pro', 'Legacy Paid', 10, 10, 100000, 5368709120, '{"autoBackup":true,"autoBackupIntervalHours":24,"wsConnections":500,"customDomain":true}'),
109510
- ('scale', 'Startup', 20, 50, 500000, 21474836480, '{"autoBackup":true,"autoBackupIntervalHours":6,"wsConnections":5000,"customDomain":true}')
109509
+ ('free', 'Hobby', 0, 5, 20000, 1073741824, '{"autoBackup":false,"wsConnections":1000,"wsConnectionsPerAppSafety":500,"customDomain":false}'),
109510
+ ('pro', 'Legacy Paid', 10, 10, 100000, 5368709120, '{"autoBackup":true,"autoBackupIntervalHours":24,"wsConnections":10000,"wsConnectionsPerAppSafety":5000,"customDomain":true}'),
109511
+ ('scale', 'Startup', 20, 50, 500000, 21474836480, '{"autoBackup":true,"autoBackupIntervalHours":6,"wsConnections":100000,"wsConnectionsPerAppSafety":25000,"customDomain":true}')
109511
109512
  ON CONFLICT (id) DO NOTHING`);
109512
109513
  await params.rawSql(`CREATE TABLE IF NOT EXISTS model_pricing (
109513
109514
  id SERIAL PRIMARY KEY,
@@ -109799,6 +109800,11 @@ function rowsFromResult7(result2) {
109799
109800
  }
109800
109801
  return [];
109801
109802
  }
109803
+ function positiveInt(value) {
109804
+ const parsed = Number(value);
109805
+ if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
109806
+ return Math.floor(parsed);
109807
+ }
109802
109808
  function parseRealtimeConnectionLimit(rawFeatures) {
109803
109809
  let features = {};
109804
109810
  if (typeof rawFeatures === "string" && rawFeatures.trim()) {
@@ -109810,9 +109816,7 @@ function parseRealtimeConnectionLimit(rawFeatures) {
109810
109816
  } else if (rawFeatures && typeof rawFeatures === "object") {
109811
109817
  features = rawFeatures;
109812
109818
  }
109813
- const limit = Number(features.wsConnections);
109814
- if (!Number.isFinite(limit) || limit <= 0) return void 0;
109815
- return Math.floor(limit);
109819
+ return positiveInt(features.wsConnectionsPerAppSafety) ?? positiveInt(features.wsConnectionsPerApp) ?? positiveInt(features.wsConnections);
109816
109820
  }
109817
109821
  async function resolveRealtimeConnectionLimit(params) {
109818
109822
  try {