@ai-ecoverse/slicc-spectrum 1.60.1 → 1.61.0

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/dist/slicc-ui.js CHANGED
@@ -6304,10 +6304,10 @@ function getNearestOverflowAncestor(node) {
6304
6304
  }
6305
6305
  return getNearestOverflowAncestor(parentNode);
6306
6306
  }
6307
- function getOverflowAncestors(node, list4, traverseIframes) {
6307
+ function getOverflowAncestors(node, list5, traverseIframes) {
6308
6308
  var _node$ownerDocument2;
6309
- if (list4 === void 0) {
6310
- list4 = [];
6309
+ if (list5 === void 0) {
6310
+ list5 = [];
6311
6311
  }
6312
6312
  if (traverseIframes === void 0) {
6313
6313
  traverseIframes = true;
@@ -6317,9 +6317,9 @@ function getOverflowAncestors(node, list4, traverseIframes) {
6317
6317
  const win = getWindow(scrollableAncestor);
6318
6318
  if (isBody) {
6319
6319
  const frameElement = getFrameElement(win);
6320
- return list4.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], frameElement && traverseIframes ? getOverflowAncestors(frameElement) : []);
6320
+ return list5.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], frameElement && traverseIframes ? getOverflowAncestors(frameElement) : []);
6321
6321
  } else {
6322
- return list4.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes));
6322
+ return list5.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes));
6323
6323
  }
6324
6324
  }
6325
6325
  function getFrameElement(win) {
@@ -12300,7 +12300,7 @@ function remoteLight(status2) {
12300
12300
  "linked devices are watching the agent."
12301
12301
  )
12302
12302
  ].join(" ");
12303
- const by = (list4) => list4.length === 1 ? list4[0].name : `${list4.length} linked devices`;
12303
+ const by = (list5) => list5.length === 1 ? list5[0].name : `${list5.length} linked devices`;
12304
12304
  if (asking2.length) {
12305
12305
  const text2 = asking2.length === 1 ? `${asking2[0].name} asks to follow` : `${asking2.length} devices ask to follow`;
12306
12306
  return { mode: "ask", variant: "notice", text: text2, tooltip };
@@ -12908,8 +12908,8 @@ var SliccNetwork = class extends ModelElement {
12908
12908
  }
12909
12909
  const device = status2.links?.devices.find((item2) => item2.id === exit.id);
12910
12910
  if (device?.state === "connected") return A;
12911
- const who = device ? `${device.name} is ${device.state}` : "The linked device isn\u2019t connected";
12912
- return b`<div class="warning" data-exit="device" data-held><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><div class="banner"><span>${who}. Internet traffic waits until it’s connected.</span><div class="actions"><swc-button size="s" variant="secondary" fill-style="outline" data-action="exit-direct" ?pending=${this.busy === "exit"} ?disabled=${this.#waiting("exit")} @click=${() => this.#act("exit", () => port.setExit?.(null), "error")}>Send directly</swc-button></div></div></div>`;
12911
+ const who2 = device ? `${device.name} is ${device.state}` : "The linked device isn\u2019t connected";
12912
+ return b`<div class="warning" data-exit="device" data-held><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><div class="banner"><span>${who2}. Internet traffic waits until it’s connected.</span><div class="actions"><swc-button size="s" variant="secondary" fill-style="outline" data-action="exit-direct" ?pending=${this.busy === "exit"} ?disabled=${this.#waiting("exit")} @click=${() => this.#act("exit", () => port.setExit?.(null), "error")}>Send directly</swc-button></div></div></div>`;
12913
12913
  }
12914
12914
  #exitPicker(port, status2) {
12915
12915
  const tailnet = status2.tailnet;
@@ -13694,8 +13694,8 @@ var Panels = class {
13694
13694
  if (bad) return bad;
13695
13695
  this.quietly(() => this.#host.open(id, focus, section));
13696
13696
  if (place3) {
13697
- const moved = this.move(id, place3);
13698
- if (moved.code !== 0) return moved;
13697
+ const moved2 = this.move(id, place3);
13698
+ if (moved2.code !== 0) return moved2;
13699
13699
  }
13700
13700
  return ok(`${focus ? "focused" : info.open ? "showed" : "opened"} ${id}`);
13701
13701
  }
@@ -13752,9 +13752,12 @@ var help = `usage: panel <command>
13752
13752
  close <id> close a panel
13753
13753
  move <id> <place> move an open panel
13754
13754
  reset restore the default layout
13755
+ tabs the tabs panel can reach (answered by the agent)
13755
13756
 
13756
13757
  place: left | center | right, or --left | --right | --above | --below | --within <id>
13757
13758
  open and focus also take --section <name>, e.g. panel open settings --section accounts
13759
+ --tab <tab> sends the command to that tab instead of the one showing your cone;
13760
+ see panel tabs
13758
13761
 
13759
13762
  ids: the ones panel list shows, e.g. files, changes, terminal, monitor, settings,
13760
13763
  chat (your cone's chat), chat:<agent>, terminal:<n>, sprinkle:<name>,
@@ -13813,37 +13816,37 @@ function coneOf(agents, id) {
13813
13816
  }
13814
13817
  return agent?.kind === "cone" ? agent.id : null;
13815
13818
  }
13816
- function resolve(panels, raw2, reachable, cone) {
13819
+ function resolve(panels, raw2, reachable2, cone) {
13817
13820
  if (/^(file|diff):./.test(raw2)) return raw2;
13818
13821
  if (raw2 === "chat") {
13819
13822
  const agent = cone === void 0 ? panels.active() : cone;
13820
- return reachable.some((info) => info.id === `chat:${agent}`) ? `chat:${agent}` : null;
13823
+ return reachable2.some((info) => info.id === `chat:${agent}`) ? `chat:${agent}` : null;
13821
13824
  }
13822
13825
  if (raw2 === "terminal") {
13823
- const terminals = reachable.filter((info) => info.kind === "terminal");
13826
+ const terminals = reachable2.filter((info) => info.kind === "terminal");
13824
13827
  const picked = terminals.find((info) => info.active) ?? terminals.filter((info) => info.open).at(-1) ?? terminals.at(-1);
13825
13828
  return picked?.id ?? null;
13826
13829
  }
13827
- if (reachable.some((info) => info.id === raw2)) return raw2;
13830
+ if (reachable2.some((info) => info.id === raw2)) return raw2;
13828
13831
  const name = raw2.startsWith("sprinkle:") ? raw2.slice(9) : raw2;
13829
- const sprinkles = reachable.filter((info) => info.kind === "sprinkle");
13832
+ const sprinkles = reachable2.filter((info) => info.kind === "sprinkle");
13830
13833
  const found = sprinkles.find((info) => info.id === `sprinkle:${name}`) ?? sprinkles.find((info) => cone && info.id === `sprinkle:${cone}/${name}`) ?? sprinkles.find((info) => info.id.split("/").at(-1) === name);
13831
13834
  return found?.id ?? null;
13832
13835
  }
13833
13836
  function state(info) {
13834
- const shown2 = info.active ? "active" : info.visible ? "shown" : info.open ? "open" : "closed";
13835
- return info.busy ? `${shown2} \xB7 busy: ${info.busy}` : shown2;
13837
+ const shown3 = info.active ? "active" : info.visible ? "shown" : info.open ? "open" : "closed";
13838
+ return info.busy ? `${shown3} \xB7 busy: ${info.busy}` : shown3;
13836
13839
  }
13837
13840
  function table(rows) {
13838
13841
  const lines = [["ID", "TITLE", "STATE"], ...rows.map((row) => [row.id, row.title, state(row)])];
13839
13842
  const widths = [0, 1].map((column) => Math.max(...lines.map((line) => line[column].length)));
13840
- return lines.map(([id, title, shown2]) => `${id.padEnd(widths[0])} ${title.padEnd(widths[1])} ${shown2}`).join("\n");
13843
+ return lines.map(([id, title, shown3]) => `${id.padEnd(widths[0])} ${title.padEnd(widths[1])} ${shown3}`).join("\n");
13841
13844
  }
13842
13845
  var done = (result, note = "") => result.code === 0 ? { stdout: `${result.message}${note}
13843
13846
  `, stderr: "", code: 0 } : { stdout: "", stderr: `panel: ${result.message}
13844
13847
  `, code: result.code };
13845
13848
  function act(scope, parsed) {
13846
- const { panels, reachable, cone } = scope;
13849
+ const { panels, reachable: reachable2, cone } = scope;
13847
13850
  if (parsed.verb === "reset") {
13848
13851
  const recent = scope.agent ? panels.recentClose() : null;
13849
13852
  if (recent)
@@ -13853,11 +13856,11 @@ function act(scope, parsed) {
13853
13856
  return done(panels.reset());
13854
13857
  }
13855
13858
  const raw2 = parsed.id;
13856
- const id = resolve(panels, raw2, reachable, cone);
13859
+ const id = resolve(panels, raw2, reachable2, cone);
13857
13860
  if (!id) return done(unknown(raw2));
13858
13861
  let place3 = parsed.place;
13859
13862
  if (typeof place3 === "object") {
13860
- const ref = resolve(panels, place3.ref, reachable, cone);
13863
+ const ref = resolve(panels, place3.ref, reachable2, cone);
13861
13864
  if (!ref) return done(unknown(place3.ref));
13862
13865
  place3 = { ...place3, ref };
13863
13866
  }
@@ -13892,12 +13895,12 @@ ${help}`, code: 2 };
13892
13895
  const agents = panels.agents();
13893
13896
  const scoped2 = caller.agentId !== null || !!caller.cone;
13894
13897
  const cone = scoped2 ? caller.cone ?? coneOf(agents, caller.agentId) : void 0;
13895
- const reachable = panels.list().filter(
13898
+ const reachable2 = panels.list().filter(
13896
13899
  (info) => !scoped2 || info.agent === null || !!cone && coneOf(agents, info.agent) === cone
13897
13900
  );
13898
13901
  if (parsed.verb !== "list")
13899
- return act({ panels, agent: caller.agentId !== null, cone, reachable }, parsed);
13900
- const body4 = parsed.json ? JSON.stringify(reachable, null, 2) : table(reachable);
13902
+ return act({ panels, agent: caller.agentId !== null, cone, reachable: reachable2 }, parsed);
13903
+ const body4 = parsed.json ? JSON.stringify(reachable2, null, 2) : table(reachable2);
13901
13904
  return { stdout: `${body4}
13902
13905
  `, stderr: "", code: 0 };
13903
13906
  }
@@ -20678,12 +20681,12 @@ function user(message) {
20678
20681
  const tag3 = delivered === "steer" || delivered === "follow-up" ? deliveries[delivered] : null;
20679
20682
  const agent = message.origin === "agent";
20680
20683
  const name = message.from ?? "an agent";
20681
- const who = agent ? name : message.from ? `From ${message.from}` : "You";
20684
+ const who2 = agent ? name : message.from ? `From ${message.from}` : "You";
20682
20685
  const label = agent ? `Request from ${name}` : message.from ? `Message from ${message.from}` : "Your message";
20683
20686
  return b`<swc-conversation-turn type="user" class="message user" accessible-label=${label} data-id=${message.id} data-mode=${message.mode ?? "send"} data-delivered=${delivered ?? "run"} data-origin=${message.origin ?? "user"}>
20684
20687
  <div class="bubble">
20685
20688
  <div class="meta">
20686
- <span class="who">${who}</span><span>${time(message.createdAt)}</span>
20689
+ <span class="who">${who2}</span><span>${time(message.createdAt)}</span>
20687
20690
  ${agent ? b`<swc-badge class="origin" size="s" variant="indigo">Agent</swc-badge>` : A}
20688
20691
  ${tag3 ? b`<swc-badge class="tag" size="s" variant=${delivered === "steer" ? "notice" : "informative"} subtle>${tag3}</swc-badge>` : A}
20689
20692
  </div>
@@ -21942,8 +21945,8 @@ var SliccUpdates = class extends ModelElement {
21942
21945
  #checked(at) {
21943
21946
  if (at === null) return b`<span>Not checked yet</span>`;
21944
21947
  const date = new Date(at);
21945
- const shown2 = date.toLocaleString("en-GB", { dateStyle: "medium", timeStyle: "short" });
21946
- return b`<span>Checked <time datetime=${date.toISOString()}>${shown2}</time></span>`;
21948
+ const shown3 = date.toLocaleString("en-GB", { dateStyle: "medium", timeStyle: "short" });
21949
+ return b`<span>Checked <time datetime=${date.toISOString()}>${shown3}</time></span>`;
21947
21950
  }
21948
21951
  #item(item2) {
21949
21952
  const [variant, state3] = states[item2.state];
@@ -22008,14 +22011,14 @@ var SliccUpdates = class extends ModelElement {
22008
22011
  #package(item2, all2, act2) {
22009
22012
  const light = packageLight(item2);
22010
22013
  const busy = this.busy.get(item2.id);
22011
- const shown2 = busy ? [busy] : act2 ? item2.actions : [];
22014
+ const shown3 = busy ? [busy] : act2 ? item2.actions : [];
22012
22015
  const error = this.problems.get(item2.id) ?? item2.error;
22013
22016
  return b`<li data-id=${item2.id} data-state=${item2.state}>
22014
22017
  <div class="name">
22015
22018
  <h3 title=${item2.package}>${item2.label}</h3>
22016
22019
  ${light ? b`<swc-status-light size="s" variant=${light[0]}>${light[1]}</swc-status-light>` : A}
22017
22020
  </div>
22018
- <div class="buttons">${shown2.map(
22021
+ <div class="buttons">${shown3.map(
22019
22022
  (action) => b`<swc-button
22020
22023
  size="s"
22021
22024
  variant="secondary"
@@ -22032,7 +22035,7 @@ var SliccUpdates = class extends ModelElement {
22032
22035
  (warning) => b`<span><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle>${warning}</span>`
22033
22036
  )}</p>` : A}
22034
22037
  ${item2.commands.length ? b`<p class="commands"><span class="hidden">Commands: </span>${item2.commands.map(
22035
- (command2) => b`<code>${command2}</code>`
22038
+ (command3) => b`<code>${command3}</code>`
22036
22039
  )}</p>` : A}
22037
22040
  ${this.#packageProgress(item2)}
22038
22041
  ${error ? b`<div class="error" role="alert" data-error>
@@ -22713,9 +22716,9 @@ var SliccApp = class extends ThemedElement {
22713
22716
  if (!model) return [];
22714
22717
  const selected = this.#cone();
22715
22718
  const cones = model.agent.list().filter((agent) => agent.kind === "cone");
22716
- const list4 = model.sprinkles.list();
22717
- const order = [selected, ...cones.map((cone) => cone.id), ...list4.map((item2) => item2.agentId)];
22718
- return list4.map((sprinkle) => {
22719
+ const list5 = model.sprinkles.list();
22720
+ const order = [selected, ...cones.map((cone) => cone.id), ...list5.map((item2) => item2.agentId)];
22721
+ return list5.map((sprinkle) => {
22719
22722
  const cone = cones.find((agent) => agent.id === sprinkle.agentId && agent.id !== selected);
22720
22723
  return cone ? { ...sprinkle, title: `${cone.name} / ${sprinkle.title}` } : sprinkle;
22721
22724
  }).sort((a22, b13) => order.indexOf(a22.agentId) - order.indexOf(b13.agentId));
@@ -22771,11 +22774,11 @@ var SliccApp = class extends ThemedElement {
22771
22774
  #rail(items, side) {
22772
22775
  const primary = side === "bottom" ? items.filter((item2) => phoneRail.includes(item2.id)) : items;
22773
22776
  const fit = primary.length < items.length ? Math.min(primary.length, railCapacity(this.#width, Infinity)) : railCapacity(this.#width, items.length);
22774
- const shown2 = side === "bottom" ? primary.slice(0, fit) : items;
22775
- const grouped = new Set(shown2.map((item2) => item2.cone).filter(Boolean)).size > 1;
22776
- return b`<nav class=${`rail ${side}`} aria-label=${`Panels, ${side}`} @dragover=${this.#railOver} @dragleave=${this.#railLeave} @drop=${this.#railDrop}>${shown2.map(
22777
- (item2, index) => b`${grouped && index && shown2[index - 1].cone !== item2.cone ? b`<span class="cones" aria-hidden="true"></span>` : A}${this.#railButton(item2, side)}`
22778
- )}${this.#more(items.filter((item2) => !shown2.includes(item2)))}</nav>`;
22777
+ const shown3 = side === "bottom" ? primary.slice(0, fit) : items;
22778
+ const grouped = new Set(shown3.map((item2) => item2.cone).filter(Boolean)).size > 1;
22779
+ return b`<nav class=${`rail ${side}`} aria-label=${`Panels, ${side}`} @dragover=${this.#railOver} @dragleave=${this.#railLeave} @drop=${this.#railDrop}>${shown3.map(
22780
+ (item2, index) => b`${grouped && index && shown3[index - 1].cone !== item2.cone ? b`<span class="cones" aria-hidden="true"></span>` : A}${this.#railButton(item2, side)}`
22781
+ )}${this.#more(items.filter((item2) => !shown3.includes(item2)))}</nav>`;
22779
22782
  }
22780
22783
  #rails() {
22781
22784
  const dock = this.renderRoot.querySelector("slicc-dock");
@@ -23018,6 +23021,202 @@ function sudo(approval) {
23018
23021
  const as = approval.target && approval.target !== "root" ? `-u ${approval.target} ` : "";
23019
23022
  return `sudo ${as}${approval.command}`;
23020
23023
  }
23024
+ var rulePaths = ["/etc/fnox", "/etc/fnox/config.toml", "/etc/slicc/secrets-policy.toml"];
23025
+ var ruleParents = ["/", "/etc", "/etc/slicc"];
23026
+ function absolute(cwd, word) {
23027
+ const parts2 = [];
23028
+ for (const part2 of `${word.startsWith("/") ? "" : cwd}/${word}`.split("/")) {
23029
+ if (part2 === "..") parts2.pop();
23030
+ else if (part2 && part2 !== ".") parts2.push(part2);
23031
+ const alias = parts2.join("/").match(/^proc\/([^/]+)\/(?:task\/[^/]+\/)?(root|cwd)$/);
23032
+ if (alias?.[2] === "root") parts2.length = 0;
23033
+ else if (alias && /^(?:self|thread-self)$/.test(alias[1]))
23034
+ parts2.splice(0, parts2.length, ...cwd.split("/").filter(Boolean));
23035
+ }
23036
+ return `/${parts2.join("/")}`;
23037
+ }
23038
+ function pattern(path) {
23039
+ const source = path.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "").replace(/\*\*/g, "").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\u0001/g, "(?:.*/)?").replace(/\u0002/g, ".*");
23040
+ return new RegExp(`^${source}$`);
23041
+ }
23042
+ var opaque = /^\/(?:proc\/[^/]+\/(?:task\/[^/]+\/)?(?:cwd|fd|map_files)|dev\/fd)(?:\/|$)/;
23043
+ var certain = (rules) => rules.startsWith("/") && !opaque.test(rules) && !rules.endsWith("\u2026");
23044
+ var readOnly = [
23045
+ "cat",
23046
+ "cd",
23047
+ "df",
23048
+ "diff",
23049
+ "du",
23050
+ "echo",
23051
+ "file",
23052
+ "grep",
23053
+ "head",
23054
+ "ls",
23055
+ "pwd",
23056
+ "readlink",
23057
+ "realpath",
23058
+ "stat",
23059
+ "tail",
23060
+ "tree",
23061
+ "wc"
23062
+ ];
23063
+ function candidates(word) {
23064
+ if (word === "--") return [];
23065
+ if (word.startsWith("--")) return [word];
23066
+ if (!word.startsWith("-")) return [word];
23067
+ const slash = word.indexOf("/");
23068
+ const cluster2 = Array.from(word.slice(2), (_9, index) => word.slice(index + 2));
23069
+ return [...cluster2, ...slash > 2 ? [word.slice(slash)] : []];
23070
+ }
23071
+ function protects(path, whole) {
23072
+ const inside = (candidate) => candidate === "/etc/fnox" || candidate.startsWith("/etc/fnox/");
23073
+ if (opaque.test(path)) return true;
23074
+ if (/[[{$]/.test(path)) {
23075
+ const prefix = path.slice(0, path.search(/[*?[{$]/));
23076
+ return inside(prefix) || rulePaths.some((rule) => rule.startsWith(prefix));
23077
+ }
23078
+ if (!/[*?]/.test(path))
23079
+ return inside(path) || rulePaths.includes(path) || whole && ruleParents.includes(path);
23080
+ const glob = pattern(path);
23081
+ const top = path.split("/").slice(0, 3).join("/");
23082
+ return rulePaths.some((rule) => glob.test(rule)) || top !== path && pattern(top).test("/etc/fnox") || whole && ruleParents.some((parent) => glob.test(parent));
23083
+ }
23084
+ var command2 = (word) => word.split("/").pop() ?? "";
23085
+ var trusted = (word) => readOnly.includes(word.replace(/^\/(?:usr\/)?bin\/(?=[^/]+$)/, ""));
23086
+ var expansion = /\$\([^)]*\)?|\$\{[^}]*\}?|\$[\w@*#?$!-]*|`[^`]*`?/;
23087
+ function moved(cwds, word) {
23088
+ for (const cwd of [...cwds]) {
23089
+ const next = absolute(cwd, word);
23090
+ if (protects(next, false) || cwds.size >= 64) return next;
23091
+ cwds.add(next);
23092
+ }
23093
+ return null;
23094
+ }
23095
+ function touched(cwds, word, whole) {
23096
+ for (const candidate of candidates(word))
23097
+ for (const cwd of cwds) {
23098
+ const path = absolute(cwd, candidate);
23099
+ if (protects(path, whole)) return path;
23100
+ }
23101
+ return null;
23102
+ }
23103
+ function ansi(command3) {
23104
+ const escape2 = /\\(?:x([0-9a-fA-F]{1,2})|u([0-9a-fA-F]{1,4})|U([0-9a-fA-F]{1,8})|([0-7]{1,3})|([\s\S]))/g;
23105
+ return command3.replace(
23106
+ /\$'((?:[^'\\]|\\[\s\S])*)'/g,
23107
+ (_9, body4) => `'${body4.replace(escape2, (_match, hex, short2, long, octal, plain) => {
23108
+ if (octal) return String.fromCharCode(Number.parseInt(octal, 8));
23109
+ if (plain) return plain;
23110
+ return String.fromCodePoint(
23111
+ Math.min(Number.parseInt(hex ?? short2 ?? long, 16), 1114111)
23112
+ );
23113
+ }).replace(/'/g, "'\\''")}'`
23114
+ ).replace(/\$"/g, '"');
23115
+ }
23116
+ function unglob(command3) {
23117
+ const chars = Array.from(command3);
23118
+ const close = /* @__PURE__ */ new Map();
23119
+ const open = [];
23120
+ for (const [index, char] of chars.entries()) {
23121
+ if (char === "(") open.push(index);
23122
+ else if (char === ")" && open.length) close.set(open.pop(), index);
23123
+ }
23124
+ const out = [];
23125
+ for (let index = 0; index < chars.length; index++) {
23126
+ const end = close.get(index);
23127
+ if (end !== void 0 && /^[?*+@!]$/.test(out.at(-1) ?? "")) {
23128
+ out[out.length - 1] = "*";
23129
+ index = end;
23130
+ } else out.push(chars[index]);
23131
+ }
23132
+ return out.join("");
23133
+ }
23134
+ var separators = /[;&|\n()`]/;
23135
+ function hide2(char) {
23136
+ return separators.test(char) ? String.fromCharCode(57344 + char.charCodeAt(0)) : char;
23137
+ }
23138
+ function unshield(segment2) {
23139
+ return segment2.replace(
23140
+ /[\ue000-\ue07f]/g,
23141
+ (char) => String.fromCharCode(char.charCodeAt(0) - 57344)
23142
+ );
23143
+ }
23144
+ function shield(text2) {
23145
+ let out = "";
23146
+ let quote = "";
23147
+ for (let index = 0; index < text2.length; index++) {
23148
+ const char = text2[index];
23149
+ if (quote === '"' && char === "\\") out += hide2(char) + hide2(text2[++index] ?? "");
23150
+ else if (quote) {
23151
+ out += char === quote ? char : hide2(char);
23152
+ if (char === quote) quote = "";
23153
+ } else if (char === "'" || char === '"') {
23154
+ quote = char;
23155
+ out += char;
23156
+ } else if (char === "\\") out += char + hide2(text2[++index] ?? "");
23157
+ else out += char;
23158
+ }
23159
+ return out;
23160
+ }
23161
+ function quoted(segment2) {
23162
+ const words = [];
23163
+ let word = "";
23164
+ let quote = "";
23165
+ for (let index = 0; index < segment2.length; index++) {
23166
+ const char = segment2[index];
23167
+ if (quote === '"' && char === "\\" && /["\\$`\n]/.test(segment2[index + 1] ?? ""))
23168
+ word += segment2[++index];
23169
+ else if (quote) {
23170
+ if (char === quote) quote = "";
23171
+ else word += char;
23172
+ } else if (char === "'" || char === '"') quote = char;
23173
+ else if (char === "\\") word += segment2[++index] ?? "";
23174
+ else if (/[\s<>=]/.test(char)) {
23175
+ words.push(word);
23176
+ word = "";
23177
+ } else word += char;
23178
+ }
23179
+ return [...words, word];
23180
+ }
23181
+ function commas(words) {
23182
+ return words.flatMap((word) => word.includes(",") ? [word, ...word.split(",")] : [word]).filter(Boolean);
23183
+ }
23184
+ function scan2(words, cwds, whole) {
23185
+ let cd = false;
23186
+ for (const [index, word] of words.entries()) {
23187
+ let hit = null;
23188
+ if (index === 0 || words[index - 1] === "-c") {
23189
+ cd = ["cd", "pushd"].includes(command2(word));
23190
+ hit = touched(cwds, word, whole);
23191
+ } else if (!cd) hit = touched(cwds, word, whole);
23192
+ else if (!word.startsWith("-")) {
23193
+ cd = false;
23194
+ hit = moved(cwds, word);
23195
+ }
23196
+ if (hit) return hit;
23197
+ }
23198
+ return null;
23199
+ }
23200
+ function secretRules(approval) {
23201
+ if (approval.command.length > 4096) return `${approval.command.slice(0, 40)}\u2026`;
23202
+ const start = absolute("/", approval.cwd ?? "/");
23203
+ if (protects(start, false)) return start;
23204
+ const cwds = /* @__PURE__ */ new Set([start]);
23205
+ const text2 = unglob(ansi(approval.command.replace(/\\\n/g, "")));
23206
+ const plain = text2.split(separators).map((segment2) => commas(segment2.split(/[\s<>=]+/).map((word) => word.replace(/['"\\]/g, ""))));
23207
+ const aware = shield(text2).split(separators).map((segment2) => commas(quoted(unshield(segment2))));
23208
+ const segments = JSON.stringify(aware) === JSON.stringify(plain) ? plain : [...plain, ...aware];
23209
+ const whole = segments.some((words) => words.length > 0 && !trusted(words[0]));
23210
+ for (const words of segments) {
23211
+ const hit = scan2(words, cwds, whole);
23212
+ if (hit) return hit;
23213
+ }
23214
+ const named = segments.flat().find((word) => !word.startsWith("/") && /fnox|secrets-policy/.test(word));
23215
+ if (named) return absolute([...cwds].pop() ?? start, named);
23216
+ const home2 = segments.flat().find((word) => /^~(?:[+-](?:\/|$)|.*\/\.\.(?:\/|$))/.test(word));
23217
+ if (home2) return home2;
23218
+ return segments.flat().some((word) => word.includes("$")) || text2.includes("`") ? text2.match(expansion)[0] : null;
23219
+ }
23021
23220
  function waiting(model, now) {
23022
23221
  return [...model?.approvals?.list() ?? []].filter((approval) => approval.expiresAt > now).sort((a22, b13) => a22.createdAt - b13.createdAt);
23023
23222
  }
@@ -23083,6 +23282,27 @@ var SliccApprovals = class extends ModelElement {
23083
23282
  margin-block-start: var(--swc-spacing-300);
23084
23283
  font-variant-numeric: tabular-nums;
23085
23284
  }
23285
+ .rules {
23286
+ display: flex;
23287
+ align-items: flex-start;
23288
+ gap: var(--swc-spacing-100);
23289
+ margin-block-start: var(--swc-spacing-200);
23290
+ padding: var(--swc-spacing-200) var(--swc-spacing-300);
23291
+ border-radius: var(--swc-corner-radius-medium-default);
23292
+ background: var(--swc-notice-subtle-background-color-default);
23293
+ --swc-icon-color: var(--swc-icon-color-notice);
23294
+ }
23295
+ .rules swc-icon-alert-triangle {
23296
+ flex: none;
23297
+ margin-block-start: 0.15em;
23298
+ }
23299
+ .rules p {
23300
+ margin: 0;
23301
+ }
23302
+ .rules code {
23303
+ font-family: var(--swc-code-font-family-stack);
23304
+ overflow-wrap: anywhere;
23305
+ }
23086
23306
  .problem {
23087
23307
  display: flex;
23088
23308
  align-items: flex-start;
@@ -23137,6 +23357,7 @@ var SliccApprovals = class extends ModelElement {
23137
23357
  async decide(approval, decision) {
23138
23358
  const port = this.model?.approvals;
23139
23359
  if (!port?.decide || this.busy || decision !== "deny" && !this.#armed) return;
23360
+ if (decision === "always" && secretRules(approval)) return;
23140
23361
  this.busy = decision;
23141
23362
  this.failure = null;
23142
23363
  this.#pending.add(approval.id);
@@ -23158,15 +23379,15 @@ var SliccApprovals = class extends ModelElement {
23158
23379
  }
23159
23380
  willUpdate(changed) {
23160
23381
  super.willUpdate(changed);
23161
- const list4 = this.#current();
23382
+ const list5 = this.#current();
23162
23383
  const showing = this.#showing;
23163
23384
  if (showing && this.#pending.has(showing.id)) return;
23164
- if (showing && !this.gone && !this.#done.has(showing.id) && !list4.some((approval) => approval.id === showing.id)) {
23385
+ if (showing && !this.gone && !this.#done.has(showing.id) && !list5.some((approval) => approval.id === showing.id)) {
23165
23386
  const reason = this.#ended.get(showing.id) ?? (showing.expiresAt <= this.now ? "expired" : "answered");
23166
23387
  if (reason !== "withdrawn") this.gone = { approval: showing, reason: gone(reason) };
23167
23388
  }
23168
23389
  if (this.gone) return;
23169
- const next = list4[0] ?? null;
23390
+ const next = list5[0] ?? null;
23170
23391
  if (next?.id === showing?.id) return;
23171
23392
  this.#showing = next;
23172
23393
  clearTimeout(this.#arming);
@@ -23237,10 +23458,12 @@ var SliccApprovals = class extends ModelElement {
23237
23458
  );
23238
23459
  }
23239
23460
  const held = this.#showing && this.#pending.has(this.#showing.id) ? this.#showing : null;
23240
- const list4 = this.#current();
23241
- const [approval, ...rest] = held ? [held, ...list4.filter(({ id }) => id !== held.id)] : list4;
23461
+ const list5 = this.#current();
23462
+ const [approval, ...rest] = held ? [held, ...list5.filter(({ id }) => id !== held.id)] : list5;
23242
23463
  if (!approval) return A;
23243
23464
  const failure2 = this.failure?.id === approval.id ? this.failure.message : "";
23465
+ const rules = secretRules(approval);
23466
+ const always = approval.always && !rules;
23244
23467
  return this.#dialog(
23245
23468
  approval,
23246
23469
  approval.id,
@@ -23248,12 +23471,13 @@ var SliccApprovals = class extends ModelElement {
23248
23471
  <h2 id="title" slot="heading">Allow sudo?</h2>
23249
23472
  ${this.#request(approval)}
23250
23473
  ${approval.cwd ? b`<p class="cwd">In ${approval.cwd}</p>` : A}
23251
- ${approval.always ? b`<p class="hint">Always allow lets ${approval.requester.name} run exactly this command with sudo without asking again.</p>` : A}
23474
+ ${rules ? b`<div class="rules" data-secret-rules=${rules}><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><p>${certain(rules) ? b`<strong>This changes secret rules.</strong> It names <code>${rules}</code>, where SLICC keeps which secrets programs may use and where they may be sent.` : b`<strong>This may change secret rules.</strong> SLICC can't tell where <code>${rules}</code> leads, and it could reach the files where SLICC keeps which secrets programs may use and where they may be sent.`} Allow it only if you asked for this change.</p></div>` : A}
23475
+ ${always ? b`<p class="hint">Always allow lets ${approval.requester.name} run exactly this command with sudo without asking again.</p>` : A}
23252
23476
  ${failure2 ? b`<p class="problem" role="alert" data-error><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${failure2}</span></p>` : A}
23253
23477
  <p class="timer" data-timer>Denied automatically in ${countdown(approval.expiresAt - this.now)}.</p>
23254
23478
  ${rest.length ? b`<p class="more" data-more>${rest.length === 1 ? "1 more request is waiting." : `${rest.length} more requests are waiting.`}</p>` : A}
23255
23479
  ${this.#button(approval, "deny", "Deny", "secondary")}
23256
- ${approval.always ? this.#button(approval, "always", "Always allow", "secondary") : A}
23480
+ ${always ? this.#button(approval, "always", "Always allow", "secondary") : A}
23257
23481
  ${this.#button(approval, "once", "Allow once", "accent")}
23258
23482
  </sp-alert-dialog>`
23259
23483
  );
@@ -24165,9 +24389,9 @@ var SliccBrowser = class extends ModelElement {
24165
24389
  if (visible2 && tab && !this.#ticker)
24166
24390
  this.#ticker = setInterval(() => this.requestUpdate(), 1e3);
24167
24391
  if (!(visible2 && tab)) this.#still();
24168
- const list4 = this.renderRoot.querySelector(".actions:not(.last)");
24169
- const count = list4?.childElementCount ?? 0;
24170
- if (list4 && count !== this.#count) list4.scrollTop = list4.scrollHeight;
24392
+ const list5 = this.renderRoot.querySelector(".actions:not(.last)");
24393
+ const count = list5?.childElementCount ?? 0;
24394
+ if (list5 && count !== this.#count) list5.scrollTop = list5.scrollHeight;
24171
24395
  this.#count = count;
24172
24396
  }
24173
24397
  #still() {
@@ -25061,18 +25285,18 @@ function failedTurns(messages, messageId) {
25061
25285
  }
25062
25286
  return ids;
25063
25287
  }
25064
- var touched = false;
25288
+ var touched2 = false;
25065
25289
  document.addEventListener(
25066
25290
  "pointerdown",
25067
25291
  (event2) => {
25068
- touched = event2.pointerType !== "mouse";
25292
+ touched2 = event2.pointerType !== "mouse";
25069
25293
  },
25070
25294
  true
25071
25295
  );
25072
25296
  document.addEventListener(
25073
25297
  "keydown",
25074
25298
  () => {
25075
- touched = false;
25299
+ touched2 = false;
25076
25300
  },
25077
25301
  true
25078
25302
  );
@@ -25248,7 +25472,7 @@ var SliccChat = class extends ThemedElement {
25248
25472
  this.#observe();
25249
25473
  }
25250
25474
  focus() {
25251
- if (!touched && globalThis.matchMedia("(pointer: fine)").matches)
25475
+ if (!touched2 && globalThis.matchMedia("(pointer: fine)").matches)
25252
25476
  this.focusOn("slicc-composer", ".log");
25253
25477
  else this.focusOn(".log");
25254
25478
  }
@@ -26254,7 +26478,7 @@ e25("swc-icon-clock", o84);
26254
26478
  // src/app/composer.ts
26255
26479
  var limit = 25 * 1024 * 1024;
26256
26480
  function available(model) {
26257
- return commands2.filter((command2) => command2.available?.(model) ?? true);
26481
+ return commands2.filter((command3) => command3.available?.(model) ?? true);
26258
26482
  }
26259
26483
  var commands2 = [
26260
26484
  {
@@ -26333,8 +26557,8 @@ function trigger(text2, caret) {
26333
26557
  const argument = before.match(/^\/(\w+)\s+(\S*)$/);
26334
26558
  if (argument)
26335
26559
  return { kind: "argument", start: before.length - argument[2].length, query: argument[2] };
26336
- const command2 = before.match(/^\/([\w:-]*)$/);
26337
- if (command2) return { kind: "command", start: 0, query: command2[1] };
26560
+ const command3 = before.match(/^\/([\w:-]*)$/);
26561
+ if (command3) return { kind: "command", start: 0, query: command3[1] };
26338
26562
  const mention = before.match(/(^|\s)@([\w./-]*)$/);
26339
26563
  if (mention)
26340
26564
  return { kind: "mention", start: before.length - mention[2].length - 1, query: mention[2] };
@@ -26641,14 +26865,14 @@ var SliccComposer = class extends ModelElement {
26641
26865
  const text2 = this.value.trim();
26642
26866
  const attachments = this.attachments.filter((attachment2) => !attachment2.error);
26643
26867
  if (!model || !text2 && attachments.length === 0 || this.#held()) return;
26644
- const command2 = text2.match(/^\/(\w+)(?:\s+(.*))?$/);
26645
- const known = command2 && available(model).find((candidate) => candidate.name === command2[1]);
26868
+ const command3 = text2.match(/^\/(\w+)(?:\s+(.*))?$/);
26869
+ const known = command3 && available(model).find((candidate) => candidate.name === command3[1]);
26646
26870
  this.value = "";
26647
26871
  this.attachments = [];
26648
26872
  this.popup = null;
26649
26873
  this.#history = -1;
26650
26874
  if (known) {
26651
- known.run(model, this.#agent, (command2[2] ?? "").trim());
26875
+ known.run(model, this.#agent, (command3[2] ?? "").trim());
26652
26876
  return;
26653
26877
  }
26654
26878
  const busy = this.#busy();
@@ -26736,22 +26960,22 @@ var SliccComposer = class extends ModelElement {
26736
26960
  const model = this.model;
26737
26961
  if (kind === "command") {
26738
26962
  const usable = available(model);
26739
- const builtins = usable.map((command2) => ({
26740
- value: command2.name,
26741
- label: `/${command2.name}`,
26742
- detail: command2.detail
26963
+ const builtins = usable.map((command3) => ({
26964
+ value: command3.name,
26965
+ label: `/${command3.name}`,
26966
+ detail: command3.detail
26743
26967
  }));
26744
- const own4 = model.agent.commands(this.#agent).filter((command2) => !usable.some((builtin) => builtin.name === command2.name)).map((command2) => ({
26745
- value: command2.name,
26746
- label: `/${command2.name}`,
26747
- detail: command2.description,
26748
- group: command2.kind === "skill" ? "Skills" : "Prompts"
26968
+ const own4 = model.agent.commands(this.#agent).filter((command3) => !usable.some((builtin) => builtin.name === command3.name)).map((command3) => ({
26969
+ value: command3.name,
26970
+ label: `/${command3.name}`,
26971
+ detail: command3.description,
26972
+ group: command3.kind === "skill" ? "Skills" : "Prompts"
26749
26973
  })).sort((a22, b13) => a22.group === b13.group ? 0 : a22.group === "Prompts" ? -1 : 1);
26750
26974
  return filter([...builtins, ...own4], query, Number.POSITIVE_INFINITY);
26751
26975
  }
26752
26976
  if (kind === "argument") {
26753
26977
  const name = this.value.match(/^\/(\w+)/)?.[1];
26754
- return filter(commands2.find((command2) => command2.name === name)?.args?.(model) ?? [], query);
26978
+ return filter(commands2.find((command3) => command3.name === name)?.args?.(model) ?? [], query);
26755
26979
  }
26756
26980
  const files = this.#paths.map((path) => ({
26757
26981
  value: path,
@@ -26771,7 +26995,7 @@ var SliccComposer = class extends ModelElement {
26771
26995
  if (this.popup?.kind === "file" || this.popup?.kind === "secret") return;
26772
26996
  const found = trigger(this.value, area.selectionStart ?? this.value.length);
26773
26997
  const name = this.value.match(/^\/(\w+)/)?.[1];
26774
- const takes = commands2.some((command2) => command2.name === name && command2.args);
26998
+ const takes = commands2.some((command3) => command3.name === name && command3.args);
26775
26999
  this.popup = found && (found.kind !== "argument" || takes) ? { ...found, items: this.#choices(found.kind, found.query), index: 0 } : null;
26776
27000
  }
26777
27001
  openPopup(kind) {
@@ -27365,8 +27589,8 @@ var SliccMemory = class extends ModelElement {
27365
27589
  const scopes = this.#scopes;
27366
27590
  const scope = this.scope;
27367
27591
  const option2 = (item2) => b`<sp-menu-item value=${item2.id}>${item2.label}</sp-menu-item>`;
27368
- const shown2 = visible(all2, scope, this.tag, this.query);
27369
- return b`<div class="bar"><span>${shown2.length} of ${all2.filter((memory) => memory.scope === scope).length} memories</span><span class="spacer"></span>
27592
+ const shown3 = visible(all2, scope, this.tag, this.query);
27593
+ return b`<div class="bar"><span>${shown3.length} of ${all2.filter((memory) => memory.scope === scope).length} memories</span><span class="spacer"></span>
27370
27594
  <swc-action-button size="s" quiet @click=${() => this.editing = "new"}><swc-icon-add slot="icon"></swc-icon-add>Remember</swc-action-button>
27371
27595
  </div>
27372
27596
  <div class="tools">
@@ -27388,7 +27612,7 @@ var SliccMemory = class extends ModelElement {
27388
27612
  </div>
27389
27613
  <div class="list">
27390
27614
  ${this.editing === "new" ? b`<div class="row">${this.#form({ scope })}</div>` : A}
27391
- ${shown2.length ? sections(shown2).map(
27615
+ ${shown3.length ? sections(shown3).map(
27392
27616
  ([section, items]) => b`<h3>${section}</h3>${items.map((memory) => this.#row(memory))}`
27393
27617
  ) : b`<div class="note">${all2.length ? "No memories match." : "No memories yet."}</div>`}
27394
27618
  </div>`;
@@ -27696,10 +27920,10 @@ var SliccMonitor = class extends ModelElement {
27696
27920
  const surface2 = surfaces2[section.id];
27697
27921
  const rows = section.rows.length > 0;
27698
27922
  const open = rows && (this.expanded[section.id] ?? flag !== null);
27699
- const list4 = `rows-${section.id}`;
27923
+ const list5 = `rows-${section.id}`;
27700
27924
  return b`<li class="section" data-id=${section.id} data-open=${open ? "" : A}>
27701
27925
  <div class="line">
27702
- ${rows ? b`<button class="toggle" aria-expanded=${open ? "true" : "false"} aria-controls=${open ? list4 : A} @click=${() => {
27926
+ ${rows ? b`<button class="toggle" aria-expanded=${open ? "true" : "false"} aria-controls=${open ? list5 : A} @click=${() => {
27703
27927
  this.expanded = { ...this.expanded, [section.id]: !open };
27704
27928
  }}><span class="chevron"><swc-icon-chevron-right size="s" aria-hidden="true"></swc-icon-chevron-right></span><span class="name">${section.label}</span></button>` : b`<span class="chevron"></span><span class="name">${section.label}</span>`}
27705
27929
  ${flag ? b`<swc-badge size="s" variant=${flag.variant} subtle>${flag.text}</swc-badge>` : A}
@@ -27708,7 +27932,7 @@ var SliccMonitor = class extends ModelElement {
27708
27932
  >Open</swc-action-button
27709
27933
  >` : A}
27710
27934
  </div>
27711
- ${open ? b`<ul class="rows" id=${list4} aria-label=${section.label}>${c(
27935
+ ${open ? b`<ul class="rows" id=${list5} aria-label=${section.label}>${c(
27712
27936
  section.rows,
27713
27937
  (row, i25) => row.id ?? i25,
27714
27938
  (row) => this.#row(row)
@@ -27915,6 +28139,748 @@ var SliccNotices = class extends ModelElement {
27915
28139
  }
27916
28140
  };
27917
28141
 
28142
+ // src/app/vault.ts
28143
+ var exitNames = {
28144
+ "vault-direct": "Straight from the vault",
28145
+ "local-proxy": "Local proxy",
28146
+ "cli-v": "Local SLICC, direct",
28147
+ tailscale: "Tailscale",
28148
+ "local-link": "Linked SLICC, through this page"
28149
+ };
28150
+ function devicesOf(model) {
28151
+ return model?.network?.status().links?.devices ?? [];
28152
+ }
28153
+ function deviceName2(id, devices) {
28154
+ return (devices.find((device) => device.id === id)?.name ?? id).replace(/^slicc\b/i, "SLICC");
28155
+ }
28156
+ function exitLabel(exit, devices) {
28157
+ if (exit.startsWith("device:")) return deviceName2(exit.slice("device:".length), devices);
28158
+ return exitNames[exit];
28159
+ }
28160
+ function list4(items) {
28161
+ if (items.length < 2) return items.join("");
28162
+ return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
28163
+ }
28164
+ function ruleLine(rule) {
28165
+ const methods = rule.methods.length ? rule.methods.join(" or ") : "any method";
28166
+ const paths = rule.paths.length ? rule.paths.join(" or ") : "any path";
28167
+ return `${rule.domain}, in ${rule.header}, ${methods}, ${paths}`;
28168
+ }
28169
+ function shellWord(word) {
28170
+ return /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replace(/'/g, "'\\''")}'`;
28171
+ }
28172
+ function execLine(grant) {
28173
+ if (grant === false) return "Never revealed to programs";
28174
+ if (grant === true) return "Revealed to any program that asks";
28175
+ return `Revealed only to ${list4(grant.argv.map((argv) => argv.map(shellWord).join(" ")))}`;
28176
+ }
28177
+ function widening(change, devices) {
28178
+ switch (change.kind) {
28179
+ case "rule":
28180
+ return `${change.secret} may now be sent to ${ruleLine(change.rule)}`;
28181
+ case "methods":
28182
+ return `${change.secret} may now use ${list4(change.methods)} on ${change.domain}`;
28183
+ case "paths":
28184
+ return `${change.secret} may now reach ${list4(change.paths)} on ${change.domain}`;
28185
+ case "exec": {
28186
+ const line = execLine(change.grant);
28187
+ return `${change.secret}: ${line[0].toLowerCase()}${line.slice(1)}`;
28188
+ }
28189
+ case "exit":
28190
+ return `${change.secret} may now leave through ${exitLabel(change.exit, devices)}`;
28191
+ default:
28192
+ return `The limit ${change.field} rose from ${change.from} to ${change.to}`;
28193
+ }
28194
+ }
28195
+ function shown2(element2, style = getComputedStyle(element2)) {
28196
+ return !element2.hidden && !element2.inert && style.display !== "none";
28197
+ }
28198
+ function disabled(element2) {
28199
+ if (element2.matches(":disabled")) return true;
28200
+ if (!element2.matches("button, input, select, textarea")) return false;
28201
+ let fieldset = element2.closest("fieldset[disabled]");
28202
+ while (fieldset) {
28203
+ if (!fieldset.querySelector(":scope > legend")?.contains(element2)) return true;
28204
+ fieldset = fieldset.parentElement?.closest("fieldset[disabled]") ?? null;
28205
+ }
28206
+ return false;
28207
+ }
28208
+ function reachable(element2) {
28209
+ const radio = element2;
28210
+ if (radio.localName !== "input" || radio.type !== "radio" || !radio.name) return true;
28211
+ const root2 = radio.getRootNode();
28212
+ const group = Array.from(root2.querySelectorAll("input[type=radio]")).filter(
28213
+ (other) => other.name === radio.name && other.form === radio.form
28214
+ );
28215
+ const checked = group.find((other) => other.checked);
28216
+ return checked ? checked === radio : radio === group[0] || radio === group.at(-1);
28217
+ }
28218
+ function tabbables(root2, stop, out = []) {
28219
+ for (const child of Array.from(root2.children)) {
28220
+ if (child === stop) {
28221
+ out.push(null);
28222
+ continue;
28223
+ }
28224
+ const style = getComputedStyle(child);
28225
+ if (!shown2(child, style)) continue;
28226
+ if (child.localName === "details" && !child.open) {
28227
+ const summary = child.querySelector(":scope > summary");
28228
+ if (summary && shown2(summary) && getComputedStyle(summary).visibility !== "hidden" && !(summary.hasAttribute("tabindex") && summary.tabIndex < 0))
28229
+ out.push(summary);
28230
+ continue;
28231
+ }
28232
+ if (child.tabIndex >= 0 && !disabled(child) && style.visibility !== "hidden" && reachable(child))
28233
+ out.push(child);
28234
+ if (child.shadowRoot) tabbables(child.shadowRoot, stop, out);
28235
+ tabbables(child, stop, out);
28236
+ }
28237
+ return out;
28238
+ }
28239
+ function sequential(order) {
28240
+ const rank = (element2) => element2 && element2.tabIndex > 0 ? element2.tabIndex : Number.POSITIVE_INFINITY;
28241
+ return [...order].sort((a22, b13) => rank(a22) - rank(b13) || 0);
28242
+ }
28243
+ var SliccVaultButton = class extends i2 {
28244
+ static properties = {
28245
+ vault: { attribute: false },
28246
+ config: { attribute: false }
28247
+ };
28248
+ #button = null;
28249
+ #off = null;
28250
+ #ready = false;
28251
+ #sent = "";
28252
+ static styles = i`
28253
+ :host {
28254
+ display: inline-flex;
28255
+ vertical-align: middle;
28256
+ }
28257
+ iframe {
28258
+ border: 0;
28259
+ background: transparent;
28260
+ color-scheme: normal;
28261
+ }
28262
+ `;
28263
+ constructor() {
28264
+ super();
28265
+ this.vault = null;
28266
+ this.config = null;
28267
+ }
28268
+ connectedCallback() {
28269
+ super.connectedCallback();
28270
+ this.requestUpdate();
28271
+ }
28272
+ disconnectedCallback() {
28273
+ super.disconnectedCallback();
28274
+ this.#drop();
28275
+ }
28276
+ focus() {
28277
+ this.#button?.post({ type: "slicc-vault", v: 1, op: "focus" });
28278
+ }
28279
+ updated(changed) {
28280
+ if (changed.has("vault")) this.#drop();
28281
+ if (!this.#button && this.vault) this.#mount(this.vault);
28282
+ this.#send();
28283
+ }
28284
+ #mount(vault) {
28285
+ const button = vault.button();
28286
+ this.#button = button;
28287
+ this.#off = button.listen((event2) => this.#event(event2));
28288
+ this.renderRoot.append(button.element);
28289
+ }
28290
+ #drop() {
28291
+ this.#off?.();
28292
+ this.#button?.element.remove();
28293
+ this.#button = null;
28294
+ this.#off = null;
28295
+ this.#ready = false;
28296
+ this.#sent = "";
28297
+ }
28298
+ #send() {
28299
+ if (!(this.#ready && this.#button && this.config)) return;
28300
+ const sent = JSON.stringify(this.config);
28301
+ if (sent === this.#sent) return;
28302
+ this.#sent = sent;
28303
+ this.#button.post({ type: "slicc-vault", v: 1, op: "button", button: this.config });
28304
+ }
28305
+ #event(event2) {
28306
+ const element2 = this.#button?.element;
28307
+ if (event2.event === "ready") {
28308
+ this.#ready = true;
28309
+ this.#send();
28310
+ } else if (event2.event === "size") {
28311
+ element2.style.width = `${event2.width}px`;
28312
+ element2.style.height = `${event2.height}px`;
28313
+ element2.style.margin = `${-event2.inset}px`;
28314
+ } else if (event2.event === "label") {
28315
+ element2.title = event2.label;
28316
+ } else if (event2.event === "focus") {
28317
+ const order = sequential(tabbables(this.ownerDocument, this));
28318
+ const at = order.indexOf(null);
28319
+ order[event2.direction === "next" ? at + 1 : at - 1]?.focus();
28320
+ } else if (event2.event === "popup") {
28321
+ const { intent, state: state3, result } = event2;
28322
+ this.dispatchEvent(
28323
+ new CustomEvent("vault-popup", {
28324
+ detail: { intent, state: state3, ...result ? { result } : {} },
28325
+ bubbles: true,
28326
+ composed: true
28327
+ })
28328
+ );
28329
+ }
28330
+ }
28331
+ };
28332
+
28333
+ // src/app/secrets.ts
28334
+ var kinds = { secret: null, grant: "Sign-in", model: "Model key", mcp: "MCP server" };
28335
+ var lockModes = {
28336
+ auto: "Unlocks with this browser",
28337
+ passkey: "Unlocks with a passkey",
28338
+ passphrase: "Unlocks with a passphrase"
28339
+ };
28340
+ function until(at, now = Date.now()) {
28341
+ const minutes = Math.ceil((at - now) / 6e4);
28342
+ if (minutes <= 0) return "expired";
28343
+ if (minutes < 60) return `in ${minutes} min`;
28344
+ const hours = Math.round(minutes / 60);
28345
+ if (hours < 24) return `in ${hours} h`;
28346
+ const days = Math.round(hours / 24);
28347
+ return `in ${days} ${days === 1 ? "day" : "days"}`;
28348
+ }
28349
+ function who(entry) {
28350
+ if (entry.tag) return entry.tag;
28351
+ if (entry.uid === null) return "the vault";
28352
+ return entry.uid === 0 ? "root" : `uid ${entry.uid}`;
28353
+ }
28354
+ function sourceLine(secret, devices) {
28355
+ const on = secret.sourceDevice ? ` on ${deviceName2(secret.sourceDevice, devices)}` : "";
28356
+ const [kind, detail = ""] = secret.source.split(/:(.*)/s);
28357
+ if (kind === "popup") return `Entered in the vault${on}`;
28358
+ if (kind === "process") return `Saved by a program${on}`;
28359
+ if (kind === "import") return `Imported from ${detail}`;
28360
+ if (kind === "oauth") {
28361
+ const as = secret.account ? ` as ${secret.account}` : "";
28362
+ return `Signed in to ${secret.provider ?? detail}${as}`;
28363
+ }
28364
+ return `A jerk lease for ${detail.split("@")[0]}${on}`;
28365
+ }
28366
+ function startOfDay(now) {
28367
+ const date = new Date(now);
28368
+ date.setHours(0, 0, 0, 0);
28369
+ return date.getTime();
28370
+ }
28371
+ function revealed(name, entries, now = Date.now(), more = false) {
28372
+ const reveals = entries.filter(
28373
+ (entry) => entry.type === "materialize" && entry.name === name
28374
+ );
28375
+ if (!reveals.length) return null;
28376
+ const [last] = reveals;
28377
+ const start = startOfDay(now);
28378
+ const today = reveals.filter((entry) => entry.at >= start).length;
28379
+ const partial = more && (entries[entries.length - 1]?.at ?? 0) >= start;
28380
+ const when = today ? `${partial ? "at least " : ""}${today} ${today === 1 ? "time" : "times"} today` : since(last.at, now);
28381
+ return b`Revealed to programs ${when}, last by ${who(last)} with <code>${last.argv}</code>`;
28382
+ }
28383
+ function auditLine(entry, devices) {
28384
+ switch (entry.type) {
28385
+ case "inject":
28386
+ case "forward":
28387
+ return b`${entry.type === "inject" ? "Sent to" : "Forwarded to"} ${entry.domain}: <code>${entry.method} ${entry.path}</code>, through ${exitLabel(entry.exit, devices)}`;
28388
+ case "materialize":
28389
+ return b`Revealed to <code>${entry.argv}</code> in <code>${entry.cwd}</code>`;
28390
+ case "seal":
28391
+ return b`${entry.rotated ? "Value replaced" : "Value saved"}`;
28392
+ case "remove":
28393
+ return b`Removed`;
28394
+ case "restore":
28395
+ return b`Restored`;
28396
+ default: {
28397
+ const count = entry.widened.length;
28398
+ const widened = count ? `, widening ${count} ${count === 1 ? "rule" : "rules"}` : "";
28399
+ return b`<code>${entry.file}</code> ${entry.accepted ? `changed${widened}` : "changed and was refused"}${entry.warning ? b`. ${entry.warning}` : A}`;
28400
+ }
28401
+ }
28402
+ }
28403
+ function merged(...pages) {
28404
+ const bySeq = /* @__PURE__ */ new Map();
28405
+ for (const page of pages) for (const entry of page) bySeq.set(entry.seq, entry);
28406
+ return [...bySeq.values()].sort((a22, b13) => b13.seq - a22.seq);
28407
+ }
28408
+ function prompt2(text2) {
28409
+ return text2.length > 200 ? `${text2.slice(0, 199)}\u2026` : text2;
28410
+ }
28411
+ var SliccSecrets = class extends ThemedElement {
28412
+ static properties = {
28413
+ ...ThemedElement.properties,
28414
+ older: { state: true },
28415
+ more: { state: true },
28416
+ loading: { state: true },
28417
+ reveals: { state: true },
28418
+ popup: { state: true },
28419
+ acking: { state: true },
28420
+ failure: { state: true }
28421
+ };
28422
+ #tick;
28423
+ constructor() {
28424
+ super();
28425
+ this.older = [];
28426
+ this.more = null;
28427
+ this.loading = false;
28428
+ this.reveals = false;
28429
+ this.popup = null;
28430
+ this.acking = /* @__PURE__ */ new Set();
28431
+ this.failure = null;
28432
+ }
28433
+ static styles = i`
28434
+ :host {
28435
+ display: block;
28436
+ container-type: inline-size;
28437
+ }
28438
+ h2 {
28439
+ font-size: var(--swc-heading-size-xs);
28440
+ font-weight: var(--swc-bold-font-weight);
28441
+ line-height: var(--swc-heading-line-height);
28442
+ color: var(--swc-heading-color);
28443
+ margin: 0;
28444
+ }
28445
+ h3 {
28446
+ font-size: var(--swc-font-size-100);
28447
+ font-weight: var(--swc-bold-font-weight);
28448
+ color: var(--swc-heading-color);
28449
+ margin: var(--swc-spacing-400) 0 var(--swc-spacing-100);
28450
+ }
28451
+ p {
28452
+ margin: var(--swc-spacing-100) 0;
28453
+ }
28454
+ ul,
28455
+ ol {
28456
+ list-style: none;
28457
+ margin: 0;
28458
+ padding: 0;
28459
+ }
28460
+ code {
28461
+ font-family: var(--swc-code-font-family, ui-monospace, monospace);
28462
+ font-size: 0.9em;
28463
+ overflow-wrap: anywhere;
28464
+ }
28465
+ .head {
28466
+ display: flex;
28467
+ flex-wrap: wrap;
28468
+ align-items: baseline;
28469
+ gap: var(--swc-spacing-200) var(--swc-spacing-300);
28470
+ margin: var(--swc-spacing-500) 0 var(--swc-spacing-200);
28471
+ }
28472
+ .head slicc-vault-button {
28473
+ align-self: center;
28474
+ margin-inline-start: auto;
28475
+ }
28476
+ .muted {
28477
+ color: var(--swc-neutral-subdued-content-color-default);
28478
+ }
28479
+ .box {
28480
+ display: grid;
28481
+ gap: var(--swc-spacing-100);
28482
+ padding: var(--swc-spacing-200) var(--swc-spacing-300);
28483
+ margin: var(--swc-spacing-200) 0;
28484
+ border-radius: var(--swc-corner-radius-100);
28485
+ border: var(--swc-border-width-100) solid var(--swc-gray-200);
28486
+ background: var(--swc-background-layer-1-color);
28487
+ }
28488
+ .box.notice {
28489
+ border-color: var(--swc-notice-visual-color);
28490
+ --swc-icon-color: var(--swc-notice-visual-color);
28491
+ }
28492
+ .box .title {
28493
+ display: flex;
28494
+ align-items: flex-start;
28495
+ gap: var(--swc-spacing-100);
28496
+ font-weight: var(--swc-bold-font-weight);
28497
+ }
28498
+ .box ul {
28499
+ list-style: disc;
28500
+ padding-inline-start: var(--swc-spacing-400);
28501
+ }
28502
+ .box .actions {
28503
+ display: flex;
28504
+ flex-wrap: wrap;
28505
+ align-items: center;
28506
+ gap: var(--swc-spacing-200);
28507
+ }
28508
+ .prompt {
28509
+ margin: 0;
28510
+ padding-inline-start: var(--swc-spacing-200);
28511
+ border-inline-start: var(--swc-border-width-200) solid var(--swc-gray-300);
28512
+ overflow-wrap: anywhere;
28513
+ }
28514
+ .said {
28515
+ white-space: pre-wrap;
28516
+ }
28517
+ .from {
28518
+ display: block;
28519
+ font-size: var(--swc-font-size-75);
28520
+ color: var(--swc-neutral-subdued-content-color-default);
28521
+ }
28522
+ .secrets {
28523
+ display: grid;
28524
+ gap: var(--swc-spacing-200);
28525
+ }
28526
+ .secret {
28527
+ display: grid;
28528
+ grid-template-columns: minmax(0, 1fr) max-content;
28529
+ gap: var(--swc-spacing-100) var(--swc-spacing-300);
28530
+ padding: var(--swc-spacing-200) 0;
28531
+ border-top: var(--swc-border-width-100) solid var(--swc-gray-200);
28532
+ }
28533
+ .secret:first-child {
28534
+ border-top: 0;
28535
+ }
28536
+ .name {
28537
+ display: flex;
28538
+ flex-wrap: wrap;
28539
+ align-items: baseline;
28540
+ gap: var(--swc-spacing-100) var(--swc-spacing-200);
28541
+ min-width: 0;
28542
+ }
28543
+ .name code {
28544
+ font-size: var(--swc-font-size-100);
28545
+ font-weight: var(--swc-bold-font-weight);
28546
+ }
28547
+ .secret slicc-vault-button {
28548
+ align-self: start;
28549
+ }
28550
+ .removed {
28551
+ margin-top: var(--swc-spacing-200);
28552
+ border-top: var(--swc-border-width-100) solid var(--swc-gray-200);
28553
+ padding-top: var(--swc-spacing-200);
28554
+ }
28555
+ .removed summary {
28556
+ cursor: pointer;
28557
+ font-size: var(--swc-font-size-75);
28558
+ color: var(--swc-neutral-subdued-content-color-default);
28559
+ }
28560
+ .removed .secret dl,
28561
+ .removed .secret .name code {
28562
+ color: var(--swc-neutral-subdued-content-color-default);
28563
+ }
28564
+ dl {
28565
+ grid-column: 1 / -1;
28566
+ display: grid;
28567
+ grid-template-columns: max-content minmax(0, 1fr);
28568
+ gap: var(--swc-spacing-50) var(--swc-spacing-300);
28569
+ margin: 0;
28570
+ font-size: var(--swc-font-size-75);
28571
+ line-height: var(--swc-line-height-200);
28572
+ }
28573
+ dt {
28574
+ color: var(--swc-neutral-subdued-content-color-default);
28575
+ }
28576
+ dd {
28577
+ margin: 0;
28578
+ min-width: 0;
28579
+ overflow-wrap: anywhere;
28580
+ }
28581
+ .audit-head {
28582
+ display: flex;
28583
+ flex-wrap: wrap;
28584
+ align-items: baseline;
28585
+ justify-content: space-between;
28586
+ gap: var(--swc-spacing-200);
28587
+ margin: var(--swc-spacing-400) 0 var(--swc-spacing-100);
28588
+ }
28589
+ .audit-head h3 {
28590
+ margin-block: 0;
28591
+ }
28592
+ .audit li {
28593
+ display: grid;
28594
+ grid-template-columns: 7em minmax(0, 1fr);
28595
+ gap: var(--swc-spacing-50) var(--swc-spacing-300);
28596
+ padding: var(--swc-spacing-100) 0;
28597
+ border-top: var(--swc-border-width-100) solid var(--swc-gray-200);
28598
+ font-size: var(--swc-font-size-75);
28599
+ line-height: var(--swc-line-height-200);
28600
+ }
28601
+ .audit time {
28602
+ color: var(--swc-neutral-subdued-content-color-default);
28603
+ }
28604
+ .audit .what {
28605
+ overflow-wrap: anywhere;
28606
+ }
28607
+ .audit .by {
28608
+ color: var(--swc-neutral-subdued-content-color-default);
28609
+ }
28610
+ .row {
28611
+ display: flex;
28612
+ flex-wrap: wrap;
28613
+ align-items: center;
28614
+ gap: var(--swc-spacing-200);
28615
+ margin-top: var(--swc-spacing-200);
28616
+ }
28617
+ .error {
28618
+ display: flex;
28619
+ align-items: flex-start;
28620
+ gap: var(--swc-spacing-100);
28621
+ color: var(--swc-negative-content-color-default);
28622
+ font-size: var(--swc-font-size-75);
28623
+ --swc-icon-color: var(--swc-negative-content-color-default);
28624
+ }
28625
+ @container (max-width: 480px) {
28626
+ .secret {
28627
+ grid-template-columns: minmax(0, 1fr);
28628
+ }
28629
+ .secret slicc-vault-button {
28630
+ order: 1;
28631
+ justify-self: start;
28632
+ }
28633
+ dl {
28634
+ grid-template-columns: minmax(0, 1fr);
28635
+ }
28636
+ dd {
28637
+ margin-bottom: var(--swc-spacing-75);
28638
+ }
28639
+ .audit li {
28640
+ grid-template-columns: minmax(0, 1fr);
28641
+ }
28642
+ }
28643
+ `;
28644
+ #pushed = [];
28645
+ disconnectedCallback() {
28646
+ super.disconnectedCallback();
28647
+ clearTimeout(this.#tick);
28648
+ this.older = [];
28649
+ this.more = null;
28650
+ this.#pushed = [];
28651
+ }
28652
+ willUpdate(changed) {
28653
+ super.willUpdate(changed);
28654
+ const before = changed.get("model");
28655
+ if (!changed.has("model") || before?.vault === this.model?.vault) return;
28656
+ this.older = [];
28657
+ this.more = null;
28658
+ this.loading = false;
28659
+ this.popup = null;
28660
+ this.acking = /* @__PURE__ */ new Set();
28661
+ this.failure = null;
28662
+ this.#pushed = [];
28663
+ }
28664
+ updated() {
28665
+ clearTimeout(this.#tick);
28666
+ if (!this.isConnected) return;
28667
+ const now = Date.now();
28668
+ const next = Math.min(
28669
+ now + 6e4,
28670
+ ...(this.model?.vault?.list()?.secrets ?? []).map((secret) => secret.expiresAt ?? Number.POSITIVE_INFINITY).filter((at) => at > now)
28671
+ );
28672
+ this.#tick = setTimeout(() => this.requestUpdate(), next - now);
28673
+ }
28674
+ #listed(vault) {
28675
+ this.#pushed = [];
28676
+ const head2 = vault.list()?.audit ?? [];
28677
+ const last = head2[head2.length - 1];
28678
+ if (this.older.length && (!last || last.seq > this.older[0].seq + 1)) {
28679
+ this.older = [];
28680
+ this.more = null;
28681
+ }
28682
+ this.requestUpdate();
28683
+ }
28684
+ #noticed(notice) {
28685
+ this.#pushed = [...this.#pushed.filter((item2) => item2.id !== notice.id), notice];
28686
+ this.requestUpdate();
28687
+ }
28688
+ subscribe(model) {
28689
+ const update = () => this.requestUpdate();
28690
+ return [
28691
+ ...super.subscribe(model),
28692
+ ...model.vault ? [
28693
+ model.vault.on("status", update),
28694
+ model.vault.on("list", () => this.#listed(model.vault)),
28695
+ model.vault.on("notice", (notice) => this.#noticed(notice))
28696
+ ] : [],
28697
+ ...model.network ? [model.network.on("network", update)] : []
28698
+ ];
28699
+ }
28700
+ #button(config) {
28701
+ const theme3 = this.color;
28702
+ return b`<slicc-vault-button
28703
+ .vault=${this.model?.vault}
28704
+ .config=${{ size: "m", variant: "secondary", ...config, theme: theme3 }}
28705
+ @vault-popup=${(event2) => this.#popup(event2.detail)}
28706
+ ></slicc-vault-button>`;
28707
+ }
28708
+ #popup(detail) {
28709
+ this.popup = detail;
28710
+ }
28711
+ async showOlder(vault, entries) {
28712
+ this.loading = true;
28713
+ this.failure = null;
28714
+ try {
28715
+ const before = entries[entries.length - 1]?.seq;
28716
+ const page = await vault.audit(before);
28717
+ if (this.model?.vault !== vault || !this.isConnected) return;
28718
+ const shown3 = merged(vault.list()?.audit ?? [], this.older);
28719
+ if (shown3[shown3.length - 1]?.seq !== before) return;
28720
+ this.older = merged(this.older, page.audit);
28721
+ this.more = page.more;
28722
+ } catch (error) {
28723
+ if (this.model?.vault === vault && this.isConnected) this.failure = error.message;
28724
+ } finally {
28725
+ if (this.model?.vault === vault) this.loading = false;
28726
+ }
28727
+ }
28728
+ async acknowledge(vault, notice) {
28729
+ if (this.acking.has(notice.id)) return;
28730
+ this.acking = /* @__PURE__ */ new Set([...this.acking, notice.id]);
28731
+ this.failure = null;
28732
+ try {
28733
+ await vault.acknowledge(notice.id);
28734
+ if (this.model?.vault === vault)
28735
+ this.#pushed = this.#pushed.filter((item2) => item2.id !== notice.id);
28736
+ } catch (error) {
28737
+ if (this.model?.vault === vault && this.isConnected) this.failure = error.message;
28738
+ } finally {
28739
+ if (this.model?.vault === vault)
28740
+ this.acking = new Set([...this.acking].filter((id) => id !== notice.id));
28741
+ }
28742
+ }
28743
+ #request(request2) {
28744
+ const asker = who(request2);
28745
+ const what = request2.kind === "value" ? b`${asker} asks for <code>${request2.name}</code>` : request2.kind === "oauth" ? b`${asker} asks you to sign in to ${request2.provider}` : b`${asker} asks for a jerk lease for ${request2.provider}`;
28746
+ return b`<div class="box" data-request=${request2.id}>
28747
+ <span class="title">${what}</span>
28748
+ ${request2.prompt ? b`<p class="prompt" data-prompt><span class="from">From the agent, not checked by SLICC:</span> <span class="said">${prompt2(request2.prompt)}</span></p>` : A}
28749
+ <div class="actions">
28750
+ ${this.#button({ intent: { kind: "request", request: request2.id }, label: "Answer in the vault", variant: "accent" })}
28751
+ <span class="muted">Expires ${until(request2.expiresAt)}</span>
28752
+ </div>
28753
+ </div>`;
28754
+ }
28755
+ #notice(vault, notice, devices) {
28756
+ return b`<div class="box notice" role="alert" data-notice=${notice.id}>
28757
+ <span class="title"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>Rules widened in <code>${notice.file}</code> ${since(notice.at)}</span></span>
28758
+ <ul>${notice.widened.map((change) => b`<li>${widening(change, devices)}</li>`)}</ul>
28759
+ <span>If you didn’t mean this, change the file back with sudo.</span>
28760
+ <div class="actions"><swc-button size="s" variant="secondary" data-action="acknowledge" ?pending=${this.acking.has(notice.id)} @click=${() => this.acknowledge(vault, notice)}>Acknowledge</swc-button></div>
28761
+ </div>`;
28762
+ }
28763
+ #state(secret) {
28764
+ const [variant, label] = secret.state === "removed" ? ["neutral", "Removed"] : secret.expiresAt !== void 0 && secret.expiresAt <= Date.now() ? ["notice", "Expired"] : secret.state === "inert" ? ["neutral", "No rule yet"] : ["positive", "Active"];
28765
+ return b`<swc-status-light size="s" variant=${variant}>${label}</swc-status-light>`;
28766
+ }
28767
+ #action(secret) {
28768
+ if (secret.state === "removed")
28769
+ return this.#button({
28770
+ intent: { kind: "manage", name: secret.name },
28771
+ label: "Manage",
28772
+ size: "s"
28773
+ });
28774
+ if (secret.source.startsWith("oauth:"))
28775
+ return this.#button({
28776
+ intent: { kind: "signin", provider: secret.source.slice("oauth:".length) },
28777
+ label: "Sign in again",
28778
+ size: "s"
28779
+ });
28780
+ return this.#button({
28781
+ intent: { kind: "enter", name: secret.name },
28782
+ label: "Replace value",
28783
+ size: "s"
28784
+ });
28785
+ }
28786
+ #secret(secret, entries, devices, more) {
28787
+ const kind = kinds[secret.kind];
28788
+ const reveal = revealed(secret.name, entries, Date.now(), more);
28789
+ const gone2 = secret.state === "removed";
28790
+ return b`<li class="secret" data-secret=${secret.name} data-state=${secret.state}>
28791
+ <div class="name">
28792
+ <code>${secret.name}</code>
28793
+ ${kind ? b`<swc-badge size="s" variant="neutral" subtle>${kind}</swc-badge>` : A}
28794
+ ${this.#state(secret)}
28795
+ </div>
28796
+ ${this.#action(secret)}
28797
+ <dl>
28798
+ <dt>${gone2 ? "Was sent to" : "Sent to"}</dt>
28799
+ <dd data-rules>${secret.rules.length ? secret.rules.map((rule, index) => b`${index ? b`<br />` : A}${ruleLine(rule)}`) : "Nowhere yet: no rule names it"}</dd>
28800
+ <dt>${gone2 ? "Left through" : "Leaves through"}</dt>
28801
+ <dd data-exits>${list4(secret.exits.map((exit) => exitLabel(exit, devices)))}</dd>
28802
+ <dt>Programs</dt>
28803
+ <dd data-exec>${execLine(secret.exec)}${reveal ? b`<br />${reveal}` : A}</dd>
28804
+ <dt>From</dt>
28805
+ <dd data-source>${sourceLine(secret, devices)}</dd>
28806
+ ${secret.usedAt ? b`<dt>Last used</dt><dd data-used>${since(secret.usedAt)}</dd>` : A}
28807
+ ${secret.expiresAt ? b`<dt>Expires</dt><dd data-expires>${until(secret.expiresAt)}</dd>` : A}
28808
+ ${secret.removedAt ? b`<dt>Removed</dt><dd>${since(secret.removedAt)}</dd>` : A}
28809
+ </dl>
28810
+ </li>`;
28811
+ }
28812
+ #secrets(list5, entries, devices) {
28813
+ const more = this.more ?? list5.auditMore;
28814
+ const row = (secret) => this.#secret(secret, entries, devices, more);
28815
+ const live = list5.secrets.filter((secret) => secret.state !== "removed");
28816
+ const removed = list5.secrets.filter((secret) => secret.state === "removed");
28817
+ return b`${live.length ? b`<ul class="secrets" aria-labelledby="secrets">${live.map(row)}</ul>` : A}
28818
+ ${removed.length ? b`<details class="removed" data-removed>
28819
+ <summary>Removed (${removed.length})</summary>
28820
+ <ul class="secrets">${removed.map(row)}</ul>
28821
+ </details>` : A}`;
28822
+ }
28823
+ #entry(entry, devices) {
28824
+ const date = new Date(entry.at);
28825
+ const name = entry.type === "policy" ? null : entry.name;
28826
+ return b`<li data-seq=${entry.seq} data-type=${entry.type}>
28827
+ <time datetime=${date.toISOString()} title=${date.toLocaleString("en-GB", { dateStyle: "medium", timeStyle: "short" })}>${since(entry.at)}</time>
28828
+ <span class="what">${name ? b`<code>${name}</code> · ` : A}${auditLine(entry, devices)}${entry.type === "policy" ? A : b` <span class="by">by ${who(entry)}</span>`}</span>
28829
+ </li>`;
28830
+ }
28831
+ #audit(vault, list5, entries, devices) {
28832
+ const shown3 = this.reveals ? entries.filter((entry) => entry.type === "materialize") : entries;
28833
+ const more = this.more ?? list5.auditMore;
28834
+ return b`<div class="audit-head">
28835
+ <h3 id="secrets-audit">Activity</h3>
28836
+ <sp-switch size="s" data-action="reveals" .checked=${this.reveals} @change=${(event2) => {
28837
+ this.reveals = event2.target.checked;
28838
+ }}>Only reveals to programs</sp-switch>
28839
+ </div>
28840
+ ${shown3.length ? b`<ol class="audit" aria-labelledby="secrets-audit">${shown3.map((entry) => this.#entry(entry, devices))}</ol>` : b`<p class="muted" data-audit-empty>${this.reveals ? more ? "No reveals in the activity loaded so far." : "No program has been given a secret." : "Nothing has used a secret yet."}</p>`}
28841
+ ${more ? b`<div class="row"><swc-button size="s" variant="secondary" fill-style="outline" data-action="older" ?pending=${this.loading} @click=${() => this.showOlder(vault, entries)}>Show older</swc-button></div>` : A}`;
28842
+ }
28843
+ #feedback() {
28844
+ if (this.popup?.state === "blocked")
28845
+ return b`<div class="error" role="alert" data-popup="blocked"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>The browser blocked the vault window. Allow pop-ups for this site, then try again.</span></div>`;
28846
+ if (this.popup?.state === "done" && this.popup.result)
28847
+ return b`<p role="status" data-popup="done">Saved <code>${this.popup.result.name}</code> in the vault.</p>`;
28848
+ return A;
28849
+ }
28850
+ render() {
28851
+ const vault = this.model?.vault;
28852
+ if (!vault) return A;
28853
+ const status2 = vault.status();
28854
+ const current = vault.list();
28855
+ const devices = devicesOf(this.model);
28856
+ const entries = merged(current?.audit ?? [], this.older);
28857
+ const listed = current?.notices ?? [];
28858
+ const notices = [
28859
+ ...listed,
28860
+ ...this.#pushed.filter((notice) => !listed.some((item2) => item2.id === notice.id))
28861
+ ].filter((notice) => !notice.acknowledged);
28862
+ return b`<section aria-labelledby="secrets" data-vault=${status2 ? status2.locked ? "locked" : "unlocked" : "loading"}>
28863
+ <div class="head">
28864
+ <h2 id="secrets">Secrets</h2>
28865
+ ${status2 ? b`<swc-status-light size="m" variant=${status2.locked ? "notice" : "positive"}>${status2.locked ? "Locked" : "Unlocked"}</swc-status-light><span class="muted">${lockModes[status2.lock]}</span>` : A}
28866
+ ${status2?.locked ? this.#button({ intent: { kind: "unlock" }, label: "Unlock", variant: "accent" }) : this.#button({ intent: { kind: "manage" }, label: "Open the vault" })}
28867
+ </div>
28868
+ <p class="muted">Agents and programs use secrets by name. The values stay in the vault, so SLICC never sees or shows them.</p>
28869
+ ${this.#feedback()}
28870
+ ${this.failure ? b`<div class="error" role="alert" data-error="vault"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${this.failure}</span></div>` : A}
28871
+ ${status2?.waiting.length ? b`<h3>Waiting for you</h3>${status2.waiting.map((request2) => this.#request(request2))}` : A}
28872
+ ${notices.map((notice) => this.#notice(vault, notice, devices))}
28873
+ ${!current ? b`<p class="muted" data-loading>Loading secrets…</p>` : current.secrets.length ? this.#secrets(current, entries, devices) : b`<p data-empty>No secrets yet. When an agent or program needs one, it asks here, and you enter the value in the vault.</p>`}
28874
+ <p class="muted" data-policy>Rules live in <code>/etc/fnox/config.toml</code> and <code>/etc/slicc/secrets-policy.toml</code>. Change them with sudo; SLICC asks you before every change.</p>
28875
+ <div class="row">
28876
+ ${this.#button({ intent: { kind: "import" }, label: "Import\u2026", size: "s" })}
28877
+ ${this.#button({ intent: { kind: "export" }, label: "Export\u2026", size: "s" })}
28878
+ </div>
28879
+ ${current ? this.#audit(vault, current, entries, devices) : A}
28880
+ </section>`;
28881
+ }
28882
+ };
28883
+
27918
28884
  // src/app/settings.ts
27919
28885
  var accountStatus = {
27920
28886
  connected: ["positive", "Connected"],
@@ -28272,6 +29238,7 @@ var SliccSettings = class extends ModelElement {
28272
29238
  <div class="accounts">${providers.map((account) => this.#account(account))}</div>
28273
29239
  ${servers.length ? b`<h2>MCP servers</h2>
28274
29240
  <div class="accounts" data-group="mcp">${servers.map((account) => this.#account(account))}</div>` : A}
29241
+ ${this.model?.vault ? b`<slicc-secrets .model=${this.model}></slicc-secrets>` : A}
28275
29242
  </div>`;
28276
29243
  }
28277
29244
  };
@@ -31067,14 +32034,14 @@ var RegexSource = class {
31067
32034
  return CAPTURING_REGEX_SOURCE.test(regexSource);
31068
32035
  }
31069
32036
  static replaceCaptures(regexSource, captureSource, captureIndices) {
31070
- return regexSource.replace(CAPTURING_REGEX_SOURCE, (match, index, commandIndex, command2) => {
32037
+ return regexSource.replace(CAPTURING_REGEX_SOURCE, (match, index, commandIndex, command3) => {
31071
32038
  let capture = captureIndices[parseInt(index || commandIndex, 10)];
31072
32039
  if (capture) {
31073
32040
  let result = captureSource.substring(capture.start, capture.end);
31074
32041
  while (result[0] === ".") {
31075
32042
  result = result.substring(1);
31076
32043
  }
31077
- switch (command2) {
32044
+ switch (command3) {
31078
32045
  case "downcase":
31079
32046
  return result.toLowerCase();
31080
32047
  case "upcase":
@@ -32088,8 +33055,8 @@ var IncludeOnlyRule = class extends Rule {
32088
33055
  }
32089
33056
  }
32090
33057
  collectPatterns(grammar2, out) {
32091
- for (const pattern of this.patterns) {
32092
- const rule = grammar2.getRule(pattern);
33058
+ for (const pattern2 of this.patterns) {
33059
+ const rule = grammar2.getRule(pattern2);
32093
33060
  rule.collectPatterns(grammar2, out);
32094
33061
  }
32095
33062
  }
@@ -32156,8 +33123,8 @@ var BeginEndRule = class extends Rule {
32156
33123
  _getCachedCompiledPatterns(grammar2, endRegexSource) {
32157
33124
  if (!this._cachedCompiledPatterns) {
32158
33125
  this._cachedCompiledPatterns = new RegExpSourceList();
32159
- for (const pattern of this.patterns) {
32160
- const rule = grammar2.getRule(pattern);
33126
+ for (const pattern2 of this.patterns) {
33127
+ const rule = grammar2.getRule(pattern2);
32161
33128
  rule.collectPatterns(grammar2, this._cachedCompiledPatterns);
32162
33129
  }
32163
33130
  if (this.applyEndPatternLast) {
@@ -32229,8 +33196,8 @@ var BeginWhileRule = class extends Rule {
32229
33196
  _getCachedCompiledPatterns(grammar2) {
32230
33197
  if (!this._cachedCompiledPatterns) {
32231
33198
  this._cachedCompiledPatterns = new RegExpSourceList();
32232
- for (const pattern of this.patterns) {
32233
- const rule = grammar2.getRule(pattern);
33199
+ for (const pattern2 of this.patterns) {
33200
+ const rule = grammar2.getRule(pattern2);
32234
33201
  rule.collectPatterns(grammar2, this._cachedCompiledPatterns);
32235
33202
  }
32236
33203
  }
@@ -32351,14 +33318,14 @@ var RuleFactory = class _RuleFactory {
32351
33318
  let r39 = [];
32352
33319
  if (patterns) {
32353
33320
  for (let i25 = 0, len = patterns.length; i25 < len; i25++) {
32354
- const pattern = patterns[i25];
33321
+ const pattern2 = patterns[i25];
32355
33322
  let ruleId = -1;
32356
- if (pattern.include) {
32357
- const reference2 = parseInclude(pattern.include);
33323
+ if (pattern2.include) {
33324
+ const reference2 = parseInclude(pattern2.include);
32358
33325
  switch (reference2.kind) {
32359
33326
  case 0:
32360
33327
  case 1:
32361
- ruleId = _RuleFactory.getCompiledRuleId(repository[pattern.include], helper, repository);
33328
+ ruleId = _RuleFactory.getCompiledRuleId(repository[pattern2.include], helper, repository);
32362
33329
  break;
32363
33330
  case 2:
32364
33331
  let localIncludedRule = repository[reference2.ruleName];
@@ -32387,7 +33354,7 @@ var RuleFactory = class _RuleFactory {
32387
33354
  break;
32388
33355
  }
32389
33356
  } else {
32390
- ruleId = _RuleFactory.getCompiledRuleId(pattern, helper, repository);
33357
+ ruleId = _RuleFactory.getCompiledRuleId(pattern2, helper, repository);
32391
33358
  }
32392
33359
  if (ruleId !== -1) {
32393
33360
  const rule = helper.getRule(ruleId);
@@ -37230,13 +38197,13 @@ function splitTokens(tokens, breakpoints) {
37230
38197
  });
37231
38198
  });
37232
38199
  }
37233
- function flatTokenVariants(merged, variantsOrder, cssVariablePrefix, defaultColor, colorsRendering = "css-vars") {
38200
+ function flatTokenVariants(merged2, variantsOrder, cssVariablePrefix, defaultColor, colorsRendering = "css-vars") {
37234
38201
  const token2 = {
37235
- content: merged.content,
37236
- explanation: merged.explanation,
37237
- offset: merged.offset
38202
+ content: merged2.content,
38203
+ explanation: merged2.explanation,
38204
+ offset: merged2.offset
37238
38205
  };
37239
- const styles = variantsOrder.map((t88) => getTokenStyleObject(merged.variants[t88]));
38206
+ const styles = variantsOrder.map((t88) => getTokenStyleObject(merged2.variants[t88]));
37240
38207
  const styleKeys = new Set(styles.flatMap((t88) => Object.keys(t88)));
37241
38208
  const mergedStyles = {};
37242
38209
  const varKey = (idx, key2) => {
@@ -39346,25 +40313,25 @@ var namedCaptureDelim = r35`\(\?<(?![=!])(?<captureName>[^>]+)>`;
39346
40313
  var captureDelim = r35`${namedCaptureDelim}|(?<unnamed>\()(?!\?)`;
39347
40314
  var token = new RegExp(r35`${namedCaptureDelim}|${recursiveToken}|\(\?|\\?.`, "gsu");
39348
40315
  var overlappingRecursionMsg = "Cannot use multiple overlapping recursions";
39349
- function recursion(pattern, data) {
40316
+ function recursion(pattern2, data) {
39350
40317
  const { hiddenCaptures, mode } = {
39351
40318
  hiddenCaptures: [],
39352
40319
  mode: "plugin",
39353
40320
  ...data
39354
40321
  };
39355
40322
  let captureTransfers = data?.captureTransfers ?? /* @__PURE__ */ new Map();
39356
- if (!new RegExp(recursiveToken, "su").test(pattern)) {
40323
+ if (!new RegExp(recursiveToken, "su").test(pattern2)) {
39357
40324
  return {
39358
- pattern,
40325
+ pattern: pattern2,
39359
40326
  captureTransfers,
39360
40327
  hiddenCaptures
39361
40328
  };
39362
40329
  }
39363
- if (mode === "plugin" && hasUnescaped(pattern, r35`\(\?\(DEFINE\)`, Context.DEFAULT)) {
40330
+ if (mode === "plugin" && hasUnescaped(pattern2, r35`\(\?\(DEFINE\)`, Context.DEFAULT)) {
39364
40331
  throw new Error("DEFINE groups cannot be used with recursion");
39365
40332
  }
39366
40333
  const addedHiddenCaptures = [];
39367
- const hasNumberedBackref = hasUnescaped(pattern, r35`\\[1-9]`, Context.DEFAULT);
40334
+ const hasNumberedBackref = hasUnescaped(pattern2, r35`\\[1-9]`, Context.DEFAULT);
39368
40335
  const groupContentsStartPos = /* @__PURE__ */ new Map();
39369
40336
  const openGroups = [];
39370
40337
  let hasRecursed = false;
@@ -39372,7 +40339,7 @@ function recursion(pattern, data) {
39372
40339
  let numCapturesPassed = 0;
39373
40340
  let match;
39374
40341
  token.lastIndex = 0;
39375
- while (match = token.exec(pattern)) {
40342
+ while (match = token.exec(pattern2)) {
39376
40343
  const { 0: m15, groups: { captureName, rDepth, gRNameOrNum, gRDepth } } = match;
39377
40344
  if (m15 === "[") {
39378
40345
  numCharClassesOpen++;
@@ -39389,13 +40356,13 @@ function recursion(pattern, data) {
39389
40356
  `${mode === "external" ? "Backrefs" : "Numbered backrefs"} cannot be used with global recursion`
39390
40357
  );
39391
40358
  }
39392
- const left = pattern.slice(0, match.index);
39393
- const right = pattern.slice(token.lastIndex);
40359
+ const left = pattern2.slice(0, match.index);
40360
+ const right = pattern2.slice(token.lastIndex);
39394
40361
  if (hasUnescaped(right, recursiveToken, Context.DEFAULT)) {
39395
40362
  throw new Error(overlappingRecursionMsg);
39396
40363
  }
39397
40364
  const reps = +rDepth - 1;
39398
- pattern = makeRecursive(
40365
+ pattern2 = makeRecursive(
39399
40366
  left,
39400
40367
  right,
39401
40368
  reps,
@@ -39429,7 +40396,7 @@ function recursion(pattern, data) {
39429
40396
  throw new Error(r35`Recursive \g cannot be used outside the referenced group "${mode === "external" ? gRNameOrNum : r35`\g<${gRNameOrNum}&R=${gRDepth}>`}"`);
39430
40397
  }
39431
40398
  const startPos = groupContentsStartPos.get(gRNameOrNum);
39432
- const groupContents = getGroupContents(pattern, startPos);
40399
+ const groupContents = getGroupContents(pattern2, startPos);
39433
40400
  if (hasNumberedBackref && hasUnescaped(groupContents, r35`${namedCaptureDelim}|\((?!\?)`, Context.DEFAULT)) {
39434
40401
  throw new Error(
39435
40402
  // When used in `external` mode by transpilers other than Regex+, backrefs might have
@@ -39437,11 +40404,11 @@ function recursion(pattern, data) {
39437
40404
  `${mode === "external" ? "Backrefs" : "Numbered backrefs"} cannot be used with recursion of capturing groups`
39438
40405
  );
39439
40406
  }
39440
- const groupContentsLeft = pattern.slice(startPos, match.index);
40407
+ const groupContentsLeft = pattern2.slice(startPos, match.index);
39441
40408
  const groupContentsRight = groupContents.slice(groupContentsLeft.length + m15.length);
39442
40409
  const numAddedHiddenCapturesPreExpansion = addedHiddenCaptures.length;
39443
40410
  const reps = +gRDepth - 1;
39444
- const expansion = makeRecursive(
40411
+ const expansion2 = makeRecursive(
39445
40412
  groupContentsLeft,
39446
40413
  groupContentsRight,
39447
40414
  reps,
@@ -39458,10 +40425,10 @@ function recursion(pattern, data) {
39458
40425
  numAddedHiddenCapturesPreExpansion,
39459
40426
  numCapturesPassed
39460
40427
  );
39461
- const pre = pattern.slice(0, startPos);
39462
- const post = pattern.slice(startPos + groupContents.length);
39463
- pattern = `${pre}${expansion}${post}`;
39464
- token.lastIndex += expansion.length - m15.length - groupContentsLeft.length - groupContentsRight.length;
40428
+ const pre = pattern2.slice(0, startPos);
40429
+ const post = pattern2.slice(startPos + groupContents.length);
40430
+ pattern2 = `${pre}${expansion2}${post}`;
40431
+ token.lastIndex += expansion2.length - m15.length - groupContentsLeft.length - groupContentsRight.length;
39465
40432
  openGroups.forEach((g7) => g7.hasRecursedWithin = true);
39466
40433
  hasRecursed = true;
39467
40434
  } else if (captureName) {
@@ -39488,7 +40455,7 @@ function recursion(pattern, data) {
39488
40455
  }
39489
40456
  hiddenCaptures.push(...addedHiddenCaptures);
39490
40457
  return {
39491
- pattern,
40458
+ pattern: pattern2,
39492
40459
  captureTransfers,
39493
40460
  hiddenCaptures
39494
40461
  };
@@ -39519,14 +40486,14 @@ function makeRecursive(left, right, reps, isSubpattern, hiddenCaptures, addedHid
39519
40486
  ];
39520
40487
  return `${left}${repeatWithDepth(`(?:${left}`, "forward", ...rest)}(?:)${repeatWithDepth(`${right})`, "backward", ...rest)}${right}`;
39521
40488
  }
39522
- function repeatWithDepth(pattern, direction, reps, namesInRecursed, hiddenCaptures, addedHiddenCaptures, numCapturesPassed) {
40489
+ function repeatWithDepth(pattern2, direction, reps, namesInRecursed, hiddenCaptures, addedHiddenCaptures, numCapturesPassed) {
39523
40490
  const startNum = 2;
39524
40491
  const getDepthNum = (i25) => direction === "forward" ? i25 + startNum : reps - i25 + startNum - 1;
39525
40492
  let result = "";
39526
40493
  for (let i25 = 0; i25 < reps; i25++) {
39527
40494
  const depthNum = getDepthNum(i25);
39528
40495
  result += replaceUnescaped(
39529
- pattern,
40496
+ pattern2,
39530
40497
  r35`${captureDelim}|\\k<(?<backref>[^>]+)>`,
39531
40498
  ({ 0: m15, groups: { captureName, unnamed, backref } }) => {
39532
40499
  if (backref && namesInRecursed && !namesInRecursed.has(backref)) {
@@ -40609,8 +41576,8 @@ function isLoneGLookaround(node, options) {
40609
41576
  function isValidJsGroupName(name) {
40610
41577
  return /^[$_\p{IDS}][$\u200C\u200D\p{IDC}]*$/u.test(name);
40611
41578
  }
40612
- function parseFragment(pattern, options) {
40613
- const ast = J2(pattern, {
41579
+ function parseFragment(pattern2, options) {
41580
+ const ast = J2(pattern2, {
40614
41581
  ...options,
40615
41582
  // Providing a custom set of Unicode property names avoids converting some JS Unicode
40616
41583
  // properties (ex: `\p{Alpha}`) to Onig POSIX classes
@@ -41206,13 +42173,13 @@ var EmulatedRegExp = class _EmulatedRegExp extends RegExp {
41206
42173
  @param {EmulatedRegExp} pattern
41207
42174
  @param {string} [flags]
41208
42175
  */
41209
- constructor(pattern, flags, options) {
42176
+ constructor(pattern2, flags, options) {
41210
42177
  const lazyCompile = !!options?.lazyCompile;
41211
- if (pattern instanceof RegExp) {
42178
+ if (pattern2 instanceof RegExp) {
41212
42179
  if (options) {
41213
42180
  throw new Error("Cannot provide options when copying a regexp");
41214
42181
  }
41215
- const re3 = pattern;
42182
+ const re3 = pattern2;
41216
42183
  super(re3, flags);
41217
42184
  this.#pattern = re3.source;
41218
42185
  if (re3 instanceof _EmulatedRegExp) {
@@ -41228,8 +42195,8 @@ var EmulatedRegExp = class _EmulatedRegExp extends RegExp {
41228
42195
  transfers: [],
41229
42196
  ...options
41230
42197
  };
41231
- super(lazyCompile ? "" : pattern, flags);
41232
- this.#pattern = pattern;
42198
+ super(lazyCompile ? "" : pattern2, flags);
42199
+ this.#pattern = pattern2;
41233
42200
  this.#captureMap = createCaptureMap(opts.hiddenCaptures, opts.transfers);
41234
42201
  this.#strategy = opts.strategy;
41235
42202
  this.rawOptions = options ?? {};
@@ -41355,13 +42322,13 @@ function createCaptureMap(hiddenCaptures, transfers) {
41355
42322
  }
41356
42323
  return captureMap;
41357
42324
  }
41358
- function createNameMap(pattern) {
42325
+ function createNameMap(pattern2) {
41359
42326
  const re3 = /(?<capture>\((?:\?<(?![=!])(?<name>[^>]+)>|(?!\?)))|\\?./gsu;
41360
42327
  const map = /* @__PURE__ */ new Map();
41361
42328
  let numCharClassesOpen = 0;
41362
42329
  let numCaptures = 0;
41363
42330
  let match;
41364
- while (match = re3.exec(pattern)) {
42331
+ while (match = re3.exec(pattern2)) {
41365
42332
  const { 0: m15, groups: { capture, name } } = match;
41366
42333
  if (m15 === "[") {
41367
42334
  numCharClassesOpen++;
@@ -41378,16 +42345,16 @@ function createNameMap(pattern) {
41378
42345
  }
41379
42346
  return map;
41380
42347
  }
41381
- function toRegExp(pattern, options) {
41382
- const d22 = toRegExpDetails(pattern, options);
42348
+ function toRegExp(pattern2, options) {
42349
+ const d22 = toRegExpDetails(pattern2, options);
41383
42350
  if (d22.options) {
41384
42351
  return new EmulatedRegExp(d22.pattern, d22.flags, d22.options);
41385
42352
  }
41386
42353
  return new RegExp(d22.pattern, d22.flags);
41387
42354
  }
41388
- function toRegExpDetails(pattern, options) {
42355
+ function toRegExpDetails(pattern2, options) {
41389
42356
  const opts = getOptions(options);
41390
- const onigurumaAst = J2(pattern, {
42357
+ const onigurumaAst = J2(pattern2, {
41391
42358
  flags: opts.flags,
41392
42359
  normalizeUnknownPropertyNames: true,
41393
42360
  rules: {
@@ -41440,8 +42407,8 @@ function toRegExpDetails(pattern, options) {
41440
42407
  }
41441
42408
 
41442
42409
  // node_modules/@shikijs/engine-javascript/dist/engine-compile.mjs
41443
- function defaultJavaScriptRegexConstructor(pattern, options) {
41444
- return toRegExp(pattern, {
42410
+ function defaultJavaScriptRegexConstructor(pattern2, options) {
42411
+ return toRegExp(pattern2, {
41445
42412
  global: true,
41446
42413
  hasIndices: true,
41447
42414
  lazyCompileLength: 3e3,
@@ -41461,7 +42428,7 @@ function createJavaScriptRegexEngine(options = {}) {
41461
42428
  cache: /* @__PURE__ */ new Map(),
41462
42429
  ...options
41463
42430
  };
41464
- _options.regexConstructor ||= (pattern) => defaultJavaScriptRegexConstructor(pattern, { target: _options.target });
42431
+ _options.regexConstructor ||= (pattern2) => defaultJavaScriptRegexConstructor(pattern2, { target: _options.target });
41465
42432
  return {
41466
42433
  createScanner(patterns) {
41467
42434
  return new JavaScriptScanner(patterns, _options);
@@ -42791,8 +43758,8 @@ var InteractionManager = class {
42791
43758
  const indexes = this.getLineIndex(point.lineNumber, point.side);
42792
43759
  if (indexes == null) return;
42793
43760
  const lineIndex = this.mode === "diff" ? `${indexes[0]},${indexes[1]}` : `${indexes[0]}`;
42794
- const candidates = this.pre.querySelectorAll(`[data-column-number="${point.lineNumber}"][data-line-index="${lineIndex}"]`);
42795
- for (const element2 of candidates) {
43761
+ const candidates2 = this.pre.querySelectorAll(`[data-column-number="${point.lineNumber}"][data-line-index="${lineIndex}"]`);
43762
+ for (const element2 of candidates2) {
42796
43763
  if (!(element2 instanceof HTMLElement)) continue;
42797
43764
  const target = this.resolvePointerTarget(getElementPath(element2));
42798
43765
  if (!isLinePointerTarget(target)) continue;
@@ -44044,6 +45011,7 @@ var EXTENSION_TO_FILE_FORMAT = {
44044
45011
  mli: "ocaml",
44045
45012
  mll: "ocaml",
44046
45013
  mly: "ocaml",
45014
+ odin: "odin",
44047
45015
  pas: "pascal",
44048
45016
  p: "pascal",
44049
45017
  pl: "prolog",
@@ -44076,6 +45044,17 @@ var EXTENSION_TO_FILE_FORMAT = {
44076
45044
  py: "python",
44077
45045
  pyw: "python",
44078
45046
  pyi: "python",
45047
+ BUCK: "python",
45048
+ BUILD: "python",
45049
+ PACKAGE: "python",
45050
+ WORKSPACE: "python",
45051
+ Tiltfile: "python",
45052
+ bazel: "python",
45053
+ bxl: "python",
45054
+ bzl: "python",
45055
+ bzlmod: "python",
45056
+ star: "python",
45057
+ sky: "python",
44079
45058
  qml: "qml",
44080
45059
  qmldir: "qmldir",
44081
45060
  qss: "qss",
@@ -44186,16 +45165,22 @@ var EXTENSION_TO_FILE_FORMAT = {
44186
45165
  sty: "tex"
44187
45166
  };
44188
45167
  function getFiletypeFromFileName(fileName) {
45168
+ const format = matchFiletypeFromName(fileName);
45169
+ if (format != null) return format;
45170
+ const baseName = fileName.split(/[/\\]/).pop() ?? fileName;
45171
+ return baseName === fileName ? "text" : matchFiletypeFromName(baseName) ?? "text";
45172
+ }
45173
+ function matchFiletypeFromName(fileName) {
44189
45174
  if (CUSTOM_EXTENSION_TO_FILE_FORMAT.has(fileName)) return CUSTOM_EXTENSION_TO_FILE_FORMAT.get(fileName) ?? "text";
44190
- if (EXTENSION_TO_FILE_FORMAT[fileName] != null) return EXTENSION_TO_FILE_FORMAT[fileName];
45175
+ if (Object.hasOwn(EXTENSION_TO_FILE_FORMAT, fileName)) return EXTENSION_TO_FILE_FORMAT[fileName] ?? null;
44191
45176
  const compoundMatch = fileName.match(/\.([^/\\]+\.[^/\\]+)$/);
44192
45177
  if (compoundMatch != null) {
44193
45178
  if (CUSTOM_EXTENSION_TO_FILE_FORMAT.has(compoundMatch[1])) return CUSTOM_EXTENSION_TO_FILE_FORMAT.get(compoundMatch[1]) ?? "text";
44194
- if (EXTENSION_TO_FILE_FORMAT[compoundMatch[1]] != null) return EXTENSION_TO_FILE_FORMAT[compoundMatch[1]] ?? "text";
45179
+ if (Object.hasOwn(EXTENSION_TO_FILE_FORMAT, compoundMatch[1])) return EXTENSION_TO_FILE_FORMAT[compoundMatch[1]] ?? "text";
44195
45180
  }
44196
45181
  const simpleMatch = fileName.match(/\.([^.]+)$/)?.[1] ?? "";
44197
45182
  if (CUSTOM_EXTENSION_TO_FILE_FORMAT.has(simpleMatch)) return CUSTOM_EXTENSION_TO_FILE_FORMAT.get(simpleMatch) ?? "text";
44198
- return EXTENSION_TO_FILE_FORMAT[simpleMatch] ?? "text";
45183
+ return Object.hasOwn(EXTENSION_TO_FILE_FORMAT, simpleMatch) ? EXTENSION_TO_FILE_FORMAT[simpleMatch] ?? "text" : null;
44199
45184
  }
44200
45185
 
44201
45186
  // node_modules/@pierre/diffs/dist/utils/getHighlighterOptions.js
@@ -44561,8 +45546,8 @@ function normalizeThemeColors(theme3) {
44561
45546
  function fill(colors, key2, value) {
44562
45547
  if (value != null && value !== "") colors[key2] = value;
44563
45548
  }
44564
- function firstColor(...candidates) {
44565
- for (const candidate of candidates) if (candidate != null && candidate !== "") return candidate;
45549
+ function firstColor(...candidates2) {
45550
+ for (const candidate of candidates2) if (candidate != null && candidate !== "") return candidate;
44566
45551
  }
44567
45552
  function matchesSurface(color, surface2) {
44568
45553
  return surface2 != null && color.toLowerCase() === surface2.toLowerCase();
@@ -45668,6 +46653,12 @@ function isStyleNode(element2) {
45668
46653
  return typeof tagName === "string" && tagName.toLowerCase() === "style";
45669
46654
  }
45670
46655
 
46656
+ // node_modules/@pierre/diffs/dist/utils/platform.js
46657
+ var _isSafari = void 0;
46658
+ function isSafari() {
46659
+ return _isSafari ??= "safari" in window && "pushNotification" in window.safari || /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
46660
+ }
46661
+
45671
46662
  // node_modules/@pierre/diffs/dist/utils/editSessionAnnotations.js
45672
46663
  function adoptEditSessionAnnotations(annotations, getName, previousSession) {
45673
46664
  const slotNames = /* @__PURE__ */ new Map();
@@ -45759,10 +46750,31 @@ function createHostThemeStyleNode() {
45759
46750
  return node;
45760
46751
  }
45761
46752
 
45762
- // node_modules/@pierre/diffs/dist/utils/platform.js
45763
- var _isSafari = void 0;
45764
- function isSafari() {
45765
- return _isSafari ??= "safari" in window && "pushNotification" in window.safari || /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
46753
+ // node_modules/@pierre/diffs/dist/worker/errors.js
46754
+ var WorkerPoolTerminatedError = class extends Error {
46755
+ constructor() {
46756
+ super("WorkerPoolManager: operation canceled because the pool terminated");
46757
+ this.name = "WorkerPoolTerminatedError";
46758
+ }
46759
+ };
46760
+ var WorkerPoolTaskCanceledError = class extends Error {
46761
+ constructor() {
46762
+ super("WorkerPoolManager: operation canceled before the task completed");
46763
+ this.name = "WorkerPoolTaskCanceledError";
46764
+ }
46765
+ };
46766
+ var WorkerPoolWorkerError = class extends Error {
46767
+ constructor(message, options) {
46768
+ super(message, options);
46769
+ this.name = "WorkerPoolWorkerError";
46770
+ }
46771
+ };
46772
+ var reportedWorkerErrors = /* @__PURE__ */ new WeakSet();
46773
+ function isWorkerPoolCancellation(error) {
46774
+ return error instanceof WorkerPoolTerminatedError || error instanceof WorkerPoolTaskCanceledError;
46775
+ }
46776
+ function isHandledWorkerPoolError(error) {
46777
+ return isWorkerPoolCancellation(error) || error instanceof WorkerPoolWorkerError && reportedWorkerErrors.has(error);
45766
46778
  }
45767
46779
 
45768
46780
  // node_modules/@pierre/diffs/dist/components/web-components.js
@@ -46414,6 +47426,7 @@ var File = class {
46414
47426
  const tokenizeMaxLength = this.options.tokenizeMaxLength ?? 1e5;
46415
47427
  if (this.fileRenderer.getOrCreateLineCache(file).length > tokenizeMaxLength) return;
46416
47428
  await workerManager.primeFileHighlightCache(file).catch((error) => {
47429
+ if (isHandledWorkerPoolError(error)) return;
46417
47430
  console.error(error);
46418
47431
  });
46419
47432
  }
@@ -49285,13 +50298,13 @@ function getChangeIterationRanges(state3, content, diffStyle) {
49285
50298
  }
49286
50299
  if (iterationRanges.length === 0) return iterationRanges;
49287
50300
  iterationRanges.sort((a22, b13) => a22[0] - b13[0]);
49288
- const merged = [iterationRanges[0]];
50301
+ const merged2 = [iterationRanges[0]];
49289
50302
  for (const [start, end] of iterationRanges.slice(1)) {
49290
- const last = merged[merged.length - 1];
50303
+ const last = merged2[merged2.length - 1];
49291
50304
  if (start <= last[1]) last[1] = Math.max(last[1], end);
49292
- else merged.push([start, end]);
50305
+ else merged2.push([start, end]);
49293
50306
  }
49294
- return merged;
50307
+ return merged2;
49295
50308
  }
49296
50309
  function getChangeLineData({ hunkIndex, hunk, collapsedAfter, collapsedBefore, diffStyle, index, unifiedLineIndex, splitLineIndex, additionLineIndex, deletionLineIndex, additionLineNumber, deletionLineNumber, content, isLastContent, unifiedCount, splitCount }) {
49297
50310
  const unifiedDeletionLineIndex = index < content.deletions ? unifiedLineIndex + index : void 0;
@@ -50488,7 +51501,7 @@ var DiffHunksRenderer = class {
50488
51501
  * row set without a line-count change — the host must escalate to a full
50489
51502
  * re-render instead of its cheap refresh path.
50490
51503
  */
50491
- updateRenderCache(dirtyLines, themeType, lineCountChangeInFlight = false) {
51504
+ updateRenderCache(dirtyLines, themeType, lineCountChangeInFlight = false, changedDocumentLines, documentLineCount) {
50492
51505
  this.pendingStructuralRows = void 0;
50493
51506
  const { renderCache } = this;
50494
51507
  if (renderCache == null) return false;
@@ -50499,13 +51512,27 @@ var DiffHunksRenderer = class {
50499
51512
  const pendingStructuralRows = this.pendingStructuralRows = lineCountChangeInFlight ? /* @__PURE__ */ new Map() : void 0;
50500
51513
  const changedAdditionLines = [];
50501
51514
  const previousAdditionLines = /* @__PURE__ */ new Map();
51515
+ if (pendingStructuralRows == null && changedDocumentLines != null) {
51516
+ const additionLineCount = diff.additionLines.length;
51517
+ const canAppendFinalRow = canAppendFinalDocumentRow(diff.additionLines, documentLineCount);
51518
+ for (const [line, text2] of changedDocumentLines) {
51519
+ if (line < 0 || line > additionLineCount || line === additionLineCount && (text2 === "" || !canAppendFinalRow)) continue;
51520
+ const previous = diff.additionLines[line] ?? "";
51521
+ if (previous !== text2) {
51522
+ diff.additionLines[line] = text2;
51523
+ changedAdditionLines.push(line);
51524
+ previousAdditionLines.set(line, previous);
51525
+ if (!dirtyLines.has(line)) hastLines[line] = createPlainAdditionLineElement(line, cleanLastNewline(text2));
51526
+ }
51527
+ }
51528
+ }
50502
51529
  for (const [line, tokens] of dirtyLines) {
50503
51530
  const prevProps = hastLines[line]?.properties ?? {};
50504
51531
  const lineText = tokens.map((a22) => a22[2]).join("");
50505
51532
  const canSyncDiffLine = line < diff.additionLines.length;
50506
51533
  const prevLine = canSyncDiffLine ? diff.additionLines[line] ?? "" : "";
50507
51534
  const prevText = cleanLastNewline(prevLine);
50508
- if (pendingStructuralRows == null && canSyncDiffLine) {
51535
+ if (pendingStructuralRows == null && canSyncDiffLine && changedDocumentLines?.has(line) !== true) {
50509
51536
  diff.additionLines[line] = applyLineTextWithNewline(prevLine, lineText);
50510
51537
  if (prevText !== lineText) {
50511
51538
  changedAdditionLines.push(line);
@@ -51499,6 +52526,9 @@ function realignAdditionHastLines(previousLines, nextLines, hastLines, textDocum
51499
52526
  for (let index = prefix; index < nextLines.length; index++) realigned[index] ??= createPlainAdditionLineElement(index, textDocument.getLineText(index));
51500
52527
  return realigned;
51501
52528
  }
52529
+ function canAppendFinalDocumentRow(additionLines, documentLineCount) {
52530
+ return documentLineCount === additionLines.length + 1 && additionLines.at(-1)?.endsWith("\n") === true;
52531
+ }
51502
52532
  function createPlainAdditionLineElement(lineIndex, lineText, unifiedLineIndex = lineIndex, splitLineIndex = lineIndex) {
51503
52533
  return {
51504
52534
  type: "element",
@@ -51601,6 +52631,7 @@ function getDiffHunksRendererOptions(options) {
51601
52631
  hunkSeparators: typeof options?.hunkSeparators === "function" ? "custom" : options?.hunkSeparators,
51602
52632
  expandUnchanged: options?.expandUnchanged,
51603
52633
  loadDiffFiles: options?.loadDiffFiles,
52634
+ parseDiffOptions: options?.parseDiffOptions,
51604
52635
  collapsedContextThreshold: options?.collapsedContextThreshold,
51605
52636
  lineDiffType: options?.lineDiffType,
51606
52637
  maxLineDiffLength: options?.maxLineDiffLength,
@@ -52593,7 +53624,7 @@ var FileDiff = class {
52593
53624
  this.detachAdditionLines();
52594
53625
  this.hunksRenderer.beginEditSession(editSessionDiff);
52595
53626
  const { shouldRefreshDiffsView, lineCountChangeInFlight } = options;
52596
- if (this.hunksRenderer.updateRenderCache(dirtyLines, themeType, lineCountChangeInFlight)) {
53627
+ if (this.hunksRenderer.updateRenderCache(dirtyLines, themeType, lineCountChangeInFlight, options.changedDocumentLines, options.documentLineCount)) {
52597
53628
  if (this.refreshViewTimeout != null) {
52598
53629
  clearTimeout(this.refreshViewTimeout);
52599
53630
  this.refreshViewTimeout = void 0;
@@ -52740,6 +53771,7 @@ var FileDiff = class {
52740
53771
  const tokenizeMaxLength = this.options.tokenizeMaxLength ?? 1e5;
52741
53772
  if (Math.max(fileDiff.additionLines.length, fileDiff.deletionLines.length) > tokenizeMaxLength) return;
52742
53773
  await workerManager.primeDiffHighlightCache(fileDiff).catch((error) => {
53774
+ if (isHandledWorkerPoolError(error)) return;
52743
53775
  console.error(error);
52744
53776
  });
52745
53777
  }
@@ -54237,9 +55269,9 @@ function disableTextSelection(rootNode = document) {
54237
55269
  }
54238
55270
  function getDockviewTheme(element2) {
54239
55271
  function toClassList(element3) {
54240
- const list4 = [];
54241
- for (let i25 = 0; i25 < element3.classList.length; i25++) list4.push(element3.classList.item(i25));
54242
- return list4;
55272
+ const list5 = [];
55273
+ for (let i25 = 0; i25 < element3.classList.length; i25++) list5.push(element3.classList.item(i25));
55274
+ return list5;
54243
55275
  }
54244
55276
  let theme3 = void 0;
54245
55277
  let parent = element2;
@@ -55443,7 +56475,7 @@ var BranchNode = class BranchNode2 extends CompositeDisposable {
55443
56475
  if (child instanceof BranchNode2) child.proportionalLayout = value;
55444
56476
  });
55445
56477
  }
55446
- constructor(orientation, proportionalLayout, styles, size4, orthogonalSize, disabled, margin, childDescriptors) {
56478
+ constructor(orientation, proportionalLayout, styles, size4, orthogonalSize, disabled2, margin, childDescriptors) {
55447
56479
  super();
55448
56480
  this.orientation = orientation;
55449
56481
  this.styles = styles;
@@ -55486,7 +56518,7 @@ var BranchNode = class BranchNode2 extends CompositeDisposable {
55486
56518
  });
55487
56519
  this.splitview.layout(this.size, this.orthogonalSize);
55488
56520
  }
55489
- this.disabled = disabled;
56521
+ this.disabled = disabled2;
55490
56522
  this.addDisposables(this._onDidChange, this._onDidVisibilityChange, this.splitview.onDidSashEnd(() => {
55491
56523
  this._onDidChange.fire({});
55492
56524
  }));
@@ -65090,9 +66122,9 @@ var ENTERPRISE_MODULE_NAMES = /* @__PURE__ */ new Set([
65090
66122
  var _warnedMissingModule = /* @__PURE__ */ new Set();
65091
66123
  function missingModuleMessage(moduleName, reason) {
65092
66124
  const reasons = reason === void 0 ? [] : [].concat(reason);
65093
- const quoted = reasons.map((r39) => `\`${r39}\``).join(", ");
66125
+ const quoted2 = reasons.map((r39) => `\`${r39}\``).join(", ");
65094
66126
  const requires = reasons.length > 1 ? "require" : "requires";
65095
- const needed = reasons.length ? `${quoted} ${requires} the "${moduleName}" module` : `The "${moduleName}" module is required`;
66127
+ const needed = reasons.length ? `${quoted2} ${requires} the "${moduleName}" module` : `The "${moduleName}" module is required`;
65096
66128
  if (ENTERPRISE_MODULE_NAMES.has(moduleName)) return `dockview: ${needed}, which ships in dockview-enterprise.
65097
66129
 
65098
66130
  npm install dockview-enterprise
@@ -65692,9 +66724,9 @@ var RootDropTargetService = class {
65692
66724
  }
65693
66725
  setOptions(options) {
65694
66726
  if ("dndEdges" in options) {
65695
- const disabled = typeof options.dndEdges === "boolean" && options.dndEdges === false;
65696
- this._html5Target.disabled = disabled;
65697
- this._pointerTarget.disabled = disabled;
66727
+ const disabled2 = typeof options.dndEdges === "boolean" && options.dndEdges === false;
66728
+ this._html5Target.disabled = disabled2;
66729
+ this._pointerTarget.disabled = disabled2;
65698
66730
  }
65699
66731
  if ("dndEdges" in options || "dockToEdgeGroups" in options) {
65700
66732
  const model = resolveRootOverlayModel({
@@ -66073,16 +67105,16 @@ var nextContextMenuItemId = () => `dv-ctx-menu-item-${_nextId++}`;
66073
67105
  function isItemConfig(item2) {
66074
67106
  return typeof item2 === "object";
66075
67107
  }
66076
- function buildItem(label, close, action, disabled) {
67108
+ function buildItem(label, close, action, disabled2) {
66077
67109
  const el = document.createElement("div");
66078
67110
  el.className = "dv-context-menu-item";
66079
67111
  el.setAttribute("role", "menuitem");
66080
- if (disabled) {
67112
+ if (disabled2) {
66081
67113
  el.classList.add("dv-context-menu-item--disabled");
66082
67114
  el.setAttribute("aria-disabled", "true");
66083
67115
  }
66084
67116
  el.textContent = label;
66085
- if (!disabled) el.addEventListener("click", () => {
67117
+ if (!disabled2) el.addEventListener("click", () => {
66086
67118
  action();
66087
67119
  close();
66088
67120
  });
@@ -71284,9 +72316,9 @@ var getBaseFileName = (path) => {
71284
72316
  };
71285
72317
  var getExtensionCandidates = (fileName) => {
71286
72318
  const segments = fileName.toLowerCase().split(".");
71287
- const candidates = [];
71288
- for (let index = 1; index < segments.length; index += 1) candidates.push(segments.slice(index).join("."));
71289
- return candidates;
72319
+ const candidates2 = [];
72320
+ for (let index = 1; index < segments.length; index += 1) candidates2.push(segments.slice(index).join("."));
72321
+ return candidates2;
71290
72322
  };
71291
72323
  function remapEntryToIcon(entry, remappedFrom) {
71292
72324
  if (typeof entry === "string") return {
@@ -77439,8 +78471,8 @@ function getFileTreeRowAriaLabel(row) {
77439
78471
  if (flattenedSegments == null || flattenedSegments.length === 0) return row.name;
77440
78472
  return flattenedSegments.map((segment2) => segment2.name).join(" / ");
77441
78473
  }
77442
- function computeStickyRowsFromCandidates(candidates, scrollTop, itemHeight, totalRowCount) {
77443
- return candidates.map((candidate, slotDepth) => {
78474
+ function computeStickyRowsFromCandidates(candidates2, scrollTop, itemHeight, totalRowCount) {
78475
+ return candidates2.map((candidate, slotDepth) => {
77444
78476
  const defaultTop = slotDepth * itemHeight;
77445
78477
  const nextBoundaryIndex = candidate.subtreeEndIndex + 1;
77446
78478
  if (nextBoundaryIndex >= totalRowCount) return {
@@ -77487,9 +78519,9 @@ function getPointElement(rootNode, clientX, clientY) {
77487
78519
  return element2 instanceof HTMLElement ? element2 : null;
77488
78520
  }
77489
78521
  function getShadowPointElementByGeometry(rootNode, clientX, clientY) {
77490
- const candidates = Array.from(rootNode.querySelectorAll('[data-type="item"], [data-item-flattened-subitem]'));
77491
- for (let index = candidates.length - 1; index >= 0; index--) {
77492
- const candidate = candidates[index];
78522
+ const candidates2 = Array.from(rootNode.querySelectorAll('[data-type="item"], [data-item-flattened-subitem]'));
78523
+ for (let index = candidates2.length - 1; index >= 0; index--) {
78524
+ const candidate = candidates2[index];
77493
78525
  const rect = candidate.getBoundingClientRect();
77494
78526
  if (clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom) return candidate;
77495
78527
  }
@@ -80007,6 +81039,8 @@ var elements = {
80007
81039
  "slicc-network": SliccNetwork,
80008
81040
  "slicc-notices": SliccNotices,
80009
81041
  "slicc-approvals": SliccApprovals,
81042
+ "slicc-secrets": SliccSecrets,
81043
+ "slicc-vault-button": SliccVaultButton,
80010
81044
  "slicc-confirm": SliccConfirm,
80011
81045
  "slicc-prompt": SliccPrompt,
80012
81046
  "slicc-app": SliccApp
@@ -80043,7 +81077,9 @@ export {
80043
81077
  SliccNetwork,
80044
81078
  SliccNotices,
80045
81079
  SliccPrompt,
81080
+ SliccSecrets,
80046
81081
  SliccUpdates,
81082
+ SliccVaultButton,
80047
81083
  confirm,
80048
81084
  define2 as define,
80049
81085
  grammarBase,