dsh-builtin-browser 0.4.2 → 0.4.6

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 (49) hide show
  1. package/CHANGELOG.md +1753 -0
  2. package/README.en.md +63 -20
  3. package/README.md +60 -20
  4. package/client.js +433 -24
  5. package/desktop-bridge/install.mjs +66 -6
  6. package/desktop-bridge/plugin-browser-bridge.js +425 -163
  7. package/docs/architecture.md +4 -4
  8. package/docs/tool-reference.md +3 -3
  9. package/docs/user-guide.md +50 -2
  10. package/lib/browser/types.d.ts +9 -2
  11. package/lib/browser-electron/bridge-connection.d.ts +18 -21
  12. package/lib/browser-electron/bridge-connection.js +42 -1
  13. package/lib/browser-electron/desktop-bridge-host.d.ts +69 -36
  14. package/lib/browser-electron/desktop-bridge-host.js +151 -86
  15. package/lib/browser-electron/entry.d.ts +20 -0
  16. package/lib/browser-electron/entry.js +110 -7
  17. package/lib/browser-electron/history-store.d.ts +11 -2
  18. package/lib/browser-electron/history-store.js +27 -5
  19. package/lib/browser-electron/host-main.js +11 -1
  20. package/lib/browser-electron/provider.d.ts +62 -2
  21. package/lib/browser-electron/provider.js +28 -12
  22. package/lib/browser-electron/remote-host.js +19 -6
  23. package/lib/browser-electron/rpc-ops.d.ts +18 -0
  24. package/lib/browser-electron/rpc-ops.js +36 -0
  25. package/lib/browser-electron/settings-route.d.ts +6 -6
  26. package/lib/browser-electron/settings-route.js +48 -8
  27. package/lib/browser-electron/settings-store.js +57 -16
  28. package/lib/browser-electron/system-browser.d.ts +31 -9
  29. package/lib/browser-electron/system-browser.js +116 -27
  30. package/lib/index.js +0 -1
  31. package/lib/tool-browser/element-fields.d.ts +145 -0
  32. package/lib/tool-browser/element-fields.js +152 -0
  33. package/lib/tool-browser/index.js +26 -60
  34. package/package.json +6 -2
  35. package/src/browser/types.ts +9 -2
  36. package/src/browser-electron/bridge-connection.ts +37 -1
  37. package/src/browser-electron/desktop-bridge-host.ts +146 -84
  38. package/src/browser-electron/entry.ts +113 -8
  39. package/src/browser-electron/history-store.ts +32 -7
  40. package/src/browser-electron/host-main.ts +10 -1
  41. package/src/browser-electron/provider.ts +91 -18
  42. package/src/browser-electron/remote-host.ts +19 -6
  43. package/src/browser-electron/rpc-ops.ts +41 -0
  44. package/src/browser-electron/settings-route.ts +54 -13
  45. package/src/browser-electron/settings-store.ts +56 -17
  46. package/src/browser-electron/system-browser.ts +121 -28
  47. package/src/index.ts +3 -2
  48. package/src/tool-browser/element-fields.ts +167 -0
  49. package/src/tool-browser/index.ts +31 -60
@@ -18,6 +18,12 @@ import type { Context } from '@deepseek-ai/cordis'
18
18
  import { defineTool } from '@deepseek-ai/dsh-tools'
19
19
  import type {} from '@deepseek-ai/dsh-system-prompt'
20
20
  import type { BrowserSessionId } from '../browser/types.js'
21
+ import {
22
+ ELEMENT_ITEM_SCHEMA,
23
+ NODE_ITEM_SCHEMA,
24
+ projectElement,
25
+ projectNode,
26
+ } from './element-fields.js'
21
27
 
22
28
  /**
23
29
  * Register a cleanup that runs once when the process exits.
@@ -329,24 +335,8 @@ export function apply(ctx: Context, config: Config = {}): void {
329
335
  elements: {
330
336
  type: 'array',
331
337
  required: true,
332
- items: {
333
- type: 'object',
334
- additionalProperties: false,
335
- properties: {
336
- ref: { type: 'number', required: true },
337
- kind: { type: 'string', required: true },
338
- label: { type: 'string', required: true },
339
- x: { type: 'number', required: true },
340
- y: { type: 'number', required: true },
341
- frame: { type: 'boolean' },
342
- // The page-side script computes this for every element and the tool layer
343
- // passes it through. Without it in the schema, a declared
344
- // additionalProperties: false rejects the whole result at run time — which is
345
- // how this was found, on the very first real call, after a static review had
346
- // called the change verified.
347
- selector: { type: 'string' },
348
- },
349
- },
338
+ // Same shared item schema as browser_open's, so the two cannot drift apart.
339
+ items: ELEMENT_ITEM_SCHEMA,
350
340
  },
351
341
  challenge: {
352
342
  type: 'object',
@@ -379,7 +369,7 @@ export function apply(ctx: Context, config: Config = {}): void {
379
369
  return {
380
370
  url: snapshot.url,
381
371
  ...snapshot.title !== undefined ? { title: snapshot.title } : {},
382
- elements: snapshot.elements.map(el => ({ ref: el.ref, kind: el.kind, label: el.label, selector: el.selector, x: el.x, y: el.y, ...el.frame === true ? { frame: true } : {} })),
372
+ elements: snapshot.elements.map(el => projectElement(el)),
383
373
  truncated: snapshot.truncated,
384
374
  ...snapshot.challenge !== undefined ? { challenge: snapshot.challenge } : {},
385
375
  }
@@ -445,7 +435,7 @@ export function apply(ctx: Context, config: Config = {}): void {
445
435
  return {
446
436
  url: snapshot.url,
447
437
  ...snapshot.title !== undefined ? { title: snapshot.title } : {},
448
- elements: snapshot.elements.map(el => ({ ref: el.ref, kind: el.kind, label: el.label, selector: el.selector, x: el.x, y: el.y, ...el.frame === true ? { frame: true } : {} })),
438
+ elements: snapshot.elements.map(el => projectElement(el)),
449
439
  truncated: snapshot.truncated,
450
440
  ...snapshot.challenge !== undefined ? { challenge: snapshot.challenge } : {},
451
441
  }
@@ -472,28 +462,8 @@ export function apply(ctx: Context, config: Config = {}): void {
472
462
  nodes: {
473
463
  type: 'array',
474
464
  required: true,
475
- items: {
476
- type: 'object',
477
- additionalProperties: false,
478
- properties: {
479
- ref: { type: 'number', required: true },
480
- role: { type: 'string', required: true },
481
- name: { type: 'string', required: true },
482
- value: { type: 'string' },
483
- states: { type: 'array', required: true, items: { type: 'string' } },
484
- depth: { type: 'number', required: true },
485
- tag: { type: 'string', required: true },
486
- x: { type: 'number', required: true },
487
- y: { type: 'number', required: true },
488
- frame: { type: 'boolean' },
489
- // The page-side script computes this for every element and the tool layer
490
- // passes it through. Without it in the schema, a declared
491
- // additionalProperties: false rejects the whole result at run time — which is
492
- // how this was found, on the very first real call, after a static review had
493
- // called the change verified.
494
- selector: { type: 'string' },
495
- },
496
- },
465
+ // Same shared table as the node projection.
466
+ items: NODE_ITEM_SCHEMA,
497
467
  },
498
468
  },
499
469
  },
@@ -541,23 +511,18 @@ export function apply(ctx: Context, config: Config = {}): void {
541
511
  ...result.title !== undefined ? { title: result.title } : {},
542
512
  count: result.count,
543
513
  truncated: result.truncated,
544
- nodes: result.nodes.map(n => ({
545
- ref: n.ref,
546
- role: n.role,
547
- name: n.name,
548
- ...n.value !== null ? { value: n.value } : {},
549
- states: [...n.states],
550
- depth: n.depth,
551
- tag: n.tag,
552
- // The provider computes this and the renderer prints it, but this mapping is an
553
- // explicit allow-list rather than a pass-through — so leaving it out here silently
554
- // dropped the field between the two, and the tool promised a reference it never
555
- // sent. The snapshot mapping lists its selector; this one did not.
556
- selector: n.selector,
557
- x: n.x,
558
- y: n.y,
559
- ...n.frame === true ? { frame: true } : {},
560
- })),
514
+ // The projection drops `value: null` (the schema declares a string, and the contract is
515
+ // that an absent value means "not applicable"), so the run-time shape and the inferred
516
+ // type differ by exactly that. The field table is what keeps the schema and the
517
+ // projection in agreement; this assertion is the type-level echo of that.
518
+ // The projection and the schema are now generated from one field table, so the shape
519
+ // they agree on is guaranteed at run time. TypeScript cannot see that agreement — it
520
+ // infers the output type from the schema object and compares it against the provider's
521
+ // interface, which describes the provider's internal shape rather than the contract
522
+ // (`value` nullable internally, absent on the wire; `selector` required by the
523
+ // interface, optional in the inferred schema). The assertion bridges that last gap; the
524
+ // test that walks every registered tool is what checks the agreement for real.
525
+ nodes: result.nodes.map(n => projectNode(n)) as never,
561
526
  }
562
527
  },
563
528
  }))
@@ -1313,7 +1278,7 @@ export function apply(ctx: Context, config: Config = {}): void {
1313
1278
  path: { type: 'string', description: 'The file path the screenshot was saved to, when savePath was given.' },
1314
1279
  },
1315
1280
  },
1316
- render: (_args, value) => [{ type: 'text', text: `Screenshot captured (${Math.round(value.dataUrl.length * 3 / 4 / 1024)} KiB)${value.path !== undefined ? ` saved to ${value.path}` : ''}.` }],
1281
+ render: (_args, value) => [{ type: 'text', text: `Screenshot captured (${Math.round(value.dataUrl.length * 3 / 4 / 1024)} KiB${value.width !== undefined && value.height !== undefined ? `, ${value.width}x${value.height}` : ''})${value.path !== undefined ? ` saved to ${value.path}` : ''}.` }],
1317
1282
  },
1318
1283
  timeoutMs,
1319
1284
  isConcurrencySafe: () => true,
@@ -1332,6 +1297,12 @@ export function apply(ctx: Context, config: Config = {}): void {
1332
1297
  return {
1333
1298
  dataUrl: shot.dataUrl,
1334
1299
  ...shot.path !== undefined ? { path: shot.path } : {},
1300
+ // The provider measures these when it scales, the schema declares them, and the seam
1301
+ // type carries them — but this mapping is an allow-list, so leaving them out dropped
1302
+ // them silently. Same shape of bug as the a11y selector, found the same way: by
1303
+ // reading the three places that mention a field and noticing this one did not.
1304
+ ...shot.width !== undefined ? { width: shot.width } : {},
1305
+ ...shot.height !== undefined ? { height: shot.height } : {},
1335
1306
  }
1336
1307
  },
1337
1308
  }))