seisin 0.6.3 → 0.6.5

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 (50) hide show
  1. package/README.md +143 -686
  2. package/package.json +2 -2
  3. package/src/canary.js +1 -1
  4. package/src/cli.js +47 -3
  5. package/src/commands/check.js +68 -12
  6. package/src/commands/hook.js +1 -1
  7. package/src/commands/log.js +21 -14
  8. package/src/commands/requests.js +10 -15
  9. package/src/commands/review.js +2 -1
  10. package/src/commands/run.js +331 -245
  11. package/src/commands/walls.js +12 -5
  12. package/src/config-fields.js +305 -0
  13. package/src/config.js +114 -534
  14. package/src/controls.js +1 -1
  15. package/src/decide-request.js +37 -0
  16. package/src/denial-attribution.js +338 -0
  17. package/src/grants.js +1 -1
  18. package/src/help.js +6 -4
  19. package/src/index.js +1 -1
  20. package/src/inspect/enforce.js +1 -1
  21. package/src/inspect/files.js +1 -1
  22. package/src/inspect/home.js +1 -1
  23. package/src/inspect/inject.js +1 -1
  24. package/src/intake.js +3 -2
  25. package/src/keys.js +2 -2
  26. package/src/kinds.js +19 -0
  27. package/src/layout.js +1 -1
  28. package/src/log.js +3 -2
  29. package/src/mcp.js +1 -1
  30. package/src/notify-config.js +58 -0
  31. package/src/notify.js +4 -52
  32. package/src/owners.js +2 -2
  33. package/src/requests.js +9 -4
  34. package/src/rundir.js +2 -2
  35. package/src/runs.js +29 -10
  36. package/src/sandboxes/command.js +0 -2
  37. package/src/sandboxes/index.js +15 -2
  38. package/src/sandboxes/srt.js +1 -1
  39. package/src/sandboxes/tartaros.js +34 -6
  40. package/src/serve.js +11 -22
  41. package/src/spool.js +1 -1
  42. package/src/{srt.js → srt-settings.js} +5 -2
  43. package/src/state.js +25 -0
  44. package/src/suggest.js +26 -1
  45. package/src/surface.js +3 -2
  46. package/src/territory.js +20 -1
  47. package/src/toml.js +250 -0
  48. package/src/violations.js +16 -315
  49. package/src/walls.js +4 -2
  50. package/ui/index.html +35 -9
package/ui/index.html CHANGED
@@ -2088,9 +2088,9 @@ table.runs tr.sys td{color:var(--dim)}
2088
2088
  * that was gone or had a new token. After two failed polls in a row the page
2089
2089
  * says which of the two it is, since when, and turns the writes off. */
2090
2090
  var FAILS = 0, OFFLINE = null;
2091
- function setOffline(kind){
2092
- if (kind && OFFLINE && OFFLINE.kind === kind) return;
2093
- OFFLINE = kind ? { kind: kind, since: (OFFLINE && OFFLINE.since) || new Date() } : null;
2091
+ function setOffline(kind, why){
2092
+ if (kind && OFFLINE && OFFLINE.kind === kind && OFFLINE.why === why) return;
2093
+ OFFLINE = kind ? { kind: kind, why: why || null, since: (OFFLINE && OFFLINE.since) || new Date() } : null;
2094
2094
  var label = document.getElementById("source");
2095
2095
  var ban = document.getElementById("off-banner");
2096
2096
  if (!OFFLINE) {
@@ -2099,7 +2099,11 @@ table.runs tr.sys td{color:var(--dim)}
2099
2099
  } else {
2100
2100
  var t = pad(OFFLINE.since.getHours()) + ":" + pad(OFFLINE.since.getMinutes());
2101
2101
  label.style.color = "var(--amber)";
2102
- if (kind === "expired") {
2102
+ if (kind === "policy") {
2103
+ label.textContent = "seisin.toml cannot be read";
2104
+ ban.innerHTML = "<b>seisin.toml cannot be read</b> (since " + t + "): " + esc(OFFLINE.why) + " " +
2105
+ "What is on screen is the last policy that loaded. Decisions and saves are off until the file is fixed.";
2106
+ } else if (kind === "expired") {
2103
2107
  label.textContent = "link expired — open the new one";
2104
2108
  ban.innerHTML = "<b>This tab's link has expired</b> (since " + t + "): the console was restarted with a new one. " +
2105
2109
  "Open the new link — run <code>seisin ui --link</code> in the repository. Decisions and saves are off in this tab.";
@@ -2184,7 +2188,13 @@ table.runs tr.sys td{color:var(--dim)}
2184
2188
  if (!fresh && LAST.etag && LAST.url === url) h["if-none-match"] = LAST.etag;
2185
2189
  var r = await fetch(url, { cache: "no-store", headers: h });
2186
2190
  if (r.status === 304) return { same: true };
2187
- if (!r.ok) return { status: r.status };
2191
+ // A refusal carries the server's reason (a seisin.toml that no longer
2192
+ // parses): kept, so it is shown instead of reading as the server gone.
2193
+ if (!r.ok) {
2194
+ var why = null;
2195
+ try { why = (await r.json()).error || null; } catch (_) { /* no body */ }
2196
+ return { status: r.status, error: why };
2197
+ }
2188
2198
  var s = await r.json();
2189
2199
  if (s.error) return { status: r.status, error: s.error };
2190
2200
  LAST = { url: url, etag: r.headers.get("etag") };
@@ -2311,6 +2321,16 @@ table.runs tr.sys td{color:var(--dim)}
2311
2321
  'passes the example policy off as yours.</p></div>';
2312
2322
  return;
2313
2323
  }
2324
+ if (!s && got && got.error) {
2325
+ label.textContent = "seisin.toml cannot be read";
2326
+ label.style.color = "var(--amber)";
2327
+ document.querySelector("main").innerHTML =
2328
+ '<div class="box" style="padding:22px 24px;max-width:640px">' +
2329
+ '<h2 style="margin:0 0 8px;font-size:17px">The console cannot read this repository\'s policy</h2>' +
2330
+ '<p style="margin:0 0 8px" class="err">' + esc(got.error) + '</p>' +
2331
+ '<p style="margin:0;color:var(--mid)">Fix the file and reload. <code>seisin check</code> says the same in a terminal.</p></div>';
2332
+ return;
2333
+ }
2314
2334
  if (!s) {
2315
2335
  label.textContent = location.protocol.startsWith("http") && !TOKEN
2316
2336
  ? "no token — showing an example"
@@ -2403,15 +2423,18 @@ table.runs tr.sys td{color:var(--dim)}
2403
2423
  if (w.text) bits.push('started <span title="' + escA(w.title) + '">' + esc(w.text) + "</span>");
2404
2424
  return bits.join(" · ");
2405
2425
  }
2406
- function outcomeTag(o){
2426
+ function outcomeTag(o, dropped){
2407
2427
  var says = { clean: "clean", denied: "denied", refused: "canary failed", unread: "policy unreadable" };
2408
- return '<span class="oc ' + escA(o) + '">' + esc(says[o] || o) + "</span>";
2428
+ // A run whose system log dropped kernel messages: its walls may be missing some.
2429
+ var lost = dropped ? ' <span class="oc refused" title="the system log dropped ' + escA(String(dropped)) +
2430
+ ' batch(es) of kernel messages during this run: denials may be missing from its record">record incomplete</span>' : "";
2431
+ return '<span class="oc ' + escA(o) + '">' + esc(says[o] || o) + "</span>" + lost;
2409
2432
  }
2410
2433
 
2411
2434
  function drawRunBanner(){
2412
2435
  var b = document.getElementById("run-banner");
2413
2436
  var r = RUNINFO || { id: RUN };
2414
- b.innerHTML = "<b>Run " + esc(r.run_id || r.id) + "</b> " + outcomeTag(r.outcome || "") +
2437
+ b.innerHTML = "<b>Run " + esc(r.run_id || r.id) + "</b> " + outcomeTag(r.outcome || "", r.dropped) +
2415
2438
  ' · <a href="./">all runs</a>' +
2416
2439
  '<div class="meta">' + (runMeta(r) || "no run.json: only what its policy and log say") +
2417
2440
  " · read only: nothing is granted from here</div>";
@@ -2442,6 +2465,7 @@ table.runs tr.sys td{color:var(--dim)}
2442
2465
  document.getElementById("rn-kpis").innerHTML =
2443
2466
  card("b", "runs", t.runs, (t.outcome.denied || 0) + " denied something · " + (t.outcome.clean || 0) + " clean") +
2444
2467
  card(t.outcome.refused ? "a" : "g", "canary failed", t.outcome.refused || 0, "runs that never started") +
2468
+ (t.incomplete ? card("a", "record incomplete", t.incomplete, "runs whose system log dropped kernel messages") : "") +
2445
2469
  card(t.denied ? "a" : "g", "denied", t.denied, (k.file || 0).toLocaleString() + " files · " + (k.network || 0).toLocaleString() + " network") +
2446
2470
  card(t.retried ? "a" : "g", "spent retrying", t.retried, "calls after the first denial of the same thing");
2447
2471
 
@@ -2463,7 +2487,7 @@ table.runs tr.sys td{color:var(--dim)}
2463
2487
  var w = when(r.started_at || "");
2464
2488
  return "<tr><td><a href=\"./?run=" + encodeURIComponent(r.id) + "#requests\"><code>" + esc(r.run_id || r.id) + "</code></a></td>" +
2465
2489
  '<td title="' + escA(w.title || "") + '">' + esc(w.text || "") + "</td><td>" + esc(r.team || "") + "</td><td>" + esc(r.role || "") +
2466
- "</td><td>" + esc(r.work_item || "") + "</td><td>" + esc(r.status || "") + "</td><td>" + outcomeTag(r.outcome) +
2490
+ "</td><td>" + esc(r.work_item || "") + "</td><td>" + esc(r.status || "") + "</td><td>" + outcomeTag(r.outcome, r.dropped) +
2467
2491
  "</td><td class=num>" + r.denied + "</td><td class=num>" + r.retried + "</td><td>" +
2468
2492
  (r.hosts || []).slice(0, 3).map(function(h){ return "<code>" + esc(h.target) + "</code>"; }).join(" ") + "</td></tr>";
2469
2493
  }).join("") + "</tbody>"
@@ -2628,6 +2652,8 @@ table.runs tr.sys td{color:var(--dim)}
2628
2652
  if (got.s) applyState(got.s);
2629
2653
  return;
2630
2654
  }
2655
+ // The server answered, with the reason it cannot: said at once, not as an outage.
2656
+ if (got.error && got.status !== 401 && got.status !== 403) { FAILS = 0; setOffline("policy", got.error); return; }
2631
2657
  FAILS++;
2632
2658
  if (FAILS >= 2) setOffline(got.status === 401 || got.status === 403 ? "expired" : "down");
2633
2659
  }