← Commit history
README.md+2
@@ -276,6 +276,8 @@ v1.1.20: /command returns errorCode command_not_found (HTTP 400) with did-you-me  v1.1.21: event-driven tab census + Claude session identity (John 2026-09-03: never poll the iframes again). GET /tabs and the SSE stream GET /tabs/events publish the extension host's own tabGroups census on every change; POST /claude/session-start ingests the Claude SessionStart hook (id + transcript path, before the jsonl is flushed) and GET /claude/sessions lists it; POST /claude/open-session opens an existing conversation by id and confirms the bind (retagging sdk-cli transcripts, which the Claude extension otherwise declines). Capability markers in /health: tabs.list, tabs.events, claude.open-session, claude.session-start, claude.sessions. +v1.1.64: the usage widgets ask their provider at most once every three minutes, pause while the window is hidden, back off on errors (doubling to 15 minutes), and re-read the sign-in file only when it changes.+ v1.1.63: the Claude and Codex usage widgets carry their vendor marks (the launch bar artwork) so you can tell them apart at a glance, in the slot and in the Widgets pulldown.  v1.1.62: the widget pages size themselves to their slot, so they fit the full Title Bar and the compact one.
VERSION+1−1
@@ -1 +1 @@-1.1.63+1.1.64
bin/adom-vscode
⋯ 1 unchanged line ⋯
bin/hydrogen-widget-claude-usage+3−3
@@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Claude usage in the title bar: 5-hour session, weekly, and the weekly model buckets that apply to your plan, as percent used. Reads the Claude Code sign-in in ~/.claude/.credentials.json-and asks the same usage endpoint the Claude Code CLI shows in /usage. Refreshes every minute."""+and asks the same usage endpoint the Claude Code CLI shows in /usage. Asks the provider at most once every three minutes, pauses while the window is hidden, backs off on errors.""" import json, os, sys sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) from importlib.machinery import SourceFileLoader@@ -11,7 +11,7 @@ lib = SourceFileLoader('hwlib', os.path.join(os.path.dirname(os.path.realpath(__ def data():     if not os.path.exists(os.path.expanduser('~/.claude/.credentials.json')):         return {'error': 'sign in to Claude Code in this workspace'}-    c = json.load(open(os.path.expanduser('~/.claude/.credentials.json')))+    c = lib.read_json_if_changed('~/.claude/.credentials.json')     oa = c.get('claudeAiOauth') or c     tok = oa.get('accessToken')     if not tok: return {'error': 'not signed in to Claude Code'}@@ -23,4 +23,4 @@ def data():     return out  RENDER = r"""(d.error?`<span class="err">${d.error}</span>`:bar('5h',d.five_hour??0)+bar('week',d.seven_day??0)+(d.models||[]).slice(0,1).map(m=>bar(m.name,m.pct)).join(''))"""-lib.serve('Claude usage', data, RENDER, 60000, mark=marks.MARKS['claude'])+lib.serve('Claude usage', data, RENDER, 180000, mark=marks.MARKS['claude'])
bin/hydrogen-widget-codex-usage+3−3
@@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Codex usage in the title bar: the plan's rate-limit window as percent used, plus a secondary window when the plan has one. Reads the Codex sign-in in ~/.codex/auth.json and asks-the usage endpoint the Codex CLI shows in /status. Refreshes every minute."""+the usage endpoint the Codex CLI shows in /status. Asks the provider at most once every three minutes, pauses while the window is hidden, backs off on errors.""" import json, os, sys from importlib.machinery import SourceFileLoader marks = SourceFileLoader('hwmarks', os.path.join(os.path.dirname(os.path.realpath(__file__)), 'hydrogen-widget-marks.py')).load_module()@@ -10,7 +10,7 @@ lib = SourceFileLoader('hwlib', os.path.join(os.path.dirname(os.path.realpath(__ def data():     if not os.path.exists(os.path.expanduser('~/.codex/auth.json')):         return {'error': 'sign in to Codex in this workspace'}-    a = json.load(open(os.path.expanduser('~/.codex/auth.json')))+    a = lib.read_json_if_changed('~/.codex/auth.json')     t = a.get('tokens') or {}     tok = t.get('access_token'); acct = t.get('account_id') or ''     if not tok: return {'error': 'not signed in to Codex'}@@ -25,4 +25,4 @@ def data():     return out  RENDER = r"""(d.error?`<span class="err">${d.error}</span>`:bar(d.primary.label,d.primary.pct)+(d.secondary?bar(d.secondary.label,d.secondary.pct):''))"""-lib.serve('Codex usage', data, RENDER, 60000, mark=marks.MARKS['codex'])+lib.serve('Codex usage', data, RENDER, 180000, mark=marks.MARKS['codex'])
bin/hydrogen-widget-lib.py+20−5
@@ -25,19 +25,25 @@ html,body{margin:0;height:100%%;background:transparent;color:#e6ebef;font-family const R=%(refresh_ms)d; function bar(l,p){const c=p>=90?'hot':p>=70?'warn':'';return `<span class="lbl">${l}</span><span class="bar ${c}"><i style="width:${Math.max(0,Math.min(100,p))}%%"></i></span><span class="v">${Math.round(p)}%%</span>`;} async function tick(){try{const r=await fetch('/data.json',{cache:'no-store'});const d=await r.json();document.getElementById('w').innerHTML=%(render_js)s;}catch(e){document.getElementById('w').innerHTML='<span class="err">no data</span>';}}-tick();setInterval(tick,R);+let tm=null;function arm(){if(tm)return;tm=setInterval(tick,R);}function disarm(){if(tm){clearInterval(tm);tm=null;}}+document.addEventListener('visibilitychange',()=>{if(document.hidden)disarm();else{tick();arm();}});+tick();if(!document.hidden)arm(); </script></body>"""  def serve(title, data_fn, render_js, refresh_ms=60000, mark=None):     mark = mark or {'svg': '', 'color': 'currentColor'}     port = int(os.environ.get('HYDROGEN_WIDGET_PORT') or (sys.argv[sys.argv.index('--port')+1] if '--port' in sys.argv else 8930))-    cache = {'t': 0, 'v': {'error': 'starting'}}+    # One upstream call per interval at most, however often the page asks; on an upstream+    # error the interval doubles up to 15 minutes and returns to normal on the next success.+    cache = {'t': 0, 'v': {'error': 'starting'}, 'wait': refresh_ms/1000}     lock = threading.Lock()     def data():         with lock:-            if time.time() - cache['t'] > refresh_ms/1000 - 1:-                try: cache['v'] = data_fn()-                except Exception as e: cache['v'] = {'error': str(e)[:120]}+            if time.time() - cache['t'] >= cache['wait'] - 1:+                try:+                    cache['v'] = data_fn(); cache['wait'] = refresh_ms/1000+                except Exception as e:+                    cache['v'] = {'error': str(e)[:120]}; cache['wait'] = min(900, max(refresh_ms/1000, cache['wait']) * 2)                 cache['t'] = time.time()             return cache['v']     class H(BaseHTTPRequestHandler):@@ -63,6 +69,15 @@ def serve(title, data_fn, render_js, refresh_ms=60000, mark=None):         threading.Timer(0.5, announce).start()     srv.serve_forever() +_creds = {}+def read_json_if_changed(path):+    """Parse a JSON file only when its mtime moved; the parsed value is cached otherwise."""+    path = os.path.expanduser(path)+    st = os.stat(path)+    ent = _creds.get(path)+    if ent and ent[0] == st.st_mtime_ns: return ent[1]+    v = json.load(open(path)); _creds[path] = (st.st_mtime_ns, v); return v+ def http_json(url, headers):     req = urllib.request.Request(url, headers=headers)     with urllib.request.urlopen(req, timeout=15) as r: return json.loads(r.read().decode())
bin/hydrogen-widget-marks.py

  
install.sh

  
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-vscode",-  "version": "1.1.63",+  "version": "1.1.64",   "type": "app",   "description": "CLI + VS Code extension that lets AI assistants and the Hydrogen Desktop frontend drive VS Code from inside an Adom container: open files, live get/set of any setting/theme/font (no reload), native streamed exec, a frontend event queue, container identity (hd/hw), api-key status/inject, extension query/update, modes, paste screenshots, and any VS Code command via a local HTTP API on port 8821 (with port-file fallback discovery).",   "license": "PROPRIETARY",
page.json+2−2
@@ -4,7 +4,7 @@   "slug": "adom-vscode",   "title": "adom-vscode",   "brief": "Let Claude, Codex and Kimi drive VS Code inside your Adom container",-  "version": "1.1.63",+  "version": "1.1.64",   "license": "PROPRIETARY",   "visibility": "public",   "hero": {@@ -141,4 +141,4 @@     "adom-vscode-extensions",     "adom-vscode-modes"   ]-}\ No newline at end of file+}