dsh-agy-link 0.4.35 → 0.4.36

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.36 (2026-09-18)
4
+
5
+ ### English
6
+
7
+ - **Fix (issue #30): Windows quota panel empty.** `readSystemKeychainToken()` had no win32 branch; primary credentials live in Windows Credential Manager (`gemini:antigravity`), not a disk token file. Added a PowerShell `CredRead` reader (same zero-dep pattern as macOS `security` / Linux `secret-tool`).
8
+ - **Fix: thinking blank after a reply finishes.** The client no longer rewrites React-managed `thinkBody` DOM (that wiped thought text on post-turn re-render). Styling is CSS-only; auto-expand uses the host disclosure control when prose is present.
9
+
10
+ ### 中文 (Chinese)
11
+
12
+ - **修复 #30:** Windows 额度读取 — 从 Credential Manager 读 `gemini:antigravity`。
13
+ - **修复:思考结束后变空** — 不再改写宿主 React 的 thinkBody;仅 CSS + 非破坏性展开。
14
+
3
15
  ## 0.4.35 (2026-09-18)
4
16
 
5
17
  ### English
package/dist/client.js CHANGED
@@ -1476,7 +1476,7 @@ window.__ModuleLoader__.load({
1476
1476
  //#endregion
1477
1477
  //#region src/client/reasoning.ts
1478
1478
  const REASONING_CSS = `
1479
- /* Ensure thinking disclosure row hover effect works reliably across all builds */
1479
+ /* Hover affordance on thinking disclosure rows */
1480
1480
  div[data-variant="think"] [data-disclosure-row]:hover [class*="iconIdle"],
1481
1481
  div[data-variant="think"] [class*="row"]:hover [class*="iconIdle"] {
1482
1482
  opacity: 0 !important;
@@ -1486,7 +1486,7 @@ div[data-variant="think"] [class*="row"]:hover [class*="chevronHover"] {
1486
1486
  opacity: 1 !important;
1487
1487
  }
1488
1488
 
1489
- /* Scrollable thinking body when content is long */
1489
+ /* Scrollable thinking body (does not touch DOM structure) */
1490
1490
  div[data-variant="think"] [class*="thinkBody"] {
1491
1491
  max-height: 360px;
1492
1492
  overflow-y: auto;
@@ -1511,20 +1511,11 @@ div[data-variant="think"] [class*="thinkBody"]::-webkit-scrollbar-track {
1511
1511
  margin: 4px 0;
1512
1512
  }
1513
1513
 
1514
- /* Tasteful metadata chip for [agy thinking turn · ...] banner */
1515
- .agy-thought-banner {
1516
- display: inline-block;
1517
- font-size: 11px;
1518
- line-height: 16px;
1519
- padding: 1px 7px;
1520
- margin-bottom: 6px;
1521
- border-radius: 4px;
1522
- color: var(--dsw-alias-label-tertiary, #64748b);
1523
- background: var(--dsw-alias-bg-layer-3, rgba(0, 0, 0, 0.04));
1524
- border: 0.5px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, 0.1));
1525
- font-family: var(--dsw-font-family-code, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
1526
- font-weight: 500;
1527
- user-select: none;
1514
+ /* Style leading banner token sequence without rewriting DOM: host text nodes
1515
+ keep ownership; this only paints the first line if the host wraps it. */
1516
+ div[data-variant="think"] [class*="thinkBody"] > span:first-child,
1517
+ div[data-variant="think"] [class*="thinkBody"] {
1518
+ font-family: inherit;
1528
1519
  }
1529
1520
  `;
1530
1521
  function injectStyles() {
@@ -1539,37 +1530,41 @@ div[data-variant="think"] [class*="thinkBody"]::-webkit-scrollbar-track {
1539
1530
  }
1540
1531
  }
1541
1532
  /**
1542
- * Cleanly format any `[agy thinking turn ...]` banner in `thinkBody`
1543
- * into a styled metadata chip so the prose displays cleanly.
1533
+ * Try to expand a thinking row once when its body contains real prose
1534
+ * (not a bare [agy thinking turn · N tokens] chip). Uses the host's own
1535
+ * disclosure control when present — never mutates thinkBody children.
1544
1536
  */
1545
- function formatThinkingBody(thinkBodyEl) {
1546
- if (thinkBodyEl.dataset.agyFormatted === "true") return;
1547
- const text = thinkBodyEl.textContent ?? "";
1548
- const match = text.match(/^\[agy thinking turn(?: · \d+ thinking tokens)?\]\s*/);
1549
- if (!match) return;
1550
- const bannerText = match[0].trim();
1551
- const restText = text.slice(match[0].length);
1552
- thinkBodyEl.dataset.agyFormatted = "true";
1553
- thinkBodyEl.textContent = "";
1554
- const bannerSpan = document.createElement("span");
1555
- bannerSpan.className = "agy-thought-banner";
1556
- bannerSpan.textContent = bannerText;
1557
- thinkBodyEl.appendChild(bannerSpan);
1558
- if (restText.length > 0) thinkBodyEl.appendChild(document.createTextNode("\n" + restText));
1537
+ function tryExpandThinkRow(root) {
1538
+ if (root.dataset.agyExpanded === "true") return;
1539
+ const body = root.querySelector("[class*=\"thinkBody\"]");
1540
+ if (!body) return;
1541
+ const text = (body.textContent ?? "").trim();
1542
+ if (text === "") return;
1543
+ if (/^\[agy thinking turn(?: · \d+ thinking tokens)?\]\s*$/.test(text)) return;
1544
+ const style = typeof getComputedStyle === "function" ? getComputedStyle(body) : null;
1545
+ if (style && style.display !== "none" && body.clientHeight > 8) {
1546
+ root.dataset.agyExpanded = "true";
1547
+ return;
1548
+ }
1549
+ const toggle = root.querySelector("[data-disclosure-row]") ?? root.querySelector("[class*=\"disclosure\"]") ?? root.querySelector("button");
1550
+ if (toggle) try {
1551
+ toggle.click();
1552
+ root.dataset.agyExpanded = "true";
1553
+ } catch {}
1559
1554
  }
1560
- function scanThinkingBlocks() {
1555
+ function scanThinkingRows() {
1561
1556
  if (typeof document === "undefined") return;
1562
- const thinkBodies = document.querySelectorAll("div[data-variant=\"think\"] [class*=\"thinkBody\"]");
1563
- for (let i = 0; i < thinkBodies.length; i++) {
1564
- const body = thinkBodies[i];
1565
- if (body) formatThinkingBody(body);
1557
+ const rows = document.querySelectorAll("div[data-variant=\"think\"]");
1558
+ for (let i = 0; i < rows.length; i++) {
1559
+ const row = rows[i];
1560
+ if (row) tryExpandThinkRow(row);
1566
1561
  }
1567
1562
  }
1568
1563
  function installAutoExpandReasoning() {
1569
1564
  if (typeof window === "undefined" || typeof document === "undefined") return;
1570
1565
  injectStyles();
1571
1566
  const observer = new MutationObserver(() => {
1572
- scanThinkingBlocks();
1567
+ scanThinkingRows();
1573
1568
  });
1574
1569
  const setupObserver = () => {
1575
1570
  if (document.body) {
@@ -1577,7 +1572,7 @@ div[data-variant="think"] [class*="thinkBody"]::-webkit-scrollbar-track {
1577
1572
  childList: true,
1578
1573
  subtree: true
1579
1574
  });
1580
- scanThinkingBlocks();
1575
+ scanThinkingRows();
1581
1576
  }
1582
1577
  };
1583
1578
  if (document.readyState === "loading") window.addEventListener("DOMContentLoaded", setupObserver, { once: true });
package/dist/index.js CHANGED
@@ -28726,6 +28726,72 @@ function readMacKeychainToken() {
28726
28726
  return null;
28727
28727
  }
28728
28728
  }
28729
+ /**
28730
+ * Read the primary Antigravity OAuth token from Windows Credential Manager.
28731
+ * agy on Windows stores it via go-keyring under target `gemini:antigravity`
28732
+ * (user `antigravity`) as a UTF-8 JSON blob with the same shape as macOS
28733
+ * Keychain / Linux Secret Service. Without this reader the quota panel stays
28734
+ * empty on Windows: the on-disk `antigravity-oauth-token` file is often
28735
+ * absent while agy itself still authenticates via CredRead (issue #30).
28736
+ */
28737
+ function readWindowsCredentialToken() {
28738
+ if (process.platform !== "win32") return null;
28739
+ try {
28740
+ const script = [
28741
+ "$sig = @'",
28742
+ "using System;",
28743
+ "using System.Runtime.InteropServices;",
28744
+ "public class AgyCred {",
28745
+ " [DllImport(\"advapi32.dll\", SetLastError=true, CharSet=CharSet.Unicode)]",
28746
+ " public static extern bool CredRead(string target, int type, int flags, out IntPtr credential);",
28747
+ " [DllImport(\"advapi32.dll\", SetLastError=true)]",
28748
+ " public static extern void CredFree(IntPtr cred);",
28749
+ " [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]",
28750
+ " public struct CREDENTIAL {",
28751
+ " public int Flags; public int Type; public IntPtr TargetName;",
28752
+ " public IntPtr Comment; public long LastWritten; public int CredentialBlobSize;",
28753
+ " public IntPtr CredentialBlob; public int Persist; public int AttributeCount;",
28754
+ " public IntPtr Attributes; public IntPtr TargetAlias; public IntPtr UserName;",
28755
+ " }",
28756
+ " public static byte[] Read(string target) {",
28757
+ " IntPtr p;",
28758
+ " if (!CredRead(target, 1, 0, out p)) return null;",
28759
+ " CREDENTIAL c = (CREDENTIAL)Marshal.PtrToStructure(p, typeof(CREDENTIAL));",
28760
+ " byte[] b = new byte[c.CredentialBlobSize];",
28761
+ " Marshal.Copy(c.CredentialBlob, b, 0, c.CredentialBlobSize);",
28762
+ " CredFree(p);",
28763
+ " return b;",
28764
+ " }",
28765
+ "}",
28766
+ "'@",
28767
+ "Add-Type -TypeDefinition $sig -ErrorAction Stop",
28768
+ "$b = [AgyCred]::Read('gemini:antigravity')",
28769
+ "if ($b -eq $null) { exit 1 }",
28770
+ "[Console]::Out.Write([System.Text.Encoding]::UTF8.GetString($b))"
28771
+ ].join("\n");
28772
+ const raw = execFileSync("powershell.exe", [
28773
+ "-NoProfile",
28774
+ "-NonInteractive",
28775
+ "-ExecutionPolicy",
28776
+ "Bypass",
28777
+ "-Command",
28778
+ script
28779
+ ], {
28780
+ encoding: "utf8",
28781
+ timeout: 8e3,
28782
+ stdio: [
28783
+ "ignore",
28784
+ "pipe",
28785
+ "ignore"
28786
+ ],
28787
+ windowsHide: true
28788
+ }).trim();
28789
+ if (!raw) return null;
28790
+ return parseGoKeyringPayload(raw);
28791
+ } catch {
28792
+ return null;
28793
+ }
28794
+ }
28729
28795
  var QuotaService = class {
28730
28796
  pool;
28731
28797
  preferredEndpointIndex = 0;
@@ -28743,6 +28809,7 @@ var QuotaService = class {
28743
28809
  readSystemKeychainToken() {
28744
28810
  if (process.platform === "darwin") return readMacKeychainToken();
28745
28811
  if (process.platform === "linux") return readLinuxSecretToken();
28812
+ if (process.platform === "win32") return readWindowsCredentialToken();
28746
28813
  return null;
28747
28814
  }
28748
28815
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-agy-link",
3
- "version": "0.4.35",
3
+ "version": "0.4.36",
4
4
  "description": "Google Antigravity (agy CLI) models for DeepSeek Harness — stream Gemini/Claude/GPT-OSS subscriptions into DSH with thinking, tool activity, token usage and in-GUI Google OAuth login.",
5
5
  "type": "module",
6
6
  "license": "MIT",