@ran-sh/dsh-crew 2.2.14 → 2.2.16
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ran-sh/dsh-crew",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/hub/entry.mjs",
|
|
6
6
|
"bin": {
|
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
"description": "DeepSeek Harness plugin: dispatch work to DSH agents from Claude Code / Codex, as native subagents with live progress",
|
|
10
10
|
"dshCrew": {
|
|
11
11
|
"payloadSchema": 2,
|
|
12
|
-
"windowsSupervisorHandoff": 1
|
|
12
|
+
"windowsSupervisorHandoff": 1,
|
|
13
|
+
"payloadRewrite": {
|
|
14
|
+
"schema": 1,
|
|
15
|
+
"rewritten": false
|
|
16
|
+
}
|
|
13
17
|
},
|
|
14
18
|
"dsh": {
|
|
15
19
|
"bundle": {
|
package/src/dsh-cohort.mjs
CHANGED
|
@@ -7,6 +7,76 @@ export const DSH_CLI_PACKAGE = '@deepseek-ai/dsh';
|
|
|
7
7
|
export const TARGET_DSH_VERSION = '0.1.6-alpha.1';
|
|
8
8
|
export const TARGET_DSH_SPEC = `${DSH_CLI_PACKAGE}@${TARGET_DSH_VERSION}`;
|
|
9
9
|
|
|
10
|
+
export const DSH_SCOPE = '@deepseek-ai/';
|
|
11
|
+
|
|
12
|
+
/** Cohort namespace: the CLI plus every `@deepseek-ai/dsh-*` package. */
|
|
13
|
+
export function isDshCohortPackage(name) {
|
|
14
|
+
return name === DSH_CLI_PACKAGE || name.startsWith(`${DSH_CLI_PACKAGE}-`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// WHAT THIS LIST IS: the names a persisted Crew payload pins to
|
|
18
|
+
// TARGET_DSH_VERSION when the plugin declares them. It is the payload's cohort
|
|
19
|
+
// authority, and it answers one question — "which exact version does this
|
|
20
|
+
// package resolve to inside a release?"
|
|
21
|
+
//
|
|
22
|
+
// WHAT IT IS NOT: an inventory of everything that must exist at run time. The
|
|
23
|
+
// runtime tree is a cohort of its own, and `src/dsh-cli-runtime.mjs` judges it
|
|
24
|
+
// dynamically — it walks every installed @deepseek-ai/dsh* copy and compares it
|
|
25
|
+
// against TARGET_DSH_VERSION, so a member it was never told about still fails
|
|
26
|
+
// the reuse gate instead of drifting silently. Adding a name here does not make
|
|
27
|
+
// the runtime install it; removing one does not excuse it from that judgement.
|
|
28
|
+
//
|
|
29
|
+
// Two boundary cases, recorded so they are not re-litigated:
|
|
30
|
+
// * @deepseek-ai/dsh-base and @deepseek-ai/dsh-web-app are PROFILE BUNDLES,
|
|
31
|
+
// named by CREW_PROFILE_DEFAULT_BUNDLES in src/dsh-cli-runtime.mjs. They
|
|
32
|
+
// belong to the runtime tree the profile composes, not to this plugin's
|
|
33
|
+
// peer set, so they are correctly absent here. A payload never vendors them.
|
|
34
|
+
// * @deepseek-ai/dsh-agent-presets is listed but currently inert: nothing in
|
|
35
|
+
// package.json declares it, and only a declared name is pinned. It stays
|
|
36
|
+
// because it is the 0.1.6-alpha.1 provider of the `agentPresets` service this
|
|
37
|
+
// plugin resolves, and because its name dies at 0.1.7 — a host that needs the
|
|
38
|
+
// provider must not get it from the peer block, so the only way it can ever
|
|
39
|
+
// be pinned to the runtime cohort is from this list.
|
|
40
|
+
//
|
|
41
|
+
// The list is authoritative wherever a member is declared: a peer range and an
|
|
42
|
+
// explicit dependency both resolve to TARGET_DSH_VERSION in the staged payload.
|
|
43
|
+
// A package that exists only in a later cohort — a rename, for example — belongs
|
|
44
|
+
// in the peer block for that host and must NOT be added here until
|
|
45
|
+
// TARGET_DSH_VERSION moves with it.
|
|
46
|
+
export const DSH_COHORT_PACKAGES = Object.freeze([
|
|
47
|
+
DSH_CLI_PACKAGE,
|
|
48
|
+
'@deepseek-ai/dsh-agent',
|
|
49
|
+
'@deepseek-ai/dsh-agent-loop',
|
|
50
|
+
'@deepseek-ai/dsh-agent-presets',
|
|
51
|
+
'@deepseek-ai/dsh-bash-local',
|
|
52
|
+
'@deepseek-ai/dsh-client-locale',
|
|
53
|
+
'@deepseek-ai/dsh-client-ui-renderer',
|
|
54
|
+
'@deepseek-ai/dsh-client-ui-settings',
|
|
55
|
+
'@deepseek-ai/dsh-compaction-basic',
|
|
56
|
+
'@deepseek-ai/dsh-fs-local',
|
|
57
|
+
'@deepseek-ai/dsh-fs-observation-policy',
|
|
58
|
+
'@deepseek-ai/dsh-llm',
|
|
59
|
+
'@deepseek-ai/dsh-llm-deepseek',
|
|
60
|
+
'@deepseek-ai/dsh-sandbox-local',
|
|
61
|
+
'@deepseek-ai/dsh-sandbox-policy',
|
|
62
|
+
'@deepseek-ai/dsh-sdk-client',
|
|
63
|
+
'@deepseek-ai/dsh-sdk-jsonrpc-server',
|
|
64
|
+
'@deepseek-ai/dsh-sdk-minimal',
|
|
65
|
+
'@deepseek-ai/dsh-session',
|
|
66
|
+
'@deepseek-ai/dsh-session-persistence-jsonl',
|
|
67
|
+
'@deepseek-ai/dsh-subprocess-local',
|
|
68
|
+
'@deepseek-ai/dsh-token-meter',
|
|
69
|
+
'@deepseek-ai/dsh-tool-fs',
|
|
70
|
+
'@deepseek-ai/dsh-tool-todo',
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
/** Exact `name -> TARGET_DSH_VERSION` specs for the vendored payload cohort. */
|
|
74
|
+
export function dshCohortPins(version = TARGET_DSH_VERSION) {
|
|
75
|
+
const pins = {};
|
|
76
|
+
for (const name of DSH_COHORT_PACKAGES) pins[name] = version;
|
|
77
|
+
return pins;
|
|
78
|
+
}
|
|
79
|
+
|
|
10
80
|
// Retained-cohort support: when a release's manifest pins an older DSH
|
|
11
81
|
// cohort than the current TARGET, the old runtime is retained on disk so a
|
|
12
82
|
// rollback can restore it offline (no registry round-trip).
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
writeFileSync,
|
|
37
37
|
} from 'node:fs';
|
|
38
38
|
import { createRequire } from 'node:module';
|
|
39
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
39
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
40
40
|
import { fileURLToPath } from 'node:url';
|
|
41
41
|
import { homedir } from 'node:os';
|
|
42
42
|
import * as realInstaller from './install.mjs';
|
|
@@ -45,7 +45,7 @@ import { crewDshHome, crewProfileDir, claudeIntegrationLine } from './install.mj
|
|
|
45
45
|
import { integrationRoot } from './crew-paths.mjs';
|
|
46
46
|
import { releaseClaimsState } from '../release-in-use.mjs';
|
|
47
47
|
import { compareProcessToken, processStartToken } from '../process-identity.mjs';
|
|
48
|
-
import { RELEASE_COHORT_FILENAME } from '../dsh-cohort.mjs';
|
|
48
|
+
import { DSH_SCOPE, RELEASE_COHORT_FILENAME, dshCohortPins } from '../dsh-cohort.mjs';
|
|
49
49
|
import { renameTree } from './tree-move.mjs';
|
|
50
50
|
import { checkRuntimeAdvance, normalizeRuntimeState, runtimeStateMayHaveStarted } from './runtime-lifecycle.mjs';
|
|
51
51
|
import { ensureCrewDshRuntime, ensureCrewPluginRegistration, ensureCrewWebProfile, removeCrewPluginRegistration, migrateCrewDshRuntime, installDshInto, restoreRetainedRuntime, crewDshRuntimeRoot, payloadDshVersion, gcRetainedRuntimes, TARGET_DSH_VERSION } from '../dsh-cli-runtime.mjs';
|
|
@@ -998,6 +998,22 @@ export function exactSpecOverrides(dependencies) {
|
|
|
998
998
|
|
|
999
999
|
// ---- payload staging ---------------------------------------------------------
|
|
1000
1000
|
|
|
1001
|
+
// Which manifest form a file is: absent or `rewritten: false` is the shape npm
|
|
1002
|
+
// published, `rewritten: true` is the shape the CLI staged. A version number alone
|
|
1003
|
+
// cannot tell the two apart, and they do not behave alike (`npm ls`, the dependency
|
|
1004
|
+
// tree, and any future integrity check all see different content), so a bug report
|
|
1005
|
+
// has to be able to name its form without guessing.
|
|
1006
|
+
export function payloadRewriteRecord({ source, dshVersion, now = new Date() } = {}) {
|
|
1007
|
+
const kind = typeof source?.kind === 'string' && source.kind ? source.kind : 'unknown';
|
|
1008
|
+
const record = { schema: 1, rewritten: true, source: { kind }, targetDshVersion: dshVersion ?? null };
|
|
1009
|
+
for (const key of ['spec', 'integrity', 'file', 'path', 'version']) {
|
|
1010
|
+
const value = source?.[key];
|
|
1011
|
+
if (typeof value === 'string' && value) record.source[key] = value;
|
|
1012
|
+
}
|
|
1013
|
+
if (now instanceof Date && !Number.isNaN(now.valueOf())) record.rewrittenAt = now.toISOString();
|
|
1014
|
+
return record;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1001
1017
|
/**
|
|
1002
1018
|
* Stage the candidate into its FINAL release directory, guarded by an
|
|
1003
1019
|
* incompleteness marker. Committing = removing the marker (a single-file
|
|
@@ -1005,13 +1021,15 @@ export function exactSpecOverrides(dependencies) {
|
|
|
1005
1021
|
* pointer last. This avoids directory renames, which Windows can refuse with
|
|
1006
1022
|
* transient EPERM while a freshly written tree is being scanned.
|
|
1007
1023
|
*
|
|
1008
|
-
* The persisted manifest
|
|
1009
|
-
* `dependencies
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1024
|
+
* The persisted manifest re-pins every declared DSH peer to the cohort in
|
|
1025
|
+
* `dependencies`, with matching `overrides`, so a released payload resolves
|
|
1026
|
+
* exactly one cohort, and it keeps the `peerDependencies` declaration so the
|
|
1027
|
+
* host compatibility gate still has something to check. Only `devDependencies`
|
|
1028
|
+
* is dropped — it is not a runtime input.
|
|
1012
1029
|
*/
|
|
1013
1030
|
export function stageCandidatePayload({
|
|
1014
1031
|
sourceRoot = runningPackageRoot(),
|
|
1032
|
+
source = null,
|
|
1015
1033
|
home = homedir(),
|
|
1016
1034
|
log = () => {},
|
|
1017
1035
|
now = new Date(),
|
|
@@ -1042,21 +1060,63 @@ export function stageCandidatePayload({
|
|
|
1042
1060
|
writeFileSync(destination, bytes, { mode: content.modes.get(entry) });
|
|
1043
1061
|
}
|
|
1044
1062
|
|
|
1045
|
-
// Persist an adjusted manifest: identity/runtime fields stay;
|
|
1046
|
-
//
|
|
1047
|
-
//
|
|
1048
|
-
//
|
|
1063
|
+
// Persist an adjusted manifest: identity/runtime fields stay; declared DSH
|
|
1064
|
+
// peers are re-pinned to the cohort in src/dsh-cohort.mjs so the standalone
|
|
1065
|
+
// payload resolves exactly one cohort; optional dependencies keep their own
|
|
1066
|
+
// section so npm reconciles them per-platform.
|
|
1067
|
+
//
|
|
1068
|
+
// The peer VALUES are deliberately not the payload's cohort. A peer key declares
|
|
1069
|
+
// which hosts will load the plugin, so it carries a range; a released payload
|
|
1070
|
+
// has to resolve one exact cohort, and exact specs are the only ones
|
|
1071
|
+
// `exactSpecOverrides` can pin. A scoped peer outside TARGET_DSH_VERSION — a
|
|
1072
|
+
// later-cohort rename, or the Cordis framework — is therefore a host-gate
|
|
1073
|
+
// declaration only: the running host supplies it, as it supplies every other
|
|
1074
|
+
// shared module. Without this pin the payload stops carrying a resolvable
|
|
1075
|
+
// cohort at all, and `payloadDshVersion` fails closed on the range.
|
|
1076
|
+
//
|
|
1077
|
+
// `peerDependencies` STAYS in the payload. The host gate opens with
|
|
1078
|
+
// `if (!Object.hasOwn(fields, "peerDependencies")) return void 0`, so a payload
|
|
1079
|
+
// that dropped the key stops being checked at all: the installed form would
|
|
1080
|
+
// trade the host's compatibility protection for nothing, and a future runtime
|
|
1081
|
+
// that genuinely mismatched would fail silently at run time instead of being
|
|
1082
|
+
// refused at boot. Keeping the declaration costs nothing — the exact cohort pin
|
|
1083
|
+
// lives in `dependencies` plus `overrides`, which answers a different question
|
|
1084
|
+
// than the range a host gate compares — and `peerDependenciesMeta.optional`
|
|
1085
|
+
// keeps npm from treating the pinned copies as unsatisfied required peers.
|
|
1086
|
+
// `devDependencies` is removed: it is not a runtime input and must never be
|
|
1087
|
+
// materialized into the payload.
|
|
1049
1088
|
const stagedManifest = { ...manifest };
|
|
1050
1089
|
delete stagedManifest.devDependencies;
|
|
1051
|
-
delete stagedManifest.peerDependencies;
|
|
1052
|
-
delete stagedManifest.peerDependenciesMeta;
|
|
1053
1090
|
const optionalNames = new Set(Object.keys(manifest.optionalDependencies ?? {}));
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1091
|
+
const cohortPins = dshCohortPins();
|
|
1092
|
+
const stagedDependencies = { ...(manifest.dependencies ?? {}) };
|
|
1093
|
+
for (const [name, spec] of Object.entries(manifest.peerDependencies ?? {})) {
|
|
1094
|
+
if (!name.startsWith(DSH_SCOPE)) {
|
|
1095
|
+
if (!(name in stagedDependencies)) stagedDependencies[name] = spec;
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
// A scoped peer outside the cohort is a host-gate declaration only.
|
|
1099
|
+
if (!Object.hasOwn(cohortPins, name)) continue;
|
|
1100
|
+
stagedDependencies[name] = cohortPins[name];
|
|
1101
|
+
}
|
|
1102
|
+
// The cohort list is authoritative wherever a member is declared, including a
|
|
1103
|
+
// member an author promoted into `dependencies`. Pinning there too keeps one
|
|
1104
|
+
// exact cohort; the alternative silently floats that package and re-opens
|
|
1105
|
+
// exactly the drift the payload pin exists to prevent.
|
|
1106
|
+
for (const [name, spec] of Object.entries(stagedDependencies)) {
|
|
1107
|
+
if (!Object.hasOwn(cohortPins, name)) continue;
|
|
1108
|
+
if (spec !== cohortPins[name]) {
|
|
1109
|
+
log(`- pinning ${name} ${spec} -> ${cohortPins[name]} (the payload resolves one cohort)`);
|
|
1110
|
+
}
|
|
1111
|
+
stagedDependencies[name] = cohortPins[name];
|
|
1112
|
+
}
|
|
1113
|
+
stagedManifest.dependencies = stagedDependencies;
|
|
1058
1114
|
const overrides = exactSpecOverrides(stagedManifest.dependencies);
|
|
1059
1115
|
if (overrides) stagedManifest.overrides = overrides;
|
|
1116
|
+
stagedManifest.dshCrew = {
|
|
1117
|
+
...(stagedManifest.dshCrew ?? {}),
|
|
1118
|
+
payloadRewrite: payloadRewriteRecord({ source, dshVersion: TARGET_DSH_VERSION, now }),
|
|
1119
|
+
};
|
|
1060
1120
|
writeFileSync(join(stageDir, 'package.json'), JSON.stringify(stagedManifest, null, 2) + '\n');
|
|
1061
1121
|
|
|
1062
1122
|
// Materialize dependencies: prefer replicating the exact bits the candidate
|
|
@@ -2862,7 +2922,7 @@ async function npxInstallInner({ home, log, sourceRoot, installer, ensureRuntime
|
|
|
2862
2922
|
return { ok: true, repaired: true, version: manifest.version, path: health.pointer.path };
|
|
2863
2923
|
}
|
|
2864
2924
|
|
|
2865
|
-
const staged = stageCandidatePayload({ sourceRoot: candidateRoot, home, log, npmInstaller });
|
|
2925
|
+
const staged = stageCandidatePayload({ sourceRoot: candidateRoot, home, log, npmInstaller, source: { kind: 'running', path: candidateRoot, version: manifest.version } });
|
|
2866
2926
|
if (!staged.ok) {
|
|
2867
2927
|
log(`✗ staging failed (${staged.code})${staged.detail ? `: ${Array.isArray(staged.detail) ? staged.detail.join('; ') : String(staged.detail)}` : ''}`);
|
|
2868
2928
|
return { ok: false, error: `staging failed (${staged.code})` };
|
|
@@ -2960,7 +3020,7 @@ export function resolveUpdateCandidate({
|
|
|
2960
3020
|
if (existsSync(join(value, 'package.json'))) {
|
|
2961
3021
|
const manifest = readManifest(value);
|
|
2962
3022
|
if (!manifest?.name || !manifest?.version) return { ok: false, code: 'CANDIDATE_MANIFEST_INVALID', detail: value };
|
|
2963
|
-
return { ok: true, sourceRoot: value, version: manifest.version, cleanup: null };
|
|
3023
|
+
return { ok: true, sourceRoot: value, version: manifest.version, cleanup: null, source: { kind: 'candidate', path: value } };
|
|
2964
3024
|
}
|
|
2965
3025
|
if (/\.tgz$/i.test(value)) {
|
|
2966
3026
|
const tmpRoot = join(crewReleasesDir({ home }), `.candidate-${timestampStamp()}-${process.pid}`);
|
|
@@ -2978,6 +3038,7 @@ export function resolveUpdateCandidate({
|
|
|
2978
3038
|
ok: true,
|
|
2979
3039
|
sourceRoot: extracted.sourceRoot,
|
|
2980
3040
|
version: manifest.version,
|
|
3041
|
+
source: { kind: 'candidate', file: basename(value) },
|
|
2981
3042
|
cleanup: () => rmSync(tmpRoot, { recursive: true, force: true }),
|
|
2982
3043
|
};
|
|
2983
3044
|
}
|
|
@@ -3022,6 +3083,7 @@ export function resolveUpdateCandidate({
|
|
|
3022
3083
|
ok: true,
|
|
3023
3084
|
sourceRoot: extracted.sourceRoot,
|
|
3024
3085
|
version: manifest.version,
|
|
3086
|
+
source: { kind: 'registry', spec: effectiveSpec, ...(typeof info.integrity === 'string' && info.integrity ? { integrity: info.integrity } : {}), ...(typeof info.filename === 'string' && info.filename ? { file: info.filename } : {}) },
|
|
3025
3087
|
cleanup: () => rmSync(tmpDir, { recursive: true, force: true }),
|
|
3026
3088
|
};
|
|
3027
3089
|
} catch (error) {
|
|
@@ -3078,7 +3140,7 @@ async function npxUpdateInner({ home, log, sourceRoot, candidate, spec, installe
|
|
|
3078
3140
|
let resolved;
|
|
3079
3141
|
if (launcherCanConverge) {
|
|
3080
3142
|
log(`- newer launcher ${launcherManifest.version}; converging managed payload ${initialHealth.pointer.version} before registry resolution`);
|
|
3081
|
-
resolved = { ok: true, sourceRoot: launcherRoot, version: launcherManifest.version, cleanup: null };
|
|
3143
|
+
resolved = { ok: true, sourceRoot: launcherRoot, version: launcherManifest.version, cleanup: null, source: { kind: 'launcher', path: launcherRoot, version: launcherManifest.version } };
|
|
3082
3144
|
} else {
|
|
3083
3145
|
resolved = resolveUpdateCandidate({ candidate: explicitCandidate, spec, home, log, runner });
|
|
3084
3146
|
}
|
|
@@ -3120,7 +3182,7 @@ async function npxUpdateInner({ home, log, sourceRoot, candidate, spec, installe
|
|
|
3120
3182
|
log(`- updating managed payload ${health.pointer.version} -> ${manifest.version}`);
|
|
3121
3183
|
}
|
|
3122
3184
|
|
|
3123
|
-
const staged = stageCandidatePayload({ sourceRoot: resolved.sourceRoot, home, log, npmInstaller });
|
|
3185
|
+
const staged = stageCandidatePayload({ sourceRoot: resolved.sourceRoot, home, log, npmInstaller, source: resolved.source });
|
|
3124
3186
|
if (!staged.ok) {
|
|
3125
3187
|
log(`✗ staging failed (${staged.code})${staged.detail ? `: ${Array.isArray(staged.detail) ? staged.detail.join('; ') : String(staged.detail)}` : ''}`);
|
|
3126
3188
|
return { ok: false, error: `staging failed (${staged.code})` };
|
|
@@ -13,6 +13,15 @@ function runtimeManifest(manifest) {
|
|
|
13
13
|
delete normalized.devDependencies;
|
|
14
14
|
delete normalized.peerDependencies;
|
|
15
15
|
delete normalized.peerDependenciesMeta;
|
|
16
|
+
// The install-form record is bookkeeping about the manifest, not a property of
|
|
17
|
+
// the shipped files: normalize it away so one payload keeps one digest before
|
|
18
|
+
// and after the CLI rewrite. Without this, `samePayloadContent` would report
|
|
19
|
+
// every rewritten payload as changed and the same-version repair path would
|
|
20
|
+
// never be reachable again.
|
|
21
|
+
if (normalized.dshCrew && typeof normalized.dshCrew === 'object') {
|
|
22
|
+
const { payloadRewrite, ...rest } = normalized.dshCrew;
|
|
23
|
+
normalized.dshCrew = rest;
|
|
24
|
+
}
|
|
16
25
|
return canonical(normalized);
|
|
17
26
|
}
|
|
18
27
|
|
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 = '2.2.
|
|
39
|
+
export const RUNTIME_VERSION = '2.2.16';
|
|
40
40
|
export const HUB_PROTOCOL_VERSION = 1;
|
|
41
41
|
|
|
42
42
|
export const HUB_CAPABILITIES = Object.freeze([
|