@yadsh/dsh-prompt-firewall 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -4,8 +4,8 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- let react = require("react");
8
7
  let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
9
  //#region ../../node_modules/.pnpm/zod@4.5.2/node_modules/zod/v4/core/util.js
10
10
  function getEnumValues(entries) {
11
11
  const numericValues = Object.values(entries).filter((v) => typeof v === "number");
@@ -5124,7 +5124,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5124
5124
  },
5125
5125
  sourceLocation: {
5126
5126
  "file": "packages/dsh-prompt-firewall/src/index.ts",
5127
- "line": 120,
5127
+ "line": 140,
5128
5128
  "column": 3
5129
5129
  }
5130
5130
  }, {
@@ -5173,45 +5173,200 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5173
5173
  },
5174
5174
  sourceLocation: {
5175
5175
  "file": "packages/dsh-prompt-firewall/src/index.ts",
5176
- "line": 134,
5176
+ "line": 154,
5177
5177
  "column": 9
5178
5178
  }
5179
5179
  }]
5180
5180
  };
5181
5181
  //#endregion
5182
- //#region src/client/styles.ts
5183
- const styles = `
5184
- .pf-card{border:1px solid var(--border,#2a2f3a);border-radius:14px;background:var(--card,#151821);color:var(--foreground,#eef1f6);overflow:hidden;box-shadow:0 10px 32px rgba(0,0,0,.12)}
5185
- .pf-card *{box-sizing:border-box}.pf-head{display:flex;justify-content:space-between;gap:20px;padding:20px 22px;border-bottom:1px solid var(--border,#2a2f3a);background:linear-gradient(135deg,rgba(75,114,255,.10),transparent 55%)}
5186
- .pf-title{display:flex;gap:13px;align-items:flex-start}.pf-shield{width:38px;height:38px;display:grid;place-items:center;border-radius:11px;background:rgba(75,114,255,.15);color:#7c9cff;flex:0 0 auto}.pf-shield svg{width:22px;height:22px}.pf-title h2{font-size:17px;line-height:1.25;margin:1px 0 5px}.pf-title p,.pf-muted{color:var(--muted-foreground,#9da5b4);font-size:12px;margin:0;line-height:1.5}.pf-badge{display:inline-flex;align-items:center;gap:6px;height:25px;padding:0 10px;border-radius:999px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;background:rgba(45,190,125,.13);color:#51d69b}.pf-badge.off{background:rgba(148,154,166,.13);color:#aeb4c1}.pf-dot{width:7px;height:7px;border-radius:50%;background:currentColor}.pf-body{padding:20px 22px;display:grid;gap:18px}.pf-section{display:grid;gap:12px}.pf-section-title{display:flex;justify-content:space-between;align-items:center;gap:12px}.pf-section-title h3{font-size:13px;margin:0}.pf-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:11px}.pf-field{display:grid;gap:6px}.pf-field>span{font-size:11px;color:var(--muted-foreground,#9da5b4);font-weight:600}.pf-control{width:100%;height:36px;border:1px solid var(--border,#343946);border-radius:8px;background:var(--background,#10131a);color:inherit;padding:0 10px;font:inherit;font-size:12px;outline:none}.pf-control:focus{border-color:#6687ff;box-shadow:0 0 0 3px rgba(75,114,255,.12)}.pf-toggle-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid var(--border,#2a2f3a);border-radius:9px}.pf-toggle-copy{display:grid;gap:2px}.pf-toggle-copy strong{font-size:12px}.pf-toggle-copy span{font-size:10px;color:var(--muted-foreground,#9da5b4)}.pf-toggle{appearance:none;width:34px;height:19px;border-radius:999px;background:#4a505c;position:relative;cursor:pointer;transition:.18s;flex:none}.pf-toggle:after{content:'';position:absolute;top:3px;left:3px;width:13px;height:13px;border-radius:50%;background:white;transition:.18s}.pf-toggle:checked{background:#5578f5}.pf-toggle:checked:after{transform:translateX(15px)}.pf-toggle:disabled,.pf-control:disabled,.pf-btn:disabled{cursor:not-allowed;opacity:.45}.pf-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}.pf-stat{padding:12px;border-radius:9px;background:var(--muted,#1c202a);border:1px solid var(--border,#292e39)}.pf-stat b{display:block;font-size:17px;margin-bottom:3px}.pf-stat span{font-size:10px;color:var(--muted-foreground,#9da5b4)}.pf-editor{display:grid;grid-template-columns:1.2fr .8fr .8fr auto;gap:8px}.pf-btn{height:34px;border:1px solid var(--border,#343946);border-radius:8px;background:var(--muted,#20242e);color:inherit;padding:0 12px;font:inherit;font-size:11px;font-weight:650;cursor:pointer;white-space:nowrap}.pf-btn:hover:not(:disabled){border-color:#677080;background:#272c38}.pf-btn.primary{border-color:#5578f5;background:#5578f5;color:white}.pf-btn.danger{color:#ff8791}.pf-btn.link{height:27px;padding:0 8px;background:transparent}.pf-rules{display:grid;gap:6px}.pf-rule{display:grid;grid-template-columns:minmax(0,1fr) 70px 70px auto;gap:9px;align-items:center;padding:7px 9px;border:1px solid var(--border,#292e39);border-radius:8px;font-size:11px}.pf-rule code{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#cbd5ff}.pf-pill{font-size:9px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted-foreground,#9da5b4)}.pf-pill.block{color:#ff8791}.pf-pill.allow{color:#62dba2}.pf-pill.protect{color:#7ca1ff}.pf-advanced{border:1px solid var(--border,#292e39);border-radius:10px;padding:0 12px}.pf-advanced summary{cursor:pointer;padding:11px 0;font-size:12px;font-weight:650}.pf-advanced-content{padding:2px 0 13px;display:grid;gap:10px}.pf-table-wrap{overflow:auto;border:1px solid var(--border,#292e39);border-radius:10px}.pf-table{width:100%;border-collapse:collapse;font-size:11px;min-width:720px}.pf-table th{text-align:left;color:var(--muted-foreground,#9da5b4);font-weight:600;background:var(--muted,#1c202a);padding:8px 10px}.pf-table td{padding:9px 10px;border-top:1px solid var(--border,#292e39);vertical-align:top}.pf-section-name{display:flex;align-items:center;gap:6px;font-family:ui-monospace,SFMono-Regular,Consolas,monospace}.pf-new,.pf-warn{font-family:inherit;font-size:8px;font-weight:800;border-radius:4px;padding:2px 4px;background:rgba(85,120,245,.16);color:#88a5ff}.pf-warn{background:rgba(255,177,75,.14);color:#ffbe68}.pf-actions{display:flex;gap:4px}.pf-preview{margin-top:5px;max-width:360px;white-space:pre-wrap;color:var(--muted-foreground,#9da5b4);font-size:10px;line-height:1.4}.pf-empty{padding:22px;text-align:center;color:var(--muted-foreground,#9da5b4);font-size:12px}.pf-error{padding:9px 11px;border-radius:8px;background:rgba(235,76,88,.12);color:#ff9098;font-size:11px}.pf-footer-note{font-size:10px;color:var(--muted-foreground,#9da5b4)}
5187
- @media(max-width:720px){.pf-head,.pf-body{padding-left:15px;padding-right:15px}.pf-grid{grid-template-columns:1fr}.pf-stats{grid-template-columns:repeat(2,minmax(0,1fr))}.pf-editor{grid-template-columns:1fr 1fr}.pf-editor input{grid-column:1/-1}.pf-rule{grid-template-columns:minmax(0,1fr) 58px auto}.pf-rule .pf-kind{display:none}}
5182
+ //#region ../../packages/plugin-kit/lib/client/plugin-card-css.js
5183
+ /**
5184
+ * Canonical DSH settings-plugin card shell CSS — the single source of truth
5185
+ * for the shared `dsh-plugin-card` outer shell (see AGENTS.md). Plugins must
5186
+ * inject this CSS unchanged and may only append rules for their own controls
5187
+ * inside the card body; plugin-specific shell styling is forbidden.
5188
+ */
5189
+ const PLUGIN_CARD_SHELL_CSS = `
5190
+ .dsh-plugin-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}
5191
+ .dsh-plugin-card:hover{border-color:var(--dsw-alias-label-dimmed)}
5192
+ .dsh-plugin-card--open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
5193
+ .dsh-plugin-card__header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
5194
+ .dsh-plugin-card__header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
5195
+ .dsh-plugin-card__head-text{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
5196
+ .dsh-plugin-card__name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}
5197
+ .dsh-plugin-card__description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}
5198
+ .dsh-plugin-card__badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
5199
+ .dsh-plugin-card__chevron{width:14px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}
5200
+ .dsh-plugin-card--open .dsh-plugin-card__chevron{transform:rotate(180deg)}
5201
+ .dsh-plugin-card__body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}
5188
5202
  `;
5189
5203
  //#endregion
5190
- //#region src/client/index.tsx
5191
- const SETTINGS_NAMESPACE = "prompt-firewall";
5192
- const REFRESH_INTERVAL_MS = 3e3;
5193
- const EMPTY_LIST = Object.freeze([]);
5194
- function list(config, field) {
5195
- return config?.[field] ?? EMPTY_LIST;
5196
- }
5197
- function displayError(error) {
5198
- if (error instanceof Error) return error.message;
5199
- if (typeof error === "string") return error;
5200
- return "Could not load Prompt Inspector data.";
5201
- }
5202
- function Shield() {
5203
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5204
- className: "pf-shield",
5204
+ //#region ../../packages/plugin-kit/lib/client/chevron.js
5205
+ /**
5206
+ * The standard disclosure chevron of the shared settings-card shell: a
5207
+ * 14x14 inline SVG stroked with `currentColor` (round caps and joins).
5208
+ * Font glyphs must not be used for this shape — their appearance and
5209
+ * baseline vary by font and encoding.
5210
+ */
5211
+ function ChevronDown() {
5212
+ return (0, react_jsx_runtime.jsx)("svg", {
5213
+ className: "dsh-plugin-card__chevron",
5214
+ viewBox: "0 0 14 14",
5215
+ fill: "none",
5205
5216
  "aria-hidden": "true",
5206
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
5207
- viewBox: "0 0 24 24",
5208
- fill: "none",
5217
+ children: (0, react_jsx_runtime.jsx)("path", {
5218
+ d: "m3.5 5.25 3.5 3.5 3.5-3.5",
5209
5219
  stroke: "currentColor",
5210
- strokeWidth: "1.8",
5211
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 3 19 6v5c0 4.8-2.8 8.2-7 10-4.2-1.8-7-5.2-7-10V6l7-3Z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m9 12 2 2 4-4" })]
5220
+ strokeLinecap: "round",
5221
+ strokeLinejoin: "round"
5212
5222
  })
5213
5223
  });
5214
5224
  }
5225
+ //#endregion
5226
+ //#region ../../packages/plugin-kit/lib/client/card-shell.js
5227
+ /**
5228
+ * Shared outer shell of a plugin configuration card (AGENTS.md contract): a
5229
+ * direct `<li>` child of the host list with a full-width header button
5230
+ * (`type="button"`, `aria-expanded`, accessible show/hide label, the
5231
+ * title/description stack, an optional status badge, then the chevron) and a
5232
+ * body rendered only while open. Plugin-specific controls belong inside the
5233
+ * body and must not restyle the shell.
5234
+ */
5235
+ function CardShell(props) {
5236
+ const [open, setOpen] = (0, react.useState)(false);
5237
+ const body = props.bodyClassName === void 0 ? "dsh-plugin-card__body" : `dsh-plugin-card__body ${props.bodyClassName}`;
5238
+ return (0, react_jsx_runtime.jsxs)("li", {
5239
+ className: open ? "dsh-plugin-card dsh-plugin-card--open" : "dsh-plugin-card",
5240
+ children: [(0, react_jsx_runtime.jsxs)("button", {
5241
+ type: "button",
5242
+ className: "dsh-plugin-card__header",
5243
+ "aria-expanded": open,
5244
+ "aria-label": props.label(open),
5245
+ onClick: () => {
5246
+ setOpen(!open);
5247
+ },
5248
+ children: [
5249
+ (0, react_jsx_runtime.jsxs)("span", {
5250
+ className: "dsh-plugin-card__head-text",
5251
+ children: [(0, react_jsx_runtime.jsx)("span", {
5252
+ className: "dsh-plugin-card__name",
5253
+ children: props.title
5254
+ }), (0, react_jsx_runtime.jsx)("span", {
5255
+ className: "dsh-plugin-card__description",
5256
+ children: props.description
5257
+ })]
5258
+ }),
5259
+ props.badge ?? null,
5260
+ (0, react_jsx_runtime.jsx)(ChevronDown, {})
5261
+ ]
5262
+ }), open ? (0, react_jsx_runtime.jsx)("div", {
5263
+ className: body,
5264
+ children: props.children
5265
+ }) : null]
5266
+ });
5267
+ }
5268
+ function noop() {}
5269
+ /**
5270
+ * Inject one stylesheet tag for the plugin's card CSS. The tag carries
5271
+ * `data-plugin="<pluginName>"` and is guarded against double injection; the
5272
+ * returned disposer removes the tag when this call created it. No-op in
5273
+ * non-DOM environments (headless bundles, module probes).
5274
+ */
5275
+ function injectCardStyles(pluginName, css) {
5276
+ if (typeof document === "undefined") return noop;
5277
+ const selector = `style[data-plugin="${pluginName}"]`;
5278
+ if (typeof document.querySelector === "function" && document.querySelector(selector) !== null) return noop;
5279
+ const tag = document.createElement("style");
5280
+ tag.dataset.plugin = pluginName;
5281
+ tag.textContent = css;
5282
+ document.head.appendChild(tag);
5283
+ return () => {
5284
+ tag.remove();
5285
+ };
5286
+ }
5287
+ /**
5288
+ * Register the card component into the shared settings-plugins slot. Returns
5289
+ * the registration disposer. Use this variant when the plugin drives the
5290
+ * slot's inject-factory itself (e.g. to dispose a form controller alongside).
5291
+ */
5292
+ function registerSettingsSlot(host, options) {
5293
+ const slotOptions = {
5294
+ name: options.slotName ?? "settings.plugin.item",
5295
+ key: options.key,
5296
+ ...options.locale === void 0 ? {} : { locale: options.locale },
5297
+ ...options.inject === void 0 ? {} : { inject: options.inject }
5298
+ };
5299
+ return host.slots.register(slotOptions, options.component);
5300
+ }
5301
+ /**
5302
+ * Full bootstrap: inject {@link options.styles} once (when provided), then
5303
+ * mount the card into the settings-plugins slot. Returns a disposer removing
5304
+ * the slot effect and the injected stylesheet.
5305
+ */
5306
+ function registerSettingsCard(host, options) {
5307
+ const removeStyles = options.styles === void 0 || options.pluginName === void 0 ? void 0 : injectCardStyles(options.pluginName, options.styles);
5308
+ const slotName = options.slotName ?? "settings.plugin.item";
5309
+ const disposeEffect = host.slots.inject(slotName, () => registerSettingsSlot(host, options));
5310
+ return () => {
5311
+ disposeEffect();
5312
+ removeStyles?.();
5313
+ };
5314
+ }
5315
+ //#endregion
5316
+ //#region ../../packages/plugin-kit/lib/client/settings-store.js
5317
+ /**
5318
+ * React calls external-store callbacks as plain functions, while
5319
+ * SettingsScope's methods depend on their receiver. Forwarding the methods
5320
+ * directly loses `this` and crashes while reading the internal store, so
5321
+ * bind them through stable wrappers.
5322
+ */
5323
+ function bindSettingsExternalStore(scope) {
5324
+ return {
5325
+ subscribe: (listener) => scope.subscribe(listener),
5326
+ getSnapshot: () => scope.getSnapshot()
5327
+ };
5328
+ }
5329
+ //#endregion
5330
+ //#region ../../packages/plugin-kit/lib/client/polling.js
5331
+ function startVisibilityAwarePolling(refresh, intervalMs, visibilityDocument = document, pollingWindow = window) {
5332
+ const refreshWhenVisible = () => {
5333
+ if (!visibilityDocument.hidden) refresh();
5334
+ };
5335
+ const handleVisibilityChange = () => {
5336
+ refreshWhenVisible();
5337
+ };
5338
+ refreshWhenVisible();
5339
+ const timer = pollingWindow.setInterval(refreshWhenVisible, intervalMs);
5340
+ visibilityDocument.addEventListener("visibilitychange", handleVisibilityChange);
5341
+ return () => {
5342
+ pollingWindow.clearInterval(timer);
5343
+ visibilityDocument.removeEventListener("visibilitychange", handleVisibilityChange);
5344
+ };
5345
+ }
5346
+ //#endregion
5347
+ //#region src/client/styles.ts
5348
+ const styles = `${PLUGIN_CARD_SHELL_CSS}
5349
+ .pf-body,.pf-body *{box-sizing:border-box}
5350
+ .pf-body{padding-top:16px;display:grid;gap:18px;color:var(--dsw-alias-label-primary)}
5351
+ .pf-section{display:grid;gap:12px}.pf-section-title{display:flex;justify-content:space-between;align-items:center;gap:12px}.pf-section-title h3{font-size:13px;margin:0}.pf-muted{color:var(--dsw-alias-label-tertiary);font-size:12px;margin:0;line-height:1.5}
5352
+ .pf-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:11px}.pf-field{display:grid;gap:6px}.pf-field>span{font-size:11px;color:var(--dsw-alias-label-secondary);font-weight:600}
5353
+ .pf-control{width:100%;height:36px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);padding:0 10px;font:inherit;font-size:12px;outline:none}.pf-control:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-1px}
5354
+ .pf-toggle-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px}.pf-toggle-copy{display:grid;gap:2px}.pf-toggle-copy strong{font-size:12px;font-weight:500}.pf-toggle-copy span{font-size:10px;color:var(--dsw-alias-label-tertiary)}
5355
+ .pf-toggle{appearance:none;width:34px;height:19px;border-radius:999px;background:var(--dsw-alias-label-dimmed);position:relative;cursor:pointer;transition:.18s;flex:none}.pf-toggle:after{content:'';position:absolute;top:3px;left:3px;width:13px;height:13px;border-radius:50%;background:var(--dsw-alias-bg-layer-3);transition:.18s}.pf-toggle:checked{background:var(--dsw-alias-brand-primary)}.pf-toggle:checked:after{transform:translateX(15px)}
5356
+ .pf-toggle:disabled,.pf-control:disabled,.pf-btn:disabled{cursor:default;opacity:.45}
5357
+ .pf-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}.pf-stat{padding:12px;border-radius:9px;background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2)}.pf-stat b{display:block;font-size:17px;margin-bottom:3px}.pf-stat span{font-size:10px;color:var(--dsw-alias-label-tertiary)}
5358
+ .pf-editor{display:grid;grid-template-columns:1.2fr .8fr .8fr auto;gap:8px}.pf-btn{height:34px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);padding:0 12px;font:inherit;font-size:11px;font-weight:600;cursor:pointer;white-space:nowrap}.pf-btn:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed);background:var(--dsw-alias-interactive-bg-hover)}.pf-btn.primary{border-color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-layer-3)}.pf-btn.danger{color:var(--dsw-alias-label-error)}.pf-btn.link{height:27px;padding:0 8px;background:transparent}
5359
+ .pf-rules{display:grid;gap:6px}.pf-rule{display:grid;grid-template-columns:minmax(0,1fr) 70px 70px auto;gap:9px;align-items:center;padding:7px 9px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;font-size:11px}.pf-rule code{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary)}.pf-pill{font-size:9px;text-transform:uppercase;letter-spacing:.04em;color:var(--dsw-alias-label-tertiary)}.pf-pill.block{color:var(--dsw-alias-label-error)}.pf-pill.allow{color:var(--dsw-alias-state-business-primary)}.pf-pill.protect{color:var(--dsw-alias-brand-primary)}
5360
+ .pf-advanced{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:0 12px}.pf-advanced summary{cursor:pointer;padding:11px 0;font-size:12px;font-weight:600}.pf-advanced-content{padding:2px 0 13px;display:grid;gap:10px}
5361
+ .pf-table-wrap{overflow:auto;border:1px solid var(--dsw-alias-border-l2);border-radius:10px}.pf-table{width:100%;border-collapse:collapse;font-size:11px;min-width:720px}.pf-table th{text-align:left;color:var(--dsw-alias-label-tertiary);font-weight:600;background:var(--dsw-alias-bg-module-platform);padding:8px 10px}.pf-table td{padding:9px 10px;border-top:1px solid var(--dsw-alias-border-l2);vertical-align:top}.pf-section-name{display:flex;align-items:center;gap:6px;font-family:ui-monospace,SFMono-Regular,Consolas,monospace}.pf-new,.pf-warn{font-family:inherit;font-size:8px;font-weight:800;border-radius:4px;padding:2px 4px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-brand-primary)}.pf-warn{color:var(--dsw-alias-label-secondary)}.pf-actions{display:flex;gap:4px}.pf-preview{margin-top:5px;max-width:360px;white-space:pre-wrap;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:1.4}.pf-empty{padding:22px;text-align:center;color:var(--dsw-alias-label-tertiary);font-size:12px}.pf-error{padding:9px 11px;border-radius:8px;background:var(--dsw-alias-bg-error);color:var(--dsw-alias-label-error);font-size:11px}.pf-footer-note{font-size:10px;color:var(--dsw-alias-label-tertiary)}
5362
+ @media(max-width:720px){.pf-grid{grid-template-columns:1fr}.pf-stats{grid-template-columns:repeat(2,minmax(0,1fr))}.pf-editor{grid-template-columns:1fr 1fr}.pf-editor input{grid-column:1/-1}.pf-rule{grid-template-columns:minmax(0,1fr) 58px auto}.pf-rule .pf-kind{display:none}}
5363
+ `;
5364
+ //#endregion
5365
+ //#region src/client/sections.tsx
5366
+ const EMPTY_LIST = Object.freeze([]);
5367
+ function list(config, field) {
5368
+ return config?.[field] ?? EMPTY_LIST;
5369
+ }
5215
5370
  function Toggle(props) {
5216
5371
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5217
5372
  className: "pf-toggle-row",
@@ -5229,66 +5384,163 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5229
5384
  })]
5230
5385
  });
5231
5386
  }
5232
- function PromptFirewallCard({ scope, inspect, setSectionPolicy }) {
5233
- const settings = (0, react.useSyncExternalStore)(scope.subscribe, scope.getSnapshot, scope.getSnapshot);
5234
- const config = settings.value;
5235
- const writable = settings.status === "ready" && settings.writable;
5236
- const [inspector, setInspector] = (0, react.useState)(null);
5237
- const [error, setError] = (0, react.useState)(null);
5238
- const [refreshing, setRefreshing] = (0, react.useState)(false);
5387
+ function PolicySection(props) {
5388
+ const { config, writable, setPath } = props;
5389
+ const configuredMode = config?.mode ?? (config?.preset === "strict" ? "allowlist" : config?.preset === "audit-only" ? "audit" : "blocklist");
5390
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5391
+ className: "pf-section",
5392
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5393
+ className: "pf-section-title",
5394
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "Policy" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5395
+ className: "pf-muted",
5396
+ children: "Changes apply live"
5397
+ })]
5398
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5399
+ className: "pf-grid",
5400
+ children: [
5401
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5402
+ checked: config?.enabled ?? true,
5403
+ disabled: !writable,
5404
+ label: "Firewall enabled",
5405
+ hint: "Keep auditing available when policy is off.",
5406
+ onChange: (value) => {
5407
+ setPath(["enabled"], value);
5408
+ }
5409
+ }),
5410
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5411
+ checked: config?.protectCoreSections ?? true,
5412
+ disabled: !writable,
5413
+ label: "Protect core sections",
5414
+ hint: "Prevents ordinary rules from removing Harness internals.",
5415
+ onChange: (value) => {
5416
+ setPath(["protectCoreSections"], value);
5417
+ }
5418
+ }),
5419
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5420
+ className: "pf-field",
5421
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Mode" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5422
+ className: "pf-control",
5423
+ value: configuredMode,
5424
+ disabled: !writable,
5425
+ onChange: (event) => {
5426
+ setPath(["mode"], event.currentTarget.value);
5427
+ },
5428
+ children: [
5429
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5430
+ value: "off",
5431
+ children: "Off"
5432
+ }),
5433
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5434
+ value: "audit",
5435
+ children: "Audit only"
5436
+ }),
5437
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5438
+ value: "blocklist",
5439
+ children: "Blocklist"
5440
+ }),
5441
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5442
+ value: "allowlist",
5443
+ children: "Allowlist"
5444
+ })
5445
+ ]
5446
+ })]
5447
+ }),
5448
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5449
+ className: "pf-field",
5450
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Preset" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5451
+ className: "pf-control",
5452
+ value: config?.preset ?? "",
5453
+ disabled: !writable,
5454
+ onChange: (event) => {
5455
+ const value = event.currentTarget.value;
5456
+ if (value === "") props.unsetPreset();
5457
+ else setPath(["preset"], value);
5458
+ },
5459
+ children: [
5460
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5461
+ value: "",
5462
+ children: "None"
5463
+ }),
5464
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5465
+ value: "clean",
5466
+ children: "Clean"
5467
+ }),
5468
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5469
+ value: "strict",
5470
+ children: "Strict"
5471
+ }),
5472
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5473
+ value: "audit-only",
5474
+ children: "Audit only"
5475
+ })
5476
+ ]
5477
+ })]
5478
+ }),
5479
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5480
+ className: "pf-field",
5481
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Unknown plugin sections" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5482
+ className: "pf-control",
5483
+ value: config?.unknownPluginPolicy ?? "allow",
5484
+ disabled: !writable,
5485
+ onChange: (event) => {
5486
+ setPath(["unknownPluginPolicy"], event.currentTarget.value);
5487
+ },
5488
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5489
+ value: "allow",
5490
+ children: "Allow"
5491
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5492
+ value: "block",
5493
+ children: "Block"
5494
+ })]
5495
+ })]
5496
+ })
5497
+ ]
5498
+ })]
5499
+ });
5500
+ }
5501
+ function LastRequestSection(props) {
5502
+ const last = props.inspector?.last ?? null;
5503
+ const effectiveMode = props.inspector?.config.mode ?? props.config?.mode ?? "blocklist";
5504
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5505
+ className: "pf-section",
5506
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5507
+ className: "pf-section-title",
5508
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "Last request" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5509
+ className: "pf-btn",
5510
+ disabled: props.refreshing,
5511
+ onClick: () => {
5512
+ props.onRefresh();
5513
+ },
5514
+ children: props.refreshing ? "Refreshing…" : "Refresh"
5515
+ })]
5516
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5517
+ className: "pf-stats",
5518
+ children: [
5519
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5520
+ className: "pf-stat",
5521
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: last?.totalSections ?? "—" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "sections" })]
5522
+ }),
5523
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5524
+ className: "pf-stat",
5525
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: last?.blockedSections ?? "—" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "blocked" })]
5526
+ }),
5527
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5528
+ className: "pf-stat",
5529
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: last === null ? "—" : "~" + String(last.estimatedTokensRemoved) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "tokens removed (estimate)" })]
5530
+ }),
5531
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5532
+ className: "pf-stat",
5533
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: effectiveMode }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "effective mode" })]
5534
+ })
5535
+ ]
5536
+ })]
5537
+ });
5538
+ }
5539
+ function RulesSection(props) {
5540
+ const { config, writable, setPath } = props;
5239
5541
  const [rule, setRule] = (0, react.useState)("");
5240
5542
  const [ruleKind, setRuleKind] = (0, react.useState)("exact");
5241
5543
  const [ruleAction, setRuleAction] = (0, react.useState)("block");
5242
- const activeRequest = (0, react.useRef)(0);
5243
- const refresh = (0, react.useCallback)(async () => {
5244
- const request = ++activeRequest.current;
5245
- setRefreshing(true);
5246
- try {
5247
- const result = await inspect();
5248
- if (request !== activeRequest.current) return;
5249
- if (result.ok) {
5250
- setInspector(result.value);
5251
- setError(null);
5252
- } else setError(displayError(result.error));
5253
- } catch (cause) {
5254
- if (request === activeRequest.current) setError(displayError(cause));
5255
- } finally {
5256
- if (request === activeRequest.current) setRefreshing(false);
5257
- }
5258
- }, [inspect]);
5259
- (0, react.useEffect)(() => {
5260
- refresh();
5261
- const interval = window.setInterval(() => {
5262
- refresh();
5263
- }, REFRESH_INTERVAL_MS);
5264
- return () => {
5265
- window.clearInterval(interval);
5266
- activeRequest.current += 1;
5267
- };
5268
- }, [refresh]);
5269
- const setPath = (0, react.useCallback)((path, value) => {
5270
- const [field, nested] = path;
5271
- if (field === void 0) return;
5272
- if (nested === void 0) {
5273
- scope.set(field, value);
5274
- return;
5275
- }
5276
- const current = scope.getSnapshot().value;
5277
- const parent = field === "audit" ? current?.audit : current?.metrics;
5278
- scope.set(field, {
5279
- ...parent,
5280
- [nested]: value
5281
- });
5282
- }, [scope]);
5283
- const setPolicy = (0, react.useCallback)(async (name, policy) => {
5284
- const result = await setSectionPolicy(name, policy, scope.getSnapshot().revision);
5285
- if (!result.ok) setError(displayError(result.error));
5286
- await refresh();
5287
- }, [
5288
- refresh,
5289
- scope,
5290
- setSectionPolicy
5291
- ]);
5292
5544
  const addRule = (event) => {
5293
5545
  event.preventDefault();
5294
5546
  const value = rule.trim();
@@ -5342,480 +5594,427 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5342
5594
  action: "block"
5343
5595
  }))
5344
5596
  ];
5345
- const enabled = config?.enabled ?? true;
5346
- const last = inspector?.last ?? null;
5347
- const effectiveMode = inspector?.config.mode ?? config?.mode ?? "blocklist";
5348
- const configuredMode = config?.mode ?? (config?.preset === "strict" ? "allowlist" : config?.preset === "audit-only" ? "audit" : "blocklist");
5349
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
5350
- className: "pf-card",
5351
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
5352
- className: "pf-head",
5353
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5354
- className: "pf-title",
5355
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Shield, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: "Prompt Firewall" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "Prompt hygiene, section policy, and request-level observability." })] })]
5356
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5357
- className: `pf-badge${enabled ? "" : " off"}`,
5358
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", { className: "pf-dot" }), enabled ? "Enabled" : "Disabled"]
5359
- })]
5360
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5361
- className: "pf-body",
5362
- children: [
5363
- settings.status === "unavailable" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5364
- className: "pf-error",
5365
- children: "Settings are unavailable for this connection."
5366
- }),
5367
- error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5368
- className: "pf-error",
5369
- children: error
5370
- }),
5371
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5372
- className: "pf-section",
5373
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5374
- className: "pf-section-title",
5375
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "Policy" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5376
- className: "pf-muted",
5377
- children: "Changes apply live"
5378
- })]
5379
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5380
- className: "pf-grid",
5597
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5598
+ className: "pf-section",
5599
+ children: [
5600
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5601
+ className: "pf-section-title",
5602
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "Rules" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5603
+ className: "pf-muted",
5604
+ children: "Explicit rules; preset rules are applied in addition"
5605
+ })]
5606
+ }),
5607
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
5608
+ className: "pf-editor",
5609
+ onSubmit: addRule,
5610
+ children: [
5611
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5612
+ className: "pf-control",
5613
+ value: rule,
5614
+ disabled: !writable,
5615
+ placeholder: "plugin:example or announcement:*",
5616
+ onChange: (event) => {
5617
+ setRule(event.currentTarget.value);
5618
+ }
5619
+ }),
5620
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5621
+ className: "pf-control",
5622
+ value: ruleKind,
5623
+ disabled: !writable || ruleAction === "protect",
5624
+ onChange: (event) => {
5625
+ setRuleKind(event.currentTarget.value);
5626
+ },
5381
5627
  children: [
5382
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5383
- checked: enabled,
5384
- disabled: !writable,
5385
- label: "Firewall enabled",
5386
- hint: "Keep auditing available when policy is off.",
5387
- onChange: (value) => {
5388
- setPath(["enabled"], value);
5389
- }
5390
- }),
5391
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5392
- checked: config?.protectCoreSections ?? true,
5393
- disabled: !writable,
5394
- label: "Protect core sections",
5395
- hint: "Prevents ordinary rules from removing Harness internals.",
5396
- onChange: (value) => {
5397
- setPath(["protectCoreSections"], value);
5398
- }
5628
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5629
+ value: "exact",
5630
+ children: "Exact"
5399
5631
  }),
5400
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5401
- className: "pf-field",
5402
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Mode" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5403
- className: "pf-control",
5404
- value: configuredMode,
5405
- disabled: !writable,
5406
- onChange: (event) => {
5407
- setPath(["mode"], event.currentTarget.value);
5408
- },
5409
- children: [
5410
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5411
- value: "off",
5412
- children: "Off"
5413
- }),
5414
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5415
- value: "audit",
5416
- children: "Audit only"
5417
- }),
5418
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5419
- value: "blocklist",
5420
- children: "Blocklist"
5421
- }),
5422
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5423
- value: "allowlist",
5424
- children: "Allowlist"
5425
- })
5426
- ]
5427
- })]
5428
- }),
5429
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5430
- className: "pf-field",
5431
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Preset" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5432
- className: "pf-control",
5433
- value: config?.preset ?? "",
5434
- disabled: !writable,
5435
- onChange: (event) => {
5436
- const value = event.currentTarget.value;
5437
- if (value === "") scope.unset("preset");
5438
- else setPath(["preset"], value);
5439
- },
5440
- children: [
5441
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5442
- value: "",
5443
- children: "None"
5444
- }),
5445
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5446
- value: "clean",
5447
- children: "Clean"
5448
- }),
5449
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5450
- value: "strict",
5451
- children: "Strict"
5452
- }),
5453
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5454
- value: "audit-only",
5455
- children: "Audit only"
5456
- })
5457
- ]
5458
- })]
5632
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5633
+ value: "prefix",
5634
+ children: "Prefix"
5459
5635
  }),
5460
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5461
- className: "pf-field",
5462
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Unknown plugin sections" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5463
- className: "pf-control",
5464
- value: config?.unknownPluginPolicy ?? "allow",
5465
- disabled: !writable,
5466
- onChange: (event) => {
5467
- setPath(["unknownPluginPolicy"], event.currentTarget.value);
5468
- },
5469
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5470
- value: "allow",
5471
- children: "Allow"
5472
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5473
- value: "block",
5474
- children: "Block"
5475
- })]
5476
- })]
5636
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5637
+ value: "glob",
5638
+ children: "Glob"
5477
5639
  })
5478
5640
  ]
5479
- })]
5480
- }),
5481
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5482
- className: "pf-section",
5483
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5484
- className: "pf-section-title",
5485
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "Last request" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5486
- className: "pf-btn",
5487
- disabled: refreshing,
5488
- onClick: () => {
5489
- refresh();
5490
- },
5491
- children: refreshing ? "Refreshing…" : "Refresh"
5492
- })]
5493
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5494
- className: "pf-stats",
5641
+ }),
5642
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5643
+ className: "pf-control",
5644
+ value: ruleAction,
5645
+ disabled: !writable,
5646
+ onChange: (event) => {
5647
+ const action = event.currentTarget.value;
5648
+ setRuleAction(action);
5649
+ if (action === "protect") setRuleKind("exact");
5650
+ },
5495
5651
  children: [
5496
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5497
- className: "pf-stat",
5498
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: last?.totalSections ?? "—" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "sections" })]
5499
- }),
5500
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5501
- className: "pf-stat",
5502
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: last?.blockedSections ?? "—" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "blocked" })]
5652
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5653
+ value: "block",
5654
+ children: "Block"
5503
5655
  }),
5504
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5505
- className: "pf-stat",
5506
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: last === null ? "—" : `~${last.estimatedTokensRemoved}` }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "tokens removed (estimate)" })]
5656
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5657
+ value: "allow",
5658
+ children: "Allow"
5507
5659
  }),
5508
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5509
- className: "pf-stat",
5510
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: effectiveMode }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "effective mode" })]
5660
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5661
+ value: "protect",
5662
+ children: "Protect"
5511
5663
  })
5512
5664
  ]
5665
+ }),
5666
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5667
+ className: "pf-btn primary",
5668
+ type: "submit",
5669
+ disabled: !writable || rule.trim().length === 0,
5670
+ children: "Add rule"
5671
+ })
5672
+ ]
5673
+ }),
5674
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5675
+ className: "pf-rules",
5676
+ children: [explicitRules.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5677
+ className: "pf-empty",
5678
+ children: "No explicit rules yet."
5679
+ }), explicitRules.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5680
+ className: "pf-rule",
5681
+ children: [
5682
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
5683
+ title: item.value,
5684
+ children: item.value
5685
+ }),
5686
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5687
+ className: "pf-pill pf-kind",
5688
+ children: item.kind
5689
+ }),
5690
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5691
+ className: `pf-pill ${item.action}`,
5692
+ children: item.action
5693
+ }),
5694
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5695
+ className: "pf-btn link danger",
5696
+ disabled: !writable,
5697
+ onClick: () => {
5698
+ setPath([item.field], list(config, item.field).filter((value) => value !== item.value));
5699
+ },
5700
+ children: "Remove"
5701
+ })
5702
+ ]
5703
+ }, `${item.field}:${item.value}`))]
5704
+ })
5705
+ ]
5706
+ });
5707
+ }
5708
+ function AuditSection(props) {
5709
+ const { config, writable, setPath } = props;
5710
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
5711
+ className: "pf-advanced",
5712
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: "Audit & metrics" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5713
+ className: "pf-advanced-content pf-grid",
5714
+ children: [
5715
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5716
+ checked: config?.audit?.enabled ?? true,
5717
+ disabled: !writable,
5718
+ label: "Audit history",
5719
+ hint: "Keep recent assemblies in Host memory.",
5720
+ onChange: (value) => {
5721
+ setPath(["audit", "enabled"], value);
5722
+ }
5723
+ }),
5724
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5725
+ checked: config?.audit?.includePreview ?? false,
5726
+ disabled: !writable,
5727
+ label: "Section preview",
5728
+ hint: "Expose only the configured prefix in Inspector.",
5729
+ onChange: (value) => {
5730
+ setPath(["audit", "includePreview"], value);
5731
+ }
5732
+ }),
5733
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5734
+ checked: config?.audit?.logBlocked ?? true,
5735
+ disabled: !writable,
5736
+ label: "Log blocked sections",
5737
+ hint: "Names and sizes only unless preview is enabled.",
5738
+ onChange: (value) => {
5739
+ setPath(["audit", "logBlocked"], value);
5740
+ }
5741
+ }),
5742
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5743
+ checked: config?.audit?.logAllowed ?? false,
5744
+ disabled: !writable,
5745
+ label: "Log allowed sections",
5746
+ hint: "Useful for initial policy discovery.",
5747
+ onChange: (value) => {
5748
+ setPath(["audit", "logAllowed"], value);
5749
+ }
5750
+ }),
5751
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5752
+ checked: config?.audit?.highlightNewSections ?? true,
5753
+ disabled: !writable,
5754
+ label: "Highlight new plugin sections",
5755
+ hint: "Informational only; never blocks automatically.",
5756
+ onChange: (value) => {
5757
+ setPath(["audit", "highlightNewSections"], value);
5758
+ }
5759
+ }),
5760
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5761
+ checked: config?.metrics?.enabled ?? true,
5762
+ disabled: !writable,
5763
+ label: "Aggregate metrics",
5764
+ hint: "No arbitrary section-name labels.",
5765
+ onChange: (value) => {
5766
+ setPath(["metrics", "enabled"], value);
5767
+ }
5768
+ }),
5769
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5770
+ className: "pf-field",
5771
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Preview characters" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5772
+ className: "pf-control",
5773
+ type: "number",
5774
+ min: "0",
5775
+ step: "1",
5776
+ value: config?.audit?.previewChars ?? 160,
5777
+ disabled: !writable,
5778
+ onChange: (event) => {
5779
+ setPath(["audit", "previewChars"], Number(event.currentTarget.value));
5780
+ }
5513
5781
  })]
5514
5782
  }),
5515
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5516
- className: "pf-section",
5517
- children: [
5518
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5519
- className: "pf-section-title",
5520
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "Rules" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5783
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5784
+ className: "pf-field",
5785
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "History size" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5786
+ className: "pf-control",
5787
+ type: "number",
5788
+ min: "0",
5789
+ step: "1",
5790
+ value: config?.audit?.historySize ?? 100,
5791
+ disabled: !writable,
5792
+ onChange: (event) => {
5793
+ setPath(["audit", "historySize"], Number(event.currentTarget.value));
5794
+ }
5795
+ })]
5796
+ })
5797
+ ]
5798
+ })]
5799
+ });
5800
+ }
5801
+ function InspectorSection(props) {
5802
+ const last = props.inspector?.last ?? null;
5803
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5804
+ className: "pf-section",
5805
+ children: [
5806
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5807
+ className: "pf-section-title",
5808
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "Prompt Inspector" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5809
+ className: "pf-muted",
5810
+ children: "Updates every 3 seconds"
5811
+ })]
5812
+ }),
5813
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5814
+ className: "pf-table-wrap",
5815
+ children: last === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5816
+ className: "pf-empty",
5817
+ children: "No audited prompt assembly yet."
5818
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
5819
+ className: "pf-table",
5820
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
5821
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Section" }),
5822
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Size" }),
5823
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Decision" }),
5824
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Reason" }),
5825
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Policy" })
5826
+ ] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: last.sections.map((section, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
5827
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5828
+ className: "pf-section-name",
5829
+ children: [
5830
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: section.name }),
5831
+ section.isNew && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5832
+ className: "pf-new",
5833
+ children: "NEW"
5834
+ }),
5835
+ section.suspicious && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5836
+ className: "pf-warn",
5837
+ children: "POSSIBLE ANNOUNCEMENT"
5838
+ })
5839
+ ]
5840
+ }), section.preview !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5841
+ className: "pf-preview",
5842
+ children: section.preview
5843
+ })] }),
5844
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [
5845
+ "~",
5846
+ section.estimatedTokens,
5847
+ " t",
5848
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}),
5849
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5521
5850
  className: "pf-muted",
5522
- children: "Explicit rules; preset rules are applied in addition"
5523
- })]
5524
- }),
5525
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
5526
- className: "pf-editor",
5527
- onSubmit: addRule,
5851
+ children: [section.chars, " chars"]
5852
+ })
5853
+ ] }),
5854
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5855
+ className: `pf-pill ${section.decision === "blocked" ? "block" : section.decision === "protected" ? "protect" : "allow"}`,
5856
+ children: section.decision
5857
+ }) }),
5858
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: section.reason }),
5859
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5860
+ className: "pf-actions",
5528
5861
  children: [
5529
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5530
- className: "pf-control",
5531
- value: rule,
5532
- disabled: !writable,
5533
- placeholder: "plugin:example or announcement:*",
5534
- onChange: (event) => {
5535
- setRule(event.currentTarget.value);
5536
- }
5862
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5863
+ className: "pf-btn link",
5864
+ disabled: !props.writable,
5865
+ onClick: () => {
5866
+ props.setPolicy(section.name, "allow");
5867
+ },
5868
+ children: "Allow"
5537
5869
  }),
5538
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5539
- className: "pf-control",
5540
- value: ruleKind,
5541
- disabled: !writable || ruleAction === "protect",
5542
- onChange: (event) => {
5543
- setRuleKind(event.currentTarget.value);
5870
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5871
+ className: "pf-btn link danger",
5872
+ disabled: !props.writable,
5873
+ onClick: () => {
5874
+ props.setPolicy(section.name, "block");
5544
5875
  },
5545
- children: [
5546
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5547
- value: "exact",
5548
- children: "Exact"
5549
- }),
5550
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5551
- value: "prefix",
5552
- children: "Prefix"
5553
- }),
5554
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5555
- value: "glob",
5556
- children: "Glob"
5557
- })
5558
- ]
5876
+ children: "Block"
5559
5877
  }),
5560
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5561
- className: "pf-control",
5562
- value: ruleAction,
5563
- disabled: !writable,
5564
- onChange: (event) => {
5565
- const action = event.currentTarget.value;
5566
- setRuleAction(action);
5567
- if (action === "protect") setRuleKind("exact");
5878
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5879
+ className: "pf-btn link",
5880
+ disabled: !props.writable,
5881
+ onClick: () => {
5882
+ props.setPolicy(section.name, "protect");
5568
5883
  },
5569
- children: [
5570
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5571
- value: "block",
5572
- children: "Block"
5573
- }),
5574
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5575
- value: "allow",
5576
- children: "Allow"
5577
- }),
5578
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5579
- value: "protect",
5580
- children: "Protect"
5581
- })
5582
- ]
5884
+ children: "Protect"
5583
5885
  }),
5584
5886
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5585
- className: "pf-btn primary",
5586
- type: "submit",
5587
- disabled: !writable || rule.trim().length === 0,
5588
- children: "Add rule"
5887
+ className: "pf-btn link",
5888
+ disabled: !props.writable,
5889
+ onClick: () => {
5890
+ props.setPolicy(section.name, "clear");
5891
+ },
5892
+ children: "Clear"
5589
5893
  })
5590
5894
  ]
5591
- }),
5592
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5593
- className: "pf-rules",
5594
- children: [explicitRules.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5595
- className: "pf-empty",
5596
- children: "No explicit rules yet."
5597
- }), explicitRules.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5598
- className: "pf-rule",
5599
- children: [
5600
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
5601
- title: item.value,
5602
- children: item.value
5603
- }),
5604
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5605
- className: "pf-pill pf-kind",
5606
- children: item.kind
5607
- }),
5608
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5609
- className: `pf-pill ${item.action}`,
5610
- children: item.action
5611
- }),
5612
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5613
- className: "pf-btn link danger",
5614
- disabled: !writable,
5615
- onClick: () => {
5616
- setPath([item.field], list(config, item.field).filter((value) => value !== item.value));
5617
- },
5618
- children: "Remove"
5619
- })
5620
- ]
5621
- }, `${item.field}:${item.value}`))]
5622
- })
5623
- ]
5624
- }),
5625
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
5626
- className: "pf-advanced",
5627
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: "Audit & metrics" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5628
- className: "pf-advanced-content pf-grid",
5629
- children: [
5630
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5631
- checked: config?.audit?.enabled ?? true,
5632
- disabled: !writable,
5633
- label: "Audit history",
5634
- hint: "Keep recent assemblies in Host memory.",
5635
- onChange: (value) => {
5636
- setPath(["audit", "enabled"], value);
5637
- }
5638
- }),
5639
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5640
- checked: config?.audit?.includePreview ?? false,
5641
- disabled: !writable,
5642
- label: "Section preview",
5643
- hint: "Expose only the configured prefix in Inspector.",
5644
- onChange: (value) => {
5645
- setPath(["audit", "includePreview"], value);
5646
- }
5647
- }),
5648
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5649
- checked: config?.audit?.logBlocked ?? true,
5650
- disabled: !writable,
5651
- label: "Log blocked sections",
5652
- hint: "Names and sizes only unless preview is enabled.",
5653
- onChange: (value) => {
5654
- setPath(["audit", "logBlocked"], value);
5655
- }
5656
- }),
5657
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5658
- checked: config?.audit?.logAllowed ?? false,
5659
- disabled: !writable,
5660
- label: "Log allowed sections",
5661
- hint: "Useful for initial policy discovery.",
5662
- onChange: (value) => {
5663
- setPath(["audit", "logAllowed"], value);
5664
- }
5665
- }),
5666
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5667
- checked: config?.audit?.highlightNewSections ?? true,
5668
- disabled: !writable,
5669
- label: "Highlight new plugin sections",
5670
- hint: "Informational only; never blocks automatically.",
5671
- onChange: (value) => {
5672
- setPath(["audit", "highlightNewSections"], value);
5673
- }
5674
- }),
5675
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
5676
- checked: config?.metrics?.enabled ?? true,
5677
- disabled: !writable,
5678
- label: "Aggregate metrics",
5679
- hint: "No arbitrary section-name labels.",
5680
- onChange: (value) => {
5681
- setPath(["metrics", "enabled"], value);
5682
- }
5683
- }),
5684
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5685
- className: "pf-field",
5686
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Preview characters" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5687
- className: "pf-control",
5688
- type: "number",
5689
- min: "0",
5690
- step: "1",
5691
- value: config?.audit?.previewChars ?? 160,
5692
- disabled: !writable,
5693
- onChange: (event) => {
5694
- setPath(["audit", "previewChars"], Number(event.currentTarget.value));
5695
- }
5696
- })]
5697
- }),
5698
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5699
- className: "pf-field",
5700
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "History size" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5701
- className: "pf-control",
5702
- type: "number",
5703
- min: "0",
5704
- step: "1",
5705
- value: config?.audit?.historySize ?? 100,
5706
- disabled: !writable,
5707
- onChange: (event) => {
5708
- setPath(["audit", "historySize"], Number(event.currentTarget.value));
5709
- }
5710
- })]
5711
- })
5712
- ]
5713
- })]
5714
- }),
5715
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5716
- className: "pf-section",
5717
- children: [
5718
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5719
- className: "pf-section-title",
5720
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "Prompt Inspector" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5721
- className: "pf-muted",
5722
- children: "Updates every 3 seconds"
5723
- })]
5724
- }),
5725
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5726
- className: "pf-table-wrap",
5727
- children: last === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5728
- className: "pf-empty",
5729
- children: "No audited prompt assembly yet."
5730
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
5731
- className: "pf-table",
5732
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
5733
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Section" }),
5734
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Size" }),
5735
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Decision" }),
5736
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Reason" }),
5737
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Policy" })
5738
- ] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: last.sections.map((section, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
5739
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5740
- className: "pf-section-name",
5741
- children: [
5742
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: section.name }),
5743
- section.isNew && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5744
- className: "pf-new",
5745
- children: "NEW"
5746
- }),
5747
- section.suspicious && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5748
- className: "pf-warn",
5749
- children: "POSSIBLE ANNOUNCEMENT"
5750
- })
5751
- ]
5752
- }), section.preview !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5753
- className: "pf-preview",
5754
- children: section.preview
5755
- })] }),
5756
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [
5757
- "~",
5758
- section.estimatedTokens,
5759
- " t",
5760
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}),
5761
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5762
- className: "pf-muted",
5763
- children: [section.chars, " chars"]
5764
- })
5765
- ] }),
5766
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5767
- className: `pf-pill ${section.decision === "blocked" ? "block" : section.decision === "protected" ? "protect" : "allow"}`,
5768
- children: section.decision
5769
- }) }),
5770
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: section.reason }),
5771
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5772
- className: "pf-actions",
5773
- children: [
5774
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5775
- className: "pf-btn link",
5776
- disabled: !writable,
5777
- onClick: () => {
5778
- setPolicy(section.name, "allow");
5779
- },
5780
- children: "Allow"
5781
- }),
5782
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5783
- className: "pf-btn link danger",
5784
- disabled: !writable,
5785
- onClick: () => {
5786
- setPolicy(section.name, "block");
5787
- },
5788
- children: "Block"
5789
- }),
5790
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5791
- className: "pf-btn link",
5792
- disabled: !writable,
5793
- onClick: () => {
5794
- setPolicy(section.name, "protect");
5795
- },
5796
- children: "Protect"
5797
- }),
5798
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5799
- className: "pf-btn link",
5800
- disabled: !writable,
5801
- onClick: () => {
5802
- setPolicy(section.name, "clear");
5803
- },
5804
- children: "Clear"
5805
- })
5806
- ]
5807
- }) })
5808
- ] }, `${section.name}:${index}`)) })]
5809
- })
5810
- }),
5811
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5812
- className: "pf-footer-note",
5813
- children: "Token counts are estimates. Prompt Firewall is a hygiene and observability layer, not a security boundary."
5814
- })
5815
- ]
5895
+ }) })
5896
+ ] }, `${section.name}:${index}`)) })]
5816
5897
  })
5817
- ]
5818
- })]
5898
+ }),
5899
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5900
+ className: "pf-footer-note",
5901
+ children: "Token counts are estimates. Prompt Firewall is a hygiene and observability layer, not a security boundary."
5902
+ })
5903
+ ]
5904
+ });
5905
+ }
5906
+ //#endregion
5907
+ //#region src/client/index.tsx
5908
+ const SETTINGS_NAMESPACE = "prompt-firewall";
5909
+ const REFRESH_INTERVAL_MS = 3e3;
5910
+ function displayError(error) {
5911
+ if (error instanceof Error) return error.message;
5912
+ if (typeof error === "string") return error;
5913
+ return "Could not load Prompt Inspector data.";
5914
+ }
5915
+ function PromptFirewallCard({ scope, inspect, setSectionPolicy }) {
5916
+ const settingsStore = (0, react.useMemo)(() => bindSettingsExternalStore(scope), [scope]);
5917
+ const settings = (0, react.useSyncExternalStore)(settingsStore.subscribe, settingsStore.getSnapshot, settingsStore.getSnapshot);
5918
+ const config = settings.value;
5919
+ const writable = settings.status === "ready" && settings.writable;
5920
+ const [inspector, setInspector] = (0, react.useState)(null);
5921
+ const [error, setError] = (0, react.useState)(null);
5922
+ const [refreshing, setRefreshing] = (0, react.useState)(false);
5923
+ const activeRequest = (0, react.useRef)(0);
5924
+ const refresh = (0, react.useCallback)(async () => {
5925
+ const request = ++activeRequest.current;
5926
+ setRefreshing(true);
5927
+ try {
5928
+ const result = await inspect();
5929
+ if (request !== activeRequest.current) return;
5930
+ if (result.ok) {
5931
+ setInspector(result.value);
5932
+ setError(null);
5933
+ } else setError(displayError(result.error));
5934
+ } catch (cause) {
5935
+ if (request === activeRequest.current) setError(displayError(cause));
5936
+ } finally {
5937
+ if (request === activeRequest.current) setRefreshing(false);
5938
+ }
5939
+ }, [inspect]);
5940
+ (0, react.useEffect)(() => {
5941
+ const stopPolling = startVisibilityAwarePolling(refresh, REFRESH_INTERVAL_MS);
5942
+ return () => {
5943
+ stopPolling();
5944
+ activeRequest.current += 1;
5945
+ };
5946
+ }, [refresh]);
5947
+ const setPath = (0, react.useCallback)((path, value) => {
5948
+ const [field, nested] = path;
5949
+ if (field === void 0) return;
5950
+ if (nested === void 0) {
5951
+ scope.set(field, value);
5952
+ return;
5953
+ }
5954
+ const current = scope.getSnapshot().value;
5955
+ const parent = field === "audit" ? current?.audit : current?.metrics;
5956
+ scope.set(field, {
5957
+ ...parent,
5958
+ [nested]: value
5959
+ });
5960
+ }, [scope]);
5961
+ const setPolicy = (0, react.useCallback)(async (name, policy) => {
5962
+ const result = await setSectionPolicy(name, policy, scope.getSnapshot().revision);
5963
+ if (!result.ok) setError(displayError(result.error));
5964
+ await refresh();
5965
+ }, [
5966
+ refresh,
5967
+ scope,
5968
+ setSectionPolicy
5969
+ ]);
5970
+ const enabled = config?.enabled ?? true;
5971
+ if (settings.status === "unavailable") return null;
5972
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CardShell, {
5973
+ title: "Prompt Firewall",
5974
+ description: "Prompt hygiene, section policy, and request-level observability.",
5975
+ badge: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5976
+ className: "dsh-plugin-card__badge",
5977
+ children: enabled ? "Enabled" : "Disabled"
5978
+ }),
5979
+ label: (open) => `${open ? "Hide" : "Show"} settings: Prompt Firewall`,
5980
+ bodyClassName: "pf-body",
5981
+ children: [
5982
+ error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5983
+ className: "pf-error",
5984
+ children: error
5985
+ }),
5986
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PolicySection, {
5987
+ config,
5988
+ writable,
5989
+ setPath,
5990
+ unsetPreset: () => {
5991
+ scope.unset("preset");
5992
+ }
5993
+ }),
5994
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LastRequestSection, {
5995
+ config,
5996
+ inspector,
5997
+ refreshing,
5998
+ onRefresh: () => {
5999
+ refresh();
6000
+ }
6001
+ }),
6002
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RulesSection, {
6003
+ config,
6004
+ writable,
6005
+ setPath
6006
+ }),
6007
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AuditSection, {
6008
+ config,
6009
+ writable,
6010
+ setPath
6011
+ }),
6012
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InspectorSection, {
6013
+ inspector,
6014
+ writable,
6015
+ setPolicy
6016
+ })
6017
+ ]
5819
6018
  });
5820
6019
  }
5821
6020
  const inject = [
@@ -5825,27 +6024,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5825
6024
  ];
5826
6025
  /** Mount the generated Remote contribution and register the native Settings card. */
5827
6026
  async function apply(ctx) {
5828
- const remote = ctx.remote;
5829
- const disposeRemote = await remote.$mount(TYPERT_REMOTE);
5830
- const face = {
5831
- scope: ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE }),
5832
- inspect: () => remote.promptFirewall.inspect(),
5833
- setSectionPolicy: (section, policy, revision) => remote.promptFirewall.setSectionPolicy(section, policy, revision)
5834
- };
5835
- const style = document.createElement("style");
5836
- style.dataset.plugin = "dsh-prompt-firewall";
5837
- style.textContent = styles;
5838
- document.head.appendChild(style);
5839
- const disposeSlot = ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
5840
- name: "settings.plugin.item",
5841
- key: SETTINGS_NAMESPACE,
5842
- inject: () => face
5843
- }, PromptFirewallCard));
5844
- return async () => {
5845
- disposeSlot();
5846
- style.remove();
6027
+ const disposeRemote = await ctx.remote.$mount(TYPERT_REMOTE);
6028
+ try {
6029
+ await ctx.inject(["remote.promptFirewall"], (remoteCtx) => {
6030
+ const injectedRemote = remoteCtx.remote;
6031
+ const face = {
6032
+ scope: remoteCtx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE }),
6033
+ inspect: () => injectedRemote.promptFirewall.inspect(),
6034
+ setSectionPolicy: (section, policy, revision) => injectedRemote.promptFirewall.setSectionPolicy(section, policy, revision)
6035
+ };
6036
+ return registerSettingsCard(remoteCtx, {
6037
+ key: SETTINGS_NAMESPACE,
6038
+ pluginName: "dsh-prompt-firewall",
6039
+ styles,
6040
+ component: PromptFirewallCard,
6041
+ inject: () => face
6042
+ });
6043
+ });
6044
+ } catch (cause) {
5847
6045
  await disposeRemote();
5848
- };
6046
+ throw cause;
6047
+ }
6048
+ return disposeRemote;
5849
6049
  }
5850
6050
  //#endregion
5851
6051
  exports.apply = apply;