dsh-opencode-palette 2.0.8 → 2.0.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.
- package/lib/client.js +18 -7
- package/lib/index.js +25 -14
- package/package.json +4 -2
- package/lib/vendor/dsh-plugin-update/client.js +0 -30
- package/lib/vendor/dsh-plugin-update/commands.js +0 -85
- package/lib/vendor/dsh-plugin-update/config.js +0 -102
- package/lib/vendor/dsh-plugin-update/gate.js +0 -120
- package/lib/vendor/dsh-plugin-update/host.js +0 -284
- package/lib/vendor/dsh-plugin-update/ports.js +0 -10
- package/lib/vendor/dsh-plugin-update/reader.js +0 -198
- package/lib/vendor/dsh-plugin-update/service.js +0 -360
- package/lib/vendor/dsh-plugin-update/store.js +0 -351
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
// 派生文件(构建生成,人手不改):由 npm 包 dsh-plugin-update@0.1.1 的 dist/ 原样复制。
|
|
2
|
-
// 为什么必须放进本包:更新包的读取器用 containingPackage(import.meta.url, 目标包名) 从自己的文件位置
|
|
3
|
-
// 往上找目标包;以 npm 依赖形态安装时它住在别的目录,会抛 unknown-profile 且环境判定恒为 installation-changed。
|
|
4
|
-
// 门禁:tests/update-panel.test.mjs 断言本副本与 npm 包逐字节一致(除本头部)。
|
|
5
|
-
// vendor-source: dsh-plugin-update@0.1.1 dist/host.js
|
|
6
|
-
// AUTO-GENERATED by node packages/dsh-plugin-update/build.mjs — DO NOT EDIT. Source: packages/dsh-plugin-update/src/host.ts
|
|
7
|
-
import { realpath } from "node:fs/promises";
|
|
8
|
-
import { homedir } from "node:os";
|
|
9
|
-
import { join, sep } from "node:path";
|
|
10
|
-
import { fileURLToPath } from "node:url";
|
|
11
|
-
import {
|
|
12
|
-
DEFAULT_CONFIRMATION_TTL_MS,
|
|
13
|
-
DEFAULT_CHECK_TIMEOUT_MS,
|
|
14
|
-
DEFAULT_INSTALL_TIMEOUT_MS,
|
|
15
|
-
buildPhoneNames,
|
|
16
|
-
resolveUpdateConfig
|
|
17
|
-
} from "./config.js";
|
|
18
|
-
import { manualCommand } from "./commands.js";
|
|
19
|
-
import { validVersion } from "./service.js";
|
|
20
|
-
import { createUpdateDiskPorts, createUpdateExecutor } from "./store.js";
|
|
21
|
-
import { containingPackage, createUpdateReader, defaultHomeDir } from "./reader.js";
|
|
22
|
-
import { buildPhoneName, buildPhoneNames as buildPhoneNames2, resolveUpdateConfig as resolveUpdateConfig2 } from "./config.js";
|
|
23
|
-
import { parseEventListManifest, checkEventFields, checkEventCounts } from "./gate.js";
|
|
24
|
-
import { containingPackage as containingPackage2, defaultHomeDir as defaultHomeDir2, profileNameValid, registrySpec } from "./reader.js";
|
|
25
|
-
import { createUpdateDiskPorts as createUpdateDiskPorts2, createUpdateExecutor as createUpdateExecutor2, pathsForUpdate, resolveCliEntry } from "./store.js";
|
|
26
|
-
let sharedReader = null;
|
|
27
|
-
let sharedReaderKey = "";
|
|
28
|
-
let sharedDesktopPnpm = null;
|
|
29
|
-
function ctxService(ctx, name) {
|
|
30
|
-
try {
|
|
31
|
-
const get = ctx?.get;
|
|
32
|
-
return typeof get === "function" ? get.call(ctx, name) : void 0;
|
|
33
|
-
} catch {
|
|
34
|
-
return void 0;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function detectEnvironmentKind(ctx) {
|
|
38
|
-
const profiles = ctxService(ctx, "desktopProfiles");
|
|
39
|
-
return profiles === void 0 || profiles === null ? "cli" : "desktop";
|
|
40
|
-
}
|
|
41
|
-
function watchDesktopPnpm(ctx) {
|
|
42
|
-
try {
|
|
43
|
-
if (detectEnvironmentKind(ctx) !== "desktop") return;
|
|
44
|
-
const inject = ctx?.inject;
|
|
45
|
-
if (typeof inject !== "function") return;
|
|
46
|
-
inject.call(ctx, ["desktopPnpm"], (desktopCtx) => {
|
|
47
|
-
try {
|
|
48
|
-
const service = desktopCtx?.desktopPnpm;
|
|
49
|
-
if (service) sharedDesktopPnpm = service;
|
|
50
|
-
} catch {
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
} catch {
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
function hash8(s) {
|
|
57
|
-
try {
|
|
58
|
-
const t = String(s || "");
|
|
59
|
-
let h = 5381;
|
|
60
|
-
for (let i = 0; i < t.length; i++) h = (h << 5) + h + t.charCodeAt(i) >>> 0;
|
|
61
|
-
return `0000000${h.toString(16)}`.slice(-8);
|
|
62
|
-
} catch {
|
|
63
|
-
return "00000000";
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
async function inferProfileDir(loaded, homeDirDefault, targetPackageName) {
|
|
67
|
-
try {
|
|
68
|
-
const dir = loaded && loaded.directory ? String(loaded.directory) : "";
|
|
69
|
-
const marker = `${sep}node_modules${sep}${targetPackageName}`;
|
|
70
|
-
const at = dir.lastIndexOf(marker);
|
|
71
|
-
if (at > 0) {
|
|
72
|
-
const candidate = dir.slice(0, at);
|
|
73
|
-
try {
|
|
74
|
-
return await realpath(candidate);
|
|
75
|
-
} catch {
|
|
76
|
-
return candidate;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
} catch {
|
|
80
|
-
}
|
|
81
|
-
return join(homeDirDefault, "profiles", "web");
|
|
82
|
-
}
|
|
83
|
-
async function getSharedReader(pluginId, config, overrides = {}) {
|
|
84
|
-
const env = overrides.env ?? process.env;
|
|
85
|
-
const osHome = overrides.osHome ?? homedir();
|
|
86
|
-
const homeDirDefault = defaultHomeDir(env, osHome);
|
|
87
|
-
const targetPackageName = overrides.targetPackageName ?? config.targetPackageName;
|
|
88
|
-
const loaded = await containingPackage(fileURLToPath(import.meta.url), targetPackageName).catch(() => null);
|
|
89
|
-
const runningVersion = overrides.runningVersion ?? (loaded && validVersion(loaded.manifest.version) ? String(loaded.manifest.version) : null);
|
|
90
|
-
if (!runningVersion) throw Object.assign(new Error("unknown-profile"), { code: "unknown-profile" });
|
|
91
|
-
const profileDirInput = overrides.profileDir ?? await inferProfileDir(loaded, homeDirDefault, targetPackageName);
|
|
92
|
-
const homeDirInput = overrides.homeDir ?? config.homeDir ?? homeDirDefault;
|
|
93
|
-
const environmentKind = overrides.environmentKind ?? detectEnvironmentKind(overrides.ctx);
|
|
94
|
-
const checkTimeoutMs = overrides.checkTimeoutMs ?? config.checkTimeoutMs;
|
|
95
|
-
const confirmationTtlMs = overrides.confirmationTtlMs ?? config.confirmationTtlMs;
|
|
96
|
-
const installTimeoutMs = overrides.installTimeoutMs ?? config.installTimeoutMs;
|
|
97
|
-
const registryUrl = overrides.registryUrl ?? config.registryUrl;
|
|
98
|
-
const key = `${pluginId}\0${config.prefix}\0${runningVersion}\0${profileDirInput}\0${overrides.profileName ?? ""}\0${homeDirInput}\0${overrides.runInstall ? "exec" : ""}\0${environmentKind}\0${targetPackageName}`;
|
|
99
|
-
if (sharedReader && sharedReaderKey === key) return sharedReader;
|
|
100
|
-
const disk = overrides.readJob && overrides.writeJob ? null : createUpdateDiskPorts(homeDirInput, pluginId, profileDirInput);
|
|
101
|
-
const defaultRun = createUpdateExecutor({
|
|
102
|
-
profileName: overrides.profileName ?? null,
|
|
103
|
-
environmentKind,
|
|
104
|
-
profileDir: profileDirInput,
|
|
105
|
-
subprocess: () => overrides.subprocess ?? ctxService(overrides.ctx, "subprocess"),
|
|
106
|
-
desktopPnpm: () => overrides.desktopPnpm ?? sharedDesktopPnpm,
|
|
107
|
-
desktopProfiles: () => overrides.desktopProfiles ?? ctxService(overrides.ctx, "desktopProfiles"),
|
|
108
|
-
runtimeExecutable: () => overrides.runtimeExecutable,
|
|
109
|
-
runtimeExecArgs: () => overrides.runtimeExecArgs,
|
|
110
|
-
cliEntry: () => overrides.cliEntry,
|
|
111
|
-
targetPackageName,
|
|
112
|
-
registryUrl,
|
|
113
|
-
installTimeoutMs,
|
|
114
|
-
pluginId,
|
|
115
|
-
log: emitInstallLog
|
|
116
|
-
});
|
|
117
|
-
sharedReader = createUpdateReader({
|
|
118
|
-
runningVersion,
|
|
119
|
-
profileDir: profileDirInput,
|
|
120
|
-
pluginId,
|
|
121
|
-
profileName: overrides.profileName ?? void 0,
|
|
122
|
-
homeDir: overrides.homeDir,
|
|
123
|
-
env,
|
|
124
|
-
osHome,
|
|
125
|
-
fetchImpl: overrides.fetchImpl,
|
|
126
|
-
now: overrides.now,
|
|
127
|
-
randomId: overrides.randomId,
|
|
128
|
-
nodeVersion: overrides.nodeVersion,
|
|
129
|
-
environmentKind,
|
|
130
|
-
targetPackageName,
|
|
131
|
-
registryUrl,
|
|
132
|
-
checkTimeoutMs,
|
|
133
|
-
confirmationTtlMs,
|
|
134
|
-
readInstalled: overrides.readInstalled,
|
|
135
|
-
readJob: overrides.readJob,
|
|
136
|
-
writeJob: overrides.writeJob,
|
|
137
|
-
tryAcquireLock: overrides.tryAcquireLock ?? disk?.tryAcquireLock,
|
|
138
|
-
releaseLock: overrides.releaseLock ?? disk?.releaseLock,
|
|
139
|
-
backupJob: overrides.backupJob ?? disk?.backupJob,
|
|
140
|
-
runInstall: overrides.runInstall ?? defaultRun
|
|
141
|
-
});
|
|
142
|
-
sharedReaderKey = key;
|
|
143
|
-
return sharedReader;
|
|
144
|
-
}
|
|
145
|
-
function toUpdateErrorPayload(error) {
|
|
146
|
-
const code = error && typeof error.code === "string" ? String(error.code) : "";
|
|
147
|
-
const known = [
|
|
148
|
-
"check-failed",
|
|
149
|
-
"invalid-release",
|
|
150
|
-
"check-expired",
|
|
151
|
-
"update-busy",
|
|
152
|
-
"install-failed",
|
|
153
|
-
"unknown-profile",
|
|
154
|
-
"source-install",
|
|
155
|
-
"invalid-installation",
|
|
156
|
-
"installation-changed",
|
|
157
|
-
"pending-restart",
|
|
158
|
-
"incompatible-node",
|
|
159
|
-
"registry-conflict",
|
|
160
|
-
"recovery-required"
|
|
161
|
-
];
|
|
162
|
-
if (known.includes(code)) return { error: code, errorKind: code };
|
|
163
|
-
return { error: "check-failed", errorKind: "internal" };
|
|
164
|
-
}
|
|
165
|
-
function manualOfEnv(env, snapshot, config) {
|
|
166
|
-
try {
|
|
167
|
-
return manualCommand({
|
|
168
|
-
profileName: env?.profileName ?? null,
|
|
169
|
-
latestVersion: snapshot?.latestVersion ?? null,
|
|
170
|
-
installedVersion: snapshot?.installedVersion ?? null,
|
|
171
|
-
runningVersion: String(snapshot?.runningVersion ?? ""),
|
|
172
|
-
jobTargetVersion: snapshot?.job?.targetVersion ?? null,
|
|
173
|
-
blockedReason: snapshot?.blockedReason ?? null,
|
|
174
|
-
sourceInstall: env?.sourceInstall === true,
|
|
175
|
-
targetPackageName: config.targetPackageName,
|
|
176
|
-
registryUrl: config.registryUrl
|
|
177
|
-
});
|
|
178
|
-
} catch {
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
async function snapWithManual(reader, snapshot, config) {
|
|
183
|
-
try {
|
|
184
|
-
return { snapshot, manual: manualOfEnv(await reader.readEnv(), snapshot, config) };
|
|
185
|
-
} catch {
|
|
186
|
-
return { snapshot, manual: null };
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
let phoneLogCtx = null;
|
|
190
|
-
function emitInstallLog(level, event, fields) {
|
|
191
|
-
try {
|
|
192
|
-
if (phoneLogCtx && typeof phoneLogCtx.fire === "function") phoneLogCtx.fire(level, event, fields);
|
|
193
|
-
} catch {
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
function loggedPhone(method, kind, pluginId, fn) {
|
|
197
|
-
return async function(args) {
|
|
198
|
-
const t0 = Date.now();
|
|
199
|
-
const emit = (level, event, fields) => {
|
|
200
|
-
try {
|
|
201
|
-
if (phoneLogCtx && typeof phoneLogCtx.fire === "function") phoneLogCtx.fire(level, event, fields);
|
|
202
|
-
} catch {
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
try {
|
|
206
|
-
const out = await fn(args);
|
|
207
|
-
const snapshot = out && out.snapshot ? out.snapshot : out;
|
|
208
|
-
emit("info", "host.call", { method, latencyMs: Date.now() - t0, ok: true, kind, pluginId });
|
|
209
|
-
return { ok: true, snapshot, manual: out && out.snapshot ? out.manual ?? null : null, receipt: out && out.receipt ? out.receipt : null };
|
|
210
|
-
} catch (error) {
|
|
211
|
-
const payload = toUpdateErrorPayload(error);
|
|
212
|
-
emit("warn", "host.call.fail", { method, kind, errorHash: hash8(String(error?.message || payload.error)), pluginId });
|
|
213
|
-
return { ok: false, ...payload };
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
function createHostUpdate(deps = {}, configInput) {
|
|
218
|
-
const config = resolveUpdateConfig(configInput);
|
|
219
|
-
const pluginId = config.pluginId;
|
|
220
|
-
phoneLogCtx = deps.logCtx ?? phoneLogCtx;
|
|
221
|
-
const ctx = deps.ctx ?? null;
|
|
222
|
-
if (deps.desktopPnpm) sharedDesktopPnpm = deps.desktopPnpm;
|
|
223
|
-
else watchDesktopPnpm(ctx);
|
|
224
|
-
const readerOverrides = { ...deps.readerOverrides ?? {}, ctx };
|
|
225
|
-
const phoneNames = buildPhoneNames(config.prefix);
|
|
226
|
-
async function readStatus(args) {
|
|
227
|
-
const reader = await getSharedReader(pluginId, config, {
|
|
228
|
-
...readerOverrides,
|
|
229
|
-
profileDir: args && args.profileDir ? String(args.profileDir) : readerOverrides.profileDir
|
|
230
|
-
});
|
|
231
|
-
return snapWithManual(reader, await reader.status(), config);
|
|
232
|
-
}
|
|
233
|
-
async function readCheck(args) {
|
|
234
|
-
const reader = await getSharedReader(pluginId, config, {
|
|
235
|
-
...readerOverrides,
|
|
236
|
-
profileDir: args && args.profileDir ? String(args.profileDir) : readerOverrides.profileDir
|
|
237
|
-
});
|
|
238
|
-
const result = await reader.check();
|
|
239
|
-
const withManual = await snapWithManual(reader, result.snapshot, config);
|
|
240
|
-
return { snapshot: withManual.snapshot, manual: withManual.manual, receipt: result.receipt ?? null };
|
|
241
|
-
}
|
|
242
|
-
async function runInstall(args) {
|
|
243
|
-
const checkId = args && typeof args.checkId === "string" ? args.checkId : "";
|
|
244
|
-
const requestId = args && typeof args.requestId === "string" ? args.requestId : "";
|
|
245
|
-
const reader = await getSharedReader(pluginId, config, {
|
|
246
|
-
...readerOverrides,
|
|
247
|
-
profileDir: args && args.profileDir ? String(args.profileDir) : readerOverrides.profileDir
|
|
248
|
-
});
|
|
249
|
-
return snapWithManual(reader, await reader.install({ checkId, requestId }), config);
|
|
250
|
-
}
|
|
251
|
-
const handlers = {
|
|
252
|
-
[phoneNames.updateStatus]: loggedPhone(phoneNames.updateStatus, "update-status", pluginId, readStatus),
|
|
253
|
-
[phoneNames.updateCheck]: loggedPhone(phoneNames.updateCheck, "update-check", pluginId, readCheck),
|
|
254
|
-
[phoneNames.updateInstall]: loggedPhone(phoneNames.updateInstall, "update-install", pluginId, runInstall)
|
|
255
|
-
};
|
|
256
|
-
return { phoneNames, handlers };
|
|
257
|
-
}
|
|
258
|
-
void DEFAULT_CHECK_TIMEOUT_MS;
|
|
259
|
-
void DEFAULT_CONFIRMATION_TTL_MS;
|
|
260
|
-
void DEFAULT_INSTALL_TIMEOUT_MS;
|
|
261
|
-
function __resetSharedUpdateReaderForTests() {
|
|
262
|
-
sharedReader = null;
|
|
263
|
-
sharedReaderKey = "";
|
|
264
|
-
sharedDesktopPnpm = null;
|
|
265
|
-
}
|
|
266
|
-
export {
|
|
267
|
-
__resetSharedUpdateReaderForTests,
|
|
268
|
-
buildPhoneName,
|
|
269
|
-
buildPhoneNames2 as buildPhoneNames,
|
|
270
|
-
checkEventCounts,
|
|
271
|
-
checkEventFields,
|
|
272
|
-
containingPackage2 as containingPackage,
|
|
273
|
-
createHostUpdate,
|
|
274
|
-
createUpdateDiskPorts2 as createUpdateDiskPorts,
|
|
275
|
-
createUpdateExecutor2 as createUpdateExecutor,
|
|
276
|
-
defaultHomeDir2 as defaultHomeDir,
|
|
277
|
-
detectEnvironmentKind,
|
|
278
|
-
parseEventListManifest,
|
|
279
|
-
pathsForUpdate,
|
|
280
|
-
profileNameValid,
|
|
281
|
-
registrySpec,
|
|
282
|
-
resolveCliEntry,
|
|
283
|
-
resolveUpdateConfig2 as resolveUpdateConfig
|
|
284
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// 派生文件(构建生成,人手不改):由 npm 包 dsh-plugin-update@0.1.1 的 dist/ 原样复制。
|
|
2
|
-
// 为什么必须放进本包:更新包的读取器用 containingPackage(import.meta.url, 目标包名) 从自己的文件位置
|
|
3
|
-
// 往上找目标包;以 npm 依赖形态安装时它住在别的目录,会抛 unknown-profile 且环境判定恒为 installation-changed。
|
|
4
|
-
// 门禁:tests/update-panel.test.mjs 断言本副本与 npm 包逐字节一致(除本头部)。
|
|
5
|
-
// vendor-source: dsh-plugin-update@0.1.1 dist/ports.js
|
|
6
|
-
// AUTO-GENERATED by node packages/dsh-plugin-update/build.mjs — DO NOT EDIT. Source: packages/dsh-plugin-update/src/ports.ts
|
|
7
|
-
const PORTS_SOURCE = "packages/dsh-plugin-update/src/ports.ts";
|
|
8
|
-
export {
|
|
9
|
-
PORTS_SOURCE
|
|
10
|
-
};
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
// 派生文件(构建生成,人手不改):由 npm 包 dsh-plugin-update@0.1.1 的 dist/ 原样复制。
|
|
2
|
-
// 为什么必须放进本包:更新包的读取器用 containingPackage(import.meta.url, 目标包名) 从自己的文件位置
|
|
3
|
-
// 往上找目标包;以 npm 依赖形态安装时它住在别的目录,会抛 unknown-profile 且环境判定恒为 installation-changed。
|
|
4
|
-
// 门禁:tests/update-panel.test.mjs 断言本副本与 npm 包逐字节一致(除本头部)。
|
|
5
|
-
// vendor-source: dsh-plugin-update@0.1.1 dist/reader.js
|
|
6
|
-
// AUTO-GENERATED by node packages/dsh-plugin-update/build.mjs — DO NOT EDIT. Source: packages/dsh-plugin-update/src/reader.ts
|
|
7
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
8
|
-
import { readFile, realpath, stat } from "node:fs/promises";
|
|
9
|
-
import { homedir } from "node:os";
|
|
10
|
-
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
12
|
-
import { assertPluginId } from "./config.js";
|
|
13
|
-
import { createUpdateCore, validVersion } from "./service.js";
|
|
14
|
-
const LOCK_FILES = ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package-lock.json"];
|
|
15
|
-
function inside(directory, filename) {
|
|
16
|
-
const suffix = relative(directory, filename);
|
|
17
|
-
return suffix !== ".." && !suffix.startsWith(`..${sep}`) && !isAbsolute(suffix);
|
|
18
|
-
}
|
|
19
|
-
async function readOptional(filename) {
|
|
20
|
-
try {
|
|
21
|
-
return await readFile(filename, "utf8");
|
|
22
|
-
} catch (error) {
|
|
23
|
-
if (error && error.code === "ENOENT") return "";
|
|
24
|
-
throw error;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
async function packageAt(directory) {
|
|
28
|
-
const contents = await readFile(join(directory, "package.json"), "utf8");
|
|
29
|
-
return { directory: await realpath(directory), manifest: JSON.parse(contents), contents };
|
|
30
|
-
}
|
|
31
|
-
async function containingPackage(filename, name) {
|
|
32
|
-
let directory = dirname(await realpath(filename));
|
|
33
|
-
while (true) {
|
|
34
|
-
try {
|
|
35
|
-
const found = await packageAt(directory);
|
|
36
|
-
if (found.manifest && found.manifest.name === name) return found;
|
|
37
|
-
} catch (error) {
|
|
38
|
-
if (!error || error.code !== "ENOENT") throw error;
|
|
39
|
-
}
|
|
40
|
-
const parent = dirname(directory);
|
|
41
|
-
if (parent === directory) return null;
|
|
42
|
-
directory = parent;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function profileNameValid(name) {
|
|
46
|
-
return typeof name === "string" && name.length > 0 && Buffer.byteLength(name) <= 255 && !name.startsWith("-") && ![".", "..", "node_modules"].includes(name) && !/[\\/\x00-\x1f\x7f<>:"|?*]/u.test(name);
|
|
47
|
-
}
|
|
48
|
-
function registrySpec(spec) {
|
|
49
|
-
return typeof spec === "string" && spec.trim().length > 0 && (validVersion(spec.trim()) || /^[~^>=< ]*[0-9x*][0-9x*./\-_ |~^>=<]*$/u.test(spec.trim()) || /^[A-Za-z][A-Za-z0-9._-]*$/u.test(spec.trim()));
|
|
50
|
-
}
|
|
51
|
-
async function validPackage(pkg, targetName) {
|
|
52
|
-
if (!pkg || pkg.manifest?.name !== targetName || !validVersion(pkg.manifest.version)) return false;
|
|
53
|
-
const main = pkg.manifest.main;
|
|
54
|
-
const exportsField = pkg.manifest.exports;
|
|
55
|
-
const dshField = pkg.manifest.dsh;
|
|
56
|
-
const bundle = dshField?.bundle;
|
|
57
|
-
const entries = [main, exportsField?.["./client"], bundle?.patch];
|
|
58
|
-
for (const entry of entries) {
|
|
59
|
-
if (typeof entry !== "string" || !entry || isAbsolute(entry) || entry.includes("\0")) return false;
|
|
60
|
-
const filename = resolve(pkg.directory, entry);
|
|
61
|
-
if (!inside(pkg.directory, filename)) return false;
|
|
62
|
-
let target = filename;
|
|
63
|
-
try {
|
|
64
|
-
target = await realpath(filename);
|
|
65
|
-
} catch {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
if (!inside(pkg.directory, target)) return false;
|
|
69
|
-
try {
|
|
70
|
-
if (!(await stat(target)).isFile()) return false;
|
|
71
|
-
} catch {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
function defaultHomeDir(env, osHome) {
|
|
78
|
-
let selected = env && env.DSH_HOME && env.DSH_HOME.trim() ? env.DSH_HOME : join(osHome, ".dsh");
|
|
79
|
-
if (selected === "~") selected = osHome;
|
|
80
|
-
else if (/^~[\\/]/u.test(selected)) selected = join(osHome, selected.slice(2));
|
|
81
|
-
return resolve(selected);
|
|
82
|
-
}
|
|
83
|
-
function createUpdateReader(options) {
|
|
84
|
-
const runningVersion = options.runningVersion;
|
|
85
|
-
if (typeof runningVersion !== "string" || !runningVersion) {
|
|
86
|
-
throw new Error("[dsh-plugin-update] \u5EFA\u8BFB\u53D6\u5668\u5FC5\u987B\u7ED9\u8FD0\u884C\u7248\u672C runningVersion\uFF08\u8BFB\u5BBF\u4E3B\u81EA\u5E26\u6E05\u5355\uFF09");
|
|
87
|
-
}
|
|
88
|
-
const profileDirInput = options.profileDir;
|
|
89
|
-
if (typeof profileDirInput !== "string" || !isAbsolute(profileDirInput)) {
|
|
90
|
-
throw new Error("[dsh-plugin-update] \u5EFA\u8BFB\u53D6\u5668\u5FC5\u987B\u7ED9\u7EDD\u5BF9\u8DEF\u5F84\u7684\u4F7F\u7528\u8303\u56F4\u76EE\u5F55 profileDir");
|
|
91
|
-
}
|
|
92
|
-
const pluginId = assertPluginId(options.pluginId);
|
|
93
|
-
const env = options.env ?? process.env;
|
|
94
|
-
const osHome = options.osHome ?? homedir();
|
|
95
|
-
const homeDirDefault = defaultHomeDir(env, osHome);
|
|
96
|
-
const fallbackFetch = globalThis.fetch;
|
|
97
|
-
if (!options.fetchImpl && !fallbackFetch) {
|
|
98
|
-
throw new Error("[dsh-plugin-update] \u5EFA\u8BFB\u53D6\u5668\u5FC5\u987B\u7ED9\u7F51\u7EDC\u5B9E\u73B0 fetchImpl\uFF08\u672C\u673A\u6CA1\u6709\u5168\u5C40 fetch\uFF09");
|
|
99
|
-
}
|
|
100
|
-
const fetchImpl = options.fetchImpl ?? fallbackFetch;
|
|
101
|
-
const now = options.now ?? Date.now;
|
|
102
|
-
const randomId = options.randomId ?? randomUUID;
|
|
103
|
-
const nodeVersion = options.nodeVersion ?? (typeof process !== "undefined" ? process.versions.node : "");
|
|
104
|
-
const environmentKind = options.environmentKind ?? "cli";
|
|
105
|
-
const targetPackageName = options.targetPackageName ?? "dsh-mattpocock-skills-deck";
|
|
106
|
-
const registryUrl = options.registryUrl ?? "https://registry.npmjs.org/";
|
|
107
|
-
const loadedPackage = containingPackage(fileURLToPath(import.meta.url), targetPackageName).catch(() => null);
|
|
108
|
-
let boundIdentity;
|
|
109
|
-
async function readInstalledReal() {
|
|
110
|
-
const result = {
|
|
111
|
-
profileName: null,
|
|
112
|
-
environmentKind,
|
|
113
|
-
homeDir: null,
|
|
114
|
-
profileDir: null,
|
|
115
|
-
installedVersion: null,
|
|
116
|
-
packageValid: false,
|
|
117
|
-
sourceInstall: false,
|
|
118
|
-
blockedReason: null,
|
|
119
|
-
installationKey: null,
|
|
120
|
-
eligible: false
|
|
121
|
-
};
|
|
122
|
-
const profileName = options.profileName ?? basename(profileDirInput);
|
|
123
|
-
result.profileName = profileName;
|
|
124
|
-
if (!profileNameValid(profileName)) {
|
|
125
|
-
result.blockedReason = "unknown-profile";
|
|
126
|
-
return result;
|
|
127
|
-
}
|
|
128
|
-
let homeDir;
|
|
129
|
-
let profileDir;
|
|
130
|
-
try {
|
|
131
|
-
homeDir = await realpath(options.homeDir ?? homeDirDefault);
|
|
132
|
-
profileDir = await realpath(profileDirInput);
|
|
133
|
-
} catch {
|
|
134
|
-
result.blockedReason = "unknown-profile";
|
|
135
|
-
return result;
|
|
136
|
-
}
|
|
137
|
-
result.homeDir = homeDir;
|
|
138
|
-
result.profileDir = profileDir;
|
|
139
|
-
let profile;
|
|
140
|
-
let installed;
|
|
141
|
-
try {
|
|
142
|
-
profile = await packageAt(profileDir);
|
|
143
|
-
installed = await packageAt(join(profileDir, "node_modules", targetPackageName));
|
|
144
|
-
} catch {
|
|
145
|
-
result.blockedReason = "invalid-installation";
|
|
146
|
-
return result;
|
|
147
|
-
}
|
|
148
|
-
const deps = profile.manifest.dependencies ?? {};
|
|
149
|
-
result.sourceInstall = !registrySpec(deps[targetPackageName]) || !inside(join(profileDir, "node_modules"), installed.directory);
|
|
150
|
-
result.installedVersion = typeof installed.manifest.version === "string" ? installed.manifest.version : null;
|
|
151
|
-
result.packageValid = await validPackage(installed, targetPackageName);
|
|
152
|
-
const loaded = await loadedPackage;
|
|
153
|
-
const identity = `${homeDir}\0${profileDir}\0${profileName}\0${pluginId}`;
|
|
154
|
-
const sameLoadedPackage = loaded?.directory === installed.directory && loaded?.manifest.version === installed.manifest.version;
|
|
155
|
-
if (boundIdentity === void 0 && sameLoadedPackage && result.packageValid) boundIdentity = identity;
|
|
156
|
-
const stateFiles = await Promise.all(LOCK_FILES.map((name) => readOptional(join(profileDir, name))));
|
|
157
|
-
try {
|
|
158
|
-
result.installationKey = createHash("sha256").update(JSON.stringify([identity, profile.contents, installed.directory, installed.contents, ...stateFiles])).digest("hex");
|
|
159
|
-
} catch {
|
|
160
|
-
result.installationKey = null;
|
|
161
|
-
}
|
|
162
|
-
if (boundIdentity !== void 0 && boundIdentity !== identity) result.blockedReason = "installation-changed";
|
|
163
|
-
else if (!sameLoadedPackage && boundIdentity === void 0) result.blockedReason = "installation-changed";
|
|
164
|
-
else if (!result.packageValid) result.blockedReason = "invalid-installation";
|
|
165
|
-
else if (result.sourceInstall) result.blockedReason = "source-install";
|
|
166
|
-
else if (result.installedVersion !== runningVersion) result.blockedReason = "pending-restart";
|
|
167
|
-
result.eligible = !result.blockedReason;
|
|
168
|
-
result.blockedReason = result.blockedReason ?? null;
|
|
169
|
-
return result;
|
|
170
|
-
}
|
|
171
|
-
const readInstalled = options.readInstalled ?? readInstalledReal;
|
|
172
|
-
const core = createUpdateCore({
|
|
173
|
-
readRunningVersion: () => runningVersion,
|
|
174
|
-
readInstalled,
|
|
175
|
-
fetchImpl,
|
|
176
|
-
now,
|
|
177
|
-
randomId,
|
|
178
|
-
nodeVersion,
|
|
179
|
-
checkTimeoutMs: options.checkTimeoutMs,
|
|
180
|
-
confirmationTtlMs: options.confirmationTtlMs,
|
|
181
|
-
targetPackageName,
|
|
182
|
-
registryUrl,
|
|
183
|
-
readJob: options.readJob,
|
|
184
|
-
writeJob: options.writeJob,
|
|
185
|
-
tryAcquireLock: options.tryAcquireLock,
|
|
186
|
-
releaseLock: options.releaseLock,
|
|
187
|
-
backupJob: options.backupJob,
|
|
188
|
-
runInstall: options.runInstall
|
|
189
|
-
});
|
|
190
|
-
return Object.assign(core, { readEnv: readInstalled });
|
|
191
|
-
}
|
|
192
|
-
export {
|
|
193
|
-
containingPackage,
|
|
194
|
-
createUpdateReader,
|
|
195
|
-
defaultHomeDir,
|
|
196
|
-
profileNameValid,
|
|
197
|
-
registrySpec
|
|
198
|
-
};
|