@moneysiren/cli 0.1.0-alpha.1 → 0.1.0-alpha.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 (35) hide show
  1. package/README.md +2 -2
  2. package/dist/apps/cli/src/cli.d.ts +3 -0
  3. package/dist/apps/cli/src/cli.js +9 -2
  4. package/dist/apps/cli/src/commands/install.js +10 -1
  5. package/dist/apps/cli/src/commands/modes.js +13 -11
  6. package/dist/apps/cli/src/commands/runtime.d.ts +2 -0
  7. package/dist/apps/cli/src/commands/runtime.js +167 -0
  8. package/dist/apps/cli/src/desktop-runtime.d.ts +31 -0
  9. package/dist/apps/cli/src/desktop-runtime.js +441 -0
  10. package/dist/apps/cli/src/home.js +16 -0
  11. package/dist/apps/cli/src/postinstall.js +1 -1
  12. package/dist/apps/cli/src/release-installer.d.ts +4 -1
  13. package/dist/apps/cli/src/release-installer.js +26 -3
  14. package/dist/apps/cli/src/slash.js +12 -0
  15. package/dist/apps/cli/src/version.d.ts +1 -1
  16. package/dist/apps/cli/src/version.js +1 -1
  17. package/dist/packages/config/src/load.js +3 -0
  18. package/dist/packages/config/src/schema.d.ts +3 -0
  19. package/dist/packages/config/src/schema.js +3 -0
  20. package/dist/packages/local-api/src/server.js +1 -1
  21. package/dist/packages/view-model/src/hud-model.d.ts +74 -0
  22. package/dist/packages/view-model/src/hud-model.js +295 -0
  23. package/dist/packages/view-model/src/index.d.ts +5 -2
  24. package/dist/packages/view-model/src/index.js +4 -1
  25. package/dist/packages/view-model/src/notification-preferences-model.d.ts +30 -2
  26. package/dist/packages/view-model/src/notification-preferences-model.js +183 -1
  27. package/dist/packages/view-model/src/notification-preferences.d.ts +1 -1
  28. package/dist/packages/view-model/src/notification-preferences.js +1 -1
  29. package/dist/packages/view-model/src/sync-state.d.ts +47 -0
  30. package/dist/packages/view-model/src/sync-state.js +140 -0
  31. package/dist/packages/view-model/src/usage-progress.d.ts +22 -0
  32. package/dist/packages/view-model/src/usage-progress.js +57 -0
  33. package/dist/packages/view-model/src/view-model.d.ts +22 -0
  34. package/dist/packages/view-model/src/view-model.js +142 -0
  35. package/package.json +3 -2
@@ -4,8 +4,10 @@ const OPENAI_PROVIDER_KEY = "openai";
4
4
  const AWS_PROVIDER_KEY = "aws";
5
5
  const SUPABASE_PROVIDER_KEY = "supabase";
6
6
  const CLOUDFLARE_PROVIDER_KEY = "cloudflare";
7
+ const CODEX_APP_PROVIDER_KEY = "codex-app";
7
8
  const CODEX_CLI_PROVIDER_KEY = "codex-cli";
8
9
  const CLAUDE_CLI_PROVIDER_KEY = "claude-cli";
10
+ const MS_PER_DAY = 24 * 60 * 60 * 1000;
9
11
  const SENSITIVE_TEXT_PATTERN = /(https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9/_-]+|\b(?:sk|sbp|xox[baprs])[-_][A-Za-z0-9_-]+\b|\bacct[_-][A-Za-z0-9_-]+\b|\b(?:proj|project)[_-][A-Za-z0-9_-]+\b|\b(?:in|invoice)[_-][A-Za-z0-9_-]+\b|[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})/gi;
10
12
  const EMPTY_STORE = {
11
13
  providers: [],
@@ -115,10 +117,23 @@ export function buildTodayLiveView(store, options) {
115
117
  ...provider,
116
118
  providerKey: safeText(provider.providerKey),
117
119
  displayName: safeText(provider.displayName),
120
+ ...(provider.expiresAt === undefined ? {} : { expiresAt: provider.expiresAt }),
121
+ ...(provider.lastAttemptAt === undefined ? {} : { lastAttemptAt: provider.lastAttemptAt }),
122
+ ...(provider.lastSuccessAt === undefined ? {} : { lastSuccessAt: provider.lastSuccessAt }),
123
+ ...(provider.freshUntil === undefined ? {} : { freshUntil: provider.freshUntil }),
124
+ ...(provider.staleUntil === undefined ? {} : { staleUntil: provider.staleUntil }),
125
+ ...(provider.lastRefreshFailed === undefined ? {} : { lastRefreshFailed: provider.lastRefreshFailed }),
126
+ ...(provider.revision === undefined ? {} : { revision: provider.revision }),
127
+ ...(provider.message === undefined ? {} : { message: safeText(provider.message) }),
118
128
  metrics: (provider.metrics ?? []).map((metric) => ({
119
129
  key: safeText(metric.key),
120
130
  value: metric.value,
121
131
  unit: safeText(metric.unit),
132
+ ...(metric.resetAt === undefined ? {} : { resetAt: safeText(metric.resetAt) }),
133
+ ...(metric.resetAtLatest === undefined ? {} : { resetAtLatest: safeText(metric.resetAtLatest) }),
134
+ ...(metric.itemKey === undefined ? {} : { itemKey: safeText(metric.itemKey) }),
135
+ ...(metric.accuracy === undefined ? {} : { accuracy: metric.accuracy }),
136
+ ...(metric.source === undefined ? {} : { source: safeText(metric.source) }),
122
137
  })),
123
138
  }));
124
139
  const includedProviders = providers.filter((provider) => provider.included && provider.todayLiveAmountMinor !== null);
@@ -387,6 +402,8 @@ function buildDigestItems(overview, todayLive, alerts) {
387
402
  usedTokensMetricKey: "weekly_tokens",
388
403
  clickPath: "/ko/services/codex-cli",
389
404
  }),
405
+ codexResetCreditCountItem(todayLive),
406
+ codexResetCreditExpiryItem(todayLive),
390
407
  {
391
408
  widgetKey: "supabase_usage_health",
392
409
  kind: "health",
@@ -425,6 +442,13 @@ function cliRemainingPercentItem(options) {
425
442
  severity: remainingPercentSeverity(percent),
426
443
  label: options.label,
427
444
  value: percent === null ? "Not available" : formatPercent(percent),
445
+ ...(percent === null ? {} : {
446
+ numericValue: percent,
447
+ unit: "percent",
448
+ remainingPercent: percent,
449
+ usedPercent: clampPercent(100 - percent),
450
+ }),
451
+ providerKey: options.providerKey,
428
452
  ...(firstProvider === undefined
429
453
  ? {}
430
454
  : {
@@ -434,6 +458,68 @@ function cliRemainingPercentItem(options) {
434
458
  clickPath: options.clickPath,
435
459
  };
436
460
  }
461
+ function codexResetCreditCountItem(todayLive) {
462
+ const providers = [
463
+ ...todayProviders(todayLive, CODEX_APP_PROVIDER_KEY),
464
+ ...todayProviders(todayLive, CODEX_CLI_PROVIDER_KEY),
465
+ ];
466
+ const firstProvider = providers[0];
467
+ const metricEntry = firstMetricEntry(providers, "usage_reset_credits");
468
+ const count = metricEntry?.metric.value ?? null;
469
+ const clickProviderKey = metricEntry?.provider.providerKey ?? CODEX_CLI_PROVIDER_KEY;
470
+ return {
471
+ widgetKey: "codex_reset_credit_count",
472
+ kind: "usage",
473
+ severity: "info",
474
+ label: "Codex reset credits",
475
+ value: count === null ? "Not available" : formatCount(count),
476
+ ...(count === null ? {} : {
477
+ numericValue: count,
478
+ unit: "count",
479
+ }),
480
+ ...(metricEntry === undefined ? {} : { providerKey: metricEntry.provider.providerKey }),
481
+ ...(firstProvider === undefined
482
+ ? {}
483
+ : {
484
+ freshness: firstProvider.freshness,
485
+ confidence: firstProvider.confidence,
486
+ }),
487
+ clickPath: `/ko/services/${clickProviderKey}`,
488
+ };
489
+ }
490
+ function codexResetCreditExpiryItem(todayLive) {
491
+ const providers = [
492
+ ...todayProviders(todayLive, CODEX_APP_PROVIDER_KEY),
493
+ ...todayProviders(todayLive, CODEX_CLI_PROVIDER_KEY),
494
+ ];
495
+ const firstProvider = providers[0];
496
+ const exactExpiry = earliestMetricResetAt(providers, "usage_reset_credit");
497
+ const estimatedExpiry = earliestMetricResetAt(providers, "usage_reset_credit_estimate");
498
+ const earliestExpiry = exactExpiry ?? estimatedExpiry;
499
+ const metric = earliestResetCreditMetric(providers, exactExpiry === null ? "usage_reset_credit_estimate" : "usage_reset_credit", earliestExpiry);
500
+ const metricProviderKey = metric === undefined ? undefined : providerForMetric(providers, metric);
501
+ const daysUntil = earliestExpiry === null
502
+ ? null
503
+ : Math.ceil((Date.parse(earliestExpiry) - Date.parse(todayLive.generatedAt)) / MS_PER_DAY);
504
+ return {
505
+ widgetKey: "codex_reset_credit_expiry",
506
+ kind: "usage",
507
+ severity: resetCreditExpirySeverity(daysUntil),
508
+ label: "Codex reset credit expiry",
509
+ value: resetCreditExpiryValue(daysUntil),
510
+ ...(earliestExpiry === null ? {} : { resetAt: earliestExpiry }),
511
+ ...(metric?.resetAtLatest === undefined ? {} : { resetAtLatest: metric.resetAtLatest }),
512
+ ...(metric?.accuracy === undefined ? {} : { accuracy: metric.accuracy }),
513
+ ...(metricProviderKey === undefined ? {} : { providerKey: metricProviderKey }),
514
+ ...(firstProvider === undefined
515
+ ? {}
516
+ : {
517
+ freshness: firstProvider.freshness,
518
+ confidence: firstProvider.confidence,
519
+ }),
520
+ clickPath: "/ko/services/codex-cli",
521
+ };
522
+ }
437
523
  async function resolveStore(options) {
438
524
  if (options.store !== undefined) {
439
525
  return options.store;
@@ -529,6 +615,33 @@ function metricFirst(providers, metricKey) {
529
615
  }
530
616
  return null;
531
617
  }
618
+ function firstMetricEntry(providers, metricKey) {
619
+ for (const provider of providers) {
620
+ const metric = provider.metrics.find((item) => item.key === metricKey);
621
+ if (metric !== undefined) {
622
+ return { provider, metric };
623
+ }
624
+ }
625
+ return undefined;
626
+ }
627
+ function earliestMetricResetAt(providers, metricKey) {
628
+ const values = providers.flatMap((provider) => provider.metrics
629
+ .filter((metric) => metric.key === metricKey && metric.resetAt !== undefined)
630
+ .map((metric) => metric.resetAt)
631
+ .filter((value) => Number.isFinite(Date.parse(value))));
632
+ return values.sort((first, second) => Date.parse(first) - Date.parse(second))[0] ?? null;
633
+ }
634
+ function earliestResetCreditMetric(providers, metricKey, resetAt) {
635
+ if (resetAt === null) {
636
+ return undefined;
637
+ }
638
+ return providers
639
+ .flatMap((provider) => provider.metrics)
640
+ .find((metric) => metric.key === metricKey && metric.resetAt === resetAt);
641
+ }
642
+ function providerForMetric(providers, target) {
643
+ return providers.find((provider) => provider.metrics.some((metric) => metric === target))?.providerKey;
644
+ }
532
645
  function remainingPercentFromMetrics(providers, remainingTokensMetricKey, usedTokensMetricKey, usedPercentMetricKey) {
533
646
  const remainingTokens = metricSum(providers, remainingTokensMetricKey);
534
647
  const usedTokens = metricSum(providers, usedTokensMetricKey);
@@ -541,6 +654,30 @@ function remainingPercentFromMetrics(providers, remainingTokensMetricKey, usedTo
541
654
  const usedPercent = metricFirst(providers, usedPercentMetricKey);
542
655
  return usedPercent === null ? null : clampPercent(100 - usedPercent);
543
656
  }
657
+ function resetCreditExpirySeverity(daysUntil) {
658
+ if (daysUntil === null) {
659
+ return "info";
660
+ }
661
+ if (daysUntil <= 1) {
662
+ return "critical";
663
+ }
664
+ if (daysUntil <= 7) {
665
+ return "warning";
666
+ }
667
+ return "info";
668
+ }
669
+ function resetCreditExpiryValue(daysUntil) {
670
+ if (daysUntil === null) {
671
+ return "Not available";
672
+ }
673
+ if (daysUntil <= 0) {
674
+ return "May expire now";
675
+ }
676
+ if (daysUntil === 1) {
677
+ return "May expire within 1 day";
678
+ }
679
+ return `May expire within ${daysUntil} days`;
680
+ }
544
681
  function providerRiskSeverity(provider) {
545
682
  if (provider?.riskLevel === "critical") {
546
683
  return "critical";
@@ -662,6 +799,11 @@ function formatTokens(tokens) {
662
799
  maximumFractionDigits: 0,
663
800
  }).format(tokens);
664
801
  }
802
+ function formatCount(value) {
803
+ return new Intl.NumberFormat("en-US", {
804
+ maximumFractionDigits: 0,
805
+ }).format(value);
806
+ }
665
807
  function formatPercent(percent) {
666
808
  const rounded = Number.isInteger(percent) ? percent.toFixed(0) : percent.toFixed(1);
667
809
  return `${rounded}%`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moneysiren/cli",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.3",
4
4
  "description": "Local-first cloud/SaaS usage, status, and expected billing CLI for MoneySiren.",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -10,7 +10,8 @@
10
10
  "node": ">=20.11.0"
11
11
  },
12
12
  "bin": {
13
- "moneysiren": "dist/apps/cli/src/index.js"
13
+ "moneysiren": "dist/apps/cli/src/index.js",
14
+ "msiren": "dist/apps/cli/src/index.js"
14
15
  },
15
16
  "main": "dist/apps/cli/src/cli.js",
16
17
  "types": "dist/apps/cli/src/cli.d.ts",