@ai-ecoverse/slicc-spectrum 1.60.2 → 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
  }
@@ -13816,37 +13816,37 @@ function coneOf(agents, id) {
13816
13816
  }
13817
13817
  return agent?.kind === "cone" ? agent.id : null;
13818
13818
  }
13819
- function resolve(panels, raw2, reachable, cone) {
13819
+ function resolve(panels, raw2, reachable2, cone) {
13820
13820
  if (/^(file|diff):./.test(raw2)) return raw2;
13821
13821
  if (raw2 === "chat") {
13822
13822
  const agent = cone === void 0 ? panels.active() : cone;
13823
- return reachable.some((info) => info.id === `chat:${agent}`) ? `chat:${agent}` : null;
13823
+ return reachable2.some((info) => info.id === `chat:${agent}`) ? `chat:${agent}` : null;
13824
13824
  }
13825
13825
  if (raw2 === "terminal") {
13826
- const terminals = reachable.filter((info) => info.kind === "terminal");
13826
+ const terminals = reachable2.filter((info) => info.kind === "terminal");
13827
13827
  const picked = terminals.find((info) => info.active) ?? terminals.filter((info) => info.open).at(-1) ?? terminals.at(-1);
13828
13828
  return picked?.id ?? null;
13829
13829
  }
13830
- if (reachable.some((info) => info.id === raw2)) return raw2;
13830
+ if (reachable2.some((info) => info.id === raw2)) return raw2;
13831
13831
  const name = raw2.startsWith("sprinkle:") ? raw2.slice(9) : raw2;
13832
- const sprinkles = reachable.filter((info) => info.kind === "sprinkle");
13832
+ const sprinkles = reachable2.filter((info) => info.kind === "sprinkle");
13833
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);
13834
13834
  return found?.id ?? null;
13835
13835
  }
13836
13836
  function state(info) {
13837
- const shown2 = info.active ? "active" : info.visible ? "shown" : info.open ? "open" : "closed";
13838
- 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;
13839
13839
  }
13840
13840
  function table(rows) {
13841
13841
  const lines = [["ID", "TITLE", "STATE"], ...rows.map((row) => [row.id, row.title, state(row)])];
13842
13842
  const widths = [0, 1].map((column) => Math.max(...lines.map((line) => line[column].length)));
13843
- 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");
13844
13844
  }
13845
13845
  var done = (result, note = "") => result.code === 0 ? { stdout: `${result.message}${note}
13846
13846
  `, stderr: "", code: 0 } : { stdout: "", stderr: `panel: ${result.message}
13847
13847
  `, code: result.code };
13848
13848
  function act(scope, parsed) {
13849
- const { panels, reachable, cone } = scope;
13849
+ const { panels, reachable: reachable2, cone } = scope;
13850
13850
  if (parsed.verb === "reset") {
13851
13851
  const recent = scope.agent ? panels.recentClose() : null;
13852
13852
  if (recent)
@@ -13856,11 +13856,11 @@ function act(scope, parsed) {
13856
13856
  return done(panels.reset());
13857
13857
  }
13858
13858
  const raw2 = parsed.id;
13859
- const id = resolve(panels, raw2, reachable, cone);
13859
+ const id = resolve(panels, raw2, reachable2, cone);
13860
13860
  if (!id) return done(unknown(raw2));
13861
13861
  let place3 = parsed.place;
13862
13862
  if (typeof place3 === "object") {
13863
- const ref = resolve(panels, place3.ref, reachable, cone);
13863
+ const ref = resolve(panels, place3.ref, reachable2, cone);
13864
13864
  if (!ref) return done(unknown(place3.ref));
13865
13865
  place3 = { ...place3, ref };
13866
13866
  }
@@ -13895,12 +13895,12 @@ ${help}`, code: 2 };
13895
13895
  const agents = panels.agents();
13896
13896
  const scoped2 = caller.agentId !== null || !!caller.cone;
13897
13897
  const cone = scoped2 ? caller.cone ?? coneOf(agents, caller.agentId) : void 0;
13898
- const reachable = panels.list().filter(
13898
+ const reachable2 = panels.list().filter(
13899
13899
  (info) => !scoped2 || info.agent === null || !!cone && coneOf(agents, info.agent) === cone
13900
13900
  );
13901
13901
  if (parsed.verb !== "list")
13902
- return act({ panels, agent: caller.agentId !== null, cone, reachable }, parsed);
13903
- 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);
13904
13904
  return { stdout: `${body4}
13905
13905
  `, stderr: "", code: 0 };
13906
13906
  }
@@ -20681,12 +20681,12 @@ function user(message) {
20681
20681
  const tag3 = delivered === "steer" || delivered === "follow-up" ? deliveries[delivered] : null;
20682
20682
  const agent = message.origin === "agent";
20683
20683
  const name = message.from ?? "an agent";
20684
- const who = agent ? name : message.from ? `From ${message.from}` : "You";
20684
+ const who2 = agent ? name : message.from ? `From ${message.from}` : "You";
20685
20685
  const label = agent ? `Request from ${name}` : message.from ? `Message from ${message.from}` : "Your message";
20686
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"}>
20687
20687
  <div class="bubble">
20688
20688
  <div class="meta">
20689
- <span class="who">${who}</span><span>${time(message.createdAt)}</span>
20689
+ <span class="who">${who2}</span><span>${time(message.createdAt)}</span>
20690
20690
  ${agent ? b`<swc-badge class="origin" size="s" variant="indigo">Agent</swc-badge>` : A}
20691
20691
  ${tag3 ? b`<swc-badge class="tag" size="s" variant=${delivered === "steer" ? "notice" : "informative"} subtle>${tag3}</swc-badge>` : A}
20692
20692
  </div>
@@ -21945,8 +21945,8 @@ var SliccUpdates = class extends ModelElement {
21945
21945
  #checked(at) {
21946
21946
  if (at === null) return b`<span>Not checked yet</span>`;
21947
21947
  const date = new Date(at);
21948
- const shown2 = date.toLocaleString("en-GB", { dateStyle: "medium", timeStyle: "short" });
21949
- 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>`;
21950
21950
  }
21951
21951
  #item(item2) {
21952
21952
  const [variant, state3] = states[item2.state];
@@ -22011,14 +22011,14 @@ var SliccUpdates = class extends ModelElement {
22011
22011
  #package(item2, all2, act2) {
22012
22012
  const light = packageLight(item2);
22013
22013
  const busy = this.busy.get(item2.id);
22014
- const shown2 = busy ? [busy] : act2 ? item2.actions : [];
22014
+ const shown3 = busy ? [busy] : act2 ? item2.actions : [];
22015
22015
  const error = this.problems.get(item2.id) ?? item2.error;
22016
22016
  return b`<li data-id=${item2.id} data-state=${item2.state}>
22017
22017
  <div class="name">
22018
22018
  <h3 title=${item2.package}>${item2.label}</h3>
22019
22019
  ${light ? b`<swc-status-light size="s" variant=${light[0]}>${light[1]}</swc-status-light>` : A}
22020
22020
  </div>
22021
- <div class="buttons">${shown2.map(
22021
+ <div class="buttons">${shown3.map(
22022
22022
  (action) => b`<swc-button
22023
22023
  size="s"
22024
22024
  variant="secondary"
@@ -22035,7 +22035,7 @@ var SliccUpdates = class extends ModelElement {
22035
22035
  (warning) => b`<span><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle>${warning}</span>`
22036
22036
  )}</p>` : A}
22037
22037
  ${item2.commands.length ? b`<p class="commands"><span class="hidden">Commands: </span>${item2.commands.map(
22038
- (command2) => b`<code>${command2}</code>`
22038
+ (command3) => b`<code>${command3}</code>`
22039
22039
  )}</p>` : A}
22040
22040
  ${this.#packageProgress(item2)}
22041
22041
  ${error ? b`<div class="error" role="alert" data-error>
@@ -22716,9 +22716,9 @@ var SliccApp = class extends ThemedElement {
22716
22716
  if (!model) return [];
22717
22717
  const selected = this.#cone();
22718
22718
  const cones = model.agent.list().filter((agent) => agent.kind === "cone");
22719
- const list4 = model.sprinkles.list();
22720
- const order = [selected, ...cones.map((cone) => cone.id), ...list4.map((item2) => item2.agentId)];
22721
- 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) => {
22722
22722
  const cone = cones.find((agent) => agent.id === sprinkle.agentId && agent.id !== selected);
22723
22723
  return cone ? { ...sprinkle, title: `${cone.name} / ${sprinkle.title}` } : sprinkle;
22724
22724
  }).sort((a22, b13) => order.indexOf(a22.agentId) - order.indexOf(b13.agentId));
@@ -22774,11 +22774,11 @@ var SliccApp = class extends ThemedElement {
22774
22774
  #rail(items, side) {
22775
22775
  const primary = side === "bottom" ? items.filter((item2) => phoneRail.includes(item2.id)) : items;
22776
22776
  const fit = primary.length < items.length ? Math.min(primary.length, railCapacity(this.#width, Infinity)) : railCapacity(this.#width, items.length);
22777
- const shown2 = side === "bottom" ? primary.slice(0, fit) : items;
22778
- const grouped = new Set(shown2.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}>${shown2.map(
22780
- (item2, index) => b`${grouped && index && shown2[index - 1].cone !== item2.cone ? b`<span class="cones" aria-hidden="true"></span>` : A}${this.#railButton(item2, side)}`
22781
- )}${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>`;
22782
22782
  }
22783
22783
  #rails() {
22784
22784
  const dock = this.renderRoot.querySelector("slicc-dock");
@@ -23021,6 +23021,202 @@ function sudo(approval) {
23021
23021
  const as = approval.target && approval.target !== "root" ? `-u ${approval.target} ` : "";
23022
23022
  return `sudo ${as}${approval.command}`;
23023
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
+ }
23024
23220
  function waiting(model, now) {
23025
23221
  return [...model?.approvals?.list() ?? []].filter((approval) => approval.expiresAt > now).sort((a22, b13) => a22.createdAt - b13.createdAt);
23026
23222
  }
@@ -23086,6 +23282,27 @@ var SliccApprovals = class extends ModelElement {
23086
23282
  margin-block-start: var(--swc-spacing-300);
23087
23283
  font-variant-numeric: tabular-nums;
23088
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
+ }
23089
23306
  .problem {
23090
23307
  display: flex;
23091
23308
  align-items: flex-start;
@@ -23140,6 +23357,7 @@ var SliccApprovals = class extends ModelElement {
23140
23357
  async decide(approval, decision) {
23141
23358
  const port = this.model?.approvals;
23142
23359
  if (!port?.decide || this.busy || decision !== "deny" && !this.#armed) return;
23360
+ if (decision === "always" && secretRules(approval)) return;
23143
23361
  this.busy = decision;
23144
23362
  this.failure = null;
23145
23363
  this.#pending.add(approval.id);
@@ -23161,15 +23379,15 @@ var SliccApprovals = class extends ModelElement {
23161
23379
  }
23162
23380
  willUpdate(changed) {
23163
23381
  super.willUpdate(changed);
23164
- const list4 = this.#current();
23382
+ const list5 = this.#current();
23165
23383
  const showing = this.#showing;
23166
23384
  if (showing && this.#pending.has(showing.id)) return;
23167
- 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)) {
23168
23386
  const reason = this.#ended.get(showing.id) ?? (showing.expiresAt <= this.now ? "expired" : "answered");
23169
23387
  if (reason !== "withdrawn") this.gone = { approval: showing, reason: gone(reason) };
23170
23388
  }
23171
23389
  if (this.gone) return;
23172
- const next = list4[0] ?? null;
23390
+ const next = list5[0] ?? null;
23173
23391
  if (next?.id === showing?.id) return;
23174
23392
  this.#showing = next;
23175
23393
  clearTimeout(this.#arming);
@@ -23240,10 +23458,12 @@ var SliccApprovals = class extends ModelElement {
23240
23458
  );
23241
23459
  }
23242
23460
  const held = this.#showing && this.#pending.has(this.#showing.id) ? this.#showing : null;
23243
- const list4 = this.#current();
23244
- 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;
23245
23463
  if (!approval) return A;
23246
23464
  const failure2 = this.failure?.id === approval.id ? this.failure.message : "";
23465
+ const rules = secretRules(approval);
23466
+ const always = approval.always && !rules;
23247
23467
  return this.#dialog(
23248
23468
  approval,
23249
23469
  approval.id,
@@ -23251,12 +23471,13 @@ var SliccApprovals = class extends ModelElement {
23251
23471
  <h2 id="title" slot="heading">Allow sudo?</h2>
23252
23472
  ${this.#request(approval)}
23253
23473
  ${approval.cwd ? b`<p class="cwd">In ${approval.cwd}</p>` : A}
23254
- ${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}
23255
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}
23256
23477
  <p class="timer" data-timer>Denied automatically in ${countdown(approval.expiresAt - this.now)}.</p>
23257
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}
23258
23479
  ${this.#button(approval, "deny", "Deny", "secondary")}
23259
- ${approval.always ? this.#button(approval, "always", "Always allow", "secondary") : A}
23480
+ ${always ? this.#button(approval, "always", "Always allow", "secondary") : A}
23260
23481
  ${this.#button(approval, "once", "Allow once", "accent")}
23261
23482
  </sp-alert-dialog>`
23262
23483
  );
@@ -24168,9 +24389,9 @@ var SliccBrowser = class extends ModelElement {
24168
24389
  if (visible2 && tab && !this.#ticker)
24169
24390
  this.#ticker = setInterval(() => this.requestUpdate(), 1e3);
24170
24391
  if (!(visible2 && tab)) this.#still();
24171
- const list4 = this.renderRoot.querySelector(".actions:not(.last)");
24172
- const count = list4?.childElementCount ?? 0;
24173
- 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;
24174
24395
  this.#count = count;
24175
24396
  }
24176
24397
  #still() {
@@ -25064,18 +25285,18 @@ function failedTurns(messages, messageId) {
25064
25285
  }
25065
25286
  return ids;
25066
25287
  }
25067
- var touched = false;
25288
+ var touched2 = false;
25068
25289
  document.addEventListener(
25069
25290
  "pointerdown",
25070
25291
  (event2) => {
25071
- touched = event2.pointerType !== "mouse";
25292
+ touched2 = event2.pointerType !== "mouse";
25072
25293
  },
25073
25294
  true
25074
25295
  );
25075
25296
  document.addEventListener(
25076
25297
  "keydown",
25077
25298
  () => {
25078
- touched = false;
25299
+ touched2 = false;
25079
25300
  },
25080
25301
  true
25081
25302
  );
@@ -25251,7 +25472,7 @@ var SliccChat = class extends ThemedElement {
25251
25472
  this.#observe();
25252
25473
  }
25253
25474
  focus() {
25254
- if (!touched && globalThis.matchMedia("(pointer: fine)").matches)
25475
+ if (!touched2 && globalThis.matchMedia("(pointer: fine)").matches)
25255
25476
  this.focusOn("slicc-composer", ".log");
25256
25477
  else this.focusOn(".log");
25257
25478
  }
@@ -26257,7 +26478,7 @@ e25("swc-icon-clock", o84);
26257
26478
  // src/app/composer.ts
26258
26479
  var limit = 25 * 1024 * 1024;
26259
26480
  function available(model) {
26260
- return commands2.filter((command2) => command2.available?.(model) ?? true);
26481
+ return commands2.filter((command3) => command3.available?.(model) ?? true);
26261
26482
  }
26262
26483
  var commands2 = [
26263
26484
  {
@@ -26336,8 +26557,8 @@ function trigger(text2, caret) {
26336
26557
  const argument = before.match(/^\/(\w+)\s+(\S*)$/);
26337
26558
  if (argument)
26338
26559
  return { kind: "argument", start: before.length - argument[2].length, query: argument[2] };
26339
- const command2 = before.match(/^\/([\w:-]*)$/);
26340
- 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] };
26341
26562
  const mention = before.match(/(^|\s)@([\w./-]*)$/);
26342
26563
  if (mention)
26343
26564
  return { kind: "mention", start: before.length - mention[2].length - 1, query: mention[2] };
@@ -26644,14 +26865,14 @@ var SliccComposer = class extends ModelElement {
26644
26865
  const text2 = this.value.trim();
26645
26866
  const attachments = this.attachments.filter((attachment2) => !attachment2.error);
26646
26867
  if (!model || !text2 && attachments.length === 0 || this.#held()) return;
26647
- const command2 = text2.match(/^\/(\w+)(?:\s+(.*))?$/);
26648
- 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]);
26649
26870
  this.value = "";
26650
26871
  this.attachments = [];
26651
26872
  this.popup = null;
26652
26873
  this.#history = -1;
26653
26874
  if (known) {
26654
- known.run(model, this.#agent, (command2[2] ?? "").trim());
26875
+ known.run(model, this.#agent, (command3[2] ?? "").trim());
26655
26876
  return;
26656
26877
  }
26657
26878
  const busy = this.#busy();
@@ -26739,22 +26960,22 @@ var SliccComposer = class extends ModelElement {
26739
26960
  const model = this.model;
26740
26961
  if (kind === "command") {
26741
26962
  const usable = available(model);
26742
- const builtins = usable.map((command2) => ({
26743
- value: command2.name,
26744
- label: `/${command2.name}`,
26745
- detail: command2.detail
26963
+ const builtins = usable.map((command3) => ({
26964
+ value: command3.name,
26965
+ label: `/${command3.name}`,
26966
+ detail: command3.detail
26746
26967
  }));
26747
- const own4 = model.agent.commands(this.#agent).filter((command2) => !usable.some((builtin) => builtin.name === command2.name)).map((command2) => ({
26748
- value: command2.name,
26749
- label: `/${command2.name}`,
26750
- detail: command2.description,
26751
- 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"
26752
26973
  })).sort((a22, b13) => a22.group === b13.group ? 0 : a22.group === "Prompts" ? -1 : 1);
26753
26974
  return filter([...builtins, ...own4], query, Number.POSITIVE_INFINITY);
26754
26975
  }
26755
26976
  if (kind === "argument") {
26756
26977
  const name = this.value.match(/^\/(\w+)/)?.[1];
26757
- return filter(commands2.find((command2) => command2.name === name)?.args?.(model) ?? [], query);
26978
+ return filter(commands2.find((command3) => command3.name === name)?.args?.(model) ?? [], query);
26758
26979
  }
26759
26980
  const files = this.#paths.map((path) => ({
26760
26981
  value: path,
@@ -26774,7 +26995,7 @@ var SliccComposer = class extends ModelElement {
26774
26995
  if (this.popup?.kind === "file" || this.popup?.kind === "secret") return;
26775
26996
  const found = trigger(this.value, area.selectionStart ?? this.value.length);
26776
26997
  const name = this.value.match(/^\/(\w+)/)?.[1];
26777
- const takes = commands2.some((command2) => command2.name === name && command2.args);
26998
+ const takes = commands2.some((command3) => command3.name === name && command3.args);
26778
26999
  this.popup = found && (found.kind !== "argument" || takes) ? { ...found, items: this.#choices(found.kind, found.query), index: 0 } : null;
26779
27000
  }
26780
27001
  openPopup(kind) {
@@ -27368,8 +27589,8 @@ var SliccMemory = class extends ModelElement {
27368
27589
  const scopes = this.#scopes;
27369
27590
  const scope = this.scope;
27370
27591
  const option2 = (item2) => b`<sp-menu-item value=${item2.id}>${item2.label}</sp-menu-item>`;
27371
- const shown2 = visible(all2, scope, this.tag, this.query);
27372
- 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>
27373
27594
  <swc-action-button size="s" quiet @click=${() => this.editing = "new"}><swc-icon-add slot="icon"></swc-icon-add>Remember</swc-action-button>
27374
27595
  </div>
27375
27596
  <div class="tools">
@@ -27391,7 +27612,7 @@ var SliccMemory = class extends ModelElement {
27391
27612
  </div>
27392
27613
  <div class="list">
27393
27614
  ${this.editing === "new" ? b`<div class="row">${this.#form({ scope })}</div>` : A}
27394
- ${shown2.length ? sections(shown2).map(
27615
+ ${shown3.length ? sections(shown3).map(
27395
27616
  ([section, items]) => b`<h3>${section}</h3>${items.map((memory) => this.#row(memory))}`
27396
27617
  ) : b`<div class="note">${all2.length ? "No memories match." : "No memories yet."}</div>`}
27397
27618
  </div>`;
@@ -27699,10 +27920,10 @@ var SliccMonitor = class extends ModelElement {
27699
27920
  const surface2 = surfaces2[section.id];
27700
27921
  const rows = section.rows.length > 0;
27701
27922
  const open = rows && (this.expanded[section.id] ?? flag !== null);
27702
- const list4 = `rows-${section.id}`;
27923
+ const list5 = `rows-${section.id}`;
27703
27924
  return b`<li class="section" data-id=${section.id} data-open=${open ? "" : A}>
27704
27925
  <div class="line">
27705
- ${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=${() => {
27706
27927
  this.expanded = { ...this.expanded, [section.id]: !open };
27707
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>`}
27708
27929
  ${flag ? b`<swc-badge size="s" variant=${flag.variant} subtle>${flag.text}</swc-badge>` : A}
@@ -27711,7 +27932,7 @@ var SliccMonitor = class extends ModelElement {
27711
27932
  >Open</swc-action-button
27712
27933
  >` : A}
27713
27934
  </div>
27714
- ${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(
27715
27936
  section.rows,
27716
27937
  (row, i25) => row.id ?? i25,
27717
27938
  (row) => this.#row(row)
@@ -27918,6 +28139,748 @@ var SliccNotices = class extends ModelElement {
27918
28139
  }
27919
28140
  };
27920
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
+
27921
28884
  // src/app/settings.ts
27922
28885
  var accountStatus = {
27923
28886
  connected: ["positive", "Connected"],
@@ -28275,6 +29238,7 @@ var SliccSettings = class extends ModelElement {
28275
29238
  <div class="accounts">${providers.map((account) => this.#account(account))}</div>
28276
29239
  ${servers.length ? b`<h2>MCP servers</h2>
28277
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}
28278
29242
  </div>`;
28279
29243
  }
28280
29244
  };
@@ -31070,14 +32034,14 @@ var RegexSource = class {
31070
32034
  return CAPTURING_REGEX_SOURCE.test(regexSource);
31071
32035
  }
31072
32036
  static replaceCaptures(regexSource, captureSource, captureIndices) {
31073
- return regexSource.replace(CAPTURING_REGEX_SOURCE, (match, index, commandIndex, command2) => {
32037
+ return regexSource.replace(CAPTURING_REGEX_SOURCE, (match, index, commandIndex, command3) => {
31074
32038
  let capture = captureIndices[parseInt(index || commandIndex, 10)];
31075
32039
  if (capture) {
31076
32040
  let result = captureSource.substring(capture.start, capture.end);
31077
32041
  while (result[0] === ".") {
31078
32042
  result = result.substring(1);
31079
32043
  }
31080
- switch (command2) {
32044
+ switch (command3) {
31081
32045
  case "downcase":
31082
32046
  return result.toLowerCase();
31083
32047
  case "upcase":
@@ -32091,8 +33055,8 @@ var IncludeOnlyRule = class extends Rule {
32091
33055
  }
32092
33056
  }
32093
33057
  collectPatterns(grammar2, out) {
32094
- for (const pattern of this.patterns) {
32095
- const rule = grammar2.getRule(pattern);
33058
+ for (const pattern2 of this.patterns) {
33059
+ const rule = grammar2.getRule(pattern2);
32096
33060
  rule.collectPatterns(grammar2, out);
32097
33061
  }
32098
33062
  }
@@ -32159,8 +33123,8 @@ var BeginEndRule = class extends Rule {
32159
33123
  _getCachedCompiledPatterns(grammar2, endRegexSource) {
32160
33124
  if (!this._cachedCompiledPatterns) {
32161
33125
  this._cachedCompiledPatterns = new RegExpSourceList();
32162
- for (const pattern of this.patterns) {
32163
- const rule = grammar2.getRule(pattern);
33126
+ for (const pattern2 of this.patterns) {
33127
+ const rule = grammar2.getRule(pattern2);
32164
33128
  rule.collectPatterns(grammar2, this._cachedCompiledPatterns);
32165
33129
  }
32166
33130
  if (this.applyEndPatternLast) {
@@ -32232,8 +33196,8 @@ var BeginWhileRule = class extends Rule {
32232
33196
  _getCachedCompiledPatterns(grammar2) {
32233
33197
  if (!this._cachedCompiledPatterns) {
32234
33198
  this._cachedCompiledPatterns = new RegExpSourceList();
32235
- for (const pattern of this.patterns) {
32236
- const rule = grammar2.getRule(pattern);
33199
+ for (const pattern2 of this.patterns) {
33200
+ const rule = grammar2.getRule(pattern2);
32237
33201
  rule.collectPatterns(grammar2, this._cachedCompiledPatterns);
32238
33202
  }
32239
33203
  }
@@ -32354,14 +33318,14 @@ var RuleFactory = class _RuleFactory {
32354
33318
  let r39 = [];
32355
33319
  if (patterns) {
32356
33320
  for (let i25 = 0, len = patterns.length; i25 < len; i25++) {
32357
- const pattern = patterns[i25];
33321
+ const pattern2 = patterns[i25];
32358
33322
  let ruleId = -1;
32359
- if (pattern.include) {
32360
- const reference2 = parseInclude(pattern.include);
33323
+ if (pattern2.include) {
33324
+ const reference2 = parseInclude(pattern2.include);
32361
33325
  switch (reference2.kind) {
32362
33326
  case 0:
32363
33327
  case 1:
32364
- ruleId = _RuleFactory.getCompiledRuleId(repository[pattern.include], helper, repository);
33328
+ ruleId = _RuleFactory.getCompiledRuleId(repository[pattern2.include], helper, repository);
32365
33329
  break;
32366
33330
  case 2:
32367
33331
  let localIncludedRule = repository[reference2.ruleName];
@@ -32390,7 +33354,7 @@ var RuleFactory = class _RuleFactory {
32390
33354
  break;
32391
33355
  }
32392
33356
  } else {
32393
- ruleId = _RuleFactory.getCompiledRuleId(pattern, helper, repository);
33357
+ ruleId = _RuleFactory.getCompiledRuleId(pattern2, helper, repository);
32394
33358
  }
32395
33359
  if (ruleId !== -1) {
32396
33360
  const rule = helper.getRule(ruleId);
@@ -37233,13 +38197,13 @@ function splitTokens(tokens, breakpoints) {
37233
38197
  });
37234
38198
  });
37235
38199
  }
37236
- function flatTokenVariants(merged, variantsOrder, cssVariablePrefix, defaultColor, colorsRendering = "css-vars") {
38200
+ function flatTokenVariants(merged2, variantsOrder, cssVariablePrefix, defaultColor, colorsRendering = "css-vars") {
37237
38201
  const token2 = {
37238
- content: merged.content,
37239
- explanation: merged.explanation,
37240
- offset: merged.offset
38202
+ content: merged2.content,
38203
+ explanation: merged2.explanation,
38204
+ offset: merged2.offset
37241
38205
  };
37242
- const styles = variantsOrder.map((t88) => getTokenStyleObject(merged.variants[t88]));
38206
+ const styles = variantsOrder.map((t88) => getTokenStyleObject(merged2.variants[t88]));
37243
38207
  const styleKeys = new Set(styles.flatMap((t88) => Object.keys(t88)));
37244
38208
  const mergedStyles = {};
37245
38209
  const varKey = (idx, key2) => {
@@ -39349,25 +40313,25 @@ var namedCaptureDelim = r35`\(\?<(?![=!])(?<captureName>[^>]+)>`;
39349
40313
  var captureDelim = r35`${namedCaptureDelim}|(?<unnamed>\()(?!\?)`;
39350
40314
  var token = new RegExp(r35`${namedCaptureDelim}|${recursiveToken}|\(\?|\\?.`, "gsu");
39351
40315
  var overlappingRecursionMsg = "Cannot use multiple overlapping recursions";
39352
- function recursion(pattern, data) {
40316
+ function recursion(pattern2, data) {
39353
40317
  const { hiddenCaptures, mode } = {
39354
40318
  hiddenCaptures: [],
39355
40319
  mode: "plugin",
39356
40320
  ...data
39357
40321
  };
39358
40322
  let captureTransfers = data?.captureTransfers ?? /* @__PURE__ */ new Map();
39359
- if (!new RegExp(recursiveToken, "su").test(pattern)) {
40323
+ if (!new RegExp(recursiveToken, "su").test(pattern2)) {
39360
40324
  return {
39361
- pattern,
40325
+ pattern: pattern2,
39362
40326
  captureTransfers,
39363
40327
  hiddenCaptures
39364
40328
  };
39365
40329
  }
39366
- if (mode === "plugin" && hasUnescaped(pattern, r35`\(\?\(DEFINE\)`, Context.DEFAULT)) {
40330
+ if (mode === "plugin" && hasUnescaped(pattern2, r35`\(\?\(DEFINE\)`, Context.DEFAULT)) {
39367
40331
  throw new Error("DEFINE groups cannot be used with recursion");
39368
40332
  }
39369
40333
  const addedHiddenCaptures = [];
39370
- const hasNumberedBackref = hasUnescaped(pattern, r35`\\[1-9]`, Context.DEFAULT);
40334
+ const hasNumberedBackref = hasUnescaped(pattern2, r35`\\[1-9]`, Context.DEFAULT);
39371
40335
  const groupContentsStartPos = /* @__PURE__ */ new Map();
39372
40336
  const openGroups = [];
39373
40337
  let hasRecursed = false;
@@ -39375,7 +40339,7 @@ function recursion(pattern, data) {
39375
40339
  let numCapturesPassed = 0;
39376
40340
  let match;
39377
40341
  token.lastIndex = 0;
39378
- while (match = token.exec(pattern)) {
40342
+ while (match = token.exec(pattern2)) {
39379
40343
  const { 0: m15, groups: { captureName, rDepth, gRNameOrNum, gRDepth } } = match;
39380
40344
  if (m15 === "[") {
39381
40345
  numCharClassesOpen++;
@@ -39392,13 +40356,13 @@ function recursion(pattern, data) {
39392
40356
  `${mode === "external" ? "Backrefs" : "Numbered backrefs"} cannot be used with global recursion`
39393
40357
  );
39394
40358
  }
39395
- const left = pattern.slice(0, match.index);
39396
- const right = pattern.slice(token.lastIndex);
40359
+ const left = pattern2.slice(0, match.index);
40360
+ const right = pattern2.slice(token.lastIndex);
39397
40361
  if (hasUnescaped(right, recursiveToken, Context.DEFAULT)) {
39398
40362
  throw new Error(overlappingRecursionMsg);
39399
40363
  }
39400
40364
  const reps = +rDepth - 1;
39401
- pattern = makeRecursive(
40365
+ pattern2 = makeRecursive(
39402
40366
  left,
39403
40367
  right,
39404
40368
  reps,
@@ -39432,7 +40396,7 @@ function recursion(pattern, data) {
39432
40396
  throw new Error(r35`Recursive \g cannot be used outside the referenced group "${mode === "external" ? gRNameOrNum : r35`\g<${gRNameOrNum}&R=${gRDepth}>`}"`);
39433
40397
  }
39434
40398
  const startPos = groupContentsStartPos.get(gRNameOrNum);
39435
- const groupContents = getGroupContents(pattern, startPos);
40399
+ const groupContents = getGroupContents(pattern2, startPos);
39436
40400
  if (hasNumberedBackref && hasUnescaped(groupContents, r35`${namedCaptureDelim}|\((?!\?)`, Context.DEFAULT)) {
39437
40401
  throw new Error(
39438
40402
  // When used in `external` mode by transpilers other than Regex+, backrefs might have
@@ -39440,11 +40404,11 @@ function recursion(pattern, data) {
39440
40404
  `${mode === "external" ? "Backrefs" : "Numbered backrefs"} cannot be used with recursion of capturing groups`
39441
40405
  );
39442
40406
  }
39443
- const groupContentsLeft = pattern.slice(startPos, match.index);
40407
+ const groupContentsLeft = pattern2.slice(startPos, match.index);
39444
40408
  const groupContentsRight = groupContents.slice(groupContentsLeft.length + m15.length);
39445
40409
  const numAddedHiddenCapturesPreExpansion = addedHiddenCaptures.length;
39446
40410
  const reps = +gRDepth - 1;
39447
- const expansion = makeRecursive(
40411
+ const expansion2 = makeRecursive(
39448
40412
  groupContentsLeft,
39449
40413
  groupContentsRight,
39450
40414
  reps,
@@ -39461,10 +40425,10 @@ function recursion(pattern, data) {
39461
40425
  numAddedHiddenCapturesPreExpansion,
39462
40426
  numCapturesPassed
39463
40427
  );
39464
- const pre = pattern.slice(0, startPos);
39465
- const post = pattern.slice(startPos + groupContents.length);
39466
- pattern = `${pre}${expansion}${post}`;
39467
- 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;
39468
40432
  openGroups.forEach((g7) => g7.hasRecursedWithin = true);
39469
40433
  hasRecursed = true;
39470
40434
  } else if (captureName) {
@@ -39491,7 +40455,7 @@ function recursion(pattern, data) {
39491
40455
  }
39492
40456
  hiddenCaptures.push(...addedHiddenCaptures);
39493
40457
  return {
39494
- pattern,
40458
+ pattern: pattern2,
39495
40459
  captureTransfers,
39496
40460
  hiddenCaptures
39497
40461
  };
@@ -39522,14 +40486,14 @@ function makeRecursive(left, right, reps, isSubpattern, hiddenCaptures, addedHid
39522
40486
  ];
39523
40487
  return `${left}${repeatWithDepth(`(?:${left}`, "forward", ...rest)}(?:)${repeatWithDepth(`${right})`, "backward", ...rest)}${right}`;
39524
40488
  }
39525
- function repeatWithDepth(pattern, direction, reps, namesInRecursed, hiddenCaptures, addedHiddenCaptures, numCapturesPassed) {
40489
+ function repeatWithDepth(pattern2, direction, reps, namesInRecursed, hiddenCaptures, addedHiddenCaptures, numCapturesPassed) {
39526
40490
  const startNum = 2;
39527
40491
  const getDepthNum = (i25) => direction === "forward" ? i25 + startNum : reps - i25 + startNum - 1;
39528
40492
  let result = "";
39529
40493
  for (let i25 = 0; i25 < reps; i25++) {
39530
40494
  const depthNum = getDepthNum(i25);
39531
40495
  result += replaceUnescaped(
39532
- pattern,
40496
+ pattern2,
39533
40497
  r35`${captureDelim}|\\k<(?<backref>[^>]+)>`,
39534
40498
  ({ 0: m15, groups: { captureName, unnamed, backref } }) => {
39535
40499
  if (backref && namesInRecursed && !namesInRecursed.has(backref)) {
@@ -40612,8 +41576,8 @@ function isLoneGLookaround(node, options) {
40612
41576
  function isValidJsGroupName(name) {
40613
41577
  return /^[$_\p{IDS}][$\u200C\u200D\p{IDC}]*$/u.test(name);
40614
41578
  }
40615
- function parseFragment(pattern, options) {
40616
- const ast = J2(pattern, {
41579
+ function parseFragment(pattern2, options) {
41580
+ const ast = J2(pattern2, {
40617
41581
  ...options,
40618
41582
  // Providing a custom set of Unicode property names avoids converting some JS Unicode
40619
41583
  // properties (ex: `\p{Alpha}`) to Onig POSIX classes
@@ -41209,13 +42173,13 @@ var EmulatedRegExp = class _EmulatedRegExp extends RegExp {
41209
42173
  @param {EmulatedRegExp} pattern
41210
42174
  @param {string} [flags]
41211
42175
  */
41212
- constructor(pattern, flags, options) {
42176
+ constructor(pattern2, flags, options) {
41213
42177
  const lazyCompile = !!options?.lazyCompile;
41214
- if (pattern instanceof RegExp) {
42178
+ if (pattern2 instanceof RegExp) {
41215
42179
  if (options) {
41216
42180
  throw new Error("Cannot provide options when copying a regexp");
41217
42181
  }
41218
- const re3 = pattern;
42182
+ const re3 = pattern2;
41219
42183
  super(re3, flags);
41220
42184
  this.#pattern = re3.source;
41221
42185
  if (re3 instanceof _EmulatedRegExp) {
@@ -41231,8 +42195,8 @@ var EmulatedRegExp = class _EmulatedRegExp extends RegExp {
41231
42195
  transfers: [],
41232
42196
  ...options
41233
42197
  };
41234
- super(lazyCompile ? "" : pattern, flags);
41235
- this.#pattern = pattern;
42198
+ super(lazyCompile ? "" : pattern2, flags);
42199
+ this.#pattern = pattern2;
41236
42200
  this.#captureMap = createCaptureMap(opts.hiddenCaptures, opts.transfers);
41237
42201
  this.#strategy = opts.strategy;
41238
42202
  this.rawOptions = options ?? {};
@@ -41358,13 +42322,13 @@ function createCaptureMap(hiddenCaptures, transfers) {
41358
42322
  }
41359
42323
  return captureMap;
41360
42324
  }
41361
- function createNameMap(pattern) {
42325
+ function createNameMap(pattern2) {
41362
42326
  const re3 = /(?<capture>\((?:\?<(?![=!])(?<name>[^>]+)>|(?!\?)))|\\?./gsu;
41363
42327
  const map = /* @__PURE__ */ new Map();
41364
42328
  let numCharClassesOpen = 0;
41365
42329
  let numCaptures = 0;
41366
42330
  let match;
41367
- while (match = re3.exec(pattern)) {
42331
+ while (match = re3.exec(pattern2)) {
41368
42332
  const { 0: m15, groups: { capture, name } } = match;
41369
42333
  if (m15 === "[") {
41370
42334
  numCharClassesOpen++;
@@ -41381,16 +42345,16 @@ function createNameMap(pattern) {
41381
42345
  }
41382
42346
  return map;
41383
42347
  }
41384
- function toRegExp(pattern, options) {
41385
- const d22 = toRegExpDetails(pattern, options);
42348
+ function toRegExp(pattern2, options) {
42349
+ const d22 = toRegExpDetails(pattern2, options);
41386
42350
  if (d22.options) {
41387
42351
  return new EmulatedRegExp(d22.pattern, d22.flags, d22.options);
41388
42352
  }
41389
42353
  return new RegExp(d22.pattern, d22.flags);
41390
42354
  }
41391
- function toRegExpDetails(pattern, options) {
42355
+ function toRegExpDetails(pattern2, options) {
41392
42356
  const opts = getOptions(options);
41393
- const onigurumaAst = J2(pattern, {
42357
+ const onigurumaAst = J2(pattern2, {
41394
42358
  flags: opts.flags,
41395
42359
  normalizeUnknownPropertyNames: true,
41396
42360
  rules: {
@@ -41443,8 +42407,8 @@ function toRegExpDetails(pattern, options) {
41443
42407
  }
41444
42408
 
41445
42409
  // node_modules/@shikijs/engine-javascript/dist/engine-compile.mjs
41446
- function defaultJavaScriptRegexConstructor(pattern, options) {
41447
- return toRegExp(pattern, {
42410
+ function defaultJavaScriptRegexConstructor(pattern2, options) {
42411
+ return toRegExp(pattern2, {
41448
42412
  global: true,
41449
42413
  hasIndices: true,
41450
42414
  lazyCompileLength: 3e3,
@@ -41464,7 +42428,7 @@ function createJavaScriptRegexEngine(options = {}) {
41464
42428
  cache: /* @__PURE__ */ new Map(),
41465
42429
  ...options
41466
42430
  };
41467
- _options.regexConstructor ||= (pattern) => defaultJavaScriptRegexConstructor(pattern, { target: _options.target });
42431
+ _options.regexConstructor ||= (pattern2) => defaultJavaScriptRegexConstructor(pattern2, { target: _options.target });
41468
42432
  return {
41469
42433
  createScanner(patterns) {
41470
42434
  return new JavaScriptScanner(patterns, _options);
@@ -42794,8 +43758,8 @@ var InteractionManager = class {
42794
43758
  const indexes = this.getLineIndex(point.lineNumber, point.side);
42795
43759
  if (indexes == null) return;
42796
43760
  const lineIndex = this.mode === "diff" ? `${indexes[0]},${indexes[1]}` : `${indexes[0]}`;
42797
- const candidates = this.pre.querySelectorAll(`[data-column-number="${point.lineNumber}"][data-line-index="${lineIndex}"]`);
42798
- 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) {
42799
43763
  if (!(element2 instanceof HTMLElement)) continue;
42800
43764
  const target = this.resolvePointerTarget(getElementPath(element2));
42801
43765
  if (!isLinePointerTarget(target)) continue;
@@ -44047,6 +45011,7 @@ var EXTENSION_TO_FILE_FORMAT = {
44047
45011
  mli: "ocaml",
44048
45012
  mll: "ocaml",
44049
45013
  mly: "ocaml",
45014
+ odin: "odin",
44050
45015
  pas: "pascal",
44051
45016
  p: "pascal",
44052
45017
  pl: "prolog",
@@ -44079,6 +45044,17 @@ var EXTENSION_TO_FILE_FORMAT = {
44079
45044
  py: "python",
44080
45045
  pyw: "python",
44081
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",
44082
45058
  qml: "qml",
44083
45059
  qmldir: "qmldir",
44084
45060
  qss: "qss",
@@ -44189,16 +45165,22 @@ var EXTENSION_TO_FILE_FORMAT = {
44189
45165
  sty: "tex"
44190
45166
  };
44191
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) {
44192
45174
  if (CUSTOM_EXTENSION_TO_FILE_FORMAT.has(fileName)) return CUSTOM_EXTENSION_TO_FILE_FORMAT.get(fileName) ?? "text";
44193
- 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;
44194
45176
  const compoundMatch = fileName.match(/\.([^/\\]+\.[^/\\]+)$/);
44195
45177
  if (compoundMatch != null) {
44196
45178
  if (CUSTOM_EXTENSION_TO_FILE_FORMAT.has(compoundMatch[1])) return CUSTOM_EXTENSION_TO_FILE_FORMAT.get(compoundMatch[1]) ?? "text";
44197
- 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";
44198
45180
  }
44199
45181
  const simpleMatch = fileName.match(/\.([^.]+)$/)?.[1] ?? "";
44200
45182
  if (CUSTOM_EXTENSION_TO_FILE_FORMAT.has(simpleMatch)) return CUSTOM_EXTENSION_TO_FILE_FORMAT.get(simpleMatch) ?? "text";
44201
- return EXTENSION_TO_FILE_FORMAT[simpleMatch] ?? "text";
45183
+ return Object.hasOwn(EXTENSION_TO_FILE_FORMAT, simpleMatch) ? EXTENSION_TO_FILE_FORMAT[simpleMatch] ?? "text" : null;
44202
45184
  }
44203
45185
 
44204
45186
  // node_modules/@pierre/diffs/dist/utils/getHighlighterOptions.js
@@ -44564,8 +45546,8 @@ function normalizeThemeColors(theme3) {
44564
45546
  function fill(colors, key2, value) {
44565
45547
  if (value != null && value !== "") colors[key2] = value;
44566
45548
  }
44567
- function firstColor(...candidates) {
44568
- 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;
44569
45551
  }
44570
45552
  function matchesSurface(color, surface2) {
44571
45553
  return surface2 != null && color.toLowerCase() === surface2.toLowerCase();
@@ -45671,6 +46653,12 @@ function isStyleNode(element2) {
45671
46653
  return typeof tagName === "string" && tagName.toLowerCase() === "style";
45672
46654
  }
45673
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
+
45674
46662
  // node_modules/@pierre/diffs/dist/utils/editSessionAnnotations.js
45675
46663
  function adoptEditSessionAnnotations(annotations, getName, previousSession) {
45676
46664
  const slotNames = /* @__PURE__ */ new Map();
@@ -45762,10 +46750,31 @@ function createHostThemeStyleNode() {
45762
46750
  return node;
45763
46751
  }
45764
46752
 
45765
- // node_modules/@pierre/diffs/dist/utils/platform.js
45766
- var _isSafari = void 0;
45767
- function isSafari() {
45768
- 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);
45769
46778
  }
45770
46779
 
45771
46780
  // node_modules/@pierre/diffs/dist/components/web-components.js
@@ -46417,6 +47426,7 @@ var File = class {
46417
47426
  const tokenizeMaxLength = this.options.tokenizeMaxLength ?? 1e5;
46418
47427
  if (this.fileRenderer.getOrCreateLineCache(file).length > tokenizeMaxLength) return;
46419
47428
  await workerManager.primeFileHighlightCache(file).catch((error) => {
47429
+ if (isHandledWorkerPoolError(error)) return;
46420
47430
  console.error(error);
46421
47431
  });
46422
47432
  }
@@ -49288,13 +50298,13 @@ function getChangeIterationRanges(state3, content, diffStyle) {
49288
50298
  }
49289
50299
  if (iterationRanges.length === 0) return iterationRanges;
49290
50300
  iterationRanges.sort((a22, b13) => a22[0] - b13[0]);
49291
- const merged = [iterationRanges[0]];
50301
+ const merged2 = [iterationRanges[0]];
49292
50302
  for (const [start, end] of iterationRanges.slice(1)) {
49293
- const last = merged[merged.length - 1];
50303
+ const last = merged2[merged2.length - 1];
49294
50304
  if (start <= last[1]) last[1] = Math.max(last[1], end);
49295
- else merged.push([start, end]);
50305
+ else merged2.push([start, end]);
49296
50306
  }
49297
- return merged;
50307
+ return merged2;
49298
50308
  }
49299
50309
  function getChangeLineData({ hunkIndex, hunk, collapsedAfter, collapsedBefore, diffStyle, index, unifiedLineIndex, splitLineIndex, additionLineIndex, deletionLineIndex, additionLineNumber, deletionLineNumber, content, isLastContent, unifiedCount, splitCount }) {
49300
50310
  const unifiedDeletionLineIndex = index < content.deletions ? unifiedLineIndex + index : void 0;
@@ -50491,7 +51501,7 @@ var DiffHunksRenderer = class {
50491
51501
  * row set without a line-count change — the host must escalate to a full
50492
51502
  * re-render instead of its cheap refresh path.
50493
51503
  */
50494
- updateRenderCache(dirtyLines, themeType, lineCountChangeInFlight = false) {
51504
+ updateRenderCache(dirtyLines, themeType, lineCountChangeInFlight = false, changedDocumentLines, documentLineCount) {
50495
51505
  this.pendingStructuralRows = void 0;
50496
51506
  const { renderCache } = this;
50497
51507
  if (renderCache == null) return false;
@@ -50502,13 +51512,27 @@ var DiffHunksRenderer = class {
50502
51512
  const pendingStructuralRows = this.pendingStructuralRows = lineCountChangeInFlight ? /* @__PURE__ */ new Map() : void 0;
50503
51513
  const changedAdditionLines = [];
50504
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
+ }
50505
51529
  for (const [line, tokens] of dirtyLines) {
50506
51530
  const prevProps = hastLines[line]?.properties ?? {};
50507
51531
  const lineText = tokens.map((a22) => a22[2]).join("");
50508
51532
  const canSyncDiffLine = line < diff.additionLines.length;
50509
51533
  const prevLine = canSyncDiffLine ? diff.additionLines[line] ?? "" : "";
50510
51534
  const prevText = cleanLastNewline(prevLine);
50511
- if (pendingStructuralRows == null && canSyncDiffLine) {
51535
+ if (pendingStructuralRows == null && canSyncDiffLine && changedDocumentLines?.has(line) !== true) {
50512
51536
  diff.additionLines[line] = applyLineTextWithNewline(prevLine, lineText);
50513
51537
  if (prevText !== lineText) {
50514
51538
  changedAdditionLines.push(line);
@@ -51502,6 +52526,9 @@ function realignAdditionHastLines(previousLines, nextLines, hastLines, textDocum
51502
52526
  for (let index = prefix; index < nextLines.length; index++) realigned[index] ??= createPlainAdditionLineElement(index, textDocument.getLineText(index));
51503
52527
  return realigned;
51504
52528
  }
52529
+ function canAppendFinalDocumentRow(additionLines, documentLineCount) {
52530
+ return documentLineCount === additionLines.length + 1 && additionLines.at(-1)?.endsWith("\n") === true;
52531
+ }
51505
52532
  function createPlainAdditionLineElement(lineIndex, lineText, unifiedLineIndex = lineIndex, splitLineIndex = lineIndex) {
51506
52533
  return {
51507
52534
  type: "element",
@@ -51604,6 +52631,7 @@ function getDiffHunksRendererOptions(options) {
51604
52631
  hunkSeparators: typeof options?.hunkSeparators === "function" ? "custom" : options?.hunkSeparators,
51605
52632
  expandUnchanged: options?.expandUnchanged,
51606
52633
  loadDiffFiles: options?.loadDiffFiles,
52634
+ parseDiffOptions: options?.parseDiffOptions,
51607
52635
  collapsedContextThreshold: options?.collapsedContextThreshold,
51608
52636
  lineDiffType: options?.lineDiffType,
51609
52637
  maxLineDiffLength: options?.maxLineDiffLength,
@@ -52596,7 +53624,7 @@ var FileDiff = class {
52596
53624
  this.detachAdditionLines();
52597
53625
  this.hunksRenderer.beginEditSession(editSessionDiff);
52598
53626
  const { shouldRefreshDiffsView, lineCountChangeInFlight } = options;
52599
- if (this.hunksRenderer.updateRenderCache(dirtyLines, themeType, lineCountChangeInFlight)) {
53627
+ if (this.hunksRenderer.updateRenderCache(dirtyLines, themeType, lineCountChangeInFlight, options.changedDocumentLines, options.documentLineCount)) {
52600
53628
  if (this.refreshViewTimeout != null) {
52601
53629
  clearTimeout(this.refreshViewTimeout);
52602
53630
  this.refreshViewTimeout = void 0;
@@ -52743,6 +53771,7 @@ var FileDiff = class {
52743
53771
  const tokenizeMaxLength = this.options.tokenizeMaxLength ?? 1e5;
52744
53772
  if (Math.max(fileDiff.additionLines.length, fileDiff.deletionLines.length) > tokenizeMaxLength) return;
52745
53773
  await workerManager.primeDiffHighlightCache(fileDiff).catch((error) => {
53774
+ if (isHandledWorkerPoolError(error)) return;
52746
53775
  console.error(error);
52747
53776
  });
52748
53777
  }
@@ -54240,9 +55269,9 @@ function disableTextSelection(rootNode = document) {
54240
55269
  }
54241
55270
  function getDockviewTheme(element2) {
54242
55271
  function toClassList(element3) {
54243
- const list4 = [];
54244
- for (let i25 = 0; i25 < element3.classList.length; i25++) list4.push(element3.classList.item(i25));
54245
- return list4;
55272
+ const list5 = [];
55273
+ for (let i25 = 0; i25 < element3.classList.length; i25++) list5.push(element3.classList.item(i25));
55274
+ return list5;
54246
55275
  }
54247
55276
  let theme3 = void 0;
54248
55277
  let parent = element2;
@@ -55446,7 +56475,7 @@ var BranchNode = class BranchNode2 extends CompositeDisposable {
55446
56475
  if (child instanceof BranchNode2) child.proportionalLayout = value;
55447
56476
  });
55448
56477
  }
55449
- constructor(orientation, proportionalLayout, styles, size4, orthogonalSize, disabled, margin, childDescriptors) {
56478
+ constructor(orientation, proportionalLayout, styles, size4, orthogonalSize, disabled2, margin, childDescriptors) {
55450
56479
  super();
55451
56480
  this.orientation = orientation;
55452
56481
  this.styles = styles;
@@ -55489,7 +56518,7 @@ var BranchNode = class BranchNode2 extends CompositeDisposable {
55489
56518
  });
55490
56519
  this.splitview.layout(this.size, this.orthogonalSize);
55491
56520
  }
55492
- this.disabled = disabled;
56521
+ this.disabled = disabled2;
55493
56522
  this.addDisposables(this._onDidChange, this._onDidVisibilityChange, this.splitview.onDidSashEnd(() => {
55494
56523
  this._onDidChange.fire({});
55495
56524
  }));
@@ -65093,9 +66122,9 @@ var ENTERPRISE_MODULE_NAMES = /* @__PURE__ */ new Set([
65093
66122
  var _warnedMissingModule = /* @__PURE__ */ new Set();
65094
66123
  function missingModuleMessage(moduleName, reason) {
65095
66124
  const reasons = reason === void 0 ? [] : [].concat(reason);
65096
- const quoted = reasons.map((r39) => `\`${r39}\``).join(", ");
66125
+ const quoted2 = reasons.map((r39) => `\`${r39}\``).join(", ");
65097
66126
  const requires = reasons.length > 1 ? "require" : "requires";
65098
- 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`;
65099
66128
  if (ENTERPRISE_MODULE_NAMES.has(moduleName)) return `dockview: ${needed}, which ships in dockview-enterprise.
65100
66129
 
65101
66130
  npm install dockview-enterprise
@@ -65695,9 +66724,9 @@ var RootDropTargetService = class {
65695
66724
  }
65696
66725
  setOptions(options) {
65697
66726
  if ("dndEdges" in options) {
65698
- const disabled = typeof options.dndEdges === "boolean" && options.dndEdges === false;
65699
- this._html5Target.disabled = disabled;
65700
- this._pointerTarget.disabled = disabled;
66727
+ const disabled2 = typeof options.dndEdges === "boolean" && options.dndEdges === false;
66728
+ this._html5Target.disabled = disabled2;
66729
+ this._pointerTarget.disabled = disabled2;
65701
66730
  }
65702
66731
  if ("dndEdges" in options || "dockToEdgeGroups" in options) {
65703
66732
  const model = resolveRootOverlayModel({
@@ -66076,16 +67105,16 @@ var nextContextMenuItemId = () => `dv-ctx-menu-item-${_nextId++}`;
66076
67105
  function isItemConfig(item2) {
66077
67106
  return typeof item2 === "object";
66078
67107
  }
66079
- function buildItem(label, close, action, disabled) {
67108
+ function buildItem(label, close, action, disabled2) {
66080
67109
  const el = document.createElement("div");
66081
67110
  el.className = "dv-context-menu-item";
66082
67111
  el.setAttribute("role", "menuitem");
66083
- if (disabled) {
67112
+ if (disabled2) {
66084
67113
  el.classList.add("dv-context-menu-item--disabled");
66085
67114
  el.setAttribute("aria-disabled", "true");
66086
67115
  }
66087
67116
  el.textContent = label;
66088
- if (!disabled) el.addEventListener("click", () => {
67117
+ if (!disabled2) el.addEventListener("click", () => {
66089
67118
  action();
66090
67119
  close();
66091
67120
  });
@@ -71287,9 +72316,9 @@ var getBaseFileName = (path) => {
71287
72316
  };
71288
72317
  var getExtensionCandidates = (fileName) => {
71289
72318
  const segments = fileName.toLowerCase().split(".");
71290
- const candidates = [];
71291
- for (let index = 1; index < segments.length; index += 1) candidates.push(segments.slice(index).join("."));
71292
- return candidates;
72319
+ const candidates2 = [];
72320
+ for (let index = 1; index < segments.length; index += 1) candidates2.push(segments.slice(index).join("."));
72321
+ return candidates2;
71293
72322
  };
71294
72323
  function remapEntryToIcon(entry, remappedFrom) {
71295
72324
  if (typeof entry === "string") return {
@@ -77442,8 +78471,8 @@ function getFileTreeRowAriaLabel(row) {
77442
78471
  if (flattenedSegments == null || flattenedSegments.length === 0) return row.name;
77443
78472
  return flattenedSegments.map((segment2) => segment2.name).join(" / ");
77444
78473
  }
77445
- function computeStickyRowsFromCandidates(candidates, scrollTop, itemHeight, totalRowCount) {
77446
- return candidates.map((candidate, slotDepth) => {
78474
+ function computeStickyRowsFromCandidates(candidates2, scrollTop, itemHeight, totalRowCount) {
78475
+ return candidates2.map((candidate, slotDepth) => {
77447
78476
  const defaultTop = slotDepth * itemHeight;
77448
78477
  const nextBoundaryIndex = candidate.subtreeEndIndex + 1;
77449
78478
  if (nextBoundaryIndex >= totalRowCount) return {
@@ -77490,9 +78519,9 @@ function getPointElement(rootNode, clientX, clientY) {
77490
78519
  return element2 instanceof HTMLElement ? element2 : null;
77491
78520
  }
77492
78521
  function getShadowPointElementByGeometry(rootNode, clientX, clientY) {
77493
- const candidates = Array.from(rootNode.querySelectorAll('[data-type="item"], [data-item-flattened-subitem]'));
77494
- for (let index = candidates.length - 1; index >= 0; index--) {
77495
- 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];
77496
78525
  const rect = candidate.getBoundingClientRect();
77497
78526
  if (clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom) return candidate;
77498
78527
  }
@@ -80010,6 +81039,8 @@ var elements = {
80010
81039
  "slicc-network": SliccNetwork,
80011
81040
  "slicc-notices": SliccNotices,
80012
81041
  "slicc-approvals": SliccApprovals,
81042
+ "slicc-secrets": SliccSecrets,
81043
+ "slicc-vault-button": SliccVaultButton,
80013
81044
  "slicc-confirm": SliccConfirm,
80014
81045
  "slicc-prompt": SliccPrompt,
80015
81046
  "slicc-app": SliccApp
@@ -80046,7 +81077,9 @@ export {
80046
81077
  SliccNetwork,
80047
81078
  SliccNotices,
80048
81079
  SliccPrompt,
81080
+ SliccSecrets,
80049
81081
  SliccUpdates,
81082
+ SliccVaultButton,
80050
81083
  confirm,
80051
81084
  define2 as define,
80052
81085
  grammarBase,