@agentuity/cli 0.0.100 → 0.0.102
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/AGENTS.md +19 -188
- package/bin/cli.ts +13 -6
- package/dist/api.d.ts +1 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +1 -1
- package/dist/api.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +41 -12
- package/dist/cli.js.map +1 -1
- package/dist/cmd/ai/index.d.ts.map +1 -1
- package/dist/cmd/ai/index.js +6 -1
- package/dist/cmd/ai/index.js.map +1 -1
- package/dist/cmd/ai/prompt/agent.d.ts +7 -0
- package/dist/cmd/ai/prompt/agent.d.ts.map +1 -1
- package/dist/cmd/ai/prompt/agent.js +12 -323
- package/dist/cmd/ai/prompt/agent.js.map +1 -1
- package/dist/cmd/ai/prompt/api.d.ts +7 -0
- package/dist/cmd/ai/prompt/api.d.ts.map +1 -1
- package/dist/cmd/ai/prompt/api.js +12 -260
- package/dist/cmd/ai/prompt/api.js.map +1 -1
- package/dist/cmd/ai/prompt/version.d.ts +35 -0
- package/dist/cmd/ai/prompt/version.d.ts.map +1 -0
- package/dist/cmd/ai/prompt/version.js +55 -0
- package/dist/cmd/ai/prompt/version.js.map +1 -0
- package/dist/cmd/ai/prompt/web.d.ts +7 -0
- package/dist/cmd/ai/prompt/web.d.ts.map +1 -1
- package/dist/cmd/ai/prompt/web.js +12 -283
- package/dist/cmd/ai/prompt/web.js.map +1 -1
- package/dist/cmd/ai/skills/generate.d.ts +3 -0
- package/dist/cmd/ai/skills/generate.d.ts.map +1 -0
- package/dist/cmd/ai/skills/generate.js +65 -0
- package/dist/cmd/ai/skills/generate.js.map +1 -0
- package/dist/cmd/ai/skills/generator.d.ts +4 -0
- package/dist/cmd/ai/skills/generator.d.ts.map +1 -0
- package/dist/cmd/ai/skills/generator.js +402 -0
- package/dist/cmd/ai/skills/generator.js.map +1 -0
- package/dist/cmd/ai/skills/index.d.ts +4 -0
- package/dist/cmd/ai/skills/index.d.ts.map +1 -0
- package/dist/cmd/ai/skills/index.js +21 -0
- package/dist/cmd/ai/skills/index.js.map +1 -0
- package/dist/cmd/auth/signup.d.ts.map +1 -1
- package/dist/cmd/auth/signup.js +1 -0
- package/dist/cmd/auth/signup.js.map +1 -1
- package/dist/cmd/build/ast.d.ts +2 -1
- package/dist/cmd/build/ast.d.ts.map +1 -1
- package/dist/cmd/build/ast.js +135 -47
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/entry-generator.d.ts.map +1 -1
- package/dist/cmd/build/entry-generator.js +255 -188
- package/dist/cmd/build/entry-generator.js.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.js +103 -45
- package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.d.ts +7 -1
- package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.js +52 -26
- package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
- package/dist/cmd/build/vite/docs-generator.d.ts +13 -0
- package/dist/cmd/build/vite/docs-generator.d.ts.map +1 -0
- package/dist/cmd/build/vite/docs-generator.js +81 -0
- package/dist/cmd/build/vite/docs-generator.js.map +1 -0
- package/dist/cmd/build/vite/index.d.ts +3 -3
- package/dist/cmd/build/vite/index.d.ts.map +1 -1
- package/dist/cmd/build/vite/index.js +9 -7
- package/dist/cmd/build/vite/index.js.map +1 -1
- package/dist/cmd/build/vite/lifecycle-generator.d.ts +1 -1
- package/dist/cmd/build/vite/lifecycle-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/lifecycle-generator.js +19 -5
- package/dist/cmd/build/vite/lifecycle-generator.js.map +1 -1
- package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/metadata-generator.js +203 -7
- package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
- package/dist/cmd/build/vite/prompt-generator.d.ts +23 -0
- package/dist/cmd/build/vite/prompt-generator.d.ts.map +1 -0
- package/dist/cmd/build/vite/prompt-generator.js +123 -0
- package/dist/cmd/build/vite/prompt-generator.js.map +1 -0
- package/dist/cmd/build/vite/registry-generator.d.ts +3 -3
- package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/registry-generator.js +644 -103
- package/dist/cmd/build/vite/registry-generator.js.map +1 -1
- package/dist/cmd/build/vite/route-discovery.d.ts +4 -0
- package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/route-discovery.js.map +1 -1
- package/dist/cmd/build/vite/server-bundler.d.ts +4 -0
- package/dist/cmd/build/vite/server-bundler.d.ts.map +1 -1
- package/dist/cmd/build/vite/server-bundler.js +63 -17
- package/dist/cmd/build/vite/server-bundler.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.js +4 -0
- package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.d.ts +1 -1
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +118 -96
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/build/vite-bundler.js +6 -6
- package/dist/cmd/build/vite-bundler.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +89 -32
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/create-namespace.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/create-namespace.js +3 -1
- package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/delete-namespace.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/delete-namespace.js +3 -1
- package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/delete.js +3 -1
- package/dist/cmd/cloud/keyvalue/delete.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/set.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/set.js +4 -2
- package/dist/cmd/cloud/keyvalue/set.js.map +1 -1
- package/dist/cmd/cloud/stream/get.d.ts.map +1 -1
- package/dist/cmd/cloud/stream/get.js +2 -13
- package/dist/cmd/cloud/stream/get.js.map +1 -1
- package/dist/cmd/cloud/vector/delete-namespace.d.ts +3 -0
- package/dist/cmd/cloud/vector/delete-namespace.d.ts.map +1 -0
- package/dist/cmd/cloud/vector/delete-namespace.js +77 -0
- package/dist/cmd/cloud/vector/delete-namespace.js.map +1 -0
- package/dist/cmd/cloud/vector/index.d.ts.map +1 -1
- package/dist/cmd/cloud/vector/index.js +21 -4
- package/dist/cmd/cloud/vector/index.js.map +1 -1
- package/dist/cmd/cloud/vector/list-namespaces.d.ts +3 -0
- package/dist/cmd/cloud/vector/list-namespaces.d.ts.map +1 -0
- package/dist/cmd/cloud/vector/list-namespaces.js +42 -0
- package/dist/cmd/cloud/vector/list-namespaces.js.map +1 -0
- package/dist/cmd/cloud/vector/stats.d.ts +3 -0
- package/dist/cmd/cloud/vector/stats.d.ts.map +1 -0
- package/dist/cmd/cloud/vector/stats.js +142 -0
- package/dist/cmd/cloud/vector/stats.js.map +1 -0
- package/dist/cmd/cloud/vector/upsert.d.ts +3 -0
- package/dist/cmd/cloud/vector/upsert.d.ts.map +1 -0
- package/dist/cmd/cloud/vector/upsert.js +192 -0
- package/dist/cmd/cloud/vector/upsert.js.map +1 -0
- package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
- package/dist/cmd/dev/file-watcher.js +94 -33
- package/dist/cmd/dev/file-watcher.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +298 -61
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/dev/skills.d.ts +10 -0
- package/dist/cmd/dev/skills.d.ts.map +1 -0
- package/dist/cmd/dev/skills.js +57 -0
- package/dist/cmd/dev/skills.js.map +1 -0
- package/dist/cmd/dev/sync.d.ts.map +1 -1
- package/dist/cmd/dev/sync.js +19 -3
- package/dist/cmd/dev/sync.js.map +1 -1
- package/dist/cmd/index.d.ts.map +1 -1
- package/dist/cmd/index.js +1 -0
- package/dist/cmd/index.js.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/create.js +3 -0
- package/dist/cmd/project/create.js.map +1 -1
- package/dist/cmd/project/template-flow.d.ts +1 -0
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +30 -5
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/cmd/setup/index.d.ts.map +1 -1
- package/dist/cmd/setup/index.js +1 -0
- package/dist/cmd/setup/index.js.map +1 -1
- package/dist/cmd/upgrade/index.d.ts +15 -0
- package/dist/cmd/upgrade/index.d.ts.map +1 -1
- package/dist/cmd/upgrade/index.js +59 -4
- package/dist/cmd/upgrade/index.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/domain.d.ts +45 -0
- package/dist/domain.d.ts.map +1 -0
- package/dist/domain.js +200 -0
- package/dist/domain.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/schema-generator.d.ts +2 -0
- package/dist/schema-generator.d.ts.map +1 -1
- package/dist/schema-generator.js +18 -0
- package/dist/schema-generator.js.map +1 -1
- package/dist/steps.d.ts +1 -1
- package/dist/steps.d.ts.map +1 -1
- package/dist/steps.js +16 -5
- package/dist/steps.js.map +1 -1
- package/dist/tui/prompt.d.ts +1 -2
- package/dist/tui/prompt.d.ts.map +1 -1
- package/dist/tui/prompt.js +8 -4
- package/dist/tui/prompt.js.map +1 -1
- package/dist/tui.d.ts +16 -0
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +23 -2
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +9 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -3
- package/dist/types.js.map +1 -1
- package/package.json +5 -8
- package/src/api.ts +1 -1
- package/src/cli.ts +47 -12
- package/src/cmd/ai/index.ts +6 -1
- package/src/cmd/ai/prompt/agent.md +306 -0
- package/src/cmd/ai/prompt/agent.ts +12 -322
- package/src/cmd/ai/prompt/api.md +360 -0
- package/src/cmd/ai/prompt/api.ts +13 -260
- package/src/cmd/ai/prompt/version.ts +61 -0
- package/src/cmd/ai/prompt/web.md +509 -0
- package/src/cmd/ai/prompt/web.ts +12 -282
- package/src/cmd/ai/skills/generate.ts +75 -0
- package/src/cmd/ai/skills/generator.ts +519 -0
- package/src/cmd/ai/skills/index.ts +23 -0
- package/src/cmd/auth/signup.ts +1 -0
- package/src/cmd/build/ast.ts +161 -48
- package/src/cmd/build/entry-generator.ts +258 -187
- package/src/cmd/build/vite/agent-discovery.ts +151 -58
- package/src/cmd/build/vite/bun-dev-server.ts +57 -27
- package/src/cmd/build/vite/docs-generator.ts +87 -0
- package/src/cmd/build/vite/index.ts +9 -7
- package/src/cmd/build/vite/lifecycle-generator.ts +19 -5
- package/src/cmd/build/vite/metadata-generator.ts +251 -7
- package/src/cmd/build/vite/prompt-generator.ts +169 -0
- package/src/cmd/build/vite/registry-generator.ts +750 -108
- package/src/cmd/build/vite/route-discovery.ts +4 -0
- package/src/cmd/build/vite/server-bundler.ts +73 -23
- package/src/cmd/build/vite/vite-asset-server-config.ts +5 -0
- package/src/cmd/build/vite/vite-builder.ts +134 -100
- package/src/cmd/build/vite-bundler.ts +6 -6
- package/src/cmd/cloud/deploy.ts +114 -36
- package/src/cmd/cloud/keyvalue/create-namespace.ts +3 -1
- package/src/cmd/cloud/keyvalue/delete-namespace.ts +3 -1
- package/src/cmd/cloud/keyvalue/delete.ts +3 -1
- package/src/cmd/cloud/keyvalue/set.ts +4 -2
- package/src/cmd/cloud/stream/get.ts +2 -9
- package/src/cmd/cloud/vector/delete-namespace.ts +89 -0
- package/src/cmd/cloud/vector/index.ts +21 -4
- package/src/cmd/cloud/vector/list-namespaces.ts +46 -0
- package/src/cmd/cloud/vector/stats.ts +160 -0
- package/src/cmd/cloud/vector/upsert.ts +216 -0
- package/src/cmd/dev/file-watcher.ts +109 -34
- package/src/cmd/dev/index.ts +364 -60
- package/src/cmd/dev/skills.ts +82 -0
- package/src/cmd/dev/sync.ts +41 -6
- package/src/cmd/index.ts +1 -0
- package/src/cmd/project/create.ts +3 -0
- package/src/cmd/project/template-flow.ts +37 -5
- package/src/cmd/setup/index.ts +1 -0
- package/src/cmd/upgrade/index.ts +68 -4
- package/src/config.ts +9 -0
- package/src/domain.ts +273 -0
- package/src/index.ts +0 -5
- package/src/runtime-bootstrap.md +1 -1
- package/src/schema-generator.ts +23 -0
- package/src/steps.ts +16 -5
- package/src/tui/prompt.ts +11 -5
- package/src/tui.ts +21 -2
- package/src/types/md.d.ts +8 -0
- package/src/types.ts +12 -3
- package/dist/cmd/cloud/domain.d.ts +0 -17
- package/dist/cmd/cloud/domain.d.ts.map +0 -1
- package/dist/cmd/cloud/domain.js +0 -79
- package/dist/cmd/cloud/domain.js.map +0 -1
- package/dist/runtime-bootstrap.d.ts +0 -56
- package/dist/runtime-bootstrap.d.ts.map +0 -1
- package/dist/runtime-bootstrap.js +0 -95
- package/dist/runtime-bootstrap.js.map +0 -1
- package/src/cmd/cloud/domain.ts +0 -100
- package/src/runtime-bootstrap.ts +0 -131
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Registry Generator
|
|
3
3
|
*
|
|
4
|
-
* Generates
|
|
4
|
+
* Generates src/generated/registry.ts from discovered agents
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { join } from 'node:path';
|
|
8
|
-
import { writeFileSync, mkdirSync, existsSync, unlinkSync } from 'node:fs';
|
|
8
|
+
import { writeFileSync, mkdirSync, existsSync, unlinkSync, readFileSync } from 'node:fs';
|
|
9
9
|
import { StructuredError } from '@agentuity/core';
|
|
10
10
|
import { toCamelCase, toPascalCase } from '../../../utils/string';
|
|
11
11
|
import type { AgentMetadata } from './agent-discovery';
|
|
@@ -14,12 +14,27 @@ import type { RouteInfo } from './route-discovery';
|
|
|
14
14
|
const AgentIdentifierCollisionError = StructuredError('AgentIdentifierCollisionError');
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* Regex to strip route parameter characters that produce invalid TypeScript property names:
|
|
18
|
+
* - Leading : (path parameters, e.g., :id)
|
|
19
|
+
* - Leading * (wildcard routes, e.g., *path)
|
|
20
|
+
* - Trailing ?, +, * (optional/one-or-more/wildcard modifiers, e.g., :userId?)
|
|
21
|
+
*/
|
|
22
|
+
const ROUTE_PARAM_CHARS = /^[:*]|[?+*]$/g;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sanitize a route path segment for use as a TypeScript property name.
|
|
26
|
+
* Strips route parameter characters and converts to camelCase.
|
|
27
|
+
*/
|
|
28
|
+
function sanitizePathSegment(segment: string): string {
|
|
29
|
+
return toCamelCase(segment.replace(ROUTE_PARAM_CHARS, ''));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generate src/generated/registry.ts with agent registry and types
|
|
18
34
|
*/
|
|
19
35
|
export function generateAgentRegistry(srcDir: string, agents: AgentMetadata[]): void {
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const registryPath = join(agentuityDir, 'registry.generated.ts');
|
|
36
|
+
const generatedDir = join(srcDir, 'generated');
|
|
37
|
+
const registryPath = join(generatedDir, 'registry.ts');
|
|
23
38
|
|
|
24
39
|
// Detect naming collisions in generated identifiers
|
|
25
40
|
const generatedNames = new Set<string>();
|
|
@@ -50,60 +65,131 @@ export function generateAgentRegistry(srcDir: string, agents: AgentMetadata[]):
|
|
|
50
65
|
// Handle both './agent/...' and 'src/agent/...' formats
|
|
51
66
|
let relativePath = filename;
|
|
52
67
|
if (relativePath.startsWith('./agent/')) {
|
|
53
|
-
|
|
68
|
+
// ./agent/foo.ts -> ../agent/foo.js (use .js extension for TypeScript)
|
|
69
|
+
relativePath = relativePath
|
|
70
|
+
.replace(/^\.\/agent\//, '../agent/')
|
|
71
|
+
.replace(/\.tsx?$/, '.js');
|
|
54
72
|
} else if (relativePath.startsWith('src/agent/')) {
|
|
55
|
-
|
|
73
|
+
// src/agent/foo.ts -> ../agent/foo.js (use .js extension for TypeScript)
|
|
74
|
+
relativePath = relativePath
|
|
75
|
+
.replace(/^src\/agent\//, '../agent/')
|
|
76
|
+
.replace(/\.tsx?$/, '.js');
|
|
56
77
|
}
|
|
57
78
|
return `import ${camelName} from '${relativePath}';`;
|
|
58
79
|
})
|
|
59
80
|
.join('\n');
|
|
60
81
|
|
|
61
|
-
// Generate
|
|
62
|
-
const
|
|
63
|
-
.map(({ name }) => {
|
|
82
|
+
// Generate schema type exports for all agents
|
|
83
|
+
const schemaTypeExports = agents
|
|
84
|
+
.map(({ name, description }) => {
|
|
64
85
|
const camelName = toCamelCase(name);
|
|
65
|
-
|
|
86
|
+
const pascalName = toPascalCase(name);
|
|
87
|
+
const descComment = description ? `\n * ${description}` : '';
|
|
88
|
+
|
|
89
|
+
const parts = [
|
|
90
|
+
'',
|
|
91
|
+
`/**`,
|
|
92
|
+
` * Input type for ${name} agent${descComment}`,
|
|
93
|
+
` */`,
|
|
94
|
+
`export type ${pascalName}Input = InferInput<typeof ${camelName}['inputSchema']>;`,
|
|
95
|
+
'',
|
|
96
|
+
`/**`,
|
|
97
|
+
` * Output type for ${name} agent${descComment}`,
|
|
98
|
+
` */`,
|
|
99
|
+
`export type ${pascalName}Output = InferOutput<typeof ${camelName}['outputSchema']>;`,
|
|
100
|
+
'',
|
|
101
|
+
`/**`,
|
|
102
|
+
` * Input schema type for ${name} agent${descComment}`,
|
|
103
|
+
` */`,
|
|
104
|
+
`export type ${pascalName}InputSchema = typeof ${camelName}['inputSchema'];`,
|
|
105
|
+
'',
|
|
106
|
+
`/**`,
|
|
107
|
+
` * Output schema type for ${name} agent${descComment}`,
|
|
108
|
+
` */`,
|
|
109
|
+
`export type ${pascalName}OutputSchema = typeof ${camelName}['outputSchema'];`,
|
|
110
|
+
'',
|
|
111
|
+
`/**`,
|
|
112
|
+
` * Agent type for ${name}${descComment}`,
|
|
113
|
+
` */`,
|
|
114
|
+
`export type ${pascalName}Agent = AgentRunner<`,
|
|
115
|
+
`\t${pascalName}InputSchema,`,
|
|
116
|
+
`\t${pascalName}OutputSchema,`,
|
|
117
|
+
`\ttypeof ${camelName}['stream'] extends true ? true : false`,
|
|
118
|
+
`>;`,
|
|
119
|
+
];
|
|
120
|
+
return parts.join('\n');
|
|
66
121
|
})
|
|
67
122
|
.join('\n');
|
|
68
123
|
|
|
69
|
-
// Generate
|
|
70
|
-
const
|
|
71
|
-
.map(({ name }) => {
|
|
124
|
+
// Generate flat registry structure with JSDoc
|
|
125
|
+
const registry = agents
|
|
126
|
+
.map(({ name, description }) => {
|
|
72
127
|
const camelName = toCamelCase(name);
|
|
73
128
|
const pascalName = toPascalCase(name);
|
|
74
|
-
|
|
129
|
+
const descComment = description ? `\n\t * ${description}` : '';
|
|
130
|
+
|
|
131
|
+
return `\t/**
|
|
132
|
+
\t * ${name}${descComment}
|
|
133
|
+
\t * @type {${pascalName}Agent}
|
|
134
|
+
\t */
|
|
135
|
+
\t${camelName},`;
|
|
75
136
|
})
|
|
76
137
|
.join('\n');
|
|
77
138
|
|
|
78
139
|
// Generate flat agent type definitions for AgentRegistry interface augmentation
|
|
140
|
+
// Uses the exported Agent types defined above
|
|
79
141
|
const runtimeAgentTypes = agents
|
|
80
142
|
.map(({ name }) => {
|
|
81
143
|
const camelName = toCamelCase(name);
|
|
82
|
-
|
|
144
|
+
const pascalName = toPascalCase(name);
|
|
145
|
+
return ` ${camelName}: ${pascalName}Agent;`;
|
|
83
146
|
})
|
|
84
147
|
.join('\n');
|
|
85
148
|
|
|
86
|
-
const generatedContent =
|
|
87
|
-
// Auto-generated by Agentuity -
|
|
149
|
+
const generatedContent = `// @generated
|
|
150
|
+
// Auto-generated by Agentuity - DO NOT EDIT
|
|
88
151
|
${imports}
|
|
89
|
-
import type { AgentRunner
|
|
90
|
-
import type {
|
|
152
|
+
import type { AgentRunner } from '@agentuity/runtime';
|
|
153
|
+
import type { InferInput, InferOutput } from '@agentuity/core';
|
|
154
|
+
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Schema Type Exports
|
|
157
|
+
// ============================================================================
|
|
158
|
+
${schemaTypeExports}
|
|
159
|
+
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// Agent Definitions
|
|
162
|
+
// ============================================================================
|
|
91
163
|
|
|
92
164
|
/**
|
|
165
|
+
* Agent Definitions
|
|
166
|
+
*
|
|
93
167
|
* Registry of all agents in this application.
|
|
94
168
|
* Provides strongly-typed access to agent metadata and runner functions.
|
|
95
|
-
*
|
|
169
|
+
*
|
|
170
|
+
* @remarks
|
|
171
|
+
* This object is auto-generated from your agent files during build.
|
|
172
|
+
* Each agent has corresponding Input, Output, and Runner types exported above.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* \`\`\`typescript
|
|
176
|
+
* import { AgentDefinitions, SessionBasicInput } from './generated/registry';
|
|
177
|
+
*
|
|
178
|
+
* // Access agent definition
|
|
179
|
+
* const agent = AgentDefinitions.sessionBasic;
|
|
180
|
+
*
|
|
181
|
+
* // Use typed schema types
|
|
182
|
+
* const input: SessionBasicInput = { ... };
|
|
183
|
+
* const result = await agent.run(input);
|
|
184
|
+
* \`\`\`
|
|
96
185
|
*/
|
|
97
|
-
export const
|
|
186
|
+
export const AgentDefinitions = {
|
|
98
187
|
${registry}
|
|
99
188
|
} as const;
|
|
100
189
|
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// Typed runners for each agent
|
|
106
|
-
${typeExports}
|
|
190
|
+
// ============================================================================
|
|
191
|
+
// Module Augmentation
|
|
192
|
+
// ============================================================================
|
|
107
193
|
|
|
108
194
|
// Augment @agentuity/runtime types with strongly-typed agents from this project
|
|
109
195
|
declare module "@agentuity/runtime" {
|
|
@@ -113,38 +199,289 @@ ${runtimeAgentTypes}
|
|
|
113
199
|
}
|
|
114
200
|
}
|
|
115
201
|
|
|
116
|
-
//
|
|
117
|
-
//
|
|
202
|
+
// FOUND AN ERROR IN THIS FILE?
|
|
203
|
+
// Please file an issue at https://github.com/agentuity/sdk/issues
|
|
204
|
+
// or if you know the fix please submit a PR!
|
|
118
205
|
`;
|
|
119
206
|
|
|
120
207
|
const agentsDir = join(srcDir, 'agent');
|
|
121
208
|
const legacyTypesPath = join(agentsDir, 'types.generated.d.ts');
|
|
122
209
|
|
|
123
|
-
// Ensure
|
|
124
|
-
if (!existsSync(
|
|
125
|
-
mkdirSync(
|
|
210
|
+
// Ensure src/generated directory exists
|
|
211
|
+
if (!existsSync(generatedDir)) {
|
|
212
|
+
mkdirSync(generatedDir, { recursive: true });
|
|
126
213
|
}
|
|
127
214
|
|
|
128
|
-
|
|
215
|
+
// Collapse 2+ consecutive empty lines into 1 empty line (3+ \n becomes 2 \n)
|
|
216
|
+
const cleanedContent = generatedContent.replace(/\n{3,}/g, '\n\n');
|
|
129
217
|
|
|
130
|
-
|
|
218
|
+
writeFileSync(registryPath, cleanedContent, 'utf-8');
|
|
219
|
+
|
|
220
|
+
// Remove legacy types.generated.d.ts if it exists (legacy cleanup)
|
|
131
221
|
if (existsSync(legacyTypesPath)) {
|
|
132
222
|
unlinkSync(legacyTypesPath);
|
|
133
223
|
}
|
|
134
224
|
}
|
|
135
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Helper function to generate RPC-style nested registry type.
|
|
228
|
+
* Converts routes like "POST /api/hello" to nested structure: post.api.hello
|
|
229
|
+
*/
|
|
230
|
+
function generateRPCRegistryType(
|
|
231
|
+
apiRoutes: RouteInfo[],
|
|
232
|
+
websocketRoutes: RouteInfo[],
|
|
233
|
+
sseRoutes: RouteInfo[],
|
|
234
|
+
_agentImports: Map<string, string>,
|
|
235
|
+
_schemaImportAliases: Map<string, Map<string, string>>,
|
|
236
|
+
agentMetadataMap: Map<string, AgentMetadata>
|
|
237
|
+
): string {
|
|
238
|
+
// Build nested structure from routes
|
|
239
|
+
interface NestedNode {
|
|
240
|
+
[key: string]: NestedNode | { input: string; output: string; type: string; route: RouteInfo };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const tree: NestedNode = {};
|
|
244
|
+
|
|
245
|
+
// Helper to add route to tree
|
|
246
|
+
const addRoute = (route: RouteInfo, routeType: 'api' | 'websocket' | 'sse' | 'stream') => {
|
|
247
|
+
const method = route.method.toLowerCase();
|
|
248
|
+
|
|
249
|
+
// Strip /api prefix from path
|
|
250
|
+
let cleanPath = route.path;
|
|
251
|
+
if (cleanPath.startsWith('/api/')) {
|
|
252
|
+
cleanPath = cleanPath.substring(4); // Remove '/api'
|
|
253
|
+
} else if (cleanPath === '/api') {
|
|
254
|
+
cleanPath = '/';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const pathParts = cleanPath.split('/').filter(Boolean);
|
|
258
|
+
|
|
259
|
+
// Navigate/create tree structure: path segments first, then method
|
|
260
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
261
|
+
let current: any = tree;
|
|
262
|
+
|
|
263
|
+
// Add path segments - sanitize for valid TypeScript property names
|
|
264
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
265
|
+
const part = sanitizePathSegment(pathParts[i]);
|
|
266
|
+
if (!current[part]) {
|
|
267
|
+
current[part] = {};
|
|
268
|
+
}
|
|
269
|
+
current = current[part];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Determine terminal method name based on route type
|
|
273
|
+
// For stream types (websocket, sse, stream), use the type name as the method
|
|
274
|
+
// For regular API routes, use the HTTP method
|
|
275
|
+
const terminalMethod =
|
|
276
|
+
routeType === 'websocket'
|
|
277
|
+
? 'websocket'
|
|
278
|
+
: routeType === 'sse'
|
|
279
|
+
? 'eventstream'
|
|
280
|
+
: routeType === 'stream'
|
|
281
|
+
? 'stream'
|
|
282
|
+
: method;
|
|
283
|
+
|
|
284
|
+
// Add method as final level with schema types
|
|
285
|
+
const routeKey = `${route.method.toUpperCase()} ${route.path}`;
|
|
286
|
+
const safeName = routeKey
|
|
287
|
+
.replace(/[^a-zA-Z0-9]/g, '_')
|
|
288
|
+
.replace(/^_+|_+$/g, '')
|
|
289
|
+
.replace(/_+/g, '_');
|
|
290
|
+
const pascalName = toPascalCase(safeName);
|
|
291
|
+
|
|
292
|
+
// Only reference type names if route has schemas, otherwise use 'never'
|
|
293
|
+
const hasSchemas =
|
|
294
|
+
route.hasValidator ||
|
|
295
|
+
route.inputSchemaVariable ||
|
|
296
|
+
route.outputSchemaVariable ||
|
|
297
|
+
route.agentVariable;
|
|
298
|
+
|
|
299
|
+
current[terminalMethod] = {
|
|
300
|
+
input: hasSchemas ? `${pascalName}Input` : 'never',
|
|
301
|
+
output: hasSchemas ? `${pascalName}Output` : 'never',
|
|
302
|
+
type: `'${routeType}'`,
|
|
303
|
+
route,
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
// Add all routes with their types
|
|
308
|
+
apiRoutes.forEach((route) => {
|
|
309
|
+
const routeType = route.routeType === 'stream' ? 'stream' : 'api';
|
|
310
|
+
addRoute(route, routeType);
|
|
311
|
+
});
|
|
312
|
+
websocketRoutes.forEach((route) => addRoute(route, 'websocket'));
|
|
313
|
+
sseRoutes.forEach((route) => addRoute(route, 'sse'));
|
|
314
|
+
|
|
315
|
+
// Convert tree to TypeScript type string
|
|
316
|
+
function treeToTypeString(node: NestedNode, indent: string = '\t\t'): string {
|
|
317
|
+
const lines: string[] = [];
|
|
318
|
+
|
|
319
|
+
for (const [key, value] of Object.entries(node)) {
|
|
320
|
+
if (
|
|
321
|
+
value &&
|
|
322
|
+
typeof value === 'object' &&
|
|
323
|
+
'input' in value &&
|
|
324
|
+
'output' in value &&
|
|
325
|
+
'type' in value &&
|
|
326
|
+
'route' in value
|
|
327
|
+
) {
|
|
328
|
+
// Leaf node with schema and type - add JSDoc
|
|
329
|
+
const route = value.route;
|
|
330
|
+
const jsdoc: string[] = [];
|
|
331
|
+
|
|
332
|
+
// Access route info from value
|
|
333
|
+
const routeInfo = route as RouteInfo;
|
|
334
|
+
|
|
335
|
+
// Look up agent metadata
|
|
336
|
+
let agentMeta: AgentMetadata | undefined;
|
|
337
|
+
if (routeInfo.agentVariable) {
|
|
338
|
+
agentMeta = agentMetadataMap.get(routeInfo.agentVariable);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Build JSDoc comment
|
|
342
|
+
jsdoc.push(`${indent}/**`);
|
|
343
|
+
jsdoc.push(`${indent} * Route: ${routeInfo.method.toUpperCase()} ${routeInfo.path}`);
|
|
344
|
+
if (agentMeta?.name) {
|
|
345
|
+
jsdoc.push(`${indent} * @agent ${agentMeta.name}`);
|
|
346
|
+
}
|
|
347
|
+
if (agentMeta?.description) {
|
|
348
|
+
jsdoc.push(`${indent} * @description ${agentMeta.description}`);
|
|
349
|
+
}
|
|
350
|
+
jsdoc.push(`${indent} */`);
|
|
351
|
+
lines.push(...jsdoc);
|
|
352
|
+
|
|
353
|
+
lines.push(
|
|
354
|
+
`${indent}${key}: { input: ${value.input}; output: ${value.output}; type: ${value.type} };`
|
|
355
|
+
);
|
|
356
|
+
} else {
|
|
357
|
+
// Nested node
|
|
358
|
+
lines.push(`${indent}${key}: {`);
|
|
359
|
+
lines.push(treeToTypeString(value as NestedNode, indent + '\t'));
|
|
360
|
+
lines.push(`${indent}};`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return lines.join('\n');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (Object.keys(tree).length === 0) {
|
|
368
|
+
return '\t\t// No routes discovered';
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return treeToTypeString(tree);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Generate runtime metadata object for RPC routes.
|
|
376
|
+
* This allows the client to know route types at runtime.
|
|
377
|
+
*/
|
|
378
|
+
function generateRPCRuntimeMetadata(
|
|
379
|
+
apiRoutes: RouteInfo[],
|
|
380
|
+
websocketRoutes: RouteInfo[],
|
|
381
|
+
sseRoutes: RouteInfo[]
|
|
382
|
+
): string {
|
|
383
|
+
interface MetadataNode {
|
|
384
|
+
[key: string]: MetadataNode | { type: string };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const tree: MetadataNode = {};
|
|
388
|
+
|
|
389
|
+
const addRoute = (route: RouteInfo, routeType: string) => {
|
|
390
|
+
let cleanPath = route.path;
|
|
391
|
+
if (cleanPath.startsWith('/api/')) {
|
|
392
|
+
cleanPath = cleanPath.substring(4);
|
|
393
|
+
} else if (cleanPath === '/api') {
|
|
394
|
+
cleanPath = '/';
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const pathParts = cleanPath.split('/').filter(Boolean);
|
|
398
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
399
|
+
let current: any = tree;
|
|
400
|
+
|
|
401
|
+
// Sanitize path segments for valid property names (must match type generation)
|
|
402
|
+
for (const part of pathParts) {
|
|
403
|
+
const sanitized = sanitizePathSegment(part);
|
|
404
|
+
if (!current[sanitized]) current[sanitized] = {};
|
|
405
|
+
current = current[sanitized];
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Use terminal method name based on route type
|
|
409
|
+
const terminalMethod =
|
|
410
|
+
routeType === 'websocket'
|
|
411
|
+
? 'websocket'
|
|
412
|
+
: routeType === 'sse'
|
|
413
|
+
? 'eventstream'
|
|
414
|
+
: routeType === 'stream'
|
|
415
|
+
? 'stream'
|
|
416
|
+
: route.method.toLowerCase();
|
|
417
|
+
|
|
418
|
+
current[terminalMethod] = { type: routeType };
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
apiRoutes.forEach((r) => addRoute(r, r.routeType === 'stream' ? 'stream' : 'api'));
|
|
422
|
+
websocketRoutes.forEach((r) => addRoute(r, 'websocket'));
|
|
423
|
+
sseRoutes.forEach((r) => addRoute(r, 'sse'));
|
|
424
|
+
|
|
425
|
+
return JSON.stringify(tree, null, '\t\t');
|
|
426
|
+
}
|
|
427
|
+
|
|
136
428
|
/**
|
|
137
429
|
* Generate RouteRegistry type definitions from discovered routes.
|
|
138
430
|
*
|
|
139
431
|
* Creates a module augmentation for @agentuity/react that provides
|
|
140
432
|
* strongly-typed route keys with input/output schema information.
|
|
141
433
|
*/
|
|
142
|
-
export function generateRouteRegistry(
|
|
434
|
+
export function generateRouteRegistry(
|
|
435
|
+
srcDir: string,
|
|
436
|
+
routes: RouteInfo[],
|
|
437
|
+
agents: AgentMetadata[] = []
|
|
438
|
+
): void {
|
|
439
|
+
// Check if project uses @agentuity/react
|
|
440
|
+
const projectRoot = join(srcDir, '..');
|
|
441
|
+
const packageJsonPath = join(projectRoot, 'package.json');
|
|
442
|
+
let hasReactDependency = false;
|
|
443
|
+
|
|
444
|
+
try {
|
|
445
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
446
|
+
hasReactDependency = !!(
|
|
447
|
+
packageJson.dependencies?.['@agentuity/react'] ||
|
|
448
|
+
packageJson.devDependencies?.['@agentuity/react']
|
|
449
|
+
);
|
|
450
|
+
} catch {
|
|
451
|
+
// If we can't read package.json, assume no React dependency
|
|
452
|
+
}
|
|
453
|
+
|
|
143
454
|
// Filter routes by type
|
|
144
455
|
const apiRoutes = routes.filter((r) => r.routeType === 'api' || r.routeType === 'stream');
|
|
145
456
|
const websocketRoutes = routes.filter((r) => r.routeType === 'websocket');
|
|
146
457
|
const sseRoutes = routes.filter((r) => r.routeType === 'sse');
|
|
147
458
|
|
|
459
|
+
const allRoutes = [...apiRoutes, ...websocketRoutes, ...sseRoutes];
|
|
460
|
+
|
|
461
|
+
// Create maps for agent metadata lookup
|
|
462
|
+
const agentMetadataMap = new Map<string, AgentMetadata>();
|
|
463
|
+
const agentNameMap = new Map<string, AgentMetadata>();
|
|
464
|
+
|
|
465
|
+
// Map by agent name for easy lookup
|
|
466
|
+
agents.forEach((agent) => {
|
|
467
|
+
agentNameMap.set(agent.name, agent);
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// Map agent import variables to metadata by extracting agent name from import path
|
|
471
|
+
allRoutes.forEach((route) => {
|
|
472
|
+
if (route.agentVariable && route.agentImportPath) {
|
|
473
|
+
// Extract agent name from import path (e.g., "@agent/hello" -> "hello")
|
|
474
|
+
const match = route.agentImportPath.match(/@agent[s]?\/([^/]+)/);
|
|
475
|
+
if (match) {
|
|
476
|
+
const agentName = match[1];
|
|
477
|
+
const metadata = agentNameMap.get(agentName);
|
|
478
|
+
if (metadata) {
|
|
479
|
+
agentMetadataMap.set(route.agentVariable, metadata);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
148
485
|
if (apiRoutes.length === 0 && websocketRoutes.length === 0 && sseRoutes.length === 0) {
|
|
149
486
|
return;
|
|
150
487
|
}
|
|
@@ -154,25 +491,73 @@ export function generateRouteRegistry(srcDir: string, routes: RouteInfo[]): void
|
|
|
154
491
|
const agentImports = new Map<string, string>();
|
|
155
492
|
const routeFileImports = new Map<string, Set<string>>();
|
|
156
493
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// Collect agent imports
|
|
494
|
+
// Collect agent and schema imports from routes with validators or exported schemas
|
|
160
495
|
allRoutes.forEach((route) => {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
496
|
+
const hasSchemaVars = !!route.inputSchemaVariable || !!route.outputSchemaVariable;
|
|
497
|
+
if (!route.hasValidator && !hasSchemaVars && !route.agentVariable) return;
|
|
498
|
+
|
|
499
|
+
// Collect agent imports (when using agent.validator())
|
|
500
|
+
if (
|
|
501
|
+
route.hasValidator &&
|
|
502
|
+
route.agentVariable &&
|
|
503
|
+
route.agentImportPath &&
|
|
504
|
+
!agentImports.has(route.agentVariable)
|
|
505
|
+
) {
|
|
164
506
|
let resolvedPath = route.agentImportPath;
|
|
165
507
|
|
|
166
|
-
if (resolvedPath.startsWith('@agent/')) {
|
|
167
|
-
|
|
508
|
+
if (resolvedPath.startsWith('@agents/') || resolvedPath.startsWith('@agent/')) {
|
|
509
|
+
// Handle both @agents/ and @agent/ aliases -> ../agent/
|
|
510
|
+
const suffix = resolvedPath.startsWith('@agents/')
|
|
511
|
+
? resolvedPath.substring('@agents/'.length)
|
|
512
|
+
: resolvedPath.substring('@agent/'.length);
|
|
513
|
+
|
|
514
|
+
// Convert @agent/hello -> ../agent/hello/index.js
|
|
515
|
+
// Convert @agent/hello/agent -> ../agent/hello/agent.js
|
|
516
|
+
if (!suffix.includes('/')) {
|
|
517
|
+
// Bare module (e.g., @agent/hello) - add /index.js
|
|
518
|
+
resolvedPath = `../agent/${suffix}/index.js`;
|
|
519
|
+
} else {
|
|
520
|
+
// File path (e.g., @agent/hello/agent) - add .js
|
|
521
|
+
const finalPath = suffix.endsWith('.js')
|
|
522
|
+
? suffix
|
|
523
|
+
: suffix.replace(/\.tsx?$/, '') + '.js';
|
|
524
|
+
resolvedPath = `../agent/${finalPath}`;
|
|
525
|
+
}
|
|
168
526
|
} else if (resolvedPath.startsWith('@api/')) {
|
|
169
|
-
|
|
527
|
+
// src/generated/ -> src/api/ is ../api/
|
|
528
|
+
const suffix = resolvedPath.substring('@api/'.length);
|
|
529
|
+
const finalPath = suffix.endsWith('.js')
|
|
530
|
+
? suffix
|
|
531
|
+
: suffix.replace(/\.tsx?$/, '') + '.js';
|
|
532
|
+
resolvedPath = `../api/${finalPath}`;
|
|
170
533
|
} else if (resolvedPath.startsWith('./') || resolvedPath.startsWith('../')) {
|
|
534
|
+
// Resolve relative import from route file's directory
|
|
171
535
|
const routeDir = route.filename.substring(0, route.filename.lastIndexOf('/'));
|
|
172
|
-
|
|
536
|
+
// Join and normalize the path
|
|
537
|
+
const joined = `${routeDir}/${resolvedPath}`;
|
|
538
|
+
// Normalize by resolving .. and . segments
|
|
539
|
+
const normalized = joined
|
|
540
|
+
.split('/')
|
|
541
|
+
.reduce((acc: string[], segment) => {
|
|
542
|
+
if (segment === '..') {
|
|
543
|
+
acc.pop();
|
|
544
|
+
} else if (segment !== '.' && segment !== '') {
|
|
545
|
+
acc.push(segment);
|
|
546
|
+
}
|
|
547
|
+
return acc;
|
|
548
|
+
}, [])
|
|
549
|
+
.join('/');
|
|
550
|
+
// Remove 'src/' prefix if present (routes are in src/, generated is in src/generated/)
|
|
551
|
+
const withoutSrc = normalized.startsWith('src/') ? normalized.substring(4) : normalized;
|
|
552
|
+
// Make it relative from src/generated/
|
|
553
|
+
resolvedPath = `../${withoutSrc}`;
|
|
554
|
+
// Add .js extension if not already present
|
|
555
|
+
if (!resolvedPath.endsWith('.js')) {
|
|
556
|
+
resolvedPath = resolvedPath.replace(/\.tsx?$/, '') + '.js';
|
|
557
|
+
}
|
|
173
558
|
}
|
|
174
559
|
|
|
175
|
-
const uniqueImportName =
|
|
560
|
+
const uniqueImportName = route.agentVariable;
|
|
176
561
|
imports.push(`import type ${uniqueImportName} from '${resolvedPath}';`);
|
|
177
562
|
agentImports.set(route.agentVariable, uniqueImportName);
|
|
178
563
|
}
|
|
@@ -180,7 +565,12 @@ export function generateRouteRegistry(srcDir: string, routes: RouteInfo[]): void
|
|
|
180
565
|
// Collect schema variable imports
|
|
181
566
|
if (route.inputSchemaVariable || route.outputSchemaVariable) {
|
|
182
567
|
const filename = route.filename.replace(/\\/g, '/');
|
|
183
|
-
|
|
568
|
+
// Remove 'src/' prefix if present (routes.filename might be './api/...' or 'src/api/...')
|
|
569
|
+
const withoutSrc = filename.startsWith('src/') ? filename.substring(4) : filename;
|
|
570
|
+
const withoutLeadingDot = withoutSrc.startsWith('./')
|
|
571
|
+
? withoutSrc.substring(2)
|
|
572
|
+
: withoutSrc;
|
|
573
|
+
const importPath = `../${withoutLeadingDot.replace(/\.ts$/, '')}`;
|
|
184
574
|
|
|
185
575
|
if (!routeFileImports.has(importPath)) {
|
|
186
576
|
routeFileImports.set(importPath, new Set());
|
|
@@ -195,94 +585,346 @@ export function generateRouteRegistry(srcDir: string, routes: RouteInfo[]): void
|
|
|
195
585
|
}
|
|
196
586
|
});
|
|
197
587
|
|
|
198
|
-
// Generate schema imports
|
|
588
|
+
// Generate schema imports with unique aliases to avoid conflicts
|
|
589
|
+
const schemaImportAliases = new Map<string, Map<string, string>>(); // importPath -> (schemaName -> alias)
|
|
590
|
+
let aliasCounter = 0;
|
|
591
|
+
|
|
199
592
|
routeFileImports.forEach((schemas, importPath) => {
|
|
200
|
-
const
|
|
201
|
-
|
|
593
|
+
const aliases = new Map<string, string>();
|
|
594
|
+
const importParts: string[] = [];
|
|
595
|
+
|
|
596
|
+
for (const schemaName of Array.from(schemas)) {
|
|
597
|
+
// Create a unique alias for this schema to avoid collisions
|
|
598
|
+
const alias = `${schemaName}_${aliasCounter++}`;
|
|
599
|
+
aliases.set(schemaName, alias);
|
|
600
|
+
importParts.push(`${schemaName} as ${alias}`);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
schemaImportAliases.set(importPath, aliases);
|
|
604
|
+
imports.push(`import type { ${importParts.join(', ')} } from '${importPath}';`);
|
|
202
605
|
});
|
|
203
606
|
|
|
204
|
-
const importsStr = imports.join('\n');
|
|
607
|
+
const importsStr = imports.length > 0 ? imports.join('\n') + '\n' : '';
|
|
608
|
+
|
|
609
|
+
// Add InferInput/InferOutput imports if we have any routes with schemas
|
|
610
|
+
const hasSchemas = allRoutes.some(
|
|
611
|
+
(r) => r.hasValidator || r.inputSchemaVariable || r.outputSchemaVariable || r.agentVariable
|
|
612
|
+
);
|
|
613
|
+
const typeImports = hasSchemas
|
|
614
|
+
? `import type { InferInput, InferOutput } from '@agentuity/core';\n`
|
|
615
|
+
: '';
|
|
616
|
+
|
|
617
|
+
// Generate individual route schema types
|
|
618
|
+
const routeSchemaTypes = allRoutes
|
|
619
|
+
.filter(
|
|
620
|
+
(r) => r.hasValidator || r.inputSchemaVariable || r.outputSchemaVariable || r.agentVariable
|
|
621
|
+
)
|
|
622
|
+
.map((route) => {
|
|
623
|
+
const routeKey = route.method ? `${route.method.toUpperCase()} ${route.path}` : route.path;
|
|
624
|
+
const safeName = routeKey
|
|
625
|
+
.replace(/[^a-zA-Z0-9]/g, '_')
|
|
626
|
+
.replace(/^_+|_+$/g, '')
|
|
627
|
+
.replace(/_+/g, '_');
|
|
628
|
+
const pascalName = toPascalCase(safeName);
|
|
629
|
+
|
|
630
|
+
let inputType = 'never';
|
|
631
|
+
let outputType = 'never';
|
|
632
|
+
let inputSchemaType = 'never';
|
|
633
|
+
let outputSchemaType = 'never';
|
|
634
|
+
let agentMeta: AgentMetadata | undefined;
|
|
635
|
+
|
|
636
|
+
// Look up agent metadata if available
|
|
637
|
+
if (route.agentVariable) {
|
|
638
|
+
agentMeta = agentMetadataMap.get(route.agentVariable);
|
|
639
|
+
}
|
|
205
640
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
641
|
+
if (route.agentVariable) {
|
|
642
|
+
const importName = agentImports.get(route.agentVariable)!;
|
|
643
|
+
inputType = `InferInput<typeof ${importName}['inputSchema']>`;
|
|
644
|
+
outputType = `InferOutput<typeof ${importName}['outputSchema']>`;
|
|
645
|
+
inputSchemaType = `typeof ${importName} extends { inputSchema?: infer I } ? I : never`;
|
|
646
|
+
outputSchemaType = `typeof ${importName} extends { outputSchema?: infer O } ? O : never`;
|
|
647
|
+
} else if (route.inputSchemaVariable || route.outputSchemaVariable) {
|
|
648
|
+
// Get the aliased schema names for this route's file
|
|
649
|
+
const filename = route.filename.replace(/\\/g, '/');
|
|
650
|
+
const withoutSrc = filename.startsWith('src/') ? filename.substring(4) : filename;
|
|
651
|
+
const withoutLeadingDot = withoutSrc.startsWith('./')
|
|
652
|
+
? withoutSrc.substring(2)
|
|
653
|
+
: withoutSrc;
|
|
654
|
+
const importPath = `../${withoutLeadingDot.replace(/\.ts$/, '')}`;
|
|
655
|
+
const aliases = schemaImportAliases.get(importPath);
|
|
656
|
+
|
|
657
|
+
const inputAlias = route.inputSchemaVariable && aliases?.get(route.inputSchemaVariable);
|
|
658
|
+
const outputAlias =
|
|
659
|
+
route.outputSchemaVariable && aliases?.get(route.outputSchemaVariable);
|
|
660
|
+
|
|
661
|
+
inputType = inputAlias ? `InferInput<typeof ${inputAlias}>` : 'never';
|
|
662
|
+
outputType = outputAlias ? `InferOutput<typeof ${outputAlias}>` : 'never';
|
|
663
|
+
inputSchemaType = inputAlias ? `typeof ${inputAlias}` : 'never';
|
|
664
|
+
outputSchemaType = outputAlias ? `typeof ${outputAlias}` : 'never';
|
|
665
|
+
}
|
|
209
666
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
inputSchema: never;
|
|
214
|
-
outputSchema: never;
|
|
215
|
-
stream: ${streamValue};
|
|
216
|
-
};`;
|
|
217
|
-
}
|
|
667
|
+
if (inputType === 'never' && outputType === 'never') {
|
|
668
|
+
return ''; // Skip routes without schemas
|
|
669
|
+
}
|
|
218
670
|
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
671
|
+
// Build JSDoc with agent description and schema details
|
|
672
|
+
const inputJSDoc = ['/**', ` * Input type for route: ${routeKey}`];
|
|
673
|
+
if (agentMeta?.description) {
|
|
674
|
+
inputJSDoc.push(` * @description ${agentMeta.description}`);
|
|
675
|
+
}
|
|
676
|
+
if (agentMeta?.inputSchemaCode) {
|
|
677
|
+
inputJSDoc.push(` * @schema ${agentMeta.inputSchemaCode}`);
|
|
678
|
+
}
|
|
679
|
+
inputJSDoc.push(' */');
|
|
227
680
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
681
|
+
const outputJSDoc = ['/**', ` * Output type for route: ${routeKey}`];
|
|
682
|
+
if (agentMeta?.description) {
|
|
683
|
+
outputJSDoc.push(` * @description ${agentMeta.description}`);
|
|
684
|
+
}
|
|
685
|
+
if (agentMeta?.outputSchemaCode) {
|
|
686
|
+
outputJSDoc.push(` * @schema ${agentMeta.outputSchemaCode}`);
|
|
687
|
+
}
|
|
688
|
+
outputJSDoc.push(' */');
|
|
689
|
+
|
|
690
|
+
const parts = [
|
|
691
|
+
'',
|
|
692
|
+
...inputJSDoc,
|
|
693
|
+
`export type ${pascalName}Input = ${inputType};`,
|
|
694
|
+
'',
|
|
695
|
+
...outputJSDoc,
|
|
696
|
+
`export type ${pascalName}Output = ${outputType};`,
|
|
697
|
+
'',
|
|
698
|
+
`/**`,
|
|
699
|
+
` * Input schema type for route: ${routeKey}`,
|
|
700
|
+
` */`,
|
|
701
|
+
`export type ${pascalName}InputSchema = ${inputSchemaType};`,
|
|
702
|
+
'',
|
|
703
|
+
`/**`,
|
|
704
|
+
` * Output schema type for route: ${routeKey}`,
|
|
705
|
+
` */`,
|
|
706
|
+
`export type ${pascalName}OutputSchema = ${outputSchemaType};`,
|
|
707
|
+
];
|
|
708
|
+
return parts.join('\n');
|
|
709
|
+
})
|
|
710
|
+
.filter(Boolean)
|
|
711
|
+
.join('\n');
|
|
712
|
+
|
|
713
|
+
// Helper to generate route entry - uses exported schema types
|
|
714
|
+
const generateRouteEntry = (route: RouteInfo, pathIncludesMethod = false): string => {
|
|
715
|
+
const routeKey = route.path;
|
|
716
|
+
// For WebSocket/SSE routes, we need to include the method in the type name
|
|
717
|
+
// to match the generated types (which use "POST /api/websocket/echo" as the routeKey)
|
|
718
|
+
// For API routes, the method is already in the path from the caller
|
|
719
|
+
const typeRouteKey = pathIncludesMethod
|
|
720
|
+
? route.path
|
|
721
|
+
: `${route.method?.toUpperCase()} ${route.path}`;
|
|
722
|
+
const safeName = typeRouteKey
|
|
723
|
+
.replace(/[^a-zA-Z0-9]/g, '_')
|
|
724
|
+
.replace(/^_+|_+$/g, '')
|
|
725
|
+
.replace(/_+/g, '_');
|
|
726
|
+
const pascalName = toPascalCase(safeName);
|
|
727
|
+
|
|
728
|
+
if (
|
|
729
|
+
!route.hasValidator &&
|
|
730
|
+
!route.inputSchemaVariable &&
|
|
731
|
+
!route.outputSchemaVariable &&
|
|
732
|
+
!route.agentVariable
|
|
733
|
+
) {
|
|
235
734
|
const streamValue = route.stream === true ? 'true' : 'false';
|
|
236
|
-
return
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
735
|
+
return `\t'${routeKey}': {
|
|
736
|
+
\t\tinputSchema: never;
|
|
737
|
+
\t\toutputSchema: never;
|
|
738
|
+
\t\tstream: ${streamValue};
|
|
739
|
+
\t};`;
|
|
241
740
|
}
|
|
242
741
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
742
|
+
// Use the exported schema types we generated above
|
|
743
|
+
const importName = route.agentVariable ? agentImports.get(route.agentVariable)! : null;
|
|
744
|
+
const streamValue = importName
|
|
745
|
+
? `typeof ${importName} extends { stream?: infer S } ? S : false`
|
|
746
|
+
: route.stream === true
|
|
747
|
+
? 'true'
|
|
748
|
+
: 'false';
|
|
749
|
+
|
|
750
|
+
return `\t'${routeKey}': {
|
|
751
|
+
\t\tinputSchema: ${pascalName}InputSchema;
|
|
752
|
+
\t\toutputSchema: ${pascalName}OutputSchema;
|
|
753
|
+
\t\tstream: ${streamValue};
|
|
754
|
+
\t};`;
|
|
247
755
|
};
|
|
248
756
|
|
|
249
757
|
// Generate route entries with METHOD prefix for API routes
|
|
250
758
|
const apiRouteEntries = apiRoutes
|
|
251
759
|
.map((route) => {
|
|
252
760
|
const routeKey = `${route.method.toUpperCase()} ${route.path}`;
|
|
253
|
-
return generateRouteEntry({ ...route, path: routeKey });
|
|
761
|
+
return generateRouteEntry({ ...route, path: routeKey }, true);
|
|
254
762
|
})
|
|
255
763
|
.join('\n');
|
|
256
764
|
|
|
257
|
-
const websocketRouteEntries = websocketRoutes
|
|
258
|
-
|
|
765
|
+
const websocketRouteEntries = websocketRoutes
|
|
766
|
+
.map((r) => generateRouteEntry(r, false))
|
|
767
|
+
.join('\n');
|
|
768
|
+
const sseRouteEntries = sseRoutes.map((r) => generateRouteEntry(r, false)).join('\n');
|
|
769
|
+
|
|
770
|
+
// Generate RPC-style nested registry type
|
|
771
|
+
const rpcRegistryType = generateRPCRegistryType(
|
|
772
|
+
apiRoutes,
|
|
773
|
+
websocketRoutes,
|
|
774
|
+
sseRoutes,
|
|
775
|
+
agentImports,
|
|
776
|
+
schemaImportAliases,
|
|
777
|
+
agentMetadataMap
|
|
778
|
+
);
|
|
779
|
+
const rpcRuntimeMetadata = generateRPCRuntimeMetadata(apiRoutes, websocketRoutes, sseRoutes);
|
|
780
|
+
|
|
781
|
+
const generatedContent = `// @generated
|
|
782
|
+
// Auto-generated by Agentuity - DO NOT EDIT
|
|
783
|
+
${importsStr}${typeImports}${
|
|
784
|
+
!hasReactDependency
|
|
785
|
+
? `
|
|
786
|
+
import { createClient } from '@agentuity/frontend';`
|
|
787
|
+
: ''
|
|
788
|
+
}
|
|
789
|
+
// ============================================================================
|
|
790
|
+
// Route Schema Type Exports
|
|
791
|
+
// ============================================================================
|
|
792
|
+
${routeSchemaTypes}
|
|
259
793
|
|
|
260
|
-
|
|
261
|
-
|
|
794
|
+
// ============================================================================
|
|
795
|
+
// Route Definitions
|
|
796
|
+
// ============================================================================
|
|
262
797
|
|
|
263
|
-
|
|
798
|
+
/**
|
|
799
|
+
* Route Definitions
|
|
800
|
+
*
|
|
801
|
+
* Type-safe route registry for all API routes, WebSocket connections, and SSE endpoints.
|
|
802
|
+
* Used by @agentuity/react for client-side type-safe routing.
|
|
803
|
+
*
|
|
804
|
+
* @remarks
|
|
805
|
+
* This module augmentation is auto-generated from your route files during build.
|
|
806
|
+
* Individual route Input/Output types are exported above for direct usage.
|
|
807
|
+
*/
|
|
808
|
+
${
|
|
809
|
+
!hasReactDependency
|
|
810
|
+
? `
|
|
811
|
+
/**
|
|
812
|
+
* RPC Route Registry
|
|
813
|
+
*
|
|
814
|
+
* Nested structure for RPC-style client access (e.g., client.hello.post())
|
|
815
|
+
* Used by createClient() from @agentuity/frontend for type-safe RPC calls.
|
|
816
|
+
*/
|
|
817
|
+
export interface RPCRouteRegistry {
|
|
818
|
+
${rpcRegistryType}
|
|
819
|
+
}
|
|
820
|
+
`
|
|
821
|
+
: ''
|
|
822
|
+
}
|
|
264
823
|
declare module '@agentuity/react' {
|
|
265
|
-
|
|
824
|
+
\t/**
|
|
825
|
+
\t * API Route Registry
|
|
826
|
+
\t *
|
|
827
|
+
\t * Maps route keys (METHOD /path) to their input/output schemas
|
|
828
|
+
\t */
|
|
829
|
+
\texport interface RouteRegistry {
|
|
266
830
|
${apiRouteEntries}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
831
|
+
\t}
|
|
832
|
+
\t
|
|
833
|
+
\t/**
|
|
834
|
+
\t * WebSocket Route Registry
|
|
835
|
+
\t *
|
|
836
|
+
\t * Maps WebSocket route paths to their schemas
|
|
837
|
+
\t */
|
|
838
|
+
\texport interface WebSocketRouteRegistry {
|
|
270
839
|
${websocketRouteEntries}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
840
|
+
\t}
|
|
841
|
+
\t
|
|
842
|
+
\t/**
|
|
843
|
+
\t * Server-Sent Events Route Registry
|
|
844
|
+
\t *
|
|
845
|
+
\t * Maps SSE route paths to their schemas
|
|
846
|
+
\t */
|
|
847
|
+
\texport interface SSERouteRegistry {
|
|
274
848
|
${sseRouteEntries}
|
|
275
|
-
|
|
849
|
+
\t}
|
|
850
|
+
|
|
851
|
+
\t/**
|
|
852
|
+
\t * RPC Route Registry
|
|
853
|
+
\t *
|
|
854
|
+
\t * Nested structure for RPC-style client access (e.g., client.hello.post())
|
|
855
|
+
\t * Used by createClient() from @agentuity/core for type-safe RPC calls.
|
|
856
|
+
\t */
|
|
857
|
+
\texport interface RPCRouteRegistry {
|
|
858
|
+
${rpcRegistryType}
|
|
859
|
+
\t}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Runtime metadata for RPC routes.
|
|
864
|
+
* Contains route type information for client routing decisions.
|
|
865
|
+
* @internal
|
|
866
|
+
*/
|
|
867
|
+
const _rpcRouteMetadata = ${rpcRuntimeMetadata} as const;
|
|
868
|
+
|
|
869
|
+
// Store metadata globally for createAPIClient() to access
|
|
870
|
+
if (typeof globalThis !== 'undefined') {
|
|
871
|
+
(globalThis as Record<string, unknown>).__rpcRouteMetadata = _rpcRouteMetadata;
|
|
276
872
|
}
|
|
873
|
+
${
|
|
874
|
+
!hasReactDependency
|
|
875
|
+
? `
|
|
876
|
+
/**
|
|
877
|
+
* Create a type-safe API client with optional configuration.
|
|
878
|
+
*
|
|
879
|
+
* This function is only generated when @agentuity/react is not installed.
|
|
880
|
+
* If using React, import createAPIClient from '@agentuity/react' instead.
|
|
881
|
+
*
|
|
882
|
+
* @example
|
|
883
|
+
* \`\`\`typescript
|
|
884
|
+
* import { createAPIClient } from './generated/routes';
|
|
885
|
+
*
|
|
886
|
+
* // Basic usage
|
|
887
|
+
* const api = createAPIClient();
|
|
888
|
+
* const result = await api.hello.post({ name: 'World' });
|
|
889
|
+
*
|
|
890
|
+
* // With custom headers
|
|
891
|
+
* const api = createAPIClient({ headers: { 'X-Custom-Header': 'value' } });
|
|
892
|
+
* await api.hello.post({ name: 'World' });
|
|
893
|
+
* \`\`\`
|
|
894
|
+
*/
|
|
895
|
+
export function createAPIClient(options?: Parameters<typeof createClient>[0]): import('@agentuity/frontend').Client<RPCRouteRegistry> {
|
|
896
|
+
return createClient(options || {}, _rpcRouteMetadata) as import('@agentuity/frontend').Client<RPCRouteRegistry>;
|
|
897
|
+
}
|
|
898
|
+
`
|
|
899
|
+
: `
|
|
900
|
+
/**
|
|
901
|
+
* Type-safe API client is available from @agentuity/react
|
|
902
|
+
*
|
|
903
|
+
* @example
|
|
904
|
+
* \`\`\`typescript
|
|
905
|
+
* import { createAPIClient } from '@agentuity/react';
|
|
906
|
+
*
|
|
907
|
+
* const api = createAPIClient();
|
|
908
|
+
* const result = await api.hello.post({ name: 'World' });
|
|
909
|
+
* \`\`\`
|
|
910
|
+
*/
|
|
911
|
+
`
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// FOUND AN ERROR IN THIS FILE?
|
|
915
|
+
// Please file an issue at https://github.com/agentuity/sdk/issues
|
|
916
|
+
// or if you know the fix please submit a PR!
|
|
277
917
|
`;
|
|
278
918
|
|
|
279
|
-
const
|
|
280
|
-
const
|
|
281
|
-
const registryPath = join(agentuityDir, 'routes.generated.ts');
|
|
919
|
+
const generatedDir = join(srcDir, 'generated');
|
|
920
|
+
const registryPath = join(generatedDir, 'routes.ts');
|
|
282
921
|
|
|
283
|
-
if (!existsSync(
|
|
284
|
-
mkdirSync(
|
|
922
|
+
if (!existsSync(generatedDir)) {
|
|
923
|
+
mkdirSync(generatedDir, { recursive: true });
|
|
285
924
|
}
|
|
286
925
|
|
|
287
|
-
|
|
926
|
+
// Collapse 2+ consecutive empty lines into 1 empty line (3+ \n becomes 2 \n)
|
|
927
|
+
const cleanedContent = generatedContent.replace(/\n{3,}/g, '\n\n');
|
|
928
|
+
|
|
929
|
+
writeFileSync(registryPath, cleanedContent, 'utf-8');
|
|
288
930
|
}
|