@seeed-studio/solution-cli 0.3.2 → 0.4.1
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 +161 -113
- package/bin/cli.js +427 -34
- package/catalog/client-config.json +2 -2
- package/catalog/knowledge-core.json +75469 -639
- package/docs/cli-guide.md +195 -227
- package/lib/agent.js +132 -23
- 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 +1140 -103
- package/package.json +4 -3
- package/skills/solution-cli/SKILL.md +120 -28
- 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, searchProducts, 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,45 @@ 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]
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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]
|
|
37
|
+
product Query product families, SKUs, physical interfaces, box contents, and verified accessories
|
|
38
|
+
Usage: solution-cli product search <keyword|model|sku> [--limit 5] [--locale zh|en|ja] [--json]
|
|
39
|
+
solution-cli product query <family_id> [--locale zh|en|ja] [--json]
|
|
40
|
+
solution-cli product accessories <family_id> [--sku <sku>] [--category <cats>] [--compact] [--json]
|
|
34
41
|
solution-cli product verify-compatibility --host <family_id> [--json]
|
|
42
|
+
gotchas Search edge hardware and chip troubleshooting knowledge (RK3588, reCamera, Jetson, Hailo)
|
|
43
|
+
Usage: solution-cli gotchas [keyword] [--chip rk3588|recamera|jetson|hailo] [--json]
|
|
35
44
|
lockfile Create an immutable DeploymentLockfile to isolate deployment execution
|
|
36
45
|
Usage: solution-cli lockfile create --solution <id> [--version <v>] [--hardware <fams>] [--json]
|
|
37
46
|
help Show this help message
|
|
38
47
|
|
|
39
48
|
Options:
|
|
40
|
-
--agent <name|all> Target agent(s): workbuddy, claude, codex, codebuddy, or all
|
|
49
|
+
--agent <name|all> Target agent(s): workbuddy, doubao, claude, codex, codebuddy, antigravity, hermes, hermes:<profile>, or all
|
|
50
|
+
--skills-dir <path> Install skill directly to custom directory (supports any third-party or proprietary Agent)
|
|
41
51
|
-y, --yes Accept default detected selections non-interactively
|
|
42
52
|
--dry-run Simulate operations without making changes to filesystem
|
|
43
53
|
--skip-content Skip pulling IoT solution packages from CDN
|
|
44
54
|
--force Re-download equal/older stable Engine and overwrite existing skill files, including local modifications; never downgrade a newer local Engine
|
|
45
55
|
--json Output machine-readable JSON (supported by all query/diagnostic commands)
|
|
46
|
-
--
|
|
56
|
+
--compact Output token-efficient minified response (omits verbose compatibility dimension trees)
|
|
57
|
+
--locale <zh|en|ja> Language for hub and product results (default: zh)
|
|
47
58
|
--limit <num> Maximum results for search (default: 5)
|
|
48
59
|
--engine-path <path> Use custom engine executable path
|
|
49
60
|
|
|
50
61
|
Examples:
|
|
51
62
|
npx @seeed-studio/solution-cli install
|
|
63
|
+
solution-cli update
|
|
64
|
+
solution-cli agent list
|
|
65
|
+
solution-cli install --skills-dir ~/.cursor/skills -y
|
|
52
66
|
solution-cli hub search "跌倒"
|
|
53
67
|
solution-cli product accessories recomputer_j40
|
|
54
68
|
solution-cli product query sensecap_watcher --json
|
|
@@ -68,14 +82,23 @@ function parseFlags(rawArgs) {
|
|
|
68
82
|
const usedFlags = new Set();
|
|
69
83
|
const flags = {
|
|
70
84
|
agent: null,
|
|
85
|
+
skillsDir: null,
|
|
71
86
|
yes: false,
|
|
72
87
|
dryRun: false,
|
|
73
88
|
skipContent: false,
|
|
74
89
|
force: false,
|
|
75
90
|
json: false,
|
|
91
|
+
compact: false,
|
|
76
92
|
enginePath: null,
|
|
77
93
|
limit: 5,
|
|
94
|
+
page: 1,
|
|
95
|
+
type: null,
|
|
96
|
+
section: 'overview',
|
|
97
|
+
sections: null,
|
|
78
98
|
locale: 'zh',
|
|
99
|
+
preset: null,
|
|
100
|
+
category: null,
|
|
101
|
+
chip: null,
|
|
79
102
|
sku: null,
|
|
80
103
|
host: null,
|
|
81
104
|
accessory: null,
|
|
@@ -111,6 +134,7 @@ function parseFlags(rawArgs) {
|
|
|
111
134
|
};
|
|
112
135
|
|
|
113
136
|
if (valueFlag('--agent', value => { flags.agent = value; })) {
|
|
137
|
+
} else if (valueFlag('--skills-dir', value => { flags.skillsDir = value; })) {
|
|
114
138
|
} else if (a === '-y' || a === '--yes') {
|
|
115
139
|
usedFlags.add('--yes');
|
|
116
140
|
flags.yes = true;
|
|
@@ -126,6 +150,9 @@ function parseFlags(rawArgs) {
|
|
|
126
150
|
} else if (a === '--json') {
|
|
127
151
|
usedFlags.add('--json');
|
|
128
152
|
flags.json = true;
|
|
153
|
+
} else if (a === '--compact') {
|
|
154
|
+
usedFlags.add('--compact');
|
|
155
|
+
flags.compact = true;
|
|
129
156
|
} else if (valueFlag('--engine-path', value => { flags.enginePath = value; })) {
|
|
130
157
|
} else if (valueFlag('--limit', value => {
|
|
131
158
|
if (!/^[1-9]\d*$/.test(value)) throw new CliArgumentError('--limit requires a positive integer');
|
|
@@ -133,6 +160,18 @@ function parseFlags(rawArgs) {
|
|
|
133
160
|
if (!Number.isSafeInteger(limit)) throw new CliArgumentError('--limit is too large');
|
|
134
161
|
flags.limit = limit;
|
|
135
162
|
})) {
|
|
163
|
+
} else if (valueFlag('--page', value => {
|
|
164
|
+
if (!/^[1-9]\d*$/.test(value)) throw new CliArgumentError('--page requires a positive integer');
|
|
165
|
+
const page = Number(value);
|
|
166
|
+
if (!Number.isSafeInteger(page)) throw new CliArgumentError('--page is too large');
|
|
167
|
+
flags.page = page;
|
|
168
|
+
})) {
|
|
169
|
+
} else if (valueFlag('--type', value => { flags.type = value; })) {
|
|
170
|
+
} else if (valueFlag('--section', value => { flags.section = value; })) {
|
|
171
|
+
} else if (valueFlag('--sections', value => { flags.sections = value; })) {
|
|
172
|
+
} else if (valueFlag('--preset', value => { flags.preset = value; })) {
|
|
173
|
+
} else if (valueFlag('--category', value => { flags.category = value; })) {
|
|
174
|
+
} else if (valueFlag('--chip', value => { flags.chip = value; })) {
|
|
136
175
|
} else if (valueFlag('--locale', value => { flags.locale = value; })) {
|
|
137
176
|
} else if (valueFlag('--sku', value => { flags.sku = value; })) {
|
|
138
177
|
} else if (valueFlag('--host', value => { flags.host = value; })) {
|
|
@@ -151,23 +190,42 @@ function parseFlags(rawArgs) {
|
|
|
151
190
|
}
|
|
152
191
|
|
|
153
192
|
function validatePositionals(command, positionals) {
|
|
154
|
-
if (['install', 'uninstall', 'status', 'doctor'].includes(command) && positionals.length > 0) {
|
|
193
|
+
if (['install', 'update', 'uninstall', 'status', 'doctor'].includes(command) && positionals.length > 0) {
|
|
155
194
|
throw new CliArgumentError(`${command} does not accept positional arguments`);
|
|
156
195
|
}
|
|
196
|
+
if (command === 'agent') {
|
|
197
|
+
const sub = positionals[0] || 'list';
|
|
198
|
+
if (sub !== 'list') {
|
|
199
|
+
throw new CliArgumentError(`Unknown agent action "${sub}". Supported: list`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
157
202
|
}
|
|
158
203
|
|
|
159
204
|
function validateCommandFlags(command, flags) {
|
|
160
205
|
const allowed = {
|
|
161
|
-
install: ['--agent', '--yes', '--dry-run', '--skip-content', '--force', '--engine-path'],
|
|
206
|
+
install: ['--agent', '--skills-dir', '--yes', '--dry-run', '--skip-content', '--force', '--engine-path'],
|
|
207
|
+
update: ['--agent', '--skills-dir', '--yes', '--dry-run', '--skip-content', '--force', '--engine-path'],
|
|
162
208
|
uninstall: ['--agent', '--dry-run', '--force'],
|
|
163
209
|
status: ['--json'],
|
|
164
210
|
doctor: ['--json'],
|
|
165
|
-
|
|
166
|
-
|
|
211
|
+
agent: ['--json'],
|
|
212
|
+
hub: ['--json', '--compact', '--locale', '--limit', '--page', '--type', '--section', '--sections', '--preset'],
|
|
213
|
+
product: ['--json', '--compact', '--locale', '--sku', '--host', '--accessory', '--category', '--limit'],
|
|
214
|
+
gotchas: ['--json', '--chip'],
|
|
167
215
|
lockfile: ['--json', '--solution', '--version', '--hardware'],
|
|
168
216
|
help: [],
|
|
169
217
|
};
|
|
170
|
-
|
|
218
|
+
let supported = allowed[command];
|
|
219
|
+
if (command === 'hub') {
|
|
220
|
+
const sub = flags.positionals[0] || 'search';
|
|
221
|
+
const perSubcommand = {
|
|
222
|
+
search: ['--json', '--compact', '--locale', '--limit'],
|
|
223
|
+
list: ['--json', '--compact', '--locale', '--limit', '--page', '--type'],
|
|
224
|
+
get: ['--json', '--compact', '--locale', '--type', '--section', '--sections', '--preset'],
|
|
225
|
+
};
|
|
226
|
+
if (!perSubcommand[sub]) throw new CliArgumentError(`Unknown hub action "${sub}". Supported: search, list, get`);
|
|
227
|
+
supported = perSubcommand[sub];
|
|
228
|
+
}
|
|
171
229
|
if (!supported) return;
|
|
172
230
|
for (const flag of flags.usedFlags) {
|
|
173
231
|
if (!supported.includes(flag)) {
|
|
@@ -176,6 +234,85 @@ function validateCommandFlags(command, flags) {
|
|
|
176
234
|
}
|
|
177
235
|
}
|
|
178
236
|
|
|
237
|
+
function renderSectionOutput(r, section) {
|
|
238
|
+
if (section === 'links') {
|
|
239
|
+
const landingEntries = Object.entries(r.links?.landing || {});
|
|
240
|
+
if (landingEntries.length) landingEntries.forEach(([locale, url]) => console.log(` Landing (${locale}): ${url}`));
|
|
241
|
+
else console.log(' Landing: unavailable');
|
|
242
|
+
console.log(` Wiki: ${r.links?.wiki || 'unavailable'}`);
|
|
243
|
+
console.log(` Repo: ${r.links?.repo || 'unavailable'}`);
|
|
244
|
+
console.log(` Declared documents: ${r.links?.declared_documents?.length ? r.links.declared_documents.join(', ') : 'unavailable'}`);
|
|
245
|
+
} else if (section === 'description') {
|
|
246
|
+
console.log(` Content locale: ${r.description?.content_locale || 'unknown'}`);
|
|
247
|
+
console.log(` Source role: ${r.description?.source_role || 'unavailable'}`);
|
|
248
|
+
console.log(` Source: ${r.description?.source || 'unavailable'}`);
|
|
249
|
+
if (r.description?.body) console.log(`\n${r.description.body}`);
|
|
250
|
+
else console.log(' Content: unavailable (the selected section has no body)');
|
|
251
|
+
} else if (section === 'architecture') {
|
|
252
|
+
console.log(` Content locale: ${r.architecture?.content_locale || 'unknown'}`);
|
|
253
|
+
console.log(` Fallback: ${r.architecture?.fallback ? 'yes' : 'no'}`);
|
|
254
|
+
console.log(` Source role: ${r.architecture?.source_role || 'unavailable'}`);
|
|
255
|
+
if (r.architecture?.source) console.log(` Source: ${r.architecture.source}`);
|
|
256
|
+
if (r.architecture?.intro) console.log(`\n${r.architecture.intro}`);
|
|
257
|
+
(r.architecture?.modules || []).forEach((m) => {
|
|
258
|
+
console.log(` ${m.label || m.id || 'Architecture'}:`);
|
|
259
|
+
if (m.card_image) console.log(` Image: ${m.card_image}`);
|
|
260
|
+
if (m.description) console.log(` ${m.description}`);
|
|
261
|
+
if (m.body) console.log(`\n${m.body}`);
|
|
262
|
+
});
|
|
263
|
+
(r.architecture?.gallery || []).forEach((g) => console.log(` ${g.role || 'Gallery'}: ${g.src}`));
|
|
264
|
+
if (!(r.architecture?.modules?.length || r.architecture?.gallery?.length)) console.log(' Content: unavailable (no architecture modules or gallery items)');
|
|
265
|
+
} else if (section === 'products') {
|
|
266
|
+
console.log(` Content locale: ${r.products_content_locale || 'unknown'}`);
|
|
267
|
+
console.log(` Source role: ${r.products_source_role || 'unavailable'}`);
|
|
268
|
+
if (!Object.keys(r.products || {}).length && !Object.keys(r.bom || {}).length) console.log(' Content: unavailable (no product or BOM entries)');
|
|
269
|
+
console.log(JSON.stringify({ products: r.products || {}, bom: r.bom || {} }, null, 2));
|
|
270
|
+
} else if (section === 'deployment') {
|
|
271
|
+
console.log(` Content locale: ${r.deployment?.content_locale || 'unknown'}`);
|
|
272
|
+
console.log(` Source role: ${r.deployment?.source_role || 'unavailable'}`);
|
|
273
|
+
console.log(` Deployable: ${r.deployment?.deployable ? 'yes' : 'no'}`);
|
|
274
|
+
console.log(` Solution: ${r.deployment?.solution_id || 'unavailable'}`);
|
|
275
|
+
if (r.deployment?.filtered_by_preset) {
|
|
276
|
+
console.log(` \x1b[32mFiltered by Preset:\x1b[0m ${r.deployment.filtered_by_preset}`);
|
|
277
|
+
}
|
|
278
|
+
console.log(` Presets: ${r.deployment?.presets?.length ? r.deployment.presets.map((p) => p.id || p.name || p).join(', ') : 'unavailable'}`);
|
|
279
|
+
console.log(` Guides: ${Object.keys(r.deployment?.guides || {}).length ? JSON.stringify(r.deployment.guides) : 'unavailable'}`);
|
|
280
|
+
console.log(` Source files: ${r.deployment?.source_files?.length ? r.deployment.source_files.join(', ') : 'unavailable'}`);
|
|
281
|
+
if (r.deployment?.guide?.body) {
|
|
282
|
+
console.log(` Guide locale: ${r.deployment.guide.content_locale}`);
|
|
283
|
+
console.log(`\n${r.deployment.guide.body}`);
|
|
284
|
+
} else console.log(' Content: unavailable (no deployment guide body returned)');
|
|
285
|
+
} else if (section === 'playbook') {
|
|
286
|
+
console.log(` \x1b[36m[现场工况诊断手册 (Diagnostic Playbook)]\x1b[0m`);
|
|
287
|
+
console.log(` 领域: ${r.playbook?.domain || r.title}`);
|
|
288
|
+
console.log(` 核心决断维度: ${(r.playbook?.core_dimensions || []).join(' | ')}`);
|
|
289
|
+
console.log('\n \x1b[33m[专家工况诊断追问 (2~3问)]\x1b[0m');
|
|
290
|
+
(r.playbook?.questions || []).forEach((q, qIdx) => {
|
|
291
|
+
console.log(` ${qIdx + 1}. \x1b[1m${q.question}\x1b[0m`);
|
|
292
|
+
if (q.rationale) console.log(` 💡 技术/商务决断理由: ${q.rationale}`);
|
|
293
|
+
});
|
|
294
|
+
} else if (section === 'integration') {
|
|
295
|
+
console.log(` \x1b[36m[系统集成与对接规范 (Integration Contract)]\x1b[0m`);
|
|
296
|
+
console.log(` 支持协议: ${(r.integration?.protocols || []).join(', ').toUpperCase()}`);
|
|
297
|
+
console.log('\n \x1b[33m[输入流与环境要求 (Inputs)]\x1b[0m');
|
|
298
|
+
if ((r.integration?.input_requirements || []).length === 0) {
|
|
299
|
+
console.log(' (根据具体选型接入标准 RTSP/传感器数据流)');
|
|
300
|
+
} else {
|
|
301
|
+
r.integration.input_requirements.forEach((inp) => console.log(` - [${inp.type?.toUpperCase()}] ${inp.description}`));
|
|
302
|
+
}
|
|
303
|
+
console.log('\n \x1b[32m[输出与数据总线 (Outputs)]\x1b[0m');
|
|
304
|
+
(r.integration?.interfaces || []).forEach((iface) => {
|
|
305
|
+
console.log(` - \x1b[1m[${iface.protocol?.toUpperCase()}]\x1b[0m ${iface.topic || iface.endpoint || ''} ${iface.port ? `(Port: ${iface.port})` : ''}`);
|
|
306
|
+
if (iface.description) console.log(` 说明: ${iface.description}`);
|
|
307
|
+
if (iface.payload_example) console.log(` 载荷示例: ${typeof iface.payload_example === 'string' ? iface.payload_example : JSON.stringify(iface.payload_example)}`);
|
|
308
|
+
});
|
|
309
|
+
if (r.integration?.system_ingestion_guide?.length) {
|
|
310
|
+
console.log('\n \x1b[35m[系统快速对接指引]\x1b[0m');
|
|
311
|
+
r.integration.system_ingestion_guide.forEach((g) => console.log(` ${g}`));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
179
316
|
async function main() {
|
|
180
317
|
if (args.some(a => a === '--help' || a === '-h')) {
|
|
181
318
|
printHelp();
|
|
@@ -187,8 +324,9 @@ async function main() {
|
|
|
187
324
|
return;
|
|
188
325
|
}
|
|
189
326
|
|
|
327
|
+
let flags = { json: args.includes('--json') };
|
|
190
328
|
try {
|
|
191
|
-
|
|
329
|
+
flags = parseFlags(args.slice(1));
|
|
192
330
|
validatePositionals(command, flags.positionals);
|
|
193
331
|
validateCommandFlags(command, flags);
|
|
194
332
|
|
|
@@ -202,6 +340,28 @@ async function main() {
|
|
|
202
340
|
await runInstall(flags);
|
|
203
341
|
break;
|
|
204
342
|
|
|
343
|
+
case 'update':
|
|
344
|
+
await runUpdate({ ...flags, yes: flags.usedFlags.has('--yes') ? flags.yes : true });
|
|
345
|
+
break;
|
|
346
|
+
|
|
347
|
+
case 'agent': {
|
|
348
|
+
const list = listAgents();
|
|
349
|
+
if (flags.json) {
|
|
350
|
+
console.log(JSON.stringify({ agents: list }, null, 2));
|
|
351
|
+
} else {
|
|
352
|
+
console.log('\n🤖 Supported AI Agent Targets:\n');
|
|
353
|
+
list.forEach((a, idx) => {
|
|
354
|
+
const detectedTag = a.detected ? '\x1b[32m[Detected]\x1b[0m' : '\x1b[90m[Not Detected]\x1b[0m';
|
|
355
|
+
const installTag = a.installed ? '\x1b[36m(Installed)\x1b[0m' : '\x1b[90m(Not Installed)\x1b[0m';
|
|
356
|
+
console.log(` ${idx + 1}. \x1b[1m${a.name}\x1b[0m (ID: ${a.id}) ${detectedTag} ${installTag}`);
|
|
357
|
+
console.log(` Skills Dir: ${a.skillsDir}`);
|
|
358
|
+
});
|
|
359
|
+
console.log('\n💡 Tip: To install into any custom or proprietary Agent, use:');
|
|
360
|
+
console.log(' solution-cli install --skills-dir <path> -y\n');
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
|
|
205
365
|
case 'status':
|
|
206
366
|
runStatus(flags);
|
|
207
367
|
break;
|
|
@@ -228,32 +388,94 @@ async function main() {
|
|
|
228
388
|
|
|
229
389
|
case 'hub': {
|
|
230
390
|
const sub = flags.positionals[0] || 'search';
|
|
231
|
-
if (
|
|
232
|
-
console.error(`Unknown hub action "${sub}". Supported:
|
|
391
|
+
if (!['search', 'list', 'get'].includes(sub)) {
|
|
392
|
+
console.error(`Unknown hub action "${sub}". Supported: search, list, get`);
|
|
233
393
|
process.exit(1);
|
|
234
394
|
}
|
|
235
|
-
|
|
236
|
-
|
|
395
|
+
if (sub === 'list' && flags.positionals.length > 1) {
|
|
396
|
+
throw new CliArgumentError('hub list does not accept positional IDs; use --type, --page, and --limit');
|
|
397
|
+
}
|
|
398
|
+
if (sub === 'get' && (flags.positionals.length !== 2 || !flags.positionals[1])) {
|
|
399
|
+
throw new CliArgumentError('hub get requires exactly one record ID');
|
|
400
|
+
}
|
|
401
|
+
let dto;
|
|
402
|
+
if (sub === 'list') {
|
|
403
|
+
const listLimit = flags.usedFlags.has('--limit') ? flags.limit : 50;
|
|
404
|
+
dto = listHub({ type: flags.type, locale: flags.locale, page: flags.page, limit: listLimit });
|
|
405
|
+
} else if (sub === 'get') {
|
|
406
|
+
const row = getHubRecord(flags.positionals[1], {
|
|
407
|
+
type: flags.type,
|
|
408
|
+
locale: flags.locale,
|
|
409
|
+
section: flags.section,
|
|
410
|
+
sections: flags.sections,
|
|
411
|
+
preset: flags.preset,
|
|
412
|
+
});
|
|
413
|
+
if (!row) {
|
|
414
|
+
console.error(`Hub record "${flags.positionals[1]}" not found in the bundled catalog.`);
|
|
415
|
+
process.exit(1);
|
|
416
|
+
}
|
|
417
|
+
const core = loadKnowledgeCore();
|
|
418
|
+
dto = {
|
|
419
|
+
records: [row],
|
|
420
|
+
source: 'bundled_manifest',
|
|
421
|
+
freshness: 'UNKNOWN',
|
|
422
|
+
dataset_version: core.dataset_version || null,
|
|
423
|
+
build_time: core.build_time || null,
|
|
424
|
+
provenance: core.provenance || null,
|
|
425
|
+
scope: 'bundled_catalog_snapshot',
|
|
426
|
+
locale: flags.locale,
|
|
427
|
+
warnings: [],
|
|
428
|
+
};
|
|
429
|
+
} else {
|
|
430
|
+
const query = flags.positionals.slice(1).join(' ');
|
|
431
|
+
dto = await searchHub({ query, locale: flags.locale, limit: flags.limit });
|
|
432
|
+
}
|
|
237
433
|
if (flags.json) {
|
|
238
434
|
console.log(JSON.stringify(dto, null, 2));
|
|
239
435
|
} else {
|
|
240
|
-
console.log(`\n🔍 Solutions Hub
|
|
436
|
+
console.log(`\n🔍 Solutions Hub ${sub} (${dto.source}):\n`);
|
|
241
437
|
if (dto.records.length === 0) {
|
|
242
438
|
console.log(' No matching scenarios or reference designs found.');
|
|
243
439
|
} else {
|
|
244
440
|
dto.records.forEach((r, idx) => {
|
|
245
|
-
const tag = r.type === 'scenario' ? '\x1b[36m[Scenario]\x1b[0m' : '\x1b[35m[Reference Design]\x1b[0m';
|
|
441
|
+
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
442
|
console.log(` ${idx + 1}. ${tag} \x1b[1m${r.title}\x1b[0m (${r.id})`);
|
|
443
|
+
if (sub === 'get') {
|
|
444
|
+
const activeSections = r.sections || (r.section ? [r.section] : ['overview']);
|
|
445
|
+
activeSections.forEach((sec) => {
|
|
446
|
+
renderSectionOutput(r, sec);
|
|
447
|
+
});
|
|
448
|
+
if (activeSections.includes('overview') && r.available_sections?.length) {
|
|
449
|
+
console.log('\n \x1b[36m[Available Sections & Status 渐进式目录]:\x1b[0m');
|
|
450
|
+
r.available_sections.forEach((sec) => {
|
|
451
|
+
const status = r.section_status?.[sec] || 'documented';
|
|
452
|
+
const isCurrent = sec === 'overview' ? ' \x1b[32m[Current]\x1b[0m' : '';
|
|
453
|
+
const cmd = sec === 'overview' ? `hub get ${r.id}` : `hub get ${r.id} --section ${sec}${sec === 'deployment' ? ' [--preset <id>]' : ''}`;
|
|
454
|
+
console.log(` - \x1b[1m${sec}\x1b[0m (${status}): ${cmd}${isCurrent}`);
|
|
455
|
+
});
|
|
456
|
+
console.log(' 💡 批量拉取: hub get ' + r.id + ' --sections architecture,integration');
|
|
457
|
+
}
|
|
458
|
+
}
|
|
247
459
|
if (r.summary) console.log(` ${r.summary}`);
|
|
248
|
-
if (r.
|
|
460
|
+
if (r.architecture_preview?.image_url) {
|
|
461
|
+
console.log(` 官方架构图: ${r.architecture_preview.image_url}`);
|
|
462
|
+
if (r.architecture_preview.summary) console.log(` 架构拓扑: ${r.architecture_preview.summary}`);
|
|
463
|
+
}
|
|
464
|
+
if (r.landing_url) console.log(` Landing: ${r.landing_url}`);
|
|
465
|
+
console.log(` Scenario IDs: ${r.scenario_ids?.length ? r.scenario_ids.join(', ') : 'none'}`);
|
|
466
|
+
console.log(` Tech IDs: ${r.technology_ids?.length ? r.technology_ids.join(', ') : 'none'}`);
|
|
467
|
+
console.log(` Reference Design IDs: ${r.reference_designs?.length ? r.reference_designs.join(', ') : 'none'}`);
|
|
468
|
+
if (r.solution_id) console.log(` 可部署内容: ${r.solution_id}`);
|
|
249
469
|
if (r.family_ids?.length) console.log(` 推荐硬件族: ${r.family_ids.join(', ')}`);
|
|
250
470
|
if (r.presets?.length) console.log(` 预设模式: ${r.presets.join(', ')}`);
|
|
251
471
|
if (r.wiki_url) console.log(` 开发 Wiki: ${r.wiki_url}`);
|
|
252
|
-
if (r.
|
|
253
|
-
|
|
254
|
-
r.
|
|
255
|
-
|
|
256
|
-
|
|
472
|
+
if (r.has_playbook) console.log(' \x1b[32m现场工况诊断:\x1b[0m 可用 (使用 hub get ' + r.id + ' --section playbook 查看专业追问)');
|
|
473
|
+
if (r.available_sections?.length) {
|
|
474
|
+
const sectionSummary = r.available_sections.map((s) => {
|
|
475
|
+
const st = r.section_status?.[s];
|
|
476
|
+
return st && st !== 'documented' ? `${s}(${st})` : s;
|
|
477
|
+
}).join(', ');
|
|
478
|
+
console.log(` Available Sections: ${sectionSummary}`);
|
|
257
479
|
}
|
|
258
480
|
console.log('');
|
|
259
481
|
});
|
|
@@ -261,15 +483,115 @@ async function main() {
|
|
|
261
483
|
if (dto.warnings.length) {
|
|
262
484
|
dto.warnings.forEach((w) => console.log(`\x1b[33mNote:\x1b[0m ${w}`));
|
|
263
485
|
}
|
|
486
|
+
if (sub === 'list' && dto.pagination) {
|
|
487
|
+
const next = dto.pagination.next_page;
|
|
488
|
+
console.log(`\nCatalog snapshot: total=${dto.pagination.total} returned=${dto.pagination.returned} has_more=${dto.pagination.has_more} next_page=${next ?? 'none'}`);
|
|
489
|
+
if (dto.pagination.has_more && next) {
|
|
490
|
+
const typeArg = flags.type ? ` --type ${flags.type}` : '';
|
|
491
|
+
console.log(`Next page: solution-cli hub list --locale ${flags.locale} --page ${next} --limit ${dto.pagination.limit}${typeArg}`);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
264
494
|
}
|
|
265
495
|
break;
|
|
266
496
|
}
|
|
267
497
|
|
|
268
498
|
case 'product': {
|
|
269
|
-
const
|
|
270
|
-
const
|
|
499
|
+
const rawFirst = flags.positionals[0];
|
|
500
|
+
const core = loadKnowledgeCore();
|
|
501
|
+
let sub = rawFirst;
|
|
502
|
+
let queryTarget = flags.positionals[1] || flags.host;
|
|
503
|
+
|
|
504
|
+
// Auto routing: if first argument is not a known subcommand keyword
|
|
505
|
+
if (rawFirst !== 'search' && rawFirst !== 'query' && rawFirst !== 'accessories' && rawFirst !== 'verify-compatibility') {
|
|
506
|
+
if (rawFirst && core.product_families?.[rawFirst]) {
|
|
507
|
+
sub = 'query';
|
|
508
|
+
queryTarget = rawFirst;
|
|
509
|
+
} else if (rawFirst) {
|
|
510
|
+
sub = 'search';
|
|
511
|
+
queryTarget = flags.positionals.join(' ');
|
|
512
|
+
} else {
|
|
513
|
+
sub = 'query';
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
const familyId = queryTarget;
|
|
517
|
+
|
|
518
|
+
if (sub === 'search') {
|
|
519
|
+
const q = (rawFirst === 'search' ? flags.positionals.slice(1).join(' ') : queryTarget) || '';
|
|
520
|
+
if (!q.trim()) {
|
|
521
|
+
console.error('Please specify a search query (e.g. "solution-cli product search J4012 Super")');
|
|
522
|
+
process.exit(1);
|
|
523
|
+
}
|
|
524
|
+
const dto = searchProducts({ query: q, locale: flags.locale, limit: flags.limit ? parseInt(flags.limit, 10) : 5 });
|
|
525
|
+
if (flags.json) {
|
|
526
|
+
console.log(JSON.stringify(dto, null, 2));
|
|
527
|
+
} else {
|
|
528
|
+
if (dto.records.length === 0) {
|
|
529
|
+
console.log(`未找到与 "${q}" 匹配的硬件产品。`);
|
|
530
|
+
} else {
|
|
531
|
+
console.log(`\n🔍 找到 ${dto.records.length} 个匹配的硬件产品族:`);
|
|
532
|
+
dto.records.forEach((fam) => {
|
|
533
|
+
console.log(`\n======================================================================`);
|
|
534
|
+
console.log(`📦 \x1b[1m${fam.title}\x1b[0m (Family ID: \x1b[36m${fam.family_id}\x1b[0m)`);
|
|
535
|
+
if (fam.summary) console.log(` 💡 简介: ${fam.summary}`);
|
|
536
|
+
if (fam.interfaces?.length) console.log(` 🔌 板载接口 (Interfaces): ${fam.interfaces.join(', ')}`);
|
|
537
|
+
if (fam.protocols?.length) console.log(` 📡 通信协议 (Protocols): ${fam.protocols.join(', ')}`);
|
|
538
|
+
|
|
539
|
+
if (fam.supported_scenarios?.length) {
|
|
540
|
+
console.log(` 🚀 官方落地方案 (该设备可以干嘛):`);
|
|
541
|
+
fam.supported_scenarios.slice(0, 5).forEach((sc) => {
|
|
542
|
+
const scTitle = sc.title?.[flags.locale] || sc.title?.zh || sc.title?.en || sc.id;
|
|
543
|
+
console.log(` - \x1b[32m${scTitle}\x1b[0m (ID: ${sc.id})`);
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (fam.matched_skus?.length) {
|
|
548
|
+
console.log(` 🎯 具体型号 (SKU) & 开箱配置:`);
|
|
549
|
+
fam.matched_skus.forEach((s) => {
|
|
550
|
+
console.log(` --------------------------------------------------`);
|
|
551
|
+
console.log(` SKU: \x1b[1m${s.sku}\x1b[0m | 型号: ${s.model || '标准款'} | 名称: ${s.name}`);
|
|
552
|
+
if (s.ai_performance) console.log(` ⚡ 算力: ${s.ai_performance}`);
|
|
271
553
|
|
|
272
|
-
|
|
554
|
+
// Power & Readiness summary
|
|
555
|
+
if (s.package_profile?.readiness_summary) {
|
|
556
|
+
const icon = s.package_profile.out_of_the_box_ready ? '\x1b[32m✔ 开箱即用\x1b[0m' : '\x1b[33m⚠️ 无法直接开机/需配件\x1b[0m';
|
|
557
|
+
console.log(` ⚡ 开箱状态: ${icon}`);
|
|
558
|
+
console.log(` ${s.package_profile.readiness_summary}`);
|
|
559
|
+
} else if (s.power.adapter_included === true) {
|
|
560
|
+
console.log(` 🔌 电源适配器: \x1b[32m✔ 随附\x1b[0m (${s.power.adapter_spec || s.power.input_voltage || '标配适配器'})`);
|
|
561
|
+
} else if (s.power.adapter_included === false) {
|
|
562
|
+
console.log(` 🔌 电源适配器: \x1b[31m✖ 不随附\x1b[0m (需要自备或加配: ${s.power.adapter_spec || s.power.input_voltage || '需外接电源'})`);
|
|
563
|
+
} else if (s.power.input_voltage) {
|
|
564
|
+
console.log(` 🔌 供电要求: ${s.power.input_voltage} ${s.power.input_connector || ''}`);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Required accessories if any
|
|
568
|
+
if (s.required_accessories?.length) {
|
|
569
|
+
console.log(` 🔌 必需加配配件:`);
|
|
570
|
+
s.required_accessories.forEach((acc) => {
|
|
571
|
+
console.log(` • \x1b[1m${acc.name}\x1b[0m (SKU: ${acc.sku || 'N/A'})${acc.reason ? ` - ${acc.reason}` : ''}`);
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Package contents
|
|
576
|
+
if (s.package_contents?.length) {
|
|
577
|
+
console.log(` 📦 包装清单 (Box Contents):`);
|
|
578
|
+
s.package_contents.forEach((item) => console.log(` • ${item}`));
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// I/O ports
|
|
582
|
+
if (s.io_ports?.length) {
|
|
583
|
+
const portSummary = s.io_ports.map((p) => `${p.count}x ${p.type}${p.notes ? ` (${p.notes})` : ''}`).join(', ');
|
|
584
|
+
console.log(` 🖥️ 外设端口 (I/O Ports): ${portSummary}`);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (s.url) console.log(` 🔗 购买链接: ${s.url}`);
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
console.log(`\n💡 提示: 查看该设备必需与推荐配件请使用: solution-cli product accessories <family_id>`);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
} else if (sub === 'query') {
|
|
273
595
|
if (!familyId) {
|
|
274
596
|
console.error('Please specify a product family ID (e.g. "solution-cli product query recomputer_j40")');
|
|
275
597
|
process.exit(1);
|
|
@@ -283,9 +605,23 @@ async function main() {
|
|
|
283
605
|
console.log(`Product family "${familyId}" not found.`);
|
|
284
606
|
} else {
|
|
285
607
|
console.log(`\n📦 Product Family: \x1b[1m${fam.title}\x1b[0m (${fam.family_id})`);
|
|
608
|
+
if (fam.summary) console.log(` Summary: ${fam.summary}`);
|
|
286
609
|
console.log(` Default SKU: ${fam.default_sku || 'N/A'}`);
|
|
287
610
|
console.log(` Regions: ${(fam.regions || []).join(', ') || 'Global'}`);
|
|
288
611
|
console.log(` SKUs Available: ${fam.skus_count}`);
|
|
612
|
+
if (fam.interfaces?.length) {
|
|
613
|
+
console.log(` Interfaces: ${fam.interfaces.join(', ')}`);
|
|
614
|
+
}
|
|
615
|
+
if (fam.protocols?.length) {
|
|
616
|
+
console.log(` Protocols: ${fam.protocols.join(', ')}`);
|
|
617
|
+
}
|
|
618
|
+
if (fam.supported_scenarios?.length) {
|
|
619
|
+
console.log(` Supported Scenarios:`);
|
|
620
|
+
fam.supported_scenarios.slice(0, 5).forEach((sc) => {
|
|
621
|
+
const scTitle = sc.title?.[flags.locale] || sc.title?.zh || sc.title?.en || sc.id;
|
|
622
|
+
console.log(` - ${scTitle} (${sc.id})`);
|
|
623
|
+
});
|
|
624
|
+
}
|
|
289
625
|
if (fam.physical_spec?.power_input) {
|
|
290
626
|
const p = fam.physical_spec.power_input;
|
|
291
627
|
console.log(` Power Spec: ${p.voltage_v || ''}V ${p.min_current_a || ''}A (${p.connector}) ${p.polarity || ''}`);
|
|
@@ -299,7 +635,12 @@ async function main() {
|
|
|
299
635
|
console.error('Please specify a product family ID (e.g. "solution-cli product accessories recomputer_j40")');
|
|
300
636
|
process.exit(1);
|
|
301
637
|
}
|
|
302
|
-
const dto = queryAccessories(familyId, {
|
|
638
|
+
const dto = queryAccessories(familyId, {
|
|
639
|
+
sku: flags.sku,
|
|
640
|
+
locale: flags.locale,
|
|
641
|
+
compact: flags.compact,
|
|
642
|
+
category: flags.category,
|
|
643
|
+
});
|
|
303
644
|
if (flags.json) {
|
|
304
645
|
console.log(JSON.stringify(dto, null, 2));
|
|
305
646
|
} else {
|
|
@@ -308,6 +649,9 @@ async function main() {
|
|
|
308
649
|
console.log(`Product family "${familyId}" not found.`);
|
|
309
650
|
} else {
|
|
310
651
|
console.log(`\n📦 Verified Accessories for \x1b[1m${record.host_title}\x1b[0m (${record.family_id}):`);
|
|
652
|
+
if (record.filtered_by_category) {
|
|
653
|
+
console.log(` \x1b[32m[Filtered by category]:\x1b[0m ${record.filtered_by_category}`);
|
|
654
|
+
}
|
|
311
655
|
console.log(` Host Default SKU: ${record.host_sku}`);
|
|
312
656
|
if (record.physical_spec?.power_input) {
|
|
313
657
|
const p = record.physical_spec.power_input;
|
|
@@ -316,7 +660,7 @@ async function main() {
|
|
|
316
660
|
|
|
317
661
|
console.log('\n \x1b[33m[REQUIRED (必需配件)]\x1b[0m');
|
|
318
662
|
if (record.accessories.required.length === 0) {
|
|
319
|
-
console.log(' (
|
|
663
|
+
console.log(' (无符合条件的必需配件)');
|
|
320
664
|
} else {
|
|
321
665
|
record.accessories.required.forEach((acc) => {
|
|
322
666
|
const stat = acc.compatibility.is_compatible ? '\x1b[32m✔\x1b[0m' : '\x1b[31m✖\x1b[0m';
|
|
@@ -328,7 +672,7 @@ async function main() {
|
|
|
328
672
|
|
|
329
673
|
console.log('\n \x1b[36m[OPTIONAL (推荐选配)]\x1b[0m');
|
|
330
674
|
if (record.accessories.optional.length === 0) {
|
|
331
|
-
console.log(' (
|
|
675
|
+
console.log(' (无符合条件的选配项)');
|
|
332
676
|
} else {
|
|
333
677
|
record.accessories.optional.slice(0, 10).forEach((acc) => {
|
|
334
678
|
const stat = acc.compatibility.is_compatible ? '\x1b[32m✔\x1b[0m' : '\x1b[31m✖\x1b[0m';
|
|
@@ -336,6 +680,16 @@ async function main() {
|
|
|
336
680
|
if (acc.url) console.log(` 购买链接: ${acc.url}`);
|
|
337
681
|
});
|
|
338
682
|
}
|
|
683
|
+
|
|
684
|
+
if (record.bundle_cart_url) {
|
|
685
|
+
console.log('\n \x1b[35m[一键批量加购 (Bundle Cart)]\x1b[0m');
|
|
686
|
+
console.log(` 主机与必需配件已打包,点击直达购物车一键结算:`);
|
|
687
|
+
console.log(` \x1b[4m${record.bundle_cart_url}\x1b[0m`);
|
|
688
|
+
} else if (record.domestic_channel_note) {
|
|
689
|
+
console.log('\n \x1b[35m[国内采购渠道说明]\x1b[0m');
|
|
690
|
+
console.log(` ${record.domestic_channel_note}`);
|
|
691
|
+
}
|
|
692
|
+
|
|
339
693
|
console.log('');
|
|
340
694
|
}
|
|
341
695
|
}
|
|
@@ -361,6 +715,35 @@ async function main() {
|
|
|
361
715
|
break;
|
|
362
716
|
}
|
|
363
717
|
|
|
718
|
+
case 'gotchas': {
|
|
719
|
+
const query = flags.positionals.join(' ');
|
|
720
|
+
const results = searchGotchas({ query, chip: flags.chip });
|
|
721
|
+
if (flags.json) {
|
|
722
|
+
console.log(JSON.stringify({
|
|
723
|
+
total: results.length,
|
|
724
|
+
query: query || null,
|
|
725
|
+
chip: flags.chip || null,
|
|
726
|
+
records: results,
|
|
727
|
+
}, null, 2));
|
|
728
|
+
} else {
|
|
729
|
+
console.log(`\n🩺 Edge Hardware Gotchas & Troubleshooting (${results.length} found):\n`);
|
|
730
|
+
if (results.length === 0) {
|
|
731
|
+
console.log(' No matching troubleshooting cards found.');
|
|
732
|
+
} else {
|
|
733
|
+
results.forEach((rec, idx) => {
|
|
734
|
+
console.log(` ${idx + 1}. \x1b[1m[${rec.chip.toUpperCase()}] ${rec.title}\x1b[0m`);
|
|
735
|
+
console.log(` 现象 (Symptom): ${rec.symptoms}`);
|
|
736
|
+
console.log(` 根因 (Root Cause): ${rec.root_cause}`);
|
|
737
|
+
console.log(' \x1b[32m解决方案:\x1b[0m');
|
|
738
|
+
rec.solution.forEach((s) => console.log(` ${s}`));
|
|
739
|
+
if (rec.forbidden) console.log(` \x1b[31m⚠️ 严禁行为:\x1b[0m ${rec.forbidden}`);
|
|
740
|
+
console.log('');
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
|
|
364
747
|
case 'lockfile': {
|
|
365
748
|
const sub = flags.positionals[0] || 'create';
|
|
366
749
|
if (sub !== 'create') {
|
|
@@ -391,8 +774,18 @@ async function main() {
|
|
|
391
774
|
process.exit(1);
|
|
392
775
|
}
|
|
393
776
|
} catch (err) {
|
|
394
|
-
|
|
395
|
-
|
|
777
|
+
const isSectionError = err?.name === 'HubSectionError';
|
|
778
|
+
const isArgError = err instanceof CliArgumentError || isSectionError;
|
|
779
|
+
if (flags?.json) {
|
|
780
|
+
console.log(JSON.stringify({
|
|
781
|
+
error: err.message,
|
|
782
|
+
...(err.suggestion ? { suggestion: err.suggestion } : {}),
|
|
783
|
+
...(err.allowed_sections ? { allowed_sections: err.allowed_sections } : {}),
|
|
784
|
+
}, null, 2));
|
|
785
|
+
} else {
|
|
786
|
+
console.error(`\n\x1b[31mError:\x1b[0m ${err.message}\n`);
|
|
787
|
+
}
|
|
788
|
+
process.exitCode = isArgError ? 2 : 1;
|
|
396
789
|
}
|
|
397
790
|
}
|
|
398
791
|
|