@xano/cli 0.0.2

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.
Files changed (49) hide show
  1. package/README.md +1200 -0
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +5 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +5 -0
  6. package/dist/base-command.d.ts +11 -0
  7. package/dist/base-command.js +40 -0
  8. package/dist/commands/ephemeral/run/job/index.d.ts +19 -0
  9. package/dist/commands/ephemeral/run/job/index.js +318 -0
  10. package/dist/commands/ephemeral/run/service/index.d.ts +18 -0
  11. package/dist/commands/ephemeral/run/service/index.js +286 -0
  12. package/dist/commands/function/create/index.d.ts +18 -0
  13. package/dist/commands/function/create/index.js +280 -0
  14. package/dist/commands/function/edit/index.d.ts +24 -0
  15. package/dist/commands/function/edit/index.js +482 -0
  16. package/dist/commands/function/get/index.d.ts +18 -0
  17. package/dist/commands/function/get/index.js +279 -0
  18. package/dist/commands/function/list/index.d.ts +19 -0
  19. package/dist/commands/function/list/index.js +208 -0
  20. package/dist/commands/profile/create/index.d.ts +17 -0
  21. package/dist/commands/profile/create/index.js +123 -0
  22. package/dist/commands/profile/delete/index.d.ts +14 -0
  23. package/dist/commands/profile/delete/index.js +124 -0
  24. package/dist/commands/profile/edit/index.d.ts +18 -0
  25. package/dist/commands/profile/edit/index.js +129 -0
  26. package/dist/commands/profile/get-default/index.d.ts +6 -0
  27. package/dist/commands/profile/get-default/index.js +44 -0
  28. package/dist/commands/profile/list/index.d.ts +10 -0
  29. package/dist/commands/profile/list/index.js +115 -0
  30. package/dist/commands/profile/set-default/index.d.ts +9 -0
  31. package/dist/commands/profile/set-default/index.js +63 -0
  32. package/dist/commands/profile/wizard/index.d.ts +15 -0
  33. package/dist/commands/profile/wizard/index.js +350 -0
  34. package/dist/commands/static_host/build/create/index.d.ts +18 -0
  35. package/dist/commands/static_host/build/create/index.js +194 -0
  36. package/dist/commands/static_host/build/get/index.d.ts +16 -0
  37. package/dist/commands/static_host/build/get/index.js +165 -0
  38. package/dist/commands/static_host/build/list/index.d.ts +17 -0
  39. package/dist/commands/static_host/build/list/index.js +192 -0
  40. package/dist/commands/static_host/list/index.d.ts +15 -0
  41. package/dist/commands/static_host/list/index.js +187 -0
  42. package/dist/commands/workspace/list/index.d.ts +11 -0
  43. package/dist/commands/workspace/list/index.js +154 -0
  44. package/dist/help.d.ts +20 -0
  45. package/dist/help.js +26 -0
  46. package/dist/index.d.ts +1 -0
  47. package/dist/index.js +1 -0
  48. package/oclif.manifest.json +1370 -0
  49. package/package.json +79 -0
@@ -0,0 +1,124 @@
1
+ import { Args, Command, 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
+ export default class ProfileDelete extends Command {
7
+ static args = {
8
+ name: Args.string({
9
+ description: 'Profile name to delete',
10
+ required: true,
11
+ }),
12
+ };
13
+ static flags = {
14
+ force: Flags.boolean({
15
+ char: 'f',
16
+ description: 'Skip confirmation prompt',
17
+ required: false,
18
+ default: false,
19
+ }),
20
+ };
21
+ static description = 'Delete a profile configuration';
22
+ static examples = [
23
+ `$ xano profile:delete old-profile
24
+ Are you sure you want to delete profile 'old-profile'? (y/n): y
25
+ Profile 'old-profile' deleted successfully from ~/.xano/credentials.yaml
26
+ `,
27
+ `$ xano profile:delete old-profile --force
28
+ Profile 'old-profile' deleted successfully from ~/.xano/credentials.yaml
29
+ `,
30
+ `$ xano profile:delete old-profile -f
31
+ Profile 'old-profile' deleted successfully from ~/.xano/credentials.yaml
32
+ `,
33
+ ];
34
+ async run() {
35
+ const { args, flags } = await this.parse(ProfileDelete);
36
+ const configDir = path.join(os.homedir(), '.xano');
37
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
38
+ // Check if credentials file exists
39
+ if (!fs.existsSync(credentialsPath)) {
40
+ this.error(`Credentials file not found at ${credentialsPath}. No profiles to delete.`);
41
+ }
42
+ // Read existing credentials file
43
+ let credentials;
44
+ try {
45
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
46
+ const parsed = yaml.load(fileContent);
47
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
48
+ this.error('Credentials file has invalid format.');
49
+ }
50
+ credentials = parsed;
51
+ }
52
+ catch (error) {
53
+ this.error(`Failed to parse credentials file: ${error}`);
54
+ }
55
+ // Check if profile exists
56
+ if (!(args.name in credentials.profiles)) {
57
+ this.error(`Profile '${args.name}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}`);
58
+ }
59
+ // Confirm deletion unless --force flag is used
60
+ if (!flags.force) {
61
+ const confirm = await this.confirm(`Are you sure you want to delete profile '${args.name}'?`);
62
+ if (!confirm) {
63
+ this.log('Deletion cancelled.');
64
+ return;
65
+ }
66
+ }
67
+ // Check if deleting the default profile
68
+ const wasDefault = credentials.default === args.name;
69
+ // Delete the profile
70
+ delete credentials.profiles[args.name];
71
+ // If deleted profile was the default, update to first available profile
72
+ if (wasDefault) {
73
+ const remainingProfiles = Object.keys(credentials.profiles);
74
+ if (remainingProfiles.length > 0) {
75
+ credentials.default = remainingProfiles[0];
76
+ }
77
+ else {
78
+ delete credentials.default;
79
+ }
80
+ }
81
+ // Write the updated credentials back to the file
82
+ try {
83
+ const yamlContent = yaml.dump(credentials, {
84
+ indent: 2,
85
+ lineWidth: -1,
86
+ noRefs: true,
87
+ });
88
+ fs.writeFileSync(credentialsPath, yamlContent, 'utf8');
89
+ this.log(`Profile '${args.name}' deleted successfully from ${credentialsPath}`);
90
+ if (wasDefault) {
91
+ if (credentials.default) {
92
+ this.log(`Default profile changed to '${credentials.default}'`);
93
+ }
94
+ else {
95
+ this.log(`Default profile removed (no profiles remaining)`);
96
+ }
97
+ }
98
+ }
99
+ catch (error) {
100
+ this.error(`Failed to write credentials file: ${error}`);
101
+ }
102
+ }
103
+ async confirm(message) {
104
+ // Simple confirmation using stdin
105
+ const response = await this.promptInput(`${message} (y/n): `);
106
+ return response.toLowerCase() === 'y' || response.toLowerCase() === 'yes';
107
+ }
108
+ async promptInput(prompt) {
109
+ process.stdout.write(prompt);
110
+ // Set stdin to raw mode temporarily
111
+ const wasRaw = process.stdin.isRaw;
112
+ process.stdin.setRawMode?.(false);
113
+ process.stdin.resume();
114
+ return new Promise((resolve) => {
115
+ process.stdin.once('data', (data) => {
116
+ process.stdin.pause();
117
+ if (wasRaw !== undefined) {
118
+ process.stdin.setRawMode?.(wasRaw);
119
+ }
120
+ resolve(data.toString().trim());
121
+ });
122
+ });
123
+ }
124
+ }
@@ -0,0 +1,18 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ProfileEdit extends Command {
3
+ static args: {
4
+ name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static flags: {
7
+ account_origin: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ instance_origin: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ access_token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'remove-workspace': import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ 'remove-branch': import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ };
15
+ static description: string;
16
+ static examples: string[];
17
+ run(): Promise<void>;
18
+ }
@@ -0,0 +1,129 @@
1
+ import { Args, Command, 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
+ export default class ProfileEdit extends Command {
7
+ static args = {
8
+ name: Args.string({
9
+ description: 'Profile name to edit',
10
+ required: true,
11
+ }),
12
+ };
13
+ static flags = {
14
+ account_origin: Flags.string({
15
+ char: 'a',
16
+ description: 'Update account origin URL',
17
+ required: false,
18
+ }),
19
+ instance_origin: Flags.string({
20
+ char: 'i',
21
+ description: 'Update instance origin URL',
22
+ required: false,
23
+ }),
24
+ access_token: Flags.string({
25
+ char: 't',
26
+ description: 'Update access token for the Xano Metadata API',
27
+ required: false,
28
+ }),
29
+ workspace: Flags.string({
30
+ char: 'w',
31
+ description: 'Update workspace name',
32
+ required: false,
33
+ }),
34
+ branch: Flags.string({
35
+ char: 'b',
36
+ description: 'Update branch name',
37
+ required: false,
38
+ }),
39
+ 'remove-workspace': Flags.boolean({
40
+ description: 'Remove workspace from profile',
41
+ required: false,
42
+ default: false,
43
+ }),
44
+ 'remove-branch': Flags.boolean({
45
+ description: 'Remove branch from profile',
46
+ required: false,
47
+ default: false,
48
+ }),
49
+ };
50
+ static description = 'Edit an existing profile configuration';
51
+ static examples = [
52
+ `$ xano profile:edit production --access_token new_token123
53
+ Profile 'production' updated successfully at ~/.xano/credentials.yaml
54
+ `,
55
+ `$ xano profile:edit staging -i https://new-staging-instance.xano.com -t new_token456
56
+ Profile 'staging' updated successfully at ~/.xano/credentials.yaml
57
+ `,
58
+ `$ xano profile:edit dev -w new-workspace -b new-branch
59
+ Profile 'dev' updated successfully at ~/.xano/credentials.yaml
60
+ `,
61
+ `$ xano profile:edit dev --remove-workspace
62
+ Profile 'dev' updated successfully at ~/.xano/credentials.yaml
63
+ `,
64
+ ];
65
+ async run() {
66
+ const { args, flags } = await this.parse(ProfileEdit);
67
+ const configDir = path.join(os.homedir(), '.xano');
68
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
69
+ // Check if credentials file exists
70
+ if (!fs.existsSync(credentialsPath)) {
71
+ this.error(`Credentials file not found at ${credentialsPath}. Create a profile first using 'profile:create'.`);
72
+ }
73
+ // Read existing credentials file
74
+ let credentials;
75
+ try {
76
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
77
+ const parsed = yaml.load(fileContent);
78
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
79
+ this.error('Credentials file has invalid format.');
80
+ }
81
+ credentials = parsed;
82
+ }
83
+ catch (error) {
84
+ this.error(`Failed to parse credentials file: ${error}`);
85
+ }
86
+ // Check if profile exists
87
+ if (!(args.name in credentials.profiles)) {
88
+ this.error(`Profile '${args.name}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}`);
89
+ }
90
+ // Get the existing profile
91
+ const existingProfile = credentials.profiles[args.name];
92
+ // Check if any flags were provided
93
+ const hasFlags = flags.account_origin || flags.instance_origin || flags.access_token ||
94
+ flags.workspace || flags.branch || flags['remove-workspace'] || flags['remove-branch'];
95
+ if (!hasFlags) {
96
+ this.error('No fields specified to update. Use at least one flag to edit the profile.');
97
+ }
98
+ // Update only the fields that were provided
99
+ const updatedProfile = {
100
+ ...existingProfile,
101
+ ...(flags.account_origin !== undefined && { account_origin: flags.account_origin }),
102
+ ...(flags.instance_origin !== undefined && { instance_origin: flags.instance_origin }),
103
+ ...(flags.access_token !== undefined && { access_token: flags.access_token }),
104
+ ...(flags.workspace !== undefined && { workspace: flags.workspace }),
105
+ ...(flags.branch !== undefined && { branch: flags.branch }),
106
+ };
107
+ // Handle removal flags
108
+ if (flags['remove-workspace']) {
109
+ delete updatedProfile.workspace;
110
+ }
111
+ if (flags['remove-branch']) {
112
+ delete updatedProfile.branch;
113
+ }
114
+ credentials.profiles[args.name] = updatedProfile;
115
+ // Write the updated credentials back to the file
116
+ try {
117
+ const yamlContent = yaml.dump(credentials, {
118
+ indent: 2,
119
+ lineWidth: -1,
120
+ noRefs: true,
121
+ });
122
+ fs.writeFileSync(credentialsPath, yamlContent, 'utf8');
123
+ this.log(`Profile '${args.name}' updated successfully at ${credentialsPath}`);
124
+ }
125
+ catch (error) {
126
+ this.error(`Failed to write credentials file: ${error}`);
127
+ }
128
+ }
129
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ProfileGetDefault extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,44 @@
1
+ import { Command } 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
+ export default class ProfileGetDefault extends Command {
7
+ static description = 'Get the current default profile name';
8
+ static examples = [
9
+ `$ xano profile:get-default
10
+ production
11
+ `,
12
+ `$ xano profile:get-default
13
+ No default profile set
14
+ `,
15
+ ];
16
+ async run() {
17
+ const configDir = path.join(os.homedir(), '.xano');
18
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
19
+ // Check if credentials file exists
20
+ if (!fs.existsSync(credentialsPath)) {
21
+ this.error(`Credentials file not found at ${credentialsPath}. No profiles exist.`);
22
+ }
23
+ // Read credentials file
24
+ let credentials;
25
+ try {
26
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
27
+ const parsed = yaml.load(fileContent);
28
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
29
+ this.error('Credentials file has invalid format.');
30
+ }
31
+ credentials = parsed;
32
+ }
33
+ catch (error) {
34
+ this.error(`Failed to parse credentials file: ${error}`);
35
+ }
36
+ // Get and display the default profile
37
+ if (credentials.default) {
38
+ this.log(credentials.default);
39
+ }
40
+ else {
41
+ this.log('No default profile set');
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ProfileList extends Command {
3
+ static flags: {
4
+ details: import("@oclif/core/interfaces").BooleanFlag<boolean>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ run(): Promise<void>;
9
+ private maskToken;
10
+ }
@@ -0,0 +1,115 @@
1
+ import { Command, 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
+ export default class ProfileList extends Command {
7
+ static flags = {
8
+ details: Flags.boolean({
9
+ char: 'd',
10
+ description: 'Show detailed information for each profile',
11
+ required: false,
12
+ default: false,
13
+ }),
14
+ };
15
+ static description = 'List all available profile configurations';
16
+ static examples = [
17
+ `$ xano profile:list
18
+ Available profiles:
19
+ - default
20
+ - production
21
+ - staging
22
+ - development
23
+ `,
24
+ `$ xano profile:list --details
25
+ Available profiles:
26
+
27
+ Profile: default
28
+ Account Origin: https://account.xano.com
29
+ Instance Origin: https://instance.xano.com
30
+ Access Token: ***...***
31
+ Workspace: my-workspace
32
+ Branch: main
33
+
34
+ Profile: production
35
+ Account Origin: https://account.xano.com
36
+ Instance Origin: https://prod-instance.xano.com
37
+ Access Token: ***...***
38
+ `,
39
+ `$ xano profile:list -d
40
+ Available profiles:
41
+
42
+ Profile: default
43
+ Account Origin: https://account.xano.com
44
+ Instance Origin: https://instance.xano.com
45
+ Access Token: ***...***
46
+ Workspace: my-workspace
47
+ Branch: main
48
+ `,
49
+ ];
50
+ async run() {
51
+ const { flags } = await this.parse(ProfileList);
52
+ const configDir = path.join(os.homedir(), '.xano');
53
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
54
+ // Check if credentials file exists
55
+ if (!fs.existsSync(credentialsPath)) {
56
+ this.log(`No profiles found. The credentials file does not exist at ${credentialsPath}`);
57
+ this.log(`Create a profile using 'xano profile:create'`);
58
+ return;
59
+ }
60
+ // Read credentials file
61
+ let credentials;
62
+ try {
63
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
64
+ const parsed = yaml.load(fileContent);
65
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
66
+ this.error('Credentials file has invalid format.');
67
+ }
68
+ credentials = parsed;
69
+ }
70
+ catch (error) {
71
+ this.error(`Failed to parse credentials file: ${error}`);
72
+ }
73
+ // Get profile names
74
+ const profileNames = Object.keys(credentials.profiles);
75
+ if (profileNames.length === 0) {
76
+ this.log('No profiles found in credentials file.');
77
+ this.log(`Create a profile using 'xano profile:create'`);
78
+ return;
79
+ }
80
+ // Display profiles
81
+ if (flags.details) {
82
+ this.log('Available profiles:\n');
83
+ for (const name of profileNames.sort()) {
84
+ const profile = credentials.profiles[name];
85
+ const isDefault = credentials.default === name ? ' [DEFAULT]' : '';
86
+ this.log(`Profile: ${name}${isDefault}`);
87
+ this.log(` Account Origin: ${profile.account_origin || '(not set)'}`);
88
+ this.log(` Instance Origin: ${profile.instance_origin}`);
89
+ this.log(` Access Token: ${this.maskToken(profile.access_token)}`);
90
+ if (profile.workspace) {
91
+ this.log(` Workspace: ${profile.workspace}`);
92
+ }
93
+ if (profile.branch) {
94
+ this.log(` Branch: ${profile.branch}`);
95
+ }
96
+ this.log(''); // Empty line between profiles
97
+ }
98
+ }
99
+ else {
100
+ this.log('Available profiles:');
101
+ for (const name of profileNames.sort()) {
102
+ const isDefault = credentials.default === name ? ' [DEFAULT]' : '';
103
+ this.log(` - ${name}${isDefault}`);
104
+ }
105
+ }
106
+ }
107
+ maskToken(token) {
108
+ if (token.length <= 8) {
109
+ return '***';
110
+ }
111
+ const start = token.slice(0, 3);
112
+ const end = token.slice(-3);
113
+ return `${start}...${end}`;
114
+ }
115
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ProfileSetDefault extends Command {
3
+ static args: {
4
+ name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,63 @@
1
+ import { Args, Command } 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
+ export default class ProfileSetDefault extends Command {
7
+ static args = {
8
+ name: Args.string({
9
+ description: 'Profile name to set as default',
10
+ required: true,
11
+ }),
12
+ };
13
+ static description = 'Set the default profile';
14
+ static examples = [
15
+ `$ xano profile:set-default production
16
+ Default profile set to 'production'
17
+ `,
18
+ `$ xano profile:set-default staging
19
+ Default profile set to 'staging'
20
+ `,
21
+ ];
22
+ async run() {
23
+ const { args } = await this.parse(ProfileSetDefault);
24
+ const configDir = path.join(os.homedir(), '.xano');
25
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
26
+ // Check if credentials file exists
27
+ if (!fs.existsSync(credentialsPath)) {
28
+ this.error(`Credentials file not found at ${credentialsPath}. Create a profile first using 'profile:create'.`);
29
+ }
30
+ // Read existing credentials file
31
+ let credentials;
32
+ try {
33
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
34
+ const parsed = yaml.load(fileContent);
35
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
36
+ this.error('Credentials file has invalid format.');
37
+ }
38
+ credentials = parsed;
39
+ }
40
+ catch (error) {
41
+ this.error(`Failed to parse credentials file: ${error}`);
42
+ }
43
+ // Check if profile exists
44
+ if (!(args.name in credentials.profiles)) {
45
+ this.error(`Profile '${args.name}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}`);
46
+ }
47
+ // Set the default profile
48
+ credentials.default = args.name;
49
+ // Write the updated credentials back to the file
50
+ try {
51
+ const yamlContent = yaml.dump(credentials, {
52
+ indent: 2,
53
+ lineWidth: -1,
54
+ noRefs: true,
55
+ });
56
+ fs.writeFileSync(credentialsPath, yamlContent, 'utf8');
57
+ this.log(`Default profile set to '${args.name}'`);
58
+ }
59
+ catch (error) {
60
+ this.error(`Failed to write credentials file: ${error}`);
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,15 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ProfileWizard extends Command {
3
+ static flags: {
4
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
5
+ origin: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ run(): Promise<void>;
10
+ private fetchInstances;
11
+ private fetchWorkspaces;
12
+ private fetchBranches;
13
+ private getDefaultProfileName;
14
+ private saveProfile;
15
+ }