@agentuity/cli 0.0.105 → 0.0.106

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 (204) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +93 -21
  3. package/dist/cli.js.map +1 -1
  4. package/dist/cmd/build/ast.d.ts.map +1 -1
  5. package/dist/cmd/build/ast.js +130 -4
  6. package/dist/cmd/build/ast.js.map +1 -1
  7. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  8. package/dist/cmd/build/entry-generator.js +8 -2
  9. package/dist/cmd/build/entry-generator.js.map +1 -1
  10. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  11. package/dist/cmd/build/vite/registry-generator.js +8 -9
  12. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  13. package/dist/cmd/cloud/db/create.d.ts.map +1 -1
  14. package/dist/cmd/cloud/db/create.js +11 -2
  15. package/dist/cmd/cloud/db/create.js.map +1 -1
  16. package/dist/cmd/cloud/db/delete.d.ts.map +1 -1
  17. package/dist/cmd/cloud/db/delete.js +13 -2
  18. package/dist/cmd/cloud/db/delete.js.map +1 -1
  19. package/dist/cmd/cloud/deploy.js +3 -3
  20. package/dist/cmd/cloud/deploy.js.map +1 -1
  21. package/dist/cmd/cloud/env/delete.js +1 -1
  22. package/dist/cmd/cloud/env/delete.js.map +1 -1
  23. package/dist/cmd/cloud/env/import.js +4 -4
  24. package/dist/cmd/cloud/env/import.js.map +1 -1
  25. package/dist/cmd/cloud/env/pull.d.ts.map +1 -1
  26. package/dist/cmd/cloud/env/pull.js +7 -9
  27. package/dist/cmd/cloud/env/pull.js.map +1 -1
  28. package/dist/cmd/cloud/env/push.js +2 -2
  29. package/dist/cmd/cloud/env/push.js.map +1 -1
  30. package/dist/cmd/cloud/env/set.js +3 -3
  31. package/dist/cmd/cloud/env/set.js.map +1 -1
  32. package/dist/cmd/cloud/index.d.ts.map +1 -1
  33. package/dist/cmd/cloud/index.js +2 -0
  34. package/dist/cmd/cloud/index.js.map +1 -1
  35. package/dist/cmd/cloud/sandbox/cp.d.ts +3 -0
  36. package/dist/cmd/cloud/sandbox/cp.d.ts.map +1 -0
  37. package/dist/cmd/cloud/sandbox/cp.js +334 -0
  38. package/dist/cmd/cloud/sandbox/cp.js.map +1 -0
  39. package/dist/cmd/cloud/sandbox/create.d.ts +3 -0
  40. package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -0
  41. package/dist/cmd/cloud/sandbox/create.js +105 -0
  42. package/dist/cmd/cloud/sandbox/create.js.map +1 -0
  43. package/dist/cmd/cloud/sandbox/delete.d.ts +3 -0
  44. package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -0
  45. package/dist/cmd/cloud/sandbox/delete.js +72 -0
  46. package/dist/cmd/cloud/sandbox/delete.js.map +1 -0
  47. package/dist/cmd/cloud/sandbox/exec.d.ts +3 -0
  48. package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -0
  49. package/dist/cmd/cloud/sandbox/exec.js +211 -0
  50. package/dist/cmd/cloud/sandbox/exec.js.map +1 -0
  51. package/dist/cmd/cloud/sandbox/execution/get.d.ts +3 -0
  52. package/dist/cmd/cloud/sandbox/execution/get.d.ts.map +1 -0
  53. package/dist/cmd/cloud/sandbox/execution/get.js +96 -0
  54. package/dist/cmd/cloud/sandbox/execution/get.js.map +1 -0
  55. package/dist/cmd/cloud/sandbox/execution/index.d.ts +3 -0
  56. package/dist/cmd/cloud/sandbox/execution/index.d.ts.map +1 -0
  57. package/dist/cmd/cloud/sandbox/execution/index.js +24 -0
  58. package/dist/cmd/cloud/sandbox/execution/index.js.map +1 -0
  59. package/dist/cmd/cloud/sandbox/execution/list.d.ts +3 -0
  60. package/dist/cmd/cloud/sandbox/execution/list.d.ts.map +1 -0
  61. package/dist/cmd/cloud/sandbox/execution/list.js +100 -0
  62. package/dist/cmd/cloud/sandbox/execution/list.js.map +1 -0
  63. package/dist/cmd/cloud/sandbox/get.d.ts +3 -0
  64. package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -0
  65. package/dist/cmd/cloud/sandbox/get.js +95 -0
  66. package/dist/cmd/cloud/sandbox/get.js.map +1 -0
  67. package/dist/cmd/cloud/sandbox/index.d.ts +3 -0
  68. package/dist/cmd/cloud/sandbox/index.d.ts.map +1 -0
  69. package/dist/cmd/cloud/sandbox/index.js +45 -0
  70. package/dist/cmd/cloud/sandbox/index.js.map +1 -0
  71. package/dist/cmd/cloud/sandbox/list.d.ts +3 -0
  72. package/dist/cmd/cloud/sandbox/list.d.ts.map +1 -0
  73. package/dist/cmd/cloud/sandbox/list.js +120 -0
  74. package/dist/cmd/cloud/sandbox/list.js.map +1 -0
  75. package/dist/cmd/cloud/sandbox/run.d.ts +3 -0
  76. package/dist/cmd/cloud/sandbox/run.d.ts.map +1 -0
  77. package/dist/cmd/cloud/sandbox/run.js +152 -0
  78. package/dist/cmd/cloud/sandbox/run.js.map +1 -0
  79. package/dist/cmd/cloud/sandbox/snapshot/create.d.ts +3 -0
  80. package/dist/cmd/cloud/sandbox/snapshot/create.d.ts.map +1 -0
  81. package/dist/cmd/cloud/sandbox/snapshot/create.js +65 -0
  82. package/dist/cmd/cloud/sandbox/snapshot/create.js.map +1 -0
  83. package/dist/cmd/cloud/sandbox/snapshot/delete.d.ts +3 -0
  84. package/dist/cmd/cloud/sandbox/snapshot/delete.d.ts.map +1 -0
  85. package/dist/cmd/cloud/sandbox/snapshot/delete.js +66 -0
  86. package/dist/cmd/cloud/sandbox/snapshot/delete.js.map +1 -0
  87. package/dist/cmd/cloud/sandbox/snapshot/get.d.ts +3 -0
  88. package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -0
  89. package/dist/cmd/cloud/sandbox/snapshot/get.js +154 -0
  90. package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -0
  91. package/dist/cmd/cloud/sandbox/snapshot/index.d.ts +3 -0
  92. package/dist/cmd/cloud/sandbox/snapshot/index.d.ts.map +1 -0
  93. package/dist/cmd/cloud/sandbox/snapshot/index.js +27 -0
  94. package/dist/cmd/cloud/sandbox/snapshot/index.js.map +1 -0
  95. package/dist/cmd/cloud/sandbox/snapshot/list.d.ts +3 -0
  96. package/dist/cmd/cloud/sandbox/snapshot/list.d.ts.map +1 -0
  97. package/dist/cmd/cloud/sandbox/snapshot/list.js +83 -0
  98. package/dist/cmd/cloud/sandbox/snapshot/list.js.map +1 -0
  99. package/dist/cmd/cloud/sandbox/snapshot/tag.d.ts +3 -0
  100. package/dist/cmd/cloud/sandbox/snapshot/tag.d.ts.map +1 -0
  101. package/dist/cmd/cloud/sandbox/snapshot/tag.js +63 -0
  102. package/dist/cmd/cloud/sandbox/snapshot/tag.js.map +1 -0
  103. package/dist/cmd/cloud/sandbox/util.d.ts +15 -0
  104. package/dist/cmd/cloud/sandbox/util.d.ts.map +1 -0
  105. package/dist/cmd/cloud/sandbox/util.js +50 -0
  106. package/dist/cmd/cloud/sandbox/util.js.map +1 -0
  107. package/dist/cmd/cloud/secret/delete.d.ts.map +1 -1
  108. package/dist/cmd/cloud/secret/delete.js +3 -3
  109. package/dist/cmd/cloud/secret/delete.js.map +1 -1
  110. package/dist/cmd/cloud/secret/import.js +6 -6
  111. package/dist/cmd/cloud/secret/import.js.map +1 -1
  112. package/dist/cmd/cloud/secret/index.d.ts.map +1 -1
  113. package/dist/cmd/cloud/secret/index.js +1 -0
  114. package/dist/cmd/cloud/secret/index.js.map +1 -1
  115. package/dist/cmd/cloud/secret/pull.d.ts.map +1 -1
  116. package/dist/cmd/cloud/secret/pull.js +7 -9
  117. package/dist/cmd/cloud/secret/pull.js.map +1 -1
  118. package/dist/cmd/cloud/secret/push.js +3 -3
  119. package/dist/cmd/cloud/secret/push.js.map +1 -1
  120. package/dist/cmd/cloud/secret/set.d.ts.map +1 -1
  121. package/dist/cmd/cloud/secret/set.js +3 -3
  122. package/dist/cmd/cloud/secret/set.js.map +1 -1
  123. package/dist/cmd/cloud/storage/create.d.ts.map +1 -1
  124. package/dist/cmd/cloud/storage/create.js +13 -2
  125. package/dist/cmd/cloud/storage/create.js.map +1 -1
  126. package/dist/cmd/cloud/storage/delete.d.ts.map +1 -1
  127. package/dist/cmd/cloud/storage/delete.js +13 -2
  128. package/dist/cmd/cloud/storage/delete.js.map +1 -1
  129. package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
  130. package/dist/cmd/cloud/stream/list.js +2 -13
  131. package/dist/cmd/cloud/stream/list.js.map +1 -1
  132. package/dist/cmd/dev/index.d.ts.map +1 -1
  133. package/dist/cmd/dev/index.js +1 -0
  134. package/dist/cmd/dev/index.js.map +1 -1
  135. package/dist/cmd/profile/create.d.ts.map +1 -1
  136. package/dist/cmd/profile/create.js +1 -0
  137. package/dist/cmd/profile/create.js.map +1 -1
  138. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  139. package/dist/cmd/project/template-flow.js +27 -10
  140. package/dist/cmd/project/template-flow.js.map +1 -1
  141. package/dist/config.d.ts +0 -2
  142. package/dist/config.d.ts.map +1 -1
  143. package/dist/config.js +3 -0
  144. package/dist/config.js.map +1 -1
  145. package/dist/env-util.d.ts +16 -8
  146. package/dist/env-util.d.ts.map +1 -1
  147. package/dist/env-util.js +46 -18
  148. package/dist/env-util.js.map +1 -1
  149. package/dist/tui.d.ts +20 -3
  150. package/dist/tui.d.ts.map +1 -1
  151. package/dist/tui.js +82 -23
  152. package/dist/tui.js.map +1 -1
  153. package/dist/types.d.ts +18 -4
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/types.js +1 -0
  156. package/dist/types.js.map +1 -1
  157. package/package.json +4 -4
  158. package/src/cli.ts +99 -21
  159. package/src/cmd/build/ast.ts +163 -4
  160. package/src/cmd/build/entry-generator.ts +8 -2
  161. package/src/cmd/build/vite/registry-generator.ts +8 -11
  162. package/src/cmd/cloud/db/create.ts +13 -2
  163. package/src/cmd/cloud/db/delete.ts +15 -2
  164. package/src/cmd/cloud/deploy.ts +3 -3
  165. package/src/cmd/cloud/env/delete.ts +1 -1
  166. package/src/cmd/cloud/env/import.ts +4 -4
  167. package/src/cmd/cloud/env/pull.ts +7 -16
  168. package/src/cmd/cloud/env/push.ts +2 -2
  169. package/src/cmd/cloud/env/set.ts +3 -3
  170. package/src/cmd/cloud/index.ts +2 -0
  171. package/src/cmd/cloud/sandbox/cp.ts +531 -0
  172. package/src/cmd/cloud/sandbox/create.ts +114 -0
  173. package/src/cmd/cloud/sandbox/delete.ts +80 -0
  174. package/src/cmd/cloud/sandbox/exec.ts +254 -0
  175. package/src/cmd/cloud/sandbox/execution/get.ts +106 -0
  176. package/src/cmd/cloud/sandbox/execution/index.ts +25 -0
  177. package/src/cmd/cloud/sandbox/execution/list.ts +111 -0
  178. package/src/cmd/cloud/sandbox/get.ts +104 -0
  179. package/src/cmd/cloud/sandbox/index.ts +46 -0
  180. package/src/cmd/cloud/sandbox/list.ts +129 -0
  181. package/src/cmd/cloud/sandbox/run.ts +170 -0
  182. package/src/cmd/cloud/sandbox/snapshot/create.ts +71 -0
  183. package/src/cmd/cloud/sandbox/snapshot/delete.ts +74 -0
  184. package/src/cmd/cloud/sandbox/snapshot/get.ts +188 -0
  185. package/src/cmd/cloud/sandbox/snapshot/index.ts +28 -0
  186. package/src/cmd/cloud/sandbox/snapshot/list.ts +90 -0
  187. package/src/cmd/cloud/sandbox/snapshot/tag.ts +70 -0
  188. package/src/cmd/cloud/sandbox/util.ts +59 -0
  189. package/src/cmd/cloud/secret/delete.ts +8 -3
  190. package/src/cmd/cloud/secret/import.ts +6 -6
  191. package/src/cmd/cloud/secret/index.ts +1 -0
  192. package/src/cmd/cloud/secret/pull.ts +7 -16
  193. package/src/cmd/cloud/secret/push.ts +3 -3
  194. package/src/cmd/cloud/secret/set.ts +8 -3
  195. package/src/cmd/cloud/storage/create.ts +15 -2
  196. package/src/cmd/cloud/storage/delete.ts +15 -2
  197. package/src/cmd/cloud/stream/list.ts +2 -9
  198. package/src/cmd/dev/index.ts +1 -0
  199. package/src/cmd/profile/create.ts +1 -0
  200. package/src/cmd/project/template-flow.ts +29 -13
  201. package/src/config.ts +3 -0
  202. package/src/env-util.ts +52 -21
  203. package/src/tui.ts +131 -39
  204. package/src/types.ts +18 -16
package/src/cli.ts CHANGED
@@ -141,10 +141,17 @@ async function executeOrValidate(
141
141
  /**
142
142
  * Format a user-friendly message for a validation issue
143
143
  */
144
- function formatValidationIssueMessage(field: string, message: string): string {
144
+ function formatValidationIssueMessage(
145
+ field: string,
146
+ message: string,
147
+ isArg: boolean = false
148
+ ): string {
145
149
  // Detect "expected X, received undefined" pattern (missing required value)
146
150
  if (message.includes('received undefined')) {
147
151
  if (field && field !== 'unknown') {
152
+ if (isArg) {
153
+ return `Missing required argument: <${field}>`;
154
+ }
148
155
  return `Missing required option: --${field}`;
149
156
  }
150
157
  return 'Missing required value';
@@ -155,6 +162,9 @@ function formatValidationIssueMessage(field: string, message: string): string {
155
162
  if (typeMatch) {
156
163
  const [, expected, received] = typeMatch;
157
164
  if (field && field !== 'unknown') {
165
+ if (isArg) {
166
+ return `Invalid value for <${field}>: expected ${expected}, got ${received}`;
167
+ }
158
168
  return `Invalid value for --${field}: expected ${expected}, got ${received}`;
159
169
  }
160
170
  return `Invalid value: expected ${expected}, got ${received}`;
@@ -162,25 +172,69 @@ function formatValidationIssueMessage(field: string, message: string): string {
162
172
 
163
173
  // Default: include the field name if we have it
164
174
  if (field && field !== 'unknown') {
175
+ if (isArg) {
176
+ return `<${field}>: ${message}`;
177
+ }
165
178
  return `--${field}: ${message}`;
166
179
  }
167
180
  return message;
168
181
  }
169
182
 
183
+ /**
184
+ * Custom error class to wrap ZodErrors with context about whether they are for args or options
185
+ */
186
+ class SchemaValidationError extends Error {
187
+ constructor(
188
+ public readonly originalError: unknown,
189
+ public readonly isArg: boolean
190
+ ) {
191
+ super('Schema validation error');
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Parse args schema and wrap any ZodError with context
197
+ */
198
+ function parseArgs<T>(schema: { parse: (input: unknown) => T }, input: unknown): T {
199
+ try {
200
+ return schema.parse(input);
201
+ } catch (error) {
202
+ if (error && typeof error === 'object' && 'issues' in error) {
203
+ throw new SchemaValidationError(error, true);
204
+ }
205
+ throw error;
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Parse options schema (no wrapping needed, isArg defaults to false)
211
+ */
212
+ function parseOptions<T>(schema: { parse: (input: unknown) => T }, input: unknown): T {
213
+ return schema.parse(input);
214
+ }
215
+
170
216
  function handleValidationError(
171
217
  error: unknown,
172
218
  commandName: string,
173
219
  baseCtx: { options: GlobalOptions; logger: Logger }
174
220
  ): never {
175
- if (error && typeof error === 'object' && 'issues' in error) {
176
- const issues = (error as { issues: Array<{ path: string[]; message: string }> }).issues;
221
+ // Unwrap SchemaValidationError to get context about whether it's an arg or option
222
+ let actualError = error;
223
+ let isArg = false;
224
+ if (error instanceof SchemaValidationError) {
225
+ actualError = error.originalError;
226
+ isArg = error.isArg;
227
+ }
228
+
229
+ if (actualError && typeof actualError === 'object' && 'issues' in actualError) {
230
+ const issues = (actualError as { issues: Array<{ path: string[]; message: string }> }).issues;
177
231
 
178
232
  const formattedIssues = issues.map((issue) => {
179
233
  const field = issue.path?.length ? issue.path.join('.') : 'unknown';
180
234
  return {
181
235
  field,
182
236
  message: issue.message,
183
- formatted: formatValidationIssueMessage(field, issue.message),
237
+ formatted: formatValidationIssueMessage(field, issue.message, isArg),
184
238
  };
185
239
  });
186
240
 
@@ -227,6 +281,22 @@ type Normalized = {
227
281
  optionalRegion: boolean;
228
282
  };
229
283
 
284
+ /**
285
+ * Get the full command path for a command (e.g., "cloud sandbox snapshot delete")
286
+ * Uses Commander's _getCommandAndAncestors to traverse the command hierarchy.
287
+ */
288
+ function getFullCommandPath(cmd: Command): string {
289
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
290
+ const ancestors = (cmd as any)._getCommandAndAncestors() as Command[];
291
+ // ancestors is [current, parent, grandparent, ...root] - reverse and skip root program name
292
+ const names = ancestors.map((c) => c.name()).reverse();
293
+ // Skip the first entry if it's the root program (usually empty or 'agentuity')
294
+ if (names.length > 1 && (names[0] === '' || names[0] === 'agentuity')) {
295
+ return names.slice(1).join(' ');
296
+ }
297
+ return names.join(' ');
298
+ }
299
+
230
300
  function normalizeReqs(def: CommandDefinition | SubcommandDefinition): Normalized {
231
301
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
232
302
  const d: any = def as any;
@@ -802,6 +872,7 @@ async function registerSubcommand(
802
872
 
803
873
  if (subcommand.schema?.options) {
804
874
  const parsed = parseOptionsSchema(subcommand.schema.options);
875
+ const aliases = subcommand.schema.aliases ?? {};
805
876
  for (const opt of parsed) {
806
877
  const flag = opt.name
807
878
  .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
@@ -814,8 +885,15 @@ async function registerSubcommand(
814
885
  }
815
886
 
816
887
  const desc = opt.description || '';
817
- // Add short flag alias for verbose
818
- const flagSpec = flag === 'verbose' ? `-v, --${flag}` : `--${flag}`;
888
+ // Build flag spec with aliases (check both camelCase and kebab-case names)
889
+ const optAliases = aliases[opt.name] ?? aliases[flag] ?? [];
890
+ let flagSpec = `--${flag}`;
891
+ if (flag === 'verbose') {
892
+ flagSpec = `-v, --${flag}`;
893
+ } else if (optAliases.length > 0) {
894
+ const shortFlags = optAliases.map((a) => `-${a}`).join(', ');
895
+ flagSpec = `${shortFlags}, --${flag}`;
896
+ }
819
897
  if (opt.type === 'boolean') {
820
898
  if (opt.hasDefault) {
821
899
  const defaultValue =
@@ -1019,10 +1097,10 @@ async function registerSubcommand(
1019
1097
  ctx.projectDir = projectDir;
1020
1098
  }
1021
1099
  if (subcommand.schema.args) {
1022
- ctx.args = subcommand.schema.args.parse(input.args);
1100
+ ctx.args = parseArgs(subcommand.schema.args, input.args);
1023
1101
  }
1024
1102
  if (subcommand.schema.options) {
1025
- ctx.opts = subcommand.schema.options.parse(input.options);
1103
+ ctx.opts = parseOptions(subcommand.schema.options, input.options);
1026
1104
  }
1027
1105
  if (normalized.requiresAPIClient) {
1028
1106
  // Recreate apiClient with auth credentials
@@ -1059,14 +1137,14 @@ async function registerSubcommand(
1059
1137
  }
1060
1138
  await executeOrValidate(
1061
1139
  ctx as CommandContext,
1062
- `${parent.name()} ${subcommand.name}`,
1140
+ getFullCommandPath(cmd),
1063
1141
  subcommand.handler,
1064
1142
  !!subcommand.schema?.response,
1065
1143
  subcommand.webUrl
1066
1144
  );
1067
1145
  } catch (error) {
1068
1146
  if (error && typeof error === 'object' && 'issues' in error) {
1069
- handleValidationError(error, `${parent.name()} ${subcommand.name}`, baseCtx);
1147
+ handleValidationError(error, getFullCommandPath(cmd), baseCtx);
1070
1148
  }
1071
1149
  handleProjectConfigError(
1072
1150
  error,
@@ -1139,7 +1217,7 @@ async function registerSubcommand(
1139
1217
  exitWithError(
1140
1218
  createError(
1141
1219
  ErrorCode.INTERNAL_ERROR,
1142
- `Command '${parent.name()} ${subcommand.name}' declares a response schema but returned no data. This is a bug in the command implementation.`
1220
+ `Command '${getFullCommandPath(cmd)}' declares a response schema but returned no data. This is a bug in the command implementation.`
1143
1221
  ),
1144
1222
  baseCtx.logger,
1145
1223
  baseCtx.options.errorFormat
@@ -1192,10 +1270,10 @@ async function registerSubcommand(
1192
1270
  ctx.projectDir = projectDir;
1193
1271
  }
1194
1272
  if (subcommand.schema.args) {
1195
- ctx.args = subcommand.schema.args.parse(input.args);
1273
+ ctx.args = parseArgs(subcommand.schema.args, input.args);
1196
1274
  }
1197
1275
  if (subcommand.schema.options) {
1198
- ctx.opts = subcommand.schema.options.parse(input.options);
1276
+ ctx.opts = parseOptions(subcommand.schema.options, input.options);
1199
1277
  }
1200
1278
  if (normalized.requiresAPIClient) {
1201
1279
  // Recreate apiClient with auth credentials
@@ -1238,14 +1316,14 @@ async function registerSubcommand(
1238
1316
  }
1239
1317
  await executeOrValidate(
1240
1318
  ctx as CommandContext,
1241
- `${parent.name()} ${subcommand.name}`,
1319
+ getFullCommandPath(cmd),
1242
1320
  subcommand.handler,
1243
1321
  !!subcommand.schema?.response,
1244
1322
  subcommand.webUrl
1245
1323
  );
1246
1324
  } catch (error) {
1247
1325
  if (error && typeof error === 'object' && 'issues' in error) {
1248
- handleValidationError(error, `${parent.name()} ${subcommand.name}`, baseCtx);
1326
+ handleValidationError(error, getFullCommandPath(cmd), baseCtx);
1249
1327
  }
1250
1328
  handleProjectConfigError(
1251
1329
  error,
@@ -1314,7 +1392,7 @@ async function registerSubcommand(
1314
1392
  exitWithError(
1315
1393
  createError(
1316
1394
  ErrorCode.INTERNAL_ERROR,
1317
- `Command '${parent.name()} ${subcommand.name}' declares a response schema but returned no data. This is a bug in the command implementation.`
1395
+ `Command '${getFullCommandPath(cmd)}' declares a response schema but returned no data. This is a bug in the command implementation.`
1318
1396
  ),
1319
1397
  baseCtx.logger,
1320
1398
  baseCtx.options.errorFormat
@@ -1343,10 +1421,10 @@ async function registerSubcommand(
1343
1421
  ctx.projectDir = projectDir;
1344
1422
  }
1345
1423
  if (subcommand.schema.args) {
1346
- ctx.args = subcommand.schema.args.parse(input.args);
1424
+ ctx.args = parseArgs(subcommand.schema.args, input.args);
1347
1425
  }
1348
1426
  if (subcommand.schema.options) {
1349
- ctx.opts = subcommand.schema.options.parse(input.options);
1427
+ ctx.opts = parseOptions(subcommand.schema.options, input.options);
1350
1428
  }
1351
1429
  if (normalized.requiresAPIClient && !ctx.apiClient) {
1352
1430
  ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
@@ -1363,14 +1441,14 @@ async function registerSubcommand(
1363
1441
  }
1364
1442
  await executeOrValidate(
1365
1443
  ctx as CommandContext,
1366
- `${parent.name()} ${subcommand.name}`,
1444
+ getFullCommandPath(cmd),
1367
1445
  subcommand.handler,
1368
1446
  !!subcommand.schema?.response,
1369
1447
  subcommand.webUrl
1370
1448
  );
1371
1449
  } catch (error) {
1372
1450
  if (error && typeof error === 'object' && 'issues' in error) {
1373
- handleValidationError(error, `${parent.name()} ${subcommand.name}`, baseCtx);
1451
+ handleValidationError(error, getFullCommandPath(cmd), baseCtx);
1374
1452
  }
1375
1453
  handleProjectConfigError(
1376
1454
  error,
@@ -1426,7 +1504,7 @@ async function registerSubcommand(
1426
1504
  exitWithError(
1427
1505
  createError(
1428
1506
  ErrorCode.INTERNAL_ERROR,
1429
- `Command '${parent.name()} ${subcommand.name}' declares a response schema but returned no data. This is a bug in the command implementation.`
1507
+ `Command '${getFullCommandPath(cmd)}' declares a response schema but returned no data. This is a bug in the command implementation.`
1430
1508
  ),
1431
1509
  baseCtx.logger,
1432
1510
  baseCtx.options.errorFormat
@@ -1300,12 +1300,168 @@ export async function parseRoute(
1300
1300
  const action = statement.expression.arguments[0];
1301
1301
  let suffix = '';
1302
1302
  let config: Record<string, unknown> | undefined;
1303
+ // Supported HTTP methods that can be represented in BuildMetadata
1304
+ const SUPPORTED_HTTP_METHODS = ['get', 'post', 'put', 'delete', 'patch'] as const;
1305
+ type SupportedHttpMethod = (typeof SUPPORTED_HTTP_METHODS)[number];
1306
+
1307
+ const isSupportedHttpMethod = (m: string): m is SupportedHttpMethod =>
1308
+ SUPPORTED_HTTP_METHODS.includes(m.toLowerCase() as SupportedHttpMethod);
1309
+
1303
1310
  switch (method) {
1304
1311
  case 'use':
1305
- case 'on':
1306
- case 'all':
1307
1312
  case 'route': {
1308
- // Skip Hono middleware/routing methods - they don't represent API routes
1313
+ // Skip Hono middleware and sub-router mounting - they don't represent API routes
1314
+ continue;
1315
+ }
1316
+ case 'on': {
1317
+ // router.on(method | method[], path, handler)
1318
+ // First arg is method(s), second arg is path
1319
+ const methodArg = statement.expression.arguments[0];
1320
+ const pathArg = statement.expression.arguments[1];
1321
+
1322
+ // Extract methods from first argument
1323
+ const methods: SupportedHttpMethod[] = [];
1324
+
1325
+ if (methodArg && (methodArg as ASTLiteral).type === 'Literal') {
1326
+ // Single method: router.on('GET', '/path', handler)
1327
+ const raw = String((methodArg as ASTLiteral).value || '').toLowerCase();
1328
+ if (isSupportedHttpMethod(raw)) {
1329
+ methods.push(raw as SupportedHttpMethod);
1330
+ }
1331
+ } else if (methodArg && (methodArg as ASTNode).type === 'ArrayExpression') {
1332
+ // Array of methods: router.on(['GET', 'POST'], '/path', handler)
1333
+ const arr = methodArg as { elements: ASTNode[] };
1334
+ for (const el of arr.elements) {
1335
+ if (!el || (el as ASTLiteral).type !== 'Literal') continue;
1336
+ const raw = String((el as ASTLiteral).value || '').toLowerCase();
1337
+ if (isSupportedHttpMethod(raw)) {
1338
+ methods.push(raw as SupportedHttpMethod);
1339
+ }
1340
+ }
1341
+ }
1342
+
1343
+ // Skip if no supported methods or path is not a literal
1344
+ if (
1345
+ methods.length === 0 ||
1346
+ !pathArg ||
1347
+ (pathArg as ASTLiteral).type !== 'Literal'
1348
+ ) {
1349
+ continue;
1350
+ }
1351
+
1352
+ const pathSuffix = String((pathArg as ASTLiteral).value);
1353
+
1354
+ // Create a route entry for each method
1355
+ for (const httpMethod of methods) {
1356
+ const thepath = `${routePrefix}/${routeName}/${pathSuffix}`
1357
+ .replaceAll(/\/{2,}/g, '/')
1358
+ .replaceAll(/\/$/g, '');
1359
+ const id = generateRouteId(
1360
+ projectId,
1361
+ deploymentId,
1362
+ 'api',
1363
+ httpMethod,
1364
+ rel,
1365
+ thepath,
1366
+ version
1367
+ );
1368
+
1369
+ // Check if this route uses validator middleware
1370
+ const validatorInfo = hasValidatorCall(statement.expression.arguments);
1371
+ const routeConfig: Record<string, unknown> = {};
1372
+ if (validatorInfo.hasValidator) {
1373
+ routeConfig.hasValidator = true;
1374
+ if (validatorInfo.agentVariable) {
1375
+ routeConfig.agentVariable = validatorInfo.agentVariable;
1376
+ const agentImportPath = importMap.get(validatorInfo.agentVariable);
1377
+ if (agentImportPath) {
1378
+ routeConfig.agentImportPath = agentImportPath;
1379
+ }
1380
+ }
1381
+ if (validatorInfo.inputSchemaVariable) {
1382
+ routeConfig.inputSchemaVariable =
1383
+ validatorInfo.inputSchemaVariable;
1384
+ }
1385
+ if (validatorInfo.outputSchemaVariable) {
1386
+ routeConfig.outputSchemaVariable =
1387
+ validatorInfo.outputSchemaVariable;
1388
+ }
1389
+ if (validatorInfo.stream !== undefined) {
1390
+ routeConfig.stream = validatorInfo.stream;
1391
+ }
1392
+ }
1393
+
1394
+ routes.push({
1395
+ id,
1396
+ method: httpMethod,
1397
+ type: 'api',
1398
+ filename: rel,
1399
+ path: thepath,
1400
+ version,
1401
+ config: Object.keys(routeConfig).length > 0 ? routeConfig : undefined,
1402
+ });
1403
+ }
1404
+ continue;
1405
+ }
1406
+ case 'all': {
1407
+ // router.all(path, handler) - matches all HTTP methods
1408
+ // First arg is path (same as get/post/etc.)
1409
+ if (!action || (action as ASTLiteral).type !== 'Literal') {
1410
+ continue;
1411
+ }
1412
+
1413
+ const pathSuffix = String((action as ASTLiteral).value);
1414
+
1415
+ // Create a route entry for each supported method
1416
+ for (const httpMethod of SUPPORTED_HTTP_METHODS) {
1417
+ const thepath = `${routePrefix}/${routeName}/${pathSuffix}`
1418
+ .replaceAll(/\/{2,}/g, '/')
1419
+ .replaceAll(/\/$/g, '');
1420
+ const id = generateRouteId(
1421
+ projectId,
1422
+ deploymentId,
1423
+ 'api',
1424
+ httpMethod,
1425
+ rel,
1426
+ thepath,
1427
+ version
1428
+ );
1429
+
1430
+ // Check if this route uses validator middleware
1431
+ const validatorInfo = hasValidatorCall(statement.expression.arguments);
1432
+ const routeConfig: Record<string, unknown> = {};
1433
+ if (validatorInfo.hasValidator) {
1434
+ routeConfig.hasValidator = true;
1435
+ if (validatorInfo.agentVariable) {
1436
+ routeConfig.agentVariable = validatorInfo.agentVariable;
1437
+ const agentImportPath = importMap.get(validatorInfo.agentVariable);
1438
+ if (agentImportPath) {
1439
+ routeConfig.agentImportPath = agentImportPath;
1440
+ }
1441
+ }
1442
+ if (validatorInfo.inputSchemaVariable) {
1443
+ routeConfig.inputSchemaVariable =
1444
+ validatorInfo.inputSchemaVariable;
1445
+ }
1446
+ if (validatorInfo.outputSchemaVariable) {
1447
+ routeConfig.outputSchemaVariable =
1448
+ validatorInfo.outputSchemaVariable;
1449
+ }
1450
+ if (validatorInfo.stream !== undefined) {
1451
+ routeConfig.stream = validatorInfo.stream;
1452
+ }
1453
+ }
1454
+
1455
+ routes.push({
1456
+ id,
1457
+ method: httpMethod,
1458
+ type: 'api',
1459
+ filename: rel,
1460
+ path: thepath,
1461
+ version,
1462
+ config: Object.keys(routeConfig).length > 0 ? routeConfig : undefined,
1463
+ });
1464
+ }
1309
1465
  continue;
1310
1466
  }
1311
1467
  case 'get':
@@ -1456,7 +1612,10 @@ export async function parseRoute(
1456
1612
  }
1457
1613
 
1458
1614
  // For WebSocket/SSE/stream routes that don't use validator(), fall back to exported schemas
1459
- if (!routeConfig.hasValidator && (type === 'websocket' || type === 'sse' || type === 'stream')) {
1615
+ if (
1616
+ !routeConfig.hasValidator &&
1617
+ (type === 'websocket' || type === 'sse' || type === 'stream')
1618
+ ) {
1460
1619
  if (!routeConfig.inputSchemaVariable && exportedInputSchemaName) {
1461
1620
  routeConfig.inputSchemaVariable = exportedInputSchemaName;
1462
1621
  }
@@ -57,6 +57,7 @@ export async function generateEntryFile(options: GenerateEntryOptions): Promise<
57
57
  ` getAppState,`,
58
58
  ` getAppConfig,`,
59
59
  ` register,`,
60
+ ` getSpanProcessors,`,
60
61
  ` createServices,`,
61
62
  ` runAgentSetups,`,
62
63
  ` getThreadProvider,`,
@@ -84,7 +85,7 @@ export async function generateEntryFile(options: GenerateEntryOptions): Promise<
84
85
  ].filter(Boolean);
85
86
 
86
87
  imports.push(`import { type LogLevel } from '@agentuity/core';`);
87
- imports.push(`import { bootstrapRuntimeEnv } from '@agentuity/runtime';`);
88
+ imports.push(`import { bootstrapRuntimeEnv, patchBunS3ForStorageDev } from '@agentuity/runtime';`);
88
89
 
89
90
  // Generate route mounting code for all discovered routes
90
91
  const routeImportsAndMounts: string[] = [];
@@ -428,9 +429,14 @@ if (isDevelopment()) {
428
429
  await bootstrapRuntimeEnv({ projectDir: import.meta.dir + '/../..' });
429
430
  }
430
431
 
432
+ // Step 0.5: Patch Bun's S3 client for Agentuity storage endpoints
433
+ // Agentuity storage uses virtual-hosted-style URLs (*.storage.dev)
434
+ // This patches s3.file() to automatically set virtualHostedStyle: true
435
+ patchBunS3ForStorageDev();
436
+
431
437
  // Step 1: Initialize telemetry and services
432
438
  const serverUrl = \`http://127.0.0.1:\${process.env.PORT || '3500'}\`;
433
- const otel = register({ processors: [], logLevel: (process.env.AGENTUITY_LOG_LEVEL || 'info') as LogLevel });
439
+ const otel = register({ processors: getSpanProcessors(), logLevel: (process.env.AGENTUITY_LOG_LEVEL || 'info') as LogLevel });
434
440
 
435
441
  // Step 2: Create router and set as global
436
442
  const app = createRouter();
@@ -289,12 +289,11 @@ function generateRPCRegistryType(
289
289
  .replace(/_+/g, '_');
290
290
  const pascalName = toPascalCase(safeName);
291
291
 
292
- // Only reference type names if route has schemas, otherwise use 'never'
292
+ // Only reference type names if route has actual schemas extracted, otherwise use 'never'
293
+ // Note: hasValidator may be true (e.g., zValidator('query', ...)) but no schemas extracted
294
+ // because only 'json' validators extract input schemas
293
295
  const hasSchemas =
294
- route.hasValidator ||
295
- route.inputSchemaVariable ||
296
- route.outputSchemaVariable ||
297
- route.agentVariable;
296
+ route.inputSchemaVariable || route.outputSchemaVariable || route.agentVariable;
298
297
 
299
298
  current[terminalMethod] = {
300
299
  input: hasSchemas ? `${pascalName}Input` : 'never',
@@ -725,12 +724,10 @@ export function generateRouteRegistry(
725
724
  .replace(/_+/g, '_');
726
725
  const pascalName = toPascalCase(safeName);
727
726
 
728
- if (
729
- !route.hasValidator &&
730
- !route.inputSchemaVariable &&
731
- !route.outputSchemaVariable &&
732
- !route.agentVariable
733
- ) {
727
+ // Use 'never' types if no schemas were actually extracted
728
+ // Note: hasValidator may be true (e.g., zValidator('query', ...)) but no schemas extracted
729
+ // because only 'json' validators extract input schemas
730
+ if (!route.inputSchemaVariable && !route.outputSchemaVariable && !route.agentVariable) {
734
731
  const streamValue = route.stream === true ? 'true' : 'false';
735
732
  return `\t'${routeKey}': {
736
733
  \t\tinputSchema: never;
@@ -6,6 +6,7 @@ import { getCatalystAPIClient } from '../../../config';
6
6
  import { getCommand } from '../../../command-prefix';
7
7
  import { isDryRunMode, outputDryRun } from '../../../explain';
8
8
  import { ErrorCode } from '../../../errors';
9
+ import { addResourceEnvVars } from '../../../env-util';
9
10
 
10
11
  export const createSubcommand = defineSubcommand({
11
12
  name: 'create',
@@ -62,12 +63,22 @@ export const createSubcommand = defineSubcommand({
62
63
  },
63
64
  });
64
65
  if (created.length > 0) {
66
+ const resource = created[0];
67
+
68
+ // Write environment variables to .env if running inside a project
69
+ if (ctx.projectDir && resource.env && Object.keys(resource.env).length > 0) {
70
+ await addResourceEnvVars(ctx.projectDir, resource.env);
71
+ if (!options.json) {
72
+ tui.info('Environment variables written to .env');
73
+ }
74
+ }
75
+
65
76
  if (!options.json) {
66
- tui.success(`Created database: ${tui.bold(created[0].name)}`);
77
+ tui.success(`Created database: ${tui.bold(resource.name)}`);
67
78
  }
68
79
  return {
69
80
  success: true,
70
- name: created[0].name,
81
+ name: resource.name,
71
82
  };
72
83
  } else {
73
84
  tui.fatal('Failed to create database');
@@ -7,6 +7,7 @@ import { getCatalystAPIClient } from '../../../config';
7
7
  import { getCommand } from '../../../command-prefix';
8
8
  import { isDryRunMode, outputDryRun } from '../../../explain';
9
9
  import { ErrorCode } from '../../../errors';
10
+ import { removeResourceEnvVars } from '../../../env-util';
10
11
 
11
12
  export const deleteSubcommand = createSubcommand({
12
13
  name: 'delete',
@@ -109,10 +110,22 @@ export const deleteSubcommand = createSubcommand({
109
110
  });
110
111
 
111
112
  if (deleted.length > 0) {
112
- tui.success(`Deleted database: ${tui.bold(deleted[0])}`);
113
+ const resource = deleted[0];
114
+
115
+ // Remove env vars from .env if running inside a project
116
+ if (ctx.projectDir && resource.env_keys.length > 0) {
117
+ await removeResourceEnvVars(ctx.projectDir, resource.env_keys);
118
+ if (!options.json) {
119
+ tui.info(`Removed ${resource.env_keys.join(', ')} from .env`);
120
+ }
121
+ }
122
+
123
+ if (!options.json) {
124
+ tui.success(`Deleted database: ${tui.bold(resource.name)}`);
125
+ }
113
126
  return {
114
127
  success: true,
115
- name: deleted[0],
128
+ name: resource.name,
116
129
  };
117
130
  } else {
118
131
  tui.error('Failed to delete database');
@@ -34,7 +34,7 @@ import {
34
34
  getAppBaseURL,
35
35
  } from '@agentuity/server';
36
36
  import {
37
- findEnvFile,
37
+ findExistingEnvFile,
38
38
  readEnvFile,
39
39
  filterAgentuitySdkKeys,
40
40
  splitEnvAndSecrets,
@@ -176,8 +176,8 @@ export const deploySubcommand = createSubcommand({
176
176
  label: 'Sync Env & Secrets',
177
177
  run: async () => {
178
178
  try {
179
- // Read local env file (.env.production or .env)
180
- const envFilePath = await findEnvFile(projectDir);
179
+ // Read env file
180
+ const envFilePath = await findExistingEnvFile(projectDir);
181
181
  const localEnv = await readEnvFile(envFilePath);
182
182
 
183
183
  // Filter out AGENTUITY_ keys
@@ -45,7 +45,7 @@ export const deleteSubcommand = createSubcommand({
45
45
  });
46
46
  });
47
47
 
48
- // Update local .env file (prefer .env.production, fallback to .env)
48
+ // Update local .env file (prefer .env)
49
49
  const envFilePath = await findExistingEnvFile(projectDir);
50
50
  const currentEnv = await readEnvFile(envFilePath);
51
51
  delete currentEnv[args.key];
@@ -3,7 +3,7 @@ import { createSubcommand } from '../../../types';
3
3
  import * as tui from '../../../tui';
4
4
  import { projectEnvUpdate } from '@agentuity/server';
5
5
  import {
6
- findEnvFile,
6
+ findExistingEnvFile,
7
7
  readEnvFile,
8
8
  writeEnvFile,
9
9
  filterAgentuitySdkKeys,
@@ -23,7 +23,7 @@ const EnvImportResponseSchema = z.object({
23
23
 
24
24
  export const importSubcommand = createSubcommand({
25
25
  name: 'import',
26
- description: 'Import environment variables from a file to cloud and local .env.production',
26
+ description: 'Import environment variables from a file to cloud and local .env',
27
27
  tags: [
28
28
  'mutating',
29
29
  'creates-resource',
@@ -131,8 +131,8 @@ export const importSubcommand = createSubcommand({
131
131
  });
132
132
  });
133
133
 
134
- // Merge with local .env.production file
135
- const localEnvPath = await findEnvFile(projectDir);
134
+ // Merge environment
135
+ const localEnvPath = await findExistingEnvFile(projectDir);
136
136
  const localEnv = await readEnvFile(localEnvPath);
137
137
  const mergedEnv = mergeEnvVars(localEnv, filteredEnv);
138
138