packages/conversation is a library with no server: safe-fs, the Pi session parser, CHAT-01 pages, pinned snapshots, cursors and follow. The control board adds GET /api/conversations and /api/conversation behind the Host and Origin guard. Both are read-only, their queries are validated, and each refusal code maps to a status. Dewey authored it (packet 0cf177b1, revision 2). Filbert reviewed the code: R1 revise (branch ids moving on append, the assumed-link bridge merging branches, one unreadable seat directory turning the catalogue into a 500), then R2 approve (3b14d66c). Darkwing reviewed the routes: R1 approve (07b10ad1), R2 approve (b9d92003). The package lands with the routes, because serve.mjs imports the reader at load. On an index export: the eight suites 24/90/43/17/14/15/63/18, conversation and control-board 153/153, webui 9/9. Co-Authored-By: Claude Opus 5.5 <[email protected]>
31 lines
2.2 KiB
Python
31 lines
2.2 KiB
Python
import subprocess, os
|
|
S="/tmp/dewey-chat02/scratch-board"
|
|
f=os.path.join(S,"packages/control-board/src/serve.mjs")
|
|
orig=open(f).read()
|
|
M=[
|
|
("conversation routes before the guard",[(""" const refused = foreignRequest(req);""",""" { const u0 = new URL(req.url, "http://localhost"); if (u0.pathname.startsWith("/api/conversation")) { let o; try { o = conversationResponse(reader, u0); } catch { o = { status: 500, body: {} }; } return sendConversationJson(res, o.status, o.body); } }
|
|
const refused = foreignRequest(req);""")]),
|
|
("no nosniff",[(', "x-content-type-options": "nosniff"','')]),
|
|
("all refusals 422",[("status: REFUSAL_STATUS[out.refusal.code] ?? 422","status: 422")]),
|
|
("no query validation",[(" if (values.length !== 1 || !QUERY_VALUE.test(values[0])) return { error: `invalid ${key}` };\n","")]),
|
|
("unknown params allowed",[(""" if (!["id", "branch", "cursor"].includes(key)) return { error: `unknown parameter: ${key}` };\n""","")]),
|
|
("reconcile dropped",[("refusal: { code: out.refusal.code, reconcile: out.refusal.reconcile }","refusal: { code: out.refusal.code }")]),
|
|
("cursor ignored",[(" const out = query.cursor\n"," const out = false\n")]),
|
|
("registrations ignored",[("rootsFromSpecs(specs, loadRegistrations(seatsDir).registrations)","rootsFromSpecs(specs, [])")]),
|
|
("cursor without branch served",[(" if (out.cursor && !out.branch) return"," if (false) return")]),
|
|
("unavailable falls to 422",[(" unavailable: 404,\n","")]),
|
|
("CORS header sent",[('"x-content-type-options": "nosniff" });','"x-content-type-options": "nosniff", "access-control-allow-origin": "*" });')]),
|
|
]
|
|
for name,reps in M:
|
|
src=orig
|
|
ok=True
|
|
for a,b in reps:
|
|
if a not in src: print("NOT APPLIED",name); ok=False; break
|
|
src=src.replace(a,b,1)
|
|
if not ok: continue
|
|
open(f,"w").write(src)
|
|
r=subprocess.run(["node","--test","--test-concurrency=1","tests/serve.test.mjs"],cwd=os.path.join(S,"packages/control-board"),capture_output=True,text=True,timeout=600)
|
|
fails=sorted(set(l.strip()[2:].split(" (")[0][:60] for l in r.stdout.splitlines() if l.lstrip().startswith("✖") and "failing tests" not in l))
|
|
print(("CAUGHT " if r.returncode else "MISSED ")+name+" -> "+"; ".join(fails))
|
|
open(f,"w").write(orig)
|