@profullstack/qryptchat 0.1.0 → 0.1.1

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.
@@ -1049,10 +1049,32 @@ var TIMEOUT_MS = 5 * 60 * 1000;
1049
1049
  var PAGE = (title, body) => `<!doctype html><meta charset="utf-8"><title>${title}</title>
1050
1050
  <style>body{font:16px system-ui,sans-serif;display:grid;place-items:center;min-height:90vh;background:#0b0d12;color:#e6e8ee}main{max-width:28rem;text-align:center}</style>
1051
1051
  <main><h1>${title}</h1><p>${body}</p></main>`;
1052
+ function isRemote({ platform = process.platform, env = process.env } = {}) {
1053
+ if (env.QC_NO_BROWSER)
1054
+ return true;
1055
+ return !!(env.SSH_CONNECTION || env.SSH_TTY) && !env.DISPLAY && !env.WAYLAND_DISPLAY && platform !== "darwin" && platform !== "win32";
1056
+ }
1057
+ function parsePasted(input, state) {
1058
+ const text = String(input || "").trim();
1059
+ if (!text)
1060
+ return null;
1061
+ if (/^https?:\/\//i.test(text)) {
1062
+ let url;
1063
+ try {
1064
+ url = new URL(text);
1065
+ } catch {
1066
+ return null;
1067
+ }
1068
+ const got = url.searchParams.get("state");
1069
+ if (got && got !== state)
1070
+ throw new Error("That link belongs to a different login. Run qc login again.");
1071
+ return url.searchParams.get("code");
1072
+ }
1073
+ return /^[A-Za-z0-9_-]{20,}$/.test(text) ? text : null;
1074
+ }
1052
1075
  function openBrowser(url, { platform = process.platform, env = process.env } = {}) {
1053
- if (env.QC_NO_BROWSER || (env.SSH_CONNECTION || env.SSH_TTY) && !env.DISPLAY && !env.WAYLAND_DISPLAY && platform === "linux") {
1076
+ if (isRemote({ platform, env }))
1054
1077
  return false;
1055
- }
1056
1078
  const [cmd, args] = platform === "darwin" ? ["open", [url]] : platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
1057
1079
  try {
1058
1080
  const child = spawn(cmd, args, { stdio: "ignore", detached: true });
@@ -1102,7 +1124,8 @@ async function login(options = {}) {
1102
1124
  const ephemeral = await ephemeralKeypair();
1103
1125
  let server;
1104
1126
  let redirectUri = "oob";
1105
- if (!options.oob) {
1127
+ const oob = options.oob || isRemote({ env });
1128
+ if (!oob) {
1106
1129
  server = createServer();
1107
1130
  await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
1108
1131
  redirectUri = `http://127.0.0.1:${server.address().port}/callback`;
@@ -1124,20 +1147,44 @@ async function login(options = {}) {
1124
1147
  print(` Confirmation code: ${match}`);
1125
1148
  print(" Check that the browser shows the same code before you approve.");
1126
1149
  print("");
1127
- const opened = !options.oob && openBrowser(url.toString(), { env });
1150
+ const opened = !oob && openBrowser(url.toString(), { env });
1128
1151
  print(opened ? " Opened your browser. If it did not, open:" : " Open this link in a browser where you are signed in to qrypt.chat:");
1129
1152
  print(` ${url.toString()}`);
1130
1153
  print("");
1131
1154
  let code;
1155
+ let rl;
1132
1156
  try {
1133
1157
  if (server) {
1134
- code = await waitForCallback(server, state);
1158
+ const waits = [waitForCallback(server, state)];
1159
+ if (process.stdin.isTTY) {
1160
+ rl = createInterface({ input: process.stdin, output: process.stderr });
1161
+ waits.push((async () => {
1162
+ for (;; ) {
1163
+ let line;
1164
+ try {
1165
+ line = await rl.question(" Waiting for the browser… or paste the URL it ended up on: ");
1166
+ } catch {
1167
+ return new Promise(() => {});
1168
+ }
1169
+ const pasted = parsePasted(line, state);
1170
+ if (pasted)
1171
+ return pasted;
1172
+ print(" That is not a qc login code or callback URL.");
1173
+ }
1174
+ })());
1175
+ }
1176
+ code = await Promise.race(waits);
1135
1177
  } else {
1136
- const rl = createInterface({ input: process.stdin, output: process.stderr });
1137
- code = (await rl.question(" Paste the code from the browser: ")).trim();
1138
- rl.close();
1178
+ rl = createInterface({ input: process.stdin, output: process.stderr });
1179
+ for (;; ) {
1180
+ code = parsePasted(await rl.question(" Paste the code (or the URL) from the browser: "), state);
1181
+ if (code)
1182
+ break;
1183
+ print(" That is not a qc login code.");
1184
+ }
1139
1185
  }
1140
1186
  } finally {
1187
+ rl?.close();
1141
1188
  server?.close();
1142
1189
  }
1143
1190
  if (!code)
package/dist/commands.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  parseArgs2,
4
4
  findChat2,
5
5
  main2
6
- } from "./chunk-9kkxje88.js";
6
+ } from "./chunk-12bbzs4r.js";
7
7
  export {
8
8
  HELP2 as HELP,
9
9
  findChat2 as findChat,
package/dist/mcp.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  findChat2
3
- } from "./chunk-9kkxje88.js";
3
+ } from "./chunk-12bbzs4r.js";
4
4
 
5
5
  // src/cli/mcp.js
6
6
  import { createInterface } from "node:readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/qryptchat",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
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": {