parley-chat 0.2.0 → 0.2.2

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/main.mjs +49 -5
  2. package/package.json +1 -1
package/main.mjs CHANGED
@@ -76,13 +76,14 @@ async function fileExists(path) {
76
76
  var init_files = () => {};
77
77
 
78
78
  // src/main.ts
79
- import { execFile } from "child_process";
79
+ import { execFile, spawn as spawn2 } from "child_process";
80
80
  import { mkdir as mkdir3, readFile as readFile3, rm as rm3, stat as stat3, writeFile as writeFile3 } from "fs/promises";
81
81
  import { homedir as homedir3 } from "os";
82
82
  import { join as join3 } from "path";
83
83
  import { promisify } from "util";
84
84
 
85
85
  // ../shared/src/protocol.ts
86
+ var cliVersion = "0.2.2";
86
87
  var skillDocument = `---
87
88
  name: parley
88
89
  description: Ask a mutual contact's local agent a question through Parley, with both inbound execution and outbound replies approved in Discord.
@@ -113,7 +114,7 @@ You can run this entire setup for your human. Steps 2 and 6 need them present (b
113
114
  - \`parley logout\`: deletes the local token.
114
115
  - \`parley invite\`: creates a single-use code that expires after 48 hours.
115
116
  - \`parley join CODE\`: redeems a friend's invite and waits for their approval.
116
- - \`parley contacts\`: lists mutual contacts and whether their responder is connected.
117
+ - \`parley contacts\`: lists mutual contacts with their Discord display name, responder connectivity, and (when online) their CLI version. Note: your contacts see your CLI version the same way.
117
118
  - \`parley remove NAME\`: removes a mutual contact for both people.
118
119
  - \`parley ask @NAME "question"\`: blocks for a one-off response. It fails immediately when the responder is offline.
119
120
  - \`parley listen\`: runs the local responder in the foreground.
@@ -198,6 +199,9 @@ async function fileExists2(path) {
198
199
  // src/responder.ts
199
200
  import { spawn } from "node:child_process";
200
201
 
202
+ // ../shared/src/protocol.ts
203
+ var cliVersion2 = "0.2.2";
204
+
201
205
  // src/http.ts
202
206
  init_files();
203
207
  async function api2(path, init = {}) {
@@ -228,7 +232,7 @@ Question: ${question}
228
232
  }
229
233
  async function listen() {
230
234
  const auth = await Promise.resolve().then(() => (init_files(), exports_files)).then((module) => module.credentials());
231
- const socket = new WebSocket(wsUrl("/v1/responders/connect"), [`parley.${auth.token}`]);
235
+ const socket = new WebSocket(wsUrl(`/v1/responders/connect?version=${encodeURIComponent(cliVersion2)}`), [`parley.${auth.token}`]);
232
236
  await new Promise((resolve, reject) => {
233
237
  socket.addEventListener("open", () => resolve(), { once: true });
234
238
  socket.addEventListener("error", () => reject(new Error("responder connection failed")), { once: true });
@@ -342,8 +346,14 @@ async function contacts() {
342
346
  const entries = Array.isArray(value?.contacts) ? value.contacts : [];
343
347
  for (const entry of entries) {
344
348
  const contact = object(entry);
345
- if (contact)
346
- process.stdout.write(`${text(contact.username)} (${contact.online === true ? "online" : "offline"})
349
+ if (!contact)
350
+ continue;
351
+ const username = text(contact.username);
352
+ const displayName = typeof contact.displayName === "string" && contact.displayName !== username ? ` (${contact.displayName})` : "";
353
+ const online = contact.online === true;
354
+ const version = typeof contact.version === "string" ? contact.version : null;
355
+ const staleness = online && version !== null && version !== cliVersion ? ` v${version} (outdated)` : online && version !== null ? ` v${version}` : "";
356
+ process.stdout.write(`${username}${displayName} \u2014 ${online ? "online" : "offline"}${staleness}
347
357
  `);
348
358
  }
349
359
  return 0;
@@ -538,6 +548,40 @@ async function doctor() {
538
548
  }, 5000);
539
549
  });
540
550
  checks.push(["handler command found", handler ? "yes" : "no \u2014 the command in config.toml is not on PATH", handler]);
551
+ if (handler) {
552
+ process.stdout.write(`running a test draft through your handler (this is what answers your friends; may take a minute)\u2026
553
+ `);
554
+ const probe = await new Promise((resolve) => {
555
+ const child = spawn2("sh", ["-c", local.command], { stdio: ["pipe", "pipe", "pipe"] });
556
+ const out = [];
557
+ const err = [];
558
+ child.stdout.on("data", (chunk) => out.push(chunk));
559
+ child.stderr.on("data", (chunk) => err.push(chunk));
560
+ const timer = setTimeout(() => {
561
+ child.kill();
562
+ resolve({ ok: false, detail: "timed out after 120s \u2014 the handler hung instead of answering" });
563
+ }, 120000);
564
+ child.on("close", (code) => {
565
+ clearTimeout(timer);
566
+ const text2 = Buffer.concat(out).toString("utf8").trim();
567
+ const errText = Buffer.concat(err).toString("utf8").trim().split(`
568
+ `).at(-1) ?? "";
569
+ if (code !== 0)
570
+ resolve({ ok: false, detail: `exited ${code}${errText ? ` \u2014 ${errText}` : ""}` });
571
+ else if (text2.length === 0)
572
+ resolve({ ok: false, detail: "produced no output \u2014 a real ask would auto-deny" });
573
+ else
574
+ resolve({ ok: true, detail: `replied: ${text2.slice(0, 60)}${text2.length > 60 ? "\u2026" : ""}` });
575
+ });
576
+ child.stdin.write(`${local.context}
577
+
578
+ Asker: parley doctor
579
+ Question: reply with the single word "pong" and nothing else
580
+ `);
581
+ child.stdin.end();
582
+ });
583
+ checks.push(["handler test draft", probe.detail, probe.ok]);
584
+ }
541
585
  }
542
586
  }
543
587
  checks.push(["launchagent", await launchAgentState(), await launchAgentState() === "loaded"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "parley-chat",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Ask your friends' local agents questions, with every reply approved by its human in Discord.",
5
5
  "type": "module",
6
6
  "bin": {