app
adom-vscode
Public Made by Adomby adom
Let Claude, Codex and Kimi drive VS Code inside your Adom container
← Commit history
release 1.1.64
10 files changed
+32−15
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.pyinstall.shpackage.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" ]-}+}