@objectstack/cli 4.0.1 → 4.0.3

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 (84) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +33 -0
  3. package/dist/commands/auth/login.d.ts +13 -0
  4. package/dist/commands/auth/login.d.ts.map +1 -0
  5. package/dist/commands/auth/login.js +167 -0
  6. package/dist/commands/auth/login.js.map +1 -0
  7. package/dist/commands/auth/logout.d.ts +10 -0
  8. package/dist/commands/auth/logout.d.ts.map +1 -0
  9. package/dist/commands/auth/logout.js +46 -0
  10. package/dist/commands/auth/logout.js.map +1 -0
  11. package/dist/commands/auth/whoami.d.ts +12 -0
  12. package/dist/commands/auth/whoami.d.ts.map +1 -0
  13. package/dist/commands/auth/whoami.js +76 -0
  14. package/dist/commands/auth/whoami.js.map +1 -0
  15. package/dist/commands/data/create.d.ts +17 -0
  16. package/dist/commands/data/create.d.ts.map +1 -0
  17. package/dist/commands/data/create.js +106 -0
  18. package/dist/commands/data/create.js.map +1 -0
  19. package/dist/commands/data/delete.d.ts +16 -0
  20. package/dist/commands/data/delete.d.ts.map +1 -0
  21. package/dist/commands/data/delete.js +78 -0
  22. package/dist/commands/data/delete.js.map +1 -0
  23. package/dist/commands/data/get.d.ts +16 -0
  24. package/dist/commands/data/get.d.ts.map +1 -0
  25. package/dist/commands/data/get.js +79 -0
  26. package/dist/commands/data/get.js.map +1 -0
  27. package/dist/commands/data/query.d.ts +20 -0
  28. package/dist/commands/data/query.d.ts.map +1 -0
  29. package/dist/commands/data/query.js +118 -0
  30. package/dist/commands/data/query.js.map +1 -0
  31. package/dist/commands/data/update.d.ts +18 -0
  32. package/dist/commands/data/update.d.ts.map +1 -0
  33. package/dist/commands/data/update.js +110 -0
  34. package/dist/commands/data/update.js.map +1 -0
  35. package/dist/commands/meta/delete.d.ts +16 -0
  36. package/dist/commands/meta/delete.d.ts.map +1 -0
  37. package/dist/commands/meta/delete.js +73 -0
  38. package/dist/commands/meta/delete.js.map +1 -0
  39. package/dist/commands/meta/get.d.ts +16 -0
  40. package/dist/commands/meta/get.d.ts.map +1 -0
  41. package/dist/commands/meta/get.js +65 -0
  42. package/dist/commands/meta/get.js.map +1 -0
  43. package/dist/commands/meta/list.d.ts +15 -0
  44. package/dist/commands/meta/list.d.ts.map +1 -0
  45. package/dist/commands/meta/list.js +103 -0
  46. package/dist/commands/meta/list.js.map +1 -0
  47. package/dist/commands/meta/register.d.ts +16 -0
  48. package/dist/commands/meta/register.d.ts.map +1 -0
  49. package/dist/commands/meta/register.js +89 -0
  50. package/dist/commands/meta/register.js.map +1 -0
  51. package/dist/commands/serve.d.ts.map +1 -1
  52. package/dist/commands/serve.js +33 -0
  53. package/dist/commands/serve.js.map +1 -1
  54. package/dist/utils/api-client.d.ts +42 -0
  55. package/dist/utils/api-client.d.ts.map +1 -0
  56. package/dist/utils/api-client.js +53 -0
  57. package/dist/utils/api-client.js.map +1 -0
  58. package/dist/utils/auth-config.d.ts +50 -0
  59. package/dist/utils/auth-config.d.ts.map +1 -0
  60. package/dist/utils/auth-config.js +73 -0
  61. package/dist/utils/auth-config.js.map +1 -0
  62. package/dist/utils/output-formatter.d.ts +9 -0
  63. package/dist/utils/output-formatter.d.ts.map +1 -0
  64. package/dist/utils/output-formatter.js +80 -0
  65. package/dist/utils/output-formatter.js.map +1 -0
  66. package/package.json +19 -14
  67. package/src/commands/auth/login.ts +188 -0
  68. package/src/commands/auth/logout.ts +51 -0
  69. package/src/commands/auth/whoami.ts +85 -0
  70. package/src/commands/data/create.ts +110 -0
  71. package/src/commands/data/delete.ts +84 -0
  72. package/src/commands/data/get.ts +84 -0
  73. package/src/commands/data/query.ts +127 -0
  74. package/src/commands/data/update.ts +114 -0
  75. package/src/commands/meta/delete.ts +79 -0
  76. package/src/commands/meta/get.ts +73 -0
  77. package/src/commands/meta/list.ts +105 -0
  78. package/src/commands/meta/register.ts +97 -0
  79. package/src/commands/serve.ts +38 -0
  80. package/src/utils/api-client.ts +88 -0
  81. package/src/utils/auth-config.ts +107 -0
  82. package/src/utils/output-formatter.ts +91 -0
  83. package/test/remote-api-commands.test.ts +188 -0
  84. package/test/remote-api-utils.test.ts +196 -0
@@ -0,0 +1,84 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
3
+ import { Args, Command, Flags } from '@oclif/core';
4
+ import { printError } from '../../utils/format.js';
5
+ import { createApiClient, requireAuth } from '../../utils/api-client.js';
6
+ import { formatOutput } from '../../utils/output-formatter.js';
7
+
8
+ export default class DataGet extends Command {
9
+ static override description = 'Get a single record by ID';
10
+
11
+ static override examples = [
12
+ '$ os data get project_task abc123',
13
+ '$ os data get project_task abc123 --format json',
14
+ ];
15
+
16
+ static override args = {
17
+ object: Args.string({
18
+ description: 'Object name (snake_case)',
19
+ required: true,
20
+ }),
21
+ id: Args.string({
22
+ description: 'Record ID',
23
+ required: true,
24
+ }),
25
+ };
26
+
27
+ static override flags = {
28
+ url: Flags.string({
29
+ char: 'u',
30
+ description: 'Server URL',
31
+ env: 'OBJECTSTACK_URL',
32
+ }),
33
+ token: Flags.string({
34
+ char: 't',
35
+ description: 'Authentication token',
36
+ env: 'OBJECTSTACK_TOKEN',
37
+ }),
38
+ format: Flags.string({
39
+ char: 'f',
40
+ description: 'Output format',
41
+ options: ['json', 'table', 'yaml'],
42
+ default: 'table',
43
+ }),
44
+ };
45
+
46
+ async run(): Promise<void> {
47
+ const { args, flags } = await this.parse(DataGet);
48
+
49
+ try {
50
+ const { client, token } = await createApiClient({
51
+ url: flags.url,
52
+ token: flags.token,
53
+ });
54
+
55
+ requireAuth(token);
56
+
57
+ // Get the record
58
+ const result = await client.data.get(args.object, args.id);
59
+
60
+ if (flags.format === 'json') {
61
+ formatOutput(result, 'json');
62
+ } else if (flags.format === 'yaml') {
63
+ formatOutput(result, 'yaml');
64
+ } else {
65
+ // Table format - show the record
66
+ if (result.record) {
67
+ formatOutput(result.record, 'table');
68
+ } else {
69
+ console.log('Record not found.');
70
+ }
71
+ }
72
+ } catch (error: any) {
73
+ if (flags.format === 'json') {
74
+ console.log(JSON.stringify({
75
+ success: false,
76
+ error: error.message,
77
+ }, null, 2));
78
+ this.exit(1);
79
+ }
80
+ printError(error.message || String(error));
81
+ this.exit(1);
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,127 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
3
+ import { Args, Command, Flags } from '@oclif/core';
4
+ import { printError } from '../../utils/format.js';
5
+ import { createApiClient, requireAuth } from '../../utils/api-client.js';
6
+ import { formatOutput } from '../../utils/output-formatter.js';
7
+
8
+ export default class DataQuery extends Command {
9
+ static override description = 'Query records from an object';
10
+
11
+ static override examples = [
12
+ '$ os data query project_task',
13
+ '$ os data query project_task --filter \'{"status":"open"}\'',
14
+ '$ os data query project_task --limit 10 --offset 0',
15
+ '$ os data query project_task --fields name,status,created_at',
16
+ '$ os data query project_task --sort -created_at',
17
+ '$ os data query project_task --format json',
18
+ ];
19
+
20
+ static override args = {
21
+ object: Args.string({
22
+ description: 'Object name (snake_case)',
23
+ required: true,
24
+ }),
25
+ };
26
+
27
+ static override flags = {
28
+ url: Flags.string({
29
+ char: 'u',
30
+ description: 'Server URL',
31
+ env: 'OBJECTSTACK_URL',
32
+ }),
33
+ token: Flags.string({
34
+ char: 't',
35
+ description: 'Authentication token',
36
+ env: 'OBJECTSTACK_TOKEN',
37
+ }),
38
+ filter: Flags.string({
39
+ description: 'Filter criteria as JSON object',
40
+ }),
41
+ fields: Flags.string({
42
+ description: 'Comma-separated list of fields to retrieve',
43
+ }),
44
+ sort: Flags.string({
45
+ description: 'Sort field (prefix with - for descending)',
46
+ }),
47
+ limit: Flags.integer({
48
+ description: 'Maximum number of records to return',
49
+ default: 50,
50
+ }),
51
+ offset: Flags.integer({
52
+ description: 'Number of records to skip',
53
+ default: 0,
54
+ }),
55
+ format: Flags.string({
56
+ char: 'f',
57
+ description: 'Output format',
58
+ options: ['json', 'table', 'yaml'],
59
+ default: 'table',
60
+ }),
61
+ };
62
+
63
+ async run(): Promise<void> {
64
+ const { args, flags } = await this.parse(DataQuery);
65
+
66
+ try {
67
+ const { client, token } = await createApiClient({
68
+ url: flags.url,
69
+ token: flags.token,
70
+ });
71
+
72
+ requireAuth(token);
73
+
74
+ // Build query options
75
+ const queryOptions: any = {
76
+ limit: flags.limit,
77
+ offset: flags.offset,
78
+ };
79
+
80
+ if (flags.filter) {
81
+ try {
82
+ queryOptions.where = JSON.parse(flags.filter);
83
+ } catch (e) {
84
+ throw new Error(`Invalid filter JSON: ${(e as Error).message}`);
85
+ }
86
+ }
87
+
88
+ if (flags.fields) {
89
+ queryOptions.fields = flags.fields.split(',').map(f => f.trim());
90
+ }
91
+
92
+ if (flags.sort) {
93
+ queryOptions.orderBy = flags.sort;
94
+ }
95
+
96
+ // Execute query
97
+ const result = await client.data.query(args.object, queryOptions);
98
+
99
+ if (flags.format === 'json') {
100
+ formatOutput(result, 'json');
101
+ } else if (flags.format === 'yaml') {
102
+ formatOutput(result, 'yaml');
103
+ } else {
104
+ // Table format
105
+ if (result.records && result.records.length > 0) {
106
+ formatOutput(result.records, 'table');
107
+ } else {
108
+ console.log('No records found.');
109
+ }
110
+
111
+ if (result.total !== undefined) {
112
+ console.log(`\nTotal: ${result.total} record(s)`);
113
+ }
114
+ }
115
+ } catch (error: any) {
116
+ if (flags.format === 'json') {
117
+ console.log(JSON.stringify({
118
+ success: false,
119
+ error: error.message,
120
+ }, null, 2));
121
+ this.exit(1);
122
+ }
123
+ printError(error.message || String(error));
124
+ this.exit(1);
125
+ }
126
+ }
127
+ }
@@ -0,0 +1,114 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
3
+ import { Args, Command, Flags } from '@oclif/core';
4
+ import { printError, printSuccess } from '../../utils/format.js';
5
+ import { createApiClient, requireAuth } from '../../utils/api-client.js';
6
+ import { formatOutput } from '../../utils/output-formatter.js';
7
+
8
+ export default class DataUpdate extends Command {
9
+ static override description = 'Update an existing record';
10
+
11
+ static override examples = [
12
+ '$ os data update project_task abc123 \'{"status":"completed"}\'',
13
+ '$ os data update project_task abc123 --data update-data.json',
14
+ '$ os data update project_task abc123 --data update-data.json --format json',
15
+ ];
16
+
17
+ static override args = {
18
+ object: Args.string({
19
+ description: 'Object name (snake_case)',
20
+ required: true,
21
+ }),
22
+ id: Args.string({
23
+ description: 'Record ID',
24
+ required: true,
25
+ }),
26
+ data: Args.string({
27
+ description: 'Update data as JSON string (or use --data flag for file)',
28
+ }),
29
+ };
30
+
31
+ static override flags = {
32
+ url: Flags.string({
33
+ char: 'u',
34
+ description: 'Server URL',
35
+ env: 'OBJECTSTACK_URL',
36
+ }),
37
+ token: Flags.string({
38
+ char: 't',
39
+ description: 'Authentication token',
40
+ env: 'OBJECTSTACK_TOKEN',
41
+ }),
42
+ data: Flags.string({
43
+ char: 'd',
44
+ description: 'Path to JSON file containing update data',
45
+ }),
46
+ format: Flags.string({
47
+ char: 'f',
48
+ description: 'Output format',
49
+ options: ['json', 'table', 'yaml'],
50
+ default: 'table',
51
+ }),
52
+ };
53
+
54
+ async run(): Promise<void> {
55
+ const { args, flags } = await this.parse(DataUpdate);
56
+
57
+ try {
58
+ const { client, token } = await createApiClient({
59
+ url: flags.url,
60
+ token: flags.token,
61
+ });
62
+
63
+ requireAuth(token);
64
+
65
+ // Parse update data
66
+ let updateData: any;
67
+
68
+ if (flags.data) {
69
+ // Read from file
70
+ const { readFile } = await import('node:fs/promises');
71
+ const fileContent = await readFile(flags.data, 'utf-8');
72
+ try {
73
+ updateData = JSON.parse(fileContent);
74
+ } catch (e) {
75
+ throw new Error(`Invalid JSON in file: ${(e as Error).message}`);
76
+ }
77
+ } else if (args.data) {
78
+ // Parse from argument
79
+ try {
80
+ updateData = JSON.parse(args.data);
81
+ } catch (e) {
82
+ throw new Error(`Invalid JSON: ${(e as Error).message}`);
83
+ }
84
+ } else {
85
+ throw new Error('Update data is required (provide JSON string or use --data flag)');
86
+ }
87
+
88
+ // Update the record
89
+ const result = await client.data.update(args.object, args.id, updateData);
90
+
91
+ if (flags.format === 'json') {
92
+ formatOutput(result, 'json');
93
+ } else if (flags.format === 'yaml') {
94
+ formatOutput(result, 'yaml');
95
+ } else {
96
+ printSuccess(`Record updated: ${result.id}`);
97
+ if (result.record) {
98
+ console.log('');
99
+ formatOutput(result.record, 'table');
100
+ }
101
+ }
102
+ } catch (error: any) {
103
+ if (flags.format === 'json') {
104
+ console.log(JSON.stringify({
105
+ success: false,
106
+ error: error.message,
107
+ }, null, 2));
108
+ this.exit(1);
109
+ }
110
+ printError(error.message || String(error));
111
+ this.exit(1);
112
+ }
113
+ }
114
+ }
@@ -0,0 +1,79 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
3
+ import { Args, Command, Flags } from '@oclif/core';
4
+ import { printError, printSuccess } from '../../utils/format.js';
5
+ import { createApiClient, requireAuth } from '../../utils/api-client.js';
6
+ import { formatOutput } from '../../utils/output-formatter.js';
7
+
8
+ export default class MetaDelete extends Command {
9
+ static override description = 'Delete a metadata item';
10
+
11
+ static override examples = [
12
+ '$ os meta delete object my_custom_object',
13
+ '$ os meta delete plugin my-plugin',
14
+ '$ os meta delete object my_custom_object --format json',
15
+ ];
16
+
17
+ static override args = {
18
+ type: Args.string({
19
+ description: 'Metadata type',
20
+ required: true,
21
+ }),
22
+ name: Args.string({
23
+ description: 'Item name (snake_case)',
24
+ required: true,
25
+ }),
26
+ };
27
+
28
+ static override flags = {
29
+ url: Flags.string({
30
+ char: 'u',
31
+ description: 'Server URL',
32
+ env: 'OBJECTSTACK_URL',
33
+ }),
34
+ token: Flags.string({
35
+ char: 't',
36
+ description: 'Authentication token',
37
+ env: 'OBJECTSTACK_TOKEN',
38
+ }),
39
+ format: Flags.string({
40
+ char: 'f',
41
+ description: 'Output format',
42
+ options: ['json', 'table', 'yaml'],
43
+ default: 'table',
44
+ }),
45
+ };
46
+
47
+ async run(): Promise<void> {
48
+ const { args, flags } = await this.parse(MetaDelete);
49
+
50
+ try {
51
+ const { client, token } = await createApiClient({
52
+ url: flags.url,
53
+ token: flags.token,
54
+ });
55
+
56
+ requireAuth(token);
57
+
58
+ const result = await client.meta.deleteItem(args.type, args.name);
59
+
60
+ if (flags.format === 'json') {
61
+ formatOutput({ success: true, type: args.type, name: args.name, deleted: result.deleted }, 'json');
62
+ } else if (flags.format === 'yaml') {
63
+ formatOutput({ success: true, type: args.type, name: args.name, deleted: result.deleted }, 'yaml');
64
+ } else {
65
+ printSuccess(`Metadata deleted: ${args.type}/${args.name}`);
66
+ }
67
+ } catch (error: any) {
68
+ if (flags.format === 'json') {
69
+ console.log(JSON.stringify({
70
+ success: false,
71
+ error: error.message,
72
+ }, null, 2));
73
+ this.exit(1);
74
+ }
75
+ printError(error.message || String(error));
76
+ this.exit(1);
77
+ }
78
+ }
79
+ }
@@ -0,0 +1,73 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
3
+ import { Args, Command, Flags } from '@oclif/core';
4
+ import { printError } from '../../utils/format.js';
5
+ import { createApiClient, requireAuth } from '../../utils/api-client.js';
6
+ import { formatOutput } from '../../utils/output-formatter.js';
7
+
8
+ export default class MetaGet extends Command {
9
+ static override description = 'Get a metadata item';
10
+
11
+ static override examples = [
12
+ '$ os meta get object project_task',
13
+ '$ os meta get plugin my-plugin --format json',
14
+ ];
15
+
16
+ static override args = {
17
+ type: Args.string({
18
+ description: 'Metadata type',
19
+ required: true,
20
+ }),
21
+ name: Args.string({
22
+ description: 'Item name (snake_case)',
23
+ required: true,
24
+ }),
25
+ };
26
+
27
+ static override flags = {
28
+ url: Flags.string({
29
+ char: 'u',
30
+ description: 'Server URL',
31
+ env: 'OBJECTSTACK_URL',
32
+ }),
33
+ token: Flags.string({
34
+ char: 't',
35
+ description: 'Authentication token',
36
+ env: 'OBJECTSTACK_TOKEN',
37
+ }),
38
+ format: Flags.string({
39
+ char: 'f',
40
+ description: 'Output format',
41
+ options: ['json', 'table', 'yaml'],
42
+ default: 'json',
43
+ }),
44
+ };
45
+
46
+ async run(): Promise<void> {
47
+ const { args, flags } = await this.parse(MetaGet);
48
+
49
+ try {
50
+ const { client, token } = await createApiClient({
51
+ url: flags.url,
52
+ token: flags.token,
53
+ });
54
+
55
+ requireAuth(token);
56
+
57
+ // Get the metadata item
58
+ const item = await client.meta.getItem(args.type, args.name);
59
+
60
+ formatOutput(item, flags.format as any);
61
+ } catch (error: any) {
62
+ if (flags.format === 'json') {
63
+ console.log(JSON.stringify({
64
+ success: false,
65
+ error: error.message,
66
+ }, null, 2));
67
+ this.exit(1);
68
+ }
69
+ printError(error.message || String(error));
70
+ this.exit(1);
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,105 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
3
+ import { Args, Command, Flags } from '@oclif/core';
4
+ import { printError } from '../../utils/format.js';
5
+ import { createApiClient, requireAuth } from '../../utils/api-client.js';
6
+ import { formatOutput } from '../../utils/output-formatter.js';
7
+
8
+ export default class MetaList extends Command {
9
+ static override description = 'List metadata types or items';
10
+
11
+ static override examples = [
12
+ '$ os meta list',
13
+ '$ os meta list object',
14
+ '$ os meta list plugin --format json',
15
+ ];
16
+
17
+ static override args = {
18
+ type: Args.string({
19
+ description: 'Metadata type (object, plugin, view, etc.)',
20
+ }),
21
+ };
22
+
23
+ static override flags = {
24
+ url: Flags.string({
25
+ char: 'u',
26
+ description: 'Server URL',
27
+ env: 'OBJECTSTACK_URL',
28
+ }),
29
+ token: Flags.string({
30
+ char: 't',
31
+ description: 'Authentication token',
32
+ env: 'OBJECTSTACK_TOKEN',
33
+ }),
34
+ format: Flags.string({
35
+ char: 'f',
36
+ description: 'Output format',
37
+ options: ['json', 'table', 'yaml'],
38
+ default: 'table',
39
+ }),
40
+ };
41
+
42
+ async run(): Promise<void> {
43
+ const { args, flags } = await this.parse(MetaList);
44
+
45
+ try {
46
+ const { client, token } = await createApiClient({
47
+ url: flags.url,
48
+ token: flags.token,
49
+ });
50
+
51
+ requireAuth(token);
52
+
53
+ if (!args.type) {
54
+ // List all metadata types
55
+ const types = await client.meta.getTypes();
56
+
57
+ if (flags.format === 'json') {
58
+ formatOutput(types, 'json');
59
+ } else if (flags.format === 'yaml') {
60
+ formatOutput(types, 'yaml');
61
+ } else {
62
+ console.log('\nAvailable metadata types:\n');
63
+ if (Array.isArray(types)) {
64
+ types.forEach(type => console.log(` • ${type}`));
65
+ } else {
66
+ console.log('No types available');
67
+ }
68
+ console.log('');
69
+ }
70
+ } else {
71
+ // List items of a specific type
72
+ const items = await client.meta.getItems(args.type);
73
+
74
+ if (flags.format === 'json') {
75
+ formatOutput(items, 'json');
76
+ } else if (flags.format === 'yaml') {
77
+ formatOutput(items, 'yaml');
78
+ } else {
79
+ console.log(`\n${args.type} items:\n`);
80
+ if (Array.isArray(items)) {
81
+ if (items.length === 0) {
82
+ console.log(' (no items)');
83
+ } else {
84
+ items.forEach(item => {
85
+ const name = item.name || item.id || JSON.stringify(item);
86
+ console.log(` • ${name}`);
87
+ });
88
+ }
89
+ }
90
+ console.log('');
91
+ }
92
+ }
93
+ } catch (error: any) {
94
+ if (flags.format === 'json') {
95
+ console.log(JSON.stringify({
96
+ success: false,
97
+ error: error.message,
98
+ }, null, 2));
99
+ this.exit(1);
100
+ }
101
+ printError(error.message || String(error));
102
+ this.exit(1);
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,97 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
3
+ import { Args, Command, Flags } from '@oclif/core';
4
+ import { printError, printSuccess } from '../../utils/format.js';
5
+ import { createApiClient, requireAuth } from '../../utils/api-client.js';
6
+ import { formatOutput } from '../../utils/output-formatter.js';
7
+
8
+ export default class MetaRegister extends Command {
9
+ static override description = 'Register metadata (create or update)';
10
+
11
+ static override examples = [
12
+ '$ os meta register object --data object-def.json',
13
+ '$ os meta register plugin --data plugin-manifest.json',
14
+ ];
15
+
16
+ static override args = {
17
+ type: Args.string({
18
+ description: 'Metadata type',
19
+ required: true,
20
+ }),
21
+ };
22
+
23
+ static override flags = {
24
+ url: Flags.string({
25
+ char: 'u',
26
+ description: 'Server URL',
27
+ env: 'OBJECTSTACK_URL',
28
+ }),
29
+ token: Flags.string({
30
+ char: 't',
31
+ description: 'Authentication token',
32
+ env: 'OBJECTSTACK_TOKEN',
33
+ }),
34
+ data: Flags.string({
35
+ char: 'd',
36
+ description: 'Path to JSON file containing metadata definition',
37
+ required: true,
38
+ }),
39
+ format: Flags.string({
40
+ char: 'f',
41
+ description: 'Output format',
42
+ options: ['json', 'table', 'yaml'],
43
+ default: 'table',
44
+ }),
45
+ };
46
+
47
+ async run(): Promise<void> {
48
+ const { args, flags } = await this.parse(MetaRegister);
49
+
50
+ try {
51
+ const { client, token } = await createApiClient({
52
+ url: flags.url,
53
+ token: flags.token,
54
+ });
55
+
56
+ requireAuth(token);
57
+
58
+ // Read metadata from file
59
+ const { readFile } = await import('node:fs/promises');
60
+ const fileContent = await readFile(flags.data, 'utf-8');
61
+ let metadata: any;
62
+
63
+ try {
64
+ metadata = JSON.parse(fileContent);
65
+ } catch (e) {
66
+ throw new Error(`Invalid JSON in file: ${(e as Error).message}`);
67
+ }
68
+
69
+ // Extract name from metadata
70
+ const name = metadata.name;
71
+ if (!name) {
72
+ throw new Error('Metadata definition must include a "name" field');
73
+ }
74
+
75
+ // Register the metadata
76
+ const result = await client.meta.saveItem(args.type, name, metadata);
77
+
78
+ if (flags.format === 'json') {
79
+ formatOutput(result, 'json');
80
+ } else if (flags.format === 'yaml') {
81
+ formatOutput(result, 'yaml');
82
+ } else {
83
+ printSuccess(`Metadata registered: ${args.type}/${name}`);
84
+ }
85
+ } catch (error: any) {
86
+ if (flags.format === 'json') {
87
+ console.log(JSON.stringify({
88
+ success: false,
89
+ error: error.message,
90
+ }, null, 2));
91
+ this.exit(1);
92
+ }
93
+ printError(error.message || String(error));
94
+ this.exit(1);
95
+ }
96
+ }
97
+ }