@xano/cli 1.3.4 → 1.3.5-beta.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.
Files changed (101) hide show
  1. package/README.md +69 -2
  2. package/dist/base-command.d.ts +12 -1
  3. package/dist/base-command.js +36 -22
  4. package/dist/commands/branch/set_live/index.d.ts +14 -0
  5. package/dist/commands/branch/set_live/index.js +38 -0
  6. package/dist/commands/ephemeral/push/index.js +6 -1
  7. package/dist/commands/function/create/index.d.ts +15 -0
  8. package/dist/commands/function/create/index.js +39 -0
  9. package/dist/commands/function/edit/index.d.ts +15 -0
  10. package/dist/commands/function/edit/index.js +39 -0
  11. package/dist/commands/function/run/index.js +5 -11
  12. package/dist/commands/policy/catalogue/index.d.ts +16 -0
  13. package/dist/commands/policy/catalogue/index.js +60 -0
  14. package/dist/commands/policy/coverage/index.d.ts +17 -0
  15. package/dist/commands/policy/coverage/index.js +33 -0
  16. package/dist/commands/policy/create/index.d.ts +18 -0
  17. package/dist/commands/policy/create/index.js +44 -0
  18. package/dist/commands/policy/delete/index.d.ts +19 -0
  19. package/dist/commands/policy/delete/index.js +61 -0
  20. package/dist/commands/policy/evaluate/index.d.ts +32 -0
  21. package/dist/commands/policy/evaluate/index.js +71 -0
  22. package/dist/commands/policy/list/index.d.ts +14 -0
  23. package/dist/commands/policy/list/index.js +21 -0
  24. package/dist/commands/policy/parse/index.d.ts +19 -0
  25. package/dist/commands/policy/parse/index.js +18 -0
  26. package/dist/commands/policy/publish/index.d.ts +20 -0
  27. package/dist/commands/policy/publish/index.js +38 -0
  28. package/dist/commands/policy/runs/index.d.ts +54 -0
  29. package/dist/commands/policy/runs/index.js +273 -0
  30. package/dist/commands/policy/status/index.d.ts +21 -0
  31. package/dist/commands/policy/status/index.js +54 -0
  32. package/dist/commands/release/create/index.js +4 -0
  33. package/dist/commands/release/deploy/index.d.ts +19 -0
  34. package/dist/commands/release/deploy/index.js +68 -6
  35. package/dist/commands/release/import/index.js +5 -1
  36. package/dist/commands/release/list/index.d.ts +7 -0
  37. package/dist/commands/release/list/index.js +46 -9
  38. package/dist/commands/release/push/index.js +19 -1
  39. package/dist/commands/sandbox/push/index.js +6 -1
  40. package/dist/commands/skills/pull/index.d.ts +30 -0
  41. package/dist/commands/skills/pull/index.js +118 -0
  42. package/dist/commands/tenant/create/index.d.ts +3 -0
  43. package/dist/commands/tenant/create/index.js +21 -0
  44. package/dist/commands/tenant/deploy_release/index.d.ts +40 -0
  45. package/dist/commands/tenant/deploy_release/index.js +182 -4
  46. package/dist/commands/tenant/edit/index.d.ts +2 -0
  47. package/dist/commands/tenant/edit/index.js +39 -4
  48. package/dist/commands/tenant_deploy_request/bypass/index.d.ts +31 -0
  49. package/dist/commands/tenant_deploy_request/bypass/index.js +109 -0
  50. package/dist/commands/tenant_deploy_request/create/index.d.ts +21 -0
  51. package/dist/commands/tenant_deploy_request/create/index.js +108 -0
  52. package/dist/commands/tenant_deploy_request/edit/index.d.ts +22 -0
  53. package/dist/commands/tenant_deploy_request/edit/index.js +103 -0
  54. package/dist/commands/tenant_deploy_request/get/index.d.ts +25 -0
  55. package/dist/commands/tenant_deploy_request/get/index.js +113 -0
  56. package/dist/commands/tenant_deploy_request/list/index.d.ts +20 -0
  57. package/dist/commands/tenant_deploy_request/list/index.js +122 -0
  58. package/dist/commands/tenant_deploy_request/revision/index.d.ts +21 -0
  59. package/dist/commands/tenant_deploy_request/revision/index.js +83 -0
  60. package/dist/commands/tenant_deploy_request/set_status/index.d.ts +38 -0
  61. package/dist/commands/tenant_deploy_request/set_status/index.js +159 -0
  62. package/dist/commands/workspace/pull/index.d.ts +10 -0
  63. package/dist/commands/workspace/pull/index.js +70 -7
  64. package/dist/commands/workspace/push/index.d.ts +19 -0
  65. package/dist/commands/workspace/push/index.js +135 -6
  66. package/dist/policy-command.d.ts +91 -0
  67. package/dist/policy-command.js +95 -0
  68. package/dist/utils/document-parser.d.ts +10 -0
  69. package/dist/utils/document-parser.js +21 -0
  70. package/dist/utils/multidoc-push.d.ts +57 -2
  71. package/dist/utils/multidoc-push.js +102 -33
  72. package/dist/utils/policy/catalogue.d.ts +85 -0
  73. package/dist/utils/policy/catalogue.js +196 -0
  74. package/dist/utils/policy/coverage.d.ts +19 -0
  75. package/dist/utils/policy/coverage.js +93 -0
  76. package/dist/utils/policy/errors.d.ts +20 -0
  77. package/dist/utils/policy/errors.js +93 -0
  78. package/dist/utils/policy/feedback.d.ts +90 -0
  79. package/dist/utils/policy/feedback.js +210 -0
  80. package/dist/utils/policy/findings.d.ts +29 -0
  81. package/dist/utils/policy/findings.js +82 -0
  82. package/dist/utils/policy/gate.d.ts +138 -0
  83. package/dist/utils/policy/gate.js +193 -0
  84. package/dist/utils/policy/permission.d.ts +7 -0
  85. package/dist/utils/policy/permission.js +55 -0
  86. package/dist/utils/policy/push-gate.d.ts +5 -0
  87. package/dist/utils/policy/push-gate.js +43 -0
  88. package/dist/utils/policy/release.d.ts +85 -0
  89. package/dist/utils/policy/release.js +117 -0
  90. package/dist/utils/policy/request.d.ts +55 -0
  91. package/dist/utils/policy/request.js +119 -0
  92. package/dist/utils/policy/runs.d.ts +30 -0
  93. package/dist/utils/policy/runs.js +101 -0
  94. package/dist/utils/policy/status.d.ts +55 -0
  95. package/dist/utils/policy/status.js +127 -0
  96. package/dist/utils/policy/template.d.ts +66 -0
  97. package/dist/utils/policy/template.js +152 -0
  98. package/dist/utils/policy/types.d.ts +295 -0
  99. package/dist/utils/policy/types.js +1 -0
  100. package/oclif.manifest.json +5782 -2573
  101. package/package.json +9 -1
@@ -103,7 +103,6 @@ export default class FunctionRun extends BaseCommand {
103
103
  branch,
104
104
  name: functionName,
105
105
  profile,
106
- tenant: flags.tenant,
107
106
  verbose: flags.verbose,
108
107
  workspaceId,
109
108
  });
@@ -115,8 +114,9 @@ export default class FunctionRun extends BaseCommand {
115
114
  input = await this.promptForMissing(input, missingRequired);
116
115
  }
117
116
  }
118
- // 3) Execute via the meta run endpoint.
119
- const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function/run`;
117
+ // 3) Execute via the meta run endpoint, scoped to the tenant when one is given.
118
+ const tenantPath = flags.tenant ? `/tenant/${encodeURIComponent(flags.tenant)}` : '';
119
+ const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}${tenantPath}/function/run`;
120
120
  const body = { input, name: functionName };
121
121
  if (branch)
122
122
  body.branch = branch;
@@ -132,9 +132,6 @@ export default class FunctionRun extends BaseCommand {
132
132
  const dataSource = flags.datasource?.trim();
133
133
  if (dataSource)
134
134
  headers['X-Data-Source'] = dataSource;
135
- // X-Tenant runs the function on that tenant instead of the workspace.
136
- if (flags.tenant)
137
- headers['X-Tenant'] = flags.tenant;
138
135
  const response = await this.verboseFetch(apiUrl, {
139
136
  body: JSON.stringify(body),
140
137
  headers,
@@ -177,16 +174,13 @@ export default class FunctionRun extends BaseCommand {
177
174
  * transient introspection problem never blocks an otherwise-valid run.
178
175
  */
179
176
  async fetchFunctionByName(opts) {
180
- const { branch, name, profile, tenant, verbose, workspaceId } = opts;
177
+ const { branch, name, profile, verbose, workspaceId } = opts;
181
178
  const params = new URLSearchParams({ per_page: '100', search: name });
182
179
  if (branch)
183
180
  params.set('branch', branch);
184
181
  const url = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function?${params.toString()}`;
185
- const headers = { accept: 'application/json', Authorization: `Bearer ${profile.access_token}` };
186
- if (tenant)
187
- headers['X-Tenant'] = tenant;
188
182
  try {
189
- const response = await this.verboseFetch(url, { headers, method: 'GET' }, verbose, profile.access_token);
183
+ const response = await this.verboseFetch(url, { headers: { accept: 'application/json', Authorization: `Bearer ${profile.access_token}` }, method: 'GET' }, verbose, profile.access_token);
190
184
  if (!response.ok)
191
185
  return undefined;
192
186
  const page = (await response.json());
@@ -0,0 +1,16 @@
1
+ import PolicyCommand from '../../../policy-command.js';
2
+ export default class PolicyCatalogue extends PolicyCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ check: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ recipe: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ };
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,60 @@
1
+ import { Flags } from '@oclif/core';
2
+ import PolicyCommand from '../../../policy-command.js';
3
+ import { policyCatalogueSummary, policyNotCheckableSummary, policyRecipeSummary, policyTemplateSummary, selectCatalogueCheck, selectCatalogueRecipe, } from '../../../utils/policy/catalogue.js';
4
+ import { listItems } from '../../../utils/policy/request.js';
5
+ /** A served list, or none when the instance serves something else (an older one serves no recipes). */
6
+ const array = (value) => Array.isArray(value) ? value : [];
7
+ export default class PolicyCatalogue extends PolicyCommand {
8
+ static description = 'List built-in policy checks with their uses and parameter schemas, the templates a policy can start from, the recipes that combine them and what no check can see';
9
+ static examples = [
10
+ '$ xano policy catalogue -o json',
11
+ '$ xano policy catalogue --check object.auth_required',
12
+ '$ xano policy catalogue --recipe protect_card_data',
13
+ ];
14
+ static flags = {
15
+ ...PolicyCommand.policyFlags,
16
+ check: Flags.string({ description: 'Show only this check id (an unknown id names the closest matches)', exclusive: ['recipe'] }),
17
+ recipe: Flags.string({ description: 'Show only this recipe id and what it cannot see (an unknown id names the closest matches)', exclusive: ['check'] }),
18
+ };
19
+ async run() {
20
+ const { flags } = await this.parse(PolicyCatalogue);
21
+ const { request } = this.policyTarget(flags);
22
+ const result = await request('/check');
23
+ const sections = (result && typeof result === 'object' ? result : {});
24
+ const recipeContext = {
25
+ frameworks: array(sections.template_frameworks),
26
+ frameworksNote: sections.template_frameworks_note ?? '',
27
+ notCheckable: array(sections.not_checkable),
28
+ templates: array(sections.templates),
29
+ };
30
+ if (flags.check) {
31
+ // `--check` narrows both output modes, so `-o json` stays pipeable for one check too.
32
+ const selected = selectCatalogueCheck(listItems(result), flags.check);
33
+ this.log(flags.output === 'json' ? JSON.stringify(selected, null, 2) : policyCatalogueSummary(selected).join('\n'));
34
+ }
35
+ else if (flags.recipe) {
36
+ // `--recipe` narrows both output modes too; the summary adds the topics the recipe cannot see.
37
+ const selected = selectCatalogueRecipe(array(sections.recipes), flags.recipe);
38
+ if (flags.output === 'json') {
39
+ this.log(JSON.stringify(selected, null, 2));
40
+ return;
41
+ }
42
+ const cited = new Set(selected.flatMap(recipe => array(recipe.limits)));
43
+ const limits = recipeContext.notCheckable.filter(entry => cited.has(entry.id));
44
+ this.log([...policyRecipeSummary(selected, recipeContext), ...(limits.length > 0 ? ['', ...policyNotCheckableSummary(limits)] : [])].join('\n'));
45
+ }
46
+ else if (flags.output === 'json') {
47
+ this.log(JSON.stringify(result, null, 2));
48
+ }
49
+ else {
50
+ for (const line of policyCatalogueSummary(listItems(result)))
51
+ this.log(line);
52
+ const templates = policyTemplateSummary(recipeContext.templates, array(sections.template_categories), recipeContext.frameworks, recipeContext.frameworksNote);
53
+ const recipes = policyRecipeSummary(array(sections.recipes), recipeContext);
54
+ const notCheckable = policyNotCheckableSummary(recipeContext.notCheckable);
55
+ for (const section of [templates, recipes, notCheckable])
56
+ if (section.length > 0)
57
+ this.log(['', ...section].join('\n'));
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,17 @@
1
+ import PolicyCommand from '../../../policy-command.js';
2
+ export default class PolicyCoverage extends PolicyCommand {
3
+ static args: {
4
+ object: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ };
16
+ run(): Promise<void>;
17
+ }
@@ -0,0 +1,33 @@
1
+ import { Args } from '@oclif/core';
2
+ import PolicyCommand from '../../../policy-command.js';
3
+ import { coverageLines, parseObjectRef } from '../../../utils/policy/coverage.js';
4
+ export default class PolicyCoverage extends PolicyCommand {
5
+ static args = {
6
+ object: Args.string({
7
+ description: 'The object, written type:id as findings name it (query:42, function:12, table:7); for workspace, the workspace\'s own id (workspace:17)',
8
+ required: true,
9
+ }),
10
+ };
11
+ static description = "Show which active policies apply to one object, why, and the object's findings in the branch's latest run, "
12
+ + 'with each fix hint. Needs only the read permission for that object kind (workspace:api for an endpoint, '
13
+ + 'workspace:function for a function, …), not workspace:policy; the answer leaves out policy details that need it.';
14
+ static examples = [
15
+ '$ xano policy coverage query:42',
16
+ '$ xano policy coverage function:12 -b dev -o json',
17
+ ];
18
+ static flags = { ...PolicyCommand.policyFlags };
19
+ async run() {
20
+ const { args, flags } = await this.parse(PolicyCoverage);
21
+ const object = parseObjectRef(args.object);
22
+ if (!object)
23
+ this.error(`Name the object as type:id, as findings name it (for example query:42); got "${args.object}".`);
24
+ const { request } = this.policyTarget(flags);
25
+ const answer = (await request('/object/coverage', 'GET', undefined, { id: String(object.id), type: object.type }));
26
+ if (flags.output === 'json') {
27
+ this.log(JSON.stringify(answer, null, 2));
28
+ return;
29
+ }
30
+ for (const line of coverageLines(answer))
31
+ this.log(line);
32
+ }
33
+ }
@@ -0,0 +1,18 @@
1
+ import PolicyCommand from '../../../policy-command.js';
2
+ export default class PolicyCreate extends PolicyCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ key: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ param: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ template: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ message: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ };
17
+ run(): Promise<void>;
18
+ }
@@ -0,0 +1,44 @@
1
+ import { Flags } from '@oclif/core';
2
+ import PolicyCommand from '../../../policy-command.js';
3
+ import { listAllPolicies } from '../../../utils/policy/request.js';
4
+ import { policyFromTemplate, recipeRefusal } from '../../../utils/policy/template.js';
5
+ export default class PolicyCreate extends PolicyCommand {
6
+ static description = 'Create an independent policy from a platform template';
7
+ static examples = [
8
+ '$ xano policy create --template endpoints_need_login -b dev',
9
+ `$ xano policy create --template no_pii_in_responses --key DATA-020 --param 'fields=["email","ssn"]'`,
10
+ ];
11
+ static flags = {
12
+ ...PolicyCommand.policyFlags,
13
+ ...PolicyCommand.publishFlags,
14
+ key: Flags.string({ description: 'New policy key (default: the template key with a free numeric suffix if taken)' }),
15
+ param: Flags.string({ description: 'Parameter override: N.path=JSON, 1-based rule number; path=JSON for a single-rule template', multiple: true }),
16
+ template: Flags.string({ description: 'Template id from policy catalogue', required: true }),
17
+ };
18
+ async run() {
19
+ const { flags } = await this.parse(PolicyCreate);
20
+ const target = this.policyTarget(flags);
21
+ const { request } = target;
22
+ const catalogue = await request('/check');
23
+ const template = catalogue.templates?.find(item => item.id === flags.template);
24
+ // A recipe is data with no create action: name the templates it points at instead.
25
+ const recipe = template || !Array.isArray(catalogue.recipes) ? undefined : catalogue.recipes.find(item => item.id === flags.template);
26
+ if (recipe)
27
+ this.error(recipeRefusal(recipe));
28
+ if (!template)
29
+ this.error(`Unknown template "${flags.template}". Run xano policy catalogue to see this instance's templates.`);
30
+ const keys = (await listAllPolicies(request)).map(policy => policy.key);
31
+ // The key is checked against the pattern this instance's catalogue serves, and an unfilled need's
32
+ // example falls back to its check's own.
33
+ const document = policyFromTemplate(template, keys, { checks: catalogue.items, key: flags.key, keyRule: catalogue.document?.key, overrides: flags.param });
34
+ const parsed = await this.parseSource(request, { data: document });
35
+ const saved = await request('', 'POST', { data: { source: parsed.source }, ...(flags.message ? { message: flags.message } : {}) });
36
+ if (typeof saved?.id !== 'number')
37
+ this.error('The platform did not return the saved policy. Check xano policy list before retrying.');
38
+ this.logRuleWarnings(parsed);
39
+ if (flags.output === 'json')
40
+ this.log(JSON.stringify(saved, null, 2));
41
+ else
42
+ this.log(`Created ${parsed.policy.key} (Version ${saved.version}) in ${this.where(target)}.`);
43
+ }
44
+ }
@@ -0,0 +1,19 @@
1
+ import PolicyCommand from '../../../policy-command.js';
2
+ export default class PolicyDelete extends PolicyCommand {
3
+ static args: {
4
+ policy: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ };
17
+ /** `workspace push` is additive, so deleting a policy file leaves the policy in place; this removes it. */
18
+ run(): Promise<void>;
19
+ }
@@ -0,0 +1,61 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import PolicyCommand from '../../../policy-command.js';
3
+ import { confirm } from '../../../utils/multidoc-push.js';
4
+ import { listAllPolicies, sameKey } from '../../../utils/policy/request.js';
5
+ export default class PolicyDelete extends PolicyCommand {
6
+ static args = {
7
+ policy: Args.string({
8
+ description: 'Policy key (AUTH-001) or ID to delete',
9
+ required: true,
10
+ }),
11
+ };
12
+ static description = 'Delete a policy from a branch; its Version History is kept. It needs the workspace:policy delete permission only, an active, blocking policy included.';
13
+ static examples = [
14
+ `$ xano policy delete TMP-DX-001
15
+ Delete policy TMP-DX-001 (ID: 922, Version 2) from workspace 3? Its Version History is kept. (y/N) y
16
+ Deleted policy TMP-DX-001 (ID: 922) from workspace 3.
17
+ `,
18
+ '$ xano policy delete 922 --force',
19
+ '$ xano policy delete TMP-DX-001 -w 3 --force -o json',
20
+ ];
21
+ static flags = {
22
+ ...PolicyCommand.policyFlags,
23
+ force: Flags.boolean({
24
+ char: 'f',
25
+ default: false,
26
+ description: '[IMPORTANT] NEVER run without explicit user confirmation. Skips the confirmation prompt.',
27
+ }),
28
+ };
29
+ /** `workspace push` is additive, so deleting a policy file leaves the policy in place; this removes it. */
30
+ async run() {
31
+ const { args, flags } = await this.parse(PolicyDelete);
32
+ const target = this.policyTarget(flags);
33
+ const wanted = args.policy.trim();
34
+ if (!wanted)
35
+ this.error('Provide the policy key or ID to delete.');
36
+ const policies = await listAllPolicies(target.request);
37
+ const matched = policies.find((policy) => sameKey(policy.key, wanted))
38
+ ?? (/^\d+$/.test(wanted) ? policies.find((policy) => policy.id === Number(wanted)) : undefined);
39
+ if (!matched) {
40
+ const known = policies.map((policy) => policy.key).sort();
41
+ this.error(`No policy "${wanted}" on ${this.where(target)}.${known.length > 0 ? ` This branch has: ${known.join(', ')}.` : ' This branch has no policies.'}`);
42
+ }
43
+ if (!flags.force) {
44
+ // Nobody can answer the prompt: refuse rather than report a cancel as success.
45
+ if (!process.stdin.isTTY)
46
+ this.error('Non-interactive environment detected. Use --force to skip confirmation.');
47
+ const confirmed = await confirm(`Delete policy ${matched.key} (ID: ${matched.id}, Version ${matched.version}) from ${this.where(target)}? Its Version History is kept.`);
48
+ if (!confirmed) {
49
+ this.log('Deletion cancelled.');
50
+ return;
51
+ }
52
+ }
53
+ // The policy as listed is what the prompt named: a delete of a policy changed since is refused (`policy_stale`).
54
+ const staleCheck = matched.updated_at ? { last_updated_at: String(matched.updated_at) } : {};
55
+ await target.request(`/${matched.id}`, 'DELETE', undefined, staleCheck);
56
+ if (flags.output === 'json')
57
+ this.log(JSON.stringify({ deleted: true, id: matched.id, key: matched.key }, null, 2));
58
+ else
59
+ this.log(`Deleted policy ${matched.key} (ID: ${matched.id}) from ${this.where(target)}.`);
60
+ }
61
+ }
@@ -0,0 +1,32 @@
1
+ import PolicyCommand from '../../../policy-command.js';
2
+ /** One rule's clear objects in a trial's answer: the first ones and how many there are. */
3
+ type TrialClear = {
4
+ items?: Array<{
5
+ name?: string;
6
+ }>;
7
+ policy_key?: string;
8
+ rule_id?: string;
9
+ total?: number;
10
+ };
11
+ /**
12
+ * What a trial says it is, then each rule's clear objects: the objects it examined and found
13
+ * nothing on. A trial is never stored, so its answer is all there is to show.
14
+ */
15
+ export declare function trialLines(key: string, clear: TrialClear[] | undefined): string[];
16
+ export default class PolicyEvaluate extends PolicyCommand {
17
+ static description: string;
18
+ static examples: string[];
19
+ static flags: {
20
+ policy: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ 'run-detail': import("@oclif/core/interfaces").BooleanFlag<boolean>;
22
+ summary: import("@oclif/core/interfaces").BooleanFlag<boolean>;
23
+ branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
24
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
25
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
26
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
27
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
28
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
29
+ };
30
+ run(): Promise<void>;
31
+ }
32
+ export {};
@@ -0,0 +1,71 @@
1
+ import { Flags } from '@oclif/core';
2
+ import PolicyCommand from '../../../policy-command.js';
3
+ import { evaluationEvidence, notStoredLine, policyCheckWarning, policyExitCode, policySummary, } from '../../../utils/policy/feedback.js';
4
+ /** How many clear objects a trial names per rule; the rest are counted. */
5
+ const TRIAL_CLEAR_NAMES = 5;
6
+ /**
7
+ * What a trial says it is, then each rule's clear objects: the objects it examined and found
8
+ * nothing on. A trial is never stored, so its answer is all there is to show.
9
+ */
10
+ export function trialLines(key, clear) {
11
+ const lines = [`Trial of ${key}: this policy alone, evaluated without storing a run. It is not a report and gates nothing.`];
12
+ if (!clear?.length)
13
+ return lines;
14
+ lines.push('Clear objects (examined, nothing found):');
15
+ for (const rule of clear) {
16
+ const names = (rule.items ?? []).map((item) => item.name ?? '').filter(Boolean).slice(0, TRIAL_CLEAR_NAMES);
17
+ const total = rule.total ?? 0;
18
+ const more = total > names.length ? `${names.length > 0 ? ', ' : ''}${total - names.length} more` : '';
19
+ lines.push(` ${rule.rule_id ?? ''} ${total}${names.length > 0 || more ? ` ${names.join(', ')}${more}` : ''}`);
20
+ }
21
+ return lines;
22
+ }
23
+ export default class PolicyEvaluate extends PolicyCommand {
24
+ static description = 'Evaluate active branch policies and report their findings; blocking findings exit 2. With --policy, try one policy alone, whatever its state, without storing a run';
25
+ static examples = ['$ xano policy evaluate -o json', '$ xano policy evaluate --run-detail', '$ xano policy evaluate --summary -o json', '$ xano policy evaluate --policy AUTH-001'];
26
+ static flags = {
27
+ ...PolicyCommand.policyFlags,
28
+ policy: Flags.string({
29
+ description: 'Try this one policy (its key) alone, whatever its state: nothing is stored, and the answer lists each rule\'s clear objects',
30
+ }),
31
+ 'run-detail': Flags.boolean({
32
+ default: false,
33
+ description: 'Also print what this run recorded: each policy description and the settings each rule ran with',
34
+ }),
35
+ summary: Flags.boolean({
36
+ default: false,
37
+ description: 'Answer the run summary and its first 50 findings instead of the whole run; `xano policy runs <id>` pages through the rest',
38
+ }),
39
+ };
40
+ async run() {
41
+ const { flags } = await this.parse(PolicyEvaluate);
42
+ if (flags.policy !== undefined && !flags.policy.trim())
43
+ this.error('--policy must name a policy key.');
44
+ const { request } = this.policyTarget(flags);
45
+ // The evaluation answers with its run, so the run's own snapshot names any unnamed rule. The
46
+ // summary answer carries the same snapshot and results, and only the first findings.
47
+ const body = { ...(flags.summary ? { answer: 'summary' } : {}), ...(flags.policy ? { policy: flags.policy } : {}) };
48
+ const result = (await request('/evaluate', 'POST', Object.keys(body).length > 0 ? body : undefined));
49
+ if (flags.output === 'json')
50
+ this.log(JSON.stringify(result, null, 2));
51
+ else {
52
+ for (const line of policySummary(result.policy_check, evaluationEvidence(result)))
53
+ this.log(line);
54
+ // A trial is never stored by design; the "cannot record runs" note is about credentials.
55
+ const notStored = flags.policy ? null : notStoredLine(result);
56
+ if (notStored)
57
+ this.log(notStored);
58
+ if (flags.policy)
59
+ for (const line of trialLines(flags.policy, result.clear))
60
+ this.log(line);
61
+ if (flags['run-detail'])
62
+ this.logRunDetail(result);
63
+ }
64
+ const warning = policyCheckWarning(result.policy_check);
65
+ if (warning)
66
+ this.warn(warning);
67
+ const code = policyExitCode(result.policy_check);
68
+ if (code)
69
+ process.exitCode = code;
70
+ }
71
+ }
@@ -0,0 +1,14 @@
1
+ import PolicyCommand from '../../../policy-command.js';
2
+ export default class PolicyList extends PolicyCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,21 @@
1
+ import PolicyCommand from '../../../policy-command.js';
2
+ import { listItems } from '../../../utils/policy/request.js';
3
+ export default class PolicyList extends PolicyCommand {
4
+ static description = 'List workspace policies and their descriptions';
5
+ static examples = ['$ xano policy list -o json'];
6
+ static flags = { ...PolicyCommand.policyFlags };
7
+ async run() {
8
+ const { flags } = await this.parse(PolicyList);
9
+ const { request } = this.policyTarget(flags);
10
+ const result = await request();
11
+ if (flags.output === 'json') {
12
+ this.log(JSON.stringify(result, null, 2));
13
+ return;
14
+ }
15
+ const policies = listItems(result);
16
+ if (policies.length === 0)
17
+ this.log('No policies found.');
18
+ for (const policy of policies)
19
+ this.log(`${policy.key} ${policy.active ? `Active, ${policy.enforcement === 'blocking' ? 'Blocking' : 'Advisory'}` : 'Inactive'} ${policy.title ?? ''} (ID: ${policy.id}, Version ${policy.version})`);
20
+ }
21
+ }
@@ -0,0 +1,19 @@
1
+ import PolicyCommand from '../../../policy-command.js';
2
+ export default class PolicyParse extends PolicyCommand {
3
+ static args: {
4
+ file: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ };
18
+ run(): Promise<void>;
19
+ }
@@ -0,0 +1,18 @@
1
+ import PolicyCommand from '../../../policy-command.js';
2
+ export default class PolicyParse extends PolicyCommand {
3
+ static args = { ...PolicyCommand.sourceArgs };
4
+ static description = 'Validate and format policy XanoScript using the platform parser, with rule warnings against the branch';
5
+ static examples = [
6
+ '$ xano policy parse policies/AUTH-001.xs',
7
+ '$ xano policy parse --file policies/AUTH-001.xs',
8
+ ];
9
+ static flags = { ...PolicyCommand.policyFlags, ...PolicyCommand.sourceFlags };
10
+ async run() {
11
+ const { args, flags } = await this.parse(PolicyParse);
12
+ const { request } = this.policyTarget(flags);
13
+ const parsed = await this.parseSource(request, { source: this.readSource(flags, args.file) });
14
+ this.log(flags.output === 'json' ? JSON.stringify(parsed, null, 2) : parsed.source);
15
+ if (flags.output !== 'json')
16
+ this.logRuleWarnings(parsed);
17
+ }
18
+ }
@@ -0,0 +1,20 @@
1
+ import PolicyCommand from '../../../policy-command.js';
2
+ export default class PolicyPublish extends PolicyCommand {
3
+ static args: {
4
+ file: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ message: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
+ };
19
+ run(): Promise<void>;
20
+ }
@@ -0,0 +1,38 @@
1
+ import PolicyCommand from '../../../policy-command.js';
2
+ import { listAllPolicies, sameKey } from '../../../utils/policy/request.js';
3
+ export default class PolicyPublish extends PolicyCommand {
4
+ static args = { ...PolicyCommand.sourceArgs };
5
+ static description = 'Create or update a workspace policy from native XanoScript';
6
+ static examples = [
7
+ '$ xano policy publish policies/AUTH-001.xs',
8
+ '$ xano policy publish --file policies/AUTH-001.xs',
9
+ '$ xano policy publish --file policies/AUTH-001.xs -m "Tightened the scope"',
10
+ ];
11
+ static flags = { ...PolicyCommand.policyFlags, ...PolicyCommand.sourceFlags, ...PolicyCommand.publishFlags };
12
+ async run() {
13
+ const { args, flags } = await this.parse(PolicyPublish);
14
+ const target = this.policyTarget(flags);
15
+ const { request } = target;
16
+ const source = this.readSource(flags, args.file);
17
+ // The key picks PUT or POST, so the list is read alongside the parse rather than after it.
18
+ const [parsed, policies] = await Promise.all([this.parseSource(request, { source }), listAllPolicies(request)]);
19
+ // A key that differs only in case names the same policy, which PUT updates, key included.
20
+ const existing = policies.find((policy) => sameKey(policy.key, parsed.policy.key));
21
+ const body = { data: { source: parsed.source } };
22
+ if (flags.message)
23
+ body.message = flags.message;
24
+ const saved = (await request(existing ? `/${existing.id}` : '', existing ? 'PUT' : 'POST', body));
25
+ if (typeof saved?.id !== 'number') {
26
+ this.error('The platform did not return the saved policy. Check `xano policy list` before retrying.');
27
+ }
28
+ this.logRuleWarnings(parsed);
29
+ if (flags.output === 'json') {
30
+ this.log(JSON.stringify(saved, null, 2));
31
+ return;
32
+ }
33
+ // A save identical to the stored definition writes nothing: no version, history entry or audit record.
34
+ this.log(saved.unchanged === true
35
+ ? `No changes to ${parsed.policy.key} (Version ${saved.version}) in ${this.where(target)}.`
36
+ : `Published ${parsed.policy.key} (Version ${saved.version}) to ${this.where(target)}.`);
37
+ }
38
+ }
@@ -0,0 +1,54 @@
1
+ import { Interfaces } from '@oclif/core';
2
+ import PolicyCommand from '../../../policy-command.js';
3
+ export default class PolicyRuns extends PolicyCommand {
4
+ static args: {
5
+ run_id: Interfaces.Arg<string | undefined, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ advisory: Interfaces.BooleanFlag<boolean>;
11
+ all: Interfaces.BooleanFlag<boolean>;
12
+ blocking: Interfaces.BooleanFlag<boolean>;
13
+ kind: Interfaces.OptionFlag<string[] | undefined, Interfaces.CustomOptions>;
14
+ limit: Interfaces.OptionFlag<number | undefined, Interfaces.CustomOptions>;
15
+ object: Interfaces.OptionFlag<string[] | undefined, Interfaces.CustomOptions>;
16
+ offset: Interfaces.OptionFlag<number | undefined, Interfaces.CustomOptions>;
17
+ policy: Interfaces.OptionFlag<string[] | undefined, Interfaces.CustomOptions>;
18
+ recheck: Interfaces.BooleanFlag<boolean>;
19
+ release: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
20
+ 'release-id': Interfaces.OptionFlag<number | undefined, Interfaces.CustomOptions>;
21
+ rule: Interfaces.OptionFlag<string[] | undefined, Interfaces.CustomOptions>;
22
+ 'run-detail': Interfaces.BooleanFlag<boolean>;
23
+ search: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
24
+ severity: Interfaces.OptionFlag<string[] | undefined, Interfaces.CustomOptions>;
25
+ tag: Interfaces.OptionFlag<string[] | undefined, Interfaces.CustomOptions>;
26
+ branch: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
27
+ output: Interfaces.OptionFlag<string, Interfaces.CustomOptions>;
28
+ workspace: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
29
+ config: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
30
+ profile: Interfaces.OptionFlag<string | undefined, Interfaces.CustomOptions>;
31
+ verbose: Interfaces.BooleanFlag<boolean>;
32
+ };
33
+ run(): Promise<void>;
34
+ /**
35
+ * `--all`: every finding that matches, read a page of 500 at a time in the platform's order, as
36
+ * one page. A run of many pages says on stderr what it is reading.
37
+ */
38
+ private everyFinding;
39
+ /** The retained runs, newest first; the flags that read one run's findings are refused. */
40
+ private listRuns;
41
+ /**
42
+ * `--release <name>`: the release's stored check (`GET release/{id}/policy_run`) and a page of its
43
+ * findings, as a run is read. `--recheck` checks the release again from its archive first. A
44
+ * release with no check says so; under `-o json` it is `{run: null}`.
45
+ */
46
+ private readRelease;
47
+ /** The release's ID from its name, reading the workspace's releases a page at a time until it is found. */
48
+ private releaseId;
49
+ /**
50
+ * One run's summary and a page of its findings (`path`), or under `-o json` the summary with
51
+ * `findings` and `findings_page`, plus what `extra` adds to it.
52
+ */
53
+ private reportRun;
54
+ }