@aderis-erp/cli 1.0.2 → 1.0.3

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/{processing-logs.d.ts → access-groups.d.ts} +1 -1
  2. package/dist/commands/{access-groups 2.js → access-groups.js} +30 -18
  3. package/dist/commands/account-transactions.d.ts +41 -0
  4. package/dist/commands/account-transactions.js +1032 -0
  5. package/dist/commands/accounts.d.ts +40 -0
  6. package/dist/commands/accounts.js +1024 -0
  7. package/dist/commands/agent-runs.d.ts +39 -0
  8. package/dist/commands/agent-runs.js +1013 -0
  9. package/dist/commands/{pubsub.d.ts → async-endpoints-internal.d.ts} +1 -1
  10. package/dist/commands/{pubsub.js → async-endpoints-internal.js} +37 -25
  11. package/dist/commands/audit-log.d.ts +39 -0
  12. package/dist/commands/{processing-logs.js → audit-log.js} +42 -28
  13. package/dist/commands/auth/login.d.ts +14 -0
  14. package/dist/commands/auth/login.js +116 -0
  15. package/dist/commands/auth/logout.d.ts +10 -0
  16. package/dist/commands/auth/logout.js +58 -0
  17. package/dist/commands/auth/status.d.ts +12 -0
  18. package/dist/commands/auth/status.js +101 -0
  19. package/dist/commands/auth/tenant.d.ts +19 -0
  20. package/dist/commands/auth/tenant.js +184 -0
  21. package/dist/commands/auth.d.ts +37 -0
  22. package/dist/commands/auth.js +871 -0
  23. package/dist/commands/bank-accounts.d.ts +41 -0
  24. package/dist/commands/bank-accounts.js +1032 -0
  25. package/dist/commands/bank-integrations.js +30 -18
  26. package/dist/commands/business-partners---contacts.js +30 -18
  27. package/dist/commands/business-partners.js +30 -18
  28. package/dist/commands/config/add.d.ts +13 -0
  29. package/dist/commands/config/add.js +120 -0
  30. package/dist/commands/config/current.d.ts +9 -0
  31. package/dist/commands/config/current.js +43 -0
  32. package/dist/commands/config/delete.d.ts +12 -0
  33. package/dist/commands/config/delete.js +67 -0
  34. package/dist/commands/config/list.d.ts +9 -0
  35. package/dist/commands/config/list.js +50 -0
  36. package/dist/commands/config/use.d.ts +9 -0
  37. package/dist/commands/config/use.js +40 -0
  38. package/dist/commands/credit-card-accounts.js +30 -18
  39. package/dist/commands/documents.js +30 -18
  40. package/dist/commands/dunning-configs.js +30 -18
  41. package/dist/commands/dunning-reminders.js +30 -18
  42. package/dist/commands/dunning-runs.js +30 -18
  43. package/dist/commands/employees.js +30 -18
  44. package/dist/commands/event-processor-mappings.js +30 -18
  45. package/dist/commands/files-internal.js +30 -18
  46. package/dist/commands/health.js +30 -18
  47. package/dist/commands/inbox.js +33 -19
  48. package/dist/commands/integrations.js +30 -18
  49. package/dist/commands/item-categories.js +30 -18
  50. package/dist/commands/items.js +30 -18
  51. package/dist/commands/journal-lines.js +30 -18
  52. package/dist/commands/journals.js +30 -18
  53. package/dist/commands/ledgers.js +30 -18
  54. package/dist/commands/office-locations.js +30 -18
  55. package/dist/commands/payment-method-ledger-mappings.js +30 -18
  56. package/dist/commands/payment-methods.js +30 -18
  57. package/dist/commands/payment-runs.js +30 -18
  58. package/dist/commands/payment-terms.js +30 -18
  59. package/dist/commands/price-lists.js +30 -18
  60. package/dist/commands/processor-mappings.js +30 -18
  61. package/dist/commands/processors.js +31 -19
  62. package/dist/commands/reconciliation.js +30 -18
  63. package/dist/commands/reports.js +30 -18
  64. package/dist/commands/salary-types.js +1018 -0
  65. package/dist/commands/sales-invoices.js +30 -18
  66. package/dist/commands/sales-orders.js +32 -19
  67. package/dist/commands/switch.d.ts +1 -0
  68. package/dist/commands/switch.js +58 -3
  69. package/dist/commands/tags.js +30 -18
  70. package/dist/commands/tax-codes.js +30 -18
  71. package/dist/commands/tax-matrix.js +30 -18
  72. package/dist/commands/tenants.js +30 -18
  73. package/dist/commands/text-templates.js +30 -18
  74. package/dist/commands/tools.js +30 -18
  75. package/dist/commands/vendor-invoices.js +33 -19
  76. package/dist/commands/webhooks.js +30 -18
  77. package/dist/commands/workflows.js +30 -18
  78. package/dist/index 2.js +1 -0
  79. package/dist/index.d 2.ts +1 -0
  80. package/dist/lib/auth.d.ts +16 -1
  81. package/dist/lib/auth.js +153 -45
  82. package/dist/lib/config.d.ts +7 -0
  83. package/dist/lib/config.js +6 -0
  84. package/oclif.manifest.json +2092 -669
  85. package/package.json +2 -2
@@ -834,32 +834,44 @@ export default class BankIntegrations extends Command {
834
834
  filterFields(data, fields) {
835
835
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
836
836
  const pick = (obj, keys) => {
837
- const result = {};
837
+ // Group keys by their first segment so sibling sub-paths
838
+ // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
839
+ // and don't clobber each other on merge.
840
+ const directKeys = new Set();
841
+ const nestedKeys = new Map();
838
842
  for (const key of keys) {
839
843
  if (key.split('.').some(isUnsafeKey))
840
844
  continue;
841
845
  if (key.includes('.')) {
842
- // Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
843
846
  const [first, ...rest] = key.split('.');
844
- const val = obj[first];
845
- if (val && typeof val === 'object') {
846
- const restKey = rest.join('.');
847
- if (Array.isArray(val)) {
848
- // Map over array elements (e.g., allocations[].salesInvoiceId)
849
- const existing = Array.isArray(result[first]) ? result[first] : [];
850
- result[first] = val.map((item, i) => ({
851
- ...(existing[i] || {}),
852
- ...pick(item, [restKey]),
853
- }));
854
- }
855
- else {
856
- const nested = pick(val, [restKey]);
857
- result[first] = { ...(result[first] || {}), ...nested };
858
- }
847
+ if (isUnsafeKey(first))
848
+ continue;
849
+ const list = nestedKeys.get(first);
850
+ if (list) {
851
+ list.push(rest.join('.'));
859
852
  }
853
+ else {
854
+ nestedKeys.set(first, [rest.join('.')]);
855
+ }
856
+ }
857
+ else {
858
+ directKeys.add(key);
860
859
  }
861
- else if (key in obj) {
860
+ }
861
+ const result = {};
862
+ for (const key of directKeys) {
863
+ if (key in obj)
862
864
  result[key] = obj[key];
865
+ }
866
+ for (const [first, restKeys] of nestedKeys) {
867
+ const val = obj[first];
868
+ if (!val || typeof val !== 'object')
869
+ continue;
870
+ if (Array.isArray(val)) {
871
+ result[first] = val.map(item => pick(item, restKeys));
872
+ }
873
+ else {
874
+ result[first] = pick(val, restKeys);
863
875
  }
864
876
  }
865
877
  return result;
@@ -819,32 +819,44 @@ export default class BusinessPartnersContacts extends Command {
819
819
  filterFields(data, fields) {
820
820
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
821
821
  const pick = (obj, keys) => {
822
- const result = {};
822
+ // Group keys by their first segment so sibling sub-paths
823
+ // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
824
+ // and don't clobber each other on merge.
825
+ const directKeys = new Set();
826
+ const nestedKeys = new Map();
823
827
  for (const key of keys) {
824
828
  if (key.split('.').some(isUnsafeKey))
825
829
  continue;
826
830
  if (key.includes('.')) {
827
- // Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
828
831
  const [first, ...rest] = key.split('.');
829
- const val = obj[first];
830
- if (val && typeof val === 'object') {
831
- const restKey = rest.join('.');
832
- if (Array.isArray(val)) {
833
- // Map over array elements (e.g., allocations[].salesInvoiceId)
834
- const existing = Array.isArray(result[first]) ? result[first] : [];
835
- result[first] = val.map((item, i) => ({
836
- ...(existing[i] || {}),
837
- ...pick(item, [restKey]),
838
- }));
839
- }
840
- else {
841
- const nested = pick(val, [restKey]);
842
- result[first] = { ...(result[first] || {}), ...nested };
843
- }
832
+ if (isUnsafeKey(first))
833
+ continue;
834
+ const list = nestedKeys.get(first);
835
+ if (list) {
836
+ list.push(rest.join('.'));
844
837
  }
838
+ else {
839
+ nestedKeys.set(first, [rest.join('.')]);
840
+ }
841
+ }
842
+ else {
843
+ directKeys.add(key);
845
844
  }
846
- else if (key in obj) {
845
+ }
846
+ const result = {};
847
+ for (const key of directKeys) {
848
+ if (key in obj)
847
849
  result[key] = obj[key];
850
+ }
851
+ for (const [first, restKeys] of nestedKeys) {
852
+ const val = obj[first];
853
+ if (!val || typeof val !== 'object')
854
+ continue;
855
+ if (Array.isArray(val)) {
856
+ result[first] = val.map(item => pick(item, restKeys));
857
+ }
858
+ else {
859
+ result[first] = pick(val, restKeys);
848
860
  }
849
861
  }
850
862
  return result;
@@ -825,32 +825,44 @@ export default class BusinessPartners extends Command {
825
825
  filterFields(data, fields) {
826
826
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
827
827
  const pick = (obj, keys) => {
828
- const result = {};
828
+ // Group keys by their first segment so sibling sub-paths
829
+ // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
830
+ // and don't clobber each other on merge.
831
+ const directKeys = new Set();
832
+ const nestedKeys = new Map();
829
833
  for (const key of keys) {
830
834
  if (key.split('.').some(isUnsafeKey))
831
835
  continue;
832
836
  if (key.includes('.')) {
833
- // Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
834
837
  const [first, ...rest] = key.split('.');
835
- const val = obj[first];
836
- if (val && typeof val === 'object') {
837
- const restKey = rest.join('.');
838
- if (Array.isArray(val)) {
839
- // Map over array elements (e.g., allocations[].salesInvoiceId)
840
- const existing = Array.isArray(result[first]) ? result[first] : [];
841
- result[first] = val.map((item, i) => ({
842
- ...(existing[i] || {}),
843
- ...pick(item, [restKey]),
844
- }));
845
- }
846
- else {
847
- const nested = pick(val, [restKey]);
848
- result[first] = { ...(result[first] || {}), ...nested };
849
- }
838
+ if (isUnsafeKey(first))
839
+ continue;
840
+ const list = nestedKeys.get(first);
841
+ if (list) {
842
+ list.push(rest.join('.'));
850
843
  }
844
+ else {
845
+ nestedKeys.set(first, [rest.join('.')]);
846
+ }
847
+ }
848
+ else {
849
+ directKeys.add(key);
851
850
  }
852
- else if (key in obj) {
851
+ }
852
+ const result = {};
853
+ for (const key of directKeys) {
854
+ if (key in obj)
853
855
  result[key] = obj[key];
856
+ }
857
+ for (const [first, restKeys] of nestedKeys) {
858
+ const val = obj[first];
859
+ if (!val || typeof val !== 'object')
860
+ continue;
861
+ if (Array.isArray(val)) {
862
+ result[first] = val.map(item => pick(item, restKeys));
863
+ }
864
+ else {
865
+ result[first] = pick(val, restKeys);
854
866
  }
855
867
  }
856
868
  return result;
@@ -0,0 +1,13 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ConfigAdd extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'client-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'client-secret': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ environment: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,120 @@
1
+ import { confirm, input, select } from '@inquirer/prompts';
2
+ import { Command, Flags } from '@oclif/core';
3
+ import { addContext, DEFAULT_CLIENT_IDS, getContext } from '../../lib/config.js';
4
+ export default class ConfigAdd extends Command {
5
+ static description = 'Add a new context configuration';
6
+ static examples = [
7
+ '<%= config.bin %> config add',
8
+ '<%= config.bin %> config add --name my-client --environment live',
9
+ '<%= config.bin %> config add --name dev-context --environment dev --client-id abc123',
10
+ ];
11
+ static flags = {
12
+ 'client-id': Flags.string({
13
+ description: 'OAuth client ID (uses default if not specified)',
14
+ }),
15
+ 'client-secret': Flags.string({
16
+ description: 'OAuth client secret (optional, for confidential clients)',
17
+ }),
18
+ environment: Flags.string({
19
+ char: 'e',
20
+ description: 'Environment (dev, test, live)',
21
+ options: ['dev', 'test', 'live'],
22
+ }),
23
+ force: Flags.boolean({
24
+ char: 'f',
25
+ default: false,
26
+ description: 'Overwrite existing context without prompting',
27
+ }),
28
+ name: Flags.string({
29
+ char: 'n',
30
+ description: 'Context name',
31
+ }),
32
+ };
33
+ async run() {
34
+ const { flags } = await this.parse(ConfigAdd);
35
+ // Get context name
36
+ let { name } = flags;
37
+ if (!name) {
38
+ name = await input({
39
+ message: 'Context name:',
40
+ validate(value) {
41
+ if (!value.trim())
42
+ return 'Context name is required';
43
+ if (!/^[a-zA-Z0-9_-]+$/.test(value)) {
44
+ return 'Context name can only contain letters, numbers, hyphens, and underscores';
45
+ }
46
+ return true;
47
+ },
48
+ });
49
+ }
50
+ // Check if context already exists
51
+ const existing = getContext(name);
52
+ if (existing && !flags.force) {
53
+ const overwrite = await confirm({
54
+ default: false,
55
+ message: `Context "${name}" already exists. Overwrite?`,
56
+ });
57
+ if (!overwrite) {
58
+ this.log('Aborted.');
59
+ return;
60
+ }
61
+ }
62
+ // Get environment
63
+ let environment = flags.environment;
64
+ if (!environment) {
65
+ environment = (await select({
66
+ choices: [
67
+ { name: 'live (production)', value: 'live' },
68
+ { name: 'test (staging)', value: 'test' },
69
+ { name: 'dev (development)', value: 'dev' },
70
+ ],
71
+ message: 'Environment:',
72
+ }));
73
+ }
74
+ // Get client ID
75
+ let clientId = flags['client-id'];
76
+ if (!clientId) {
77
+ const defaultClientId = DEFAULT_CLIENT_IDS[environment];
78
+ if (defaultClientId) {
79
+ const useDefault = await confirm({
80
+ default: true,
81
+ message: `Use default client ID for ${environment}?`,
82
+ });
83
+ if (useDefault) {
84
+ clientId = defaultClientId;
85
+ }
86
+ }
87
+ if (!clientId) {
88
+ clientId = await input({
89
+ message: 'OAuth client ID:',
90
+ validate: (value) => (value.trim() ? true : 'Client ID is required'),
91
+ });
92
+ }
93
+ }
94
+ // Get client secret (optional)
95
+ let clientSecret = flags['client-secret'];
96
+ if (!clientSecret && !flags.name) {
97
+ // Only prompt for secret in interactive mode
98
+ const hasSecret = await confirm({
99
+ default: false,
100
+ message: 'Do you have a client secret? (for confidential clients)',
101
+ });
102
+ if (hasSecret) {
103
+ clientSecret = await input({
104
+ message: 'OAuth client secret:',
105
+ });
106
+ }
107
+ }
108
+ // Create the context
109
+ addContext({
110
+ client_id: clientId,
111
+ client_secret: clientSecret,
112
+ environment,
113
+ name,
114
+ });
115
+ this.log(`\nContext "${name}" created successfully.`);
116
+ this.log(`Environment: ${environment}`);
117
+ this.log(`Client ID: ${clientId}`);
118
+ this.log(`\nTo authenticate, run: aderis auth login`);
119
+ }
120
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ConfigCurrent extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,43 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { getCurrentContext } from '../../lib/config.js';
3
+ export default class ConfigCurrent extends Command {
4
+ static description = 'Show the current context';
5
+ static examples = ['<%= config.bin %> config current', '<%= config.bin %> config current --json'];
6
+ static flags = {
7
+ json: Flags.boolean({
8
+ default: false,
9
+ description: 'Output as JSON',
10
+ }),
11
+ };
12
+ async run() {
13
+ const { flags } = await this.parse(ConfigCurrent);
14
+ const context = getCurrentContext();
15
+ if (!context) {
16
+ if (flags.json) {
17
+ this.log(JSON.stringify({ current: null }, null, 2));
18
+ }
19
+ else {
20
+ this.log('No current context configured.');
21
+ this.log('Create one with: aderis auth login --new');
22
+ }
23
+ return;
24
+ }
25
+ if (flags.json) {
26
+ this.log(JSON.stringify({
27
+ authenticated: Boolean(context.tokens),
28
+ client_id: context.client_id,
29
+ environment: context.environment,
30
+ name: context.name,
31
+ tenant_id: context.tenant_id,
32
+ }, null, 2));
33
+ return;
34
+ }
35
+ this.log(`Current context: ${context.name}`);
36
+ this.log(`Environment: ${context.environment}`);
37
+ this.log(`Client ID: ${context.client_id}`);
38
+ if (context.tenant_id) {
39
+ this.log(`Tenant ID: ${context.tenant_id}`);
40
+ }
41
+ this.log(`Authenticated: ${context.tokens ? 'Yes' : 'No'}`);
42
+ }
43
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ConfigDelete extends Command {
3
+ static args: {
4
+ name: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,67 @@
1
+ import { confirm, select } from '@inquirer/prompts';
2
+ import { Args, Command, Flags } from '@oclif/core';
3
+ import { deleteContext, getContext, getCurrentContext, listContexts } from '../../lib/config.js';
4
+ export default class ConfigDelete extends Command {
5
+ static args = {
6
+ name: Args.string({
7
+ description: 'Context name to delete',
8
+ required: false,
9
+ }),
10
+ };
11
+ static description = 'Delete a configured context';
12
+ static examples = [
13
+ '<%= config.bin %> config delete my-client',
14
+ '<%= config.bin %> config delete my-client --force',
15
+ ];
16
+ static flags = {
17
+ force: Flags.boolean({
18
+ char: 'f',
19
+ default: false,
20
+ description: 'Skip confirmation prompt',
21
+ }),
22
+ };
23
+ async run() {
24
+ const { args, flags } = await this.parse(ConfigDelete);
25
+ let contextName = args.name;
26
+ if (!contextName) {
27
+ const contexts = listContexts();
28
+ if (contexts.length === 0) {
29
+ this.error('No contexts configured.');
30
+ }
31
+ contextName = await select({
32
+ choices: contexts.map((c) => ({
33
+ name: `${c.name} (${c.environment})`,
34
+ value: c.name,
35
+ })),
36
+ message: 'Select a context to delete:',
37
+ });
38
+ }
39
+ const context = getContext(contextName);
40
+ if (!context) {
41
+ this.error(`Context "${contextName}" not found.`);
42
+ }
43
+ const current = getCurrentContext();
44
+ const isCurrent = current?.name === contextName;
45
+ if (!flags.force) {
46
+ const confirmed = await confirm({
47
+ default: false,
48
+ message: `Are you sure you want to delete context "${contextName}"?${isCurrent ? ' (This is your current context)' : ''}`,
49
+ });
50
+ if (!confirmed) {
51
+ this.log('Cancelled.');
52
+ return;
53
+ }
54
+ }
55
+ deleteContext(contextName);
56
+ this.log(`Context "${contextName}" deleted.`);
57
+ if (isCurrent) {
58
+ const newCurrent = getCurrentContext();
59
+ if (newCurrent) {
60
+ this.log(`Switched to context "${newCurrent.name}"`);
61
+ }
62
+ else {
63
+ this.log('\nNo contexts remaining. Create one with: aderis auth login --new');
64
+ }
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ConfigList extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,50 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { getCurrentContext, listContexts } from '../../lib/config.js';
3
+ export default class ConfigList extends Command {
4
+ static description = 'List all configured contexts';
5
+ static examples = ['<%= config.bin %> config list', '<%= config.bin %> config list --json'];
6
+ static flags = {
7
+ json: Flags.boolean({
8
+ default: false,
9
+ description: 'Output as JSON',
10
+ }),
11
+ };
12
+ async run() {
13
+ const { flags } = await this.parse(ConfigList);
14
+ const contexts = listContexts();
15
+ const current = getCurrentContext();
16
+ if (flags.json) {
17
+ this.log(JSON.stringify({
18
+ contexts: contexts.map((c) => ({
19
+ authenticated: Boolean(c.tokens),
20
+ client_id: c.client_id,
21
+ environment: c.environment,
22
+ name: c.name,
23
+ tenant_id: c.tenant_id,
24
+ })),
25
+ current: current?.name || null,
26
+ }, null, 2));
27
+ return;
28
+ }
29
+ if (contexts.length === 0) {
30
+ this.log('No contexts configured.');
31
+ this.log('\nCreate a new context with: aderis auth login --new');
32
+ return;
33
+ }
34
+ this.log('Configured contexts:\n');
35
+ for (const ctx of contexts) {
36
+ const isCurrent = current?.name === ctx.name;
37
+ const marker = isCurrent ? '*' : ' ';
38
+ const auth = ctx.tokens ? '✓' : '✗';
39
+ this.log(`${marker} ${ctx.name}`);
40
+ this.log(` Environment: ${ctx.environment}`);
41
+ this.log(` Client ID: ${ctx.client_id}`);
42
+ this.log(` Authenticated: ${auth}`);
43
+ if (ctx.tenant_id) {
44
+ this.log(` Tenant ID: ${ctx.tenant_id}`);
45
+ }
46
+ this.log('');
47
+ }
48
+ this.log('* = current context');
49
+ }
50
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ConfigUse extends Command {
3
+ static args: {
4
+ name: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,40 @@
1
+ import { select } from '@inquirer/prompts';
2
+ import { Args, Command } from '@oclif/core';
3
+ import { getContext, listContexts, setCurrentContext } from '../../lib/config.js';
4
+ export default class ConfigUse extends Command {
5
+ static args = {
6
+ name: Args.string({
7
+ description: 'Context name to switch to',
8
+ required: false,
9
+ }),
10
+ };
11
+ static description = 'Switch to a different context';
12
+ static examples = ['<%= config.bin %> config use my-client', '<%= config.bin %> config use'];
13
+ async run() {
14
+ const { args } = await this.parse(ConfigUse);
15
+ let contextName = args.name;
16
+ if (!contextName) {
17
+ const contexts = listContexts();
18
+ if (contexts.length === 0) {
19
+ this.error('No contexts configured. Create one with: aderis auth login --new');
20
+ }
21
+ contextName = await select({
22
+ choices: contexts.map((c) => ({
23
+ name: `${c.name} (${c.environment})${c.tokens ? ' ✓' : ''}`,
24
+ value: c.name,
25
+ })),
26
+ message: 'Select a context:',
27
+ });
28
+ }
29
+ const context = getContext(contextName);
30
+ if (!context) {
31
+ this.error(`Context "${contextName}" not found.`);
32
+ }
33
+ setCurrentContext(contextName);
34
+ this.log(`Switched to context "${contextName}" (${context.environment})`);
35
+ if (!context.tokens) {
36
+ this.log(`\nNote: You are not logged in to this context.`);
37
+ this.log(`Run: aderis auth login`);
38
+ }
39
+ }
40
+ }
@@ -831,32 +831,44 @@ export default class CreditCardAccounts extends Command {
831
831
  filterFields(data, fields) {
832
832
  const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
833
833
  const pick = (obj, keys) => {
834
- const result = {};
834
+ // Group keys by their first segment so sibling sub-paths
835
+ // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
836
+ // and don't clobber each other on merge.
837
+ const directKeys = new Set();
838
+ const nestedKeys = new Map();
835
839
  for (const key of keys) {
836
840
  if (key.split('.').some(isUnsafeKey))
837
841
  continue;
838
842
  if (key.includes('.')) {
839
- // Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
840
843
  const [first, ...rest] = key.split('.');
841
- const val = obj[first];
842
- if (val && typeof val === 'object') {
843
- const restKey = rest.join('.');
844
- if (Array.isArray(val)) {
845
- // Map over array elements (e.g., allocations[].salesInvoiceId)
846
- const existing = Array.isArray(result[first]) ? result[first] : [];
847
- result[first] = val.map((item, i) => ({
848
- ...(existing[i] || {}),
849
- ...pick(item, [restKey]),
850
- }));
851
- }
852
- else {
853
- const nested = pick(val, [restKey]);
854
- result[first] = { ...(result[first] || {}), ...nested };
855
- }
844
+ if (isUnsafeKey(first))
845
+ continue;
846
+ const list = nestedKeys.get(first);
847
+ if (list) {
848
+ list.push(rest.join('.'));
856
849
  }
850
+ else {
851
+ nestedKeys.set(first, [rest.join('.')]);
852
+ }
853
+ }
854
+ else {
855
+ directKeys.add(key);
857
856
  }
858
- else if (key in obj) {
857
+ }
858
+ const result = {};
859
+ for (const key of directKeys) {
860
+ if (key in obj)
859
861
  result[key] = obj[key];
862
+ }
863
+ for (const [first, restKeys] of nestedKeys) {
864
+ const val = obj[first];
865
+ if (!val || typeof val !== 'object')
866
+ continue;
867
+ if (Array.isArray(val)) {
868
+ result[first] = val.map(item => pick(item, restKeys));
869
+ }
870
+ else {
871
+ result[first] = pick(val, restKeys);
860
872
  }
861
873
  }
862
874
  return result;