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.
@@ -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)` still surfaces `effective_status` and `organization_lifecycle_state` because that endpoint scopes to a single project and the derivation collapses per-project `archived_at` / `deleted_at` together with the organization's lifecycle.
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.0",
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":"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"}
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
- try {
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
- let rehearsed;
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;