@agentuity/cli 0.0.110 → 0.0.111
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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +19 -4
- package/dist/cli.js.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.d.ts +1 -1
- package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.js +3 -3
- package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
- package/dist/cmd/build/vite/index.js +1 -1
- package/dist/cmd/build/vite/index.js.map +1 -1
- package/dist/cmd/build/vite/metadata-generator.js +1 -1
- package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
- package/dist/cmd/build/vite/registry-generator.d.ts +1 -1
- package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/registry-generator.js +70 -23
- package/dist/cmd/build/vite/registry-generator.js.map +1 -1
- package/dist/cmd/build/vite/route-discovery.d.ts +6 -0
- package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/route-discovery.js +19 -0
- package/dist/cmd/build/vite/route-discovery.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +1 -1
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +63 -1
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/create.js +18 -0
- package/dist/cmd/cloud/sandbox/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/delete.js +2 -6
- package/dist/cmd/cloud/sandbox/delete.js.map +1 -1
- package/dist/cmd/cloud/sandbox/download.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/download.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/download.js +89 -0
- package/dist/cmd/cloud/sandbox/download.js.map +1 -0
- package/dist/cmd/cloud/sandbox/env.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/env.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/env.js +90 -0
- package/dist/cmd/cloud/sandbox/env.js.map +1 -0
- package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/get.js +5 -0
- package/dist/cmd/cloud/sandbox/get.js.map +1 -1
- package/dist/cmd/cloud/sandbox/index.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/index.js +14 -0
- package/dist/cmd/cloud/sandbox/index.js.map +1 -1
- package/dist/cmd/cloud/sandbox/ls.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/ls.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/ls.js +119 -0
- package/dist/cmd/cloud/sandbox/ls.js.map +1 -0
- package/dist/cmd/cloud/sandbox/mkdir.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/mkdir.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/mkdir.js +59 -0
- package/dist/cmd/cloud/sandbox/mkdir.js.map +1 -0
- package/dist/cmd/cloud/sandbox/rm.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/rm.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/rm.js +45 -0
- package/dist/cmd/cloud/sandbox/rm.js.map +1 -0
- package/dist/cmd/cloud/sandbox/rmdir.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/rmdir.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/rmdir.js +59 -0
- package/dist/cmd/cloud/sandbox/rmdir.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/create.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/create.js +0 -2
- package/dist/cmd/cloud/sandbox/snapshot/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/get.js +0 -2
- package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/list.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/list.js +0 -3
- package/dist/cmd/cloud/sandbox/snapshot/list.js.map +1 -1
- package/dist/cmd/cloud/sandbox/upload.d.ts +3 -0
- package/dist/cmd/cloud/sandbox/upload.d.ts.map +1 -0
- package/dist/cmd/cloud/sandbox/upload.js +77 -0
- package/dist/cmd/cloud/sandbox/upload.js.map +1 -0
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +17 -8
- 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 +8 -14
- package/dist/cmd/dev/sync.js.map +1 -1
- package/dist/cmd/git/account/add.d.ts +17 -0
- package/dist/cmd/git/account/add.d.ts.map +1 -0
- package/dist/cmd/git/account/add.js +244 -0
- package/dist/cmd/git/account/add.js.map +1 -0
- package/dist/cmd/git/account/index.d.ts +3 -0
- package/dist/cmd/git/account/index.d.ts.map +1 -0
- package/dist/cmd/git/account/index.js +11 -0
- package/dist/cmd/git/account/index.js.map +1 -0
- package/dist/cmd/git/account/list.d.ts +2 -0
- package/dist/cmd/git/account/list.d.ts.map +1 -0
- package/dist/cmd/git/account/list.js +111 -0
- package/dist/cmd/git/account/list.js.map +1 -0
- package/dist/cmd/git/account/remove.d.ts +2 -0
- package/dist/cmd/git/account/remove.d.ts.map +1 -0
- package/dist/cmd/git/account/remove.js +171 -0
- package/dist/cmd/git/account/remove.js.map +1 -0
- package/dist/cmd/git/index.d.ts +3 -0
- package/dist/cmd/git/index.d.ts.map +1 -0
- package/dist/cmd/git/index.js +19 -0
- package/dist/cmd/git/index.js.map +1 -0
- package/dist/cmd/git/link.d.ts +32 -0
- package/dist/cmd/git/link.d.ts.map +1 -0
- package/dist/cmd/git/link.js +357 -0
- package/dist/cmd/git/link.js.map +1 -0
- package/dist/cmd/git/list.d.ts +2 -0
- package/dist/cmd/git/list.d.ts.map +1 -0
- package/dist/cmd/git/list.js +137 -0
- package/dist/cmd/git/list.js.map +1 -0
- package/dist/cmd/git/status.d.ts +2 -0
- package/dist/cmd/git/status.d.ts.map +1 -0
- package/dist/cmd/git/status.js +119 -0
- package/dist/cmd/git/status.js.map +1 -0
- package/dist/cmd/git/unlink.d.ts +2 -0
- package/dist/cmd/git/unlink.d.ts.map +1 -0
- package/dist/cmd/git/unlink.js +98 -0
- package/dist/cmd/git/unlink.js.map +1 -0
- package/dist/cmd/index.d.ts.map +1 -1
- package/dist/cmd/index.js +2 -0
- package/dist/cmd/index.js.map +1 -1
- package/dist/cmd/integration/api.d.ts +61 -0
- package/dist/cmd/integration/api.d.ts.map +1 -0
- package/dist/cmd/integration/api.js +176 -0
- package/dist/cmd/integration/api.js.map +1 -0
- package/dist/cmd/integration/github/connect.d.ts +2 -0
- package/dist/cmd/integration/github/connect.d.ts.map +1 -0
- package/dist/cmd/integration/github/connect.js +197 -0
- package/dist/cmd/integration/github/connect.js.map +1 -0
- package/dist/cmd/integration/github/disconnect.d.ts +2 -0
- package/dist/cmd/integration/github/disconnect.d.ts.map +1 -0
- package/dist/cmd/integration/github/disconnect.js +121 -0
- package/dist/cmd/integration/github/disconnect.js.map +1 -0
- package/dist/cmd/integration/github/index.d.ts +2 -0
- package/dist/cmd/integration/github/index.d.ts.map +1 -0
- package/dist/cmd/integration/github/index.js +21 -0
- package/dist/cmd/integration/github/index.js.map +1 -0
- package/dist/cmd/integration/index.d.ts +2 -0
- package/dist/cmd/integration/index.d.ts.map +1 -0
- package/dist/cmd/integration/index.js +16 -0
- package/dist/cmd/integration/index.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +24 -0
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +5 -0
- package/dist/errors.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -1
- package/package.json +6 -6
- package/src/cli.ts +20 -4
- package/src/cmd/build/vite/agent-discovery.ts +4 -4
- package/src/cmd/build/vite/index.ts +1 -1
- package/src/cmd/build/vite/metadata-generator.ts +1 -1
- package/src/cmd/build/vite/registry-generator.ts +78 -24
- package/src/cmd/build/vite/route-discovery.ts +20 -0
- package/src/cmd/build/vite/vite-builder.ts +1 -1
- package/src/cmd/cloud/deploy.ts +78 -1
- package/src/cmd/cloud/sandbox/create.ts +22 -0
- package/src/cmd/cloud/sandbox/delete.ts +2 -6
- package/src/cmd/cloud/sandbox/download.ts +96 -0
- package/src/cmd/cloud/sandbox/env.ts +104 -0
- package/src/cmd/cloud/sandbox/get.ts +5 -0
- package/src/cmd/cloud/sandbox/index.ts +14 -0
- package/src/cmd/cloud/sandbox/ls.ts +126 -0
- package/src/cmd/cloud/sandbox/mkdir.ts +65 -0
- package/src/cmd/cloud/sandbox/rm.ts +51 -0
- package/src/cmd/cloud/sandbox/rmdir.ts +65 -0
- package/src/cmd/cloud/sandbox/snapshot/create.ts +0 -2
- package/src/cmd/cloud/sandbox/snapshot/get.ts +0 -2
- package/src/cmd/cloud/sandbox/snapshot/list.ts +0 -3
- package/src/cmd/cloud/sandbox/upload.ts +83 -0
- package/src/cmd/dev/index.ts +32 -19
- package/src/cmd/dev/sync.ts +26 -30
- package/src/cmd/git/account/add.ts +317 -0
- package/src/cmd/git/account/index.ts +12 -0
- package/src/cmd/git/account/list.ts +139 -0
- package/src/cmd/git/account/remove.ts +212 -0
- package/src/cmd/git/index.ts +20 -0
- package/src/cmd/git/link.ts +468 -0
- package/src/cmd/git/list.ts +161 -0
- package/src/cmd/git/status.ts +144 -0
- package/src/cmd/git/unlink.ts +117 -0
- package/src/cmd/index.ts +2 -0
- package/src/cmd/integration/api.ts +379 -0
- package/src/cmd/integration/github/connect.ts +242 -0
- package/src/cmd/integration/github/disconnect.ts +149 -0
- package/src/cmd/integration/github/index.ts +21 -0
- package/src/cmd/integration/index.ts +16 -0
- package/src/config.ts +34 -0
- package/src/errors.ts +7 -0
- package/src/types.ts +4 -0
package/src/cli.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
} from './types';
|
|
16
16
|
import { showBanner, generateBanner } from './banner';
|
|
17
17
|
import { requireAuth, optionalAuth, requireOrg, optionalOrg as selectOptionalOrg } from './auth';
|
|
18
|
-
import { listRegions, type RegionList } from '@agentuity/server';
|
|
18
|
+
import { listRegions, type RegionList, ValidationOutputError } from '@agentuity/server';
|
|
19
19
|
import enquirer from 'enquirer';
|
|
20
20
|
import * as tui from './tui';
|
|
21
21
|
import { parseArgsSchema, parseOptionsSchema, buildValidationInput } from './schema-parser';
|
|
@@ -26,6 +26,22 @@ import { getCommand } from './command-prefix';
|
|
|
26
26
|
import { isValidateMode, outputValidation, type ValidationResult } from './output';
|
|
27
27
|
import { StructuredError } from '@agentuity/core';
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Check if an error is a CLI input validation error (Zod error from schema parsing),
|
|
31
|
+
* and not an API response validation error (ValidationOutputError).
|
|
32
|
+
*/
|
|
33
|
+
function isCLIValidationError(error: unknown): boolean {
|
|
34
|
+
if (!error || typeof error !== 'object' || !('issues' in error)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
// ValidationOutputError from API responses should NOT be treated as CLI validation errors
|
|
38
|
+
if (error instanceof ValidationOutputError) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
// Check for Zod error structure (has name 'ZodError' or is from SchemaValidationError)
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
29
45
|
const APIClientConfigError = StructuredError('APIClientConfigError');
|
|
30
46
|
|
|
31
47
|
function createAPIClient(baseCtx: CommandContext, config: Config | null): APIClient {
|
|
@@ -1143,7 +1159,7 @@ async function registerSubcommand(
|
|
|
1143
1159
|
subcommand.webUrl
|
|
1144
1160
|
);
|
|
1145
1161
|
} catch (error) {
|
|
1146
|
-
if (error
|
|
1162
|
+
if (isCLIValidationError(error)) {
|
|
1147
1163
|
handleValidationError(error, getFullCommandPath(cmd), baseCtx);
|
|
1148
1164
|
}
|
|
1149
1165
|
handleProjectConfigError(
|
|
@@ -1322,7 +1338,7 @@ async function registerSubcommand(
|
|
|
1322
1338
|
subcommand.webUrl
|
|
1323
1339
|
);
|
|
1324
1340
|
} catch (error) {
|
|
1325
|
-
if (error
|
|
1341
|
+
if (isCLIValidationError(error)) {
|
|
1326
1342
|
handleValidationError(error, getFullCommandPath(cmd), baseCtx);
|
|
1327
1343
|
}
|
|
1328
1344
|
handleProjectConfigError(
|
|
@@ -1447,7 +1463,7 @@ async function registerSubcommand(
|
|
|
1447
1463
|
subcommand.webUrl
|
|
1448
1464
|
);
|
|
1449
1465
|
} catch (error) {
|
|
1450
|
-
if (error
|
|
1466
|
+
if (isCLIValidationError(error)) {
|
|
1451
1467
|
handleValidationError(error, getFullCommandPath(cmd), baseCtx);
|
|
1452
1468
|
}
|
|
1453
1469
|
handleProjectConfigError(
|
|
@@ -65,7 +65,7 @@ export interface AgentMetadata {
|
|
|
65
65
|
|
|
66
66
|
export interface EvalMetadata {
|
|
67
67
|
id: string;
|
|
68
|
-
|
|
68
|
+
identifier: string;
|
|
69
69
|
name: string;
|
|
70
70
|
filename: string;
|
|
71
71
|
version: string;
|
|
@@ -459,13 +459,13 @@ function extractEvalsFromSource(
|
|
|
459
459
|
|
|
460
460
|
if (evalName) {
|
|
461
461
|
const id = getEvalId(projectId, deploymentId, filename, evalName, version);
|
|
462
|
-
const
|
|
462
|
+
const identifier = generateStableEvalId(projectId, agentId, evalName);
|
|
463
463
|
|
|
464
|
-
logger.trace(`Found eval '${evalName}' in ${filename} (
|
|
464
|
+
logger.trace(`Found eval '${evalName}' in ${filename} (identifier: ${identifier})`);
|
|
465
465
|
|
|
466
466
|
evals.push({
|
|
467
467
|
id,
|
|
468
|
-
|
|
468
|
+
identifier,
|
|
469
469
|
name: evalName,
|
|
470
470
|
filename,
|
|
471
471
|
version,
|
|
@@ -87,7 +87,7 @@ export function agentuityPlugin(options: AgentuityPluginOptions): Plugin {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
if (routeInfoList.length > 0) {
|
|
90
|
-
generateRouteRegistry(srcDir, routeInfoList, agents);
|
|
90
|
+
await generateRouteRegistry(srcDir, routeInfoList, agents);
|
|
91
91
|
logger.trace('Generated route registry with %d route(s)', routeInfoList.length);
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -443,7 +443,7 @@ export async function generateMetadata(options: MetadataGeneratorOptions): Promi
|
|
|
443
443
|
evals: agent.evals?.map((evalItem) => ({
|
|
444
444
|
filename: evalItem.filename,
|
|
445
445
|
id: evalItem.id,
|
|
446
|
-
|
|
446
|
+
identifier: evalItem.identifier,
|
|
447
447
|
name: evalItem.name,
|
|
448
448
|
version: evalItem.version,
|
|
449
449
|
description: evalItem.description,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { join } from 'node:path';
|
|
8
8
|
import { writeFileSync, mkdirSync, existsSync, unlinkSync, readFileSync } from 'node:fs';
|
|
9
|
+
import { stat } from 'node:fs/promises';
|
|
9
10
|
import { StructuredError } from '@agentuity/core';
|
|
10
11
|
import { toCamelCase, toPascalCase } from '../../../utils/string';
|
|
11
12
|
import type { AgentMetadata } from './agent-discovery';
|
|
@@ -29,6 +30,28 @@ function sanitizePathSegment(segment: string): string {
|
|
|
29
30
|
return toCamelCase(segment.replace(ROUTE_PARAM_CHARS, ''));
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Generate TypeScript type for path parameters.
|
|
35
|
+
* Returns 'never' if no path params, or '{ param1: string; param2: string }' format.
|
|
36
|
+
*/
|
|
37
|
+
function generatePathParamsType(pathParams?: string[]): string {
|
|
38
|
+
if (!pathParams || pathParams.length === 0) {
|
|
39
|
+
return 'never';
|
|
40
|
+
}
|
|
41
|
+
return `{ ${pathParams.map((p) => `${p}: string`).join('; ')} }`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generate TypeScript tuple type for path parameters (for positional args).
|
|
46
|
+
* Returns '[]' if no path params, or '[string, string]' format.
|
|
47
|
+
*/
|
|
48
|
+
function generatePathParamsTupleType(pathParams?: string[]): string {
|
|
49
|
+
if (!pathParams || pathParams.length === 0) {
|
|
50
|
+
return '[]';
|
|
51
|
+
}
|
|
52
|
+
return `[${pathParams.map(() => 'string').join(', ')}]`;
|
|
53
|
+
}
|
|
54
|
+
|
|
32
55
|
/**
|
|
33
56
|
* Generate src/generated/registry.ts with agent registry and types
|
|
34
57
|
*/
|
|
@@ -362,8 +385,10 @@ function generateRPCRegistryType(
|
|
|
362
385
|
jsdoc.push(`${indent} */`);
|
|
363
386
|
lines.push(...jsdoc);
|
|
364
387
|
|
|
388
|
+
const pathParamsType = generatePathParamsType(routeInfo.pathParams);
|
|
389
|
+
const pathParamsTupleType = generatePathParamsTupleType(routeInfo.pathParams);
|
|
365
390
|
lines.push(
|
|
366
|
-
`${indent}${key}: { input: ${value.input}; output: ${value.output}; type: ${value.type} };`
|
|
391
|
+
`${indent}${key}: { input: ${value.input}; output: ${value.output}; type: ${value.type}; params: ${pathParamsType}; paramsTuple: ${pathParamsTupleType} };`
|
|
367
392
|
);
|
|
368
393
|
} else {
|
|
369
394
|
// Nested node
|
|
@@ -393,7 +418,7 @@ function generateRPCRuntimeMetadata(
|
|
|
393
418
|
sseRoutes: RouteInfo[]
|
|
394
419
|
): string {
|
|
395
420
|
interface MetadataNode {
|
|
396
|
-
[key: string]: MetadataNode | { type: string };
|
|
421
|
+
[key: string]: MetadataNode | { type: string; path: string; pathParams?: string[] };
|
|
397
422
|
}
|
|
398
423
|
|
|
399
424
|
const tree: MetadataNode = {};
|
|
@@ -431,7 +456,14 @@ function generateRPCRuntimeMetadata(
|
|
|
431
456
|
? 'stream'
|
|
432
457
|
: route.method.toLowerCase();
|
|
433
458
|
|
|
434
|
-
|
|
459
|
+
const metadata: { type: string; path: string; pathParams?: string[] } = {
|
|
460
|
+
type: routeType,
|
|
461
|
+
path: route.path,
|
|
462
|
+
};
|
|
463
|
+
if (route.pathParams && route.pathParams.length > 0) {
|
|
464
|
+
metadata.pathParams = route.pathParams;
|
|
465
|
+
}
|
|
466
|
+
current[terminalMethod] = metadata;
|
|
435
467
|
};
|
|
436
468
|
|
|
437
469
|
apiRoutes.forEach((r) => addRoute(r, r.routeType === 'stream' ? 'stream' : 'api'));
|
|
@@ -461,15 +493,15 @@ function generateRPCRuntimeMetadata(
|
|
|
461
493
|
* Creates a module augmentation for @agentuity/react that provides
|
|
462
494
|
* strongly-typed route keys with input/output schema information.
|
|
463
495
|
*/
|
|
464
|
-
export function generateRouteRegistry(
|
|
496
|
+
export async function generateRouteRegistry(
|
|
465
497
|
srcDir: string,
|
|
466
498
|
routes: RouteInfo[],
|
|
467
499
|
agents: AgentMetadata[] = []
|
|
468
|
-
): void {
|
|
469
|
-
// Check if project uses @agentuity/react
|
|
500
|
+
): Promise<void> {
|
|
470
501
|
const projectRoot = join(srcDir, '..');
|
|
471
502
|
const packageJsonPath = join(projectRoot, 'package.json');
|
|
472
503
|
let hasReactDependency = false;
|
|
504
|
+
let hasFrontendDependency = false;
|
|
473
505
|
|
|
474
506
|
try {
|
|
475
507
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
@@ -477,10 +509,25 @@ export function generateRouteRegistry(
|
|
|
477
509
|
packageJson.dependencies?.['@agentuity/react'] ||
|
|
478
510
|
packageJson.devDependencies?.['@agentuity/react']
|
|
479
511
|
);
|
|
512
|
+
hasFrontendDependency = !!(
|
|
513
|
+
packageJson.dependencies?.['@agentuity/frontend'] ||
|
|
514
|
+
packageJson.devDependencies?.['@agentuity/frontend']
|
|
515
|
+
);
|
|
480
516
|
} catch {
|
|
481
|
-
// If we can't read package.json, assume no
|
|
517
|
+
// If we can't read package.json, assume no frontend dependencies
|
|
482
518
|
}
|
|
483
519
|
|
|
520
|
+
const webDir = join(srcDir, 'web');
|
|
521
|
+
let hasWebDirectory = false;
|
|
522
|
+
try {
|
|
523
|
+
const webDirStat = await stat(webDir);
|
|
524
|
+
hasWebDirectory = webDirStat.isDirectory();
|
|
525
|
+
} catch {
|
|
526
|
+
// Directory doesn't exist
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const shouldEmitFrontendClient = hasFrontendDependency && !hasReactDependency && hasWebDirectory;
|
|
530
|
+
|
|
484
531
|
// Filter routes by type and sort by path for deterministic output
|
|
485
532
|
const sortByPath = (a: RouteInfo, b: RouteInfo) => a.path.localeCompare(b.path);
|
|
486
533
|
const apiRoutes = routes
|
|
@@ -769,13 +816,18 @@ export function generateRouteRegistry(
|
|
|
769
816
|
// because only 'json' validators extract input schemas
|
|
770
817
|
// Also check if agentVariable exists but import wasn't added (missing agentImportPath)
|
|
771
818
|
const hasValidAgentImport = route.agentVariable ? !!importName : false;
|
|
819
|
+
|
|
820
|
+
// Generate pathParams type
|
|
821
|
+
const pathParamsType = generatePathParamsType(route.pathParams);
|
|
822
|
+
|
|
772
823
|
if (!route.inputSchemaVariable && !route.outputSchemaVariable && !hasValidAgentImport) {
|
|
773
824
|
const streamValue = route.stream === true ? 'true' : 'false';
|
|
774
825
|
return `\t'${routeKey}': {
|
|
775
|
-
\t\tinputSchema: never;
|
|
776
|
-
\t\toutputSchema: never;
|
|
777
|
-
\t\tstream: ${streamValue};
|
|
778
|
-
\t}
|
|
826
|
+
\t\tinputSchema: never;
|
|
827
|
+
\t\toutputSchema: never;
|
|
828
|
+
\t\tstream: ${streamValue};
|
|
829
|
+
\t\tparams: ${pathParamsType};
|
|
830
|
+
\t};`;
|
|
779
831
|
}
|
|
780
832
|
const streamValue = importName
|
|
781
833
|
? `typeof ${importName} extends { stream?: infer S } ? S : false`
|
|
@@ -784,10 +836,11 @@ export function generateRouteRegistry(
|
|
|
784
836
|
: 'false';
|
|
785
837
|
|
|
786
838
|
return `\t'${routeKey}': {
|
|
787
|
-
\t\tinputSchema: ${pascalName}InputSchema;
|
|
788
|
-
\t\toutputSchema: ${pascalName}OutputSchema;
|
|
789
|
-
\t\tstream: ${streamValue};
|
|
790
|
-
\t}
|
|
839
|
+
\t\tinputSchema: ${pascalName}InputSchema;
|
|
840
|
+
\t\toutputSchema: ${pascalName}OutputSchema;
|
|
841
|
+
\t\tstream: ${streamValue};
|
|
842
|
+
\t\tparams: ${pathParamsType};
|
|
843
|
+
\t};`;
|
|
791
844
|
};
|
|
792
845
|
|
|
793
846
|
// Generate route entries with METHOD prefix for API routes
|
|
@@ -817,7 +870,7 @@ export function generateRouteRegistry(
|
|
|
817
870
|
const generatedContent = `// @generated
|
|
818
871
|
// Auto-generated by Agentuity - DO NOT EDIT
|
|
819
872
|
${importsStr}${typeImports}${
|
|
820
|
-
|
|
873
|
+
shouldEmitFrontendClient
|
|
821
874
|
? `
|
|
822
875
|
import { createClient } from '@agentuity/frontend';`
|
|
823
876
|
: ''
|
|
@@ -842,7 +895,7 @@ ${routeSchemaTypes}
|
|
|
842
895
|
* Individual route Input/Output types are exported above for direct usage.
|
|
843
896
|
*/
|
|
844
897
|
${
|
|
845
|
-
|
|
898
|
+
shouldEmitFrontendClient
|
|
846
899
|
? `
|
|
847
900
|
/**
|
|
848
901
|
* RPC Route Registry
|
|
@@ -907,13 +960,14 @@ if (typeof globalThis !== 'undefined') {
|
|
|
907
960
|
(globalThis as Record<string, unknown>).__rpcRouteMetadata = _rpcRouteMetadata;
|
|
908
961
|
}
|
|
909
962
|
${
|
|
910
|
-
|
|
963
|
+
shouldEmitFrontendClient
|
|
911
964
|
? `
|
|
912
965
|
/**
|
|
913
966
|
* Create a type-safe API client with optional configuration.
|
|
914
967
|
*
|
|
915
|
-
* This function is only generated when @agentuity/
|
|
916
|
-
*
|
|
968
|
+
* This function is only generated when @agentuity/frontend is installed
|
|
969
|
+
* but @agentuity/react is not. For React apps, import createAPIClient
|
|
970
|
+
* from '@agentuity/react' instead.
|
|
917
971
|
*
|
|
918
972
|
* @example
|
|
919
973
|
* \`\`\`typescript
|
|
@@ -932,7 +986,8 @@ export function createAPIClient(options?: Parameters<typeof createClient>[0]): i
|
|
|
932
986
|
return createClient(options || {}, _rpcRouteMetadata) as import('@agentuity/frontend').Client<RPCRouteRegistry>;
|
|
933
987
|
}
|
|
934
988
|
`
|
|
935
|
-
:
|
|
989
|
+
: hasReactDependency
|
|
990
|
+
? `
|
|
936
991
|
/**
|
|
937
992
|
* Type-safe API client is available from @agentuity/react
|
|
938
993
|
*
|
|
@@ -945,6 +1000,7 @@ export function createAPIClient(options?: Parameters<typeof createClient>[0]): i
|
|
|
945
1000
|
* \`\`\`
|
|
946
1001
|
*/
|
|
947
1002
|
`
|
|
1003
|
+
: ''
|
|
948
1004
|
}
|
|
949
1005
|
|
|
950
1006
|
// FOUND AN ERROR IN THIS FILE?
|
|
@@ -955,9 +1011,7 @@ export function createAPIClient(options?: Parameters<typeof createClient>[0]): i
|
|
|
955
1011
|
const generatedDir = join(srcDir, 'generated');
|
|
956
1012
|
const registryPath = join(generatedDir, 'routes.ts');
|
|
957
1013
|
|
|
958
|
-
|
|
959
|
-
mkdirSync(generatedDir, { recursive: true });
|
|
960
|
-
}
|
|
1014
|
+
mkdirSync(generatedDir, { recursive: true });
|
|
961
1015
|
|
|
962
1016
|
// Collapse 2+ consecutive empty lines into 1 empty line (3+ \n becomes 2 \n)
|
|
963
1017
|
const cleanedContent = generatedContent.replace(/\n{3,}/g, '\n\n');
|
|
@@ -40,6 +40,24 @@ export interface RouteInfo {
|
|
|
40
40
|
inputSchemaCode?: string;
|
|
41
41
|
outputSchemaCode?: string;
|
|
42
42
|
stream?: boolean;
|
|
43
|
+
pathParams?: string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Extract path parameters from a route path.
|
|
48
|
+
* Matches patterns like :id, :userId, :id?, *path, etc.
|
|
49
|
+
*/
|
|
50
|
+
export function extractPathParams(path: string): string[] {
|
|
51
|
+
const params: string[] = [];
|
|
52
|
+
const parts = path.split('/');
|
|
53
|
+
for (const part of parts) {
|
|
54
|
+
if (part.startsWith(':')) {
|
|
55
|
+
params.push(part.replace(/^:|[?+*]$/g, ''));
|
|
56
|
+
} else if (part.startsWith('*') && part.length > 1) {
|
|
57
|
+
params.push(part.substring(1).replace(/[?+*]$/g, ''));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return params;
|
|
43
61
|
}
|
|
44
62
|
|
|
45
63
|
/**
|
|
@@ -90,6 +108,7 @@ export async function discoverRoutes(
|
|
|
90
108
|
|
|
91
109
|
// Convert to RouteInfo for registry
|
|
92
110
|
for (const route of parsedRoutes) {
|
|
111
|
+
const pathParams = extractPathParams(route.path);
|
|
93
112
|
routeInfoList.push({
|
|
94
113
|
method: route.method.toUpperCase(),
|
|
95
114
|
path: route.path,
|
|
@@ -106,6 +125,7 @@ export async function discoverRoutes(
|
|
|
106
125
|
: route.type === 'stream'
|
|
107
126
|
? true
|
|
108
127
|
: undefined,
|
|
128
|
+
pathParams: pathParams.length > 0 ? pathParams : undefined,
|
|
109
129
|
});
|
|
110
130
|
}
|
|
111
131
|
}
|
|
@@ -305,7 +305,7 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
|
|
|
305
305
|
// Generate agent and route registries for type augmentation BEFORE builds
|
|
306
306
|
// (TypeScript needs these files to exist during type checking)
|
|
307
307
|
generateAgentRegistry(srcDir, agentMetadata);
|
|
308
|
-
generateRouteRegistry(srcDir, routeInfoList);
|
|
308
|
+
await generateRouteRegistry(srcDir, routeInfoList);
|
|
309
309
|
logger.debug('Agent and route registries generated');
|
|
310
310
|
|
|
311
311
|
// Check if web frontend exists
|
package/src/cmd/cloud/deploy.ts
CHANGED
|
@@ -8,7 +8,14 @@ import { isRunningFromExecutable } from '../upgrade';
|
|
|
8
8
|
import { createSubcommand, DeployOptionsSchema } from '../../types';
|
|
9
9
|
import { getUserAgent } from '../../api';
|
|
10
10
|
import * as tui from '../../tui';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
saveProjectDir,
|
|
13
|
+
getDefaultConfigDir,
|
|
14
|
+
loadProjectSDKKey,
|
|
15
|
+
updateProjectConfig,
|
|
16
|
+
} from '../../config';
|
|
17
|
+
import { getProjectGithubStatus } from '../integration/api';
|
|
18
|
+
import { runGitLink } from '../git/link';
|
|
12
19
|
import {
|
|
13
20
|
runSteps,
|
|
14
21
|
stepSuccess,
|
|
@@ -163,6 +170,76 @@ export const deploySubcommand = createSubcommand({
|
|
|
163
170
|
try {
|
|
164
171
|
await saveProjectDir(projectDir);
|
|
165
172
|
|
|
173
|
+
// Check GitHub status and prompt for setup if not linked
|
|
174
|
+
// Skip in non-TTY environments (CI, automated runs) to prevent hanging
|
|
175
|
+
const hasTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
176
|
+
if (!useExistingDeployment && !project.skipGitSetup && hasTTY) {
|
|
177
|
+
try {
|
|
178
|
+
const githubStatus = await getProjectGithubStatus(apiClient, project.projectId);
|
|
179
|
+
|
|
180
|
+
if (githubStatus.linked && githubStatus.autoDeploy) {
|
|
181
|
+
// GitHub is already set up with auto-deploy, tell user to push instead
|
|
182
|
+
tui.newline();
|
|
183
|
+
tui.info(
|
|
184
|
+
`This project is linked to ${tui.bold(githubStatus.repoFullName ?? 'GitHub')} with automatic deployments enabled.`
|
|
185
|
+
);
|
|
186
|
+
tui.newline();
|
|
187
|
+
tui.info(
|
|
188
|
+
`Push a commit to the ${tui.bold(githubStatus.branch ?? 'main')} branch to trigger a deployment.`
|
|
189
|
+
);
|
|
190
|
+
tui.newline();
|
|
191
|
+
throw new DeploymentCancelledError();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!githubStatus.linked) {
|
|
195
|
+
tui.newline();
|
|
196
|
+
const wantSetup = await tui.confirm(
|
|
197
|
+
'Would you like to set up automatic deployments from GitHub?'
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if (wantSetup) {
|
|
201
|
+
const result = await runGitLink({
|
|
202
|
+
apiClient,
|
|
203
|
+
projectId: project.projectId,
|
|
204
|
+
orgId: project.orgId,
|
|
205
|
+
logger,
|
|
206
|
+
skipAlreadyLinkedCheck: true,
|
|
207
|
+
config,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
if (result.linked && result.autoDeploy) {
|
|
211
|
+
// GitHub linked with auto-deploy, tell user to push instead
|
|
212
|
+
tui.newline();
|
|
213
|
+
tui.info('GitHub integration set up successfully!');
|
|
214
|
+
tui.newline();
|
|
215
|
+
tui.info('Push a commit to trigger your first deployment.');
|
|
216
|
+
tui.newline();
|
|
217
|
+
throw new DeploymentCancelledError();
|
|
218
|
+
} else if (result.linked) {
|
|
219
|
+
// Linked but auto-deploy disabled, continue with manual deploy
|
|
220
|
+
tui.newline();
|
|
221
|
+
tui.info('GitHub repository linked. Continuing with deployment...');
|
|
222
|
+
tui.newline();
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
await updateProjectConfig(projectDir, { skipGitSetup: true }, config);
|
|
226
|
+
tui.newline();
|
|
227
|
+
tui.info(
|
|
228
|
+
`Skipping GitHub setup. Run ${tui.bold(getCommand('git link'))} later to enable it.`
|
|
229
|
+
);
|
|
230
|
+
tui.newline();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
} catch (err) {
|
|
234
|
+
// Re-throw intentional cancellations
|
|
235
|
+
if (err instanceof DeploymentCancelledError) {
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
// Log other errors as non-fatal and continue
|
|
239
|
+
logger.trace('Failed to check GitHub status: %s', err);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
166
243
|
await runSteps(
|
|
167
244
|
[
|
|
168
245
|
!project.deployment?.domains?.length
|
|
@@ -4,6 +4,12 @@ import * as tui from '../../../tui';
|
|
|
4
4
|
import { createSandboxClient, parseFileArgs } from './util';
|
|
5
5
|
import { getCommand } from '../../../command-prefix';
|
|
6
6
|
import { sandboxCreate } from '@agentuity/server';
|
|
7
|
+
import { StructuredError } from '@agentuity/core';
|
|
8
|
+
|
|
9
|
+
const InvalidMetadataError = StructuredError(
|
|
10
|
+
'InvalidMetadataError',
|
|
11
|
+
'Metadata must be a valid JSON object'
|
|
12
|
+
);
|
|
7
13
|
|
|
8
14
|
const SandboxCreateResponseSchema = z.object({
|
|
9
15
|
sandboxId: z.string().describe('Unique sandbox identifier'),
|
|
@@ -55,6 +61,7 @@ export const createSubcommand = createCommand({
|
|
|
55
61
|
.array(z.string())
|
|
56
62
|
.optional()
|
|
57
63
|
.describe('Apt packages to install (can be specified multiple times)'),
|
|
64
|
+
metadata: z.string().optional().describe('JSON object of user-defined metadata'),
|
|
58
65
|
}),
|
|
59
66
|
response: SandboxCreateResponseSchema,
|
|
60
67
|
},
|
|
@@ -77,6 +84,20 @@ export const createSubcommand = createCommand({
|
|
|
77
84
|
const files = parseFileArgs(opts.file);
|
|
78
85
|
const hasFiles = files.length > 0;
|
|
79
86
|
|
|
87
|
+
let metadata: Record<string, unknown> | undefined;
|
|
88
|
+
if (opts.metadata) {
|
|
89
|
+
let parsed: unknown;
|
|
90
|
+
try {
|
|
91
|
+
parsed = JSON.parse(opts.metadata);
|
|
92
|
+
} catch {
|
|
93
|
+
throw new InvalidMetadataError();
|
|
94
|
+
}
|
|
95
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
96
|
+
throw new InvalidMetadataError();
|
|
97
|
+
}
|
|
98
|
+
metadata = parsed as Record<string, unknown>;
|
|
99
|
+
}
|
|
100
|
+
|
|
80
101
|
const result = await sandboxCreate(client, {
|
|
81
102
|
options: {
|
|
82
103
|
resources:
|
|
@@ -93,6 +114,7 @@ export const createSubcommand = createCommand({
|
|
|
93
114
|
command: hasFiles ? { exec: [], files } : undefined,
|
|
94
115
|
snapshot: opts.snapshot,
|
|
95
116
|
dependencies: opts.dependency,
|
|
117
|
+
metadata,
|
|
96
118
|
},
|
|
97
119
|
orgId,
|
|
98
120
|
});
|
|
@@ -14,7 +14,7 @@ const SandboxDeleteResponseSchema = z.object({
|
|
|
14
14
|
|
|
15
15
|
export const deleteSubcommand = createCommand({
|
|
16
16
|
name: 'delete',
|
|
17
|
-
aliases: ['del', '
|
|
17
|
+
aliases: ['del', 'remove', 'destroy'],
|
|
18
18
|
description: 'Delete a sandbox',
|
|
19
19
|
tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth'],
|
|
20
20
|
requires: { auth: true, region: true, org: true },
|
|
@@ -25,11 +25,7 @@ export const deleteSubcommand = createCommand({
|
|
|
25
25
|
description: 'Delete a sandbox',
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
|
-
command: getCommand('cloud sandbox
|
|
29
|
-
description: 'Delete using alias',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
command: getCommand('cloud sandbox rm abc123 --confirm'),
|
|
28
|
+
command: getCommand('cloud sandbox delete abc123 --confirm'),
|
|
33
29
|
description: 'Delete without confirmation prompt',
|
|
34
30
|
},
|
|
35
31
|
],
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { writeFileSync } from 'node:fs';
|
|
3
|
+
import { createCommand } from '../../../types';
|
|
4
|
+
import * as tui from '../../../tui';
|
|
5
|
+
import { createSandboxClient } from './util';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
import { sandboxDownloadArchive } from '@agentuity/server';
|
|
8
|
+
|
|
9
|
+
export const downloadSubcommand = createCommand({
|
|
10
|
+
name: 'download',
|
|
11
|
+
aliases: ['dl'],
|
|
12
|
+
description: 'Download files from a sandbox as a compressed archive',
|
|
13
|
+
tags: ['slow', 'requires-auth'],
|
|
14
|
+
requires: { auth: true, region: true, org: true },
|
|
15
|
+
examples: [
|
|
16
|
+
{
|
|
17
|
+
command: getCommand('cloud sandbox download sbx_abc123 ./backup.tar.gz'),
|
|
18
|
+
description: 'Download sandbox files as tar.gz archive',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
command: getCommand('cloud sandbox download sbx_abc123 ./backup.zip --format zip'),
|
|
22
|
+
description: 'Download sandbox files as zip archive',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
command: getCommand('cloud sandbox download sbx_abc123 ./backup.tar.gz --path /subdir'),
|
|
26
|
+
description: 'Download only a specific directory',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
schema: {
|
|
30
|
+
args: z.object({
|
|
31
|
+
sandboxId: z.string().describe('The sandbox ID'),
|
|
32
|
+
output: z.string().describe('Output file path for the archive'),
|
|
33
|
+
}),
|
|
34
|
+
options: z.object({
|
|
35
|
+
path: z.string().optional().describe('Path in sandbox to download (defaults to root)'),
|
|
36
|
+
format: z
|
|
37
|
+
.enum(['zip', 'tar.gz'])
|
|
38
|
+
.default('tar.gz')
|
|
39
|
+
.optional()
|
|
40
|
+
.describe('Archive format (zip or tar.gz)'),
|
|
41
|
+
}),
|
|
42
|
+
response: z.object({
|
|
43
|
+
success: z.boolean(),
|
|
44
|
+
output: z.string(),
|
|
45
|
+
bytes: z.number(),
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
async handler(ctx) {
|
|
50
|
+
const { args, opts, options, auth, region, logger, orgId } = ctx;
|
|
51
|
+
|
|
52
|
+
const client = createSandboxClient(logger, auth, region);
|
|
53
|
+
const format = opts.format || 'tar.gz';
|
|
54
|
+
|
|
55
|
+
const stream = await sandboxDownloadArchive(client, {
|
|
56
|
+
sandboxId: args.sandboxId,
|
|
57
|
+
path: opts.path || '.',
|
|
58
|
+
format,
|
|
59
|
+
orgId,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const chunks: Uint8Array[] = [];
|
|
63
|
+
const reader = stream.getReader();
|
|
64
|
+
|
|
65
|
+
while (true) {
|
|
66
|
+
const { done, value } = await reader.read();
|
|
67
|
+
if (done) break;
|
|
68
|
+
chunks.push(value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const totalBytes = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
|
72
|
+
const buffer = new Uint8Array(totalBytes);
|
|
73
|
+
let offset = 0;
|
|
74
|
+
for (const chunk of chunks) {
|
|
75
|
+
buffer.set(chunk, offset);
|
|
76
|
+
offset += chunk.length;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
writeFileSync(args.output, buffer);
|
|
80
|
+
|
|
81
|
+
if (!options.json) {
|
|
82
|
+
tui.success(`Downloaded ${formatSize(totalBytes)} to ${args.output}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { success: true, output: args.output, bytes: totalBytes };
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
function formatSize(bytes: number): string {
|
|
90
|
+
if (bytes < 1024) return `${bytes} bytes`;
|
|
91
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
92
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
93
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default downloadSubcommand;
|