@candledottv/cli 0.13.6 → 0.13.7

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 +11 -1
  2. package/dist/index.js +148 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -125,7 +125,7 @@ emergency procedure, rebinding, trusted wallets and API key access levels are al
125
125
  | `candle token edit <mint> [--chain solana\|hood] [--description <text>] [--website <url>] [--x <url>] [--telegram <url>] [--discord <url>] [--clear <field>]...` | As the token's creator, set Candle's copy of its description and links; `--clear` removes one. Name, symbol and image cannot be changed, and nothing is written on chain. Needs a `launch:write` key from the launching account. Served only where `TOKEN_METADATA_EDIT_ENABLED` is on, and `METADATA_EDIT_DISABLED` elsewhere. |
126
126
  | `candle token history <mint> [--chain solana\|hood]` | The token's metadata edits, newest first. Public; no key sent. |
127
127
  | `candle token events <mint> [--chain solana\|hood] [--kind trade\|signal\|callout] [--since <iso-8601>] [--limit <n>] [--before <cursor>]` | One token's signals, callouts and tracked-wallet trades, one event per line: time, kind, name or short address (with the wallet's kind), action and size, sells with `W` or `L` when known. `--kind` filters the fetched page locally; `--before` continues the page. No key needed; a configured key is sent so group names follow its tier. Served only where `TOKEN_EVENTS_ENABLED` is on, and `TOKEN_EVENTS_DISABLED` elsewhere. |
128
- | `candle token about\|claim\|section set\|section restore\|history\|flag <address>` | The token's About page: `about` and `history` are public reads with no key, `about --capabilities` is this account's own eligibility (`token-pages:read`), and `claim`, `section set\|restore` and `flag` act as the key's account (`token-pages:write`, granted only through `keys create --scopes`). Every write needs the account's own current 0.5% of supply (any positive balance to flag), checked by Candle. The creator proof is a message the creator signs on their own machine (`claim --creator --challenge`, then `--challenge-id` and `--signature`), never a transaction. Section text comes from `--file`; a revision conflict exits 4 and leaves the file untouched; no answer exits 3 with the `--idempotency-key` to rerun with. |
128
+ | `candle token about\|claim\|section set\|section restore\|history\|flag\|comment\|comments <address>` | The token's About page: `about`, `history` and `comments` are public reads with no key, `about --capabilities` is this account's own eligibility (`token-pages:read`), and `claim`, `section set\|restore`, `comment` and `flag` act as the key's account (`token-pages:write`, granted only through `keys create --scopes`). Every write needs the account's own current 0.5% of supply (any positive balance to flag), checked by Candle. The creator proof is a message the creator signs on their own machine (`claim --creator --challenge`, then `--challenge-id` and `--signature`), never a transaction. Section text comes from `--file`; a revision conflict exits 4 and leaves the file untouched; no answer exits 3 with the `--idempotency-key` to rerun with. |
129
129
  | `candle plans` | The plans the API serves (`GET /api/v1/agent/plans`): each plan's monthly price, agent trade fee, Hyperliquid perps builder fee, the limits a new key gets, and one row per capability (buying and selling tokens not launched on Candle, self-launch, atomic launch, linked wallets, perps, limit orders). Every number is the server's; the CLI bundles none. Needs no key and sends none. `--json` is the server's body. Your own plan is `candle doctor`'s Plan row. |
130
130
  | `candle lp pools\|add\|positions\|remove\|claim` | Meteora DAMM v2 liquidity from a TEE wallet through its bound key (`lp:write`, opt-in). `pools <token>` lists pools with liquidity, fee, volume and an estimated yield; `add <pool> --amount <n> <token> --wallet <tee>` opens or grows a position at the pool's ratio after showing both tokens' Token-2022 warnings; `positions` lists every position across your TEE wallets; `remove <position> --percent <n>` withdraws (100% also claims fees and closes the position); `claim <position>` claims fees and rewards. No Candle fee. A `tee sweep` closes open positions itself; see TEE wallets. |
131
131
  | `candle perps setup\|deposit\|open\|close\|cancel\|orders\|positions\|leverage` | Hyperliquid perpetuals from the EVM TEE wallet bound to the key (`perps:write`, opt-in). `setup` approves Candle's builder fee (0.1%, none on Max) once; `deposit <amount> <SOL\|USDC\|ETH\|USDG>` moves funds onto the Hyperliquid account through Relay from the key's Solana or Hood TEE wallet (`swap:write`; `--to` names the EVM TEE wallet from Solana; `deposit status <id>` reads it; no withdrawal yet); `open <coin> <long\|short> <size>` trades at market, or as a limit with `--price`, with optional `--tp` and `--sl`; `close <coin>` closes reduce-only; `cancel <cloid>`; `leverage <coin> <x> [--isolated]`; `orders` and `positions` read live. Candle builds each action within the key's limits; the CLI recomputes its hash and checks the action type and Candle's builder before anything is signed, then submits the signed action to Hyperliquid itself. `CANDLE_HYPERLIQUID_BUILDER` deliberately replaces the release builder pin; its use is shown in the preview. |
@@ -558,3 +558,13 @@ exit 3 retains the key for an explicit retry. The post cannot be edited.
558
558
  soft-deletes a post owned by that key's account, including posts from its agents, after selling too.
559
559
  Both commands support `--json` and default-off server refusals. Package release remains a separate
560
560
  human-owned step.
561
+
562
+ ### Token comments (unreleased source)
563
+
564
+ `candle token comment <address> --file ./comment.txt --chain solana --idempotency-key <saved-key>`
565
+ posts 1–500 characters under the same `token-pages:write` key and fresh 0.5% eligibility as a
566
+ section edit. The file is validated locally and stays untouched; a lost answer retries identical
567
+ bytes once, and exit 3 retains the key. A comment cannot be edited and is never pushed to holders.
568
+ `candle token comments <address> [--limit <n>] [--cursor <c>]` lists comments newest first with
569
+ tombstones; it is public and sends no key. `candle token post delete` removes the key account's own
570
+ comment, including after selling. Package release remains a separate human-owned step.
package/dist/index.js CHANGED
@@ -3476,7 +3476,7 @@ var init_terminal_theme = __esm(() => {
3476
3476
  });
3477
3477
 
3478
3478
  // src/version.ts
3479
- var CLI_VERSION = "0.13.6";
3479
+ var CLI_VERSION = "0.13.7";
3480
3480
 
3481
3481
  // src/presentation.ts
3482
3482
  function terminalStyle(deps, stream, machine = false) {
@@ -44280,13 +44280,15 @@ function isTokenPageTool(name) {
44280
44280
  }
44281
44281
  function buildTokenPageRequest(name, args, cfg) {
44282
44282
  const { chain: chain2, address, id, section: sectionName, cursor, limit, idempotencyKey, ...body } = args;
44283
- const publicRead = name === "candle_token_page_updates" || name === "candle_token_page_about" || name === "candle_token_page_burns" || name === "candle_token_page_history";
44283
+ const publicRead = name === "candle_token_page_updates" || name === "candle_token_page_comments" || name === "candle_token_page_about" || name === "candle_token_page_burns" || name === "candle_token_page_history";
44284
44284
  if (!publicRead && !cfg.apiKey)
44285
44285
  throw new Error("CANDLE_AGENT_API_KEY with token-pages scopes is required for this tool.");
44286
44286
  const suffix = {
44287
44287
  candle_token_page_updates: "/updates",
44288
44288
  candle_token_page_update: "/updates",
44289
44289
  candle_token_page_post_delete: `/posts/${encodeURIComponent(String(id))}`,
44290
+ candle_token_page_comments: "/comments",
44291
+ candle_token_page_comment: "/comments",
44290
44292
  candle_token_page_seen: "/seen",
44291
44293
  candle_token_page_mark_seen: "/seen",
44292
44294
  candle_token_page_about: "",
@@ -44407,6 +44409,8 @@ var init_token_pages = __esm(() => {
44407
44409
  candle_token_page_updates: { ...token, ...pagination },
44408
44410
  candle_token_page_update: { ...token, ...write, text: exports_external.string() },
44409
44411
  candle_token_page_post_delete: { ...token, ...write, id: exports_external.string().uuid() },
44412
+ candle_token_page_comments: { ...token, ...pagination },
44413
+ candle_token_page_comment: { ...token, ...write, text: exports_external.string() },
44410
44414
  candle_token_page_seen: token,
44411
44415
  candle_token_page_mark_seen: { ...token, ...write, visibleUpdateSequence: revision },
44412
44416
  candle_token_page_about: token,
@@ -44967,10 +44971,22 @@ function registerToolSubset(server, options) {
44967
44971
  }, (args) => tokenPageRelay("candle_token_page_update", args, getConfig(), doFetch));
44968
44972
  register("candle_token_page_post_delete", {
44969
44973
  title: "Token page post delete",
44970
- description: "MUTATION: delete your own post, including after selling. Requires effective server switches; private calls require explicit token-pages scopes. All switches default off.",
44974
+ description: "MUTATION: delete your own update or comment, including after selling. Requires effective server switches; private calls require explicit token-pages scopes. All switches default off.",
44971
44975
  inputSchema: tokenPageShapes.candle_token_page_post_delete,
44972
44976
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }
44973
44977
  }, (args) => tokenPageRelay("candle_token_page_post_delete", args, getConfig(), doFetch));
44978
+ register("candle_token_page_comments", {
44979
+ title: "Token page comments",
44980
+ description: "READ: list a token page's comments, newest first, with tombstones for hidden, removed or deleted ones. Public and keyless; use cursor/limit (1-50). Requires effective server switches. All switches default off.",
44981
+ inputSchema: tokenPageShapes.candle_token_page_comments,
44982
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
44983
+ }, (args) => tokenPageRelay("candle_token_page_comments", args, getConfig(), doFetch));
44984
+ register("candle_token_page_comment", {
44985
+ title: "Token page comment",
44986
+ description: "MUTATION: append an immutable comment, up to 500 characters, with fresh 0.5% eligibility. Requires token-pages:write. Comments are never pushed to holders and cannot be edited; delete with candle_token_page_post_delete. Supply a durable idempotencyKey and reuse the same key and body after a lost answer. A transport failure retries once with identical bytes. HTTP refusals are not retried. All token page switches default off.",
44987
+ inputSchema: tokenPageShapes.candle_token_page_comment,
44988
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }
44989
+ }, (args) => tokenPageRelay("candle_token_page_comment", args, getConfig(), doFetch));
44974
44990
  register("candle_token_page_seen", {
44975
44991
  title: "Token page seen",
44976
44992
  description: "READ: list updates or own unread state. Requires effective server switches; private calls require explicit token-pages scopes. All switches default off.",
@@ -45549,6 +45565,8 @@ var init_tools = __esm(() => {
45549
45565
  "candle_token_page_updates",
45550
45566
  "candle_token_page_update",
45551
45567
  "candle_token_page_post_delete",
45568
+ "candle_token_page_comments",
45569
+ "candle_token_page_comment",
45552
45570
  "candle_token_page_seen",
45553
45571
  "candle_token_page_mark_seen",
45554
45572
  "candle_token_forensics",
@@ -76550,6 +76568,8 @@ var HELP = {
76550
76568
  "candle token history <mint> [--chain solana|hood] [--limit <n>] [--cursor <c>] [--json]",
76551
76569
  "candle token history <address> <thesis|tokenomics|lore> [--limit <n>] [--cursor <c>] [--json]",
76552
76570
  "candle token update <address> --file <path> [--chain solana|hood] [--idempotency-key <key>] [--json]",
76571
+ "candle token comment <address> --file <path> [--chain solana|hood] [--idempotency-key <key>] [--json]",
76572
+ "candle token comments <address> [--chain solana|hood] [--limit <n>] [--cursor <c>] [--json]",
76553
76573
  "candle token post delete <address> <post-id> [--chain solana|hood] [--idempotency-key <key>] [--json]",
76554
76574
  "candle token flag <address> --content <id> --reason <reason> [--detail <text>] [--json]"
76555
76575
  ],
@@ -76585,8 +76605,19 @@ var HELP = {
76585
76605
  description: "The token's metadata edits, newest first; with a section, that section's versions"
76586
76606
  },
76587
76607
  { invocation: "update <address>", description: "Post an immutable holder update from --file (1000 characters)" },
76588
- { invocation: "post delete <address> <post-id>", description: "Delete your own post, including after selling" },
76589
- { invocation: "flag <address>", description: "Report one exact version to Candle's review queue" }
76608
+ {
76609
+ invocation: "comment <address>",
76610
+ description: "Post an immutable comment from --file (500 characters); never pushed to holders"
76611
+ },
76612
+ { invocation: "comments <address>", description: "The token's comments, newest first, with tombstones" },
76613
+ {
76614
+ invocation: "post delete <address> <post-id>",
76615
+ description: "Delete your own update or comment, including after selling"
76616
+ },
76617
+ {
76618
+ invocation: "flag <address>",
76619
+ description: "Report one exact version, update or comment to Candle's review queue"
76620
+ }
76590
76621
  ],
76591
76622
  flags: [
76592
76623
  { invocation: "--chain <chain>", description: "solana (default) or hood; Hood cluster analysis is unavailable" },
@@ -76596,7 +76627,7 @@ var HELP = {
76596
76627
  },
76597
76628
  {
76598
76629
  invocation: "--limit <n>",
76599
- description: "events, history or vamp-candidates: how many to read (events 1 to 1000, history 1 to 50)"
76630
+ description: "events, history, comments or vamp-candidates: how many to read (events 1 to 1000, history and comments 1 to 50)"
76600
76631
  },
76601
76632
  { invocation: "--min-copies <n>", description: "vamp-candidates: copies in the last 24h, at least" },
76602
76633
  { invocation: "--since <iso-8601>", description: "events: list only events at or after this time" },
@@ -76607,13 +76638,16 @@ var HELP = {
76607
76638
  description: "The Solana vault key that pays, by label or address (needed when the vault has several)"
76608
76639
  },
76609
76640
  { invocation: "--yes", description: "Pay without the typed confirm (the vault unlock still prompts)" },
76610
- { invocation: "--file <path>", description: "section set: the section text, UTF-8; an empty file needs --clear" },
76641
+ {
76642
+ invocation: "--file <path>",
76643
+ description: "section set, update or comment: the text, UTF-8; an empty section file needs --clear"
76644
+ },
76611
76645
  {
76612
76646
  invocation: "--base-revision <n>",
76613
76647
  description: "section set|restore: the revision you started from (0 first)"
76614
76648
  },
76615
76649
  { invocation: "--version-id <id>", description: "section restore: the earlier version to copy into a new one" },
76616
- { invocation: "--content <id>", description: "flag: the exact version id being reported" },
76650
+ { invocation: "--content <id>", description: "flag: the exact version, update or comment id being reported" },
76617
76651
  {
76618
76652
  invocation: "--reason <reason>",
76619
76653
  description: "flag: spam, scam, abuse, misleading, unsafe_link or other; --detail adds up to 500 characters"
@@ -92052,6 +92086,8 @@ var MCP_TOOL_NAMES = [
92052
92086
  "candle_token_page_updates",
92053
92087
  "candle_token_page_update",
92054
92088
  "candle_token_page_post_delete",
92089
+ "candle_token_page_comments",
92090
+ "candle_token_page_comment",
92055
92091
  "candle_token_page_seen",
92056
92092
  "candle_token_page_mark_seen",
92057
92093
  "candle_token_forensics",
@@ -92121,6 +92157,7 @@ var READ_ONLY_TOOL_NAMES = [
92121
92157
  "candle_token_page_burns",
92122
92158
  "candle_token_page_history",
92123
92159
  "candle_token_page_updates",
92160
+ "candle_token_page_comments",
92124
92161
  "candle_token_forensics",
92125
92162
  "candle_token_clusters",
92126
92163
  "candle_get_agent_profile",
@@ -103678,12 +103715,7 @@ async function tokenClusters(args, ctx) {
103678
103715
  if (ctx.json)
103679
103716
  out.write(`${JSON.stringify(result)}
103680
103717
  `);
103681
- else if (result.status === "unavailable")
103682
- writeFields(ctx.deps, "stdout", [
103683
- ["Status", result.message],
103684
- ["Reason", result.reason]
103685
- ]);
103686
- else {
103718
+ else if (result.status === "unavailable") {} else {
103687
103719
  writeFields(ctx.deps, "stdout", [
103688
103720
  ["Fetched", formatTime(result.fetchedAt)],
103689
103721
  ["Cluster supply", `${result.analysis.clusterSupplyPct}%`]
@@ -103873,7 +103905,9 @@ var USAGE13 = {
103873
103905
  section: `Usage: candle token section set <address> <thesis|tokenomics|lore> --file <path> --base-revision <n> [--clear] [--chain solana|hood] [--idempotency-key <key>]
103874
103906
  candle token section restore <address> <thesis|tokenomics|lore> --version-id <id> --base-revision <n> [--chain solana|hood] [--idempotency-key <key>]`,
103875
103907
  history: "Usage: candle token history <address> <thesis|tokenomics|lore> [--chain solana|hood] [--limit <n>] [--cursor <c>]",
103876
- flag: "Usage: candle token flag <address> --content <id> --reason <spam|scam|abuse|misleading|unsafe_link|other> [--detail <text>] [--chain solana|hood] [--idempotency-key <key>]"
103908
+ flag: "Usage: candle token flag <address> --content <id> --reason <spam|scam|abuse|misleading|unsafe_link|other> [--detail <text>] [--chain solana|hood] [--idempotency-key <key>]",
103909
+ comment: "Usage: candle token comment <address> --file <path> [--chain solana|hood] [--idempotency-key <key>]",
103910
+ comments: "Usage: candle token comments <address> [--chain solana|hood] [--limit <n>] [--cursor <c>]"
103877
103911
  };
103878
103912
  var REFUSALS = {
103879
103913
  feature_off: "This token page action is not available yet on this Candle server. Nothing was written.",
@@ -104459,7 +104493,7 @@ async function tokenFlag(args, ctx) {
104459
104493
  return usage12("--chain must be solana or hood.");
104460
104494
  const contentId = values["--content"];
104461
104495
  if (contentId === undefined || !UUID2.test(contentId))
104462
- return usage12("--content is the exact version id being reported (candle token about or history).");
104496
+ return usage12("--content is the exact version, update or comment id being reported (candle token about, history or comments).");
104463
104497
  const reason = values["--reason"];
104464
104498
  if (!reason || !FLAG_REASONS.includes(reason))
104465
104499
  return usage12(`--reason is one of ${FLAG_REASONS.join(", ")}.`);
@@ -104540,6 +104574,102 @@ async function tokenUpdate(args, ctx) {
104540
104574
  ], "Update posted");
104541
104575
  return 0;
104542
104576
  }
104577
+ async function tokenComment(args, ctx) {
104578
+ const usage12 = usageFor(ctx, USAGE13.comment);
104579
+ const parsed = parseArgs(args, { valueFlags: ["--chain", "--file", "--idempotency-key"], pathFlags: ["--file"] });
104580
+ if ("error" in parsed)
104581
+ return usage12(parsed.error);
104582
+ const chain2 = chainOf(parsed.values), address = parsed.positionals[0], file = parsed.values["--file"];
104583
+ if (!chain2 || !address || !file || parsed.positionals.length !== 1)
104584
+ return usage12();
104585
+ let bytes;
104586
+ try {
104587
+ bytes = await ctx.deps.readBytes(file);
104588
+ } catch {
104589
+ return usage12("The comment file could not be read.");
104590
+ }
104591
+ const checked = sectionText(bytes, "comment");
104592
+ if ("error" in checked || !checked.text.trim())
104593
+ return usage12(`The comment must contain 1–${TOKEN_PAGE_LIMITS.comment} valid characters.`);
104594
+ const idempotencyKey = idempotencyKeyOf(parsed.values, usage12);
104595
+ if (typeof idempotencyKey === "number")
104596
+ return idempotencyKey;
104597
+ const apiKey = await requireKey(ctx);
104598
+ if (!apiKey)
104599
+ return 1;
104600
+ const result = await write2(ctx, {
104601
+ method: "POST",
104602
+ path: pagePath(chain2, address, "/comments"),
104603
+ body: { text: checked.text },
104604
+ apiKey,
104605
+ idempotencyKey
104606
+ });
104607
+ if (result === "unknown")
104608
+ return writeUnknown(ctx, idempotencyKey);
104609
+ if (!result.ok)
104610
+ return writeApiFailure(ctx, result, "key");
104611
+ const data = dataOf(result);
104612
+ if (ctx.json)
104613
+ ctx.deps.stdout.write(`${JSON.stringify({ ok: true, data, idempotencyKey })}
104614
+ `);
104615
+ else
104616
+ writeFields(ctx.deps, "stdout", [
104617
+ ["Comment", data.id],
104618
+ ["Retry key", idempotencyKey]
104619
+ ], "Comment posted");
104620
+ return 0;
104621
+ }
104622
+ async function tokenComments(args, ctx) {
104623
+ const usage12 = usageFor(ctx, USAGE13.comments);
104624
+ const parsed = parseArgs(args, { valueFlags: ["--chain", "--limit", "--cursor"] });
104625
+ if ("error" in parsed)
104626
+ return usage12(parsed.error);
104627
+ const { values } = parsed;
104628
+ const chain2 = chainOf(values);
104629
+ const address = parsed.positionals[0];
104630
+ if (parsed.positionals.length !== 1 || !address)
104631
+ return usage12();
104632
+ if (!chain2)
104633
+ return usage12("--chain must be solana or hood.");
104634
+ const limit = values["--limit"];
104635
+ if (limit !== undefined && (!/^[1-9]\d?$/.test(limit) || Number(limit) > 50))
104636
+ return usage12("--limit is a whole number from 1 to 50.");
104637
+ const query3 = new URLSearchParams({
104638
+ ...limit !== undefined ? { limit } : {},
104639
+ ...values["--cursor"] !== undefined ? { cursor: values["--cursor"] } : {}
104640
+ });
104641
+ const result = await read2(ctx, pagePath(chain2, address, `/comments${query3.size ? `?${query3}` : ""}`));
104642
+ if (!result.ok)
104643
+ return writeApiFailure(ctx, result, "none");
104644
+ const data = dataOf(result);
104645
+ if (ctx.json) {
104646
+ ctx.deps.stdout.write(`${JSON.stringify({ ok: true, data })}
104647
+ `);
104648
+ return 0;
104649
+ }
104650
+ if (!data.items.length)
104651
+ ctx.deps.stdout.write(`No comments.
104652
+ `);
104653
+ for (const item of data.items) {
104654
+ writeHeading(ctx.deps, "stdout", item.createdAt);
104655
+ writeFields(ctx.deps, "stdout", [
104656
+ ["Comment", item.id],
104657
+ ["Written by", `${identity3(item.author)}${item.dev ? " (dev)" : ""}`],
104658
+ ["Share then", bpsPercent(item.shareBps)],
104659
+ ["State", item.tombstone ?? "visible"],
104660
+ ["Links", item.linksWithheld ? "withheld pending review" : undefined]
104661
+ ]);
104662
+ if (item.text !== null)
104663
+ ctx.deps.stdout.write(`
104664
+ ${terminalTextKeepingLines(item.text)}
104665
+ `);
104666
+ }
104667
+ if (data.nextCursor)
104668
+ ctx.deps.stdout.write(`
104669
+ More: --cursor ${data.nextCursor}
104670
+ `);
104671
+ return 0;
104672
+ }
104543
104673
  async function tokenPost(args, ctx) {
104544
104674
  const usage12 = usageFor(ctx, "Usage: candle token post delete <address> <post-id> [--chain solana|hood] [--idempotency-key <key>]");
104545
104675
  if (args[0] !== "delete")
@@ -113709,6 +113839,8 @@ var COMMANDS = {
113709
113839
  flag: tokenFlag,
113710
113840
  edit: tokenEdit,
113711
113841
  update: tokenUpdate,
113842
+ comment: tokenComment,
113843
+ comments: tokenComments,
113712
113844
  post: tokenPost
113713
113845
  }
113714
113846
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@candledottv/cli",
3
- "version": "0.13.6",
3
+ "version": "0.13.7",
4
4
  "description": "The Candle CLI: authorize a device from your browser, then manage API keys, wallets, and setup health from the terminal",
5
5
  "type": "module",
6
6
  "bin": {