@dsh-workbench/adapter-probe 1.1.14-beta.1 → 1.1.14
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/bootstrap-run.d.ts +31 -0
- package/dist/bootstrap-run.js +59 -0
- package/dist/browser-wake.d.ts +2 -2
- package/dist/browser-wake.js +11 -15
- package/dist/desktop-runtime.d.ts +14 -0
- package/dist/desktop-runtime.js +59 -0
- package/dist/diy-preset-health.js +3 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/platform.d.ts +3 -1
- package/dist/platform.js +5 -2
- package/dist/preflight-route.d.ts +3 -2
- package/dist/preflight-route.js +20 -14
- package/dist/preflight.d.ts +3 -2
- package/dist/preflight.js +13 -11
- package/dist/probe-cache.js +3 -2
- package/dist/probe-libs.d.ts +2 -3
- package/dist/probe-libs.js +2 -2
- package/dist/probe-units.d.ts +3 -4
- package/dist/probe-units.js +21 -14
- package/dist/release-port.d.ts +3 -3
- package/dist/release-port.js +2 -2
- package/dist/runtime-seed.d.ts +4 -6
- package/dist/runtime-seed.js +31 -61
- package/dist/self-update.d.ts +3 -3
- package/dist/self-update.js +10 -13
- package/dist/supply-port.d.ts +5 -2
- package/dist/supply-port.js +3 -2
- package/dist/toolchain-probe.d.ts +4 -2
- package/dist/toolchain-probe.js +80 -14
- package/dist/toolchain.d.ts +7 -19
- package/dist/toolchain.js +19 -63
- package/package.json +5 -8
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Seam, ShellKind } from '@dsh-workbench/shared/host';
|
|
2
|
+
export interface BootstrapEnvOptions {
|
|
3
|
+
readonly opencliRoot?: string | undefined;
|
|
4
|
+
readonly realHome?: string | undefined;
|
|
5
|
+
readonly parent?: NodeJS.ProcessEnv | undefined;
|
|
6
|
+
readonly shellKind?: ShellKind | undefined;
|
|
7
|
+
}
|
|
8
|
+
export interface BootstrapSpawnOptions {
|
|
9
|
+
readonly env: Readonly<Record<string, string>>;
|
|
10
|
+
readonly timeout: number;
|
|
11
|
+
}
|
|
12
|
+
export interface BootstrapSpawnResult {
|
|
13
|
+
readonly exitCode: number | null;
|
|
14
|
+
readonly timedOut?: boolean | undefined;
|
|
15
|
+
readonly stdout: string;
|
|
16
|
+
readonly stderr: string;
|
|
17
|
+
}
|
|
18
|
+
export type BootstrapSpawn = (file: string, args: readonly string[], options: BootstrapSpawnOptions) => Promise<BootstrapSpawnResult>;
|
|
19
|
+
export interface BootstrapRunOptions {
|
|
20
|
+
readonly timeoutMs: number;
|
|
21
|
+
readonly env?: Readonly<Record<string, string>> | undefined;
|
|
22
|
+
readonly shellKind?: ShellKind | undefined;
|
|
23
|
+
readonly spawn?: BootstrapSpawn | undefined;
|
|
24
|
+
readonly seam?: Seam | undefined;
|
|
25
|
+
}
|
|
26
|
+
export interface BootstrapRunResult {
|
|
27
|
+
readonly ok: boolean;
|
|
28
|
+
readonly detail: string;
|
|
29
|
+
}
|
|
30
|
+
export declare function bootstrapEnv(options?: BootstrapEnvOptions): Record<string, string>;
|
|
31
|
+
export declare function runBootstrapCommand(line: string, options: BootstrapRunOptions): Promise<BootstrapRunResult>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { isolatedRoot } from '@dsh-workbench/adapter-runtime';
|
|
3
|
+
import { bootstrapEnvOf, tailLines } from '@dsh-workbench/shared/host';
|
|
4
|
+
import { desktopRuntimeEnvPath } from './desktop-runtime.js';
|
|
5
|
+
import { currentShellKind } from './platform.js';
|
|
6
|
+
export function bootstrapEnv(options = {}) {
|
|
7
|
+
const parent = options.parent ?? process.env;
|
|
8
|
+
return bootstrapEnvOf({
|
|
9
|
+
root: options.opencliRoot ?? isolatedRoot(),
|
|
10
|
+
realHome: options.realHome ?? homedir(),
|
|
11
|
+
shellKind: options.shellKind ?? currentShellKind(),
|
|
12
|
+
parent,
|
|
13
|
+
path: desktopRuntimeEnvPath(parent.PATH),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function commandArgsOf(line, shellKind) {
|
|
17
|
+
return shellKind === 'pwsh'
|
|
18
|
+
? { file: 'pwsh', args: ['-NoProfile', '-NonInteractive', '-Command', line] }
|
|
19
|
+
: { file: 'bash', args: ['-c', line] };
|
|
20
|
+
}
|
|
21
|
+
async function seamSpawn(seam, line, shellKind, env, timeoutMs) {
|
|
22
|
+
if (seam === undefined)
|
|
23
|
+
throw new Error('没有执行缝,自举命令起不来');
|
|
24
|
+
const result = await seam.run({
|
|
25
|
+
command: line,
|
|
26
|
+
shellKind,
|
|
27
|
+
intent: { kind: 'host' },
|
|
28
|
+
env,
|
|
29
|
+
timeoutMs,
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
exitCode: result.exitCode,
|
|
33
|
+
timedOut: result.timedOut,
|
|
34
|
+
stdout: result.stdout,
|
|
35
|
+
stderr: result.stderr,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export async function runBootstrapCommand(line, options) {
|
|
39
|
+
const shellKind = options.shellKind ?? currentShellKind();
|
|
40
|
+
const env = { ...bootstrapEnv({ shellKind }), ...(options.env ?? {}) };
|
|
41
|
+
const command = commandArgsOf(line, shellKind);
|
|
42
|
+
try {
|
|
43
|
+
const result = options.spawn === undefined
|
|
44
|
+
? await seamSpawn(options.seam, line, shellKind, env, options.timeoutMs)
|
|
45
|
+
: await options.spawn(command.file, command.args, { env, timeout: options.timeoutMs });
|
|
46
|
+
if (result.timedOut === true)
|
|
47
|
+
return { ok: false, detail: `超时(${String(options.timeoutMs)} ms):${line}` };
|
|
48
|
+
if (result.exitCode === 0)
|
|
49
|
+
return { ok: true, detail: tailLines(result.stdout, 1) };
|
|
50
|
+
const detail = tailLines(result.stderr || result.stdout, 4);
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
detail: detail === '' ? `没跑通(退出码 ${String(result.exitCode)}):${line}` : detail,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return { ok: false, detail: `${String(error)}:${line}` };
|
|
58
|
+
}
|
|
59
|
+
}
|
package/dist/browser-wake.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Seam } from '@dsh-workbench/shared/host';
|
|
2
2
|
import type { WakeBrowserReply } from '@dsh-workbench/shared/contract/protocol/preflight-wake';
|
|
3
3
|
export interface DesktopBrowser {
|
|
4
4
|
observe(): Promise<void>;
|
|
5
5
|
wake(): Promise<WakeBrowserReply>;
|
|
6
6
|
}
|
|
7
7
|
export declare function createDesktopBrowser(options: {
|
|
8
|
-
|
|
8
|
+
seam: Seam;
|
|
9
9
|
command: string;
|
|
10
10
|
stateFile: string;
|
|
11
11
|
platform?: string;
|
package/dist/browser-wake.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import {
|
|
3
|
+
import { join, win32 } from 'node:path';
|
|
4
4
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
5
|
-
import { quoteShellArg,
|
|
5
|
+
import { noWrite, now, quoteShellArg, writeJsonFile } from '@dsh-workbench/shared/host';
|
|
6
6
|
const BROWSER_IDS = ['chrome', 'edge', 'brave', 'chromium', 'vivaldi'];
|
|
7
7
|
const BROWSERS = [
|
|
8
8
|
{
|
|
@@ -62,13 +62,10 @@ export function createDesktopBrowser(options) {
|
|
|
62
62
|
const pollMs = options.pollMs ?? POLL_MS;
|
|
63
63
|
const command = options.command;
|
|
64
64
|
async function run(line, timeoutMs) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
return { ok: false, stdout: '' };
|
|
71
|
-
}
|
|
65
|
+
const result = await options.seam
|
|
66
|
+
.run({ command: line, intent: noWrite(), retry: 'safe', timeoutMs, bestEffort: true })
|
|
67
|
+
.catch(() => undefined);
|
|
68
|
+
return { ok: result !== undefined && result.exitCode === 0 && !result.timedOut, stdout: result?.stdout ?? '' };
|
|
72
69
|
}
|
|
73
70
|
function winCandidates(id) {
|
|
74
71
|
const programFiles = env.ProgramFiles ?? env.PROGRAMFILES ?? 'C:\\Program Files';
|
|
@@ -116,8 +113,7 @@ export function createDesktopBrowser(options) {
|
|
|
116
113
|
}
|
|
117
114
|
function save(id) {
|
|
118
115
|
try {
|
|
119
|
-
|
|
120
|
-
writeFileSync(options.stateFile, `${JSON.stringify({ browser: id }, null, 2)}\n`, 'utf8');
|
|
116
|
+
writeJsonFile(options.stateFile, { browser: id });
|
|
121
117
|
}
|
|
122
118
|
catch {
|
|
123
119
|
return;
|
|
@@ -172,13 +168,13 @@ export function createDesktopBrowser(options) {
|
|
|
172
168
|
return 'unknown';
|
|
173
169
|
}
|
|
174
170
|
async function waitConnected(ms) {
|
|
175
|
-
const deadline =
|
|
171
|
+
const deadline = now() + ms;
|
|
176
172
|
for (;;) {
|
|
177
173
|
if ((await daemonStatus()) === 'connected')
|
|
178
174
|
return true;
|
|
179
|
-
if (
|
|
175
|
+
if (now() >= deadline)
|
|
180
176
|
return false;
|
|
181
|
-
await delay(Math.min(pollMs, Math.max(0, deadline -
|
|
177
|
+
await delay(Math.min(pollMs, Math.max(0, deadline - now())));
|
|
182
178
|
}
|
|
183
179
|
}
|
|
184
180
|
function pickTarget(now) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface PnpmProcess {
|
|
2
|
+
readonly versions: {
|
|
3
|
+
readonly electron?: string | undefined;
|
|
4
|
+
readonly node?: string | undefined;
|
|
5
|
+
};
|
|
6
|
+
readonly resourcesPath?: string | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare function desktopResourcesDir(proc?: PnpmProcess): string | undefined;
|
|
9
|
+
export declare function desktopPnpmPath(proc?: PnpmProcess, exists?: (path: string) => boolean): string | undefined;
|
|
10
|
+
export declare function desktopBundledBins(proc?: PnpmProcess, exists?: (path: string) => boolean): readonly string[];
|
|
11
|
+
export declare function desktopNodeExecutable(proc?: PnpmProcess, exists?: (path: string) => boolean): string | undefined;
|
|
12
|
+
export declare function desktopRuntimeEnvPath(parentPath: string | undefined, proc?: PnpmProcess, exists?: (path: string) => boolean, separator?: string): string | undefined;
|
|
13
|
+
export declare function desktopRuntimeEnv(parent?: NodeJS.ProcessEnv, proc?: PnpmProcess, exists?: (path: string) => boolean): Record<string, string>;
|
|
14
|
+
export declare function desktopPnpmVersion(proc?: PnpmProcess, exists?: (path: string) => boolean): string | undefined;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { delimiter, join } from 'node:path';
|
|
3
|
+
import { inheritedEnvOf, pathWithBinsOf } from '@dsh-workbench/shared/host';
|
|
4
|
+
import { hostSurface } from './platform.js';
|
|
5
|
+
export function desktopResourcesDir(proc = process) {
|
|
6
|
+
if (hostSurface({ proc }).deployment !== 'desktop')
|
|
7
|
+
return undefined;
|
|
8
|
+
const declared = proc.resourcesPath;
|
|
9
|
+
return declared === undefined || declared === '' ? undefined : declared;
|
|
10
|
+
}
|
|
11
|
+
export function desktopPnpmPath(proc = process, exists = existsSync) {
|
|
12
|
+
const resources = desktopResourcesDir(proc);
|
|
13
|
+
if (resources === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
const path = join(resources, 'runtime', 'primary-runtime', 'dependencies', 'pnpm', 'bin', 'pnpm.cjs');
|
|
16
|
+
return exists(path) ? path : undefined;
|
|
17
|
+
}
|
|
18
|
+
export function desktopBundledBins(proc = process, exists = existsSync) {
|
|
19
|
+
const resources = desktopResourcesDir(proc);
|
|
20
|
+
if (resources === undefined)
|
|
21
|
+
return [];
|
|
22
|
+
const dependencies = join(resources, 'runtime', 'primary-runtime', 'dependencies');
|
|
23
|
+
return [join(dependencies, 'node', 'bin'), join(dependencies, 'pnpm', 'bin')].filter((dir) => exists(dir));
|
|
24
|
+
}
|
|
25
|
+
export function desktopNodeExecutable(proc = process, exists = existsSync) {
|
|
26
|
+
const resources = desktopResourcesDir(proc);
|
|
27
|
+
if (resources === undefined)
|
|
28
|
+
return undefined;
|
|
29
|
+
const bin = join(resources, 'runtime', 'primary-runtime', 'dependencies', 'node', 'bin');
|
|
30
|
+
return ['node.exe', 'node'].map((name) => join(bin, name)).find((path) => exists(path));
|
|
31
|
+
}
|
|
32
|
+
export function desktopRuntimeEnvPath(parentPath, proc = process, exists = existsSync, separator = delimiter) {
|
|
33
|
+
const bins = desktopBundledBins(proc, exists);
|
|
34
|
+
if (bins.length === 0)
|
|
35
|
+
return undefined;
|
|
36
|
+
const parts = (parentPath ?? '').split(separator).filter((part) => part !== '' && !bins.includes(part));
|
|
37
|
+
return [...bins, ...parts].join(separator);
|
|
38
|
+
}
|
|
39
|
+
export function desktopRuntimeEnv(parent = process.env, proc = process, exists = existsSync) {
|
|
40
|
+
const bins = desktopBundledBins(proc, exists);
|
|
41
|
+
if (bins.length === 0)
|
|
42
|
+
return {};
|
|
43
|
+
return { ...inheritedEnvOf(parent), PATH: pathWithBinsOf(parent.PATH, bins) };
|
|
44
|
+
}
|
|
45
|
+
export function desktopPnpmVersion(proc = process, exists = existsSync) {
|
|
46
|
+
const resources = desktopResourcesDir(proc);
|
|
47
|
+
if (resources === undefined)
|
|
48
|
+
return undefined;
|
|
49
|
+
const manifest = join(resources, 'runtime', 'primary-runtime', 'dependencies', 'pnpm', 'package.json');
|
|
50
|
+
if (!exists(manifest))
|
|
51
|
+
return undefined;
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(readFileSync(manifest, 'utf8'));
|
|
54
|
+
return typeof parsed.version === 'string' ? parsed.version : undefined;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { now } from '@dsh-workbench/shared/host';
|
|
1
2
|
import { DIY_PRESET_ID } from '@dsh-workbench/shared/contract/protocol/diy';
|
|
2
3
|
export const DIY_UPGRADE_COMMAND = 'npm i -g @deepseek-ai/dsh@latest';
|
|
3
4
|
export function brokenRowsOf(detail) {
|
|
@@ -29,7 +30,7 @@ export function createDiyPresetProbe(ctx, options = {}) {
|
|
|
29
30
|
const registry = ctx.get('agentPresets');
|
|
30
31
|
if (registry === undefined || typeof registry.list !== 'function')
|
|
31
32
|
return undefined;
|
|
32
|
-
const until =
|
|
33
|
+
const until = now() + deadlineMs;
|
|
33
34
|
for (;;) {
|
|
34
35
|
const rows = await registry.list().catch(() => undefined);
|
|
35
36
|
if (rows === undefined)
|
|
@@ -37,7 +38,7 @@ export function createDiyPresetProbe(ctx, options = {}) {
|
|
|
37
38
|
const verdict = diyPresetVerdictOf(rows);
|
|
38
39
|
if (verdict !== undefined)
|
|
39
40
|
return verdict;
|
|
40
|
-
if (
|
|
41
|
+
if (now() >= until)
|
|
41
42
|
return undefined;
|
|
42
43
|
await new Promise((resolve) => setTimeout(resolve, everyMs));
|
|
43
44
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './bootstrap-run.js';
|
|
1
2
|
export * from './browser-wake.js';
|
|
2
3
|
export * from './diy-preset-health.js';
|
|
3
4
|
export * from './platform.js';
|
|
@@ -8,6 +9,7 @@ export * from './probe-libs.js';
|
|
|
8
9
|
export * from './probe-units.js';
|
|
9
10
|
export * from './probe-verdict.js';
|
|
10
11
|
export * from './runtime-seed.js';
|
|
12
|
+
export * from './desktop-runtime.js';
|
|
11
13
|
export * from './toolchain.js';
|
|
12
14
|
export * from './toolchain-probe.js';
|
|
13
15
|
export * from './version-range.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './bootstrap-run.js';
|
|
1
2
|
export * from './browser-wake.js';
|
|
2
3
|
export * from './diy-preset-health.js';
|
|
3
4
|
export * from './platform.js';
|
|
@@ -8,6 +9,7 @@ export * from './probe-libs.js';
|
|
|
8
9
|
export * from './probe-units.js';
|
|
9
10
|
export * from './probe-verdict.js';
|
|
10
11
|
export * from './runtime-seed.js';
|
|
12
|
+
export * from './desktop-runtime.js';
|
|
11
13
|
export * from './toolchain.js';
|
|
12
14
|
export * from './toolchain-probe.js';
|
|
13
15
|
export * from './version-range.js';
|
package/dist/platform.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ShellKind } from '@dsh-workbench/shared/host';
|
|
1
2
|
import type { HostOs } from '@dsh-workbench/shared/contract/protocol/platform';
|
|
2
|
-
export { hostOsOf, hostSurface, profileNameOf, type HostSurfaceInput } from '@dsh-workbench/shared/host';
|
|
3
|
+
export { hostOsOf, hostSurface, profileNameOf, shellKindOf, type HostSurfaceInput } from '@dsh-workbench/shared/host';
|
|
3
4
|
export declare function currentHostOs(): HostOs;
|
|
5
|
+
export declare function currentShellKind(): ShellKind;
|
package/dist/platform.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { hostOsOf } from '@dsh-workbench/shared/host';
|
|
2
|
-
export { hostOsOf, hostSurface, profileNameOf } from '@dsh-workbench/shared/host';
|
|
1
|
+
import { hostOsOf, shellKindOf } from '@dsh-workbench/shared/host';
|
|
2
|
+
export { hostOsOf, hostSurface, profileNameOf, shellKindOf } from '@dsh-workbench/shared/host';
|
|
3
3
|
export function currentHostOs() {
|
|
4
4
|
return hostOsOf(process.platform);
|
|
5
5
|
}
|
|
6
|
+
export function currentShellKind() {
|
|
7
|
+
return shellKindOf(process.platform);
|
|
8
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { DshHostContext,
|
|
1
|
+
import type { DshHostContext, RuntimeCapabilities } from '@dsh-workbench/shared';
|
|
2
2
|
import type { RouteTable } from '@dsh-workbench/shared/host';
|
|
3
|
+
import { type Seam } from '@dsh-workbench/shared/host';
|
|
3
4
|
import { type DesktopBrowser } from './browser-wake.js';
|
|
4
5
|
export declare function registerPreflight(input: {
|
|
5
6
|
routes: RouteTable;
|
|
6
7
|
ctx: DshHostContext;
|
|
7
|
-
|
|
8
|
+
seam: Seam;
|
|
8
9
|
runtimeCommands: Readonly<Record<string, string>>;
|
|
9
10
|
runtimeCapabilities?: RuntimeCapabilities | undefined;
|
|
10
11
|
runtimeCheck?: (() => Promise<{
|
package/dist/preflight-route.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { basename, join } from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { PREFLIGHT_INSTALL_TIMEOUT_MS, preflightHandler, workbenchRoot } from '@dsh-workbench/shared/host';
|
|
3
3
|
import { WAKE_BROWSER_ROUTE } from '@dsh-workbench/shared/contract/protocol/preflight-wake';
|
|
4
|
+
import { runBootstrapCommand } from './bootstrap-run.js';
|
|
4
5
|
import { createDiyPresetProbe } from './diy-preset-health.js';
|
|
6
|
+
import { opencliInstallCommand } from './install.js';
|
|
5
7
|
import { hostSurface } from './platform.js';
|
|
6
8
|
import { createPreflight } from './preflight.js';
|
|
7
9
|
import { probeDesktopToolchain } from './toolchain-probe.js';
|
|
@@ -15,7 +17,7 @@ function desktopBrowserOf(input) {
|
|
|
15
17
|
if (!surface.capabilities.browserWake)
|
|
16
18
|
return undefined;
|
|
17
19
|
return createDesktopBrowser({
|
|
18
|
-
|
|
20
|
+
seam: input.seam,
|
|
19
21
|
command: input.command,
|
|
20
22
|
stateFile: join(workbenchRoot(), 'browser.json'),
|
|
21
23
|
...(input.ctx.logger === undefined ? {} : { logger: input.ctx.logger }),
|
|
@@ -23,24 +25,31 @@ function desktopBrowserOf(input) {
|
|
|
23
25
|
}
|
|
24
26
|
export function registerPreflight(input) {
|
|
25
27
|
const command = input.runtimeCommands['opencli'] ?? Object.values(input.runtimeCommands)[0] ?? 'opencli';
|
|
26
|
-
const
|
|
28
|
+
const configuredInstall = process.env.DSH_WORKBENCH_OPENCLI_INSTALL;
|
|
29
|
+
const installCommand = configuredInstall === undefined || configuredInstall === ''
|
|
30
|
+
? opencliInstallCommand()
|
|
31
|
+
: configuredInstall;
|
|
27
32
|
const desktopBrowser = input.desktopBrowser ??
|
|
28
33
|
desktopBrowserOf({
|
|
29
34
|
ctx: input.ctx,
|
|
30
|
-
|
|
35
|
+
seam: input.seam,
|
|
31
36
|
command,
|
|
32
37
|
});
|
|
33
38
|
const preflight = createPreflight({
|
|
34
|
-
|
|
39
|
+
seam: input.seam,
|
|
35
40
|
command,
|
|
36
|
-
|
|
41
|
+
installCommand,
|
|
42
|
+
install: () => runBootstrapCommand(installCommand, {
|
|
43
|
+
timeoutMs: PREFLIGHT_INSTALL_TIMEOUT_MS,
|
|
44
|
+
seam: input.seam,
|
|
45
|
+
}),
|
|
37
46
|
logger: input.ctx.logger,
|
|
38
47
|
warmReads: input.warmReads,
|
|
39
48
|
...(input.runtimeCapabilities === undefined ? {} : { capabilities: input.runtimeCapabilities }),
|
|
40
49
|
...(input.runtimeCheck === undefined ? {} : { runtimeCheck: input.runtimeCheck }),
|
|
41
50
|
...(input.runtimeLabel === undefined ? {} : { runtimeLabel: input.runtimeLabel }),
|
|
42
51
|
probeDiy: createDiyPresetProbe(input.ctx),
|
|
43
|
-
probeToolchain: () => probeDesktopToolchain(input.
|
|
52
|
+
probeToolchain: () => probeDesktopToolchain(input.seam),
|
|
44
53
|
provisionPnpm: async () => {
|
|
45
54
|
const root = runtimeSeedRootOf(input.ctx);
|
|
46
55
|
if (root === null)
|
|
@@ -49,13 +58,10 @@ export function registerPreflight(input) {
|
|
|
49
58
|
root,
|
|
50
59
|
pnpm: process.env.DSH_WB_PNPM?.trim() || 'pnpm',
|
|
51
60
|
npm: 'npm',
|
|
52
|
-
run:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
detail: tailLines(ran.stderr || ran.stdout, 3),
|
|
57
|
-
};
|
|
58
|
-
},
|
|
61
|
+
run: (command) => runBootstrapCommand(command, {
|
|
62
|
+
timeoutMs: PREFLIGHT_INSTALL_TIMEOUT_MS,
|
|
63
|
+
seam: input.seam,
|
|
64
|
+
}),
|
|
59
65
|
});
|
|
60
66
|
if (result.ok && result.binDir !== undefined) {
|
|
61
67
|
prependPath(result.binDir);
|
package/dist/preflight.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RuntimeCapabilities } from '@dsh-workbench/shared';
|
|
2
|
+
import { type Seam } from '@dsh-workbench/shared/host';
|
|
2
3
|
import type { DiyPresetVerdict } from './diy-preset-health.js';
|
|
3
4
|
import { type LocalNpmProvision, type LocalPnpmProvision } from './toolchain.js';
|
|
4
5
|
import type { ToolchainReport } from './toolchain-probe.js';
|
|
@@ -55,7 +56,7 @@ export declare function canWarm(bridge: {
|
|
|
55
56
|
issues: readonly PreflightBridgeIssue[];
|
|
56
57
|
}): boolean;
|
|
57
58
|
export declare function createPreflight(options: {
|
|
58
|
-
|
|
59
|
+
seam: Seam;
|
|
59
60
|
command?: string;
|
|
60
61
|
installCommand?: string;
|
|
61
62
|
logger?: {
|
package/dist/preflight.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PREFLIGHT_DOCTOR_TIMEOUT_MS, PREFLIGHT_FRESH_MS, PREFLIGHT_INSTALL_TIMEOUT_MS, PREFLIGHT_VERSION_TIMEOUT_MS, WARM_TIMEOUT_MS, now } from '@dsh-workbench/shared/host';
|
|
2
2
|
import { tailLines } from '@dsh-workbench/shared/host';
|
|
3
3
|
import { opencliCommandLine } from '@dsh-workbench/adapter-runtime';
|
|
4
|
-
import {
|
|
4
|
+
import { productWrite } from '@dsh-workbench/shared/host';
|
|
5
5
|
import { DIY_UPGRADE_COMMAND } from './diy-preset-health.js';
|
|
6
6
|
import { opencliInstallCommand } from './install.js';
|
|
7
7
|
import { LOCAL_PNPM_VERSION } from './toolchain.js';
|
|
@@ -29,7 +29,7 @@ function step(id, label) {
|
|
|
29
29
|
return { id, status: 'pending', ...(label === undefined || label === '' ? {} : { label }) };
|
|
30
30
|
}
|
|
31
31
|
export function createPreflight(options) {
|
|
32
|
-
const {
|
|
32
|
+
const { seam, command = 'opencli', logger } = options;
|
|
33
33
|
const capabilities = options.capabilities ?? { console: true, pluginMarket: true, browserBridge: true };
|
|
34
34
|
const withConsole = capabilities.console;
|
|
35
35
|
const withBridge = capabilities.browserBridge;
|
|
@@ -66,14 +66,14 @@ export function createPreflight(options) {
|
|
|
66
66
|
let current = {
|
|
67
67
|
phase: 'idle',
|
|
68
68
|
steps: steps(),
|
|
69
|
-
at:
|
|
69
|
+
at: now(),
|
|
70
70
|
log: [],
|
|
71
71
|
...(options.desktopBrowser === undefined ? {} : { canWakeBrowser: true }),
|
|
72
72
|
};
|
|
73
73
|
let running = null;
|
|
74
74
|
let shellStalled = false;
|
|
75
75
|
function set(next) {
|
|
76
|
-
current = { ...current, ...next, log: [...logLines], at:
|
|
76
|
+
current = { ...current, ...next, log: [...logLines], at: now() };
|
|
77
77
|
return current;
|
|
78
78
|
}
|
|
79
79
|
function patchStep(id, patch) {
|
|
@@ -82,13 +82,13 @@ export function createPreflight(options) {
|
|
|
82
82
|
function hostFailure(detail) {
|
|
83
83
|
return set({ phase: 'failed', error: detail, failure: 'host', guidance: 'host' });
|
|
84
84
|
}
|
|
85
|
-
async function exec(stepId, commandLine, timeoutMs) {
|
|
85
|
+
async function exec(stepId, commandLine, timeoutMs, retry = 'safe') {
|
|
86
86
|
if (shellStalled) {
|
|
87
87
|
return { kind: 'stalled', detail: `前一条命令就没交回结果,这一步没跑:${commandLine}` };
|
|
88
88
|
}
|
|
89
89
|
appendLog(stepId, `> ${commandLine}`);
|
|
90
90
|
try {
|
|
91
|
-
const result = await
|
|
91
|
+
const result = await seam.run({ command: commandLine, intent: productWrite(), timeoutMs, retry });
|
|
92
92
|
if (result.stalled) {
|
|
93
93
|
shellStalled = true;
|
|
94
94
|
appendLog(stepId, `没返回:${commandLine}`);
|
|
@@ -124,7 +124,7 @@ export function createPreflight(options) {
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
async function probeBridge() {
|
|
127
|
-
const result = await exec('bridge', `${command} doctor`,
|
|
127
|
+
const result = await exec('bridge', `${command} doctor`, PREFLIGHT_DOCTOR_TIMEOUT_MS);
|
|
128
128
|
if (result.kind === 'stalled') {
|
|
129
129
|
return { ok: false, detail: result.detail, issues: ['unknown'], stalled: true };
|
|
130
130
|
}
|
|
@@ -143,7 +143,7 @@ export function createPreflight(options) {
|
|
|
143
143
|
appendLog('cli', installed.detail);
|
|
144
144
|
return installed;
|
|
145
145
|
}
|
|
146
|
-
const result = await exec('cli', installCommand,
|
|
146
|
+
const result = await exec('cli', installCommand, PREFLIGHT_INSTALL_TIMEOUT_MS, 'never');
|
|
147
147
|
if (result.kind !== 'ok')
|
|
148
148
|
return { ok: false, detail: result.detail };
|
|
149
149
|
return { ok: true, detail: tailLines(result.stdout, 1) };
|
|
@@ -222,6 +222,8 @@ export function createPreflight(options) {
|
|
|
222
222
|
}));
|
|
223
223
|
if (toolchain !== undefined && toolchain.detail.trim() !== '')
|
|
224
224
|
appendLog('toolchain', toolchain.detail);
|
|
225
|
+
for (const line of toolchain?.diagnostics ?? [])
|
|
226
|
+
appendLog('toolchain', line);
|
|
225
227
|
if (toolchain !== undefined && toolchain.ok && toolchain.pnpmProblem && options.provisionPnpm !== undefined) {
|
|
226
228
|
set({ phase: 'installing' });
|
|
227
229
|
patchStep('toolchain', { status: 'running', action: `装本地 pnpm@${LOCAL_PNPM_VERSION}` });
|
|
@@ -249,7 +251,7 @@ export function createPreflight(options) {
|
|
|
249
251
|
action: undefined,
|
|
250
252
|
});
|
|
251
253
|
if (!toolchain.ok || toolchain.pnpmProblem) {
|
|
252
|
-
return toolchain.stalled === true
|
|
254
|
+
return toolchain.stalled === true || toolchain.hostBroken === true
|
|
253
255
|
? hostFailure(toolchain.detail)
|
|
254
256
|
: set({ phase: 'failed', error: toolchain.detail, failure: 'missing', guidance: 'install' });
|
|
255
257
|
}
|
|
@@ -358,7 +360,7 @@ export function createPreflight(options) {
|
|
|
358
360
|
});
|
|
359
361
|
}
|
|
360
362
|
function freshReady() {
|
|
361
|
-
return current.phase === 'ready' &&
|
|
363
|
+
return current.phase === 'ready' && now() - current.at < PREFLIGHT_FRESH_MS;
|
|
362
364
|
}
|
|
363
365
|
async function start(runOptions = {}) {
|
|
364
366
|
if (running !== null)
|
package/dist/probe-cache.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { now as clockNow } from '@dsh-workbench/shared/host';
|
|
1
2
|
export function createTimedMemo(ttlMs) {
|
|
2
3
|
const entries = new Map();
|
|
3
4
|
return {
|
|
4
|
-
get(key, now =
|
|
5
|
+
get(key, now = clockNow()) {
|
|
5
6
|
const hit = entries.get(key);
|
|
6
7
|
if (hit === undefined)
|
|
7
8
|
return undefined;
|
|
@@ -9,7 +10,7 @@ export function createTimedMemo(ttlMs) {
|
|
|
9
10
|
return undefined;
|
|
10
11
|
return hit.value;
|
|
11
12
|
},
|
|
12
|
-
set(key, value, now =
|
|
13
|
+
set(key, value, now = clockNow()) {
|
|
13
14
|
entries.set(key, { at: now, value });
|
|
14
15
|
},
|
|
15
16
|
clear() {
|
package/dist/probe-libs.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { LibNeed, NeedState, SupplyIndex } from '@dsh-workbench/shared/host';
|
|
1
|
+
import type { LibNeed, NeedState, Seam, SupplyIndex } from '@dsh-workbench/shared/host';
|
|
3
2
|
export declare function installedLibVersion(name: string): string | undefined;
|
|
4
3
|
export declare function probeLibs(options: {
|
|
5
4
|
readonly needs: readonly LibNeed[];
|
|
6
|
-
readonly
|
|
5
|
+
readonly seam: Seam;
|
|
7
6
|
readonly force: boolean;
|
|
8
7
|
readonly index?: SupplyIndex | undefined;
|
|
9
8
|
}): Promise<ReadonlyMap<string, NeedState>>;
|
package/dist/probe-libs.js
CHANGED
|
@@ -28,7 +28,7 @@ function stateOf(need, status, detail, extra = {}) {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
export async function probeLibs(options) {
|
|
31
|
-
const { needs,
|
|
31
|
+
const { needs, seam, force, index } = options;
|
|
32
32
|
const found = new Map();
|
|
33
33
|
for (const need of needs) {
|
|
34
34
|
if (found.has(need.name))
|
|
@@ -52,7 +52,7 @@ export async function probeLibs(options) {
|
|
|
52
52
|
const registry = source === undefined ? undefined : npmRegistryOf(source, process.env);
|
|
53
53
|
const latest = registry === undefined || !registry.ok
|
|
54
54
|
? undefined
|
|
55
|
-
: await latestVersion({
|
|
55
|
+
: await latestVersion({ seam, name: need.name, registry: registry.value, force });
|
|
56
56
|
const behind = latest !== undefined && isNewerVersion(latest, installed);
|
|
57
57
|
found.set(need.name, stateOf(need, behind ? 'outdated' : 'ok', behind ? `装的 ${installed},源上最新 ${latest ?? ''}` : `${installed} 版本对`, {
|
|
58
58
|
installed,
|
package/dist/probe-units.d.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { type NeedProbeInput, type UnitCapability } from '@dsh-workbench/shared/host';
|
|
1
|
+
import { type Seam, type NeedProbeInput, type UnitCapability } from '@dsh-workbench/shared/host';
|
|
3
2
|
export type { NeedProbeInput, NeedState, UnitCapability, } from '@dsh-workbench/shared/host';
|
|
4
3
|
export declare function parsePluginList(stdout: string): unknown[] | undefined;
|
|
5
4
|
export declare function probeUnits(options: {
|
|
6
|
-
|
|
5
|
+
seam: Seam;
|
|
7
6
|
runtimeCommands: Readonly<Record<string, string>>;
|
|
8
7
|
units: readonly NeedProbeInput[];
|
|
9
8
|
force?: boolean;
|
|
10
9
|
}): Promise<UnitCapability[]>;
|
|
11
10
|
export declare function probeUnitCommands(options: {
|
|
12
|
-
|
|
11
|
+
seam: Seam;
|
|
13
12
|
runtimeCommands?: Readonly<Record<string, string>> | undefined;
|
|
14
13
|
unit: NeedProbeInput;
|
|
15
14
|
force?: boolean;
|