siteplane 0.1.60 → 0.1.61

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
@@ -81,7 +81,10 @@ siteplane setup deploy --wait
81
81
 
82
82
  Commit only intended source before deploy. The CLI securely transfers its Production environment, adopts or
83
83
  starts one matching deployment and verifies the actual build and signed runtime. Unchanged resume creates no
84
- new deployment. A saved 15-minute provider deadline and operation identity prevent blind retries after response
84
+ new deployment. If the linked project has multiple verified, non-redirecting Production domains, select the
85
+ canonical one on the first run with `siteplane setup deploy --wait --production-origin <https-origin>`. The CLI
86
+ rejects foreign, redirect, branch and unverified domains, stores the binding and reuses it on resume. A saved
87
+ 15-minute provider deadline and operation identity prevent blind retries after response
85
88
  loss. If that deadline expires after the deploy was requested, rerun the same command: each invocation gets a
86
89
  bounded 120-second readback of that same operation. A ready result is adopted; a still-running or unreachable
87
90
  provider remains resumable and never authorizes a second deploy. Uploads use committed source in a temporary
@@ -102,8 +105,8 @@ Before starting, add `.siteplane/credentials.json` to the repository’s `.gitig
102
105
  unprotected credentials before any provisioning request.
103
106
 
104
107
  ```bash
105
- npx -y siteplane@0.1.60 setup start --modules editing --instructions-file instructions.txt
106
- npx -y siteplane@0.1.60 setup context
108
+ npx -y siteplane@0.1.61 setup start --modules editing --instructions-file instructions.txt
109
+ npx -y siteplane@0.1.61 setup context
107
110
  ```
108
111
 
109
112
  Start performs read-only preflight and saves the installation key and original request privately before the
@@ -5605,7 +5605,7 @@ ${task.goal}
5605
5605
 
5606
5606
  ${task.discovery.rules.map((rule) => `- ${rule}`).join("\n")}
5607
5607
 
5608
- Run setup context for the current user instructions and authorized modules. Keep the user instructions distinct from your selection summary. Use setup deploy --wait for environment transfer and deployment; never read or print credential values. If a requested deploy passes its provider deadline, rerun the same command for a bounded readback of that operation; do not start a replacement while its outcome is unresolved. For image fields, withSiteplane adds only the exact Siteplane asset route and website /siteplane-assets/ pattern while preserving existing sources, loaders, and redirect policy. This grants no hosting permission. If the website uses a global or component image loader, keep it and verify Siteplane image delivery explicitly. Inspect CSP written by middleware/proxy/meta tags before deployment. Use the provider-selected public SITEPLANE_SITE_ORIGIN for local builds.
5608
+ Run setup context for the current user instructions and authorized modules. Keep the user instructions distinct from your selection summary. Use setup deploy --wait for environment transfer and deployment; never read or print credential values. If the linked provider project has multiple eligible Production domains, choose its canonical verified non-redirecting origin with --production-origin <https-origin>; before an origin-dependent local build use that exact value as SITEPLANE_SITE_ORIGIN, then pass the same origin to setup deploy. Resume reuses the stored choice. If a requested deploy passes its provider deadline, rerun the same command for a bounded readback of that operation; do not start a replacement while its outcome is unresolved. For image fields, withSiteplane adds only the exact Siteplane asset route and website /siteplane-assets/ pattern while preserving existing sources, loaders, and redirect policy. This grants no hosting permission. If the website uses a global or component image loader, keep it and verify Siteplane image delivery explicitly. Inspect CSP written by middleware/proxy/meta tags before deployment. Never use a wildcard, private host or different origin for local builds.
5609
5609
  `);
5610
5610
  for (const [path, content] of files) {
5611
5611
  if (path === layoutPath || path === nextPath || path === guidePath)
@@ -5902,6 +5902,17 @@ var digest = (value) => `sha256:${createHash4("sha256").update(stableJsonStringi
5902
5902
  function fail2(code, message) {
5903
5903
  throw new SetupFailure(code, message);
5904
5904
  }
5905
+ function normalizeProductionOrigin(value) {
5906
+ let url;
5907
+ try {
5908
+ url = new URL(value);
5909
+ } catch {
5910
+ fail2("production_origin_invalid", "Choose an absolute public HTTPS origin without a path, query, fragment or credentials.");
5911
+ }
5912
+ if (url.protocol !== "https:" || url.username || url.password || url.pathname !== "/" || url.search || url.hash)
5913
+ fail2("production_origin_invalid", "Choose an absolute public HTTPS origin without a path, query, fragment or credentials.");
5914
+ return url.origin;
5915
+ }
5905
5916
  var defaults = {
5906
5917
  preflight: setupPreflightCommand,
5907
5918
  context: siteSetupContextCommand,
@@ -5951,16 +5962,8 @@ async function setupDeployCommand(input, deps = defaults) {
5951
5962
  ...record ? { deploymentId: record.deploymentId, deadline: record.deadline } : {}
5952
5963
  };
5953
5964
  const { local, provider } = preflight;
5954
- if (local.git.changes.length || !local.git.revision || !local.git.branch)
5955
- fail2("uncommitted_source", "Commit only the intended setup source on a named branch before deployment. Siteplane does not stage, reset or publish unrelated local work.");
5956
- const revision = local.git.revision;
5957
- if (!/^[0-9a-f]{40}$/u.test(revision))
5958
- fail2("invalid_git_revision", "A full committed Git revision is required.");
5959
- try {
5960
- await assertExactSetupPackages(local.projectDirectory, config.packages);
5961
- } catch {
5962
- fail2("siteplane_package_pin_conflict", "Install the exact Siteplane package versions from the current task and commit their canonical lockfile.");
5963
- }
5965
+ if (record && (record.projectId !== provider.projectId || record.teamId !== provider.teamId))
5966
+ fail2("deployment_target_conflict", "This setup already belongs to a different provider project/team. Restore its original local link.");
5964
5967
  const recordedOperationDeadline = record && !["verified", "deployed", "failed"].includes(record.phase) ? Date.parse(record.deadline) : null;
5965
5968
  const isExpiredDeployReadback = record?.phase === "deploy_requested" && recordedOperationDeadline !== null && recordedOperationDeadline <= deps.now();
5966
5969
  let operationDeadline = isExpiredDeployReadback ? deps.now() + PROVIDER_RESUME_READBACK_TIMEOUT_MS : recordedOperationDeadline ?? deps.now() + PROVIDER_OPERATION_TIMEOUT_MS;
@@ -5971,14 +5974,9 @@ async function setupDeployCommand(input, deps = defaults) {
5971
5974
  fail2("provider_deadline_exceeded", deadlineExceededMessage);
5972
5975
  return milliseconds;
5973
5976
  };
5974
- const processCommand = async (binary, args, options) => {
5975
- const output = await deps.process(binary, args, {
5976
- ...options,
5977
- timeoutMs: Math.min(options.timeoutMs ?? 12e4, remaining())
5978
- });
5979
- remaining();
5980
- return output;
5981
- };
5977
+ const selectedOrigin = input.productionOrigin ? normalizeProductionOrigin(input.productionOrigin) : null;
5978
+ if (record && selectedOrigin && selectedOrigin !== record.origin)
5979
+ fail2("production_origin_conflict", "This setup is already bound to another Production origin. Resume with the stored origin instead of changing it silently.");
5982
5980
  const scope = `teamId=${provider.teamId}`;
5983
5981
  const api = async (endpoint, body, method) => {
5984
5982
  remaining();
@@ -5994,11 +5992,32 @@ async function setupDeployCommand(input, deps = defaults) {
5994
5992
  gitBranch: z40.string().nullable().optional()
5995
5993
  }))
5996
5994
  }).parse(await api(`/v9/projects/${provider.projectId}/domains?${scope}`)).domains.filter((domain) => domain.verified && !domain.redirect && !domain.gitBranch);
5997
- const origin = record?.origin ?? (domains.length === 1 ? `https://${domains[0].name}` : null);
5998
- if (!origin || !domains.some((domain) => `https://${domain.name}` === origin))
5999
- fail2("production_origin_ambiguous", "The linked project needs one verified canonical Production domain, or the existing setup domain must still belong to it.");
6000
- if (record && (record.projectId !== provider.projectId || record.teamId !== provider.teamId))
6001
- fail2("deployment_target_conflict", "This setup already belongs to a different provider project/team. Restore its original local link.");
5995
+ const eligibleOrigins = domains.map((domain) => new URL(`https://${domain.name}`).origin);
5996
+ const origin = record?.origin ?? selectedOrigin ?? (eligibleOrigins.length === 1 ? eligibleOrigins[0] : null);
5997
+ if (selectedOrigin && !eligibleOrigins.includes(selectedOrigin))
5998
+ fail2("production_origin_not_bound", "The selected origin must be a verified, non-redirecting Production domain of this linked provider project.");
5999
+ if (!origin)
6000
+ fail2("production_origin_ambiguous", `Choose the canonical Production origin explicitly and rerun setup deploy --wait --production-origin <origin>. Eligible origins: ${eligibleOrigins.sort().join(", ") || "none"}.`);
6001
+ if (!eligibleOrigins.includes(origin))
6002
+ fail2("production_origin_not_bound", "The stored Production origin is no longer a verified, non-redirecting Production domain of this linked provider project.");
6003
+ if (local.git.changes.length || !local.git.revision || !local.git.branch)
6004
+ fail2("uncommitted_source", "Commit only the intended setup source on a named branch before deployment. Siteplane does not stage, reset or publish unrelated local work.");
6005
+ const revision = local.git.revision;
6006
+ if (!/^[0-9a-f]{40}$/u.test(revision))
6007
+ fail2("invalid_git_revision", "A full committed Git revision is required.");
6008
+ try {
6009
+ await assertExactSetupPackages(local.projectDirectory, config.packages);
6010
+ } catch {
6011
+ fail2("siteplane_package_pin_conflict", "Install the exact Siteplane package versions from the current task and commit their canonical lockfile.");
6012
+ }
6013
+ const processCommand = async (binary, args, options) => {
6014
+ const output = await deps.process(binary, args, {
6015
+ ...options,
6016
+ timeoutMs: Math.min(options.timeoutMs ?? 12e4, remaining())
6017
+ });
6018
+ remaining();
6019
+ return output;
6020
+ };
6002
6021
  const listEnv = async () => z40.object({ envs: z40.array(envSchema) }).parse(await api(`/v10/projects/${provider.projectId}/env?${scope}`)).envs;
6003
6022
  let env = await listEnv();
6004
6023
  const owned = (key) => {
@@ -6642,8 +6661,10 @@ async function runSetupCommand(args, dependencies) {
6642
6661
  return { exitCode: 0 };
6643
6662
  }
6644
6663
  if (area === "deploy") {
6664
+ const productionOrigin = readOption(areaArgs, "--production-origin");
6645
6665
  const result2 = await dependencies.commands.setupDeployCommand({
6646
6666
  projectDir,
6667
+ ...productionOrigin ? { productionOrigin } : {},
6647
6668
  onProgress: (progress) => dependencies.stderr(JSON.stringify({ status: "waiting", ...progress }))
6648
6669
  });
6649
6670
  dependencies.stdout(JSON.stringify(result2, null, 2));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "siteplane",
3
3
  "private": false,
4
- "version": "0.1.60",
4
+ "version": "0.1.61",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "publishConfig": {