@mokuyoaxis/dsh-iris 0.1.4 → 0.2.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.
Files changed (110) 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 +99 -1
  5. package/README.en.md +124 -31
  6. package/README.md +124 -31
  7. package/bin/dsh-iris.js +593 -13
  8. package/docs/AGENT_QUICKSTART.md +39 -0
  9. package/docs/ARCHITECTURE.md +87 -64
  10. package/docs/ARTIFACT_LIBRARY.md +5 -5
  11. package/docs/ARTIFACT_MANIFEST.md +60 -0
  12. package/docs/CLI_MANAGEMENT.md +181 -0
  13. package/docs/COMPOSITE_VISION.md +35 -0
  14. package/docs/CORE_RUNTIME_CONTRACT.md +84 -0
  15. package/docs/DSH_CORE_MIGRATION.md +135 -0
  16. package/docs/DSH_RC2_ADAPTATION.md +38 -0
  17. package/docs/FAULT_INJECTION.md +12 -2
  18. package/docs/HEADLESS_CLI.md +223 -0
  19. package/docs/HOST_ADAPTER_CONTRACT.md +36 -17
  20. package/docs/HOST_DOCTOR.md +4 -2
  21. package/docs/MODEL_PORT_CONTRACT.md +305 -0
  22. package/docs/OCR_MODEL.md +39 -0
  23. package/docs/PROMPT_OPTIMIZER.md +47 -0
  24. package/docs/PROVIDER_ADAPTER_CONTRACT.md +53 -10
  25. package/docs/PROVIDER_HEALTH.md +22 -5
  26. package/docs/PROVIDER_RUNTIME_HARNESS.md +33 -0
  27. package/docs/PROVIDER_SUBMISSION_CONTRACT.md +2 -2
  28. package/docs/ROADMAP.md +49 -83
  29. package/docs/SECURITY.md +8 -7
  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/assets/logo/README.md +55 -0
  35. package/docs/assets/logo/iris-bubble-concept.svg +67 -0
  36. package/docs/assets/logo/iris-bubble.svg +14 -0
  37. package/docs/assets/logo/iris-interim-flower.png +0 -0
  38. package/docs/assets/logo/iris-logo-mono-inverse.svg +78 -0
  39. package/docs/assets/logo/iris-logo-mono.svg +78 -0
  40. package/docs/assets/logo/iris-logo.svg +78 -0
  41. package/docs/assets/logo/iris-mark-mono-inverse.svg +42 -0
  42. package/docs/assets/logo/iris-mark-mono.svg +42 -0
  43. package/docs/assets/logo/iris-mark.svg +42 -0
  44. package/docs/file-access-across-environments.md +8 -5
  45. package/docs/releases/0.2.0.md +48 -0
  46. package/docs/releases/0.2.1.md +56 -0
  47. package/lib/actions.js +520 -591
  48. package/lib/adapters.js +184 -36
  49. package/lib/api.js +200 -11
  50. package/lib/chromium-browser.js +117 -0
  51. package/lib/client.js +1390 -105
  52. package/lib/command-service.js +534 -0
  53. package/lib/composite-vision-routing.js +42 -0
  54. package/lib/config.js +143 -51
  55. package/lib/core-artifact-store.js +630 -0
  56. package/lib/core-artifacts.js +4 -0
  57. package/lib/core-attention.js +103 -0
  58. package/lib/core-contract.js +105 -0
  59. package/lib/core-doctor.js +41 -0
  60. package/lib/core-lease-recovery.js +170 -0
  61. package/lib/core-maintenance.js +213 -0
  62. package/lib/core-runtime.js +232 -0
  63. package/lib/core-tasks.js +567 -0
  64. package/lib/core-user-projection.js +193 -0
  65. package/lib/doctor.js +92 -33
  66. package/lib/dsh-core-adapter.js +653 -0
  67. package/lib/dsh-host-adapter.js +58 -7
  68. package/lib/dsh-text-model-adapter.js +222 -0
  69. package/lib/dsh-vision-model-adapter.js +75 -0
  70. package/lib/generation-input.js +126 -0
  71. package/lib/guard.js +3 -3
  72. package/lib/headless-vision.js +181 -0
  73. package/lib/host-runtime.js +1 -0
  74. package/lib/http-vision-model-adapter.js +187 -0
  75. package/lib/image-edit-input.js +34 -0
  76. package/lib/index.js +323 -188
  77. package/lib/locate.js +86 -28
  78. package/lib/media-probe.js +2 -1
  79. package/lib/model-call-runtime.js +45 -0
  80. package/lib/model-invoker.js +220 -0
  81. package/lib/model-port-contract.js +341 -0
  82. package/lib/models.js +105 -14
  83. package/lib/ocr-model-routing.js +26 -0
  84. package/lib/ocr.js +110 -64
  85. package/lib/prompt-optimizer-config.js +2 -1
  86. package/lib/prompt-optimizer-core.js +121 -0
  87. package/lib/prompt-optimizer.js +37 -193
  88. package/lib/provider-adapter.js +49 -23
  89. package/lib/provider-adapters.js +165 -26
  90. package/lib/provider-catalog.js +183 -0
  91. package/lib/provider-config-service.js +236 -0
  92. package/lib/provider-contract.js +74 -4
  93. package/lib/provider-health.js +80 -1
  94. package/lib/provider-protocol.js +74 -2
  95. package/lib/provider-task-runner.js +370 -0
  96. package/lib/summarize.js +31 -20
  97. package/lib/summary-input.js +52 -0
  98. package/lib/task-wait.js +40 -0
  99. package/lib/tasks.js +16 -4
  100. package/lib/video-input.js +30 -0
  101. package/lib/vision-core.js +42 -0
  102. package/lib/vision-image-input.js +83 -0
  103. package/lib/vision-input.js +31 -0
  104. package/lib/vision-model-routing.js +180 -0
  105. package/lib/vision.js +31 -9
  106. package/lib/workbench-artifacts.js +52 -0
  107. package/lib/workbench-management.js +151 -0
  108. package/lib/workbench-zip.js +39 -0
  109. package/package.json +8 -8
  110. package/user_guide.md +71 -26
@@ -0,0 +1,232 @@
1
+ 'use strict';
2
+
3
+ import crypto from 'node:crypto';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import {
7
+ CORE_CONTRACT_VERSION,
8
+ CoreContractError,
9
+ assertCoreOperation,
10
+ coreDataRootBusyError,
11
+ createCoreLifecycle,
12
+ normalizeCoreOptions,
13
+ transitionCoreLifecycle
14
+ } from './core-contract.js';
15
+ import { ensurePrivateDir, writePrivateFile } from './private-storage.js';
16
+
17
+ const WRITER_LEASE_DIR = '.iris-runtime-writer-v0';
18
+ const WRITER_OWNER_FILE = 'owner.json';
19
+
20
+ /** 只读租约证据;PID 缺失不等于已经获准恢复,绝不自动夺取租约。 */
21
+ export function inspectCoreWriterLease(dataRoot) {
22
+ const options = normalizeCoreOptions({ dataRoot, mode: 'reader' });
23
+ const directory = path.join(options.dataRoot, WRITER_LEASE_DIR);
24
+ let stat;
25
+ try { stat = fs.lstatSync(directory); }
26
+ catch (error) { return { status: error?.code === 'ENOENT' ? 'absent' : 'invalid' }; }
27
+ try {
28
+ if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('invalid lease');
29
+ const entries = fs.readdirSync(directory);
30
+ if (entries.length !== 1 || entries[0] !== WRITER_OWNER_FILE) throw new Error('invalid contents');
31
+ const ownerFile = path.join(directory, WRITER_OWNER_FILE);
32
+ const ownerStat = fs.lstatSync(ownerFile);
33
+ if (!ownerStat.isFile() || ownerStat.isSymbolicLink() || ownerStat.size > 16384) throw new Error('invalid owner');
34
+ const owner = JSON.parse(fs.readFileSync(ownerFile, 'utf8'));
35
+ const created = Date.parse(owner?.createdAt);
36
+ if (owner?.contractVersion !== CORE_CONTRACT_VERSION || !Number.isSafeInteger(owner.pid) || owner.pid <= 0
37
+ || !/^[a-f0-9-]{36}$/.test(String(owner.instanceId || '')) || !Number.isFinite(created)) {
38
+ throw new Error('incomplete owner');
39
+ }
40
+ let ownerStatus = 'alive';
41
+ try { process.kill(owner.pid, 0); }
42
+ catch (error) { ownerStatus = error?.code === 'ESRCH' ? 'missing' : 'unknown'; }
43
+ return {
44
+ status: 'present', ownerStatus, pid: owner.pid,
45
+ createdAt: new Date(created).toISOString(), ageMs: Math.max(0, Date.now() - created)
46
+ };
47
+ } catch (_) { return { status: 'invalid' }; }
48
+ }
49
+
50
+ function coreError(code, message) {
51
+ return new CoreContractError(code, message);
52
+ }
53
+
54
+ function physicalDataRoot(options) {
55
+ if (options.mode === 'writer') {
56
+ try { ensurePrivateDir(options.dataRoot); }
57
+ catch (_) { throw coreError('IRIS_CORE_DATA_ROOT_UNAVAILABLE', '无法创建或访问 Iris 数据根'); }
58
+ }
59
+ let root;
60
+ try { root = fs.realpathSync.native(options.dataRoot); }
61
+ catch (error) {
62
+ if (error && error.code === 'ENOENT') {
63
+ throw coreError('IRIS_CORE_DATA_ROOT_NOT_FOUND', 'Iris 数据根不存在;只读模式不会自动创建');
64
+ }
65
+ throw coreError('IRIS_CORE_DATA_ROOT_UNAVAILABLE', '无法访问 Iris 数据根');
66
+ }
67
+ try {
68
+ if (!fs.statSync(root).isDirectory()) {
69
+ throw coreError('IRIS_CORE_DATA_ROOT_UNAVAILABLE', 'Iris 数据根必须是目录');
70
+ }
71
+ } catch (error) {
72
+ if (error instanceof CoreContractError) throw error;
73
+ throw coreError('IRIS_CORE_DATA_ROOT_UNAVAILABLE', '无法检查 Iris 数据根');
74
+ }
75
+ return root;
76
+ }
77
+
78
+ function acquireWriterLease(root, instanceId) {
79
+ const directory = path.join(root, WRITER_LEASE_DIR);
80
+ const ownerFile = path.join(directory, WRITER_OWNER_FILE);
81
+ try {
82
+ fs.mkdirSync(directory, { mode: 0o700 });
83
+ } catch (error) {
84
+ if (error && error.code === 'EEXIST') throw coreDataRootBusyError();
85
+ throw coreError('IRIS_CORE_DATA_ROOT_UNAVAILABLE', '无法取得 Iris 数据根写者租约');
86
+ }
87
+
88
+ const owner = Object.freeze({
89
+ contractVersion: CORE_CONTRACT_VERSION,
90
+ instanceId,
91
+ pid: process.pid,
92
+ createdAt: new Date().toISOString()
93
+ });
94
+ const serialized = JSON.stringify(owner, null, 2) + '\n';
95
+ try {
96
+ writePrivateFile(ownerFile, serialized, { flag: 'wx' });
97
+ } catch (_) {
98
+ try { fs.unlinkSync(ownerFile); } catch (_) { /* owner 文件可能尚未建立 */ }
99
+ try { fs.rmdirSync(directory); } catch (_) { /* 保留不确定租约比误删安全 */ }
100
+ throw coreError('IRIS_CORE_DATA_ROOT_UNAVAILABLE', '无法记录 Iris 数据根写者租约');
101
+ }
102
+ return Object.freeze({ directory, ownerFile, owner, serialized });
103
+ }
104
+
105
+ function releaseWriterLease(lease) {
106
+ let actual;
107
+ try {
108
+ const stat = fs.lstatSync(lease.directory);
109
+ if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('lease is not a directory');
110
+ const entries = fs.readdirSync(lease.directory);
111
+ if (entries.length !== 1 || entries[0] !== WRITER_OWNER_FILE) throw new Error('unexpected lease contents');
112
+ actual = JSON.parse(fs.readFileSync(lease.ownerFile, 'utf8'));
113
+ } catch (_) {
114
+ throw coreError('IRIS_CORE_LEASE_INVALID', 'Iris 写者租约无法安全释放;已保留现场');
115
+ }
116
+ if (actual.instanceId !== lease.owner.instanceId) {
117
+ throw coreError('IRIS_CORE_LEASE_INVALID', 'Iris 写者租约所有者不匹配;已保留现场');
118
+ }
119
+
120
+ try {
121
+ fs.unlinkSync(lease.ownerFile);
122
+ fs.rmdirSync(lease.directory);
123
+ } catch (_) {
124
+ // 若目录删除失败,尽力恢复 owner 证据;绝不把仍不确定的根宣布为可写。
125
+ if (fs.existsSync(lease.directory) && !fs.existsSync(lease.ownerFile)) {
126
+ try { writePrivateFile(lease.ownerFile, lease.serialized, { flag: 'wx' }); } catch (_) { /* 保持失败 */ }
127
+ }
128
+ throw coreError('IRIS_CORE_LEASE_INVALID', 'Iris 写者租约释放失败;已保留现场');
129
+ }
130
+ }
131
+
132
+ function runtimeSnapshot(options, lifecycle, hasLease, inFlight) {
133
+ return Object.freeze({
134
+ contractVersion: CORE_CONTRACT_VERSION,
135
+ state: lifecycle.state,
136
+ mode: options.mode,
137
+ writerLease: Boolean(hasLease),
138
+ inFlight
139
+ });
140
+ }
141
+
142
+ /**
143
+ * 创建一个最小 Core Runtime 实例。它尚未迁移 0.1.4 的模块级存储;当前职责只有
144
+ * 显式数据根、单写者租约、操作权限、AbortSignal 与可验证释放顺序。
145
+ */
146
+ export function createCoreRuntime(input) {
147
+ const options = normalizeCoreOptions(input);
148
+ const instanceId = crypto.randomUUID();
149
+ const abortController = new AbortController();
150
+ const inFlight = new Set();
151
+ const cleanup = [];
152
+ let lifecycle = createCoreLifecycle();
153
+ let root = null;
154
+ let lease = null;
155
+ let disposePromise = null;
156
+
157
+ function snapshot() {
158
+ return runtimeSnapshot(options, lifecycle, lease, inFlight.size);
159
+ }
160
+
161
+ function start() {
162
+ if (lifecycle.state !== 'created') {
163
+ return transitionCoreLifecycle(lifecycle, 'start');
164
+ }
165
+ const candidateRoot = physicalDataRoot(options);
166
+ const candidateLease = options.mode === 'writer' ? acquireWriterLease(candidateRoot, instanceId) : null;
167
+ root = candidateRoot;
168
+ lease = candidateLease;
169
+ lifecycle = transitionCoreLifecycle(lifecycle, 'start');
170
+ return snapshot();
171
+ }
172
+
173
+ function onDispose(callback) {
174
+ if (typeof callback !== 'function') throw new TypeError('Core Runtime cleanup 必须是函数');
175
+ if (lifecycle.state === 'disposing' || lifecycle.state === 'disposed') {
176
+ throw coreError('IRIS_CORE_STATE_INVALID', 'Core Runtime 已开始释放,不能注册 cleanup');
177
+ }
178
+ cleanup.push(callback);
179
+ let active = true;
180
+ return () => {
181
+ if (!active) return false;
182
+ active = false;
183
+ const index = cleanup.indexOf(callback);
184
+ if (index >= 0) cleanup.splice(index, 1);
185
+ return index >= 0;
186
+ };
187
+ }
188
+
189
+ function run(operation, callback) {
190
+ if (typeof callback !== 'function') throw new TypeError('Core Runtime 操作必须是函数');
191
+ assertCoreOperation(options, lifecycle, operation);
192
+ const context = Object.freeze({ dataRoot: root, signal: abortController.signal });
193
+ let result;
194
+ try { result = callback(context); }
195
+ catch (error) { result = Promise.reject(error); }
196
+ const promise = Promise.resolve(result);
197
+ inFlight.add(promise);
198
+ promise.then(
199
+ () => inFlight.delete(promise),
200
+ () => inFlight.delete(promise)
201
+ );
202
+ return promise;
203
+ }
204
+
205
+ function dispose() {
206
+ if (disposePromise) return disposePromise;
207
+ lifecycle = transitionCoreLifecycle(lifecycle, 'dispose');
208
+ abortController.abort();
209
+ disposePromise = (async () => {
210
+ await Promise.allSettled([...inFlight]);
211
+ for (const callback of [...cleanup].reverse()) await callback();
212
+ cleanup.length = 0;
213
+ if (lease) {
214
+ releaseWriterLease(lease);
215
+ lease = null;
216
+ }
217
+ root = null;
218
+ lifecycle = transitionCoreLifecycle(lifecycle, 'finish-dispose');
219
+ return snapshot();
220
+ })();
221
+ return disposePromise;
222
+ }
223
+
224
+ return Object.freeze({
225
+ start,
226
+ run,
227
+ onDispose,
228
+ dispose,
229
+ snapshot,
230
+ get signal() { return abortController.signal; }
231
+ });
232
+ }