@chabokan.net/cli 0.8.15 → 0.9.1

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 (54) hide show
  1. package/README.md +520 -66
  2. package/dist/base.d.ts +30 -7
  3. package/dist/base.js +163 -14
  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 +29 -19
  8. package/dist/commands/account/remove.d.ts +5 -1
  9. package/dist/commands/account/remove.js +23 -12
  10. package/dist/commands/account/use.d.ts +6 -2
  11. package/dist/commands/account/use.js +22 -11
  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 +13 -3
  25. package/dist/commands/deploy.js +96 -60
  26. package/dist/commands/login.d.ts +8 -4
  27. package/dist/commands/login.js +69 -41
  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 +29 -21
  34. package/dist/commands/service/logs.d.ts +6 -2
  35. package/dist/commands/service/logs.js +33 -30
  36. package/dist/commands/service/resize.d.ts +9 -5
  37. package/dist/commands/service/resize.js +73 -69
  38. package/dist/commands/service/restart.d.ts +6 -2
  39. package/dist/commands/service/restart.js +28 -32
  40. package/dist/commands/service/start.d.ts +6 -2
  41. package/dist/commands/service/start.js +29 -31
  42. package/dist/commands/service/stop.d.ts +6 -2
  43. package/dist/commands/service/stop.js +29 -31
  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 +23 -7
  49. package/dist/helper.js +294 -113
  50. package/dist/types.d.ts +28 -8
  51. package/dist/ui.d.ts +23 -0
  52. package/dist/ui.js +70 -0
  53. package/oclif.manifest.json +635 -25
  54. package/package.json +39 -35
@@ -1,53 +1,50 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import Command from "../../base.js";
3
- import { isEmptyObject, handleApiError, logErrorDetails } 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
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
- const all_services = await this.get_services({ 'not_status': 'pending' });
25
- if (all_services && all_services.length > 0) {
26
- const { 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
- }
34
- else {
35
- cli.log("No services available.");
36
- return;
37
- }
38
- }
39
- if (selected_service) {
40
- await this.send_request(cli, selected_service);
33
+ const selected_service = await this.select_service(flags.service, { 'not_status': 'pending' }, 'Start Service');
34
+ if (!selected_service) {
35
+ return;
41
36
  }
37
+ await this.send_request(cli, selected_service);
42
38
  }
43
39
  async send_request(cli, selected_service) {
44
40
  try {
45
41
  const { data } = await axios.get(`services/${selected_service}/start/`, this.axiosConfig);
46
42
  if (data.success) {
47
- cli.log(`${chalk.green('[Success]')} Service '${selected_service}' started successfully.`);
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}`)}`));
48
45
  }
49
46
  else {
50
- cli.log(`${chalk.red('[Error]')} Failed to start service '${selected_service}'. ${data.message || 'Please check the service status and try again.'}`);
47
+ cli.log(ui.error(`Could not start ${ui.value(selected_service)}. ${data.message || 'Check the service status and try again.'}`));
51
48
  }
52
49
  }
53
50
  catch (error) {
@@ -57,10 +54,11 @@ export default class ServiceStart extends Command {
57
54
  operation: 'Start Service'
58
55
  });
59
56
  if (errorInfo.status === 404) {
60
- cli.log(`${chalk.red('[Error]')} Service '${selected_service}' not found. Please verify the service name using 'chabok service list' and try again.`);
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')}`));
61
59
  }
62
60
  else {
63
- cli.log(`${chalk.red('[Error]')} ${errorInfo.message}`);
61
+ cli.log(ui.error(errorInfo.message));
64
62
  }
65
63
  logErrorDetails(errorInfo, cli);
66
64
  }
@@ -1,9 +1,13 @@
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
13
  send_request(cli: Command, selected_service: string): Promise<void>;
@@ -1,53 +1,50 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import Command from "../../base.js";
3
- import { isEmptyObject, handleApiError, logErrorDetails } 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
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
- const all_services = await this.get_services({ 'not_status': 'pending' });
25
- if (all_services && all_services.length > 0) {
26
- const { 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
- }
34
- else {
35
- cli.log("No services available.");
36
- return;
37
- }
38
- }
39
- if (selected_service) {
40
- await this.send_request(cli, selected_service);
33
+ const selected_service = await this.select_service(flags.service, { 'not_status': 'pending' }, 'Stop Service');
34
+ if (!selected_service) {
35
+ return;
41
36
  }
37
+ await this.send_request(cli, selected_service);
42
38
  }
43
39
  async send_request(cli, selected_service) {
44
40
  try {
45
41
  const { data } = await axios.get(`services/${selected_service}/stop/`, this.axiosConfig);
46
42
  if (data.success) {
47
- cli.log(`${chalk.green('[Success]')} Service '${selected_service}' stopped successfully.`);
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}`)}`));
48
45
  }
49
46
  else {
50
- cli.log(`${chalk.red('[Error]')} Failed to stop service '${selected_service}'. ${data.message || 'Please try again later.'}`);
47
+ cli.log(ui.error(`Could not stop ${ui.value(selected_service)}. ${data.message || 'Please try again in a moment.'}`));
51
48
  }
52
49
  }
53
50
  catch (error) {
@@ -57,10 +54,11 @@ export default class ServiceStop extends Command {
57
54
  operation: 'Stop Service'
58
55
  });
59
56
  if (errorInfo.status === 404) {
60
- cli.log(`${chalk.red('[Error]')} Service '${selected_service}' not found. Please verify the service name using 'chabok service list' and try again.`);
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')}`));
61
59
  }
62
60
  else {
63
- cli.log(`${chalk.red('[Error]')} ${errorInfo.message}`);
61
+ cli.log(ui.error(errorInfo.message));
64
62
  }
65
63
  logErrorDetails(errorInfo, cli);
66
64
  }
@@ -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,19 +1,35 @@
1
1
  import { AxiosRequestConfig } from "axios";
2
- import { Ignore } from 'ignore';
3
- import type { ConfigFile, Service, ErrorInfo } from './types.js';
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;
4
7
  export declare function isObject(obj: unknown): obj is Record<string, unknown>;
5
8
  export declare function isEmptyObject(obj: unknown): boolean;
6
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
+ */
7
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>[];
8
22
  export declare function trimLines(lines: string[]): string[];
9
23
  export declare const loadIgnoreFile: (ignoreInstance: Ignore, ignoreFilePath: string, projectPath: string) => void;
10
24
  export declare function addIgnorePatterns(ignoreInstance: Ignore, projectPath: string, dir: string): 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
+ */
11
31
  export declare const checkUpdate: (version: string) => Promise<void>;
12
- export declare function handleApiError(error: unknown, defaultMessage: string, context?: {
13
- endpoint?: string;
14
- serviceName?: string;
15
- operation?: string;
16
- }): ErrorInfo;
32
+ export declare function handleApiError(error: unknown, defaultMessage: string, context?: ErrorContext): ErrorInfo;
17
33
  export declare function logErrorDetails(errorInfo: ErrorInfo, command: {
18
34
  log: (msg: string) => void;
19
35
  }): void;