gencow 0.1.245 → 0.1.246
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/lib/deploy-admission-boundary.mjs +52 -0
- package/lib/deploy-failure-diagnostic.mjs +26 -0
- package/lib/deploy-package-runtime.mjs +20 -5
- package/lib/deploy-runtime.mjs +7 -2
- package/lib/deploy-static-fullstack-runtime.mjs +16 -9
- package/lib/deployment-admission-retry.mjs +8 -3
- package/lib/static-deploy-command.mjs +139 -46
- package/lib/static-deploy-receipt-store.mjs +100 -0
- package/package.json +3 -3
- package/runtime/server.mjs +124 -23
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const DEFAULT_ADMISSION_TIMEOUT_MS = 5 * 60 * 1000;
|
|
2
|
+
|
|
3
|
+
function pendingResponse({ requestId, appName, resumeCommand = null }) {
|
|
4
|
+
const response = new Response(
|
|
5
|
+
JSON.stringify({
|
|
6
|
+
error: "Deployment admission is still running. The server request was not cancelled.",
|
|
7
|
+
code: "PLATFORM_DEPLOYMENT_ADMISSION_PENDING",
|
|
8
|
+
stage: "admission",
|
|
9
|
+
retryClass: "retryable-infra",
|
|
10
|
+
userAction: "VIEW_STATUS",
|
|
11
|
+
requestId,
|
|
12
|
+
appName,
|
|
13
|
+
statusCommand: "gencow deploy status",
|
|
14
|
+
resumeCommand,
|
|
15
|
+
serverOperationContinues: "UNKNOWN",
|
|
16
|
+
databaseState: "UNKNOWN",
|
|
17
|
+
incumbentServing: "UNKNOWN",
|
|
18
|
+
operationState: "UNKNOWN",
|
|
19
|
+
}),
|
|
20
|
+
{ status: 503, headers: { "Content-Type": "application/json", "Retry-After": "5" } },
|
|
21
|
+
);
|
|
22
|
+
// The caller must surface this state without replaying the same admission
|
|
23
|
+
// request as a new deployment. Keep this marker private to the in-process
|
|
24
|
+
// response path; the public body carries the stable diagnostic contract.
|
|
25
|
+
response.deploymentAdmissionPending = true;
|
|
26
|
+
return response;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function postDeploymentAdmissionWithTimeout({
|
|
30
|
+
send,
|
|
31
|
+
requestId = null,
|
|
32
|
+
appName,
|
|
33
|
+
resumeCommand = requestId ? `gencow deploy --resume ${requestId}` : null,
|
|
34
|
+
timeoutMs = DEFAULT_ADMISSION_TIMEOUT_MS,
|
|
35
|
+
setTimeoutImpl = setTimeout,
|
|
36
|
+
clearTimeoutImpl = clearTimeout,
|
|
37
|
+
}) {
|
|
38
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1) throw new Error("DEPLOYMENT_ADMISSION_TIMEOUT_INVALID");
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
let timer;
|
|
41
|
+
const deadline = new Promise((resolve) => {
|
|
42
|
+
timer = setTimeoutImpl(() => {
|
|
43
|
+
resolve(pendingResponse({ requestId, appName, resumeCommand }));
|
|
44
|
+
}, timeoutMs);
|
|
45
|
+
timer?.unref?.();
|
|
46
|
+
});
|
|
47
|
+
try {
|
|
48
|
+
return await Promise.race([send({ signal: controller.signal }), deadline]);
|
|
49
|
+
} finally {
|
|
50
|
+
if (timer !== undefined) clearTimeoutImpl(timer);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -310,6 +310,32 @@ export function renderDeployFailureDiagnosticLines(responseBody, statusText, opt
|
|
|
310
310
|
Number.isSafeInteger(Number(contract.operationId))
|
|
311
311
|
? `gencow deploy status --deployment ${contract.operationId}`
|
|
312
312
|
: "gencow deploy status";
|
|
313
|
+
if (contract.code === "PLATFORM_DEPLOYMENT_ADMISSION_PENDING") {
|
|
314
|
+
lines.push(
|
|
315
|
+
`Database state: ${safeText(responseBody.databaseState, 32) ?? "UNKNOWN"}`,
|
|
316
|
+
`Incumbent serving: ${safeText(responseBody.incumbentServing, 32) ?? "UNKNOWN"}`,
|
|
317
|
+
`Operation state: ${safeText(responseBody.operationState, 32) ?? "UNKNOWN"}`,
|
|
318
|
+
);
|
|
319
|
+
const requestId = safeText(responseBody.requestId, 128);
|
|
320
|
+
if (requestId && /^[A-Za-z0-9_-]{8,128}$/u.test(requestId)) lines.push(`Request ID: ${requestId}`);
|
|
321
|
+
lines.push("Server outcome: unknown; inspect the existing request before submitting another deployment.");
|
|
322
|
+
const resumeCommand = safeText(responseBody.resumeCommand, 240);
|
|
323
|
+
const resumeMatch = resumeCommand?.match(
|
|
324
|
+
/^gencow deploy (?:--resume [A-Za-z0-9_-]{8,128}|--static(?: --prod)? --app ([A-Za-z0-9_-]{1,128})(?: --client [A-Za-z0-9_-]{1,128})? --resume [A-Za-z0-9_-]{8,128})$/u,
|
|
325
|
+
);
|
|
326
|
+
const resumeToken = resumeCommand?.match(/--resume ([A-Za-z0-9_-]{8,128})$/u)?.[1];
|
|
327
|
+
const resumeAppName = resumeMatch?.[1];
|
|
328
|
+
const expectedResumeApp = options.expectedAppName ?? responseBody.appName;
|
|
329
|
+
if (
|
|
330
|
+
resumeMatch &&
|
|
331
|
+
requestId &&
|
|
332
|
+
resumeToken === requestId &&
|
|
333
|
+
(!resumeAppName || !expectedResumeApp || resumeAppName === expectedResumeApp)
|
|
334
|
+
) {
|
|
335
|
+
lines.push(`Next command: ${resumeCommand}`);
|
|
336
|
+
return lines;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
313
339
|
lines.push(`Next command: ${selfService?.action.command ?? exactStatusCommand}`);
|
|
314
340
|
if (contract.blockingOperationId && responseBody.retryReadinessReason === "ACTIVE_OWNER" && selfService) {
|
|
315
341
|
lines.push(`Then check retry gate: ${exactStatusCommand}`);
|
|
@@ -20,6 +20,7 @@ import { buildDeployClaimFromArchive } from "./deploy-package-claim.mjs";
|
|
|
20
20
|
import { renderDeployFailureDiagnosticLines } from "./deploy-failure-diagnostic.mjs";
|
|
21
21
|
import { readJsonObjectResponse } from "./http-response-json.mjs";
|
|
22
22
|
import { pollAcceptedDeploymentOperation } from "./deployment-operation-poll.mjs";
|
|
23
|
+
import { postDeploymentAdmissionWithTimeout } from "./deploy-admission-boundary.mjs";
|
|
23
24
|
import { submitDeploymentWithIdempotentRetry } from "./deployment-admission-retry.mjs";
|
|
24
25
|
import { acquireDeployReceipt, releaseDeployReceipt } from "./deploy-receipt-store.mjs";
|
|
25
26
|
import { CYAN, DIM, RED, RESET, YELLOW, error, info, log, success, warn } from "./output.mjs";
|
|
@@ -154,6 +155,7 @@ export function createDeployPackageRuntime({
|
|
|
154
155
|
} = {}) {
|
|
155
156
|
let migrationProjectConfig = null;
|
|
156
157
|
let preparedDeployCodegenArtifacts = null;
|
|
158
|
+
let lastDeployOutcome = "idle";
|
|
157
159
|
function cleanupPreparedDeploySource() {
|
|
158
160
|
preparedDeployCodegenArtifacts?.cleanup?.();
|
|
159
161
|
preparedDeployCodegenArtifacts = null;
|
|
@@ -596,6 +598,7 @@ export function createDeployPackageRuntime({
|
|
|
596
598
|
releaseAttemptId = null,
|
|
597
599
|
clientName = null,
|
|
598
600
|
}) {
|
|
601
|
+
lastDeployOutcome = "pending";
|
|
599
602
|
const cwd = cwdImpl();
|
|
600
603
|
if (deployClaim?.claim?.migrationBundle?.manifestSha256) {
|
|
601
604
|
emitMigrationResponsibilityNotice(warnImpl);
|
|
@@ -612,7 +615,7 @@ export function createDeployPackageRuntime({
|
|
|
612
615
|
: null;
|
|
613
616
|
let activeBundleBuffer = receipt?.bundleBuffer ?? bundleBuffer;
|
|
614
617
|
let activeDeployClaim = receipt?.deployClaim ?? deployClaim;
|
|
615
|
-
const deployOnce = async (targetAppId) =>
|
|
618
|
+
const deployOnce = async (targetAppId, signal) =>
|
|
616
619
|
platformFetchImpl(creds, `/platform/apps/${targetAppId}/deploy?env=${envTarget}`, {
|
|
617
620
|
method: "POST",
|
|
618
621
|
headers: {
|
|
@@ -633,6 +636,7 @@ export function createDeployPackageRuntime({
|
|
|
633
636
|
: {}),
|
|
634
637
|
},
|
|
635
638
|
body: activeBundleBuffer,
|
|
639
|
+
signal,
|
|
636
640
|
});
|
|
637
641
|
const pollAcceptedDeployment = (initialResponse) =>
|
|
638
642
|
pollAcceptedDeploymentOperation({
|
|
@@ -643,11 +647,15 @@ export function createDeployPackageRuntime({
|
|
|
643
647
|
setTimeoutImpl,
|
|
644
648
|
});
|
|
645
649
|
const submitDeployAdmission = () => {
|
|
646
|
-
// A legacy request without a deploy claim has no stable pre-receipt
|
|
647
|
-
// identity. Never retry that request automatically.
|
|
648
|
-
if (!activeDeployClaim) return deployOnce(targetAppId);
|
|
649
650
|
return submitDeploymentWithIdempotentRetry({
|
|
650
|
-
submit: () =>
|
|
651
|
+
submit: () =>
|
|
652
|
+
postDeploymentAdmissionWithTimeout({
|
|
653
|
+
requestId: activeDeployClaim?.claim?.requestId ?? explicitRequestId,
|
|
654
|
+
appName: targetAppId,
|
|
655
|
+
send: ({ signal }) => deployOnce(targetAppId, signal),
|
|
656
|
+
}),
|
|
657
|
+
retryOnResponse: Boolean(activeDeployClaim),
|
|
658
|
+
retryOnTransport: Boolean(activeDeployClaim),
|
|
651
659
|
sleepImpl: (ms) => new Promise((resolveDelay) => setTimeoutImpl(resolveDelay, ms)),
|
|
652
660
|
});
|
|
653
661
|
};
|
|
@@ -679,6 +687,9 @@ export function createDeployPackageRuntime({
|
|
|
679
687
|
if (deployRes.status === 202) {
|
|
680
688
|
deployRes = await pollAcceptedDeployment(deployRes);
|
|
681
689
|
}
|
|
690
|
+
} catch (error) {
|
|
691
|
+
lastDeployOutcome = "pending";
|
|
692
|
+
throw error;
|
|
682
693
|
} finally {
|
|
683
694
|
stopSpinner(spinner);
|
|
684
695
|
}
|
|
@@ -727,6 +738,7 @@ export function createDeployPackageRuntime({
|
|
|
727
738
|
errData?.code === "DEPLOY_REQUEST_IN_PROGRESS" ||
|
|
728
739
|
errData?.code === "CONTROL_PLANE_OBSERVATION_INCOMPLETE" ||
|
|
729
740
|
errData?.retryClass === "retryable-infra";
|
|
741
|
+
lastDeployOutcome = retryableResponse ? "pending" : "failed";
|
|
730
742
|
if (receipt && !retryableResponse) releaseDeployReceipt(receipt);
|
|
731
743
|
const migrationDiagnostic = isActionableMigrationDiagnostic(errData?.diagnostic)
|
|
732
744
|
? normalizeRetiredMigrationGateDiagnostic(extractActionableMigrationDiagnostic(errData.diagnostic))
|
|
@@ -751,6 +763,7 @@ export function createDeployPackageRuntime({
|
|
|
751
763
|
|
|
752
764
|
const deployData = await readJsonObjectResponse(deployRes);
|
|
753
765
|
if (!deployData) {
|
|
766
|
+
lastDeployOutcome = "unknown";
|
|
754
767
|
errorImpl(
|
|
755
768
|
`Deploy failed: no terminal deployment receipt was returned after packaging.\nCode: CLI_DEPLOY_TERMINAL_RECEIPT_MISSING\nStage: controller\nHTTP status: ${deployRes?.status ?? "unknown"}\nNext action: RETRY`,
|
|
756
769
|
);
|
|
@@ -758,6 +771,7 @@ export function createDeployPackageRuntime({
|
|
|
758
771
|
return null;
|
|
759
772
|
}
|
|
760
773
|
emitMigrationAdvisories(deployData?.warnings, warnImpl);
|
|
774
|
+
lastDeployOutcome = "completed";
|
|
761
775
|
if (receipt) releaseDeployReceipt(receipt);
|
|
762
776
|
const deployElapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
763
777
|
logImpl("");
|
|
@@ -816,5 +830,6 @@ export function createDeployPackageRuntime({
|
|
|
816
830
|
packageDeployProject,
|
|
817
831
|
createCloudAppIfNeeded,
|
|
818
832
|
deployBundleWithRetry,
|
|
833
|
+
getLastDeployOutcome: () => lastDeployOutcome,
|
|
819
834
|
};
|
|
820
835
|
}
|
package/lib/deploy-runtime.mjs
CHANGED
|
@@ -28,10 +28,12 @@ export function parseDeployArgs(deployArgs) {
|
|
|
28
28
|
isRollback: false,
|
|
29
29
|
region: null,
|
|
30
30
|
requestId: null,
|
|
31
|
+
resumeRequested: false,
|
|
31
32
|
staticDeploy: false,
|
|
32
33
|
staticDirArg: null,
|
|
33
34
|
unknownArg: null,
|
|
34
35
|
};
|
|
36
|
+
Object.defineProperty(parsed, "resumeRequested", { value: false, writable: true });
|
|
35
37
|
|
|
36
38
|
for (let index = 0; index < deployArgs.length; index += 1) {
|
|
37
39
|
const arg = deployArgs[index];
|
|
@@ -47,6 +49,7 @@ export function parseDeployArgs(deployArgs) {
|
|
|
47
49
|
break;
|
|
48
50
|
}
|
|
49
51
|
parsed.requestId = value;
|
|
52
|
+
if (arg === "--resume") parsed.resumeRequested = true;
|
|
50
53
|
index += 1;
|
|
51
54
|
} else if (arg.startsWith("--request-id=") || arg.startsWith("--resume=")) {
|
|
52
55
|
const value = arg.slice(arg.indexOf("=") + 1);
|
|
@@ -55,6 +58,7 @@ export function parseDeployArgs(deployArgs) {
|
|
|
55
58
|
break;
|
|
56
59
|
}
|
|
57
60
|
parsed.requestId = value;
|
|
61
|
+
if (arg.startsWith("--resume=")) parsed.resumeRequested = true;
|
|
58
62
|
} else if (arg === "--region") {
|
|
59
63
|
const value = deployArgs[index + 1];
|
|
60
64
|
if (!value || value.startsWith("-")) {
|
|
@@ -518,7 +522,7 @@ export function createDeployCommand({
|
|
|
518
522
|
}
|
|
519
523
|
|
|
520
524
|
let staticArtifactPreflight = null;
|
|
521
|
-
if (parsed.staticDeploy) {
|
|
525
|
+
if (parsed.staticDeploy && !parsed.resumeRequested) {
|
|
522
526
|
try {
|
|
523
527
|
staticArtifactPreflight = preflightStaticDeployArtifactImpl({
|
|
524
528
|
staticDirArg: resolvedStaticDirArg,
|
|
@@ -608,7 +612,7 @@ export function createDeployCommand({
|
|
|
608
612
|
return;
|
|
609
613
|
}
|
|
610
614
|
|
|
611
|
-
if (parsed.envTarget === "prod" && appId && !parsed.existingBundleMode) {
|
|
615
|
+
if (parsed.envTarget === "prod" && appId && !parsed.existingBundleMode && !parsed.resumeRequested) {
|
|
612
616
|
const prodTarget = await ensureProductionDeployTarget({
|
|
613
617
|
appId: rootAppId,
|
|
614
618
|
displayName,
|
|
@@ -649,6 +653,7 @@ export function createDeployCommand({
|
|
|
649
653
|
envTarget: parsed.envTarget,
|
|
650
654
|
forceDeploy: parsed.forceDeploy,
|
|
651
655
|
requestId: parsed.requestId,
|
|
656
|
+
resumeStaticOnly: parsed.resumeRequested,
|
|
652
657
|
gencowJsonPath,
|
|
653
658
|
hasDetectedBackend,
|
|
654
659
|
selectedClient,
|
|
@@ -153,12 +153,16 @@ export async function deployBackendPackage({
|
|
|
153
153
|
releaseAttemptId: releaseAttempt?.attemptId ?? null,
|
|
154
154
|
clientName,
|
|
155
155
|
});
|
|
156
|
-
if (!deployed
|
|
156
|
+
if (!deployed && !["pending", "unknown"].includes(activePackageRuntime.getLastDeployOutcome?.())) {
|
|
157
|
+
await markAttemptFailed("deploy", "DEPLOY_FAILED");
|
|
158
|
+
}
|
|
157
159
|
} catch (caught) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
if (activePackageRuntime.getLastDeployOutcome?.() !== "pending") {
|
|
161
|
+
await markAttemptFailed(
|
|
162
|
+
packageResult ? "deploy" : "package",
|
|
163
|
+
packageResult ? "DEPLOY_FAILED" : "PACKAGE_FAILED",
|
|
164
|
+
);
|
|
165
|
+
}
|
|
162
166
|
throw caught;
|
|
163
167
|
} finally {
|
|
164
168
|
if (packageResult) {
|
|
@@ -220,6 +224,7 @@ export async function runStaticDeployFlow({
|
|
|
220
224
|
envTarget,
|
|
221
225
|
forceDeploy,
|
|
222
226
|
requestId = null,
|
|
227
|
+
resumeStaticOnly = false,
|
|
223
228
|
gencowJsonPath,
|
|
224
229
|
hasDetectedBackend,
|
|
225
230
|
staticDirArg,
|
|
@@ -251,7 +256,7 @@ export async function runStaticDeployFlow({
|
|
|
251
256
|
const frontendCwd = cwdImpl();
|
|
252
257
|
let activeAppId = appId;
|
|
253
258
|
let artifactPreflight = staticArtifactPreflight;
|
|
254
|
-
if (!artifactPreflight) {
|
|
259
|
+
if (!artifactPreflight && !resumeStaticOnly) {
|
|
255
260
|
try {
|
|
256
261
|
artifactPreflight = preflightStaticDeployArtifactImpl({
|
|
257
262
|
staticDirArg,
|
|
@@ -265,8 +270,8 @@ export async function runStaticDeployFlow({
|
|
|
265
270
|
return;
|
|
266
271
|
}
|
|
267
272
|
}
|
|
268
|
-
const resolvedStaticDir = artifactPreflight?.targetDir;
|
|
269
|
-
if (!resolvedStaticDir) {
|
|
273
|
+
const resolvedStaticDir = artifactPreflight?.targetDir ?? staticDirArg;
|
|
274
|
+
if (!resolvedStaticDir && !resumeStaticOnly) {
|
|
270
275
|
errorImpl("Static files directory not found.");
|
|
271
276
|
infoImpl("Auto-detect order: dist, out, build, .next/out");
|
|
272
277
|
infoImpl("Specify manually: gencow deploy --static <dir>");
|
|
@@ -274,7 +279,7 @@ export async function runStaticDeployFlow({
|
|
|
274
279
|
return;
|
|
275
280
|
}
|
|
276
281
|
|
|
277
|
-
const shouldDeployBackend = hasDetectedBackend;
|
|
282
|
+
const shouldDeployBackend = hasDetectedBackend && !resumeStaticOnly;
|
|
278
283
|
let releaseAttemptId = null;
|
|
279
284
|
if (shouldDeployBackend) {
|
|
280
285
|
logImpl(`\n${BOLD}${CYAN}Gencow Deploy (Fullstack)${RESET}\n`);
|
|
@@ -321,8 +326,10 @@ export async function runStaticDeployFlow({
|
|
|
321
326
|
envTarget,
|
|
322
327
|
forceDeploy,
|
|
323
328
|
hasBackend: shouldDeployBackend,
|
|
329
|
+
resumeStaticOnly,
|
|
324
330
|
releaseAttemptId,
|
|
325
331
|
releaseSourceRoot: backendRoot,
|
|
332
|
+
requestId,
|
|
326
333
|
},
|
|
327
334
|
});
|
|
328
335
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
const INITIAL_DEPLOYMENT_ADMISSION_ATTEMPTS = 3;
|
|
2
2
|
|
|
3
3
|
function isRetryableInitialDeploymentAdmission(response) {
|
|
4
|
-
return
|
|
4
|
+
return response?.deploymentAdmissionPending !== true
|
|
5
|
+
&& Number.isInteger(response?.status)
|
|
6
|
+
&& response.status >= 500
|
|
7
|
+
&& response.status <= 599;
|
|
5
8
|
}
|
|
6
9
|
|
|
7
10
|
/**
|
|
@@ -12,17 +15,19 @@ function isRetryableInitialDeploymentAdmission(response) {
|
|
|
12
15
|
export async function submitDeploymentWithIdempotentRetry({
|
|
13
16
|
submit,
|
|
14
17
|
sleepImpl = (ms) => new Promise((resolveDelay) => setTimeout(resolveDelay, ms)),
|
|
18
|
+
retryOnResponse = true,
|
|
19
|
+
retryOnTransport = true,
|
|
15
20
|
}) {
|
|
16
21
|
let response;
|
|
17
22
|
for (let attempt = 1; attempt <= INITIAL_DEPLOYMENT_ADMISSION_ATTEMPTS; attempt += 1) {
|
|
18
23
|
try {
|
|
19
24
|
response = await submit();
|
|
20
25
|
} catch (error) {
|
|
21
|
-
if (attempt === INITIAL_DEPLOYMENT_ADMISSION_ATTEMPTS) throw error;
|
|
26
|
+
if (!retryOnTransport || attempt === INITIAL_DEPLOYMENT_ADMISSION_ATTEMPTS) throw error;
|
|
22
27
|
await sleepImpl(250 * attempt);
|
|
23
28
|
continue;
|
|
24
29
|
}
|
|
25
|
-
if (!isRetryableInitialDeploymentAdmission(response) || attempt === INITIAL_DEPLOYMENT_ADMISSION_ATTEMPTS) {
|
|
30
|
+
if (!retryOnResponse || !isRetryableInitialDeploymentAdmission(response) || attempt === INITIAL_DEPLOYMENT_ADMISSION_ATTEMPTS) {
|
|
26
31
|
return response;
|
|
27
32
|
}
|
|
28
33
|
await sleepImpl(250 * attempt);
|
|
@@ -20,6 +20,12 @@ import { getCliInvocationSelection } from "./project-context.mjs";
|
|
|
20
20
|
import { pollAcceptedDeploymentOperation } from "./deployment-operation-poll.mjs";
|
|
21
21
|
import { renderDeployFailureDiagnosticLines } from "./deploy-failure-diagnostic.mjs";
|
|
22
22
|
import { writeProjectMetadata } from "./deploy-project-metadata.mjs";
|
|
23
|
+
import { postDeploymentAdmissionWithTimeout } from "./deploy-admission-boundary.mjs";
|
|
24
|
+
import {
|
|
25
|
+
loadStaticDeployReceipt,
|
|
26
|
+
releaseStaticDeployReceipt,
|
|
27
|
+
saveStaticDeployReceipt,
|
|
28
|
+
} from "./static-deploy-receipt-store.mjs";
|
|
23
29
|
|
|
24
30
|
function shouldPreserveFinalizeAttempt(error) {
|
|
25
31
|
return error?.retryable === true || error?.message === "APP_SERVING_ROUTE_COMMIT_PENDING";
|
|
@@ -242,23 +248,55 @@ export function createStaticDeployRuntime({
|
|
|
242
248
|
opts = {},
|
|
243
249
|
}) {
|
|
244
250
|
const cwd = cwdImpl();
|
|
251
|
+
const receiptContext = {
|
|
252
|
+
clientName: opts.clientName ?? getCliInvocationSelection()?.client ?? null,
|
|
253
|
+
sourceRoot: resolvePathImpl(opts.releaseSourceRoot || cwd),
|
|
254
|
+
metadataPath: resolvePathImpl(gencowJsonPath || resolvePathImpl(cwd, "gencow.json")),
|
|
255
|
+
};
|
|
245
256
|
const hasBackend = opts.hasBackend === true;
|
|
257
|
+
let resumedStaticReceipt = null;
|
|
258
|
+
if (opts.resumeStaticOnly && opts.requestId) {
|
|
259
|
+
if (!appId) {
|
|
260
|
+
errorImpl("Static deployment resume requires the original app target.");
|
|
261
|
+
infoImpl("Code: STATIC_DEPLOY_RECEIPT_SCOPE_REQUIRED");
|
|
262
|
+
exitImpl(1);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
resumedStaticReceipt = loadStaticDeployReceipt({
|
|
267
|
+
cwd,
|
|
268
|
+
requestId: opts.requestId,
|
|
269
|
+
appId,
|
|
270
|
+
envTarget: opts.envTarget || "dev",
|
|
271
|
+
context: receiptContext,
|
|
272
|
+
});
|
|
273
|
+
} catch (caught) {
|
|
274
|
+
const contextMismatch = caught.message === "STATIC_DEPLOY_RECEIPT_CONTEXT_MISMATCH";
|
|
275
|
+
errorImpl("Static deployment resume could not validate the saved receipt.");
|
|
276
|
+
infoImpl(`Code: ${contextMismatch ? caught.message : "STATIC_DEPLOY_RECEIPT_UNAVAILABLE"}`);
|
|
277
|
+
infoImpl("No deployment request sent; saved receipt retained. Retry from the original workspace with the original client selection.");
|
|
278
|
+
exitImpl(1);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
246
282
|
let artifactPreflight;
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
283
|
+
if (!resumedStaticReceipt) {
|
|
284
|
+
try {
|
|
285
|
+
artifactPreflight = preflightStaticDeployArtifact({
|
|
286
|
+
staticDirArg,
|
|
287
|
+
cwd,
|
|
288
|
+
existsSyncImpl,
|
|
289
|
+
lstatSyncImpl,
|
|
290
|
+
readdirSyncImpl,
|
|
291
|
+
resolvePathImpl,
|
|
292
|
+
});
|
|
293
|
+
} catch (manifestError) {
|
|
294
|
+
errorImpl(manifestError instanceof Error ? manifestError.message : String(manifestError));
|
|
295
|
+
exitImpl(1);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
260
298
|
}
|
|
261
|
-
const targetDir = artifactPreflight?.targetDir;
|
|
299
|
+
const targetDir = artifactPreflight?.targetDir ?? resumedStaticReceipt?.sourceDir;
|
|
262
300
|
|
|
263
301
|
if (!targetDir) {
|
|
264
302
|
errorImpl("Static files directory not found.");
|
|
@@ -273,10 +311,10 @@ export function createStaticDeployRuntime({
|
|
|
273
311
|
infoImpl(appId ? `App ID: ${appId}` : `App: new (ID auto-generated)`);
|
|
274
312
|
logImpl("");
|
|
275
313
|
|
|
276
|
-
const
|
|
277
|
-
infoImpl(`Artifact: ${artifactManifest.mode} (${artifactManifest.fileCount} files)`);
|
|
314
|
+
const artifactManifest = artifactPreflight?.artifactManifest;
|
|
315
|
+
if (artifactManifest) infoImpl(`Artifact: ${artifactManifest.mode} (${artifactManifest.fileCount} files)`);
|
|
278
316
|
|
|
279
|
-
try {
|
|
317
|
+
if (!resumedStaticReceipt) try {
|
|
280
318
|
const apiRefFiles = findBuiltApiReferenceFiles({
|
|
281
319
|
targetDir: resolvePathImpl(cwd, targetDir),
|
|
282
320
|
readFileSyncImpl,
|
|
@@ -341,8 +379,8 @@ export function createStaticDeployRuntime({
|
|
|
341
379
|
await new Promise((resolveDelay) => setTimeoutImpl(resolveDelay, 3000));
|
|
342
380
|
}
|
|
343
381
|
|
|
344
|
-
let releaseAttemptId = opts.releaseAttemptId || null;
|
|
345
|
-
if (!releaseAttemptId && prepareReleaseAttemptImpl) {
|
|
382
|
+
let releaseAttemptId = opts.releaseAttemptId || resumedStaticReceipt?.releaseAttemptId || null;
|
|
383
|
+
if (!releaseAttemptId && !resumedStaticReceipt && prepareReleaseAttemptImpl) {
|
|
346
384
|
const releaseAttempt = await prepareReleaseAttemptImpl({
|
|
347
385
|
root: opts.releaseSourceRoot || cwd,
|
|
348
386
|
sourceRootKind: "static",
|
|
@@ -376,6 +414,15 @@ export function createStaticDeployRuntime({
|
|
|
376
414
|
const tmpBundle = resolvePathImpl(cwd, ".gencow", "static-bundle.tar.gz");
|
|
377
415
|
mkdirSyncImpl(resolvePathImpl(tmpBundle, ".."), { recursive: true });
|
|
378
416
|
|
|
417
|
+
let bundleSize;
|
|
418
|
+
let bundleBuffer;
|
|
419
|
+
let deployClaim;
|
|
420
|
+
if (resumedStaticReceipt) {
|
|
421
|
+
bundleBuffer = resumedStaticReceipt.bundleBuffer;
|
|
422
|
+
bundleSize = bundleBuffer.length;
|
|
423
|
+
deployClaim = resumedStaticReceipt.deployClaim;
|
|
424
|
+
successImpl(`Resuming exact static bundle: ${(bundleSize / 1024).toFixed(1)} KB`);
|
|
425
|
+
} else {
|
|
379
426
|
try {
|
|
380
427
|
await createTarGzipArchive({
|
|
381
428
|
cwd: resolvePathImpl(cwd, targetDir),
|
|
@@ -390,7 +437,7 @@ export function createStaticDeployRuntime({
|
|
|
390
437
|
return;
|
|
391
438
|
}
|
|
392
439
|
|
|
393
|
-
|
|
440
|
+
bundleSize = statSyncImpl(tmpBundle).size;
|
|
394
441
|
if (bundleSize > 100 * 1024 * 1024) {
|
|
395
442
|
await markAttemptFailed("package", "PACKAGE_TOO_LARGE");
|
|
396
443
|
errorImpl(`Bundle too large: ${(bundleSize / (1024 * 1024)).toFixed(1)} MB (max 100 MB)`);
|
|
@@ -404,13 +451,13 @@ export function createStaticDeployRuntime({
|
|
|
404
451
|
}
|
|
405
452
|
successImpl(`Bundle created: ${(bundleSize / 1024).toFixed(1)} KB`);
|
|
406
453
|
|
|
407
|
-
|
|
408
|
-
let deployClaim;
|
|
454
|
+
bundleBuffer = readFileSyncImpl(tmpBundle);
|
|
409
455
|
try {
|
|
410
456
|
deployClaim = await buildDeployClaimFromArchiveImpl({
|
|
411
457
|
bundleBuffer,
|
|
412
458
|
declaredRuntime: "none",
|
|
413
459
|
cliVersion,
|
|
460
|
+
...(opts.requestId ? { requestId: opts.requestId } : {}),
|
|
414
461
|
});
|
|
415
462
|
} catch (caught) {
|
|
416
463
|
await markAttemptFailed("package", "DEPLOY_CONTRACT_FAILED");
|
|
@@ -423,6 +470,32 @@ export function createStaticDeployRuntime({
|
|
|
423
470
|
exitImpl(1);
|
|
424
471
|
return;
|
|
425
472
|
}
|
|
473
|
+
}
|
|
474
|
+
let staticReceipt = resumedStaticReceipt;
|
|
475
|
+
if (!staticReceipt && deployClaim?.claim?.requestId) {
|
|
476
|
+
try {
|
|
477
|
+
staticReceipt = saveStaticDeployReceipt({
|
|
478
|
+
cwd,
|
|
479
|
+
requestId: deployClaim.claim.requestId,
|
|
480
|
+
appId,
|
|
481
|
+
envTarget: opts.envTarget || "dev",
|
|
482
|
+
sourceDir: targetDir,
|
|
483
|
+
context: receiptContext,
|
|
484
|
+
releaseAttemptId,
|
|
485
|
+
bundleBuffer,
|
|
486
|
+
deployClaim,
|
|
487
|
+
});
|
|
488
|
+
} catch (caught) {
|
|
489
|
+
if (caught.message === "STATIC_DEPLOY_RECEIPT_IDENTITY_CONFLICT") {
|
|
490
|
+
errorImpl("Static deploy request ID is already bound to a different bundle or release.");
|
|
491
|
+
infoImpl("Code: STATIC_DEPLOY_RECEIPT_IDENTITY_CONFLICT");
|
|
492
|
+
infoImpl("No deployment request sent; use the saved receipt or omit --request-id for a new deployment.");
|
|
493
|
+
exitImpl(1);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
throw caught;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
426
499
|
|
|
427
500
|
infoImpl("Deploying static files...");
|
|
428
501
|
let deployRes;
|
|
@@ -430,22 +503,31 @@ export function createStaticDeployRuntime({
|
|
|
430
503
|
try {
|
|
431
504
|
deployRes = await postStaticDeployWithTransientProxyRetry({
|
|
432
505
|
send: () =>
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
506
|
+
postDeploymentAdmissionWithTimeout({
|
|
507
|
+
requestId: deployClaim.claim.requestId,
|
|
508
|
+
appName: appId,
|
|
509
|
+
// Resume static admission directly; fullstack flow skips the
|
|
510
|
+
// already-completed backend component for this scoped receipt.
|
|
511
|
+
resumeCommand: `gencow deploy --static${opts.envTarget === "prod" ? " --prod" : ""} --app ${appId}${opts.clientName ? ` --client ${opts.clientName}` : ""} --resume ${deployClaim.claim.requestId}`,
|
|
512
|
+
send: ({ signal }) =>
|
|
513
|
+
platformFetchImpl(creds, `/platform/apps/${appId}/deploy-static`, {
|
|
514
|
+
method: "POST",
|
|
515
|
+
headers: {
|
|
516
|
+
"Content-Type": "application/octet-stream",
|
|
517
|
+
"X-Deploy-Local-Dir": cwd,
|
|
518
|
+
"X-Gencow-Deploy-Claim": deployClaim.header,
|
|
519
|
+
"X-Gencow-Deploy-Protocol": "1",
|
|
520
|
+
"X-Gencow-CLI-Version": deployClaim.claim.generatedBy.version,
|
|
521
|
+
...(releaseAttemptId
|
|
522
|
+
? {
|
|
523
|
+
"X-Gencow-Release-Attempt-Id": releaseAttemptId,
|
|
524
|
+
"X-Gencow-Release-Environment": opts.envTarget || "dev",
|
|
525
|
+
}
|
|
526
|
+
: {}),
|
|
527
|
+
},
|
|
528
|
+
body: bundleBuffer,
|
|
529
|
+
signal,
|
|
530
|
+
}),
|
|
449
531
|
}),
|
|
450
532
|
retryDelaysMs: staticDeployRetryDelaysMs,
|
|
451
533
|
delayImpl: staticDeployRetryDelayImpl,
|
|
@@ -510,8 +592,15 @@ export function createStaticDeployRuntime({
|
|
|
510
592
|
errData.operationId === undefined &&
|
|
511
593
|
errData.deployId === undefined &&
|
|
512
594
|
errData.state === undefined;
|
|
513
|
-
|
|
595
|
+
const retryableInfrastructureFailure = errData?.retryClass === "retryable-infra";
|
|
596
|
+
if ((verifiedTerminalFailure || rejectedBeforeOperation) && !retryableInfrastructureFailure)
|
|
514
597
|
await markAttemptFailed("deploy", "DEPLOY_FAILED");
|
|
598
|
+
if (
|
|
599
|
+
staticReceipt &&
|
|
600
|
+
(verifiedTerminalFailure || rejectedBeforeOperation) &&
|
|
601
|
+
!retryableInfrastructureFailure
|
|
602
|
+
)
|
|
603
|
+
releaseStaticDeployReceipt(staticReceipt);
|
|
515
604
|
const [headline, ...details] = renderDeployFailureDiagnosticLines(errData, deployRes.statusText, {
|
|
516
605
|
expectedAppName: appId,
|
|
517
606
|
});
|
|
@@ -540,10 +629,12 @@ export function createStaticDeployRuntime({
|
|
|
540
629
|
} catch (caught) {
|
|
541
630
|
if (!shouldPreserveFinalizeAttempt(caught)) {
|
|
542
631
|
await markAttemptFailed("finalize", "APP_RELEASE_FINALIZE_FAILED");
|
|
632
|
+
if (staticReceipt) releaseStaticDeployReceipt(staticReceipt);
|
|
543
633
|
}
|
|
544
634
|
throw caught;
|
|
545
635
|
}
|
|
546
636
|
}
|
|
637
|
+
if (staticReceipt) releaseStaticDeployReceipt(staticReceipt);
|
|
547
638
|
logImpl("");
|
|
548
639
|
successImpl("Static deploy complete!");
|
|
549
640
|
infoImpl(`App ID: ${appId}`);
|
|
@@ -572,15 +663,17 @@ export function createStaticDeployRuntime({
|
|
|
572
663
|
}
|
|
573
664
|
|
|
574
665
|
const envSyncRoot = opts.releaseSourceRoot || cwd;
|
|
575
|
-
const envSyncConfig = loadConfigImpl
|
|
666
|
+
const envSyncConfig = !opts.resumeStaticOnly && loadConfigImpl
|
|
576
667
|
? await loadConfigImpl({ cwd: envSyncRoot, processEnv: process.env, throwOnInvalid: false })
|
|
577
668
|
: null;
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
669
|
+
if (!opts.resumeStaticOnly) {
|
|
670
|
+
updateEnvLocalUrlImpl(data.url, {
|
|
671
|
+
config: envSyncConfig,
|
|
672
|
+
cwd: envSyncRoot,
|
|
673
|
+
clientName: opts.clientName ?? getCliInvocationSelection()?.client ?? null,
|
|
674
|
+
projectDir: envSyncRoot,
|
|
675
|
+
});
|
|
676
|
+
}
|
|
584
677
|
logImpl("");
|
|
585
678
|
if (!existsSyncImpl(gencowJsonPath)) {
|
|
586
679
|
writeProjectMetadata({
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
|
|
5
|
+
const REQUEST_ID_PATTERN = /^[A-Za-z0-9_-]{8,128}$/u;
|
|
6
|
+
const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
|
|
7
|
+
|
|
8
|
+
function sha256(value) {
|
|
9
|
+
return createHash("sha256").update(value).digest("hex");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function directoryFor(cwd, requestId) {
|
|
13
|
+
if (typeof cwd !== "string" || cwd.includes("\0") || !REQUEST_ID_PATTERN.test(requestId)) {
|
|
14
|
+
throw new Error("STATIC_DEPLOY_RECEIPT_INPUT_INVALID");
|
|
15
|
+
}
|
|
16
|
+
return resolve(cwd, ".gencow", "static-deploy-receipts", requestId);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function saveStaticDeployReceipt({
|
|
20
|
+
cwd,
|
|
21
|
+
requestId,
|
|
22
|
+
appId,
|
|
23
|
+
envTarget,
|
|
24
|
+
sourceDir,
|
|
25
|
+
releaseAttemptId = null,
|
|
26
|
+
bundleBuffer,
|
|
27
|
+
deployClaim,
|
|
28
|
+
context = null,
|
|
29
|
+
}) {
|
|
30
|
+
const directory = directoryFor(cwd, requestId);
|
|
31
|
+
const archiveSha256 = Buffer.isBuffer(bundleBuffer) ? sha256(bundleBuffer) : null;
|
|
32
|
+
if (
|
|
33
|
+
!Buffer.isBuffer(bundleBuffer) ||
|
|
34
|
+
deployClaim?.claim?.requestId !== requestId ||
|
|
35
|
+
deployClaim.claim.sourceBundleSha256 !== archiveSha256
|
|
36
|
+
) {
|
|
37
|
+
throw new Error("STATIC_DEPLOY_RECEIPT_INPUT_INVALID");
|
|
38
|
+
}
|
|
39
|
+
mkdirSync(resolve(directory, ".."), { recursive: true, mode: 0o700 });
|
|
40
|
+
const receipt = {
|
|
41
|
+
version: 1,
|
|
42
|
+
component: "static",
|
|
43
|
+
requestId,
|
|
44
|
+
appId,
|
|
45
|
+
envTarget,
|
|
46
|
+
sourceDir,
|
|
47
|
+
context,
|
|
48
|
+
releaseAttemptId,
|
|
49
|
+
archiveSha256,
|
|
50
|
+
claim: deployClaim.claim,
|
|
51
|
+
claimHeader: deployClaim.header,
|
|
52
|
+
};
|
|
53
|
+
const temporary = `${directory}.tmp-${process.pid}`;
|
|
54
|
+
mkdirSync(temporary, { recursive: true, mode: 0o700 });
|
|
55
|
+
writeFileSync(resolve(temporary, "archive.tar.gz"), bundleBuffer, { mode: 0o600 });
|
|
56
|
+
writeFileSync(resolve(temporary, "receipt.json"), `${JSON.stringify(receipt, null, 2)}\n`, { mode: 0o600 });
|
|
57
|
+
try {
|
|
58
|
+
renameSync(temporary, directory);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
rmSync(temporary, { recursive: true, force: true });
|
|
61
|
+
if (!existsSync(directory)) throw error;
|
|
62
|
+
const existing = loadStaticDeployReceipt({ cwd, requestId, appId, envTarget, context });
|
|
63
|
+
if (!existing.bundleBuffer.equals(bundleBuffer) || existing.deployClaim.header !== deployClaim.header ||
|
|
64
|
+
existing.releaseAttemptId !== releaseAttemptId) throw new Error("STATIC_DEPLOY_RECEIPT_IDENTITY_CONFLICT");
|
|
65
|
+
return existing;
|
|
66
|
+
}
|
|
67
|
+
return { directory, bundleBuffer, deployClaim, releaseAttemptId };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function loadStaticDeployReceipt({ cwd, requestId, appId, envTarget, context = null }) {
|
|
71
|
+
const directory = directoryFor(cwd, requestId);
|
|
72
|
+
const receipt = JSON.parse(readFileSync(resolve(directory, "receipt.json"), "utf8"));
|
|
73
|
+
const bundleBuffer = readFileSync(resolve(directory, "archive.tar.gz"));
|
|
74
|
+
if (
|
|
75
|
+
receipt.version !== 1 || receipt.component !== "static" || receipt.requestId !== requestId || receipt.appId !== appId ||
|
|
76
|
+
receipt.envTarget !== envTarget || typeof receipt.sourceDir !== "string" ||
|
|
77
|
+
!SHA256_PATTERN.test(receipt.archiveSha256) ||
|
|
78
|
+
sha256(bundleBuffer) !== receipt.archiveSha256 ||
|
|
79
|
+
receipt.claim?.sourceBundleSha256 !== receipt.archiveSha256 ||
|
|
80
|
+
receipt.claim?.requestId !== requestId ||
|
|
81
|
+
typeof receipt.claimHeader !== "string"
|
|
82
|
+
) throw new Error("STATIC_DEPLOY_RECEIPT_CORRUPT");
|
|
83
|
+
if (context && receipt.context && (
|
|
84
|
+
receipt.context.clientName !== context.clientName || receipt.context.sourceRoot !== context.sourceRoot ||
|
|
85
|
+
receipt.context.metadataPath !== context.metadataPath)) {
|
|
86
|
+
throw new Error("STATIC_DEPLOY_RECEIPT_CONTEXT_MISMATCH");
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
directory,
|
|
90
|
+
sourceDir: receipt.sourceDir,
|
|
91
|
+
bundleBuffer,
|
|
92
|
+
releaseAttemptId: receipt.releaseAttemptId ?? null,
|
|
93
|
+
deployClaim: { claim: receipt.claim, header: receipt.claimHeader },
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function releaseStaticDeployReceipt(receipt) {
|
|
98
|
+
if (!receipt?.directory) throw new Error("STATIC_DEPLOY_RECEIPT_RELEASE_INVALID");
|
|
99
|
+
rmSync(receipt.directory, { recursive: true, force: true });
|
|
100
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gencow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.246",
|
|
4
4
|
"description": "Gencow — AI Backend Engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"@types/node": "^25.9.5",
|
|
39
39
|
"better-auth": "^1.6.23",
|
|
40
40
|
"@typescript/native": "npm:typescript@7.0.2",
|
|
41
|
-
"@gencow/client": "0.2.7",
|
|
42
41
|
"@gencow/react": "0.2.7",
|
|
43
|
-
"@gencow/core": "0.1.45"
|
|
42
|
+
"@gencow/core": "0.1.45",
|
|
43
|
+
"@gencow/client": "0.2.7"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"prebuild": "pnpm --filter @gencow/migration-contract run build && pnpm --filter @gencow/server run build",
|
package/runtime/server.mjs
CHANGED
|
@@ -160748,6 +160748,10 @@ var init_server_platform_media_worker_storage_runtime = __esm({
|
|
|
160748
160748
|
});
|
|
160749
160749
|
|
|
160750
160750
|
// ../internal/dist/src/platform-scheduled-job-secret.js
|
|
160751
|
+
function globalSigningSecretStore() {
|
|
160752
|
+
const globalObject2 = globalThis;
|
|
160753
|
+
return globalObject2[PLATFORM_SIGNING_SECRET_GLOBAL_KEY] ??= {};
|
|
160754
|
+
}
|
|
160751
160755
|
function usableSecret(value) {
|
|
160752
160756
|
const secret = value?.trim();
|
|
160753
160757
|
return secret && secret !== DEFAULT_PLATFORM_SECRET ? secret : null;
|
|
@@ -160759,17 +160763,38 @@ function capturePlatformScheduledJobSigningSecret(env2 = process.env) {
|
|
|
160759
160763
|
if (env2.IS_PLATFORM !== "true")
|
|
160760
160764
|
return void 0;
|
|
160761
160765
|
scheduledJobSigningSecretSnapshot = resolveSecret(env2);
|
|
160766
|
+
globalSigningSecretStore().value = scheduledJobSigningSecretSnapshot ?? void 0;
|
|
160762
160767
|
return scheduledJobSigningSecretSnapshot ?? void 0;
|
|
160763
160768
|
}
|
|
160764
160769
|
function getPlatformScheduledJobSigningSecret() {
|
|
160765
|
-
return scheduledJobSigningSecretSnapshot ??
|
|
160770
|
+
return scheduledJobSigningSecretSnapshot ?? globalSigningSecretStore().value;
|
|
160771
|
+
}
|
|
160772
|
+
function initializePlatformScheduledJobSigningSecret(secret) {
|
|
160773
|
+
const usable = usableSecret(secret);
|
|
160774
|
+
if (!usable)
|
|
160775
|
+
throw new Error("SCHEDULED_JOB_DELIVERY_SIGNING_SECRET_UNAVAILABLE");
|
|
160776
|
+
const existing = scheduledJobSigningSecretSnapshot ?? globalSigningSecretStore().value;
|
|
160777
|
+
if (existing && existing !== usable) {
|
|
160778
|
+
throw new Error("SCHEDULED_JOB_DELIVERY_SIGNING_SECRET_ALREADY_INITIALIZED");
|
|
160779
|
+
}
|
|
160780
|
+
scheduledJobSigningSecretSnapshot = usable;
|
|
160781
|
+
globalSigningSecretStore().value = usable;
|
|
160766
160782
|
}
|
|
160767
|
-
|
|
160783
|
+
async function loadPlatformProvisionerWithScheduledJobSigningSecret(load) {
|
|
160784
|
+
const secret = getPlatformScheduledJobSigningSecret();
|
|
160785
|
+
if (!secret)
|
|
160786
|
+
throw new Error("SCHEDULED_JOB_DELIVERY_SIGNING_SECRET_UNAVAILABLE");
|
|
160787
|
+
const provisioner = await load();
|
|
160788
|
+
provisioner.initializePlatformScheduledJobSigningSecret(secret);
|
|
160789
|
+
return provisioner;
|
|
160790
|
+
}
|
|
160791
|
+
var DEFAULT_PLATFORM_SECRET, scheduledJobSigningSecretSnapshot, PLATFORM_SIGNING_SECRET_GLOBAL_KEY;
|
|
160768
160792
|
var init_platform_scheduled_job_secret = __esm({
|
|
160769
160793
|
"../internal/dist/src/platform-scheduled-job-secret.js"() {
|
|
160770
160794
|
"use strict";
|
|
160771
160795
|
DEFAULT_PLATFORM_SECRET = "gencow-default-secret";
|
|
160772
160796
|
scheduledJobSigningSecretSnapshot = null;
|
|
160797
|
+
PLATFORM_SIGNING_SECRET_GLOBAL_KEY = /* @__PURE__ */ Symbol.for("gencow.platformScheduledJobSigningSecret");
|
|
160773
160798
|
}
|
|
160774
160799
|
});
|
|
160775
160800
|
|
|
@@ -186891,7 +186916,18 @@ async function beginAppNameD1Delete(input) {
|
|
|
186891
186916
|
(public.app_name_incarnation_handoffs.state = 'd1_blocked'
|
|
186892
186917
|
AND public.app_name_incarnation_handoffs.d1_app_id = ${input.appId}
|
|
186893
186918
|
AND public.app_name_incarnation_handoffs.d1_app_instance_id = ${input.appInstanceId}
|
|
186894
|
-
AND
|
|
186919
|
+
AND (
|
|
186920
|
+
public.app_name_incarnation_handoffs.d1_delete_operation_id = ${input.deleteOperationId}
|
|
186921
|
+
OR EXISTS (
|
|
186922
|
+
SELECT 1
|
|
186923
|
+
FROM public.app_lifecycle_operations prior_delete
|
|
186924
|
+
WHERE prior_delete.id = public.app_name_incarnation_handoffs.d1_delete_operation_id
|
|
186925
|
+
AND prior_delete.app_id = ${input.appId}
|
|
186926
|
+
AND prior_delete.app_instance_id = ${input.appInstanceId}
|
|
186927
|
+
AND prior_delete.app_name = ${input.publicName}
|
|
186928
|
+
AND prior_delete.state = 'failed_delete_recovery_required'
|
|
186929
|
+
)
|
|
186930
|
+
))
|
|
186895
186931
|
)
|
|
186896
186932
|
RETURNING id
|
|
186897
186933
|
`);
|
|
@@ -191890,6 +191926,7 @@ var init_provisioner_cron_sync = __esm({
|
|
|
191890
191926
|
"use strict";
|
|
191891
191927
|
init_postgres_temporal2();
|
|
191892
191928
|
init_platform_scheduled_job_secret();
|
|
191929
|
+
init_platform_scheduled_job_secret();
|
|
191893
191930
|
PLATFORM_SECRET = process.env.PLATFORM_INTERNAL_SECRET || process.env.BETTER_AUTH_SECRET || "gencow-default-secret";
|
|
191894
191931
|
MAX_CRON_JOBS_FREE = 5;
|
|
191895
191932
|
CRON_MANIFEST_RELATIVE_PATH = ".gencow/cron-jobs.json";
|
|
@@ -430467,6 +430504,7 @@ __export(provisioner_exports, {
|
|
|
430467
430504
|
hasLiveRuntimeProcess: () => hasLiveRuntimeProcess2,
|
|
430468
430505
|
hasWarmPoolBytecodeBundle: () => hasWarmPoolBytecodeBundle3,
|
|
430469
430506
|
initRunner: () => initRunner,
|
|
430507
|
+
initializePlatformScheduledJobSigningSecret: () => initializePlatformScheduledJobSigningSecret,
|
|
430470
430508
|
isManagedAppProcessCommand: () => isManagedAppProcessCommand2,
|
|
430471
430509
|
isRunning: () => isRunning,
|
|
430472
430510
|
listWarmPoolAssignedApps: () => listWarmPoolAssignedApps,
|
|
@@ -431158,6 +431196,9 @@ async function prepareControlPlaneCandidateWorkspace(params) {
|
|
|
431158
431196
|
}
|
|
431159
431197
|
async function buildControlPlaneCandidateEnv(params) {
|
|
431160
431198
|
try {
|
|
431199
|
+
if (!getPlatformScheduledJobSigningSecret()) {
|
|
431200
|
+
capturePlatformScheduledJobSigningSecret(process.env);
|
|
431201
|
+
}
|
|
431161
431202
|
return await buildProvisionerRuntimeEnv({
|
|
431162
431203
|
appName: params.appName,
|
|
431163
431204
|
port: params.port,
|
|
@@ -431643,6 +431684,8 @@ var init_provisioner = __esm({
|
|
|
431643
431684
|
init_provisioner_shutdown();
|
|
431644
431685
|
init_warm_pool_backend();
|
|
431645
431686
|
init_provisioner_cron_sync();
|
|
431687
|
+
init_platform_scheduled_job_secret();
|
|
431688
|
+
init_provisioner_cron_sync();
|
|
431646
431689
|
init_provisioner_schema();
|
|
431647
431690
|
init_migration_push_preflight_error();
|
|
431648
431691
|
init_provisioner_wake_timing();
|
|
@@ -442214,11 +442257,8 @@ function createRuntimeRouteProjectionConverger(input) {
|
|
|
442214
442257
|
return input.db.transaction(async (tx) => {
|
|
442215
442258
|
await acquireLock(tx, `app-activation:${target.appId}`);
|
|
442216
442259
|
const routePort = input.getAppPort(target.appName);
|
|
442217
|
-
if (routePort !== void 0 && target.expectedPort !== void 0 && routePort !== target.expectedPort) {
|
|
442218
|
-
throw new Error("RUNTIME_ROUTE_PROJECTION_BINDING_CHANGED");
|
|
442219
|
-
}
|
|
442220
442260
|
const [appRows, desiredRows, activationRows, instanceRows] = await Promise.all([
|
|
442221
|
-
tx.select({ name: apps.name }).from(apps).where(eq68(apps.id, target.appId)),
|
|
442261
|
+
tx.select({ name: apps.name, catalogPort: apps.port }).from(apps).where(eq68(apps.id, target.appId)),
|
|
442222
442262
|
tx.select({
|
|
442223
442263
|
desiredActivationId: appRuntimeDesiredState.desiredActivationId,
|
|
442224
442264
|
desiredRuntimeState: appRuntimeDesiredState.desiredRuntimeState,
|
|
@@ -442261,6 +442301,10 @@ function createRuntimeRouteProjectionConverger(input) {
|
|
|
442261
442301
|
const servingWithoutInstance = desired?.desiredRuntimeState === "serving" && desired.nodeId === input.currentNodeId && desired.desiredActivationId !== null && activationRows.some((activation) => activation.id === desired.desiredActivationId) && !instanceRows.some(
|
|
442262
442302
|
(instance) => instance.activationEpoch === desired.activationEpoch && instance.state === "serving"
|
|
442263
442303
|
);
|
|
442304
|
+
const staleCatalogRoutePort = desiredAbsentOnNode && routePort !== void 0 && target.expectedPort !== void 0 && routePort !== target.expectedPort && appRows[0]?.catalogPort === routePort;
|
|
442305
|
+
if (routePort !== void 0 && target.expectedPort !== void 0 && routePort !== target.expectedPort && !staleCatalogRoutePort) {
|
|
442306
|
+
throw new Error("RUNTIME_ROUTE_PROJECTION_BINDING_CHANGED");
|
|
442307
|
+
}
|
|
442264
442308
|
if (desired?.authorityMode !== "enforce" || !desiredAbsentOnNode && !desiredMovedFromNode && !stoppedDeletionProjection && !servingWithoutInstance) {
|
|
442265
442309
|
throw new Error("RUNTIME_ROUTE_PROJECTION_STATE_AMBIGUOUS");
|
|
442266
442310
|
}
|
|
@@ -446822,11 +446866,11 @@ async function probeSlotCapability(input) {
|
|
|
446822
446866
|
method: "GET",
|
|
446823
446867
|
path: path4
|
|
446824
446868
|
}),
|
|
446825
|
-
signal: AbortSignal.timeout(
|
|
446869
|
+
signal: AbortSignal.timeout(input.timeoutMs ?? DEFAULT_SLOT_CAPABILITY_PROBE_TIMEOUT_MS)
|
|
446826
446870
|
});
|
|
446827
446871
|
if (response.status === 404 && input.port !== input.currentPort) {
|
|
446828
446872
|
const readiness = await input.fetchImpl(`${origin}/health/ready`, {
|
|
446829
|
-
signal: AbortSignal.timeout(
|
|
446873
|
+
signal: AbortSignal.timeout(input.timeoutMs ?? DEFAULT_SLOT_CAPABILITY_PROBE_TIMEOUT_MS)
|
|
446830
446874
|
});
|
|
446831
446875
|
return readiness.ok ? "legacy" : "unavailable";
|
|
446832
446876
|
}
|
|
@@ -446880,7 +446924,10 @@ async function convergeDeploymentRouteAcrossEligibleSlots(input) {
|
|
|
446880
446924
|
env: env2,
|
|
446881
446925
|
boundNodeOrigin: boundNodeOrigin?.origin
|
|
446882
446926
|
};
|
|
446883
|
-
const capability2 = port === currentPort ? await probeCurrentSlotCapabilityWithRetry(probeInput) : await probeSlotCapability(
|
|
446927
|
+
const capability2 = port === currentPort && input.currentSlotKnownCapable ? "capable" : port === currentPort ? await probeCurrentSlotCapabilityWithRetry(probeInput) : await probeSlotCapability({
|
|
446928
|
+
...probeInput,
|
|
446929
|
+
timeoutMs: input.currentSlotKnownCapable ? KNOWN_CURRENT_SLOT_SIBLING_PROBE_TIMEOUT_MS : void 0
|
|
446930
|
+
});
|
|
446884
446931
|
return { port, capability: capability2 };
|
|
446885
446932
|
})
|
|
446886
446933
|
);
|
|
@@ -446934,12 +446981,14 @@ async function convergeDeploymentRouteAcrossEligibleSlots(input) {
|
|
|
446934
446981
|
}
|
|
446935
446982
|
return { routeConvergence: "converged", acknowledgedSlots };
|
|
446936
446983
|
}
|
|
446937
|
-
var DEFAULT_PLATFORM_GATEWAY_ENV_FILE;
|
|
446984
|
+
var DEFAULT_PLATFORM_GATEWAY_ENV_FILE, DEFAULT_SLOT_CAPABILITY_PROBE_TIMEOUT_MS, KNOWN_CURRENT_SLOT_SIBLING_PROBE_TIMEOUT_MS;
|
|
446938
446985
|
var init_runtime_route_fleet_client = __esm({
|
|
446939
446986
|
"../platform/src/runtime-route-fleet-client.ts"() {
|
|
446940
446987
|
"use strict";
|
|
446941
446988
|
init_node_proxy_signature();
|
|
446942
446989
|
DEFAULT_PLATFORM_GATEWAY_ENV_FILE = "/etc/gencow/platform-internal-gateway.env";
|
|
446990
|
+
DEFAULT_SLOT_CAPABILITY_PROBE_TIMEOUT_MS = 750;
|
|
446991
|
+
KNOWN_CURRENT_SLOT_SIBLING_PROBE_TIMEOUT_MS = 250;
|
|
446943
446992
|
}
|
|
446944
446993
|
});
|
|
446945
446994
|
|
|
@@ -447364,6 +447413,7 @@ async function restartRetainedRuntimeActivation(input) {
|
|
|
447364
447413
|
})
|
|
447365
447414
|
);
|
|
447366
447415
|
await convergeDeploymentRouteAcrossEligibleSlots({
|
|
447416
|
+
currentSlotKnownCapable: true,
|
|
447367
447417
|
binding: buildCanonicalTenantRouteBinding({
|
|
447368
447418
|
appId: current.appId,
|
|
447369
447419
|
appName: current.appName,
|
|
@@ -447444,21 +447494,27 @@ var init_runtime_retained_activation_recovery = __esm({
|
|
|
447444
447494
|
});
|
|
447445
447495
|
|
|
447446
447496
|
// ../platform/src/runtime-retained-activation-wake.ts
|
|
447497
|
+
function retainedActivationRestartOwnsServingRoute(restoreOperationId) {
|
|
447498
|
+
return !restoreOperationId;
|
|
447499
|
+
}
|
|
447447
447500
|
function createRetainedActivationWakeAction(input) {
|
|
447448
447501
|
return (snapshot) => {
|
|
447449
447502
|
const restoreOperationId = deleteRestoreOperationIdForApp(input.recoveryCommand, snapshot.appId);
|
|
447450
447503
|
return restartRetainedActivationWithFallback({
|
|
447451
447504
|
restoreOperationId,
|
|
447452
|
-
restart: () =>
|
|
447453
|
-
|
|
447454
|
-
|
|
447455
|
-
|
|
447456
|
-
|
|
447457
|
-
|
|
447458
|
-
|
|
447459
|
-
|
|
447460
|
-
|
|
447461
|
-
|
|
447505
|
+
restart: async () => {
|
|
447506
|
+
await observeRuntimeWakePhase(
|
|
447507
|
+
currentRuntimeWakePhaseTrace(),
|
|
447508
|
+
{ phase: "retained_activation_restart", failureReason: "phase_failed" },
|
|
447509
|
+
() => restartRetainedRuntimeActivation({
|
|
447510
|
+
db: input.db,
|
|
447511
|
+
snapshot,
|
|
447512
|
+
currentNodeId: input.currentNodeId,
|
|
447513
|
+
restoreOperationId
|
|
447514
|
+
})
|
|
447515
|
+
);
|
|
447516
|
+
return retainedActivationRestartOwnsServingRoute(restoreOperationId) ? { retainedActivationRestartCompleted: true } : void 0;
|
|
447517
|
+
},
|
|
447462
447518
|
restore: (operationId2) => input.replaceCurrentDeployment(snapshot, "restore_exact_generation", void 0, operationId2)
|
|
447463
447519
|
});
|
|
447464
447520
|
};
|
|
@@ -450353,6 +450409,35 @@ async function planExplicitRuntimeStop(input) {
|
|
|
450353
450409
|
if (!reset[0]) throw new Error("RUNTIME_EXPLICIT_STOP_RETRY_CAS_FAILED");
|
|
450354
450410
|
}
|
|
450355
450411
|
}
|
|
450412
|
+
const matchingActiveDrain = (await tx.execute(sql140`
|
|
450413
|
+
SELECT d.lifecycle_request_id, r.correlation_id, d.id AS drain_id, d.activation_id, d.instance_id FROM runtime_drain_requests d
|
|
450414
|
+
JOIN runtime_lifecycle_requests r ON r.id = d.lifecycle_request_id
|
|
450415
|
+
WHERE d.app_id = ${input.appId}
|
|
450416
|
+
AND d.activation_id = ${activation.id}
|
|
450417
|
+
AND d.instance_id = ${instance.instanceId}
|
|
450418
|
+
AND d.reason = 'app_delete'
|
|
450419
|
+
AND d.state IN ('planned', 'armed', 'dispatched')
|
|
450420
|
+
AND r.action = ${input.reason}
|
|
450421
|
+
AND (r.actor_kind = ${input.actorKind} OR (${input.reason === "app_delete" && input.actorKind === "owner" && input.correlationId.startsWith("app_delete_")} AND r.actor_kind = 'platform'))
|
|
450422
|
+
AND r.actor_id_hash = ${actorIdHash}
|
|
450423
|
+
AND (r.correlation_id = ${input.correlationId} OR (${input.reason === "app_delete" && input.correlationId.startsWith("app_delete_")} AND r.correlation_id LIKE ${`lifecycle_app_delete_${input.appId}_%`}))
|
|
450424
|
+
LIMIT 1
|
|
450425
|
+
`)).rows?.[0];
|
|
450426
|
+
if (matchingActiveDrain) {
|
|
450427
|
+
return {
|
|
450428
|
+
appId: input.appId,
|
|
450429
|
+
appName: input.appName,
|
|
450430
|
+
activationId: matchingActiveDrain.activation_id,
|
|
450431
|
+
instanceId: matchingActiveDrain.instance_id,
|
|
450432
|
+
lifecycleRequestId: matchingActiveDrain.lifecycle_request_id,
|
|
450433
|
+
drainId: matchingActiveDrain.drain_id,
|
|
450434
|
+
reason: input.reason,
|
|
450435
|
+
actorKind: input.actorKind,
|
|
450436
|
+
actorIdHash,
|
|
450437
|
+
correlationId: matchingActiveDrain.correlation_id,
|
|
450438
|
+
replayed: true
|
|
450439
|
+
};
|
|
450440
|
+
}
|
|
450356
450441
|
const activeDrains = await tx.select().from(runtimeDrainRequests).where(
|
|
450357
450442
|
and90(
|
|
450358
450443
|
eq97(runtimeDrainRequests.appId, input.appId),
|
|
@@ -453676,6 +453761,9 @@ function isDeferredActionDecision(decision) {
|
|
|
453676
453761
|
function isRuntimeArtifactTerminalization(value) {
|
|
453677
453762
|
return typeof value === "object" && value !== null && value.terminalized === true && value.terminalization === "runtime_artifact_unavailable";
|
|
453678
453763
|
}
|
|
453764
|
+
function isRetainedActivationRestartCompleted(value) {
|
|
453765
|
+
return typeof value === "object" && value !== null && value.retainedActivationRestartCompleted === true;
|
|
453766
|
+
}
|
|
453679
453767
|
async function reconcileRuntimeControlPlaneApp(input) {
|
|
453680
453768
|
const snapshot = await input.loadSnapshot(input.appId);
|
|
453681
453769
|
if (snapshot.appId !== input.appId) throw new Error("CONTROL_PLANE_SNAPSHOT_SCOPE_MISMATCH");
|
|
@@ -453710,6 +453798,9 @@ async function reconcileRuntimeControlPlaneApp(input) {
|
|
|
453710
453798
|
await input.acquireLock(input.appId, async (lockedDb) => {
|
|
453711
453799
|
await input.recordDecision?.({ appId: input.appId, ...settled }, lockedDb);
|
|
453712
453800
|
});
|
|
453801
|
+
if (deferredAction.result.decision === "restart_retained_activation" && isRetainedActivationRestartCompleted(actionResult)) {
|
|
453802
|
+
return { ...settled, retainedActivationRestartCompleted: true };
|
|
453803
|
+
}
|
|
453713
453804
|
return settled;
|
|
453714
453805
|
}
|
|
453715
453806
|
var init_runtime_reconciliation_controller = __esm({
|
|
@@ -455779,7 +455870,7 @@ function publicAppDeleteErrorCode(error51) {
|
|
|
455779
455870
|
const code = stableAppDeleteErrorCode(error51);
|
|
455780
455871
|
return PUBLICLY_NORMALIZED_APP_DELETE_CODES.has(code) ? "APP_DELETE_OPERATION_PENDING" : code;
|
|
455781
455872
|
}
|
|
455782
|
-
function appDeleteFailureWithReceipt(error51, receipt) {
|
|
455873
|
+
function appDeleteFailureWithReceipt(error51, receipt, statusQueryName = "apps.deleteStatus") {
|
|
455783
455874
|
const code = publicAppDeleteErrorCode(error51);
|
|
455784
455875
|
const restorationDiagnostic = code === "APP_DELETE_RESTORE_IN_PROGRESS" ? {
|
|
455785
455876
|
stateImpact: "The same restore receipt remains active; no replacement delete or restore operation was created.",
|
|
@@ -455795,6 +455886,13 @@ function appDeleteFailureWithReceipt(error51, receipt) {
|
|
|
455795
455886
|
retryClass: "owner-action",
|
|
455796
455887
|
userAction: "RETRY_DELETE_WITH_NEW_REQUEST",
|
|
455797
455888
|
nextAction: "RETRY_DELETE_WITH_NEW_REQUEST"
|
|
455889
|
+
} : statusQueryName === "admin.apps.deleteStatus" ? {
|
|
455890
|
+
stateImpact: "The same delete operation remains authoritative; no replacement operation was created.",
|
|
455891
|
+
retryable: false,
|
|
455892
|
+
retryPrecondition: `The administrator must POST /api/query with {"name":"${statusQueryName}","args":{"name":"<app>","operationId":"<operationId>"}} and wait for deletion to reach a terminal state.`,
|
|
455893
|
+
retryClass: "platform-action",
|
|
455894
|
+
userAction: "VIEW_DELETE_STATUS",
|
|
455895
|
+
nextAction: "VIEW_DELETE_STATUS"
|
|
455798
455896
|
} : {};
|
|
455799
455897
|
return Object.assign(new Error(code, { cause: error51 }), {
|
|
455800
455898
|
code,
|
|
@@ -458907,7 +459005,7 @@ async function bootstrapPlatformRuntime(params) {
|
|
|
458907
459005
|
const { buildAppRuntimeUnavailableResult, ensureAppRuntimeReady } = await import(resolve124(params.functionsPath, "../src/app-runtime-recovery.ts"));
|
|
458908
459006
|
const { createPublicRuntimeWakeCorrelation, executeLegacyRuntimeWakeByName } = await import(resolve124(params.functionsPath, "../src/simple-lifecycle/legacy-runtime-effect-adapter.ts"));
|
|
458909
459007
|
await initializePlatformRuntimeGenerationForServer(params.functionsPath);
|
|
458910
|
-
const { getAppPort: getLivePort, setAppPort: setAppPort2, removeAppPort: removeAppPort2, removeAppPortIfEquals: removeAppPortIfEquals2, setAppMeta: setAppMeta2, setLastAccess: markLastAccess, hasLiveRuntimeProcess: hasLiveRuntimeProcess3, resolveLiveRuntimePort: resolveLiveRuntimePort3, reapWarmPoolStale: reapWarmPoolStale2, getWakeCapacitySnapshot: getWakeCapacitySnapshot2, setWakeCapacityMaxConcurrentRunning: setWakeCapacityMaxConcurrentRunning2, setWakeCapacityUpdateInProgress: setWakeCapacityUpdateInProgress2, generateInternalToken: generateInternalToken3, setDatabasePreflightRawSql: setDatabasePreflightRawSql2, setPlatformRuntimeDb: setPlatformRuntimeDb2, buildAppDatabaseUrl: buildAppDatabaseUrl2 } = await import(resolve124(params.functionsPath, "../src/provisioner.ts"));
|
|
459008
|
+
const { getAppPort: getLivePort, setAppPort: setAppPort2, removeAppPort: removeAppPort2, removeAppPortIfEquals: removeAppPortIfEquals2, setAppMeta: setAppMeta2, setLastAccess: markLastAccess, hasLiveRuntimeProcess: hasLiveRuntimeProcess3, resolveLiveRuntimePort: resolveLiveRuntimePort3, reapWarmPoolStale: reapWarmPoolStale2, getWakeCapacitySnapshot: getWakeCapacitySnapshot2, setWakeCapacityMaxConcurrentRunning: setWakeCapacityMaxConcurrentRunning2, setWakeCapacityUpdateInProgress: setWakeCapacityUpdateInProgress2, generateInternalToken: generateInternalToken3, setDatabasePreflightRawSql: setDatabasePreflightRawSql2, setPlatformRuntimeDb: setPlatformRuntimeDb2, buildAppDatabaseUrl: buildAppDatabaseUrl2 } = await loadPlatformProvisionerWithScheduledJobSigningSecret(() => import(resolve124(params.functionsPath, "../src/provisioner.ts")));
|
|
458911
459009
|
setDatabasePreflightRawSql2(params.rawSql);
|
|
458912
459010
|
wirePlatformDatabaseUrls(setPlatformRuntimeDb2, params.db, params.databaseUrlResolvers);
|
|
458913
459011
|
const storageDirectRuntime = await createPlatformStorageDirectRuntime(params.functionsPath, buildAppDatabaseUrl2);
|
|
@@ -459336,6 +459434,7 @@ var init_server_platform_runtime_bootstrap = __esm({
|
|
|
459336
459434
|
init_server_platform_control_plane_observer_runtime();
|
|
459337
459435
|
init_server_platform_cron_scheduler();
|
|
459338
459436
|
init_server_scheduled_jobs_runtime();
|
|
459437
|
+
init_platform_scheduled_job_secret();
|
|
459339
459438
|
init_server_execution();
|
|
459340
459439
|
init_server_wake_signal_diagnostics_route();
|
|
459341
459440
|
init_server_platform_worker_diagnostics();
|
|
@@ -465643,6 +465742,7 @@ var SAFE_DIAGNOSTIC_STATE_IMPACTS = /* @__PURE__ */ new Set([
|
|
|
465643
465742
|
"Runtime reconciliation made no state change; the existing delete operation remains authoritative.",
|
|
465644
465743
|
"The failed delete receipt remains unchanged; the app runtime was not started.",
|
|
465645
465744
|
"The same restore receipt remains active; no replacement delete or restore operation was created.",
|
|
465745
|
+
"The same delete operation remains authoritative; no replacement operation was created.",
|
|
465646
465746
|
"The previous restore is complete; this request did not create a delete operation.",
|
|
465647
465747
|
"No matching upload grant was found; no storage catalog object was finalized.",
|
|
465648
465748
|
"The upload grant is expired or aborted; no storage catalog object was finalized.",
|
|
@@ -465666,6 +465766,7 @@ var SAFE_DIAGNOSTIC_RETRY_PRECONDITIONS = /* @__PURE__ */ new Set([
|
|
|
465666
465766
|
"Refresh the app list before retrying with the same request ID.",
|
|
465667
465767
|
"Refresh the app list to confirm whether the app is already removed.",
|
|
465668
465768
|
'The owner must POST /api/query with {"name":"apps.deleteStatus","args":{"name":"<app>","operationId":"<operationId>"}} and wait for deletion to reach a terminal state.',
|
|
465769
|
+
'The administrator must POST /api/query with {"name":"admin.apps.deleteStatus","args":{"name":"<app>","operationId":"<operationId>"}} and wait for deletion to reach a terminal state.',
|
|
465669
465770
|
"Wait until the stated runtime or serving-route condition is clear, then retry the same owner wake/resume operation.",
|
|
465670
465771
|
"Wait until the active app deployment attempt finishes and its lease clears; then retry the same owner wake.",
|
|
465671
465772
|
"Wait until this restore receipt leaves restoring; then check the app status.",
|