run402 4.106.1 → 4.107.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. `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`. `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/functions.mjs CHANGED
@@ -485,7 +485,10 @@ async function invoke(projectId, name, args) {
485
485
  }
486
486
  const invokeOpts = { method: opts.method };
487
487
  if (requestBody !== undefined && opts.method !== "GET" && opts.method !== "HEAD") {
488
+ // The body was validated as JSON above. Sent as a raw string it would go
489
+ // out as text/plain (run402-private#817), so say what it is.
488
490
  invokeOpts.body = requestBody;
491
+ invokeOpts.headers = { "content-type": "application/json" };
489
492
  }
490
493
  if (opts.idempotencyKey !== undefined) invokeOpts.idempotencyKey = opts.idempotencyKey;
491
494
  if (opts.wait) invokeOpts.wait = waitOpts;
package/lib/snapshots.mjs CHANGED
@@ -15,24 +15,34 @@ import {
15
15
  const HELP = `run402 snapshots — Project database restore points
16
16
 
17
17
  Usage:
18
- run402 snapshots create [--project <project_id>] [--json]
18
+ run402 snapshots create [--label <text>] [--metadata <json>] [--project <project_id>] [--json]
19
19
  run402 snapshots list [--project <project_id>] [--kind <kind>] [--limit <n>] [--after <cursor>] [--json]
20
20
  run402 snapshots get <snapshot_id> [--project <project_id>] [--json]
21
- run402 snapshots restore <snapshot_id> [--project <project_id>] [--include-auth] [--confirm <token>] [--json]
21
+ run402 snapshots restore <snapshot_id> [--project <project_id>] [--release keep|snapshot] [--include-auth] [--confirm <token>] [--json]
22
+ run402 snapshots restore-status <snapshot_id> <restore_id> [--project <project_id>] [--json]
22
23
  run402 snapshots delete <snapshot_id> [--project <project_id>] [--json]
23
24
 
24
25
  Legacy (still supported): a leading prj_... positional selects the project,
25
26
  e.g. run402 snapshots restore prj_abc123 <snapshot_id>. --project defaults to
26
27
  the active project.
27
28
 
29
+ --label (1-120 characters) and --metadata (a flat JSON object of string,
30
+ number, boolean, or string-array values, up to 4 KB) are stored with the
31
+ snapshot and survive restores. Never put secrets in metadata.
32
+
28
33
  Restore is a two-step handshake. First call without --confirm to get a
29
34
  restore_plan.confirm.token, then re-run with --confirm after reviewing the
30
- data-loss statement.
35
+ data-loss statement. Pass the same --release and --include-auth both times.
36
+ --release snapshot also puts back the release that was live when the snapshot
37
+ was taken (site, routes, subdomains); the plan's release.warnings lists any
38
+ function whose code does not roll back. The confirmed restore waits for the
39
+ result; restore-status reads a restore by id.
31
40
  `;
32
41
 
33
- const FLAG_VALUES = ["--project", "--kind", "--limit", "--after", "--confirm"];
42
+ const FLAG_VALUES = ["--project", "--kind", "--limit", "--after", "--confirm", "--label", "--metadata", "--release"];
34
43
  const FLAGS = new Set([...FLAG_VALUES, "--include-auth", "--json", "--help", "-h"]);
35
44
  const SNAPSHOT_KINDS = ["manual", "pre_migration", "pre_restore", "scheduled"];
45
+ const RELEASE_MODES = ["keep", "snapshot"];
36
46
 
37
47
  export async function run(sub, args = []) {
38
48
  const all = [sub, ...args].filter(Boolean);
@@ -47,16 +57,48 @@ export async function run(sub, args = []) {
47
57
  case "list": return list(rest);
48
58
  case "get": return get(rest);
49
59
  case "restore": return restore(rest);
60
+ case "restore-status": return restoreStatus(rest);
50
61
  case "delete": return deleteSnapshot(rest);
51
62
  default:
52
63
  failUnknownSubcommand("snapshots", sub);
53
64
  }
54
65
  }
55
66
 
67
+ function parseMetadataFlag(args) {
68
+ const raw = flagValue(args, "--metadata");
69
+ if (raw === null) return undefined;
70
+ let parsed;
71
+ try {
72
+ parsed = JSON.parse(raw);
73
+ } catch (err) {
74
+ fail({
75
+ code: "BAD_FLAG",
76
+ message: `--metadata must be a JSON object: ${err instanceof Error ? err.message : String(err)}`,
77
+ details: { flag: "--metadata" },
78
+ });
79
+ }
80
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
81
+ fail({ code: "BAD_FLAG", message: "--metadata must be a JSON object", details: { flag: "--metadata" } });
82
+ }
83
+ return parsed;
84
+ }
85
+
86
+ function releaseFlag(args) {
87
+ const release = flagValue(args, "--release");
88
+ if (release === null) return undefined;
89
+ assertAllowedValue(release, RELEASE_MODES, "--release");
90
+ return release;
91
+ }
92
+
56
93
  async function create(args) {
57
94
  const { projectId } = resolveProjectSelector(args, { valueFlags: FLAG_VALUES });
95
+ const label = flagValue(args, "--label") ?? undefined;
96
+ const metadata = parseMetadataFlag(args);
58
97
  try {
59
- const snapshot = await getSdk().snapshots.create(projectId);
98
+ const snapshot = await getSdk().snapshots.create(projectId, {
99
+ ...(label !== undefined ? { label } : {}),
100
+ ...(metadata !== undefined ? { metadata } : {}),
101
+ });
60
102
  console.log(JSON.stringify({ ok: snapshot.status === "ready", snapshot }, null, 2));
61
103
  } catch (err) {
62
104
  reportSdkError(err);
@@ -94,26 +136,41 @@ async function get(args) {
94
136
  async function restore(args) {
95
137
  const { projectId, snapshotId } = resolveProjectAndSnapshot(args, "run402 snapshots restore [project_id] <snapshot_id> [--confirm <token>]");
96
138
  const includeAuth = args.includes("--include-auth");
139
+ const release = releaseFlag(args);
97
140
  const confirm = flagValue(args, "--confirm");
141
+ const opts = { includeAuth, ...(release !== undefined ? { release } : {}) };
98
142
  try {
99
143
  if (confirm) {
100
- const result = await getSdk().snapshots.restore(projectId, snapshotId, confirm, { includeAuth });
144
+ const result = await getSdk().snapshots.restore(projectId, snapshotId, confirm, opts);
101
145
  console.log(JSON.stringify({ ok: result.status === "ready", restore: result }, null, 2));
102
146
  return;
103
147
  }
104
- const plan = await getSdk().snapshots.restorePlan(projectId, snapshotId, { includeAuth });
148
+ const plan = await getSdk().snapshots.restorePlan(projectId, snapshotId, opts);
105
149
  console.log(JSON.stringify({
106
150
  ok: true,
107
151
  project_id: projectId,
108
152
  snapshot_id: snapshotId,
109
153
  ...plan,
110
- confirm_command: `run402 snapshots restore ${projectId} ${snapshotId} --confirm ${JSON.stringify(plan.restore_plan.confirm.token)}${includeAuth ? " --include-auth" : ""} --json`,
154
+ confirm_command: `run402 snapshots restore ${snapshotId} --project ${projectId} --confirm ${JSON.stringify(plan.restore_plan.confirm.token)}${release !== undefined ? ` --release ${release}` : ""}${includeAuth ? " --include-auth" : ""} --json`,
111
155
  }, null, 2));
112
156
  } catch (err) {
113
157
  reportSdkError(err);
114
158
  }
115
159
  }
116
160
 
161
+ async function restoreStatus(args) {
162
+ const usage = "run402 snapshots restore-status [project_id] <snapshot_id> <restore_id>";
163
+ const { projectId, rest } = resolveProjectSelector(args, { valueFlags: FLAG_VALUES, requireRestPositional: true });
164
+ const pos = positionalArgs(rest, FLAG_VALUES);
165
+ if (pos.length !== 2) fail({ code: "BAD_USAGE", message: `Usage: ${usage}` });
166
+ try {
167
+ const restoreRead = await getSdk().snapshots.getRestore(projectId, pos[0], pos[1]);
168
+ console.log(JSON.stringify({ ok: restoreRead.status !== "failed", restore: restoreRead }, null, 2));
169
+ } catch (err) {
170
+ reportSdkError(err);
171
+ }
172
+ }
173
+
117
174
  async function deleteSnapshot(args) {
118
175
  const { projectId, snapshotId } = resolveProjectAndSnapshot(args, "run402 snapshots delete [project_id] <snapshot_id>");
119
176
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "4.106.1",
3
+ "version": "4.107.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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"}
@@ -1072,19 +1072,22 @@ function siteToCoreSpec(site) {
1072
1072
  ...(site.public_paths ? { public_paths: site.public_paths } : {}),
1073
1073
  };
1074
1074
  }
1075
- // tenant-site-embedding never reaches core: the catalog is gateway-owned and
1076
- // core validates site keys against its own list.
1075
+ // tenant-site-embedding and site.sign_in_path never reach core: both are
1076
+ // gateway-owned and core validates site keys against its own list.
1077
1077
  return site.public_paths ? { public_paths: site.public_paths } : {};
1078
1078
  }
1079
1079
  function siteToWire(site) {
1080
1080
  // tenant-site-embedding: `null` is a meaningful value on the wire (clear), so
1081
1081
  // it is carried whenever the key is present, unlike public_paths.
1082
+ // site.sign_in_path follows the same rule (`null` clears back to the hosted page).
1082
1083
  const embedding = "embedding" in site && site.embedding !== undefined ? { embedding: site.embedding } : {};
1084
+ const signInPath = "sign_in_path" in site && site.sign_in_path !== undefined ? { sign_in_path: site.sign_in_path } : {};
1083
1085
  if ("replace" in site && site.replace) {
1084
1086
  return {
1085
1087
  replace: fileSetToWire(site.replace),
1086
1088
  ...(site.public_paths ? { public_paths: site.public_paths } : {}),
1087
1089
  ...embedding,
1090
+ ...signInPath,
1088
1091
  };
1089
1092
  }
1090
1093
  if ("patch" in site && site.patch) {
@@ -1095,11 +1098,13 @@ function siteToWire(site) {
1095
1098
  },
1096
1099
  ...(site.public_paths ? { public_paths: site.public_paths } : {}),
1097
1100
  ...embedding,
1101
+ ...signInPath,
1098
1102
  };
1099
1103
  }
1100
1104
  return {
1101
1105
  ...(site.public_paths ? { public_paths: site.public_paths } : {}),
1102
1106
  ...embedding,
1107
+ ...signInPath,
1103
1108
  };
1104
1109
  }
1105
1110
  function i18nToWire(i18n) {
@@ -2308,7 +2313,7 @@ const FUNCTION_SPEC_FIELDS = new Set([
2308
2313
  const FUNCTION_CONFIG_FIELDS = new Set(["timeoutSeconds", "memoryMb"]);
2309
2314
  const FUNCTION_TRIGGER_FIELDS = new Set(["id", "type", "cron", "timezone", "misfire_policy", "overlap_policy", "mailbox", "events", "run"]);
2310
2315
  const FUNCTION_TRIGGER_RUN_FIELDS = new Set(["event_type", "payload", "retry", "expires_after_seconds"]);
2311
- const SITE_SPEC_FIELDS = new Set(["replace", "patch", "public_paths", "embedding"]);
2316
+ const SITE_SPEC_FIELDS = new Set(["replace", "patch", "public_paths", "embedding", "sign_in_path"]);
2312
2317
  const SITE_PATCH_FIELDS = new Set(["put", "delete"]);
2313
2318
  const SITE_PUBLIC_PATHS_FIELDS = new Set(["mode", "replace"]);
2314
2319
  const PUBLIC_STATIC_PATH_FIELDS = new Set(["asset", "cache_class"]);
@@ -2644,6 +2649,28 @@ function validateSiteSpec(site) {
2644
2649
  if (obj.embedding !== undefined) {
2645
2650
  validateSiteEmbeddingSpec(obj.embedding, "site.embedding");
2646
2651
  }
2652
+ if (obj.sign_in_path !== undefined) {
2653
+ validateSiteSignInPath(obj.sign_in_path, "site.sign_in_path");
2654
+ }
2655
+ }
2656
+ /** site.sign_in_path. Structural check only (the gateway is authoritative on
2657
+ * the reserved prefixes and the full rule set): a same-origin absolute path
2658
+ * like "/join", or `null` to clear back to the hosted sign-in page. Omitted
2659
+ * carries forward. */
2660
+ function validateSiteSignInPath(value, resource) {
2661
+ if (value === null)
2662
+ return;
2663
+ const hint = `omit ${resource} to carry the previous value forward or send null to use the hosted sign-in page`;
2664
+ if (typeof value !== "string") {
2665
+ throw invalidSpec(`ReleaseSpec.${resource} must be a same-origin path string (e.g. "/join") or null; ${hint}`, resource);
2666
+ }
2667
+ if (!value.startsWith("/") ||
2668
+ value.startsWith("//") ||
2669
+ value.length > 512 ||
2670
+ value.includes("://") ||
2671
+ /[?#\\\s\u0000-\u001f\u007f]/.test(value)) {
2672
+ throw invalidSpec(`ReleaseSpec.${resource} must be a same-origin path starting with a single "/" (e.g. "/join"), at most 512 characters, with no query, fragment, whitespace, backslash, or scheme; ${hint}`, resource);
2673
+ }
2647
2674
  }
2648
2675
  /** tenant-site-embedding. Structural check only: the catalog is the gateway's
2649
2676
  * (it names the valid keys in INVALID_SPEC), so a new key never needs an SDK
@@ -3014,6 +3041,10 @@ function hasSiteContent(site) {
3014
3041
  if (site && typeof site === "object" && "embedding" in site && site.embedding !== undefined) {
3015
3042
  return true;
3016
3043
  }
3044
+ // site.sign_in_path: same rule, a value or an explicit null is content.
3045
+ if (site && typeof site === "object" && "sign_in_path" in site && site.sign_in_path !== undefined) {
3046
+ return true;
3047
+ }
3017
3048
  if (!isRecord(site))
3018
3049
  return false;
3019
3050
  if (hasRecordEntries(site.replace))
@@ -3468,6 +3499,10 @@ async function normalizeReleaseSpec(client, spec, opts = {}) {
3468
3499
  const embedding = "embedding" in spec.site && spec.site.embedding !== undefined
3469
3500
  ? { embedding: spec.site.embedding }
3470
3501
  : {};
3502
+ // site.sign_in_path: present (string or null) travels as-is.
3503
+ const signInPath = "sign_in_path" in spec.site && spec.site.sign_in_path !== undefined
3504
+ ? { sign_in_path: spec.site.sign_in_path }
3505
+ : {};
3471
3506
  if ("replace" in spec.site && spec.site.replace) {
3472
3507
  const map = await normalizeFileSet(spec.site.replace, rememberRelease);
3473
3508
  // Re-check post-expansion so `dir("dist")` (a LocalDirRef whose keys are
@@ -3477,6 +3512,7 @@ async function normalizeReleaseSpec(client, spec, opts = {}) {
3477
3512
  replace: map,
3478
3513
  ...(publicPaths ? { public_paths: publicPaths } : {}),
3479
3514
  ...embedding,
3515
+ ...signInPath,
3480
3516
  };
3481
3517
  }
3482
3518
  else if ("patch" in spec.site && spec.site.patch) {
@@ -3491,12 +3527,14 @@ async function normalizeReleaseSpec(client, spec, opts = {}) {
3491
3527
  patch,
3492
3528
  ...(publicPaths ? { public_paths: publicPaths } : {}),
3493
3529
  ...embedding,
3530
+ ...signInPath,
3494
3531
  };
3495
3532
  }
3496
- else if (publicPaths || "embedding" in embedding) {
3533
+ else if (publicPaths || "embedding" in embedding || "sign_in_path" in signInPath) {
3497
3534
  normalized.site = {
3498
3535
  ...(publicPaths ? { public_paths: publicPaths } : {}),
3499
3536
  ...embedding,
3537
+ ...signInPath,
3500
3538
  };
3501
3539
  }
3502
3540
  }