@agentuity/cli 0.0.71 → 0.0.72

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 (79) hide show
  1. package/dist/cmd/auth/api.d.ts.map +1 -1
  2. package/dist/cmd/auth/api.js +1 -1
  3. package/dist/cmd/auth/api.js.map +1 -1
  4. package/dist/cmd/build/bundler.d.ts +1 -2
  5. package/dist/cmd/build/bundler.d.ts.map +1 -1
  6. package/dist/cmd/build/bundler.js +28 -8
  7. package/dist/cmd/build/bundler.js.map +1 -1
  8. package/dist/cmd/cloud/db/create.js +2 -2
  9. package/dist/cmd/cloud/db/create.js.map +1 -1
  10. package/dist/cmd/cloud/db/delete.js +2 -2
  11. package/dist/cmd/cloud/db/delete.js.map +1 -1
  12. package/dist/cmd/cloud/db/get.js +2 -2
  13. package/dist/cmd/cloud/db/get.js.map +1 -1
  14. package/dist/cmd/cloud/db/list.js +2 -2
  15. package/dist/cmd/cloud/db/list.js.map +1 -1
  16. package/dist/cmd/cloud/db/logs.js +2 -2
  17. package/dist/cmd/cloud/db/logs.js.map +1 -1
  18. package/dist/cmd/cloud/db/sql.js +2 -2
  19. package/dist/cmd/cloud/db/sql.js.map +1 -1
  20. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  21. package/dist/cmd/cloud/deploy.js +163 -24
  22. package/dist/cmd/cloud/deploy.js.map +1 -1
  23. package/dist/cmd/cloud/session/get.js +2 -2
  24. package/dist/cmd/cloud/session/get.js.map +1 -1
  25. package/dist/cmd/cloud/session/list.js +2 -2
  26. package/dist/cmd/cloud/session/list.js.map +1 -1
  27. package/dist/cmd/cloud/storage/create.js +2 -2
  28. package/dist/cmd/cloud/storage/create.js.map +1 -1
  29. package/dist/cmd/cloud/storage/delete.js +2 -2
  30. package/dist/cmd/cloud/storage/delete.js.map +1 -1
  31. package/dist/cmd/cloud/storage/download.js +2 -2
  32. package/dist/cmd/cloud/storage/download.js.map +1 -1
  33. package/dist/cmd/cloud/storage/get.js +2 -2
  34. package/dist/cmd/cloud/storage/get.js.map +1 -1
  35. package/dist/cmd/cloud/storage/list.js +2 -2
  36. package/dist/cmd/cloud/storage/list.js.map +1 -1
  37. package/dist/cmd/cloud/storage/upload.js +2 -2
  38. package/dist/cmd/cloud/storage/upload.js.map +1 -1
  39. package/dist/cmd/cloud/thread/delete.js +2 -2
  40. package/dist/cmd/cloud/thread/delete.js.map +1 -1
  41. package/dist/cmd/cloud/thread/get.js +2 -2
  42. package/dist/cmd/cloud/thread/get.js.map +1 -1
  43. package/dist/cmd/cloud/thread/list.js +2 -2
  44. package/dist/cmd/cloud/thread/list.js.map +1 -1
  45. package/dist/config.d.ts +2 -1
  46. package/dist/config.d.ts.map +1 -1
  47. package/dist/config.js +8 -1
  48. package/dist/config.js.map +1 -1
  49. package/dist/output.d.ts.map +1 -1
  50. package/dist/output.js +5 -1
  51. package/dist/output.js.map +1 -1
  52. package/dist/tui.d.ts +27 -1
  53. package/dist/tui.d.ts.map +1 -1
  54. package/dist/tui.js +140 -33
  55. package/dist/tui.js.map +1 -1
  56. package/package.json +3 -3
  57. package/src/cmd/auth/api.ts +1 -5
  58. package/src/cmd/build/bundler.ts +35 -9
  59. package/src/cmd/cloud/db/create.ts +2 -2
  60. package/src/cmd/cloud/db/delete.ts +2 -2
  61. package/src/cmd/cloud/db/get.ts +2 -2
  62. package/src/cmd/cloud/db/list.ts +2 -2
  63. package/src/cmd/cloud/db/logs.ts +2 -2
  64. package/src/cmd/cloud/db/sql.ts +2 -2
  65. package/src/cmd/cloud/deploy.ts +187 -24
  66. package/src/cmd/cloud/session/get.ts +2 -2
  67. package/src/cmd/cloud/session/list.ts +2 -2
  68. package/src/cmd/cloud/storage/create.ts +2 -2
  69. package/src/cmd/cloud/storage/delete.ts +2 -2
  70. package/src/cmd/cloud/storage/download.ts +2 -2
  71. package/src/cmd/cloud/storage/get.ts +2 -2
  72. package/src/cmd/cloud/storage/list.ts +2 -2
  73. package/src/cmd/cloud/storage/upload.ts +2 -2
  74. package/src/cmd/cloud/thread/delete.ts +2 -2
  75. package/src/cmd/cloud/thread/get.ts +2 -2
  76. package/src/cmd/cloud/thread/list.ts +2 -2
  77. package/src/config.ts +9 -6
  78. package/src/output.ts +7 -1
  79. package/src/tui.ts +192 -34
@@ -1,23 +1,25 @@
1
1
  import { z } from 'zod';
2
2
  import { join, resolve } from 'node:path';
3
3
  import { createPublicKey } from 'node:crypto';
4
- import { createReadStream, createWriteStream } from 'node:fs';
4
+ import { createReadStream, createWriteStream, existsSync, mkdirSync, writeFileSync } from 'node:fs';
5
5
  import { tmpdir } from 'node:os';
6
6
  import { createSubcommand } from '../../types';
7
7
  import * as tui from '../../tui';
8
- import { saveProjectDir } from '../../config';
8
+ import { saveProjectDir, getDefaultConfigDir } from '../../config';
9
9
  import { runSteps, stepSuccess, stepSkipped, stepError, Step, ProgressCallback } from '../../steps';
10
10
  import { bundle } from '../build/bundler';
11
- import { loadBuildMetadata } from '../../config';
11
+ import { loadBuildMetadata, getStreamURL } from '../../config';
12
12
  import {
13
13
  projectEnvUpdate,
14
14
  projectDeploymentCreate,
15
15
  projectDeploymentUpdate,
16
16
  projectDeploymentComplete,
17
+ projectDeploymentStatus,
17
18
  type Deployment,
18
19
  type BuildMetadata,
19
20
  type DeploymentInstructions,
20
21
  type DeploymentComplete,
22
+ type DeploymentStatusResult,
21
23
  } from '@agentuity/server';
22
24
  import {
23
25
  findEnvFile,
@@ -30,11 +32,13 @@ import { encryptFIPSKEMDEMStream } from '../../crypto/box';
30
32
  import { getCommand } from '../../command-prefix';
31
33
  import { checkCustomDomainForDNS } from './domain';
32
34
  import { DeployOptionsSchema } from '../../schemas/deploy';
35
+ import { ErrorCode } from '../../errors';
33
36
 
34
37
  const DeployResponseSchema = z.object({
35
38
  success: z.boolean().describe('Whether deployment succeeded'),
36
39
  deploymentId: z.string().describe('Deployment ID'),
37
40
  projectId: z.string().describe('Project ID'),
41
+ logs: z.array(z.string()).optional().describe('The deployment startup logs'),
38
42
  urls: z
39
43
  .object({
40
44
  deployment: z.string().describe('Deployment-specific URL'),
@@ -77,12 +81,14 @@ export const deploySubcommand = createSubcommand({
77
81
  },
78
82
 
79
83
  async handler(ctx) {
80
- const { project, apiClient, projectDir, config, options, opts } = ctx;
84
+ const { project, apiClient, projectDir, config, options, opts, logger } = ctx;
81
85
 
82
86
  let deployment: Deployment | undefined;
83
87
  let build: BuildMetadata | undefined;
84
88
  let instructions: DeploymentInstructions | undefined;
85
89
  let complete: DeploymentComplete | undefined;
90
+ let statusResult: DeploymentStatusResult | undefined;
91
+ const logs: string[] = [];
86
92
 
87
93
  try {
88
94
  await saveProjectDir(projectDir);
@@ -237,7 +243,6 @@ export const deploySubcommand = createSubcommand({
237
243
  if (relative.startsWith('.env')) return false;
238
244
  if (relative.startsWith('.git/')) return false;
239
245
  if (relative.startsWith('.ssh/')) return false;
240
- if (relative.startsWith('node_modules/')) return false;
241
246
  if (relative === '.DS_Store') return false;
242
247
  return true;
243
248
  },
@@ -367,9 +372,174 @@ export const deploySubcommand = createSubcommand({
367
372
  ].filter(Boolean) as Step[],
368
373
  options.logLevel
369
374
  );
370
- tui.success('Your project was deployed!');
371
375
 
372
- if (complete?.publicUrls && deployment) {
376
+ if (!deployment) {
377
+ return {
378
+ success: false,
379
+ deploymentId: '',
380
+ projectId: project.projectId,
381
+ };
382
+ }
383
+
384
+ const streamId = complete?.streamId;
385
+
386
+ // Poll for deployment status with optional log streaming
387
+ const pollInterval = 500;
388
+ const maxAttempts = 600;
389
+ let attempts = 0;
390
+
391
+ if (streamId) {
392
+ // Use progress logger to stream logs while polling
393
+ const streamsUrl = getStreamURL(project.region, config);
394
+
395
+ await tui
396
+ .progress({
397
+ message: 'Deploying project...',
398
+ type: 'logger',
399
+ maxLines: 2,
400
+ clearOnSuccess: true,
401
+ callback: async (log) => {
402
+ // Start log streaming
403
+ const logStreamController = new AbortController();
404
+ const logStreamPromise = (async () => {
405
+ try {
406
+ logger.debug('fetching stream: %s/%s', streamsUrl, streamId);
407
+ const resp = await fetch(`${streamsUrl}/${streamId}`, {
408
+ signal: logStreamController.signal,
409
+ });
410
+ if (!resp.ok || !resp.body) {
411
+ ctx.logger.trace(
412
+ `Failed to connect to warmup log stream: ${resp.status}`
413
+ );
414
+ return;
415
+ }
416
+ const reader = resp.body.getReader();
417
+ const decoder = new TextDecoder();
418
+ let buffer = '';
419
+ while (true) {
420
+ const { done, value } = await reader.read();
421
+ if (done) break;
422
+ buffer += decoder.decode(value, { stream: true });
423
+ const lines = buffer.split('\n');
424
+ buffer = lines.pop() || ''; // Keep incomplete line in buffer
425
+ for (const line of lines) {
426
+ // Strip ISO 8601 timestamp prefix if present
427
+ const message = line.replace(
428
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z\s?/,
429
+ ''
430
+ );
431
+ if (message) {
432
+ logs.push(message);
433
+ log(message);
434
+ }
435
+ }
436
+ }
437
+ } catch (err) {
438
+ if (err instanceof Error && err.name === 'AbortError') {
439
+ return;
440
+ }
441
+ ctx.logger.trace(`Warmup log stream error: ${err}`);
442
+ }
443
+ })();
444
+
445
+ // Poll for deployment status
446
+ while (attempts < maxAttempts) {
447
+ attempts++;
448
+ try {
449
+ statusResult = await projectDeploymentStatus(
450
+ apiClient,
451
+ deployment?.id ?? ''
452
+ );
453
+
454
+ logger.trace('status result: %s', statusResult);
455
+
456
+ if (statusResult.state === 'completed') {
457
+ logStreamController.abort();
458
+ break;
459
+ }
460
+
461
+ if (statusResult.state === 'failed') {
462
+ throw new Error('Deployment failed');
463
+ }
464
+
465
+ await Bun.sleep(pollInterval);
466
+ } catch (err) {
467
+ logStreamController.abort();
468
+ throw err;
469
+ }
470
+ }
471
+
472
+ // Wait for log stream to finish
473
+ await logStreamPromise;
474
+
475
+ if (attempts >= maxAttempts) {
476
+ throw new Error('Deployment timed out');
477
+ }
478
+ },
479
+ })
480
+ .then(() => {
481
+ tui.success('Your project was deployed!');
482
+ })
483
+ .catch((ex) => {
484
+ const exwithmessage = ex as { message: string };
485
+ const msg =
486
+ exwithmessage.message === 'Deployment failed' ? '' : exwithmessage.toString();
487
+ tui.error(`Your deployment failed to start${msg ? `: ${msg}` : ''}`);
488
+ if (logs.length) {
489
+ const logsDir = join(getDefaultConfigDir(), 'logs');
490
+ if (!existsSync(logsDir)) {
491
+ mkdirSync(logsDir, { recursive: true });
492
+ }
493
+ const errorFile = join(logsDir, `${deployment?.id ?? Date.now()}.txt`);
494
+ writeFileSync(errorFile, logs.join('\n'));
495
+ const count = Math.min(logs.length, 10);
496
+ const last = logs.length - count;
497
+ tui.newline();
498
+ tui.warning(`The last ${count} lines of the log:`);
499
+ let offset = last + 1; // we want to show the offset from inside the log starting at 1
500
+ const max = String(logs.length).length;
501
+ for (const _log of logs.slice(last)) {
502
+ console.log(tui.muted(`${offset.toFixed().padEnd(max)} | ${_log}`));
503
+ offset++;
504
+ }
505
+ tui.newline();
506
+ tui.fatal(`The logs were written to ${errorFile}`, ErrorCode.BUILD_FAILED);
507
+ }
508
+ tui.fatal('Deployment failed', ErrorCode.BUILD_FAILED);
509
+ });
510
+ } else {
511
+ // No stream ID - poll without log streaming
512
+ await tui.spinner({
513
+ message: 'Deploying project...',
514
+ type: 'simple',
515
+ clearOnSuccess: true,
516
+ callback: async () => {
517
+ while (attempts < maxAttempts) {
518
+ attempts++;
519
+ statusResult = await projectDeploymentStatus(apiClient, deployment?.id ?? '');
520
+
521
+ if (statusResult.state === 'completed') {
522
+ break;
523
+ }
524
+
525
+ if (statusResult.state === 'failed') {
526
+ throw new Error('Deployment failed');
527
+ }
528
+
529
+ await Bun.sleep(pollInterval);
530
+ }
531
+
532
+ if (attempts >= maxAttempts) {
533
+ throw new Error('Deployment timed out');
534
+ }
535
+ },
536
+ });
537
+
538
+ tui.success('Your project was deployed!');
539
+ }
540
+
541
+ // Show deployment URLs
542
+ if (complete?.publicUrls) {
373
543
  tui.arrow(tui.bold(tui.padRight('Deployment ID:', 17)) + tui.link(deployment.id));
374
544
  if (complete.publicUrls.custom?.length) {
375
545
  for (const url of complete.publicUrls.custom) {
@@ -386,25 +556,18 @@ export const deploySubcommand = createSubcommand({
386
556
  }
387
557
  }
388
558
 
389
- // Return deployment result (if available)
390
- if (deployment && complete?.publicUrls) {
391
- return {
392
- success: true,
393
- deploymentId: deployment.id,
394
- projectId: project.projectId,
395
- urls: {
396
- deployment: complete.publicUrls.deployment,
397
- latest: complete.publicUrls.latest,
398
- custom: complete.publicUrls.custom,
399
- },
400
- };
401
- }
402
-
403
- // Fallback response
404
559
  return {
405
- success: false,
406
- deploymentId: '',
560
+ success: true,
561
+ deploymentId: deployment.id,
407
562
  projectId: project.projectId,
563
+ logs,
564
+ urls: complete?.publicUrls
565
+ ? {
566
+ deployment: complete.publicUrls.deployment,
567
+ latest: complete.publicUrls.latest,
568
+ custom: complete.publicUrls.custom,
569
+ }
570
+ : undefined,
408
571
  };
409
572
  } catch (ex) {
410
573
  tui.fatal(`unexpected error trying to deploy project. ${ex}`);
@@ -125,8 +125,8 @@ export const getSubcommand = createSubcommand({
125
125
  response: SessionGetResponseSchema,
126
126
  },
127
127
  async handler(ctx) {
128
- const { config, logger, auth, args, options, region } = ctx;
129
- const catalystClient = getCatalystAPIClient(config, logger, auth, region);
128
+ const { logger, auth, args, options, region } = ctx;
129
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
130
130
 
131
131
  try {
132
132
  const enriched = await sessionGet(catalystClient, { id: args.session_id });
@@ -89,8 +89,8 @@ export const listSubcommand = createSubcommand({
89
89
  response: SessionListResponseSchema,
90
90
  },
91
91
  async handler(ctx) {
92
- const { config, logger, auth, project, opts, options, region } = ctx;
93
- const catalystClient = getCatalystAPIClient(config, logger, auth, region);
92
+ const { logger, auth, project, opts, options, region } = ctx;
93
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
94
94
 
95
95
  const projectId = opts.projectId || project?.projectId;
96
96
 
@@ -35,7 +35,7 @@ export const createSubcommand = defineSubcommand({
35
35
  },
36
36
 
37
37
  async handler(ctx) {
38
- const { logger, orgId, region, config, auth, options } = ctx;
38
+ const { logger, orgId, region, auth, options } = ctx;
39
39
 
40
40
  // Handle dry-run mode
41
41
  if (isDryRunMode(options)) {
@@ -51,7 +51,7 @@ export const createSubcommand = defineSubcommand({
51
51
  };
52
52
  }
53
53
 
54
- const catalystClient = getCatalystAPIClient(config, logger, auth, region);
54
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
55
55
 
56
56
  const created = await tui.spinner({
57
57
  message: `Creating storage in ${region}`,
@@ -49,9 +49,9 @@ export const deleteSubcommand = createSubcommand({
49
49
  },
50
50
 
51
51
  async handler(ctx) {
52
- const { logger, args, opts, config, orgId, region, auth, options } = ctx;
52
+ const { logger, args, opts, orgId, region, auth, options } = ctx;
53
53
 
54
- const catalystClient = getCatalystAPIClient(config, logger, auth, region);
54
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
55
55
 
56
56
  const resources = await tui.spinner({
57
57
  message: `Fetching storage for ${orgId} in ${region}`,
@@ -51,9 +51,9 @@ export const downloadSubcommand = createSubcommand({
51
51
  },
52
52
 
53
53
  async handler(ctx) {
54
- const { logger, args, opts, options, orgId, region, config, auth } = ctx;
54
+ const { logger, args, opts, options, orgId, region, auth } = ctx;
55
55
 
56
- const catalystClient = getCatalystAPIClient(config, logger, auth, region);
56
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
57
57
 
58
58
  // Fetch bucket credentials
59
59
  const resources = await tui.spinner({
@@ -51,9 +51,9 @@ export const getSubcommand = createSubcommand({
51
51
  },
52
52
 
53
53
  async handler(ctx) {
54
- const { logger, args, opts, options, orgId, region, config, auth } = ctx;
54
+ const { logger, args, opts, options, orgId, region, auth } = ctx;
55
55
 
56
- const catalystClient = getCatalystAPIClient(config, logger, auth, region);
56
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
57
57
 
58
58
  const resources = await tui.spinner({
59
59
  message: `Fetching storage bucket ${args.name}`,
@@ -74,9 +74,9 @@ export const listSubcommand = createSubcommand({
74
74
  },
75
75
 
76
76
  async handler(ctx) {
77
- const { logger, args, opts, options, orgId, region, config, auth } = ctx;
77
+ const { logger, args, opts, options, orgId, region, auth } = ctx;
78
78
 
79
- const catalystClient = getCatalystAPIClient(config, logger, auth, region);
79
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
80
80
 
81
81
  const resources = await tui.spinner({
82
82
  message: `Fetching storage for ${orgId} in ${region}`,
@@ -49,9 +49,9 @@ export const uploadSubcommand = createSubcommand({
49
49
  },
50
50
 
51
51
  async handler(ctx) {
52
- const { logger, args, opts, options, orgId, region, config, auth } = ctx;
52
+ const { logger, args, opts, options, orgId, region, auth } = ctx;
53
53
 
54
- const catalystClient = getCatalystAPIClient(config, logger, auth, region);
54
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
55
55
 
56
56
  // Fetch bucket credentials
57
57
  const resources = await tui.spinner({
@@ -24,8 +24,8 @@ export const deleteSubcommand = createSubcommand({
24
24
  }),
25
25
  },
26
26
  async handler(ctx) {
27
- const { config, logger, auth, args, region } = ctx;
28
- const catalystClient = getCatalystAPIClient(config, logger, auth, region);
27
+ const { logger, auth, args, region } = ctx;
28
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
29
29
 
30
30
  try {
31
31
  await threadDelete(catalystClient, { id: args.thread_id });
@@ -37,8 +37,8 @@ export const getSubcommand = createSubcommand({
37
37
  response: ThreadGetResponseSchema,
38
38
  },
39
39
  async handler(ctx) {
40
- const { config, logger, auth, args, options, region } = ctx;
41
- const catalystClient = getCatalystAPIClient(config, logger, auth, region);
40
+ const { logger, auth, args, options, region } = ctx;
41
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
42
42
 
43
43
  try {
44
44
  const thread = await threadGet(catalystClient, { id: args.thread_id });
@@ -62,8 +62,8 @@ export const listSubcommand = createSubcommand({
62
62
  response: ThreadListResponseSchema,
63
63
  },
64
64
  async handler(ctx) {
65
- const { config, logger, auth, project, opts, options, region } = ctx;
66
- const catalystClient = getCatalystAPIClient(config, logger, auth, region);
65
+ const { logger, auth, project, opts, options, region } = ctx;
66
+ const catalystClient = getCatalystAPIClient(logger, auth, region);
67
67
 
68
68
  const projectId = opts.projectId || project?.projectId;
69
69
  const orgId = opts.orgId;
package/src/config.ts CHANGED
@@ -727,12 +727,7 @@ export async function loadProjectSDKKey(
727
727
  logger.trace(`[SDK_KEY] AGENTUITY_SDK_KEY not found in any file`);
728
728
  }
729
729
 
730
- export function getCatalystAPIClient(
731
- config: Config | null,
732
- logger: Logger,
733
- auth: AuthData,
734
- region: string
735
- ) {
730
+ export function getCatalystAPIClient(logger: Logger, auth: AuthData, region: string) {
736
731
  const serviceUrls = getServiceUrls(region);
737
732
  const catalystUrl = serviceUrls.catalyst;
738
733
  return new ServerAPIClient(catalystUrl, logger, auth.apiKey);
@@ -745,3 +740,11 @@ export function getIONHost(config: Config | null) {
745
740
  const url = new URL(config?.overrides?.ion_url ?? 'https://ion.agentuity.cloud');
746
741
  return url.hostname;
747
742
  }
743
+
744
+ export function getStreamURL(region: string, config: Config | null) {
745
+ if (config?.name === 'local') {
746
+ return 'https://streams.agentuity.io';
747
+ }
748
+ const serviceUrls = getServiceUrls(region);
749
+ return serviceUrls.stream;
750
+ }
package/src/output.ts CHANGED
@@ -39,7 +39,13 @@ export function isQuietMode(options: GlobalOptions): boolean {
39
39
  * Check if progress indicators should be disabled
40
40
  */
41
41
  export function shouldDisableProgress(options: GlobalOptions): boolean {
42
- return options.noProgress === true || options.json === true || options.quiet === true;
42
+ return (
43
+ options.noProgress === true ||
44
+ options.json === true ||
45
+ options.quiet === true ||
46
+ options.logLevel === 'debug' ||
47
+ options.logLevel === 'trace'
48
+ );
43
49
  }
44
50
 
45
51
  /**