@seeed-studio/solution-cli 0.3.0 → 0.3.2
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/README.md +35 -15
- package/bin/cli.js +104 -38
- package/bin/runner.js +1 -1
- package/catalog/knowledge-core.json +666 -144
- package/docs/cli-guide.md +58 -17
- package/lib/agent.js +5 -5
- package/lib/detector.js +3 -3
- package/lib/downloader.js +85 -21
- package/lib/installer.js +88 -53
- package/lib/knowledge.js +141 -17
- package/lib/ledger.js +1 -1
- package/package.json +3 -6
- package/skills/{sensecraft → solution-cli}/SKILL.md +40 -14
- package/skills/{sensecraft → solution-cli}/references/consult-and-select.md +4 -0
- package/skills/{sensecraft → solution-cli}/references/device-management.md +45 -13
- package/skills/{sensecraft → solution-cli}/references/diagnose.md +1 -1
- package/skills/{sensecraft → solution-cli}/references/secondary-development.md +2 -5
- /package/skills/{sensecraft → solution-cli}/references/app-only.md +0 -0
- /package/skills/{sensecraft → solution-cli}/references/deploy.md +0 -0
- /package/skills/{sensecraft → solution-cli}/references/error-catalog.md +0 -0
- /package/skills/{sensecraft → solution-cli}/references/hardware-bom.md +0 -0
- /package/skills/{sensecraft → solution-cli}/references/offline.md +0 -0
- /package/skills/{sensecraft → solution-cli}/references/verify.md +0 -0
package/lib/knowledge.js
CHANGED
|
@@ -144,8 +144,12 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
144
144
|
const core = loadKnowledgeCore();
|
|
145
145
|
const onlinePromises = [];
|
|
146
146
|
|
|
147
|
+
const locKey = locale === 'zh' ? 'zh_hans' : locale;
|
|
148
|
+
const fetchLimit = Math.max(limit * 5, 30);
|
|
149
|
+
|
|
147
150
|
if (keySolution) {
|
|
148
|
-
const
|
|
151
|
+
const solFilter = 'published:!=false';
|
|
152
|
+
const solUrl = `${host}/collections/Solution/documents/search?q=${encodeURIComponent(query)}&query_by=title_zh_hans,title_en,excerpt_zh_hans,excerpt_en,display_tags_zh_hans&filter_by=${encodeURIComponent(solFilter)}&per_page=${fetchLimit}`;
|
|
149
153
|
onlinePromises.push(
|
|
150
154
|
fetchJson(solUrl, { 'X-TYPESENSE-API-KEY': keySolution }, 2500)
|
|
151
155
|
.then((data) => ({ type: 'scenario', hits: data?.hits || [] }))
|
|
@@ -154,7 +158,8 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
if (keyRefDesign) {
|
|
157
|
-
const
|
|
161
|
+
const rdFilter = 'published:!=false';
|
|
162
|
+
const rdUrl = `${host}/collections/reference_design/documents/search?q=${encodeURIComponent(query)}&query_by=name_zh_hans,name_en,summary_zh_hans,summary_en,tags&filter_by=${encodeURIComponent(rdFilter)}&per_page=${fetchLimit}`;
|
|
158
163
|
onlinePromises.push(
|
|
159
164
|
fetchJson(rdUrl, { 'X-TYPESENSE-API-KEY': keyRefDesign }, 2500)
|
|
160
165
|
.then((data) => ({ type: 'reference_design', hits: data?.hits || [] }))
|
|
@@ -163,13 +168,19 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
163
168
|
}
|
|
164
169
|
|
|
165
170
|
const results = await Promise.all(onlinePromises);
|
|
166
|
-
const
|
|
171
|
+
const onlineScenarios = [];
|
|
172
|
+
const onlineRefDesigns = [];
|
|
167
173
|
|
|
168
174
|
for (const res of results) {
|
|
169
175
|
if (res.type === 'scenario') {
|
|
170
176
|
for (const hit of res.hits) {
|
|
171
177
|
const doc = hit.document || {};
|
|
178
|
+
// Filter unreleased or staging scenarios from production recommendations
|
|
179
|
+
if (doc.published === false || doc.visibility === 'staging' || doc.visibility === 'draft') continue;
|
|
180
|
+
if (doc[`visibility_${locKey}`] === 'staging' || doc[`visibility_${locKey}`] === 'draft') continue;
|
|
181
|
+
|
|
172
182
|
const docId = doc.id || doc.slug;
|
|
183
|
+
const docType = doc.doc_type || 'scenario';
|
|
173
184
|
const title = locale === 'en'
|
|
174
185
|
? (doc.title_en || doc.title_zh_hans || docId)
|
|
175
186
|
: (doc.title_zh_hans || doc.title_en || docId);
|
|
@@ -178,22 +189,23 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
178
189
|
: (doc.excerpt_zh_hans || doc.seo_description_zh_hans || doc.excerpt_en || '');
|
|
179
190
|
const tags = (locale === 'en' ? doc.display_tags_en : doc.display_tags_zh_hans) || doc.display_tags || [];
|
|
180
191
|
const pb = core.diagnostic_playbooks?.[docId] || doc.diagnostic_playbook || null;
|
|
192
|
+
const rawLanding = doc.external_url || doc.landing_url || `https://www.seeedstudio.com/solutions/${docId}`;
|
|
181
193
|
|
|
182
|
-
|
|
183
|
-
type:
|
|
194
|
+
onlineScenarios.push({
|
|
195
|
+
type: docType,
|
|
184
196
|
id: docId,
|
|
185
197
|
title,
|
|
186
198
|
summary,
|
|
187
199
|
tags,
|
|
188
200
|
family_ids: doc.family_ids || [],
|
|
189
201
|
reference_designs: doc.reference_designs || [],
|
|
190
|
-
landing_url: appendUtm(
|
|
202
|
+
landing_url: appendUtm(rawLanding, {
|
|
191
203
|
source: 'sensecraft_agent',
|
|
192
204
|
medium: 'solution_consult',
|
|
193
205
|
campaign: docId,
|
|
194
|
-
content:
|
|
206
|
+
content: `${docType}_landing`,
|
|
195
207
|
}),
|
|
196
|
-
repo_url: '',
|
|
208
|
+
repo_url: doc.repo_url || '',
|
|
197
209
|
diagnostic_playbook: pb,
|
|
198
210
|
source: 'typesense',
|
|
199
211
|
});
|
|
@@ -201,6 +213,10 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
201
213
|
} else if (res.type === 'reference_design') {
|
|
202
214
|
for (const hit of res.hits) {
|
|
203
215
|
const doc = hit.document || {};
|
|
216
|
+
// Filter unreleased or staging reference designs from production recommendations
|
|
217
|
+
if (doc.published === false || doc.visibility === 'staging' || doc.visibility === 'draft') continue;
|
|
218
|
+
const locKey = locale === 'zh' ? 'zh_hans' : locale;
|
|
219
|
+
if (doc[`visibility_${locKey}`] === 'staging' || doc[`visibility_${locKey}`] === 'draft') continue;
|
|
204
220
|
const docId = doc.id;
|
|
205
221
|
const title = locale === 'en'
|
|
206
222
|
? (doc.name_en || doc.name_zh_hans || docId)
|
|
@@ -209,7 +225,7 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
209
225
|
? (doc.summary_en || doc.tagline_en || doc.summary_zh_hans || '')
|
|
210
226
|
: (doc.summary_zh_hans || doc.tagline_zh_hans || doc.summary_en || '');
|
|
211
227
|
|
|
212
|
-
let pb = doc.diagnostic_playbook || null;
|
|
228
|
+
let pb = doc.diagnostic_playbook || core.diagnostic_playbooks?.[docId] || null;
|
|
213
229
|
if (!pb) {
|
|
214
230
|
for (const sc of core.scenarios || []) {
|
|
215
231
|
if ((sc.reference_designs || []).includes(docId)) {
|
|
@@ -219,12 +235,24 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
219
235
|
}
|
|
220
236
|
}
|
|
221
237
|
|
|
222
|
-
|
|
238
|
+
const rawPresets = doc.presets || [];
|
|
239
|
+
const presets = rawPresets.map((p) => {
|
|
240
|
+
if (typeof p === 'string') return p;
|
|
241
|
+
if (p && typeof p === 'object') {
|
|
242
|
+
return (locale === 'en' ? (p.name_en || p.name || p.id) : (p.name_zh_hans || p.name || p.id)) || p.id || '';
|
|
243
|
+
}
|
|
244
|
+
return String(p || '');
|
|
245
|
+
}).filter(Boolean);
|
|
246
|
+
|
|
247
|
+
const bundledRd = (core.reference_designs || []).find((r) => r.id === docId);
|
|
248
|
+
const repoUrl = doc.repo_url || bundledRd?.repo_url || '';
|
|
249
|
+
|
|
250
|
+
onlineRefDesigns.push({
|
|
223
251
|
type: 'reference_design',
|
|
224
252
|
id: docId,
|
|
225
253
|
title,
|
|
226
254
|
summary,
|
|
227
|
-
presets
|
|
255
|
+
presets,
|
|
228
256
|
landing_url: appendUtm(`https://www.seeedstudio.com/solutions/reference-designs/${docId}`, {
|
|
229
257
|
source: 'sensecraft_agent',
|
|
230
258
|
medium: 'solution_consult',
|
|
@@ -236,7 +264,7 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
236
264
|
medium: 'tech_wiki',
|
|
237
265
|
campaign: docId,
|
|
238
266
|
}) : '',
|
|
239
|
-
repo_url:
|
|
267
|
+
repo_url: repoUrl,
|
|
240
268
|
diagnostic_playbook: pb,
|
|
241
269
|
source: 'typesense',
|
|
242
270
|
});
|
|
@@ -244,11 +272,90 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
244
272
|
}
|
|
245
273
|
}
|
|
246
274
|
|
|
247
|
-
|
|
275
|
+
// If one collection returned results but the other was unavailable/failed/empty, supplement from bundled catalog
|
|
276
|
+
if ((onlineScenarios.length > 0 && onlineRefDesigns.length === 0) || (onlineRefDesigns.length > 0 && onlineScenarios.length === 0)) {
|
|
277
|
+
const tokens = qLower.split(/\s+/).filter(Boolean);
|
|
278
|
+
if (onlineScenarios.length === 0) {
|
|
279
|
+
for (const s of core.scenarios || []) {
|
|
280
|
+
if (s.published === false || s.visibility === 'staging' || s.visibility === 'draft') continue;
|
|
281
|
+
const locKey = locale === 'zh' ? 'zh_hans' : locale;
|
|
282
|
+
if (s[`visibility_${locKey}`] === 'staging' || s[`visibility_${locKey}`] === 'draft') continue;
|
|
283
|
+
|
|
284
|
+
const haystack = [s.id, s.title?.zh, s.title?.en, s.subtitle?.zh, s.subtitle?.en, s.summary, ...(s.tags || [])].join(' ').toLowerCase();
|
|
285
|
+
const score = tokens.reduce((acc, t) => (haystack.includes(t) ? acc + 1 : acc), 0);
|
|
286
|
+
if (tokens.length === 0 || score > 0) {
|
|
287
|
+
onlineScenarios.push({
|
|
288
|
+
type: 'scenario',
|
|
289
|
+
id: s.id,
|
|
290
|
+
title: s.title?.[locale] || s.title?.zh || s.title?.en || s.id,
|
|
291
|
+
summary: s.subtitle?.[locale] || s.subtitle?.zh || s.summary || '',
|
|
292
|
+
tags: s.tags || [],
|
|
293
|
+
family_ids: s.family_ids || [],
|
|
294
|
+
reference_designs: s.reference_designs || [],
|
|
295
|
+
landing_url: appendUtm(s.landing_url || `https://www.seeedstudio.com/solutions/${s.id}`, {
|
|
296
|
+
source: 'sensecraft_agent',
|
|
297
|
+
medium: 'solution_consult',
|
|
298
|
+
campaign: s.id,
|
|
299
|
+
content: 'scenario_landing',
|
|
300
|
+
}),
|
|
301
|
+
repo_url: s.repo_url || '',
|
|
302
|
+
diagnostic_playbook: s.diagnostic_playbook || null,
|
|
303
|
+
source: 'bundled_manifest',
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (onlineRefDesigns.length === 0) {
|
|
309
|
+
for (const rd of core.reference_designs || []) {
|
|
310
|
+
if (rd.published === false || rd.visibility === 'staging' || rd.visibility === 'draft') continue;
|
|
311
|
+
const locKey = locale === 'zh' ? 'zh_hans' : locale;
|
|
312
|
+
if (rd[`visibility_${locKey}`] === 'staging' || rd[`visibility_${locKey}`] === 'draft') continue;
|
|
313
|
+
|
|
314
|
+
const haystack = [rd.id, rd.title?.zh, rd.title?.en, rd.summary?.zh, rd.summary?.en, ...(rd.presets || [])].join(' ').toLowerCase();
|
|
315
|
+
const score = tokens.reduce((acc, t) => (haystack.includes(t) ? acc + 1 : acc), 0);
|
|
316
|
+
if (tokens.length === 0 || score > 0) {
|
|
317
|
+
onlineRefDesigns.push({
|
|
318
|
+
type: 'reference_design',
|
|
319
|
+
id: rd.id,
|
|
320
|
+
title: rd.title?.[locale] || rd.title?.zh || rd.title?.en || rd.id,
|
|
321
|
+
summary: rd.summary?.[locale] || rd.summary?.zh || rd.summary?.en || '',
|
|
322
|
+
presets: rd.presets || [],
|
|
323
|
+
landing_url: appendUtm(rd.landing_url || `https://www.seeedstudio.com/solutions/reference-designs/${rd.id}`, {
|
|
324
|
+
source: 'sensecraft_agent',
|
|
325
|
+
medium: 'solution_consult',
|
|
326
|
+
campaign: rd.id,
|
|
327
|
+
content: 'solution_landing',
|
|
328
|
+
}),
|
|
329
|
+
wiki_url: rd.wiki_url ? appendUtm(rd.wiki_url, {
|
|
330
|
+
source: 'sensecraft_agent',
|
|
331
|
+
medium: 'tech_wiki',
|
|
332
|
+
campaign: rd.id,
|
|
333
|
+
}) : '',
|
|
334
|
+
repo_url: rd.repo_url || '',
|
|
335
|
+
diagnostic_playbook: rd.diagnostic_playbook || null,
|
|
336
|
+
source: 'bundled_manifest',
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const mergedRecords = [];
|
|
344
|
+
const maxOnline = Math.max(onlineScenarios.length, onlineRefDesigns.length);
|
|
345
|
+
for (let i = 0; i < maxOnline && mergedRecords.length < limit; i++) {
|
|
346
|
+
if (i < onlineScenarios.length && mergedRecords.length < limit) {
|
|
347
|
+
mergedRecords.push(onlineScenarios[i]);
|
|
348
|
+
}
|
|
349
|
+
if (i < onlineRefDesigns.length && mergedRecords.length < limit) {
|
|
350
|
+
mergedRecords.push(onlineRefDesigns[i]);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (mergedRecords.length > 0) {
|
|
248
355
|
return createCatalogDto({
|
|
249
|
-
source: 'typesense',
|
|
356
|
+
source: (onlineScenarios.some(r => r.source === 'typesense') || onlineRefDesigns.some(r => r.source === 'typesense')) ? 'typesense' : 'bundled_manifest',
|
|
250
357
|
freshness: 'FRESH',
|
|
251
|
-
records:
|
|
358
|
+
records: mergedRecords,
|
|
252
359
|
});
|
|
253
360
|
}
|
|
254
361
|
} catch {
|
|
@@ -264,6 +371,10 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
264
371
|
const tokens = qLower.split(/\s+/).filter(Boolean);
|
|
265
372
|
|
|
266
373
|
for (const s of core.scenarios || []) {
|
|
374
|
+
if (s.published === false || s.visibility === 'staging' || s.visibility === 'draft') continue;
|
|
375
|
+
const locKey = locale === 'zh' ? 'zh_hans' : locale;
|
|
376
|
+
if (s[`visibility_${locKey}`] === 'staging' || s[`visibility_${locKey}`] === 'draft') continue;
|
|
377
|
+
|
|
267
378
|
const haystack = [
|
|
268
379
|
s.id,
|
|
269
380
|
s.title?.zh,
|
|
@@ -298,6 +409,10 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
298
409
|
}
|
|
299
410
|
|
|
300
411
|
for (const rd of core.reference_designs || []) {
|
|
412
|
+
if (rd.published === false || rd.visibility === 'staging' || rd.visibility === 'draft') continue;
|
|
413
|
+
const locKey = locale === 'zh' ? 'zh_hans' : locale;
|
|
414
|
+
if (rd[`visibility_${locKey}`] === 'staging' || rd[`visibility_${locKey}`] === 'draft') continue;
|
|
415
|
+
|
|
301
416
|
const haystack = [
|
|
302
417
|
rd.id,
|
|
303
418
|
rd.title?.zh,
|
|
@@ -326,7 +441,7 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
326
441
|
medium: 'tech_wiki',
|
|
327
442
|
campaign: rd.id,
|
|
328
443
|
}) : '',
|
|
329
|
-
repo_url: rd.repo_url ||
|
|
444
|
+
repo_url: rd.repo_url || '',
|
|
330
445
|
diagnostic_playbook: rd.diagnostic_playbook || null,
|
|
331
446
|
score,
|
|
332
447
|
});
|
|
@@ -337,7 +452,16 @@ export async function searchHub({ query = '', locale = 'zh', limit = 5 } = {}) {
|
|
|
337
452
|
matchedScenarios.sort((a, b) => b.score - a.score);
|
|
338
453
|
matchedRefDesigns.sort((a, b) => b.score - a.score);
|
|
339
454
|
|
|
340
|
-
const combined = [
|
|
455
|
+
const combined = [];
|
|
456
|
+
const maxOffline = Math.max(matchedScenarios.length, matchedRefDesigns.length);
|
|
457
|
+
for (let i = 0; i < maxOffline && combined.length < limit; i++) {
|
|
458
|
+
if (i < matchedScenarios.length && combined.length < limit) {
|
|
459
|
+
combined.push(matchedScenarios[i]);
|
|
460
|
+
}
|
|
461
|
+
if (i < matchedRefDesigns.length && combined.length < limit) {
|
|
462
|
+
combined.push(matchedRefDesigns[i]);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
341
465
|
|
|
342
466
|
return createCatalogDto({
|
|
343
467
|
source: 'bundled_manifest',
|
package/lib/ledger.js
CHANGED
|
@@ -246,7 +246,7 @@ export function removeInstallation(agentId, options = {}) {
|
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
// Remove marker file
|
|
249
|
-
const marker = path.join(targetDir, '.installed-by-
|
|
249
|
+
const marker = path.join(targetDir, '.installed-by-solution-cli');
|
|
250
250
|
if (fs.existsSync(marker) && isSafePathInside(marker, targetDir)) {
|
|
251
251
|
actions.push({ file: marker, action: 'delete_marker' });
|
|
252
252
|
if (!dryRun) {
|
package/package.json
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seeed-studio/solution-cli",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.2",
|
|
4
|
+
"description": "Installer for the Rust deployment engine and Solution CLI Agent skills (Work Buddy, Claude Code, Codex, CodeBuddy)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/installer.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"solution-cli": "bin/cli.js",
|
|
9
|
-
"sensecraft-
|
|
10
|
-
"sensecraft": "bin/cli.js",
|
|
11
|
-
"sensecraft-engine": "bin/runner.js",
|
|
12
|
-
"sensecraft-skill": "bin/cli.js"
|
|
9
|
+
"sensecraft-engine": "bin/runner.js"
|
|
13
10
|
},
|
|
14
11
|
"publishConfig": {
|
|
15
12
|
"access": "public"
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description:
|
|
2
|
+
name: solution-cli
|
|
3
|
+
description: 用于客户询盘、客户需求、方案选型、我们能提供什么、怎么回复客户等边缘/IoT 方案咨询,涵盖智慧农业、跌倒检测、楼宇温控、恒温器、传感器及相关硬件。Use for customer inquiries, requirements analysis, solution selection, proposal guidance, edge IoT deployments, hardware compatibility, deployment, and troubleshooting. First search the SenseCraft solution catalog with `solution-cli`; do not trigger for general business writing, generic proposal drafting, or unrelated coding.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Solution CLI
|
|
7
7
|
|
|
8
|
-
This skill drives the `sensecraft-engine` execution engine and `solution-cli` toolset. It operates across the complete lifecycle: **Solution Consultation ➔ Hardware & Accessory BOM Selection ➔ Local Deployment ➔ Self-Healing Diagnostics**.
|
|
8
|
+
This skill drives the `sensecraft-engine` execution engine and `solution-cli` toolset. It operates across the complete lifecycle: **Solution Consultation ➔ Hardware & Accessory BOM Selection ➔ Local Deployment ➔ Self-Healing Diagnostics**. For Engine-only operations, use the injected absolute Engine path. If the wrapper is not on `PATH`, invoke the CLI as `npx -y --package=@seeed-studio/solution-cli@latest solution-cli <args>`. Keep catalog queries such as `hub` and `product` on `solution-cli`; do not forward them to the Engine.
|
|
9
9
|
|
|
10
10
|
## Workflow Routing & Progressive Disclosure
|
|
11
11
|
|
|
@@ -16,7 +16,7 @@ Do NOT read all reference files at once. Determine the user's workflow state and
|
|
|
16
16
|
| **DISCOVERY / CONSULT**<br>“我想做防跌倒监控”、“有智慧农场方案吗” | `references/consult-and-select.md` | `solution-cli hub search "<keyword>" --json` | **DO NOT run deploy**; user is exploring scenarios and has not confirmed hardware. |
|
|
17
17
|
| **PROCUREMENT / VERIFY_BOM**<br>“买什么硬件?”、“J4012 需要配什么配件?” | `references/hardware-bom.md` | `solution-cli product accessories <id> --json` | **DO NOT fabricate** third-party SKUs or prices. Always distinguish REQUIRED vs OPTIONAL accessories. |
|
|
18
18
|
| **ONBOARDING / PRECHECK**<br>“设备到了,插上了网线/USB”、“环境自检” | `references/diagnose.md` | `solution-cli doctor` / `sensecraft-engine device scan` | Freeze hardware requirements into DeploymentLockfile before proceeding to apply. |
|
|
19
|
-
| **DEPLOYING / APPLY**<br>“开始安装”、“部署 fall_detection” | `references/deploy.md` | `sensecraft-engine deploy
|
|
19
|
+
| **DEPLOYING / APPLY**<br>“开始安装”、“部署 fall_detection” | `references/deploy.md` | `sensecraft-engine deploy <id>` | **DEPLOYMENT CRITICAL SECTION**: Zero external commercial queries allowed during apply. |
|
|
20
20
|
| **MANAGE / RECONFIG**<br>“改一下 MQTT 地址”、“查看已安装应用”、“重启服务”、“修改配置” | `references/device-management.md` | `curl BASE/api/docker-devices/local/config/<id>` | **Check before write**: Only alter `reconfigurable: true` fields; check current value before modifying. |
|
|
21
21
|
| **CUSTOMIZE / SECONDARY_DEV**<br>“我想改源码”、“换成自研模型”、“改前端看板”、“GitHub 仓库” | `references/secondary-development.md` | `git clone <repo_url>` / `sensecraft-engine --solutions-dir` | **Never edit cached content**: Clone to user workspace, pair-program, test locally with `--solutions-dir`. |
|
|
22
22
|
| **TROUBLESHOOTING**<br>“部署报错”、“黑屏”、“无视频流” | `references/error-catalog.md` | `sensecraft-engine logs` / `solution-cli product verify-compatibility` | Max 3 fix-and-retry rounds. Follow local auto-patching rule. |
|
|
@@ -78,7 +78,7 @@ daemon is a host prerequisite, not evidence that a deployment has passed.
|
|
|
78
78
|
you checked, what you changed). Transient registry or network errors follow
|
|
79
79
|
the backoff rule in `references/deploy.md` §6 and do not use up a round.
|
|
80
80
|
4. **Preserving official cache & Local auto-patching**: Never edit files directly under the official content cache directory (`~/.sensecraft/content/`). When fixing a solution-level bug (timeout too short, wrong port/env var, minor syntax/transfer glitch, broken health check):
|
|
81
|
-
- In CLI / Engine mode: copy the solution from `~/.sensecraft/content/solutions/<id>` into `./.sensecraft-work/solutions/<id>`, apply the minimal fix in that copy.
|
|
81
|
+
- In CLI / Engine mode: copy the solution from `~/.sensecraft/content/solutions/<id>` into `./.sensecraft-work/solutions/<id>`, apply the minimal fix in that copy. Then re-run deployment with `sensecraft-engine deploy <id> --solutions-dir ./.sensecraft-work/solutions` so the engine picks up the patched files. Show the user the patch diff.
|
|
82
82
|
- If running with the Desktop App: use the app's edit copy mechanism (`references/app-only.md` §4).
|
|
83
83
|
5. **Reading output:** read the CLI's JSON output directly; it is small. If a
|
|
84
84
|
filter is needed, use `jq` only when it is already installed. Do not start
|
|
@@ -103,7 +103,7 @@ daemon is a host prerequisite, not evidence that a deployment has passed.
|
|
|
103
103
|
asset reuse, power/cabling, inspection speed/materials) to help the user converge
|
|
104
104
|
on the optimal hardware architecture and budget.
|
|
105
105
|
|
|
106
|
-
## Step 0 — Install the engine and the solutions (
|
|
106
|
+
## Step 0 — Install the engine and the solutions (when needed)
|
|
107
107
|
|
|
108
108
|
1. Check whether it is installed: `sensecraft-engine --version`. If the command
|
|
109
109
|
is not found, also try `~/.sensecraft/bin/sensecraft-engine --version`
|
|
@@ -121,14 +121,41 @@ daemon is a host prerequisite, not evidence that a deployment has passed.
|
|
|
121
121
|
command and stop; do not install packages or start another interpreter.
|
|
122
122
|
`minisign` is required to verify the signed release manifest; Unix `jq` is
|
|
123
123
|
required by the shell installer to read it.
|
|
124
|
-
3.
|
|
125
|
-
it downloads a program)
|
|
124
|
+
3. If it is not installed, install it together with the solutions (ask the user
|
|
125
|
+
first, it downloads a program). If the user explicitly asks to upgrade or
|
|
126
|
+
reinstall, use the same solution-cli command; do not reinstall on every
|
|
127
|
+
deployment:
|
|
126
128
|
|
|
127
|
-
**Recommended (Zero-friction via solution-cli,
|
|
129
|
+
**Recommended (Zero-friction via solution-cli, target the current Agent explicitly)**:
|
|
128
130
|
```bash
|
|
129
|
-
npx @seeed-studio/solution-cli install -y
|
|
131
|
+
npx -y @seeed-studio/solution-cli@latest install --agent <current-agent-id> -y
|
|
130
132
|
```
|
|
131
133
|
|
|
134
|
+
Fill `<current-agent-id>` with the actual host ID: `workbuddy`, `claude`,
|
|
135
|
+
`codex`, `codebuddy`, or `antigravity`. The WorkBuddy example is:
|
|
136
|
+
```bash
|
|
137
|
+
npx -y @seeed-studio/solution-cli@latest install --agent workbuddy -y
|
|
138
|
+
```
|
|
139
|
+
An AI agent must use its current host ID; Codex must not hardcode WorkBuddy.
|
|
140
|
+
Use `npx -y @seeed-studio/solution-cli@latest install` for interactive
|
|
141
|
+
selection. When one or more Agents are detected, pressing Enter selects all
|
|
142
|
+
detected Agents; entering the matching number selects one host. In
|
|
143
|
+
non-interactive mode, `install -y` without `--agent` also selects all detected
|
|
144
|
+
Agents when any are detected. If none are detected, these defaults still use
|
|
145
|
+
the CLI's default target set; pass `--agent <agent-id>` to control the scope
|
|
146
|
+
explicitly.
|
|
147
|
+
|
|
148
|
+
The command checks the signed stable Engine on fresh and repeat runs, reuses
|
|
149
|
+
an equal or newer local version, and upgrades an older one. It preserves
|
|
150
|
+
modified/unregistered skills and protected symlinks. Add `--agent ...` for an
|
|
151
|
+
explicit agent selection or `--engine-path <path>` for a valid custom pin.
|
|
152
|
+
`--force` can re-download equal/older versions and overwrite existing skill
|
|
153
|
+
files, including user modifications; use it only when that overwrite is
|
|
154
|
+
intended. Protected external symlinks are not followed, and it never
|
|
155
|
+
downgrades a newer discovered Engine. Automatic reuse/upgrade rules start with
|
|
156
|
+
publicly published solution-cli 0.3.1. Latest/signature failure
|
|
157
|
+
stops before writes; offline use requires a valid explicit local Engine path.
|
|
158
|
+
|
|
132
159
|
**Fallback (Standalone shell script when Node.js is not present)**:
|
|
133
160
|
```bash
|
|
134
161
|
curl --compressed -fsSL https://sensecraft-statics.seeed.cc/solution-app/engine/install.sh | sh -s -- --with-content
|
|
@@ -162,9 +189,8 @@ daemon is a host prerequisite, not evidence that a deployment has passed.
|
|
|
162
189
|
list` or `deploy-info` exits with code 3 when no usable resource/solution
|
|
163
190
|
directory is available and tells you to run `content pull`. If a usable
|
|
164
191
|
directory exists but the requested solution ID is unknown, the query exits
|
|
165
|
-
with code 2. After `content pull` / `update`,
|
|
166
|
-
|
|
167
|
-
`sensecraft-engine daemon stop` when no deployment is running.
|
|
192
|
+
with code 2. After `content pull` / `update`, newly downloaded or updated
|
|
193
|
+
solutions are available to subsequent deployment and query commands.
|
|
168
194
|
5. Device pre-checks on this computer: `sensecraft-engine doctor --json`. Each
|
|
169
195
|
check has `status` (`ok` / `warn` / `fail`) and a `fix` command. Show the
|
|
170
196
|
user any `fail` and its `fix`; running a `fix` changes this computer and
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
| **建筑能耗 / 暖通** | 楼宇能耗、电表、水表、空调节能、HVAC, energy retrofit | `building-energy-retrofit` | `building_energy_management` |
|
|
19
19
|
| **室内外定位 / 资产** | 蓝牙信标、资产定位、人员卡片、防丢、GNSS, tracker, beacon, T1000 | `indoor-outdoor-positioning` | `indoor_positioning_ble_lorawan` |
|
|
20
20
|
|
|
21
|
+
客户询盘、客户需求分析、方案选型、如何回复客户或“我们能提供什么”也属于咨询路由;涉及楼宇温控、恒温器、温度/湿度/空气质量传感器等需求时,先用客户原话和关键技术词搜索方案库。若搜不到匹配结果,明确说明官方方案库缺少现成能力,再给出需定制验证的边界;不要把通用 IoT 能力编造成已有的 thermostat 硬件或现成方案。
|
|
22
|
+
|
|
21
23
|
---
|
|
22
24
|
|
|
23
25
|
## 2. 工具调用标准动作
|
|
@@ -27,6 +29,8 @@
|
|
|
27
29
|
```bash
|
|
28
30
|
solution-cli hub search "<用户关键词>" --limit 5 --json
|
|
29
31
|
```
|
|
32
|
+
- **咨询顺序**:先用用户原话和同义技术词查询,再根据返回的场景与 `diagnostic_playbook` 拆解需求;向用户确认点位、协议、供电、通信、控制闭环和预算等关键参数后,给出可提供的方案、硬件方向和缺口说明。
|
|
33
|
+
- **交付边界**:咨询阶段只做检索、需求澄清和方案建议,不自动执行 `deploy`;用户确认硬件和部署目标后,再转入硬件校验或部署流程。
|
|
30
34
|
- **输出结果说明**:
|
|
31
35
|
- `Scenario`:业务场景、痛点、成效、官方场景落地页(`landing_url`);
|
|
32
36
|
- `Reference Design`:技术路线、预设模式 presets、开发 wiki(`wiki_url`)、官方方案落地页(`landing_url`)、源码仓库(`repo_url`);
|
|
@@ -22,50 +22,57 @@
|
|
|
22
22
|
当设备已经部署运行,用户希望修改 L1 参数(如更改 MQTT 目标服务器)时:
|
|
23
23
|
|
|
24
24
|
### 步骤一:获取运行时信息与当前部署列表
|
|
25
|
-
|
|
25
|
+
读取运行环境指针并校验服务健康(桌面端每次启动分配随机端口并写入 `~/.sensecraft/runtime.json`;源码开发环境固定为 `http://127.0.0.1:3260`):
|
|
26
26
|
```bash
|
|
27
|
+
# 解析运行时 BASE_URL 并校验连通性
|
|
28
|
+
BASE_URL=$(jq -r '.base_url // "http://127.0.0.1:3260"' ~/.sensecraft/runtime.json 2>/dev/null || echo "http://127.0.0.1:3260")
|
|
29
|
+
curl -sf "$BASE_URL/api/health" > /dev/null || { echo "SenseCraft 服务未在 $BASE_URL 就绪"; exit 1; }
|
|
30
|
+
|
|
27
31
|
# 查询当前设备上活跃运行的方案实例
|
|
28
|
-
curl -s "
|
|
32
|
+
curl -s "$BASE_URL/api/device-management/active"
|
|
29
33
|
```
|
|
30
34
|
- 输出包含 `solution_id`、`device_id`、`status`(running/stopped)、`deployed_at` 及运行 URL。
|
|
31
35
|
|
|
32
36
|
### 步骤二:读取可配参数及当前值(Get Config)
|
|
33
37
|
```bash
|
|
34
38
|
# 本地 Docker 部署实例
|
|
35
|
-
curl -s "
|
|
39
|
+
curl -s "$BASE_URL/api/docker-devices/local/config/<solution_id>"
|
|
36
40
|
|
|
37
41
|
# 远程设备实例(通过 SSH 提取运行态值)
|
|
38
|
-
curl -s -X POST "
|
|
42
|
+
curl -s -X POST "$BASE_URL/api/docker-devices/config/<solution_id>" \
|
|
39
43
|
-H "Content-Type: application/json" \
|
|
40
44
|
-d '{"connection": {"host": "<ip>", "username": "<user>", "password": "<pwd>"}}'
|
|
41
45
|
```
|
|
42
46
|
- 检查返回的 `fields` 列表:
|
|
43
|
-
- 确认用户想要修改的参数是否存在(`field.id`);
|
|
47
|
+
- 确认用户想要修改的参数是否存在(`field.id`,如 `mqtt_broker`、`mqtt_port`);
|
|
44
48
|
- 查看当前已生效的数值(`field.current_value`);
|
|
45
49
|
- 核对其校验规则(`field.validation`)。
|
|
46
50
|
|
|
47
51
|
### 步骤三:验证并提交新参数(Update Config)
|
|
52
|
+
> [!IMPORTANT]
|
|
53
|
+
> `values` 字典中的 key 必须使用步骤二返回的 `field.id`(例如 `mqtt_broker`、`mqtt_port`),严禁使用环境变量大写名(如 `MQTT_BROKER_HOST`),否则会被后端配置解析器静默忽略。
|
|
54
|
+
|
|
48
55
|
向用户确认修改后,发起热更新请求:
|
|
49
56
|
```bash
|
|
50
|
-
# 本地 Docker
|
|
51
|
-
curl -s -X PUT "
|
|
57
|
+
# 本地 Docker 部署实例更新(使用从 GET 返回查得的 field.id)
|
|
58
|
+
curl -s -X PUT "$BASE_URL/api/docker-devices/local/config/<solution_id>" \
|
|
52
59
|
-H "Content-Type: application/json" \
|
|
53
60
|
-d '{
|
|
54
61
|
"values": {
|
|
55
|
-
"
|
|
56
|
-
"
|
|
62
|
+
"mqtt_broker": "192.168.1.200",
|
|
63
|
+
"mqtt_port": 1883
|
|
57
64
|
}
|
|
58
65
|
}'
|
|
59
66
|
|
|
60
67
|
# 远程设备实例更新
|
|
61
|
-
curl -s -X PUT "
|
|
68
|
+
curl -s -X PUT "$BASE_URL/api/docker-devices/config/<solution_id>" \
|
|
62
69
|
-H "Content-Type: application/json" \
|
|
63
70
|
-d '{
|
|
64
|
-
"values": { "
|
|
71
|
+
"values": { "mqtt_broker": "192.168.1.200" },
|
|
65
72
|
"connection": { "host": "<ip>", "username": "<user>", "password": "<pwd>" }
|
|
66
73
|
}'
|
|
67
74
|
```
|
|
68
|
-
-
|
|
75
|
+
- **后端自动执行**:根据 `field.id` 执行模板替换 ➔ 生成新的 env ➔ `docker compose up -d` 重新创建受影响容器 ➔ 更新部署 manifest 记录。
|
|
69
76
|
- **无需整机重装**,数据卷与现有文件完全保留。
|
|
70
77
|
|
|
71
78
|
---
|
|
@@ -74,16 +81,41 @@ curl -s -X PUT "http://127.0.0.1:3260/api/docker-devices/config/<solution_id>" \
|
|
|
74
81
|
|
|
75
82
|
当用户要求“重启服务”、“暂停运行”或“更新容器镜像”时:
|
|
76
83
|
|
|
84
|
+
### 本地 Docker 部署实例(无凭据要求)
|
|
77
85
|
```bash
|
|
78
86
|
# 执行 start / stop / restart / update
|
|
79
|
-
curl -s -X POST "
|
|
87
|
+
curl -s -X POST "$BASE_URL/api/device-management/<deployment_id>/action" \
|
|
80
88
|
-H "Content-Type: application/json" \
|
|
81
89
|
-d '{
|
|
82
90
|
"action": "restart",
|
|
83
91
|
"device_id": "<step_id>"
|
|
84
92
|
}'
|
|
85
93
|
```
|
|
94
|
+
|
|
95
|
+
### 远程设备实例(必须显式携带 SSH 认证凭据)
|
|
96
|
+
远程设备(Jetson / Raspberry Pi 等)通过 SSH 协议由底层执行容器编排动作。由于后端安全机制禁用了 SSH-agent 与自动公钥搜寻,且不持久化保存设备明文密码,因此远程操作**必须向用户获取并显式传入 `password` 或 `key_file`**:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# 远程实例 - 密码认证
|
|
100
|
+
curl -s -X POST "$BASE_URL/api/device-management/<deployment_id>/action" \
|
|
101
|
+
-H "Content-Type: application/json" \
|
|
102
|
+
-d '{
|
|
103
|
+
"action": "restart",
|
|
104
|
+
"device_id": "<step_id>",
|
|
105
|
+
"password": "<ssh_password>"
|
|
106
|
+
}'
|
|
107
|
+
|
|
108
|
+
# 远程实例 - 私钥文件认证(支持 ~/.ssh/ 下的私钥路径)
|
|
109
|
+
curl -s -X POST "$BASE_URL/api/device-management/<deployment_id>/action" \
|
|
110
|
+
-H "Content-Type: application/json" \
|
|
111
|
+
-d '{
|
|
112
|
+
"action": "restart",
|
|
113
|
+
"device_id": "<step_id>",
|
|
114
|
+
"key_file": "~/.ssh/id_rsa"
|
|
115
|
+
}'
|
|
116
|
+
```
|
|
86
117
|
- `action` 支持:`start`(启动)、`stop`(停止)、`restart`(重启)、`update`(拉取最新镜像并重启)。
|
|
118
|
+
- **判定规则**:从步骤一 `/api/device-management/active` 的返回值中检查目标实例的 `host` 字段。若 `host` 不为 `localhost` 或 `127.0.0.1`,即属于远程设备,发起 action 请求前必须准备好认证凭据。
|
|
87
119
|
|
|
88
120
|
---
|
|
89
121
|
|
|
@@ -81,7 +81,7 @@ three classes:
|
|
|
81
81
|
| Class | Example | What you do |
|
|
82
82
|
|---|---|---|
|
|
83
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
|
|
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, and redeploy with `sensecraft-engine deploy <id> --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
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
86
|
|
|
87
87
|
If the evidence does not fit any row, say so and show the user the first error
|
|
@@ -65,11 +65,8 @@ git clone https://github.com/Seeed-Studio/ModelAssistant.git ./my-model-assistan
|
|
|
65
65
|
修改完成后,无需打包推送到 CDN,直接利用 `sensecraft-engine` 的 `--solutions-dir` 特性在本地无缝验证:
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
#
|
|
69
|
-
sensecraft-engine
|
|
70
|
-
|
|
71
|
-
# 2. 从本地自定义工程目录加载方案并执行部署测试
|
|
72
|
-
sensecraft-engine deploy --solutions-dir ./my-solutions/solutions --solution <id>
|
|
68
|
+
# 从本地自定义工程目录加载方案并执行部署测试
|
|
69
|
+
sensecraft-engine deploy <id> --solutions-dir ./my-solutions/solutions
|
|
73
70
|
```
|
|
74
71
|
- 引擎将直接读取本地工程中的 `solution.yaml`、`devices/*.yaml` 和 `docker-compose.yml`,秒级完成本地部署生效。
|
|
75
72
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|