@supacloud/cli 0.84.0 → 0.85.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/dist/index.js CHANGED
@@ -9249,7 +9249,7 @@ var ACTION_POLICY = {
9249
9249
  app: {
9250
9250
  local: ["init", "generate", "dev", "watch", "verify-plan", "compile", "check", "graph", "explain", "export-tools", "context", "doctor", "fix", "plan", "build"],
9251
9251
  read: ["status", "logs", "transfer-plan", "promote-plan", "rollback-plan", "history", "deploy-plan", "diff", "promote-status", "preview-plan", "previews", "preview-list", "preview-status"],
9252
- write: ["upload", "configure", "transfer", "promote", "promote-reconcile", "deploy", "rollback", "reconcile", "retire", "preview", "preview-create", "preview-reconcile", "preview-cleanup"]
9252
+ write: ["release", "upload", "configure", "transfer", "promote", "promote-reconcile", "deploy", "rollback", "reconcile", "retire", "preview", "preview-create", "preview-reconcile", "preview-cleanup"]
9253
9253
  },
9254
9254
  db: {
9255
9255
  local: ["context", "lint", "explain", "diff", "role_sql"],
@@ -24419,14 +24419,14 @@ var REMOTE_APP_ACTIONS = {
24419
24419
  "preview-reconcile": "reconcile_preview"
24420
24420
  };
24421
24421
  var REMOTE_APP_DESCRIPTIONS = {
24422
- ref: "[upload/configure/transfer-plan/transfer/promote-plan/diff/promote/promote-status/promote-reconcile/preview-plan/preview/previews/preview-list/preview-create/preview-status/preview-reconcile/preview-cleanup/deploy/deploy-plan/status/rollback/rollback-plan/history/reconcile/retire] Project ref (defaults to context)",
24423
- id: "[upload/configure/transfer-plan/transfer/promote-plan/diff/promote/promote-status/promote-reconcile/preview-plan/preview/previews/preview-list/preview-create/preview-status/preview-reconcile/preview-cleanup/deploy/deploy-plan/status/rollback/rollback-plan/history/reconcile/retire] Application ID",
24424
- environment_id: "[configure/promote-plan/diff/promote/promote-status/promote-reconcile/preview-plan/preview/previews/preview-list/preview-create/preview-status/preview-reconcile/preview-cleanup/deploy/deploy-plan/status/rollback/rollback-plan/history/reconcile/retire] Target environment ID",
24425
- configuration_id: "[promote-plan/diff/promote/preview-plan/preview/preview-create/deploy/deploy-plan/rollback] Immutable target configuration revision",
24422
+ ref: "[release/upload/configure/transfer-plan/transfer/promote-plan/diff/promote/promote-status/promote-reconcile/preview-plan/preview/previews/preview-list/preview-create/preview-status/preview-reconcile/preview-cleanup/deploy/deploy-plan/status/rollback/rollback-plan/history/reconcile/retire] Project ref (defaults to context)",
24423
+ id: "[release/upload/configure/transfer-plan/transfer/promote-plan/diff/promote/promote-status/promote-reconcile/preview-plan/preview/previews/preview-list/preview-create/preview-status/preview-reconcile/preview-cleanup/deploy/deploy-plan/status/rollback/rollback-plan/history/reconcile/retire] Application ID",
24424
+ environment_id: "[release/configure/promote-plan/diff/promote/promote-status/promote-reconcile/preview-plan/preview/previews/preview-list/preview-create/preview-status/preview-reconcile/preview-cleanup/deploy/deploy-plan/status/rollback/rollback-plan/history/reconcile/retire] Target environment ID",
24425
+ configuration_id: "[release/promote-plan/diff/promote/preview-plan/preview/preview-create/deploy/deploy-plan/rollback] Immutable target configuration revision",
24426
24426
  activation_id: "[deploy/rollback/reconcile/retire] Required explicit activation ID",
24427
24427
  expected_activation_id: "[deploy/rollback] Required current activation ID, or absent for first activation",
24428
- configuration_path: "[configure] Configuration write JSON including revision and expected revision",
24429
- manifest_path: "[upload] Local delivery.manifest.json",
24428
+ configuration_path: "[release/configure] Configuration write JSON including revision and expected revision",
24429
+ manifest_path: "[release/upload] Local delivery.manifest.json",
24430
24430
  release_id: "[preview-plan/preview/preview-create/deploy/deploy-plan/diff/rollback/reconcile] Required immutable application release ID",
24431
24431
  source_ref: "[transfer-plan/transfer/promote-plan/diff/promote] Source project ref",
24432
24432
  source_release_id: "[transfer-plan/transfer/promote-plan/diff/promote] Source immutable release ID",
@@ -25340,6 +25340,177 @@ async function runDelivery(args) {
25340
25340
  const result = await buildDeliveryProject(options, loaded.delivery);
25341
25341
  return textResult(JSON.stringify(result, null, 2), !result.ok);
25342
25342
  }
25343
+ function resultObject(result) {
25344
+ try {
25345
+ const value = JSON.parse(result.content[0]?.text ?? "");
25346
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
25347
+ } catch {
25348
+ return;
25349
+ }
25350
+ }
25351
+ function resultErrorCode(result, fallback) {
25352
+ const payload = resultObject(result);
25353
+ const error = payload?.error;
25354
+ const errorRecord = error !== null && typeof error === "object" && !Array.isArray(error) ? error : undefined;
25355
+ return typeof errorRecord?.code === "string" ? errorRecord.code : fallback;
25356
+ }
25357
+ function releasePhaseFailure(phase, result, completed) {
25358
+ const phaseResult = resultObject(result);
25359
+ const upload = completed.upload !== null && typeof completed.upload === "object" && !Array.isArray(completed.upload) ? completed.upload : undefined;
25360
+ const configure = completed.configure !== null && typeof completed.configure === "object" && !Array.isArray(completed.configure) ? completed.configure : undefined;
25361
+ const releaseId = typeof upload?.release_id === "string" ? upload.release_id : undefined;
25362
+ const configurationId = typeof configure?.configuration_id === "string" ? configure.configuration_id : undefined;
25363
+ const payload = {
25364
+ schema: RELEASE_CONTROL_RESPONSE_SCHEMA,
25365
+ ok: false,
25366
+ operation: "applications.release",
25367
+ phase,
25368
+ phases: completed,
25369
+ error: { code: resultErrorCode(result, phase === "build" ? "BUILD_FAILED" : "PHASE_FAILED") },
25370
+ ...releaseId === undefined ? {} : { release_id: releaseId },
25371
+ ...configurationId === undefined ? {} : { configuration_id: configurationId },
25372
+ ...phaseResult === undefined ? {} : { phase_result: phaseResult }
25373
+ };
25374
+ if (phaseResult?.activation_id !== undefined)
25375
+ payload.activation_id = phaseResult.activation_id;
25376
+ if (phaseResult?.expected_activation_id !== undefined) {
25377
+ payload.expected_activation_id = phaseResult.expected_activation_id;
25378
+ }
25379
+ return textResult(JSON.stringify(payload, null, 2), true);
25380
+ }
25381
+ function releaseField(payload, container, field) {
25382
+ const value = payload?.[container];
25383
+ if (value === null || typeof value !== "object" || Array.isArray(value))
25384
+ return;
25385
+ const record = value;
25386
+ return typeof record[field] === "string" ? record[field] : undefined;
25387
+ }
25388
+ async function runAppRelease(request, options, delegate) {
25389
+ const ref = request.ref ?? options.projectRef;
25390
+ const id = request.id?.trim();
25391
+ const environmentId = request.environment_id?.trim();
25392
+ if (!ref || !id || !environmentId) {
25393
+ throw new Error("app release requires --ref, --id and --environment_id (ref may come from context)");
25394
+ }
25395
+ if (!Check2(ApplicationIdSchema2, id) || !Check2(ApplicationIdSchema2, environmentId)) {
25396
+ throw new Error("app release requires valid --id and --environment_id");
25397
+ }
25398
+ if (request.configuration_id !== undefined && !Check2(ApplicationConfigurationIdSchema2, request.configuration_id)) {
25399
+ throw new Error("app release requires a valid --configuration_id");
25400
+ }
25401
+ if (request.configuration_path !== undefined && request.configuration_id !== undefined) {
25402
+ throw new Error("app release accepts either --configuration_path or --configuration_id, not both");
25403
+ }
25404
+ if (request.configuration_path === undefined && request.configuration_id === undefined) {
25405
+ throw new Error("app release requires --configuration_path or --configuration_id");
25406
+ }
25407
+ if (request.activation_id !== undefined || request.expected_activation_id !== undefined) {
25408
+ throw new Error("app release generates one activation identity; use app deploy for an exact retry");
25409
+ }
25410
+ const root = resolve17(request.root || process.cwd());
25411
+ const completed = {};
25412
+ let manifestPath;
25413
+ let buildReceipt;
25414
+ if (request.skip_build === true) {
25415
+ if (request.manifest_path === undefined) {
25416
+ throw new Error("app release --skip_build requires --manifest_path");
25417
+ }
25418
+ manifestPath = resolve17(root, request.manifest_path);
25419
+ completed.build = { skipped: true, manifest_path: manifestPath };
25420
+ } else {
25421
+ const built = await runDelivery({ ...request, action: "build", root, format: "json" });
25422
+ buildReceipt = resultObject(built);
25423
+ if (built.isError || !buildReceipt?.ok || buildReceipt.manifest === null || buildReceipt.manifest === undefined) {
25424
+ return releasePhaseFailure("build", built, completed);
25425
+ }
25426
+ const loaded = await loadSupacloudConfig(root);
25427
+ const resolved = resolveSupacloudConfig(loaded, root);
25428
+ manifestPath = join13(resolved.outDir, "delivery", "delivery.manifest.json");
25429
+ completed.build = {
25430
+ ok: true,
25431
+ manifest_path: manifestPath,
25432
+ written: Array.isArray(buildReceipt.written) ? buildReceipt.written.length : 0
25433
+ };
25434
+ }
25435
+ const uploadArgs = {
25436
+ action: "upload_release",
25437
+ ref,
25438
+ id,
25439
+ manifest_path: manifestPath
25440
+ };
25441
+ validateToolArguments(APPLICATION_TOOL_SCHEMA, uploadArgs);
25442
+ const uploaded = await delegate(uploadArgs);
25443
+ const uploadReceipt = resultObject(uploaded);
25444
+ if (uploaded.isError || !uploadReceipt?.ok) {
25445
+ return releasePhaseFailure("upload", uploaded, completed);
25446
+ }
25447
+ const releaseId = releaseField(uploadReceipt, "release", "release_id");
25448
+ if (!releaseId)
25449
+ return releasePhaseFailure("upload", releaseControlFailure("applications.upload_release", "INVALID_RESPONSE", null, { project_ref: ref, application_id: id }), completed);
25450
+ completed.upload = { ok: true, release_id: releaseId };
25451
+ let configurationId = request.configuration_id;
25452
+ if (request.configuration_path !== undefined) {
25453
+ const configurationPath = resolve17(root, request.configuration_path);
25454
+ const configureArgs = {
25455
+ action: "put_configuration",
25456
+ ref,
25457
+ id,
25458
+ environment_id: environmentId,
25459
+ configuration_path: configurationPath
25460
+ };
25461
+ validateToolArguments(APPLICATION_TOOL_SCHEMA, configureArgs);
25462
+ const configured = await delegate(configureArgs);
25463
+ const configurationReceipt = resultObject(configured);
25464
+ if (configured.isError || !configurationReceipt?.ok) {
25465
+ return releasePhaseFailure("configure", configured, completed);
25466
+ }
25467
+ configurationId = releaseField(configurationReceipt, "configuration", "configuration_id");
25468
+ if (!configurationId)
25469
+ return releasePhaseFailure("configure", releaseControlFailure("applications.put_configuration", "INVALID_RESPONSE", null, { project_ref: ref, application_id: id, environment_id: environmentId }), completed);
25470
+ completed.configure = { ok: true, configuration_id: configurationId };
25471
+ } else {
25472
+ completed.configure = { skipped: true, configuration_id: configurationId };
25473
+ }
25474
+ const deployArgs = {
25475
+ action: "deploy_release",
25476
+ ref,
25477
+ id,
25478
+ environment_id: environmentId,
25479
+ release_id: releaseId,
25480
+ configuration_id: configurationId
25481
+ };
25482
+ validateToolArguments(APPLICATION_TOOL_SCHEMA, deployArgs);
25483
+ const deployed = await delegate(deployArgs);
25484
+ const deployReceipt = resultObject(deployed);
25485
+ if (deployed.isError || !deployReceipt?.ok) {
25486
+ return releasePhaseFailure("deploy", deployed, completed);
25487
+ }
25488
+ completed.deploy = deployReceipt;
25489
+ const result = {
25490
+ schema: RELEASE_CONTROL_RESPONSE_SCHEMA,
25491
+ ok: true,
25492
+ operation: "applications.release",
25493
+ project_ref: ref,
25494
+ application_id: id,
25495
+ environment_id: environmentId,
25496
+ release_id: releaseId,
25497
+ configuration_id: configurationId,
25498
+ manifest_path: manifestPath,
25499
+ build: buildReceipt ? completed.build : { skipped: true, manifest_path: manifestPath },
25500
+ phases: completed,
25501
+ no_op: deployReceipt.no_op === true,
25502
+ execution_performed: deployReceipt.execution_performed === true,
25503
+ ...typeof deployReceipt.activation_id === "string" ? { activation_id: deployReceipt.activation_id } : {}
25504
+ };
25505
+ return request.format === "json" || request.json === true ? textResult(JSON.stringify(result, null, 2)) : textResult([
25506
+ `${ref}/${id}/${environmentId}: ${result.no_op ? "no-op" : "released"}`,
25507
+ `Release: ${releaseId}`,
25508
+ `Configuration: ${configurationId}`,
25509
+ `Activation: ${result.activation_id ?? "not performed"}`,
25510
+ `Build: ${request.skip_build ? "skipped" : "completed"}`
25511
+ ].join(`
25512
+ `));
25513
+ }
25343
25514
  function formatReleaseTransfer(result) {
25344
25515
  if (result.isError)
25345
25516
  return result;
@@ -25610,7 +25781,7 @@ async function runAppTool(request, options = {}) {
25610
25781
  if (request.database_url !== undefined && request.action !== "dev" && request.action !== "watch") {
25611
25782
  throw new ScaffoldError("SCAFFOLD_OPTION_INVALID", "--database-url applies only to app dev --profile integration");
25612
25783
  }
25613
- if (request.json !== undefined && !Object.hasOwn(REMOTE_APP_ACTIONS, request.action) && request.action !== "promote")
25784
+ if (request.json !== undefined && !Object.hasOwn(REMOTE_APP_ACTIONS, request.action) && request.action !== "promote" && request.action !== "release")
25614
25785
  throw new Error("--json applies only to remote app actions");
25615
25786
  if (request.json && request.format === "text")
25616
25787
  throw new Error("--json and --format text cannot be combined");
@@ -25620,6 +25791,12 @@ async function runAppTool(request, options = {}) {
25620
25791
  return textResult("App remote actions require a Management API context.", true);
25621
25792
  return runPromotion(request, options, delegate);
25622
25793
  }
25794
+ if (request.action === "release") {
25795
+ const delegate = options.getApplications?.();
25796
+ if (!delegate)
25797
+ return textResult("App remote actions require a Management API context.", true);
25798
+ return runAppRelease(request, options, delegate);
25799
+ }
25623
25800
  if (Object.hasOwn(REMOTE_APP_ACTIONS, request.action)) {
25624
25801
  const delegate = options.getApplications?.();
25625
25802
  if (!delegate)
@@ -25716,6 +25893,7 @@ function registerAppTools(server, options = {}) {
25716
25893
  "fix",
25717
25894
  "plan",
25718
25895
  "build",
25896
+ "release",
25719
25897
  "upload",
25720
25898
  "configure",
25721
25899
  "transfer-plan",
@@ -25753,11 +25931,11 @@ function registerAppTools(server, options = {}) {
25753
25931
  "dry-run": optional(Boolean2(), "[generate] CLI alias of dry_run"),
25754
25932
  register_in: optional(String2(), "[generate] Existing parent module file for module/resource; uses the compiler AST editor"),
25755
25933
  "register-in": optional(String2(), "[generate] CLI alias of register_in"),
25756
- root: optional(String2(), "[dev/plan/build/compile/check] Project directory containing supacloud.config.ts (default: current directory)"),
25934
+ root: optional(String2(), "[dev/plan/build/release/compile/check] Project directory containing supacloud.config.ts (default: current directory)"),
25757
25935
  include: optional(String2(), "[dev/compile/check] Comma-separated glob patterns for source files"),
25758
25936
  out_dir: optional(String2(), "[dev/compile/plan/build/export-tools] Output directory (default: configured outDir)"),
25759
25937
  strict: optional(Boolean2(), "[dev/compile/check] Promote warnings to errors"),
25760
- format: optional(stringEnum(["text", "json"]), "[dev/generate/compile/check/plan/deploy-plan/diff/history/promote-plan/graph/export-tools] Output format (default: text)"),
25938
+ format: optional(stringEnum(["text", "json"]), "[dev/generate/compile/check/plan/release/deploy-plan/diff/history/promote-plan/graph/export-tools] Output format (default: text)"),
25761
25939
  json: optional(Boolean2(), "[history/transfer-plan/transfer/promote-plan] Preserve or return the full machine-readable receipt"),
25762
25940
  profile: optional(stringEnum(["fast", "integration"]), "[dev] Run dev or dev:integration; integration verifies an explicit loopback database"),
25763
25941
  once: optional(Boolean2(), "[dev] Validate and report once without watching"),
@@ -25766,7 +25944,8 @@ function registerAppTools(server, options = {}) {
25766
25944
  "database-url": optional(String2(), "[dev] CLI alias of database_url"),
25767
25945
  target: optional(String2(), "[explain/context/verify-plan] Provider class name / token name / command name / job name / module name"),
25768
25946
  fix: optional(String2(), "[fix] Path to a DiagnosticFix JSON file produced by `doctor --format json`"),
25769
- write: optional(Boolean2(), "[fix] Write the fix to disk (default: preview only)")
25947
+ write: optional(Boolean2(), "[fix] Write the fix to disk (default: preview only)"),
25948
+ skip_build: optional(Boolean2(), "[release] Reuse --manifest_path instead of running the local delivery build")
25770
25949
  }, (request) => runAppTool(request, options));
25771
25950
  }
25772
25951
  var APP_ALIAS_ACTIONS = ["generate", "compile", "check", "graph", "explain", "context", "doctor", "fix"];
@@ -28523,7 +28702,7 @@ function registerRemoteDevTools(server, options = {}) {
28523
28702
  // package.json
28524
28703
  var package_default = {
28525
28704
  name: "@supacloud/cli",
28526
- version: "0.84.0",
28705
+ version: "0.85.0",
28527
28706
  description: "Project-scoped CLI for SupaCloud users",
28528
28707
  type: "module",
28529
28708
  main: "./dist/index.js",
@@ -28842,6 +29021,7 @@ EXAMPLES
28842
29021
  ${preferredCommand} app compile --root .
28843
29022
  ${preferredCommand} app plan --root . --format json
28844
29023
  ${preferredCommand} app build --root .
29024
+ ${preferredCommand} --env test app release --id orders --environment_id test --configuration_path configuration.json
28845
29025
  ${preferredCommand} app upload --ref abc123 --id orders --manifest_path generated/delivery/delivery.manifest.json
28846
29026
  ${preferredCommand} app configure --ref abc123 --id orders --environment_id test --configuration_path configuration.json
28847
29027
  ${preferredCommand} app deploy-plan --ref abc123 --id orders --environment_id test --release_id <sha256> --configuration_id <uuid>
@@ -36,7 +36,7 @@ export interface AppToolOptions {
36
36
  onDevProgress?: (result: ToolResult) => void;
37
37
  }
38
38
  export interface AppToolArguments {
39
- action: "init" | "generate" | "dev" | "watch" | "verify-plan" | "compile" | "check" | "graph" | "explain" | "export-tools" | "context" | "doctor" | "fix" | "plan" | "build" | "promote" | keyof typeof REMOTE_APP_ACTIONS;
39
+ action: "init" | "generate" | "dev" | "watch" | "verify-plan" | "compile" | "check" | "graph" | "explain" | "export-tools" | "context" | "doctor" | "fix" | "plan" | "build" | "release" | "promote" | keyof typeof REMOTE_APP_ACTIONS;
40
40
  ref?: string;
41
41
  id?: string;
42
42
  environment_id?: string;
@@ -45,6 +45,8 @@ export interface AppToolArguments {
45
45
  expected_activation_id?: string | null;
46
46
  configuration_path?: string;
47
47
  manifest_path?: string;
48
+ /** `app release`: reuse an existing configuration revision instead of writing configuration_path. */
49
+ skip_build?: boolean;
48
50
  release_id?: string;
49
51
  source_ref?: string;
50
52
  source_release_id?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.84.0",
3
+ "version": "0.85.0",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",