@choiceopen/automation-plugin-cli 0.1.2 → 0.1.3

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/README.md CHANGED
@@ -20,7 +20,7 @@ $ npm install -g @choiceopen/automation-plugin-cli
20
20
  $ automation COMMAND
21
21
  running command...
22
22
  $ automation (--version)
23
- @choiceopen/automation-plugin-cli/0.1.2 darwin-arm64 node-v24.12.0
23
+ @choiceopen/automation-plugin-cli/0.1.3 darwin-arm64 node-v24.13.0
24
24
  $ automation --help [COMMAND]
25
25
  USAGE
26
26
  $ automation COMMAND
@@ -29,36 +29,10 @@ USAGE
29
29
  <!-- usagestop -->
30
30
  # Commands
31
31
  <!-- commands -->
32
- * [`automation auth login`](#automation-auth-login)
33
32
  * [`automation autocomplete [SHELL]`](#automation-autocomplete-shell)
34
33
  * [`automation help [COMMAND]`](#automation-help-command)
35
- * [`automation plugin refresh-key`](#automation-plugin-refresh-key)
36
34
  * [`automation version`](#automation-version)
37
35
 
38
- ## `automation auth login`
39
-
40
- Uses device authorization flow to login with your Choiceform account by following these steps:
41
-
42
- ```
43
- USAGE
44
- $ automation auth login
45
-
46
- DESCRIPTION
47
- Uses device authorization flow to login with your Choiceform account by following these steps:
48
-
49
- 1. Request a validation code automatically
50
- 2. Show the validation code and a verification URL to the user
51
- 3. Open the verification URL in the user's browser and paste the verification code
52
- 4. Submit the validation code to complete the device authorization flow
53
-
54
- EXAMPLES
55
- Login by using device authorization flow
56
-
57
- $ automation auth login
58
- ```
59
-
60
- _See code: [src/commands/auth/login.ts](https://github.com/choice-open/automation-plugin-cli/blob/v0.1.2/src/commands/auth/login.ts)_
61
-
62
36
  ## `automation autocomplete [SHELL]`
63
37
 
64
38
  Display autocomplete installation instructions.
@@ -110,23 +84,6 @@ DESCRIPTION
110
84
 
111
85
  _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.2.36/src/commands/help.ts)_
112
86
 
113
- ## `automation plugin refresh-key`
114
-
115
- Refresh or create API Key for plugin debugging in development stage.
116
-
117
- ```
118
- USAGE
119
- $ automation plugin refresh-key
120
-
121
- DESCRIPTION
122
- Refresh or create API Key for plugin debugging in development stage.
123
-
124
- EXAMPLES
125
- $ automation plugin refresh-key
126
- ```
127
-
128
- _See code: [src/commands/plugin/refresh-key.ts](https://github.com/choice-open/automation-plugin-cli/blob/v0.1.2/src/commands/plugin/refresh-key.ts)_
129
-
130
87
  ## `automation version`
131
88
 
132
89
  ```
@@ -1,92 +1,4 @@
1
1
  {
2
- "commands": {
3
- "auth:login": {
4
- "aliases": [],
5
- "args": {},
6
- "description": "Uses device authorization flow to login with your Choiceform account by following these steps:\n\n1. Request a validation code automatically\n2. Show the validation code and a verification URL to the user\n3. Open the verification URL in the user's browser and paste the verification code\n4. Submit the validation code to complete the device authorization flow",
7
- "examples": [
8
- {
9
- "command": "<%= config.bin %> <%= command.id %>",
10
- "description": "Login by using device authorization flow"
11
- }
12
- ],
13
- "flags": {},
14
- "hasDynamicHelp": false,
15
- "hiddenAliases": [],
16
- "id": "auth:login",
17
- "pluginAlias": "@choiceopen/automation-plugin-cli",
18
- "pluginName": "@choiceopen/automation-plugin-cli",
19
- "pluginType": "core",
20
- "strict": true,
21
- "enableJsonFlag": false,
22
- "isESM": true,
23
- "relativePath": [
24
- "dist",
25
- "commands",
26
- "auth",
27
- "login.js"
28
- ]
29
- },
30
- "plugin": {
31
- "aliases": [],
32
- "args": {},
33
- "description": "Manages your plugin via subcommands",
34
- "examples": [
35
- {
36
- "command": "<%= config.bin %> help <%= command.id %> [COMMAND]",
37
- "description": "Check help for each individual sub-command"
38
- }
39
- ],
40
- "flags": {
41
- "json": {
42
- "description": "Format output as json.",
43
- "helpGroup": "GLOBAL",
44
- "name": "json",
45
- "allowNo": false,
46
- "type": "boolean"
47
- }
48
- },
49
- "hasDynamicHelp": false,
50
- "hidden": true,
51
- "hiddenAliases": [],
52
- "id": "plugin",
53
- "pluginAlias": "@choiceopen/automation-plugin-cli",
54
- "pluginName": "@choiceopen/automation-plugin-cli",
55
- "pluginType": "core",
56
- "strict": true,
57
- "enableJsonFlag": true,
58
- "isESM": true,
59
- "relativePath": [
60
- "dist",
61
- "commands",
62
- "plugin",
63
- "index.js"
64
- ]
65
- },
66
- "plugin:refresh-key": {
67
- "aliases": [],
68
- "args": {},
69
- "description": "Refresh or create API Key for plugin debugging in development stage.",
70
- "examples": [
71
- "<%= config.bin %> <%= command.id %>"
72
- ],
73
- "flags": {},
74
- "hasDynamicHelp": false,
75
- "hiddenAliases": [],
76
- "id": "plugin:refresh-key",
77
- "pluginAlias": "@choiceopen/automation-plugin-cli",
78
- "pluginName": "@choiceopen/automation-plugin-cli",
79
- "pluginType": "core",
80
- "strict": true,
81
- "enableJsonFlag": false,
82
- "isESM": true,
83
- "relativePath": [
84
- "dist",
85
- "commands",
86
- "plugin",
87
- "refresh-key.js"
88
- ]
89
- }
90
- },
91
- "version": "0.1.2"
2
+ "commands": {},
3
+ "version": "0.1.3"
92
4
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@choiceopen/automation-plugin-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "A command-line utility for building and publishing Choiceform Automation Plugin.",
5
5
  "keywords": [
6
6
  "oclif"
@@ -1,117 +0,0 @@
1
- import confirm from "@inquirer/confirm";
2
- import { Command } from "@oclif/core";
3
- import { colorize } from "@oclif/core/ux";
4
- import { assert } from "es-toolkit";
5
- import open from "open";
6
- import { dedent } from "ts-dedent";
7
- import yoctoSpinner from "yocto-spinner";
8
- import * as configStore from "../../utils/config.js";
9
- export default class AuthLogin extends Command {
10
- static description = dedent `
11
- Uses device authorization flow to login with your Choiceform account by following these steps:
12
-
13
- 1. Request a validation code automatically
14
- 2. Show the validation code and a verification URL to the user
15
- 3. Open the verification URL in the user's browser and paste the verification code
16
- 4. Submit the validation code to complete the device authorization flow
17
- `;
18
- static examples = [
19
- {
20
- command: "<%= config.bin %> <%= command.id %>",
21
- description: "Login by using device authorization flow",
22
- },
23
- ];
24
- static flags = {};
25
- pollingInterval = 5;
26
- client_id = "automation_plugin_cli";
27
- grant_type = "urn:ietf:params:oauth:grant-type:device_code";
28
- async run() {
29
- await this.parse(AuthLogin);
30
- const config = await configStore.load();
31
- assert(config.auth?.endpoint, "Auth endpoint is required");
32
- this.endpoint = config.auth.endpoint;
33
- const payload = await this.requestDeviceCode(this.endpoint);
34
- this.log(colorize("yellowBright", "Starting device authorization flow...\n"));
35
- this.log(colorize("bold", colorize("gray", "Verification URL : ")), payload.verification_uri);
36
- this.log(colorize("bold", colorize("gray", "Verification Code: ")), `${payload.user_code}\n`);
37
- const autoOpen = await confirm({
38
- message: dedent `
39
- Do you want to open the verification URL in your browser automatically?
40
- If not, you can manually open the URL and paste the code.
41
- `,
42
- });
43
- if (autoOpen) {
44
- await open(payload.verification_uri_complete);
45
- }
46
- const spinner = yoctoSpinner({ text: "Polling for token..." }).start();
47
- const result = (await this.pollForToken(payload.device_code, spinner));
48
- await configStore.update({ auth: { access_token: result.access_token } });
49
- const session = await fetch(`${this.endpoint}/v1/auth/get-session`, {
50
- headers: {
51
- "Content-Type": "application/json",
52
- "User-Agent": "Choiceform (Automation Plugin CLI)",
53
- Authorization: `Bearer ${result.access_token}`,
54
- },
55
- }).then((response) => response.json());
56
- this.log(colorize("greenBright", dedent `
57
- Welcome back, ${session.user.name} <${session.user.email}>!
58
- To create a new plugin, you can use the following command:
59
- \`${colorize("bold", colorize("yellowBright", "automation plugin init"))}\`
60
- `));
61
- }
62
- async requestDeviceCode(endpoint) {
63
- const response = await fetch(`${endpoint}/v1/auth/device/code`, {
64
- method: "POST",
65
- headers: {
66
- "Content-Type": "application/json",
67
- "User-Agent": "Choiceform (Automation Plugin CLI)",
68
- },
69
- body: JSON.stringify({ client_id: this.client_id }),
70
- });
71
- return (await response.json());
72
- }
73
- async pollForToken(device_code, spinner) {
74
- return new Promise((resolve) => {
75
- const poll = async () => {
76
- const response = await fetch(`${this.endpoint}/v1/auth/device/token`, {
77
- method: "POST",
78
- headers: {
79
- "Content-Type": "application/json",
80
- "User-Agent": "Choiceform (Automation Plugin CLI)",
81
- },
82
- body: JSON.stringify({
83
- grant_type: this.grant_type,
84
- client_id: this.client_id,
85
- device_code,
86
- }),
87
- });
88
- const payload = (await response.json());
89
- if (payload.error) {
90
- switch (payload.error) {
91
- case "authorization_pending":
92
- setTimeout(poll, this.pollingInterval * 1000);
93
- break;
94
- case "slow_down":
95
- this.pollingInterval += 5;
96
- setTimeout(poll, this.pollingInterval * 1000);
97
- break;
98
- case "access_denied":
99
- spinner.error("Access was denied by the user");
100
- return process.exit(0);
101
- case "expired_token":
102
- spinner.error("The device code has expired. Please try again.");
103
- return process.exit(0);
104
- default:
105
- spinner.error(`Error: ${payload.error_description}`);
106
- return process.exit(1);
107
- }
108
- }
109
- else {
110
- spinner.success("Token acquired successfully\n");
111
- resolve(payload);
112
- }
113
- };
114
- setTimeout(poll, this.pollingInterval * 1000);
115
- });
116
- }
117
- }
@@ -1,16 +0,0 @@
1
- import { Command } from "@oclif/core";
2
- export default class Plugin extends Command {
3
- static description = "Manages your plugin via subcommands";
4
- static enableJsonFlag = true;
5
- static examples = [
6
- {
7
- command: "<%= config.bin %> help <%= command.id %> [COMMAND]",
8
- description: "Check help for each individual sub-command",
9
- },
10
- ];
11
- static hidden = true;
12
- async run() {
13
- await this.parse(Plugin);
14
- await this.config.runCommand("help", ["plugin"]);
15
- }
16
- }
@@ -1,103 +0,0 @@
1
- import { promises as fs } from "node:fs";
2
- import { join } from "node:path";
3
- import { Command } from "@oclif/core";
4
- import { colorize } from "@oclif/core/ux";
5
- import { assert } from "es-toolkit";
6
- import { dedent } from "ts-dedent";
7
- import * as configStore from "../../utils/config.js";
8
- export default class PluginRefreshKey extends Command {
9
- static description = dedent `Refresh or create API Key for plugin debugging in development stage.`;
10
- static examples = ["<%= config.bin %> <%= command.id %>"];
11
- async run() {
12
- await this.parse(PluginRefreshKey);
13
- // Step 1: Check access token
14
- const config = await configStore.load();
15
- if (!config.auth?.access_token) {
16
- this.log(colorize("red", "✗ You're not authenticated yet, please run 'automation auth login' first."));
17
- return process.exit(1);
18
- }
19
- try {
20
- // Step 2: Fetch debug API Key
21
- const apiKey = await this.fetchDebugApiKey(config.auth.access_token);
22
- // Step 3: Manage .env file
23
- await this.updateEnvFile(apiKey);
24
- // Display success message
25
- this.log(colorize("green", "✓ Debug API Key refreshed successfully"));
26
- this.log(colorize("green", "✓ DEBUG_API_KEY updated in .env file"));
27
- this.log("");
28
- this.log("Your debug API Key has been saved to .env file.");
29
- this.log(`Key preview: ${this.maskApiKey(apiKey)}`);
30
- }
31
- catch (error) {
32
- const message = error instanceof Error ? error.message : "Unknown error";
33
- this.log(colorize("red", `✗ Failed to refresh debug API Key: ${message}`));
34
- return process.exit(1);
35
- }
36
- }
37
- async fetchDebugApiKey(accessToken) {
38
- // Get endpoint from config, use default if not available
39
- const config = await configStore.load();
40
- assert(config.hub?.endpoint, "Hub endpoint is required");
41
- const response = await fetch(`${config.hub.endpoint}/api/v1/debug_api_key`, {
42
- method: "GET",
43
- headers: {
44
- "Content-Type": "application/json",
45
- "User-Agent": "Choiceform (Automation Plugin CLI)",
46
- Authorization: `Bearer ${accessToken}`,
47
- },
48
- });
49
- if (!response.ok) {
50
- if (response.status === 401) {
51
- throw new Error("Access token is invalid or expired, please login again");
52
- }
53
- throw new Error(`API request failed: ${response.status} ${response.statusText}`);
54
- }
55
- const data = (await response.json());
56
- if (!data.api_key) {
57
- throw new Error("API response format error: missing api_key field");
58
- }
59
- return data.api_key;
60
- }
61
- async updateEnvFile(apiKey) {
62
- const envPath = join(process.cwd(), ".env");
63
- try {
64
- // Check if .env file exists
65
- let envContent = "";
66
- let existingKey = false;
67
- try {
68
- envContent = await fs.readFile(envPath, "utf-8");
69
- existingKey = envContent.includes("DEBUG_API_KEY=");
70
- }
71
- catch (_error) {
72
- // File doesn't exist, will create new file
73
- }
74
- let newContent;
75
- if (existingKey) {
76
- // Replace existing DEBUG_API_KEY
77
- newContent = envContent.replace(/^DEBUG_API_KEY=.*$/m, `DEBUG_API_KEY=${apiKey}`);
78
- }
79
- else {
80
- // Append new DEBUG_API_KEY
81
- const separator = envContent && !envContent.endsWith("\n") ? "\n" : "";
82
- newContent = `${envContent + separator}DEBUG_API_KEY=${apiKey}\n`;
83
- }
84
- await fs.writeFile(envPath, newContent, "utf-8");
85
- }
86
- catch (error) {
87
- if (error instanceof Error &&
88
- "code" in error &&
89
- error.code === "EACCES") {
90
- throw new Error("Permission denied: cannot write .env file");
91
- }
92
- throw new Error(`Failed to update .env file: ${error instanceof Error ? error.message : "Unknown error"}`);
93
- }
94
- }
95
- maskApiKey(apiKey) {
96
- if (apiKey.length <= 8) {
97
- return "***";
98
- }
99
- const start = apiKey.substring(0, 4);
100
- const end = apiKey.substring(apiKey.length - 4);
101
- return `${start}...${end}`;
102
- }
103
- }
@@ -1,15 +0,0 @@
1
- import { z } from "zod";
2
- declare const ConfigSchema: z.ZodObject<{
3
- auth: z.ZodOptional<z.ZodObject<{
4
- endpoint: z.ZodOptional<z.ZodURL>;
5
- access_token: z.ZodOptional<z.ZodString>;
6
- }, z.core.$strip>>;
7
- hub: z.ZodOptional<z.ZodObject<{
8
- endpoint: z.ZodOptional<z.ZodURL>;
9
- }, z.core.$strip>>;
10
- }, z.core.$strip>;
11
- export type Config = z.infer<typeof ConfigSchema>;
12
- export declare function save(config: Config): Promise<void>;
13
- export declare function load(): Promise<Config>;
14
- export declare function update(updates: Partial<Config>): Promise<void>;
15
- export {};
@@ -1,65 +0,0 @@
1
- import { promises as fs } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { join } from "node:path";
4
- import { toMerged } from "es-toolkit/object";
5
- import { z } from "zod";
6
- function getConfigDir() {
7
- return process.env.CHOICEFORM_CONFIG_DIR ?? join(homedir(), ".choiceform");
8
- }
9
- function getConfigFile() {
10
- return join(getConfigDir(), "atomemo.json");
11
- }
12
- const ConfigSchema = z.object({
13
- auth: z
14
- .object({
15
- endpoint: z.url().optional(),
16
- access_token: z.string().optional(),
17
- })
18
- .optional(),
19
- hub: z
20
- .object({
21
- endpoint: z.url().optional(),
22
- })
23
- .optional(),
24
- });
25
- export async function save(config) {
26
- const validated = ConfigSchema.parse(config);
27
- const configDir = getConfigDir();
28
- const configFile = getConfigFile();
29
- await fs.mkdir(configDir, { recursive: true });
30
- await fs.writeFile(configFile, JSON.stringify(validated, null, 2), "utf-8");
31
- }
32
- export async function load() {
33
- const configFile = getConfigFile();
34
- try {
35
- await fs.access(configFile);
36
- }
37
- catch (error) {
38
- if (error instanceof Error &&
39
- ("code" in error ? error.code === "ENOENT" : false)) {
40
- const defaultConfig = {
41
- auth: {
42
- endpoint: process.env.NODE_ENV === "production"
43
- ? "https://oneauth.choiceform.io"
44
- : "https://oneauth.choiceform.io",
45
- },
46
- hub: {
47
- endpoint: process.env.NODE_ENV === "production"
48
- ? "https://automation-plugin-api.choiceform.io"
49
- : "https://automation-plugin-api.choiceform.io",
50
- },
51
- };
52
- await save(defaultConfig);
53
- return defaultConfig;
54
- }
55
- throw error;
56
- }
57
- const content = await fs.readFile(configFile, "utf-8");
58
- const parsed = JSON.parse(content);
59
- return ConfigSchema.parse(parsed);
60
- }
61
- export async function update(updates) {
62
- const existing = await load();
63
- const merged = toMerged(existing ?? {}, updates);
64
- await save(merged);
65
- }