@seeed-studio/solution-cli 0.0.0-stage → 0.1.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,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,78 @@
1
+ # Deploy where there is no internet (offline package)
2
+
3
+ Prepare on a computer that has internet, deploy on site without it. The unit
4
+ is **step × target**: you can prepare one step only, or several device models
5
+ for the same step. The engine has no separate command for this; use
6
+ `sensecraft-engine api` (run `sensecraft-engine api --help` for the same notes).
7
+
8
+ Ask the user first: preparing downloads container images and files, which can
9
+ take several GB of disk space.
10
+
11
+ ## 1. Plan: what is needed, what is already here
12
+
13
+ Write the request to a file, e.g. `./.sensecraft-work/plan.json`:
14
+
15
+ ```json
16
+ {
17
+ "solution_id": "<id>",
18
+ "preset_id": "<preset>",
19
+ "targets": {"<step_id>": "<target_id>"},
20
+ "arch": {"<step_id>": "aarch64"},
21
+ "params": {"username": "<the username the deploy will use>"},
22
+ "lang": "en"
23
+ }
24
+ ```
25
+
26
+ - `targets`: only the steps listed are prepared; leave it out for every step.
27
+ - `arch`: needed when the target names no device type (`aarch64` for
28
+ ARM boards such as Jetson, Raspberry Pi, RK3588).
29
+ - `params`: **the same connection values the later deploy will use** (from
30
+ `deploy-info`'s `request_template`, e.g. `username`). The package records
31
+ them; a deploy with different values is refused before anything runs on the
32
+ device (`the offline package was prepared with different values: ...`), and
33
+ you have to plan and prepare again with the right `params`.
34
+
35
+ ```
36
+ sensecraft-engine api POST /api/staging/plan --data "$(cat ./.sensecraft-work/plan.json)"
37
+ ```
38
+
39
+ The response lists each item and whether it is already here. Items marked
40
+ `partial` still need internet on site (for example a model the container
41
+ downloads at start); tell the user which.
42
+
43
+ ## 2. Prepare: download it here
44
+
45
+ ```
46
+ sensecraft-engine api POST /api/staging/prepare --data "$(cat ./.sensecraft-work/plan.json)"
47
+ sensecraft-engine api GET /api/staging/jobs/<id> # repeat every 5–10 s until status is not "running"
48
+ ```
49
+
50
+ `prepare` returns a job with `id` and `status: running`. Cancel it with
51
+ `sensecraft-engine api POST /api/staging/jobs/<id>/cancel`.
52
+
53
+ ## 3. Check, carry, clean up
54
+
55
+ ```
56
+ sensecraft-engine api GET /api/staging/entries # what is prepared
57
+ sensecraft-engine api POST /api/staging/export --data '{"path": "/path/kit.tar"}' # pack it
58
+ sensecraft-engine api POST /api/staging/import --data '{"path": "/path/kit.tar"}' # on the deploying computer
59
+ ```
60
+
61
+ Deleting prepared entries (`POST /api/staging/entries/delete`) frees disk space;
62
+ ask the user first.
63
+
64
+ ## 4. Deploy on site
65
+
66
+ Deploy as in `deploy.md`, with `"force_offline": true` in the step's
67
+ connection:
68
+
69
+ ```json
70
+ "device_connections": {
71
+ "<step_id>": {"target": "<target_id>", "target_type": "remote",
72
+ "host": "<ip>", "username": "<same as params>",
73
+ "password": "<password>", "force_offline": true}
74
+ }
75
+ ```
76
+
77
+ The engine checks the offline package before connecting to the device and
78
+ stops with a list of what is missing.
@@ -0,0 +1,136 @@
1
+ # Finish a `verify_required` step
2
+
3
+ The engine does not run verification steps that need a person or a browser.
4
+ When a deployment reaches one, it writes a `verify_required` event to the
5
+ deployment's log, marks that step `step_skipped` (`reason: verify_required`)
6
+ and carries on. Read the events with
7
+ `sensecraft-engine logs <deployment_id> --since <N>`.
8
+
9
+ Fields every `verify_required` event has:
10
+
11
+ | Field | Content |
12
+ |---|---|
13
+ | `device_id` / `step_id` | The step |
14
+ | `kind` | The step type (`web_dashboard`, `serial_camera`, `serial_wizard`, `robot_inspect`, `preview`, `image_predict`, ...) |
15
+ | `name`, `required` | Display name; whether the solution counts it as required |
16
+ | `url`, `endpoint` | Where to check, with `{{host}}` already filled in (may be null) |
17
+ | `sample_request` | Request to send to `endpoint`: `{method, url, headers?, content_type?, file_field?, body?, curl}`, `curl` being a ready command line (may be null) |
18
+ | `mode` | Steps with several check modes: the mode the event describes (the step's default) |
19
+ | `instructions` | What to check, in the solution's words |
20
+ | `expected_status` | `web_dashboard` only: 200 |
21
+
22
+ Steps for hardware sessions add `port` / `db_port` / `host` (filled from the
23
+ step that flashed or deployed the device; null when the connection did not
24
+ include it), `commands {...}` with the exact CLI commands, and `confirm`
25
+ (how to tell it worked). `deploy-info` lists the same commands in advance as
26
+ `counting_cli`, `face_cli`, `wizard_cli` or `robot_cli` on that step.
27
+
28
+ General rules:
29
+
30
+ - Replace `<PORT>`, `<DB_PORT>`, `<HOST>` in `commands` with the event's `port`,
31
+ `db_port`, `host`. If one is null, take it from the connection you gave the
32
+ step named in `port_from_device` / `db_port_from_device` /
33
+ `host_from_device`, or ask the user (`sensecraft-engine device ports` lists
34
+ serial ports).
35
+ - Anything a person must do with their hands — plugging a cable, connecting a
36
+ servo, sitting in front of a camera, walking past it, standing clear of a
37
+ robot arm — is the user's job. Say exactly what to do, then **wait for the
38
+ user to confirm** before running the next command.
39
+ - These commands print a JSON result on stdout. Exit codes: 0 confirmed by the
40
+ device; 1 the device did not confirm (timeout, error, read-back mismatch);
41
+ 2 bad arguments; 3 port busy or cannot be opened, or (robot) the status
42
+ service is unreachable; 5 internal error. On 3 with `port_busy`, another
43
+ deployment or session holds the port: wait for it, do not force it.
44
+ - At most 3 rounds per problem, as in `SKILL.md`.
45
+
46
+ ## 1. Dashboards and interactive checks (`web_dashboard`, `preview`, `image_predict`, `text_chat`, `voice_chat`, ...)
47
+
48
+ - `web_dashboard`: `curl -s -o /dev/null -w "%{http_code}" <url>` must return
49
+ `expected_status` (200). Then give the user the `url` to open.
50
+ - `endpoint` + `sample_request` present: run `sample_request.curl` and show
51
+ the user the relevant part of the response. In a multipart request
52
+ (`-F <file_field>=@IMAGE.jpg`) replace `IMAGE.jpg` with a real picture
53
+ (a photo the user gave you, or one downloaded into `./.sensecraft-work/`);
54
+ a JSON response with results (for example detections with labels and
55
+ scores, possibly an empty list for a picture without matching objects)
56
+ means the service works. An HTTP error or no answer goes to `diagnose.md`.
57
+ These events also come for verify steps that only describe a check in the
58
+ guide (no separate config file), with the default mode's endpoint.
59
+ - Otherwise follow `instructions`. When they say to run the check in the
60
+ SenseCraft Solution app, tell the user: that check needs the desktop app.
61
+
62
+ ## 2. People counting lines (`serial_camera` with `counting_cli`)
63
+
64
+ 1. Ask the user where the counting lines / zones should be, in camera
65
+ coordinates 0–1000 (0,0 top left, 1000,1000 bottom right): a line is two
66
+ points `X1,Y1,X2,Y2`, a zone four corners. Direction: crossing from the
67
+ right side of the line (looking from point 1 to point 2) to the left counts
68
+ as `in`; swap the two points to reverse it.
69
+ 2. Write them:
70
+ `sensecraft-engine counting set --port <PORT> --line 0=100,500,900,500 [--roi 1=X1,Y1,...,X4,Y4] [--anchor-mode foot]`
71
+ (`foot` for a camera tilted down from above, `center` otherwise). The result
72
+ must have `verified: true`; `mismatches[]` lists slots that read back
73
+ differently.
74
+ 3. Start counting from zero and keep the camera running:
75
+ `sensecraft-engine counting reset --port <PORT> --keep-running`.
76
+ 4. Ask the user to walk across each line (and into each zone) and tell you when
77
+ done.
78
+ 5. `sensecraft-engine counting get --port <PORT>`: each line's `in` / `out` and
79
+ each zone's `cur` / `entered` must have changed as expected. Show the user.
80
+
81
+ ## 3. Face enrollment (`serial_camera` with `face_cli`, SenseCAP Watcher)
82
+
83
+ The Watcher has two USB serial ports: the camera (`--port`) and the face
84
+ database (`--db-port`).
85
+
86
+ 1. Ask the user whose face to enroll (name: ASCII up to 31 characters, Chinese
87
+ up to 4 characters).
88
+ 2. Ask that person to sit in front of the camera, facing it, and to tell you
89
+ when ready.
90
+ 3. `sensecraft-engine face enroll --port <PORT> --db-port <DB_PORT> --name <NAME> --no-prompt`.
91
+ Success: `stored: true`, `verified: true`. Too few samples
92
+ (`samples_collected` below the minimum): ask for better light / a closer,
93
+ straight-on face, then run it again.
94
+ 4. `sensecraft-engine face list --db-port <DB_PORT>` and show the names to the
95
+ user. Fix mistakes with `face rename --db-port <DB_PORT> --name OLD --new-name NEW`
96
+ or `face delete --db-port <DB_PORT> --name NAME` (deleting needs the user's OK).
97
+ 5. Ask an enrolled person to stand in front of the camera and confirm the
98
+ device recognizes them by name.
99
+
100
+ ## 4. Servo ID setup (`serial_wizard` with `wizard_cli`)
101
+
102
+ The firmware sets one servo ID at a time and prints prompts.
103
+
104
+ 1. Tell the user to connect **only** the servo for the current ID (the
105
+ event's `instructions` name them, e.g. ID 1 front wheel, 2 left rear, 3 right
106
+ rear) and to confirm.
107
+ 2. `sensecraft-engine wizard action --port <PORT> --name enter`. Pass the
108
+ firmware's `prompt` text on to the user.
109
+ 3. `step_error` set: ask the user to check the wiring, then
110
+ `sensecraft-engine wizard action --port <PORT> --name retry`.
111
+ 4. Repeat for each servo until the result has `step_done: true`
112
+ (`wizard read --port <PORT> --until done` waits for it). Then ask the user
113
+ to power off, connect all servos and power on again.
114
+
115
+ ## 5. Robot arm check (`robot_inspect` with `robot_cli`)
116
+
117
+ Commands that move the arm need the user's OK each time.
118
+
119
+ 1. `sensecraft-engine robot observe --host <HOST>`: every joint has a value.
120
+ 2. Ask the user's OK, then `robot torque off --host <HOST>`; ask the user to
121
+ turn one joint by hand; `robot observe` again — the reading must follow.
122
+ Show the user the numbers.
123
+ 3. Recording a motion: for each pose the user sets by hand, run `robot observe`
124
+ and add the `observation` to a frame file
125
+ (`[{"joints": {...}, "delay": 0.4}, ...]`).
126
+ 4. Before `robot actions test --host <HOST> --frames <file>`, ask the user to
127
+ stand clear of the arm and confirm. Afterwards ask whether the motion was
128
+ right.
129
+ 5. Only after the user approves it:
130
+ `robot actions save --host <HOST> --name <name> --frames <file>`.
131
+ `robot actions cancel --host <HOST>` stops a running motion.
132
+
133
+ ## 6. Report
134
+
135
+ For each verify step tell the user: what you checked, the command result
136
+ (the relevant fields, not the whole JSON), and anything left for them to do.