@godv61/dsh-task-engine 0.22.5 → 0.22.7
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/.p0-test.mjs +22 -0
- package/README.md +3 -1
- package/docs/manual.html +1 -1
- package/lib/client.js +278 -9
- package/lib/client.js.map +2 -2
- package/lib/controller.d.ts +30 -0
- package/lib/controller.js +77 -7
- package/lib/controller.js.map +1 -1
- package/package.json +1 -1
package/.p0-test.mjs
CHANGED
|
@@ -723,4 +723,26 @@ function memProbe(files) {
|
|
|
723
723
|
assert(client.includes('sourceDir'), 'client schema carries the source directory')
|
|
724
724
|
}
|
|
725
725
|
|
|
726
|
+
// ── 37. 0.22.7: installSkill auto-locates a single nested skill root ───────
|
|
727
|
+
{
|
|
728
|
+
const controller = readFileSync('./lib/controller.js', 'utf8')
|
|
729
|
+
assert(controller.includes('多个带 SKILL.md 的子目录') || controller.includes("candidates.length > 1"),
|
|
730
|
+
'installSkill rejects an ambiguous container directory')
|
|
731
|
+
assert(controller.includes('缺少') && controller.includes('candidates[0]'),
|
|
732
|
+
'installSkill auto-locates the single nested skill root')
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// ── 38. 0.22.7: install directory picker (listDirs) + package-skill bounds ──
|
|
736
|
+
{
|
|
737
|
+
const controller = readFileSync('./lib/controller.js', 'utf8')
|
|
738
|
+
assert(controller.includes('async listDirs'), 'listDirs Remote serves the install directory picker')
|
|
739
|
+
assert(controller.includes('SKILL_DIR_FILTER'), 'picker filters dependency caches from the listing')
|
|
740
|
+
assert(controller.includes('SKILL_MAX_FILES = 1000'), 'package-skill file bound is generous')
|
|
741
|
+
assert(controller.includes('SKILL_MAX_BYTES = 100'), 'package-skill byte bound is generous')
|
|
742
|
+
}
|
|
743
|
+
{
|
|
744
|
+
const client = readFileSync('./lib/client.js', 'utf8')
|
|
745
|
+
assert(client.includes('listDirs'), 'client declares the listDirs Remote contract')
|
|
746
|
+
}
|
|
747
|
+
|
|
726
748
|
console.log(`\nP0 acceptance: ${passed} checks passed`)
|
package/README.md
CHANGED
|
@@ -202,4 +202,6 @@ enableStrictWorkspaces()
|
|
|
202
202
|
- **质量修补(0.22.2,采纳 codex 评审五项)**:① client typecheck 修复——`project.ts` 不再依赖 `node:path`,浏览器面可完整类型检查;② sandbox 升级补审批——`sandbox_permissions` 必须与 `justification` 成对出现,`danger-full-access` 需人工一次批准,无审批服务即拒绝;③ Remote 任意路径设防——工作台 Remote 拒绝非绝对路径与系统级根目录;④ `set_risk` 升到 `high_risk` 时受流程能力门约束(`minimal`/`agile` 拒绝,不再绕过 `create` 的检查);⑤ `loadTask` 补齐旧记录缺失字段(items / verification / review / commits),损坏记录 fail-closed 而非引擎裸崩。
|
|
203
203
|
- **可信边界加固(0.22.3,采纳 GPT 评审)**:① 验证命令统一在任务记录的项目根运行(monorepo 子目录不再跑错目录),receipt.root 与任务根强校验;② 旧任务(无 frozen 快照)禁止升 `high_risk`——迁移或重建后才可;③ `init apply` 强制 `expected_hash`,新增 `existing_hash` 防审批期间文件被换(TOCTOU);④ 任务记录新增 `revision`,每次写入 compare-and-swap,并发覆盖直接报错(`changed concurrently`);⑤ 工作台 Remote 增加 host workspace registry(`registerWorkspace` 供 harness 集成,注册后未授权路径一律拒绝);⑥ sandbox 升级审批展示 workspace;⑦ 文档明确 hook(本地反馈)/ host(工具流约束)/ CI(最终可信门禁)三层职责边界。
|
|
204
204
|
- **并发与发布闭环(0.22.4,采纳 REVIEW-0.22.3)**:① `init apply` 在目标文件存在时**强制** `existing_hash`(不再可选),propose→apply 之间文件被篡改直接 fail-closed;② 任务写入升级为真正的原子 CAS——逻辑 `revision` 检查 + dsh-fs 的 `lstat` 版本号 `replaceIfVersion` write intent,并发写入报 `FS_STALE_VERSION` 而非静默覆盖;③ workspace registry 增加 `enableStrictWorkspaces()` 生产模式(空 registry 不再回退放行);④ `exports["./client"]` 补 `types` 条件(`lib/client.d.ts` 随构建生成);⑤ 新增 `npm run verify:package` 发布包黑盒验证(pack → 白名单一致性 → 真实安装 → 主入口 import → client 注册 → 包内 123 checks → CLI 语法);⑥ 工作台 UI 优化——侧栏「工程流程」入口与设置按钮逐像素对齐(实测对齐 36 圆形 rail / 42 高 12 圆角展开行)、流程预设改卡片式选择器、工作台 header 品牌化、初始化预览容器化。REVIEW 指出的 Typert invocation context、host 可信状态签名、CI 最终门禁仍属 harness 层,集成点已在文档标注。
|
|
205
|
-
- **目录型 skill 安装(0.22.5)**:工作台「技能」页新增「安装 skill」——把本机已有的目录型 skill(`SKILL.md` + references / scripts / agents 等文件)一键装到<b>项目级</b>(工作区 `.dsh/skills`,团队共享)或<b>用户级</b>(`$DSH_HOME/skills`,个人所有项目可用);安装校验 SKILL.md frontmatter、拒绝覆盖内置同名、拒绝覆盖已装同名、自动排除 node_modules / .git / `__pycache__` 等缓存目录并限制 200 文件 / 20 MB;实机验证 `software-testing` 目录型 skill 从 UI 安装完整落盘(含 references + scripts)。
|
|
205
|
+
- **目录型 skill 安装(0.22.5)**:工作台「技能」页新增「安装 skill」——把本机已有的目录型 skill(`SKILL.md` + references / scripts / agents 等文件)一键装到<b>项目级</b>(工作区 `.dsh/skills`,团队共享)或<b>用户级</b>(`$DSH_HOME/skills`,个人所有项目可用);安装校验 SKILL.md frontmatter、拒绝覆盖内置同名、拒绝覆盖已装同名、自动排除 node_modules / .git / `__pycache__` 等缓存目录并限制 200 文件 / 20 MB;实机验证 `software-testing` 目录型 skill 从 UI 安装完整落盘(含 references + scripts)。
|
|
206
|
+
- **文案与安装体验(0.22.6)**:「安装 skill」输入框 placeholder 去掉示例绝对路径,改为通用提示「本机 skill 目录,需含 SKILL.md」;`installSkill` 支持<b>容器目录自动定位</b>——填的目录自身没有 SKILL.md 但直接子目录里恰好有一个含 SKILL.md 的 skill 根时自动装入该子目录(多个候选则提示直接填 skill 根)。
|
|
207
|
+
- **skill 目录选择器 + 包形态(0.22.7)**:① 安装表单新增「浏览…」——host 端 `listDirs` 逐级列举目录(Windows 盘符快捷 + 路径输入回车跳转 + ↑ 上级 + **可点击面包屑**标明当前位置),含 `SKILL.md` 的子目录标「含 SKILL.md ✓」,当前目录能否直接安装实时提示,点「选此目录」回填路径,彻底不用手输;② 安装上限放宽到 1000 文件 / 100 MB(单文件 20 MB 上限),skill 作为<b>完整包</b>安装(references / scripts / 模板 / 资源等全部保留,仍自动排除 node_modules / `.git` / `__pycache__` 缓存);③ 实机验证<b>项目级</b>(工作区 `.dsh/skills`)与<b>用户级</b>(`$DSH_HOME/skills`)两条安装路径均完整落盘,逐级进入与面包屑经浏览器实测确认。
|
package/docs/manual.html
CHANGED
|
@@ -198,7 +198,7 @@ pnpm install && pnpm run build && pnpm dsh web</code></pre>
|
|
|
198
198
|
<li><strong>选流程预设</strong>:standard / agile / minimal 点一下即切。</li>
|
|
199
199
|
<li><strong>给节点挂 skill / rule</strong>:先选一个节点(如「开发」),再勾选它用什么 skill、守哪条 rule,保存。</li>
|
|
200
200
|
</ol>
|
|
201
|
-
<p>「技能」页支持新建 / <b>安装</b> / 查看 / 编辑 / 删除:内置资源只读(正文以 markdown 富文本渲染);项目级 / 用户级资源可编辑、可删除(两步确认)。「安装 skill」把本机已有的<b>目录型 skill
|
|
201
|
+
<p>「技能」页支持新建 / <b>安装</b> / 查看 / 编辑 / 删除:内置资源只读(正文以 markdown 富文本渲染);项目级 / 用户级资源可编辑、可删除(两步确认)。「安装 skill」把本机已有的<b>目录型 skill 包</b>(含 <code>SKILL.md</code>,可带 references / scripts / 模板 / 资源)一键装到项目级(工作区 <code>.dsh/skills</code>,团队共享)或用户级(<code>$DSH_HOME/skills</code>,个人所有项目可用):点「浏览…」逐级选目录(支持盘符快捷与路径跳转,含 <code>SKILL.md</code> 的目录会标记出来),也可直接填路径;装外层容器目录时会自动定位到内含 <code>SKILL.md</code> 的子目录。安装自动排除 node_modules / <code>.git</code> / <code>__pycache__</code> 等缓存(上限 1000 文件 / 100 MB),同名内置或已装技能拒绝覆盖。</p>
|
|
202
202
|
<pre><code>// .dsh/eng.json 等价内容
|
|
203
203
|
{
|
|
204
204
|
"flow": "standard",
|
package/lib/client.js
CHANGED
|
@@ -4139,6 +4139,15 @@ var installSkillRequestSchema = external_exports.object({
|
|
|
4139
4139
|
level: external_exports.enum(["project", "user"]),
|
|
4140
4140
|
path: external_exports.string().optional()
|
|
4141
4141
|
});
|
|
4142
|
+
var listDirsRequestSchema = external_exports.object({ path: external_exports.string() });
|
|
4143
|
+
var listDirsViewSchema = external_exports.object({
|
|
4144
|
+
ok: external_exports.boolean(),
|
|
4145
|
+
path: external_exports.string(),
|
|
4146
|
+
entries: external_exports.array(external_exports.object({ name: external_exports.string(), hasSkill: external_exports.boolean() })),
|
|
4147
|
+
roots: external_exports.array(external_exports.string()),
|
|
4148
|
+
currentHasSkill: external_exports.boolean(),
|
|
4149
|
+
error: external_exports.string().optional()
|
|
4150
|
+
});
|
|
4142
4151
|
var writeRuleRequestSchema = external_exports.object({
|
|
4143
4152
|
name: external_exports.string(),
|
|
4144
4153
|
content: external_exports.string(),
|
|
@@ -4329,6 +4338,22 @@ var TYPERT_REMOTE = {
|
|
|
4329
4338
|
],
|
|
4330
4339
|
result: { mode: "strict", typeSymbol: `${PACKAGE}/types#WriteResourceResult`, schema: writeResourceResultSchema }
|
|
4331
4340
|
},
|
|
4341
|
+
{
|
|
4342
|
+
id: `${PACKAGE}#task-engine/listDirs`,
|
|
4343
|
+
service: "taskEngineController",
|
|
4344
|
+
namespace: "task-engine",
|
|
4345
|
+
method: "listDirs",
|
|
4346
|
+
invocation: { kind: "direct" },
|
|
4347
|
+
parameters: [
|
|
4348
|
+
{
|
|
4349
|
+
name: "request",
|
|
4350
|
+
wire: "request",
|
|
4351
|
+
source: "json",
|
|
4352
|
+
codec: { mode: "strict", typeSymbol: `${PACKAGE}/types#ListDirsRequest`, schema: listDirsRequestSchema }
|
|
4353
|
+
}
|
|
4354
|
+
],
|
|
4355
|
+
result: { mode: "strict", typeSymbol: `${PACKAGE}/types#ListDirsView`, schema: listDirsViewSchema }
|
|
4356
|
+
},
|
|
4332
4357
|
{
|
|
4333
4358
|
id: `${PACKAGE}#task-engine/writeRule`,
|
|
4334
4359
|
service: "taskEngineController",
|
|
@@ -5432,6 +5457,42 @@ function levelOf(source) {
|
|
|
5432
5457
|
if (source === "bundled") return "bundled";
|
|
5433
5458
|
return source.startsWith("user") ? "user" : "project";
|
|
5434
5459
|
}
|
|
5460
|
+
function joinPath(dir, name) {
|
|
5461
|
+
if (dir === "") return name;
|
|
5462
|
+
return dir.replace(/[\\/]+$/u, "") + "/" + name;
|
|
5463
|
+
}
|
|
5464
|
+
function breadcrumbSegments(path) {
|
|
5465
|
+
const trimmed = path.replace(/[\\/]+$/u, "");
|
|
5466
|
+
const sep = trimmed.includes("\\") ? "\\" : "/";
|
|
5467
|
+
const parts = trimmed.split(/[\\/]+/u).filter((part) => part !== "");
|
|
5468
|
+
const segments = [];
|
|
5469
|
+
let acc = "";
|
|
5470
|
+
for (const part of parts) {
|
|
5471
|
+
if (acc === "") {
|
|
5472
|
+
acc = /^[A-Za-z]:$/u.test(part) ? part + sep : sep + part;
|
|
5473
|
+
segments.push({ label: part, path: acc });
|
|
5474
|
+
} else {
|
|
5475
|
+
acc = acc.replace(/[\\/]+$/u, "") + sep + part;
|
|
5476
|
+
segments.push({ label: part, path: acc });
|
|
5477
|
+
}
|
|
5478
|
+
}
|
|
5479
|
+
return segments;
|
|
5480
|
+
}
|
|
5481
|
+
function isRootLevel(path) {
|
|
5482
|
+
if (path === "") return true;
|
|
5483
|
+
return /^[A-Za-z]:[\\/]?$/u.test(path) || path === "/";
|
|
5484
|
+
}
|
|
5485
|
+
var paneStyle = {
|
|
5486
|
+
flex: "1 1 0",
|
|
5487
|
+
minWidth: 0,
|
|
5488
|
+
overflowY: "auto",
|
|
5489
|
+
display: "flex",
|
|
5490
|
+
flexDirection: "column",
|
|
5491
|
+
gap: 2,
|
|
5492
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
5493
|
+
borderRadius: 8,
|
|
5494
|
+
padding: 4
|
|
5495
|
+
};
|
|
5435
5496
|
function SkillManager({ workspace, remote }) {
|
|
5436
5497
|
const [skills, setSkills] = (0, import_react4.useState)([]);
|
|
5437
5498
|
const [level, setLevel] = (0, import_react4.useState)("project");
|
|
@@ -5447,6 +5508,81 @@ function SkillManager({ workspace, remote }) {
|
|
|
5447
5508
|
const [installing, setInstalling] = (0, import_react4.useState)(false);
|
|
5448
5509
|
const [installDir, setInstallDir] = (0, import_react4.useState)("");
|
|
5449
5510
|
const [installLevel, setInstallLevel] = (0, import_react4.useState)("project");
|
|
5511
|
+
const [browsing, setBrowsing] = (0, import_react4.useState)(false);
|
|
5512
|
+
const [browseCurrent, setBrowseCurrent] = (0, import_react4.useState)("");
|
|
5513
|
+
const [browseEntries, setBrowseEntries] = (0, import_react4.useState)([]);
|
|
5514
|
+
const [browseSelected, setBrowseSelected] = (0, import_react4.useState)(null);
|
|
5515
|
+
const [browseChildEntries, setBrowseChildEntries] = (0, import_react4.useState)([]);
|
|
5516
|
+
const [browseChildHasSkill, setBrowseChildHasSkill] = (0, import_react4.useState)(false);
|
|
5517
|
+
const [browseRoots, setBrowseRoots] = (0, import_react4.useState)([]);
|
|
5518
|
+
const [browseHasSkill, setBrowseHasSkill] = (0, import_react4.useState)(false);
|
|
5519
|
+
const [browseDraft, setBrowseDraft] = (0, import_react4.useState)("");
|
|
5520
|
+
const [browseError, setBrowseError] = (0, import_react4.useState)("");
|
|
5521
|
+
const loadLevel = (path) => {
|
|
5522
|
+
setBrowseError("");
|
|
5523
|
+
setBrowseSelected(null);
|
|
5524
|
+
setBrowseChildEntries([]);
|
|
5525
|
+
setBrowseChildHasSkill(false);
|
|
5526
|
+
void remote.listDirs({ path }).then((r) => {
|
|
5527
|
+
if (r.ok && r.value.ok) {
|
|
5528
|
+
setBrowseCurrent(r.value.path);
|
|
5529
|
+
setBrowseDraft(r.value.path);
|
|
5530
|
+
setBrowseEntries(r.value.entries);
|
|
5531
|
+
setBrowseRoots(r.value.roots);
|
|
5532
|
+
setBrowseHasSkill(r.value.currentHasSkill);
|
|
5533
|
+
} else {
|
|
5534
|
+
setBrowseEntries([]);
|
|
5535
|
+
setBrowseError(describeError(r.ok ? r.value.error : r.error));
|
|
5536
|
+
}
|
|
5537
|
+
}, (error) => {
|
|
5538
|
+
setBrowseEntries([]);
|
|
5539
|
+
setBrowseError("\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF1A" + describeError(error));
|
|
5540
|
+
});
|
|
5541
|
+
};
|
|
5542
|
+
const loadChild = (path) => {
|
|
5543
|
+
setBrowseChildEntries([]);
|
|
5544
|
+
setBrowseChildHasSkill(false);
|
|
5545
|
+
void remote.listDirs({ path }).then((r) => {
|
|
5546
|
+
if (r.ok && r.value.ok) {
|
|
5547
|
+
setBrowseChildEntries(r.value.entries);
|
|
5548
|
+
setBrowseChildHasSkill(r.value.currentHasSkill);
|
|
5549
|
+
} else {
|
|
5550
|
+
setBrowseChildEntries([]);
|
|
5551
|
+
}
|
|
5552
|
+
}, () => {
|
|
5553
|
+
setBrowseChildEntries([]);
|
|
5554
|
+
});
|
|
5555
|
+
};
|
|
5556
|
+
const selectRow = (entry) => {
|
|
5557
|
+
const next = joinPath(browseCurrent, entry.name);
|
|
5558
|
+
setBrowseSelected({ name: entry.name, path: next });
|
|
5559
|
+
loadChild(next);
|
|
5560
|
+
};
|
|
5561
|
+
const selectChildRow = (entry) => {
|
|
5562
|
+
if (browseSelected === null) return;
|
|
5563
|
+
const base = browseSelected.path;
|
|
5564
|
+
const baseEntries = browseChildEntries;
|
|
5565
|
+
const baseHasSkill = browseChildHasSkill;
|
|
5566
|
+
setBrowseCurrent(base);
|
|
5567
|
+
setBrowseDraft(base);
|
|
5568
|
+
setBrowseEntries(baseEntries);
|
|
5569
|
+
setBrowseHasSkill(baseHasSkill);
|
|
5570
|
+
const next = joinPath(base, entry.name);
|
|
5571
|
+
setBrowseSelected({ name: entry.name, path: next });
|
|
5572
|
+
loadChild(next);
|
|
5573
|
+
};
|
|
5574
|
+
const openBrowse = () => {
|
|
5575
|
+
setBrowsing(true);
|
|
5576
|
+
setBrowseError("");
|
|
5577
|
+
loadLevel("");
|
|
5578
|
+
};
|
|
5579
|
+
const browseUp = () => {
|
|
5580
|
+
const p = browseCurrent.replace(/[\\/]+$/u, "");
|
|
5581
|
+
const idx = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
|
|
5582
|
+
loadLevel(idx <= 2 ? "" : p.slice(0, idx));
|
|
5583
|
+
};
|
|
5584
|
+
const pickedPath = browseSelected !== null ? browseSelected.path : browseCurrent;
|
|
5585
|
+
const pickedHasSkill = browseSelected !== null ? browseChildHasSkill : browseHasSkill;
|
|
5450
5586
|
const refresh = (0, import_react4.useCallback)(() => {
|
|
5451
5587
|
void remote.listSkills(workspace).then((r) => {
|
|
5452
5588
|
if (r.ok) setSkills(r.value.skills);
|
|
@@ -5703,17 +5839,149 @@ function SkillManager({ workspace, remote }) {
|
|
|
5703
5839
|
setInstallLevel("user");
|
|
5704
5840
|
}, title: "\u88C5\u5230 $DSH_HOME/skills\uFF0C\u4E2A\u4EBA\u6240\u6709\u9879\u76EE\u53EF\u7528" }, "\u7528\u6237\u7EA7")
|
|
5705
5841
|
),
|
|
5706
|
-
(0, import_react4.createElement)(
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5842
|
+
(0, import_react4.createElement)(
|
|
5843
|
+
"div",
|
|
5844
|
+
{ style: styles.row },
|
|
5845
|
+
(0, import_react4.createElement)("input", {
|
|
5846
|
+
style: { ...control, flex: "1 1 220px", minWidth: 160 },
|
|
5847
|
+
placeholder: "\u6E90\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u672C\u673A skill \u76EE\u5F55\uFF0C\u9700\u542B SKILL.md\uFF09",
|
|
5848
|
+
value: installDir,
|
|
5849
|
+
onChange: (ev) => {
|
|
5850
|
+
setInstallDir(ev.target.value);
|
|
5851
|
+
}
|
|
5852
|
+
}),
|
|
5853
|
+
(0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "outline", size: "sm", onClick: openBrowse }, "\u6D4F\u89C8\u2026")
|
|
5854
|
+
),
|
|
5855
|
+
(0, import_react4.createElement)("p", { style: styles.hint }, "\u8981\u6C42\uFF1A\u76EE\u5F55\u5185\u6709 SKILL.md\uFF08frontmatter \u542B name + description\uFF09\u3002skill \u53EF\u4EE5\u662F\u5B8C\u6574\u5305\uFF08references / scripts / \u6A21\u677F\u7B49\u90FD\u4F1A\u4FDD\u7559\uFF09\uFF1B\u5B89\u88C5\u81EA\u52A8\u6392\u9664 node_modules / .git / __pycache__ \u7B49\u7F13\u5B58\u76EE\u5F55\uFF08\u4E0A\u9650 1000 \u6587\u4EF6 / 100 MB\uFF09\uFF0C\u540C\u540D\u5185\u7F6E\u6280\u80FD\u62D2\u7EDD\u8986\u76D6\uFF0C\u540C\u540D\u5DF2\u88C5\u6280\u80FD\u9700\u5148\u5220\u9664\u3002\u70B9\u300C\u6D4F\u89C8\u2026\u300D\u9009\u62E9\u76EE\u5F55\uFF0C\u6216\u76F4\u63A5\u8F93\u5165\u8DEF\u5F84\u3002"),
|
|
5715
5856
|
msg !== "" ? (0, import_react4.createElement)("p", { style: styles.status }, msg) : null
|
|
5716
5857
|
);
|
|
5858
|
+
const renderDirRow = (entry, active, onClick) => (0, import_react4.createElement)(
|
|
5859
|
+
"button",
|
|
5860
|
+
{
|
|
5861
|
+
key: entry.name,
|
|
5862
|
+
type: "button",
|
|
5863
|
+
onClick,
|
|
5864
|
+
title: entry.name,
|
|
5865
|
+
style: {
|
|
5866
|
+
display: "flex",
|
|
5867
|
+
alignItems: "center",
|
|
5868
|
+
gap: 6,
|
|
5869
|
+
width: "100%",
|
|
5870
|
+
border: "none",
|
|
5871
|
+
cursor: "pointer",
|
|
5872
|
+
textAlign: "left",
|
|
5873
|
+
padding: "5px 8px",
|
|
5874
|
+
borderRadius: 6,
|
|
5875
|
+
fontSize: 13,
|
|
5876
|
+
background: active ? "var(--dsw-alias-bg-layer-2)" : "transparent",
|
|
5877
|
+
color: "var(--dsw-alias-label-primary)",
|
|
5878
|
+
fontWeight: active ? 600 : 400
|
|
5879
|
+
}
|
|
5880
|
+
},
|
|
5881
|
+
(0, import_react4.createElement)("span", { style: { flex: "1 1 auto", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, " " + entry.name),
|
|
5882
|
+
entry.hasSkill ? (0, import_react4.createElement)("span", { style: { fontSize: 11, color: "var(--dsw-alias-state-success-primary)" } }, "SKILL.md") : null
|
|
5883
|
+
);
|
|
5884
|
+
const renderBrowseModal = () => (0, import_react4.createElement)(
|
|
5885
|
+
ResourceModal,
|
|
5886
|
+
{
|
|
5887
|
+
title: "\u9009\u62E9 skill \u76EE\u5F55",
|
|
5888
|
+
description: "\u76EE\u5F55\u5217\u8868\u7531\u4F60\u7684 DSH \u4E3B\u673A\u8BFB\u53D6\uFF1B\u5C55\u5F00\u5230\u542B SKILL.md \u7684\u76EE\u5F55\u540E\u70B9\u300C\u9009\u6B64\u76EE\u5F55\u300D\u3002",
|
|
5889
|
+
onClose: () => {
|
|
5890
|
+
setBrowsing(false);
|
|
5891
|
+
},
|
|
5892
|
+
footer: (0, import_react4.createElement)(
|
|
5893
|
+
"div",
|
|
5894
|
+
{ style: styles.row },
|
|
5895
|
+
(0, import_react4.createElement)("span", {
|
|
5896
|
+
style: {
|
|
5897
|
+
...styles.sourceText,
|
|
5898
|
+
flex: "1 1 auto",
|
|
5899
|
+
color: pickedHasSkill ? "var(--dsw-alias-state-success-primary)" : "var(--dsw-alias-label-tertiary)"
|
|
5900
|
+
}
|
|
5901
|
+
}, pickedHasSkill ? "\u2713 \u5F53\u524D\u76EE\u5F55\u542B SKILL.md\uFF0C\u53EF\u76F4\u63A5\u5B89\u88C5" : "\u5F53\u524D\u76EE\u5F55\u4E0D\u542B SKILL.md\u2014\u2014\u8FDB\u5165\u542B SKILL.md \u7684\u5B50\u76EE\u5F55\u518D\u9009"),
|
|
5902
|
+
(0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, {
|
|
5903
|
+
variant: "primary",
|
|
5904
|
+
size: "sm",
|
|
5905
|
+
disabled: pickedPath.trim() === "",
|
|
5906
|
+
onClick: () => {
|
|
5907
|
+
setInstallDir(pickedPath);
|
|
5908
|
+
setBrowsing(false);
|
|
5909
|
+
}
|
|
5910
|
+
}, "\u9009\u6B64\u76EE\u5F55"),
|
|
5911
|
+
(0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "ghost", size: "sm", onClick: () => {
|
|
5912
|
+
setBrowsing(false);
|
|
5913
|
+
} }, "\u53D6\u6D88")
|
|
5914
|
+
)
|
|
5915
|
+
},
|
|
5916
|
+
(0, import_react4.createElement)(
|
|
5917
|
+
"div",
|
|
5918
|
+
{ style: { ...styles.row, marginBottom: 6 } },
|
|
5919
|
+
(0, import_react4.createElement)("input", {
|
|
5920
|
+
style: { ...control, flex: "1 1 200px", minWidth: 140 },
|
|
5921
|
+
placeholder: "\u8F93\u5165\u7EDD\u5BF9\u8DEF\u5F84\u540E\u56DE\u8F66\u524D\u5F80",
|
|
5922
|
+
value: browseDraft,
|
|
5923
|
+
onChange: (ev) => {
|
|
5924
|
+
setBrowseDraft(ev.target.value);
|
|
5925
|
+
},
|
|
5926
|
+
onKeyDown: (ev) => {
|
|
5927
|
+
if (ev.key === "Enter") loadLevel(browseDraft.trim());
|
|
5928
|
+
}
|
|
5929
|
+
}),
|
|
5930
|
+
(0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "ghost", size: "sm", onClick: () => {
|
|
5931
|
+
loadLevel(browseDraft.trim());
|
|
5932
|
+
} }, "\u524D\u5F80"),
|
|
5933
|
+
(0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "ghost", size: "sm", disabled: isRootLevel(browseCurrent), onClick: browseUp, title: "\u8FDB\u5165\u4E0A\u7EA7\u76EE\u5F55" }, "\u2191 \u4E0A\u7EA7")
|
|
5934
|
+
),
|
|
5935
|
+
browseRoots.length > 1 ? (0, import_react4.createElement)(
|
|
5936
|
+
"div",
|
|
5937
|
+
{ style: { ...styles.chips, marginBottom: 6 } },
|
|
5938
|
+
...browseRoots.map((root) => (0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { key: root, variant: "ghost", size: "sm", onClick: () => {
|
|
5939
|
+
loadLevel(root);
|
|
5940
|
+
} }, root.replace(/[\\/]+$/u, "")))
|
|
5941
|
+
) : null,
|
|
5942
|
+
// Breadcrumb: the current level with every ancestor clickable — the
|
|
5943
|
+
// visible answer to "where am I / how do I go up".
|
|
5944
|
+
(0, import_react4.createElement)(
|
|
5945
|
+
"div",
|
|
5946
|
+
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: 2, marginBottom: 6, minHeight: 22 } },
|
|
5947
|
+
browseCurrent === "" ? (0, import_react4.createElement)("span", { style: styles.sourceText }, "\u4E3B\u76EE\u5F55") : (0, import_react4.createElement)(
|
|
5948
|
+
"span",
|
|
5949
|
+
{ style: { display: "inline-flex", alignItems: "center", flexWrap: "wrap", gap: 2 } },
|
|
5950
|
+
...breadcrumbSegments(browseCurrent).map(
|
|
5951
|
+
(seg) => (0, import_react4.createElement)("button", {
|
|
5952
|
+
key: seg.path,
|
|
5953
|
+
type: "button",
|
|
5954
|
+
onClick: () => {
|
|
5955
|
+
loadLevel(seg.path);
|
|
5956
|
+
},
|
|
5957
|
+
title: seg.path,
|
|
5958
|
+
style: { border: "none", background: "transparent", cursor: "pointer", padding: "1px 3px", borderRadius: 4, fontSize: 12, color: "var(--dsw-alias-label-secondary)" }
|
|
5959
|
+
}, seg.label + " \u203A")
|
|
5960
|
+
)
|
|
5961
|
+
)
|
|
5962
|
+
),
|
|
5963
|
+
browseError !== "" ? (0, import_react4.createElement)("p", { style: { ...styles.status, color: "var(--dsw-alias-state-error-primary)" } }, browseError) : null,
|
|
5964
|
+
(0, import_react4.createElement)(
|
|
5965
|
+
"div",
|
|
5966
|
+
{ style: { display: "flex", gap: 8, height: 260 } },
|
|
5967
|
+
(0, import_react4.createElement)(
|
|
5968
|
+
"div",
|
|
5969
|
+
{ style: paneStyle },
|
|
5970
|
+
browseEntries.length === 0 && browseError === "" ? (0, import_react4.createElement)("p", { style: styles.sourceText }, "\uFF08\u6B64\u76EE\u5F55\u4E0B\u6CA1\u6709\u5B50\u76EE\u5F55\uFF09") : null,
|
|
5971
|
+
...browseEntries.map((entry) => renderDirRow(entry, browseSelected?.name === entry.name, () => {
|
|
5972
|
+
selectRow(entry);
|
|
5973
|
+
}))
|
|
5974
|
+
),
|
|
5975
|
+
browseSelected !== null ? (0, import_react4.createElement)(
|
|
5976
|
+
"div",
|
|
5977
|
+
{ style: paneStyle },
|
|
5978
|
+
browseChildEntries.length === 0 ? (0, import_react4.createElement)("p", { style: styles.sourceText }, "\uFF08\u6CA1\u6709\u5B50\u76EE\u5F55\uFF09") : null,
|
|
5979
|
+
...browseChildEntries.map((entry) => renderDirRow(entry, false, () => {
|
|
5980
|
+
selectChildRow(entry);
|
|
5981
|
+
}))
|
|
5982
|
+
) : null
|
|
5983
|
+
)
|
|
5984
|
+
);
|
|
5717
5985
|
return (0, import_react4.createElement)(
|
|
5718
5986
|
"div",
|
|
5719
5987
|
{ style: styles.section },
|
|
@@ -5738,6 +6006,7 @@ function SkillManager({ workspace, remote }) {
|
|
|
5738
6006
|
)
|
|
5739
6007
|
),
|
|
5740
6008
|
installing ? renderInstallForm() : null,
|
|
6009
|
+
browsing ? renderBrowseModal() : null,
|
|
5741
6010
|
formOpen && !installing ? renderForm() : null
|
|
5742
6011
|
);
|
|
5743
6012
|
}
|