@agentuity/cli 0.0.72 → 0.0.74
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/bin/cli.ts +19 -5
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +13 -9
- package/dist/auth.js.map +1 -1
- package/dist/banner.js +1 -1
- package/dist/banner.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +79 -21
- package/dist/cli.js.map +1 -1
- package/dist/cmd/ai/prompt/api.d.ts.map +1 -1
- package/dist/cmd/ai/prompt/api.js +5 -4
- package/dist/cmd/ai/prompt/api.js.map +1 -1
- package/dist/cmd/auth/api.d.ts +2 -2
- package/dist/cmd/auth/api.d.ts.map +1 -1
- package/dist/cmd/auth/api.js +15 -14
- package/dist/cmd/auth/api.js.map +1 -1
- package/dist/cmd/auth/login.d.ts.map +1 -1
- package/dist/cmd/auth/login.js +37 -16
- package/dist/cmd/auth/login.js.map +1 -1
- package/dist/cmd/auth/ssh/api.d.ts.map +1 -1
- package/dist/cmd/auth/ssh/api.js +3 -2
- package/dist/cmd/auth/ssh/api.js.map +1 -1
- package/dist/cmd/build/ast.d.ts.map +1 -1
- package/dist/cmd/build/ast.js +76 -14
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/bundler.d.ts +3 -1
- package/dist/cmd/build/bundler.d.ts.map +1 -1
- package/dist/cmd/build/bundler.js +21 -9
- package/dist/cmd/build/bundler.js.map +1 -1
- package/dist/cmd/build/format-schema.d.ts +6 -0
- package/dist/cmd/build/format-schema.d.ts.map +1 -0
- package/dist/cmd/build/format-schema.js +60 -0
- package/dist/cmd/build/format-schema.js.map +1 -0
- package/dist/cmd/build/index.d.ts.map +1 -1
- package/dist/cmd/build/index.js +13 -0
- package/dist/cmd/build/index.js.map +1 -1
- package/dist/cmd/build/plugin.d.ts.map +1 -1
- package/dist/cmd/build/plugin.js +123 -32
- package/dist/cmd/build/plugin.js.map +1 -1
- package/dist/cmd/build/route-discovery.d.ts +50 -0
- package/dist/cmd/build/route-discovery.d.ts.map +1 -0
- package/dist/cmd/build/route-discovery.js +143 -0
- package/dist/cmd/build/route-discovery.js.map +1 -0
- package/dist/cmd/build/route-registry.d.ts.map +1 -1
- package/dist/cmd/build/route-registry.js +25 -10
- package/dist/cmd/build/route-registry.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +8 -6
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
- package/dist/cmd/cloud/deployment/show.js +34 -10
- package/dist/cmd/cloud/deployment/show.js.map +1 -1
- package/dist/cmd/dev/agents.d.ts.map +1 -1
- package/dist/cmd/dev/agents.js +2 -2
- package/dist/cmd/dev/agents.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +21 -0
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/dev/sync.d.ts.map +1 -1
- package/dist/cmd/dev/sync.js +2 -2
- package/dist/cmd/dev/sync.js.map +1 -1
- package/dist/cmd/project/download.d.ts.map +1 -1
- package/dist/cmd/project/download.js +16 -2
- package/dist/cmd/project/download.js.map +1 -1
- package/dist/cmd/project/list.d.ts.map +1 -1
- package/dist/cmd/project/list.js +2 -10
- package/dist/cmd/project/list.js.map +1 -1
- package/dist/cmd/project/show.d.ts.map +1 -1
- package/dist/cmd/project/show.js +8 -7
- package/dist/cmd/project/show.js.map +1 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +14 -2
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/steps.d.ts +20 -30
- package/dist/steps.d.ts.map +1 -1
- package/dist/steps.js +339 -184
- package/dist/steps.js.map +1 -1
- package/dist/tui/box.d.ts.map +1 -1
- package/dist/tui/box.js +8 -4
- package/dist/tui/box.js.map +1 -1
- package/dist/tui/prompt.d.ts.map +1 -1
- package/dist/tui/prompt.js +7 -2
- package/dist/tui/prompt.js.map +1 -1
- package/dist/tui.d.ts +20 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +90 -18
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/auth.ts +13 -10
- package/src/banner.ts +1 -1
- package/src/cli.ts +89 -27
- package/src/cmd/ai/prompt/api.ts +5 -4
- package/src/cmd/auth/api.ts +20 -22
- package/src/cmd/auth/login.ts +36 -17
- package/src/cmd/auth/ssh/api.ts +5 -9
- package/src/cmd/build/ast.ts +88 -14
- package/src/cmd/build/bundler.ts +32 -11
- package/src/cmd/build/format-schema.ts +66 -0
- package/src/cmd/build/index.ts +14 -0
- package/src/cmd/build/plugin.ts +146 -36
- package/src/cmd/build/route-discovery.ts +197 -0
- package/src/cmd/build/route-registry.ts +26 -10
- package/src/cmd/cloud/deploy.ts +19 -6
- package/src/cmd/cloud/deployment/show.ts +42 -10
- package/src/cmd/dev/agents.ts +2 -10
- package/src/cmd/dev/index.ts +25 -0
- package/src/cmd/dev/sync.ts +2 -12
- package/src/cmd/project/download.ts +16 -2
- package/src/cmd/project/list.ts +2 -9
- package/src/cmd/project/show.ts +8 -6
- package/src/cmd/project/template-flow.ts +21 -2
- package/src/config.ts +10 -0
- package/src/index.ts +2 -2
- package/src/steps.ts +397 -229
- package/src/tui/box.ts +8 -4
- package/src/tui/prompt.ts +7 -4
- package/src/tui.ts +125 -20
package/src/cmd/auth/api.ts
CHANGED
|
@@ -4,11 +4,11 @@ import type { APIClient } from '../../api';
|
|
|
4
4
|
import { StructuredError } from '@agentuity/core';
|
|
5
5
|
|
|
6
6
|
// Zod schemas for API validation
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const CodeStartDataSchema = z.object({
|
|
8
|
+
code: z.string(),
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const CodeCompleteDataSchema = z.object({
|
|
12
12
|
apiKey: z.string(),
|
|
13
13
|
userId: z.string(),
|
|
14
14
|
expires: z.number(),
|
|
@@ -20,8 +20,8 @@ const SignupCompleteDataSchema = z.object({
|
|
|
20
20
|
expiresAt: z.number(),
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
const
|
|
24
|
-
|
|
23
|
+
const CodeCheckRequestSchema = z.object({
|
|
24
|
+
code: z.string(),
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
// Exported result types
|
|
@@ -37,23 +37,23 @@ export interface SignupResult {
|
|
|
37
37
|
expires: Date;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const
|
|
41
|
-
'
|
|
42
|
-
'Error generating the
|
|
40
|
+
const CodeGenerationError = StructuredError(
|
|
41
|
+
'CodeGenerationError',
|
|
42
|
+
'Error generating the login code'
|
|
43
43
|
);
|
|
44
44
|
|
|
45
|
-
export async function
|
|
46
|
-
const resp = await apiClient.get('/cli/auth/start', APIResponseSchema(
|
|
45
|
+
export async function generateLoginCode(apiClient: APIClient): Promise<string> {
|
|
46
|
+
const resp = await apiClient.get('/cli/auth/start', APIResponseSchema(CodeStartDataSchema));
|
|
47
47
|
|
|
48
48
|
if (!resp.success) {
|
|
49
|
-
throw new
|
|
49
|
+
throw new CodeGenerationError();
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
if (!resp.data) {
|
|
53
|
-
throw new
|
|
53
|
+
throw new CodeGenerationError();
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
return resp.data.
|
|
56
|
+
return resp.data.code;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const PollForLoginError = StructuredError('PollForLoginError');
|
|
@@ -64,18 +64,17 @@ const PollForLoginTimeout = StructuredError(
|
|
|
64
64
|
|
|
65
65
|
export async function pollForLoginCompletion(
|
|
66
66
|
apiClient: APIClient,
|
|
67
|
-
|
|
68
|
-
timeoutMs =
|
|
67
|
+
code: string,
|
|
68
|
+
timeoutMs = 300000 // 5 minutes
|
|
69
69
|
): Promise<LoginResult> {
|
|
70
70
|
const started = Date.now();
|
|
71
71
|
|
|
72
72
|
while (Date.now() - started < timeoutMs) {
|
|
73
|
-
const resp = await apiClient.
|
|
74
|
-
'POST',
|
|
73
|
+
const resp = await apiClient.post(
|
|
75
74
|
'/cli/auth/check',
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
{ code },
|
|
76
|
+
APIResponseSchemaOptionalData(CodeCompleteDataSchema),
|
|
77
|
+
CodeCheckRequestSchema
|
|
79
78
|
);
|
|
80
79
|
|
|
81
80
|
if (!resp.success) {
|
|
@@ -126,8 +125,7 @@ export async function pollForSignupCompletion(
|
|
|
126
125
|
|
|
127
126
|
while (Date.now() - started < timeoutMs) {
|
|
128
127
|
try {
|
|
129
|
-
const resp = await apiClient.
|
|
130
|
-
'GET',
|
|
128
|
+
const resp = await apiClient.get(
|
|
131
129
|
`/cli/auth/signup/${otp}`,
|
|
132
130
|
APIResponseSchema(SignupCompleteDataSchema)
|
|
133
131
|
);
|
package/src/cmd/auth/login.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createSubcommand } from '../../types';
|
|
2
2
|
import { getAppBaseURL } from '../../api';
|
|
3
3
|
import { saveAuth } from '../../config';
|
|
4
|
-
import {
|
|
4
|
+
import { generateLoginCode, pollForLoginCompletion } from './api';
|
|
5
5
|
import * as tui from '../../tui';
|
|
6
6
|
import { getCommand } from '../../command-prefix';
|
|
7
7
|
import { ErrorCode } from '../../errors';
|
|
@@ -23,41 +23,60 @@ export const loginCommand = createSubcommand({
|
|
|
23
23
|
const appUrl = getAppBaseURL(config);
|
|
24
24
|
|
|
25
25
|
try {
|
|
26
|
-
const
|
|
27
|
-
message: 'Generating login
|
|
26
|
+
const code = await tui.spinner({
|
|
27
|
+
message: 'Generating login code...',
|
|
28
28
|
clearOnSuccess: true,
|
|
29
29
|
callback: () => {
|
|
30
|
-
return
|
|
30
|
+
return generateLoginCode(apiClient);
|
|
31
31
|
},
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
if (!
|
|
34
|
+
if (!code) {
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const authURL = `${appUrl}/auth/cli`;
|
|
38
|
+
const authURL = `${appUrl}/auth/cli?code=${code}`;
|
|
39
39
|
|
|
40
|
-
const copied = await tui.copyToClipboard(
|
|
40
|
+
const copied = await tui.copyToClipboard(authURL);
|
|
41
41
|
|
|
42
|
+
tui.newline();
|
|
43
|
+
console.log(`Your login code: ${tui.bold(code)}`);
|
|
42
44
|
tui.newline();
|
|
43
45
|
if (copied) {
|
|
44
|
-
console.log(
|
|
46
|
+
console.log('Login URL copied to clipboard! Open it in your browser:');
|
|
45
47
|
} else {
|
|
46
|
-
console.log('
|
|
47
|
-
tui.newline();
|
|
48
|
-
console.log(` ${tui.bold(otp)}`);
|
|
48
|
+
console.log('Open this URL in your browser to approve the login:');
|
|
49
49
|
}
|
|
50
50
|
tui.newline();
|
|
51
|
-
console.log('Then open the URL in your browser and paste the code:');
|
|
52
|
-
tui.newline();
|
|
53
51
|
console.log(` ${tui.link(authURL)}`);
|
|
54
52
|
tui.newline();
|
|
55
|
-
console.log(tui.muted('
|
|
53
|
+
console.log(tui.muted('Press Enter to open in your browser, or Ctrl+C to cancel'));
|
|
56
54
|
tui.newline();
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
const result = await tui.spinner({
|
|
57
|
+
type: 'countdown',
|
|
58
|
+
message: 'Waiting for approval',
|
|
59
|
+
timeoutMs: 300000, // 5 minutes
|
|
60
|
+
clearOnSuccess: true,
|
|
61
|
+
onEnterPress: () => {
|
|
62
|
+
// Open URL in default browser
|
|
63
|
+
const platform = process.platform;
|
|
64
|
+
if (platform === 'win32') {
|
|
65
|
+
// Windows: use cmd.exe to invoke start (it's a shell builtin, not an executable)
|
|
66
|
+
// Empty string is required as the window title argument
|
|
67
|
+
Bun.spawn(['cmd', '/c', 'start', '', authURL], {
|
|
68
|
+
stdout: 'ignore',
|
|
69
|
+
stderr: 'ignore',
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
const command = platform === 'darwin' ? 'open' : 'xdg-open';
|
|
73
|
+
Bun.spawn([command, authURL], { stdout: 'ignore', stderr: 'ignore' });
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
callback: async () => {
|
|
77
|
+
return await pollForLoginCompletion(apiClient, code);
|
|
78
|
+
},
|
|
79
|
+
});
|
|
61
80
|
|
|
62
81
|
await saveAuth({
|
|
63
82
|
apiKey: result.apiKey,
|
package/src/cmd/auth/ssh/api.ts
CHANGED
|
@@ -58,11 +58,10 @@ const AddSSHKeyUnexpectedError = StructuredError(
|
|
|
58
58
|
);
|
|
59
59
|
|
|
60
60
|
export async function addSSHKey(apiClient: APIClient, publicKey: string): Promise<AddSSHKeyResult> {
|
|
61
|
-
const resp = await apiClient.
|
|
62
|
-
'POST',
|
|
61
|
+
const resp = await apiClient.post(
|
|
63
62
|
'/cli/auth/ssh-keys',
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
{ publicKey },
|
|
64
|
+
APIResponseSchema(AddSSHKeyResponseSchema)
|
|
66
65
|
);
|
|
67
66
|
|
|
68
67
|
if (!resp.success) {
|
|
@@ -79,11 +78,7 @@ export async function addSSHKey(apiClient: APIClient, publicKey: string): Promis
|
|
|
79
78
|
const ListSSHKeysError = StructuredError('ListSSHKeysError');
|
|
80
79
|
|
|
81
80
|
export async function listSSHKeys(apiClient: APIClient): Promise<SSHKey[]> {
|
|
82
|
-
const resp = await apiClient.
|
|
83
|
-
'GET',
|
|
84
|
-
'/cli/auth/ssh-keys',
|
|
85
|
-
APIResponseSchema(z.array(SSHKeySchema))
|
|
86
|
-
);
|
|
81
|
+
const resp = await apiClient.get('/cli/auth/ssh-keys', APIResponseSchema(z.array(SSHKeySchema)));
|
|
87
82
|
|
|
88
83
|
if (!resp.success) {
|
|
89
84
|
throw new ListSSHKeysError({ message: resp.message });
|
|
@@ -95,6 +90,7 @@ export async function listSSHKeys(apiClient: APIClient): Promise<SSHKey[]> {
|
|
|
95
90
|
const RemoveSSHKeysError = StructuredError('RemoveSSHKeysError');
|
|
96
91
|
|
|
97
92
|
export async function removeSSHKey(apiClient: APIClient, fingerprint: string): Promise<boolean> {
|
|
93
|
+
// NOTE: Using .request() here because DELETE with body is required by the API
|
|
98
94
|
const resp = await apiClient.request(
|
|
99
95
|
'DELETE',
|
|
100
96
|
'/cli/auth/ssh-keys',
|
package/src/cmd/build/ast.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as acornLoose from 'acorn-loose';
|
|
2
|
-
import {
|
|
2
|
+
import { dirname, relative } from 'node:path';
|
|
3
3
|
import { parse as parseCronExpression } from '@datasert/cronjs-parser';
|
|
4
4
|
import { generate } from 'astring';
|
|
5
5
|
import type { BuildMetadata } from '../../types';
|
|
@@ -10,6 +10,7 @@ import type { LogLevel } from '../../types';
|
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
12
12
|
import JSON5 from 'json5';
|
|
13
|
+
import { formatSchemaCode } from './format-schema';
|
|
13
14
|
|
|
14
15
|
const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL || 'info') as LogLevel);
|
|
15
16
|
|
|
@@ -213,11 +214,11 @@ function extractSchemaCode(callargexp: ASTObjectExpression): {
|
|
|
213
214
|
for (const prop of schemaObj.properties) {
|
|
214
215
|
if (prop.key.type === 'Identifier') {
|
|
215
216
|
if (prop.key.name === 'input' && prop.value) {
|
|
216
|
-
// Generate source code from AST node
|
|
217
|
-
inputSchemaCode = generate(prop.value);
|
|
217
|
+
// Generate source code from AST node and format it
|
|
218
|
+
inputSchemaCode = formatSchemaCode(generate(prop.value));
|
|
218
219
|
} else if (prop.key.name === 'output' && prop.value) {
|
|
219
|
-
// Generate source code from AST node
|
|
220
|
-
outputSchemaCode = generate(prop.value);
|
|
220
|
+
// Generate source code from AST node and format it
|
|
221
|
+
outputSchemaCode = formatSchemaCode(generate(prop.value));
|
|
221
222
|
}
|
|
222
223
|
}
|
|
223
224
|
}
|
|
@@ -524,7 +525,7 @@ export async function parseAgentMetadata(
|
|
|
524
525
|
});
|
|
525
526
|
let exportName: string | undefined;
|
|
526
527
|
const rel = relative(rootDir, filename);
|
|
527
|
-
|
|
528
|
+
let name: string | undefined; // Will be set from createAgent identifier
|
|
528
529
|
const version = hash(contents);
|
|
529
530
|
const id = getAgentId(projectId, deploymentId, rel, version);
|
|
530
531
|
|
|
@@ -560,6 +561,9 @@ export async function parseAgentMetadata(
|
|
|
560
561
|
);
|
|
561
562
|
}
|
|
562
563
|
|
|
564
|
+
// Extract agent identifier from createAgent first argument
|
|
565
|
+
name = nameArg.value;
|
|
566
|
+
|
|
563
567
|
const callargexp = configArg;
|
|
564
568
|
|
|
565
569
|
// Extract schema code before processing metadata
|
|
@@ -588,7 +592,7 @@ export async function parseAgentMetadata(
|
|
|
588
592
|
break;
|
|
589
593
|
}
|
|
590
594
|
}
|
|
591
|
-
if (!result) {
|
|
595
|
+
if (!result && name) {
|
|
592
596
|
result = createAgentMetadataNode(
|
|
593
597
|
id,
|
|
594
598
|
name,
|
|
@@ -656,6 +660,9 @@ export async function parseAgentMetadata(
|
|
|
656
660
|
);
|
|
657
661
|
}
|
|
658
662
|
|
|
663
|
+
// Extract agent identifier from createAgent first argument
|
|
664
|
+
name = nameArg.value;
|
|
665
|
+
|
|
659
666
|
const callargexp = configArg;
|
|
660
667
|
|
|
661
668
|
// Extract schema code before processing metadata
|
|
@@ -684,7 +691,7 @@ export async function parseAgentMetadata(
|
|
|
684
691
|
break;
|
|
685
692
|
}
|
|
686
693
|
}
|
|
687
|
-
if (!result) {
|
|
694
|
+
if (!result && name) {
|
|
688
695
|
result = createAgentMetadataNode(
|
|
689
696
|
id,
|
|
690
697
|
name,
|
|
@@ -782,6 +789,8 @@ function hasValidatorCall(args: unknown[]): ValidatorInfo {
|
|
|
782
789
|
// Check if this is a CallExpression with callee named 'validator'
|
|
783
790
|
if (node.type === 'CallExpression') {
|
|
784
791
|
const callExpr = node as ASTCallExpression;
|
|
792
|
+
|
|
793
|
+
// Check for standalone validator({ input, output })
|
|
785
794
|
if (callExpr.callee.type === 'Identifier') {
|
|
786
795
|
const identifier = callExpr.callee as ASTNodeIdentifier;
|
|
787
796
|
if (identifier.name === 'validator') {
|
|
@@ -789,7 +798,13 @@ function hasValidatorCall(args: unknown[]): ValidatorInfo {
|
|
|
789
798
|
const schemas = extractValidatorSchemas(callExpr);
|
|
790
799
|
return { hasValidator: true, ...schemas };
|
|
791
800
|
}
|
|
801
|
+
// Check for zValidator('json', schema)
|
|
802
|
+
if (identifier.name === 'zValidator') {
|
|
803
|
+
const schemas = extractZValidatorSchema(callExpr);
|
|
804
|
+
return { hasValidator: true, ...schemas };
|
|
805
|
+
}
|
|
792
806
|
}
|
|
807
|
+
|
|
793
808
|
// Check for agent.validator()
|
|
794
809
|
if (callExpr.callee.type === 'MemberExpression') {
|
|
795
810
|
const member = callExpr.callee as ASTMemberExpression;
|
|
@@ -799,7 +814,9 @@ function hasValidatorCall(args: unknown[]): ValidatorInfo {
|
|
|
799
814
|
member.object.type === 'Identifier'
|
|
800
815
|
? (member.object as ASTNodeIdentifier).name
|
|
801
816
|
: undefined;
|
|
802
|
-
|
|
817
|
+
// Also check for schema overrides: agent.validator({ input, output })
|
|
818
|
+
const schemas = extractValidatorSchemas(callExpr);
|
|
819
|
+
return { hasValidator: true, agentVariable, ...schemas };
|
|
803
820
|
}
|
|
804
821
|
}
|
|
805
822
|
}
|
|
@@ -845,14 +862,59 @@ function extractValidatorSchemas(callExpr: ASTCallExpression): {
|
|
|
845
862
|
return result;
|
|
846
863
|
}
|
|
847
864
|
|
|
865
|
+
/**
|
|
866
|
+
* Extract schema from zValidator() call arguments
|
|
867
|
+
* Example: zValidator('json', mySchema) or zValidator('json', z.object({...}))
|
|
868
|
+
* Returns the schema as inputSchemaVariable since zValidator is for request body validation
|
|
869
|
+
* Only extracts schemas for 'json' target, not 'query', 'param', 'header', or 'cookie'
|
|
870
|
+
*/
|
|
871
|
+
function extractZValidatorSchema(callExpr: ASTCallExpression): {
|
|
872
|
+
inputSchemaVariable?: string;
|
|
873
|
+
} {
|
|
874
|
+
const result: { inputSchemaVariable?: string } = {};
|
|
875
|
+
|
|
876
|
+
// zValidator requires at least 2 arguments: zValidator(target, schema)
|
|
877
|
+
if (!callExpr.arguments || callExpr.arguments.length < 2) {
|
|
878
|
+
return result;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// First argument should be 'json' literal
|
|
882
|
+
const targetArg = callExpr.arguments[0] as ASTNode;
|
|
883
|
+
if (targetArg.type === 'Literal') {
|
|
884
|
+
const targetValue = (targetArg as ASTLiteral).value;
|
|
885
|
+
// Only extract schemas for JSON body validation
|
|
886
|
+
if (targetValue !== 'json') {
|
|
887
|
+
return result;
|
|
888
|
+
}
|
|
889
|
+
} else {
|
|
890
|
+
// If first arg is not a literal, we can't determine the target, skip
|
|
891
|
+
return result;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// Second argument is the schema
|
|
895
|
+
const schemaArg = callExpr.arguments[1] as ASTNode;
|
|
896
|
+
|
|
897
|
+
// If it's an identifier (variable reference), extract the name
|
|
898
|
+
if (schemaArg.type === 'Identifier') {
|
|
899
|
+
result.inputSchemaVariable = (schemaArg as ASTNodeIdentifier).name;
|
|
900
|
+
}
|
|
901
|
+
// If it's inline schema (CallExpression like z.object({...})), we detect but don't extract yet
|
|
902
|
+
// TODO: Extract inline schema code
|
|
903
|
+
|
|
904
|
+
return result;
|
|
905
|
+
}
|
|
906
|
+
|
|
848
907
|
export async function parseRoute(
|
|
849
908
|
rootDir: string,
|
|
850
909
|
filename: string,
|
|
851
910
|
projectId: string,
|
|
852
911
|
deploymentId: string
|
|
853
912
|
): Promise<BuildMetadata['routes']> {
|
|
854
|
-
const
|
|
855
|
-
const version = hash(
|
|
913
|
+
const rawContents = await Bun.file(filename).text();
|
|
914
|
+
const version = hash(rawContents);
|
|
915
|
+
// Transpile TypeScript to JavaScript so acorn-loose can parse it properly
|
|
916
|
+
const transpiler = new Bun.Transpiler({ loader: 'ts', target: 'bun' });
|
|
917
|
+
const contents = transpiler.transformSync(rawContents);
|
|
856
918
|
const ast = acornLoose.parse(contents, {
|
|
857
919
|
locations: true,
|
|
858
920
|
ecmaVersion: 'latest',
|
|
@@ -933,12 +995,24 @@ export async function parseRoute(
|
|
|
933
995
|
}
|
|
934
996
|
|
|
935
997
|
const rel = relative(rootDir, filename);
|
|
936
|
-
const dir = dirname(filename);
|
|
937
|
-
const name = basename(dir);
|
|
938
998
|
|
|
939
999
|
// For src/api/index.ts, we don't want to add the folder name since it's the root API router
|
|
940
1000
|
const isRootApi = filename.includes('src/api/index.ts');
|
|
941
|
-
|
|
1001
|
+
|
|
1002
|
+
// For nested routes, use the full path from src/api/ instead of just the immediate parent
|
|
1003
|
+
// e.g., src/api/v1/users/route.ts -> routeName = "v1/users"
|
|
1004
|
+
// src/api/auth/route.ts -> routeName = "auth"
|
|
1005
|
+
// src/api/test.ts -> routeName = "" (file directly in src/api/)
|
|
1006
|
+
let routeName = '';
|
|
1007
|
+
if (!isRootApi) {
|
|
1008
|
+
const apiMatch = filename.match(/src\/api\/(.+?)\/[^/]+\.ts$/);
|
|
1009
|
+
if (apiMatch) {
|
|
1010
|
+
// File in subdirectory: src/api/auth/route.ts -> "auth"
|
|
1011
|
+
routeName = apiMatch[1];
|
|
1012
|
+
}
|
|
1013
|
+
// For files directly in src/api/ (e.g., test.ts), routeName stays empty
|
|
1014
|
+
// This prevents double /api prefix since these files often define full paths
|
|
1015
|
+
}
|
|
942
1016
|
|
|
943
1017
|
const routes: RouteDefinition = [];
|
|
944
1018
|
const routePrefix = '/api';
|
package/src/cmd/build/bundler.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { cpSync, existsSync, mkdirSync, rmSync, readdirSync } from 'node:fs';
|
|
|
4
4
|
import gitParseUrl from 'git-url-parse';
|
|
5
5
|
import { StructuredError } from '@agentuity/core';
|
|
6
6
|
import * as tui from '../../tui';
|
|
7
|
+
import { pauseStepUI } from '../../steps';
|
|
7
8
|
import AgentuityBundler, { getBuildMetadata } from './plugin';
|
|
8
9
|
import { getFilesRecursively } from './file';
|
|
9
10
|
import { getVersion } from '../../version';
|
|
@@ -16,25 +17,38 @@ import { type DeployOptions } from '../../schemas/deploy';
|
|
|
16
17
|
|
|
17
18
|
const minBunVersion = '>=1.3.3';
|
|
18
19
|
|
|
19
|
-
async function checkBunVersion() {
|
|
20
|
+
async function checkBunVersion(): Promise<string[]> {
|
|
20
21
|
if (semver.satisfies(Bun.version, minBunVersion)) {
|
|
21
|
-
return;
|
|
22
|
+
return []; // Version is OK, no output needed
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
|
|
25
|
+
const message = `Bun is using version ${Bun.version}. This project requires Bun version ${minBunVersion} to build.`;
|
|
26
|
+
|
|
27
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
28
|
+
// Pause the step UI for interactive prompt
|
|
29
|
+
const resume = pauseStepUI();
|
|
30
|
+
|
|
31
|
+
tui.warning(message);
|
|
32
|
+
const ok = await tui.confirm('Would you like to upgrade now?');
|
|
33
|
+
|
|
34
|
+
// Small delay to ensure console.log('') in confirm completes
|
|
35
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
36
|
+
|
|
37
|
+
resume(); // Resume step UI
|
|
38
|
+
|
|
27
39
|
if (ok) {
|
|
28
40
|
await $`bun upgrade`.quiet();
|
|
29
41
|
const version = (await $`bun -v`.quiet().text()).trim();
|
|
30
|
-
|
|
31
|
-
return;
|
|
42
|
+
// Return success message to show in output box
|
|
43
|
+
return [tui.colorSuccess(`Upgraded Bun to ${version}`)];
|
|
32
44
|
}
|
|
33
45
|
}
|
|
46
|
+
|
|
47
|
+
// Failed to upgrade or user declined
|
|
34
48
|
throw new InvalidBunVersion({
|
|
35
49
|
current: Bun.version,
|
|
36
50
|
required: minBunVersion,
|
|
37
|
-
message
|
|
51
|
+
message,
|
|
38
52
|
});
|
|
39
53
|
}
|
|
40
54
|
|
|
@@ -102,7 +116,9 @@ export async function bundle({
|
|
|
102
116
|
env,
|
|
103
117
|
region,
|
|
104
118
|
logger,
|
|
105
|
-
}: BundleOptions) {
|
|
119
|
+
}: BundleOptions): Promise<{ output: string[] }> {
|
|
120
|
+
const output: string[] = [];
|
|
121
|
+
|
|
106
122
|
const appFile = join(rootDir, 'app.ts');
|
|
107
123
|
if (!existsSync(appFile)) {
|
|
108
124
|
throw new AppFileNotFoundError({
|
|
@@ -110,7 +126,8 @@ export async function bundle({
|
|
|
110
126
|
});
|
|
111
127
|
}
|
|
112
128
|
|
|
113
|
-
await checkBunVersion();
|
|
129
|
+
const versionOutput = await checkBunVersion();
|
|
130
|
+
output.push(...versionOutput);
|
|
114
131
|
|
|
115
132
|
const outDir = customOutDir ?? join(rootDir, '.agentuity');
|
|
116
133
|
const srcDir = join(rootDir, 'src');
|
|
@@ -312,6 +329,8 @@ export async function bundle({
|
|
|
312
329
|
id: projectId ?? '',
|
|
313
330
|
name: pkgContents.name,
|
|
314
331
|
version: pkgContents.version,
|
|
332
|
+
description: pkgContents.description,
|
|
333
|
+
keywords: pkgContents.keywords,
|
|
315
334
|
orgId: orgId ?? '',
|
|
316
335
|
};
|
|
317
336
|
buildmetadata.deployment = {
|
|
@@ -713,4 +732,6 @@ export async function bundle({
|
|
|
713
732
|
`${outDir}/.routemapping.json`,
|
|
714
733
|
dev ? JSON.stringify(routeMapping, null, 2) : JSON.stringify(routeMapping)
|
|
715
734
|
);
|
|
735
|
+
|
|
736
|
+
return { output };
|
|
716
737
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple formatter for schema code strings.
|
|
3
|
+
* Adds basic indentation and line breaks for readability.
|
|
4
|
+
*/
|
|
5
|
+
export function formatSchemaCode(code: string): string {
|
|
6
|
+
if (!code) return code;
|
|
7
|
+
|
|
8
|
+
let indentLevel = 0;
|
|
9
|
+
const indentSize = 2;
|
|
10
|
+
const lines: string[] = [];
|
|
11
|
+
let currentLine = '';
|
|
12
|
+
|
|
13
|
+
for (let i = 0; i < code.length; i++) {
|
|
14
|
+
const char = code[i];
|
|
15
|
+
const nextChar = code[i + 1];
|
|
16
|
+
const prevChar = i > 0 ? code[i - 1] : '';
|
|
17
|
+
|
|
18
|
+
// Skip existing whitespace/newlines
|
|
19
|
+
if (char === '\n' || char === '\r' || (char === ' ' && prevChar === ' ')) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Handle opening braces
|
|
24
|
+
if (char === '{') {
|
|
25
|
+
currentLine += char;
|
|
26
|
+
lines.push(' '.repeat(indentLevel * indentSize) + currentLine.trim());
|
|
27
|
+
indentLevel++;
|
|
28
|
+
currentLine = '';
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Handle closing braces
|
|
33
|
+
if (char === '}') {
|
|
34
|
+
if (currentLine.trim()) {
|
|
35
|
+
lines.push(' '.repeat(indentLevel * indentSize) + currentLine.trim());
|
|
36
|
+
currentLine = '';
|
|
37
|
+
}
|
|
38
|
+
indentLevel--;
|
|
39
|
+
// Check if next char is closing paren - if so, put on same line
|
|
40
|
+
if (nextChar === ')') {
|
|
41
|
+
currentLine = '}';
|
|
42
|
+
} else {
|
|
43
|
+
lines.push(' '.repeat(indentLevel * indentSize) + char);
|
|
44
|
+
}
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Handle commas - add line break after
|
|
49
|
+
if (char === ',') {
|
|
50
|
+
currentLine += char;
|
|
51
|
+
lines.push(' '.repeat(indentLevel * indentSize) + currentLine.trim());
|
|
52
|
+
currentLine = '';
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Accumulate characters
|
|
57
|
+
currentLine += char;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Add any remaining content
|
|
61
|
+
if (currentLine.trim()) {
|
|
62
|
+
lines.push(' '.repeat(indentLevel * indentSize) + currentLine.trim());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return lines.join('\n');
|
|
66
|
+
}
|
package/src/cmd/build/index.ts
CHANGED
|
@@ -77,6 +77,20 @@ export const command = createCommand({
|
|
|
77
77
|
logger: ctx.logger,
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
+
// Copy profile-specific .env file AFTER bundling (bundler clears outDir first)
|
|
81
|
+
if (opts.dev && ctx.config?.name) {
|
|
82
|
+
const envSourcePath = join(absoluteProjectDir, `.env.${ctx.config.name}`);
|
|
83
|
+
const envDestPath = join(outDir, '.env');
|
|
84
|
+
|
|
85
|
+
const envFile = Bun.file(envSourcePath);
|
|
86
|
+
if (await envFile.exists()) {
|
|
87
|
+
await Bun.write(envDestPath, envFile);
|
|
88
|
+
ctx.logger.debug(`Copied ${envSourcePath} to ${envDestPath}`);
|
|
89
|
+
} else {
|
|
90
|
+
ctx.logger.debug(`No .env.${ctx.config.name} file found, skipping env copy`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
80
94
|
// Run TypeScript type checking after registry generation (skip in dev mode)
|
|
81
95
|
if (!opts.dev && !opts.skipTypeCheck) {
|
|
82
96
|
try {
|