@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,26 +1,28 @@
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;
7
7
  const HAS_CAMT053 = true;
8
8
  const NESTED_IMPORTS = ["allocations", "allocations-notes"];
9
- const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "allocations-reprocess-unbooked", "allocations-unbooked", "list-allocations", "create-allocations", "get-allocations", "update-allocations", "delete-allocations", "allocations-confirm", "import-camt053", "list-allocations-notes", "create-allocations-notes", "get-allocations-notes", "update-allocations-notes", "delete-allocations-notes"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
9
+ const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "reconcile", "allocations-reprocess-unbooked", "allocations-unbooked", "list-allocations", "create-allocations", "get-allocations", "update-allocations", "delete-allocations", "list-allocations-settlement-accounts", "allocations-confirm", "import-camt053", "list-allocations-notes", "create-allocations-notes", "get-allocations-notes", "update-allocations-notes", "delete-allocations-notes"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
11
  'list': { method: 'GET', path: '/v1/account-transactions', summary: 'Get all account transactions with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
12
12
  'create': { method: 'POST', path: '/v1/account-transactions', summary: 'Create a new account transaction', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['allocations'] },
13
13
  'get': { method: 'GET', path: '/v1/account-transactions/{id}', summary: 'Get an account transaction by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
14
14
  'update': { method: 'PATCH', path: '/v1/account-transactions/{id}', summary: 'Update an account transaction', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['allocations'] },
15
15
  'delete': { method: 'DELETE', path: '/v1/account-transactions/{id}', summary: 'Delete an account transaction', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
16
- 'allocations-reprocess-unbooked': { method: 'POST', path: '/v1/account-transactions/allocations/reprocess-unbooked', summary: 'Internal — re-book confirmed allocations that have no journal', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['allocationIds'] },
17
- 'allocations-unbooked': { method: 'GET', path: '/v1/account-transactions/allocations/unbooked', summary: 'Internal — list confirmed allocations that have no journal', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
16
+ 'reconcile': { method: 'POST', path: '/v1/account-transactions/reconcile', summary: 'Send selected transactions to the reconciliation agent', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['transactionIds'] },
17
+ 'allocations-reprocess-unbooked': { method: 'POST', path: '/v1/account-transactions/allocations/reprocess-unbooked', summary: 'Internal — re-book or repair allocations stuck at CONFIRMED', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['allocationIds'] },
18
+ 'allocations-unbooked': { method: 'GET', path: '/v1/account-transactions/allocations/unbooked', summary: 'Internal — list allocations stuck at CONFIRMED', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
18
19
  'list-allocations': { method: 'GET', path: '/v1/account-transactions/{transactionId}/allocations', summary: 'Get all allocations for an account transaction', pathParams: ['transactionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
19
20
  'create-allocations': { method: 'POST', path: '/v1/account-transactions/{transactionId}/allocations', summary: 'Create an allocation for an account transaction', pathParams: ['transactionId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['notes'] },
20
21
  'get-allocations': { method: 'GET', path: '/v1/account-transactions/{transactionId}/allocations/{allocationId}', summary: 'Get an allocation by ID', pathParams: ['transactionId', 'allocationId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
21
22
  'update-allocations': { method: 'PATCH', path: '/v1/account-transactions/{transactionId}/allocations/{allocationId}', summary: 'Update an allocation', pathParams: ['transactionId', 'allocationId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['notes'] },
22
23
  'delete-allocations': { method: 'DELETE', path: '/v1/account-transactions/{transactionId}/allocations/{allocationId}', summary: 'Delete an allocation', pathParams: ['transactionId', 'allocationId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
23
- 'allocations-confirm': { method: 'POST', path: '/v1/account-transactions/{transactionId}/allocations/{allocationId}/confirm', summary: 'Confirm an allocation (transition from REVIEW to CONFIRMED)', pathParams: ['transactionId', 'allocationId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
24
+ 'list-allocations-settlement-accounts': { method: 'GET', path: '/v1/account-transactions/{transactionId}/allocations/{allocationId}/settlement-accounts', summary: 'Which account each ledger would settle the allocated document against', pathParams: ['transactionId', 'allocationId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
25
+ 'allocations-confirm': { method: 'POST', path: '/v1/account-transactions/{transactionId}/allocations/{allocationId}/confirm', summary: 'Confirm an allocation (transition from REVIEW to CONFIRMED)', pathParams: ['transactionId', 'allocationId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['counterAccounts'] },
24
26
  'import-camt053': { method: 'POST', path: '/v1/account-transactions/import/camt053', summary: 'Import CAMT.053 bank statement (ISO 20022 format)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
25
27
  'list-allocations-notes': { method: 'GET', path: '/v1/account-transactions/allocations/{allocationId}/notes', summary: 'Get all notes for an allocation', pathParams: ['allocationId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
26
28
  'create-allocations-notes': { method: 'POST', path: '/v1/account-transactions/allocations/{allocationId}/notes', summary: 'Create a note for an allocation', pathParams: ['allocationId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
@@ -43,7 +45,7 @@ export default class AccountTransactions extends Command {
43
45
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
44
46
  };
45
47
  static flags = {
46
- context: Flags.string({ char: 'c', description: 'Context to use' }),
48
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
47
49
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
48
50
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
49
51
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -59,7 +61,7 @@ export default class AccountTransactions extends Command {
59
61
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
60
62
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
61
63
  query: Flags.string({ description: 'Additional query params as JSON' }),
62
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
64
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
63
65
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
64
66
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
65
67
  };
@@ -117,9 +119,9 @@ export default class AccountTransactions extends Command {
117
119
  }
118
120
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
119
121
  if (!context)
120
- this.error('No context configured. Run: aderis auth login --new');
121
- if (!context.tokens)
122
- this.error('Not logged in. Run: aderis auth login');
122
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
123
+ if (!hasCredentials(context))
124
+ this.error(missingCredentialsMessage(context));
123
125
  const queryParams = {};
124
126
  if (flags.page)
125
127
  queryParams.page = flags.page;
@@ -131,6 +133,8 @@ export default class AccountTransactions extends Command {
131
133
  queryParams.search = flags.search;
132
134
  if (flags['sort-by'])
133
135
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
136
+ if (flags.fields)
137
+ queryParams.fields = flags.fields;
134
138
  if (flags.query) {
135
139
  try {
136
140
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -333,11 +337,6 @@ export default class AccountTransactions extends Command {
333
337
  output = obj[arrayKeys[0]];
334
338
  }
335
339
  }
336
- // Filter fields if --fields flag is provided
337
- if (flags.fields) {
338
- const fieldList = flags.fields.split(',').map(f => f.trim());
339
- output = this.filterFields(output, fieldList);
340
- }
341
340
  // Sanitize output if requested
342
341
  if (flags.sanitize) {
343
342
  output = this.sanitizeOutput(output);
@@ -362,9 +361,9 @@ export default class AccountTransactions extends Command {
362
361
  async runImport(flags) {
363
362
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
364
363
  if (!context)
365
- this.error('No context configured. Run: aderis auth login --new');
366
- if (!context.tokens)
367
- this.error('Not logged in. Run: aderis auth login');
364
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
365
+ if (!hasCredentials(context))
366
+ this.error(missingCredentialsMessage(context));
368
367
  // Get data from file, --data, or --set flags
369
368
  let items = [];
370
369
  if (flags.file) {
@@ -574,9 +573,9 @@ export default class AccountTransactions extends Command {
574
573
  async runExport(flags) {
575
574
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
576
575
  if (!context)
577
- this.error('No context configured. Run: aderis auth login --new');
578
- if (!context.tokens)
579
- this.error('Not logged in. Run: aderis auth login');
576
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
577
+ if (!hasCredentials(context))
578
+ this.error(missingCredentialsMessage(context));
580
579
  const listEndpoint = ENDPOINTS['list'];
581
580
  if (!listEndpoint)
582
581
  this.error('Export requires a list endpoint');
@@ -592,6 +591,8 @@ export default class AccountTransactions extends Command {
592
591
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
593
592
  if (flags.include)
594
593
  baseParams.include = flags.include;
594
+ if (flags.fields)
595
+ baseParams.fields = flags.fields;
595
596
  if (flags.query) {
596
597
  try {
597
598
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -646,12 +647,7 @@ export default class AccountTransactions extends Command {
646
647
  }
647
648
  }
648
649
  process.stderr.write(' done\n');
649
- // Filter fields if specified
650
650
  let output = allItems;
651
- if (flags.fields) {
652
- const fieldList = flags.fields.split(',').map(f => f.trim());
653
- output = this.filterFields(allItems, fieldList);
654
- }
655
651
  // Sanitize output if requested
656
652
  if (flags.sanitize) {
657
653
  output = this.sanitizeOutput(output);
@@ -668,9 +664,9 @@ export default class AccountTransactions extends Command {
668
664
  async runCamt053Import(flags) {
669
665
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
670
666
  if (!context)
671
- this.error('No context configured. Run: aderis auth login --new');
672
- if (!context.tokens)
673
- this.error('Not logged in. Run: aderis auth login');
667
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
668
+ if (!hasCredentials(context))
669
+ this.error(missingCredentialsMessage(context));
674
670
  if (!flags.file) {
675
671
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
676
672
  }
@@ -758,9 +754,9 @@ export default class AccountTransactions extends Command {
758
754
  }
759
755
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
760
756
  if (!context)
761
- this.error('No context configured. Run: aderis auth login --new');
762
- if (!context.tokens)
763
- this.error('Not logged in. Run: aderis auth login');
757
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
758
+ if (!hasCredentials(context))
759
+ this.error(missingCredentialsMessage(context));
764
760
  // Get data from file, --data, or --set flags
765
761
  let items = [];
766
762
  if (flags.file) {
@@ -980,56 +976,6 @@ export default class AccountTransactions extends Command {
980
976
  return `\$${op}`;
981
977
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
982
978
  }
983
- filterFields(data, fields) {
984
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
985
- const pick = (obj, keys) => {
986
- // Group keys by their first segment so sibling sub-paths
987
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
988
- // and don't clobber each other on merge.
989
- const directKeys = new Set();
990
- const nestedKeys = new Map();
991
- for (const key of keys) {
992
- if (key.split('.').some(isUnsafeKey))
993
- continue;
994
- if (key.includes('.')) {
995
- const [first, ...rest] = key.split('.');
996
- if (isUnsafeKey(first))
997
- continue;
998
- const list = nestedKeys.get(first);
999
- if (list) {
1000
- list.push(rest.join('.'));
1001
- }
1002
- else {
1003
- nestedKeys.set(first, [rest.join('.')]);
1004
- }
1005
- }
1006
- else {
1007
- directKeys.add(key);
1008
- }
1009
- }
1010
- const result = {};
1011
- for (const key of directKeys) {
1012
- if (key in obj)
1013
- result[key] = obj[key];
1014
- }
1015
- for (const [first, restKeys] of nestedKeys) {
1016
- const val = obj[first];
1017
- if (!val || typeof val !== 'object')
1018
- continue;
1019
- if (Array.isArray(val)) {
1020
- result[first] = val.map(item => pick(item, restKeys));
1021
- }
1022
- else {
1023
- result[first] = pick(val, restKeys);
1024
- }
1025
- }
1026
- return result;
1027
- };
1028
- if (Array.isArray(data)) {
1029
- return data.map(item => pick(item, fields));
1030
- }
1031
- return pick(data, fields);
1032
- }
1033
979
  sanitizeOutput(data) {
1034
980
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1035
981
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -32,7 +32,6 @@ export default class Accounts extends Command {
32
32
  private runCamt053Import;
33
33
  private runNestedImport;
34
34
  private parseFilterValue;
35
- private filterFields;
36
35
  private sanitizeOutput;
37
36
  private getColumns;
38
37
  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;
@@ -9,9 +9,9 @@ const NESTED_IMPORTS = ["notes"];
9
9
  const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
11
  'list': { method: 'GET', path: '/v1/accounts', summary: 'List all accounts', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
12
- 'create': { method: 'POST', path: '/v1/accounts', summary: 'Create a new account', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'notes'] },
12
+ 'create': { method: 'POST', path: '/v1/accounts', summary: 'Create a new account', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'notes'] },
13
13
  'get': { method: 'GET', path: '/v1/accounts/{id}', summary: 'Get an account by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
14
- 'update': { method: 'PATCH', path: '/v1/accounts/{id}', summary: 'Update an account', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds'] },
14
+ 'update': { method: 'PATCH', path: '/v1/accounts/{id}', summary: 'Update an account', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes'] },
15
15
  'delete': { method: 'DELETE', path: '/v1/accounts/{id}', summary: 'Delete an account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
16
16
  'list-notes': { method: 'GET', path: '/v1/accounts/{accountId}/notes', summary: 'Get all notes for an account', pathParams: ['accountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
17
17
  'create-notes': { method: 'POST', path: '/v1/accounts/{accountId}/notes', summary: 'Create a note for an account', pathParams: ['accountId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
@@ -34,7 +34,7 @@ export default class Accounts extends Command {
34
34
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
35
35
  };
36
36
  static flags = {
37
- context: Flags.string({ char: 'c', description: 'Context to use' }),
37
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
38
38
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
39
39
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
40
40
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -49,7 +49,7 @@ export default class Accounts 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 Accounts 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 Accounts 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 Accounts 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 Accounts 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 Accounts 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 Accounts 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 Accounts 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 Accounts 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 Accounts 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 Accounts 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;
@@ -32,7 +32,6 @@ export default class AgentPlatform extends Command {
32
32
  private runCamt053Import;
33
33
  private runNestedImport;
34
34
  private parseFilterValue;
35
- private filterFields;
36
35
  private sanitizeOutput;
37
36
  private getColumns;
38
37
  private getValue;