@xano/cli 1.2.0 → 1.3.1-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.
- package/README.md +381 -4
- package/dist/base-command.d.ts +54 -36
- package/dist/base-command.js +246 -169
- package/dist/commands/auth/index.d.ts +1 -1
- package/dist/commands/auth/index.js +7 -7
- package/dist/commands/branch/create/index.d.ts +1 -1
- package/dist/commands/branch/create/index.js +1 -1
- package/dist/commands/branch/set_live/index.d.ts +22 -0
- package/dist/commands/branch/set_live/index.js +60 -1
- package/dist/commands/ephemeral/create/index.d.ts +18 -0
- package/dist/commands/ephemeral/create/index.js +103 -0
- package/dist/commands/ephemeral/delete/index.d.ts +18 -0
- package/dist/commands/ephemeral/delete/index.js +99 -0
- package/dist/commands/ephemeral/edit/index.d.ts +18 -0
- package/dist/commands/ephemeral/edit/index.js +109 -0
- package/dist/commands/ephemeral/get/index.d.ts +21 -0
- package/dist/commands/ephemeral/get/index.js +125 -0
- package/dist/commands/ephemeral/impersonate/index.d.ts +20 -0
- package/dist/commands/ephemeral/impersonate/index.js +120 -0
- package/dist/commands/ephemeral/list/index.d.ts +19 -0
- package/dist/commands/ephemeral/list/index.js +123 -0
- package/dist/commands/ephemeral/pull/index.d.ts +19 -0
- package/dist/commands/ephemeral/pull/index.js +163 -0
- package/dist/commands/ephemeral/push/index.d.ts +35 -0
- package/dist/commands/ephemeral/push/index.js +228 -0
- package/dist/commands/ephemeral/static_host/build/delete/index.d.ts +20 -0
- package/dist/commands/ephemeral/static_host/build/delete/index.js +121 -0
- package/dist/commands/ephemeral/static_host/build/get/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/build/get/index.js +137 -0
- package/dist/commands/ephemeral/static_host/build/list/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/build/list/index.js +137 -0
- package/dist/commands/ephemeral/static_host/build/pull/index.d.ts +53 -0
- package/dist/commands/ephemeral/static_host/build/pull/index.js +308 -0
- package/dist/commands/ephemeral/static_host/build/push/index.d.ts +24 -0
- package/dist/commands/ephemeral/static_host/build/push/index.js +250 -0
- package/dist/commands/ephemeral/static_host/create/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/create/index.js +92 -0
- package/dist/commands/ephemeral/static_host/deploy/index.d.ts +19 -0
- package/dist/commands/ephemeral/static_host/deploy/index.js +112 -0
- package/dist/commands/ephemeral/static_host/edit/index.d.ts +24 -0
- package/dist/commands/ephemeral/static_host/edit/index.js +158 -0
- package/dist/commands/ephemeral/static_host/get/index.d.ts +19 -0
- package/dist/commands/ephemeral/static_host/get/index.js +101 -0
- package/dist/commands/ephemeral/static_host/list/index.d.ts +17 -0
- package/dist/commands/ephemeral/static_host/list/index.js +132 -0
- package/dist/commands/flatten/index.d.ts +34 -0
- package/dist/commands/flatten/index.js +140 -0
- package/dist/commands/function/create/index.d.ts +15 -0
- package/dist/commands/function/create/index.js +41 -1
- package/dist/commands/function/edit/index.d.ts +15 -0
- package/dist/commands/function/edit/index.js +66 -32
- package/dist/commands/function/get/index.js +2 -3
- package/dist/commands/function/run/index.d.ts +1 -0
- package/dist/commands/function/run/index.js +19 -6
- package/dist/commands/policy/catalogue/index.d.ts +15 -0
- package/dist/commands/policy/catalogue/index.js +29 -0
- package/dist/commands/policy/create/index.d.ts +18 -0
- package/dist/commands/policy/create/index.js +39 -0
- package/dist/commands/policy/delete/index.d.ts +19 -0
- package/dist/commands/policy/delete/index.js +61 -0
- package/dist/commands/policy/evaluate/index.d.ts +32 -0
- package/dist/commands/policy/evaluate/index.js +71 -0
- package/dist/commands/policy/list/index.d.ts +14 -0
- package/dist/commands/policy/list/index.js +21 -0
- package/dist/commands/policy/parse/index.d.ts +19 -0
- package/dist/commands/policy/parse/index.js +18 -0
- package/dist/commands/policy/publish/index.d.ts +20 -0
- package/dist/commands/policy/publish/index.js +38 -0
- package/dist/commands/policy/runs/index.d.ts +54 -0
- package/dist/commands/policy/runs/index.js +273 -0
- package/dist/commands/policy/status/index.d.ts +21 -0
- package/dist/commands/policy/status/index.js +54 -0
- package/dist/commands/profile/create/index.d.ts +1 -1
- package/dist/commands/profile/create/index.js +7 -7
- package/dist/commands/profile/me/index.js +1 -1
- package/dist/commands/profile/use/index.js +0 -4
- package/dist/commands/profile/wizard/index.d.ts +1 -1
- package/dist/commands/profile/wizard/index.js +12 -10
- package/dist/commands/profile/workspace/set/index.js +1 -1
- package/dist/commands/release/create/index.js +7 -0
- package/dist/commands/release/deploy/index.d.ts +28 -0
- package/dist/commands/release/deploy/index.js +90 -6
- package/dist/commands/release/edit/index.js +2 -2
- package/dist/commands/release/export/index.js +1 -2
- package/dist/commands/release/import/index.js +6 -2
- package/dist/commands/release/list/index.d.ts +7 -0
- package/dist/commands/release/list/index.js +54 -10
- package/dist/commands/release/pull/index.d.ts +0 -6
- package/dist/commands/release/pull/index.js +13 -99
- package/dist/commands/release/push/index.js +21 -3
- package/dist/commands/sandbox/env/get_all/index.js +1 -1
- package/dist/commands/sandbox/env/set_all/index.js +1 -1
- package/dist/commands/sandbox/get/index.js +1 -1
- package/dist/commands/sandbox/license/get/index.js +1 -1
- package/dist/commands/sandbox/license/set/index.js +1 -1
- package/dist/commands/sandbox/pull/index.d.ts +0 -1
- package/dist/commands/sandbox/pull/index.js +13 -81
- package/dist/commands/sandbox/push/index.d.ts +1 -1
- package/dist/commands/sandbox/push/index.js +11 -6
- package/dist/commands/skills/pull/index.d.ts +30 -0
- package/dist/commands/skills/pull/index.js +118 -0
- package/dist/commands/static_host/build/create/index.d.ts +1 -1
- package/dist/commands/static_host/build/create/index.js +2 -2
- package/dist/commands/static_host/build/pull/index.js +1 -1
- package/dist/commands/static_host/build/push/index.js +1 -1
- package/dist/commands/static_host/edit/index.js +1 -1
- package/dist/commands/tenant/backup/export/index.js +1 -2
- package/dist/commands/tenant/backup/import/index.js +1 -1
- package/dist/commands/tenant/cluster/create/index.js +1 -1
- package/dist/commands/tenant/cluster/license/get/index.js +1 -1
- package/dist/commands/tenant/cluster/license/set/index.js +1 -1
- package/dist/commands/tenant/create/index.d.ts +4 -1
- package/dist/commands/tenant/create/index.js +26 -5
- package/dist/commands/tenant/deploy_platform/index.js +1 -1
- package/dist/commands/tenant/deploy_release/index.d.ts +40 -0
- package/dist/commands/tenant/deploy_release/index.js +186 -4
- package/dist/commands/tenant/edit/index.d.ts +2 -0
- package/dist/commands/tenant/edit/index.js +46 -11
- package/dist/commands/tenant/env/get_all/index.js +1 -1
- package/dist/commands/tenant/env/set_all/index.js +1 -1
- package/dist/commands/tenant/impersonate/index.d.ts +1 -1
- package/dist/commands/tenant/impersonate/index.js +14 -14
- package/dist/commands/tenant/license/get/index.js +1 -1
- package/dist/commands/tenant/license/set/index.js +1 -1
- package/dist/commands/tenant/pull/index.d.ts +0 -6
- package/dist/commands/tenant/pull/index.js +14 -100
- package/dist/commands/tenant_deploy_request/bypass/index.d.ts +31 -0
- package/dist/commands/tenant_deploy_request/bypass/index.js +109 -0
- package/dist/commands/tenant_deploy_request/create/index.d.ts +21 -0
- package/dist/commands/tenant_deploy_request/create/index.js +108 -0
- package/dist/commands/tenant_deploy_request/edit/index.d.ts +22 -0
- package/dist/commands/tenant_deploy_request/edit/index.js +103 -0
- package/dist/commands/tenant_deploy_request/get/index.d.ts +25 -0
- package/dist/commands/tenant_deploy_request/get/index.js +116 -0
- package/dist/commands/tenant_deploy_request/list/index.d.ts +20 -0
- package/dist/commands/tenant_deploy_request/list/index.js +122 -0
- package/dist/commands/tenant_deploy_request/revision/index.d.ts +21 -0
- package/dist/commands/tenant_deploy_request/revision/index.js +83 -0
- package/dist/commands/tenant_deploy_request/set_status/index.d.ts +38 -0
- package/dist/commands/tenant_deploy_request/set_status/index.js +159 -0
- package/dist/commands/workspace/git/pull/index.d.ts +4 -4
- package/dist/commands/workspace/git/pull/index.js +158 -97
- package/dist/commands/workspace/pull/index.d.ts +9 -5
- package/dist/commands/workspace/pull/index.js +83 -106
- package/dist/commands/workspace/push/index.d.ts +19 -0
- package/dist/commands/workspace/push/index.js +124 -5
- package/dist/help.js +4 -2
- package/dist/policy-command.d.ts +90 -0
- package/dist/policy-command.js +94 -0
- package/dist/update-check.d.ts +1 -1
- package/dist/update-check.js +1 -1
- package/dist/utils/document-parser.d.ts +126 -0
- package/dist/utils/document-parser.js +332 -4
- package/dist/utils/flatten.d.ts +36 -0
- package/dist/utils/flatten.js +68 -0
- package/dist/utils/knowledge-sync.d.ts +1 -0
- package/dist/utils/knowledge-sync.js +11 -6
- package/dist/utils/microservice-wait.d.ts +53 -0
- package/dist/utils/microservice-wait.js +84 -0
- package/dist/utils/multidoc-push.d.ts +143 -2
- package/dist/utils/multidoc-push.js +479 -40
- package/dist/utils/paging.d.ts +2 -1
- package/dist/utils/paging.js +0 -1
- package/dist/utils/policy/catalogue.d.ts +7 -0
- package/dist/utils/policy/catalogue.js +72 -0
- package/dist/utils/policy/errors.d.ts +12 -0
- package/dist/utils/policy/errors.js +61 -0
- package/dist/utils/policy/feedback.d.ts +68 -0
- package/dist/utils/policy/feedback.js +165 -0
- package/dist/utils/policy/findings.d.ts +23 -0
- package/dist/utils/policy/findings.js +65 -0
- package/dist/utils/policy/gate.d.ts +161 -0
- package/dist/utils/policy/gate.js +201 -0
- package/dist/utils/policy/goal.d.ts +46 -0
- package/dist/utils/policy/goal.js +95 -0
- package/dist/utils/policy/permission.d.ts +23 -0
- package/dist/utils/policy/permission.js +84 -0
- package/dist/utils/policy/push-gate.d.ts +5 -0
- package/dist/utils/policy/push-gate.js +29 -0
- package/dist/utils/policy/release.d.ts +85 -0
- package/dist/utils/policy/release.js +115 -0
- package/dist/utils/policy/request.d.ts +55 -0
- package/dist/utils/policy/request.js +115 -0
- package/dist/utils/policy/runs.d.ts +30 -0
- package/dist/utils/policy/runs.js +101 -0
- package/dist/utils/policy/status.d.ts +47 -0
- package/dist/utils/policy/status.js +117 -0
- package/dist/utils/policy/types.d.ts +185 -0
- package/dist/utils/policy/types.js +1 -0
- package/dist/utils/reference-checker.js +14 -3
- package/oclif.manifest.json +8538 -3445
- package/package.json +16 -1
|
@@ -3,8 +3,9 @@ import inquirer from 'inquirer';
|
|
|
3
3
|
import { execSync } from 'node:child_process';
|
|
4
4
|
import * as fs from 'node:fs';
|
|
5
5
|
import * as os from 'node:os';
|
|
6
|
-
import
|
|
6
|
+
import path from 'node:path';
|
|
7
7
|
import BaseCommand, { buildUserAgent } from '../../../base-command.js';
|
|
8
|
+
import { BLANK_POLICY_OVERRIDE, blankPolicyOverride, gateRefusal, publishRefusal, quoted } from '../../../utils/policy/gate.js';
|
|
8
9
|
export default class FunctionEdit extends BaseCommand {
|
|
9
10
|
static args = {
|
|
10
11
|
function_id: Args.string({
|
|
@@ -59,6 +60,7 @@ Name: my_function
|
|
|
59
60
|
...
|
|
60
61
|
}
|
|
61
62
|
`,
|
|
63
|
+
`$ xano function:edit 163 -f function.xs --policy-override "Exception approved; finding tracked in JIRA-12"`,
|
|
62
64
|
];
|
|
63
65
|
static flags = {
|
|
64
66
|
...BaseCommand.baseFlags,
|
|
@@ -81,6 +83,7 @@ Name: my_function
|
|
|
81
83
|
options: ['summary', 'json'],
|
|
82
84
|
required: false,
|
|
83
85
|
}),
|
|
86
|
+
'policy-override': Flags.string({ description: 'Publish past a blocking live-branch publish policy gate with an audited reason (requires workspace:policy update)' }),
|
|
84
87
|
publish: Flags.boolean({
|
|
85
88
|
default: true,
|
|
86
89
|
description: 'Publish the function after editing',
|
|
@@ -99,8 +102,18 @@ Name: my_function
|
|
|
99
102
|
required: false,
|
|
100
103
|
}),
|
|
101
104
|
};
|
|
105
|
+
/** Set once the publish policy gate refuses, the one failure that exits 2. */
|
|
106
|
+
refusedByPolicyGate = false;
|
|
107
|
+
/** Exit 2 is the publish gate's refusal; every other failure, flag errors included, exits 1. */
|
|
108
|
+
async catch(error) {
|
|
109
|
+
if (this.refusedByPolicyGate)
|
|
110
|
+
return super.catch(error);
|
|
111
|
+
return this.catchAsOperational(error);
|
|
112
|
+
}
|
|
102
113
|
async run() {
|
|
103
114
|
const { args, flags } = await this.parse(FunctionEdit);
|
|
115
|
+
if (blankPolicyOverride(flags['policy-override']))
|
|
116
|
+
this.error(BLANK_POLICY_OVERRIDE);
|
|
104
117
|
const { profile, profileName } = this.resolveProfile(flags);
|
|
105
118
|
// Validate flag combinations
|
|
106
119
|
if (flags.edit && !flags.file) {
|
|
@@ -120,8 +133,7 @@ Name: my_function
|
|
|
120
133
|
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
121
134
|
}
|
|
122
135
|
// If function_id is not provided, prompt user to select from list
|
|
123
|
-
|
|
124
|
-
functionId = args.function_id ? args.function_id : await this.promptForFunctionId(profile, workspaceId);
|
|
136
|
+
const functionId = args.function_id || (await this.promptForFunctionId(profile, workspaceId));
|
|
125
137
|
// Read XanoScript content
|
|
126
138
|
let xanoscript;
|
|
127
139
|
if (flags.file) {
|
|
@@ -176,6 +188,10 @@ Name: my_function
|
|
|
176
188
|
include_xanoscript: 'false',
|
|
177
189
|
publish: flags.publish ? 'true' : 'false',
|
|
178
190
|
});
|
|
191
|
+
// A reason from someone with workspace:policy update publishes past a blocking publish gate.
|
|
192
|
+
const overrideReason = flags['policy-override']?.trim();
|
|
193
|
+
if (overrideReason)
|
|
194
|
+
queryParams.set('override_reason', overrideReason);
|
|
179
195
|
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function/${functionId}?${queryParams.toString()}`;
|
|
180
196
|
// Update function via API
|
|
181
197
|
try {
|
|
@@ -189,6 +205,9 @@ Name: my_function
|
|
|
189
205
|
method: 'PUT',
|
|
190
206
|
}, flags.verbose, profile.access_token);
|
|
191
207
|
if (!response.ok) {
|
|
208
|
+
const refused = await gateRefusal(response);
|
|
209
|
+
if (refused)
|
|
210
|
+
this.refusePublish(refused, flags, functionId);
|
|
192
211
|
const errorText = await response.text();
|
|
193
212
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
194
213
|
}
|
|
@@ -209,6 +228,8 @@ Name: my_function
|
|
|
209
228
|
}
|
|
210
229
|
}
|
|
211
230
|
catch (error) {
|
|
231
|
+
if (this.refusedByPolicyGate)
|
|
232
|
+
throw error;
|
|
212
233
|
if (error instanceof Error) {
|
|
213
234
|
this.error(`Failed to update function: ${error.message}`);
|
|
214
235
|
}
|
|
@@ -221,14 +242,14 @@ Name: my_function
|
|
|
221
242
|
// Get the EDITOR environment variable
|
|
222
243
|
const editor = process.env.EDITOR || process.env.VISUAL;
|
|
223
244
|
if (!editor) {
|
|
224
|
-
this.error('No editor configured. Please set the EDITOR or VISUAL environment variable.\
|
|
245
|
+
this.error('No editor configured. Please set the EDITOR or VISUAL environment variable.\nExample: export EDITOR=vim');
|
|
225
246
|
}
|
|
226
247
|
// Validate editor executable exists
|
|
227
248
|
try {
|
|
228
249
|
execSync(`which ${editor.split(' ')[0]}`, { stdio: 'ignore' });
|
|
229
250
|
}
|
|
230
251
|
catch {
|
|
231
|
-
this.error(`Editor '${editor}' not found. Please set EDITOR to a valid editor.\
|
|
252
|
+
this.error(`Editor '${editor}' not found. Please set EDITOR to a valid editor.\nExample: export EDITOR=vim`);
|
|
232
253
|
}
|
|
233
254
|
// Read the original file
|
|
234
255
|
let originalContent;
|
|
@@ -321,35 +342,30 @@ Name: my_function
|
|
|
321
342
|
include_xanoscript: 'true',
|
|
322
343
|
});
|
|
323
344
|
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function/${functionId}?${queryParams.toString()}`;
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
345
|
+
const response = await fetch(apiUrl, {
|
|
346
|
+
headers: {
|
|
347
|
+
accept: 'application/json',
|
|
348
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
349
|
+
'User-Agent': buildUserAgent(this.config.version),
|
|
350
|
+
},
|
|
351
|
+
method: 'GET',
|
|
352
|
+
});
|
|
353
|
+
if (!response.ok) {
|
|
354
|
+
const errorText = await response.text();
|
|
355
|
+
throw new Error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
356
|
+
}
|
|
357
|
+
const result = (await response.json());
|
|
358
|
+
// Handle xanoscript as an object with status and value
|
|
359
|
+
if (result.xanoscript) {
|
|
360
|
+
if (result.xanoscript.status === 'ok' && result.xanoscript.value !== undefined) {
|
|
361
|
+
return result.xanoscript.value;
|
|
336
362
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
if (result.xanoscript) {
|
|
340
|
-
if (result.xanoscript.status === 'ok' && result.xanoscript.value !== undefined) {
|
|
341
|
-
return result.xanoscript.value;
|
|
342
|
-
}
|
|
343
|
-
if (typeof result.xanoscript === 'string') {
|
|
344
|
-
return result.xanoscript;
|
|
345
|
-
}
|
|
346
|
-
throw new Error(`Invalid xanoscript format: ${JSON.stringify(result.xanoscript)}`);
|
|
363
|
+
if (typeof result.xanoscript === 'string') {
|
|
364
|
+
return result.xanoscript;
|
|
347
365
|
}
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
catch (error) {
|
|
351
|
-
throw error;
|
|
366
|
+
throw new Error(`Invalid xanoscript format: ${JSON.stringify(result.xanoscript)}`);
|
|
352
367
|
}
|
|
368
|
+
return '';
|
|
353
369
|
}
|
|
354
370
|
async promptForFunctionId(profile, workspaceId) {
|
|
355
371
|
try {
|
|
@@ -365,9 +381,9 @@ Name: my_function
|
|
|
365
381
|
const listUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function?${queryParams.toString()}`;
|
|
366
382
|
const response = await fetch(listUrl, {
|
|
367
383
|
headers: {
|
|
368
|
-
'User-Agent': buildUserAgent(this.config.version),
|
|
369
384
|
accept: 'application/json',
|
|
370
385
|
Authorization: `Bearer ${profile.access_token}`,
|
|
386
|
+
'User-Agent': buildUserAgent(this.config.version),
|
|
371
387
|
},
|
|
372
388
|
method: 'GET',
|
|
373
389
|
});
|
|
@@ -434,4 +450,22 @@ Name: my_function
|
|
|
434
450
|
process.stdin.resume();
|
|
435
451
|
});
|
|
436
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* The live-branch publish gate refused the update: the verdict on stdout (under `-o json`,
|
|
455
|
+
* `{updated: false, message, policy_gate}`), then the platform's message and how to proceed,
|
|
456
|
+
* exiting 2.
|
|
457
|
+
*/
|
|
458
|
+
refusePublish(refused, flags, functionId) {
|
|
459
|
+
const workspace = flags.workspace ? ` -w ${quoted(flags.workspace)}` : '';
|
|
460
|
+
const source = flags.file ? ` -f ${quoted(flags.file)}` : flags.stdin ? ' --stdin' : '';
|
|
461
|
+
const { lines, message } = publishRefusal(refused, {
|
|
462
|
+
json: flags.output === 'json',
|
|
463
|
+
outcome: 'updated',
|
|
464
|
+
rerun: `xano function edit ${quoted(functionId)}${workspace}${source} --policy-override "<why>"`,
|
|
465
|
+
});
|
|
466
|
+
for (const line of lines)
|
|
467
|
+
this.log(line);
|
|
468
|
+
this.refusedByPolicyGate = true;
|
|
469
|
+
this.error(message, { exit: 2 });
|
|
470
|
+
}
|
|
437
471
|
}
|
|
@@ -89,8 +89,7 @@ function yo {
|
|
|
89
89
|
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
90
90
|
}
|
|
91
91
|
// If function_id is not provided, prompt user to select from list
|
|
92
|
-
|
|
93
|
-
functionId = args.function_id ? args.function_id : await this.promptForFunctionId(profile, workspaceId);
|
|
92
|
+
const functionId = args.function_id || (await this.promptForFunctionId(profile, workspaceId));
|
|
94
93
|
// Build query parameters
|
|
95
94
|
// Automatically set include_xanoscript to true if output format is xs
|
|
96
95
|
const includeXanoscript = flags.output === 'xs' ? true : flags.include_xanoscript;
|
|
@@ -178,9 +177,9 @@ function yo {
|
|
|
178
177
|
const listUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function?${queryParams.toString()}`;
|
|
179
178
|
const response = await fetch(listUrl, {
|
|
180
179
|
headers: {
|
|
181
|
-
'User-Agent': buildUserAgent(this.config.version),
|
|
182
180
|
accept: 'application/json',
|
|
183
181
|
Authorization: `Bearer ${profile.access_token}`,
|
|
182
|
+
'User-Agent': buildUserAgent(this.config.version),
|
|
184
183
|
},
|
|
185
184
|
method: 'GET',
|
|
186
185
|
});
|
|
@@ -8,6 +8,7 @@ export default class FunctionRun extends BaseCommand {
|
|
|
8
8
|
static flags: {
|
|
9
9
|
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
data: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
datasource: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
json: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
logs: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
14
|
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -17,6 +17,8 @@ export default class FunctionRun extends BaseCommand {
|
|
|
17
17
|
`$ xano function:run calcScore --json @payload.json --data env=staging`,
|
|
18
18
|
`$ echo '{"email":"jo@x.com"}' | xano function:run calcScore --stdin -o json | jq .result`,
|
|
19
19
|
`$ xano function:run calcScore --branch dev --logs`,
|
|
20
|
+
`$ xano function:run calcScore --datasource test
|
|
21
|
+
# Runs against the 'test' data source instead of 'live'`,
|
|
20
22
|
];
|
|
21
23
|
static flags = {
|
|
22
24
|
...BaseCommand.baseFlags,
|
|
@@ -30,6 +32,10 @@ export default class FunctionRun extends BaseCommand {
|
|
|
30
32
|
multiple: true,
|
|
31
33
|
required: false,
|
|
32
34
|
}),
|
|
35
|
+
datasource: Flags.string({
|
|
36
|
+
description: 'Data source to run against, e.g. test (defaults to live)',
|
|
37
|
+
required: false,
|
|
38
|
+
}),
|
|
33
39
|
json: Flags.string({
|
|
34
40
|
description: "Input as a JSON object: inline, @file.json, or '-' for stdin",
|
|
35
41
|
exclusive: ['stdin'],
|
|
@@ -106,13 +112,21 @@ export default class FunctionRun extends BaseCommand {
|
|
|
106
112
|
const body = { input, name: functionName };
|
|
107
113
|
if (branch)
|
|
108
114
|
body.branch = branch;
|
|
115
|
+
const headers = {
|
|
116
|
+
'accept': 'application/json',
|
|
117
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
118
|
+
'Content-Type': 'application/json',
|
|
119
|
+
};
|
|
120
|
+
// The Metadata API resolves the workspace's tables against the data source
|
|
121
|
+
// named by X-Data-Source (defaulting to `live`), so the run reads and writes
|
|
122
|
+
// the same tables a request carrying that header would. An unknown label is
|
|
123
|
+
// rejected server-side with "Invalid data source."
|
|
124
|
+
const dataSource = flags.datasource?.trim();
|
|
125
|
+
if (dataSource)
|
|
126
|
+
headers['X-Data-Source'] = dataSource;
|
|
109
127
|
const response = await this.verboseFetch(apiUrl, {
|
|
110
128
|
body: JSON.stringify(body),
|
|
111
|
-
headers
|
|
112
|
-
'accept': 'application/json',
|
|
113
|
-
'Authorization': `Bearer ${profile.access_token}`,
|
|
114
|
-
'Content-Type': 'application/json',
|
|
115
|
-
},
|
|
129
|
+
headers,
|
|
116
130
|
method: 'POST',
|
|
117
131
|
}, flags.verbose, profile.access_token);
|
|
118
132
|
if (!response.ok) {
|
|
@@ -153,7 +167,6 @@ export default class FunctionRun extends BaseCommand {
|
|
|
153
167
|
*/
|
|
154
168
|
async fetchFunctionByName(opts) {
|
|
155
169
|
const { branch, name, profile, verbose, workspaceId } = opts;
|
|
156
|
-
// eslint-disable-next-line camelcase -- external Metadata API query param
|
|
157
170
|
const params = new URLSearchParams({ per_page: '100', search: name });
|
|
158
171
|
if (branch)
|
|
159
172
|
params.set('branch', branch);
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
3
|
+
import { policyCatalogueSummary, selectCatalogueCheck } from '../../../utils/policy/catalogue.js';
|
|
4
|
+
import { listItems } from '../../../utils/policy/request.js';
|
|
5
|
+
export default class PolicyCatalogue extends PolicyCommand {
|
|
6
|
+
static description = 'List built-in policy checks and their parameter schemas';
|
|
7
|
+
static examples = ['$ xano policy catalogue -o json', '$ xano policy catalogue --check object.auth_required'];
|
|
8
|
+
static flags = {
|
|
9
|
+
...PolicyCommand.policyFlags,
|
|
10
|
+
check: Flags.string({ description: 'Show only this check id (an unknown id names the closest matches)' }),
|
|
11
|
+
};
|
|
12
|
+
async run() {
|
|
13
|
+
const { flags } = await this.parse(PolicyCatalogue);
|
|
14
|
+
const { request } = this.policyTarget(flags);
|
|
15
|
+
const result = await request('/check');
|
|
16
|
+
if (flags.check) {
|
|
17
|
+
// `--check` narrows both output modes, so `-o json` stays pipeable for one check too.
|
|
18
|
+
const selected = selectCatalogueCheck(listItems(result), flags.check);
|
|
19
|
+
this.log(flags.output === 'json' ? JSON.stringify(selected, null, 2) : policyCatalogueSummary(selected).join('\n'));
|
|
20
|
+
}
|
|
21
|
+
else if (flags.output === 'json') {
|
|
22
|
+
this.log(JSON.stringify(result, null, 2));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
for (const line of policyCatalogueSummary(listItems(result)))
|
|
26
|
+
this.log(line);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -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
|
+
goal: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
key: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
param: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, 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,39 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
3
|
+
import { policyFromGoal } from '../../../utils/policy/goal.js';
|
|
4
|
+
import { listAllPolicies } from '../../../utils/policy/request.js';
|
|
5
|
+
export default class PolicyCreate extends PolicyCommand {
|
|
6
|
+
static description = 'Create an independent policy from a platform goal';
|
|
7
|
+
static examples = [
|
|
8
|
+
'$ xano policy create --goal endpoints_need_login -b dev',
|
|
9
|
+
`$ xano policy create --goal no_pii_in_responses --key DATA-020 --param 'fields=["email","ssn"]'`,
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
...PolicyCommand.policyFlags,
|
|
13
|
+
...PolicyCommand.publishFlags,
|
|
14
|
+
goal: Flags.string({ description: 'Goal id from policy catalogue -o json', required: true }),
|
|
15
|
+
key: Flags.string({ description: 'New policy key (default: the goal key with a free numeric suffix if taken)' }),
|
|
16
|
+
param: Flags.string({ description: 'Parameter override: N.path=JSON, 1-based rule number; path=JSON for a single-rule goal', multiple: 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 goal = catalogue.goals?.find(item => item.id === flags.goal);
|
|
24
|
+
if (!goal)
|
|
25
|
+
this.error(`Unknown goal "${flags.goal}". Run xano policy catalogue -o json to see this instance's goals.`);
|
|
26
|
+
const keys = (await listAllPolicies(request)).map(policy => policy.key);
|
|
27
|
+
// The key is checked against the pattern this instance's catalogue serves.
|
|
28
|
+
const document = policyFromGoal(goal, keys, { key: flags.key, keyRule: catalogue.document?.key, overrides: flags.param });
|
|
29
|
+
const parsed = await this.parseSource(request, { data: document });
|
|
30
|
+
const saved = await request('', 'POST', { data: { source: parsed.source }, ...(flags.message ? { message: flags.message } : {}) });
|
|
31
|
+
if (typeof saved?.id !== 'number')
|
|
32
|
+
this.error('The platform did not return the saved policy. Check xano policy list before retrying.');
|
|
33
|
+
this.logRuleWarnings(parsed);
|
|
34
|
+
if (flags.output === 'json')
|
|
35
|
+
this.log(JSON.stringify(saved, null, 2));
|
|
36
|
+
else
|
|
37
|
+
this.log(`Created ${parsed.policy.key} (Version ${saved.version}) in ${this.where(target)}.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -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. Deleting an active, mandatory policy also needs the workspace:policy update permission.';
|
|
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 === 'mandatory' ? 'Mandatory' : '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
|
+
}
|