@agentuity/cli 0.1.11 → 0.1.12

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 (114) hide show
  1. package/dist/auth.d.ts.map +1 -1
  2. package/dist/auth.js +6 -10
  3. package/dist/auth.js.map +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +89 -41
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cmd/auth/login.d.ts.map +1 -1
  8. package/dist/cmd/auth/login.js +49 -5
  9. package/dist/cmd/auth/login.js.map +1 -1
  10. package/dist/cmd/build/patch/_util.d.ts.map +1 -1
  11. package/dist/cmd/build/patch/_util.js +6 -2
  12. package/dist/cmd/build/patch/_util.js.map +1 -1
  13. package/dist/cmd/cloud/agent/get.d.ts.map +1 -1
  14. package/dist/cmd/cloud/agent/get.js +10 -6
  15. package/dist/cmd/cloud/agent/get.js.map +1 -1
  16. package/dist/cmd/cloud/db/create.d.ts.map +1 -1
  17. package/dist/cmd/cloud/db/create.js +14 -3
  18. package/dist/cmd/cloud/db/create.js.map +1 -1
  19. package/dist/cmd/cloud/db/get.d.ts.map +1 -1
  20. package/dist/cmd/cloud/db/get.js +13 -3
  21. package/dist/cmd/cloud/db/get.js.map +1 -1
  22. package/dist/cmd/cloud/db/list.d.ts.map +1 -1
  23. package/dist/cmd/cloud/db/list.js +17 -25
  24. package/dist/cmd/cloud/db/list.js.map +1 -1
  25. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  26. package/dist/cmd/cloud/deployment/show.js +50 -37
  27. package/dist/cmd/cloud/deployment/show.js.map +1 -1
  28. package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
  29. package/dist/cmd/cloud/sandbox/create.js +19 -2
  30. package/dist/cmd/cloud/sandbox/create.js.map +1 -1
  31. package/dist/cmd/cloud/sandbox/execution/get.d.ts.map +1 -1
  32. package/dist/cmd/cloud/sandbox/execution/get.js +14 -11
  33. package/dist/cmd/cloud/sandbox/execution/get.js.map +1 -1
  34. package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
  35. package/dist/cmd/cloud/sandbox/get.js +48 -39
  36. package/dist/cmd/cloud/sandbox/get.js.map +1 -1
  37. package/dist/cmd/cloud/sandbox/list.d.ts.map +1 -1
  38. package/dist/cmd/cloud/sandbox/list.js +6 -1
  39. package/dist/cmd/cloud/sandbox/list.js.map +1 -1
  40. package/dist/cmd/cloud/sandbox/run.d.ts.map +1 -1
  41. package/dist/cmd/cloud/sandbox/run.js +5 -1
  42. package/dist/cmd/cloud/sandbox/run.js.map +1 -1
  43. package/dist/cmd/cloud/sandbox/snapshot/build.d.ts.map +1 -1
  44. package/dist/cmd/cloud/sandbox/snapshot/build.js +39 -23
  45. package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
  46. package/dist/cmd/cloud/sandbox/snapshot/generate.d.ts.map +1 -1
  47. package/dist/cmd/cloud/sandbox/snapshot/generate.js +2 -0
  48. package/dist/cmd/cloud/sandbox/snapshot/generate.js.map +1 -1
  49. package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
  50. package/dist/cmd/cloud/sandbox/snapshot/get.js +10 -7
  51. package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
  52. package/dist/cmd/cloud/session/get.d.ts.map +1 -1
  53. package/dist/cmd/cloud/session/get.js +24 -23
  54. package/dist/cmd/cloud/session/get.js.map +1 -1
  55. package/dist/cmd/cloud/session/list.d.ts.map +1 -1
  56. package/dist/cmd/cloud/session/list.js +7 -2
  57. package/dist/cmd/cloud/session/list.js.map +1 -1
  58. package/dist/cmd/cloud/thread/get.d.ts.map +1 -1
  59. package/dist/cmd/cloud/thread/get.js +11 -8
  60. package/dist/cmd/cloud/thread/get.js.map +1 -1
  61. package/dist/cmd/cloud/thread/list.d.ts.map +1 -1
  62. package/dist/cmd/cloud/thread/list.js +6 -1
  63. package/dist/cmd/cloud/thread/list.js.map +1 -1
  64. package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
  65. package/dist/cmd/dev/file-watcher.js +2 -0
  66. package/dist/cmd/dev/file-watcher.js.map +1 -1
  67. package/dist/cmd/dev/index.d.ts.map +1 -1
  68. package/dist/cmd/dev/index.js +62 -4
  69. package/dist/cmd/dev/index.js.map +1 -1
  70. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  71. package/dist/cmd/project/template-flow.js +15 -1
  72. package/dist/cmd/project/template-flow.js.map +1 -1
  73. package/dist/cmd/setup/index.d.ts.map +1 -1
  74. package/dist/cmd/setup/index.js +40 -3
  75. package/dist/cmd/setup/index.js.map +1 -1
  76. package/dist/config.d.ts.map +1 -1
  77. package/dist/config.js +3 -2
  78. package/dist/config.js.map +1 -1
  79. package/dist/steps.d.ts.map +1 -1
  80. package/dist/steps.js +4 -2
  81. package/dist/steps.js.map +1 -1
  82. package/dist/tui.d.ts +11 -1
  83. package/dist/tui.d.ts.map +1 -1
  84. package/dist/tui.js +47 -12
  85. package/dist/tui.js.map +1 -1
  86. package/package.json +6 -6
  87. package/src/auth.ts +12 -11
  88. package/src/cli.ts +121 -43
  89. package/src/cmd/auth/login.ts +57 -5
  90. package/src/cmd/build/patch/_util.ts +6 -2
  91. package/src/cmd/cloud/agent/get.ts +14 -6
  92. package/src/cmd/cloud/db/create.ts +15 -3
  93. package/src/cmd/cloud/db/get.ts +14 -3
  94. package/src/cmd/cloud/db/list.ts +16 -26
  95. package/src/cmd/cloud/deployment/show.ts +53 -47
  96. package/src/cmd/cloud/sandbox/create.ts +20 -2
  97. package/src/cmd/cloud/sandbox/execution/get.ts +16 -13
  98. package/src/cmd/cloud/sandbox/get.ts +48 -38
  99. package/src/cmd/cloud/sandbox/list.ts +6 -1
  100. package/src/cmd/cloud/sandbox/run.ts +5 -1
  101. package/src/cmd/cloud/sandbox/snapshot/build.ts +51 -24
  102. package/src/cmd/cloud/sandbox/snapshot/generate.ts +2 -0
  103. package/src/cmd/cloud/sandbox/snapshot/get.ts +11 -7
  104. package/src/cmd/cloud/session/get.ts +25 -29
  105. package/src/cmd/cloud/session/list.ts +7 -2
  106. package/src/cmd/cloud/thread/get.ts +12 -8
  107. package/src/cmd/cloud/thread/list.ts +6 -1
  108. package/src/cmd/dev/file-watcher.ts +2 -0
  109. package/src/cmd/dev/index.ts +76 -4
  110. package/src/cmd/project/template-flow.ts +15 -1
  111. package/src/cmd/setup/index.ts +41 -3
  112. package/src/config.ts +3 -2
  113. package/src/steps.ts +4 -2
  114. package/src/tui.ts +61 -12
@@ -1,5 +1,6 @@
1
+ import { z } from 'zod';
1
2
  import { createSubcommand } from '../../types';
2
- import { getAppBaseURL } from '../../api';
3
+ import { getAPIBaseURL, getAppBaseURL } from '../../api';
3
4
  import { saveAuth } from '../../config';
4
5
  import { generateLoginCode, pollForLoginCompletion } from './api';
5
6
  import * as tui from '../../tui';
@@ -17,8 +18,53 @@ export const loginCommand = createSubcommand({
17
18
  { command: getCommand('auth login'), description: 'Login to account' },
18
19
  { command: getCommand('login'), description: 'Login to account' },
19
20
  ],
21
+ schema: {
22
+ options: z.object({
23
+ setupToken: z.string().optional().describe('Use a one-time use setup token'),
24
+ }),
25
+ response: z.object({ success: z.boolean() }),
26
+ },
20
27
  async handler(ctx) {
21
- const { logger, config, apiClient } = ctx;
28
+ const { logger, config, apiClient, opts, options } = ctx;
29
+
30
+ if (opts.setupToken) {
31
+ const url = getAPIBaseURL(config);
32
+ try {
33
+ const res = await fetch(
34
+ `${url}/cli/auth/setup-token/${encodeURIComponent(opts.setupToken)}`,
35
+ {
36
+ signal: AbortSignal.timeout(5000),
37
+ }
38
+ );
39
+ if (res.ok) {
40
+ const result = (await res.json()) as {
41
+ success: boolean;
42
+ message: string;
43
+ data?: { apiKey: string; expiresAt: number; userId: string };
44
+ };
45
+ if (result.success && result.data) {
46
+ await saveAuth({
47
+ apiKey: result.data.apiKey,
48
+ userId: result.data.userId,
49
+ expires: new Date(result.data.expiresAt),
50
+ });
51
+ if (!options.json) {
52
+ tui.success('Welcome to Agentuity! You are now logged in');
53
+ }
54
+ return { success: true };
55
+ }
56
+ } else {
57
+ throw new Error(await res.text());
58
+ }
59
+ } catch (ex) {
60
+ if (options.json) {
61
+ return {
62
+ success: false,
63
+ };
64
+ }
65
+ tui.error(`error validating the setup token: ${ex}`);
66
+ }
67
+ }
22
68
 
23
69
  const appUrl = getAppBaseURL(config);
24
70
 
@@ -32,7 +78,7 @@ export const loginCommand = createSubcommand({
32
78
  });
33
79
 
34
80
  if (!code) {
35
- return;
81
+ return { success: false };
36
82
  }
37
83
 
38
84
  const authURL = `${appUrl}/auth/cli?code=${code}`;
@@ -84,11 +130,17 @@ export const loginCommand = createSubcommand({
84
130
  expires: result.expires,
85
131
  });
86
132
 
87
- tui.newline();
88
- tui.success('Welcome to Agentuity! You are now logged in');
133
+ if (!options.json) {
134
+ tui.newline();
135
+ tui.success('Welcome to Agentuity! You are now logged in');
136
+ }
137
+
138
+ return { success: true };
89
139
  } catch (error) {
90
140
  logger.trace(error);
91
141
  logger.fatal('Login failed: %s', error, ErrorCode.AUTH_FAILED);
92
142
  }
143
+
144
+ return { success: false };
93
145
  },
94
146
  });
@@ -17,9 +17,13 @@ export interface PatchModule {
17
17
 
18
18
  export function generateEnvWarning(envkey: string): string {
19
19
  return `if (process.env.AGENTUITY_ENVIRONMENT === 'development' || process.env.NODE_ENV !== 'production') {
20
- console.error('The required environment variable ${envkey} must be set in your project .env file or in your local system environment.');
20
+ console.error('[ERROR] No credentials found for this AI provider. To fix this, either:');
21
+ console.error(' 1. Login to Agentuity Cloud (agentuity auth login) to use the AI Gateway (recommended)');
22
+ console.error(' 2. Set ${envkey} in your .env file to use the provider directly');
21
23
  } else {
22
- console.error('The required environment variable ${envkey} is required for this project. Use "agentuity env set ${envkey}" to set it and redeploy your project.');
24
+ console.error('[ERROR] The environment variable ${envkey} is required. Either:');
25
+ console.error(' 1. Use Agentuity Cloud AI Gateway by ensuring AGENTUITY_SDK_KEY is configured');
26
+ console.error(' 2. Set ${envkey} using "agentuity env set ${envkey}" and redeploy');
23
27
  }
24
28
  `;
25
29
  }
@@ -43,12 +43,20 @@ export const getSubcommand = createSubcommand({
43
43
  }
44
44
 
45
45
  // Display agent details
46
- console.log(tui.bold('ID: ') + agent.identifier);
47
- console.log(tui.bold('Name: ') + agent.name);
48
- console.log(tui.bold('Description: ') + (agent.description || 'N/A'));
49
- console.log(tui.bold('Dev Mode: ') + (agent.devmode ? 'Yes' : 'No'));
50
- console.log(tui.bold('Created: ') + new Date(agent.createdAt).toLocaleString());
51
- console.log(tui.bold('Updated: ') + new Date(agent.updatedAt).toLocaleString());
46
+ tui.table(
47
+ [
48
+ {
49
+ ID: agent.identifier,
50
+ Name: agent.name,
51
+ Description: agent.description || 'N/A',
52
+ 'Dev Mode': agent.devmode ? 'Yes' : 'No',
53
+ Created: new Date(agent.createdAt).toLocaleString(),
54
+ Updated: new Date(agent.updatedAt).toLocaleString(),
55
+ },
56
+ ],
57
+ ['ID', 'Name', 'Description', 'Dev Mode', 'Created', 'Updated'],
58
+ { layout: 'vertical', padStart: ' ' }
59
+ );
52
60
 
53
61
  // Display metadata if present
54
62
  if (agent.metadata && Object.keys(agent.metadata).length > 0) {
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { createResources, APIError } from '@agentuity/server';
2
+ import { createResources, APIError, validateDatabaseName } from '@agentuity/server';
3
3
  import { createSubcommand as defineSubcommand } from '../../../types';
4
4
  import * as tui from '../../../tui';
5
5
  import { getCatalystAPIClient } from '../../../config';
@@ -24,6 +24,7 @@ export const createSubcommand = defineSubcommand({
24
24
  schema: {
25
25
  options: z.object({
26
26
  name: z.string().optional().describe('Custom database name'),
27
+ description: z.string().optional().describe('Optional database description'),
27
28
  }),
28
29
  response: z.object({
29
30
  success: z.boolean().describe('Whether creation succeeded'),
@@ -34,11 +35,22 @@ export const createSubcommand = defineSubcommand({
34
35
  async handler(ctx) {
35
36
  const { logger, opts, orgId, region, auth, options } = ctx;
36
37
 
38
+ // Validate database name if provided
39
+ if (opts.name) {
40
+ const validation = validateDatabaseName(opts.name);
41
+ if (!validation.valid) {
42
+ tui.fatal(validation.error!, ErrorCode.INVALID_ARGUMENT);
43
+ }
44
+ }
45
+
37
46
  // Handle dry-run mode
38
47
  if (isDryRunMode(options)) {
39
- const message = opts.name
48
+ let message = opts.name
40
49
  ? `Would create database with name: ${opts.name} in region: ${region}`
41
50
  : `Would create database in region: ${region}`;
51
+ if (opts.description) {
52
+ message += ` with description: "${opts.description}"`;
53
+ }
42
54
  outputDryRun(message, options);
43
55
  if (!options.json) {
44
56
  tui.newline();
@@ -58,7 +70,7 @@ export const createSubcommand = defineSubcommand({
58
70
  clearOnSuccess: true,
59
71
  callback: async () => {
60
72
  return await createResources(catalystClient, orgId, region!, [
61
- { type: 'db', name: opts.name },
73
+ { type: 'db', name: opts.name, description: opts.description },
62
74
  ]);
63
75
  },
64
76
  });
@@ -10,6 +10,7 @@ import { getResourceInfo, setResourceInfo } from '../../../cache';
10
10
  const DBGetResponseSchema = z
11
11
  .object({
12
12
  name: z.string().describe('Database name'),
13
+ description: z.string().optional().describe('Database description'),
13
14
  url: z.string().optional().describe('Database connection URL'),
14
15
  })
15
16
  .or(
@@ -171,15 +172,25 @@ export const getSubcommand = createSubcommand({
171
172
  const shouldMask = !options.json && !shouldShowCredentials;
172
173
 
173
174
  if (!options.json) {
174
- console.log(tui.bold('Name: ') + db.name);
175
+ const tableData: Record<string, string> = {
176
+ Name: tui.bold(db.name),
177
+ };
178
+ if (db.description) {
179
+ tableData['Description'] = db.description;
180
+ }
181
+ if (db.cloud_region) {
182
+ tableData['Region'] = db.cloud_region;
183
+ }
175
184
  if (db.url) {
176
- const displayUrl = shouldMask ? tui.maskSecret(db.url) : db.url;
177
- console.log(tui.bold('URL: ') + displayUrl);
185
+ tableData['URL'] = shouldMask ? tui.maskSecret(db.url) : db.url;
178
186
  }
187
+
188
+ tui.table([tableData], Object.keys(tableData), { layout: 'vertical', padStart: ' ' });
179
189
  }
180
190
 
181
191
  return {
182
192
  name: db.name,
193
+ description: db.description ?? undefined,
183
194
  url: db.url ?? undefined,
184
195
  };
185
196
  },
@@ -4,13 +4,13 @@ import { createSubcommand } from '../../../types';
4
4
  import * as tui from '../../../tui';
5
5
  import { getGlobalCatalystAPIClient } from '../../../config';
6
6
  import { getCommand } from '../../../command-prefix';
7
- import { setResourceInfo } from '../../../cache';
8
7
 
9
8
  const DBListResponseSchema = z.object({
10
9
  databases: z
11
10
  .array(
12
11
  z.object({
13
12
  name: z.string().describe('Database name'),
13
+ description: z.string().optional().describe('Database description'),
14
14
  url: z.string().optional().describe('Database connection URL'),
15
15
  cloud_region: z.string().optional().describe('Cloud region where database is hosted'),
16
16
  })
@@ -23,7 +23,7 @@ export const listSubcommand = createSubcommand({
23
23
  aliases: ['ls'],
24
24
  description: 'List database resources',
25
25
  tags: ['read-only', 'fast', 'requires-auth'],
26
- requires: { auth: true, org: true },
26
+ requires: { auth: true },
27
27
  idempotent: true,
28
28
  examples: [
29
29
  { command: getCommand('cloud db list'), description: 'List items' },
@@ -49,24 +49,18 @@ export const listSubcommand = createSubcommand({
49
49
  webUrl: '/services/database',
50
50
 
51
51
  async handler(ctx) {
52
- const { logger, opts, options, orgId, auth, config } = ctx;
52
+ const { logger, opts, options, auth, config } = ctx;
53
53
 
54
54
  const catalystClient = await getGlobalCatalystAPIClient(logger, auth, config?.name);
55
55
 
56
- const profileName = config?.name ?? 'production';
57
56
  const resources = await tui.spinner({
58
- message: `Fetching databases for ${orgId}`,
57
+ message: 'Fetching databases',
59
58
  clearOnSuccess: true,
60
59
  callback: async () => {
61
- return listOrgResources(catalystClient, { type: 'db', orgId });
60
+ return listOrgResources(catalystClient, { type: 'db' });
62
61
  },
63
62
  });
64
63
 
65
- // Cache each database with its region and orgId for future lookups
66
- for (const db of resources.db) {
67
- await setResourceInfo('db', profileName, db.name, db.cloud_region, orgId);
68
- }
69
-
70
64
  // Mask credentials in terminal output by default, unless --show-credentials is passed
71
65
  const shouldShowCredentials = opts.showCredentials === true;
72
66
  const shouldMask = !options.json && !shouldShowCredentials;
@@ -74,29 +68,25 @@ export const listSubcommand = createSubcommand({
74
68
  if (!options.json) {
75
69
  if (resources.db.length === 0) {
76
70
  tui.info('No databases found');
77
- } else {
78
- if (!opts.nameOnly) {
79
- tui.info(tui.bold('Databases'));
80
- tui.newline();
81
- }
71
+ } else if (opts.nameOnly) {
82
72
  for (const db of resources.db) {
83
- if (opts.nameOnly) {
84
- console.log(db.name);
85
- continue;
86
- }
87
- console.log(tui.bold(db.name));
88
- if (db.url) {
89
- const displayUrl = shouldMask ? tui.maskSecret(db.url) : db.url;
90
- console.log(` URL: ${tui.muted(displayUrl)}`);
91
- }
92
- tui.newline();
73
+ console.log(db.name);
93
74
  }
75
+ } else {
76
+ const tableData = resources.db.map((db) => ({
77
+ Name: db.name,
78
+ Description: db.description ?? '',
79
+ Region: db.cloud_region,
80
+ URL: db.url ? (shouldMask ? tui.maskSecret(db.url) : db.url) : '',
81
+ }));
82
+ tui.table(tableData);
94
83
  }
95
84
  }
96
85
 
97
86
  return {
98
87
  databases: resources.db.map((db) => ({
99
88
  name: db.name,
89
+ description: db.description ?? undefined,
100
90
  url: db.url ?? undefined,
101
91
  cloud_region: db.cloud_region,
102
92
  })),
@@ -90,82 +90,88 @@ export const showSubcommand = createSubcommand({
90
90
 
91
91
  // Skip TUI output in JSON mode
92
92
  if (!options.json) {
93
- const maxWidth = 18;
94
- console.log(tui.bold('ID:'.padEnd(maxWidth)) + deployment.id);
95
- console.log(tui.bold('Project:'.padEnd(maxWidth)) + projectId);
96
- console.log(tui.bold('State:'.padEnd(maxWidth)) + (deployment.state || 'unknown'));
97
- console.log(tui.bold('Active:'.padEnd(maxWidth)) + (deployment.active ? 'Yes' : 'No'));
98
- console.log(
99
- tui.bold('Created:'.padEnd(maxWidth)) +
100
- new Date(deployment.createdAt).toLocaleString()
101
- );
93
+ const tableData: Record<string, string> = {
94
+ ID: deployment.id,
95
+ Project: projectId,
96
+ State: deployment.state || 'unknown',
97
+ Active: deployment.active ? 'Yes' : 'No',
98
+ Created: new Date(deployment.createdAt).toLocaleString(),
99
+ };
102
100
  if (deployment.updatedAt) {
103
- console.log(
104
- tui.bold('Updated:'.padEnd(maxWidth)) +
105
- new Date(deployment.updatedAt).toLocaleString()
106
- );
101
+ tableData['Updated'] = new Date(deployment.updatedAt).toLocaleString();
107
102
  }
108
103
  if (deployment.message) {
109
- console.log(tui.bold('Message:'.padEnd(maxWidth)) + deployment.message);
104
+ tableData['Message'] = deployment.message;
110
105
  }
111
106
  if (deployment.tags.length > 0) {
112
- console.log(tui.bold('Tags:'.padEnd(maxWidth)) + deployment.tags.join(', '));
107
+ tableData['Tags'] = deployment.tags.join(', ');
113
108
  }
114
109
  if (deployment.customDomains && deployment.customDomains.length > 0) {
115
- console.log(
116
- tui.bold('Domains:'.padEnd(maxWidth)) + deployment.customDomains.join(', ')
117
- );
110
+ tableData['Domains'] = deployment.customDomains.join(', ');
118
111
  }
119
112
  if (deployment.cloudRegion) {
120
- console.log(tui.bold('Region:'.padEnd(maxWidth)) + deployment.cloudRegion);
113
+ tableData['Region'] = deployment.cloudRegion;
121
114
  }
122
115
  if (deployment.resourceDb) {
123
- console.log(tui.bold('Database:'.padEnd(maxWidth)) + deployment.resourceDb);
116
+ tableData['Database'] = deployment.resourceDb;
124
117
  }
125
118
  if (deployment.resourceStorage) {
126
- console.log(tui.bold('Storage:'.padEnd(maxWidth)) + deployment.resourceStorage);
119
+ tableData['Storage'] = deployment.resourceStorage;
127
120
  }
128
121
  if (deployment.deploymentLogsURL) {
129
- console.log(
130
- tui.bold('Deployment Logs:'.padEnd(maxWidth)) +
131
- tui.link(deployment.deploymentLogsURL)
132
- );
122
+ tableData['Deployment Logs'] = tui.link(deployment.deploymentLogsURL);
133
123
  }
134
124
  if (deployment.buildLogsURL) {
135
- console.log(
136
- tui.bold('Build Logs:'.padEnd(maxWidth)) + tui.link(deployment.buildLogsURL)
137
- );
125
+ tableData['Build Logs'] = tui.link(deployment.buildLogsURL);
138
126
  }
139
127
 
128
+ tui.table([tableData], Object.keys(tableData), { layout: 'vertical', padStart: ' ' });
129
+
140
130
  // Git metadata
141
131
  const git = deployment.metadata?.git;
142
132
  if (git) {
143
- tui.newline();
144
- tui.info('Git Information');
145
- if (git.repo) console.log(` Repo: ${git.repo}`);
146
- if (git.branch) console.log(` Branch: ${git.branch}`);
147
- if (git.commit) console.log(` Commit: ${git.commit}`);
148
- if (git.message) console.log(` Message: ${git.message}`);
149
- if (git.url) console.log(` URL: ${git.url}`);
150
- if (git.trigger) console.log(` Trigger: ${git.trigger}`);
151
- if (git.provider) console.log(` Provider: ${git.provider}`);
152
- if (git.event) console.log(` Event: ${git.event}`);
133
+ const gitData: Record<string, string> = {};
134
+ if (git.repo) gitData['Repo'] = git.repo;
135
+ if (git.branch) gitData['Branch'] = git.branch;
136
+ if (git.commit) gitData['Commit'] = git.commit;
137
+ if (git.message) gitData['Message'] = git.message;
138
+ if (git.url) gitData['URL'] = git.url;
139
+ if (git.trigger) gitData['Trigger'] = git.trigger;
140
+ if (git.provider) gitData['Provider'] = git.provider;
141
+ if (git.event) gitData['Event'] = git.event;
153
142
  if (git.pull_request) {
154
- console.log(` PR: #${git.pull_request.number}`);
155
- if (git.pull_request.url) console.log(` PR URL: ${git.pull_request.url}`);
143
+ gitData['PR'] = `#${git.pull_request.number}`;
144
+ if (git.pull_request.url) gitData['PR URL'] = git.pull_request.url;
145
+ }
146
+ if (git.buildUrl) gitData['Build'] = git.buildUrl;
147
+
148
+ if (Object.keys(gitData).length > 0) {
149
+ tui.newline();
150
+ tui.info('Git Information');
151
+ tui.table([gitData], Object.keys(gitData), {
152
+ layout: 'vertical',
153
+ padStart: ' ',
154
+ });
156
155
  }
157
- if (git.buildUrl) console.log(` Build: ${git.buildUrl}`);
158
156
  }
159
157
 
160
158
  // Build metadata
161
159
  const build = deployment.metadata?.build;
162
160
  if (build) {
163
- tui.newline();
164
- tui.info('Build Information');
165
- if (build.agentuity) console.log(` Agentuity: ${build.agentuity}`);
166
- if (build.bun) console.log(` Bun: ${build.bun}`);
167
- if (build.platform) console.log(` Platform: ${build.platform}`);
168
- if (build.arch) console.log(` Arch: ${build.arch}`);
161
+ const buildData: Record<string, string> = {};
162
+ if (build.agentuity) buildData['Agentuity'] = build.agentuity;
163
+ if (build.bun) buildData['Bun'] = build.bun;
164
+ if (build.platform) buildData['Platform'] = build.platform;
165
+ if (build.arch) buildData['Arch'] = build.arch;
166
+
167
+ if (Object.keys(buildData).length > 0) {
168
+ tui.newline();
169
+ tui.info('Build Information');
170
+ tui.table([buildData], Object.keys(buildData), {
171
+ layout: 'vertical',
172
+ padStart: ' ',
173
+ });
174
+ }
169
175
  }
170
176
  }
171
177
 
@@ -25,6 +25,7 @@ export const createSubcommand = createCommand({
25
25
  description: 'Create an interactive sandbox for multiple executions',
26
26
  tags: ['slow', 'requires-auth'],
27
27
  requires: { auth: true, region: true, org: true },
28
+ optional: { project: true },
28
29
  examples: [
29
30
  {
30
31
  command: getCommand('cloud sandbox create'),
@@ -46,6 +47,10 @@ export const createSubcommand = createCommand({
46
47
  command: getCommand('cloud sandbox create --env KEY=VAL'),
47
48
  description: 'Create a sandbox with a specific environment variable',
48
49
  },
50
+ {
51
+ command: getCommand('cloud sandbox create --project-id proj_123'),
52
+ description: 'Create a sandbox associated with a specific project',
53
+ },
49
54
  ],
50
55
  schema: {
51
56
  options: z.object({
@@ -72,12 +77,21 @@ export const createSubcommand = createCommand({
72
77
  .optional()
73
78
  .describe('Apt packages to install (can be specified multiple times)'),
74
79
  metadata: z.string().optional().describe('JSON object of user-defined metadata'),
80
+ port: z
81
+ .number()
82
+ .int()
83
+ .min(1024)
84
+ .max(65535)
85
+ .optional()
86
+ .describe('Port to expose from the sandbox to the outside Internet (1024-65535)'),
87
+ projectId: z.string().optional().describe('Project ID to associate this sandbox with'),
75
88
  }),
76
89
  response: SandboxCreateResponseSchema,
77
90
  },
78
91
 
79
92
  async handler(ctx) {
80
- const { opts, options, auth, region, config, logger, orgId } = ctx;
93
+ const { opts, options, auth, region, config, logger, orgId, project } = ctx;
94
+ const projectId = opts.projectId || project?.projectId;
81
95
  const client = createSandboxClient(logger, auth, region);
82
96
  const started = Date.now();
83
97
 
@@ -152,6 +166,7 @@ export const createSubcommand = createCommand({
152
166
 
153
167
  const result = await sandboxCreate(client, {
154
168
  options: {
169
+ projectId,
155
170
  runtime: opts.runtime,
156
171
  runtimeId: opts.runtimeId,
157
172
  name: opts.name,
@@ -164,7 +179,10 @@ export const createSubcommand = createCommand({
164
179
  disk: opts.disk,
165
180
  }
166
181
  : undefined,
167
- network: opts.network ? { enabled: true } : undefined,
182
+ network:
183
+ opts.network || opts.port
184
+ ? { enabled: opts.network || opts.port !== undefined, port: opts.port }
185
+ : undefined,
168
186
  timeout: opts.idleTimeout ? { idle: opts.idleTimeout } : undefined,
169
187
  env: Object.keys(envMap).length > 0 ? envMap : undefined,
170
188
  command: hasFiles ? { exec: [], files } : undefined,
@@ -55,36 +55,39 @@ export const getSubcommand = createCommand({
55
55
  ? tui.colorError
56
56
  : tui.colorMuted;
57
57
 
58
- console.log(`${tui.muted('Execution:')} ${tui.bold(result.executionId)}`);
59
- console.log(`${tui.muted('Sandbox:')} ${result.sandboxId}`);
60
- console.log(`${tui.muted('Status:')} ${statusColor(result.status)}`);
58
+ const tableData: Record<string, string | number> = {
59
+ Execution: tui.bold(result.executionId),
60
+ Sandbox: result.sandboxId,
61
+ Status: statusColor(result.status),
62
+ };
63
+
61
64
  if (result.exitCode !== undefined) {
62
65
  const exitCodeColor = result.exitCode === 0 ? tui.colorSuccess : tui.colorError;
63
- console.log(
64
- `${tui.muted('Exit Code:')} ${exitCodeColor(String(result.exitCode))}`
65
- );
66
+ tableData['Exit Code'] = exitCodeColor(String(result.exitCode));
66
67
  }
67
68
  if (result.durationMs !== undefined) {
68
- console.log(`${tui.muted('Duration:')} ${result.durationMs}ms`);
69
+ tableData['Duration'] = `${result.durationMs}ms`;
69
70
  }
70
71
  if (result.startedAt) {
71
- console.log(`${tui.muted('Started:')} ${result.startedAt}`);
72
+ tableData['Started'] = result.startedAt;
72
73
  }
73
74
  if (result.completedAt) {
74
- console.log(`${tui.muted('Completed:')} ${result.completedAt}`);
75
+ tableData['Completed'] = result.completedAt;
75
76
  }
76
77
  if (result.error) {
77
- console.log(`${tui.muted('Error:')} ${tui.colorError(result.error)}`);
78
+ tableData['Error'] = tui.colorError(result.error);
78
79
  }
79
80
  if (result.stdoutStreamUrl) {
80
- console.log(`${tui.muted('Stdout:')} ${result.stdoutStreamUrl}`);
81
+ tableData['Stdout'] = result.stdoutStreamUrl;
81
82
  }
82
83
  if (result.stderrStreamUrl) {
83
- console.log(`${tui.muted('Stderr:')} ${result.stderrStreamUrl}`);
84
+ tableData['Stderr'] = result.stderrStreamUrl;
84
85
  }
85
86
  if (result.command && result.command.length > 0) {
86
- console.log(`${tui.muted('Command:')} ${result.command.join(' ')}`);
87
+ tableData['Command'] = result.command.join(' ');
87
88
  }
89
+
90
+ tui.table([tableData], Object.keys(tableData), { layout: 'vertical', padStart: ' ' });
88
91
  }
89
92
 
90
93
  return {