@sstar/embedlink_agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +107 -0
  2. package/dist/.platform +1 -0
  3. package/dist/board/docs.js +59 -0
  4. package/dist/board/notes.js +11 -0
  5. package/dist/board_uart/history.js +81 -0
  6. package/dist/board_uart/index.js +66 -0
  7. package/dist/board_uart/manager.js +313 -0
  8. package/dist/board_uart/resource.js +578 -0
  9. package/dist/board_uart/sessions.js +559 -0
  10. package/dist/config/index.js +341 -0
  11. package/dist/core/activity.js +7 -0
  12. package/dist/core/errors.js +45 -0
  13. package/dist/core/log_stream.js +26 -0
  14. package/dist/files/__tests__/files_manager.test.js +209 -0
  15. package/dist/files/artifact_manager.js +68 -0
  16. package/dist/files/file_operation_logger.js +271 -0
  17. package/dist/files/files_manager.js +511 -0
  18. package/dist/files/index.js +87 -0
  19. package/dist/files/types.js +5 -0
  20. package/dist/firmware/burn_recover.js +733 -0
  21. package/dist/firmware/prepare_images.js +184 -0
  22. package/dist/firmware/user_guide.js +43 -0
  23. package/dist/index.js +449 -0
  24. package/dist/logger.js +245 -0
  25. package/dist/macro/index.js +241 -0
  26. package/dist/macro/runner.js +168 -0
  27. package/dist/nfs/index.js +105 -0
  28. package/dist/plugins/loader.js +30 -0
  29. package/dist/proto/agent.proto +473 -0
  30. package/dist/resources/docs/board-interaction.md +115 -0
  31. package/dist/resources/docs/firmware-upgrade.md +404 -0
  32. package/dist/resources/docs/nfs-mount-guide.md +78 -0
  33. package/dist/resources/docs/tftp-transfer-guide.md +81 -0
  34. package/dist/secrets/index.js +9 -0
  35. package/dist/server/grpc.js +1069 -0
  36. package/dist/server/web.js +2284 -0
  37. package/dist/ssh/adapter.js +126 -0
  38. package/dist/ssh/candidates.js +85 -0
  39. package/dist/ssh/index.js +3 -0
  40. package/dist/ssh/paircheck.js +35 -0
  41. package/dist/ssh/tunnel.js +111 -0
  42. package/dist/tftp/client.js +345 -0
  43. package/dist/tftp/index.js +284 -0
  44. package/dist/tftp/server.js +731 -0
  45. package/dist/uboot/index.js +45 -0
  46. package/dist/ui/assets/index-BlnLVmbt.js +374 -0
  47. package/dist/ui/assets/index-xMbarYXA.css +32 -0
  48. package/dist/ui/index.html +21 -0
  49. package/dist/utils/network.js +150 -0
  50. package/dist/utils/platform.js +83 -0
  51. package/dist/utils/port-check.js +153 -0
  52. package/dist/utils/user-prompt.js +139 -0
  53. package/package.json +64 -0
@@ -0,0 +1,105 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { ErrorCodes, error } from '../core/errors.js';
4
+ function normalizePosixSubpath(subpath) {
5
+ if (!subpath)
6
+ return '';
7
+ const replaced = subpath.replace(/\\/g, '/');
8
+ const parts = replaced.split('/').filter((p) => p && p !== '.' && p !== '..');
9
+ return parts.join('/');
10
+ }
11
+ function resolveUnderRoot(root, subpath) {
12
+ const rel = normalizePosixSubpath(subpath);
13
+ if (!rel)
14
+ return root;
15
+ const segments = rel.split('/');
16
+ return path.join(root, ...segments);
17
+ }
18
+ export async function nfsUpload(rootDir, remoteSubpath, content) {
19
+ try {
20
+ const full = resolveUnderRoot(rootDir, remoteSubpath);
21
+ const dir = path.dirname(full);
22
+ await fs.mkdir(dir, { recursive: true });
23
+ await fs.writeFile(full, content);
24
+ const st = await fs.stat(full);
25
+ return { remoteSubpath, size: st.size };
26
+ }
27
+ catch (e) {
28
+ throw error(ErrorCodes.EL_TFTP_UPLOAD_FAILED, e?.message || String(e));
29
+ }
30
+ }
31
+ export async function nfsDownload(rootDir, remoteSubpath) {
32
+ const full = resolveUnderRoot(rootDir, remoteSubpath);
33
+ try {
34
+ const buf = await fs.readFile(full);
35
+ const st = await fs.stat(full);
36
+ return { content: buf, size: st.size };
37
+ }
38
+ catch (e) {
39
+ throw error(ErrorCodes.EL_TFTP_VERIFY_FAILED, e?.message || String(e));
40
+ }
41
+ }
42
+ export async function nfsList(rootDir, remoteSubpath, mode = 'simple', limit) {
43
+ const entries = [];
44
+ let truncated = false;
45
+ const start = resolveUnderRoot(rootDir, remoteSubpath);
46
+ const maxEntries = limit || 10000; // 默认上限防止内存问题
47
+ // 只返回1层,不递归
48
+ try {
49
+ const isQueryDir = await fs.stat(start).catch(() => null);
50
+ if (isQueryDir?.isDirectory()) {
51
+ // 如果是查询目录,列出其直接子项
52
+ const children = await fs.readdir(start, { withFileTypes: true }).catch(() => []);
53
+ for (const ent of children) {
54
+ if (entries.length >= maxEntries) {
55
+ truncated = true;
56
+ break;
57
+ }
58
+ const childFull = path.join(start, ent.name);
59
+ const st = await fs.stat(childFull).catch(() => null);
60
+ if (!st)
61
+ continue;
62
+ const isDir = st.isDirectory();
63
+ if (mode === 'simple') {
64
+ entries.push({
65
+ name: ent.name, // 只返回当前层级的名称
66
+ isDirectory: isDir,
67
+ });
68
+ }
69
+ else {
70
+ entries.push({
71
+ name: ent.name, // 只返回当前层级的名称
72
+ isDirectory: isDir,
73
+ sizeBytes: isDir ? 0 : st.size,
74
+ modifiedAtMs: st.mtimeMs || 0,
75
+ });
76
+ }
77
+ }
78
+ }
79
+ else {
80
+ // 如果不是目录(例如具体文件),直接返回该文件信息
81
+ const name = path.basename(start);
82
+ if (mode === 'simple') {
83
+ entries.push({
84
+ name: name,
85
+ isDirectory: false,
86
+ });
87
+ }
88
+ else {
89
+ const st = await fs.stat(start).catch(() => null);
90
+ if (st) {
91
+ entries.push({
92
+ name: name,
93
+ isDirectory: false,
94
+ sizeBytes: st.size,
95
+ modifiedAtMs: st.mtimeMs || 0,
96
+ });
97
+ }
98
+ }
99
+ }
100
+ }
101
+ catch {
102
+ // ignore missing dirs or files
103
+ }
104
+ return { entries, truncated };
105
+ }
@@ -0,0 +1,30 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ export async function loadPlugins(dir) {
4
+ const abs = path.isAbsolute(dir) ? dir : path.join(process.cwd(), dir);
5
+ try {
6
+ const items = await fs.readdir(abs);
7
+ const loaded = [];
8
+ for (const f of items) {
9
+ if (!f.endsWith('.js') && !f.endsWith('.mjs'))
10
+ continue;
11
+ try {
12
+ const mod = await import(path.join(abs, f));
13
+ const p = mod.default || mod;
14
+ if (p.init)
15
+ await p.init();
16
+ loaded.push(p);
17
+ }
18
+ catch (e) {
19
+ console.warn('plugin load failed:', f, e);
20
+ }
21
+ }
22
+ for (const p of loaded)
23
+ if (p.start)
24
+ await p.start();
25
+ return loaded;
26
+ }
27
+ catch {
28
+ return [];
29
+ }
30
+ }
@@ -0,0 +1,473 @@
1
+ syntax = "proto3";
2
+
3
+ package embedlink.agent.v1;
4
+
5
+ service AgentService {
6
+ rpc Status (StatusRequest) returns (StatusResponse);
7
+ rpc BoardUartWrite (BoardUartWriteRequest) returns (BoardUartWriteResponse);
8
+ // Host -> Agent: best-effort structured log streaming
9
+ rpc HostLogStream (stream HostLogEntry) returns (HostLogSummary);
10
+ // 板卡 UART 会话接口:多 session + 读写
11
+ rpc BoardUartListSessions (BoardUartListSessionsRequest) returns (BoardUartListSessionsResponse);
12
+ rpc BoardUartSessionWrite (BoardUartSessionWriteRequest) returns (BoardUartSessionWriteResponse);
13
+ rpc BoardUartSessionRead (BoardUartSessionReadRequest) returns (BoardUartSessionReadResponse);
14
+ rpc BoardUartOpenManual (BoardUartOpenManualRequest) returns (BoardUartOpenManualResponse);
15
+ rpc BoardUartCloseSession (BoardUartCloseSessionRequest) returns (BoardUartCloseSessionResponse);
16
+ rpc BoardUartStatus (BoardUartStatusRequest) returns (BoardUartStatusResponse);
17
+ rpc BoardUartForceClose (BoardUartForceCloseRequest) returns (BoardUartForceCloseResponse);
18
+ rpc TftpUpload (TftpUploadRequest) returns (TftpUploadResponse);
19
+ rpc TftpDownload (TftpDownloadRequest) returns (TftpDownloadResponse);
20
+ rpc TftpList (TftpListRequest) returns (TftpListResponse);
21
+ rpc TftpUserguide (TftpUserguideRequest) returns (TftpUserguideResponse);
22
+ rpc UBootBreak (UBootBreakRequest) returns (UBootBreakResponse);
23
+ rpc UBootRunCommand (UBootRunCommandRequest) returns (UBootRunCommandResponse);
24
+ rpc TunnelStart (TunnelStartRequest) returns (TunnelStartResponse);
25
+ rpc TunnelStop (TunnelStopRequest) returns (TunnelStopResponse);
26
+ // Added utility endpoints
27
+ rpc BoardUartListPorts (BoardUartListPortsRequest) returns (BoardUartListPortsResponse);
28
+ rpc ListSshCandidates (ListSshCandidatesRequest) returns (ListSshCandidatesResponse);
29
+ // NFS 文件同步与浏览
30
+ rpc NfsUpload (NfsUploadRequest) returns (NfsUploadResponse);
31
+ rpc NfsDownload (NfsDownloadRequest) returns (NfsDownloadResponse);
32
+ rpc NfsList (NfsListRequest) returns (NfsListResponse);
33
+ rpc NfsInfo (NfsInfoRequest) returns (NfsInfoResponse);
34
+ rpc NfsUserguide (NfsUserguideRequest) returns (NfsUserguideResponse);
35
+ // 板卡环境说明
36
+ rpc BoardNotes (BoardNotesRequest) returns (BoardNotesResponse);
37
+ // 固件镜像准备与 ISP 恢复烧录
38
+ rpc FirmwarePrepareImages (FirmwarePrepareImagesRequest) returns (FirmwarePrepareImagesResponse);
39
+ rpc FirmwareBurnRecover (FirmwareBurnRecoverRequest) returns (FirmwareBurnRecoverResponse);
40
+ rpc FirmwareUserGuide (FirmwareUserGuideRequest) returns (FirmwareUserGuideResponse);
41
+ }
42
+
43
+ // Files Service - 极简文件服务
44
+ service FilesService {
45
+ // 公开接口 - 对应 MCP 工具
46
+ rpc PutPath(stream PutPathChunk) returns (PutPathSummary);
47
+ rpc GetPath(GetPathRequest) returns (stream PathChunk);
48
+
49
+ // 内部接口 - 完整 CRUD 实现,但不暴露为 MCP 工具
50
+ rpc ListFiles(ListFilesRequest) returns (ListFilesResponse);
51
+ rpc RemoveFiles(RemoveFilesRequest) returns (RemoveFilesResponse);
52
+ rpc MakeDirectory(MakeDirectoryRequest) returns (MakeDirectoryResponse);
53
+ rpc GetStat(GetStatRequest) returns (GetStatResponse);
54
+ }
55
+
56
+ // Pairing service: validate that a given endpoint matches the intended Agent instance
57
+ service PairingService {
58
+ rpc PairCheck (PairCheckRequest) returns (PairCheckResponse);
59
+ }
60
+
61
+ message PairCheckRequest {
62
+ string pairCode = 1;
63
+ string clientId = 2;
64
+ int64 timestamp = 3;
65
+ }
66
+
67
+ message PairCheckResponse {
68
+ bool accepted = 1;
69
+ string agentId = 2;
70
+ string version = 3;
71
+ string startupTime = 4;
72
+ string reason = 5;
73
+ }
74
+
75
+ message StatusRequest {}
76
+ message StatusResponse {
77
+ string status = 1;
78
+ string version = 2;
79
+ int64 timestamp = 3;
80
+ repeated NetworkInterfaceInfo networkInterfaces = 4;
81
+ string error = 5;
82
+ // Network mount (CIFS/NFS) helpers for AI orchestration
83
+ string shareUsername = 6;
84
+ string sharePassword = 7;
85
+ string nfsExportPath = 8;
86
+ // Platform information
87
+ string platform = 9;
88
+ string arch = 10;
89
+ }
90
+
91
+ message NetworkInterfaceInfo {
92
+ string ip = 1;
93
+ string netmask = 2;
94
+ string broadcast = 3;
95
+ string gateway = 4;
96
+ string interface = 5;
97
+ bool internal = 6;
98
+ }
99
+
100
+ message BoardUartWriteRequest {
101
+ string sessionId = 1; // Empty means default session
102
+ bytes data = 2;
103
+ bool mock = 3;
104
+ // New fields for Smart Sync
105
+ optional string waitFor = 4;
106
+ optional int64 timeoutMs = 5;
107
+ }
108
+
109
+ message BoardUartWriteResponse {
110
+ string status = 1;
111
+ string output = 2;
112
+ string nextStep = 3;
113
+ int64 elapsedMs = 4;
114
+ int64 lastOutputGapMs = 5;
115
+ uint32 remains = 6; // 此次返回后,缓冲区中剩余待读取的数据字节数
116
+ }
117
+
118
+ // 板卡 UART 会话相关
119
+ message BoardUartSessionInfo {
120
+ string id = 1;
121
+ string label = 2;
122
+ string role = 3;
123
+ bool isDefault = 4;
124
+ bool canWrite = 5;
125
+ }
126
+
127
+ message BoardUartListSessionsRequest {}
128
+ message BoardUartListSessionsResponse { repeated BoardUartSessionInfo sessions = 1; }
129
+
130
+ message BoardUartSessionWriteRequest {
131
+ string sessionId = 1; // 为空表示默认会话
132
+ bytes data = 2;
133
+ }
134
+ message BoardUartSessionWriteResponse {
135
+ uint32 bytesWritten = 1;
136
+ uint32 remains = 2; // 此次返回后,缓冲区中剩余待读取的数据字节数
137
+ }
138
+
139
+ message BoardUartSessionReadRequest {
140
+ string sessionId = 1; // 为空表示默认会话
141
+ uint32 maxBytes = 2;
142
+ uint32 quietMs = 3;
143
+ uint32 timeoutMs = 4;
144
+ }
145
+ message BoardUartSessionReadResponse {
146
+ bytes data = 1;
147
+ uint32 bytes = 2;
148
+ bool timedOut = 3;
149
+ uint32 remains = 4; // 此次返回后,缓冲区中剩余待读取的数据字节数
150
+ }
151
+
152
+ message BoardUartOpenManualRequest {
153
+ string port = 1;
154
+ uint32 baud = 2;
155
+ }
156
+ message BoardUartOpenManualResponse {
157
+ BoardUartSessionInfo session = 1;
158
+ }
159
+
160
+ message BoardUartCloseSessionRequest { string sessionId = 1; }
161
+ message BoardUartCloseSessionResponse { bool closed = 1; }
162
+
163
+ message BoardUartAttachedTool {
164
+ string id = 1;
165
+ string source = 2;
166
+ string sessionId = 3;
167
+ string port = 4;
168
+ uint32 baud = 5;
169
+ int64 attachedAtMs = 6;
170
+ }
171
+
172
+ message BoardUartStatusRequest {}
173
+ message BoardUartStatusResponse {
174
+ string status = 1;
175
+ bool disabled = 2;
176
+ bool hasModule = 3;
177
+ string port = 4;
178
+ uint32 baud = 5;
179
+ repeated BoardUartSessionInfo sessions = 6;
180
+ repeated BoardUartAttachedTool attachedTools = 7;
181
+ bool suspended = 8;
182
+ string suspendOwner = 9;
183
+ int64 suspendedSinceMs = 10;
184
+ int64 maxSuspendMs = 11;
185
+ bool autoResumed = 12;
186
+ }
187
+
188
+ message BoardUartForceCloseRequest {}
189
+ message BoardUartForceCloseResponse { bool ok = 1; }
190
+
191
+ message BoardUartListPortsRequest {}
192
+ message BoardUartPortInfo { string path = 1; string friendlyName = 2; }
193
+ message BoardUartListPortsResponse { repeated BoardUartPortInfo ports = 1; }
194
+
195
+ message TftpUploadRequest { string fileName = 1; bytes content = 2; }
196
+ message TftpUploadResponse { string path = 1; uint64 size = 2; string sha256 = 3; }
197
+
198
+ message TftpDownloadRequest { string path = 1; }
199
+ message TftpDownloadResponse { bytes content = 1; uint64 size = 2; }
200
+
201
+ message TftpListRequest {
202
+ string basePath = 1; // 相对 TFTP 根目录的 POSIX 路径,可为空表示根目录
203
+ uint32 depth = 2; // 递归深度
204
+ }
205
+
206
+
207
+ message TftpListResponse {
208
+ repeated FileEntry entries = 1;
209
+ bool truncated = 2;
210
+ }
211
+
212
+
213
+ message TftpUserguideRequest {}
214
+ message TftpUserguideResponse {
215
+ string content = 1;
216
+ string format = 2;
217
+ }
218
+
219
+ message UBootBreakRequest { string sessionId = 1; uint32 timeoutMs = 2; bool mock = 3; }
220
+ message UBootBreakResponse { bool ok = 1; string details = 2; }
221
+
222
+ message UBootRunCommandRequest { string sessionId = 1; string command = 2; uint32 timeoutMs = 3; bool mock = 4; }
223
+ message UBootRunCommandResponse { bool ok = 1; string output = 2; }
224
+
225
+
226
+ message TunnelStartRequest { string host = 1; uint32 port = 2; string user = 3; string password = 4; string privateKeyPath = 5; uint32 localPort = 6; string forwardHost = 7; uint32 forwardPort = 8; }
227
+ message TunnelStartResponse { bool connected = 1; string details = 2; }
228
+
229
+ message TunnelStopRequest {}
230
+ message TunnelStopResponse { bool stopped = 1; }
231
+
232
+
233
+ message ListSshCandidatesRequest {}
234
+ message SshCandidate { string label = 1; string host = 2; }
235
+ message ListSshCandidatesResponse { repeated SshCandidate hosts = 1; }
236
+
237
+ // NFS 相关
238
+ message NfsUploadRequest { string remoteSubpath = 1; bytes content = 2; }
239
+ message NfsUploadResponse { string remoteSubpath = 1; uint64 size = 2; }
240
+
241
+ message NfsDownloadRequest { string remoteSubpath = 1; }
242
+ message NfsDownloadResponse { bytes content = 1; uint64 size = 2; }
243
+
244
+ enum NfsListMode {
245
+ NFS_LIST_MODE_UNSPECIFIED = 0;
246
+ NFS_LIST_MODE_SIMPLE = 1;
247
+ NFS_LIST_MODE_DETAILED = 2;
248
+ }
249
+
250
+ message NfsListRequest {
251
+ string remoteSubpath = 1;
252
+ NfsListMode mode = 2;
253
+ optional uint32 limit = 3;
254
+ }
255
+ message NfsListResponse {
256
+ repeated FileEntry entries = 1;
257
+ bool truncated = 2;
258
+ }
259
+
260
+ message NfsInfoRequest {}
261
+ message NfsInfoResponse {
262
+ bool enabled = 1;
263
+ string exportName = 2;
264
+ string description = 3;
265
+ }
266
+
267
+ message NfsUserguideRequest {}
268
+ message NfsUserguideResponse {
269
+ string content = 1;
270
+ string format = 2;
271
+ }
272
+
273
+ // 板卡说明文档
274
+ message BoardNotesRequest {
275
+ string board = 1;
276
+ string section = 2;
277
+ }
278
+ message BoardNotesResponse {
279
+ string content = 1;
280
+ string format = 2;
281
+ }
282
+
283
+ // Files 相关消息定义
284
+
285
+ enum FileType {
286
+ FILE_TYPE_UNSPECIFIED = 0;
287
+ FILE_TYPE_FILE = 1;
288
+ FILE_TYPE_DIRECTORY = 2;
289
+ }
290
+
291
+ // 统一路径传输
292
+ message PutPathChunk {
293
+ oneof kind {
294
+ PutPathMeta meta = 1;
295
+ FileContentChunk data = 2;
296
+ }
297
+ }
298
+
299
+ message PutPathMeta {
300
+ FileType src_type = 1;
301
+ string dst_path = 2; // 相对 Agent files 根目录
302
+ uint32 total_files = 3;
303
+ uint64 total_bytes = 4;
304
+ bool overwrite = 5;
305
+ }
306
+
307
+ message FileContentChunk {
308
+ string relative_path = 1; // 目录内的相对路径;单文件时为文件名
309
+ bytes content = 2;
310
+ bool is_file_complete = 3;
311
+ }
312
+
313
+ message PutPathSummary {
314
+ bool success = 1;
315
+ uint32 files_written = 2;
316
+ uint64 bytes_written = 3;
317
+ repeated string failed_files = 4;
318
+ }
319
+
320
+ message GetPathRequest {
321
+ string src_path = 1; // 相对 Agent files 根目录
322
+ bool recursive = 2; // 目录是否递归
323
+ uint32 chunk_size = 3; // 可选分块大小
324
+ bool skip_hidden = 4; // 是否跳过隐藏文件
325
+ }
326
+
327
+ message PathChunk {
328
+ FileType src_type = 1; // FILE_TYPE_FILE 或 FILE_TYPE_DIRECTORY
329
+ string relative_path = 2; // 目录内的相对路径;单文件时为文件名
330
+ bytes content = 3;
331
+ bool is_file_complete = 4; // 当前文件完成
332
+ bool is_directory_complete = 5; // 目录全部完成
333
+ uint32 files_completed = 6; // 已完成文件数
334
+ uint32 total_files = 7; // 总文件数
335
+ }
336
+
337
+ message FileEntry {
338
+ string name = 1;
339
+ string path = 2;
340
+ FileType type = 3;
341
+ uint64 size = 4;
342
+ int64 mtime = 5;
343
+ }
344
+
345
+ message ListFilesRequest {
346
+ optional string path = 1;
347
+ optional bool all = 2;
348
+ optional bool recursive = 3;
349
+ optional string pattern = 4;
350
+ }
351
+
352
+ message ListFilesResponse {
353
+ repeated FileEntry entries = 1;
354
+ uint32 total_entries = 2;
355
+ bool truncated = 3;
356
+ }
357
+
358
+ message RemoveFilesRequest {
359
+ string path = 1;
360
+ optional bool recursive = 2;
361
+ }
362
+
363
+ message RemoveFilesResponse {
364
+ bool success = 1;
365
+ uint32 deleted_count = 2;
366
+ }
367
+
368
+ message MakeDirectoryRequest {
369
+ string path = 1;
370
+ optional bool parents = 2;
371
+ }
372
+
373
+ message MakeDirectoryResponse {
374
+ bool success = 1;
375
+ string path = 2;
376
+ }
377
+
378
+ message GetStatRequest {
379
+ string path = 1;
380
+ }
381
+
382
+ message FileStat {
383
+ string path = 1;
384
+ string name = 2;
385
+ uint64 size = 3;
386
+ bool is_file = 4;
387
+ bool is_directory = 5;
388
+ int64 modified_at = 6;
389
+ optional string permissions = 7;
390
+ }
391
+
392
+ message GetStatResponse {
393
+ bool success = 1;
394
+ FileStat stat = 2;
395
+ }
396
+
397
+ // 固件镜像及 ISP 烧录相关
398
+
399
+ message FirmwarePrepareImagesRequest {
400
+ // 相对 TFTP 根目录的 images 子目录路径,例如 "images_agent" 或 "images_agent/build123"
401
+ string imagesRoot = 1;
402
+ }
403
+
404
+
405
+ message FirmwarePartitionMeta {
406
+ string name = 1;
407
+ // 分区起始地址,字符串形式(例如 "0x0", "0x200000")
408
+ string address = 2;
409
+ uint64 size = 3;
410
+ // 可选的分组名称(例如 "BOOT", "SYSTEM")
411
+ string group = 4;
412
+ }
413
+
414
+ message FirmwareStrategySegment {
415
+ string file = 1;
416
+ string offset = 2;
417
+ }
418
+
419
+ message FirmwareRecommendedStrategy {
420
+ string id = 1;
421
+ string description = 2;
422
+ repeated FirmwareStrategySegment segments = 3;
423
+ }
424
+
425
+ message FirmwareImagesMeta {
426
+ // 整体镜像类型(任意字符串;自动检测失败时为 "unknown")
427
+ string imagesType = 1;
428
+ // 文件列表,每行格式:文件路径 大小
429
+ string files = 2;
430
+ repeated FirmwarePartitionMeta partitions = 3;
431
+ repeated FirmwareRecommendedStrategy recommendedStrategies = 4;
432
+ }
433
+
434
+ message FirmwarePrepareImagesResponse {
435
+ string meta = 1; // 纯字符串格式,包含 TOON 格式分区信息
436
+ }
437
+
438
+ message FirmwareBurnRecoverRequest {
439
+ string imagesRoot = 1;
440
+ // 由 AI 构造的 ISP 工具参数数组(顺序敏感)
441
+ repeated string args = 2;
442
+ int64 timeoutMs = 3;
443
+ bool force = 4;
444
+ }
445
+
446
+ message FirmwareBurnRecoverResponse {
447
+ bool ok = 1;
448
+ int32 exitCode = 2;
449
+ string details = 3;
450
+ string logTail = 4;
451
+ }
452
+
453
+ message FirmwareUserGuideRequest {}
454
+
455
+ message FirmwareUserGuideResponse {
456
+ // Markdown 文本,内部 MAY 包含 JSON fenced block(FSM 模板、语义提示等)
457
+ string json = 1;
458
+ }
459
+
460
+ // Host -> Agent best-effort structured log streaming
461
+ message HostLogEntry {
462
+ int64 ts = 1;
463
+ string level = 2;
464
+ string event = 3;
465
+ // JSON string. Agent may parse it for UI rendering.
466
+ string data_json = 4;
467
+ int32 pid = 5;
468
+ }
469
+
470
+ message HostLogSummary {
471
+ bool accepted = 1;
472
+ string reason = 2;
473
+ }