@xano/cli 1.0.6-beta.3 → 1.0.6-beta.6
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 +95 -4
- package/dist/commands/branch/list/index.js +5 -1
- package/dist/commands/ephemeral/list/index.js +2 -1
- package/dist/commands/ephemeral/pull/index.d.ts +0 -6
- package/dist/commands/ephemeral/pull/index.js +10 -95
- package/dist/commands/ephemeral/static_host/build/list/index.d.ts +2 -3
- package/dist/commands/ephemeral/static_host/build/list/index.js +19 -40
- package/dist/commands/ephemeral/static_host/list/index.d.ts +2 -3
- package/dist/commands/ephemeral/static_host/list/index.js +20 -40
- package/dist/commands/function/list/index.d.ts +4 -4
- package/dist/commands/function/list/index.js +30 -38
- package/dist/commands/knowledge/list/index.js +2 -1
- package/dist/commands/platform/list/index.js +13 -14
- package/dist/commands/release/list/index.js +13 -14
- package/dist/commands/release/pull/index.d.ts +0 -6
- package/dist/commands/release/pull/index.js +10 -95
- package/dist/commands/sandbox/env/list/index.js +4 -0
- package/dist/commands/sandbox/pull/index.d.ts +0 -1
- package/dist/commands/sandbox/pull/index.js +10 -77
- package/dist/commands/sandbox/unit_test/list/index.d.ts +2 -0
- package/dist/commands/sandbox/unit_test/list/index.js +9 -14
- package/dist/commands/sandbox/unit_test/run_all/index.d.ts +1 -0
- package/dist/commands/sandbox/unit_test/run_all/index.js +67 -37
- package/dist/commands/sandbox/workflow_test/list/index.d.ts +2 -0
- package/dist/commands/sandbox/workflow_test/list/index.js +9 -14
- package/dist/commands/sandbox/workflow_test/run_all/index.d.ts +1 -0
- package/dist/commands/sandbox/workflow_test/run_all/index.js +65 -35
- package/dist/commands/static_host/build/list/index.d.ts +2 -3
- package/dist/commands/static_host/build/list/index.js +19 -40
- package/dist/commands/static_host/build/pull/index.js +12 -9
- package/dist/commands/static_host/list/index.d.ts +2 -3
- package/dist/commands/static_host/list/index.js +20 -40
- package/dist/commands/static_host/migrate/index.js +14 -10
- package/dist/commands/tenant/backup/list/index.js +13 -14
- package/dist/commands/tenant/cluster/list/index.js +9 -10
- package/dist/commands/tenant/env/list/index.js +4 -0
- package/dist/commands/tenant/list/index.js +19 -20
- package/dist/commands/tenant/pull/index.d.ts +0 -6
- package/dist/commands/tenant/pull/index.js +7 -142
- package/dist/commands/tenant/snapshot/list/index.js +5 -1
- package/dist/commands/tenant/unit_test/list/index.d.ts +4 -2
- package/dist/commands/tenant/unit_test/list/index.js +9 -14
- package/dist/commands/tenant/unit_test/run_all/index.d.ts +1 -0
- package/dist/commands/tenant/unit_test/run_all/index.js +67 -37
- package/dist/commands/tenant/workflow_test/list/index.d.ts +4 -2
- package/dist/commands/tenant/workflow_test/list/index.js +9 -14
- package/dist/commands/tenant/workflow_test/run_all/index.d.ts +1 -0
- package/dist/commands/tenant/workflow_test/run_all/index.js +65 -35
- package/dist/commands/unit_test/list/index.d.ts +3 -1
- package/dist/commands/unit_test/list/index.js +9 -14
- package/dist/commands/unit_test/run_all/index.d.ts +1 -0
- package/dist/commands/unit_test/run_all/index.js +68 -40
- package/dist/commands/workflow_test/list/index.d.ts +3 -1
- package/dist/commands/workflow_test/list/index.js +10 -13
- package/dist/commands/workflow_test/run_all/index.d.ts +1 -0
- package/dist/commands/workflow_test/run_all/index.js +67 -36
- package/dist/commands/workspace/list/index.js +7 -13
- package/dist/commands/workspace/pull/index.d.ts +0 -6
- package/dist/commands/workspace/pull/index.js +7 -144
- package/dist/utils/concurrency.d.ts +29 -0
- package/dist/utils/concurrency.js +54 -0
- package/dist/utils/document-parser.d.ts +39 -0
- package/dist/utils/document-parser.js +139 -0
- package/dist/utils/paging.d.ts +141 -0
- package/dist/utils/paging.js +194 -0
- package/oclif.manifest.json +2950 -2824
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
import { buildPagingJson, formatPagingFooter, normalizeListResponse, pagingFlags } from '../../../utils/paging.js';
|
|
3
4
|
export default class FunctionList extends BaseCommand {
|
|
4
5
|
static args = {};
|
|
5
6
|
static description = 'List all functions in a workspace from the Xano Metadata API';
|
|
@@ -16,20 +17,30 @@ Available functions:
|
|
|
16
17
|
- another-function (ID: 2)
|
|
17
18
|
`,
|
|
18
19
|
`$ xano function:list -w 40 --output json
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
{
|
|
21
|
+
"count": 1,
|
|
22
|
+
"page": 1,
|
|
23
|
+
"per_page": 50,
|
|
24
|
+
"next_page": 2,
|
|
25
|
+
"items": [
|
|
26
|
+
{
|
|
27
|
+
"id": 1,
|
|
28
|
+
"name": "function-1"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
25
32
|
`,
|
|
26
33
|
`$ xano function:list -p staging -o json --include_draft
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
{
|
|
35
|
+
"count": 1,
|
|
36
|
+
"page": 1,
|
|
37
|
+
"items": [
|
|
38
|
+
{
|
|
39
|
+
"id": 1,
|
|
40
|
+
"name": "function-1"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
33
44
|
`,
|
|
34
45
|
];
|
|
35
46
|
static flags = {
|
|
@@ -57,16 +68,7 @@ Available functions:
|
|
|
57
68
|
options: ['summary', 'json'],
|
|
58
69
|
required: false,
|
|
59
70
|
}),
|
|
60
|
-
|
|
61
|
-
default: 1,
|
|
62
|
-
description: 'Page number for pagination',
|
|
63
|
-
required: false,
|
|
64
|
-
}),
|
|
65
|
-
per_page: Flags.integer({
|
|
66
|
-
default: 50,
|
|
67
|
-
description: 'Number of results per page',
|
|
68
|
-
required: false,
|
|
69
|
-
}),
|
|
71
|
+
...pagingFlags('envelope', { maxPerPage: 10_000 }),
|
|
70
72
|
sort: Flags.string({
|
|
71
73
|
default: 'created_at',
|
|
72
74
|
description: 'Sort field',
|
|
@@ -118,24 +120,11 @@ Available functions:
|
|
|
118
120
|
const errorText = await response.text();
|
|
119
121
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
120
122
|
}
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
let functions;
|
|
124
|
-
if (Array.isArray(data)) {
|
|
125
|
-
functions = data;
|
|
126
|
-
}
|
|
127
|
-
else if (data && typeof data === 'object' && 'functions' in data && Array.isArray(data.functions)) {
|
|
128
|
-
functions = data.functions;
|
|
129
|
-
}
|
|
130
|
-
else if (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items)) {
|
|
131
|
-
functions = data.items;
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
this.error('Unexpected API response format');
|
|
135
|
-
}
|
|
123
|
+
const list = normalizeListResponse(await response.json(), ['functions']);
|
|
124
|
+
const functions = list.items;
|
|
136
125
|
// Output results
|
|
137
126
|
if (flags.output === 'json') {
|
|
138
|
-
this.log(JSON.stringify(
|
|
127
|
+
this.log(JSON.stringify(buildPagingJson(list, { page: flags.page, perPage: flags.per_page, tier: 'envelope' }), null, 2));
|
|
139
128
|
}
|
|
140
129
|
else {
|
|
141
130
|
// summary format
|
|
@@ -153,6 +142,9 @@ Available functions:
|
|
|
153
142
|
}
|
|
154
143
|
}
|
|
155
144
|
}
|
|
145
|
+
const footer = formatPagingFooter(list, { noun: 'function', page: flags.page, tier: 'envelope' });
|
|
146
|
+
if (footer)
|
|
147
|
+
this.log(footer);
|
|
156
148
|
}
|
|
157
149
|
}
|
|
158
150
|
catch (error) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
import { buildPagingJson } from '../../../utils/paging.js';
|
|
3
4
|
export default class KnowledgeList extends BaseCommand {
|
|
4
5
|
static args = {};
|
|
5
6
|
static description = 'List workspace knowledge and skills as plain-text markdown';
|
|
@@ -92,7 +93,7 @@ export default class KnowledgeList extends BaseCommand {
|
|
|
92
93
|
const data = await response.json();
|
|
93
94
|
const items = Array.isArray(data) ? data : (data.items ?? []);
|
|
94
95
|
if (flags.output === 'json') {
|
|
95
|
-
this.log(JSON.stringify(items, null, 2));
|
|
96
|
+
this.log(JSON.stringify(buildPagingJson({ items }, { tier: 'none' }), null, 2));
|
|
96
97
|
return;
|
|
97
98
|
}
|
|
98
99
|
this.log(this.formatMarkdown(items));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
import { buildPagingJson } from '../../../utils/paging.js';
|
|
3
4
|
export default class PlatformList extends BaseCommand {
|
|
4
5
|
static description = 'List all platforms';
|
|
5
6
|
static examples = [
|
|
@@ -47,22 +48,20 @@ Platforms:
|
|
|
47
48
|
this.error('Unexpected API response format');
|
|
48
49
|
}
|
|
49
50
|
if (flags.output === 'json') {
|
|
50
|
-
this.log(JSON.stringify(platforms, null, 2));
|
|
51
|
+
this.log(JSON.stringify(buildPagingJson({ items: platforms }, { tier: 'none' }), null, 2));
|
|
52
|
+
}
|
|
53
|
+
else if (platforms.length === 0) {
|
|
54
|
+
this.log('No platforms found');
|
|
51
55
|
}
|
|
52
56
|
else {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const created = platform.created_at
|
|
62
|
-
? ` | Created: ${new Date(platform.created_at).toISOString().split('T')[0]}`
|
|
63
|
-
: '';
|
|
64
|
-
this.log(` ${label}${helmTag}${created}`);
|
|
65
|
-
}
|
|
57
|
+
this.log('Platforms:');
|
|
58
|
+
for (const platform of platforms) {
|
|
59
|
+
const label = platform.name || `ID: ${platform.id}`;
|
|
60
|
+
const helmTag = platform.helm?.tag ? ` | Helm: ${platform.helm.tag}` : '';
|
|
61
|
+
const created = platform.created_at
|
|
62
|
+
? ` | Created: ${new Date(platform.created_at).toISOString().split('T')[0]}`
|
|
63
|
+
: '';
|
|
64
|
+
this.log(` ${label}${helmTag}${created}`);
|
|
66
65
|
}
|
|
67
66
|
}
|
|
68
67
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
import { buildPagingJson } from '../../../utils/paging.js';
|
|
3
4
|
export default class ReleaseList extends BaseCommand {
|
|
4
5
|
static description = 'List all releases in a workspace';
|
|
5
6
|
static examples = [
|
|
@@ -57,22 +58,20 @@ Releases in workspace 5:
|
|
|
57
58
|
this.error('Unexpected API response format');
|
|
58
59
|
}
|
|
59
60
|
if (flags.output === 'json') {
|
|
60
|
-
this.log(JSON.stringify(releases, null, 2));
|
|
61
|
+
this.log(JSON.stringify(buildPagingJson({ items: releases }, { tier: 'none' }), null, 2));
|
|
62
|
+
}
|
|
63
|
+
else if (releases.length === 0) {
|
|
64
|
+
this.log('No releases found');
|
|
61
65
|
}
|
|
62
66
|
else {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const createdAt = release.created_at
|
|
72
|
-
? ` (${new Date(release.created_at).toLocaleString(undefined, { timeZoneName: 'short' })})`
|
|
73
|
-
: '';
|
|
74
|
-
this.log(` - ${release.name} (ID: ${release.id})${branch}${hotfix}${createdAt}`);
|
|
75
|
-
}
|
|
67
|
+
this.log(`Releases in workspace ${workspaceId}:`);
|
|
68
|
+
for (const release of releases) {
|
|
69
|
+
const branch = release.branch ? ` - ${release.branch}` : '';
|
|
70
|
+
const hotfix = release.hotfix ? ' [hotfix]' : '';
|
|
71
|
+
const createdAt = release.created_at
|
|
72
|
+
? ` (${new Date(release.created_at).toLocaleString(undefined, { timeZoneName: 'short' })})`
|
|
73
|
+
: '';
|
|
74
|
+
this.log(` - ${release.name} (ID: ${release.id})${branch}${hotfix}${createdAt}`);
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
77
|
}
|
|
@@ -14,10 +14,4 @@ export default class ReleasePull extends BaseCommand {
|
|
|
14
14
|
};
|
|
15
15
|
run(): Promise<void>;
|
|
16
16
|
private resolveReleaseName;
|
|
17
|
-
/**
|
|
18
|
-
* Sanitize a document name for use as a filename.
|
|
19
|
-
* Strips quotes, replaces spaces with underscores, and removes
|
|
20
|
-
* characters that are unsafe in filenames.
|
|
21
|
-
*/
|
|
22
|
-
private sanitizeFilename;
|
|
23
17
|
}
|
|
@@ -3,7 +3,7 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import snakeCase from 'lodash.snakecase';
|
|
5
5
|
import BaseCommand from '../../../base-command.js';
|
|
6
|
-
import { buildApiGroupFolderResolver, parseDocument } from '../../../utils/document-parser.js';
|
|
6
|
+
import { buildApiGroupFolderResolver, buildChannelServerResolver, parseDocument, resolveDocumentPath, } from '../../../utils/document-parser.js';
|
|
7
7
|
export default class ReleasePull extends BaseCommand {
|
|
8
8
|
static description = 'Pull a release multidoc from the Xano Metadata API and split into individual files';
|
|
9
9
|
static examples = [
|
|
@@ -123,96 +123,19 @@ Pulled 58 documents from release 'v1.0'
|
|
|
123
123
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
124
124
|
// Resolve api_group names to unique folder names, disambiguating collisions
|
|
125
125
|
const getApiGroupFolder = buildApiGroupFolderResolver(documents, snakeCase);
|
|
126
|
+
// Resolve a realtime v2 channel path -> owning realtime_server name, so
|
|
127
|
+
// messages can nest under their channel's server (see resolver docs).
|
|
128
|
+
const getChannelServer = buildChannelServerResolver(documents);
|
|
126
129
|
// Track filenames per type to handle duplicates
|
|
127
130
|
const filenameCounters = new Map();
|
|
128
131
|
let writtenCount = 0;
|
|
129
132
|
for (const doc of documents) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
else if (doc.type === 'workspace_trigger') {
|
|
138
|
-
// workspace_trigger → workspace/trigger/{name}.xs
|
|
139
|
-
typeDir = path.join(outputDir, 'workspace', 'trigger');
|
|
140
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
141
|
-
}
|
|
142
|
-
else if (doc.type === 'error_trigger') {
|
|
143
|
-
// error_trigger → workspace/trigger/{name}.xs (singleton, colocated with workspace triggers)
|
|
144
|
-
typeDir = path.join(outputDir, 'workspace', 'trigger');
|
|
145
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
146
|
-
}
|
|
147
|
-
else if (doc.type === 'agent') {
|
|
148
|
-
// agent → ai/agent/{name}.xs
|
|
149
|
-
typeDir = path.join(outputDir, 'ai', 'agent');
|
|
150
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
151
|
-
}
|
|
152
|
-
else if (doc.type === 'mcp_server') {
|
|
153
|
-
// mcp_server → ai/mcp_server/{name}.xs
|
|
154
|
-
typeDir = path.join(outputDir, 'ai', 'mcp_server');
|
|
155
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
156
|
-
}
|
|
157
|
-
else if (doc.type === 'tool') {
|
|
158
|
-
// tool → ai/tool/{name}.xs
|
|
159
|
-
typeDir = path.join(outputDir, 'ai', 'tool');
|
|
160
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
161
|
-
}
|
|
162
|
-
else if (doc.type === 'agent_trigger') {
|
|
163
|
-
// agent_trigger → ai/agent/trigger/{name}.xs
|
|
164
|
-
typeDir = path.join(outputDir, 'ai', 'agent', 'trigger');
|
|
165
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
166
|
-
}
|
|
167
|
-
else if (doc.type === 'mcp_server_trigger') {
|
|
168
|
-
// mcp_server_trigger → ai/mcp_server/trigger/{name}.xs
|
|
169
|
-
typeDir = path.join(outputDir, 'ai', 'mcp_server', 'trigger');
|
|
170
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
171
|
-
}
|
|
172
|
-
else if (doc.type === 'table_trigger') {
|
|
173
|
-
// table_trigger → table/trigger/{name}.xs
|
|
174
|
-
typeDir = path.join(outputDir, 'table', 'trigger');
|
|
175
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
176
|
-
}
|
|
177
|
-
else if (doc.type === 'realtime_channel') {
|
|
178
|
-
// realtime_channel → realtime/channel/{name}.xs
|
|
179
|
-
typeDir = path.join(outputDir, 'realtime', 'channel');
|
|
180
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
181
|
-
}
|
|
182
|
-
else if (doc.type === 'realtime_trigger') {
|
|
183
|
-
// realtime_trigger → realtime/trigger/{name}.xs
|
|
184
|
-
typeDir = path.join(outputDir, 'realtime', 'trigger');
|
|
185
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
186
|
-
}
|
|
187
|
-
else if (doc.type === 'api_group') {
|
|
188
|
-
// api_group "test" → api/{resolved_folder}/{name}.xs
|
|
189
|
-
const groupFolder = getApiGroupFolder(doc.name);
|
|
190
|
-
typeDir = path.join(outputDir, 'api', groupFolder);
|
|
191
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
192
|
-
}
|
|
193
|
-
else if (doc.type === 'query' && doc.apiGroup) {
|
|
194
|
-
// query in group "test" → api/{resolved_folder}/{query_name}.xs
|
|
195
|
-
const groupFolder = getApiGroupFolder(doc.apiGroup);
|
|
196
|
-
const nameParts = doc.name.split('/');
|
|
197
|
-
const leafName = nameParts.pop();
|
|
198
|
-
const folderParts = nameParts.map((part) => snakeCase(part));
|
|
199
|
-
typeDir = path.join(outputDir, 'api', groupFolder, ...folderParts);
|
|
200
|
-
baseName = this.sanitizeFilename(leafName);
|
|
201
|
-
if (doc.verb) {
|
|
202
|
-
baseName = `${baseName}_${doc.verb}`;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
// Default: split folder path from name
|
|
207
|
-
const nameParts = doc.name.split('/');
|
|
208
|
-
const leafName = nameParts.pop();
|
|
209
|
-
const folderParts = nameParts.map((part) => snakeCase(part));
|
|
210
|
-
typeDir = path.join(outputDir, doc.type, ...folderParts);
|
|
211
|
-
baseName = this.sanitizeFilename(leafName);
|
|
212
|
-
if (doc.verb) {
|
|
213
|
-
baseName = `${baseName}_${doc.verb}`;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
133
|
+
const { baseName, typeDir } = resolveDocumentPath(doc, outputDir, {
|
|
134
|
+
getApiGroupFolder,
|
|
135
|
+
getChannelServer,
|
|
136
|
+
join: path.join,
|
|
137
|
+
snakeCase,
|
|
138
|
+
});
|
|
216
139
|
fs.mkdirSync(typeDir, { recursive: true });
|
|
217
140
|
// Track duplicates per directory
|
|
218
141
|
const dirKey = path.relative(outputDir, typeDir);
|
|
@@ -257,12 +180,4 @@ Pulled 58 documents from release 'v1.0'
|
|
|
257
180
|
}
|
|
258
181
|
return match.id;
|
|
259
182
|
}
|
|
260
|
-
/**
|
|
261
|
-
* Sanitize a document name for use as a filename.
|
|
262
|
-
* Strips quotes, replaces spaces with underscores, and removes
|
|
263
|
-
* characters that are unsafe in filenames.
|
|
264
|
-
*/
|
|
265
|
-
sanitizeFilename(name) {
|
|
266
|
-
return snakeCase(name.replaceAll('"', ''));
|
|
267
|
-
}
|
|
268
183
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import BaseCommand from '../../../../base-command.js';
|
|
3
|
+
import { formatPagingFooter } from '../../../../utils/paging.js';
|
|
3
4
|
export default class SandboxEnvList extends BaseCommand {
|
|
4
5
|
static description = 'List environment variable keys for a sandbox environment';
|
|
5
6
|
static examples = [
|
|
@@ -51,6 +52,9 @@ Environment variables for sandbox environment:
|
|
|
51
52
|
this.log(` - ${envVar.name}`);
|
|
52
53
|
}
|
|
53
54
|
}
|
|
55
|
+
const footer = formatPagingFooter({ items: envVars }, { noun: 'environment variable', tier: 'none' });
|
|
56
|
+
if (footer)
|
|
57
|
+
this.log(footer);
|
|
54
58
|
}
|
|
55
59
|
}
|
|
56
60
|
catch (error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import snakeCase from 'lodash.snakecase';
|
|
3
3
|
import BaseCommand from '../../../base-command.js';
|
|
4
|
-
import { buildApiGroupFolderResolver, parseDocument } from '../../../utils/document-parser.js';
|
|
4
|
+
import { buildApiGroupFolderResolver, buildChannelServerResolver, parseDocument, resolveDocumentPath, } from '../../../utils/document-parser.js';
|
|
5
5
|
import { fetchKnowledge, writeKnowledge } from '../../../utils/knowledge-sync.js';
|
|
6
6
|
import * as fs from 'node:fs';
|
|
7
7
|
import * as path from 'node:path';
|
|
@@ -92,82 +92,18 @@ Pulled 42 documents from sandbox environment to ./my-sandbox
|
|
|
92
92
|
const outputDir = path.resolve(flags.directory);
|
|
93
93
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
94
94
|
const getApiGroupFolder = buildApiGroupFolderResolver(documents, snakeCase);
|
|
95
|
+
// Resolve a realtime v2 channel path -> owning realtime_server name, so
|
|
96
|
+
// messages can nest under their channel's server (see resolver docs).
|
|
97
|
+
const getChannelServer = buildChannelServerResolver(documents);
|
|
95
98
|
const filenameCounters = new Map();
|
|
96
99
|
let writtenCount = 0;
|
|
97
100
|
for (const doc of documents) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
else if (doc.type === 'workspace_trigger') {
|
|
105
|
-
typeDir = path.join(outputDir, 'workspace', 'trigger');
|
|
106
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
107
|
-
}
|
|
108
|
-
else if (doc.type === 'error_trigger') {
|
|
109
|
-
// error_trigger → workspace/trigger/{name}.xs (singleton, colocated with workspace triggers)
|
|
110
|
-
typeDir = path.join(outputDir, 'workspace', 'trigger');
|
|
111
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
112
|
-
}
|
|
113
|
-
else if (doc.type === 'agent') {
|
|
114
|
-
typeDir = path.join(outputDir, 'ai', 'agent');
|
|
115
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
116
|
-
}
|
|
117
|
-
else if (doc.type === 'mcp_server') {
|
|
118
|
-
typeDir = path.join(outputDir, 'ai', 'mcp_server');
|
|
119
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
120
|
-
}
|
|
121
|
-
else if (doc.type === 'tool') {
|
|
122
|
-
typeDir = path.join(outputDir, 'ai', 'tool');
|
|
123
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
124
|
-
}
|
|
125
|
-
else if (doc.type === 'agent_trigger') {
|
|
126
|
-
typeDir = path.join(outputDir, 'ai', 'agent', 'trigger');
|
|
127
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
128
|
-
}
|
|
129
|
-
else if (doc.type === 'mcp_server_trigger') {
|
|
130
|
-
typeDir = path.join(outputDir, 'ai', 'mcp_server', 'trigger');
|
|
131
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
132
|
-
}
|
|
133
|
-
else if (doc.type === 'table_trigger') {
|
|
134
|
-
typeDir = path.join(outputDir, 'table', 'trigger');
|
|
135
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
136
|
-
}
|
|
137
|
-
else if (doc.type === 'realtime_channel') {
|
|
138
|
-
typeDir = path.join(outputDir, 'realtime', 'channel');
|
|
139
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
140
|
-
}
|
|
141
|
-
else if (doc.type === 'realtime_trigger') {
|
|
142
|
-
typeDir = path.join(outputDir, 'realtime', 'trigger');
|
|
143
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
144
|
-
}
|
|
145
|
-
else if (doc.type === 'api_group') {
|
|
146
|
-
const groupFolder = getApiGroupFolder(doc.name);
|
|
147
|
-
typeDir = path.join(outputDir, 'api', groupFolder);
|
|
148
|
-
baseName = this.sanitizeFilename(doc.name);
|
|
149
|
-
}
|
|
150
|
-
else if (doc.type === 'query' && doc.apiGroup) {
|
|
151
|
-
const groupFolder = getApiGroupFolder(doc.apiGroup);
|
|
152
|
-
const nameParts = doc.name.split('/');
|
|
153
|
-
const leafName = nameParts.pop();
|
|
154
|
-
const folderParts = nameParts.map((part) => snakeCase(part));
|
|
155
|
-
typeDir = path.join(outputDir, 'api', groupFolder, ...folderParts);
|
|
156
|
-
baseName = this.sanitizeFilename(leafName);
|
|
157
|
-
if (doc.verb) {
|
|
158
|
-
baseName = `${baseName}_${doc.verb}`;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
const nameParts = doc.name.split('/');
|
|
163
|
-
const leafName = nameParts.pop();
|
|
164
|
-
const folderParts = nameParts.map((part) => snakeCase(part));
|
|
165
|
-
typeDir = path.join(outputDir, doc.type, ...folderParts);
|
|
166
|
-
baseName = this.sanitizeFilename(leafName);
|
|
167
|
-
if (doc.verb) {
|
|
168
|
-
baseName = `${baseName}_${doc.verb}`;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
101
|
+
const { baseName, typeDir } = resolveDocumentPath(doc, outputDir, {
|
|
102
|
+
getApiGroupFolder,
|
|
103
|
+
getChannelServer,
|
|
104
|
+
join: path.join,
|
|
105
|
+
snakeCase,
|
|
106
|
+
});
|
|
171
107
|
fs.mkdirSync(typeDir, { recursive: true });
|
|
172
108
|
const dirKey = path.relative(outputDir, typeDir);
|
|
173
109
|
if (!filenameCounters.has(dirKey)) {
|
|
@@ -194,7 +130,4 @@ Pulled 42 documents from sandbox environment to ./my-sandbox
|
|
|
194
130
|
parts.push(`${knowledgeCount} knowledge file${knowledgeCount === 1 ? '' : 's'}`);
|
|
195
131
|
this.log(`Pulled ${parts.join(' + ')} from sandbox environment to ${flags.directory}`);
|
|
196
132
|
}
|
|
197
|
-
sanitizeFilename(name) {
|
|
198
|
-
return snakeCase(name.replaceAll('"', ''));
|
|
199
|
-
}
|
|
200
133
|
}
|
|
@@ -3,6 +3,8 @@ export default class SandboxUnitTestList extends BaseCommand {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
|
+
page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
per_page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
8
|
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
9
|
'obj-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
10
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import BaseCommand from '../../../../base-command.js';
|
|
3
|
+
import { buildPagingJson, buildPagingParams, formatPagingFooter, normalizeListResponse, pagingFlags } from '../../../../utils/paging.js';
|
|
3
4
|
export default class SandboxUnitTestList extends BaseCommand {
|
|
4
5
|
static description = 'List all unit tests for a sandbox environment';
|
|
5
6
|
static examples = [
|
|
@@ -28,12 +29,12 @@ Unit tests:
|
|
|
28
29
|
options: ['summary', 'json'],
|
|
29
30
|
required: false,
|
|
30
31
|
}),
|
|
32
|
+
...pagingFlags('envelope', { maxPerPage: 10_000 }),
|
|
31
33
|
};
|
|
32
34
|
async run() {
|
|
33
35
|
const { flags } = await this.parse(SandboxUnitTestList);
|
|
34
36
|
const { profile } = this.resolveProfile(flags);
|
|
35
|
-
const params = new URLSearchParams();
|
|
36
|
-
params.set('per_page', '10000');
|
|
37
|
+
const params = new URLSearchParams(buildPagingParams(flags, 'envelope'));
|
|
37
38
|
if (flags.branch)
|
|
38
39
|
params.set('branch', flags.branch);
|
|
39
40
|
if (flags['obj-type'])
|
|
@@ -51,19 +52,10 @@ Unit tests:
|
|
|
51
52
|
const message = await this.parseApiError(response, 'API request failed');
|
|
52
53
|
this.error(message);
|
|
53
54
|
}
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
if (Array.isArray(data)) {
|
|
57
|
-
tests = data;
|
|
58
|
-
}
|
|
59
|
-
else if (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items)) {
|
|
60
|
-
tests = data.items;
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
this.error('Unexpected API response format');
|
|
64
|
-
}
|
|
55
|
+
const list = normalizeListResponse(await response.json());
|
|
56
|
+
const tests = list.items;
|
|
65
57
|
if (flags.output === 'json') {
|
|
66
|
-
this.log(JSON.stringify(
|
|
58
|
+
this.log(JSON.stringify(buildPagingJson(list, { page: flags.page, perPage: flags.per_page, tier: 'envelope' }), null, 2));
|
|
67
59
|
}
|
|
68
60
|
else {
|
|
69
61
|
if (tests.length === 0) {
|
|
@@ -75,6 +67,9 @@ Unit tests:
|
|
|
75
67
|
this.log(` - ${test.name} (ID: ${test.id}) [${test.obj_type}: ${test.obj_name}]`);
|
|
76
68
|
}
|
|
77
69
|
}
|
|
70
|
+
const footer = formatPagingFooter(list, { noun: 'unit test', page: flags.page, tier: 'envelope' });
|
|
71
|
+
if (footer)
|
|
72
|
+
this.log(footer);
|
|
78
73
|
}
|
|
79
74
|
}
|
|
80
75
|
catch (error) {
|
|
@@ -4,6 +4,7 @@ export default class SandboxUnitTestRunAll extends BaseCommand {
|
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
6
|
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
concurrency: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
8
|
'obj-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|