@heyamiko/amiko-cli 0.9.0-beta.12 → 0.9.0-beta.15

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +326 -95
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @heyamiko/amiko-cli (v0.9.0-beta.10)
1
+ # @heyamiko/amiko-cli (v0.9.0-beta.15)
2
2
 
3
3
  Manage wallets, credits, swaps, MPP marketplace services, and your Amiko twin (identity, documents, voice, avatar, friends, feed, Composio apps) from the terminal. Works for both human users and AI agents running on OpenClaw.
4
4
 
package/dist/index.js CHANGED
@@ -5,25 +5,43 @@ 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;
8
13
  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
+ }
9
21
  target = mod != null ? __create(__getProtoOf(mod)) : {};
10
22
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
23
  for (let key of __getOwnPropNames(mod))
12
24
  if (!__hasOwnProp.call(to, key))
13
25
  __defProp(to, key, {
14
- get: () => mod[key],
26
+ get: __accessProp.bind(mod, key),
15
27
  enumerable: true
16
28
  });
29
+ if (canCache)
30
+ cache.set(mod, to);
17
31
  return to;
18
32
  };
19
33
  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
+ }
20
38
  var __export = (target, all) => {
21
39
  for (var name in all)
22
40
  __defProp(target, name, {
23
41
  get: all[name],
24
42
  enumerable: true,
25
43
  configurable: true,
26
- set: (newValue) => all[name] = () => newValue
44
+ set: __exportSetter.bind(all, name)
27
45
  });
28
46
  };
29
47
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -3515,11 +3533,36 @@ var require_emoji_regex = __commonJS((exports, module) => {
3515
3533
  };
3516
3534
  });
3517
3535
 
3518
- // node_modules/mppx/dist/BodyDigest.js
3519
- var init_BodyDigest = () => {};
3520
-
3521
- // node_modules/mppx/dist/PaymentRequest.js
3522
- var init_PaymentRequest = () => {};
3536
+ // node_modules/mppx/dist/Method.js
3537
+ var exports_Method = {};
3538
+ __export(exports_Method, {
3539
+ toServer: () => toServer,
3540
+ toClient: () => toClient,
3541
+ from: () => from
3542
+ });
3543
+ function from(method) {
3544
+ return method;
3545
+ }
3546
+ function toClient(method, options) {
3547
+ const { context, createCredential } = options;
3548
+ return {
3549
+ ...method,
3550
+ context,
3551
+ createCredential
3552
+ };
3553
+ }
3554
+ function toServer(method, options) {
3555
+ const { defaults, html, request, respond, transport, verify } = options;
3556
+ return {
3557
+ ...method,
3558
+ defaults,
3559
+ html,
3560
+ request,
3561
+ respond,
3562
+ transport,
3563
+ verify
3564
+ };
3565
+ }
3523
3566
 
3524
3567
  // node_modules/mppx/node_modules/zod/v4/core/core.js
3525
3568
  function $constructor(name, initializer, params) {
@@ -16604,89 +16647,8 @@ var init_zod = __esm(() => {
16604
16647
  init_mini();
16605
16648
  });
16606
16649
 
16607
- // node_modules/mppx/dist/Challenge.js
16608
- var Schema;
16609
- var init_Challenge = __esm(() => {
16610
- init_zod();
16611
- Schema = object({
16612
- description: optional(string2()),
16613
- digest: optional(string2().check(_regex(/^sha-256=/, "Invalid digest format"))),
16614
- expires: optional(datetime3()),
16615
- id: string2(),
16616
- intent: string2(),
16617
- method: string2(),
16618
- opaque: optional(record(string2(), string2())),
16619
- realm: string2(),
16620
- request: record(string2(), unknown())
16621
- });
16622
- });
16623
-
16624
- // node_modules/mppx/dist/Credential.js
16625
- var init_Credential = () => {};
16626
-
16627
- // node_modules/mppx/dist/Errors.js
16628
- var init_Errors = () => {};
16629
-
16630
- // node_modules/mppx/dist/Expires.js
16631
- var init_Expires = () => {};
16632
-
16633
- // node_modules/mppx/dist/Method.js
16634
- var exports_Method = {};
16635
- __export(exports_Method, {
16636
- toServer: () => toServer,
16637
- toClient: () => toClient,
16638
- from: () => from
16639
- });
16640
- function from(method) {
16641
- return method;
16642
- }
16643
- function toClient(method, options) {
16644
- const { context, createCredential } = options;
16645
- return {
16646
- ...method,
16647
- context,
16648
- createCredential
16649
- };
16650
- }
16651
- function toServer(method, options) {
16652
- const { defaults, html, request, respond, transport, verify } = options;
16653
- return {
16654
- ...method,
16655
- defaults,
16656
- html,
16657
- request,
16658
- respond,
16659
- transport,
16660
- verify
16661
- };
16662
- }
16663
-
16664
- // node_modules/mppx/dist/Receipt.js
16665
- var Schema2;
16666
- var init_Receipt = __esm(() => {
16667
- init_zod();
16668
- Schema2 = object({
16669
- method: string2(),
16670
- reference: string2(),
16671
- externalId: optional(string2()),
16672
- status: literal("success"),
16673
- timestamp: datetime3()
16674
- });
16675
- });
16676
-
16677
- // node_modules/mppx/dist/Store.js
16678
- var init_Store = () => {};
16679
-
16680
16650
  // node_modules/mppx/dist/index.js
16681
16651
  var init_dist = __esm(() => {
16682
- init_BodyDigest();
16683
- init_Challenge();
16684
- init_Credential();
16685
- init_Errors();
16686
- init_Expires();
16687
- init_PaymentRequest();
16688
- init_Receipt();
16689
- init_Store();
16690
16652
  init_zod();
16691
16653
  });
16692
16654
 
@@ -19839,7 +19801,8 @@ function registerSearchCommand(program2) {
19839
19801
  "Content-Type": "application/json",
19840
19802
  "X-Payment-Tx": txHash,
19841
19803
  "X-Payment-Amount": amount2,
19842
- "X-Payment-Token": token
19804
+ "X-Payment-Token": token,
19805
+ "X-Twin-Id": auth.twinId
19843
19806
  }
19844
19807
  });
19845
19808
  spinner.stop();
@@ -19962,7 +19925,8 @@ function registerImageCommand(program2) {
19962
19925
  method: "POST",
19963
19926
  headers: {
19964
19927
  "Content-Type": "application/json",
19965
- "X-Payment-Tx": txHash
19928
+ "X-Payment-Tx": txHash,
19929
+ "X-Twin-Id": auth.twinId
19966
19930
  },
19967
19931
  body: JSON.stringify({
19968
19932
  prompt,
@@ -20060,7 +20024,7 @@ function registerAmazonCommand(program2) {
20060
20024
  q: `site:amazon.com ${query}`
20061
20025
  });
20062
20026
  const res = await fetch(`${mppBase2}/v1/search/google_shopping?${params}`, {
20063
- headers: { "X-Payment-Tx": txHash }
20027
+ headers: { "X-Payment-Tx": txHash, "X-Twin-Id": auth.twinId }
20064
20028
  });
20065
20029
  spinner.stop();
20066
20030
  if (!res.ok) {
@@ -20208,7 +20172,8 @@ async function payAndCall(mppBase2, method, path2, auth, wallet, amountOrAuto, b
20208
20172
  throw new Error("Payment sent but no tx hash returned.");
20209
20173
  const headers = {
20210
20174
  "Content-Type": "application/json",
20211
- "X-Payment-Tx": txHash
20175
+ "X-Payment-Tx": txHash,
20176
+ "X-Twin-Id": auth.twinId
20212
20177
  };
20213
20178
  const fetchOpts = { method, headers };
20214
20179
  if (body && method !== "GET") {
@@ -20696,7 +20661,7 @@ function registerDocsCommand(program2) {
20696
20661
  dim("CREATED")
20697
20662
  ],
20698
20663
  ...d.docs.map((doc2) => [
20699
- doc2.id.slice(0, 8),
20664
+ doc2.id,
20700
20665
  doc2.title || doc2.filename,
20701
20666
  doc2.file_type || "-",
20702
20667
  doc2.is_processed ? "processed" : doc2.is_parsed ? "parsed" : "pending",
@@ -20989,7 +20954,7 @@ function registerFriendsCommand(program2) {
20989
20954
  const rows = [
20990
20955
  [dim("ID"), dim("NAME"), dim("HANDLE"), dim("TYPE"), dim("SINCE")],
20991
20956
  ...items.map((f) => [
20992
- f.friendship_id.slice(0, 8),
20957
+ f.friendship_id,
20993
20958
  f.friend.name ?? "-",
20994
20959
  f.friend.handle ?? "-",
20995
20960
  f.friend.type,
@@ -21037,6 +21002,191 @@ function registerFriendsCommand(program2) {
21037
21002
  process.exit(1);
21038
21003
  }
21039
21004
  });
21005
+ program2.command("requests").description("List pending friend requests (incoming and outgoing)").option("--direction <incoming|outgoing|all>", "Filter direction", "all").option("--json", "Output as JSON").action(async (opts) => {
21006
+ const auth = resolveAuth();
21007
+ const spinner = opts.json ? null : ora("Loading requests...").start();
21008
+ try {
21009
+ const data = await amikoWebFetch(auth, "/api/friends/requests");
21010
+ spinner?.stop();
21011
+ renderOutput(data, (d) => {
21012
+ const dir = opts.direction;
21013
+ const showIn = dir === "all" || dir === "incoming";
21014
+ const showOut = dir === "all" || dir === "outgoing";
21015
+ const incoming = d.incoming ?? [];
21016
+ const outgoing = d.outgoing ?? [];
21017
+ if (showIn) {
21018
+ console.log(heading(`Incoming (${incoming.length})`));
21019
+ if (incoming.length === 0) {
21020
+ console.log(dim(" none"));
21021
+ } else {
21022
+ const rows = [
21023
+ [dim("FRIENDSHIP"), dim("FROM"), dim("HANDLE"), dim("SENT")],
21024
+ ...incoming.map((r) => [
21025
+ r.friendship_id,
21026
+ r.user.name ?? "-",
21027
+ r.user.handle ?? "-",
21028
+ r.sent_at?.slice(0, 10) ?? "-"
21029
+ ])
21030
+ ];
21031
+ console.log(table(rows));
21032
+ console.log(dim(`
21033
+ Accept with: amiko friends accept <friendshipId>`));
21034
+ }
21035
+ }
21036
+ if (showOut) {
21037
+ if (showIn)
21038
+ console.log();
21039
+ console.log(heading(`Outgoing (${outgoing.length})`));
21040
+ if (outgoing.length === 0) {
21041
+ console.log(dim(" none"));
21042
+ } else {
21043
+ const rows = [
21044
+ [dim("FRIENDSHIP"), dim("TO"), dim("HANDLE"), dim("SENT")],
21045
+ ...outgoing.map((r) => [
21046
+ r.friendship_id,
21047
+ r.user.name ?? "-",
21048
+ r.user.handle ?? "-",
21049
+ r.sent_at?.slice(0, 10) ?? "-"
21050
+ ])
21051
+ ];
21052
+ console.log(table(rows));
21053
+ }
21054
+ }
21055
+ }, { json: opts.json });
21056
+ } catch (e) {
21057
+ spinner?.stop();
21058
+ console.error(error(e instanceof Error ? e.message : String(e)));
21059
+ process.exit(1);
21060
+ }
21061
+ });
21062
+ const reports = program2.command("reports").description("Friend matching reports");
21063
+ reports.command("list").description("List friend matching reports").option("--page <n>", "Page number", "1").option("--limit <n>", "Page size (1-50)", "20").option("--json", "Output as JSON").action(async (opts) => {
21064
+ const auth = resolveAuth();
21065
+ const spinner = opts.json ? null : ora("Loading reports...").start();
21066
+ try {
21067
+ const data = await amikoWebFetch(auth, "/api/friends/reports", {
21068
+ query: { page: opts.page, limit: opts.limit }
21069
+ });
21070
+ spinner?.stop();
21071
+ renderOutput(data, (d) => {
21072
+ const items = d.items ?? [];
21073
+ if (items.length === 0) {
21074
+ console.log(dim("No reports."));
21075
+ return;
21076
+ }
21077
+ console.log(heading(`Reports (${items.length}/${d.total ?? items.length})`));
21078
+ const rows = [
21079
+ [
21080
+ dim("ID"),
21081
+ dim("TYPE"),
21082
+ dim("STATUS"),
21083
+ dim("SCORE"),
21084
+ dim("WITH"),
21085
+ dim("CREATED")
21086
+ ],
21087
+ ...items.map((r) => {
21088
+ const other = r.respondent ?? r.initiator;
21089
+ return [
21090
+ r.id,
21091
+ r.report_type ?? "-",
21092
+ r.status ?? "-",
21093
+ r.score_preview != null ? String(r.score_preview) : "-",
21094
+ other?.name ?? other?.handle ?? "-",
21095
+ r.created_at?.slice(0, 10) ?? "-"
21096
+ ];
21097
+ })
21098
+ ];
21099
+ console.log(table(rows));
21100
+ }, { json: opts.json });
21101
+ } catch (e) {
21102
+ spinner?.stop();
21103
+ console.error(error(e instanceof Error ? e.message : String(e)));
21104
+ process.exit(1);
21105
+ }
21106
+ });
21107
+ reports.command("view <id>").description("View a friend matching report").option("--json", "Output as JSON").action(async (id, opts) => {
21108
+ const auth = resolveAuth();
21109
+ const spinner = opts.json ? null : ora("Loading report...").start();
21110
+ try {
21111
+ const data = await amikoWebFetch(auth, `/api/friends/reports/${encodeURIComponent(id)}`);
21112
+ spinner?.stop();
21113
+ renderOutput(data, (d) => {
21114
+ const r = d.report;
21115
+ console.log(heading(`Report ${r.id}`));
21116
+ if (r.report_type)
21117
+ console.log(label("Type", r.report_type));
21118
+ if (r.status)
21119
+ console.log(label("Status", r.status));
21120
+ const other = r.respondent ?? r.initiator;
21121
+ if (other)
21122
+ console.log(label("With", other.name ?? other.handle ?? other.id));
21123
+ if (r.created_at)
21124
+ console.log(label("Created", r.created_at.slice(0, 10)));
21125
+ if (r.teaser_text) {
21126
+ console.log();
21127
+ console.log(r.teaser_text);
21128
+ }
21129
+ if (r.report_json) {
21130
+ console.log();
21131
+ console.log(heading("Report"));
21132
+ console.log(typeof r.report_json === "string" ? r.report_json : JSON.stringify(r.report_json, null, 2));
21133
+ }
21134
+ }, { json: opts.json });
21135
+ } catch (e) {
21136
+ spinner?.stop();
21137
+ console.error(error(e instanceof Error ? e.message : String(e)));
21138
+ process.exit(1);
21139
+ }
21140
+ });
21141
+ program2.command("matches").description("List friend matches").option("--limit <n>", "Max results (1-50)", "10").option("--offset <n>", "Offset", "0").option("--relationship-type <type>", "Filter by relationship type").option("--dimension <dimension>", "Filter by match dimension").option("--json", "Output as JSON").action(async (opts) => {
21142
+ const auth = resolveAuth();
21143
+ const spinner = opts.json ? null : ora("Loading matches...").start();
21144
+ try {
21145
+ const data = await amikoWebFetch(auth, "/api/friends/matches", {
21146
+ query: {
21147
+ limit: opts.limit,
21148
+ offset: opts.offset,
21149
+ relationship_type: opts.relationshipType,
21150
+ dimension: opts.dimension
21151
+ }
21152
+ });
21153
+ spinner?.stop();
21154
+ renderOutput(data, (d) => {
21155
+ const items = d.items ?? [];
21156
+ if (items.length === 0) {
21157
+ console.log(dim("No matches."));
21158
+ return;
21159
+ }
21160
+ console.log(heading(`Matches (${items.length}/${d.total ?? items.length})`));
21161
+ const rows = [
21162
+ [
21163
+ dim("USER"),
21164
+ dim("NAME"),
21165
+ dim("REL"),
21166
+ dim("DIM"),
21167
+ dim("SCORE"),
21168
+ dim("STATUS")
21169
+ ],
21170
+ ...items.map((m) => [
21171
+ m.matched_user_id,
21172
+ m.display_name ?? m.handle ?? "-",
21173
+ m.relationship_type ?? "-",
21174
+ m.match_dimension ?? "-",
21175
+ m.score != null ? String(m.score) : "-",
21176
+ m.friendship_status ?? "-"
21177
+ ])
21178
+ ];
21179
+ console.log(table(rows));
21180
+ if (d.has_more)
21181
+ console.log(dim(`
21182
+ (more available)`));
21183
+ }, { json: opts.json });
21184
+ } catch (e) {
21185
+ spinner?.stop();
21186
+ console.error(error(e instanceof Error ? e.message : String(e)));
21187
+ process.exit(1);
21188
+ }
21189
+ });
21040
21190
  program2.command("remove <friendshipId>").description("Remove a friend").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (friendshipId, opts) => {
21041
21191
  await confirmDestructive({
21042
21192
  action: `Remove friend ${friendshipId}`,
@@ -21057,6 +21207,85 @@ function registerFriendsCommand(program2) {
21057
21207
  });
21058
21208
  }
21059
21209
 
21210
+ // src/commands/users.ts
21211
+ function registerUsersCommand(program2) {
21212
+ program2.command("search <query>").description("Search for users by name or handle").option("--limit <n>", "Max results (1-20)", "10").option("--cursor <cursor>", "Pagination cursor").option("--json", "Output as JSON").action(async (query, opts) => {
21213
+ const auth = resolveAuth();
21214
+ const spinner = opts.json ? null : ora("Searching users...").start();
21215
+ try {
21216
+ const data = await amikoWebFetch(auth, "/api/search", {
21217
+ query: {
21218
+ q: query,
21219
+ type: "people",
21220
+ limit: opts.limit,
21221
+ cursor: opts.cursor
21222
+ }
21223
+ });
21224
+ spinner?.stop();
21225
+ renderOutput(data, (d) => {
21226
+ const items = d.users ?? [];
21227
+ if (items.length === 0) {
21228
+ console.log(dim("No users found."));
21229
+ return;
21230
+ }
21231
+ console.log(heading(`Users (${items.length})`));
21232
+ const rows = [
21233
+ [dim("ID"), dim("NAME"), dim("HANDLE")],
21234
+ ...items.map((u) => [
21235
+ u.id,
21236
+ u.name ?? "-",
21237
+ u.handle ?? "-"
21238
+ ])
21239
+ ];
21240
+ console.log(table(rows));
21241
+ if (d.hasMore && d.nextCursor) {
21242
+ console.log(dim(`
21243
+ Next cursor: ${d.nextCursor}`));
21244
+ }
21245
+ }, { json: opts.json });
21246
+ } catch (e) {
21247
+ spinner?.stop();
21248
+ console.error(error(e instanceof Error ? e.message : String(e)));
21249
+ process.exit(1);
21250
+ }
21251
+ });
21252
+ program2.command("profile <handle>").description("View a user's public profile").option("--json", "Output as JSON").action(async (handle, opts) => {
21253
+ const auth = resolveAuth();
21254
+ const spinner = opts.json ? null : ora("Loading profile...").start();
21255
+ try {
21256
+ const data = await amikoWebFetch(auth, `/api/users/${encodeURIComponent(handle)}`);
21257
+ spinner?.stop();
21258
+ renderOutput(data, (d) => {
21259
+ const u = d.user;
21260
+ console.log(heading(u.name ?? u.handle ?? u.id));
21261
+ console.log(label("ID", u.id));
21262
+ if (u.handle)
21263
+ console.log(label("Handle", u.handle));
21264
+ if (u.created_at)
21265
+ console.log(label("Joined", u.created_at.slice(0, 10)));
21266
+ if (u.last_seen_at)
21267
+ console.log(label("Last seen", u.last_seen_at.slice(0, 10)));
21268
+ if (u.twins && u.twins.length > 0) {
21269
+ console.log(heading(`
21270
+ Twins (${u.twins.length})`));
21271
+ for (const t of u.twins) {
21272
+ console.log(` ${t.name ?? "-"} ${dim(t.handle ?? t.id.slice(0, 8))}`);
21273
+ }
21274
+ }
21275
+ if (Array.isArray(u.mutual_friends) && u.mutual_friends.length > 0) {
21276
+ console.log(label("Mutual friends", String(u.mutual_friends.length)));
21277
+ } else if (typeof u.mutual_friends === "number") {
21278
+ console.log(label("Mutual friends", String(u.mutual_friends)));
21279
+ }
21280
+ }, { json: opts.json });
21281
+ } catch (e) {
21282
+ spinner?.stop();
21283
+ console.error(error(e instanceof Error ? e.message : String(e)));
21284
+ process.exit(1);
21285
+ }
21286
+ });
21287
+ }
21288
+
21060
21289
  // src/commands/feed.ts
21061
21290
  function registerFeedCommand(program2) {
21062
21291
  program2.command("feed").description("Get feed posts").option("--type <for_you|friends>", "Feed type", "friends").option("--hashtag <tag>", "Filter by hashtag").option("--limit <n>", "Max results (default 10, max 100)").option("--cursor <id>", "Pagination cursor").option("--json", "Output as JSON").action(async (opts) => {
@@ -21397,7 +21626,8 @@ var twin = program2.command("twin").description("Update twin identity (name, des
21397
21626
  var docs = program2.command("docs").description("Manage twin RAG documents — list, upload, delete, presign");
21398
21627
  var voice = program2.command("voice").description("Manage twin voice — design, create, clone, reset");
21399
21628
  var avatar = program2.command("avatar").description("Manage twin avatar image");
21400
- var friends = program2.command("friends").description("Manage friendships — list, add, accept, remove");
21629
+ var friends = program2.command("friends").description("Manage friendships — list, requests, add, accept, remove, reports, matches");
21630
+ var users = program2.command("users").description("Search users and view public profiles");
21401
21631
  var post = program2.command("post").description("Create posts and comments on the feed");
21402
21632
  var composio = program2.command("composio").description("Manage Composio app connections");
21403
21633
  registerCreditsCommand(program2);
@@ -21419,6 +21649,7 @@ registerDocsCommand(docs);
21419
21649
  registerVoiceCommand(voice);
21420
21650
  registerAvatarCommand(avatar);
21421
21651
  registerFriendsCommand(friends);
21652
+ registerUsersCommand(users);
21422
21653
  registerFeedCommand(program2);
21423
21654
  registerPostCommand(post);
21424
21655
  registerComposioCommand(composio);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.9.0-beta.12",
3
+ "version": "0.9.0-beta.15",
4
4
  "description": "Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "build": "bun build src/index.ts --outdir dist --target node --format esm",
11
+ "prepublishOnly": "bun run build",
11
12
  "dev": "bun run src/index.ts",
12
13
  "typecheck": "bun x tsc --noEmit",
13
14
  "test": "bun test"