@supacloud/cli 0.98.0 → 0.99.0

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
@@ -659,12 +659,19 @@ FA-owned business features are documented in
659
659
 
660
660
  `deploy --target`, `--id`, `--build_command`, `--output_dir`, `--plan`, `--diff`
661
661
  and `--dry_run` are removed. Put build settings in `supacloud.json`; use
662
- `app deploy-plan` for read-only review. Historical project-specific manifests
663
- and standalone Function targets must move to application delivery configuration.
664
- The CLI rejects them rather than silently falling back to a partial Web deployment.
665
- For an intentional Web-only release in a mixed repository, use
666
- `supacloud --env test deploy --assets_only`. This preserves historical manifests,
667
- Functions, schedules and migrations without invoking application compilation.
662
+ `app deploy-plan` for read-only review. With a frontend configured, `supacloud deploy`
663
+ publishes only its static assets, even in a mixed repository. `--assets_only`
664
+ remains an explicit alias. This preserves historical manifests, Functions,
665
+ Storage, schedules and migrations without invoking application compilation.
666
+ Use `supacloud --env test deploy --backend` to publish the configured Application
667
+ independently; it never builds or uploads Web. Unsupported historical backend
668
+ manifests and standalone Function targets still fail closed on that Application path.
669
+
670
+ Application deployments return `accepted` after durable queue insertion, not a
671
+ successful activation. Inspect the receipt with the read-only
672
+ `supacloud --env test deploy --status <deployment-id>`. Queue delivery survives
673
+ restart; interrupted side effects require reconciliation rather than blind retry.
674
+
668
675
  Static publication reuses immutable archives, pre-upload CAS, mutation receipts
669
676
  and exact post-activation readback. It never retries a conflicting activation.
670
677
 
package/dist/index.js CHANGED
@@ -8786,7 +8786,7 @@ function outcomeUnknownGuidance(text) {
8786
8786
  }
8787
8787
 
8788
8788
  // src/shared/deployment-upgrade.ts
8789
- var DEPLOYMENT_UPGRADE = "DEPLOYMENT_UPGRADE_REQUIRED: Legacy deployment commands are no longer supported. Upgrade with `npm install -g @supacloud/cli@latest`, configure frontend in supacloud.json for static assets, or application.id and application.environmentId for executable application delivery, then run `supacloud deploy`. Use `supacloud deploy --assets_only` to publish only Web assets in a mixed repository. SupaCloud owns activation; do not retry with legacy commands.";
8789
+ var DEPLOYMENT_UPGRADE = "DEPLOYMENT_UPGRADE_REQUIRED: Legacy deployment commands are no longer supported. Upgrade with `npm install -g @supacloud/cli@latest`, configure frontend in supacloud.json for static assets, or application.id and application.environmentId for executable application delivery, then run `supacloud deploy`. With a frontend configured, deploy publishes only Web assets; use `supacloud deploy --backend` for the application backend. `supacloud deploy --assets_only` remains an explicit Web-only option. SupaCloud owns activation; do not retry with legacy commands.";
8790
8790
  var removedFrontendActions = new Set([
8791
8791
  "deploy_git",
8792
8792
  "deploy_upload",
@@ -9339,6 +9339,8 @@ function declaredMode(moduleName, action) {
9339
9339
  return;
9340
9340
  }
9341
9341
  function executionMode(moduleName, action, args) {
9342
+ if (moduleName === "deploy" && typeof args.status === "string")
9343
+ return "read";
9342
9344
  if (moduleName === "deploy" && (args.dry_run === true || args.plan === true || args.diff === true))
9343
9345
  return "read";
9344
9346
  if (moduleName === "database" && ["push_migrations", "baseline_migrations"].includes(action) && args.dry_run === true)
@@ -16763,7 +16765,7 @@ async function getFrontendRelease(http, projectRef, deploymentId, releaseId) {
16763
16765
  }
16764
16766
  return toolResponse({ project_ref: projectRef, deployment_id: deploymentId, release });
16765
16767
  }
16766
- async function uploadFrontendRelease(http, projectRef, deploymentId, archivePath) {
16768
+ async function uploadFrontendReleaseWithReadback(http, projectRef, deploymentId, archivePath, verifyReadback) {
16767
16769
  const endpoint = releaseEndpoint(projectRef, deploymentId);
16768
16770
  const archive = await verifiedArchive(archivePath);
16769
16771
  let response;
@@ -16782,13 +16784,15 @@ async function uploadFrontendRelease(http, projectRef, deploymentId, archivePath
16782
16784
  } finally {
16783
16785
  await archive.handle.close();
16784
16786
  }
16785
- const release = response.ok ? releaseEnvelope(response.data, { projectRef, deploymentId, releaseId: archive.sha256 }) : null;
16787
+ const release = response.ok && (response.status === 200 || response.status === 201) ? releaseEnvelope(response.data, { projectRef, deploymentId, releaseId: archive.sha256 }) : null;
16786
16788
  if (!release) {
16787
16789
  if (response.status >= 400 && response.status < 500 && response.status !== 408 && !response.transportError) {
16788
16790
  return releaseFailure("frontend.upload_release", "HTTP_ERROR", response.status);
16789
16791
  }
16790
16792
  return uploadReadback(http, projectRef, deploymentId, archive.sha256, response.status);
16791
16793
  }
16794
+ if (!verifyReadback)
16795
+ return toolResponse({ project_ref: projectRef, deployment_id: deploymentId, release });
16792
16796
  const readback = await http.get(`${endpoint}/${release.release_id}`, { maxJsonBytes: RESPONSE_MAX_BYTES });
16793
16797
  const verified = readback.ok ? releaseEnvelope(readback.data, { projectRef, deploymentId, releaseId: release.release_id }) : null;
16794
16798
  if (!verified || verified.tree_sha256 !== release.tree_sha256) {
@@ -16796,65 +16800,11 @@ async function uploadFrontendRelease(http, projectRef, deploymentId, archivePath
16796
16800
  }
16797
16801
  return toolResponse({ project_ref: projectRef, deployment_id: deploymentId, release: verified });
16798
16802
  }
16799
- async function uploadProjectWebRelease(http, projectRef, environmentId, deploymentId, archivePath) {
16800
- const endpoint = `/v1/projects/${encodeURIComponent(projectRef)}/environments/${encodeURIComponent(environmentId)}/deployments/web-release?deployment_id=${encodeURIComponent(deploymentId)}`;
16801
- const archive = await verifiedArchive(archivePath);
16802
- const successPayload = (value) => {
16803
- const release = value ? releaseRecord(value.release) : null;
16804
- if (!value || value.ok !== true || value.project_ref !== projectRef || value.environment_id !== environmentId || value.deployment_id !== deploymentId || value.release_id !== archive.sha256 || release?.release_id !== archive.sha256 || release.tree_sha256.length !== 64)
16805
- return null;
16806
- return toolResponse({
16807
- project_ref: projectRef,
16808
- environment_id: environmentId,
16809
- deployment_id: deploymentId,
16810
- release_id: archive.sha256,
16811
- release
16812
- });
16813
- };
16814
- try {
16815
- const response = await http.postBinary(endpoint, {
16816
- stream: archiveStream(archive),
16817
- byteLength: archive.sizeBytes
16818
- }, {
16819
- contentType: FRONTEND_ARCHIVE_CONTENT_TYPE,
16820
- contentLength: archive.sizeBytes,
16821
- contentSha256: archive.sha256,
16822
- maxJsonBytes: RESPONSE_MAX_BYTES,
16823
- timeoutMs: UPLOAD_REQUEST_TIMEOUT_MS,
16824
- responseTimeoutMs: MUTATION_RESPONSE_TIMEOUT_MS
16825
- });
16826
- const value = response.data && typeof response.data === "object" && !Array.isArray(response.data) ? response.data : null;
16827
- if (response.ok && response.status === 201) {
16828
- const success = successPayload(value);
16829
- if (success)
16830
- return success;
16831
- }
16832
- if (response.transportError || response.responseReadError || response.status >= 500 || response.status === 202) {
16833
- const readback = await getFrontendRelease(http, projectRef, deploymentId, archive.sha256);
16834
- if (!readback.isError) {
16835
- const readbackValue = JSON.parse(readback.content[0]?.text ?? "{}");
16836
- const release = readbackValue.release;
16837
- if (release && typeof release === "object" && !Array.isArray(release) && release.release_id === archive.sha256) {
16838
- return toolResponse({
16839
- project_ref: projectRef,
16840
- environment_id: environmentId,
16841
- deployment_id: deploymentId,
16842
- release_id: archive.sha256,
16843
- release
16844
- });
16845
- }
16846
- }
16847
- }
16848
- return releaseFailure("frontend.upload_project_web_release", response.status >= 400 && response.status < 500 && !response.transportError ? "HTTP_ERROR" : "OUTCOME_UNKNOWN", response.status);
16849
- } finally {
16850
- await archive.handle.close();
16851
- }
16852
- }
16853
16803
  async function uploadReadback(http, projectRef, deploymentId, releaseId, uploadStatus) {
16854
16804
  const response = await http.get(releasePath(projectRef, deploymentId, releaseId), {
16855
16805
  maxJsonBytes: RESPONSE_MAX_BYTES
16856
16806
  });
16857
- const release = response.ok ? releaseEnvelope(response.data, { projectRef, deploymentId, releaseId }) : null;
16807
+ const release = response.ok && response.status === 200 ? releaseEnvelope(response.data, { projectRef, deploymentId, releaseId }) : null;
16858
16808
  if (!release) {
16859
16809
  return releaseFailure("frontend.upload_release", "OUTCOME_UNKNOWN", uploadStatus);
16860
16810
  }
@@ -16913,13 +16863,9 @@ function activationReceipt(candidate, input) {
16913
16863
  return release;
16914
16864
  }
16915
16865
  async function activeReleaseReadback(http, identity) {
16916
- const snapshot = await readActiveFrontendRelease(http, identity.projectRef, identity.deploymentId);
16917
- if ("failure" in snapshot)
16918
- return { release: null, status: snapshot.status };
16919
- const inventory = snapshot.inventory;
16920
- if (inventory.active_release_id !== identity.releaseId || inventory.active_activation_id !== identity.mutationId) {
16921
- return { release: null, status: 200 };
16922
- }
16866
+ const snapshot = await activeReleaseSnapshotReadback(http, identity);
16867
+ if (!snapshot.release)
16868
+ return snapshot;
16923
16869
  const releaseRead = await http.get(releasePath(identity.projectRef, identity.deploymentId, identity.releaseId), { maxJsonBytes: RESPONSE_MAX_BYTES });
16924
16870
  const release = releaseRead.ok && releaseRead.status === 200 ? releaseEnvelope(releaseRead.data, {
16925
16871
  projectRef: identity.projectRef,
@@ -16928,6 +16874,16 @@ async function activeReleaseReadback(http, identity) {
16928
16874
  }) : null;
16929
16875
  return { release, status: releaseRead.status };
16930
16876
  }
16877
+ async function activeReleaseSnapshotReadback(http, identity) {
16878
+ const snapshot = await readActiveFrontendRelease(http, identity.projectRef, identity.deploymentId);
16879
+ if ("failure" in snapshot)
16880
+ return { release: null, status: snapshot.status };
16881
+ const inventory = snapshot.inventory;
16882
+ if (inventory.active_release_id !== identity.releaseId || inventory.active_activation_id !== identity.mutationId) {
16883
+ return { release: null, status: 200 };
16884
+ }
16885
+ return { release: inventory.releases[0] ?? null, status: 200 };
16886
+ }
16931
16887
  async function readActiveFrontendRelease(http, projectRef, deploymentId) {
16932
16888
  const response = await http.get(`${deploymentEndpoint(projectRef, deploymentId)}/active-release`, {
16933
16889
  maxJsonBytes: RESPONSE_MAX_BYTES
@@ -16962,7 +16918,7 @@ async function getActiveFrontendRelease(http, projectRef, deploymentId) {
16962
16918
  const snapshot = await readActiveFrontendRelease(http, projectRef, deploymentId);
16963
16919
  return "failure" in snapshot ? snapshot.failure : toolResponse(snapshot.inventory);
16964
16920
  }
16965
- async function publishFrontendRelease(http, projectRef, deploymentId, archivePath, expectedTreeSha256) {
16921
+ async function publishFrontendRelease(http, projectRef, deploymentId, archiveSource, expectedTreeSha256) {
16966
16922
  const authority = await readActiveFrontendRelease(http, projectRef, deploymentId);
16967
16923
  if ("failure" in authority)
16968
16924
  return authority.failure;
@@ -16979,7 +16935,14 @@ async function publishFrontendRelease(http, projectRef, deploymentId, archivePat
16979
16935
  unchanged: true
16980
16936
  });
16981
16937
  }
16982
- const uploaded = await uploadFrontendRelease(http, projectRef, deploymentId, archivePath);
16938
+ const archive = typeof archiveSource === "string" ? { archivePath: archiveSource } : await archiveSource();
16939
+ let uploaded;
16940
+ try {
16941
+ uploaded = await uploadFrontendReleaseWithReadback(http, projectRef, deploymentId, archive.archivePath, false);
16942
+ } finally {
16943
+ if ("cleanup" in archive)
16944
+ await archive.cleanup();
16945
+ }
16983
16946
  if (uploaded.isError)
16984
16947
  return uploaded;
16985
16948
  const value = JSON.parse(uploaded.content[0].text);
@@ -16990,14 +16953,14 @@ async function publishFrontendRelease(http, projectRef, deploymentId, archivePat
16990
16953
  return releaseFailure("frontend.deploy", "INVALID_RESPONSE", 200);
16991
16954
  }
16992
16955
  const mutationId = crypto.randomUUID();
16993
- const activated = await activateFrontendRelease(http, {
16956
+ const activated = await activateFrontendReleaseWithReadback(http, {
16994
16957
  projectRef,
16995
16958
  deploymentId,
16996
16959
  releaseId: release.release_id,
16997
16960
  mutationId,
16998
16961
  expectedActiveReleaseId: inventory.active_release_id ?? "absent",
16999
16962
  expectedActivationId: inventory.active_activation_id ?? "absent"
17000
- });
16963
+ }, activeReleaseSnapshotReadback);
17001
16964
  const activatedPayload = JSON.parse(activated.content[0].text);
17002
16965
  const active = activatedPayload.release;
17003
16966
  const treeMismatch = !activated.isError && (!active || typeof active !== "object" || !("tree_sha256" in active) || active.tree_sha256 !== expectedTreeSha256);
@@ -17176,6 +17139,9 @@ async function readPreviousFrontendRelease(http, projectRef, deploymentId) {
17176
17139
  };
17177
17140
  }
17178
17141
  async function activateFrontendRelease(http, input) {
17142
+ return activateFrontendReleaseWithReadback(http, input, activeReleaseReadback);
17143
+ }
17144
+ async function activateFrontendReleaseWithReadback(http, input, readActive) {
17179
17145
  const mutationId = input.mutationId;
17180
17146
  if (!MUTATION_ID_PATTERN2.test(mutationId))
17181
17147
  throw new Error("'mutation_id' must be a UUIDv4");
@@ -17201,7 +17167,7 @@ async function activateFrontendRelease(http, input) {
17201
17167
  }
17202
17168
  return activationReadback(http, input, mutationId, response.status);
17203
17169
  }
17204
- const readback = await activeReleaseReadback(http, { ...input, mutationId });
17170
+ const readback = await readActive(http, { ...input, mutationId });
17205
17171
  if (!readback.release || readback.release.tree_sha256 !== release.tree_sha256) {
17206
17172
  return releaseFailure("frontend.activate_release", "OUTCOME_UNKNOWN", readback.status);
17207
17173
  }
@@ -26359,6 +26325,7 @@ async function runAppRelease(request, options, delegate) {
26359
26325
  schema: RELEASE_CONTROL_RESPONSE_SCHEMA,
26360
26326
  ok: true,
26361
26327
  operation: "applications.release",
26328
+ ...deployReceipt.status === "accepted" ? { status: "accepted" } : {},
26362
26329
  project_ref: ref,
26363
26330
  application_id: id,
26364
26331
  environment_id: environmentId,
@@ -26369,16 +26336,17 @@ async function runAppRelease(request, options, delegate) {
26369
26336
  build: buildReceipt ? completed.build : { skipped: true, manifest_path: manifestPath },
26370
26337
  phases: completed,
26371
26338
  no_op: deployReceipt.action === "no-op" || deployReceipt.no_op === true,
26372
- execution_performed: deployReceipt.execution_performed === true,
26339
+ ...deployReceipt.status === "accepted" ? {} : { execution_performed: deployReceipt.execution_performed === true },
26373
26340
  ...typeof deployReceipt.activation_id === "string" ? { activation_id: deployReceipt.activation_id } : {},
26374
26341
  ...deployReceipt.web === undefined ? {} : { web: deployReceipt.web }
26375
26342
  };
26376
26343
  return request.format === "json" || request.json === true ? textResult(JSON.stringify(result, null, 2)) : textResult([
26377
- `${ref}/${id}/${environmentId}: ${result.no_op ? "no-op" : "released"}`,
26344
+ `${ref}/${id}/${environmentId}: ${deployReceipt.status === "accepted" ? "accepted" : result.no_op ? "no-op" : "released"}`,
26378
26345
  `Release: ${releaseId}`,
26379
26346
  `Configuration: ${configurationId}`,
26380
26347
  `Activation: ${result.activation_id ?? "not performed"}`,
26381
26348
  ...result.deployment_id === undefined ? [] : [`Deployment: ${result.deployment_id}`],
26349
+ ...deployReceipt.status === "accepted" && result.deployment_id !== undefined ? [`Status: supacloud deploy --status ${result.deployment_id}`] : [],
26382
26350
  `Build: ${request.skip_build ? "skipped" : "completed"}`
26383
26351
  ].join(`
26384
26352
  `));
@@ -29057,11 +29025,22 @@ async function requireProjectDeploymentProtocol(http, projectRef, environmentId)
29057
29025
  throw new Error("DEPLOYMENT_PROTOCOL_MISMATCH: Upgrade the SupaCloud server and CLI together (`npm install -g @supacloud/cli@latest`), then retry `supacloud deploy`. No upload or deployment was attempted; legacy endpoints will not be used.");
29058
29026
  }
29059
29027
  }
29028
+ var operation2 = "applications.project_deploy";
29060
29029
  var scopeSchema = _Object_({
29061
29030
  project_ref: String2({ pattern: "^[a-z0-9-]{1,20}$" }),
29062
29031
  application_id: ApplicationIdSchema,
29063
29032
  environment_id: ApplicationIdSchema
29064
29033
  });
29034
+ var acceptedSchema = _Object_({
29035
+ schema: Literal("supacloud.project-deployment-accepted.v1"),
29036
+ ok: Literal(true),
29037
+ status: Literal("accepted"),
29038
+ deployment_id: ApplicationDeployRequestSchema.properties.mutation_id,
29039
+ ...scopeSchema.properties,
29040
+ release_id: ApplicationDeployRequestSchema.properties.release_id,
29041
+ request_sha256: String2({ pattern: "^[a-f0-9]{64}$" }),
29042
+ replayed: Boolean2()
29043
+ }, { additionalProperties: false });
29065
29044
  var statusSchema = _Object_({
29066
29045
  schema: Literal("supacloud.project-deployment-status.v1"),
29067
29046
  deployment_id: ApplicationDeployRequestSchema.properties.mutation_id,
@@ -29086,6 +29065,27 @@ var statusSchema = _Object_({
29086
29065
  receipt: Optional(Unknown()),
29087
29066
  failure_code: Optional(String2({ pattern: "^[A-Z][A-Z0-9_]{0,63}$" }))
29088
29067
  }, { additionalProperties: false });
29068
+ async function readProjectDeploymentStatus(http, projectRef, environmentId, deploymentId) {
29069
+ if (!/^[a-z0-9-]{1,20}$/.test(projectRef) || !Check2(ApplicationIdSchema, environmentId) || !Check2(ApplicationDeployRequestSchema.properties.mutation_id, deploymentId)) {
29070
+ return releaseControlFailure(operation2, "INVALID_RESPONSE", null);
29071
+ }
29072
+ const path = `/v1/projects/${encodeURIComponent(projectRef)}/environments/${encodeURIComponent(environmentId)}/deployments/${deploymentId}`;
29073
+ const response = await http.get(path, { retry: false, totalTimeoutMs: 1e4, maxJsonBytes: 131072 });
29074
+ try {
29075
+ const state = response.data;
29076
+ if (!response.ok || response.status !== 200 || !Check2(statusSchema, state) || state.project_ref !== projectRef || state.environment_id !== environmentId || state.deployment_id !== deploymentId)
29077
+ throw new Error;
29078
+ if (state.status === "succeeded") {
29079
+ const receipt = parseApplicationDeployResult(state.receipt);
29080
+ if (state.phase !== "verifying" || receipt.project_ref !== projectRef || receipt.environment_id !== environmentId || receipt.application_id !== state.application_id || receipt.mutation_id !== deploymentId)
29081
+ throw new Error;
29082
+ } else if (state.receipt !== undefined)
29083
+ throw new Error;
29084
+ return state.status === "outcome_unknown" || state.status === "failed_terminal" ? releaseControlFailure(operation2, state.status === "outcome_unknown" ? "OUTCOME_UNKNOWN" : "MUTATION_NOT_SUCCEEDED", response.status, { ...state }) : releaseControlSuccess(operation2, { ...state });
29085
+ } catch {
29086
+ return releaseControlFailure(operation2, "INVALID_RESPONSE", response.status);
29087
+ }
29088
+ }
29089
29089
 
29090
29090
  // src/shared/tools/deploy-tools.ts
29091
29091
  var MAX_SOURCE_BYTES = 256 * 1024 * 1024;
@@ -29102,6 +29102,8 @@ var deployToolSchema = {
29102
29102
  cwd: optional(String2(), "Project directory (default: current directory)"),
29103
29103
  skip_build: optional(Boolean2(), "Use an existing output directory without building"),
29104
29104
  assets_only: optional(Boolean2(), "Publish only configured static assets; leave backend Functions, schedules and migrations unchanged"),
29105
+ backend: optional(Boolean2(), "Publish the application backend independently; do not build or activate frontend assets"),
29106
+ status: optional(String2({ format: "uuid" }), "Read a backend deployment status without publishing"),
29105
29107
  json: optional(Boolean2(), "Print only the final JSON result")
29106
29108
  };
29107
29109
  function record4(candidate) {
@@ -29477,8 +29479,7 @@ async function collectArchiveFiles(root) {
29477
29479
  }
29478
29480
  return { files, bytes, count: paths.length, treeSha256: tree.digest("hex") };
29479
29481
  }
29480
- async function createFrontendArchive(outputDirectory) {
29481
- const collected = await collectArchiveFiles(outputDirectory);
29482
+ async function createCollectedFrontendArchive(outputDirectory, collected) {
29482
29483
  const temporaryDirectory = await mkdtemp2(join18(tmpdir3(), "supacloud-deploy-"));
29483
29484
  const archivePath = join18(temporaryDirectory, `${basename8(outputDirectory)}.tar.zst`);
29484
29485
  try {
@@ -29499,16 +29500,7 @@ async function createFrontendArchive(outputDirectory) {
29499
29500
  throw error;
29500
29501
  }
29501
29502
  }
29502
- function payload(response) {
29503
- if (response.isError)
29504
- throw new Error(response.content[0]?.text || "Deployment operation failed");
29505
- const text = response.content.find((chunk) => chunk.type === "text")?.text;
29506
- const parsed = text ? record4(JSON.parse(text)) : null;
29507
- if (!parsed)
29508
- throw new Error("Deployment operation returned an invalid response");
29509
- return parsed;
29510
- }
29511
- async function stageApplicationWebRelease(http, projectRef, environmentId, configRoot, skipBuild, json, environmentVariables, assetsOnly = false) {
29503
+ async function publishConfiguredFrontend(http, projectRef, configRoot, skipBuild, json, environmentVariables) {
29512
29504
  const target = selectFrontendTarget(configRoot, await readDeployConfig(configRoot));
29513
29505
  if (!target)
29514
29506
  return null;
@@ -29532,27 +29524,8 @@ async function stageApplicationWebRelease(http, projectRef, environmentId, confi
29532
29524
  if (!skipBuild) {
29533
29525
  await runBuild(buildCommand, projectDirectory, build.env, json);
29534
29526
  }
29535
- const archive = await createFrontendArchive(outputDirectory);
29536
- try {
29537
- if (assetsOnly) {
29538
- return { result: await publishFrontendRelease(http, projectRef, selected.id, archive.archivePath, archive.treeSha256) };
29539
- }
29540
- const uploaded = payload(await uploadProjectWebRelease(http, projectRef, environmentId, selected.id, archive.archivePath));
29541
- const release = record4(uploaded.release);
29542
- const releaseId = requiredString(release?.release_id, "release.release_id");
29543
- if (releaseId !== archive.sha256)
29544
- throw new Error("Uploaded Web release identity does not match the local archive");
29545
- if (release?.tree_sha256 !== archive.treeSha256)
29546
- throw new Error("Uploaded Web release tree does not match the local build");
29547
- return {
29548
- request: {
29549
- deployment_id: selected.id,
29550
- release_id: releaseId
29551
- }
29552
- };
29553
- } finally {
29554
- await archive.cleanup();
29555
- }
29527
+ const collected = await collectArchiveFiles(outputDirectory);
29528
+ return publishFrontendRelease(http, projectRef, selected.id, () => createCollectedFrontendArchive(outputDirectory, collected), collected.treeSha256);
29556
29529
  }
29557
29530
  function registerDeployTools(server, http, options = {}) {
29558
29531
  registerTool(server, "deploy", "Build and publish the linked project with one command", deployToolSchema, async (args) => {
@@ -29560,6 +29533,16 @@ function registerDeployTools(server, http, options = {}) {
29560
29533
  const projectRef = String(args.ref || options.projectRef || "").trim();
29561
29534
  if (!projectRef)
29562
29535
  throw new Error("A linked project ref is required. Set SUPACLOUD_PROJECT_REF or pass --ref");
29536
+ if (args.assets_only === true && args.backend === true)
29537
+ throw new Error("Choose either frontend assets or backend deployment");
29538
+ if (typeof args.status === "string") {
29539
+ if (args.assets_only === true || args.backend === true)
29540
+ throw new Error("Status cannot be combined with publishing options");
29541
+ const environmentId = options.environment?.trim() || options.application?.environmentId?.trim();
29542
+ if (!environmentId)
29543
+ throw new Error("Deployment status requires SUPACLOUD_ENV");
29544
+ return readProjectDeploymentStatus(http, projectRef, environmentId, args.status);
29545
+ }
29563
29546
  const configRoot = await findDeployConfigRoot(invocationDirectory, options.environment ?? undefined);
29564
29547
  const config = await readDeployConfig(configRoot);
29565
29548
  const application = options.application?.id ? options.application : config.application ? {
@@ -29567,7 +29550,7 @@ function registerDeployTools(server, http, options = {}) {
29567
29550
  environmentId: options.environment || config.application.environmentId
29568
29551
  } : undefined;
29569
29552
  const legacyManifest = await hasProjectReleaseManifest(configRoot);
29570
- const assetsOnly = args.assets_only === true || !application?.id && selectFrontendTarget(configRoot, config) !== null && !legacyManifest && !Object.values(config.targets ?? {}).some((target) => target.type === "edge_function");
29553
+ const assetsOnly = args.assets_only === true || args.backend !== true && selectFrontendTarget(configRoot, config) !== null;
29571
29554
  if (assetsOnly) {
29572
29555
  if (!selectFrontendTarget(configRoot, config)) {
29573
29556
  throw new Error("Static asset deployment requires a frontend configuration in supacloud.json");
@@ -29577,10 +29560,10 @@ function registerDeployTools(server, http, options = {}) {
29577
29560
  throw new Error("Static asset deployment requires SUPACLOUD_ENV");
29578
29561
  }
29579
29562
  await requireProjectDeploymentProtocol(http, projectRef, environmentId);
29580
- const staged = await stageApplicationWebRelease(http, projectRef, environmentId, configRoot, args.skip_build === true, args.json === true, options.environmentVariables, true);
29581
- if (!staged?.result)
29563
+ const result = await publishConfiguredFrontend(http, projectRef, configRoot, args.skip_build === true, args.json === true, options.environmentVariables);
29564
+ if (!result)
29582
29565
  throw new Error("Static asset deployment did not produce a receipt");
29583
- return staged.result;
29566
+ return result;
29584
29567
  }
29585
29568
  if (legacyManifest) {
29586
29569
  throw new Error(`${DEPLOYMENT_UPGRADE} Replace deploy-manifest.json with the current application delivery configuration; no remote mutation was performed.`);
@@ -29602,7 +29585,6 @@ function registerDeployTools(server, http, options = {}) {
29602
29585
  throw new Error("Application profile must set only one of SUPACLOUD_APPLICATION_CONFIGURATION_PATH or SUPACLOUD_APPLICATION_CONFIGURATION_ID");
29603
29586
  }
29604
29587
  await requireProjectDeploymentProtocol(http, projectRef, environmentId);
29605
- const stagedWeb = await stageApplicationWebRelease(http, projectRef, environmentId, configRoot, args.skip_build === true, args.json === true, options.environmentVariables);
29606
29588
  const result = await options.applicationRelease({
29607
29589
  action: "release",
29608
29590
  ref: projectRef,
@@ -29613,7 +29595,6 @@ function registerDeployTools(server, http, options = {}) {
29613
29595
  ...configurationId ? { configuration_id: configurationId } : {},
29614
29596
  ...application.manifestPath ? { manifest_path: application.manifestPath } : {},
29615
29597
  ...args.skip_build === true ? { skip_build: true } : {},
29616
- ...stagedWeb?.request === undefined ? {} : { web_release: stagedWeb.request },
29617
29598
  ...args.json === true ? { format: "json", json: true } : {}
29618
29599
  });
29619
29600
  return result;
@@ -30408,12 +30389,22 @@ function registerDeveloperExperienceTools(server, options = {}) {
30408
30389
  }
30409
30390
 
30410
30391
  // src/shared/tools/project-deployment.ts
30411
- var operation2 = "applications.project_deploy";
30392
+ var operation3 = "applications.project_deploy";
30412
30393
  var scopeSchema2 = _Object_({
30413
30394
  project_ref: String2({ pattern: "^[a-z0-9-]{1,20}$" }),
30414
30395
  application_id: ApplicationIdSchema,
30415
30396
  environment_id: ApplicationIdSchema
30416
30397
  });
30398
+ var acceptedSchema2 = _Object_({
30399
+ schema: Literal("supacloud.project-deployment-accepted.v1"),
30400
+ ok: Literal(true),
30401
+ status: Literal("accepted"),
30402
+ deployment_id: ApplicationDeployRequestSchema.properties.mutation_id,
30403
+ ...scopeSchema2.properties,
30404
+ release_id: ApplicationDeployRequestSchema.properties.release_id,
30405
+ request_sha256: String2({ pattern: "^[a-f0-9]{64}$" }),
30406
+ replayed: Boolean2()
30407
+ }, { additionalProperties: false });
30417
30408
  var statusSchema2 = _Object_({
30418
30409
  schema: Literal("supacloud.project-deployment-status.v1"),
30419
30410
  deployment_id: ApplicationDeployRequestSchema.properties.mutation_id,
@@ -30457,7 +30448,7 @@ async function runProjectDeployment(http, args) {
30457
30448
  ...args.web_release === undefined ? {} : { web_release: args.web_release }
30458
30449
  };
30459
30450
  if (!Check2(scopeSchema2, scope) || !Check2(ApplicationDeployRequestSchema, request) || request.configuration_id !== undefined && request.configuration_write !== undefined && request.configuration_id !== request.configuration_write.configuration_id) {
30460
- return releaseControlFailure(operation2, "INVALID_RESPONSE", null);
30451
+ return releaseControlFailure(operation3, "INVALID_RESPONSE", null);
30461
30452
  }
30462
30453
  const expected = { ...scope, request };
30463
30454
  const identity = {
@@ -30473,15 +30464,29 @@ async function runProjectDeployment(http, args) {
30473
30464
  deployment_id: mutation_id,
30474
30465
  application_id: scope.application_id
30475
30466
  }, { timeoutMs: 120000, maxJsonBytes: 131072, responseTimeoutMs: 30000 });
30476
- if (result.ok && result.status === 200) {
30467
+ if (result.ok && result.status === 202) {
30468
+ const accepted = result.data;
30469
+ if (Check2(acceptedSchema2, accepted) && accepted.deployment_id === mutation_id && accepted.project_ref === scope.project_ref && accepted.application_id === scope.application_id && accepted.environment_id === scope.environment_id && accepted.release_id === request.release_id && accepted.request_sha256 === mutationRequestFingerprint(request)) {
30470
+ return releaseControlSuccess(operation3, {
30471
+ status: "accepted",
30472
+ deployment_id: mutation_id,
30473
+ project_ref: scope.project_ref,
30474
+ application_id: scope.application_id,
30475
+ environment_id: scope.environment_id,
30476
+ release_id: request.release_id,
30477
+ replayed: accepted.replayed,
30478
+ status_path: `${endpoint}/${mutation_id}`
30479
+ });
30480
+ }
30481
+ } else if (result.ok && result.status === 200) {
30477
30482
  try {
30478
- return releaseControlSuccess(operation2, {
30483
+ return releaseControlSuccess(operation3, {
30479
30484
  ...responseReceipt(result.data, expected),
30480
30485
  deployment_id: mutation_id
30481
30486
  });
30482
30487
  } catch {}
30483
30488
  } else if (!result.ok && !result.transportError && !result.responseReadError && result.status < 500 && result.status !== 408) {
30484
- return releaseControlMutationFailure(operation2, result, identity);
30489
+ return releaseControlMutationFailure(operation3, result, identity);
30485
30490
  }
30486
30491
  const observed = await http.get(`${endpoint}/${encodeURIComponent(mutation_id)}`, {
30487
30492
  retry: false,
@@ -30495,20 +30500,20 @@ async function runProjectDeployment(http, args) {
30495
30500
  if (state.deployment_id !== mutation_id || state.project_ref !== scope.project_ref || state.application_id !== scope.application_id || state.environment_id !== scope.environment_id || state.status !== "succeeded" || state.phase !== "verifying")
30496
30501
  throw new Error;
30497
30502
  const receipt = parseApplicationDeployResult(state.receipt, expected);
30498
- return releaseControlSuccess(operation2, {
30503
+ return releaseControlSuccess(operation3, {
30499
30504
  ...receipt,
30500
30505
  deployment_id: mutation_id,
30501
30506
  replayed: true,
30502
30507
  execution_performed: false
30503
30508
  });
30504
30509
  } catch {
30505
- return releaseControlFailure(operation2, "OUTCOME_UNKNOWN", result.transportError ? null : result.status, identity);
30510
+ return releaseControlFailure(operation3, "OUTCOME_UNKNOWN", result.transportError ? null : result.status, identity);
30506
30511
  }
30507
30512
  }
30508
30513
  // package.json
30509
30514
  var package_default = {
30510
30515
  name: "@supacloud/cli",
30511
- version: "0.98.0",
30516
+ version: "0.99.0",
30512
30517
  description: "Project-scoped CLI for SupaCloud users",
30513
30518
  type: "module",
30514
30519
  main: "./dist/index.js",
@@ -1,2 +1,2 @@
1
- export declare const DEPLOYMENT_UPGRADE = "DEPLOYMENT_UPGRADE_REQUIRED: Legacy deployment commands are no longer supported. Upgrade with `npm install -g @supacloud/cli@latest`, configure frontend in supacloud.json for static assets, or application.id and application.environmentId for executable application delivery, then run `supacloud deploy`. Use `supacloud deploy --assets_only` to publish only Web assets in a mixed repository. SupaCloud owns activation; do not retry with legacy commands.";
1
+ export declare const DEPLOYMENT_UPGRADE = "DEPLOYMENT_UPGRADE_REQUIRED: Legacy deployment commands are no longer supported. Upgrade with `npm install -g @supacloud/cli@latest`, configure frontend in supacloud.json for static assets, or application.id and application.environmentId for executable application delivery, then run `supacloud deploy`. With a frontend configured, deploy publishes only Web assets; use `supacloud deploy --backend` for the application backend. `supacloud deploy --assets_only` remains an explicit Web-only option. SupaCloud owns activation; do not retry with legacy commands.";
2
2
  export declare function assertCurrentDeploymentInput(tool: string, input: unknown): void;
@@ -27,6 +27,8 @@ export declare const deployToolSchema: {
27
27
  cwd: Type.TOptional<Type.TString>;
28
28
  skip_build: Type.TOptional<Type.TBoolean>;
29
29
  assets_only: Type.TOptional<Type.TBoolean>;
30
+ backend: Type.TOptional<Type.TBoolean>;
31
+ status: Type.TOptional<Type.TString>;
30
32
  json: Type.TOptional<Type.TBoolean>;
31
33
  };
32
34
  export declare function findDeployConfigRoot(startDirectory: string, environmentName?: string): Promise<string>;
@@ -46,7 +46,10 @@ export declare function uploadProjectWebRelease(http: FrontendReleaseHttp, proje
46
46
  export declare function uploadFrontendSource(http: FrontendReleaseHttp, projectRef: string, deploymentId: string, archivePath: string): Promise<ToolResponse>;
47
47
  export declare function getActiveFrontendRelease(http: FrontendReleaseHttp, projectRef: string, deploymentId: string): Promise<ToolResponse>;
48
48
  export declare function readFrontendReleaseAuthority(http: FrontendReleaseHttp, projectRef: string, deploymentId: string): Promise<ToolResponse>;
49
- export declare function publishFrontendRelease(http: FrontendReleaseHttp, projectRef: string, deploymentId: string, archivePath: string, expectedTreeSha256: string): Promise<ToolResponse>;
49
+ export declare function publishFrontendRelease(http: FrontendReleaseHttp, projectRef: string, deploymentId: string, archiveSource: string | (() => Promise<{
50
+ archivePath: string;
51
+ cleanup(): Promise<void>;
52
+ }>), expectedTreeSha256: string): Promise<ToolResponse>;
50
53
  export declare function rollbackFrontendRelease(http: FrontendReleaseHttp, projectRef: string, deploymentId: string, releaseId?: string, options?: {
51
54
  expectedActiveReleaseId?: string;
52
55
  expectedActivationId?: string;
@@ -2,3 +2,4 @@ import type { ToolResult } from "../tool-server";
2
2
  import type { HttpTransport } from "../transports/http";
3
3
  export declare function requireProjectDeploymentProtocol(http: Pick<HttpTransport, "get">, projectRef: string, environmentId: string): Promise<void>;
4
4
  export declare function runProjectDeployment(http: Pick<HttpTransport, "post" | "get">, args: Record<string, unknown>): Promise<ToolResult>;
5
+ export declare function readProjectDeploymentStatus(http: Pick<HttpTransport, "get">, projectRef: string, environmentId: string, deploymentId: string): Promise<ToolResult>;
@@ -131,7 +131,7 @@ import { Type as Type2 } from "typebox";
131
131
  import { Value as Value2 } from "typebox/value";
132
132
 
133
133
  // src/shared/deployment-upgrade.ts
134
- var DEPLOYMENT_UPGRADE = "DEPLOYMENT_UPGRADE_REQUIRED: Legacy deployment commands are no longer supported. Upgrade with `npm install -g @supacloud/cli@latest`, configure frontend in supacloud.json for static assets, or application.id and application.environmentId for executable application delivery, then run `supacloud deploy`. Use `supacloud deploy --assets_only` to publish only Web assets in a mixed repository. SupaCloud owns activation; do not retry with legacy commands.";
134
+ var DEPLOYMENT_UPGRADE = "DEPLOYMENT_UPGRADE_REQUIRED: Legacy deployment commands are no longer supported. Upgrade with `npm install -g @supacloud/cli@latest`, configure frontend in supacloud.json for static assets, or application.id and application.environmentId for executable application delivery, then run `supacloud deploy`. With a frontend configured, deploy publishes only Web assets; use `supacloud deploy --backend` for the application backend. `supacloud deploy --assets_only` remains an explicit Web-only option. SupaCloud owns activation; do not retry with legacy commands.";
135
135
  var removedFrontendActions = new Set([
136
136
  "deploy_git",
137
137
  "deploy_upload",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.98.0",
3
+ "version": "0.99.0",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",