@xano/cli 1.3.1-beta.1 → 1.3.2

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.
Files changed (98) hide show
  1. package/README.md +1 -273
  2. package/dist/base-command.d.ts +1 -12
  3. package/dist/base-command.js +22 -36
  4. package/dist/commands/branch/set_live/index.d.ts +0 -22
  5. package/dist/commands/branch/set_live/index.js +1 -60
  6. package/dist/commands/ephemeral/push/index.js +1 -6
  7. package/dist/commands/function/create/index.d.ts +0 -15
  8. package/dist/commands/function/create/index.js +0 -40
  9. package/dist/commands/function/edit/index.d.ts +0 -15
  10. package/dist/commands/function/edit/index.js +0 -40
  11. package/dist/commands/function/run/index.d.ts +0 -1
  12. package/dist/commands/function/run/index.js +2 -10
  13. package/dist/commands/release/create/index.js +0 -7
  14. package/dist/commands/release/deploy/index.d.ts +0 -28
  15. package/dist/commands/release/deploy/index.js +6 -90
  16. package/dist/commands/release/import/index.js +1 -5
  17. package/dist/commands/release/list/index.d.ts +0 -7
  18. package/dist/commands/release/list/index.js +10 -54
  19. package/dist/commands/release/push/index.js +1 -19
  20. package/dist/commands/sandbox/push/index.js +1 -6
  21. package/dist/commands/tenant/create/index.d.ts +0 -3
  22. package/dist/commands/tenant/create/index.js +0 -21
  23. package/dist/commands/tenant/deploy_release/index.d.ts +0 -40
  24. package/dist/commands/tenant/deploy_release/index.js +4 -186
  25. package/dist/commands/tenant/edit/index.d.ts +0 -2
  26. package/dist/commands/tenant/edit/index.js +4 -39
  27. package/dist/commands/workspace/pull/index.d.ts +0 -10
  28. package/dist/commands/workspace/pull/index.js +7 -70
  29. package/dist/commands/workspace/push/index.d.ts +0 -19
  30. package/dist/commands/workspace/push/index.js +5 -124
  31. package/dist/utils/document-parser.d.ts +0 -10
  32. package/dist/utils/document-parser.js +0 -21
  33. package/dist/utils/multidoc-push.d.ts +2 -55
  34. package/dist/utils/multidoc-push.js +27 -103
  35. package/oclif.manifest.json +3570 -6579
  36. package/package.json +1 -7
  37. package/dist/commands/policy/catalogue/index.d.ts +0 -15
  38. package/dist/commands/policy/catalogue/index.js +0 -29
  39. package/dist/commands/policy/create/index.d.ts +0 -18
  40. package/dist/commands/policy/create/index.js +0 -39
  41. package/dist/commands/policy/delete/index.d.ts +0 -19
  42. package/dist/commands/policy/delete/index.js +0 -61
  43. package/dist/commands/policy/evaluate/index.d.ts +0 -32
  44. package/dist/commands/policy/evaluate/index.js +0 -71
  45. package/dist/commands/policy/list/index.d.ts +0 -14
  46. package/dist/commands/policy/list/index.js +0 -21
  47. package/dist/commands/policy/parse/index.d.ts +0 -19
  48. package/dist/commands/policy/parse/index.js +0 -18
  49. package/dist/commands/policy/publish/index.d.ts +0 -20
  50. package/dist/commands/policy/publish/index.js +0 -38
  51. package/dist/commands/policy/runs/index.d.ts +0 -54
  52. package/dist/commands/policy/runs/index.js +0 -273
  53. package/dist/commands/policy/status/index.d.ts +0 -21
  54. package/dist/commands/policy/status/index.js +0 -54
  55. package/dist/commands/skills/pull/index.d.ts +0 -30
  56. package/dist/commands/skills/pull/index.js +0 -118
  57. package/dist/commands/tenant_deploy_request/bypass/index.d.ts +0 -31
  58. package/dist/commands/tenant_deploy_request/bypass/index.js +0 -109
  59. package/dist/commands/tenant_deploy_request/create/index.d.ts +0 -21
  60. package/dist/commands/tenant_deploy_request/create/index.js +0 -108
  61. package/dist/commands/tenant_deploy_request/edit/index.d.ts +0 -22
  62. package/dist/commands/tenant_deploy_request/edit/index.js +0 -103
  63. package/dist/commands/tenant_deploy_request/get/index.d.ts +0 -25
  64. package/dist/commands/tenant_deploy_request/get/index.js +0 -116
  65. package/dist/commands/tenant_deploy_request/list/index.d.ts +0 -20
  66. package/dist/commands/tenant_deploy_request/list/index.js +0 -122
  67. package/dist/commands/tenant_deploy_request/revision/index.d.ts +0 -21
  68. package/dist/commands/tenant_deploy_request/revision/index.js +0 -83
  69. package/dist/commands/tenant_deploy_request/set_status/index.d.ts +0 -38
  70. package/dist/commands/tenant_deploy_request/set_status/index.js +0 -159
  71. package/dist/policy-command.d.ts +0 -90
  72. package/dist/policy-command.js +0 -94
  73. package/dist/utils/policy/catalogue.d.ts +0 -7
  74. package/dist/utils/policy/catalogue.js +0 -72
  75. package/dist/utils/policy/errors.d.ts +0 -12
  76. package/dist/utils/policy/errors.js +0 -61
  77. package/dist/utils/policy/feedback.d.ts +0 -68
  78. package/dist/utils/policy/feedback.js +0 -165
  79. package/dist/utils/policy/findings.d.ts +0 -23
  80. package/dist/utils/policy/findings.js +0 -65
  81. package/dist/utils/policy/gate.d.ts +0 -161
  82. package/dist/utils/policy/gate.js +0 -201
  83. package/dist/utils/policy/goal.d.ts +0 -46
  84. package/dist/utils/policy/goal.js +0 -95
  85. package/dist/utils/policy/permission.d.ts +0 -23
  86. package/dist/utils/policy/permission.js +0 -84
  87. package/dist/utils/policy/push-gate.d.ts +0 -5
  88. package/dist/utils/policy/push-gate.js +0 -29
  89. package/dist/utils/policy/release.d.ts +0 -85
  90. package/dist/utils/policy/release.js +0 -115
  91. package/dist/utils/policy/request.d.ts +0 -55
  92. package/dist/utils/policy/request.js +0 -115
  93. package/dist/utils/policy/runs.d.ts +0 -30
  94. package/dist/utils/policy/runs.js +0 -101
  95. package/dist/utils/policy/status.d.ts +0 -47
  96. package/dist/utils/policy/status.js +0 -117
  97. package/dist/utils/policy/types.d.ts +0 -185
  98. package/dist/utils/policy/types.js +0 -1
@@ -1,7 +1,5 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import BaseCommand from '../../../base-command.js';
3
- import { BLANK_POLICY_OVERRIDE, blankPolicyOverride, gateOverrideHint, gateRefusal, liveGateLines, quoted, weakeningRefusal, } from '../../../utils/policy/gate.js';
4
- import { policyWeakeningGuidance } from '../../../utils/policy/permission.js';
5
3
  export default class ReleaseDeploy extends BaseCommand {
6
4
  static args = {
7
5
  release_name: Args.string({
@@ -9,7 +7,7 @@ export default class ReleaseDeploy extends BaseCommand {
9
7
  required: true,
10
8
  }),
11
9
  };
12
- static description = "[IMPORTANT] ALWAYS confirm with the user before deploying a release. Deploys a release to its workspace as a new branch. With --set_live the branch is then set live through the set-live policy gate: when blocking findings refuse it, the branch stays, set live is refused, and the command exits 2 unless --policy-override gives a reason. A release that weakens a mandatory policy of the live branch needs workspace:policy update to be set live; without it the branch stays and the command exits 1.";
10
+ static description = '[IMPORTANT] ALWAYS confirm with the user before deploying a release. Deploys a release to its workspace as a new branch.';
13
11
  static examples = [
14
12
  `$ xano release deploy "v1.0"
15
13
  Are you sure you want to deploy release "v1.0"? (y/N) y
@@ -20,7 +18,6 @@ Deployed release "v1.0" to workspace 40 (branch: v1.0)
20
18
  `,
21
19
  `$ xano release deploy "v1.0" --branch "restore-v1" --no-set_live`,
22
20
  `$ xano release deploy "v1.0" -w 40 -o json --force`,
23
- `$ xano release deploy "v1.0" --branch "rollback-v1" --set_live --policy-override "Rollback approved; finding tracked in JIRA-12"`,
24
21
  ];
25
22
  static flags = {
26
23
  ...BaseCommand.baseFlags,
@@ -42,7 +39,6 @@ Deployed release "v1.0" to workspace 40 (branch: v1.0)
42
39
  options: ['summary', 'json'],
43
40
  required: false,
44
41
  }),
45
- 'policy-override': Flags.string({ description: 'With --set_live, set the branch live past a blocking set-live policy gate with an audited reason (requires workspace:policy update)' }),
46
42
  set_live: Flags.boolean({
47
43
  default: false,
48
44
  description: '[CRITICAL] STOP and confirm with the user before setting the deployed branch as live.',
@@ -54,20 +50,8 @@ Deployed release "v1.0" to workspace 40 (branch: v1.0)
54
50
  required: false,
55
51
  }),
56
52
  };
57
- /** Set once the platform refuses set live for a weakened mandatory policy: already explained, it exits 1 as it is. */
58
- refusedByPermission = false;
59
- /** Set once the set-live policy gate refuses, the one failure that exits 2. */
60
- refusedByPolicyGate = false;
61
- /** Exit 2 is the set-live gate's refusal; every other failure, flag errors included, exits 1. */
62
- async catch(error) {
63
- if (this.refusedByPolicyGate)
64
- return super.catch(error);
65
- return this.catchAsOperational(error);
66
- }
67
53
  async run() {
68
54
  const { args, flags } = await this.parse(ReleaseDeploy);
69
- if (blankPolicyOverride(flags['policy-override']))
70
- this.error(BLANK_POLICY_OVERRIDE);
71
55
  const { profile } = this.resolveProfile(flags);
72
56
  const workspaceId = flags.workspace || profile.workspace;
73
57
  if (!workspaceId) {
@@ -75,10 +59,11 @@ Deployed release "v1.0" to workspace 40 (branch: v1.0)
75
59
  }
76
60
  const releaseName = encodeURIComponent(args.release_name);
77
61
  const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/release/${releaseName}/deploy`;
78
- const body = deployBody(flags);
79
- if (body.override_reason && !flags.set_live) {
80
- this.error('--policy-override only applies with --set_live: a branch that is not set live is not gated.');
81
- }
62
+ const body = {
63
+ set_live: flags.set_live,
64
+ };
65
+ if (flags.branch)
66
+ body.branch = flags.branch;
82
67
  if (!flags.force) {
83
68
  const confirmed = await this.confirm(`Are you sure you want to deploy release "${args.release_name}" to workspace ${workspaceId}?`);
84
69
  if (!confirmed) {
@@ -99,7 +84,6 @@ Deployed release "v1.0" to workspace 40 (branch: v1.0)
99
84
  method: 'POST',
100
85
  }, flags.verbose, profile.access_token);
101
86
  if (!response.ok) {
102
- await this.refuseIfSetLiveRefused(response, flags, args.release_name);
103
87
  const errorText = await response.text();
104
88
  this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
105
89
  }
@@ -114,17 +98,10 @@ Deployed release "v1.0" to workspace 40 (branch: v1.0)
114
98
  this.log(`Deployed release "${release.name}" to workspace ${workspaceId} (branch: ${branchLabel}${liveStatus})`);
115
99
  if (release.description)
116
100
  this.log(` Description: ${release.description}`);
117
- this.logPolicyOutcome(release);
118
101
  this.log(` Time: ${elapsed}s`);
119
102
  }
120
- // A policy of the release the new branch could not take is never dropped silently.
121
- const skipped = release.policies_skipped?.message;
122
- if (skipped)
123
- this.warn(skipped);
124
103
  }
125
104
  catch (error) {
126
- if (this.refusedByPolicyGate || this.refusedByPermission)
127
- throw error;
128
105
  if (error instanceof Error) {
129
106
  this.error(`Failed to deploy release: ${error.message}`);
130
107
  }
@@ -146,65 +123,4 @@ Deployed release "v1.0" to workspace 40 (branch: v1.0)
146
123
  });
147
124
  });
148
125
  }
149
- /** The set-live verdict a successful deploy answered with --set_live (`pass`, `overridden`, ...). */
150
- logPolicyOutcome(release) {
151
- const status = release.policy_gate?.status;
152
- if (status)
153
- this.log(` Policy gate: ${status}`);
154
- }
155
- /** Stops the command when set live was refused after the branch was created: by the gate (exit 2) or for a weakened mandatory policy (exit 1). */
156
- async refuseIfSetLiveRefused(response, flags, releaseName) {
157
- const refused = await gateRefusal(response);
158
- if (refused)
159
- this.refuseSetLive(refused, flags, releaseName);
160
- const weakening = await weakeningRefusal(response);
161
- if (weakening)
162
- this.refuseWeakening(weakening, flags);
163
- }
164
- /**
165
- * The set-live gate refused after the branch was created: the verdict, the refusal, and how to
166
- * proceed, exiting 2. Under `-o json` the refusal is `{branch_created, set_live, branch, message,
167
- * policy_gate}`.
168
- */
169
- refuseSetLive(refused, flags, releaseName) {
170
- const answer = refused.answer;
171
- const label = answer.branch?.label || flags.branch || 'the release branch';
172
- if (flags.output === 'json') {
173
- this.log(JSON.stringify({ branch: answer.branch ?? null, branch_created: true, message: refused.message, policy_gate: answer, set_live: false }, null, 2));
174
- }
175
- else {
176
- for (const line of liveGateLines(answer, 'release'))
177
- this.log(line);
178
- }
179
- const workspace = flags.workspace ? ` -w ${quoted(flags.workspace)}` : '';
180
- const rerun = `xano branch delete ${quoted(label)}${workspace} && xano release deploy ${quoted(releaseName)}${workspace}${flags.branch ? ` --branch ${quoted(flags.branch)}` : ''} --set_live --policy-override "<why>"`;
181
- const hint = gateOverrideHint(answer, rerun);
182
- this.refusedByPolicyGate = true;
183
- this.error(`The branch was created; set live was refused. ${refused.message}${hint}${answer.can_override && !answer.override_denied ? `\nOr set "${label}" live from Studio's Branches panel, which asks for the reason.` : ''}`, { exit: 2 });
184
- }
185
- /**
186
- * Set live was refused after the branch was created because the release weakens (or lacks) a
187
- * mandatory policy of the live branch, and the credential lacks `workspace:policy` update. That is a
188
- * permission refusal, not a blocking finding: it exits 1 (under `-o json` as `{error}`) with the
189
- * platform's sentence, which names the branch, and how to go on.
190
- */
191
- refuseWeakening(refused, flags) {
192
- const label = refused.payload.branch?.label || flags.branch;
193
- const workspace = flags.workspace ? ` -w ${quoted(flags.workspace)}` : '';
194
- const ways = label
195
- ? [`The branch stays. To remove it: xano branch delete ${quoted(label)}${workspace}`, `Someone with that permission can set it live: xano branch set_live ${quoted(label)}${workspace}`]
196
- : [];
197
- this.refusedByPermission = true;
198
- this.error(`${refused.message}${policyWeakeningGuidance(refused.payload, ways, true)}`);
199
- }
200
- }
201
- /** The deploy request's body: the new branch's label, set_live, and the override reason when one is given. */
202
- function deployBody(flags) {
203
- const body = { set_live: flags.set_live };
204
- if (flags.branch)
205
- body.branch = flags.branch;
206
- const overrideReason = flags['policy-override']?.trim();
207
- if (overrideReason)
208
- body.override_reason = overrideReason;
209
- return body;
210
126
  }
@@ -2,7 +2,6 @@ import { Flags } from '@oclif/core';
2
2
  import * as fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import BaseCommand from '../../../base-command.js';
5
- import { releaseImportRefusal, releasePolicyRunLines } from '../../../utils/policy/release.js';
6
5
  export default class ReleaseImport extends BaseCommand {
7
6
  static args = {};
8
7
  static description = '[IMPORTANT] ALWAYS confirm with the user before importing a release. Imports a release file into a workspace.';
@@ -59,8 +58,7 @@ Imported release as #15
59
58
  }, flags.verbose, profile.access_token);
60
59
  if (!response.ok) {
61
60
  const errorText = await response.text();
62
- this.error(releaseImportRefusal(errorText, response.status, apiUrl) ??
63
- `API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
61
+ this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
64
62
  }
65
63
  const result = await response.json();
66
64
  if (flags.output === 'json') {
@@ -69,8 +67,6 @@ Imported release as #15
69
67
  else {
70
68
  const sizeMb = (fileBuffer.length / 1024 / 1024).toFixed(2);
71
69
  this.log(`Imported release as #${result.id} (${sizeMb} MB)`);
72
- for (const line of releasePolicyRunLines(result.policy_run, result.name, result.id))
73
- this.log(line);
74
70
  }
75
71
  }
76
72
  catch (error) {
@@ -10,11 +10,4 @@ export default class ReleaseList extends BaseCommand {
10
10
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
11
  };
12
12
  run(): Promise<void>;
13
- /**
14
- * The releases' stored policy checks, read once for the whole list. The list never fails or
15
- * changes because of this read: it answers `null` when the checks cannot be read (the Policies
16
- * feature is off, the credential lacks `workspace:policy` read, or the request failed), and the
17
- * list then prints as it would without policies.
18
- */
19
- private policyChecks;
20
13
  }
@@ -1,21 +1,13 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import BaseCommand from '../../../base-command.js';
3
3
  import { buildPagingJson } from '../../../utils/paging.js';
4
- import { readReleasePolicyChecks, releasePolicyCheckTag } from '../../../utils/policy/release.js';
5
4
  export default class ReleaseList extends BaseCommand {
6
- static description = "List all releases in a workspace, each with its stored policy check when the credential can read policies (the release's own check, as Studio's Policies column shows it)";
5
+ static description = 'List all releases in a workspace';
7
6
  static examples = [
8
7
  `$ xano release list
9
8
  Releases in workspace 5:
10
9
  - v1.0 (ID: 10) - main
11
10
  - v1.1-hotfix (ID: 11) - main [hotfix]
12
- `,
13
- `$ xano release list
14
- Releases in workspace 5:
15
- - v1.2 (ID: 12) - main (9/28/2026, 4:12:03 PM PDT) [policies: 2 blocking, 1 advisory]
16
- - v1.1-hotfix (ID: 11) - main [hotfix] (9/27/2026, 9:40:15 AM PDT) [policies: passed]
17
- - v1.0 (ID: 10) - main (9/20/2026, 1:05:44 PM PDT) [policies: not checked]
18
- Policy check details: xano policy runs --release <name>
19
11
  `,
20
12
  `$ xano release list -w 5 --output json`,
21
13
  ];
@@ -65,19 +57,22 @@ Policy check details: xano policy runs --release <name>
65
57
  else {
66
58
  this.error('Unexpected API response format');
67
59
  }
68
- const checks = await this.policyChecks(profile, String(workspaceId), releases, flags.verbose);
69
60
  if (flags.output === 'json') {
70
- this.log(JSON.stringify(buildPagingJson({ items: withPolicyChecks(releases, checks) }, { tier: 'none' }), null, 2));
61
+ this.log(JSON.stringify(buildPagingJson({ items: releases }, { tier: 'none' }), null, 2));
71
62
  }
72
63
  else if (releases.length === 0) {
73
64
  this.log('No releases found');
74
65
  }
75
66
  else {
76
67
  this.log(`Releases in workspace ${workspaceId}:`);
77
- for (const release of releases)
78
- this.log(releaseLine(release, checks));
79
- if (checks)
80
- this.log('Policy check details: xano policy runs --release <name>');
68
+ for (const release of releases) {
69
+ const branch = release.branch ? ` - ${release.branch}` : '';
70
+ const hotfix = release.hotfix ? ' [hotfix]' : '';
71
+ const createdAt = release.created_at
72
+ ? ` (${new Date(release.created_at).toLocaleString(undefined, { timeZoneName: 'short' })})`
73
+ : '';
74
+ this.log(` - ${release.name} (ID: ${release.id})${branch}${hotfix}${createdAt}`);
75
+ }
81
76
  }
82
77
  }
83
78
  catch (error) {
@@ -89,43 +84,4 @@ Policy check details: xano policy runs --release <name>
89
84
  }
90
85
  }
91
86
  }
92
- /**
93
- * The releases' stored policy checks, read once for the whole list. The list never fails or
94
- * changes because of this read: it answers `null` when the checks cannot be read (the Policies
95
- * feature is off, the credential lacks `workspace:policy` read, or the request failed), and the
96
- * list then prints as it would without policies.
97
- */
98
- async policyChecks(profile, workspace, releases, verbose) {
99
- if (releases.length === 0)
100
- return null;
101
- return readReleasePolicyChecks({ logToStderr: (message) => this.logToStderr(message), verboseFetch: (...args) => this.verboseFetch(...args) }, { branch: '', profile, verbose, workspace }, releases.map(release => release.id));
102
- }
103
- }
104
- /** A release's summary line, tagged with its stored check when the checks were read. */
105
- function releaseLine(release, checks) {
106
- const branch = release.branch ? ` - ${release.branch}` : '';
107
- const hotfix = release.hotfix ? ' [hotfix]' : '';
108
- const createdAt = release.created_at
109
- ? ` (${new Date(release.created_at).toLocaleString(undefined, { timeZoneName: 'short' })})`
110
- : '';
111
- const policies = checks ? ` ${releasePolicyCheckTag(checks.get(release.id))}` : '';
112
- return ` - ${release.name} (ID: ${release.id})${branch}${hotfix}${createdAt}${policies}`;
113
- }
114
- /**
115
- * The releases for `-o json`: each gains `policy_check` (its stored check as the platform lists
116
- * it, without the release id it is keyed by, or `null` for a release with no stored check) when
117
- * the checks were read, and stays as served when they could not be, so a reader can tell an
118
- * unchecked release (`null`) from checks the CLI could not read (absent).
119
- */
120
- function withPolicyChecks(releases, checks) {
121
- if (!checks)
122
- return releases;
123
- return releases.map(release => {
124
- const check = checks.get(release.id);
125
- if (!check)
126
- return { ...release, policy_check: null };
127
- const stored = { ...check };
128
- delete stored.release_id;
129
- return { ...release, policy_check: stored };
130
- });
131
87
  }
@@ -2,9 +2,7 @@ import { Flags } from '@oclif/core';
2
2
  import * as fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import BaseCommand from '../../../base-command.js';
5
- import { findFilesWithGuid, parseDocument } from '../../../utils/document-parser.js';
6
- import { policiesSkippedNotice } from '../../../utils/policy/feedback.js';
7
- import { releasePolicyRunLines } from '../../../utils/policy/release.js';
5
+ import { findFilesWithGuid } from '../../../utils/document-parser.js';
8
6
  export default class ReleasePush extends BaseCommand {
9
7
  static description = '[IMPORTANT] ALWAYS confirm with the user before creating a release from local files. Creates a new release from local XanoScript files via the multidoc endpoint.';
10
8
  static examples = [
@@ -112,12 +110,6 @@ Output release details as JSON
112
110
  if (documentEntries.length === 0) {
113
111
  this.error(`All .xs files in ${flags.directory} are empty`);
114
112
  }
115
- // A release carries its policy files like its code.
116
- const policyKeys = documentEntries
117
- .map((entry) => parseDocument(entry.content))
118
- .filter((document) => document?.type === 'policy')
119
- .map((document) => document.name)
120
- .sort();
121
113
  const multidoc = documentEntries.map((d) => d.content).join('\n---\n');
122
114
  // Construct the API URL with query params
123
115
  const queryParams = new URLSearchParams({
@@ -166,10 +158,6 @@ Output release details as JSON
166
158
  this.error(errorMessage);
167
159
  }
168
160
  const release = (await response.json());
169
- // The platform says which policy files it left out: all of them while the Policies feature is
170
- // off, and those of a caller who may not author policies, whose release carries the live
171
- // branch's policies instead.
172
- const skipped = policiesSkippedNotice(release);
173
161
  if (flags.output === 'json') {
174
162
  this.log(JSON.stringify(release, null, 2));
175
163
  }
@@ -183,14 +171,8 @@ Output release details as JSON
183
171
  this.log(` Description: ${release.description}`);
184
172
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
185
173
  this.log(` Documents: ${documentEntries.length}`);
186
- if (policyKeys.length > 0 && !skipped)
187
- this.log(` Policy documents: ${policyKeys.length} (${policyKeys.join(', ')})`);
188
- for (const line of releasePolicyRunLines(release.policy_run, release.name ?? flags.name))
189
- this.log(line);
190
174
  this.log(` Time: ${elapsed}s`);
191
175
  }
192
- if (skipped)
193
- this.warn(skipped);
194
176
  }
195
177
  catch (error) {
196
178
  if (error instanceof Error) {
@@ -4,7 +4,6 @@ import { resolve } from 'node:path';
4
4
  import open from 'open';
5
5
  import BaseCommand from '../../../base-command.js';
6
6
  import { executePush } from '../../../utils/multidoc-push.js';
7
- import { policiesSkippedNotice } from '../../../utils/policy/feedback.js';
8
7
  export default class SandboxPush extends BaseCommand {
9
8
  static description = '[IMPORTANT] ALWAYS run --dry-run first and show the user the output before pushing. Push local documents to your sandbox environment via multidoc import. By default, only changed files are pushed (partial mode). Use --sync to push all files. Shows a preview of changes before pushing unless --force is specified. Use --dry-run to preview only. Include/exclude glob filters are intentionally not supported on sandbox push — partial pushes can hide deletions during review and lead to data loss when promoted to the workspace. Pushing into a sandbox that currently holds a different workspace will prompt for confirmation; run `xano sandbox reset` first to start clean.';
10
9
  static examples = [
@@ -127,7 +126,7 @@ Push and open sandbox review in the browser
127
126
  truncate: flags.truncate,
128
127
  verbose: flags.verbose,
129
128
  };
130
- const result = await executePush({
129
+ await executePush({
131
130
  accessToken: profile.access_token,
132
131
  branch: '',
133
132
  command: this,
@@ -138,10 +137,6 @@ Push and open sandbox review in the browser
138
137
  },
139
138
  verboseFetch: this.verboseFetch.bind(this),
140
139
  }, target, pushFlags);
141
- // When the platform leaves policy files out, it says so once.
142
- const skipped = policiesSkippedNotice(result.response ?? result.preview);
143
- if (skipped)
144
- this.warn(skipped);
145
140
  if (flags.review) {
146
141
  await this.openReview(profile.instance_origin, profile.access_token, flags.verbose);
147
142
  }
@@ -6,8 +6,6 @@ export default class TenantCreate extends BaseCommand {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
- allow_deploy_bypass: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
- allow_quick_deploy: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
9
  cluster_id: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
10
  description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
11
  domain: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -15,7 +13,6 @@ export default class TenantCreate extends BaseCommand {
15
13
  license: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
14
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
17
15
  platform_id: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
- required_reviewers: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
19
16
  tasks: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
17
  type: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
21
18
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -17,20 +17,9 @@ Created tenant: Production (production) - ID: 42
17
17
  `,
18
18
  `$ xano tenant create "Staging" --description "Staging env" --cluster_id 1 --platform_id 1 --type tier2 -o json`,
19
19
  `$ xano tenant create "Staging" --type tier2 --cluster_id 1 --license ./license.yaml`,
20
- `$ xano tenant create "Production" --required_reviewers 1 --allow_deploy_bypass`,
21
20
  ];
22
21
  static flags = {
23
22
  ...BaseCommand.baseFlags,
24
- allow_deploy_bypass: Flags.boolean({
25
- default: false,
26
- description: 'Allow authorized users to bypass the deploy approval gate on this tenant (via tenant_deploy_request bypass). Has no effect unless --required_reviewers is also set above 0.',
27
- required: false,
28
- }),
29
- allow_quick_deploy: Flags.boolean({
30
- default: false,
31
- description: 'Allow this tenant to skip the deploy approval gate entirely for quick deploys, regardless of --required_reviewers.',
32
- required: false,
33
- }),
34
23
  cluster_id: Flags.integer({
35
24
  description: 'Cluster ID to deploy to (required for tier2/tier3)',
36
25
  required: false,
@@ -65,11 +54,6 @@ Created tenant: Production (production) - ID: 42
65
54
  description: 'Platform ID to use',
66
55
  required: false,
67
56
  }),
68
- required_reviewers: Flags.integer({
69
- default: 0,
70
- description: 'When greater than 0, that many distinct reviewers must approve a tenant_deploy_request before a release may be deployed to this tenant. 0 (default) means not gated.',
71
- required: false,
72
- }),
73
57
  tasks: Flags.boolean({
74
58
  allowNo: true,
75
59
  default: true,
@@ -95,11 +79,6 @@ Created tenant: Production (production) - ID: 42
95
79
  this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
96
80
  }
97
81
  const body = {
98
- deploy_settings: {
99
- allow_deploy_bypass: flags.allow_deploy_bypass,
100
- allow_quick_deploy: flags.allow_quick_deploy,
101
- required_reviewers: flags.required_reviewers,
102
- },
103
82
  display: args.display,
104
83
  ingress: flags.ingress,
105
84
  license: flags.type,
@@ -6,52 +6,12 @@ export default class TenantDeployRelease extends BaseCommand {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
- check: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
9
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
- 'override-reason': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
10
  release: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
11
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
12
  config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
13
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
14
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
15
  };
18
- /** Set once the policy gate refuses the deploy, the one failure that exits 2. */
19
- private refusedByPolicyGate;
20
- /** Exit 2 is the policy gate's refusal; every other failure, flag errors included, exits 1. */
21
- protected catch(error: Error & {
22
- oclif?: {
23
- exit?: number;
24
- };
25
- }): Promise<void>;
26
16
  run(): Promise<void>;
27
- /**
28
- * `--check`: what the policy gate would decide for this release on this tenant, without deploying.
29
- * Exits 2 when the deploy would be refused; a preview the platform cannot answer exits 1.
30
- */
31
- private checkPolicyGate;
32
- /**
33
- * Does an approved tenant_deploy_request already exist for this (tenant, release)
34
- * pair? The list endpoint has no release filter, so this fetches approved
35
- * requests for the tenant and filters client-side by the resolved release name.
36
- */
37
- private hasApprovedDeployRequest;
38
- /** A deploy the policy gate refused: its verdict, then the refusal and how to proceed, exiting 2. */
39
- private refuseByPolicyGate;
40
- /**
41
- * Does this tenant have the deploy approval gate enabled?
42
- *
43
- * Returns false if the tenant has `allow_quick_deploy` set — that flag lets a
44
- * tenant skip the approval gate entirely for quick deploys, regardless of
45
- * `required_reviewers`.
46
- *
47
- * Deliberately uses `tenant list` rather than `tenant get`: the single-tenant
48
- * GET route's output whitelist (tenant-detail.yaml) omits `deploy_settings`
49
- * (which holds `required_reviewers`, `allow_deploy_bypass`, and
50
- * `allow_quick_deploy`) even though the list route's output includes it
51
- * — confirmed live against a running instance. Filed as a backend bug; this
52
- * is the workaround until it's fixed. See TENANT_DEPLOY_REQUEST_TEST.md.
53
- */
54
- private tenantRequiresApproval;
55
- /** A release policy the tenant's import could not land (its key is held by another policy) is never dropped silently. */
56
- private warnPoliciesSkipped;
57
17
  }