@aderis-erp/cli 1.1.3 → 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 (99) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/access-groups.js +42 -8
  3. package/dist/commands/account-transaction-allocations.js +38 -4
  4. package/dist/commands/account-transactions.d.ts +2 -0
  5. package/dist/commands/account-transactions.js +68 -32
  6. package/dist/commands/accounts.js +46 -12
  7. package/dist/commands/agent-platform.js +37 -3
  8. package/dist/commands/agent-runs.js +49 -15
  9. package/dist/commands/analytics.js +39 -5
  10. package/dist/commands/api-clients.d.ts +58 -0
  11. package/dist/commands/api-clients.js +1375 -0
  12. package/dist/commands/api-keys.js +40 -6
  13. package/dist/commands/async-jobs.js +45 -11
  14. package/dist/commands/audit-log.js +38 -4
  15. package/dist/commands/bank-accounts.js +54 -20
  16. package/dist/commands/bank-integrations.js +56 -22
  17. package/dist/commands/business-partners.js +77 -43
  18. package/dist/commands/chat.js +43 -9
  19. package/dist/commands/credit-card-accounts.d.ts +4 -0
  20. package/dist/commands/credit-card-accounts.js +64 -25
  21. package/dist/commands/currencies.d.ts +56 -0
  22. package/dist/commands/currencies.js +1361 -0
  23. package/dist/commands/custody-accounts.d.ts +56 -0
  24. package/dist/commands/custody-accounts.js +1369 -0
  25. package/dist/commands/{integration-identities.d.ts → dashboards.d.ts} +3 -3
  26. package/dist/commands/dashboards.js +1381 -0
  27. package/dist/commands/delivery-notes.js +69 -35
  28. package/dist/commands/document-checksums.d.ts +57 -0
  29. package/dist/commands/{integration-identities.js → document-checksums.js} +48 -21
  30. package/dist/commands/dunning-configs.js +41 -7
  31. package/dist/commands/dunning-reminders.js +44 -10
  32. package/dist/commands/dunning-runs.js +37 -3
  33. package/dist/commands/email-transactions.js +39 -5
  34. package/dist/commands/employees.js +77 -43
  35. package/dist/commands/entity-configurations.js +39 -5
  36. package/dist/commands/event-mapping-rules.js +37 -3
  37. package/dist/commands/event-processor-mappings.js +41 -7
  38. package/dist/commands/files.js +48 -14
  39. package/dist/commands/financial-instrument-transactions.d.ts +56 -0
  40. package/dist/commands/financial-instrument-transactions.js +1369 -0
  41. package/dist/commands/financial-instrument-types.d.ts +56 -0
  42. package/dist/commands/financial-instrument-types.js +1369 -0
  43. package/dist/commands/financial-instruments.d.ts +56 -0
  44. package/dist/commands/financial-instruments.js +1369 -0
  45. package/dist/commands/health.js +41 -7
  46. package/dist/commands/imports.js +39 -5
  47. package/dist/commands/inbox.js +42 -8
  48. package/dist/commands/integrations.js +39 -5
  49. package/dist/commands/item-categories.js +41 -7
  50. package/dist/commands/items.js +42 -8
  51. package/dist/commands/journal-lines.js +37 -3
  52. package/dist/commands/journals.js +61 -27
  53. package/dist/commands/{workflows.d.ts → kpis.d.ts} +1 -1
  54. package/dist/commands/kpis.js +1371 -0
  55. package/dist/commands/ledgers.js +83 -49
  56. package/dist/commands/my-account.js +54 -20
  57. package/dist/commands/notifications.d.ts +56 -0
  58. package/dist/commands/notifications.js +1360 -0
  59. package/dist/commands/office-locations.js +41 -7
  60. package/dist/commands/payment-methods.js +46 -12
  61. package/dist/commands/payment-runs.js +54 -20
  62. package/dist/commands/payment-terms.js +42 -8
  63. package/dist/commands/payments.js +43 -9
  64. package/dist/commands/payroll-runs.js +71 -37
  65. package/dist/commands/permissions.js +37 -3
  66. package/dist/commands/price-lists.js +48 -14
  67. package/dist/commands/processor-mappings.js +40 -6
  68. package/dist/commands/processors.js +46 -12
  69. package/dist/commands/purchase-order-types.js +41 -7
  70. package/dist/commands/purchase-orders.js +60 -26
  71. package/dist/commands/reports.js +43 -8
  72. package/dist/commands/roles.js +40 -6
  73. package/dist/commands/salary-types.js +41 -7
  74. package/dist/commands/sales-invoices.js +85 -51
  75. package/dist/commands/sales-orders.js +69 -35
  76. package/dist/commands/saved-views.js +39 -5
  77. package/dist/commands/scheduled-processor-mappings.js +41 -7
  78. package/dist/commands/schedules.js +45 -11
  79. package/dist/commands/settings.d.ts +56 -0
  80. package/dist/commands/settings.js +1361 -0
  81. package/dist/commands/tags.js +42 -8
  82. package/dist/commands/tax-codes.js +44 -10
  83. package/dist/commands/tax-matrix.js +41 -7
  84. package/dist/commands/templates.js +50 -15
  85. package/dist/commands/tenants.js +44 -10
  86. package/dist/commands/tools.js +38 -4
  87. package/dist/commands/users.js +58 -24
  88. package/dist/commands/vat-declarations.js +61 -27
  89. package/dist/commands/vendor-invoices.js +82 -47
  90. package/dist/commands/webhooks.js +43 -9
  91. package/dist/commands/work-summary.d.ts +56 -0
  92. package/dist/commands/{workflows.js → work-summary.js} +49 -18
  93. package/dist/lib/auth/index.js +5 -0
  94. package/dist/lib/paths.d.ts +8 -0
  95. package/dist/lib/paths.js +16 -0
  96. package/dist/lib/production-guard.d.ts +42 -0
  97. package/dist/lib/production-guard.js +94 -0
  98. package/oclif.manifest.json +2142 -333
  99. 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,11 +12,11 @@ const SERVER_IMPORTS = [];
11
12
  const NESTED_UPSERTS = [];
12
13
  const SUBCOMMANDS = [...["get", "get-all", "ready", "get-3", "v1"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'get': { method: 'GET', path: '/', summary: 'GET /', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
- 'get-all': { method: 'GET', path: '/health', summary: 'GET /health', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
16
- 'ready': { method: 'GET', path: '/ready', summary: 'GET /ready', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
17
- 'get-3': { method: 'GET', path: '/v1/status', summary: 'GET /v1/status', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
18
- 'v1': { method: 'GET', path: '/v1', summary: 'GET /v1', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'get': { method: 'GET', path: '/', summary: 'GET /', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
16
+ 'get-all': { method: 'GET', path: '/health', summary: 'GET /health', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
+ 'ready': { method: 'GET', path: '/ready', summary: 'GET /ready', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
18
+ 'get-3': { method: 'GET', path: '/v1/status', summary: 'GET /v1/status', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
19
+ 'v1': { method: 'GET', path: '/v1', summary: 'GET /v1', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
19
20
  };
20
21
  export default class Health extends Command {
21
22
  static description = 'Manage health';
@@ -32,7 +33,7 @@ export default class Health extends Command {
32
33
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
33
34
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
34
35
  data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
35
- file: Flags.string({ char: 'f', description: 'Read request body from file' }),
36
+ file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
36
37
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
37
38
  page: Flags.string({ description: 'Page number' }),
38
39
  limit: Flags.string({ description: 'Items per page' }),
@@ -163,6 +164,17 @@ export default class Health extends Command {
163
164
  }
164
165
  }
165
166
  }
167
+ // Flags generated from this endpoint's own parameters. Raw-body endpoints
168
+ // have them: their body is not JSON, so --set cannot carry a value there.
169
+ for (const param of endpoint.specParams) {
170
+ const value = flags[param.flag];
171
+ if (value === undefined || value === '') {
172
+ if (param.required)
173
+ this.error(`Missing required flag: --${param.flag}`);
174
+ continue;
175
+ }
176
+ queryParams[param.name] = value;
177
+ }
166
178
  let data;
167
179
  if (endpoint.isMultipart) {
168
180
  if (!flags.file)
@@ -183,6 +195,25 @@ export default class Health extends Command {
183
195
  }
184
196
  data = form;
185
197
  }
198
+ else if (endpoint.bodyMediaType) {
199
+ // The body is not JSON (today only application/x-ndjson): it is sent
200
+ // verbatim, under the content type the endpoint asks for.
201
+ if (flags.file) {
202
+ const fsModule = await import('node:fs');
203
+ try {
204
+ data = fsModule.readFileSync(flags.file, 'utf-8');
205
+ }
206
+ catch {
207
+ this.error(`Failed to read file: ${flags.file}`);
208
+ }
209
+ }
210
+ else if (flags.data) {
211
+ data = flags.data;
212
+ }
213
+ else {
214
+ this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
215
+ }
216
+ }
186
217
  else if (endpoint.hasBody) {
187
218
  // For binary-response endpoints, --file is the output path, not a JSON
188
219
  // input file — skip the read-as-body branch.
@@ -278,7 +309,7 @@ export default class Health extends Command {
278
309
  process.stderr.write(`Body: [multipart/form-data file upload]\n`);
279
310
  }
280
311
  else if (data) {
281
- process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
312
+ process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
282
313
  }
283
314
  process.stderr.write('\n');
284
315
  }
@@ -292,6 +323,7 @@ export default class Health extends Command {
292
323
  const binResponse = await apiRequest(endpoint.method, apiPath, {
293
324
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
294
325
  data,
326
+ contentType: endpoint.bodyMediaType || undefined,
295
327
  contextName: flags.context,
296
328
  binary: true,
297
329
  });
@@ -317,6 +349,7 @@ export default class Health extends Command {
317
349
  const raw = await apiRequest(endpoint.method, apiPath, {
318
350
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
319
351
  data,
352
+ contentType: endpoint.bodyMediaType || undefined,
320
353
  contextName: flags.context,
321
354
  binary: true,
322
355
  });
@@ -337,6 +370,7 @@ export default class Health extends Command {
337
370
  response = await apiRequest(endpoint.method, apiPath, {
338
371
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
339
372
  data,
373
+ contentType: endpoint.bodyMediaType || undefined,
340
374
  contextName: flags.context,
341
375
  acceptAny: true,
342
376
  });
@@ -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", "get", "delete"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'list': { method: 'GET', path: '/v1/imports', summary: 'List the tenant\'s import runs', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
- 'get': { method: 'GET', path: '/v1/imports/{id}', summary: 'Read one import run back', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
16
- 'delete': { method: 'DELETE', path: '/v1/imports/{id}', summary: 'Throw one import run away', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'list': { method: 'GET', path: '/v1/imports', summary: 'List the tenant\'s import runs', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
16
+ 'get': { method: 'GET', path: '/v1/imports/{id}', summary: 'Read one import run back', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
+ 'delete': { method: 'DELETE', path: '/v1/imports/{id}', summary: 'Throw one import run away', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
18
  };
18
19
  export default class Imports extends Command {
19
20
  static description = 'Manage imports';
@@ -32,7 +33,7 @@ export default class Imports extends Command {
32
33
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
33
34
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
34
35
  data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
35
- file: Flags.string({ char: 'f', description: 'Read request body from file' }),
36
+ file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
36
37
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
37
38
  page: Flags.string({ description: 'Page number' }),
38
39
  limit: Flags.string({ description: 'Items per page' }),
@@ -163,6 +164,17 @@ export default class Imports extends Command {
163
164
  }
164
165
  }
165
166
  }
167
+ // Flags generated from this endpoint's own parameters. Raw-body endpoints
168
+ // have them: their body is not JSON, so --set cannot carry a value there.
169
+ for (const param of endpoint.specParams) {
170
+ const value = flags[param.flag];
171
+ if (value === undefined || value === '') {
172
+ if (param.required)
173
+ this.error(`Missing required flag: --${param.flag}`);
174
+ continue;
175
+ }
176
+ queryParams[param.name] = value;
177
+ }
166
178
  let data;
167
179
  if (endpoint.isMultipart) {
168
180
  if (!flags.file)
@@ -183,6 +195,25 @@ export default class Imports extends Command {
183
195
  }
184
196
  data = form;
185
197
  }
198
+ else if (endpoint.bodyMediaType) {
199
+ // The body is not JSON (today only application/x-ndjson): it is sent
200
+ // verbatim, under the content type the endpoint asks for.
201
+ if (flags.file) {
202
+ const fsModule = await import('node:fs');
203
+ try {
204
+ data = fsModule.readFileSync(flags.file, 'utf-8');
205
+ }
206
+ catch {
207
+ this.error(`Failed to read file: ${flags.file}`);
208
+ }
209
+ }
210
+ else if (flags.data) {
211
+ data = flags.data;
212
+ }
213
+ else {
214
+ this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
215
+ }
216
+ }
186
217
  else if (endpoint.hasBody) {
187
218
  // For binary-response endpoints, --file is the output path, not a JSON
188
219
  // input file — skip the read-as-body branch.
@@ -278,7 +309,7 @@ export default class Imports extends Command {
278
309
  process.stderr.write(`Body: [multipart/form-data file upload]\n`);
279
310
  }
280
311
  else if (data) {
281
- process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
312
+ process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
282
313
  }
283
314
  process.stderr.write('\n');
284
315
  }
@@ -292,6 +323,7 @@ export default class Imports extends Command {
292
323
  const binResponse = await apiRequest(endpoint.method, apiPath, {
293
324
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
294
325
  data,
326
+ contentType: endpoint.bodyMediaType || undefined,
295
327
  contextName: flags.context,
296
328
  binary: true,
297
329
  });
@@ -317,6 +349,7 @@ export default class Imports extends Command {
317
349
  const raw = await apiRequest(endpoint.method, apiPath, {
318
350
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
319
351
  data,
352
+ contentType: endpoint.bodyMediaType || undefined,
320
353
  contextName: flags.context,
321
354
  binary: true,
322
355
  });
@@ -337,6 +370,7 @@ export default class Imports extends Command {
337
370
  response = await apiRequest(endpoint.method, apiPath, {
338
371
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
339
372
  data,
373
+ contentType: endpoint.bodyMediaType || undefined,
340
374
  contextName: flags.context,
341
375
  acceptAny: true,
342
376
  });
@@ -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,12 +12,12 @@ const SERVER_IMPORTS = [];
11
12
  const NESTED_UPSERTS = [];
12
13
  const SUBCOMMANDS = [...["create-upload", "email-webhook", "document-types", "uploads-files-content", "list-processing-logs", "processing-logs"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'create-upload': { method: 'POST', path: '/v1/inbox/upload', summary: 'Upload document(s) to inbox for processing', pathParams: [], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
- 'email-webhook': { method: 'POST', path: '/v1/inbox/email-webhook/{secret}', summary: 'Email webhook for document ingestion (SendGrid inbound-parse)', pathParams: ['secret'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
16
- 'document-types': { method: 'GET', path: '/v1/inbox/document-types', summary: 'Get available document types and per-parent allowed types', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
17
- 'uploads-files-content': { method: 'GET', path: '/v1/inbox/uploads/{processingId}/files/{fileId}/content', summary: 'Stream the original bytes of an uploaded inbox file', pathParams: ['fileId', 'processingId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
18
- 'list-processing-logs': { method: 'GET', path: '/v1/inbox/processing-logs', summary: 'List recent inbox processing logs (newest first)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
19
- 'processing-logs': { method: 'GET', path: '/v1/inbox/processing-logs/{processingId}', summary: 'Get a processing run by its processing identifier', pathParams: ['processingId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'create-upload': { method: 'POST', path: '/v1/inbox/upload', summary: 'Upload document(s) to inbox for processing', pathParams: [], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
16
+ 'email-webhook': { method: 'POST', path: '/v1/inbox/email-webhook/{secret}', summary: 'Email webhook for document ingestion (SendGrid inbound-parse)', pathParams: ['secret'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
+ 'document-types': { method: 'GET', path: '/v1/inbox/document-types', summary: 'Get available document types and per-parent allowed types', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
18
+ 'uploads-files-content': { method: 'GET', path: '/v1/inbox/uploads/{processingId}/files/{fileId}/content', summary: 'Stream the original bytes of an uploaded inbox file', pathParams: ['fileId', 'processingId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
19
+ 'list-processing-logs': { method: 'GET', path: '/v1/inbox/processing-logs', summary: 'List recent inbox processing logs (newest first)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
20
+ 'processing-logs': { method: 'GET', path: '/v1/inbox/processing-logs/{processingId}', summary: 'Get a processing run by its processing identifier', pathParams: ['processingId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
20
21
  };
21
22
  export default class Inbox extends Command {
22
23
  static description = 'Manage inbox';
@@ -31,7 +32,7 @@ export default class Inbox extends Command {
31
32
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
32
33
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
33
34
  data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
34
- file: Flags.string({ char: 'f', description: 'Read request body from file' }),
35
+ file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
35
36
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
36
37
  'processing-id': Flags.string({ description: 'Path param: processingId' }),
37
38
  page: Flags.string({ description: 'Page number' }),
@@ -163,6 +164,17 @@ export default class Inbox extends Command {
163
164
  }
164
165
  }
165
166
  }
167
+ // Flags generated from this endpoint's own parameters. Raw-body endpoints
168
+ // have them: their body is not JSON, so --set cannot carry a value there.
169
+ for (const param of endpoint.specParams) {
170
+ const value = flags[param.flag];
171
+ if (value === undefined || value === '') {
172
+ if (param.required)
173
+ this.error(`Missing required flag: --${param.flag}`);
174
+ continue;
175
+ }
176
+ queryParams[param.name] = value;
177
+ }
166
178
  let data;
167
179
  if (endpoint.isMultipart) {
168
180
  if (!flags.file)
@@ -183,6 +195,25 @@ export default class Inbox extends Command {
183
195
  }
184
196
  data = form;
185
197
  }
198
+ else if (endpoint.bodyMediaType) {
199
+ // The body is not JSON (today only application/x-ndjson): it is sent
200
+ // verbatim, under the content type the endpoint asks for.
201
+ if (flags.file) {
202
+ const fsModule = await import('node:fs');
203
+ try {
204
+ data = fsModule.readFileSync(flags.file, 'utf-8');
205
+ }
206
+ catch {
207
+ this.error(`Failed to read file: ${flags.file}`);
208
+ }
209
+ }
210
+ else if (flags.data) {
211
+ data = flags.data;
212
+ }
213
+ else {
214
+ this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
215
+ }
216
+ }
186
217
  else if (endpoint.hasBody) {
187
218
  // For binary-response endpoints, --file is the output path, not a JSON
188
219
  // input file — skip the read-as-body branch.
@@ -278,7 +309,7 @@ export default class Inbox extends Command {
278
309
  process.stderr.write(`Body: [multipart/form-data file upload]\n`);
279
310
  }
280
311
  else if (data) {
281
- process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
312
+ process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
282
313
  }
283
314
  process.stderr.write('\n');
284
315
  }
@@ -292,6 +323,7 @@ export default class Inbox extends Command {
292
323
  const binResponse = await apiRequest(endpoint.method, apiPath, {
293
324
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
294
325
  data,
326
+ contentType: endpoint.bodyMediaType || undefined,
295
327
  contextName: flags.context,
296
328
  binary: true,
297
329
  });
@@ -317,6 +349,7 @@ export default class Inbox extends Command {
317
349
  const raw = await apiRequest(endpoint.method, apiPath, {
318
350
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
319
351
  data,
352
+ contentType: endpoint.bodyMediaType || undefined,
320
353
  contextName: flags.context,
321
354
  binary: true,
322
355
  });
@@ -337,6 +370,7 @@ export default class Inbox extends Command {
337
370
  response = await apiRequest(endpoint.method, apiPath, {
338
371
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
339
372
  data,
373
+ contentType: endpoint.bodyMediaType || undefined,
340
374
  contextName: flags.context,
341
375
  acceptAny: true,
342
376
  });
@@ -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 = [...["get", "update", "delete"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'get': { method: 'GET', path: '/v1/integrations/{integrationName}/{configKey}', summary: 'Get integration configuration', pathParams: ['integrationName', 'configKey'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
- 'update': { method: 'PUT', path: '/v1/integrations/{integrationName}/{configKey}', summary: 'Create or update integration configuration', pathParams: ['integrationName', 'configKey'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
16
- 'delete': { method: 'DELETE', path: '/v1/integrations/{integrationName}/{configKey}', summary: 'Delete integration configuration', pathParams: ['integrationName', 'configKey'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'get': { method: 'GET', path: '/v1/integrations/{integrationName}/{configKey}', summary: 'Get integration configuration', pathParams: ['integrationName', 'configKey'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
16
+ 'update': { method: 'PUT', path: '/v1/integrations/{integrationName}/{configKey}', summary: 'Create or update integration configuration', pathParams: ['integrationName', 'configKey'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
+ 'delete': { method: 'DELETE', path: '/v1/integrations/{integrationName}/{configKey}', summary: 'Delete integration configuration', pathParams: ['integrationName', 'configKey'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
18
  };
18
19
  export default class Integrations extends Command {
19
20
  static description = 'Manage integrations';
@@ -31,7 +32,7 @@ export default class Integrations extends Command {
31
32
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
32
33
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
33
34
  data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
34
- file: Flags.string({ char: 'f', description: 'Read request body from file' }),
35
+ file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
35
36
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
36
37
  'config-key': Flags.string({ description: 'Path param: configKey' }),
37
38
  page: Flags.string({ description: 'Page number' }),
@@ -163,6 +164,17 @@ export default class Integrations extends Command {
163
164
  }
164
165
  }
165
166
  }
167
+ // Flags generated from this endpoint's own parameters. Raw-body endpoints
168
+ // have them: their body is not JSON, so --set cannot carry a value there.
169
+ for (const param of endpoint.specParams) {
170
+ const value = flags[param.flag];
171
+ if (value === undefined || value === '') {
172
+ if (param.required)
173
+ this.error(`Missing required flag: --${param.flag}`);
174
+ continue;
175
+ }
176
+ queryParams[param.name] = value;
177
+ }
166
178
  let data;
167
179
  if (endpoint.isMultipart) {
168
180
  if (!flags.file)
@@ -183,6 +195,25 @@ export default class Integrations extends Command {
183
195
  }
184
196
  data = form;
185
197
  }
198
+ else if (endpoint.bodyMediaType) {
199
+ // The body is not JSON (today only application/x-ndjson): it is sent
200
+ // verbatim, under the content type the endpoint asks for.
201
+ if (flags.file) {
202
+ const fsModule = await import('node:fs');
203
+ try {
204
+ data = fsModule.readFileSync(flags.file, 'utf-8');
205
+ }
206
+ catch {
207
+ this.error(`Failed to read file: ${flags.file}`);
208
+ }
209
+ }
210
+ else if (flags.data) {
211
+ data = flags.data;
212
+ }
213
+ else {
214
+ this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
215
+ }
216
+ }
186
217
  else if (endpoint.hasBody) {
187
218
  // For binary-response endpoints, --file is the output path, not a JSON
188
219
  // input file — skip the read-as-body branch.
@@ -278,7 +309,7 @@ export default class Integrations extends Command {
278
309
  process.stderr.write(`Body: [multipart/form-data file upload]\n`);
279
310
  }
280
311
  else if (data) {
281
- process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
312
+ process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
282
313
  }
283
314
  process.stderr.write('\n');
284
315
  }
@@ -292,6 +323,7 @@ export default class Integrations extends Command {
292
323
  const binResponse = await apiRequest(endpoint.method, apiPath, {
293
324
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
294
325
  data,
326
+ contentType: endpoint.bodyMediaType || undefined,
295
327
  contextName: flags.context,
296
328
  binary: true,
297
329
  });
@@ -317,6 +349,7 @@ export default class Integrations extends Command {
317
349
  const raw = await apiRequest(endpoint.method, apiPath, {
318
350
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
319
351
  data,
352
+ contentType: endpoint.bodyMediaType || undefined,
320
353
  contextName: flags.context,
321
354
  binary: true,
322
355
  });
@@ -337,6 +370,7 @@ export default class Integrations extends Command {
337
370
  response = await apiRequest(endpoint.method, apiPath, {
338
371
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
339
372
  data,
373
+ contentType: endpoint.bodyMediaType || undefined,
340
374
  contextName: flags.context,
341
375
  acceptAny: true,
342
376
  });
@@ -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 = [...["list", "create", "get", "update", "delete"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'list': { method: 'GET', path: '/v1/item-categories', summary: 'Get all item categories with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
- 'create': { method: 'POST', path: '/v1/item-categories', summary: 'Create a new item category', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
16
- 'get': { method: 'GET', path: '/v1/item-categories/{id}', summary: 'Get an item category by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
17
- 'update': { method: 'PATCH', path: '/v1/item-categories/{id}', summary: 'Update an item category', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
18
- 'delete': { method: 'DELETE', path: '/v1/item-categories/{id}', summary: 'Delete an item category', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'list': { method: 'GET', path: '/v1/item-categories', summary: 'Get all item categories with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
16
+ 'create': { method: 'POST', path: '/v1/item-categories', summary: 'Create a new item category', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
+ 'get': { method: 'GET', path: '/v1/item-categories/{id}', summary: 'Get an item category by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
18
+ 'update': { method: 'PATCH', path: '/v1/item-categories/{id}', summary: 'Update an item category', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
19
+ 'delete': { method: 'DELETE', path: '/v1/item-categories/{id}', summary: 'Delete an item category', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
19
20
  };
20
21
  export default class ItemCategories extends Command {
21
22
  static description = 'Manage item categories';
@@ -37,7 +38,7 @@ export default class ItemCategories extends Command {
37
38
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
38
39
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
39
40
  data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
40
- file: Flags.string({ char: 'f', description: 'Read request body from file' }),
41
+ file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
41
42
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
42
43
  page: Flags.string({ description: 'Page number' }),
43
44
  limit: Flags.string({ description: 'Items per page' }),
@@ -168,6 +169,17 @@ export default class ItemCategories extends Command {
168
169
  }
169
170
  }
170
171
  }
172
+ // Flags generated from this endpoint's own parameters. Raw-body endpoints
173
+ // have them: their body is not JSON, so --set cannot carry a value there.
174
+ for (const param of endpoint.specParams) {
175
+ const value = flags[param.flag];
176
+ if (value === undefined || value === '') {
177
+ if (param.required)
178
+ this.error(`Missing required flag: --${param.flag}`);
179
+ continue;
180
+ }
181
+ queryParams[param.name] = value;
182
+ }
171
183
  let data;
172
184
  if (endpoint.isMultipart) {
173
185
  if (!flags.file)
@@ -188,6 +200,25 @@ export default class ItemCategories extends Command {
188
200
  }
189
201
  data = form;
190
202
  }
203
+ else if (endpoint.bodyMediaType) {
204
+ // The body is not JSON (today only application/x-ndjson): it is sent
205
+ // verbatim, under the content type the endpoint asks for.
206
+ if (flags.file) {
207
+ const fsModule = await import('node:fs');
208
+ try {
209
+ data = fsModule.readFileSync(flags.file, 'utf-8');
210
+ }
211
+ catch {
212
+ this.error(`Failed to read file: ${flags.file}`);
213
+ }
214
+ }
215
+ else if (flags.data) {
216
+ data = flags.data;
217
+ }
218
+ else {
219
+ this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
220
+ }
221
+ }
191
222
  else if (endpoint.hasBody) {
192
223
  // For binary-response endpoints, --file is the output path, not a JSON
193
224
  // input file — skip the read-as-body branch.
@@ -283,7 +314,7 @@ export default class ItemCategories extends Command {
283
314
  process.stderr.write(`Body: [multipart/form-data file upload]\n`);
284
315
  }
285
316
  else if (data) {
286
- process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
317
+ process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
287
318
  }
288
319
  process.stderr.write('\n');
289
320
  }
@@ -297,6 +328,7 @@ export default class ItemCategories extends Command {
297
328
  const binResponse = await apiRequest(endpoint.method, apiPath, {
298
329
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
299
330
  data,
331
+ contentType: endpoint.bodyMediaType || undefined,
300
332
  contextName: flags.context,
301
333
  binary: true,
302
334
  });
@@ -322,6 +354,7 @@ export default class ItemCategories extends Command {
322
354
  const raw = await apiRequest(endpoint.method, apiPath, {
323
355
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
324
356
  data,
357
+ contentType: endpoint.bodyMediaType || undefined,
325
358
  contextName: flags.context,
326
359
  binary: true,
327
360
  });
@@ -342,6 +375,7 @@ export default class ItemCategories extends Command {
342
375
  response = await apiRequest(endpoint.method, apiPath, {
343
376
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
344
377
  data,
378
+ contentType: endpoint.bodyMediaType || undefined,
345
379
  contextName: flags.context,
346
380
  acceptAny: true,
347
381
  });