@mokuyoaxis/dsh-iris 0.2.0 → 0.2.2

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 (93) hide show
  1. package/.dsh/skills/iris-compose-media/SKILL.md +10 -5
  2. package/.dsh/skills/iris-compose-media/references/workflows.md +7 -1
  3. package/.dsh/skills/iris-verify-ui/SKILL.md +2 -1
  4. package/CHANGELOG.md +69 -2
  5. package/README.en.md +67 -31
  6. package/README.md +67 -31
  7. package/bin/dsh-iris.js +222 -21
  8. package/docs/AGENT_QUICKSTART.md +10 -6
  9. package/docs/ARCHITECTURE.md +34 -19
  10. package/docs/ARTIFACT_LIBRARY.md +5 -5
  11. package/docs/ARTIFACT_MANIFEST.md +1 -1
  12. package/docs/CLI_MANAGEMENT.md +183 -0
  13. package/docs/COMPOSITE_VISION.md +35 -0
  14. package/docs/CORE_RUNTIME_CONTRACT.md +16 -7
  15. package/docs/DSH_CORE_MIGRATION.md +48 -19
  16. package/docs/DSH_RC2_ADAPTATION.md +4 -2
  17. package/docs/FAULT_INJECTION.md +21 -2
  18. package/docs/HEADLESS_CLI.md +15 -7
  19. package/docs/HOST_ADAPTER_CONTRACT.md +10 -10
  20. package/docs/HOST_DOCTOR.md +3 -3
  21. package/docs/MODEL_PORT_CONTRACT.md +21 -14
  22. package/docs/OCR_MODEL.md +39 -0
  23. package/docs/PROMPT_OPTIMIZER.md +77 -0
  24. package/docs/PROVIDER_ADAPTER_CONTRACT.md +15 -5
  25. package/docs/PROVIDER_HEALTH.md +22 -5
  26. package/docs/PROVIDER_RUNTIME_HARNESS.md +1 -1
  27. package/docs/PROVIDER_SUBMISSION_CONTRACT.md +2 -2
  28. package/docs/ROADMAP.md +37 -19
  29. package/docs/SECURITY.md +5 -5
  30. package/docs/TASK_SEMANTICS.md +2 -2
  31. package/docs/VISION_CLI.md +132 -0
  32. package/docs/VISION_MODEL.md +51 -0
  33. package/docs/WORKBENCH_ARTIFACTS.md +67 -0
  34. package/docs/file-access-across-environments.md +8 -5
  35. package/docs/releases/0.2.1.md +56 -0
  36. package/docs/releases/0.2.2.md +31 -0
  37. package/lib/actions.js +199 -286
  38. package/lib/adapters.js +216 -41
  39. package/lib/api.js +49 -7
  40. package/lib/capability.js +23 -16
  41. package/lib/chromium-browser.js +117 -0
  42. package/lib/client.js +933 -117
  43. package/lib/command-service.js +121 -7
  44. package/lib/composite-vision-routing.js +42 -0
  45. package/lib/config.js +102 -14
  46. package/lib/core-artifact-store.js +45 -13
  47. package/lib/core-lease-recovery.js +64 -92
  48. package/lib/core-maintenance.js +213 -0
  49. package/lib/core-runtime.js +45 -47
  50. package/lib/core-tasks.js +12 -5
  51. package/lib/core-user-projection.js +1 -0
  52. package/lib/core-writer-lease.js +134 -0
  53. package/lib/dsh-core-adapter.js +63 -9
  54. package/lib/dsh-text-model-adapter.js +222 -0
  55. package/lib/dsh-vision-model-adapter.js +75 -0
  56. package/lib/generation-input.js +36 -5
  57. package/lib/headless-vision.js +181 -0
  58. package/lib/host-runtime.js +1 -0
  59. package/lib/http-vision-model-adapter.js +187 -0
  60. package/lib/image-edit-input.js +34 -0
  61. package/lib/index.js +205 -175
  62. package/lib/locate.js +86 -28
  63. package/lib/media-probe.js +2 -1
  64. package/lib/model-call-runtime.js +45 -0
  65. package/lib/model-invoker.js +4 -2
  66. package/lib/model-port-contract.js +13 -5
  67. package/lib/models.js +29 -42
  68. package/lib/ocr-model-routing.js +26 -0
  69. package/lib/ocr.js +110 -64
  70. package/lib/prompt-optimizer-config.js +21 -10
  71. package/lib/prompt-optimizer-core.js +247 -0
  72. package/lib/prompt-optimizer.js +37 -193
  73. package/lib/provider-adapters.js +87 -10
  74. package/lib/provider-catalog.js +19 -7
  75. package/lib/provider-config-service.js +235 -0
  76. package/lib/provider-contract.js +3 -2
  77. package/lib/provider-health.js +80 -1
  78. package/lib/provider-protocol.js +84 -0
  79. package/lib/provider-task-runner.js +63 -19
  80. package/lib/summarize.js +31 -20
  81. package/lib/summary-input.js +52 -0
  82. package/lib/task-wait.js +40 -0
  83. package/lib/video-input.js +30 -0
  84. package/lib/vision-core.js +42 -0
  85. package/lib/vision-image-input.js +83 -0
  86. package/lib/vision-input.js +31 -0
  87. package/lib/vision-model-routing.js +180 -0
  88. package/lib/vision.js +3 -2
  89. package/lib/workbench-artifacts.js +52 -0
  90. package/lib/workbench-management.js +151 -0
  91. package/lib/workbench-zip.js +39 -0
  92. package/package.json +1 -1
  93. package/user_guide.md +74 -30
@@ -0,0 +1,117 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import http from 'node:http';
5
+ import crypto from 'node:crypto';
6
+ import { spawn } from 'node:child_process';
7
+
8
+ // 与 DSH render 的页面权限一致:只渲染内联样式和内嵌图片,不执行脚本或加载网络资源。
9
+ const CSP = "sandbox; default-src 'none'; img-src data: blob:; style-src 'unsafe-inline'; font-src data:; connect-src 'none'; form-action 'none'; base-uri 'none'; frame-src 'none'";
10
+ const failure = (code, message) => Object.assign(new Error(message), { code });
11
+
12
+ export function createChromiumBrowser({ executable, noSandbox = false, timeoutMs = 30000 } = {}) {
13
+ if (!executable || !path.isAbsolute(executable)) {
14
+ throw failure('IRIS_BROWSER_REQUIRED', '请通过 --browser-executable 或 IRIS_BROWSER_EXECUTABLE 提供 Chromium 的绝对路径');
15
+ }
16
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 120000) {
17
+ throw failure('IRIS_BROWSER_INPUT_INVALID', '浏览器超时必须是 1–120000 毫秒');
18
+ }
19
+ return Object.freeze({ async renderHtml({ html, width, height, fullPage, signal }) {
20
+ const operation = AbortSignal.any([...(signal ? [signal] : []), AbortSignal.timeout(timeoutMs)]);
21
+ operation.throwIfAborted();
22
+ const profile = fs.mkdtempSync(path.join(os.tmpdir(), 'iris-browser-'));
23
+ const route = '/' + crypto.randomBytes(16).toString('hex');
24
+ const server = http.createServer((req, res) => {
25
+ if (req.url !== route || !['GET', 'HEAD'].includes(req.method)) { res.writeHead(404); res.end(); return; }
26
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Content-Security-Policy': CSP,
27
+ 'Referrer-Policy': 'no-referrer', 'X-Content-Type-Options': 'nosniff', 'Cache-Control': 'no-store' });
28
+ res.end(req.method === 'HEAD' ? '' : html);
29
+ });
30
+ let child;
31
+ let closed;
32
+ let nextId = 0;
33
+ let incoming = Buffer.alloc(0);
34
+ const pending = new Map();
35
+ const events = new Map();
36
+ const rejectPending = error => {
37
+ for (const request of pending.values()) request.reject(error);
38
+ pending.clear();
39
+ for (const event of events.values()) event.reject(error);
40
+ events.clear();
41
+ };
42
+ const kill = () => {
43
+ if (!child?.pid) return;
44
+ try { process.kill(-child.pid, 'SIGKILL'); } catch (_) { /* 已结束 */ }
45
+ };
46
+ const onAbort = () => { rejectPending(operation.reason); kill(); };
47
+ operation.addEventListener('abort', onAbort, { once: true });
48
+ const send = (method, params = {}, sessionId) => {
49
+ operation.throwIfAborted();
50
+ return new Promise((resolve, reject) => {
51
+ const id = ++nextId;
52
+ pending.set(id, { resolve, reject });
53
+ child.stdio[3].write(JSON.stringify({ id, method, params, ...(sessionId ? { sessionId } : {}) }) + '\0', error => {
54
+ if (error) { pending.delete(id); reject(failure('IRIS_BROWSER_FAILED', '浏览器通信失败')); }
55
+ });
56
+ });
57
+ };
58
+ try {
59
+ await new Promise((resolve, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', resolve); });
60
+ operation.throwIfAborted();
61
+ const args = ['--headless=new', '--hide-scrollbars', '--remote-debugging-pipe', '--disable-gpu', '--disable-dev-shm-usage', '--disable-background-networking',
62
+ '--no-first-run', '--no-default-browser-check', '--disable-extensions', '--disable-sync',
63
+ '--user-data-dir=' + profile, ...(noSandbox ? ['--no-sandbox'] : [])];
64
+ child = spawn(executable, args, { detached: true, stdio: ['ignore', 'ignore', 'ignore', 'pipe', 'pipe'] });
65
+ closed = new Promise(resolve => {
66
+ child.once('error', () => { rejectPending(failure('IRIS_BROWSER_FAILED', '无法启动 Chromium')); resolve(); });
67
+ child.once('close', () => { rejectPending(failure('IRIS_BROWSER_FAILED', 'Chromium 提前退出;请检查可执行文件及运行环境')); resolve(); });
68
+ });
69
+ child.stdio[3].on('error', () => rejectPending(failure('IRIS_BROWSER_FAILED', '浏览器通信失败')));
70
+ child.stdio[4].on('data', chunk => {
71
+ incoming = Buffer.concat([incoming, chunk]);
72
+ let separator;
73
+ while ((separator = incoming.indexOf(0)) >= 0) {
74
+ const packet = incoming.subarray(0, separator); incoming = incoming.subarray(separator + 1);
75
+ let message;
76
+ try { message = JSON.parse(packet.toString()); } catch (_) { continue; }
77
+ if (message.id) {
78
+ const request = pending.get(message.id);
79
+ pending.delete(message.id);
80
+ if (request) message.error ? request.reject(failure('IRIS_BROWSER_FAILED', 'Chromium 无法完成截图操作')) : request.resolve(message.result);
81
+ } else {
82
+ const key = message.sessionId + ':' + message.method;
83
+ const event = events.get(key);
84
+ if (event) { events.delete(key); event.resolve(message.params); }
85
+ }
86
+ }
87
+ });
88
+ const { targetId } = await send('Target.createTarget', { url: 'about:blank' });
89
+ const { sessionId } = await send('Target.attachToTarget', { targetId, flatten: true });
90
+ await send('Page.enable', {}, sessionId);
91
+ await send('Emulation.setDeviceMetricsOverride', { width, height, deviceScaleFactor: 1, mobile: false }, sessionId);
92
+ const loaded = new Promise((resolve, reject) => events.set(sessionId + ':Page.loadEventFired', { resolve, reject }));
93
+ // 导航失败时也必须消费事件 Promise 的拒绝。
94
+ loaded.catch(() => {});
95
+ const navigation = await send('Page.navigate', { url: 'http://127.0.0.1:' + server.address().port + route }, sessionId);
96
+ if (navigation.errorText) throw failure('IRIS_BROWSER_FAILED', 'HTML 页面无法加载');
97
+ await loaded;
98
+ const layout = await send('Page.getLayoutMetrics', {}, sessionId);
99
+ const size = fullPage ? layout.cssContentSize : { width, height };
100
+ const captureWidth = Math.max(width, Math.ceil(size.width)), captureHeight = Math.ceil(size.height);
101
+ if (captureWidth > 16384 || captureHeight > 16384 || captureWidth * captureHeight > 40000000) {
102
+ throw failure('IRIS_BROWSER_INPUT_INVALID', '截图尺寸超过限制(边长 16384,4000 万像素)');
103
+ }
104
+ const screenshot = await send('Page.captureScreenshot', { format: 'png', captureBeyondViewport: fullPage,
105
+ clip: { x: 0, y: 0, width: captureWidth, height: captureHeight, scale: 1 } }, sessionId);
106
+ operation.throwIfAborted();
107
+ return { bytes: Buffer.from(screenshot.data, 'base64'), mediaType: 'image/png' };
108
+ } finally {
109
+ operation.removeEventListener('abort', onAbort);
110
+ kill();
111
+ if (closed) await closed;
112
+ server.closeAllConnections();
113
+ await new Promise(resolve => server.close(resolve));
114
+ fs.rmSync(profile, { recursive: true, force: true });
115
+ }
116
+ } });
117
+ }