cortad 0.3.5 → 0.3.6

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/lib/replay.mjs CHANGED
@@ -461,7 +461,12 @@ export function makeAccounts(file = null) {
461
461
  let bare = null;
462
462
  if (file) {
463
463
  try {
464
- for (const a of JSON.parse(readFileSync(file, "utf8"))) {
464
+ const stored = JSON.parse(readFileSync(file, "utf8"));
465
+ // A visitor who is not signed in still sends what the app lets them in by (its Origin): ulaim
466
+ // refused every test request of a reconnect with 403, having been sent none of it.
467
+ const visitor = Array.isArray(stored) ? null : stored?.visitor;
468
+ if (visitor && typeof visitor === "object" && !signInKey(visitor)) bare = Object.fromEntries(Object.entries(visitor).filter(([k, v]) => typeof k === "string" && typeof v === "string"));
469
+ for (const a of Array.isArray(stored) ? stored : stored?.accounts ?? []) {
465
470
  if (kept.length >= ACCOUNTS_MAX || typeof a?.key !== "string" || !a.headers || typeof a.headers !== "object") continue;
466
471
  const headers = Object.fromEntries(Object.entries(a.headers).filter(([k, v]) => typeof k === "string" && typeof v === "string"));
467
472
  if (signInKey(headers) !== a.key || (identityOf(headers).expiresInS ?? 1) <= 0) continue;
@@ -473,7 +478,7 @@ export function makeAccounts(file = null) {
473
478
  if (!file) return;
474
479
  try {
475
480
  mkdirSync(dirname(file), { recursive: true, mode: 0o700 });
476
- writeFileSync(file, JSON.stringify(kept.map((a) => ({ key: a.key, headers: a.headers, bodies: Object.fromEntries([...a.bodies].map(([door, leaves]) => [door, Object.fromEntries(leaves)])) }))), { mode: 0o600 });
481
+ writeFileSync(file, JSON.stringify({ visitor: bare, accounts: kept.map((a) => ({ key: a.key, headers: a.headers, bodies: Object.fromEntries([...a.bodies].map(([door, leaves]) => [door, Object.fromEntries(leaves)])) })) }), { mode: 0o600 });
477
482
  } catch { /* kept for this connect only */ }
478
483
  };
479
484
  const at = (role) => { const m = ACCOUNT_ROLE.exec(role); return m ? kept[Number(m[1] ?? 1) - 1] ?? kept[0] ?? null : null; };
@@ -481,7 +486,7 @@ export function makeAccounts(file = null) {
481
486
  // `door` and `body`: the request as that customer sent it, for the values in it that are theirs.
482
487
  keep(headers, door, body) {
483
488
  const key = signInKey(headers);
484
- if (!key) { bare = headers; return; }
489
+ if (!key) { bare = headers; save(); return; }
485
490
  let account = kept.find((a) => a.key === key);
486
491
  if (account) account.headers = headers;
487
492
  else {
package/local.mjs CHANGED
@@ -410,7 +410,12 @@ async function verb(job) {
410
410
  if (role === CAPTURED || role.startsWith(`${CAPTURED}:`)) {
411
411
  spokenAs = role;
412
412
  // Speaking as one of the customers whose requests we watched: every header their own client sent.
413
- for (const [name, value] of Object.entries(capture?.headers(role) ?? {})) { for (const k of Object.keys(headers)) if (k.toLowerCase() === name) delete headers[k]; headers[name] = value; }
413
+ const theirs = capture?.headers(role);
414
+ for (const [name, value] of Object.entries(theirs ?? {})) { for (const k of Object.keys(headers)) if (k.toLowerCase() === name) delete headers[k]; headers[name] = value; }
415
+ // No request of theirs is held (a new connect): sent from the app's own address, as Cortad's
416
+ // own test request is, so an app that lets visitors in by Origin lets this one in.
417
+ const from = !theirs && app ? ownOrigin(app.port) : null;
418
+ if (from && !Object.keys(headers).some((k) => k.toLowerCase() === "origin")) { headers.origin = from; headers.referer = `${from}/`; }
414
419
  } else {
415
420
  const held = await identities?.headerFor(role);
416
421
  if (held) { for (const k of Object.keys(headers)) if (k.toLowerCase() === held.name) delete headers[k]; headers[held.name] = held.value; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortad",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Connects the AI app on your machine to Cortad for test conversations, and gives your coding agent the MCP and skill to run them. No dependencies.",
5
5
  "bin": {
6
6
  "cortad": "local.mjs"