parley-chat 0.2.1 → 0.2.3
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/main.mjs +44 -9
- package/package.json +2 -2
package/main.mjs
CHANGED
|
@@ -83,6 +83,7 @@ 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.3";
|
|
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
|
|
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.3";
|
|
204
|
+
|
|
201
205
|
// src/http.ts
|
|
202
206
|
init_files();
|
|
203
207
|
async function api2(path, init = {}) {
|
|
@@ -217,18 +221,28 @@ async function draft(requestId, asker, question) {
|
|
|
217
221
|
Asker: ${asker}
|
|
218
222
|
Question: ${question}
|
|
219
223
|
`;
|
|
220
|
-
const child = spawn("sh", ["-c", local.command], { stdio: ["pipe", "pipe", "
|
|
224
|
+
const child = spawn("sh", ["-c", local.command], { stdio: ["pipe", "pipe", "pipe"] });
|
|
221
225
|
child.stdin.write(prompt);
|
|
222
226
|
child.stdin.end();
|
|
223
227
|
const chunks = [];
|
|
228
|
+
const errChunks = [];
|
|
224
229
|
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
230
|
+
child.stderr.on("data", (chunk) => errChunks.push(chunk));
|
|
225
231
|
const code = await new Promise((resolve) => child.on("close", (value) => resolve(value ?? 1)));
|
|
226
232
|
const output = Buffer.concat(chunks).toString("utf8").trim();
|
|
233
|
+
if (code !== 0 || output.length === 0) {
|
|
234
|
+
const errText = Buffer.concat(errChunks).toString("utf8").trim().split(`
|
|
235
|
+
`).slice(-5).join(`
|
|
236
|
+
`);
|
|
237
|
+
process.stderr.write(`draft failed (exit ${code})${errText ? `:
|
|
238
|
+
${errText}` : " with no stderr"}
|
|
239
|
+
`);
|
|
240
|
+
}
|
|
227
241
|
await api2(`/v1/asks/${requestId}/draft`, { method: "POST", body: JSON.stringify({ draft: code === 0 ? output : "" }) });
|
|
228
242
|
}
|
|
229
243
|
async function listen() {
|
|
230
244
|
const auth = await Promise.resolve().then(() => (init_files(), exports_files)).then((module) => module.credentials());
|
|
231
|
-
const socket = new WebSocket(wsUrl(
|
|
245
|
+
const socket = new WebSocket(wsUrl(`/v1/responders/connect?version=${encodeURIComponent(cliVersion2)}`), [`parley.${auth.token}`]);
|
|
232
246
|
await new Promise((resolve, reject) => {
|
|
233
247
|
socket.addEventListener("open", () => resolve(), { once: true });
|
|
234
248
|
socket.addEventListener("error", () => reject(new Error("responder connection failed")), { once: true });
|
|
@@ -342,8 +356,14 @@ async function contacts() {
|
|
|
342
356
|
const entries = Array.isArray(value?.contacts) ? value.contacts : [];
|
|
343
357
|
for (const entry of entries) {
|
|
344
358
|
const contact = object(entry);
|
|
345
|
-
if (contact)
|
|
346
|
-
|
|
359
|
+
if (!contact)
|
|
360
|
+
continue;
|
|
361
|
+
const username = text(contact.username);
|
|
362
|
+
const displayName = typeof contact.displayName === "string" && contact.displayName !== username ? ` (${contact.displayName})` : "";
|
|
363
|
+
const online = contact.online === true;
|
|
364
|
+
const version = typeof contact.version === "string" ? contact.version : null;
|
|
365
|
+
const staleness = online && version !== null && version !== cliVersion ? ` v${version} (outdated)` : online && version !== null ? ` v${version}` : "";
|
|
366
|
+
process.stdout.write(`${username}${displayName} \u2014 ${online ? "online" : "offline"}${staleness}
|
|
347
367
|
`);
|
|
348
368
|
}
|
|
349
369
|
return 0;
|
|
@@ -425,13 +445,23 @@ function plist(entry) {
|
|
|
425
445
|
async function install() {
|
|
426
446
|
if (process.platform !== "darwin") {
|
|
427
447
|
process.stderr.write(`not supported yet
|
|
448
|
+
`);
|
|
449
|
+
return 1;
|
|
450
|
+
}
|
|
451
|
+
const uid = process.getuid?.() ?? 501;
|
|
452
|
+
if (uid === 0) {
|
|
453
|
+
process.stderr.write(`do not run parley install with sudo \u2014 launchd user agents belong to your own account. Re-run as yourself:
|
|
454
|
+
parley install
|
|
428
455
|
`);
|
|
429
456
|
return 1;
|
|
430
457
|
}
|
|
431
458
|
const path = join3(homedir3(), "Library/LaunchAgents/sh.parley.listen.plist");
|
|
459
|
+
await execute("launchctl", ["bootout", `gui/${uid}`, path]).catch(() => {
|
|
460
|
+
return;
|
|
461
|
+
});
|
|
432
462
|
await mkdir3(join3(parleyHome2(), "logs"), { recursive: true, mode: 448 });
|
|
433
463
|
await writeFile3(path, plist(process.argv[1] ?? "parley"), { mode: 384 });
|
|
434
|
-
await execute("launchctl", ["bootstrap", `gui/${
|
|
464
|
+
await execute("launchctl", ["bootstrap", `gui/${uid}`, path]);
|
|
435
465
|
process.stdout.write(`installed
|
|
436
466
|
`);
|
|
437
467
|
return 0;
|
|
@@ -574,12 +604,17 @@ Question: reply with the single word "pong" and nothing else
|
|
|
574
604
|
}
|
|
575
605
|
}
|
|
576
606
|
}
|
|
577
|
-
|
|
607
|
+
const agentState = await launchAgentState();
|
|
608
|
+
checks.push(["launchagent", agentState, agentState === "loaded"]);
|
|
578
609
|
const online = hasCredentials ? object(await apiJson("/v1/status").catch(() => null))?.online === true : false;
|
|
579
610
|
checks.push(["responder online", online ? "yes" : "no \u2014 run: parley install (or parley listen)", online]);
|
|
580
611
|
const logPath = join3(parleyHome2(), "logs/listen.log");
|
|
581
|
-
const
|
|
582
|
-
`).filter((line) => line.length > 0)
|
|
612
|
+
const logLines = await readFile3(logPath, "utf8").then((value) => value.split(`
|
|
613
|
+
`).filter((line) => line.length > 0)).catch(() => []);
|
|
614
|
+
const crashing = agentState === "loaded" && !online && logLines.slice(-20).some((line) => /error|throw|at .*\(node:/i.test(line));
|
|
615
|
+
if (crashing)
|
|
616
|
+
checks.push(["responder process", "crashing on startup \u2014 run `parley listen` to see the error directly, or `parley install` to regenerate the launch agent", false]);
|
|
617
|
+
const recentLog = logLines.slice(crashing ? -20 : -3);
|
|
583
618
|
let failures = 0;
|
|
584
619
|
for (const [name, detail, ok] of checks) {
|
|
585
620
|
if (!ok)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "parley-chat",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
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": {
|
|
@@ -16,6 +16,6 @@
|
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "https://github.com/
|
|
19
|
+
"url": "https://github.com/UsefulSoftwareCo/parley"
|
|
20
20
|
}
|
|
21
21
|
}
|