@ran-sh/dsh-crew 0.3.4 → 0.3.6
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/README.md +8 -1
- package/README.zh.md +8 -1
- package/package.json +1 -1
- package/src/install/npx-lifecycle.mjs +80 -20
- package/src/runtime-identity.mjs +1 -1
package/README.md
CHANGED
|
@@ -59,7 +59,14 @@ The globally installed package is only the launcher/manager. The actual Crew run
|
|
|
59
59
|
|
|
60
60
|
> Known compatibility issue: transient `npx @ran-sh/dsh-crew …` execution is currently unreliable on some npm versions (npm/cli#9870: the npx cache bin is not put on the spawned command PATH). Until that upstream fix reaches your npm, use the global-launcher flow above instead.
|
|
61
61
|
|
|
62
|
-
An update of the managed payload does not require a clone or rebuild
|
|
62
|
+
An update of the managed payload does not require a clone or rebuild. When the launcher and payload versions match, `dsh-crew update` resolves the newest permitted package from your configured npm registry (or an explicit `--candidate <path>` override). When the running launcher is newer, its validated package payload is used first to converge the older managed payload. Every path stages and validates before switching. If the managed payload is newer than the launcher, it is never downgraded and the CLI prints the exact launcher-refresh command.
|
|
63
|
+
|
|
64
|
+
> Migration boundary for legacy `<= 0.3.3`: those immutable launchers cannot discover newer registry versions, so their old update behavior cannot be retroactively fixed. Refresh the launcher first, then converge the managed payload—no source checkout is required:
|
|
65
|
+
>
|
|
66
|
+
> ```bash
|
|
67
|
+
> npm install -g @ran-sh/dsh-crew@latest
|
|
68
|
+
> dsh-crew update
|
|
69
|
+
> ```
|
|
63
70
|
|
|
64
71
|
Developer / source setup (alternative path):
|
|
65
72
|
|
package/README.zh.md
CHANGED
|
@@ -59,7 +59,14 @@ dsh-crew uninstall # 加 --purge 才会同时删除配置/备份
|
|
|
59
59
|
|
|
60
60
|
> 已知兼容性问题:部分 npm 版本下,临时 `npx @ran-sh/dsh-crew …` 执行不可靠(npm/cli#9870:npx 缓存中的 bin 未加入子进程 PATH)。在该上游修复可用之前,请使用上面的全局启动器方式。
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
托管载荷的更新无需克隆或构建。启动器与载荷版本相同时,`dsh-crew update` 会从你配置的 npm registry 解析最新允许版本(或用 `--candidate <path>` 显式指定);当前启动器较新时,会优先用这个已校验的启动器包收敛旧载荷。所有路径都会先暂存、校验,再切换。托管载荷较新时绝不降级,CLI 会打印刷新启动器的准确命令。
|
|
63
|
+
|
|
64
|
+
> 旧版 `<= 0.3.3` 的迁移边界:这些不可变的旧启动器无法发现更新的 registry 版本,其旧 update 行为无法被追溯修复。受支持的迁移方式是先刷新启动器,再收敛托管载荷;无需源码检出:
|
|
65
|
+
>
|
|
66
|
+
> ```bash
|
|
67
|
+
> npm install -g @ran-sh/dsh-crew@latest
|
|
68
|
+
> dsh-crew update
|
|
69
|
+
> ```
|
|
63
70
|
|
|
64
71
|
开发者 / 源码安装(备选路径):
|
|
65
72
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Packaged global-launcher lifecycle: install / status / update / uninstall.
|
|
2
2
|
//
|
|
3
3
|
// Public UX:
|
|
4
|
-
//
|
|
4
|
+
// npm install -g @ran-sh/dsh-crew@latest
|
|
5
|
+
// dsh-crew install|status|update|uninstall
|
|
5
6
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
7
|
+
// A package-manager launcher may run from a replaceable global/cache path.
|
|
8
|
+
// This module therefore persists the already-built package payload into
|
|
8
9
|
// Crew-owned state BEFORE registering it with the Harness profile, so the
|
|
9
10
|
// registration never depends on the cache, tarball, or temp extraction dir:
|
|
10
11
|
//
|
|
@@ -124,7 +125,10 @@ function writeCurrentPointer({ home, name, version, path }) {
|
|
|
124
125
|
|
|
125
126
|
function listPackageEdges(manifest) {
|
|
126
127
|
// Returns [{name, optional}] covering dependencies and
|
|
127
|
-
//
|
|
128
|
+
// required peerDependencies. Packages in the DSH cohort use peers for
|
|
129
|
+
// shared protocol/runtime modules, but a persisted Crew payload has no host
|
|
130
|
+
// node_modules to supply them, so their transitive peers are runtime edges.
|
|
131
|
+
// Platform-specific optional bits and optional peers stay non-fatal.
|
|
128
132
|
const edges = [];
|
|
129
133
|
for (const [name] of Object.entries(manifest?.dependencies ?? {})) {
|
|
130
134
|
edges.push({ name, optional: false });
|
|
@@ -132,6 +136,10 @@ function listPackageEdges(manifest) {
|
|
|
132
136
|
for (const [name] of Object.entries(manifest?.optionalDependencies ?? {})) {
|
|
133
137
|
if (!edges.some((edge) => edge.name === name)) edges.push({ name, optional: true });
|
|
134
138
|
}
|
|
139
|
+
for (const [name] of Object.entries(manifest?.peerDependencies ?? {})) {
|
|
140
|
+
if (edges.some((edge) => edge.name === name)) continue;
|
|
141
|
+
edges.push({ name, optional: manifest?.peerDependenciesMeta?.[name]?.optional === true });
|
|
142
|
+
}
|
|
135
143
|
return edges.filter((edge) => typeof edge.name === 'string' && edge.name.trim());
|
|
136
144
|
}
|
|
137
145
|
|
|
@@ -328,12 +336,41 @@ export function stageCandidatePayload({
|
|
|
328
336
|
* start and answer --help from inside the staged tree alone.
|
|
329
337
|
*/
|
|
330
338
|
export function defaultPayloadSmoke(dir, { nodePath = process.execPath, runner = spawnSync } = {}) {
|
|
331
|
-
const
|
|
339
|
+
const cli = runner(nodePath, [join(dir, 'bin', 'dsh-crew.mjs'), '--help'], {
|
|
340
|
+
encoding: 'utf8', timeout: 120_000, windowsHide: true,
|
|
341
|
+
env: { ...process.env },
|
|
342
|
+
});
|
|
343
|
+
if (cli.status !== 0) {
|
|
344
|
+
return { ok: false, detail: `bin --help exited ${cli.status}: ${(cli.stderr || cli.stdout || '').trim().slice(-300)}` };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const initialize = {
|
|
348
|
+
jsonrpc: '2.0',
|
|
349
|
+
id: 1,
|
|
350
|
+
method: 'initialize',
|
|
351
|
+
params: {
|
|
352
|
+
protocolVersion: '2024-11-05',
|
|
353
|
+
capabilities: {},
|
|
354
|
+
clientInfo: { name: 'dsh-crew-payload-smoke', version: '1.0.0' },
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
const mcp = runner(nodePath, [join(dir, 'src', 'server.mjs')], {
|
|
332
358
|
encoding: 'utf8', timeout: 120_000, windowsHide: true,
|
|
359
|
+
input: JSON.stringify(initialize) + '\n',
|
|
333
360
|
env: { ...process.env },
|
|
334
361
|
});
|
|
335
|
-
if (
|
|
336
|
-
|
|
362
|
+
if (mcp.status !== 0) {
|
|
363
|
+
return { ok: false, detail: `MCP initialize exited ${mcp.status}: ${(mcp.stderr || mcp.stdout || '').trim().slice(-300)}` };
|
|
364
|
+
}
|
|
365
|
+
let response;
|
|
366
|
+
try {
|
|
367
|
+
response = String(mcp.stdout ?? '').split(/\r?\n/).filter(Boolean)
|
|
368
|
+
.map((line) => JSON.parse(line)).find((entry) => entry?.id === initialize.id);
|
|
369
|
+
} catch { /* handled by the fail-closed response check below */ }
|
|
370
|
+
if (response?.result?.serverInfo?.name !== 'dsh-crew') {
|
|
371
|
+
return { ok: false, detail: `MCP initialize returned no valid dsh-crew response: ${String(mcp.stdout ?? '').trim().slice(-300)}` };
|
|
372
|
+
}
|
|
373
|
+
return { ok: true };
|
|
337
374
|
}
|
|
338
375
|
|
|
339
376
|
// Keyword boundaries reject identifiers containing the keywords
|
|
@@ -587,16 +624,16 @@ export function extractPackageTarball(tgzPath, destDir, { runner = spawnSync } =
|
|
|
587
624
|
|
|
588
625
|
/**
|
|
589
626
|
* The globally installed launcher intentionally does not self-replace. When
|
|
590
|
-
*
|
|
591
|
-
*
|
|
627
|
+
* the payload is newer, keep it authoritative and give the exact launcher
|
|
628
|
+
* refresh command. A newer launcher is handled by npxUpdate before this point.
|
|
592
629
|
*/
|
|
593
630
|
function noteLauncherDivergence({ log, home = homedir() }) {
|
|
594
631
|
const launcherVersion = readManifest(runningPackageRoot())?.version ?? null;
|
|
595
632
|
let installedVersion = null;
|
|
596
633
|
try { installedVersion = readCurrentPointer({ home }).version ?? null; } catch { installedVersion = null; }
|
|
597
|
-
if (launcherVersion && installedVersion && launcherVersion
|
|
634
|
+
if (launcherVersion && installedVersion && compareVersions(installedVersion, launcherVersion) > 0) {
|
|
598
635
|
log('');
|
|
599
|
-
log(`- note: the global launcher
|
|
636
|
+
log(`- note: managed payload ${installedVersion} is newer than the global launcher ${launcherVersion}; the payload remains authoritative.`);
|
|
600
637
|
log(` Refresh the launcher when convenient: npm install -g ${UPDATE_PACKAGE_NAME}@${installedVersion}`);
|
|
601
638
|
}
|
|
602
639
|
}
|
|
@@ -705,10 +742,26 @@ export async function npxUpdate({
|
|
|
705
742
|
runner = spawnSync,
|
|
706
743
|
} = {}) {
|
|
707
744
|
log('DSH Crew updater');
|
|
708
|
-
// Candidate resolution: explicit path/dir override >
|
|
709
|
-
// configured npm registry (@latest).
|
|
710
|
-
//
|
|
711
|
-
|
|
745
|
+
// Candidate resolution: explicit path/dir override > a newer validated
|
|
746
|
+
// running launcher > configured npm registry (@latest). This makes the
|
|
747
|
+
// supported legacy bootstrap (`npm install -g ...@latest`, then `update`)
|
|
748
|
+
// independent of registry propagation after the launcher refresh.
|
|
749
|
+
const explicitCandidate = candidate ?? sourceRoot;
|
|
750
|
+
const initialHealth = currentInstallationHealth({ home });
|
|
751
|
+
const launcherRoot = runningPackageRoot();
|
|
752
|
+
const launcherManifest = readManifest(launcherRoot);
|
|
753
|
+
const launcherCanConverge = explicitCandidate === undefined
|
|
754
|
+
&& initialHealth.pointer?.version
|
|
755
|
+
&& launcherManifest?.name === UPDATE_PACKAGE_NAME
|
|
756
|
+
&& launcherManifest?.version
|
|
757
|
+
&& compareVersions(launcherManifest.version, initialHealth.pointer.version) > 0;
|
|
758
|
+
let resolved;
|
|
759
|
+
if (launcherCanConverge) {
|
|
760
|
+
log(`- newer launcher ${launcherManifest.version}; converging managed payload ${initialHealth.pointer.version} before registry resolution`);
|
|
761
|
+
resolved = { ok: true, sourceRoot: launcherRoot, version: launcherManifest.version, cleanup: null };
|
|
762
|
+
} else {
|
|
763
|
+
resolved = resolveUpdateCandidate({ candidate: explicitCandidate, spec, home, log, runner });
|
|
764
|
+
}
|
|
712
765
|
if (!resolved.ok) {
|
|
713
766
|
log(`✗ candidate resolution failed (${resolved.code})${resolved.detail ? `: ${resolved.detail}` : ''}`);
|
|
714
767
|
return { ok: false, error: `candidate resolution failed (${resolved.code})` };
|
|
@@ -729,8 +782,9 @@ export async function npxUpdate({
|
|
|
729
782
|
return { ok: true, idempotent: true, version: manifest.version, path: health.pointer.path };
|
|
730
783
|
}
|
|
731
784
|
|
|
732
|
-
if (health.installed && health.healthy && compareVersions(manifest.version, health.pointer.version) < 0
|
|
733
|
-
|
|
785
|
+
if (health.installed && health.healthy && compareVersions(manifest.version, health.pointer.version) < 0) {
|
|
786
|
+
const source = explicitCandidate === undefined && !launcherCanConverge ? 'registry latest' : 'candidate';
|
|
787
|
+
log(`- ${source} (${manifest.version}) is not newer than the installed payload (${health.pointer.version}); nothing to update`);
|
|
734
788
|
const activated = await activateRelease({ home, releaseDir: health.pointer.path, manifest: readManifest(health.pointer.path), log, installer });
|
|
735
789
|
if (!activated) return { ok: false, error: 'activation failed' };
|
|
736
790
|
return { ok: true, idempotent: true, version: health.pointer.version, path: health.pointer.path };
|
|
@@ -845,8 +899,14 @@ export function npxStatus({
|
|
|
845
899
|
log(`DSH Crew launcher/candidate: ${candidateVersion ?? 'unknown'}`);
|
|
846
900
|
log(`Installed DSH Crew payload: ${installedLine}`);
|
|
847
901
|
if (candidateVersion && installedVersion && candidateVersion !== installedVersion) {
|
|
848
|
-
|
|
849
|
-
|
|
902
|
+
const direction = compareVersions(candidateVersion, installedVersion);
|
|
903
|
+
if (direction > 0) {
|
|
904
|
+
log(`- launcher ${candidateVersion} is newer than the managed payload ${installedVersion}.`);
|
|
905
|
+
log(' Run: dsh-crew update');
|
|
906
|
+
} else {
|
|
907
|
+
log(`- managed payload ${installedVersion} is newer than the launcher ${candidateVersion}; the payload remains authoritative.`);
|
|
908
|
+
log(` Refresh the launcher with: npm install -g ${UPDATE_PACKAGE_NAME}@${installedVersion}`);
|
|
909
|
+
}
|
|
850
910
|
}
|
|
851
911
|
log(`DSH plugin: ${dshPlugin} (dedicated dsh-crew profile; official web profile ignored)`);
|
|
852
912
|
log(`Codex Desktop integration: ${codex}`);
|
package/src/runtime-identity.mjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// Keep this module pure and dependency-free so Hub, MCP and tests all use the
|
|
9
9
|
// exact same compatibility rules.
|
|
10
10
|
|
|
11
|
-
export const RUNTIME_VERSION = '0.3.
|
|
11
|
+
export const RUNTIME_VERSION = '0.3.6';
|
|
12
12
|
export const HUB_PROTOCOL_VERSION = 1;
|
|
13
13
|
|
|
14
14
|
export const HUB_CAPABILITIES = Object.freeze([
|