@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
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
|
-
import * as fs from 'node:fs';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
2
|
import snakeCase from 'lodash.snakecase';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
5
|
import BaseCommand from '../../../base-command.js';
|
|
6
|
-
import { buildApiGroupFolderResolver, parseDocument } from '../../../utils/document-parser.js';
|
|
6
|
+
import { buildApiGroupFolderResolver, buildChannelServerResolver, parseDocument, policyBaseName, resolveDocumentPath, } from '../../../utils/document-parser.js';
|
|
7
7
|
import { fetchKnowledge, writeKnowledge } from '../../../utils/knowledge-sync.js';
|
|
8
|
+
import { policyListing } from '../../../utils/policy/request.js';
|
|
8
9
|
export default class Pull extends BaseCommand {
|
|
9
10
|
static description = 'Pull a workspace multidoc from the Xano Metadata API and split into individual files';
|
|
10
11
|
static examples = [
|
|
@@ -27,7 +28,7 @@ Pulled 58 documents
|
|
|
27
28
|
...BaseCommand.baseFlags,
|
|
28
29
|
branch: Flags.string({
|
|
29
30
|
char: 'b',
|
|
30
|
-
description:
|
|
31
|
+
description: "Branch name (defaults to profile branch or live; -b '' selects live)",
|
|
31
32
|
required: false,
|
|
32
33
|
}),
|
|
33
34
|
directory: Flags.string({
|
|
@@ -36,14 +37,14 @@ Pulled 58 documents
|
|
|
36
37
|
description: 'Output directory for pulled documents (defaults to current directory)',
|
|
37
38
|
required: false,
|
|
38
39
|
}),
|
|
39
|
-
|
|
40
|
+
draft: Flags.boolean({
|
|
40
41
|
default: false,
|
|
41
|
-
description: 'Include
|
|
42
|
+
description: 'Include draft versions',
|
|
42
43
|
required: false,
|
|
43
44
|
}),
|
|
44
|
-
|
|
45
|
+
env: Flags.boolean({
|
|
45
46
|
default: false,
|
|
46
|
-
description: 'Include
|
|
47
|
+
description: 'Include environment variables',
|
|
47
48
|
required: false,
|
|
48
49
|
}),
|
|
49
50
|
records: Flags.boolean({
|
|
@@ -73,8 +74,8 @@ Pulled 58 documents
|
|
|
73
74
|
` 1. Provide it as a flag: xano workspace pull -w <workspace_id>\n` +
|
|
74
75
|
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
75
76
|
}
|
|
76
|
-
// Determine branch from flag or profile
|
|
77
|
-
const branch = flags.branch
|
|
77
|
+
// Determine branch from flag or profile; `-b ''` selects live whatever the profile's branch, as in push.
|
|
78
|
+
const branch = flags.branch ?? profile.branch ?? '';
|
|
78
79
|
// Build query parameters
|
|
79
80
|
const queryParams = new URLSearchParams({
|
|
80
81
|
branch,
|
|
@@ -123,107 +124,31 @@ Pulled 58 documents
|
|
|
123
124
|
documents.push(parsed);
|
|
124
125
|
}
|
|
125
126
|
}
|
|
127
|
+
// Resolve the output directory
|
|
128
|
+
const outputDir = path.resolve(flags.directory);
|
|
129
|
+
const clashing = await this.policyClashes(documents, outputDir, () => policyListing({ logToStderr: (message) => this.logToStderr(message), verboseFetch: (...args) => this.verboseFetch(...args) }, { branch, profile, verbose: flags.verbose, workspace: workspaceId }));
|
|
126
130
|
if (documents.length === 0) {
|
|
127
131
|
this.log('No documents found in response');
|
|
128
132
|
return;
|
|
129
133
|
}
|
|
130
|
-
// Resolve the output directory
|
|
131
|
-
const outputDir = path.resolve(flags.directory);
|
|
132
134
|
// Create the output directory if it doesn't exist
|
|
133
135
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
134
136
|
// Resolve api_group names to unique folder names, disambiguating collisions
|
|
135
137
|
// where different names produce the same snakeCase (e.g., "Authentication" vs "authentication")
|
|
136
138
|
const getApiGroupFolder = buildApiGroupFolderResolver(documents, snakeCase);
|
|
139
|
+
// Resolve a realtime v2 channel path -> owning realtime_server name, so
|
|
140
|
+
// messages can nest under their channel's server (see resolver docs).
|
|
141
|
+
const getChannelServer = buildChannelServerResolver(documents);
|
|
137
142
|
// Track filenames per type to handle duplicates
|
|
138
143
|
const filenameCounters = new Map();
|
|
139
144
|
let writtenCount = 0;
|
|
140
|
-
for (const doc of documents) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
else if (doc.type === 'workspace_trigger') {
|
|
149
|
-
// workspace_trigger → workspace/trigger/{name}.xs
|
|
150
|
-
typeDir = path.join(outputDir, 'workspace', 'trigger');
|
|
151
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
152
|
-
}
|
|
153
|
-
else if (doc.type === 'error_trigger') {
|
|
154
|
-
// error_trigger → workspace/trigger/{name}.xs (singleton, colocated with workspace triggers)
|
|
155
|
-
typeDir = path.join(outputDir, 'workspace', 'trigger');
|
|
156
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
157
|
-
}
|
|
158
|
-
else if (doc.type === 'agent') {
|
|
159
|
-
// agent → ai/agent/{name}.xs
|
|
160
|
-
typeDir = path.join(outputDir, 'ai', 'agent');
|
|
161
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
162
|
-
}
|
|
163
|
-
else if (doc.type === 'mcp_server') {
|
|
164
|
-
// mcp_server → ai/mcp_server/{name}.xs
|
|
165
|
-
typeDir = path.join(outputDir, 'ai', 'mcp_server');
|
|
166
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
167
|
-
}
|
|
168
|
-
else if (doc.type === 'tool') {
|
|
169
|
-
// tool → ai/tool/{name}.xs
|
|
170
|
-
typeDir = path.join(outputDir, 'ai', 'tool');
|
|
171
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
172
|
-
}
|
|
173
|
-
else if (doc.type === 'agent_trigger') {
|
|
174
|
-
// agent_trigger → ai/agent/trigger/{name}.xs
|
|
175
|
-
typeDir = path.join(outputDir, 'ai', 'agent', 'trigger');
|
|
176
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
177
|
-
}
|
|
178
|
-
else if (doc.type === 'mcp_server_trigger') {
|
|
179
|
-
// mcp_server_trigger → ai/mcp_server/trigger/{name}.xs
|
|
180
|
-
typeDir = path.join(outputDir, 'ai', 'mcp_server', 'trigger');
|
|
181
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
182
|
-
}
|
|
183
|
-
else if (doc.type === 'table_trigger') {
|
|
184
|
-
// table_trigger → table/trigger/{name}.xs
|
|
185
|
-
typeDir = path.join(outputDir, 'table', 'trigger');
|
|
186
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
187
|
-
}
|
|
188
|
-
else if (doc.type === 'realtime_channel') {
|
|
189
|
-
// realtime_channel → realtime/channel/{name}.xs
|
|
190
|
-
typeDir = path.join(outputDir, 'realtime', 'channel');
|
|
191
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
192
|
-
}
|
|
193
|
-
else if (doc.type === 'realtime_trigger') {
|
|
194
|
-
// realtime_trigger → realtime/trigger/{name}.xs
|
|
195
|
-
typeDir = path.join(outputDir, 'realtime', 'trigger');
|
|
196
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
197
|
-
}
|
|
198
|
-
else if (doc.type === 'api_group') {
|
|
199
|
-
// api_group "test" → api/{resolved_folder}/{name}.xs
|
|
200
|
-
const groupFolder = getApiGroupFolder(doc.name);
|
|
201
|
-
typeDir = path.join(outputDir, 'api', groupFolder);
|
|
202
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
203
|
-
}
|
|
204
|
-
else if (doc.type === 'query' && doc.apiGroup) {
|
|
205
|
-
// query in group "test" → api/{resolved_folder}/{query_name}.xs
|
|
206
|
-
const groupFolder = getApiGroupFolder(doc.apiGroup);
|
|
207
|
-
const nameParts = doc.name.split('/');
|
|
208
|
-
const leafName = nameParts.pop();
|
|
209
|
-
const folderParts = nameParts.map((part) => snakeCase(part));
|
|
210
|
-
typeDir = path.join(outputDir, 'api', groupFolder, ...folderParts);
|
|
211
|
-
baseName = this.sanitizeFilename(leafName);
|
|
212
|
-
if (doc.verb) {
|
|
213
|
-
baseName = `${baseName}_${doc.verb}`;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
// Default: split folder path from name
|
|
218
|
-
const nameParts = doc.name.split('/');
|
|
219
|
-
const leafName = nameParts.pop();
|
|
220
|
-
const folderParts = nameParts.map((part) => snakeCase(part));
|
|
221
|
-
typeDir = path.join(outputDir, doc.type, ...folderParts);
|
|
222
|
-
baseName = this.sanitizeFilename(leafName);
|
|
223
|
-
if (doc.verb) {
|
|
224
|
-
baseName = `${baseName}_${doc.verb}`;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
145
|
+
for (const doc of documents.filter((document) => !clashing.has(document))) {
|
|
146
|
+
const { baseName, typeDir } = resolveDocumentPath(doc, outputDir, {
|
|
147
|
+
getApiGroupFolder,
|
|
148
|
+
getChannelServer,
|
|
149
|
+
join: path.join,
|
|
150
|
+
snakeCase,
|
|
151
|
+
});
|
|
227
152
|
fs.mkdirSync(typeDir, { recursive: true });
|
|
228
153
|
// Track duplicates per directory
|
|
229
154
|
const dirKey = path.relative(outputDir, typeDir);
|
|
@@ -234,8 +159,7 @@ Pulled 58 documents
|
|
|
234
159
|
const count = typeCounters.get(baseName) || 0;
|
|
235
160
|
typeCounters.set(baseName, count + 1);
|
|
236
161
|
// Append numeric suffix for duplicates
|
|
237
|
-
|
|
238
|
-
filename = count === 0 ? `${baseName}.xs` : `${baseName}_${count + 1}.xs`;
|
|
162
|
+
const filename = count === 0 ? `${baseName}.xs` : `${baseName}_${count + 1}.xs`;
|
|
239
163
|
const filePath = path.join(typeDir, filename);
|
|
240
164
|
fs.writeFileSync(filePath, doc.content, 'utf8');
|
|
241
165
|
writtenCount++;
|
|
@@ -251,13 +175,66 @@ Pulled 58 documents
|
|
|
251
175
|
if (knowledgeCount > 0)
|
|
252
176
|
parts.push(`${knowledgeCount} knowledge file${knowledgeCount === 1 ? '' : 's'}`);
|
|
253
177
|
this.log(`Pulled ${parts.join(' + ')} to ${flags.directory}`);
|
|
178
|
+
// Policy files state the rules; the skill the instance generates tells an agent how to follow them.
|
|
179
|
+
// Only a pull that carried policy files says so: an instance with the Policies feature off exports
|
|
180
|
+
// none, and its skill route answers 403.
|
|
181
|
+
if (documents.some((doc) => doc.type === 'policy')) {
|
|
182
|
+
this.log('Run `xano skills pull` to install the policies skill for your coding agent.');
|
|
183
|
+
}
|
|
254
184
|
}
|
|
255
185
|
/**
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
186
|
+
* Policy keys are unique on the server without regard to case, but an older export or a local file
|
|
187
|
+
* can still differ only in case, and file names are not case-sensitive on every checkout. The
|
|
188
|
+
* policies whose file name differs only in case from another exported policy, or from a local file
|
|
189
|
+
* it would overwrite, are left out with a warning; everything else is written. Local policy files
|
|
190
|
+
* absent from the export are kept. They are reported as stale only when this credential reads the
|
|
191
|
+
* branch's policies, because an export leaves out the policies its credential cannot read, and
|
|
192
|
+
* every policy while the Policies feature is off.
|
|
259
193
|
*/
|
|
260
|
-
|
|
261
|
-
|
|
194
|
+
async policyClashes(documents, outputDir, readsPolicies) {
|
|
195
|
+
const exported = new Map();
|
|
196
|
+
for (const doc of documents.filter(doc => doc.type === 'policy')) {
|
|
197
|
+
const filename = `${policyBaseName(doc.name)}.xs`;
|
|
198
|
+
exported.set(filename.toLowerCase(), [...(exported.get(filename.toLowerCase()) ?? []), filename]);
|
|
199
|
+
}
|
|
200
|
+
const clashes = new Map();
|
|
201
|
+
for (const [normalized, names] of exported) {
|
|
202
|
+
if (names.length > 1)
|
|
203
|
+
clashes.set(normalized, names.map(name => `policies/${name}`).join(' and '));
|
|
204
|
+
}
|
|
205
|
+
const policyDir = path.join(outputDir, 'policies');
|
|
206
|
+
const localFiles = fs.existsSync(policyDir)
|
|
207
|
+
? fs.readdirSync(policyDir, { withFileTypes: true })
|
|
208
|
+
.filter(entry => entry.isFile() && entry.name.toLowerCase().endsWith('.xs'))
|
|
209
|
+
.map(entry => entry.name)
|
|
210
|
+
.sort()
|
|
211
|
+
: [];
|
|
212
|
+
const stale = [];
|
|
213
|
+
for (const filename of localFiles) {
|
|
214
|
+
const names = exported.get(filename.toLowerCase());
|
|
215
|
+
if (!names)
|
|
216
|
+
stale.push(`policies/${filename}`);
|
|
217
|
+
else if (!names.includes(filename) && !clashes.has(filename.toLowerCase())) {
|
|
218
|
+
clashes.set(filename.toLowerCase(), `policies/${names[0]} (local policies/${filename})`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (clashes.size > 0) {
|
|
222
|
+
this.warn(`Policy files that differ only in case are left out of this pull; everything else is written:\n ${[...clashes.values()].join('\n ')}\n` +
|
|
223
|
+
'Rename one of the policies, or move the local file aside, then pull again.');
|
|
224
|
+
}
|
|
225
|
+
if (stale.length > 0) {
|
|
226
|
+
// An export that carries a policy was made by a credential that reads them.
|
|
227
|
+
const listing = exported.size > 0 ? 'listed' : await readsPolicies();
|
|
228
|
+
if (listing === 'listed') {
|
|
229
|
+
this.warn(`Stale local policy files are absent from this export and were kept:\n ${stale.join('\n ')}\nReview them before pushing; a push can publish these policies again.`);
|
|
230
|
+
}
|
|
231
|
+
else if (listing === 'feature_off') {
|
|
232
|
+
this.log('Local policy files were kept: Policies are not enabled on this instance, so the export carries none.');
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
this.log(`Local policy files were kept: this credential cannot list this workspace's policies, and the export carries none.`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return new Set(documents.filter(doc => doc.type === 'policy' && clashes.has(`${doc.name}.xs`.toLowerCase())));
|
|
262
239
|
}
|
|
263
240
|
}
|
|
@@ -12,6 +12,9 @@ export default class Push extends BaseCommand {
|
|
|
12
12
|
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
13
|
guids: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
14
|
include: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
message: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
'policy-override': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
18
|
records: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
19
|
sync: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
20
|
transaction: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -21,5 +24,21 @@ export default class Push extends BaseCommand {
|
|
|
21
24
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
25
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
23
26
|
};
|
|
27
|
+
protected catch(error: Error & {
|
|
28
|
+
oclif?: {
|
|
29
|
+
exit?: number;
|
|
30
|
+
};
|
|
31
|
+
}): Promise<void>;
|
|
24
32
|
run(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* A failure after the import landed. The import stands, so its policy feedback is reported as for
|
|
35
|
+
* any push and a blocking finding still exits 2; otherwise the failure exits 1. Under `-o json`
|
|
36
|
+
* stdout holds the import's document with the failure as `error`.
|
|
37
|
+
*/
|
|
38
|
+
private catchAfterImport;
|
|
39
|
+
/**
|
|
40
|
+
* What happened to the policy documents, then the policy check the import answered with. When the
|
|
41
|
+
* platform left the policy files out, its notice says so instead of a count of what was sent.
|
|
42
|
+
*/
|
|
43
|
+
private reportPolicyFeedback;
|
|
25
44
|
}
|
|
@@ -2,7 +2,28 @@ import { Flags } from '@oclif/core';
|
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../base-command.js';
|
|
5
|
-
import {
|
|
5
|
+
import { parseDocument } from '../../../utils/document-parser.js';
|
|
6
|
+
import { executePush, FailedAfterImportError, } from '../../../utils/multidoc-push.js';
|
|
7
|
+
import { policyCodeGuidance } from '../../../utils/policy/errors.js';
|
|
8
|
+
import { policiesSkippedNotice, policyCheckWarning, policyDocumentSummary, policyExitCode, policySummary, pushEvidence, } from '../../../utils/policy/feedback.js';
|
|
9
|
+
import { BLANK_POLICY_OVERRIDE, blankPolicyOverride } from '../../../utils/policy/gate.js';
|
|
10
|
+
import { policyFilePushGuidance } from '../../../utils/policy/permission.js';
|
|
11
|
+
import { PushPolicyGateError } from '../../../utils/policy/push-gate.js';
|
|
12
|
+
/** The `-o json` document for a push whose import ran: the import response and what was sent. */
|
|
13
|
+
function importDocument(result) {
|
|
14
|
+
return { ...result.response, documents: result.sent.length, imported: true, knowledge: result.knowledge };
|
|
15
|
+
}
|
|
16
|
+
/** The policy check the import answered with, if any. */
|
|
17
|
+
function policyCheck(result) {
|
|
18
|
+
return result.response?.policy_check;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The platform's one notice that it left the push's policy files out (the Policies feature is off):
|
|
22
|
+
* the import's, else its preview's, since a partial push may not send the files the preview named.
|
|
23
|
+
*/
|
|
24
|
+
function policiesSkipped(result) {
|
|
25
|
+
return policiesSkippedNotice(result.response) ?? policiesSkippedNotice(result.preview);
|
|
26
|
+
}
|
|
6
27
|
export default class Push extends BaseCommand {
|
|
7
28
|
static description = '[IMPORTANT] ALWAYS run --dry-run first and show the user the output before pushing. Push local documents to a workspace. By default, only changed files are pushed (partial mode). Use --sync to push all files. Shows a preview of changes before pushing unless --force is specified. Use --dry-run to preview only.';
|
|
8
29
|
static examples = [
|
|
@@ -56,6 +77,9 @@ Push functions but exclude test files
|
|
|
56
77
|
`,
|
|
57
78
|
`$ xano workspace push -i "knowledge/**"
|
|
58
79
|
Push only knowledge files (agents.md / skills / docs)
|
|
80
|
+
`,
|
|
81
|
+
`$ xano workspace push -m "Tightened the auth policies"
|
|
82
|
+
Label the Version History entry of each policy this push changes; other objects get no message
|
|
59
83
|
`,
|
|
60
84
|
`$ xano workspace push --sync --delete
|
|
61
85
|
Full sync including knowledge files; removes server objects not present locally
|
|
@@ -65,7 +89,7 @@ Full sync including knowledge files; removes server objects not present locally
|
|
|
65
89
|
...BaseCommand.baseFlags,
|
|
66
90
|
branch: Flags.string({
|
|
67
91
|
char: 'b',
|
|
68
|
-
description:
|
|
92
|
+
description: "Branch name (defaults to profile branch or live; -b '' selects live)",
|
|
69
93
|
required: false,
|
|
70
94
|
}),
|
|
71
95
|
delete: Flags.boolean({
|
|
@@ -112,6 +136,19 @@ Full sync including knowledge files; removes server objects not present locally
|
|
|
112
136
|
multiple: true,
|
|
113
137
|
required: false,
|
|
114
138
|
}),
|
|
139
|
+
message: Flags.string({
|
|
140
|
+
char: 'm',
|
|
141
|
+
description: 'Labels the Version History entry of each policy this push changes; other objects get no message',
|
|
142
|
+
required: false,
|
|
143
|
+
}),
|
|
144
|
+
output: Flags.string({
|
|
145
|
+
char: 'o',
|
|
146
|
+
default: 'summary',
|
|
147
|
+
description: 'Output format; JSON retains the complete import and policy feedback',
|
|
148
|
+
options: ['summary', 'json'],
|
|
149
|
+
required: false,
|
|
150
|
+
}),
|
|
151
|
+
'policy-override': Flags.string({ description: 'Override a blocking live-branch policy gate with an audited reason (requires workspace:policy update)' }),
|
|
115
152
|
records: Flags.boolean({
|
|
116
153
|
default: false,
|
|
117
154
|
description: '[CRITICAL] STOP and ALWAYS run --dry-run first to show the user a preview before pushing live table records. Includes table records in import.',
|
|
@@ -139,8 +176,20 @@ Full sync including knowledge files; removes server objects not present locally
|
|
|
139
176
|
required: false,
|
|
140
177
|
}),
|
|
141
178
|
};
|
|
179
|
+
async catch(error) {
|
|
180
|
+
if (error instanceof PushPolicyGateError) {
|
|
181
|
+
if (this.isJsonOutput())
|
|
182
|
+
this.log(JSON.stringify({ imported: false, refused: error.payload }, null, 2));
|
|
183
|
+
this.error(error.message, { exit: 2 });
|
|
184
|
+
}
|
|
185
|
+
if (error instanceof FailedAfterImportError)
|
|
186
|
+
return this.catchAfterImport(error);
|
|
187
|
+
return this.catchAsOperational(error);
|
|
188
|
+
}
|
|
142
189
|
async run() {
|
|
143
190
|
const { flags } = await this.parse(Push);
|
|
191
|
+
if (blankPolicyOverride(flags['policy-override']))
|
|
192
|
+
this.error(BLANK_POLICY_OVERRIDE);
|
|
144
193
|
const { profile, profileName } = this.resolveProfile(flags);
|
|
145
194
|
// Determine workspace_id from flag or profile
|
|
146
195
|
let workspaceId;
|
|
@@ -162,12 +211,23 @@ Full sync including knowledge files; removes server objects not present locally
|
|
|
162
211
|
if (!fs.statSync(inputDir).isDirectory()) {
|
|
163
212
|
this.error(`Not a directory: ${inputDir}`);
|
|
164
213
|
}
|
|
165
|
-
const branch = flags.branch
|
|
214
|
+
const branch = flags.branch ?? profile.branch ?? '';
|
|
166
215
|
const baseUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}`;
|
|
216
|
+
const json = flags.output === 'json';
|
|
217
|
+
// Only the import writes Version History entries, so only the import carries the message.
|
|
218
|
+
const message = flags.message?.trim();
|
|
167
219
|
const target = {
|
|
168
220
|
buildDryRunUrl: (params) => `${baseUrl}/multidoc/dry-run?${params.toString()}`,
|
|
169
|
-
buildPushUrl
|
|
221
|
+
buildPushUrl(params) {
|
|
222
|
+
const query = new URLSearchParams(params);
|
|
223
|
+
if (message)
|
|
224
|
+
query.set('message', message);
|
|
225
|
+
if (flags['policy-override']?.trim())
|
|
226
|
+
query.set('override_reason', flags['policy-override'].trim());
|
|
227
|
+
return `${baseUrl}/multidoc?${query.toString()}`;
|
|
228
|
+
},
|
|
170
229
|
cliVersion: this.config.version,
|
|
230
|
+
explainRefusal: (status, payload) => (status === 403 ? policyFilePushGuidance(payload) : policyCodeGuidance(payload) || undefined),
|
|
171
231
|
instanceOrigin: profile.instance_origin,
|
|
172
232
|
label: `workspace ${workspaceId}`,
|
|
173
233
|
supportsBranches: true,
|
|
@@ -187,7 +247,7 @@ Full sync including knowledge files; removes server objects not present locally
|
|
|
187
247
|
truncate: flags.truncate,
|
|
188
248
|
verbose: flags.verbose,
|
|
189
249
|
};
|
|
190
|
-
await executePush({
|
|
250
|
+
const result = await executePush({
|
|
191
251
|
accessToken: profile.access_token,
|
|
192
252
|
branch,
|
|
193
253
|
command: this,
|
|
@@ -196,7 +256,66 @@ Full sync including knowledge files; removes server objects not present locally
|
|
|
196
256
|
listUrl: () => `${baseUrl}/knowledge/sync`,
|
|
197
257
|
rootDir: inputDir,
|
|
198
258
|
},
|
|
259
|
+
// Under `-o json` stdout carries one JSON document, so progress goes to stderr.
|
|
260
|
+
log: json ? this.logToStderr.bind(this) : undefined,
|
|
199
261
|
verboseFetch: this.verboseFetch.bind(this),
|
|
200
262
|
}, target, pushFlags);
|
|
263
|
+
if (json) {
|
|
264
|
+
this.log(JSON.stringify(result.stopped
|
|
265
|
+
? { imported: false, preview: result.preview, reason: result.stopped }
|
|
266
|
+
: importDocument(result), null, 2));
|
|
267
|
+
}
|
|
268
|
+
// Policy feedback describes the multidoc import, so a push that imported none has none. Its
|
|
269
|
+
// preview may still say that the policy files are left out.
|
|
270
|
+
if (result.response) {
|
|
271
|
+
this.reportPolicyFeedback(result, json);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
const skipped = policiesSkipped(result);
|
|
275
|
+
if (skipped)
|
|
276
|
+
this.warn(skipped);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* A failure after the import landed. The import stands, so its policy feedback is reported as for
|
|
281
|
+
* any push and a blocking finding still exits 2; otherwise the failure exits 1. Under `-o json`
|
|
282
|
+
* stdout holds the import's document with the failure as `error`.
|
|
283
|
+
*/
|
|
284
|
+
catchAfterImport(error) {
|
|
285
|
+
const json = this.isJsonOutput();
|
|
286
|
+
const exit = policyExitCode(policyCheck(error.imported)) || 1;
|
|
287
|
+
if (json)
|
|
288
|
+
this.log(JSON.stringify({ ...importDocument(error.imported), error: { exit, message: error.message } }, null, 2));
|
|
289
|
+
this.reportPolicyFeedback(error.imported, json);
|
|
290
|
+
this.error(error, { exit });
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* What happened to the policy documents, then the policy check the import answered with. When the
|
|
294
|
+
* platform left the policy files out, its notice says so instead of a count of what was sent.
|
|
295
|
+
*/
|
|
296
|
+
reportPolicyFeedback(result, json) {
|
|
297
|
+
const check = policyCheck(result);
|
|
298
|
+
const skipped = policiesSkipped(result);
|
|
299
|
+
if (skipped)
|
|
300
|
+
this.warn(skipped);
|
|
301
|
+
if (!json) {
|
|
302
|
+
if (!skipped) {
|
|
303
|
+
const sentPolicies = result.sent.filter((entry) => parseDocument(entry.content)?.type === 'policy').length;
|
|
304
|
+
for (const line of policyDocumentSummary(result.preview, sentPolicies))
|
|
305
|
+
this.log(line);
|
|
306
|
+
}
|
|
307
|
+
for (const line of policySummary(check, pushEvidence(check)))
|
|
308
|
+
this.log(line);
|
|
309
|
+
}
|
|
310
|
+
const warning = policyCheckWarning(check);
|
|
311
|
+
if (warning)
|
|
312
|
+
this.warn(warning);
|
|
313
|
+
const code = policyExitCode(check);
|
|
314
|
+
if (code) {
|
|
315
|
+
process.exitCode = code;
|
|
316
|
+
if (!json)
|
|
317
|
+
this.log('Next: `xano policy status --run-detail` for the current standing, or `xano policy runs` for this run.');
|
|
318
|
+
this.warn('Workspace import completed with blocking policy findings; the imported changes were not rolled back.');
|
|
319
|
+
}
|
|
201
320
|
}
|
|
202
321
|
}
|
package/dist/help.js
CHANGED
|
@@ -45,7 +45,9 @@ export default class Help extends BaseHelp {
|
|
|
45
45
|
if (this.config.topicSeparator !== ':')
|
|
46
46
|
c.id = c.id.replaceAll(':', this.config.topicSeparator);
|
|
47
47
|
const summary = this.summary(c);
|
|
48
|
-
|
|
48
|
+
// Strip ANSI colour codes from the summary so the help table aligns.
|
|
49
|
+
// eslint-disable-next-line no-control-regex -- matching the escape char is the point
|
|
50
|
+
return [c.id, summary ? summary.replaceAll(/\u001B\[\d+m/g, '') : ''];
|
|
49
51
|
});
|
|
50
52
|
// Only add promoted commands at the root level, not within a specific topic
|
|
51
53
|
if (isRootHelp) {
|
|
@@ -59,6 +61,6 @@ export default class Help extends BaseHelp {
|
|
|
59
61
|
spacer: '\n',
|
|
60
62
|
stripAnsi: this.opts.stripAnsi,
|
|
61
63
|
});
|
|
62
|
-
return this.section('COMMANDS', body + `\n\n\
|
|
64
|
+
return this.section('COMMANDS', body + `\n\n\u001B[2mSee xano <topic> --help for all commands in a topic.\u001B[0m`);
|
|
63
65
|
}
|
|
64
66
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Policy, PolicyRun } from './utils/policy/types.js';
|
|
2
|
+
import BaseCommand from './base-command.js';
|
|
3
|
+
import { type PolicyRequest } from './utils/policy/request.js';
|
|
4
|
+
/** One rule's warnings from the parse route: its 0-based index, id and sentences. */
|
|
5
|
+
export interface RuleWarnings {
|
|
6
|
+
rule: number;
|
|
7
|
+
rule_id: string;
|
|
8
|
+
warnings: string[];
|
|
9
|
+
}
|
|
10
|
+
/** What the parse route answers. */
|
|
11
|
+
export interface ParsedPolicy {
|
|
12
|
+
policy: Policy;
|
|
13
|
+
rule_warnings: RuleWarnings[];
|
|
14
|
+
source: string;
|
|
15
|
+
}
|
|
16
|
+
/** The workspace and branch a policy command acts on, and requests bound to them. */
|
|
17
|
+
export interface PolicyTarget {
|
|
18
|
+
branch: string;
|
|
19
|
+
request: PolicyRequest;
|
|
20
|
+
workspace: string;
|
|
21
|
+
}
|
|
22
|
+
interface TargetFlags {
|
|
23
|
+
branch?: string;
|
|
24
|
+
config?: string;
|
|
25
|
+
profile?: string;
|
|
26
|
+
verbose: boolean;
|
|
27
|
+
workspace?: string;
|
|
28
|
+
}
|
|
29
|
+
/** Shared flags, request helper and output for the `policy *` commands and `skills pull`. */
|
|
30
|
+
export default abstract class PolicyCommand extends BaseCommand {
|
|
31
|
+
static policyFlags: {
|
|
32
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
33
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
34
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
35
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
36
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
37
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
38
|
+
};
|
|
39
|
+
/** `publish -m`: the message on the Version History entry the save creates. */
|
|
40
|
+
static publishFlags: {
|
|
41
|
+
message: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
42
|
+
};
|
|
43
|
+
/** One positional, as CLAUDE.md requires: the file `--file` would otherwise name. */
|
|
44
|
+
static sourceArgs: {
|
|
45
|
+
file: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
46
|
+
};
|
|
47
|
+
static sourceFlags: {
|
|
48
|
+
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
49
|
+
stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
50
|
+
};
|
|
51
|
+
/** Any failure exits 1; an error that is not the CLI's own names the command it broke. */
|
|
52
|
+
protected catch(error: Error & {
|
|
53
|
+
oclif?: {
|
|
54
|
+
exit?: number;
|
|
55
|
+
};
|
|
56
|
+
}): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Print each rule's warnings (a name, tag or value that makes the rule check nothing) to stderr,
|
|
59
|
+
* so piped source stays clean; nothing when there are none.
|
|
60
|
+
*/
|
|
61
|
+
protected logRuleWarnings(parsed: ParsedPolicy): void;
|
|
62
|
+
/** Print a run's recorded detail, or say it evaluated no policy. */
|
|
63
|
+
protected logRunDetail(run: PolicyRun): void;
|
|
64
|
+
/**
|
|
65
|
+
* Ask the parse route for the policy, its canonical source and each rule's warnings against the
|
|
66
|
+
* target branch, from XanoScript (`{source}`) or a policy document (`{data}`); the policy and source
|
|
67
|
+
* must come back.
|
|
68
|
+
*/
|
|
69
|
+
protected parseSource(request: PolicyRequest, input: {
|
|
70
|
+
data: unknown;
|
|
71
|
+
} | {
|
|
72
|
+
source: string;
|
|
73
|
+
}): Promise<ParsedPolicy>;
|
|
74
|
+
/**
|
|
75
|
+
* The workspace and branch from the flags or the profile, and a request helper bound to them for
|
|
76
|
+
* a route gated by `workspace:policy`: `/policy` unless another is named.
|
|
77
|
+
*/
|
|
78
|
+
protected policyTarget(flags: TargetFlags, route?: Pick<import("./utils/policy/request.js").PolicyRequestRoute, "path" | "label">): PolicyTarget;
|
|
79
|
+
/** Policy source from the positional, `--file` or `--stdin`; a file named twice must be named the same. */
|
|
80
|
+
protected readSource(flags: {
|
|
81
|
+
file?: string;
|
|
82
|
+
stdin?: boolean;
|
|
83
|
+
}, positional?: string): string;
|
|
84
|
+
/** `workspace 3 (feature)`, or just `workspace 3` for live. */
|
|
85
|
+
protected where({ branch, workspace }: {
|
|
86
|
+
branch: string;
|
|
87
|
+
workspace: string;
|
|
88
|
+
}): string;
|
|
89
|
+
}
|
|
90
|
+
export {};
|