@dsh-workbench/core 1.1.12 → 1.1.14-beta.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 -3
- package/bin/seed-workshop-windows.mjs +10 -13
- package/client.js +186 -136
- package/dist/client/client.body.js +186 -136
- package/dist/client/index.js +186 -136
- package/dist/host/index.js +120 -49
- package/dist/types/app/client.d.ts +4 -1
- package/dist/types/app/layouts/env-gate/gate-text.d.ts +5 -0
- package/dist/types/kernel/contract/index.d.ts +2 -1
- package/dist/types/kernel/host/mount-units.d.ts +2 -1
- package/dist/types/shared/api/host/git-exec.d.ts +2 -2
- package/dist/types/shared/hooks/runtime-queries.d.ts +2 -1
- package/dist/types/shared/hooks/use-platform.d.ts +13 -0
- package/dist/types/shared/hooks/use-runtime-capabilities.d.ts +3 -1
- package/package.json +4 -4
- package/scripts/create-workbench.mjs +7 -4
- package/scripts/seed-workshop.sh +45 -7
- package/skills/workbench-module-diy/BUILD.md +33 -24
- package/skills/workbench-module-diy/DATASOURCE.md +3 -2
- package/skills/workbench-module-diy/FAMILY.md +1 -1
- package/skills/workbench-module-diy/FLOW.md +33 -7
- package/skills/workbench-module-diy/KIT.md +3 -1
- package/skills/workbench-module-diy/PANEL.md +7 -6
- package/skills/workbench-module-diy/REVIEW.md +9 -8
- package/skills/workbench-module-diy/SKILL.md +3 -2
- package/skills/workbench-module-diy/STEPS.md +1 -1
- package/skills/workbench-module-diy/TOOLS.md +56 -1
- package/skills/workbench-module-diy/WIRING.md +2 -0
- package/skills/workbench-module-diy/WRITES.md +1 -1
package/dist/host/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import path, { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
1
|
+
import path, { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
2
2
|
import dayjs from "dayjs";
|
|
3
3
|
import fs, { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
-
import "node:os";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import utc from "dayjs/plugin/utc.js";
|
|
7
7
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -406,16 +406,22 @@ var DAY = 864e5;
|
|
|
406
406
|
365 * DAY;
|
|
407
407
|
//#endregion
|
|
408
408
|
//#region ../shared/dist/lib/board-fact.js
|
|
409
|
+
function normalizePath(value) {
|
|
410
|
+
const forward = value.replace(/\\/g, "/").replace(/\/+$/g, "");
|
|
411
|
+
return /^[A-Za-z]:\//.test(forward) || forward.startsWith("//") ? forward.toLowerCase() : forward;
|
|
412
|
+
}
|
|
409
413
|
/**
|
|
410
414
|
* 判一个路径落不落在这个工作区里(按路径前缀比,不是字符串包含)。
|
|
411
415
|
* @param root - 工作区根路径。
|
|
412
416
|
* @param target - 要判的路径。
|
|
413
|
-
* @returns 同一个路径或在其下给 `true
|
|
417
|
+
* @returns 同一个路径或在其下给 `true`;分隔符先归一(Windows 的 `\` 与 `/` 都认),
|
|
418
|
+
* 盘符路径按大小写不敏感比;`root` 末尾带不带斜杠都认(`/a` 不匹配 `/ab`)。
|
|
414
419
|
*/
|
|
415
420
|
function insideWorkshop(root, target) {
|
|
416
|
-
|
|
417
|
-
const
|
|
418
|
-
|
|
421
|
+
const base = normalizePath(root);
|
|
422
|
+
const real = normalizePath(target);
|
|
423
|
+
if (real === base) return true;
|
|
424
|
+
return real.startsWith(base === "" ? "/" : `${base}/`);
|
|
419
425
|
}
|
|
420
426
|
/**
|
|
421
427
|
* 把探针读数归成左栏那句人话的档位。
|
|
@@ -596,9 +602,9 @@ function stringArrayProblems(at, value) {
|
|
|
596
602
|
function declarationProblemsOf(subject, raw) {
|
|
597
603
|
if (!isRecord(raw)) return [`${subject}: dsh.workbench 不是一个对象 —— ${JSON.stringify(raw)}`];
|
|
598
604
|
const version = raw.apiVersion;
|
|
599
|
-
if (version === void 0) return [`${subject}: dsh.workbench 没有 apiVersion —— 协议从 v3 起,写 \`"apiVersion": 3\`。缺它 =
|
|
605
|
+
if (version === void 0) return [`${subject}: dsh.workbench 没有 apiVersion —— 协议从 v3 起,写 \`"apiVersion": 3\`。缺它 = 外壳不认这个模块(\`dsh-wb-module migrate\` 能自动补这一项)`];
|
|
600
606
|
if (!Number.isInteger(version) || !SUPPORTED_API_VERSIONS.includes(version)) {
|
|
601
|
-
const hint = version === 2 ? " —— v3 把卡片标题挪成了顶层 `title`(`{ \"zh\": \"…\", \"en\": \"…\" }` 字面量):把 `card.titleKey` 删掉、在顶层写 title,再把 apiVersion 改成 3" : "";
|
|
607
|
+
const hint = version === 2 ? " —— v3 把卡片标题挪成了顶层 `title`(`{ \"zh\": \"…\", \"en\": \"…\" }` 字面量):把 `card.titleKey` 删掉、在顶层写 title,再把 apiVersion 改成 3" : " —— 先跑 `dsh-wb-module migrate` 把版本号改到 3,再按 check 的提示改源码";
|
|
602
608
|
return [`${subject}: dsh.workbench.apiVersion 是 ${JSON.stringify(version)},这里只认 ${SUPPORTED_API_VERSIONS.join(" / ")}(要整数)${hint}`];
|
|
603
609
|
}
|
|
604
610
|
const problems = [];
|
|
@@ -843,37 +849,72 @@ function refusalReason() {
|
|
|
843
849
|
//#endregion
|
|
844
850
|
//#region ../shared/dist/host/shell.js
|
|
845
851
|
var DEFAULT_MAX_BYTES$3 = 8388608;
|
|
852
|
+
var GUARD_SLACK_MS = 1e3;
|
|
853
|
+
function stalledResult() {
|
|
854
|
+
return {
|
|
855
|
+
exitCode: null,
|
|
856
|
+
timedOut: true,
|
|
857
|
+
aborted: false,
|
|
858
|
+
stdout: "",
|
|
859
|
+
stderr: "",
|
|
860
|
+
truncated: false,
|
|
861
|
+
stalled: true
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
async function settleWithin(work, limitMs) {
|
|
865
|
+
let timer;
|
|
866
|
+
const guard = new Promise((resolve) => {
|
|
867
|
+
timer = setTimeout(() => resolve(null), limitMs);
|
|
868
|
+
});
|
|
869
|
+
try {
|
|
870
|
+
return await Promise.race([work, guard]);
|
|
871
|
+
} finally {
|
|
872
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
846
875
|
async function runShell(shell, command, options = {}) {
|
|
847
|
-
const { workdir, timeoutMs = GIT_TIMEOUT_MS, signal } = options;
|
|
876
|
+
const { workdir, timeoutMs = GIT_TIMEOUT_MS, signal, env } = options;
|
|
848
877
|
const spec = shell.resolve({
|
|
849
878
|
command,
|
|
850
879
|
...workdir === void 0 ? {} : { workdir },
|
|
851
880
|
timeoutMs,
|
|
852
881
|
onExpiry: "kill",
|
|
853
882
|
stdoutMaxBytes: DEFAULT_MAX_BYTES$3,
|
|
854
|
-
...signal === void 0 ? {} : { signal }
|
|
883
|
+
...signal === void 0 ? {} : { signal },
|
|
884
|
+
...env === void 0 ? {} : { env }
|
|
855
885
|
});
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
886
|
+
return await settleWithin((async () => {
|
|
887
|
+
const result = await (await shell.execute(spec)).result();
|
|
888
|
+
return {
|
|
889
|
+
exitCode: result.exitCode,
|
|
890
|
+
timedOut: result.timedOut === true,
|
|
891
|
+
aborted: result.aborted === true,
|
|
892
|
+
stdout: result.stdout.text,
|
|
893
|
+
stderr: result.stderr.text,
|
|
894
|
+
truncated: result.stdout.truncated || result.stderr.truncated,
|
|
895
|
+
stalled: false
|
|
896
|
+
};
|
|
897
|
+
})(), timeoutMs + GUARD_SLACK_MS) ?? stalledResult();
|
|
865
898
|
}
|
|
866
899
|
//#endregion
|
|
867
900
|
//#region ../shared/dist/host/shell-text.js
|
|
868
901
|
/**
|
|
869
|
-
*
|
|
902
|
+
* 按目标平台判 shell 种类:win32 是 pwsh(dsh 的 Windows 执行器就是 PowerShell),其余是 POSIX。
|
|
903
|
+
* @param platform - 目标平台,默认当前 `process.platform`。
|
|
904
|
+
* @returns `'pwsh'` 或 `'posix'`。
|
|
905
|
+
*/
|
|
906
|
+
function shellKindOf(platform = process.platform) {
|
|
907
|
+
return platform === "win32" ? "pwsh" : "posix";
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* 按目标平台把一个值裹成 shell 字面量:POSIX 用单引号并按 `'\''` 逃逸,pwsh 也用单引号但按 `''` 逃逸。
|
|
870
911
|
* @param value - 任意值;不是字符串的先 `String()`。
|
|
871
912
|
* @param platform - 目标平台,默认当前 `process.platform`。
|
|
872
913
|
* @returns 可安全拼进该平台 shell 命令的一段文本;含空格 / 元字符的值不会被当成语法。
|
|
873
914
|
*/
|
|
874
915
|
function quoteShellArgFor(value, platform = process.platform) {
|
|
875
916
|
const text = String(value);
|
|
876
|
-
if (platform === "
|
|
917
|
+
if (shellKindOf(platform) === "pwsh") return `'${text.replace(/'/g, "''")}'`;
|
|
877
918
|
return `'${text.replace(/'/g, `'\\''`)}'`;
|
|
878
919
|
}
|
|
879
920
|
/**
|
|
@@ -4330,14 +4371,17 @@ function sessionIdOf(exec) {
|
|
|
4330
4371
|
//#region ../shared/dist/host/host-kit.js
|
|
4331
4372
|
/**
|
|
4332
4373
|
* 建一份宿主 kit;每个挂载点一份,没有模块级状态。
|
|
4333
|
-
* @
|
|
4374
|
+
* @param parts.libInstallCommand 拼「装这个共用依赖」的那条命令。
|
|
4375
|
+
* @param parts.surface 宿主平台事实;由组合根按 profile 与宿主进程算好再交进来。
|
|
4376
|
+
* @returns flow / sessionIdOf / sessionWorkspace / libInstallCommand / surface。
|
|
4334
4377
|
*/
|
|
4335
4378
|
function createHostKit(parts) {
|
|
4336
4379
|
return {
|
|
4337
4380
|
flow: createFlowHost(),
|
|
4338
4381
|
sessionIdOf,
|
|
4339
4382
|
sessionWorkspace,
|
|
4340
|
-
libInstallCommand: parts.libInstallCommand
|
|
4383
|
+
libInstallCommand: parts.libInstallCommand,
|
|
4384
|
+
surface: parts.surface
|
|
4341
4385
|
};
|
|
4342
4386
|
}
|
|
4343
4387
|
//#endregion
|
|
@@ -4727,15 +4771,15 @@ function createValueHandoff(options = {}) {
|
|
|
4727
4771
|
function dshHome() {
|
|
4728
4772
|
const configured = process.env.DSH_HOME?.trim();
|
|
4729
4773
|
if (configured !== void 0 && configured !== "") return configured;
|
|
4730
|
-
return
|
|
4774
|
+
return join(homedir(), ".dsh");
|
|
4731
4775
|
}
|
|
4732
4776
|
function workbenchRoot() {
|
|
4733
4777
|
const configured = process.env.DSH_WORKBENCH_HOME?.trim();
|
|
4734
4778
|
if (configured !== void 0 && configured !== "") return configured;
|
|
4735
|
-
return
|
|
4779
|
+
return join(dshHome(), "workbench");
|
|
4736
4780
|
}
|
|
4737
4781
|
function workspacesRoot() {
|
|
4738
|
-
return
|
|
4782
|
+
return join(workbenchRoot(), "workspaces");
|
|
4739
4783
|
}
|
|
4740
4784
|
//#endregion
|
|
4741
4785
|
//#region ../shared/dist/host/workspace-store.js
|
|
@@ -4747,11 +4791,15 @@ function workspaceKey(workspace) {
|
|
|
4747
4791
|
const abs = path.resolve(workspace);
|
|
4748
4792
|
return `${slug(path.basename(abs))}-${createHash("sha1").update(abs).digest("hex").slice(0, 8)}`;
|
|
4749
4793
|
}
|
|
4750
|
-
function
|
|
4794
|
+
function safeName(name) {
|
|
4751
4795
|
const trimmed = name.trim();
|
|
4752
4796
|
if (trimmed === "" || trimmed === "." || trimmed === "..") throw new Error(`模块文件夹里只接受相对文件名,收到的是「${name}」`);
|
|
4753
|
-
if (trimmed.includes("\0") || path.isAbsolute(trimmed)) throw new Error(`模块只能写自己的文件夹:不接受绝对路径「${name}」`);
|
|
4754
|
-
|
|
4797
|
+
if (trimmed.includes("\0") || path.isAbsolute(trimmed) || /^[A-Za-z]:[\\/]/.test(trimmed)) throw new Error(`模块只能写自己的文件夹:不接受绝对路径「${name}」`);
|
|
4798
|
+
if (trimmed.replace(/\\/g, "/").split("/").includes("..")) throw new Error(`模块只能写自己的文件夹:路径越界「${name}」`);
|
|
4799
|
+
return trimmed;
|
|
4800
|
+
}
|
|
4801
|
+
function inside(dir, name) {
|
|
4802
|
+
const full = path.resolve(dir, safeName(name));
|
|
4755
4803
|
if (full !== dir && !full.startsWith(dir + path.sep)) throw new Error(`模块只能写自己的文件夹:路径越界「${name}」`);
|
|
4756
4804
|
return full;
|
|
4757
4805
|
}
|
|
@@ -4760,15 +4808,15 @@ function memoryStore(dir) {
|
|
|
4760
4808
|
return {
|
|
4761
4809
|
dir,
|
|
4762
4810
|
read: (name) => {
|
|
4763
|
-
|
|
4811
|
+
safeName(name);
|
|
4764
4812
|
return files.get(name) ?? null;
|
|
4765
4813
|
},
|
|
4766
4814
|
write: (name, value) => {
|
|
4767
|
-
|
|
4815
|
+
safeName(name);
|
|
4768
4816
|
files.set(name, value);
|
|
4769
4817
|
},
|
|
4770
4818
|
remove: (name) => {
|
|
4771
|
-
|
|
4819
|
+
safeName(name);
|
|
4772
4820
|
files.delete(name);
|
|
4773
4821
|
},
|
|
4774
4822
|
list: () => [...files.keys()].sort()
|
|
@@ -4849,7 +4897,7 @@ var TARGET_FLAGS = [
|
|
|
4849
4897
|
"--dir",
|
|
4850
4898
|
"--store-dir"
|
|
4851
4899
|
];
|
|
4852
|
-
var INSTALL_VERBS = /^\s*npm\s+(i|install|add)\b/;
|
|
4900
|
+
var INSTALL_VERBS = /^\s*(?:npm\s+(?:i|install|add)|pnpm\s+(?:add|install))\b/;
|
|
4853
4901
|
var NPM_PATH = /^npm (?:error|ERR!) path (.+)$/m;
|
|
4854
4902
|
function commandTokens(command) {
|
|
4855
4903
|
const tokens = [];
|
|
@@ -6779,14 +6827,31 @@ function bundleInspectHandler(ctx) {
|
|
|
6779
6827
|
}
|
|
6780
6828
|
//#endregion
|
|
6781
6829
|
//#region src/shared/api/host/git-exec.ts
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6830
|
+
function gitNoPromptEnv(platform = process.platform) {
|
|
6831
|
+
const shared = {
|
|
6832
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
6833
|
+
SSH_ASKPASS_REQUIRE: "never",
|
|
6834
|
+
GCM_INTERACTIVE: "never",
|
|
6835
|
+
GIT_SSH_COMMAND: "ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new"
|
|
6836
|
+
};
|
|
6837
|
+
if (platform === "win32") return shared;
|
|
6838
|
+
return {
|
|
6839
|
+
...shared,
|
|
6840
|
+
GIT_ASKPASS: "/usr/bin/false",
|
|
6841
|
+
SSH_ASKPASS: "/usr/bin/false"
|
|
6842
|
+
};
|
|
6843
|
+
}
|
|
6844
|
+
function gitNoPromptProcessEnv(parent = process.env, platform = process.platform) {
|
|
6845
|
+
const env = {
|
|
6846
|
+
...parent,
|
|
6847
|
+
...gitNoPromptEnv(platform)
|
|
6848
|
+
};
|
|
6849
|
+
if (platform === "win32") {
|
|
6850
|
+
delete env.GIT_ASKPASS;
|
|
6851
|
+
delete env.SSH_ASKPASS;
|
|
6852
|
+
}
|
|
6853
|
+
return env;
|
|
6854
|
+
}
|
|
6790
6855
|
function withoutAskpassNoise(text) {
|
|
6791
6856
|
return text.split("\n").filter((line) => !/unable to read askpass response/i.test(line)).join("\n").trim();
|
|
6792
6857
|
}
|
|
@@ -6796,10 +6861,7 @@ var DEFAULT_MAX_BYTES$2 = 8388608;
|
|
|
6796
6861
|
var runGitProcess = async (args, options = {}) => {
|
|
6797
6862
|
const result = await execa("git", [...args], {
|
|
6798
6863
|
...options.cwd === void 0 ? {} : { cwd: options.cwd },
|
|
6799
|
-
env:
|
|
6800
|
-
...process.env,
|
|
6801
|
-
...GIT_NO_PROMPT_ENV
|
|
6802
|
-
},
|
|
6864
|
+
env: gitNoPromptProcessEnv(),
|
|
6803
6865
|
stdin: "ignore",
|
|
6804
6866
|
reject: false,
|
|
6805
6867
|
...options.timeoutMs === void 0 ? {} : { timeout: options.timeoutMs },
|
|
@@ -8363,7 +8425,10 @@ function asText$1(value) {
|
|
|
8363
8425
|
return typeof value === "string" ? value.trim() : "";
|
|
8364
8426
|
}
|
|
8365
8427
|
function insideRoot(root, target) {
|
|
8366
|
-
|
|
8428
|
+
const base = existsSync(root) ? realpathSync(root) : resolve(root);
|
|
8429
|
+
const real = existsSync(target) ? realpathSync(target) : resolve(target);
|
|
8430
|
+
const rel = relative(base, real);
|
|
8431
|
+
return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
8367
8432
|
}
|
|
8368
8433
|
function manifestAt(dir) {
|
|
8369
8434
|
try {
|
|
@@ -13115,7 +13180,7 @@ function sourceProblemText(problem, env) {
|
|
|
13115
13180
|
//#region src/modules/panels/model/plugin-name.ts
|
|
13116
13181
|
function pluginDirOf(entry, files) {
|
|
13117
13182
|
if (entry.local === true || files?.dir === void 0) return entry.package;
|
|
13118
|
-
return
|
|
13183
|
+
return join(files.dir, entry.package);
|
|
13119
13184
|
}
|
|
13120
13185
|
function pluginOnDisk(entry, files) {
|
|
13121
13186
|
return pluginAt(pluginDirOf(entry, files), files?.manifest ?? "") !== void 0;
|
|
@@ -17337,7 +17402,10 @@ function mountUnits(units, deps) {
|
|
|
17337
17402
|
message: "没有注入 DataReader:模块只能走 deps.data 读数据"
|
|
17338
17403
|
}
|
|
17339
17404
|
});
|
|
17340
|
-
const hostKit = createHostKit({
|
|
17405
|
+
const hostKit = createHostKit({
|
|
17406
|
+
libInstallCommand: (options) => adaptersOf(deps.ctx).supply.libInstallCommand(options),
|
|
17407
|
+
surface: deps.surface ?? hostSurface()
|
|
17408
|
+
});
|
|
17341
17409
|
const data = deps.data ?? {
|
|
17342
17410
|
read: missingData,
|
|
17343
17411
|
request: missingData,
|
|
@@ -18269,10 +18337,13 @@ async function apply(ctx, config) {
|
|
|
18269
18337
|
const capabilities = adapters.runtime.capabilities(ctx);
|
|
18270
18338
|
const runtimeProfile = adapters.runtime.profile?.(ctx) ?? null;
|
|
18271
18339
|
const runtimeCheck = adapters.runtime.check;
|
|
18340
|
+
const profileDir = adapters.profile.dirOf(ctx);
|
|
18341
|
+
const surface = hostSurface({ profile: profileDir ?? void 0 });
|
|
18272
18342
|
routes.health = async (search) => ({
|
|
18273
18343
|
...await baseHealth?.(search),
|
|
18274
18344
|
capabilities,
|
|
18275
18345
|
runtimeProfile,
|
|
18346
|
+
surface,
|
|
18276
18347
|
adapterSet: {
|
|
18277
18348
|
configured: adapterSpec,
|
|
18278
18349
|
active: adapters.id,
|
|
@@ -18286,7 +18357,6 @@ async function apply(ctx, config) {
|
|
|
18286
18357
|
}
|
|
18287
18358
|
});
|
|
18288
18359
|
const renderers = /* @__PURE__ */ new Map();
|
|
18289
|
-
const profileDir = adapters.profile.dirOf(ctx);
|
|
18290
18360
|
const packageRoot = adapters.runtime.packageRoot(import.meta.url);
|
|
18291
18361
|
const reconcileProfile = async () => {
|
|
18292
18362
|
const hmr = ctx.get("hmr");
|
|
@@ -18439,6 +18509,7 @@ async function apply(ctx, config) {
|
|
|
18439
18509
|
commands: adapters.commands,
|
|
18440
18510
|
probe: (unit, options) => probeOne(unit, options?.force ?? false),
|
|
18441
18511
|
values,
|
|
18512
|
+
surface,
|
|
18442
18513
|
longTasks: {
|
|
18443
18514
|
scope: process.env["DSH_HOME"] ?? "",
|
|
18444
18515
|
bump: (names) => facts.bump([...names, LONG_TASKS_ROUTE]),
|
|
@@ -9,10 +9,12 @@ import * as longTaskModule from '../shared/hooks/use-long-task';
|
|
|
9
9
|
import * as capabilityGateModule from '../shared/hooks/use-capability-gate';
|
|
10
10
|
import * as unitBoardModule from '../shared/hooks/use-unit-board';
|
|
11
11
|
import * as visibleBusyModule from '../shared/hooks/use-visible-busy';
|
|
12
|
+
import { usePlatform } from '../shared/hooks/use-platform';
|
|
12
13
|
import * as blockerStateModule from '../shared/components/BlockerState';
|
|
13
|
-
import { ActivityAction, ActivityBlock, ActivityLine, ApprovalDetail, ApprovalPlan, Card, CardAnchor, CardBoundary, CardHead, CenteredState, ChoiceGroup, ConfirmBar, EmptyState, Field, Form, FormActions, FormRow, FormRows, IconBrandMark, IconConversation, IconModuleMark, IconNpm, IconOpencli, IconPanelScreen, Link, LongTaskRow, longTaskIsLive, Note, Row, Rows, SaveBar, SelectField, SettingsSection, SkeletonRows, Spinner, Stack, StateBlock, TextField, apiGet, apiPost, blockerOfCapability, blockerOfError, cardIdOf, cx, defaultSelectFilter, duplicateKeysOf, errorReasonOf, excerpt, factOfRoute, filterSelectOptions, firstProblemOf, formFirstProblemOf, formValuesOf, holdBusy, parseJsonLoose, parseLocalTimestamp, readText, relativeTime, relativeTimeAgo, relativeTimeBucket, relativeTimeCompact, revealFirstProblem, searchTextOf, selectCursorValue, selectEntries, shortenPath, stepSelectCursor, writeText } from '@dsh-workbench/shared';
|
|
14
|
+
import { ActivityAction, ActivityBlock, ActivityLine, ApprovalDetail, ApprovalPlan, Card, CardAnchor, CardBoundary, CardHead, CenteredState, ChoiceGroup, ConfirmBar, EmptyState, Field, Form, FormActions, FormRow, FormRows, IconBrandMark, IconConversation, IconModuleMark, IconNpm, IconOpencli, IconPanelScreen, Link, LongTaskRow, longTaskIsLive, Note, Row, Rows, SaveBar, SelectField, SettingsSection, SkeletonRows, Spinner, Stack, StateBlock, TextField, apiGet, apiPost, blockerOfCapability, blockerOfError, cardIdOf, cx, defaultSelectFilter, duplicateKeysOf, errorReasonOf, excerpt, factOfRoute, filterSelectOptions, firstProblemOf, formFirstProblemOf, formValuesOf, holdBusy, parseJsonLoose, parseLocalTimestamp, pathBase, readText, relativeTime, relativeTimeAgo, relativeTimeBucket, relativeTimeCompact, revealFirstProblem, searchTextOf, selectCursorValue, selectEntries, shortenPath, stepSelectCursor, writeText } from '@dsh-workbench/shared';
|
|
14
15
|
export declare const kit: {
|
|
15
16
|
hooks: {
|
|
17
|
+
usePlatform: typeof usePlatform;
|
|
16
18
|
heldPhase(phase: visibleBusyModule.HeldPhase, refreshing: boolean): visibleBusyModule.HeldPhase;
|
|
17
19
|
busyView(active: boolean, visible: boolean): visibleBusyModule.BusyView;
|
|
18
20
|
useVisibleBusy(active: boolean, floorMs: number): visibleBusyModule.BusyView;
|
|
@@ -121,6 +123,7 @@ export declare const kit: {
|
|
|
121
123
|
holdBusy: typeof holdBusy;
|
|
122
124
|
parseJsonLoose: typeof parseJsonLoose;
|
|
123
125
|
parseLocalTimestamp: typeof parseLocalTimestamp;
|
|
126
|
+
pathBase: typeof pathBase;
|
|
124
127
|
readText: typeof readText;
|
|
125
128
|
relativeTime: typeof relativeTime;
|
|
126
129
|
relativeTimeAgo: typeof relativeTimeAgo;
|
|
@@ -2,6 +2,11 @@ import type { Translate } from '@dsh-workbench/shared';
|
|
|
2
2
|
import type { PreflightBridgeIssue, PreflightGuidance, PreflightSnapshot, PreflightStep } from '@dsh-workbench/shared/host';
|
|
3
3
|
import type { RuntimeProfile } from '@dsh-workbench/shared';
|
|
4
4
|
export declare function failedStepText(guidance: PreflightGuidance | undefined, t: Translate): string;
|
|
5
|
+
export interface GuidanceText {
|
|
6
|
+
readonly why: string;
|
|
7
|
+
readonly step: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function guidanceTextOf(guidance: PreflightGuidance | undefined, t: Translate): GuidanceText;
|
|
5
10
|
export declare function stepDetailText(step: PreflightStep, snapshot: PreflightSnapshot, t: Translate): string;
|
|
6
11
|
export declare function gateHintOf(snapshot: PreflightSnapshot, t: Translate): string;
|
|
7
12
|
export declare function showsRawDetails(snapshot: PreflightSnapshot): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type { UnitModuleFace, UnitMountDeps, UnitMountResult } from '@dsh-workbench/shared/host';
|
|
2
2
|
export type { FlowAction, FlowActionContext, FlowActionRegistry, FlowActionStep, FlowCheck, FlowDefinition, FlowForEach, FlowHost, FlowPoll, FlowPredicate, FlowProblem, FlowResult, FlowRetry, FlowRunOptions, FlowScope, FlowStep, FlowStepEvent, FlowSubFlowStep, FlowValue, HostKit, } from '@dsh-workbench/shared/host';
|
|
3
3
|
export type { ExtensionCaller, ExtensionCallOptions, ExtensionNeed, ExtensionSession, CommandReader, ExtensionValue, GitReader, LibReader } from '@dsh-workbench/shared';
|
|
4
|
-
export type { BoardModuleProps as BoardProps, CallOverrides, DataError, DataReader, DataRequest, DataResult, RequestRecipe, UnitClientApi, UnitClientFace, } from '@dsh-workbench/shared';
|
|
4
|
+
export type { BoardModuleProps as BoardProps, CallOverrides, DataError, DataReader, DataRequest, DataResult, DshToolDefinition, DshToolRegistry, DshToolRunContext, RequestRecipe, UnitClientApi, UnitClientFace, } from '@dsh-workbench/shared';
|
|
5
5
|
export type { DshClientContext as ClientContext, DshHostContext as HostContext } from '@dsh-workbench/shared';
|
|
6
|
+
export type { ClientCapabilities, ClientShell, ClientSurface, Deployment, HostCapabilities, HostOs, HostRuntime, HostSurface, ProfileName, SelfUpdateCli, ToolchainPlan, } from '@dsh-workbench/shared';
|
|
6
7
|
export type { UnitRegistry } from '../host/unit-registry.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DshHostContext } from '@dsh-workbench/shared';
|
|
1
|
+
import type { DshHostContext, HostSurface } from '@dsh-workbench/shared';
|
|
2
2
|
import type { UnitManifest } from '@dsh-workbench/shared/host';
|
|
3
3
|
import { type RouteTable } from '@dsh-workbench/shared/host';
|
|
4
4
|
import type { ContextRenderer, LongTaskBase, LongTaskChange, LongTaskRegistry, UnitMountDeps, UnitMountResult, UnitCall } from '@dsh-workbench/shared/host';
|
|
@@ -68,6 +68,7 @@ export declare function mountUnits(units: readonly HostUnit[], deps: {
|
|
|
68
68
|
probe?: UnitMountDeps['probe'];
|
|
69
69
|
store?: UnitMountDeps['store'];
|
|
70
70
|
values?: UnitMountDeps['values'];
|
|
71
|
+
surface?: HostSurface | undefined;
|
|
71
72
|
longTasks?: {
|
|
72
73
|
readonly scope: string;
|
|
73
74
|
readonly bump: (facts: readonly string[]) => void;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare
|
|
2
|
-
export declare function
|
|
1
|
+
export declare function gitNoPromptEnv(platform?: NodeJS.Platform): Readonly<Record<string, string>>;
|
|
2
|
+
export declare function gitNoPromptProcessEnv(parent?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): NodeJS.ProcessEnv;
|
|
3
3
|
export declare function withoutAskpassNoise(text: string): string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { ApiResponse, RuntimeCapabilities, RuntimeProfile } from '@dsh-workbench/shared';
|
|
1
|
+
import type { ApiResponse, HostSurface, RuntimeCapabilities, RuntimeProfile } from '@dsh-workbench/shared';
|
|
2
2
|
export type RuntimeCapabilitiesPayload = {
|
|
3
3
|
readonly capabilities: RuntimeCapabilities;
|
|
4
4
|
readonly runtimeProfile?: RuntimeProfile | null;
|
|
5
|
+
readonly surface?: HostSurface | null;
|
|
5
6
|
};
|
|
6
7
|
export declare function readRuntimeCapabilities(): Promise<ApiResponse<RuntimeCapabilitiesPayload>>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ClientSurface, HostSurface } from '@dsh-workbench/shared';
|
|
2
|
+
/**
|
|
3
|
+
* 卡片看到的平台事实:客户端侧现算,宿主侧从 health 读;没读到之前宿主侧是 `undefined`。
|
|
4
|
+
*/
|
|
5
|
+
export interface PlatformView {
|
|
6
|
+
readonly client: ClientSurface;
|
|
7
|
+
readonly host: HostSurface | undefined;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 卡片里拿平台事实的唯一入口:部署形态 / 能力 / 操作系统的差异按这里的值分叉,别自己读 `window` 或 `process`。
|
|
11
|
+
* @returns `client` 是浏览器侧事实(deployment / shell / 宿主机 OS / capabilities),`host` 是宿主侧 surface(可能还没读到)。
|
|
12
|
+
*/
|
|
13
|
+
export declare function usePlatform(): PlatformView;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { RuntimeCapabilities, RuntimeProfile } from '@dsh-workbench/shared';
|
|
1
|
+
import type { HostSurface, RuntimeCapabilities, RuntimeProfile } from '@dsh-workbench/shared';
|
|
2
2
|
export declare function pluginMarketOf(capabilities: RuntimeCapabilities | undefined): boolean;
|
|
3
3
|
export declare function runtimeCapabilitiesValue(): RuntimeCapabilities | undefined;
|
|
4
4
|
export declare function runtimeProfileValue(): RuntimeProfile | undefined;
|
|
5
|
+
export declare function hostSurfaceValue(): HostSurface | undefined;
|
|
5
6
|
export declare function resetRuntimeCapabilities(): void;
|
|
6
7
|
export declare function useRuntimeCapabilities(): RuntimeCapabilities | undefined;
|
|
8
|
+
export declare function useHostSurface(): HostSurface | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-workbench/core",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14-beta.0",
|
|
4
4
|
"description": "个人工作台:Agent 工作台(左栏 + 常驻对话 + 自研右侧数据面板)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"icon": "./icon.svg",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@arch-guard/core": "^0.11.
|
|
79
|
+
"@arch-guard/core": "^0.11.3",
|
|
80
80
|
"@deepseek-ai/dsh-client-ui-primitives": "0.2.1-rc.2",
|
|
81
81
|
"@types/node": "^24.19.0",
|
|
82
82
|
"@types/proper-lockfile": "^4.1.4",
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
"proper-lockfile": "^4.1.2",
|
|
101
101
|
"which": "^7.0.0",
|
|
102
102
|
"write-file-atomic": "^8.0.0",
|
|
103
|
-
"@dsh-workbench/
|
|
104
|
-
"@dsh-workbench/
|
|
103
|
+
"@dsh-workbench/shared": "1.1.14-beta.0",
|
|
104
|
+
"@dsh-workbench/adapters-default": "1.1.14-beta.0"
|
|
105
105
|
},
|
|
106
106
|
"license": "MIT",
|
|
107
107
|
"repository": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { execaSync } from 'execa';
|
|
4
4
|
import { existsSync, readdirSync } from 'node:fs';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
6
|
import { dirname, join } from 'node:path';
|
|
@@ -56,9 +56,12 @@ function nameProblem(value, source, profiles) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
function runSetup(args) {
|
|
59
|
-
const child =
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const child = execaSync('bash', [join(PKG_ROOT, 'bin', 'dsh-workbench'), ...args], {
|
|
60
|
+
stdio: 'inherit',
|
|
61
|
+
reject: false,
|
|
62
|
+
});
|
|
63
|
+
if (child.failed) fail(child.shortMessage ?? `bash 起不来(退出码 ${child.exitCode ?? '未知'})`);
|
|
64
|
+
process.exit(child.exitCode ?? 1);
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
package/scripts/seed-workshop.sh
CHANGED
|
@@ -648,10 +648,32 @@ EOF
|
|
|
648
648
|
- 造一个新模块:`dsh-wb-module init <名字> --dir packages/module-<名字>`
|
|
649
649
|
(`dsh-wb-module` 挂在 PATH 上、与 cwd 无关;写成 `pnpm exec dsh-wb-module …` 也对)
|
|
650
650
|
- 每个模块自己的四件事:`pnpm build` / `pnpm typecheck` / `pnpm test` / `pnpm selfcheck`,
|
|
651
|
-
外加 `dsh-wb-module check`
|
|
651
|
+
外加 `dsh-wb-module check` 的十三条判据。
|
|
652
652
|
|
|
653
653
|
不要手改这个目录下的 `package.json` / `pnpm-workspace.yaml` / `.npmrc`:
|
|
654
654
|
它们是 `dsh-workbench setup` 生成的,模块要加依赖就加在**模块自己**的 `package.json` 里。
|
|
655
|
+
EOF
|
|
656
|
+
cat >"$DIY_DIR/AGENTS.md" <<'EOF'
|
|
657
|
+
# DIY 工坊 · agent 说明
|
|
658
|
+
|
|
659
|
+
模块住在 `packages/`,一个模块一个目录。开新模块:
|
|
660
|
+
|
|
661
|
+
dsh-wb-module init <名字> --dir packages/module-<名字>
|
|
662
|
+
|
|
663
|
+
## 依赖
|
|
664
|
+
|
|
665
|
+
依赖装在这个工坊根(`packages/` 的上一层),已经装好;模块包里不要再 `pnpm install`:
|
|
666
|
+
|
|
667
|
+
- 缺依赖 / 工具链换代:在**宿主终端**到工坊根跑 `pnpm install`;`dsh-workbench setup` 也做这件事。
|
|
668
|
+
- 会话沙箱里直接跑 `pnpm install` 会 EPERM:link 目标在可写根之外。
|
|
669
|
+
- 模块自己加依赖,写进**模块自己**的 `package.json`,再在工坊根装一次。
|
|
670
|
+
|
|
671
|
+
## 每个模块
|
|
672
|
+
|
|
673
|
+
`pnpm build` / `pnpm typecheck` / `pnpm test` / `pnpm selfcheck`,外加 `dsh-wb-module check`
|
|
674
|
+
(十三条判据);单跑一条:`dsh-wb-module check --only <判据>`。
|
|
675
|
+
|
|
676
|
+
这个目录的 `package.json` / `pnpm-workspace.yaml` / `.npmrc` 由 setup 生成,手改会被覆盖。
|
|
655
677
|
EOF
|
|
656
678
|
fi
|
|
657
679
|
|
|
@@ -665,17 +687,24 @@ fi
|
|
|
665
687
|
# 用 pnpm 的 overrides 而不是改模块模板:模板里写死 link 路径会把"装到别人机器上"这条弄坏,
|
|
666
688
|
# overrides 活在**这个工坊**里,模块的 package.json 仍旧是干净的 `^0.1.x`。
|
|
667
689
|
# 放在第一次 `pnpm install` **之前**,这样一次装就是对的;已存在的工坊再跑一次 setup 也会被修好。
|
|
690
|
+
#
|
|
691
|
+
# devDependencies 与 overrides **必须同一套值、一起重写**:只刷 overrides 的话,老清单里的
|
|
692
|
+
# devDependencies 会永远停在旧路径(实测:dev 指向早已不存在的 `packages/workbench`、overrides
|
|
693
|
+
# 指向 `packages/core`),`init` 读外壳 manifest 时还会踩到悬空链接。
|
|
668
694
|
if [ "$IN_REPO" = "1" ] && [ -f "$DIY_DIR/package.json" ]; then
|
|
669
695
|
node - "$DIY_DIR/package.json" "$CORE_SPEC" "$TOOLS_SPEC" "$SHARED_SPEC" <<'NODE'
|
|
670
696
|
const [file, core, tools, shared] = process.argv.slice(2);
|
|
671
697
|
const fs = require('node:fs');
|
|
672
698
|
const manifest = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
699
|
+
const want = {
|
|
700
|
+
'@dsh-workbench/core': core,
|
|
701
|
+
'@dsh-workbench/module-tools': tools,
|
|
702
|
+
'@dsh-workbench/shared': shared,
|
|
703
|
+
};
|
|
704
|
+
manifest.devDependencies = { ...(manifest.devDependencies ?? {}), ...want };
|
|
673
705
|
manifest.pnpm = {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
'@dsh-workbench/module-tools': tools,
|
|
677
|
-
'@dsh-workbench/shared': shared,
|
|
678
|
-
},
|
|
706
|
+
...(manifest.pnpm ?? {}),
|
|
707
|
+
overrides: { ...(manifest.pnpm?.overrides ?? {}), ...want },
|
|
679
708
|
};
|
|
680
709
|
fs.writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
681
710
|
NODE
|
|
@@ -688,7 +717,16 @@ elif ! command -v pnpm >/dev/null 2>&1; then
|
|
|
688
717
|
echo " cd $DIY_DIR && pnpm install" >&2
|
|
689
718
|
else
|
|
690
719
|
echo "→ 装工坊依赖(第一次会下载 react / typescript / vitest,几百 MB,之后不再下)"
|
|
691
|
-
|
|
720
|
+
install_log="${TMPDIR:-/tmp}/dsh-wb-install.$$.log"
|
|
721
|
+
if (cd "$DIY_DIR" && pnpm install >"$install_log" 2>&1); then
|
|
722
|
+
tail -6 "$install_log"
|
|
723
|
+
rm -f "$install_log"
|
|
724
|
+
else
|
|
725
|
+
tail -20 "$install_log" >&2
|
|
726
|
+
rm -f "$install_log"
|
|
727
|
+
echo "→ ⚠️ 装工坊依赖失败。若上面报 EPERM:link 目标在可写根之外 —— 到宿主终端重跑 setup(它由人跑、不受会话沙箱)。" >&2
|
|
728
|
+
echo " 装好前工坊里的模块跑不了 pnpm build / check;恢复后在工坊根重跑 pnpm install 即可。" >&2
|
|
729
|
+
fi
|
|
692
730
|
fi
|
|
693
731
|
|
|
694
732
|
# 完整的这一趟跑完:先挂指引(不管有没有基准 —— setup 那趟也要建起来),再看要不要自愈。
|