gencow 0.1.193 → 0.1.194

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
@@ -38,6 +38,7 @@ import {
38
38
  updateEnvLocalUrl,
39
39
  } from "../lib/cli-project-runtime.mjs";
40
40
  import { createCodegenCommand } from "../lib/codegen-command.mjs";
41
+ import { runCliCommand } from "../lib/cli-command-runner.mjs";
41
42
  import { createDoctorCommand } from "../lib/doctor-command.mjs";
42
43
  import { maybeCheckCliVersion } from "../lib/cli-version-check.mjs";
43
44
  import { updateComponentReadme } from "../lib/component-readme.mjs";
@@ -579,13 +580,10 @@ maybeCheckCliVersion({
579
580
  warnImpl: warn,
580
581
  });
581
582
 
582
- if (commands[cmd]) {
583
- Promise.resolve(commands[cmd](...args)).catch((e) => {
584
- error(e.message || String(e));
585
- process.exit(1);
586
- });
587
- } else {
588
- error(`Unknown command: ${cmd}`);
589
- commands.help();
590
- process.exit(1);
591
- }
583
+ process.exitCode = await runCliCommand({
584
+ args,
585
+ command: cmd,
586
+ commands,
587
+ errorImpl: error,
588
+ helpImpl: commands.help,
589
+ });
@@ -24,6 +24,14 @@ import { updateEnvLocalUrl } from "./cli-project-runtime.mjs";
24
24
  // Covers the 60s runtime-observer restart grace plus multiple 5s reconciliation scans.
25
25
  const DEFAULT_DELETE_POLL_ATTEMPTS = 75;
26
26
  const DELETE_POLL_INTERVAL_MS = 1_000;
27
+ const DEFAULT_APP_CREATE_TIMEOUT_MS = 60_000;
28
+
29
+ function resolveAppCreateTimeoutMs(value) {
30
+ const parsed = Number(value);
31
+ return Number.isSafeInteger(parsed) && parsed >= 100 && parsed <= 600_000
32
+ ? parsed
33
+ : DEFAULT_APP_CREATE_TIMEOUT_MS;
34
+ }
27
35
 
28
36
  function appResponseStatus(response) {
29
37
  return Number.isSafeInteger(response?.status) ? `HTTP ${response.status}` : "unknown HTTP status";
@@ -105,23 +113,52 @@ async function confirmDelete(name) {
105
113
  }
106
114
 
107
115
  export function createAppCommand({
116
+ appCreateTimeoutMs = resolveAppCreateTimeoutMs(process.env.GENCOW_APP_CREATE_TIMEOUT_MS),
117
+ clearTimeoutImpl = clearTimeout,
118
+ confirmDeleteImpl = confirmDelete,
119
+ createAbortControllerImpl = () => new AbortController(),
108
120
  cwdImpl = () => process.cwd(),
109
121
  loadConfig,
110
122
  errorImpl = error,
111
123
  infoImpl = info,
112
124
  loadCredsImpl = loadCreds,
113
125
  logImpl = log,
114
- processRef = process,
115
126
  requireCredsImpl = requireCreds,
116
127
  rpcMutationImpl = rpcMutation,
117
128
  rpcQueryImpl = rpcQuery,
118
129
  saveCredsImpl = saveCreds,
130
+ setTimeoutImpl = setTimeout,
119
131
  sleepImpl = (ms) => new Promise((resolvePromise) => setTimeout(resolvePromise, ms)),
120
132
  deletePollAttempts = DEFAULT_DELETE_POLL_ATTEMPTS,
121
133
  successImpl = success,
122
134
  updateEnvLocalUrlImpl = updateEnvLocalUrl,
123
135
  warnImpl = warn,
124
136
  }) {
137
+ const boundedAppCreateTimeoutMs = resolveAppCreateTimeoutMs(appCreateTimeoutMs);
138
+
139
+ async function requestAppCreate(creds, name) {
140
+ const controller = createAbortControllerImpl();
141
+ const timeoutError = new Error(
142
+ `App creation request timed out after ${boundedAppCreateTimeoutMs}ms. Please try again.`,
143
+ );
144
+ let timeoutHandle;
145
+ const timeoutPromise = new Promise((_resolve, reject) => {
146
+ timeoutHandle = setTimeoutImpl(() => {
147
+ controller.abort(timeoutError);
148
+ reject(timeoutError);
149
+ }, boundedAppCreateTimeoutMs);
150
+ });
151
+ try {
152
+ const request = (async () => {
153
+ const response = await rpcMutationImpl(creds, "apps.create", { name }, { signal: controller.signal });
154
+ return { response, data: await readJsonObjectResponse(response) };
155
+ })();
156
+ return await Promise.race([request, timeoutPromise]);
157
+ } finally {
158
+ clearTimeoutImpl(timeoutHandle);
159
+ }
160
+ }
161
+
125
162
  return async function app(subcmd, ...rest) {
126
163
  if (subcmd === "--help" || subcmd === "-h" || rest.includes("--help") || rest.includes("-h")) {
127
164
  logImpl(`\n${BOLD}${CYAN}gencow app${RESET} — App management\n`);
@@ -131,10 +168,10 @@ export function createAppCommand({
131
168
  logImpl(` ${CYAN}create${RESET} Create a new app`);
132
169
  logImpl(` ${CYAN}delete${RESET} Delete an app (confirmation required)`);
133
170
  logImpl(` ${CYAN}status${RESET} Show app status\n`);
134
- return;
171
+ return 0;
135
172
  }
136
173
 
137
- const creds = requireCredsImpl();
174
+ const creds = requireCredsImpl({ throwOnMissing: true });
138
175
 
139
176
  if (!subcmd || subcmd === "list") {
140
177
  logImpl(`\n${BOLD}${CYAN}Your Apps${RESET}\n`);
@@ -142,18 +179,16 @@ export function createAppCommand({
142
179
  const body = await res.json().catch(() => null);
143
180
  if (!res.ok) {
144
181
  errorImpl(appResponseError(body, invalidAppResponse("app list", res, "error")));
145
- processRef.exit(1);
146
- return;
182
+ return 1;
147
183
  }
148
184
  const apps = parseAppListResponse(body);
149
185
  if (!apps) {
150
186
  errorImpl(invalidAppResponse("app list", res, "success"));
151
- processRef.exit(1);
152
- return;
187
+ return 1;
153
188
  }
154
189
  if (!apps.length) {
155
190
  infoImpl("No apps yet. Run: gencow app create <name>");
156
- return;
191
+ return 0;
157
192
  }
158
193
 
159
194
  logImpl(` ${"NAME".padEnd(22)} ${"STATUS".padEnd(12)} ${"URL".padEnd(38)} DEPLOYED`);
@@ -167,29 +202,26 @@ export function createAppCommand({
167
202
  );
168
203
  }
169
204
  logImpl("");
170
- return;
205
+ return 0;
171
206
  }
172
207
 
173
208
  if (subcmd === "create") {
174
209
  const name = rest[0];
175
210
  if (!name) {
176
211
  errorImpl("Usage: gencow app create <name>");
177
- processRef.exit(1);
212
+ return 1;
178
213
  }
179
214
 
180
215
  logImpl(`\n${BOLD}${CYAN}Gencow App Create${RESET}\n`);
181
216
  infoImpl(`Creating app "${name}"...`);
182
- const res = await rpcMutationImpl(creds, "apps.create", { name });
183
- const data = await readJsonObjectResponse(res);
217
+ const { response: res, data } = await requestAppCreate(creds, name);
184
218
  if (!res.ok) {
185
219
  errorImpl(appResponseError(data, invalidAppResponse("app create", res, "error")));
186
- processRef.exit(1);
187
- return;
220
+ return 1;
188
221
  }
189
222
  if (!data) {
190
223
  errorImpl(invalidAppResponse("app create", res, "success"));
191
- processRef.exit(1);
192
- return;
224
+ return 1;
193
225
  }
194
226
 
195
227
  let createdApp;
@@ -197,8 +229,7 @@ export function createAppCommand({
197
229
  createdApp = resolveCreatedAppResponse(data, { platformUrl: creds.platformUrl });
198
230
  } catch (caught) {
199
231
  errorImpl(caught.message);
200
- processRef.exit(1);
201
- return;
232
+ return 1;
202
233
  }
203
234
  const { appId, appUrl } = createdApp;
204
235
 
@@ -259,7 +290,7 @@ ${dashboardLine}
259
290
 
260
291
  ${DIM}pnpm gencow dev — watch & auto-deploy to cloud + live logs${RESET}
261
292
  `);
262
- return;
293
+ return 0;
263
294
  }
264
295
 
265
296
  if (subcmd === "delete") {
@@ -270,15 +301,14 @@ ${dashboardLine}
270
301
  const name = rest.find((value) => !value.startsWith("-")) || loadCredsImpl()?.currentApp;
271
302
  if (!name || unsupportedOption) {
272
303
  errorImpl("Usage: gencow app delete <name> [--force]");
273
- processRef.exit(1);
274
- return;
304
+ return 1;
275
305
  }
276
306
 
277
307
  if (!force) {
278
- const confirmed = await confirmDelete(name);
308
+ const confirmed = await confirmDeleteImpl(name);
279
309
  if (!confirmed) {
280
310
  warnImpl("Cancelled — name did not match.");
281
- processRef.exit(0);
311
+ return 0;
282
312
  }
283
313
  }
284
314
 
@@ -304,8 +334,7 @@ ${dashboardLine}
304
334
  invalidAppResponse("app delete", delRes, delRes.ok ? "success" : "error"),
305
335
  ),
306
336
  );
307
- processRef.exit(1);
308
- return;
337
+ return 1;
309
338
  }
310
339
 
311
340
  const currentCreds = loadCredsImpl();
@@ -314,15 +343,14 @@ ${dashboardLine}
314
343
  }
315
344
 
316
345
  successImpl(`App "${name}" deleted`);
317
- return;
346
+ return 0;
318
347
  }
319
348
 
320
349
  if (subcmd === "status") {
321
350
  const name = rest[0] || loadCredsImpl()?.currentApp;
322
351
  if (!name) {
323
352
  errorImpl("Usage: gencow app status <name>");
324
- processRef.exit(1);
325
- return;
353
+ return 1;
326
354
  }
327
355
  const res = await rpcQueryImpl(creds, "apps.get", { name });
328
356
  const decoded = await readJsonValueResponse(res);
@@ -333,24 +361,20 @@ ${dashboardLine}
333
361
  ? appNotFoundDiagnostic()
334
362
  : appResponseError(body, invalidAppResponse("app status", res, "error")),
335
363
  );
336
- processRef.exit(1);
337
- return;
364
+ return 1;
338
365
  }
339
366
  if (!decoded.parsed) {
340
367
  errorImpl(invalidAppResponse("app status", res, "success"));
341
- processRef.exit(1);
342
- return;
368
+ return 1;
343
369
  }
344
370
  const lookup = parseAppLookupResponse(body, name);
345
371
  if (lookup.kind === "not_found") {
346
372
  errorImpl(appNotFoundDiagnostic());
347
- processRef.exit(1);
348
- return;
373
+ return 1;
349
374
  }
350
375
  if (lookup.kind === "invalid_response") {
351
376
  errorImpl(invalidAppResponse("app status", res, "success"));
352
- processRef.exit(1);
353
- return;
377
+ return 1;
354
378
  }
355
379
  const data = lookup.app;
356
380
  logImpl(`\n ${BOLD}${data.name}${RESET}`);
@@ -377,10 +401,11 @@ ${dashboardLine}
377
401
  );
378
402
  }
379
403
  logImpl("");
380
- return;
404
+ return 0;
381
405
  }
382
406
 
383
407
  errorImpl(`Unknown app subcommand: ${subcmd}`);
384
408
  logImpl(` Usage: gencow app [list|create|delete|status]`);
409
+ return 1;
385
410
  };
386
411
  }
@@ -0,0 +1,27 @@
1
+ function commandErrorMessage(caught) {
2
+ if (caught instanceof Error && caught.message) return caught.message;
3
+ return String(caught);
4
+ }
5
+
6
+ function normalizeCommandExitCode(exitCode) {
7
+ if (exitCode === undefined || exitCode === 0) return 0;
8
+ if (exitCode === 1) return 1;
9
+ throw new Error("CLI command returned an invalid exit code");
10
+ }
11
+
12
+ export async function runCliCommand({ args, command, commands, errorImpl, helpImpl }) {
13
+ const handler = Object.hasOwn(commands, command) ? commands[command] : undefined;
14
+ if (typeof handler !== "function") {
15
+ errorImpl(`Unknown command: ${command}`);
16
+ helpImpl();
17
+ return 1;
18
+ }
19
+
20
+ try {
21
+ const exitCode = await Reflect.apply(handler, commands, args);
22
+ return normalizeCommandExitCode(exitCode);
23
+ } catch (caught) {
24
+ errorImpl(commandErrorMessage(caught));
25
+ return 1;
26
+ }
27
+ }
@@ -468,10 +468,7 @@ export function createDeployCommand({
468
468
  const declaredStaticClients = Array.isArray(workspaceConfig?.clients)
469
469
  ? workspaceConfig.clients.filter((client) => client?.static?.dir)
470
470
  : [];
471
- const clientSelection = selectClient(
472
- declaredStaticClients,
473
- cliSelection?.client ?? null,
474
- );
471
+ const clientSelection = selectClient(declaredStaticClients, cliSelection?.client ?? null);
475
472
  if (clientSelection.error) {
476
473
  errorImpl(clientSelection.error);
477
474
  exitImpl(1);
@@ -541,7 +538,9 @@ export function createDeployCommand({
541
538
  resolvePathImpl,
542
539
  }) || displayName;
543
540
  const backendDeployRuntime =
544
- backendProjectRoot === cwd ? deployPackageRuntime : createDeployPackageRuntimeForCwd(() => backendProjectRoot);
541
+ backendProjectRoot === cwd
542
+ ? deployPackageRuntime
543
+ : createDeployPackageRuntimeForCwd(() => backendProjectRoot);
545
544
 
546
545
  if (parsed.existingBundleMode && !appId) {
547
546
  errorImpl("--existing-bundle requires an existing app target.");
@@ -607,7 +606,7 @@ export function createDeployCommand({
607
606
  }
608
607
 
609
608
  if (parsed.staticDeploy) {
610
- return runStaticDeployFlow({
609
+ await runStaticDeployFlow({
611
610
  appId,
612
611
  backendRoot: backendProjectRoot,
613
612
  creds,
@@ -659,6 +658,7 @@ export function createDeployCommand({
659
658
  })
660
659
  : undefined,
661
660
  });
661
+ return 0;
662
662
  }
663
663
 
664
664
  logImpl(`\n${BOLD}${CYAN}Gencow Deploy${RESET}\n`);
@@ -42,9 +42,15 @@ export function resolveCredsFromSources({ env = process.env, loadCredsImpl = loa
42
42
  return creds;
43
43
  }
44
44
 
45
+ export function formatPlatformCredentialsRequiredMessage({ isCi = Boolean(process.env.CI) } = {}) {
46
+ return isCi
47
+ ? "CI environment detected but no credentials found. Set GENCOW_TOKEN in your CI/CD settings."
48
+ : "Not logged in. Run: gencow login";
49
+ }
50
+
45
51
  export class PlatformCredentialsRequiredError extends Error {
46
- constructor() {
47
- super("Platform credentials are required");
52
+ constructor(message = "Platform credentials are required") {
53
+ super(message);
48
54
  this.name = "PlatformCredentialsRequiredError";
49
55
  }
50
56
  }
@@ -52,7 +58,9 @@ export class PlatformCredentialsRequiredError extends Error {
52
58
  export function requireCreds({ throwOnMissing = false } = {}) {
53
59
  const creds = resolveCredsFromSources();
54
60
  if (!creds?.apiKey) {
55
- if (throwOnMissing) throw new PlatformCredentialsRequiredError();
61
+ if (throwOnMissing) {
62
+ throw new PlatformCredentialsRequiredError(formatPlatformCredentialsRequiredMessage());
63
+ }
56
64
  if (process.env.CI) {
57
65
  error("CI environment detected but no credentials found.");
58
66
  info(`Set ${CYAN}GENCOW_TOKEN${RESET} secret in your CI/CD settings.`);
@@ -86,10 +94,11 @@ export async function rpcQuery(creds, queryName, args = {}) {
86
94
  });
87
95
  }
88
96
 
89
- export async function rpcMutation(creds, mutationName, args = {}) {
97
+ export async function rpcMutation(creds, mutationName, args = {}, requestOptions = {}) {
90
98
  return platformFetch(creds, "/api/mutation", {
99
+ ...requestOptions,
91
100
  method: "POST",
92
- headers: { "Content-Type": "application/json" },
101
+ headers: { ...requestOptions.headers, "Content-Type": "application/json" },
93
102
  body: JSON.stringify({ name: mutationName, args }),
94
103
  });
95
104
  }
@@ -66,11 +66,13 @@ export function readStaticProjectContext(options = {}) {
66
66
  projectRoot = selection.projectDir;
67
67
  } else {
68
68
  const staticBackend = detectStaticDeployBackendImpl({ cwd, existsSyncImpl, resolvePathImpl });
69
- projectRoot = staticBackend.detectedBackend && staticBackend.backendRoot ? staticBackend.backendRoot : cwd;
69
+ projectRoot =
70
+ staticBackend.detectedBackend && staticBackend.backendRoot ? staticBackend.backendRoot : cwd;
70
71
  }
71
72
  } catch {
72
73
  const staticBackend = detectStaticDeployBackendImpl({ cwd, existsSyncImpl, resolvePathImpl });
73
- projectRoot = staticBackend.detectedBackend && staticBackend.backendRoot ? staticBackend.backendRoot : cwd;
74
+ projectRoot =
75
+ staticBackend.detectedBackend && staticBackend.backendRoot ? staticBackend.backendRoot : cwd;
74
76
  }
75
77
  const gencowJsonPath = resolveProjectMetadataPath(projectRoot, resolvePathImpl);
76
78
  let appId = explicitAppId;
@@ -201,15 +203,19 @@ export function createStaticCommand(options = {}) {
201
203
 
202
204
  const parsed = parseStaticArgs(staticArgs);
203
205
  const creds = requireCredsImpl();
204
- const { appId, displayName: initialDisplayName, gencowJsonPath, prodAppId, projectRoot } = readStaticProjectContextImpl({
206
+ const {
207
+ appId,
208
+ displayName: initialDisplayName,
209
+ gencowJsonPath,
210
+ prodAppId,
211
+ projectRoot,
212
+ } = readStaticProjectContextImpl({
205
213
  cwd: cwdImpl(),
206
214
  explicitAppId: parsed.appId,
207
215
  });
208
216
  const processEnv = process.env;
209
217
  const cliSelection = getCliInvocationSelection();
210
- const config = loadConfigImpl
211
- ? await loadConfigImpl({ cwd: projectRoot, processEnv })
212
- : null;
218
+ const config = loadConfigImpl ? await loadConfigImpl({ cwd: projectRoot, processEnv }) : null;
213
219
  const displayName = config
214
220
  ? readProjectDisplayNameImpl({
215
221
  config,
@@ -289,7 +295,7 @@ export function createStaticCommand(options = {}) {
289
295
  );
290
296
  }
291
297
 
292
- return runStaticDeployRuntimeImpl({
298
+ await runStaticDeployRuntimeImpl({
293
299
  creds,
294
300
  appId: deployTarget,
295
301
  displayName,
@@ -302,5 +308,6 @@ export function createStaticCommand(options = {}) {
302
308
  releaseSourceRoot: projectRoot,
303
309
  },
304
310
  });
311
+ return 0;
305
312
  };
306
313
  }
@@ -53,7 +53,13 @@ async function createSourceBundle({ cwd, staticOutputDir, mkdirSyncImpl = mkdirS
53
53
  return out;
54
54
  }
55
55
 
56
- async function createStaticBundle({ cwd, outputDir, mkdirSyncImpl = mkdirSync, resolvePathImpl = resolve, tarCreateImpl }) {
56
+ async function createStaticBundle({
57
+ cwd,
58
+ outputDir,
59
+ mkdirSyncImpl = mkdirSync,
60
+ resolvePathImpl = resolve,
61
+ tarCreateImpl,
62
+ }) {
57
63
  const out = resolve(cwd, ".gencow", "template-static.tar.gz");
58
64
  mkdirSyncImpl(resolve(cwd, ".gencow"), { recursive: true });
59
65
  await createTarGzipArchive({
@@ -65,12 +71,7 @@ async function createStaticBundle({ cwd, outputDir, mkdirSyncImpl = mkdirSync, r
65
71
  return out;
66
72
  }
67
73
 
68
- async function extractTemplateArchive({
69
- archive,
70
- targetDir,
71
- mkdirSyncImpl = mkdirSync,
72
- tarExtractImpl,
73
- }) {
74
+ async function extractTemplateArchive({ archive, targetDir, mkdirSyncImpl = mkdirSync, tarExtractImpl }) {
74
75
  mkdirSyncImpl(targetDir, { recursive: true });
75
76
  const extractImpl = tarExtractImpl ?? (await import("tar")).extract;
76
77
  await extractImpl({ file: archive, cwd: targetDir });
@@ -230,7 +231,10 @@ export function createTemplateMarketplaceCommand({
230
231
  if (subcommand === "publish") return publish(options);
231
232
  if (subcommand === "list") return list();
232
233
  if (subcommand === "info") return infoCommand(options._[0]);
233
- if (subcommand === "download") return download(options._[0], options);
234
+ if (subcommand === "download") {
235
+ await download(options._[0], options);
236
+ return 0;
237
+ }
234
238
  if (subcommand === "clone") {
235
239
  const slug = options._[0];
236
240
  const dir = options._[1] || slug;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gencow",
3
- "version": "0.1.193",
3
+ "version": "0.1.194",
4
4
  "description": "Gencow — AI Backend Engine",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,9 +33,9 @@
33
33
  "devDependencies": {
34
34
  "@types/node": "^25.9.5",
35
35
  "better-auth": "^1.6.23",
36
- "@gencow/react": "0.2.6",
37
36
  "@gencow/core": "0.1.42",
38
- "@gencow/client": "0.2.6"
37
+ "@gencow/client": "0.2.6",
38
+ "@gencow/react": "0.2.6"
39
39
  },
40
40
  "scripts": {
41
41
  "prebuild": "pnpm --filter @gencow/migration-contract run build && pnpm --filter @gencow/server run build",
@@ -184101,6 +184101,15 @@ function resolveGenericDeploymentFailurePolicy(input) {
184101
184101
  };
184102
184102
  }
184103
184103
  if (input.stage === "candidate") {
184104
+ if (code === "BACKEND_CAPABILITY_EMPTY") {
184105
+ return {
184106
+ status: 409,
184107
+ error: "Deploy failed: backend deploy did not expose any query, mutation, or procedure capability; use gencow static for frontend-only deployments",
184108
+ code,
184109
+ retryClass: "terminal-client",
184110
+ userAction: "EDIT_AND_CHECK"
184111
+ };
184112
+ }
184104
184113
  if (code === "APP_DEPENDENCY_INSTALL_FAILED") {
184105
184114
  return retry(
184106
184115
  code,
@@ -184224,6 +184233,7 @@ var init_deployment_failure_contract = __esm({
184224
184233
  "DEPLOYMENT_CONTROL_PLANE_",
184225
184234
  "RUNTIME_COWBOX_",
184226
184235
  "RUNTIME_ABSENCE_",
184236
+ "RUNTIME_DEPLOYMENT_",
184227
184237
  "RUNTIME_GENERATION_",
184228
184238
  "RUNTIME_OPERATION_",
184229
184239
  "RUNTIME_OWNERSHIP_",
@@ -185899,6 +185909,27 @@ async function admitDeploymentExecutorRequest(input) {
185899
185909
  throw new DeploymentExecutorAdmissionError("DEPLOYMENT_EXECUTOR_ADMISSION_UNAVAILABLE", 503);
185900
185910
  }
185901
185911
  }
185912
+ async function admitDeploymentExecutorRequestAfterActiveOperation(input, options = {}) {
185913
+ const timeoutMs = options.timeoutMs ?? 3e4;
185914
+ const pollMs = options.pollMs ?? 500;
185915
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0 || timeoutMs > 6e4 || !Number.isSafeInteger(pollMs) || pollMs < 1 || pollMs > 5e3) {
185916
+ throw new Error("DEPLOYMENT_EXECUTOR_BUSY_RETRY_CONFIG_INVALID");
185917
+ }
185918
+ const sleep6 = options.sleep ?? ((milliseconds) => new Promise((resolveWait) => setTimeout(resolveWait, milliseconds)));
185919
+ const now = options.now ?? Date.now;
185920
+ const admit = options.admit ?? admitDeploymentExecutorRequest;
185921
+ const deadlineMs = now() + timeoutMs;
185922
+ while (true) {
185923
+ try {
185924
+ return await admit(input);
185925
+ } catch (error51) {
185926
+ if (!(error51 instanceof DeploymentExecutorAdmissionError) || error51.code !== "DEPLOYMENT_EXECUTOR_APP_BUSY" || now() >= deadlineMs) {
185927
+ throw error51;
185928
+ }
185929
+ await sleep6(Math.min(pollMs, Math.max(1, deadlineMs - now())));
185930
+ }
185931
+ }
185932
+ }
185902
185933
  function deploymentExecutorAdmissionPublicMessage(code) {
185903
185934
  const messages = {
185904
185935
  DEPLOYMENT_EXECUTOR_QUEUE_FULL: "Deployment executor queue capacity is exhausted",
@@ -185968,6 +185999,7 @@ function registerPlatformApiDeployRoutes({
185968
185999
  acquireAdmissionLock: acquireAdmissionLock2,
185969
186000
  maxQueuedOperations,
185970
186001
  maxQueuedBytes,
186002
+ busyRetryTimeoutMs,
185971
186003
  logger: logger3 = console
185972
186004
  }) {
185973
186005
  const deployApp = async (c) => {
@@ -186033,21 +186065,24 @@ function registerPlatformApiDeployRoutes({
186033
186065
  for (const [header, value] of Object.entries(protocolHeaders)) c.header(header, value);
186034
186066
  };
186035
186067
  try {
186036
- const admission = await admitDeploymentExecutorRequest({
186037
- db,
186038
- app: appRow,
186039
- userId: user2.id,
186040
- environment,
186041
- bundle: bundleBuffer,
186042
- getHeader: (header) => c.req.header(header) || "",
186043
- currentNodeId,
186044
- currentSlotId,
186045
- currentRevision,
186046
- spoolRoot: executorSpoolRoot2,
186047
- acquireAdmissionLock: acquireAdmissionLock2,
186048
- maxQueuedOperations,
186049
- maxQueuedBytes
186050
- });
186068
+ const admission = await admitDeploymentExecutorRequestAfterActiveOperation(
186069
+ {
186070
+ db,
186071
+ app: appRow,
186072
+ userId: user2.id,
186073
+ environment,
186074
+ bundle: bundleBuffer,
186075
+ getHeader: (header) => c.req.header(header) || "",
186076
+ currentNodeId,
186077
+ currentSlotId,
186078
+ currentRevision,
186079
+ spoolRoot: executorSpoolRoot2,
186080
+ acquireAdmissionLock: acquireAdmissionLock2,
186081
+ maxQueuedOperations,
186082
+ maxQueuedBytes
186083
+ },
186084
+ { timeoutMs: busyRetryTimeoutMs }
186085
+ );
186051
186086
  protocolHeaders = admission.protocolHeaders;
186052
186087
  applyProtocolHeaders();
186053
186088
  const committedAttempt = admission.attempt;
@@ -411684,6 +411719,20 @@ function isPrepared(dataDir, source) {
411684
411719
  return false;
411685
411720
  }
411686
411721
  }
411722
+ function loadVerifiedRuntimeLayout(dataDir, source) {
411723
+ assertVerifiedRuntimeExecutionSource(source);
411724
+ const metadata = readAppRuntimeMetadata(dataDir);
411725
+ if (!metadata || metadata.resolvedReleaseId !== source.releaseId || !isPrepared(dataDir, source)) {
411726
+ throw new Error("RUNTIME_CANDIDATE_LAYOUT_NOT_PREPARED");
411727
+ }
411728
+ return {
411729
+ ...metadata,
411730
+ layout: source.layout,
411731
+ serverEntry: source.serverEntry,
411732
+ runtimeNodeModulesPath: getManagedRuntimeNodeModulesPath(dataDir),
411733
+ publishedReleaseDir: source.runtimeRoot
411734
+ };
411735
+ }
411687
411736
  function prepareVerifiedRuntimeLayout(dataDir, source, runtimeCompatibility = source.releaseId) {
411688
411737
  assertVerifiedRuntimeExecutionSource(source);
411689
411738
  const metadata = {
@@ -419268,6 +419317,12 @@ async function createRuntimeControlPlaneProvisionerAdapter(input) {
419268
419317
  });
419269
419318
  preparedRuntimeLayouts.set(workspacePath, runtimeLayout);
419270
419319
  },
419320
+ loadSealedWorkspace(workspacePath) {
419321
+ preparedRuntimeLayouts.set(
419322
+ workspacePath,
419323
+ loadVerifiedRuntimeLayout(workspacePath, runtimeSource)
419324
+ );
419325
+ },
419271
419326
  async allocateCandidateTarget(workspacePath) {
419272
419327
  void workspacePath;
419273
419328
  return {
@@ -419306,6 +419361,7 @@ var init_runtime_control_plane_provisioner_adapter = __esm({
419306
419361
  init_warm_pool_backend();
419307
419362
  init_runtime_process_identity();
419308
419363
  init_provisioner_database_preflight();
419364
+ init_provisioner_verified_runtime_layout();
419309
419365
  }
419310
419366
  });
419311
419367
 
@@ -420223,6 +420279,13 @@ async function probeSlotCapability(input) {
420223
420279
  return "unavailable";
420224
420280
  }
420225
420281
  }
420282
+ async function probeCurrentSlotCapabilityWithRetry(input) {
420283
+ let capability2 = await probeSlotCapability(input);
420284
+ for (let attempt = 1; attempt < 3 && capability2 === "unavailable"; attempt += 1) {
420285
+ capability2 = await probeSlotCapability(input);
420286
+ }
420287
+ return capability2;
420288
+ }
420226
420289
  async function convergeDeploymentRouteAcrossEligibleSlots(input) {
420227
420290
  const fetchImpl = input.fetchImpl ?? fetch;
420228
420291
  const env2 = input.env ?? process.env;
@@ -420231,13 +420294,14 @@ async function convergeDeploymentRouteAcrossEligibleSlots(input) {
420231
420294
  const eligible = [];
420232
420295
  let siblingBlocked = false;
420233
420296
  for (const port of ports) {
420234
- const capability2 = await probeSlotCapability({
420297
+ const probeInput = {
420235
420298
  fetchImpl,
420236
420299
  port,
420237
420300
  currentPort,
420238
420301
  expectedNodeId: input.binding.nodeId,
420239
420302
  env: env2
420240
- });
420303
+ };
420304
+ const capability2 = port === currentPort ? await probeCurrentSlotCapabilityWithRetry(probeInput) : await probeSlotCapability(probeInput);
420241
420305
  if (capability2 === "capable" || capability2 === "legacy") eligible.push(port);
420242
420306
  if (port !== currentPort && capability2 === "blocked") siblingBlocked = true;
420243
420307
  }
@@ -420534,7 +420598,7 @@ async function deployWithRuntimeControlPlane(input) {
420534
420598
  }
420535
420599
  );
420536
420600
  await input.onExecutorPhase?.("runtime_preparing");
420537
- await provisioner.prepareWorkspace(input.app.name, generation.workspacePath);
420601
+ provisioner.loadSealedWorkspace(generation.workspacePath);
420538
420602
  const identity2 = await prepareIdentity({
420539
420603
  workspacePath: generation.workspacePath,
420540
420604
  artifactRef: input.artifact.artifactRef,