@dsh-workbench/adapter-probe 1.1.14-beta.2 → 1.1.15
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 +2 -3
- package/dist/bootstrap-run.js +25 -47
- 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 +1 -0
- package/dist/index.js +1 -0
- package/dist/preflight-route.d.ts +3 -2
- package/dist/preflight-route.js +12 -6
- package/dist/preflight.d.ts +3 -2
- package/dist/preflight.js +12 -10
- 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 -69
- package/package.json +5 -8
package/dist/bootstrap-run.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ShellKind } from '@dsh-workbench/shared/host';
|
|
1
|
+
import type { Seam, ShellKind } from '@dsh-workbench/shared/host';
|
|
2
2
|
export interface BootstrapEnvOptions {
|
|
3
3
|
readonly opencliRoot?: string | undefined;
|
|
4
4
|
readonly realHome?: string | undefined;
|
|
@@ -8,8 +8,6 @@ export interface BootstrapEnvOptions {
|
|
|
8
8
|
export interface BootstrapSpawnOptions {
|
|
9
9
|
readonly env: Readonly<Record<string, string>>;
|
|
10
10
|
readonly timeout: number;
|
|
11
|
-
readonly reject: false;
|
|
12
|
-
readonly stripFinalNewline: false;
|
|
13
11
|
}
|
|
14
12
|
export interface BootstrapSpawnResult {
|
|
15
13
|
readonly exitCode: number | null;
|
|
@@ -23,6 +21,7 @@ export interface BootstrapRunOptions {
|
|
|
23
21
|
readonly env?: Readonly<Record<string, string>> | undefined;
|
|
24
22
|
readonly shellKind?: ShellKind | undefined;
|
|
25
23
|
readonly spawn?: BootstrapSpawn | undefined;
|
|
24
|
+
readonly seam?: Seam | undefined;
|
|
26
25
|
}
|
|
27
26
|
export interface BootstrapRunResult {
|
|
28
27
|
readonly ok: boolean;
|
package/dist/bootstrap-run.js
CHANGED
|
@@ -1,65 +1,43 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
1
|
import { homedir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { execa } from 'execa';
|
|
5
2
|
import { isolatedRoot } from '@dsh-workbench/adapter-runtime';
|
|
6
|
-
import { tailLines } from '@dsh-workbench/shared/host';
|
|
3
|
+
import { bootstrapEnvOf, invocationOf, tailLines } from '@dsh-workbench/shared/host';
|
|
4
|
+
import { desktopRuntimeEnvPath } from './desktop-runtime.js';
|
|
7
5
|
import { currentShellKind } from './platform.js';
|
|
8
6
|
export function bootstrapEnv(options = {}) {
|
|
9
|
-
const shellKind = options.shellKind ?? currentShellKind();
|
|
10
|
-
const opencliRoot = options.opencliRoot ?? isolatedRoot();
|
|
11
|
-
const realHome = options.realHome ?? homedir();
|
|
12
7
|
const parent = options.parent ?? process.env;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (shellKind === 'pwsh')
|
|
21
|
-
env['USERPROFILE'] = home;
|
|
22
|
-
const userConfig = join(realHome, '.npmrc');
|
|
23
|
-
if (existsSync(userConfig))
|
|
24
|
-
env['NPM_CONFIG_USERCONFIG'] = userConfig;
|
|
25
|
-
const cache = join(opencliRoot, 'npm-cache');
|
|
26
|
-
env['npm_config_cache'] = cache;
|
|
27
|
-
env['NPM_CONFIG_CACHE'] = cache;
|
|
28
|
-
env['NO_COLOR'] = '1';
|
|
29
|
-
env['TERM'] = 'dumb';
|
|
30
|
-
return env;
|
|
31
|
-
}
|
|
32
|
-
function shellOf(line, shellKind) {
|
|
33
|
-
return shellKind === 'pwsh'
|
|
34
|
-
? { file: 'pwsh', args: ['-NoProfile', '-NonInteractive', '-Command', line] }
|
|
35
|
-
: { file: 'bash', args: ['-c', line] };
|
|
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
|
+
});
|
|
36
15
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
16
|
+
async function seamSpawn(seam, line, shellKind, env, timeoutMs) {
|
|
17
|
+
if (seam === undefined)
|
|
18
|
+
throw new Error('没有执行缝,自举命令起不来');
|
|
19
|
+
const result = await seam.run({
|
|
20
|
+
command: line,
|
|
21
|
+
shellKind,
|
|
22
|
+
intent: { kind: 'host' },
|
|
23
|
+
env,
|
|
24
|
+
timeoutMs,
|
|
43
25
|
});
|
|
44
26
|
return {
|
|
45
|
-
exitCode: result.exitCode
|
|
46
|
-
timedOut: result.timedOut
|
|
27
|
+
exitCode: result.exitCode,
|
|
28
|
+
timedOut: result.timedOut,
|
|
47
29
|
stdout: result.stdout,
|
|
48
30
|
stderr: result.stderr,
|
|
49
31
|
};
|
|
50
|
-
}
|
|
32
|
+
}
|
|
51
33
|
export async function runBootstrapCommand(line, options) {
|
|
52
34
|
const shellKind = options.shellKind ?? currentShellKind();
|
|
53
|
-
const spawn = options.spawn ?? defaultSpawn;
|
|
54
35
|
const env = { ...bootstrapEnv({ shellKind }), ...(options.env ?? {}) };
|
|
55
|
-
const command =
|
|
36
|
+
const command = invocationOf(shellKind, line);
|
|
56
37
|
try {
|
|
57
|
-
const result =
|
|
58
|
-
env,
|
|
59
|
-
timeout: options.timeoutMs
|
|
60
|
-
reject: false,
|
|
61
|
-
stripFinalNewline: false,
|
|
62
|
-
});
|
|
38
|
+
const result = options.spawn === undefined
|
|
39
|
+
? await seamSpawn(options.seam, line, shellKind, env, options.timeoutMs)
|
|
40
|
+
: await options.spawn(command.file, command.args, { env, timeout: options.timeoutMs });
|
|
63
41
|
if (result.timedOut === true)
|
|
64
42
|
return { ok: false, detail: `超时(${String(options.timeoutMs)} ms):${line}` };
|
|
65
43
|
if (result.exitCode === 0)
|
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
|
@@ -9,6 +9,7 @@ export * from './probe-libs.js';
|
|
|
9
9
|
export * from './probe-units.js';
|
|
10
10
|
export * from './probe-verdict.js';
|
|
11
11
|
export * from './runtime-seed.js';
|
|
12
|
+
export * from './desktop-runtime.js';
|
|
12
13
|
export * from './toolchain.js';
|
|
13
14
|
export * from './toolchain-probe.js';
|
|
14
15
|
export * from './version-range.js';
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export * from './probe-libs.js';
|
|
|
9
9
|
export * from './probe-units.js';
|
|
10
10
|
export * from './probe-verdict.js';
|
|
11
11
|
export * from './runtime-seed.js';
|
|
12
|
+
export * from './desktop-runtime.js';
|
|
12
13
|
export * from './toolchain.js';
|
|
13
14
|
export * from './toolchain-probe.js';
|
|
14
15
|
export * from './version-range.js';
|
|
@@ -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
|
@@ -17,7 +17,7 @@ function desktopBrowserOf(input) {
|
|
|
17
17
|
if (!surface.capabilities.browserWake)
|
|
18
18
|
return undefined;
|
|
19
19
|
return createDesktopBrowser({
|
|
20
|
-
|
|
20
|
+
seam: input.seam,
|
|
21
21
|
command: input.command,
|
|
22
22
|
stateFile: join(workbenchRoot(), 'browser.json'),
|
|
23
23
|
...(input.ctx.logger === undefined ? {} : { logger: input.ctx.logger }),
|
|
@@ -32,21 +32,24 @@ export function registerPreflight(input) {
|
|
|
32
32
|
const desktopBrowser = input.desktopBrowser ??
|
|
33
33
|
desktopBrowserOf({
|
|
34
34
|
ctx: input.ctx,
|
|
35
|
-
|
|
35
|
+
seam: input.seam,
|
|
36
36
|
command,
|
|
37
37
|
});
|
|
38
38
|
const preflight = createPreflight({
|
|
39
|
-
|
|
39
|
+
seam: input.seam,
|
|
40
40
|
command,
|
|
41
41
|
installCommand,
|
|
42
|
-
install: () => runBootstrapCommand(installCommand, {
|
|
42
|
+
install: () => runBootstrapCommand(installCommand, {
|
|
43
|
+
timeoutMs: PREFLIGHT_INSTALL_TIMEOUT_MS,
|
|
44
|
+
seam: input.seam,
|
|
45
|
+
}),
|
|
43
46
|
logger: input.ctx.logger,
|
|
44
47
|
warmReads: input.warmReads,
|
|
45
48
|
...(input.runtimeCapabilities === undefined ? {} : { capabilities: input.runtimeCapabilities }),
|
|
46
49
|
...(input.runtimeCheck === undefined ? {} : { runtimeCheck: input.runtimeCheck }),
|
|
47
50
|
...(input.runtimeLabel === undefined ? {} : { runtimeLabel: input.runtimeLabel }),
|
|
48
51
|
probeDiy: createDiyPresetProbe(input.ctx),
|
|
49
|
-
probeToolchain: () => probeDesktopToolchain(input.
|
|
52
|
+
probeToolchain: () => probeDesktopToolchain(input.seam),
|
|
50
53
|
provisionPnpm: async () => {
|
|
51
54
|
const root = runtimeSeedRootOf(input.ctx);
|
|
52
55
|
if (root === null)
|
|
@@ -55,7 +58,10 @@ export function registerPreflight(input) {
|
|
|
55
58
|
root,
|
|
56
59
|
pnpm: process.env.DSH_WB_PNPM?.trim() || 'pnpm',
|
|
57
60
|
npm: 'npm',
|
|
58
|
-
run: (command) => runBootstrapCommand(command, {
|
|
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 { PREFLIGHT_DOCTOR_TIMEOUT_MS, PREFLIGHT_FRESH_MS, PREFLIGHT_INSTALL_TIMEOUT_MS, PREFLIGHT_VERSION_TIMEOUT_MS, WARM_TIMEOUT_MS, } from '@dsh-workbench/shared/host';
|
|
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}`);
|
|
@@ -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, PREFLIGHT_INSTALL_TIMEOUT_MS);
|
|
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;
|
package/dist/probe-units.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { commandNamesMatch } from './command-name.js';
|
|
2
|
-
import { OPENCLI_TIMEOUT_MS, PROBE_TTL_MS, buildSupplyIndex, NEED_KIND_SPECS, needIdOf, npmSourceOf, parseJsonLoose,
|
|
2
|
+
import { OPENCLI_TIMEOUT_MS, PROBE_TTL_MS, buildSupplyIndex, NEED_KIND_SPECS, needIdOf, noWrite, npmSourceOf, parseJsonLoose, productWrite, quoteShellArg, } from '@dsh-workbench/shared/host';
|
|
3
3
|
import { npmRegistryOf } from '@dsh-workbench/adapter-supply';
|
|
4
4
|
import { probeRelease } from './release-port.js';
|
|
5
5
|
import { createTimedMemo } from './probe-cache.js';
|
|
@@ -13,17 +13,24 @@ export function parsePluginList(stdout) {
|
|
|
13
13
|
return parsed;
|
|
14
14
|
return /no plugins? installed/i.test(stdout) ? [] : undefined;
|
|
15
15
|
}
|
|
16
|
-
async function probeBinary(
|
|
16
|
+
async function probeBinary(seam, name) {
|
|
17
17
|
const line = process.platform === 'win32' ? `where ${quoteShellArg(name)}` : `command -v ${quoteShellArg(name)}`;
|
|
18
|
-
const result = await
|
|
18
|
+
const result = await seam
|
|
19
|
+
.run({ command: line, intent: noWrite(), retry: 'safe', timeoutMs: BINARY_PROBE_TIMEOUT_MS, bestEffort: true })
|
|
20
|
+
.catch(() => undefined);
|
|
19
21
|
return result?.exitCode === 0;
|
|
20
22
|
}
|
|
21
23
|
async function runtimeList(options) {
|
|
22
24
|
if (!options.needsPlugins)
|
|
23
25
|
return {};
|
|
24
|
-
const result = await
|
|
26
|
+
const result = await options.seam
|
|
27
|
+
.run({
|
|
28
|
+
command: `${options.command} plugin list -f json`,
|
|
29
|
+
intent: productWrite(),
|
|
30
|
+
retry: 'safe',
|
|
25
31
|
timeoutMs: OPENCLI_TIMEOUT_MS,
|
|
26
|
-
})
|
|
32
|
+
})
|
|
33
|
+
.then((value) => ({ ok: true, stdout: value.exitCode === 0 ? value.stdout : '', stderr: value.stderr }), (error) => ({ ok: false, stdout: '', stderr: String(error) }));
|
|
27
34
|
if (!result.ok)
|
|
28
35
|
return { failure: result.stderr };
|
|
29
36
|
const list = parsePluginList(result.stdout);
|
|
@@ -46,7 +53,7 @@ async function releasesOf(index, options) {
|
|
|
46
53
|
continue;
|
|
47
54
|
const registry = npmRegistryOf(source, process.env);
|
|
48
55
|
const signal = await probeRelease({
|
|
49
|
-
|
|
56
|
+
seam: options.seam,
|
|
50
57
|
package: entry.package,
|
|
51
58
|
...(registry.ok ? { registry: registry.value } : {}),
|
|
52
59
|
force: options.force,
|
|
@@ -70,7 +77,7 @@ function runtimeCommandOf(need, runtimeCommands) {
|
|
|
70
77
|
return KNOWN_RUNTIMES.includes(need.runtime) ? need.runtime : undefined;
|
|
71
78
|
}
|
|
72
79
|
export async function probeUnits(options) {
|
|
73
|
-
const {
|
|
80
|
+
const { seam, runtimeCommands, units, force = false } = options;
|
|
74
81
|
const commandKey = Object.values(runtimeCommands).sort().join('|');
|
|
75
82
|
const hits = units.map((unit) => unitCache.get(cacheKeyOf(commandKey, units, unit.unit)));
|
|
76
83
|
if (!force && hits.every((hit) => hit !== undefined)) {
|
|
@@ -82,7 +89,7 @@ export async function probeUnits(options) {
|
|
|
82
89
|
if (shared !== undefined)
|
|
83
90
|
return shared;
|
|
84
91
|
}
|
|
85
|
-
const task = loadCapabilities({
|
|
92
|
+
const task = loadCapabilities({ seam, runtimeCommands, units, force });
|
|
86
93
|
pendingProbes.set(key, task);
|
|
87
94
|
try {
|
|
88
95
|
return await task;
|
|
@@ -93,7 +100,7 @@ export async function probeUnits(options) {
|
|
|
93
100
|
}
|
|
94
101
|
}
|
|
95
102
|
async function loadCapabilities(options) {
|
|
96
|
-
const {
|
|
103
|
+
const { seam, runtimeCommands, units, force } = options;
|
|
97
104
|
const index = buildSupplyIndex(units.map((unit) => ({ id: unit.unit, needs: unit.needs })), {
|
|
98
105
|
env: process.env,
|
|
99
106
|
packagePrefix: 'opencli-plugin-',
|
|
@@ -105,7 +112,7 @@ async function loadCapabilities(options) {
|
|
|
105
112
|
const runtimeNames = Object.values(runtimeCommands);
|
|
106
113
|
await Promise.all(binNames.map(async (name) => {
|
|
107
114
|
const known = runtimeNames.some((command) => commandNamesMatch(name, command));
|
|
108
|
-
binaries.set(name, known ? true : await probeBinary(
|
|
115
|
+
binaries.set(name, known ? true : await probeBinary(seam, name));
|
|
109
116
|
}));
|
|
110
117
|
const extensionNeeds = units.flatMap((unit) => unit.needs.filter((need) => need.kind === 'extension'));
|
|
111
118
|
const runtimes = [...new Set(extensionNeeds.map((need) => need.runtime))];
|
|
@@ -117,11 +124,11 @@ async function loadCapabilities(options) {
|
|
|
117
124
|
return;
|
|
118
125
|
}
|
|
119
126
|
const needsPlugins = extensionNeeds.some((need) => need.runtime === runtime && conflictOf(index, need) === undefined);
|
|
120
|
-
lists.set(runtime, await runtimeList({
|
|
127
|
+
lists.set(runtime, await runtimeList({ seam, command, needsPlugins }));
|
|
121
128
|
}));
|
|
122
129
|
const libNeeds = units.flatMap((unit) => unit.needs.filter((need) => need.kind === 'lib'));
|
|
123
|
-
const libStates = await probeLibs({ needs: libNeeds,
|
|
124
|
-
const releases = await releasesOf(index, {
|
|
130
|
+
const libStates = await probeLibs({ needs: libNeeds, seam, force, index });
|
|
131
|
+
const releases = await releasesOf(index, { seam, force });
|
|
125
132
|
const statesOfUnit = (unit) => unit.needs.map((need) => {
|
|
126
133
|
const conflict = need.kind === 'extension' || need.kind === 'lib' ? conflictOf(index, need) : undefined;
|
|
127
134
|
if (conflict !== undefined) {
|
|
@@ -179,7 +186,7 @@ async function loadCapabilities(options) {
|
|
|
179
186
|
export async function probeUnitCommands(options) {
|
|
180
187
|
const units = options.catalog === undefined ? [options.unit] : options.catalog;
|
|
181
188
|
const all = await probeUnits({
|
|
182
|
-
|
|
189
|
+
seam: options.seam,
|
|
183
190
|
runtimeCommands: options.runtimeCommands ?? {},
|
|
184
191
|
units,
|
|
185
192
|
...(options.force === undefined ? {} : { force: options.force }),
|