@vex-chat/cli 0.3.0 → 0.4.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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/vex-chat.js +52 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vex-chat/cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Terminal client for vex-chat.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,7 +31,7 @@
31
31
  "dependencies": {
32
32
  "better-sqlite3": "11.10.0",
33
33
  "msgpackr": "^1.11.9",
34
- "@vex-chat/libvex": "^7.1.2"
34
+ "@vex-chat/libvex": "^7.4.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^25.6.0",
package/src/vex-chat.js CHANGED
@@ -145,6 +145,9 @@ async function main() {
145
145
  case "invite":
146
146
  await inviteCommand(ctx, positionals);
147
147
  break;
148
+ case "entitlements":
149
+ await entitlementsCommand(ctx, positionals);
150
+ break;
148
151
  case "group":
149
152
  await groupCommand(ctx, positionals);
150
153
  break;
@@ -176,6 +179,7 @@ function isCommand(command) {
176
179
  "channels",
177
180
  "chat",
178
181
  "dm",
182
+ "entitlements",
179
183
  "group",
180
184
  "help",
181
185
  "invite",
@@ -285,6 +289,7 @@ async function createContext(flags) {
285
289
  return {
286
290
  dataDir,
287
291
  configPath,
292
+ flags,
288
293
  clientOptions: {
289
294
  dbFolder: path.join(dataDir, "db"),
290
295
  deviceName: "vex-chat-cli",
@@ -377,6 +382,11 @@ function httpFromApiUrl(raw) {
377
382
  }
378
383
  }
379
384
 
385
+ function apiBaseUrl(ctx) {
386
+ const scheme = ctx.clientOptions.unsafeHttp ? "http" : "https";
387
+ return `${scheme}://${ctx.clientOptions.host}`;
388
+ }
389
+
380
390
  function normalizeAccountHost(host) {
381
391
  return String(host ?? DEFAULT_HOST)
382
392
  .trim()
@@ -1495,6 +1505,47 @@ async function inviteCommand(ctx, args) {
1495
1505
  });
1496
1506
  }
1497
1507
 
1508
+ async function entitlementsCommand(ctx, args) {
1509
+ const sub = args.shift() ?? "status";
1510
+ if (sub === "set" || sub === "grant") {
1511
+ const userID = requireArg(args, 0, "user id");
1512
+ const tier = requireArg(args, 1, "tier");
1513
+ if (!["free", "plus", "pro"].includes(tier)) {
1514
+ throw new Error("Tier must be one of: free, plus, pro.");
1515
+ }
1516
+ const devKey = ctx.clientOptions.devApiKey;
1517
+ if (!devKey) {
1518
+ throw new Error(
1519
+ "A dev key is required. Pass --dev-key or set DEV_API_KEY.",
1520
+ );
1521
+ }
1522
+ const expiresAt = ctx.flags["expires-at"]
1523
+ ? String(ctx.flags["expires-at"])
1524
+ : null;
1525
+ const res = await fetch(`${apiBaseUrl(ctx)}/__dev/billing/grants`, {
1526
+ body: JSON.stringify({ expiresAt, tier, userID }),
1527
+ headers: {
1528
+ "Content-Type": "application/json",
1529
+ "x-dev-api-key": devKey,
1530
+ },
1531
+ method: "POST",
1532
+ });
1533
+ if (!res.ok) {
1534
+ throw new Error(
1535
+ `Entitlement grant failed with HTTP ${String(res.status)}: ${await res.text()}`,
1536
+ );
1537
+ }
1538
+ const body = await res.json();
1539
+ console.log(
1540
+ `${color(ROOT_ACCENT, "granted")} ${color("bold", tier)} ${color("dim", `to ${userID}`)} ${color("dim", `source=${body.source ?? "unknown"}`)}`,
1541
+ );
1542
+ return;
1543
+ }
1544
+ throw new Error(
1545
+ "Usage: vex entitlements set <user-id> <free|plus|pro> [--expires-at <iso>] --dev-key <key>",
1546
+ );
1547
+ }
1548
+
1498
1549
  async function groupCommand(ctx, args) {
1499
1550
  const sub = args[0];
1500
1551
  if (sub === "send") {
@@ -4642,6 +4693,7 @@ Commands:
4642
4693
  vex auth requests list pending device login requests
4643
4694
  vex auth accounts
4644
4695
  vex auth use <username>
4696
+ vex entitlements set <user-id> <free|plus|pro> --dev-key <key>
4645
4697
  vex whoami
4646
4698
 
4647
4699
  Flags: