@agentuity/cli 0.0.53 → 0.0.54

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 (133) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +66 -8
  3. package/dist/cli.js.map +1 -1
  4. package/dist/cmd/auth/ssh/add.d.ts.map +1 -1
  5. package/dist/cmd/auth/ssh/add.js +28 -13
  6. package/dist/cmd/auth/ssh/add.js.map +1 -1
  7. package/dist/cmd/auth/ssh/delete.d.ts.map +1 -1
  8. package/dist/cmd/auth/ssh/delete.js +28 -18
  9. package/dist/cmd/auth/ssh/delete.js.map +1 -1
  10. package/dist/cmd/auth/ssh/list.d.ts.map +1 -1
  11. package/dist/cmd/auth/ssh/list.js +5 -6
  12. package/dist/cmd/auth/ssh/list.js.map +1 -1
  13. package/dist/cmd/build/ast.d.ts +34 -0
  14. package/dist/cmd/build/ast.d.ts.map +1 -1
  15. package/dist/cmd/build/ast.js +159 -0
  16. package/dist/cmd/build/ast.js.map +1 -1
  17. package/dist/cmd/build/bundler.d.ts +2 -1
  18. package/dist/cmd/build/bundler.d.ts.map +1 -1
  19. package/dist/cmd/build/bundler.js +77 -16
  20. package/dist/cmd/build/bundler.js.map +1 -1
  21. package/dist/cmd/build/plugin.d.ts.map +1 -1
  22. package/dist/cmd/build/plugin.js +74 -4
  23. package/dist/cmd/build/plugin.js.map +1 -1
  24. package/dist/cmd/cloud/deployment/list.d.ts.map +1 -1
  25. package/dist/cmd/cloud/deployment/list.js +28 -23
  26. package/dist/cmd/cloud/deployment/list.js.map +1 -1
  27. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  28. package/dist/cmd/cloud/deployment/show.js +50 -47
  29. package/dist/cmd/cloud/deployment/show.js.map +1 -1
  30. package/dist/cmd/cloud/env/get.d.ts.map +1 -1
  31. package/dist/cmd/cloud/env/get.js +16 -14
  32. package/dist/cmd/cloud/env/get.js.map +1 -1
  33. package/dist/cmd/cloud/env/list.d.ts.map +1 -1
  34. package/dist/cmd/cloud/env/list.js +24 -20
  35. package/dist/cmd/cloud/env/list.js.map +1 -1
  36. package/dist/cmd/cloud/keyvalue/get.d.ts.map +1 -1
  37. package/dist/cmd/cloud/keyvalue/get.js +18 -16
  38. package/dist/cmd/cloud/keyvalue/get.js.map +1 -1
  39. package/dist/cmd/cloud/keyvalue/keys.d.ts.map +1 -1
  40. package/dist/cmd/cloud/keyvalue/keys.js +11 -11
  41. package/dist/cmd/cloud/keyvalue/keys.js.map +1 -1
  42. package/dist/cmd/cloud/keyvalue/list-namespaces.d.ts.map +1 -1
  43. package/dist/cmd/cloud/keyvalue/list-namespaces.js +11 -7
  44. package/dist/cmd/cloud/keyvalue/list-namespaces.js.map +1 -1
  45. package/dist/cmd/cloud/keyvalue/search.d.ts.map +1 -1
  46. package/dist/cmd/cloud/keyvalue/search.js +16 -17
  47. package/dist/cmd/cloud/keyvalue/search.js.map +1 -1
  48. package/dist/cmd/cloud/keyvalue/stats.d.ts.map +1 -1
  49. package/dist/cmd/cloud/keyvalue/stats.js +38 -23
  50. package/dist/cmd/cloud/keyvalue/stats.js.map +1 -1
  51. package/dist/cmd/cloud/objectstore/get.d.ts.map +1 -1
  52. package/dist/cmd/cloud/objectstore/get.js +17 -15
  53. package/dist/cmd/cloud/objectstore/get.js.map +1 -1
  54. package/dist/cmd/cloud/objectstore/list-buckets.d.ts.map +1 -1
  55. package/dist/cmd/cloud/objectstore/list-buckets.js +12 -8
  56. package/dist/cmd/cloud/objectstore/list-buckets.js.map +1 -1
  57. package/dist/cmd/cloud/objectstore/list-keys.d.ts.map +1 -1
  58. package/dist/cmd/cloud/objectstore/list-keys.js +13 -10
  59. package/dist/cmd/cloud/objectstore/list-keys.js.map +1 -1
  60. package/dist/cmd/cloud/resource/list.d.ts.map +1 -1
  61. package/dist/cmd/cloud/resource/list.js +38 -27
  62. package/dist/cmd/cloud/resource/list.js.map +1 -1
  63. package/dist/cmd/cloud/secret/get.d.ts.map +1 -1
  64. package/dist/cmd/cloud/secret/get.js +17 -15
  65. package/dist/cmd/cloud/secret/get.js.map +1 -1
  66. package/dist/cmd/cloud/secret/list.d.ts.map +1 -1
  67. package/dist/cmd/cloud/secret/list.js +24 -20
  68. package/dist/cmd/cloud/secret/list.js.map +1 -1
  69. package/dist/cmd/cloud/session/logs.d.ts.map +1 -1
  70. package/dist/cmd/cloud/session/logs.js +18 -15
  71. package/dist/cmd/cloud/session/logs.js.map +1 -1
  72. package/dist/cmd/dev/agents.d.ts.map +1 -1
  73. package/dist/cmd/dev/agents.js +55 -41
  74. package/dist/cmd/dev/agents.js.map +1 -1
  75. package/dist/cmd/dev/index.d.ts.map +1 -1
  76. package/dist/cmd/dev/index.js +2 -0
  77. package/dist/cmd/dev/index.js.map +1 -1
  78. package/dist/cmd/profile/create.js +1 -1
  79. package/dist/cmd/profile/create.js.map +1 -1
  80. package/dist/cmd/profile/delete.d.ts.map +1 -1
  81. package/dist/cmd/profile/delete.js +1 -1
  82. package/dist/cmd/profile/delete.js.map +1 -1
  83. package/dist/cmd/profile/list.d.ts.map +1 -1
  84. package/dist/cmd/profile/list.js +29 -11
  85. package/dist/cmd/profile/list.js.map +1 -1
  86. package/dist/cmd/profile/show.d.ts.map +1 -1
  87. package/dist/cmd/profile/show.js +7 -10
  88. package/dist/cmd/profile/show.js.map +1 -1
  89. package/dist/cmd/project/delete.js +1 -1
  90. package/dist/cmd/project/delete.js.map +1 -1
  91. package/dist/cmd/version/index.d.ts.map +1 -1
  92. package/dist/cmd/version/index.js +1 -1
  93. package/dist/cmd/version/index.js.map +1 -1
  94. package/dist/tui.d.ts.map +1 -1
  95. package/dist/tui.js +3 -1
  96. package/dist/tui.js.map +1 -1
  97. package/dist/types.d.ts +32 -8
  98. package/dist/types.d.ts.map +1 -1
  99. package/dist/types.js.map +1 -1
  100. package/package.json +3 -3
  101. package/src/cli.ts +109 -8
  102. package/src/cmd/auth/ssh/add.ts +37 -17
  103. package/src/cmd/auth/ssh/delete.ts +36 -23
  104. package/src/cmd/auth/ssh/list.ts +8 -6
  105. package/src/cmd/build/ast.ts +203 -0
  106. package/src/cmd/build/bundler.ts +81 -15
  107. package/src/cmd/build/plugin.ts +92 -4
  108. package/src/cmd/cloud/deployment/list.ts +30 -26
  109. package/src/cmd/cloud/deployment/show.ts +47 -42
  110. package/src/cmd/cloud/env/get.ts +14 -12
  111. package/src/cmd/cloud/env/list.ts +24 -22
  112. package/src/cmd/cloud/keyvalue/get.ts +19 -14
  113. package/src/cmd/cloud/keyvalue/keys.ts +10 -12
  114. package/src/cmd/cloud/keyvalue/list-namespaces.ts +10 -8
  115. package/src/cmd/cloud/keyvalue/search.ts +14 -17
  116. package/src/cmd/cloud/keyvalue/stats.ts +52 -28
  117. package/src/cmd/cloud/objectstore/get.ts +18 -13
  118. package/src/cmd/cloud/objectstore/list-buckets.ts +11 -9
  119. package/src/cmd/cloud/objectstore/list-keys.ts +12 -11
  120. package/src/cmd/cloud/resource/list.ts +33 -23
  121. package/src/cmd/cloud/secret/get.ts +15 -13
  122. package/src/cmd/cloud/secret/list.ts +24 -22
  123. package/src/cmd/cloud/session/logs.ts +18 -17
  124. package/src/cmd/dev/agents.ts +70 -50
  125. package/src/cmd/dev/index.ts +2 -0
  126. package/src/cmd/profile/create.ts +3 -3
  127. package/src/cmd/profile/delete.ts +5 -2
  128. package/src/cmd/profile/list.ts +31 -11
  129. package/src/cmd/profile/show.ts +15 -12
  130. package/src/cmd/project/delete.ts +1 -1
  131. package/src/cmd/version/index.ts +5 -1
  132. package/src/tui.ts +3 -1
  133. package/src/types.ts +32 -10
@@ -1,5 +1,6 @@
1
1
  import { $ } from 'bun';
2
2
  import { join, relative, resolve, dirname } from 'node:path';
3
+ import { createRequire } from 'node:module';
3
4
  import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs';
4
5
  import gitParseUrl from 'git-url-parse';
5
6
  import AgentuityBundler, { getBuildMetadata } from './plugin';
@@ -7,6 +8,8 @@ import { getFilesRecursively } from './file';
7
8
  import { getVersion } from '../../version';
8
9
  import type { Project } from '../../types';
9
10
  import { fixDuplicateExportsInDirectory } from './fix-duplicate-exports';
11
+ import { createLogger } from '@agentuity/server';
12
+ import type { LogLevel } from '../../types';
10
13
 
11
14
  export interface BundleOptions {
12
15
  rootDir: string;
@@ -16,6 +19,7 @@ export interface BundleOptions {
16
19
  projectId?: string;
17
20
  deploymentId?: string;
18
21
  project?: Project;
22
+ port?: number;
19
23
  }
20
24
 
21
25
  export async function bundle({
@@ -25,6 +29,7 @@ export async function bundle({
25
29
  dev = false,
26
30
  rootDir,
27
31
  project,
32
+ port,
28
33
  }: BundleOptions) {
29
34
  const appFile = join(rootDir, 'app.ts');
30
35
  if (!existsSync(appFile)) {
@@ -246,26 +251,87 @@ export async function bundle({
246
251
  }
247
252
  }
248
253
  })();
254
+ }
249
255
 
250
- if (!dev && buildmetadata) {
251
- const webPublicDir = join(webDir, 'public');
252
- if (existsSync(webPublicDir)) {
253
- const assets = buildmetadata.assets;
254
- const webOutPublicDir = join(outDir, 'web', 'public');
255
- cpSync(webPublicDir, webOutPublicDir, { recursive: true });
256
- [...new Bun.Glob('**.*').scanSync(webOutPublicDir)].forEach((f) => {
257
- const bf = Bun.file(join(webOutPublicDir, f));
258
- assets.push({
259
- filename: join('public', f),
260
- kind: 'static',
261
- contentType: bf.type,
262
- size: bf.size,
263
- });
264
- });
256
+ // Bundle workbench app if detected via setupWorkbench
257
+ const { analyzeWorkbench } = await import('./ast');
258
+ if (existsSync(appFile)) {
259
+ const appContent = await Bun.file(appFile).text();
260
+ const analysis = await analyzeWorkbench(appContent);
261
+
262
+ if (analysis.hasWorkbench) {
263
+ // Encode workbench config for environment variable
264
+ const { encodeWorkbenchConfig } = await import('@agentuity/core');
265
+ const config = analysis.config || { route: '/workbench', headers: {} };
266
+ // Add port to config (defaults to 3500 if not provided)
267
+ const configWithPort = { ...config, port: port || 3500 };
268
+ const encodedConfig = encodeWorkbenchConfig(configWithPort);
269
+ const workbenchDefine = {
270
+ ...define,
271
+ AGENTUITY_WORKBENCH_CONFIG_INLINE: JSON.stringify(encodedConfig),
272
+ };
273
+ const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL as LogLevel) || 'info');
274
+ try {
275
+ const projectRequire = createRequire(resolve(rootDir, 'package.json'));
276
+ const workbenchPkgPath = projectRequire.resolve('@agentuity/workbench/package.json');
277
+ const workbenchAppDir = join(dirname(workbenchPkgPath), 'src', 'app');
278
+
279
+ if (existsSync(workbenchAppDir)) {
280
+ const workbenchIndexFile = join(workbenchAppDir, 'index.html');
281
+ if (existsSync(workbenchIndexFile)) {
282
+ // Bundle workbench using same config as main web app
283
+ const workbenchBuildConfig: Bun.BuildConfig = {
284
+ entrypoints: [workbenchIndexFile],
285
+ root: workbenchAppDir,
286
+ outdir: join(outDir, 'workbench'),
287
+ define: workbenchDefine,
288
+ sourcemap: dev ? 'inline' : 'linked',
289
+ plugins: [AgentuityBundler],
290
+ target: 'browser',
291
+ format: 'esm',
292
+ banner: `// Generated file. DO NOT EDIT`,
293
+ minify: true,
294
+ splitting: true,
295
+ packages: 'bundle',
296
+ naming: {
297
+ entry: '[dir]/[name].[ext]',
298
+ chunk: 'workbench/chunk/[name]-[hash].[ext]',
299
+ asset: 'workbench/asset/[name]-[hash].[ext]',
300
+ },
301
+ };
302
+
303
+ const workbenchResult = await Bun.build(workbenchBuildConfig);
304
+ if (workbenchResult.success) {
305
+ logger.debug('Workbench bundled successfully');
306
+ } else {
307
+ logger.error('Workbench bundling failed:', workbenchResult.logs.join('\n'));
308
+ }
309
+ }
310
+ }
311
+ } catch (error) {
312
+ logger.error('Failed to bundle workbench:', error);
265
313
  }
266
314
  }
267
315
  }
268
316
 
317
+ if (!dev && buildmetadata) {
318
+ const webPublicDir = join(webDir, 'public');
319
+ if (existsSync(webPublicDir)) {
320
+ const assets = buildmetadata.assets;
321
+ const webOutPublicDir = join(outDir, 'web', 'public');
322
+ cpSync(webPublicDir, webOutPublicDir, { recursive: true });
323
+ [...new Bun.Glob('**.*').scanSync(webOutPublicDir)].forEach((f) => {
324
+ const bf = Bun.file(join(webOutPublicDir, f));
325
+ assets.push({
326
+ filename: join('public', f),
327
+ kind: 'static',
328
+ contentType: bf.type,
329
+ size: bf.size,
330
+ });
331
+ });
332
+ }
333
+ }
334
+
269
335
  if (!dev && Bun.which('git') && buildmetadata?.deployment) {
270
336
  buildmetadata.deployment.git = {
271
337
  commit: process.env.GIT_SHA || process.env.GITHUB_SHA,
@@ -2,7 +2,14 @@ import type { BunPlugin } from 'bun';
2
2
  import { dirname, basename, join, resolve } from 'node:path';
3
3
  import { existsSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs';
4
4
  import type { BuildMetadata } from '@agentuity/server';
5
- import { parseAgentMetadata, parseRoute, parseEvalMetadata } from './ast';
5
+ import {
6
+ parseAgentMetadata,
7
+ parseRoute,
8
+ parseEvalMetadata,
9
+ analyzeWorkbench,
10
+ checkRouteConflicts,
11
+ type WorkbenchConfig,
12
+ } from './ast';
6
13
  import { applyPatch, generatePatches } from './patch';
7
14
  import { detectSubagent } from '../../utils/detectSubagent';
8
15
  import { createLogger } from '@agentuity/server';
@@ -19,6 +26,50 @@ function toPascalCase(str: string): string {
19
26
  return camel.charAt(0).toUpperCase() + camel.slice(1);
20
27
  }
21
28
 
29
+ /**
30
+ * Setup workbench configuration by analyzing app.ts file
31
+ */
32
+ async function setupWorkbench(srcDir: string): Promise<WorkbenchConfig | null> {
33
+ // Look for app.ts in both root and src directories
34
+ const rootAppFile = join(dirname(srcDir), 'app.ts');
35
+ const srcAppFile = join(srcDir, 'app.ts');
36
+
37
+ let appFile = '';
38
+ if (existsSync(rootAppFile)) {
39
+ appFile = rootAppFile;
40
+ } else if (existsSync(srcAppFile)) {
41
+ appFile = srcAppFile;
42
+ }
43
+
44
+ if (!appFile || !existsSync(appFile)) {
45
+ return null;
46
+ }
47
+
48
+ const appContent = await Bun.file(appFile).text();
49
+ const analysis = await analyzeWorkbench(appContent);
50
+
51
+ if (!analysis.hasWorkbench) {
52
+ return null;
53
+ }
54
+
55
+ const workbenchConfig = analysis.config;
56
+
57
+ // Check for route conflicts if workbench is being used
58
+ if (workbenchConfig?.route) {
59
+ const hasConflict = await checkRouteConflicts(appContent, workbenchConfig.route);
60
+ if (hasConflict) {
61
+ const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL as LogLevel) || 'info');
62
+ logger.error(`🚨 Route conflict detected!\n`);
63
+ logger.error(
64
+ ` Workbench route '${workbenchConfig.route}' conflicts with existing application route`
65
+ );
66
+ logger.error(` Please use a different route or remove the conflicting route.\n`);
67
+ }
68
+ }
69
+
70
+ return workbenchConfig;
71
+ }
72
+
22
73
  function generateAgentRegistry(srcDir: string, agentInfo: Array<Record<string, string>>) {
23
74
  // Separate parent agents and subagents
24
75
  const parentAgents = agentInfo.filter((a) => !a.parent);
@@ -226,7 +277,6 @@ ${typeExports}
226
277
  // Augment @agentuity/runtime types with strongly-typed agents from this project
227
278
  declare module "@agentuity/runtime" {
228
279
  // Augment the AgentRegistry interface with project-specific strongly-typed agents
229
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
230
280
  export interface AgentRegistry {
231
281
  ${runtimeAgentTypes}
232
282
  }
@@ -291,6 +341,10 @@ const AgentuityBundler: BunPlugin = {
291
341
  const deploymentId = build.config.define?.['process.env.AGENTUITY_CLOUD_DEPLOYMENT_ID']
292
342
  ? JSON.parse(build.config.define['process.env.AGENTUITY_CLOUD_DEPLOYMENT_ID'])
293
343
  : '';
344
+ const isDevMode =
345
+ (build.config.define?.['process.env.NODE_ENV']
346
+ ? JSON.parse(build.config.define['process.env.NODE_ENV'])
347
+ : 'production') === 'development';
294
348
  const routes: Set<string> = new Set();
295
349
  const agentInfo: Array<Record<string, string>> = [];
296
350
  const agentMetadata: Map<string, Map<string, string>> = new Map<
@@ -493,11 +547,45 @@ const AgentuityBundler: BunPlugin = {
493
547
  const indexFile = join(srcDir, 'web', 'index.html');
494
548
 
495
549
  if (existsSync(indexFile)) {
550
+ // Setup workbench configuration - evaluate fresh each time during builds
551
+ const workbenchConfig = await setupWorkbench(srcDir);
552
+
496
553
  inserts.unshift(`await (async () => {
497
554
  const { serveStatic } = require('hono/bun');
498
- const { getRouter } = await import('@agentuity/runtime');
555
+ const { getRouter, registerDevModeRoutes } = await import('@agentuity/runtime');
499
556
  const router = getRouter()!;
500
- const index = await Bun.file(import.meta.dir + '/web/index.html').text();
557
+
558
+ // Setup workbench routes if workbench was bundled
559
+ const workbenchIndexPath = import.meta.dir + '/workbench/index.html';
560
+ if (await Bun.file(workbenchIndexPath).exists()) {
561
+ let workbenchIndex = await Bun.file(workbenchIndexPath).text();
562
+
563
+ // Always serve assets at /workbench/* regardless of HTML route
564
+ const workbenchStatic = serveStatic({ root: import.meta.dir + '/workbench' });
565
+ router.get('/workbench/*', workbenchStatic);
566
+
567
+ // Use the workbench config determined at build time
568
+ const route = ${JSON.stringify(workbenchConfig?.route || '/workbench')};
569
+
570
+ // If using custom route, update HTML to point to absolute /workbench/ paths
571
+ if (route !== '/workbench') {
572
+ workbenchIndex = workbenchIndex.replace(new RegExp('src="\\\\.\\\\/workbench\\\\/', 'g'), 'src="/workbench/');
573
+ }
574
+
575
+ // Serve HTML at the configured route
576
+ router.get(route, (c) => c.html(workbenchIndex));
577
+ }
578
+
579
+ let index = await Bun.file(import.meta.dir + '/web/index.html').text();
580
+ if (${isDevMode}) {
581
+ const end = index.lastIndexOf('</html>');
582
+ const html = registerDevModeRoutes(router);
583
+ if (end > 0) {
584
+ index = index.substring(0,end) + html + index.substring(end);
585
+ } else {
586
+ index += html;
587
+ }
588
+ }
501
589
  const webstatic = serveStatic({ root: import.meta.dir + '/web' });
502
590
  router.get('/', (c) => c.html(index));
503
591
  router.get('/web/chunk/*', webstatic);
@@ -53,42 +53,46 @@ export const listSubcommand = createSubcommand({
53
53
  },
54
54
  async handler(ctx) {
55
55
  const projectId = resolveProjectId(ctx, { projectId: ctx.opts['project-id'] });
56
- const { apiClient, opts } = ctx;
56
+ const { apiClient, opts, options } = ctx;
57
57
 
58
58
  try {
59
59
  const deployments = await projectDeploymentList(apiClient, projectId, opts.count);
60
60
 
61
- if (deployments.length === 0) {
62
- tui.info('No deployments found.');
63
- return [];
64
- }
65
-
66
- const tableData = deployments.map((d) => ({
67
- ID: d.id,
68
- State: d.state || 'unknown',
69
- Active: d.active ? 'Yes' : '',
70
- Created: new Date(d.createdAt).toLocaleString(),
71
- Message: d.message || '',
72
- Tags: d.tags.join(', '),
73
- }));
74
-
75
- tui.table(tableData, [
76
- { name: 'ID', alignment: 'left' },
77
- { name: 'State', alignment: 'left' },
78
- { name: 'Active', alignment: 'center' },
79
- { name: 'Created', alignment: 'left' },
80
- { name: 'Message', alignment: 'left' },
81
- { name: 'Tags', alignment: 'left' },
82
- ]);
83
-
84
- return deployments.map((d) => ({
61
+ const result = deployments.map((d) => ({
85
62
  id: d.id,
86
63
  state: d.state,
87
64
  active: d.active,
88
65
  createdAt: d.createdAt,
89
- message: d.message,
66
+ message: d.message ?? undefined,
90
67
  tags: d.tags,
91
68
  }));
69
+
70
+ // Skip TUI output in JSON mode
71
+ if (!options.json) {
72
+ if (deployments.length === 0) {
73
+ tui.info('No deployments found.');
74
+ } else {
75
+ const tableData = deployments.map((d) => ({
76
+ ID: d.id,
77
+ State: d.state || 'unknown',
78
+ Active: d.active ? 'Yes' : '',
79
+ Created: new Date(d.createdAt).toLocaleString(),
80
+ Message: d.message || '',
81
+ Tags: d.tags.join(', '),
82
+ }));
83
+
84
+ tui.table(tableData, [
85
+ { name: 'ID', alignment: 'left' },
86
+ { name: 'State', alignment: 'left' },
87
+ { name: 'Active', alignment: 'center' },
88
+ { name: 'Created', alignment: 'left' },
89
+ { name: 'Message', alignment: 'left' },
90
+ { name: 'Tags', alignment: 'left' },
91
+ ]);
92
+ }
93
+ }
94
+
95
+ return result;
92
96
  } catch (ex) {
93
97
  tui.fatal(
94
98
  `Failed to list deployments: ${ex instanceof Error ? ex.message : String(ex)}`,
@@ -42,63 +42,68 @@ export const showSubcommand = createSubcommand({
42
42
  idempotent: true,
43
43
  async handler(ctx) {
44
44
  const projectId = resolveProjectId(ctx, { projectId: ctx.opts['project-id'] });
45
- const { apiClient, args } = ctx;
45
+ const { apiClient, args, options } = ctx;
46
46
 
47
47
  try {
48
48
  const deployment = await projectDeploymentGet(apiClient, projectId, args.deployment_id);
49
49
 
50
- tui.banner(`Deployment ${deployment.id}`, `State: ${deployment.state || 'unknown'}`);
50
+ // Skip TUI output in JSON mode
51
+ if (!options.json) {
52
+ tui.banner(`Deployment ${deployment.id}`, `State: ${deployment.state || 'unknown'}`);
51
53
 
52
- console.log(tui.bold('ID: ') + deployment.id);
53
- console.log(tui.bold('Project: ') + projectId);
54
- console.log(tui.bold('State: ') + (deployment.state || 'unknown'));
55
- console.log(tui.bold('Active: ') + (deployment.active ? 'Yes' : 'No'));
56
- console.log(tui.bold('Created: ') + new Date(deployment.createdAt).toLocaleString());
57
- if (deployment.updatedAt) {
58
- console.log(tui.bold('Updated: ') + new Date(deployment.updatedAt).toLocaleString());
59
- }
60
- if (deployment.message) {
61
- console.log(tui.bold('Message: ') + deployment.message);
62
- }
63
- if (deployment.tags.length > 0) {
64
- console.log(tui.bold('Tags: ') + deployment.tags.join(', '));
65
- }
66
- if (deployment.customDomains && deployment.customDomains.length > 0) {
67
- console.log(tui.bold('Domains: ') + deployment.customDomains.join(', '));
68
- }
69
- if (deployment.cloudRegion) {
70
- console.log(tui.bold('Region: ') + deployment.cloudRegion);
71
- }
54
+ console.log(tui.bold('ID: ') + deployment.id);
55
+ console.log(tui.bold('Project: ') + projectId);
56
+ console.log(tui.bold('State: ') + (deployment.state || 'unknown'));
57
+ console.log(tui.bold('Active: ') + (deployment.active ? 'Yes' : 'No'));
58
+ console.log(tui.bold('Created: ') + new Date(deployment.createdAt).toLocaleString());
59
+ if (deployment.updatedAt) {
60
+ console.log(
61
+ tui.bold('Updated: ') + new Date(deployment.updatedAt).toLocaleString()
62
+ );
63
+ }
64
+ if (deployment.message) {
65
+ console.log(tui.bold('Message: ') + deployment.message);
66
+ }
67
+ if (deployment.tags.length > 0) {
68
+ console.log(tui.bold('Tags: ') + deployment.tags.join(', '));
69
+ }
70
+ if (deployment.customDomains && deployment.customDomains.length > 0) {
71
+ console.log(tui.bold('Domains: ') + deployment.customDomains.join(', '));
72
+ }
73
+ if (deployment.cloudRegion) {
74
+ console.log(tui.bold('Region: ') + deployment.cloudRegion);
75
+ }
72
76
 
73
- // Metadata
74
- const origin = deployment.metadata?.origin;
75
- if (origin?.commit) {
76
- tui.newline();
77
- tui.info('Origin Information');
78
- if (origin.trigger) console.log(` Trigger: ${origin.trigger}`);
79
- if (origin.provider) console.log(` Provider: ${origin.provider}`);
80
- if (origin.event) console.log(` Event: ${origin.event}`);
81
- if (origin.branch) console.log(` Branch: ${origin.branch}`);
77
+ // Metadata
78
+ const origin = deployment.metadata?.origin;
79
+ if (origin?.commit) {
80
+ tui.newline();
81
+ tui.info('Origin Information');
82
+ if (origin.trigger) console.log(` Trigger: ${origin.trigger}`);
83
+ if (origin.provider) console.log(` Provider: ${origin.provider}`);
84
+ if (origin.event) console.log(` Event: ${origin.event}`);
85
+ if (origin.branch) console.log(` Branch: ${origin.branch}`);
82
86
 
83
- if (origin.commit) {
84
- console.log(` Commit: ${origin.commit.hash}`);
85
- if (origin.commit.message) console.log(` Message: ${origin.commit.message}`);
86
- if (origin.commit.author?.name)
87
- console.log(` Author: ${origin.commit.author.name}`);
87
+ if (origin.commit) {
88
+ console.log(` Commit: ${origin.commit.hash}`);
89
+ if (origin.commit.message) console.log(` Message: ${origin.commit.message}`);
90
+ if (origin.commit.author?.name)
91
+ console.log(` Author: ${origin.commit.author.name}`);
92
+ }
88
93
  }
89
94
  }
90
95
 
91
96
  return {
92
97
  id: deployment.id,
93
- state: deployment.state,
98
+ state: deployment.state ?? undefined,
94
99
  active: deployment.active,
95
100
  createdAt: deployment.createdAt,
96
- updatedAt: deployment.updatedAt,
97
- message: deployment.message,
101
+ updatedAt: deployment.updatedAt ?? undefined,
102
+ message: deployment.message ?? undefined,
98
103
  tags: deployment.tags,
99
- customDomains: deployment.customDomains,
100
- cloudRegion: deployment.cloudRegion,
101
- metadata: deployment.metadata,
104
+ customDomains: deployment.customDomains ?? undefined,
105
+ cloudRegion: deployment.cloudRegion ?? undefined,
106
+ metadata: deployment.metadata ?? undefined,
102
107
  };
103
108
  } catch (ex) {
104
109
  tui.fatal(`Failed to show deployment: ${ex instanceof Error ? ex.message : String(ex)}`);
@@ -32,7 +32,7 @@ export const getSubcommand = createSubcommand({
32
32
  idempotent: true,
33
33
 
34
34
  async handler(ctx) {
35
- const { args, opts, apiClient, project } = ctx;
35
+ const { args, opts, apiClient, project, options } = ctx;
36
36
 
37
37
  // Fetch project with unmasked secrets
38
38
  const projectData = await tui.spinner('Fetching environment variables', () => {
@@ -46,18 +46,20 @@ export const getSubcommand = createSubcommand({
46
46
  tui.fatal(`Environment variable '${args.key}' not found`, ErrorCode.RESOURCE_NOT_FOUND);
47
47
  }
48
48
 
49
- // Display the value, masked if requested
50
- if (process.stdout.isTTY) {
51
- if (opts?.mask) {
52
- tui.success(`${args.key}=${maskSecret(value)}`);
49
+ if (!options.json) {
50
+ // Display the value, masked if requested
51
+ if (process.stdout.isTTY) {
52
+ if (opts?.mask) {
53
+ tui.success(`${args.key}=${maskSecret(value)}`);
54
+ } else {
55
+ tui.success(`${args.key}=${value}`);
56
+ }
53
57
  } else {
54
- tui.success(`${args.key}=${value}`);
55
- }
56
- } else {
57
- if (opts?.mask) {
58
- console.log(`${args.key}=${maskSecret(value)}`);
59
- } else {
60
- console.log(`${args.key}=${value}`);
58
+ if (opts?.mask) {
59
+ console.log(`${args.key}=${maskSecret(value)}`);
60
+ } else {
61
+ console.log(`${args.key}=${value}`);
62
+ }
61
63
  }
62
64
  }
63
65
 
@@ -29,7 +29,7 @@ export const listSubcommand = createSubcommand({
29
29
  },
30
30
 
31
31
  async handler(ctx) {
32
- const { opts, apiClient, project } = ctx;
32
+ const { opts, apiClient, project, options } = ctx;
33
33
 
34
34
  // Fetch project with unmasked secrets
35
35
  const projectData = await tui.spinner('Fetching environment variables', () => {
@@ -38,28 +38,30 @@ export const listSubcommand = createSubcommand({
38
38
 
39
39
  const env = projectData.env || {};
40
40
 
41
- if (Object.keys(env).length === 0) {
42
- tui.info('No environment variables found');
43
- return {};
44
- }
45
-
46
- // Display the variables
47
- if (process.stdout.isTTY) {
48
- tui.newline();
49
- tui.info(`Environment Variables (${Object.keys(env).length}):`);
50
- tui.newline();
51
- }
52
-
53
- const sortedKeys = Object.keys(env).sort();
54
- // For env vars, masking should be explicitly opted-in (default false)
55
- const shouldMask = opts?.mask === true;
56
- for (const key of sortedKeys) {
57
- const value = env[key];
58
- const displayValue = shouldMask ? maskSecret(value) : value;
59
- if (process.stdout.isTTY) {
60
- console.log(`${tui.bold(key)}=${displayValue}`);
41
+ // Skip TUI output in JSON mode
42
+ if (!options.json) {
43
+ if (Object.keys(env).length === 0) {
44
+ tui.info('No environment variables found');
61
45
  } else {
62
- console.log(`${key}=${displayValue}`);
46
+ // Display the variables
47
+ if (process.stdout.isTTY) {
48
+ tui.newline();
49
+ tui.info(`Environment Variables (${Object.keys(env).length}):`);
50
+ tui.newline();
51
+ }
52
+
53
+ const sortedKeys = Object.keys(env).sort();
54
+ // For env vars, masking should be explicitly opted-in (default false)
55
+ const shouldMask = opts?.mask === true;
56
+ for (const key of sortedKeys) {
57
+ const value = env[key];
58
+ const displayValue = shouldMask ? maskSecret(value) : value;
59
+ if (process.stdout.isTTY) {
60
+ console.log(`${tui.bold(key)}=${displayValue}`);
61
+ } else {
62
+ console.log(`${key}=${displayValue}`);
63
+ }
64
+ }
63
65
  }
64
66
  }
65
67
 
@@ -31,27 +31,32 @@ export const getSubcommand = createCommand({
31
31
  idempotent: true,
32
32
 
33
33
  async handler(ctx) {
34
- const { args } = ctx;
34
+ const { args, options } = ctx;
35
35
  const storage = await createStorageAdapter(ctx);
36
36
  const started = Date.now();
37
37
  const res = await storage.get(args.namespace, args.key);
38
- if (res.exists) {
39
- if (res.data) {
40
- if (res.contentType?.includes('json')) {
41
- const val = tryParseJSON(res.data as unknown as string);
42
- tui.json(val);
43
- } else if (res.contentType?.includes('text')) {
44
- console.log(String(res.data));
38
+
39
+ if (!options.json) {
40
+ if (res.exists) {
41
+ if (res.data) {
42
+ if (res.contentType?.includes('json')) {
43
+ const val = tryParseJSON(res.data as unknown as string);
44
+ tui.json(val);
45
+ } else if (res.contentType?.includes('text')) {
46
+ console.log(String(res.data));
47
+ } else {
48
+ const b = res.data as ArrayBuffer;
49
+ tui.info(`Read ${b.byteLength} bytes (${res.contentType})`);
50
+ }
51
+ tui.success(
52
+ `retrieved in ${(Date.now() - started).toFixed(1)}ms (${res.contentType})`
53
+ );
45
54
  } else {
46
- const b = res.data as ArrayBuffer;
47
- tui.info(`Read ${b.byteLength} bytes (${res.contentType})`);
55
+ tui.warning(`${args.key} returned empty data for ${args.namespace}`);
48
56
  }
49
- tui.success(`retrieved in ${(Date.now() - started).toFixed(1)}ms (${res.contentType})`);
50
57
  } else {
51
- tui.warning(`${args.key} returned empty data for ${args.namespace}`);
58
+ tui.warning(`${args.key} does not exist in ${args.namespace}`);
52
59
  }
53
- } else {
54
- tui.warning(`${args.key} does not exist in ${args.namespace}`);
55
60
  }
56
61
 
57
62
  return {
@@ -29,22 +29,20 @@ export const keysSubcommand = createCommand({
29
29
  },
30
30
 
31
31
  async handler(ctx) {
32
- const { args } = ctx;
32
+ const { args, options } = ctx;
33
33
  const kv = await createStorageAdapter(ctx);
34
34
 
35
35
  const keys = await kv.getKeys(args.name);
36
36
 
37
- if (keys.length === 0) {
38
- tui.info(`No keys found in namespace ${tui.bold(args.name)}`);
39
- return {
40
- namespace: args.name,
41
- keys: [],
42
- };
43
- }
44
-
45
- tui.info(`Found ${keys.length} key(s) in ${tui.bold(args.name)}:`);
46
- for (const key of keys) {
47
- tui.info(` ${key}`);
37
+ if (!options.json) {
38
+ if (keys.length === 0) {
39
+ tui.info(`No keys found in namespace ${tui.bold(args.name)}`);
40
+ } else {
41
+ tui.info(`Found ${keys.length} key(s) in ${tui.bold(args.name)}:`);
42
+ for (const key of keys) {
43
+ tui.info(` ${key}`);
44
+ }
45
+ }
48
46
  }
49
47
 
50
48
  return {
@@ -23,17 +23,19 @@ export const listNamespacesSubcommand = createCommand({
23
23
  idempotent: true,
24
24
 
25
25
  async handler(ctx) {
26
+ const { options } = ctx;
26
27
  const storage = await createStorageAdapter(ctx);
27
28
  const namespaces = await storage.getNamespaces();
28
29
 
29
- if (namespaces.length === 0) {
30
- tui.info('No namespaces found');
31
- return [];
32
- }
33
-
34
- tui.info(`Found ${namespaces.length} namespace(s):`);
35
- for (const name of namespaces) {
36
- tui.arrow(name);
30
+ if (!options.json) {
31
+ if (namespaces.length === 0) {
32
+ tui.info('No namespaces found');
33
+ } else {
34
+ tui.info(`Found ${namespaces.length} namespace(s):`);
35
+ for (const name of namespaces) {
36
+ tui.arrow(name);
37
+ }
38
+ }
37
39
  }
38
40
 
39
41
  return namespaces;