@aderis-erp/cli 1.0.14 → 1.0.15

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 (83) hide show
  1. package/dist/commands/access-groups.js +17 -17
  2. package/dist/commands/account-transaction-allocations.js +17 -17
  3. package/dist/commands/account-transactions.js +19 -19
  4. package/dist/commands/accounts.js +19 -19
  5. package/dist/commands/agent-platform.js +19 -18
  6. package/dist/commands/analytics.d.ts +40 -0
  7. package/dist/commands/{documents.js → analytics.js} +29 -39
  8. package/dist/commands/audit-log.js +17 -17
  9. package/dist/commands/auth/login.js +11 -2
  10. package/dist/commands/auth/logout.d.ts +2 -0
  11. package/dist/commands/auth/logout.js +19 -6
  12. package/dist/commands/auth/status.js +26 -12
  13. package/dist/commands/auth/tenant.js +12 -3
  14. package/dist/commands/auth.js +32 -17
  15. package/dist/commands/bank-accounts.js +17 -17
  16. package/dist/commands/bank-integrations.js +17 -17
  17. package/dist/commands/business-partners.js +25 -22
  18. package/dist/commands/chat.js +17 -17
  19. package/dist/commands/config/add.d.ts +9 -0
  20. package/dist/commands/config/add.js +64 -2
  21. package/dist/commands/config/current.js +9 -4
  22. package/dist/commands/config/list.js +9 -4
  23. package/dist/commands/config/set-api-key.d.ts +10 -0
  24. package/dist/commands/config/set-api-key.js +49 -0
  25. package/dist/commands/config/use.js +3 -4
  26. package/dist/commands/credit-card-accounts.js +17 -17
  27. package/dist/commands/delivery-notes.js +22 -21
  28. package/dist/commands/dunning-configs.js +17 -17
  29. package/dist/commands/dunning-reminders.js +17 -17
  30. package/dist/commands/dunning-runs.js +17 -17
  31. package/dist/commands/email-transactions.js +17 -17
  32. package/dist/commands/employees.js +19 -19
  33. package/dist/commands/entity-configurations.js +17 -17
  34. package/dist/commands/event-processor-mappings.js +17 -17
  35. package/dist/commands/{documents.d.ts → files.d.ts} +1 -1
  36. package/dist/commands/{files-internal.js → files.js} +35 -29
  37. package/dist/commands/health.js +17 -17
  38. package/dist/commands/inbox.js +17 -17
  39. package/dist/commands/integrations.js +17 -17
  40. package/dist/commands/item-categories.js +17 -17
  41. package/dist/commands/items.js +19 -19
  42. package/dist/commands/journal-lines.js +17 -17
  43. package/dist/commands/journals.js +21 -20
  44. package/dist/commands/ledgers.js +21 -18
  45. package/dist/commands/office-locations.js +17 -17
  46. package/dist/commands/payment-methods.js +17 -17
  47. package/dist/commands/payment-runs.js +19 -18
  48. package/dist/commands/payment-terms.js +17 -17
  49. package/dist/commands/payments.js +17 -17
  50. package/dist/commands/price-lists.js +17 -17
  51. package/dist/commands/processor-mappings.js +17 -17
  52. package/dist/commands/processors.js +17 -17
  53. package/dist/commands/purchase-order-types.js +17 -17
  54. package/dist/commands/purchase-orders.d.ts +1 -0
  55. package/dist/commands/purchase-orders.js +28 -21
  56. package/dist/commands/reports.js +17 -17
  57. package/dist/commands/salary-types.js +17 -17
  58. package/dist/commands/sales-invoices.js +27 -25
  59. package/dist/commands/sales-orders.js +19 -19
  60. package/dist/commands/saved-views.js +17 -17
  61. package/dist/commands/scheduled-processor-mappings.js +17 -17
  62. package/dist/commands/schedules.js +17 -17
  63. package/dist/commands/switch.js +10 -3
  64. package/dist/commands/{files-internal.d.ts → tag-configurations.d.ts} +1 -1
  65. package/dist/commands/tag-configurations.js +1178 -0
  66. package/dist/commands/tags.js +17 -17
  67. package/dist/commands/tax-codes.js +17 -17
  68. package/dist/commands/tax-matrix.js +17 -17
  69. package/dist/commands/tenants.js +17 -17
  70. package/dist/commands/text-templates.js +17 -17
  71. package/dist/commands/tools.js +17 -17
  72. package/dist/commands/vat-declarations.js +17 -17
  73. package/dist/commands/vendor-invoices.js +21 -21
  74. package/dist/commands/webhooks.js +17 -17
  75. package/dist/commands/workflows.js +17 -17
  76. package/dist/lib/api-client.js +11 -6
  77. package/dist/lib/auth.d.ts +8 -0
  78. package/dist/lib/auth.js +51 -7
  79. package/dist/lib/base-api-command.js +6 -4
  80. package/dist/lib/config.d.ts +31 -1
  81. package/dist/lib/config.js +31 -0
  82. package/oclif.manifest.json +729 -247
  83. package/package.json +2 -2
@@ -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;
@@ -42,7 +42,7 @@ export default class BankIntegrations extends Command {
42
42
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
43
43
  };
44
44
  static flags = {
45
- context: Flags.string({ char: 'c', description: 'Context to use' }),
45
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
46
46
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
47
47
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
48
48
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -116,9 +116,9 @@ export default class BankIntegrations extends Command {
116
116
  }
117
117
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
118
118
  if (!context)
119
- this.error('No context configured. Run: aderis auth login --new');
120
- if (!context.tokens)
121
- this.error('Not logged in. Run: aderis auth login');
119
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
120
+ if (!hasCredentials(context))
121
+ this.error(missingCredentialsMessage(context));
122
122
  const queryParams = {};
123
123
  if (flags.page)
124
124
  queryParams.page = flags.page;
@@ -361,9 +361,9 @@ export default class BankIntegrations extends Command {
361
361
  async runImport(flags) {
362
362
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
363
363
  if (!context)
364
- this.error('No context configured. Run: aderis auth login --new');
365
- if (!context.tokens)
366
- 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));
367
367
  // Get data from file, --data, or --set flags
368
368
  let items = [];
369
369
  if (flags.file) {
@@ -573,9 +573,9 @@ export default class BankIntegrations extends Command {
573
573
  async runExport(flags) {
574
574
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
575
575
  if (!context)
576
- this.error('No context configured. Run: aderis auth login --new');
577
- if (!context.tokens)
578
- 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));
579
579
  const listEndpoint = ENDPOINTS['list'];
580
580
  if (!listEndpoint)
581
581
  this.error('Export requires a list endpoint');
@@ -667,9 +667,9 @@ export default class BankIntegrations extends Command {
667
667
  async runCamt053Import(flags) {
668
668
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
669
669
  if (!context)
670
- this.error('No context configured. Run: aderis auth login --new');
671
- if (!context.tokens)
672
- this.error('Not logged in. Run: aderis auth login');
670
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
671
+ if (!hasCredentials(context))
672
+ this.error(missingCredentialsMessage(context));
673
673
  if (!flags.file) {
674
674
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
675
675
  }
@@ -757,9 +757,9 @@ export default class BankIntegrations extends Command {
757
757
  }
758
758
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
759
759
  if (!context)
760
- this.error('No context configured. Run: aderis auth login --new');
761
- if (!context.tokens)
762
- this.error('Not logged in. Run: aderis auth login');
760
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
761
+ if (!hasCredentials(context))
762
+ this.error(missingCredentialsMessage(context));
763
763
  // Get data from file, --data, or --set flags
764
764
  let items = [];
765
765
  if (flags.file) {
@@ -1,29 +1,32 @@
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 = ["notes", "contacts", "contacts-notes"];
9
- const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "merge", "activate", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-contacts", "create-contacts", "get-contacts", "update-contacts", "delete-contacts", "list-contacts-notes", "create-contacts-notes", "get-contacts-notes", "update-contacts-notes", "delete-contacts-notes"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
9
+ const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "merge", "activate", "get-favicon", "update-favicon", "trigger-favicon-backfill", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-contacts", "create-contacts", "get-contacts", "update-contacts", "delete-contacts", "list-contacts-notes", "create-contacts-notes", "get-contacts-notes", "update-contacts-notes", "delete-contacts-notes"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
11
  'list': { method: 'GET', path: '/v1/business-partners', summary: 'Get all business partners with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
12
- 'create': { method: 'POST', path: '/v1/business-partners', summary: 'Create a new business partner', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salesOrderRecipientCc', 'salesOrderRecipientBcc', 'salesInvoiceRecipientCc', 'salesInvoiceRecipientBcc', 'deliveryNoteRecipientCc', 'deliveryNoteRecipientBcc', 'contacts', 'tagIds', 'salesEarlyPaymentDiscounts', 'purchaseEarlyPaymentDiscounts', 'notes'] },
12
+ 'create': { method: 'POST', path: '/v1/business-partners', summary: 'Create a new business partner', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salesOrderRecipientCc', 'salesOrderRecipientBcc', 'salesInvoiceRecipientCc', 'salesInvoiceRecipientBcc', 'deliveryNoteRecipientCc', 'deliveryNoteRecipientBcc', 'contacts', 'tagIds', 'tagCodes', 'salesEarlyPaymentDiscounts', 'purchaseEarlyPaymentDiscounts', 'notes'] },
13
13
  'get': { method: 'GET', path: '/v1/business-partners/{id}', summary: 'Get a business partner by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
14
- 'update': { method: 'PATCH', path: '/v1/business-partners/{id}', summary: 'Update a business partner', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salesOrderRecipientCc', 'salesOrderRecipientBcc', 'salesInvoiceRecipientCc', 'salesInvoiceRecipientBcc', 'deliveryNoteRecipientCc', 'deliveryNoteRecipientBcc', 'contacts', 'tagIds', 'salesEarlyPaymentDiscounts', 'purchaseEarlyPaymentDiscounts', 'notes'] },
14
+ 'update': { method: 'PATCH', path: '/v1/business-partners/{id}', summary: 'Update a business partner', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salesOrderRecipientCc', 'salesOrderRecipientBcc', 'salesInvoiceRecipientCc', 'salesInvoiceRecipientBcc', 'deliveryNoteRecipientCc', 'deliveryNoteRecipientBcc', 'contacts', 'tagIds', 'tagCodes', 'salesEarlyPaymentDiscounts', 'purchaseEarlyPaymentDiscounts', 'notes'] },
15
15
  'delete': { method: 'DELETE', path: '/v1/business-partners/{id}', summary: 'Delete a business partner', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
16
16
  'merge': { method: 'POST', path: '/v1/business-partners/{id}/merge', summary: 'Merge a business partner into another', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
17
17
  'activate': { method: 'POST', path: '/v1/business-partners/{id}/activate', summary: 'Activate a business partner', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
18
+ 'get-favicon': { method: 'GET', path: '/v1/business-partners/{id}/favicon', summary: 'Download a business partner\'s stored favicon', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
19
+ 'update-favicon': { method: 'PUT', path: '/v1/business-partners/{id}/favicon', summary: 'Manually upload a business partner\'s favicon', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
20
+ 'trigger-favicon-backfill': { method: 'POST', path: '/v1/business-partners/favicon-backfill', summary: 'Fetch favicons for every business partner in this tenant that has none yet', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
18
21
  'list-notes': { method: 'GET', path: '/v1/business-partners/{businessPartnerId}/notes', summary: 'Get all notes for a business partner', pathParams: ['businessPartnerId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
19
22
  'create-notes': { method: 'POST', path: '/v1/business-partners/{businessPartnerId}/notes', summary: 'Create a note for a business partner', pathParams: ['businessPartnerId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
20
23
  'get-notes': { method: 'GET', path: '/v1/business-partners/{businessPartnerId}/notes/{noteId}', summary: 'Get a specific note for a business partner', pathParams: ['businessPartnerId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
21
24
  'update-notes': { method: 'PATCH', path: '/v1/business-partners/{businessPartnerId}/notes/{noteId}', summary: 'Update a note for a business partner', pathParams: ['businessPartnerId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
22
25
  'delete-notes': { method: 'DELETE', path: '/v1/business-partners/{businessPartnerId}/notes/{noteId}', summary: 'Delete a note from a business partner', pathParams: ['businessPartnerId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
23
26
  'list-contacts': { method: 'GET', path: '/v1/business-partners/{businessPartnerId}/contacts', summary: 'Get all contacts for a business partner with pagination', pathParams: ['businessPartnerId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
24
- 'create-contacts': { method: 'POST', path: '/v1/business-partners/{businessPartnerId}/contacts', summary: 'Create a new contact for a business partner', pathParams: ['businessPartnerId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['notes'] },
27
+ 'create-contacts': { method: 'POST', path: '/v1/business-partners/{businessPartnerId}/contacts', summary: 'Create a new contact for a business partner', pathParams: ['businessPartnerId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'notes'] },
25
28
  'get-contacts': { method: 'GET', path: '/v1/business-partners/{businessPartnerId}/contacts/{id}', summary: 'Get a contact by ID', pathParams: ['id', 'businessPartnerId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
26
- 'update-contacts': { method: 'PATCH', path: '/v1/business-partners/{businessPartnerId}/contacts/{id}', summary: 'Update a contact', pathParams: ['id', 'businessPartnerId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['notes'] },
29
+ 'update-contacts': { method: 'PATCH', path: '/v1/business-partners/{businessPartnerId}/contacts/{id}', summary: 'Update a contact', pathParams: ['id', 'businessPartnerId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'notes'] },
27
30
  'delete-contacts': { method: 'DELETE', path: '/v1/business-partners/{businessPartnerId}/contacts/{id}', summary: 'Delete a contact', pathParams: ['id', 'businessPartnerId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
28
31
  'list-contacts-notes': { method: 'GET', path: '/v1/business-partners/contacts/{contactId}/notes', summary: 'Get all notes for a contact', pathParams: ['contactId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
29
32
  'create-contacts-notes': { method: 'POST', path: '/v1/business-partners/contacts/{contactId}/notes', summary: 'Create a note for a contact', pathParams: ['contactId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
@@ -46,7 +49,7 @@ export default class BusinessPartners extends Command {
46
49
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
47
50
  };
48
51
  static flags = {
49
- context: Flags.string({ char: 'c', description: 'Context to use' }),
52
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
50
53
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
51
54
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
52
55
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -120,9 +123,9 @@ export default class BusinessPartners extends Command {
120
123
  }
121
124
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
122
125
  if (!context)
123
- this.error('No context configured. Run: aderis auth login --new');
124
- if (!context.tokens)
125
- this.error('Not logged in. Run: aderis auth login');
126
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
127
+ if (!hasCredentials(context))
128
+ this.error(missingCredentialsMessage(context));
126
129
  const queryParams = {};
127
130
  if (flags.page)
128
131
  queryParams.page = flags.page;
@@ -365,9 +368,9 @@ export default class BusinessPartners extends Command {
365
368
  async runImport(flags) {
366
369
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
367
370
  if (!context)
368
- this.error('No context configured. Run: aderis auth login --new');
369
- if (!context.tokens)
370
- this.error('Not logged in. Run: aderis auth login');
371
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
372
+ if (!hasCredentials(context))
373
+ this.error(missingCredentialsMessage(context));
371
374
  // Get data from file, --data, or --set flags
372
375
  let items = [];
373
376
  if (flags.file) {
@@ -577,9 +580,9 @@ export default class BusinessPartners extends Command {
577
580
  async runExport(flags) {
578
581
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
579
582
  if (!context)
580
- this.error('No context configured. Run: aderis auth login --new');
581
- if (!context.tokens)
582
- this.error('Not logged in. Run: aderis auth login');
583
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
584
+ if (!hasCredentials(context))
585
+ this.error(missingCredentialsMessage(context));
583
586
  const listEndpoint = ENDPOINTS['list'];
584
587
  if (!listEndpoint)
585
588
  this.error('Export requires a list endpoint');
@@ -671,9 +674,9 @@ export default class BusinessPartners extends Command {
671
674
  async runCamt053Import(flags) {
672
675
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
673
676
  if (!context)
674
- this.error('No context configured. Run: aderis auth login --new');
675
- if (!context.tokens)
676
- this.error('Not logged in. Run: aderis auth login');
677
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
678
+ if (!hasCredentials(context))
679
+ this.error(missingCredentialsMessage(context));
677
680
  if (!flags.file) {
678
681
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
679
682
  }
@@ -761,9 +764,9 @@ export default class BusinessPartners extends Command {
761
764
  }
762
765
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
763
766
  if (!context)
764
- this.error('No context configured. Run: aderis auth login --new');
765
- if (!context.tokens)
766
- this.error('Not logged in. Run: aderis auth login');
767
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
768
+ if (!hasCredentials(context))
769
+ this.error(missingCredentialsMessage(context));
767
770
  // Get data from file, --data, or --set flags
768
771
  let items = [];
769
772
  if (flags.file) {
@@ -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 = false;
@@ -28,7 +28,7 @@ export default class Chat extends Command {
28
28
  id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
29
29
  };
30
30
  static flags = {
31
- context: Flags.string({ char: 'c', description: 'Context to use' }),
31
+ context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
32
32
  output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
33
33
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
34
34
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
@@ -100,9 +100,9 @@ export default class Chat extends Command {
100
100
  }
101
101
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
102
102
  if (!context)
103
- this.error('No context configured. Run: aderis auth login --new');
104
- if (!context.tokens)
105
- this.error('Not logged in. Run: aderis auth login');
103
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
104
+ if (!hasCredentials(context))
105
+ this.error(missingCredentialsMessage(context));
106
106
  const queryParams = {};
107
107
  if (flags.page)
108
108
  queryParams.page = flags.page;
@@ -345,9 +345,9 @@ export default class Chat extends Command {
345
345
  async runImport(flags) {
346
346
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
347
347
  if (!context)
348
- this.error('No context configured. Run: aderis auth login --new');
349
- if (!context.tokens)
350
- 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));
351
351
  // Get data from file, --data, or --set flags
352
352
  let items = [];
353
353
  if (flags.file) {
@@ -557,9 +557,9 @@ export default class Chat extends Command {
557
557
  async runExport(flags) {
558
558
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
559
559
  if (!context)
560
- this.error('No context configured. Run: aderis auth login --new');
561
- if (!context.tokens)
562
- 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));
563
563
  const listEndpoint = ENDPOINTS['list'];
564
564
  if (!listEndpoint)
565
565
  this.error('Export requires a list endpoint');
@@ -651,9 +651,9 @@ export default class Chat extends Command {
651
651
  async runCamt053Import(flags) {
652
652
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
653
653
  if (!context)
654
- this.error('No context configured. Run: aderis auth login --new');
655
- if (!context.tokens)
656
- this.error('Not logged in. Run: aderis auth login');
654
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
655
+ if (!hasCredentials(context))
656
+ this.error(missingCredentialsMessage(context));
657
657
  if (!flags.file) {
658
658
  this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
659
659
  }
@@ -741,9 +741,9 @@ export default class Chat extends Command {
741
741
  }
742
742
  const context = flags.context ? getContext(flags.context) : getCurrentContext();
743
743
  if (!context)
744
- this.error('No context configured. Run: aderis auth login --new');
745
- if (!context.tokens)
746
- this.error('Not logged in. Run: aderis auth login');
744
+ this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
745
+ if (!hasCredentials(context))
746
+ this.error(missingCredentialsMessage(context));
747
747
  // Get data from file, --data, or --set flags
748
748
  let items = [];
749
749
  if (flags.file) {
@@ -3,6 +3,8 @@ export default class ConfigAdd extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'auth-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
8
  'client-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
9
  'client-secret': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
10
  environment: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -10,4 +12,11 @@ export default class ConfigAdd extends Command {
10
12
  name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
13
  };
12
14
  run(): Promise<void>;
15
+ /**
16
+ * Decide the auth type from flags, falling back to a prompt only when the
17
+ * invocation leaves it genuinely open. `--client-id`/`--client-secret` imply
18
+ * OAuth, and a non-TTY stdin means we must not block on a prompt — both keep
19
+ * scripted `config add` calls working exactly as before API-key support.
20
+ */
21
+ private resolveAuthType;
13
22
  }
@@ -1,14 +1,22 @@
1
- import { confirm, input, select } from '@inquirer/prompts';
1
+ import { confirm, input, password, select } from '@inquirer/prompts';
2
2
  import { Command, Flags } from '@oclif/core';
3
- import { addContext, DEFAULT_CLIENT_IDS, getContext } from '../../lib/config.js';
3
+ import { addContext, DEFAULT_CLIENT_IDS, getContext, } from '../../lib/config.js';
4
4
  export default class ConfigAdd extends Command {
5
5
  static description = 'Add a new context configuration';
6
6
  static examples = [
7
7
  '<%= config.bin %> config add',
8
8
  '<%= config.bin %> config add --name my-client --environment live',
9
9
  '<%= config.bin %> config add --name dev-context --environment dev --client-id abc123',
10
+ '<%= config.bin %> config add --name ci --environment live --auth-type api-key --api-key <key>',
10
11
  ];
11
12
  static flags = {
13
+ 'api-key': Flags.string({
14
+ description: 'API key to store (only with --auth-type api-key). Prompted for if omitted.',
15
+ }),
16
+ 'auth-type': Flags.string({
17
+ description: 'How to authenticate: oauth2 (browser login) or api-key (x-api-key header)',
18
+ options: ['oauth2', 'api-key'],
19
+ }),
12
20
  'client-id': Flags.string({
13
21
  description: 'OAuth client ID (uses default if not specified)',
14
22
  }),
@@ -71,6 +79,35 @@ export default class ConfigAdd extends Command {
71
79
  message: 'Environment:',
72
80
  }));
73
81
  }
82
+ // Get auth type. The flag uses the hyphenated `api-key` spelling; the stored
83
+ // value is the `api_key` AuthType. Only prompt when nothing in the invocation
84
+ // implies an answer, so existing non-interactive usage keeps working.
85
+ const authType = await this.resolveAuthType(flags);
86
+ if (authType === 'api_key') {
87
+ if (flags['client-id'] || flags['client-secret']) {
88
+ this.error('--client-id and --client-secret only apply to OAuth contexts; omit them with --auth-type api-key.');
89
+ }
90
+ const apiKey = flags['api-key'] ??
91
+ (await password({
92
+ mask: true,
93
+ message: 'API key:',
94
+ validate: (value) => (value.trim() ? true : 'API key is required'),
95
+ }));
96
+ if (!apiKey.trim()) {
97
+ this.error('API key is required');
98
+ }
99
+ addContext({
100
+ api_key: apiKey.trim(),
101
+ auth_type: 'api_key',
102
+ environment,
103
+ name,
104
+ });
105
+ this.log(`\nContext "${name}" created successfully.`);
106
+ this.log(`Environment: ${environment}`);
107
+ this.log(`Auth: API key (sent as x-api-key)`);
108
+ this.log(`\nNo login needed — the CLI is ready to use.`);
109
+ return;
110
+ }
74
111
  // Get client ID
75
112
  let clientId = flags['client-id'];
76
113
  if (!clientId) {
@@ -107,6 +144,7 @@ export default class ConfigAdd extends Command {
107
144
  }
108
145
  // Create the context
109
146
  addContext({
147
+ auth_type: 'oauth2',
110
148
  client_id: clientId,
111
149
  client_secret: clientSecret,
112
150
  environment,
@@ -117,4 +155,28 @@ export default class ConfigAdd extends Command {
117
155
  this.log(`Client ID: ${clientId}`);
118
156
  this.log(`\nTo authenticate, run: aderis auth login`);
119
157
  }
158
+ /**
159
+ * Decide the auth type from flags, falling back to a prompt only when the
160
+ * invocation leaves it genuinely open. `--client-id`/`--client-secret` imply
161
+ * OAuth, and a non-TTY stdin means we must not block on a prompt — both keep
162
+ * scripted `config add` calls working exactly as before API-key support.
163
+ */
164
+ async resolveAuthType(flags) {
165
+ if (flags['auth-type']) {
166
+ return flags['auth-type'] === 'api-key' ? 'api_key' : 'oauth2';
167
+ }
168
+ // A key without --auth-type unambiguously means API-key auth.
169
+ if (flags['api-key'])
170
+ return 'api_key';
171
+ // OAuth-only flags, or a non-interactive shell: keep the historical default.
172
+ if (flags['client-id'] || flags['client-secret'] || !process.stdin.isTTY)
173
+ return 'oauth2';
174
+ return select({
175
+ choices: [
176
+ { description: 'Browser login via OAuth2 (PKCE)', name: 'oauth2', value: 'oauth2' },
177
+ { description: 'Long-lived API key, sent as the x-api-key header', name: 'api-key', value: 'api_key' },
178
+ ],
179
+ message: 'Authentication method:',
180
+ });
181
+ }
120
182
  }
@@ -1,5 +1,5 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
- import { getCurrentContext } from '../../lib/config.js';
2
+ import { getAuthType, getCurrentContext, hasCredentials } from '../../lib/config.js';
3
3
  export default class ConfigCurrent extends Command {
4
4
  static description = 'Show the current context';
5
5
  static examples = ['<%= config.bin %> config current', '<%= config.bin %> config current --json'];
@@ -24,7 +24,8 @@ export default class ConfigCurrent extends Command {
24
24
  }
25
25
  if (flags.json) {
26
26
  this.log(JSON.stringify({
27
- authenticated: Boolean(context.tokens),
27
+ auth_type: getAuthType(context),
28
+ authenticated: hasCredentials(context),
28
29
  client_id: context.client_id,
29
30
  environment: context.environment,
30
31
  name: context.name,
@@ -32,12 +33,16 @@ export default class ConfigCurrent extends Command {
32
33
  }, null, 2));
33
34
  return;
34
35
  }
36
+ const authType = getAuthType(context);
35
37
  this.log(`Current context: ${context.name}`);
36
38
  this.log(`Environment: ${context.environment}`);
37
- this.log(`Client ID: ${context.client_id}`);
39
+ this.log(`Auth: ${authType === 'api_key' ? 'API key (x-api-key)' : 'oauth2'}`);
40
+ if (authType === 'oauth2') {
41
+ this.log(`Client ID: ${context.client_id ?? 'N/A'}`);
42
+ }
38
43
  if (context.tenant_id) {
39
44
  this.log(`Tenant ID: ${context.tenant_id}`);
40
45
  }
41
- this.log(`Authenticated: ${context.tokens ? 'Yes' : 'No'}`);
46
+ this.log(`Authenticated: ${hasCredentials(context) ? 'Yes' : 'No'}`);
42
47
  }
43
48
  }
@@ -1,5 +1,5 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
- import { getCurrentContext, listContexts } from '../../lib/config.js';
2
+ import { getAuthType, getCurrentContext, hasCredentials, listContexts } from '../../lib/config.js';
3
3
  export default class ConfigList extends Command {
4
4
  static description = 'List all configured contexts';
5
5
  static examples = ['<%= config.bin %> config list', '<%= config.bin %> config list --json'];
@@ -16,7 +16,8 @@ export default class ConfigList extends Command {
16
16
  if (flags.json) {
17
17
  this.log(JSON.stringify({
18
18
  contexts: contexts.map((c) => ({
19
- authenticated: Boolean(c.tokens),
19
+ auth_type: getAuthType(c),
20
+ authenticated: hasCredentials(c),
20
21
  client_id: c.client_id,
21
22
  environment: c.environment,
22
23
  name: c.name,
@@ -35,10 +36,14 @@ export default class ConfigList extends Command {
35
36
  for (const ctx of contexts) {
36
37
  const isCurrent = current?.name === ctx.name;
37
38
  const marker = isCurrent ? '*' : ' ';
38
- const auth = ctx.tokens ? '✓' : '✗';
39
+ const auth = hasCredentials(ctx) ? '✓' : '✗';
40
+ const authType = getAuthType(ctx);
39
41
  this.log(`${marker} ${ctx.name}`);
40
42
  this.log(` Environment: ${ctx.environment}`);
41
- this.log(` Client ID: ${ctx.client_id}`);
43
+ this.log(` Auth: ${authType === 'api_key' ? 'API key (x-api-key)' : 'oauth2'}`);
44
+ if (authType === 'oauth2') {
45
+ this.log(` Client ID: ${ctx.client_id ?? 'N/A'}`);
46
+ }
42
47
  this.log(` Authenticated: ${auth}`);
43
48
  if (ctx.tenant_id) {
44
49
  this.log(` Tenant ID: ${ctx.tenant_id}`);
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ConfigSetApiKey extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ context: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,49 @@
1
+ import { password } from '@inquirer/prompts';
2
+ import { Command, Flags } from '@oclif/core';
3
+ import { getAuthType, getContext, getCurrentContext, maskApiKey, saveApiKey } from '../../lib/config.js';
4
+ export default class ConfigSetApiKey extends Command {
5
+ static description = 'Set or rotate the API key for a context (switches it to api-key auth)';
6
+ static examples = [
7
+ '<%= config.bin %> config set-api-key',
8
+ '<%= config.bin %> config set-api-key --context ci',
9
+ '<%= config.bin %> config set-api-key --context ci --api-key <key>',
10
+ ];
11
+ static flags = {
12
+ 'api-key': Flags.string({
13
+ description: 'The API key. Prompted for (hidden) if omitted.',
14
+ }),
15
+ context: Flags.string({
16
+ aliases: ['config'],
17
+ char: 'c',
18
+ description: 'Context to update, overriding the current context (alias: --config)',
19
+ env: 'ADERIS_CONTEXT',
20
+ }),
21
+ };
22
+ async run() {
23
+ const { flags } = await this.parse(ConfigSetApiKey);
24
+ const contextName = flags.context ?? getCurrentContext()?.name;
25
+ if (!contextName) {
26
+ this.error('No context configured. Run: aderis config add');
27
+ }
28
+ const context = getContext(contextName);
29
+ if (!context) {
30
+ this.error(`Context "${contextName}" not found.`);
31
+ }
32
+ const apiKey = flags['api-key'] ??
33
+ (await password({
34
+ mask: true,
35
+ message: `API key for "${contextName}":`,
36
+ validate: (value) => (value.trim() ? true : 'API key is required'),
37
+ }));
38
+ if (!apiKey.trim()) {
39
+ this.error('API key is required');
40
+ }
41
+ const wasOAuth = getAuthType(context) === 'oauth2';
42
+ saveApiKey(contextName, apiKey.trim());
43
+ this.log(`API key set for context "${contextName}" (${maskApiKey(apiKey.trim())})`);
44
+ if (wasOAuth) {
45
+ this.log('Auth method switched from oauth2 to api-key; requests now send the x-api-key header.');
46
+ this.log(`Any stored OAuth tokens are kept but unused. Remove them with: aderis auth logout --context ${contextName}`);
47
+ }
48
+ }
49
+ }
@@ -1,6 +1,6 @@
1
1
  import { select } from '@inquirer/prompts';
2
2
  import { Args, Command } from '@oclif/core';
3
- import { getContext, listContexts, setCurrentContext } from '../../lib/config.js';
3
+ import { getContext, hasCredentials, listContexts, missingCredentialsMessage, setCurrentContext, } from '../../lib/config.js';
4
4
  export default class ConfigUse extends Command {
5
5
  static args = {
6
6
  name: Args.string({
@@ -32,9 +32,8 @@ export default class ConfigUse extends Command {
32
32
  }
33
33
  setCurrentContext(contextName);
34
34
  this.log(`Switched to context "${contextName}" (${context.environment})`);
35
- if (!context.tokens) {
36
- this.log(`\nNote: You are not logged in to this context.`);
37
- this.log(`Run: aderis auth login`);
35
+ if (!hasCredentials(context)) {
36
+ this.log(`\nNote: ${missingCredentialsMessage(context)}`);
38
37
  }
39
38
  }
40
39
  }