@dsh-workbench/adapter-probe 1.1.12 → 1.1.14-beta.0
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/dist/bridge-report.d.ts +7 -0
- package/dist/bridge-report.js +32 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/install.js +3 -0
- package/dist/preflight-route.js +24 -47
- package/dist/preflight.d.ts +10 -18
- package/dist/preflight.js +102 -109
- package/dist/runtime-seed.js +11 -2
- package/dist/toolchain-probe.d.ts +10 -0
- package/dist/toolchain-probe.js +60 -0
- package/dist/toolchain.d.ts +3 -0
- package/dist/toolchain.js +48 -15
- package/package.json +5 -5
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type PreflightBridgeIssue = 'daemon' | 'extension' | 'profile' | 'stale' | 'unknown';
|
|
2
|
+
export declare const BRIDGE_ISSUE_ORDER: readonly PreflightBridgeIssue[];
|
|
3
|
+
export declare function readBridgeReport(stdout: string): {
|
|
4
|
+
readonly ok: boolean;
|
|
5
|
+
readonly issues: readonly PreflightBridgeIssue[];
|
|
6
|
+
readonly detail: string;
|
|
7
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { tailLines } from '@dsh-workbench/shared/host';
|
|
2
|
+
export const BRIDGE_ISSUE_ORDER = ['profile', 'stale', 'daemon', 'extension', 'unknown'];
|
|
3
|
+
export function readBridgeReport(stdout) {
|
|
4
|
+
const text = String(stdout ?? '');
|
|
5
|
+
const ok = /^\[OK\] Daemon:/m.test(text) && /^\[OK\] Extension:/m.test(text) && /^\[OK\] Connectivity:/m.test(text);
|
|
6
|
+
if (ok) {
|
|
7
|
+
const good = text.split('\n').find((line) => line.trim() === 'Everything looks good!');
|
|
8
|
+
return { ok: true, issues: [], detail: good?.trim() ?? tailLines(text, 1) };
|
|
9
|
+
}
|
|
10
|
+
const found = new Set();
|
|
11
|
+
if (!/^\[OK\] Daemon:/m.test(text))
|
|
12
|
+
found.add('daemon');
|
|
13
|
+
if (!/^\[OK\] Extension:/m.test(text))
|
|
14
|
+
found.add('extension');
|
|
15
|
+
if (/Multiple Chrome profiles are connected/i.test(text))
|
|
16
|
+
found.add('profile');
|
|
17
|
+
if (/Default browser profile is stale|Selected browser profile is not connected/i.test(text)) {
|
|
18
|
+
found.add('stale');
|
|
19
|
+
}
|
|
20
|
+
if (found.has('profile') || found.has('stale'))
|
|
21
|
+
found.delete('extension');
|
|
22
|
+
if (found.size === 0)
|
|
23
|
+
found.add('unknown');
|
|
24
|
+
return { ok: false, issues: BRIDGE_ISSUE_ORDER.filter((issue) => found.has(issue)), detail: issuesSection(text) };
|
|
25
|
+
}
|
|
26
|
+
function issuesSection(text) {
|
|
27
|
+
const lines = text.split('\n');
|
|
28
|
+
const start = lines.findIndex((line) => /^Issues:/.test(line.trim()));
|
|
29
|
+
if (start < 0)
|
|
30
|
+
return tailLines(text, 6);
|
|
31
|
+
return lines.slice(start).join('\n').trim();
|
|
32
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from './probe-units.js';
|
|
|
9
9
|
export * from './probe-verdict.js';
|
|
10
10
|
export * from './runtime-seed.js';
|
|
11
11
|
export * from './toolchain.js';
|
|
12
|
+
export * from './toolchain-probe.js';
|
|
12
13
|
export * from './version-range.js';
|
|
13
14
|
export * from './install.js';
|
|
14
15
|
export * from './self-update.js';
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export * from './probe-units.js';
|
|
|
9
9
|
export * from './probe-verdict.js';
|
|
10
10
|
export * from './runtime-seed.js';
|
|
11
11
|
export * from './toolchain.js';
|
|
12
|
+
export * from './toolchain-probe.js';
|
|
12
13
|
export * from './version-range.js';
|
|
13
14
|
export * from './install.js';
|
|
14
15
|
export * from './self-update.js';
|
package/dist/install.js
CHANGED
|
@@ -2,6 +2,9 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { isolatedInstallCommand, isolatedPnpmInstallCommand } from '@dsh-workbench/adapter-runtime';
|
|
3
3
|
import { desktopPnpmPath } from './toolchain.js';
|
|
4
4
|
export function opencliInstallCommand(proc = process, exists = existsSync) {
|
|
5
|
+
const override = process.env.DSH_WB_PNPM?.trim();
|
|
6
|
+
if (override !== undefined && override !== '')
|
|
7
|
+
return isolatedInstallCommand();
|
|
5
8
|
const pnpm = desktopPnpmPath(proc, exists);
|
|
6
9
|
return pnpm === undefined ? isolatedInstallCommand() : isolatedPnpmInstallCommand(pnpm);
|
|
7
10
|
}
|
package/dist/preflight-route.js
CHANGED
|
@@ -3,11 +3,12 @@ import { INSTALL_TIMEOUT_MS, preflightHandler, runShell, tailLines, workbenchRoo
|
|
|
3
3
|
import { WAKE_BROWSER_ROUTE } from '@dsh-workbench/shared/contract/protocol/preflight-wake';
|
|
4
4
|
import { createDiyPresetProbe } from './diy-preset-health.js';
|
|
5
5
|
import { hostSurface } from './platform.js';
|
|
6
|
-
import { createPreflight
|
|
6
|
+
import { createPreflight } from './preflight.js';
|
|
7
|
+
import { probeDesktopToolchain } from './toolchain-probe.js';
|
|
7
8
|
import { createDesktopBrowser } from './browser-wake.js';
|
|
8
9
|
import { profileDirOf } from '@dsh-workbench/adapter-supply';
|
|
9
10
|
import { prependPath, runtimeSeedRootOf } from './runtime-seed.js';
|
|
10
|
-
import {
|
|
11
|
+
import { localPnpmCommand, provisionLocalPnpm } from './toolchain.js';
|
|
11
12
|
function desktopBrowserOf(input) {
|
|
12
13
|
const dir = profileDirOf(input.ctx);
|
|
13
14
|
const surface = hostSurface({ profile: dir === null ? undefined : basename(dir) });
|
|
@@ -29,8 +30,6 @@ export function registerPreflight(input) {
|
|
|
29
30
|
shell: input.shell,
|
|
30
31
|
command,
|
|
31
32
|
});
|
|
32
|
-
const profileDir = profileDirOf(input.ctx);
|
|
33
|
-
const surface = hostSurface({ profile: profileDir === null ? undefined : basename(profileDir) });
|
|
34
33
|
const preflight = createPreflight({
|
|
35
34
|
shell: input.shell,
|
|
36
35
|
command,
|
|
@@ -41,51 +40,29 @@ export function registerPreflight(input) {
|
|
|
41
40
|
...(input.runtimeCheck === undefined ? {} : { runtimeCheck: input.runtimeCheck }),
|
|
42
41
|
...(input.runtimeLabel === undefined ? {} : { runtimeLabel: input.runtimeLabel }),
|
|
43
42
|
probeDiy: createDiyPresetProbe(input.ctx),
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
if (result.ok && result.binDir !== undefined) {
|
|
62
|
-
prependPath(result.binDir);
|
|
63
|
-
if (process.env.npm_config_cache === undefined) {
|
|
64
|
-
process.env.npm_config_cache = localNpmCache(root);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return result;
|
|
68
|
-
},
|
|
69
|
-
provisionPnpm: async () => {
|
|
70
|
-
const root = runtimeSeedRootOf(input.ctx);
|
|
71
|
-
if (root === null)
|
|
72
|
-
return { ok: false, detail: '找不到工作台根' };
|
|
73
|
-
const result = await provisionLocalPnpm({
|
|
74
|
-
root,
|
|
75
|
-
run: async (command) => {
|
|
76
|
-
const ran = await runShell(input.shell, command, { timeoutMs: INSTALL_TIMEOUT_MS });
|
|
77
|
-
return {
|
|
78
|
-
ok: ran.exitCode === 0 && !ran.timedOut,
|
|
79
|
-
detail: tailLines(ran.stderr || ran.stdout, 3),
|
|
80
|
-
};
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
if (result.ok && result.binDir !== undefined)
|
|
84
|
-
prependPath(result.binDir);
|
|
85
|
-
return result;
|
|
43
|
+
probeToolchain: () => probeDesktopToolchain(input.shell),
|
|
44
|
+
provisionPnpm: async () => {
|
|
45
|
+
const root = runtimeSeedRootOf(input.ctx);
|
|
46
|
+
if (root === null)
|
|
47
|
+
return { ok: false, detail: '找不到工作台根' };
|
|
48
|
+
const result = await provisionLocalPnpm({
|
|
49
|
+
root,
|
|
50
|
+
pnpm: process.env.DSH_WB_PNPM?.trim() || 'pnpm',
|
|
51
|
+
npm: 'npm',
|
|
52
|
+
run: async (command) => {
|
|
53
|
+
const ran = await runShell(input.shell, command, { timeoutMs: INSTALL_TIMEOUT_MS });
|
|
54
|
+
return {
|
|
55
|
+
ok: ran.exitCode === 0 && !ran.timedOut,
|
|
56
|
+
detail: tailLines(ran.stderr || ran.stdout, 3),
|
|
57
|
+
};
|
|
86
58
|
},
|
|
59
|
+
});
|
|
60
|
+
if (result.ok && result.binDir !== undefined) {
|
|
61
|
+
prependPath(result.binDir);
|
|
62
|
+
process.env.DSH_WB_PNPM = localPnpmCommand(root);
|
|
87
63
|
}
|
|
88
|
-
|
|
64
|
+
return result;
|
|
65
|
+
},
|
|
89
66
|
...(desktopBrowser === undefined ? {} : { desktopBrowser }),
|
|
90
67
|
});
|
|
91
68
|
input.routes.preflight = preflightHandler(preflight);
|
package/dist/preflight.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { DshShell, RuntimeCapabilities } from '@dsh-workbench/shared';
|
|
2
2
|
import type { DiyPresetVerdict } from './diy-preset-health.js';
|
|
3
3
|
import { type LocalNpmProvision, type LocalPnpmProvision } from './toolchain.js';
|
|
4
|
+
import type { ToolchainReport } from './toolchain-probe.js';
|
|
4
5
|
import type { DesktopBrowser } from './browser-wake.js';
|
|
6
|
+
export type PreflightFailure = 'permission' | 'network' | 'missing' | 'host' | 'unknown';
|
|
7
|
+
export declare function classifyFailure(text: string): PreflightFailure;
|
|
8
|
+
export type PreflightGuidance = 'install' | 'path' | 'shell' | 'host';
|
|
5
9
|
export type PreflightStepId = 'toolchain' | 'runtime' | 'cli' | 'bridge' | 'diy' | 'warm';
|
|
6
10
|
export type PreflightStepStatus = 'pending' | 'running' | 'ok' | 'warn' | 'failed';
|
|
7
11
|
export interface PreflightStep {
|
|
@@ -10,25 +14,11 @@ export interface PreflightStep {
|
|
|
10
14
|
readonly detail?: string;
|
|
11
15
|
readonly action?: string;
|
|
12
16
|
readonly command?: string;
|
|
17
|
+
readonly log?: readonly string[];
|
|
13
18
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
readonly nodeMissing: boolean;
|
|
18
|
-
readonly npmMissing: boolean;
|
|
19
|
-
readonly pnpmProblem: boolean;
|
|
20
|
-
}
|
|
21
|
-
export type PreflightFailure = 'permission' | 'network' | 'missing' | 'unknown';
|
|
22
|
-
export declare function classifyFailure(text: string): PreflightFailure;
|
|
23
|
-
export type PreflightGuidance = 'install' | 'path' | 'shell';
|
|
24
|
-
export declare function probeDesktopToolchain(shell: DshShell): Promise<ToolchainReport>;
|
|
25
|
-
export type PreflightBridgeIssue = 'daemon' | 'extension' | 'profile' | 'stale' | 'unknown';
|
|
26
|
-
export declare const BRIDGE_ISSUE_ORDER: readonly PreflightBridgeIssue[];
|
|
27
|
-
export declare function readBridgeReport(stdout: string): {
|
|
28
|
-
readonly ok: boolean;
|
|
29
|
-
readonly issues: readonly PreflightBridgeIssue[];
|
|
30
|
-
readonly detail: string;
|
|
31
|
-
};
|
|
19
|
+
import { type PreflightBridgeIssue } from './bridge-report.js';
|
|
20
|
+
export { BRIDGE_ISSUE_ORDER, readBridgeReport } from './bridge-report.js';
|
|
21
|
+
export type { PreflightBridgeIssue } from './bridge-report.js';
|
|
32
22
|
export interface PreflightDiyGap {
|
|
33
23
|
readonly rows: readonly string[];
|
|
34
24
|
readonly detail: string;
|
|
@@ -44,6 +34,7 @@ export interface PreflightSnapshot {
|
|
|
44
34
|
readonly bridgeIssues?: readonly PreflightBridgeIssue[];
|
|
45
35
|
readonly diyGap?: PreflightDiyGap;
|
|
46
36
|
readonly canWakeBrowser?: boolean;
|
|
37
|
+
readonly log?: readonly string[];
|
|
47
38
|
}
|
|
48
39
|
export interface Preflight {
|
|
49
40
|
run(options?: {
|
|
@@ -57,6 +48,7 @@ export interface BridgeVerdict {
|
|
|
57
48
|
readonly detail: string;
|
|
58
49
|
readonly issues: readonly PreflightBridgeIssue[];
|
|
59
50
|
readonly command?: string | undefined;
|
|
51
|
+
readonly stalled?: boolean | undefined;
|
|
60
52
|
}
|
|
61
53
|
export declare function canWarm(bridge: {
|
|
62
54
|
ok: boolean;
|
package/dist/preflight.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { INSTALL_TIMEOUT_MS, OPENCLI_TIMEOUT_MS, PREFLIGHT_FRESH_MS, WARM_TIMEOUT_MS } from '@dsh-workbench/shared/host';
|
|
1
|
+
import { INSTALL_TIMEOUT_MS, OPENCLI_TIMEOUT_MS, PREFLIGHT_FRESH_MS, PREFLIGHT_VERSION_TIMEOUT_MS, WARM_TIMEOUT_MS, } from '@dsh-workbench/shared/host';
|
|
2
2
|
import { tailLines } from '@dsh-workbench/shared/host';
|
|
3
3
|
import { opencliCommandLine } from '@dsh-workbench/adapter-runtime';
|
|
4
4
|
import { runShell } from '@dsh-workbench/shared/host';
|
|
5
5
|
import { DIY_UPGRADE_COMMAND } from './diy-preset-health.js';
|
|
6
6
|
import { opencliInstallCommand } from './install.js';
|
|
7
|
-
import {
|
|
7
|
+
import { LOCAL_PNPM_VERSION } from './toolchain.js';
|
|
8
8
|
import { bridgeFixCommand } from './browser-wake.js';
|
|
9
9
|
export function classifyFailure(text) {
|
|
10
10
|
const lower = text.toLowerCase();
|
|
@@ -16,73 +16,8 @@ export function classifyFailure(text) {
|
|
|
16
16
|
return 'missing';
|
|
17
17
|
return 'unknown';
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const result = await runShell(shell, line, { timeoutMs: 10_000 });
|
|
22
|
-
const text = (result.stdout || result.stderr).trim().split('\n')[0] ?? '';
|
|
23
|
-
return { ok: result.exitCode === 0 && !result.timedOut, text };
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return { ok: false, text: '' };
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export async function probeDesktopToolchain(shell) {
|
|
30
|
-
const [node, npm, pnpm] = await Promise.all([
|
|
31
|
-
toolVersion(shell, 'node --version'),
|
|
32
|
-
toolVersion(shell, 'npm --version'),
|
|
33
|
-
toolVersion(shell, 'pnpm --version'),
|
|
34
|
-
]);
|
|
35
|
-
const nodeMissing = !node.ok;
|
|
36
|
-
const npmMissing = !npm.ok;
|
|
37
|
-
const hasBundledPnpm = desktopPnpmPath() !== undefined;
|
|
38
|
-
const pnpmMajor = /^v?(\d+)/.exec(pnpm.text)?.[1];
|
|
39
|
-
const pnpmProblem = !pnpm.ok || pnpmMajor !== '10';
|
|
40
|
-
const parts = [
|
|
41
|
-
nodeMissing ? 'node 不在 PATH(Desktop 自带 Node 的 shim 没生效)' : `node ${node.text}(Desktop 自带)`,
|
|
42
|
-
npmMissing
|
|
43
|
-
? hasBundledPnpm
|
|
44
|
-
? 'npm 缺(opencli 安装走 pnpm;插件安装/模块库仍需 npm)'
|
|
45
|
-
: 'npm 缺(装不了 opencli:bundled pnpm 也没找到)'
|
|
46
|
-
: `npm ${npm.text}`,
|
|
47
|
-
pnpmProblem
|
|
48
|
-
? pnpm.ok
|
|
49
|
-
? `pnpm ${pnpm.text}(工坊按 10 方言)`
|
|
50
|
-
: 'pnpm 缺(只影响工坊 build/install)'
|
|
51
|
-
: `pnpm ${pnpm.text}`,
|
|
52
|
-
];
|
|
53
|
-
return { ok: !nodeMissing, detail: parts.join(' · '), nodeMissing, npmMissing, pnpmProblem };
|
|
54
|
-
}
|
|
55
|
-
export const BRIDGE_ISSUE_ORDER = ['profile', 'stale', 'daemon', 'extension', 'unknown'];
|
|
56
|
-
export function readBridgeReport(stdout) {
|
|
57
|
-
const text = String(stdout ?? '');
|
|
58
|
-
const ok = /^\[OK\] Daemon:/m.test(text) && /^\[OK\] Extension:/m.test(text) && /^\[OK\] Connectivity:/m.test(text);
|
|
59
|
-
if (ok) {
|
|
60
|
-
const good = text.split('\n').find((line) => line.trim() === 'Everything looks good!');
|
|
61
|
-
return { ok: true, issues: [], detail: good?.trim() ?? tailLines(text, 1) };
|
|
62
|
-
}
|
|
63
|
-
const found = new Set();
|
|
64
|
-
if (!/^\[OK\] Daemon:/m.test(text))
|
|
65
|
-
found.add('daemon');
|
|
66
|
-
if (!/^\[OK\] Extension:/m.test(text))
|
|
67
|
-
found.add('extension');
|
|
68
|
-
if (/Multiple Chrome profiles are connected/i.test(text))
|
|
69
|
-
found.add('profile');
|
|
70
|
-
if (/Default browser profile is stale|Selected browser profile is not connected/i.test(text)) {
|
|
71
|
-
found.add('stale');
|
|
72
|
-
}
|
|
73
|
-
if (found.has('profile') || found.has('stale'))
|
|
74
|
-
found.delete('extension');
|
|
75
|
-
if (found.size === 0)
|
|
76
|
-
found.add('unknown');
|
|
77
|
-
return { ok: false, issues: BRIDGE_ISSUE_ORDER.filter((issue) => found.has(issue)), detail: issuesSection(text) };
|
|
78
|
-
}
|
|
79
|
-
function issuesSection(text) {
|
|
80
|
-
const lines = text.split('\n');
|
|
81
|
-
const start = lines.findIndex((line) => /^Issues:/.test(line.trim()));
|
|
82
|
-
if (start < 0)
|
|
83
|
-
return tailLines(text, 6);
|
|
84
|
-
return lines.slice(start).join('\n').trim();
|
|
85
|
-
}
|
|
19
|
+
import { readBridgeReport } from './bridge-report.js';
|
|
20
|
+
export { BRIDGE_ISSUE_ORDER, readBridgeReport } from './bridge-report.js';
|
|
86
21
|
export function opencliInstallCommandDefault() {
|
|
87
22
|
return opencliInstallCommand();
|
|
88
23
|
}
|
|
@@ -108,43 +43,91 @@ export function createPreflight(options) {
|
|
|
108
43
|
...(options.probeDiy === undefined ? [] : [step('diy')]),
|
|
109
44
|
...(warmReads().length === 0 ? [] : [step('warm')]),
|
|
110
45
|
];
|
|
46
|
+
let logLines = [];
|
|
47
|
+
const stepLogs = new Map();
|
|
48
|
+
function appendLog(stepId, text) {
|
|
49
|
+
const stepLines = stepLogs.get(stepId) ?? [];
|
|
50
|
+
for (const line of String(text).split('\n')) {
|
|
51
|
+
if (line.trim() === '')
|
|
52
|
+
continue;
|
|
53
|
+
const clipped = line.length > 400 ? `${line.slice(0, 400)}…` : line;
|
|
54
|
+
stepLines.push(clipped);
|
|
55
|
+
logLines.push(clipped);
|
|
56
|
+
}
|
|
57
|
+
if (stepLines.length > 200)
|
|
58
|
+
stepLines.splice(0, stepLines.length - 200);
|
|
59
|
+
if (logLines.length > 200)
|
|
60
|
+
logLines = logLines.slice(-200);
|
|
61
|
+
stepLogs.set(stepId, stepLines);
|
|
62
|
+
set({
|
|
63
|
+
steps: current.steps.map((item) => item.id === stepId ? { ...item, log: [...stepLines] } : item),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
111
66
|
let current = {
|
|
112
67
|
phase: 'idle',
|
|
113
68
|
steps: steps(),
|
|
114
69
|
at: Date.now(),
|
|
70
|
+
log: [],
|
|
115
71
|
...(options.desktopBrowser === undefined ? {} : { canWakeBrowser: true }),
|
|
116
72
|
};
|
|
117
73
|
let running = null;
|
|
74
|
+
let shellStalled = false;
|
|
118
75
|
function set(next) {
|
|
119
|
-
current = { ...current, ...next, at: Date.now() };
|
|
76
|
+
current = { ...current, ...next, log: [...logLines], at: Date.now() };
|
|
120
77
|
return current;
|
|
121
78
|
}
|
|
122
79
|
function patchStep(id, patch) {
|
|
123
80
|
return set({ steps: current.steps.map((item) => (item.id === id ? { ...item, ...patch } : item)) });
|
|
124
81
|
}
|
|
125
|
-
|
|
82
|
+
function hostFailure(detail) {
|
|
83
|
+
return set({ phase: 'failed', error: detail, failure: 'host', guidance: 'host' });
|
|
84
|
+
}
|
|
85
|
+
async function exec(stepId, commandLine, timeoutMs) {
|
|
86
|
+
if (shellStalled) {
|
|
87
|
+
return { kind: 'stalled', detail: `前一条命令就没交回结果,这一步没跑:${commandLine}` };
|
|
88
|
+
}
|
|
89
|
+
appendLog(stepId, `> ${commandLine}`);
|
|
126
90
|
try {
|
|
127
91
|
const result = await runShell(shell, commandLine, { timeoutMs });
|
|
92
|
+
if (result.stalled) {
|
|
93
|
+
shellStalled = true;
|
|
94
|
+
appendLog(stepId, `没返回:${commandLine}`);
|
|
95
|
+
return { kind: 'stalled', detail: `没有返回(宿主没把结果交回来):${commandLine}` };
|
|
96
|
+
}
|
|
128
97
|
if (result.timedOut) {
|
|
98
|
+
appendLog(stepId, `超时(${String(timeoutMs)} ms):${commandLine}`);
|
|
129
99
|
return { kind: 'error', detail: `超时(${String(timeoutMs)} ms):${commandLine}` };
|
|
130
100
|
}
|
|
101
|
+
const output = result.stdout || result.stderr;
|
|
102
|
+
if (output.trim() !== '')
|
|
103
|
+
appendLog(stepId, tailLines(output, 12));
|
|
131
104
|
if (result.exitCode !== 0) {
|
|
132
105
|
return { kind: 'nonzero', detail: tailLines(result.stderr || result.stdout, 4) };
|
|
133
106
|
}
|
|
134
107
|
return { kind: 'ok', stdout: result.stdout };
|
|
135
108
|
}
|
|
136
109
|
catch (error) {
|
|
110
|
+
appendLog(stepId, String(error));
|
|
137
111
|
return { kind: 'error', detail: String(error) };
|
|
138
112
|
}
|
|
139
113
|
}
|
|
140
114
|
async function probeVersion() {
|
|
141
|
-
const result = await exec(`${command} --version`,
|
|
142
|
-
if (result.kind === 'ok')
|
|
143
|
-
return { ok: true, missing: false, detail: result.stdout.trim().split('\n')[0] ?? '' };
|
|
144
|
-
|
|
115
|
+
const result = await exec('cli', `${command} --version`, PREFLIGHT_VERSION_TIMEOUT_MS);
|
|
116
|
+
if (result.kind === 'ok') {
|
|
117
|
+
return { ok: true, missing: false, detail: result.stdout.trim().split('\n')[0] ?? '', stalled: false };
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
ok: false,
|
|
121
|
+
missing: result.kind === 'nonzero',
|
|
122
|
+
detail: result.detail,
|
|
123
|
+
stalled: result.kind === 'stalled',
|
|
124
|
+
};
|
|
145
125
|
}
|
|
146
126
|
async function probeBridge() {
|
|
147
|
-
const result = await exec(`${command} doctor`, OPENCLI_TIMEOUT_MS);
|
|
127
|
+
const result = await exec('bridge', `${command} doctor`, OPENCLI_TIMEOUT_MS);
|
|
128
|
+
if (result.kind === 'stalled') {
|
|
129
|
+
return { ok: false, detail: result.detail, issues: ['unknown'], stalled: true };
|
|
130
|
+
}
|
|
148
131
|
if (result.kind !== 'ok')
|
|
149
132
|
return { ok: false, detail: result.detail, issues: ['unknown'] };
|
|
150
133
|
const report = readBridgeReport(result.stdout);
|
|
@@ -153,9 +136,14 @@ export function createPreflight(options) {
|
|
|
153
136
|
return { ...report, command: bridgeFixCommand(command, report.issues) };
|
|
154
137
|
}
|
|
155
138
|
async function installOpencli() {
|
|
156
|
-
if (options.install !== undefined)
|
|
157
|
-
|
|
158
|
-
|
|
139
|
+
if (options.install !== undefined) {
|
|
140
|
+
appendLog('cli', '> 安装 opencli');
|
|
141
|
+
const installed = await options.install();
|
|
142
|
+
if (installed.detail.trim() !== '')
|
|
143
|
+
appendLog('cli', installed.detail);
|
|
144
|
+
return installed;
|
|
145
|
+
}
|
|
146
|
+
const result = await exec('cli', installCommand, INSTALL_TIMEOUT_MS);
|
|
159
147
|
if (result.kind !== 'ok')
|
|
160
148
|
return { ok: false, detail: result.detail };
|
|
161
149
|
return { ok: true, detail: tailLines(result.stdout, 1) };
|
|
@@ -170,9 +158,9 @@ export function createPreflight(options) {
|
|
|
170
158
|
}
|
|
171
159
|
patchStep('warm', {
|
|
172
160
|
status: 'running',
|
|
173
|
-
action:
|
|
161
|
+
action: '预热浏览器站点',
|
|
174
162
|
});
|
|
175
|
-
const results = await Promise.all(reads.map((read) => exec(opencliCommandLine(command, read), WARM_TIMEOUT_MS)));
|
|
163
|
+
const results = await Promise.all(reads.map((read) => exec('warm', opencliCommandLine(command, read), WARM_TIMEOUT_MS)));
|
|
176
164
|
const failed = reads.filter((_, index) => results[index]?.kind !== 'ok');
|
|
177
165
|
patchStep('warm', {
|
|
178
166
|
status: failed.length === 0 ? 'ok' : 'warn',
|
|
@@ -206,6 +194,9 @@ export function createPreflight(options) {
|
|
|
206
194
|
return { rows: verdict.rows, detail: verdict.detail, command: DIY_UPGRADE_COMMAND };
|
|
207
195
|
}
|
|
208
196
|
async function doRun() {
|
|
197
|
+
shellStalled = false;
|
|
198
|
+
logLines = [];
|
|
199
|
+
stepLogs.clear();
|
|
209
200
|
set({
|
|
210
201
|
phase: 'checking',
|
|
211
202
|
error: undefined,
|
|
@@ -227,30 +218,20 @@ export function createPreflight(options) {
|
|
|
227
218
|
nodeMissing: true,
|
|
228
219
|
npmMissing: true,
|
|
229
220
|
pnpmProblem: true,
|
|
221
|
+
stalled: false,
|
|
230
222
|
}));
|
|
231
|
-
if (toolchain !== undefined && toolchain.
|
|
232
|
-
|
|
233
|
-
patchStep('toolchain', { status: 'running', action: `装本地 npm@${LOCAL_NPM_VERSION}` });
|
|
234
|
-
const provisioned = await options.provisionNpm().catch((error) => ({
|
|
235
|
-
ok: false,
|
|
236
|
-
detail: String(error),
|
|
237
|
-
}));
|
|
238
|
-
if (provisioned.ok) {
|
|
239
|
-
const reprobed = await options.probeToolchain?.();
|
|
240
|
-
if (reprobed !== undefined)
|
|
241
|
-
toolchain = reprobed;
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
toolchain = { ...toolchain, detail: `${toolchain.detail};本地 npm 没装成:${provisioned.detail}` };
|
|
245
|
-
}
|
|
246
|
-
}
|
|
223
|
+
if (toolchain !== undefined && toolchain.detail.trim() !== '')
|
|
224
|
+
appendLog('toolchain', toolchain.detail);
|
|
247
225
|
if (toolchain !== undefined && toolchain.ok && toolchain.pnpmProblem && options.provisionPnpm !== undefined) {
|
|
248
226
|
set({ phase: 'installing' });
|
|
249
227
|
patchStep('toolchain', { status: 'running', action: `装本地 pnpm@${LOCAL_PNPM_VERSION}` });
|
|
228
|
+
appendLog('toolchain', '> 安装本地 pnpm');
|
|
250
229
|
const provisioned = await options.provisionPnpm().catch((error) => ({
|
|
251
230
|
ok: false,
|
|
252
231
|
detail: String(error),
|
|
253
232
|
}));
|
|
233
|
+
if (provisioned.detail.trim() !== '')
|
|
234
|
+
appendLog('toolchain', provisioned.detail);
|
|
254
235
|
if (provisioned.ok) {
|
|
255
236
|
const reprobed = await options.probeToolchain?.();
|
|
256
237
|
if (reprobed !== undefined)
|
|
@@ -263,35 +244,39 @@ export function createPreflight(options) {
|
|
|
263
244
|
if (toolchain !== undefined) {
|
|
264
245
|
set({ phase: 'checking' });
|
|
265
246
|
patchStep('toolchain', {
|
|
266
|
-
status: toolchain.ok
|
|
247
|
+
status: toolchain.ok && !toolchain.pnpmProblem ? 'ok' : 'failed',
|
|
267
248
|
detail: toolchain.detail,
|
|
268
249
|
action: undefined,
|
|
269
250
|
});
|
|
270
|
-
if (!toolchain.ok) {
|
|
271
|
-
return
|
|
272
|
-
|
|
273
|
-
error: toolchain.detail,
|
|
274
|
-
failure: 'missing',
|
|
275
|
-
guidance: 'install',
|
|
276
|
-
});
|
|
251
|
+
if (!toolchain.ok || toolchain.pnpmProblem) {
|
|
252
|
+
return toolchain.stalled === true
|
|
253
|
+
? hostFailure(toolchain.detail)
|
|
254
|
+
: set({ phase: 'failed', error: toolchain.detail, failure: 'missing', guidance: 'install' });
|
|
277
255
|
}
|
|
278
256
|
}
|
|
279
257
|
if (options.runtimeCheck !== undefined) {
|
|
258
|
+
appendLog('runtime', '> 检查运行环境');
|
|
280
259
|
patchStep('runtime', { status: 'running' });
|
|
281
260
|
const verdict = await options.runtimeCheck().catch((error) => ({
|
|
282
261
|
ok: false,
|
|
283
262
|
detail: error instanceof Error ? error.message : String(error),
|
|
284
263
|
}));
|
|
264
|
+
if (verdict.detail.trim() !== '')
|
|
265
|
+
appendLog('runtime', verdict.detail);
|
|
285
266
|
patchStep('runtime', { status: verdict.ok ? 'ok' : 'failed', detail: verdict.detail, action: undefined });
|
|
286
267
|
if (!verdict.ok)
|
|
287
268
|
return set({ phase: 'failed', error: verdict.detail, failure: 'missing', guidance: 'install' });
|
|
288
269
|
}
|
|
289
270
|
const diyStarted = options.probeDiy?.();
|
|
290
271
|
if (withConsole) {
|
|
291
|
-
patchStep('cli', { status: 'running', action:
|
|
272
|
+
patchStep('cli', { status: 'running', action: '检查 opencli 版本' });
|
|
292
273
|
if (diyStarted !== undefined)
|
|
293
274
|
patchStep('diy', { status: 'running' });
|
|
294
275
|
let version = await probeVersion();
|
|
276
|
+
if (version.stalled) {
|
|
277
|
+
patchStep('cli', { status: 'failed', detail: version.detail, action: undefined });
|
|
278
|
+
return hostFailure(version.detail);
|
|
279
|
+
}
|
|
295
280
|
if (!version.ok && !version.missing) {
|
|
296
281
|
patchStep('cli', { status: 'failed', detail: version.detail, action: undefined });
|
|
297
282
|
return set({
|
|
@@ -312,7 +297,7 @@ export function createPreflight(options) {
|
|
|
312
297
|
});
|
|
313
298
|
}
|
|
314
299
|
set({ phase: 'installing' });
|
|
315
|
-
patchStep('cli', { status: 'running', action:
|
|
300
|
+
patchStep('cli', { status: 'running', action: '安装 opencli', command: installCommand });
|
|
316
301
|
const installed = await installOpencli();
|
|
317
302
|
if (!installed.ok) {
|
|
318
303
|
patchStep('cli', {
|
|
@@ -327,8 +312,12 @@ export function createPreflight(options) {
|
|
|
327
312
|
guidance: 'install',
|
|
328
313
|
});
|
|
329
314
|
}
|
|
330
|
-
patchStep('cli', { status: 'running', action:
|
|
315
|
+
patchStep('cli', { status: 'running', action: '检查 opencli 版本' });
|
|
331
316
|
version = await probeVersion();
|
|
317
|
+
if (version.stalled) {
|
|
318
|
+
patchStep('cli', { status: 'failed', detail: version.detail, action: undefined });
|
|
319
|
+
return hostFailure(version.detail);
|
|
320
|
+
}
|
|
332
321
|
if (!version.ok) {
|
|
333
322
|
patchStep('cli', { status: 'failed', detail: version.detail, command: installCommand });
|
|
334
323
|
return set({
|
|
@@ -341,8 +330,12 @@ export function createPreflight(options) {
|
|
|
341
330
|
}
|
|
342
331
|
patchStep('cli', { status: 'ok', detail: version.detail, action: undefined });
|
|
343
332
|
}
|
|
344
|
-
patchStep('bridge', { status: 'running', action:
|
|
333
|
+
patchStep('bridge', { status: 'running', action: '检查浏览器通道' });
|
|
345
334
|
const bridge = withBridge ? await probeBridge() : { ok: true, detail: '', issues: [] };
|
|
335
|
+
if (bridge.stalled === true) {
|
|
336
|
+
patchStep('bridge', { status: 'failed', detail: bridge.detail, action: undefined });
|
|
337
|
+
return hostFailure(bridge.detail);
|
|
338
|
+
}
|
|
346
339
|
if (withBridge && options.desktopBrowser !== undefined && bridge.ok) {
|
|
347
340
|
await options.desktopBrowser.observe().catch(() => undefined);
|
|
348
341
|
}
|
package/dist/runtime-seed.js
CHANGED
|
@@ -4,8 +4,8 @@ import { createRequire } from 'node:module';
|
|
|
4
4
|
import writeFileAtomic from 'write-file-atomic';
|
|
5
5
|
import { chmodSync, closeSync, mkdirSync, openSync, readFileSync, rmSync, } from 'node:fs';
|
|
6
6
|
import { delimiter, dirname, join, sep } from 'node:path';
|
|
7
|
-
import { quoteShellArg } from '@dsh-workbench/shared/host';
|
|
8
|
-
import { ensureToolchain, localNpmCache, provisionLocalPnpmSync } from './toolchain.js';
|
|
7
|
+
import { quoteShellArg, quoteShellArgFor } from '@dsh-workbench/shared/host';
|
|
8
|
+
import { desktopPnpmPath, desktopPnpmVersion, ensureToolchain, localNpmCache, provisionLocalPnpmSync } from './toolchain.js';
|
|
9
9
|
export function opencliBinName(platform = process.platform) {
|
|
10
10
|
return platform === 'win32' ? 'opencli.cmd' : 'opencli';
|
|
11
11
|
}
|
|
@@ -125,6 +125,15 @@ export function prependPath(entryDir, env = process.env) {
|
|
|
125
125
|
return true;
|
|
126
126
|
}
|
|
127
127
|
export function seedWorkbenchToolchain(root, log) {
|
|
128
|
+
const bundledPnpm = desktopPnpmPath();
|
|
129
|
+
const bundledVersion = desktopPnpmVersion()?.split('.')[0];
|
|
130
|
+
if (bundledPnpm !== undefined && bundledVersion === '10') {
|
|
131
|
+
process.env.DSH_WB_PNPM = `node ${quoteShellArgFor(bundledPnpm, process.platform)}`;
|
|
132
|
+
log(`pnpm 用 Desktop 自带的:${bundledPnpm}`);
|
|
133
|
+
}
|
|
134
|
+
else if (bundledPnpm !== undefined) {
|
|
135
|
+
log(`Desktop 自带 pnpm 版本不是 10,优先用 PATH 上的 pnpm`);
|
|
136
|
+
}
|
|
128
137
|
const toolchain = ensureToolchain({ root, log });
|
|
129
138
|
for (const file of toolchain.files)
|
|
130
139
|
log(`工具链写下 ${file}`);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DshShell } from '@dsh-workbench/shared';
|
|
2
|
+
export interface ToolchainReport {
|
|
3
|
+
readonly ok: boolean;
|
|
4
|
+
readonly detail: string;
|
|
5
|
+
readonly nodeMissing: boolean;
|
|
6
|
+
readonly npmMissing: boolean;
|
|
7
|
+
readonly pnpmProblem: boolean;
|
|
8
|
+
readonly stalled?: boolean | undefined;
|
|
9
|
+
}
|
|
10
|
+
export declare function probeDesktopToolchain(shell: DshShell): Promise<ToolchainReport>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { runShell } from '@dsh-workbench/shared/host';
|
|
2
|
+
import { quoteShellArg } from '@dsh-workbench/shared/host';
|
|
3
|
+
import { desktopPnpmPath, desktopPnpmVersion } from './toolchain.js';
|
|
4
|
+
import { hostSurface } from './platform.js';
|
|
5
|
+
async function toolVersion(shell, line) {
|
|
6
|
+
try {
|
|
7
|
+
const result = await runShell(shell, line, { timeoutMs: 10_000 });
|
|
8
|
+
const text = (result.stdout || result.stderr).trim().split('\n')[0] ?? '';
|
|
9
|
+
return { ok: result.exitCode === 0 && !result.timedOut, text, stalled: result.stalled };
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return { ok: false, text: '', stalled: false };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function probeDesktopToolchain(shell) {
|
|
16
|
+
const bundledPnpm = desktopPnpmPath();
|
|
17
|
+
const bundledMajor = desktopPnpmVersion()?.split('.')[0];
|
|
18
|
+
const preferBundled = bundledPnpm !== undefined && bundledMajor === '10';
|
|
19
|
+
const pnpmLine = preferBundled
|
|
20
|
+
? `node ${quoteShellArg(bundledPnpm ?? '')} --version`
|
|
21
|
+
: 'pnpm --version';
|
|
22
|
+
const [node, npm, pnpm] = await Promise.all([
|
|
23
|
+
toolVersion(shell, 'node --version'),
|
|
24
|
+
toolVersion(shell, 'npm --version'),
|
|
25
|
+
toolVersion(shell, pnpmLine),
|
|
26
|
+
]);
|
|
27
|
+
if (node.stalled || npm.stalled || pnpm.stalled) {
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
detail: '版本探测没有返回:node / npm / pnpm 的命令都没把结果交回来(不是没装)',
|
|
31
|
+
nodeMissing: false,
|
|
32
|
+
npmMissing: false,
|
|
33
|
+
pnpmProblem: false,
|
|
34
|
+
stalled: true,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const nodeMissing = !node.ok;
|
|
38
|
+
const npmMissing = !npm.ok;
|
|
39
|
+
const pnpmMajor = /^v?(\d+)/.exec(pnpm.text)?.[1];
|
|
40
|
+
const pnpmProblem = !pnpm.ok || pnpmMajor !== '10';
|
|
41
|
+
const desktop = hostSurface({ proc: process }).deployment === 'desktop';
|
|
42
|
+
const parts = [
|
|
43
|
+
nodeMissing
|
|
44
|
+
? desktop
|
|
45
|
+
? 'node 不在 PATH(Desktop 自带 Node 的 shim 没生效)'
|
|
46
|
+
: 'node 不在 PATH'
|
|
47
|
+
: desktop
|
|
48
|
+
? `node ${node.text}(Desktop 自带)`
|
|
49
|
+
: `node ${node.text}`,
|
|
50
|
+
npmMissing ? 'npm 缺(已改用 pnpm)' : `npm ${npm.text}`,
|
|
51
|
+
pnpmProblem
|
|
52
|
+
? pnpm.ok
|
|
53
|
+
? `pnpm ${pnpm.text}(工坊按 10 方言)`
|
|
54
|
+
: 'pnpm 缺(装不了 opencli/插件/模块库)'
|
|
55
|
+
: preferBundled
|
|
56
|
+
? `pnpm ${pnpm.text}(Desktop 自带)`
|
|
57
|
+
: `pnpm ${pnpm.text}`,
|
|
58
|
+
];
|
|
59
|
+
return { ok: !nodeMissing, detail: parts.join(' · '), nodeMissing, npmMissing, pnpmProblem };
|
|
60
|
+
}
|
package/dist/toolchain.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface PnpmProcess {
|
|
|
14
14
|
readonly resourcesPath?: string | undefined;
|
|
15
15
|
}
|
|
16
16
|
export declare function desktopPnpmPath(proc?: PnpmProcess, exists?: (path: string) => boolean): string | undefined;
|
|
17
|
+
export declare function desktopPnpmVersion(proc?: PnpmProcess, exists?: (path: string) => boolean): string | undefined;
|
|
17
18
|
export interface ToolchainOptions {
|
|
18
19
|
readonly root: string;
|
|
19
20
|
readonly proc?: ToolchainProcess | undefined;
|
|
@@ -68,6 +69,8 @@ export declare function provisionLocalPnpm(options: {
|
|
|
68
69
|
readonly platform?: NodeJS.Platform | undefined;
|
|
69
70
|
readonly proc?: PnpmProcess | undefined;
|
|
70
71
|
readonly exists?: ((path: string) => boolean) | undefined;
|
|
72
|
+
readonly pnpm?: string | undefined;
|
|
73
|
+
readonly npm?: string | undefined;
|
|
71
74
|
}): Promise<LocalPnpmProvision>;
|
|
72
75
|
export declare function provisionLocalPnpmSync(options: {
|
|
73
76
|
readonly root: string;
|
package/dist/toolchain.js
CHANGED
|
@@ -14,6 +14,23 @@ export function desktopPnpmPath(proc = process, exists = existsSync) {
|
|
|
14
14
|
const path = join(resources, 'runtime', 'primary-runtime', 'dependencies', 'pnpm', 'bin', 'pnpm.cjs');
|
|
15
15
|
return exists(path) ? path : undefined;
|
|
16
16
|
}
|
|
17
|
+
export function desktopPnpmVersion(proc = process, exists = existsSync) {
|
|
18
|
+
if (hostSurface({ proc }).deployment !== 'desktop')
|
|
19
|
+
return undefined;
|
|
20
|
+
const resources = proc.resourcesPath;
|
|
21
|
+
if (resources === undefined || resources === '')
|
|
22
|
+
return undefined;
|
|
23
|
+
const manifest = join(resources, 'runtime', 'primary-runtime', 'dependencies', 'pnpm', 'package.json');
|
|
24
|
+
if (!exists(manifest))
|
|
25
|
+
return undefined;
|
|
26
|
+
try {
|
|
27
|
+
const parsed = JSON.parse(readFileSync(manifest, 'utf8'));
|
|
28
|
+
return typeof parsed.version === 'string' ? parsed.version : undefined;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
17
34
|
export const LOCAL_NPM_VERSION = [10, 9, 4].join('.');
|
|
18
35
|
export const LOCAL_PNPM_VERSION = [10, 32, 1].join('.');
|
|
19
36
|
export function localNpmRoot(root) {
|
|
@@ -75,27 +92,43 @@ export function localPnpmInstallCommand(root, proc = process, exists = existsSyn
|
|
|
75
92
|
`pnpm@${LOCAL_PNPM_VERSION} --store-dir ${quoteShellArgFor(join(root, 'pnpm-store'), platform)}`);
|
|
76
93
|
}
|
|
77
94
|
export async function provisionLocalPnpm(options) {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
95
|
+
const platform = options.platform ?? process.platform;
|
|
96
|
+
const bootstrap = options.pnpm?.trim();
|
|
97
|
+
const pnpmCommand = bootstrap === undefined || bootstrap === ''
|
|
98
|
+
? localPnpmInstallCommand(options.root, options.proc ?? process, options.exists ?? existsSync)
|
|
99
|
+
: `${bootstrap} add --dir ${quoteShellArgFor(localPnpmRoot(options.root), platform)} pnpm@${LOCAL_PNPM_VERSION} --store-dir ${quoteShellArgFor(join(options.root, 'pnpm-store'), platform)}`;
|
|
100
|
+
const npm = options.npm?.trim();
|
|
101
|
+
const npmCommand = npm === undefined || npm === ''
|
|
102
|
+
? undefined
|
|
103
|
+
: `${npm} install --prefix ${quoteShellArgFor(localPnpmRoot(options.root), platform)} pnpm@${LOCAL_PNPM_VERSION} --cache ${quoteShellArgFor(join(options.root, 'npm-cache'), platform)} --no-audit --no-fund`;
|
|
104
|
+
const commands = [pnpmCommand, npmCommand].filter((item) => item !== undefined);
|
|
105
|
+
if (commands.length === 0)
|
|
106
|
+
return { ok: false, detail: '找不到可用的 pnpm 或 npm,装不了本地 pnpm 10' };
|
|
81
107
|
try {
|
|
82
108
|
mkdirSync(localPnpmRoot(options.root), { recursive: true });
|
|
83
109
|
}
|
|
84
110
|
catch (error) {
|
|
85
111
|
return { ok: false, detail: `建不了 ${localPnpmRoot(options.root)}:${String(error)}` };
|
|
86
112
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
113
|
+
let detail = '';
|
|
114
|
+
for (const command of commands) {
|
|
115
|
+
const result = await options.run(command);
|
|
116
|
+
if (result.ok) {
|
|
117
|
+
const installed = options.installed?.() ?? existsSync(localPnpmCommand(options.root, platform));
|
|
118
|
+
if (installed) {
|
|
119
|
+
return {
|
|
120
|
+
ok: true,
|
|
121
|
+
binDir: localPnpmBin(options.root),
|
|
122
|
+
detail: `pnpm@${LOCAL_PNPM_VERSION} 装到 ${localPnpmRoot(options.root)}`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
detail = 'pnpm 跑完但本地 pnpm 10 不在';
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
detail = result.detail;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { ok: false, detail };
|
|
99
132
|
}
|
|
100
133
|
export function provisionLocalPnpmSync(options) {
|
|
101
134
|
const command = localPnpmInstallCommand(options.root, options.proc ?? process, options.exists ?? existsSync);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-workbench/adapter-probe",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14-beta.0",
|
|
4
4
|
"description": "Workbench adapter-probe adapter.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"execa": "^10.2.0",
|
|
17
17
|
"proper-lockfile": "^4.1.2",
|
|
18
18
|
"write-file-atomic": "^8.0.0",
|
|
19
|
-
"@dsh-workbench/
|
|
20
|
-
"@dsh-workbench/adapter-runtime": "1.1.
|
|
21
|
-
"@dsh-workbench/adapter-
|
|
22
|
-
"@dsh-workbench/
|
|
19
|
+
"@dsh-workbench/adapter-profile": "1.1.14-beta.0",
|
|
20
|
+
"@dsh-workbench/adapter-runtime": "1.1.14-beta.0",
|
|
21
|
+
"@dsh-workbench/adapter-supply": "1.1.14-beta.0",
|
|
22
|
+
"@dsh-workbench/shared": "1.1.14-beta.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^24.19.0",
|