clankerbend 0.1.0 → 0.1.1

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/host/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # OneWhack Host
1
+ # ClankerBend Host
2
2
 
3
- OneWhack is an independent OneWill project compatible with OpenAI Codex
3
+ ClankerBend is an independent OneWill project compatible with OpenAI Codex
4
4
  Desktop. It is not affiliated with or endorsed by OpenAI.
5
5
 
6
- This package contains reusable host primitives for OneWhack `0.1`:
6
+ This package contains reusable host primitives for ClankerBend `0.1`:
7
7
 
8
8
  - loopback HTTP server
9
9
  - JSON response envelopes and error envelopes
@@ -14,5 +14,5 @@ This package contains reusable host primitives for OneWhack `0.1`:
14
14
  - monotonic selection handling
15
15
  - mock transcript adapter for tests and local development
16
16
 
17
- The host does not contain Vim Navigator-specific logic. Apps register a
17
+ The host does not contain VimNav-specific logic. Apps register a
18
18
  manifest, static panel directory, state function, and action handler.
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { dirname, resolve } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
 
5
- export const ONEWHACK_APP_MANIFEST_VERSION = "0.1";
5
+ export const CLANKERBEND_APP_MANIFEST_VERSION = "0.1";
6
6
 
7
7
  const VALID_DISTRIBUTION_KINDS = new Set(["local", "npm", "tarball", "binary"]);
8
8
  const VALID_PLATFORM_OS = new Set(["darwin", "linux", "win32", "any"]);
@@ -51,9 +51,9 @@ export function validateAppManifest(manifest, options = {}) {
51
51
  if (!isPlainObject(manifest)) errors.push("manifest must be a JSON object");
52
52
  if (errors.length) throwManifestError(errors, options);
53
53
 
54
- requireString(manifest, "oneWhackVersion", errors);
55
- if (manifest.oneWhackVersion !== ONEWHACK_APP_MANIFEST_VERSION) {
56
- errors.push(`oneWhackVersion must be ${ONEWHACK_APP_MANIFEST_VERSION}`);
54
+ requireString(manifest, "clankerbendVersion", errors);
55
+ if (manifest.clankerbendVersion !== CLANKERBEND_APP_MANIFEST_VERSION) {
56
+ errors.push(`clankerbendVersion must be ${CLANKERBEND_APP_MANIFEST_VERSION}`);
57
57
  }
58
58
  requireString(manifest, "appId", errors);
59
59
  requireString(manifest, "version", errors);
@@ -202,7 +202,7 @@ export function normalizeAppFromManifest(app, manifest) {
202
202
 
203
203
  export function defaultRegistryConfig() {
204
204
  return {
205
- oneWhackVersion: ONEWHACK_APP_MANIFEST_VERSION,
205
+ clankerbendVersion: CLANKERBEND_APP_MANIFEST_VERSION,
206
206
  installedApps: {},
207
207
  profiles: {
208
208
  default: {
@@ -307,7 +307,7 @@ function requireString(object, key, errors, label = key) {
307
307
 
308
308
  function throwManifestError(errors, options = {}) {
309
309
  const prefix = options.manifestPath ? `${options.manifestPath}: ` : "";
310
- throw new Error(`${prefix}invalid OneWhack app manifest:\n${errors.map((error) => `- ${error}`).join("\n")}`);
310
+ throw new Error(`${prefix}invalid ClankerBend app manifest:\n${errors.map((error) => `- ${error}`).join("\n")}`);
311
311
  }
312
312
 
313
313
  function isPlainObject(value) {
@@ -52,7 +52,7 @@ function normalizeProviders(options = {}, rendererBridges = []) {
52
52
 
53
53
  function userPanelError(error) {
54
54
  if (/native Browser panel controls not found|panel app did not load|panel unavailable/i.test(String(error || ""))) {
55
- return "Waiting for Codex Desktop to expose the Browser side panel. Open a Codex thread, then OneWhack will load the selected app automatically.";
55
+ return "Waiting for Codex Desktop to expose the Browser side panel. Open a Codex thread, then ClankerBend will load the selected app automatically.";
56
56
  }
57
57
  return error || "Waiting for Codex Desktop to expose the Browser side panel.";
58
58
  }
@@ -219,7 +219,7 @@ class CodexDesktopCdpAdapter {
219
219
  const bridge = this.bridgeForProvider("transcriptHighlight");
220
220
  const appId = this.bridgeAppId(bridge);
221
221
  return this.evaluate(`(() => {
222
- const runtime = window.__oneWhackRuntime;
222
+ const runtime = window.__clankerbendRuntime;
223
223
  const bridge = runtime?.getBridge?.(${JSON.stringify(appId)});
224
224
  if (bridge?.highlightRange) return bridge.highlightRange(${JSON.stringify(range)}, ${JSON.stringify(options)});
225
225
  if (runtime?.highlightRange) return runtime.highlightRange(${JSON.stringify(range)}, ${JSON.stringify(options)});
@@ -230,7 +230,7 @@ class CodexDesktopCdpAdapter {
230
230
 
231
231
  async setComposerDraft(draft, options = {}) {
232
232
  return this.evaluate(`(() => {
233
- const runtime = window.__oneWhackRuntime;
233
+ const runtime = window.__clankerbendRuntime;
234
234
  if (!runtime?.setComposerDraft) return { ok: false, error: "composer draft unavailable" };
235
235
  return runtime.setComposerDraft(${JSON.stringify(draft)}, ${JSON.stringify(options)});
236
236
  })()`);
@@ -238,7 +238,7 @@ class CodexDesktopCdpAdapter {
238
238
 
239
239
  async submitComposer(draft, options = {}) {
240
240
  return this.evaluate(`(() => {
241
- const runtime = window.__oneWhackRuntime;
241
+ const runtime = window.__clankerbendRuntime;
242
242
  if (!runtime?.submitComposer) return { ok: false, error: "composer submit unavailable" };
243
243
  return runtime.submitComposer(${JSON.stringify(draft)}, ${JSON.stringify(options)});
244
244
  })()`);
@@ -347,7 +347,7 @@ class CodexDesktopCdpAdapter {
347
347
  return { ok: false, error: "app-server active thread is unknown", mode: "app-server-inject-items", diagnostic: diagnostics };
348
348
  }
349
349
  const items = buildAttachmentResponseItems(files, {
350
- appName: options.appName || "OneWhack",
350
+ appName: options.appName || "ClankerBend",
351
351
  createdAt: new Date().toISOString()
352
352
  });
353
353
  const response = await client.threadInjectItems(thread.threadId, items);
@@ -491,20 +491,20 @@ class CodexDesktopCdpAdapter {
491
491
  const protocolVersion = ${JSON.stringify(this.host.protocolVersion)};
492
492
  const hostUrl = ${JSON.stringify(this.host.state.host.url)};
493
493
  const seededApps = ${JSON.stringify(apps)};
494
- const previous = window.__oneWhackRuntime;
494
+ const previous = window.__clankerbendRuntime;
495
495
  const slots = previous && typeof previous.apps === "object" ? previous.apps : {};
496
496
  const cssEscape = (value) => window.CSS?.escape ? window.CSS.escape(String(value)) : String(value).replace(/["\\\\]/g, "\\\\$&");
497
497
  const ensureAnnotationLayoutStyle = () => {
498
- const styleId = "onewhack-annotation-layout-style";
498
+ const styleId = "clankerbend-annotation-layout-style";
499
499
  if (document.getElementById(styleId)) return;
500
500
  const style = document.createElement("style");
501
501
  style.id = styleId;
502
502
  style.textContent = \`
503
- .onewhack-transcript-anchor {
503
+ .clankerbend-transcript-anchor {
504
504
  position: relative !important;
505
505
  overflow: visible !important;
506
506
  }
507
- .onewhack-transcript-annotations {
507
+ .clankerbend-transcript-annotations {
508
508
  position: absolute !important;
509
509
  left: -62px !important;
510
510
  top: 4px !important;
@@ -515,37 +515,35 @@ class CodexDesktopCdpAdapter {
515
515
  gap: 6px !important;
516
516
  pointer-events: none !important;
517
517
  }
518
- .onewhack-transcript-annotation-slot {
518
+ .clankerbend-transcript-annotation-slot {
519
519
  display: flex !important;
520
520
  align-items: center !important;
521
521
  justify-content: center !important;
522
522
  pointer-events: auto !important;
523
523
  }
524
524
  @media (max-width: 900px) {
525
- .onewhack-transcript-annotations {
526
- position: static !important;
527
- flex-direction: row !important;
528
- justify-content: flex-start !important;
529
- align-items: center !important;
530
- margin: 0 0 6px 0 !important;
525
+ .clankerbend-transcript-annotations {
526
+ position: absolute !important;
527
+ left: -44px !important;
528
+ top: 4px !important;
531
529
  }
532
530
  }
533
531
  \`;
534
532
  document.head.appendChild(style);
535
533
  };
536
534
  const annotationHost = (anchor, anchorId) => {
537
- if (!(anchor instanceof HTMLElement)) throw new Error("OneWhack annotation anchor must be an HTMLElement");
535
+ if (!(anchor instanceof HTMLElement)) throw new Error("ClankerBend annotation anchor must be an HTMLElement");
538
536
  ensureAnnotationLayoutStyle();
539
- anchor.classList.add("onewhack-transcript-anchor");
537
+ anchor.classList.add("clankerbend-transcript-anchor");
540
538
  let host = Array.from(anchor.children).find((child) =>
541
539
  child instanceof HTMLElement &&
542
- child.classList.contains("onewhack-transcript-annotations") &&
543
- child.dataset.onewhackAnchorId === anchorId
540
+ child.classList.contains("clankerbend-transcript-annotations") &&
541
+ child.dataset.clankerbendAnchorId === anchorId
544
542
  );
545
543
  if (!host) {
546
544
  host = document.createElement("div");
547
- host.className = "onewhack-transcript-annotations";
548
- host.dataset.onewhackAnchorId = anchorId;
545
+ host.className = "clankerbend-transcript-annotations";
546
+ host.dataset.clankerbendAnchorId = anchorId;
549
547
  anchor.insertAdjacentElement("afterbegin", host);
550
548
  }
551
549
  return host;
@@ -565,15 +563,15 @@ class CodexDesktopCdpAdapter {
565
563
  }
566
564
  return left;
567
565
  })();
568
- const visibleLeft = clippingLeft - rect.left;
569
- host.style.setProperty("left", Math.max(visibleLeft, desiredLeft) + "px", "important");
566
+ host.style.setProperty("left", desiredLeft + "px", "important");
567
+ host.style.setProperty("visibility", rect.left + desiredLeft < clippingLeft ? "hidden" : "visible", "important");
570
568
  };
571
569
  const sortAnnotationSlots = (host) => {
572
570
  [...host.children]
573
571
  .sort((a, b) =>
574
- Number(a.dataset.onewhackPriority || 100) - Number(b.dataset.onewhackPriority || 100) ||
575
- String(a.dataset.onewhackAppId || "").localeCompare(String(b.dataset.onewhackAppId || "")) ||
576
- String(a.dataset.onewhackMarkerId || "").localeCompare(String(b.dataset.onewhackMarkerId || ""))
572
+ Number(a.dataset.clankerbendPriority || 100) - Number(b.dataset.clankerbendPriority || 100) ||
573
+ String(a.dataset.clankerbendAppId || "").localeCompare(String(b.dataset.clankerbendAppId || "")) ||
574
+ String(a.dataset.clankerbendMarkerId || "").localeCompare(String(b.dataset.clankerbendMarkerId || ""))
577
575
  )
578
576
  .forEach((child) => host.appendChild(child));
579
577
  };
@@ -589,8 +587,8 @@ class CodexDesktopCdpAdapter {
589
587
  const findAnchorElement = (anchorId) => document.querySelector(transcriptAnchorSelector(anchorId));
590
588
  const isHostUiElement = (el) => {
591
589
  for (let node = el; node; node = node.parentElement) {
592
- if (node.classList?.contains?.("onewhack-host-ui")) return true;
593
- if (/^onewhack-/.test(String(node.id || ""))) return true;
590
+ if (node.classList?.contains?.("clankerbend-host-ui")) return true;
591
+ if (/^clankerbend-/.test(String(node.id || ""))) return true;
594
592
  }
595
593
  return false;
596
594
  };
@@ -633,16 +631,16 @@ class CodexDesktopCdpAdapter {
633
631
  );
634
632
  return candidates[0]?.el || null;
635
633
  };
636
- const ONEWHACK_CONTEXT_START = "--- OneWhack context ---";
637
- const ONEWHACK_CONTEXT_END = "--- End OneWhack context ---";
638
- const stripOneWhackContext = (text) => {
634
+ const CLANKERBEND_CONTEXT_START = "--- ClankerBend context ---";
635
+ const CLANKERBEND_CONTEXT_END = "--- End ClankerBend context ---";
636
+ const stripContextBlock = (text) => {
639
637
  let output = String(text || "");
640
638
  while (true) {
641
- const start = output.indexOf(ONEWHACK_CONTEXT_START);
639
+ const start = output.indexOf(CLANKERBEND_CONTEXT_START);
642
640
  if (start < 0) return output.trimStart();
643
- const end = output.indexOf(ONEWHACK_CONTEXT_END, start + ONEWHACK_CONTEXT_START.length);
641
+ const end = output.indexOf(CLANKERBEND_CONTEXT_END, start + CLANKERBEND_CONTEXT_START.length);
644
642
  if (end < 0) return output.slice(0, start).trimEnd();
645
- output = output.slice(0, start) + output.slice(end + ONEWHACK_CONTEXT_END.length);
643
+ output = output.slice(0, start) + output.slice(end + CLANKERBEND_CONTEXT_END.length);
646
644
  }
647
645
  };
648
646
  const setComposerValue = (el, value) => {
@@ -678,7 +676,7 @@ class CodexDesktopCdpAdapter {
678
676
  hostUrl,
679
677
  apps: slots,
680
678
  registerApp(app) {
681
- if (!app || !app.appId) throw new Error("OneWhack appId is required");
679
+ if (!app || !app.appId) throw new Error("ClankerBend appId is required");
682
680
  const seeded = seededApps[app.appId] || {};
683
681
  const current = this.apps[app.appId] || {};
684
682
  const currentVersion = Number(current.bridge?.version);
@@ -690,7 +688,7 @@ class CodexDesktopCdpAdapter {
690
688
  Number.isFinite(nextVersion) &&
691
689
  nextVersion < currentVersion
692
690
  ) {
693
- throw new Error("OneWhack app bridge version regressed for " + app.appId);
691
+ throw new Error("ClankerBend app bridge version regressed for " + app.appId);
694
692
  }
695
693
  const slot = {
696
694
  ...current,
@@ -713,22 +711,22 @@ class CodexDesktopCdpAdapter {
713
711
  return this.getApp(appId)?.entryUrl || null;
714
712
  },
715
713
  placeAnnotation(anchor, annotation) {
716
- if (!annotation?.appId) throw new Error("OneWhack annotation appId is required");
717
- if (!annotation?.anchorId) throw new Error("OneWhack annotation anchorId is required");
718
- if (!(annotation.element instanceof HTMLElement)) throw new Error("OneWhack annotation element is required");
714
+ if (!annotation?.appId) throw new Error("ClankerBend annotation appId is required");
715
+ if (!annotation?.anchorId) throw new Error("ClankerBend annotation anchorId is required");
716
+ if (!(annotation.element instanceof HTMLElement)) throw new Error("ClankerBend annotation element is required");
719
717
  const markerId = annotation.markerId || annotation.anchorId;
720
718
  const host = annotationHost(anchor, annotation.anchorId);
721
- const selector = '.onewhack-transcript-annotation-slot[data-onewhack-app-id="' + cssEscape(annotation.appId) + '"][data-onewhack-marker-id="' + cssEscape(markerId) + '"]';
719
+ const selector = '.clankerbend-transcript-annotation-slot[data-clankerbend-app-id="' + cssEscape(annotation.appId) + '"][data-clankerbend-marker-id="' + cssEscape(markerId) + '"]';
722
720
  let slot = host.querySelector(selector);
723
721
  if (!slot) {
724
722
  slot = document.createElement("div");
725
- slot.className = "onewhack-transcript-annotation-slot";
726
- slot.dataset.onewhackAppId = annotation.appId;
727
- slot.dataset.onewhackMarkerId = markerId;
723
+ slot.className = "clankerbend-transcript-annotation-slot";
724
+ slot.dataset.clankerbendAppId = annotation.appId;
725
+ slot.dataset.clankerbendMarkerId = markerId;
728
726
  host.appendChild(slot);
729
727
  }
730
- slot.dataset.onewhackAnchorId = annotation.anchorId;
731
- slot.dataset.onewhackPriority = String(annotation.priority ?? 100);
728
+ slot.dataset.clankerbendAnchorId = annotation.anchorId;
729
+ slot.dataset.clankerbendPriority = String(annotation.priority ?? 100);
732
730
  if (slot.firstElementChild !== annotation.element) slot.replaceChildren(annotation.element);
733
731
  sortAnnotationSlots(host);
734
732
  positionAnnotationHost(host, anchor);
@@ -736,10 +734,10 @@ class CodexDesktopCdpAdapter {
736
734
  },
737
735
  removeAnnotations(appId, liveAnchorIds = null) {
738
736
  const live = liveAnchorIds ? new Set([...liveAnchorIds].map(String)) : null;
739
- document.querySelectorAll('.onewhack-transcript-annotation-slot[data-onewhack-app-id="' + cssEscape(appId) + '"]').forEach((slot) => {
740
- if (!live || !live.has(slot.dataset.onewhackAnchorId || "")) slot.remove();
737
+ document.querySelectorAll('.clankerbend-transcript-annotation-slot[data-clankerbend-app-id="' + cssEscape(appId) + '"]').forEach((slot) => {
738
+ if (!live || !live.has(slot.dataset.clankerbendAnchorId || "")) slot.remove();
741
739
  });
742
- document.querySelectorAll(".onewhack-transcript-annotations").forEach((host) => {
740
+ document.querySelectorAll(".clankerbend-transcript-annotations").forEach((host) => {
743
741
  if (!host.children.length) host.remove();
744
742
  });
745
743
  },
@@ -747,14 +745,14 @@ class CodexDesktopCdpAdapter {
747
745
  if (!range?.anchorId) return { ok: false, error: "range.anchorId is required" };
748
746
  const anchor = findAnchorElement(range.anchorId);
749
747
  if (!anchor) return { ok: false, error: "anchor not found", anchorId: range.anchorId };
750
- anchor.classList.add("onewhack-range-highlight");
748
+ anchor.classList.add("clankerbend-range-highlight");
751
749
  const previousOutline = anchor.style.outline;
752
750
  const previousOffset = anchor.style.outlineOffset;
753
751
  anchor.style.outline = "3px solid #f6c945";
754
752
  anchor.style.outlineOffset = "3px";
755
753
  anchor.scrollIntoView({ block: options.block || "center", behavior: options.behavior || "smooth" });
756
754
  setTimeout(() => {
757
- anchor.classList.remove("onewhack-range-highlight");
755
+ anchor.classList.remove("clankerbend-range-highlight");
758
756
  anchor.style.outline = previousOutline;
759
757
  anchor.style.outlineOffset = previousOffset;
760
758
  }, Number(options.durationMs || 1200));
@@ -764,8 +762,8 @@ class CodexDesktopCdpAdapter {
764
762
  const el = findComposer();
765
763
  if (!el) return { ok: false, error: "composer not found" };
766
764
  const currentText = composerText(el);
767
- const text = draft.oneWhackContext
768
- ? (draft.text ? mergeDraftText(stripOneWhackContext(currentText), String(draft.text || ""), "prepend") : stripOneWhackContext(currentText))
765
+ const text = draft.clankerbendContext
766
+ ? (draft.text ? mergeDraftText(stripContextBlock(currentText), String(draft.text || ""), "prepend") : stripContextBlock(currentText))
769
767
  : mergeDraftText(currentText, String(draft.text || ""), draft.mode || "replace");
770
768
  if (!setComposerValue(el, text)) return { ok: false, error: "composer cannot be updated" };
771
769
  return { ok: true, draft: { ...draft, text } };
@@ -790,7 +788,7 @@ class CodexDesktopCdpAdapter {
790
788
  injectedAt: runtime.apps[appId]?.injectedAt || null
791
789
  };
792
790
  }
793
- window.__oneWhackRuntime = runtime;
791
+ window.__clankerbendRuntime = runtime;
794
792
  })()`);
795
793
  for (const bridge of this.rendererBridges) {
796
794
  await this.evaluate(bridge.injectedSource);
@@ -1042,9 +1040,9 @@ class CodexDesktopCdpAdapter {
1042
1040
  async evalBridge(expression, bridge = this.primaryBridge()) {
1043
1041
  const appId = this.bridgeAppId(bridge);
1044
1042
  return this.evaluate(`(() => {
1045
- const runtime = window.__oneWhackRuntime;
1043
+ const runtime = window.__clankerbendRuntime;
1046
1044
  const bridge = runtime?.getBridge?.(${JSON.stringify(appId)});
1047
- if (!bridge) return { ok: false, error: "OneWhack bridge is not installed for " + ${JSON.stringify(appId)} };
1045
+ if (!bridge) return { ok: false, error: "ClankerBend bridge is not installed for " + ${JSON.stringify(appId)} };
1048
1046
  return bridge.${expression};
1049
1047
  })()`);
1050
1048
  }
@@ -1240,8 +1238,8 @@ class AppServerClient {
1240
1238
  async initialize() {
1241
1239
  const response = await this.call("initialize", {
1242
1240
  clientInfo: {
1243
- name: "onewhack-host",
1244
- title: "OneWhack Host",
1241
+ name: "clankerbend-host",
1242
+ title: "ClankerBend Host",
1245
1243
  version: "0.1"
1246
1244
  },
1247
1245
  capabilities: {
@@ -1448,7 +1446,7 @@ function buildAttachmentResponseItems(files = [], options = {}) {
1448
1446
  const createdAt = options.createdAt || new Date().toISOString();
1449
1447
  return files.map((file, index) => {
1450
1448
  const text = [
1451
- "OneWhack attached runtime file context.",
1449
+ "ClankerBend attached runtime file context.",
1452
1450
  "",
1453
1451
  `Attached at: ${createdAt}`,
1454
1452
  `File ${index + 1}: ${file.name || basename(file.path)}`,
@@ -1456,7 +1454,7 @@ function buildAttachmentResponseItems(files = [], options = {}) {
1456
1454
  file.relativePath ? `Runtime path: ${file.relativePath}` : null,
1457
1455
  file.mimeType ? `MIME type: ${file.mimeType}` : null,
1458
1456
  "",
1459
- "The following file was created by a OneWhack app and should be treated as attached context for the user's next prompt.",
1457
+ "The following file was created by a ClankerBend app and should be treated as attached context for the user's next prompt.",
1460
1458
  "",
1461
1459
  "```markdown",
1462
1460
  readFileSync(file.path, "utf8"),
@@ -1731,14 +1729,14 @@ function nativeAttachmentDiagnosticExpression(paths) {
1731
1729
  (() => {
1732
1730
  const paths = ${JSON.stringify(paths)};
1733
1731
  const basenames = paths.map((path) => String(path).split(/[\\\\/]/).pop()).filter(Boolean);
1734
- const oneWhackChipCount = document.querySelectorAll("#onewhack-composer-chips .onewhack-context-chip").length;
1732
+ const clankerbendChipCount = document.querySelectorAll("#clankerbend-composer-chips .clankerbend-context-chip").length;
1735
1733
  const composerInputs = [...document.querySelectorAll("textarea,[contenteditable='true'],[role='textbox']")]
1736
1734
  .map((el) => ({ el, rect: el.getBoundingClientRect() }))
1737
1735
  .filter((item) => item.rect.width > 180 && item.rect.height > 18 && item.rect.bottom > window.innerHeight * 0.45)
1738
1736
  .sort((a, b) => b.rect.bottom - a.rect.bottom);
1739
1737
  const composerRect = composerInputs[0]?.rect || null;
1740
1738
  const candidates = [...document.querySelectorAll("button,[role='button'],span,div")]
1741
- .filter((el) => !el.closest("#onewhack-composer-chips"))
1739
+ .filter((el) => !el.closest("#clankerbend-composer-chips"))
1742
1740
  .map((el) => {
1743
1741
  const text = (el.innerText || el.textContent || "").replace(/\\s+/g, " ").trim();
1744
1742
  const rect = el.getBoundingClientRect();
@@ -1760,10 +1758,10 @@ function nativeAttachmentDiagnosticExpression(paths) {
1760
1758
  );
1761
1759
  const matched = basenames.filter((name) => candidates.some((item) => item.text.includes(name)));
1762
1760
  return {
1763
- ok: matched.length === basenames.length && oneWhackChipCount === 0,
1761
+ ok: matched.length === basenames.length && clankerbendChipCount === 0,
1764
1762
  matched,
1765
1763
  basenames,
1766
- oneWhackChipCount,
1764
+ clankerbendChipCount,
1767
1765
  candidates: candidates.slice(0, 12)
1768
1766
  };
1769
1767
  })()