@xano/cli 1.1.0 → 1.3.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 +155 -4
- package/dist/base-command.d.ts +43 -36
- package/dist/base-command.js +223 -160
- 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/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 +223 -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.js +1 -1
- package/dist/commands/function/edit/index.js +26 -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/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/edit/index.js +2 -2
- package/dist/commands/release/export/index.js +1 -2
- package/dist/commands/release/import/index.js +1 -1
- 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 +2 -2
- 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 +5 -5
- package/dist/commands/sandbox/unit_test/run/index.js +17 -11
- package/dist/commands/sandbox/unit_test/run_all/index.js +9 -1
- package/dist/commands/sandbox/workflow_test/run/index.js +12 -9
- package/dist/commands/sandbox/workflow_test/run_all/index.js +9 -1
- 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 +1 -1
- package/dist/commands/tenant/create/index.js +6 -6
- package/dist/commands/tenant/deploy_platform/index.js +1 -1
- package/dist/commands/tenant/edit/index.js +7 -7
- 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/unit_test/run/index.js +19 -11
- package/dist/commands/tenant/unit_test/run_all/index.js +13 -1
- package/dist/commands/tenant/workflow_test/run/index.js +14 -9
- package/dist/commands/tenant/workflow_test/run_all/index.js +13 -1
- package/dist/commands/unit_test/run/index.js +19 -11
- package/dist/commands/unit_test/run_all/index.js +13 -1
- package/dist/commands/workflow_test/run/index.js +15 -10
- package/dist/commands/workflow_test/run_all/index.js +15 -3
- 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 +1 -7
- package/dist/commands/workspace/pull/index.js +17 -103
- package/dist/help.js +4 -2
- package/dist/update-check.d.ts +1 -1
- package/dist/update-check.js +1 -1
- package/dist/utils/document-parser.d.ts +116 -0
- package/dist/utils/document-parser.js +311 -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 +88 -0
- package/dist/utils/multidoc-push.js +378 -15
- package/dist/utils/paging.d.ts +2 -1
- package/dist/utils/paging.js +0 -1
- package/dist/utils/reference-checker.js +14 -3
- package/oclif.manifest.json +5898 -3804
- package/package.json +10 -1
|
@@ -61,9 +61,9 @@ Workspace updated to 'Production API' (xyz789) on profile 'production'
|
|
|
61
61
|
async fetchWorkspaces(accessToken, origin) {
|
|
62
62
|
const response = await fetch(`${origin}/api:meta/workspace`, {
|
|
63
63
|
headers: {
|
|
64
|
-
'User-Agent': buildUserAgent(this.config.version),
|
|
65
64
|
accept: 'application/json',
|
|
66
65
|
Authorization: `Bearer ${accessToken}`,
|
|
66
|
+
'User-Agent': buildUserAgent(this.config.version),
|
|
67
67
|
},
|
|
68
68
|
method: 'GET',
|
|
69
69
|
});
|
|
@@ -69,8 +69,8 @@ Updated release: v1.0-final - ID: 10
|
|
|
69
69
|
const current = await getResponse.json();
|
|
70
70
|
// Merge in user-provided values
|
|
71
71
|
const body = {
|
|
72
|
-
description: flags.description
|
|
73
|
-
name: flags.name
|
|
72
|
+
description: flags.description === undefined ? (current.description ?? '') : flags.description,
|
|
73
|
+
name: flags.name === undefined ? current.name : flags.name,
|
|
74
74
|
};
|
|
75
75
|
// Update release
|
|
76
76
|
const putResponse = await this.verboseFetch(baseUrl, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
import BaseCommand, { buildUserAgent } from '../../../base-command.js';
|
|
5
5
|
export default class ReleaseExport extends BaseCommand {
|
|
6
6
|
static args = {
|
|
@@ -79,7 +79,6 @@ Downloaded release 'v1.0' to ./release-v1.0.tar.gz
|
|
|
79
79
|
const fileStream = fs.createWriteStream(resolvedPath);
|
|
80
80
|
const reader = downloadResponse.body.getReader();
|
|
81
81
|
let totalBytes = 0;
|
|
82
|
-
// eslint-disable-next-line no-constant-condition
|
|
83
82
|
while (true) {
|
|
84
83
|
// eslint-disable-next-line no-await-in-loop
|
|
85
84
|
const { done, value } = await reader.read();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../base-command.js';
|
|
5
5
|
export default class ReleaseImport extends BaseCommand {
|
|
6
6
|
static args = {};
|
|
@@ -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
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
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, 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);
|
|
@@ -223,8 +146,7 @@ Pulled 58 documents from release 'v1.0'
|
|
|
223
146
|
const count = typeCounters.get(baseName) || 0;
|
|
224
147
|
typeCounters.set(baseName, count + 1);
|
|
225
148
|
// Append numeric suffix for duplicates
|
|
226
|
-
|
|
227
|
-
filename = count === 0 ? `${baseName}.xs` : `${baseName}_${count + 1}.xs`;
|
|
149
|
+
const filename = count === 0 ? `${baseName}.xs` : `${baseName}_${count + 1}.xs`;
|
|
228
150
|
const filePath = path.join(typeDir, filename);
|
|
229
151
|
fs.writeFileSync(filePath, doc.content, 'utf8');
|
|
230
152
|
writtenCount++;
|
|
@@ -257,12 +179,4 @@ Pulled 58 documents from release 'v1.0'
|
|
|
257
179
|
}
|
|
258
180
|
return match.id;
|
|
259
181
|
}
|
|
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
182
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../base-command.js';
|
|
5
5
|
import { findFilesWithGuid } from '../../../utils/document-parser.js';
|
|
6
6
|
export default class ReleasePush extends BaseCommand {
|
|
@@ -72,7 +72,7 @@ Output release details as JSON
|
|
|
72
72
|
};
|
|
73
73
|
async run() {
|
|
74
74
|
const { flags } = await this.parse(ReleasePush);
|
|
75
|
-
const {
|
|
75
|
+
const { profile, profileName } = this.resolveProfile(flags);
|
|
76
76
|
// Determine workspace_id from flag or profile
|
|
77
77
|
let workspaceId;
|
|
78
78
|
if (flags.workspace) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import * as yaml from 'js-yaml';
|
|
3
3
|
import * as fs from 'node:fs';
|
|
4
|
-
import
|
|
4
|
+
import path from 'node:path';
|
|
5
5
|
import BaseCommand from '../../../../base-command.js';
|
|
6
6
|
export default class SandboxEnvGetAll extends BaseCommand {
|
|
7
7
|
static description = 'Get all environment variables for a sandbox environment and save to a YAML file';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import * as yaml from 'js-yaml';
|
|
3
3
|
import * as fs from 'node:fs';
|
|
4
|
-
import
|
|
4
|
+
import path from 'node:path';
|
|
5
5
|
import BaseCommand from '../../../../base-command.js';
|
|
6
6
|
export default class SandboxEnvSetAll extends BaseCommand {
|
|
7
7
|
static description = '[CRITICAL] STOP and confirm with the user; this replaces all environment variables with the imported file. Sets all environment variables for a sandbox environment from a YAML file.';
|
|
@@ -36,7 +36,7 @@ Sandbox Environment: (tc24-abcd-x1y2)
|
|
|
36
36
|
this.log(` Domain: ${tenant.xano_domain}`);
|
|
37
37
|
if (tenant.sandbox_expires_at) {
|
|
38
38
|
const expiresAt = new Date(tenant.sandbox_expires_at);
|
|
39
|
-
if (!isNaN(expiresAt.getTime())) {
|
|
39
|
+
if (!Number.isNaN(expiresAt.getTime())) {
|
|
40
40
|
const msLeft = expiresAt.getTime() - Date.now();
|
|
41
41
|
if (msLeft > 0) {
|
|
42
42
|
const minsLeft = Math.ceil(msLeft / 60_000);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../../base-command.js';
|
|
5
5
|
export default class SandboxLicenseGet extends BaseCommand {
|
|
6
6
|
static description = 'Get the license for a sandbox environment';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../../base-command.js';
|
|
5
5
|
export default class SandboxLicenseSet extends BaseCommand {
|
|
6
6
|
static description = 'Set/update the license for a sandbox environment';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import snakeCase from 'lodash.snakecase';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
3
5
|
import BaseCommand from '../../../base-command.js';
|
|
4
|
-
import { buildApiGroupFolderResolver, parseDocument } from '../../../utils/document-parser.js';
|
|
6
|
+
import { buildApiGroupFolderResolver, buildChannelServerResolver, parseDocument, resolveDocumentPath, } from '../../../utils/document-parser.js';
|
|
5
7
|
import { fetchKnowledge, writeKnowledge } from '../../../utils/knowledge-sync.js';
|
|
6
|
-
import * as fs from 'node:fs';
|
|
7
|
-
import * as path from 'node:path';
|
|
8
8
|
export default class SandboxPull extends BaseCommand {
|
|
9
9
|
static description = 'Pull documents from your sandbox environment and split into individual files';
|
|
10
10
|
static examples = [
|
|
@@ -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)) {
|
|
@@ -176,8 +112,7 @@ Pulled 42 documents from sandbox environment to ./my-sandbox
|
|
|
176
112
|
const typeCounters = filenameCounters.get(dirKey);
|
|
177
113
|
const count = typeCounters.get(baseName) || 0;
|
|
178
114
|
typeCounters.set(baseName, count + 1);
|
|
179
|
-
|
|
180
|
-
filename = count === 0 ? `${baseName}.xs` : `${baseName}_${count + 1}.xs`;
|
|
115
|
+
const filename = count === 0 ? `${baseName}.xs` : `${baseName}_${count + 1}.xs`;
|
|
181
116
|
const filePath = path.join(typeDir, filename);
|
|
182
117
|
fs.writeFileSync(filePath, doc.content, 'utf8');
|
|
183
118
|
writtenCount++;
|
|
@@ -194,7 +129,4 @@ Pulled 42 documents from sandbox environment to ./my-sandbox
|
|
|
194
129
|
parts.push(`${knowledgeCount} knowledge file${knowledgeCount === 1 ? '' : 's'}`);
|
|
195
130
|
this.log(`Pulled ${parts.join(' + ')} from sandbox environment to ${flags.directory}`);
|
|
196
131
|
}
|
|
197
|
-
sanitizeFilename(name) {
|
|
198
|
-
return snakeCase(name.replaceAll('"', ''));
|
|
199
|
-
}
|
|
200
132
|
}
|
|
@@ -3,8 +3,8 @@ export default class SandboxPush extends BaseCommand {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
|
-
directory: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
6
|
delete: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
directory: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
8
|
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
9
|
env: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
10
|
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -33,17 +33,17 @@ Push and open sandbox review in the browser
|
|
|
33
33
|
];
|
|
34
34
|
static flags = {
|
|
35
35
|
...BaseCommand.baseFlags,
|
|
36
|
+
delete: Flags.boolean({
|
|
37
|
+
default: false,
|
|
38
|
+
description: 'Delete sandbox objects not included in the push (requires --sync)',
|
|
39
|
+
required: false,
|
|
40
|
+
}),
|
|
36
41
|
directory: Flags.string({
|
|
37
42
|
char: 'd',
|
|
38
43
|
default: '.',
|
|
39
44
|
description: 'Directory containing documents to push (defaults to current directory)',
|
|
40
45
|
required: false,
|
|
41
46
|
}),
|
|
42
|
-
delete: Flags.boolean({
|
|
43
|
-
default: false,
|
|
44
|
-
description: 'Delete sandbox objects not included in the push (requires --sync)',
|
|
45
|
-
required: false,
|
|
46
|
-
}),
|
|
47
47
|
'dry-run': Flags.boolean({
|
|
48
48
|
default: false,
|
|
49
49
|
description: 'Show preview of changes without pushing (exit after preview)',
|
|
@@ -46,24 +46,30 @@ Result: PASS
|
|
|
46
46
|
this.error(message);
|
|
47
47
|
}
|
|
48
48
|
const result = (await response.json());
|
|
49
|
+
// Anything other than 'ok' is a failure -- the API has been observed
|
|
50
|
+
// returning several distinct non-ok statuses, so never enumerate them.
|
|
51
|
+
const passed = result.status === 'ok';
|
|
49
52
|
if (flags.output === 'json') {
|
|
50
53
|
this.log(JSON.stringify(result, null, 2));
|
|
51
54
|
}
|
|
55
|
+
else if (passed) {
|
|
56
|
+
this.log('Result: PASS');
|
|
57
|
+
}
|
|
52
58
|
else {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const failedExpects = result.results?.filter((r) => r.status === 'fail') ?? [];
|
|
59
|
-
for (const expect of failedExpects) {
|
|
60
|
-
if (expect.message) {
|
|
61
|
-
this.log(` Error: ${expect.message}`);
|
|
62
|
-
}
|
|
59
|
+
this.log('Result: FAIL');
|
|
60
|
+
const failedExpects = result.results?.filter((r) => r.status === 'fail') ?? [];
|
|
61
|
+
for (const expect of failedExpects) {
|
|
62
|
+
if (expect.message) {
|
|
63
|
+
this.log(` Error: ${expect.message}`);
|
|
63
64
|
}
|
|
64
|
-
this.exit(1);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
// Set the code rather than throwing via this.exit(): the exit must apply
|
|
68
|
+
// in both output modes, and process.exitCode cannot be swallowed by the
|
|
69
|
+
// surrounding catch. Mirrors what run_all already does.
|
|
70
|
+
if (!passed) {
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
}
|
|
67
73
|
}
|
|
68
74
|
catch (error) {
|
|
69
75
|
if (error instanceof Error && 'oclif' in error)
|
|
@@ -78,7 +78,15 @@ Results: 4 passed, 1 failed
|
|
|
78
78
|
},
|
|
79
79
|
});
|
|
80
80
|
if (tests.length === 0) {
|
|
81
|
-
|
|
81
|
+
// An empty suite is a normal state (fresh branches have no tests) and
|
|
82
|
+
// is a success, not a failure. Under -o json it must still be parseable,
|
|
83
|
+
// with the same keys the populated path emits below.
|
|
84
|
+
if (flags.output === 'json') {
|
|
85
|
+
this.log(JSON.stringify({ failed: 0, passed: 0, results: [] }, null, 2));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.log('No unit tests found');
|
|
89
|
+
}
|
|
82
90
|
return;
|
|
83
91
|
}
|
|
84
92
|
if (flags.output === 'summary') {
|
|
@@ -46,22 +46,25 @@ Result: PASS (0.25s)
|
|
|
46
46
|
this.error(message);
|
|
47
47
|
}
|
|
48
48
|
const result = (await response.json());
|
|
49
|
+
// Anything other than 'ok' is a failure -- the API has been observed
|
|
50
|
+
// returning several distinct non-ok statuses, so never enumerate them.
|
|
51
|
+
const passed = result.status === 'ok';
|
|
49
52
|
if (flags.output === 'json') {
|
|
50
53
|
this.log(JSON.stringify(result, null, 2));
|
|
51
54
|
}
|
|
52
55
|
else {
|
|
53
56
|
const timing = result.timing ? ` (${result.timing}s)` : '';
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
else {
|
|
58
|
-
this.log(`Result: FAIL${timing}`);
|
|
59
|
-
if (result.message) {
|
|
60
|
-
this.log(` Error: ${result.message}`);
|
|
61
|
-
}
|
|
62
|
-
this.exit(1);
|
|
57
|
+
this.log(`Result: ${passed ? 'PASS' : 'FAIL'}${timing}`);
|
|
58
|
+
if (!passed && result.message) {
|
|
59
|
+
this.log(` Error: ${result.message}`);
|
|
63
60
|
}
|
|
64
61
|
}
|
|
62
|
+
// Set the code rather than throwing via this.exit(): the exit must apply
|
|
63
|
+
// in both output modes, and process.exitCode cannot be swallowed by the
|
|
64
|
+
// surrounding catch. Mirrors what run_all already does.
|
|
65
|
+
if (!passed) {
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
}
|
|
65
68
|
}
|
|
66
69
|
catch (error) {
|
|
67
70
|
if (error instanceof Error && 'oclif' in error)
|
|
@@ -71,7 +71,15 @@ Results: 2 passed, 1 failed
|
|
|
71
71
|
},
|
|
72
72
|
});
|
|
73
73
|
if (tests.length === 0) {
|
|
74
|
-
|
|
74
|
+
// An empty suite is a normal state (fresh branches have no tests) and
|
|
75
|
+
// is a success, not a failure. Under -o json it must still be parseable,
|
|
76
|
+
// with the same keys the populated path emits below.
|
|
77
|
+
if (flags.output === 'json') {
|
|
78
|
+
this.log(JSON.stringify({ failed: 0, passed: 0, results: [] }, null, 2));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
this.log('No workflow tests found');
|
|
82
|
+
}
|
|
75
83
|
return;
|
|
76
84
|
}
|
|
77
85
|
if (flags.output === 'summary') {
|
|
@@ -6,7 +6,6 @@ import BaseCommand from '../../../../base-command.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function generateBuildName(date?: Date): string;
|
|
8
8
|
export default class StaticHostBuildCreate extends BaseCommand {
|
|
9
|
-
static hidden: boolean;
|
|
10
9
|
static args: {
|
|
11
10
|
static_host: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
12
11
|
};
|
|
@@ -23,5 +22,6 @@ export default class StaticHostBuildCreate extends BaseCommand {
|
|
|
23
22
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
23
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
25
24
|
};
|
|
25
|
+
static hidden: boolean;
|
|
26
26
|
run(): Promise<void>;
|
|
27
27
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../../base-command.js';
|
|
5
5
|
const pad2 = (n) => String(n).padStart(2, '0');
|
|
6
6
|
/**
|
|
@@ -18,7 +18,6 @@ export function generateBuildName(date = new Date()) {
|
|
|
18
18
|
return `${y}${mo}${d}-${h}${mi}${s}`;
|
|
19
19
|
}
|
|
20
20
|
export default class StaticHostBuildCreate extends BaseCommand {
|
|
21
|
-
static hidden = true;
|
|
22
21
|
static args = {
|
|
23
22
|
static_host: Args.string({
|
|
24
23
|
description: 'Static Host name',
|
|
@@ -88,6 +87,7 @@ Description: Production build
|
|
|
88
87
|
required: false,
|
|
89
88
|
}),
|
|
90
89
|
};
|
|
90
|
+
static hidden = true;
|
|
91
91
|
async run() {
|
|
92
92
|
this.warn('`static_host build create` is deprecated. Use `static_host build push -f <file>` instead.');
|
|
93
93
|
const { args, flags } = await this.parse(StaticHostBuildCreate);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../../base-command.js';
|
|
5
5
|
import { normalizeListResponse } from '../../../../utils/paging.js';
|
|
6
6
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Args, Flags, ux } from '@oclif/core';
|
|
2
2
|
import archiver from 'archiver';
|
|
3
3
|
import * as fs from 'node:fs';
|
|
4
|
-
import
|
|
4
|
+
import path from 'node:path';
|
|
5
5
|
import BaseCommand from '../../../../base-command.js';
|
|
6
6
|
import { collectStaticHostFiles } from '../../../../utils/static-host-files.js';
|
|
7
7
|
import { generateBuildName } from '../create/index.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../base-command.js';
|
|
5
5
|
export default class StaticHostEdit extends BaseCommand {
|
|
6
6
|
static args = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
import BaseCommand, { buildUserAgent } from '../../../../base-command.js';
|
|
5
5
|
export default class TenantBackupExport extends BaseCommand {
|
|
6
6
|
static args = {
|
|
@@ -82,7 +82,6 @@ Downloaded backup #10 to ./tenant-t1234-abcd-xyz1-backup-10.tar.gz
|
|
|
82
82
|
const fileStream = fs.createWriteStream(resolvedPath);
|
|
83
83
|
const reader = downloadResponse.body.getReader();
|
|
84
84
|
let totalBytes = 0;
|
|
85
|
-
// eslint-disable-next-line no-constant-condition
|
|
86
85
|
while (true) {
|
|
87
86
|
// eslint-disable-next-line no-await-in-loop
|
|
88
87
|
const { done, value } = await reader.read();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../../base-command.js';
|
|
5
5
|
export default class TenantBackupImport extends BaseCommand {
|
|
6
6
|
static args = {
|