@crazx/dsh 0.2.1-rc.2.zw.1 → 0.2.1-rc.2.zw.2
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/bin.js +181 -75
- package/lib/{dump-config-D-jtgwY3.js → dump-config-CWs_HdOc.js} +18 -6
- package/lib/dump-config-CxNkSYcK.js +2 -0
- package/lib/dump-config-schema-D_PxIzpe.js +41 -0
- package/lib/plugin-BiveYA7h.js +101 -0
- package/lib/profile-boot-C14i-8IN.js +329 -0
- package/lib/profile-boot.js +2 -0
- package/package.json +5 -5
- package/lib/plugin-9h8shc4d.js +0 -129
- package/lib/profile-boot-BnJoK_kl.js +0 -2
- package/lib/profile-boot-DG5t9aNs.js +0 -283
- package/lib/web-log-CKn_UFWp.js +0 -83
- package/lib/{web-log-DZLBFfre.js → web-log-DVrp_oFP.js} +1 -1
package/lib/bin.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { StartupError, getDshRuntimeVersion, loadLayeredEnv } from "@deepseek-ai/dsh-app-boot";
|
|
3
|
+
import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
|
|
4
|
+
import { Command, CommanderError, InvalidArgumentError } from "commander";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { inspect } from "node:util";
|
|
6
9
|
//#region lib/types/args.js
|
|
7
10
|
/**
|
|
8
11
|
* Commander adapter for the `dsh` command line.
|
|
@@ -16,9 +19,9 @@ import { Command, CommanderError } from "commander";
|
|
|
16
19
|
* `dsh --profile tui --resume abc` boots the tui profile with `--resume abc`,
|
|
17
20
|
* and `dsh --profile web -h` prints the web app's help, not this one's.
|
|
18
21
|
*
|
|
19
|
-
* `
|
|
20
|
-
* plugin dependencies by forwarding to pnpm.
|
|
21
|
-
* `web:log:tmp`, which boot the web profile while teeing output to a log file.
|
|
22
|
+
* `dsh <name>` abbreviates `dsh --profile <name>`; `plugin` manages a profile's
|
|
23
|
+
* plugin dependencies by forwarding to pnpm.
|
|
24
|
+
* The fork adds `web:log` / `web:log:tmp`, which boot the web profile while teeing output to a log file.
|
|
22
25
|
* @module @deepseek-ai/dsh/args
|
|
23
26
|
*/
|
|
24
27
|
/**
|
|
@@ -26,21 +29,30 @@ import { Command, CommanderError } from "commander";
|
|
|
26
29
|
* variadic — a variadic `--patch` would swallow the inner arguments.
|
|
27
30
|
*/
|
|
28
31
|
const collect = (value, previous = []) => [...previous, value];
|
|
32
|
+
function selectProfile(value, previous) {
|
|
33
|
+
if (previous !== void 0) throw new InvalidArgumentError("select a profile only once");
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
function rejectElectronProfile(program, profile) {
|
|
37
|
+
if (profile.toLowerCase() === "desktop") program.error("error: profile \"desktop\" is managed exclusively by the Electron application");
|
|
38
|
+
}
|
|
29
39
|
/** The launcher's own help text; each app prints its own. */
|
|
30
40
|
const HELP_EXAMPLES = `
|
|
31
41
|
Examples:
|
|
32
|
-
dsh
|
|
33
|
-
dsh --profile
|
|
34
|
-
|
|
35
|
-
dsh
|
|
36
|
-
dsh --
|
|
37
|
-
dsh
|
|
42
|
+
dsh web boot the web profile (same as: dsh --profile web)
|
|
43
|
+
dsh rescue --from-default-profile web
|
|
44
|
+
recreate rescue from the shipped web template, then boot it
|
|
45
|
+
dsh headless "run the tests" answer one task, print the result, and exit
|
|
46
|
+
dsh tui --patch ./extra.yml boot a custom profile with one extra overlay
|
|
47
|
+
dsh tui --resume <session> arguments after the launcher flags reach the app
|
|
48
|
+
dsh web --help the web app's own flags and help
|
|
49
|
+
dsh plugin --profile tui add <package> install a plugin into the tui profile
|
|
38
50
|
dsh web:log --port 0 boot web with output teed to a timestamped log file (fork-local)
|
|
39
51
|
`;
|
|
40
52
|
/**
|
|
41
53
|
* Resolve a boot or dump invocation from the launcher flags and the leftover
|
|
42
54
|
* inner arguments.
|
|
43
|
-
* @param program - the command whose options were parsed
|
|
55
|
+
* @param program - the command whose options were parsed.
|
|
44
56
|
* @param profile - the profile these flags boot.
|
|
45
57
|
* @param options - the launcher flags commander collected.
|
|
46
58
|
* @param args - the leftover arguments, in argv order.
|
|
@@ -49,19 +61,33 @@ Examples:
|
|
|
49
61
|
function resolveBoot(program, profile, options, args) {
|
|
50
62
|
const patches = options.patch ?? [];
|
|
51
63
|
if (patches.includes("")) program.error("error: --patch needs a path");
|
|
52
|
-
if (options.
|
|
64
|
+
if (options.fromDefaultProfile === "") program.error("error: --from-default-profile needs a name");
|
|
65
|
+
const dumps = [
|
|
66
|
+
options.dumpConfig,
|
|
67
|
+
options.dumpDefaultConfig,
|
|
68
|
+
options.dumpConfigSchema
|
|
69
|
+
].filter(Boolean);
|
|
70
|
+
if (dumps.length === 0) return {
|
|
53
71
|
mode: "profile",
|
|
54
72
|
profile,
|
|
73
|
+
fromDefaultProfile: options.fromDefaultProfile,
|
|
55
74
|
patches,
|
|
56
75
|
args
|
|
57
76
|
};
|
|
58
|
-
if (
|
|
77
|
+
if (dumps.length > 1) program.error("error: --dump-config, --dump-default-config, and --dump-config-schema are mutually exclusive");
|
|
59
78
|
if (args.length > 0) program.error(`error: config dumps take no app arguments, got ${args.map((argument) => JSON.stringify(argument)).join(" ")}`);
|
|
79
|
+
if (options.dumpConfigSchema === true) return {
|
|
80
|
+
mode: "dump-config-schema",
|
|
81
|
+
profile,
|
|
82
|
+
fromDefaultProfile: options.fromDefaultProfile,
|
|
83
|
+
patches
|
|
84
|
+
};
|
|
60
85
|
const defaultOnly = options.dumpDefaultConfig === true;
|
|
61
86
|
if (defaultOnly && patches.length > 0) program.error("error: --dump-default-config prints the bundle layers and takes no --patch");
|
|
62
87
|
return {
|
|
63
88
|
mode: "dump-config",
|
|
64
89
|
profile,
|
|
90
|
+
fromDefaultProfile: options.fromDefaultProfile,
|
|
65
91
|
defaultOnly,
|
|
66
92
|
patches
|
|
67
93
|
};
|
|
@@ -71,30 +97,28 @@ function resolveBoot(program, profile, options, args) {
|
|
|
71
97
|
* error.
|
|
72
98
|
* @param argv - arguments after the Node binary and script.
|
|
73
99
|
* @param version - version string printed by `--version`.
|
|
100
|
+
* @param manageDesktopProfile - permit Desktop's installed carrier to manage its reserved profile's plugins.
|
|
74
101
|
* @returns the resolved invocation.
|
|
75
102
|
*/
|
|
76
|
-
function parseDshArgs(argv, version) {
|
|
103
|
+
function parseDshArgs(argv, version, manageDesktopProfile = false) {
|
|
104
|
+
const first = argv[0];
|
|
77
105
|
let resolved;
|
|
78
106
|
const program = new Command();
|
|
79
|
-
program.name("dsh").version(version, "-V, --version", "output the version number").description("dsh: boot a DeepSeek Harness profile — an ordered stack of plugin-bundle patch layers under your own overrides.").addHelpText("after", HELP_EXAMPLES).exitOverride().helpOption(false).allowUnknownOption().passThroughOptions().enablePositionalOptions().argument("[args...]", "arguments for the booted profile's app (see: dsh --profile <name> --help)").option("--profile <name>", "the profile under $DSH_HOME/profiles to boot").option("--patch <path>", "extra patch-list overlay applied after the profile layer (repeatable)", collect).option("--dump-config", "print the composed profile tree and exit").option("--dump-default-config", "print the profile tree without its user layer or --patch overlays and exit").action((args, options) => {
|
|
107
|
+
program.name("dsh").version(version, "-V, --version", "output the version number").usage("[--profile] <name> [options] [app-args...]\n dsh plugin --profile <name> <pnpm-args...>").description("dsh: boot a DeepSeek Harness profile — an ordered stack of plugin-bundle patch layers under your own overrides.").addHelpText("after", HELP_EXAMPLES).exitOverride().helpOption(false).helpCommand(false).allowUnknownOption().passThroughOptions().enablePositionalOptions().argument("[args...]", "arguments for the booted profile's app (see: dsh --profile <name> --help)").option("--profile <name>", "the profile under $DSH_HOME/profiles to boot", selectProfile).option("--from-default-profile <name>", "replace a custom profile with a fresh copy of a shipped profile template").option("--patch <path>", "extra patch-list overlay applied after the profile layer (repeatable)", collect).option("--dump-config", "print the composed profile tree and exit").option("--dump-config-schema", "print JSON Schema for profile entries and patches without mounting").option("--dump-default-config", "print the profile tree without its user layer or --patch overlays and exit").action((args, options) => {
|
|
80
108
|
if (options.profile === void 0) {
|
|
81
109
|
if (args.some((argument) => argument === "-h" || argument === "--help")) program.help();
|
|
82
110
|
program.error("error: --profile <name> is required");
|
|
83
111
|
}
|
|
84
112
|
const profile = options.profile;
|
|
85
113
|
if (profile === "") program.error("error: --profile needs a name");
|
|
114
|
+
rejectElectronProfile(program, profile);
|
|
86
115
|
resolved = resolveBoot(program, profile, options, args);
|
|
87
116
|
});
|
|
88
117
|
/** Reject parent options supplied before a subcommand. */
|
|
89
118
|
const rejectParentOptions = (command) => {
|
|
90
119
|
const parent = program.opts();
|
|
91
|
-
if (parent.profile !== void 0 || parent.patch !== void 0 || parent.dumpConfig !== void 0 || parent.dumpDefaultConfig !== void 0) program.error(`error: ${command} takes none of parent --profile, --patch, --dump-config, or --dump-default-config`);
|
|
120
|
+
if (parent.profile !== void 0 || parent.patch !== void 0 || parent.dumpConfig !== void 0 || parent.dumpDefaultConfig !== void 0 || parent.fromDefaultProfile !== void 0) program.error(`error: ${command} takes none of parent --profile, --from-default-profile, --patch, --dump-config, or --dump-default-config`);
|
|
92
121
|
};
|
|
93
|
-
const web = program.command("web").description("boot the web profile (alias of --profile web); the web app's own flags follow");
|
|
94
|
-
web.helpOption(false).allowUnknownOption().passThroughOptions().enablePositionalOptions().argument("[args...]", "arguments for the web app (see: dsh web --help)").option("--patch <path>", "extra patch-list overlay applied after the profile layer (repeatable)", collect).option("--dump-config", "print the composed web-profile tree (with the user layer and any --patch) and exit").option("--dump-default-config", "print the web profile's bundle layers (no user layer) and exit").action((args, options) => {
|
|
95
|
-
rejectParentOptions("web");
|
|
96
|
-
resolved = resolveBoot(web, "web", options, args);
|
|
97
|
-
});
|
|
98
122
|
/**
|
|
99
123
|
* Fork-local logged web boots: `dsh web:log` tees the web app's output to a
|
|
100
124
|
* timestamped file under $DSH_HOME/logs, `dsh web:log:tmp` under the OS temp
|
|
@@ -115,18 +139,23 @@ function parseDshArgs(argv, version) {
|
|
|
115
139
|
};
|
|
116
140
|
registerWebLog("web:log", false);
|
|
117
141
|
registerWebLog("web:log:tmp", true);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
142
|
+
if (first === "plugin") {
|
|
143
|
+
const plugin = program.command("plugin").description("manage a profile's plugins by forwarding the remaining arguments to pnpm in the profile directory");
|
|
144
|
+
plugin.helpOption("-h, --help", "show this help").passThroughOptions().requiredOption("--profile <name>", "the profile whose plugins to manage (initialized on first use)", selectProfile).allowUnknownOption().argument("[args...]", "pnpm arguments, forwarded verbatim (add <pkg>, remove <pkg>, why <pkg>, ...)").action((args, options) => {
|
|
145
|
+
if (options.profile === "") program.error("error: --profile needs a name");
|
|
146
|
+
if (!manageDesktopProfile) rejectElectronProfile(plugin, options.profile);
|
|
147
|
+
if (args.length === 0) program.error("error: plugin needs pnpm arguments to forward (e.g. add <package>)");
|
|
148
|
+
resolved = {
|
|
149
|
+
mode: "plugin",
|
|
150
|
+
profile: options.profile.toLowerCase() === "desktop" ? "desktop" : options.profile,
|
|
151
|
+
args
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
}
|
|
128
155
|
try {
|
|
129
|
-
|
|
156
|
+
const forkCommand = first === "plugin" || first === "web:log" || first === "web:log:tmp";
|
|
157
|
+
const expanded = first !== void 0 && !first.startsWith("-") && !forkCommand ? ["--profile", ...argv] : argv;
|
|
158
|
+
program.parse(expanded, { from: "user" });
|
|
130
159
|
} catch (error) {
|
|
131
160
|
return process.exit(error instanceof CommanderError ? error.exitCode : 1);
|
|
132
161
|
}
|
|
@@ -135,51 +164,128 @@ function parseDshArgs(argv, version) {
|
|
|
135
164
|
return resolved;
|
|
136
165
|
}
|
|
137
166
|
//#endregion
|
|
138
|
-
//#region lib/types/
|
|
167
|
+
//#region lib/types/startup-diagnostics.js
|
|
168
|
+
/** Save original startup diagnostics while keeping the terminal report concise. */
|
|
169
|
+
/** Wait for stderr to finish the write before the failed process exits. */
|
|
170
|
+
function writeStderr(text) {
|
|
171
|
+
return new Promise((resolve, reject) => {
|
|
172
|
+
process.stderr.write(text, (error) => {
|
|
173
|
+
if (error) reject(error);
|
|
174
|
+
else resolve();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
139
178
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* @
|
|
179
|
+
* Print the startup summary and save a private, uniquely named report under DSH_HOME/logs.
|
|
180
|
+
* Failed writes print the complete report to stderr instead of claiming a saved path.
|
|
181
|
+
* @param error - startup audit failure retaining plugin metadata and original errors.
|
|
182
|
+
* @param context - resolved Harness home, application version, and selected profile.
|
|
183
|
+
* @param write - terminal output sink; awaited before returning, defaults to stderr.
|
|
184
|
+
* @returns after saving or printing the report and completing terminal writes.
|
|
144
185
|
*/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
186
|
+
async function reportStartupFailure(error, context, write = writeStderr) {
|
|
187
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
188
|
+
const report = "WARNING: Raw diagnostics may contain configuration or credential values from plugin errors. Review before sharing.\n\n" + inspect({
|
|
189
|
+
timestamp: now,
|
|
190
|
+
dshVersion: context.version,
|
|
191
|
+
nodeVersion: process.version,
|
|
192
|
+
platform: process.platform,
|
|
193
|
+
arch: process.arch,
|
|
194
|
+
profile: context.profile,
|
|
195
|
+
error
|
|
196
|
+
}, {
|
|
197
|
+
depth: null,
|
|
198
|
+
maxArrayLength: null,
|
|
199
|
+
maxStringLength: null,
|
|
200
|
+
showHidden: true,
|
|
201
|
+
customInspect: false,
|
|
202
|
+
getters: false,
|
|
203
|
+
colors: false
|
|
204
|
+
}) + "\n";
|
|
205
|
+
await write(`${error.message}\n`);
|
|
206
|
+
const logDir = join(context.home, "logs");
|
|
207
|
+
const logPath = join(logDir, `startup-${now.replaceAll(":", "-")}-${randomUUID()}.log`);
|
|
208
|
+
try {
|
|
209
|
+
await mkdir(logDir, {
|
|
210
|
+
recursive: true,
|
|
211
|
+
mode: 448
|
|
160
212
|
});
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const { runPlugin } = await import("./plugin-9h8shc4d.js");
|
|
165
|
-
process.exit(runPlugin(invocation.profile, invocation.args));
|
|
166
|
-
break;
|
|
167
|
-
}
|
|
168
|
-
case "web-log": {
|
|
169
|
-
const { runWebLog } = await import("./web-log-DZLBFfre.js");
|
|
170
|
-
runWebLog({
|
|
171
|
-
tmp: invocation.tmp,
|
|
172
|
-
patches: invocation.patches,
|
|
173
|
-
args: invocation.args
|
|
213
|
+
await writeFile(logPath, report, {
|
|
214
|
+
flag: "wx",
|
|
215
|
+
mode: 384
|
|
174
216
|
});
|
|
175
|
-
|
|
217
|
+
} catch (writeError) {
|
|
218
|
+
await write(`\ndsh: warning: could not write startup diagnostics: ${String(writeError)}\nFull diagnostics:\n${report}`);
|
|
219
|
+
return;
|
|
176
220
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
221
|
+
await write(`\nFull diagnostics: ${logPath}\n`);
|
|
222
|
+
}
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region lib/types/bin.js
|
|
225
|
+
/**
|
|
226
|
+
* Command-line entry for dsh.
|
|
227
|
+
* @module @deepseek-ai/dsh/bin
|
|
228
|
+
*/
|
|
229
|
+
/* v8 ignore file -- built-bin acceptance exercises this self-executing dispatch. */
|
|
230
|
+
/**
|
|
231
|
+
* Run the public dsh command-line interface.
|
|
232
|
+
* @param options - Package runtime and Desktop profile access supplied by the installation.
|
|
233
|
+
* @returns a promise that settles when the selected command mode finishes.
|
|
234
|
+
*/
|
|
235
|
+
async function runCli(options = {}) {
|
|
236
|
+
const version = getDshRuntimeVersion();
|
|
237
|
+
const { manageDesktopProfile, ...profileOptions } = options;
|
|
238
|
+
const invocation = parseDshArgs(process.argv.slice(2), version, manageDesktopProfile);
|
|
239
|
+
switch (invocation.mode) {
|
|
240
|
+
case "profile": {
|
|
241
|
+
const { runProfile } = await import("./profile-boot.js");
|
|
242
|
+
try {
|
|
243
|
+
await runProfile({
|
|
244
|
+
environment: loadLayeredEnv("dsh"),
|
|
245
|
+
profile: invocation.profile,
|
|
246
|
+
fromDefaultProfile: invocation.fromDefaultProfile,
|
|
247
|
+
patchFiles: invocation.patches,
|
|
248
|
+
args: invocation.args,
|
|
249
|
+
...profileOptions
|
|
250
|
+
});
|
|
251
|
+
} catch (error) {
|
|
252
|
+
if (!(error instanceof StartupError)) throw error;
|
|
253
|
+
await reportStartupFailure(error, {
|
|
254
|
+
home: resolveDshHome(),
|
|
255
|
+
version,
|
|
256
|
+
profile: invocation.profile
|
|
257
|
+
});
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
case "plugin": {
|
|
263
|
+
const { runPlugin } = await import("./plugin-BiveYA7h.js");
|
|
264
|
+
process.exit(await runPlugin(invocation.profile, invocation.args, options.packageManager));
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
case "web-log": {
|
|
268
|
+
const { runWebLog } = await import("./web-log-DVrp_oFP.js");
|
|
269
|
+
runWebLog({
|
|
270
|
+
tmp: invocation.tmp,
|
|
271
|
+
patches: invocation.patches,
|
|
272
|
+
args: invocation.args
|
|
273
|
+
});
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
case "dump-config": {
|
|
277
|
+
const { runDumpConfig } = await import("./dump-config-CxNkSYcK.js");
|
|
278
|
+
runDumpConfig(invocation.profile, invocation.defaultOnly, invocation.patches, invocation.fromDefaultProfile);
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
case "dump-config-schema": {
|
|
282
|
+
const { runDumpConfigSchema } = await import("./dump-config-schema-D_PxIzpe.js");
|
|
283
|
+
await runDumpConfigSchema(invocation.profile, invocation.patches, invocation.fromDefaultProfile);
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
default: throw new Error(`dsh: unhandled invocation mode ${JSON.stringify(invocation)}`);
|
|
181
287
|
}
|
|
182
|
-
default: throw new Error(`dsh: unhandled invocation mode ${JSON.stringify(invocation)}`);
|
|
183
288
|
}
|
|
289
|
+
if (import.meta.main) await runCli();
|
|
184
290
|
//#endregion
|
|
185
|
-
export {};
|
|
291
|
+
export { runCli };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { a as prepareProfile, n as PROFILE_ROOT_FILENAME, r as homePatchPath } from "./profile-boot-C14i-8IN.js";
|
|
3
2
|
import { loadOptionalPatches, loadOverlayPatches, renderConfigDump } from "@deepseek-ai/dsh-app-boot";
|
|
4
3
|
import { join, resolve } from "node:path";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
5
|
//#region lib/types/dump-config.js
|
|
6
6
|
/**
|
|
7
7
|
* Config-dump entry for `dsh --profile <name> --dump-config`: compose the
|
|
@@ -19,9 +19,21 @@ const NAME = "dsh";
|
|
|
19
19
|
* (the recovery diagnostic for a broken `cordis.patch.yml`, which is then
|
|
20
20
|
* never parsed).
|
|
21
21
|
* @param patches - `--patch` overlay paths, in argv order.
|
|
22
|
+
* @param fromDefaultProfile - shipped template that replaces the profile before loading it.
|
|
22
23
|
*/
|
|
23
|
-
function runDumpConfig(profile, defaultOnly, patches) {
|
|
24
|
-
const loaded = prepareProfile(profile, !defaultOnly);
|
|
24
|
+
function runDumpConfig(profile, defaultOnly, patches, fromDefaultProfile) {
|
|
25
|
+
const loaded = prepareProfile(profile, !defaultOnly, fromDefaultProfile);
|
|
26
|
+
const layers = collectConfigDumpLayers(loaded, defaultOnly, patches);
|
|
27
|
+
process.stdout.write(renderConfigDump(NAME, join(loaded.dir, PROFILE_ROOT_FILENAME), layers));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Read dump layers in bundle, profile, home, then argv order without composing them.
|
|
31
|
+
* @param loaded - prepared profile and parsed bundle and profile patches.
|
|
32
|
+
* @param defaultOnly - omit profile, home, and argv layers without reading their files.
|
|
33
|
+
* @param patches - overlay paths relative to the invoking directory, in argv order.
|
|
34
|
+
* @returns the labeled layers shared by YAML and schema dumps.
|
|
35
|
+
*/
|
|
36
|
+
function collectConfigDumpLayers(loaded, defaultOnly, patches) {
|
|
25
37
|
const layers = loaded.layers.map((layer) => ({
|
|
26
38
|
label: layer.packageName,
|
|
27
39
|
patches: layer.patches
|
|
@@ -45,8 +57,8 @@ function runDumpConfig(profile, defaultOnly, patches) {
|
|
|
45
57
|
});
|
|
46
58
|
}
|
|
47
59
|
}
|
|
48
|
-
|
|
60
|
+
return layers;
|
|
49
61
|
}
|
|
50
62
|
/* v8 ignore stop */
|
|
51
63
|
//#endregion
|
|
52
|
-
export { runDumpConfig };
|
|
64
|
+
export { runDumpConfig as n, collectConfigDumpLayers as t };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { a as prepareProfile, t as INSTALL_ANCHOR } from "./profile-boot-C14i-8IN.js";
|
|
2
|
+
import { t as collectConfigDumpLayers } from "./dump-config-CWs_HdOc.js";
|
|
3
|
+
import { generateConfigSchema } from "@deepseek-ai/dsh-app-boot";
|
|
4
|
+
//#region lib/types/dump-config-schema.js
|
|
5
|
+
/**
|
|
6
|
+
* Schema-dump entry: inspect the composed profile without applying its plugins.
|
|
7
|
+
* Imports and lazy schema builders execute trusted module code.
|
|
8
|
+
* @module @deepseek-ai/dsh/dump-config-schema
|
|
9
|
+
*/
|
|
10
|
+
/* v8 ignore file -- built-bin acceptance drives schema collection and output. */
|
|
11
|
+
const NAME = "dsh";
|
|
12
|
+
/**
|
|
13
|
+
* Print one JSON Schema document; incomplete collection or projection sets exitCode to 1.
|
|
14
|
+
* Profile initialization writes match the YAML dump. Plugin imports may execute
|
|
15
|
+
* top-level code, but collection never applies plugins or evaluates `!!js`.
|
|
16
|
+
* During schema generation, ordinary stdout writes are redirected to stderr.
|
|
17
|
+
* @param profile - the profile name.
|
|
18
|
+
* @param patches - overlay paths, in argv order.
|
|
19
|
+
* @param fromDefaultProfile - shipped template that replaces the profile before loading it.
|
|
20
|
+
* @returns after collection and output, with diagnostics also written to stderr.
|
|
21
|
+
*/
|
|
22
|
+
async function runDumpConfigSchema(profile, patches, fromDefaultProfile) {
|
|
23
|
+
const loaded = prepareProfile(profile, true, fromDefaultProfile);
|
|
24
|
+
const layers = collectConfigDumpLayers(loaded, false, patches);
|
|
25
|
+
const stdoutWrite = process.stdout.write;
|
|
26
|
+
let dump;
|
|
27
|
+
process.stdout.write = process.stderr.write.bind(process.stderr);
|
|
28
|
+
try {
|
|
29
|
+
dump = await generateConfigSchema(loaded, layers.map((layer) => layer.patches), INSTALL_ANCHOR);
|
|
30
|
+
} finally {
|
|
31
|
+
process.stdout.write = stdoutWrite;
|
|
32
|
+
}
|
|
33
|
+
process.stdout.write(`${JSON.stringify(dump, null, 2)}\n`);
|
|
34
|
+
for (const diagnostic of dump["x-cordis"].diagnostics) {
|
|
35
|
+
const location = diagnostic.path === void 0 ? "" : ` [${diagnostic.path}]`;
|
|
36
|
+
process.stderr.write(`${NAME}: ${diagnostic.level}:${location} ${diagnostic.message}\n`);
|
|
37
|
+
}
|
|
38
|
+
if (!dump["x-cordis"].complete) process.exitCode = 1;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { runDumpConfigSchema };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { t as INSTALL_ANCHOR } from "./profile-boot-C14i-8IN.js";
|
|
2
|
+
import { DEFAULT_PROFILE_BUNDLES, PROFILE_TEMPLATES, initProfile, readProfileCompatibility, resolveProfileDir } from "@deepseek-ai/dsh-app-boot";
|
|
3
|
+
import { mkdir } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { runPluginCommand, runProfilePnpm, setProfileVersionExemption } from "@deepseek-ai/dsh-plugin-manager/operations";
|
|
7
|
+
import { withFileLock } from "@deepseek-ai/dsh-atomic-write";
|
|
8
|
+
//#region lib/types/plugin.js
|
|
9
|
+
/** Profile package management and explicit, exact-version compatibility approvals. */
|
|
10
|
+
function requireDesktopProfile(dir) {
|
|
11
|
+
if (!existsSync(join(dir, "package.json"))) throw new Error("Open DeepSeek Harness Desktop once to initialize its profile, then fully quit it before running dsh plugin --profile desktop.");
|
|
12
|
+
}
|
|
13
|
+
/** Parse only DSH-owned commands; all other arguments remain pnpm's responsibility. */
|
|
14
|
+
async function versionCommand(profile, args) {
|
|
15
|
+
const [command, ...rest] = args;
|
|
16
|
+
if (command !== "allow-version" && command !== "revoke-version" && command !== "version-exemptions") return void 0;
|
|
17
|
+
try {
|
|
18
|
+
let packageVersion;
|
|
19
|
+
let runtimeVersion;
|
|
20
|
+
let acceptRisk = false;
|
|
21
|
+
const argumentsIterator = rest.values();
|
|
22
|
+
for (const argument of argumentsIterator) if (argument === "--accept-risk" && command === "allow-version" && !acceptRisk) acceptRisk = true;
|
|
23
|
+
else if (argument === "--dsh-version" && runtimeVersion === void 0) runtimeVersion = argumentsIterator.next().value;
|
|
24
|
+
else if (argument.startsWith("--dsh-version=") && runtimeVersion === void 0) runtimeVersion = argument.slice(14);
|
|
25
|
+
else if (!argument.startsWith("-") && packageVersion === void 0) packageVersion = argument;
|
|
26
|
+
else throw new Error(`unexpected argument ${JSON.stringify(argument)}`);
|
|
27
|
+
if (command === "version-exemptions" && rest.length > 0) throw new Error("usage: dsh plugin version-exemptions");
|
|
28
|
+
let request;
|
|
29
|
+
if (command !== "version-exemptions") {
|
|
30
|
+
if (packageVersion === void 0 || runtimeVersion === void 0) throw new Error(`usage: dsh plugin ${command} <package@version> --dsh-version <exact>${command === "allow-version" ? " --accept-risk" : ""}`);
|
|
31
|
+
request = {
|
|
32
|
+
packageVersion,
|
|
33
|
+
runtimeVersion
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (command === "allow-version") process.stderr.write("dsh: warning: allowing incompatible plugin versions can break the application or corrupt data. Approval applies only to the exact package and DSH versions.\n");
|
|
37
|
+
const dir = resolveProfileDir(profile);
|
|
38
|
+
if (profile !== "desktop") await mkdir(dir, { recursive: true });
|
|
39
|
+
await withFileLock(join(dir, "package.json"), async () => {
|
|
40
|
+
if (profile === "desktop") requireDesktopProfile(dir);
|
|
41
|
+
else if (!existsSync(join(dir, "package.json"))) initProfile(dir, PROFILE_TEMPLATES[profile]?.bundles ?? DEFAULT_PROFILE_BUNDLES);
|
|
42
|
+
if (request === void 0) {
|
|
43
|
+
const { exemptions, warnings } = readProfileCompatibility(dir);
|
|
44
|
+
for (const warning of warnings) process.stderr.write(`dsh: warning: ${warning}\n`);
|
|
45
|
+
process.stdout.write(JSON.stringify(exemptions, void 0, 2) + "\n");
|
|
46
|
+
} else {
|
|
47
|
+
await setProfileVersionExemption(dir, request.packageVersion, request.runtimeVersion, command === "allow-version", acceptRisk);
|
|
48
|
+
process.stdout.write(`dsh: ${command === "allow-version" ? "allowed" : "revoked"} ${request.packageVersion} for DSH ${request.runtimeVersion}\n`);
|
|
49
|
+
}
|
|
50
|
+
}, { waitMs: 12e4 });
|
|
51
|
+
return 0;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
process.stderr.write(`dsh: ${String(error)}\n`);
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Run package management for a profile.
|
|
58
|
+
* @param profile Profile name; Desktop's reserved profile must already be initialized by the application.
|
|
59
|
+
* @param args DSH exemption command or pnpm arguments relative to the invoking directory.
|
|
60
|
+
* @param packageManager Installation-owned executable and environment for pnpm operations.
|
|
61
|
+
* @returns Zero on success; nonzero on invalid approval or package-manager failure.
|
|
62
|
+
*/
|
|
63
|
+
async function runPlugin(profile, args, packageManager) {
|
|
64
|
+
if (profile === "desktop") try {
|
|
65
|
+
requireDesktopProfile(resolveProfileDir(profile));
|
|
66
|
+
} catch (error) {
|
|
67
|
+
process.stderr.write(`dsh: ${String(error)}\n`);
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
const versionResult = await versionCommand(profile, args);
|
|
71
|
+
if (versionResult !== void 0) return versionResult;
|
|
72
|
+
const dir = resolveProfileDir(profile);
|
|
73
|
+
if (existsSync(join(dir, "package.json"))) for (const warning of readProfileCompatibility(dir).warnings) process.stderr.write(`dsh: warning: ${warning}\n`);
|
|
74
|
+
const context = {
|
|
75
|
+
profile,
|
|
76
|
+
dir,
|
|
77
|
+
installAnchor: INSTALL_ANCHOR,
|
|
78
|
+
cwd: process.cwd()
|
|
79
|
+
};
|
|
80
|
+
const options = {
|
|
81
|
+
...packageManager,
|
|
82
|
+
execution: "cli",
|
|
83
|
+
outputBytes: 16384,
|
|
84
|
+
lockWaitMs: 12e4,
|
|
85
|
+
lookupTimeoutMs: 12e4,
|
|
86
|
+
onOutput: (text, stream) => {
|
|
87
|
+
process[stream].write(text);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const result = profile === "desktop" ? await withFileLock(join(dir, "package.json"), async () => {
|
|
91
|
+
requireDesktopProfile(dir);
|
|
92
|
+
return runProfilePnpm(context, args, options);
|
|
93
|
+
}, { waitMs: 12e4 }) : await runPluginCommand(context, args, options);
|
|
94
|
+
if (result.exitCode === 127) process.stderr.write("dsh: pnpm was not found; install pnpm and make it available on PATH.\n");
|
|
95
|
+
for (const { name, version, runtimeVersion } of result.incompatible ?? []) process.stderr.write(`dsh: to accept the risk, run: dsh plugin --profile ${profile} allow-version ${name}@${version} --dsh-version ${runtimeVersion} --accept-risk\n`);
|
|
96
|
+
if (result.exitCode !== 0) process.stderr.write(`dsh: plugin command failed; diagnostics: ${result.logPath}\n`);
|
|
97
|
+
if (result.exitCode !== 0 && args.some((argument) => /^git\+|^github:|\.git(?:#|$)/.test(argument))) process.stderr.write(`dsh: git-hosted plugins build on install via their prepare script, which pnpm blocks until allowed — add the exact key pnpm printed above under allowBuilds in ${join(resolveProfileDir(profile), "pnpm-workspace.yaml")}, then re-run\n`);
|
|
98
|
+
return result.exitCode;
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { runPlugin };
|