@seeed-studio/solution-cli 0.0.0-stage → 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.
@@ -0,0 +1,56 @@
1
+ # 方案咨询与场景匹配手册 (Consult & Select Guide)
2
+
3
+ > **定位**:供 AI Agent 在用户处于 **场景探索(DISCOVERY)** 或 **方案咨询(CONSULT)** 时查阅。当用户询问“我想做某某事情”、“有什么物联网方案”、“如何解决 XX 问题”时,使用本手册定位场景、推荐参考设计并对齐硬件族。
4
+
5
+ ---
6
+
7
+ ## 1. 意图识别与场景路由规则
8
+
9
+ 当用户的自然语言输入包含以下关键词时,直接调用 CLI 工具查询相关场景:
10
+
11
+ | 行业领域 | 触发关键词(中英文) | 对应场景 Slug | 核心参考设计 |
12
+ |---|---|---|---|
13
+ | **智慧养老 / 监护** | 养老、跌倒检测、防摔、老人、呼叫、fall detection, elderly | `elderly-care-monitoring` | `fall_detection` |
14
+ | **智慧农业 / 环境** | 农田、大棚、温湿度、气象、土壤、agriculture, soil, greenhouse | `environment-monitoring` | `agri_env_monitor` |
15
+ | **工业质检 / 缺陷** | 缺陷检测、工业视觉、表面划痕、流水线装配、visual inspection, defect | `industrial-visual-inspection` | `edge_inspection_surface`, `edge_inspection_assembly` |
16
+ | **智慧交通 / 园区** | 车牌识别、停车场、车辆计数、停车位、anpr, license plate, parking | `parking-traffic-vision` | `edge-anpr-plate-recognition` |
17
+ | **边缘语音 / 助手** | 离线语音、大模型音箱、呼救感知、voice ai, audio assistant, respeaker | `conversational-voice-ai` | `home-assistant-local-voice-hub`, `conversational_voice_ai` |
18
+ | **建筑能耗 / 暖通** | 楼宇能耗、电表、水表、空调节能、HVAC, energy retrofit | `building-energy-retrofit` | `building_energy_management` |
19
+ | **室内外定位 / 资产** | 蓝牙信标、资产定位、人员卡片、防丢、GNSS, tracker, beacon, T1000 | `indoor-outdoor-positioning` | `indoor_positioning_ble_lorawan` |
20
+
21
+ ---
22
+
23
+ ## 2. 工具调用标准动作
24
+
25
+ ### 动作一:搜索场景与参考设计
26
+ 使用命令:
27
+ ```bash
28
+ solution-cli hub search "<用户关键词>" --limit 5 --json
29
+ ```
30
+ - **输出结果解释**:
31
+ - 提取匹配的 `Scenario`(阐述业务场景、痛点、成效);
32
+ - 提取推荐的 `Reference Design`(阐述技术路线、预设模式 presets、开发 wiki);
33
+ - 提取 `family_ids`(推荐的主板/相机/网关产品族)。
34
+
35
+ ### 动作二:向用户呈现专业方案推荐模板
36
+ 在向用户回答时,遵循以下结构化呈现,**严禁一上来就输出 deploy 命令**:
37
+
38
+ ```markdown
39
+ ### 💡 方案匹配建议
40
+
41
+ 1. **业务场景**:[场景名称](如:养老看护)
42
+ - **痛点与价值**:[简述核心业务价值,如:端侧跌倒检测,1.4s 本地快速判定,视频不出设备彻底保护隐私]
43
+ 2. **推荐参考设计**:[参考设计名称](ID: `fall_detection`)
44
+ - **技术方案**:[骨骼姿态估计 / MQTT 告警联动]
45
+ - **预设模式**:[如 recamera, jetson, rk, hailo]
46
+ - **开发 Wiki**:[官方 Wiki 链接]
47
+ 3. **推荐主硬件**:[硬件族列表,如 reComputer J4012, reCamera Pro]
48
+ - 采购与配件明细可进一步通过硬件选型指引查询。
49
+ ```
50
+
51
+ ---
52
+
53
+ ## 3. 防幻觉与守则
54
+
55
+ 1. **禁止编造方案**:如果 `solution-cli hub search` 返回为空,诚实告知用户官方方案库暂无现成参考设计,并基于通用 Jetson / 树莓派 Docker 能力提供定制建议。
56
+ 2. **严禁在咨询期直接执行部署**:用户此时通常未采购或未连接硬件。应引导用户进入下一步【硬件选型与配件推荐】(查阅 `references/hardware-bom.md`)。
@@ -0,0 +1,300 @@
1
+ # Deploy a solution
2
+
3
+ Run Step 0 in `SKILL.md` first (engine installed, solutions pulled). Examples
4
+ use `--lang en`; use `--lang zh` for a Chinese-speaking user.
5
+
6
+ ## 1. Find the solution
7
+
8
+ ```
9
+ sensecraft-engine solution list --json
10
+ ```
11
+
12
+ Match the user's wording against `name` / `summary`. Confirm the `id` with the
13
+ user if more than one fits. `sensecraft-engine solution show <id> --lang en`
14
+ prints the full solution if you need more detail.
15
+
16
+ ## 2. Read what the deployment needs
17
+
18
+ ```
19
+ sensecraft-engine deploy-info <id> --lang en
20
+ ```
21
+
22
+ - `presets[]` — deployment variants. If there is more than one, ask the user which
23
+ one, then run again with `--preset <preset>`. Without `--preset` the template
24
+ lists the steps of every preset.
25
+ - `steps[]` — each step's `device_id`, `type`, `required`, `connection_type`
26
+ (`ssh` / `serial` / `none` / ...), `parameters`, `category` (`deploy` or
27
+ `verify`) and `interactive`. Steps with `has_targets: true` (e.g.
28
+ `docker_deploy`) list `targets`: `local` = this computer's Docker, `remote` =
29
+ a device over SSH.
30
+ - Verify steps that need the user carry a ready command list:
31
+ `counting_cli`, `face_cli`, `wizard_cli` or `robot_cli` (see `verify.md`).
32
+ - `request_template` — a ready request body. Values shaped like
33
+ `<REQUIRED: ...>` must be filled in; everything else has a working default.
34
+
35
+ Never make up step or device IDs: the keys of `device_connections` and the
36
+ entries of `selected_devices` are exactly the ones in `request_template`.
37
+
38
+ Optional discovery, to pre-fill values:
39
+
40
+ ```
41
+ sensecraft-engine device ports # serial ports (USB devices)
42
+ sensecraft-engine device scan --timeout 5 # SSH devices on the LAN (mDNS, IPv4)
43
+ sensecraft-engine device list # device catalog
44
+ sensecraft-engine doctor --json # this computer: serial permission, Docker, disk, network
45
+ ```
46
+
47
+ ## 3. Fill in and confirm
48
+
49
+ Ask the user for every `<REQUIRED: ...>` value (device IP, username, password,
50
+ serial port, and any user inputs). Serial steps carry both `port` and
51
+ `serial_port` placeholders; fill both with the port name from `device ports`.
52
+
53
+ For `docker_deploy` steps pick the target with `"target": "<target id>"` **and
54
+ set `"target_type"` to that target's own type** (`local` / `remote`) — or
55
+ remove `target_type`. Leaving the template's type while switching `target` is
56
+ rejected. Each deploy-info target carries `type` (`local` / `remote`) and,
57
+ when the target is tied to a board, `device` / `device_name` /
58
+ `device_class` (for example `rk3576`, `RK3576`): pick the target whose
59
+ `type` and device match the user's hardware, and copy its `type` into
60
+ `target_type`. `device` is null for targets that run on any Docker host. On
61
+ engine builds whose targets have no `type` yet, the target id suffix (for
62
+ example `_local`, `_remote`) is only a hint; confirm the type with `validate`
63
+ (§3, dry run) before starting. Remote targets need `host`, `username`
64
+ and either `password` or `key_file`:
65
+
66
+ Windows native `local` Docker is unsupported by both deployers and returns the
67
+ platform check failure; use a `remote` target over SSH to a Linux Docker host.
68
+ WSL must use the Linux engine binary and is Linux-host evidence, not Windows
69
+ native local Docker support. On macOS, the local target uses the literal
70
+ `docker` executable visible to the new engine process. If Docker Desktop's CLI
71
+ is only in its application bundle, provide that bundle's `bin` directory in a
72
+ temporary engine-process `PATH`; do not alter global shell configuration or
73
+ assume an already-running instance inherits the path. Docker Desktop being
74
+ running is a prerequisite check, not a deployment result.
75
+
76
+ - Password: `"password": "<the user's password>"`.
77
+ - SSH key: `"key_file": "~/.ssh/id_ed25519"` and no `password` field. The key
78
+ must be a regular file inside `~/.ssh` (or the engine's data directory); a
79
+ relative path is resolved under `~/.ssh`; other locations are refused.
80
+
81
+ Add `"auto_replace_containers": true` (or the `--replace-existing` flag) only if
82
+ the user agrees to replace existing containers with the same name.
83
+
84
+ Write the filled body to `./.sensecraft-work/body.json` in the current working
85
+ directory, not `/tmp` (a file keeps the password out of the process list and
86
+ avoids shell quoting problems, notably in PowerShell):
87
+
88
+ ```json
89
+ {
90
+ "solution_id": "<id>",
91
+ "preset_id": "<preset>",
92
+ "selected_devices": ["<step_id>"],
93
+ "device_connections": {
94
+ "<step_id>": {"target": "<target_id>", "target_type": "remote",
95
+ "host": "192.168.1.50", "username": "<user>",
96
+ "password": "<password>"}
97
+ }
98
+ }
99
+ ```
100
+
101
+ Show the user the body with the password as `<REDACTED>` before starting.
102
+
103
+ On Windows PowerShell 5.1, write the JSON as UTF-8 **without a BOM**. The
104
+ default `Set-Content -Encoding UTF8` adds a BOM, which the Rust request reader
105
+ rejects. Keep the request file under the current workspace and do not print it
106
+ when it contains a password:
107
+
108
+ ```powershell
109
+ $RequestPath = [System.IO.Path]::GetFullPath('.sensecraft-work/body.json')
110
+ $RequestDir = [System.IO.Path]::GetDirectoryName($RequestPath)
111
+ New-Item -ItemType Directory -Force -Path $RequestDir | Out-Null
112
+ $body = @{
113
+ solution_id = '<id>'
114
+ preset_id = '<preset>'
115
+ selected_devices = @('<step_id>')
116
+ device_connections = @{
117
+ '<step_id>' = @{
118
+ target = '<target_id>'
119
+ target_type = 'remote'
120
+ host = '192.168.1.50'
121
+ username = '<user>'
122
+ password = '<password>'
123
+ }
124
+ }
125
+ }
126
+ $json = $body | ConvertTo-Json -Depth 50
127
+ [System.IO.File]::WriteAllText($RequestPath, $json, [System.Text.UTF8Encoding]::new($false))
128
+ ```
129
+
130
+ `-Depth 50` preserves nested `device_connections`; PowerShell's default depth
131
+ of 2 is insufficient. Replace placeholders with user-provided values and keep
132
+ the file protected; never print the password.
133
+
134
+ Before a remote deploy, run the dry run (no changes on any device):
135
+
136
+ ```
137
+ sensecraft-engine validate --params ./.sensecraft-work/body.json
138
+ ```
139
+
140
+ It reports parameter errors and previews which verify steps will need the user
141
+ (`verify_required`). It also lists the changes the deployment may make on each
142
+ target beyond the solution's own files and containers, as
143
+ `side_effects: {"<step_id>": [{"kind", "what", "when", "decided_in"}, ...]}`:
144
+
145
+ | `kind` | Change on the target |
146
+ |---|---|
147
+ | `sudo` | Commands run with `sudo` (Docker through `sudo docker`, actions marked `sudo`) |
148
+ | `daemon_json_write` | Merges `registry-mirrors` into `/etc/docker/daemon.json` (other keys kept) |
149
+ | `docker_daemon_restart` | Restarts the Docker daemon (all containers on the device restart with it) |
150
+ | `package_install` | Installs packages (Docker / Compose, `.deb`, apt / pip in an action) |
151
+ | `system_config` | Changes system settings (`usermod`, `update-alternatives`, services) |
152
+ | `reboot` | Reboots the device |
153
+ | `firmware_flash` | Flashes firmware |
154
+ | `local_script` | Runs a script on this computer |
155
+
156
+ `what` says what happens in plain words, `when` the condition: `always`, a
157
+ condition the engine checks during the deploy (for example "the device cannot
158
+ reach Docker Hub"), or "the action's script decides". `decided_in` points at
159
+ engine code; it is for bug reports, not something to read.
160
+
161
+ Tell the user what the list contains. If the user forbade a kind that is
162
+ listed (Docker restart, daemon.json changes, reboot, package installs) and
163
+ has not agreed to it, stop and ask before starting, also when `when` is a
164
+ condition: the deploy may meet it. Exception: when the action's script is in
165
+ the solution's step file (`devices/<x>.yaml` under `actions` or
166
+ `remote_overrides.actions` in `~/.sensecraft/content/solutions/<id>/`, or
167
+ `$SC_HOME/content/solutions/<id>/` when `SC_HOME` is set), read
168
+ it; if its condition can be checked read-only on the device and is not met,
169
+ say so and continue. Actions that come from the engine's built-in device
170
+ profile are not in that file: treat them as possible. Builds without a
171
+ `side_effects` field: assume a remote Docker deploy may write
172
+ `/etc/docker/daemon.json` and ask when the user restricted that.
173
+
174
+ Before the deploy, save the target's current Docker daemon config so changes
175
+ can be shown and restored:
176
+
177
+ ```
178
+ ssh <user>@<ip> cat /etc/docker/daemon.json > ./.sensecraft-work/daemon.json.before
179
+ ```
180
+
181
+ (a missing file is fine: note that it did not exist). After the deploy, `cat`
182
+ it again and show the user any difference; restoring it is a change on the
183
+ device and needs the user's OK.
184
+
185
+ ## 4. Start and follow
186
+
187
+ ```
188
+ sensecraft-engine deploy --params ./.sensecraft-work/body.json --yes --detach --lang en
189
+ ```
190
+
191
+ `--detach` prints `{"deployment_id": "...", "log": "..."}` and returns. Keep the
192
+ `deployment_id`. Exit code 2 means the request or arguments are invalid. When
193
+ the JSON error contains a `missing[]` list, ask for those fields
194
+ (`preset_id`, `device_connections.<id>.<key>`); for `bad_request` without
195
+ `missing[]`, inspect the solution ID, JSON shape, and parameter spelling before
196
+ asking for another value.
197
+ Exit code 3 with a 409 means the device is busy with another deployment.
198
+
199
+ Follow progress in one of two ways:
200
+
201
+ - **Poll** (works with any agent's command timeout): every 3–5 seconds run
202
+ `sensecraft-engine status <deployment_id> --json`. `status` is `running`
203
+ while in progress and ends as `completed`, `failed`, `cancelled` or
204
+ `interrupted`; each device and step has its own `status` and `message`.
205
+ Exit code: 0 while running or completed, 1 for failed / cancelled /
206
+ interrupted.
207
+ - **Event stream:** `sensecraft-engine logs <deployment_id> --since <N>`
208
+ prints NDJSON events (`{seq, type, ...}`) with `seq > N` and exits; add
209
+ `--follow` to keep streaming until the deployment ends (run it in the
210
+ background if your command timeout is short). Remember the last `seq` and
211
+ pass it as `--since` next time, so you never read the same events twice.
212
+
213
+ Event types to act on:
214
+
215
+ | `type` | Meaning |
216
+ |---|---|
217
+ | `log` | `level`, `message`; `error`-level lines are evidence for `diagnose.md` |
218
+ | `step_skipped` with `reason: verify_required` | The step needs the user — the `verify_required` event before it has the details |
219
+ | `verify_required` | Go to `verify.md` with this event |
220
+ | `device_completed` | One device finished |
221
+ | `deployment_completed` | Last event; `status` is the final result |
222
+
223
+ Read the JSON directly instead of grepping it (no interpreter, see `SKILL.md`
224
+ rule 5). Delete `./.sensecraft-work/` afterwards (`body.json` holds a
225
+ password).
226
+
227
+ Without `--detach`, `deploy --json` streams the same events on stdout and ends
228
+ with one `{"type": "result", "deployment_id", "status", "devices"}` line; the
229
+ exit code is the deployment's result. Ctrl-C or a killed agent command only
230
+ detaches: the deployment keeps running in the engine instance; continue with
231
+ `status` / `logs`.
232
+
233
+ If `--detach` prints `"backend": "python"`, the desktop app's backend is
234
+ running this deployment: `logs` then has `log` and `deployment_completed`
235
+ events only, no `verify_required`. Do the guide's verify steps with the user
236
+ as described in `verify.md` §1.
237
+
238
+ ## 5. Verify
239
+
240
+ `completed` is not enough. Check the result is actually usable:
241
+
242
+ - `verify_required` events: handle each one with `verify.md`.
243
+ - Docker steps: on the target the solution's containers have `status`
244
+ `running` (and are `(healthy)` in `docker ps` when they define a health
245
+ check):
246
+ `sensecraft-engine docker ps --json` (this computer),
247
+ `sensecraft-engine docker ps --host <ip> --username <user> --key-file ~/.ssh/id_ed25519 --json`
248
+ (remote with an SSH key; same path rules as `key_file` in §3), or
249
+ `SC_SSH_PASSWORD=<password> sensecraft-engine docker ps --host <ip> --username <user> --json`
250
+ (remote with a password; `--password-stdin` reads it from stdin instead).
251
+ `docker logs <container> --host ... --key-file ...` takes the same options.
252
+ Older engines without `--key-file` accept a password only; with a key-only
253
+ device use `ssh <user>@<ip> docker ps`.
254
+ - Services with a port: `curl -s -o /dev/null -w "%{http_code}" http://<host>:<port>/`
255
+ returns 2xx/3xx.
256
+ - Stay running: `completed` can precede a crash loop. CLI deploys already
257
+ watch each Docker service for about 60 seconds after its health check
258
+ answers and fail the step with `Service <name> is not healthy: ...` when a
259
+ container restarts, exits or disappears. Check once more yourself: run `docker ps` once,
260
+ wait about 60 seconds, then run it again and read the container restart
261
+ count (`docker inspect -f '{{.Name}} {{.RestartCount}} {{.State.Status}}' <container>`
262
+ over SSH, or the `Restarting` / `Up Less than a second` status in `docker ps`).
263
+ The counts must stay 0 and the verify endpoint (the step's `url`, or the
264
+ port check above) must still answer after the wait. If a container restarts
265
+ or the endpoint stops answering, report a crash loop, not success, and go to
266
+ `diagnose.md`.
267
+ - Tell the user the URL to open and anything the guide says to do next.
268
+
269
+ ## 6. If anything failed
270
+
271
+ Go straight to `diagnose.md` with the `deployment_id`. Do not stop to ask.
272
+
273
+ To retry after a fix, send the same body again — same `preset_id` and
274
+ `device_connections` — with `selected_devices` listing only the device(s) that
275
+ failed (or `--device <step_id>` for one device); the others are already done.
276
+ Leaving manual steps in is harmless (they are skipped).
277
+
278
+ Two kinds of failure, two policies:
279
+
280
+ - **Transient registry or network errors** (image pull answered 401 / 429 /
281
+ 500 / 502 / 503 / 504, `TLS handshake timeout`, `i/o timeout`,
282
+ `connection reset`, a mirror that flaps): retry the same body without
283
+ asking, up to 5 times, waiting 15, 30, 60, 120, 240 seconds between tries.
284
+ These retries do not count as fix rounds. If the engine log shows the
285
+ mirror changing between tries, that is the same transient case. After the
286
+ 5th failure, stop and report the error text, the waits and the registry host.
287
+ - **Deterministic failures** (bad parameter or credentials, port taken, disk
288
+ full, missing file, a container that exits on start, the same error with the
289
+ same text after a fix): stop retrying blindly. First retry after a fix
290
+ without asking; later retries need the user's OK; at most 3 rounds, then
291
+ report.
292
+
293
+ If you cannot tell which kind it is, treat the second occurrence of the same
294
+ error after a wait as deterministic.
295
+
296
+ ## Cancel
297
+
298
+ Only with the user's OK: `sensecraft-engine cancel <deployment_id>`. Then check
299
+ `status` until it is `cancelled`, and tell the user which steps had already run:
300
+ the device may be half-configured.
@@ -0,0 +1,133 @@
1
+ # Diagnose a failed or misbehaving deployment
2
+
3
+ Follow the rules in `SKILL.md` (what needs the user's OK, at most 3 rounds).
4
+ If the user runs the desktop app and deployed from it, `app-only.md` §3 covers
5
+ the app's own deployment list and logs.
6
+
7
+ ## 1. Find the deployment
8
+
9
+ If you started it, you already have the `deployment_id`. Otherwise:
10
+
11
+ ```
12
+ sensecraft-engine api GET "/api/deployments?limit=20" # newest first: id, solution_id, status
13
+ ```
14
+
15
+ ## 2. Collect evidence
16
+
17
+ Start with the status. **The real error is usually in the failing step's
18
+ `message`** (`devices[].steps[]` with `status: failed`); top-level errors may
19
+ only say "Device X deployment failed".
20
+
21
+ ```
22
+ sensecraft-engine status <deployment_id> --json
23
+ sensecraft-engine api GET /api/deployments/<deployment_id>/summary
24
+ ```
25
+
26
+ Then the error-level events, and the full log of the failing device:
27
+
28
+ ```
29
+ sensecraft-engine logs <deployment_id> # NDJSON; look at type=log, level=error
30
+ sensecraft-engine api GET "/api/deployments/<deployment_id>/logs?device_id=<device_id>&limit=300"
31
+ ```
32
+
33
+ If the logs are empty or end abruptly (status `interrupted`: the engine
34
+ instance stopped during the deployment), read the engine log
35
+ `~/.sensecraft/logs/engine.log` (Windows: `%USERPROFILE%\.sensecraft\logs\engine.log`),
36
+ last ~200 lines. `sensecraft-engine daemon status` prints JSON (`--json` is
37
+ accepted and changes nothing; exit 1 with `"running": false` when no instance
38
+ is running). A running instance shows `busy` (a deployment is running),
39
+ `python_backend` (null: no desktop-app backend involved) and
40
+ `python_supervisor.state`.
41
+
42
+ Note the failing device, step and the first error — later errors are usually
43
+ consequences of the first.
44
+
45
+ ## 3. Check the device (read-only, no need to ask)
46
+
47
+ Which containers belong to the solution: in the solution folder
48
+ (`~/.sensecraft/content/solutions/<id>/`) the step's `devices/<x>.yaml` names
49
+ the compose file (`docker.compose_file`) and project
50
+ (`docker.options.project_name`); the compose file may set `container_name`.
51
+
52
+ Through the engine (no separate SSH client needed):
53
+
54
+ ```
55
+ SC_SSH_PASSWORD=<password> sensecraft-engine docker ps --host <ip> --username <user> --json
56
+ SC_SSH_PASSWORD=<password> sensecraft-engine docker logs <container> --host <ip> --username <user> --tail 100
57
+ # SSH key (engines with --key-file; older engines accept a password only):
58
+ sensecraft-engine docker ps --host <ip> --username <user> --key-file ~/.ssh/id_ed25519 --json
59
+ ```
60
+
61
+ Omit `--host` for this computer's Docker. With an SSH client, the same
62
+ read-only commands as before:
63
+
64
+ ```
65
+ docker ps -a --format '{{.Names}}\t{{.Status}}\t{{.Ports}}'
66
+ docker logs --tail 100 <container>
67
+ df -h /
68
+ ss -ltnp # who holds a port (use: sudo ss -ltnp if names are hidden)
69
+ ping -c 2 8.8.8.8 # internet reachability
70
+ ```
71
+
72
+ For local targets and USB devices also run `sensecraft-engine doctor --json`
73
+ (serial permission, Docker, disk, network) and
74
+ `sensecraft-engine device ports` (is the USB device visible).
75
+
76
+ ## 4. Classify, then act
77
+
78
+ Match the evidence against `error-catalog.md`. Every problem falls in one of
79
+ three classes:
80
+
81
+ | Class | Example | What you do |
82
+ |---|---|---|
83
+ | **Device / environment** | port taken, disk full, Docker missing, network blocked, wrong password, serial permission | Propose the exact fix commands → user OK → run them → retry |
84
+ | **Solution configuration** | timeout too short, wrong port/env in compose, health check endpoint 404, minor syntax/transfer glitch | **CLI / Engine mode (Auto-healing patch)**: Copy the solution from `~/.sensecraft/content/solutions/<id>` to `./.sensecraft-work/solutions/<id>`, apply the minimal required fix. Stop the previous engine instance (`sensecraft-engine daemon stop`) so it reloads the new solution directory, and redeploy with `--solutions-dir ./.sensecraft-work/solutions` so the user succeeds immediately! Show the user the patch diff. Desktop app running: fix it in the edit copy (`app-only.md` §4) → validate → apply → retry. |
85
+ | **Engine bug** | exit code 5, a panic in `engine.log`, a step that fails the same way on any device, behaviour contradicting the solution's own config | Stop. Prepare the issue report below |
86
+
87
+ If the evidence does not fit any row, say so and show the user the first error
88
+ with your best hypothesis and what would confirm it. Do not guess-and-retry.
89
+
90
+ ## 5. Retry
91
+
92
+ Restart only the failed device (`selected_devices` = that device, see
93
+ `deploy.md` §6). First retry without asking; later ones need the user's OK.
94
+ After 3 rounds without success, stop and report.
95
+
96
+ ## 6. Report
97
+
98
+ Always finish with: what failed, the evidence (first error line), what you
99
+ changed (commands run, files edited with a diff), and the result.
100
+
101
+ ### Engine bug: issue report
102
+
103
+ Engine bugs are reported on the public repository
104
+ `suharvest/sensecraft-solutions` with the **App / engine bug** form; the
105
+ maintainers move them on from there. Do not submit it yourself — give the user
106
+ a link to open in the browser and submit with their own GitHub account.
107
+
108
+ 1. Fill these fields (passwords, tokens and IP addresses removed):
109
+
110
+ | Field id | Content |
111
+ |---|---|
112
+ | `title` | `[engine] <step type> fails: <first error, one line>` |
113
+ | `app_version` | `engine <version>` from `sensecraft-engine --version` (or the app's version when deployed from the app) |
114
+ | `os` | this computer's OS and version |
115
+ | `device` | target device model, OS, CPU architecture (or "this computer (local Docker)") |
116
+ | `solution` | `<solution_id> / <preset_id> / <step id> (<type>)` |
117
+ | `edited` | `No` or `Yes (diff below)` |
118
+ | `what_happened` | first error and the 10–20 log lines around it |
119
+ | `reproduce` | numbered steps (the `sensecraft-engine` commands, password removed) |
120
+ | `ruled_out` | device checks that passed |
121
+ | `diff` | the edit copy's diff (`app-only.md` §4.5), only if edited |
122
+
123
+ 2. Build the link, URL-encoding every value:
124
+
125
+ ```
126
+ https://github.com/suharvest/sensecraft-solutions/issues/new?template=engine-bug.yml&title=<...>&app_version=<...>&os=<...>&device=<...>&solution=<...>&edited=<...>&what_happened=<...>&reproduce=<...>&ruled_out=<...>
127
+ ```
128
+
129
+ Keep the link under about 7,000 characters: if the log excerpt or diff is
130
+ longer, leave those fields out of the link and give the user that text
131
+ separately to paste into the form.
132
+ 3. Show the user the filled fields and the link, and say the report will be
133
+ public.
@@ -0,0 +1,33 @@
1
+ # Error catalog
2
+
3
+ Match the **first** error of the failing step. Class: **Env** = device /
4
+ environment, **Config** = solution configuration, **Engine** = engine / app bug.
5
+
6
+ | Evidence | Class | Cause | Action |
7
+ |---|---|---|---|
8
+ | Exit 3, "no solutions" / hint to run `content pull` | Env | Solutions not downloaded yet | `sensecraft-engine content pull`, then run the command again |
9
+ | Exit 2, `{"type":"error","code":"missing_params","missing":[...]}` | Env | A required value is not in the request | Ask the user for each `missing` field (`preset_id`, `device_connections.<id>.<key>`) |
10
+ | Exit 3, HTTP 409 with `busy: [...]` | Env | Another deployment is using the same device | Wait for it (`status <id>`), or ask the user whether to cancel it |
11
+ | Exit 4, `requires_desktop_app` | Env | This step's deployer is not in the engine | Tell the user to deploy the solution from the SenseCraft Solution app |
12
+ | `key_file` refused (outside `~/.ssh`) | Env | The engine only reads SSH keys inside `~/.ssh` or its data directory | Use a key under `~/.ssh` (copying a key there needs the user's OK) |
13
+ | `the offline package was prepared with different values: ...` | Env | Deploy parameters differ from the ones the offline package was prepared with | Plan / prepare again with the deploy's values (`offline.md`), or deploy with the prepared values |
14
+ | `doctor` serial check: not a member of `dialout`; serial port permission denied | Env | User may not open USB serial ports (Linux) | Propose the `fix` from `doctor` (`sudo usermod -aG dialout <user>`, then log out and in) — user OK |
15
+ | `port_busy` from `counting` / `face` / `wizard` | Env | A deployment or another session holds the serial port | Wait until it finishes; do not force it |
16
+ | `Missing host` / connection fields empty | Env | `host` not given in `device_connections` | Ask the user for the device IP, redeploy |
17
+ | SSH `Authentication failed` / `Permission denied (publickey,password)` | Env | Wrong username or password | Ask the user to re-check; the solution's default user is in `request_template` |
18
+ | SSH timeout / `No route to host` | Env | Device off, wrong IP, other network | `ping` it; ask the user to check power and that both are on the same LAN |
19
+ | `port is already allocated` / `address already in use`; pre-check `port_available` failed | Env | Another process or container holds the port | `ss -ltnp` / `docker ps` to find it; propose stopping it (user OK), or change the port in config |
20
+ | `Conflict. The container name ... is already in use` | Env | Leftover container from an earlier deployment | Redeploy with `"auto_replace_containers": true` after the user agrees |
21
+ | `no space left on device`; pre-check `disk_space` failed | Env | Disk full | `df -h`, `docker system df`; propose `docker image prune` (user OK) |
22
+ | `docker: command not found`; pre-check `docker_version` failed | Env | Docker not installed / too old | Tell the user; installing needs their OK |
23
+ | `permission denied while trying to connect to the Docker daemon socket` | Env | User not in `docker` group | Propose `sudo usermod -aG docker <user>` then re-login (user OK) |
24
+ | Image pull: `TLS handshake timeout`, `i/o timeout`, `toomanyrequests` | Env | Registry unreachable or rate-limited | Look for `Mirror context resolved` in the logs: absent → mirror detection failed; retry once, then report network status to the user |
25
+ | `manifest unknown` / `not found` for an image | Config | Image name or tag wrong, or not built for the device's CPU (arm64 vs amd64) | `docker manifest inspect <image>` (read-only): "no such manifest" → the tag does not exist, fix it; it exists but lists no platform matching the device's `uname -m` → wrong architecture, pick an image built for it |
26
+ | Container `Restarting` / `Exited (1)`; error in `docker logs` | Config (usually) | App inside the container misconfigured: env var, mounted path, port, timeout | Read `docker logs`; CLI mode: copy to `./.sensecraft-work/solutions/<id>`, fix compose / env / timeout, and redeploy with `--solutions-dir ./.sensecraft-work/solutions`; Desktop app: fix in edit copy (`app-only.md` §4) |
27
+ | Container `(healthy)` but the step reports health-check failure | Config | `health_check_endpoint` points at a URL that returns 401/404 | Fix the endpoint in the device YAML (`docker.services[].health_check_endpoint`) via local patch copy or app edit copy |
28
+ | Containers up, but the page / port does not answer from another machine | Env | Device firewall | Check `sudo iptables -L -n` / `ufw status`; propose opening the port (user OK) |
29
+ | USB device not detected (`No device found`, `detect` returns nothing) | Env | Cable is charge-only, wrong port, driver missing | Ask the user to replug with a data cable; on Windows check Device Manager for a COM port |
30
+ | YAML / validation error naming a solution file | Config | Broken solution file, syntax typo, or timeout too short | CLI mode: copy to `./.sensecraft-work/solutions/<id>`, fix typo/timeout, and redeploy with `--solutions-dir ./.sensecraft-work/solutions`; Desktop app: fix in edit copy (`app-only.md` §4) |
31
+ | Python traceback mentioning `provisioning_station/` | Engine | App bug | Issue report (`diagnose.md` §6) |
32
+ | `sensecraft-engine` exits 5, or a panic in `~/.sensecraft/logs/engine.log` | Engine | Engine bug | Issue report (`diagnose.md` §6) |
33
+ | Same step fails identically on two different devices after the environment checks pass | Engine (likely) | App bug | Issue report (`diagnose.md` §6) |
@@ -0,0 +1,83 @@
1
+ # 硬件选型与配件推荐准则 (Hardware & BOM Guide)
2
+
3
+ > **定位**:供 AI Agent 在用户处于 **采购选型(PROCUREMENT)** 或 **BOM 验证(VERIFY_BOM)** 阶段查阅。当用户询问“需要买什么硬件”、“XX 主板需要配什么配件”、“电源/天线怎么选”时使用本手册。
4
+
5
+ ---
6
+
7
+ ## 1. 核心流程:主板 ➔ 必选配件 ➔ 选配传感器/增强件
8
+
9
+ 为用户提供硬件采购清单时,必须严格区分:
10
+ 1. **主运算单元(Host Platform)**:如 `recomputer_j40`、`recamera_pro`、`sensecap_m2_gateway`。
11
+ 2. **必选配件(REQUIRED Accessories)**:**若缺少则系统物理上无法工作**(如:Super 款未配电源线、网关未配天线、工控机无输入电源端子)。
12
+ 3. **选配配件(OPTIONAL Accessories)**:根据具体功能选配(如:存储 SSD、4G 蜂窝网卡、降噪麦克风阵列、云台支架)。
13
+
14
+ ---
15
+
16
+ ## 2. 工具调用标准命令
17
+
18
+ ### 动作一:查询主设备规格与默认 SKU
19
+ ```bash
20
+ solution-cli product query <family_id> --json
21
+ ```
22
+ - 获取产品族名称、官方默认主 SKU、物理电气规格(电压、电流接口、M.2 槽位等)。
23
+
24
+ ### 动作二:查询经过验证的配件清单
25
+ ```bash
26
+ solution-cli product accessories <family_id> --json
27
+ ```
28
+ - 输出严格由确定性代码计算物理兼容性的配件清单:
29
+ - `REQUIRED` 列表:带必买原因(`reason`)、购买 SKU 与直达链接;
30
+ - `OPTIONAL` 列表:带周边传感器、通信模块、固态硬盘与购买链接。
31
+
32
+ ### 动作三:验证物理配件兼容性
33
+ 当用户提到已有某个第三方配件或自制电源/天线时:
34
+ ```bash
35
+ solution-cli product verify-compatibility --host <family_id> --accessory "<用户配件描述>" --json
36
+ ```
37
+
38
+ ---
39
+
40
+ ## 3. 六大物理兼容性硬红线(严禁大模型自行脑补)
41
+
42
+ Agent 严禁凭“常识”告知配件通用,必须遵循 CLI 评估结果:
43
+
44
+ 1. **供电 (Power Supply)**:
45
+ - `recomputer_j40` / `j30` 必须满足 **19V 4.74A (90W)**,DC Jack 规格为 **5.5x2.5mm**(与普通 5.5x2.1mm 路由器插头不兼容);
46
+ - USB-C 供电的设备(如 `recamera_2002`、`sensecap_watcher`),必须核对 PD 协议档位与最低电流。
47
+ 2. **射频与天线 (RF Antenna)**:
48
+ - 必须严格区分 **N 型(N-Type)** 与 **SMA**;
49
+ - 室外网关多为 **N-Type Female(母头)**,天线必须选 **N-Type Male(公头,带针)**,若为 SMA 天线必须提醒用户加购 **N转SMA转接线(Pigtail Cable)**;
50
+ - 频段必须对齐:欧洲选 `EU868`,北美选 `US915`,亚洲选 `AS923`。
51
+ 3. **M.2 扩展卡 (M.2 Key)**:
52
+ - **Key-M**:仅用于 NVMe SSD(尺寸通常为 2280);
53
+ - **Key-E**:仅用于 WiFi / 蓝牙 / AI 加速卡(尺寸通常为 2230);
54
+ - 严禁建议用户把 Key-M SSD 插到 Key-E 槽位。
55
+ 4. **以太网供电 (PoE)**:
56
+ - 区分 `802.3af`(15.4W)与 `802.3at`(30W PoE+)。`reCamera Pro` 需保证交换机端口支持 802.3at。
57
+
58
+ ---
59
+
60
+ ## 4. 推荐呈现模板
61
+
62
+ 向用户输出采购清单时,必须使用如下清晰结构:
63
+
64
+ ```markdown
65
+ ### 🛒 硬件采购清单建议(BOM)
66
+
67
+ #### 1. 主计算单元
68
+ - **产品型号**:reComputer J4012(NVIDIA Jetson Orin NX 16GB)
69
+ - **推荐 SKU**:`110110145`
70
+ - **购买链接**:[Seeed 官方商城直达](https://www.seeedstudio.com/...)
71
+
72
+ #### 2. 必需配件 (REQUIRED - 缺少无法开机)
73
+ - ⚠️ **19V 4.74A 电源适配器** (SKU: `313080684`)
74
+ - 原因:本主机出厂不配电源适配器,必须单独采购 5.5x2.5mm 19V 适配器。
75
+ - 购买链接:[购买电源](https://www.seeedstudio.com/...)
76
+ - ⚠️ **国标/美标 AC 三脚电源线** (SKU: `106990470`)
77
+
78
+ #### 3. 推荐选配 (OPTIONAL - 按需选购)
79
+ - **M.2 NVMe SSD 256GB** (SKU: `112990246`):用于部署 Docker 容器及缓存模型权重。
80
+ - **reSpeaker 4 麦克风阵列** (SKU: `107990193`):若需额外实现语音呼救感知功能。
81
+
82
+ > 收到设备并连接网线/电源后,只需对我说“设备已连接,开始部署”,我将直接为您自动化部署落地!
83
+ ```