@dsh-workbench/adapter-probe 1.1.8 → 1.1.10

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.
@@ -19,3 +19,4 @@ export declare function createDesktopBrowser(options: {
19
19
  shortWaitMs?: number;
20
20
  pollMs?: number;
21
21
  }): DesktopBrowser;
22
+ export declare function bridgeFixCommand(command: string, issues: readonly string[]): string | undefined;
@@ -261,3 +261,10 @@ export function createDesktopBrowser(options) {
261
261
  }
262
262
  return { observe, wake };
263
263
  }
264
+ export function bridgeFixCommand(command, issues) {
265
+ if (issues.includes('profile') || issues.includes('stale'))
266
+ return `${command} profile list`;
267
+ if (issues.includes('daemon'))
268
+ return `${command} daemon restart`;
269
+ return undefined;
270
+ }
@@ -0,0 +1 @@
1
+ export declare function commandNamesMatch(declared: string, resolved: string): boolean;
@@ -0,0 +1,12 @@
1
+ const SCRIPT_EXTENSION = /\.(cmd|exe|bat)$/i;
2
+ function baseName(value) {
3
+ const parts = value.trim().split(/[\\/]/);
4
+ return (parts[parts.length - 1] ?? '').replace(SCRIPT_EXTENSION, '');
5
+ }
6
+ export function commandNamesMatch(declared, resolved) {
7
+ const left = baseName(declared);
8
+ const right = baseName(resolved);
9
+ if (left === '' || right === '')
10
+ return false;
11
+ return left === right;
12
+ }
package/dist/index.d.ts CHANGED
@@ -12,3 +12,7 @@ export * from './toolchain.js';
12
12
  export * from './version-range.js';
13
13
  export * from './install.js';
14
14
  export * from './self-update.js';
15
+ export { createProbePort } from './probe-port.js';
16
+ export { createSupplyPort } from './supply-port.js';
17
+ export { packageRootOf } from '@dsh-workbench/shared/host';
18
+ export { resetReleaseCache } from './release-port.js';
package/dist/index.js CHANGED
@@ -12,3 +12,7 @@ export * from './toolchain.js';
12
12
  export * from './version-range.js';
13
13
  export * from './install.js';
14
14
  export * from './self-update.js';
15
+ export { createProbePort } from './probe-port.js';
16
+ export { createSupplyPort } from './supply-port.js';
17
+ export { packageRootOf } from '@dsh-workbench/shared/host';
18
+ export { resetReleaseCache } from './release-port.js';
@@ -1,12 +1,17 @@
1
- import type { DshHostContext, DshShell } from '@dsh-workbench/shared';
1
+ import type { DshHostContext, DshShell, RuntimeCapabilities } from '@dsh-workbench/shared';
2
2
  import type { RouteTable } from '@dsh-workbench/shared/host';
3
3
  import { type DesktopBrowser } from './browser-wake.js';
4
4
  export declare function registerPreflight(input: {
5
5
  routes: RouteTable;
6
6
  ctx: DshHostContext;
7
7
  shell: DshShell;
8
- opencliCommand: string;
9
- installCommand?: string | undefined;
8
+ runtimeCommands: Readonly<Record<string, string>>;
9
+ runtimeCapabilities?: RuntimeCapabilities | undefined;
10
+ runtimeCheck?: (() => Promise<{
11
+ ok: boolean;
12
+ detail: string;
13
+ }>) | undefined;
14
+ runtimeLabel?: string | undefined;
10
15
  warmReads: () => readonly (readonly string[])[];
11
16
  desktopBrowser?: DesktopBrowser | undefined;
12
17
  }): void;
@@ -21,20 +21,25 @@ function desktopBrowserOf(input) {
21
21
  });
22
22
  }
23
23
  export function registerPreflight(input) {
24
+ const command = input.runtimeCommands['opencli'] ?? Object.values(input.runtimeCommands)[0] ?? 'opencli';
25
+ const installCommand = process.env.DSH_WORKBENCH_OPENCLI_INSTALL;
24
26
  const desktopBrowser = input.desktopBrowser ??
25
27
  desktopBrowserOf({
26
28
  ctx: input.ctx,
27
29
  shell: input.shell,
28
- command: input.opencliCommand,
30
+ command,
29
31
  });
30
32
  const profileDir = profileDirOf(input.ctx);
31
33
  const surface = hostSurface({ profile: profileDir === null ? undefined : basename(profileDir) });
32
34
  const preflight = createPreflight({
33
35
  shell: input.shell,
34
- command: input.opencliCommand,
35
- ...(input.installCommand === undefined ? {} : { installCommand: input.installCommand }),
36
+ command,
37
+ ...(installCommand === undefined || installCommand === '' ? {} : { installCommand }),
36
38
  logger: input.ctx.logger,
37
39
  warmReads: input.warmReads,
40
+ ...(input.runtimeCapabilities === undefined ? {} : { capabilities: input.runtimeCapabilities }),
41
+ ...(input.runtimeCheck === undefined ? {} : { runtimeCheck: input.runtimeCheck }),
42
+ ...(input.runtimeLabel === undefined ? {} : { runtimeLabel: input.runtimeLabel }),
38
43
  probeDiy: createDiyPresetProbe(input.ctx),
39
44
  ...(surface.deployment === 'desktop'
40
45
  ? {
@@ -1,8 +1,8 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
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
4
  import type { DesktopBrowser } from './browser-wake.js';
5
- export type PreflightStepId = 'toolchain' | 'opencli' | 'bridge' | 'diy' | 'warm';
5
+ export type PreflightStepId = 'toolchain' | 'runtime' | 'cli' | 'bridge' | 'diy' | 'warm';
6
6
  export type PreflightStepStatus = 'pending' | 'running' | 'ok' | 'warn' | 'failed';
7
7
  export interface PreflightStep {
8
8
  readonly id: PreflightStepId;
@@ -23,6 +23,7 @@ export declare function classifyFailure(text: string): PreflightFailure;
23
23
  export type PreflightGuidance = 'install' | 'path' | 'shell';
24
24
  export declare function probeDesktopToolchain(shell: DshShell): Promise<ToolchainReport>;
25
25
  export type PreflightBridgeIssue = 'daemon' | 'extension' | 'profile' | 'stale' | 'unknown';
26
+ export declare const BRIDGE_ISSUE_ORDER: readonly PreflightBridgeIssue[];
26
27
  export declare function readBridgeReport(stdout: string): {
27
28
  readonly ok: boolean;
28
29
  readonly issues: readonly PreflightBridgeIssue[];
@@ -50,7 +51,7 @@ export interface Preflight {
50
51
  }): Promise<PreflightSnapshot>;
51
52
  snapshot(): PreflightSnapshot;
52
53
  }
53
- export declare const OPENCLI_INSTALL_COMMAND: string;
54
+ export declare function opencliInstallCommandDefault(): string;
54
55
  export interface BridgeVerdict {
55
56
  readonly ok: boolean;
56
57
  readonly detail: string;
@@ -78,4 +79,10 @@ export declare function createPreflight(options: {
78
79
  provisionNpm?: (() => Promise<LocalNpmProvision>) | undefined;
79
80
  provisionPnpm?: (() => Promise<LocalPnpmProvision>) | undefined;
80
81
  desktopBrowser?: DesktopBrowser | undefined;
82
+ capabilities?: RuntimeCapabilities | undefined;
83
+ runtimeCheck?: (() => Promise<{
84
+ ok: boolean;
85
+ detail: string;
86
+ }>) | undefined;
87
+ runtimeLabel?: string | undefined;
81
88
  }): Preflight;
package/dist/preflight.js CHANGED
@@ -5,6 +5,7 @@ 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
7
  import { desktopPnpmPath, LOCAL_NPM_VERSION, LOCAL_PNPM_VERSION, } from './toolchain.js';
8
+ import { bridgeFixCommand } from './browser-wake.js';
8
9
  export function classifyFailure(text) {
9
10
  const lower = text.toLowerCase();
10
11
  if (/eacces|eperm|permission denied|operation not permitted/.test(lower))
@@ -51,6 +52,7 @@ export async function probeDesktopToolchain(shell) {
51
52
  ];
52
53
  return { ok: !nodeMissing, detail: parts.join(' · '), nodeMissing, npmMissing, pnpmProblem };
53
54
  }
55
+ export const BRIDGE_ISSUE_ORDER = ['profile', 'stale', 'daemon', 'extension', 'unknown'];
54
56
  export function readBridgeReport(stdout) {
55
57
  const text = String(stdout ?? '');
56
58
  const ok = /^\[OK\] Daemon:/m.test(text) && /^\[OK\] Extension:/m.test(text) && /^\[OK\] Connectivity:/m.test(text);
@@ -72,8 +74,7 @@ export function readBridgeReport(stdout) {
72
74
  found.delete('extension');
73
75
  if (found.size === 0)
74
76
  found.add('unknown');
75
- const order = ['profile', 'stale', 'daemon', 'extension', 'unknown'];
76
- return { ok: false, issues: order.filter((issue) => found.has(issue)), detail: issuesSection(text) };
77
+ return { ok: false, issues: BRIDGE_ISSUE_ORDER.filter((issue) => found.has(issue)), detail: issuesSection(text) };
77
78
  }
78
79
  function issuesSection(text) {
79
80
  const lines = text.split('\n');
@@ -82,29 +83,28 @@ function issuesSection(text) {
82
83
  return tailLines(text, 6);
83
84
  return lines.slice(start).join('\n').trim();
84
85
  }
85
- function bridgeFixCommand(command, issues) {
86
- if (issues.includes('profile') || issues.includes('stale'))
87
- return `${command} profile list`;
88
- if (issues.includes('daemon'))
89
- return `${command} daemon restart`;
90
- return undefined;
86
+ export function opencliInstallCommandDefault() {
87
+ return opencliInstallCommand();
91
88
  }
92
- export const OPENCLI_INSTALL_COMMAND = opencliInstallCommand();
93
89
  const WARMABLE_BRIDGE_ISSUES = ['daemon', 'stale'];
94
90
  export function canWarm(bridge) {
95
91
  return bridge.ok || bridge.issues.every((issue) => WARMABLE_BRIDGE_ISSUES.includes(issue));
96
92
  }
97
- function step(id) {
98
- return { id, status: 'pending' };
93
+ function step(id, label) {
94
+ return { id, status: 'pending', ...(label === undefined || label === '' ? {} : { label }) };
99
95
  }
100
96
  export function createPreflight(options) {
101
97
  const { shell, command = 'opencli', logger } = options;
102
- const installCommand = options.installCommand ?? OPENCLI_INSTALL_COMMAND;
98
+ const capabilities = options.capabilities ?? { console: true, pluginMarket: true, browserBridge: true };
99
+ const withConsole = capabilities.console;
100
+ const withBridge = capabilities.browserBridge;
101
+ const installCommand = options.installCommand ?? opencliInstallCommandDefault();
103
102
  const warmReads = () => options.warmReads?.() ?? [];
104
103
  const steps = () => [
105
104
  ...(options.probeToolchain === undefined ? [] : [step('toolchain')]),
106
- step('opencli'),
107
- step('bridge'),
105
+ ...(options.runtimeCheck === undefined ? [] : [step('runtime', options.runtimeLabel)]),
106
+ ...(withConsole ? [step('cli')] : []),
107
+ ...(withBridge ? [step('bridge')] : []),
108
108
  ...(options.probeDiy === undefined ? [] : [step('diy')]),
109
109
  ...(warmReads().length === 0 ? [] : [step('warm')]),
110
110
  ];
@@ -229,6 +229,7 @@ export function createPreflight(options) {
229
229
  pnpmProblem: true,
230
230
  }));
231
231
  if (toolchain !== undefined && toolchain.ok && toolchain.npmMissing && options.provisionNpm !== undefined) {
232
+ set({ phase: 'installing' });
232
233
  patchStep('toolchain', { status: 'running', action: `装本地 npm@${LOCAL_NPM_VERSION}` });
233
234
  const provisioned = await options.provisionNpm().catch((error) => ({
234
235
  ok: false,
@@ -244,6 +245,7 @@ export function createPreflight(options) {
244
245
  }
245
246
  }
246
247
  if (toolchain !== undefined && toolchain.ok && toolchain.pnpmProblem && options.provisionPnpm !== undefined) {
248
+ set({ phase: 'installing' });
247
249
  patchStep('toolchain', { status: 'running', action: `装本地 pnpm@${LOCAL_PNPM_VERSION}` });
248
250
  const provisioned = await options.provisionPnpm().catch((error) => ({
249
251
  ok: false,
@@ -259,6 +261,7 @@ export function createPreflight(options) {
259
261
  }
260
262
  }
261
263
  if (toolchain !== undefined) {
264
+ set({ phase: 'checking' });
262
265
  patchStep('toolchain', {
263
266
  status: toolchain.ok ? (toolchain.npmMissing || toolchain.pnpmProblem ? 'warn' : 'ok') : 'failed',
264
267
  detail: toolchain.detail,
@@ -273,62 +276,74 @@ export function createPreflight(options) {
273
276
  });
274
277
  }
275
278
  }
276
- patchStep('opencli', { status: 'running', action: `${command} --version` });
277
- const diyStarted = options.probeDiy?.();
278
- if (diyStarted !== undefined)
279
- patchStep('diy', { status: 'running' });
280
- let version = await probeVersion();
281
- if (!version.ok && !version.missing) {
282
- patchStep('opencli', { status: 'failed', detail: version.detail, action: undefined });
283
- return set({
284
- phase: 'failed',
285
- error: version.detail,
286
- failure: classifyFailure(version.detail),
287
- guidance: 'shell',
288
- });
279
+ if (options.runtimeCheck !== undefined) {
280
+ patchStep('runtime', { status: 'running' });
281
+ const verdict = await options.runtimeCheck().catch((error) => ({
282
+ ok: false,
283
+ detail: error instanceof Error ? error.message : String(error),
284
+ }));
285
+ patchStep('runtime', { status: verdict.ok ? 'ok' : 'failed', detail: verdict.detail, action: undefined });
286
+ if (!verdict.ok)
287
+ return set({ phase: 'failed', error: verdict.detail, failure: 'missing', guidance: 'install' });
289
288
  }
290
- if (!version.ok) {
291
- if (toolchain?.npmMissing === true && installCommand.trim().startsWith('npm ')) {
292
- patchStep('opencli', { status: 'failed', detail: '没有 npm:装不了 opencli(只影响 opencli 安装)' });
293
- return set({
294
- phase: 'failed',
295
- error: '没有 npm:装不了 opencli(只影响 opencli 安装)',
296
- failure: 'missing',
297
- guidance: 'install',
298
- });
299
- }
300
- set({ phase: 'installing' });
301
- patchStep('opencli', { status: 'running', action: installCommand, command: installCommand });
302
- const installed = await installOpencli();
303
- if (!installed.ok) {
304
- patchStep('opencli', {
305
- status: 'failed',
306
- detail: installed.detail,
307
- command: installCommand,
308
- });
309
- return set({
310
- phase: 'failed',
311
- error: installed.detail,
312
- failure: classifyFailure(installed.detail),
313
- guidance: 'install',
314
- });
315
- }
316
- patchStep('opencli', { status: 'running', action: `${command} --version` });
317
- version = await probeVersion();
318
- if (!version.ok) {
319
- patchStep('opencli', { status: 'failed', detail: version.detail, command: installCommand });
289
+ const diyStarted = options.probeDiy?.();
290
+ if (withConsole) {
291
+ patchStep('cli', { status: 'running', action: `${command} --version` });
292
+ if (diyStarted !== undefined)
293
+ patchStep('diy', { status: 'running' });
294
+ let version = await probeVersion();
295
+ if (!version.ok && !version.missing) {
296
+ patchStep('cli', { status: 'failed', detail: version.detail, action: undefined });
320
297
  return set({
321
298
  phase: 'failed',
322
299
  error: version.detail,
323
300
  failure: classifyFailure(version.detail),
324
- guidance: 'path',
301
+ guidance: 'shell',
325
302
  });
326
303
  }
304
+ if (!version.ok) {
305
+ if (toolchain?.npmMissing === true && installCommand.trim().startsWith('npm ')) {
306
+ patchStep('cli', { status: 'failed', detail: '没有 npm:装不了 opencli(只影响 opencli 安装)' });
307
+ return set({
308
+ phase: 'failed',
309
+ error: '没有 npm:装不了 opencli(只影响 opencli 安装)',
310
+ failure: 'missing',
311
+ guidance: 'install',
312
+ });
313
+ }
314
+ set({ phase: 'installing' });
315
+ patchStep('cli', { status: 'running', action: installCommand, command: installCommand });
316
+ const installed = await installOpencli();
317
+ if (!installed.ok) {
318
+ patchStep('cli', {
319
+ status: 'failed',
320
+ detail: installed.detail,
321
+ command: installCommand,
322
+ });
323
+ return set({
324
+ phase: 'failed',
325
+ error: installed.detail,
326
+ failure: classifyFailure(installed.detail),
327
+ guidance: 'install',
328
+ });
329
+ }
330
+ patchStep('cli', { status: 'running', action: `${command} --version` });
331
+ version = await probeVersion();
332
+ if (!version.ok) {
333
+ patchStep('cli', { status: 'failed', detail: version.detail, command: installCommand });
334
+ return set({
335
+ phase: 'failed',
336
+ error: version.detail,
337
+ failure: classifyFailure(version.detail),
338
+ guidance: 'path',
339
+ });
340
+ }
341
+ }
342
+ patchStep('cli', { status: 'ok', detail: version.detail, action: undefined });
327
343
  }
328
- patchStep('opencli', { status: 'ok', detail: version.detail, action: undefined });
329
344
  patchStep('bridge', { status: 'running', action: `${command} doctor` });
330
- const bridge = await probeBridge();
331
- if (options.desktopBrowser !== undefined && bridge.ok) {
345
+ const bridge = withBridge ? await probeBridge() : { ok: true, detail: '', issues: [] };
346
+ if (withBridge && options.desktopBrowser !== undefined && bridge.ok) {
332
347
  await options.desktopBrowser.observe().catch(() => undefined);
333
348
  }
334
349
  const healable = !bridge.ok && warmReads().length > 0 && canWarm(bridge);
@@ -340,7 +355,7 @@ export function createPreflight(options) {
340
355
  ...(bridge.command === undefined ? {} : { command: bridge.command }),
341
356
  });
342
357
  }
343
- const settled = await warmSites(bridge);
358
+ const settled = withBridge ? await warmSites(bridge) : bridge;
344
359
  const diyGap = await settleDiy(diyStarted);
345
360
  logger?.warn?.(`[dsh-workbench] 环境体检:${current.steps.map((item) => `${item.id}=${item.status}`).join(' ')}`);
346
361
  return set({
@@ -1,9 +1,11 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { libInstallCommand, needIdOf, npmSourceOf } from '@dsh-workbench/shared/host';
3
+ import { libInstallCommand } from '@dsh-workbench/adapter-runtime';
4
+ import { needIdOf, npmSourceOf } from '@dsh-workbench/shared/host';
4
5
  import { libDir } from '@dsh-workbench/adapter-runtime';
5
6
  import { npmRegistryOf } from '@dsh-workbench/adapter-supply';
6
- import { isNewerVersion, latestVersion, resetReleaseCache } from '@dsh-workbench/adapter-profile';
7
+ import { isNewerVersion } from '@dsh-workbench/adapter-profile';
8
+ import { latestVersion, resetReleaseCache } from './release-port.js';
7
9
  import { checkVersionRange } from './version-range.js';
8
10
  export function installedLibVersion(name) {
9
11
  try {
@@ -0,0 +1,2 @@
1
+ import type { ProbePort } from '@dsh-workbench/shared/host';
2
+ export declare function createProbePort(): ProbePort;
@@ -0,0 +1,6 @@
1
+ import { probeUnits } from './probe-units.js';
2
+ import { registerPreflight } from './preflight-route.js';
3
+ import { declaredPluginCommands } from '@dsh-workbench/adapter-supply';
4
+ export function createProbePort() {
5
+ return { probeUnits, registerPreflight, declaredPluginCommands };
6
+ }
@@ -1,6 +1,7 @@
1
- import { OPENCLI_TIMEOUT_MS, PROBE_TTL_MS, buildSupplyIndex, NEED_KIND_SPECS, needIdOf, commandNamesMatch, npmSourceOf, parseJsonLoose, quoteShellArg, runShell, } from '@dsh-workbench/shared/host';
1
+ import { commandNamesMatch } from './command-name.js';
2
+ import { OPENCLI_TIMEOUT_MS, PROBE_TTL_MS, buildSupplyIndex, NEED_KIND_SPECS, needIdOf, npmSourceOf, parseJsonLoose, quoteShellArg, runShell, } from '@dsh-workbench/shared/host';
2
3
  import { npmRegistryOf } from '@dsh-workbench/adapter-supply';
3
- import { probeRelease } from '@dsh-workbench/adapter-profile';
4
+ import { probeRelease } from './release-port.js';
4
5
  import { createTimedMemo } from './probe-cache.js';
5
6
  import { probeLibs } from './probe-libs.js';
6
7
  import { aggregate, binStateOf, envStateOf, extensionStateOf } from './probe-verdict.js';
@@ -95,6 +96,7 @@ async function loadCapabilities(options) {
95
96
  const { shell, runtimeCommands, units, force } = options;
96
97
  const index = buildSupplyIndex(units.map((unit) => ({ id: unit.unit, needs: unit.needs })), {
97
98
  env: process.env,
99
+ packagePrefix: 'opencli-plugin-',
98
100
  });
99
101
  const binNames = [
100
102
  ...new Set(units.flatMap((unit) => unit.needs.filter((need) => need.kind === 'bin').map((need) => need.name.trim()).filter((name) => name !== ''))),
@@ -1,4 +1,5 @@
1
- import { binInstallCommand, needIdOf, quoteShellArg } from '@dsh-workbench/shared/host';
1
+ import { binInstallCommand } from '@dsh-workbench/adapter-runtime';
2
+ import { needIdOf, quoteShellArg } from '@dsh-workbench/shared/host';
2
3
  import { resolvePluginSource } from '@dsh-workbench/adapter-supply';
3
4
  import { isNewerVersion } from '@dsh-workbench/adapter-profile';
4
5
  import { currentHostOs } from './platform.js';
@@ -0,0 +1,20 @@
1
+ export declare const latestVersion: (options: {
2
+ readonly shell: import("@dsh-workbench/shared").DshShell;
3
+ readonly name: string;
4
+ readonly registry: string;
5
+ readonly force?: boolean;
6
+ }) => Promise<string | undefined>;
7
+ export declare const missingReleaseParts: (options: {
8
+ readonly shell: import("@dsh-workbench/shared").DshShell;
9
+ readonly name: string;
10
+ readonly registry: string;
11
+ readonly version: string;
12
+ readonly force?: boolean;
13
+ }) => Promise<readonly string[]>;
14
+ export declare const probeRelease: (options: {
15
+ readonly shell: import("@dsh-workbench/shared").DshShell;
16
+ readonly package: string;
17
+ readonly registry?: string | undefined;
18
+ readonly force?: boolean;
19
+ }) => Promise<import("@dsh-workbench/shared").PluginRelease | undefined>;
20
+ export declare const resetReleaseCache: () => void;
@@ -0,0 +1,7 @@
1
+ import { createReleasePort } from '@dsh-workbench/adapter-profile';
2
+ import { npmCacheDir, vendorPrefix } from '@dsh-workbench/adapter-runtime';
3
+ const port = createReleasePort({ cacheDir: npmCacheDir(), prefix: vendorPrefix() });
4
+ export const latestVersion = port.latestVersion;
5
+ export const missingReleaseParts = port.missingReleaseParts;
6
+ export const probeRelease = port.probeRelease;
7
+ export const resetReleaseCache = port.reset;
@@ -24,9 +24,10 @@ export declare function runtimeSeedPaths(root: string, platform?: NodeJS.Platfor
24
24
  export declare function runtimeSeedRootOf(ctx: DshHostContext): string | null;
25
25
  export declare function coversWritableRoot(workspaceRoot: string, root: string): boolean;
26
26
  export declare function warnUnwritableRoot(ctx: DshHostContext): void;
27
- export declare function packageRootOf(moduleUrl: string): string;
27
+ export declare function wrapperRootOf(fallbackRoot: string): string;
28
28
  export declare function isInstalledCopy(packageRoot: string): boolean;
29
29
  export declare function opencliWrapperScript(packageRoot: string, home: string, platform?: NodeJS.Platform): string;
30
+ export declare const runtimeWrapperScript: typeof opencliWrapperScript;
30
31
  export interface SeedCommand {
31
32
  readonly file: string;
32
33
  readonly args: readonly string[];
@@ -1,8 +1,9 @@
1
1
  import { execa } from 'execa';
2
+ import { toolchainManifestOf } from '@dsh-workbench/adapter-runtime';
3
+ import { createRequire } from 'node:module';
2
4
  import writeFileAtomic from 'write-file-atomic';
3
5
  import { chmodSync, closeSync, mkdirSync, openSync, readFileSync, rmSync, } from 'node:fs';
4
6
  import { delimiter, dirname, join, sep } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
7
  import { quoteShellArg } from '@dsh-workbench/shared/host';
7
8
  import { ensureToolchain, localNpmCache, provisionLocalPnpmSync } from './toolchain.js';
8
9
  export function opencliBinName(platform = process.platform) {
@@ -51,19 +52,25 @@ export function warnUnwritableRoot(ctx) {
51
52
  return;
52
53
  ctx.logger?.warn?.(`[dsh-workbench] 可写根 ${workspaceRoot} 盖不住 ${root}:市场装插件、自举命令可能被沙箱挡(把 profile/sandbox.patch.yml 那一行写进这个 profile 的 cordis.patch.yml 再重启)`);
53
54
  }
54
- export function packageRootOf(moduleUrl) {
55
- return dirname(dirname(dirname(fileURLToPath(moduleUrl))));
55
+ export function wrapperRootOf(fallbackRoot) {
56
+ try {
57
+ return dirname(createRequire(import.meta.url).resolve('@dsh-workbench/adapter-runtime/package.json'));
58
+ }
59
+ catch {
60
+ return fallbackRoot;
61
+ }
56
62
  }
57
63
  export function isInstalledCopy(packageRoot) {
58
64
  return packageRoot.split(sep).includes('node_modules');
59
65
  }
60
66
  export function opencliWrapperScript(packageRoot, home, platform = process.platform) {
61
- const script = join(packageRoot, 'bin', 'opencli-isolated.mjs');
67
+ const script = join(wrapperRootOf(packageRoot), 'bin', 'opencli-isolated.mjs');
62
68
  if (platform === 'win32') {
63
69
  return `@echo off\r\nset "DSH_HOME=${home}"\r\nnode "${script}" %*\r\n`;
64
70
  }
65
71
  return `#!/bin/sh\nexport DSH_HOME=${quoteShellArg(home)}\nexec node ${quoteShellArg(script)} "$@"\n`;
66
72
  }
73
+ export const runtimeWrapperScript = opencliWrapperScript;
67
74
  export function workshopSeedPlan(packageRoot, home, root, platform = process.platform) {
68
75
  const script = join(packageRoot, 'scripts', 'seed-workshop.sh');
69
76
  const env = { DSH_HOME: home, DSH_WB_SHELL_ROOT: packageRoot };
@@ -155,6 +162,13 @@ export function seedRuntimeFiles(options) {
155
162
  for (const dir of [paths.entryDir, paths.opencliBinDir, paths.opencliHome, paths.workshopPackages]) {
156
163
  mkdirSync(dir, { recursive: true });
157
164
  }
165
+ const manifest = join(paths.opencliRoot, 'toolchain', 'package.json');
166
+ mkdirSync(dirname(manifest), { recursive: true });
167
+ if (readText(manifest) !== toolchainManifestOf()) {
168
+ writeFileAtomic.sync(manifest, toolchainManifestOf(), 'utf8');
169
+ done.push(manifest);
170
+ }
171
+ rmSync(join(paths.opencliRoot, 'toolchain', 'pnpm-workspace.yaml'), { force: true });
158
172
  const wanted = opencliWrapperScript(options.packageRoot, options.home, platform);
159
173
  if (readText(paths.opencliBin) !== wanted) {
160
174
  rmSync(paths.opencliBin, { force: true });
@@ -0,0 +1,2 @@
1
+ import type { SupplyPort } from '@dsh-workbench/shared/host';
2
+ export declare function createSupplyPort(): SupplyPort;
@@ -0,0 +1,48 @@
1
+ import { cliCatalogPath, ensureLibsBinOnPath, ensureLibsOpencliPeer, isolatedHome, libInstallCommand, libUninstallCommand, vendorPluginsDir } from '@dsh-workbench/adapter-runtime';
2
+ import { isolatedRoot } from '@dsh-workbench/adapter-runtime';
3
+ import { createRegistryNotebook, isNewerVersion } from '@dsh-workbench/adapter-profile';
4
+ import { latestVersion, missingReleaseParts, probeRelease } from './release-port.js';
5
+ import { opencliInstallCommand } from './install.js';
6
+ import { startSelfUpdate } from './self-update.js';
7
+ import { declarationGaps, declarationRefusalOf, moduleDeclarationOf, packageRouteOf, pathDeclarationOf, pluginInstallPlan, profileDirOf, profileModules, readInstalledModule, readProfilePackage, registryDeclarationOf, resolvePluginSource, splitNpmSpec, } from '@dsh-workbench/adapter-supply';
8
+ import { readBundleManager, refusalReason } from '@dsh-workbench/shared/host';
9
+ import { OPENCLI_MANIFEST_FILE } from '@dsh-workbench/adapter-runtime';
10
+ export function createSupplyPort() {
11
+ const port = {
12
+ isolatedRoot,
13
+ isolatedHome,
14
+ pluginPackagePrefix: () => 'opencli-plugin-',
15
+ pluginManifestFile: () => OPENCLI_MANIFEST_FILE,
16
+ cliCatalogPath,
17
+ vendorPluginsDir,
18
+ runtimeInstallCommand: opencliInstallCommand,
19
+ afterLibInstall() {
20
+ ensureLibsOpencliPeer();
21
+ ensureLibsBinOnPath();
22
+ },
23
+ profileDirOf,
24
+ profileModules,
25
+ readBundleManager,
26
+ refusalReason,
27
+ readInstalledModule,
28
+ readProfilePackage,
29
+ createRegistryNotebook,
30
+ declarationGaps,
31
+ moduleDeclarationOf,
32
+ registryDeclarationOf,
33
+ pathDeclarationOf,
34
+ declarationRefusalOf,
35
+ latestVersion,
36
+ missingReleaseParts,
37
+ isNewerVersion,
38
+ pluginInstallPlan,
39
+ packageRouteOf,
40
+ probeRelease,
41
+ resolvePluginSource,
42
+ splitNpmSpec,
43
+ libInstallCommand,
44
+ libUninstallCommand,
45
+ startSelfUpdate,
46
+ };
47
+ return { ...port, env: port, modules: port };
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/adapter-probe",
3
- "version": "1.1.8",
3
+ "version": "1.1.10",
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/adapter-profile": "1.1.8",
20
- "@dsh-workbench/adapter-supply": "1.1.8",
21
- "@dsh-workbench/shared": "1.1.8",
22
- "@dsh-workbench/adapter-runtime": "1.1.8"
19
+ "@dsh-workbench/adapter-supply": "1.1.10",
20
+ "@dsh-workbench/shared": "1.1.10",
21
+ "@dsh-workbench/adapter-runtime": "1.1.10",
22
+ "@dsh-workbench/adapter-profile": "1.1.10"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^24.19.0",