@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,30 +1,30 @@
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 = false;
8
8
  const NESTED_IMPORTS = ["line-items", "notes", "files", "payments"];
9
- const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "submit", "approve", "decline", "book", "rebuild-journal", "cancel", "confirm-cancellation", "change-vendor", "recalculate-payment-status", "validate", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-payments", "create-payments", "get-payments", "update-payments", "delete-payments", "payments-approve", "payments-cancel"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
9
+ const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "payment-derivation", "submit", "approve", "decline", "book", "rebuild-journal", "cancel", "change-vendor", "recalculate-payment-status", "validate", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-payments", "create-payments", "get-payments", "update-payments", "delete-payments", "payments-approve", "payments-reopen", "payments-cancel"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
11
  'list': { method: 'GET', path: '/v1/vendor-invoices', summary: 'Get all vendor invoices with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
12
- 'create': { method: 'POST', path: '/v1/vendor-invoices', summary: 'Create a new vendor invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'payments'] },
12
+ 'create': { method: 'POST', path: '/v1/vendor-invoices', summary: 'Create a new vendor invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'payments'] },
13
13
  'get': { method: 'GET', path: '/v1/vendor-invoices/{id}', summary: 'Get a vendor invoice by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
14
- 'update': { method: 'PATCH', path: '/v1/vendor-invoices/{id}', summary: 'Update a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'removeFileIds'] },
14
+ 'update': { method: 'PATCH', path: '/v1/vendor-invoices/{id}', summary: 'Update a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
15
15
  'delete': { method: 'DELETE', path: '/v1/vendor-invoices/{id}', summary: 'Delete a vendor invoice', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
16
+ 'payment-derivation': { method: 'GET', path: '/v1/vendor-invoices/{id}/payment-derivation', summary: 'Explain how a vendor invoice\'s paid amount is derived', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
16
17
  'submit': { method: 'POST', path: '/v1/vendor-invoices/{id}/submit', summary: 'Submit a vendor invoice for approval (transitions DRAFT to PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
17
- 'approve': { method: 'POST', path: '/v1/vendor-invoices/{id}/approve', summary: 'Approve a vendor invoice (transitions DRAFT/PENDING_APPROVAL to APPROVED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'removeFileIds'] },
18
+ 'approve': { method: 'POST', path: '/v1/vendor-invoices/{id}/approve', summary: 'Approve a vendor invoice (transitions DRAFT/PENDING_APPROVAL to APPROVED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
18
19
  'decline': { method: 'POST', path: '/v1/vendor-invoices/{id}/decline', summary: 'Decline a vendor invoice (transitions PENDING_APPROVAL to DECLINED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
19
20
  'book': { method: 'POST', path: '/v1/vendor-invoices/{id}/book', summary: 'Manually book a vendor invoice (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
20
21
  'rebuild-journal': { method: 'POST', path: '/v1/vendor-invoices/{id}/rebuild-journal', summary: 'Rebuild a vendor invoice booking journal (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
21
22
  'cancel': { method: 'POST', path: '/v1/vendor-invoices/{id}/cancel', summary: 'Cancel a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
22
- 'confirm-cancellation': { method: 'POST', path: '/v1/vendor-invoices/{id}/confirm-cancellation', summary: 'Confirm bank cancellation for a vendor invoice (PENDING_CANCELLATION to CANCELLED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
23
23
  'change-vendor': { method: 'POST', path: '/v1/vendor-invoices/{id}/change-vendor', summary: 'Change the vendor on a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
24
24
  'recalculate-payment-status': { method: 'POST', path: '/v1/vendor-invoices/{id}/recalculate-payment-status', summary: 'Recalculate a vendor invoice\'s open amount and payment status', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
25
- 'validate': { method: 'POST', path: '/v1/vendor-invoices/{id}/validate', summary: 'Validate a vendor invoice update without persisting changes', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'removeFileIds'] },
26
- 'create-line-items': { method: 'POST', path: '/v1/vendor-invoices/{invoiceId}/line-items', summary: 'Add a line item to a vendor invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
27
- 'update-line-items': { method: 'PATCH', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
25
+ 'validate': { method: 'POST', path: '/v1/vendor-invoices/{id}/validate', summary: 'Validate a vendor invoice update without persisting changes', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
26
+ 'create-line-items': { method: 'POST', path: '/v1/vendor-invoices/{invoiceId}/line-items', summary: 'Add a line item to a vendor invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems'] },
27
+ 'update-line-items': { method: 'PATCH', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems'] },
28
28
  'delete-line-items': { method: 'DELETE', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Remove a line item from a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
29
29
  'list-notes': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Get all notes for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
30
30
  'create-notes': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Create a note for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
@@ -43,6 +43,7 @@ const ENDPOINTS = {
43
43
  'update-payments': { method: 'PATCH', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Update a payment for a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
44
44
  'delete-payments': { method: 'DELETE', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Delete a payment from a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
45
45
  'payments-approve': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/approve', summary: 'Approve a payment entry (OPEN → APPROVED)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
46
+ 'payments-reopen': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/reopen', summary: 'Reopen an approved payment entry (APPROVED → OPEN)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
46
47
  'payments-cancel': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/cancel', summary: 'Cancel a sent payment entry (SENT -> ERROR)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
47
48
  };
48
49
  export default class VendorInvoices extends Command {
@@ -60,7 +61,7 @@ export default class VendorInvoices extends Command {
60
61
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
61
62
  };
62
63
  static flags = {
63
- context: Flags.string({ char: 'c', description: 'Context to use' }),
64
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
64
65
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
65
66
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
66
67
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -78,7 +79,7 @@ export default class VendorInvoices extends Command {
78
79
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
79
80
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
80
81
  query: Flags.string({ description: 'Additional query params as JSON' }),
81
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
82
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
82
83
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
83
84
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
84
85
  };
@@ -136,9 +137,9 @@ export default class VendorInvoices extends Command {
136
137
  }
137
138
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
138
139
  if (!context)
139
- this.error('No context configured. Run: aderis auth login --new');
140
- if (!context.tokens)
141
- this.error('Not logged in. Run: aderis auth login');
140
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
141
+ if (!hasCredentials(context))
142
+ this.error(missingCredentialsMessage(context));
142
143
  const queryParams = {};
143
144
  if (flags.page)
144
145
  queryParams.page = flags.page;
@@ -150,6 +151,8 @@ export default class VendorInvoices extends Command {
150
151
  queryParams.search = flags.search;
151
152
  if (flags['sort-by'])
152
153
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
154
+ if (flags.fields)
155
+ queryParams.fields = flags.fields;
153
156
  if (flags.query) {
154
157
  try {
155
158
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -352,11 +355,6 @@ export default class VendorInvoices extends Command {
352
355
  output = obj[arrayKeys[0]];
353
356
  }
354
357
  }
355
- // Filter fields if --fields flag is provided
356
- if (flags.fields) {
357
- const fieldList = flags.fields.split(',').map(f => f.trim());
358
- output = this.filterFields(output, fieldList);
359
- }
360
358
  // Sanitize output if requested
361
359
  if (flags.sanitize) {
362
360
  output = this.sanitizeOutput(output);
@@ -381,9 +379,9 @@ export default class VendorInvoices extends Command {
381
379
  async runImport(flags) {
382
380
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
383
381
  if (!context)
384
- this.error('No context configured. Run: aderis auth login --new');
385
- if (!context.tokens)
386
- this.error('Not logged in. Run: aderis auth login');
382
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
383
+ if (!hasCredentials(context))
384
+ this.error(missingCredentialsMessage(context));
387
385
  // Get data from file, --data, or --set flags
388
386
  let items = [];
389
387
  if (flags.file) {
@@ -593,9 +591,9 @@ export default class VendorInvoices extends Command {
593
591
  async runExport(flags) {
594
592
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
595
593
  if (!context)
596
- this.error('No context configured. Run: aderis auth login --new');
597
- if (!context.tokens)
598
- this.error('Not logged in. Run: aderis auth login');
594
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
595
+ if (!hasCredentials(context))
596
+ this.error(missingCredentialsMessage(context));
599
597
  const listEndpoint = ENDPOINTS['list'];
600
598
  if (!listEndpoint)
601
599
  this.error('Export requires a list endpoint');
@@ -611,6 +609,8 @@ export default class VendorInvoices extends Command {
611
609
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
612
610
  if (flags.include)
613
611
  baseParams.include = flags.include;
612
+ if (flags.fields)
613
+ baseParams.fields = flags.fields;
614
614
  if (flags.query) {
615
615
  try {
616
616
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -665,12 +665,7 @@ export default class VendorInvoices extends Command {
665
665
  }
666
666
  }
667
667
  process.stderr.write(' done\n');
668
- // Filter fields if specified
669
668
  let output = allItems;
670
- if (flags.fields) {
671
- const fieldList = flags.fields.split(',').map(f => f.trim());
672
- output = this.filterFields(allItems, fieldList);
673
- }
674
669
  // Sanitize output if requested
675
670
  if (flags.sanitize) {
676
671
  output = this.sanitizeOutput(output);
@@ -687,9 +682,9 @@ export default class VendorInvoices extends Command {
687
682
  async runCamt053Import(flags) {
688
683
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
689
684
  if (!context)
690
- this.error('No context configured. Run: aderis auth login --new');
691
- if (!context.tokens)
692
- this.error('Not logged in. Run: aderis auth login');
685
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
686
+ if (!hasCredentials(context))
687
+ this.error(missingCredentialsMessage(context));
693
688
  if (!flags.file) {
694
689
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
695
690
  }
@@ -777,9 +772,9 @@ export default class VendorInvoices extends Command {
777
772
  }
778
773
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
779
774
  if (!context)
780
- this.error('No context configured. Run: aderis auth login --new');
781
- if (!context.tokens)
782
- this.error('Not logged in. Run: aderis auth login');
775
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
776
+ if (!hasCredentials(context))
777
+ this.error(missingCredentialsMessage(context));
783
778
  // Get data from file, --data, or --set flags
784
779
  let items = [];
785
780
  if (flags.file) {
@@ -999,56 +994,6 @@ export default class VendorInvoices extends Command {
999
994
  return `\$${op}`;
1000
995
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
1001
996
  }
1002
- filterFields(data, fields) {
1003
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
1004
- const pick = (obj, keys) => {
1005
- // Group keys by their first segment so sibling sub-paths
1006
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
1007
- // and don't clobber each other on merge.
1008
- const directKeys = new Set();
1009
- const nestedKeys = new Map();
1010
- for (const key of keys) {
1011
- if (key.split('.').some(isUnsafeKey))
1012
- continue;
1013
- if (key.includes('.')) {
1014
- const [first, ...rest] = key.split('.');
1015
- if (isUnsafeKey(first))
1016
- continue;
1017
- const list = nestedKeys.get(first);
1018
- if (list) {
1019
- list.push(rest.join('.'));
1020
- }
1021
- else {
1022
- nestedKeys.set(first, [rest.join('.')]);
1023
- }
1024
- }
1025
- else {
1026
- directKeys.add(key);
1027
- }
1028
- }
1029
- const result = {};
1030
- for (const key of directKeys) {
1031
- if (key in obj)
1032
- result[key] = obj[key];
1033
- }
1034
- for (const [first, restKeys] of nestedKeys) {
1035
- const val = obj[first];
1036
- if (!val || typeof val !== 'object')
1037
- continue;
1038
- if (Array.isArray(val)) {
1039
- result[first] = val.map(item => pick(item, restKeys));
1040
- }
1041
- else {
1042
- result[first] = pick(val, restKeys);
1043
- }
1044
- }
1045
- return result;
1046
- };
1047
- if (Array.isArray(data)) {
1048
- return data.map(item => pick(item, fields));
1049
- }
1050
- return pick(data, fields);
1051
- }
1052
997
  sanitizeOutput(data) {
1053
998
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1054
999
  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 Webhooks 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;
@@ -31,7 +31,7 @@ export default class Webhooks extends Command {
31
31
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
32
32
  };
33
33
  static flags = {
34
- context: Flags.string({ char: 'c', description: 'Context to use' }),
34
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
35
35
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
36
36
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
37
37
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -45,7 +45,7 @@ export default class Webhooks extends Command {
45
45
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
46
46
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
47
47
  query: Flags.string({ description: 'Additional query params as JSON' }),
48
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
48
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
49
49
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
50
50
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
51
51
  };
@@ -103,9 +103,9 @@ export default class Webhooks extends Command {
103
103
  }
104
104
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
105
105
  if (!context)
106
- this.error('No context configured. Run: aderis auth login --new');
107
- if (!context.tokens)
108
- this.error('Not logged in. Run: aderis auth login');
106
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
107
+ if (!hasCredentials(context))
108
+ this.error(missingCredentialsMessage(context));
109
109
  const queryParams = {};
110
110
  if (flags.page)
111
111
  queryParams.page = flags.page;
@@ -117,6 +117,8 @@ export default class Webhooks extends Command {
117
117
  queryParams.search = flags.search;
118
118
  if (flags['sort-by'])
119
119
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
120
+ if (flags.fields)
121
+ queryParams.fields = flags.fields;
120
122
  if (flags.query) {
121
123
  try {
122
124
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -319,11 +321,6 @@ export default class Webhooks extends Command {
319
321
  output = obj[arrayKeys[0]];
320
322
  }
321
323
  }
322
- // Filter fields if --fields flag is provided
323
- if (flags.fields) {
324
- const fieldList = flags.fields.split(',').map(f => f.trim());
325
- output = this.filterFields(output, fieldList);
326
- }
327
324
  // Sanitize output if requested
328
325
  if (flags.sanitize) {
329
326
  output = this.sanitizeOutput(output);
@@ -348,9 +345,9 @@ export default class Webhooks extends Command {
348
345
  async runImport(flags) {
349
346
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
350
347
  if (!context)
351
- this.error('No context configured. Run: aderis auth login --new');
352
- if (!context.tokens)
353
- this.error('Not logged in. Run: aderis auth login');
348
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
349
+ if (!hasCredentials(context))
350
+ this.error(missingCredentialsMessage(context));
354
351
  // Get data from file, --data, or --set flags
355
352
  let items = [];
356
353
  if (flags.file) {
@@ -560,9 +557,9 @@ export default class Webhooks extends Command {
560
557
  async runExport(flags) {
561
558
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
562
559
  if (!context)
563
- this.error('No context configured. Run: aderis auth login --new');
564
- if (!context.tokens)
565
- this.error('Not logged in. Run: aderis auth login');
560
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
561
+ if (!hasCredentials(context))
562
+ this.error(missingCredentialsMessage(context));
566
563
  const listEndpoint = ENDPOINTS['list'];
567
564
  if (!listEndpoint)
568
565
  this.error('Export requires a list endpoint');
@@ -578,6 +575,8 @@ export default class Webhooks extends Command {
578
575
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
579
576
  if (flags.include)
580
577
  baseParams.include = flags.include;
578
+ if (flags.fields)
579
+ baseParams.fields = flags.fields;
581
580
  if (flags.query) {
582
581
  try {
583
582
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -632,12 +631,7 @@ export default class Webhooks extends Command {
632
631
  }
633
632
  }
634
633
  process.stderr.write(' done\n');
635
- // Filter fields if specified
636
634
  let output = allItems;
637
- if (flags.fields) {
638
- const fieldList = flags.fields.split(',').map(f => f.trim());
639
- output = this.filterFields(allItems, fieldList);
640
- }
641
635
  // Sanitize output if requested
642
636
  if (flags.sanitize) {
643
637
  output = this.sanitizeOutput(output);
@@ -654,9 +648,9 @@ export default class Webhooks extends Command {
654
648
  async runCamt053Import(flags) {
655
649
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
656
650
  if (!context)
657
- this.error('No context configured. Run: aderis auth login --new');
658
- if (!context.tokens)
659
- this.error('Not logged in. Run: aderis auth login');
651
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
652
+ if (!hasCredentials(context))
653
+ this.error(missingCredentialsMessage(context));
660
654
  if (!flags.file) {
661
655
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
662
656
  }
@@ -744,9 +738,9 @@ export default class Webhooks extends Command {
744
738
  }
745
739
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
746
740
  if (!context)
747
- this.error('No context configured. Run: aderis auth login --new');
748
- if (!context.tokens)
749
- this.error('Not logged in. Run: aderis auth login');
741
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
742
+ if (!hasCredentials(context))
743
+ this.error(missingCredentialsMessage(context));
750
744
  // Get data from file, --data, or --set flags
751
745
  let items = [];
752
746
  if (flags.file) {
@@ -966,56 +960,6 @@ export default class Webhooks extends Command {
966
960
  return `\$${op}`;
967
961
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
968
962
  }
969
- filterFields(data, fields) {
970
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
971
- const pick = (obj, keys) => {
972
- // Group keys by their first segment so sibling sub-paths
973
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
974
- // and don't clobber each other on merge.
975
- const directKeys = new Set();
976
- const nestedKeys = new Map();
977
- for (const key of keys) {
978
- if (key.split('.').some(isUnsafeKey))
979
- continue;
980
- if (key.includes('.')) {
981
- const [first, ...rest] = key.split('.');
982
- if (isUnsafeKey(first))
983
- continue;
984
- const list = nestedKeys.get(first);
985
- if (list) {
986
- list.push(rest.join('.'));
987
- }
988
- else {
989
- nestedKeys.set(first, [rest.join('.')]);
990
- }
991
- }
992
- else {
993
- directKeys.add(key);
994
- }
995
- }
996
- const result = {};
997
- for (const key of directKeys) {
998
- if (key in obj)
999
- result[key] = obj[key];
1000
- }
1001
- for (const [first, restKeys] of nestedKeys) {
1002
- const val = obj[first];
1003
- if (!val || typeof val !== 'object')
1004
- continue;
1005
- if (Array.isArray(val)) {
1006
- result[first] = val.map(item => pick(item, restKeys));
1007
- }
1008
- else {
1009
- result[first] = pick(val, restKeys);
1010
- }
1011
- }
1012
- return result;
1013
- };
1014
- if (Array.isArray(data)) {
1015
- return data.map(item => pick(item, fields));
1016
- }
1017
- return pick(data, fields);
1018
- }
1019
963
  sanitizeOutput(data) {
1020
964
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1021
965
  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 Workflows 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 = false;
6
6
  const HAS_EXPORT = true;
@@ -23,7 +23,7 @@ export default class Workflows extends Command {
23
23
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
24
24
  };
25
25
  static flags = {
26
- context: Flags.string({ char: 'c', description: 'Context to use' }),
26
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
27
27
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
28
28
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
29
29
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -37,7 +37,7 @@ export default class Workflows extends Command {
37
37
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
38
38
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
39
39
  query: Flags.string({ description: 'Additional query params as JSON' }),
40
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
40
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
41
41
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
42
42
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
43
43
  };
@@ -95,9 +95,9 @@ export default class Workflows extends Command {
95
95
  }
96
96
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
97
97
  if (!context)
98
- this.error('No context configured. Run: aderis auth login --new');
99
- if (!context.tokens)
100
- this.error('Not logged in. Run: aderis auth login');
98
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
99
+ if (!hasCredentials(context))
100
+ this.error(missingCredentialsMessage(context));
101
101
  const queryParams = {};
102
102
  if (flags.page)
103
103
  queryParams.page = flags.page;
@@ -109,6 +109,8 @@ export default class Workflows extends Command {
109
109
  queryParams.search = flags.search;
110
110
  if (flags['sort-by'])
111
111
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
112
+ if (flags.fields)
113
+ queryParams.fields = flags.fields;
112
114
  if (flags.query) {
113
115
  try {
114
116
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -311,11 +313,6 @@ export default class Workflows extends Command {
311
313
  output = obj[arrayKeys[0]];
312
314
  }
313
315
  }
314
- // Filter fields if --fields flag is provided
315
- if (flags.fields) {
316
- const fieldList = flags.fields.split(',').map(f => f.trim());
317
- output = this.filterFields(output, fieldList);
318
- }
319
316
  // Sanitize output if requested
320
317
  if (flags.sanitize) {
321
318
  output = this.sanitizeOutput(output);
@@ -340,9 +337,9 @@ export default class Workflows extends Command {
340
337
  async runImport(flags) {
341
338
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
342
339
  if (!context)
343
- this.error('No context configured. Run: aderis auth login --new');
344
- if (!context.tokens)
345
- this.error('Not logged in. Run: aderis auth login');
340
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
341
+ if (!hasCredentials(context))
342
+ this.error(missingCredentialsMessage(context));
346
343
  // Get data from file, --data, or --set flags
347
344
  let items = [];
348
345
  if (flags.file) {
@@ -552,9 +549,9 @@ export default class Workflows extends Command {
552
549
  async runExport(flags) {
553
550
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
554
551
  if (!context)
555
- this.error('No context configured. Run: aderis auth login --new');
556
- if (!context.tokens)
557
- this.error('Not logged in. Run: aderis auth login');
552
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
553
+ if (!hasCredentials(context))
554
+ this.error(missingCredentialsMessage(context));
558
555
  const listEndpoint = ENDPOINTS['list'];
559
556
  if (!listEndpoint)
560
557
  this.error('Export requires a list endpoint');
@@ -570,6 +567,8 @@ export default class Workflows extends Command {
570
567
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
571
568
  if (flags.include)
572
569
  baseParams.include = flags.include;
570
+ if (flags.fields)
571
+ baseParams.fields = flags.fields;
573
572
  if (flags.query) {
574
573
  try {
575
574
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -624,12 +623,7 @@ export default class Workflows extends Command {
624
623
  }
625
624
  }
626
625
  process.stderr.write(' done\n');
627
- // Filter fields if specified
628
626
  let output = allItems;
629
- if (flags.fields) {
630
- const fieldList = flags.fields.split(',').map(f => f.trim());
631
- output = this.filterFields(allItems, fieldList);
632
- }
633
627
  // Sanitize output if requested
634
628
  if (flags.sanitize) {
635
629
  output = this.sanitizeOutput(output);
@@ -646,9 +640,9 @@ export default class Workflows extends Command {
646
640
  async runCamt053Import(flags) {
647
641
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
648
642
  if (!context)
649
- this.error('No context configured. Run: aderis auth login --new');
650
- if (!context.tokens)
651
- this.error('Not logged in. Run: aderis auth login');
643
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
644
+ if (!hasCredentials(context))
645
+ this.error(missingCredentialsMessage(context));
652
646
  if (!flags.file) {
653
647
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
654
648
  }
@@ -736,9 +730,9 @@ export default class Workflows extends Command {
736
730
  }
737
731
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
738
732
  if (!context)
739
- this.error('No context configured. Run: aderis auth login --new');
740
- if (!context.tokens)
741
- this.error('Not logged in. Run: aderis auth login');
733
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
734
+ if (!hasCredentials(context))
735
+ this.error(missingCredentialsMessage(context));
742
736
  // Get data from file, --data, or --set flags
743
737
  let items = [];
744
738
  if (flags.file) {
@@ -958,56 +952,6 @@ export default class Workflows extends Command {
958
952
  return `\$${op}`;
959
953
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
960
954
  }
961
- filterFields(data, fields) {
962
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
963
- const pick = (obj, keys) => {
964
- // Group keys by their first segment so sibling sub-paths
965
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
966
- // and don't clobber each other on merge.
967
- const directKeys = new Set();
968
- const nestedKeys = new Map();
969
- for (const key of keys) {
970
- if (key.split('.').some(isUnsafeKey))
971
- continue;
972
- if (key.includes('.')) {
973
- const [first, ...rest] = key.split('.');
974
- if (isUnsafeKey(first))
975
- continue;
976
- const list = nestedKeys.get(first);
977
- if (list) {
978
- list.push(rest.join('.'));
979
- }
980
- else {
981
- nestedKeys.set(first, [rest.join('.')]);
982
- }
983
- }
984
- else {
985
- directKeys.add(key);
986
- }
987
- }
988
- const result = {};
989
- for (const key of directKeys) {
990
- if (key in obj)
991
- result[key] = obj[key];
992
- }
993
- for (const [first, restKeys] of nestedKeys) {
994
- const val = obj[first];
995
- if (!val || typeof val !== 'object')
996
- continue;
997
- if (Array.isArray(val)) {
998
- result[first] = val.map(item => pick(item, restKeys));
999
- }
1000
- else {
1001
- result[first] = pick(val, restKeys);
1002
- }
1003
- }
1004
- return result;
1005
- };
1006
- if (Array.isArray(data)) {
1007
- return data.map(item => pick(item, fields));
1008
- }
1009
- return pick(data, fields);
1010
- }
1011
955
  sanitizeOutput(data) {
1012
956
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1013
957
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;