agentica 0.1.0 → 0.12.4

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 (53) hide show
  1. package/README.md +67 -67
  2. package/bin/bases/Connector.d.ts +9 -0
  3. package/bin/bases/Connector.js +67 -0
  4. package/bin/bases/Connector.js.map +1 -0
  5. package/bin/bases/Package.d.ts +11 -0
  6. package/bin/bases/Package.js +74 -0
  7. package/bin/bases/Package.js.map +1 -0
  8. package/bin/bases/Tsconfig.d.ts +5 -0
  9. package/bin/bases/Tsconfig.js +135 -0
  10. package/bin/bases/Tsconfig.js.map +1 -0
  11. package/bin/executable/AgenticaStart.d.ts +7 -0
  12. package/bin/executable/AgenticaStart.js +324 -0
  13. package/bin/executable/AgenticaStart.js.map +1 -0
  14. package/bin/index.js +24 -43
  15. package/bin/index.js.map +1 -1
  16. package/bin/structures/IAgenticaStart.d.ts +31 -0
  17. package/bin/structures/IAgenticaStart.js +3 -0
  18. package/bin/structures/IAgenticaStart.js.map +1 -0
  19. package/bin/structures/IAgenticaStartOption.d.ts +13 -0
  20. package/bin/structures/IAgenticaStartOption.js +3 -0
  21. package/bin/structures/IAgenticaStartOption.js.map +1 -0
  22. package/bin/utils/capitalize.d.ts +1 -0
  23. package/bin/utils/capitalize.js +12 -0
  24. package/bin/utils/capitalize.js.map +1 -0
  25. package/bin/utils/createProjectDirectory.d.ts +3 -0
  26. package/bin/utils/createProjectDirectory.js +16 -0
  27. package/bin/utils/createProjectDirectory.js.map +1 -0
  28. package/bin/utils/getNpmPackages.d.ts +14 -0
  29. package/bin/utils/getNpmPackages.js +147 -0
  30. package/bin/utils/getNpmPackages.js.map +1 -0
  31. package/bin/utils/getQuestions.d.ts +8 -0
  32. package/bin/utils/getQuestions.js +45 -0
  33. package/bin/utils/getQuestions.js.map +1 -0
  34. package/bin/utils/types/PackageManager.d.ts +1 -0
  35. package/bin/utils/types/PackageManager.js +3 -0
  36. package/bin/utils/types/PackageManager.js.map +1 -0
  37. package/bin/utils/types/ProjectOption.d.ts +4 -0
  38. package/bin/utils/types/ProjectOption.js +3 -0
  39. package/bin/utils/types/ProjectOption.js.map +1 -0
  40. package/package.json +11 -3
  41. package/src/bases/Connector.ts +80 -0
  42. package/src/bases/Package.ts +75 -0
  43. package/src/bases/Tsconfig.ts +122 -0
  44. package/src/executable/AgenticaStart.ts +301 -0
  45. package/src/index.ts +39 -60
  46. package/src/structures/IAgenticaStart.ts +35 -0
  47. package/src/structures/IAgenticaStartOption.ts +13 -0
  48. package/src/utils/capitalize.ts +7 -0
  49. package/src/utils/createProjectDirectory.ts +14 -0
  50. package/src/utils/getNpmPackages.ts +45 -0
  51. package/src/utils/getQuestions.ts +48 -0
  52. package/src/utils/types/PackageManager.ts +1 -0
  53. package/src/utils/types/ProjectOption.ts +4 -0
@@ -0,0 +1,301 @@
1
+ import chalk from "chalk";
2
+ import cp from "child_process";
3
+ import fs from "fs/promises";
4
+ import inquirer, { QuestionCollection } from "inquirer";
5
+ import path from "path";
6
+ import prettier from "prettier";
7
+ import typia from "typia";
8
+
9
+ import { Connector } from "../bases/Connector";
10
+ import { Package } from "../bases/Package";
11
+ import { Tsconfig } from "../bases/Tsconfig";
12
+ import { IAgenticaStart } from "../structures/IAgenticaStart";
13
+ import { IAgenticaStartOption } from "../structures/IAgenticaStartOption";
14
+ import { createProjectDirectory } from "../utils/createProjectDirectory";
15
+ import { getNpmPackages } from "../utils/getNpmPackages";
16
+ import { PackageManager } from "../utils/types/PackageManager";
17
+ import { ProjectOptionValue } from "../utils/types/ProjectOption";
18
+
19
+ export namespace AgenticaStart {
20
+ /**
21
+ * Execute `start` command.
22
+ */
23
+ export async function execute({
24
+ projectName,
25
+ options,
26
+ }: IAgenticaStart.IExecuteInput) {
27
+ const projectPath = path.join(process.cwd(), projectName);
28
+
29
+ // Check if project already exists
30
+ if (
31
+ await fs
32
+ .access(path.join(process.cwd(), projectName))
33
+ .then(() => true)
34
+ .catch(() => false)
35
+ ) {
36
+ console.error(
37
+ `❌ Project ${chalk.redBright(projectName)} already exists`,
38
+ );
39
+ return;
40
+ }
41
+
42
+ // Get connector package names from npm and sort alphabetically
43
+ const availableServices = (await getNpmPackages()).sort((a, b) =>
44
+ a.name.localeCompare(b.name),
45
+ );
46
+
47
+ const questions = getQuestions({ services: availableServices, options });
48
+ const config = {
49
+ ...(await inquirer.prompt<{
50
+ projectType: ProjectOptionValue;
51
+ services: string[];
52
+ packageManager: PackageManager;
53
+ openAIKey: string;
54
+ }>(questions)),
55
+ ...(options.project ? { projectType: options.project } : {}),
56
+ };
57
+
58
+ const validAnswers = typia.assert(config);
59
+ const { packageManager, openAIKey, projectType, services } = validAnswers;
60
+
61
+ await AgenticaStarter.execute(projectType)({
62
+ projectName,
63
+ projectPath,
64
+ openAIKey,
65
+ services,
66
+ });
67
+
68
+ // Run package installation
69
+ console.log("📦 Package installation in progress...");
70
+
71
+ Package.installPackage(packageManager)({
72
+ projectPath,
73
+ services,
74
+ });
75
+
76
+ console.log(`\n🎉 Project ${projectName} created`);
77
+
78
+ console.log(
79
+ `\n⚠️ ${chalk.yellow("Note:")} Please implement constructor values for each controller generated in agent.ts or index.ts`,
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Get questions for `start` command.
85
+ */
86
+ const getQuestions = (
87
+ input: IAgenticaStart.IGetQuestionsInput,
88
+ ): QuestionCollection[] => {
89
+ const questions = [
90
+ {
91
+ type: "list",
92
+ name: "packageManager",
93
+ message: "Package Manager",
94
+ choices: [
95
+ "npm",
96
+ "pnpm",
97
+ {
98
+ name: `yarn (berry ${chalk.blueBright("is not supported")})`,
99
+ value: "yarn",
100
+ },
101
+ ],
102
+ },
103
+ input.options.project
104
+ ? null
105
+ : {
106
+ type: "list",
107
+ name: "projectType",
108
+ message: "Project Type",
109
+ choices: Object.values(AgenticaStarter.PROJECT).map((project) => ({
110
+ name: project.title,
111
+ value: project.key,
112
+ })),
113
+ },
114
+ {
115
+ type: "checkbox",
116
+ name: "services",
117
+ message: "Embedded Controllers",
118
+ choices: input.services,
119
+ },
120
+ {
121
+ type: "input",
122
+ name: "openAIKey",
123
+ message: "Please enter your OPENAI_API_KEY:",
124
+ },
125
+ ] satisfies (QuestionCollection | null)[];
126
+
127
+ return questions.filter((question) => question !== null);
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Methods for Agentica start options.
133
+ */
134
+ namespace AgenticaStarter {
135
+ export const execute = (option: ProjectOptionValue) => {
136
+ const runner = PROJECT[option].runner;
137
+ if (!runner) {
138
+ throw new Error(`Not supported project type: ${option}`);
139
+ }
140
+
141
+ return runner;
142
+ };
143
+
144
+ export const PROJECT = {
145
+ standalone: {
146
+ title: `Standalone ${chalk.blueBright("Application")}`,
147
+ key: "standalone",
148
+ runner: async (input: IAgenticaStartOption.IProject): Promise<void> => {
149
+ // Create project directory
150
+ createProjectDirectory({ projectPath: input.projectPath });
151
+
152
+ // Create package.json (without dependencies)
153
+ await Promise.all([
154
+ // create package.json
155
+ Package.create({
156
+ projectName: input.projectName,
157
+ projectPath: input.projectPath,
158
+ }),
159
+ // create tsconfig.json
160
+ Tsconfig.create({ projectPath: input.projectPath }),
161
+ ]);
162
+
163
+ await fs.mkdir(path.join(input.projectPath, "src"), {
164
+ recursive: false,
165
+ });
166
+
167
+ // Create Agentica code
168
+ const agenticaCode = Connector.createAll({ services: input.services });
169
+
170
+ await writeTypescriptFile({
171
+ filePath: path.join(input.projectPath, "src/agent.ts"),
172
+ taskName: "Agentica code",
173
+ content: agenticaCode,
174
+ });
175
+ await setEnvFiles(input);
176
+ },
177
+ },
178
+
179
+ nodejs: {
180
+ title: `NodeJS ${chalk.blueBright("Agent Server")}`,
181
+ key: "nodejs",
182
+ runner: async (input: IAgenticaStartOption.IProject): Promise<void> =>
183
+ nonStandalone("nodejs")(input)(async () => {
184
+ // Modify index.ts: replace import and controller code
185
+ const indexFilePath = path.join(input.projectPath, "src/index.ts");
186
+ const indexFileContent = await fs
187
+ .readFile(indexFilePath, "utf-8")
188
+ .then((content) => {
189
+ if (input.services.length !== 0) {
190
+ return content
191
+ .replace(/import { BbsArticleService }.*;\n/g, "")
192
+ .replace(
193
+ /controllers:\s*\[[\s\S]*?\],\n/,
194
+ "controllers: [/// INSERT CONTROLLER HERE],\n",
195
+ );
196
+ }
197
+ return content;
198
+ });
199
+
200
+ return { indexFilePath, indexFileContent };
201
+ }),
202
+ },
203
+ nestjs: {
204
+ title: `NestJS ${chalk.blueBright("Agent Server")}`,
205
+ key: "nestjs",
206
+ runner: async (input: IAgenticaStartOption.IProject): Promise<void> =>
207
+ nonStandalone("nestjs")(input)(async () => {
208
+ const indexFilePath = path.join(
209
+ input.projectPath,
210
+ "src/controllers/chat/ChatController.ts",
211
+ );
212
+
213
+ const indexFileContent = await fs.readFile(indexFilePath, "utf-8");
214
+ return { indexFilePath, indexFileContent };
215
+ }),
216
+ },
217
+ react: {
218
+ title: `React ${chalk.blueBright("Client Application")} (Currently not supported)`,
219
+ key: "react",
220
+ runner: undefined,
221
+ },
222
+ } as const;
223
+
224
+ const nonStandalone =
225
+ (option: "nodejs" | "nestjs") =>
226
+ (input: IAgenticaStartOption.IProject) =>
227
+ async (
228
+ getIndexFileInfo: () => Promise<{
229
+ indexFilePath: string;
230
+ indexFileContent: string;
231
+ }>,
232
+ ) => {
233
+ clone(option, input.projectName);
234
+
235
+ // Create Agentica code
236
+ const importCode = Connector.create("import")({
237
+ services: input.services,
238
+ });
239
+
240
+ const connectorCode = Connector.create("connector")({
241
+ services: input.services,
242
+ });
243
+
244
+ const { indexFilePath, indexFileContent } = await getIndexFileInfo();
245
+
246
+ // Insert importCode and connectorCode
247
+ const agenticaCode = indexFileContent
248
+ .replace("/// INSERT IMPORT HERE", importCode)
249
+ .replace("/// INSERT CONTROLLER HERE", connectorCode);
250
+
251
+ await writeTypescriptFile({
252
+ filePath: indexFilePath,
253
+ taskName: "Agentica code",
254
+ content: agenticaCode,
255
+ });
256
+ await setEnvFiles(input);
257
+ };
258
+
259
+ const writeTypescriptFile = async (props: {
260
+ filePath: string;
261
+ taskName: string;
262
+ content: string;
263
+ }): Promise<void> => {
264
+ const formattedFileContent = await prettier.format(props.content, {
265
+ parser: "typescript",
266
+ });
267
+
268
+ await fs.writeFile(props.filePath, formattedFileContent);
269
+ console.log(`✅ ${props.taskName} created`);
270
+ };
271
+ /**
272
+ * Set project .env files
273
+ */
274
+ const setEnvFiles = async (
275
+ input: IAgenticaStartOption.IProject,
276
+ ): Promise<void> => {
277
+ // Create .env file
278
+ const envContent = `OPENAI_API_KEY=${input.openAIKey}\n`;
279
+ await fs.writeFile(path.join(input.projectPath, ".env"), envContent);
280
+ console.log("✅ .env created");
281
+ };
282
+ /**
283
+ * Git Clone from template repository.
284
+ */
285
+ export const clone = (type: ProjectOptionValue, directory: string): void => {
286
+ const execute = (command: string): void => {
287
+ console.log(`\n$ ${command}`);
288
+ cp.execSync(command, { stdio: "inherit" });
289
+ };
290
+
291
+ // COPY PROJECTS
292
+ execute(
293
+ `git clone git@github.com:wrtnlabs/agentica.template.${type} ${directory}`,
294
+ );
295
+ process.chdir(directory);
296
+
297
+ // REMOVE .GIT DIRECTORY
298
+ cp.execSync("npx rimraf .git");
299
+ cp.execSync("npx rimraf .github/dependabot.yml");
300
+ };
301
+ }
package/src/index.ts CHANGED
@@ -1,60 +1,39 @@
1
- #!/usr/bin/env node
2
- import cp from "child_process";
3
- import fs from "fs";
4
-
5
- const USAGE = `Wrong command has been detected. Use like below:
6
-
7
- npx agentica <type> <directory>
8
-
9
- 1. npx agentica start <directory>
10
- 2. npx agentica backend <directory>
11
- 3. npx agentica client <directory>
12
- `;
13
-
14
- const halt = (desc: string): never => {
15
- console.error(desc);
16
- process.exit(-1);
17
- };
18
-
19
- const clone = async (type: string, directory: string): Promise<void> => {
20
- const execute = (command: string): void => {
21
- console.log(`\n$ ${command}`);
22
- cp.execSync(command, { stdio: "inherit" });
23
- };
24
-
25
- // COPY PROJECTS
26
- execute(
27
- `git clone https://github.com/wrtnlabs/agentica-template-${type} ${directory}`,
28
- );
29
- console.log(`cd "${directory}"`);
30
- process.chdir(directory);
31
-
32
- // INSTALL DEPENDENCIES
33
- execute("npm install");
34
-
35
- // BUILD TYPESCRIPT
36
- execute("npm run build");
37
-
38
- // DO TEST
39
- execute("npm run test");
40
-
41
- // REMOVE .GIT DIRECTORY
42
- cp.execSync("npx rimraf .git");
43
- cp.execSync("npx rimraf .github/dependabot.yml");
44
- };
45
-
46
- const main = async (): Promise<void> => {
47
- const [_v0, _v1, type, directory] = process.argv;
48
- if (
49
- ["start", "backend", "client", "standalone"].includes(type) === false ||
50
- directory === undefined
51
- )
52
- halt(USAGE);
53
- else if (fs.existsSync(directory) === true)
54
- halt("The target directory already exists.");
55
- await clone(type, directory);
56
- };
57
- main().catch((exp) => {
58
- console.log(exp.message);
59
- process.exit(-1);
60
- });
1
+ #!/usr/bin/env node
2
+ import chalk from "chalk";
3
+ import { Command } from "commander";
4
+
5
+ import { AgenticaStart } from "./executable/AgenticaStart";
6
+ import { IAgenticaStart } from "./structures/IAgenticaStart";
7
+
8
+ async function main() {
9
+ const program = new Command();
10
+
11
+ program
12
+ .command("start <directory>")
13
+ .description("Start a new project")
14
+ .option(
15
+ "-p, --project [nodejs|nestjs|react|standalone]",
16
+ "The project type",
17
+ )
18
+ .action(async (directory: string, options: IAgenticaStart.IOptions) => {
19
+ if ((options.project as any) === true) {
20
+ console.error(
21
+ `\n The value of ${chalk.redBright("--project")} is required`,
22
+ );
23
+ return;
24
+ }
25
+
26
+ AgenticaStart.execute({ projectName: directory, options });
27
+ });
28
+
29
+ console.log("--------------------------------");
30
+ console.log(` 🚀 ${"Agentica"} ${chalk.blueBright("Setup Wizard")}`);
31
+ console.log("--------------------------------");
32
+
33
+ program.parse(process.argv);
34
+ }
35
+
36
+ main().catch((exp) => {
37
+ console.error(exp.message);
38
+ process.exit(-1);
39
+ });
@@ -0,0 +1,35 @@
1
+ import { ProjectOptionValue } from "../utils/types/ProjectOption";
2
+
3
+ /**
4
+ * Types about `npx agentica start` command.
5
+ *
6
+ * @author Michael
7
+ */
8
+ export namespace IAgenticaStart {
9
+ /**
10
+ * Options for `npx agentica start` command.
11
+ */
12
+ export type IOptions = Partial<{
13
+ project: ProjectOptionValue;
14
+ }>;
15
+
16
+ /**
17
+ * Parameters for execute `npx agentica start` command.
18
+ */
19
+ export interface IExecuteInput {
20
+ projectName: string;
21
+ options: IOptions;
22
+ }
23
+
24
+ /**
25
+ * Input for `getQuestions` function.
26
+ */
27
+ export interface IGetQuestionsInput {
28
+ services: {
29
+ name: string;
30
+ value: string;
31
+ }[];
32
+
33
+ options: IOptions;
34
+ }
35
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Types about options for `npx agentica start` command.
3
+ *
4
+ * @author Michael
5
+ */
6
+ export namespace IAgenticaStartOption {
7
+ export interface IProject {
8
+ projectName: string;
9
+ projectPath: string;
10
+ openAIKey: string;
11
+ services: string[];
12
+ }
13
+ }
@@ -0,0 +1,7 @@
1
+ // Convert first letter to uppercase (ex: aws-s3 -> AwsS3)
2
+ export const capitalize = (service: string): string => {
3
+ return service
4
+ .split("-")
5
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
6
+ .join("");
7
+ };
@@ -0,0 +1,14 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export const createProjectDirectory = (input: {
5
+ projectPath: string;
6
+ }): void => {
7
+ if (fs.existsSync(input.projectPath)) {
8
+ throw new Error(
9
+ `${path.basename(input.projectPath)} directory already exists.`,
10
+ );
11
+ }
12
+
13
+ fs.mkdirSync(input.projectPath);
14
+ };
@@ -0,0 +1,45 @@
1
+ import axios from "axios";
2
+ import typia, { tags } from "typia";
3
+
4
+ export interface INpmPackages {
5
+ objects: {
6
+ package: {
7
+ name: string;
8
+ };
9
+ }[];
10
+ total: number;
11
+ time: string & tags.Format<"date-time">;
12
+ }
13
+
14
+ export const getNpmPackages = async (): Promise<
15
+ { name: string; value: string }[]
16
+ > => {
17
+ try {
18
+ const response = await axios.get(
19
+ "https://registry.npmjs.org/-/v1/search?text=scope:@wrtnlabs&size=10000",
20
+ );
21
+ const data = typia.assert<INpmPackages>(response.data);
22
+
23
+ return data.objects
24
+ .map((pkg: any) => pkg.package.name)
25
+ .filter((name: string) => {
26
+ // shared is not connector package. This is connector util package.
27
+ if (name === "@wrtnlabs/connector-shared") {
28
+ return false;
29
+ }
30
+
31
+ const regex = /^@wrtnlabs\/connector-(?:[a-z0-9-]+)+$/;
32
+ return regex.test(name);
33
+ })
34
+ .map((name: string) => {
35
+ const serviceName = name.replace("@wrtnlabs/connector-", "");
36
+ return {
37
+ name: serviceName.replace("-", " ").toUpperCase(),
38
+ value: serviceName,
39
+ };
40
+ });
41
+ } catch (error) {
42
+ console.error("Error occurred while fetching package list:", error);
43
+ return [];
44
+ }
45
+ };
@@ -0,0 +1,48 @@
1
+ import chalk from "chalk";
2
+ import { QuestionCollection } from "inquirer";
3
+
4
+ export interface GetQuestionsInput {
5
+ services: {
6
+ name: string;
7
+ value: string;
8
+ }[];
9
+ }
10
+
11
+ export const getQuestions = (
12
+ input: GetQuestionsInput,
13
+ ): QuestionCollection[] => {
14
+ return [
15
+ {
16
+ type: "list",
17
+ name: "packageManager",
18
+ message: "Package Manager",
19
+ choices: [
20
+ "npm",
21
+ "pnpm",
22
+ `yarn (berry ${chalk.blueBright("is not supported")})`,
23
+ ],
24
+ },
25
+ {
26
+ type: "list",
27
+ name: "projectType",
28
+ message: "Project Type",
29
+ choices: [
30
+ `NodeJS ${chalk.blueBright("Agent Server")}`,
31
+ `NestJS ${chalk.blueBright("Agent Server")}`,
32
+ `React ${chalk.blueBright("Client Application")}`,
33
+ `Standard ${chalk.blueBright("Application")}`,
34
+ ],
35
+ },
36
+ {
37
+ type: "checkbox",
38
+ name: "services",
39
+ message: "Embedded Controllers",
40
+ choices: input.services,
41
+ },
42
+ {
43
+ type: "input",
44
+ name: "openAIKey",
45
+ message: "Please enter your OPEN_AI_API_KEY:",
46
+ },
47
+ ];
48
+ };
@@ -0,0 +1 @@
1
+ export type PackageManager = "npm" | "yarn" | "pnpm";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Value of the `--project` option.
3
+ */
4
+ export type ProjectOptionValue = "nodejs" | "nestjs" | "react" | "standalone";