@profullstack/coinpay 0.19.0 → 0.20.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/bin/coinpay.js CHANGED
@@ -682,7 +682,7 @@ ${colors.cyan}Commands:${colors.reset}
682
682
  due [--days 7] What is due and overdue across every account (--days up to 60)
683
683
  recurring Every recurring charge detected, with next date and monthly cost
684
684
  due-day <id> <1-31|clear> A card/loan's statement due day (--amount 810.46, or balance)
685
- daily [show|on|off|send-now] Daily summary email: due soon, new activity, position (--hour 7 --to …)
685
+ daily [show|on|off|send-now] Daily summary email: spend vs target, top cuts, due soon, position (--hour 7 --to …, --target 100)
686
686
 
687
687
  ${colors.bright}escrow${colors.reset}
688
688
  create Create a new escrow
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/coinpay",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "CoinPay SDK & CLI \u2014 Accept cryptocurrency and card payments (BTC, ETH, SOL, POL, BCH, USDC) with wallet, swap and a live finances dashboard",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -891,10 +891,21 @@ export async function runFinancesCommand(subcommand, args, flags, ctx) {
891
891
  const action = flags.off ? 'off' : args[0] || 'show';
892
892
  const describe = (s) => s
893
893
  ? `Daily summary ${s.active ? 'on' : 'off'} · ${String(s.hour).padStart(2, '0')}:00 ${s.timezone} · to ${s.recipients.join(', ')}${s.nextRunAt ? ` · next ${s.nextRunAt}` : ''}${s.lastSentAt ? ` · last ${s.lastSentAt}` : ''}`
894
- : 'No daily summary configured. Turn it on with: coinpay finances daily on --hour 7 --to you@example.com';
894
+ : 'No daily summary configured. Turn it on with: coinpay finances daily on --hour 7';
895
+ const targetText = (t) => (t === null || t === undefined ? 'no daily spend target' : `target $${t}/day`);
896
+ if (flags.target !== undefined) {
897
+ const raw = String(flags.target);
898
+ const target = ['off', 'clear', 'none'].includes(raw) ? null : Number(raw.replace(/^\$/, ''));
899
+ if (target !== null && (!Number.isFinite(target) || target < 0)) throw new CliExit(EXIT.INVALID, 'The target is a daily amount, e.g. --target 100, or --target clear');
900
+ const data = await api.setDailySpendTarget(client, target);
901
+ if (action === 'show') {
902
+ emit({ schedule: data.schedule, target: data.target }, `${describe(data.schedule)}\n${target === null ? 'Cleared the daily spend target.' : `Daily spend target set to $${data.target}/day.`}`);
903
+ return EXIT.OK;
904
+ }
905
+ }
895
906
  if (action === 'show') {
896
- const s = await api.getDailySummary(client);
897
- emit({ schedule: s }, describe(s));
907
+ const { schedule: s, target } = await api.getDailySummaryWithTarget(client);
908
+ emit({ schedule: s, target }, `${describe(s)} · ${targetText(target)}`);
898
909
  return EXIT.OK;
899
910
  }
900
911
  if (action === 'on' || action === 'set') {
@@ -917,7 +928,7 @@ export async function runFinancesCommand(subcommand, args, flags, ctx) {
917
928
  emit(data, `Sent to ${data.sent.join(', ') || 'nobody'} (${data.due} due, ${data.overdue} overdue)${data.failed.length ? `; failed: ${data.failed.map((f) => `${f.to} (${f.error})`).join(', ')}` : ''}`);
918
929
  return data.sent.length > 0 ? EXIT.OK : EXIT.FAILURE;
919
930
  }
920
- throw new CliExit(EXIT.INVALID, 'Usage: coinpay finances daily [show|on --hour 7 --to a@x|off|send-now]');
931
+ throw new CliExit(EXIT.INVALID, 'Usage: coinpay finances daily [show|on --hour 7 --to a@x|off|send-now] [--target 100|--target clear]');
921
932
  }
922
933
 
923
934
  case 'payloads': {
@@ -461,6 +461,12 @@ export async function getDailySummary(client) {
461
461
  return data.schedule;
462
462
  }
463
463
 
464
+ /** The daily summary schedule plus the owner's $/day spend target (null when none). */
465
+ export async function getDailySummaryWithTarget(client) {
466
+ const data = await call(client, '/finances/email-schedules?kind=daily_summary');
467
+ return { schedule: data.schedule, target: data.dailySpendTarget ?? null };
468
+ }
469
+
464
470
  export async function setDailySummary(client, { weekdays, hour, timezone, recipients, active } = {}) {
465
471
  const data = await call(client, '/finances/email-schedules', {
466
472
  method: 'POST',
@@ -469,6 +475,15 @@ export async function setDailySummary(client, { weekdays, hour, timezone, recipi
469
475
  return data.schedule;
470
476
  }
471
477
 
478
+ /** Set the daily summary's $/day spend target, or clear it with null. Does not turn the summary on. */
479
+ export async function setDailySpendTarget(client, target) {
480
+ const data = await call(client, '/finances/email-schedules', {
481
+ method: 'POST',
482
+ body: JSON.stringify({ kind: 'daily_summary', target }),
483
+ });
484
+ return { schedule: data.schedule, target: data.dailySpendTarget ?? null };
485
+ }
486
+
472
487
  export async function deleteDailySummary(client) {
473
488
  return call(client, '/finances/email-schedules?kind=daily_summary', { method: 'DELETE' });
474
489
  }
package/src/mcp.js CHANGED
@@ -95,13 +95,14 @@ export const TOOLS = [
95
95
  },
96
96
  {
97
97
  name: 'finance_daily_summary',
98
- description: 'The daily summary email (due soon, new activity, position): show it, turn it on (optionally hour 0-23 and recipients), turn it off, or send one now.',
98
+ description: 'The daily summary email (spend vs a $/day target, top cuts, due soon, new activity, position): show it, turn it on (optionally hour 0-23 and recipients), turn it off, send one now, or set_target (target in $/day, null clears).',
99
99
  inputSchema: {
100
100
  type: 'object',
101
101
  properties: {
102
- action: { type: 'string', enum: ['show', 'on', 'off', 'send_now'] },
102
+ action: { type: 'string', enum: ['show', 'on', 'off', 'send_now', 'set_target'] },
103
103
  hour: { type: 'integer', minimum: 0, maximum: 23 },
104
104
  to: { type: 'array', items: { type: 'string' }, maxItems: 5 },
105
+ target: { type: ['number', 'null'], minimum: 0 },
105
106
  },
106
107
  required: ['action'],
107
108
  additionalProperties: false,
@@ -110,7 +111,8 @@ export const TOOLS = [
110
111
  if (args.action === 'on') return text({ schedule: await reports.setDailySummary(client, { hour: args.hour, recipients: args.to, active: true }) });
111
112
  if (args.action === 'off') return text(await reports.deleteDailySummary(client));
112
113
  if (args.action === 'send_now') return text(await reports.sendDailySummaryNow(client));
113
- return text({ schedule: await reports.getDailySummary(client) });
114
+ if (args.action === 'set_target') return text(await reports.setDailySpendTarget(client, args.target ?? null));
115
+ return text(await reports.getDailySummaryWithTarget(client));
114
116
  },
115
117
  },
116
118
  {