@profullstack/qryptchat 0.5.1 → 0.6.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.
@@ -1,11 +1,12 @@
1
1
  import {
2
+ DEFAULT_URL,
2
3
  sessionPath,
3
4
  baseUrl,
4
5
  loadSession,
5
6
  unlockSession,
6
7
  storeSession,
7
8
  clearSession
8
- } from "./chunk-vfahhydf.js";
9
+ } from "./chunk-menzxtw0.js";
9
10
 
10
11
  // src/cli/crypto.js
11
12
  import { MlKem1024 as MlKem10242 } from "mlkem";
@@ -1090,11 +1091,56 @@ async function login(options = {}) {
1090
1091
  return { session, save };
1091
1092
  }
1092
1093
 
1094
+ // src/cli/agent.js
1095
+ import { randomBytes as randomBytes2 } from "node:crypto";
1096
+ function parseInvite(input) {
1097
+ const text = String(input ?? "").trim();
1098
+ let base = DEFAULT_URL;
1099
+ let token = text;
1100
+ if (/^https?:\/\//i.test(text)) {
1101
+ const url = new URL(text);
1102
+ base = url.origin;
1103
+ token = url.hash.replace(/^#/, "") || url.searchParams.get("token") || "";
1104
+ }
1105
+ if (!/^[A-Za-z0-9_-]{40,64}$/.test(token))
1106
+ throw new Error("That is not an agent invite link (https://qrypt.chat/agents/join#...).");
1107
+ return { base, token };
1108
+ }
1109
+ function usernameFor(name) {
1110
+ const stem = String(name || "agent").toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 20) || "agent";
1111
+ return `${stem.length >= 3 ? stem : `${stem}_agent`}_${randomBytes2(3).toString("hex")}`.slice(0, 30);
1112
+ }
1113
+ async function joinAsAgent(invite, { name, username, env = process.env, fetchImpl = fetch, vault } = {}) {
1114
+ const { base, token } = parseInvite(invite);
1115
+ const keys1024 = await ephemeralKeypair();
1116
+ const res = await fetchImpl(`${base}/api/agents/redeem`, {
1117
+ method: "POST",
1118
+ headers: { "Content-Type": "application/json" },
1119
+ body: JSON.stringify({ token, username: username || usernameFor(name), displayName: name, publicKey: keys1024.publicKey })
1120
+ });
1121
+ const body = await res.json().catch(() => ({}));
1122
+ if (!res.ok)
1123
+ throw new Error(body.error || `Could not join (HTTP ${res.status})`);
1124
+ const session = {
1125
+ base,
1126
+ access_token: body.access_token,
1127
+ refresh_token: body.refresh_token,
1128
+ expires_at: body.expires_at,
1129
+ user: body.user,
1130
+ keys: { keys1024 },
1131
+ agent: true,
1132
+ created_at: new Date().toISOString()
1133
+ };
1134
+ await storeSession(session, env, vault);
1135
+ return { session, conversationId: body.conversation_id, operator: body.operator };
1136
+ }
1137
+
1093
1138
  // src/cli/commands.js
1094
1139
  var HELP2 = `qc: qrypt.chat in your terminal (end-to-end encrypted, ML-KEM-1024)
1095
1140
 
1096
1141
  Usage:
1097
1142
  qc open the chat client (signs you in first if needed)
1143
+ qc agent join <link> join as an AI agent from an invite (makes its own keys) [--name --username]
1098
1144
  qc login [--oob] sign in through your browser; --oob to paste a code (SSH)
1099
1145
  qc logout forget this terminal's session and keys
1100
1146
  qc whoami who this terminal is signed in as
@@ -1127,7 +1173,7 @@ function parseArgs2(argv) {
1127
1173
  const [k, v] = a.slice(2).split("=", 2);
1128
1174
  if (v !== undefined)
1129
1175
  args.flags[k] = v;
1130
- else if (["url", "n", "limit", "reply", "emoji", "pronouns", "website", "bio"].includes(k) && argv[i + 1] !== undefined && !argv[i + 1].startsWith("--"))
1176
+ else if (["url", "n", "limit", "reply", "emoji", "pronouns", "website", "bio", "name", "username"].includes(k) && argv[i + 1] !== undefined && !argv[i + 1].startsWith("--"))
1131
1177
  args.flags[k] = argv[++i];
1132
1178
  else
1133
1179
  args.flags[k] = true;
@@ -1196,6 +1242,21 @@ async function main2(argv, { version = "0.0.0" } = {}) {
1196
1242
  const { runTui } = await import("./tui.js");
1197
1243
  return runTui(c, { initialChat: rest[0] });
1198
1244
  }
1245
+ case "agent": {
1246
+ if (rest[0] !== "join" || !rest[1])
1247
+ throw new Error("Usage: qc agent join <invite link> [--name NAME] [--username NAME]");
1248
+ const { session, conversationId, operator } = await joinAsAgent(rest[1], {
1249
+ name: typeof flags.name === "string" ? flags.name : undefined,
1250
+ username: typeof flags.username === "string" ? flags.username : undefined
1251
+ });
1252
+ if (flags.json)
1253
+ return json({ user: session.user, conversation_id: conversationId, operator });
1254
+ out(`Joined qrypt.chat as @${session.user?.username} (agent), operated by @${operator?.username ?? "?"}.`);
1255
+ out(`Keys made here and sealed in ${sessionPath()}; only the public key left this machine.`);
1256
+ const chatName = operator?.display_name || operator?.username || "<chat>";
1257
+ out(`Your chat with ${chatName} is open. Try: qc listen or qc send "${chatName}" "hello" or qc mcp`);
1258
+ return;
1259
+ }
1199
1260
  case "login": {
1200
1261
  const { session } = await login({ oob: !!flags.oob });
1201
1262
  out(`Signed in as @${session.user?.username ?? "unknown"} on ${session.base}. Keys sealed (ChaCha20-Poly1305) in ${sessionPath()}.`);
@@ -187,4 +187,4 @@ function clearSession(env = process.env) {
187
187
  rmSync(file);
188
188
  }
189
189
 
190
- export { configDir, sessionPath, baseUrl, loadSession, unlockSession, storeSession, clearSession };
190
+ export { DEFAULT_URL, configDir, sessionPath, baseUrl, loadSession, unlockSession, storeSession, clearSession };
package/dist/commands.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  parseArgs2,
4
4
  findChat2,
5
5
  main2
6
- } from "./chunk-byfvkvkr.js";
7
- import"./chunk-vfahhydf.js";
6
+ } from "./chunk-074nrnc8.js";
7
+ import"./chunk-menzxtw0.js";
8
8
  export {
9
9
  HELP2 as HELP,
10
10
  findChat2 as findChat,
package/dist/mcp.js CHANGED
@@ -1,7 +1,7 @@
1
- import"./chunk-vfahhydf.js";
1
+ import"./chunk-menzxtw0.js";
2
2
  import {
3
3
  findChat2
4
- } from "./chunk-byfvkvkr.js";
4
+ } from "./chunk-074nrnc8.js";
5
5
 
6
6
  // src/cli/mcp.js
7
7
  import { createInterface } from "node:readline";
package/dist/tui.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  configDir
3
- } from "./chunk-vfahhydf.js";
3
+ } from "./chunk-menzxtw0.js";
4
4
 
5
5
  // src/cli/tui.js
6
6
  import { createApp, createImageStore, drawIcon, drawRichText, editText, emojify, insertText, stringWidth, truncate, widgets, wrap } from "@profullstack/hqtui";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/qryptchat",
3
- "version": "0.5.1",
3
+ "version": "0.6.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": {