@liquidmetal-ai/raindrop 0.4.10 → 0.4.12

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 (52) hide show
  1. package/README.md +203 -51
  2. package/dist/base-command.d.ts +6 -0
  3. package/dist/base-command.d.ts.map +1 -1
  4. package/dist/base-command.js +16 -1
  5. package/dist/codegen.js +3 -3
  6. package/dist/codegen.test.js +6 -6
  7. package/dist/commands/bucket/create-credential.d.ts +25 -0
  8. package/dist/commands/bucket/create-credential.d.ts.map +1 -0
  9. package/dist/commands/bucket/create-credential.js +171 -0
  10. package/dist/commands/bucket/delete-credential.d.ts +24 -0
  11. package/dist/commands/bucket/delete-credential.d.ts.map +1 -0
  12. package/dist/commands/bucket/delete-credential.js +140 -0
  13. package/dist/commands/bucket/get-credential.d.ts +24 -0
  14. package/dist/commands/bucket/get-credential.d.ts.map +1 -0
  15. package/dist/commands/bucket/get-credential.js +149 -0
  16. package/dist/commands/bucket/list-credentials.d.ts +23 -0
  17. package/dist/commands/bucket/list-credentials.d.ts.map +1 -0
  18. package/dist/commands/bucket/list-credentials.js +146 -0
  19. package/dist/commands/build/branch.d.ts +1 -0
  20. package/dist/commands/build/branch.d.ts.map +1 -1
  21. package/dist/commands/build/branch.js +17 -0
  22. package/dist/commands/build/deploy.d.ts +1 -0
  23. package/dist/commands/build/deploy.d.ts.map +1 -1
  24. package/dist/commands/build/deploy.js +17 -0
  25. package/dist/commands/build/find.d.ts +2 -0
  26. package/dist/commands/build/find.d.ts.map +1 -1
  27. package/dist/commands/build/find.js +143 -16
  28. package/dist/commands/build/list.d.ts +6 -0
  29. package/dist/commands/build/list.d.ts.map +1 -1
  30. package/dist/commands/build/list.js +280 -99
  31. package/dist/commands/build/status.d.ts +0 -4
  32. package/dist/commands/build/status.d.ts.map +1 -1
  33. package/dist/commands/build/status.js +30 -80
  34. package/dist/commands/object/delete.d.ts.map +1 -1
  35. package/dist/commands/object/delete.js +10 -8
  36. package/dist/commands/object/get.d.ts.map +1 -1
  37. package/dist/commands/object/get.js +9 -8
  38. package/dist/commands/object/list.d.ts.map +1 -1
  39. package/dist/commands/object/list.js +8 -6
  40. package/dist/commands/object/put.d.ts.map +1 -1
  41. package/dist/commands/object/put.js +12 -10
  42. package/dist/index.d.ts +2 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +4 -0
  45. package/dist/status.d.ts +21 -0
  46. package/dist/status.d.ts.map +1 -0
  47. package/dist/status.js +137 -0
  48. package/dist/tsconfig.tsbuildinfo +1 -1
  49. package/oclif.manifest.json +2219 -1204
  50. package/package.json +4 -3
  51. package/templates/db/node_modules/.bin/tsc +17 -0
  52. package/templates/db/node_modules/.bin/tsserver +17 -0
@@ -0,0 +1,146 @@
1
+ import { toJsonString } from '@bufbuild/protobuf';
2
+ import { timestampDate } from '@bufbuild/protobuf/wkt';
3
+ import { valueOf } from '@liquidmetal-ai/drizzle/appify/build';
4
+ import { ListCredentialsResponseSchema } from '@liquidmetal-ai/drizzle/liquidmetal/v1alpha1/bucket_api_pb';
5
+ import { Flags } from '@oclif/core';
6
+ import { BaseCommand } from '../../base-command.js';
7
+ export default class ListCredentials extends BaseCommand {
8
+ static description = 'List S3 credentials for a bucket';
9
+ static examples = [
10
+ `<%= config.bin %> bucket list-credentials --bucket my-bucket
11
+ List all credentials for my-bucket
12
+ `,
13
+ `<%= config.bin %> bucket list-credentials --api-url https://bucket.example.com
14
+ List all credentials using a direct API URL
15
+ `,
16
+ ];
17
+ static flags = {
18
+ ...BaseCommand.HIDDEN_FLAGS,
19
+ bucket: Flags.string({
20
+ char: 'b',
21
+ description: 'bucket name',
22
+ required: false,
23
+ exclusive: ['api-url'],
24
+ }),
25
+ 'api-url': Flags.string({
26
+ description: 'direct API URL (bypasses bucket discovery)',
27
+ required: false,
28
+ exclusive: ['bucket', 'application', 'version'],
29
+ }),
30
+ application: Flags.string({
31
+ char: 'a',
32
+ description: 'application name',
33
+ required: false,
34
+ exclusive: ['api-url'],
35
+ }),
36
+ version: Flags.string({
37
+ char: 'v',
38
+ description: 'application version',
39
+ required: false,
40
+ exclusive: ['api-url'],
41
+ }),
42
+ output: Flags.string({
43
+ char: 'o',
44
+ description: 'output format',
45
+ default: 'text',
46
+ options: ['text', 'json'],
47
+ }),
48
+ impersonate: Flags.string({
49
+ char: 'i',
50
+ description: 'impersonate organization',
51
+ required: false,
52
+ hidden: true,
53
+ }),
54
+ manifest: Flags.string({
55
+ char: 'M',
56
+ description: 'project manifest',
57
+ required: false,
58
+ default: 'raindrop.manifest',
59
+ hidden: true,
60
+ }),
61
+ };
62
+ async run() {
63
+ const { flags } = await this.parse(ListCredentials);
64
+ // Validate that we have either api-url or bucket
65
+ if (!flags['api-url'] && !flags.bucket) {
66
+ this.error('Either --api-url or --bucket must be specified');
67
+ }
68
+ let apiUrl;
69
+ let bucketName;
70
+ if (flags['api-url']) {
71
+ // Direct API URL provided, skip discovery
72
+ apiUrl = flags['api-url'];
73
+ }
74
+ else {
75
+ // Need to discover the bucket
76
+ bucketName = flags.bucket;
77
+ // Get application info from flags or config/manifest
78
+ let applicationName = flags.application;
79
+ let applicationVersionId = flags.version;
80
+ if (!applicationVersionId) {
81
+ const config = await this.loadConfig();
82
+ applicationVersionId = config.versionId;
83
+ }
84
+ if (!applicationName) {
85
+ const apps = await this.loadManifest();
86
+ const app = apps[0];
87
+ if (app === undefined) {
88
+ this.error('No application provided or found in manifest', { exit: 1 });
89
+ }
90
+ applicationName = valueOf(app.name);
91
+ }
92
+ // Query for the bucket module
93
+ const { client: catalogService, userId, organizationId: defaultOrganizationId } = await this.catalogService();
94
+ const organizationId = flags.impersonate ?? defaultOrganizationId;
95
+ const modulesResp = await catalogService.queryModules({
96
+ userId,
97
+ applicationName,
98
+ applicationVersionId,
99
+ organizationId,
100
+ moduleType: 'bucket',
101
+ });
102
+ // Find the specific bucket
103
+ const bucketModule = modulesResp.modules.find(m => m.name === bucketName && m.type === 'bucket');
104
+ if (!bucketModule) {
105
+ this.error(`Bucket '${bucketName}' not found in application ${applicationName}@${applicationVersionId}`);
106
+ }
107
+ // Extract the API URL from the bucket module
108
+ apiUrl = bucketModule.bucket?.s3?.apiUrl;
109
+ if (!apiUrl) {
110
+ this.error(`Bucket '${bucketName}' does not have an API URL configured`);
111
+ }
112
+ }
113
+ // Create the S3 credential service client
114
+ const { client: credentialService } = await this.bucketApiService(apiUrl);
115
+ try {
116
+ const response = await credentialService.listCredentials({});
117
+ // Output the result
118
+ if (flags.output === 'json') {
119
+ console.log(toJsonString(ListCredentialsResponseSchema, response, { prettySpaces: 2 }));
120
+ }
121
+ else {
122
+ const credentials = response.credentials;
123
+ if (credentials.length === 0) {
124
+ console.log(`No credentials found${bucketName ? ` for bucket '${bucketName}'` : ''}`);
125
+ return;
126
+ }
127
+ console.log(`Found ${credentials.length} credential${credentials.length === 1 ? '' : 's'}${bucketName ? ` for bucket '${bucketName}'` : ''}:\n`);
128
+ for (const cred of credentials) {
129
+ console.log(`Access Key: ${cred.accessKey}`);
130
+ console.log(`Name: ${cred.name}`);
131
+ if (cred.createdAt) {
132
+ console.log(`Created: ${timestampDate(cred.createdAt).toISOString()}`);
133
+ }
134
+ if (cred.expiresAt) {
135
+ console.log(`Expires: ${timestampDate(cred.expiresAt).toISOString()}`);
136
+ }
137
+ console.log('---');
138
+ }
139
+ }
140
+ }
141
+ catch (error) {
142
+ const err = error;
143
+ this.error(`Failed to list credentials: ${err.message}`);
144
+ }
145
+ }
146
+ }
@@ -12,6 +12,7 @@ export default class Branch extends BaseCommand<typeof Branch> {
12
12
  versionId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
13
  impersonate: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
14
  start: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ 'no-watch': import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
16
  show: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
17
  config: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
17
18
  rainbowAuthService: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
@@ -1 +1 @@
1
- {"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../../src/commands/build/branch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,WAAW,CAAC,OAAO,MAAM,CAAC;IAC5D,MAAM,CAAC,IAAI;;MAET;IAEF,MAAM,CAAC,WAAW,SAAmC;IAErD,MAAM,CAAC,QAAQ,WAIb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;MA4BV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAkD3B"}
1
+ {"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../../src/commands/build/branch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,WAAW,CAAC,OAAO,MAAM,CAAC;IAC5D,MAAM,CAAC,IAAI;;MAET;IAEF,MAAM,CAAC,WAAW,SAAmC;IAErD,MAAM,CAAC,QAAQ,WAIb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;;MAiCV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA+D3B"}
@@ -1,6 +1,7 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { BaseCommand } from '../../base-command.js';
3
3
  import { deploy, sandbox } from '../../deploy.js';
4
+ import { watchStatus } from '../../status.js';
4
5
  export default class Branch extends BaseCommand {
5
6
  static args = {
6
7
  branch: Args.string({ description: 'branch name', required: true }),
@@ -38,6 +39,11 @@ Branch a Raindrop application.
38
39
  default: false,
39
40
  required: false,
40
41
  }),
42
+ 'no-watch': Flags.boolean({
43
+ description: 'skip watching deployment status after branch',
44
+ required: false,
45
+ default: false,
46
+ }),
41
47
  show: Flags.boolean({ description: 'show the current branch', required: false }),
42
48
  };
43
49
  async run() {
@@ -87,5 +93,16 @@ Branch a Raindrop application.
87
93
  throw error;
88
94
  }
89
95
  this.log(`🔔 Branch is in Sandbox mode`);
96
+ // Show status with watch output
97
+ if (!this.flags['no-watch']) {
98
+ this.log('\n📊 Watching deployment status...\n');
99
+ await watchStatus({
100
+ command: this,
101
+ root: this.flags.root,
102
+ manifest: this.flags.manifest,
103
+ output: 'watch',
104
+ impersonate: this.flags.impersonate,
105
+ });
106
+ }
90
107
  }
91
108
  }
@@ -10,6 +10,7 @@ export default class Deploy extends BaseCommand<typeof Deploy> {
10
10
  versionId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
11
  impersonate: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
12
  start: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ 'no-watch': import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
14
  resume: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
15
  lock: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
16
  amend: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/commands/build/deploy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,WAAW,CAAC,OAAO,MAAM,CAAC;IAC5D,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAAmC;IAErD,MAAM,CAAC,QAAQ,WAIb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;;;MAsCV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA0C3B"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/commands/build/deploy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,WAAW,CAAC,OAAO,MAAM,CAAC;IAC5D,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAAmC;IAErD,MAAM,CAAC,QAAQ,WAIb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;;;;MA2CV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAuD3B"}
@@ -1,6 +1,7 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { BaseCommand } from '../../base-command.js';
3
3
  import { deploy } from '../../deploy.js';
4
+ import { watchStatus } from '../../status.js';
4
5
  export default class Deploy extends BaseCommand {
5
6
  static args = {};
6
7
  static description = 'deploy a Raindrop application';
@@ -37,6 +38,11 @@ Deploy a Raindrop application version.
37
38
  required: false,
38
39
  default: false,
39
40
  }),
41
+ 'no-watch': Flags.boolean({
42
+ description: 'skip watching deployment status after deploy',
43
+ required: false,
44
+ default: false,
45
+ }),
40
46
  resume: Flags.boolean({
41
47
  description: 'resume a deployment',
42
48
  required: false,
@@ -81,5 +87,16 @@ Deploy a Raindrop application version.
81
87
  if (!amend) {
82
88
  this.log(`🔔 You deployed a full version, updates will require a full versioned deployment to work`);
83
89
  }
90
+ // Show status with watch output
91
+ if (!this.flags['no-watch']) {
92
+ this.log('\n📊 Watching deployment status...\n');
93
+ await watchStatus({
94
+ command: this,
95
+ root: this.flags.root,
96
+ manifest: this.flags.manifest,
97
+ output: 'watch',
98
+ impersonate: this.flags.impersonate,
99
+ });
100
+ }
84
101
  }
85
102
  }
@@ -29,6 +29,8 @@ export default class Find extends BaseCommand<typeof Find> {
29
29
  moduleType?: string;
30
30
  all?: boolean;
31
31
  }): Promise<void>;
32
+ private extractModuleAttributes;
33
+ private flattenObject;
32
34
  queryResources(flags: {
33
35
  output: string;
34
36
  rainbowAuthService: string;
@@ -1 +1 @@
1
- {"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/commands/build/find.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAQpD,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,WAAW,CAAC,OAAO,IAAI,CAAC;IACxD,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAAgC;IAElD,MAAM,CAAC,QAAQ,WAGb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;;;MA6CV;IAEI,YAAY,CAAC,KAAK,EAAE;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,kBAAkB,EAAE,MAAM,CAAC;QAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,GAAG,CAAC,EAAE,OAAO,CAAC;KACf;IAuDK,cAAc,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;IA+C5G,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAO3B"}
1
+ {"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/commands/build/find.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,WAAW,CAAC,OAAO,IAAI,CAAC;IACxD,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAAgC;IAElD,MAAM,CAAC,QAAQ,WAab;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;;;MA6CV;IAEI,YAAY,CAAC,KAAK,EAAE;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,kBAAkB,EAAE,MAAM,CAAC;QAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,GAAG,CAAC,EAAE,OAAO,CAAC;KACf;IAuID,OAAO,CAAC,uBAAuB;IAe/B,OAAO,CAAC,aAAa;IAsBf,cAAc,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;IA+C5G,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAO3B"}
@@ -1,15 +1,26 @@
1
+ import { toJsonString } from '@bufbuild/protobuf';
1
2
  import { timestampDate } from '@bufbuild/protobuf/wkt';
2
3
  import { valueOf } from '@liquidmetal-ai/drizzle/appify/build';
4
+ import { QueryModulesResponseSchema, QueryResourcesResponseSchema, } from '@liquidmetal-ai/drizzle/liquidmetal/v1alpha1/catalog_pb';
3
5
  import { Flags } from '@oclif/core';
6
+ import chalk from 'chalk';
4
7
  import { BaseCommand } from '../../base-command.js';
5
8
  import { EPOCH_TS } from '../../index.js';
6
- import { toJsonString } from '@bufbuild/protobuf';
7
- import { QueryModulesResponseSchema, QueryResourcesResponseSchema, } from '@liquidmetal-ai/drizzle/liquidmetal/v1alpha1/catalog_pb';
8
9
  export default class Find extends BaseCommand {
9
10
  static args = {};
10
11
  static description = 'find resources in Raindrop';
11
12
  static examples = [
12
- `<%= config.bin %> <%= command.id %> .
13
+ `<%= config.bin %> <%= command.id %>
14
+ Find all modules with full details (default).
15
+
16
+ <%= config.bin %> <%= command.id %> -o compact
17
+ Find all modules in compact view.
18
+
19
+ <%= config.bin %> <%= command.id %> --moduleType smartbucket
20
+ Find only smartbucket modules.
21
+
22
+ <%= config.bin %> <%= command.id %> -a myapp -v 1.0.0
23
+ Find modules for a specific application and version.
13
24
  `,
14
25
  ];
15
26
  static flags = {
@@ -38,8 +49,8 @@ export default class Find extends BaseCommand {
38
49
  output: Flags.string({
39
50
  char: 'o',
40
51
  description: 'output format',
41
- default: 'table',
42
- options: ['text', 'table', 'json'],
52
+ default: 'full',
53
+ options: ['text', 'full', 'json', 'compact'],
43
54
  }),
44
55
  sudo: Flags.boolean({
45
56
  char: 's',
@@ -84,26 +95,142 @@ export default class Find extends BaseCommand {
84
95
  organizationId,
85
96
  moduleType: flags.moduleType || '',
86
97
  });
87
- if (flags.output === 'table') {
88
- console.table(resp.modules.reduce((acc, v) => {
89
- acc[v.name] = {
90
- ...v,
91
- attributes: v.module.value,
92
- convergedAt: v.convergedAt ? timestampDate(v.convergedAt).toISOString() : undefined,
93
- };
98
+ if (flags.output === 'compact') {
99
+ // Group modules by application
100
+ const modulesByApp = resp.modules.reduce((acc, module) => {
101
+ const appKey = `${module.applicationName}@${module.applicationVersionId}`;
102
+ if (!acc[appKey]) {
103
+ acc[appKey] = [];
104
+ }
105
+ acc[appKey].push(module);
94
106
  return acc;
95
- },
96
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
- {}), ['moduleId', 'name', 'type', 'attributes', 'createdAt', 'updatedAt']);
107
+ }, {});
108
+ // Display grouped modules in compact format
109
+ for (const [appKey, modules] of Object.entries(modulesByApp)) {
110
+ console.log(`\n${chalk.bold(appKey)} ${chalk.dim(`(${modules.length} module${modules.length !== 1 ? 's' : ''})`)}`);
111
+ for (const module of modules) {
112
+ const convergedStatus = module.convergedAt
113
+ ? chalk.green('converged')
114
+ : chalk.yellow('pending');
115
+ const date = module.convergedAt
116
+ ? timestampDate(module.convergedAt).toLocaleDateString()
117
+ : 'N/A';
118
+ console.log(` └─ ${chalk.cyan(module.name)} ${chalk.dim(`(${module.moduleId.substring(0, 8)}...)`)} ${chalk.yellow(module.type)} - ${convergedStatus} - ${chalk.dim(date)}`);
119
+ }
120
+ }
121
+ // Add summary at the end
122
+ const totalApps = Object.keys(modulesByApp).length;
123
+ const totalModules = resp.modules.length;
124
+ const convergedModules = resp.modules.filter(m => m.convergedAt).length;
125
+ console.log('');
126
+ console.log(chalk.dim('─'.repeat(50)));
127
+ console.log(chalk.dim(`Total: ${totalApps} application${totalApps !== 1 ? 's' : ''}, ${totalModules} modules (${convergedModules} converged)`));
128
+ }
129
+ else if (flags.output === 'full') {
130
+ // Group modules by application
131
+ const modulesByApp = resp.modules.reduce((acc, module) => {
132
+ const appKey = `${module.applicationName}@${module.applicationVersionId}`;
133
+ if (!acc[appKey]) {
134
+ acc[appKey] = [];
135
+ }
136
+ acc[appKey].push(module);
137
+ return acc;
138
+ }, {});
139
+ // Display grouped modules in full format (compact style with all details)
140
+ for (const [appKey, modules] of Object.entries(modulesByApp)) {
141
+ console.log(`\n${chalk.bold(appKey)} ${chalk.dim(`(${modules.length} module${modules.length !== 1 ? 's' : ''})`)}`);
142
+ for (const module of modules) {
143
+ const convergedStatus = module.convergedAt
144
+ ? chalk.green('converged')
145
+ : chalk.yellow('pending');
146
+ const date = module.convergedAt
147
+ ? timestampDate(module.convergedAt).toISOString()
148
+ : 'N/A';
149
+ // First line with basic info
150
+ console.log(` └─ ${chalk.cyan(module.name)} ${chalk.dim(`(${module.moduleId})`)} ${chalk.yellow(module.type)}`);
151
+ console.log(` Status: ${convergedStatus} at ${chalk.dim(date)}`);
152
+ // Module-specific attributes
153
+ const attrs = this.extractModuleAttributes(module);
154
+ if (Object.keys(attrs).length > 0) {
155
+ console.log(` ${chalk.bold('Attributes:')}`);
156
+ for (const [key, value] of Object.entries(attrs)) {
157
+ console.log(` ${key}: ${chalk.yellow(String(value))}`);
158
+ }
159
+ }
160
+ }
161
+ }
162
+ // Add summary at the end
163
+ const totalApps = Object.keys(modulesByApp).length;
164
+ const totalModules = resp.modules.length;
165
+ const convergedModules = resp.modules.filter(m => m.convergedAt).length;
166
+ console.log('');
167
+ console.log(chalk.dim('─'.repeat(50)));
168
+ console.log(chalk.dim(`Total: ${totalApps} application${totalApps !== 1 ? 's' : ''}, ${totalModules} modules (${convergedModules} converged)`));
98
169
  }
99
170
  else if (flags.output === 'json') {
100
171
  console.log(toJsonString(QueryModulesResponseSchema, resp, { prettySpaces: 2 }));
101
172
  }
102
173
  else {
103
174
  for (const m of resp.modules) {
104
- console.log(`${m.moduleId} ${m.name} ${m.type} ${m.convergedAt ? timestampDate(m.convergedAt).toISOString() : undefined} ${JSON.stringify(m.module.value)}`);
175
+ // Build a detailed text output
176
+ let details = `${m.name} ${m.moduleId} ${m.type}`;
177
+ // Add module-specific details
178
+ const attrs = this.extractModuleAttributes(m);
179
+ const attrString = Object.entries(attrs)
180
+ .map(([k, v]) => `${k}=${v}`)
181
+ .join(' ');
182
+ if (attrString) {
183
+ details += ` ${attrString}`;
184
+ }
185
+ if (m.convergedAt) {
186
+ details += ` converged=${timestampDate(m.convergedAt).toISOString()}`;
187
+ }
188
+ console.log(details);
189
+ }
190
+ }
191
+ }
192
+ extractModuleAttributes(module) {
193
+ // Check each possible module field and extract attributes
194
+ if (module.smartBucket)
195
+ return this.flattenObject(module.smartBucket);
196
+ if (module.bucket)
197
+ return this.flattenObject(module.bucket);
198
+ if (module.service)
199
+ return this.flattenObject(module.service);
200
+ if (module.observer)
201
+ return this.flattenObject(module.observer);
202
+ if (module.task)
203
+ return this.flattenObject(module.task);
204
+ if (module.sqlDatabase)
205
+ return this.flattenObject(module.sqlDatabase);
206
+ if (module.vectorIndex)
207
+ return this.flattenObject(module.vectorIndex);
208
+ if (module.queue)
209
+ return this.flattenObject(module.queue);
210
+ if (module.kvStore)
211
+ return this.flattenObject(module.kvStore);
212
+ if (module.smartMemory)
213
+ return this.flattenObject(module.smartMemory);
214
+ return {};
215
+ }
216
+ flattenObject(obj, parentKey = '') {
217
+ const result = {};
218
+ for (const [key, value] of Object.entries(obj)) {
219
+ if (value === null || value === undefined)
220
+ continue;
221
+ // Skip protobuf internal fields
222
+ if (key === '$typeName' || key === '$unknown')
223
+ continue;
224
+ const newKey = parentKey ? `${parentKey}.${key}` : key;
225
+ if (typeof value === 'object' && !Array.isArray(value)) {
226
+ // Recursively flatten nested objects
227
+ Object.assign(result, this.flattenObject(value, newKey));
228
+ }
229
+ else {
230
+ result[newKey] = value;
105
231
  }
106
232
  }
233
+ return result;
107
234
  }
108
235
  async queryResources(flags) {
109
236
  if (!flags.version) {
@@ -15,6 +15,9 @@ export default class List extends BaseCommand<typeof List> {
15
15
  static flags: {
16
16
  all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
17
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
18
+ app: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ limit: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ active: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
21
  impersonate: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
22
  manifest: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
20
23
  config: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
@@ -30,6 +33,9 @@ export default class List extends BaseCommand<typeof List> {
30
33
  nodesMap: Map<string, VersionNode>;
31
34
  };
32
35
  renderGitLogStyle(applications: ApplicationsResponse_Application[]): string;
36
+ groupApplicationsByName(applications: ApplicationsResponse_Application[]): Map<string, ApplicationsResponse_Application[]>;
37
+ renderCompactView(applications: ApplicationsResponse_Application[]): string;
38
+ renderTreeView(applications: ApplicationsResponse_Application[]): string;
33
39
  listApplications(): Promise<void>;
34
40
  run(): Promise<void>;
35
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/build/list.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gCAAgC,EAEhC,SAAS,EACV,MAAM,yDAAyD,CAAC;AAGjE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAKpD,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,gCAAgC,CAAC;IAC9C,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CASzC,CAAC;AAEX,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,WAAW,CAAC,OAAO,IAAI,CAAC;IACxD,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAAqC;IAEvD,MAAM,CAAC,QAAQ,WAIb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;MAsBV;IAEF,gBAAgB,CAAC,YAAY,EAAE,gCAAgC,EAAE;;;;IAkCjE,iBAAiB,CAAC,YAAY,EAAE,gCAAgC,EAAE;IAqH5D,gBAAgB;IA0ChB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAG3B"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/build/list.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,gCAAgC,EAEhC,SAAS,EACV,MAAM,yDAAyD,CAAC;AAGjE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,gCAAgC,CAAC;IAC9C,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CASzC,CAAC;AAEX,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,WAAW,CAAC,OAAO,IAAI,CAAC;IACxD,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,WAAW,SAAqC;IAEvD,MAAM,CAAC,QAAQ,WAgBb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;;;MAmCV;IAEF,gBAAgB,CAAC,YAAY,EAAE,gCAAgC,EAAE;;;;IAkCjE,iBAAiB,CAAC,YAAY,EAAE,gCAAgC,EAAE;IAuIlE,uBAAuB,CAAC,YAAY,EAAE,gCAAgC,EAAE;IAwBxE,iBAAiB,CAAC,YAAY,EAAE,gCAAgC,EAAE;IA+DlE,cAAc,CAAC,YAAY,EAAE,gCAAgC,EAAE;IAiDzD,gBAAgB;IA8DhB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAG3B"}