@mokuyoaxis/dsh-iris 0.2.7 → 0.3.0-beta.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/CHANGELOG.md +143 -1
- package/README.en.md +109 -311
- package/README.md +106 -309
- package/bin/dsh-iris.js +452 -92
- package/docs/AGENT_CORE_QUICKSTART.md +64 -0
- package/docs/AGENT_QUICKSTART.md +2 -0
- package/docs/ARCHITECTURE.md +13 -3
- package/docs/ARTIFACT_MANIFEST.md +4 -0
- package/docs/AUDIO_FLOWS.md +87 -0
- package/docs/BETA_SCOPE.md +42 -0
- package/docs/CONTACT_SHEET.md +62 -0
- package/docs/CORE_CONNECTIONS.md +101 -0
- package/docs/CORE_QUICKSTART.md +110 -0
- package/docs/CORE_RUNTIME_CONTRACT.md +7 -2
- package/docs/CORE_SERVICE.md +101 -0
- package/docs/CORE_SKILLS.md +35 -3
- package/docs/DSH_COMPATIBILITY.md +21 -1
- package/docs/DSH_CORE_MIGRATION.md +6 -0
- package/docs/DSH_RC2_ADAPTATION.md +1 -1
- package/docs/DSH_RUNTIME.md +6 -2
- package/docs/HEADLESS_CLI.md +30 -5
- package/docs/HOST_ADAPTER_CONTRACT.md +5 -3
- package/docs/IMAGE_FLOW.md +4 -0
- package/docs/IMAGE_GENERATION.md +127 -0
- package/docs/LOCAL_API.md +4 -2
- package/docs/LOCAL_API_CONTRACT.md +42 -15
- package/docs/LOCAL_CREATION.md +7 -1
- package/docs/LOCAL_OPERATIONS.md +46 -0
- package/docs/LOCAL_SELECTION.md +57 -0
- package/docs/LOCAL_WEB.md +207 -0
- package/docs/MCP.md +212 -0
- package/docs/MEDIA_SCHEDULING.md +72 -0
- package/docs/MUSIC.md +2 -0
- package/docs/NETWORK_RESOURCES.md +106 -0
- package/docs/NETWORK_ROUTING.md +103 -0
- package/docs/NETWORK_WORKBENCH.md +65 -0
- package/docs/PANEL_EXTENSIONS.md +64 -0
- package/docs/PLUGINS.md +193 -0
- package/docs/PLUGIN_DEVELOPMENT.md +39 -0
- package/docs/PLUGIN_STARTUP.md +113 -0
- package/docs/PROVIDER_ADAPTER_CONTRACT.md +8 -2
- package/docs/PROVIDER_CREDENTIALS.md +67 -0
- package/docs/PROVIDER_EXTENSIONS.md +82 -0
- package/docs/PROVIDER_HEALTH.md +5 -1
- package/docs/PROVIDER_NETWORK.md +45 -0
- package/docs/PROVIDER_PLUGIN_CONTRACT.md +24 -0
- package/docs/PROVIDER_SETTINGS.md +91 -0
- package/docs/PROVIDER_SUBMISSION_CONTRACT.md +8 -0
- package/docs/ROADMAP.md +18 -8
- package/docs/SECURITY.md +1 -1
- package/docs/SYNC_DELIVERY_RECOVERY.md +5 -1
- package/docs/VIDEO_FLOWS.md +89 -0
- package/docs/VISION_CLI.md +1 -1
- package/docs/VISION_MODEL.md +2 -0
- package/docs/WEB_PROTOTYPE.md +21 -6
- package/docs/WEB_RECOVERY.md +46 -0
- package/docs/WORKBENCH_ARTIFACTS.md +6 -0
- package/docs/WORKBENCH_LOGIN.md +63 -0
- package/docs/assets/screenshots/core-connections-preview.webp +0 -0
- package/docs/assets/screenshots/core-model-pools-preview.webp +0 -0
- package/docs/assets/screenshots/core-overview-preview.webp +0 -0
- package/docs/assets/screenshots/core-spectrum-preview.webp +0 -0
- package/docs/assets/screenshots/core-studio-dark-preview.webp +0 -0
- package/docs/assets/screenshots/core-studio-mobile-preview.webp +0 -0
- package/docs/assets/screenshots/core-studio-preview.webp +0 -0
- package/docs/releases/0.3.0-alpha.1.md +51 -0
- package/docs/releases/0.3.0-beta.1.md +34 -0
- package/docs/screenshots.md +41 -1
- package/lib/actions.js +56 -4
- package/lib/adapters.js +75 -49
- package/lib/api.js +23 -13
- package/lib/application-runtime.js +73 -17
- package/lib/application-writer.js +193 -0
- package/lib/audio-flow-input.js +37 -0
- package/lib/client.d.ts +3 -3
- package/lib/client.js +402 -72
- package/lib/comfyui-adapter.js +95 -0
- package/lib/comfyui-client.js +60 -0
- package/lib/comfyui-inspection.js +73 -0
- package/lib/comfyui-workflow.js +94 -0
- package/lib/command-service.js +18 -5
- package/lib/config.js +49 -10
- package/lib/configured-image-runtime.js +69 -0
- package/lib/connection-skill.js +98 -0
- package/lib/connection-template.js +23 -0
- package/lib/core-artifact-store.js +84 -28
- package/lib/core-bridge-selection.js +31 -0
- package/lib/core-bridge-tools.js +85 -0
- package/lib/core-capability-catalog.js +192 -0
- package/lib/core-connection-diagnostics.js +49 -0
- package/lib/core-connection-store.js +277 -0
- package/lib/core-connections.js +225 -0
- package/lib/core-delivery-cache.js +8 -3
- package/lib/core-maintenance.js +9 -3
- package/lib/core-runtime.js +2 -1
- package/lib/core-service-client.js +174 -0
- package/lib/core-tasks.js +113 -21
- package/lib/core-writer-lease.js +16 -0
- package/lib/creation-request-store.js +41 -64
- package/lib/doctor.js +2 -2
- package/lib/dsh-core-adapter.js +61 -9
- package/lib/dsh-host-adapter.js +4 -1
- package/lib/dsh-provider-config.js +1 -1
- package/lib/dsh-service-bridge.js +67 -0
- package/lib/execution-ownership.js +43 -0
- package/lib/flow-store.js +156 -14
- package/lib/generation-input.js +10 -1
- package/lib/headless-vision.js +7 -7
- package/lib/image-edit-input.js +1 -1
- package/lib/image-flow.js +97 -31
- package/lib/index.js +47 -22
- package/lib/local-api-projection.js +15 -6
- package/lib/local-api-token-directory.js +31 -0
- package/lib/local-api.js +861 -71
- package/lib/local-creation.js +96 -24
- package/lib/local-operations.js +36 -0
- package/lib/local-request-root.js +86 -0
- package/lib/local-web-assets.js +39 -0
- package/lib/mcp-bridge.js +58 -0
- package/lib/mcp-check.js +26 -0
- package/lib/mcp-config.js +42 -0
- package/lib/mcp-delivery.js +46 -0
- package/lib/mcp-http.js +174 -0
- package/lib/mcp-server.js +69 -0
- package/lib/media-scheduler.js +194 -0
- package/lib/model-directory-service.js +37 -10
- package/lib/model-directory.js +3 -3
- package/lib/models.js +2 -2
- package/lib/music-transport.js +6 -3
- package/lib/network-config-service.js +123 -0
- package/lib/network-config.js +127 -0
- package/lib/network-doctor.js +94 -0
- package/lib/network-management.js +51 -0
- package/lib/network-policy.js +19 -0
- package/lib/network-route-evidence.js +38 -0
- package/lib/network-routing.js +283 -0
- package/lib/operation-runner.js +281 -0
- package/lib/operation-store.js +216 -0
- package/lib/panel-extension.js +36 -0
- package/lib/plugin-contract.js +194 -0
- package/lib/plugin-execution.js +51 -0
- package/lib/plugin-launch.js +86 -0
- package/lib/plugin-operation-store.js +101 -0
- package/lib/plugin-operations.js +28 -0
- package/lib/plugin-registry.js +109 -0
- package/lib/plugin-selection.js +15 -0
- package/lib/plugin-source-image.js +5 -0
- package/lib/plugin-web-assets.js +54 -0
- package/lib/plugins.d.ts +307 -0
- package/lib/plugins.js +59 -0
- package/lib/provider-adapter.js +7 -3
- package/lib/provider-adapters.js +92 -22
- package/lib/provider-catalog.js +64 -36
- package/lib/provider-config-format.js +51 -0
- package/lib/provider-config-service.js +143 -31
- package/lib/provider-context.d.ts +90 -0
- package/lib/provider-context.js +42 -0
- package/lib/provider-credentials.js +97 -0
- package/lib/provider-diagnostics.js +56 -0
- package/lib/provider-extensions.js +29 -0
- package/lib/provider-health.js +18 -6
- package/lib/provider-monitor.js +81 -0
- package/lib/provider-network.js +52 -0
- package/lib/provider-protocol.js +73 -22
- package/lib/provider-registry.js +128 -0
- package/lib/provider-response.js +38 -0
- package/lib/provider-settings.js +136 -0
- package/lib/provider-task-runner.js +132 -43
- package/lib/provider-transport.js +16 -0
- package/lib/render.js +11 -1
- package/lib/scheduling-policy.js +66 -0
- package/lib/skill-installation.js +23 -11
- package/lib/tasks.js +3 -2
- package/lib/video-flow-input.js +16 -0
- package/lib/video-input.js +12 -5
- package/lib/vision-model-routing.js +5 -0
- package/lib/web/app.js +703 -0
- package/lib/web/appearance.js +111 -0
- package/lib/web/comfyui.js +146 -0
- package/lib/web/connections.js +516 -0
- package/lib/web/creation.js +359 -0
- package/lib/web/flow-view.js +34 -0
- package/lib/web/gallery.js +133 -0
- package/lib/web/index.html +344 -0
- package/lib/web/media-client.js +218 -0
- package/lib/web/network.js +232 -0
- package/lib/web/plugin-panel.js +223 -0
- package/lib/web/plugin-status.js +140 -0
- package/lib/web/plugin-surface.css +17 -0
- package/lib/web/providers.js +513 -0
- package/lib/web/recovery.js +105 -0
- package/lib/web/startup.js +18 -0
- package/lib/web/styles.css +571 -0
- package/lib/web/transport.js +101 -0
- package/lib/web/workbench-login.js +165 -0
- package/lib/workbench-artifacts.js +3 -2
- package/lib/workbench-auth-cli.js +47 -0
- package/lib/workbench-auth.js +200 -0
- package/package.json +24 -6
- package/plugins/contact-sheet/index.d.mts +4 -0
- package/plugins/contact-sheet/index.mjs +33 -0
- package/plugins/contact-sheet/web/board.css +14 -0
- package/plugins/contact-sheet/web/board.mjs +93 -0
- package/skills/iris-core/SKILL.md +8 -4
- package/skills/iris-core/references/cli.md +2 -2
- package/skills/iris-core/references/comfyui.md +69 -0
- package/skills/iris-core/references/tasks.md +2 -0
- package/templates/plugin-starter/LICENSE +21 -0
- package/templates/plugin-starter/README.md +74 -0
- package/templates/plugin-starter/assets/peach.png +0 -0
- package/templates/plugin-starter/assets/violet.png +0 -0
- package/templates/plugin-starter/index.mjs +27 -0
- package/templates/plugin-starter/package.json +18 -0
- package/templates/plugin-starter/scripts/core-cli.mjs +20 -0
- package/templates/plugin-starter/scripts/environment.mjs +65 -0
- package/templates/plugin-starter/scripts/mcp-probe.mjs +22 -0
- package/templates/plugin-starter/scripts/setup.mjs +42 -0
- package/templates/plugin-starter/scripts/smoke.mjs +76 -0
- package/templates/plugin-starter/web/board.css +27 -0
- package/templates/plugin-starter/web/board.mjs +160 -0
- package/templates/plugin-starter/web/png.mjs +23 -0
- package/templates/provider-starter/LICENSE +21 -0
- package/templates/provider-starter/README.md +50 -0
- package/templates/provider-starter/index.d.mts +5 -0
- package/templates/provider-starter/index.mjs +165 -0
- package/templates/provider-starter/package.json +13 -0
- package/templates/provider-starter/scripts/core-cli.mjs +22 -0
- package/templates/provider-starter/scripts/environment.mjs +122 -0
- package/templates/provider-starter/scripts/fixture.mjs +54 -0
- package/templates/provider-starter/scripts/mcp-probe.mjs +21 -0
- package/templates/provider-starter/scripts/protocol-smoke.mjs +48 -0
- package/templates/provider-starter/scripts/setup.mjs +44 -0
- package/templates/provider-starter/scripts/smoke.mjs +145 -0
- package/user_guide.md +5 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Explicit startup policy; never discovers files or rewrites existing configuration.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export function schedulingError(code, message) {
|
|
6
|
+
return Object.assign(new Error(message), { code, stage: 'prepare', category: 'rate_limit', acceptance: 'not_accepted' });
|
|
7
|
+
}
|
|
8
|
+
const invalid = message => { throw schedulingError('IRIS_SCHEDULER_CONFIG_INVALID', message); };
|
|
9
|
+
function fields(value, names, where) {
|
|
10
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).some(name => !names.includes(name))) invalid(`${where} 包含无效字段`);
|
|
11
|
+
}
|
|
12
|
+
function integer(value, min, max, name) {
|
|
13
|
+
if (!Number.isSafeInteger(value) || value < min || value > max) invalid(`${name} 需要 ${min}–${max} 的整数`);
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function identity(value, where) {
|
|
17
|
+
if (typeof value !== 'string' || !value.trim() || value !== value.trim() || value.length > 256 || /[\x00-\x1f\x7f]/.test(value)) invalid(`${where} 无效`);
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function limits(value, defaults = {}) {
|
|
21
|
+
return Object.fromEntries([
|
|
22
|
+
['httpConcurrency', 1, 4], ['requestsPerSecond', 1, 1000], ['remoteLimit', 1, 1000]
|
|
23
|
+
].flatMap(([key, min, max]) => value[key] === undefined && defaults[key] === undefined ? []
|
|
24
|
+
: [[key, integer(value[key] === undefined ? defaults[key] : value[key], min, max, key)]]));
|
|
25
|
+
}
|
|
26
|
+
export function checkedSchedulingPolicy(input) {
|
|
27
|
+
fields(input, ['schemaVersion', 'httpConcurrency', 'requestsPerSecond', 'remoteLimit', 'queueLimit', 'queueTimeoutMs', 'providers'], '调度策略');
|
|
28
|
+
if (input.schemaVersion !== 1) invalid('仅支持 scheduler schemaVersion=1;不会忽略或迁移其他版本');
|
|
29
|
+
const providers = input.providers === undefined ? [] : input.providers;
|
|
30
|
+
if (!Array.isArray(providers) || providers.length > 128) invalid('providers 需要至多 128 项的数组');
|
|
31
|
+
const seen = new Set();
|
|
32
|
+
return Object.freeze({ schemaVersion: 1, ...limits(input, { httpConcurrency: 4, requestsPerSecond: 2, remoteLimit: 8 }),
|
|
33
|
+
queueLimit: integer(input.queueLimit === undefined ? 32 : input.queueLimit, 8, 128, 'queueLimit'),
|
|
34
|
+
queueTimeoutMs: integer(input.queueTimeoutMs === undefined ? 30000 : input.queueTimeoutMs, 1, 300000, 'queueTimeoutMs'),
|
|
35
|
+
providers: Object.freeze(providers.map(provider => {
|
|
36
|
+
fields(provider, ['providerId', 'httpConcurrency', 'requestsPerSecond', 'remoteLimit', 'models'], '供应商策略');
|
|
37
|
+
const providerId = identity(provider.providerId, 'providerId');
|
|
38
|
+
if (seen.has(providerId)) invalid('providerId 重复'); seen.add(providerId);
|
|
39
|
+
const models = provider.models === undefined ? [] : provider.models, ids = new Set();
|
|
40
|
+
if (!Array.isArray(models) || models.length > 256) invalid('models 需要至多 256 项的数组');
|
|
41
|
+
return Object.freeze({ providerId, ...limits(provider), models: Object.freeze(models.map(model => {
|
|
42
|
+
fields(model, ['modelId', 'httpConcurrency', 'requestsPerSecond', 'remoteLimit'], '模型策略');
|
|
43
|
+
const modelId = identity(model.modelId, 'modelId');
|
|
44
|
+
if (ids.has(modelId)) invalid('modelId 重复'); ids.add(modelId);
|
|
45
|
+
return Object.freeze({ modelId, ...limits(model) });
|
|
46
|
+
})) });
|
|
47
|
+
})) });
|
|
48
|
+
}
|
|
49
|
+
export function readSchedulingPolicy(file) {
|
|
50
|
+
if (typeof file !== 'string' || !path.isAbsolute(file)) invalid('scheduler-config 需要明确的绝对路径');
|
|
51
|
+
let fd;
|
|
52
|
+
try {
|
|
53
|
+
fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
54
|
+
const stat = fs.fstatSync(fd);
|
|
55
|
+
if (!stat.isFile() || stat.size > 262144) invalid('scheduler-config 必须是 ≤256 KiB 的普通文件');
|
|
56
|
+
if (process.platform !== 'win32' && (stat.mode & 0o077)) invalid('scheduler-config 请限制为仅当前用户可读写');
|
|
57
|
+
const bytes = fs.readFileSync(fd);
|
|
58
|
+
if (bytes.length > 262144) invalid('scheduler-config 过大');
|
|
59
|
+
let value;
|
|
60
|
+
try { value = JSON.parse(bytes.toString('utf8')); } catch { invalid('scheduler-config 不是有效 JSON'); }
|
|
61
|
+
return checkedSchedulingPolicy(value);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error.code === 'IRIS_SCHEDULER_CONFIG_INVALID') throw error;
|
|
64
|
+
throw schedulingError('IRIS_SCHEDULER_CONFIG_IO', '无法读取 scheduler-config;请检查路径、权限和文件状态,不会退回无调度模式');
|
|
65
|
+
} finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
66
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
/** Explicit
|
|
1
|
+
/** Explicit installation of the bundled Core skill or a rendered connection snapshot. No host/model I/O. */
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { connectionSkillResources } from './connection-skill.js';
|
|
7
8
|
|
|
8
9
|
const PACKAGE_ROOT = fileURLToPath(new URL('../', import.meta.url));
|
|
9
10
|
const SKILL = 'iris-core';
|
|
@@ -58,10 +59,10 @@ function sameTree(tree, recorded) {
|
|
|
58
59
|
&& JSON.stringify(tree.directories) === JSON.stringify([...recorded.directories].sort());
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
function validReceipt(value, packageName) {
|
|
62
|
+
function validReceipt(value, packageName, skillName) {
|
|
62
63
|
const relative = name => typeof name === 'string' && name.length > 0 && !name.includes('\\')
|
|
63
64
|
&& !name.includes('\0') && !name.split('/').some(part => !part || part === '.' || part === '..');
|
|
64
|
-
return value?.schemaVersion === 1 && value.packageName === packageName && value.skillName ===
|
|
65
|
+
return value?.schemaVersion === 1 && value.packageName === packageName && value.skillName === skillName
|
|
65
66
|
&& typeof value.packageVersion === 'string' && value.packageVersion.length > 0
|
|
66
67
|
&& value.files && !Array.isArray(value.files) && typeof value.files === 'object'
|
|
67
68
|
&& Object.hasOwn(value.files, 'SKILL.md') && !Object.hasOwn(value.files, RECEIPT)
|
|
@@ -77,7 +78,18 @@ function packageResources(packageRoot) {
|
|
|
77
78
|
if (!tree.files['SKILL.md'] || tree.files[RECEIPT]) {
|
|
78
79
|
throw error('IRIS_SKILL_PACKAGE_INVALID', '包内缺少 Skill 入口,或含有不应发布的安装记录');
|
|
79
80
|
}
|
|
80
|
-
return { source, metadata, ...tree };
|
|
81
|
+
return { skillName: SKILL, source, metadata, ...tree };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function bundleResources(file, packageRoot) {
|
|
85
|
+
if (typeof file !== 'string' || !path.isAbsolute(file)) throw error('IRIS_SKILL_OPTIONS_INVALID', '--bundle 必须是绝对路径');
|
|
86
|
+
const entry = fs.lstatSync(file);
|
|
87
|
+
if (!entry.isFile() || entry.isSymbolicLink() || entry.size > 16384) throw error('IRIS_SKILL_BUNDLE_INVALID', 'Skill 交付文件必须是最多 16 KiB 的普通 JSON 文件');
|
|
88
|
+
let value;
|
|
89
|
+
try { value = JSON.parse(fs.readFileSync(file, 'utf8')); }
|
|
90
|
+
catch (cause) { if (!(cause instanceof SyntaxError)) throw cause; throw error('IRIS_SKILL_BUNDLE_INVALID', 'Skill 交付文件不是有效 JSON'); }
|
|
91
|
+
const metadata = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
92
|
+
return { ...connectionSkillResources(value, metadata), source: fs.realpathSync(file) };
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
function inspect(destination, resources) {
|
|
@@ -92,7 +104,7 @@ function inspect(destination, resources) {
|
|
|
92
104
|
if (!(cause instanceof SyntaxError)) throw cause;
|
|
93
105
|
return { status: 'unmanaged', reason: 'invalid_receipt' };
|
|
94
106
|
}
|
|
95
|
-
if (!validReceipt(receipt, resources.metadata.name)) return { status: 'unmanaged', reason: 'invalid_receipt' };
|
|
107
|
+
if (!validReceipt(receipt, resources.metadata.name, resources.skillName)) return { status: 'unmanaged', reason: 'invalid_receipt' };
|
|
96
108
|
const base = { installedVersion: receipt.packageVersion };
|
|
97
109
|
let tree;
|
|
98
110
|
try { tree = readTree(destination, { installed: true, expected: receipt.files }); }
|
|
@@ -125,7 +137,7 @@ function ensureDirectory(directory) {
|
|
|
125
137
|
|
|
126
138
|
function archive(destination, control, action) {
|
|
127
139
|
const transaction = fs.mkdtempSync(path.join(control, 'backup-'));
|
|
128
|
-
const backupPath = path.join(transaction,
|
|
140
|
+
const backupPath = path.join(transaction, path.basename(destination));
|
|
129
141
|
fs.writeFileSync(path.join(transaction, 'move.json'), JSON.stringify({ schemaVersion: 1, action,
|
|
130
142
|
at: new Date().toISOString(), origin: destination, backupPath }, null, 2) + '\n', { flag: 'wx', mode: 0o600 });
|
|
131
143
|
fs.renameSync(destination, backupPath);
|
|
@@ -137,24 +149,24 @@ function copyResources(destination, resources) {
|
|
|
137
149
|
for (const relative of resources.directories) fs.mkdirSync(path.join(destination, relative), { recursive: true, mode: 0o700 });
|
|
138
150
|
for (const [relative, bytes] of Object.entries(resources.bytes)) fs.writeFileSync(path.join(destination, relative), bytes, { flag: 'wx', mode: 0o600 });
|
|
139
151
|
fs.writeFileSync(path.join(destination, RECEIPT), JSON.stringify({ schemaVersion: 1,
|
|
140
|
-
packageName: resources.metadata.name, packageVersion: resources.metadata.version, skillName:
|
|
152
|
+
packageName: resources.metadata.name, packageVersion: resources.metadata.version, skillName: resources.skillName,
|
|
141
153
|
installedAt: new Date().toISOString(), files: resources.files, directories: resources.directories
|
|
142
154
|
}, null, 2) + '\n', { flag: 'wx', mode: 0o600 });
|
|
143
155
|
}
|
|
144
156
|
|
|
145
157
|
/** Internal CLI service. Explicit mutations only; no postinstall or public SDK export. */
|
|
146
|
-
export function manageCoreSkill({ action, target, directory, dryRun = false }, {
|
|
158
|
+
export function manageCoreSkill({ action, target, directory, bundle, dryRun = false }, {
|
|
147
159
|
packageRoot = PACKAGE_ROOT, env = process.env, home = os.homedir()
|
|
148
160
|
} = {}) {
|
|
149
161
|
if (!['install', 'status', 'uninstall'].includes(action) || action === 'status' && dryRun) {
|
|
150
162
|
throw error('IRIS_SKILL_OPTIONS_INVALID', '使用 skills install/status/uninstall;status 不接受 --dry-run');
|
|
151
163
|
}
|
|
152
|
-
const resources = packageResources(packageRoot);
|
|
153
|
-
const resolved = resolveDirectory({ target, directory, env, home }), destination = path.join(resolved,
|
|
164
|
+
const resources = bundle === undefined ? packageResources(packageRoot) : bundleResources(bundle, packageRoot);
|
|
165
|
+
const resolved = resolveDirectory({ target, directory, env, home }), destination = path.join(resolved, resources.skillName);
|
|
154
166
|
if (isWithin(resources.source, destination) || isWithin(destination, resources.source)) {
|
|
155
167
|
throw error('IRIS_SKILL_OPTIONS_INVALID', '安装目标不能与包内 Skill 源目录重叠');
|
|
156
168
|
}
|
|
157
|
-
const base = { skill:
|
|
169
|
+
const base = { skill: resources.skillName, target: target || 'directory', directory: resolved, destination,
|
|
158
170
|
packageVersion: resources.metadata.version, discovery: 'not_checked' };
|
|
159
171
|
const before = inspect(destination, resources);
|
|
160
172
|
const result = { ...base, ...before };
|
package/lib/tasks.js
CHANGED
|
@@ -718,7 +718,7 @@ export async function submitGuard(task, fn) {
|
|
|
718
718
|
/**
|
|
719
719
|
* 重启恢复:接管仍在运行的远程任务。DSH 重启期间百炼任务在服务端继续跑,
|
|
720
720
|
* 结果 URL 存活 24h,重启后重新盯守即可落袋。
|
|
721
|
-
* @param {(task) => deps|null} depsFor 返回盯守依赖;null
|
|
721
|
+
* @param {(task) => deps|null} depsFor 返回盯守依赖;null 仅暂停接管(如连接暂不可用),不证明远端失败
|
|
722
722
|
* @returns {string[]} 成功接管的任务 id
|
|
723
723
|
*/
|
|
724
724
|
export function resumePending(depsFor) {
|
|
@@ -737,9 +737,10 @@ export function resumePending(depsFor) {
|
|
|
737
737
|
}
|
|
738
738
|
const deps = depsFor(t);
|
|
739
739
|
if (!deps) {
|
|
740
|
-
update(t.id, {
|
|
740
|
+
update(t.id, { error: '无法恢复盯守:对应供应商不可用;远端任务状态未改变,可在连接恢复后再次接管' });
|
|
741
741
|
continue;
|
|
742
742
|
}
|
|
743
|
+
if (t.error) update(t.id, { error: undefined });
|
|
743
744
|
watch(t, deps);
|
|
744
745
|
resumed.push(t.id);
|
|
745
746
|
continue;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { normalizeGenerationInput } from './generation-input.js';
|
|
2
|
+
import { parseModelRef } from './models.js';
|
|
3
|
+
|
|
4
|
+
export const VIDEO_RECIPE = 'video-generate/v0';
|
|
5
|
+
export const MAX_VIDEO_BYTES = 32 * 1024 * 1024;
|
|
6
|
+
export const VIDEO_TYPES = Object.freeze({ 'video/mp4': 'mp4', 'video/webm': 'webm' });
|
|
7
|
+
export function normalizeVideoFlowInput(input) {
|
|
8
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)
|
|
9
|
+
|| Object.keys(input).some(key => !['model_ref', 'prompt', 'size', 'duration'].includes(key))
|
|
10
|
+
|| !parseModelRef(input.model_ref) || /s2v/i.test(parseModelRef(input.model_ref).modelId)
|
|
11
|
+
|| input.duration !== undefined && (typeof input.duration !== 'number' || !Number.isFinite(input.duration))) {
|
|
12
|
+
throw Object.assign(new Error('文生视频仅接受明确模型、prompt 和可选 size/duration;不接受首帧、S2V 或路径'), { code: 'IRIS_VIDEO_FLOW_INPUT_INVALID' });
|
|
13
|
+
}
|
|
14
|
+
const value = normalizeGenerationInput('video', input);
|
|
15
|
+
return { model_ref: value.modelRef, ...value.providerInput };
|
|
16
|
+
}
|
package/lib/video-input.js
CHANGED
|
@@ -7,19 +7,26 @@ import { parseModelRef } from './models.js';
|
|
|
7
7
|
import { prepareProviderInput } from './provider-adapter.js';
|
|
8
8
|
import { ProviderContractError } from './provider-contract.js';
|
|
9
9
|
|
|
10
|
+
/** Recognized input families constrain routing; opaque compatible IDs keep their existing t2v/i2v support. */
|
|
11
|
+
export function videoModelSupportsMode(modelId, mode) {
|
|
12
|
+
const family = /(?:^|[-_.])(t2v|i2v|s2v|r2v)(?=$|[-_.])/i.exec(modelId)?.[1].toLowerCase();
|
|
13
|
+
if (family) return family === mode;
|
|
14
|
+
return mode === 't2v' || mode === 'i2v';
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
export function videoTaskCandidates(candidates, normalized) {
|
|
11
18
|
const s2v = normalized.videoMode === 's2v';
|
|
12
|
-
const compatible = candidates.filter(candidate =>
|
|
19
|
+
const compatible = candidates.filter(candidate => videoModelSupportsMode(parseModelRef(candidate.model)?.modelId || '', normalized.videoMode));
|
|
13
20
|
if (!compatible.length) throw new GenerationInputError(s2v
|
|
14
|
-
? 'audio_path 只能配合 s2v 数字人模型' : 's2v 模型需要首帧和音频输入');
|
|
21
|
+
? 'audio_path 只能配合 s2v 数字人模型' : '没有与 ' + normalized.videoMode + ' 输入匹配的视频模型;s2v 模型需要首帧和音频输入');
|
|
15
22
|
return compatible.map(candidate => ({ ...candidate,
|
|
16
|
-
...(normalized.firstFramePath ? { async prepareInput({ signal }) {
|
|
23
|
+
...(normalized.firstFramePath ? { async prepareInput({ signal, taskId, attemptId, networkBudget, recordNetworkRoute }) {
|
|
17
24
|
if (signal.aborted) throw new ProviderContractError('视频输入准备已取消', { stage: 'prepare', category: 'aborted', acceptance: 'not_accepted' });
|
|
18
25
|
if (s2v) {
|
|
19
26
|
const model = parseModelRef(candidate.model).modelId;
|
|
20
|
-
const image = await prepareProviderInput(candidate.adapter, { model, filePath: normalized.firstFramePath, signal });
|
|
27
|
+
const image = await prepareProviderInput(candidate.adapter, { model, filePath: normalized.firstFramePath, signal }, { taskId, attemptId, networkBudget, recordNetworkRoute });
|
|
21
28
|
if (signal.aborted) throw new ProviderContractError('视频输入准备已取消', { stage: 'upload', category: 'aborted', acceptance: 'not_accepted' });
|
|
22
|
-
const audio = await prepareProviderInput(candidate.adapter, { model, filePath: normalized.audioPath, signal });
|
|
29
|
+
const audio = await prepareProviderInput(candidate.adapter, { model, filePath: normalized.audioPath, signal }, { taskId, attemptId, networkBudget, recordNetworkRoute });
|
|
23
30
|
return { ...normalized.providerInput, imgDataUrl: image.url, audioUrl: audio.url };
|
|
24
31
|
}
|
|
25
32
|
const mime = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.webp': 'image/webp', '.gif': 'image/gif' }[path.extname(normalized.firstFramePath).toLowerCase()];
|
|
@@ -12,6 +12,7 @@ import { modelAvailabilityBinding, modelUnavailable } from './model-directory.js
|
|
|
12
12
|
import { completeVision, VISION_BUDGET } from './vision-core.js';
|
|
13
13
|
import { open } from 'node:fs/promises';
|
|
14
14
|
import { DEFAULT_VISION_INPUT, visionInputLimits } from './vision-image-input.js';
|
|
15
|
+
import { providerNetworkRequirement, networkPolicyError } from './network-policy.js';
|
|
15
16
|
|
|
16
17
|
const features = Object.freeze({ system: 'unknown', temperature: 'unknown', maxOutputTokens: 'unknown', reasoning: 'unknown' });
|
|
17
18
|
|
|
@@ -50,6 +51,7 @@ function deferredDshPort(host, { prepareImages, orientImages, prepareImageReques
|
|
|
50
51
|
/** 配置只绑定一个自持模型;CLI 不附加宿主候选。 */
|
|
51
52
|
export function createConfiguredVisionModelPort(provider, model, { allowRateLimited = false, onRateLimit, onModelAvailability, prepareImages = false,
|
|
52
53
|
orientImages = false, prepareImageRequest } = {}) {
|
|
54
|
+
if (providerNetworkRequirement(provider)) throw networkPolicyError('IRIS_NETWORK_CAPABILITY_UNSUPPORTED', '独立视觉尚未接入此网络目录;已停止调用,不会默认直连。请保留原视觉配置');
|
|
53
55
|
const parsed = parseModelRef(resolveModelRef(provider, 'vision', model));
|
|
54
56
|
const id = 'iris-provider:' + encodeURIComponent(provider.id);
|
|
55
57
|
if (!parsed) return unavailablePort(id);
|
|
@@ -61,6 +63,9 @@ export function createConfiguredVisionModelPort(provider, model, { allowRateLimi
|
|
|
61
63
|
orientImages, prepareImageRequest,
|
|
62
64
|
...(prepareImages ? { imageInput: visionInputLimits(provider, parsed.modelId) } : {}) });
|
|
63
65
|
const availability = result => {
|
|
66
|
+
// Ordinary vision/OCR is read-only for provider configuration. Explicit probes
|
|
67
|
+
// and definitive failures still update availability through the existing hook.
|
|
68
|
+
if (result.ok === true && !allowRateLimited) return;
|
|
64
69
|
const event = { ...result, stage: 'submit', source: allowRateLimited ? 'probe' : 'task',
|
|
65
70
|
binding: modelAvailabilityBinding(provider, parsed.modelId, 'vision') };
|
|
66
71
|
if (!recordModelAvailability(provider, parsed.modelId, 'vision', event)) return;
|