@aderis-erp/cli 1.0.14 → 1.0.17

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 (167) hide show
  1. package/README.md +30 -8
  2. package/dist/commands/access-groups.d.ts +0 -1
  3. package/dist/commands/access-groups.js +22 -78
  4. package/dist/commands/account-transaction-allocations.d.ts +0 -1
  5. package/dist/commands/account-transaction-allocations.js +22 -78
  6. package/dist/commands/account-transactions.d.ts +0 -1
  7. package/dist/commands/account-transactions.js +28 -82
  8. package/dist/commands/accounts.d.ts +0 -1
  9. package/dist/commands/accounts.js +24 -80
  10. package/dist/commands/agent-platform.d.ts +0 -1
  11. package/dist/commands/agent-platform.js +24 -79
  12. package/dist/commands/analytics.d.ts +39 -0
  13. package/dist/commands/analytics.js +1113 -0
  14. package/dist/commands/{documents.d.ts → api-keys.d.ts} +1 -2
  15. package/dist/commands/api-keys.js +1117 -0
  16. package/dist/commands/audit-log.d.ts +0 -1
  17. package/dist/commands/audit-log.js +22 -78
  18. package/dist/commands/auth/login.d.ts +9 -0
  19. package/dist/commands/auth/login.js +83 -21
  20. package/dist/commands/auth/logout.d.ts +6 -0
  21. package/dist/commands/auth/logout.js +23 -6
  22. package/dist/commands/auth/status.js +46 -23
  23. package/dist/commands/auth/tenant.js +15 -9
  24. package/dist/commands/auth.d.ts +0 -1
  25. package/dist/commands/auth.js +37 -51
  26. package/dist/commands/bank-accounts.d.ts +0 -1
  27. package/dist/commands/bank-accounts.js +22 -78
  28. package/dist/commands/bank-integrations.d.ts +0 -1
  29. package/dist/commands/bank-integrations.js +22 -78
  30. package/dist/commands/business-partners.d.ts +1 -1
  31. package/dist/commands/business-partners.js +38 -84
  32. package/dist/commands/chat.d.ts +0 -1
  33. package/dist/commands/chat.js +22 -78
  34. package/dist/commands/config/add.d.ts +12 -0
  35. package/dist/commands/config/add.js +97 -17
  36. package/dist/commands/config/current.js +12 -4
  37. package/dist/commands/config/list.js +12 -4
  38. package/dist/commands/config/set-api-key.d.ts +10 -0
  39. package/dist/commands/config/set-api-key.js +49 -0
  40. package/dist/commands/config/use.js +3 -4
  41. package/dist/commands/credit-card-accounts.d.ts +0 -1
  42. package/dist/commands/credit-card-accounts.js +22 -78
  43. package/dist/commands/delivery-notes.d.ts +0 -1
  44. package/dist/commands/delivery-notes.js +29 -84
  45. package/dist/commands/dunning-configs.d.ts +0 -1
  46. package/dist/commands/dunning-configs.js +22 -78
  47. package/dist/commands/dunning-reminders.d.ts +0 -1
  48. package/dist/commands/dunning-reminders.js +22 -78
  49. package/dist/commands/dunning-runs.d.ts +0 -1
  50. package/dist/commands/dunning-runs.js +22 -78
  51. package/dist/commands/email-transactions.d.ts +0 -1
  52. package/dist/commands/email-transactions.js +22 -78
  53. package/dist/commands/employees.d.ts +0 -1
  54. package/dist/commands/employees.js +24 -80
  55. package/dist/commands/entity-configurations.d.ts +0 -1
  56. package/dist/commands/entity-configurations.js +22 -78
  57. package/dist/commands/event-processor-mappings.d.ts +0 -1
  58. package/dist/commands/event-processor-mappings.js +22 -78
  59. package/dist/commands/{files-internal.d.ts → files.d.ts} +1 -2
  60. package/dist/commands/{files-internal.js → files.js} +40 -90
  61. package/dist/commands/health.d.ts +0 -1
  62. package/dist/commands/health.js +22 -78
  63. package/dist/commands/inbox.d.ts +0 -1
  64. package/dist/commands/inbox.js +22 -78
  65. package/dist/commands/integrations.d.ts +0 -1
  66. package/dist/commands/integrations.js +22 -78
  67. package/dist/commands/item-categories.d.ts +0 -1
  68. package/dist/commands/item-categories.js +22 -78
  69. package/dist/commands/items.d.ts +0 -1
  70. package/dist/commands/items.js +24 -80
  71. package/dist/commands/journal-lines.d.ts +0 -1
  72. package/dist/commands/journal-lines.js +22 -78
  73. package/dist/commands/journals.d.ts +1 -1
  74. package/dist/commands/journals.js +29 -81
  75. package/dist/commands/ledgers.d.ts +0 -1
  76. package/dist/commands/ledgers.js +26 -79
  77. package/dist/commands/nango-integrations.d.ts +39 -0
  78. package/dist/commands/nango-integrations.js +1115 -0
  79. package/dist/commands/office-locations.d.ts +0 -1
  80. package/dist/commands/office-locations.js +22 -78
  81. package/dist/commands/payment-methods.d.ts +0 -1
  82. package/dist/commands/payment-methods.js +22 -78
  83. package/dist/commands/payment-runs.d.ts +0 -1
  84. package/dist/commands/payment-runs.js +24 -79
  85. package/dist/commands/payment-terms.d.ts +0 -1
  86. package/dist/commands/payment-terms.js +22 -78
  87. package/dist/commands/payments.d.ts +0 -1
  88. package/dist/commands/payments.js +22 -78
  89. package/dist/commands/price-lists.d.ts +0 -1
  90. package/dist/commands/price-lists.js +22 -78
  91. package/dist/commands/processor-mappings.d.ts +0 -1
  92. package/dist/commands/processor-mappings.js +22 -78
  93. package/dist/commands/processors.d.ts +0 -1
  94. package/dist/commands/processors.js +22 -78
  95. package/dist/commands/purchase-order-types.d.ts +0 -1
  96. package/dist/commands/purchase-order-types.js +22 -78
  97. package/dist/commands/purchase-orders.d.ts +1 -1
  98. package/dist/commands/purchase-orders.js +35 -84
  99. package/dist/commands/reports.d.ts +0 -1
  100. package/dist/commands/reports.js +23 -79
  101. package/dist/commands/salary-types.d.ts +0 -1
  102. package/dist/commands/salary-types.js +22 -78
  103. package/dist/commands/sales-invoices.d.ts +0 -1
  104. package/dist/commands/sales-invoices.js +38 -89
  105. package/dist/commands/sales-orders.d.ts +0 -1
  106. package/dist/commands/sales-orders.js +26 -82
  107. package/dist/commands/saved-views.d.ts +0 -1
  108. package/dist/commands/saved-views.js +22 -78
  109. package/dist/commands/scheduled-processor-mappings.d.ts +0 -1
  110. package/dist/commands/scheduled-processor-mappings.js +22 -78
  111. package/dist/commands/schedules.d.ts +0 -1
  112. package/dist/commands/schedules.js +22 -78
  113. package/dist/commands/switch.js +14 -7
  114. package/dist/commands/tag-configurations.d.ts +39 -0
  115. package/dist/commands/{documents.js → tag-configurations.js} +40 -96
  116. package/dist/commands/tags.d.ts +0 -1
  117. package/dist/commands/tags.js +22 -78
  118. package/dist/commands/tax-codes.d.ts +0 -1
  119. package/dist/commands/tax-codes.js +22 -78
  120. package/dist/commands/tax-matrix.d.ts +0 -1
  121. package/dist/commands/tax-matrix.js +22 -78
  122. package/dist/commands/tenants.d.ts +0 -1
  123. package/dist/commands/tenants.js +24 -79
  124. package/dist/commands/text-templates.d.ts +0 -1
  125. package/dist/commands/text-templates.js +22 -78
  126. package/dist/commands/tools.d.ts +0 -1
  127. package/dist/commands/tools.js +22 -78
  128. package/dist/commands/vat-declarations.d.ts +1 -1
  129. package/dist/commands/vat-declarations.js +28 -79
  130. package/dist/commands/vendor-invoices.d.ts +0 -1
  131. package/dist/commands/vendor-invoices.js +31 -86
  132. package/dist/commands/webhooks.d.ts +0 -1
  133. package/dist/commands/webhooks.js +22 -78
  134. package/dist/commands/workflows.d.ts +0 -1
  135. package/dist/commands/workflows.js +22 -78
  136. package/dist/lib/api-client.js +8 -6
  137. package/dist/lib/auth/constants.d.ts +11 -0
  138. package/dist/lib/auth/constants.js +11 -0
  139. package/dist/lib/auth/errors.d.ts +17 -0
  140. package/dist/lib/auth/errors.js +22 -0
  141. package/dist/lib/auth/flow.d.ts +27 -0
  142. package/dist/lib/auth/flow.js +163 -0
  143. package/dist/lib/auth/guards.d.ts +5 -0
  144. package/dist/lib/auth/guards.js +14 -0
  145. package/dist/lib/auth/http.d.ts +16 -0
  146. package/dist/lib/auth/http.js +49 -0
  147. package/dist/lib/auth/index.d.ts +49 -0
  148. package/dist/lib/auth/index.js +217 -0
  149. package/dist/lib/auth/keycloak-provider.d.ts +75 -0
  150. package/dist/lib/auth/keycloak-provider.js +215 -0
  151. package/dist/lib/auth/pages.d.ts +25 -0
  152. package/dist/lib/auth/pages.js +123 -0
  153. package/dist/lib/auth/pkce.d.ts +11 -0
  154. package/dist/lib/auth/pkce.js +17 -0
  155. package/dist/lib/auth/tokens.d.ts +54 -0
  156. package/dist/lib/auth/tokens.js +104 -0
  157. package/dist/lib/auth/types.d.ts +57 -0
  158. package/dist/lib/auth/types.js +1 -0
  159. package/dist/lib/auth/validated-url.d.ts +6 -0
  160. package/dist/lib/auth/validated-url.js +53 -0
  161. package/dist/lib/base-api-command.js +6 -4
  162. package/dist/lib/config.d.ts +44 -4
  163. package/dist/lib/config.js +55 -19
  164. package/oclif.manifest.json +1181 -330
  165. package/package.json +3 -2
  166. package/dist/lib/auth.d.ts +0 -35
  167. package/dist/lib/auth.js +0 -566
@@ -1,6 +1,6 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
3
- import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
3
+ import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
4
4
  // Add 'import' action if both create and update exist
5
5
  const HAS_IMPORT = true;
6
6
  const HAS_EXPORT = true;
@@ -27,7 +27,7 @@ export default class ProcessorMappings extends Command {
27
27
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
28
28
  };
29
29
  static flags = {
30
- context: Flags.string({ char: 'c', description: 'Context to use' }),
30
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
31
31
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
32
32
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
33
33
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -41,7 +41,7 @@ export default class ProcessorMappings extends Command {
41
41
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
42
42
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
43
43
  query: Flags.string({ description: 'Additional query params as JSON' }),
44
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
44
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
45
45
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
46
46
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
47
47
  };
@@ -99,9 +99,9 @@ export default class ProcessorMappings extends Command {
99
99
  }
100
100
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
101
101
  if (!context)
102
- this.error('No context configured. Run: aderis auth login --new');
103
- if (!context.tokens)
104
- this.error('Not logged in. Run: aderis auth login');
102
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
103
+ if (!hasCredentials(context))
104
+ this.error(missingCredentialsMessage(context));
105
105
  const queryParams = {};
106
106
  if (flags.page)
107
107
  queryParams.page = flags.page;
@@ -113,6 +113,8 @@ export default class ProcessorMappings extends Command {
113
113
  queryParams.search = flags.search;
114
114
  if (flags['sort-by'])
115
115
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
116
+ if (flags.fields)
117
+ queryParams.fields = flags.fields;
116
118
  if (flags.query) {
117
119
  try {
118
120
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -315,11 +317,6 @@ export default class ProcessorMappings extends Command {
315
317
  output = obj[arrayKeys[0]];
316
318
  }
317
319
  }
318
- // Filter fields if --fields flag is provided
319
- if (flags.fields) {
320
- const fieldList = flags.fields.split(',').map(f => f.trim());
321
- output = this.filterFields(output, fieldList);
322
- }
323
320
  // Sanitize output if requested
324
321
  if (flags.sanitize) {
325
322
  output = this.sanitizeOutput(output);
@@ -344,9 +341,9 @@ export default class ProcessorMappings extends Command {
344
341
  async runImport(flags) {
345
342
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
346
343
  if (!context)
347
- this.error('No context configured. Run: aderis auth login --new');
348
- if (!context.tokens)
349
- this.error('Not logged in. Run: aderis auth login');
344
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
345
+ if (!hasCredentials(context))
346
+ this.error(missingCredentialsMessage(context));
350
347
  // Get data from file, --data, or --set flags
351
348
  let items = [];
352
349
  if (flags.file) {
@@ -556,9 +553,9 @@ export default class ProcessorMappings extends Command {
556
553
  async runExport(flags) {
557
554
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
558
555
  if (!context)
559
- this.error('No context configured. Run: aderis auth login --new');
560
- if (!context.tokens)
561
- this.error('Not logged in. Run: aderis auth login');
556
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
557
+ if (!hasCredentials(context))
558
+ this.error(missingCredentialsMessage(context));
562
559
  const listEndpoint = ENDPOINTS['list'];
563
560
  if (!listEndpoint)
564
561
  this.error('Export requires a list endpoint');
@@ -574,6 +571,8 @@ export default class ProcessorMappings extends Command {
574
571
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
575
572
  if (flags.include)
576
573
  baseParams.include = flags.include;
574
+ if (flags.fields)
575
+ baseParams.fields = flags.fields;
577
576
  if (flags.query) {
578
577
  try {
579
578
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -628,12 +627,7 @@ export default class ProcessorMappings extends Command {
628
627
  }
629
628
  }
630
629
  process.stderr.write(' done\n');
631
- // Filter fields if specified
632
630
  let output = allItems;
633
- if (flags.fields) {
634
- const fieldList = flags.fields.split(',').map(f => f.trim());
635
- output = this.filterFields(allItems, fieldList);
636
- }
637
631
  // Sanitize output if requested
638
632
  if (flags.sanitize) {
639
633
  output = this.sanitizeOutput(output);
@@ -650,9 +644,9 @@ export default class ProcessorMappings extends Command {
650
644
  async runCamt053Import(flags) {
651
645
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
652
646
  if (!context)
653
- this.error('No context configured. Run: aderis auth login --new');
654
- if (!context.tokens)
655
- this.error('Not logged in. Run: aderis auth login');
647
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
648
+ if (!hasCredentials(context))
649
+ this.error(missingCredentialsMessage(context));
656
650
  if (!flags.file) {
657
651
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
658
652
  }
@@ -740,9 +734,9 @@ export default class ProcessorMappings extends Command {
740
734
  }
741
735
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
742
736
  if (!context)
743
- this.error('No context configured. Run: aderis auth login --new');
744
- if (!context.tokens)
745
- this.error('Not logged in. Run: aderis auth login');
737
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
738
+ if (!hasCredentials(context))
739
+ this.error(missingCredentialsMessage(context));
746
740
  // Get data from file, --data, or --set flags
747
741
  let items = [];
748
742
  if (flags.file) {
@@ -962,56 +956,6 @@ export default class ProcessorMappings extends Command {
962
956
  return `\$${op}`;
963
957
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
964
958
  }
965
- filterFields(data, fields) {
966
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
967
- const pick = (obj, keys) => {
968
- // Group keys by their first segment so sibling sub-paths
969
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
970
- // and don't clobber each other on merge.
971
- const directKeys = new Set();
972
- const nestedKeys = new Map();
973
- for (const key of keys) {
974
- if (key.split('.').some(isUnsafeKey))
975
- continue;
976
- if (key.includes('.')) {
977
- const [first, ...rest] = key.split('.');
978
- if (isUnsafeKey(first))
979
- continue;
980
- const list = nestedKeys.get(first);
981
- if (list) {
982
- list.push(rest.join('.'));
983
- }
984
- else {
985
- nestedKeys.set(first, [rest.join('.')]);
986
- }
987
- }
988
- else {
989
- directKeys.add(key);
990
- }
991
- }
992
- const result = {};
993
- for (const key of directKeys) {
994
- if (key in obj)
995
- result[key] = obj[key];
996
- }
997
- for (const [first, restKeys] of nestedKeys) {
998
- const val = obj[first];
999
- if (!val || typeof val !== 'object')
1000
- continue;
1001
- if (Array.isArray(val)) {
1002
- result[first] = val.map(item => pick(item, restKeys));
1003
- }
1004
- else {
1005
- result[first] = pick(val, restKeys);
1006
- }
1007
- }
1008
- return result;
1009
- };
1010
- if (Array.isArray(data)) {
1011
- return data.map(item => pick(item, fields));
1012
- }
1013
- return pick(data, fields);
1014
- }
1015
959
  sanitizeOutput(data) {
1016
960
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1017
961
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -31,7 +31,6 @@ export default class Processors extends Command {
31
31
  private runCamt053Import;
32
32
  private runNestedImport;
33
33
  private parseFilterValue;
34
- private filterFields;
35
34
  private sanitizeOutput;
36
35
  private getColumns;
37
36
  private getValue;
@@ -1,6 +1,6 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
3
- import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
3
+ import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
4
4
  // Add 'import' action if both create and update exist
5
5
  const HAS_IMPORT = true;
6
6
  const HAS_EXPORT = true;
@@ -35,7 +35,7 @@ export default class Processors extends Command {
35
35
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
36
36
  };
37
37
  static flags = {
38
- context: Flags.string({ char: 'c', description: 'Context to use' }),
38
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
39
39
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
40
40
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
41
41
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -49,7 +49,7 @@ export default class Processors extends Command {
49
49
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
50
50
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
51
51
  query: Flags.string({ description: 'Additional query params as JSON' }),
52
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
52
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
53
53
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
54
54
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
55
55
  };
@@ -107,9 +107,9 @@ export default class Processors extends Command {
107
107
  }
108
108
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
109
109
  if (!context)
110
- this.error('No context configured. Run: aderis auth login --new');
111
- if (!context.tokens)
112
- this.error('Not logged in. Run: aderis auth login');
110
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
111
+ if (!hasCredentials(context))
112
+ this.error(missingCredentialsMessage(context));
113
113
  const queryParams = {};
114
114
  if (flags.page)
115
115
  queryParams.page = flags.page;
@@ -121,6 +121,8 @@ export default class Processors extends Command {
121
121
  queryParams.search = flags.search;
122
122
  if (flags['sort-by'])
123
123
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
124
+ if (flags.fields)
125
+ queryParams.fields = flags.fields;
124
126
  if (flags.query) {
125
127
  try {
126
128
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -323,11 +325,6 @@ export default class Processors extends Command {
323
325
  output = obj[arrayKeys[0]];
324
326
  }
325
327
  }
326
- // Filter fields if --fields flag is provided
327
- if (flags.fields) {
328
- const fieldList = flags.fields.split(',').map(f => f.trim());
329
- output = this.filterFields(output, fieldList);
330
- }
331
328
  // Sanitize output if requested
332
329
  if (flags.sanitize) {
333
330
  output = this.sanitizeOutput(output);
@@ -352,9 +349,9 @@ export default class Processors extends Command {
352
349
  async runImport(flags) {
353
350
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
354
351
  if (!context)
355
- this.error('No context configured. Run: aderis auth login --new');
356
- if (!context.tokens)
357
- this.error('Not logged in. Run: aderis auth login');
352
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
353
+ if (!hasCredentials(context))
354
+ this.error(missingCredentialsMessage(context));
358
355
  // Get data from file, --data, or --set flags
359
356
  let items = [];
360
357
  if (flags.file) {
@@ -564,9 +561,9 @@ export default class Processors extends Command {
564
561
  async runExport(flags) {
565
562
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
566
563
  if (!context)
567
- this.error('No context configured. Run: aderis auth login --new');
568
- if (!context.tokens)
569
- this.error('Not logged in. Run: aderis auth login');
564
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
565
+ if (!hasCredentials(context))
566
+ this.error(missingCredentialsMessage(context));
570
567
  const listEndpoint = ENDPOINTS['list'];
571
568
  if (!listEndpoint)
572
569
  this.error('Export requires a list endpoint');
@@ -582,6 +579,8 @@ export default class Processors extends Command {
582
579
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
583
580
  if (flags.include)
584
581
  baseParams.include = flags.include;
582
+ if (flags.fields)
583
+ baseParams.fields = flags.fields;
585
584
  if (flags.query) {
586
585
  try {
587
586
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -636,12 +635,7 @@ export default class Processors extends Command {
636
635
  }
637
636
  }
638
637
  process.stderr.write(' done\n');
639
- // Filter fields if specified
640
638
  let output = allItems;
641
- if (flags.fields) {
642
- const fieldList = flags.fields.split(',').map(f => f.trim());
643
- output = this.filterFields(allItems, fieldList);
644
- }
645
639
  // Sanitize output if requested
646
640
  if (flags.sanitize) {
647
641
  output = this.sanitizeOutput(output);
@@ -658,9 +652,9 @@ export default class Processors extends Command {
658
652
  async runCamt053Import(flags) {
659
653
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
660
654
  if (!context)
661
- this.error('No context configured. Run: aderis auth login --new');
662
- if (!context.tokens)
663
- this.error('Not logged in. Run: aderis auth login');
655
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
656
+ if (!hasCredentials(context))
657
+ this.error(missingCredentialsMessage(context));
664
658
  if (!flags.file) {
665
659
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
666
660
  }
@@ -748,9 +742,9 @@ export default class Processors extends Command {
748
742
  }
749
743
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
750
744
  if (!context)
751
- this.error('No context configured. Run: aderis auth login --new');
752
- if (!context.tokens)
753
- this.error('Not logged in. Run: aderis auth login');
745
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
746
+ if (!hasCredentials(context))
747
+ this.error(missingCredentialsMessage(context));
754
748
  // Get data from file, --data, or --set flags
755
749
  let items = [];
756
750
  if (flags.file) {
@@ -970,56 +964,6 @@ export default class Processors extends Command {
970
964
  return `\$${op}`;
971
965
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
972
966
  }
973
- filterFields(data, fields) {
974
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
975
- const pick = (obj, keys) => {
976
- // Group keys by their first segment so sibling sub-paths
977
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
978
- // and don't clobber each other on merge.
979
- const directKeys = new Set();
980
- const nestedKeys = new Map();
981
- for (const key of keys) {
982
- if (key.split('.').some(isUnsafeKey))
983
- continue;
984
- if (key.includes('.')) {
985
- const [first, ...rest] = key.split('.');
986
- if (isUnsafeKey(first))
987
- continue;
988
- const list = nestedKeys.get(first);
989
- if (list) {
990
- list.push(rest.join('.'));
991
- }
992
- else {
993
- nestedKeys.set(first, [rest.join('.')]);
994
- }
995
- }
996
- else {
997
- directKeys.add(key);
998
- }
999
- }
1000
- const result = {};
1001
- for (const key of directKeys) {
1002
- if (key in obj)
1003
- result[key] = obj[key];
1004
- }
1005
- for (const [first, restKeys] of nestedKeys) {
1006
- const val = obj[first];
1007
- if (!val || typeof val !== 'object')
1008
- continue;
1009
- if (Array.isArray(val)) {
1010
- result[first] = val.map(item => pick(item, restKeys));
1011
- }
1012
- else {
1013
- result[first] = pick(val, restKeys);
1014
- }
1015
- }
1016
- return result;
1017
- };
1018
- if (Array.isArray(data)) {
1019
- return data.map(item => pick(item, fields));
1020
- }
1021
- return pick(data, fields);
1022
- }
1023
967
  sanitizeOutput(data) {
1024
968
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1025
969
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -31,7 +31,6 @@ export default class PurchaseOrderTypes extends Command {
31
31
  private runCamt053Import;
32
32
  private runNestedImport;
33
33
  private parseFilterValue;
34
- private filterFields;
35
34
  private sanitizeOutput;
36
35
  private getColumns;
37
36
  private getValue;
@@ -1,6 +1,6 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
3
- import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
3
+ import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
4
4
  // Add 'import' action if both create and update exist
5
5
  const HAS_IMPORT = true;
6
6
  const HAS_EXPORT = true;
@@ -29,7 +29,7 @@ export default class PurchaseOrderTypes extends Command {
29
29
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
30
30
  };
31
31
  static flags = {
32
- context: Flags.string({ char: 'c', description: 'Context to use' }),
32
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
33
33
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
34
34
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
35
35
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -43,7 +43,7 @@ export default class PurchaseOrderTypes extends Command {
43
43
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
44
44
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
45
45
  query: Flags.string({ description: 'Additional query params as JSON' }),
46
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
46
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
47
47
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
48
48
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
49
49
  };
@@ -101,9 +101,9 @@ export default class PurchaseOrderTypes extends Command {
101
101
  }
102
102
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
103
103
  if (!context)
104
- this.error('No context configured. Run: aderis auth login --new');
105
- if (!context.tokens)
106
- this.error('Not logged in. Run: aderis auth login');
104
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
105
+ if (!hasCredentials(context))
106
+ this.error(missingCredentialsMessage(context));
107
107
  const queryParams = {};
108
108
  if (flags.page)
109
109
  queryParams.page = flags.page;
@@ -115,6 +115,8 @@ export default class PurchaseOrderTypes extends Command {
115
115
  queryParams.search = flags.search;
116
116
  if (flags['sort-by'])
117
117
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
118
+ if (flags.fields)
119
+ queryParams.fields = flags.fields;
118
120
  if (flags.query) {
119
121
  try {
120
122
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -317,11 +319,6 @@ export default class PurchaseOrderTypes extends Command {
317
319
  output = obj[arrayKeys[0]];
318
320
  }
319
321
  }
320
- // Filter fields if --fields flag is provided
321
- if (flags.fields) {
322
- const fieldList = flags.fields.split(',').map(f => f.trim());
323
- output = this.filterFields(output, fieldList);
324
- }
325
322
  // Sanitize output if requested
326
323
  if (flags.sanitize) {
327
324
  output = this.sanitizeOutput(output);
@@ -346,9 +343,9 @@ export default class PurchaseOrderTypes extends Command {
346
343
  async runImport(flags) {
347
344
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
348
345
  if (!context)
349
- this.error('No context configured. Run: aderis auth login --new');
350
- if (!context.tokens)
351
- this.error('Not logged in. Run: aderis auth login');
346
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
347
+ if (!hasCredentials(context))
348
+ this.error(missingCredentialsMessage(context));
352
349
  // Get data from file, --data, or --set flags
353
350
  let items = [];
354
351
  if (flags.file) {
@@ -558,9 +555,9 @@ export default class PurchaseOrderTypes extends Command {
558
555
  async runExport(flags) {
559
556
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
560
557
  if (!context)
561
- this.error('No context configured. Run: aderis auth login --new');
562
- if (!context.tokens)
563
- this.error('Not logged in. Run: aderis auth login');
558
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
559
+ if (!hasCredentials(context))
560
+ this.error(missingCredentialsMessage(context));
564
561
  const listEndpoint = ENDPOINTS['list'];
565
562
  if (!listEndpoint)
566
563
  this.error('Export requires a list endpoint');
@@ -576,6 +573,8 @@ export default class PurchaseOrderTypes extends Command {
576
573
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
577
574
  if (flags.include)
578
575
  baseParams.include = flags.include;
576
+ if (flags.fields)
577
+ baseParams.fields = flags.fields;
579
578
  if (flags.query) {
580
579
  try {
581
580
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -630,12 +629,7 @@ export default class PurchaseOrderTypes extends Command {
630
629
  }
631
630
  }
632
631
  process.stderr.write(' done\n');
633
- // Filter fields if specified
634
632
  let output = allItems;
635
- if (flags.fields) {
636
- const fieldList = flags.fields.split(',').map(f => f.trim());
637
- output = this.filterFields(allItems, fieldList);
638
- }
639
633
  // Sanitize output if requested
640
634
  if (flags.sanitize) {
641
635
  output = this.sanitizeOutput(output);
@@ -652,9 +646,9 @@ export default class PurchaseOrderTypes extends Command {
652
646
  async runCamt053Import(flags) {
653
647
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
654
648
  if (!context)
655
- this.error('No context configured. Run: aderis auth login --new');
656
- if (!context.tokens)
657
- this.error('Not logged in. Run: aderis auth login');
649
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
650
+ if (!hasCredentials(context))
651
+ this.error(missingCredentialsMessage(context));
658
652
  if (!flags.file) {
659
653
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
660
654
  }
@@ -742,9 +736,9 @@ export default class PurchaseOrderTypes extends Command {
742
736
  }
743
737
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
744
738
  if (!context)
745
- this.error('No context configured. Run: aderis auth login --new');
746
- if (!context.tokens)
747
- this.error('Not logged in. Run: aderis auth login');
739
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
740
+ if (!hasCredentials(context))
741
+ this.error(missingCredentialsMessage(context));
748
742
  // Get data from file, --data, or --set flags
749
743
  let items = [];
750
744
  if (flags.file) {
@@ -964,56 +958,6 @@ export default class PurchaseOrderTypes extends Command {
964
958
  return `\$${op}`;
965
959
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
966
960
  }
967
- filterFields(data, fields) {
968
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
969
- const pick = (obj, keys) => {
970
- // Group keys by their first segment so sibling sub-paths
971
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
972
- // and don't clobber each other on merge.
973
- const directKeys = new Set();
974
- const nestedKeys = new Map();
975
- for (const key of keys) {
976
- if (key.split('.').some(isUnsafeKey))
977
- continue;
978
- if (key.includes('.')) {
979
- const [first, ...rest] = key.split('.');
980
- if (isUnsafeKey(first))
981
- continue;
982
- const list = nestedKeys.get(first);
983
- if (list) {
984
- list.push(rest.join('.'));
985
- }
986
- else {
987
- nestedKeys.set(first, [rest.join('.')]);
988
- }
989
- }
990
- else {
991
- directKeys.add(key);
992
- }
993
- }
994
- const result = {};
995
- for (const key of directKeys) {
996
- if (key in obj)
997
- result[key] = obj[key];
998
- }
999
- for (const [first, restKeys] of nestedKeys) {
1000
- const val = obj[first];
1001
- if (!val || typeof val !== 'object')
1002
- continue;
1003
- if (Array.isArray(val)) {
1004
- result[first] = val.map(item => pick(item, restKeys));
1005
- }
1006
- else {
1007
- result[first] = pick(val, restKeys);
1008
- }
1009
- }
1010
- return result;
1011
- };
1012
- if (Array.isArray(data)) {
1013
- return data.map(item => pick(item, fields));
1014
- }
1015
- return pick(data, fields);
1016
- }
1017
961
  sanitizeOutput(data) {
1018
962
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1019
963
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -16,6 +16,7 @@ export default class PurchaseOrders extends Command {
16
16
  set: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
17
  'line-item-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
18
  'note-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ 'file-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
20
  page: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
21
  limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
22
  include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -33,7 +34,6 @@ export default class PurchaseOrders extends Command {
33
34
  private runCamt053Import;
34
35
  private runNestedImport;
35
36
  private parseFilterValue;
36
- private filterFields;
37
37
  private sanitizeOutput;
38
38
  private getColumns;
39
39
  private getValue;