@xano/cli 0.0.21 → 0.0.22
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/base-command.d.ts +1 -1
- package/dist/base-command.js +6 -6
- package/dist/commands/branch/create/index.d.ts +17 -0
- package/dist/commands/branch/create/index.js +164 -0
- package/dist/commands/branch/delete/index.d.ts +18 -0
- package/dist/commands/branch/delete/index.js +156 -0
- package/dist/commands/branch/edit/index.d.ts +19 -0
- package/dist/commands/branch/edit/index.js +166 -0
- package/dist/commands/branch/get/index.d.ts +16 -0
- package/dist/commands/branch/get/index.js +135 -0
- package/dist/commands/branch/list/index.d.ts +18 -0
- package/dist/commands/branch/list/index.js +138 -0
- package/dist/commands/branch/set-live/index.d.ts +18 -0
- package/dist/commands/branch/set-live/index.js +155 -0
- package/dist/commands/function/create/index.d.ts +6 -6
- package/dist/commands/function/create/index.js +55 -55
- package/dist/commands/function/edit/index.d.ts +10 -10
- package/dist/commands/function/edit/index.js +155 -162
- package/dist/commands/function/get/index.d.ts +5 -5
- package/dist/commands/function/get/index.js +55 -60
- package/dist/commands/function/list/index.d.ts +5 -5
- package/dist/commands/function/list/index.js +52 -52
- package/dist/commands/profile/create/index.d.ts +6 -6
- package/dist/commands/profile/create/index.js +37 -37
- package/dist/commands/profile/delete/index.d.ts +2 -2
- package/dist/commands/profile/delete/index.js +9 -9
- package/dist/commands/profile/edit/index.d.ts +7 -7
- package/dist/commands/profile/edit/index.js +47 -47
- package/dist/commands/profile/get-default/index.js +1 -1
- package/dist/commands/profile/list/index.d.ts +2 -2
- package/dist/commands/profile/list/index.js +9 -9
- package/dist/commands/profile/me/index.d.ts +3 -3
- package/dist/commands/profile/me/index.js +21 -21
- package/dist/commands/profile/project/index.js +1 -1
- package/dist/commands/profile/set-default/index.js +1 -1
- package/dist/commands/profile/token/index.js +1 -1
- package/dist/commands/profile/wizard/index.d.ts +4 -4
- package/dist/commands/profile/wizard/index.js +118 -122
- package/dist/commands/run/env/delete/index.d.ts +2 -2
- package/dist/commands/run/env/delete/index.js +9 -9
- package/dist/commands/run/env/get/index.d.ts +2 -2
- package/dist/commands/run/env/get/index.js +10 -10
- package/dist/commands/run/env/list/index.d.ts +2 -2
- package/dist/commands/run/env/list/index.js +16 -18
- package/dist/commands/run/env/set/index.d.ts +2 -2
- package/dist/commands/run/env/set/index.js +4 -4
- package/dist/commands/run/exec/index.d.ts +11 -11
- package/dist/commands/run/exec/index.js +109 -109
- package/dist/commands/run/info/index.d.ts +4 -4
- package/dist/commands/run/info/index.js +26 -26
- package/dist/commands/run/projects/create/index.d.ts +3 -3
- package/dist/commands/run/projects/create/index.js +22 -22
- package/dist/commands/run/projects/delete/index.d.ts +2 -2
- package/dist/commands/run/projects/delete/index.js +9 -9
- package/dist/commands/run/projects/list/index.d.ts +2 -2
- package/dist/commands/run/projects/list/index.js +11 -11
- package/dist/commands/run/projects/update/index.d.ts +3 -3
- package/dist/commands/run/projects/update/index.js +20 -20
- package/dist/commands/run/secrets/delete/index.d.ts +2 -2
- package/dist/commands/run/secrets/delete/index.js +9 -9
- package/dist/commands/run/secrets/get/index.d.ts +2 -2
- package/dist/commands/run/secrets/get/index.js +10 -10
- package/dist/commands/run/secrets/list/index.d.ts +2 -2
- package/dist/commands/run/secrets/list/index.js +21 -23
- package/dist/commands/run/secrets/set/index.d.ts +3 -3
- package/dist/commands/run/secrets/set/index.js +15 -15
- package/dist/commands/run/sessions/delete/index.d.ts +2 -2
- package/dist/commands/run/sessions/delete/index.js +9 -9
- package/dist/commands/run/sessions/get/index.d.ts +2 -2
- package/dist/commands/run/sessions/get/index.js +10 -10
- package/dist/commands/run/sessions/list/index.d.ts +2 -2
- package/dist/commands/run/sessions/list/index.js +10 -10
- package/dist/commands/run/sessions/start/index.d.ts +2 -2
- package/dist/commands/run/sessions/start/index.js +10 -10
- package/dist/commands/run/sessions/stop/index.d.ts +2 -2
- package/dist/commands/run/sessions/stop/index.js +10 -10
- package/dist/commands/run/sink/get/index.d.ts +2 -2
- package/dist/commands/run/sink/get/index.js +10 -10
- package/dist/commands/static_host/build/create/index.d.ts +4 -4
- package/dist/commands/static_host/build/create/index.js +33 -33
- package/dist/commands/static_host/build/get/index.d.ts +4 -4
- package/dist/commands/static_host/build/get/index.js +20 -20
- package/dist/commands/static_host/build/list/index.d.ts +3 -3
- package/dist/commands/static_host/build/list/index.js +31 -31
- package/dist/commands/static_host/list/index.d.ts +3 -3
- package/dist/commands/static_host/list/index.js +31 -31
- package/dist/commands/workspace/create/index.d.ts +3 -3
- package/dist/commands/workspace/create/index.js +24 -24
- package/dist/commands/workspace/delete/index.d.ts +2 -2
- package/dist/commands/workspace/delete/index.js +18 -18
- package/dist/commands/workspace/edit/index.d.ts +5 -5
- package/dist/commands/workspace/edit/index.js +34 -34
- package/dist/commands/workspace/get/index.d.ts +2 -2
- package/dist/commands/workspace/get/index.js +12 -12
- package/dist/commands/workspace/list/index.d.ts +2 -2
- package/dist/commands/workspace/list/index.js +15 -15
- package/dist/commands/workspace/pull/index.d.ts +4 -4
- package/dist/commands/workspace/pull/index.js +46 -51
- package/dist/commands/workspace/push/index.js +3 -3
- package/dist/help.d.ts +1 -1
- package/dist/lib/base-run-command.d.ts +4 -4
- package/dist/lib/base-run-command.js +3 -3
- package/dist/lib/run-http-client.d.ts +20 -20
- package/dist/lib/run-http-client.js +71 -71
- package/dist/lib/run-types.d.ts +80 -80
- package/oclif.manifest.json +1336 -815
- package/package.json +1 -1
package/dist/base-command.d.ts
CHANGED
|
@@ -8,6 +8,6 @@ export default abstract class BaseCommand extends Command {
|
|
|
8
8
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
10
|
};
|
|
11
|
-
protected getProfile(): string | undefined;
|
|
12
11
|
protected getDefaultProfile(): string;
|
|
12
|
+
protected getProfile(): string | undefined;
|
|
13
13
|
}
|
package/dist/base-command.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
2
3
|
import * as fs from 'node:fs';
|
|
3
4
|
import * as os from 'node:os';
|
|
4
5
|
import * as path from 'node:path';
|
|
5
|
-
import * as yaml from 'js-yaml';
|
|
6
6
|
export default class BaseCommand extends Command {
|
|
7
7
|
static baseFlags = {
|
|
8
8
|
profile: Flags.string({
|
|
@@ -13,18 +13,14 @@ export default class BaseCommand extends Command {
|
|
|
13
13
|
}),
|
|
14
14
|
verbose: Flags.boolean({
|
|
15
15
|
char: 'v',
|
|
16
|
+
default: false,
|
|
16
17
|
description: 'Show detailed request/response information',
|
|
17
18
|
env: 'XANO_VERBOSE',
|
|
18
19
|
required: false,
|
|
19
|
-
default: false,
|
|
20
20
|
}),
|
|
21
21
|
};
|
|
22
22
|
// Override the flags property to include baseFlags
|
|
23
23
|
static flags = BaseCommand.baseFlags;
|
|
24
|
-
// Helper method to get the profile flag value
|
|
25
|
-
getProfile() {
|
|
26
|
-
return this.flags?.profile;
|
|
27
|
-
}
|
|
28
24
|
// Helper method to get the default profile from credentials file
|
|
29
25
|
getDefaultProfile() {
|
|
30
26
|
try {
|
|
@@ -44,4 +40,8 @@ export default class BaseCommand extends Command {
|
|
|
44
40
|
return 'default';
|
|
45
41
|
}
|
|
46
42
|
}
|
|
43
|
+
// Helper method to get the profile flag value
|
|
44
|
+
getProfile() {
|
|
45
|
+
return this.flags?.profile;
|
|
46
|
+
}
|
|
47
47
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class BranchCreate extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
color: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
label: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
source: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<number | undefined, 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
|
+
run(): Promise<void>;
|
|
16
|
+
private loadCredentials;
|
|
17
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class BranchCreate extends BaseCommand {
|
|
8
|
+
static description = 'Create a new branch by cloning from an existing branch';
|
|
9
|
+
static examples = [
|
|
10
|
+
`$ xano branch create --label dev
|
|
11
|
+
Created branch: dev
|
|
12
|
+
Cloned from: v1
|
|
13
|
+
`,
|
|
14
|
+
`$ xano branch create -l feature-auth -s dev -d "Authentication feature"
|
|
15
|
+
Created branch: feature-auth
|
|
16
|
+
Cloned from: dev
|
|
17
|
+
Description: Authentication feature
|
|
18
|
+
`,
|
|
19
|
+
`$ xano branch create --label staging --color "#ebc346" --output json
|
|
20
|
+
{
|
|
21
|
+
"created_at": "2024-02-11T10:00:00Z",
|
|
22
|
+
"label": "staging",
|
|
23
|
+
"backup": false,
|
|
24
|
+
"live": false
|
|
25
|
+
}
|
|
26
|
+
`,
|
|
27
|
+
];
|
|
28
|
+
static flags = {
|
|
29
|
+
...BaseCommand.baseFlags,
|
|
30
|
+
color: Flags.string({
|
|
31
|
+
char: 'c',
|
|
32
|
+
description: 'Color hex code for the branch (e.g., "#ebc346")',
|
|
33
|
+
required: false,
|
|
34
|
+
}),
|
|
35
|
+
description: Flags.string({
|
|
36
|
+
char: 'd',
|
|
37
|
+
description: 'Description for the new branch',
|
|
38
|
+
required: false,
|
|
39
|
+
}),
|
|
40
|
+
label: Flags.string({
|
|
41
|
+
char: 'l',
|
|
42
|
+
description: 'Label for the new branch',
|
|
43
|
+
required: true,
|
|
44
|
+
}),
|
|
45
|
+
output: Flags.string({
|
|
46
|
+
char: 'o',
|
|
47
|
+
default: 'summary',
|
|
48
|
+
description: 'Output format',
|
|
49
|
+
options: ['summary', 'json'],
|
|
50
|
+
required: false,
|
|
51
|
+
}),
|
|
52
|
+
source: Flags.string({
|
|
53
|
+
char: 's',
|
|
54
|
+
default: 'v1',
|
|
55
|
+
description: 'Source branch to clone from (defaults to "v1")',
|
|
56
|
+
required: false,
|
|
57
|
+
}),
|
|
58
|
+
workspace: Flags.integer({
|
|
59
|
+
char: 'w',
|
|
60
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
61
|
+
required: false,
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
async run() {
|
|
65
|
+
const { flags } = await this.parse(BranchCreate);
|
|
66
|
+
// Get profile name (default or from flag/env)
|
|
67
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
68
|
+
// Load credentials
|
|
69
|
+
const credentials = this.loadCredentials();
|
|
70
|
+
// Get the profile configuration
|
|
71
|
+
if (!(profileName in credentials.profiles)) {
|
|
72
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
73
|
+
`Create a profile using 'xano profile create'`);
|
|
74
|
+
}
|
|
75
|
+
const profile = credentials.profiles[profileName];
|
|
76
|
+
// Validate required fields
|
|
77
|
+
if (!profile.instance_origin) {
|
|
78
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
79
|
+
}
|
|
80
|
+
if (!profile.access_token) {
|
|
81
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
82
|
+
}
|
|
83
|
+
// Get workspace ID from flag or profile
|
|
84
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
85
|
+
if (!workspaceId) {
|
|
86
|
+
this.error('No workspace ID provided. Either use --workspace flag or set one in your profile.\n' +
|
|
87
|
+
'Usage: xano branch create --label <label> [--workspace <workspace_id>]');
|
|
88
|
+
}
|
|
89
|
+
// Build request body
|
|
90
|
+
const body = {
|
|
91
|
+
label: flags.label,
|
|
92
|
+
source_branch: flags.source,
|
|
93
|
+
};
|
|
94
|
+
if (flags.description) {
|
|
95
|
+
body.description = flags.description;
|
|
96
|
+
}
|
|
97
|
+
if (flags.color) {
|
|
98
|
+
body.color = flags.color;
|
|
99
|
+
}
|
|
100
|
+
// Construct the API URL
|
|
101
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/branch`;
|
|
102
|
+
// Create branch via the API
|
|
103
|
+
try {
|
|
104
|
+
const response = await fetch(apiUrl, {
|
|
105
|
+
body: JSON.stringify(body),
|
|
106
|
+
headers: {
|
|
107
|
+
'accept': 'application/json',
|
|
108
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
109
|
+
'content-type': 'application/json',
|
|
110
|
+
},
|
|
111
|
+
method: 'POST',
|
|
112
|
+
});
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
const errorText = await response.text();
|
|
115
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
116
|
+
}
|
|
117
|
+
const branch = await response.json();
|
|
118
|
+
// Output results
|
|
119
|
+
if (flags.output === 'json') {
|
|
120
|
+
this.log(JSON.stringify(branch, null, 2));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// summary format
|
|
124
|
+
this.log(`Created branch: ${branch.label}`);
|
|
125
|
+
this.log(` Cloned from: ${flags.source}`);
|
|
126
|
+
if (flags.description) {
|
|
127
|
+
this.log(` Description: ${flags.description}`);
|
|
128
|
+
}
|
|
129
|
+
if (flags.color) {
|
|
130
|
+
this.log(` Color: ${flags.color}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (error instanceof Error) {
|
|
136
|
+
this.error(`Failed to create branch: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
this.error(`Failed to create branch: ${String(error)}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
loadCredentials() {
|
|
144
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
145
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
146
|
+
// Check if credentials file exists
|
|
147
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
148
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
149
|
+
`Create a profile using 'xano profile create'`);
|
|
150
|
+
}
|
|
151
|
+
// Read credentials file
|
|
152
|
+
try {
|
|
153
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
154
|
+
const parsed = yaml.load(fileContent);
|
|
155
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
156
|
+
this.error('Credentials file has invalid format.');
|
|
157
|
+
}
|
|
158
|
+
return parsed;
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class BranchDelete extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
branch_label: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<number | undefined, 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
|
+
run(): Promise<void>;
|
|
16
|
+
private confirm;
|
|
17
|
+
private loadCredentials;
|
|
18
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class BranchDelete extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
branch_label: Args.string({
|
|
10
|
+
description: 'Branch label to delete (cannot delete "v1" or the live branch)',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static description = 'Delete a branch (cannot delete "v1" or the live branch)';
|
|
15
|
+
static examples = [
|
|
16
|
+
`$ xano branch delete feature-old
|
|
17
|
+
Are you sure you want to delete branch 'feature-old'? This action cannot be undone. (y/N) y
|
|
18
|
+
Deleted branch: feature-old
|
|
19
|
+
`,
|
|
20
|
+
`$ xano branch delete dev --force
|
|
21
|
+
Deleted branch: dev
|
|
22
|
+
`,
|
|
23
|
+
`$ xano branch delete staging -f -o json
|
|
24
|
+
{
|
|
25
|
+
"deleted": true,
|
|
26
|
+
"branch_label": "staging"
|
|
27
|
+
}
|
|
28
|
+
`,
|
|
29
|
+
];
|
|
30
|
+
static flags = {
|
|
31
|
+
...BaseCommand.baseFlags,
|
|
32
|
+
force: Flags.boolean({
|
|
33
|
+
char: 'f',
|
|
34
|
+
default: false,
|
|
35
|
+
description: 'Skip confirmation prompt',
|
|
36
|
+
required: false,
|
|
37
|
+
}),
|
|
38
|
+
output: Flags.string({
|
|
39
|
+
char: 'o',
|
|
40
|
+
default: 'summary',
|
|
41
|
+
description: 'Output format',
|
|
42
|
+
options: ['summary', 'json'],
|
|
43
|
+
required: false,
|
|
44
|
+
}),
|
|
45
|
+
workspace: Flags.integer({
|
|
46
|
+
char: 'w',
|
|
47
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
48
|
+
required: false,
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
async run() {
|
|
52
|
+
const { args, flags } = await this.parse(BranchDelete);
|
|
53
|
+
// Get profile name (default or from flag/env)
|
|
54
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
55
|
+
// Load credentials
|
|
56
|
+
const credentials = this.loadCredentials();
|
|
57
|
+
// Get the profile configuration
|
|
58
|
+
if (!(profileName in credentials.profiles)) {
|
|
59
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
60
|
+
`Create a profile using 'xano profile create'`);
|
|
61
|
+
}
|
|
62
|
+
const profile = credentials.profiles[profileName];
|
|
63
|
+
// Validate required fields
|
|
64
|
+
if (!profile.instance_origin) {
|
|
65
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
66
|
+
}
|
|
67
|
+
if (!profile.access_token) {
|
|
68
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
69
|
+
}
|
|
70
|
+
// Get workspace ID from flag or profile
|
|
71
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
72
|
+
if (!workspaceId) {
|
|
73
|
+
this.error('No workspace ID provided. Either use --workspace flag or set one in your profile.\n' +
|
|
74
|
+
'Usage: xano branch delete <branch_label> [--workspace <workspace_id>]');
|
|
75
|
+
}
|
|
76
|
+
const branchLabel = args.branch_label;
|
|
77
|
+
// Warn about protected branches
|
|
78
|
+
if (branchLabel === 'v1') {
|
|
79
|
+
this.error('Cannot delete the "v1" branch. This is the default branch and cannot be removed.');
|
|
80
|
+
}
|
|
81
|
+
// Confirmation prompt unless --force is used
|
|
82
|
+
if (!flags.force) {
|
|
83
|
+
const confirmed = await this.confirm(`Are you sure you want to delete branch '${branchLabel}'? This action cannot be undone.`);
|
|
84
|
+
if (!confirmed) {
|
|
85
|
+
this.log('Deletion cancelled.');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Construct the API URL
|
|
90
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/branch/${encodeURIComponent(branchLabel)}`;
|
|
91
|
+
// Delete branch via the API
|
|
92
|
+
try {
|
|
93
|
+
const response = await fetch(apiUrl, {
|
|
94
|
+
headers: {
|
|
95
|
+
'accept': 'application/json',
|
|
96
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
97
|
+
},
|
|
98
|
+
method: 'DELETE',
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
const errorText = await response.text();
|
|
102
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
103
|
+
}
|
|
104
|
+
// Output results
|
|
105
|
+
if (flags.output === 'json') {
|
|
106
|
+
this.log(JSON.stringify({ branch_label: branchLabel, deleted: true }, null, 2));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
this.log(`Deleted branch: ${branchLabel}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
if (error instanceof Error) {
|
|
114
|
+
this.error(`Failed to delete branch: ${error.message}`);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
this.error(`Failed to delete branch: ${String(error)}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async confirm(message) {
|
|
122
|
+
// Use readline for simple yes/no confirmation
|
|
123
|
+
const readline = await import('node:readline');
|
|
124
|
+
const rl = readline.createInterface({
|
|
125
|
+
input: process.stdin,
|
|
126
|
+
output: process.stdout,
|
|
127
|
+
});
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
rl.question(`${message} (y/N) `, (answer) => {
|
|
130
|
+
rl.close();
|
|
131
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
loadCredentials() {
|
|
136
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
137
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
138
|
+
// Check if credentials file exists
|
|
139
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
140
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
141
|
+
`Create a profile using 'xano profile create'`);
|
|
142
|
+
}
|
|
143
|
+
// Read credentials file
|
|
144
|
+
try {
|
|
145
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
146
|
+
const parsed = yaml.load(fileContent);
|
|
147
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
148
|
+
this.error('Credentials file has invalid format.');
|
|
149
|
+
}
|
|
150
|
+
return parsed;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class BranchEdit extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
branch_label: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
color: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
label: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
private loadCredentials;
|
|
19
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class BranchEdit extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
branch_label: Args.string({
|
|
10
|
+
description: 'Branch label to edit (cannot edit "v1" label)',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static description = 'Update an existing branch (cannot update "v1" label)';
|
|
15
|
+
static examples = [
|
|
16
|
+
`$ xano branch edit dev --label development
|
|
17
|
+
Updated branch: development
|
|
18
|
+
`,
|
|
19
|
+
`$ xano branch edit feature-auth -l feature-authentication --color "#ff5733"
|
|
20
|
+
Updated branch: feature-authentication
|
|
21
|
+
Color: #ff5733
|
|
22
|
+
`,
|
|
23
|
+
`$ xano branch edit staging --description "Staging environment" -o json
|
|
24
|
+
{
|
|
25
|
+
"created_at": "2024-02-10T09:15:00Z",
|
|
26
|
+
"label": "staging",
|
|
27
|
+
"backup": false
|
|
28
|
+
}
|
|
29
|
+
`,
|
|
30
|
+
];
|
|
31
|
+
static flags = {
|
|
32
|
+
...BaseCommand.baseFlags,
|
|
33
|
+
color: Flags.string({
|
|
34
|
+
char: 'c',
|
|
35
|
+
description: 'New color hex code for the branch (e.g., "#ff5733")',
|
|
36
|
+
required: false,
|
|
37
|
+
}),
|
|
38
|
+
description: Flags.string({
|
|
39
|
+
char: 'd',
|
|
40
|
+
description: 'New description for the branch',
|
|
41
|
+
required: false,
|
|
42
|
+
}),
|
|
43
|
+
label: Flags.string({
|
|
44
|
+
char: 'l',
|
|
45
|
+
description: 'New label for the branch',
|
|
46
|
+
required: false,
|
|
47
|
+
}),
|
|
48
|
+
output: Flags.string({
|
|
49
|
+
char: 'o',
|
|
50
|
+
default: 'summary',
|
|
51
|
+
description: 'Output format',
|
|
52
|
+
options: ['summary', 'json'],
|
|
53
|
+
required: false,
|
|
54
|
+
}),
|
|
55
|
+
workspace: Flags.integer({
|
|
56
|
+
char: 'w',
|
|
57
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
58
|
+
required: false,
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
async run() {
|
|
62
|
+
const { args, flags } = await this.parse(BranchEdit);
|
|
63
|
+
// Get profile name (default or from flag/env)
|
|
64
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
65
|
+
// Load credentials
|
|
66
|
+
const credentials = this.loadCredentials();
|
|
67
|
+
// Get the profile configuration
|
|
68
|
+
if (!(profileName in credentials.profiles)) {
|
|
69
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
70
|
+
`Create a profile using 'xano profile create'`);
|
|
71
|
+
}
|
|
72
|
+
const profile = credentials.profiles[profileName];
|
|
73
|
+
// Validate required fields
|
|
74
|
+
if (!profile.instance_origin) {
|
|
75
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
76
|
+
}
|
|
77
|
+
if (!profile.access_token) {
|
|
78
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
79
|
+
}
|
|
80
|
+
// Get workspace ID from flag or profile
|
|
81
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
82
|
+
if (!workspaceId) {
|
|
83
|
+
this.error('No workspace ID provided. Either use --workspace flag or set one in your profile.\n' +
|
|
84
|
+
'Usage: xano branch edit <branch_label> [--workspace <workspace_id>]');
|
|
85
|
+
}
|
|
86
|
+
const branchLabel = args.branch_label;
|
|
87
|
+
// Build request body - only include fields that were specified
|
|
88
|
+
const body = {};
|
|
89
|
+
if (flags.label !== undefined) {
|
|
90
|
+
body.label = flags.label;
|
|
91
|
+
}
|
|
92
|
+
if (flags.description !== undefined) {
|
|
93
|
+
body.description = flags.description;
|
|
94
|
+
}
|
|
95
|
+
if (flags.color !== undefined) {
|
|
96
|
+
body.color = flags.color;
|
|
97
|
+
}
|
|
98
|
+
// Check if at least one field is being updated
|
|
99
|
+
if (Object.keys(body).length === 0) {
|
|
100
|
+
this.error('No fields specified to update. Use --label, --description, or --color flags.\n' +
|
|
101
|
+
'Example: xano branch edit dev --label development');
|
|
102
|
+
}
|
|
103
|
+
// Construct the API URL
|
|
104
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/branch/${encodeURIComponent(branchLabel)}`;
|
|
105
|
+
// Update branch via the API
|
|
106
|
+
try {
|
|
107
|
+
const response = await fetch(apiUrl, {
|
|
108
|
+
body: JSON.stringify(body),
|
|
109
|
+
headers: {
|
|
110
|
+
'accept': 'application/json',
|
|
111
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
112
|
+
'content-type': 'application/json',
|
|
113
|
+
},
|
|
114
|
+
method: 'PUT',
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
const errorText = await response.text();
|
|
118
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
119
|
+
}
|
|
120
|
+
const branch = await response.json();
|
|
121
|
+
// Output results
|
|
122
|
+
if (flags.output === 'json') {
|
|
123
|
+
this.log(JSON.stringify(branch, null, 2));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
// summary format
|
|
127
|
+
this.log(`Updated branch: ${branch.label}`);
|
|
128
|
+
if (flags.description) {
|
|
129
|
+
this.log(` Description: ${flags.description}`);
|
|
130
|
+
}
|
|
131
|
+
if (flags.color) {
|
|
132
|
+
this.log(` Color: ${flags.color}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (error instanceof Error) {
|
|
138
|
+
this.error(`Failed to update branch: ${error.message}`);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
this.error(`Failed to update branch: ${String(error)}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
loadCredentials() {
|
|
146
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
147
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
148
|
+
// Check if credentials file exists
|
|
149
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
150
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
151
|
+
`Create a profile using 'xano profile create'`);
|
|
152
|
+
}
|
|
153
|
+
// Read credentials file
|
|
154
|
+
try {
|
|
155
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
156
|
+
const parsed = yaml.load(fileContent);
|
|
157
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
158
|
+
this.error('Credentials file has invalid format.');
|
|
159
|
+
}
|
|
160
|
+
return parsed;
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class BranchGet extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
branch_label: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
private loadCredentials;
|
|
16
|
+
}
|