@agentuity/cli 0.0.56 → 0.0.57

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.
Files changed (50) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +41 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/cmd/build/ast.d.ts +4 -10
  5. package/dist/cmd/build/ast.d.ts.map +1 -1
  6. package/dist/cmd/build/ast.js +9 -10
  7. package/dist/cmd/build/ast.js.map +1 -1
  8. package/dist/cmd/build/bundler.d.ts +25 -2
  9. package/dist/cmd/build/bundler.d.ts.map +1 -1
  10. package/dist/cmd/build/bundler.js +130 -38
  11. package/dist/cmd/build/bundler.js.map +1 -1
  12. package/dist/cmd/build/plugin.js +7 -7
  13. package/dist/cmd/build/plugin.js.map +1 -1
  14. package/dist/cmd/build/workbench-templates.d.ts +4 -0
  15. package/dist/cmd/build/workbench-templates.d.ts.map +1 -0
  16. package/dist/cmd/build/workbench-templates.js +49 -0
  17. package/dist/cmd/build/workbench-templates.js.map +1 -0
  18. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  19. package/dist/cmd/cloud/deploy.js +11 -3
  20. package/dist/cmd/cloud/deploy.js.map +1 -1
  21. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  22. package/dist/cmd/cloud/deployment/show.js +73 -20
  23. package/dist/cmd/cloud/deployment/show.js.map +1 -1
  24. package/dist/cmd/index.d.ts.map +1 -1
  25. package/dist/cmd/index.js +2 -0
  26. package/dist/cmd/index.js.map +1 -1
  27. package/dist/schema-generator.d.ts +1 -1
  28. package/dist/schema-generator.d.ts.map +1 -1
  29. package/dist/schema-parser.d.ts +2 -1
  30. package/dist/schema-parser.d.ts.map +1 -1
  31. package/dist/schema-parser.js +18 -2
  32. package/dist/schema-parser.js.map +1 -1
  33. package/dist/steps.d.ts +2 -1
  34. package/dist/steps.d.ts.map +1 -1
  35. package/dist/steps.js +26 -3
  36. package/dist/steps.js.map +1 -1
  37. package/dist/types.d.ts +15 -0
  38. package/dist/types.d.ts.map +1 -1
  39. package/package.json +3 -3
  40. package/src/cli.ts +49 -2
  41. package/src/cmd/build/ast.ts +15 -27
  42. package/src/cmd/build/bundler.ts +149 -37
  43. package/src/cmd/build/plugin.ts +8 -8
  44. package/src/cmd/build/workbench-templates.ts +52 -0
  45. package/src/cmd/cloud/deploy.ts +11 -3
  46. package/src/cmd/cloud/deployment/show.ts +60 -17
  47. package/src/cmd/index.ts +2 -0
  48. package/src/schema-generator.ts +1 -1
  49. package/src/schema-parser.ts +19 -4
  50. package/src/steps.ts +27 -4
@@ -3,6 +3,11 @@ import { basename, dirname, relative } from 'node:path';
3
3
  import { generate } from 'astring';
4
4
  import type { BuildMetadata } from '../../types';
5
5
  import { createLogger } from '@agentuity/server';
6
+ import * as ts from 'typescript';
7
+ import type { WorkbenchConfig } from '@agentuity/core';
8
+ import type { LogLevel } from '../../types';
9
+
10
+ const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL || 'info') as LogLevel);
6
11
 
7
12
  interface ASTNode {
8
13
  type: string;
@@ -925,14 +930,6 @@ export async function parseRoute(
925
930
  return routes;
926
931
  }
927
932
 
928
- /**
929
- * Configuration extracted from createWorkbench call
930
- */
931
- export interface WorkbenchConfig {
932
- route: string;
933
- headers?: Record<string, string>;
934
- }
935
-
936
933
  /**
937
934
  * Result of workbench analysis
938
935
  */
@@ -949,15 +946,14 @@ export interface WorkbenchAnalysis {
949
946
  * @param functionName - The function name to check for (e.g., 'createWorkbench')
950
947
  * @returns true if the function is both imported and called
951
948
  */
952
- export async function checkFunctionUsage(content: string, functionName: string): Promise<boolean> {
949
+ export function checkFunctionUsage(content: string, functionName: string): boolean {
953
950
  try {
954
- const ts = await import('typescript');
955
951
  const sourceFile = ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true);
956
952
 
957
953
  let hasImport = false;
958
954
  let hasUsage = false;
959
955
 
960
- function visitNode(node: import('typescript').Node): void {
956
+ function visitNode(node: ts.Node): void {
961
957
  // Check for import declarations with the function
962
958
  if (ts.isImportDeclaration(node) && node.importClause?.namedBindings) {
963
959
  if (ts.isNamedImports(node.importClause.namedBindings)) {
@@ -983,7 +979,7 @@ export async function checkFunctionUsage(content: string, functionName: string):
983
979
  return hasImport && hasUsage;
984
980
  } catch (error) {
985
981
  // Fallback to string check if AST parsing fails
986
- console.warn(`AST parsing failed for ${functionName}, falling back to string check:`, error);
982
+ logger.warn(`AST parsing failed for ${functionName}, falling back to string check:`, error);
987
983
  return content.includes(functionName);
988
984
  }
989
985
  }
@@ -991,17 +987,13 @@ export async function checkFunctionUsage(content: string, functionName: string):
991
987
  /**
992
988
  * Check if app.ts contains conflicting routes for a given endpoint
993
989
  */
994
- export async function checkRouteConflicts(
995
- content: string,
996
- workbenchEndpoint: string
997
- ): Promise<boolean> {
990
+ export function checkRouteConflicts(content: string, workbenchEndpoint: string): boolean {
998
991
  try {
999
- const ts = await import('typescript');
1000
992
  const sourceFile = ts.createSourceFile('app.ts', content, ts.ScriptTarget.Latest, true);
1001
993
 
1002
994
  let hasConflict = false;
1003
995
 
1004
- function visitNode(node: import('typescript').Node): void {
996
+ function visitNode(node: ts.Node): void {
1005
997
  // Check for router.get calls
1006
998
  if (
1007
999
  ts.isCallExpression(node) &&
@@ -1033,16 +1025,15 @@ export async function checkRouteConflicts(
1033
1025
  * @param content - The TypeScript source code
1034
1026
  * @returns workbench analysis including usage and config
1035
1027
  */
1036
- export async function analyzeWorkbench(content: string): Promise<WorkbenchAnalysis> {
1028
+ export function analyzeWorkbench(content: string): WorkbenchAnalysis {
1037
1029
  try {
1038
- const ts = await import('typescript');
1039
1030
  const sourceFile = ts.createSourceFile('app.ts', content, ts.ScriptTarget.Latest, true);
1040
1031
 
1041
1032
  let hasImport = false;
1042
1033
  let hasUsage = false;
1043
1034
  let config: WorkbenchConfig | null = null;
1044
1035
 
1045
- function visitNode(node: import('typescript').Node): void {
1036
+ function visitNode(node: ts.Node): void {
1046
1037
  // Check for import declarations with createWorkbench
1047
1038
  if (ts.isImportDeclaration(node) && node.importClause?.namedBindings) {
1048
1039
  if (ts.isNamedImports(node.importClause.namedBindings)) {
@@ -1062,7 +1053,7 @@ export async function analyzeWorkbench(content: string): Promise<WorkbenchAnalys
1062
1053
  // Extract configuration from the first argument (if any)
1063
1054
  if (node.arguments.length > 0) {
1064
1055
  const configArg = node.arguments[0];
1065
- config = parseConfigObject(configArg, ts);
1056
+ config = parseConfigObject(configArg);
1066
1057
  } else {
1067
1058
  // Default config if no arguments provided
1068
1059
  config = { route: '/workbench' };
@@ -1087,7 +1078,7 @@ export async function analyzeWorkbench(content: string): Promise<WorkbenchAnalys
1087
1078
  };
1088
1079
  } catch (error) {
1089
1080
  // Fallback to simple check if AST parsing fails
1090
- console.warn('Workbench AST parsing failed, falling back to string check:', error);
1081
+ logger.warn('Workbench AST parsing failed, falling back to string check:', error);
1091
1082
  const hasWorkbench = content.includes('createWorkbench');
1092
1083
  return {
1093
1084
  hasWorkbench,
@@ -1099,10 +1090,7 @@ export async function analyzeWorkbench(content: string): Promise<WorkbenchAnalys
1099
1090
  /**
1100
1091
  * Parse a TypeScript object literal to extract configuration
1101
1092
  */
1102
- function parseConfigObject(
1103
- node: import('typescript').Node,
1104
- ts: typeof import('typescript')
1105
- ): WorkbenchConfig | null {
1093
+ function parseConfigObject(node: ts.Node): WorkbenchConfig | null {
1106
1094
  if (!ts.isObjectLiteralExpression(node)) {
1107
1095
  return { route: '/workbench' }; // Default config
1108
1096
  }
@@ -1,6 +1,6 @@
1
1
  import { $ } from 'bun';
2
+ import { z } from 'zod';
2
3
  import { join, relative, resolve, dirname } from 'node:path';
3
- import { createRequire } from 'node:module';
4
4
  import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs';
5
5
  import gitParseUrl from 'git-url-parse';
6
6
  import AgentuityBundler, { getBuildMetadata } from './plugin';
@@ -10,8 +10,37 @@ import type { Project } from '../../types';
10
10
  import { fixDuplicateExportsInDirectory } from './fix-duplicate-exports';
11
11
  import { createLogger } from '@agentuity/server';
12
12
  import type { LogLevel } from '../../types';
13
+ import { generateWorkbenchMainTsx, generateWorkbenchIndexHtml } from './workbench-templates';
14
+ import { analyzeWorkbench } from './ast';
15
+ import { encodeWorkbenchConfig } from '@agentuity/core';
13
16
 
14
- export interface BundleOptions {
17
+ export const DeployOptionsSchema = z.object({
18
+ tag: z
19
+ .array(z.string())
20
+ .default(['latest'])
21
+ .optional()
22
+ .describe('One or more tags to add to the deployment'),
23
+ logsUrl: z.url().optional().describe('The url to the CI build logs'),
24
+ trigger: z
25
+ .enum(['cli', 'workflow', 'webhook'])
26
+ .default('cli')
27
+ .optional()
28
+ .describe('The trigger that caused the build'),
29
+ commitUrl: z.url().optional().describe('The url to the CI commit'),
30
+ provider: z.string().optional().describe('The CI provider name (attempts to autodetect)'),
31
+ event: z
32
+ .enum(['pull_request', 'push', 'manual', 'workflow'])
33
+ .default('manual')
34
+ .optional()
35
+ .describe('The event that triggered the deployment'),
36
+ pullRequestNumber: z.number().optional().describe('the pull request number'),
37
+ pullRequestCommentId: z.string().optional().describe('the pull request comment id'),
38
+ pullRequestURL: z.url().optional().describe('the pull request url'),
39
+ });
40
+
41
+ type DeployOptions = z.infer<typeof DeployOptionsSchema>;
42
+
43
+ export interface BundleOptions extends DeployOptions {
15
44
  rootDir: string;
16
45
  dev?: boolean;
17
46
  env?: Map<string, string>;
@@ -30,6 +59,15 @@ export async function bundle({
30
59
  rootDir,
31
60
  project,
32
61
  port,
62
+ tag,
63
+ logsUrl,
64
+ commitUrl,
65
+ provider,
66
+ trigger,
67
+ event,
68
+ pullRequestNumber,
69
+ pullRequestCommentId,
70
+ pullRequestURL,
33
71
  }: BundleOptions) {
34
72
  const appFile = join(rootDir, 'app.ts');
35
73
  if (!existsSync(appFile)) {
@@ -254,14 +292,12 @@ export async function bundle({
254
292
  }
255
293
 
256
294
  // Bundle workbench app if detected via setupWorkbench
257
- const { analyzeWorkbench } = await import('./ast');
258
295
  if (existsSync(appFile)) {
259
296
  const appContent = await Bun.file(appFile).text();
260
- const analysis = await analyzeWorkbench(appContent);
297
+ const analysis = analyzeWorkbench(appContent);
261
298
 
262
299
  if (analysis.hasWorkbench) {
263
300
  // Encode workbench config for environment variable
264
- const { encodeWorkbenchConfig } = await import('@agentuity/core');
265
301
  const config = analysis.config || { route: '/workbench', headers: {} };
266
302
  // Add port to config (defaults to 3500 if not provided)
267
303
  const configWithPort = { ...config, port: port || 3500 };
@@ -272,41 +308,55 @@ export async function bundle({
272
308
  };
273
309
  const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL as LogLevel) || 'info');
274
310
  try {
275
- const projectRequire = createRequire(resolve(rootDir, 'package.json'));
276
- const workbenchPkgPath = projectRequire.resolve('@agentuity/workbench/package.json');
277
- const workbenchAppDir = join(dirname(workbenchPkgPath), 'src', 'app');
311
+ // Generate workbench files on the fly instead of using files from package
312
+ const tempWorkbenchDir = join(outDir, 'temp-workbench');
313
+ mkdirSync(tempWorkbenchDir, { recursive: true });
278
314
 
279
- if (existsSync(workbenchAppDir)) {
280
- const workbenchIndexFile = join(workbenchAppDir, 'index.html');
281
- if (existsSync(workbenchIndexFile)) {
282
- // Bundle workbench using same config as main web app
283
- const workbenchBuildConfig: Bun.BuildConfig = {
284
- entrypoints: [workbenchIndexFile],
285
- root: workbenchAppDir,
286
- outdir: join(outDir, 'workbench'),
287
- define: workbenchDefine,
288
- sourcemap: dev ? 'inline' : 'linked',
289
- plugins: [AgentuityBundler],
290
- target: 'browser',
291
- format: 'esm',
292
- banner: `// Generated file. DO NOT EDIT`,
293
- minify: true,
294
- splitting: true,
295
- packages: 'bundle',
296
- naming: {
297
- entry: '[dir]/[name].[ext]',
298
- chunk: 'workbench/chunk/[name]-[hash].[ext]',
299
- asset: 'workbench/asset/[name]-[hash].[ext]',
300
- },
301
- };
315
+ // Generate files using templates
316
+ await Bun.write(join(tempWorkbenchDir, 'main.tsx'), generateWorkbenchMainTsx(config));
317
+ const workbenchIndexFile = join(tempWorkbenchDir, 'index.html');
318
+ await Bun.write(workbenchIndexFile, generateWorkbenchIndexHtml());
302
319
 
303
- const workbenchResult = await Bun.build(workbenchBuildConfig);
304
- if (workbenchResult.success) {
305
- logger.debug('Workbench bundled successfully');
306
- } else {
307
- logger.error('Workbench bundling failed:', workbenchResult.logs.join('\n'));
308
- }
320
+ // Bundle workbench using generated files
321
+ const workbenchBuildConfig: Bun.BuildConfig = {
322
+ entrypoints: [workbenchIndexFile],
323
+ root: tempWorkbenchDir,
324
+ outdir: join(outDir, 'workbench'),
325
+ define: workbenchDefine,
326
+ sourcemap: dev ? 'inline' : 'linked',
327
+ plugins: [AgentuityBundler],
328
+ target: 'browser',
329
+ format: 'esm',
330
+ banner: `// Generated file. DO NOT EDIT`,
331
+ minify: true,
332
+ splitting: true,
333
+ packages: 'bundle',
334
+ naming: {
335
+ entry: '[dir]/[name].[ext]',
336
+ chunk: 'workbench/chunk/[name]-[hash].[ext]',
337
+ asset: 'workbench/asset/[name]-[hash].[ext]',
338
+ },
339
+ };
340
+
341
+ const workbenchResult = await Bun.build(workbenchBuildConfig);
342
+ if (workbenchResult.success) {
343
+ logger.debug('Workbench bundled successfully');
344
+ // Clean up temp directory
345
+ rmSync(tempWorkbenchDir, { recursive: true, force: true });
346
+ } else {
347
+ logger.error('Workbench bundling failed. Logs:', workbenchResult.logs);
348
+ if (workbenchResult.logs.length === 0) {
349
+ logger.error('No build logs available. Checking generated files...');
350
+ logger.error('Temp dir exists:', await Bun.file(tempWorkbenchDir).exists());
351
+ logger.error('Index file exists:', await Bun.file(workbenchIndexFile).exists());
352
+ logger.error(
353
+ 'Main.tsx exists:',
354
+ await Bun.file(join(tempWorkbenchDir, 'main.tsx')).exists()
355
+ );
309
356
  }
357
+ // Clean up temp directory even on failure
358
+ rmSync(tempWorkbenchDir, { recursive: true, force: true });
359
+ process.exit(1);
310
360
  }
311
361
  } catch (error) {
312
362
  logger.error('Failed to bundle workbench:', error);
@@ -339,7 +389,14 @@ export async function bundle({
339
389
  repo: process.env.GITHUB_REPOSITORY
340
390
  ? gitParseUrl(process.env.GITHUB_REPOSITORY).toString('https')
341
391
  : '',
392
+ provider: 'git',
342
393
  };
394
+ if (process.env.GITHUB_REPOSITORY) {
395
+ buildmetadata.deployment.git.provider = 'github';
396
+ }
397
+ if (process.env.CI && !trigger) {
398
+ buildmetadata.deployment.git.trigger = 'ci';
399
+ }
343
400
  // pull out the git information if we have it
344
401
  try {
345
402
  let gitDir = join(rootDir, '.git');
@@ -396,6 +453,61 @@ export async function bundle({
396
453
  }
397
454
  }
398
455
 
456
+ // if in gitlab CI, set defaults before user overrides
457
+ if (process.env.GITLAB_CI && buildmetadata?.deployment) {
458
+ buildmetadata.deployment.git ??= {};
459
+ buildmetadata.deployment.git.provider ??= 'gitlab';
460
+ buildmetadata.deployment.git.branch ??= process.env.CI_COMMIT_REF_NAME;
461
+ buildmetadata.deployment.git.commit ??= process.env.CI_COMMIT_SHA;
462
+ buildmetadata.deployment.git.buildUrl ??=
463
+ process.env.CI_JOB_URL ?? process.env.CI_PIPELINE_URL;
464
+ }
465
+
466
+ // configure any overrides or any that aren't detected automatically
467
+ if (buildmetadata?.deployment) {
468
+ buildmetadata.deployment.git ??= {};
469
+
470
+ // build tags: start with existing discovered tags, add defaults, then merge explicit tags
471
+ const tags = new Set(buildmetadata.deployment.git.tags ?? []);
472
+ tags.add('latest');
473
+ if (buildmetadata.deployment.git.branch) {
474
+ tags.add(buildmetadata.deployment.git.branch);
475
+ }
476
+ if (buildmetadata.deployment.git.commit) {
477
+ tags.add(buildmetadata.deployment.git.commit.substring(0, 7));
478
+ }
479
+ if (tag?.length && !(tag.length === 1 && tag[0] === 'latest')) {
480
+ for (const t of tag) {
481
+ tags.add(t);
482
+ }
483
+ tags.delete('latest'); // if you specify explicit tags we remove latest
484
+ }
485
+ buildmetadata.deployment.git.tags = Array.from(tags);
486
+
487
+ if (provider) {
488
+ buildmetadata.deployment.git.provider = provider;
489
+ }
490
+ if (logsUrl) {
491
+ buildmetadata.deployment.git.buildUrl = logsUrl;
492
+ }
493
+ if (commitUrl) {
494
+ buildmetadata.deployment.git.url = commitUrl;
495
+ }
496
+ if (trigger) {
497
+ buildmetadata.deployment.git.trigger = trigger;
498
+ }
499
+ if (event) {
500
+ buildmetadata.deployment.git.event = event;
501
+ }
502
+ if (pullRequestNumber) {
503
+ buildmetadata.deployment.git.pull_request = {
504
+ number: pullRequestNumber,
505
+ url: pullRequestURL,
506
+ commentId: pullRequestCommentId,
507
+ };
508
+ }
509
+ }
510
+
399
511
  await Bun.write(
400
512
  `${outDir}/package.json`,
401
513
  JSON.stringify({ name: pkgContents.name, version: pkgContents.version }, null, 2)
@@ -8,8 +8,8 @@ import {
8
8
  parseEvalMetadata,
9
9
  analyzeWorkbench,
10
10
  checkRouteConflicts,
11
- type WorkbenchConfig,
12
11
  } from './ast';
12
+ import type { WorkbenchConfig } from '@agentuity/core';
13
13
  import { applyPatch, generatePatches } from './patch';
14
14
  import { detectSubagent } from '../../utils/detectSubagent';
15
15
  import { createLogger } from '@agentuity/server';
@@ -35,18 +35,18 @@ async function setupWorkbench(srcDir: string): Promise<WorkbenchConfig | null> {
35
35
  const srcAppFile = join(srcDir, 'app.ts');
36
36
 
37
37
  let appFile = '';
38
- if (existsSync(rootAppFile)) {
38
+ if (await Bun.file(rootAppFile).exists()) {
39
39
  appFile = rootAppFile;
40
- } else if (existsSync(srcAppFile)) {
40
+ } else if (await Bun.file(srcAppFile).exists()) {
41
41
  appFile = srcAppFile;
42
42
  }
43
43
 
44
- if (!appFile || !existsSync(appFile)) {
44
+ if (!appFile || !(await Bun.file(appFile).exists())) {
45
45
  return null;
46
46
  }
47
47
 
48
48
  const appContent = await Bun.file(appFile).text();
49
- const analysis = await analyzeWorkbench(appContent);
49
+ const analysis = analyzeWorkbench(appContent);
50
50
 
51
51
  if (!analysis.hasWorkbench) {
52
52
  return null;
@@ -56,7 +56,7 @@ async function setupWorkbench(srcDir: string): Promise<WorkbenchConfig | null> {
56
56
 
57
57
  // Check for route conflicts if workbench is being used
58
58
  if (workbenchConfig?.route) {
59
- const hasConflict = await checkRouteConflicts(appContent, workbenchConfig.route);
59
+ const hasConflict = checkRouteConflicts(appContent, workbenchConfig.route);
60
60
  if (hasConflict) {
61
61
  const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL as LogLevel) || 'info');
62
62
  logger.error(`🚨 Route conflict detected!\n`);
@@ -573,8 +573,8 @@ const AgentuityBundler: BunPlugin = {
573
573
  // Use the workbench config determined at build time
574
574
  const route = ${JSON.stringify(workbenchConfig?.route || '/workbench')};
575
575
 
576
- // If using custom route, update HTML to point to absolute /workbench/ paths
577
- if (route !== '/workbench') {
576
+ // If using custom route, update HTML to point to absolute /workbench/ paths
577
+ if (route !== '/workbench') {
578
578
  workbenchIndex = workbenchIndex.replace(new RegExp('src="\\\\.\\\\/workbench\\\\/', 'g'), 'src="/workbench/');
579
579
  }
580
580
 
@@ -0,0 +1,52 @@
1
+ import type { WorkbenchConfig } from '@agentuity/core';
2
+
3
+ export function generateWorkbenchMainTsx(config: WorkbenchConfig): string {
4
+ const configString = JSON.stringify(config);
5
+
6
+ return `// Generated workbench entry point
7
+ import React from 'react';
8
+ import { createRoot } from 'react-dom/client';
9
+ import { AgentuityProvider } from '@agentuity/react';
10
+ import { createWorkbench, Workbench } from '@agentuity/workbench';
11
+ import '@agentuity/workbench/styles';
12
+
13
+ // Root element
14
+ const rootElement = document.getElementById('root');
15
+ if (!rootElement) {
16
+ throw new Error('Root element not found');
17
+ }
18
+
19
+ // Create workbench instance with config from bundler
20
+ const workbenchConfig = ${configString};
21
+ const workbench = createWorkbench(workbenchConfig);
22
+
23
+ function App() {
24
+ return (
25
+ <AgentuityProvider baseUrl={window.location.origin}>
26
+ <div className="min-h-screen bg-background text-foreground">
27
+ <Workbench workbench={workbench} />
28
+ </div>
29
+ </AgentuityProvider>
30
+ );
31
+ }
32
+
33
+ // Render the app
34
+ const root = createRoot(rootElement);
35
+ root.render(<App />);
36
+ `;
37
+ }
38
+
39
+ export function generateWorkbenchIndexHtml(): string {
40
+ return `<!DOCTYPE html>
41
+ <html lang="en">
42
+ <head>
43
+ <meta charset="UTF-8">
44
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
45
+ <title>Agentuity Workbench</title>
46
+ </head>
47
+ <body>
48
+ <div id="root"></div>
49
+ <script type="module" src="./main.tsx"></script>
50
+ </body>
51
+ </html>`;
52
+ }
@@ -1,3 +1,4 @@
1
+ import { z } from 'zod';
1
2
  import { join, resolve } from 'node:path';
2
3
  import { createPublicKey } from 'node:crypto';
3
4
  import { createReadStream, createWriteStream } from 'node:fs';
@@ -26,9 +27,9 @@ import {
26
27
  } from '../../env-util';
27
28
  import { zipDir } from '../../utils/zip';
28
29
  import { encryptFIPSKEMDEMStream } from '../../crypto/box';
29
- import { checkCustomDomainForDNS } from './domain';
30
+ import { DeployOptionsSchema } from '../build/bundler';
30
31
  import { getCommand } from '../../command-prefix';
31
- import { z } from 'zod';
32
+ import { checkCustomDomainForDNS } from './domain';
32
33
 
33
34
  const DeployResponseSchema = z.object({
34
35
  success: z.boolean().describe('Whether deployment succeeded'),
@@ -58,17 +59,19 @@ export const deploySubcommand = createSubcommand({
58
59
  examples: [
59
60
  `${getCommand('cloud deploy')} # Deploy current project`,
60
61
  `${getCommand('cloud deploy')} --log-level=debug # Deploy with verbose output`,
62
+ `${getCommand('cloud deploy')} --tag a --tag b # Deploy with specific tags`,
61
63
  ],
62
64
  toplevel: true,
63
65
  idempotent: false,
64
66
  requires: { auth: true, project: true, apiClient: true },
65
67
  prerequisites: ['auth login'],
66
68
  schema: {
69
+ options: DeployOptionsSchema,
67
70
  response: DeployResponseSchema,
68
71
  },
69
72
 
70
73
  async handler(ctx) {
71
- const { project, apiClient, projectDir, config, options } = ctx;
74
+ const { project, apiClient, projectDir, config, options, opts } = ctx;
72
75
 
73
76
  let deployment: Deployment | undefined;
74
77
  let build: BuildMetadata | undefined;
@@ -171,6 +174,11 @@ export const deploySubcommand = createSubcommand({
171
174
  orgId: deployment.orgId,
172
175
  projectId: project.projectId,
173
176
  project,
177
+ commitUrl: opts?.commitUrl,
178
+ logsUrl: opts?.logsUrl,
179
+ provider: opts?.provider,
180
+ trigger: opts?.trigger,
181
+ tag: opts?.tag,
174
182
  });
175
183
  build = await loadBuildMetadata(join(projectDir, '.agentuity'));
176
184
  instructions = await projectDeploymentUpdate(
@@ -15,7 +15,39 @@ const DeploymentShowResponseSchema = z.object({
15
15
  tags: z.array(z.string()).describe('Deployment tags'),
16
16
  customDomains: z.array(z.string()).optional().describe('Custom domains'),
17
17
  cloudRegion: z.string().optional().describe('Cloud region'),
18
- metadata: z.any().optional().describe('Deployment metadata'),
18
+ metadata: z
19
+ .object({
20
+ git: z
21
+ .object({
22
+ repo: z.string().optional(),
23
+ commit: z.string().optional(),
24
+ message: z.string().optional(),
25
+ branch: z.string().optional(),
26
+ url: z.string().optional(),
27
+ trigger: z.string().optional(),
28
+ provider: z.string().optional(),
29
+ event: z.string().optional(),
30
+ buildUrl: z.string().optional(),
31
+ pull_request: z
32
+ .object({
33
+ number: z.number(),
34
+ url: z.string().optional(),
35
+ commentId: z.string().optional(),
36
+ })
37
+ .optional(),
38
+ })
39
+ .optional(),
40
+ build: z
41
+ .object({
42
+ agentuity: z.string().optional(),
43
+ bun: z.string().optional(),
44
+ platform: z.string().optional(),
45
+ arch: z.string().optional(),
46
+ })
47
+ .optional(),
48
+ })
49
+ .optional()
50
+ .describe('Deployment metadata'),
19
51
  });
20
52
 
21
53
  export const showSubcommand = createSubcommand({
@@ -49,8 +81,6 @@ export const showSubcommand = createSubcommand({
49
81
 
50
82
  // Skip TUI output in JSON mode
51
83
  if (!options.json) {
52
- tui.banner(`Deployment ${deployment.id}`, `State: ${deployment.state || 'unknown'}`);
53
-
54
84
  console.log(tui.bold('ID: ') + deployment.id);
55
85
  console.log(tui.bold('Project: ') + projectId);
56
86
  console.log(tui.bold('State: ') + (deployment.state || 'unknown'));
@@ -74,22 +104,35 @@ export const showSubcommand = createSubcommand({
74
104
  console.log(tui.bold('Region: ') + deployment.cloudRegion);
75
105
  }
76
106
 
77
- // Metadata
78
- const origin = deployment.metadata?.origin;
79
- if (origin?.commit) {
107
+ // Git metadata
108
+ const git = deployment.metadata?.git;
109
+ if (git) {
80
110
  tui.newline();
81
- tui.info('Origin Information');
82
- if (origin.trigger) console.log(` Trigger: ${origin.trigger}`);
83
- if (origin.provider) console.log(` Provider: ${origin.provider}`);
84
- if (origin.event) console.log(` Event: ${origin.event}`);
85
- if (origin.branch) console.log(` Branch: ${origin.branch}`);
86
-
87
- if (origin.commit) {
88
- console.log(` Commit: ${origin.commit.hash}`);
89
- if (origin.commit.message) console.log(` Message: ${origin.commit.message}`);
90
- if (origin.commit.author?.name)
91
- console.log(` Author: ${origin.commit.author.name}`);
111
+ tui.info('Git Information');
112
+ if (git.repo) console.log(` Repo: ${git.repo}`);
113
+ if (git.branch) console.log(` Branch: ${git.branch}`);
114
+ if (git.commit) console.log(` Commit: ${git.commit}`);
115
+ if (git.message) console.log(` Message: ${git.message}`);
116
+ if (git.url) console.log(` URL: ${git.url}`);
117
+ if (git.trigger) console.log(` Trigger: ${git.trigger}`);
118
+ if (git.provider) console.log(` Provider: ${git.provider}`);
119
+ if (git.event) console.log(` Event: ${git.event}`);
120
+ if (git.pull_request) {
121
+ console.log(` PR: #${git.pull_request.number}`);
122
+ if (git.pull_request.url) console.log(` PR URL: ${git.pull_request.url}`);
92
123
  }
124
+ if (git.buildUrl) console.log(` Build: ${git.buildUrl}`);
125
+ }
126
+
127
+ // Build metadata
128
+ const build = deployment.metadata?.build;
129
+ if (build) {
130
+ tui.newline();
131
+ tui.info('Build Information');
132
+ if (build.agentuity) console.log(` Agentuity: ${build.agentuity}`);
133
+ if (build.bun) console.log(` Bun: ${build.bun}`);
134
+ if (build.platform) console.log(` Platform: ${build.platform}`);
135
+ if (build.arch) console.log(` Arch: ${build.arch}`);
93
136
  }
94
137
  }
95
138
 
package/src/cmd/index.ts CHANGED
@@ -38,6 +38,8 @@ export async function discoverCommands(): Promise<CommandDefinition[]> {
38
38
  prerequisites: (subcommand as any).prerequisites,
39
39
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
40
  tags: (subcommand as any).tags,
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ examples: (subcommand as any).examples,
41
43
  };
42
44
  commands.push(alias as CommandDefinition);
43
45
  }
@@ -13,7 +13,7 @@ export interface SchemaArgument {
13
13
 
14
14
  export interface SchemaOption {
15
15
  name: string;
16
- type: 'string' | 'number' | 'boolean';
16
+ type: 'string' | 'number' | 'boolean' | 'array';
17
17
  required: boolean;
18
18
  default?: unknown;
19
19
  description?: string;