@ran-sh/dsh-crew 1.5.1 → 1.6.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/.claude-plugin/plugin.json +1 -1
- package/lib/client.js +2 -905
- package/official-web-bridge/lib/client.js +1 -71
- package/package.json +1 -1
- package/src/client/collapsible-sections.mjs +5 -7
- package/src/client/index.tsx +3 -357
- package/src/client/quick-entry.tsx +1 -1
- package/src/client/quick-panel.tsx +3 -38
- package/src/client/surface-detection.mjs +1 -1
- package/src/hub/index.mjs +1 -76
- package/src/i18n.mjs +1 -1
- package/src/install/install-legacy.mjs +728 -739
- package/src/policy-legacy.mjs +1 -51
- package/src/provider-inventory.mjs +1 -3
- package/src/runtime-controls.mjs +1 -7
- package/src/runtime-identity.mjs +1 -1
- package/src/server.mjs +1 -2
- package/src/image-output.mjs +0 -71
- package/src/multimodal.mjs +0 -686
- package/src/vision-route.mjs +0 -138
package/src/policy-legacy.mjs
CHANGED
|
@@ -506,15 +506,6 @@ export function normalizeGlobalConfig(raw = {}) {
|
|
|
506
506
|
else { flashState = flashState ?? 'auto'; proState = proState ?? 'auto'; }
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
-
// Capability switches: an old config with provider=off must not flip the
|
|
510
|
-
// capability back on; anything else keeps the capability enabled.
|
|
511
|
-
const visionEnabled = has('vision_enabled')
|
|
512
|
-
? normalizeEnabled(raw.vision_enabled)
|
|
513
|
-
: raw.vision_provider !== 'off';
|
|
514
|
-
const imagegenEnabled = has('imagegen_enabled')
|
|
515
|
-
? normalizeEnabled(raw.imagegen_enabled)
|
|
516
|
-
: raw.imagegen_provider !== 'off';
|
|
517
|
-
|
|
518
509
|
const normalized = {
|
|
519
510
|
...raw,
|
|
520
511
|
subagents_enabled: normalizeEnabled(raw.subagents_enabled),
|
|
@@ -534,8 +525,6 @@ export function normalizeGlobalConfig(raw = {}) {
|
|
|
534
525
|
pro_model_priority: normalizeModelPriority(raw.pro_model_priority),
|
|
535
526
|
pro_model_priority_configured: raw.pro_model_priority_configured === true || normalizeModelPriority(raw.pro_model_priority).length > 0,
|
|
536
527
|
pro_model_fallback: raw.pro_model_fallback === 'harness-default' ? raw.pro_model_fallback : 'harness-default',
|
|
537
|
-
vision_enabled: visionEnabled,
|
|
538
|
-
imagegen_enabled: imagegenEnabled,
|
|
539
528
|
};
|
|
540
529
|
// This normalized legacy view is the single input to the v0.2 canonical
|
|
541
530
|
// migration, so every consumer sees the same worker/reviewer model policy
|
|
@@ -788,43 +777,4 @@ export function validateRoles(raw) {
|
|
|
788
777
|
if (!seen.has(r)) { seen.add(r); out.push(r); }
|
|
789
778
|
}
|
|
790
779
|
return { roles: out, dropped };
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// ---------- multimodal capability ----------
|
|
794
|
-
|
|
795
|
-
/** Capability availability after both the switch and the provider are checked. */
|
|
796
|
-
export function getCapabilities(config) {
|
|
797
|
-
const c = config;
|
|
798
|
-
const vision = {
|
|
799
|
-
enabled: normalizeBool(c.vision_enabled, true),
|
|
800
|
-
provider: c.vision_provider ?? 'off',
|
|
801
|
-
providerOff: c.vision_provider === 'off',
|
|
802
|
-
usable: normalizeBool(c.vision_enabled, true) && c.vision_provider !== 'off',
|
|
803
|
-
};
|
|
804
|
-
const imagegen = {
|
|
805
|
-
enabled: normalizeBool(c.imagegen_enabled, true),
|
|
806
|
-
provider: c.imagegen_provider ?? 'off',
|
|
807
|
-
providerOff: c.imagegen_provider === 'off',
|
|
808
|
-
usable: normalizeBool(c.imagegen_enabled, true) && c.imagegen_provider !== 'off',
|
|
809
|
-
};
|
|
810
|
-
return { vision, imagegen };
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
/**
|
|
814
|
-
* Registration plan for the hub's multimodal bridge: which tools to register
|
|
815
|
-
* and whether the vision route (deepseek-vision adapter + transcription
|
|
816
|
-
* waterfall) should be installed. Tool names are the decision points; the
|
|
817
|
-
* hub applies the plan at plugin boot, so capability switches take effect
|
|
818
|
-
* after a DSH restart.
|
|
819
|
-
*/
|
|
820
|
-
export function getMultimodalRegistrationPlan(config) {
|
|
821
|
-
const { vision, imagegen } = getCapabilities(config);
|
|
822
|
-
return {
|
|
823
|
-
tools: {
|
|
824
|
-
describe_image: vision.usable,
|
|
825
|
-
generate_image: imagegen.usable,
|
|
826
|
-
},
|
|
827
|
-
visionRoute: vision.usable,
|
|
828
|
-
requiresRestart: true,
|
|
829
|
-
};
|
|
830
|
-
}
|
|
780
|
+
}
|
|
@@ -92,7 +92,7 @@ function modelIds(provider) {
|
|
|
92
92
|
* Build a secret-free provider inventory from live Harness and Crew-owned
|
|
93
93
|
* observations. No credential value is accepted or copied into the result.
|
|
94
94
|
*/
|
|
95
|
-
export function buildProviderInventory({ catalog = {}, declarations = [], policy = {}, tombstones = {}, activeJobs = [],
|
|
95
|
+
export function buildProviderInventory({ catalog = {}, declarations = [], policy = {}, tombstones = {}, activeJobs = [], additionalProviderIds = [] } = {}) {
|
|
96
96
|
const catalogProviders = Array.isArray(catalog?.providers) ? catalog.providers : [];
|
|
97
97
|
const safeDeclarations = Array.isArray(declarations) ? declarations : [];
|
|
98
98
|
const ids = [];
|
|
@@ -121,7 +121,6 @@ export function buildProviderInventory({ catalog = {}, declarations = [], policy
|
|
|
121
121
|
? activeJobs.filter((job) => normalizeProviderId(job?.provider) === id
|
|
122
122
|
&& !['done', 'failed', 'cancelled'].includes(job?.status)).length
|
|
123
123
|
: Number.isInteger(activeJobs?.[id]) ? Math.max(0, activeJobs[id]) : 0;
|
|
124
|
-
const multimodal = Array.isArray(multimodalRefs?.[id]) ? multimodalRefs[id].length : Number.isInteger(multimodalRefs?.[id]) ? Math.max(0, multimodalRefs[id]) : 0;
|
|
125
124
|
const declarationFile = text(declaration?.file);
|
|
126
125
|
const locator = text(declaration?.locator);
|
|
127
126
|
|
|
@@ -173,7 +172,6 @@ export function buildProviderInventory({ catalog = {}, declarations = [], policy
|
|
|
173
172
|
reviewer_priority: findPriorityIndex(policy?.reviewer ?? policy?.review, 'priority', id),
|
|
174
173
|
reviewer_escalation: findPriorityIndex(policy?.reviewer ?? policy?.review, 'escalation_priority', id),
|
|
175
174
|
active_jobs: active,
|
|
176
|
-
multimodal_refs: multimodal,
|
|
177
175
|
},
|
|
178
176
|
desired_state: tombstoned ? 'absent' : 'present',
|
|
179
177
|
activation: declared ? 'restart-required' : 'live',
|
package/src/runtime-controls.mjs
CHANGED
|
@@ -41,9 +41,6 @@ export const RUNTIME_SETTING_ACTIVATION = Object.freeze({
|
|
|
41
41
|
pro_model_fallback: Object.freeze({ global: N, session: null }),
|
|
42
42
|
adaptive_routing: Object.freeze({ global: N, session: null, note: 'Opt-in provider health ordering is read for the next Hub model selection; existing explicit priorities remain authoritative.' }),
|
|
43
43
|
isolation: Object.freeze({ global: N, session: null }),
|
|
44
|
-
vision_provider: Object.freeze({ global: N, session: null }),
|
|
45
|
-
vision_model: Object.freeze({ global: N, session: null }),
|
|
46
|
-
imagegen_provider: Object.freeze({ global: N, session: null }),
|
|
47
44
|
|
|
48
45
|
// These values are copied into the MCP session defaults at process startup.
|
|
49
46
|
// dsh_worker_config can override them for subsequent workflows in that same
|
|
@@ -56,11 +53,8 @@ export const RUNTIME_SETTING_ACTIVATION = Object.freeze({
|
|
|
56
53
|
preset_flash: Object.freeze({ global: S, session: N }),
|
|
57
54
|
preset_pro: Object.freeze({ global: S, session: N }),
|
|
58
55
|
|
|
59
|
-
// hub-client freezes its base URL at module initialization.
|
|
60
|
-
// Image Generation tool registration is also decided at Hub plugin boot.
|
|
56
|
+
// hub-client freezes its base URL at module initialization.
|
|
61
57
|
hub_url: Object.freeze({ global: R, session: null }),
|
|
62
|
-
vision_enabled: Object.freeze({ global: R, session: null }),
|
|
63
|
-
imagegen_enabled: Object.freeze({ global: R, session: null }),
|
|
64
58
|
});
|
|
65
59
|
|
|
66
60
|
export function activationForSetting(key) {
|
package/src/runtime-identity.mjs
CHANGED
|
@@ -36,7 +36,7 @@ export {
|
|
|
36
36
|
// included in the identity contract.
|
|
37
37
|
const RUNTIME_ID = randomUUID();
|
|
38
38
|
|
|
39
|
-
export const RUNTIME_VERSION = '1.
|
|
39
|
+
export const RUNTIME_VERSION = '1.6.1';
|
|
40
40
|
export const HUB_PROTOCOL_VERSION = 1;
|
|
41
41
|
|
|
42
42
|
export const HUB_CAPABILITIES = Object.freeze([
|
package/src/server.mjs
CHANGED
|
@@ -305,10 +305,9 @@ const SAFE_GLOBAL_KEYS = [
|
|
|
305
305
|
'default_tier', 'default_effort', 'mode', 'default_timeout_seconds', 'hub_url',
|
|
306
306
|
'tier_policy', 'escalate_on_failure', 'subagents_enabled', 'collaboration_mode',
|
|
307
307
|
'main_agent_mode', 'flash_state', 'pro_state', 'flash_roles', 'pro_roles',
|
|
308
|
-
'pro_reviews_flash', 'worker_provider_mode',
|
|
308
|
+
'pro_reviews_flash', 'worker_provider_mode',
|
|
309
309
|
'flash_model_priority', 'flash_model_priority_configured', 'flash_model_fallback',
|
|
310
310
|
'pro_model_priority', 'pro_model_priority_configured', 'pro_model_fallback',
|
|
311
|
-
'vision_provider', 'vision_model', 'imagegen_provider',
|
|
312
311
|
'preset_flash', 'preset_pro',
|
|
313
312
|
];
|
|
314
313
|
|
package/src/image-output.mjs
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync, mkdirSync, lstatSync, openSync, readSync, closeSync, renameSync, rmSync } from 'node:fs';
|
|
2
|
-
import { basename, dirname, isAbsolute, join } from 'node:path';
|
|
3
|
-
|
|
4
|
-
export async function downloadImageBytes(url, { fetchImpl = globalThis.fetch, timeoutMs = 60_000, maxBytes = 64 * 1024 * 1024 } = {}) {
|
|
5
|
-
if (!['http:', 'https:'].includes(new URL(url).protocol)) throw new Error('unsupported image URL protocol');
|
|
6
|
-
const controller = new AbortController();
|
|
7
|
-
let reader;
|
|
8
|
-
let timer;
|
|
9
|
-
const timeout = new Promise((_, reject) => {
|
|
10
|
-
timer = setTimeout(() => {
|
|
11
|
-
controller.abort();
|
|
12
|
-
reject(new Error('image download timed out'));
|
|
13
|
-
}, timeoutMs);
|
|
14
|
-
});
|
|
15
|
-
const download = async () => {
|
|
16
|
-
const response = await fetchImpl(url, { signal: controller.signal });
|
|
17
|
-
controller.signal.throwIfAborted();
|
|
18
|
-
if (!response.ok) throw new Error(`image download failed: HTTP ${response.status}`);
|
|
19
|
-
if (Number(response.headers.get('content-length')) > maxBytes) throw new Error('image download exceeds size limit');
|
|
20
|
-
if (!response.body) throw new Error('image download has no body');
|
|
21
|
-
reader = response.body.getReader();
|
|
22
|
-
const chunks = [];
|
|
23
|
-
let bytes = 0;
|
|
24
|
-
while (true) {
|
|
25
|
-
const { done, value } = await reader.read();
|
|
26
|
-
controller.signal.throwIfAborted();
|
|
27
|
-
if (done) break;
|
|
28
|
-
bytes += value.byteLength;
|
|
29
|
-
if (bytes > maxBytes) throw new Error('image download exceeds size limit');
|
|
30
|
-
chunks.push(Buffer.from(value));
|
|
31
|
-
}
|
|
32
|
-
return Buffer.concat(chunks, bytes);
|
|
33
|
-
};
|
|
34
|
-
try { return await Promise.race([download(), timeout]); }
|
|
35
|
-
finally {
|
|
36
|
-
clearTimeout(timer);
|
|
37
|
-
controller.abort();
|
|
38
|
-
if (reader) void reader.cancel().catch(() => {});
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function validateImage(file) {
|
|
43
|
-
const info = lstatSync(file);
|
|
44
|
-
if (!info.isFile() || info.size === 0 || info.size > 64 * 1024 * 1024) throw new Error('generated image has invalid type or size');
|
|
45
|
-
const head = Buffer.alloc(32);
|
|
46
|
-
const fd = openSync(file, 'r');
|
|
47
|
-
let length;
|
|
48
|
-
try { length = readSync(fd, head, 0, head.length, 0); } finally { closeSync(fd); }
|
|
49
|
-
const bytes = head.subarray(0, length);
|
|
50
|
-
const png = bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
|
|
51
|
-
const jpeg = bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255;
|
|
52
|
-
const gif = ['GIF87a', 'GIF89a'].includes(bytes.subarray(0, 6).toString('ascii'));
|
|
53
|
-
const webp = bytes.subarray(0, 4).toString('ascii') === 'RIFF' && bytes.subarray(8, 12).toString('ascii') === 'WEBP';
|
|
54
|
-
if (!png && !jpeg && !gif && !webp) throw new Error('generated output does not have a supported image signature');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** Stage each request separately; an existing destination never proves success. */
|
|
58
|
-
export async function withGeneratedImageOutput(outputPath, generate) {
|
|
59
|
-
if (!isAbsolute(outputPath)) throw new Error('output_path must be absolute');
|
|
60
|
-
mkdirSync(dirname(outputPath), { recursive: true });
|
|
61
|
-
const stage = mkdtempSync(join(dirname(outputPath), '.dsh-image-'));
|
|
62
|
-
const stagedFile = join(stage, basename(outputPath));
|
|
63
|
-
try {
|
|
64
|
-
await generate(stagedFile);
|
|
65
|
-
validateImage(stagedFile);
|
|
66
|
-
renameSync(stagedFile, outputPath);
|
|
67
|
-
return outputPath;
|
|
68
|
-
} finally {
|
|
69
|
-
rmSync(stage, { recursive: true, force: true });
|
|
70
|
-
}
|
|
71
|
-
}
|