run402-mcp 4.122.0 → 4.122.1
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/dist/docs/llms-sdk.txt +3 -3
- package/package.json +3 -3
- package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
- package/sdk/dist/namespaces/deploy.js +84 -31
- package/sdk/dist/namespaces/deploy.js.map +1 -1
- package/sdk/dist/namespaces/projects.d.ts +6 -2
- package/sdk/dist/namespaces/projects.d.ts.map +1 -1
- package/sdk/dist/namespaces/projects.js +46 -31
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/projects.types.d.ts +14 -8
- package/sdk/dist/namespaces/projects.types.d.ts.map +1 -1
- package/sdk/dist/stale-project-key.d.ts +14 -0
- package/sdk/dist/stale-project-key.d.ts.map +1 -0
- package/sdk/dist/stale-project-key.js +88 -0
- package/sdk/dist/stale-project-key.js.map +1 -0
- package/sdk/dist/type-contract.d.ts.map +1 -1
- package/sdk/dist/type-contract.js.map +1 -1
package/dist/docs/llms-sdk.txt
CHANGED
|
@@ -731,9 +731,9 @@ const info = await r.projects.info(projectId);
|
|
|
731
731
|
|
|
732
732
|
## Rehearsals, snapshots, and branches
|
|
733
733
|
|
|
734
|
-
Rehearsal is automatic inside `apply()` and `r.up()`: when the plan's `rehearsal.available` is true (migrations, and the project has a live release), the plan is rehearsed on a contained branch and committed only on a passing report, bound to the report's `required_plan`. The result carries `rehearsal: { status: "passed", report, operation_id, branch_project_id }`, or `{ status: "skipped", reason: "no_live_release" | "no_migrations" | "migrations_unchanged" | "ci_session_unsupported" | "disabled" | "reviewed_plan" | "unsupported" }` (`migrations_unchanged`: every migration is already applied with an identical checksum — `plan.migrations.new` is empty — so nothing is rehearsed and a page-only redeploy ships in seconds; `ci_session_unsupported`: the plan was made by a CI session, which the gateway does not rehearse, so it commits directly). A failed rehearsal throws `Run402DeployError` with `code: "REHEARSAL_FAILED"` (the report is in `body.rehearsal`); nothing is committed. Pass `{ noRehearse: true }` to skip it. `onEvent` sees `rehearsal.started`, `rehearsal.finished`, and `rehearsal.skipped`.
|
|
734
|
+
Rehearsal is automatic inside `apply()` and `r.up()`: when the plan's `rehearsal.available` is true (migrations, and the project has a live release), the plan is rehearsed on a contained branch and committed only on a passing report, bound to the report's `required_plan`. The result carries `rehearsal: { status: "passed", report, operation_id, branch_project_id }`, or `{ status: "skipped", reason: "no_live_release" | "no_migrations" | "migrations_unchanged" | "ci_session_unsupported" | "disabled" | "reviewed_plan" | "unsupported" }` (`migrations_unchanged`: every migration is already applied with an identical checksum — `plan.migrations.new` is empty — so nothing is rehearsed and a page-only redeploy ships in seconds; `ci_session_unsupported`: the plan was made by a CI session, which the gateway does not rehearse, so it commits directly). A failed rehearsal throws `Run402DeployError` with `code: "REHEARSAL_FAILED"` (the report is in `body.rehearsal`); nothing is committed. Pass `{ noRehearse: true }` to skip it. The rehearsal runs on the gateway as an operation, not inside one request: the SDK starts it with `Prefer: respond-async` and polls the operation until its report is there, so a slow snapshot never holds a request open; a rehearsal whose gateway task stopped before it finished throws `OPERATION_INTERRUPTED` (retryable, `operationId` set, the branch it left in `body.details.branch_project_id`). `onEvent` sees `rehearsal.started`, the recorded `rehearsal.phase` events, `rehearsal.finished`, and `rehearsal.skipped`.
|
|
735
735
|
|
|
736
|
-
The primitive is still there for experts: `r.project(id).apply.rehearse(planId, { teardown })` rehearses an already-persisted plan without committing (a project with no live release rehearses on an EMPTY branch — never a refusal).
|
|
736
|
+
The primitive is still there for experts: `r.project(id).apply.rehearse(planId, { teardown })` rehearses an already-persisted plan without committing and polls its operation for the report (a project with no live release rehearses on an EMPTY branch — never a refusal).
|
|
737
737
|
|
|
738
738
|
Manual restore points are exposed as `r.snapshots` and the scoped `r.project(id).snapshots`. `restorePlan()` is the no-mutation loss-statement step; `restore()` requires the confirm token from the plan and performs the atomic offline-materialize-then-flip restore. Auth users/passkeys are restored only with `{ includeAuth: true }`; sessions and tokens are never restored. Give a restore point a name with `create(projectId, { label: "before engine upgrade", metadata })`; labels live outside the project's database, so they survive restores. `{ release: "snapshot" }` on both `restorePlan` and `restore` also puts back the release that was live at capture (and re-deploys every function that differs to its capture-time code). A deployed function can do all of this for its own project with the `@run402/functions` `snapshots` namespace and its service key.
|
|
739
739
|
|
|
@@ -1434,7 +1434,7 @@ demoteUser(id, email): Promise<void>
|
|
|
1434
1434
|
|
|
1435
1435
|
`r.projects.setRepoName(projectId, name)` sets or renames the project's per-org-unique, ADDRESS-form name (`POST /projects/v1/:id/repo-name`, repo-first-onramp design D6, task 4.2) — the `<name>` half of `run402::<org-slug>/<name>` — and returns `{ project_id, repo_name, previous_repo_name }`. Distinct from `rename` above (the free-text display name, unchanged): the address-form name is charset-restricted (`[a-z0-9-]`, ≤63 chars) and per-org-unique. No fee, unlike the org-slug claim. Same authority as `rename`. Scoped form: `r.project(id).setRepoName(name)`.
|
|
1436
1436
|
|
|
1437
|
-
`r.projects.getUsage(id)`
|
|
1437
|
+
`r.projects.getUsage(id)` (`GET /projects/v1/admin/:project_id/usage`, service key) returns a `UsageReport`: `project_id`, `tier`, `org_id` (`| null`), `api_calls`, `api_calls_limit`, `storage_bytes`, `storage_limit_bytes`, `storage_limit` (human string, e.g. `"250 MB"`), `status` (the derived effective status), `lease_expires_at`, and `lease_perpetual`. `lease_expires_at` is the owning organization's ISO-8601 lease expiry, and `null` when the lease is perpetual (`lease_perpetual: true`) or the project has no organization; `lease_perpetual` is `false` otherwise. The lease fields match `r.tier.status()`.
|
|
1438
1438
|
|
|
1439
1439
|
### `r.project(id).apply`
|
|
1440
1440
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "run402-mcp",
|
|
3
|
-
"version": "4.122.
|
|
3
|
+
"version": "4.122.1",
|
|
4
4
|
"packageManager": "npm@11.19.0",
|
|
5
5
|
"mcpName": "com.run402/mcp",
|
|
6
6
|
"description": "MCP server for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.",
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
"test:skill": "node scripts/build-agent-skills-index.mjs --check && node --test --import tsx SKILL.test.ts buzz/setup.test.mjs buzz/helper.test.mjs buzz/package.test.mjs scripts/build-agent-skills-index.test.mjs",
|
|
33
33
|
"test:vault-surface": "node scripts/build-vault-surface.mjs --check && node --test scripts/build-vault-surface.test.mjs",
|
|
34
34
|
"test:buzz-installer": "node buzz/install-smoke.mjs",
|
|
35
|
-
"test:sync": "node --test --import tsx sync.test.ts examples/tenant-x402-wallet-stats/example.test.mjs examples/static-config-js/example.test.mjs",
|
|
35
|
+
"test:sync": "node --test --import tsx sync.test.ts scripts/types-node-engines.test.mjs examples/tenant-x402-wallet-stats/example.test.mjs examples/static-config-js/example.test.mjs",
|
|
36
36
|
"test:run402-app-schema-drift": "node scripts/check-run402-app-schema-drift.mjs",
|
|
37
37
|
"test:docs": "node scripts/check-typed-config-docs.mjs && npm run check:docs --workspace=@run402/sdk -- --skip-build && npm run test:docs:quality",
|
|
38
|
-
"test": "npm run build && node scripts/build-agent-skills-index.mjs --check && node scripts/build-vault-surface.mjs --check && node --experimental-test-module-mocks --test --import tsx SKILL.test.ts sync.test.ts buzz/setup.test.mjs buzz/helper.test.mjs buzz/package.test.mjs scripts/build-agent-skills-index.test.mjs scripts/build-agent-flat-docs.test.mjs scripts/build-vault-surface.test.mjs examples/tenant-x402-wallet-stats/example.test.mjs examples/static-config-js/example.test.mjs 'core/src/**/*.test.ts' 'sdk/src/**/*.test.ts' 'src/**/*.test.ts' && node --test cli-core-target.test.mjs mcp-vendored-sdk-imports.test.mjs scripts/publish-idempotent.test.mjs && node --experimental-test-module-mocks --test cli-up-argv.test.mjs cli-up-repo-compose.test.mjs cli-repos-surface.test.mjs cli-repos-naming.test.mjs cli-projects-vault-scaffold.test.mjs cli-identity-link.test.mjs cli-identity-link-matrix.test.mjs cli-redeem.test.mjs cli-rooms-context-session-key.test.mjs cli-messages-wait.test.mjs cli-rooms-invite-key-leak.test.mjs cli-rooms-join-arrival.test.mjs cli/lib/buzz.test.mjs && node --test cli-help-listing.test.mjs cli-module-references.test.mjs cli-output-contract.test.mjs cli-json-noop-contract.test.mjs cli-e2e.test.mjs cli-help.test.mjs cli-subscriptions-help.test.mjs cli-provision-active.test.mjs cli-typed-config-modes.test.mjs cli-argv.test.mjs cli-repos-naming.test.mjs cli-conventions-gate.test.mjs cli-doctor.test.mjs cli-rest-diagnostics.test.mjs cli-credentials.test.mjs paid-stack-deps.test.mjs settlement-disclosure.test.mjs cli-suggestions-gate.test.mjs cli-env.test.mjs cli-wallets.test.mjs cli-org.test.mjs cli-org-context.test.mjs cli-image-org.test.mjs cli-harness-context.test.mjs cli-ci.test.mjs cli-snapshots.test.mjs cli-deploy-ci.test.mjs cli-login-loopback.test.mjs cli-vault-remote-helper.test.mjs cli-vault-daemon.test.mjs cli-deploy-vault-lane.test.mjs cli/lib/buzz-doctor.test.mjs cli/lib/diagnostic-telemetry.test.mjs cli/lib/doctor-source-scan.test.mjs cli/lib/errors.test.mjs cli/lib/argparse.test.mjs cli/lib/path-lookup.test.mjs cli/lib/pay.test.mjs cli/lib/up.test.mjs cli/lib/deploy-rehearse-discovery.test.mjs cli/lib/update-check.test.mjs cli-bootstrap-next-actions.test.mjs cli-sdk-errors-room-invite-hint.test.mjs && npm run test:docs",
|
|
38
|
+
"test": "npm run build && node scripts/build-agent-skills-index.mjs --check && node scripts/build-vault-surface.mjs --check && node --experimental-test-module-mocks --test --import tsx SKILL.test.ts sync.test.ts buzz/setup.test.mjs buzz/helper.test.mjs buzz/package.test.mjs scripts/build-agent-skills-index.test.mjs scripts/build-agent-flat-docs.test.mjs scripts/build-vault-surface.test.mjs scripts/types-node-engines.test.mjs examples/tenant-x402-wallet-stats/example.test.mjs examples/static-config-js/example.test.mjs 'core/src/**/*.test.ts' 'sdk/src/**/*.test.ts' 'src/**/*.test.ts' && node --test cli-core-target.test.mjs mcp-vendored-sdk-imports.test.mjs scripts/publish-idempotent.test.mjs && node --experimental-test-module-mocks --test cli-up-argv.test.mjs cli-up-repo-compose.test.mjs cli-repos-surface.test.mjs cli-repos-naming.test.mjs cli-projects-vault-scaffold.test.mjs cli-identity-link.test.mjs cli-identity-link-matrix.test.mjs cli-redeem.test.mjs cli-rooms-context-session-key.test.mjs cli-messages-wait.test.mjs cli-rooms-invite-key-leak.test.mjs cli-rooms-join-arrival.test.mjs cli/lib/buzz.test.mjs && node --test cli-help-listing.test.mjs cli-module-references.test.mjs cli-output-contract.test.mjs cli-json-noop-contract.test.mjs cli-e2e.test.mjs cli-help.test.mjs cli-subscriptions-help.test.mjs cli-provision-active.test.mjs cli-typed-config-modes.test.mjs cli-argv.test.mjs cli-repos-naming.test.mjs cli-conventions-gate.test.mjs cli-doctor.test.mjs cli-rest-diagnostics.test.mjs cli-credentials.test.mjs paid-stack-deps.test.mjs settlement-disclosure.test.mjs cli-suggestions-gate.test.mjs cli-env.test.mjs cli-wallets.test.mjs cli-org.test.mjs cli-org-context.test.mjs cli-image-org.test.mjs cli-harness-context.test.mjs cli-ci.test.mjs cli-snapshots.test.mjs cli-deploy-ci.test.mjs cli-login-loopback.test.mjs cli-vault-remote-helper.test.mjs cli-vault-daemon.test.mjs cli-deploy-vault-lane.test.mjs cli/lib/buzz-doctor.test.mjs cli/lib/diagnostic-telemetry.test.mjs cli/lib/doctor-source-scan.test.mjs cli/lib/errors.test.mjs cli/lib/argparse.test.mjs cli/lib/path-lookup.test.mjs cli/lib/pay.test.mjs cli/lib/up.test.mjs cli/lib/deploy-rehearse-discovery.test.mjs cli/lib/update-check.test.mjs cli-bootstrap-next-actions.test.mjs cli-sdk-errors-room-invite-hint.test.mjs && npm run test:docs",
|
|
39
39
|
"test:e2e": "npm run build && node --test cli-core-target.test.mjs && node --experimental-test-module-mocks --test cli-repos-surface.test.mjs cli-repos-naming.test.mjs cli-up-repo-compose.test.mjs cli-projects-vault-scaffold.test.mjs cli-identity-link.test.mjs cli-identity-link-matrix.test.mjs cli-redeem.test.mjs cli-rooms-context-session-key.test.mjs cli-messages-wait.test.mjs cli-rooms-invite-key-leak.test.mjs cli-rooms-join-arrival.test.mjs && node --test cli-help-listing.test.mjs cli-module-references.test.mjs cli-output-contract.test.mjs cli-json-noop-contract.test.mjs cli-e2e.test.mjs cli-help.test.mjs cli-subscriptions-help.test.mjs cli-provision-active.test.mjs cli-typed-config-modes.test.mjs cli-argv.test.mjs cli-repos-naming.test.mjs cli-conventions-gate.test.mjs cli-doctor.test.mjs cli-rest-diagnostics.test.mjs cli-credentials.test.mjs paid-stack-deps.test.mjs settlement-disclosure.test.mjs cli-suggestions-gate.test.mjs cli-env.test.mjs cli-wallets.test.mjs cli-org.test.mjs cli-org-context.test.mjs cli-image-org.test.mjs cli-harness-context.test.mjs cli-ci.test.mjs cli-snapshots.test.mjs cli-deploy-ci.test.mjs cli-login-loopback.test.mjs cli-vault-remote-helper.test.mjs cli-vault-daemon.test.mjs cli-deploy-vault-lane.test.mjs cli/lib/buzz-doctor.test.mjs cli/lib/diagnostic-telemetry.test.mjs cli/lib/doctor-source-scan.test.mjs cli/lib/errors.test.mjs cli/lib/path-lookup.test.mjs cli/lib/up.test.mjs cli/lib/update-check.test.mjs cli-bootstrap-next-actions.test.mjs cli-sdk-errors-room-invite-hint.test.mjs",
|
|
40
40
|
"test:help": "npm run build && node --test cli-help.test.mjs",
|
|
41
41
|
"test:integration": "node --test --import tsx core/src/siwx-integration.integ.ts",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/namespaces/deploy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAwB3C,OAAO,KAAK,EACV,YAAY,EACZ,sBAAsB,EAQtB,WAAW,EACX,oBAAoB,EAEpB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EAIvB,sBAAsB,EACtB,oBAAoB,EAUpB,iBAAiB,EAGjB,YAAY,EACZ,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,2BAA2B,EAC3B,uBAAuB,EACvB,WAAW,EACX,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAGlB,YAAY,EAEb,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/namespaces/deploy.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,6 +33,9 @@ const COMMIT_POLL_INITIAL_MS = 1_000;
|
|
|
33
33
|
const COMMIT_POLL_MAX_MS = 30_000;
|
|
34
34
|
const COMMIT_POLL_BACKOFF_AFTER_MS = 30_000;
|
|
35
35
|
const COMMIT_POLL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
36
|
+
const REHEARSAL_POLL_INITIAL_MS = 1_000;
|
|
37
|
+
const REHEARSAL_POLL_MAX_MS = 10_000;
|
|
38
|
+
const REHEARSAL_POLL_TIMEOUT_MS = 30 * 60 * 1000;
|
|
36
39
|
const APPLY_RETRY_DEFAULT_MAX_RETRIES = 2;
|
|
37
40
|
const APPLY_RETRY_BASE_DELAY_MS = 250;
|
|
38
41
|
const APPLY_RETRY_MAX_DELAY_MS = 2_000;
|
|
@@ -221,25 +224,7 @@ export class Deploy {
|
|
|
221
224
|
const body = {};
|
|
222
225
|
if (opts.teardown !== undefined)
|
|
223
226
|
body.teardown = opts.teardown;
|
|
224
|
-
|
|
225
|
-
return await this.client.request(`/apply/v1/plans/${encodeURIComponent(planId)}/rehearse`, {
|
|
226
|
-
method: "POST",
|
|
227
|
-
body,
|
|
228
|
-
...(opts.project
|
|
229
|
-
? {
|
|
230
|
-
authMeta: {
|
|
231
|
-
method: "deploy.rehearse",
|
|
232
|
-
capability: "project.deploy",
|
|
233
|
-
target: { project_id: opts.project },
|
|
234
|
-
},
|
|
235
|
-
}
|
|
236
|
-
: {}),
|
|
237
|
-
context: "rehearsing deploy plan",
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
catch (err) {
|
|
241
|
-
throw translateDeployError(err, "rehearsal", planId, null);
|
|
242
|
-
}
|
|
227
|
+
return await requestRehearsal(this.client, planId, body, opts.project);
|
|
243
228
|
}
|
|
244
229
|
/**
|
|
245
230
|
* Resume an operation in `schema_settling` or `activation_pending`. The
|
|
@@ -610,18 +595,7 @@ async function rehearseBeforeCommit(client, plan, planId, project, opts, emit) {
|
|
|
610
595
|
if (unchanged)
|
|
611
596
|
return skip(unchanged);
|
|
612
597
|
emit({ type: "rehearsal.started", planId });
|
|
613
|
-
|
|
614
|
-
try {
|
|
615
|
-
rehearsed = await client.request(`/apply/v1/plans/${encodeURIComponent(planId)}/rehearse`, {
|
|
616
|
-
method: "POST",
|
|
617
|
-
body: {},
|
|
618
|
-
authMeta: { method: "deploy.rehearse", capability: "project.deploy", target: { project_id: project } },
|
|
619
|
-
context: "rehearsing deploy plan",
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
catch (err) {
|
|
623
|
-
throw translateDeployError(err, "rehearsal", planId, null);
|
|
624
|
-
}
|
|
598
|
+
const rehearsed = await requestRehearsal(client, planId, {}, project, emit);
|
|
625
599
|
const report = rehearsed.report;
|
|
626
600
|
emit({
|
|
627
601
|
type: "rehearsal.finished",
|
|
@@ -653,6 +627,85 @@ async function rehearseBeforeCommit(client, plan, planId, project, opts, emit) {
|
|
|
653
627
|
...(bound ? { requiredPlan: { planId: bound.plan_id, planFingerprint: bound.plan_fingerprint } } : {}),
|
|
654
628
|
};
|
|
655
629
|
}
|
|
630
|
+
/**
|
|
631
|
+
* Start a rehearsal and return its report. The gateway runs a rehearsal as an
|
|
632
|
+
* operation, detached from the request (a snapshot can take minutes). With a
|
|
633
|
+
* project to poll as, the request asks for the asynchronous shape
|
|
634
|
+
* (`Prefer: respond-async`), gets the operation at once, and polls it until
|
|
635
|
+
* the report is there, relaying its recorded `rehearsal.phase` events; every
|
|
636
|
+
* failure from then on carries the rehearsal's operation id. A gateway that
|
|
637
|
+
* answers with the report inline is used as is, and without a project the
|
|
638
|
+
* response waits for the report.
|
|
639
|
+
*/
|
|
640
|
+
async function requestRehearsal(client, planId, body, project, emit) {
|
|
641
|
+
let started;
|
|
642
|
+
try {
|
|
643
|
+
started = await client.request(`/apply/v1/plans/${encodeURIComponent(planId)}/rehearse`, {
|
|
644
|
+
method: "POST",
|
|
645
|
+
body,
|
|
646
|
+
...(project
|
|
647
|
+
? {
|
|
648
|
+
headers: { Prefer: "respond-async" },
|
|
649
|
+
authMeta: {
|
|
650
|
+
method: "deploy.rehearse",
|
|
651
|
+
capability: "project.deploy",
|
|
652
|
+
target: { project_id: project },
|
|
653
|
+
},
|
|
654
|
+
}
|
|
655
|
+
: {}),
|
|
656
|
+
context: "rehearsing deploy plan",
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
catch (err) {
|
|
660
|
+
throw translateDeployError(err, "rehearsal", planId, null);
|
|
661
|
+
}
|
|
662
|
+
if (started.report || !project)
|
|
663
|
+
return started;
|
|
664
|
+
return await pollRehearsal(client, planId, started.operation_id, project, emit);
|
|
665
|
+
}
|
|
666
|
+
async function pollRehearsal(client, planId, operationId, project, emit) {
|
|
667
|
+
const pollUrl = `/apply/v1/operations/${encodeURIComponent(operationId)}`;
|
|
668
|
+
const headers = await apikeyHeaders(client, project);
|
|
669
|
+
const start = Date.now();
|
|
670
|
+
let interval = REHEARSAL_POLL_INITIAL_MS;
|
|
671
|
+
let eventCursor = null;
|
|
672
|
+
let eventsSupported = Boolean(emit);
|
|
673
|
+
for (;;) {
|
|
674
|
+
await sleep(interval);
|
|
675
|
+
let snapshot;
|
|
676
|
+
try {
|
|
677
|
+
// A poll lost to a 502/503/504 (a gateway rollout) says nothing about
|
|
678
|
+
// the rehearsal, which keeps running on the gateway; read it again.
|
|
679
|
+
snapshot = await requestWithTransientRetry(() => client.request(pollUrl, { headers, context: "polling rehearsal operation" }));
|
|
680
|
+
}
|
|
681
|
+
catch (err) {
|
|
682
|
+
throw translateDeployError(err, "rehearsal", planId, operationId);
|
|
683
|
+
}
|
|
684
|
+
if (emit && eventsSupported) {
|
|
685
|
+
const recorded = await emitRecordedOperationEvents(client, operationId, headers, eventCursor, emit);
|
|
686
|
+
eventsSupported = recorded.supported;
|
|
687
|
+
eventCursor = recorded.cursor;
|
|
688
|
+
}
|
|
689
|
+
if (TERMINAL_STATUSES.includes(snapshot.status)) {
|
|
690
|
+
if (snapshot.rehearsal_report) {
|
|
691
|
+
return { operation_id: operationId, status: snapshot.status, poll_url: pollUrl, report: snapshot.rehearsal_report };
|
|
692
|
+
}
|
|
693
|
+
throw translateGatewayError(snapshot.error, "rehearsal", planId, operationId);
|
|
694
|
+
}
|
|
695
|
+
if (Date.now() - start > REHEARSAL_POLL_TIMEOUT_MS) {
|
|
696
|
+
throw new Run402DeployError(`Timed out waiting for rehearsal operation ${operationId} to finish`, {
|
|
697
|
+
code: "INTERNAL_ERROR",
|
|
698
|
+
phase: "rehearsal",
|
|
699
|
+
retryable: true,
|
|
700
|
+
operationId,
|
|
701
|
+
planId,
|
|
702
|
+
status: 504,
|
|
703
|
+
context: "polling rehearsal",
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
interval = Math.min(Math.floor(interval * 1.5), REHEARSAL_POLL_MAX_MS);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
656
709
|
/**
|
|
657
710
|
* `"migrations_unchanged"` when the plan carries modern migration buckets and
|
|
658
711
|
* none are new (all noop); `"no_migrations"` when the buckets are both empty;
|