@morehao/dsh-plugin-kit 0.1.0 → 0.3.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 +89 -4
- package/dist/exec.d.ts +52 -0
- package/dist/exec.js +171 -0
- package/dist/fs.d.ts +12 -0
- package/dist/fs.js +119 -0
- package/dist/index.d.ts +4 -32
- package/dist/index.js +45 -44
- package/dist/profile-RKpxKDlK.d.ts +117 -0
- package/dist/state.d.ts +32 -0
- package/dist/state.js +186 -0
- package/dist/update-client.d.ts +2 -1
- package/dist/update.d.ts +56 -5
- package/dist/update.js +244 -40
- package/package.json +21 -3
- package/dist/profile-C-eyP5F6.d.ts +0 -44
package/dist/update.js
CHANGED
|
@@ -1,3 +1,170 @@
|
|
|
1
|
+
// src/exec.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
var ENV_KEYS = [
|
|
4
|
+
"PATH",
|
|
5
|
+
"HOME",
|
|
6
|
+
"USER",
|
|
7
|
+
"LOGNAME",
|
|
8
|
+
"SHELL",
|
|
9
|
+
"TMPDIR",
|
|
10
|
+
"TMP",
|
|
11
|
+
"TEMP",
|
|
12
|
+
"LANG",
|
|
13
|
+
"LC_ALL",
|
|
14
|
+
"LC_CTYPE",
|
|
15
|
+
"SSL_CERT_FILE",
|
|
16
|
+
"SSL_CERT_DIR",
|
|
17
|
+
"GIT_AUTHOR_NAME",
|
|
18
|
+
"GIT_AUTHOR_EMAIL",
|
|
19
|
+
"GIT_COMMITTER_NAME",
|
|
20
|
+
"GIT_COMMITTER_EMAIL",
|
|
21
|
+
"GIT_CONFIG_GLOBAL",
|
|
22
|
+
"GIT_SSH_COMMAND"
|
|
23
|
+
];
|
|
24
|
+
var PROXY_KEYS = [
|
|
25
|
+
"HTTP_PROXY",
|
|
26
|
+
"HTTPS_PROXY",
|
|
27
|
+
"NO_PROXY",
|
|
28
|
+
"ALL_PROXY",
|
|
29
|
+
"http_proxy",
|
|
30
|
+
"https_proxy",
|
|
31
|
+
"no_proxy",
|
|
32
|
+
"all_proxy"
|
|
33
|
+
];
|
|
34
|
+
function sanitizedEnv(source = process.env, extra = {}) {
|
|
35
|
+
const env = {};
|
|
36
|
+
for (const key of [...ENV_KEYS, ...PROXY_KEYS]) {
|
|
37
|
+
const value = source[key];
|
|
38
|
+
if (typeof value === "string" && value !== "") env[key] = value;
|
|
39
|
+
}
|
|
40
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
41
|
+
if (typeof value === "string" && value !== "") env[key] = value;
|
|
42
|
+
}
|
|
43
|
+
return env;
|
|
44
|
+
}
|
|
45
|
+
function keepTail(text, limit) {
|
|
46
|
+
if (!Number.isFinite(limit) || limit <= 0 || text.length <= limit) return text;
|
|
47
|
+
return text.slice(text.length - limit);
|
|
48
|
+
}
|
|
49
|
+
async function spawnCollect(command, args = [], options = {}) {
|
|
50
|
+
const started = Date.now();
|
|
51
|
+
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
52
|
+
const abortGraceMs = options.abortGraceMs ?? 2e3;
|
|
53
|
+
const maxStdout = options.maxStdout ?? 1024 * 1024;
|
|
54
|
+
const maxStderr = options.maxStderr ?? 1024 * 1024;
|
|
55
|
+
const maxOutput = options.maxOutput ?? maxStdout + maxStderr;
|
|
56
|
+
let stdout = "";
|
|
57
|
+
let stderr = "";
|
|
58
|
+
let output = "";
|
|
59
|
+
let timedOut = false;
|
|
60
|
+
return new Promise((resolve) => {
|
|
61
|
+
let child;
|
|
62
|
+
try {
|
|
63
|
+
child = spawn(command, args, {
|
|
64
|
+
cwd: options.cwd,
|
|
65
|
+
env: options.env ?? process.env,
|
|
66
|
+
stdio: [options.input === void 0 ? "ignore" : "pipe", "pipe", "pipe"],
|
|
67
|
+
windowsHide: true
|
|
68
|
+
});
|
|
69
|
+
} catch (error) {
|
|
70
|
+
resolve(failureResult(error, started, options.signal?.aborted === true));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (options.input !== void 0) {
|
|
74
|
+
child.stdin?.on("error", () => void 0);
|
|
75
|
+
child.stdin?.end(options.input);
|
|
76
|
+
}
|
|
77
|
+
let settled = false;
|
|
78
|
+
let timer;
|
|
79
|
+
let hardKill;
|
|
80
|
+
const killTree = (signal) => {
|
|
81
|
+
try {
|
|
82
|
+
child.kill(signal);
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const softKill = () => {
|
|
87
|
+
killTree("SIGTERM");
|
|
88
|
+
hardKill = setTimeout(() => killTree("SIGKILL"), abortGraceMs);
|
|
89
|
+
hardKill.unref?.();
|
|
90
|
+
};
|
|
91
|
+
const onAbort = () => softKill();
|
|
92
|
+
const finish = (code, signal, error) => {
|
|
93
|
+
if (settled) return;
|
|
94
|
+
settled = true;
|
|
95
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
96
|
+
if (hardKill !== void 0) clearTimeout(hardKill);
|
|
97
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
98
|
+
resolve({
|
|
99
|
+
code,
|
|
100
|
+
signal,
|
|
101
|
+
stdout,
|
|
102
|
+
stderr,
|
|
103
|
+
output,
|
|
104
|
+
timedOut,
|
|
105
|
+
aborted: options.signal?.aborted === true,
|
|
106
|
+
durationMs: Date.now() - started,
|
|
107
|
+
...error === void 0 ? {} : { error }
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
child.stdout?.setEncoding("utf8");
|
|
111
|
+
child.stdout?.on("data", (chunk) => {
|
|
112
|
+
stdout = keepTail(stdout + chunk, maxStdout);
|
|
113
|
+
output = keepTail(output + chunk, maxOutput);
|
|
114
|
+
try {
|
|
115
|
+
options.onData?.(chunk, "stdout");
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
child.stderr?.setEncoding("utf8");
|
|
120
|
+
child.stderr?.on("data", (chunk) => {
|
|
121
|
+
stderr = keepTail(stderr + chunk, maxStderr);
|
|
122
|
+
output = keepTail(output + chunk, maxOutput);
|
|
123
|
+
try {
|
|
124
|
+
options.onData?.(chunk, "stderr");
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
if (options.signal?.aborted === true) onAbort();
|
|
129
|
+
else options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
130
|
+
if (Number.isFinite(timeoutMs) && timeoutMs > 0) {
|
|
131
|
+
timer = setTimeout(() => {
|
|
132
|
+
timedOut = true;
|
|
133
|
+
softKill();
|
|
134
|
+
}, timeoutMs);
|
|
135
|
+
}
|
|
136
|
+
child.on("error", (error) => finish(null, null, error));
|
|
137
|
+
child.on("close", (code, signalName) => finish(code, signalName));
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
function failureResult(error, started, aborted) {
|
|
141
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
142
|
+
return {
|
|
143
|
+
code: null,
|
|
144
|
+
signal: null,
|
|
145
|
+
stdout: "",
|
|
146
|
+
stderr: "",
|
|
147
|
+
output: err.message,
|
|
148
|
+
timedOut: false,
|
|
149
|
+
aborted,
|
|
150
|
+
durationMs: Date.now() - started,
|
|
151
|
+
error: err
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function collectVersionView(result) {
|
|
155
|
+
if (result.code !== 0) return null;
|
|
156
|
+
const text = result.stdout.trim();
|
|
157
|
+
if (text === "") return null;
|
|
158
|
+
let parsed;
|
|
159
|
+
try {
|
|
160
|
+
parsed = JSON.parse(text);
|
|
161
|
+
} catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
const value = Array.isArray(parsed) ? parsed[parsed.length - 1] : parsed;
|
|
165
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
166
|
+
}
|
|
167
|
+
|
|
1
168
|
// src/registry.ts
|
|
2
169
|
var OFFICIAL_REGISTRY = "https://registry.npmjs.org/";
|
|
3
170
|
var DEFAULT_TIMEOUT_MS = 8e3;
|
|
@@ -38,56 +205,61 @@ function registryPlan(registries) {
|
|
|
38
205
|
if (ordered.length === 0) ordered.push(OFFICIAL_REGISTRY);
|
|
39
206
|
return ordered;
|
|
40
207
|
}
|
|
208
|
+
function planFromHost(hostPlan, registries) {
|
|
209
|
+
if (registries === null || typeof registries !== "object") return null;
|
|
210
|
+
const ordered = [];
|
|
211
|
+
const push = (value) => {
|
|
212
|
+
const normalized = normalizeRegistry(value);
|
|
213
|
+
if (normalized !== null && !ordered.includes(normalized)) ordered.push(normalized);
|
|
214
|
+
};
|
|
215
|
+
let result;
|
|
216
|
+
try {
|
|
217
|
+
result = hostPlan(void 0, registries);
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
if (!Array.isArray(result)) return null;
|
|
222
|
+
for (const entry of result) {
|
|
223
|
+
if (entry === null) {
|
|
224
|
+
push(registries.resolved);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
push(entry);
|
|
228
|
+
}
|
|
229
|
+
return ordered.length === 0 ? null : ordered;
|
|
230
|
+
}
|
|
231
|
+
function resolveRegistryPlan(registries, hostPlan) {
|
|
232
|
+
if (typeof hostPlan === "function") {
|
|
233
|
+
const fromHost = planFromHost(hostPlan, registries);
|
|
234
|
+
if (fromHost !== null) return fromHost;
|
|
235
|
+
}
|
|
236
|
+
return registryPlan(registries);
|
|
237
|
+
}
|
|
41
238
|
function manualCommand(packageName, profileName) {
|
|
42
239
|
const spec = `${packageName}@latest`;
|
|
43
240
|
return typeof profileName === "string" && profileName !== "" ? `dsh plugin --profile ${profileName} add ${spec}` : `dsh plugin add ${spec}`;
|
|
44
241
|
}
|
|
45
242
|
|
|
46
243
|
// src/semver.ts
|
|
47
|
-
|
|
244
|
+
import semver from "semver";
|
|
48
245
|
function parseSemver(value) {
|
|
49
246
|
const text = String(value ?? "").trim();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
function comparePrerelease(left, right) {
|
|
59
|
-
if (left.length === 0 || right.length === 0) {
|
|
60
|
-
return left.length === right.length ? 0 : left.length === 0 ? 1 : -1;
|
|
61
|
-
}
|
|
62
|
-
const length = Math.max(left.length, right.length);
|
|
63
|
-
for (let index = 0; index < length; index += 1) {
|
|
64
|
-
const a = left[index];
|
|
65
|
-
const b = right[index];
|
|
66
|
-
if (a === void 0 || b === void 0) return a === b ? 0 : a === void 0 ? -1 : 1;
|
|
67
|
-
if (a === b) continue;
|
|
68
|
-
const aNumeric = /^\d+$/.test(a);
|
|
69
|
-
const bNumeric = /^\d+$/.test(b);
|
|
70
|
-
if (aNumeric && bNumeric) {
|
|
71
|
-
const an = BigInt(a);
|
|
72
|
-
const bn = BigInt(b);
|
|
73
|
-
if (an !== bn) return an > bn ? 1 : -1;
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (aNumeric !== bNumeric) return aNumeric ? -1 : 1;
|
|
77
|
-
return a > b ? 1 : -1;
|
|
78
|
-
}
|
|
79
|
-
return 0;
|
|
247
|
+
if (text === "") return void 0;
|
|
248
|
+
const parsed = semver.parse(text);
|
|
249
|
+
if (parsed === null) return void 0;
|
|
250
|
+
return {
|
|
251
|
+
core: [parsed.major, parsed.minor, parsed.patch],
|
|
252
|
+
// semver 会把纯数字预发布段解析为 number,这里统一转回字符串以保持旧契约。
|
|
253
|
+
prerelease: parsed.prerelease.map((part) => String(part))
|
|
254
|
+
};
|
|
80
255
|
}
|
|
81
256
|
function isNewerVersion(current, candidate) {
|
|
82
257
|
const a = parseSemver(current);
|
|
83
258
|
const b = parseSemver(candidate);
|
|
84
259
|
if (a === void 0 || b === void 0) return false;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (right !== left) return right > left;
|
|
89
|
-
}
|
|
90
|
-
return comparePrerelease(b.prerelease, a.prerelease) > 0;
|
|
260
|
+
const left = String(current).trim();
|
|
261
|
+
const right = String(candidate).trim();
|
|
262
|
+
return semver.gt(right, left);
|
|
91
263
|
}
|
|
92
264
|
|
|
93
265
|
// src/latest.ts
|
|
@@ -131,7 +303,7 @@ function createLatestResolver(deps) {
|
|
|
131
303
|
}
|
|
132
304
|
}
|
|
133
305
|
async function viaRegistries() {
|
|
134
|
-
const plan =
|
|
306
|
+
const plan = resolveRegistryPlan(await readRegistries(), deps.registryPlan);
|
|
135
307
|
for (const base of plan) {
|
|
136
308
|
try {
|
|
137
309
|
const response = await fetchImpl(`${base}${encodePackageName(deps.meta.name)}/latest`, {
|
|
@@ -273,6 +445,7 @@ function createPluginUpdater(deps) {
|
|
|
273
445
|
...deps.getPluginManager === void 0 ? {} : { getPluginManager: deps.getPluginManager },
|
|
274
446
|
...deps.fetchImpl === void 0 ? {} : { fetchImpl: deps.fetchImpl },
|
|
275
447
|
...deps.runPnpmView === void 0 ? {} : { runPnpmView: deps.runPnpmView },
|
|
448
|
+
...deps.registryPlan === void 0 ? {} : { registryPlan: deps.registryPlan },
|
|
276
449
|
...deps.now === void 0 ? {} : { now: deps.now },
|
|
277
450
|
...deps.cacheTtlMs === void 0 ? {} : { cacheTtlMs: deps.cacheTtlMs },
|
|
278
451
|
...deps.timeoutMs === void 0 ? {} : { timeoutMs: deps.timeoutMs },
|
|
@@ -376,7 +549,7 @@ function createPluginUpdater(deps) {
|
|
|
376
549
|
}
|
|
377
550
|
const service = manager();
|
|
378
551
|
if (service === null || typeof service.installBundle !== "function") {
|
|
379
|
-
throw new PluginUpdateError("NO_MANAGER", "\u5F53\u524D\
|
|
552
|
+
throw new PluginUpdateError("NO_MANAGER", "\u5F53\u524D profile \u672A\u63D0\u4F9B\u4E00\u952E\u5B89\u88C5\u80FD\u529B\uFF08pluginManager.installBundle \u4E0D\u53EF\u7528\uFF09\uFF0C\u8BF7\u624B\u52A8\u66F4\u65B0", {
|
|
380
553
|
manualCommand: view.manualCommand
|
|
381
554
|
});
|
|
382
555
|
}
|
|
@@ -453,7 +626,38 @@ function createPluginUpdater(deps) {
|
|
|
453
626
|
isApplying: () => applying
|
|
454
627
|
};
|
|
455
628
|
}
|
|
629
|
+
function createPnpmViewRunner(deps) {
|
|
630
|
+
const timeoutMs = deps.timeoutMs ?? 8e3;
|
|
631
|
+
const run = deps.exec ?? spawnCollect;
|
|
632
|
+
const env = sanitizedEnv(deps.env ?? process.env);
|
|
633
|
+
return async (spec) => {
|
|
634
|
+
let profile;
|
|
635
|
+
try {
|
|
636
|
+
profile = normalizeProfile(deps.getProfile());
|
|
637
|
+
} catch {
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
if (profile === null || typeof profile.dir !== "string" || profile.dir === "") return null;
|
|
641
|
+
const command = profile.packageManager?.command ?? "pnpm";
|
|
642
|
+
const prefix = profile.packageManager?.args ?? [];
|
|
643
|
+
let result;
|
|
644
|
+
try {
|
|
645
|
+
result = await run(command, [...prefix, "view", spec, "version", "--json"], {
|
|
646
|
+
cwd: profile.dir,
|
|
647
|
+
env,
|
|
648
|
+
timeoutMs,
|
|
649
|
+
maxStdout: 64 * 1024,
|
|
650
|
+
maxStderr: 16 * 1024
|
|
651
|
+
});
|
|
652
|
+
} catch {
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
const version = collectVersionView(result);
|
|
656
|
+
return version !== null && parseSemver(version) !== void 0 ? version : null;
|
|
657
|
+
};
|
|
658
|
+
}
|
|
456
659
|
export {
|
|
457
660
|
PluginUpdateError,
|
|
458
|
-
createPluginUpdater
|
|
661
|
+
createPluginUpdater,
|
|
662
|
+
createPnpmViewRunner
|
|
459
663
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morehao/dsh-plugin-kit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "DeepSeek Harness 插件共用工具包:插件自更新引擎(查 npm 最新版 + 经宿主 pluginManager 重装)、headless
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "DeepSeek Harness 插件共用工具包:插件自更新引擎(查 npm 最新版 + 经宿主 pluginManager 重装)、headless 客户端控制器、HTTP 小工具,以及子进程执行 / 原子写 / JSON 状态持久化等通用底座。零 @deepseek-ai 运行时依赖,宿主能力全部注入。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -22,6 +22,18 @@
|
|
|
22
22
|
"types": "./dist/http.d.ts",
|
|
23
23
|
"default": "./dist/http.js"
|
|
24
24
|
},
|
|
25
|
+
"./exec": {
|
|
26
|
+
"types": "./dist/exec.d.ts",
|
|
27
|
+
"default": "./dist/exec.js"
|
|
28
|
+
},
|
|
29
|
+
"./fs": {
|
|
30
|
+
"types": "./dist/fs.d.ts",
|
|
31
|
+
"default": "./dist/fs.js"
|
|
32
|
+
},
|
|
33
|
+
"./state": {
|
|
34
|
+
"types": "./dist/state.d.ts",
|
|
35
|
+
"default": "./dist/state.js"
|
|
36
|
+
},
|
|
25
37
|
"./package.json": "./package.json"
|
|
26
38
|
},
|
|
27
39
|
"files": [
|
|
@@ -45,10 +57,13 @@
|
|
|
45
57
|
"typecheck": "tsc --noEmit",
|
|
46
58
|
"test": "node --test \"test/*.test.mjs\"",
|
|
47
59
|
"pretest": "npm run build",
|
|
48
|
-
"check": "npm run typecheck && npm run build && npm test --ignore-scripts"
|
|
60
|
+
"check": "npm run typecheck && npm run build && npm test --ignore-scripts",
|
|
61
|
+
"check:release-notes": "node scripts/smoke-release-notes.mjs",
|
|
62
|
+
"prepack": "npm run build"
|
|
49
63
|
},
|
|
50
64
|
"devDependencies": {
|
|
51
65
|
"@types/node": "22.20.5",
|
|
66
|
+
"@types/semver": "^7.8.0",
|
|
52
67
|
"tsup": "8.5.1",
|
|
53
68
|
"typescript": "5.9.3"
|
|
54
69
|
},
|
|
@@ -65,5 +80,8 @@
|
|
|
65
80
|
"publishConfig": {
|
|
66
81
|
"access": "public",
|
|
67
82
|
"registry": "https://registry.npmjs.org/"
|
|
83
|
+
},
|
|
84
|
+
"dependencies": {
|
|
85
|
+
"semver": "^7.8.5"
|
|
68
86
|
}
|
|
69
87
|
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* profile 读取:判断本插件是以什么形态装进当前 profile。
|
|
3
|
-
*
|
|
4
|
-
* 一键更新最危险的场景是「开发用的本地链接」:profile 里写的是 `link:/path/to/repo`,
|
|
5
|
-
* 一旦用 registry 重装,本地的 link 会被换成 npm 版,开发流就被悄悄改掉了。所以
|
|
6
|
-
* 更新前先读 `<profile.dir>/package.json` 的依赖声明,link: / file: / workspace: 一律
|
|
7
|
-
* 关闭一键更新,只给手动命令。
|
|
8
|
-
*/
|
|
9
|
-
type InstallKind = 'registry' | 'link' | 'file' | 'workspace' | 'unknown';
|
|
10
|
-
interface ProfileLike {
|
|
11
|
-
name?: string;
|
|
12
|
-
dir?: string;
|
|
13
|
-
home?: string;
|
|
14
|
-
packageManager?: {
|
|
15
|
-
command?: string;
|
|
16
|
-
args?: string[];
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
interface InstallKindResult {
|
|
20
|
-
kind: InstallKind;
|
|
21
|
-
/** profile package.json 里声明的原始 spec(读不到就是 undefined)。 */
|
|
22
|
-
spec?: string;
|
|
23
|
-
/** 说明来源,便于日志与界面提示。 */
|
|
24
|
-
source?: string;
|
|
25
|
-
}
|
|
26
|
-
/** 把宿主 `profileContext`(任意形状)收敛成只含我们关心的字段;拿不到返回 null。 */
|
|
27
|
-
declare function normalizeProfile(value: unknown): ProfileLike | null;
|
|
28
|
-
/** `dependencies` 里声明的 spec 决定安装形态;`npm:` 别名仍按 registry 处理。 */
|
|
29
|
-
declare function classifyInstallSpec(spec: unknown): InstallKind;
|
|
30
|
-
interface DetectDeps {
|
|
31
|
-
profile: ProfileLike | null;
|
|
32
|
-
packageName: string;
|
|
33
|
-
/** 测试注入;默认读真实文件。 */
|
|
34
|
-
readFile?: (path: string) => Promise<string>;
|
|
35
|
-
readJson?: (path: string) => Promise<unknown>;
|
|
36
|
-
}
|
|
37
|
-
/** 读 profile 的 package.json,返回本插件声明的 spec(找不到返回 undefined)。 */
|
|
38
|
-
declare function readDeclaredSpec(deps: DetectDeps): Promise<{
|
|
39
|
-
spec?: string;
|
|
40
|
-
source?: string;
|
|
41
|
-
}>;
|
|
42
|
-
declare function detectInstallKind(deps: DetectDeps): Promise<InstallKindResult>;
|
|
43
|
-
|
|
44
|
-
export { type DetectDeps as D, type InstallKind as I, type ProfileLike as P, type InstallKindResult as a, classifyInstallSpec as c, detectInstallKind as d, normalizeProfile as n, readDeclaredSpec as r };
|