@hasna/todos 0.6.0 → 0.7.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/dist/index.js CHANGED
@@ -207,6 +207,33 @@ var MIGRATIONS = [
207
207
  );
208
208
 
209
209
  INSERT OR IGNORE INTO _migrations (id) VALUES (6);
210
+ `,
211
+ `
212
+ CREATE TABLE IF NOT EXISTS billing_customers (
213
+ id TEXT PRIMARY KEY,
214
+ stripe_customer_id TEXT UNIQUE,
215
+ email TEXT,
216
+ name TEXT,
217
+ plan TEXT NOT NULL DEFAULT 'free' CHECK(plan IN ('free', 'pro', 'team', 'enterprise')),
218
+ stripe_subscription_id TEXT,
219
+ subscription_status TEXT DEFAULT 'active' CHECK(subscription_status IN ('active', 'past_due', 'canceled', 'trialing', 'incomplete')),
220
+ current_period_end TEXT,
221
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
222
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
223
+ );
224
+ CREATE INDEX IF NOT EXISTS idx_billing_stripe ON billing_customers(stripe_customer_id);
225
+
226
+ CREATE TABLE IF NOT EXISTS usage_records (
227
+ id TEXT PRIMARY KEY,
228
+ customer_id TEXT REFERENCES billing_customers(id),
229
+ metric TEXT NOT NULL,
230
+ count INTEGER NOT NULL DEFAULT 0,
231
+ period TEXT NOT NULL,
232
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
233
+ );
234
+ CREATE INDEX IF NOT EXISTS idx_usage_customer ON usage_records(customer_id, period);
235
+
236
+ INSERT OR IGNORE INTO _migrations (id) VALUES (7);
210
237
  `
211
238
  ];
212
239
  var _db = null;
@@ -1648,13 +1675,70 @@ async function dispatchWebhooks(event, payload, db) {
1648
1675
  } catch {}
1649
1676
  }
1650
1677
  }
1678
+ // src/db/billing.ts
1679
+ function getOrCreateCustomer(db) {
1680
+ const d = db || getDatabase();
1681
+ const existing = d.query("SELECT * FROM billing_customers LIMIT 1").get();
1682
+ if (existing)
1683
+ return existing;
1684
+ const id = uuid();
1685
+ const timestamp = now();
1686
+ d.run(`INSERT INTO billing_customers (id, plan, created_at, updated_at) VALUES (?, 'free', ?, ?)`, [id, timestamp, timestamp]);
1687
+ return d.query("SELECT * FROM billing_customers WHERE id = ?").get(id);
1688
+ }
1689
+ function updateCustomer(id, input, db) {
1690
+ const d = db || getDatabase();
1691
+ const sets = ["updated_at = ?"];
1692
+ const params = [now()];
1693
+ for (const [key, value] of Object.entries(input)) {
1694
+ if (value !== undefined) {
1695
+ sets.push(`${key} = ?`);
1696
+ params.push(value);
1697
+ }
1698
+ }
1699
+ params.push(id);
1700
+ d.run(`UPDATE billing_customers SET ${sets.join(", ")} WHERE id = ?`, params);
1701
+ return d.query("SELECT * FROM billing_customers WHERE id = ?").get(id);
1702
+ }
1703
+ function getCustomerByStripeId(stripeCustomerId, db) {
1704
+ const d = db || getDatabase();
1705
+ return d.query("SELECT * FROM billing_customers WHERE stripe_customer_id = ?").get(stripeCustomerId);
1706
+ }
1707
+ function getUsage(customerId, period, db) {
1708
+ const d = db || getDatabase();
1709
+ return d.query("SELECT * FROM usage_records WHERE customer_id = ? AND period = ?").all(customerId, period);
1710
+ }
1711
+ function trackUsage(customerId, metric, count = 1, db) {
1712
+ const d = db || getDatabase();
1713
+ const period = new Date().toISOString().slice(0, 7);
1714
+ const existing = d.query("SELECT * FROM usage_records WHERE customer_id = ? AND metric = ? AND period = ?").get(customerId, metric, period);
1715
+ if (existing) {
1716
+ d.run("UPDATE usage_records SET count = count + ? WHERE id = ?", [count, existing.id]);
1717
+ } else {
1718
+ d.run("INSERT INTO usage_records (id, customer_id, metric, count, period, created_at) VALUES (?, ?, ?, ?, ?, ?)", [uuid(), customerId, metric, count, period, now()]);
1719
+ }
1720
+ }
1721
+ var PLAN_LIMITS = {
1722
+ free: { tasks: 100, projects: 3, plans: 5, api_keys: 1, webhooks: 0 },
1723
+ pro: { tasks: 1e4, projects: 50, plans: 100, api_keys: 10, webhooks: 10 },
1724
+ team: { tasks: 1e5, projects: 500, plans: 1000, api_keys: 50, webhooks: 50 },
1725
+ enterprise: { tasks: -1, projects: -1, plans: -1, api_keys: -1, webhooks: -1 }
1726
+ };
1727
+ var PLAN_PRICES = {
1728
+ free: { monthly: 0, yearly: 0 },
1729
+ pro: { monthly: 9, yearly: 84 },
1730
+ team: { monthly: 29, yearly: 276 },
1731
+ enterprise: { monthly: 99, yearly: 948 }
1732
+ };
1651
1733
  export {
1652
1734
  validateApiKey,
1653
1735
  updateTask,
1654
1736
  updateSessionActivity,
1655
1737
  updateProject,
1656
1738
  updatePlan,
1739
+ updateCustomer,
1657
1740
  unlockTask,
1741
+ trackUsage,
1658
1742
  syncWithAgents,
1659
1743
  syncWithAgent,
1660
1744
  startTask,
@@ -1675,6 +1759,7 @@ export {
1675
1759
  listApiKeys,
1676
1760
  hasAnyApiKeys,
1677
1761
  getWebhook,
1762
+ getUsage,
1678
1763
  getTaskWithRelations,
1679
1764
  getTaskDependents,
1680
1765
  getTaskDependencies,
@@ -1683,7 +1768,9 @@ export {
1683
1768
  getProjectByPath,
1684
1769
  getProject,
1685
1770
  getPlan,
1771
+ getOrCreateCustomer,
1686
1772
  getDatabase,
1773
+ getCustomerByStripeId,
1687
1774
  getComment,
1688
1775
  getAuditLog,
1689
1776
  ensureProject,
@@ -1713,6 +1800,8 @@ export {
1713
1800
  ProjectNotFoundError,
1714
1801
  PlanNotFoundError,
1715
1802
  PLAN_STATUSES,
1803
+ PLAN_PRICES,
1804
+ PLAN_LIMITS,
1716
1805
  LockError,
1717
1806
  DependencyCycleError
1718
1807
  };
package/dist/mcp/index.js CHANGED
@@ -4249,6 +4249,33 @@ var MIGRATIONS = [
4249
4249
  );
4250
4250
 
4251
4251
  INSERT OR IGNORE INTO _migrations (id) VALUES (6);
4252
+ `,
4253
+ `
4254
+ CREATE TABLE IF NOT EXISTS billing_customers (
4255
+ id TEXT PRIMARY KEY,
4256
+ stripe_customer_id TEXT UNIQUE,
4257
+ email TEXT,
4258
+ name TEXT,
4259
+ plan TEXT NOT NULL DEFAULT 'free' CHECK(plan IN ('free', 'pro', 'team', 'enterprise')),
4260
+ stripe_subscription_id TEXT,
4261
+ subscription_status TEXT DEFAULT 'active' CHECK(subscription_status IN ('active', 'past_due', 'canceled', 'trialing', 'incomplete')),
4262
+ current_period_end TEXT,
4263
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
4264
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
4265
+ );
4266
+ CREATE INDEX IF NOT EXISTS idx_billing_stripe ON billing_customers(stripe_customer_id);
4267
+
4268
+ CREATE TABLE IF NOT EXISTS usage_records (
4269
+ id TEXT PRIMARY KEY,
4270
+ customer_id TEXT REFERENCES billing_customers(id),
4271
+ metric TEXT NOT NULL,
4272
+ count INTEGER NOT NULL DEFAULT 0,
4273
+ period TEXT NOT NULL,
4274
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
4275
+ );
4276
+ CREATE INDEX IF NOT EXISTS idx_usage_customer ON usage_records(customer_id, period);
4277
+
4278
+ INSERT OR IGNORE INTO _migrations (id) VALUES (7);
4252
4279
  `
4253
4280
  ];
4254
4281
  var _db = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -49,6 +49,7 @@
49
49
  "commander": "^13.1.0",
50
50
  "ink": "^5.2.0",
51
51
  "react": "^18.3.1",
52
+ "stripe": "^20.3.1",
52
53
  "zod": "^3.24.2"
53
54
  },
54
55
  "devDependencies": {