@seeed-studio/solution-cli 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -113
- package/bin/cli.js +315 -29
- package/catalog/client-config.json +2 -2
- package/catalog/knowledge-core.json +58452 -488
- package/docs/cli-guide.md +176 -231
- package/lib/agent.js +69 -10
- package/lib/catalog-dto.js +14 -1
- package/lib/doctor.js +19 -1
- package/lib/downloader.js +24 -0
- package/lib/gotchas.js +126 -0
- package/lib/installer.js +75 -9
- package/lib/integration.js +265 -0
- package/lib/knowledge.js +838 -100
- package/package.json +1 -1
- package/skills/solution-cli/SKILL.md +106 -24
- package/skills/solution-cli/references/consult-and-select.md +58 -14
- package/skills/solution-cli/references/deploy.md +60 -248
- package/skills/solution-cli/references/device-management.md +249 -77
- package/skills/solution-cli/references/diagnose.md +15 -0
- package/skills/solution-cli/references/edge-gotchas.md +205 -0
- package/skills/solution-cli/references/generic-rk3588.md +106 -0
- package/skills/solution-cli/references/hardware-bom.md +9 -3
- package/skills/solution-cli/references/sales-feasibility.md +65 -0
- package/skills/solution-cli/references/secondary-development.md +10 -7
package/bin/cli.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { runInstall, runStatus, runUninstall } from '../lib/installer.js';
|
|
5
|
+
import { runInstall, runUpdate, listAgents, runStatus, runUninstall } from '../lib/installer.js';
|
|
6
6
|
import { runDoctor } from '../lib/doctor.js';
|
|
7
|
-
import { searchHub, queryProductFamily, queryAccessories, createDeploymentLockfile } from '../lib/knowledge.js';
|
|
7
|
+
import { searchHub, listHub, getHubRecord, loadKnowledgeCore, queryProductFamily, queryAccessories, createDeploymentLockfile } from '../lib/knowledge.js';
|
|
8
8
|
import { assertCompatibility } from '../lib/compatibility.js';
|
|
9
|
+
import { searchGotchas } from '../lib/gotchas.js';
|
|
9
10
|
|
|
10
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf8'));
|
|
@@ -23,32 +24,44 @@ Usage:
|
|
|
23
24
|
|
|
24
25
|
Commands:
|
|
25
26
|
install Inspect environment, verify/download engine, and configure AI Agent skills (default)
|
|
27
|
+
update Check for Engine/catalog updates and synchronize all installed AI Agent skills
|
|
26
28
|
status Display current engine, solution content, and agent skills status
|
|
27
29
|
doctor Diagnose environment (platform, permissions, docker, PATH, ledger)
|
|
30
|
+
agent Manage and list supported AI Agent skill destinations
|
|
31
|
+
Usage: solution-cli agent list [--json]
|
|
28
32
|
uninstall Cleanly remove Solution CLI skills from specified agent(s)
|
|
29
33
|
hub Search scenarios and reference designs from Seeed Solution Hub
|
|
30
|
-
Usage: solution-cli hub search <keyword> [--limit 5] [--locale zh|en] [--json]
|
|
34
|
+
Usage: solution-cli hub search <keyword> [--limit 5] [--locale zh|en|ja] [--json]
|
|
35
|
+
solution-cli hub list [--type scenario|tech|reference_design] [--page N] [--limit 50] [--locale zh|en|ja] [--json]
|
|
36
|
+
solution-cli hub get <id> [--type ...] [--section overview|links|description|architecture|products|deployment|playbook|integration] [--preset <id>] [--locale zh|en|ja] [--json]
|
|
31
37
|
product Query product families, SKUs, and verified accessories
|
|
32
38
|
Usage: solution-cli product query <family_id> [--locale zh|en] [--json]
|
|
33
|
-
solution-cli product accessories <family_id> [--sku <sku>] [--json]
|
|
39
|
+
solution-cli product accessories <family_id> [--sku <sku>] [--category <cats>] [--compact] [--json]
|
|
34
40
|
solution-cli product verify-compatibility --host <family_id> [--json]
|
|
41
|
+
gotchas Search edge hardware and chip troubleshooting knowledge (RK3588, reCamera, Jetson, Hailo)
|
|
42
|
+
Usage: solution-cli gotchas [keyword] [--chip rk3588|recamera|jetson|hailo] [--json]
|
|
35
43
|
lockfile Create an immutable DeploymentLockfile to isolate deployment execution
|
|
36
44
|
Usage: solution-cli lockfile create --solution <id> [--version <v>] [--hardware <fams>] [--json]
|
|
37
45
|
help Show this help message
|
|
38
46
|
|
|
39
47
|
Options:
|
|
40
|
-
--agent <name|all> Target agent(s): workbuddy, claude, codex, codebuddy, or all
|
|
48
|
+
--agent <name|all> Target agent(s): workbuddy, doubao, claude, codex, codebuddy, antigravity, or all
|
|
49
|
+
--skills-dir <path> Install skill directly to custom directory (supports any third-party or proprietary Agent)
|
|
41
50
|
-y, --yes Accept default detected selections non-interactively
|
|
42
51
|
--dry-run Simulate operations without making changes to filesystem
|
|
43
52
|
--skip-content Skip pulling IoT solution packages from CDN
|
|
44
53
|
--force Re-download equal/older stable Engine and overwrite existing skill files, including local modifications; never downgrade a newer local Engine
|
|
45
54
|
--json Output machine-readable JSON (supported by all query/diagnostic commands)
|
|
46
|
-
--
|
|
55
|
+
--compact Output token-efficient minified response (omits verbose compatibility dimension trees)
|
|
56
|
+
--locale <zh|en|ja> Language for hub and product results (default: zh)
|
|
47
57
|
--limit <num> Maximum results for search (default: 5)
|
|
48
58
|
--engine-path <path> Use custom engine executable path
|
|
49
59
|
|
|
50
60
|
Examples:
|
|
51
61
|
npx @seeed-studio/solution-cli install
|
|
62
|
+
solution-cli update
|
|
63
|
+
solution-cli agent list
|
|
64
|
+
solution-cli install --skills-dir ~/.cursor/skills -y
|
|
52
65
|
solution-cli hub search "跌倒"
|
|
53
66
|
solution-cli product accessories recomputer_j40
|
|
54
67
|
solution-cli product query sensecap_watcher --json
|
|
@@ -68,14 +81,23 @@ function parseFlags(rawArgs) {
|
|
|
68
81
|
const usedFlags = new Set();
|
|
69
82
|
const flags = {
|
|
70
83
|
agent: null,
|
|
84
|
+
skillsDir: null,
|
|
71
85
|
yes: false,
|
|
72
86
|
dryRun: false,
|
|
73
87
|
skipContent: false,
|
|
74
88
|
force: false,
|
|
75
89
|
json: false,
|
|
90
|
+
compact: false,
|
|
76
91
|
enginePath: null,
|
|
77
92
|
limit: 5,
|
|
93
|
+
page: 1,
|
|
94
|
+
type: null,
|
|
95
|
+
section: 'overview',
|
|
96
|
+
sections: null,
|
|
78
97
|
locale: 'zh',
|
|
98
|
+
preset: null,
|
|
99
|
+
category: null,
|
|
100
|
+
chip: null,
|
|
79
101
|
sku: null,
|
|
80
102
|
host: null,
|
|
81
103
|
accessory: null,
|
|
@@ -111,6 +133,7 @@ function parseFlags(rawArgs) {
|
|
|
111
133
|
};
|
|
112
134
|
|
|
113
135
|
if (valueFlag('--agent', value => { flags.agent = value; })) {
|
|
136
|
+
} else if (valueFlag('--skills-dir', value => { flags.skillsDir = value; })) {
|
|
114
137
|
} else if (a === '-y' || a === '--yes') {
|
|
115
138
|
usedFlags.add('--yes');
|
|
116
139
|
flags.yes = true;
|
|
@@ -126,6 +149,9 @@ function parseFlags(rawArgs) {
|
|
|
126
149
|
} else if (a === '--json') {
|
|
127
150
|
usedFlags.add('--json');
|
|
128
151
|
flags.json = true;
|
|
152
|
+
} else if (a === '--compact') {
|
|
153
|
+
usedFlags.add('--compact');
|
|
154
|
+
flags.compact = true;
|
|
129
155
|
} else if (valueFlag('--engine-path', value => { flags.enginePath = value; })) {
|
|
130
156
|
} else if (valueFlag('--limit', value => {
|
|
131
157
|
if (!/^[1-9]\d*$/.test(value)) throw new CliArgumentError('--limit requires a positive integer');
|
|
@@ -133,6 +159,18 @@ function parseFlags(rawArgs) {
|
|
|
133
159
|
if (!Number.isSafeInteger(limit)) throw new CliArgumentError('--limit is too large');
|
|
134
160
|
flags.limit = limit;
|
|
135
161
|
})) {
|
|
162
|
+
} else if (valueFlag('--page', value => {
|
|
163
|
+
if (!/^[1-9]\d*$/.test(value)) throw new CliArgumentError('--page requires a positive integer');
|
|
164
|
+
const page = Number(value);
|
|
165
|
+
if (!Number.isSafeInteger(page)) throw new CliArgumentError('--page is too large');
|
|
166
|
+
flags.page = page;
|
|
167
|
+
})) {
|
|
168
|
+
} else if (valueFlag('--type', value => { flags.type = value; })) {
|
|
169
|
+
} else if (valueFlag('--section', value => { flags.section = value; })) {
|
|
170
|
+
} else if (valueFlag('--sections', value => { flags.sections = value; })) {
|
|
171
|
+
} else if (valueFlag('--preset', value => { flags.preset = value; })) {
|
|
172
|
+
} else if (valueFlag('--category', value => { flags.category = value; })) {
|
|
173
|
+
} else if (valueFlag('--chip', value => { flags.chip = value; })) {
|
|
136
174
|
} else if (valueFlag('--locale', value => { flags.locale = value; })) {
|
|
137
175
|
} else if (valueFlag('--sku', value => { flags.sku = value; })) {
|
|
138
176
|
} else if (valueFlag('--host', value => { flags.host = value; })) {
|
|
@@ -151,23 +189,42 @@ function parseFlags(rawArgs) {
|
|
|
151
189
|
}
|
|
152
190
|
|
|
153
191
|
function validatePositionals(command, positionals) {
|
|
154
|
-
if (['install', 'uninstall', 'status', 'doctor'].includes(command) && positionals.length > 0) {
|
|
192
|
+
if (['install', 'update', 'uninstall', 'status', 'doctor'].includes(command) && positionals.length > 0) {
|
|
155
193
|
throw new CliArgumentError(`${command} does not accept positional arguments`);
|
|
156
194
|
}
|
|
195
|
+
if (command === 'agent') {
|
|
196
|
+
const sub = positionals[0] || 'list';
|
|
197
|
+
if (sub !== 'list') {
|
|
198
|
+
throw new CliArgumentError(`Unknown agent action "${sub}". Supported: list`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
157
201
|
}
|
|
158
202
|
|
|
159
203
|
function validateCommandFlags(command, flags) {
|
|
160
204
|
const allowed = {
|
|
161
|
-
install: ['--agent', '--yes', '--dry-run', '--skip-content', '--force', '--engine-path'],
|
|
205
|
+
install: ['--agent', '--skills-dir', '--yes', '--dry-run', '--skip-content', '--force', '--engine-path'],
|
|
206
|
+
update: ['--agent', '--skills-dir', '--yes', '--dry-run', '--skip-content', '--force', '--engine-path'],
|
|
162
207
|
uninstall: ['--agent', '--dry-run', '--force'],
|
|
163
208
|
status: ['--json'],
|
|
164
209
|
doctor: ['--json'],
|
|
165
|
-
|
|
166
|
-
|
|
210
|
+
agent: ['--json'],
|
|
211
|
+
hub: ['--json', '--compact', '--locale', '--limit', '--page', '--type', '--section', '--sections', '--preset'],
|
|
212
|
+
product: ['--json', '--compact', '--locale', '--sku', '--host', '--accessory', '--category'],
|
|
213
|
+
gotchas: ['--json', '--chip'],
|
|
167
214
|
lockfile: ['--json', '--solution', '--version', '--hardware'],
|
|
168
215
|
help: [],
|
|
169
216
|
};
|
|
170
|
-
|
|
217
|
+
let supported = allowed[command];
|
|
218
|
+
if (command === 'hub') {
|
|
219
|
+
const sub = flags.positionals[0] || 'search';
|
|
220
|
+
const perSubcommand = {
|
|
221
|
+
search: ['--json', '--compact', '--locale', '--limit'],
|
|
222
|
+
list: ['--json', '--compact', '--locale', '--limit', '--page', '--type'],
|
|
223
|
+
get: ['--json', '--compact', '--locale', '--type', '--section', '--sections', '--preset'],
|
|
224
|
+
};
|
|
225
|
+
if (!perSubcommand[sub]) throw new CliArgumentError(`Unknown hub action "${sub}". Supported: search, list, get`);
|
|
226
|
+
supported = perSubcommand[sub];
|
|
227
|
+
}
|
|
171
228
|
if (!supported) return;
|
|
172
229
|
for (const flag of flags.usedFlags) {
|
|
173
230
|
if (!supported.includes(flag)) {
|
|
@@ -176,6 +233,85 @@ function validateCommandFlags(command, flags) {
|
|
|
176
233
|
}
|
|
177
234
|
}
|
|
178
235
|
|
|
236
|
+
function renderSectionOutput(r, section) {
|
|
237
|
+
if (section === 'links') {
|
|
238
|
+
const landingEntries = Object.entries(r.links?.landing || {});
|
|
239
|
+
if (landingEntries.length) landingEntries.forEach(([locale, url]) => console.log(` Landing (${locale}): ${url}`));
|
|
240
|
+
else console.log(' Landing: unavailable');
|
|
241
|
+
console.log(` Wiki: ${r.links?.wiki || 'unavailable'}`);
|
|
242
|
+
console.log(` Repo: ${r.links?.repo || 'unavailable'}`);
|
|
243
|
+
console.log(` Declared documents: ${r.links?.declared_documents?.length ? r.links.declared_documents.join(', ') : 'unavailable'}`);
|
|
244
|
+
} else if (section === 'description') {
|
|
245
|
+
console.log(` Content locale: ${r.description?.content_locale || 'unknown'}`);
|
|
246
|
+
console.log(` Source role: ${r.description?.source_role || 'unavailable'}`);
|
|
247
|
+
console.log(` Source: ${r.description?.source || 'unavailable'}`);
|
|
248
|
+
if (r.description?.body) console.log(`\n${r.description.body}`);
|
|
249
|
+
else console.log(' Content: unavailable (the selected section has no body)');
|
|
250
|
+
} else if (section === 'architecture') {
|
|
251
|
+
console.log(` Content locale: ${r.architecture?.content_locale || 'unknown'}`);
|
|
252
|
+
console.log(` Fallback: ${r.architecture?.fallback ? 'yes' : 'no'}`);
|
|
253
|
+
console.log(` Source role: ${r.architecture?.source_role || 'unavailable'}`);
|
|
254
|
+
if (r.architecture?.source) console.log(` Source: ${r.architecture.source}`);
|
|
255
|
+
if (r.architecture?.intro) console.log(`\n${r.architecture.intro}`);
|
|
256
|
+
(r.architecture?.modules || []).forEach((m) => {
|
|
257
|
+
console.log(` ${m.label || m.id || 'Architecture'}:`);
|
|
258
|
+
if (m.card_image) console.log(` Image: ${m.card_image}`);
|
|
259
|
+
if (m.description) console.log(` ${m.description}`);
|
|
260
|
+
if (m.body) console.log(`\n${m.body}`);
|
|
261
|
+
});
|
|
262
|
+
(r.architecture?.gallery || []).forEach((g) => console.log(` ${g.role || 'Gallery'}: ${g.src}`));
|
|
263
|
+
if (!(r.architecture?.modules?.length || r.architecture?.gallery?.length)) console.log(' Content: unavailable (no architecture modules or gallery items)');
|
|
264
|
+
} else if (section === 'products') {
|
|
265
|
+
console.log(` Content locale: ${r.products_content_locale || 'unknown'}`);
|
|
266
|
+
console.log(` Source role: ${r.products_source_role || 'unavailable'}`);
|
|
267
|
+
if (!Object.keys(r.products || {}).length && !Object.keys(r.bom || {}).length) console.log(' Content: unavailable (no product or BOM entries)');
|
|
268
|
+
console.log(JSON.stringify({ products: r.products || {}, bom: r.bom || {} }, null, 2));
|
|
269
|
+
} else if (section === 'deployment') {
|
|
270
|
+
console.log(` Content locale: ${r.deployment?.content_locale || 'unknown'}`);
|
|
271
|
+
console.log(` Source role: ${r.deployment?.source_role || 'unavailable'}`);
|
|
272
|
+
console.log(` Deployable: ${r.deployment?.deployable ? 'yes' : 'no'}`);
|
|
273
|
+
console.log(` Solution: ${r.deployment?.solution_id || 'unavailable'}`);
|
|
274
|
+
if (r.deployment?.filtered_by_preset) {
|
|
275
|
+
console.log(` \x1b[32mFiltered by Preset:\x1b[0m ${r.deployment.filtered_by_preset}`);
|
|
276
|
+
}
|
|
277
|
+
console.log(` Presets: ${r.deployment?.presets?.length ? r.deployment.presets.map((p) => p.id || p.name || p).join(', ') : 'unavailable'}`);
|
|
278
|
+
console.log(` Guides: ${Object.keys(r.deployment?.guides || {}).length ? JSON.stringify(r.deployment.guides) : 'unavailable'}`);
|
|
279
|
+
console.log(` Source files: ${r.deployment?.source_files?.length ? r.deployment.source_files.join(', ') : 'unavailable'}`);
|
|
280
|
+
if (r.deployment?.guide?.body) {
|
|
281
|
+
console.log(` Guide locale: ${r.deployment.guide.content_locale}`);
|
|
282
|
+
console.log(`\n${r.deployment.guide.body}`);
|
|
283
|
+
} else console.log(' Content: unavailable (no deployment guide body returned)');
|
|
284
|
+
} else if (section === 'playbook') {
|
|
285
|
+
console.log(` \x1b[36m[现场工况诊断手册 (Diagnostic Playbook)]\x1b[0m`);
|
|
286
|
+
console.log(` 领域: ${r.playbook?.domain || r.title}`);
|
|
287
|
+
console.log(` 核心决断维度: ${(r.playbook?.core_dimensions || []).join(' | ')}`);
|
|
288
|
+
console.log('\n \x1b[33m[专家工况诊断追问 (2~3问)]\x1b[0m');
|
|
289
|
+
(r.playbook?.questions || []).forEach((q, qIdx) => {
|
|
290
|
+
console.log(` ${qIdx + 1}. \x1b[1m${q.question}\x1b[0m`);
|
|
291
|
+
if (q.rationale) console.log(` 💡 技术/商务决断理由: ${q.rationale}`);
|
|
292
|
+
});
|
|
293
|
+
} else if (section === 'integration') {
|
|
294
|
+
console.log(` \x1b[36m[系统集成与对接规范 (Integration Contract)]\x1b[0m`);
|
|
295
|
+
console.log(` 支持协议: ${(r.integration?.protocols || []).join(', ').toUpperCase()}`);
|
|
296
|
+
console.log('\n \x1b[33m[输入流与环境要求 (Inputs)]\x1b[0m');
|
|
297
|
+
if ((r.integration?.input_requirements || []).length === 0) {
|
|
298
|
+
console.log(' (根据具体选型接入标准 RTSP/传感器数据流)');
|
|
299
|
+
} else {
|
|
300
|
+
r.integration.input_requirements.forEach((inp) => console.log(` - [${inp.type?.toUpperCase()}] ${inp.description}`));
|
|
301
|
+
}
|
|
302
|
+
console.log('\n \x1b[32m[输出与数据总线 (Outputs)]\x1b[0m');
|
|
303
|
+
(r.integration?.interfaces || []).forEach((iface) => {
|
|
304
|
+
console.log(` - \x1b[1m[${iface.protocol?.toUpperCase()}]\x1b[0m ${iface.topic || iface.endpoint || ''} ${iface.port ? `(Port: ${iface.port})` : ''}`);
|
|
305
|
+
if (iface.description) console.log(` 说明: ${iface.description}`);
|
|
306
|
+
if (iface.payload_example) console.log(` 载荷示例: ${typeof iface.payload_example === 'string' ? iface.payload_example : JSON.stringify(iface.payload_example)}`);
|
|
307
|
+
});
|
|
308
|
+
if (r.integration?.system_ingestion_guide?.length) {
|
|
309
|
+
console.log('\n \x1b[35m[系统快速对接指引]\x1b[0m');
|
|
310
|
+
r.integration.system_ingestion_guide.forEach((g) => console.log(` ${g}`));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
179
315
|
async function main() {
|
|
180
316
|
if (args.some(a => a === '--help' || a === '-h')) {
|
|
181
317
|
printHelp();
|
|
@@ -187,8 +323,9 @@ async function main() {
|
|
|
187
323
|
return;
|
|
188
324
|
}
|
|
189
325
|
|
|
326
|
+
let flags = { json: args.includes('--json') };
|
|
190
327
|
try {
|
|
191
|
-
|
|
328
|
+
flags = parseFlags(args.slice(1));
|
|
192
329
|
validatePositionals(command, flags.positionals);
|
|
193
330
|
validateCommandFlags(command, flags);
|
|
194
331
|
|
|
@@ -202,6 +339,28 @@ async function main() {
|
|
|
202
339
|
await runInstall(flags);
|
|
203
340
|
break;
|
|
204
341
|
|
|
342
|
+
case 'update':
|
|
343
|
+
await runUpdate({ ...flags, yes: flags.usedFlags.has('--yes') ? flags.yes : true });
|
|
344
|
+
break;
|
|
345
|
+
|
|
346
|
+
case 'agent': {
|
|
347
|
+
const list = listAgents();
|
|
348
|
+
if (flags.json) {
|
|
349
|
+
console.log(JSON.stringify({ agents: list }, null, 2));
|
|
350
|
+
} else {
|
|
351
|
+
console.log('\n🤖 Supported AI Agent Targets:\n');
|
|
352
|
+
list.forEach((a, idx) => {
|
|
353
|
+
const detectedTag = a.detected ? '\x1b[32m[Detected]\x1b[0m' : '\x1b[90m[Not Detected]\x1b[0m';
|
|
354
|
+
const installTag = a.installed ? '\x1b[36m(Installed)\x1b[0m' : '\x1b[90m(Not Installed)\x1b[0m';
|
|
355
|
+
console.log(` ${idx + 1}. \x1b[1m${a.name}\x1b[0m (ID: ${a.id}) ${detectedTag} ${installTag}`);
|
|
356
|
+
console.log(` Skills Dir: ${a.skillsDir}`);
|
|
357
|
+
});
|
|
358
|
+
console.log('\n💡 Tip: To install into any custom or proprietary Agent, use:');
|
|
359
|
+
console.log(' solution-cli install --skills-dir <path> -y\n');
|
|
360
|
+
}
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
|
|
205
364
|
case 'status':
|
|
206
365
|
runStatus(flags);
|
|
207
366
|
break;
|
|
@@ -228,32 +387,94 @@ async function main() {
|
|
|
228
387
|
|
|
229
388
|
case 'hub': {
|
|
230
389
|
const sub = flags.positionals[0] || 'search';
|
|
231
|
-
if (
|
|
232
|
-
console.error(`Unknown hub action "${sub}". Supported:
|
|
390
|
+
if (!['search', 'list', 'get'].includes(sub)) {
|
|
391
|
+
console.error(`Unknown hub action "${sub}". Supported: search, list, get`);
|
|
233
392
|
process.exit(1);
|
|
234
393
|
}
|
|
235
|
-
|
|
236
|
-
|
|
394
|
+
if (sub === 'list' && flags.positionals.length > 1) {
|
|
395
|
+
throw new CliArgumentError('hub list does not accept positional IDs; use --type, --page, and --limit');
|
|
396
|
+
}
|
|
397
|
+
if (sub === 'get' && (flags.positionals.length !== 2 || !flags.positionals[1])) {
|
|
398
|
+
throw new CliArgumentError('hub get requires exactly one record ID');
|
|
399
|
+
}
|
|
400
|
+
let dto;
|
|
401
|
+
if (sub === 'list') {
|
|
402
|
+
const listLimit = flags.usedFlags.has('--limit') ? flags.limit : 50;
|
|
403
|
+
dto = listHub({ type: flags.type, locale: flags.locale, page: flags.page, limit: listLimit });
|
|
404
|
+
} else if (sub === 'get') {
|
|
405
|
+
const row = getHubRecord(flags.positionals[1], {
|
|
406
|
+
type: flags.type,
|
|
407
|
+
locale: flags.locale,
|
|
408
|
+
section: flags.section,
|
|
409
|
+
sections: flags.sections,
|
|
410
|
+
preset: flags.preset,
|
|
411
|
+
});
|
|
412
|
+
if (!row) {
|
|
413
|
+
console.error(`Hub record "${flags.positionals[1]}" not found in the bundled catalog.`);
|
|
414
|
+
process.exit(1);
|
|
415
|
+
}
|
|
416
|
+
const core = loadKnowledgeCore();
|
|
417
|
+
dto = {
|
|
418
|
+
records: [row],
|
|
419
|
+
source: 'bundled_manifest',
|
|
420
|
+
freshness: 'UNKNOWN',
|
|
421
|
+
dataset_version: core.dataset_version || null,
|
|
422
|
+
build_time: core.build_time || null,
|
|
423
|
+
provenance: core.provenance || null,
|
|
424
|
+
scope: 'bundled_catalog_snapshot',
|
|
425
|
+
locale: flags.locale,
|
|
426
|
+
warnings: [],
|
|
427
|
+
};
|
|
428
|
+
} else {
|
|
429
|
+
const query = flags.positionals.slice(1).join(' ');
|
|
430
|
+
dto = await searchHub({ query, locale: flags.locale, limit: flags.limit });
|
|
431
|
+
}
|
|
237
432
|
if (flags.json) {
|
|
238
433
|
console.log(JSON.stringify(dto, null, 2));
|
|
239
434
|
} else {
|
|
240
|
-
console.log(`\n🔍 Solutions Hub
|
|
435
|
+
console.log(`\n🔍 Solutions Hub ${sub} (${dto.source}):\n`);
|
|
241
436
|
if (dto.records.length === 0) {
|
|
242
437
|
console.log(' No matching scenarios or reference designs found.');
|
|
243
438
|
} else {
|
|
244
439
|
dto.records.forEach((r, idx) => {
|
|
245
|
-
const tag = r.type === 'scenario' ? '\x1b[36m[Scenario]\x1b[0m' : '\x1b[35m[Reference Design]\x1b[0m';
|
|
440
|
+
const tag = r.type === 'tech' ? '\x1b[34m[Tech]\x1b[0m' : r.type === 'scenario' ? '\x1b[36m[Scenario]\x1b[0m' : '\x1b[35m[Reference Design]\x1b[0m';
|
|
246
441
|
console.log(` ${idx + 1}. ${tag} \x1b[1m${r.title}\x1b[0m (${r.id})`);
|
|
442
|
+
if (sub === 'get') {
|
|
443
|
+
const activeSections = r.sections || (r.section ? [r.section] : ['overview']);
|
|
444
|
+
activeSections.forEach((sec) => {
|
|
445
|
+
renderSectionOutput(r, sec);
|
|
446
|
+
});
|
|
447
|
+
if (activeSections.includes('overview') && r.available_sections?.length) {
|
|
448
|
+
console.log('\n \x1b[36m[Available Sections & Status 渐进式目录]:\x1b[0m');
|
|
449
|
+
r.available_sections.forEach((sec) => {
|
|
450
|
+
const status = r.section_status?.[sec] || 'documented';
|
|
451
|
+
const isCurrent = sec === 'overview' ? ' \x1b[32m[Current]\x1b[0m' : '';
|
|
452
|
+
const cmd = sec === 'overview' ? `hub get ${r.id}` : `hub get ${r.id} --section ${sec}${sec === 'deployment' ? ' [--preset <id>]' : ''}`;
|
|
453
|
+
console.log(` - \x1b[1m${sec}\x1b[0m (${status}): ${cmd}${isCurrent}`);
|
|
454
|
+
});
|
|
455
|
+
console.log(' 💡 批量拉取: hub get ' + r.id + ' --sections architecture,integration');
|
|
456
|
+
}
|
|
457
|
+
}
|
|
247
458
|
if (r.summary) console.log(` ${r.summary}`);
|
|
248
|
-
if (r.
|
|
459
|
+
if (r.architecture_preview?.image_url) {
|
|
460
|
+
console.log(` 官方架构图: ${r.architecture_preview.image_url}`);
|
|
461
|
+
if (r.architecture_preview.summary) console.log(` 架构拓扑: ${r.architecture_preview.summary}`);
|
|
462
|
+
}
|
|
463
|
+
if (r.landing_url) console.log(` Landing: ${r.landing_url}`);
|
|
464
|
+
console.log(` Scenario IDs: ${r.scenario_ids?.length ? r.scenario_ids.join(', ') : 'none'}`);
|
|
465
|
+
console.log(` Tech IDs: ${r.technology_ids?.length ? r.technology_ids.join(', ') : 'none'}`);
|
|
466
|
+
console.log(` Reference Design IDs: ${r.reference_designs?.length ? r.reference_designs.join(', ') : 'none'}`);
|
|
467
|
+
if (r.solution_id) console.log(` 可部署内容: ${r.solution_id}`);
|
|
249
468
|
if (r.family_ids?.length) console.log(` 推荐硬件族: ${r.family_ids.join(', ')}`);
|
|
250
469
|
if (r.presets?.length) console.log(` 预设模式: ${r.presets.join(', ')}`);
|
|
251
470
|
if (r.wiki_url) console.log(` 开发 Wiki: ${r.wiki_url}`);
|
|
252
|
-
if (r.
|
|
253
|
-
|
|
254
|
-
r.
|
|
255
|
-
|
|
256
|
-
|
|
471
|
+
if (r.has_playbook) console.log(' \x1b[32m现场工况诊断:\x1b[0m 可用 (使用 hub get ' + r.id + ' --section playbook 查看专业追问)');
|
|
472
|
+
if (r.available_sections?.length) {
|
|
473
|
+
const sectionSummary = r.available_sections.map((s) => {
|
|
474
|
+
const st = r.section_status?.[s];
|
|
475
|
+
return st && st !== 'documented' ? `${s}(${st})` : s;
|
|
476
|
+
}).join(', ');
|
|
477
|
+
console.log(` Available Sections: ${sectionSummary}`);
|
|
257
478
|
}
|
|
258
479
|
console.log('');
|
|
259
480
|
});
|
|
@@ -261,6 +482,14 @@ async function main() {
|
|
|
261
482
|
if (dto.warnings.length) {
|
|
262
483
|
dto.warnings.forEach((w) => console.log(`\x1b[33mNote:\x1b[0m ${w}`));
|
|
263
484
|
}
|
|
485
|
+
if (sub === 'list' && dto.pagination) {
|
|
486
|
+
const next = dto.pagination.next_page;
|
|
487
|
+
console.log(`\nCatalog snapshot: total=${dto.pagination.total} returned=${dto.pagination.returned} has_more=${dto.pagination.has_more} next_page=${next ?? 'none'}`);
|
|
488
|
+
if (dto.pagination.has_more && next) {
|
|
489
|
+
const typeArg = flags.type ? ` --type ${flags.type}` : '';
|
|
490
|
+
console.log(`Next page: solution-cli hub list --locale ${flags.locale} --page ${next} --limit ${dto.pagination.limit}${typeArg}`);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
264
493
|
}
|
|
265
494
|
break;
|
|
266
495
|
}
|
|
@@ -299,7 +528,12 @@ async function main() {
|
|
|
299
528
|
console.error('Please specify a product family ID (e.g. "solution-cli product accessories recomputer_j40")');
|
|
300
529
|
process.exit(1);
|
|
301
530
|
}
|
|
302
|
-
const dto = queryAccessories(familyId, {
|
|
531
|
+
const dto = queryAccessories(familyId, {
|
|
532
|
+
sku: flags.sku,
|
|
533
|
+
locale: flags.locale,
|
|
534
|
+
compact: flags.compact,
|
|
535
|
+
category: flags.category,
|
|
536
|
+
});
|
|
303
537
|
if (flags.json) {
|
|
304
538
|
console.log(JSON.stringify(dto, null, 2));
|
|
305
539
|
} else {
|
|
@@ -308,6 +542,9 @@ async function main() {
|
|
|
308
542
|
console.log(`Product family "${familyId}" not found.`);
|
|
309
543
|
} else {
|
|
310
544
|
console.log(`\n📦 Verified Accessories for \x1b[1m${record.host_title}\x1b[0m (${record.family_id}):`);
|
|
545
|
+
if (record.filtered_by_category) {
|
|
546
|
+
console.log(` \x1b[32m[Filtered by category]:\x1b[0m ${record.filtered_by_category}`);
|
|
547
|
+
}
|
|
311
548
|
console.log(` Host Default SKU: ${record.host_sku}`);
|
|
312
549
|
if (record.physical_spec?.power_input) {
|
|
313
550
|
const p = record.physical_spec.power_input;
|
|
@@ -316,7 +553,7 @@ async function main() {
|
|
|
316
553
|
|
|
317
554
|
console.log('\n \x1b[33m[REQUIRED (必需配件)]\x1b[0m');
|
|
318
555
|
if (record.accessories.required.length === 0) {
|
|
319
|
-
console.log(' (
|
|
556
|
+
console.log(' (无符合条件的必需配件)');
|
|
320
557
|
} else {
|
|
321
558
|
record.accessories.required.forEach((acc) => {
|
|
322
559
|
const stat = acc.compatibility.is_compatible ? '\x1b[32m✔\x1b[0m' : '\x1b[31m✖\x1b[0m';
|
|
@@ -328,7 +565,7 @@ async function main() {
|
|
|
328
565
|
|
|
329
566
|
console.log('\n \x1b[36m[OPTIONAL (推荐选配)]\x1b[0m');
|
|
330
567
|
if (record.accessories.optional.length === 0) {
|
|
331
|
-
console.log(' (
|
|
568
|
+
console.log(' (无符合条件的选配项)');
|
|
332
569
|
} else {
|
|
333
570
|
record.accessories.optional.slice(0, 10).forEach((acc) => {
|
|
334
571
|
const stat = acc.compatibility.is_compatible ? '\x1b[32m✔\x1b[0m' : '\x1b[31m✖\x1b[0m';
|
|
@@ -336,6 +573,16 @@ async function main() {
|
|
|
336
573
|
if (acc.url) console.log(` 购买链接: ${acc.url}`);
|
|
337
574
|
});
|
|
338
575
|
}
|
|
576
|
+
|
|
577
|
+
if (record.bundle_cart_url) {
|
|
578
|
+
console.log('\n \x1b[35m[一键批量加购 (Bundle Cart)]\x1b[0m');
|
|
579
|
+
console.log(` 主机与必需配件已打包,点击直达购物车一键结算:`);
|
|
580
|
+
console.log(` \x1b[4m${record.bundle_cart_url}\x1b[0m`);
|
|
581
|
+
} else if (record.domestic_channel_note) {
|
|
582
|
+
console.log('\n \x1b[35m[国内采购渠道说明]\x1b[0m');
|
|
583
|
+
console.log(` ${record.domestic_channel_note}`);
|
|
584
|
+
}
|
|
585
|
+
|
|
339
586
|
console.log('');
|
|
340
587
|
}
|
|
341
588
|
}
|
|
@@ -361,6 +608,35 @@ async function main() {
|
|
|
361
608
|
break;
|
|
362
609
|
}
|
|
363
610
|
|
|
611
|
+
case 'gotchas': {
|
|
612
|
+
const query = flags.positionals.join(' ');
|
|
613
|
+
const results = searchGotchas({ query, chip: flags.chip });
|
|
614
|
+
if (flags.json) {
|
|
615
|
+
console.log(JSON.stringify({
|
|
616
|
+
total: results.length,
|
|
617
|
+
query: query || null,
|
|
618
|
+
chip: flags.chip || null,
|
|
619
|
+
records: results,
|
|
620
|
+
}, null, 2));
|
|
621
|
+
} else {
|
|
622
|
+
console.log(`\n🩺 Edge Hardware Gotchas & Troubleshooting (${results.length} found):\n`);
|
|
623
|
+
if (results.length === 0) {
|
|
624
|
+
console.log(' No matching troubleshooting cards found.');
|
|
625
|
+
} else {
|
|
626
|
+
results.forEach((rec, idx) => {
|
|
627
|
+
console.log(` ${idx + 1}. \x1b[1m[${rec.chip.toUpperCase()}] ${rec.title}\x1b[0m`);
|
|
628
|
+
console.log(` 现象 (Symptom): ${rec.symptoms}`);
|
|
629
|
+
console.log(` 根因 (Root Cause): ${rec.root_cause}`);
|
|
630
|
+
console.log(' \x1b[32m解决方案:\x1b[0m');
|
|
631
|
+
rec.solution.forEach((s) => console.log(` ${s}`));
|
|
632
|
+
if (rec.forbidden) console.log(` \x1b[31m⚠️ 严禁行为:\x1b[0m ${rec.forbidden}`);
|
|
633
|
+
console.log('');
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
break;
|
|
638
|
+
}
|
|
639
|
+
|
|
364
640
|
case 'lockfile': {
|
|
365
641
|
const sub = flags.positionals[0] || 'create';
|
|
366
642
|
if (sub !== 'create') {
|
|
@@ -391,8 +667,18 @@ async function main() {
|
|
|
391
667
|
process.exit(1);
|
|
392
668
|
}
|
|
393
669
|
} catch (err) {
|
|
394
|
-
|
|
395
|
-
|
|
670
|
+
const isSectionError = err?.name === 'HubSectionError';
|
|
671
|
+
const isArgError = err instanceof CliArgumentError || isSectionError;
|
|
672
|
+
if (flags?.json) {
|
|
673
|
+
console.log(JSON.stringify({
|
|
674
|
+
error: err.message,
|
|
675
|
+
...(err.suggestion ? { suggestion: err.suggestion } : {}),
|
|
676
|
+
...(err.allowed_sections ? { allowed_sections: err.allowed_sections } : {}),
|
|
677
|
+
}, null, 2));
|
|
678
|
+
} else {
|
|
679
|
+
console.error(`\n\x1b[31mError:\x1b[0m ${err.message}\n`);
|
|
680
|
+
}
|
|
681
|
+
process.exitCode = isArgError ? 2 : 1;
|
|
396
682
|
}
|
|
397
683
|
}
|
|
398
684
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://spec.seeedstudio.com/schemas/v2/client-config.json",
|
|
3
|
-
"baked_at": "2026-10-
|
|
3
|
+
"baked_at": "2026-10-13T11:34:09.048Z",
|
|
4
4
|
"typesense": {
|
|
5
5
|
"host": "https://rel-search.seeedstudio.com",
|
|
6
6
|
"search_key_solution": "dNsKBp0Pepdz9tvyIY1O92EMOM00XCHv",
|
|
7
7
|
"search_key_reference_design": "rEy4VhZnqEmrduFG2e8uvfWMz5ZzXhjb",
|
|
8
|
-
"search_key_purchase_profile": "
|
|
8
|
+
"search_key_purchase_profile": ""
|
|
9
9
|
}
|
|
10
10
|
}
|