@slothmoney/agent-cli 0.3.0 → 0.4.0

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Add the read-only `sloth-agent accounts` command with strict runtime
6
+ validation for opaque references, ownership, native balances, sources, and
7
+ freshness metadata.
8
+ - Prepare 0.4.0 by removing the obsolete `joint-budget-settings` command. Shared
9
+ personal transactions now enter the joint budget through their assignment.
10
+ - Verify each trusted npm release from a fresh temporary directory so the
11
+ registry smoke test cannot resolve a repository-local CLI executable.
12
+
13
+ ## 0.3.1 - 2026-07-31
14
+
15
+ - Coordinate transaction reads with the Sloth Budget daily refresh process.
16
+ - Wait up to 45 seconds for fresh persisted data, then return readable cached
17
+ transactions with structured refresh status when work continues or fails.
18
+ - Validate the additive transaction refresh response contract.
19
+
5
20
  ## 0.3.0 - 2026-07-30
6
21
 
7
22
  - Add command-specific help for every command and auth subcommand, including
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Sloth Agent CLI
2
2
 
3
- Use your own agent to manage goals and categorise transactions through the
3
+ Use your own agent to inspect known accounts, manage goals, and categorise transactions through the
4
4
  [Sloth Money Agent API](https://slothmoney.app/developers/).
5
5
 
6
6
  ## Install
@@ -15,7 +15,7 @@ sloth-agent --version
15
15
  For a one-off pinned run:
16
16
 
17
17
  ```bash
18
- npm exec --yes --package=@slothmoney/agent-cli@0.3.0 -- sloth-agent --help
18
+ npm exec --yes --package=@slothmoney/agent-cli@0.4.0 -- sloth-agent --help
19
19
  ```
20
20
 
21
21
  ## Authenticate
@@ -96,10 +96,10 @@ options, output, and examples:
96
96
 
97
97
  ```bash
98
98
  sloth-agent auth login --help
99
+ sloth-agent accounts --help
99
100
  sloth-agent categories --help
100
101
  sloth-agent transactions --help
101
102
  sloth-agent assign --help
102
- sloth-agent joint-budget-settings --help
103
103
  sloth-agent goals create --help
104
104
  sloth-agent goals update --help
105
105
  sloth-agent ask-partner --help
@@ -174,6 +174,20 @@ query. Assignments do not create a separate list.
174
174
 
175
175
  ### Other workflows
176
176
 
177
+ Read the existing Sloth account inventory:
178
+
179
+ ```bash
180
+ sloth-agent accounts
181
+ ```
182
+
183
+ The command is read-only and cache-only: it does not refresh linked banks or
184
+ change account data. Each result contains an opaque `accountRef`, personal or
185
+ joint ownership, connected or manual source, native balance/currency when
186
+ known, `lastBalanceUpdatedAt`, and `connectionState`. Missing values are JSON
187
+ `null`; currencies are never converted or combined. Partner personal accounts
188
+ are excluded, while enabled shared joint accounts follow Sloth's existing
189
+ visibility rules.
190
+
177
191
  List your goals:
178
192
 
179
193
  ```bash
@@ -221,19 +235,35 @@ Read uncategorised contributions to the joint budget:
221
235
  sloth-agent transactions --assignment-scope joint --uncategorized
222
236
  ```
223
237
 
238
+ The first transaction read after the UTC day changes may refresh linked bank
239
+ data. The CLI waits up to 45 seconds for that refresh to persist, then returns
240
+ the requested booked transactions. If the refresh is still running, partially
241
+ fails, or fails globally, readable cached transactions are still returned with
242
+ a structured `refresh` object:
243
+
244
+ ```json
245
+ {
246
+ "refresh": {
247
+ "status": "in_progress",
248
+ "reason": "wait_timeout",
249
+ "utcDate": "2026-07-31"
250
+ }
251
+ }
252
+ ```
253
+
254
+ Re-run the transaction query later to observe the completed refresh. A partial
255
+ account failure remains eligible for an automatic retry.
256
+
224
257
  Set `"assignmentScope": "joint"` on an assignment to categorise the eligible
225
258
  shared portion for the joint budget.
226
259
 
227
- Set whether the shared portions of personal-account transactions count in the
228
- linked joint budget. The first command previews; the second applies:
260
+ Transaction reads expose `personalBudgetAmountPence` for the caller's explicit
261
+ personal-only portion and `jointBudgetContribution.amountPence` for the full
262
+ shared portion. The 60/40 settlement ratio does not reduce joint-budget spend.
229
263
 
230
- ```bash
231
- sloth-agent joint-budget-settings \
232
- --include-shared-personal-transactions=true
233
- sloth-agent joint-budget-settings \
234
- --include-shared-personal-transactions=true \
235
- --apply
236
- ```
264
+ Shared personal-account transactions with a joint assignment are included in
265
+ the joint budget automatically. The settlement ratio remains independent from
266
+ the amount attributed to the joint budget.
237
267
 
238
268
  Create a partner clarification link:
239
269
 
@@ -277,3 +307,15 @@ npm run verify
277
307
 
278
308
  `npm run test:package` packs the exact npm artifact, installs it into a clean
279
309
  temporary project, and runs the installed binary.
310
+
311
+ ## Releasing
312
+
313
+ Releases are published only through the trusted `Publish npm release` GitHub
314
+ workflow from a reviewed `v*` tag whose version matches `package.json`. The
315
+ workflow runs the full verification suite before publishing.
316
+
317
+ After npm accepts the package, the workflow verifies the exact published
318
+ version with `npm run test:registry -- VERSION`. That script runs `npm exec`
319
+ from a fresh temporary directory with an isolated npm cache, so a checkout's
320
+ older local `sloth-agent` executable cannot satisfy the registry smoke test.
321
+ The temporary directory is removed after the check.
package/dist/args.js CHANGED
@@ -411,10 +411,10 @@ function helpTopic(argv) {
411
411
  return 'goals-delete';
412
412
  return 'goals';
413
413
  }
414
- if (command === 'categories'
414
+ if (command === 'accounts'
415
+ || command === 'categories'
415
416
  || command === 'transactions'
416
417
  || command === 'assign'
417
- || command === 'joint-budget-settings'
418
418
  || command === 'ask-partner') {
419
419
  return command;
420
420
  }
@@ -443,6 +443,12 @@ export function parseArgs(argv) {
443
443
  }
444
444
  return withBaseUrl({ command }, baseUrl);
445
445
  }
446
+ if (command === 'accounts') {
447
+ if (args.length > 0) {
448
+ throw new UsageError(`Unknown accounts option: ${args[0]}`);
449
+ }
450
+ return withBaseUrl({ command }, baseUrl);
451
+ }
446
452
  if (command === 'transactions') {
447
453
  return withBaseUrl({ command, filters: parseTransactions(args) }, baseUrl);
448
454
  }
@@ -471,43 +477,6 @@ export function parseArgs(argv) {
471
477
  throw new UsageError('assign requires --input <file>');
472
478
  return withBaseUrl({ command, input, apply }, baseUrl);
473
479
  }
474
- if (command === 'joint-budget-settings') {
475
- let includeSharedPersonalTransactions;
476
- let apply = false;
477
- for (let index = 0; index < args.length; index += 1) {
478
- const argument = args[index];
479
- if (argument === '--apply') {
480
- if (apply)
481
- throw new UsageError('--apply may only be provided once');
482
- apply = true;
483
- continue;
484
- }
485
- const optionName = '--include-shared-personal-transactions';
486
- if (argument === optionName || argument.startsWith(`${optionName}=`)) {
487
- const value = argument === optionName
488
- ? readOptionValue(args, index, optionName)
489
- : argument.slice(`${optionName}=`.length);
490
- if (argument === optionName)
491
- index += 1;
492
- if (value !== 'true' && value !== 'false') {
493
- throw new UsageError(`${optionName} must be true or false`);
494
- }
495
- includeSharedPersonalTransactions = setOnce(includeSharedPersonalTransactions, value === 'true', optionName);
496
- continue;
497
- }
498
- throw new UsageError(`Unknown joint-budget-settings option: ${argument}`);
499
- }
500
- if (apply && includeSharedPersonalTransactions === undefined) {
501
- throw new UsageError('--apply requires --include-shared-personal-transactions=true|false');
502
- }
503
- return withBaseUrl({
504
- command,
505
- ...(includeSharedPersonalTransactions === undefined
506
- ? {}
507
- : { includeSharedPersonalTransactions }),
508
- apply,
509
- }, baseUrl);
510
- }
511
480
  if (command === 'ask-partner') {
512
481
  let transactionRef;
513
482
  for (let index = 0; index < args.length; index += 1) {
package/dist/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import fs from 'node:fs';
2
2
  import { parseArgs, resolveBaseUrl, } from './args.js';
3
- import { parseApiResponse, validateAssignmentPayload, validateJointBudgetSettingsResponse, } from './contracts.js';
3
+ import { parseApiResponse, validateAssignmentPayload, } from './contracts.js';
4
4
  import { createSystemCredentialStore, secureStorageUnavailableError, } from './credential-store.js';
5
5
  import { ApiError, CliError, ConfigError, UsageError, } from './errors.js';
6
- export const CLI_VERSION = '0.3.0';
7
- const REQUEST_TIMEOUT_MS = 30_000;
6
+ export const CLI_VERSION = '0.4.0';
7
+ const REQUEST_TIMEOUT_MS = 60_000;
8
8
  const API_ORIGIN_HELP_LINES = [
9
9
  '',
10
10
  'API origin:',
@@ -24,12 +24,12 @@ export function usageText() {
24
24
  ' sloth-agent auth login [--token-stdin | --from-env] [--base-url URL]',
25
25
  ' sloth-agent auth status [--base-url URL]',
26
26
  ' sloth-agent auth logout [--base-url URL]',
27
+ ' sloth-agent accounts [--base-url URL]',
27
28
  ' sloth-agent categories [--base-url URL]',
28
29
  ' sloth-agent transactions [--uncategorized[=true|false]] [--limit N]',
29
30
  ' [--start-date YYYY-MM-DD] [--end-date YYYY-MM-DD] [--q TEXT]',
30
31
  ' [--account-id ID] [--category-id ID] [--cursor CURSOR] [--base-url URL]',
31
32
  ' sloth-agent assign --input assignments.json [--apply] [--base-url URL]',
32
- ' sloth-agent joint-budget-settings [--include-shared-personal-transactions=true|false] [--apply]',
33
33
  ' sloth-agent goals [list] [--base-url URL]',
34
34
  ' sloth-agent goals create --name NAME [--target-amount AMOUNT]',
35
35
  ' [--target-month YYYY-MM] [--apply] [--base-url URL]',
@@ -182,6 +182,31 @@ export function categoriesHelpText() {
182
182
  ' jointLineItemsByCategoryId Joint child line items keyed by category ID',
183
183
  ].join('\n');
184
184
  }
185
+ export function accountsHelpText() {
186
+ return [
187
+ 'Sloth Agent CLI — accounts',
188
+ '',
189
+ 'Read the existing Sloth account inventory known to the authenticated user.',
190
+ '',
191
+ 'Usage:',
192
+ ' sloth-agent accounts [--base-url URL]',
193
+ '',
194
+ 'Options:',
195
+ ' --base-url URL Optional. Override the API origin.',
196
+ ' -h, --help Show this help.',
197
+ ...API_ORIGIN_HELP_LINES,
198
+ '',
199
+ 'Access:',
200
+ ' This command is read-only and does not refresh connected accounts.',
201
+ '',
202
+ 'Output:',
203
+ ' asOf Server response time',
204
+ ' accounts[].accountRef Opaque stable account reference',
205
+ ' accounts[].ownership personal or joint',
206
+ ' accounts[].balanceAmount and currency in the native currency when known',
207
+ ' accounts[].connectionState and lastBalanceUpdatedAt for freshness',
208
+ ].join('\n');
209
+ }
185
210
  export function transactionsHelpText() {
186
211
  return [
187
212
  'Sloth Agent CLI — transactions',
@@ -208,10 +233,14 @@ export function transactionsHelpText() {
208
233
  'Constraints:',
209
234
  ' All filters are omitted by default.',
210
235
  ' --end-date must not be before --start-date.',
211
- ' This command is read-only.',
236
+ ' The first transaction read each UTC day may refresh linked bank data.',
237
+ ' Refresh remotely persists booked transactions and account balances.',
238
+ ' The command waits up to 45 seconds, then returns cached data if refresh continues.',
212
239
  '',
213
240
  'Output:',
214
- ' JSON containing transactions and nextCursor. Use nextCursor with --cursor',
241
+ ' JSON containing transactions, nextCursor, and structured refresh status.',
242
+ ' Refresh failures do not hide readable cached transactions.',
243
+ ' Use nextCursor with --cursor',
215
244
  ' to request the next page. A null nextCursor means there are no more pages.',
216
245
  '',
217
246
  'Examples:',
@@ -284,37 +313,6 @@ export function assignHelpText() {
284
313
  ' Assignments do not create a separate list.',
285
314
  ].join('\n');
286
315
  }
287
- export function jointBudgetSettingsHelpText() {
288
- return [
289
- 'Sloth Agent CLI — joint-budget-settings',
290
- '',
291
- 'Read or update whether shared personal transactions count in the linked joint budget.',
292
- '',
293
- 'Usage:',
294
- ' sloth-agent joint-budget-settings [options]',
295
- '',
296
- 'Options:',
297
- ' --include-shared-personal-transactions=true|false',
298
- ' Optional. Preview the linked setting change.',
299
- ' --apply Optional. Apply the previewed setting change.',
300
- ' --base-url URL Optional. Override the API origin.',
301
- ' -h, --help Show this help.',
302
- ...API_ORIGIN_HELP_LINES,
303
- '',
304
- 'Safety:',
305
- ' With no setting option, the command is read-only.',
306
- ' Without --apply, a setting option returns a dry-run preview and does not write.',
307
- ' --apply requires an explicit true or false setting value.',
308
- '',
309
- 'Examples:',
310
- ' sloth-agent joint-budget-settings',
311
- ' sloth-agent joint-budget-settings --include-shared-personal-transactions=true',
312
- ' sloth-agent joint-budget-settings --include-shared-personal-transactions=true --apply',
313
- '',
314
- 'Output:',
315
- ' JSON containing the linked setting, audit metadata, or a dry-run payload.',
316
- ].join('\n');
317
- }
318
316
  export function goalsHelpText() {
319
317
  return [
320
318
  'Sloth Agent CLI — goals',
@@ -485,10 +483,10 @@ export function commandHelpText(topic) {
485
483
  'auth-login': authLoginHelpText,
486
484
  'auth-status': authStatusHelpText,
487
485
  'auth-logout': authLogoutHelpText,
486
+ accounts: accountsHelpText,
488
487
  categories: categoriesHelpText,
489
488
  transactions: transactionsHelpText,
490
489
  assign: assignHelpText,
491
- 'joint-budget-settings': jointBudgetSettingsHelpText,
492
490
  goals: goalsHelpText,
493
491
  'goals-list': goalsListHelpText,
494
492
  'goals-create': goalsCreateHelpText,
@@ -843,36 +841,6 @@ export async function runCli(argv = process.argv.slice(2), options = {}) {
843
841
  writeJson(writeStdout, data);
844
842
  return hasFailures(data) ? 1 : 0;
845
843
  }
846
- if (parsed.command === 'joint-budget-settings') {
847
- const endpoint = `${baseUrl}/api/agent/v1/joint-budget-settings`;
848
- if (parsed.includeSharedPersonalTransactions !== undefined && !parsed.apply) {
849
- writeJson(writeStdout, {
850
- dryRun: true,
851
- endpoint,
852
- payload: {
853
- includeSharedPersonalTransactions: parsed.includeSharedPersonalTransactions,
854
- },
855
- });
856
- return 0;
857
- }
858
- const response = await fetchImplementation(endpoint, {
859
- method: parsed.apply ? 'PUT' : 'GET',
860
- headers: parsed.apply
861
- ? { ...headers, 'Content-Type': 'application/json' }
862
- : headers,
863
- ...(parsed.apply
864
- ? {
865
- body: JSON.stringify({
866
- includeSharedPersonalTransactions: parsed.includeSharedPersonalTransactions,
867
- }),
868
- }
869
- : {}),
870
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
871
- });
872
- const data = validateJointBudgetSettingsResponse(await parseHttpResponse(response, token));
873
- writeJson(writeStdout, data);
874
- return 0;
875
- }
876
844
  if (parsed.command === 'ask-partner') {
877
845
  const response = await fetchImplementation(`${baseUrl}/api/agent/v1/transaction-explanation-requests`, {
878
846
  method: 'POST',
@@ -897,15 +865,19 @@ export async function runCli(argv = process.argv.slice(2), options = {}) {
897
865
  writeJson(writeStdout, data);
898
866
  return 0;
899
867
  }
900
- const path = parsed.command === 'categories'
901
- ? '/api/agent/v1/categories'
902
- : `/api/agent/v1/transactions${(() => {
903
- const query = buildTransactionsQuery(parsed.filters);
904
- return query ? `?${query}` : '';
905
- })()}`;
868
+ const path = parsed.command === 'accounts'
869
+ ? '/api/agent/v1/accounts'
870
+ : parsed.command === 'categories'
871
+ ? '/api/agent/v1/categories'
872
+ : `/api/agent/v1/transactions${(() => {
873
+ const query = buildTransactionsQuery(parsed.filters);
874
+ return query ? `?${query}` : '';
875
+ })()}`;
906
876
  const response = await fetchImplementation(`${baseUrl}${path}`, {
907
877
  method: 'GET',
908
- headers,
878
+ headers: parsed.command === 'transactions'
879
+ ? { ...headers, Prefer: 'wait=45' }
880
+ : headers,
909
881
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
910
882
  });
911
883
  const data = parseApiResponse(parsed.command, await parseHttpResponse(response, token));
package/dist/contracts.js CHANGED
@@ -2,6 +2,12 @@ import { ApiError, UsageError, } from './errors.js';
2
2
  function isObject(value) {
3
3
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
4
4
  }
5
+ function isIsoDate(value) {
6
+ if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value))
7
+ return false;
8
+ const parsed = new Date(`${value}T00:00:00.000Z`);
9
+ return !Number.isNaN(parsed.valueOf()) && parsed.toISOString().slice(0, 10) === value;
10
+ }
5
11
  function requireObject(value, label) {
6
12
  if (!isObject(value))
7
13
  throw new UsageError(`${label} must be an object`);
@@ -139,6 +145,8 @@ function isTransaction(value) {
139
145
  && (value.categoryId === null || typeof value.categoryId === 'string')
140
146
  && (value.lineItemId === null || typeof value.lineItemId === 'string')
141
147
  && Array.isArray(value.categorySplits)
148
+ && Number.isInteger(value.personalBudgetAmountPence)
149
+ && Number(value.personalBudgetAmountPence) >= 0
142
150
  && (value.jointBudgetContribution === null
143
151
  || (isObject(value.jointBudgetContribution)
144
152
  && typeof value.jointBudgetContribution.eligible === 'boolean'
@@ -158,10 +166,27 @@ function isTransaction(value) {
158
166
  || value.incomeSubtype === 'interest'));
159
167
  }
160
168
  function isTransactionsResponse(value) {
169
+ const validStatuses = new Set(['skipped', 'completed', 'in_progress', 'partial', 'failed']);
170
+ const validReasons = new Set([
171
+ 'all_fetched_today',
172
+ 'no_api_connections',
173
+ 'no_selected_accounts',
174
+ 'refreshed',
175
+ 'wait_timeout',
176
+ 'account_failures',
177
+ 'refresh_error',
178
+ ]);
179
+ const refresh = isObject(value) ? value.refresh : undefined;
161
180
  return (isObject(value)
162
181
  && Array.isArray(value.transactions)
163
182
  && value.transactions.every(isTransaction)
164
- && (value.nextCursor === null || typeof value.nextCursor === 'string'));
183
+ && (value.nextCursor === null || typeof value.nextCursor === 'string')
184
+ && isObject(refresh)
185
+ && typeof refresh.status === 'string'
186
+ && validStatuses.has(refresh.status)
187
+ && typeof refresh.reason === 'string'
188
+ && validReasons.has(refresh.reason)
189
+ && isIsoDate(refresh.utcDate));
165
190
  }
166
191
  function isAssignmentResponse(value) {
167
192
  return (isObject(value)
@@ -174,20 +199,6 @@ function isAssignmentResponse(value) {
174
199
  && typeof item.error === 'string'
175
200
  && (item.transactionRef === undefined || typeof item.transactionRef === 'string'))));
176
201
  }
177
- export function validateJointBudgetSettingsResponse(value) {
178
- if (!isObject(value)
179
- || Object.keys(value).some((key) => !new Set([
180
- 'includeSharedPersonalTransactions',
181
- 'updatedAt',
182
- 'updatedBy',
183
- ]).has(key))
184
- || typeof value.includeSharedPersonalTransactions !== 'boolean'
185
- || (value.updatedAt !== null && !isIsoDateTime(value.updatedAt))
186
- || (value.updatedBy !== null && typeof value.updatedBy !== 'string')) {
187
- throw new ApiError('Invalid joint budget settings response from the Agent API');
188
- }
189
- return value;
190
- }
191
202
  function isHttpUrl(value) {
192
203
  if (typeof value !== 'string')
193
204
  return false;
@@ -243,6 +254,51 @@ function isGoal(value) {
243
254
  function isCurrency(value) {
244
255
  return typeof value === 'string' && /^[A-Z]{3}$/.test(value);
245
256
  }
257
+ function isNullableNonEmptyString(value) {
258
+ return value === null || (typeof value === 'string'
259
+ && value.length > 0
260
+ && value === value.trim());
261
+ }
262
+ function isAccount(value) {
263
+ return (isObject(value)
264
+ && hasOnlyFields(value, [
265
+ 'accountRef',
266
+ 'accountName',
267
+ 'institutionName',
268
+ 'accountType',
269
+ 'ownership',
270
+ 'balanceAmount',
271
+ 'currency',
272
+ 'source',
273
+ 'lastBalanceUpdatedAt',
274
+ 'connectionState',
275
+ ])
276
+ && typeof value.accountRef === 'string'
277
+ && /^sloth_account_v1_[A-Za-z0-9_-]{43}$/.test(value.accountRef)
278
+ && isNullableNonEmptyString(value.accountName)
279
+ && isNullableNonEmptyString(value.institutionName)
280
+ && (value.accountType === 'current'
281
+ || value.accountType === 'savings'
282
+ || value.accountType === 'investments')
283
+ && (value.ownership === 'personal' || value.ownership === 'joint')
284
+ && (value.balanceAmount === null
285
+ || (typeof value.balanceAmount === 'number' && Number.isFinite(value.balanceAmount)))
286
+ && (value.currency === null || isCurrency(value.currency))
287
+ && (value.source === 'connected' || value.source === 'manual')
288
+ && (value.lastBalanceUpdatedAt === null
289
+ || isIsoDateTime(value.lastBalanceUpdatedAt))
290
+ && (value.connectionState === 'active'
291
+ || value.connectionState === 'expired'
292
+ || value.connectionState === 'manual'
293
+ || value.connectionState === 'unknown'));
294
+ }
295
+ function isAccountsResponse(value) {
296
+ return (isObject(value)
297
+ && hasOnlyFields(value, ['asOf', 'accounts'])
298
+ && isIsoDateTime(value.asOf)
299
+ && Array.isArray(value.accounts)
300
+ && value.accounts.every(isAccount));
301
+ }
246
302
  function isGoalsResponse(value) {
247
303
  return (isObject(value)
248
304
  && hasOnlyFields(value, ['currency', 'goals'])
@@ -264,19 +320,21 @@ function isGoalDeleteResponse(value) {
264
320
  && value.deletedGoalId.trim().length > 0);
265
321
  }
266
322
  export function parseApiResponse(command, value) {
267
- const valid = command === 'categories'
268
- ? isCategoryResponse(value)
269
- : command === 'transactions'
270
- ? isTransactionsResponse(value)
271
- : command === 'assign'
272
- ? isAssignmentResponse(value)
273
- : command === 'ask-partner'
274
- ? isPartnerResponse(value)
275
- : command === 'goals-list'
276
- ? isGoalsResponse(value)
277
- : command === 'goals-delete'
278
- ? isGoalDeleteResponse(value)
279
- : isGoalMutationResponse(value);
323
+ const valid = command === 'accounts'
324
+ ? isAccountsResponse(value)
325
+ : command === 'categories'
326
+ ? isCategoryResponse(value)
327
+ : command === 'transactions'
328
+ ? isTransactionsResponse(value)
329
+ : command === 'assign'
330
+ ? isAssignmentResponse(value)
331
+ : command === 'ask-partner'
332
+ ? isPartnerResponse(value)
333
+ : command === 'goals-list'
334
+ ? isGoalsResponse(value)
335
+ : command === 'goals-delete'
336
+ ? isGoalDeleteResponse(value)
337
+ : isGoalMutationResponse(value);
280
338
  if (!valid) {
281
339
  const label = command === 'assign' ? 'assignment' : command;
282
340
  throw new ApiError(`Invalid ${label} response from the Agent API`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slothmoney/agent-cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Command-line access to the Sloth Money Agent API.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,6 +16,7 @@
16
16
  "prepack": "npm run build",
17
17
  "test": "vitest run",
18
18
  "test:package": "node scripts/test-package.mjs",
19
+ "test:registry": "node scripts/test-registry-package.mjs",
19
20
  "typecheck": "tsc --noEmit",
20
21
  "verify": "npm run lint && npm run typecheck && npm test && npm run build && npm run test:package"
21
22
  },