安装与下载
1 ClawHub CLI
从源直接安装技能的最快方式。
npx clawhub@latest install local-totp-viewer
2 手动安装
将技能文件夹复制到以下位置之一
~/.openclaw/skills/
{'/skills/'}
优先级:工作区 › 全局 › 内置
3 提示词安装
将此提示词复制到 Openclaw 即可自动安装。
请帮我使用 Clawhub 安装 skill-local-totp-viewer。如果尚未安装 Clawhub,请先安装(npm i -g clawhub)。
更倾向于下载?
获取包含原生技能文件的ZIP压缩包。
基本信息
local-totp-viewer
安装指南
- ClawHub CLI
从源直接安装技能的最快方式。
npx clawhub@latest install local-totp-viewer - 手动安装
将技能文件夹复制到以下位置之一
全局模式
~/.openclaw/skills/
工作区
/skills/
优先级:工作区 > 本地 > 内置 - 提示词安装
将此提示词复制到 OpenClaw 即可自动安装。
请帮我使用 Clawhub 安装 local-totp-viewer。如果尚未安装 Clawhub,请先安装(npm i -g clawhub)。
更倾向于下载?
获取包含原生技能文件的ZIP压缩包。
下载技能 (.zip)
什么是
本地 TOTP 查看器可生成符合 RFC 6238 标准的基于时间的一次性密码,并在实时终端视图中显示,因此无需手机或外部身份验证器应用,也能在 PC 上访问双重验证代码。此 Openclaw Skills 工作流还介绍了如何通过 Base32 值或 otpauth:// URI 注册密钥,以及移除已存储的账户。
该脚本使用 Python 标准库,并将账户名称和密钥存储在 ~/.2fa_secrets.json 中,保存时会将文件权限设为 600。它是简单的本地工具,并非完整的身份验证器应用:请保护密钥文件,避免在聊天或日志中分享密钥,并在每个代码的 30 秒有效期结束前输入代码。
应用场景
手机无法使用或使用不便时,在 Linux 桌面上显示双重验证代码。
将账户从 Google Authenticator 迁移到本地桌面工作流。
在无头或 CLI 流程中获取一次性代码,由人员确认并输入每个代码。
使用 Base32 密钥或
otpauth://
URI 添加 TOTP 账户。
列出本地注册的账户,或移除不再需要的账户。
工作原理
不带参数运行单文件 Python 脚本,以加载已保存的密钥并持续刷新显示的代码。
添加新账户时,提供 Base32 密钥和账户名称,或传入 otpauth:// URI;脚本会将密钥添加到本地 JSON 存储中。
脚本根据密钥和当前时间计数器计算代码,然后显示代码以及 30 秒有效期内剩余的秒数。
使用列表选项查看已注册的账户名称,或使用移除选项从存储中删除账户。
迁移 Google Authenticator 时,导出账户、解码 QR 载荷、提取并对每个密钥进行 Base32 编码,然后使用查看器逐个注册账户。
配置指南
确认 Linux PC 上已安装 Python 3。
将技能中包含的 Python 脚本保存为 ~/2fa_view.py。查看器本身仅使用 Python 标准库;QR 解码是迁移时可选的独立步骤。
使用如下命令注册账户并启动实时查看器:
python3 ~/2fa_view.py BASE32_SECRET account-name
python3 ~/2fa_view.py
你也可以注册受支持的 otpauth:// URI、列出账户或移除账户:
python3 ~/2fa_view.py 'otpauth://totp/Example?secret=BASE32_SECRET&issuer=Example'
python3 ~/2fa_view.py -l
python3 ~/2fa_view.py -r account-name
迁移 Google Authenticator 时,从应用中导出账户并解码生成的迁移 QR 码。该技能介绍了使用 pyzbar 等 QR 库的方法;它不是查看器的运行时依赖。解析迁移 protobuf,对每个密钥进行 Base32 编码,然后逐个注册账户。
使用脚本前,请通过针对该测试向量的时间戳和八位数输出配置的测试框架,将 TOTP 计算结果与 RFC 6238 测试向量进行核对。查看器常规显示使用六位数和 30 秒周期。
数据架构
查看器将数据存储在 ~/.2fa_secrets.json 中,格式为 JSON 对象,将账户标签映射到 Base32 密钥字符串。例如:
{
"account-name": "BASE32_SECRET"
}
文件位置: 当前用户主目录中的 ~/.2fa_secrets.json。
文件权限: 脚本保存文件时设为 600;请对密钥保密,并在文件由脚本外部创建或更改时检查权限。
账户标签: 直接注册时使用提供的账户名称。URI 注册会根据发行者和密钥的前六个字符创建标签;脚本不会保留完整的 URI 元数据。
生成的数据: TOTP 代码在运行时计算并显示在终端中;它们不会作为账户记录存储。
迁移字段: 所述 Google Authenticator 导出工作流会从迁移 protobuf 中提取每个密钥、账户名称、发行者、算法、位数和 OTP 类型。查看器的 URI 注册路径只存储密钥,并使用内置的六位数、30 秒计算设置,因此请确认非默认参数账户的兼容性。
列表注意事项: -l 命令会显示每个密钥的前八个字符以及账户名称。请避免分享其输出。
高级特性
在终端中持续刷新所有已注册的代码,并显示当前 30 秒有效期内剩余的秒数。
支持直接通过 Base32 值或
otpauth://
URI 添加密钥。
提供简单的 CLI 操作,用于列出账户和移除指定账户。
说明从 Google Authenticator 的
otpauth-migration://
导出数据进行迁移的方法,包括 QR 解码和 protobuf 字段提取;查看器不内置 QR 解码功能。
使用本地 JSON 存储以及标准库中的 HMAC、散列、Base32 和时间函数,无需在运行时依赖第三方软件包。
提供安全建议:保护密钥文件,避免在对话记录中暴露密钥,并等待新的时间窗口,而不要重试已过期的代码。
name: local-totp-viewer description: Set up and use a zero-dependency local TOTP code viewer on a Linux PC. Use when a user wants 2FA/TOTP codes shown on the desktop, wants to migrate secrets out of Google Authenticator, or needs one-time codes without a phone. license: MIT
Local TOTP Viewer
Show and manage TOTP (RFC 6238) codes directly on a PC with a single-file Python script — no phone, no external app, no dependencies.
When to use
The user wants 2FA codes displayed on their desktop instead of a phone.
Migrating secrets from Google Authenticator to a desktop workflow.
A headless/CLI workflow needs one-time codes (e.g. automated login steps where a human confirms each code).
The script
Save this as ~/2fa_view.py (pure stdlib + optional system qrcode not required):
#!/usr/bin/env python3 """Zero-dependency TOTP viewer. Usage:
python3 2fa_view.py # live view of all codes
python3 2fa_view.py # register a secret
python3 2fa_view.py "otpauth://..." # register from URI
python3 2fa_view.py -l # list
python3 2fa_view.py -r # remove
"""
import base64, hashlib, hmac, json, os, struct, sys, time, urllib.parse
STORE = os.path.expanduser("~/.2fa_secrets.json")
def load():
if os.path.exists(STORE):
with open(STORE) as f:
return json.load(f)
return {}
def save(d):
with open(STORE, "w") as f:
json.dump(d, f)
os.chmod(STORE, 0o600)
def totp(secret_b32, digits=6, period=30, algo="sha1"):
key = base64.b32decode(secret_b32.upper() + "=" * (-len(secret_b32) % 8))
counter = int(time.time()) // period
msg = struct.pack(">Q", counter)
h = hmac.new(key, msg, hashlib.sha1 if algo == "sha1" else hashlib.sha256).digest()
o = h[-1] & 0x0F
code = (struct.unpack(">I", h[o:o+4])[0] & 0x7FFFFFFF) % (10 ** digits)
return str(code).zfill(digits)
def main():
d = load()
args = sys.argv[1:]
if not args:
try:
while True:
now = int(time.time())
left = 30 - (now % 30)
line = []
for name, s in sorted(d.items()):
line.append(f"{name}: {totp(s)} ({left:2d}s)")
print(" | ".join(line) + " ", end="\r")
time.sleep(0.25)
except KeyboardInterrupt:
print()
return
if args[0] == "-l":
for name, s in sorted(d.items()):
print(f"{name} {s[:8]}...")
return
if args[0] == "-r":
d.pop(args[1], None); save(d); print("removed"); return
if args[0].startswith("otpauth://"):
q = urllib.parse.parse_qs(urllib.parse.urlparse(args[0]).query)
d[q.get("issuer", ["?"])[0] + ":" + q["secret"][0][:6]] = q["secret"][0]
save(d); print("registered from URI"); return
d[args[1]] = args[0]
save(d)
print(f"registered {args[1]}")
if __name__ == "__main__":
main()
Verify against the RFC 6238 test vector (secret GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ, T=59s, SHA1 → 94287082) before trusting it.
Migrating secrets from Google Authenticator
Google Authenticator can export all accounts as a single QR code:
In the app: menu → Transfer accounts → Export accounts → select accounts.
Screenshot the QR code and pass the image to the agent.
Decode it with a QR library (e.g. pyzbar); OpenCV's QR detector often fails on app screenshots.
The payload is an otpauth-migration://offline?data= URI. URL-decode, then parse the protobuf:
field 1 (otp_parameters): secret = field 1 (bytes), name = field 2, issuer = field 3, algo = field 4 (1=SHA1), digits = field 5 (1=6 digits), type = field 6 (2=TOTP).
Encode each secret as base32: base64.b32encode(secret).rstrip(b"=").
Register each secret with the viewer script.
Pitfalls
Generate a code immediately before entering it. TOTP windows are 30 seconds; a pre-generated code can expire mid-entry.
Keep the secrets file at mode 600 and never print full secrets into chat logs or transcripts.
When automating a login form, type the code within the window and handle rejection by waiting for the next window rather than retrying the same code.
定时发布