@agentuity/cli 0.1.41 → 0.1.43

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 (247) hide show
  1. package/dist/agent-detection.d.ts.map +1 -1
  2. package/dist/agent-detection.js +8 -2
  3. package/dist/agent-detection.js.map +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +9 -5
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cmd/ai/index.d.ts.map +1 -1
  8. package/dist/cmd/ai/index.js +1 -8
  9. package/dist/cmd/ai/index.js.map +1 -1
  10. package/dist/cmd/ai/prompt/version.js +1 -1
  11. package/dist/cmd/ai/prompt/version.js.map +1 -1
  12. package/dist/cmd/auth/api.d.ts.map +1 -1
  13. package/dist/cmd/auth/api.js +4 -1
  14. package/dist/cmd/auth/api.js.map +1 -1
  15. package/dist/cmd/auth/ssh/api.js +2 -2
  16. package/dist/cmd/auth/ssh/api.js.map +1 -1
  17. package/dist/cmd/build/ast.d.ts.map +1 -1
  18. package/dist/cmd/build/ast.js +14 -13
  19. package/dist/cmd/build/ast.js.map +1 -1
  20. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  21. package/dist/cmd/build/entry-generator.js +137 -1
  22. package/dist/cmd/build/entry-generator.js.map +1 -1
  23. package/dist/cmd/build/patch/index.d.ts.map +1 -1
  24. package/dist/cmd/build/patch/index.js +3 -0
  25. package/dist/cmd/build/patch/index.js.map +1 -1
  26. package/dist/cmd/build/vite/index.d.ts +1 -0
  27. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  28. package/dist/cmd/build/vite/index.js +1 -0
  29. package/dist/cmd/build/vite/index.js.map +1 -1
  30. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  31. package/dist/cmd/build/vite/metadata-generator.js +19 -9
  32. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  33. package/dist/cmd/build/vite/public-asset-path-plugin.d.ts +46 -0
  34. package/dist/cmd/build/vite/public-asset-path-plugin.d.ts.map +1 -0
  35. package/dist/cmd/build/vite/public-asset-path-plugin.js +133 -0
  36. package/dist/cmd/build/vite/public-asset-path-plugin.js.map +1 -0
  37. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  38. package/dist/cmd/build/vite/registry-generator.js +14 -5
  39. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  40. package/dist/cmd/build/vite/route-discovery.js +1 -1
  41. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  42. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  43. package/dist/cmd/build/vite/vite-asset-server-config.js +17 -7
  44. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  45. package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
  46. package/dist/cmd/build/vite/vite-asset-server.js +1 -1
  47. package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
  48. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  49. package/dist/cmd/build/vite/vite-builder.js +13 -9
  50. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  51. package/dist/cmd/canary/index.d.ts.map +1 -1
  52. package/dist/cmd/canary/index.js +9 -1
  53. package/dist/cmd/canary/index.js.map +1 -1
  54. package/dist/cmd/cloud/db/create.d.ts.map +1 -1
  55. package/dist/cmd/cloud/db/create.js +2 -2
  56. package/dist/cmd/cloud/db/create.js.map +1 -1
  57. package/dist/cmd/cloud/db/delete.d.ts.map +1 -1
  58. package/dist/cmd/cloud/db/delete.js +2 -2
  59. package/dist/cmd/cloud/db/delete.js.map +1 -1
  60. package/dist/cmd/cloud/env/import.d.ts.map +1 -1
  61. package/dist/cmd/cloud/env/import.js +4 -1
  62. package/dist/cmd/cloud/env/import.js.map +1 -1
  63. package/dist/cmd/cloud/env/list.d.ts.map +1 -1
  64. package/dist/cmd/cloud/env/list.js +4 -1
  65. package/dist/cmd/cloud/env/list.js.map +1 -1
  66. package/dist/cmd/cloud/env/push.d.ts.map +1 -1
  67. package/dist/cmd/cloud/env/push.js +4 -1
  68. package/dist/cmd/cloud/env/push.js.map +1 -1
  69. package/dist/cmd/cloud/region/index.js +1 -1
  70. package/dist/cmd/cloud/region/index.js.map +1 -1
  71. package/dist/cmd/cloud/sandbox/snapshot/build.d.ts.map +1 -1
  72. package/dist/cmd/cloud/sandbox/snapshot/build.js +22 -10
  73. package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
  74. package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
  75. package/dist/cmd/cloud/sandbox/snapshot/get.js +12 -2
  76. package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
  77. package/dist/cmd/cloud/storage/create.d.ts.map +1 -1
  78. package/dist/cmd/cloud/storage/create.js +2 -2
  79. package/dist/cmd/cloud/storage/create.js.map +1 -1
  80. package/dist/cmd/cloud/storage/delete.d.ts.map +1 -1
  81. package/dist/cmd/cloud/storage/delete.js +2 -2
  82. package/dist/cmd/cloud/storage/delete.js.map +1 -1
  83. package/dist/cmd/dev/download.d.ts.map +1 -1
  84. package/dist/cmd/dev/download.js +9 -2
  85. package/dist/cmd/dev/download.js.map +1 -1
  86. package/dist/cmd/dev/index.js +1 -1
  87. package/dist/cmd/dev/index.js.map +1 -1
  88. package/dist/cmd/git/account/add.d.ts.map +1 -1
  89. package/dist/cmd/git/account/add.js +4 -3
  90. package/dist/cmd/git/account/add.js.map +1 -1
  91. package/dist/cmd/git/link.js +2 -2
  92. package/dist/cmd/git/link.js.map +1 -1
  93. package/dist/cmd/git/list.d.ts.map +1 -1
  94. package/dist/cmd/git/list.js +3 -2
  95. package/dist/cmd/git/list.js.map +1 -1
  96. package/dist/cmd/project/auth/init.js +1 -1
  97. package/dist/cmd/project/auth/init.js.map +1 -1
  98. package/dist/cmd/project/auth/shared.d.ts.map +1 -1
  99. package/dist/cmd/project/auth/shared.js +8 -3
  100. package/dist/cmd/project/auth/shared.js.map +1 -1
  101. package/dist/cmd/project/delete.d.ts.map +1 -1
  102. package/dist/cmd/project/delete.js +3 -2
  103. package/dist/cmd/project/delete.js.map +1 -1
  104. package/dist/cmd/project/reconcile.d.ts.map +1 -1
  105. package/dist/cmd/project/reconcile.js +9 -5
  106. package/dist/cmd/project/reconcile.js.map +1 -1
  107. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  108. package/dist/cmd/project/template-flow.js +15 -5
  109. package/dist/cmd/project/template-flow.js.map +1 -1
  110. package/dist/cmd/support/report.d.ts.map +1 -1
  111. package/dist/cmd/support/report.js +58 -23
  112. package/dist/cmd/support/report.js.map +1 -1
  113. package/dist/config.d.ts.map +1 -1
  114. package/dist/config.js +3 -2
  115. package/dist/config.js.map +1 -1
  116. package/dist/domain.d.ts.map +1 -1
  117. package/dist/domain.js +4 -3
  118. package/dist/domain.js.map +1 -1
  119. package/dist/internal-logger.d.ts +7 -0
  120. package/dist/internal-logger.d.ts.map +1 -1
  121. package/dist/internal-logger.js +82 -27
  122. package/dist/internal-logger.js.map +1 -1
  123. package/dist/repl.d.ts.map +1 -1
  124. package/dist/repl.js +31 -14
  125. package/dist/repl.js.map +1 -1
  126. package/dist/schema-parser.d.ts.map +1 -1
  127. package/dist/schema-parser.js +4 -1
  128. package/dist/schema-parser.js.map +1 -1
  129. package/dist/sound.d.ts.map +1 -1
  130. package/dist/sound.js +2 -1
  131. package/dist/sound.js.map +1 -1
  132. package/dist/steps.d.ts.map +1 -1
  133. package/dist/steps.js +13 -6
  134. package/dist/steps.js.map +1 -1
  135. package/dist/terminal.js +2 -0
  136. package/dist/terminal.js.map +1 -1
  137. package/dist/tsc-output-parser.d.ts +3 -0
  138. package/dist/tsc-output-parser.d.ts.map +1 -1
  139. package/dist/tsc-output-parser.js +32 -9
  140. package/dist/tsc-output-parser.js.map +1 -1
  141. package/dist/tui/prompt.d.ts.map +1 -1
  142. package/dist/tui/prompt.js +6 -2
  143. package/dist/tui/prompt.js.map +1 -1
  144. package/dist/tui.d.ts.map +1 -1
  145. package/dist/tui.js +26 -15
  146. package/dist/tui.js.map +1 -1
  147. package/dist/typescript-errors.d.ts.map +1 -1
  148. package/dist/typescript-errors.js +5 -2
  149. package/dist/typescript-errors.js.map +1 -1
  150. package/dist/utils/date.d.ts.map +1 -1
  151. package/dist/utils/date.js +5 -1
  152. package/dist/utils/date.js.map +1 -1
  153. package/dist/utils/deps.d.ts.map +1 -1
  154. package/dist/utils/deps.js +5 -3
  155. package/dist/utils/deps.js.map +1 -1
  156. package/dist/utils/detectSubagent.js +1 -1
  157. package/dist/utils/detectSubagent.js.map +1 -1
  158. package/package.json +8 -7
  159. package/src/agent-detection.ts +9 -2
  160. package/src/cli.ts +9 -5
  161. package/src/cmd/ai/index.ts +1 -8
  162. package/src/cmd/ai/prompt/version.ts +1 -1
  163. package/src/cmd/auth/api.ts +4 -1
  164. package/src/cmd/auth/ssh/api.ts +2 -2
  165. package/src/cmd/build/ast.ts +14 -13
  166. package/src/cmd/build/entry-generator.ts +137 -1
  167. package/src/cmd/build/patch/index.ts +3 -0
  168. package/src/cmd/build/vite/index.ts +1 -0
  169. package/src/cmd/build/vite/metadata-generator.ts +20 -9
  170. package/src/cmd/build/vite/public-asset-path-plugin.ts +167 -0
  171. package/src/cmd/build/vite/registry-generator.ts +14 -5
  172. package/src/cmd/build/vite/route-discovery.ts +1 -1
  173. package/src/cmd/build/vite/vite-asset-server-config.ts +19 -9
  174. package/src/cmd/build/vite/vite-asset-server.ts +3 -1
  175. package/src/cmd/build/vite/vite-builder.ts +22 -18
  176. package/src/cmd/canary/index.ts +9 -1
  177. package/src/cmd/cloud/db/create.ts +16 -17
  178. package/src/cmd/cloud/db/delete.ts +19 -20
  179. package/src/cmd/cloud/env/import.ts +6 -3
  180. package/src/cmd/cloud/env/list.ts +11 -9
  181. package/src/cmd/cloud/env/push.ts +6 -3
  182. package/src/cmd/cloud/region/index.ts +3 -3
  183. package/src/cmd/cloud/sandbox/snapshot/build.ts +42 -33
  184. package/src/cmd/cloud/sandbox/snapshot/get.ts +21 -15
  185. package/src/cmd/cloud/storage/create.ts +16 -17
  186. package/src/cmd/cloud/storage/delete.ts +19 -20
  187. package/src/cmd/dev/download.ts +10 -2
  188. package/src/cmd/dev/index.ts +4 -4
  189. package/src/cmd/git/account/add.ts +5 -4
  190. package/src/cmd/git/link.ts +2 -2
  191. package/src/cmd/git/list.ts +7 -6
  192. package/src/cmd/project/auth/init.ts +6 -6
  193. package/src/cmd/project/auth/shared.ts +8 -3
  194. package/src/cmd/project/delete.ts +3 -2
  195. package/src/cmd/project/reconcile.ts +9 -5
  196. package/src/cmd/project/template-flow.ts +15 -5
  197. package/src/cmd/support/report.ts +82 -28
  198. package/src/config.ts +3 -2
  199. package/src/domain.ts +4 -3
  200. package/src/internal-logger.ts +91 -26
  201. package/src/repl.ts +27 -14
  202. package/src/schema-parser.ts +4 -1
  203. package/src/sound.ts +2 -1
  204. package/src/steps.ts +11 -6
  205. package/src/terminal.ts +2 -1
  206. package/src/tsc-output-parser.ts +61 -38
  207. package/src/tui/prompt.ts +6 -2
  208. package/src/tui.ts +26 -17
  209. package/src/typescript-errors.ts +5 -2
  210. package/src/utils/date.ts +5 -1
  211. package/src/utils/deps.ts +5 -3
  212. package/src/utils/detectSubagent.ts +1 -1
  213. package/dist/cmd/ai/cadence/index.d.ts +0 -3
  214. package/dist/cmd/ai/cadence/index.d.ts.map +0 -1
  215. package/dist/cmd/ai/cadence/index.js +0 -35
  216. package/dist/cmd/ai/cadence/index.js.map +0 -1
  217. package/dist/cmd/ai/cadence/list.d.ts +0 -3
  218. package/dist/cmd/ai/cadence/list.d.ts.map +0 -1
  219. package/dist/cmd/ai/cadence/list.js +0 -167
  220. package/dist/cmd/ai/cadence/list.js.map +0 -1
  221. package/dist/cmd/ai/cadence/pause.d.ts +0 -3
  222. package/dist/cmd/ai/cadence/pause.d.ts.map +0 -1
  223. package/dist/cmd/ai/cadence/pause.js +0 -103
  224. package/dist/cmd/ai/cadence/pause.js.map +0 -1
  225. package/dist/cmd/ai/cadence/resume.d.ts +0 -3
  226. package/dist/cmd/ai/cadence/resume.d.ts.map +0 -1
  227. package/dist/cmd/ai/cadence/resume.js +0 -106
  228. package/dist/cmd/ai/cadence/resume.js.map +0 -1
  229. package/dist/cmd/ai/cadence/status.d.ts +0 -3
  230. package/dist/cmd/ai/cadence/status.d.ts.map +0 -1
  231. package/dist/cmd/ai/cadence/status.js +0 -129
  232. package/dist/cmd/ai/cadence/status.js.map +0 -1
  233. package/dist/cmd/ai/cadence/stop.d.ts +0 -3
  234. package/dist/cmd/ai/cadence/stop.d.ts.map +0 -1
  235. package/dist/cmd/ai/cadence/stop.js +0 -107
  236. package/dist/cmd/ai/cadence/stop.js.map +0 -1
  237. package/dist/cmd/ai/cadence/util.d.ts +0 -44
  238. package/dist/cmd/ai/cadence/util.d.ts.map +0 -1
  239. package/dist/cmd/ai/cadence/util.js +0 -52
  240. package/dist/cmd/ai/cadence/util.js.map +0 -1
  241. package/src/cmd/ai/cadence/index.ts +0 -36
  242. package/src/cmd/ai/cadence/list.ts +0 -183
  243. package/src/cmd/ai/cadence/pause.ts +0 -119
  244. package/src/cmd/ai/cadence/resume.ts +0 -124
  245. package/src/cmd/ai/cadence/status.ts +0 -141
  246. package/src/cmd/ai/cadence/stop.ts +0 -124
  247. package/src/cmd/ai/cadence/util.ts +0 -86
@@ -217,8 +217,9 @@ async function selectRegion(regions: RegionList, defaultRegion?: string): Promis
217
217
  throw new Error('No cloud regions available');
218
218
  }
219
219
 
220
- if (regions.length === 1) {
221
- return regions[0].region;
220
+ const firstRegion = regions[0];
221
+ if (regions.length === 1 && firstRegion) {
222
+ return firstRegion.region;
222
223
  }
223
224
 
224
225
  // Build options from API regions
@@ -228,18 +229,21 @@ async function selectRegion(regions: RegionList, defaultRegion?: string): Promis
228
229
  }));
229
230
 
230
231
  // Move default to top if found
231
- const defaultValue = defaultRegion ?? regions[0].region;
232
+ const defaultValue = defaultRegion ?? firstRegion?.region ?? '';
232
233
  const defaultIndex = options.findIndex((r) => r.value === defaultValue);
233
234
  if (defaultIndex > 0) {
234
235
  const [defaultItem] = options.splice(defaultIndex, 1);
235
- options.unshift(defaultItem);
236
+ if (defaultItem) {
237
+ options.unshift(defaultItem);
238
+ }
236
239
  }
237
240
 
238
241
  const prompt = createPrompt();
242
+ const firstOption = options[0];
239
243
  return prompt.select({
240
244
  message: 'Select a region:',
241
245
  options,
242
- initial: options[0].value,
246
+ initial: firstOption?.value ?? '',
243
247
  });
244
248
  }
245
249
 
@@ -220,7 +220,12 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
220
220
  }
221
221
  selectedTemplate = found;
222
222
  } else if (!isInteractive || templates.length === 1) {
223
- selectedTemplate = templates[0];
223
+ const firstTemplate = templates[0];
224
+ if (!firstTemplate) {
225
+ logger.fatal('No templates available', ErrorCode.RESOURCE_NOT_FOUND);
226
+ return undefined as never;
227
+ }
228
+ selectedTemplate = firstTemplate;
224
229
  } else {
225
230
  let maxLength = 15;
226
231
  templates.forEach((t) => {
@@ -572,14 +577,19 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateF
572
577
  return createResources(catalystClient!, orgId!, region!, [{ type: 'db' }]);
573
578
  },
574
579
  });
575
- authDatabaseName = created[0].name;
580
+ const createdDb = created[0];
581
+ if (!createdDb) {
582
+ logger.fatal('Failed to create database for auth', ErrorCode.RESOURCE_NOT_FOUND);
583
+ return undefined as never;
584
+ }
585
+ authDatabaseName = createdDb.name;
576
586
 
577
587
  // Get env vars from created resource
578
- if (created[0]?.env) {
579
- authDatabaseUrl = created[0].env.DATABASE_URL;
588
+ if (createdDb.env) {
589
+ authDatabaseUrl = createdDb.env.DATABASE_URL;
580
590
  // Also add to resourceEnvVars if not already set
581
591
  if (!resourceEnvVars.DATABASE_URL) {
582
- Object.assign(resourceEnvVars, created[0].env);
592
+ Object.assign(resourceEnvVars, createdDb.env);
583
593
  }
584
594
  }
585
595
  }
@@ -1,13 +1,34 @@
1
1
  import { createSubcommand } from '../../types';
2
2
  import { z } from 'zod';
3
- import { existsSync, readFileSync } from 'node:fs';
4
- import { join } from 'node:path';
3
+ import { readFileSync } from 'node:fs';
4
+ import { join, basename } from 'node:path';
5
5
  import { tmpdir } from 'node:os';
6
- import { getLatestLogSession } from '../../internal-logger';
6
+ import { getLogSessionsInCurrentWindow } from '../../internal-logger';
7
7
  import * as tui from '../../tui';
8
8
  import { randomBytes } from 'node:crypto';
9
9
  import AdmZip from 'adm-zip';
10
10
  import { APIResponseSchema } from '@agentuity/server';
11
+ import { StructuredError } from '@agentuity/core';
12
+
13
+ // Structured errors for this module
14
+ const NoSessionDirectoriesError = StructuredError(
15
+ 'NoSessionDirectoriesError',
16
+ 'No session directories provided'
17
+ );
18
+
19
+ const ReportUploadError = StructuredError('ReportUploadError')<{
20
+ statusText: string;
21
+ status?: number;
22
+ }>();
23
+
24
+ const UploadUrlCreationError = StructuredError('UploadUrlCreationError');
25
+
26
+ const BrowserOpenError = StructuredError(
27
+ 'BrowserOpenError',
28
+ 'Failed to open browser. Please open the URL manually.'
29
+ )<{
30
+ exitCode?: number | null;
31
+ }>();
11
32
 
12
33
  const argsSchema = z.object({});
13
34
 
@@ -33,22 +54,34 @@ const ReportUploadDataSchema = z.object({
33
54
  const ReportUploadResponseSchema = APIResponseSchema(ReportUploadDataSchema);
34
55
 
35
56
  /**
36
- * Create a zip file containing session and logs
57
+ * Create a zip file containing session and logs from multiple session directories
37
58
  */
38
- async function createReportZip(sessionDir: string): Promise<string> {
39
- const sessionFile = join(sessionDir, 'session.json');
40
- const logsFile = join(sessionDir, 'logs.jsonl');
41
-
42
- if (!existsSync(sessionFile) || !existsSync(logsFile)) {
43
- throw new Error('Session files not found');
59
+ async function createReportZip(sessionDirs: string[]): Promise<string> {
60
+ if (sessionDirs.length === 0) {
61
+ throw NoSessionDirectoriesError();
44
62
  }
45
63
 
46
64
  // Create zip in temp directory
47
65
  const tempZip = join(tmpdir(), `agentuity-report-${randomBytes(8).toString('hex')}.zip`);
48
66
 
49
67
  const zip = new AdmZip();
50
- zip.addLocalFile(sessionFile);
51
- zip.addLocalFile(logsFile);
68
+
69
+ for (const sessionDir of sessionDirs) {
70
+ const sessionFile = join(sessionDir, 'session.json');
71
+ const logsFile = join(sessionDir, 'logs.jsonl');
72
+
73
+ // Extract session ID from directory name cross-platform
74
+ const sessionId = basename(sessionDir) || 'unknown';
75
+
76
+ // Add files with session ID prefix to avoid conflicts
77
+ if (await Bun.file(sessionFile).exists()) {
78
+ zip.addLocalFile(sessionFile, sessionId);
79
+ }
80
+ if (await Bun.file(logsFile).exists()) {
81
+ zip.addLocalFile(logsFile, sessionId);
82
+ }
83
+ }
84
+
52
85
  zip.writeZip(tempZip);
53
86
 
54
87
  return tempZip;
@@ -76,7 +109,11 @@ async function uploadReport(
76
109
  if (!response.ok) {
77
110
  const errorText = await response.text();
78
111
  logger.error('Upload failed', { status: response.status, error: errorText });
79
- throw new Error(`Upload failed: ${response.statusText}`);
112
+ throw new ReportUploadError({
113
+ message: `Upload failed: ${response.statusText}`,
114
+ statusText: response.statusText,
115
+ status: response.status,
116
+ });
80
117
  }
81
118
  }
82
119
 
@@ -161,11 +198,11 @@ async function openBrowser(url: string, logger: import('../../types').Logger): P
161
198
  await proc.exited;
162
199
 
163
200
  if (proc.exitCode !== 0) {
164
- throw new Error(`Browser process exited with code ${proc.exitCode}`);
201
+ throw new BrowserOpenError({ exitCode: proc.exitCode });
165
202
  }
166
203
  } catch (error) {
167
204
  logger.error('Failed to open browser', { error });
168
- throw new Error('Failed to open browser. Please open the URL manually.');
205
+ throw new BrowserOpenError({ exitCode: null, cause: error });
169
206
  }
170
207
  }
171
208
 
@@ -184,9 +221,9 @@ export default createSubcommand({
184
221
  const { opts, logger, apiClient } = ctx;
185
222
  const isJsonMode = ctx.options.json;
186
223
 
187
- // Get the latest log session
188
- const sessionDir = getLatestLogSession();
189
- if (!sessionDir) {
224
+ // Get all log sessions in the current time window (current + previous bucket)
225
+ const sessionDirs = getLogSessionsInCurrentWindow();
226
+ if (sessionDirs.length === 0) {
190
227
  if (isJsonMode) {
191
228
  console.log(JSON.stringify({ success: false, error: 'No CLI logs found' }));
192
229
  } else {
@@ -196,10 +233,27 @@ export default createSubcommand({
196
233
  return;
197
234
  }
198
235
 
199
- // Read session data to get CLI version
200
- const sessionFile = join(sessionDir, 'session.json');
201
- const sessionData = JSON.parse(readFileSync(sessionFile, 'utf-8'));
202
- const cliVersion = sessionData.cli?.version || 'unknown';
236
+ // Use the first (most recent) session for metadata
237
+ const primarySessionDir = sessionDirs[0]!;
238
+ const sessionFile = join(primarySessionDir, 'session.json');
239
+
240
+ // Safely read session data with fallback for corrupt/missing session.json
241
+ let sessionData: SessionData = {};
242
+ let cliVersion = 'unknown';
243
+ try {
244
+ if (await Bun.file(sessionFile).exists()) {
245
+ sessionData = JSON.parse(readFileSync(sessionFile, 'utf-8'));
246
+ cliVersion = sessionData.cli?.version || 'unknown';
247
+ }
248
+ } catch {
249
+ // Fall back to defaults if session.json is corrupt or unreadable
250
+ logger.trace('Failed to read session.json, using defaults');
251
+ }
252
+
253
+ // Log how many sessions we're including
254
+ if (!isJsonMode && sessionDirs.length > 1) {
255
+ tui.info(`Found ${sessionDirs.length} session(s) in the current time window`);
256
+ }
203
257
 
204
258
  // Get issue description from:
205
259
  // 1. --description flag
@@ -281,11 +335,11 @@ export default createSubcommand({
281
335
  // Debug: log the response
282
336
  logger.debug('Upload response received', { uploadResponse });
283
337
 
284
- if (!uploadResponse.success) {
285
- const errorMsg = uploadResponse.message || 'Failed to create upload URL';
286
- logger.error('Upload URL creation failed', { uploadResponse, errorMsg });
287
- throw new Error(errorMsg);
288
- }
338
+ if (!uploadResponse.success) {
339
+ const errorMsg = uploadResponse.message || 'Failed to create upload URL';
340
+ logger.error('Upload URL creation failed', { uploadResponse, errorMsg });
341
+ throw new UploadUrlCreationError({ message: errorMsg });
342
+ }
289
343
 
290
344
  const { presigned_url, url: reportUrl, report_id: reportId } = uploadResponse.data;
291
345
 
@@ -294,7 +348,7 @@ export default createSubcommand({
294
348
  tui.info('Creating report archive...');
295
349
  }
296
350
 
297
- const zipPath = await createReportZip(sessionDir);
351
+ const zipPath = await createReportZip(sessionDirs);
298
352
 
299
353
  // Step 3: Upload to S3
300
354
  if (!isJsonMode) {
package/src/config.ts CHANGED
@@ -789,8 +789,9 @@ export async function getDefaultRegion(
789
789
  const file = Bun.file(cachePath);
790
790
  if (await file.exists()) {
791
791
  const data: RegionsCacheData = await file.json();
792
- if (data.regions && data.regions.length > 0) {
793
- return data.regions[0].region;
792
+ const firstRegion = data.regions?.[0];
793
+ if (firstRegion) {
794
+ return firstRegion.region;
794
795
  }
795
796
  }
796
797
  } catch {
package/src/domain.ts CHANGED
@@ -79,8 +79,9 @@ async function fetchDNSRecord(name: string, type: string): Promise<string | null
79
79
  });
80
80
  if (res.ok) {
81
81
  const result = (await res.json()) as CFRecord;
82
- if (result?.Answer?.length) {
83
- return result.Answer[0].data.replace(/\.$/, ''); // DNS records end with . so we remove that
82
+ const firstAnswer = result?.Answer?.[0];
83
+ if (firstAnswer) {
84
+ return firstAnswer.data.replace(/\.$/, ''); // DNS records end with . so we remove that
84
85
  }
85
86
  }
86
87
  return null;
@@ -104,7 +105,7 @@ export async function checkCustomDomainForDNS(
104
105
  config?: Config | null
105
106
  ): Promise<DNSResult[]> {
106
107
  const suffix = config?.overrides?.api_url?.includes('agentuity.io') ? LOCAL_DNS : PRODUCTION_DNS;
107
- const id = Bun.hash.xxHash64(projectId).toString(16);
108
+ const id = Bun.hash.xxHash64(projectId).toString(16).padStart(16, '0');
108
109
  const proxy = `p${id}.${suffix}`;
109
110
 
110
111
  return Promise.all(
@@ -45,6 +45,9 @@ const SENSITIVE_ENV_PATTERNS = [
45
45
 
46
46
  interface SessionMetadata {
47
47
  sessionId: string;
48
+ bucket: number;
49
+ pid: number;
50
+ ppid: number;
48
51
  command: string;
49
52
  args: string[];
50
53
  timestamp: string;
@@ -107,9 +110,30 @@ function getLogsDir(): string {
107
110
  }
108
111
 
109
112
  /**
110
- * Clean up old log directories, keeping only the most recent one
113
+ * Calculate the current 5-minute bucket number
114
+ * Each bucket represents a 5-minute window (300000ms)
111
115
  */
112
- function cleanupOldLogs(currentSessionId: string): void {
116
+ function getCurrentBucket(): number {
117
+ return Math.floor(Date.now() / 300000);
118
+ }
119
+
120
+ /**
121
+ * Parse bucket number from directory name
122
+ * Directory format: {bucket}-{uuid}
123
+ * Returns null for legacy directories (uuid-only format)
124
+ */
125
+ function parseBucketFromDirName(dirName: string): number | null {
126
+ const match = dirName.match(/^(\d+)-/);
127
+ if (match && match[1]) {
128
+ return parseInt(match[1], 10);
129
+ }
130
+ return null;
131
+ }
132
+
133
+ /**
134
+ * Clean up old log directories, keeping only directories in the current bucket
135
+ */
136
+ function cleanupOldLogs(currentBucket: number): void {
113
137
  // Skip cleanup when inheriting a parent's session ID to avoid
114
138
  // deleting the parent's session directory (race condition).
115
139
  // This applies to forked deploy processes and any subprocess
@@ -125,19 +149,24 @@ function cleanupOldLogs(currentSessionId: string): void {
125
149
 
126
150
  try {
127
151
  const entries = readdirSync(logsDir, { withFileTypes: true });
128
- const dirs = entries
129
- .filter((e) => e.isDirectory())
130
- .map((e) => e.name)
131
- .filter((name) => name !== currentSessionId);
152
+ const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
132
153
 
133
- // Remove all directories except the current one
154
+ // Remove directories with bucket < currentBucket (old buckets)
155
+ // Also remove legacy directories (no bucket prefix) for backward compatibility
134
156
  for (const dir of dirs) {
135
- const dirPath = join(logsDir, dir);
136
- try {
137
- rmSync(dirPath, { recursive: true, force: true });
138
- } catch (err) {
139
- // Ignore errors during cleanup
140
- console.debug(`Failed to remove old log directory ${dir}: ${err}`);
157
+ const bucket = parseBucketFromDirName(dir);
158
+
159
+ // Delete if:
160
+ // 1. Legacy directory (no bucket prefix) - clean up old format
161
+ // 2. Bucket is older than current bucket
162
+ if (bucket === null || bucket < currentBucket) {
163
+ const dirPath = join(logsDir, dir);
164
+ try {
165
+ rmSync(dirPath, { recursive: true, force: true });
166
+ } catch (err) {
167
+ // Ignore errors during cleanup
168
+ console.debug(`Failed to remove old log directory ${dir}: ${err}`);
169
+ }
141
170
  }
142
171
  }
143
172
  } catch (err) {
@@ -154,6 +183,7 @@ export class InternalLogger implements Logger {
154
183
  private sessionDir: string;
155
184
  private sessionFile: string;
156
185
  private logsFile: string;
186
+ private bucket: number;
157
187
  private initialized = false;
158
188
  private disabled = false;
159
189
 
@@ -161,16 +191,23 @@ export class InternalLogger implements Logger {
161
191
  private cliVersion: string,
162
192
  private cliName: string
163
193
  ) {
194
+ // Calculate current 5-minute bucket
195
+ this.bucket = getCurrentBucket();
196
+
164
197
  // When a parent session ID is set in the environment, use it to ensure
165
198
  // all CLI invocations (parent and any subprocesses) write to the same log file.
166
199
  // This prevents race conditions where child processes delete parent's logs.
167
200
  const parentSessionId = process.env.AGENTUITY_INTERNAL_SESSION_ID;
168
201
  if (parentSessionId) {
169
202
  this.sessionId = parentSessionId;
203
+ // Parent session ID already includes bucket prefix, use as-is for directory name
204
+ this.sessionDir = join(getLogsDir(), parentSessionId);
170
205
  } else {
171
- this.sessionId = randomUUID();
206
+ // Generate new session ID with bucket prefix: {bucket}-{uuid}
207
+ const uuid = randomUUID();
208
+ this.sessionId = `${this.bucket}-${uuid}`;
209
+ this.sessionDir = join(getLogsDir(), this.sessionId);
172
210
  }
173
- this.sessionDir = join(getLogsDir(), this.sessionId);
174
211
  this.sessionFile = join(this.sessionDir, 'session.json');
175
212
  this.logsFile = join(this.sessionDir, 'logs.jsonl');
176
213
  }
@@ -189,9 +226,9 @@ export class InternalLogger implements Logger {
189
226
  // Create logs directory (may already exist if we're a child process)
190
227
  mkdirSync(this.sessionDir, { recursive: true, mode: 0o700 });
191
228
 
192
- // Clean up old logs (keep only this session)
229
+ // Clean up old logs (directories with bucket < current bucket)
193
230
  // This is skipped for child processes to avoid deleting parent's session
194
- cleanupOldLogs(this.sessionId);
231
+ cleanupOldLogs(this.bucket);
195
232
 
196
233
  // When inheriting a parent's session ID, skip session.json creation
197
234
  // (parent already created it) but enable logging
@@ -232,6 +269,9 @@ export class InternalLogger implements Logger {
232
269
  // Gather session metadata
233
270
  const sessionMetadata: SessionMetadata = {
234
271
  sessionId: this.sessionId,
272
+ bucket: this.bucket,
273
+ pid: process.pid,
274
+ ppid: process.ppid,
235
275
  command,
236
276
  args,
237
277
  timestamp: new Date().toISOString(),
@@ -422,29 +462,54 @@ export function createInternalLogger(cliVersion: string, cliName: string): Inter
422
462
  }
423
463
 
424
464
  /**
425
- * Get the latest log session directory (if any)
465
+ * Get all log session directories in the current time window
466
+ * Returns directories from current bucket AND previous bucket (to handle boundary cases)
426
467
  */
427
- export function getLatestLogSession(): string | null {
468
+ export function getLogSessionsInCurrentWindow(): string[] {
428
469
  const logsDir = getLogsDir();
429
470
  if (!existsSync(logsDir)) {
430
- return null;
471
+ return [];
431
472
  }
432
473
 
433
474
  try {
475
+ const currentBucket = getCurrentBucket();
476
+ const previousBucket = currentBucket - 1;
477
+
434
478
  const entries = readdirSync(logsDir, { withFileTypes: true });
435
479
  const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
436
480
 
437
- if (dirs.length === 0) {
438
- return null;
439
- }
481
+ // Filter to directories in current or previous bucket
482
+ const validDirs = dirs.filter((dir) => {
483
+ const bucket = parseBucketFromDirName(dir);
484
+ // Include current bucket, previous bucket, and legacy directories (for backward compat)
485
+ return bucket === currentBucket || bucket === previousBucket || bucket === null;
486
+ });
487
+
488
+ // Sort by bucket (descending) then by name to get most recent first
489
+ validDirs.sort((a, b) => {
490
+ const bucketA = parseBucketFromDirName(a) ?? 0;
491
+ const bucketB = parseBucketFromDirName(b) ?? 0;
492
+ if (bucketA !== bucketB) {
493
+ return bucketB - bucketA; // Higher bucket first
494
+ }
495
+ return b.localeCompare(a); // Then by name descending
496
+ });
440
497
 
441
- // Return the first directory (should be the only one due to cleanup)
442
- return join(logsDir, dirs[0]);
498
+ return validDirs.map((dir) => join(logsDir, dir));
443
499
  } catch {
444
- return null;
500
+ return [];
445
501
  }
446
502
  }
447
503
 
504
+ /**
505
+ * Get the latest log session directory (if any)
506
+ * For backward compatibility, returns the first session in the current window
507
+ */
508
+ export function getLatestLogSession(): string | null {
509
+ const sessions = getLogSessionsInCurrentWindow();
510
+ return sessions[0] ?? null;
511
+ }
512
+
448
513
  /**
449
514
  * Get the logs directory path (exported for external use)
450
515
  */
package/src/repl.ts CHANGED
@@ -184,6 +184,7 @@ function parseCommandLine(line: string): ParsedCommand {
184
184
  // Parse remaining tokens into args and options
185
185
  for (let i = 1; i < tokens.length; i++) {
186
186
  const token = tokens[i];
187
+ if (token === undefined) continue;
187
188
 
188
189
  if (token.startsWith('--')) {
189
190
  // Long option: --name=value or --flag
@@ -194,8 +195,9 @@ function parseCommandLine(line: string): ParsedCommand {
194
195
  options[name.slice(0, eqIndex)] = name.slice(eqIndex + 1);
195
196
  } else {
196
197
  // Check if next token is a value
197
- if (i + 1 < tokens.length && !tokens[i + 1].startsWith('-')) {
198
- options[name] = tokens[i + 1];
198
+ const nextToken = tokens[i + 1];
199
+ if (i + 1 < tokens.length && nextToken !== undefined && !nextToken.startsWith('-')) {
200
+ options[name] = nextToken;
199
201
  i++;
200
202
  } else {
201
203
  options[name] = true;
@@ -206,8 +208,9 @@ function parseCommandLine(line: string): ParsedCommand {
206
208
  const name = token.slice(1);
207
209
 
208
210
  // Check if next token is a value
209
- if (i + 1 < tokens.length && !tokens[i + 1].startsWith('-')) {
210
- options[name] = tokens[i + 1];
211
+ const nextToken = tokens[i + 1];
212
+ if (i + 1 < tokens.length && nextToken !== undefined && !nextToken.startsWith('-')) {
213
+ options[name] = nextToken;
211
214
  i++;
212
215
  } else {
213
216
  options[name] = true;
@@ -603,7 +606,7 @@ function getAutocompleteMatches(
603
606
  if (!buffer.trim()) return [];
604
607
 
605
608
  const tokens = buffer.trim().split(/\s+/);
606
- const firstToken = tokens[0].toLowerCase();
609
+ const firstToken = tokens[0]?.toLowerCase() ?? '';
607
610
 
608
611
  // If we're typing the first word (no trailing space), suggest commands
609
612
  if (tokens.length === 1 && buffer === buffer.trimEnd()) {
@@ -623,7 +626,7 @@ function getAutocompleteMatches(
623
626
  if (tokens.length === 2 && buffer === buffer.trimEnd() && commandMap) {
624
627
  const cmd = commandMap.get(firstToken);
625
628
  if (cmd?.subcommands) {
626
- const subToken = tokens[1].toLowerCase();
629
+ const subToken = tokens[1]?.toLowerCase() ?? '';
627
630
  return cmd.subcommands
628
631
  .filter((sub) => sub.name.startsWith(subToken) && sub.name !== subToken)
629
632
  .map((sub) => sub.name);
@@ -647,8 +650,10 @@ function getAutocompleteSuggestion(
647
650
  if (matches.length === 0) return '';
648
651
 
649
652
  const selectedMatch = matches[cycleIndex % matches.length];
653
+ if (selectedMatch === undefined) return '';
654
+
650
655
  const tokens = buffer.trim().split(/\s+/);
651
- const firstToken = tokens[0].toLowerCase();
656
+ const firstToken = tokens[0]?.toLowerCase() ?? '';
652
657
 
653
658
  // Typing first word (command name)
654
659
  if (tokens.length === 1 && buffer === buffer.trimEnd()) {
@@ -675,7 +680,7 @@ function getAutocompleteSuggestion(
675
680
  // Typing subcommand name
676
681
  if (tokens.length === 2 && buffer === buffer.trimEnd()) {
677
682
  const cmd = commandMap.get(firstToken);
678
- const subToken = tokens[1];
683
+ const subToken = tokens[1] ?? '';
679
684
  const subcommand = cmd?.subcommands?.find((sub) => sub.name === selectedMatch);
680
685
 
681
686
  let suggestion = selectedMatch.slice(subToken.length);
@@ -791,7 +796,7 @@ class ActivityIndicator {
791
796
  }
792
797
 
793
798
  private draw() {
794
- const frame = this.frames[this.currentFrame];
799
+ const frame = this.frames[this.currentFrame] ?? this.frames[0] ?? '⠋';
795
800
  // Clear line, draw spinner, stay on same line
796
801
  process.stdout.write('\r\x1b[K'); // Clear line from cursor
797
802
  process.stdout.write(`${tui.muted(frame)} ${tui.muted(this.message)}...`);
@@ -855,6 +860,7 @@ async function showCommandPicker(
855
860
  // Draw commands
856
861
  for (let i = 0; i < commands.length; i++) {
857
862
  const cmd = commands[i];
863
+ if (cmd === undefined) continue;
858
864
  const isSelected = i === selectedIndex;
859
865
  const prefix = isSelected ? '▶ ' : ' ';
860
866
  const style = isSelected ? '\x1b[7m' : ''; // Reverse video for selected
@@ -913,7 +919,11 @@ async function showCommandPicker(
913
919
  const selected = commands[selectedIndex];
914
920
  cleanup();
915
921
  clearPicker();
916
- resolve(selected.name + (selected.argHint ? ' ' : ''));
922
+ if (selected) {
923
+ resolve(selected.name + (selected.argHint ? ' ' : ''));
924
+ } else {
925
+ resolve(null);
926
+ }
917
927
  return;
918
928
  }
919
929
 
@@ -957,6 +967,7 @@ function applySyntaxHighlighting(buffer: string, commands: string[]): string {
957
967
 
958
968
  for (let i = 0; i < tokens.length; i++) {
959
969
  const token = tokens[i];
970
+ if (token === undefined) continue;
960
971
 
961
972
  // Skip whitespace
962
973
  if (/^\s+$/.test(token)) {
@@ -1018,7 +1029,8 @@ async function readLine(
1018
1029
 
1019
1030
  const searchHistory = (query: string, startFrom: number): number => {
1020
1031
  for (let i = startFrom - 1; i >= 0; i--) {
1021
- if (history[i].toLowerCase().includes(query.toLowerCase())) {
1032
+ const historyEntry = history[i];
1033
+ if (historyEntry !== undefined && historyEntry.toLowerCase().includes(query.toLowerCase())) {
1022
1034
  return i;
1023
1035
  }
1024
1036
  }
@@ -1057,7 +1069,8 @@ async function readLine(
1057
1069
  }
1058
1070
  const linePrompt = currentLineIndex === 0 ? prompt : '... ';
1059
1071
  process.stdout.write('\r');
1060
- process.stdout.write(linePrompt + lines[currentLineIndex].slice(0, cursorPos));
1072
+ const currentLine = lines[currentLineIndex] ?? '';
1073
+ process.stdout.write(linePrompt + currentLine.slice(0, cursorPos));
1061
1074
  } else {
1062
1075
  // Single-line mode (original behavior)
1063
1076
  process.stdout.write('\r\x1b[K');
@@ -1282,7 +1295,7 @@ async function readLine(
1282
1295
  lines.push('');
1283
1296
  currentLineIndex++;
1284
1297
  cursorPos = 0;
1285
- buffer = lines[currentLineIndex];
1298
+ buffer = lines[currentLineIndex] ?? '';
1286
1299
  process.stdout.write('\n');
1287
1300
  process.stdout.write('... ');
1288
1301
  return;
@@ -1293,7 +1306,7 @@ async function readLine(
1293
1306
  if (searchMode) {
1294
1307
  // Accept search result
1295
1308
  if (searchResultIndex >= 0) {
1296
- buffer = history[searchResultIndex];
1309
+ buffer = history[searchResultIndex] ?? '';
1297
1310
  }
1298
1311
  searchMode = false;
1299
1312
  process.stdout.write('\n');
@@ -373,7 +373,10 @@ export function buildValidationInput(
373
373
  if (schemas.args) {
374
374
  const parsed = parseArgsSchema(schemas.args);
375
375
  for (let i = 0; i < parsed.names.length; i++) {
376
- result.args[parsed.names[i]] = rawArgs[i];
376
+ const name = parsed.names[i];
377
+ if (name !== undefined) {
378
+ result.args[name] = rawArgs[i];
379
+ }
377
380
  }
378
381
  }
379
382
 
package/src/sound.ts CHANGED
@@ -17,7 +17,8 @@ export function playSound(): void {
17
17
  return;
18
18
  }
19
19
 
20
- if (process.stdout.isTTY && Bun.which(command[0])) {
20
+ const executable = command[0];
21
+ if (process.stdout.isTTY && executable && Bun.which(executable)) {
21
22
  try {
22
23
  Bun.spawn(command, {
23
24
  stdio: ['ignore', 'ignore', 'ignore'],