dsh-plugin-capabilities 0.3.4 → 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/lib/client.js +2 -2
- package/lib/client.js.map +1 -1
- package/lib/index.js +88 -51
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
- 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 +8 -2
- 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)
|
|
@@ -195,7 +195,7 @@ function skipUntil(ctx, sep2, 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,7 +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
|
|
774
|
+
import { join as join10, sep } from "node:path";
|
|
775
775
|
|
|
776
776
|
// src/http.ts
|
|
777
777
|
async function readJsonBody(request) {
|
|
@@ -949,19 +949,58 @@ function openDirectory(dir) {
|
|
|
949
949
|
}
|
|
950
950
|
|
|
951
951
|
// src/repos.ts
|
|
952
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, readdirSync
|
|
953
|
-
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
|
+
}
|
|
954
993
|
|
|
955
994
|
// src/state.ts
|
|
956
|
-
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3,
|
|
995
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "node:fs";
|
|
957
996
|
import { homedir as homedir3 } from "node:os";
|
|
958
|
-
import { join as
|
|
997
|
+
import { join as join5 } from "node:path";
|
|
959
998
|
import { randomBytes } from "node:crypto";
|
|
960
999
|
function pluginStateDir(dshHome = process.env.DSH_HOME) {
|
|
961
|
-
return
|
|
1000
|
+
return join5(dshHome ?? join5(homedir3(), ".dsh"), "dsh-plugin-capabilities");
|
|
962
1001
|
}
|
|
963
1002
|
function statePath(dshHome) {
|
|
964
|
-
return
|
|
1003
|
+
return join5(pluginStateDir(dshHome), "state.json");
|
|
965
1004
|
}
|
|
966
1005
|
function emptyState() {
|
|
967
1006
|
return { skillRoots: [] };
|
|
@@ -987,7 +1026,7 @@ function newEntryId(kind) {
|
|
|
987
1026
|
return `${kind}-${randomBytes(4).toString("hex")}`;
|
|
988
1027
|
}
|
|
989
1028
|
function materialDirFor(entryId, dshHome) {
|
|
990
|
-
return
|
|
1029
|
+
return join5(pluginStateDir(dshHome), "repos", entryId);
|
|
991
1030
|
}
|
|
992
1031
|
function addSkillRoot(entry, dshHome) {
|
|
993
1032
|
const state = loadState(dshHome);
|
|
@@ -999,13 +1038,10 @@ function addSkillRoot(entry, dshHome) {
|
|
|
999
1038
|
function removeSkillRoot(id, dshHome) {
|
|
1000
1039
|
const state = loadState(dshHome);
|
|
1001
1040
|
const at = state.skillRoots.findIndex((entry) => entry.id === id);
|
|
1002
|
-
if (at === -1) return
|
|
1041
|
+
if (at === -1) return void 0;
|
|
1003
1042
|
const [removed] = state.skillRoots.splice(at, 1);
|
|
1004
1043
|
saveState(state, dshHome);
|
|
1005
|
-
|
|
1006
|
-
rmSync(removed.materialDir, { recursive: true, force: true, maxRetries: 2 });
|
|
1007
|
-
}
|
|
1008
|
-
return true;
|
|
1044
|
+
return removed;
|
|
1009
1045
|
}
|
|
1010
1046
|
function findRootByUrl(url, dshHome) {
|
|
1011
1047
|
return loadState(dshHome).skillRoots.find((entry) => entry.url === url);
|
|
@@ -1014,7 +1050,7 @@ function findRootByUrl(url, dshHome) {
|
|
|
1014
1050
|
// src/tar.ts
|
|
1015
1051
|
import { gunzipSync } from "node:zlib";
|
|
1016
1052
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1017
|
-
import { dirname as dirname2, join as
|
|
1053
|
+
import { dirname as dirname2, join as join6 } from "node:path";
|
|
1018
1054
|
var LIMITS = {
|
|
1019
1055
|
/** Per-entry cap (a skill repo holds markdown and small assets). */
|
|
1020
1056
|
entryBytes: 64 * 1024 * 1024,
|
|
@@ -1055,7 +1091,7 @@ function safeJoin(target, name2) {
|
|
|
1055
1091
|
parts.push(part);
|
|
1056
1092
|
}
|
|
1057
1093
|
if (parts.length === 0) return null;
|
|
1058
|
-
return
|
|
1094
|
+
return join6(target, ...parts);
|
|
1059
1095
|
}
|
|
1060
1096
|
function paxRecords(content) {
|
|
1061
1097
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -1159,42 +1195,42 @@ function looksLikeSkillFile(file) {
|
|
|
1159
1195
|
function holdsSkills(dir) {
|
|
1160
1196
|
let entries;
|
|
1161
1197
|
try {
|
|
1162
|
-
entries =
|
|
1198
|
+
entries = readdirSync2(dir);
|
|
1163
1199
|
} catch {
|
|
1164
1200
|
return false;
|
|
1165
1201
|
}
|
|
1166
|
-
if (existsSync4(
|
|
1202
|
+
if (existsSync4(join7(dir, "SKILL.md"))) return true;
|
|
1167
1203
|
for (const name2 of entries) {
|
|
1168
|
-
if (name2.endsWith(".md") && looksLikeSkillFile(
|
|
1204
|
+
if (name2.endsWith(".md") && looksLikeSkillFile(join7(dir, name2))) return true;
|
|
1169
1205
|
}
|
|
1170
1206
|
for (const name2 of entries) {
|
|
1171
|
-
const child =
|
|
1207
|
+
const child = join7(dir, name2);
|
|
1172
1208
|
try {
|
|
1173
|
-
if (statSync2(child).isDirectory() && existsSync4(
|
|
1209
|
+
if (statSync2(child).isDirectory() && existsSync4(join7(child, "SKILL.md"))) return true;
|
|
1174
1210
|
} catch {
|
|
1175
1211
|
}
|
|
1176
1212
|
}
|
|
1177
1213
|
return false;
|
|
1178
1214
|
}
|
|
1179
1215
|
function detectSkillRoots(checkout) {
|
|
1180
|
-
if (existsSync4(
|
|
1216
|
+
if (existsSync4(join7(checkout, "SKILL.md"))) return { roots: [], single: true };
|
|
1181
1217
|
const roots = [];
|
|
1182
1218
|
if (holdsSkills(checkout)) roots.push(checkout);
|
|
1183
1219
|
let entries;
|
|
1184
1220
|
try {
|
|
1185
|
-
entries =
|
|
1221
|
+
entries = readdirSync2(checkout);
|
|
1186
1222
|
} catch {
|
|
1187
1223
|
return { roots, single: false };
|
|
1188
1224
|
}
|
|
1189
1225
|
for (const name2 of entries) {
|
|
1190
1226
|
if (name2.startsWith(".")) continue;
|
|
1191
|
-
const child =
|
|
1227
|
+
const child = join7(checkout, name2);
|
|
1192
1228
|
try {
|
|
1193
1229
|
if (!statSync2(child).isDirectory()) continue;
|
|
1194
1230
|
} catch {
|
|
1195
1231
|
continue;
|
|
1196
1232
|
}
|
|
1197
|
-
if (existsSync4(
|
|
1233
|
+
if (existsSync4(join7(child, "SKILL.md"))) continue;
|
|
1198
1234
|
if (holdsSkills(child)) roots.push(child);
|
|
1199
1235
|
}
|
|
1200
1236
|
return { roots, single: false };
|
|
@@ -1211,12 +1247,12 @@ async function addLocalRepo(path, dshHome) {
|
|
|
1211
1247
|
if (detected.single) {
|
|
1212
1248
|
const id = newEntryId("local");
|
|
1213
1249
|
const material = materialDirFor(id, dshHome);
|
|
1214
|
-
|
|
1250
|
+
removeTree(material);
|
|
1215
1251
|
mkdirSync3(material, { recursive: true });
|
|
1216
1252
|
try {
|
|
1217
|
-
symlinkSync(resolved,
|
|
1253
|
+
symlinkSync(resolved, join7(material, "skill"), process.platform === "win32" ? "junction" : "dir");
|
|
1218
1254
|
} catch {
|
|
1219
|
-
|
|
1255
|
+
removeTree(material);
|
|
1220
1256
|
throw new Error("single-skill local folders need a directory link; try adding their parent folder instead");
|
|
1221
1257
|
}
|
|
1222
1258
|
return addSkillRoot({ id, kind: "local", label: basename(resolved), path: resolved, roots: [material], materialDir: material }, dshHome);
|
|
@@ -1236,8 +1272,8 @@ async function addGitRepo(url, options = {}) {
|
|
|
1236
1272
|
if (archive.byteLength > TARBALL_MAX_BYTES) throw new Error("repository tarball too large");
|
|
1237
1273
|
const id = newEntryId("git");
|
|
1238
1274
|
const material = materialDirFor(id, options.dshHome);
|
|
1239
|
-
|
|
1240
|
-
const checkout =
|
|
1275
|
+
removeTree(material);
|
|
1276
|
+
const checkout = join7(material, "repo");
|
|
1241
1277
|
try {
|
|
1242
1278
|
mkdirSync3(checkout, { recursive: true });
|
|
1243
1279
|
extractTarGz(archive, checkout, { stripComponents: 1 });
|
|
@@ -1251,7 +1287,7 @@ async function addGitRepo(url, options = {}) {
|
|
|
1251
1287
|
options.dshHome
|
|
1252
1288
|
);
|
|
1253
1289
|
} catch (error) {
|
|
1254
|
-
|
|
1290
|
+
removeTree(material);
|
|
1255
1291
|
throw error;
|
|
1256
1292
|
}
|
|
1257
1293
|
}
|
|
@@ -1310,12 +1346,12 @@ function restartOwnedByShell(env = process.env) {
|
|
|
1310
1346
|
}
|
|
1311
1347
|
|
|
1312
1348
|
// src/skills.ts
|
|
1313
|
-
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";
|
|
1314
1350
|
import { homedir as homedir4 } from "node:os";
|
|
1315
|
-
import { join as
|
|
1351
|
+
import { join as join8 } from "node:path";
|
|
1316
1352
|
var SKILL_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1317
1353
|
function userSkillsDir(dshHome = process.env.DSH_HOME) {
|
|
1318
|
-
return
|
|
1354
|
+
return join8(dshHome ?? join8(homedir4(), ".dsh"), "skills");
|
|
1319
1355
|
}
|
|
1320
1356
|
function quote(value) {
|
|
1321
1357
|
return JSON.stringify(value);
|
|
@@ -1345,12 +1381,12 @@ function validateSkillInput(input) {
|
|
|
1345
1381
|
return null;
|
|
1346
1382
|
}
|
|
1347
1383
|
function skillDir(name2, dshHome) {
|
|
1348
|
-
return
|
|
1384
|
+
return join8(userSkillsDir(dshHome), name2);
|
|
1349
1385
|
}
|
|
1350
1386
|
function writeSkill(input, dshHome) {
|
|
1351
1387
|
const dir = skillDir(input.name, dshHome);
|
|
1352
1388
|
mkdirSync4(dir, { recursive: true });
|
|
1353
|
-
const file =
|
|
1389
|
+
const file = join8(dir, "SKILL.md");
|
|
1354
1390
|
writeFileSync3(file, serializeSkill(input), "utf8");
|
|
1355
1391
|
return file;
|
|
1356
1392
|
}
|
|
@@ -1358,7 +1394,7 @@ function deleteSkill(name2, dshHome) {
|
|
|
1358
1394
|
if (!SKILL_NAME_RE.test(name2)) return false;
|
|
1359
1395
|
const dir = skillDir(name2, dshHome);
|
|
1360
1396
|
if (!existsSync5(dir) || !statSync3(dir).isDirectory()) return false;
|
|
1361
|
-
|
|
1397
|
+
rmSync2(dir, { recursive: true, force: true });
|
|
1362
1398
|
return true;
|
|
1363
1399
|
}
|
|
1364
1400
|
function setSkillPolicy(file, enabled) {
|
|
@@ -1389,12 +1425,12 @@ function updateSkillFile(file, input) {
|
|
|
1389
1425
|
|
|
1390
1426
|
// src/mcp.ts
|
|
1391
1427
|
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1392
|
-
import { join as
|
|
1428
|
+
import { join as join9 } from "node:path";
|
|
1393
1429
|
import { parseDocument, Document } from "yaml";
|
|
1394
1430
|
var MCP_PLUGIN = "@deepseek-ai/dsh-mcp-client";
|
|
1395
1431
|
var SERVER_NAME_RE = /^[A-Za-z0-9_-]{1,32}$/;
|
|
1396
1432
|
function loadPatch(profileDirPath) {
|
|
1397
|
-
const path =
|
|
1433
|
+
const path = join9(profileDirPath, "cordis.patch.yml");
|
|
1398
1434
|
const text = existsSync6(path) ? readFileSync6(path, "utf8") : "[]";
|
|
1399
1435
|
const doc = parseDocument(text);
|
|
1400
1436
|
const contents = doc.contents;
|
|
@@ -1403,7 +1439,7 @@ function loadPatch(profileDirPath) {
|
|
|
1403
1439
|
}
|
|
1404
1440
|
function savePatch(profileDirPath, doc) {
|
|
1405
1441
|
mkdirSync5(profileDirPath, { recursive: true });
|
|
1406
|
-
writeFileSync4(
|
|
1442
|
+
writeFileSync4(join9(profileDirPath, "cordis.patch.yml"), String(doc), "utf8");
|
|
1407
1443
|
}
|
|
1408
1444
|
function toNode(value) {
|
|
1409
1445
|
return new Document(value).contents;
|
|
@@ -1665,7 +1701,7 @@ function mountCapabilitiesRoutes(host, config) {
|
|
|
1665
1701
|
sendJson(response, 403, { error: `skills from source '${existing.source}' are read-only` });
|
|
1666
1702
|
return;
|
|
1667
1703
|
}
|
|
1668
|
-
updateSkillFile(
|
|
1704
|
+
updateSkillFile(join10(dir, "SKILL.md"), input);
|
|
1669
1705
|
} else {
|
|
1670
1706
|
writeSkill(input);
|
|
1671
1707
|
}
|
|
@@ -1857,12 +1893,13 @@ function mountCapabilitiesRoutes(host, config) {
|
|
|
1857
1893
|
sendJson(response, 400, { error: "id is required" });
|
|
1858
1894
|
return;
|
|
1859
1895
|
}
|
|
1860
|
-
const
|
|
1861
|
-
if (
|
|
1896
|
+
const removed = removeSkillRoot(body.id);
|
|
1897
|
+
if (removed === void 0) {
|
|
1862
1898
|
sendJson(response, 404, { error: "repository not found" });
|
|
1863
1899
|
return;
|
|
1864
1900
|
}
|
|
1865
1901
|
await config.remountProvider();
|
|
1902
|
+
if (removed.materialDir !== void 0) removeTree(removed.materialDir);
|
|
1866
1903
|
sendJson(response, 200, { ok: true });
|
|
1867
1904
|
} catch (error) {
|
|
1868
1905
|
sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
@@ -2171,7 +2208,7 @@ function mountCapabilitiesRoutes(host, config) {
|
|
|
2171
2208
|
// src/index.ts
|
|
2172
2209
|
var name = "dsh-plugin-capabilities";
|
|
2173
2210
|
function packagedSkillsDir() {
|
|
2174
|
-
return
|
|
2211
|
+
return join11(dirname4(fileURLToPath2(import.meta.url)), "..", "skills");
|
|
2175
2212
|
}
|
|
2176
2213
|
var inject = ["webServer", "skills"];
|
|
2177
2214
|
function apply(ctx, config) {
|