@hasna/domains 0.0.15 → 0.0.16

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/README.md CHANGED
@@ -17,6 +17,7 @@ Domain portfolio and DNS management for AI agents — CLI + MCP server with SQLi
17
17
  - **Registrar sync** — Namecheap and GoDaddy integration
18
18
  - **Brand monitoring** — typosquat/threat detection via Brandsight API
19
19
  - **MCP server** — full Model Context Protocol support for AI agents
20
+ - **Interactive TUI** — browse your portfolio in the terminal with `domains interactive`
20
21
 
21
22
  ## Installation
22
23
 
@@ -29,7 +30,10 @@ Data is stored at `~/.hasna/domains/domains.db`. Override with `HASNA_DOMAINS_DI
29
30
  ## CLI Usage
30
31
 
31
32
  ```bash
32
- # Domain management
33
+ # Interactive portfolio browser (Ink TUI)
34
+ domains interactive
35
+ domains interactive --status active
36
+ ```
33
37
  domains add --name example.com --registrar Namecheap --expires-at 2025-01-01
34
38
  domains list
35
39
  domains list --status active --registrar Namecheap
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerInteractiveCommand(program: Command): void;
3
+ //# sourceMappingURL=interactive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/interactive.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgBjE"}
package/dist/cli/index.js CHANGED
@@ -5,43 +5,25 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- function __accessProp(key) {
9
- return this[key];
10
- }
11
- var __toESMCache_node;
12
- var __toESMCache_esm;
13
8
  var __toESM = (mod, isNodeMode, target) => {
14
- var canCache = mod != null && typeof mod === "object";
15
- if (canCache) {
16
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
- var cached = cache.get(mod);
18
- if (cached)
19
- return cached;
20
- }
21
9
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
10
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
11
  for (let key of __getOwnPropNames(mod))
24
12
  if (!__hasOwnProp.call(to, key))
25
13
  __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
14
+ get: () => mod[key],
27
15
  enumerable: true
28
16
  });
29
- if (canCache)
30
- cache.set(mod, to);
31
17
  return to;
32
18
  };
33
19
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
- var __returnValue = (v) => v;
35
- function __exportSetter(name, newValue) {
36
- this[name] = __returnValue.bind(null, newValue);
37
- }
38
20
  var __export = (target, all) => {
39
21
  for (var name in all)
40
22
  __defProp(target, name, {
41
23
  get: all[name],
42
24
  enumerable: true,
43
25
  configurable: true,
44
- set: __exportSetter.bind(all, name)
26
+ set: (newValue) => all[name] = () => newValue
45
27
  });
46
28
  };
47
29
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -19549,7 +19531,7 @@ var require_package = __commonJS((exports, module) => {
19549
19531
 
19550
19532
  // node_modules/@aws-sdk/util-user-agent-node/dist-cjs/index.js
19551
19533
  var require_dist_cjs41 = __commonJS((exports) => {
19552
- var __dirname = "/home/hasna/workspace/hasna/opensource/open-domains/node_modules/@aws-sdk/util-user-agent-node/dist-cjs";
19534
+ var __dirname = "/Users/hasna/Workspace/hasna/opensource/open-domains/node_modules/@aws-sdk/util-user-agent-node/dist-cjs";
19553
19535
  var node_os = __require("os");
19554
19536
  var node_process = __require("process");
19555
19537
  var utilConfigProvider = require_dist_cjs33();
@@ -39944,6 +39926,649 @@ Domain: ${name}`);
39944
39926
  });
39945
39927
  }
39946
39928
 
39929
+ // src/cli/commands/interactive.tsx
39930
+ import chalk from "chalk";
39931
+ import { render } from "ink";
39932
+
39933
+ // src/cli/tui/App.tsx
39934
+ init_domains();
39935
+ init_dns_records();
39936
+ import { useCallback, useEffect, useMemo, useState as useState2 } from "react";
39937
+ import { Box as Box5, Text as Text5, useApp, useInput as useInput2 } from "ink";
39938
+
39939
+ // src/cli/tui/Header.tsx
39940
+ import { Box, Text } from "ink";
39941
+
39942
+ // src/cli/tui/format.ts
39943
+ var TABLE_COLS = {
39944
+ name: 28,
39945
+ status: 14,
39946
+ expires: 12,
39947
+ registrar: 16
39948
+ };
39949
+ function pad(value, width) {
39950
+ if (value.length > width)
39951
+ return value.slice(0, width - 1) + "\u2026";
39952
+ return value.padEnd(width);
39953
+ }
39954
+ function formatDate(iso) {
39955
+ if (!iso)
39956
+ return "\u2014";
39957
+ return iso.slice(0, 10);
39958
+ }
39959
+ function daysUntil(iso) {
39960
+ if (!iso)
39961
+ return "\u2014";
39962
+ const ms = new Date(iso).getTime() - Date.now();
39963
+ const days = Math.ceil(ms / (1000 * 60 * 60 * 24));
39964
+ if (days < 0)
39965
+ return "expired";
39966
+ if (days === 0)
39967
+ return "today";
39968
+ return `${days}d`;
39969
+ }
39970
+ var STATUS_COLORS = {
39971
+ discovered: "gray",
39972
+ researching: "blue",
39973
+ offered: "yellow",
39974
+ negotiating: "yellow",
39975
+ purchased: "green",
39976
+ active: "green",
39977
+ not_available: "red",
39978
+ premium_only: "magenta",
39979
+ declined: "red",
39980
+ expired: "red",
39981
+ transferring: "cyan",
39982
+ redemption: "red"
39983
+ };
39984
+ var DOMAIN_FILTERS = ["all", "active", "expiring", "premium"];
39985
+ function filterLabel(filter) {
39986
+ switch (filter) {
39987
+ case "all":
39988
+ return "All";
39989
+ case "active":
39990
+ return "Active";
39991
+ case "expiring":
39992
+ return "Expiring (30d)";
39993
+ case "premium":
39994
+ return "Premium";
39995
+ }
39996
+ }
39997
+
39998
+ // src/cli/tui/Header.tsx
39999
+ import { jsxDEV } from "react/jsx-dev-runtime";
40000
+ function Header({ count, filter, view }) {
40001
+ const viewLabel = view === "list" ? "Portfolio" : view === "detail" ? "Detail" : "Search";
40002
+ return /* @__PURE__ */ jsxDEV(Box, {
40003
+ flexDirection: "column",
40004
+ marginBottom: 1,
40005
+ children: [
40006
+ /* @__PURE__ */ jsxDEV(Box, {
40007
+ children: [
40008
+ /* @__PURE__ */ jsxDEV(Text, {
40009
+ bold: true,
40010
+ color: "cyan",
40011
+ children: "domains"
40012
+ }, undefined, false, undefined, this),
40013
+ /* @__PURE__ */ jsxDEV(Text, {
40014
+ dimColor: true,
40015
+ children: " \u2014 interactive portfolio browser"
40016
+ }, undefined, false, undefined, this)
40017
+ ]
40018
+ }, undefined, true, undefined, this),
40019
+ /* @__PURE__ */ jsxDEV(Box, {
40020
+ children: /* @__PURE__ */ jsxDEV(Text, {
40021
+ dimColor: true,
40022
+ children: [
40023
+ viewLabel,
40024
+ " \xB7 ",
40025
+ filterLabel(filter),
40026
+ " \xB7 ",
40027
+ count,
40028
+ " domain",
40029
+ count === 1 ? "" : "s"
40030
+ ]
40031
+ }, undefined, true, undefined, this)
40032
+ }, undefined, false, undefined, this)
40033
+ ]
40034
+ }, undefined, true, undefined, this);
40035
+ }
40036
+
40037
+ // src/cli/tui/DomainTable.tsx
40038
+ import { Box as Box2, Text as Text2 } from "ink";
40039
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
40040
+ var VISIBLE_ROWS = 16;
40041
+ function DomainTable({ domains, selectedIndex, compact = false }) {
40042
+ if (domains.length === 0) {
40043
+ return /* @__PURE__ */ jsxDEV2(Box2, {
40044
+ flexDirection: "column",
40045
+ paddingLeft: compact ? 0 : 1,
40046
+ children: /* @__PURE__ */ jsxDEV2(Text2, {
40047
+ dimColor: true,
40048
+ children: "No domains match this filter. Press [f] to change filter or [r] to refresh."
40049
+ }, undefined, false, undefined, this)
40050
+ }, undefined, false, undefined, this);
40051
+ }
40052
+ const start = Math.max(0, Math.min(selectedIndex - Math.floor(VISIBLE_ROWS / 2), Math.max(0, domains.length - VISIBLE_ROWS)));
40053
+ const visible = domains.slice(start, start + VISIBLE_ROWS);
40054
+ return /* @__PURE__ */ jsxDEV2(Box2, {
40055
+ flexDirection: "column",
40056
+ width: "100%",
40057
+ paddingRight: 0,
40058
+ children: [
40059
+ /* @__PURE__ */ jsxDEV2(Box2, {
40060
+ children: /* @__PURE__ */ jsxDEV2(Text2, {
40061
+ bold: true,
40062
+ dimColor: true,
40063
+ children: [
40064
+ pad("NAME", TABLE_COLS.name),
40065
+ pad("STATUS", TABLE_COLS.status),
40066
+ pad("EXPIRES", TABLE_COLS.expires),
40067
+ pad("REGISTRAR", TABLE_COLS.registrar),
40068
+ !compact ? " TTL" : ""
40069
+ ]
40070
+ }, undefined, true, undefined, this)
40071
+ }, undefined, false, undefined, this),
40072
+ /* @__PURE__ */ jsxDEV2(Text2, {
40073
+ dimColor: true,
40074
+ children: "\u2500".repeat(TABLE_COLS.name + TABLE_COLS.status + TABLE_COLS.expires + TABLE_COLS.registrar + (compact ? 0 : 6))
40075
+ }, undefined, false, undefined, this),
40076
+ start > 0 && /* @__PURE__ */ jsxDEV2(Text2, {
40077
+ dimColor: true,
40078
+ children: [
40079
+ " \u2191 ",
40080
+ start,
40081
+ " more above"
40082
+ ]
40083
+ }, undefined, true, undefined, this),
40084
+ visible.map((domain, offset) => {
40085
+ const index = start + offset;
40086
+ const selected = index === selectedIndex;
40087
+ const statusColor = STATUS_COLORS[domain.status] ?? "white";
40088
+ return /* @__PURE__ */ jsxDEV2(Box2, {
40089
+ children: [
40090
+ /* @__PURE__ */ jsxDEV2(Text2, {
40091
+ color: selected ? "cyan" : undefined,
40092
+ bold: selected,
40093
+ children: selected ? "\u276F " : " "
40094
+ }, undefined, false, undefined, this),
40095
+ /* @__PURE__ */ jsxDEV2(Text2, {
40096
+ bold: selected,
40097
+ children: pad(domain.name, TABLE_COLS.name)
40098
+ }, undefined, false, undefined, this),
40099
+ /* @__PURE__ */ jsxDEV2(Text2, {
40100
+ color: statusColor,
40101
+ children: pad(domain.status, TABLE_COLS.status)
40102
+ }, undefined, false, undefined, this),
40103
+ /* @__PURE__ */ jsxDEV2(Text2, {
40104
+ children: pad(formatDate(domain.expires_at), TABLE_COLS.expires)
40105
+ }, undefined, false, undefined, this),
40106
+ /* @__PURE__ */ jsxDEV2(Text2, {
40107
+ dimColor: true,
40108
+ children: pad(domain.registrar ?? "\u2014", TABLE_COLS.registrar)
40109
+ }, undefined, false, undefined, this),
40110
+ !compact && /* @__PURE__ */ jsxDEV2(Text2, {
40111
+ dimColor: true,
40112
+ children: [
40113
+ " ",
40114
+ daysUntil(domain.expires_at)
40115
+ ]
40116
+ }, undefined, true, undefined, this)
40117
+ ]
40118
+ }, domain.id, true, undefined, this);
40119
+ }),
40120
+ start + visible.length < domains.length && /* @__PURE__ */ jsxDEV2(Text2, {
40121
+ dimColor: true,
40122
+ children: [
40123
+ " \u2193 ",
40124
+ domains.length - start - visible.length,
40125
+ " more below"
40126
+ ]
40127
+ }, undefined, true, undefined, this)
40128
+ ]
40129
+ }, undefined, true, undefined, this);
40130
+ }
40131
+
40132
+ // src/cli/tui/DomainDetail.tsx
40133
+ import { Box as Box3, Text as Text3 } from "ink";
40134
+ import { jsxDEV as jsxDEV3, Fragment } from "react/jsx-dev-runtime";
40135
+ function DomainDetail({ details, dnsRecords = [], compact = false }) {
40136
+ const { domain, offers, emails } = details;
40137
+ const statusColor = STATUS_COLORS[domain.status] ?? "white";
40138
+ return /* @__PURE__ */ jsxDEV3(Box3, {
40139
+ flexDirection: "column",
40140
+ width: compact ? "45%" : "100%",
40141
+ paddingLeft: compact ? 1 : 0,
40142
+ children: [
40143
+ !compact && /* @__PURE__ */ jsxDEV3(Box3, {
40144
+ marginBottom: 1,
40145
+ children: /* @__PURE__ */ jsxDEV3(Text3, {
40146
+ dimColor: true,
40147
+ children: "[esc] back \xB7 [r] refresh \xB7 q quit"
40148
+ }, undefined, false, undefined, this)
40149
+ }, undefined, false, undefined, this),
40150
+ /* @__PURE__ */ jsxDEV3(Box3, {
40151
+ flexDirection: "column",
40152
+ marginBottom: 1,
40153
+ children: [
40154
+ /* @__PURE__ */ jsxDEV3(Text3, {
40155
+ bold: true,
40156
+ color: "cyan",
40157
+ children: domain.name
40158
+ }, undefined, false, undefined, this),
40159
+ /* @__PURE__ */ jsxDEV3(Text3, {
40160
+ color: statusColor,
40161
+ children: [
40162
+ "[",
40163
+ domain.status,
40164
+ "]"
40165
+ ]
40166
+ }, undefined, true, undefined, this)
40167
+ ]
40168
+ }, undefined, true, undefined, this),
40169
+ /* @__PURE__ */ jsxDEV3(Field, {
40170
+ label: "Registrar",
40171
+ value: domain.registrar
40172
+ }, undefined, false, undefined, this),
40173
+ /* @__PURE__ */ jsxDEV3(Field, {
40174
+ label: "Registered",
40175
+ value: formatDate(domain.registered_at)
40176
+ }, undefined, false, undefined, this),
40177
+ /* @__PURE__ */ jsxDEV3(Field, {
40178
+ label: "Expires",
40179
+ value: formatDate(domain.expires_at)
40180
+ }, undefined, false, undefined, this),
40181
+ /* @__PURE__ */ jsxDEV3(Field, {
40182
+ label: "Auto-renew",
40183
+ value: domain.auto_renew ? "yes" : "no"
40184
+ }, undefined, false, undefined, this),
40185
+ domain.is_premium && /* @__PURE__ */ jsxDEV3(Fragment, {
40186
+ children: [
40187
+ /* @__PURE__ */ jsxDEV3(Field, {
40188
+ label: "Premium",
40189
+ value: "yes"
40190
+ }, undefined, false, undefined, this),
40191
+ /* @__PURE__ */ jsxDEV3(Field, {
40192
+ label: "Premium ask",
40193
+ value: domain.premium_price?.toString() ?? "\u2014"
40194
+ }, undefined, false, undefined, this)
40195
+ ]
40196
+ }, undefined, true, undefined, this),
40197
+ domain.purchase_price !== null && /* @__PURE__ */ jsxDEV3(Field, {
40198
+ label: "Purchase price",
40199
+ value: String(domain.purchase_price)
40200
+ }, undefined, false, undefined, this),
40201
+ /* @__PURE__ */ jsxDEV3(Field, {
40202
+ label: "SSL issuer",
40203
+ value: domain.ssl_issuer
40204
+ }, undefined, false, undefined, this),
40205
+ /* @__PURE__ */ jsxDEV3(Field, {
40206
+ label: "SSL expires",
40207
+ value: formatDate(domain.ssl_expires_at)
40208
+ }, undefined, false, undefined, this),
40209
+ domain.nameservers.length > 0 && /* @__PURE__ */ jsxDEV3(Box3, {
40210
+ flexDirection: "column",
40211
+ marginTop: 1,
40212
+ children: [
40213
+ /* @__PURE__ */ jsxDEV3(Text3, {
40214
+ bold: true,
40215
+ dimColor: true,
40216
+ children: "Nameservers"
40217
+ }, undefined, false, undefined, this),
40218
+ domain.nameservers.slice(0, compact ? 3 : 8).map((ns) => /* @__PURE__ */ jsxDEV3(Text3, {
40219
+ dimColor: true,
40220
+ children: [
40221
+ " ",
40222
+ ns
40223
+ ]
40224
+ }, ns, true, undefined, this))
40225
+ ]
40226
+ }, undefined, true, undefined, this),
40227
+ !compact && dnsRecords.length > 0 && /* @__PURE__ */ jsxDEV3(Box3, {
40228
+ flexDirection: "column",
40229
+ marginTop: 1,
40230
+ children: [
40231
+ /* @__PURE__ */ jsxDEV3(Text3, {
40232
+ bold: true,
40233
+ children: [
40234
+ "DNS records (",
40235
+ dnsRecords.length,
40236
+ ")"
40237
+ ]
40238
+ }, undefined, true, undefined, this),
40239
+ dnsRecords.slice(0, 8).map((record) => /* @__PURE__ */ jsxDEV3(Text3, {
40240
+ dimColor: true,
40241
+ children: [
40242
+ " ",
40243
+ record.type,
40244
+ " ",
40245
+ record.name,
40246
+ " \u2192 ",
40247
+ record.value
40248
+ ]
40249
+ }, record.id, true, undefined, this)),
40250
+ dnsRecords.length > 8 && /* @__PURE__ */ jsxDEV3(Text3, {
40251
+ dimColor: true,
40252
+ children: [
40253
+ " \u2026 ",
40254
+ dnsRecords.length - 8,
40255
+ " more"
40256
+ ]
40257
+ }, undefined, true, undefined, this)
40258
+ ]
40259
+ }, undefined, true, undefined, this),
40260
+ (offers.length > 0 || emails.length > 0) && /* @__PURE__ */ jsxDEV3(Box3, {
40261
+ marginTop: 1,
40262
+ children: /* @__PURE__ */ jsxDEV3(Text3, {
40263
+ dimColor: true,
40264
+ children: [
40265
+ "Offers: ",
40266
+ offers.length,
40267
+ " \xB7 Linked emails: ",
40268
+ emails.length,
40269
+ dnsRecords.length > 0 ? ` \xB7 DNS: ${dnsRecords.length}` : ""
40270
+ ]
40271
+ }, undefined, true, undefined, this)
40272
+ }, undefined, false, undefined, this),
40273
+ domain.notes && !compact && /* @__PURE__ */ jsxDEV3(Box3, {
40274
+ flexDirection: "column",
40275
+ marginTop: 1,
40276
+ children: [
40277
+ /* @__PURE__ */ jsxDEV3(Text3, {
40278
+ bold: true,
40279
+ dimColor: true,
40280
+ children: "Notes"
40281
+ }, undefined, false, undefined, this),
40282
+ /* @__PURE__ */ jsxDEV3(Text3, {
40283
+ wrap: "wrap",
40284
+ children: domain.notes
40285
+ }, undefined, false, undefined, this)
40286
+ ]
40287
+ }, undefined, true, undefined, this),
40288
+ !compact && /* @__PURE__ */ jsxDEV3(Box3, {
40289
+ marginTop: 1,
40290
+ children: /* @__PURE__ */ jsxDEV3(Text3, {
40291
+ dimColor: true,
40292
+ children: [
40293
+ "ID: ",
40294
+ domain.id
40295
+ ]
40296
+ }, undefined, true, undefined, this)
40297
+ }, undefined, false, undefined, this)
40298
+ ]
40299
+ }, undefined, true, undefined, this);
40300
+ }
40301
+ function Field({ label, value }) {
40302
+ if (!value || value === "\u2014") {
40303
+ return /* @__PURE__ */ jsxDEV3(Box3, {
40304
+ children: [
40305
+ /* @__PURE__ */ jsxDEV3(Text3, {
40306
+ dimColor: true,
40307
+ children: label.padEnd(14)
40308
+ }, undefined, false, undefined, this),
40309
+ /* @__PURE__ */ jsxDEV3(Text3, {
40310
+ dimColor: true,
40311
+ children: "\u2014"
40312
+ }, undefined, false, undefined, this)
40313
+ ]
40314
+ }, undefined, true, undefined, this);
40315
+ }
40316
+ return /* @__PURE__ */ jsxDEV3(Box3, {
40317
+ children: [
40318
+ /* @__PURE__ */ jsxDEV3(Text3, {
40319
+ dimColor: true,
40320
+ children: label.padEnd(14)
40321
+ }, undefined, false, undefined, this),
40322
+ /* @__PURE__ */ jsxDEV3(Text3, {
40323
+ children: value
40324
+ }, undefined, false, undefined, this)
40325
+ ]
40326
+ }, undefined, true, undefined, this);
40327
+ }
40328
+
40329
+ // src/cli/tui/SearchView.tsx
40330
+ import { useState } from "react";
40331
+ import { Box as Box4, Text as Text4, useInput } from "ink";
40332
+ import TextInput from "ink-text-input";
40333
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
40334
+ function SearchView({
40335
+ results,
40336
+ selectedIndex,
40337
+ onSearch,
40338
+ onSelect,
40339
+ onBack
40340
+ }) {
40341
+ const [query, setQuery] = useState("");
40342
+ useInput((input, key) => {
40343
+ if (key.escape)
40344
+ onBack();
40345
+ if (key.return && results[selectedIndex]) {
40346
+ onSelect(results[selectedIndex]);
40347
+ }
40348
+ });
40349
+ return /* @__PURE__ */ jsxDEV4(Box4, {
40350
+ flexDirection: "column",
40351
+ children: [
40352
+ /* @__PURE__ */ jsxDEV4(Box4, {
40353
+ marginBottom: 1,
40354
+ children: /* @__PURE__ */ jsxDEV4(Text4, {
40355
+ bold: true,
40356
+ children: "Search domains"
40357
+ }, undefined, false, undefined, this)
40358
+ }, undefined, false, undefined, this),
40359
+ /* @__PURE__ */ jsxDEV4(Box4, {
40360
+ marginBottom: 1,
40361
+ children: [
40362
+ /* @__PURE__ */ jsxDEV4(Text4, {
40363
+ dimColor: true,
40364
+ children: "Query: "
40365
+ }, undefined, false, undefined, this),
40366
+ /* @__PURE__ */ jsxDEV4(TextInput, {
40367
+ value: query,
40368
+ onChange: (value) => {
40369
+ setQuery(value);
40370
+ onSearch(value);
40371
+ },
40372
+ placeholder: "name, registrar, notes\u2026"
40373
+ }, undefined, false, undefined, this)
40374
+ ]
40375
+ }, undefined, true, undefined, this),
40376
+ /* @__PURE__ */ jsxDEV4(Text4, {
40377
+ dimColor: true,
40378
+ children: "[enter] open \xB7 [esc] back"
40379
+ }, undefined, false, undefined, this),
40380
+ /* @__PURE__ */ jsxDEV4(Box4, {
40381
+ marginTop: 1,
40382
+ children: /* @__PURE__ */ jsxDEV4(DomainTable, {
40383
+ domains: results,
40384
+ selectedIndex
40385
+ }, undefined, false, undefined, this)
40386
+ }, undefined, false, undefined, this)
40387
+ ]
40388
+ }, undefined, true, undefined, this);
40389
+ }
40390
+
40391
+ // src/cli/tui/App.tsx
40392
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
40393
+ function loadDomainsForFilter(activeFilter) {
40394
+ switch (activeFilter) {
40395
+ case "active":
40396
+ return listDomains({ status: "active" });
40397
+ case "premium":
40398
+ return listDomains({ is_premium: true });
40399
+ case "expiring":
40400
+ return listExpiring(30);
40401
+ default:
40402
+ return listDomains();
40403
+ }
40404
+ }
40405
+ function App({ initialStatus }) {
40406
+ const { exit } = useApp();
40407
+ const [view, setView] = useState2("list");
40408
+ const initialFilter = initialStatus === "active" ? "active" : initialStatus === "premium" ? "premium" : "all";
40409
+ const [filter, setFilter] = useState2(initialFilter);
40410
+ const [selectedIndex, setSelectedIndex] = useState2(0);
40411
+ const [details, setDetails] = useState2(null);
40412
+ const [searchResults, setSearchResults] = useState2([]);
40413
+ const [searchIndex, setSearchIndex] = useState2(0);
40414
+ const [domains, setDomains] = useState2(() => loadDomainsForFilter(initialFilter));
40415
+ const refresh = useCallback(() => {
40416
+ const next = loadDomainsForFilter(filter);
40417
+ setDomains(next);
40418
+ setSelectedIndex((current) => Math.min(current, Math.max(0, next.length - 1)));
40419
+ return next;
40420
+ }, [filter]);
40421
+ useEffect(() => {
40422
+ refresh();
40423
+ }, [refresh]);
40424
+ const selectedDomain = domains[selectedIndex] ?? null;
40425
+ const previewDetails = useMemo(() => {
40426
+ if (!selectedDomain)
40427
+ return null;
40428
+ return getDomainDetails(selectedDomain.id);
40429
+ }, [selectedDomain]);
40430
+ const previewDns = useMemo(() => {
40431
+ if (!selectedDomain)
40432
+ return [];
40433
+ return listDnsRecords(selectedDomain.id);
40434
+ }, [selectedDomain]);
40435
+ useInput2((input, key) => {
40436
+ if (input === "q" && view !== "search") {
40437
+ exit();
40438
+ return;
40439
+ }
40440
+ if (view === "list") {
40441
+ if (key.upArrow || input === "k") {
40442
+ setSelectedIndex((index) => Math.max(0, index - 1));
40443
+ } else if (key.downArrow || input === "j") {
40444
+ setSelectedIndex((index) => Math.min(domains.length - 1, index + 1));
40445
+ } else if (key.return && selectedDomain) {
40446
+ const full = getDomainDetails(selectedDomain.id);
40447
+ if (full) {
40448
+ setDetails(full);
40449
+ setView("detail");
40450
+ }
40451
+ } else if (input === "/") {
40452
+ setSearchResults([]);
40453
+ setSearchIndex(0);
40454
+ setView("search");
40455
+ } else if (input === "f") {
40456
+ setFilter((current) => {
40457
+ const index = DOMAIN_FILTERS.indexOf(current);
40458
+ return DOMAIN_FILTERS[(index + 1) % DOMAIN_FILTERS.length];
40459
+ });
40460
+ } else if (input === "r") {
40461
+ refresh();
40462
+ }
40463
+ return;
40464
+ }
40465
+ if (view === "search") {
40466
+ if (key.upArrow || input === "k") {
40467
+ setSearchIndex((index) => Math.max(0, index - 1));
40468
+ } else if (key.downArrow || input === "j") {
40469
+ setSearchIndex((index) => Math.min(searchResults.length - 1, index + 1));
40470
+ }
40471
+ return;
40472
+ }
40473
+ if (view === "detail") {
40474
+ if (key.escape) {
40475
+ setView("list");
40476
+ refresh();
40477
+ } else if (input === "r" && details) {
40478
+ setDetails(getDomainDetails(details.domain.id));
40479
+ }
40480
+ }
40481
+ });
40482
+ const handleSearch = useCallback((query) => {
40483
+ const trimmed = query.trim();
40484
+ if (trimmed.length < 2) {
40485
+ setSearchResults([]);
40486
+ setSearchIndex(0);
40487
+ return;
40488
+ }
40489
+ const results = searchDomains(trimmed);
40490
+ setSearchResults(results);
40491
+ setSearchIndex(0);
40492
+ }, []);
40493
+ const handleSearchSelect = useCallback((domain) => {
40494
+ const full = getDomainDetails(domain.id);
40495
+ if (full) {
40496
+ setDetails(full);
40497
+ setView("detail");
40498
+ }
40499
+ }, []);
40500
+ const detailDns = details ? listDnsRecords(details.domain.id) : [];
40501
+ return /* @__PURE__ */ jsxDEV5(Box5, {
40502
+ flexDirection: "column",
40503
+ padding: 1,
40504
+ children: [
40505
+ /* @__PURE__ */ jsxDEV5(Header, {
40506
+ count: view === "search" ? searchResults.length : domains.length,
40507
+ filter,
40508
+ view
40509
+ }, undefined, false, undefined, this),
40510
+ view === "list" && /* @__PURE__ */ jsxDEV5(Box5, {
40511
+ flexDirection: "column",
40512
+ children: [
40513
+ /* @__PURE__ */ jsxDEV5(DomainTable, {
40514
+ domains,
40515
+ selectedIndex
40516
+ }, undefined, false, undefined, this),
40517
+ previewDetails && /* @__PURE__ */ jsxDEV5(Box5, {
40518
+ marginTop: 1,
40519
+ flexDirection: "column",
40520
+ children: [
40521
+ /* @__PURE__ */ jsxDEV5(Text5, {
40522
+ bold: true,
40523
+ dimColor: true,
40524
+ children: "Selected"
40525
+ }, undefined, false, undefined, this),
40526
+ /* @__PURE__ */ jsxDEV5(DomainDetail, {
40527
+ details: previewDetails,
40528
+ dnsRecords: previewDns,
40529
+ compact: true
40530
+ }, undefined, false, undefined, this)
40531
+ ]
40532
+ }, undefined, true, undefined, this),
40533
+ /* @__PURE__ */ jsxDEV5(Box5, {
40534
+ marginTop: 1,
40535
+ children: /* @__PURE__ */ jsxDEV5(Text5, {
40536
+ dimColor: true,
40537
+ children: "\u2191\u2193 j/k navigate \xB7 enter detail \xB7 / search \xB7 f filter \xB7 r refresh \xB7 q quit"
40538
+ }, undefined, false, undefined, this)
40539
+ }, undefined, false, undefined, this)
40540
+ ]
40541
+ }, undefined, true, undefined, this),
40542
+ view === "detail" && details && /* @__PURE__ */ jsxDEV5(DomainDetail, {
40543
+ details,
40544
+ dnsRecords: detailDns
40545
+ }, undefined, false, undefined, this),
40546
+ view === "search" && /* @__PURE__ */ jsxDEV5(SearchView, {
40547
+ results: searchResults,
40548
+ selectedIndex: searchIndex,
40549
+ onSearch: handleSearch,
40550
+ onSelect: handleSearchSelect,
40551
+ onBack: () => setView("list")
40552
+ }, undefined, false, undefined, this)
40553
+ ]
40554
+ }, undefined, true, undefined, this);
40555
+ }
40556
+
40557
+ // src/cli/commands/interactive.tsx
40558
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
40559
+ function registerInteractiveCommand(program2) {
40560
+ program2.command("interactive").description("Launch interactive domain portfolio browser (Ink TUI)").option("--status <status>", "Initial filter status (e.g. active, premium)").action((opts) => {
40561
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
40562
+ console.error(chalk.red("Interactive mode requires a TTY terminal."));
40563
+ console.error(chalk.dim("Use `domains domain list` or `domains domain get <name>` for non-interactive use."));
40564
+ process.exit(1);
40565
+ }
40566
+ render(/* @__PURE__ */ jsxDEV6(App, {
40567
+ initialStatus: opts.status
40568
+ }, undefined, false, undefined, this));
40569
+ });
40570
+ }
40571
+
39947
40572
  // src/cli/index.ts
39948
40573
  init_version();
39949
40574
  var program2 = new Command;
@@ -39968,4 +40593,5 @@ registerOutreachCommand(program2);
39968
40593
  registerSedoCommand(program2);
39969
40594
  registerWalletCommand(program2);
39970
40595
  registerProvisionCommand(program2);
40596
+ registerInteractiveCommand(program2);
39971
40597
  program2.parse(process.argv);
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ export interface AppProps {
3
+ initialStatus?: string;
4
+ }
5
+ export declare function App({ initialStatus }: AppProps): React.JSX.Element;
6
+ //# sourceMappingURL=App.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoD,MAAM,OAAO,CAAC;AAmBzE,MAAM,WAAW,QAAQ;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAeD,wBAAgB,GAAG,CAAC,EAAE,aAAa,EAAE,EAAE,QAAQ,qBAqJ9C"}
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import type { DomainDetails } from "../../db/domains.js";
3
+ import type { DnsRecord } from "../../db/dns-records.js";
4
+ interface DomainDetailProps {
5
+ details: DomainDetails;
6
+ dnsRecords?: DnsRecord[];
7
+ compact?: boolean;
8
+ }
9
+ export declare function DomainDetail({ details, dnsRecords, compact }: DomainDetailProps): React.JSX.Element;
10
+ export {};
11
+ //# sourceMappingURL=DomainDetail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DomainDetail.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/DomainDetail.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGzD,UAAU,iBAAiB;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,UAAe,EAAE,OAAe,EAAE,EAAE,iBAAiB,qBAqF5F"}
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import type { Domain } from "../../db/domains.js";
3
+ interface DomainTableProps {
4
+ domains: Domain[];
5
+ selectedIndex: number;
6
+ compact?: boolean;
7
+ }
8
+ export declare function DomainTable({ domains, selectedIndex, compact }: DomainTableProps): React.JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=DomainTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DomainTable.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/DomainTable.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAWlD,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,OAAe,EAAE,EAAE,gBAAgB,qBA2DxF"}
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import type { DomainFilter } from "./format.js";
3
+ interface HeaderProps {
4
+ count: number;
5
+ filter: DomainFilter;
6
+ view: "list" | "detail" | "search";
7
+ }
8
+ export declare function Header({ count, filter, view }: HeaderProps): React.JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=Header.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/Header.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;CACpC;AAED,wBAAgB,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,WAAW,qBAmB1D"}
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import type { Domain } from "../../db/domains.js";
3
+ interface SearchViewProps {
4
+ results: Domain[];
5
+ selectedIndex: number;
6
+ onSearch: (query: string) => void;
7
+ onSelect: (domain: Domain) => void;
8
+ onBack: () => void;
9
+ }
10
+ export declare function SearchView({ results, selectedIndex, onSearch, onSelect, onBack, }: SearchViewProps): React.JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=SearchView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchView.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/SearchView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAGlD,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE,eAAe,qBAgCjB"}
@@ -0,0 +1,17 @@
1
+ import type { Domain, DomainStatus } from "../../db/domains.js";
2
+ export declare const TABLE_COLS: {
3
+ readonly name: 28;
4
+ readonly status: 14;
5
+ readonly expires: 12;
6
+ readonly registrar: 16;
7
+ };
8
+ export declare function pad(value: string, width: number): string;
9
+ export declare function formatDate(iso: string | null | undefined): string;
10
+ export declare function daysUntil(iso: string | null | undefined): string;
11
+ export declare const STATUS_COLORS: Record<DomainStatus, string>;
12
+ export declare function domainRowLabel(domain: Domain): string;
13
+ export declare function stripAnsi(value: string | undefined): string;
14
+ export type DomainFilter = "all" | "active" | "expiring" | "premium";
15
+ export declare const DOMAIN_FILTERS: DomainFilter[];
16
+ export declare function filterLabel(filter: DomainFilter): string;
17
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEhE,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAEX,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGjE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAOhE;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAatD,CAAC;AAEF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE3D;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAErE,eAAO,MAAM,cAAc,EAAE,YAAY,EAA6C,CAAC;AAEvF,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAWxD"}
package/dist/index.js CHANGED
@@ -4,43 +4,25 @@ var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- function __accessProp(key) {
8
- return this[key];
9
- }
10
- var __toESMCache_node;
11
- var __toESMCache_esm;
12
7
  var __toESM = (mod, isNodeMode, target) => {
13
- var canCache = mod != null && typeof mod === "object";
14
- if (canCache) {
15
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
- var cached = cache.get(mod);
17
- if (cached)
18
- return cached;
19
- }
20
8
  target = mod != null ? __create(__getProtoOf(mod)) : {};
21
9
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
10
  for (let key of __getOwnPropNames(mod))
23
11
  if (!__hasOwnProp.call(to, key))
24
12
  __defProp(to, key, {
25
- get: __accessProp.bind(mod, key),
13
+ get: () => mod[key],
26
14
  enumerable: true
27
15
  });
28
- if (canCache)
29
- cache.set(mod, to);
30
16
  return to;
31
17
  };
32
18
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
- var __returnValue = (v) => v;
34
- function __exportSetter(name, newValue) {
35
- this[name] = __returnValue.bind(null, newValue);
36
- }
37
19
  var __export = (target, all) => {
38
20
  for (var name in all)
39
21
  __defProp(target, name, {
40
22
  get: all[name],
41
23
  enumerable: true,
42
24
  configurable: true,
43
- set: __exportSetter.bind(all, name)
25
+ set: (newValue) => all[name] = () => newValue
44
26
  });
45
27
  };
46
28
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -15823,7 +15805,7 @@ var require_package = __commonJS((exports, module) => {
15823
15805
 
15824
15806
  // node_modules/@aws-sdk/util-user-agent-node/dist-cjs/index.js
15825
15807
  var require_dist_cjs41 = __commonJS((exports) => {
15826
- var __dirname = "/home/hasna/workspace/hasna/opensource/open-domains/node_modules/@aws-sdk/util-user-agent-node/dist-cjs";
15808
+ var __dirname = "/Users/hasna/Workspace/hasna/opensource/open-domains/node_modules/@aws-sdk/util-user-agent-node/dist-cjs";
15827
15809
  var node_os = __require("os");
15828
15810
  var node_process = __require("process");
15829
15811
  var utilConfigProvider = require_dist_cjs33();
package/dist/mcp/index.js CHANGED
@@ -5,43 +5,25 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- function __accessProp(key) {
9
- return this[key];
10
- }
11
- var __toESMCache_node;
12
- var __toESMCache_esm;
13
8
  var __toESM = (mod, isNodeMode, target) => {
14
- var canCache = mod != null && typeof mod === "object";
15
- if (canCache) {
16
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
- var cached = cache.get(mod);
18
- if (cached)
19
- return cached;
20
- }
21
9
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
10
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
11
  for (let key of __getOwnPropNames(mod))
24
12
  if (!__hasOwnProp.call(to, key))
25
13
  __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
14
+ get: () => mod[key],
27
15
  enumerable: true
28
16
  });
29
- if (canCache)
30
- cache.set(mod, to);
31
17
  return to;
32
18
  };
33
19
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
- var __returnValue = (v) => v;
35
- function __exportSetter(name, newValue) {
36
- this[name] = __returnValue.bind(null, newValue);
37
- }
38
20
  var __export = (target, all) => {
39
21
  for (var name in all)
40
22
  __defProp(target, name, {
41
23
  get: all[name],
42
24
  enumerable: true,
43
25
  configurable: true,
44
- set: __exportSetter.bind(all, name)
26
+ set: (newValue) => all[name] = () => newValue
45
27
  });
46
28
  };
47
29
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -16073,7 +16055,7 @@ var require_package = __commonJS((exports, module) => {
16073
16055
 
16074
16056
  // node_modules/@aws-sdk/util-user-agent-node/dist-cjs/index.js
16075
16057
  var require_dist_cjs41 = __commonJS((exports) => {
16076
- var __dirname = "/home/hasna/workspace/hasna/opensource/open-domains/node_modules/@aws-sdk/util-user-agent-node/dist-cjs";
16058
+ var __dirname = "/Users/hasna/Workspace/hasna/opensource/open-domains/node_modules/@aws-sdk/util-user-agent-node/dist-cjs";
16077
16059
  var node_os = __require("os");
16078
16060
  var node_process = __require("process");
16079
16061
  var utilConfigProvider = require_dist_cjs33();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/domains",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "description": "Domain portfolio and DNS management for AI agents — CLI + MCP server with SQLite",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,7 +21,7 @@
21
21
  "README.md"
22
22
  ],
23
23
  "scripts": {
24
- "build": "bun build src/cli/index.ts --outdir dist/cli --target bun --external @modelcontextprotocol/sdk --external @hasna/cloud && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external @hasna/cloud && bun build src/index.ts --outdir dist --target bun --external @hasna/cloud && tsc --emitDeclarationOnly --outDir dist",
24
+ "build": "bun build src/cli/index.ts --outdir dist/cli --target bun --external ink --external react --external chalk --external ink-text-input --external @modelcontextprotocol/sdk --external @hasna/cloud && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external @hasna/cloud && bun build src/index.ts --outdir dist --target bun --external @hasna/cloud && tsc --emitDeclarationOnly --outDir dist",
25
25
  "typecheck": "tsc --noEmit",
26
26
  "test": "bun test",
27
27
  "dev:cli": "bun run src/cli/index.ts",
@@ -65,11 +65,17 @@
65
65
  "@hasna/contacts": "^0.6.16",
66
66
  "@hasna/wallets": "^0.1.7",
67
67
  "@modelcontextprotocol/sdk": "^1.12.1",
68
+ "chalk": "^5.4.1",
68
69
  "commander": "^13.1.0",
70
+ "ink": "^5.2.0",
71
+ "ink-text-input": "^6.0.0",
72
+ "react": "^18.3.1",
69
73
  "zod": "^3.24.2"
70
74
  },
71
75
  "devDependencies": {
72
76
  "@types/bun": "^1.2.4",
77
+ "@types/react": "^18.3.18",
78
+ "ink-testing-library": "^4.0.0",
73
79
  "typescript": "^5.7.3"
74
80
  }
75
81
  }