agentica 0.1.0 → 0.12.5

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