@syllm/brickly-sdk 0.1.0 → 0.1.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 CHANGED
@@ -184,7 +184,7 @@ brickly-typegen
184
184
  普通 JS runtime 可以直接使用生成的 wrapper:
185
185
 
186
186
  ```js
187
- const { BricklyRuntime } = require('./_sdk')
187
+ const { BricklyRuntime } = require('@syllm/brickly-sdk')
188
188
  const { textToolkit } = require('./_generated/deps')
189
189
 
190
190
  const brick = new BricklyRuntime({ brickId: 'com.example.composite' })
@@ -384,10 +384,10 @@ win.onNetwork((event) => {
384
384
  ```bash
385
385
  # 在 Brickly/ 目录下
386
386
  npm run sdk:build
387
- npm run sdk:sync:demo-window-lab # Window API 实验室
388
387
  ```
389
388
 
390
- Brick runtime 通过 `require('./_sdk')` 加载,保持 Brick 自包含、可分发。
389
+ Brick runtime 通过 `require('@syllm/brickly-sdk')` 加载,并在 `runtime/node/package.json`
390
+ 声明依赖。SDK 协议变更后发布新版 npm 包,Brick 升级依赖版本即可。
391
391
 
392
392
  ---
393
393
 
package/dist/api.d.ts CHANGED
@@ -53,6 +53,8 @@ export interface CommandContext {
53
53
  invokeStream<TBrickId extends string = string, TCommandId extends string = string>(brickId: TBrickId, commandId: TCommandId, input?: InvokeInput<TBrickId, TCommandId>, options?: InvokeOptions): AsyncIterable<HostInvokeStreamEvent>;
54
54
  /** 打开跨 Brick 会话。会话关闭前,宿主保持目标 Brick 实例不被回收。 */
55
55
  openSession<TBrickId extends string = string>(brickId: TBrickId, options?: SessionOptions): Promise<BrickSession<TBrickId>>;
56
+ /** 结构化日志(SLF4J 风格),自动绑定当前调用链。 */
57
+ readonly log: LogApi;
56
58
  /** 子窗口与事件 API(与 brick.ui / brick.events 同源,便于在 handler 内调用)。 */
57
59
  readonly ui: UiApi;
58
60
  readonly events: EventsApi;
@@ -60,6 +62,13 @@ export interface CommandContext {
60
62
  /** 由 host.hello.config 注入的当前 Profile 配置快照。 */
61
63
  readonly config: Record<string, unknown>;
62
64
  }
65
+ /** SLF4J 风格的结构化日志接口。 */
66
+ export interface LogApi {
67
+ debug(message: string, fields?: Record<string, unknown>): void;
68
+ info(message: string, fields?: Record<string, unknown>): void;
69
+ warn(message: string, fields?: Record<string, unknown>): void;
70
+ error(message: string, error?: unknown, fields?: Record<string, unknown>): void;
71
+ }
63
72
  export type CommandHandler = (ctx: CommandContext, input: unknown) => unknown | Promise<unknown>;
64
73
  export interface InvokeOptions {
65
74
  /** 目标 Brick Profile ID;不传则由宿主选择目标 Brick 默认 Profile。 */
@@ -176,6 +185,7 @@ export interface EventEnvelope {
176
185
  payload: unknown;
177
186
  sourceBrickId: string;
178
187
  publishedAt: number;
188
+ requestId?: string;
179
189
  }
180
190
  /** 子窗口句柄。close() 调 host.ui.closeWindow,其余方法走 host.ui.callWindow。 */
181
191
  export declare class WindowHandle extends EventEmitter {
@@ -385,6 +395,13 @@ export declare class BricklyRuntime {
385
395
  onReady(handler: () => unknown | Promise<unknown>): this;
386
396
  /** 注册 runtime.shutdown 处理逻辑;返回后 SDK 自动发送 runtime.bye 并退出。 */
387
397
  onShutdown(handler: () => unknown | Promise<unknown>): this;
398
+ /** 结构化日志:发送 runtime.log 到宿主,映射为当前 Trace 下的 Event。 */
399
+ readonly log: {
400
+ debug: (message: string, fields?: Record<string, unknown>) => void;
401
+ info: (message: string, fields?: Record<string, unknown>) => void;
402
+ warn: (message: string, fields?: Record<string, unknown>) => void;
403
+ error: (message: string, error?: unknown, fields?: Record<string, unknown>) => void;
404
+ };
388
405
  /** 子窗口 / UI API。 */
389
406
  readonly ui: UiApi;
390
407
  /** 事件订阅 API。 */
package/dist/api.js CHANGED
@@ -7,9 +7,16 @@ const errors_1 = require("./errors");
7
7
  const runtime_1 = require("./runtime");
8
8
  const protocol_1 = require("./protocol");
9
9
  const commandScope = new node_async_hooks_1.AsyncLocalStorage();
10
+ const eventScope = new node_async_hooks_1.AsyncLocalStorage();
10
11
  function currentCommandScope() {
11
12
  return commandScope.getStore();
12
13
  }
14
+ function currentEventScope() {
15
+ return eventScope.getStore();
16
+ }
17
+ function currentTrace() {
18
+ return currentCommandScope()?.trace ?? currentEventScope()?.trace;
19
+ }
13
20
  class BrickSession {
14
21
  transport;
15
22
  id;
@@ -78,9 +85,11 @@ class WindowHandle extends node_events_1.EventEmitter {
78
85
  if (this.closed) {
79
86
  return Promise.reject(new errors_1.BppError('INVALID_INPUT', `Window ${this.id} already closed`));
80
87
  }
81
- const parentRequestId = currentCommandScope()?.requestId ?? explicitPayloadRequestId(args);
88
+ const parentRequestId = currentCommandScope()?.requestId ??
89
+ currentEventScope()?.requestId ??
90
+ explicitPayloadRequestId(args);
82
91
  if (!parentRequestId) {
83
- return Promise.reject(new errors_1.BppError('PARENT_INVOCATION_REQUIRED', 'webContents.send() must run inside command handler or include payload.requestId'));
92
+ return Promise.reject(new errors_1.BppError('PARENT_INVOCATION_REQUIRED', 'webContents.send() must run inside command/event handler or include payload.requestId'));
84
93
  }
85
94
  return this.transport.hostCall({
86
95
  type: 'host.ui.callWindow',
@@ -458,7 +467,8 @@ class BricklyRuntime {
458
467
  this.brickId = opts.brickId;
459
468
  this.protocolVersion = opts.protocolVersion ?? protocol_1.PROTOCOL_VERSION;
460
469
  this.transport = new runtime_1.BppTransport(opts);
461
- this.transport.on('error', (err) => this.transport.log('transport error:', String(err)));
470
+ this.transport.traceProvider = () => currentTrace();
471
+ this.transport.on('error', (err) => this.transport.logInternal('transport error:', String(err)));
462
472
  this.transport.on('message', (msg) => this.handleHello(msg));
463
473
  this.transport.on('command', (msg) => this.handleCommand(msg));
464
474
  this.transport.on('event', (msg) => this.handleEvent(msg));
@@ -480,6 +490,13 @@ class BricklyRuntime {
480
490
  this.shutdownHandler = handler;
481
491
  return this;
482
492
  }
493
+ /** 结构化日志:发送 runtime.log 到宿主,映射为当前 Trace 下的 Event。 */
494
+ log = {
495
+ debug: (message, fields) => this.transport.debug(message, fields),
496
+ info: (message, fields) => this.transport.info(message, fields),
497
+ warn: (message, fields) => this.transport.warn(message, fields),
498
+ error: (message, error, fields) => this.transport.error(message, error, fields)
499
+ };
483
500
  /** 子窗口 / UI API。 */
484
501
  ui = {
485
502
  createBrowserWindow: async (url, options) => {
@@ -769,6 +786,7 @@ class BricklyRuntime {
769
786
  transport.on('message', onMessage);
770
787
  transport.once('end', onEnd);
771
788
  transport.once('error', onError);
789
+ const trace = currentTrace();
772
790
  transport.send({
773
791
  type: 'host.invoke',
774
792
  id,
@@ -777,7 +795,8 @@ class BricklyRuntime {
777
795
  input,
778
796
  parentRequestId,
779
797
  stream: true,
780
- ...(options?.profileId ? { profileId: options.profileId } : {})
798
+ ...(options?.profileId ? { profileId: options.profileId } : {}),
799
+ ...(trace ? { trace } : {})
781
800
  });
782
801
  return {
783
802
  [Symbol.asyncIterator]() {
@@ -820,7 +839,7 @@ class BricklyRuntime {
820
839
  if (this.readyHandler) {
821
840
  Promise.resolve()
822
841
  .then(() => this.readyHandler?.())
823
- .catch((err) => this.transport.log('onReady error:', errors_1.BppError.from(err).message));
842
+ .catch((err) => this.transport.logInternal('onReady error:', errors_1.BppError.from(err).message));
824
843
  }
825
844
  }
826
845
  handleCommand(msg) {
@@ -832,7 +851,7 @@ class BricklyRuntime {
832
851
  fn();
833
852
  }
834
853
  catch (err) {
835
- this.transport.log('cancel handler error:', errors_1.BppError.from(err).message);
854
+ this.transport.logInternal('cancel handler error:', errors_1.BppError.from(err).message);
836
855
  }
837
856
  }
838
857
  return;
@@ -854,7 +873,7 @@ class BricklyRuntime {
854
873
  }
855
874
  const ctx = this.buildContext(reqId, msg.commandId, msg.invocation);
856
875
  Promise.resolve()
857
- .then(() => commandScope.run({ requestId: reqId }, () => handler(ctx, msg.input)))
876
+ .then(() => commandScope.run({ requestId: reqId, trace: msg.trace }, () => handler(ctx, msg.input)))
858
877
  .then((result) => {
859
878
  this.transport.send({ type: 'command.result', id: reqId, result });
860
879
  })
@@ -874,30 +893,37 @@ class BricklyRuntime {
874
893
  event: msg.event,
875
894
  payload: msg.payload,
876
895
  sourceBrickId: msg.sourceBrickId,
877
- publishedAt: msg.publishedAt
896
+ publishedAt: msg.publishedAt,
897
+ requestId: msg.requestId
878
898
  };
879
- // 转发到 WindowHandle:window.* 事件如果 payload.windowId 命中已知窗口,路由到句柄。
880
- if (msg.event.startsWith('window.')) {
881
- const wid = msg.payload?.windowId;
882
- if (typeof wid === 'number') {
883
- const handle = this.windows.get(wid);
884
- if (handle) {
885
- const subEvent = msg.event.slice('window.'.length);
886
- handle._emit(subEvent, msg.payload);
899
+ const dispatch = () => {
900
+ // 转发到 WindowHandle:window.* 事件如果 payload.windowId 命中已知窗口,路由到句柄。
901
+ if (msg.event.startsWith('window.')) {
902
+ const wid = msg.payload?.windowId;
903
+ if (typeof wid === 'number') {
904
+ const handle = this.windows.get(wid);
905
+ if (handle) {
906
+ const subEvent = msg.event.slice('window.'.length);
907
+ handle._emit(subEvent, msg.payload);
908
+ }
887
909
  }
888
910
  }
889
- }
890
- const set = this.eventListeners.get(msg.event);
891
- if (set) {
892
- for (const fn of set) {
893
- try {
894
- fn(msg.payload, env);
895
- }
896
- catch (err) {
897
- this.transport.log('event handler error:', errors_1.BppError.from(err).message);
911
+ const set = this.eventListeners.get(msg.event);
912
+ if (set) {
913
+ for (const fn of set) {
914
+ try {
915
+ fn(msg.payload, env);
916
+ }
917
+ catch (err) {
918
+ this.transport.logInternal('event handler error:', errors_1.BppError.from(err).message);
919
+ }
898
920
  }
899
921
  }
900
- }
922
+ };
923
+ if (msg.requestId)
924
+ eventScope.run({ requestId: msg.requestId, trace: msg.trace }, dispatch);
925
+ else
926
+ dispatch();
901
927
  }
902
928
  async handleShutdown() {
903
929
  if (this.exiting)
@@ -908,7 +934,7 @@ class BricklyRuntime {
908
934
  await this.shutdownHandler();
909
935
  }
910
936
  catch (err) {
911
- this.transport.log('shutdown handler error:', errors_1.BppError.from(err).message);
937
+ this.transport.logInternal('shutdown handler error:', errors_1.BppError.from(err).message);
912
938
  }
913
939
  this.transport.send({ type: 'runtime.bye' });
914
940
  // 留一点时间把字节冲出去再退出
@@ -918,13 +944,13 @@ class BricklyRuntime {
918
944
  if (this.exiting)
919
945
  return;
920
946
  this.exiting = true;
921
- this.transport.log('host disconnected; exiting brick runtime');
947
+ this.transport.logInternal('host disconnected; exiting brick runtime');
922
948
  try {
923
949
  if (this.shutdownHandler)
924
950
  await this.shutdownHandler();
925
951
  }
926
952
  catch (err) {
927
- this.transport.log('host disconnect cleanup error:', errors_1.BppError.from(err).message);
953
+ this.transport.logInternal('host disconnect cleanup error:', errors_1.BppError.from(err).message);
928
954
  }
929
955
  setTimeout(() => process.exit(0), 50).unref?.();
930
956
  }
@@ -985,6 +1011,7 @@ class BricklyRuntime {
985
1011
  invoke,
986
1012
  invokeStream,
987
1013
  openSession,
1014
+ log: this.log,
988
1015
  ui: this.ui,
989
1016
  events: this.events,
990
1017
  platform: this.platform,
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export { BricklyRuntime, BrickSession, WindowHandle } from './api';
2
- export type { BricklyRuntimeOptions, CommandMap, CommandContext, CommandHandler, EventEnvelope, EventsApi, InvokeOptions, PlatformApi, SessionOptions, UiApi } from './api';
2
+ export type { BricklyRuntimeOptions, CommandMap, CommandContext, CommandHandler, EventEnvelope, EventsApi, InvokeOptions, LogApi, PlatformApi, SessionOptions, UiApi } from './api';
3
3
  export { BppTransport } from './runtime';
4
4
  export type { BppTransportOptions } from './runtime';
5
5
  export { BppError, payloadToError } from './errors';
6
- export { PROTOCOL_VERSION, type BppMessage, type BppMessageType, type BridgeErrorCode, type BridgeErrorPayload, type BrickUiWindowOptions, type BrickWindowMethod, type CommandInvocationContext, type HostResourceRegisterPayload, type PlatformSystemPathName } from './protocol';
6
+ export { PROTOCOL_VERSION, type BppMessage, type BppMessageType, type BridgeErrorCode, type BridgeErrorPayload, type BrickUiWindowOptions, type BrickWindowMethod, type CommandInvocationContext, type HostResourceRegisterPayload, type PlatformSystemPathName, type TraceContext } from './protocol';
7
7
  export { generateDependencyTypes } from './typegen';
8
8
  export type { TypegenOptions, TypegenResult } from './typegen';
@@ -247,6 +247,10 @@ export interface BrickWindowNetworkEvent {
247
247
  }
248
248
  /** host.ui.callWindow 方法白名单。 */
249
249
  export type BrickWindowMethod = 'setBounds' | 'getBounds' | 'setPosition' | 'getPosition' | 'setSize' | 'getSize' | 'setOpacity' | 'getOpacity' | 'setAlwaysOnTop' | 'isAlwaysOnTop' | 'setIgnoreMouseEvents' | 'setSkipTaskbar' | 'setTitle' | 'getTitle' | 'setResizable' | 'setMovable' | 'setFocusable' | 'setHasShadow' | 'setBackgroundColor' | 'setVisibleOnAllWorkspaces' | 'minimize' | 'maximize' | 'unmaximize' | 'restore' | 'hide' | 'show' | 'showInactive' | 'focus' | 'blur' | 'close' | 'isDestroyed' | 'isVisible' | 'isFocused' | 'isMinimized' | 'isMaximized' | 'loadURL' | 'loadFile' | 'reload' | 'webContents.send' | 'webContents.executeJavaScript' | 'webContents.openDevTools' | 'webContents.closeDevTools' | 'setContentBounds' | 'getContentBounds' | 'setContentSize' | 'getContentSize' | 'setMinimumSize' | 'getMinimumSize' | 'setMaximumSize' | 'getMaximumSize' | 'setAspectRatio' | 'center' | 'moveTop' | 'moveAbove' | 'setFullScreen' | 'isFullScreen' | 'isNormal' | 'isModal' | 'destroy' | 'getNormalBounds' | 'isResizable' | 'isMovable' | 'isFocusable' | 'setMinimizable' | 'isMinimizable' | 'setMaximizable' | 'isMaximizable' | 'setClosable' | 'isClosable' | 'setFullScreenable' | 'isFullScreenable' | 'setEnabled' | 'isEnabled' | 'hasShadow' | 'isVisibleOnAllWorkspaces' | 'setKiosk' | 'isKiosk' | 'flashFrame' | 'setProgressBar' | 'setMenuBarVisibility' | 'isMenuBarVisible' | 'setAutoHideMenuBar' | 'isMenuBarAutoHide' | 'removeMenu' | 'invalidateShadow' | 'setRepresentedFilename' | 'getRepresentedFilename' | 'setDocumentEdited' | 'isDocumentEdited' | 'webContents.toggleDevTools' | 'webContents.isDevToolsOpened' | 'webContents.goBack' | 'webContents.goForward' | 'webContents.canGoBack' | 'webContents.canGoForward' | 'webContents.getURL' | 'webContents.getTitle' | 'webContents.setZoomFactor' | 'webContents.getZoomFactor' | 'webContents.setZoomLevel' | 'webContents.getZoomLevel' | 'webContents.copy' | 'webContents.paste' | 'webContents.cut' | 'webContents.selectAll' | 'webContents.undo' | 'webContents.redo';
250
+ export interface TraceContext {
251
+ traceId: string;
252
+ parentSpanId?: string;
253
+ }
250
254
  export type BppMessage = {
251
255
  type: 'host.hello';
252
256
  protocolVersion: string;
@@ -267,19 +271,24 @@ export type BppMessage = {
267
271
  payload?: unknown;
268
272
  sourceBrickId: string;
269
273
  publishedAt: number;
274
+ requestId?: string;
275
+ trace?: TraceContext;
270
276
  } | {
271
277
  type: 'host.event.publish';
272
278
  id: string;
273
279
  event: string;
274
280
  payload?: unknown;
281
+ trace?: TraceContext;
275
282
  } | {
276
283
  type: 'host.resource.register';
277
284
  id: string;
278
285
  resource: HostResourceRegisterPayload;
286
+ trace?: TraceContext;
279
287
  } | {
280
288
  type: 'host.resource.get';
281
289
  id: string;
282
290
  resourceId: string;
291
+ trace?: TraceContext;
283
292
  } | {
284
293
  type: 'host.invoke';
285
294
  id: string;
@@ -289,6 +298,7 @@ export type BppMessage = {
289
298
  parentRequestId: string;
290
299
  profileId?: string;
291
300
  stream?: boolean;
301
+ trace?: TraceContext;
292
302
  } | {
293
303
  type: 'host.invokeRoot';
294
304
  id: string;
@@ -296,6 +306,7 @@ export type BppMessage = {
296
306
  commandId: string;
297
307
  input: unknown;
298
308
  profileId?: string;
309
+ trace?: TraceContext;
299
310
  } | {
300
311
  type: 'host.invoke.progress';
301
312
  id: string;
@@ -316,6 +327,7 @@ export type BppMessage = {
316
327
  id: string;
317
328
  brickId: string;
318
329
  profileId?: string;
330
+ trace?: TraceContext;
319
331
  } | {
320
332
  type: 'host.session.invoke';
321
333
  id: string;
@@ -323,10 +335,12 @@ export type BppMessage = {
323
335
  commandId: string;
324
336
  input: unknown;
325
337
  parentRequestId: string;
338
+ trace?: TraceContext;
326
339
  } | {
327
340
  type: 'host.session.close';
328
341
  id: string;
329
342
  sessionId: string;
343
+ trace?: TraceContext;
330
344
  } | {
331
345
  type: 'host.result';
332
346
  id: string;
@@ -341,6 +355,7 @@ export type BppMessage = {
341
355
  commandId: string;
342
356
  input: unknown;
343
357
  invocation?: CommandInvocationContext;
358
+ trace?: TraceContext;
344
359
  } | {
345
360
  type: 'command.cancel';
346
361
  id: string;
@@ -373,6 +388,7 @@ export type BppMessage = {
373
388
  url: string;
374
389
  options?: BrickUiWindowOptions;
375
390
  parentWindowId?: number;
391
+ trace?: TraceContext;
376
392
  } | {
377
393
  type: 'host.ui.callWindow';
378
394
  id: string;
@@ -380,151 +396,198 @@ export type BppMessage = {
380
396
  method: BrickWindowMethod;
381
397
  args?: unknown[];
382
398
  parentRequestId?: string;
399
+ trace?: TraceContext;
383
400
  } | {
384
401
  type: 'host.ui.closeWindow';
385
402
  id: string;
386
403
  windowId: number;
404
+ trace?: TraceContext;
387
405
  } | {
388
406
  type: 'host.ui.listWindows';
389
407
  id: string;
408
+ trace?: TraceContext;
390
409
  } | {
391
410
  type: 'host.platform.input.keyboardTap';
392
411
  id: string;
393
412
  payload: PlatformInputKeyboardTapPayload;
413
+ trace?: TraceContext;
394
414
  } | {
395
415
  type: 'host.platform.input.mouseMove';
396
416
  id: string;
397
417
  payload: PlatformInputMousePointPayload;
418
+ trace?: TraceContext;
398
419
  } | {
399
420
  type: 'host.platform.input.mouseClick';
400
421
  id: string;
401
422
  payload: PlatformInputMousePointPayload;
423
+ trace?: TraceContext;
402
424
  } | {
403
425
  type: 'host.platform.input.mouseDoubleClick';
404
426
  id: string;
405
427
  payload: PlatformInputMousePointPayload;
428
+ trace?: TraceContext;
406
429
  } | {
407
430
  type: 'host.platform.input.mouseRightClick';
408
431
  id: string;
409
432
  payload: PlatformInputMousePointPayload;
433
+ trace?: TraceContext;
410
434
  } | {
411
435
  type: 'host.platform.clipboard.readContent';
412
436
  id: string;
437
+ trace?: TraceContext;
413
438
  } | {
414
439
  type: 'host.platform.clipboard.setContent';
415
440
  id: string;
416
441
  content: PlatformClipboardContent;
442
+ trace?: TraceContext;
417
443
  } | {
418
444
  type: 'host.platform.screenshot.selectRegion';
419
445
  id: string;
420
446
  options?: PlatformScreenshotRegionOptions;
447
+ trace?: TraceContext;
421
448
  } | {
422
449
  type: 'host.platform.screen.captureRegion';
423
450
  id: string;
424
451
  options?: PlatformScreenCaptureOptions;
452
+ trace?: TraceContext;
425
453
  } | {
426
454
  type: 'host.platform.screen.pickColor';
427
455
  id: string;
428
456
  options?: PlatformScreenColorPickOptions;
457
+ trace?: TraceContext;
429
458
  } | {
430
459
  type: 'host.platform.screen.getPrimaryDisplay';
431
460
  id: string;
461
+ trace?: TraceContext;
432
462
  } | {
433
463
  type: 'host.platform.screen.getAllDisplays';
434
464
  id: string;
465
+ trace?: TraceContext;
435
466
  } | {
436
467
  type: 'host.platform.screen.getCursorScreenPoint';
437
468
  id: string;
469
+ trace?: TraceContext;
438
470
  } | {
439
471
  type: 'host.platform.screen.getDisplayNearestPoint';
440
472
  id: string;
441
473
  point: PlatformScreenPoint;
474
+ trace?: TraceContext;
442
475
  } | {
443
476
  type: 'host.platform.screen.getDisplayMatching';
444
477
  id: string;
445
478
  rect: PlatformScreenRect;
479
+ trace?: TraceContext;
446
480
  } | {
447
481
  type: 'host.platform.screen.screenToDipPoint';
448
482
  id: string;
449
483
  point: PlatformScreenPoint;
484
+ trace?: TraceContext;
450
485
  } | {
451
486
  type: 'host.platform.screen.dipToScreenPoint';
452
487
  id: string;
453
488
  point: PlatformScreenPoint;
489
+ trace?: TraceContext;
454
490
  } | {
455
491
  type: 'host.platform.screen.screenToDipRect';
456
492
  id: string;
457
493
  rect: PlatformScreenRect;
494
+ trace?: TraceContext;
458
495
  } | {
459
496
  type: 'host.platform.screen.dipToScreenRect';
460
497
  id: string;
461
498
  rect: PlatformScreenRect;
499
+ trace?: TraceContext;
462
500
  } | {
463
501
  type: 'host.platform.screen.desktopCaptureSources';
464
502
  id: string;
465
503
  options: PlatformScreenDesktopCaptureOptions;
504
+ trace?: TraceContext;
466
505
  } | {
467
506
  type: 'host.platform.system.showNotification';
468
507
  id: string;
469
508
  body: string;
470
509
  clickFeatureCode?: string;
510
+ trace?: TraceContext;
471
511
  } | {
472
512
  type: 'host.platform.system.shellOpenPath';
473
513
  id: string;
474
514
  fullPath: string;
515
+ trace?: TraceContext;
475
516
  } | {
476
517
  type: 'host.platform.system.shellTrashItem';
477
518
  id: string;
478
519
  fullPath: string;
520
+ trace?: TraceContext;
479
521
  } | {
480
522
  type: 'host.platform.system.shellShowItemInFolder';
481
523
  id: string;
482
524
  fullPath: string;
525
+ trace?: TraceContext;
483
526
  } | {
484
527
  type: 'host.platform.system.shellOpenExternal';
485
528
  id: string;
486
529
  url: string;
530
+ trace?: TraceContext;
487
531
  } | {
488
532
  type: 'host.platform.system.shellBeep';
489
533
  id: string;
534
+ trace?: TraceContext;
490
535
  } | {
491
536
  type: 'host.platform.system.getNativeId';
492
537
  id: string;
538
+ trace?: TraceContext;
493
539
  } | {
494
540
  type: 'host.platform.system.getAppName';
495
541
  id: string;
542
+ trace?: TraceContext;
496
543
  } | {
497
544
  type: 'host.platform.system.getAppVersion';
498
545
  id: string;
546
+ trace?: TraceContext;
499
547
  } | {
500
548
  type: 'host.platform.system.getPath';
501
549
  id: string;
502
550
  name: PlatformSystemPathName;
551
+ trace?: TraceContext;
503
552
  } | {
504
553
  type: 'host.platform.system.getFileIcon';
505
554
  id: string;
506
555
  filePath: string;
556
+ trace?: TraceContext;
507
557
  } | {
508
558
  type: 'host.platform.system.readCurrentFolderPath';
509
559
  id: string;
560
+ trace?: TraceContext;
510
561
  } | {
511
562
  type: 'host.platform.system.readCurrentBrowserUrl';
512
563
  id: string;
564
+ trace?: TraceContext;
513
565
  } | {
514
566
  type: 'host.platform.system.isDev';
515
567
  id: string;
568
+ trace?: TraceContext;
516
569
  } | {
517
570
  type: 'host.platform.system.isMacOS';
518
571
  id: string;
572
+ trace?: TraceContext;
519
573
  } | {
520
574
  type: 'host.platform.system.isWindows';
521
575
  id: string;
576
+ trace?: TraceContext;
522
577
  } | {
523
578
  type: 'host.platform.system.isLinux';
524
579
  id: string;
580
+ trace?: TraceContext;
525
581
  } | {
526
582
  type: 'runtime.shutdown';
527
583
  } | {
528
584
  type: 'runtime.bye';
585
+ } | {
586
+ type: 'runtime.log';
587
+ level: 'debug' | 'info' | 'warn' | 'error';
588
+ message: string;
589
+ fields?: Record<string, unknown>;
590
+ error?: BridgeErrorPayload;
591
+ trace?: TraceContext;
529
592
  };
530
593
  export type BppMessageType = BppMessage['type'];
package/dist/runtime.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { EventEmitter } from 'node:events';
2
2
  import type { Readable, Writable } from 'node:stream';
3
- import type { BppMessage } from './protocol';
3
+ import type { BppMessage, TraceContext } from './protocol';
4
4
  export interface BppTransportOptions {
5
5
  /** Brick id,用于生成请求 id 前缀与 runtime.ready 身份。 */
6
6
  brickId: string;
@@ -37,6 +37,8 @@ export declare class BppTransport extends EventEmitter {
37
37
  private idCounter;
38
38
  private started;
39
39
  private pending;
40
+ /** 返回当前异步上下文中的 TraceContext,由上层 api.ts 设置。 */
41
+ traceProvider?: () => TraceContext | undefined;
40
42
  constructor(opts: BppTransportOptions);
41
43
  /** 启动 stdin 监听。重复调用安全。 */
42
44
  start(): void;
@@ -44,8 +46,20 @@ export declare class BppTransport extends EventEmitter {
44
46
  stop(reason?: string): void;
45
47
  /** 写一条 BPP 消息到 stdout。线程安全(Node 单线程,单次 write 是原子的)。 */
46
48
  send(message: BppMessage): void;
47
- /** 写 stderr 日志。stdout 永远只写协议;Brick 身份由宿主日志中心附加。 */
48
- log(...parts: unknown[]): void;
49
+ /**
50
+ * 结构化日志:发送 runtime.log BPP 消息到宿主,映射为当前 Trace 下的 Event。
51
+ * 有调用链上下文时自动绑定 trace;无上下文时宿主自建 noise Trace。
52
+ */
53
+ debug(message: string, fields?: Record<string, unknown>): void;
54
+ info(message: string, fields?: Record<string, unknown>): void;
55
+ warn(message: string, fields?: Record<string, unknown>): void;
56
+ error(message: string, error?: unknown, fields?: Record<string, unknown>): void;
57
+ /**
58
+ * 内部诊断日志(SDK 自身错误),直接写 stderr。
59
+ * 供协议尚未建立或 transport 内部异常时使用。
60
+ */
61
+ logInternal(...parts: unknown[]): void;
62
+ private sendLog;
49
63
  /**
50
64
  * 发起一个需要等待 host.result/host.error 回复的 host.* 调用。
51
65
  * 入参省略 id(SDK 自动分配),返回 result 字段。
package/dist/runtime.js CHANGED
@@ -24,6 +24,8 @@ class BppTransport extends node_events_1.EventEmitter {
24
24
  idCounter = 0;
25
25
  started = false;
26
26
  pending = new Map();
27
+ /** 返回当前异步上下文中的 TraceContext,由上层 api.ts 设置。 */
28
+ traceProvider;
27
29
  constructor(opts) {
28
30
  super();
29
31
  this.brickId = opts.brickId;
@@ -66,23 +68,60 @@ class BppTransport extends node_events_1.EventEmitter {
66
68
  const line = JSON.stringify(message) + '\n';
67
69
  this.stdout.write(line);
68
70
  }
69
- /** 写 stderr 日志。stdout 永远只写协议;Brick 身份由宿主日志中心附加。 */
70
- log(...parts) {
71
+ /**
72
+ * 结构化日志:发送 runtime.log BPP 消息到宿主,映射为当前 Trace 下的 Event。
73
+ * 有调用链上下文时自动绑定 trace;无上下文时宿主自建 noise Trace。
74
+ */
75
+ debug(message, fields) {
76
+ this.sendLog('debug', message, fields);
77
+ }
78
+ info(message, fields) {
79
+ this.sendLog('info', message, fields);
80
+ }
81
+ warn(message, fields) {
82
+ this.sendLog('warn', message, fields);
83
+ }
84
+ error(message, error, fields) {
85
+ this.sendLog('error', message, fields, error);
86
+ }
87
+ /**
88
+ * 内部诊断日志(SDK 自身错误),直接写 stderr。
89
+ * 供协议尚未建立或 transport 内部异常时使用。
90
+ */
91
+ logInternal(...parts) {
71
92
  const line = `${parts.map((p) => (typeof p === 'string' ? p : JSON.stringify(p))).join(' ')}\n`;
72
93
  this.stderr.write(line);
73
94
  }
95
+ sendLog(level, message, fields, error) {
96
+ const msg = { type: 'runtime.log', level, message };
97
+ const trace = this.traceProvider?.();
98
+ if (trace)
99
+ msg.trace = trace;
100
+ if (fields)
101
+ msg.fields = fields;
102
+ if (error) {
103
+ msg.error = error instanceof Error
104
+ ? { code: 'BRICK_ERROR', message: error.message, details: error.stack }
105
+ : { code: 'BRICK_ERROR', message: String(error) };
106
+ }
107
+ this.send(msg);
108
+ }
74
109
  /**
75
110
  * 发起一个需要等待 host.result/host.error 回复的 host.* 调用。
76
111
  * 入参省略 id(SDK 自动分配),返回 result 字段。
77
112
  */
78
113
  hostCall(msg) {
79
114
  const id = this.nextId();
115
+ const trace = this.traceProvider?.();
116
+ const merged = trace && !msg.trace
117
+ ? { ...msg, trace }
118
+ : msg;
80
119
  return new Promise((resolve, reject) => {
81
120
  this.pending.set(id, {
82
121
  resolve: (v) => resolve(v),
83
122
  reject
84
123
  });
85
- this.send({ ...msg, id });
124
+ this.send({ ...merged, id });
86
125
  });
87
126
  }
88
127
  nextHostId() {
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syllm/brickly-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Brickly Brick Node runtime 官方 SDK",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -16,7 +16,6 @@
16
16
  "build": "tsc -p tsconfig.json",
17
17
  "test": "tsx --test tests/runtime.test.ts tests/api.test.ts tests/typegen.test.ts",
18
18
  "typegen": "tsx src/typegen-cli.ts",
19
- "sync:demo-window-lab": "node scripts/sync-to-brick.mjs com.brickly.demo-window-lab",
20
19
  "prepublishOnly": "npm run build"
21
20
  },
22
21
  "devDependencies": {