@chabokan.net/cli 0.8.10 → 0.9.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.
Files changed (55) hide show
  1. package/README.md +520 -66
  2. package/dist/base.d.ts +34 -9
  3. package/dist/base.js +172 -20
  4. package/dist/commands/account/info.d.ts +12 -0
  5. package/dist/commands/account/info.js +40 -0
  6. package/dist/commands/account/list.d.ts +5 -1
  7. package/dist/commands/account/list.js +35 -16
  8. package/dist/commands/account/remove.d.ts +5 -1
  9. package/dist/commands/account/remove.js +30 -15
  10. package/dist/commands/account/use.d.ts +6 -2
  11. package/dist/commands/account/use.js +26 -15
  12. package/dist/commands/cloudserver/create.d.ts +40 -0
  13. package/dist/commands/cloudserver/create.js +242 -0
  14. package/dist/commands/cloudserver/delete.d.ts +14 -0
  15. package/dist/commands/cloudserver/delete.js +65 -0
  16. package/dist/commands/cloudserver/list.d.ts +12 -0
  17. package/dist/commands/cloudserver/list.js +46 -0
  18. package/dist/commands/cloudserver/restart.d.ts +13 -0
  19. package/dist/commands/cloudserver/restart.js +51 -0
  20. package/dist/commands/cloudserver/start.d.ts +13 -0
  21. package/dist/commands/cloudserver/start.js +51 -0
  22. package/dist/commands/cloudserver/stop.d.ts +13 -0
  23. package/dist/commands/cloudserver/stop.js +52 -0
  24. package/dist/commands/deploy.d.ts +16 -5
  25. package/dist/commands/deploy.js +182 -101
  26. package/dist/commands/login.d.ts +8 -4
  27. package/dist/commands/login.js +97 -49
  28. package/dist/commands/service/domain/add.d.ts +15 -0
  29. package/dist/commands/service/domain/add.js +74 -0
  30. package/dist/commands/service/domain/remove.d.ts +15 -0
  31. package/dist/commands/service/domain/remove.js +72 -0
  32. package/dist/commands/service/list.d.ts +5 -1
  33. package/dist/commands/service/list.js +41 -20
  34. package/dist/commands/service/logs.d.ts +7 -3
  35. package/dist/commands/service/logs.js +46 -34
  36. package/dist/commands/service/resize.d.ts +10 -6
  37. package/dist/commands/service/resize.js +94 -72
  38. package/dist/commands/service/restart.d.ts +7 -3
  39. package/dist/commands/service/restart.js +40 -31
  40. package/dist/commands/service/start.d.ts +7 -3
  41. package/dist/commands/service/start.js +41 -30
  42. package/dist/commands/service/stop.d.ts +7 -3
  43. package/dist/commands/service/stop.js +41 -30
  44. package/dist/commands/wallet/list.d.ts +12 -0
  45. package/dist/commands/wallet/list.js +41 -0
  46. package/dist/constants.d.ts +2 -0
  47. package/dist/constants.js +7 -2
  48. package/dist/helper.d.ts +32 -9
  49. package/dist/helper.js +426 -49
  50. package/dist/types.d.ts +77 -0
  51. package/dist/types.js +2 -0
  52. package/dist/ui.d.ts +23 -0
  53. package/dist/ui.js +70 -0
  54. package/oclif.manifest.json +635 -25
  55. package/package.json +39 -35
@@ -1,57 +1,66 @@
1
1
  import { Flags } from "@oclif/core";
2
2
  import Command from "../../base.js";
3
- import { isEmptyObject } from "../../helper.js";
4
- import chalk from "chalk";
5
- import inquirer from "inquirer";
3
+ import { handleApiError, logErrorDetails } from "../../helper.js";
4
+ import * as ui from "../../ui.js";
6
5
  import axios from "axios";
7
6
  export default class ServiceRestart extends Command {
8
7
  static description = "restart a service";
8
+ static examples = [
9
+ {
10
+ description: 'Pick a service from an interactive list',
11
+ command: '<%= config.bin %> service restart',
12
+ },
13
+ {
14
+ description: 'Restart a specific service by name',
15
+ command: '<%= config.bin %> service restart --service my-app',
16
+ },
17
+ {
18
+ description: 'Restart, then tail the logs to confirm it came back up',
19
+ command: '<%= config.bin %> service restart -s my-app && <%= config.bin %> service logs -s my-app',
20
+ },
21
+ ];
9
22
  static flags = {
10
23
  ...Command.flags,
11
- service: Flags.string({ char: "s", description: "service name" }),
24
+ service: Flags.string({ char: "s", description: "name of the service to restart" }),
12
25
  };
13
26
  async run() {
14
- const { args, flags } = await this.parse(ServiceRestart);
27
+ const { flags } = await this.parse(ServiceRestart);
15
28
  const cli = this;
16
29
  await this.init_run();
17
- const config_json = await this.read_config();
18
- let selected_service = flags.service;
19
- if (isEmptyObject(config_json.users)) {
20
- cli.log(`${chalk.red("[Error]")} first you should login!`);
30
+ if (!(await this.require_login())) {
21
31
  return;
22
32
  }
23
- if (!flags.service) {
24
- let all_services = await this.get_services({
25
- not_status: "pending",
26
- });
27
- if (all_services) {
28
- let { service } = await inquirer.prompt({
29
- type: "list",
30
- message: "Please select a service:",
31
- name: "service",
32
- choices: all_services,
33
- });
34
- selected_service = service;
35
- }
33
+ const selected_service = await this.select_service(flags.service, { not_status: "pending" }, "Restart Service");
34
+ if (!selected_service) {
35
+ return;
36
36
  }
37
37
  await this.send_request(cli, selected_service);
38
38
  }
39
39
  async send_request(cli, selected_service) {
40
40
  try {
41
- if (selected_service) {
42
- const { data } = await axios.get("services/" + selected_service + "/restart/", this.axiosConfig);
43
- if (data.success) {
44
- cli.log(`${chalk.green("[Success]")} service restarted successfully.`);
45
- }
41
+ const { data } = await axios.get(`services/${selected_service}/restart/`, this.axiosConfig);
42
+ if (data.success) {
43
+ cli.log(ui.success(`Service ${ui.value(selected_service)} is restarting.`));
44
+ cli.log(ui.hint(`Follow its output with ${ui.cmd(`chabok service logs -s ${selected_service}`)}`));
45
+ }
46
+ else {
47
+ cli.log(ui.error(`Could not restart ${ui.value(selected_service)}. ${data.message || 'Please try again in a moment.'}`));
46
48
  }
47
49
  }
48
- catch (e) {
49
- if (e.response.status == 404) {
50
- cli.log(chalk.blue("Selected Service Not Founded."));
50
+ catch (error) {
51
+ const errorInfo = handleApiError(error, 'Failed to restart service.', {
52
+ endpoint: `services/${selected_service}/restart/`,
53
+ serviceName: selected_service,
54
+ operation: 'Restart Service'
55
+ });
56
+ if (errorInfo.status === 404) {
57
+ cli.log(ui.error(`Service ${ui.value(selected_service)} was not found.`));
58
+ cli.log(ui.hint(`Check the available names with ${ui.cmd('chabok service list')}`));
51
59
  }
52
60
  else {
53
- console.log(e.data);
61
+ cli.log(ui.error(errorInfo.message));
54
62
  }
63
+ logErrorDetails(errorInfo, cli);
55
64
  }
56
65
  }
57
66
  }
@@ -1,10 +1,14 @@
1
1
  import Command from "../../base.js";
2
2
  export default class ServiceStart extends Command {
3
3
  static description: string;
4
+ static examples: {
5
+ description: string;
6
+ command: string;
7
+ }[];
4
8
  static flags: {
5
- service: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
6
- help: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<void>;
9
+ service: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ help: import("@oclif/core/interfaces").BooleanFlag<void>;
7
11
  };
8
12
  run(): Promise<void>;
9
- send_request(cli: any, selected_service: any): Promise<void>;
13
+ send_request(cli: Command, selected_service: string): Promise<void>;
10
14
  }
@@ -1,55 +1,66 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import Command from "../../base.js";
3
- import { isEmptyObject } from "../../helper.js";
4
- import chalk from "chalk";
5
- import inquirer from 'inquirer';
3
+ import { handleApiError, logErrorDetails } from "../../helper.js";
4
+ import * as ui from "../../ui.js";
6
5
  import axios from "axios";
7
6
  export default class ServiceStart extends Command {
8
- static description = 'start a service';
7
+ static description = 'start a stopped service';
8
+ static examples = [
9
+ {
10
+ description: 'Pick a service from an interactive list',
11
+ command: '<%= config.bin %> service start',
12
+ },
13
+ {
14
+ description: 'Start a specific service by name',
15
+ command: '<%= config.bin %> service start --service my-app',
16
+ },
17
+ {
18
+ description: 'Same, using the short flag',
19
+ command: '<%= config.bin %> service start -s my-app',
20
+ },
21
+ ];
9
22
  static flags = {
10
23
  ...Command.flags,
11
- service: Flags.string({ char: 's', description: 'service name' }),
24
+ service: Flags.string({ char: 's', description: 'name of the service to start' }),
12
25
  };
13
26
  async run() {
14
- const { args, flags } = await this.parse(ServiceStart);
27
+ const { flags } = await this.parse(ServiceStart);
15
28
  const cli = this;
16
29
  await this.init_run();
17
- const config_json = await this.read_config();
18
- let selected_service = flags.service;
19
- if (isEmptyObject(config_json.users)) {
20
- cli.log(`${chalk.red('[Error]')} first you should login!`);
30
+ if (!(await this.require_login())) {
21
31
  return;
22
32
  }
23
- if (!flags.service) {
24
- let all_services = await this.get_services({ 'not_status': 'pending' });
25
- if (all_services) {
26
- let { service } = await inquirer.prompt({
27
- type: 'list',
28
- message: 'Please select a service:',
29
- name: 'service',
30
- choices: all_services
31
- });
32
- selected_service = service;
33
- }
33
+ const selected_service = await this.select_service(flags.service, { 'not_status': 'pending' }, 'Start Service');
34
+ if (!selected_service) {
35
+ return;
34
36
  }
35
37
  await this.send_request(cli, selected_service);
36
38
  }
37
39
  async send_request(cli, selected_service) {
38
40
  try {
39
- if (selected_service) {
40
- const { data } = await axios.get("services/" + selected_service + "/start/", this.axiosConfig);
41
- if (data.success) {
42
- cli.log(`${chalk.green('[Success]')} service start successfully.`);
43
- }
41
+ const { data } = await axios.get(`services/${selected_service}/start/`, this.axiosConfig);
42
+ if (data.success) {
43
+ cli.log(ui.success(`Service ${ui.value(selected_service)} is starting.`));
44
+ cli.log(ui.hint(`Follow its output with ${ui.cmd(`chabok service logs -s ${selected_service}`)}`));
45
+ }
46
+ else {
47
+ cli.log(ui.error(`Could not start ${ui.value(selected_service)}. ${data.message || 'Check the service status and try again.'}`));
44
48
  }
45
49
  }
46
- catch (e) {
47
- if (e.response.status == 404) {
48
- cli.log(chalk.blue("Selected Service Not Founded."));
50
+ catch (error) {
51
+ const errorInfo = handleApiError(error, 'Failed to start service.', {
52
+ endpoint: `services/${selected_service}/start/`,
53
+ serviceName: selected_service,
54
+ operation: 'Start Service'
55
+ });
56
+ if (errorInfo.status === 404) {
57
+ cli.log(ui.error(`Service ${ui.value(selected_service)} was not found.`));
58
+ cli.log(ui.hint(`Check the available names with ${ui.cmd('chabok service list')}`));
49
59
  }
50
60
  else {
51
- console.log(e.data);
61
+ cli.log(ui.error(errorInfo.message));
52
62
  }
63
+ logErrorDetails(errorInfo, cli);
53
64
  }
54
65
  }
55
66
  }
@@ -1,10 +1,14 @@
1
1
  import Command from "../../base.js";
2
2
  export default class ServiceStop extends Command {
3
3
  static description: string;
4
+ static examples: {
5
+ description: string;
6
+ command: string;
7
+ }[];
4
8
  static flags: {
5
- service: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
6
- help: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<void>;
9
+ service: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ help: import("@oclif/core/interfaces").BooleanFlag<void>;
7
11
  };
8
12
  run(): Promise<void>;
9
- send_request(cli: any, selected_service: any): Promise<void>;
13
+ send_request(cli: Command, selected_service: string): Promise<void>;
10
14
  }
@@ -1,55 +1,66 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import Command from "../../base.js";
3
- import { isEmptyObject } from "../../helper.js";
4
- import chalk from "chalk";
5
- import inquirer from 'inquirer';
3
+ import { handleApiError, logErrorDetails } from "../../helper.js";
4
+ import * as ui from "../../ui.js";
6
5
  import axios from "axios";
7
6
  export default class ServiceStop extends Command {
8
- static description = 'stop a service';
7
+ static description = 'stop a running service';
8
+ static examples = [
9
+ {
10
+ description: 'Pick a service from an interactive list',
11
+ command: '<%= config.bin %> service stop',
12
+ },
13
+ {
14
+ description: 'Stop a specific service by name',
15
+ command: '<%= config.bin %> service stop --service my-app',
16
+ },
17
+ {
18
+ description: 'Same, using the short flag',
19
+ command: '<%= config.bin %> service stop -s my-app',
20
+ },
21
+ ];
9
22
  static flags = {
10
23
  ...Command.flags,
11
- service: Flags.string({ char: 's', description: 'service name' }),
24
+ service: Flags.string({ char: 's', description: 'name of the service to stop' }),
12
25
  };
13
26
  async run() {
14
- const { args, flags } = await this.parse(ServiceStop);
27
+ const { flags } = await this.parse(ServiceStop);
15
28
  const cli = this;
16
29
  await this.init_run();
17
- const config_json = await this.read_config();
18
- let selected_service = flags.service;
19
- if (isEmptyObject(config_json.users)) {
20
- cli.log(`${chalk.red('[Error]')} first you should login!`);
30
+ if (!(await this.require_login())) {
21
31
  return;
22
32
  }
23
- if (!flags.service) {
24
- let all_services = await this.get_services({ 'not_status': 'pending' });
25
- if (all_services) {
26
- let { service } = await inquirer.prompt({
27
- type: 'list',
28
- message: 'Please select a service:',
29
- name: 'service',
30
- choices: all_services
31
- });
32
- selected_service = service;
33
- }
33
+ const selected_service = await this.select_service(flags.service, { 'not_status': 'pending' }, 'Stop Service');
34
+ if (!selected_service) {
35
+ return;
34
36
  }
35
37
  await this.send_request(cli, selected_service);
36
38
  }
37
39
  async send_request(cli, selected_service) {
38
40
  try {
39
- if (selected_service) {
40
- const { data } = await axios.get("services/" + selected_service + "/stop/", this.axiosConfig);
41
- if (data.success) {
42
- cli.log(`${chalk.green('[Success]')} service stop successfully.`);
43
- }
41
+ const { data } = await axios.get(`services/${selected_service}/stop/`, this.axiosConfig);
42
+ if (data.success) {
43
+ cli.log(ui.success(`Service ${ui.value(selected_service)} is stopping.`));
44
+ cli.log(ui.hint(`Bring it back with ${ui.cmd(`chabok service start -s ${selected_service}`)}`));
45
+ }
46
+ else {
47
+ cli.log(ui.error(`Could not stop ${ui.value(selected_service)}. ${data.message || 'Please try again in a moment.'}`));
44
48
  }
45
49
  }
46
- catch (e) {
47
- if (e.response.status == 404) {
48
- cli.log(chalk.blue("Selected Service Not Founded."));
50
+ catch (error) {
51
+ const errorInfo = handleApiError(error, 'Failed to stop service.', {
52
+ endpoint: `services/${selected_service}/stop/`,
53
+ serviceName: selected_service,
54
+ operation: 'Stop Service'
55
+ });
56
+ if (errorInfo.status === 404) {
57
+ cli.log(ui.error(`Service ${ui.value(selected_service)} was not found.`));
58
+ cli.log(ui.hint(`Check the available names with ${ui.cmd('chabok service list')}`));
49
59
  }
50
60
  else {
51
- console.log(e.data);
61
+ cli.log(ui.error(errorInfo.message));
52
62
  }
63
+ logErrorDetails(errorInfo, cli);
53
64
  }
54
65
  }
55
66
  }
@@ -0,0 +1,12 @@
1
+ import Command from "../../base.js";
2
+ export default class WalletList extends Command {
3
+ static description: string;
4
+ static examples: {
5
+ description: string;
6
+ command: string;
7
+ }[];
8
+ static flags: {
9
+ help: import("@oclif/core/interfaces").BooleanFlag<void>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,41 @@
1
+ import Command from "../../base.js";
2
+ import * as ui from "../../ui.js";
3
+ import { handleApiError, logErrorDetails, extractRecords } from "../../helper.js";
4
+ import axios from "axios";
5
+ export default class WalletList extends Command {
6
+ static description = "list the wallets on your account";
7
+ static examples = [
8
+ {
9
+ description: 'Show every wallet and its balance',
10
+ command: '<%= config.bin %> wallet list',
11
+ },
12
+ ];
13
+ static flags = {
14
+ ...Command.flags,
15
+ };
16
+ async run() {
17
+ await this.parse(WalletList);
18
+ const cli = this;
19
+ await this.init_run();
20
+ if (!(await this.require_login())) {
21
+ return;
22
+ }
23
+ try {
24
+ const { data } = await axios.get('wallets/', this.axiosConfig);
25
+ const wallets = extractRecords(data, ['wallets', 'results', 'data']);
26
+ if (wallets.length === 0) {
27
+ cli.log(ui.info('You have no wallets yet.'));
28
+ return;
29
+ }
30
+ ui.printRecords(wallets);
31
+ }
32
+ catch (error) {
33
+ const errorInfo = handleApiError(error, 'Failed to fetch wallets.', {
34
+ endpoint: 'wallets/',
35
+ operation: 'List Wallets'
36
+ });
37
+ cli.log(ui.error(errorInfo.message));
38
+ logErrorDetails(errorInfo, cli);
39
+ }
40
+ }
41
+ }
@@ -1,2 +1,4 @@
1
1
  export declare const GLOBAL_CONF_PATH: string;
2
2
  export declare let BASE_API_URL: string;
3
+ export declare const REQUEST_TIMEOUT = 30000;
4
+ export declare const UPLOAD_TIMEOUT: number;
package/dist/constants.js CHANGED
@@ -1,7 +1,12 @@
1
- import * as path from 'path';
2
- import * as os from 'os';
1
+ import * as path from 'node:path';
2
+ import * as os from 'node:os';
3
3
  export const GLOBAL_CONF_PATH = path.join(os.homedir(), '.chabok.json');
4
4
  export let BASE_API_URL = 'https://apihub.chabokan.net/fa/api/v1/';
5
5
  if (process.env.CHABOK_API_URL) {
6
6
  BASE_API_URL = process.env.CHABOK_API_URL;
7
7
  }
8
+ // Timeout for regular API calls. Deploy uploads use their own (much longer)
9
+ // budget, so this only bounds the short control-plane requests.
10
+ export const REQUEST_TIMEOUT = 30_000;
11
+ // Deploy uploads can legitimately take minutes on a slow connection.
12
+ export const UPLOAD_TIMEOUT = 30 * 60 * 1000;
package/dist/helper.d.ts CHANGED
@@ -1,12 +1,35 @@
1
- import { Ignore } from 'ignore';
2
- export declare function isObject(obj: any): boolean;
3
- export declare function isEmptyObject(obj: any): any;
4
- export declare function read_config_file(): {
5
- users: {};
6
- default_user: string;
7
- };
8
- export declare function get_all_services(filter: {} | undefined, axiosConfig: any): Promise<any>;
1
+ import { AxiosRequestConfig } from "axios";
2
+ import type { Ignore } from 'ignore';
3
+ import type { ConfigFile, Service, ErrorInfo, ErrorContext } from './types.js';
4
+ /** Some API error/description text comes wrapped in HTML tags (e.g. `<p>...</p>`). */
5
+ export declare function stripHtml(text: string): string;
6
+ export declare function isDebug(): boolean;
7
+ export declare function isObject(obj: unknown): obj is Record<string, unknown>;
8
+ export declare function isEmptyObject(obj: unknown): boolean;
9
+ export declare function read_config_file(): ConfigFile;
10
+ export declare function write_config_file(config_json: ConfigFile): void;
11
+ /**
12
+ * Fetches the account's services. Throws on failure so callers can surface a
13
+ * real error instead of an empty list that reads as "you have no services".
14
+ */
15
+ export declare function get_all_services(filter: Record<string, string> | undefined, axiosConfig: AxiosRequestConfig): Promise<Service[]>;
16
+ /**
17
+ * Pulls a list of records out of an API response whose exact wrapper key
18
+ * isn't guaranteed (some endpoints return a bare array, others wrap it under
19
+ * `data`, `results`, or a resource-named key).
20
+ */
21
+ export declare function extractRecords(data: unknown, wrapperKeys?: string[]): Record<string, unknown>[];
9
22
  export declare function trimLines(lines: string[]): string[];
10
23
  export declare const loadIgnoreFile: (ignoreInstance: Ignore, ignoreFilePath: string, projectPath: string) => void;
11
24
  export declare function addIgnorePatterns(ignoreInstance: Ignore, projectPath: string, dir: string): void;
12
- export declare const checkUpdate: (version: any) => Promise<void>;
25
+ /**
26
+ * Tells the user about a newer release. This runs ahead of every command, so it
27
+ * is deliberately defensive: it is skippable, it only hits the network once a
28
+ * day, it gives up after 1.5s, and any failure is silent. A user with no
29
+ * internet should not notice that this function exists.
30
+ */
31
+ export declare const checkUpdate: (version: string) => Promise<void>;
32
+ export declare function handleApiError(error: unknown, defaultMessage: string, context?: ErrorContext): ErrorInfo;
33
+ export declare function logErrorDetails(errorInfo: ErrorInfo, command: {
34
+ log: (msg: string) => void;
35
+ }): void;