@mearl/setup 2.20.5 → 2.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/aliSkills.d.ts +1 -1
- package/dist/aliSkills.js +1 -1
- package/dist/internalPlugins.d.ts +1 -1
- package/dist/internalPlugins.js +1 -1
- package/dist/lifecycle.d.ts +3 -1
- package/dist/lifecycle.js +15 -10
- package/dist/packageManager.js +1 -1
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.js +6 -3
- package/dist/pluginRuntime.d.ts +1 -1
- package/dist/pluginRuntime.js +1 -1
- package/dist/provider.d.ts +3 -3
- package/dist/provider.js +21 -4
- package/dist/runtimeCleanup.d.ts +2 -0
- package/dist/runtimeCleanup.js +50 -0
- package/package.json +4 -7
- package/dist/process.d.ts +0 -38
- package/dist/process.js +0 -139
package/README.md
CHANGED
|
@@ -121,6 +121,12 @@ npx @mearl/setup provider uninstall brow
|
|
|
121
121
|
Ali Skills 安装的插件会同时移除 Skill;手工注册的外部插件只清除注册、保留源码;Provider
|
|
122
122
|
只有在包管理器成功卸载 npm 包后才清除注册。单项失败不阻止其他项继续清理,命令最终返回非零。
|
|
123
123
|
该选项不删除 Chrome 扩展;`extension --full` 仍表示安装扩展及完整本地环境。
|
|
124
|
+
- `uninstall --full` 在所有环境、插件与 Provider 成功移除后统一收尾:停止插件执行服务,
|
|
125
|
+
确认没有活跃或退出状态未明的任务,再删除任务历史、源码快照、服务日志、AuthX SDK 缓存、
|
|
126
|
+
旧设计稿缓存和已结束宿主的页面会话文件。只卸载部分环境、单项失败或服务重新启动时保留共享缓存。
|
|
127
|
+
即使包和注册已移除,也可以再次运行该命令完成缓存收尾。自定义 `MEARL_PLUGIN_DIR` 只清理该注册表的执行缓存。
|
|
128
|
+
记事本、迭代记录、配置与身份、插件持久状态、Provider 私有数据、浏览器配置及用户导出的设计稿均保留;
|
|
129
|
+
CDP 标签 ID 计数也保留,防止旧请求命中新标签。
|
|
124
130
|
- `--mcp` 的客户端配置和清理由 `@mearl/mcp-server` 自己的命令完成。
|
|
125
131
|
- 默认的 `install` / `update` 不处理浏览器扩展。`extension` 是独立命令:从
|
|
126
132
|
`https://mearl.alibaba-inc.com/` 识别并下载最新的 `mearl-chrome-v*.zip`,原子解压到
|
package/dist/aliSkills.d.ts
CHANGED
package/dist/aliSkills.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
|
-
import { executableNames, findExecutable, isExecutable, prependCommandPath, prependNodePath, resolveSpawnCommand, runCommand, versionManagerExecutableCandidates, } from '
|
|
5
|
+
import { executableNames, findExecutable, isExecutable, prependCommandPath, prependNodePath, resolveSpawnCommand, runCommand, versionManagerExecutableCandidates, } from '@mearl/daemon-core';
|
|
6
6
|
const COMMAND_TIMEOUT_MS = 120_000;
|
|
7
7
|
const MAX_OUTPUT_LENGTH = 1_000_000;
|
|
8
8
|
const ANSI_ESCAPE_PATTERN = new RegExp(String.raw `\u001B\[[0-?]*[ -/]*[@-~]`, 'g');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { findPluginRegistration, listPluginNames, unregisterPlugin } from '@mearl/client/plugin';
|
|
2
2
|
import { resolveAliSkillsInvocation } from './aliSkills.js';
|
|
3
3
|
import { installPlugins } from './plugin.js';
|
|
4
|
-
import { findExecutable, type CommandRunner } from '
|
|
4
|
+
import { findExecutable, type CommandRunner } from '@mearl/daemon-core';
|
|
5
5
|
export { ALIBABA_NPM_REGISTRY } from './npmRegistry.js';
|
|
6
6
|
export declare const ALIBABA_INTERNAL_PLUGIN_NAMES: string[];
|
|
7
7
|
export interface InternalPluginOptions {
|
package/dist/internalPlugins.js
CHANGED
|
@@ -3,7 +3,7 @@ import { findOfficialPlugin, findPluginRegistration, listPluginNames, listOffici
|
|
|
3
3
|
import { resolveAliSkillsInvocation } from './aliSkills.js';
|
|
4
4
|
import { ALIBABA_NPM_REGISTRY } from './npmRegistry.js';
|
|
5
5
|
import { installPlugins } from './plugin.js';
|
|
6
|
-
import { findExecutable, prependCommandPath, runCommand } from '
|
|
6
|
+
import { findExecutable, prependCommandPath, runCommand, } from '@mearl/daemon-core';
|
|
7
7
|
export { ALIBABA_NPM_REGISTRY } from './npmRegistry.js';
|
|
8
8
|
export const ALIBABA_INTERNAL_PLUGIN_NAMES = listOfficialPluginNames('alibaba');
|
|
9
9
|
export async function canAccessAlibabaNpmRegistry(options = {}, dependencies = {}) {
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -4,10 +4,11 @@ import { runGlobalMearlSkillOperation } from './aliSkills.js';
|
|
|
4
4
|
import { detectGlobalPackage, type PackageManagerInstallation, type PackageDetectionOptions } from './packageManager.js';
|
|
5
5
|
import { type DetectedMearlProfiles, type MearlProfile } from './profiles.js';
|
|
6
6
|
import { uninstallNativeHostArtifacts } from './nativeHost.js';
|
|
7
|
-
import { type CommandRunner } from '
|
|
7
|
+
import { type CommandRunner } from '@mearl/daemon-core';
|
|
8
8
|
import { installAlibabaInternalPlugins, uninstallAlibabaInternalPlugins } from './internalPlugins.js';
|
|
9
9
|
import { uninstallPlugin } from './plugin.js';
|
|
10
10
|
import { uninstallProviderPackage } from './provider.js';
|
|
11
|
+
import { cleanupUninstalledRuntime } from './runtimeCleanup.js';
|
|
11
12
|
export type MearlLifecycleOperation = 'install' | 'update' | 'uninstall';
|
|
12
13
|
export interface MearlLifecycleOptions {
|
|
13
14
|
operation: MearlLifecycleOperation;
|
|
@@ -23,6 +24,7 @@ export interface MearlLifecycleOptions {
|
|
|
23
24
|
env?: NodeJS.ProcessEnv;
|
|
24
25
|
}
|
|
25
26
|
export interface MearlLifecycleDependencies {
|
|
27
|
+
cleanupRuntime?: typeof cleanupUninstalledRuntime;
|
|
26
28
|
cleanupNativeHost?: typeof uninstallNativeHostArtifacts;
|
|
27
29
|
confirm?: (message: string) => Promise<boolean>;
|
|
28
30
|
detectPackage?: typeof detectGlobalPackage;
|
package/dist/lifecycle.js
CHANGED
|
@@ -8,12 +8,13 @@ import { runGlobalMearlSkillOperation } from './aliSkills.js';
|
|
|
8
8
|
import { buildGlobalPackageCommand, detectGlobalPackage, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';
|
|
9
9
|
import { MEARL_PROFILES, detectMearlProfiles, } from './profiles.js';
|
|
10
10
|
import { uninstallNativeHostArtifacts } from './nativeHost.js';
|
|
11
|
-
import { executableNames, prependCommandPath, runCommand, } from '
|
|
11
|
+
import { executableNames, prependCommandPath, runCommand, } from '@mearl/daemon-core';
|
|
12
12
|
import { isMearlReleaseLine } from './version.js';
|
|
13
13
|
import { installAlibabaInternalPlugins, uninstallAlibabaInternalPlugins, } from './internalPlugins.js';
|
|
14
14
|
import { uninstallPlugin } from './plugin.js';
|
|
15
15
|
import { uninstallProviderPackage } from './provider.js';
|
|
16
16
|
import { packageRegistryEnvironment } from './npmRegistry.js';
|
|
17
|
+
import { cleanupUninstalledRuntime } from './runtimeCleanup.js';
|
|
17
18
|
const LEGACY_CLOUD_CLIENT = '@mearl/cloud-client';
|
|
18
19
|
const UNIFIED_CLIENT = '@mearl/client';
|
|
19
20
|
const PROFILE_ORDER = ['local', 'cloud', 'mcp'];
|
|
@@ -227,17 +228,9 @@ export async function runMearlLifecycle(options, dependencies = {}) {
|
|
|
227
228
|
return false;
|
|
228
229
|
}
|
|
229
230
|
}
|
|
230
|
-
const hasFullIntegrationTargets = fullPluginNames.length > 0 || fullProviders.length > 0;
|
|
231
|
-
if (options.operation === 'uninstall' &&
|
|
232
|
-
options.full &&
|
|
233
|
-
lifecycleSelected.length === 0 &&
|
|
234
|
-
!hasFullIntegrationTargets) {
|
|
235
|
-
writeLine('No installed Mearl environments, CLI plugins, or Providers were detected.');
|
|
236
|
-
return true;
|
|
237
|
-
}
|
|
238
231
|
if (selected.length === 0 &&
|
|
239
232
|
!autoUpdateClient &&
|
|
240
|
-
!(options.operation === 'uninstall' && options.full
|
|
233
|
+
!(options.operation === 'uninstall' && options.full)) {
|
|
241
234
|
writeLine('No installed Mearl environment was detected on the current PATH.');
|
|
242
235
|
if (options.operation === 'uninstall')
|
|
243
236
|
return true;
|
|
@@ -488,6 +481,18 @@ export async function runMearlLifecycle(options, dependencies = {}) {
|
|
|
488
481
|
}
|
|
489
482
|
}
|
|
490
483
|
if (packagesOk && skillOk && fullOk) {
|
|
484
|
+
if (options.operation === 'uninstall' &&
|
|
485
|
+
options.full &&
|
|
486
|
+
PROFILE_ORDER.every(profile => !detected.has(profile) || lifecycleSelected.includes(profile))) {
|
|
487
|
+
try {
|
|
488
|
+
await (dependencies.cleanupRuntime ?? cleanupUninstalledRuntime)();
|
|
489
|
+
writeLine('Runtime caches: cleaned; user data and browser profiles were retained.');
|
|
490
|
+
}
|
|
491
|
+
catch (error) {
|
|
492
|
+
writeLine(`Runtime caches: ${error instanceof Error ? error.message : String(error)}`);
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
491
496
|
writeLine('Mearl setup complete.');
|
|
492
497
|
return true;
|
|
493
498
|
}
|
package/dist/packageManager.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { executableNames, findExecutable, isExecutable } from '
|
|
3
|
+
import { executableNames, findExecutable, isExecutable } from '@mearl/daemon-core';
|
|
4
4
|
function normalizePath(filePath) {
|
|
5
5
|
return filePath.replaceAll('\\', '/').replace(/\/+$/, '');
|
|
6
6
|
}
|
package/dist/plugin.d.ts
CHANGED
package/dist/plugin.js
CHANGED
|
@@ -4,7 +4,7 @@ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'nod
|
|
|
4
4
|
import { assertPluginName, findPluginDirectoryEntry, findOfficialPlugin, findPluginEntry, findPluginRegistration, listPluginNames, loadPlugin, OFFICIAL_PLUGIN_MANAGER, registerPlugin, resolvePluginEntry, unregisterPlugin, } from '@mearl/client/plugin';
|
|
5
5
|
import { ensurePluginRuntime } from './pluginRuntime.js';
|
|
6
6
|
import { resolveAliSkillsInvocation, stripAnsi } from './aliSkills.js';
|
|
7
|
-
import { findExecutable, prependNodePath, runCommand } from '
|
|
7
|
+
import { findExecutable, prependNodePath, runCommand, } from '@mearl/daemon-core';
|
|
8
8
|
const registrationContext = {
|
|
9
9
|
options: {},
|
|
10
10
|
invoke: async () => {
|
|
@@ -38,6 +38,7 @@ export async function setupPlugin(target, requestedName) {
|
|
|
38
38
|
await ensurePluginRuntime();
|
|
39
39
|
return {
|
|
40
40
|
entry: registerPlugin(name, entry, plugin.description, {
|
|
41
|
+
hasPublicCommands: Boolean(plugin.commandHelp(name)),
|
|
41
42
|
toolbox: plugin.toolbox,
|
|
42
43
|
appProfiles: plugin.appProfiles,
|
|
43
44
|
profileSources: plugin.profileSources,
|
|
@@ -266,6 +267,7 @@ export async function installPlugins(args, runner = runCommand, options = {}) {
|
|
|
266
267
|
throw new Error(`多个 skill 使用插件名称 ${name},请调整入口的 name metadata。`);
|
|
267
268
|
plugins.set(name, {
|
|
268
269
|
entry,
|
|
270
|
+
hasPublicCommands: Boolean(plugin.commandHelp(name)),
|
|
269
271
|
description: plugin.description,
|
|
270
272
|
...(plugin.toolbox ? { toolbox: plugin.toolbox } : {}),
|
|
271
273
|
appProfiles: plugin.appProfiles,
|
|
@@ -277,14 +279,15 @@ export async function installPlugins(args, runner = runCommand, options = {}) {
|
|
|
277
279
|
return 0;
|
|
278
280
|
}
|
|
279
281
|
await ensurePluginRuntime(runner);
|
|
280
|
-
for (const [name, { entry, description, toolbox, appProfiles, profileSources }] of plugins) {
|
|
282
|
+
for (const [name, { entry, description, toolbox, appProfiles, profileSources, hasPublicCommands },] of plugins) {
|
|
281
283
|
registerPlugin(name, entry, description, {
|
|
284
|
+
hasPublicCommands,
|
|
282
285
|
managedBy: options.registrationManagedBy,
|
|
283
286
|
...(toolbox ? { toolbox } : {}),
|
|
284
287
|
appProfiles,
|
|
285
288
|
profileSources,
|
|
286
289
|
});
|
|
287
|
-
console.error(`Plugin ${name} registered: ${entry}
|
|
290
|
+
console.error(`Plugin ${name} registered: ${entry}${hasPublicCommands ? `\nRun: mearl ${name} --help` : toolbox ? '\nOpen in Mearl toolbox.' : ''}`);
|
|
288
291
|
}
|
|
289
292
|
return 0;
|
|
290
293
|
}
|
package/dist/pluginRuntime.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CommandRunner } from '
|
|
1
|
+
import { type CommandRunner } from '@mearl/daemon-core';
|
|
2
2
|
export declare const PLUGIN_RUNTIME_VERSION: string;
|
|
3
3
|
/** 插件使用当前 setup 同版本的运行时;已安装的更高版本保持不动。 */
|
|
4
4
|
export declare function ensurePluginRuntime(runner?: CommandRunner): Promise<void>;
|
package/dist/pluginRuntime.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { buildGlobalPackageCommand, detectCliPackage, detectGlobalPackage, resolveInvokingPackageManager, } from './packageManager.js';
|
|
5
|
-
import { prependCommandPath, runCommand } from '
|
|
5
|
+
import { prependCommandPath, runCommand } from '@mearl/daemon-core';
|
|
6
6
|
import { compareMearlReleaseVersions, isMearlReleaseVersion } from './version.js';
|
|
7
7
|
import { packageRegistryEnvironment } from './npmRegistry.js';
|
|
8
8
|
const require = createRequire(import.meta.url);
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { listInstalledProviders, registerProviderPackage,
|
|
1
|
+
import { listInstalledProviders, registerProviderPackage, uninstallProvider, type InstalledProviderManifest } from '@mearl/provider/host';
|
|
2
2
|
import { resolvePackageManagerInstallation, type PackageManagerInstallation } from './packageManager.js';
|
|
3
|
-
import { type CommandRunner } from '
|
|
3
|
+
import { type CommandRunner } from '@mearl/daemon-core';
|
|
4
4
|
interface ProviderPackageSpec {
|
|
5
5
|
packageName: string;
|
|
6
6
|
target: string;
|
|
@@ -13,7 +13,7 @@ export interface ProviderSetupDependencies {
|
|
|
13
13
|
resolvePackageManager?: typeof resolvePackageManagerInstallation;
|
|
14
14
|
resolvePackageRoot?: (installation: PackageManagerInstallation, packageName: string, runner: CommandRunner, env: NodeJS.ProcessEnv) => Promise<string>;
|
|
15
15
|
runCommand?: CommandRunner;
|
|
16
|
-
stopProvider?:
|
|
16
|
+
stopProvider?: (providerId: string) => void | boolean | Promise<void | boolean>;
|
|
17
17
|
unregisterProvider?: typeof uninstallProvider;
|
|
18
18
|
}
|
|
19
19
|
export interface ProviderSetupResult {
|
package/dist/provider.js
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
|
-
import { listInstalledProviders, registerProviderPackage, stopProvider, uninstallProvider, } from '@mearl/provider/host';
|
|
3
|
+
import { listInstalledProviders, registerProviderPackage, stopProvider, readLiveProvider, readProviderStart, uninstallProvider, } from '@mearl/provider/host';
|
|
4
4
|
import { buildGlobalPackageCommand, resolveInvokingPackageManager, resolvePackageManagerInstallation, } from './packageManager.js';
|
|
5
5
|
import { packageRegistryEnvironment } from './npmRegistry.js';
|
|
6
|
-
import { prependCommandPath, runCommand } from '
|
|
6
|
+
import { isProcessOwnerAlive, processIdentity, prependCommandPath, runCommand, } from '@mearl/daemon-core';
|
|
7
|
+
async function stopProviderBeforePackageChange(providerId) {
|
|
8
|
+
if (readProviderStart(providerId))
|
|
9
|
+
throw new Error(`Provider ${providerId} is still starting`);
|
|
10
|
+
const live = readLiveProvider(providerId);
|
|
11
|
+
if (!live)
|
|
12
|
+
return;
|
|
13
|
+
const owner = { pid: live.pid, processIdentity: processIdentity(live.pid) };
|
|
14
|
+
stopProvider(providerId);
|
|
15
|
+
const deadline = Date.now() + 5000;
|
|
16
|
+
while (isProcessOwnerAlive(owner)) {
|
|
17
|
+
if (Date.now() >= deadline)
|
|
18
|
+
throw new Error(`Provider ${providerId} did not finish stopping`);
|
|
19
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
20
|
+
}
|
|
21
|
+
if (readLiveProvider(providerId) || readProviderStart(providerId))
|
|
22
|
+
throw new Error(`Provider ${providerId} restarted during shutdown`);
|
|
23
|
+
}
|
|
7
24
|
export function parseProviderPackageSpec(value) {
|
|
8
25
|
const input = value.trim();
|
|
9
26
|
const nameMatch = input.match(/^(@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*|[a-z0-9][a-z0-9._-]*)(?:@(.+))?$/i);
|
|
@@ -94,7 +111,7 @@ export async function updateProviderPackage(selector, dependencies = {}) {
|
|
|
94
111
|
const runner = dependencies.runCommand ?? runCommand;
|
|
95
112
|
const detectedManager = (dependencies.resolvePackageManager ?? resolvePackageManagerInstallation)(current.packageRoot, current.packageName) ?? undefined;
|
|
96
113
|
const installation = providerManager(env, dependencies, detectedManager);
|
|
97
|
-
(dependencies.stopProvider ??
|
|
114
|
+
await (dependencies.stopProvider ?? stopProviderBeforePackageChange)(current.providerId);
|
|
98
115
|
await runPackageCommand(installation, 'update', current.packageName, 'latest', runner, env);
|
|
99
116
|
const manifest = (dependencies.registerPackage ?? registerProviderPackage)(current.packageRoot);
|
|
100
117
|
return { manifest, manager: installation.manager };
|
|
@@ -104,7 +121,7 @@ export async function uninstallProviderPackage(selector, dependencies = {}) {
|
|
|
104
121
|
const manifest = findRegisteredProvider(selector, listProviders);
|
|
105
122
|
const env = process.env;
|
|
106
123
|
const installation = (dependencies.resolvePackageManager ?? resolvePackageManagerInstallation)(manifest.packageRoot, manifest.packageName) ?? providerManager(env, dependencies);
|
|
107
|
-
(dependencies.stopProvider ??
|
|
124
|
+
await (dependencies.stopProvider ?? stopProviderBeforePackageChange)(manifest.providerId);
|
|
108
125
|
await runPackageCommand(installation, 'uninstall', manifest.packageName, 'latest', dependencies.runCommand ?? runCommand, env);
|
|
109
126
|
(dependencies.unregisterProvider ?? uninstallProvider)(manifest.providerId);
|
|
110
127
|
return { manifest, manager: installation.manager };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { readdirSync, rmSync } from 'node:fs';
|
|
2
|
+
import { basename, join, resolve } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { cleanupPluginRuntime, pluginDirectory } from '@mearl/client/plugin';
|
|
5
|
+
import { isProcessOwnerAlive, listRecordFiles, pruneOrphanLogs, readRecord, removeRecordIfPid, } from '@mearl/daemon-core';
|
|
6
|
+
/** User state, browser profiles and CDP tab-ID counters survive uninstall. */
|
|
7
|
+
export async function cleanupUninstalledRuntime() {
|
|
8
|
+
await cleanupPluginRuntime();
|
|
9
|
+
const root = join(homedir(), '.mearl');
|
|
10
|
+
if (resolve(pluginDirectory()) !== resolve(join(root, 'plugins')))
|
|
11
|
+
return;
|
|
12
|
+
if (listRecordFiles(join(root, 'providers/installed')).length)
|
|
13
|
+
throw new Error('Registered Providers still use runtime caches; caches were retained');
|
|
14
|
+
for (const directory of ['providers/live', 'providers/starting']) {
|
|
15
|
+
if (listRecordFiles(join(root, directory)).some(file => {
|
|
16
|
+
const record = readRecord(file);
|
|
17
|
+
return record && isProcessOwnerAlive(record);
|
|
18
|
+
}))
|
|
19
|
+
throw new Error('Providers still use runtime caches; caches were retained');
|
|
20
|
+
}
|
|
21
|
+
for (const directory of [
|
|
22
|
+
'authx-sdk',
|
|
23
|
+
'design-overlay/objects',
|
|
24
|
+
'design-overlay/uploads',
|
|
25
|
+
'design-overlay/pages',
|
|
26
|
+
])
|
|
27
|
+
rmSync(join(root, directory), { recursive: true, force: true });
|
|
28
|
+
pruneOrphanLogs(join(root, 'managed-browsers'), 0);
|
|
29
|
+
pruneOrphanLogs(join(root, 'providers/logs'), 0);
|
|
30
|
+
let entries;
|
|
31
|
+
try {
|
|
32
|
+
entries = readdirSync(join(root, 'plugin-pages'), { withFileTypes: true });
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error.code === 'ENOENT')
|
|
36
|
+
return;
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
if (!entry.isDirectory() || !/^[a-f0-9]{16}\.json\.d$/.test(entry.name))
|
|
41
|
+
continue;
|
|
42
|
+
for (const file of listRecordFiles(join(root, 'plugin-pages', entry.name))) {
|
|
43
|
+
if (!/^session-[a-f0-9]{16}\.json$/.test(basename(file)))
|
|
44
|
+
continue;
|
|
45
|
+
const record = readRecord(file);
|
|
46
|
+
if (record && !isProcessOwnerAlive(record))
|
|
47
|
+
removeRecordIfPid(file, record.pid);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/setup",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.21.0",
|
|
4
4
|
"description": "One-shot environment setup and configuration for Mearl",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,10 +18,6 @@
|
|
|
18
18
|
"types": "./dist/aliSkills.d.ts",
|
|
19
19
|
"import": "./dist/aliSkills.js"
|
|
20
20
|
},
|
|
21
|
-
"./process": {
|
|
22
|
-
"types": "./dist/process.d.ts",
|
|
23
|
-
"import": "./dist/process.js"
|
|
24
|
-
},
|
|
25
21
|
"./package-manager": {
|
|
26
22
|
"types": "./dist/packageManager.d.ts",
|
|
27
23
|
"import": "./dist/packageManager.js"
|
|
@@ -69,8 +65,9 @@
|
|
|
69
65
|
"commander": "^15.0.0",
|
|
70
66
|
"extract-zip": "^2.0.1",
|
|
71
67
|
"ws": "^8.18.3",
|
|
72
|
-
"@mearl/client": "2.
|
|
73
|
-
"@mearl/provider": "2.
|
|
68
|
+
"@mearl/client": "2.21.0",
|
|
69
|
+
"@mearl/provider": "2.21.0",
|
|
70
|
+
"@mearl/daemon-core": "2.21.0"
|
|
74
71
|
},
|
|
75
72
|
"devDependencies": {
|
|
76
73
|
"@types/node": "^24.9.1",
|
package/dist/process.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
export interface SpawnCommand {
|
|
3
|
-
command: string;
|
|
4
|
-
args: string[];
|
|
5
|
-
}
|
|
6
|
-
export interface CommandResult {
|
|
7
|
-
code: number;
|
|
8
|
-
stdout: string;
|
|
9
|
-
stderr: string;
|
|
10
|
-
}
|
|
11
|
-
export interface RunCommandOptions {
|
|
12
|
-
capture?: boolean;
|
|
13
|
-
/** 显示并捕获输出,同时继承 stdin 以保留安装器交互。 */
|
|
14
|
-
tee?: boolean;
|
|
15
|
-
cwd?: string;
|
|
16
|
-
env?: NodeJS.ProcessEnv;
|
|
17
|
-
timeoutMs?: number;
|
|
18
|
-
}
|
|
19
|
-
export type CommandRunner = (command: string, args: string[], options?: RunCommandOptions) => Promise<CommandResult>;
|
|
20
|
-
export declare function isExecutable(filePath: string, platform?: NodeJS.Platform): boolean;
|
|
21
|
-
export declare function executableNames(name: string, platform?: NodeJS.Platform): string[];
|
|
22
|
-
export interface VersionManagerExecutableCandidateOptions {
|
|
23
|
-
env?: NodeJS.ProcessEnv;
|
|
24
|
-
platform?: NodeJS.Platform;
|
|
25
|
-
readdirSync?: typeof fs.readdirSync;
|
|
26
|
-
}
|
|
27
|
-
export declare function versionManagerExecutableCandidates(home: string, names: string[], options?: VersionManagerExecutableCandidateOptions): string[];
|
|
28
|
-
export declare function resolveSpawnCommand(executable: string, args: string[], platform?: NodeJS.Platform, commandInterpreter?: string | undefined): SpawnCommand;
|
|
29
|
-
export declare function prependCommandPath(command: string, env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
30
|
-
export declare function prependNodePath(env?: NodeJS.ProcessEnv, nodeExecutable?: string): NodeJS.ProcessEnv;
|
|
31
|
-
export declare function prependPathDirectory(directory: string, env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
32
|
-
export declare function findExecutable(name: string, options?: {
|
|
33
|
-
env?: NodeJS.ProcessEnv;
|
|
34
|
-
existsSync?: (filePath: string) => boolean;
|
|
35
|
-
platform?: NodeJS.Platform;
|
|
36
|
-
extraDirectories?: string[];
|
|
37
|
-
}): string | null;
|
|
38
|
-
export declare const runCommand: CommandRunner;
|
package/dist/process.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
4
|
-
const MAX_CAPTURE_LENGTH = 1_000_000;
|
|
5
|
-
export function isExecutable(filePath, platform = process.platform) {
|
|
6
|
-
try {
|
|
7
|
-
fs.accessSync(filePath, platform === 'win32' ? fs.constants.F_OK : fs.constants.X_OK);
|
|
8
|
-
return true;
|
|
9
|
-
}
|
|
10
|
-
catch {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
export function executableNames(name, platform = process.platform) {
|
|
15
|
-
return platform === 'win32' ? [`${name}.cmd`, `${name}.exe`, name] : [name];
|
|
16
|
-
}
|
|
17
|
-
export function versionManagerExecutableCandidates(home, names, options = {}) {
|
|
18
|
-
const env = options.env ?? process.env;
|
|
19
|
-
const platform = options.platform ?? process.platform;
|
|
20
|
-
const readdirSync = options.readdirSync ?? fs.readdirSync;
|
|
21
|
-
const fnmDefaultRoot = env.XDG_DATA_HOME
|
|
22
|
-
? path.join(env.XDG_DATA_HOME, 'fnm')
|
|
23
|
-
: platform === 'darwin'
|
|
24
|
-
? path.join(home, 'Library', 'Application Support', 'fnm')
|
|
25
|
-
: platform === 'win32' && env.APPDATA
|
|
26
|
-
? path.join(env.APPDATA, 'fnm')
|
|
27
|
-
: path.join(home, '.local', 'share', 'fnm');
|
|
28
|
-
const roots = [
|
|
29
|
-
path.join(env.NVM_DIR || path.join(home, '.nvm'), 'versions', 'node'),
|
|
30
|
-
path.join(env.FNM_DIR || fnmDefaultRoot, 'node-versions'),
|
|
31
|
-
path.join(home, '.fnm', 'node-versions'),
|
|
32
|
-
].filter((root, index, all) => all.indexOf(root) === index);
|
|
33
|
-
return roots.flatMap(root => {
|
|
34
|
-
try {
|
|
35
|
-
return readdirSync(root, { withFileTypes: true })
|
|
36
|
-
.filter(entry => entry.isDirectory())
|
|
37
|
-
.sort((left, right) => right.name.localeCompare(left.name, undefined, { numeric: true }))
|
|
38
|
-
.flatMap(entry => names.flatMap(name => [
|
|
39
|
-
path.join(root, entry.name, 'bin', name),
|
|
40
|
-
path.join(root, entry.name, 'installation', 'bin', name),
|
|
41
|
-
]));
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
return [];
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
export function resolveSpawnCommand(executable, args, platform = process.platform, commandInterpreter = process.env.ComSpec) {
|
|
49
|
-
if (platform === 'win32' && /\.(?:cmd|bat)$/i.test(executable)) {
|
|
50
|
-
return {
|
|
51
|
-
command: commandInterpreter || 'cmd.exe',
|
|
52
|
-
args: ['/d', '/s', '/c', executable, ...args],
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
return { command: executable, args };
|
|
56
|
-
}
|
|
57
|
-
export function prependCommandPath(command, env = process.env) {
|
|
58
|
-
if (!path.isAbsolute(command))
|
|
59
|
-
return env;
|
|
60
|
-
return prependPathDirectory(path.dirname(command), env);
|
|
61
|
-
}
|
|
62
|
-
export function prependNodePath(env = process.env, nodeExecutable = process.execPath) {
|
|
63
|
-
return prependPathDirectory(path.dirname(nodeExecutable), env);
|
|
64
|
-
}
|
|
65
|
-
export function prependPathDirectory(directory, env = process.env) {
|
|
66
|
-
const pathKey = Object.keys(env).find(key => key.toLowerCase() === 'path') || 'PATH';
|
|
67
|
-
const inherited = env[pathKey] || '';
|
|
68
|
-
const entries = inherited.split(path.delimiter).filter(Boolean);
|
|
69
|
-
if (entries.includes(directory))
|
|
70
|
-
return env;
|
|
71
|
-
return { ...env, [pathKey]: [directory, ...entries].join(path.delimiter) };
|
|
72
|
-
}
|
|
73
|
-
export function findExecutable(name, options = {}) {
|
|
74
|
-
const env = options.env ?? process.env;
|
|
75
|
-
const platform = options.platform ?? process.platform;
|
|
76
|
-
const existsSync = options.existsSync ?? (candidate => isExecutable(candidate, platform));
|
|
77
|
-
const pathKey = Object.keys(env).find(key => key.toLowerCase() === 'path') || 'PATH';
|
|
78
|
-
const directories = [
|
|
79
|
-
...(options.extraDirectories ?? []),
|
|
80
|
-
...(env[pathKey] ?? '').split(path.delimiter).filter(Boolean),
|
|
81
|
-
];
|
|
82
|
-
for (const directory of directories) {
|
|
83
|
-
for (const executableName of executableNames(name, platform)) {
|
|
84
|
-
const candidate = path.join(directory, executableName);
|
|
85
|
-
if (existsSync(candidate))
|
|
86
|
-
return candidate;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
export const runCommand = (command, args, options = {}) => new Promise((resolve, reject) => {
|
|
92
|
-
const capture = options.capture === true || options.tee === true;
|
|
93
|
-
const launch = resolveSpawnCommand(command, args);
|
|
94
|
-
const child = spawn(launch.command, launch.args, {
|
|
95
|
-
cwd: options.cwd,
|
|
96
|
-
env: options.env,
|
|
97
|
-
stdio: capture ? [options.tee ? 'inherit' : 'ignore', 'pipe', 'pipe'] : 'inherit',
|
|
98
|
-
windowsHide: true,
|
|
99
|
-
});
|
|
100
|
-
let stdout = '';
|
|
101
|
-
let stderr = '';
|
|
102
|
-
let settled = false;
|
|
103
|
-
const append = (current, chunk) => options.tee
|
|
104
|
-
? (current + chunk).slice(-MAX_CAPTURE_LENGTH)
|
|
105
|
-
: current.length >= MAX_CAPTURE_LENGTH
|
|
106
|
-
? current
|
|
107
|
-
: (current + chunk).slice(0, MAX_CAPTURE_LENGTH);
|
|
108
|
-
const finish = (error, code = 1) => {
|
|
109
|
-
if (settled)
|
|
110
|
-
return;
|
|
111
|
-
settled = true;
|
|
112
|
-
if (timer)
|
|
113
|
-
clearTimeout(timer);
|
|
114
|
-
if (error)
|
|
115
|
-
reject(error);
|
|
116
|
-
else
|
|
117
|
-
resolve({ code, stdout, stderr });
|
|
118
|
-
};
|
|
119
|
-
const timer = options.timeoutMs === undefined
|
|
120
|
-
? undefined
|
|
121
|
-
: setTimeout(() => {
|
|
122
|
-
child.kill('SIGTERM');
|
|
123
|
-
finish(new Error(`Command timed out after ${options.timeoutMs}ms`));
|
|
124
|
-
}, options.timeoutMs);
|
|
125
|
-
if (capture) {
|
|
126
|
-
child.stdout?.setEncoding('utf8').on('data', (chunk) => {
|
|
127
|
-
stdout = append(stdout, chunk);
|
|
128
|
-
if (options.tee)
|
|
129
|
-
process.stdout.write(chunk);
|
|
130
|
-
});
|
|
131
|
-
child.stderr?.setEncoding('utf8').on('data', (chunk) => {
|
|
132
|
-
stderr = append(stderr, chunk);
|
|
133
|
-
if (options.tee)
|
|
134
|
-
process.stderr.write(chunk);
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
child.once('error', error => finish(error));
|
|
138
|
-
child.once('close', code => finish(undefined, code ?? 1));
|
|
139
|
-
});
|