Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions .agents/skills/hapsign-signing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,27 @@ Interpret doctor capabilities separately:

```text
<cli> sign --hap <absolute-input.hap> \
--output <absolute-output.hap> --browser system_controlled --json
--output <absolute-output.hap> --browser auto --events json --json
```

`system_controlled` uses an isolated Edge/Chrome context without the user's
cookies or saved passwords and grants the authorization page local callback
access. Use `system` only when the user explicitly prefers their normal
browser profile or controlled launch is unavailable; disclose that cached
SSO state, extensions, and local-network permissions can change the flow.
Use `playwright` only when bundled Chromium is explicitly preferred.
`auto` uses an isolated Edge/Chrome context in a visible desktop session.
In SSH, CI, or a headless Linux session it emits an `auth_required` event
instead of launching an invisible browser. `system_controlled` forces the isolated
browser, `system` uses the user's normal browser profile, and `playwright`
explicitly prefers bundled Chromium.

A first-time login or reauthentication after cache rejection does not support
a completely browserless, terminal-only flow. An external flow removes the
desktop requirement from the CLI host, but the user still needs a modern
browser on some computer and a secure same-port loopback forwarding path.
A cached token that the service still accepts can be reused without opening a
browser for that invocation.

Real user-visible source CLI login has been verified on Windows 11 Pro 64-bit
with isolated Edge, and Ubuntu 24.04.4 LTS x86_64 under WSL2 with the Windows
default browser. This covers the login page, loopback callback, token exchange,
and platform cache reuse. Do not extend that claim to native Linux or remote
SSH, macOS, GUI, USB, signing, or installation without separate validation.

If persistent locations matter, pass absolute `--state-dir` and either an
exact `--output` or `--output-dir`. CLI flags override
Expand All @@ -111,9 +123,23 @@ Interpret doctor capabilities separately:
requires it.

4. If authorization opens, tell the user to complete login, CAPTCHA, consent,
and two-factor verification manually, then wait for the process. Never
request, read, store, type, or automate credentials, CAPTCHA, one-time codes,
consent clicks, or tokens.
and two-factor verification manually, then wait for the process. If stderr
emits `HAPSIGN_EVENT=` with `event: auth_required` and `method` set to
`ssh_loopback` or `loopback_forwarding`, keep the process running and give
the user the exact same-port SSH forwarding command described by
`callback_port` when the host is reachable by SSH:

```text
ssh -N -L 127.0.0.1:<port>:127.0.0.1:<port> <same-SSH-target>
```

For a container or another remote runtime, use its equivalent private port
forwarding instead of exposing the callback publicly. Tell the user to open
`verification_uri` only on the computer that owns the forwarded loopback
port. Treat that URI as an ephemeral secret: do not put
it in persistent logs or share it beyond the authorizing user. Never expose
the callback on `0.0.0.0`, and never request, read, store, type, or automate
credentials, CAPTCHA, one-time codes, consent clicks, or tokens.

5. Treat a nonzero exit code or JSON `ok: false` as failure. On success, read
`signed_hap` from JSON instead of guessing a path, then verify it:
Expand Down Expand Up @@ -142,6 +168,9 @@ Keep logs at a nonsensitive level and use stage markers:

- No `[callback]` entry: the browser did not reach the loopback callback;
check browser mode and local-network access.
- `auth_required` with `method: ssh_loopback` or `loopback_forwarding`: verify
the local and remote forwarded ports are identical and the browser is on the
forwarding computer.
- Callback POST/GET without `授权回调校验成功`: inspect the redacted CSRF or
parameter error.
- Callback validation succeeded: browser spinning or subsequent
Expand Down
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
无效时会执行一次性材料刷新;能力模式可通过一致使用 `--enable-capability` 匹配,
只有其余一致性校验通过时,才可显式运行
`migrate-cache --profile-type normal|system-basic` 保留缓存。
- `HAPSIGN-BREAKING-002`:CLI 默认浏览器从普通系统 Profile 改为隔离的
`system_controlled`;可用 `--browser` 或 `HAPSIGN_BROWSER` 配置。
- `HAPSIGN-BREAKING-002`:CLI 默认浏览器从普通系统 Profile 改为自适应 `auto`;
桌面会话仍优先使用隔离的受控浏览器,SSH、CI 或无桌面 Linux 会话改为外部浏览器交接。
可用 `--browser` 或 `HAPSIGN_BROWSER` 配置。
- `HAPSIGN-BREAKING-003`:CLI 默认状态/产物从 PR #5 的 `~/.hapsign`(更早版本为
进程工作目录)改到应用目录;可用 `--state-dir`、`--output-dir`、
`HAPSIGN_SIGNING_DIR` 和 `HAPSIGN_SIGNED_HAPS_DIR` 恢复原路径;`inspect` 会检测
Expand All @@ -22,6 +23,9 @@

### Added

- CLI 新增 `auto`/`external` 浏览器模式、可配置 loopback 回调端口和认证超时;SSH、
CI 或无桌面 Linux 会话会输出安全端口转发指引,`--events json` 提供结构化中间事件且不改变
最终单行 JSON stdout 协议。
- 新增面向 Agent 的 `auth`、`devices list`、`sign`、`install`、`deploy` CLI
子命令;支持单行 JSON stdout、stderr 日志、明确退出码与输入校验。
- CLI 支持显式 HDC `--serial`、真机/模拟器候选标记、签名与安装分离,以及安装后
Expand Down
18 changes: 17 additions & 1 deletion PORTABLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,25 @@
```bash
./hapsign-cli doctor --json
./hapsign-cli sign --hap app.hap --output app-signed.hap \
--browser system_controlled --json
--browser auto --json
```

CLI 的 `auto` 模式在 SSH、CI 或无桌面 Linux 会话中会显示一次性登录地址和 callback
端口。保持命令运行,在有浏览器的电脑按提示建立同端口 SSH loopback 转发后打开该
地址;容器等运行时使用其等价的私有端口转发能力。
`--events json` 可输出带 `HAPSIGN_EVENT=` 前缀的结构化交接事件;最终 stdout JSON
协议不变。首次认证仍需要现代浏览器,CLI 不会收集账号密码或验证码。

> [!IMPORTANT]
> 首次认证以及缓存失效后的重新认证,不支持完全无浏览器的纯命令行闭环。无桌面
> Linux 可以运行 CLI,但必须能使用另一台有现代浏览器的电脑,并将同一 loopback
> 端口安全转发回 CLI。已有且仍有效的 Token 缓存可直接复用,无需每次打开浏览器。

本登录实现已在 Windows 11 专业版 64 位,以及 Ubuntu 24.04.4 LTS x86_64(WSL2)
完成源码 CLI 真实登录验证。验证覆盖用户可见登录页、loopback 回调、Token 换取和
平台缓存复用;不包含原生 Linux/远程 SSH、macOS、GUI、USB 或完整签名安装链路,
这些场景仍需分别回归。

Windows PowerShell 使用 `.\hapsign-cli.exe`。完整约定见 `AGENT_SIGNING.md`;升级
已有安装前请查看 `MIGRATIONS.md`,或读取 `doctor --json` 的机器可读变更目录。

Expand Down
58 changes: 51 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ python -m playwright install --no-shell chromium

桌面设置提供“受控系统浏览器”“内置 Chromium”和“非受控系统默认浏览器”三种
模式;环境变量 `HAPSIGN_BROWSER` 可使用 `system_controlled`、`playwright`
或 `system` 覆盖代码默认值。
或 `system` 覆盖桌面默认值。CLI 默认使用 `auto`:SSH、CI 或无桌面 Linux 会话不会
尝试启动不可见的浏览器,而会显示外部浏览器交接信息;也可显式使用 `external`。

### 可选:配置 DevEco Studio 回退路径

Expand Down Expand Up @@ -235,15 +236,28 @@ CLI 的标准输出可以保持为单个 JSON 文档,普通运行日志写入
hapsign doctor --json
hapsign inspect --hap app.hap --json
hapsign sign --hap app.hap --output artifacts/app-signed.hap \
--state-dir .hapsign-state --browser system_controlled --json
--state-dir .hapsign-state --browser auto --json
```

在 SSH、CI 或无桌面 Linux 上,`auto` 会保持命令运行并输出一次性登录地址与回调端口。
在有浏览器的电脑另开终端,把提示中的同一端口转发到运行 HapSign 的 SSH 目标,再打开
地址:

```bash
ssh -N -L 127.0.0.1:<端口>:127.0.0.1:<端口> <同一SSH目标>
```

容器等环境应使用对应的私有端口转发能力。回调服务始终只监听 loopback,不应改为公网
监听。`--events json` 会在 stderr 输出带
`HAPSIGN_EVENT=` 前缀的结构化交接事件;最终 stdout 仍只有原有单行 JSON。登录地址
包含一次性状态值,不要分享或写入持久日志。

首次为某个应用申请 Profile 时仍需要设备 UDID。默认会通过 HDC 从已连接设备读取;
如果 agent 已从可信来源获得 UDID,可以跳过本机设备探测:

```bash
hapsign sign --hap app.hap --device-udid <64位十六进制UDID> \
--browser system_controlled --json
--browser auto --json
```

同一天已有与包名、能力模式及已知设备匹配的可用签名材料缓存时,仅签名模式不要求
Expand Down Expand Up @@ -310,7 +324,7 @@ Windows/macOS 输出 `dist/HapSign-portable-<platform>.zip`,Linux 输出
hapsign doctor [--state-dir DIR] [--output-dir DIR] [--json]
hapsign inspect --hap HAP [--bundle-name NAME] [--state-dir DIR] [--enable-capability] [--json]
hapsign migrate-cache --hap HAP --profile-type normal|system-basic [--state-dir DIR] [--json]
hapsign auth [login|status] [--refresh] [--state-dir DIR] [--json]
hapsign auth [login|status] [--refresh] [认证交互选项] [--state-dir DIR] [--json]
hapsign devices [list] [--connected-only] [--json]
hapsign sign --hap HAP [--serial SERIAL | --device-udid UDID] [签名选项] [--json]
hapsign install --hap SIGNED_HAP --serial SERIAL [--bundle-name NAME] [--json]
Expand All @@ -325,7 +339,10 @@ sign / deploy 签名选项:
--output-dir DIR 未指定 --output 时的签名产物目录
--output FILE 签名 HAP 的精确输出路径
--overwrite-output 允许覆盖 --output 指定的已有文件
--browser MODE system、system_controlled 或 playwright
--browser MODE auto、external、system、system_controlled 或 playwright
--callback-port PORT loopback 回调端口,默认 0 自动分配
--auth-timeout SEC 浏览器授权等待时间,默认 600 秒
--events FORMAT 中间事件格式:human 或 json(写入 stderr)
--enable-capability 使用 Real Profile(APL=system_basic)
--refresh-token 强制浏览器认证,同时刷新签名材料
--refresh-signing 只重新申请证书/Profile,复用有效 Token
Expand Down Expand Up @@ -354,7 +371,9 @@ sign / deploy 签名选项:

### 首次运行

会弹出浏览器窗口,打开华为登录页。手动输入账号密码登录,如果有验证码或二次验证也手动处理。登录成功后浏览器会自动关闭,后续自动完成签名和安装。
有可见桌面时会打开华为登录页;SSH、CI 或无桌面 Linux 会给出外部浏览器和安全
loopback 转发指引。账号密码、验证码和二次验证始终由用户在浏览器中处理。登录成功后
继续完成签名和安装。

### Real Profile(system_basic 权限)

Expand Down Expand Up @@ -425,9 +444,34 @@ token 缓存是权限限制为 `0o600` 的明文文件,签名私钥等材料
不要上传、分享或放入云同步目录。共享电脑使用完毕后应删除 `signing_files/`。详细
说明见 [SECURITY.md](SECURITY.md)。

## 限制
## 已验证平台与规格限制

### 已实机验证的登录环境

下表只描述本次源码 CLI 的真实登录验证范围,不代表 GUI、USB、HAP 签名安装或所有
同类操作系统版本均已验证。

| CLI 运行环境 | 浏览器路径 | 已验证结果 |
| --- | --- | --- |
| Windows 11 专业版 64 位 | `auto` 启动隔离的 Edge | 用户可见登录页、loopback 回调、Token 换取、DPAPI 缓存及缓存复用均通过 |
| Ubuntu 24.04.4 LTS x86_64(WSL2) | `auto` 调用 Windows 默认浏览器 | 用户可见登录页、跨 WSL loopback 回调、Token 换取、`0o600` 缓存及缓存复用均通过 |

WSL2 结果不能替代原生 Linux 发行版或远程 SSH 环境的实机验证;这些环境应在发布前
按其网络、浏览器交接和权限配置单独回归。未列入上表的平台不等同于不支持,只表示
尚未完成同等范围的真实登录验证。

### 认证规格边界

- 首次认证以及缓存失效后的重新认证,不支持“全程只有纯命令行、任何电脑都没有可用
浏览器”的运行方式。OAuth 登录必须由用户在一台能运行现代浏览器的电脑上完成
- SSH、CI 或无桌面 Linux 可以运行 CLI;`auto`/`external` 会输出一次性登录地址和
callback 端口,通过安全的同端口 loopback 转发把浏览器回调送回 CLI。因此“不要求
Linux 主机有桌面”不等于“不需要浏览器”
- 已有且仍被服务端接受的 Token 缓存可以直接复用,此时本次调用无需再次打开浏览器
- 登录验证码 / 二次验证需要用户在浏览器中手动处理

### 其他限制

- 拖拽安装脚本仅支持 Windows(`sign_install.bat`);Linux/macOS 请使用
`hapsign` 命令行
- Windows 便携版已经过完整构建和实机流程验证;Linux x64 已有锁定公开工具链、
Expand Down
44 changes: 38 additions & 6 deletions docs/AGENT_SIGNING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@
源码安装后的命令名是 `hapsign`;便携版使用 `hapsign-cli.exe`(Windows)或
`./hapsign-cli`(Linux/macOS),其余参数和结果完全相同。

## 认证规格边界与验证范围

首次认证以及缓存失效后的重新认证,不支持完全无浏览器的纯命令行闭环。Linux CLI
可以运行在 SSH、CI 或无桌面环境中,但用户必须能在另一台有现代浏览器的电脑上打开
一次性登录地址,并通过同端口 loopback 转发把回调安全送回 CLI。“Linux 主机不需要
桌面”不表示整个认证过程不需要浏览器。已有且仍被服务端接受的 Token 缓存可以直接
复用,此时本次调用不需要打开浏览器。

源码 CLI 的用户可见真实登录已在以下环境验证:

- Windows 11 专业版 64 位:`auto` 启动隔离 Edge,完成 loopback 回调、Token 换取、
DPAPI 缓存和缓存复用
- Ubuntu 24.04.4 LTS x86_64(WSL2):`auto` 调用 Windows 默认浏览器,完成跨 WSL
loopback 回调、Token 换取、`0o600` 缓存和缓存复用

该结果不覆盖原生 Linux/远程 SSH、macOS、GUI、USB 或完整签名安装链路;未列入不
等同于不支持,但发布前应在目标环境执行相应回归。

## 稳定调用顺序

### 1. 诊断运行环境
Expand Down Expand Up @@ -58,7 +76,7 @@ Real Profile 使用 `system-basic`。不能从旧元数据可靠推断类型,
hapsign sign \
--hap app.hap \
--output artifacts/app-signed.hap \
--browser system_controlled \
--browser auto \
--json
```

Expand All @@ -67,17 +85,29 @@ hapsign sign \
拒绝时尝试刷新。agent 应提示用户在浏览器中完成必要的授权,然后继续等待进程退出。
成功结果的 `signed_hap` 是可交付产物的绝对路径。

Agent 默认应显式使用 `system_controlled`:它调用系统 Edge/Chrome,但使用隔离的临时
上下文,不复用用户 cookie、已保存密码或旧 SSO 状态,并可预授予登录页访问 loopback
回调的权限。`system` 会打开用户的普通默认浏览器 Profile,仅在用户明确希望复用登录
状态或受控浏览器不可用时使用;浏览器扩展、旧 cookie 和本地网络权限可能改变授权流程。
Agent 默认使用 `auto`。普通桌面会话会调用受控 Edge/Chrome;SSH、CI 或无桌面 Linux
会话会输出 `auth_required` 交接信息,而不是尝试启动不可见的浏览器。`system_controlled`
可强制使用隔离的临时浏览器上下文;`system` 会复用用户普通默认浏览器 Profile。

交接事件包含一次性 `verification_uri` 和 `callback_port`。`method=ssh_loopback` 或
`loopback_forwarding` 时,Agent 应提示用户保持原命令运行,并在有浏览器的电脑建立
同端口 loopback 转发:

```bash
ssh -N -L 127.0.0.1:<端口>:127.0.0.1:<端口> <同一SSH目标>
```

容器等运行时应使用其等价的私有端口转发能力。然后在建立转发的电脑打开一次性地址。
`--events json` 会把事件以
`HAPSIGN_EVENT=<json>` 写到 stderr;stdout 仍只在命令结束时输出一个 JSON 文档。
不得把登录地址写入持久日志,也不得把回调服务暴露到 `0.0.0.0`。

首次申请调试 Profile 需要设备 UDID。可以连接一台已授权调试设备让 HDC 自动读取,
也可以由调用方提供已核实的值:

```bash
hapsign sign --hap app.hap --device-udid <64位十六进制UDID> \
--browser system_controlled --json
--browser auto --json
```

当日 `metadata.json` 中的 `.p12/.cer/.p7b` 仍有效,且包名、能力模式与已知设备
Expand Down Expand Up @@ -124,6 +154,8 @@ hapsign install --hap app-signed.hap --serial <serial> --json
## 授权诊断

- 没有任何 `[callback]`:浏览器没有访问 loopback,检查浏览器模式和本地网络权限。
- `auth_required` 的 `method=ssh_loopback` 或 `loopback_forwarding`:确认本地端口和
远端回调端口完全相同,并且浏览器运行在建立转发的那台电脑上。
- 收到 POST/GET 但没有“授权回调校验成功”:检查脱敏后的 CSRF 或参数错误。
- 已校验成功:浏览器继续转圈或随后出现 `net::ERR_ABORTED` 通常是回调后关闭页面产生;
应继续检查 token 交换和后续签名阶段。
Expand Down
7 changes: 4 additions & 3 deletions docs/MIGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
| 编号 | 兼容策略 | 明确结论 |
| --- | --- | --- |
| 001 | 配置匹配、显式迁移或备份后刷新 | 接受新的缓存一致性校验 |
| 002 | 可配置恢复普通系统浏览器 | 接受 `system_controlled` 新默认值 |
| 002 | 可配置恢复普通系统浏览器 | 接受 `auto` 新默认值 |
| 003 | 可配置恢复旧状态与产物目录 | 接受应用目录新默认值 |
| 004 | 仅迁移调用方,不提供混合输出模式 | 保留 stdout 结果与 stderr 日志分离 |
| 005 | 仅迁移调用方,不提供扁平参数兼容层 | 保留显式子命令接口 |
Expand Down Expand Up @@ -78,8 +78,9 @@ hapsign inspect --hap app.hap --enable-capability --json

## HAPSIGN-BREAKING-002

CLI 登录浏览器默认值由 `system` 改为 `system_controlled`。新默认使用隔离的临时
Edge/Chrome 上下文,不带用户默认浏览器中的 cookie、保存密码、扩展和旧 SSO 状态。
CLI 登录浏览器默认值由 `system` 改为 `auto`。桌面会话优先使用隔离的临时
Edge/Chrome 上下文;SSH、CI 或无桌面 Linux 会话输出外部浏览器和安全 loopback
转发指引,不再尝试启动用户看不到的浏览器。

需要旧行为时显式配置:

Expand Down
Loading
Loading