@agentuity/cli 0.0.6
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 +139 -0
- package/README.md +239 -0
- package/bin/cli.ts +71 -0
- package/dist/api.d.ts +25 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/auth.d.ts +7 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/banner.d.ts +2 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cmd/auth/api.d.ts +9 -0
- package/dist/cmd/auth/api.d.ts.map +1 -0
- package/dist/cmd/auth/index.d.ts +2 -0
- package/dist/cmd/auth/index.d.ts.map +1 -0
- package/dist/cmd/auth/login.d.ts +3 -0
- package/dist/cmd/auth/login.d.ts.map +1 -0
- package/dist/cmd/auth/logout.d.ts +3 -0
- package/dist/cmd/auth/logout.d.ts.map +1 -0
- package/dist/cmd/bundle/ast.d.ts +2 -0
- package/dist/cmd/bundle/ast.d.ts.map +1 -0
- package/dist/cmd/bundle/bundler.d.ts +6 -0
- package/dist/cmd/bundle/bundler.d.ts.map +1 -0
- package/dist/cmd/bundle/file.d.ts +2 -0
- package/dist/cmd/bundle/file.d.ts.map +1 -0
- package/dist/cmd/bundle/index.d.ts +2 -0
- package/dist/cmd/bundle/index.d.ts.map +1 -0
- package/dist/cmd/bundle/plugin.d.ts +4 -0
- package/dist/cmd/bundle/plugin.d.ts.map +1 -0
- package/dist/cmd/dev/index.d.ts +2 -0
- package/dist/cmd/dev/index.d.ts.map +1 -0
- package/dist/cmd/example/create-user.d.ts +2 -0
- package/dist/cmd/example/create-user.d.ts.map +1 -0
- package/dist/cmd/example/create.d.ts +2 -0
- package/dist/cmd/example/create.d.ts.map +1 -0
- package/dist/cmd/example/deploy.d.ts +2 -0
- package/dist/cmd/example/deploy.d.ts.map +1 -0
- package/dist/cmd/example/index.d.ts +2 -0
- package/dist/cmd/example/index.d.ts.map +1 -0
- package/dist/cmd/example/list.d.ts +2 -0
- package/dist/cmd/example/list.d.ts.map +1 -0
- package/dist/cmd/example/run-command.d.ts +2 -0
- package/dist/cmd/example/run-command.d.ts.map +1 -0
- package/dist/cmd/example/sound.d.ts +3 -0
- package/dist/cmd/example/sound.d.ts.map +1 -0
- package/dist/cmd/example/spinner.d.ts +2 -0
- package/dist/cmd/example/spinner.d.ts.map +1 -0
- package/dist/cmd/example/steps.d.ts +2 -0
- package/dist/cmd/example/steps.d.ts.map +1 -0
- package/dist/cmd/example/version.d.ts +2 -0
- package/dist/cmd/example/version.d.ts.map +1 -0
- package/dist/cmd/index.d.ts +3 -0
- package/dist/cmd/index.d.ts.map +1 -0
- package/dist/cmd/profile/create.d.ts +2 -0
- package/dist/cmd/profile/create.d.ts.map +1 -0
- package/dist/cmd/profile/delete.d.ts +2 -0
- package/dist/cmd/profile/delete.d.ts.map +1 -0
- package/dist/cmd/profile/index.d.ts +2 -0
- package/dist/cmd/profile/index.d.ts.map +1 -0
- package/dist/cmd/profile/list.d.ts +3 -0
- package/dist/cmd/profile/list.d.ts.map +1 -0
- package/dist/cmd/profile/show.d.ts +2 -0
- package/dist/cmd/profile/show.d.ts.map +1 -0
- package/dist/cmd/profile/use.d.ts +2 -0
- package/dist/cmd/profile/use.d.ts.map +1 -0
- package/dist/cmd/project/create.d.ts +2 -0
- package/dist/cmd/project/create.d.ts.map +1 -0
- package/dist/cmd/project/delete.d.ts +2 -0
- package/dist/cmd/project/delete.d.ts.map +1 -0
- package/dist/cmd/project/index.d.ts +2 -0
- package/dist/cmd/project/index.d.ts.map +1 -0
- package/dist/cmd/project/list.d.ts +2 -0
- package/dist/cmd/project/list.d.ts.map +1 -0
- package/dist/cmd/project/show.d.ts +2 -0
- package/dist/cmd/project/show.d.ts.map +1 -0
- package/dist/cmd/version/index.d.ts +2 -0
- package/dist/cmd/version/index.d.ts.map +1 -0
- package/dist/command-prefix.d.ts +11 -0
- package/dist/command-prefix.d.ts.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/legacy-check.d.ts +6 -0
- package/dist/legacy-check.d.ts.map +1 -0
- package/dist/logger.d.ts +24 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/schema-parser.d.ts +24 -0
- package/dist/schema-parser.d.ts.map +1 -0
- package/dist/sound.d.ts +2 -0
- package/dist/sound.d.ts.map +1 -0
- package/dist/steps.d.ts +59 -0
- package/dist/steps.d.ts.map +1 -0
- package/dist/terminal.d.ts +3 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/tui.d.ts +156 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/types.d.ts +164 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +46 -0
- package/src/api-errors.md +115 -0
- package/src/api.ts +186 -0
- package/src/auth.ts +91 -0
- package/src/banner.ts +23 -0
- package/src/cli.ts +198 -0
- package/src/cmd/auth/README.md +95 -0
- package/src/cmd/auth/api.ts +71 -0
- package/src/cmd/auth/index.ts +9 -0
- package/src/cmd/auth/login.ts +76 -0
- package/src/cmd/auth/logout.ts +14 -0
- package/src/cmd/bundle/ast.ts +228 -0
- package/src/cmd/bundle/bundler.ts +88 -0
- package/src/cmd/bundle/file.ts +16 -0
- package/src/cmd/bundle/index.ts +38 -0
- package/src/cmd/bundle/plugin.ts +259 -0
- package/src/cmd/dev/index.ts +83 -0
- package/src/cmd/example/create-user.ts +38 -0
- package/src/cmd/example/create.ts +31 -0
- package/src/cmd/example/deploy.ts +36 -0
- package/src/cmd/example/index.ts +27 -0
- package/src/cmd/example/list.ts +32 -0
- package/src/cmd/example/run-command.ts +45 -0
- package/src/cmd/example/sound.ts +14 -0
- package/src/cmd/example/spinner.ts +44 -0
- package/src/cmd/example/steps.ts +66 -0
- package/src/cmd/example/version.ts +13 -0
- package/src/cmd/index.ts +46 -0
- package/src/cmd/profile/README.md +80 -0
- package/src/cmd/profile/create.ts +57 -0
- package/src/cmd/profile/delete.ts +52 -0
- package/src/cmd/profile/index.ts +12 -0
- package/src/cmd/profile/list.ts +27 -0
- package/src/cmd/profile/show.ts +54 -0
- package/src/cmd/profile/use.ts +30 -0
- package/src/cmd/project/create.ts +247 -0
- package/src/cmd/project/delete.ts +13 -0
- package/src/cmd/project/index.ts +11 -0
- package/src/cmd/project/list.ts +13 -0
- package/src/cmd/project/show.ts +12 -0
- package/src/cmd/version/index.ts +16 -0
- package/src/command-prefix.ts +43 -0
- package/src/config.ts +304 -0
- package/src/index.ts +40 -0
- package/src/legacy-check.ts +127 -0
- package/src/logger.ts +235 -0
- package/src/runtime.ts +22 -0
- package/src/schema-parser.ts +213 -0
- package/src/sound.ts +25 -0
- package/src/steps.ts +245 -0
- package/src/terminal.ts +151 -0
- package/src/tui.md +254 -0
- package/src/tui.ts +838 -0
- package/src/types.ts +243 -0
- package/src/version.ts +29 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { existsSync, rmSync } from 'node:fs';
|
|
3
|
+
import AgentuityBundler from './plugin';
|
|
4
|
+
import { getFilesRecursively } from './file';
|
|
5
|
+
import { getVersion } from '../../version';
|
|
6
|
+
|
|
7
|
+
export interface BundleOptions {
|
|
8
|
+
rootDir: string;
|
|
9
|
+
dev?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function bundle({ dev = false, rootDir }: BundleOptions) {
|
|
13
|
+
const appFile = join(rootDir, 'app.ts');
|
|
14
|
+
if (!existsSync(appFile)) {
|
|
15
|
+
throw new Error(`App file not found at expected location: ${appFile}`);
|
|
16
|
+
}
|
|
17
|
+
const outDir = join(rootDir, '.agentuity');
|
|
18
|
+
const srcDir = join(rootDir, 'src');
|
|
19
|
+
|
|
20
|
+
const entrypoints: string[] = [];
|
|
21
|
+
|
|
22
|
+
for (const folder of ['apis', 'agents', 'web']) {
|
|
23
|
+
const dir = join(srcDir, folder);
|
|
24
|
+
if (!existsSync(dir)) {
|
|
25
|
+
if (folder === 'agents') {
|
|
26
|
+
throw new Error(`Expected directory not found: ${dir}`);
|
|
27
|
+
}
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const files = await getFilesRecursively(dir);
|
|
31
|
+
for (const filename of files) {
|
|
32
|
+
if (/\.[jt]sx?$/.test(filename)) {
|
|
33
|
+
entrypoints.push(filename);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
entrypoints.push(appFile);
|
|
39
|
+
|
|
40
|
+
if (existsSync(outDir)) {
|
|
41
|
+
rmSync(outDir, { recursive: true, force: true });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const pkgFile = Bun.file('./package.json');
|
|
45
|
+
const pkgContents = JSON.parse(await pkgFile.text());
|
|
46
|
+
|
|
47
|
+
const isProd = !dev;
|
|
48
|
+
|
|
49
|
+
const define = {
|
|
50
|
+
'process.env.AGENTUITY_CLOUD_SDK_VERSION': JSON.stringify(getVersion() ?? '1.0.0'),
|
|
51
|
+
'process.env.NODE_ENV': JSON.stringify(isProd ? 'production' : 'development'),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const config: Bun.BuildConfig = {
|
|
55
|
+
entrypoints,
|
|
56
|
+
root: rootDir,
|
|
57
|
+
outdir: outDir,
|
|
58
|
+
define,
|
|
59
|
+
sourcemap: dev ? 'inline' : 'external',
|
|
60
|
+
env: 'AGENTUITY_CLOUD_*',
|
|
61
|
+
plugins: [AgentuityBundler],
|
|
62
|
+
target: 'bun',
|
|
63
|
+
format: 'esm',
|
|
64
|
+
banner: `// Generated file. DO NOT EDIT`,
|
|
65
|
+
minify: isProd,
|
|
66
|
+
drop: isProd ? ['debugger'] : undefined,
|
|
67
|
+
naming: isProd
|
|
68
|
+
? {
|
|
69
|
+
entry: '[dir]/[name].js',
|
|
70
|
+
chunk: 'chunks/[name]-[hash].js',
|
|
71
|
+
asset: 'assets/[name]-[hash].[ext]',
|
|
72
|
+
}
|
|
73
|
+
: undefined,
|
|
74
|
+
conditions: [isProd ? 'production' : 'development', 'bun'],
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
await Bun.write(
|
|
78
|
+
`${outDir}/package.json`,
|
|
79
|
+
JSON.stringify({ name: pkgContents.name, version: pkgContents.version }, null, 2)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const agentuityYAML = Bun.file('./agentuity.yaml');
|
|
83
|
+
if (await agentuityYAML.exists()) {
|
|
84
|
+
await Bun.write(`${outDir}/agentuity.yaml`, agentuityYAML);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await Bun.build(config);
|
|
88
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
|
|
5
|
+
const readdir = promisify(fs.readdir);
|
|
6
|
+
|
|
7
|
+
export async function getFilesRecursively(dir: string): Promise<string[]> {
|
|
8
|
+
const subdirs = await readdir(dir, { withFileTypes: true });
|
|
9
|
+
const files = await Promise.all(
|
|
10
|
+
subdirs.map(async (dirent: fs.Dirent) => {
|
|
11
|
+
const res = resolve(dir, dirent.name);
|
|
12
|
+
return dirent.isDirectory() ? getFilesRecursively(res) : res;
|
|
13
|
+
})
|
|
14
|
+
);
|
|
15
|
+
return files.flat();
|
|
16
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { bundle } from './bundler';
|
|
5
|
+
|
|
6
|
+
export const command = createSubcommand({
|
|
7
|
+
name: 'bundle',
|
|
8
|
+
description: 'Bundle Agentuity application for deployment',
|
|
9
|
+
aliases: ['build'],
|
|
10
|
+
schema: {
|
|
11
|
+
options: z.object({
|
|
12
|
+
dir: z.string().optional().describe('Root directory of the project'),
|
|
13
|
+
dev: z.boolean().optional().describe('Enable development mode'),
|
|
14
|
+
}),
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
async handler(ctx) {
|
|
18
|
+
const { logger, opts } = ctx;
|
|
19
|
+
const rootDir = resolve(opts.dir || process.cwd());
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
logger.info(`Bundling project at: ${rootDir}`);
|
|
23
|
+
|
|
24
|
+
await bundle({
|
|
25
|
+
rootDir,
|
|
26
|
+
dev: opts.dev || false,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
logger.info('✓ Bundle complete');
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error instanceof Error) {
|
|
32
|
+
logger.fatal(`Bundle failed: ${error.message}`);
|
|
33
|
+
} else {
|
|
34
|
+
logger.fatal('Bundle failed');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
});
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import type { BunPlugin } from 'bun';
|
|
2
|
+
import { dirname, basename, join } from 'node:path';
|
|
3
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { parseAgentMetadata } from './ast';
|
|
5
|
+
|
|
6
|
+
function toCamelCase(str: string): string {
|
|
7
|
+
return str
|
|
8
|
+
.replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ''))
|
|
9
|
+
.replace(/^(.)/, (char) => char.toLowerCase());
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function toPascalCase(str: string): string {
|
|
13
|
+
const camel = toCamelCase(str);
|
|
14
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function generateAgentRegistry(srcDir: string, agentInfo: Array<Record<string, string>>) {
|
|
18
|
+
const imports = agentInfo
|
|
19
|
+
.map(({ name, path }) => {
|
|
20
|
+
const camelName = toCamelCase(name);
|
|
21
|
+
const relativePath = path.replace(/^\.\/agents\//, './');
|
|
22
|
+
return `import ${camelName}Agent from '${relativePath}';`;
|
|
23
|
+
})
|
|
24
|
+
.join('\n');
|
|
25
|
+
|
|
26
|
+
const registry = agentInfo
|
|
27
|
+
.map(({ name }) => {
|
|
28
|
+
const camelName = toCamelCase(name);
|
|
29
|
+
return ` ${camelName}: ${camelName}Agent,`;
|
|
30
|
+
})
|
|
31
|
+
.join('\n');
|
|
32
|
+
|
|
33
|
+
const typeExports = agentInfo
|
|
34
|
+
.map(({ name }) => {
|
|
35
|
+
const camelName = toCamelCase(name);
|
|
36
|
+
const pascalName = toPascalCase(name);
|
|
37
|
+
return `export type ${pascalName}AgentRunner = AgentRunner<typeof ${camelName}Agent['inputSchema'], typeof ${camelName}Agent['outputSchema'], typeof ${camelName}Agent['stream'] extends true ? true : false>;`;
|
|
38
|
+
})
|
|
39
|
+
.join('\n');
|
|
40
|
+
|
|
41
|
+
const generatedContent = `// Auto-generated by Agentuity - do not edit manually
|
|
42
|
+
${imports}
|
|
43
|
+
import type { AgentRunner, Logger } from '@agentuity/runtime';
|
|
44
|
+
import type { KeyValueStorage, ObjectStorage, StreamStorage, VectorStorage } from '@agentuity/core';
|
|
45
|
+
|
|
46
|
+
export const agentRegistry = {
|
|
47
|
+
${registry}
|
|
48
|
+
} as const;
|
|
49
|
+
|
|
50
|
+
export type AgentName = keyof typeof agentRegistry;
|
|
51
|
+
export type AgentRegistry = typeof agentRegistry;
|
|
52
|
+
|
|
53
|
+
// Typed runners for each agent
|
|
54
|
+
${typeExports}
|
|
55
|
+
|
|
56
|
+
// Augment Context to provide strongly-typed agents
|
|
57
|
+
declare module "hono" {
|
|
58
|
+
interface Context {
|
|
59
|
+
agentName: AgentName;
|
|
60
|
+
agent: {
|
|
61
|
+
[K in AgentName]: AgentRunner<AgentRegistry[K]['inputSchema'], AgentRegistry[K]['outputSchema'], AgentRegistry[K]['stream'] extends true ? true : false>;
|
|
62
|
+
};
|
|
63
|
+
waitUntil: (promise: Promise<void> | (() => void | Promise<void>)) => void;
|
|
64
|
+
logger: Logger;
|
|
65
|
+
kv: KeyValueStorage;
|
|
66
|
+
objectstore: ObjectStorage;
|
|
67
|
+
stream: StreamStorage;
|
|
68
|
+
vector: VectorStorage;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
const agentsDir = join(srcDir, 'agents');
|
|
74
|
+
const registryPath = join(agentsDir, 'registry.generated.ts');
|
|
75
|
+
|
|
76
|
+
writeFileSync(registryPath, generatedContent, 'utf-8');
|
|
77
|
+
|
|
78
|
+
const clientTypesContent = `// Auto-generated by Agentuity - do not edit manually
|
|
79
|
+
// This file augments @agentuity/react with your project's agent types
|
|
80
|
+
${agentInfo
|
|
81
|
+
.map(({ name, path }) => {
|
|
82
|
+
const camelName = toCamelCase(name);
|
|
83
|
+
const relativePath = path.replace(/^\.\/agents\//, './');
|
|
84
|
+
return `import type ${camelName}Agent from '${relativePath}';`;
|
|
85
|
+
})
|
|
86
|
+
.join('\n')}
|
|
87
|
+
import type { Agent } from '@agentuity/react';
|
|
88
|
+
|
|
89
|
+
declare module '@agentuity/react' {
|
|
90
|
+
interface AgentRegistry {
|
|
91
|
+
${agentInfo
|
|
92
|
+
.map(({ name }) => {
|
|
93
|
+
const camelName = toCamelCase(name);
|
|
94
|
+
return ` '${name}': Agent<typeof ${camelName}Agent['inputSchema'], typeof ${camelName}Agent['outputSchema']>;`;
|
|
95
|
+
})
|
|
96
|
+
.join('\n')}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
const clientTypesPath = join(agentsDir, 'types.generated.d.ts');
|
|
102
|
+
writeFileSync(clientTypesPath, clientTypesContent, 'utf-8');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const AgentuityBundler: BunPlugin = {
|
|
106
|
+
name: 'Agentuity Bundler',
|
|
107
|
+
setup(build) {
|
|
108
|
+
const rootDir = build.config.root ?? '.';
|
|
109
|
+
const srcDir = join(rootDir, 'src');
|
|
110
|
+
const routes: Set<string> = new Set();
|
|
111
|
+
const agentInfo: Array<Record<string, string>> = [];
|
|
112
|
+
const agentMetadata: Map<string, Map<string, string>> = new Map<
|
|
113
|
+
string,
|
|
114
|
+
Map<string, string>
|
|
115
|
+
>();
|
|
116
|
+
const transpiler = new Bun.Transpiler({ loader: 'ts' });
|
|
117
|
+
|
|
118
|
+
build.onResolve({ filter: /\/route\.ts$/, namespace: 'file' }, async (args) => {
|
|
119
|
+
if (args.path.startsWith(srcDir)) {
|
|
120
|
+
const importPath = args.path
|
|
121
|
+
.replace(rootDir, '')
|
|
122
|
+
.replace('.ts', '')
|
|
123
|
+
.replace('/src/', './');
|
|
124
|
+
routes.add(importPath);
|
|
125
|
+
}
|
|
126
|
+
return args;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
build.onLoad({ filter: /\/agent\.ts$/, namespace: 'file' }, async (args) => {
|
|
130
|
+
let newsource = await Bun.file(args.path).text();
|
|
131
|
+
if (args.path.startsWith(srcDir)) {
|
|
132
|
+
const contents = transpiler.transformSync(newsource);
|
|
133
|
+
const [ns, md] = parseAgentMetadata(rootDir, args.path, contents);
|
|
134
|
+
newsource = ns;
|
|
135
|
+
agentMetadata.set(md.get('identifier')!, md);
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
contents: newsource,
|
|
139
|
+
loader: 'ts',
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
build.onLoad(
|
|
144
|
+
{
|
|
145
|
+
filter: new RegExp(join(rootDir, 'app.ts')),
|
|
146
|
+
namespace: 'file',
|
|
147
|
+
},
|
|
148
|
+
async (args) => {
|
|
149
|
+
await args.defer();
|
|
150
|
+
|
|
151
|
+
const inserts: string[] = [];
|
|
152
|
+
|
|
153
|
+
for (const route of routes) {
|
|
154
|
+
const name = basename(dirname(route));
|
|
155
|
+
const agent = route.replace(/\/route$/, '/agent');
|
|
156
|
+
const hasAgent = existsSync(join(srcDir, agent + '.ts'));
|
|
157
|
+
const agentPath = route
|
|
158
|
+
.replace(/\/route$/, '/*')
|
|
159
|
+
.replace('/agents', '/agent')
|
|
160
|
+
.replace('./', '/');
|
|
161
|
+
const routePath = route
|
|
162
|
+
.replace(/\/route$/, '')
|
|
163
|
+
.replace('/apis/', '/api/')
|
|
164
|
+
.replace('/apis', '/api')
|
|
165
|
+
.replace('/agents', '/agent')
|
|
166
|
+
.replace('/agents', '/agent')
|
|
167
|
+
.replace('./', '/');
|
|
168
|
+
|
|
169
|
+
let agentDetail: Record<string, string> = {};
|
|
170
|
+
|
|
171
|
+
if (hasAgent) {
|
|
172
|
+
const md = agentMetadata.get(name);
|
|
173
|
+
if (!md) {
|
|
174
|
+
throw new Error(`Couldn't find agent metadata for ${route}`);
|
|
175
|
+
}
|
|
176
|
+
agentDetail = {
|
|
177
|
+
name,
|
|
178
|
+
path: `.${agent}`,
|
|
179
|
+
filename: md.get('filename')!,
|
|
180
|
+
identifier: md.get('identifier')!,
|
|
181
|
+
description: md.get('description') ?? '',
|
|
182
|
+
};
|
|
183
|
+
agentInfo.push(agentDetail);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let buffer = `await (async() => {
|
|
187
|
+
const { createAgentMiddleware, getApp, registerAgent } = await import('@agentuity/runtime');
|
|
188
|
+
const app = getApp()!;
|
|
189
|
+
const route = require('./src/${route}').default;`;
|
|
190
|
+
if (hasAgent) {
|
|
191
|
+
buffer += `
|
|
192
|
+
const agent = require('./src/${agent}').default;
|
|
193
|
+
app.all("${agentPath}", createAgentMiddleware('${name}'));
|
|
194
|
+
registerAgent("${name}", agent);`;
|
|
195
|
+
}
|
|
196
|
+
buffer += `
|
|
197
|
+
app.route("${routePath}", route);
|
|
198
|
+
})();`;
|
|
199
|
+
inserts.push(buffer);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const indexFile = join(srcDir, 'web', 'app.tsx');
|
|
203
|
+
|
|
204
|
+
if (existsSync(indexFile)) {
|
|
205
|
+
const uniqid = Math.random().toString(36);
|
|
206
|
+
const clientjscode = `
|
|
207
|
+
import React from "react";
|
|
208
|
+
import { createRoot } from "react-dom/client";
|
|
209
|
+
import { App } from "./app";
|
|
210
|
+
|
|
211
|
+
const root = createRoot(document.getElementById("root"));
|
|
212
|
+
root.render(<App />);
|
|
213
|
+
`;
|
|
214
|
+
const clientFile = join(srcDir, 'web', 'client.generated.js');
|
|
215
|
+
writeFileSync(clientFile, clientjscode);
|
|
216
|
+
|
|
217
|
+
inserts.unshift(`await (async () => {
|
|
218
|
+
const { renderToString } = require('react-dom/server');
|
|
219
|
+
const { serveStatic } = require('hono/bun');
|
|
220
|
+
const { getApp } = await import('@agentuity/runtime');
|
|
221
|
+
const app = getApp()!;
|
|
222
|
+
const routehtml = renderToString(require("./src/web/app").default);
|
|
223
|
+
app.get("/", (c) => c.html(\`<html><body><div id="root">\${routehtml}</div><script src="/web/${uniqid}/client.js" type="module"></script></body></html>\`));
|
|
224
|
+
app.get("/web/${uniqid}/client.js", (c) => new Response(Bun.file('./src/web/client.generated.js'), { type: 'text/javascript', headers: { 'Cache-Control': 'public, max-age=31536000, immutable' } }));
|
|
225
|
+
app.get('/public/*', serveStatic({ root: './src/web' }));
|
|
226
|
+
})();`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
generateAgentRegistry(srcDir, agentInfo);
|
|
230
|
+
|
|
231
|
+
const file = Bun.file(args.path);
|
|
232
|
+
let contents = await file.text();
|
|
233
|
+
let inserted = false;
|
|
234
|
+
const index = contents.indexOf(' createApp(');
|
|
235
|
+
if (index > 0) {
|
|
236
|
+
const endSemi = contents.indexOf(');', index);
|
|
237
|
+
if (endSemi > 0) {
|
|
238
|
+
contents =
|
|
239
|
+
contents.slice(0, endSemi + 2) +
|
|
240
|
+
'\n\n' +
|
|
241
|
+
inserts.join('\n') +
|
|
242
|
+
contents.slice(endSemi + 2);
|
|
243
|
+
inserted = true;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (!inserted) {
|
|
247
|
+
contents += `\n${inserts.join('\n')}`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
contents,
|
|
252
|
+
loader: 'ts',
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export default AgentuityBundler;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createCommand } from '@/types';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { bundle } from '../bundle/bundler';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import * as tui from '@/tui';
|
|
7
|
+
|
|
8
|
+
export const command = createCommand({
|
|
9
|
+
name: 'dev',
|
|
10
|
+
description: 'Build and run the development server',
|
|
11
|
+
schema: {
|
|
12
|
+
options: z.object({
|
|
13
|
+
dir: z.string().optional().describe('Root directory of the project'),
|
|
14
|
+
}),
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
async handler(ctx) {
|
|
18
|
+
const { opts } = ctx;
|
|
19
|
+
const rootDir = resolve(opts.dir || process.cwd());
|
|
20
|
+
const agentuityDir = resolve(rootDir, '.agentuity');
|
|
21
|
+
const appPath = resolve(agentuityDir, 'app.js');
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
await tui.spinner('Building project...', async () => {
|
|
25
|
+
await bundle({
|
|
26
|
+
rootDir,
|
|
27
|
+
dev: true,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
tui.success('Build complete');
|
|
32
|
+
|
|
33
|
+
if (!existsSync(appPath)) {
|
|
34
|
+
tui.error(`App file not found: ${appPath}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
tui.newline();
|
|
39
|
+
tui.info('Starting development server...');
|
|
40
|
+
tui.newline();
|
|
41
|
+
|
|
42
|
+
// Use shell to run in a process group for proper cleanup
|
|
43
|
+
// The 'exec' ensures the shell is replaced by the actual process
|
|
44
|
+
const devServer = Bun.spawn(['sh', '-c', `exec bun run "${appPath}"`], {
|
|
45
|
+
cwd: rootDir,
|
|
46
|
+
stdout: 'inherit',
|
|
47
|
+
stderr: 'inherit',
|
|
48
|
+
stdin: 'inherit',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Handle signals to ensure entire process tree is killed
|
|
52
|
+
const cleanup = () => {
|
|
53
|
+
if (devServer.pid) {
|
|
54
|
+
try {
|
|
55
|
+
// Kill the process group (negative PID kills entire group)
|
|
56
|
+
process.kill(-devServer.pid, 'SIGTERM');
|
|
57
|
+
} catch {
|
|
58
|
+
// Fallback: kill the direct process
|
|
59
|
+
try {
|
|
60
|
+
devServer.kill();
|
|
61
|
+
} catch {
|
|
62
|
+
// Ignore if already dead
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
process.exit(0);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
process.on('SIGINT', cleanup);
|
|
70
|
+
process.on('SIGTERM', cleanup);
|
|
71
|
+
|
|
72
|
+
const exitCode = await devServer.exited;
|
|
73
|
+
process.exit(exitCode);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (error instanceof Error) {
|
|
76
|
+
tui.error(`Dev server failed: ${error.message}`);
|
|
77
|
+
} else {
|
|
78
|
+
tui.error('Dev server failed');
|
|
79
|
+
}
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
export const createUserSubcommand = createSubcommand({
|
|
5
|
+
name: 'create-user',
|
|
6
|
+
description: 'Example: Create a user with validation (requires authentication)',
|
|
7
|
+
requiresAuth: true,
|
|
8
|
+
schema: {
|
|
9
|
+
args: z.object({
|
|
10
|
+
username: z.string().min(3).max(20).describe('Username for the new user'),
|
|
11
|
+
}),
|
|
12
|
+
options: z.object({
|
|
13
|
+
email: z.string().email().optional().describe('Email address'),
|
|
14
|
+
admin: z.boolean().optional().describe('Grant admin privileges'),
|
|
15
|
+
age: z.number().int().positive().optional().describe('User age'),
|
|
16
|
+
}),
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
async handler(ctx) {
|
|
20
|
+
// ctx.args and ctx.opts are fully typed - NO CASTING NEEDED!
|
|
21
|
+
const { logger, auth, args, opts } = ctx;
|
|
22
|
+
|
|
23
|
+
logger.info(`Creating user: ${args.username}`);
|
|
24
|
+
logger.debug(`Authenticated as: ${auth.userId}`);
|
|
25
|
+
|
|
26
|
+
if (opts.email) {
|
|
27
|
+
logger.info(`Email: ${opts.email}`);
|
|
28
|
+
}
|
|
29
|
+
if (opts.admin) {
|
|
30
|
+
logger.info('Admin privileges: enabled');
|
|
31
|
+
}
|
|
32
|
+
if (opts.age) {
|
|
33
|
+
logger.info(`Age: ${opts.age}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
logger.info('User created successfully!');
|
|
37
|
+
},
|
|
38
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
export const createCommand = createSubcommand({
|
|
5
|
+
name: 'create',
|
|
6
|
+
description: 'Create a new example',
|
|
7
|
+
schema: {
|
|
8
|
+
args: z.object({
|
|
9
|
+
name: z.string().describe('Name of the example'),
|
|
10
|
+
}),
|
|
11
|
+
options: z.object({
|
|
12
|
+
type: z.string().optional().describe('Example type'),
|
|
13
|
+
force: z.boolean().optional().describe('Force creation'),
|
|
14
|
+
}),
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
async handler(ctx) {
|
|
18
|
+
const { logger, args, opts } = ctx;
|
|
19
|
+
|
|
20
|
+
logger.trace('Starting create command...');
|
|
21
|
+
logger.debug(`Type: ${opts.type || 'default'}`);
|
|
22
|
+
logger.debug(`Force: ${opts.force || false}`);
|
|
23
|
+
logger.info(`Creating example: ${args.name}`);
|
|
24
|
+
|
|
25
|
+
if (opts.force) {
|
|
26
|
+
logger.warn('Force mode enabled');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
logger.info('Example created successfully!');
|
|
30
|
+
},
|
|
31
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
export const deploySubcommand = createSubcommand({
|
|
5
|
+
name: 'deploy',
|
|
6
|
+
description: 'Example: Deploy to an environment (requires authentication)',
|
|
7
|
+
requiresAuth: true,
|
|
8
|
+
schema: {
|
|
9
|
+
args: z.object({
|
|
10
|
+
environment: z.string().describe('Target environment (dev/staging/prod)'),
|
|
11
|
+
}),
|
|
12
|
+
options: z.object({
|
|
13
|
+
force: z.boolean().optional().describe('Force deployment without confirmation'),
|
|
14
|
+
dryRun: z.boolean().optional().describe('Preview changes without deploying'),
|
|
15
|
+
}),
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
async handler(ctx) {
|
|
19
|
+
// ctx.args and ctx.opts are fully typed - NO CASTING NEEDED!
|
|
20
|
+
const { logger, auth, args, opts } = ctx;
|
|
21
|
+
|
|
22
|
+
logger.info(`Deploying to: ${args.environment}`);
|
|
23
|
+
logger.debug(`Using API key: ${auth.apiKey.substring(0, 8)}...`);
|
|
24
|
+
|
|
25
|
+
if (opts.dryRun) {
|
|
26
|
+
logger.info('Dry run mode - no changes made');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (opts.force) {
|
|
31
|
+
logger.info('Force deployment enabled');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
logger.info('Deployment complete');
|
|
35
|
+
},
|
|
36
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createCommand } from '@/types';
|
|
2
|
+
import { createCommand as createSubCmd } from './create';
|
|
3
|
+
import { listSubcommand } from './list';
|
|
4
|
+
import { stepsSubcommand } from './steps';
|
|
5
|
+
import { spinnerSubcommand } from './spinner';
|
|
6
|
+
import { deploySubcommand } from './deploy';
|
|
7
|
+
import { versionSubcommand } from './version';
|
|
8
|
+
import { createUserSubcommand } from './create-user';
|
|
9
|
+
import { runCommandSubcommand } from './run-command';
|
|
10
|
+
import { soundSubcommand } from './sound';
|
|
11
|
+
|
|
12
|
+
export const command = createCommand({
|
|
13
|
+
name: 'example',
|
|
14
|
+
description: 'Example command with subcommands',
|
|
15
|
+
hidden: true,
|
|
16
|
+
subcommands: [
|
|
17
|
+
createSubCmd,
|
|
18
|
+
listSubcommand,
|
|
19
|
+
stepsSubcommand,
|
|
20
|
+
spinnerSubcommand,
|
|
21
|
+
deploySubcommand,
|
|
22
|
+
versionSubcommand,
|
|
23
|
+
createUserSubcommand,
|
|
24
|
+
runCommandSubcommand,
|
|
25
|
+
soundSubcommand,
|
|
26
|
+
],
|
|
27
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
export const listSubcommand = createSubcommand({
|
|
5
|
+
name: 'list',
|
|
6
|
+
description: 'List examples',
|
|
7
|
+
schema: {
|
|
8
|
+
options: z.object({
|
|
9
|
+
all: z.boolean().optional().describe('Show all examples'),
|
|
10
|
+
json: z.boolean().optional().describe('Output as JSON'),
|
|
11
|
+
}),
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
async handler(ctx) {
|
|
15
|
+
const { logger, opts } = ctx;
|
|
16
|
+
|
|
17
|
+
logger.trace('Starting list command...');
|
|
18
|
+
logger.debug(`All: ${opts.all || false}`);
|
|
19
|
+
logger.debug(`JSON: ${opts.json || false}`);
|
|
20
|
+
|
|
21
|
+
const examples = ['example1', 'example2', 'example3'];
|
|
22
|
+
|
|
23
|
+
if (opts.json) {
|
|
24
|
+
console.log(JSON.stringify(examples));
|
|
25
|
+
} else {
|
|
26
|
+
logger.info('Examples:');
|
|
27
|
+
for (const example of examples) {
|
|
28
|
+
logger.info(` - ${example}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import * as tui from '@/tui';
|
|
3
|
+
|
|
4
|
+
export const runCommandSubcommand = createSubcommand({
|
|
5
|
+
name: 'run-command',
|
|
6
|
+
description: 'Demo of runCommand TUI component',
|
|
7
|
+
|
|
8
|
+
async handler() {
|
|
9
|
+
tui.info('Example 1: Running a successful command\n');
|
|
10
|
+
await tui.runCommand({
|
|
11
|
+
command: 'echo "Hello, World!"',
|
|
12
|
+
cmd: ['echo', 'Hello, World!'],
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
tui.newline();
|
|
16
|
+
tui.info('Example 2: Command with multiple output lines\n');
|
|
17
|
+
await tui.runCommand({
|
|
18
|
+
command: 'ls -la',
|
|
19
|
+
cmd: ['ls', '-la'],
|
|
20
|
+
cwd: process.cwd(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
tui.newline();
|
|
24
|
+
tui.info('Example 3: Long running command\n');
|
|
25
|
+
await tui.runCommand({
|
|
26
|
+
command: 'for i in {1..100}; do echo "Line $i"; sleep 0.01; done',
|
|
27
|
+
cmd: ['sh', '-c', 'for i in {1..100}; do echo "Line $i"; sleep 0.01; done'],
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
tui.newline();
|
|
31
|
+
tui.info('Example 4: Failing command\n');
|
|
32
|
+
const exitCode = await tui.runCommand({
|
|
33
|
+
command: 'exit 1',
|
|
34
|
+
cmd: ['sh', '-c', 'echo "This will fail"; exit 1'],
|
|
35
|
+
});
|
|
36
|
+
tui.info(`Exit code: ${exitCode}`);
|
|
37
|
+
|
|
38
|
+
tui.newline();
|
|
39
|
+
tui.info('Example 5: Tree\n');
|
|
40
|
+
await tui.runCommand({
|
|
41
|
+
command: 'tree',
|
|
42
|
+
cmd: ['tree'],
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
});
|