@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
package/README.md CHANGED
@@ -170,104 +170,6 @@ xano profile delete myprofile
170
170
  xano profile delete myprofile --force
171
171
  ```
172
172
 
173
- ### Policies
174
-
175
- Policies combine a description with deterministic check rules; the platform parses, validates and evaluates
176
- them. `xano policy <command> --help` gives each command's flags and output, and the Xano developer MCP documents
177
- the rest: the `policies` topic of `xano_xanoscript_docs` (the language) and the `policy` topic of `xano_cli_docs`.
178
-
179
- ```bash
180
- xano policy catalogue --check object.auth_required # The checks a rule can use (all of them without --check)
181
- xano policy create --goal endpoints_need_login -b dev # Create an independent policy from a platform goal
182
- xano policy create --goal no_pii_in_responses --key DATA-020 --param 'fields=["email","ssn"]'
183
- xano policy list # Policies on the branch
184
- xano policy parse policies/AUTH-001.xs # Validate and print canonical XanoScript (or --file, --stdin)
185
- xano policy publish policies/AUTH-001.xs -m "Why" # Create or update by key; -m labels the Version History entry
186
- xano policy evaluate # Evaluate the branch now
187
- xano policy evaluate --summary # The same, answering the run summary and its first 50 findings
188
- xano policy status --fail-on-findings # The latest run, without evaluating (CI)
189
- xano policy runs 1674 --run-detail # One run and its first 100 findings; without an id, the retained runs
190
- xano policy runs 1674 --offset 100 --limit 100 # The next page of that run's findings (at most 500 a page)
191
- xano policy runs 1674 --all -o json # Every finding, read page by page
192
- xano policy runs 1674 --blocking --policy AUTH-001 # Only some findings: --blocking/--advisory, --policy, --rule,
193
- # --severity, --kind, --object type:id, --tag (repeatable), --search
194
- xano policy runs --release v1.2 # A release's check (stored when it was cut), paged and filtered the same way
195
- xano policy runs --release v1.2 --recheck # Check the release again from its archive, store the run, then read it
196
- xano policy runs --release-id 12 # Read an imported release by ID without a name lookup
197
- xano policy delete TMP-001 # Remove a policy; its Version History is kept
198
- ```
199
-
200
- `policy create --goal <id>` reads the instance's goals from `policy catalogue -o json`, copies a goal's
201
- sparse settings, validates them natively and creates an Active, Advisory policy. Required goal settings must
202
- be filled before it saves. Repeat `--param 'N.path=JSON'` for overrides: rule numbers start at 1; the `N.`
203
- prefix is optional for a single-rule goal. Nested paths work, for example `--param '1.when.statement="db.edit"'`.
204
- Values are JSON, including quoted strings, arrays, booleans and numbers. The goal key gets a free numeric
205
- suffix if already taken (case-insensitive); an explicitly taken `--key` is refused. It never updates an
206
- existing policy. `-m/--message` labels its Version History entry; `-o json` returns the saved policy and
207
- native rule warnings go to stderr. No continuing link to the goal is stored.
208
-
209
- A push to the live branch is refused before commit when mandatory policies block it. The gate judges the branch by the policies it will have after the push, so the pushed policy files count, and a push that weakens a mandatory policy of the live branch (demotes, deactivates or deletes it, or loosens one of its rules) needs `workspace:policy` update and is otherwise refused before anything is imported. A 403 `policy_gate` exits **2**, says nothing was imported, and lists the blocking findings and introduced/changed counts. With `-o json` it prints `{imported: false, refused: <payload>}`. `--policy-override "reason"` sends an audited override, requiring `workspace:policy` update permission. This gate requires transactions: remove `--no-transaction` if the server refuses it (exit 1).
210
-
211
- Every policy command takes `-w/--workspace`, `-b/--branch` (the profile's branch by default; `-b ''` is live)
212
- and `-o/--output summary|json`. `workspace pull` and `workspace push` carry policies as `policies/<KEY>.xs`.
213
- Policies use `active = true|false` before `enforcement`; authored `lifecycle` is refused. The list shows
214
- `Active, Mandatory`, `Active, Advisory` or `Inactive`. Inactive policies are saved directly and skipped by
215
- branch checks; `policy status` reports `inactive`. An inactive policy may have no rules.
216
- `release push` carries them too, for a credential holding `workspace:policy` create and update: a release ships its
217
- policies like its code, and a tenant deploy lands them (see Releases). Tier1 tenant, sandbox and ephemeral pushes
218
- carry them as well; a remote tenant push, or a tenant created before policies, leaves them out and prints the
219
- platform's notice.
220
- While the instance's Policies feature is off, `workspace pull` exports no policy (and keeps local policy files)
221
- and `workspace push` leaves its policy files out, printing the platform's notice instead of a policy count.
222
-
223
- A run can hold tens of thousands of findings, so the CLI reads them a page at a time. `policy runs <id>` reads
224
- the run's summary and one page of its findings; a page that is not every finding says `(one page)` and how to read
225
- the next. `-o json` is the summary with `findings`, the page's findings as an array, and
226
- `findings_page: {total, offset, limit, next_offset}`. `--all` reads every page (500 a request, saying so on stderr
227
- when there are more) and returns every matching finding in `findings`. `policy status` reads the latest run's
228
- summary, so its `-o json` `run` carries each policy's finding count rather than the findings. The `policy_check` a
229
- `workspace push` prints lists the first 100 findings; when there are more it prints their counts and
230
- `Listed: the first 100 of N; \`xano policy runs <run_id>\` has them all.` `policy evaluate --summary` does the same
231
- with the first 50, and `policy evaluate` without it still answers the whole run.
232
-
233
- `policy evaluate --policy AUTH-001` tries that one policy alone, even if inactive. It never stores or audits
234
- the trial, replaces the latest run or gates anything. The output names each rule's clear objects (up to five
235
- names, with the rest counted). Combine it with `--summary`, `--run-detail` or `-o json`; an inactive trial
236
- does not exit 2. A blank key is refused before any request, and an unknown key gets the platform's 404.
237
-
238
- `policy runs --release <name>` reads a release's check: the run stored when the release was cut, evaluated on what
239
- the release ships against the policies it ships. It takes every finding flag a run read takes, `-o json` has the same
240
- shape (plus the release's `shipped` policies), and a release with no check says so (`{"run": null}` under `-o json`).
241
- `--recheck` checks the release again from its archive and stores the new run, so a read-only credential is refused.
242
- `--release-id <id>` reads the same report directly by ID and also supports `--recheck`; it is exclusive with
243
- `--release` and `--branch`.
244
-
245
- **Exit codes** of `policy evaluate`, `workspace push` and `policy status --fail-on-findings`: `2` when a finding
246
- blocks (an active, mandatory policy failed), whatever else happened; `1` when a request or the import failed, or
247
- when `status --fail-on-findings` finds stale, missing or errored evidence; otherwise `0`. `tenant deploy_release`,
248
- `tenant_deploy_request set_status` and `tenant_deploy_request bypass` exit `2` when the tenant deploy policy gate
249
- refuses the deploy (and `tenant deploy_release --check` when it would), `1` for any other failure, otherwise `0`.
250
- `release deploy --set_live` and `branch set_live` exit `2` when the set-live policy gate refuses to set the branch live, and `1` for any other failure; `branch set_live --policy-override "<reason>"` sets it live past the gate with an audited reason, as `release deploy` does. `function create` and `function edit` exit `2` when the live-branch publish gate refuses the save (see Functions), and `1` for any other failure. A blank `--policy-override` is refused before any request. A `workspace push`, `branch set_live`, `release deploy --set_live` or `policy delete` refused with `policy_weakening_permission_required` exits `1`, like the other permission refusals: it is a missing permission, not a blocking finding.
251
-
252
- Each route needs the `workspace:policy` permission at the request's level. A refusal's `payload.code` names the
253
- gate and the CLI prints its remedy: `policy_feature_disabled` (Policies are off on the instance),
254
- `policy_permission_required` (an instance admin grants your role the level) or `policy_scope_required` (create a
255
- Metadata API token that has the scope). A push to the live branch, a set live or a merge is judged by the policies it
256
- leaves, so one that weakens a policy active and mandatory on the branch (demotes, deactivates or deletes it, or
257
- loosens one of its rules; for a set live, a live mandatory policy the branch lacks counts as deleted) needs
258
- `workspace:policy` update, and without it is refused before anything is written with
259
- `policy_weakening_permission_required`, naming the policies. `--policy-override` does not lift it: the CLI prints the
260
- platform's sentence and how to go on (a push can leave the policy files out with `-e "policies/*"`), and a push's
261
- `--dry-run` is refused the same way. `policy delete` of an active, mandatory policy needs update too, not only delete,
262
- and is refused with the same code otherwise. A set live whose check could not read the policies fails closed: only
263
- update proceeds, and the refusal says so (`unavailable`). A rule naming a check the instance does not have (`policy_unknown_check`)
264
- points at `xano policy catalogue`. `policy delete` sends the `updated_at` it listed, so a policy changed since is
265
- refused (`policy_stale`) rather than deleted; it asks first, and without `--force` in a non-interactive shell it
266
- exits 1. A create past the plan's per-branch policy cap (lower plans cap the policies on each branch; Pro and above are unlimited) is refused with
267
- `policy_plan_limit`, naming the plan, and a `workspace push` whose new policies would not fit imports nothing.
268
- `policy parse` and `policy publish` print rule warnings (a part of a rule that would do nothing or allow too much on
269
- that branch) on stderr; `-o json` keeps stdout the JSON.
270
-
271
173
  ### Workspaces
272
174
 
273
175
  ```bash
@@ -295,7 +197,6 @@ xano workspace delete -w <workspace_id> --force
295
197
  xano workspace pull
296
198
  xano workspace pull -d ./my-workspace # Specify output directory
297
199
  xano workspace pull -b dev # Specific branch
298
- xano workspace pull -b '' # The live branch, whatever the profile's branch
299
200
  xano workspace pull --env --records # Include env vars and table records
300
201
  xano workspace pull --draft # Include draft changes
301
202
 
@@ -303,7 +204,6 @@ xano workspace pull --draft # Include draft changes
303
204
  xano workspace push
304
205
  xano workspace push -d ./my-workspace # Push from a specific directory
305
206
  xano workspace push -b dev
306
- xano workspace push -b '' # The live branch, whatever the profile's branch
307
207
  xano workspace push --sync # Full push — send all files, not just changed ones
308
208
  xano workspace push --sync --delete # Full push + delete remote objects not included
309
209
  xano workspace push --dry-run # Preview changes without pushing
@@ -316,9 +216,6 @@ xano workspace push --force # Skip preview and conf
316
216
  xano workspace push -i "function/*" # Push only matching files
317
217
  xano workspace push -e "table/*" # Push all files except tables
318
218
  xano workspace push -i "function/*" -e "**/test*" # Include functions, exclude tests
319
- xano workspace push -o json # One JSON document: the preview (with --dry-run) or the import result with policy_check
320
- xano workspace push -m "Tightened the auth policies" # Label the Version History entry of each policy this push changes; other objects get no message
321
- xano workspace push --policy-override "Approved exception" # Proceed past a blocking live-branch gate with an audited reason
322
219
 
323
220
  # Pull from a git repository to local files (defaults to current directory)
324
221
  xano workspace git pull -r https://github.com/owner/repo
@@ -433,7 +330,6 @@ xano branch edit <branch_label> --color "#ff0000"
433
330
  # Set live branch
434
331
  xano branch set_live <branch_label>
435
332
  xano branch set_live <branch_label> --force
436
- xano branch set_live <branch_label> --policy-override "Rollback approved" # Past a blocking set-live policy gate (audited)
437
333
 
438
334
  # Delete a branch
439
335
  xano branch delete <branch_label>
@@ -458,7 +354,6 @@ xano function get <function_id> --include_draft # Include draft version
458
354
  xano function create -f function.xs
459
355
  xano function create -f function.xs --edit # Open in $EDITOR before creating
460
356
  cat function.xs | xano function create --stdin
461
- xano function create -f function.xs --policy-override "Exception approved" # Past a blocking publish policy gate (audited)
462
357
 
463
358
  # Edit a function
464
359
  xano function edit <function_id> # Opens in $EDITOR
@@ -466,7 +361,6 @@ xano function edit <function_id> -f new.xs # Update from file
466
361
  xano function edit <function_id> -f new.xs --edit # Open in $EDITOR before updating
467
362
  cat function.xs | xano function edit <function_id> --stdin # Update from stdin
468
363
  xano function edit <function_id> --no-publish # Edit without publishing
469
- xano function edit <function_id> -f new.xs --policy-override "Exception approved" # Past a blocking publish policy gate (audited)
470
364
 
471
365
  # Run (execute) a function by name
472
366
  xano function run <name> # Prompts for declared inputs (on a TTY)
@@ -475,14 +369,8 @@ xano function run <name> --json @payload.json --data env=staging # base payloa
475
369
  echo '{"email":"jo@x.com"}' | xano function run <name> --stdin -o json | jq .result
476
370
  xano function run <name> --branch dev --logs # run on a branch, show execution logs
477
371
  xano function run <name> --datasource test # run against the 'test' data source
478
- xano function run <name> --tenant my-tenant # run on a tenant instead of the workspace
479
372
  ```
480
373
 
481
- `function create` and `function edit` save a function on the live branch through the publish policy gate. When
482
- blocking findings refuse the save, nothing is saved: the command prints the verdict and the platform's message and
483
- exits `2` (any other failure exits `1`), and `-o json` prints `{created|updated: false, message, policy_gate}`.
484
- `--policy-override "<reason>"` saves past the gate with an audited reason (requires `workspace:policy` update).
485
-
486
374
  Input flexibility for `function run` (assembled into one JSON `input` object):
487
375
 
488
376
  | Form | Meaning |
@@ -498,9 +386,6 @@ Input flexibility for `function run` (assembled into one JSON `input` object):
498
386
  to run against `live`; an unknown label is rejected by the server with
499
387
  `Invalid data source.`
500
388
 
501
- `-t/--tenant <name>` runs the function on that tenant
502
- (`/workspace/{id}/tenant/{name}/function/run`) instead of the workspace.
503
-
504
389
  Merge order is JSON base first, then `--data` overrides. Missing required inputs are
505
390
  prompted for on an interactive terminal; in a non-TTY (CI) context the command fails
506
391
  listing them. Output defaults to the raw `result` (`-o json`); the exit code is non-zero
@@ -536,27 +421,12 @@ xano knowledge get "deploy-runbook" -w 40 --output json
536
421
  xano knowledge get "deploy-runbook" -w 40 --file checklist.md
537
422
  ```
538
423
 
539
- ### Agent skills
540
-
541
- ```bash
542
- xano skills pull # Writes ./.claude/skills/xano-policies/SKILL.md
543
- xano skills pull -d ./my-project # Install into another project directory
544
- xano skills pull -b dev -o json # Another branch; print the result as JSON
545
- ```
546
-
547
- `xano skills pull` writes the `xano-policies` skill the instance generates from its check catalogue to
548
- `<directory>/.claude/skills/xano-policies/SKILL.md`, replacing that file without a backup; pull it again after an
549
- instance upgrade. A copy installed anywhere else is not touched, so remove it. The route needs the
550
- `workspace:policy` read permission. The CLI writes the file's frontmatter (`name` and `description`) itself and drops
551
- any frontmatter the served content opens with, so a workspace knowledge record that replaces the platform skill cannot
552
- set agent settings such as `allowed-tools` or `hooks`. It refuses to write through a symbolic link at that path.
553
-
554
424
  ### Releases
555
425
 
556
426
  All release commands use **release names** (e.g., `v1.0`), not IDs.
557
427
 
558
428
  ```bash
559
- # List releases, each tagged with its stored policy check when Policies is enabled and readable (see Policies)
429
+ # List releases
560
430
  xano release list
561
431
 
562
432
  # Get release details
@@ -597,54 +467,8 @@ xano release deploy "v1.0"
597
467
  xano release deploy "v1.0" --force
598
468
  xano release deploy "v1.0" --branch "restore-v1" --no-set_live
599
469
  xano release deploy "v1.0" -w 40 -o json --force
600
-
601
- # Set it live past a blocking set-live policy gate with an audited reason (needs workspace:policy update)
602
- xano release deploy "v1.0" --branch "rollback-v1" --set_live --policy-override "Rollback approved; tracked in JIRA-12"
603
-
604
- # Read a release's policy check, or check it again (see Policies)
605
- xano policy runs --release v1.0
606
- xano policy runs --release v1.0 --recheck
607
470
  ```
608
471
 
609
- A release carries its branch's policies like its code (`release push` sends `policies/*.xs` with the rest), and a
610
- tenant deploy lands them on the tenant. A release built by `release push` carries those files only for a credential
611
- holding `workspace:policy` create and update; for anyone else it carries the workspace's live-branch policies instead,
612
- and when the files differ from them the push prints the platform's notice naming them (exit `0`). `release import` of
613
- an archive that carries policies needs the same levels: without them nothing is stored, and the command fails with
614
- the platform's message and the remedy for your role or token.
615
-
616
- Cutting a release (`release create`, `release push`, `release import`) stores its policy check, and the summary
617
- prints it: `Policy check: fail (run 1712), 2 blocking, 1 advisory findings`, or
618
- `Policy check disabled|skipped|error: <why>` when none was recorded. `-o json` keeps it as `policy_run`
619
- (`{status, run_id, run_status, blocking, advisory, message?}`, or `null` for a credential without `workspace:policy`
620
- read). A cut is never refused for its findings: its blocking findings are what gates each tenant deploy (see Tenant
621
- deployments).
622
-
623
- `release list` tags each release with its stored check, worded as Studio's Policies column: `[policies: 2 blocking,
624
- 1 advisory]`, `[policies: 11 advisory]`, `[policies: passed]`, `[policies: none shipped]` (the release carries no
625
- policies), `[policies: could not check]` (a rule could not run) or `[policies: not checked]` (no stored check:
626
- deploying the release checks it, and so does `policy runs --release <name> --recheck`), and ends with
627
- `Policy check details: xano policy runs --release <name>`. The tag is the release's own check, its code against the
628
- policies it ships, and it is also what the tenant deploy gate judges: a release with blocking findings is held on
629
- every standard or run tenant (`tenant deploy_release --check`). The list reads `release/policy_check` once, stores nothing, and
630
- prints as it did before when that read is refused (Policies off, no `workspace:policy` read) or fails. `-o json`
631
- adds `policy_check` to each item (`{run_id, status, finished_at, counts {findings, blocking, advisory, errors},
632
- policies}`, or `null` for a release with no stored check) and leaves it out when the checks could not be read.
633
- Tier1 tenant, ephemeral and sandbox pushes carry policy files too when Policies is enabled and the tenant has
634
- a policy table. Remote tenant pushes still leave them out; remote release deploys carry them.
635
-
636
- `release deploy --set_live` lands the release as a new branch, then sets it live through the same policy gate as
637
- `branch set_live`: the branch is checked against its own policies, which live will have once it is set live, and
638
- blocking findings it introduces compared with the live branch, or leaves on objects it changes, refuse it. A branch
639
- that weakens a mandatory policy of the live branch needs `workspace:policy` update (a release that lands no policy is
640
- judged by the live branch's policies instead, and nothing is checked for weakening): without it set live is refused
641
- with `policy_weakening_permission_required`, the branch stays, and the command exits `1`, naming the branch to set
642
- live or delete. A gate refusal keeps the branch, prints the verdict, says `The branch was
643
- created; set live was refused` and exits `2`; `-o json` prints `{"branch_created": true, "set_live": false, "branch",
644
- "message", "policy_gate"}`. Set it live from Studio's Branches panel with a reason, or delete the branch and deploy
645
- again with `--policy-override "<reason>"`, which sets it live past the gate and is audited. A policy of the release
646
- that the new branch could not take (its key is held by another policy) is named in a warning.
647
-
648
472
  ### Platforms
649
473
 
650
474
  ```bash
@@ -782,14 +606,9 @@ xano tenant get <tenant_name>
782
606
  xano tenant create "My Tenant"
783
607
  xano tenant create "My Tenant" -d "Description" --type tier2 --cluster_id 1 --platform_id 5
784
608
  xano tenant create "My Tenant" --type tier2 --cluster_id 1 --license ./license.yaml
785
- xano tenant create "Production" --required_reviewers 1 --allow_deploy_bypass
786
- xano tenant create "Staging" --required_reviewers 0 --allow_quick_deploy
787
609
 
788
610
  # Edit a tenant
789
611
  xano tenant edit <tenant_name> --display "New Name" -d "New description"
790
- xano tenant edit <tenant_name> --required_reviewers 1
791
- xano tenant edit <tenant_name> --required_reviewers 0
792
- xano tenant edit <tenant_name> --allow_quick_deploy
793
612
 
794
613
  # Delete a tenant (confirmation required)
795
614
  xano tenant delete <tenant_name>
@@ -836,101 +655,10 @@ xano tenant deploy_platform <tenant_name> --platform_id 5
836
655
  # Deploy a release by name
837
656
  xano tenant deploy_release <tenant_name> --release v1.0
838
657
 
839
- # Preview the release's policy gate on the tenant without deploying (exit 2 when it would refuse)
840
- xano tenant deploy_release <tenant_name> --release v1.0 --check
841
- xano tenant deploy_release <tenant_name> --release v1.0 --check -o json
842
-
843
- # Deploy past a blocking policy gate with an audited reason (needs workspace:policy update)
844
- xano tenant deploy_release <tenant_name> --release v1.0 --override-reason "Accepted; fix tracked in JIRA-12"
845
-
846
658
  # Deploy with a license override file (deploy_platform only)
847
659
  xano tenant deploy_platform <tenant_name> --platform_id 5 --license ./license.yaml
848
660
  ```
849
661
 
850
- If a tenant has `required_reviewers` set above 0, `tenant deploy_release` fails with an
851
- error naming the `tenant_deploy_request` command to run instead — see below. A tenant
852
- with `allow_quick_deploy` enabled skips the gate entirely, regardless of
853
- `required_reviewers`.
854
-
855
- A release deploy is also gated on policy. The gate judges the release alone, by its own policy check (the one
856
- stored when it was cut, or checked from its archive on demand): the blocking findings of its active mandatory policies
857
- refuse the deploy, including findings the tenant's current release already has. It does not compare the release with
858
- what the tenant runs, so the same release gets the same verdict on every tenant. Ephemeral and sandbox tenants get the
859
- verdict but are never refused. A release cut before releases carried policies reads `not_carried` and is not gated.
860
- `--check` prints the verdict (status, the release and its policy run, the number of blocking findings, and the first
861
- of them) without deploying; `-o json` is the gate's answer as served. A refused deploy prints the same verdict and exits `2`, naming the
862
- `--override-reason` rerun when your credential may override; under `-o json` it prints
863
- `{"deployed": false, "message", "policy_gate"}`. `--override-reason` sends `override_policy: true` with the reason,
864
- and the platform audits it. A successful deploy's summary prints the gate's status when the platform returns one,
865
- and a warning names any policy of the release the tenant could not take (its key is held by another policy).
866
-
867
- #### Tenant Deploy Requests
868
-
869
- When a tenant has `required_reviewers` set above 0 and `allow_quick_deploy` is off (see
870
- `tenant create`/`tenant edit` above), a release may only be deployed to it through an
871
- **approved deploy request**, requiring that many distinct reviewer approvals. An author
872
- with no deploy permission opens a request; a named reviewer who does hold deploy
873
- permission on the tenant votes to approve it, and once enough reviewers have approved,
874
- it deploys as part of that action. `allow_deploy_bypass` (set at `tenant create` time
875
- only) additionally allows an authorized user to skip a specific request's gate via
876
- `tenant_deploy_request bypass`, audited with a required reason.
877
-
878
- ```bash
879
- # List deploy requests in a workspace (results show the target tenant and release)
880
- xano tenant_deploy_request list
881
- xano tenant_deploy_request list --tenant prod --status pending
882
- xano tenant_deploy_request list --tenant prod --release v1.2
883
- xano tenant_deploy_request list --to-review
884
-
885
- # Get details of a specific deploy request, with its release's policy check on its tenant
886
- xano tenant_deploy_request get <id>
887
-
888
- # Open a deploy request (submits for review immediately unless --draft is passed)
889
- xano tenant_deploy_request create "Deploy v1.2 to prod" --tenant prod --release v1.2 --reviewers 12,45
890
- xano tenant_deploy_request create "Deploy v1.2 to prod" --tenant prod --release v1.2 --draft
891
-
892
- # Edit a deploy request's title, description or reviewers
893
- xano tenant_deploy_request edit <id> --title "Deploy v1.2.1 to prod"
894
- xano tenant_deploy_request edit <id> --reviewers 12,45,67
895
-
896
- # Change status: draft, submit, approve, request_changes, close, or reopen
897
- # (approve records one reviewer's vote; once enough votes are in it deploys the
898
- # release as part of that same call)
899
- xano tenant_deploy_request set_status <id> --status submit
900
- xano tenant_deploy_request set_status <id> --status approve
901
- xano tenant_deploy_request set_status <id> --status approve --override-policy --reason "Accepted; fix tracked in JIRA-12"
902
- xano tenant_deploy_request set_status <id> --status request_changes --reason "Needs a migration note"
903
- xano tenant_deploy_request set_status <id> --status close --reason "Superseded by a newer request"
904
- xano tenant_deploy_request set_status <id> --status reopen
905
-
906
- # Link a newer release to a request after changes were requested (author only)
907
- xano tenant_deploy_request revision <id> --release v1.2.1 --note "Added the missing migration"
908
-
909
- # Deploy past the approval gate under the deploy-bypass permission (requires the
910
- # tenant's allow_deploy_bypass to be enabled, and a separately-grantable RBAC
911
- # permission). Every use is audited.
912
- xano tenant_deploy_request bypass <id> --reason "Prod incident, reviewer unavailable"
913
- xano tenant_deploy_request bypass <id> --reason "Prod incident, reviewer unavailable" --override-policy
914
- ```
915
-
916
- `tenant_deploy_request get` always names the tenant and release. For requests that are neither approved nor closed,
917
- it also prints what the policy gate would decide now for the request's release on its
918
- tenant (under `-o json`, as `policy_gate`). It needs `workspace:policy` read; without it, or with the Policies
919
- feature off, it prints `Policy checks: not available (<why>)` (`policy_gate: null` and `policy_gate_unavailable`
920
- under `-o json`) and the command still succeeds. The deploy an approval triggers, and a bypass deploy, are gated like
921
- `tenant deploy_release`: a refusal prints the verdict and exits `2`. An approval refused this way is still recorded
922
- and the request stays approved; deploy it with `tenant deploy_release --override-reason`. `--override-policy`
923
- (with `--reason` as the override reason, and the `workspace:policy` update permission) proceeds past the gate on
924
- `set_status --status approve` and on `bypass`. Approval bypass and policy override are separate permissions:
925
- neither implies the other.
926
-
927
- CI example (e.g. a GitHub Action step):
928
-
929
- ```bash
930
- xano tenant_deploy_request create "$PR_TITLE" --tenant prod --release v1.2 \
931
- --description "$PR_BODY" --reviewers 12,45 -o json
932
- ```
933
-
934
662
  #### Tenant License
935
663
 
936
664
  ```bash
@@ -63,16 +63,6 @@ export default abstract class BaseCommand extends Command {
63
63
  * Sets NODE_TLS_REJECT_UNAUTHORIZED=0 so all fetch() calls skip cert verification.
64
64
  */
65
65
  protected applyInsecureFromProfile(): void;
66
- /**
67
- * Report a failure as exit 1 for commands that reserve exit 2 for findings: operational errors,
68
- * flag errors and oclif's own default exit 2 all become 1, and under `-o json` the failure is also
69
- * written to stdout as `{"error": {"exit": 1, "message"}}`. A deliberate exit 0 passes through.
70
- */
71
- protected catchAsOperational(error: Error & {
72
- oclif?: {
73
- exit?: number;
74
- };
75
- }): Promise<void>;
76
66
  finally(_: Error | undefined): Promise<void>;
77
67
  /**
78
68
  * Get the resolved credentials file path, respecting --config flag and XANO_CONFIG env var.
@@ -88,8 +78,6 @@ export default abstract class BaseCommand extends Command {
88
78
  protected getOrCreateSandbox(profile: ProfileConfig, verbose: boolean): Promise<SandboxTenant>;
89
79
  protected getProfile(): string | undefined;
90
80
  init(): Promise<void>;
91
- /** Raw-argv check: the parsed flags are not available yet when the banner prints. */
92
- protected isJsonOutput(): boolean;
93
81
  protected loadCredentialsFile(): CredentialsFile | null;
94
82
  /**
95
83
  * Poll a static-host build until it reaches a terminal status (ok | error),
@@ -141,6 +129,7 @@ export default abstract class BaseCommand extends Command {
141
129
  verbose: boolean;
142
130
  workspaceId: string;
143
131
  }): Promise<string>;
132
+ private isJsonOutput;
144
133
  /**
145
134
  * Find and parse the nearest project-local profile.yaml, unless an explicit
146
135
  * -p/XANO_PROFILE was given (in which case the local file is ignored).
@@ -156,18 +156,6 @@ export default class BaseCommand extends Command {
156
156
  // Don't fail the command if we can't read the profile for insecure check
157
157
  }
158
158
  }
159
- /**
160
- * Report a failure as exit 1 for commands that reserve exit 2 for findings: operational errors,
161
- * flag errors and oclif's own default exit 2 all become 1, and under `-o json` the failure is also
162
- * written to stdout as `{"error": {"exit": 1, "message"}}`. A deliberate exit 0 passes through.
163
- */
164
- async catchAsOperational(error) {
165
- if (error.oclif?.exit === 0)
166
- return super.catch(error);
167
- if (this.isJsonOutput())
168
- this.log(JSON.stringify({ error: { exit: 1, message: error.message } }, null, 2));
169
- this.error(error, { exit: 1 });
170
- }
171
159
  async finally(_) {
172
160
  if (this.updateNotice && !this.isJsonOutput()) {
173
161
  this.log(this.updateNotice);
@@ -233,19 +221,6 @@ export default class BaseCommand extends Command {
233
221
  const forceUpdateCheck = process.env.XANO_FORCE_UPDATE_CHECK === '1';
234
222
  this.updateNotice = checkForUpdate(this.config.version, forceUpdateCheck);
235
223
  }
236
- /** Raw-argv check: the parsed flags are not available yet when the banner prints. */
237
- isJsonOutput() {
238
- const args = this.argv;
239
- for (let i = 0; i < args.length; i++) {
240
- if (args[i] === '--output' && args[i + 1] === 'json')
241
- return true;
242
- if (args[i] === '-o' && args[i + 1] === 'json')
243
- return true;
244
- if (args[i] === '--output=json' || args[i] === '-o=json')
245
- return true;
246
- }
247
- return false;
248
- }
249
224
  loadCredentialsFile() {
250
225
  const credentialsPath = this.getCredentialsPath();
251
226
  if (!fs.existsSync(credentialsPath)) {
@@ -379,19 +354,18 @@ export default class BaseCommand extends Command {
379
354
  ...(timeoutMs > 0 && !options.signal ? { signal: AbortSignal.timeout(timeoutMs) } : {}),
380
355
  });
381
356
  const contentType = headers['Content-Type'] || 'application/json';
382
- const logDiagnostic = this.isJsonOutput() ? this.logToStderr.bind(this) : this.log.bind(this);
383
357
  if (verbose) {
384
- logDiagnostic('');
385
- logDiagnostic('─'.repeat(60));
386
- logDiagnostic(`→ ${method} ${url}`);
387
- logDiagnostic(` Content-Type: ${contentType}`);
358
+ this.log('');
359
+ this.log('─'.repeat(60));
360
+ this.log(`→ ${method} ${url}`);
361
+ this.log(` Content-Type: ${contentType}`);
388
362
  if (authToken) {
389
- logDiagnostic(` Authorization: Bearer ${authToken.slice(0, 8)}...${authToken.slice(-4)}`);
363
+ this.log(` Authorization: Bearer ${authToken.slice(0, 8)}...${authToken.slice(-4)}`);
390
364
  }
391
365
  if (options.body) {
392
366
  const bodyStr = typeof options.body === 'string' ? options.body : String(options.body);
393
367
  const bodyPreview = bodyStr.length > 500 ? bodyStr.slice(0, 500) + '...' : bodyStr;
394
- logDiagnostic(` Body: ${bodyPreview}`);
368
+ this.log(` Body: ${bodyPreview}`);
395
369
  }
396
370
  }
397
371
  const startTime = Date.now();
@@ -407,7 +381,7 @@ export default class BaseCommand extends Command {
407
381
  if (dispatcherSupported && isInvalidDispatcherError(error)) {
408
382
  dispatcherSupported = false;
409
383
  if (verbose) {
410
- logDiagnostic(' (this Node runtime rejected undici dispatcher; retrying without it — see DEV-7773)');
384
+ this.log(' (this Node runtime rejected undici dispatcher; retrying without it — see DEV-7773)');
411
385
  }
412
386
  response = await fetch(url, buildFetchOptions(false));
413
387
  }
@@ -417,9 +391,9 @@ export default class BaseCommand extends Command {
417
391
  }
418
392
  const elapsed = Date.now() - startTime;
419
393
  if (verbose) {
420
- logDiagnostic(`← ${response.status} ${response.statusText} (${elapsed}ms)`);
421
- logDiagnostic('─'.repeat(60));
422
- logDiagnostic('');
394
+ this.log(`← ${response.status} ${response.statusText} (${elapsed}ms)`);
395
+ this.log('─'.repeat(60));
396
+ this.log('');
423
397
  }
424
398
  return response;
425
399
  }
@@ -494,6 +468,18 @@ export default class BaseCommand extends Command {
494
468
  conclude(`stopped waiting after ${Math.round(timeoutMs / 1000)}s (last status: ${stage || 'unknown'})`);
495
469
  return stage;
496
470
  }
471
+ isJsonOutput() {
472
+ const args = process.argv;
473
+ for (let i = 0; i < args.length; i++) {
474
+ if (args[i] === '--output' && args[i + 1] === 'json')
475
+ return true;
476
+ if (args[i] === '-o' && args[i + 1] === 'json')
477
+ return true;
478
+ if (args[i] === '--output=json' || args[i] === '-o=json')
479
+ return true;
480
+ }
481
+ return false;
482
+ }
497
483
  /**
498
484
  * Find and parse the nearest project-local profile.yaml, unless an explicit
499
485
  * -p/XANO_PROFILE was given (in which case the local file is ignored).
@@ -8,33 +8,11 @@ export default class BranchSetLive extends BaseCommand {
8
8
  static flags: {
9
9
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
10
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
- 'policy-override': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
11
  workspace: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
12
  config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
13
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
14
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
15
  };
17
- /** Set once the platform refuses for a weakened mandatory policy: already explained, it exits 1 as it is. */
18
- private refusedByPermission;
19
- /** Set once the set-live policy gate refuses, the one failure that exits 2. */
20
- private refusedByPolicyGate;
21
- /** Exit 2 is the set-live gate's refusal; every other failure, flag errors included, exits 1. */
22
- protected catch(error: Error & {
23
- oclif?: {
24
- exit?: number;
25
- };
26
- }): Promise<void>;
27
16
  run(): Promise<void>;
28
17
  private confirm;
29
- /**
30
- * The set-live gate refused: the verdict on stdout (under `-o json`, `{set_live: false, message,
31
- * policy_gate}`), then the platform's message and how to proceed, exiting 2.
32
- */
33
- private refuseSetLive;
34
- /**
35
- * The branch weakens a mandatory policy of the live branch (or lacks one), and the credential lacks
36
- * `workspace:policy` update. That is a permission refusal, not a blocking finding: it exits 1 (under
37
- * `-o json` as `{error}`) with the platform's sentence and how to go on. The live branch is unchanged.
38
- */
39
- private refuseWeakening;
40
18
  }