@profullstack/qryptchat 0.6.3 → 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/bin/qc.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  // qc: qrypt.chat in the terminal. `qc` opens the chat client; `qc --help` lists the rest.
3
3
  import { readFileSync } from 'node:fs';
4
- import { errorLine, main } from '../dist/commands.js';
4
+ import { errorLine, exitWhenFlushed, main } from '../dist/commands.js';
5
5
 
6
6
  const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
7
7
 
8
8
  main(process.argv.slice(2), { version: pkg.version }).then(
9
- () => process.exit(0),
9
+ () => exitWhenFlushed(0),
10
10
  (err) => {
11
11
  process.stderr.write(`${errorLine(err)}\n`);
12
- process.exit(1);
12
+ exitWhenFlushed(1);
13
13
  },
14
14
  );
@@ -778,6 +778,15 @@ class QcClient {
778
778
  }));
779
779
  return { messages: out, hasMore };
780
780
  }
781
+ async joinGroup(code) {
782
+ return this.request("/api/conversations/join", { method: "POST", body: { code } });
783
+ }
784
+ async groupInvite(conversationId, { reset = false, off = false } = {}) {
785
+ const path = `/api/chat/conversations/${encodeURIComponent(conversationId)}/invite`;
786
+ if (off)
787
+ return this.request(path, { method: "DELETE" });
788
+ return this.request(path, { method: "POST", body: { reset } });
789
+ }
781
790
  async react(conversationId, messageId, emoji, remove = false) {
782
791
  return this.send(conversationId, reactionEnvelope(messageId, emoji, remove), { messageType: REACTION_TYPE });
783
792
  }
@@ -1136,6 +1145,21 @@ async function joinAsAgent(invite, { name, username, env = process.env, fetchImp
1136
1145
  return { session, conversationId: body.conversation_id, operator: body.operator };
1137
1146
  }
1138
1147
 
1148
+ // src/lib/chat/group-invites.js
1149
+ var CODE_RE = /^[A-Za-z0-9_-]{16,64}$/;
1150
+ function parseInviteCode(input) {
1151
+ const text = String(input ?? "").trim();
1152
+ let code = text;
1153
+ if (/^https?:\/\//i.test(text)) {
1154
+ try {
1155
+ code = new URL(text).pathname.match(/\/join\/([^/]+)\/?$/)?.[1] ?? "";
1156
+ } catch {
1157
+ return null;
1158
+ }
1159
+ }
1160
+ return CODE_RE.test(code) ? code : null;
1161
+ }
1162
+
1139
1163
  // src/cli/style.js
1140
1164
  import { emojify, icon } from "@profullstack/hqtui";
1141
1165
  var mark = (name) => {
@@ -1157,6 +1181,12 @@ function who(user, { agent = false } = {}) {
1157
1181
  return `${face}${name}${handle}${pronouns}`;
1158
1182
  }
1159
1183
 
1184
+ // src/cli/exit.js
1185
+ function exitWhenFlushed2(code, { timeoutMs = 30000 } = {}) {
1186
+ setTimeout(() => process.exit(code), timeoutMs);
1187
+ process.stdout.end(() => process.stderr.end(() => process.exit(code)));
1188
+ }
1189
+
1160
1190
  // src/cli/commands.js
1161
1191
  var errorLine2 = (err) => `qc: ${fail(err?.message ?? String(err))}`;
1162
1192
  var HELP2 = `qc: qrypt.chat in your terminal (end-to-end encrypted, ML-KEM-1024)
@@ -1178,6 +1208,8 @@ Usage:
1178
1208
  qc react <chat> <message-id> <emoji> [--remove] react to a message
1179
1209
  qc listen print new messages as they arrive (NDJSON with --json; --notify alerts with it)
1180
1210
  qc mcp run as an MCP server on stdio (list_chats, read_chat, send_message)
1211
+ qc join <link> join a group chat from its invite link (https://qrypt.chat/join/...)
1212
+ qc invite <chat> print a group's invite link (group admins) [--reset makes a new one, --off turns it off]
1181
1213
 
1182
1214
  <chat> is a chat id or part of its name. Options: --json, --url <server> (or QC_URL).
1183
1215
  Keys and tokens are sealed (ChaCha20-Poly1305) in ${"$"}QC_HOME or ~/.config/qc; the key is in your
@@ -1379,6 +1411,31 @@ ${info("Set: qc profile --emoji :telescope: --pronouns she/her --website https:/
1379
1411
  }
1380
1412
  return;
1381
1413
  }
1414
+ case "join": {
1415
+ if (!rest[0])
1416
+ throw new Error("Usage: qc join <invite link>");
1417
+ const code = parseInviteCode(rest[0]);
1418
+ if (!code)
1419
+ throw new Error("That is not a group invite link (https://qrypt.chat/join/...).");
1420
+ const c = await client(flags);
1421
+ const result = await c.joinGroup(code);
1422
+ const chat = (await c.conversations()).find((x) => x.id === result.conversation_id);
1423
+ if (flags.json)
1424
+ return json(result);
1425
+ return out(ok(`${result.joined ? "Joined" : "Already in"} ${chat?.title ?? "the group"}. Try: qc read "${chat?.title ?? result.conversation_id}"`));
1426
+ }
1427
+ case "invite": {
1428
+ if (!rest[0])
1429
+ throw new Error("Usage: qc invite <chat> [--reset | --off]");
1430
+ const c = await client(flags);
1431
+ const chat = findChat2(await c.conversations(), rest[0]);
1432
+ const result = await c.groupInvite(chat.id, { reset: !!flags.reset, off: !!flags.off });
1433
+ if (flags.json)
1434
+ return json(result);
1435
+ if (flags.off)
1436
+ return out(ok(`The invite link for ${chat.title} is off.`));
1437
+ return out(result.invite.url);
1438
+ }
1382
1439
  case "react": {
1383
1440
  const [chatQuery, messageId, emoji] = rest;
1384
1441
  if (!chatQuery || !messageId || !emoji)
@@ -1451,4 +1508,4 @@ ${HELP2}`);
1451
1508
  }
1452
1509
  }
1453
1510
 
1454
- export { errorLine2, HELP2, parseArgs2, findChat2, main2 };
1511
+ export { exitWhenFlushed2, errorLine2, HELP2, parseArgs2, findChat2, main2 };
package/dist/commands.js CHANGED
@@ -1,14 +1,16 @@
1
1
  import {
2
+ exitWhenFlushed2,
2
3
  errorLine2,
3
4
  HELP2,
4
5
  parseArgs2,
5
6
  findChat2,
6
7
  main2
7
- } from "./chunk-1htbyjhe.js";
8
+ } from "./chunk-bdg6cmfy.js";
8
9
  import"./chunk-g9xn5036.js";
9
10
  export {
10
11
  HELP2 as HELP,
11
12
  errorLine2 as errorLine,
13
+ exitWhenFlushed2 as exitWhenFlushed,
12
14
  findChat2 as findChat,
13
15
  main2 as main,
14
16
  parseArgs2 as parseArgs
package/dist/mcp.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import"./chunk-g9xn5036.js";
2
2
  import {
3
3
  findChat2
4
- } from "./chunk-1htbyjhe.js";
4
+ } from "./chunk-bdg6cmfy.js";
5
5
 
6
6
  // src/cli/mcp.js
7
7
  import { createInterface } from "node:readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/qryptchat",
3
- "version": "0.6.3",
3
+ "version": "0.7.0",
4
4
  "description": "qc: qrypt.chat in your terminal. A full-screen end-to-end encrypted chat client (ML-KEM-1024), plus a scriptable CLI and an MCP server.",
5
5
  "type": "module",
6
6
  "bin": {