tirtc-device-builder 0.2.0
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.
- package/.codex-plugin/plugin.json +36 -0
- package/CHANGELOG.md +19 -0
- package/LICENSE +21 -0
- package/NOTICE +7 -0
- package/README.md +604 -0
- package/SECURITY.md +15 -0
- package/bin/tirtc-device-builder.js +214 -0
- package/package.json +56 -0
- package/skills/tirtc-esp32-builder/SKILL.md +34 -0
- package/skills/tirtc-esp32-builder/USAGE.md +94 -0
- package/skills/tirtc-esp32-builder/agents/openai.yaml +4 -0
- package/skills/tirtc-esp32-builder/assets/hardware-ir.example.json +78 -0
- package/skills/tirtc-esp32-builder/assets/report-template.md +55 -0
- package/skills/tirtc-esp32-builder/references/capability-rules.md +32 -0
- package/skills/tirtc-esp32-builder/references/environment.md +54 -0
- package/skills/tirtc-esp32-builder/references/hardware-ir.md +49 -0
- package/skills/tirtc-esp32-builder/references/reporting.md +23 -0
- package/skills/tirtc-esp32-builder/references/workflow.md +70 -0
- package/skills/tirtc-esp32-builder/scripts/doctor.py +441 -0
- package/skills/tirtc-esp32-builder/scripts/hardware_ir.py +425 -0
package/SECURITY.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Report a vulnerability
|
|
4
|
+
|
|
5
|
+
Use a private GitHub security advisory:
|
|
6
|
+
|
|
7
|
+
https://github.com/tangeai/tirtc-device-builder/security/advisories/new
|
|
8
|
+
|
|
9
|
+
Do not include device keys, Wi-Fi passwords, service tokens, certificates, private user data, or captured media in a public Issue. Redact logs and provide the smallest reproduction that still demonstrates the problem.
|
|
10
|
+
|
|
11
|
+
## Scope
|
|
12
|
+
|
|
13
|
+
Reports may cover unsafe command execution, path handling, credential disclosure, unintended network access, serial-device selection, firmware flashing authorization, package installation behavior, and generated-project security boundaries.
|
|
14
|
+
|
|
15
|
+
Vulnerabilities in ESP-IDF, TiRTC SDK, ThingConnect, vendor BSPs, chip toolchains, or board firmware should also be reported to the corresponding upstream maintainer.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import {
|
|
5
|
+
cpSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
renameSync,
|
|
10
|
+
rmSync,
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { dirname, join, resolve } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
17
|
+
const PACKAGE = JSON.parse(
|
|
18
|
+
readFileSync(join(PACKAGE_ROOT, "package.json"), "utf8"),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const PLATFORMS = new Map([
|
|
22
|
+
[
|
|
23
|
+
"esp32",
|
|
24
|
+
{
|
|
25
|
+
aliases: new Set(["esp32", "esp32s3", "tirtc-esp32-builder"]),
|
|
26
|
+
skill: "tirtc-esp32-builder",
|
|
27
|
+
summary: "ESP32-S3 / ESP-IDF 5.5.x",
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
function printHelp() {
|
|
33
|
+
console.log(`TiRTC Device Builder ${PACKAGE.version}
|
|
34
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
tirtc-device-builder list
|
|
37
|
+
tirtc-device-builder install <platform> [--skills-dir <path>] [--force]
|
|
38
|
+
tirtc-device-builder doctor <platform> [doctor options]
|
|
39
|
+
tirtc-device-builder --version
|
|
40
|
+
|
|
41
|
+
Platforms:
|
|
42
|
+
esp32 ESP32-S3 / ESP-IDF 5.5.x
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
npx tirtc-device-builder install esp32
|
|
46
|
+
npx tirtc-device-builder install esp32 --skills-dir /absolute/path/skills
|
|
47
|
+
npx tirtc-device-builder doctor esp32 --project /absolute/path/project
|
|
48
|
+
|
|
49
|
+
Install defaults to ${"$"}{CODEX_HOME:-~/.codex}/skills. Existing skills are
|
|
50
|
+
preserved unless --force is explicitly supplied.`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function fail(message) {
|
|
54
|
+
console.error(`ERROR: ${message}`);
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function resolvePlatform(identifier) {
|
|
59
|
+
for (const [name, platform] of PLATFORMS) {
|
|
60
|
+
if (platform.aliases.has(identifier)) {
|
|
61
|
+
return { name, ...platform };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function defaultSkillsDir() {
|
|
68
|
+
const codexHome = process.env.CODEX_HOME
|
|
69
|
+
? resolve(process.env.CODEX_HOME)
|
|
70
|
+
: join(homedir(), ".codex");
|
|
71
|
+
return join(codexHome, "skills");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function parseInstallOptions(args) {
|
|
75
|
+
const options = {
|
|
76
|
+
force: false,
|
|
77
|
+
skillsDir: defaultSkillsDir(),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
81
|
+
const argument = args[index];
|
|
82
|
+
if (argument === "--force") {
|
|
83
|
+
options.force = true;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (argument === "--skills-dir") {
|
|
87
|
+
const value = args[index + 1];
|
|
88
|
+
if (!value || value.startsWith("--")) {
|
|
89
|
+
throw new Error("--skills-dir requires a path");
|
|
90
|
+
}
|
|
91
|
+
options.skillsDir = resolve(value);
|
|
92
|
+
index += 1;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
throw new Error(`unknown install option: ${argument}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return options;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function installSkill(platform, options) {
|
|
102
|
+
const source = join(PACKAGE_ROOT, "skills", platform.skill);
|
|
103
|
+
const skillsDir = options.skillsDir;
|
|
104
|
+
const target = join(skillsDir, platform.skill);
|
|
105
|
+
const nonce = `${process.pid}-${Date.now()}`;
|
|
106
|
+
const staged = join(skillsDir, `.${platform.skill}.install-${nonce}`);
|
|
107
|
+
const backup = join(skillsDir, `.${platform.skill}.backup-${nonce}`);
|
|
108
|
+
|
|
109
|
+
if (!existsSync(join(source, "SKILL.md"))) {
|
|
110
|
+
throw new Error(`package is missing ${platform.skill}/SKILL.md`);
|
|
111
|
+
}
|
|
112
|
+
if (existsSync(target) && !options.force) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`${target} already exists; rerun with --force only when replacement is intended`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
119
|
+
let movedExisting = false;
|
|
120
|
+
try {
|
|
121
|
+
cpSync(source, staged, {
|
|
122
|
+
errorOnExist: true,
|
|
123
|
+
force: false,
|
|
124
|
+
recursive: true,
|
|
125
|
+
});
|
|
126
|
+
if (!existsSync(join(staged, "SKILL.md"))) {
|
|
127
|
+
throw new Error("staged skill failed validation");
|
|
128
|
+
}
|
|
129
|
+
if (existsSync(target)) {
|
|
130
|
+
renameSync(target, backup);
|
|
131
|
+
movedExisting = true;
|
|
132
|
+
}
|
|
133
|
+
renameSync(staged, target);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (existsSync(staged)) {
|
|
136
|
+
rmSync(staged, { force: true, recursive: true });
|
|
137
|
+
}
|
|
138
|
+
if (movedExisting && !existsSync(target) && existsSync(backup)) {
|
|
139
|
+
renameSync(backup, target);
|
|
140
|
+
}
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (movedExisting && existsSync(backup)) {
|
|
145
|
+
rmSync(backup, { force: true, recursive: true });
|
|
146
|
+
}
|
|
147
|
+
console.log(`Installed ${platform.skill} ${PACKAGE.version} to ${target}`);
|
|
148
|
+
console.log(`Start a new Codex session, then invoke $${platform.skill}.`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function runDoctor(platform, args) {
|
|
152
|
+
const script = join(
|
|
153
|
+
PACKAGE_ROOT,
|
|
154
|
+
"skills",
|
|
155
|
+
platform.skill,
|
|
156
|
+
"scripts",
|
|
157
|
+
"doctor.py",
|
|
158
|
+
);
|
|
159
|
+
if (!existsSync(script)) {
|
|
160
|
+
return fail(`package is missing doctor script for ${platform.name}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const python = process.env.PYTHON || "python3";
|
|
164
|
+
const result = spawnSync(python, [script, ...args], {
|
|
165
|
+
stdio: "inherit",
|
|
166
|
+
});
|
|
167
|
+
if (result.error) {
|
|
168
|
+
return fail(`could not run ${python}: ${result.error.message}`);
|
|
169
|
+
}
|
|
170
|
+
return Number.isInteger(result.status) ? result.status : 1;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function main(args) {
|
|
174
|
+
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
175
|
+
printHelp();
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
if (args[0] === "--version" || args[0] === "-v") {
|
|
179
|
+
console.log(PACKAGE.version);
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
if (args[0] === "list") {
|
|
183
|
+
for (const [name, platform] of PLATFORMS) {
|
|
184
|
+
console.log(`${name}\t${platform.skill}\t${platform.summary}`);
|
|
185
|
+
}
|
|
186
|
+
return 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const [command, identifier, ...rest] = args;
|
|
190
|
+
if (command !== "install" && command !== "doctor") {
|
|
191
|
+
return fail(`unknown command: ${command}; run with --help`);
|
|
192
|
+
}
|
|
193
|
+
if (!identifier) {
|
|
194
|
+
return fail(`${command} requires a platform; run "list" to see options`);
|
|
195
|
+
}
|
|
196
|
+
const platform = resolvePlatform(identifier);
|
|
197
|
+
if (!platform) {
|
|
198
|
+
return fail(`unsupported platform: ${identifier}; run "list" to see options`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (command === "doctor") {
|
|
202
|
+
return runDoctor(platform, rest);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const options = parseInstallOptions(rest);
|
|
207
|
+
installSkill(platform, options);
|
|
208
|
+
return 0;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
process.exitCode = main(process.argv.slice(2));
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tirtc-device-builder",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Install and run TiRTC device-development skills for Codex.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "TangeAI",
|
|
8
|
+
"url": "https://github.com/tangeai"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"bin": {
|
|
12
|
+
"tirtc-device-builder": "bin/tirtc-device-builder.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
".codex-plugin/",
|
|
16
|
+
"bin/",
|
|
17
|
+
"skills/",
|
|
18
|
+
"CHANGELOG.md",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"NOTICE",
|
|
21
|
+
"SECURITY.md"
|
|
22
|
+
],
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/tangeai/tirtc-device-builder.git"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/tangeai/tirtc-device-builder#readme",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/tangeai/tirtc-device-builder/issues"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"codex",
|
|
33
|
+
"skill",
|
|
34
|
+
"tirtc",
|
|
35
|
+
"esp32",
|
|
36
|
+
"esp-idf",
|
|
37
|
+
"embedded",
|
|
38
|
+
"ai-intercom"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public",
|
|
45
|
+
"registry": "https://registry.npmjs.org/"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"test": "npm run test:node && npm run test:python && npm run validate && npm run validate:tarball && npm run test:package",
|
|
49
|
+
"test:node": "node --test test/*.test.js",
|
|
50
|
+
"test:python": "python3 -m unittest discover -s skills/tirtc-esp32-builder/scripts -p 'test_*.py'",
|
|
51
|
+
"validate": "python3 scripts/validate_package.py",
|
|
52
|
+
"validate:tarball": "node scripts/validate-tarball.js",
|
|
53
|
+
"test:package": "node scripts/test-packed-package.js",
|
|
54
|
+
"prepack": "npm test"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tirtc-esp32-builder
|
|
3
|
+
description: Check or set up ESP-IDF, then generate, port, build, flash, and validate ThingConnect TiRTC ESP32 projects from a board model, schematic, BSP, pin map, or peripheral examples when H5 live view/talkback or AI intercom is requested. Use for environment diagnosis, supported-board generation, and new-board hardware intake; exclude Linux device-sim-c and server-only work.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TiRTC Embedded Builder
|
|
7
|
+
|
|
8
|
+
Turn board evidence into an evidence-backed ESP-IDF project. Treat the Hardware IR as the single handoff between probabilistic document extraction and deterministic capability, generation, build, and verification steps.
|
|
9
|
+
|
|
10
|
+
## Start
|
|
11
|
+
|
|
12
|
+
1. Locate the ThingConnect root containing `device-sim/` using the explicit input, `TIRTC_THING_CONNECT_ROOT`, or workspace discovery. Read its applicable `AGENTS.md` and preserve its public protocol contracts.
|
|
13
|
+
2. Read [environment.md](references/environment.md) and run `python3 <skill-dir>/scripts/doctor.py --expected-idf 5.5 --target esp32s3`. Add `--require-workspace` when generation or repository reference documents are needed; a self-contained generated project can instead resolve its bundled SDK through `--project`. Resolve every required failure before claiming build readiness. Installation, cloning, or shell-profile changes require an explicit destination and the applicable authorization.
|
|
14
|
+
3. Read [workflow.md](references/workflow.md). Select the registered-board, new-board intake, or existing-project branch. The branch is selected when every supplied artifact has been accounted for and the exact board revision is known or explicitly unresolved.
|
|
15
|
+
4. Read [hardware-ir.md](references/hardware-ir.md) when a Hardware IR must be created or updated. Record a source and verification level for every hardware fact that affects a requested feature.
|
|
16
|
+
5. Run `python3 <skill-dir>/scripts/hardware_ir.py validate <hardware-ir.json>` and then `assess --strict`. Generation may proceed for a requested feature only when it is `READY_TO_PORT` or `HIL_VERIFIED`; otherwise report the exact missing evidence and continue with safe discovery or scaffolding only.
|
|
17
|
+
|
|
18
|
+
## Build the project
|
|
19
|
+
|
|
20
|
+
Run `<thing-connect-root>/device-sim/scripts/create_esp32_project.py` for the current ESP32-S3 H5/AI starter. Keep ThingConnect onboarding, H5, AI, TiRTC lifecycle, callback, stream, and generation behavior in the existing deep modules. Put board-specific camera, microphone, encoder, codec, amplifier, GPIO, DMA, and task behavior behind the `starter_media` seam or a board media adapter owned by it.
|
|
21
|
+
|
|
22
|
+
Before changing media code, read [capability-rules.md](references/capability-rules.md) and the repository documents it routes to. A camera sensor alone does not establish H5 video support; the complete H.264 Annex-B and key-frame path must be evidenced. Choose half duplex for AI when the supplied hardware and BSP do not establish a usable full-duplex/AEC path.
|
|
23
|
+
|
|
24
|
+
Run focused tests before ESP-IDF build. Resolve the TiRTC SDK target and `manifest/build-contract.env` against the generated `sdkconfig`; a mismatched precompiled SDK is a blocked build, not a code-generation problem.
|
|
25
|
+
|
|
26
|
+
## Flash and verify
|
|
27
|
+
|
|
28
|
+
Flash only when the user requested hardware mutation and the exact serial port and chip have been resolved. When more than one candidate device exists, obtain the target choice before writing. Keep credentials outside generated files and redact device keys, Wi-Fi passwords, MQTT/WHIP tokens, and user media from logs and reports.
|
|
29
|
+
|
|
30
|
+
Read [reporting.md](references/reporting.md) before end-to-end verification. Report every acceptance level as `PASS`, `FAIL`, or `SKIP`, with commands and evidence. A build-only result is not H5 or AI completion; missing hardware, browser, account, service, or network evidence remains an explicit `SKIP` or blocker.
|
|
31
|
+
|
|
32
|
+
## Finish
|
|
33
|
+
|
|
34
|
+
Return the generated project path, Hardware IR, capability assessment, build artifacts, flash record when applicable, and `TIRTC_PORTING_REPORT.md`. The task is complete only when every requested feature is either verified at the requested level or named as a blocker with the smallest next action that can resolve it.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# TiRTC ESP32 Builder 使用说明
|
|
2
|
+
|
|
3
|
+
安装后可在 Codex 中显式调用:
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
$tirtc-esp32-builder
|
|
7
|
+
|
|
8
|
+
在“厂商 + 完整开发板型号 + PCB 版本”上实现:
|
|
9
|
+
- H5 实时视频和声音
|
|
10
|
+
- H5 按住说话
|
|
11
|
+
- AI 双向对讲
|
|
12
|
+
|
|
13
|
+
资料:
|
|
14
|
+
- 产品页或资料链接:...
|
|
15
|
+
- 原理图:/absolute/path/board-schematic.pdf
|
|
16
|
+
- BSP 或示例工程:/absolute/path/vendor-bsp
|
|
17
|
+
- ThingConnect:/absolute/path/tirtc-server-example/thing-connect
|
|
18
|
+
- 输出目录:/absolute/path/my-tirtc-device
|
|
19
|
+
|
|
20
|
+
先完成能力分析;具备条件后生成并编译。只有我明确指定串口时才烧录。
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## ThingConnect 工作区
|
|
24
|
+
|
|
25
|
+
这个 Skill 不复制 ThingConnect 源码和 TiRTC 静态库。首次使用可以准备公开仓库:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git clone https://github.com/tangeai/tirtc-server-example.git \
|
|
29
|
+
/absolute/path/tirtc-server-example
|
|
30
|
+
|
|
31
|
+
export TIRTC_THING_CONNECT_ROOT=\
|
|
32
|
+
/absolute/path/tirtc-server-example/thing-connect
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
也可以在调用 Skill 时直接给出 ThingConnect 绝对路径,不需要设置持久环境变量。
|
|
36
|
+
|
|
37
|
+
## 常见输入方式
|
|
38
|
+
|
|
39
|
+
只有板卡型号:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
$tirtc-esp32-builder 分析 <厂商> <型号> <硬件版本>,目标是 H5 实时视频、talkback 和 AI 对讲。先输出缺失资料与能力结论。
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
提供本地资料:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
$tirtc-esp32-builder 使用原理图 /path/board.pdf、BSP /path/vendor-project 和 ThingConnect /path/tirtc-server-example/thing-connect,为该板生成 TiRTC H5/AI ESP-IDF 工程并编译。
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
完整实机流程:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
$tirtc-esp32-builder 使用 /path/hardware-ir.json 生成工程,编译后烧录到 /dev/ttyACM0,验证绑定、H5 和 AI,并生成 TIRTC_PORTING_REPORT.md。
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## ESP-IDF 环境检查
|
|
58
|
+
|
|
59
|
+
将 `<skill-dir>` 替换为安装后的 Skill 路径,例如 `~/.codex/skills/tirtc-esp32-builder`。
|
|
60
|
+
|
|
61
|
+
生成工程前检查工作区和开发环境:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
python3 <skill-dir>/scripts/doctor.py \
|
|
65
|
+
--expected-idf 5.5 \
|
|
66
|
+
--target esp32s3 \
|
|
67
|
+
--thing-connect-root /absolute/path/tirtc-server-example/thing-connect \
|
|
68
|
+
--require-workspace
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
工程生成后检查其内置 SDK 和配置契约:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python3 <skill-dir>/scripts/doctor.py \
|
|
75
|
+
--expected-idf 5.5 \
|
|
76
|
+
--target esp32s3 \
|
|
77
|
+
--project /absolute/path/my-tirtc-device
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
如果 `idf.py` 缺失,Skill 默认只报告安装计划。只有明确授权安装版本、目录和环境修改后,才按照 Espressif 官方步骤安装并重新运行检查。
|
|
81
|
+
|
|
82
|
+
## Hardware IR 工具
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
python3 <skill-dir>/scripts/hardware_ir.py init /tmp/hardware-ir.json
|
|
86
|
+
python3 <skill-dir>/scripts/hardware_ir.py validate /tmp/hardware-ir.json
|
|
87
|
+
python3 <skill-dir>/scripts/hardware_ir.py assess --strict /tmp/hardware-ir.json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`BLOCKED` 表示资料已确认硬件不满足;`NEEDS_CONFIRMATION` 表示仍有未知项或只有单一来源;`READY_TO_PORT` 表示可以生成并实现板级适配;`HIL_VERIFIED` 表示端到端实机验收通过。
|
|
91
|
+
|
|
92
|
+
## 当前边界
|
|
93
|
+
|
|
94
|
+
ThingConnect 仓库提供 ESP32-S3 H5/AI 模板和生成器,但默认媒体适配器不包含特定开发板的摄像头、麦克风、H.264 编码和扬声器驱动。模板生成和编译成功只证明工程与协议骨架可用,不代表 Web 已经出图或 AI 音频已经通过实机验收。
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"board": {
|
|
4
|
+
"id": "example_esp32s3_media_board",
|
|
5
|
+
"vendor": "Example Vendor",
|
|
6
|
+
"model": "ESP32-S3 Media Board",
|
|
7
|
+
"hardware_revision": "unspecified"
|
|
8
|
+
},
|
|
9
|
+
"sources": [
|
|
10
|
+
{
|
|
11
|
+
"id": "board-materials",
|
|
12
|
+
"kind": "user-supplied-materials",
|
|
13
|
+
"location": "user-supplied://board-materials",
|
|
14
|
+
"revision": "unspecified"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"soc": {
|
|
18
|
+
"target": "esp32s3",
|
|
19
|
+
"module": "ESP32-S3-WROOM-1-N16R8",
|
|
20
|
+
"flash_mb": 16,
|
|
21
|
+
"psram_mb": 8,
|
|
22
|
+
"source_refs": [
|
|
23
|
+
"board-materials"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"toolchain": {
|
|
27
|
+
"framework": "esp-idf",
|
|
28
|
+
"framework_version": "5.5.x",
|
|
29
|
+
"verification": "extracted",
|
|
30
|
+
"tirtc": {
|
|
31
|
+
"platform": "espressif-esp32s3",
|
|
32
|
+
"version": "2.3.0",
|
|
33
|
+
"sdk_path": "device-sim/sdk/espressif-esp32s3/2.3.0",
|
|
34
|
+
"build_contract": "manifest/build-contract.env"
|
|
35
|
+
},
|
|
36
|
+
"source_refs": [
|
|
37
|
+
"board-materials"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"camera": {
|
|
41
|
+
"present": null,
|
|
42
|
+
"sensor": null,
|
|
43
|
+
"interface": null,
|
|
44
|
+
"h264": {
|
|
45
|
+
"available": null,
|
|
46
|
+
"output_format": null,
|
|
47
|
+
"key_frame_control": null,
|
|
48
|
+
"verification": "extracted"
|
|
49
|
+
},
|
|
50
|
+
"source_refs": [
|
|
51
|
+
"board-materials"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"audio_input": {
|
|
55
|
+
"present": null,
|
|
56
|
+
"interface": null,
|
|
57
|
+
"codecs": [],
|
|
58
|
+
"source_refs": [
|
|
59
|
+
"board-materials"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"audio_output": {
|
|
63
|
+
"present": null,
|
|
64
|
+
"interface": null,
|
|
65
|
+
"codecs": [],
|
|
66
|
+
"source_refs": [
|
|
67
|
+
"board-materials"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
"features": {
|
|
71
|
+
"requested": [
|
|
72
|
+
"h5_live_audio",
|
|
73
|
+
"h5_live_video",
|
|
74
|
+
"h5_talkback",
|
|
75
|
+
"ai_talk"
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# TiRTC Porting Report
|
|
2
|
+
|
|
3
|
+
## Target
|
|
4
|
+
|
|
5
|
+
- Board ID: `{{BOARD_ID}}`
|
|
6
|
+
- Model/revision: `{{BOARD_MODEL_REVISION}}`
|
|
7
|
+
- Requested features: `{{REQUESTED_FEATURES}}`
|
|
8
|
+
- Output project: `{{PROJECT_PATH}}`
|
|
9
|
+
|
|
10
|
+
## Locked inputs
|
|
11
|
+
|
|
12
|
+
| Input | Version/revision | Source or SHA-256 |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| Hardware IR | | |
|
|
15
|
+
| ESP-IDF | | |
|
|
16
|
+
| TiRTC SDK | | |
|
|
17
|
+
| BSP/board adapter | | |
|
|
18
|
+
|
|
19
|
+
## Capability assessment
|
|
20
|
+
|
|
21
|
+
| Feature | Status | Evidence or blocker |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| H5 live audio | | |
|
|
24
|
+
| H5 live video | | |
|
|
25
|
+
| H5 talkback | | |
|
|
26
|
+
| AI intercom | | |
|
|
27
|
+
|
|
28
|
+
## Acceptance
|
|
29
|
+
|
|
30
|
+
| Level | PASS/FAIL/SKIP | Command and evidence |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| L-1 Environment | | |
|
|
33
|
+
| L0 Generate | | |
|
|
34
|
+
| L1 Build | | |
|
|
35
|
+
| L2 Boot | | |
|
|
36
|
+
| L3 Online | | |
|
|
37
|
+
| L4 Media | | |
|
|
38
|
+
| L5 H5 | | |
|
|
39
|
+
| L6 AI | | |
|
|
40
|
+
| L7 Stability | | |
|
|
41
|
+
|
|
42
|
+
## Firmware and flash record
|
|
43
|
+
|
|
44
|
+
- Serial port/chip: `{{SERIAL_TARGET}}`
|
|
45
|
+
- Firmware artifacts: `{{FIRMWARE_ARTIFACTS}}`
|
|
46
|
+
- Firmware SHA-256: `{{FIRMWARE_SHA256}}`
|
|
47
|
+
- Flash command/result: `{{FLASH_RESULT}}`
|
|
48
|
+
|
|
49
|
+
## Remaining work and risks
|
|
50
|
+
|
|
51
|
+
`{{REMAINING_WORK}}`
|
|
52
|
+
|
|
53
|
+
## Sanitization
|
|
54
|
+
|
|
55
|
+
Logs and artifacts were checked for device keys, Wi-Fi passwords, complete MQTT/WHIP tokens, and user media: `{{SANITIZATION_RESULT}}`.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Capability rules
|
|
2
|
+
|
|
3
|
+
Run `hardware_ir.py assess --strict` before generation. The script applies the minimum current starter contract; use this reference when explaining or extending the result.
|
|
4
|
+
|
|
5
|
+
## Current starter contract
|
|
6
|
+
|
|
7
|
+
| Feature | Required hardware/media path |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `h5_live_audio` | Microphone path that produces G.711 A-law, 8 kHz, mono for stream 10 |
|
|
10
|
+
| `h5_live_video` | Camera plus H.264 Annex-B access units, SPS/PPS and IDR, and key-frame request control for stream 11 |
|
|
11
|
+
| `h5_talkback` | G.711 A-law, 8 kHz downlink decode and speaker path for stream 14 |
|
|
12
|
+
| `ai_talk` | A-law 8 kHz microphone and speaker paths for AI stream 1, started only after `start_session` succeeds |
|
|
13
|
+
|
|
14
|
+
The complete media path must be at least `corroborated` to become `READY_TO_PORT`. A path with unknown facts is `NEEDS_CONFIRMATION`; a confirmed missing or incompatible resource is `BLOCKED`. Only an end-to-end board run becomes `HIL_VERIFIED`.
|
|
15
|
+
|
|
16
|
+
## Non-negotiable checks
|
|
17
|
+
|
|
18
|
+
- Match the TiRTC precompiled SDK platform to the ESP-IDF target and its `manifest/build-contract.env` to the generated configuration.
|
|
19
|
+
- Keep H5 stream IDs and formats stable unless the user explicitly authorizes a coordinated public contract change across the server and all consumers.
|
|
20
|
+
- Start AI media only after the successful `start_session` response, and stop/flush media before disconnecting the session.
|
|
21
|
+
- Copy SDK callback payloads into bounded queues before returning. Perform decoding, playback, HTTP, and lifecycle changes outside SDK callbacks.
|
|
22
|
+
- Use monotonic timestamps and session generation to reject stale frames and delayed callbacks.
|
|
23
|
+
|
|
24
|
+
## Typical blocked cases
|
|
25
|
+
|
|
26
|
+
- A camera outputs JPEG but no evidenced H.264 encoder produces Annex-B access units.
|
|
27
|
+
- The board has a microphone but no encoder path for the required A-law sample rate.
|
|
28
|
+
- The board has a codec but its playback pins, clock, amplifier enable, or BSP driver remain unknown.
|
|
29
|
+
- A generic ESP32-S3 module is named without the carrier board that defines camera/audio wiring.
|
|
30
|
+
- The available TiRTC archive targets another chip, ESP-IDF ABI, or FreeRTOS configuration.
|
|
31
|
+
|
|
32
|
+
When a blocked case would require changing the H5 contract, replacing hardware, or obtaining a new TiRTC SDK build, report those alternatives instead of silently changing the project.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# ESP-IDF environment
|
|
2
|
+
|
|
3
|
+
Run the doctor before generation, build, flash, or monitor:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
python3 <skill-dir>/scripts/doctor.py \
|
|
7
|
+
--expected-idf 5.5 \
|
|
8
|
+
--target esp32s3 \
|
|
9
|
+
--require-workspace
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Add `--project <generated-project>` after generation so the doctor can compare `sdkconfig` or `sdkconfig.defaults` with the TiRTC SDK build contract. Use `--json` when the result will be included in another report.
|
|
13
|
+
|
|
14
|
+
## ThingConnect workspace
|
|
15
|
+
|
|
16
|
+
This distributable skill does not bundle the ThingConnect source tree or TiRTC static library. Resolve the public ThingConnect workspace in this order:
|
|
17
|
+
|
|
18
|
+
1. an explicit `--thing-connect-root <path>`;
|
|
19
|
+
2. `TIRTC_THING_CONNECT_ROOT`;
|
|
20
|
+
3. an ancestor of the project or current directory containing `device-sim/scripts/create_esp32_project.py`;
|
|
21
|
+
4. an ancestor whose `thing-connect/` child contains that generator.
|
|
22
|
+
|
|
23
|
+
The public source is `https://github.com/tangeai/tirtc-server-example`. Clone it only into an explicit destination. The root accepted by the doctor may be either the repository root or its `thing-connect/` child.
|
|
24
|
+
|
|
25
|
+
SDK resolution is independent after generation: an explicit `--sdk-dir` wins, followed by `<project>/third_party/tirtc`, then the SDK packaged in the resolved ThingConnect workspace. The generated project path therefore remains diagnosable after it is moved away from the source repository.
|
|
26
|
+
|
|
27
|
+
## Required checks
|
|
28
|
+
|
|
29
|
+
- `python3`, `git`, `idf.py`, and the target compiler are available in the active shell;
|
|
30
|
+
- `idf.py --version` matches the project's required ESP-IDF line;
|
|
31
|
+
- `IDF_PATH` is coherent when set;
|
|
32
|
+
- the TiRTC SDK contains its header, archive, and `manifest/build-contract.env`;
|
|
33
|
+
- generated Kconfig values explicitly match TiRTC's FreeRTOS ABI-sensitive contract;
|
|
34
|
+
- the requested serial port exists and is writable before flash or monitor.
|
|
35
|
+
|
|
36
|
+
CMake and Ninja are reported separately because an activated ESP-IDF environment may provide or select its own managed versions.
|
|
37
|
+
|
|
38
|
+
## Missing ESP-IDF
|
|
39
|
+
|
|
40
|
+
Checking is read-only. Installing ESP-IDF downloads code and tools, consumes disk space, and may change the developer's shell setup, so perform it only after the user approves the exact version, install directory, supported target, and shell activation method.
|
|
41
|
+
|
|
42
|
+
For an approved installation:
|
|
43
|
+
|
|
44
|
+
1. Confirm the project and TiRTC package require the same ESP-IDF major/minor line. This repository's current ESP32-S3 starter requires 5.5.x.
|
|
45
|
+
2. Consult the current official Espressif installation instructions for the developer's operating system.
|
|
46
|
+
3. Install a pinned 5.5.x release into a user-approved directory; enable the `esp32s3` target and keep the vendor installer logs.
|
|
47
|
+
4. Activate the installed environment in the current shell. Modify a persistent shell profile only when the user explicitly requests it.
|
|
48
|
+
5. Rerun the doctor. Installation is complete only when the IDF version, compiler, SDK files, and project contract checks pass.
|
|
49
|
+
|
|
50
|
+
When downloads, package installation, administrator rights, USB drivers, or group membership are required, surface the exact action and obtain the applicable authorization instead of treating it as an ordinary code edit.
|
|
51
|
+
|
|
52
|
+
## Existing but inactive ESP-IDF
|
|
53
|
+
|
|
54
|
+
If `IDF_PATH/tools/idf.py` exists but `idf.py` or the target compiler is absent from `PATH`, report the environment as inactive. Activate that installation using its vendor-provided export script and rerun the doctor; do not install a second copy merely because the current shell is inactive.
|