@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,7 +12,7 @@ const SERVER_IMPORTS = [];
11
12
  const NESTED_UPSERTS = [];
12
13
  const SUBCOMMANDS = [...["usage"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'usage': { method: 'GET', path: '/v1/agent-platform/usage', summary: 'Fetch tenant LLM usage report (agents-platform pass-through).', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'usage': { method: 'GET', path: '/v1/agent-platform/usage', summary: 'Fetch tenant LLM usage report (agents-platform pass-through).', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
15
16
  };
16
17
  export default class AgentPlatform extends Command {
17
18
  static description = 'Manage agent platform';
@@ -26,7 +27,7 @@ export default class AgentPlatform 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 AgentPlatform 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 AgentPlatform 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 AgentPlatform 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 AgentPlatform 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 AgentPlatform 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 AgentPlatform 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,19 +12,19 @@ const SERVER_IMPORTS = [];
11
12
  const NESTED_UPSERTS = [];
12
13
  const SUBCOMMANDS = [...["sources", "sources-counts", "sources-email", "get-documents", "documents-process-anyway", "documents-discard", "documents-send-to-inbox", "documents-attach", "get-runs", "get-runs-runId", "runs-answer", "runs-restart", "runs-discard"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'sources': { method: 'GET', path: '/v1/agent-runs/sources', summary: 'List v2 sources: arrivals with their files, documents, results and actions (agents-platform pass-through).', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
- 'sources-counts': { method: 'GET', path: '/v1/agent-runs/sources/counts', summary: 'Counts for the v2 status tabs (agents-platform pass-through).', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
16
- 'sources-email': { method: 'GET', path: '/v1/agent-runs/sources/{sourceId}/email', summary: 'The email behind a v2 source, for the View email dialog (agents-platform pass-through).', pathParams: ['sourceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
17
- 'get-documents': { method: 'GET', path: '/v1/agent-runs/documents/{documentId}', summary: 'One v2 document: header, question, results, facts, timeline, files, actions, technical (agents-platform pass-through).', pathParams: ['documentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
18
- 'documents-process-anyway': { method: 'POST', path: '/v1/agent-runs/documents/{documentId}/process-anyway', summary: 'Process a duplicate document anyway (agents-platform pass-through).', pathParams: ['documentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
19
- 'documents-discard': { method: 'POST', path: '/v1/agent-runs/documents/{documentId}/discard', summary: 'Discard a document (agents-platform pass-through).', pathParams: ['documentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
20
- 'documents-send-to-inbox': { method: 'POST', path: '/v1/agent-runs/documents/{documentId}/send-to-inbox', summary: 'Stop processing a document and file it in the Inbox.', pathParams: ['documentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
21
- 'documents-attach': { method: 'POST', path: '/v1/agent-runs/documents/{documentId}/attach', summary: 'Attach a document to another record as supporting files.', pathParams: ['documentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
22
- 'get-runs': { method: 'GET', path: '/v1/agent-runs/runs', summary: 'List v2 runs, the Runs tab worklist (agents-platform pass-through).', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
23
- 'get-runs-runId': { method: 'GET', path: '/v1/agent-runs/runs/{runId}', summary: 'One v2 run detail (agents-platform pass-through).', pathParams: ['runId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
24
- 'runs-answer': { method: 'POST', path: '/v1/agent-runs/runs/{runId}/answer', summary: 'Answer a run stopped for review (agents-platform pass-through).', pathParams: ['runId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
25
- 'runs-restart': { method: 'POST', path: '/v1/agent-runs/runs/{runId}/restart', summary: 'Restart a stopped run (agents-platform pass-through).', pathParams: ['runId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
26
- 'runs-discard': { method: 'POST', path: '/v1/agent-runs/runs/{runId}/discard', summary: 'Discard a stopped run (agents-platform pass-through).', pathParams: ['runId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'sources': { method: 'GET', path: '/v1/agent-runs/sources', summary: 'List v2 sources: arrivals with their files, documents, results and actions (agents-platform pass-through).', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
16
+ 'sources-counts': { method: 'GET', path: '/v1/agent-runs/sources/counts', summary: 'Counts for the v2 status tabs (agents-platform pass-through).', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
+ 'sources-email': { method: 'GET', path: '/v1/agent-runs/sources/{sourceId}/email', summary: 'The email behind a v2 source, for the View email dialog (agents-platform pass-through).', pathParams: ['sourceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
18
+ 'get-documents': { method: 'GET', path: '/v1/agent-runs/documents/{documentId}', summary: 'One v2 document: header, question, results, facts, timeline, files, actions, technical (agents-platform pass-through).', pathParams: ['documentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
19
+ 'documents-process-anyway': { method: 'POST', path: '/v1/agent-runs/documents/{documentId}/process-anyway', summary: 'Process a duplicate document anyway (agents-platform pass-through).', pathParams: ['documentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
20
+ 'documents-discard': { method: 'POST', path: '/v1/agent-runs/documents/{documentId}/discard', summary: 'Discard a document (agents-platform pass-through).', pathParams: ['documentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
21
+ 'documents-send-to-inbox': { method: 'POST', path: '/v1/agent-runs/documents/{documentId}/send-to-inbox', summary: 'Stop processing a document and file it in the Inbox.', pathParams: ['documentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
22
+ 'documents-attach': { method: 'POST', path: '/v1/agent-runs/documents/{documentId}/attach', summary: 'Attach a document to another record as supporting files.', pathParams: ['documentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
23
+ 'get-runs': { method: 'GET', path: '/v1/agent-runs/runs', summary: 'List v2 runs, the Runs tab worklist (agents-platform pass-through).', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
24
+ 'get-runs-runId': { method: 'GET', path: '/v1/agent-runs/runs/{runId}', summary: 'One v2 run detail (agents-platform pass-through).', pathParams: ['runId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
25
+ 'runs-answer': { method: 'POST', path: '/v1/agent-runs/runs/{runId}/answer', summary: 'Answer a run stopped for review (agents-platform pass-through).', pathParams: ['runId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
26
+ 'runs-restart': { method: 'POST', path: '/v1/agent-runs/runs/{runId}/restart', summary: 'Restart a stopped run (agents-platform pass-through).', pathParams: ['runId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
27
+ 'runs-discard': { method: 'POST', path: '/v1/agent-runs/runs/{runId}/discard', summary: 'Discard a stopped run (agents-platform pass-through).', pathParams: ['runId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
27
28
  };
28
29
  export default class AgentRuns extends Command {
29
30
  static description = 'Manage agent runs';
@@ -38,7 +39,7 @@ export default class AgentRuns extends Command {
38
39
  table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
39
40
  csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
40
41
  data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
41
- file: Flags.string({ char: 'f', description: 'Read request body from file' }),
42
+ file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
42
43
  set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
43
44
  page: Flags.string({ description: 'Page number' }),
44
45
  limit: Flags.string({ description: 'Items per page' }),
@@ -169,6 +170,17 @@ export default class AgentRuns extends Command {
169
170
  }
170
171
  }
171
172
  }
173
+ // Flags generated from this endpoint's own parameters. Raw-body endpoints
174
+ // have them: their body is not JSON, so --set cannot carry a value there.
175
+ for (const param of endpoint.specParams) {
176
+ const value = flags[param.flag];
177
+ if (value === undefined || value === '') {
178
+ if (param.required)
179
+ this.error(`Missing required flag: --${param.flag}`);
180
+ continue;
181
+ }
182
+ queryParams[param.name] = value;
183
+ }
172
184
  let data;
173
185
  if (endpoint.isMultipart) {
174
186
  if (!flags.file)
@@ -189,6 +201,25 @@ export default class AgentRuns extends Command {
189
201
  }
190
202
  data = form;
191
203
  }
204
+ else if (endpoint.bodyMediaType) {
205
+ // The body is not JSON (today only application/x-ndjson): it is sent
206
+ // verbatim, under the content type the endpoint asks for.
207
+ if (flags.file) {
208
+ const fsModule = await import('node:fs');
209
+ try {
210
+ data = fsModule.readFileSync(flags.file, 'utf-8');
211
+ }
212
+ catch {
213
+ this.error(`Failed to read file: ${flags.file}`);
214
+ }
215
+ }
216
+ else if (flags.data) {
217
+ data = flags.data;
218
+ }
219
+ else {
220
+ this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
221
+ }
222
+ }
192
223
  else if (endpoint.hasBody) {
193
224
  // For binary-response endpoints, --file is the output path, not a JSON
194
225
  // input file — skip the read-as-body branch.
@@ -284,7 +315,7 @@ export default class AgentRuns extends Command {
284
315
  process.stderr.write(`Body: [multipart/form-data file upload]\n`);
285
316
  }
286
317
  else if (data) {
287
- process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
318
+ process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
288
319
  }
289
320
  process.stderr.write('\n');
290
321
  }
@@ -298,6 +329,7 @@ export default class AgentRuns extends Command {
298
329
  const binResponse = await apiRequest(endpoint.method, apiPath, {
299
330
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
300
331
  data,
332
+ contentType: endpoint.bodyMediaType || undefined,
301
333
  contextName: flags.context,
302
334
  binary: true,
303
335
  });
@@ -323,6 +355,7 @@ export default class AgentRuns extends Command {
323
355
  const raw = await apiRequest(endpoint.method, apiPath, {
324
356
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
325
357
  data,
358
+ contentType: endpoint.bodyMediaType || undefined,
326
359
  contextName: flags.context,
327
360
  binary: true,
328
361
  });
@@ -343,6 +376,7 @@ export default class AgentRuns extends Command {
343
376
  response = await apiRequest(endpoint.method, apiPath, {
344
377
  params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
345
378
  data,
379
+ contentType: endpoint.bodyMediaType || undefined,
346
380
  contextName: flags.context,
347
381
  acceptAny: true,
348
382
  });
@@ -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 = [...["query", "views", "views-name"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
13
14
  const ENDPOINTS = {
14
- 'query': { method: 'POST', path: '/v1/analytics/query', summary: 'Run a structured aggregate query over a reporting view', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['measures', 'dimensions', 'filters', 'order'] },
15
- 'views': { method: 'GET', path: '/v1/analytics/views', summary: 'List the analytics views for discovery', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
16
- 'views-name': { method: 'GET', path: '/v1/analytics/views/{name}', summary: 'Describe one analytics view with full metadata', pathParams: ['name'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
15
+ 'query': { method: 'POST', path: '/v1/analytics/query', summary: 'Run a structured aggregate query over a reporting view', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['measures', 'dimensions', 'filters', 'order'], bodyMediaType: '', specParams: [] },
16
+ 'views': { method: 'GET', path: '/v1/analytics/views', summary: 'List the analytics views for discovery', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
+ 'views-name': { method: 'GET', path: '/v1/analytics/views/{name}', summary: 'Describe one analytics view with full metadata', pathParams: ['name'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
17
18
  };
18
19
  export default class Analytics extends Command {
19
20
  static description = 'Manage analytics';
@@ -28,7 +29,7 @@ export default class Analytics 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 Analytics 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 Analytics 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 Analytics 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 Analytics 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 Analytics 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 Analytics 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
  });
@@ -0,0 +1,58 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ApiClients extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ action: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
+ };
9
+ static flags: {
10
+ context: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ table: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ data: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ set: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ 'secret-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ 'key-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ page: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
+ search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
23
+ 'sort-by': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
24
+ filter: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
25
+ query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
26
+ fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
27
+ sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
28
+ meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
29
+ 'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
30
+ };
31
+ run(): Promise<void>;
32
+ private runUpsert;
33
+ /**
34
+ * The API's own import: the whole array goes up in one request and comes
35
+ * back as an import run, which records what happened to every record. A bad
36
+ * record lands in errorLog and the rest are still applied, and the run stays
37
+ * readable afterwards with: aderis imports get <id>.
38
+ */
39
+ private runServerImport;
40
+ /** Renders an import run: its counts, then whatever the server refused. */
41
+ private printImportRun;
42
+ private runExport;
43
+ private runCamt053Import;
44
+ private runNestedUpsert;
45
+ private parseFilterValue;
46
+ /** The page line under a table: which page, how many, and any sums the API returned. */
47
+ private printPageSummary;
48
+ /**
49
+ * The API caps how many records a page may hold. Asking for more and getting
50
+ * fewer looks like "that is everything" unless it is said out loud.
51
+ */
52
+ private warnShortPage;
53
+ private sanitizeOutput;
54
+ private getColumns;
55
+ private getValue;
56
+ private printTable;
57
+ private printCsv;
58
+ }