tirtc-device-builder 0.8.1 → 0.9.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tirtc-device-builder",
3
- "version": "0.8.1",
3
+ "version": "0.9.1",
4
4
  "description": "Codex workflows for building and validating TiRTC device firmware across supported chip platforms.",
5
5
  "author": {
6
6
  "name": "TangeAI",
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  This project follows Semantic Versioning.
4
4
 
5
+ ## 0.9.1
6
+
7
+ - Pin ESP32 Device Kit 1.1.2 with an open `TiRTC-` SoftAP and provisioning at
8
+ `http://192.168.6.1`.
9
+ - Reject Device Kit packaging when implementation or documentation retains the
10
+ legacy SSID, password, or `192.168.4.1` provisioning contract.
11
+ - Make the Hardware IR SoftAP defaults and validation match the device runtime.
12
+
13
+ ## 0.9.0
14
+
15
+ - Add native Skill-directory installation for Codex, Claude Code, OpenCode,
16
+ Gemini CLI, GitHub Copilot, Qwen Code, Windsurf Cascade, Cline, and Kiro.
17
+ - Add `--client` support to both `install` and the resumable `setup` workflow,
18
+ plus a `clients` command that reports resolved user directories.
19
+ - Keep one portable `SKILL.md` and supporting resource tree across clients,
20
+ while preserving explicit replacement and hardware-operation approvals.
21
+
5
22
  ## 0.8.1
6
23
 
7
24
  - Separate rapid `idf.py flash` iteration from portable evidence-bundle export,
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TiRTC Device Builder
2
2
 
3
- TiRTC Device Builder 用于把 ESP32-S3/ESP32-P4 开发板接入 TiRTC。输入可以只有开发板型号,也可以包含原理图、BSP、引脚表和外设示例。安装后的 Codex Skill 会先检查环境、整理有依据的硬件事实,再生成或移植独立的 ESP-IDF 工程并完成板级适配和编译。烧录和实机验证只有在开发者明确给出目标串口并授权后才会执行。
3
+ TiRTC Device Builder 用于把 ESP32-S3/ESP32-P4 开发板接入 TiRTC。输入可以只有开发板型号,也可以包含原理图、BSP、引脚表和外设示例。安装后的 Agent Skill 会先检查环境、整理有依据的硬件事实,再生成或移植独立的 ESP-IDF 工程并完成板级适配和编译。烧录和实机验证只有在开发者明确给出目标串口并授权后才会执行。
4
4
 
5
5
  当前仓库提供一个 Skill:
6
6
 
@@ -12,7 +12,7 @@ H5/AI 的 ESP32-S3 托管模板、协议文档和 TiRTC SDK 已打包在独立
12
12
 
13
13
  - npm 包:[tirtc-device-builder](https://www.npmjs.com/package/tirtc-device-builder)
14
14
  - GitHub 仓库:[tangeai/tirtc-device-builder](https://github.com/tangeai/tirtc-device-builder)
15
- - ESP32 Device Kit:[kit-esp32s3-v1.1.1](https://github.com/tangeai/tirtc-device-builder/releases/tag/kit-esp32s3-v1.1.1)
15
+ - ESP32 Device Kit:[kit-esp32s3-v1.1.2](https://github.com/tangeai/tirtc-device-builder/releases/tag/kit-esp32s3-v1.1.2)
16
16
 
17
17
  文档导航:
18
18
 
@@ -43,9 +43,15 @@ npm --version
43
43
  npx --yes tirtc-device-builder@latest setup esp32 --install
44
44
  ```
45
45
 
46
+ 默认安装到 Codex。使用其他客户端时增加 `--client`,例如:
47
+
48
+ ```bash
49
+ npx --yes tirtc-device-builder@latest setup esp32 --install --client qwen-code
50
+ ```
51
+
46
52
  这条命令会:
47
53
 
48
- - 把 `tirtc-esp32-builder` 安装到 Codex Skill 目录;
54
+ - 把 `tirtc-esp32-builder` 安装到所选 Agent 客户端的 Skill 目录;
49
55
  - 下载 ESP32 Device Kit 并校验 SHA-256;
50
56
  - 复用当前可用的 ESP-IDF 5.5.x,找不到时安装 ESP-IDF 5.5.4;
51
57
  - 安装 Espressif 管理的 ESP32-S3 工具链;
@@ -59,21 +65,21 @@ npx --yes tirtc-device-builder@latest setup esp32 --install
59
65
  ```text
60
66
  OVERALL: PASS
61
67
  SETUP: READY
62
- Start a new Codex session and invoke $tirtc-esp32-builder.
68
+ Start a new Codex session, then ask it to use tirtc-esp32-builder.
63
69
  ```
64
70
 
65
71
  如果看到 `OVERALL: NEEDS_SETUP`、`MISS` 或 `FAIL`,先看[常见问题](#常见问题)。
66
72
 
67
- ### 3. 重新打开 Codex
73
+ ### 3. 重新打开 Agent 客户端
68
74
 
69
- Skill Codex 会话启动时被发现。安装完成后,关闭当前 Codex 会话,再打开一个新会话。
75
+ Skill 通常在 Agent 会话启动时被发现。安装完成后,关闭当前会话,再打开一个新会话。
70
76
 
71
- ### 4. 把板卡和目标告诉 Codex
77
+ ### 4. 把板卡和目标告诉 Agent
72
78
 
73
79
  把你已经掌握的信息填进下面的提示词即可,不用先查齐所有硬件参数。先指定工作区根目录,本地路径尽量相对工作区填写;不确定的内容写“未知”。可直接复制的版本见[开发板接入提示词](skills/tirtc-esp32-builder/assets/developer-intake-prompt.md)。
74
80
 
75
81
  ```text
76
- 请使用 $tirtc-esp32-builder 完成这块开发板的 TiRTC 移植。
82
+ 请使用 tirtc-esp32-builder Skill 完成这块开发板的 TiRTC 移植。
77
83
 
78
84
  开发板:
79
85
  - 厂商、完整型号、PCB/硬件版本:<填写>
@@ -277,7 +283,7 @@ board-materials/
277
283
  | ESP-IDF | 5.5.x |
278
284
  | 自动安装版本 | ESP-IDF v5.5.4 |
279
285
  | TiRTC SDK | `espressif-esp32s3/2.3.0` |
280
- | ESP32 Device Kit | 1.1.1 |
286
+ | ESP32 Device Kit | 1.1.2 |
281
287
  | Node.js | 18 或更高版本 |
282
288
  | 支持自动安装的系统 | Linux、WSL、macOS |
283
289
  | 原生 Windows | 使用 Espressif 官方安装器准备 ESP-IDF,再重新运行检查 |
@@ -339,27 +345,54 @@ tirtc-device-builder setup esp32 --install
339
345
 
340
346
  安装命令是 `npm install`,不要写成 `npm --install`。
341
347
 
342
- 只执行 `npm install --global tirtc-device-builder` 会安装 CLI,不会准备完整开发环境。随后还要运行 `setup esp32 --install`,由它安装 Codex Skill、下载 Device Kit,并在需要时安装 ESP-IDF。npm 包没有 `preinstall`、`install` 或 `postinstall` 生命周期脚本,因此不会在安装 CLI 时改动这些目录。
348
+ 只执行 `npm install --global tirtc-device-builder` 会安装 CLI,不会准备完整开发环境。随后还要运行 `setup esp32 --install`,由它安装 Agent Skill、下载 Device Kit,并在需要时安装 ESP-IDF。npm 包没有 `preinstall`、`install` 或 `postinstall` 生命周期脚本,因此不会在安装 CLI 时改动这些目录。
343
349
 
344
350
  安装公开包不需要执行 `npm login`;这个命令只和维护者发布新版本有关。
345
351
 
346
352
  ### 只安装 Skill
347
353
 
348
- 如果 ESP-IDF 和 Device Kit 已经由团队统一准备,可以只复制 Codex Skill:
354
+ 如果 ESP-IDF 和 Device Kit 已经由团队统一准备,可以只复制 Agent Skill:
349
355
 
350
356
  ```bash
351
357
  npx --yes tirtc-device-builder@latest install esp32
358
+ npx --yes tirtc-device-builder@latest install esp32 --client gemini
352
359
  ```
353
360
 
354
361
  这条命令不安装 ESP-IDF,也不下载 Device Kit。完整的新用户环境仍建议使用 `setup esp32 --install`。
355
362
 
363
+ ### 支持的 Agent 客户端
364
+
365
+ 同一个 `tirtc-esp32-builder` Skill 会完整复制到所选客户端的原生目录,不维护客户端专用的内容副本。先用下面的命令查看当前机器解析出的目录:
366
+
367
+ ```bash
368
+ npx --yes tirtc-device-builder@latest clients
369
+ ```
370
+
371
+ | `--client` | 客户端 | 默认 Skill 根目录 |
372
+ |---|---|---|
373
+ | `codex` | Codex(默认) | `${CODEX_HOME:-~/.codex}/skills` |
374
+ | `claude-code` | Claude Code | `~/.claude/skills` |
375
+ | `opencode` | OpenCode | `${XDG_CONFIG_HOME:-~/.config}/opencode/skills` |
376
+ | `gemini` | Gemini CLI | `~/.gemini/skills` |
377
+ | `copilot` | GitHub Copilot | `~/.copilot/skills` |
378
+ | `qwen-code` | Qwen Code | `~/.qwen/skills` |
379
+ | `windsurf` | Windsurf Cascade | `~/.codeium/windsurf/skills` |
380
+ | `cline` | Cline | `~/.cline/skills` |
381
+ | `kiro` | Kiro | `~/.kiro/skills` |
382
+
383
+ 也可以使用 `claude`、`gemini-cli`、`github-copilot`、`qwen`、`cascade`、`kiro-cli` 等别名。`--skills-dir` 的优先级高于默认目录,适合项目级安装或客户端使用非默认配置目录的情况。
384
+
385
+ `--client` 只选择 Skill 的安装目录和提示文案,不绑定模型服务。DeepSeek、Qwen、GLM、Kimi、OpenAI 或 Claude 等模型仍在对应客户端中配置;只要客户端能发现 Skill 并允许文件、终端等所需工具,就继续使用同一份工作流。
386
+
387
+ Cline 当前还需要在 `Settings → Features → Enable Skills` 中启用实验性 Skills 功能;安装器不会修改客户端自身的权限或功能开关。
388
+
356
389
  ### 默认目录
357
390
 
358
391
  | 内容 | 默认位置 |
359
392
  |---|---|
360
- | Codex Skill | `${CODEX_HOME:-~/.codex}/skills/tirtc-esp32-builder` |
393
+ | Agent Skill | 上表中所选目录下的 `tirtc-esp32-builder` |
361
394
  | 托管根目录 | `~/.tirtc-device-builder` |
362
- | Device Kit | `~/.tirtc-device-builder/kits/esp32s3/1.1.1` |
395
+ | Device Kit | `~/.tirtc-device-builder/kits/esp32s3/1.1.2` |
363
396
  | ESP-IDF | `~/.tirtc-device-builder/esp-idf-v5.5.4` |
364
397
  | Espressif 工具 | `~/.tirtc-device-builder/espressif` |
365
398
  | 安装记录 | `~/.tirtc-device-builder/config.json` |
@@ -404,7 +437,7 @@ npx --yes tirtc-device-builder@latest setup esp32 --install \
404
437
 
405
438
  ```bash
406
439
  npx --yes tirtc-device-builder@latest setup esp32 --install \
407
- --kit-archive /absolute/path/tirtc-esp32s3-kit-1.1.1.tar.gz
440
+ --kit-archive /absolute/path/tirtc-esp32s3-kit-1.1.2.tar.gz
408
441
  ```
409
442
 
410
443
  安装器仍会核对固定的 SHA-256、目录结构、清单和每个资源文件,不接受未经验证的同名压缩包。
@@ -436,6 +469,7 @@ npx --yes tirtc-device-builder@latest setup esp32
436
469
  ```bash
437
470
  npx --yes tirtc-device-builder@latest --help
438
471
  npx --yes tirtc-device-builder@latest list
472
+ npx --yes tirtc-device-builder@latest clients
439
473
  npx --yes tirtc-device-builder@latest setup esp32 --help
440
474
  ```
441
475
 
@@ -618,7 +652,7 @@ bootloader、分区表和应用镜像;无需手工维护多条 `esptool.py`
618
652
 
619
653
  ### Wi-Fi 凭证和设备绑定
620
654
 
621
- SoftAP 是可选方案,不是接入前提。Hardware IR 要根据 BSP 的实际能力和产品要求,选择 SoftAP、BLE、SmartConfig、安全工厂/NVS 注入、不纳入版本控制的开发配置,或有文档的自定义方案。SSID 和密码不应写死在源码中。
655
+ SoftAP 是可选方案,不是接入前提。Hardware IR 要根据 BSP 的实际能力和产品要求,选择 SoftAP、BLE、SmartConfig、安全工厂/NVS 注入、不纳入版本控制的开发配置,或有文档的自定义方案。若选择 SoftAP,热点名必须以 `TiRTC-` 开头、使用开放认证(无需密码),AP 网关和配网页面地址固定为 `192.168.6.1`(`http://192.168.6.1`)。这些值要写入 Hardware IR 并通过门禁;目标 WLAN 的 SSID 和密码仍不得写死在源码中。
622
656
 
623
657
  无论选择哪种方法,都要有当前 PCB/BSP 的支持证据,凭证不能进入 Git、源码或报告,并且要保留清除或重配入口。没有 SoftAP、但支持工厂 NVS 注入的设备同样可以接入。只要工程提交了明文密码,Hardware IR v2 门禁就会判为 `BLOCKED`。
624
658
 
@@ -759,15 +793,16 @@ npx --yes tirtc-device-builder@latest setup esp32 --install \
759
793
 
760
794
  如果指定目录已存在但内容不完整或版本错误,安装器会拒绝覆盖。换一个空的新路径,或先人工备份原目录。
761
795
 
762
- ### 安装完成后 Codex 找不到 Skill
796
+ ### 安装完成后 Agent 客户端找不到 Skill
763
797
 
764
- 关闭当前 Codex 会话并重新打开。再检查默认文件是否存在:
798
+ 关闭当前 Agent 会话并重新打开。先查看客户端目录,再检查对应文件是否存在:
765
799
 
766
800
  ```bash
767
- ls -l ~/.codex/skills/tirtc-esp32-builder/SKILL.md
801
+ npx --yes tirtc-device-builder@latest clients
802
+ ls -l <skills-dir>/tirtc-esp32-builder/SKILL.md
768
803
  ```
769
804
 
770
- 设置过 `CODEX_HOME` 或 `--skills-dir` 时,要检查对应目录。不要同时把 Skill 安装到多个位置。
805
+ 确认安装和启动的是同一个客户端,例如 Qwen Code 要使用 `--client qwen-code`。设置过 `CODEX_HOME`、`XDG_CONFIG_HOME` 或 `--skills-dir` 时,要检查对应目录。项目级目录由客户端自身规则决定,可以用 `--skills-dir` 显式指定。
771
806
 
772
807
  ### 已有 Skill,安装器拒绝覆盖
773
808
 
@@ -785,7 +820,7 @@ npx --yes tirtc-device-builder@latest setup esp32 --install --force-skill
785
820
 
786
821
  ```bash
787
822
  npx --yes tirtc-device-builder@latest setup esp32 --install \
788
- --kit-archive /absolute/path/tirtc-esp32s3-kit-1.1.1.tar.gz
823
+ --kit-archive /absolute/path/tirtc-esp32s3-kit-1.1.2.tar.gz
789
824
  ```
790
825
 
791
826
  安装器会校验 SHA-256 和内部文件清单。如果校验不一致,请重新获取官方 Release 附件,不要跳过校验。
@@ -897,25 +932,25 @@ python3 ~/.codex/skills/.system/plugin-creator/scripts/validate_plugin.py .
897
932
  ```bash
898
933
  npm run pack:esp32-kit -- \
899
934
  --source /absolute/path/tirtc-server-example/thing-connect \
900
- --kit-version 1.1.1
935
+ --kit-version 1.1.2
901
936
  ```
902
937
 
903
938
  输出位于 `dist/`:
904
939
 
905
940
  ```text
906
- tirtc-esp32s3-kit-1.1.1.tar.gz
907
- tirtc-esp32s3-kit-1.1.1.tar.gz.sha256
941
+ tirtc-esp32s3-kit-1.1.2.tar.gz
942
+ tirtc-esp32s3-kit-1.1.2.tar.gz.sha256
908
943
  ```
909
944
 
910
945
  校验后推送独立的 `kit-esp32s3-v<version>` 标签。`publish-kit.yml` 会从 metadata 固定的上游 commit 重建压缩包、核对 SHA-256,并使用 GitHub Actions token 创建 Release:
911
946
 
912
947
  ```bash
913
948
  cd dist
914
- sha256sum -c tirtc-esp32s3-kit-1.1.1.tar.gz.sha256
949
+ sha256sum -c tirtc-esp32s3-kit-1.1.2.tar.gz.sha256
915
950
  cd ..
916
951
 
917
- git tag -a kit-esp32s3-v1.1.1 -m "TiRTC ESP32-S3 Device Kit 1.1.1"
918
- git push origin kit-esp32s3-v1.1.1
952
+ git tag -a kit-esp32s3-v1.1.2 -m "TiRTC ESP32-S3 Device Kit 1.1.2"
953
+ git push origin kit-esp32s3-v1.1.2
919
954
  ```
920
955
 
921
956
  metadata 中的版本、标签、上游 commit 和期望 SHA-256 必须与本地可复现打包结果一致;工作流不会从浮动的 `main` 取发布内容。
@@ -926,8 +961,8 @@ metadata 中的版本、标签、上游 commit 和期望 SHA-256 必须与本地
926
961
 
927
962
  ```bash
928
963
  npm test
929
- git tag -a v0.8.1 -m "v0.8.1"
930
- git push origin v0.8.1
964
+ git tag -a v0.9.1 -m "v0.9.1"
965
+ git push origin v0.9.1
931
966
  ```
932
967
 
933
968
  不要重复发布已经存在的 npm 版本。版本变化同步更新 `package.json`、`.codex-plugin/plugin.json` 和发布说明。
@@ -0,0 +1,141 @@
1
+ import { homedir } from "node:os";
2
+ import { join, resolve } from "node:path";
3
+
4
+ export const DEFAULT_AGENT_CLIENT = "codex";
5
+
6
+ const CLIENTS = [
7
+ {
8
+ id: "codex",
9
+ displayName: "Codex",
10
+ aliases: ["codex"],
11
+ skillsDir(environment, home) {
12
+ const codexHome = environment.CODEX_HOME
13
+ ? resolve(environment.CODEX_HOME)
14
+ : join(home, ".codex");
15
+ return join(codexHome, "skills");
16
+ },
17
+ },
18
+ {
19
+ id: "claude-code",
20
+ displayName: "Claude Code",
21
+ aliases: ["claude-code", "claude"],
22
+ skillsDir(_environment, home) {
23
+ return join(home, ".claude", "skills");
24
+ },
25
+ },
26
+ {
27
+ id: "opencode",
28
+ displayName: "OpenCode",
29
+ aliases: ["opencode", "open-code"],
30
+ skillsDir(environment, home) {
31
+ const configHome = environment.XDG_CONFIG_HOME
32
+ ? resolve(environment.XDG_CONFIG_HOME)
33
+ : join(home, ".config");
34
+ return join(configHome, "opencode", "skills");
35
+ },
36
+ },
37
+ {
38
+ id: "gemini",
39
+ displayName: "Gemini CLI",
40
+ aliases: ["gemini", "gemini-cli"],
41
+ skillsDir(_environment, home) {
42
+ return join(home, ".gemini", "skills");
43
+ },
44
+ },
45
+ {
46
+ id: "copilot",
47
+ displayName: "GitHub Copilot",
48
+ aliases: ["copilot", "github-copilot"],
49
+ skillsDir(_environment, home) {
50
+ return join(home, ".copilot", "skills");
51
+ },
52
+ },
53
+ {
54
+ id: "qwen-code",
55
+ displayName: "Qwen Code",
56
+ aliases: ["qwen-code", "qwen"],
57
+ skillsDir(_environment, home) {
58
+ return join(home, ".qwen", "skills");
59
+ },
60
+ },
61
+ {
62
+ id: "windsurf",
63
+ displayName: "Windsurf Cascade",
64
+ aliases: ["windsurf", "cascade"],
65
+ skillsDir(_environment, home) {
66
+ return join(home, ".codeium", "windsurf", "skills");
67
+ },
68
+ },
69
+ {
70
+ id: "cline",
71
+ displayName: "Cline",
72
+ aliases: ["cline"],
73
+ activationNote:
74
+ "Cline may require Settings > Features > Enable Skills before discovery.",
75
+ skillsDir(_environment, home) {
76
+ return join(home, ".cline", "skills");
77
+ },
78
+ },
79
+ {
80
+ id: "kiro",
81
+ displayName: "Kiro",
82
+ aliases: ["kiro", "kiro-cli"],
83
+ skillsDir(_environment, home) {
84
+ return join(home, ".kiro", "skills");
85
+ },
86
+ },
87
+ ];
88
+
89
+ const CLIENT_BY_ALIAS = new Map(
90
+ CLIENTS.flatMap((client) =>
91
+ client.aliases.map((alias) => [alias, client]),
92
+ ),
93
+ );
94
+
95
+ function userHome(environment) {
96
+ if (process.platform === "win32") {
97
+ if (environment.USERPROFILE) {
98
+ return resolve(environment.USERPROFILE);
99
+ }
100
+ if (environment.HOMEDRIVE && environment.HOMEPATH) {
101
+ return resolve(`${environment.HOMEDRIVE}${environment.HOMEPATH}`);
102
+ }
103
+ } else if (environment.HOME) {
104
+ return resolve(environment.HOME);
105
+ }
106
+ return homedir();
107
+ }
108
+
109
+ export function listAgentClients() {
110
+ return CLIENTS;
111
+ }
112
+
113
+ export function resolveAgentClient(identifier) {
114
+ return CLIENT_BY_ALIAS.get(String(identifier).trim().toLowerCase()) ?? null;
115
+ }
116
+
117
+ export function requireAgentClient(identifier) {
118
+ const client = resolveAgentClient(identifier);
119
+ if (!client) {
120
+ throw new Error(
121
+ `unsupported client: ${identifier}; supported clients: ${CLIENTS.map((item) => item.id).join(", ")}`,
122
+ );
123
+ }
124
+ return client;
125
+ }
126
+
127
+ export function defaultSkillsDir(client, environment = process.env) {
128
+ const resolvedClient =
129
+ typeof client === "string" ? requireAgentClient(client) : client;
130
+ return resolvedClient.skillsDir(environment, userHome(environment));
131
+ }
132
+
133
+ export function clientSessionHint(client, skillName) {
134
+ const lines = [
135
+ `Start a new ${client.displayName} session, then ask it to use ${skillName}.`,
136
+ ];
137
+ if (client.activationNote) {
138
+ lines.push(client.activationNote);
139
+ }
140
+ return lines.join("\n");
141
+ }
@@ -1,9 +1,9 @@
1
1
  export const ESP32_KIT = Object.freeze({
2
- archiveName: "tirtc-esp32s3-kit-1.1.1.tar.gz",
3
- archiveRoot: "tirtc-esp32s3-kit-1.1.1",
4
- releaseTag: "kit-esp32s3-v1.1.1",
5
- sha256: "d9818b1caf73b039d46b7f973ad675ac750d259940db4ef22b35a76896a0cdf3",
6
- sourceCommit: "35acd405ffb0ed0917fc0671033b844739fe93dd",
7
- url: "https://github.com/tangeai/tirtc-device-builder/releases/download/kit-esp32s3-v1.1.1/tirtc-esp32s3-kit-1.1.1.tar.gz",
8
- version: "1.1.1",
2
+ archiveName: "tirtc-esp32s3-kit-1.1.2.tar.gz",
3
+ archiveRoot: "tirtc-esp32s3-kit-1.1.2",
4
+ releaseTag: "kit-esp32s3-v1.1.2",
5
+ sha256: "93dc63ee6450d54e7f31f9a2f0880b462647faa52fd7e70c287bb71f8d3e0b47",
6
+ sourceCommit: "9aaf47eb3e120ac146b72140e5357b36f4b131f5",
7
+ url: "https://github.com/tangeai/tirtc-device-builder/releases/download/kit-esp32s3-v1.1.2/tirtc-esp32s3-kit-1.1.2.tar.gz",
8
+ version: "1.1.2",
9
9
  });
@@ -10,6 +10,13 @@ import {
10
10
  } from "node:fs";
11
11
  import { homedir } from "node:os";
12
12
  import { delimiter, dirname, join, resolve } from "node:path";
13
+ import {
14
+ clientSessionHint,
15
+ DEFAULT_AGENT_CLIENT,
16
+ defaultSkillsDir,
17
+ listAgentClients,
18
+ requireAgentClient,
19
+ } from "./agent-clients.js";
13
20
  import { ESP32_KIT } from "./esp32-kit-metadata.js";
14
21
 
15
22
  const EXPECTED_IDF_LINE = "5.5";
@@ -43,10 +50,10 @@ function setupRootFrom(environment) {
43
50
  );
44
51
  }
45
52
 
46
- function takeValue(args, index, option) {
53
+ function takeValue(args, index, option, kind = "a path") {
47
54
  const value = args[index + 1];
48
55
  if (!value || value.startsWith("--")) {
49
- throw new Error(`${option} requires a path`);
56
+ throw new Error(`${option} requires ${kind}`);
50
57
  }
51
58
  return value;
52
59
  }
@@ -54,13 +61,15 @@ function takeValue(args, index, option) {
54
61
  export function parseSetupOptions(args, defaults = {}) {
55
62
  const environment = defaults.environment ?? process.env;
56
63
  const options = {
64
+ client:
65
+ defaults.client ?? requireAgentClient(DEFAULT_AGENT_CLIENT),
57
66
  forceSkill: false,
58
67
  help: false,
59
68
  idfDir: null,
60
69
  install: false,
61
70
  kitArchive: null,
62
71
  rootDir: setupRootFrom(environment),
63
- skillsDir: defaults.skillsDir,
72
+ skillsDir: null,
64
73
  thingConnectRoot: null,
65
74
  };
66
75
 
@@ -78,6 +87,13 @@ export function parseSetupOptions(args, defaults = {}) {
78
87
  options.forceSkill = true;
79
88
  continue;
80
89
  }
90
+ if (argument === "--client") {
91
+ options.client = requireAgentClient(
92
+ takeValue(args, index, argument, "a name"),
93
+ );
94
+ index += 1;
95
+ continue;
96
+ }
81
97
  if (
82
98
  argument === "--root" ||
83
99
  argument === "--skills-dir" ||
@@ -103,9 +119,7 @@ export function parseSetupOptions(args, defaults = {}) {
103
119
  throw new Error(`unknown setup option: ${argument}`);
104
120
  }
105
121
 
106
- if (!options.skillsDir) {
107
- throw new Error("setup requires a default skills directory");
108
- }
122
+ options.skillsDir ??= defaultSkillsDir(options.client, environment);
109
123
  if (options.forceSkill && !options.install) {
110
124
  throw new Error("--force-skill requires --install");
111
125
  }
@@ -123,8 +137,9 @@ Usage:
123
137
 
124
138
  Options:
125
139
  --install Install missing user-space components
140
+ --client <name> Agent client (default: codex)
126
141
  --root <path> Managed files (default: ~/.tirtc-device-builder)
127
- --skills-dir <path> Codex skills directory
142
+ --skills-dir <path> Override the selected client's Skill directory
128
143
  --thing-connect-root <path> Reuse an existing Device Kit or legacy workspace
129
144
  --kit-archive <path> Install from a local verified Kit archive
130
145
  --idf-dir <path> Reuse or install ESP-IDF at this path
@@ -133,12 +148,13 @@ Options:
133
148
 
134
149
  Examples:
135
150
  npx tirtc-device-builder setup esp32
136
- npx tirtc-device-builder setup esp32 --install
151
+ npx tirtc-device-builder setup esp32 --install --client gemini
137
152
  npx tirtc-device-builder setup esp32 --install --root /opt/tirtc-dev
138
153
 
139
154
  Check mode is read-only. --install downloads the pinned ESP32 Device Kit,
140
155
  may clone ESP-IDF v5.5.4, runs Espressif's user-space tool installer, and
141
- installs the Codex Skill. It does not run sudo or edit shell profiles.`);
156
+ installs the Agent Skill. It does not run sudo or edit shell profiles.
157
+ Supported clients: ${listAgentClients().map((client) => client.id).join(", ")}.`);
142
158
  }
143
159
 
144
160
  function printCheck(status, name, detail) {
@@ -420,6 +436,7 @@ function writeManagedEnvironment(context, packageVersion) {
420
436
  device_kit_version: context.deviceKit.version,
421
437
  device_kit_root: context.thingConnectRoot,
422
438
  thing_connect_root: context.thingConnectRoot,
439
+ client: context.client.id,
423
440
  skills_dir: context.skillsDir,
424
441
  };
425
442
  writeAtomic(
@@ -542,6 +559,7 @@ function setupContext(options, runtime) {
542
559
  const skillVersion = readSkillVersion(skillTarget);
543
560
  return {
544
561
  activeIdf: active,
562
+ client: options.client,
545
563
  configPath,
546
564
  idf,
547
565
  idfDir,
@@ -569,8 +587,8 @@ function printState(context, runtime) {
569
587
  printCheck("INFO", "setup root", context.rootDir);
570
588
  printCheck(
571
589
  context.skillReady ? "PASS" : "MISS",
572
- "Codex Skill",
573
- `${context.skillTarget} (version ${
590
+ "Agent Skill",
591
+ `${context.client.displayName}: ${context.skillTarget} (version ${
574
592
  context.skillVersion || "missing"
575
593
  }; expected ${runtime.packageVersion})`,
576
594
  );
@@ -639,13 +657,13 @@ function installSkill(options, context, runtime) {
639
657
  const present = existsSync(join(context.skillTarget, "SKILL.md"));
640
658
  if (present && context.skillReady && !options.forceSkill) {
641
659
  console.log(
642
- `SKIP Codex Skill ${runtime.packageVersion} already exists: ${context.skillTarget}`,
660
+ `SKIP ${context.client.displayName} Skill ${runtime.packageVersion} already exists: ${context.skillTarget}`,
643
661
  );
644
662
  return;
645
663
  }
646
664
  if (present && !options.forceSkill) {
647
665
  throw new Error(
648
- `Codex Skill at ${context.skillTarget} has version ${
666
+ `${context.client.displayName} Skill at ${context.skillTarget} has version ${
649
667
  context.skillVersion || "missing"
650
668
  }, expected ${runtime.packageVersion}; rerun with --force-skill to replace it`,
651
669
  );
@@ -654,6 +672,8 @@ function installSkill(options, context, runtime) {
654
672
  runtime.cliPath,
655
673
  "install",
656
674
  runtime.platform.name,
675
+ "--client",
676
+ context.client.id,
657
677
  "--skills-dir",
658
678
  options.skillsDir,
659
679
  ];
@@ -830,8 +850,8 @@ export function runEsp32Setup(args, input) {
830
850
  let options;
831
851
  try {
832
852
  options = parseSetupOptions(args, {
853
+ client: input.defaultClient,
833
854
  environment: runtime.environment,
834
- skillsDir: input.defaultSkillsDir,
835
855
  });
836
856
  } catch (error) {
837
857
  console.error(
@@ -849,8 +869,12 @@ export function runEsp32Setup(args, input) {
849
869
  if (!options.install) {
850
870
  if (!isReady(context)) {
851
871
  console.log("OVERALL: NEEDS_SETUP");
872
+ const clientOption =
873
+ context.client.id === "codex"
874
+ ? ""
875
+ : ` --client ${context.client.id}`;
852
876
  console.log(
853
- `NEXT: npx tirtc-device-builder@${runtime.packageVersion} setup esp32 --install`,
877
+ `NEXT: npx tirtc-device-builder@${runtime.packageVersion} setup esp32 --install${clientOption}`,
854
878
  );
855
879
  return 1;
856
880
  }
@@ -867,7 +891,9 @@ export function runEsp32Setup(args, input) {
867
891
  return 1;
868
892
  }
869
893
  console.log("INSTALL PLAN:");
870
- console.log(` Skill: ${context.skillTarget}`);
894
+ console.log(
895
+ ` Skill: ${context.skillTarget} (${context.client.displayName})`,
896
+ );
871
897
  console.log(` Device Kit: ${context.thingConnectRoot} (${ESP32_KIT.version})`);
872
898
  console.log(` ESP-IDF: ${context.idfDir} (${PINNED_IDF_VERSION})`);
873
899
  console.log(` IDF tools: ${context.idfToolsPath || "existing default"}`);
@@ -917,6 +943,6 @@ export function runEsp32Setup(args, input) {
917
943
  }
918
944
  console.log("SETUP: READY");
919
945
  console.log(`Environment helper: ${join(context.rootDir, "env.sh")}`);
920
- console.log("Start a new Codex session and invoke $tirtc-esp32-builder.");
946
+ console.log(clientSessionHint(context.client, runtime.platform.skill));
921
947
  return 0;
922
948
  }
@@ -9,9 +9,15 @@ import {
9
9
  renameSync,
10
10
  rmSync,
11
11
  } from "node:fs";
12
- import { homedir } from "node:os";
13
12
  import { dirname, join, resolve } from "node:path";
14
13
  import { fileURLToPath } from "node:url";
14
+ import {
15
+ clientSessionHint,
16
+ DEFAULT_AGENT_CLIENT,
17
+ defaultSkillsDir,
18
+ listAgentClients,
19
+ requireAgentClient,
20
+ } from "./agent-clients.js";
15
21
  import { runEsp32Setup } from "./setup-esp32.js";
16
22
 
17
23
  const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
@@ -35,7 +41,8 @@ function printHelp() {
35
41
 
36
42
  Usage:
37
43
  tirtc-device-builder list
38
- tirtc-device-builder install <platform> [--skills-dir <path>] [--force]
44
+ tirtc-device-builder clients
45
+ tirtc-device-builder install <platform> [--client <name>] [--skills-dir <path>] [--force]
39
46
  tirtc-device-builder setup <platform> [setup options]
40
47
  tirtc-device-builder doctor <platform> [doctor options]
41
48
  tirtc-device-builder boards <platform> <list|validate|match|init-identity|candidate> [options]
@@ -46,15 +53,18 @@ Platforms:
46
53
 
47
54
  Examples:
48
55
  npx tirtc-device-builder install esp32
56
+ npx tirtc-device-builder install esp32 --client qwen-code
49
57
  npx tirtc-device-builder setup esp32
50
- npx tirtc-device-builder setup esp32 --install
58
+ npx tirtc-device-builder setup esp32 --install --client gemini
51
59
  npx tirtc-device-builder install esp32 --skills-dir /absolute/path/skills
52
60
  npx tirtc-device-builder doctor esp32 --project /absolute/path/project
53
61
  npx tirtc-device-builder boards esp32 match --identity ./board-identity.json
54
62
 
55
- Install defaults to ${"$"}{CODEX_HOME:-~/.codex}/skills. Existing skills are
56
- preserved unless --force is explicitly supplied. Setup checks are read-only;
57
- setup --install installs missing user-space components without running sudo.`);
63
+ Install defaults to the selected client's user Skill directory; Codex is the
64
+ default client. Run "clients" to list supported names and resolved directories.
65
+ Existing skills are preserved unless --force is explicitly supplied. Setup
66
+ checks are read-only; setup --install installs missing user-space components
67
+ without running sudo.`);
58
68
  }
59
69
 
60
70
  function fail(message) {
@@ -71,17 +81,11 @@ function resolvePlatform(identifier) {
71
81
  return null;
72
82
  }
73
83
 
74
- function defaultSkillsDir() {
75
- const codexHome = process.env.CODEX_HOME
76
- ? resolve(process.env.CODEX_HOME)
77
- : join(homedir(), ".codex");
78
- return join(codexHome, "skills");
79
- }
80
-
81
84
  function parseInstallOptions(args) {
82
85
  const options = {
86
+ client: requireAgentClient(DEFAULT_AGENT_CLIENT),
83
87
  force: false,
84
- skillsDir: defaultSkillsDir(),
88
+ skillsDir: null,
85
89
  };
86
90
 
87
91
  for (let index = 0; index < args.length; index += 1) {
@@ -90,6 +94,15 @@ function parseInstallOptions(args) {
90
94
  options.force = true;
91
95
  continue;
92
96
  }
97
+ if (argument === "--client") {
98
+ const value = args[index + 1];
99
+ if (!value || value.startsWith("--")) {
100
+ throw new Error("--client requires a name");
101
+ }
102
+ options.client = requireAgentClient(value);
103
+ index += 1;
104
+ continue;
105
+ }
93
106
  if (argument === "--skills-dir") {
94
107
  const value = args[index + 1];
95
108
  if (!value || value.startsWith("--")) {
@@ -102,6 +115,7 @@ function parseInstallOptions(args) {
102
115
  throw new Error(`unknown install option: ${argument}`);
103
116
  }
104
117
 
118
+ options.skillsDir ??= defaultSkillsDir(options.client);
105
119
  return options;
106
120
  }
107
121
 
@@ -151,8 +165,10 @@ function installSkill(platform, options) {
151
165
  if (movedExisting && existsSync(backup)) {
152
166
  rmSync(backup, { force: true, recursive: true });
153
167
  }
154
- console.log(`Installed ${platform.skill} ${PACKAGE.version} to ${target}`);
155
- console.log(`Start a new Codex session, then invoke $${platform.skill}.`);
168
+ console.log(
169
+ `Installed ${platform.skill} ${PACKAGE.version} for ${options.client.displayName} to ${target}`,
170
+ );
171
+ console.log(clientSessionHint(options.client, platform.skill));
156
172
  }
157
173
 
158
174
  function runDoctor(platform, args) {
@@ -214,6 +230,15 @@ function main(args) {
214
230
  }
215
231
  return 0;
216
232
  }
233
+ if (args[0] === "clients") {
234
+ for (const client of listAgentClients()) {
235
+ const marker = client.id === DEFAULT_AGENT_CLIENT ? " (default)" : "";
236
+ console.log(
237
+ `${client.id}\t${client.displayName}${marker}\t${defaultSkillsDir(client)}`,
238
+ );
239
+ }
240
+ return 0;
241
+ }
217
242
 
218
243
  const [command, identifier, ...rest] = args;
219
244
  if (
@@ -244,7 +269,7 @@ function main(args) {
244
269
  }
245
270
  return runEsp32Setup(rest, {
246
271
  cliPath: fileURLToPath(import.meta.url),
247
- defaultSkillsDir: defaultSkillsDir(),
272
+ defaultClient: requireAgentClient(DEFAULT_AGENT_CLIENT),
248
273
  packageRoot: PACKAGE_ROOT,
249
274
  packageVersion: PACKAGE.version,
250
275
  platform,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tirtc-device-builder",
3
- "version": "0.8.1",
4
- "description": "Install and run TiRTC device-development skills for Codex.",
3
+ "version": "0.9.1",
4
+ "description": "Install and run TiRTC device-development Agent Skills across popular coding clients.",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "TangeAI",
@@ -30,6 +30,10 @@
30
30
  },
31
31
  "keywords": [
32
32
  "codex",
33
+ "agent-skills",
34
+ "claude-code",
35
+ "gemini-cli",
36
+ "qwen-code",
33
37
  "skill",
34
38
  "tirtc",
35
39
  "esp32",
@@ -70,6 +70,10 @@ implementation and build facts. Stop before code only for a genuine user choice,
70
70
  unknown wiring/identity, unavailable matching SDK, or required public-contract
71
71
  change.
72
72
 
73
+ When SoftAP is the selected Wi-Fi method, carry the fixed SSID, open-auth and
74
+ IPv4 [product contract](references/hardware-ir.md#softap-product-contract) into
75
+ the IR, generated adapter, user-facing instructions and build checks.
76
+
73
77
  ## 5. Generate and port
74
78
 
75
79
  Generate a new project without overwriting an existing path. Keep platform
@@ -5,14 +5,15 @@
5
5
  ```bash
6
6
  npx tirtc-device-builder@latest setup esp32
7
7
  npx tirtc-device-builder@latest setup esp32 --install
8
+ npx tirtc-device-builder@latest clients
8
9
  ```
9
10
 
10
- 第一条命令只检查;第二条命令自动安装用户目录内缺失的 Skill、带校验的 ESP32 Device Kit、ESP-IDF 5.5.4 和 ESP32-S3 工具链,最后复跑 Doctor。它不执行 `sudo`,也不修改 shell 配置。安装完成后新开 Codex 会话即可调用 Skill。
11
+ 第一条命令只检查;第二条命令自动安装用户目录内缺失的 Skill、带校验的 ESP32 Device Kit、ESP-IDF 5.5.4 和 ESP32-S3 工具链,最后复跑 Doctor。它不执行 `sudo`,也不修改 shell 配置。默认客户端是 Codex;Gemini CLI、GitHub Copilot、Qwen Code、Windsurf、Cline、Kiro、Claude Code 和 OpenCode 可通过 `--client <name>` 选择,第三条命令会列出名称和实际安装目录。安装完成后重新启动所选 Agent 客户端。
11
12
 
12
- 安装后可在 Codex 中显式调用:
13
+ 安装后可直接要求 Agent 使用该 Skill:
13
14
 
14
15
  ```text
15
- $tirtc-esp32-builder
16
+ 请使用 tirtc-esp32-builder Skill。
16
17
 
17
18
  开发板:<厂商、完整型号、PCB 版本>
18
19
  资料与实物对应:<是/否/未知>
@@ -48,24 +49,24 @@ printf '%s\n' "$TIRTC_THING_CONNECT_ROOT"
48
49
  只有板卡型号:
49
50
 
50
51
  ```text
51
- $tirtc-esp32-builder 分析 <厂商> <型号> <硬件版本>,目标是 H5 实时视频、talkback 和 AI 对讲。先输出缺失资料与能力结论。
52
+ 请使用 tirtc-esp32-builder Skill 分析 <厂商> <型号> <硬件版本>,目标是 H5 实时视频、talkback 和 AI 对讲。先输出缺失资料与能力结论。
52
53
  ```
53
54
 
54
55
  提供本地资料:
55
56
 
56
57
  ```text
57
- $tirtc-esp32-builder 使用原理图 /path/board.pdf、BSP /path/vendor-project 和一键安装的 Device Kit,为该板生成 TiRTC H5/AI/CALL/VOIP ESP-IDF 工程并编译;AI/CALL/VOIP 必须通过 AEC 门禁。
58
+ 请使用 tirtc-esp32-builder Skill、原理图 /path/board.pdf、BSP /path/vendor-project 和一键安装的 Device Kit,为该板生成 TiRTC H5/AI/CALL/VOIP ESP-IDF 工程并编译;AI/CALL/VOIP 必须通过 AEC 门禁。
58
59
  ```
59
60
 
60
61
  完整实机流程:
61
62
 
62
63
  ```text
63
- $tirtc-esp32-builder 使用 /path/hardware-ir.json 生成工程,编译后烧录到 /dev/ttyACM0,验证绑定、H5 和 AI,并生成 TIRTC_PORTING_REPORT.md。
64
+ 请使用 tirtc-esp32-builder Skill /path/hardware-ir.json 生成工程,编译后烧录到 /dev/ttyACM0,验证绑定、H5 和 AI,并生成 TIRTC_PORTING_REPORT.md。
64
65
  ```
65
66
 
66
67
  ## ESP-IDF 环境检查
67
68
 
68
- 将 `<skill-dir>` 替换为安装后的 Skill 路径,例如 `~/.codex/skills/tirtc-esp32-builder`。
69
+ 将 `<skill-dir>` 替换为 `clients` 命令显示的 Skill 根目录下的 `tirtc-esp32-builder`,例如 `~/.qwen/skills/tirtc-esp32-builder`。
69
70
 
70
71
  生成工程前检查工作区和开发环境:
71
72
 
@@ -73,7 +74,7 @@ $tirtc-esp32-builder 使用 /path/hardware-ir.json 生成工程,编译后烧
73
74
  python3 <skill-dir>/scripts/doctor.py \
74
75
  --expected-idf 5.5 \
75
76
  --target esp32s3 \
76
- --thing-connect-root ~/.tirtc-device-builder/kits/esp32s3/1.1.1 \
77
+ --thing-connect-root ~/.tirtc-device-builder/kits/esp32s3/1.1.2 \
77
78
  --require-workspace
78
79
  ```
79
80
 
@@ -1 +1 @@
1
- 0.8.1
1
+ 0.9.1
@@ -5,7 +5,7 @@
5
5
  ## 精简模板
6
6
 
7
7
  ```text
8
- $tirtc-esp32-builder
8
+ 请使用 tirtc-esp32-builder Skill。
9
9
 
10
10
  请为下面这块开发板完成 ThingConnect TiRTC ESP32 接入。
11
11
 
@@ -27,6 +27,7 @@ $tirtc-esp32-builder
27
27
 
28
28
  接入方式:
29
29
  - Wi-Fi:<选择一种,或写“根据 BSP 选择”>
30
+ - 选择 SoftAP 时:热点名以 `TiRTC-` 开头、开放认证无需密码,AP 网关和配网页面为 `192.168.6.1` / `http://192.168.6.1`
30
31
  - 设备绑定:<选择一种,或写“根据平台合同选择”>
31
32
 
32
33
  工程:
@@ -1,28 +1,30 @@
1
- $tirtc-esp32-builder
1
+ 请使用 tirtc-esp32-builder Skill。
2
2
 
3
3
  请在当前工作区完成一次立创·实战派 ESP32-S3 的 ThingConnect TiRTC clean-room L-1/L0/L1 接入验证。
4
4
 
5
5
  这是 Skill、提示词和开发板资料的独立有效性测试。允许访问外网。
6
6
 
7
- 前置条件(必须由开发者在启动本次 Codex 会话前完成,不属于本提示词内的操作):
7
+ 前置条件(必须由开发者在启动本次 Agent 会话前完成,不属于本提示词内的操作):
8
8
 
9
9
  ```bash
10
- npx --yes tirtc-device-builder@0.8.1 setup esp32 --install --force-skill
10
+ export TIRTC_AGENT_CLIENT=codex
11
+ npx --yes tirtc-device-builder@0.9.1 setup esp32 --install \
12
+ --client "$TIRTC_AGENT_CLIENT" --force-skill
11
13
  ```
12
14
 
13
- 该命令只允许在当前用户目录安装固定版本的 Skill、managed ESP32 Device Kit、ESP-IDF 和工具链;禁止 sudo、系统级包变更和修改 shell profile。安装完成后,开发者必须关闭原 Codex 会话,再从本 clean-room 工作区启动一个新会话,然后粘贴本提示词。
15
+ `TIRTC_AGENT_CLIENT` 改为 `clients` 命令列出的目标客户端名称。该命令只允许在当前用户目录安装固定版本的 Skill、managed ESP32 Device Kit、ESP-IDF 和工具链;禁止 sudo、系统级包变更和修改 shell profile。安装完成后,开发者必须关闭原 Agent 会话,再从本 clean-room 工作区启动一个新会话,然后粘贴本提示词。
14
16
 
15
17
  本轮第一步先只读运行:
16
18
 
17
19
  ```bash
18
- npx --yes tirtc-device-builder@0.8.1 --version
19
- npx --yes tirtc-device-builder@0.8.1 setup esp32
20
+ npx --yes tirtc-device-builder@0.9.1 --version
21
+ npx --yes tirtc-device-builder@0.9.1 setup esp32 --client "$TIRTC_AGENT_CLIENT"
20
22
  ```
21
23
 
22
- 必须根据命令的实际输出和本机文件确认:npm 包为 0.8.1、已安装 Skill 的 `VERSION` 为 0.8.1、所选 Device Kit 的 `manifest.json` 中 `kit_version` 为 1.1.1,并且 Doctor 对 `--expected-kit 1.1.1` 输出 `OVERALL: PASS`。Plugin manifest 不属于这种 npm 安装方式的运行时前置条件,不得把不可访问的 Plugin 版本当作阻塞项。如果版本不一致、Skill 是在当前会话启动后才安装,或环境检查未通过,停止并报告前置条件不成立;不要在当前会话中替换 Skill 后继续生成工程。
24
+ 必须根据命令的实际输出和本机文件确认:npm 包为 0.9.1、已安装 Skill 的 `VERSION` 为 0.9.1、所选 Device Kit 的 `manifest.json` 中 `kit_version` 为 1.1.2,并且 Doctor 对 `--expected-kit 1.1.2` 输出 `OVERALL: PASS`。Plugin manifest 不属于这种 npm 安装方式的运行时前置条件,不得把不可访问的 Plugin 版本当作阻塞项。如果版本不一致、Skill 是在当前会话启动后才安装,或环境检查未通过,停止并报告前置条件不成立;不要在当前会话中替换 Skill 后继续生成工程。
23
25
 
24
26
  工作区与 clean-room 边界:
25
- - 将启动 Codex 时的当前目录定义为 `WORKSPACE_ROOT`。
27
+ - 将启动 Agent 客户端时的当前目录定义为 `WORKSPACE_ROOT`。
26
28
  - 所有工程输入和输出必须位于 `WORKSPACE_ROOT`。
27
29
  - 不得读取 `WORKSPACE_ROOT` 之外的项目、兄弟目录、父目录项目、备份、旧构建、日志、报告或 artifact。
28
30
  - 如果目标输出目录已经存在,停止并报告 clean-room 条件不成立;不要读取、覆盖或删除该目录。
@@ -58,7 +60,7 @@ npx --yes tirtc-device-builder@0.8.1 setup esp32
58
60
  - AI 对讲必须实现全双工和 AEC;构建阶段必须证明同时采集播放、真实播放参考与 `echo_cancellation.enabled=true`,实机结果仍只能在 L2-L7 验证后声明。
59
61
 
60
62
  接入要求:
61
- - SoftAP 配网,凭证保存 NVS。
63
+ - SoftAP 配网:热点名使用 `TiRTC-` 前缀、开放认证无需密码,AP 网关和配网页面为 `192.168.6.1` / `http://192.168.6.1`;目标 Wi-Fi 凭证保存 NVS。
62
64
  - 验证码绑定,并复用已有绑定。
63
65
  - 分别实现 `wifi-clear` 和 `tirtc-clear`。
64
66
  - 平台发现本轮先使用 HTTP;HTTPS 留待独立验收。
@@ -47,7 +47,7 @@ The intake phase requires:
47
47
 
48
48
  At intake, `corroborated` on these fields means the design is resolved from authoritative sources and is safe to implement. After compilation, promote a field to `build_verified` only when the generated source, component lock, semantic gate, compile result, or post-link gate establishes it. Build assessment reruns the applicable project-relative [audio contract](audio-contract.md), [video contract](video-contract.md), and mandatory [runtime contract](runtime-contract.md); self-declared `resolved=true`, `pipeline_safe=true`, or memory-budget booleans cannot replace them. The build phase requires an exact on-disk artifact SHA-256 and returns `BUILD_VERIFIED` only when every requested feature passes. Runtime measurements never need to be invented to pass intake or build.
49
49
 
50
- SoftAP is one Wi-Fi option, not a universal requirement. BLE, SmartConfig, factory NVS, development configuration, or a documented custom method can satisfy intake when the selected path is evidenced. Committed plaintext credentials are always `BLOCKED`.
50
+ SoftAP is one Wi-Fi option, not a universal requirement. BLE, SmartConfig, factory NVS, development configuration, or a documented custom method can satisfy intake when the selected path is evidenced. A selected SoftAP must satisfy the product contract in [Hardware IR](hardware-ir.md#softap-product-contract). Committed plaintext credentials are always `BLOCKED`.
51
51
 
52
52
  ## Non-negotiable runtime checks
53
53
 
@@ -16,7 +16,7 @@ The automatic branch never runs `sudo` or modifies a persistent shell profile. W
16
16
  When `<setup-root>/env.sh` exists, use it only as an activation prefix for the current command:
17
17
 
18
18
  ```bash
19
- bash -lc '. "<setup-root>/env.sh" && python3 "<skill-dir>/scripts/doctor.py" --expected-idf 5.5 --expected-kit 1.1.1 --target esp32s3 --require-workspace'
19
+ bash -lc '. "<setup-root>/env.sh" && python3 "<skill-dir>/scripts/doctor.py" --expected-idf 5.5 --expected-kit 1.1.2 --target esp32s3 --require-workspace'
20
20
  ```
21
21
 
22
22
  The helper contains paths, not device or network credentials. Read `<setup-root>/config.json` when exact managed paths are needed; the environment helper does not authorize unrelated downloads, shell-profile changes, flashing, or credential writes.
@@ -26,7 +26,7 @@ Run the doctor before generation, build, flash, or monitor:
26
26
  ```bash
27
27
  python3 <skill-dir>/scripts/doctor.py \
28
28
  --expected-idf 5.5 \
29
- --expected-kit 1.1.1 \
29
+ --expected-kit 1.1.2 \
30
30
  --target esp32s3 \
31
31
  --require-workspace
32
32
  ```
@@ -37,7 +37,7 @@ Add `--project <generated-project>` after generation so the doctor can compare `
37
37
 
38
38
  The automatic setup downloads a versioned minimal Kit instead of cloning the ThingConnect server repository. A managed Kit is ready only when its generator and SDK files exist and `manifest.json` declares the exact pinned `kit_version`. A stale environment or managed configuration that points at an older Kit is ignored in favor of the current versioned managed path. The setup configuration records the version read from that manifest; it never substitutes the desired version for the actual one.
39
39
 
40
- The installed Skill has its own `<skill-dir>/VERSION` marker. Setup requires it to equal the npm package version and reports both values. Replacing a missing or mismatched marker requires the explicit `--install --force-skill` flow and a new Codex session.
40
+ The installed Skill has its own `<skill-dir>/VERSION` marker. Setup requires it to equal the npm package version and reports both values. Replacing a missing or mismatched marker requires the explicit `--install --force-skill` flow and a new session in the selected Agent client.
41
41
 
42
42
  For an explicitly selected legacy workspace, omit `--expected-kit`; otherwise resolve the generation root in this order:
43
43
 
@@ -61,6 +61,26 @@ assessment also requires the project audio semantic gate to prove enabled AEC.
61
61
 
62
62
  The selected Wi-Fi method must be available and corroborated, credentials must remain outside tracked source, and reprovisioning must be defined. A board without SoftAP is valid when another selected method meets those conditions.
63
63
 
64
+ ### SoftAP product contract
65
+
66
+ When the selected method is SoftAP:
67
+
68
+ - generate the SSID as `TiRTC-<device suffix>` so its exact, case-sensitive
69
+ prefix is `TiRTC-`; use a stable non-secret suffix and keep the complete SSID
70
+ within the ESP32 32-byte limit;
71
+ - configure an open network (`WIFI_AUTH_OPEN`) with an empty AP password so a
72
+ user can connect directly;
73
+ - assign the AP netif, gateway and provisioning server `192.168.6.1/24`, serve
74
+ the setup page at `http://192.168.6.1`, and lease clients from that subnet.
75
+ With ESP-IDF, stop DHCP before changing the AP netif IP information and
76
+ restart it afterward.
77
+
78
+ Record these values on the selected SoftAP method as `ssid_prefix: "TiRTC-"`,
79
+ `auth_mode: "open"`, and `ipv4_address: "192.168.6.1"`. The Hardware IR gate
80
+ returns `NEEDS_CONFIRMATION` when one is missing and `BLOCKED` when one differs.
81
+ Keep the provisioning endpoints available only while onboarding is active, and
82
+ disable the AP or restart into STA mode after credentials are accepted.
83
+
64
84
  The selected video profile controls assessment. An unselected H.264 fallback cannot make an MJPEG target pass, and missing H.264 cannot block an evidenced MJPEG target.
65
85
 
66
86
  ## Artifact-bound HIL
@@ -30,7 +30,7 @@ Turn every discovered invariant that can regress into a focused test or post-lin
30
30
 
31
31
  Wi-Fi provisioning and ThingConnect binding are separate state machines.
32
32
 
33
- Select one evidenced Wi-Fi credential method from SoftAP, BLE, SmartConfig, secure factory/NVS provisioning, development configuration, or a documented custom path. SoftAP is not mandatory. A board without AP provisioning can reach `READY_TO_PORT` through another available method when credentials remain outside source control and a reprovisioning path is defined.
33
+ Select one evidenced Wi-Fi credential method from SoftAP, BLE, SmartConfig, secure factory/NVS provisioning, development configuration, or a documented custom path. SoftAP is not mandatory. A board without AP provisioning can reach `READY_TO_PORT` through another available method when credentials remain outside source control and a reprovisioning path is defined. When SoftAP is selected, implement and record the fixed [SoftAP product contract](hardware-ir.md#softap-product-contract).
34
34
 
35
35
  Development configuration may inject credentials through an untracked sdkconfig, environment, or provisioning artifact. Treat committed plaintext credentials as `BLOCKED`. Never copy SSIDs/passwords into Hardware IR, reports, examples, or source.
36
36
 
@@ -61,6 +61,9 @@ WIFI_METHOD_TYPES = {
61
61
  "development_config",
62
62
  "custom",
63
63
  }
64
+ SOFTAP_SSID_PREFIX = "TiRTC-"
65
+ SOFTAP_AUTH_MODE = "open"
66
+ SOFTAP_IPV4_ADDRESS = "192.168.6.1"
64
67
  BINDING_METHOD_TYPES = {
65
68
  "verification_code",
66
69
  "factory_bound",
@@ -444,6 +447,9 @@ def validate_onboarding(
444
447
  errors.append(
445
448
  f"{prefix}.type must be one of " + ", ".join(sorted(WIFI_METHOD_TYPES))
446
449
  )
450
+ if method_type == "softap":
451
+ for field in ("ssid_prefix", "auth_mode", "ipv4_address"):
452
+ nullable_string(method.get(field), f"{prefix}.{field}", errors)
447
453
  nullable_bool(method.get("available"), f"{prefix}.available", errors)
448
454
  validate_verification(
449
455
  method.get("verification"), f"{prefix}.verification", errors
@@ -926,6 +932,26 @@ def wifi_requirement(onboarding: dict[str, Any]) -> Requirement:
926
932
  return "NEEDS_CONFIRMATION", "selected Wi-Fi method availability is unknown", 0
927
933
  if available is False:
928
934
  return "BLOCKED", "selected Wi-Fi method is unavailable", 0
935
+ if method.get("type") == "softap":
936
+ softap_fields = (
937
+ ("ssid_prefix", SOFTAP_SSID_PREFIX, "SSID prefix"),
938
+ ("auth_mode", SOFTAP_AUTH_MODE, "authentication mode"),
939
+ ("ipv4_address", SOFTAP_IPV4_ADDRESS, "IPv4 address"),
940
+ )
941
+ for field, expected, label in softap_fields:
942
+ value = method.get(field)
943
+ if value is None:
944
+ return (
945
+ "NEEDS_CONFIRMATION",
946
+ f"SoftAP {label} is unknown; expected {expected!r}",
947
+ 0,
948
+ )
949
+ if value != expected:
950
+ return (
951
+ "BLOCKED",
952
+ f"SoftAP {label} must be {expected!r}, got {value!r}",
953
+ 0,
954
+ )
929
955
  verification = method.get("verification")
930
956
  level = VERIFICATION_LEVELS.get(verification, 0)
931
957
  if level < VERIFICATION_LEVELS["corroborated"]: