tools(relay): durable JSONL message log + pm wrapper + 120-char preview
- queue.ts: append every posted message to relay-log.jsonl (full body, survives the consume-once drain + restarts). gitignored. - server.ts: bump the stdout preview from 60 to 120 chars. - tools/relay/pm: absolute-path bash wrapper (read|pending|send) so relay ops work from any cwd without cd or hand-built JSON escaping. - Fold in Dev-C's Phase 6 ARCHITECTURE.md slice as a coordination artifact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
3
tools/relay/.gitignore
vendored
Normal file
3
tools/relay/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Runtime message archive written by queue.ts post() — local relay traffic,
|
||||
# not source. Regenerated each session; never committed.
|
||||
relay-log.jsonl
|
||||
49
tools/relay/pm
Executable file
49
tools/relay/pm
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
# PM relay helper — absolute-path wrapper around call.py so it can be invoked
|
||||
# from ANY working directory with no `cd` and no JSON-quoting by hand.
|
||||
#
|
||||
# Usage:
|
||||
# tools/relay/pm read # drain PM inbox
|
||||
# tools/relay/pm pending # pending counts for all roles
|
||||
# tools/relay/pm send <to> <kind> <body> # post_message from pm
|
||||
# e.g. tools/relay/pm send dev-c directive "## DIRECTIVE ... "
|
||||
#
|
||||
# Always works regardless of cwd because it resolves call.py by absolute path.
|
||||
set -euo pipefail
|
||||
|
||||
RELAY_DIR="/home/alee/Sources/relicario/tools/relay"
|
||||
CALL="python3 $RELAY_DIR/call.py"
|
||||
|
||||
cmd="${1:-}"
|
||||
case "$cmd" in
|
||||
read)
|
||||
$CALL read_messages '{"for":"pm"}'
|
||||
;;
|
||||
pending)
|
||||
for r in dev-a dev-b dev-c pm; do
|
||||
printf '%s: ' "$r"
|
||||
$CALL list_pending "{\"for\":\"$r\"}"
|
||||
echo
|
||||
done
|
||||
;;
|
||||
send)
|
||||
to="${2:?usage: pm send <to> <kind> <body>}"
|
||||
kind="${3:?usage: pm send <to> <kind> <body>}"
|
||||
body="${4:?usage: pm send <to> <kind> <body>}"
|
||||
# Build JSON with python to handle escaping of the body safely.
|
||||
python3 - "$to" "$kind" "$body" <<'PY'
|
||||
import json, sys, urllib.request
|
||||
to, kind, body = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
payload = {"from": "pm", "to": to, "kind": kind, "body": body}
|
||||
import subprocess
|
||||
print(subprocess.run(
|
||||
["python3", "/home/alee/Sources/relicario/tools/relay/call.py",
|
||||
"post_message", json.dumps(payload)],
|
||||
capture_output=True, text=True).stdout, end="")
|
||||
PY
|
||||
;;
|
||||
*)
|
||||
echo "usage: pm {read|pending|send <to> <kind> <body>}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -1,4 +1,13 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { appendFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
// Append-only archive of every posted message. The in-memory queues are
|
||||
// consume-once (read() drains the inbox) and vanish on restart, so this is
|
||||
// the only durable, full-body record of relay traffic. One JSON object per
|
||||
// line; never truncated.
|
||||
const LOG_PATH = join(dirname(fileURLToPath(import.meta.url)), "relay-log.jsonl");
|
||||
|
||||
export type Role = "pm" | "dev-a" | "dev-b" | "dev-c" | "dev-d" | "dev-e" | "dev-f";
|
||||
export type MessageKind = "status" | "question" | "directive" | "free";
|
||||
@@ -39,6 +48,11 @@ export class RelayQueue {
|
||||
ts: new Date().toISOString(),
|
||||
};
|
||||
this.queues.get(to)!.push(msg);
|
||||
try {
|
||||
appendFileSync(LOG_PATH, JSON.stringify(msg) + "\n");
|
||||
} catch {
|
||||
// Logging is best-effort; never let a disk error drop a message.
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,8 +86,8 @@ function handleToolCall(name: string, args: Record<string, string>) {
|
||||
const kind = args.kind as "status" | "question" | "directive" | "free";
|
||||
const msg = queue.post(args.from, args.to, kind, args.body);
|
||||
const ts = new Date(msg.ts).toTimeString().slice(0, 8);
|
||||
const preview = args.body.slice(0, 60).replace(/\n/g, " ");
|
||||
const ellipsis = args.body.length > 60 ? "..." : "";
|
||||
const preview = args.body.slice(0, 120).replace(/\n/g, " ");
|
||||
const ellipsis = args.body.length > 120 ? "..." : "";
|
||||
process.stdout.write(`[${ts}] ${args.from} → ${args.to} [${kind}] "${preview}${ellipsis}"\n`);
|
||||
return { content: [{ type: "text" as const, text: JSON.stringify({ id: msg.id }) }] };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user