@ai-ecoverse/slicc-spectrum 1.65.2 → 1.66.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.
@@ -6,7 +6,7 @@ import {
6
6
  DummySettings,
7
7
  clock,
8
8
  dummySettingsKey
9
- } from "./chunk-ZS4XIKFR.js";
9
+ } from "./chunk-2PKRU2CL.js";
10
10
  import {
11
11
  Emitter
12
12
  } from "./chunk-T5NHW4KC.js";
package/dist/slicc-ui.js CHANGED
@@ -28772,6 +28772,20 @@ function asking2(request2) {
28772
28772
  if (request2.kind === "unseal") return b`${by} asks to remove the ${request2.provider} sign-in`;
28773
28773
  return request2.name ? b`${by} asks for <code>${request2.name}</code>` : b`${by} asks for a value`;
28774
28774
  }
28775
+ var accountLights = {
28776
+ connected: ["positive", "Active"],
28777
+ expired: ["notice", "Expired"],
28778
+ "signing-in": ["info", "Signing in"],
28779
+ disconnected: ["neutral", "Not connected"]
28780
+ };
28781
+ function putOptions(secret) {
28782
+ if (secret.kind === "model") return { kind: "model", provider: secret.provider };
28783
+ if (secret.kind === "mcp") return { kind: "mcp", server: secret.server };
28784
+ return void 0;
28785
+ }
28786
+ function agentUse(secret) {
28787
+ return secret.kind === "model" || secret.kind === "mcp" && secret.rules.length === 0;
28788
+ }
28775
28789
  function byName(a22, b13) {
28776
28790
  return a22.name < b13.name ? -1 : a22.name > b13.name ? 1 : 0;
28777
28791
  }
@@ -29263,7 +29277,8 @@ var SliccSecrets = class extends ThemedElement {
29263
29277
  model.vault.on("availability", update),
29264
29278
  model.vault.on("list", () => this.#listed(model.vault))
29265
29279
  ] : [],
29266
- ...model.network ? [model.network.on("network", update)] : []
29280
+ ...model.network ? [model.network.on("network", update)] : [],
29281
+ model.settings.on("accounts", update)
29267
29282
  ];
29268
29283
  }
29269
29284
  #button(config, action) {
@@ -29349,7 +29364,7 @@ var SliccSecrets = class extends ThemedElement {
29349
29364
  }
29350
29365
  this.busy = true;
29351
29366
  try {
29352
- await vault.put?.(name, value);
29367
+ await vault.put?.(name, value, void 0, form.kind === "replace" ? form.options : void 0);
29353
29368
  if (!this.#mine(vault, form)) return;
29354
29369
  this.form = null;
29355
29370
  this.said = { kind: "saved", name };
@@ -29629,8 +29644,21 @@ var SliccSecrets = class extends ThemedElement {
29629
29644
  </div>
29630
29645
  </div>`;
29631
29646
  }
29647
+ #reach(secret, gone3) {
29648
+ if (agentUse(secret)) {
29649
+ const use = secret.kind === "model" ? `Model requests to ${this.#providerName(secret.provider ?? "")}, by the agent` : `Requests to the MCP server ${secret.server}, by the agent`;
29650
+ return b`<dt>Used for</dt><dd data-used-for>${use}</dd>`;
29651
+ }
29652
+ const rules = secret.rules.length ? secret.rules.map((rule, index) => b`${index ? b`<br />` : A}${ruleLine(rule)}`) : "Nowhere yet: no rule names it";
29653
+ return b`<dt>${gone3 ? "Was sent to" : "Sent to"}</dt><dd data-rules>${rules}</dd>`;
29654
+ }
29655
+ #account(secret) {
29656
+ const id = secret.kind === "mcp" ? `mcp:${secret.server}` : secret.provider;
29657
+ return this.model?.settings.accounts().find((account) => account.id === id);
29658
+ }
29632
29659
  #state(secret) {
29633
- 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"];
29660
+ const account = agentUse(secret) ? this.#account(secret) : void 0;
29661
+ const [variant, label] = secret.state === "removed" ? ["neutral", "Removed"] : secret.expiresAt !== void 0 && secret.expiresAt <= Date.now() ? ["notice", "Expired"] : account ? accountLights[account.status] : secret.state === "inert" && !agentUse(secret) ? ["neutral", "No rule yet"] : ["positive", "Active"];
29634
29662
  return b`<swc-status-light size="s" variant=${variant}>${label}</swc-status-light>`;
29635
29663
  }
29636
29664
  #controls(vault, secret) {
@@ -29641,15 +29669,18 @@ var SliccSecrets = class extends ThemedElement {
29641
29669
  const replace = vault.put && replaceable(secret);
29642
29670
  if (!replace && !vault.remove) return A;
29643
29671
  return b`<div class="buttons">
29644
- ${replace ? b`<swc-button size="s" variant="secondary" data-action="replace" @click=${() => this.open({ kind: "replace", name })}>Replace value</swc-button>` : A}
29672
+ ${replace ? b`<swc-button size="s" variant="secondary" data-action="replace" @click=${() => this.open({ kind: "replace", name, options: putOptions(secret) })}>Replace value</swc-button>` : A}
29645
29673
  ${vault.remove ? b`<swc-button size="s" variant="secondary" fill-style="outline" data-action="remove" ?pending=${this.acting.has(`remove:${name}`)} @click=${(event2) => this.removeSecret(vault, secret, event2.currentTarget)}>Remove</swc-button>` : A}
29646
29674
  </div>`;
29647
29675
  }
29676
+ #providerName(id) {
29677
+ return this.model?.settings.accounts().find((account) => account.id === id)?.provider ?? id;
29678
+ }
29648
29679
  #secret(vault, secret, entries, devices, more, locked) {
29649
29680
  const kind = kinds[secret.kind];
29650
29681
  const reveal = revealed(secret.name, entries, Date.now(), more);
29651
29682
  const exec = execLine(secret.exec, secret.execArgv);
29652
- const gone2 = secret.state === "removed";
29683
+ const gone3 = secret.state === "removed";
29653
29684
  const form = this.#offline ? null : this.form;
29654
29685
  return b`<li class="secret" data-secret=${secret.name} data-state=${secret.state}>
29655
29686
  <div class="name">
@@ -29659,17 +29690,18 @@ var SliccSecrets = class extends ThemedElement {
29659
29690
  </div>
29660
29691
  ${this.#controls(vault, secret)}
29661
29692
  <dl>
29662
- <dt>${gone2 ? "Was sent to" : "Sent to"}</dt>
29663
- <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>
29664
- ${secret.exits ? b`<dt>${gone2 ? "Left through" : "Leaves through"}</dt><dd data-exits>${secret.exits.length ? list4(secret.exits.map((exit) => exitLabel(exit, devices))) : "No exit"}</dd>` : A}
29693
+ ${this.#reach(secret, gone3)}
29694
+ ${secret.exits ? b`<dt>${gone3 ? "Left through" : "Leaves through"}</dt><dd data-exits>${secret.exits.length ? list4(secret.exits.map((exit) => exitLabel(exit, devices))) : "No exit"}</dd>` : A}
29665
29695
  ${exec || reveal ? b`<dt>Programs</dt><dd data-exec>${exec}${exec && reveal ? b`<br />` : A}${reveal}</dd>` : A}
29696
+ ${secret.kind === "model" && secret.provider ? b`<dt>Provider</dt><dd data-provider>${this.#providerName(secret.provider)}</dd>` : A}
29697
+ ${secret.kind === "mcp" && secret.server ? b`<dt>Server</dt><dd data-server>${secret.server}</dd>` : A}
29666
29698
  <dt>From</dt>
29667
29699
  <dd data-source>${sourceLine(secret, devices)}</dd>
29668
29700
  ${secret.usedAt ? b`<dt>Last used</dt><dd data-used>${since(secret.usedAt)}</dd>` : A}
29669
29701
  ${secret.expiresAt ? b`<dt>Expires</dt><dd data-expires>${until(secret.expiresAt)}</dd>` : A}
29670
29702
  ${secret.removedAt ? b`<dt>Removed</dt><dd>${since(secret.removedAt)}</dd>` : A}
29671
29703
  </dl>
29672
- ${form?.kind === "replace" && form.name === secret.name ? this.#valueForm(vault, form, locked) : A}
29704
+ ${form?.kind === "replace" && form.name === secret.name && (form.options?.kind ?? "secret") === (putOptions(secret)?.kind ?? "secret") ? this.#valueForm(vault, form, locked) : A}
29673
29705
  </li>`;
29674
29706
  }
29675
29707
  #secrets(vault, list5, entries, devices, locked) {
@@ -29790,6 +29822,43 @@ var SliccSecrets = class extends ThemedElement {
29790
29822
  }
29791
29823
  };
29792
29824
 
29825
+ // src/app/records.ts
29826
+ var envNames = {
29827
+ anthropic: "ANTHROPIC_API_KEY",
29828
+ openai: "OPENAI_API_KEY",
29829
+ azure: "AZURE_OPENAI_API_KEY",
29830
+ "azure-openai": "AZURE_OPENAI_API_KEY",
29831
+ "azure-openai-responses": "AZURE_OPENAI_API_KEY",
29832
+ google: "GEMINI_API_KEY",
29833
+ gemini: "GEMINI_API_KEY",
29834
+ "google-vertex": "GOOGLE_CLOUD_API_KEY",
29835
+ "amazon-bedrock": "AWS_BEARER_TOKEN_BEDROCK",
29836
+ mistral: "MISTRAL_API_KEY",
29837
+ groq: "GROQ_API_KEY",
29838
+ cerebras: "CEREBRAS_API_KEY",
29839
+ xai: "XAI_API_KEY",
29840
+ openrouter: "OPENROUTER_API_KEY",
29841
+ "vercel-ai-gateway": "AI_GATEWAY_API_KEY",
29842
+ deepseek: "DEEPSEEK_API_KEY",
29843
+ zai: "ZAI_API_KEY",
29844
+ moonshotai: "MOONSHOT_API_KEY",
29845
+ fireworks: "FIREWORKS_API_KEY",
29846
+ together: "TOGETHER_API_KEY",
29847
+ huggingface: "HF_TOKEN",
29848
+ "github-copilot": "COPILOT_GITHUB_TOKEN"
29849
+ };
29850
+ var upper = (text2) => text2.toUpperCase().replace(/[^A-Z0-9]/g, "_");
29851
+ function serverOf(account) {
29852
+ return account.id.replace(/^mcp:/, "");
29853
+ }
29854
+ function recordKind(account) {
29855
+ return account.group === "mcp" ? "mcp" : "model";
29856
+ }
29857
+ function recordName(account) {
29858
+ if (account.group === "mcp") return `MCP_${upper(serverOf(account))}_TOKEN`;
29859
+ return envNames[account.id] ?? `${upper(account.id)}_API_KEY`;
29860
+ }
29861
+
29793
29862
  // src/app/settings.ts
29794
29863
  var accountStatus = {
29795
29864
  connected: ["positive", "Connected"],
@@ -29803,20 +29872,34 @@ var accountStatus = {
29803
29872
  var popup = (account) => account.group === "mcp" && account.auth === "oauth";
29804
29873
  var secretNames = { "api-key": "API key", token: "token" };
29805
29874
  var rejectedNames = { "api-key": "API key rejected", token: "Token rejected" };
29806
- var SliccSettings = class extends ModelElement {
29875
+ var keptNames = { "api-key": "Key kept in the vault", token: "Token kept in the vault" };
29876
+ var kept2 = (account) => account.auth === "api-key" ? keptNames[account.secret ?? "api-key"] : "Kept in the vault";
29877
+ async function gone2(vault, name, kind) {
29878
+ try {
29879
+ await vault.remove?.(name, kind);
29880
+ } catch (error) {
29881
+ if (!isVaultFailure(error) || error.code !== "ENOENT") throw error;
29882
+ }
29883
+ }
29884
+ var SliccSettings = class extends ThemedElement {
29807
29885
  static properties = {
29808
- ...ModelElement.properties,
29886
+ ...ThemedElement.properties,
29809
29887
  pending: { state: true },
29810
29888
  entering: { state: true },
29811
- failure: { state: true }
29889
+ failure: { state: true },
29890
+ problem: { state: true },
29891
+ blocked: { state: true }
29812
29892
  };
29813
29893
  constructor() {
29814
29894
  super();
29815
29895
  this.pending = /* @__PURE__ */ new Set();
29816
29896
  this.entering = null;
29817
29897
  this.failure = null;
29898
+ this.problem = null;
29899
+ this.blocked = false;
29818
29900
  }
29819
29901
  static styles = [
29902
+ fieldStyles,
29820
29903
  i`
29821
29904
  :host {
29822
29905
  display: block;
@@ -29898,7 +29981,25 @@ var SliccSettings = class extends ModelElement {
29898
29981
  .key sp-textfield {
29899
29982
  flex: 1 1 auto;
29900
29983
  }
29901
- .failure {
29984
+ .key.stacked {
29985
+ display: grid;
29986
+ gap: var(--swc-spacing-200);
29987
+ max-width: 420px;
29988
+ }
29989
+ .buttons,
29990
+ .locked {
29991
+ display: flex;
29992
+ flex-wrap: wrap;
29993
+ align-items: center;
29994
+ gap: var(--swc-spacing-100);
29995
+ }
29996
+ .hint {
29997
+ margin: 0;
29998
+ color: var(--swc-neutral-subdued-content-color-default);
29999
+ font-size: var(--swc-font-size-75);
30000
+ }
30001
+ .failure,
30002
+ .problem {
29902
30003
  display: flex;
29903
30004
  align-items: flex-start;
29904
30005
  gap: var(--swc-spacing-100);
@@ -29945,7 +30046,12 @@ var SliccSettings = class extends ModelElement {
29945
30046
  ];
29946
30047
  subscribe(model) {
29947
30048
  const update = () => this.requestUpdate();
29948
- return [model.settings.on("settings", update), model.settings.on("accounts", update)];
30049
+ return [
30050
+ ...super.subscribe(model),
30051
+ model.settings.on("settings", update),
30052
+ model.settings.on("accounts", update),
30053
+ ...model.vault ? [model.vault.on("status", update)] : []
30054
+ ];
29949
30055
  }
29950
30056
  focus() {
29951
30057
  this.focusOn("sp-picker");
@@ -29960,19 +30066,78 @@ var SliccSettings = class extends ModelElement {
29960
30066
  return event2.target.checked;
29961
30067
  }
29962
30068
  #cancelled = /* @__PURE__ */ new Set();
29963
- async connect(id, secret) {
29964
- this.pending = /* @__PURE__ */ new Set([...this.pending, id]);
30069
+ #busy(id, on) {
30070
+ const rest = [...this.pending].filter((candidate) => candidate !== id);
30071
+ this.pending = new Set(on ? [...rest, id] : rest);
30072
+ }
30073
+ async connect(id, secret, fields) {
30074
+ this.#busy(id, true);
29965
30075
  this.failure = null;
29966
30076
  this.#cancelled.delete(id);
30077
+ const settings = this.model?.settings;
29967
30078
  try {
29968
- await this.model?.settings.connect(id, secret);
30079
+ await (fields ? settings?.connect(id, secret, fields) : secret === void 0 ? settings?.connect(id) : settings?.connect(id, secret));
29969
30080
  if (this.entering === id) this.entering = null;
29970
30081
  } catch (error) {
29971
30082
  if (!this.#cancelled.has(id)) this.failure = { id, message: error.message };
29972
30083
  } finally {
29973
30084
  this.#cancelled.delete(id);
29974
- this.pending = new Set([...this.pending].filter((candidate) => candidate !== id));
30085
+ this.#busy(id, false);
30086
+ }
30087
+ }
30088
+ #vault(account) {
30089
+ const vault = this.model?.vault;
30090
+ return account.auth === "api-key" && account.store === "vault" && vault?.put ? vault : null;
30091
+ }
30092
+ async keep(account, value, fields) {
30093
+ const model = this.model;
30094
+ const vault = this.#vault(account);
30095
+ const { id } = account;
30096
+ const problem = valueProblem(value);
30097
+ if (problem) {
30098
+ this.#refuse(id, "value", problem);
30099
+ return;
30100
+ }
30101
+ if (vault.status()?.locked) return;
30102
+ const options = account.group === "mcp" ? { kind: "mcp", server: serverOf(account) } : { kind: "model", provider: account.id };
30103
+ this.#busy(id, true);
30104
+ this.failure = null;
30105
+ try {
30106
+ await vault.put?.(recordName(account), value, void 0, options);
30107
+ } catch (error) {
30108
+ this.#busy(id, false);
30109
+ if (this.model === model) this.#refuse(id, "value", failureText(error));
30110
+ return;
30111
+ }
30112
+ if (this.model !== model || this.entering !== id) {
30113
+ this.#busy(id, false);
30114
+ return;
29975
30115
  }
30116
+ await this.connect(id, void 0, fields);
30117
+ }
30118
+ #refuse(id, field, message) {
30119
+ this.problem = { id, field, message };
30120
+ void this.updateComplete.then(
30121
+ () => this.renderRoot.querySelector(
30122
+ field === "value" ? ".key input[data-field=value]" : `.key input[data-account-field="${field}"]`
30123
+ )?.focus()
30124
+ );
30125
+ }
30126
+ #fields(form, account) {
30127
+ if (!account.fields?.length) return void 0;
30128
+ const values = {};
30129
+ for (const field of account.fields) {
30130
+ const input = form.querySelector(
30131
+ `input[data-account-field="${field.name}"]`
30132
+ );
30133
+ const value = input.value.trim();
30134
+ if (value) values[field.name] = value;
30135
+ else if (field.required) {
30136
+ this.#refuse(account.id, field.name, `${field.label} can\u2019t be empty`);
30137
+ return null;
30138
+ }
30139
+ }
30140
+ return values;
29976
30141
  }
29977
30142
  #start(account) {
29978
30143
  if (account.auth !== "api-key") {
@@ -29980,17 +30145,31 @@ var SliccSettings = class extends ModelElement {
29980
30145
  return;
29981
30146
  }
29982
30147
  this.failure = null;
30148
+ this.problem = null;
29983
30149
  this.entering = account.id;
29984
- void this.updateComplete.then(() => this.focusOn(".key sp-textfield"));
30150
+ void this.updateComplete.then(() => this.focusOn(".key sp-textfield", ".key input"));
29985
30151
  }
29986
- #submit(event2, id) {
30152
+ #submit(event2, account) {
29987
30153
  event2.preventDefault();
29988
- const field = event2.currentTarget.querySelector(
29989
- "sp-textfield"
29990
- );
30154
+ const form = event2.currentTarget;
30155
+ this.problem = null;
30156
+ if (this.#vault(account)) {
30157
+ const value = take(form.querySelector("input[data-field=value]"));
30158
+ const fields2 = this.#fields(form, account);
30159
+ if (fields2 !== null && !this.pending.has(account.id)) void this.keep(account, value, fields2);
30160
+ return;
30161
+ }
30162
+ const field = form.querySelector("sp-textfield");
29991
30163
  const secret = field.value.trim();
29992
30164
  field.value = "";
29993
- if (secret) void this.connect(id, secret);
30165
+ const fields = this.#fields(form, account);
30166
+ if (secret && fields !== null) void this.connect(account.id, secret, fields);
30167
+ }
30168
+ #submitOnEnter(event2) {
30169
+ const target = event2.target;
30170
+ if (event2.key !== "Enter" || target.localName !== "input") return;
30171
+ event2.preventDefault();
30172
+ this.#send(event2);
29994
30173
  }
29995
30174
  #send(event2) {
29996
30175
  event2.currentTarget.closest("form")?.requestSubmit();
@@ -29999,13 +30178,83 @@ var SliccSettings = class extends ModelElement {
29999
30178
  if (event2.key === "Enter") this.#send(event2);
30000
30179
  }
30001
30180
  #cancel() {
30181
+ clearValues(this.renderRoot);
30002
30182
  this.entering = null;
30003
30183
  this.failure = null;
30184
+ this.problem = null;
30185
+ }
30186
+ #problem(account, field) {
30187
+ const problem = this.problem;
30188
+ if (problem?.id !== account.id || problem.field !== field) return A;
30189
+ return b`<div class="problem" id="key-problem" role="alert">
30190
+ <swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle>
30191
+ <span>${problem.message}</span>
30192
+ </div>`;
30193
+ }
30194
+ #described(account, field) {
30195
+ return this.problem?.id === account.id && this.problem.field === field ? "key-problem" : A;
30196
+ }
30197
+ #invalid(account, field) {
30198
+ return this.#described(account, field) === A ? A : "true";
30199
+ }
30200
+ #extra(account) {
30201
+ return (account.fields ?? []).map(
30202
+ (field) => b`<label class="field"><span>${field.label}</span><input
30203
+ type="text"
30204
+ autocomplete="off"
30205
+ spellcheck="false"
30206
+ data-account-field=${field.name}
30207
+ aria-required=${field.required ? "true" : A}
30208
+ aria-invalid=${this.#invalid(account, field.name)}
30209
+ aria-describedby=${this.#described(account, field.name)}
30210
+ /></label>
30211
+ ${this.#problem(account, field.name)}`
30212
+ );
30213
+ }
30214
+ #unlock(vault) {
30215
+ return b`<div class="locked" data-locked>
30216
+ <span>Unlock the vault first</span>
30217
+ <slicc-vault-button
30218
+ data-action="unlock"
30219
+ .vault=${vault}
30220
+ .config=${{ intent: { kind: "unlock" }, label: "Unlock", size: "m", variant: "accent", theme: this.color }}
30221
+ @vault-popup=${(event2) => {
30222
+ this.blocked = event2.detail.state === "blocked";
30223
+ }}
30224
+ ></slicc-vault-button>
30225
+ </div>
30226
+ ${this.blocked ? b`<div class="problem" 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>` : A}`;
30227
+ }
30228
+ #kept(account, vault) {
30229
+ const busy = this.pending.has(account.id);
30230
+ const locked = !!vault.status()?.locked;
30231
+ const name = secretNames[account.secret ?? "api-key"];
30232
+ return b`<form class="key stacked" data-id=${account.id} data-store="vault" @submit=${(event2) => this.#submit(event2, account)} @keydown=${(event2) => this.#submitOnEnter(event2)}>
30233
+ <label class="field"><span>${account.provider} ${name}</span><input
30234
+ type="password"
30235
+ autocomplete="off"
30236
+ spellcheck="false"
30237
+ data-field="value"
30238
+ placeholder=${`Paste the ${name}`}
30239
+ aria-invalid=${this.#invalid(account, "value")}
30240
+ aria-describedby=${this.#described(account, "value")}
30241
+ /></label>
30242
+ ${this.#problem(account, "value")}
30243
+ ${this.#extra(account)}
30244
+ ${locked ? this.#unlock(vault) : b`<p class="hint">Saved in the vault</p>`}
30245
+ <div class="buttons">
30246
+ <swc-button size="m" variant="accent" data-action="save" ?pending=${busy} ?disabled=${locked} @click=${(event2) => this.#send(event2)}>Save</swc-button>
30247
+ <swc-button size="m" variant="secondary" data-action="cancel" @click=${() => this.#cancel()}>Cancel</swc-button>
30248
+ </div>
30249
+ </form>`;
30004
30250
  }
30005
30251
  #key(account) {
30006
30252
  if (this.entering !== account.id) return A;
30253
+ const vault = this.#vault(account);
30254
+ if (vault) return this.#kept(account, vault);
30007
30255
  const busy = this.pending.has(account.id);
30008
- return b`<form class="key" data-id=${account.id} @submit=${(event2) => this.#submit(event2, account.id)}>
30256
+ const extra = account.fields?.length;
30257
+ return b`<form class=${extra ? "key stacked" : "key"} data-id=${account.id} @submit=${(event2) => this.#submit(event2, account)} @keydown=${(event2) => this.#submitOnEnter(event2)}>
30009
30258
  <sp-textfield
30010
30259
  size="m"
30011
30260
  type="password"
@@ -30014,15 +30263,61 @@ var SliccSettings = class extends ModelElement {
30014
30263
  placeholder=${`Paste the ${secretNames[account.secret ?? "api-key"]}`}
30015
30264
  @keydown=${(event2) => this.#enter(event2)}
30016
30265
  ></sp-textfield>
30017
- <swc-button size="m" variant="accent" data-action="save" ?pending=${busy} @click=${(event2) => this.#send(event2)}>Save</swc-button>
30018
- <swc-button size="m" variant="secondary" data-action="cancel" @click=${() => this.#cancel()}>Cancel</swc-button>
30266
+ ${extra ? this.#extra(account) : A}
30267
+ ${extra ? b`<div class="buttons">${this.#buttons(busy)}</div>` : this.#buttons(busy)}
30019
30268
  </form>`;
30020
30269
  }
30270
+ #buttons(busy) {
30271
+ return b`<swc-button size="m" variant="accent" data-action="save" ?pending=${busy} @click=${(event2) => this.#send(event2)}>Save</swc-button>
30272
+ <swc-button size="m" variant="secondary" data-action="cancel" @click=${() => this.#cancel()}>Cancel</swc-button>`;
30273
+ }
30274
+ #inVault(account) {
30275
+ const vault = this.model?.vault;
30276
+ if (account.store !== "vault" || account.status !== "connected" || !vault) return false;
30277
+ return account.auth !== "api-key" || !!vault.put;
30278
+ }
30279
+ #records(account, vault) {
30280
+ if (account.auth === "api-key") return [[recordName(account), recordKind(account)]];
30281
+ const server = serverOf(account);
30282
+ const names = (vault.list()?.secrets ?? []).filter(
30283
+ (secret) => secret.kind === "mcp" && secret.server === server && secret.state !== "removed"
30284
+ ).map((secret) => secret.name);
30285
+ const tokens = names.length ? names : [`${upper(server)}_ACCESS_TOKEN`];
30286
+ return tokens.flatMap(
30287
+ (name) => [
30288
+ [name, "mcp"],
30289
+ [`${name}_GRANT`, "grant"]
30290
+ ]
30291
+ );
30292
+ }
30021
30293
  async #disconnect(account) {
30022
- const body4 = `Agents can\u2019t use ${account.provider} until you connect it again.`;
30294
+ const model = this.model;
30295
+ const vault = model?.vault;
30296
+ const vaulted = account.store === "vault" && !!vault?.remove;
30297
+ const key2 = vaulted && account.auth === "api-key";
30298
+ const signIn = vaulted && account.auth !== "api-key" && account.group === "mcp";
30299
+ const stored = key2 || signIn ? vault : void 0;
30300
+ const body4 = `Agents can\u2019t use ${account.provider} until you connect it again.${key2 ? " You can restore its key from Removed in Secrets for 30 days." : ""}`;
30023
30301
  const title = `Disconnect ${account.provider}?`;
30024
- if (await confirm({ title, body: body4, action: "Disconnect", variant: "confirmation" }))
30025
- this.model?.settings.disconnect(account.id);
30302
+ if (!await confirm({ title, body: body4, action: "Disconnect", variant: "confirmation" })) return;
30303
+ clearValues(this.renderRoot);
30304
+ if (this.model !== model) return;
30305
+ if (stored && !await this.#forget(account, stored)) return;
30306
+ if (this.model === model) model?.settings.disconnect(account.id);
30307
+ }
30308
+ async #forget(account, vault) {
30309
+ this.#busy(account.id, true);
30310
+ this.failure = null;
30311
+ try {
30312
+ for (const [name, kind] of this.#records(account, vault)) await gone2(vault, name, kind);
30313
+ return true;
30314
+ } catch (error) {
30315
+ if (this.model?.vault === vault)
30316
+ this.failure = { id: account.id, message: failureText(error) };
30317
+ return false;
30318
+ } finally {
30319
+ this.#busy(account.id, false);
30320
+ }
30026
30321
  }
30027
30322
  #stopSigningIn(account) {
30028
30323
  this.#cancelled.add(account.id);
@@ -30052,6 +30347,7 @@ var SliccSettings = class extends ModelElement {
30052
30347
  variant="secondary"
30053
30348
  data-action="disconnect"
30054
30349
  accessible-label=${`Disconnect ${account.provider}`}
30350
+ ?pending=${this.pending.has(account.id)}
30055
30351
  @click=${() => this.#disconnect(account)}
30056
30352
  >Disconnect</swc-button>`;
30057
30353
  }
@@ -30085,7 +30381,7 @@ var SliccSettings = class extends ModelElement {
30085
30381
  </div>` : A;
30086
30382
  return b`<div class="account" data-id=${account.id}>
30087
30383
  <span class="provider">${account.provider}</span>
30088
- <span class="identity">${account.identity}</span>
30384
+ <span class="identity">${account.identity}${this.#inVault(account) ? b`<span class="kept" data-store="vault">${account.identity ? " \xB7 " : ""}${kept2(account)}</span>` : A}</span>
30089
30385
  <swc-status-light class="state" variant=${variant}>${label}</swc-status-light>
30090
30386
  ${this.#action(account)}
30091
30387
  </div>
@@ -41934,12 +42230,12 @@ function getIgnoreCaseMatchChars(char) {
41934
42230
  }
41935
42231
  const set = /* @__PURE__ */ new Set();
41936
42232
  const lower2 = char.toLowerCase();
41937
- const upper = lower2.toUpperCase();
42233
+ const upper2 = lower2.toUpperCase();
41938
42234
  const title = LowerToTitleCaseMap.get(lower2);
41939
42235
  const altLower = LowerToAlternativeLowerCaseMap.get(lower2);
41940
42236
  const altUpper = LowerToAlternativeUpperCaseMap.get(lower2);
41941
- if ([...upper].length === 1) {
41942
- set.add(upper);
42237
+ if ([...upper2].length === 1) {
42238
+ set.add(upper2);
41943
42239
  }
41944
42240
  altUpper && set.add(altUpper);
41945
42241
  title && set.add(title);