@xano/cli 0.0.20 → 0.0.21
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/dist/commands/workspace/create/index.d.ts +14 -0
- package/dist/commands/workspace/create/index.js +131 -0
- package/dist/commands/workspace/delete/index.d.ts +20 -0
- package/dist/commands/workspace/delete/index.js +141 -0
- package/dist/commands/workspace/edit/index.d.ts +22 -0
- package/dist/commands/workspace/edit/index.js +176 -0
- package/dist/commands/workspace/get/index.d.ts +18 -0
- package/dist/commands/workspace/get/index.js +136 -0
- package/dist/lib/run-http-client.d.ts +1 -1
- package/dist/lib/run-http-client.js +1 -1
- package/oclif.manifest.json +859 -538
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class WorkspaceCreate extends BaseCommand {
|
|
3
|
+
static flags: {
|
|
4
|
+
name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
5
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
10
|
+
static description: string;
|
|
11
|
+
static examples: string[];
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
private loadCredentials;
|
|
14
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as yaml from 'js-yaml';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class WorkspaceCreate extends BaseCommand {
|
|
8
|
+
static flags = {
|
|
9
|
+
...BaseCommand.baseFlags,
|
|
10
|
+
name: Flags.string({
|
|
11
|
+
char: 'n',
|
|
12
|
+
description: 'Name of the workspace',
|
|
13
|
+
required: true,
|
|
14
|
+
}),
|
|
15
|
+
description: Flags.string({
|
|
16
|
+
char: 'd',
|
|
17
|
+
description: 'Description for the workspace',
|
|
18
|
+
required: false,
|
|
19
|
+
}),
|
|
20
|
+
output: Flags.string({
|
|
21
|
+
char: 'o',
|
|
22
|
+
description: 'Output format',
|
|
23
|
+
required: false,
|
|
24
|
+
default: 'summary',
|
|
25
|
+
options: ['summary', 'json'],
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
28
|
+
static description = 'Create a new workspace via the Xano Metadata API';
|
|
29
|
+
static examples = [
|
|
30
|
+
`$ xano workspace create --name "my-workspace"
|
|
31
|
+
Created workspace: my-workspace (ID: 123)
|
|
32
|
+
`,
|
|
33
|
+
`$ xano workspace create --name "my-app" --description "My application workspace"
|
|
34
|
+
Created workspace: my-app (ID: 456)
|
|
35
|
+
Description: My application workspace
|
|
36
|
+
`,
|
|
37
|
+
`$ xano workspace create -n "new-project" -d "New project workspace" -o json
|
|
38
|
+
{
|
|
39
|
+
"id": 789,
|
|
40
|
+
"name": "new-project",
|
|
41
|
+
"description": "New project workspace"
|
|
42
|
+
}
|
|
43
|
+
`,
|
|
44
|
+
];
|
|
45
|
+
async run() {
|
|
46
|
+
const { flags } = await this.parse(WorkspaceCreate);
|
|
47
|
+
// Get profile name (default or from flag/env)
|
|
48
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
49
|
+
// Load credentials
|
|
50
|
+
const credentials = this.loadCredentials();
|
|
51
|
+
// Get the profile configuration
|
|
52
|
+
if (!(profileName in credentials.profiles)) {
|
|
53
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
54
|
+
`Create a profile using 'xano profile create'`);
|
|
55
|
+
}
|
|
56
|
+
const profile = credentials.profiles[profileName];
|
|
57
|
+
// Validate required fields
|
|
58
|
+
if (!profile.instance_origin) {
|
|
59
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
60
|
+
}
|
|
61
|
+
if (!profile.access_token) {
|
|
62
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
63
|
+
}
|
|
64
|
+
// Construct the API URL
|
|
65
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace`;
|
|
66
|
+
// Build request body
|
|
67
|
+
const body = {
|
|
68
|
+
name: flags.name,
|
|
69
|
+
};
|
|
70
|
+
if (flags.description) {
|
|
71
|
+
body.description = flags.description;
|
|
72
|
+
}
|
|
73
|
+
// Create workspace via the API
|
|
74
|
+
try {
|
|
75
|
+
const response = await fetch(apiUrl, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: {
|
|
78
|
+
'accept': 'application/json',
|
|
79
|
+
'content-type': 'application/json',
|
|
80
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify(body),
|
|
83
|
+
});
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
const errorText = await response.text();
|
|
86
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
87
|
+
}
|
|
88
|
+
const workspace = await response.json();
|
|
89
|
+
// Output results
|
|
90
|
+
if (flags.output === 'json') {
|
|
91
|
+
this.log(JSON.stringify(workspace, null, 2));
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// summary format
|
|
95
|
+
this.log(`Created workspace: ${workspace.name} (ID: ${workspace.id})`);
|
|
96
|
+
if (workspace.description) {
|
|
97
|
+
this.log(` Description: ${workspace.description}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (error instanceof Error) {
|
|
103
|
+
this.error(`Failed to create workspace: ${error.message}`);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.error(`Failed to create workspace: ${String(error)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
loadCredentials() {
|
|
111
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
112
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
113
|
+
// Check if credentials file exists
|
|
114
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
115
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
116
|
+
`Create a profile using 'xano profile create'`);
|
|
117
|
+
}
|
|
118
|
+
// Read credentials file
|
|
119
|
+
try {
|
|
120
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
121
|
+
const parsed = yaml.load(fileContent);
|
|
122
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
123
|
+
this.error('Credentials file has invalid format.');
|
|
124
|
+
}
|
|
125
|
+
return parsed;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class WorkspaceDelete extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
workspace_id: import("@oclif/core/interfaces").Arg<number, {
|
|
5
|
+
max?: number;
|
|
6
|
+
min?: number;
|
|
7
|
+
}>;
|
|
8
|
+
};
|
|
9
|
+
static flags: {
|
|
10
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
};
|
|
15
|
+
static description: string;
|
|
16
|
+
static examples: string[];
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
private confirm;
|
|
19
|
+
private loadCredentials;
|
|
20
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as yaml from 'js-yaml';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class WorkspaceDelete extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
workspace_id: Args.integer({
|
|
10
|
+
description: 'Workspace ID to delete',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static flags = {
|
|
15
|
+
...BaseCommand.baseFlags,
|
|
16
|
+
force: Flags.boolean({
|
|
17
|
+
char: 'f',
|
|
18
|
+
description: 'Skip confirmation prompt',
|
|
19
|
+
required: false,
|
|
20
|
+
default: false,
|
|
21
|
+
}),
|
|
22
|
+
output: Flags.string({
|
|
23
|
+
char: 'o',
|
|
24
|
+
description: 'Output format',
|
|
25
|
+
required: false,
|
|
26
|
+
default: 'summary',
|
|
27
|
+
options: ['summary', 'json'],
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
static description = 'Delete a workspace via the Xano Metadata API. Cannot delete workspaces with active tenants.';
|
|
31
|
+
static examples = [
|
|
32
|
+
`$ xano workspace delete 123
|
|
33
|
+
Are you sure you want to delete workspace 123? This action cannot be undone. (y/N) y
|
|
34
|
+
Deleted workspace 123
|
|
35
|
+
`,
|
|
36
|
+
`$ xano workspace delete 123 --force
|
|
37
|
+
Deleted workspace 123
|
|
38
|
+
`,
|
|
39
|
+
`$ xano workspace delete 123 -f -o json
|
|
40
|
+
{
|
|
41
|
+
"deleted": true,
|
|
42
|
+
"workspace_id": 123
|
|
43
|
+
}
|
|
44
|
+
`,
|
|
45
|
+
];
|
|
46
|
+
async run() {
|
|
47
|
+
const { args, flags } = await this.parse(WorkspaceDelete);
|
|
48
|
+
// Get profile name (default or from flag/env)
|
|
49
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
50
|
+
// Load credentials
|
|
51
|
+
const credentials = this.loadCredentials();
|
|
52
|
+
// Get the profile configuration
|
|
53
|
+
if (!(profileName in credentials.profiles)) {
|
|
54
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
55
|
+
`Create a profile using 'xano profile create'`);
|
|
56
|
+
}
|
|
57
|
+
const profile = credentials.profiles[profileName];
|
|
58
|
+
// Validate required fields
|
|
59
|
+
if (!profile.instance_origin) {
|
|
60
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
61
|
+
}
|
|
62
|
+
if (!profile.access_token) {
|
|
63
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
64
|
+
}
|
|
65
|
+
const workspaceId = args.workspace_id;
|
|
66
|
+
// Confirmation prompt unless --force is used
|
|
67
|
+
if (!flags.force) {
|
|
68
|
+
const confirmed = await this.confirm(`Are you sure you want to delete workspace ${workspaceId}? This action cannot be undone.`);
|
|
69
|
+
if (!confirmed) {
|
|
70
|
+
this.log('Deletion cancelled.');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Construct the API URL
|
|
75
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}`;
|
|
76
|
+
// Delete workspace via the API
|
|
77
|
+
try {
|
|
78
|
+
const response = await fetch(apiUrl, {
|
|
79
|
+
method: 'DELETE',
|
|
80
|
+
headers: {
|
|
81
|
+
'accept': 'application/json',
|
|
82
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
const errorText = await response.text();
|
|
87
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
88
|
+
}
|
|
89
|
+
// Output results
|
|
90
|
+
if (flags.output === 'json') {
|
|
91
|
+
this.log(JSON.stringify({ deleted: true, workspace_id: workspaceId }, null, 2));
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
this.log(`Deleted workspace ${workspaceId}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof Error) {
|
|
99
|
+
this.error(`Failed to delete workspace: ${error.message}`);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
this.error(`Failed to delete workspace: ${String(error)}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async confirm(message) {
|
|
107
|
+
// Use readline for simple yes/no confirmation
|
|
108
|
+
const readline = await import('node:readline');
|
|
109
|
+
const rl = readline.createInterface({
|
|
110
|
+
input: process.stdin,
|
|
111
|
+
output: process.stdout,
|
|
112
|
+
});
|
|
113
|
+
return new Promise((resolve) => {
|
|
114
|
+
rl.question(`${message} (y/N) `, (answer) => {
|
|
115
|
+
rl.close();
|
|
116
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
loadCredentials() {
|
|
121
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
122
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
123
|
+
// Check if credentials file exists
|
|
124
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
125
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
126
|
+
`Create a profile using 'xano profile create'`);
|
|
127
|
+
}
|
|
128
|
+
// Read credentials file
|
|
129
|
+
try {
|
|
130
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
131
|
+
const parsed = yaml.load(fileContent);
|
|
132
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
133
|
+
this.error('Credentials file has invalid format.');
|
|
134
|
+
}
|
|
135
|
+
return parsed;
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class WorkspaceEdit extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
workspace_id: import("@oclif/core/interfaces").Arg<number | undefined, {
|
|
5
|
+
max?: number;
|
|
6
|
+
min?: number;
|
|
7
|
+
}>;
|
|
8
|
+
};
|
|
9
|
+
static flags: {
|
|
10
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
swagger: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
'require-token': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
18
|
+
static description: string;
|
|
19
|
+
static examples: string[];
|
|
20
|
+
run(): Promise<void>;
|
|
21
|
+
private loadCredentials;
|
|
22
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as yaml from 'js-yaml';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class WorkspaceEdit extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
workspace_id: Args.integer({
|
|
10
|
+
description: 'Workspace ID to edit (uses profile workspace if not provided)',
|
|
11
|
+
required: false,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static flags = {
|
|
15
|
+
...BaseCommand.baseFlags,
|
|
16
|
+
name: Flags.string({
|
|
17
|
+
char: 'n',
|
|
18
|
+
description: 'New name for the workspace',
|
|
19
|
+
required: false,
|
|
20
|
+
}),
|
|
21
|
+
description: Flags.string({
|
|
22
|
+
char: 'd',
|
|
23
|
+
description: 'New description for the workspace',
|
|
24
|
+
required: false,
|
|
25
|
+
}),
|
|
26
|
+
swagger: Flags.boolean({
|
|
27
|
+
description: 'Enable or disable swagger documentation',
|
|
28
|
+
required: false,
|
|
29
|
+
allowNo: true,
|
|
30
|
+
}),
|
|
31
|
+
'require-token': Flags.boolean({
|
|
32
|
+
description: 'Whether to require a token for documentation access',
|
|
33
|
+
required: false,
|
|
34
|
+
allowNo: true,
|
|
35
|
+
}),
|
|
36
|
+
output: Flags.string({
|
|
37
|
+
char: 'o',
|
|
38
|
+
description: 'Output format',
|
|
39
|
+
required: false,
|
|
40
|
+
default: 'summary',
|
|
41
|
+
options: ['summary', 'json'],
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
static description = 'Edit an existing workspace via the Xano Metadata API';
|
|
45
|
+
static examples = [
|
|
46
|
+
`$ xano workspace edit 123 --name "new-name"
|
|
47
|
+
Updated workspace: new-name (ID: 123)
|
|
48
|
+
`,
|
|
49
|
+
`$ xano workspace edit --name "updated-workspace" --description "Updated description"
|
|
50
|
+
Updated workspace: updated-workspace (ID: 123)
|
|
51
|
+
Description: Updated description
|
|
52
|
+
`,
|
|
53
|
+
`$ xano workspace edit 123 --swagger --require-token
|
|
54
|
+
Updated workspace: my-workspace (ID: 123)
|
|
55
|
+
Swagger: enabled
|
|
56
|
+
Require Token: true
|
|
57
|
+
`,
|
|
58
|
+
`$ xano workspace edit 123 --no-swagger -o json
|
|
59
|
+
{
|
|
60
|
+
"id": 123,
|
|
61
|
+
"name": "my-workspace",
|
|
62
|
+
"swagger": false
|
|
63
|
+
}
|
|
64
|
+
`,
|
|
65
|
+
];
|
|
66
|
+
async run() {
|
|
67
|
+
const { args, flags } = await this.parse(WorkspaceEdit);
|
|
68
|
+
// Get profile name (default or from flag/env)
|
|
69
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
70
|
+
// Load credentials
|
|
71
|
+
const credentials = this.loadCredentials();
|
|
72
|
+
// Get the profile configuration
|
|
73
|
+
if (!(profileName in credentials.profiles)) {
|
|
74
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
75
|
+
`Create a profile using 'xano profile create'`);
|
|
76
|
+
}
|
|
77
|
+
const profile = credentials.profiles[profileName];
|
|
78
|
+
// Validate required fields
|
|
79
|
+
if (!profile.instance_origin) {
|
|
80
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
81
|
+
}
|
|
82
|
+
if (!profile.access_token) {
|
|
83
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
84
|
+
}
|
|
85
|
+
// Get workspace ID from args or profile
|
|
86
|
+
const workspaceId = args.workspace_id || profile.workspace;
|
|
87
|
+
if (!workspaceId) {
|
|
88
|
+
this.error('No workspace ID provided. Either pass a workspace ID as an argument or set one in your profile.\n' +
|
|
89
|
+
'Usage: xano workspace edit <workspace_id> [flags]');
|
|
90
|
+
}
|
|
91
|
+
// Build request body - only include fields that were specified
|
|
92
|
+
const body = {};
|
|
93
|
+
if (flags.name !== undefined) {
|
|
94
|
+
body.name = flags.name;
|
|
95
|
+
}
|
|
96
|
+
if (flags.description !== undefined) {
|
|
97
|
+
body.description = flags.description;
|
|
98
|
+
}
|
|
99
|
+
if (flags.swagger !== undefined) {
|
|
100
|
+
body.swagger = flags.swagger;
|
|
101
|
+
}
|
|
102
|
+
if (flags['require-token'] !== undefined) {
|
|
103
|
+
body.documentation = { require_token: flags['require-token'] };
|
|
104
|
+
}
|
|
105
|
+
// Check if at least one field is being updated
|
|
106
|
+
if (Object.keys(body).length === 0) {
|
|
107
|
+
this.error('No fields specified to update. Use --name, --description, --swagger, or --require-token flags.\n' +
|
|
108
|
+
'Example: xano workspace edit 123 --name "new-name"');
|
|
109
|
+
}
|
|
110
|
+
// Construct the API URL
|
|
111
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}`;
|
|
112
|
+
// Update workspace via the API
|
|
113
|
+
try {
|
|
114
|
+
const response = await fetch(apiUrl, {
|
|
115
|
+
method: 'PUT',
|
|
116
|
+
headers: {
|
|
117
|
+
'accept': 'application/json',
|
|
118
|
+
'content-type': 'application/json',
|
|
119
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
120
|
+
},
|
|
121
|
+
body: JSON.stringify(body),
|
|
122
|
+
});
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
const errorText = await response.text();
|
|
125
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
126
|
+
}
|
|
127
|
+
const workspace = await response.json();
|
|
128
|
+
// Output results
|
|
129
|
+
if (flags.output === 'json') {
|
|
130
|
+
this.log(JSON.stringify(workspace, null, 2));
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// summary format
|
|
134
|
+
this.log(`Updated workspace: ${workspace.name} (ID: ${workspace.id})`);
|
|
135
|
+
if (workspace.description) {
|
|
136
|
+
this.log(` Description: ${workspace.description}`);
|
|
137
|
+
}
|
|
138
|
+
if (workspace.swagger !== undefined) {
|
|
139
|
+
this.log(` Swagger: ${workspace.swagger ? 'enabled' : 'disabled'}`);
|
|
140
|
+
}
|
|
141
|
+
if (workspace.documentation?.require_token !== undefined) {
|
|
142
|
+
this.log(` Require Token: ${workspace.documentation.require_token}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (error instanceof Error) {
|
|
148
|
+
this.error(`Failed to update workspace: ${error.message}`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
this.error(`Failed to update workspace: ${String(error)}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
loadCredentials() {
|
|
156
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
157
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
158
|
+
// Check if credentials file exists
|
|
159
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
160
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
161
|
+
`Create a profile using 'xano profile create'`);
|
|
162
|
+
}
|
|
163
|
+
// Read credentials file
|
|
164
|
+
try {
|
|
165
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
166
|
+
const parsed = yaml.load(fileContent);
|
|
167
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
168
|
+
this.error('Credentials file has invalid format.');
|
|
169
|
+
}
|
|
170
|
+
return parsed;
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class WorkspaceGet extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
workspace_id: import("@oclif/core/interfaces").Arg<number | undefined, {
|
|
5
|
+
max?: number;
|
|
6
|
+
min?: number;
|
|
7
|
+
}>;
|
|
8
|
+
};
|
|
9
|
+
static flags: {
|
|
10
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
};
|
|
14
|
+
static description: string;
|
|
15
|
+
static examples: string[];
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
private loadCredentials;
|
|
18
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as yaml from 'js-yaml';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class WorkspaceGet extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
workspace_id: Args.integer({
|
|
10
|
+
description: 'Workspace ID to get details for (uses profile workspace if not provided)',
|
|
11
|
+
required: false,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static flags = {
|
|
15
|
+
...BaseCommand.baseFlags,
|
|
16
|
+
output: Flags.string({
|
|
17
|
+
char: 'o',
|
|
18
|
+
description: 'Output format',
|
|
19
|
+
required: false,
|
|
20
|
+
default: 'summary',
|
|
21
|
+
options: ['summary', 'json'],
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
static description = 'Get details of a specific workspace from the Xano Metadata API';
|
|
25
|
+
static examples = [
|
|
26
|
+
`$ xano workspace get 123
|
|
27
|
+
Workspace: my-workspace (ID: 123)
|
|
28
|
+
Description: My workspace description
|
|
29
|
+
Created: 2024-01-15
|
|
30
|
+
`,
|
|
31
|
+
`$ xano workspace get --output json
|
|
32
|
+
{
|
|
33
|
+
"id": 123,
|
|
34
|
+
"name": "my-workspace",
|
|
35
|
+
"description": "My workspace description"
|
|
36
|
+
}
|
|
37
|
+
`,
|
|
38
|
+
`$ xano workspace get 456 -p production -o json
|
|
39
|
+
{
|
|
40
|
+
"id": 456,
|
|
41
|
+
"name": "production-workspace"
|
|
42
|
+
}
|
|
43
|
+
`,
|
|
44
|
+
];
|
|
45
|
+
async run() {
|
|
46
|
+
const { args, flags } = await this.parse(WorkspaceGet);
|
|
47
|
+
// Get profile name (default or from flag/env)
|
|
48
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
49
|
+
// Load credentials
|
|
50
|
+
const credentials = this.loadCredentials();
|
|
51
|
+
// Get the profile configuration
|
|
52
|
+
if (!(profileName in credentials.profiles)) {
|
|
53
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
54
|
+
`Create a profile using 'xano profile create'`);
|
|
55
|
+
}
|
|
56
|
+
const profile = credentials.profiles[profileName];
|
|
57
|
+
// Validate required fields
|
|
58
|
+
if (!profile.instance_origin) {
|
|
59
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
60
|
+
}
|
|
61
|
+
if (!profile.access_token) {
|
|
62
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
63
|
+
}
|
|
64
|
+
// Get workspace ID from args or profile
|
|
65
|
+
const workspaceId = args.workspace_id || profile.workspace;
|
|
66
|
+
if (!workspaceId) {
|
|
67
|
+
this.error('No workspace ID provided. Either pass a workspace ID as an argument or set one in your profile.\n' +
|
|
68
|
+
'Usage: xano workspace get <workspace_id>');
|
|
69
|
+
}
|
|
70
|
+
// Construct the API URL
|
|
71
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}`;
|
|
72
|
+
// Fetch workspace from the API
|
|
73
|
+
try {
|
|
74
|
+
const response = await fetch(apiUrl, {
|
|
75
|
+
method: 'GET',
|
|
76
|
+
headers: {
|
|
77
|
+
'accept': 'application/json',
|
|
78
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
const errorText = await response.text();
|
|
83
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
84
|
+
}
|
|
85
|
+
const workspace = await response.json();
|
|
86
|
+
// Output results
|
|
87
|
+
if (flags.output === 'json') {
|
|
88
|
+
this.log(JSON.stringify(workspace, null, 2));
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// summary format
|
|
92
|
+
this.log(`Workspace: ${workspace.name} (ID: ${workspace.id})`);
|
|
93
|
+
if (workspace.description) {
|
|
94
|
+
this.log(` Description: ${workspace.description}`);
|
|
95
|
+
}
|
|
96
|
+
if (workspace.created_at) {
|
|
97
|
+
const createdDate = new Date(workspace.created_at * 1000).toISOString().split('T')[0];
|
|
98
|
+
this.log(` Created: ${createdDate}`);
|
|
99
|
+
}
|
|
100
|
+
if (workspace.updated_at) {
|
|
101
|
+
const updatedDate = new Date(workspace.updated_at * 1000).toISOString().split('T')[0];
|
|
102
|
+
this.log(` Updated: ${updatedDate}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (error instanceof Error) {
|
|
108
|
+
this.error(`Failed to fetch workspace: ${error.message}`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
this.error(`Failed to fetch workspace: ${String(error)}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
loadCredentials() {
|
|
116
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
117
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
118
|
+
// Check if credentials file exists
|
|
119
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
120
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
121
|
+
`Create a profile using 'xano profile create'`);
|
|
122
|
+
}
|
|
123
|
+
// Read credentials file
|
|
124
|
+
try {
|
|
125
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
126
|
+
const parsed = yaml.load(fileContent);
|
|
127
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
128
|
+
this.error('Credentials file has invalid format.');
|
|
129
|
+
}
|
|
130
|
+
return parsed;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* HTTP client for Xano Run API
|
|
3
3
|
* Based on @xano/run-sdk HttpClient
|
|
4
4
|
*/
|
|
5
|
-
export declare const DEFAULT_RUN_BASE_URL = "https://app.xano.com/";
|
|
5
|
+
export declare const DEFAULT_RUN_BASE_URL = "https://app.dev.xano.com/";
|
|
6
6
|
export interface RunHttpClientConfig {
|
|
7
7
|
baseUrl: string;
|
|
8
8
|
authToken: string;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* HTTP client for Xano Run API
|
|
3
3
|
* Based on @xano/run-sdk HttpClient
|
|
4
4
|
*/
|
|
5
|
-
export const DEFAULT_RUN_BASE_URL = 'https://app.xano.com/';
|
|
5
|
+
export const DEFAULT_RUN_BASE_URL = 'https://app.dev.xano.com/';
|
|
6
6
|
export class RunHttpClient {
|
|
7
7
|
config;
|
|
8
8
|
constructor(config) {
|