dsh-wsl-workspace 0.7.2 → 0.7.4
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 +74 -0
- package/README.zh.md +16 -0
- package/TESTING.md +66 -15
- package/lib/client.js +4 -4
- package/lib/client.js.map +1 -1
- package/lib/fs.js +2 -2
- package/lib/index.js +127 -20
- package/lib/index.js.map +1 -1
- package/lib/{links-BQxdJOMy.js → links-DQ8GgpMj.js} +19 -19
- package/lib/links-DQ8GgpMj.js.map +1 -0
- package/lib/shell.js +2 -2
- package/lib/{wsl-CECXiGZH.js → wsl-BTA7XT8X.js} +72 -12
- package/lib/wsl-BTA7XT8X.js.map +1 -0
- package/lib/{wsl-credentials-BJXc9tfi.js → wsl-credentials-gFHTqaiN.js} +2 -2
- package/lib/{wsl-credentials-BJXc9tfi.js.map → wsl-credentials-gFHTqaiN.js.map} +1 -1
- package/lib/wsl-relay.js +1 -1
- package/lib/wsl-search.js +2 -2
- package/package.json +10 -4
- package/src/client/locales.ts +4 -4
- package/src/index.ts +222 -25
- package/src/shared/links.ts +20 -11
- package/src/shared/wsl.ts +99 -15
- package/lib/links-BQxdJOMy.js.map +0 -1
- package/lib/wsl-CECXiGZH.js.map +0 -1
package/lib/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { defaultDistro, isAbsoluteLinuxPath, isValidWslUsername, joinUnc, listDistros, mntToWindowsPath, normalizeLinuxPath, parseWslUnc, uncToLinux } from "./wsl-
|
|
2
|
-
import { canonicalWslUnc, getWindowsWorkspace, getWorkspaceUsername, listWorkspaceKeys, registerWindowsWorkspace, setWorkspaceUsername } from "./wsl-credentials-
|
|
3
|
-
import { resolveLinuxSymlinks } from "./links-
|
|
1
|
+
import { defaultDistro, isAbsoluteLinuxPath, isValidWslUsername, joinUnc, listDistros, mntToWindowsPath, normalizeLinuxPath, parseWslUnc, uncToLinux } from "./wsl-BTA7XT8X.js";
|
|
2
|
+
import { canonicalWslUnc, getWindowsWorkspace, getWorkspaceUsername, listWorkspaceKeys, registerWindowsWorkspace, setWorkspaceUsername } from "./wsl-credentials-gFHTqaiN.js";
|
|
3
|
+
import { resolveLinuxSymlinks } from "./links-DQ8GgpMj.js";
|
|
4
4
|
import z from "@deepseek-ai/schemastery";
|
|
5
5
|
import { cpSync, existsSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { dirname, join, posix } from "node:path";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
+
import { dirname, isAbsolute, join, posix } from "node:path";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
10
10
|
|
|
@@ -1039,6 +1039,13 @@ const DEFAULT_ROUTE = "/wsl-workspace/api";
|
|
|
1039
1039
|
* preset.yml text verbatim, so the plugin writes one bilingual string so
|
|
1040
1040
|
* both locales can identify each variant. Custom presets keep their own
|
|
1041
1041
|
* name.
|
|
1042
|
+
*
|
|
1043
|
+
* This table is consulted before anything the release publishes, so every id
|
|
1044
|
+
* the mode has ever carried needs an entry. `code` was the id up to
|
|
1045
|
+
* `0.1.0-rc.8` and `ptc` is the id from `0.1.1` on; without the `ptc` entry the
|
|
1046
|
+
* lookup returned undefined and the variant fell through to the release's own
|
|
1047
|
+
* display name — which `0.1.7` does not publish at all, leaving the mode named
|
|
1048
|
+
* `WSL · ptc` with the generic description `WSL execution world for ptc: …`.
|
|
1042
1049
|
*/
|
|
1043
1050
|
const MODE_DISPLAY_LABELS = {
|
|
1044
1051
|
standard: {
|
|
@@ -1049,6 +1056,10 @@ const MODE_DISPLAY_LABELS = {
|
|
|
1049
1056
|
en: "Code mode",
|
|
1050
1057
|
zh: "PTC 模式"
|
|
1051
1058
|
},
|
|
1059
|
+
ptc: {
|
|
1060
|
+
en: "PTC mode",
|
|
1061
|
+
zh: "PTC 模式"
|
|
1062
|
+
},
|
|
1052
1063
|
minimal: {
|
|
1053
1064
|
en: "Minimal mode",
|
|
1054
1065
|
zh: "极简模式"
|
|
@@ -1286,6 +1297,81 @@ async function dispatch(method, params) {
|
|
|
1286
1297
|
}
|
|
1287
1298
|
}
|
|
1288
1299
|
/**
|
|
1300
|
+
* Read one preset's declared composition across both roster generations.
|
|
1301
|
+
* @param agentPresets - the roster face.
|
|
1302
|
+
* @param preset - the roster entry to read.
|
|
1303
|
+
* @returns the composition text, plus the display name when the face publishes it.
|
|
1304
|
+
*/
|
|
1305
|
+
async function readPresetComposition(agentPresets, preset) {
|
|
1306
|
+
if (typeof agentPresets.readDocument === "function") {
|
|
1307
|
+
const document = await agentPresets.readDocument(preset.id);
|
|
1308
|
+
return document.name === void 0 ? { content: document.content } : {
|
|
1309
|
+
content: document.content,
|
|
1310
|
+
name: document.name
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
if (typeof agentPresets.read === "function") return { content: await agentPresets.read(preset.id) };
|
|
1314
|
+
throw new Error(`agentPresets: this DSH release exposes neither readDocument() nor read() (preset "${preset.id}")`);
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Parse one transformed composition back into declaration rows.
|
|
1318
|
+
*
|
|
1319
|
+
* The composition is the entry-list YAML dialect, whose `!!js` scalars are
|
|
1320
|
+
* expression nodes the Loader evaluates when it activates the row.
|
|
1321
|
+
* `@deepseek-ai/cordis-plugin-include` exports `entryListSchema` precisely so
|
|
1322
|
+
* config tooling can round-trip that dialect, and it is the same schema the
|
|
1323
|
+
* harness parses preset patches with. Parsing the text back is what lets the
|
|
1324
|
+
* (text-level) WSL transform keep working unchanged now that a variant is a
|
|
1325
|
+
* declaration row instead of a directory of YAML.
|
|
1326
|
+
*
|
|
1327
|
+
* Both modules are resolved at call time rather than at module load: they are
|
|
1328
|
+
* Host-provided (`dsh` depends on `cordis-plugin-include`, which depends on
|
|
1329
|
+
* `js-yaml`), and a release that ever drops them must fail this one variant
|
|
1330
|
+
* rather than refuse to load the whole plugin.
|
|
1331
|
+
* @param content - the variant composition text.
|
|
1332
|
+
* @returns the declaration's plugin rows.
|
|
1333
|
+
*/
|
|
1334
|
+
async function parseVariantComposition(content) {
|
|
1335
|
+
const includeSpecifier = "@deepseek-ai/cordis-plugin-include";
|
|
1336
|
+
const yamlSpecifier = "js-yaml";
|
|
1337
|
+
const [include, yamlModule] = await Promise.all([import(includeSpecifier), import(yamlSpecifier)]);
|
|
1338
|
+
const load = yamlModule.load ?? yamlModule.default?.load;
|
|
1339
|
+
if (typeof load !== "function") throw new Error("js-yaml: no load() export");
|
|
1340
|
+
const rows = load(content, { schema: include.entryListSchema });
|
|
1341
|
+
if (!Array.isArray(rows)) throw new Error("the transformed composition did not parse as an entry list");
|
|
1342
|
+
return toImportableSpecifiers(rows);
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Rewrite every absolute local module specifier as a `file:` URL.
|
|
1346
|
+
*
|
|
1347
|
+
* The generated world names THIS package's built providers (`shell.js`,
|
|
1348
|
+
* `fs.js`, …) by absolute path. That is what the directory mechanism needed:
|
|
1349
|
+
* those rows sat in an Include-backed tree, and the boot-time Include
|
|
1350
|
+
* translates an absolute path into a `file:` URL before importing it. A preset
|
|
1351
|
+
* mounted from a *declaration* is loaded by the registry's own entry tree,
|
|
1352
|
+
* which has no such translation — handing it `C:/…/lib/shell.js` leaves those
|
|
1353
|
+
* rows without a fiber, the audit reports them "never started", and the whole
|
|
1354
|
+
* variant is refused. Rewriting the specifier is therefore part of adapting to
|
|
1355
|
+
* the declaration mechanism, not a change to what the variant mounts.
|
|
1356
|
+
*
|
|
1357
|
+
* Only `name` is touched: config values (the relay path handed to the PTY
|
|
1358
|
+
* backend, the interpreter path in `shellPath`) must stay native filesystem
|
|
1359
|
+
* paths. Group rows carry their children in a `config` array, so those are
|
|
1360
|
+
* walked too.
|
|
1361
|
+
* @param rows - the parsed declaration rows.
|
|
1362
|
+
* @returns the same rows with their module specifiers made importable.
|
|
1363
|
+
*/
|
|
1364
|
+
function toImportableSpecifiers(rows) {
|
|
1365
|
+
const rewrite = (row) => {
|
|
1366
|
+
if (row === null || typeof row !== "object") return row;
|
|
1367
|
+
const entry = row;
|
|
1368
|
+
if (typeof entry.name === "string" && isAbsolute(entry.name)) entry.name = pathToFileURL(entry.name).href;
|
|
1369
|
+
if (Array.isArray(entry.config)) entry.config = entry.config.map(rewrite);
|
|
1370
|
+
return row;
|
|
1371
|
+
};
|
|
1372
|
+
return rows.map(rewrite);
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1289
1375
|
* Publish a fully staged preset directory while preserving the last complete
|
|
1290
1376
|
* variant if publication fails. Stable sibling names also let the next boot
|
|
1291
1377
|
* recover an interrupted old-to-backup rename before doing new work.
|
|
@@ -1378,7 +1464,7 @@ async function waitForSubprocess(ctx) {
|
|
|
1378
1464
|
return void 0;
|
|
1379
1465
|
}
|
|
1380
1466
|
/** Materialize one WSL variant per healthy source preset. */
|
|
1381
|
-
async function materializeVariants(agentPresets, dshHome, paths, persistentShell) {
|
|
1467
|
+
async function materializeVariants(agentPresets, dshHome, paths, persistentShell, track) {
|
|
1382
1468
|
const presets = await agentPresets.list();
|
|
1383
1469
|
const userRoot = join(dshHome, ".agent-presets");
|
|
1384
1470
|
const generated = /* @__PURE__ */ new Set();
|
|
@@ -1386,12 +1472,25 @@ async function materializeVariants(agentPresets, dshHome, paths, persistentShell
|
|
|
1386
1472
|
if (preset.broken !== void 0) continue;
|
|
1387
1473
|
if (isWslVariantId(preset.id)) continue;
|
|
1388
1474
|
const variantId = variantIdFor(preset.id);
|
|
1389
|
-
const
|
|
1390
|
-
const transformed = transformPresetForWsl(
|
|
1475
|
+
const composition = await readPresetComposition(agentPresets, preset);
|
|
1476
|
+
const transformed = transformPresetForWsl(composition.content, paths.shell, paths.fs, persistentShell ? {
|
|
1391
1477
|
relayPath: paths.relay,
|
|
1392
1478
|
nodePath: paths.node,
|
|
1393
1479
|
sandboxPath: paths.sandbox
|
|
1394
1480
|
} : void 0, paths.search, paths.jobs);
|
|
1481
|
+
if (typeof agentPresets.register === "function") {
|
|
1482
|
+
const plugins = await parseVariantComposition(transformed);
|
|
1483
|
+
const declaration = {
|
|
1484
|
+
id: variantId,
|
|
1485
|
+
name: variantName(preset.id, composition.name ?? preset.name ?? preset.id),
|
|
1486
|
+
description: variantDescription(preset.id),
|
|
1487
|
+
...preset.order === void 0 ? {} : { order: preset.order },
|
|
1488
|
+
plugins
|
|
1489
|
+
};
|
|
1490
|
+
track(await agentPresets.register(declaration));
|
|
1491
|
+
continue;
|
|
1492
|
+
}
|
|
1493
|
+
if (preset.path === void 0) throw new Error(`agentPresets: roster entry "${preset.id}" carries no path on this release`);
|
|
1395
1494
|
const dir = join(userRoot, variantId);
|
|
1396
1495
|
const staging = `${dir}.staging`;
|
|
1397
1496
|
rmSync(staging, {
|
|
@@ -1419,17 +1518,11 @@ async function materializeVariants(agentPresets, dshHome, paths, persistentShell
|
|
|
1419
1518
|
publishVariant(staging, dir);
|
|
1420
1519
|
generated.add(variantId);
|
|
1421
1520
|
}
|
|
1422
|
-
for (const entry of readdirSync(userRoot, { withFileTypes: true })) {
|
|
1521
|
+
if (existsSync(userRoot)) for (const entry of readdirSync(userRoot, { withFileTypes: true })) {
|
|
1423
1522
|
if (!entry.isDirectory()) continue;
|
|
1424
|
-
if (entry.name
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
force: true
|
|
1428
|
-
});
|
|
1429
|
-
continue;
|
|
1430
|
-
}
|
|
1431
|
-
if (!/^wsl-[a-z0-9-]+$/.test(entry.name)) continue;
|
|
1432
|
-
if (!generated.has(entry.name)) rmSync(join(userRoot, entry.name), {
|
|
1523
|
+
if (!/^wsl(-[a-z0-9-]+)?$/.test(entry.name)) continue;
|
|
1524
|
+
if (generated.has(entry.name)) continue;
|
|
1525
|
+
rmSync(join(userRoot, entry.name), {
|
|
1433
1526
|
recursive: true,
|
|
1434
1527
|
force: true
|
|
1435
1528
|
});
|
|
@@ -1463,6 +1556,17 @@ function apply(ctx, config) {
|
|
|
1463
1556
|
const nodePath = process.execPath.replace(/\\/g, "/");
|
|
1464
1557
|
const agentPresets = ctx.get("agentPresets");
|
|
1465
1558
|
if (agentPresets !== void 0) ctx.effect(() => {
|
|
1559
|
+
let stopped = false;
|
|
1560
|
+
const disposers = [];
|
|
1561
|
+
const track = (dispose) => {
|
|
1562
|
+
if (typeof dispose !== "function") return;
|
|
1563
|
+
const retire = dispose;
|
|
1564
|
+
if (stopped) {
|
|
1565
|
+
Promise.resolve(retire()).catch(() => {});
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
disposers.push(retire);
|
|
1569
|
+
};
|
|
1466
1570
|
(async () => {
|
|
1467
1571
|
const persistentShell = await supportsPersistentShell(ctx);
|
|
1468
1572
|
await materializeVariants(agentPresets, dshHome, {
|
|
@@ -1473,11 +1577,14 @@ function apply(ctx, config) {
|
|
|
1473
1577
|
sandbox: sandboxPath,
|
|
1474
1578
|
search: searchPath,
|
|
1475
1579
|
jobs: jobsPath
|
|
1476
|
-
}, persistentShell);
|
|
1580
|
+
}, persistentShell, track);
|
|
1477
1581
|
})().catch((error) => {
|
|
1478
1582
|
console.error(`dsh-wsl-workspace: WSL preset-variant generation failed: ${messageOf(error)}`);
|
|
1479
1583
|
});
|
|
1480
|
-
return () => {
|
|
1584
|
+
return () => {
|
|
1585
|
+
stopped = true;
|
|
1586
|
+
for (const retire of disposers.splice(0, disposers.length)) Promise.resolve(retire()).catch(() => {});
|
|
1587
|
+
};
|
|
1481
1588
|
}, "dsh-wsl-workspace: WSL preset variants");
|
|
1482
1589
|
const skills = ctx.get("skills");
|
|
1483
1590
|
if (skills !== void 0 && typeof skills.registerProvider === "function") ctx.effect(() => skills.registerProvider((control) => new WslSkillsProvider(control)), "dsh-wsl-workspace: WSL workspace skills provider");
|