dsh-plugin-capabilities 0.3.3 → 0.3.5
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 +1 -1
- package/lib/client.js +3134 -324
- package/lib/client.js.map +4 -4
- package/lib/index.js +133 -56
- package/lib/index.js.map +4 -4
- package/package.json +3 -1
- package/src/client/MarkdownPreview.tsx +21 -0
- package/src/client/SkillsTab.tsx +7 -5
- package/src/client/css.ts +15 -0
- package/src/client/locales.ts +2 -2
- package/src/client/primitives.d.ts +0 -6
- package/src/repos.test.ts +5 -2
- package/src/repos.ts +6 -5
- package/src/rmtree.test.ts +26 -0
- package/src/rmtree.ts +46 -0
- package/src/routes.ts +56 -10
- package/src/skills.test.ts +24 -1
- package/src/skills.ts +23 -0
- package/src/smoke.test.ts +16 -1
- package/src/state.test.ts +8 -3
- package/src/state.ts +12 -8
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { existsSync as existsSync7 } from "node:fs";
|
|
3
|
-
import { dirname as dirname4, join as
|
|
3
|
+
import { dirname as dirname4, join as join11 } from "node:path";
|
|
4
4
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
5
5
|
|
|
6
6
|
// src/agents.ts
|
|
@@ -8,7 +8,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
11
|
-
// node_modules/smol-toml/dist/date.js
|
|
11
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/date.js
|
|
12
12
|
var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
|
|
13
13
|
var TomlDate = class _TomlDate extends Date {
|
|
14
14
|
#hasDate = false;
|
|
@@ -100,7 +100,7 @@ var TomlDate = class _TomlDate extends Date {
|
|
|
100
100
|
}
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
-
// node_modules/smol-toml/dist/error.js
|
|
103
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/error.js
|
|
104
104
|
function getLineColFromPtr(string, ptr) {
|
|
105
105
|
let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g);
|
|
106
106
|
return [lines.length, lines.pop().length + 1];
|
|
@@ -140,7 +140,7 @@ ${codeblock}`, options);
|
|
|
140
140
|
}
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
-
// node_modules/smol-toml/dist/util.js
|
|
143
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/util.js
|
|
144
144
|
function indexOfNewline(str, start = 0) {
|
|
145
145
|
let idx = str.indexOf("\n", start);
|
|
146
146
|
if (str.charCodeAt(idx - 1) === 13)
|
|
@@ -174,7 +174,7 @@ function skipVoid(ctx, banNewLines, banComments) {
|
|
|
174
174
|
skipComment(ctx);
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
-
function skipUntil(ctx,
|
|
177
|
+
function skipUntil(ctx, sep2, end) {
|
|
178
178
|
let ptr = ctx.p;
|
|
179
179
|
if (!end) {
|
|
180
180
|
ptr = indexOfNewline(ctx.s, ptr);
|
|
@@ -185,7 +185,7 @@ function skipUntil(ctx, sep, end) {
|
|
|
185
185
|
let c = ctx.s.charCodeAt(ctx.p);
|
|
186
186
|
if (c === 35) {
|
|
187
187
|
skipComment(ctx);
|
|
188
|
-
} else if (c === end || c ===
|
|
188
|
+
} else if (c === end || c === sep2) {
|
|
189
189
|
return;
|
|
190
190
|
}
|
|
191
191
|
}
|
|
@@ -195,7 +195,7 @@ function skipUntil(ctx, sep, end) {
|
|
|
195
195
|
});
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
// node_modules/smol-toml/dist/primitive.js
|
|
198
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/primitive.js
|
|
199
199
|
var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
|
|
200
200
|
var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
|
|
201
201
|
var LEADING_ZERO = /^[+-]?0[0-9_]/;
|
|
@@ -351,7 +351,7 @@ function parseValue(ctx, integersAsBigInt, end) {
|
|
|
351
351
|
return date;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
// node_modules/smol-toml/dist/extract.js
|
|
354
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/extract.js
|
|
355
355
|
function extractValue(ctx, end, integersAsBigInt) {
|
|
356
356
|
let ptr = ctx.p;
|
|
357
357
|
let c = ctx.s.charCodeAt(ptr);
|
|
@@ -384,7 +384,7 @@ function extractValue(ctx, end, integersAsBigInt) {
|
|
|
384
384
|
return parseValue(ctx, integersAsBigInt, end);
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
-
// node_modules/smol-toml/dist/struct.js
|
|
387
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/struct.js
|
|
388
388
|
var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
|
|
389
389
|
function parseKey(ctx, end = "=") {
|
|
390
390
|
let start = ctx.p;
|
|
@@ -526,7 +526,7 @@ function parseArray(ctx, integersAsBigInt) {
|
|
|
526
526
|
});
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
-
// node_modules/smol-toml/dist/parse.js
|
|
529
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/parse.js
|
|
530
530
|
function peekTable(key, table, meta, type) {
|
|
531
531
|
let t = table;
|
|
532
532
|
let m = meta;
|
|
@@ -771,6 +771,7 @@ function profileDir(profile, dshHome = process.env.DSH_HOME) {
|
|
|
771
771
|
|
|
772
772
|
// src/routes.ts
|
|
773
773
|
import { mkdirSync as mkdirSync6 } from "node:fs";
|
|
774
|
+
import { join as join10, sep } from "node:path";
|
|
774
775
|
|
|
775
776
|
// src/http.ts
|
|
776
777
|
async function readJsonBody(request) {
|
|
@@ -948,19 +949,58 @@ function openDirectory(dir) {
|
|
|
948
949
|
}
|
|
949
950
|
|
|
950
951
|
// src/repos.ts
|
|
951
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, readdirSync
|
|
952
|
-
import { basename, join as
|
|
952
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, symlinkSync } from "node:fs";
|
|
953
|
+
import { basename, join as join7, resolve } from "node:path";
|
|
954
|
+
|
|
955
|
+
// src/rmtree.ts
|
|
956
|
+
import { chmodSync, readdirSync, rmSync } from "node:fs";
|
|
957
|
+
import { join as join4 } from "node:path";
|
|
958
|
+
var RETRIES = { maxRetries: 10, retryDelay: 200 };
|
|
959
|
+
function clearReadOnly(dir) {
|
|
960
|
+
let entries;
|
|
961
|
+
try {
|
|
962
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
963
|
+
} catch {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
for (const entry of entries) {
|
|
967
|
+
const child = join4(dir, entry.name);
|
|
968
|
+
if (entry.isDirectory()) {
|
|
969
|
+
try {
|
|
970
|
+
chmodSync(child, 511);
|
|
971
|
+
} catch {
|
|
972
|
+
}
|
|
973
|
+
clearReadOnly(child);
|
|
974
|
+
} else if (entry.isFile()) {
|
|
975
|
+
try {
|
|
976
|
+
chmodSync(child, 438);
|
|
977
|
+
} catch {
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
try {
|
|
982
|
+
chmodSync(dir, 511);
|
|
983
|
+
} catch {
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
function removeTree(path) {
|
|
987
|
+
try {
|
|
988
|
+
clearReadOnly(path);
|
|
989
|
+
} catch {
|
|
990
|
+
}
|
|
991
|
+
rmSync(path, { recursive: true, force: true, ...RETRIES });
|
|
992
|
+
}
|
|
953
993
|
|
|
954
994
|
// src/state.ts
|
|
955
|
-
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3,
|
|
995
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "node:fs";
|
|
956
996
|
import { homedir as homedir3 } from "node:os";
|
|
957
|
-
import { join as
|
|
997
|
+
import { join as join5 } from "node:path";
|
|
958
998
|
import { randomBytes } from "node:crypto";
|
|
959
999
|
function pluginStateDir(dshHome = process.env.DSH_HOME) {
|
|
960
|
-
return
|
|
1000
|
+
return join5(dshHome ?? join5(homedir3(), ".dsh"), "dsh-plugin-capabilities");
|
|
961
1001
|
}
|
|
962
1002
|
function statePath(dshHome) {
|
|
963
|
-
return
|
|
1003
|
+
return join5(pluginStateDir(dshHome), "state.json");
|
|
964
1004
|
}
|
|
965
1005
|
function emptyState() {
|
|
966
1006
|
return { skillRoots: [] };
|
|
@@ -986,7 +1026,7 @@ function newEntryId(kind) {
|
|
|
986
1026
|
return `${kind}-${randomBytes(4).toString("hex")}`;
|
|
987
1027
|
}
|
|
988
1028
|
function materialDirFor(entryId, dshHome) {
|
|
989
|
-
return
|
|
1029
|
+
return join5(pluginStateDir(dshHome), "repos", entryId);
|
|
990
1030
|
}
|
|
991
1031
|
function addSkillRoot(entry, dshHome) {
|
|
992
1032
|
const state = loadState(dshHome);
|
|
@@ -998,13 +1038,10 @@ function addSkillRoot(entry, dshHome) {
|
|
|
998
1038
|
function removeSkillRoot(id, dshHome) {
|
|
999
1039
|
const state = loadState(dshHome);
|
|
1000
1040
|
const at = state.skillRoots.findIndex((entry) => entry.id === id);
|
|
1001
|
-
if (at === -1) return
|
|
1041
|
+
if (at === -1) return void 0;
|
|
1002
1042
|
const [removed] = state.skillRoots.splice(at, 1);
|
|
1003
1043
|
saveState(state, dshHome);
|
|
1004
|
-
|
|
1005
|
-
rmSync(removed.materialDir, { recursive: true, force: true, maxRetries: 2 });
|
|
1006
|
-
}
|
|
1007
|
-
return true;
|
|
1044
|
+
return removed;
|
|
1008
1045
|
}
|
|
1009
1046
|
function findRootByUrl(url, dshHome) {
|
|
1010
1047
|
return loadState(dshHome).skillRoots.find((entry) => entry.url === url);
|
|
@@ -1013,7 +1050,7 @@ function findRootByUrl(url, dshHome) {
|
|
|
1013
1050
|
// src/tar.ts
|
|
1014
1051
|
import { gunzipSync } from "node:zlib";
|
|
1015
1052
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1016
|
-
import { dirname as dirname2, join as
|
|
1053
|
+
import { dirname as dirname2, join as join6 } from "node:path";
|
|
1017
1054
|
var LIMITS = {
|
|
1018
1055
|
/** Per-entry cap (a skill repo holds markdown and small assets). */
|
|
1019
1056
|
entryBytes: 64 * 1024 * 1024,
|
|
@@ -1054,7 +1091,7 @@ function safeJoin(target, name2) {
|
|
|
1054
1091
|
parts.push(part);
|
|
1055
1092
|
}
|
|
1056
1093
|
if (parts.length === 0) return null;
|
|
1057
|
-
return
|
|
1094
|
+
return join6(target, ...parts);
|
|
1058
1095
|
}
|
|
1059
1096
|
function paxRecords(content) {
|
|
1060
1097
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -1158,42 +1195,42 @@ function looksLikeSkillFile(file) {
|
|
|
1158
1195
|
function holdsSkills(dir) {
|
|
1159
1196
|
let entries;
|
|
1160
1197
|
try {
|
|
1161
|
-
entries =
|
|
1198
|
+
entries = readdirSync2(dir);
|
|
1162
1199
|
} catch {
|
|
1163
1200
|
return false;
|
|
1164
1201
|
}
|
|
1165
|
-
if (existsSync4(
|
|
1202
|
+
if (existsSync4(join7(dir, "SKILL.md"))) return true;
|
|
1166
1203
|
for (const name2 of entries) {
|
|
1167
|
-
if (name2.endsWith(".md") && looksLikeSkillFile(
|
|
1204
|
+
if (name2.endsWith(".md") && looksLikeSkillFile(join7(dir, name2))) return true;
|
|
1168
1205
|
}
|
|
1169
1206
|
for (const name2 of entries) {
|
|
1170
|
-
const child =
|
|
1207
|
+
const child = join7(dir, name2);
|
|
1171
1208
|
try {
|
|
1172
|
-
if (statSync2(child).isDirectory() && existsSync4(
|
|
1209
|
+
if (statSync2(child).isDirectory() && existsSync4(join7(child, "SKILL.md"))) return true;
|
|
1173
1210
|
} catch {
|
|
1174
1211
|
}
|
|
1175
1212
|
}
|
|
1176
1213
|
return false;
|
|
1177
1214
|
}
|
|
1178
1215
|
function detectSkillRoots(checkout) {
|
|
1179
|
-
if (existsSync4(
|
|
1216
|
+
if (existsSync4(join7(checkout, "SKILL.md"))) return { roots: [], single: true };
|
|
1180
1217
|
const roots = [];
|
|
1181
1218
|
if (holdsSkills(checkout)) roots.push(checkout);
|
|
1182
1219
|
let entries;
|
|
1183
1220
|
try {
|
|
1184
|
-
entries =
|
|
1221
|
+
entries = readdirSync2(checkout);
|
|
1185
1222
|
} catch {
|
|
1186
1223
|
return { roots, single: false };
|
|
1187
1224
|
}
|
|
1188
1225
|
for (const name2 of entries) {
|
|
1189
1226
|
if (name2.startsWith(".")) continue;
|
|
1190
|
-
const child =
|
|
1227
|
+
const child = join7(checkout, name2);
|
|
1191
1228
|
try {
|
|
1192
1229
|
if (!statSync2(child).isDirectory()) continue;
|
|
1193
1230
|
} catch {
|
|
1194
1231
|
continue;
|
|
1195
1232
|
}
|
|
1196
|
-
if (existsSync4(
|
|
1233
|
+
if (existsSync4(join7(child, "SKILL.md"))) continue;
|
|
1197
1234
|
if (holdsSkills(child)) roots.push(child);
|
|
1198
1235
|
}
|
|
1199
1236
|
return { roots, single: false };
|
|
@@ -1210,12 +1247,12 @@ async function addLocalRepo(path, dshHome) {
|
|
|
1210
1247
|
if (detected.single) {
|
|
1211
1248
|
const id = newEntryId("local");
|
|
1212
1249
|
const material = materialDirFor(id, dshHome);
|
|
1213
|
-
|
|
1250
|
+
removeTree(material);
|
|
1214
1251
|
mkdirSync3(material, { recursive: true });
|
|
1215
1252
|
try {
|
|
1216
|
-
symlinkSync(resolved,
|
|
1253
|
+
symlinkSync(resolved, join7(material, "skill"), process.platform === "win32" ? "junction" : "dir");
|
|
1217
1254
|
} catch {
|
|
1218
|
-
|
|
1255
|
+
removeTree(material);
|
|
1219
1256
|
throw new Error("single-skill local folders need a directory link; try adding their parent folder instead");
|
|
1220
1257
|
}
|
|
1221
1258
|
return addSkillRoot({ id, kind: "local", label: basename(resolved), path: resolved, roots: [material], materialDir: material }, dshHome);
|
|
@@ -1235,8 +1272,8 @@ async function addGitRepo(url, options = {}) {
|
|
|
1235
1272
|
if (archive.byteLength > TARBALL_MAX_BYTES) throw new Error("repository tarball too large");
|
|
1236
1273
|
const id = newEntryId("git");
|
|
1237
1274
|
const material = materialDirFor(id, options.dshHome);
|
|
1238
|
-
|
|
1239
|
-
const checkout =
|
|
1275
|
+
removeTree(material);
|
|
1276
|
+
const checkout = join7(material, "repo");
|
|
1240
1277
|
try {
|
|
1241
1278
|
mkdirSync3(checkout, { recursive: true });
|
|
1242
1279
|
extractTarGz(archive, checkout, { stripComponents: 1 });
|
|
@@ -1250,7 +1287,7 @@ async function addGitRepo(url, options = {}) {
|
|
|
1250
1287
|
options.dshHome
|
|
1251
1288
|
);
|
|
1252
1289
|
} catch (error) {
|
|
1253
|
-
|
|
1290
|
+
removeTree(material);
|
|
1254
1291
|
throw error;
|
|
1255
1292
|
}
|
|
1256
1293
|
}
|
|
@@ -1309,12 +1346,12 @@ function restartOwnedByShell(env = process.env) {
|
|
|
1309
1346
|
}
|
|
1310
1347
|
|
|
1311
1348
|
// src/skills.ts
|
|
1312
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, rmSync as
|
|
1349
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1313
1350
|
import { homedir as homedir4 } from "node:os";
|
|
1314
|
-
import { join as
|
|
1351
|
+
import { join as join8 } from "node:path";
|
|
1315
1352
|
var SKILL_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1316
1353
|
function userSkillsDir(dshHome = process.env.DSH_HOME) {
|
|
1317
|
-
return
|
|
1354
|
+
return join8(dshHome ?? join8(homedir4(), ".dsh"), "skills");
|
|
1318
1355
|
}
|
|
1319
1356
|
function quote(value) {
|
|
1320
1357
|
return JSON.stringify(value);
|
|
@@ -1344,12 +1381,12 @@ function validateSkillInput(input) {
|
|
|
1344
1381
|
return null;
|
|
1345
1382
|
}
|
|
1346
1383
|
function skillDir(name2, dshHome) {
|
|
1347
|
-
return
|
|
1384
|
+
return join8(userSkillsDir(dshHome), name2);
|
|
1348
1385
|
}
|
|
1349
1386
|
function writeSkill(input, dshHome) {
|
|
1350
1387
|
const dir = skillDir(input.name, dshHome);
|
|
1351
1388
|
mkdirSync4(dir, { recursive: true });
|
|
1352
|
-
const file =
|
|
1389
|
+
const file = join8(dir, "SKILL.md");
|
|
1353
1390
|
writeFileSync3(file, serializeSkill(input), "utf8");
|
|
1354
1391
|
return file;
|
|
1355
1392
|
}
|
|
@@ -1357,7 +1394,7 @@ function deleteSkill(name2, dshHome) {
|
|
|
1357
1394
|
if (!SKILL_NAME_RE.test(name2)) return false;
|
|
1358
1395
|
const dir = skillDir(name2, dshHome);
|
|
1359
1396
|
if (!existsSync5(dir) || !statSync3(dir).isDirectory()) return false;
|
|
1360
|
-
|
|
1397
|
+
rmSync2(dir, { recursive: true, force: true });
|
|
1361
1398
|
return true;
|
|
1362
1399
|
}
|
|
1363
1400
|
function setSkillPolicy(file, enabled) {
|
|
@@ -1371,15 +1408,29 @@ function setSkillPolicy(file, enabled) {
|
|
|
1371
1408
|
if (!enabled) kept.push("disable-model-invocation: true", "user-invocable: false");
|
|
1372
1409
|
writeFileSync3(file, `---${newline}${kept.join(newline)}${newline}---${body}`, "utf8");
|
|
1373
1410
|
}
|
|
1411
|
+
function updateSkillFile(file, input) {
|
|
1412
|
+
const text = readFileSync5(file, "utf8");
|
|
1413
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
|
|
1414
|
+
if (match === null) throw new Error("skill file has no frontmatter block");
|
|
1415
|
+
const newline = text.includes("\r\n---") ? "\r\n" : "\n";
|
|
1416
|
+
const lines = match[1].split(/\r?\n/);
|
|
1417
|
+
const kept = lines.filter((line) => !/^(name|description|whenToUse|disable-model-invocation|user-invocable):/.test(line));
|
|
1418
|
+
const head = [`name: ${input.name}`, `description: ${quote(input.description)}`];
|
|
1419
|
+
if (input.whenToUse !== void 0 && input.whenToUse !== "") head.push(`whenToUse: ${quote(input.whenToUse)}`);
|
|
1420
|
+
if (!input.modelInvocable) kept.push("disable-model-invocation: true");
|
|
1421
|
+
if (!input.userInvocable) kept.push("user-invocable: false");
|
|
1422
|
+
const body = input.content.replace(/\r\n/g, "\n").trim();
|
|
1423
|
+
writeFileSync3(file, `---${newline}${[...head, ...kept].join(newline)}${newline}---${newline}${newline}${body}${newline}`, "utf8");
|
|
1424
|
+
}
|
|
1374
1425
|
|
|
1375
1426
|
// src/mcp.ts
|
|
1376
1427
|
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1377
|
-
import { join as
|
|
1428
|
+
import { join as join9 } from "node:path";
|
|
1378
1429
|
import { parseDocument, Document } from "yaml";
|
|
1379
1430
|
var MCP_PLUGIN = "@deepseek-ai/dsh-mcp-client";
|
|
1380
1431
|
var SERVER_NAME_RE = /^[A-Za-z0-9_-]{1,32}$/;
|
|
1381
1432
|
function loadPatch(profileDirPath) {
|
|
1382
|
-
const path =
|
|
1433
|
+
const path = join9(profileDirPath, "cordis.patch.yml");
|
|
1383
1434
|
const text = existsSync6(path) ? readFileSync6(path, "utf8") : "[]";
|
|
1384
1435
|
const doc = parseDocument(text);
|
|
1385
1436
|
const contents = doc.contents;
|
|
@@ -1388,7 +1439,7 @@ function loadPatch(profileDirPath) {
|
|
|
1388
1439
|
}
|
|
1389
1440
|
function savePatch(profileDirPath, doc) {
|
|
1390
1441
|
mkdirSync5(profileDirPath, { recursive: true });
|
|
1391
|
-
writeFileSync4(
|
|
1442
|
+
writeFileSync4(join9(profileDirPath, "cordis.patch.yml"), String(doc), "utf8");
|
|
1392
1443
|
}
|
|
1393
1444
|
function toNode(value) {
|
|
1394
1445
|
return new Document(value).contents;
|
|
@@ -1550,10 +1601,25 @@ function removeMcp(profileDirPath, id) {
|
|
|
1550
1601
|
}
|
|
1551
1602
|
|
|
1552
1603
|
// src/routes.ts
|
|
1553
|
-
|
|
1554
|
-
|
|
1604
|
+
function customSkillWritable(dir, dshHome) {
|
|
1605
|
+
const state = pluginStateDir(dshHome);
|
|
1606
|
+
if (dir === state || dir.startsWith(state + sep)) return true;
|
|
1607
|
+
return loadState(dshHome).skillRoots.some((entry) => entry.roots.some((root) => dir === root || dir.startsWith(root + sep)));
|
|
1608
|
+
}
|
|
1609
|
+
function skillWritable(skill, dir, dshHome) {
|
|
1610
|
+
if (dir === void 0) return false;
|
|
1611
|
+
if (skill.source === "user-dsh") return true;
|
|
1612
|
+
return skill.source === "custom" && customSkillWritable(dir, dshHome);
|
|
1613
|
+
}
|
|
1614
|
+
function toSkillRow(skill, dshHome) {
|
|
1555
1615
|
const dir = skill.resourceBase?.kind === "directory" ? skill.resourceBase.path : void 0;
|
|
1556
|
-
return {
|
|
1616
|
+
return {
|
|
1617
|
+
...skill,
|
|
1618
|
+
editable: skillWritable(skill, dir, dshHome),
|
|
1619
|
+
removable: skill.source === "user-dsh",
|
|
1620
|
+
...dir !== void 0 ? { dir } : {},
|
|
1621
|
+
policyEditable: dir !== void 0
|
|
1622
|
+
};
|
|
1557
1623
|
}
|
|
1558
1624
|
function toRootView(entry) {
|
|
1559
1625
|
return { ...entry, live: entry.roots.every((root) => rootExists(root)) };
|
|
@@ -1571,7 +1637,7 @@ function mountCapabilitiesRoutes(host, config) {
|
|
|
1571
1637
|
}
|
|
1572
1638
|
try {
|
|
1573
1639
|
const skills = await host.skills.list();
|
|
1574
|
-
sendJson(response, 200, { skills: skills.map(toSkillRow) });
|
|
1640
|
+
sendJson(response, 200, { skills: skills.map((skill) => toSkillRow(skill, process.env.DSH_HOME)) });
|
|
1575
1641
|
} catch (error) {
|
|
1576
1642
|
sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
1577
1643
|
}
|
|
@@ -1628,7 +1694,17 @@ function mountCapabilitiesRoutes(host, config) {
|
|
|
1628
1694
|
sendJson(response, 400, { error: invalid });
|
|
1629
1695
|
return;
|
|
1630
1696
|
}
|
|
1631
|
-
|
|
1697
|
+
const existing = (await host.skills.list()).find((skill) => skill.name === input.name);
|
|
1698
|
+
if (existing !== void 0) {
|
|
1699
|
+
const dir = existing.resourceBase?.kind === "directory" ? existing.resourceBase.path : void 0;
|
|
1700
|
+
if (!skillWritable(existing, dir, process.env.DSH_HOME)) {
|
|
1701
|
+
sendJson(response, 403, { error: `skills from source '${existing.source}' are read-only` });
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
updateSkillFile(join10(dir, "SKILL.md"), input);
|
|
1705
|
+
} else {
|
|
1706
|
+
writeSkill(input);
|
|
1707
|
+
}
|
|
1632
1708
|
sendJson(response, 200, { ok: true, name: input.name });
|
|
1633
1709
|
} catch (error) {
|
|
1634
1710
|
sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
@@ -1817,12 +1893,13 @@ function mountCapabilitiesRoutes(host, config) {
|
|
|
1817
1893
|
sendJson(response, 400, { error: "id is required" });
|
|
1818
1894
|
return;
|
|
1819
1895
|
}
|
|
1820
|
-
const
|
|
1821
|
-
if (
|
|
1896
|
+
const removed = removeSkillRoot(body.id);
|
|
1897
|
+
if (removed === void 0) {
|
|
1822
1898
|
sendJson(response, 404, { error: "repository not found" });
|
|
1823
1899
|
return;
|
|
1824
1900
|
}
|
|
1825
1901
|
await config.remountProvider();
|
|
1902
|
+
if (removed.materialDir !== void 0) removeTree(removed.materialDir);
|
|
1826
1903
|
sendJson(response, 200, { ok: true });
|
|
1827
1904
|
} catch (error) {
|
|
1828
1905
|
sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
@@ -2131,7 +2208,7 @@ function mountCapabilitiesRoutes(host, config) {
|
|
|
2131
2208
|
// src/index.ts
|
|
2132
2209
|
var name = "dsh-plugin-capabilities";
|
|
2133
2210
|
function packagedSkillsDir() {
|
|
2134
|
-
return
|
|
2211
|
+
return join11(dirname4(fileURLToPath2(import.meta.url)), "..", "skills");
|
|
2135
2212
|
}
|
|
2136
2213
|
var inject = ["webServer", "skills"];
|
|
2137
2214
|
function apply(ctx, config) {
|