run402 4.109.1 → 4.110.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/README.md CHANGED
@@ -253,7 +253,7 @@ import { db, adminDb, auth, email, ai } from "@run402/functions";
253
253
 
254
254
  ### Same-origin web routes
255
255
 
256
- `run402 deploy` accepts `site.public_paths` for clean static browser URLs and `routes.replace` for function ingress or exact method-aware static aliases. Release asset paths such as `events.html` are distinct from public paths such as `/events`: prefer `{ "site": { "replace": { "events.html": { "data": "<h1>Events</h1>" } }, "public_paths": { "mode": "explicit", "replace": { "/events": { "asset": "events.html" } } } } }` for ordinary clean static URLs. In explicit mode `/events.html` is not public unless separately declared; `mode: "implicit"` restores filename-derived reachability and can widen access. `site.embedding.frame_ancestors` (catalog keys, e.g. `["localhost"]`, never raw origins) lets the named embedders put the site in an iframe: the gateway sends `Content-Security-Policy: frame-ancestors http://localhost:* http://127.0.0.1:*` and drops `X-Frame-Options` on every response of the host; omitted on a later apply carries the previous declaration forward, `null` returns to deny, unknown keys fail `INVALID_SPEC` naming the valid ones, and `/_run402/config.json` discloses the effective policy cross-origin so an embedder can check before framing. `site.sign_in_path` (e.g. `"/join"`) names the app's own same-origin sign-in page: a signed-out user reaching MCP authorization (`/_run402/oauth/authorize`) is sent there with `returnTo` appended instead of the hosted `/auth/sign-in`; omitted carries forward, `null` returns to the hosted page, and a value with a query/fragment, `//`, `://`, or a `/_run402/` or `/auth/` prefix fails `INVALID_SPEC` on `site.sign_in_path`. `routes.replace` is an array of route entries, not a path-keyed map. Use exact `/admin` plus final-wildcard `/admin/*` for a routed section root, narrow `/api/*` methods such as `["GET","POST","OPTIONS"]`, POST-only `/login` function routes, and exact static route targets like `{ "pattern": "/events", "methods": ["GET","HEAD"], "target": { "type": "static", "file": "events.html" } }` only for route-table alias behavior. Static route `file` is a release asset path, not a public path, URL, CAS hash, rewrite, or redirect. Recipe — static home page + SPA shell: a root alias `{ "pattern": "/", "target": { "type": "static", "file": "home.html" } }` (with `home.html` at the site root) serves real static bytes at `GET /` (`route_static_alias`) while unmatched app routes keep the `index.html` shell (`spa_fallback`); expect non-blocking `STATIC_ALIAS_SHADOWS_STATIC_PATH` (warn) / `STATIC_ALIAS_DUPLICATE_CANONICAL_URL` (info) plan lints and verify with `run402 deploy resolve --url https://<your-site>/ --method GET`. Routed functions use Node 22 Fetch Request -> Response; `req.url` is the full public URL on managed subdomains, deployment hosts, and verified custom domains. Direct `/functions/v1/:name` remains API-key protected. Known resolve literals include `host_missing`, `manifest_missing`, `active_release_missing`, `unsupported_manifest_version`, `path_error`, `none`, `static_exact`, `static_index`, `spa_fallback`, `spa_fallback_missing`, `route_function`, `route_static_alias`, and `route_method_miss`; known `authorization_result` values include `authorized`, `not_public`, `not_applicable`, `manifest_missing`, `target_missing`, `active_release_missing`, `unsupported_manifest_version`, `path_error`, `missing_cas_object`, `unfinalized_or_deleting_cas_object`, `size_mismatch`, and `unauthorized_cas_object`. Known `fallback_state` values include `active_release_missing`, `unsupported_manifest_version`, and `negative_cache_hit`; preserve unknown future strings. Release inventories may include `static_public_paths` with `public_path`, `asset_path`, `reachability_authority`, and `direct`; resolve may return the same reachability fields plus `authorization_result`, `cas_object`, `response_variant`, and route/static fields like `allow`, `route_pattern`, `target_type`, `target_name`, and `target_file`. Static route target warnings include `STATIC_ALIAS_SHADOWS_STATIC_PATH`, `STATIC_ALIAS_RELATIVE_ASSET_RISK`, `STATIC_ALIAS_DUPLICATE_CANONICAL_URL`, `STATIC_ALIAS_EXTENSIONLESS_NON_HTML`, and `STATIC_ALIAS_TABLE_NEAR_LIMIT`; inspect active routes, `static_public_paths`, and the backing `asset_path`. Runtime route failure codes to branch on: `ROUTE_MANIFEST_LOAD_FAILED`, `ROUTED_INVOKE_WORKER_SECRET_MISSING`, `ROUTED_INVOKE_AUTH_FAILED`, `ROUTED_ROUTE_STALE`, `ROUTE_METHOD_NOT_ALLOWED`, and `ROUTED_RESPONSE_TOO_LARGE`.
256
+ `run402 deploy` accepts `site.public_paths` for clean static browser URLs and `routes.replace` for function ingress or exact method-aware static aliases. Release asset paths such as `events.html` are distinct from public paths such as `/events`: prefer `{ "site": { "replace": { "events.html": { "data": "<h1>Events</h1>" } }, "public_paths": { "mode": "explicit", "replace": { "/events": { "asset": "events.html" } } } } }` for ordinary clean static URLs. In explicit mode `/events.html` is not public unless separately declared; `mode: "implicit"` restores filename-derived reachability and can widen access. `site.embedding.frame_ancestors` (catalog keys, e.g. `["localhost"]`, never raw origins) lets the named embedders put the site in an iframe: the gateway sends `Content-Security-Policy: frame-ancestors http://localhost:* http://127.0.0.1:*` and drops `X-Frame-Options` on every response of the host; omitted on a later apply carries the previous declaration forward, `null` returns to deny, unknown keys fail `INVALID_SPEC` naming the valid ones, and `/_run402/config.json` discloses the effective policy cross-origin so an embedder can check before framing. `site.sign_in_path` (e.g. `"/join"`) names the app's own same-origin sign-in page: a signed-out user reaching MCP authorization (`/_run402/oauth/authorize`) is sent there with `returnTo` appended instead of the hosted `/auth/sign-in`; omitted carries forward, `null` returns to the hosted page, and a value with a query/fragment, `//`, `://`, or a `/_run402/` or `/auth/` prefix fails `INVALID_SPEC` on `site.sign_in_path`. `site.noindex: true` adds `X-Robots-Tag: noindex, nofollow, nosnippet` to every response of the host; omitted carries forward, `false` or `null` makes the host indexable again, and anything but a boolean or `null` fails `INVALID_SPEC` on `site.noindex`. `routes.replace` is an array of route entries, not a path-keyed map. Use exact `/admin` plus final-wildcard `/admin/*` for a routed section root, narrow `/api/*` methods such as `["GET","POST","OPTIONS"]`, POST-only `/login` function routes, and exact static route targets like `{ "pattern": "/events", "methods": ["GET","HEAD"], "target": { "type": "static", "file": "events.html" } }` only for route-table alias behavior. Static route `file` is a release asset path, not a public path, URL, CAS hash, rewrite, or redirect. Recipe — static home page + SPA shell: a root alias `{ "pattern": "/", "target": { "type": "static", "file": "home.html" } }` (with `home.html` at the site root) serves real static bytes at `GET /` (`route_static_alias`) while unmatched app routes keep the `index.html` shell (`spa_fallback`); expect non-blocking `STATIC_ALIAS_SHADOWS_STATIC_PATH` (warn) / `STATIC_ALIAS_DUPLICATE_CANONICAL_URL` (info) plan lints and verify with `run402 deploy resolve --url https://<your-site>/ --method GET`. Routed functions use Node 22 Fetch Request -> Response; `req.url` is the full public URL on managed subdomains, deployment hosts, and verified custom domains. Direct `/functions/v1/:name` remains API-key protected. Known resolve literals include `host_missing`, `manifest_missing`, `active_release_missing`, `unsupported_manifest_version`, `path_error`, `none`, `static_exact`, `static_index`, `spa_fallback`, `spa_fallback_missing`, `route_function`, `route_static_alias`, and `route_method_miss`; known `authorization_result` values include `authorized`, `not_public`, `not_applicable`, `manifest_missing`, `target_missing`, `active_release_missing`, `unsupported_manifest_version`, `path_error`, `missing_cas_object`, `unfinalized_or_deleting_cas_object`, `size_mismatch`, and `unauthorized_cas_object`. Known `fallback_state` values include `active_release_missing`, `unsupported_manifest_version`, and `negative_cache_hit`; preserve unknown future strings. Release inventories may include `static_public_paths` with `public_path`, `asset_path`, `reachability_authority`, and `direct`; resolve may return the same reachability fields plus `authorization_result`, `cas_object`, `response_variant`, and route/static fields like `allow`, `route_pattern`, `target_type`, `target_name`, and `target_file`. Static route target warnings include `STATIC_ALIAS_SHADOWS_STATIC_PATH`, `STATIC_ALIAS_RELATIVE_ASSET_RISK`, `STATIC_ALIAS_DUPLICATE_CANONICAL_URL`, `STATIC_ALIAS_EXTENSIONLESS_NON_HTML`, and `STATIC_ALIAS_TABLE_NEAR_LIMIT`; inspect active routes, `static_public_paths`, and the backing `asset_path`. Runtime route failure codes to branch on: `ROUTE_MANIFEST_LOAD_FAILED`, `ROUTED_INVOKE_WORKER_SECRET_MISSING`, `ROUTED_INVOKE_AUTH_FAILED`, `ROUTED_ROUTE_STALE`, `ROUTE_METHOD_NOT_ALLOWED`, and `ROUTED_RESPONSE_TOO_LARGE`.
257
257
 
258
258
  ### Secrets
259
259
 
package/lib/deploy-v2.mjs CHANGED
@@ -1589,21 +1589,29 @@ const SITE_BULK_REMOVAL_CODE = "DESTRUCTIVE_SITE_BULK_REMOVAL";
1589
1589
 
1590
1590
  /**
1591
1591
  * Reads the SDK's `details.removed_paths_summary` on a bulk site-removal
1592
- * warning and says whether the removal drops pages or only renames
1593
- * content-hashed build bundles (the routine framework-rebuild case).
1592
+ * warning and says whether the counted removals drop pages or are only
1593
+ * content-hashed build bundles.
1594
+ *
1595
+ * A gateway that sends `details.replaced_fingerprinted` has already left
1596
+ * out bundles the build replaces in the same directory (renames), so any
1597
+ * hashed bundle still counted has no replacement there. An older gateway
1598
+ * or a Core target counts renames too.
1594
1599
  */
1595
1600
  function siteRemovalGuidance(warnings) {
1596
1601
  const warning = warnings?.find((w) => w?.code === SITE_BULK_REMOVAL_CODE);
1597
1602
  if (!warning) return null;
1598
1603
  const summary = warning.details?.removed_paths_summary;
1599
- const where = "Per-path detail is in warnings[].affected and warnings[].details.removed_paths_summary.";
1604
+ const where = "Per-path detail is in warnings[].affected and warnings[].details.";
1600
1605
  if (!summary || typeof summary.total !== "number") {
1601
1606
  return { hint: `This deploy removes more than ten percent of the live site's paths. ${where} Acknowledge ${SITE_BULK_REMOVAL_CODE} only if those removals are intended.` };
1602
1607
  }
1603
1608
  if (summary.only_content_hashed_build_assets) {
1604
1609
  const example = Array.isArray(warning.affected) && warning.affected[0] ? ` (e.g. ${warning.affected[0]})` : "";
1610
+ const gatewayPairsRenames = typeof warning.details?.replaced_fingerprinted === "number";
1605
1611
  return {
1606
- hint: `All ${summary.total} removed site paths are content-hashed build assets${example}, which a rebuild renames; no HTML pages are removed. If this deploy is a rebuild of the same site, acknowledging ${SITE_BULK_REMOVAL_CODE} is safe. ${where}`,
1612
+ hint: gatewayPairsRenames
1613
+ ? `All ${summary.total} counted removals are content-hashed build assets${example} that this build does not replace in the same directory; no HTML pages are removed. That is usually a rebuild that merged or dropped bundles. Acknowledge ${SITE_BULK_REMOVAL_CODE} once nothing outside this build still loads them. ${where}`
1614
+ : `All ${summary.total} removed site paths are content-hashed build assets${example}, which a rebuild renames or merges; no HTML pages are removed. If this deploy is a rebuild of the same site, acknowledging ${SITE_BULK_REMOVAL_CODE} is safe. ${where}`,
1607
1615
  };
1608
1616
  }
1609
1617
  const pageList = Array.isArray(summary.page_paths) && summary.page_paths.length > 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "4.109.1",
3
+ "version": "4.110.1",
4
4
  "description": "CLI 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.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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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"}
@@ -1103,22 +1103,26 @@ function siteToCoreSpec(site) {
1103
1103
  ...(site.public_paths ? { public_paths: site.public_paths } : {}),
1104
1104
  };
1105
1105
  }
1106
- // tenant-site-embedding and site.sign_in_path never reach core: both are
1107
- // gateway-owned and core validates site keys against its own list.
1106
+ // tenant-site-embedding, site.sign_in_path, and site.noindex never reach
1107
+ // core: all three are gateway-owned and core validates site keys against
1108
+ // its own list.
1108
1109
  return site.public_paths ? { public_paths: site.public_paths } : {};
1109
1110
  }
1110
1111
  function siteToWire(site) {
1111
1112
  // tenant-site-embedding: `null` is a meaningful value on the wire (clear), so
1112
1113
  // it is carried whenever the key is present, unlike public_paths.
1113
- // site.sign_in_path follows the same rule (`null` clears back to the hosted page).
1114
+ // site.sign_in_path follows the same rule (`null` clears back to the hosted page),
1115
+ // and so does site.noindex (`false` or `null` clears back to indexable).
1114
1116
  const embedding = "embedding" in site && site.embedding !== undefined ? { embedding: site.embedding } : {};
1115
1117
  const signInPath = "sign_in_path" in site && site.sign_in_path !== undefined ? { sign_in_path: site.sign_in_path } : {};
1118
+ const noindex = "noindex" in site && site.noindex !== undefined ? { noindex: site.noindex } : {};
1116
1119
  if ("replace" in site && site.replace) {
1117
1120
  return {
1118
1121
  replace: fileSetToWire(site.replace),
1119
1122
  ...(site.public_paths ? { public_paths: site.public_paths } : {}),
1120
1123
  ...embedding,
1121
1124
  ...signInPath,
1125
+ ...noindex,
1122
1126
  };
1123
1127
  }
1124
1128
  if ("patch" in site && site.patch) {
@@ -1130,12 +1134,14 @@ function siteToWire(site) {
1130
1134
  ...(site.public_paths ? { public_paths: site.public_paths } : {}),
1131
1135
  ...embedding,
1132
1136
  ...signInPath,
1137
+ ...noindex,
1133
1138
  };
1134
1139
  }
1135
1140
  return {
1136
1141
  ...(site.public_paths ? { public_paths: site.public_paths } : {}),
1137
1142
  ...embedding,
1138
1143
  ...signInPath,
1144
+ ...noindex,
1139
1145
  };
1140
1146
  }
1141
1147
  function i18nToWire(i18n) {
@@ -1654,7 +1660,7 @@ async function commitInternal(client, planId, idempotencyKey, project, requiredP
1654
1660
  : {}),
1655
1661
  context: "committing deploy",
1656
1662
  });
1657
- return await sendCommitRecoveringLostResponse(client, send, operationId, project, tries);
1663
+ return await sendCommitWithRetry(send, tries);
1658
1664
  }
1659
1665
  catch (err) {
1660
1666
  const translated = translateDeployError(err, "commit", planId, operationId ?? null);
@@ -1667,38 +1673,34 @@ async function commitInternal(client, planId, idempotencyKey, project, requiredP
1667
1673
  }
1668
1674
  }
1669
1675
  /**
1670
- * Send a deploy commit, recovering when a 502/503/504 or a dropped connection
1671
- * loses the response. The commit may still have reached the gateway (a long
1672
- * commit can outlive the connection that carried it), and a second commit
1673
- * while the first is running would drive the state machine twice. So read the
1674
- * operation first: once it has left `planning`/`uploading` the commit landed,
1675
- * and the caller follows the operation; otherwise the commit never arrived and
1676
- * is sent again. Without an operation id to read, the failure surfaces as is.
1677
- * `tries.attempts` counts the commits sent, for the error's retry metadata.
1676
+ * Send a deploy commit, re-sending when a 502/503/504 or a dropped connection
1677
+ * loses the response. Re-sending is safe because a plan commits once: the
1678
+ * gateway answers a commit of a plan whose operation is already running with
1679
+ * `status: "running"` (the caller follows the operation) and a finished one
1680
+ * with its stored result, and Run402 Core answers `commit_in_progress` while
1681
+ * the first commit runs, which is re-sent the same way. `tries.attempts`
1682
+ * counts the commits sent, for the error's retry metadata.
1678
1683
  */
1679
- async function sendCommitRecoveringLostResponse(client, send, operationId, projectId, tries) {
1684
+ async function sendCommitWithRetry(send, tries) {
1680
1685
  for (let attempt = 1;; attempt++) {
1681
1686
  tries.attempts = attempt;
1682
1687
  try {
1683
1688
  return await send();
1684
1689
  }
1685
1690
  catch (err) {
1686
- if (!operationId || !projectId || !isTransientGatewayFailure(err) || attempt >= DEPLOY_COMMIT_MAX_ATTEMPTS)
1691
+ if (!isRetryableCommitFailure(err) || attempt >= DEPLOY_COMMIT_MAX_ATTEMPTS)
1687
1692
  throw err;
1688
1693
  await sleep(DEPLOY_COMMIT_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1));
1689
- let snapshot;
1690
- try {
1691
- snapshot = await client.request(`/apply/v1/operations/${encodeURIComponent(operationId)}`, { headers: await apikeyHeaders(client, projectId), context: "fetching deploy operation" });
1692
- }
1693
- catch {
1694
- throw err;
1695
- }
1696
- if (snapshot.status !== "planning" && snapshot.status !== "uploading") {
1697
- return { operation_id: operationId, status: "running" };
1698
- }
1699
1694
  }
1700
1695
  }
1701
1696
  }
1697
+ function isRetryableCommitFailure(err) {
1698
+ if (isTransientGatewayFailure(err))
1699
+ return true;
1700
+ // Run402 Core: another commit of this plan is still running.
1701
+ return isRun402Error(err) && err.status === 409 &&
1702
+ err.body?.error === "commit_in_progress";
1703
+ }
1702
1704
  async function uploadMissing(client, projectId, presence, byteReaders, emit) {
1703
1705
  // Surface CAS dedup hits so agents can distinguish "N files were already
1704
1706
  // present" from "nothing happened". The gateway reports both present and
@@ -2371,7 +2373,7 @@ const FUNCTION_SPEC_FIELDS = new Set([
2371
2373
  const FUNCTION_CONFIG_FIELDS = new Set(["timeoutSeconds", "memoryMb"]);
2372
2374
  const FUNCTION_TRIGGER_FIELDS = new Set(["id", "type", "cron", "timezone", "misfire_policy", "overlap_policy", "mailbox", "events", "run"]);
2373
2375
  const FUNCTION_TRIGGER_RUN_FIELDS = new Set(["event_type", "payload", "retry", "expires_after_seconds"]);
2374
- const SITE_SPEC_FIELDS = new Set(["replace", "patch", "public_paths", "embedding", "sign_in_path"]);
2376
+ const SITE_SPEC_FIELDS = new Set(["replace", "patch", "public_paths", "embedding", "sign_in_path", "noindex"]);
2375
2377
  const SITE_PATCH_FIELDS = new Set(["put", "delete"]);
2376
2378
  const SITE_PUBLIC_PATHS_FIELDS = new Set(["mode", "replace"]);
2377
2379
  const PUBLIC_STATIC_PATH_FIELDS = new Set(["asset", "cache_class"]);
@@ -2710,6 +2712,17 @@ function validateSiteSpec(site) {
2710
2712
  if (obj.sign_in_path !== undefined) {
2711
2713
  validateSiteSignInPath(obj.sign_in_path, "site.sign_in_path");
2712
2714
  }
2715
+ if (obj.noindex !== undefined) {
2716
+ validateSiteNoindex(obj.noindex, "site.noindex");
2717
+ }
2718
+ }
2719
+ /** site.noindex. `true` = every response of the host carries
2720
+ * `X-Robots-Tag: noindex, nofollow, nosnippet`; `false` or `null` clears back
2721
+ * to indexable; omitted carries forward. */
2722
+ function validateSiteNoindex(value, resource) {
2723
+ if (value === null || typeof value === "boolean")
2724
+ return;
2725
+ throw invalidSpec(`ReleaseSpec.${resource} must be true, false, or null; send true to add X-Robots-Tag: noindex, nofollow, nosnippet to every response of the host, false or null to make it indexable again, or omit ${resource} to carry the previous value forward`, resource);
2713
2726
  }
2714
2727
  /** site.sign_in_path. Structural check only (the gateway is authoritative on
2715
2728
  * the reserved prefixes and the full rule set): a same-origin absolute path
@@ -3089,6 +3102,10 @@ function hasSiteContent(site) {
3089
3102
  if (site && typeof site === "object" && "sign_in_path" in site && site.sign_in_path !== undefined) {
3090
3103
  return true;
3091
3104
  }
3105
+ // site.noindex: same rule, true/false/null is content.
3106
+ if (site && typeof site === "object" && "noindex" in site && site.noindex !== undefined) {
3107
+ return true;
3108
+ }
3092
3109
  if (!isRecord(site))
3093
3110
  return false;
3094
3111
  if (hasRecordEntries(site.replace))
@@ -3545,6 +3562,10 @@ async function normalizeReleaseSpec(client, spec, opts = {}) {
3545
3562
  const signInPath = "sign_in_path" in spec.site && spec.site.sign_in_path !== undefined
3546
3563
  ? { sign_in_path: spec.site.sign_in_path }
3547
3564
  : {};
3565
+ // site.noindex: present (boolean or null) travels as-is.
3566
+ const noindex = "noindex" in spec.site && spec.site.noindex !== undefined
3567
+ ? { noindex: spec.site.noindex }
3568
+ : {};
3548
3569
  if ("replace" in spec.site && spec.site.replace) {
3549
3570
  const map = await normalizeFileSet(spec.site.replace, rememberRelease);
3550
3571
  // Re-check post-expansion so `dir("dist")` (a LocalDirRef whose keys are
@@ -3555,6 +3576,7 @@ async function normalizeReleaseSpec(client, spec, opts = {}) {
3555
3576
  ...(publicPaths ? { public_paths: publicPaths } : {}),
3556
3577
  ...embedding,
3557
3578
  ...signInPath,
3579
+ ...noindex,
3558
3580
  };
3559
3581
  }
3560
3582
  else if ("patch" in spec.site && spec.site.patch) {
@@ -3570,13 +3592,15 @@ async function normalizeReleaseSpec(client, spec, opts = {}) {
3570
3592
  ...(publicPaths ? { public_paths: publicPaths } : {}),
3571
3593
  ...embedding,
3572
3594
  ...signInPath,
3595
+ ...noindex,
3573
3596
  };
3574
3597
  }
3575
- else if (publicPaths || "embedding" in embedding || "sign_in_path" in signInPath) {
3598
+ else if (publicPaths || "embedding" in embedding || "sign_in_path" in signInPath || "noindex" in noindex) {
3576
3599
  normalized.site = {
3577
3600
  ...(publicPaths ? { public_paths: publicPaths } : {}),
3578
3601
  ...embedding,
3579
3602
  ...signInPath,
3603
+ ...noindex,
3580
3604
  };
3581
3605
  }
3582
3606
  }