@choiceopen/automation-plugin-cli 0.1.3 → 0.2.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.
- package/README.md +227 -23
- package/dist/commands/auth/index.d.ts +11 -0
- package/dist/commands/auth/index.js +15 -0
- package/dist/commands/auth/login.d.ts +16 -0
- package/dist/commands/auth/login.js +117 -0
- package/dist/commands/auth/status.d.ts +11 -0
- package/dist/commands/auth/status.js +61 -0
- package/dist/commands/plugin/checksum.d.ts +13 -0
- package/dist/commands/plugin/checksum.js +22 -0
- package/dist/commands/plugin/index.d.ts +11 -0
- package/dist/commands/plugin/index.js +16 -0
- package/dist/commands/plugin/init.d.ts +31 -0
- package/dist/commands/plugin/init.js +289 -0
- package/dist/commands/plugin/pack.d.ts +13 -0
- package/dist/commands/plugin/pack.js +22 -0
- package/dist/commands/plugin/permission.d.ts +13 -0
- package/dist/commands/plugin/permission.js +22 -0
- package/dist/commands/plugin/refresh-key.d.ts +9 -0
- package/dist/commands/plugin/refresh-key.js +103 -0
- package/dist/commands/plugin/run.d.ts +13 -0
- package/dist/commands/plugin/run.js +22 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/utils/config.d.ts +15 -0
- package/dist/utils/config.js +65 -0
- package/dist/utils/generator.d.ts +20 -0
- package/dist/utils/generator.js +68 -0
- package/dist/utils/theme.d.ts +13 -0
- package/dist/utils/theme.js +13 -0
- package/dist/utils/views.d.ts +2 -0
- package/dist/utils/views.js +8 -0
- package/oclif.manifest.json +452 -2
- package/package.json +10 -11
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import checkbox from "@inquirer/checkbox";
|
|
3
|
+
import input from "@inquirer/input";
|
|
4
|
+
import select from "@inquirer/select";
|
|
5
|
+
import { Command, Flags } from "@oclif/core";
|
|
6
|
+
import { colorize } from "@oclif/core/ux";
|
|
7
|
+
import { assert, isString } from "es-toolkit";
|
|
8
|
+
import { dedent } from "ts-dedent";
|
|
9
|
+
import z from "zod";
|
|
10
|
+
import { createPluginGenerator } from "../../utils/generator.js";
|
|
11
|
+
import { checkboxTheme, selectTheme } from "../../utils/theme.js";
|
|
12
|
+
const LOCALES = ["en_US", "zh_Hans", "ja_JP"];
|
|
13
|
+
const LANGUAGES = ["elixir", "python", "typescript"];
|
|
14
|
+
export default class PluginInit extends Command {
|
|
15
|
+
static description = dedent `
|
|
16
|
+
Initialize a new plugin with step-by-step interactive instructions.
|
|
17
|
+
|
|
18
|
+
Providing required flags skips interactive flow and completes initialization in one go.
|
|
19
|
+
`;
|
|
20
|
+
static examples = [
|
|
21
|
+
{
|
|
22
|
+
command: "<%= config.bin %> <%= command.id %>",
|
|
23
|
+
description: "Start with interactive initialization:",
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
static flags = {
|
|
27
|
+
interactive: Flags.boolean({
|
|
28
|
+
allowNo: true,
|
|
29
|
+
char: "i",
|
|
30
|
+
default: true,
|
|
31
|
+
summary: "Use interactive mode (by default)",
|
|
32
|
+
}),
|
|
33
|
+
name: Flags.string({
|
|
34
|
+
char: "n",
|
|
35
|
+
helpValue: "my-awesome-plugin",
|
|
36
|
+
summary: "Plugin name",
|
|
37
|
+
}),
|
|
38
|
+
description: Flags.string({
|
|
39
|
+
char: "d",
|
|
40
|
+
default: "",
|
|
41
|
+
helpValue: "Descriptive text...",
|
|
42
|
+
summary: "Short description",
|
|
43
|
+
}),
|
|
44
|
+
author: Flags.string({
|
|
45
|
+
char: "a",
|
|
46
|
+
default: "",
|
|
47
|
+
helpValue: "John Doe",
|
|
48
|
+
summary: "Author name",
|
|
49
|
+
}),
|
|
50
|
+
email: Flags.string({
|
|
51
|
+
char: "e",
|
|
52
|
+
default: "",
|
|
53
|
+
helpValue: "john.doe@example.com",
|
|
54
|
+
summary: "Author email address",
|
|
55
|
+
}),
|
|
56
|
+
url: Flags.string({
|
|
57
|
+
char: "u",
|
|
58
|
+
default: "",
|
|
59
|
+
summary: "Repository URL",
|
|
60
|
+
}),
|
|
61
|
+
locales: Flags.option({
|
|
62
|
+
multiple: true,
|
|
63
|
+
options: LOCALES,
|
|
64
|
+
})({
|
|
65
|
+
delimiter: ",",
|
|
66
|
+
summary: "Provide READMEs in which languages",
|
|
67
|
+
}),
|
|
68
|
+
language: Flags.option({
|
|
69
|
+
multiple: false,
|
|
70
|
+
options: LANGUAGES,
|
|
71
|
+
})({
|
|
72
|
+
char: "l",
|
|
73
|
+
summary: "Programming language to use for plugin development",
|
|
74
|
+
}),
|
|
75
|
+
type: Flags.option({
|
|
76
|
+
multiple: false,
|
|
77
|
+
options: ["extension", "llm", "tool", "trigger"],
|
|
78
|
+
})({
|
|
79
|
+
char: "t",
|
|
80
|
+
summary: "Plugin type",
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
async run() {
|
|
84
|
+
const { flags } = await this.parse(PluginInit);
|
|
85
|
+
this.reconcileInteractiveFlag(flags);
|
|
86
|
+
if (flags.interactive) {
|
|
87
|
+
await this.runInteractiveMode(flags);
|
|
88
|
+
}
|
|
89
|
+
assert(flags.name, "flags.name should be valid here...");
|
|
90
|
+
assert(flags.language, "flags.language should be valid here...");
|
|
91
|
+
const generator = createPluginGenerator(flags.language, {
|
|
92
|
+
props: {
|
|
93
|
+
...flags,
|
|
94
|
+
createdAt: new Date().toISOString(),
|
|
95
|
+
date: new Date().toISOString().slice(0, 10),
|
|
96
|
+
year: new Date().getFullYear().toString(),
|
|
97
|
+
},
|
|
98
|
+
target: path.join(process.cwd(), flags.name),
|
|
99
|
+
});
|
|
100
|
+
await generator.generate();
|
|
101
|
+
}
|
|
102
|
+
nameIsValid(name) {
|
|
103
|
+
return isString(name) && /^[a-z][a-z0-9_-]{2,62}[a-z0-9]$/.test(name);
|
|
104
|
+
}
|
|
105
|
+
reconcileInteractiveFlag(flags) {
|
|
106
|
+
if (flags.interactive && this.nameIsValid(flags.name)) {
|
|
107
|
+
flags.interactive = false;
|
|
108
|
+
}
|
|
109
|
+
if (!flags.interactive && !this.nameIsValid(flags.name)) {
|
|
110
|
+
this.log(colorize("redBright", dedent `
|
|
111
|
+
Without interactive mode, you should provide initial information manually.
|
|
112
|
+
Use ${colorize("blue", "atomemo help plugin init")} to see all available options.
|
|
113
|
+
`));
|
|
114
|
+
this.exit(0);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async runInteractiveMode(flags) {
|
|
118
|
+
this.log(dedent `
|
|
119
|
+
Guiding you through creating a new plugin in interactive mode
|
|
120
|
+
Please follow the instructions below to complete the process:\n
|
|
121
|
+
`);
|
|
122
|
+
try {
|
|
123
|
+
flags.name = await this.collectName();
|
|
124
|
+
flags.description = await this.collectDescription();
|
|
125
|
+
flags.author = await this.collectAuthor();
|
|
126
|
+
flags.email = await this.collectEmail();
|
|
127
|
+
flags.url = await this.collectURL();
|
|
128
|
+
flags.locales = await this.collectLocales(flags);
|
|
129
|
+
flags.language = await this.collectLanguage();
|
|
130
|
+
flags.type = await this.collectType();
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async collectName() {
|
|
140
|
+
return await input({
|
|
141
|
+
message: "What's the name of this new plugin:",
|
|
142
|
+
pattern: /^[a-z][a-z0-9_-]{2,62}[a-z0-9]$/,
|
|
143
|
+
patternError: dedent `
|
|
144
|
+
You must provide a name for the new plugin:
|
|
145
|
+
- Only lowercase letters, digits, underscores, and hyphens are allowed
|
|
146
|
+
- Minimum length of 4 and maximum length of 64
|
|
147
|
+
- Starts with a lowercase letter (not a digit)
|
|
148
|
+
- Ends with a lowercase letter or digit (not underscore or hyphen)
|
|
149
|
+
`,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async collectDescription() {
|
|
153
|
+
return await input({
|
|
154
|
+
message: "How do you describe this new plugin:",
|
|
155
|
+
default: "A brief description of the plugin's functionality",
|
|
156
|
+
prefill: "tab",
|
|
157
|
+
pattern: /^.{16,256}$/,
|
|
158
|
+
patternError: dedent `
|
|
159
|
+
You must provide a description for the new plugin:
|
|
160
|
+
- Allows any characters, minimum 16 characters, maximum 256 characters
|
|
161
|
+
`,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
async collectAuthor() {
|
|
165
|
+
return await input({
|
|
166
|
+
message: "Who is the author of the new plugin:",
|
|
167
|
+
default: "John Doe",
|
|
168
|
+
prefill: "tab",
|
|
169
|
+
pattern: /^.{2,64}$/,
|
|
170
|
+
patternError: dedent `
|
|
171
|
+
You must provide the author name:
|
|
172
|
+
- Allows any characters, minimum 2 characters, maximum 64 characters
|
|
173
|
+
`,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
async collectEmail() {
|
|
177
|
+
return await input({
|
|
178
|
+
message: "What is the email address of the author:",
|
|
179
|
+
default: "john.doe@example.com",
|
|
180
|
+
prefill: "tab",
|
|
181
|
+
pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(?:\.[a-zA-Z]{2,})*$/,
|
|
182
|
+
patternError: dedent `You must provide the author email`,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
async collectURL() {
|
|
186
|
+
return await input({
|
|
187
|
+
message: "What is the repository URL address (Optional):",
|
|
188
|
+
default: "https://github.com/[user]/[repo]",
|
|
189
|
+
prefill: "tab",
|
|
190
|
+
validate: (value) => {
|
|
191
|
+
const { success } = z
|
|
192
|
+
.url({ normalize: true, protocol: /https?|git/ })
|
|
193
|
+
.optional()
|
|
194
|
+
.safeParse(value);
|
|
195
|
+
return success || "You must provide a valid URL";
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async collectLocales(flags) {
|
|
200
|
+
const values = await checkbox({
|
|
201
|
+
choices: [
|
|
202
|
+
{
|
|
203
|
+
checked: flags.locales?.includes("en_US"),
|
|
204
|
+
disabled: "(required)",
|
|
205
|
+
name: "English",
|
|
206
|
+
description: "English (United States)",
|
|
207
|
+
value: "en_US",
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
checked: flags.locales?.includes("zh_Hans"),
|
|
211
|
+
disabled: false,
|
|
212
|
+
name: "简体中文",
|
|
213
|
+
description: "Simplified Chinese (China)",
|
|
214
|
+
value: "zh_Hans",
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
checked: flags.locales?.includes("ja_JP"),
|
|
218
|
+
disabled: false,
|
|
219
|
+
name: "日本語",
|
|
220
|
+
description: "Japanese (Japan)",
|
|
221
|
+
value: "ja_JP",
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
message: "Provide READMEs in which language(s)?",
|
|
225
|
+
theme: checkboxTheme,
|
|
226
|
+
});
|
|
227
|
+
return ["en_US", ...values];
|
|
228
|
+
}
|
|
229
|
+
async collectLanguage() {
|
|
230
|
+
return await select({
|
|
231
|
+
choices: [
|
|
232
|
+
{
|
|
233
|
+
name: "Elixir",
|
|
234
|
+
value: "elixir",
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: "Python",
|
|
238
|
+
value: "python",
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: "TypeScript",
|
|
242
|
+
value: "typescript",
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
message: "What programming language do you prefer for developing this plugin?",
|
|
246
|
+
theme: selectTheme,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
async collectType() {
|
|
250
|
+
return await select({
|
|
251
|
+
choices: [
|
|
252
|
+
{
|
|
253
|
+
name: "Extension",
|
|
254
|
+
value: "extension",
|
|
255
|
+
description: "Extend capabilities by integrating with external APIs.",
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: "Model",
|
|
259
|
+
value: "llm",
|
|
260
|
+
description: "Introduce more LLMs to enhance AI capabilities.",
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
name: "Tool",
|
|
264
|
+
value: "tool",
|
|
265
|
+
description: "Complete specific tasks, typically invoked by LLMs.",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
name: "Trigger",
|
|
269
|
+
value: "trigger",
|
|
270
|
+
description: "Run workflows by receiving events through webhooks.",
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
message: dedent `
|
|
274
|
+
${colorize("blue", "Choose the type of the new plugin")}
|
|
275
|
+
|
|
276
|
+
Plugins can extend the platform's capabilities in multiple ways, making workflows more flexible and powerful.
|
|
277
|
+
Based on your specific requirement, plugins can be categorized into the following types:
|
|
278
|
+
|
|
279
|
+
- ${colorize("yellowBright", "Extension")}: Provide more functionality to workflows by integrating external service APIs
|
|
280
|
+
- ${colorize("yellowBright", "Model")}: Access more large language models to enrich the AI capabilities of workflows
|
|
281
|
+
- ${colorize("yellowBright", "Tool")}: Customized logic to perform specific tasks, typically invoked by LLMs and/or Agents
|
|
282
|
+
- ${colorize("yellowBright", "Trigger")}: Receive external events through webhooks to start workflows with initial input data
|
|
283
|
+
|
|
284
|
+
Please select the matching type from the following options:
|
|
285
|
+
`,
|
|
286
|
+
theme: selectTheme,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from "@oclif/core";
|
|
2
|
+
export default class PluginPack extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
file: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Args, Command, Flags } from "@oclif/core";
|
|
2
|
+
export default class PluginPack extends Command {
|
|
3
|
+
static args = {
|
|
4
|
+
file: Args.string({ description: "file to read" }),
|
|
5
|
+
};
|
|
6
|
+
static description = "describe the command here";
|
|
7
|
+
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
8
|
+
static flags = {
|
|
9
|
+
// flag with no value (-f, --force)
|
|
10
|
+
force: Flags.boolean({ char: "f" }),
|
|
11
|
+
// flag with a value (-n, --name=VALUE)
|
|
12
|
+
name: Flags.string({ char: "n", description: "name to print" }),
|
|
13
|
+
};
|
|
14
|
+
async run() {
|
|
15
|
+
const { args, flags } = await this.parse(PluginPack);
|
|
16
|
+
const name = flags.name ?? "world";
|
|
17
|
+
this.log(`hello ${name} from /Users/nightire/Code/github.com/choice-open/automation-plugin-cli/src/commands/plugin/pack.ts`);
|
|
18
|
+
if (args.file && flags.force) {
|
|
19
|
+
this.log(`you input --force and --file: ${args.file}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from "@oclif/core";
|
|
2
|
+
export default class PluginPermission extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
file: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Args, Command, Flags } from "@oclif/core";
|
|
2
|
+
export default class PluginPermission extends Command {
|
|
3
|
+
static args = {
|
|
4
|
+
file: Args.string({ description: "file to read" }),
|
|
5
|
+
};
|
|
6
|
+
static description = "describe the command here";
|
|
7
|
+
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
8
|
+
static flags = {
|
|
9
|
+
// flag with no value (-f, --force)
|
|
10
|
+
force: Flags.boolean({ char: "f" }),
|
|
11
|
+
// flag with a value (-n, --name=VALUE)
|
|
12
|
+
name: Flags.string({ char: "n", description: "name to print" }),
|
|
13
|
+
};
|
|
14
|
+
async run() {
|
|
15
|
+
const { args, flags } = await this.parse(PluginPermission);
|
|
16
|
+
const name = flags.name ?? "world";
|
|
17
|
+
this.log(`hello ${name} from /Users/nightire/Code/github.com/choice-open/automation-plugin-cli/src/commands/plugin/permission.ts`);
|
|
18
|
+
if (args.file && flags.force) {
|
|
19
|
+
this.log(`you input --force and --file: ${args.file}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
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 'atomemo 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 (Atomemo 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
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from "@oclif/core";
|
|
2
|
+
export default class PluginRun extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
file: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Args, Command, Flags } from "@oclif/core";
|
|
2
|
+
export default class PluginRun extends Command {
|
|
3
|
+
static args = {
|
|
4
|
+
file: Args.string({ description: "file to read" }),
|
|
5
|
+
};
|
|
6
|
+
static description = "describe the command here";
|
|
7
|
+
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
8
|
+
static flags = {
|
|
9
|
+
// flag with no value (-f, --force)
|
|
10
|
+
force: Flags.boolean({ char: "f" }),
|
|
11
|
+
// flag with a value (-n, --name=VALUE)
|
|
12
|
+
name: Flags.string({ char: "n", description: "name to print" }),
|
|
13
|
+
};
|
|
14
|
+
async run() {
|
|
15
|
+
const { args, flags } = await this.parse(PluginRun);
|
|
16
|
+
const name = flags.name ?? "world";
|
|
17
|
+
this.log(`hello ${name} from /Users/nightire/Code/github.com/choice-open/automation-plugin-cli/src/commands/plugin/run.ts`);
|
|
18
|
+
if (args.file && flags.force) {
|
|
19
|
+
this.log(`you input --force and --file: ${args.file}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from "@oclif/core";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from "@oclif/core";
|
|
@@ -0,0 +1,15 @@
|
|
|
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 {};
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Eta } from "eta";
|
|
2
|
+
export interface PluginGenerator {
|
|
3
|
+
context: {
|
|
4
|
+
props: Record<string, unknown>;
|
|
5
|
+
target: string;
|
|
6
|
+
};
|
|
7
|
+
renderer: Eta;
|
|
8
|
+
type: string;
|
|
9
|
+
generate(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createPluginGenerator(type: PluginGenerator["type"], context: PluginGenerator["context"]): PluginGenerator;
|
|
12
|
+
export declare class TypeScriptPluginGenerator implements PluginGenerator {
|
|
13
|
+
#private;
|
|
14
|
+
context: PluginGenerator["context"];
|
|
15
|
+
type: "typescript";
|
|
16
|
+
renderer: Eta;
|
|
17
|
+
constructor(context: PluginGenerator["context"]);
|
|
18
|
+
generate(): Promise<void>;
|
|
19
|
+
private groupPermissions;
|
|
20
|
+
}
|