@aderis-erp/cli 1.1.4 → 1.1.5

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 (85) hide show
  1. package/dist/commands/access-groups.js +42 -8
  2. package/dist/commands/account-transaction-allocations.js +38 -4
  3. package/dist/commands/account-transactions.d.ts +2 -0
  4. package/dist/commands/account-transactions.js +68 -32
  5. package/dist/commands/accounts.js +46 -12
  6. package/dist/commands/agent-platform.js +37 -3
  7. package/dist/commands/agent-runs.js +49 -15
  8. package/dist/commands/analytics.js +39 -5
  9. package/dist/commands/api-clients.js +47 -13
  10. package/dist/commands/api-keys.js +40 -6
  11. package/dist/commands/async-jobs.js +45 -11
  12. package/dist/commands/audit-log.js +38 -4
  13. package/dist/commands/bank-accounts.js +54 -20
  14. package/dist/commands/bank-integrations.js +56 -22
  15. package/dist/commands/business-partners.js +77 -43
  16. package/dist/commands/chat.js +43 -9
  17. package/dist/commands/credit-card-accounts.js +57 -23
  18. package/dist/commands/currencies.js +37 -3
  19. package/dist/commands/custody-accounts.js +41 -7
  20. package/dist/commands/dashboards.js +52 -18
  21. package/dist/commands/delivery-notes.js +69 -35
  22. package/dist/commands/document-checksums.d.ts +1 -0
  23. package/dist/commands/document-checksums.js +38 -3
  24. package/dist/commands/dunning-configs.js +41 -7
  25. package/dist/commands/dunning-reminders.js +44 -10
  26. package/dist/commands/dunning-runs.js +37 -3
  27. package/dist/commands/email-transactions.js +39 -5
  28. package/dist/commands/employees.js +77 -43
  29. package/dist/commands/entity-configurations.js +39 -5
  30. package/dist/commands/event-mapping-rules.js +37 -3
  31. package/dist/commands/event-processor-mappings.js +41 -7
  32. package/dist/commands/files.js +48 -14
  33. package/dist/commands/financial-instrument-transactions.d.ts +56 -0
  34. package/dist/commands/financial-instrument-transactions.js +1369 -0
  35. package/dist/commands/financial-instrument-types.js +41 -7
  36. package/dist/commands/financial-instruments.js +41 -7
  37. package/dist/commands/health.js +41 -7
  38. package/dist/commands/imports.js +39 -5
  39. package/dist/commands/inbox.js +42 -8
  40. package/dist/commands/integrations.js +39 -5
  41. package/dist/commands/item-categories.js +41 -7
  42. package/dist/commands/items.js +42 -8
  43. package/dist/commands/journal-lines.js +37 -3
  44. package/dist/commands/journals.js +61 -27
  45. package/dist/commands/kpis.js +44 -10
  46. package/dist/commands/ledgers.js +83 -49
  47. package/dist/commands/my-account.js +54 -20
  48. package/dist/commands/notifications.js +37 -3
  49. package/dist/commands/office-locations.js +41 -7
  50. package/dist/commands/payment-methods.js +46 -12
  51. package/dist/commands/payment-runs.js +54 -20
  52. package/dist/commands/payment-terms.js +42 -8
  53. package/dist/commands/payments.js +43 -9
  54. package/dist/commands/payroll-runs.js +71 -37
  55. package/dist/commands/permissions.js +37 -3
  56. package/dist/commands/price-lists.js +48 -14
  57. package/dist/commands/processor-mappings.js +40 -6
  58. package/dist/commands/processors.js +46 -12
  59. package/dist/commands/purchase-order-types.js +41 -7
  60. package/dist/commands/purchase-orders.js +60 -26
  61. package/dist/commands/reports.js +43 -8
  62. package/dist/commands/roles.js +40 -6
  63. package/dist/commands/salary-types.js +41 -7
  64. package/dist/commands/sales-invoices.js +85 -51
  65. package/dist/commands/sales-orders.js +69 -35
  66. package/dist/commands/saved-views.js +39 -5
  67. package/dist/commands/scheduled-processor-mappings.js +41 -7
  68. package/dist/commands/schedules.js +45 -11
  69. package/dist/commands/settings.js +38 -4
  70. package/dist/commands/tags.js +42 -8
  71. package/dist/commands/tax-codes.js +44 -10
  72. package/dist/commands/tax-matrix.js +41 -7
  73. package/dist/commands/templates.js +49 -15
  74. package/dist/commands/tenants.js +44 -10
  75. package/dist/commands/tools.js +38 -4
  76. package/dist/commands/users.js +58 -24
  77. package/dist/commands/vat-declarations.js +61 -27
  78. package/dist/commands/vendor-invoices.js +81 -47
  79. package/dist/commands/webhooks.js +43 -9
  80. package/dist/commands/work-summary.js +37 -3
  81. package/dist/lib/paths.d.ts +8 -0
  82. package/dist/lib/paths.js +16 -0
  83. package/dist/lib/production-guard.js +2 -0
  84. package/oclif.manifest.json +199 -2
  85. package/package.json +9 -3
@@ -1,6 +1,7 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
3
3
  import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
4
+ import { resolveUserPath } from '../lib/paths.js';
4
5
  // 'upsert' loops create/update from the CLI, one request per record. It exists
5
6
  // only where the API has no import endpoint of its own; those keep the name
6
7
  // 'import' and are listed in SERVER_IMPORTS.
@@ -11,47 +12,47 @@ const SERVER_IMPORTS = [];
11
12
  const NESTED_UPSERTS = ["salaries", "payments", "notes", "files", "salary-payment-files", "salary-parts"];
12
13
  const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "get-salaries", "create-salaries", "get-salaries-salaryId", "update-salaries", "delete-salaries", "get-payments", "create-payments", "get-payments-paymentId", "update-payments", "delete-payments", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-salary-payment-files", "create-salary-payment-files", "get-salary-payment-files", "update-salary-payment-files", "delete-salary-payment-files", "download-salary-payment-files", "list-salary-parts", "create-salary-parts", "get-salary-parts", "update-salary-parts", "delete-salary-parts", "salary-parts-changes", "update-salary-parts-values", "delete-salary-parts-values", "month-overview"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'list': { method: 'GET', path: '/v1/employees', summary: 'Get all employees with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
- 'create': { method: 'POST', path: '/v1/employees', summary: 'Create a new employee', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['beneficiaries', 'children', 'salaries', 'files', 'notes', 'tagIds', 'tagCodes'] },
16
- 'get': { method: 'GET', path: '/v1/employees/{id}', summary: 'Get an employee by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
17
- 'update': { method: 'PATCH', path: '/v1/employees/{id}', summary: 'Update an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['beneficiaries', 'children', 'salaries', 'files', 'notes', 'tagIds', 'tagCodes'] },
18
- 'delete': { method: 'DELETE', path: '/v1/employees/{id}', summary: 'Delete an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
19
- 'get-salaries': { method: 'GET', path: '/v1/employees/{id}/salaries', summary: 'Get all salaries for an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
20
- 'create-salaries': { method: 'POST', path: '/v1/employees/{id}/salaries', summary: 'Add a salary configuration to an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
21
- 'get-salaries-salaryId': { method: 'GET', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Get a specific salary by ID', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
22
- 'update-salaries': { method: 'PATCH', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Update a salary configuration', pathParams: ['id', 'salaryId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
23
- 'delete-salaries': { method: 'DELETE', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Delete a salary configuration', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
24
- 'get-payments': { method: 'GET', path: '/v1/employees/{id}/payments', summary: 'Get all payments for an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
25
- 'create-payments': { method: 'POST', path: '/v1/employees/{id}/payments', summary: 'Add a payment to an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salaryComponents', 'files'] },
26
- 'get-payments-paymentId': { method: 'GET', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Get a specific payment by ID', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
27
- 'update-payments': { method: 'PATCH', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Update a payment', pathParams: ['id', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salaryComponents', 'files'] },
28
- 'delete-payments': { method: 'DELETE', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Delete a payment', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
29
- 'list-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes', summary: 'Get all notes for an employee', pathParams: ['employeeId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
30
- 'create-notes': { method: 'POST', path: '/v1/employees/{employeeId}/notes', summary: 'Create a note for an employee', pathParams: ['employeeId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
31
- 'get-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Get a specific note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
32
- 'update-notes': { method: 'PATCH', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Update a note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
33
- 'delete-notes': { method: 'DELETE', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Delete a note from an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
34
- 'list-files': { method: 'GET', path: '/v1/employees/{parentId}/files', summary: 'List file attachments for a employee', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
35
- 'create-files': { method: 'POST', path: '/v1/employees/{parentId}/files', summary: 'Upload a file attachment to a employee', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
36
- 'get-files': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Get a file attachment for a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
37
- 'update-files': { method: 'PATCH', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a employee', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
38
- 'delete-files': { method: 'DELETE', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
39
- 'files-download': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
40
- 'list-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files', summary: 'List file attachments for a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
41
- 'create-salary-payment-files': { method: 'POST', path: '/v1/salary-payments/{parentId}/files', summary: 'Upload a file attachment to a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
42
- 'get-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Get a file attachment for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
43
- 'update-salary-payment-files': { method: 'PATCH', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
44
- 'delete-salary-payment-files': { method: 'DELETE', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
45
- 'download-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
46
- 'list-salary-parts': { method: 'GET', path: '/v1/employees/{employeeId}/salary-parts', summary: 'List an employee\'s salary parts with their monthly values', pathParams: ['employeeId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
47
- 'create-salary-parts': { method: 'POST', path: '/v1/employees/{employeeId}/salary-parts', summary: 'Create a salary part', pathParams: ['employeeId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
48
- 'get-salary-parts': { method: 'GET', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}', summary: 'Get a salary part with its monthly values', pathParams: ['employeeId', 'salaryPartId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
49
- 'update-salary-parts': { method: 'PATCH', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}', summary: 'Change a salary part; an earlier end date removes later values', pathParams: ['employeeId', 'salaryPartId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
50
- 'delete-salary-parts': { method: 'DELETE', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}', summary: 'Delete a salary part and its monthly values', pathParams: ['employeeId', 'salaryPartId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
51
- 'salary-parts-changes': { method: 'POST', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}/changes', summary: 'Change a fixed salary part from a date', pathParams: ['employeeId', 'salaryPartId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
52
- 'update-salary-parts-values': { method: 'PUT', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}/values/{month}', summary: 'Create or replace the value of one month of a variable salary part', pathParams: ['employeeId', 'salaryPartId', 'month'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
53
- 'delete-salary-parts-values': { method: 'DELETE', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}/values/{month}', summary: 'Delete the value of one month of a variable salary part', pathParams: ['employeeId', 'salaryPartId', 'month'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
54
- 'month-overview': { method: 'GET', path: '/v1/salary-parts/month-overview', summary: 'Variable salary parts of all employees in one month', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'list': { method: 'GET', path: '/v1/employees', summary: 'Get all employees with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
16
+ 'create': { method: 'POST', path: '/v1/employees', summary: 'Create a new employee', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['beneficiaries', 'children', 'salaries', 'files', 'notes', 'tagIds', 'tagCodes'], bodyMediaType: '', specParams: [] },
17
+ 'get': { method: 'GET', path: '/v1/employees/{id}', summary: 'Get an employee by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
18
+ 'update': { method: 'PATCH', path: '/v1/employees/{id}', summary: 'Update an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['beneficiaries', 'children', 'salaries', 'files', 'notes', 'tagIds', 'tagCodes'], bodyMediaType: '', specParams: [] },
19
+ 'delete': { method: 'DELETE', path: '/v1/employees/{id}', summary: 'Delete an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
20
+ 'get-salaries': { method: 'GET', path: '/v1/employees/{id}/salaries', summary: 'Get all salaries for an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
21
+ 'create-salaries': { method: 'POST', path: '/v1/employees/{id}/salaries', summary: 'Add a salary configuration to an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
22
+ 'get-salaries-salaryId': { method: 'GET', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Get a specific salary by ID', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
23
+ 'update-salaries': { method: 'PATCH', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Update a salary configuration', pathParams: ['id', 'salaryId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
24
+ 'delete-salaries': { method: 'DELETE', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Delete a salary configuration', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
25
+ 'get-payments': { method: 'GET', path: '/v1/employees/{id}/payments', summary: 'Get all payments for an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
26
+ 'create-payments': { method: 'POST', path: '/v1/employees/{id}/payments', summary: 'Add a payment to an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salaryComponents', 'files'], bodyMediaType: '', specParams: [] },
27
+ 'get-payments-paymentId': { method: 'GET', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Get a specific payment by ID', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
28
+ 'update-payments': { method: 'PATCH', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Update a payment', pathParams: ['id', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salaryComponents', 'files'], bodyMediaType: '', specParams: [] },
29
+ 'delete-payments': { method: 'DELETE', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Delete a payment', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
30
+ 'list-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes', summary: 'Get all notes for an employee', pathParams: ['employeeId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
31
+ 'create-notes': { method: 'POST', path: '/v1/employees/{employeeId}/notes', summary: 'Create a note for an employee', pathParams: ['employeeId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
32
+ 'get-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Get a specific note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
33
+ 'update-notes': { method: 'PATCH', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Update a note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
34
+ 'delete-notes': { method: 'DELETE', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Delete a note from an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
35
+ 'list-files': { method: 'GET', path: '/v1/employees/{parentId}/files', summary: 'List file attachments for a employee', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
36
+ 'create-files': { method: 'POST', path: '/v1/employees/{parentId}/files', summary: 'Upload a file attachment to a employee', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
37
+ 'get-files': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Get a file attachment for a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
38
+ 'update-files': { method: 'PATCH', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a employee', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
39
+ 'delete-files': { method: 'DELETE', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
40
+ 'files-download': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
41
+ 'list-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files', summary: 'List file attachments for a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
42
+ 'create-salary-payment-files': { method: 'POST', path: '/v1/salary-payments/{parentId}/files', summary: 'Upload a file attachment to a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
43
+ 'get-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Get a file attachment for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
44
+ 'update-salary-payment-files': { method: 'PATCH', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
45
+ 'delete-salary-payment-files': { method: 'DELETE', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
46
+ 'download-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
47
+ 'list-salary-parts': { method: 'GET', path: '/v1/employees/{employeeId}/salary-parts', summary: 'List an employee\'s salary parts with their monthly values', pathParams: ['employeeId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
48
+ 'create-salary-parts': { method: 'POST', path: '/v1/employees/{employeeId}/salary-parts', summary: 'Create a salary part', pathParams: ['employeeId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
49
+ 'get-salary-parts': { method: 'GET', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}', summary: 'Get a salary part with its monthly values', pathParams: ['employeeId', 'salaryPartId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
50
+ 'update-salary-parts': { method: 'PATCH', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}', summary: 'Change a salary part; an earlier end date removes later values', pathParams: ['employeeId', 'salaryPartId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
51
+ 'delete-salary-parts': { method: 'DELETE', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}', summary: 'Delete a salary part and its monthly values', pathParams: ['employeeId', 'salaryPartId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
52
+ 'salary-parts-changes': { method: 'POST', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}/changes', summary: 'Change a fixed salary part from a date', pathParams: ['employeeId', 'salaryPartId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
53
+ 'update-salary-parts-values': { method: 'PUT', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}/values/{month}', summary: 'Create or replace the value of one month of a variable salary part', pathParams: ['employeeId', 'salaryPartId', 'month'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
54
+ 'delete-salary-parts-values': { method: 'DELETE', path: '/v1/employees/{employeeId}/salary-parts/{salaryPartId}/values/{month}', summary: 'Delete the value of one month of a variable salary part', pathParams: ['employeeId', 'salaryPartId', 'month'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
55
+ 'month-overview': { method: 'GET', path: '/v1/salary-parts/month-overview', summary: 'Variable salary parts of all employees in one month', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
55
56
  };
56
57
  export default class Employees extends Command {
57
58
  static description = 'Manage employees';
@@ -73,7 +74,7 @@ export default class Employees extends Command {
73
74
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
74
75
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
75
76
  data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
76
- file: Flags.string({ char: 'f', description: 'Read request body from file' }),
77
+ file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
77
78
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
78
79
  'salary-id': Flags.string({ description: 'Path param: salaryId' }),
79
80
  'payment-id': Flags.string({ description: 'Path param: paymentId' }),
@@ -210,6 +211,17 @@ export default class Employees extends Command {
210
211
  }
211
212
  }
212
213
  }
214
+ // Flags generated from this endpoint's own parameters. Raw-body endpoints
215
+ // have them: their body is not JSON, so --set cannot carry a value there.
216
+ for (const param of endpoint.specParams) {
217
+ const value = flags[param.flag];
218
+ if (value === undefined || value === '') {
219
+ if (param.required)
220
+ this.error(`Missing required flag: --${param.flag}`);
221
+ continue;
222
+ }
223
+ queryParams[param.name] = value;
224
+ }
213
225
  let data;
214
226
  if (endpoint.isMultipart) {
215
227
  if (!flags.file)
@@ -230,6 +242,25 @@ export default class Employees extends Command {
230
242
  }
231
243
  data = form;
232
244
  }
245
+ else if (endpoint.bodyMediaType) {
246
+ // The body is not JSON (today only application/x-ndjson): it is sent
247
+ // verbatim, under the content type the endpoint asks for.
248
+ if (flags.file) {
249
+ const fsModule = await import('node:fs');
250
+ try {
251
+ data = fsModule.readFileSync(flags.file, 'utf-8');
252
+ }
253
+ catch {
254
+ this.error(`Failed to read file: ${flags.file}`);
255
+ }
256
+ }
257
+ else if (flags.data) {
258
+ data = flags.data;
259
+ }
260
+ else {
261
+ this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
262
+ }
263
+ }
233
264
  else if (endpoint.hasBody) {
234
265
  // For binary-response endpoints, --file is the output path, not a JSON
235
266
  // input file — skip the read-as-body branch.
@@ -325,7 +356,7 @@ export default class Employees extends Command {
325
356
  process.stderr.write(`Body: [multipart/form-data file upload]\n`);
326
357
  }
327
358
  else if (data) {
328
- process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
359
+ process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
329
360
  }
330
361
  process.stderr.write('\n');
331
362
  }
@@ -339,6 +370,7 @@ export default class Employees extends Command {
339
370
  const binResponse = await apiRequest(endpoint.method, apiPath, {
340
371
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
341
372
  data,
373
+ contentType: endpoint.bodyMediaType || undefined,
342
374
  contextName: flags.context,
343
375
  binary: true,
344
376
  });
@@ -364,6 +396,7 @@ export default class Employees extends Command {
364
396
  const raw = await apiRequest(endpoint.method, apiPath, {
365
397
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
366
398
  data,
399
+ contentType: endpoint.bodyMediaType || undefined,
367
400
  contextName: flags.context,
368
401
  binary: true,
369
402
  });
@@ -384,6 +417,7 @@ export default class Employees extends Command {
384
417
  response = await apiRequest(endpoint.method, apiPath, {
385
418
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
386
419
  data,
420
+ contentType: endpoint.bodyMediaType || undefined,
387
421
  contextName: flags.context,
388
422
  acceptAny: true,
389
423
  });
@@ -1,6 +1,7 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
3
3
  import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
4
+ import { resolveUserPath } from '../lib/paths.js';
4
5
  // 'upsert' loops create/update from the CLI, one request per record. It exists
5
6
  // only where the API has no import endpoint of its own; those keep the name
6
7
  // 'import' and are listed in SERVER_IMPORTS.
@@ -11,9 +12,9 @@ const SERVER_IMPORTS = [];
11
12
  const NESTED_UPSERTS = [];
12
13
  const SUBCOMMANDS = [...["list-entities", "get-entities", "update-entities"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'list-entities': { method: 'GET', path: '/v1/config/entities', summary: 'List all entity-type configs', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
- 'get-entities': { method: 'GET', path: '/v1/config/entities/{entityType}', summary: 'Get one entity-type config', pathParams: ['entityType'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
16
- 'update-entities': { method: 'PATCH', path: '/v1/config/entities/{entityType}', summary: 'Adjust one entity-type config (admin only)', pathParams: ['entityType'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'list-entities': { method: 'GET', path: '/v1/config/entities', summary: 'List all entity-type configs', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
16
+ 'get-entities': { method: 'GET', path: '/v1/config/entities/{entityType}', summary: 'Get one entity-type config', pathParams: ['entityType'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
+ 'update-entities': { method: 'PATCH', path: '/v1/config/entities/{entityType}', summary: 'Adjust one entity-type config (admin only)', pathParams: ['entityType'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
18
  };
18
19
  export default class EntityConfigurations extends Command {
19
20
  static description = 'Manage entity configurations';
@@ -28,7 +29,7 @@ export default class EntityConfigurations extends Command {
28
29
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
29
30
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
30
31
  data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
31
- file: Flags.string({ char: 'f', description: 'Read request body from file' }),
32
+ file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
32
33
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
33
34
  page: Flags.string({ description: 'Page number' }),
34
35
  limit: Flags.string({ description: 'Items per page' }),
@@ -159,6 +160,17 @@ export default class EntityConfigurations extends Command {
159
160
  }
160
161
  }
161
162
  }
163
+ // Flags generated from this endpoint's own parameters. Raw-body endpoints
164
+ // have them: their body is not JSON, so --set cannot carry a value there.
165
+ for (const param of endpoint.specParams) {
166
+ const value = flags[param.flag];
167
+ if (value === undefined || value === '') {
168
+ if (param.required)
169
+ this.error(`Missing required flag: --${param.flag}`);
170
+ continue;
171
+ }
172
+ queryParams[param.name] = value;
173
+ }
162
174
  let data;
163
175
  if (endpoint.isMultipart) {
164
176
  if (!flags.file)
@@ -179,6 +191,25 @@ export default class EntityConfigurations extends Command {
179
191
  }
180
192
  data = form;
181
193
  }
194
+ else if (endpoint.bodyMediaType) {
195
+ // The body is not JSON (today only application/x-ndjson): it is sent
196
+ // verbatim, under the content type the endpoint asks for.
197
+ if (flags.file) {
198
+ const fsModule = await import('node:fs');
199
+ try {
200
+ data = fsModule.readFileSync(flags.file, 'utf-8');
201
+ }
202
+ catch {
203
+ this.error(`Failed to read file: ${flags.file}`);
204
+ }
205
+ }
206
+ else if (flags.data) {
207
+ data = flags.data;
208
+ }
209
+ else {
210
+ this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
211
+ }
212
+ }
182
213
  else if (endpoint.hasBody) {
183
214
  // For binary-response endpoints, --file is the output path, not a JSON
184
215
  // input file — skip the read-as-body branch.
@@ -274,7 +305,7 @@ export default class EntityConfigurations extends Command {
274
305
  process.stderr.write(`Body: [multipart/form-data file upload]\n`);
275
306
  }
276
307
  else if (data) {
277
- process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
308
+ process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
278
309
  }
279
310
  process.stderr.write('\n');
280
311
  }
@@ -288,6 +319,7 @@ export default class EntityConfigurations extends Command {
288
319
  const binResponse = await apiRequest(endpoint.method, apiPath, {
289
320
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
290
321
  data,
322
+ contentType: endpoint.bodyMediaType || undefined,
291
323
  contextName: flags.context,
292
324
  binary: true,
293
325
  });
@@ -313,6 +345,7 @@ export default class EntityConfigurations extends Command {
313
345
  const raw = await apiRequest(endpoint.method, apiPath, {
314
346
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
315
347
  data,
348
+ contentType: endpoint.bodyMediaType || undefined,
316
349
  contextName: flags.context,
317
350
  binary: true,
318
351
  });
@@ -333,6 +366,7 @@ export default class EntityConfigurations extends Command {
333
366
  response = await apiRequest(endpoint.method, apiPath, {
334
367
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
335
368
  data,
369
+ contentType: endpoint.bodyMediaType || undefined,
336
370
  contextName: flags.context,
337
371
  acceptAny: true,
338
372
  });
@@ -1,6 +1,7 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
3
3
  import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
4
+ import { resolveUserPath } from '../lib/paths.js';
4
5
  // 'upsert' loops create/update from the CLI, one request per record. It exists
5
6
  // only where the API has no import endpoint of its own; those keep the name
6
7
  // 'import' and are listed in SERVER_IMPORTS.
@@ -11,7 +12,7 @@ const SERVER_IMPORTS = [];
11
12
  const NESTED_UPSERTS = [];
12
13
  const SUBCOMMANDS = [...["fields"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'fields': { method: 'GET', path: '/v1/event-mapping-rules/fields', summary: 'List the fields an event mapping’s conditions may test', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'fields': { method: 'GET', path: '/v1/event-mapping-rules/fields', summary: 'List the fields an event mapping’s conditions may test', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
15
16
  };
16
17
  export default class EventMappingRules extends Command {
17
18
  static description = 'Manage event mapping rules';
@@ -26,7 +27,7 @@ export default class EventMappingRules extends Command {
26
27
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
27
28
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
28
29
  data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
29
- file: Flags.string({ char: 'f', description: 'Read request body from file' }),
30
+ file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
30
31
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
31
32
  page: Flags.string({ description: 'Page number' }),
32
33
  limit: Flags.string({ description: 'Items per page' }),
@@ -157,6 +158,17 @@ export default class EventMappingRules extends Command {
157
158
  }
158
159
  }
159
160
  }
161
+ // Flags generated from this endpoint's own parameters. Raw-body endpoints
162
+ // have them: their body is not JSON, so --set cannot carry a value there.
163
+ for (const param of endpoint.specParams) {
164
+ const value = flags[param.flag];
165
+ if (value === undefined || value === '') {
166
+ if (param.required)
167
+ this.error(`Missing required flag: --${param.flag}`);
168
+ continue;
169
+ }
170
+ queryParams[param.name] = value;
171
+ }
160
172
  let data;
161
173
  if (endpoint.isMultipart) {
162
174
  if (!flags.file)
@@ -177,6 +189,25 @@ export default class EventMappingRules extends Command {
177
189
  }
178
190
  data = form;
179
191
  }
192
+ else if (endpoint.bodyMediaType) {
193
+ // The body is not JSON (today only application/x-ndjson): it is sent
194
+ // verbatim, under the content type the endpoint asks for.
195
+ if (flags.file) {
196
+ const fsModule = await import('node:fs');
197
+ try {
198
+ data = fsModule.readFileSync(flags.file, 'utf-8');
199
+ }
200
+ catch {
201
+ this.error(`Failed to read file: ${flags.file}`);
202
+ }
203
+ }
204
+ else if (flags.data) {
205
+ data = flags.data;
206
+ }
207
+ else {
208
+ this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
209
+ }
210
+ }
180
211
  else if (endpoint.hasBody) {
181
212
  // For binary-response endpoints, --file is the output path, not a JSON
182
213
  // input file — skip the read-as-body branch.
@@ -272,7 +303,7 @@ export default class EventMappingRules extends Command {
272
303
  process.stderr.write(`Body: [multipart/form-data file upload]\n`);
273
304
  }
274
305
  else if (data) {
275
- process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
306
+ process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
276
307
  }
277
308
  process.stderr.write('\n');
278
309
  }
@@ -286,6 +317,7 @@ export default class EventMappingRules extends Command {
286
317
  const binResponse = await apiRequest(endpoint.method, apiPath, {
287
318
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
288
319
  data,
320
+ contentType: endpoint.bodyMediaType || undefined,
289
321
  contextName: flags.context,
290
322
  binary: true,
291
323
  });
@@ -311,6 +343,7 @@ export default class EventMappingRules extends Command {
311
343
  const raw = await apiRequest(endpoint.method, apiPath, {
312
344
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
313
345
  data,
346
+ contentType: endpoint.bodyMediaType || undefined,
314
347
  contextName: flags.context,
315
348
  binary: true,
316
349
  });
@@ -331,6 +364,7 @@ export default class EventMappingRules extends Command {
331
364
  response = await apiRequest(endpoint.method, apiPath, {
332
365
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
333
366
  data,
367
+ contentType: endpoint.bodyMediaType || undefined,
334
368
  contextName: flags.context,
335
369
  acceptAny: true,
336
370
  });
@@ -1,6 +1,7 @@
1
1
  import { Command, Args, Flags } from '@oclif/core';
2
2
  import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
3
3
  import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
4
+ import { resolveUserPath } from '../lib/paths.js';
4
5
  // 'upsert' loops create/update from the CLI, one request per record. It exists
5
6
  // only where the API has no import endpoint of its own; those keep the name
6
7
  // 'import' and are listed in SERVER_IMPORTS.
@@ -11,11 +12,11 @@ const SERVER_IMPORTS = [];
11
12
  const NESTED_UPSERTS = [];
12
13
  const SUBCOMMANDS = [...["events", "list", "create", "update", "delete"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'events': { method: 'GET', path: '/v1/event-processor-mappings/events', summary: 'List all valid event types', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
- 'list': { method: 'GET', path: '/v1/event-processor-mappings', summary: 'List all event type processor mappings for the tenant', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
16
- 'create': { method: 'POST', path: '/v1/event-processor-mappings', summary: 'Create event type processor mappings', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['eventTypes', 'includes'] },
17
- 'update': { method: 'PATCH', path: '/v1/event-processor-mappings/{id}', summary: 'Update an event type processor mapping', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['includes'] },
18
- 'delete': { method: 'DELETE', path: '/v1/event-processor-mappings/{id}', summary: 'Delete an event type processor mapping', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'events': { method: 'GET', path: '/v1/event-processor-mappings/events', summary: 'List all valid event types', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
16
+ 'list': { method: 'GET', path: '/v1/event-processor-mappings', summary: 'List all event type processor mappings for the tenant', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
+ 'create': { method: 'POST', path: '/v1/event-processor-mappings', summary: 'Create event type processor mappings', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['eventTypes', 'includes'], bodyMediaType: '', specParams: [] },
18
+ 'update': { method: 'PATCH', path: '/v1/event-processor-mappings/{id}', summary: 'Update an event type processor mapping', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['includes'], bodyMediaType: '', specParams: [] },
19
+ 'delete': { method: 'DELETE', path: '/v1/event-processor-mappings/{id}', summary: 'Delete an event type processor mapping', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
19
20
  };
20
21
  export default class EventProcessorMappings extends Command {
21
22
  static description = 'Manage event processor mappings';
@@ -36,7 +37,7 @@ export default class EventProcessorMappings extends Command {
36
37
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
37
38
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
38
39
  data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
39
- file: Flags.string({ char: 'f', description: 'Read request body from file' }),
40
+ file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
40
41
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
41
42
  page: Flags.string({ description: 'Page number' }),
42
43
  limit: Flags.string({ description: 'Items per page' }),
@@ -167,6 +168,17 @@ export default class EventProcessorMappings extends Command {
167
168
  }
168
169
  }
169
170
  }
171
+ // Flags generated from this endpoint's own parameters. Raw-body endpoints
172
+ // have them: their body is not JSON, so --set cannot carry a value there.
173
+ for (const param of endpoint.specParams) {
174
+ const value = flags[param.flag];
175
+ if (value === undefined || value === '') {
176
+ if (param.required)
177
+ this.error(`Missing required flag: --${param.flag}`);
178
+ continue;
179
+ }
180
+ queryParams[param.name] = value;
181
+ }
170
182
  let data;
171
183
  if (endpoint.isMultipart) {
172
184
  if (!flags.file)
@@ -187,6 +199,25 @@ export default class EventProcessorMappings extends Command {
187
199
  }
188
200
  data = form;
189
201
  }
202
+ else if (endpoint.bodyMediaType) {
203
+ // The body is not JSON (today only application/x-ndjson): it is sent
204
+ // verbatim, under the content type the endpoint asks for.
205
+ if (flags.file) {
206
+ const fsModule = await import('node:fs');
207
+ try {
208
+ data = fsModule.readFileSync(flags.file, 'utf-8');
209
+ }
210
+ catch {
211
+ this.error(`Failed to read file: ${flags.file}`);
212
+ }
213
+ }
214
+ else if (flags.data) {
215
+ data = flags.data;
216
+ }
217
+ else {
218
+ this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
219
+ }
220
+ }
190
221
  else if (endpoint.hasBody) {
191
222
  // For binary-response endpoints, --file is the output path, not a JSON
192
223
  // input file — skip the read-as-body branch.
@@ -282,7 +313,7 @@ export default class EventProcessorMappings extends Command {
282
313
  process.stderr.write(`Body: [multipart/form-data file upload]\n`);
283
314
  }
284
315
  else if (data) {
285
- process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
316
+ process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
286
317
  }
287
318
  process.stderr.write('\n');
288
319
  }
@@ -296,6 +327,7 @@ export default class EventProcessorMappings extends Command {
296
327
  const binResponse = await apiRequest(endpoint.method, apiPath, {
297
328
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
298
329
  data,
330
+ contentType: endpoint.bodyMediaType || undefined,
299
331
  contextName: flags.context,
300
332
  binary: true,
301
333
  });
@@ -321,6 +353,7 @@ export default class EventProcessorMappings extends Command {
321
353
  const raw = await apiRequest(endpoint.method, apiPath, {
322
354
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
323
355
  data,
356
+ contentType: endpoint.bodyMediaType || undefined,
324
357
  contextName: flags.context,
325
358
  binary: true,
326
359
  });
@@ -341,6 +374,7 @@ export default class EventProcessorMappings extends Command {
341
374
  response = await apiRequest(endpoint.method, apiPath, {
342
375
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
343
376
  data,
377
+ contentType: endpoint.bodyMediaType || undefined,
344
378
  contextName: flags.context,
345
379
  acceptAny: true,
346
380
  });