@agentuity/cli 0.0.71 → 0.0.73
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/cli.d.ts.map +1 -1
- package/dist/cli.js +77 -19
- package/dist/cli.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 +56 -8
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/bundler.d.ts +1 -2
- package/dist/cmd/build/bundler.d.ts.map +1 -1
- package/dist/cmd/build/bundler.js +30 -8
- 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 +72 -2
- package/dist/cmd/build/plugin.js.map +1 -1
- package/dist/cmd/cloud/db/create.js +2 -2
- package/dist/cmd/cloud/db/create.js.map +1 -1
- package/dist/cmd/cloud/db/delete.js +2 -2
- package/dist/cmd/cloud/db/delete.js.map +1 -1
- package/dist/cmd/cloud/db/get.js +2 -2
- package/dist/cmd/cloud/db/get.js.map +1 -1
- package/dist/cmd/cloud/db/list.js +2 -2
- package/dist/cmd/cloud/db/list.js.map +1 -1
- package/dist/cmd/cloud/db/logs.js +2 -2
- package/dist/cmd/cloud/db/logs.js.map +1 -1
- package/dist/cmd/cloud/db/sql.js +2 -2
- package/dist/cmd/cloud/db/sql.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +163 -24
- 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/cloud/session/get.js +2 -2
- package/dist/cmd/cloud/session/get.js.map +1 -1
- package/dist/cmd/cloud/session/list.js +2 -2
- package/dist/cmd/cloud/session/list.js.map +1 -1
- package/dist/cmd/cloud/storage/create.js +2 -2
- package/dist/cmd/cloud/storage/create.js.map +1 -1
- package/dist/cmd/cloud/storage/delete.js +2 -2
- package/dist/cmd/cloud/storage/delete.js.map +1 -1
- package/dist/cmd/cloud/storage/download.js +2 -2
- package/dist/cmd/cloud/storage/download.js.map +1 -1
- package/dist/cmd/cloud/storage/get.js +2 -2
- package/dist/cmd/cloud/storage/get.js.map +1 -1
- package/dist/cmd/cloud/storage/list.js +2 -2
- package/dist/cmd/cloud/storage/list.js.map +1 -1
- package/dist/cmd/cloud/storage/upload.js +2 -2
- package/dist/cmd/cloud/storage/upload.js.map +1 -1
- package/dist/cmd/cloud/thread/delete.js +2 -2
- package/dist/cmd/cloud/thread/delete.js.map +1 -1
- package/dist/cmd/cloud/thread/get.js +2 -2
- package/dist/cmd/cloud/thread/get.js.map +1 -1
- package/dist/cmd/cloud/thread/list.js +2 -2
- package/dist/cmd/cloud/thread/list.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/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/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 +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +17 -1
- package/dist/config.js.map +1 -1
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +5 -1
- package/dist/output.js.map +1 -1
- package/dist/tui.d.ts +46 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +217 -39
- 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/cli.ts +85 -25
- package/src/cmd/auth/api.ts +20 -26
- package/src/cmd/auth/login.ts +36 -17
- package/src/cmd/auth/ssh/api.ts +5 -6
- package/src/cmd/build/ast.ts +67 -8
- package/src/cmd/build/bundler.ts +37 -9
- package/src/cmd/build/format-schema.ts +66 -0
- package/src/cmd/build/index.ts +14 -0
- package/src/cmd/build/plugin.ts +86 -2
- package/src/cmd/cloud/db/create.ts +2 -2
- package/src/cmd/cloud/db/delete.ts +2 -2
- package/src/cmd/cloud/db/get.ts +2 -2
- package/src/cmd/cloud/db/list.ts +2 -2
- package/src/cmd/cloud/db/logs.ts +2 -2
- package/src/cmd/cloud/db/sql.ts +2 -2
- package/src/cmd/cloud/deploy.ts +187 -24
- package/src/cmd/cloud/deployment/show.ts +42 -10
- package/src/cmd/cloud/session/get.ts +2 -2
- package/src/cmd/cloud/session/list.ts +2 -2
- package/src/cmd/cloud/storage/create.ts +2 -2
- package/src/cmd/cloud/storage/delete.ts +2 -2
- package/src/cmd/cloud/storage/download.ts +2 -2
- package/src/cmd/cloud/storage/get.ts +2 -2
- package/src/cmd/cloud/storage/list.ts +2 -2
- package/src/cmd/cloud/storage/upload.ts +2 -2
- package/src/cmd/cloud/thread/delete.ts +2 -2
- package/src/cmd/cloud/thread/get.ts +2 -2
- package/src/cmd/cloud/thread/list.ts +2 -2
- package/src/cmd/dev/agents.ts +2 -4
- package/src/cmd/dev/sync.ts +6 -8
- package/src/cmd/project/show.ts +8 -6
- package/src/cmd/project/template-flow.ts +21 -2
- package/src/config.ts +19 -6
- package/src/output.ts +7 -1
- package/src/tui.ts +302 -41
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,8 +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',
|
|
81
|
+
const resp = await apiClient.get(
|
|
84
82
|
'/cli/auth/ssh-keys',
|
|
85
83
|
APIResponseSchema(z.array(SSHKeySchema))
|
|
86
84
|
);
|
|
@@ -95,6 +93,7 @@ export async function listSSHKeys(apiClient: APIClient): Promise<SSHKey[]> {
|
|
|
95
93
|
const RemoveSSHKeysError = StructuredError('RemoveSSHKeysError');
|
|
96
94
|
|
|
97
95
|
export async function removeSSHKey(apiClient: APIClient, fingerprint: string): Promise<boolean> {
|
|
96
|
+
// NOTE: Using .request() here because DELETE with body is required by the API
|
|
98
97
|
const resp = await apiClient.request(
|
|
99
98
|
'DELETE',
|
|
100
99
|
'/cli/auth/ssh-keys',
|
package/src/cmd/build/ast.ts
CHANGED
|
@@ -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,6 +862,48 @@ 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,
|
package/src/cmd/build/bundler.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $, semver } from 'bun';
|
|
2
2
|
import { join, relative, resolve, dirname, basename } from 'node:path';
|
|
3
3
|
import { cpSync, existsSync, mkdirSync, rmSync, readdirSync } from 'node:fs';
|
|
4
4
|
import gitParseUrl from 'git-url-parse';
|
|
5
|
+
import { StructuredError } from '@agentuity/core';
|
|
6
|
+
import * as tui from '../../tui';
|
|
5
7
|
import AgentuityBundler, { getBuildMetadata } from './plugin';
|
|
6
8
|
import { getFilesRecursively } from './file';
|
|
7
9
|
import { getVersion } from '../../version';
|
|
@@ -10,11 +12,31 @@ import { fixDuplicateExportsInDirectory } from './fix-duplicate-exports';
|
|
|
10
12
|
import type { Logger } from '../../types';
|
|
11
13
|
import { generateWorkbenchMainTsx, generateWorkbenchIndexHtml } from './workbench-templates';
|
|
12
14
|
import { analyzeWorkbench } from './ast';
|
|
13
|
-
import {
|
|
14
|
-
import { DeployOptionsSchema, type DeployOptions } from '../../schemas/deploy';
|
|
15
|
+
import { type DeployOptions } from '../../schemas/deploy';
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
const minBunVersion = '>=1.3.3';
|
|
18
|
+
|
|
19
|
+
async function checkBunVersion() {
|
|
20
|
+
if (semver.satisfies(Bun.version, minBunVersion)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const message = `The current Bun installed is using version ${Bun.version}. This project requires Bun version ${minBunVersion} to build.`;
|
|
24
|
+
tui.warning(message);
|
|
25
|
+
if (process.stdin.isTTY) {
|
|
26
|
+
const ok = await tui.confirm('You would you to upgrade now?');
|
|
27
|
+
if (ok) {
|
|
28
|
+
await $`bun upgrade`.quiet();
|
|
29
|
+
const version = (await $`bun -v`.quiet().text()).trim();
|
|
30
|
+
tui.success(`Bun upgraded to ${version}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw new InvalidBunVersion({
|
|
35
|
+
current: Bun.version,
|
|
36
|
+
required: minBunVersion,
|
|
37
|
+
message: `Please see https://bun.sh/ for information on how to download the latest version.`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
18
40
|
|
|
19
41
|
export interface BundleOptions extends DeployOptions {
|
|
20
42
|
rootDir: string;
|
|
@@ -36,6 +58,10 @@ type BuildLogs = BuildResult['logs'];
|
|
|
36
58
|
const AppFileNotFoundError = StructuredError('AppFileNotFoundError');
|
|
37
59
|
const AgentsDirNotFoundError = StructuredError('AgentsDirNotFoundError');
|
|
38
60
|
const BuildFailedError = StructuredError('BuildFailedError')<{ logs?: BuildLogs }>();
|
|
61
|
+
const InvalidBunVersion = StructuredError('InvalidBunVersion')<{
|
|
62
|
+
current: string;
|
|
63
|
+
required: string;
|
|
64
|
+
}>();
|
|
39
65
|
|
|
40
66
|
const handleBuildFailure = (buildResult: BuildResult) => {
|
|
41
67
|
// Collect all build errors with full details
|
|
@@ -83,6 +109,9 @@ export async function bundle({
|
|
|
83
109
|
message: `App file not found at expected location: ${appFile}`,
|
|
84
110
|
});
|
|
85
111
|
}
|
|
112
|
+
|
|
113
|
+
await checkBunVersion();
|
|
114
|
+
|
|
86
115
|
const outDir = customOutDir ?? join(rootDir, '.agentuity');
|
|
87
116
|
const srcDir = join(rootDir, 'src');
|
|
88
117
|
|
|
@@ -154,16 +183,13 @@ export async function bundle({
|
|
|
154
183
|
// Common externals for native modules (same as legacy CLI)
|
|
155
184
|
const commonExternals = ['bun', 'fsevents', 'chromium-bidi', 'sharp'];
|
|
156
185
|
|
|
157
|
-
// OpenTelemetry packages need to be externalized due to CommonJS/ESM hybrid issues
|
|
158
|
-
const otelExternals = ['@opentelemetry/*', '@traceloop/*'];
|
|
159
|
-
|
|
160
186
|
// Allow projects to specify custom externals via package.json "externals" field
|
|
161
187
|
const customExternals: string[] = [];
|
|
162
188
|
if (pkgContents.externals && Array.isArray(pkgContents.externals)) {
|
|
163
189
|
customExternals.push(...pkgContents.externals.filter((e: unknown) => typeof e === 'string'));
|
|
164
190
|
}
|
|
165
191
|
|
|
166
|
-
const externalPatterns = [...commonExternals, ...
|
|
192
|
+
const externalPatterns = [...commonExternals, ...customExternals];
|
|
167
193
|
|
|
168
194
|
// For production builds: install externals FIRST, then discover full dependency tree
|
|
169
195
|
// This prevents bundling dependencies that will be in node_modules anyway
|
|
@@ -286,6 +312,8 @@ export async function bundle({
|
|
|
286
312
|
id: projectId ?? '',
|
|
287
313
|
name: pkgContents.name,
|
|
288
314
|
version: pkgContents.version,
|
|
315
|
+
description: pkgContents.description,
|
|
316
|
+
keywords: pkgContents.keywords,
|
|
289
317
|
orgId: orgId ?? '',
|
|
290
318
|
};
|
|
291
319
|
buildmetadata.deployment = {
|
|
@@ -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 {
|
package/src/cmd/build/plugin.ts
CHANGED
|
@@ -371,10 +371,30 @@ import { readFileSync, existsSync } from 'node:fs';
|
|
|
371
371
|
}
|
|
372
372
|
}
|
|
373
373
|
const webstatic = serveStatic({ root: import.meta.dir + '/web' });
|
|
374
|
+
// In dev mode, serve from source; in prod, serve from build output
|
|
375
|
+
const publicRoot = ${isDevMode} ? ${JSON.stringify(join(srcDir, 'web', 'public'))} : import.meta.dir + '/web/public';
|
|
376
|
+
const publicstatic = serveStatic({ root: publicRoot, rewriteRequestPath: (path) => path });
|
|
374
377
|
router.get('/', (c) => c.html(index));
|
|
375
378
|
router.get('/web/chunk/*', webstatic);
|
|
376
379
|
router.get('/web/asset/*', webstatic);
|
|
377
|
-
|
|
380
|
+
// Serve public assets at root (e.g., /favicon.ico) - must be last
|
|
381
|
+
router.get('/*', async (c, next) => {
|
|
382
|
+
const path = c.req.path;
|
|
383
|
+
// Prevent directory traversal attacks
|
|
384
|
+
if (path.includes('..') || path.includes('%2e%2e')) {
|
|
385
|
+
return c.notFound();
|
|
386
|
+
}
|
|
387
|
+
// Only serve from public folder at root (skip /web/* routes and /)
|
|
388
|
+
if (path !== '/' && !path.startsWith('/web/')) {
|
|
389
|
+
try {
|
|
390
|
+
// serveStatic calls next() internally if file not found
|
|
391
|
+
return await publicstatic(c, next);
|
|
392
|
+
} catch (err) {
|
|
393
|
+
return next();
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return next();
|
|
397
|
+
});
|
|
378
398
|
})();`);
|
|
379
399
|
}
|
|
380
400
|
|
|
@@ -415,7 +435,11 @@ import { readFileSync, existsSync } from 'node:fs';
|
|
|
415
435
|
|
|
416
436
|
for (const subdir of subdirs) {
|
|
417
437
|
const fullPath = join(agentBaseDir, subdir);
|
|
418
|
-
if
|
|
438
|
+
// Check if this directory or any subdirectory contains agents
|
|
439
|
+
const hasAgentInTree = Array.from(agentDirs).some((agentDir) =>
|
|
440
|
+
agentDir.startsWith(fullPath)
|
|
441
|
+
);
|
|
442
|
+
if (!hasAgentInTree) {
|
|
419
443
|
throw new Error(
|
|
420
444
|
`Directory ${subdir} in src/agent must contain at least one agent (a file with a createAgent export)`
|
|
421
445
|
);
|
|
@@ -447,6 +471,56 @@ import { readFileSync, existsSync } from 'node:fs';
|
|
|
447
471
|
if (statSync(apiFile).isFile()) {
|
|
448
472
|
try {
|
|
449
473
|
const routes = await parseRoute(rootDir, apiFile, projectId, deploymentId);
|
|
474
|
+
|
|
475
|
+
// Extract schemas from agents for routes that use validators
|
|
476
|
+
for (const route of routes) {
|
|
477
|
+
// Check if route has custom schema overrides from validator({ input, output })
|
|
478
|
+
const hasCustomInput = route.config?.inputSchemaVariable;
|
|
479
|
+
const hasCustomOutput = route.config?.outputSchemaVariable;
|
|
480
|
+
|
|
481
|
+
// If route uses agent.validator(), get schemas from the agent (unless overridden)
|
|
482
|
+
if (
|
|
483
|
+
route.config?.agentImportPath &&
|
|
484
|
+
(!hasCustomInput || !hasCustomOutput)
|
|
485
|
+
) {
|
|
486
|
+
const agentImportPath = route.config.agentImportPath as string;
|
|
487
|
+
// Match by import path: @agent/zod-test -> src/agent/zod-test/agent.ts
|
|
488
|
+
// Normalize import path by removing leading '@' -> agent/zod-test
|
|
489
|
+
const importPattern = agentImportPath.replace(/^@/, '');
|
|
490
|
+
// Escape regex special characters for safe pattern matching
|
|
491
|
+
const escapedPattern = importPattern.replace(
|
|
492
|
+
/[.*+?^${}()|[\]\\]/g,
|
|
493
|
+
'\\$&'
|
|
494
|
+
);
|
|
495
|
+
// Match as complete path segment to avoid false positives (e.g., "agent/hello" matching "agent/hello-world")
|
|
496
|
+
const segmentPattern = new RegExp(`(^|/)${escapedPattern}(/|$)`);
|
|
497
|
+
|
|
498
|
+
for (const [, agentMd] of agentMetadata) {
|
|
499
|
+
const agentFilename = agentMd.get('filename');
|
|
500
|
+
if (agentFilename && segmentPattern.test(agentFilename)) {
|
|
501
|
+
// Use agent schemas unless overridden
|
|
502
|
+
const inputSchemaCode = hasCustomInput
|
|
503
|
+
? undefined
|
|
504
|
+
: agentMd.get('inputSchemaCode');
|
|
505
|
+
const outputSchemaCode = hasCustomOutput
|
|
506
|
+
? undefined
|
|
507
|
+
: agentMd.get('outputSchemaCode');
|
|
508
|
+
|
|
509
|
+
if (inputSchemaCode || outputSchemaCode) {
|
|
510
|
+
route.schema = {
|
|
511
|
+
input: inputSchemaCode,
|
|
512
|
+
output: outputSchemaCode,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// TODO: Extract inline schema code from custom validator({ input: z.string(), output: ... })
|
|
521
|
+
// For now, custom schema overrides with inline code are not extracted (would require parsing the validator call's object expression)
|
|
522
|
+
}
|
|
523
|
+
|
|
450
524
|
apiRoutesMetadata.push(...routes);
|
|
451
525
|
|
|
452
526
|
// Collect route info for RouteRegistry generation
|
|
@@ -594,6 +668,16 @@ await (async() => {
|
|
|
594
668
|
projectId,
|
|
595
669
|
};
|
|
596
670
|
|
|
671
|
+
// Extract schema codes if available
|
|
672
|
+
const inputSchemaCode = v.get('inputSchemaCode');
|
|
673
|
+
const outputSchemaCode = v.get('outputSchemaCode');
|
|
674
|
+
if (inputSchemaCode || outputSchemaCode) {
|
|
675
|
+
agentData.schema = {
|
|
676
|
+
input: inputSchemaCode,
|
|
677
|
+
output: outputSchemaCode,
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
597
681
|
const evalsStr = v.get('evals');
|
|
598
682
|
if (evalsStr) {
|
|
599
683
|
logger.trace(
|
|
@@ -31,7 +31,7 @@ export const createSubcommand = defineSubcommand({
|
|
|
31
31
|
},
|
|
32
32
|
|
|
33
33
|
async handler(ctx) {
|
|
34
|
-
const { logger, opts, orgId, region,
|
|
34
|
+
const { logger, opts, orgId, region, auth, options } = ctx;
|
|
35
35
|
|
|
36
36
|
// Handle dry-run mode
|
|
37
37
|
if (isDryRunMode(options)) {
|
|
@@ -49,7 +49,7 @@ export const createSubcommand = defineSubcommand({
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const catalystClient = getCatalystAPIClient(
|
|
52
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
53
53
|
|
|
54
54
|
try {
|
|
55
55
|
const created = await tui.spinner({
|
|
@@ -35,9 +35,9 @@ export const deleteSubcommand = createSubcommand({
|
|
|
35
35
|
},
|
|
36
36
|
|
|
37
37
|
async handler(ctx) {
|
|
38
|
-
const { logger, args, opts,
|
|
38
|
+
const { logger, args, opts, orgId, region, auth, options } = ctx;
|
|
39
39
|
|
|
40
|
-
const catalystClient = getCatalystAPIClient(
|
|
40
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
41
41
|
|
|
42
42
|
let dbName = args.name;
|
|
43
43
|
|
package/src/cmd/cloud/db/get.ts
CHANGED
|
@@ -65,9 +65,9 @@ export const getSubcommand = createSubcommand({
|
|
|
65
65
|
},
|
|
66
66
|
|
|
67
67
|
async handler(ctx) {
|
|
68
|
-
const { logger, args, opts, options, orgId, region,
|
|
68
|
+
const { logger, args, opts, options, orgId, region, auth } = ctx;
|
|
69
69
|
|
|
70
|
-
const catalystClient = getCatalystAPIClient(
|
|
70
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
71
71
|
|
|
72
72
|
const resources = await tui.spinner({
|
|
73
73
|
message: `Fetching database ${args.name}`,
|
package/src/cmd/cloud/db/list.ts
CHANGED
|
@@ -46,9 +46,9 @@ export const listSubcommand = createSubcommand({
|
|
|
46
46
|
},
|
|
47
47
|
|
|
48
48
|
async handler(ctx) {
|
|
49
|
-
const { logger, opts, options, orgId, region,
|
|
49
|
+
const { logger, opts, options, orgId, region, auth } = ctx;
|
|
50
50
|
|
|
51
|
-
const catalystClient = getCatalystAPIClient(
|
|
51
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
52
52
|
|
|
53
53
|
const resources = await tui.spinner({
|
|
54
54
|
message: `Fetching databases for ${orgId} in ${region}`,
|
package/src/cmd/cloud/db/logs.ts
CHANGED
|
@@ -78,14 +78,14 @@ export const logsSubcommand = createSubcommand({
|
|
|
78
78
|
response: DbLogsResponseSchema,
|
|
79
79
|
},
|
|
80
80
|
async handler(ctx) {
|
|
81
|
-
const { args, options, orgId, region,
|
|
81
|
+
const { args, options, orgId, region, logger, auth } = ctx;
|
|
82
82
|
const showTimestamps = ctx.opts.timestamps ?? true;
|
|
83
83
|
const showSessionId = ctx.opts.showSessionId ?? false;
|
|
84
84
|
const showUsername = ctx.opts.showUsername ?? false;
|
|
85
85
|
const prettySQL = ctx.opts.pretty ?? false;
|
|
86
86
|
|
|
87
87
|
try {
|
|
88
|
-
const catalystClient = getCatalystAPIClient(
|
|
88
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
89
89
|
|
|
90
90
|
const logs = await dbLogs(catalystClient, {
|
|
91
91
|
database: args.database,
|
package/src/cmd/cloud/db/sql.ts
CHANGED
|
@@ -42,9 +42,9 @@ export const sqlSubcommand = createSubcommand({
|
|
|
42
42
|
},
|
|
43
43
|
|
|
44
44
|
async handler(ctx) {
|
|
45
|
-
const { logger, args, options, orgId, region,
|
|
45
|
+
const { logger, args, options, orgId, region, auth } = ctx;
|
|
46
46
|
|
|
47
|
-
const catalystClient = getCatalystAPIClient(
|
|
47
|
+
const catalystClient = getCatalystAPIClient(logger, auth, region);
|
|
48
48
|
|
|
49
49
|
const result = await tui.spinner({
|
|
50
50
|
message: `Executing query on ${args.name}`,
|