@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
@@ -58,6 +58,14 @@ export const command = createCommand({
58
58
 
59
59
  // Get target from parsed args, but get forward args from raw argv
60
60
  const target = args.args[0];
61
+ if (!target) {
62
+ tui.error('Usage: agentuity canary <version> [commands...]');
63
+ return {
64
+ executed: false,
65
+ version: '',
66
+ message: 'No target specified',
67
+ };
68
+ }
61
69
  const targetIndex = canaryIndex >= 0 ? argv.indexOf(target, canaryIndex) : -1;
62
70
  const forwardArgs = targetIndex >= 0 ? argv.slice(targetIndex + 1) : args.args.slice(1);
63
71
 
@@ -79,7 +87,7 @@ export const command = createCommand({
79
87
  tarballUrl = target;
80
88
  // Extract version from URL if possible
81
89
  const match = target.match(/agentuity-cli-(\d+\.\d+\.\d+-[a-f0-9]+)\.tgz/);
82
- version = match ? match[1] : 'custom';
90
+ version = match?.[1] ?? 'custom';
83
91
  } else {
84
92
  // Version string - construct URL
85
93
  version = target;
@@ -74,25 +74,24 @@ export const createSubcommand = defineSubcommand({
74
74
  ]);
75
75
  },
76
76
  });
77
- if (created.length > 0) {
78
- const resource = created[0];
79
-
80
- // Write environment variables to .env if running inside a project
81
- if (ctx.projectDir && resource.env && Object.keys(resource.env).length > 0) {
82
- await addResourceEnvVars(ctx.projectDir, resource.env);
83
- if (!options.json) {
84
- tui.info('Environment variables written to .env');
85
- }
86
- }
87
-
77
+ const resource = created[0];
78
+ if (resource) {
79
+ // Write environment variables to .env if running inside a project
80
+ if (ctx.projectDir && resource.env && Object.keys(resource.env).length > 0) {
81
+ await addResourceEnvVars(ctx.projectDir, resource.env);
88
82
  if (!options.json) {
89
- tui.success(`Created database: ${tui.bold(resource.name)}`);
83
+ tui.info('Environment variables written to .env');
90
84
  }
91
- return {
92
- success: true,
93
- name: resource.name,
94
- };
95
- } else {
85
+ }
86
+
87
+ if (!options.json) {
88
+ tui.success(`Created database: ${tui.bold(resource.name)}`);
89
+ }
90
+ return {
91
+ success: true,
92
+ name: resource.name,
93
+ };
94
+ } else {
96
95
  tui.fatal('Failed to create database');
97
96
  }
98
97
  } catch (ex) {
@@ -178,28 +178,27 @@ export const deleteSubcommand = createSubcommand({
178
178
  },
179
179
  });
180
180
 
181
- if (deleted.length > 0) {
182
- const resource = deleted[0];
183
-
184
- // Clear cache entry for deleted database
185
- await deleteResourceRegion('db', profileName, resource.name);
186
-
187
- // Remove env vars from .env if running inside a project
188
- if (ctx.projectDir && resource.env_keys.length > 0) {
189
- await removeResourceEnvVars(ctx.projectDir, resource.env_keys);
190
- if (!options.json) {
191
- tui.info(`Removed ${resource.env_keys.join(', ')} from .env`);
192
- }
193
- }
194
-
181
+ const resource = deleted[0];
182
+ if (resource) {
183
+ // Clear cache entry for deleted database
184
+ await deleteResourceRegion('db', profileName, resource.name);
185
+
186
+ // Remove env vars from .env if running inside a project
187
+ if (ctx.projectDir && resource.env_keys.length > 0) {
188
+ await removeResourceEnvVars(ctx.projectDir, resource.env_keys);
195
189
  if (!options.json) {
196
- tui.success(`Deleted database: ${tui.bold(resource.name)}`);
190
+ tui.info(`Removed ${resource.env_keys.join(', ')} from .env`);
197
191
  }
198
- return {
199
- success: true,
200
- name: resource.name,
201
- };
202
- } else {
192
+ }
193
+
194
+ if (!options.json) {
195
+ tui.success(`Deleted database: ${tui.bold(resource.name)}`);
196
+ }
197
+ return {
198
+ success: true,
199
+ name: resource.name,
200
+ };
201
+ } else {
203
202
  tui.error('Failed to delete database');
204
203
  return { success: false, name: dbName };
205
204
  }
@@ -113,11 +113,14 @@ export const importSubcommand = createSubcommand({
113
113
  tui.warning(
114
114
  `Moving public variables to env: ${publicSecretKeys.join(', ')} (these are exposed to the frontend)`
115
115
  );
116
- for (const key of publicSecretKeys) {
117
- delete secrets[key];
118
- env[key] = filteredVars[key];
116
+ for (const key of publicSecretKeys) {
117
+ delete secrets[key];
118
+ const value = filteredVars[key];
119
+ if (value !== undefined) {
120
+ env[key] = value;
119
121
  }
120
122
  }
123
+ }
121
124
 
122
125
  const envCount = Object.keys(env).length;
123
126
  const secretCount = Object.keys(secrets).length;
@@ -168,15 +168,17 @@ export const listSubcommand = createSubcommand({
168
168
  const sortedKeys = Object.keys(result).sort();
169
169
  const shouldMask = opts?.mask !== false;
170
170
 
171
- for (const key of sortedKeys) {
172
- const { value, secret, scope } = result[key];
173
- const displayValue = shouldMask && secret ? tui.maskSecret(value) : value;
174
- const typeIndicator = secret ? ' [secret]' : '';
175
- const scopeIndicator = !useOrgScope ? ` [${scope}]` : '';
176
- console.log(
177
- `${tui.bold(key)}=${displayValue}${tui.muted(typeIndicator + scopeIndicator)}`
178
- );
179
- }
171
+ for (const key of sortedKeys) {
172
+ const entry = result[key];
173
+ if (!entry) continue;
174
+ const { value, secret, scope } = entry;
175
+ const displayValue = shouldMask && secret ? tui.maskSecret(value) : value;
176
+ const typeIndicator = secret ? ' [secret]' : '';
177
+ const scopeIndicator = !useOrgScope ? ` [${scope}]` : '';
178
+ console.log(
179
+ `${tui.bold(key)}=${displayValue}${tui.muted(typeIndicator + scopeIndicator)}`
180
+ );
181
+ }
180
182
  }
181
183
  }
182
184
 
@@ -80,11 +80,14 @@ export const pushSubcommand = createSubcommand({
80
80
  tui.warning(
81
81
  `Moving public variables to env: ${publicSecretKeys.join(', ')} (these are exposed to the frontend)`
82
82
  );
83
- for (const key of publicSecretKeys) {
84
- delete secrets[key];
85
- env[key] = filteredEnv[key];
83
+ for (const key of publicSecretKeys) {
84
+ delete secrets[key];
85
+ const value = filteredEnv[key];
86
+ if (value !== undefined) {
87
+ env[key] = value;
86
88
  }
87
89
  }
90
+ }
88
91
 
89
92
  if (useOrgScope) {
90
93
  // Organization scope
@@ -45,9 +45,9 @@ const selectCommand = createSubcommand({
45
45
  );
46
46
  }
47
47
 
48
- if (regions.length === 1) {
49
- selectedRegion = regions[0].region;
50
- } else {
48
+ if (regions.length === 1 && regions[0]) {
49
+ selectedRegion = regions[0].region;
50
+ } else {
51
51
  const response = await tui.createPrompt().select<string>({
52
52
  message: 'Select a default region',
53
53
  options: regions.map((r) => ({
@@ -65,23 +65,26 @@ function buildFileTree(files: SnapshotFileInfo[]): TreeNode {
65
65
  const parts = file.path.split('/');
66
66
  let current = root;
67
67
 
68
- for (let i = 0; i < parts.length; i++) {
69
- const part = parts[i];
70
- if (!current.children.has(part)) {
71
- current.children.set(part, {
72
- name: part,
73
- isFile: i === parts.length - 1,
74
- children: new Map(),
75
- });
76
- }
77
- current = current.children.get(part)!;
68
+ for (let i = 0; i < parts.length; i++) {
69
+ const part = parts[i];
70
+ if (!part) continue;
71
+ if (!current.children.has(part)) {
72
+ current.children.set(part, {
73
+ name: part,
74
+ isFile: i === parts.length - 1,
75
+ children: new Map(),
76
+ });
77
+ }
78
+ const child = current.children.get(part);
79
+ if (!child) continue;
80
+ current = child;
78
81
 
79
- if (i === parts.length - 1) {
80
- current.size = file.size;
81
- current.isFile = true;
82
- }
82
+ if (i === parts.length - 1) {
83
+ current.size = file.size;
84
+ current.isFile = true;
83
85
  }
84
86
  }
87
+ }
85
88
 
86
89
  return root;
87
90
  }
@@ -100,7 +103,9 @@ function printTreeNode(node: TreeNode, prefix: string): void {
100
103
  });
101
104
 
102
105
  for (let i = 0; i < entries.length; i++) {
103
- const [, child] = entries[i];
106
+ const entry = entries[i];
107
+ if (!entry) continue;
108
+ const [, child] = entry;
104
109
  const isLast = i === entries.length - 1;
105
110
  const connector = tui.muted(isLast ? '└── ' : '├── ');
106
111
  const sizeStr =
@@ -146,17 +151,21 @@ function substituteVariables(
146
151
 
147
152
  for (const [key, value] of Object.entries(values)) {
148
153
  let substituted = value;
149
- let match: RegExpExecArray | null;
150
154
 
151
155
  varPattern.lastIndex = 0;
152
- while ((match = varPattern.exec(value)) !== null) {
156
+ let match = varPattern.exec(value);
157
+ while (match !== null) {
153
158
  const varName = match[1];
154
- if (!(varName in variables)) {
159
+ if (!varName || !(varName in variables)) {
155
160
  throw new Error(
156
161
  `Variable "\${${varName}}" in "${key}" is not defined. Use --${flagName} ${varName}=value to provide it.`
157
162
  );
158
163
  }
159
- substituted = substituted.replace(match[0], variables[varName]);
164
+ const varValue = variables[varName];
165
+ if (varValue !== undefined) {
166
+ substituted = substituted.replace(match[0], varValue);
167
+ }
168
+ match = varPattern.exec(value);
160
169
  }
161
170
  result[key] = substituted;
162
171
  }
@@ -638,15 +647,15 @@ export const buildSubcommand = createCommand({
638
647
  }
639
648
  }
640
649
 
641
- if (finalEnv && Object.keys(finalEnv).length > 0) {
642
- console.log('');
643
- tui.info('Environment:');
644
- for (const key of Object.keys(finalEnv)) {
645
- console.log(` ${tui.muted('•')} ${key}=${tui.maskSecret(finalEnv[key])}`);
646
- }
650
+ if (finalEnv && Object.keys(finalEnv).length > 0) {
651
+ console.log('');
652
+ tui.info('Environment:');
653
+ for (const [envKey, envValue] of Object.entries(finalEnv)) {
654
+ console.log(` ${tui.muted('•')} ${envKey}=${tui.maskSecret(envValue)}`);
647
655
  }
656
+ }
648
657
 
649
- if (fileList.length > 0) {
658
+ if (fileList.length > 0) {
650
659
  console.log('');
651
660
  tui.info('Files:');
652
661
  printFileTree(fileList);
@@ -940,15 +949,15 @@ export const buildSubcommand = createCommand({
940
949
  }
941
950
  }
942
951
 
943
- if (finalEnv && Object.keys(finalEnv).length > 0) {
944
- console.log('');
945
- tui.info('Environment:');
946
- for (const key of Object.keys(finalEnv)) {
947
- console.log(` ${tui.muted('•')} ${key}=${tui.maskSecret(finalEnv[key])}`);
948
- }
952
+ if (finalEnv && Object.keys(finalEnv).length > 0) {
953
+ console.log('');
954
+ tui.info('Environment:');
955
+ for (const [envKey, envValue] of Object.entries(finalEnv)) {
956
+ console.log(` ${tui.muted('•')} ${envKey}=${tui.maskSecret(envValue)}`);
949
957
  }
958
+ }
950
959
 
951
- if (finalMetadata && Object.keys(finalMetadata).length > 0) {
960
+ if (finalMetadata && Object.keys(finalMetadata).length > 0) {
952
961
  console.log('');
953
962
  tui.info('Metadata:');
954
963
  for (const key of Object.keys(finalMetadata)) {
@@ -183,23 +183,26 @@ function buildFileTree(files: SnapshotFileInfo[]): TreeNode {
183
183
  const parts = file.path.split('/');
184
184
  let current = root;
185
185
 
186
- for (let i = 0; i < parts.length; i++) {
187
- const part = parts[i];
188
- if (!current.children.has(part)) {
189
- current.children.set(part, {
190
- name: part,
191
- isFile: i === parts.length - 1,
192
- children: new Map(),
193
- });
194
- }
195
- current = current.children.get(part)!;
186
+ for (let i = 0; i < parts.length; i++) {
187
+ const part = parts[i];
188
+ if (!part) continue;
189
+ if (!current.children.has(part)) {
190
+ current.children.set(part, {
191
+ name: part,
192
+ isFile: i === parts.length - 1,
193
+ children: new Map(),
194
+ });
195
+ }
196
+ const child = current.children.get(part);
197
+ if (!child) continue;
198
+ current = child;
196
199
 
197
- if (i === parts.length - 1) {
198
- current.size = file.size;
199
- current.isFile = true;
200
- }
200
+ if (i === parts.length - 1) {
201
+ current.size = file.size;
202
+ current.isFile = true;
201
203
  }
202
204
  }
205
+ }
203
206
 
204
207
  return root;
205
208
  }
@@ -218,7 +221,9 @@ function printTreeNode(node: TreeNode, prefix: string): void {
218
221
  });
219
222
 
220
223
  for (let i = 0; i < entries.length; i++) {
221
- const [, child] = entries[i];
224
+ const entry = entries[i];
225
+ if (!entry) continue;
226
+ const [, child] = entry;
222
227
  const isLast = i === entries.length - 1;
223
228
  const connector = tui.muted(isLast ? '└── ' : '├── ');
224
229
  const sizeStr =
@@ -237,6 +242,7 @@ function printSandboxTree(sandboxes: SandboxInfo[]): void {
237
242
  const sorted = [...sandboxes].sort((a, b) => a.sandboxId.localeCompare(b.sandboxId));
238
243
  for (let i = 0; i < sorted.length; i++) {
239
244
  const sandbox = sorted[i];
245
+ if (!sandbox) continue;
240
246
  const isLast = i === sorted.length - 1;
241
247
  const connector = tui.muted(isLast ? '└── ' : '├── ');
242
248
  const statusColor = sandbox.status === 'running' ? tui.success : tui.muted;
@@ -67,25 +67,24 @@ export const createSubcommand = defineSubcommand({
67
67
  },
68
68
  });
69
69
 
70
- if (created.length > 0) {
71
- const resource = created[0];
72
-
73
- // Write environment variables to .env if running inside a project
74
- if (ctx.projectDir && resource.env && Object.keys(resource.env).length > 0) {
75
- await addResourceEnvVars(ctx.projectDir, resource.env);
76
- if (!options.json) {
77
- tui.info('Environment variables written to .env');
78
- }
79
- }
80
-
70
+ const resource = created[0];
71
+ if (resource) {
72
+ // Write environment variables to .env if running inside a project
73
+ if (ctx.projectDir && resource.env && Object.keys(resource.env).length > 0) {
74
+ await addResourceEnvVars(ctx.projectDir, resource.env);
81
75
  if (!options.json) {
82
- tui.success(`Created storage: ${tui.bold(resource.name)}`);
76
+ tui.info('Environment variables written to .env');
83
77
  }
84
- return {
85
- success: true,
86
- name: resource.name,
87
- };
88
- } else {
78
+ }
79
+
80
+ if (!options.json) {
81
+ tui.success(`Created storage: ${tui.bold(resource.name)}`);
82
+ }
83
+ return {
84
+ success: true,
85
+ name: resource.name,
86
+ };
87
+ } else {
89
88
  tui.fatal('Failed to create storage');
90
89
  }
91
90
  },
@@ -242,28 +242,27 @@ export const deleteSubcommand = createSubcommand({
242
242
  },
243
243
  });
244
244
 
245
- if (deleted.length > 0) {
246
- const resource = deleted[0];
247
-
248
- // Clear cache entry for deleted bucket
249
- await deleteResourceRegion('bucket', profileName, resource.name);
250
-
251
- // Remove env vars from .env if running inside a project
252
- if (ctx.projectDir && resource.env_keys.length > 0) {
253
- await removeResourceEnvVars(ctx.projectDir, resource.env_keys);
254
- if (!options.json) {
255
- tui.info(`Removed ${resource.env_keys.join(', ')} from .env`);
256
- }
257
- }
258
-
245
+ const resource = deleted[0];
246
+ if (resource) {
247
+ // Clear cache entry for deleted bucket
248
+ await deleteResourceRegion('bucket', profileName, resource.name);
249
+
250
+ // Remove env vars from .env if running inside a project
251
+ if (ctx.projectDir && resource.env_keys.length > 0) {
252
+ await removeResourceEnvVars(ctx.projectDir, resource.env_keys);
259
253
  if (!options.json) {
260
- tui.success(`Deleted storage bucket: ${tui.bold(resource.name)}`);
254
+ tui.info(`Removed ${resource.env_keys.join(', ')} from .env`);
261
255
  }
262
- return {
263
- success: true,
264
- name: resource.name,
265
- };
266
- } else {
256
+ }
257
+
258
+ if (!options.json) {
259
+ tui.success(`Deleted storage bucket: ${tui.bold(resource.name)}`);
260
+ }
261
+ return {
262
+ success: true,
263
+ name: resource.name,
264
+ };
265
+ } else {
267
266
  tui.error('Failed to delete storage bucket');
268
267
  return { success: false, name: bucketName };
269
268
  }
@@ -54,7 +54,7 @@ export async function download(gravityDir: string): Promise<GravityClient> {
54
54
  })) as { release: string; assetFileNames: string[] };
55
55
 
56
56
  const versionTok = res.release.split('@');
57
- const version = versionTok[1];
57
+ const version = versionTok[1] ?? 'unknown';
58
58
  const releaseFilename = join(gravityDir, version, 'gravity');
59
59
  const mustDownload = !existsSync(releaseFilename);
60
60
 
@@ -76,7 +76,11 @@ export async function download(gravityDir: string): Promise<GravityClient> {
76
76
  false,
77
77
  ''
78
78
  )) as string[];
79
- return res[0] as string;
79
+ const file = res[0];
80
+ if (!file) {
81
+ throw new Error('No file downloaded from release');
82
+ }
83
+ return file;
80
84
  },
81
85
  clearOnSuccess: true,
82
86
  });
@@ -107,5 +111,9 @@ export async function download(gravityDir: string): Promise<GravityClient> {
107
111
  rmSync(outputdir, { recursive: true });
108
112
  }
109
113
 
114
+ if (!existsSync(releaseFilename)) {
115
+ throw new Error(`Failed to extract gravity binary to ${releaseFilename}`);
116
+ }
117
+
110
118
  return { filename: releaseFilename, version };
111
119
  }
@@ -1177,10 +1177,10 @@ export const command = createCommand({
1177
1177
  const text = new TextDecoder().decode(chunk);
1178
1178
  const trimmed = text.trim();
1179
1179
 
1180
- // Check for heartbeat port announcement
1181
- const match = trimmed.match(/^HEARTBEAT_PORT=(\d+)$/m);
1182
- if (match) {
1183
- const heartbeatPort = parseInt(match[1], 10);
1180
+ // Check for heartbeat port announcement
1181
+ const match = trimmed.match(/^HEARTBEAT_PORT=(\d+)$/m);
1182
+ if (match?.[1]) {
1183
+ const heartbeatPort = parseInt(match[1], 10);
1184
1184
  logger.debug('Gravity heartbeat port detected: %d', heartbeatPort);
1185
1185
 
1186
1186
  // Start sending heartbeats every 5 seconds
@@ -252,10 +252,11 @@ export const addSubcommand = createSubcommand({
252
252
 
253
253
  const sortedOrgs = [...orgStatuses].sort((a, b) => a.name.localeCompare(b.name));
254
254
 
255
- if (orgs.length === 1) {
256
- orgId = orgs[0].id;
257
- selectedOrg = orgs[0];
258
- } else {
255
+ const firstOrg = orgs[0];
256
+ if (orgs.length === 1 && firstOrg) {
257
+ orgId = firstOrg.id;
258
+ selectedOrg = firstOrg;
259
+ } else {
259
260
  const choices = sortedOrgs.map((org) => {
260
261
  const count = org.integrations.length;
261
262
  const suffix =
@@ -36,10 +36,10 @@ export function detectGitInfo(): DetectedGitInfo {
36
36
  // https://github.com/owner/repo.git
37
37
  // git@github.com:owner/repo.git
38
38
  const httpsMatch = url.match(/github\.com\/([^/]+\/[^/]+?)(?:\.git)?$/);
39
- if (httpsMatch) repo = httpsMatch[1];
39
+ if (httpsMatch?.[1]) repo = httpsMatch[1];
40
40
 
41
41
  const sshMatch = url.match(/github\.com:([^/]+\/[^/]+?)(?:\.git)?$/);
42
- if (sshMatch) repo = sshMatch[1];
42
+ if (sshMatch?.[1]) repo = sshMatch[1];
43
43
  }
44
44
 
45
45
  // Detect current branch
@@ -53,12 +53,13 @@ export const listSubcommand = createSubcommand({
53
53
  return [];
54
54
  }
55
55
 
56
- // Select org
57
- let orgId = opts.org;
58
- if (!orgId) {
59
- if (orgs.length === 1) {
60
- orgId = orgs[0].id;
61
- } else {
56
+ // Select org
57
+ let orgId = opts.org;
58
+ if (!orgId) {
59
+ const firstOrg = orgs[0];
60
+ if (orgs.length === 1 && firstOrg) {
61
+ orgId = firstOrg.id;
62
+ } else {
62
63
  tui.newline();
63
64
  const orgChoices = orgs.map((o) => ({
64
65
  name: o.id,
@@ -73,14 +73,14 @@ export const initSubcommand = createSubcommand({
73
73
  if (!databaseUrl) {
74
74
  // Check .env file
75
75
  const envPath = path.join(projectDir, '.env');
76
- if (fs.existsSync(envPath)) {
77
- const envContent = fs.readFileSync(envPath, 'utf-8');
78
- const match = envContent.match(/^DATABASE_URL=(.+)$/m);
79
- if (match) {
80
- databaseUrl = match[1].trim().replace(/^["']|["']$/g, '');
81
- }
76
+ if (fs.existsSync(envPath)) {
77
+ const envContent = fs.readFileSync(envPath, 'utf-8');
78
+ const match = envContent.match(/^DATABASE_URL=(.+)$/m);
79
+ if (match?.[1]) {
80
+ databaseUrl = match[1].trim().replace(/^["']|["']$/g, '');
82
81
  }
83
82
  }
83
+ }
84
84
 
85
85
  // Show database picker (with existing as first option if configured)
86
86
  const dbInfo = await selectOrCreateDatabase({
@@ -117,7 +117,8 @@ export async function selectOrCreateDatabase(options: {
117
117
  }
118
118
 
119
119
  if (!region) {
120
- region = databases.length > 0 ? databases[0].cloud_region : 'usc';
120
+ const firstDb = databases[0];
121
+ region = firstDb ? firstDb.cloud_region : 'usc';
121
122
  logger.trace(`[auth init] Using fallback region: ${region}`);
122
123
  }
123
124
 
@@ -129,11 +130,11 @@ export async function selectOrCreateDatabase(options: {
129
130
  callback: async () => createResources(regionalClient, orgId, region, [{ type: 'db' }]),
130
131
  });
131
132
 
132
- if (created.length === 0) {
133
+ const newDb = created[0];
134
+ if (!newDb) {
133
135
  tui.fatal('Failed to create database');
134
136
  }
135
137
 
136
- const newDb = created[0];
137
138
  tui.success(`Created database: ${tui.bold(newDb.name)}`);
138
139
 
139
140
  // Fetch updated list to get the URL
@@ -142,6 +143,8 @@ export async function selectOrCreateDatabase(options: {
142
143
 
143
144
  if (!dbInfo?.url) {
144
145
  tui.fatal('Failed to retrieve database connection URL');
146
+ // TypeScript doesn't know fatal never returns, so we need this
147
+ throw new Error('Unreachable');
145
148
  }
146
149
 
147
150
  return { name: newDb.name, url: dbInfo.url, region };
@@ -150,6 +153,8 @@ export async function selectOrCreateDatabase(options: {
150
153
  const selectedDb = databases.find((d) => d.name === response.database);
151
154
  if (!selectedDb?.url) {
152
155
  tui.fatal('Failed to retrieve database connection URL');
156
+ // TypeScript doesn't know fatal never returns, so we need this
157
+ throw new Error('Unreachable');
153
158
  }
154
159
 
155
160
  return { name: selectedDb.name, url: selectedDb.url, region: selectedDb.cloud_region };
@@ -144,9 +144,10 @@ export const deleteSubcommand = createSubcommand({
144
144
 
145
145
  // Confirm deletion
146
146
  if (!skipConfirm) {
147
+ const firstProject = projectsToDelete[0];
147
148
  const projectDisplay =
148
- projectsToDelete.length === 1
149
- ? formatProjectDisplay(projectsToDelete[0])
149
+ projectsToDelete.length === 1 && firstProject
150
+ ? formatProjectDisplay(firstProject)
150
151
  : projectsToDelete.map((p) => `\n • ${formatProjectDisplay(p)}`).join('');
151
152
  tui.warning(`You are about to delete: ${tui.bold(projectDisplay)}`);
152
153