@aixyz/cli 0.19.0 → 0.21.0
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/build/AixyzConfigPlugin.ts +6 -3
- package/build/AixyzServerPlugin.ts +71 -49
- package/build/index.ts +14 -10
- package/dev/index.ts +5 -3
- package/dev/worker.ts +20 -7
- package/package.json +3 -3
|
@@ -7,9 +7,7 @@ function label(text: string): string {
|
|
|
7
7
|
return chalk.dim(text.padEnd(14));
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
const materialized = getAixyzConfig();
|
|
12
|
-
|
|
10
|
+
function logConfig(materialized: ReturnType<typeof getAixyzConfig>): void {
|
|
13
11
|
const maxLen = Math.max(materialized.url.length, materialized.x402.payTo.length);
|
|
14
12
|
const description =
|
|
15
13
|
materialized.description.length > maxLen
|
|
@@ -33,6 +31,11 @@ export function AixyzConfigPlugin(): BunPlugin {
|
|
|
33
31
|
titleAlignment: "left",
|
|
34
32
|
}),
|
|
35
33
|
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function AixyzConfigPlugin(): BunPlugin {
|
|
37
|
+
const materialized = getAixyzConfig();
|
|
38
|
+
logConfig(materialized);
|
|
36
39
|
|
|
37
40
|
return {
|
|
38
41
|
name: "aixyz-config",
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { BunPlugin } from "bun";
|
|
2
|
-
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "fs";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs";
|
|
3
3
|
import { resolve, relative, join } from "path";
|
|
4
4
|
import { getAixyzConfig } from "@aixyz/config";
|
|
5
5
|
|
|
6
|
-
export function AixyzServerPlugin(
|
|
6
|
+
export function AixyzServerPlugin(
|
|
7
|
+
entrypoint: string,
|
|
8
|
+
mode: "vercel" | "standalone" | "executable",
|
|
9
|
+
isCustom = false,
|
|
10
|
+
): BunPlugin {
|
|
7
11
|
return {
|
|
8
12
|
name: "aixyz-entrypoint",
|
|
9
13
|
setup(build) {
|
|
@@ -12,44 +16,58 @@ export function AixyzServerPlugin(entrypoint: string, mode: "vercel" | "standalo
|
|
|
12
16
|
|
|
13
17
|
const source = await Bun.file(args.path).text();
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
// Custom server.ts manages its own lifecycle — pass through as-is
|
|
20
|
+
if (isCustom || mode === "vercel") {
|
|
21
|
+
return { contents: source, loader: "ts" };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// For generated entrypoints in standalone/executable, rewrite `export default ...` into Bun.serve().
|
|
25
|
+
// Supports both identifier exports (`export default app;`) and
|
|
26
|
+
// expression exports (`export default new AixyzApp({...});`).
|
|
27
|
+
const identifierRe = /export\s+default\s+(\w+)\s*;/;
|
|
28
|
+
const expressionRe = /export\s+default\s+/;
|
|
29
|
+
|
|
30
|
+
let transformed: string;
|
|
31
|
+
const identifierMatch = source.match(identifierRe);
|
|
32
|
+
if (identifierMatch) {
|
|
33
|
+
transformed = source.replace(
|
|
34
|
+
identifierRe,
|
|
35
|
+
`const __server = Bun.serve({ port: parseInt(process.env.PORT || "3000", 10), fetch: ${identifierMatch[1]}.fetch });\nconsole.log(\`Server listening on port \${__server.port}\`);`,
|
|
36
|
+
);
|
|
37
|
+
} else if (expressionRe.test(source)) {
|
|
38
|
+
transformed = source.replace(expressionRe, `const __app = `);
|
|
39
|
+
transformed += `\nconst __server = Bun.serve({ port: parseInt(process.env.PORT || "3000", 10), fetch: __app.fetch });\nconsole.log(\`Server listening on port \${__server.port}\`);`;
|
|
19
40
|
} else {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
`export default $1;
|
|
25
|
-
|
|
26
|
-
// Auto-start server when run directly
|
|
27
|
-
if (import.meta.main) {
|
|
28
|
-
const port = parseInt(process.env.PORT || "3000", 10);
|
|
29
|
-
$1.express.listen(port, () => {
|
|
30
|
-
console.log(\`Server listening on port \${port}\`);
|
|
31
|
-
});
|
|
32
|
-
}`,
|
|
41
|
+
throw new Error(
|
|
42
|
+
`[aixyz] Could not find \`export default\` in entrypoint ${args.path}. ` +
|
|
43
|
+
`Standalone and executable builds require the server entrypoint to use \`export default app;\` ` +
|
|
44
|
+
`or \`export default new AixyzApp({...});\`.`,
|
|
33
45
|
);
|
|
34
|
-
return { contents: transformed, loader: "ts" };
|
|
35
46
|
}
|
|
47
|
+
return { contents: transformed, loader: "ts" };
|
|
36
48
|
});
|
|
37
49
|
},
|
|
38
50
|
};
|
|
39
51
|
}
|
|
40
52
|
|
|
41
|
-
export
|
|
53
|
+
export type Entrypoint = { path: string; isCustom: boolean };
|
|
54
|
+
|
|
55
|
+
export function getEntrypointMayGenerate(cwd: string, appDirName: string, mode: "dev" | "build"): Entrypoint {
|
|
42
56
|
const appDir = resolve(cwd, appDirName);
|
|
57
|
+
const serverFile = resolve(appDir, "server.ts");
|
|
43
58
|
|
|
44
|
-
if (existsSync(
|
|
45
|
-
|
|
59
|
+
if (existsSync(serverFile)) {
|
|
60
|
+
const source = readFileSync(serverFile, "utf-8");
|
|
61
|
+
// assume that export default has `.fetch` typically `app`
|
|
62
|
+
const hasExportDefault = /export\s+default\s+/.test(source);
|
|
63
|
+
return { path: serverFile, isCustom: !hasExportDefault };
|
|
46
64
|
}
|
|
47
65
|
|
|
48
66
|
const devDir = resolve(cwd, join(".aixyz", mode));
|
|
49
67
|
mkdirSync(devDir, { recursive: true });
|
|
50
68
|
const entrypoint = resolve(devDir, "server.ts");
|
|
51
69
|
writeFileSync(entrypoint, generateServer(appDir, devDir));
|
|
52
|
-
return entrypoint;
|
|
70
|
+
return { path: entrypoint, isCustom: false };
|
|
53
71
|
}
|
|
54
72
|
|
|
55
73
|
class AixyzGlob {
|
|
@@ -109,7 +127,8 @@ function generateServer(appDir: string, entrypointDir: string): string {
|
|
|
109
127
|
const imports: string[] = [];
|
|
110
128
|
const body: string[] = [];
|
|
111
129
|
|
|
112
|
-
imports.push('import {
|
|
130
|
+
imports.push('import { AixyzApp } from "aixyz/app";');
|
|
131
|
+
imports.push('import { IndexPagePlugin } from "aixyz/app/plugins/index-page";');
|
|
113
132
|
|
|
114
133
|
const hasAccepts = existsSync(resolve(appDir, "accepts.ts"));
|
|
115
134
|
if (hasAccepts) {
|
|
@@ -119,16 +138,15 @@ function generateServer(appDir: string, entrypointDir: string): string {
|
|
|
119
138
|
}
|
|
120
139
|
|
|
121
140
|
const rootAgent = glob.hasRootAgent(appDir);
|
|
122
|
-
if (rootAgent) {
|
|
123
|
-
imports.push('import { useA2A } from "aixyz/server/adapters/a2a";');
|
|
124
|
-
imports.push(`import * as agent from "${importPrefix}/agent";`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
141
|
const agentsDir = resolve(appDir, "agents");
|
|
128
142
|
const subAgents = glob.getAgents(agentsDir);
|
|
143
|
+
const needsA2A = rootAgent || subAgents.length > 0;
|
|
129
144
|
|
|
130
|
-
if (
|
|
131
|
-
imports.push('import {
|
|
145
|
+
if (needsA2A) {
|
|
146
|
+
imports.push('import { A2APlugin } from "aixyz/app/plugins/a2a";');
|
|
147
|
+
}
|
|
148
|
+
if (rootAgent) {
|
|
149
|
+
imports.push(`import * as agent from "${importPrefix}/agent";`);
|
|
132
150
|
}
|
|
133
151
|
for (const subAgent of subAgents) {
|
|
134
152
|
imports.push(`import * as ${subAgent.identifier} from "${importPrefix}/agents/${subAgent.name}";`);
|
|
@@ -138,45 +156,49 @@ function generateServer(appDir: string, entrypointDir: string): string {
|
|
|
138
156
|
const tools = glob.getTools(toolsDir);
|
|
139
157
|
|
|
140
158
|
if (tools.length > 0) {
|
|
141
|
-
imports.push('import {
|
|
159
|
+
imports.push('import { MCPPlugin } from "aixyz/app/plugins/mcp";');
|
|
142
160
|
for (const tool of tools) {
|
|
143
161
|
imports.push(`import * as ${tool.identifier} from "${importPrefix}/tools/${tool.name}";`);
|
|
144
162
|
}
|
|
145
163
|
}
|
|
146
164
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
165
|
+
// If app/erc-8004.ts exists, auto-register ERC-8004 endpoint
|
|
166
|
+
const hasErc8004 = existsSync(resolve(appDir, "erc-8004.ts"));
|
|
167
|
+
if (hasErc8004) {
|
|
168
|
+
imports.push('import { ERC8004Plugin } from "aixyz/app/plugins/erc-8004";');
|
|
169
|
+
imports.push(`import * as erc8004 from "${importPrefix}/erc-8004";`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
body.push("const app = new AixyzApp({ facilitators: facilitator });");
|
|
173
|
+
body.push("await app.withPlugin(new IndexPagePlugin());");
|
|
150
174
|
|
|
151
175
|
if (rootAgent) {
|
|
152
|
-
body.push("
|
|
176
|
+
body.push("await app.withPlugin(new A2APlugin(agent));");
|
|
153
177
|
}
|
|
154
|
-
|
|
155
178
|
for (const subAgent of subAgents) {
|
|
156
|
-
body.push(`
|
|
179
|
+
body.push(`await app.withPlugin(new A2APlugin(${subAgent.identifier}, "${subAgent.name}"));`);
|
|
157
180
|
}
|
|
181
|
+
|
|
158
182
|
if (tools.length > 0) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
183
|
+
const toolEntries = tools.map((tool) => ` { name: "${tool.name}", exports: ${tool.identifier} },`);
|
|
184
|
+
body.push(`await app.withPlugin(new MCPPlugin([`);
|
|
185
|
+
for (const entry of toolEntries) {
|
|
186
|
+
body.push(entry);
|
|
162
187
|
}
|
|
163
|
-
body.push(
|
|
188
|
+
body.push(`]));`);
|
|
164
189
|
}
|
|
165
190
|
|
|
166
|
-
// If app/erc-8004.ts exists, auto-register ERC-8004 endpoint
|
|
167
|
-
const hasErc8004 = existsSync(resolve(appDir, "erc-8004.ts"));
|
|
168
191
|
if (hasErc8004) {
|
|
169
|
-
imports.push('import { useERC8004 } from "aixyz/server/adapters/erc-8004";');
|
|
170
|
-
imports.push(`import * as erc8004 from "${importPrefix}/erc-8004";`);
|
|
171
192
|
const a2aPaths: string[] = [];
|
|
172
193
|
if (rootAgent) a2aPaths.push("/.well-known/agent-card.json");
|
|
173
194
|
for (const subAgent of subAgents) a2aPaths.push(`/${subAgent.name}/.well-known/agent-card.json`);
|
|
174
195
|
body.push(
|
|
175
|
-
`
|
|
196
|
+
`await app.withPlugin(new ERC8004Plugin({ default: erc8004.default, options: { mcp: ${tools.length > 0}, a2a: ${JSON.stringify(a2aPaths)} } }));`,
|
|
176
197
|
);
|
|
177
198
|
}
|
|
178
199
|
|
|
179
|
-
body.push("
|
|
200
|
+
body.push("await app.initialize();");
|
|
201
|
+
body.push("export default app;");
|
|
180
202
|
|
|
181
203
|
return [...imports, "", ...body].join("\n");
|
|
182
204
|
}
|
package/build/index.ts
CHANGED
|
@@ -63,21 +63,21 @@ export async function action(options: BuildOptions = {}): Promise<void> {
|
|
|
63
63
|
const config = getAixyzConfig();
|
|
64
64
|
const target = options.output ?? config.build?.output ?? (process.env.VERCEL === "1" ? "vercel" : "standalone");
|
|
65
65
|
const appDir = options.appDir || "app";
|
|
66
|
-
const entrypoint = getEntrypointMayGenerate(cwd, appDir, "build");
|
|
66
|
+
const { path: entrypoint, isCustom } = getEntrypointMayGenerate(cwd, appDir, "build");
|
|
67
67
|
|
|
68
68
|
if (target === "vercel") {
|
|
69
69
|
console.log(chalk.cyan("▶") + " Building for " + chalk.bold("Vercel") + "...");
|
|
70
|
-
await buildVercel(entrypoint, config);
|
|
70
|
+
await buildVercel(entrypoint, config, isCustom);
|
|
71
71
|
} else if (target === "executable") {
|
|
72
72
|
console.log(chalk.cyan("▶") + " Building " + chalk.bold("Executable") + "...");
|
|
73
|
-
await buildExecutable(entrypoint);
|
|
73
|
+
await buildExecutable(entrypoint, isCustom);
|
|
74
74
|
} else {
|
|
75
75
|
console.log(chalk.cyan("▶") + " Building for " + chalk.bold("Standalone") + "...");
|
|
76
|
-
await buildBun(entrypoint);
|
|
76
|
+
await buildBun(entrypoint, isCustom);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
async function buildBun(entrypoint: string): Promise<void> {
|
|
80
|
+
async function buildBun(entrypoint: string, isCustom: boolean): Promise<void> {
|
|
81
81
|
const cwd = process.cwd();
|
|
82
82
|
|
|
83
83
|
const outputDir = resolve(cwd, ".aixyz/output");
|
|
@@ -96,7 +96,7 @@ async function buildBun(entrypoint: string): Promise<void> {
|
|
|
96
96
|
"process.env.NODE_ENV": JSON.stringify("production"),
|
|
97
97
|
"process.env.AIXYZ_ENV": JSON.stringify("production"),
|
|
98
98
|
},
|
|
99
|
-
plugins: [AixyzConfigPlugin(), AixyzServerPlugin(entrypoint, "standalone")],
|
|
99
|
+
plugins: [AixyzConfigPlugin(), AixyzServerPlugin(entrypoint, "standalone", isCustom)],
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
if (!result.success) {
|
|
@@ -136,7 +136,7 @@ async function buildBun(entrypoint: string): Promise<void> {
|
|
|
136
136
|
console.log("To run: bun .aixyz/output/server.js");
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
async function buildExecutable(entrypoint: string): Promise<void> {
|
|
139
|
+
async function buildExecutable(entrypoint: string, isCustom: boolean): Promise<void> {
|
|
140
140
|
const cwd = process.cwd();
|
|
141
141
|
|
|
142
142
|
const outputDir = resolve(cwd, ".aixyz/output");
|
|
@@ -156,7 +156,7 @@ async function buildExecutable(entrypoint: string): Promise<void> {
|
|
|
156
156
|
"process.env.NODE_ENV": JSON.stringify("production"),
|
|
157
157
|
"process.env.AIXYZ_ENV": JSON.stringify("production"),
|
|
158
158
|
},
|
|
159
|
-
plugins: [AixyzConfigPlugin(), AixyzServerPlugin(entrypoint, "executable")],
|
|
159
|
+
plugins: [AixyzConfigPlugin(), AixyzServerPlugin(entrypoint, "executable", isCustom)],
|
|
160
160
|
});
|
|
161
161
|
|
|
162
162
|
if (!result.success) {
|
|
@@ -192,7 +192,11 @@ async function buildExecutable(entrypoint: string): Promise<void> {
|
|
|
192
192
|
console.log("To run: ./.aixyz/output/server");
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
async function buildVercel(
|
|
195
|
+
async function buildVercel(
|
|
196
|
+
entrypoint: string,
|
|
197
|
+
config: ReturnType<typeof getAixyzConfig>,
|
|
198
|
+
isCustom: boolean,
|
|
199
|
+
): Promise<void> {
|
|
196
200
|
const cwd = process.cwd();
|
|
197
201
|
|
|
198
202
|
const outputDir = resolve(cwd, ".vercel/output");
|
|
@@ -213,7 +217,7 @@ async function buildVercel(entrypoint: string, config: ReturnType<typeof getAixy
|
|
|
213
217
|
"process.env.NODE_ENV": JSON.stringify("production"),
|
|
214
218
|
"process.env.AIXYZ_ENV": JSON.stringify("production"),
|
|
215
219
|
},
|
|
216
|
-
plugins: [AixyzConfigPlugin(), AixyzServerPlugin(entrypoint, "vercel")],
|
|
220
|
+
plugins: [AixyzConfigPlugin(), AixyzServerPlugin(entrypoint, "vercel", isCustom)],
|
|
217
221
|
});
|
|
218
222
|
|
|
219
223
|
if (!result.success) {
|
package/dev/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ import pkg from "../package.json";
|
|
|
8
8
|
|
|
9
9
|
export const devCommand = new Command("dev")
|
|
10
10
|
.description("Start a local development server")
|
|
11
|
-
.option("-p, --port <port>", "Port to listen on"
|
|
11
|
+
.option("-p, --port <port>", "Port to listen on")
|
|
12
12
|
.action(action);
|
|
13
13
|
|
|
14
14
|
type DevOptions = {
|
|
@@ -45,8 +45,10 @@ export async function action(options: DevOptions): Promise<void> {
|
|
|
45
45
|
let restarting = false;
|
|
46
46
|
|
|
47
47
|
function startServer() {
|
|
48
|
-
const endpoint = getEntrypointMayGenerate(cwd, appDir, "dev");
|
|
49
|
-
|
|
48
|
+
const { path: endpoint, isCustom } = getEntrypointMayGenerate(cwd, appDir, "dev");
|
|
49
|
+
const args = ["bun", workerPath, endpoint, port];
|
|
50
|
+
if (isCustom) args.push("custom");
|
|
51
|
+
child = Bun.spawn(args, {
|
|
50
52
|
cwd,
|
|
51
53
|
stdout: "inherit",
|
|
52
54
|
stderr: "inherit",
|
package/dev/worker.ts
CHANGED
|
@@ -3,26 +3,39 @@ import chalk from "chalk";
|
|
|
3
3
|
async function main() {
|
|
4
4
|
const entrypoint = process.argv[2];
|
|
5
5
|
const port = parseInt(process.argv[3], 10);
|
|
6
|
+
const isCustom = process.argv[4] === "custom";
|
|
6
7
|
|
|
7
8
|
if (!entrypoint || isNaN(port)) {
|
|
8
|
-
console.error("Usage: dev-worker <entrypoint> <port>");
|
|
9
|
+
console.error("Usage: dev-worker <entrypoint> <port> [custom]");
|
|
9
10
|
process.exit(1);
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
// Expose port so config.url fallback picks it up
|
|
14
|
+
process.env.PORT = String(port);
|
|
15
|
+
|
|
12
16
|
const startTime = performance.now();
|
|
13
17
|
const mod = await import(entrypoint);
|
|
18
|
+
|
|
19
|
+
if (isCustom) {
|
|
20
|
+
// Custom server.ts manages its own lifecycle (e.g. Express, Fastify)
|
|
21
|
+
const duration = Math.round(performance.now() - startTime);
|
|
22
|
+
console.log(chalk.blueBright("✓") + ` Ready in ${duration}ms`);
|
|
23
|
+
console.log("");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
14
27
|
const app = mod.default;
|
|
15
28
|
|
|
16
|
-
if (!app || typeof app.
|
|
29
|
+
if (!app || typeof app.fetch !== "function") {
|
|
17
30
|
console.error("Error: Entrypoint must default-export an AixyzApp");
|
|
18
31
|
process.exit(1);
|
|
19
32
|
}
|
|
20
33
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
const server = Bun.serve({ port, fetch: app.fetch });
|
|
35
|
+
|
|
36
|
+
const duration = Math.round(performance.now() - startTime);
|
|
37
|
+
console.log(chalk.blueBright("✓") + ` Ready in ${duration}ms`);
|
|
38
|
+
console.log("");
|
|
26
39
|
}
|
|
27
40
|
|
|
28
41
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aixyz/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Payment-native SDK for AI Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"bin.ts"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@aixyz/config": "0.
|
|
32
|
-
"@aixyz/erc-8004": "0.
|
|
31
|
+
"@aixyz/config": "0.21.0",
|
|
32
|
+
"@aixyz/erc-8004": "0.21.0",
|
|
33
33
|
"@inquirer/prompts": "^8.3.0",
|
|
34
34
|
"@next/env": "^16.1.6",
|
|
35
35
|
"boxen": "^8.0.1",
|