skill-creator 2.1.0 → 2.2.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/CHANGELOG.md +60 -0
- package/README.md +13 -0
- package/dist/cli.js +5373 -3653
- package/dist/cli.js.map +4 -4
- package/dist/daemon.js +2373 -828
- package/dist/daemon.js.map +4 -4
- package/dist/package.json +1 -1
- package/dist/webui/_app/immutable/assets/0.CT0Z92yp.css +2 -0
- package/dist/webui/_app/immutable/assets/ProviderView.BKgtsfqs.css +1 -0
- package/dist/webui/_app/immutable/chunks/0k4S-8Dm.js +1 -0
- package/dist/webui/_app/immutable/chunks/{NO3Cfsmo.js → BD8KMBgQ.js} +1 -1
- package/dist/webui/_app/immutable/chunks/{Dbn_qXWm.js → BLKqTz4e.js} +1 -1
- package/dist/webui/_app/immutable/chunks/BTdzDCtA.js +1 -0
- package/dist/webui/_app/immutable/chunks/{Bs27XW8_.js → BXwUnloU.js} +1 -1
- package/dist/webui/_app/immutable/chunks/BZOx0Qre.js +1 -0
- package/dist/webui/_app/immutable/chunks/BfKHhAfw.js +1 -0
- package/dist/webui/_app/immutable/chunks/Bm81ovyU.js +8 -0
- package/dist/webui/_app/immutable/chunks/{BKjuWHgW.js → BpM7GQdD.js} +3 -3
- package/dist/webui/_app/immutable/chunks/{CeR4do8T.js → BpPdtmVY.js} +1 -1
- package/dist/webui/_app/immutable/chunks/{BEPs9Z9s.js → C4d-joyd.js} +1 -1
- package/dist/webui/_app/immutable/chunks/{K56ab8zF.js → CRrxCnau.js} +1 -1
- package/dist/webui/_app/immutable/chunks/CUkgSGd5.js +1 -0
- package/dist/webui/_app/immutable/chunks/{hrRIAQd7.js → CX1CHr5A.js} +1 -1
- package/dist/webui/_app/immutable/chunks/CaRy05kX.js +1 -0
- package/dist/webui/_app/immutable/chunks/{UeEccMjN.js → CmWHiR19.js} +1 -1
- package/dist/webui/_app/immutable/chunks/CokLx9SK.js +3 -0
- package/dist/webui/_app/immutable/chunks/D-GGMKF5.js +1 -0
- package/dist/webui/_app/immutable/chunks/D00gXCrF.js +2 -0
- package/dist/webui/_app/immutable/chunks/{BIfnT_mK.js → DAvb_BTz.js} +1 -1
- package/dist/webui/_app/immutable/chunks/{CY3MARMs.js → DYphEAOV.js} +1 -1
- package/dist/webui/_app/immutable/chunks/{0umLG1IN.js → DlRJa6Rp.js} +1 -1
- package/dist/webui/_app/immutable/chunks/{D-V0xBeS.js → DsOfMPqr.js} +1 -1
- package/dist/webui/_app/immutable/chunks/DxUS3G9A.js +1 -0
- package/dist/webui/_app/immutable/chunks/{CQD9uzpA.js → LKyxHDTu.js} +1 -1
- package/dist/webui/_app/immutable/chunks/{Dl8eQ129.js → WSgbVOcX.js} +1 -1
- package/dist/webui/_app/immutable/chunks/{Bos6D7ir.js → b00Wice3.js} +2 -2
- package/dist/webui/_app/immutable/chunks/{Ckw1quWt.js → n3HFLfXO.js} +1 -1
- package/dist/webui/_app/immutable/chunks/{B0yogbIO.js → xHmEZGe9.js} +1 -1
- package/dist/webui/_app/immutable/chunks/{D8Der6oM.js → y5ROg0gY.js} +1 -1
- package/dist/webui/_app/immutable/entry/{app.CkIQoKd-.js → app.wmUVPoAL.js} +2 -2
- package/dist/webui/_app/immutable/entry/start.DeqHj237.js +1 -0
- package/dist/webui/_app/immutable/nodes/0.B1MRgs82.js +234 -0
- package/dist/webui/_app/immutable/nodes/{1.DxOotYUD.js → 1.DmFrzXB8.js} +1 -1
- package/dist/webui/_app/immutable/nodes/{2.BhwY9m4H.js → 2.C5_mWJeB.js} +1 -1
- package/dist/webui/_app/version.json +1 -1
- package/dist/webui/index.html +27 -27
- package/package.json +7 -4
- package/dist/webui/_app/immutable/assets/0.BfQ9GZ7H.css +0 -2
- package/dist/webui/_app/immutable/assets/ProviderView.DEH95hqa.css +0 -1
- package/dist/webui/_app/immutable/chunks/9b6xLvUw.js +0 -1
- package/dist/webui/_app/immutable/chunks/BDe0K-12.js +0 -1
- package/dist/webui/_app/immutable/chunks/BkEnd9_Z.js +0 -1
- package/dist/webui/_app/immutable/chunks/CIFG41pU.js +0 -1
- package/dist/webui/_app/immutable/chunks/CNPsST_i.js +0 -1
- package/dist/webui/_app/immutable/chunks/D-ihBChi.js +0 -1
- package/dist/webui/_app/immutable/chunks/D7i1h2eW.js +0 -2
- package/dist/webui/_app/immutable/chunks/DFDHkP9N.js +0 -1
- package/dist/webui/_app/immutable/chunks/DFdxGYRu.js +0 -8
- package/dist/webui/_app/immutable/chunks/QU8DIUJz.js +0 -3
- package/dist/webui/_app/immutable/chunks/UsOmL8ol.js +0 -1
- package/dist/webui/_app/immutable/entry/start.DG-jtr9O.js +0 -1
- package/dist/webui/_app/immutable/nodes/0.DY8kli0K.js +0 -234
package/dist/daemon.js
CHANGED
|
@@ -1783,28 +1783,28 @@ var init_create_prompt_CIJV_96B = __esm({
|
|
|
1783
1783
|
b = b > 0.04045 ? ((b + 0.055) / 1.055) ** 2.4 : b / 12.92;
|
|
1784
1784
|
const x = r * 0.4124 + g * 0.3576 + b * 0.1805;
|
|
1785
1785
|
const y = r * 0.2126 + g * 0.7152 + b * 0.0722;
|
|
1786
|
-
const
|
|
1786
|
+
const z31 = r * 0.0193 + g * 0.1192 + b * 0.9505;
|
|
1787
1787
|
return [
|
|
1788
1788
|
x * 100,
|
|
1789
1789
|
y * 100,
|
|
1790
|
-
|
|
1790
|
+
z31 * 100
|
|
1791
1791
|
];
|
|
1792
1792
|
};
|
|
1793
1793
|
convert$1.rgb.lab = function(rgb) {
|
|
1794
1794
|
const xyz = convert$1.rgb.xyz(rgb);
|
|
1795
1795
|
let x = xyz[0];
|
|
1796
1796
|
let y = xyz[1];
|
|
1797
|
-
let
|
|
1797
|
+
let z31 = xyz[2];
|
|
1798
1798
|
x /= 95.047;
|
|
1799
1799
|
y /= 100;
|
|
1800
|
-
|
|
1800
|
+
z31 /= 108.883;
|
|
1801
1801
|
x = x > 8856e-6 ? x ** (1 / 3) : 7.787 * x + 16 / 116;
|
|
1802
1802
|
y = y > 8856e-6 ? y ** (1 / 3) : 7.787 * y + 16 / 116;
|
|
1803
|
-
|
|
1803
|
+
z31 = z31 > 8856e-6 ? z31 ** (1 / 3) : 7.787 * z31 + 16 / 116;
|
|
1804
1804
|
return [
|
|
1805
1805
|
116 * y - 16,
|
|
1806
1806
|
500 * (x - y),
|
|
1807
|
-
200 * (y -
|
|
1807
|
+
200 * (y - z31)
|
|
1808
1808
|
];
|
|
1809
1809
|
};
|
|
1810
1810
|
convert$1.hsl.rgb = function(hsl) {
|
|
@@ -2001,13 +2001,13 @@ var init_create_prompt_CIJV_96B = __esm({
|
|
|
2001
2001
|
convert$1.xyz.rgb = function(xyz) {
|
|
2002
2002
|
const x = xyz[0] / 100;
|
|
2003
2003
|
const y = xyz[1] / 100;
|
|
2004
|
-
const
|
|
2004
|
+
const z31 = xyz[2] / 100;
|
|
2005
2005
|
let r;
|
|
2006
2006
|
let g;
|
|
2007
2007
|
let b;
|
|
2008
|
-
r = x * 3.2406 + y * -1.5372 +
|
|
2009
|
-
g = x * -0.9689 + y * 1.8758 +
|
|
2010
|
-
b = x * 0.0557 + y * -0.204 +
|
|
2008
|
+
r = x * 3.2406 + y * -1.5372 + z31 * -0.4986;
|
|
2009
|
+
g = x * -0.9689 + y * 1.8758 + z31 * 0.0415;
|
|
2010
|
+
b = x * 0.0557 + y * -0.204 + z31 * 1.057;
|
|
2011
2011
|
r = r > 31308e-7 ? 1.055 * r ** (1 / 2.4) - 0.055 : r * 12.92;
|
|
2012
2012
|
g = g > 31308e-7 ? 1.055 * g ** (1 / 2.4) - 0.055 : g * 12.92;
|
|
2013
2013
|
b = b > 31308e-7 ? 1.055 * b ** (1 / 2.4) - 0.055 : b * 12.92;
|
|
@@ -2023,17 +2023,17 @@ var init_create_prompt_CIJV_96B = __esm({
|
|
|
2023
2023
|
convert$1.xyz.lab = function(xyz) {
|
|
2024
2024
|
let x = xyz[0];
|
|
2025
2025
|
let y = xyz[1];
|
|
2026
|
-
let
|
|
2026
|
+
let z31 = xyz[2];
|
|
2027
2027
|
x /= 95.047;
|
|
2028
2028
|
y /= 100;
|
|
2029
|
-
|
|
2029
|
+
z31 /= 108.883;
|
|
2030
2030
|
x = x > 8856e-6 ? x ** (1 / 3) : 7.787 * x + 16 / 116;
|
|
2031
2031
|
y = y > 8856e-6 ? y ** (1 / 3) : 7.787 * y + 16 / 116;
|
|
2032
|
-
|
|
2032
|
+
z31 = z31 > 8856e-6 ? z31 ** (1 / 3) : 7.787 * z31 + 16 / 116;
|
|
2033
2033
|
return [
|
|
2034
2034
|
116 * y - 16,
|
|
2035
2035
|
500 * (x - y),
|
|
2036
|
-
200 * (y -
|
|
2036
|
+
200 * (y - z31)
|
|
2037
2037
|
];
|
|
2038
2038
|
};
|
|
2039
2039
|
convert$1.lab.xyz = function(lab) {
|
|
@@ -2042,23 +2042,23 @@ var init_create_prompt_CIJV_96B = __esm({
|
|
|
2042
2042
|
const b = lab[2];
|
|
2043
2043
|
let x;
|
|
2044
2044
|
let y;
|
|
2045
|
-
let
|
|
2045
|
+
let z31;
|
|
2046
2046
|
y = (l + 16) / 116;
|
|
2047
2047
|
x = a / 500 + y;
|
|
2048
|
-
|
|
2048
|
+
z31 = y - b / 200;
|
|
2049
2049
|
const y2 = y ** 3;
|
|
2050
2050
|
const x2 = x ** 3;
|
|
2051
|
-
const
|
|
2051
|
+
const z210 = z31 ** 3;
|
|
2052
2052
|
y = y2 > 8856e-6 ? y2 : (y - 16 / 116) / 7.787;
|
|
2053
2053
|
x = x2 > 8856e-6 ? x2 : (x - 16 / 116) / 7.787;
|
|
2054
|
-
|
|
2054
|
+
z31 = z210 > 8856e-6 ? z210 : (z31 - 16 / 116) / 7.787;
|
|
2055
2055
|
x *= 95.047;
|
|
2056
2056
|
y *= 100;
|
|
2057
|
-
|
|
2057
|
+
z31 *= 108.883;
|
|
2058
2058
|
return [
|
|
2059
2059
|
x,
|
|
2060
2060
|
y,
|
|
2061
|
-
|
|
2061
|
+
z31
|
|
2062
2062
|
];
|
|
2063
2063
|
};
|
|
2064
2064
|
convert$1.lab.lch = function(lab) {
|
|
@@ -2407,15 +2407,15 @@ var init_create_prompt_CIJV_96B = __esm({
|
|
|
2407
2407
|
};
|
|
2408
2408
|
}
|
|
2409
2409
|
function wrapConversion(toModel, graph) {
|
|
2410
|
-
const
|
|
2410
|
+
const path39 = [graph[toModel].parent, toModel];
|
|
2411
2411
|
let fn = conversions$1[graph[toModel].parent][toModel];
|
|
2412
2412
|
let cur = graph[toModel].parent;
|
|
2413
2413
|
while (graph[cur].parent) {
|
|
2414
|
-
|
|
2414
|
+
path39.unshift(graph[cur].parent);
|
|
2415
2415
|
fn = link(conversions$1[graph[cur].parent][cur], fn);
|
|
2416
2416
|
cur = graph[cur].parent;
|
|
2417
2417
|
}
|
|
2418
|
-
fn.conversion =
|
|
2418
|
+
fn.conversion = path39;
|
|
2419
2419
|
return fn;
|
|
2420
2420
|
}
|
|
2421
2421
|
module2.exports = function(fromModel) {
|
|
@@ -3978,18 +3978,18 @@ function parseSkillFile(filePath) {
|
|
|
3978
3978
|
const issues = [];
|
|
3979
3979
|
const suggestions$1 = [];
|
|
3980
3980
|
for (const issue of validation$1.error.errors) {
|
|
3981
|
-
const
|
|
3981
|
+
const path39 = issue.path.join(".") || "frontmatter";
|
|
3982
3982
|
if (issue.code === "invalid_type" && issue.message === "Required") {
|
|
3983
|
-
issues.push(`Missing required field '${
|
|
3984
|
-
suggestions$1.push(`Add '${
|
|
3983
|
+
issues.push(`Missing required field '${path39}' in ${filePath}`);
|
|
3984
|
+
suggestions$1.push(`Add '${path39}: <value>' to the YAML frontmatter.`);
|
|
3985
3985
|
continue;
|
|
3986
3986
|
}
|
|
3987
3987
|
if (issue.code === "too_small") {
|
|
3988
|
-
issues.push(`${
|
|
3989
|
-
suggestions$1.push(`Provide a non-empty value for '${
|
|
3988
|
+
issues.push(`${path39}: ${issue.message}`);
|
|
3989
|
+
suggestions$1.push(`Provide a non-empty value for '${path39}'.`);
|
|
3990
3990
|
continue;
|
|
3991
3991
|
}
|
|
3992
|
-
issues.push(`${
|
|
3992
|
+
issues.push(`${path39}: ${issue.message}`);
|
|
3993
3993
|
}
|
|
3994
3994
|
if (suggestions$1.length === 0) suggestions$1.push("Ensure SKILL.md frontmatter includes non-empty 'name' and 'description' fields.");
|
|
3995
3995
|
throw new ValidationError$2(filePath, issues, suggestions$1);
|
|
@@ -4103,9 +4103,9 @@ function builtInAgentRoots(baseDir, location, sourceKind) {
|
|
|
4103
4103
|
sourcePriority: sourcePriority$1(sourceKind)
|
|
4104
4104
|
}));
|
|
4105
4105
|
}
|
|
4106
|
-
function sharedRoot(
|
|
4106
|
+
function sharedRoot(path39, location, sourceKind) {
|
|
4107
4107
|
return {
|
|
4108
|
-
path:
|
|
4108
|
+
path: path39,
|
|
4109
4109
|
provider: "agents",
|
|
4110
4110
|
location,
|
|
4111
4111
|
sourceKind,
|
|
@@ -4113,9 +4113,9 @@ function sharedRoot(path33, location, sourceKind) {
|
|
|
4113
4113
|
};
|
|
4114
4114
|
}
|
|
4115
4115
|
function legacySharedRoot(baseDir, location, sourceKind) {
|
|
4116
|
-
const
|
|
4117
|
-
if (!existsSync(
|
|
4118
|
-
return [sharedRoot(
|
|
4116
|
+
const path39 = join(baseDir, ".agent", "skills");
|
|
4117
|
+
if (!existsSync(path39)) return [];
|
|
4118
|
+
return [sharedRoot(path39, location, sourceKind)];
|
|
4119
4119
|
}
|
|
4120
4120
|
function uniqueByPath(entries) {
|
|
4121
4121
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -5360,8 +5360,8 @@ function buildPreview$1(options2, selected) {
|
|
|
5360
5360
|
function promptMultiSelect(options2) {
|
|
5361
5361
|
return promptImpl(options2);
|
|
5362
5362
|
}
|
|
5363
|
-
function normalizeRepoUrl(origin,
|
|
5364
|
-
let cleanPath =
|
|
5363
|
+
function normalizeRepoUrl(origin, path39) {
|
|
5364
|
+
let cleanPath = path39.replace(/\/+$/, "");
|
|
5365
5365
|
if (cleanPath.endsWith(".git")) cleanPath = cleanPath.slice(0, -4);
|
|
5366
5366
|
return `${origin}${cleanPath}.git`;
|
|
5367
5367
|
}
|
|
@@ -5635,17 +5635,17 @@ function installSkillDir(skillDir, targetRoot, force = false) {
|
|
|
5635
5635
|
status: alreadyExists ? "overwritten" : "installed"
|
|
5636
5636
|
};
|
|
5637
5637
|
}
|
|
5638
|
-
function buildSourceArgs(input, branch,
|
|
5638
|
+
function buildSourceArgs(input, branch, path39, repo) {
|
|
5639
5639
|
return {
|
|
5640
5640
|
source: repo ?? input,
|
|
5641
5641
|
...branch ? { branch } : {},
|
|
5642
|
-
...
|
|
5642
|
+
...path39 ? { path: path39 } : {}
|
|
5643
5643
|
};
|
|
5644
5644
|
}
|
|
5645
|
-
function formatSourceLabel(input, branch,
|
|
5645
|
+
function formatSourceLabel(input, branch, path39, repo) {
|
|
5646
5646
|
const pieces = [repo ?? input];
|
|
5647
5647
|
if (branch) pieces.push(`--branch=${branch}`);
|
|
5648
|
-
if (
|
|
5648
|
+
if (path39) pieces.push(`--path=${path39}`);
|
|
5649
5649
|
return pieces.join(" ");
|
|
5650
5650
|
}
|
|
5651
5651
|
function defaultMode(source, explicit) {
|
|
@@ -5805,8 +5805,8 @@ async function selectSkills(targetDirs, argv$1, cmdBuilder) {
|
|
|
5805
5805
|
};
|
|
5806
5806
|
const source = cmdBuilder.getArg("source")[0] ?? argv$1.source;
|
|
5807
5807
|
const branch = cmdBuilder.getArg("branch")[0];
|
|
5808
|
-
const
|
|
5809
|
-
const sourceLabel = formatSourceLabel(source, branch,
|
|
5808
|
+
const path39 = cmdBuilder.getArg("path")[0];
|
|
5809
|
+
const sourceLabel = formatSourceLabel(source, branch, path39);
|
|
5810
5810
|
const listing = formatSkillListing(entries, sourceLabel);
|
|
5811
5811
|
const selectors = parseSelectors(argv$1);
|
|
5812
5812
|
if (argv$1.all) return {
|
|
@@ -6979,8 +6979,8 @@ var init_workflow_install_KvZzb83I = __esm({
|
|
|
6979
6979
|
en_default = errorMap;
|
|
6980
6980
|
overrideErrorMap = en_default;
|
|
6981
6981
|
makeIssue = (params) => {
|
|
6982
|
-
const { data, path:
|
|
6983
|
-
const fullPath = [...
|
|
6982
|
+
const { data, path: path39, errorMaps, issueData } = params;
|
|
6983
|
+
const fullPath = [...path39, ...issueData.path || []];
|
|
6984
6984
|
const fullIssue = {
|
|
6985
6985
|
...issueData,
|
|
6986
6986
|
path: fullPath
|
|
@@ -7070,11 +7070,11 @@ var init_workflow_install_KvZzb83I = __esm({
|
|
|
7070
7070
|
errorUtil$1.toString = (message) => typeof message === "string" ? message : message?.message;
|
|
7071
7071
|
})(errorUtil || (errorUtil = {}));
|
|
7072
7072
|
ParseInputLazyPath = class {
|
|
7073
|
-
constructor(parent, value,
|
|
7073
|
+
constructor(parent, value, path39, key$1) {
|
|
7074
7074
|
this._cachedPath = [];
|
|
7075
7075
|
this.parent = parent;
|
|
7076
7076
|
this.data = value;
|
|
7077
|
-
this._path =
|
|
7077
|
+
this._path = path39;
|
|
7078
7078
|
this._key = key$1;
|
|
7079
7079
|
}
|
|
7080
7080
|
get path() {
|
|
@@ -18022,7 +18022,7 @@ var init_workflow_install_KvZzb83I = __esm({
|
|
|
18022
18022
|
};
|
|
18023
18023
|
}) });
|
|
18024
18024
|
require_gray_matter = /* @__PURE__ */ __commonJS2({ "node_modules/.pnpm/gray-matter@4.0.3/node_modules/gray-matter/index.js": ((exports2, module2) => {
|
|
18025
|
-
const
|
|
18025
|
+
const fs38 = __require2("fs");
|
|
18026
18026
|
const sections = require_section_matter();
|
|
18027
18027
|
const defaults = require_defaults$1();
|
|
18028
18028
|
const stringify$1 = require_stringify();
|
|
@@ -18093,7 +18093,7 @@ var init_workflow_install_KvZzb83I = __esm({
|
|
|
18093
18093
|
return stringify$1(file, data, options2);
|
|
18094
18094
|
};
|
|
18095
18095
|
matter$1.read = function(filepath, options2) {
|
|
18096
|
-
const file = matter$1(
|
|
18096
|
+
const file = matter$1(fs38.readFileSync(filepath, "utf8"), options2);
|
|
18097
18097
|
file.path = filepath;
|
|
18098
18098
|
return file;
|
|
18099
18099
|
};
|
|
@@ -23320,8 +23320,8 @@ var init_workflow_install_KvZzb83I = __esm({
|
|
|
23320
23320
|
for (let i$4 = 0; i$4 < str$12.length; i$4++) if (str$12[i$4] === token) ind++;
|
|
23321
23321
|
return ind;
|
|
23322
23322
|
}
|
|
23323
|
-
function removeDotSegments$1(
|
|
23324
|
-
let input =
|
|
23323
|
+
function removeDotSegments$1(path39) {
|
|
23324
|
+
let input = path39;
|
|
23325
23325
|
const output = [];
|
|
23326
23326
|
let nextSlash = -1;
|
|
23327
23327
|
let len = 0;
|
|
@@ -23474,8 +23474,8 @@ var init_workflow_install_KvZzb83I = __esm({
|
|
|
23474
23474
|
wsComponent.secure = void 0;
|
|
23475
23475
|
}
|
|
23476
23476
|
if (wsComponent.resourceName) {
|
|
23477
|
-
const [
|
|
23478
|
-
wsComponent.path =
|
|
23477
|
+
const [path39, query] = wsComponent.resourceName.split("?");
|
|
23478
|
+
wsComponent.path = path39 && path39 !== "/" ? path39 : void 0;
|
|
23479
23479
|
wsComponent.query = query;
|
|
23480
23480
|
wsComponent.resourceName = void 0;
|
|
23481
23481
|
}
|
|
@@ -36772,32 +36772,32 @@ If the user names a skill, or a task clearly matches a skill that is not already
|
|
|
36772
36772
|
});
|
|
36773
36773
|
|
|
36774
36774
|
// src/daemon/dsh-profile-support.ts
|
|
36775
|
-
import { createRequire as
|
|
36776
|
-
import
|
|
36777
|
-
import
|
|
36775
|
+
import { createRequire as createRequire5 } from "node:module";
|
|
36776
|
+
import fs25 from "node:fs";
|
|
36777
|
+
import path27 from "node:path";
|
|
36778
36778
|
function ensureDirLink(link, target) {
|
|
36779
|
-
|
|
36780
|
-
if (
|
|
36781
|
-
|
|
36782
|
-
|
|
36779
|
+
fs25.mkdirSync(path27.dirname(link), { recursive: true });
|
|
36780
|
+
if (fs25.existsSync(link) && fs25.realpathSync(link) === target) return;
|
|
36781
|
+
fs25.rmSync(link, { recursive: true, force: true });
|
|
36782
|
+
fs25.symlinkSync(target, link, "dir");
|
|
36783
36783
|
}
|
|
36784
36784
|
function completeTransitiveMirror(home) {
|
|
36785
|
-
const mirrorRoot =
|
|
36786
|
-
if (!
|
|
36785
|
+
const mirrorRoot = path27.join(home, "profiles", "node_modules");
|
|
36786
|
+
if (!fs25.existsSync(mirrorRoot)) return;
|
|
36787
36787
|
const manifestOf = (dir) => {
|
|
36788
36788
|
try {
|
|
36789
|
-
return JSON.parse(
|
|
36789
|
+
return JSON.parse(fs25.readFileSync(path27.join(dir, "package.json"), "utf8"));
|
|
36790
36790
|
} catch {
|
|
36791
36791
|
return null;
|
|
36792
36792
|
}
|
|
36793
36793
|
};
|
|
36794
36794
|
const listPackages = () => {
|
|
36795
36795
|
const names = [];
|
|
36796
|
-
for (const scope of
|
|
36796
|
+
for (const scope of fs25.readdirSync(mirrorRoot)) {
|
|
36797
36797
|
if (scope.startsWith(".") || scope === ".bin") continue;
|
|
36798
|
-
const scopeDir =
|
|
36798
|
+
const scopeDir = path27.join(mirrorRoot, scope);
|
|
36799
36799
|
if (scope.startsWith("@")) {
|
|
36800
|
-
for (const name of
|
|
36800
|
+
for (const name of fs25.readdirSync(scopeDir)) names.push(`${scope}/${name}`);
|
|
36801
36801
|
} else {
|
|
36802
36802
|
names.push(scope);
|
|
36803
36803
|
}
|
|
@@ -36807,18 +36807,18 @@ function completeTransitiveMirror(home) {
|
|
|
36807
36807
|
for (let round = 0; round < 8; round += 1) {
|
|
36808
36808
|
let linked = 0;
|
|
36809
36809
|
for (const name of listPackages()) {
|
|
36810
|
-
const pkgDir =
|
|
36810
|
+
const pkgDir = path27.join(mirrorRoot, name);
|
|
36811
36811
|
const manifest = manifestOf(pkgDir);
|
|
36812
36812
|
const deps = { ...manifest?.dependencies };
|
|
36813
36813
|
if (!deps) continue;
|
|
36814
36814
|
for (const dep of Object.keys(deps)) {
|
|
36815
|
-
const depLink =
|
|
36816
|
-
if (
|
|
36815
|
+
const depLink = path27.join(mirrorRoot, dep);
|
|
36816
|
+
if (fs25.existsSync(depLink)) continue;
|
|
36817
36817
|
try {
|
|
36818
|
-
const resolved =
|
|
36818
|
+
const resolved = createRequire5(path27.join(realPkgDir(pkgDir), "package.json")).resolve(
|
|
36819
36819
|
`${dep}/package.json`
|
|
36820
36820
|
);
|
|
36821
|
-
ensureDirLink(depLink,
|
|
36821
|
+
ensureDirLink(depLink, path27.dirname(resolved));
|
|
36822
36822
|
linked += 1;
|
|
36823
36823
|
} catch {
|
|
36824
36824
|
}
|
|
@@ -36829,7 +36829,7 @@ function completeTransitiveMirror(home) {
|
|
|
36829
36829
|
}
|
|
36830
36830
|
function realPkgDir(pkgDir) {
|
|
36831
36831
|
try {
|
|
36832
|
-
return
|
|
36832
|
+
return fs25.realpathSync(pkgDir);
|
|
36833
36833
|
} catch {
|
|
36834
36834
|
return pkgDir;
|
|
36835
36835
|
}
|
|
@@ -36994,8 +36994,8 @@ __export(dsh_kernel_exports, {
|
|
|
36994
36994
|
KERNEL_AGENT_TOOL_ALLOWLIST: () => KERNEL_AGENT_TOOL_ALLOWLIST,
|
|
36995
36995
|
bootDshKernel: () => bootDshKernel
|
|
36996
36996
|
});
|
|
36997
|
-
import
|
|
36998
|
-
import
|
|
36997
|
+
import fs26 from "node:fs";
|
|
36998
|
+
import path28 from "node:path";
|
|
36999
36999
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
37000
37000
|
import {
|
|
37001
37001
|
boot,
|
|
@@ -37021,15 +37021,15 @@ async function bootDshKernel(options2) {
|
|
|
37021
37021
|
""
|
|
37022
37022
|
);
|
|
37023
37023
|
const llmYaml = localLlmRouteYaml();
|
|
37024
|
-
|
|
37025
|
-
const installAnchor =
|
|
37024
|
+
fs26.writeFileSync(path28.join(profileDir, "cordis.patch.yml"), disableYaml + llmYaml, "utf8");
|
|
37025
|
+
const installAnchor = path28.join(repoRoot, "package.json");
|
|
37026
37026
|
const profile = loadProfile("skill-creator", "kernel", installAnchor, options2.home);
|
|
37027
37027
|
await healProfilesModuleFallback({ installAnchor, profile, home: options2.home });
|
|
37028
37028
|
completeTransitiveMirror(options2.home);
|
|
37029
|
-
const presetDir =
|
|
37030
|
-
|
|
37031
|
-
|
|
37032
|
-
|
|
37029
|
+
const presetDir = path28.join(options2.home, ".agent-presets", "skill-creator");
|
|
37030
|
+
fs26.mkdirSync(presetDir, { recursive: true });
|
|
37031
|
+
fs26.writeFileSync(
|
|
37032
|
+
path28.join(presetDir, "preset.yml"),
|
|
37033
37033
|
[
|
|
37034
37034
|
"name: Skill Creator Agent",
|
|
37035
37035
|
"description: \u4EA7\u54C1\u4F1A\u8BDD\u9884\u8BBE\u2014\u2014Manager \u80FD\u529B\u9762 + ask-user\uFF0C\u65E0\u901A\u7528 fs/shell \u5DE5\u5177\u3002",
|
|
@@ -37038,8 +37038,8 @@ async function bootDshKernel(options2) {
|
|
|
37038
37038
|
].join("\n"),
|
|
37039
37039
|
"utf8"
|
|
37040
37040
|
);
|
|
37041
|
-
|
|
37042
|
-
|
|
37041
|
+
fs26.writeFileSync(
|
|
37042
|
+
path28.join(presetDir, "agent.cordis.yml"),
|
|
37043
37043
|
[
|
|
37044
37044
|
"# skill-creator \u4EA7\u54C1 preset\uFF1A\u7531 skill-creator kernel boot \u5199\u5165\uFF08user root\uFF09\u3002",
|
|
37045
37045
|
"- id: persona",
|
|
@@ -37056,7 +37056,7 @@ async function bootDshKernel(options2) {
|
|
|
37056
37056
|
].join("\n"),
|
|
37057
37057
|
"utf8"
|
|
37058
37058
|
);
|
|
37059
|
-
const configPath =
|
|
37059
|
+
const configPath = path28.join(profileDir, "cordis.yml");
|
|
37060
37060
|
const mcpRow = options2.mcp ? [
|
|
37061
37061
|
"- id: mcp-skill-creator\n",
|
|
37062
37062
|
" name: '@deepseek-ai/dsh-mcp-client'\n",
|
|
@@ -37067,7 +37067,7 @@ async function bootDshKernel(options2) {
|
|
|
37067
37067
|
" headers:\n",
|
|
37068
37068
|
" Authorization: !!js '`Bearer ${process.env.SKILL_CREATOR_MCP_TOKEN}`'\n"
|
|
37069
37069
|
].join("") : "";
|
|
37070
|
-
|
|
37070
|
+
fs26.writeFileSync(
|
|
37071
37071
|
configPath,
|
|
37072
37072
|
[
|
|
37073
37073
|
"- id: agent-presets\n",
|
|
@@ -37164,8 +37164,8 @@ var init_dsh_kernel = __esm({
|
|
|
37164
37164
|
init_dsh_profile_support();
|
|
37165
37165
|
init_agent_roles2();
|
|
37166
37166
|
modulePath = fileURLToPath2(import.meta.url);
|
|
37167
|
-
sourceMode =
|
|
37168
|
-
repoRoot = sourceMode ?
|
|
37167
|
+
sourceMode = path28.basename(modulePath) === "dsh-kernel.ts";
|
|
37168
|
+
repoRoot = sourceMode ? path28.resolve(path28.dirname(modulePath), "../../..") : path28.resolve(path28.dirname(modulePath), "..");
|
|
37169
37169
|
KERNEL_DISABLED_TOOL_ROWS = [
|
|
37170
37170
|
"tool-pwsh",
|
|
37171
37171
|
"tool-fs",
|
|
@@ -37196,8 +37196,8 @@ function resolveDaemonAppLaunch(daemonModuleUrl, runtimeExecutable = process.exe
|
|
|
37196
37196
|
}
|
|
37197
37197
|
|
|
37198
37198
|
// src/daemon/index.ts
|
|
37199
|
-
import
|
|
37200
|
-
import
|
|
37199
|
+
import fs36 from "node:fs";
|
|
37200
|
+
import path37 from "node:path";
|
|
37201
37201
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
37202
37202
|
import { randomBytes as randomBytes15 } from "node:crypto";
|
|
37203
37203
|
|
|
@@ -38199,7 +38199,7 @@ var DshSessionStreamsInputSchema = z7.object({
|
|
|
38199
38199
|
|
|
38200
38200
|
// src/shared/rpc-contract.ts
|
|
38201
38201
|
import { oc } from "@orpc/contract";
|
|
38202
|
-
import { z as
|
|
38202
|
+
import { z as z15 } from "zod";
|
|
38203
38203
|
|
|
38204
38204
|
// src/shared/contracts/acp.ts
|
|
38205
38205
|
import { z as z8 } from "zod";
|
|
@@ -39684,49 +39684,142 @@ var ApplyUpdateResultSchema = z13.object({
|
|
|
39684
39684
|
results: z13.array(ApplyUpdateResultEntrySchema)
|
|
39685
39685
|
});
|
|
39686
39686
|
|
|
39687
|
+
// src/shared/contracts/search.ts
|
|
39688
|
+
import { z as z14 } from "zod";
|
|
39689
|
+
var SkillInstallationSchema = z14.object({
|
|
39690
|
+
/** 入口原始绝对路径(Agent 环境视角,可能是指向 canonical 目录的 symlink)。 */
|
|
39691
|
+
path: z14.string().min(1),
|
|
39692
|
+
/** 该 root 所属 Workspace(Global 为 `~`,Imported 为 ws_*)。 */
|
|
39693
|
+
workspaceId: WorkspaceIdSchema,
|
|
39694
|
+
/** 该 root 的 Provider。 */
|
|
39695
|
+
providerId: ProviderIdSchema
|
|
39696
|
+
}).strict();
|
|
39697
|
+
var SkillSearchDocumentSchema = z14.object({
|
|
39698
|
+
/** 与 skills.list 一致的稳定 sk_ id(canonical path digest)。 */
|
|
39699
|
+
id: SkillIdSchema,
|
|
39700
|
+
/** frontmatter name;无效时回退目录名(见 invalidFrontmatter)。 */
|
|
39701
|
+
name: z14.string(),
|
|
39702
|
+
/** frontmatter description;无效时置空。 */
|
|
39703
|
+
description: z14.string(),
|
|
39704
|
+
/** frontmatter keywords(string|string[] 收窄后的字符串数组)。 */
|
|
39705
|
+
keywords: z14.array(z14.string()),
|
|
39706
|
+
/** frontmatter triggers(同上收窄)。 */
|
|
39707
|
+
triggers: z14.array(z14.string()),
|
|
39708
|
+
/** body 的 #/##/###/#### 标题拼接(截 30 条)。 */
|
|
39709
|
+
headings: z14.string(),
|
|
39710
|
+
/** markdown-to-search-text 正文(剥 fence 围栏行,截 12k chars)。 */
|
|
39711
|
+
body: z14.string(),
|
|
39712
|
+
/** realpath 真相。 */
|
|
39713
|
+
canonicalPath: z14.string().min(1),
|
|
39714
|
+
/** 同一 canonical 目录的全部入口(未来 RPC/GUI 可直接组装 WorkspaceProviderTarget)。 */
|
|
39715
|
+
installations: z14.array(SkillInstallationSchema).min(1),
|
|
39716
|
+
/** 实际被索引 SKILL.md 原始字节的 SHA-256 hex(64 字符,无前缀)。 */
|
|
39717
|
+
contentHash: z14.string().regex(/^[a-f0-9]{64}$/),
|
|
39718
|
+
/** 仅 .SKILL.md 在场(技能被禁用)。 */
|
|
39719
|
+
disabled: z14.boolean(),
|
|
39720
|
+
/** SKILL.md 与 .SKILL.md 并存(内容源取 SKILL.md)。 */
|
|
39721
|
+
conflict: z14.boolean(),
|
|
39722
|
+
/** frontmatter 未过 name/description schema(文档仍入索引)。 */
|
|
39723
|
+
invalidFrontmatter: z14.boolean()
|
|
39724
|
+
}).strict();
|
|
39725
|
+
var SkillSearchDuplicateSchema = z14.object({
|
|
39726
|
+
id: SkillIdSchema,
|
|
39727
|
+
canonicalPath: z14.string().min(1)
|
|
39728
|
+
}).strict();
|
|
39729
|
+
var SkillDuplicateMemberSchema = z14.object({
|
|
39730
|
+
id: SkillIdSchema,
|
|
39731
|
+
name: z14.string(),
|
|
39732
|
+
canonicalPath: z14.string().min(1),
|
|
39733
|
+
installations: z14.array(SkillInstallationSchema).min(1),
|
|
39734
|
+
disabled: z14.boolean(),
|
|
39735
|
+
conflict: z14.boolean()
|
|
39736
|
+
}).strict();
|
|
39737
|
+
var SkillDuplicateGroupSchema = z14.object({
|
|
39738
|
+
contentHash: z14.string().regex(/^[a-f0-9]{64}$/),
|
|
39739
|
+
members: z14.array(SkillDuplicateMemberSchema).min(2)
|
|
39740
|
+
}).strict();
|
|
39741
|
+
var SkillSearchResultSchema = z14.object({
|
|
39742
|
+
id: SkillIdSchema,
|
|
39743
|
+
name: z14.string(),
|
|
39744
|
+
description: z14.string(),
|
|
39745
|
+
canonicalPath: z14.string().min(1),
|
|
39746
|
+
installations: z14.array(SkillInstallationSchema).min(1),
|
|
39747
|
+
contentHash: z14.string().regex(/^[a-f0-9]{64}$/),
|
|
39748
|
+
disabled: z14.boolean(),
|
|
39749
|
+
conflict: z14.boolean(),
|
|
39750
|
+
/** final = 0.7×(bm25/(bm25+8)) + 0.3×rerank。 */
|
|
39751
|
+
score: z14.number(),
|
|
39752
|
+
/** 同 contentHash 组内被折叠的其余成员(同序)。 */
|
|
39753
|
+
duplicates: z14.array(SkillSearchDuplicateSchema)
|
|
39754
|
+
}).strict();
|
|
39755
|
+
var SkillSearchOptionsSchema = z14.object({
|
|
39756
|
+
limit: z14.number().int().min(1).max(50).default(10)
|
|
39757
|
+
}).strict();
|
|
39758
|
+
|
|
39687
39759
|
// src/shared/rpc-contract.ts
|
|
39688
|
-
var WorkspaceProviderReadInputSchema =
|
|
39760
|
+
var WorkspaceProviderReadInputSchema = z15.object({
|
|
39689
39761
|
...WorkspaceProviderTargetSchema.shape,
|
|
39690
|
-
includeDisabled:
|
|
39762
|
+
includeDisabled: z15.boolean().optional()
|
|
39691
39763
|
});
|
|
39692
39764
|
var SkillsListInputSchema = WorkspaceProviderReadInputSchema;
|
|
39693
39765
|
var SkillsInfoInputSchema = WorkspaceProviderReadInputSchema.extend({
|
|
39694
39766
|
skillId: SkillIdSchema
|
|
39695
39767
|
});
|
|
39696
|
-
var SkillsToggleInputSchema =
|
|
39768
|
+
var SkillsToggleInputSchema = z15.object({
|
|
39697
39769
|
...WorkspaceProviderTargetSchema.shape,
|
|
39698
|
-
skillIds:
|
|
39699
|
-
mode:
|
|
39770
|
+
skillIds: z15.array(SkillIdSchema).min(1),
|
|
39771
|
+
mode: z15.enum(["enable", "disable"])
|
|
39700
39772
|
});
|
|
39701
|
-
var WorkspaceAddInputSchema =
|
|
39702
|
-
path:
|
|
39703
|
-
label:
|
|
39773
|
+
var WorkspaceAddInputSchema = z15.object({
|
|
39774
|
+
path: z15.string().trim().min(1),
|
|
39775
|
+
label: z15.string().trim().min(1).optional()
|
|
39704
39776
|
});
|
|
39705
|
-
var WorkspaceRemoveInputSchema =
|
|
39706
|
-
var WorkspaceSetActiveInputSchema =
|
|
39707
|
-
var CreatorRemoveInputSchema =
|
|
39777
|
+
var WorkspaceRemoveInputSchema = z15.object({ id: ImportedWorkspaceIdSchema });
|
|
39778
|
+
var WorkspaceSetActiveInputSchema = z15.object({ id: WorkspaceIdSchema });
|
|
39779
|
+
var CreatorRemoveInputSchema = z15.object({
|
|
39708
39780
|
...WorkspaceProviderTargetSchema.shape,
|
|
39709
39781
|
skillId: SkillIdSchema,
|
|
39710
|
-
expectedRevision:
|
|
39782
|
+
expectedRevision: z15.string().regex(/^sha256:[a-f0-9]{64}$/)
|
|
39711
39783
|
});
|
|
39712
|
-
var
|
|
39713
|
-
|
|
39714
|
-
|
|
39784
|
+
var SkillsSearchInputSchema = z15.object({
|
|
39785
|
+
query: z15.string().min(1),
|
|
39786
|
+
limit: SkillSearchOptionsSchema.shape.limit
|
|
39787
|
+
}).strict();
|
|
39788
|
+
var RepositoryScanInputSchema = z15.object({
|
|
39789
|
+
source: z15.string().trim().min(1),
|
|
39790
|
+
ref: z15.string().trim().min(1).optional()
|
|
39715
39791
|
});
|
|
39716
|
-
var RepositoryPreviewInputSchema =
|
|
39792
|
+
var RepositoryPreviewInputSchema = z15.object({
|
|
39717
39793
|
sessionId: RepositorySessionIdSchema,
|
|
39718
39794
|
skillId: RemoteSkillIdSchema
|
|
39719
39795
|
});
|
|
39720
39796
|
var rpcContract = oc.errors(RpcErrorDefinitions).router({
|
|
39721
39797
|
skills: {
|
|
39722
39798
|
/** Discover skills within one explicit Workspace Provider. */
|
|
39723
|
-
list: oc.input(SkillsListInputSchema).output(
|
|
39799
|
+
list: oc.input(SkillsListInputSchema).output(z15.object({ skills: z15.array(SkillMetadataSchema) })),
|
|
39724
39800
|
/** Read one Workspace Provider-scoped skill document. */
|
|
39725
39801
|
info: oc.input(SkillsInfoInputSchema).output(SkillInfoSchema),
|
|
39726
39802
|
/** Enable or disable selected opaque skill IDs. */
|
|
39727
39803
|
toggle: oc.input(SkillsToggleInputSchema).output(ToggleSummarySchema),
|
|
39728
39804
|
/** Validate one Workspace Provider-scoped skill. */
|
|
39729
39805
|
validate: oc.input(SkillsInfoInputSchema).output(ValidateResultSchema),
|
|
39806
|
+
/**
|
|
39807
|
+
* Search local skills across all workspaces (daemon 长驻检索:canonical 去重 +
|
|
39808
|
+
* BM25 + 冻结 rerank;返回稳定 sk_ id 与 installations 作用域三元组)。
|
|
39809
|
+
*/
|
|
39810
|
+
search: oc.input(SkillsSearchInputSchema).output(z15.object({ results: z15.array(SkillSearchResultSchema) })),
|
|
39811
|
+
/**
|
|
39812
|
+
* 检索内容配置面:以系统默认编辑器打开 server-owned 的 search-config.toml
|
|
39813
|
+
* (排除目录清单;路径由 daemon 派生,不接受调用方输入)。
|
|
39814
|
+
*/
|
|
39815
|
+
searchConfig: {
|
|
39816
|
+
open: oc.input(z15.object({})).output(z15.object({ opened: z15.boolean() }))
|
|
39817
|
+
},
|
|
39818
|
+
/**
|
|
39819
|
+
* 内容重复组(索引 contentHash 分组 >1;无查询维度的全量重复清单,
|
|
39820
|
+
* 排序冻结可重放)。
|
|
39821
|
+
*/
|
|
39822
|
+
duplicates: oc.input(z15.object({})).output(z15.object({ groups: z15.array(SkillDuplicateGroupSchema) })),
|
|
39730
39823
|
update: {
|
|
39731
39824
|
/** Compare skills-CLI lock hashes against upstream and report outdated skills. */
|
|
39732
39825
|
check: oc.input(UpdateCheckInputSchema).output(UpdateCheckResultSchema),
|
|
@@ -39736,13 +39829,18 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
|
|
|
39736
39829
|
},
|
|
39737
39830
|
workspace: {
|
|
39738
39831
|
/** List Global and imported Workspaces with fresh Provider counts. */
|
|
39739
|
-
list: oc.input(
|
|
39832
|
+
list: oc.input(z15.object({})).output(z15.object({ workspaces: z15.array(WorkspaceSchema) })),
|
|
39740
39833
|
/** Import a canonical directory workspace. */
|
|
39741
|
-
add: oc.input(WorkspaceAddInputSchema).output(
|
|
39834
|
+
add: oc.input(WorkspaceAddInputSchema).output(z15.object({ workspace: WorkspaceSchema })),
|
|
39742
39835
|
/** Remove an imported workspace registration. */
|
|
39743
|
-
remove: oc.input(WorkspaceRemoveInputSchema).output(
|
|
39836
|
+
remove: oc.input(WorkspaceRemoveInputSchema).output(z15.object({ activeId: WorkspaceIdSchema })),
|
|
39744
39837
|
/** Select the active workspace. */
|
|
39745
|
-
setActive: oc.input(WorkspaceSetActiveInputSchema).output(
|
|
39838
|
+
setActive: oc.input(WorkspaceSetActiveInputSchema).output(z15.object({ activeId: WorkspaceIdSchema })),
|
|
39839
|
+
/**
|
|
39840
|
+
* 原生目录选择器(ext-dialog;tray 挂载后可用)。用户取消 → path:null;
|
|
39841
|
+
* 平台不支持 / headless → supported:false(WebUI 以此隐藏 Browse 入口)。
|
|
39842
|
+
*/
|
|
39843
|
+
pickDirectory: oc.input(z15.object({})).output(z15.object({ supported: z15.boolean(), path: z15.string().nullable() }))
|
|
39746
39844
|
},
|
|
39747
39845
|
creator: {
|
|
39748
39846
|
/** Create or revision-check and update a skill. */
|
|
@@ -39750,7 +39848,7 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
|
|
|
39750
39848
|
/** Load an editable skill document. */
|
|
39751
39849
|
load: oc.input(SkillsInfoInputSchema).output(SkillDocumentSchema),
|
|
39752
39850
|
/** Revision-check and delete one skill. */
|
|
39753
|
-
remove: oc.input(CreatorRemoveInputSchema).output(
|
|
39851
|
+
remove: oc.input(CreatorRemoveInputSchema).output(z15.object({ removed: z15.literal(true) })),
|
|
39754
39852
|
/** Read revision history for one skill (change 5: change log sub-view). */
|
|
39755
39853
|
revisions: oc.input(CreatorRevisionsInputSchema).output(CreatorRevisionsResultSchema)
|
|
39756
39854
|
},
|
|
@@ -39763,29 +39861,29 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
|
|
|
39763
39861
|
install: oc.input(RepositoryInstallInputSchema).output(InstallResultSchema),
|
|
39764
39862
|
sources: {
|
|
39765
39863
|
/** List curated (built-in) and user-persisted sources for the Discover grid. */
|
|
39766
|
-
list: oc.input(
|
|
39767
|
-
|
|
39768
|
-
builtIn:
|
|
39769
|
-
|
|
39770
|
-
id:
|
|
39771
|
-
label:
|
|
39772
|
-
gitUrl:
|
|
39773
|
-
description:
|
|
39774
|
-
homepage:
|
|
39864
|
+
list: oc.input(z15.object({})).output(
|
|
39865
|
+
z15.object({
|
|
39866
|
+
builtIn: z15.array(
|
|
39867
|
+
z15.object({
|
|
39868
|
+
id: z15.string(),
|
|
39869
|
+
label: z15.string(),
|
|
39870
|
+
gitUrl: z15.string(),
|
|
39871
|
+
description: z15.string(),
|
|
39872
|
+
homepage: z15.string().optional()
|
|
39775
39873
|
})
|
|
39776
39874
|
),
|
|
39777
|
-
user:
|
|
39875
|
+
user: z15.array(UserSourceSchema)
|
|
39778
39876
|
})
|
|
39779
39877
|
),
|
|
39780
39878
|
/** Persist a new user source (https git URL only). */
|
|
39781
|
-
add: oc.input(AddUserSourceInputSchema).output(
|
|
39879
|
+
add: oc.input(AddUserSourceInputSchema).output(z15.object({ source: UserSourceSchema })),
|
|
39782
39880
|
/** Remove one user-persisted source; built-in ids are rejected. */
|
|
39783
|
-
remove: oc.input(RemoveUserSourceInputSchema).output(
|
|
39881
|
+
remove: oc.input(RemoveUserSourceInputSchema).output(z15.object({ removed: z15.literal(true) }))
|
|
39784
39882
|
}
|
|
39785
39883
|
},
|
|
39786
39884
|
daemon: {
|
|
39787
39885
|
/** Read the live daemon and tray status. */
|
|
39788
|
-
status: oc.input(
|
|
39886
|
+
status: oc.input(z15.object({})).output(DaemonStatusSchema)
|
|
39789
39887
|
},
|
|
39790
39888
|
skillIntelligence: {
|
|
39791
39889
|
/** Read-only multi-skill analysis locked to observed revisions. */
|
|
@@ -39793,7 +39891,7 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
|
|
|
39793
39891
|
/** Store a Manager-owned proposal draft; never mutates Providers. */
|
|
39794
39892
|
propose: oc.input(ProposeInputSchema).output(ProposeResultSchema),
|
|
39795
39893
|
/** List pending proposal drafts (newest first). */
|
|
39796
|
-
list: oc.input(
|
|
39894
|
+
list: oc.input(z15.object({})).output(ListProposalsResultSchema),
|
|
39797
39895
|
/** Delete one proposal draft. */
|
|
39798
39896
|
reject: oc.input(RejectProposalInputSchema).output(RejectProposalResultSchema),
|
|
39799
39897
|
/** Apply one proposal through existing revision-safe mutations. */
|
|
@@ -39801,9 +39899,9 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
|
|
|
39801
39899
|
},
|
|
39802
39900
|
steward: {
|
|
39803
39901
|
/** Probe every registered backend (typed unavailable; no auto fallback). */
|
|
39804
|
-
backends: oc.input(
|
|
39902
|
+
backends: oc.input(z15.object({})).output(StewardBackendsResultSchema),
|
|
39805
39903
|
/** List steward runs (newest first, bounded). */
|
|
39806
|
-
list: oc.input(
|
|
39904
|
+
list: oc.input(z15.object({})).output(StewardListResultSchema),
|
|
39807
39905
|
/** Start a steward run on an explicit backend and Imported Workspace.Provider. */
|
|
39808
39906
|
start: oc.input(StewardStartInputSchema).output(StewardStartResultSchema),
|
|
39809
39907
|
/** Poll normalized run events after a seq cursor. */
|
|
@@ -39838,16 +39936,16 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
|
|
|
39838
39936
|
},
|
|
39839
39937
|
/** MCP mutation proposal 审批链(task 4.4;Manager authority 的决定面)。 */
|
|
39840
39938
|
proposals: {
|
|
39841
|
-
list: oc.input(
|
|
39842
|
-
approve: oc.input(AgentProposalDecisionInputSchema).output(
|
|
39843
|
-
reject: oc.input(AgentProposalDecisionInputSchema).output(
|
|
39939
|
+
list: oc.input(z15.object({})).output(z15.object({ proposals: z15.array(AgentMcpProposalViewSchema) })),
|
|
39940
|
+
approve: oc.input(AgentProposalDecisionInputSchema).output(z15.object({ proposal: AgentMcpProposalViewSchema })),
|
|
39941
|
+
reject: oc.input(AgentProposalDecisionInputSchema).output(z15.object({ proposal: AgentMcpProposalViewSchema }))
|
|
39844
39942
|
},
|
|
39845
39943
|
/** 面板会话:内核 agent 会话的生命周期投影(task 2.2;旧 dsh.* 收敛并入)。 */
|
|
39846
39944
|
sessions: {
|
|
39847
39945
|
/** 列出内核 live 会话(含非面板会话的 disposed 投影)。 */
|
|
39848
|
-
list: oc.input(
|
|
39946
|
+
list: oc.input(z15.object({})).output(z15.object({ sessions: z15.array(AgentSessionSummarySchema) })),
|
|
39849
39947
|
/** 脱敏 run/session 帧查询(原 dsh.sessions.streams 平移;steward 投影消费)。 */
|
|
39850
|
-
streams: oc.input(AgentSessionsStreamsInputSchema).output(
|
|
39948
|
+
streams: oc.input(AgentSessionsStreamsInputSchema).output(z15.object({ frames: z15.array(DshSessionStreamFrameSchema) })),
|
|
39851
39949
|
/**
|
|
39852
39950
|
* 清理面板会话转录(R14-C):按保留天数 / 全量 / 显式 ID 删除产品转录层
|
|
39853
39951
|
* (sessions/YYYY/MM/DD),running 会话跳过;不触碰 $DSH_HOME 内核日志。
|
|
@@ -39890,16 +39988,16 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
|
|
|
39890
39988
|
/** 单文件预览:图片缩略(jSquash)/ 文本头 / 二进制名投影。 */
|
|
39891
39989
|
preview: oc.input(AgentFilesPreviewInputSchema).output(AgentFilesPreviewResultSchema),
|
|
39892
39990
|
/** 原生文件选择(R18 @xmorse/rfd):mode 预置 filter;空 = 取消。 */
|
|
39893
|
-
pickFiles: oc.input(
|
|
39991
|
+
pickFiles: oc.input(z15.object({ mode: z15.enum(["image", "file"]) })).output(z15.object({ paths: z15.array(z15.string().min(1)) }))
|
|
39894
39992
|
},
|
|
39895
39993
|
/** pi-ai 装配目录(models.dev 镜像)投影:全量 provider 画廊(filter 在 UI)。 */
|
|
39896
39994
|
models: {
|
|
39897
|
-
catalog: oc.input(
|
|
39995
|
+
catalog: oc.input(z15.object({})).output(z15.object({ providers: z15.array(ModelProviderCatalogEntrySchema) }))
|
|
39898
39996
|
},
|
|
39899
39997
|
/** model/preset/permission/approval 投影(原 dsh.settings 平移)。 */
|
|
39900
39998
|
settings: {
|
|
39901
39999
|
/** 当前 settings 投影 + provider 凭据(apiKey 按 R16 用户裁决回显)。 */
|
|
39902
|
-
get: oc.input(
|
|
40000
|
+
get: oc.input(z15.object({})).output(DshStewardSettingsViewSchema),
|
|
39903
40001
|
/** 应用补丁;revision 只在真实变更时 +1;类型化 rejected 见契约 union。 */
|
|
39904
40002
|
update: oc.input(DshSettingsUpdateSchema).output(DshSettingsUpdateResultSchema),
|
|
39905
40003
|
/**
|
|
@@ -39924,13 +40022,13 @@ var rpcContract = oc.errors(RpcErrorDefinitions).router({
|
|
|
39924
40022
|
/** List ACP-capable agents installed on this machine (daemon-lifetime cached). */
|
|
39925
40023
|
agents: {
|
|
39926
40024
|
/** Probe known PATH binaries and return their availability projection. */
|
|
39927
|
-
list: oc.input(
|
|
40025
|
+
list: oc.input(z15.object({})).output(z15.object({ agents: z15.array(AcpAgentInfoSchema) }))
|
|
39928
40026
|
},
|
|
39929
40027
|
session: {
|
|
39930
40028
|
/** Spawn the selected agent subprocess and register a daemon-owned session. */
|
|
39931
40029
|
open: oc.input(AcpSessionOpenInputSchema).output(AcpSessionOpenResultSchema),
|
|
39932
40030
|
/** Terminate the agent subprocess for one session (idempotent). */
|
|
39933
|
-
close: oc.input(AcpSessionCloseInputSchema).output(
|
|
40031
|
+
close: oc.input(AcpSessionCloseInputSchema).output(z15.object({ closed: z15.literal(true) }))
|
|
39934
40032
|
}
|
|
39935
40033
|
}
|
|
39936
40034
|
});
|
|
@@ -41070,6 +41168,7 @@ async function save(workspaces, skills, input, revisionLog, revisionLogLimit) {
|
|
|
41070
41168
|
const content = matter.stringify(input.body, frontmatter);
|
|
41071
41169
|
atomicWriteUtf8(targetFile, content.endsWith("\n") ? content : `${content}
|
|
41072
41170
|
`);
|
|
41171
|
+
skills.invalidateDiscovery(target);
|
|
41073
41172
|
const skillId = input.mode === "create" ? (await skills.list(target, true)).find(
|
|
41074
41173
|
(skill) => skill.path === fs4.realpathSync(skillDirectory)
|
|
41075
41174
|
)?.id : input.skillId;
|
|
@@ -41122,6 +41221,7 @@ async function remove(workspaces, skills, target, skillId, expectedRevision) {
|
|
|
41122
41221
|
throw new DomainError("CONFLICT", "This skill changed on disk. Reload it before deleting.");
|
|
41123
41222
|
}
|
|
41124
41223
|
fs4.rmSync(skill.path, { recursive: true, force: false });
|
|
41224
|
+
skills.invalidateDiscovery(target);
|
|
41125
41225
|
}
|
|
41126
41226
|
function writableDirectory(workspaces, target) {
|
|
41127
41227
|
const scope = workspaces.resolveWritable(target);
|
|
@@ -41150,28 +41250,28 @@ init_workflow_install_KvZzb83I();
|
|
|
41150
41250
|
// src/daemon/repository-service.ts
|
|
41151
41251
|
import { execa as execa2 } from "execa";
|
|
41152
41252
|
import matter2 from "gray-matter";
|
|
41153
|
-
import { z as
|
|
41253
|
+
import { z as z16, ZodError as ZodError3 } from "zod";
|
|
41154
41254
|
var CLONE_TIMEOUT_MS = 6e4;
|
|
41155
41255
|
var MAX_SESSIONS = 6;
|
|
41156
|
-
var InstallerResultEntrySchema =
|
|
41157
|
-
skill:
|
|
41158
|
-
destination:
|
|
41159
|
-
path:
|
|
41160
|
-
status:
|
|
41161
|
-
error:
|
|
41256
|
+
var InstallerResultEntrySchema = z16.object({
|
|
41257
|
+
skill: z16.string(),
|
|
41258
|
+
destination: z16.string(),
|
|
41259
|
+
path: z16.string(),
|
|
41260
|
+
status: z16.enum(["installed", "skipped", "overwritten", "failed"]),
|
|
41261
|
+
error: z16.string().optional()
|
|
41162
41262
|
});
|
|
41163
|
-
var InstallerSummarySchema =
|
|
41164
|
-
results:
|
|
41165
|
-
installed:
|
|
41166
|
-
skipped:
|
|
41167
|
-
overwritten:
|
|
41168
|
-
failed:
|
|
41263
|
+
var InstallerSummarySchema = z16.object({
|
|
41264
|
+
results: z16.array(InstallerResultEntrySchema),
|
|
41265
|
+
installed: z16.number().int().nonnegative(),
|
|
41266
|
+
skipped: z16.number().int().nonnegative(),
|
|
41267
|
+
overwritten: z16.number().int().nonnegative(),
|
|
41268
|
+
failed: z16.number().int().nonnegative()
|
|
41169
41269
|
});
|
|
41170
|
-
var InstallerPreviewSchema =
|
|
41171
|
-
dryRun:
|
|
41172
|
-
skills:
|
|
41173
|
-
destinations:
|
|
41174
|
-
totalInstalls:
|
|
41270
|
+
var InstallerPreviewSchema = z16.object({
|
|
41271
|
+
dryRun: z16.literal(true),
|
|
41272
|
+
skills: z16.array(z16.object({ name: z16.string(), description: z16.string() })),
|
|
41273
|
+
destinations: z16.array(z16.object({ path: z16.string(), exists: z16.boolean() })),
|
|
41274
|
+
totalInstalls: z16.number().int().nonnegative()
|
|
41175
41275
|
});
|
|
41176
41276
|
function createRepositoryService(workspaces, skills, options2 = {}) {
|
|
41177
41277
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -41457,6 +41557,7 @@ function assertExpectedInstallerEntry(target, entry) {
|
|
|
41457
41557
|
}
|
|
41458
41558
|
}
|
|
41459
41559
|
async function installedSkillId(target, skills) {
|
|
41560
|
+
skills.invalidateDiscovery(target.target);
|
|
41460
41561
|
const canonicalPath = canonicalDirectory(target.expectedPath);
|
|
41461
41562
|
if (canonicalPath !== target.expectedPath) {
|
|
41462
41563
|
throw new Error("Installed skill path must not resolve through a symbolic link.");
|
|
@@ -41762,16 +41863,16 @@ function formatZodError(error) {
|
|
|
41762
41863
|
|
|
41763
41864
|
// src/daemon/skills-cli-probe.ts
|
|
41764
41865
|
import { execa as execa3 } from "execa";
|
|
41765
|
-
import { z as
|
|
41766
|
-
var SkillsCliListEntrySchema =
|
|
41866
|
+
import { z as z17 } from "zod";
|
|
41867
|
+
var SkillsCliListEntrySchema = z17.object({
|
|
41767
41868
|
/** 技能名。 */
|
|
41768
|
-
name:
|
|
41869
|
+
name: z17.string(),
|
|
41769
41870
|
/** 技能的规范路径(canonicalPath)。 */
|
|
41770
|
-
path:
|
|
41871
|
+
path: z17.string().min(1),
|
|
41771
41872
|
/** 作用域;CLI 输出为 `project` 或 `global`,额外值被丢弃。 */
|
|
41772
|
-
scope:
|
|
41873
|
+
scope: z17.union([z17.literal("project"), z17.literal("global")]).optional()
|
|
41773
41874
|
});
|
|
41774
|
-
var SkillsCliListEnvelopeSchema =
|
|
41875
|
+
var SkillsCliListEnvelopeSchema = z17.array(z17.unknown());
|
|
41775
41876
|
var DEFAULT_TIMEOUT_MS2 = 15e3;
|
|
41776
41877
|
async function runNpxSkillsList() {
|
|
41777
41878
|
return execa3("npx", ["skills", "list", "--json"], {
|
|
@@ -41798,6 +41899,7 @@ function parseSkillsCliList(stdout) {
|
|
|
41798
41899
|
function createSkillsCliProbe(options2 = {}) {
|
|
41799
41900
|
const run = options2.run ?? runNpxSkillsList;
|
|
41800
41901
|
let cached2 = null;
|
|
41902
|
+
let inflight = null;
|
|
41801
41903
|
const probeOnce = async () => {
|
|
41802
41904
|
try {
|
|
41803
41905
|
const { stdout } = await run();
|
|
@@ -41807,10 +41909,26 @@ function createSkillsCliProbe(options2 = {}) {
|
|
|
41807
41909
|
}
|
|
41808
41910
|
};
|
|
41809
41911
|
return {
|
|
41810
|
-
/** 返回缓存的探测映射;首次调用 shell out
|
|
41912
|
+
/** 返回缓存的探测映射;首次调用 shell out(在途共享),后续命中缓存。 */
|
|
41811
41913
|
async probe() {
|
|
41812
41914
|
if (cached2) return cached2;
|
|
41813
|
-
|
|
41915
|
+
if (inflight) return inflight;
|
|
41916
|
+
inflight = probeOnce().then((result) => {
|
|
41917
|
+
cached2 = result;
|
|
41918
|
+
return result;
|
|
41919
|
+
});
|
|
41920
|
+
try {
|
|
41921
|
+
return await inflight;
|
|
41922
|
+
} finally {
|
|
41923
|
+
inflight = null;
|
|
41924
|
+
}
|
|
41925
|
+
},
|
|
41926
|
+
/**
|
|
41927
|
+
* 同步快照(perf-firstscreen B-5):缓存命中返回映射,未就绪返回 null——
|
|
41928
|
+
* 调用方(skills.list)不阻塞等待 npx(冷启动 0-15s);daemon boot 后
|
|
41929
|
+
* 台预热 probe,就绪后的下一次投影自然补全 provenance。
|
|
41930
|
+
*/
|
|
41931
|
+
peek() {
|
|
41814
41932
|
return cached2;
|
|
41815
41933
|
},
|
|
41816
41934
|
/** 失效缓存;指定路径时仅移除对应条目,否则清空全部。 */
|
|
@@ -41821,7 +41939,7 @@ function createSkillsCliProbe(options2 = {}) {
|
|
|
41821
41939
|
return;
|
|
41822
41940
|
}
|
|
41823
41941
|
const next = new Map(cached2);
|
|
41824
|
-
for (const
|
|
41942
|
+
for (const path39 of paths) next.delete(path39);
|
|
41825
41943
|
cached2 = next;
|
|
41826
41944
|
}
|
|
41827
41945
|
};
|
|
@@ -41838,68 +41956,68 @@ import path9 from "node:path";
|
|
|
41838
41956
|
import { execa as execa4 } from "execa";
|
|
41839
41957
|
|
|
41840
41958
|
// src/shared/contracts/skills-lock.ts
|
|
41841
|
-
import { z as
|
|
41842
|
-
var SkillLockEntrySchema =
|
|
41959
|
+
import { z as z18 } from "zod";
|
|
41960
|
+
var SkillLockEntrySchema = z18.object({
|
|
41843
41961
|
/** 规范化源标识(如 `owner/repo`、`mintlify/bun.com`)。 */
|
|
41844
|
-
source:
|
|
41962
|
+
source: z18.string(),
|
|
41845
41963
|
/** 源类型(`github`、`mintlify`、`huggingface`、`local` 等)。 */
|
|
41846
|
-
sourceType:
|
|
41964
|
+
sourceType: z18.string(),
|
|
41847
41965
|
/** 安装时使用的原始 URL,升级时用于重新拉取。 */
|
|
41848
|
-
sourceUrl:
|
|
41966
|
+
sourceUrl: z18.string(),
|
|
41849
41967
|
/** 安装时使用的分支或 tag ref。 */
|
|
41850
|
-
ref:
|
|
41968
|
+
ref: z18.string().optional(),
|
|
41851
41969
|
/** 源仓库内的子路径(可选)。 */
|
|
41852
|
-
skillPath:
|
|
41970
|
+
skillPath: z18.string().optional(),
|
|
41853
41971
|
/**
|
|
41854
41972
|
* 技能整个目录的 GitHub tree SHA;任一文件变更即变化。
|
|
41855
41973
|
* 由 telemetry server 通过 GitHub Trees API 取得。
|
|
41856
41974
|
*/
|
|
41857
|
-
skillFolderHash:
|
|
41975
|
+
skillFolderHash: z18.string(),
|
|
41858
41976
|
/** 首次安装的 ISO 时间戳。 */
|
|
41859
|
-
installedAt:
|
|
41977
|
+
installedAt: z18.string(),
|
|
41860
41978
|
/** 最近一次更新的 ISO 时间戳。 */
|
|
41861
|
-
updatedAt:
|
|
41979
|
+
updatedAt: z18.string(),
|
|
41862
41980
|
/** 所属插件名(可选)。 */
|
|
41863
|
-
pluginName:
|
|
41981
|
+
pluginName: z18.string().optional()
|
|
41864
41982
|
});
|
|
41865
|
-
var DismissedPromptsSchema =
|
|
41866
|
-
var SkillLockFileSchema =
|
|
41983
|
+
var DismissedPromptsSchema = z18.object({ findSkillsPrompt: z18.boolean().optional() }).optional();
|
|
41984
|
+
var SkillLockFileSchema = z18.object({
|
|
41867
41985
|
/** schema 版本;当前固定为 3。 */
|
|
41868
|
-
version:
|
|
41986
|
+
version: z18.literal(3),
|
|
41869
41987
|
/** 技能名 → 记录的映射。 */
|
|
41870
|
-
skills:
|
|
41988
|
+
skills: z18.record(z18.string(), SkillLockEntrySchema),
|
|
41871
41989
|
/** 已忽略的提示集合(可选)。 */
|
|
41872
41990
|
dismissed: DismissedPromptsSchema,
|
|
41873
41991
|
/** 最近一次选中的 agents(可选)。 */
|
|
41874
|
-
lastSelectedAgents:
|
|
41992
|
+
lastSelectedAgents: z18.array(z18.string()).optional()
|
|
41875
41993
|
});
|
|
41876
|
-
var LocalSkillLockEntrySchema =
|
|
41994
|
+
var LocalSkillLockEntrySchema = z18.object({
|
|
41877
41995
|
/** 技能来源(npm 包名、owner/repo、本地路径等)。 */
|
|
41878
|
-
source:
|
|
41996
|
+
source: z18.string(),
|
|
41879
41997
|
/** 当 source 被规范化时,保留原始远端 URL(可选)。 */
|
|
41880
|
-
sourceUrl:
|
|
41998
|
+
sourceUrl: z18.string().optional(),
|
|
41881
41999
|
/** 安装时使用的分支或 tag ref(可选)。 */
|
|
41882
|
-
ref:
|
|
42000
|
+
ref: z18.string().optional(),
|
|
41883
42001
|
/** 源类型(`github`、`node_modules`、`local` 等)。 */
|
|
41884
|
-
sourceType:
|
|
42002
|
+
sourceType: z18.string(),
|
|
41885
42003
|
/**
|
|
41886
42004
|
* 源仓库内 SKILL.md 的路径(如 `skills/pdf/SKILL.md`)。
|
|
41887
42005
|
* 升级时按它定向重装;非仓库源可缺省。
|
|
41888
42006
|
*/
|
|
41889
|
-
skillPath:
|
|
42007
|
+
skillPath: z18.string().optional(),
|
|
41890
42008
|
/**
|
|
41891
42009
|
* 由技能目录所有文件计算的 SHA-256。
|
|
41892
42010
|
* 与全局锁的 GitHub tree SHA 不同。
|
|
41893
42011
|
*/
|
|
41894
|
-
computedHash:
|
|
42012
|
+
computedHash: z18.string(),
|
|
41895
42013
|
/** Eve subagent 目标(可选),daemon 不消费。 */
|
|
41896
|
-
subagents:
|
|
42014
|
+
subagents: z18.array(z18.string()).optional()
|
|
41897
42015
|
});
|
|
41898
|
-
var LocalSkillLockFileSchema =
|
|
42016
|
+
var LocalSkillLockFileSchema = z18.object({
|
|
41899
42017
|
/** schema 版本;当前固定为 1。 */
|
|
41900
|
-
version:
|
|
42018
|
+
version: z18.literal(1),
|
|
41901
42019
|
/** 技能名 → 记录的映射。 */
|
|
41902
|
-
skills:
|
|
42020
|
+
skills: z18.record(z18.string(), LocalSkillLockEntrySchema)
|
|
41903
42021
|
});
|
|
41904
42022
|
function parseGlobalSkillLock(value) {
|
|
41905
42023
|
const parsed = SkillLockFileSchema.safeParse(value);
|
|
@@ -42356,6 +42474,1544 @@ function safeParseJsonValue(source) {
|
|
|
42356
42474
|
}
|
|
42357
42475
|
}
|
|
42358
42476
|
|
|
42477
|
+
// src/daemon/skill-search/service.ts
|
|
42478
|
+
import fs16 from "node:fs";
|
|
42479
|
+
import path18 from "node:path";
|
|
42480
|
+
|
|
42481
|
+
// src/daemon/workspace-registry/persistence.ts
|
|
42482
|
+
import fs8 from "node:fs";
|
|
42483
|
+
import path11 from "node:path";
|
|
42484
|
+
|
|
42485
|
+
// src/daemon/workspace-registry/state.ts
|
|
42486
|
+
import path10 from "node:path";
|
|
42487
|
+
import { z as z19 } from "zod";
|
|
42488
|
+
var StoredWorkspaceSchema = z19.object({
|
|
42489
|
+
id: ImportedWorkspaceIdSchema,
|
|
42490
|
+
label: z19.string().trim().min(1),
|
|
42491
|
+
path: z19.string().min(1).refine(path10.isAbsolute, "Workspace path must be absolute.").refine((value) => path10.normalize(value) === value, "Workspace path must be normalized.")
|
|
42492
|
+
}).strict();
|
|
42493
|
+
var WorkspaceRegistryStateSchema = z19.object({
|
|
42494
|
+
schemaVersion: z19.literal(2),
|
|
42495
|
+
activeId: WorkspaceIdSchema,
|
|
42496
|
+
workspaces: z19.array(StoredWorkspaceSchema)
|
|
42497
|
+
}).strict().superRefine((state, context) => {
|
|
42498
|
+
const ids = /* @__PURE__ */ new Set();
|
|
42499
|
+
const paths = /* @__PURE__ */ new Set();
|
|
42500
|
+
for (const [index, workspace] of state.workspaces.entries()) {
|
|
42501
|
+
if (workspace.id !== opaquePathId("ws", workspace.path)) {
|
|
42502
|
+
context.addIssue({
|
|
42503
|
+
code: "custom",
|
|
42504
|
+
message: "Workspace ID does not match its path.",
|
|
42505
|
+
path: ["workspaces", index, "id"]
|
|
42506
|
+
});
|
|
42507
|
+
}
|
|
42508
|
+
if (ids.has(workspace.id)) {
|
|
42509
|
+
context.addIssue({
|
|
42510
|
+
code: "custom",
|
|
42511
|
+
message: `Duplicate Workspace ID: ${workspace.id}`,
|
|
42512
|
+
path: ["workspaces", index, "id"]
|
|
42513
|
+
});
|
|
42514
|
+
}
|
|
42515
|
+
if (paths.has(workspace.path)) {
|
|
42516
|
+
context.addIssue({
|
|
42517
|
+
code: "custom",
|
|
42518
|
+
message: `Duplicate Workspace path: ${workspace.path}`,
|
|
42519
|
+
path: ["workspaces", index, "path"]
|
|
42520
|
+
});
|
|
42521
|
+
}
|
|
42522
|
+
ids.add(workspace.id);
|
|
42523
|
+
paths.add(workspace.path);
|
|
42524
|
+
}
|
|
42525
|
+
if (state.activeId !== GLOBAL_WORKSPACE_ID && !ids.has(state.activeId)) {
|
|
42526
|
+
context.addIssue({
|
|
42527
|
+
code: "custom",
|
|
42528
|
+
message: `Active Workspace is not registered: ${state.activeId}`,
|
|
42529
|
+
path: ["activeId"]
|
|
42530
|
+
});
|
|
42531
|
+
}
|
|
42532
|
+
});
|
|
42533
|
+
function emptyRegistryState() {
|
|
42534
|
+
return { schemaVersion: 2, activeId: GLOBAL_WORKSPACE_ID, workspaces: [] };
|
|
42535
|
+
}
|
|
42536
|
+
function importWorkspace(state, entry) {
|
|
42537
|
+
const existing = state.workspaces.find((workspace) => workspace.path === entry.path);
|
|
42538
|
+
if (existing) return { state, entry: existing, changed: false };
|
|
42539
|
+
const collision = state.workspaces.find((workspace) => workspace.id === entry.id);
|
|
42540
|
+
if (collision) {
|
|
42541
|
+
throw new Error(`Workspace ID collision between ${collision.path} and ${entry.path}`);
|
|
42542
|
+
}
|
|
42543
|
+
return {
|
|
42544
|
+
state: {
|
|
42545
|
+
...state,
|
|
42546
|
+
activeId: entry.id,
|
|
42547
|
+
workspaces: [...state.workspaces, entry]
|
|
42548
|
+
},
|
|
42549
|
+
entry,
|
|
42550
|
+
changed: true
|
|
42551
|
+
};
|
|
42552
|
+
}
|
|
42553
|
+
function forgetWorkspace(state, id) {
|
|
42554
|
+
if (!state.workspaces.some((workspace) => workspace.id === id)) return null;
|
|
42555
|
+
return {
|
|
42556
|
+
...state,
|
|
42557
|
+
activeId: state.activeId === id ? GLOBAL_WORKSPACE_ID : state.activeId,
|
|
42558
|
+
workspaces: state.workspaces.filter((workspace) => workspace.id !== id)
|
|
42559
|
+
};
|
|
42560
|
+
}
|
|
42561
|
+
function activateWorkspace(state, id) {
|
|
42562
|
+
if (id !== GLOBAL_WORKSPACE_ID && !state.workspaces.some((workspace) => workspace.id === id)) {
|
|
42563
|
+
return null;
|
|
42564
|
+
}
|
|
42565
|
+
if (state.activeId === id) return state;
|
|
42566
|
+
return { ...state, activeId: id };
|
|
42567
|
+
}
|
|
42568
|
+
|
|
42569
|
+
// src/daemon/workspace-registry/persistence.ts
|
|
42570
|
+
var WORKSPACES_FILE = "workspaces.json";
|
|
42571
|
+
function createWorkspaceRegistryPersistence() {
|
|
42572
|
+
const file = path11.join(appDir(), WORKSPACES_FILE);
|
|
42573
|
+
return {
|
|
42574
|
+
load: () => loadState2(file),
|
|
42575
|
+
commit: (state) => commitState2(file, state)
|
|
42576
|
+
};
|
|
42577
|
+
}
|
|
42578
|
+
function loadState2(file) {
|
|
42579
|
+
if (!fs8.existsSync(file)) return emptyRegistryState();
|
|
42580
|
+
let source;
|
|
42581
|
+
try {
|
|
42582
|
+
source = fs8.readFileSync(file, "utf8");
|
|
42583
|
+
} catch (error) {
|
|
42584
|
+
throw new Error(
|
|
42585
|
+
`Cannot read workspace registry ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
42586
|
+
);
|
|
42587
|
+
}
|
|
42588
|
+
return safeParseJson(source, WorkspaceRegistryStateSchema) ?? emptyRegistryState();
|
|
42589
|
+
}
|
|
42590
|
+
function commitState2(file, state) {
|
|
42591
|
+
const validated = WorkspaceRegistryStateSchema.parse(state);
|
|
42592
|
+
fs8.mkdirSync(path11.dirname(file), { recursive: true, mode: 448 });
|
|
42593
|
+
atomicWriteUtf8(file, `${JSON.stringify(validated, null, 2)}
|
|
42594
|
+
`);
|
|
42595
|
+
}
|
|
42596
|
+
|
|
42597
|
+
// src/daemon/provider-roots.ts
|
|
42598
|
+
import fs9 from "node:fs";
|
|
42599
|
+
import os5 from "node:os";
|
|
42600
|
+
import path12 from "node:path";
|
|
42601
|
+
function requireProvider(providerId) {
|
|
42602
|
+
const provider = providerCatalogEntry(providerId);
|
|
42603
|
+
if (!provider) throw new DomainError("NOT_FOUND", `Provider not found: ${providerId}`);
|
|
42604
|
+
return provider;
|
|
42605
|
+
}
|
|
42606
|
+
function globalProviderRoot(provider) {
|
|
42607
|
+
if (provider.globalPath === null) return null;
|
|
42608
|
+
const home = os5.homedir();
|
|
42609
|
+
const override = globalHomeOverride(provider.id);
|
|
42610
|
+
if (override) return path12.join(override, "skills");
|
|
42611
|
+
if (provider.id === "openclaw") return openClawRoot(home);
|
|
42612
|
+
if (provider.globalPath.startsWith(".config/")) {
|
|
42613
|
+
const configHome = process.env.XDG_CONFIG_HOME?.trim() || path12.join(home, ".config");
|
|
42614
|
+
return path12.join(configHome, provider.globalPath.slice(".config/".length));
|
|
42615
|
+
}
|
|
42616
|
+
return path12.join(home, provider.globalPath);
|
|
42617
|
+
}
|
|
42618
|
+
function importedProviderRoot(workspaceRoot, provider) {
|
|
42619
|
+
const root = path12.resolve(workspaceRoot, provider.workspacePath);
|
|
42620
|
+
const relative2 = path12.relative(workspaceRoot, root);
|
|
42621
|
+
if (relative2.startsWith("..") || path12.isAbsolute(relative2)) {
|
|
42622
|
+
throw new Error(`Provider root escaped Workspace: ${provider.id}`);
|
|
42623
|
+
}
|
|
42624
|
+
return root;
|
|
42625
|
+
}
|
|
42626
|
+
function providerRootAvailable(root) {
|
|
42627
|
+
if (!root) return false;
|
|
42628
|
+
try {
|
|
42629
|
+
return fs9.statSync(root).isDirectory();
|
|
42630
|
+
} catch {
|
|
42631
|
+
return false;
|
|
42632
|
+
}
|
|
42633
|
+
}
|
|
42634
|
+
function globalHomeOverride(providerId) {
|
|
42635
|
+
const variable = providerId === "codex" ? "CODEX_HOME" : providerId === "claude-code" ? "CLAUDE_CONFIG_DIR" : providerId === "mistral-vibe" ? "VIBE_HOME" : providerId === "hermes-agent" ? "HERMES_HOME" : providerId === "autohand-code" ? "AUTOHAND_HOME" : providerId === "grok" ? "GROK_HOME" : null;
|
|
42636
|
+
const value = variable ? process.env[variable]?.trim() : void 0;
|
|
42637
|
+
return value || null;
|
|
42638
|
+
}
|
|
42639
|
+
function openClawRoot(home) {
|
|
42640
|
+
for (const directory of [".openclaw", ".clawdbot", ".moltbot"]) {
|
|
42641
|
+
if (providerRootAvailable(path12.join(home, directory)))
|
|
42642
|
+
return path12.join(home, directory, "skills");
|
|
42643
|
+
}
|
|
42644
|
+
return path12.join(home, ".openclaw", "skills");
|
|
42645
|
+
}
|
|
42646
|
+
|
|
42647
|
+
// src/daemon/skill-search/canonicalize.ts
|
|
42648
|
+
import fs11 from "node:fs";
|
|
42649
|
+
import path14 from "node:path";
|
|
42650
|
+
|
|
42651
|
+
// src/daemon/skill-search/content-files.ts
|
|
42652
|
+
import fs10 from "node:fs";
|
|
42653
|
+
import path13 from "node:path";
|
|
42654
|
+
var BUILTIN_EXCLUDED_DIRS = [
|
|
42655
|
+
"node_modules",
|
|
42656
|
+
"build",
|
|
42657
|
+
"dist",
|
|
42658
|
+
"target",
|
|
42659
|
+
"__pycache__",
|
|
42660
|
+
"tmp",
|
|
42661
|
+
"logs"
|
|
42662
|
+
];
|
|
42663
|
+
var MAX_DEPTH = 4;
|
|
42664
|
+
var MAX_FILES = 32;
|
|
42665
|
+
var IDENTITY_FILE_NAMES = /* @__PURE__ */ new Set(["skill.md", ".skill.md"]);
|
|
42666
|
+
function collectExtraMarkdownFiles(canonicalPath, excludedDirs = /* @__PURE__ */ new Set()) {
|
|
42667
|
+
const effective = /* @__PURE__ */ new Set([...BUILTIN_EXCLUDED_DIRS, ...excludedDirs]);
|
|
42668
|
+
const files = [];
|
|
42669
|
+
collectDirectory(canonicalPath, 0, effective, files);
|
|
42670
|
+
files.sort((left, right) => compareString(left.path, right.path));
|
|
42671
|
+
return files.slice(0, MAX_FILES);
|
|
42672
|
+
}
|
|
42673
|
+
function collectDirectory(directory, depth, excludedDirs, files) {
|
|
42674
|
+
if (depth > MAX_DEPTH || files.length >= MAX_FILES) return;
|
|
42675
|
+
let dirents;
|
|
42676
|
+
try {
|
|
42677
|
+
dirents = fs10.readdirSync(directory, { withFileTypes: true });
|
|
42678
|
+
} catch {
|
|
42679
|
+
return;
|
|
42680
|
+
}
|
|
42681
|
+
dirents.sort((left, right) => compareString(left.name, right.name));
|
|
42682
|
+
for (const dirent of dirents) {
|
|
42683
|
+
if (files.length >= MAX_FILES) return;
|
|
42684
|
+
const entryPath = path13.join(directory, dirent.name);
|
|
42685
|
+
if (dirent.isDirectory()) {
|
|
42686
|
+
if (dirent.name.startsWith(".") || excludedDirs.has(dirent.name)) continue;
|
|
42687
|
+
let descentStat;
|
|
42688
|
+
try {
|
|
42689
|
+
descentStat = fs10.lstatSync(entryPath);
|
|
42690
|
+
} catch {
|
|
42691
|
+
continue;
|
|
42692
|
+
}
|
|
42693
|
+
if (!descentStat.isDirectory() || descentStat.isSymbolicLink()) continue;
|
|
42694
|
+
collectDirectory(entryPath, depth + 1, excludedDirs, files);
|
|
42695
|
+
continue;
|
|
42696
|
+
}
|
|
42697
|
+
if (!dirent.isFile()) continue;
|
|
42698
|
+
if (!dirent.name.toLowerCase().endsWith(".md")) continue;
|
|
42699
|
+
if (IDENTITY_FILE_NAMES.has(dirent.name.toLowerCase())) continue;
|
|
42700
|
+
if (!isRegularFile(entryPath)) continue;
|
|
42701
|
+
let stat2;
|
|
42702
|
+
try {
|
|
42703
|
+
stat2 = fs10.statSync(entryPath);
|
|
42704
|
+
} catch {
|
|
42705
|
+
continue;
|
|
42706
|
+
}
|
|
42707
|
+
if (!stat2.isFile()) continue;
|
|
42708
|
+
files.push({
|
|
42709
|
+
path: entryPath,
|
|
42710
|
+
mtimeMs: stat2.mtimeMs,
|
|
42711
|
+
size: stat2.size,
|
|
42712
|
+
ino: stat2.ino,
|
|
42713
|
+
ctimeMs: stat2.ctimeMs
|
|
42714
|
+
});
|
|
42715
|
+
}
|
|
42716
|
+
}
|
|
42717
|
+
function isRegularFile(file) {
|
|
42718
|
+
try {
|
|
42719
|
+
return fs10.lstatSync(file).isFile();
|
|
42720
|
+
} catch {
|
|
42721
|
+
return false;
|
|
42722
|
+
}
|
|
42723
|
+
}
|
|
42724
|
+
function compareString(left, right) {
|
|
42725
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
42726
|
+
}
|
|
42727
|
+
|
|
42728
|
+
// src/daemon/skill-search/canonicalize.ts
|
|
42729
|
+
function canonicalizeCandidates(entries, excludedDirs = /* @__PURE__ */ new Set()) {
|
|
42730
|
+
const groups = /* @__PURE__ */ new Map();
|
|
42731
|
+
for (const entry of entries) {
|
|
42732
|
+
let canonicalPath;
|
|
42733
|
+
try {
|
|
42734
|
+
canonicalPath = fs11.realpathSync(entry.path);
|
|
42735
|
+
} catch {
|
|
42736
|
+
continue;
|
|
42737
|
+
}
|
|
42738
|
+
const installation = {
|
|
42739
|
+
path: entry.path,
|
|
42740
|
+
workspaceId: entry.workspaceId,
|
|
42741
|
+
providerId: entry.providerId
|
|
42742
|
+
};
|
|
42743
|
+
const group = groups.get(canonicalPath) ?? { installations: [] };
|
|
42744
|
+
const exists = group.installations.some(
|
|
42745
|
+
(candidate) => candidate.path === installation.path && candidate.workspaceId === installation.workspaceId && candidate.providerId === installation.providerId
|
|
42746
|
+
);
|
|
42747
|
+
if (!exists) group.installations.push(installation);
|
|
42748
|
+
groups.set(canonicalPath, group);
|
|
42749
|
+
}
|
|
42750
|
+
const scans = [];
|
|
42751
|
+
for (const [canonicalPath, group] of groups) {
|
|
42752
|
+
const source = selectSourceFile(canonicalPath);
|
|
42753
|
+
if (!source) continue;
|
|
42754
|
+
let sourceStat;
|
|
42755
|
+
try {
|
|
42756
|
+
sourceStat = fs11.statSync(source.sourcePath);
|
|
42757
|
+
} catch {
|
|
42758
|
+
continue;
|
|
42759
|
+
}
|
|
42760
|
+
const extras = collectExtraMarkdownFiles(canonicalPath, excludedDirs);
|
|
42761
|
+
const files = [
|
|
42762
|
+
{
|
|
42763
|
+
path: source.sourcePath,
|
|
42764
|
+
mtimeMs: sourceStat.mtimeMs,
|
|
42765
|
+
size: sourceStat.size,
|
|
42766
|
+
ino: sourceStat.ino,
|
|
42767
|
+
ctimeMs: sourceStat.ctimeMs
|
|
42768
|
+
},
|
|
42769
|
+
...extras
|
|
42770
|
+
].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
42771
|
+
scans.push({
|
|
42772
|
+
id: SkillIdSchema.parse(opaquePathId("sk", canonicalPath)),
|
|
42773
|
+
canonicalPath,
|
|
42774
|
+
installations: group.installations.sort(
|
|
42775
|
+
(left, right) => compareString2(left.path, right.path)
|
|
42776
|
+
),
|
|
42777
|
+
sourceFile: source.sourceFile,
|
|
42778
|
+
sourcePath: source.sourcePath,
|
|
42779
|
+
disabled: source.disabled,
|
|
42780
|
+
conflict: source.conflict,
|
|
42781
|
+
files
|
|
42782
|
+
});
|
|
42783
|
+
}
|
|
42784
|
+
scans.sort((left, right) => compareString2(left.canonicalPath, right.canonicalPath));
|
|
42785
|
+
return scans;
|
|
42786
|
+
}
|
|
42787
|
+
function selectSourceFile(canonicalPath) {
|
|
42788
|
+
const enabled = path14.join(canonicalPath, "SKILL.md");
|
|
42789
|
+
const disabledFile = path14.join(canonicalPath, ".SKILL.md");
|
|
42790
|
+
const hasEnabled = isRegularFile2(enabled);
|
|
42791
|
+
const hasDisabled = isRegularFile2(disabledFile);
|
|
42792
|
+
if (hasEnabled) {
|
|
42793
|
+
return { sourceFile: "SKILL.md", sourcePath: enabled, disabled: false, conflict: hasDisabled };
|
|
42794
|
+
}
|
|
42795
|
+
if (hasDisabled) {
|
|
42796
|
+
return { sourceFile: ".SKILL.md", sourcePath: disabledFile, disabled: true, conflict: false };
|
|
42797
|
+
}
|
|
42798
|
+
return null;
|
|
42799
|
+
}
|
|
42800
|
+
function compareString2(left, right) {
|
|
42801
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
42802
|
+
}
|
|
42803
|
+
function isRegularFile2(file) {
|
|
42804
|
+
try {
|
|
42805
|
+
return fs11.lstatSync(file).isFile();
|
|
42806
|
+
} catch {
|
|
42807
|
+
return false;
|
|
42808
|
+
}
|
|
42809
|
+
}
|
|
42810
|
+
|
|
42811
|
+
// src/daemon/skill-search/config.ts
|
|
42812
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
42813
|
+
import fs13 from "node:fs";
|
|
42814
|
+
import path16 from "node:path";
|
|
42815
|
+
import { parse as parseToml } from "smol-toml";
|
|
42816
|
+
import { z as z22 } from "zod";
|
|
42817
|
+
|
|
42818
|
+
// src/daemon/skill-search/index.ts
|
|
42819
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
42820
|
+
import { createRequire as createRequire4 } from "node:module";
|
|
42821
|
+
import fs12 from "node:fs";
|
|
42822
|
+
import path15 from "node:path";
|
|
42823
|
+
import MiniSearch from "minisearch";
|
|
42824
|
+
import { z as z21 } from "zod";
|
|
42825
|
+
|
|
42826
|
+
// src/daemon/skill-search/parser.ts
|
|
42827
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
42828
|
+
import matter3 from "gray-matter";
|
|
42829
|
+
import { z as z20 } from "zod";
|
|
42830
|
+
var PARSER_VERSION = "matter-mdset-v2";
|
|
42831
|
+
var HEADINGS_LIMIT = 30;
|
|
42832
|
+
var BODY_LIMIT = 12e3;
|
|
42833
|
+
var EXTRA_FILE_LIMIT = 6e3;
|
|
42834
|
+
var EXTRA_TOTAL_LIMIT = 12e3;
|
|
42835
|
+
var FENCE_MARKER_RE = /^[ \t]*(?:```|~~~)/;
|
|
42836
|
+
var HEADING_RE = /^#{1,4}[ \t]+(.+)$/gm;
|
|
42837
|
+
var FrontmatterShapeSchema = z20.object({
|
|
42838
|
+
name: z20.string().min(1),
|
|
42839
|
+
description: z20.string().min(1)
|
|
42840
|
+
});
|
|
42841
|
+
function parseSkillDocumentSet(sourceRaw, extras, directoryName) {
|
|
42842
|
+
return finalizeDocument(splitFrontmatter(sourceRaw), sourceRaw, extras, directoryName);
|
|
42843
|
+
}
|
|
42844
|
+
function finalizeDocument(split, sourceRaw, extras, directoryName) {
|
|
42845
|
+
const hash = createHash6("sha256").update(sourceRaw);
|
|
42846
|
+
for (const extra of extras) hash.update(extra.raw);
|
|
42847
|
+
const headingLines = [];
|
|
42848
|
+
const bodyLines = [];
|
|
42849
|
+
let inFence = false;
|
|
42850
|
+
for (const line of split.content.split("\n")) {
|
|
42851
|
+
if (FENCE_MARKER_RE.test(line)) {
|
|
42852
|
+
inFence = !inFence;
|
|
42853
|
+
continue;
|
|
42854
|
+
}
|
|
42855
|
+
bodyLines.push(line);
|
|
42856
|
+
if (!inFence) headingLines.push(line);
|
|
42857
|
+
}
|
|
42858
|
+
const headings = collectHeadings(headingLines.join("\n"));
|
|
42859
|
+
const skillBody = bodyLines.join("\n").slice(0, BODY_LIMIT);
|
|
42860
|
+
let extraBudget = EXTRA_TOTAL_LIMIT;
|
|
42861
|
+
const extraParts = [];
|
|
42862
|
+
for (const extra of extras) {
|
|
42863
|
+
if (extraBudget <= 0) break;
|
|
42864
|
+
const text = stripFences(extra.raw.toString("utf8")).trim().slice(0, EXTRA_FILE_LIMIT);
|
|
42865
|
+
if (!text) continue;
|
|
42866
|
+
const bounded = text.slice(0, extraBudget);
|
|
42867
|
+
extraBudget -= bounded.length;
|
|
42868
|
+
extraParts.push(bounded);
|
|
42869
|
+
}
|
|
42870
|
+
const body = extraParts.length > 0 ? `${skillBody}
|
|
42871
|
+
${extraParts.join("\n")}` : skillBody;
|
|
42872
|
+
const valid = safeParseExternal(FrontmatterShapeSchema, split.frontmatter);
|
|
42873
|
+
const frontmatterRecord = split.frontmatter !== null && typeof split.frontmatter === "object" ? split.frontmatter : {};
|
|
42874
|
+
return {
|
|
42875
|
+
name: valid ? valid.name : directoryName,
|
|
42876
|
+
description: valid ? valid.description : "",
|
|
42877
|
+
keywords: narrowStringList(frontmatterRecord.keywords),
|
|
42878
|
+
triggers: narrowStringList(frontmatterRecord.triggers),
|
|
42879
|
+
headings,
|
|
42880
|
+
body,
|
|
42881
|
+
contentHash: hash.digest("hex"),
|
|
42882
|
+
invalidFrontmatter: valid === null
|
|
42883
|
+
};
|
|
42884
|
+
}
|
|
42885
|
+
function splitFrontmatter(raw) {
|
|
42886
|
+
const text = raw.toString("utf8");
|
|
42887
|
+
try {
|
|
42888
|
+
const parsed = matter3(text);
|
|
42889
|
+
return { frontmatter: parsed.data ?? {}, content: parsed.content ?? "" };
|
|
42890
|
+
} catch {
|
|
42891
|
+
return { frontmatter: {}, content: text };
|
|
42892
|
+
}
|
|
42893
|
+
}
|
|
42894
|
+
function stripFences(text) {
|
|
42895
|
+
const lines = [];
|
|
42896
|
+
let inFence = false;
|
|
42897
|
+
for (const line of text.split("\n")) {
|
|
42898
|
+
if (FENCE_MARKER_RE.test(line)) {
|
|
42899
|
+
inFence = !inFence;
|
|
42900
|
+
continue;
|
|
42901
|
+
}
|
|
42902
|
+
lines.push(line);
|
|
42903
|
+
}
|
|
42904
|
+
return lines.join("\n");
|
|
42905
|
+
}
|
|
42906
|
+
function narrowStringList(value) {
|
|
42907
|
+
if (typeof value === "string") return [value];
|
|
42908
|
+
if (!Array.isArray(value)) return [];
|
|
42909
|
+
return value.filter((entry) => typeof entry === "string");
|
|
42910
|
+
}
|
|
42911
|
+
function collectHeadings(content) {
|
|
42912
|
+
const headings = [];
|
|
42913
|
+
for (const match of content.matchAll(HEADING_RE)) {
|
|
42914
|
+
headings.push(match[1].trim());
|
|
42915
|
+
if (headings.length >= HEADINGS_LIMIT) break;
|
|
42916
|
+
}
|
|
42917
|
+
return headings.join("\n");
|
|
42918
|
+
}
|
|
42919
|
+
|
|
42920
|
+
// src/daemon/skill-search/ranking.ts
|
|
42921
|
+
var RANKING_VERSION = "rerank-2026-09-18-v2";
|
|
42922
|
+
var TOP_CANDIDATES = 40;
|
|
42923
|
+
var RERANK_WEIGHTS = {
|
|
42924
|
+
exactName: 0.9,
|
|
42925
|
+
namePrefix: 0.5,
|
|
42926
|
+
queryInName: 0.4,
|
|
42927
|
+
keywordExact: 0.3,
|
|
42928
|
+
descCoverage: 0.2
|
|
42929
|
+
};
|
|
42930
|
+
var RERANK_CAP = 1;
|
|
42931
|
+
var MIX = { bm25: 0.7, rerank: 0.3 };
|
|
42932
|
+
var NORM_K = 8;
|
|
42933
|
+
function rankResults(candidates, query, limit, tokenize) {
|
|
42934
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
42935
|
+
const queryTokens = [...new Set(tokenize(query))];
|
|
42936
|
+
const rerankCache = /* @__PURE__ */ new Map();
|
|
42937
|
+
const groups = /* @__PURE__ */ new Map();
|
|
42938
|
+
for (const candidate of candidates) {
|
|
42939
|
+
const group = groups.get(candidate.contentHash) ?? [];
|
|
42940
|
+
group.push(candidate);
|
|
42941
|
+
groups.set(candidate.contentHash, group);
|
|
42942
|
+
}
|
|
42943
|
+
const orderedGroups = [...groups.values()].map((group) => group.slice().sort(byGroupOrder));
|
|
42944
|
+
const pool = orderedGroups.map((group) => group[0]).sort(byGroupOrder).slice(0, TOP_CANDIDATES);
|
|
42945
|
+
const scored = pool.map((candidate) => {
|
|
42946
|
+
const rerank = rerankScore(candidate, normalizedQuery, queryTokens, tokenize, rerankCache);
|
|
42947
|
+
return {
|
|
42948
|
+
candidate,
|
|
42949
|
+
final: stableFinal(
|
|
42950
|
+
MIX.bm25 * (candidate.bm25 / (candidate.bm25 + NORM_K)) + MIX.rerank * rerank
|
|
42951
|
+
)
|
|
42952
|
+
};
|
|
42953
|
+
});
|
|
42954
|
+
scored.sort(byFrozenTieBreak);
|
|
42955
|
+
return scored.slice(0, limit).map(({ candidate, final }) => {
|
|
42956
|
+
const group = groups.get(candidate.contentHash) ?? [candidate];
|
|
42957
|
+
const members = group.filter((member) => member !== candidate).sort(byGroupOrder);
|
|
42958
|
+
return {
|
|
42959
|
+
id: candidate.id,
|
|
42960
|
+
name: candidate.name,
|
|
42961
|
+
description: candidate.description,
|
|
42962
|
+
canonicalPath: candidate.canonicalPath,
|
|
42963
|
+
installations: mergeInstallations(candidate, members),
|
|
42964
|
+
contentHash: candidate.contentHash,
|
|
42965
|
+
disabled: candidate.disabled,
|
|
42966
|
+
conflict: candidate.conflict,
|
|
42967
|
+
score: final,
|
|
42968
|
+
duplicates: members.map((member) => ({
|
|
42969
|
+
id: member.id,
|
|
42970
|
+
canonicalPath: member.canonicalPath
|
|
42971
|
+
}))
|
|
42972
|
+
};
|
|
42973
|
+
});
|
|
42974
|
+
}
|
|
42975
|
+
function byGroupOrder(left, right) {
|
|
42976
|
+
if (left.bm25 !== right.bm25) return right.bm25 - left.bm25;
|
|
42977
|
+
return left.canonicalPath < right.canonicalPath ? -1 : left.canonicalPath > right.canonicalPath ? 1 : 0;
|
|
42978
|
+
}
|
|
42979
|
+
function mergeInstallations(primary, members) {
|
|
42980
|
+
const seen = /* @__PURE__ */ new Set();
|
|
42981
|
+
const merged = [];
|
|
42982
|
+
for (const installation of [primary, ...members].flatMap(
|
|
42983
|
+
(candidate) => candidate.installations
|
|
42984
|
+
)) {
|
|
42985
|
+
const key = `${installation.path}|${installation.workspaceId}|${installation.providerId}`;
|
|
42986
|
+
if (seen.has(key)) continue;
|
|
42987
|
+
seen.add(key);
|
|
42988
|
+
merged.push(installation);
|
|
42989
|
+
}
|
|
42990
|
+
return merged;
|
|
42991
|
+
}
|
|
42992
|
+
function stableFinal(raw) {
|
|
42993
|
+
return Number(raw.toPrecision(12));
|
|
42994
|
+
}
|
|
42995
|
+
function byFrozenTieBreak(left, right) {
|
|
42996
|
+
if (left.final !== right.final) return right.final - left.final;
|
|
42997
|
+
if (left.candidate.name !== right.candidate.name) {
|
|
42998
|
+
return left.candidate.name < right.candidate.name ? -1 : 1;
|
|
42999
|
+
}
|
|
43000
|
+
return left.candidate.canonicalPath < right.candidate.canonicalPath ? -1 : left.candidate.canonicalPath > right.candidate.canonicalPath ? 1 : 0;
|
|
43001
|
+
}
|
|
43002
|
+
function rerankScore(candidate, normalizedQuery, queryTokens, tokenize, descriptionTokenCache) {
|
|
43003
|
+
if (normalizedQuery === "") return 0;
|
|
43004
|
+
const lowerName = candidate.name.toLowerCase();
|
|
43005
|
+
let score = 0;
|
|
43006
|
+
if (lowerName === normalizedQuery) score += RERANK_WEIGHTS.exactName;
|
|
43007
|
+
if (lowerName.startsWith(normalizedQuery)) score += RERANK_WEIGHTS.namePrefix;
|
|
43008
|
+
if (lowerName.includes(normalizedQuery)) score += RERANK_WEIGHTS.queryInName;
|
|
43009
|
+
if (candidate.keywords.some((keyword) => keyword.toLowerCase() === normalizedQuery)) {
|
|
43010
|
+
score += RERANK_WEIGHTS.keywordExact;
|
|
43011
|
+
}
|
|
43012
|
+
if (queryTokens.length > 0) {
|
|
43013
|
+
let tokens2 = descriptionTokenCache.get(candidate.description);
|
|
43014
|
+
if (!tokens2) {
|
|
43015
|
+
tokens2 = new Set(tokenize(candidate.description));
|
|
43016
|
+
descriptionTokenCache.set(candidate.description, tokens2);
|
|
43017
|
+
}
|
|
43018
|
+
const covered = queryTokens.filter((token) => tokens2.has(token)).length;
|
|
43019
|
+
score += RERANK_WEIGHTS.descCoverage * (covered / queryTokens.length);
|
|
43020
|
+
}
|
|
43021
|
+
return Math.min(score, RERANK_CAP);
|
|
43022
|
+
}
|
|
43023
|
+
|
|
43024
|
+
// src/daemon/skill-search/tokenizer.ts
|
|
43025
|
+
var TOKENIZER_VERSION = "segmenter-bigram-v1";
|
|
43026
|
+
var CJK_SCRIPTS_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
|
|
43027
|
+
var CJK_RUN_SOURCE = "[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]+";
|
|
43028
|
+
var LATIN_RUN_SOURCE = "[@A-Za-z0-9:/._-]+";
|
|
43029
|
+
var RUN_RE = new RegExp(`${CJK_RUN_SOURCE}|${LATIN_RUN_SOURCE}|.`, "gu");
|
|
43030
|
+
var CJK_RUN_START_RE = new RegExp(`^${CJK_RUN_SOURCE}`, "u");
|
|
43031
|
+
var CAMEL_RE = new RegExp("\\p{Lu}+(?=\\p{Lu}[\\p{Ll}\\d])|\\p{Lu}[\\p{Ll}\\d]*|\\p{Lu}+|\\p{Ll}+|\\d+", "gu");
|
|
43032
|
+
function probeSegmenter() {
|
|
43033
|
+
if (typeof Intl.Segmenter === "undefined") return null;
|
|
43034
|
+
let segmenter2;
|
|
43035
|
+
try {
|
|
43036
|
+
segmenter2 = new Intl.Segmenter("zh", { granularity: "word" });
|
|
43037
|
+
} catch {
|
|
43038
|
+
return null;
|
|
43039
|
+
}
|
|
43040
|
+
const words = [...segmenter2.segment("\u7EC4\u4EF6\u8BBE\u8BA1")].filter((segment) => segment.isWordLike).map((segment) => segment.segment);
|
|
43041
|
+
if (words.length === 0) return null;
|
|
43042
|
+
if (words.every((word) => [...word].length === 1)) return null;
|
|
43043
|
+
return segmenter2;
|
|
43044
|
+
}
|
|
43045
|
+
function createSkillTokenizer() {
|
|
43046
|
+
const segmenter2 = probeSegmenter();
|
|
43047
|
+
function segmentCjk(text) {
|
|
43048
|
+
if (!segmenter2) return [...text];
|
|
43049
|
+
return [...segmenter2.segment(text)].filter((segment) => segment.isWordLike).map((segment) => segment.segment);
|
|
43050
|
+
}
|
|
43051
|
+
return { tokenize };
|
|
43052
|
+
function tokenize(text) {
|
|
43053
|
+
const normalized = text.normalize("NFKC");
|
|
43054
|
+
const out = [];
|
|
43055
|
+
for (const match of normalized.matchAll(RUN_RE)) {
|
|
43056
|
+
const run = match[0];
|
|
43057
|
+
if (CJK_RUN_START_RE.test(run)) {
|
|
43058
|
+
emitCjkRun(segmentCjk(run), out);
|
|
43059
|
+
} else if (/^[@A-Za-z0-9]/.test(run)) {
|
|
43060
|
+
splitLatinIdent(run, out);
|
|
43061
|
+
}
|
|
43062
|
+
}
|
|
43063
|
+
const seen = /* @__PURE__ */ new Set();
|
|
43064
|
+
const tokens2 = [];
|
|
43065
|
+
for (const token of out) {
|
|
43066
|
+
const lowered = token.toLowerCase();
|
|
43067
|
+
if (seen.has(lowered)) continue;
|
|
43068
|
+
seen.add(lowered);
|
|
43069
|
+
tokens2.push(lowered);
|
|
43070
|
+
}
|
|
43071
|
+
return tokens2.filter(
|
|
43072
|
+
(token) => CJK_SCRIPTS_RE.test(token) ? [...token].length >= 1 : token.length >= 2 || /\d/.test(token)
|
|
43073
|
+
);
|
|
43074
|
+
}
|
|
43075
|
+
}
|
|
43076
|
+
function emitCjkRun(words, out) {
|
|
43077
|
+
let i = 0;
|
|
43078
|
+
while (i < words.length) {
|
|
43079
|
+
const word = words[i];
|
|
43080
|
+
if ([...word].length >= 2) {
|
|
43081
|
+
out.push(word);
|
|
43082
|
+
i += 1;
|
|
43083
|
+
continue;
|
|
43084
|
+
}
|
|
43085
|
+
let j = i;
|
|
43086
|
+
while (j < words.length && [...words[j]].length === 1) j += 1;
|
|
43087
|
+
const singles = words.slice(i, j);
|
|
43088
|
+
if (singles.length === 1) {
|
|
43089
|
+
out.push(singles[0]);
|
|
43090
|
+
} else {
|
|
43091
|
+
for (let k = 0; k + 1 < singles.length; k++) {
|
|
43092
|
+
out.push(singles[k] + singles[k + 1]);
|
|
43093
|
+
}
|
|
43094
|
+
}
|
|
43095
|
+
i = j;
|
|
43096
|
+
}
|
|
43097
|
+
}
|
|
43098
|
+
function emitKebab(chunk, out) {
|
|
43099
|
+
const atoms = chunk.split(/[^A-Za-z0-9]+/).filter((atom) => atom.length > 0);
|
|
43100
|
+
for (const atom of atoms) {
|
|
43101
|
+
const words = atom.match(CAMEL_RE) ?? [atom];
|
|
43102
|
+
out.push(...words);
|
|
43103
|
+
if (words.length > 1) out.push(atom.toLowerCase());
|
|
43104
|
+
}
|
|
43105
|
+
if (atoms.length > 1) out.push(atoms.join("-").toLowerCase());
|
|
43106
|
+
}
|
|
43107
|
+
function splitLatinIdent(run, out) {
|
|
43108
|
+
const parts = run.split("/");
|
|
43109
|
+
if (parts.length >= 2) {
|
|
43110
|
+
const last = parts[parts.length - 1];
|
|
43111
|
+
const owner = parts[parts.length - 2];
|
|
43112
|
+
if (/^@?[a-z0-9][a-z0-9._-]*$/i.test(owner) && /^[a-z0-9][a-z0-9._-]*$/i.test(last)) {
|
|
43113
|
+
const scope = owner.replace(/^@/, "");
|
|
43114
|
+
out.push(scope, last, `${scope}/${last}`);
|
|
43115
|
+
}
|
|
43116
|
+
for (const part of parts) emitKebab(part, out);
|
|
43117
|
+
return;
|
|
43118
|
+
}
|
|
43119
|
+
emitKebab(run, out);
|
|
43120
|
+
}
|
|
43121
|
+
|
|
43122
|
+
// src/daemon/skill-search/index.ts
|
|
43123
|
+
var SCHEMA_VERSION = 3;
|
|
43124
|
+
var ENGINE_NAME = "minisearch";
|
|
43125
|
+
var SEARCH_INDEX_FILE = "search-index.json";
|
|
43126
|
+
var SEARCH_FIELDS = ["name", "description", "keywords", "triggers", "headings", "body"];
|
|
43127
|
+
var FIELD_BOOST = {
|
|
43128
|
+
name: 10,
|
|
43129
|
+
description: 6,
|
|
43130
|
+
keywords: 5,
|
|
43131
|
+
triggers: 5,
|
|
43132
|
+
headings: 3,
|
|
43133
|
+
body: 1
|
|
43134
|
+
};
|
|
43135
|
+
var STORE_FIELDS = [
|
|
43136
|
+
"name",
|
|
43137
|
+
"description",
|
|
43138
|
+
"keywords",
|
|
43139
|
+
"canonicalPath",
|
|
43140
|
+
"installations",
|
|
43141
|
+
"contentHash",
|
|
43142
|
+
"disabled",
|
|
43143
|
+
"conflict",
|
|
43144
|
+
"invalidFrontmatter"
|
|
43145
|
+
];
|
|
43146
|
+
var SEARCH_PREFIX = true;
|
|
43147
|
+
var SEARCH_FUZZY = 0.2;
|
|
43148
|
+
var REBUILD_DISCARD_RATIO = 0.2;
|
|
43149
|
+
var ENGINE_CONFIG_DIGEST = createHash7("sha256").update(
|
|
43150
|
+
JSON.stringify({
|
|
43151
|
+
fields: [...SEARCH_FIELDS],
|
|
43152
|
+
boost: FIELD_BOOST,
|
|
43153
|
+
prefix: SEARCH_PREFIX,
|
|
43154
|
+
fuzzy: SEARCH_FUZZY,
|
|
43155
|
+
processTerm: "identity"
|
|
43156
|
+
})
|
|
43157
|
+
).digest("hex").slice(0, 16);
|
|
43158
|
+
var SkillSearchIndexError = class extends DomainError {
|
|
43159
|
+
constructor(message, options2) {
|
|
43160
|
+
super("UNAVAILABLE", message, options2);
|
|
43161
|
+
this.name = "SkillSearchIndexError";
|
|
43162
|
+
}
|
|
43163
|
+
};
|
|
43164
|
+
var FileIdentitySchema = z21.object({
|
|
43165
|
+
path: z21.string().min(1),
|
|
43166
|
+
mtimeMs: z21.number(),
|
|
43167
|
+
size: z21.number(),
|
|
43168
|
+
ino: z21.number(),
|
|
43169
|
+
ctimeMs: z21.number()
|
|
43170
|
+
}).strict();
|
|
43171
|
+
var StatEnvelopeSchema = z21.object({
|
|
43172
|
+
canonicalPath: z21.string().min(1),
|
|
43173
|
+
files: z21.array(FileIdentitySchema).min(1),
|
|
43174
|
+
installations: z21.array(SkillInstallationSchema).min(1),
|
|
43175
|
+
contentHash: z21.string().regex(/^[a-f0-9]{64}$/),
|
|
43176
|
+
disabled: z21.boolean(),
|
|
43177
|
+
conflict: z21.boolean(),
|
|
43178
|
+
invalidFrontmatter: z21.boolean()
|
|
43179
|
+
}).strict();
|
|
43180
|
+
var IndexEnvelopeSchema = z21.object({
|
|
43181
|
+
schemaVersion: z21.literal(3),
|
|
43182
|
+
tokenizerVersion: z21.string(),
|
|
43183
|
+
parserVersion: z21.string(),
|
|
43184
|
+
rankingVersion: z21.string(),
|
|
43185
|
+
engine: z21.object({ name: z21.string(), version: z21.string(), configDigest: z21.string() }).strict(),
|
|
43186
|
+
/** 生效排除配置的冻结摘要(配置变更触发全量重建)。 */
|
|
43187
|
+
searchConfigDigest: z21.string().min(1),
|
|
43188
|
+
/** 载荷完整性摘要:sha256(JSON.stringify({index, stats})),写入时计算、加载时重算。 */
|
|
43189
|
+
payloadDigest: z21.string().regex(/^[a-f0-9]{64}$/),
|
|
43190
|
+
index: z21.unknown(),
|
|
43191
|
+
stats: z21.record(SkillIdSchema, StatEnvelopeSchema)
|
|
43192
|
+
}).strict();
|
|
43193
|
+
var StoredProjectionSchema = z21.object({
|
|
43194
|
+
name: z21.string(),
|
|
43195
|
+
description: z21.string(),
|
|
43196
|
+
keywords: z21.array(z21.string()),
|
|
43197
|
+
canonicalPath: z21.string().min(1),
|
|
43198
|
+
installations: z21.array(SkillInstallationSchema).min(1),
|
|
43199
|
+
contentHash: z21.string().regex(/^[a-f0-9]{64}$/),
|
|
43200
|
+
disabled: z21.boolean(),
|
|
43201
|
+
conflict: z21.boolean(),
|
|
43202
|
+
invalidFrontmatter: z21.boolean()
|
|
43203
|
+
});
|
|
43204
|
+
function createSearchMiniSearch(tokenizer) {
|
|
43205
|
+
return new MiniSearch(miniSearchOptions(tokenizer));
|
|
43206
|
+
}
|
|
43207
|
+
function searchMiniSearchInstance(miniSearch, query) {
|
|
43208
|
+
const results = miniSearch.search(query, {
|
|
43209
|
+
boost: FIELD_BOOST,
|
|
43210
|
+
prefix: SEARCH_PREFIX,
|
|
43211
|
+
fuzzy: SEARCH_FUZZY
|
|
43212
|
+
});
|
|
43213
|
+
const candidates = [];
|
|
43214
|
+
for (const result of results) {
|
|
43215
|
+
const id = SkillIdSchema.safeParse(result.id);
|
|
43216
|
+
if (!id.success) continue;
|
|
43217
|
+
const stored = miniSearch.getStoredFields(result.id);
|
|
43218
|
+
const projection = stored ? safeParseExternal(StoredProjectionSchema, stored) : null;
|
|
43219
|
+
if (!projection) continue;
|
|
43220
|
+
candidates.push({
|
|
43221
|
+
id: id.data,
|
|
43222
|
+
bm25: result.score,
|
|
43223
|
+
...projection
|
|
43224
|
+
});
|
|
43225
|
+
}
|
|
43226
|
+
return candidates;
|
|
43227
|
+
}
|
|
43228
|
+
function miniSearchOptions(tokenizer) {
|
|
43229
|
+
return {
|
|
43230
|
+
fields: [...SEARCH_FIELDS],
|
|
43231
|
+
storeFields: [...STORE_FIELDS],
|
|
43232
|
+
tokenize: (text) => tokenizer.tokenize(text),
|
|
43233
|
+
processTerm: (term) => term,
|
|
43234
|
+
stringifyField: (value) => Array.isArray(value) ? value.join(" ") : String(value),
|
|
43235
|
+
autoVacuum: false
|
|
43236
|
+
};
|
|
43237
|
+
}
|
|
43238
|
+
function createSkillSearchIndex(tokenizer = createSkillTokenizer(), getSearchConfigDigest = () => UNCONFIGURED_CONFIG_DIGEST) {
|
|
43239
|
+
return new MiniSearchSkillSearchIndex(tokenizer, getSearchConfigDigest);
|
|
43240
|
+
}
|
|
43241
|
+
var UNCONFIGURED_CONFIG_DIGEST = createHash7("sha256").digest("hex");
|
|
43242
|
+
var MiniSearchSkillSearchIndex = class {
|
|
43243
|
+
constructor(tokenizer, getSearchConfigDigest) {
|
|
43244
|
+
this.tokenizer = tokenizer;
|
|
43245
|
+
this.getSearchConfigDigest = getSearchConfigDigest;
|
|
43246
|
+
this.miniSearch = createSearchMiniSearch(tokenizer);
|
|
43247
|
+
}
|
|
43248
|
+
tokenizer;
|
|
43249
|
+
getSearchConfigDigest;
|
|
43250
|
+
miniSearch;
|
|
43251
|
+
stats = /* @__PURE__ */ new Map();
|
|
43252
|
+
loaded = false;
|
|
43253
|
+
freshen(scans, readDocument) {
|
|
43254
|
+
if (!this.loaded) {
|
|
43255
|
+
const load3 = this.loadFromDisk();
|
|
43256
|
+
this.loaded = true;
|
|
43257
|
+
if (load3.kind !== "ok") {
|
|
43258
|
+
return this.rebuild(scans, readDocument, load3.reason);
|
|
43259
|
+
}
|
|
43260
|
+
this.miniSearch = load3.miniSearch;
|
|
43261
|
+
this.stats = load3.stats;
|
|
43262
|
+
}
|
|
43263
|
+
const scanById = new Map(scans.map((scan2) => [scan2.id, scan2]));
|
|
43264
|
+
const indexedIds = new Set(this.stats.keys());
|
|
43265
|
+
const removed = [...indexedIds].filter((id) => !scanById.has(id));
|
|
43266
|
+
const changed = [];
|
|
43267
|
+
for (const scan2 of scans) {
|
|
43268
|
+
const stat2 = this.stats.get(scan2.id);
|
|
43269
|
+
if (!stat2 || !statIsCurrent(stat2, scan2)) changed.push(scan2);
|
|
43270
|
+
}
|
|
43271
|
+
const discardOperations = removed.length + changed.filter((scan2) => indexedIds.has(scan2.id)).length;
|
|
43272
|
+
const dirtyRatio = indexedIds.size === 0 ? 1 : discardOperations / indexedIds.size;
|
|
43273
|
+
if (dirtyRatio > REBUILD_DISCARD_RATIO) {
|
|
43274
|
+
return this.rebuild(scans, readDocument, "dirty-ratio");
|
|
43275
|
+
}
|
|
43276
|
+
if (removed.length === 0 && changed.length === 0) {
|
|
43277
|
+
return { mode: "fresh", documents: this.stats.size, parsed: 0, discarded: 0 };
|
|
43278
|
+
}
|
|
43279
|
+
const documents = changed.map((scan2) => ({ scan: scan2, document: readDocument(scan2) }));
|
|
43280
|
+
const snapshotIndex = this.miniSearch.toJSON();
|
|
43281
|
+
const snapshotStats = new Map(this.stats);
|
|
43282
|
+
let parsed = 0;
|
|
43283
|
+
try {
|
|
43284
|
+
for (const id of removed) {
|
|
43285
|
+
this.discardIndexed(id);
|
|
43286
|
+
this.stats.delete(id);
|
|
43287
|
+
}
|
|
43288
|
+
for (const { scan: scan2, document: document2 } of documents) {
|
|
43289
|
+
if (indexedIds.has(scan2.id)) this.discardIndexed(scan2.id);
|
|
43290
|
+
this.miniSearch.add(document2);
|
|
43291
|
+
this.stats.set(scan2.id, statEntry(scan2, document2));
|
|
43292
|
+
parsed += 1;
|
|
43293
|
+
}
|
|
43294
|
+
this.save();
|
|
43295
|
+
} catch (error) {
|
|
43296
|
+
this.restoreSnapshot(snapshotIndex, snapshotStats);
|
|
43297
|
+
if (error instanceof SkillSearchIndexError) this.invalidateAfterSaveFailure();
|
|
43298
|
+
throw error;
|
|
43299
|
+
}
|
|
43300
|
+
return {
|
|
43301
|
+
mode: "incremental",
|
|
43302
|
+
documents: this.stats.size,
|
|
43303
|
+
parsed,
|
|
43304
|
+
discarded: discardOperations
|
|
43305
|
+
};
|
|
43306
|
+
}
|
|
43307
|
+
search(query) {
|
|
43308
|
+
return searchMiniSearchInstance(this.miniSearch, query);
|
|
43309
|
+
}
|
|
43310
|
+
duplicates() {
|
|
43311
|
+
const byHash = /* @__PURE__ */ new Map();
|
|
43312
|
+
for (const [id, stat2] of this.stats) {
|
|
43313
|
+
const group = byHash.get(stat2.contentHash) ?? [];
|
|
43314
|
+
group.push({ id, stat: stat2 });
|
|
43315
|
+
byHash.set(stat2.contentHash, group);
|
|
43316
|
+
}
|
|
43317
|
+
const groups = [];
|
|
43318
|
+
for (const [contentHash, entries] of byHash) {
|
|
43319
|
+
if (entries.length < 2) continue;
|
|
43320
|
+
entries.sort(
|
|
43321
|
+
(left, right) => compareCanonicalPath(left.stat.canonicalPath, right.stat.canonicalPath)
|
|
43322
|
+
);
|
|
43323
|
+
const members = [];
|
|
43324
|
+
for (const { id, stat: stat2 } of entries) {
|
|
43325
|
+
const stored = this.miniSearch.getStoredFields(id);
|
|
43326
|
+
const name = typeof stored?.name === "string" ? stored.name : null;
|
|
43327
|
+
if (name === null) continue;
|
|
43328
|
+
members.push({
|
|
43329
|
+
id,
|
|
43330
|
+
name,
|
|
43331
|
+
canonicalPath: stat2.canonicalPath,
|
|
43332
|
+
installations: stat2.installations,
|
|
43333
|
+
disabled: stat2.disabled,
|
|
43334
|
+
conflict: stat2.conflict
|
|
43335
|
+
});
|
|
43336
|
+
}
|
|
43337
|
+
if (members.length >= 2) groups.push({ contentHash, members });
|
|
43338
|
+
}
|
|
43339
|
+
groups.sort(
|
|
43340
|
+
(left, right) => compareCanonicalPath(left.members[0].canonicalPath, right.members[0].canonicalPath)
|
|
43341
|
+
);
|
|
43342
|
+
return groups;
|
|
43343
|
+
}
|
|
43344
|
+
documentCount() {
|
|
43345
|
+
return this.miniSearch.documentCount;
|
|
43346
|
+
}
|
|
43347
|
+
rebuild(scans, readDocument, reason) {
|
|
43348
|
+
const discarded = this.stats.size;
|
|
43349
|
+
const miniSearch = createSearchMiniSearch(this.tokenizer);
|
|
43350
|
+
const stats = /* @__PURE__ */ new Map();
|
|
43351
|
+
for (const scan2 of scans) {
|
|
43352
|
+
const document2 = readDocument(scan2);
|
|
43353
|
+
miniSearch.add(document2);
|
|
43354
|
+
stats.set(scan2.id, statEntry(scan2, document2));
|
|
43355
|
+
}
|
|
43356
|
+
const envelopeIndex = miniSearch.toJSON();
|
|
43357
|
+
const envelopeStats = Object.fromEntries(stats);
|
|
43358
|
+
try {
|
|
43359
|
+
this.persist(envelopeIndex, envelopeStats);
|
|
43360
|
+
} catch (error) {
|
|
43361
|
+
this.invalidateAfterSaveFailure();
|
|
43362
|
+
throw error;
|
|
43363
|
+
}
|
|
43364
|
+
this.miniSearch = miniSearch;
|
|
43365
|
+
this.stats = stats;
|
|
43366
|
+
return {
|
|
43367
|
+
mode: "rebuilt",
|
|
43368
|
+
documents: stats.size,
|
|
43369
|
+
parsed: scans.length,
|
|
43370
|
+
discarded,
|
|
43371
|
+
rebuildReason: reason
|
|
43372
|
+
};
|
|
43373
|
+
}
|
|
43374
|
+
/** 落盘失败后清空不可持久化的内存快照,下一次调用必须重新读取磁盘真相。 */
|
|
43375
|
+
invalidateAfterSaveFailure() {
|
|
43376
|
+
this.loaded = false;
|
|
43377
|
+
this.miniSearch = createSearchMiniSearch(this.tokenizer);
|
|
43378
|
+
this.stats = /* @__PURE__ */ new Map();
|
|
43379
|
+
}
|
|
43380
|
+
/** 增量变更阶段失败后恢复本轮开始前的完整成员状态(索引原子一致性)。 */
|
|
43381
|
+
restoreSnapshot(snapshotIndex, snapshotStats) {
|
|
43382
|
+
try {
|
|
43383
|
+
this.miniSearch = MiniSearch.loadJSON(
|
|
43384
|
+
JSON.stringify(snapshotIndex),
|
|
43385
|
+
miniSearchOptions(this.tokenizer)
|
|
43386
|
+
);
|
|
43387
|
+
this.stats = snapshotStats;
|
|
43388
|
+
} catch {
|
|
43389
|
+
this.invalidateAfterSaveFailure();
|
|
43390
|
+
}
|
|
43391
|
+
}
|
|
43392
|
+
discardIndexed(id) {
|
|
43393
|
+
if (this.miniSearch.has(id)) this.miniSearch.discard(id);
|
|
43394
|
+
}
|
|
43395
|
+
loadFromDisk() {
|
|
43396
|
+
const file = path15.join(appDir(), SEARCH_INDEX_FILE);
|
|
43397
|
+
let source;
|
|
43398
|
+
try {
|
|
43399
|
+
source = fs12.readFileSync(file, "utf8");
|
|
43400
|
+
} catch (error) {
|
|
43401
|
+
if (error instanceof Error && error.code === "ENOENT") {
|
|
43402
|
+
return { kind: "empty", reason: "missing" };
|
|
43403
|
+
}
|
|
43404
|
+
throw new SkillSearchIndexError(
|
|
43405
|
+
`Cannot read the skill search index ${file}: ${error instanceof Error ? error.message : String(error)}`,
|
|
43406
|
+
{ cause: error }
|
|
43407
|
+
);
|
|
43408
|
+
}
|
|
43409
|
+
const envelope = safeParseJson(source, IndexEnvelopeSchema);
|
|
43410
|
+
if (!envelope) return { kind: "empty", reason: "corrupt" };
|
|
43411
|
+
if (payloadDigest(envelope.index, envelope.stats) !== envelope.payloadDigest) {
|
|
43412
|
+
return { kind: "empty", reason: "corrupt" };
|
|
43413
|
+
}
|
|
43414
|
+
if (envelope.tokenizerVersion !== TOKENIZER_VERSION || envelope.parserVersion !== PARSER_VERSION || envelope.rankingVersion !== RANKING_VERSION || envelope.engine.name !== ENGINE_NAME || envelope.engine.version !== minisearchRuntimeVersion() || envelope.engine.configDigest !== ENGINE_CONFIG_DIGEST || envelope.searchConfigDigest !== this.getSearchConfigDigest()) {
|
|
43415
|
+
return { kind: "empty", reason: "incompatible" };
|
|
43416
|
+
}
|
|
43417
|
+
try {
|
|
43418
|
+
const serialized = safeParseExternal(
|
|
43419
|
+
z21.object({
|
|
43420
|
+
documentIds: z21.record(z21.string(), z21.string()),
|
|
43421
|
+
storedFields: z21.record(z21.string(), z21.unknown())
|
|
43422
|
+
}).passthrough(),
|
|
43423
|
+
envelope.index
|
|
43424
|
+
);
|
|
43425
|
+
if (!serialized) return { kind: "empty", reason: "corrupt" };
|
|
43426
|
+
const shortIds = Object.keys(serialized.documentIds);
|
|
43427
|
+
const activeIdValues = Object.values(serialized.documentIds);
|
|
43428
|
+
const activeIds = new Set(activeIdValues);
|
|
43429
|
+
const statIds = new Set(Object.keys(envelope.stats));
|
|
43430
|
+
const storedFieldIds = Object.keys(serialized.storedFields);
|
|
43431
|
+
if (activeIds.size !== activeIdValues.length || activeIds.size !== statIds.size || activeIdValues.some((id) => !statIds.has(id)) || storedFieldIds.length !== shortIds.length || shortIds.some((shortId) => !Object.hasOwn(serialized.storedFields, shortId))) {
|
|
43432
|
+
return { kind: "empty", reason: "corrupt" };
|
|
43433
|
+
}
|
|
43434
|
+
const miniSearch = MiniSearch.loadJSON(
|
|
43435
|
+
JSON.stringify(envelope.index),
|
|
43436
|
+
miniSearchOptions(this.tokenizer)
|
|
43437
|
+
);
|
|
43438
|
+
for (const [id, stat2] of Object.entries(envelope.stats)) {
|
|
43439
|
+
if (!miniSearch.has(id)) return { kind: "empty", reason: "corrupt" };
|
|
43440
|
+
const stored = miniSearch.getStoredFields(id);
|
|
43441
|
+
const projection = stored ? safeParseExternal(StoredProjectionSchema, stored) : null;
|
|
43442
|
+
if (!projection || projection.canonicalPath !== stat2.canonicalPath || projection.contentHash !== stat2.contentHash || projection.disabled !== stat2.disabled || projection.conflict !== stat2.conflict || projection.invalidFrontmatter !== stat2.invalidFrontmatter || JSON.stringify(projection.installations) !== JSON.stringify(stat2.installations)) {
|
|
43443
|
+
return { kind: "empty", reason: "corrupt" };
|
|
43444
|
+
}
|
|
43445
|
+
}
|
|
43446
|
+
return { kind: "ok", miniSearch, stats: new Map(Object.entries(envelope.stats)) };
|
|
43447
|
+
} catch {
|
|
43448
|
+
return { kind: "empty", reason: "corrupt" };
|
|
43449
|
+
}
|
|
43450
|
+
}
|
|
43451
|
+
save() {
|
|
43452
|
+
this.persist(this.miniSearch.toJSON(), Object.fromEntries(this.stats));
|
|
43453
|
+
}
|
|
43454
|
+
/** 组装五版本信封并原子落盘;载荷摘要随写计算。 */
|
|
43455
|
+
persist(index, stats) {
|
|
43456
|
+
const file = path15.join(appDir(), SEARCH_INDEX_FILE);
|
|
43457
|
+
const envelope = {
|
|
43458
|
+
schemaVersion: SCHEMA_VERSION,
|
|
43459
|
+
tokenizerVersion: TOKENIZER_VERSION,
|
|
43460
|
+
parserVersion: PARSER_VERSION,
|
|
43461
|
+
rankingVersion: RANKING_VERSION,
|
|
43462
|
+
engine: {
|
|
43463
|
+
name: ENGINE_NAME,
|
|
43464
|
+
version: minisearchRuntimeVersion(),
|
|
43465
|
+
configDigest: ENGINE_CONFIG_DIGEST
|
|
43466
|
+
},
|
|
43467
|
+
searchConfigDigest: this.getSearchConfigDigest(),
|
|
43468
|
+
payloadDigest: payloadDigest(index, stats),
|
|
43469
|
+
index,
|
|
43470
|
+
stats
|
|
43471
|
+
};
|
|
43472
|
+
try {
|
|
43473
|
+
atomicWriteUtf8(file, JSON.stringify(envelope));
|
|
43474
|
+
} catch (error) {
|
|
43475
|
+
throw new SkillSearchIndexError(
|
|
43476
|
+
`Cannot persist the skill search index ${file}: ${error instanceof Error ? error.message : String(error)}`,
|
|
43477
|
+
{ cause: error }
|
|
43478
|
+
);
|
|
43479
|
+
}
|
|
43480
|
+
}
|
|
43481
|
+
};
|
|
43482
|
+
function compareCanonicalPath(left, right) {
|
|
43483
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
43484
|
+
}
|
|
43485
|
+
function payloadDigest(index, stats) {
|
|
43486
|
+
return createHash7("sha256").update(JSON.stringify({ index, stats })).digest("hex");
|
|
43487
|
+
}
|
|
43488
|
+
function statIsCurrent(stat2, scan2) {
|
|
43489
|
+
if (stat2.canonicalPath !== scan2.canonicalPath || stat2.disabled !== scan2.disabled || stat2.conflict !== scan2.conflict || stat2.files.length !== scan2.files.length) {
|
|
43490
|
+
return false;
|
|
43491
|
+
}
|
|
43492
|
+
for (let index = 0; index < stat2.files.length; index += 1) {
|
|
43493
|
+
const indexed = stat2.files[index];
|
|
43494
|
+
const candidate = scan2.files[index];
|
|
43495
|
+
if (indexed.path !== candidate.path || indexed.mtimeMs !== candidate.mtimeMs || indexed.size !== candidate.size || indexed.ino !== candidate.ino || indexed.ctimeMs !== candidate.ctimeMs) {
|
|
43496
|
+
return false;
|
|
43497
|
+
}
|
|
43498
|
+
}
|
|
43499
|
+
return stat2.installations.length === scan2.installations.length && stat2.installations.every((installation, index) => {
|
|
43500
|
+
const candidate = scan2.installations[index];
|
|
43501
|
+
return installation.path === candidate.path && installation.workspaceId === candidate.workspaceId && installation.providerId === candidate.providerId;
|
|
43502
|
+
});
|
|
43503
|
+
}
|
|
43504
|
+
function statEntry(scan2, document2) {
|
|
43505
|
+
return {
|
|
43506
|
+
canonicalPath: scan2.canonicalPath,
|
|
43507
|
+
files: scan2.files,
|
|
43508
|
+
installations: document2.installations,
|
|
43509
|
+
contentHash: document2.contentHash,
|
|
43510
|
+
disabled: document2.disabled,
|
|
43511
|
+
conflict: document2.conflict,
|
|
43512
|
+
invalidFrontmatter: document2.invalidFrontmatter
|
|
43513
|
+
};
|
|
43514
|
+
}
|
|
43515
|
+
var cachedRuntimeVersion = null;
|
|
43516
|
+
function minisearchRuntimeVersion() {
|
|
43517
|
+
if (cachedRuntimeVersion) return cachedRuntimeVersion;
|
|
43518
|
+
try {
|
|
43519
|
+
const require2 = createRequire4(import.meta.url);
|
|
43520
|
+
const entry = require2.resolve("minisearch");
|
|
43521
|
+
let directory = path15.dirname(entry);
|
|
43522
|
+
for (let hop = 0; hop < 4; hop += 1) {
|
|
43523
|
+
const packageFile = path15.join(directory, "package.json");
|
|
43524
|
+
if (fs12.existsSync(packageFile)) {
|
|
43525
|
+
const version = safeParseJson(
|
|
43526
|
+
fs12.readFileSync(packageFile, "utf8"),
|
|
43527
|
+
z21.object({ version: z21.string() })
|
|
43528
|
+
)?.version;
|
|
43529
|
+
cachedRuntimeVersion = version ?? "unresolved";
|
|
43530
|
+
return cachedRuntimeVersion;
|
|
43531
|
+
}
|
|
43532
|
+
directory = path15.dirname(directory);
|
|
43533
|
+
}
|
|
43534
|
+
cachedRuntimeVersion = "unresolved";
|
|
43535
|
+
} catch {
|
|
43536
|
+
cachedRuntimeVersion = "unresolved";
|
|
43537
|
+
}
|
|
43538
|
+
return cachedRuntimeVersion;
|
|
43539
|
+
}
|
|
43540
|
+
|
|
43541
|
+
// src/daemon/skill-search/config.ts
|
|
43542
|
+
var SEARCH_CONFIG_FILE = "search-config.toml";
|
|
43543
|
+
var TEMPLATE_EXCLUDE_DIRS = [
|
|
43544
|
+
".cargo",
|
|
43545
|
+
".cache",
|
|
43546
|
+
".npm",
|
|
43547
|
+
".pnpm-store",
|
|
43548
|
+
".bun",
|
|
43549
|
+
".rustup",
|
|
43550
|
+
".local"
|
|
43551
|
+
];
|
|
43552
|
+
var SearchConfigSchema = z22.object({
|
|
43553
|
+
excludeDirs: z22.array(z22.string().min(1)).default([])
|
|
43554
|
+
}).strict();
|
|
43555
|
+
function searchConfigPath() {
|
|
43556
|
+
return path16.join(appDir(), SEARCH_CONFIG_FILE);
|
|
43557
|
+
}
|
|
43558
|
+
function searchConfigTemplate() {
|
|
43559
|
+
return `# Skill search content configuration (server-owned).
|
|
43560
|
+
# \u6280\u80FD\u6B63\u6587\u7D22\u5F15\u4F1A\u6536\u96C6\u6280\u80FD\u76EE\u5F55\u5185\u7684\u5176\u4F59 *.md\uFF1B\u4E0B\u5217\u76EE\u5F55\u540D\u4E0D\u4F1A\u8FDB\u5165\u6536\u96C6
|
|
43561
|
+
# \uFF08\u5728\u5185\u7F6E\u6E05\u5355\u4E4B\u4E0A\u8FFD\u52A0\uFF0C\u53EA\u80FD\u589E\u52A0\u6392\u9664\uFF1B\u4EE5 "." \u5F00\u5934\u7684\u76EE\u5F55\u59CB\u7EC8\u5168\u8DF3\uFF0C
|
|
43562
|
+
# \u5185\u7F6E\u6E05\u5355\u4E0D\u53EF\u79FB\u9664\uFF09\u3002\u4FEE\u6539\u540E\u4E0B\u4E00\u6B21\u7D22\u5F15\u7EF4\u62A4\u4F1A\u81EA\u52A8\u5168\u91CF\u91CD\u5EFA\u3002
|
|
43563
|
+
excludeDirs = [
|
|
43564
|
+
${TEMPLATE_EXCLUDE_DIRS.map((dir) => `"${dir}"`).join(",\n ")},
|
|
43565
|
+
]
|
|
43566
|
+
`;
|
|
43567
|
+
}
|
|
43568
|
+
function loadSkillSearchConfig() {
|
|
43569
|
+
const file = searchConfigPath();
|
|
43570
|
+
let source;
|
|
43571
|
+
try {
|
|
43572
|
+
source = fs13.readFileSync(file, "utf8");
|
|
43573
|
+
} catch (error) {
|
|
43574
|
+
const code = error instanceof Error ? error.code : void 0;
|
|
43575
|
+
if (code === "ENOENT") {
|
|
43576
|
+
writeTemplate(file);
|
|
43577
|
+
return effectiveConfig(TEMPLATE_EXCLUDE_DIRS);
|
|
43578
|
+
}
|
|
43579
|
+
throw new SkillSearchIndexError(
|
|
43580
|
+
`Cannot read the skill search config ${file}: ${error instanceof Error ? error.message : String(error)}`,
|
|
43581
|
+
{ cause: error }
|
|
43582
|
+
);
|
|
43583
|
+
}
|
|
43584
|
+
let raw;
|
|
43585
|
+
try {
|
|
43586
|
+
raw = parseToml(source);
|
|
43587
|
+
} catch {
|
|
43588
|
+
return effectiveConfig([]);
|
|
43589
|
+
}
|
|
43590
|
+
const parsed = safeParseExternal(SearchConfigSchema, raw);
|
|
43591
|
+
return effectiveConfig(parsed ? parsed.excludeDirs : []);
|
|
43592
|
+
}
|
|
43593
|
+
function effectiveConfig(extraDirs) {
|
|
43594
|
+
const merged = [.../* @__PURE__ */ new Set([...BUILTIN_EXCLUDED_DIRS, ...extraDirs])].sort();
|
|
43595
|
+
return { excludedDirs: merged, configDigest: digestOf(merged) };
|
|
43596
|
+
}
|
|
43597
|
+
function digestOf(dirs) {
|
|
43598
|
+
return createHash8("sha256").update(dirs.join("\n")).digest("hex");
|
|
43599
|
+
}
|
|
43600
|
+
function writeTemplate(file) {
|
|
43601
|
+
try {
|
|
43602
|
+
atomicWriteUtf8(file, searchConfigTemplate());
|
|
43603
|
+
} catch (error) {
|
|
43604
|
+
throw new SkillSearchIndexError(
|
|
43605
|
+
`Cannot write the skill search config template ${file}: ${error instanceof Error ? error.message : String(error)}`,
|
|
43606
|
+
{ cause: error }
|
|
43607
|
+
);
|
|
43608
|
+
}
|
|
43609
|
+
}
|
|
43610
|
+
|
|
43611
|
+
// src/daemon/skill-search/scanner.ts
|
|
43612
|
+
import fs14 from "node:fs";
|
|
43613
|
+
import path17 from "node:path";
|
|
43614
|
+
var MAX_DEPTH2 = 2;
|
|
43615
|
+
var SKILL_FILE_NAMES = ["SKILL.md", ".SKILL.md"];
|
|
43616
|
+
function scanSkillRoots(roots) {
|
|
43617
|
+
const entries = [];
|
|
43618
|
+
for (const root of roots) {
|
|
43619
|
+
scanDirectory(root, root.rootPath, 1, entries);
|
|
43620
|
+
}
|
|
43621
|
+
entries.sort(
|
|
43622
|
+
(left, right) => left.path === right.path ? compareString3(left.workspaceId, right.workspaceId) || compareString3(left.providerId, right.providerId) : compareString3(left.path, right.path)
|
|
43623
|
+
);
|
|
43624
|
+
return entries;
|
|
43625
|
+
}
|
|
43626
|
+
function compareString3(left, right) {
|
|
43627
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
43628
|
+
}
|
|
43629
|
+
function scanDirectory(root, directory, depth, entries) {
|
|
43630
|
+
let dirents;
|
|
43631
|
+
try {
|
|
43632
|
+
dirents = fs14.readdirSync(directory, { withFileTypes: true });
|
|
43633
|
+
} catch {
|
|
43634
|
+
return;
|
|
43635
|
+
}
|
|
43636
|
+
dirents.sort((left, right) => compareString3(left.name, right.name));
|
|
43637
|
+
for (const dirent of dirents) {
|
|
43638
|
+
if (dirent.name.startsWith(".") || dirent.name === "node_modules") continue;
|
|
43639
|
+
if (!dirent.isDirectory() && !dirent.isSymbolicLink()) continue;
|
|
43640
|
+
if (depth > 1 && !dirent.isDirectory()) continue;
|
|
43641
|
+
const entryPath = path17.join(directory, dirent.name);
|
|
43642
|
+
let stat2;
|
|
43643
|
+
try {
|
|
43644
|
+
stat2 = fs14.statSync(entryPath);
|
|
43645
|
+
} catch {
|
|
43646
|
+
continue;
|
|
43647
|
+
}
|
|
43648
|
+
if (!stat2.isDirectory()) continue;
|
|
43649
|
+
if (hasSkillFile(entryPath)) {
|
|
43650
|
+
entries.push({ path: entryPath, workspaceId: root.workspaceId, providerId: root.providerId });
|
|
43651
|
+
}
|
|
43652
|
+
if (dirent.isDirectory() && depth < MAX_DEPTH2) {
|
|
43653
|
+
scanDirectory(root, entryPath, depth + 1, entries);
|
|
43654
|
+
}
|
|
43655
|
+
}
|
|
43656
|
+
}
|
|
43657
|
+
function hasSkillFile(entryPath) {
|
|
43658
|
+
return SKILL_FILE_NAMES.some((name) => isRegularFile3(path17.join(entryPath, name)));
|
|
43659
|
+
}
|
|
43660
|
+
function isRegularFile3(file) {
|
|
43661
|
+
try {
|
|
43662
|
+
return fs14.lstatSync(file).isFile();
|
|
43663
|
+
} catch {
|
|
43664
|
+
return false;
|
|
43665
|
+
}
|
|
43666
|
+
}
|
|
43667
|
+
|
|
43668
|
+
// src/daemon/skill-search/watcher.ts
|
|
43669
|
+
import fs15 from "node:fs";
|
|
43670
|
+
var FLUSH_DEBOUNCE_MS = 300;
|
|
43671
|
+
var SYNCHRONOUS_FLUSH_DOCUMENT_LIMIT = 2e4;
|
|
43672
|
+
var fsWatchFactory = (directory) => {
|
|
43673
|
+
const watcher = fs15.watch(directory, { recursive: true, persistent: false });
|
|
43674
|
+
return {
|
|
43675
|
+
close: () => watcher.close(),
|
|
43676
|
+
onEvent: (callback) => watcher.on("change", callback),
|
|
43677
|
+
onError: (callback) => watcher.on("error", callback)
|
|
43678
|
+
};
|
|
43679
|
+
};
|
|
43680
|
+
function createSkillSearchWatcher(options2) {
|
|
43681
|
+
const watchFactory = options2.watch ?? fsWatchFactory;
|
|
43682
|
+
const watched = /* @__PURE__ */ new Map();
|
|
43683
|
+
let dirty = false;
|
|
43684
|
+
let disposed = false;
|
|
43685
|
+
let flushTimer = null;
|
|
43686
|
+
function markDirty() {
|
|
43687
|
+
if (disposed) return;
|
|
43688
|
+
dirty = true;
|
|
43689
|
+
if (flushTimer !== null) return;
|
|
43690
|
+
flushTimer = setTimeout(() => {
|
|
43691
|
+
flushTimer = null;
|
|
43692
|
+
if (disposed || !dirty) return;
|
|
43693
|
+
if (options2.documentCount() > SYNCHRONOUS_FLUSH_DOCUMENT_LIMIT) return;
|
|
43694
|
+
try {
|
|
43695
|
+
options2.onFlush();
|
|
43696
|
+
dirty = false;
|
|
43697
|
+
} catch {
|
|
43698
|
+
}
|
|
43699
|
+
}, FLUSH_DEBOUNCE_MS);
|
|
43700
|
+
flushTimer.unref?.();
|
|
43701
|
+
}
|
|
43702
|
+
return {
|
|
43703
|
+
reconcile(watchDirs) {
|
|
43704
|
+
if (disposed) return;
|
|
43705
|
+
const desired = new Set(watchDirs);
|
|
43706
|
+
for (const [dir, entry] of watched) {
|
|
43707
|
+
if (!desired.has(dir)) {
|
|
43708
|
+
watched.delete(dir);
|
|
43709
|
+
closeQuietly(entry);
|
|
43710
|
+
}
|
|
43711
|
+
}
|
|
43712
|
+
for (const dir of desired) {
|
|
43713
|
+
if (watched.has(dir)) continue;
|
|
43714
|
+
try {
|
|
43715
|
+
const handle = watchFactory(dir);
|
|
43716
|
+
handle.onEvent(() => markDirty());
|
|
43717
|
+
handle.onError(() => {
|
|
43718
|
+
const entry = watched.get(dir);
|
|
43719
|
+
if (entry) {
|
|
43720
|
+
watched.delete(dir);
|
|
43721
|
+
closeQuietly(entry);
|
|
43722
|
+
}
|
|
43723
|
+
markDirty();
|
|
43724
|
+
});
|
|
43725
|
+
watched.set(dir, { handle, closed: false });
|
|
43726
|
+
} catch {
|
|
43727
|
+
}
|
|
43728
|
+
}
|
|
43729
|
+
},
|
|
43730
|
+
isClean(watchDirs) {
|
|
43731
|
+
if (dirty) return false;
|
|
43732
|
+
return watchDirs.every((dir) => watched.has(dir));
|
|
43733
|
+
},
|
|
43734
|
+
dispose() {
|
|
43735
|
+
disposed = true;
|
|
43736
|
+
if (flushTimer !== null) {
|
|
43737
|
+
clearTimeout(flushTimer);
|
|
43738
|
+
flushTimer = null;
|
|
43739
|
+
}
|
|
43740
|
+
for (const entry of watched.values()) closeQuietly(entry);
|
|
43741
|
+
watched.clear();
|
|
43742
|
+
dirty = false;
|
|
43743
|
+
},
|
|
43744
|
+
watchedCount() {
|
|
43745
|
+
return watched.size;
|
|
43746
|
+
}
|
|
43747
|
+
};
|
|
43748
|
+
}
|
|
43749
|
+
function closeQuietly(entry) {
|
|
43750
|
+
if (entry.closed) return;
|
|
43751
|
+
entry.closed = true;
|
|
43752
|
+
try {
|
|
43753
|
+
entry.handle.close();
|
|
43754
|
+
} catch {
|
|
43755
|
+
}
|
|
43756
|
+
}
|
|
43757
|
+
|
|
43758
|
+
// src/daemon/skill-search/service.ts
|
|
43759
|
+
var SkillSearchDocumentReadError = class extends DomainError {
|
|
43760
|
+
constructor(message, options2) {
|
|
43761
|
+
super("UNAVAILABLE", message, options2);
|
|
43762
|
+
this.name = "SkillSearchDocumentReadError";
|
|
43763
|
+
}
|
|
43764
|
+
};
|
|
43765
|
+
function createSkillSearchService() {
|
|
43766
|
+
return createSkillSearchEngine(defaultResolveRoots(createWorkspaceRegistryPersistence()));
|
|
43767
|
+
}
|
|
43768
|
+
function createSkillSearchEngine(resolveRoots) {
|
|
43769
|
+
const tokenizer = createSkillTokenizer();
|
|
43770
|
+
let config = null;
|
|
43771
|
+
const index = createSkillSearchIndex(tokenizer, () => config?.configDigest ?? "");
|
|
43772
|
+
try {
|
|
43773
|
+
config = loadSkillSearchConfig();
|
|
43774
|
+
} catch (error) {
|
|
43775
|
+
console.warn(
|
|
43776
|
+
`[skill-search] config priming failed: ${error instanceof Error ? error.message : String(error)}`
|
|
43777
|
+
);
|
|
43778
|
+
}
|
|
43779
|
+
let cachedWatchDirs = [];
|
|
43780
|
+
let cachedRootsKey = null;
|
|
43781
|
+
function maintain() {
|
|
43782
|
+
const activeConfig = loadSkillSearchConfig();
|
|
43783
|
+
config = activeConfig;
|
|
43784
|
+
const roots = resolveRoots();
|
|
43785
|
+
const candidates = scanSkillRoots(roots);
|
|
43786
|
+
const excluded = new Set(activeConfig.excludedDirs);
|
|
43787
|
+
const scans = canonicalizeCandidates(candidates, excluded);
|
|
43788
|
+
index.freshen(scans, readSkillSearchDocument);
|
|
43789
|
+
cachedWatchDirs = canonicalWatchDirs(roots);
|
|
43790
|
+
cachedRootsKey = rootsKeyOf(roots);
|
|
43791
|
+
watcher.reconcile(cachedWatchDirs);
|
|
43792
|
+
}
|
|
43793
|
+
const watcher = createSkillSearchWatcher({
|
|
43794
|
+
onFlush: () => maintain(),
|
|
43795
|
+
documentCount: () => index.documentCount()
|
|
43796
|
+
});
|
|
43797
|
+
function ensureMaintained() {
|
|
43798
|
+
const roots = resolveRoots();
|
|
43799
|
+
const rootsChanged = cachedRootsKey !== rootsKeyOf(roots);
|
|
43800
|
+
const configChanged = config === null || loadSkillSearchConfig().configDigest !== config.configDigest;
|
|
43801
|
+
if (rootsChanged || configChanged || !watcher.isClean(cachedWatchDirs)) {
|
|
43802
|
+
maintain();
|
|
43803
|
+
}
|
|
43804
|
+
}
|
|
43805
|
+
return {
|
|
43806
|
+
/** 检索本地 skills;query 为空字符串时返回空结果(CLI 层负责把空 query 判为用法错误)。 */
|
|
43807
|
+
search: async (query, searchOptions) => {
|
|
43808
|
+
const parsedOptions = SkillSearchOptionsSchema.parse(searchOptions ?? {});
|
|
43809
|
+
if (query.trim() === "") return [];
|
|
43810
|
+
ensureMaintained();
|
|
43811
|
+
const hits = index.search(query);
|
|
43812
|
+
return rankResults(hits, query, parsedOptions.limit, (text) => tokenizer.tokenize(text));
|
|
43813
|
+
},
|
|
43814
|
+
/**
|
|
43815
|
+
* 内容重复组投影(与 search 共用维护门:clean + 配置未变时零扫描直读内存)。
|
|
43816
|
+
*/
|
|
43817
|
+
duplicates: async () => {
|
|
43818
|
+
ensureMaintained();
|
|
43819
|
+
return index.duplicates().map((group) => ({
|
|
43820
|
+
contentHash: group.contentHash,
|
|
43821
|
+
members: group.members.map((member) => ({
|
|
43822
|
+
...member,
|
|
43823
|
+
id: SkillIdSchema.parse(member.id)
|
|
43824
|
+
}))
|
|
43825
|
+
}));
|
|
43826
|
+
},
|
|
43827
|
+
/** watcher 回收(daemon stop coordinator 接线;幂等)。 */
|
|
43828
|
+
dispose: () => {
|
|
43829
|
+
watcher.dispose();
|
|
43830
|
+
}
|
|
43831
|
+
};
|
|
43832
|
+
}
|
|
43833
|
+
function canonicalWatchDirs(roots) {
|
|
43834
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
43835
|
+
for (const root of roots) {
|
|
43836
|
+
try {
|
|
43837
|
+
dirs.add(fs16.realpathSync(root.rootPath));
|
|
43838
|
+
} catch {
|
|
43839
|
+
}
|
|
43840
|
+
}
|
|
43841
|
+
return [...dirs].sort();
|
|
43842
|
+
}
|
|
43843
|
+
function rootsKeyOf(roots) {
|
|
43844
|
+
return roots.map((root) => `${root.workspaceId}:${root.providerId}:${root.rootPath}`).sort().join("|");
|
|
43845
|
+
}
|
|
43846
|
+
function readSkillSearchDocument(scan2) {
|
|
43847
|
+
const sourceIdentity = scan2.files.find((file) => file.path === scan2.sourcePath);
|
|
43848
|
+
if (!sourceIdentity) {
|
|
43849
|
+
throw new SkillSearchDocumentReadError(
|
|
43850
|
+
`The scanned skill identity source is missing from the file set: ${scan2.sourcePath}`
|
|
43851
|
+
);
|
|
43852
|
+
}
|
|
43853
|
+
const extras = [];
|
|
43854
|
+
for (const file of scan2.files) {
|
|
43855
|
+
if (file.path === scan2.sourcePath) continue;
|
|
43856
|
+
extras.push({ path: file.path, raw: readFileWithIdentity(file.path, file) });
|
|
43857
|
+
}
|
|
43858
|
+
const sourceRaw = readFileWithIdentity(scan2.sourcePath, sourceIdentity);
|
|
43859
|
+
const parsed = parseSkillDocumentSet(sourceRaw, extras, path18.basename(scan2.canonicalPath));
|
|
43860
|
+
return {
|
|
43861
|
+
id: scan2.id,
|
|
43862
|
+
name: parsed.name,
|
|
43863
|
+
description: parsed.description,
|
|
43864
|
+
keywords: parsed.keywords,
|
|
43865
|
+
triggers: parsed.triggers,
|
|
43866
|
+
headings: parsed.headings,
|
|
43867
|
+
body: parsed.body,
|
|
43868
|
+
canonicalPath: scan2.canonicalPath,
|
|
43869
|
+
installations: scan2.installations,
|
|
43870
|
+
contentHash: parsed.contentHash,
|
|
43871
|
+
disabled: scan2.disabled,
|
|
43872
|
+
conflict: scan2.conflict,
|
|
43873
|
+
invalidFrontmatter: parsed.invalidFrontmatter
|
|
43874
|
+
};
|
|
43875
|
+
}
|
|
43876
|
+
function readFileWithIdentity(filePath, identity2) {
|
|
43877
|
+
let fd = null;
|
|
43878
|
+
try {
|
|
43879
|
+
try {
|
|
43880
|
+
const noFollow = process.platform === "win32" ? 0 : fs16.constants.O_NOFOLLOW;
|
|
43881
|
+
fd = fs16.openSync(filePath, fs16.constants.O_RDONLY | noFollow);
|
|
43882
|
+
} catch (error) {
|
|
43883
|
+
throw new SkillSearchDocumentReadError(
|
|
43884
|
+
`Cannot open the scanned skill file ${filePath} without following a symlink.`,
|
|
43885
|
+
{ cause: error }
|
|
43886
|
+
);
|
|
43887
|
+
}
|
|
43888
|
+
const descriptorStat = fs16.fstatSync(fd);
|
|
43889
|
+
if (!descriptorStat.isFile() || descriptorStat.ino !== identity2.ino || descriptorStat.size !== identity2.size) {
|
|
43890
|
+
throw new SkillSearchDocumentReadError(
|
|
43891
|
+
`The scanned skill file identity changed before read: ${filePath}`
|
|
43892
|
+
);
|
|
43893
|
+
}
|
|
43894
|
+
return fs16.readFileSync(fd);
|
|
43895
|
+
} catch (error) {
|
|
43896
|
+
if (error instanceof SkillSearchDocumentReadError) throw error;
|
|
43897
|
+
throw new SkillSearchDocumentReadError(`Cannot read the scanned skill file ${filePath}.`, {
|
|
43898
|
+
cause: error
|
|
43899
|
+
});
|
|
43900
|
+
} finally {
|
|
43901
|
+
if (fd !== null) fs16.closeSync(fd);
|
|
43902
|
+
}
|
|
43903
|
+
}
|
|
43904
|
+
function defaultResolveRoots(stateSource) {
|
|
43905
|
+
return () => {
|
|
43906
|
+
const roots = [];
|
|
43907
|
+
for (const provider of PROVIDER_CATALOG) {
|
|
43908
|
+
const rootPath = globalProviderRoot(provider);
|
|
43909
|
+
if (rootPath === null) continue;
|
|
43910
|
+
roots.push({
|
|
43911
|
+
rootPath,
|
|
43912
|
+
workspaceId: GLOBAL_WORKSPACE_ID,
|
|
43913
|
+
providerId: ProviderIdSchema.parse(provider.id)
|
|
43914
|
+
});
|
|
43915
|
+
}
|
|
43916
|
+
const state = stateSource.load();
|
|
43917
|
+
for (const workspace of state.workspaces) {
|
|
43918
|
+
for (const provider of PROVIDER_CATALOG) {
|
|
43919
|
+
roots.push({
|
|
43920
|
+
rootPath: importedProviderRoot(workspace.path, provider),
|
|
43921
|
+
workspaceId: workspace.id,
|
|
43922
|
+
providerId: ProviderIdSchema.parse(provider.id)
|
|
43923
|
+
});
|
|
43924
|
+
}
|
|
43925
|
+
}
|
|
43926
|
+
return roots;
|
|
43927
|
+
};
|
|
43928
|
+
}
|
|
43929
|
+
|
|
43930
|
+
// src/daemon/search-config-opener.ts
|
|
43931
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
43932
|
+
function platformOpenFile(filePath) {
|
|
43933
|
+
const platform2 = process.platform;
|
|
43934
|
+
if (platform2 === "win32") {
|
|
43935
|
+
return spawnDetached({ file: "cmd", args: ["/c", "start", "", filePath] });
|
|
43936
|
+
}
|
|
43937
|
+
const command = platform2 === "darwin" ? { file: "open", args: [filePath] } : { file: "xdg-open", args: [filePath] };
|
|
43938
|
+
return spawnDetached(command, { checkExitCode: true });
|
|
43939
|
+
}
|
|
43940
|
+
function spawnDetached(command, options2 = {}) {
|
|
43941
|
+
const label = `${command.file} ${command.args.join(" ")}`;
|
|
43942
|
+
return new Promise((resolve2, reject) => {
|
|
43943
|
+
let child;
|
|
43944
|
+
try {
|
|
43945
|
+
child = spawn5(command.file, command.args, { detached: true, stdio: "ignore" });
|
|
43946
|
+
} catch (error) {
|
|
43947
|
+
reject(openerError(label, error));
|
|
43948
|
+
return;
|
|
43949
|
+
}
|
|
43950
|
+
child.on("error", (error) => reject(openerError(label, error)));
|
|
43951
|
+
child.unref();
|
|
43952
|
+
if (!options2.checkExitCode) {
|
|
43953
|
+
resolve2();
|
|
43954
|
+
return;
|
|
43955
|
+
}
|
|
43956
|
+
child.on("exit", (code) => {
|
|
43957
|
+
if (code !== null && code !== 0) {
|
|
43958
|
+
reject(
|
|
43959
|
+
new DomainError(
|
|
43960
|
+
"UNAVAILABLE",
|
|
43961
|
+
`Cannot open the search config with ${label}: exited with code ${code} (no default application?)`
|
|
43962
|
+
)
|
|
43963
|
+
);
|
|
43964
|
+
} else {
|
|
43965
|
+
resolve2();
|
|
43966
|
+
}
|
|
43967
|
+
});
|
|
43968
|
+
});
|
|
43969
|
+
}
|
|
43970
|
+
function openerError(label, cause) {
|
|
43971
|
+
return new DomainError(
|
|
43972
|
+
"UNAVAILABLE",
|
|
43973
|
+
`Cannot open the search config with ${label}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
43974
|
+
{ cause }
|
|
43975
|
+
);
|
|
43976
|
+
}
|
|
43977
|
+
|
|
43978
|
+
// src/daemon/dialog-service.ts
|
|
43979
|
+
async function attachExtDialog(tray) {
|
|
43980
|
+
const ext = await import("@opentray/ext-dialog");
|
|
43981
|
+
return ext.attachDialog(tray);
|
|
43982
|
+
}
|
|
43983
|
+
function createDialogService(options2 = {}) {
|
|
43984
|
+
const attachDialog = options2.attachDialog ?? attachExtDialog;
|
|
43985
|
+
let picker = null;
|
|
43986
|
+
return {
|
|
43987
|
+
attach(tray) {
|
|
43988
|
+
if (picker !== null) return;
|
|
43989
|
+
try {
|
|
43990
|
+
void Promise.resolve(attachDialog(tray)).then((attached) => {
|
|
43991
|
+
picker = attached;
|
|
43992
|
+
}).catch(() => void 0);
|
|
43993
|
+
} catch {
|
|
43994
|
+
}
|
|
43995
|
+
},
|
|
43996
|
+
async pickDirectory() {
|
|
43997
|
+
if (picker === null) return { supported: false, path: null };
|
|
43998
|
+
try {
|
|
43999
|
+
return { supported: true, path: await picker.pickDirectory() };
|
|
44000
|
+
} catch (error) {
|
|
44001
|
+
const code = error.code;
|
|
44002
|
+
if (code === "dialog_platform_unsupported") {
|
|
44003
|
+
return { supported: false, path: null };
|
|
44004
|
+
}
|
|
44005
|
+
throw new DomainError(
|
|
44006
|
+
"UNAVAILABLE",
|
|
44007
|
+
"The native directory picker could not be presented.",
|
|
44008
|
+
{ cause: error }
|
|
44009
|
+
);
|
|
44010
|
+
}
|
|
44011
|
+
}
|
|
44012
|
+
};
|
|
44013
|
+
}
|
|
44014
|
+
|
|
42359
44015
|
// src/daemon/skill-intelligence-service.ts
|
|
42360
44016
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
42361
44017
|
|
|
@@ -42621,7 +44277,7 @@ function analyzeDocuments(documents) {
|
|
|
42621
44277
|
}
|
|
42622
44278
|
}
|
|
42623
44279
|
const sharedPaths = left.referencedPaths.filter(
|
|
42624
|
-
(
|
|
44280
|
+
(path39) => right.referencedPaths.includes(path39)
|
|
42625
44281
|
);
|
|
42626
44282
|
if (sharedPaths.length > 0) {
|
|
42627
44283
|
const id = findingId("shared-resource-path", [left.skillId, right.skillId]);
|
|
@@ -42635,9 +44291,9 @@ function analyzeDocuments(documents) {
|
|
|
42635
44291
|
[left.skillId]: left.revision,
|
|
42636
44292
|
[right.skillId]: right.revision
|
|
42637
44293
|
},
|
|
42638
|
-
evidence: sharedPaths.slice(0, 3).map((
|
|
44294
|
+
evidence: sharedPaths.slice(0, 3).map((path39) => ({
|
|
42639
44295
|
label: "shared path",
|
|
42640
|
-
snippet:
|
|
44296
|
+
snippet: path39
|
|
42641
44297
|
}))
|
|
42642
44298
|
});
|
|
42643
44299
|
addEdge("shared-resource", [left.skillId, right.skillId], id);
|
|
@@ -43015,30 +44671,30 @@ function createSkillIntelligenceService(skills, creator) {
|
|
|
43015
44671
|
}
|
|
43016
44672
|
|
|
43017
44673
|
// src/daemon/skill-service.ts
|
|
43018
|
-
import
|
|
43019
|
-
import
|
|
43020
|
-
import { z as
|
|
44674
|
+
import fs17 from "node:fs";
|
|
44675
|
+
import path19 from "node:path";
|
|
44676
|
+
import { z as z23 } from "zod";
|
|
43021
44677
|
function metadataId(skillPath) {
|
|
43022
44678
|
return SkillIdSchema.parse(opaquePathId("sk", canonicalDirectory(skillPath)));
|
|
43023
44679
|
}
|
|
43024
|
-
var CcskiSkillMetadataSchema =
|
|
43025
|
-
name:
|
|
43026
|
-
description:
|
|
43027
|
-
disabled:
|
|
43028
|
-
provider:
|
|
44680
|
+
var CcskiSkillMetadataSchema = z23.object({
|
|
44681
|
+
name: z23.string(),
|
|
44682
|
+
description: z23.string(),
|
|
44683
|
+
disabled: z23.boolean().optional(),
|
|
44684
|
+
provider: z23.string(),
|
|
43029
44685
|
location: SkillLocationSchema,
|
|
43030
|
-
sourcePriority:
|
|
43031
|
-
sourceKind:
|
|
43032
|
-
path:
|
|
43033
|
-
hasReferences:
|
|
43034
|
-
hasScripts:
|
|
43035
|
-
hasAssets:
|
|
44686
|
+
sourcePriority: z23.number().optional(),
|
|
44687
|
+
sourceKind: z23.string().optional(),
|
|
44688
|
+
path: z23.string().min(1),
|
|
44689
|
+
hasReferences: z23.boolean(),
|
|
44690
|
+
hasScripts: z23.boolean(),
|
|
44691
|
+
hasAssets: z23.boolean(),
|
|
43036
44692
|
pluginInfo: PluginInfoSchema.optional()
|
|
43037
44693
|
});
|
|
43038
|
-
var CcskiValidateResultSchema =
|
|
43039
|
-
success:
|
|
43040
|
-
errors:
|
|
43041
|
-
warnings:
|
|
44694
|
+
var CcskiValidateResultSchema = z23.object({
|
|
44695
|
+
success: z23.boolean(),
|
|
44696
|
+
errors: z23.array(z23.string()),
|
|
44697
|
+
warnings: z23.array(z23.string())
|
|
43042
44698
|
});
|
|
43043
44699
|
function projectProvenance(skillPath, probeMap) {
|
|
43044
44700
|
if (!probeMap) return { installedVia: "unknown", updatable: false };
|
|
@@ -43055,7 +44711,7 @@ function projectMetadata(source, probeMap) {
|
|
|
43055
44711
|
id: metadataId(canonicalPath),
|
|
43056
44712
|
name: skill.name,
|
|
43057
44713
|
description: skill.description,
|
|
43058
|
-
directoryName:
|
|
44714
|
+
directoryName: path19.basename(canonicalPath),
|
|
43059
44715
|
disabled: skill.disabled ?? false,
|
|
43060
44716
|
provider: skill.provider,
|
|
43061
44717
|
location: skill.location,
|
|
@@ -43077,19 +44733,41 @@ function createSkillService(workspaces, options2 = {}) {
|
|
|
43077
44733
|
const discoverSkills2 = options2.discoverSkills ?? listSkills;
|
|
43078
44734
|
const validateSkill2 = options2.validateSkill ?? validateSkill;
|
|
43079
44735
|
const skillsCliProbe = options2.skillsCliProbe;
|
|
44736
|
+
const discoveryInflight = /* @__PURE__ */ new Map();
|
|
44737
|
+
const discoveryKey = (target, includeDisabled) => `${target.workspaceId}:${target.providerId}:${includeDisabled}`;
|
|
44738
|
+
const cachedList = (target, includeDisabled = true) => {
|
|
44739
|
+
const key = discoveryKey(target, includeDisabled);
|
|
44740
|
+
const existing = discoveryInflight.get(key);
|
|
44741
|
+
if (existing) return existing;
|
|
44742
|
+
const promise = list(workspaces, discoverSkills2, target, includeDisabled, skillsCliProbe);
|
|
44743
|
+
discoveryInflight.set(key, promise);
|
|
44744
|
+
const forget = () => discoveryInflight.delete(key);
|
|
44745
|
+
promise.then(forget, forget);
|
|
44746
|
+
return promise;
|
|
44747
|
+
};
|
|
44748
|
+
const invalidateTarget = (target) => {
|
|
44749
|
+
discoveryInflight.delete(discoveryKey(target, true));
|
|
44750
|
+
discoveryInflight.delete(discoveryKey(target, false));
|
|
44751
|
+
};
|
|
43080
44752
|
return {
|
|
43081
|
-
list: (target, includeDisabled = true) =>
|
|
43082
|
-
resolve: (target, skillId) => resolveSkill2(
|
|
44753
|
+
list: (target, includeDisabled = true) => cachedList(target, includeDisabled),
|
|
44754
|
+
resolve: (target, skillId) => resolveSkill2(cachedList, target, skillId),
|
|
43083
44755
|
skillFile,
|
|
43084
|
-
info: (target, skillId) => info2(
|
|
43085
|
-
toggle: (target, skillIds, mode) => toggle(
|
|
43086
|
-
validate: (target, skillId) => validate2(
|
|
44756
|
+
info: (target, skillId) => info2(cachedList, target, skillId),
|
|
44757
|
+
toggle: (target, skillIds, mode) => toggle(cachedList, target, skillIds, mode).finally(() => invalidateTarget(target)),
|
|
44758
|
+
validate: (target, skillId) => validate2(cachedList, validateSkill2, workspaces, target, skillId),
|
|
44759
|
+
/**
|
|
44760
|
+
* 丢弃该 target 的 discovery 缓存(写事务用):apply/rollback 等绕过
|
|
44761
|
+
* 本 service 直接写盘后,验证读必须看到新磁盘态(perf B-6 的 TTL 缓存
|
|
44762
|
+
* 不知道外部写入)。
|
|
44763
|
+
*/
|
|
44764
|
+
invalidateDiscovery: invalidateTarget
|
|
43087
44765
|
};
|
|
43088
44766
|
}
|
|
43089
44767
|
async function list(workspaces, discoverSkills2, target, includeDisabled = true, skillsCliProbe) {
|
|
43090
44768
|
const scope = workspaces.resolve(target, includeDisabled);
|
|
43091
44769
|
const skills = await discoverSkills2(scope.options);
|
|
43092
|
-
const probeMap = skillsCliProbe
|
|
44770
|
+
const probeMap = skillsCliProbe?.peek() ?? null;
|
|
43093
44771
|
const byId = /* @__PURE__ */ new Map();
|
|
43094
44772
|
for (const skill of skills) {
|
|
43095
44773
|
const projected = projectMetadata(skill, probeMap);
|
|
@@ -43099,27 +44777,25 @@ async function list(workspaces, discoverSkills2, target, includeDisabled = true,
|
|
|
43099
44777
|
}
|
|
43100
44778
|
return [...byId.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
43101
44779
|
}
|
|
43102
|
-
async function resolveSkill2(
|
|
43103
|
-
const skill = (await
|
|
43104
|
-
(candidate) => candidate.id === skillId
|
|
43105
|
-
);
|
|
44780
|
+
async function resolveSkill2(loadList, target, skillId) {
|
|
44781
|
+
const skill = (await loadList(target, true)).find((candidate) => candidate.id === skillId);
|
|
43106
44782
|
if (!skill)
|
|
43107
44783
|
throw new DomainError("NOT_FOUND", `Skill not found in Workspace Provider: ${skillId}`);
|
|
43108
44784
|
return skill;
|
|
43109
44785
|
}
|
|
43110
44786
|
function skillFile(skill) {
|
|
43111
44787
|
const filename = skill.disabled ? ".SKILL.md" : "SKILL.md";
|
|
43112
|
-
const file =
|
|
44788
|
+
const file = path19.join(skill.path, filename);
|
|
43113
44789
|
assertPathInside(skill.path, file);
|
|
43114
|
-
if (!
|
|
44790
|
+
if (!fs17.existsSync(file) || !fs17.statSync(file).isFile()) {
|
|
43115
44791
|
throw new DomainError("UNAVAILABLE", `Skill document is unavailable: ${skill.name}`);
|
|
43116
44792
|
}
|
|
43117
44793
|
return file;
|
|
43118
44794
|
}
|
|
43119
|
-
async function info2(
|
|
43120
|
-
const skill = await resolveSkill2(
|
|
44795
|
+
async function info2(loadList, target, skillId) {
|
|
44796
|
+
const skill = await resolveSkill2(loadList, target, skillId);
|
|
43121
44797
|
const file = skillFile(skill);
|
|
43122
|
-
const content =
|
|
44798
|
+
const content = fs17.readFileSync(file, "utf8");
|
|
43123
44799
|
return {
|
|
43124
44800
|
...skill,
|
|
43125
44801
|
size: Buffer.byteLength(content),
|
|
@@ -43127,13 +44803,8 @@ async function info2(workspaces, discoverSkills2, target, skillId, skillsCliProb
|
|
|
43127
44803
|
revision: contentRevision(content)
|
|
43128
44804
|
};
|
|
43129
44805
|
}
|
|
43130
|
-
async function toggle(
|
|
43131
|
-
const discovered = new Map(
|
|
43132
|
-
(await list(workspaces, discoverSkills2, target, true, skillsCliProbe)).map((skill) => [
|
|
43133
|
-
skill.id,
|
|
43134
|
-
skill
|
|
43135
|
-
])
|
|
43136
|
-
);
|
|
44806
|
+
async function toggle(loadList, target, skillIds, mode) {
|
|
44807
|
+
const discovered = new Map((await loadList(target, true)).map((skill) => [skill.id, skill]));
|
|
43137
44808
|
const results = [];
|
|
43138
44809
|
for (const skillId of skillIds) {
|
|
43139
44810
|
const skill = discovered.get(skillId);
|
|
@@ -43146,21 +44817,21 @@ async function toggle(workspaces, discoverSkills2, target, skillIds, mode, skill
|
|
|
43146
44817
|
results.push({ skillId, name: skill.name, status: "skipped" });
|
|
43147
44818
|
continue;
|
|
43148
44819
|
}
|
|
43149
|
-
const source =
|
|
43150
|
-
const destination =
|
|
44820
|
+
const source = path19.join(skill.path, wantsDisabled ? "SKILL.md" : ".SKILL.md");
|
|
44821
|
+
const destination = path19.join(skill.path, wantsDisabled ? ".SKILL.md" : "SKILL.md");
|
|
43151
44822
|
try {
|
|
43152
44823
|
assertPathInside(skill.path, source);
|
|
43153
44824
|
assertPathInside(skill.path, destination);
|
|
43154
|
-
if (
|
|
44825
|
+
if (fs17.existsSync(destination)) {
|
|
43155
44826
|
results.push({
|
|
43156
44827
|
skillId,
|
|
43157
44828
|
name: skill.name,
|
|
43158
44829
|
status: "conflict",
|
|
43159
|
-
error: `Both ${
|
|
44830
|
+
error: `Both ${path19.basename(source)} and ${path19.basename(destination)} exist.`
|
|
43160
44831
|
});
|
|
43161
44832
|
continue;
|
|
43162
44833
|
}
|
|
43163
|
-
|
|
44834
|
+
fs17.renameSync(source, destination);
|
|
43164
44835
|
results.push({ skillId, name: skill.name, status: wantsDisabled ? "disabled" : "enabled" });
|
|
43165
44836
|
} catch {
|
|
43166
44837
|
results.push({
|
|
@@ -43182,10 +44853,8 @@ async function toggle(workspaces, discoverSkills2, target, skillIds, mode, skill
|
|
|
43182
44853
|
failed: results.filter((result) => result.status === "failed").length
|
|
43183
44854
|
};
|
|
43184
44855
|
}
|
|
43185
|
-
async function validate2(
|
|
43186
|
-
const skill = (await
|
|
43187
|
-
(candidate) => candidate.id === skillId
|
|
43188
|
-
);
|
|
44856
|
+
async function validate2(loadList, validateSkill2, workspaces, target, skillId) {
|
|
44857
|
+
const skill = (await loadList(target, true)).find((candidate) => candidate.id === skillId);
|
|
43189
44858
|
if (!skill)
|
|
43190
44859
|
throw new DomainError("NOT_FOUND", `Skill not found in Workspace Provider: ${skillId}`);
|
|
43191
44860
|
const result = safeParseExternal(
|
|
@@ -43214,9 +44883,9 @@ async function validate2(workspaces, discoverSkills2, validateSkill2, target, sk
|
|
|
43214
44883
|
}
|
|
43215
44884
|
|
|
43216
44885
|
// src/daemon/steward-service.ts
|
|
43217
|
-
import { promises as
|
|
44886
|
+
import { promises as fs18 } from "node:fs";
|
|
43218
44887
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
43219
|
-
import
|
|
44888
|
+
import path20 from "node:path";
|
|
43220
44889
|
|
|
43221
44890
|
// src/daemon/steward/harness-adapter.ts
|
|
43222
44891
|
var HarnessProcessLostError = class extends Error {
|
|
@@ -43261,7 +44930,7 @@ function createStewardService(workspaces, skills, intelligence, options2) {
|
|
|
43261
44930
|
);
|
|
43262
44931
|
const runs = /* @__PURE__ */ new Map();
|
|
43263
44932
|
let disposed = false;
|
|
43264
|
-
const executionParent = options2.executionRootParent ??
|
|
44933
|
+
const executionParent = options2.executionRootParent ?? path20.join(runDir(), "steward");
|
|
43265
44934
|
const appendEvent = (entry, event) => {
|
|
43266
44935
|
const seq = entry.events.length > 0 ? entry.events[entry.events.length - 1].seq + 1 : 1;
|
|
43267
44936
|
entry.events.push({ ...event, seq, at: (/* @__PURE__ */ new Date()).toISOString(), runId: entry.run.runId });
|
|
@@ -43290,7 +44959,7 @@ function createStewardService(workspaces, skills, intelligence, options2) {
|
|
|
43290
44959
|
};
|
|
43291
44960
|
const cleanupExecutionRoot = async (root) => {
|
|
43292
44961
|
try {
|
|
43293
|
-
await
|
|
44962
|
+
await fs18.rm(root, { recursive: true, force: true });
|
|
43294
44963
|
} catch {
|
|
43295
44964
|
}
|
|
43296
44965
|
};
|
|
@@ -43362,8 +45031,8 @@ function createStewardService(workspaces, skills, intelligence, options2) {
|
|
|
43362
45031
|
if (selected.length === 0) {
|
|
43363
45032
|
throw new DomainError("INVALID_OPERATION", "No skills selected for the steward run.");
|
|
43364
45033
|
}
|
|
43365
|
-
await
|
|
43366
|
-
const executionRoot = await
|
|
45034
|
+
await fs18.mkdir(executionParent, { recursive: true });
|
|
45035
|
+
const executionRoot = await fs18.mkdtemp(path20.join(executionParent, "run-"));
|
|
43367
45036
|
const runId = brandRunId(`sr_${randomBytes6(12).toString("hex")}`);
|
|
43368
45037
|
const run = {
|
|
43369
45038
|
runId,
|
|
@@ -43417,9 +45086,9 @@ function createStewardService(workspaces, skills, intelligence, options2) {
|
|
|
43417
45086
|
}
|
|
43418
45087
|
for (const skill of selected) {
|
|
43419
45088
|
const info3 = await skillsService.info(run.target, skill.id);
|
|
43420
|
-
const destination =
|
|
43421
|
-
await
|
|
43422
|
-
await
|
|
45089
|
+
const destination = path20.join(entry.run.executionRoot, "skills", skill.directoryName);
|
|
45090
|
+
await fs18.mkdir(destination, { recursive: true });
|
|
45091
|
+
await fs18.writeFile(path20.join(destination, "SKILL.md"), info3.content, "utf8");
|
|
43423
45092
|
}
|
|
43424
45093
|
const findingSnapshots = analyzed.report.findings.map(
|
|
43425
45094
|
(finding) => ({
|
|
@@ -43505,7 +45174,7 @@ function createStewardService(workspaces, skills, intelligence, options2) {
|
|
|
43505
45174
|
directoryName: skill.directoryName,
|
|
43506
45175
|
revision,
|
|
43507
45176
|
disabled: skill.disabled,
|
|
43508
|
-
documentPath:
|
|
45177
|
+
documentPath: path20.join("skills", skill.directoryName, "SKILL.md")
|
|
43509
45178
|
};
|
|
43510
45179
|
}),
|
|
43511
45180
|
findings: findingSnapshots,
|
|
@@ -43734,23 +45403,23 @@ var STEWARD_PROMPT_VERSION = "1.0.0";
|
|
|
43734
45403
|
var STEWARD_TOOL_VERSION = "1.0.0";
|
|
43735
45404
|
|
|
43736
45405
|
// src/daemon/steward/context-snapshot.ts
|
|
43737
|
-
import { createHash as
|
|
43738
|
-
import { promises as
|
|
43739
|
-
import
|
|
45406
|
+
import { createHash as createHash9, randomBytes as randomBytes7 } from "node:crypto";
|
|
45407
|
+
import { promises as fs19 } from "node:fs";
|
|
45408
|
+
import path21 from "node:path";
|
|
43740
45409
|
function stewardStoreDir() {
|
|
43741
|
-
return
|
|
45410
|
+
return path21.join(homeDir(), "steward-store");
|
|
43742
45411
|
}
|
|
43743
45412
|
function snapshotsDir() {
|
|
43744
|
-
return
|
|
45413
|
+
return path21.join(stewardStoreDir(), "snapshots");
|
|
43745
45414
|
}
|
|
43746
45415
|
async function walkRegularFiles(directory) {
|
|
43747
45416
|
const found = [];
|
|
43748
|
-
const entries = await
|
|
45417
|
+
const entries = await fs19.readdir(directory, { withFileTypes: true });
|
|
43749
45418
|
for (const entry of entries) {
|
|
43750
45419
|
if (entry.name === ".SKILL.md" || entry.name.startsWith(".tmp-")) continue;
|
|
43751
|
-
const absolute =
|
|
45420
|
+
const absolute = path21.join(directory, entry.name);
|
|
43752
45421
|
if (entry.isDirectory()) {
|
|
43753
|
-
found.push(...(await walkRegularFiles(absolute)).map((rel) =>
|
|
45422
|
+
found.push(...(await walkRegularFiles(absolute)).map((rel) => path21.join(entry.name, rel)));
|
|
43754
45423
|
continue;
|
|
43755
45424
|
}
|
|
43756
45425
|
if (entry.isFile()) {
|
|
@@ -43761,8 +45430,8 @@ async function walkRegularFiles(directory) {
|
|
|
43761
45430
|
return found;
|
|
43762
45431
|
}
|
|
43763
45432
|
async function sha256File(absolute) {
|
|
43764
|
-
const bytes = await
|
|
43765
|
-
return { hash:
|
|
45433
|
+
const bytes = await fs19.readFile(absolute);
|
|
45434
|
+
return { hash: createHash9("sha256").update(bytes).digest("hex"), byteSize: bytes.byteLength };
|
|
43766
45435
|
}
|
|
43767
45436
|
async function buildContextSnapshot(skills, input) {
|
|
43768
45437
|
const scopeKind = input.target.workspaceId === GLOBAL_WORKSPACE_ID ? "global" : "imported";
|
|
@@ -43825,7 +45494,7 @@ async function buildContextSnapshot(skills, input) {
|
|
|
43825
45494
|
try {
|
|
43826
45495
|
const relFiles = await walkRegularFiles(skill.path);
|
|
43827
45496
|
for (const rel of relFiles) {
|
|
43828
|
-
const { hash, byteSize: resourceBytes } = await sha256File(
|
|
45497
|
+
const { hash, byteSize: resourceBytes } = await sha256File(path21.join(skill.path, rel));
|
|
43829
45498
|
resources.push({
|
|
43830
45499
|
skillId: skill.id,
|
|
43831
45500
|
relPath: rel,
|
|
@@ -43867,8 +45536,8 @@ async function buildContextSnapshot(skills, input) {
|
|
|
43867
45536
|
}
|
|
43868
45537
|
const snapshot = parsedSnapshot.data;
|
|
43869
45538
|
try {
|
|
43870
|
-
await
|
|
43871
|
-
const file =
|
|
45539
|
+
await fs19.mkdir(snapshotsDir(), { recursive: true });
|
|
45540
|
+
const file = path21.join(snapshotsDir(), `${snapshotId}.json`);
|
|
43872
45541
|
atomicWriteUtf8(file, JSON.stringify(snapshot, null, 2));
|
|
43873
45542
|
} catch (error) {
|
|
43874
45543
|
throw new DomainError(
|
|
@@ -43880,15 +45549,15 @@ async function buildContextSnapshot(skills, input) {
|
|
|
43880
45549
|
}
|
|
43881
45550
|
|
|
43882
45551
|
// src/daemon/steward/audit-store.ts
|
|
43883
|
-
import { promises as
|
|
43884
|
-
import
|
|
45552
|
+
import { promises as fs20 } from "node:fs";
|
|
45553
|
+
import path22 from "node:path";
|
|
43885
45554
|
function storeFile(name) {
|
|
43886
|
-
return
|
|
45555
|
+
return path22.join(stewardStoreDir(), `${name}.jsonl`);
|
|
43887
45556
|
}
|
|
43888
45557
|
async function appendLine(file, value) {
|
|
43889
45558
|
try {
|
|
43890
|
-
await
|
|
43891
|
-
await
|
|
45559
|
+
await fs20.mkdir(path22.dirname(file), { recursive: true });
|
|
45560
|
+
await fs20.appendFile(file, `${JSON.stringify(value)}
|
|
43892
45561
|
`, "utf8");
|
|
43893
45562
|
} catch (error) {
|
|
43894
45563
|
throw new DomainError(
|
|
@@ -43900,7 +45569,7 @@ async function appendLine(file, value) {
|
|
|
43900
45569
|
async function readLines(file, parse2) {
|
|
43901
45570
|
let raw;
|
|
43902
45571
|
try {
|
|
43903
|
-
raw = await
|
|
45572
|
+
raw = await fs20.readFile(file, "utf8");
|
|
43904
45573
|
} catch (error) {
|
|
43905
45574
|
if (error.code === "ENOENT") return [];
|
|
43906
45575
|
throw new DomainError(
|
|
@@ -43959,21 +45628,21 @@ function createStewardAuditStore() {
|
|
|
43959
45628
|
}
|
|
43960
45629
|
|
|
43961
45630
|
// src/daemon/steward/approval-service.ts
|
|
43962
|
-
import { createHash as
|
|
43963
|
-
import
|
|
45631
|
+
import { createHash as createHash11, randomBytes as randomBytes10 } from "node:crypto";
|
|
45632
|
+
import path26 from "node:path";
|
|
43964
45633
|
|
|
43965
45634
|
// src/daemon/steward/apply-transaction.ts
|
|
43966
45635
|
import { randomBytes as randomBytes9 } from "node:crypto";
|
|
43967
|
-
import { promises as
|
|
43968
|
-
import
|
|
45636
|
+
import { promises as fs24 } from "node:fs";
|
|
45637
|
+
import path25 from "node:path";
|
|
43969
45638
|
|
|
43970
45639
|
// src/daemon/steward/journal-schema.ts
|
|
43971
|
-
import { promises as
|
|
43972
|
-
import
|
|
43973
|
-
import { z as
|
|
45640
|
+
import { promises as fs22 } from "node:fs";
|
|
45641
|
+
import path23 from "node:path";
|
|
45642
|
+
import { z as z24 } from "zod";
|
|
43974
45643
|
|
|
43975
45644
|
// src/daemon/steward/dir-identity.ts
|
|
43976
|
-
import { promises as
|
|
45645
|
+
import { promises as fs21 } from "node:fs";
|
|
43977
45646
|
function darwinSystemAlias(dir) {
|
|
43978
45647
|
if (process.platform !== "darwin") return null;
|
|
43979
45648
|
for (const root of ["/var", "/tmp", "/etc"]) {
|
|
@@ -43982,14 +45651,14 @@ function darwinSystemAlias(dir) {
|
|
|
43982
45651
|
return null;
|
|
43983
45652
|
}
|
|
43984
45653
|
async function assertCanonicalDirectory(dir) {
|
|
43985
|
-
const stat2 = await
|
|
45654
|
+
const stat2 = await fs21.lstat(dir).catch(() => null);
|
|
43986
45655
|
if (stat2 === null || !stat2.isDirectory() || stat2.isSymbolicLink()) {
|
|
43987
45656
|
throw new DomainError(
|
|
43988
45657
|
"INVALID_OPERATION",
|
|
43989
45658
|
`manager root is not a real directory (symlinked or missing): ${dir}`
|
|
43990
45659
|
);
|
|
43991
45660
|
}
|
|
43992
|
-
const real = await
|
|
45661
|
+
const real = await fs21.realpath(dir);
|
|
43993
45662
|
const darwinAlias = darwinSystemAlias(dir);
|
|
43994
45663
|
if (real !== dir && real !== darwinAlias) {
|
|
43995
45664
|
throw new DomainError(
|
|
@@ -44000,7 +45669,7 @@ async function assertCanonicalDirectory(dir) {
|
|
|
44000
45669
|
return { dev: stat2.dev, ino: stat2.ino };
|
|
44001
45670
|
}
|
|
44002
45671
|
async function verifyDirIdentity(dir, identity2) {
|
|
44003
|
-
const stat2 = await
|
|
45672
|
+
const stat2 = await fs21.lstat(dir).catch(() => null);
|
|
44004
45673
|
if (stat2 === null || !stat2.isDirectory() || stat2.dev !== identity2.dev || stat2.ino !== identity2.ino) {
|
|
44005
45674
|
throw new DomainError(
|
|
44006
45675
|
"UNAVAILABLE",
|
|
@@ -44029,20 +45698,20 @@ async function anchorManagerDirectory(dir) {
|
|
|
44029
45698
|
}
|
|
44030
45699
|
|
|
44031
45700
|
// src/daemon/steward/journal-schema.ts
|
|
44032
|
-
var BackupRefSchema =
|
|
45701
|
+
var BackupRefSchema = z24.string().regex(/^\d+-[0-9a-f]{12}\.bin$/, {
|
|
44033
45702
|
message: "Manager backup reference must match `<seq>-<sha12>.bin`."
|
|
44034
45703
|
});
|
|
44035
|
-
var Sha256HexSchema =
|
|
44036
|
-
var BackupManifestLineSchema =
|
|
45704
|
+
var Sha256HexSchema = z24.string().regex(/^[a-f0-9]{64}$/);
|
|
45705
|
+
var BackupManifestLineSchema = z24.strictObject({
|
|
44037
45706
|
ref: BackupRefSchema,
|
|
44038
|
-
seq:
|
|
45707
|
+
seq: z24.number().int().positive(),
|
|
44039
45708
|
from: RelPathSchema,
|
|
44040
45709
|
sha256: Sha256HexSchema,
|
|
44041
|
-
byteSize:
|
|
45710
|
+
byteSize: z24.number().int().nonnegative().max(64 * 1024 * 1024, "backup byteSize exceeds the manager ceiling")
|
|
44042
45711
|
});
|
|
44043
|
-
var ResourceDetailSchema =
|
|
44044
|
-
kind:
|
|
44045
|
-
strategy:
|
|
45712
|
+
var ResourceDetailSchema = z24.strictObject({
|
|
45713
|
+
kind: z24.literal("resource"),
|
|
45714
|
+
strategy: z24.enum(["copy", "move", "reference"]),
|
|
44046
45715
|
from: RelPathSchema,
|
|
44047
45716
|
to: RelPathSchema,
|
|
44048
45717
|
/** Codex R10 P1-2/P1-7:apply 时记录的落盘字节 sha256(回滚删除前逐项校验)。 */
|
|
@@ -44066,81 +45735,81 @@ var ResourceDetailSchema = z19.strictObject({
|
|
|
44066
45735
|
});
|
|
44067
45736
|
}
|
|
44068
45737
|
});
|
|
44069
|
-
var JournalEntrySchema =
|
|
44070
|
-
|
|
44071
|
-
seq:
|
|
44072
|
-
step:
|
|
44073
|
-
detail:
|
|
44074
|
-
kind:
|
|
44075
|
-
targets:
|
|
45738
|
+
var JournalEntrySchema = z24.discriminatedUnion("step", [
|
|
45739
|
+
z24.strictObject({
|
|
45740
|
+
seq: z24.number().int().positive(),
|
|
45741
|
+
step: z24.literal("precheck"),
|
|
45742
|
+
detail: z24.strictObject({
|
|
45743
|
+
kind: z24.literal("precheck"),
|
|
45744
|
+
targets: z24.array(SkillDirectoryNameSchema).min(1)
|
|
44076
45745
|
})
|
|
44077
45746
|
}),
|
|
44078
|
-
|
|
44079
|
-
seq:
|
|
44080
|
-
step:
|
|
44081
|
-
detail:
|
|
44082
|
-
kind:
|
|
45747
|
+
z24.strictObject({
|
|
45748
|
+
seq: z24.number().int().positive(),
|
|
45749
|
+
step: z24.literal("edit"),
|
|
45750
|
+
detail: z24.strictObject({
|
|
45751
|
+
kind: z24.literal("content"),
|
|
44083
45752
|
skillId: SkillIdSchema,
|
|
44084
45753
|
beforeRevision: ContentRevisionSchema
|
|
44085
45754
|
})
|
|
44086
45755
|
}),
|
|
44087
|
-
|
|
44088
|
-
seq:
|
|
44089
|
-
step:
|
|
44090
|
-
detail:
|
|
44091
|
-
kind:
|
|
45756
|
+
z24.strictObject({
|
|
45757
|
+
seq: z24.number().int().positive(),
|
|
45758
|
+
step: z24.literal("disable"),
|
|
45759
|
+
detail: z24.strictObject({
|
|
45760
|
+
kind: z24.literal("enablement"),
|
|
44092
45761
|
skillId: SkillIdSchema,
|
|
44093
45762
|
/** Codex R10 P1-3:apply 捕获的启停前态——undo 只恢复捕获状态。 */
|
|
44094
|
-
wasDisabled:
|
|
45763
|
+
wasDisabled: z24.boolean(),
|
|
44095
45764
|
revision: ContentRevisionSchema.optional()
|
|
44096
45765
|
})
|
|
44097
45766
|
}),
|
|
44098
|
-
|
|
44099
|
-
seq:
|
|
44100
|
-
step:
|
|
44101
|
-
detail:
|
|
44102
|
-
kind:
|
|
45767
|
+
z24.strictObject({
|
|
45768
|
+
seq: z24.number().int().positive(),
|
|
45769
|
+
step: z24.literal("enable"),
|
|
45770
|
+
detail: z24.strictObject({
|
|
45771
|
+
kind: z24.literal("enablement"),
|
|
44103
45772
|
skillId: SkillIdSchema,
|
|
44104
45773
|
/** Codex R10 P1-3:apply 捕获的启停前态——undo 只恢复捕获状态。 */
|
|
44105
|
-
wasEnabled:
|
|
45774
|
+
wasEnabled: z24.boolean(),
|
|
44106
45775
|
revision: ContentRevisionSchema.optional()
|
|
44107
45776
|
})
|
|
44108
45777
|
}),
|
|
44109
|
-
|
|
44110
|
-
seq:
|
|
44111
|
-
step:
|
|
44112
|
-
detail:
|
|
44113
|
-
kind:
|
|
45778
|
+
z24.strictObject({
|
|
45779
|
+
seq: z24.number().int().positive(),
|
|
45780
|
+
step: z24.literal("create-target"),
|
|
45781
|
+
detail: z24.strictObject({
|
|
45782
|
+
kind: z24.literal("content"),
|
|
44114
45783
|
directoryName: SkillDirectoryNameSchema,
|
|
44115
45784
|
/** Codex R10 P1-2:创建后 SKILL.md 的 revision(sha256:…,回滚删除前校验)。 */
|
|
44116
45785
|
revision: ContentRevisionSchema
|
|
44117
45786
|
})
|
|
44118
45787
|
}),
|
|
44119
|
-
|
|
44120
|
-
seq:
|
|
44121
|
-
step:
|
|
45788
|
+
z24.strictObject({
|
|
45789
|
+
seq: z24.number().int().positive(),
|
|
45790
|
+
step: z24.literal("resource"),
|
|
44122
45791
|
detail: ResourceDetailSchema
|
|
44123
45792
|
}),
|
|
44124
|
-
|
|
44125
|
-
seq:
|
|
44126
|
-
step:
|
|
44127
|
-
detail:
|
|
44128
|
-
kind:
|
|
45793
|
+
z24.strictObject({
|
|
45794
|
+
seq: z24.number().int().positive(),
|
|
45795
|
+
step: z24.literal("commit"),
|
|
45796
|
+
detail: z24.strictObject({
|
|
45797
|
+
kind: z24.literal("commit"),
|
|
44129
45798
|
/** 操作 ID(journal 文件名派生);回放侧按与期望 operationId 全等比对。 */
|
|
44130
|
-
proposalId:
|
|
44131
|
-
status:
|
|
44132
|
-
mutationCount:
|
|
45799
|
+
proposalId: z24.string().min(1).max(200),
|
|
45800
|
+
status: z24.literal("applied"),
|
|
45801
|
+
mutationCount: z24.number().int().nonnegative()
|
|
44133
45802
|
})
|
|
44134
45803
|
})
|
|
44135
45804
|
]);
|
|
44136
45805
|
async function readJournal(journalPath) {
|
|
44137
|
-
const parentIdentity = await anchorManagerDirectory(
|
|
45806
|
+
const parentIdentity = await anchorManagerDirectory(path23.dirname(journalPath));
|
|
44138
45807
|
let raw;
|
|
44139
45808
|
let leafIdentity;
|
|
44140
45809
|
try {
|
|
44141
|
-
const handle = await
|
|
45810
|
+
const handle = await fs22.open(
|
|
44142
45811
|
journalPath,
|
|
44143
|
-
|
|
45812
|
+
fs22.constants.O_RDONLY | fs22.constants.O_NOFOLLOW
|
|
44144
45813
|
);
|
|
44145
45814
|
try {
|
|
44146
45815
|
const stat2 = await handle.stat();
|
|
@@ -44166,14 +45835,14 @@ async function readJournal(journalPath) {
|
|
|
44166
45835
|
`Journal file unreadable (${code}); recovery required: ${journalPath}`
|
|
44167
45836
|
);
|
|
44168
45837
|
}
|
|
44169
|
-
const after = await
|
|
45838
|
+
const after = await fs22.lstat(journalPath).catch(() => null);
|
|
44170
45839
|
if (after === null || !after.isFile() || after.dev !== leafIdentity.dev || after.ino !== leafIdentity.ino) {
|
|
44171
45840
|
throw new DomainError(
|
|
44172
45841
|
"UNAVAILABLE",
|
|
44173
45842
|
`Journal leaf identity drifted after read; recovery required: ${journalPath}`
|
|
44174
45843
|
);
|
|
44175
45844
|
}
|
|
44176
|
-
await verifyDirIdentity(
|
|
45845
|
+
await verifyDirIdentity(path23.dirname(journalPath), parentIdentity);
|
|
44177
45846
|
const entries = [];
|
|
44178
45847
|
const lines = raw.split("\n");
|
|
44179
45848
|
for (let index = 0; index < lines.length; index += 1) {
|
|
@@ -44329,29 +45998,29 @@ function assertCommittedJournal(entries, expected) {
|
|
|
44329
45998
|
}
|
|
44330
45999
|
|
|
44331
46000
|
// src/daemon/steward/fs-authority.ts
|
|
44332
|
-
import { createHash as
|
|
44333
|
-
import { promises as
|
|
44334
|
-
import
|
|
46001
|
+
import { createHash as createHash10, randomBytes as randomBytes8 } from "node:crypto";
|
|
46002
|
+
import { promises as fs23 } from "node:fs";
|
|
46003
|
+
import path24 from "node:path";
|
|
44335
46004
|
var assertRealRoot = assertCanonicalDirectory;
|
|
44336
|
-
var O_NOFOLLOW =
|
|
46005
|
+
var O_NOFOLLOW = fs23.constants.O_NOFOLLOW ?? 0;
|
|
44337
46006
|
function sha256Hex(bytes) {
|
|
44338
|
-
return
|
|
46007
|
+
return createHash10("sha256").update(bytes).digest("hex");
|
|
44339
46008
|
}
|
|
44340
46009
|
async function assertNoSymlinkAncestors(to, root) {
|
|
44341
|
-
const realRoot = await
|
|
44342
|
-
const parent =
|
|
44343
|
-
const realParent = await
|
|
44344
|
-
const lexicalParent =
|
|
46010
|
+
const realRoot = await fs23.realpath(root);
|
|
46011
|
+
const parent = path24.dirname(to);
|
|
46012
|
+
const realParent = await fs23.realpath(parent);
|
|
46013
|
+
const lexicalParent = path24.join(realRoot, path24.relative(root, parent));
|
|
44345
46014
|
if (realParent !== lexicalParent) {
|
|
44346
46015
|
throw new DomainError(
|
|
44347
46016
|
"INVALID_OPERATION",
|
|
44348
|
-
`Resource target path contains a symlink ancestor: ${
|
|
46017
|
+
`Resource target path contains a symlink ancestor: ${path24.relative(root, to)}`
|
|
44349
46018
|
);
|
|
44350
46019
|
}
|
|
44351
46020
|
}
|
|
44352
46021
|
async function syncDir(dir) {
|
|
44353
46022
|
try {
|
|
44354
|
-
const handle = await
|
|
46023
|
+
const handle = await fs23.open(dir, fs23.constants.O_RDONLY | fs23.constants.O_DIRECTORY);
|
|
44355
46024
|
try {
|
|
44356
46025
|
await handle.sync();
|
|
44357
46026
|
} finally {
|
|
@@ -44367,7 +46036,7 @@ async function syncDir(dir) {
|
|
|
44367
46036
|
async function captureLeafIdentity(leaf, label, expectedByteSize) {
|
|
44368
46037
|
let stat2;
|
|
44369
46038
|
try {
|
|
44370
|
-
stat2 = await
|
|
46039
|
+
stat2 = await fs23.lstat(leaf);
|
|
44371
46040
|
} catch {
|
|
44372
46041
|
throw new DomainError("NOT_FOUND", `Resource source missing on disk: ${label}`);
|
|
44373
46042
|
}
|
|
@@ -44385,8 +46054,8 @@ async function captureLeafIdentity(leaf, label, expectedByteSize) {
|
|
|
44385
46054
|
async function readResourceBytesStrict(from, root, expectedByteSize, label) {
|
|
44386
46055
|
assertPathInside(root, from);
|
|
44387
46056
|
const rootIdentity = await assertRealRoot(root);
|
|
44388
|
-
const realRoot = await
|
|
44389
|
-
const lexicalPosition =
|
|
46057
|
+
const realRoot = await fs23.realpath(root);
|
|
46058
|
+
const lexicalPosition = path24.join(realRoot, path24.relative(root, from));
|
|
44390
46059
|
const realFrom = await realpathOrNotFound(from, label);
|
|
44391
46060
|
if (realFrom !== lexicalPosition) {
|
|
44392
46061
|
throw new DomainError(
|
|
@@ -44397,7 +46066,7 @@ async function readResourceBytesStrict(from, root, expectedByteSize, label) {
|
|
|
44397
46066
|
const identity2 = await captureLeafIdentity(from, label, expectedByteSize);
|
|
44398
46067
|
let handle;
|
|
44399
46068
|
try {
|
|
44400
|
-
handle = await
|
|
46069
|
+
handle = await fs23.open(from, fs23.constants.O_RDONLY | O_NOFOLLOW);
|
|
44401
46070
|
} catch {
|
|
44402
46071
|
throw new DomainError("NOT_FOUND", `Resource source missing on disk: ${label}`);
|
|
44403
46072
|
}
|
|
@@ -44435,7 +46104,7 @@ async function readResourceBytesStrict(from, root, expectedByteSize, label) {
|
|
|
44435
46104
|
}
|
|
44436
46105
|
async function realpathOrNotFound(target, label) {
|
|
44437
46106
|
try {
|
|
44438
|
-
return await
|
|
46107
|
+
return await fs23.realpath(target);
|
|
44439
46108
|
} catch {
|
|
44440
46109
|
throw new DomainError("NOT_FOUND", `Resource source missing on disk: ${label}`);
|
|
44441
46110
|
}
|
|
@@ -44443,13 +46112,13 @@ async function realpathOrNotFound(target, label) {
|
|
|
44443
46112
|
async function writeFileExclusiveVerified(to, root, bytes, label) {
|
|
44444
46113
|
assertPathInside(root, to);
|
|
44445
46114
|
const rootIdentity = await assertRealRoot(root);
|
|
44446
|
-
const realRoot = await
|
|
44447
|
-
const lexicalPosition =
|
|
46115
|
+
const realRoot = await fs23.realpath(root);
|
|
46116
|
+
const lexicalPosition = path24.join(realRoot, path24.relative(root, to));
|
|
44448
46117
|
await assertNoSymlinkAncestors(to, root);
|
|
44449
|
-
const parentIdentity = await assertCanonicalDirectory(
|
|
46118
|
+
const parentIdentity = await assertCanonicalDirectory(path24.dirname(to));
|
|
44450
46119
|
let handle;
|
|
44451
46120
|
try {
|
|
44452
|
-
handle = await
|
|
46121
|
+
handle = await fs23.open(to, "wx");
|
|
44453
46122
|
} catch (error) {
|
|
44454
46123
|
throw new DomainError(
|
|
44455
46124
|
"INVALID_OPERATION",
|
|
@@ -44463,14 +46132,14 @@ async function writeFileExclusiveVerified(to, root, bytes, label) {
|
|
|
44463
46132
|
throw new DomainError("INVALID_OPERATION", `Resource target is not a regular file: ${label}`);
|
|
44464
46133
|
}
|
|
44465
46134
|
identity2 = { dev: stat2.dev, ino: stat2.ino };
|
|
44466
|
-
const realTo = await
|
|
46135
|
+
const realTo = await fs23.realpath(to);
|
|
44467
46136
|
if (realTo !== lexicalPosition) {
|
|
44468
46137
|
throw new DomainError(
|
|
44469
46138
|
"UNAVAILABLE",
|
|
44470
46139
|
`Resource target escaped the root before any bytes were written (path race; at most an empty placeholder remains at ${realTo}); recovery required: ${label}`
|
|
44471
46140
|
);
|
|
44472
46141
|
}
|
|
44473
|
-
const atPosition = await
|
|
46142
|
+
const atPosition = await fs23.lstat(to);
|
|
44474
46143
|
if (atPosition.dev !== identity2.dev || atPosition.ino !== identity2.ino) {
|
|
44475
46144
|
throw new DomainError(
|
|
44476
46145
|
"UNAVAILABLE",
|
|
@@ -44478,20 +46147,20 @@ async function writeFileExclusiveVerified(to, root, bytes, label) {
|
|
|
44478
46147
|
);
|
|
44479
46148
|
}
|
|
44480
46149
|
await verifyDirIdentity(root, rootIdentity);
|
|
44481
|
-
await verifyDirIdentity(
|
|
46150
|
+
await verifyDirIdentity(path24.dirname(to), parentIdentity);
|
|
44482
46151
|
await handle.writeFile(bytes);
|
|
44483
46152
|
await handle.sync();
|
|
44484
46153
|
} finally {
|
|
44485
46154
|
await handle.close().catch(() => void 0);
|
|
44486
46155
|
}
|
|
44487
|
-
const realToAfter = await
|
|
46156
|
+
const realToAfter = await fs23.realpath(to);
|
|
44488
46157
|
if (realToAfter !== lexicalPosition) {
|
|
44489
46158
|
throw new DomainError(
|
|
44490
46159
|
"UNAVAILABLE",
|
|
44491
46160
|
`Resource target escaped the provider root after write (path race); external path untouched, recovery required: ${label}`
|
|
44492
46161
|
);
|
|
44493
46162
|
}
|
|
44494
|
-
const after = await
|
|
46163
|
+
const after = await fs23.lstat(to);
|
|
44495
46164
|
if (after.dev !== identity2.dev || after.ino !== identity2.ino) {
|
|
44496
46165
|
throw new DomainError(
|
|
44497
46166
|
"UNAVAILABLE",
|
|
@@ -44506,7 +46175,7 @@ async function restoreResourceBytesStrict(to, root, bytes, label) {
|
|
|
44506
46175
|
await assertNoSymlinkAncestors(to, root);
|
|
44507
46176
|
let stat2 = null;
|
|
44508
46177
|
try {
|
|
44509
|
-
stat2 = await
|
|
46178
|
+
stat2 = await fs23.lstat(to);
|
|
44510
46179
|
} catch {
|
|
44511
46180
|
stat2 = null;
|
|
44512
46181
|
}
|
|
@@ -44514,7 +46183,7 @@ async function restoreResourceBytesStrict(to, root, bytes, label) {
|
|
|
44514
46183
|
if (!stat2.isFile()) {
|
|
44515
46184
|
throw new Error(`move restore target exists but is not a regular file: ${label}`);
|
|
44516
46185
|
}
|
|
44517
|
-
const handle = await
|
|
46186
|
+
const handle = await fs23.open(to, fs23.constants.O_RDONLY | O_NOFOLLOW).catch(() => null);
|
|
44518
46187
|
if (handle === null) {
|
|
44519
46188
|
throw new Error(`move restore target cannot be opened without following symlinks: ${label}`);
|
|
44520
46189
|
}
|
|
@@ -44530,7 +46199,7 @@ async function restoreResourceBytesStrict(to, root, bytes, label) {
|
|
|
44530
46199
|
} finally {
|
|
44531
46200
|
await handle.close().catch(() => void 0);
|
|
44532
46201
|
}
|
|
44533
|
-
const after = await
|
|
46202
|
+
const after = await fs23.lstat(to);
|
|
44534
46203
|
if (after.dev !== stat2.dev || after.ino !== stat2.ino) {
|
|
44535
46204
|
throw new Error(`move restore target identity drifted after read: ${label}`);
|
|
44536
46205
|
}
|
|
@@ -44554,7 +46223,7 @@ async function unlinkFileVerified(leaf, root, label, quarantineJournalPath) {
|
|
|
44554
46223
|
`Resource source is hardlinked (nlink=${identity2.nlink}); exclusive-inode policy refuses to move it: ${label}`
|
|
44555
46224
|
);
|
|
44556
46225
|
}
|
|
44557
|
-
const handle = await
|
|
46226
|
+
const handle = await fs23.open(leaf, fs23.constants.O_RDONLY | O_NOFOLLOW).catch(() => null);
|
|
44558
46227
|
if (handle === null) {
|
|
44559
46228
|
throw new DomainError("NOT_FOUND", `Resource source missing on disk: ${label}`);
|
|
44560
46229
|
}
|
|
@@ -44568,13 +46237,13 @@ async function unlinkFileVerified(leaf, root, label, quarantineJournalPath) {
|
|
|
44568
46237
|
}
|
|
44569
46238
|
const quarantineRoot = await prepareBackupRoot(quarantineJournalPath);
|
|
44570
46239
|
const quarantineIdentity = await assertCanonicalDirectory(quarantineRoot);
|
|
44571
|
-
const tombstone =
|
|
46240
|
+
const tombstone = path24.join(
|
|
44572
46241
|
quarantineRoot,
|
|
44573
|
-
`removed-${randomBytes8(8).toString("hex")}-${
|
|
46242
|
+
`removed-${randomBytes8(8).toString("hex")}-${path24.basename(leaf)}`
|
|
44574
46243
|
);
|
|
44575
46244
|
try {
|
|
44576
46245
|
await verifyDirIdentity(root, rootIdentity);
|
|
44577
|
-
await
|
|
46246
|
+
await fs23.rename(leaf, tombstone);
|
|
44578
46247
|
} catch (error) {
|
|
44579
46248
|
if (error.code === "EXDEV") {
|
|
44580
46249
|
throw new DomainError(
|
|
@@ -44584,7 +46253,7 @@ async function unlinkFileVerified(leaf, root, label, quarantineJournalPath) {
|
|
|
44584
46253
|
}
|
|
44585
46254
|
throw error;
|
|
44586
46255
|
}
|
|
44587
|
-
const stillExists = await
|
|
46256
|
+
const stillExists = await fs23.lstat(leaf).then(
|
|
44588
46257
|
() => true,
|
|
44589
46258
|
() => false
|
|
44590
46259
|
);
|
|
@@ -44594,7 +46263,7 @@ async function unlinkFileVerified(leaf, root, label, quarantineJournalPath) {
|
|
|
44594
46263
|
`Resource source path survived quarantine rename (path race); recovery required: ${label}`
|
|
44595
46264
|
);
|
|
44596
46265
|
}
|
|
44597
|
-
const tomb = await
|
|
46266
|
+
const tomb = await fs23.lstat(tombstone).catch(() => null);
|
|
44598
46267
|
if (tomb === null || !tomb.isFile() || tomb.dev !== identity2.dev || tomb.ino !== identity2.ino) {
|
|
44599
46268
|
throw new DomainError(
|
|
44600
46269
|
"UNAVAILABLE",
|
|
@@ -44617,14 +46286,14 @@ async function unlinkFileVerified(leaf, root, label, quarantineJournalPath) {
|
|
|
44617
46286
|
}
|
|
44618
46287
|
async function prepareBackupRoot(journalPath) {
|
|
44619
46288
|
const backupDir = `${journalPath}.backups`;
|
|
44620
|
-
await
|
|
44621
|
-
await anchorManagerDirectory(
|
|
46289
|
+
await fs23.mkdir(path24.dirname(journalPath), 448).catch(() => void 0);
|
|
46290
|
+
await anchorManagerDirectory(path24.dirname(journalPath)).catch(() => {
|
|
44622
46291
|
throw new DomainError(
|
|
44623
46292
|
"UNAVAILABLE",
|
|
44624
|
-
`journal directory is not canonical (or was replaced during this daemon lifetime); recovery required: ${
|
|
46293
|
+
`journal directory is not canonical (or was replaced during this daemon lifetime); recovery required: ${path24.dirname(journalPath)}`
|
|
44625
46294
|
);
|
|
44626
46295
|
});
|
|
44627
|
-
await
|
|
46296
|
+
await fs23.mkdir(backupDir, 448).catch(() => void 0);
|
|
44628
46297
|
await anchorManagerDirectory(backupDir).catch(() => {
|
|
44629
46298
|
throw new DomainError(
|
|
44630
46299
|
"UNAVAILABLE",
|
|
@@ -44638,24 +46307,24 @@ async function writeBackupWithManifest(options2) {
|
|
|
44638
46307
|
const parsedRef = BackupRefSchema.parse(ref);
|
|
44639
46308
|
const root = await prepareBackupRoot(options2.journalPath);
|
|
44640
46309
|
const rootIdentity = await assertCanonicalDirectory(root);
|
|
44641
|
-
await writeFileExclusiveVerified(
|
|
46310
|
+
await writeFileExclusiveVerified(path24.join(root, parsedRef), root, bytes, `backup:${parsedRef}`);
|
|
44642
46311
|
await syncDir(root);
|
|
44643
46312
|
await verifyDirIdentity(root, rootIdentity);
|
|
44644
|
-
const manifestHandle = await
|
|
44645
|
-
|
|
44646
|
-
|
|
46313
|
+
const manifestHandle = await fs23.open(
|
|
46314
|
+
path24.join(root, "manifest.jsonl"),
|
|
46315
|
+
fs23.constants.O_WRONLY | fs23.constants.O_APPEND | fs23.constants.O_CREAT | O_NOFOLLOW,
|
|
44647
46316
|
384
|
|
44648
46317
|
);
|
|
44649
46318
|
await verifyDirIdentity(root, rootIdentity);
|
|
44650
46319
|
const manifestStat = await manifestHandle.stat();
|
|
44651
46320
|
if (!manifestStat.isFile()) {
|
|
44652
46321
|
await manifestHandle.close().catch(() => void 0);
|
|
44653
|
-
throw new Error(`backup manifest is not a regular file: ${
|
|
46322
|
+
throw new Error(`backup manifest is not a regular file: ${path24.join(root, "manifest.jsonl")}`);
|
|
44654
46323
|
}
|
|
44655
46324
|
if (manifestStat.nlink !== 1) {
|
|
44656
46325
|
await manifestHandle.close().catch(() => void 0);
|
|
44657
46326
|
throw new Error(
|
|
44658
|
-
`backup manifest is hardlinked (nlink=${manifestStat.nlink}); manager refuses to append: ${
|
|
46327
|
+
`backup manifest is hardlinked (nlink=${manifestStat.nlink}); manager refuses to append: ${path24.join(root, "manifest.jsonl")}`
|
|
44659
46328
|
);
|
|
44660
46329
|
}
|
|
44661
46330
|
try {
|
|
@@ -44671,7 +46340,7 @@ async function writeBackupWithManifest(options2) {
|
|
|
44671
46340
|
await manifestHandle.sync();
|
|
44672
46341
|
if ((await manifestHandle.stat()).nlink !== 1) {
|
|
44673
46342
|
throw new Error(
|
|
44674
|
-
`backup manifest gained a hardlink during append; recovery required: ${
|
|
46343
|
+
`backup manifest gained a hardlink during append; recovery required: ${path24.join(root, "manifest.jsonl")}`
|
|
44675
46344
|
);
|
|
44676
46345
|
}
|
|
44677
46346
|
} finally {
|
|
@@ -44682,10 +46351,10 @@ async function writeBackupWithManifest(options2) {
|
|
|
44682
46351
|
}
|
|
44683
46352
|
async function readBackupManifest(journalPath) {
|
|
44684
46353
|
const root = await prepareBackupRoot(journalPath);
|
|
44685
|
-
const manifestPath =
|
|
46354
|
+
const manifestPath = path24.join(root, "manifest.jsonl");
|
|
44686
46355
|
let raw;
|
|
44687
46356
|
try {
|
|
44688
|
-
const handle = await
|
|
46357
|
+
const handle = await fs23.open(manifestPath, fs23.constants.O_RDONLY | O_NOFOLLOW);
|
|
44689
46358
|
try {
|
|
44690
46359
|
if (!(await handle.stat()).isFile()) {
|
|
44691
46360
|
throw new Error(`backup manifest is not a regular file: ${manifestPath}`);
|
|
@@ -44776,18 +46445,18 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
|
|
|
44776
46445
|
let seq = 0;
|
|
44777
46446
|
const movedRestore = /* @__PURE__ */ new Map();
|
|
44778
46447
|
let journalWriter = null;
|
|
44779
|
-
const operationId =
|
|
46448
|
+
const operationId = path25.basename(deps.journalPath, ".jsonl");
|
|
44780
46449
|
const recordStep = async (step, detail) => {
|
|
44781
46450
|
if (journalWriter === null) {
|
|
44782
|
-
await
|
|
44783
|
-
await assertCanonicalDirectory(
|
|
44784
|
-
const journalDirIdentity = await anchorManagerDirectory(
|
|
44785
|
-
journalWriter = await
|
|
46451
|
+
await fs24.mkdir(path25.dirname(deps.journalPath), { recursive: true });
|
|
46452
|
+
await assertCanonicalDirectory(path25.dirname(deps.journalPath));
|
|
46453
|
+
const journalDirIdentity = await anchorManagerDirectory(path25.dirname(deps.journalPath));
|
|
46454
|
+
journalWriter = await fs24.open(
|
|
44786
46455
|
deps.journalPath,
|
|
44787
|
-
|
|
46456
|
+
fs24.constants.O_WRONLY | fs24.constants.O_CREAT | fs24.constants.O_EXCL | fs24.constants.O_NOFOLLOW,
|
|
44788
46457
|
384
|
|
44789
46458
|
);
|
|
44790
|
-
await verifyDirIdentity(
|
|
46459
|
+
await verifyDirIdentity(path25.dirname(deps.journalPath), journalDirIdentity);
|
|
44791
46460
|
}
|
|
44792
46461
|
seq += 1;
|
|
44793
46462
|
const entry = { seq, step, detail };
|
|
@@ -44797,6 +46466,7 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
|
|
|
44797
46466
|
await journalWriter.sync();
|
|
44798
46467
|
};
|
|
44799
46468
|
const root = deps.workspaces.resolveWritable(snapshot.target).directory;
|
|
46469
|
+
deps.skills.invalidateDiscovery(snapshot.target);
|
|
44800
46470
|
const compensate = async (failure) => {
|
|
44801
46471
|
const targetTrees = buildTargetTrees(journal);
|
|
44802
46472
|
for (const entry of [...journal].reverse()) {
|
|
@@ -44819,6 +46489,7 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
|
|
|
44819
46489
|
};
|
|
44820
46490
|
}
|
|
44821
46491
|
}
|
|
46492
|
+
deps.skills.invalidateDiscovery(snapshot.target);
|
|
44822
46493
|
return { status: "compensated", mutations, failure };
|
|
44823
46494
|
};
|
|
44824
46495
|
try {
|
|
@@ -44940,7 +46611,7 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
|
|
|
44940
46611
|
}
|
|
44941
46612
|
const targets = proposal.patch.kind === "split" ? proposal.patch.targets : [proposal.patch.target];
|
|
44942
46613
|
for (const target of targets) {
|
|
44943
|
-
const destination =
|
|
46614
|
+
const destination = path25.join(root, target.directoryName);
|
|
44944
46615
|
if (await pathExists(destination)) {
|
|
44945
46616
|
throw new DomainError(
|
|
44946
46617
|
"CONFLICT",
|
|
@@ -44985,12 +46656,12 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
|
|
|
44985
46656
|
`Resource mapping source missing from snapshot: ${mapping.sourceSkillId}`
|
|
44986
46657
|
);
|
|
44987
46658
|
}
|
|
44988
|
-
const from =
|
|
44989
|
-
const to =
|
|
46659
|
+
const from = path25.join(root, mappingSource.directoryName, mapping.sourcePath);
|
|
46660
|
+
const to = path25.join(root, target.directoryName, mapping.targetPath);
|
|
44990
46661
|
assertPathInside(root, from);
|
|
44991
46662
|
assertPathInside(root, to);
|
|
44992
|
-
assertPathInside(
|
|
44993
|
-
if (
|
|
46663
|
+
assertPathInside(path25.join(root, target.directoryName), to);
|
|
46664
|
+
if (path25.basename(to).toLowerCase() === "skill.md") {
|
|
44994
46665
|
throw new DomainError(
|
|
44995
46666
|
"INVALID_OPERATION",
|
|
44996
46667
|
`Resource targetPath must not target the skill document: ${target.directoryName}/${mapping.targetPath}`
|
|
@@ -45055,7 +46726,7 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
|
|
|
45055
46726
|
sha256: sha256Hex(writtenBytes),
|
|
45056
46727
|
...backupRef === void 0 ? {} : { backupRef, sourceSha256: liveHash }
|
|
45057
46728
|
});
|
|
45058
|
-
await
|
|
46729
|
+
await fs24.mkdir(path25.dirname(to), { recursive: true });
|
|
45059
46730
|
await assertNoSymlinkAncestors(to, root);
|
|
45060
46731
|
if (mapping.strategy === "reference") {
|
|
45061
46732
|
await writeFileExclusiveVerified(
|
|
@@ -45099,6 +46770,7 @@ async function applyProposalTransaction(proposal, snapshot, deps) {
|
|
|
45099
46770
|
break;
|
|
45100
46771
|
}
|
|
45101
46772
|
}
|
|
46773
|
+
deps.skills.invalidateDiscovery(snapshot.target);
|
|
45102
46774
|
await recordStep("commit", {
|
|
45103
46775
|
kind: "commit",
|
|
45104
46776
|
proposalId: operationId,
|
|
@@ -45137,7 +46809,7 @@ async function toggleWithPostcondition(deps, target, skillId, mode) {
|
|
|
45137
46809
|
}
|
|
45138
46810
|
async function pathExists(target) {
|
|
45139
46811
|
try {
|
|
45140
|
-
await
|
|
46812
|
+
await fs24.stat(target);
|
|
45141
46813
|
return true;
|
|
45142
46814
|
} catch {
|
|
45143
46815
|
return false;
|
|
@@ -45157,7 +46829,7 @@ async function undoJournalSteps(entries, context) {
|
|
|
45157
46829
|
checked.push(parsed.data);
|
|
45158
46830
|
}
|
|
45159
46831
|
assertCommittedJournal(checked, {
|
|
45160
|
-
proposalId:
|
|
46832
|
+
proposalId: path25.basename(context.backupJournalPath ?? context.deps.journalPath, ".jsonl"),
|
|
45161
46833
|
proposal: context.proposal,
|
|
45162
46834
|
snapshot: context.snapshot
|
|
45163
46835
|
});
|
|
@@ -45202,7 +46874,7 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
|
|
|
45202
46874
|
assertPathInside(root, dir);
|
|
45203
46875
|
let stat2;
|
|
45204
46876
|
try {
|
|
45205
|
-
stat2 = await
|
|
46877
|
+
stat2 = await fs24.lstat(dir);
|
|
45206
46878
|
} catch {
|
|
45207
46879
|
return;
|
|
45208
46880
|
}
|
|
@@ -45213,8 +46885,8 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
|
|
|
45213
46885
|
`Created target is no longer a directory (external drift): ${directoryName}`
|
|
45214
46886
|
);
|
|
45215
46887
|
}
|
|
45216
|
-
const realDir = await
|
|
45217
|
-
const lexicalDir =
|
|
46888
|
+
const realDir = await fs24.realpath(dir);
|
|
46889
|
+
const lexicalDir = path25.join(await fs24.realpath(root), path25.relative(root, dir));
|
|
45218
46890
|
if (realDir !== lexicalDir) {
|
|
45219
46891
|
throw new DomainError(
|
|
45220
46892
|
"INVALID_OPERATION",
|
|
@@ -45231,7 +46903,7 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
|
|
|
45231
46903
|
);
|
|
45232
46904
|
}
|
|
45233
46905
|
for (const [file, expectedSha] of expected) {
|
|
45234
|
-
const content = await
|
|
46906
|
+
const content = await fs24.readFile(path25.join(dir, file));
|
|
45235
46907
|
const actualSha = sha256Hex(content);
|
|
45236
46908
|
const wanted = expectedSha.startsWith("sha256:") ? expectedSha.slice("sha256:".length) : expectedSha;
|
|
45237
46909
|
if (actualSha !== wanted) {
|
|
@@ -45244,13 +46916,13 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
|
|
|
45244
46916
|
const identity2 = { dev: stat2.dev, ino: stat2.ino };
|
|
45245
46917
|
const quarantineRoot = await prepareBackupRoot(quarantineJournalPath);
|
|
45246
46918
|
const quarantineIdentity = await assertCanonicalDirectory(quarantineRoot);
|
|
45247
|
-
const tombstone =
|
|
46919
|
+
const tombstone = path25.join(
|
|
45248
46920
|
quarantineRoot,
|
|
45249
|
-
`removed-dir-${randomBytes9(8).toString("hex")}-${
|
|
46921
|
+
`removed-dir-${randomBytes9(8).toString("hex")}-${path25.basename(dir)}`
|
|
45250
46922
|
);
|
|
45251
46923
|
try {
|
|
45252
46924
|
await verifyDirIdentity(root, rootIdentity);
|
|
45253
|
-
await
|
|
46925
|
+
await fs24.rename(dir, tombstone);
|
|
45254
46926
|
} catch (error) {
|
|
45255
46927
|
if (error.code === "EXDEV") {
|
|
45256
46928
|
throw new DomainError(
|
|
@@ -45260,7 +46932,7 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
|
|
|
45260
46932
|
}
|
|
45261
46933
|
throw error;
|
|
45262
46934
|
}
|
|
45263
|
-
const stillExists = await
|
|
46935
|
+
const stillExists = await fs24.lstat(dir).then(
|
|
45264
46936
|
() => true,
|
|
45265
46937
|
() => false
|
|
45266
46938
|
);
|
|
@@ -45270,7 +46942,7 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
|
|
|
45270
46942
|
`Created target survived quarantine rename (path race); recovery required: ${directoryName}`
|
|
45271
46943
|
);
|
|
45272
46944
|
}
|
|
45273
|
-
const tomb = await
|
|
46945
|
+
const tomb = await fs24.lstat(tombstone).catch(() => null);
|
|
45274
46946
|
if (tomb === null || !tomb.isDirectory() || tomb.dev !== identity2.dev || tomb.ino !== identity2.ino) {
|
|
45275
46947
|
throw new DomainError(
|
|
45276
46948
|
"UNAVAILABLE",
|
|
@@ -45283,11 +46955,11 @@ async function removeCreatedDirectory(dir, root, directoryName, tree, quarantine
|
|
|
45283
46955
|
}
|
|
45284
46956
|
async function walkFiles(dir, prefix) {
|
|
45285
46957
|
const files = /* @__PURE__ */ new Map();
|
|
45286
|
-
const entries = await
|
|
46958
|
+
const entries = await fs24.readdir(dir, { withFileTypes: true });
|
|
45287
46959
|
for (const entry of entries) {
|
|
45288
46960
|
const rel = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
45289
46961
|
if (entry.isDirectory()) {
|
|
45290
|
-
for (const [f] of await walkFiles(
|
|
46962
|
+
for (const [f] of await walkFiles(path25.join(dir, entry.name), rel)) files.set(f, "");
|
|
45291
46963
|
} else if (entry.isFile()) {
|
|
45292
46964
|
files.set(rel, "");
|
|
45293
46965
|
} else {
|
|
@@ -45373,7 +47045,7 @@ async function undoStep(entry, context) {
|
|
|
45373
47045
|
);
|
|
45374
47046
|
}
|
|
45375
47047
|
await removeCreatedDirectory(
|
|
45376
|
-
|
|
47048
|
+
path25.join(root, directoryName),
|
|
45377
47049
|
root,
|
|
45378
47050
|
directoryName,
|
|
45379
47051
|
context.targetTrees.get(directoryName),
|
|
@@ -45384,11 +47056,11 @@ async function undoStep(entry, context) {
|
|
|
45384
47056
|
case "resource": {
|
|
45385
47057
|
const { from: fromRel, to, strategy } = entry.detail;
|
|
45386
47058
|
if (strategy === "move") {
|
|
45387
|
-
const from =
|
|
47059
|
+
const from = path25.join(root, fromRel);
|
|
45388
47060
|
assertPathInside(root, from);
|
|
45389
47061
|
const moved = context.movedRestore?.get(entry.seq);
|
|
45390
47062
|
if (moved) {
|
|
45391
|
-
await
|
|
47063
|
+
await fs24.mkdir(path25.dirname(moved.from), { recursive: true });
|
|
45392
47064
|
await restoreResourceBytesStrict(moved.from, root, moved.bytes, fromRel);
|
|
45393
47065
|
} else {
|
|
45394
47066
|
const { backupRef, sourceSha256 } = entry.detail;
|
|
@@ -45407,7 +47079,7 @@ async function undoStep(entry, context) {
|
|
|
45407
47079
|
sha256: sourceSha256
|
|
45408
47080
|
});
|
|
45409
47081
|
const backupRoot = await prepareBackupRoot(backupJournalPath);
|
|
45410
|
-
const backupPath =
|
|
47082
|
+
const backupPath = path25.join(backupRoot, backupRef);
|
|
45411
47083
|
let backup;
|
|
45412
47084
|
try {
|
|
45413
47085
|
backup = await readResourceBytesStrict(
|
|
@@ -45426,7 +47098,7 @@ async function undoStep(entry, context) {
|
|
|
45426
47098
|
`move rollback backup hash mismatch for step ${entry.seq}: ${backupRef}`
|
|
45427
47099
|
);
|
|
45428
47100
|
}
|
|
45429
|
-
await
|
|
47101
|
+
await fs24.mkdir(path25.dirname(from), { recursive: true });
|
|
45430
47102
|
await restoreResourceBytesStrict(from, root, backup, fromRel);
|
|
45431
47103
|
}
|
|
45432
47104
|
}
|
|
@@ -45438,7 +47110,7 @@ async function undoStep(entry, context) {
|
|
|
45438
47110
|
);
|
|
45439
47111
|
}
|
|
45440
47112
|
await removeCreatedDirectory(
|
|
45441
|
-
|
|
47113
|
+
path25.join(root, directoryName),
|
|
45442
47114
|
root,
|
|
45443
47115
|
directoryName,
|
|
45444
47116
|
context.targetTrees.get(directoryName),
|
|
@@ -45469,17 +47141,17 @@ function coerceYamlScalar(raw) {
|
|
|
45469
47141
|
return raw;
|
|
45470
47142
|
}
|
|
45471
47143
|
async function scanUnfinishedJournals(storeDir) {
|
|
45472
|
-
const journalDir =
|
|
47144
|
+
const journalDir = path25.join(storeDir, "journal");
|
|
45473
47145
|
let files;
|
|
45474
47146
|
try {
|
|
45475
|
-
files = await
|
|
47147
|
+
files = await fs24.readdir(journalDir);
|
|
45476
47148
|
} catch {
|
|
45477
47149
|
return [];
|
|
45478
47150
|
}
|
|
45479
47151
|
const unfinished = [];
|
|
45480
47152
|
for (const file of files) {
|
|
45481
47153
|
if (!file.endsWith(".jsonl") || file.endsWith("-rollback.jsonl")) continue;
|
|
45482
|
-
const journalPath =
|
|
47154
|
+
const journalPath = path25.join(journalDir, file);
|
|
45483
47155
|
const proposalId = file.replace(/\.jsonl$/, "");
|
|
45484
47156
|
let entries;
|
|
45485
47157
|
try {
|
|
@@ -45504,7 +47176,7 @@ async function scanUnfinishedJournals(storeDir) {
|
|
|
45504
47176
|
// src/daemon/steward/approval-service.ts
|
|
45505
47177
|
function fingerprintOf(proposal) {
|
|
45506
47178
|
const canonical = JSON.stringify(proposal.patch, Object.keys(proposal.patch).sort());
|
|
45507
|
-
return `sha256:${
|
|
47179
|
+
return `sha256:${createHash11("sha256").update(canonical).digest("hex")}`;
|
|
45508
47180
|
}
|
|
45509
47181
|
function createStewardApprovalService(deps) {
|
|
45510
47182
|
const proposals = /* @__PURE__ */ new Map();
|
|
@@ -45574,7 +47246,7 @@ function createStewardApprovalService(deps) {
|
|
|
45574
47246
|
const targets = entry.proposal.patch.kind === "split" ? entry.proposal.patch.targets.map((target) => target.directoryName) : [entry.proposal.patch.target.directoryName];
|
|
45575
47247
|
let conflict = false;
|
|
45576
47248
|
for (const name of targets) {
|
|
45577
|
-
const exists = await (await import("node:fs/promises")).stat(
|
|
47249
|
+
const exists = await (await import("node:fs/promises")).stat(path26.join(root, name)).then(() => true).catch(() => false);
|
|
45578
47250
|
if (exists) {
|
|
45579
47251
|
conflict = true;
|
|
45580
47252
|
checks.push({
|
|
@@ -45659,7 +47331,7 @@ function createStewardApprovalService(deps) {
|
|
|
45659
47331
|
skills: deps.skills,
|
|
45660
47332
|
creator: deps.creator,
|
|
45661
47333
|
store: deps.store,
|
|
45662
|
-
journalPath:
|
|
47334
|
+
journalPath: path26.join(stewardStoreDir(), "journal", `${proposalId}.jsonl`)
|
|
45663
47335
|
});
|
|
45664
47336
|
const audit = {
|
|
45665
47337
|
id: StewardAuditIdSchema.parse(`aud_${randomBytes10(8).toString("hex")}`),
|
|
@@ -45691,7 +47363,7 @@ function createStewardApprovalService(deps) {
|
|
|
45691
47363
|
const entry = requireProposal(audit.proposalId);
|
|
45692
47364
|
const patch = entry.proposal.patch;
|
|
45693
47365
|
if (patch.kind === "disable") {
|
|
45694
|
-
const journalPathForProbe =
|
|
47366
|
+
const journalPathForProbe = path26.join(
|
|
45695
47367
|
stewardStoreDir(),
|
|
45696
47368
|
"journal",
|
|
45697
47369
|
`${audit.proposalId}.jsonl`
|
|
@@ -45871,7 +47543,7 @@ function createStewardApprovalService(deps) {
|
|
|
45871
47543
|
if (fingerprintOf(entry.proposal) !== grant.fingerprint) {
|
|
45872
47544
|
throw new DomainError("CONFLICT", "Patch fingerprint drifted since the rollback grant.");
|
|
45873
47545
|
}
|
|
45874
|
-
const journalPath =
|
|
47546
|
+
const journalPath = path26.join(stewardStoreDir(), "journal", `${audit.proposalId}.jsonl`);
|
|
45875
47547
|
const root = deps.workspaces.resolveWritable(entry.snapshot.target).directory;
|
|
45876
47548
|
const mutations = [];
|
|
45877
47549
|
try {
|
|
@@ -45889,7 +47561,7 @@ function createStewardApprovalService(deps) {
|
|
|
45889
47561
|
skills: deps.skills,
|
|
45890
47562
|
creator: deps.creator,
|
|
45891
47563
|
store: deps.store,
|
|
45892
|
-
journalPath:
|
|
47564
|
+
journalPath: path26.join(stewardStoreDir(), "journal", `${auditId}-rollback.jsonl`)
|
|
45893
47565
|
},
|
|
45894
47566
|
root,
|
|
45895
47567
|
mutations,
|
|
@@ -46005,7 +47677,7 @@ function createCapabilityRegistry(definitions) {
|
|
|
46005
47677
|
}
|
|
46006
47678
|
|
|
46007
47679
|
// src/daemon/steward/capabilities.ts
|
|
46008
|
-
import { z as
|
|
47680
|
+
import { z as z25 } from "zod";
|
|
46009
47681
|
function ok(value) {
|
|
46010
47682
|
return { kind: "ok", value };
|
|
46011
47683
|
}
|
|
@@ -46029,11 +47701,11 @@ function parseAuditIdInput(input) {
|
|
|
46029
47701
|
const value = input.auditId;
|
|
46030
47702
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
46031
47703
|
}
|
|
46032
|
-
var listInputSchema =
|
|
46033
|
-
var skillIdInputSchema =
|
|
46034
|
-
var proposeInputSchema =
|
|
46035
|
-
var proposalIdInputSchema =
|
|
46036
|
-
var auditIdInputSchema =
|
|
47704
|
+
var listInputSchema = z25.unknown();
|
|
47705
|
+
var skillIdInputSchema = z25.object({ skillId: SkillIdSchema });
|
|
47706
|
+
var proposeInputSchema = z25.object({ proposal: SkillProposalSchema });
|
|
47707
|
+
var proposalIdInputSchema = z25.object({ proposalId: StewardProposalIdSchema });
|
|
47708
|
+
var auditIdInputSchema = z25.object({ auditId: z25.string().min(1) });
|
|
46037
47709
|
function createStewardCapabilities(deps) {
|
|
46038
47710
|
const { snapshot, proposals, validate: validate3, apply, rollback } = deps;
|
|
46039
47711
|
return [
|
|
@@ -46711,7 +48383,7 @@ function createSkillStewardPipelineService(deps) {
|
|
|
46711
48383
|
|
|
46712
48384
|
// src/daemon/steward/dsh-settings.ts
|
|
46713
48385
|
import { promises as fsp } from "node:fs";
|
|
46714
|
-
import
|
|
48386
|
+
import path29 from "node:path";
|
|
46715
48387
|
import { normalizeApiKey } from "@deepseek-ai/dsh-llm";
|
|
46716
48388
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
46717
48389
|
|
|
@@ -46950,7 +48622,7 @@ function dshBridgeModelEntry(model) {
|
|
|
46950
48622
|
}
|
|
46951
48623
|
async function syncDshModelRoutes(routes) {
|
|
46952
48624
|
const dshHome = resolveDefaultDshHome();
|
|
46953
|
-
const file =
|
|
48625
|
+
const file = path29.join(dshHome, "settings.yaml");
|
|
46954
48626
|
let doc = {};
|
|
46955
48627
|
try {
|
|
46956
48628
|
const raw = await fsp.readFile(file, "utf8");
|
|
@@ -46983,7 +48655,7 @@ async function syncDshModelRoutes(routes) {
|
|
|
46983
48655
|
async function syncDshRouteCredential(provider, apiKey) {
|
|
46984
48656
|
const ref = dshRouteApiKeyEnv(provider);
|
|
46985
48657
|
const dshHome = resolveDefaultDshHome();
|
|
46986
|
-
const file =
|
|
48658
|
+
const file = path29.join(dshHome, ".credentials.yaml");
|
|
46987
48659
|
let doc = {};
|
|
46988
48660
|
try {
|
|
46989
48661
|
const parsed = parseYaml(await fsp.readFile(file, "utf8"));
|
|
@@ -47015,10 +48687,10 @@ async function syncDshRouteCredential(provider, apiKey) {
|
|
|
47015
48687
|
}
|
|
47016
48688
|
}
|
|
47017
48689
|
function settingsFile() {
|
|
47018
|
-
return
|
|
48690
|
+
return path29.join(homeDir(), "steward-store", "dsh-settings.json");
|
|
47019
48691
|
}
|
|
47020
48692
|
function credentialsFile() {
|
|
47021
|
-
return
|
|
48693
|
+
return path29.join(homeDir(), "steward-store", "dsh-credentials.json");
|
|
47022
48694
|
}
|
|
47023
48695
|
async function readPrivateJson(file) {
|
|
47024
48696
|
let raw;
|
|
@@ -47290,7 +48962,7 @@ function createDshSettingsService(options2 = {}) {
|
|
|
47290
48962
|
// src/daemon/kernel/agent-sessions.ts
|
|
47291
48963
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
47292
48964
|
import { createUserMessage as createUserMessage2 } from "@deepseek-ai/dsh-llm";
|
|
47293
|
-
import { z as
|
|
48965
|
+
import { z as z27 } from "zod";
|
|
47294
48966
|
init_agent_roles();
|
|
47295
48967
|
init_dsh_kernel();
|
|
47296
48968
|
|
|
@@ -47384,6 +49056,8 @@ var AGENT_MODES = {
|
|
|
47384
49056
|
tools: [
|
|
47385
49057
|
"workspace_list",
|
|
47386
49058
|
"skills_list",
|
|
49059
|
+
"skills_search",
|
|
49060
|
+
"skills_duplicates",
|
|
47387
49061
|
"skills_info",
|
|
47388
49062
|
"skills_validate",
|
|
47389
49063
|
"creator_load",
|
|
@@ -47397,6 +49071,8 @@ var AGENT_MODES = {
|
|
|
47397
49071
|
tools: [
|
|
47398
49072
|
"workspace_list",
|
|
47399
49073
|
"skills_list",
|
|
49074
|
+
"skills_search",
|
|
49075
|
+
"skills_duplicates",
|
|
47400
49076
|
"skills_info",
|
|
47401
49077
|
"skills_validate",
|
|
47402
49078
|
"skills_toggle",
|
|
@@ -47412,6 +49088,8 @@ var AGENT_MODES = {
|
|
|
47412
49088
|
tools: [
|
|
47413
49089
|
"workspace_list",
|
|
47414
49090
|
"skills_list",
|
|
49091
|
+
"skills_search",
|
|
49092
|
+
"skills_duplicates",
|
|
47415
49093
|
"skills_info",
|
|
47416
49094
|
"skills_validate",
|
|
47417
49095
|
"repository_scan",
|
|
@@ -47587,11 +49265,11 @@ async function runBootSessionCleanup(deps) {
|
|
|
47587
49265
|
|
|
47588
49266
|
// src/daemon/kernel/agent-queue.ts
|
|
47589
49267
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
47590
|
-
import { z as
|
|
47591
|
-
var InboxMessageSchema =
|
|
47592
|
-
id:
|
|
47593
|
-
source:
|
|
47594
|
-
content:
|
|
49268
|
+
import { z as z26 } from "zod";
|
|
49269
|
+
var InboxMessageSchema = z26.object({
|
|
49270
|
+
id: z26.string().min(1),
|
|
49271
|
+
source: z26.unknown().optional(),
|
|
49272
|
+
content: z26.array(z26.object({ type: z26.string().min(1), text: z26.string().optional() }).passthrough()).min(1)
|
|
47595
49273
|
}).passthrough();
|
|
47596
49274
|
function parseInboxMessage(raw) {
|
|
47597
49275
|
const checked = InboxMessageSchema.safeParse(raw);
|
|
@@ -47633,8 +49311,8 @@ function rebuildEditedMessage(original, text) {
|
|
|
47633
49311
|
}
|
|
47634
49312
|
|
|
47635
49313
|
// src/daemon/kernel/session-transcripts.ts
|
|
47636
|
-
import
|
|
47637
|
-
import
|
|
49314
|
+
import fs27 from "node:fs";
|
|
49315
|
+
import path30 from "node:path";
|
|
47638
49316
|
function parseMeta(raw) {
|
|
47639
49317
|
if (typeof raw !== "object" || raw === null) return null;
|
|
47640
49318
|
const meta = raw;
|
|
@@ -47661,22 +49339,22 @@ function createSessionTranscripts(rootDir) {
|
|
|
47661
49339
|
let hydrated = false;
|
|
47662
49340
|
function sessionDir(meta) {
|
|
47663
49341
|
const [y, m, d] = dateSegments(meta.createdAt);
|
|
47664
|
-
return
|
|
49342
|
+
return path30.join(rootDir, y, m, d, meta.sessionId);
|
|
47665
49343
|
}
|
|
47666
49344
|
function hydrate() {
|
|
47667
49345
|
if (hydrated) return;
|
|
47668
49346
|
hydrated = true;
|
|
47669
49347
|
let years = [];
|
|
47670
49348
|
try {
|
|
47671
|
-
years =
|
|
49349
|
+
years = fs27.readdirSync(rootDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && /^\d{4}$/.test(entry.name)).map((entry) => entry.name);
|
|
47672
49350
|
} catch {
|
|
47673
49351
|
return;
|
|
47674
49352
|
}
|
|
47675
49353
|
for (const year of years) {
|
|
47676
|
-
for (const month of safeDirs(
|
|
47677
|
-
for (const day of safeDirs(
|
|
47678
|
-
for (const sid of safeDirs(
|
|
47679
|
-
if (!dirs.has(sid)) dirs.set(sid,
|
|
49354
|
+
for (const month of safeDirs(path30.join(rootDir, year))) {
|
|
49355
|
+
for (const day of safeDirs(path30.join(rootDir, year, month))) {
|
|
49356
|
+
for (const sid of safeDirs(path30.join(rootDir, year, month, day))) {
|
|
49357
|
+
if (!dirs.has(sid)) dirs.set(sid, path30.join(rootDir, year, month, day, sid));
|
|
47680
49358
|
}
|
|
47681
49359
|
}
|
|
47682
49360
|
}
|
|
@@ -47684,35 +49362,35 @@ function createSessionTranscripts(rootDir) {
|
|
|
47684
49362
|
}
|
|
47685
49363
|
function safeDirs(dir) {
|
|
47686
49364
|
try {
|
|
47687
|
-
return
|
|
49365
|
+
return fs27.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
47688
49366
|
} catch {
|
|
47689
49367
|
return [];
|
|
47690
49368
|
}
|
|
47691
49369
|
}
|
|
47692
49370
|
function pruneEmptyDateDirs() {
|
|
47693
49371
|
for (const year of safeDirs(rootDir)) {
|
|
47694
|
-
const yearDir =
|
|
49372
|
+
const yearDir = path30.join(rootDir, year);
|
|
47695
49373
|
for (const month of safeDirs(yearDir)) {
|
|
47696
|
-
const monthDir =
|
|
49374
|
+
const monthDir = path30.join(yearDir, month);
|
|
47697
49375
|
for (const day of safeDirs(monthDir)) {
|
|
47698
|
-
const dayDir =
|
|
49376
|
+
const dayDir = path30.join(monthDir, day);
|
|
47699
49377
|
if (safeDirs(dayDir).length === 0) {
|
|
47700
49378
|
try {
|
|
47701
|
-
|
|
49379
|
+
fs27.rmdirSync(dayDir);
|
|
47702
49380
|
} catch {
|
|
47703
49381
|
}
|
|
47704
49382
|
}
|
|
47705
49383
|
}
|
|
47706
49384
|
if (safeDirs(monthDir).length === 0) {
|
|
47707
49385
|
try {
|
|
47708
|
-
|
|
49386
|
+
fs27.rmdirSync(monthDir);
|
|
47709
49387
|
} catch {
|
|
47710
49388
|
}
|
|
47711
49389
|
}
|
|
47712
49390
|
}
|
|
47713
49391
|
if (safeDirs(yearDir).length === 0) {
|
|
47714
49392
|
try {
|
|
47715
|
-
|
|
49393
|
+
fs27.rmdirSync(yearDir);
|
|
47716
49394
|
} catch {
|
|
47717
49395
|
}
|
|
47718
49396
|
}
|
|
@@ -47722,12 +49400,12 @@ function createSessionTranscripts(rootDir) {
|
|
|
47722
49400
|
recordStart(meta) {
|
|
47723
49401
|
try {
|
|
47724
49402
|
const dir = sessionDir(meta);
|
|
47725
|
-
|
|
47726
|
-
const target =
|
|
49403
|
+
fs27.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
49404
|
+
const target = path30.join(dir, "meta.json");
|
|
47727
49405
|
const tmp = `${target}.tmp`;
|
|
47728
|
-
|
|
49406
|
+
fs27.writeFileSync(tmp, `${JSON.stringify(meta)}
|
|
47729
49407
|
`, { mode: 384 });
|
|
47730
|
-
|
|
49408
|
+
fs27.renameSync(tmp, target);
|
|
47731
49409
|
dirs.set(meta.sessionId, dir);
|
|
47732
49410
|
} catch (error) {
|
|
47733
49411
|
console.error(`[session-transcripts] recordStart failed for ${meta.sessionId}:`, error);
|
|
@@ -47737,7 +49415,7 @@ function createSessionTranscripts(rootDir) {
|
|
|
47737
49415
|
try {
|
|
47738
49416
|
const dir = dirs.get(sessionId);
|
|
47739
49417
|
if (dir === void 0) return;
|
|
47740
|
-
|
|
49418
|
+
fs27.appendFileSync(path30.join(dir, "frames.jsonl"), `${JSON.stringify(frame)}
|
|
47741
49419
|
`, {
|
|
47742
49420
|
mode: 384
|
|
47743
49421
|
});
|
|
@@ -47749,14 +49427,14 @@ function createSessionTranscripts(rootDir) {
|
|
|
47749
49427
|
const dir = dirs.get(sessionId);
|
|
47750
49428
|
if (dir === void 0) return false;
|
|
47751
49429
|
try {
|
|
47752
|
-
const meta = parseMeta(JSON.parse(
|
|
49430
|
+
const meta = parseMeta(JSON.parse(fs27.readFileSync(path30.join(dir, "meta.json"), "utf8")));
|
|
47753
49431
|
if (meta === null || meta.mode === mode) return meta !== null;
|
|
47754
49432
|
const next = { ...meta, mode };
|
|
47755
|
-
const target =
|
|
49433
|
+
const target = path30.join(dir, "meta.json");
|
|
47756
49434
|
const tmp = `${target}.tmp`;
|
|
47757
|
-
|
|
49435
|
+
fs27.writeFileSync(tmp, `${JSON.stringify(next)}
|
|
47758
49436
|
`, { mode: 384 });
|
|
47759
|
-
|
|
49437
|
+
fs27.renameSync(tmp, target);
|
|
47760
49438
|
return true;
|
|
47761
49439
|
} catch (error) {
|
|
47762
49440
|
console.error(`[session-transcripts] updateMode failed for ${sessionId}:`, error);
|
|
@@ -47769,14 +49447,14 @@ function createSessionTranscripts(rootDir) {
|
|
|
47769
49447
|
const dir = dirs.get(sessionId);
|
|
47770
49448
|
if (dir === void 0) return false;
|
|
47771
49449
|
try {
|
|
47772
|
-
const meta = parseMeta(JSON.parse(
|
|
49450
|
+
const meta = parseMeta(JSON.parse(fs27.readFileSync(path30.join(dir, "meta.json"), "utf8")));
|
|
47773
49451
|
if (meta === null || meta.title === trimmed) return meta !== null;
|
|
47774
49452
|
const next = { ...meta, title: trimmed };
|
|
47775
|
-
const target =
|
|
49453
|
+
const target = path30.join(dir, "meta.json");
|
|
47776
49454
|
const tmp = `${target}.tmp`;
|
|
47777
|
-
|
|
49455
|
+
fs27.writeFileSync(tmp, `${JSON.stringify(next)}
|
|
47778
49456
|
`, { mode: 384 });
|
|
47779
|
-
|
|
49457
|
+
fs27.renameSync(tmp, target);
|
|
47780
49458
|
return true;
|
|
47781
49459
|
} catch (error) {
|
|
47782
49460
|
console.error(`[session-transcripts] updateTitle failed for ${sessionId}:`, error);
|
|
@@ -47788,7 +49466,7 @@ function createSessionTranscripts(rootDir) {
|
|
|
47788
49466
|
const metas = [];
|
|
47789
49467
|
for (const dir of dirs.values()) {
|
|
47790
49468
|
try {
|
|
47791
|
-
const meta = parseMeta(JSON.parse(
|
|
49469
|
+
const meta = parseMeta(JSON.parse(fs27.readFileSync(path30.join(dir, "meta.json"), "utf8")));
|
|
47792
49470
|
if (meta !== null) metas.push(meta);
|
|
47793
49471
|
} catch {
|
|
47794
49472
|
}
|
|
@@ -47802,7 +49480,7 @@ function createSessionTranscripts(rootDir) {
|
|
|
47802
49480
|
if (dir === void 0) return [];
|
|
47803
49481
|
let lines = [];
|
|
47804
49482
|
try {
|
|
47805
|
-
lines =
|
|
49483
|
+
lines = fs27.readFileSync(path30.join(dir, "frames.jsonl"), "utf8").split("\n");
|
|
47806
49484
|
} catch {
|
|
47807
49485
|
return [];
|
|
47808
49486
|
}
|
|
@@ -47822,7 +49500,7 @@ function createSessionTranscripts(rootDir) {
|
|
|
47822
49500
|
hydrate();
|
|
47823
49501
|
const buckets = /* @__PURE__ */ new Map();
|
|
47824
49502
|
for (const [sessionId, dir] of dirs) {
|
|
47825
|
-
const segments =
|
|
49503
|
+
const segments = path30.relative(rootDir, dir).split(path30.sep);
|
|
47826
49504
|
if (segments.length !== 4) continue;
|
|
47827
49505
|
const [year, month, day] = segments;
|
|
47828
49506
|
if (!/^\d{4}$/.test(year) || !/^\d{2}$/.test(month) || !/^\d{2}$/.test(day)) continue;
|
|
@@ -47840,7 +49518,7 @@ function createSessionTranscripts(rootDir) {
|
|
|
47840
49518
|
const dir = dirs.get(sessionId);
|
|
47841
49519
|
if (dir === void 0) return false;
|
|
47842
49520
|
try {
|
|
47843
|
-
|
|
49521
|
+
fs27.rmSync(dir, { recursive: true, force: true });
|
|
47844
49522
|
} catch (error) {
|
|
47845
49523
|
console.error(`[session-transcripts] remove failed for ${sessionId}:`, error);
|
|
47846
49524
|
return false;
|
|
@@ -47863,35 +49541,35 @@ function summaryOfMeta(meta) {
|
|
|
47863
49541
|
}
|
|
47864
49542
|
|
|
47865
49543
|
// src/daemon/kernel/agent-sessions.ts
|
|
47866
|
-
var AgentChunkEventSchema =
|
|
47867
|
-
chunk:
|
|
47868
|
-
type:
|
|
47869
|
-
text:
|
|
47870
|
-
id:
|
|
47871
|
-
name:
|
|
47872
|
-
argumentsDelta:
|
|
49544
|
+
var AgentChunkEventSchema = z27.object({
|
|
49545
|
+
chunk: z27.object({
|
|
49546
|
+
type: z27.string().min(1),
|
|
49547
|
+
text: z27.string().optional(),
|
|
49548
|
+
id: z27.string().optional(),
|
|
49549
|
+
name: z27.string().optional(),
|
|
49550
|
+
argumentsDelta: z27.string().optional()
|
|
47873
49551
|
})
|
|
47874
49552
|
});
|
|
47875
|
-
var TodoWriteEventSchema =
|
|
47876
|
-
todos:
|
|
47877
|
-
|
|
47878
|
-
content:
|
|
47879
|
-
status:
|
|
49553
|
+
var TodoWriteEventSchema = z27.object({
|
|
49554
|
+
todos: z27.array(
|
|
49555
|
+
z27.object({
|
|
49556
|
+
content: z27.string(),
|
|
49557
|
+
status: z27.string().min(1)
|
|
47880
49558
|
})
|
|
47881
49559
|
)
|
|
47882
49560
|
});
|
|
47883
|
-
var MessageSourceSchema =
|
|
47884
|
-
kind:
|
|
47885
|
-
callId:
|
|
49561
|
+
var MessageSourceSchema = z27.object({
|
|
49562
|
+
kind: z27.string().optional(),
|
|
49563
|
+
callId: z27.string().optional()
|
|
47886
49564
|
}).passthrough();
|
|
47887
|
-
var MessageBlockSchema =
|
|
47888
|
-
type:
|
|
47889
|
-
text:
|
|
47890
|
-
name:
|
|
49565
|
+
var MessageBlockSchema = z27.object({
|
|
49566
|
+
type: z27.string().min(1),
|
|
49567
|
+
text: z27.string().optional(),
|
|
49568
|
+
name: z27.string().optional()
|
|
47891
49569
|
}).passthrough();
|
|
47892
|
-
var MessageShapeSchema =
|
|
49570
|
+
var MessageShapeSchema = z27.object({
|
|
47893
49571
|
source: MessageSourceSchema.optional(),
|
|
47894
|
-
content:
|
|
49572
|
+
content: z27.array(MessageBlockSchema).min(1)
|
|
47895
49573
|
}).passthrough();
|
|
47896
49574
|
function messageEnvelopeOf(raw) {
|
|
47897
49575
|
if (typeof raw === "object" && raw !== null && "message" in raw) {
|
|
@@ -47900,29 +49578,29 @@ function messageEnvelopeOf(raw) {
|
|
|
47900
49578
|
}
|
|
47901
49579
|
return raw;
|
|
47902
49580
|
}
|
|
47903
|
-
var MessageEventSchema =
|
|
47904
|
-
var TurnEndEventSchema =
|
|
47905
|
-
reason:
|
|
49581
|
+
var MessageEventSchema = z27.preprocess(messageEnvelopeOf, MessageShapeSchema);
|
|
49582
|
+
var TurnEndEventSchema = z27.object({
|
|
49583
|
+
reason: z27.object({ kind: z27.string().optional() }).passthrough().optional()
|
|
47906
49584
|
}).passthrough();
|
|
47907
|
-
var SessionTitleEventSchema =
|
|
47908
|
-
title:
|
|
49585
|
+
var SessionTitleEventSchema = z27.object({
|
|
49586
|
+
title: z27.string().optional()
|
|
47909
49587
|
}).passthrough();
|
|
47910
|
-
var ToolCallEventSchema =
|
|
47911
|
-
callId:
|
|
47912
|
-
name:
|
|
47913
|
-
arguments:
|
|
49588
|
+
var ToolCallEventSchema = z27.object({
|
|
49589
|
+
callId: z27.string().min(1),
|
|
49590
|
+
name: z27.string().min(1),
|
|
49591
|
+
arguments: z27.string()
|
|
47914
49592
|
}).passthrough();
|
|
47915
|
-
var ToolResultEventSchema =
|
|
47916
|
-
message:
|
|
47917
|
-
source:
|
|
47918
|
-
content:
|
|
47919
|
-
|
|
47920
|
-
type:
|
|
47921
|
-
toolCallId:
|
|
47922
|
-
content:
|
|
47923
|
-
|
|
47924
|
-
type:
|
|
47925
|
-
text:
|
|
49593
|
+
var ToolResultEventSchema = z27.object({
|
|
49594
|
+
message: z27.object({
|
|
49595
|
+
source: z27.object({ kind: z27.literal("tool"), callId: z27.string().min(1) }).passthrough(),
|
|
49596
|
+
content: z27.tuple([
|
|
49597
|
+
z27.object({
|
|
49598
|
+
type: z27.literal("tool-result"),
|
|
49599
|
+
toolCallId: z27.string().min(1),
|
|
49600
|
+
content: z27.array(
|
|
49601
|
+
z27.object({
|
|
49602
|
+
type: z27.string().min(1),
|
|
49603
|
+
text: z27.string().optional()
|
|
47926
49604
|
}).passthrough()
|
|
47927
49605
|
).min(1).refine(
|
|
47928
49606
|
(parts) => parts.some(
|
|
@@ -47934,15 +49612,15 @@ var ToolResultEventSchema = z22.object({
|
|
|
47934
49612
|
])
|
|
47935
49613
|
}).passthrough()
|
|
47936
49614
|
});
|
|
47937
|
-
var TurnStartEventSchema =
|
|
47938
|
-
var AgentStatusEventSchema =
|
|
47939
|
-
var SubagentCatalogEventSchema =
|
|
47940
|
-
childId:
|
|
47941
|
-
childCreatedAt:
|
|
47942
|
-
mode:
|
|
47943
|
-
label:
|
|
49615
|
+
var TurnStartEventSchema = z27.record(z27.string(), z27.unknown());
|
|
49616
|
+
var AgentStatusEventSchema = z27.record(z27.string(), z27.unknown());
|
|
49617
|
+
var SubagentCatalogEventSchema = z27.object({
|
|
49618
|
+
childId: z27.string().min(1),
|
|
49619
|
+
childCreatedAt: z27.number().optional(),
|
|
49620
|
+
mode: z27.enum(["one-shot", "continuable"]),
|
|
49621
|
+
label: z27.string().optional()
|
|
47944
49622
|
});
|
|
47945
|
-
var UsageEnvelopeSchema =
|
|
49623
|
+
var UsageEnvelopeSchema = z27.object({ usage: z27.record(z27.string(), z27.unknown()).optional() }).passthrough();
|
|
47946
49624
|
var DROPPED_EVENT_LOG_MAX = 200;
|
|
47947
49625
|
function logDroppedEvent(sessionId, type, data) {
|
|
47948
49626
|
let detail;
|
|
@@ -48996,9 +50674,9 @@ function productToolDenyList(globalNames, mode) {
|
|
|
48996
50674
|
}
|
|
48997
50675
|
|
|
48998
50676
|
// src/daemon/model-catalog.ts
|
|
48999
|
-
import
|
|
49000
|
-
import
|
|
49001
|
-
import { createRequire as
|
|
50677
|
+
import fs28 from "node:fs";
|
|
50678
|
+
import path31 from "node:path";
|
|
50679
|
+
import { createRequire as createRequire6 } from "node:module";
|
|
49002
50680
|
|
|
49003
50681
|
// src/shared/provider-icons.generated.ts
|
|
49004
50682
|
var PROVIDER_ICONS = {
|
|
@@ -49065,13 +50743,13 @@ function labelOf(provider) {
|
|
|
49065
50743
|
return cn ? `${pretty} \xB7 CN` : pretty;
|
|
49066
50744
|
}
|
|
49067
50745
|
function resolveCatalogDataDir() {
|
|
49068
|
-
const require2 =
|
|
50746
|
+
const require2 = createRequire6(import.meta.url);
|
|
49069
50747
|
const basePkg = require2.resolve("@deepseek-ai/dsh-base/package.json");
|
|
49070
|
-
const baseNm =
|
|
49071
|
-
const hostReal =
|
|
49072
|
-
const piAi =
|
|
49073
|
-
const dataDir =
|
|
49074
|
-
if (!
|
|
50748
|
+
const baseNm = path31.dirname(path31.dirname(path31.dirname(basePkg)));
|
|
50749
|
+
const hostReal = fs28.realpathSync(path31.join(baseNm, "@deepseek-ai/dsh-llm-pi-ai"));
|
|
50750
|
+
const piAi = path31.join(path31.dirname(hostReal), "../@earendil-works/pi-ai");
|
|
50751
|
+
const dataDir = path31.join(piAi, "dist", "providers", "data");
|
|
50752
|
+
if (!fs28.existsSync(dataDir)) {
|
|
49075
50753
|
throw new DomainError("UNAVAILABLE", `pi-ai catalog data not found at ${dataDir}`);
|
|
49076
50754
|
}
|
|
49077
50755
|
return dataDir;
|
|
@@ -49112,13 +50790,13 @@ function listModelProviders() {
|
|
|
49112
50790
|
if (cached !== null) return cached;
|
|
49113
50791
|
const dataDir = resolveCatalogDataDir();
|
|
49114
50792
|
const entries = [];
|
|
49115
|
-
for (const file of
|
|
50793
|
+
for (const file of fs28.readdirSync(dataDir)) {
|
|
49116
50794
|
if (!file.endsWith(".json")) continue;
|
|
49117
50795
|
const provider = file.slice(0, -".json".length);
|
|
49118
50796
|
if (EXCLUDED_PROVIDERS.has(provider)) continue;
|
|
49119
50797
|
let parsed;
|
|
49120
50798
|
try {
|
|
49121
|
-
parsed = JSON.parse(
|
|
50799
|
+
parsed = JSON.parse(fs28.readFileSync(path31.join(dataDir, file), "utf8"));
|
|
49122
50800
|
} catch {
|
|
49123
50801
|
continue;
|
|
49124
50802
|
}
|
|
@@ -49173,7 +50851,7 @@ function createModelCatalogService() {
|
|
|
49173
50851
|
}
|
|
49174
50852
|
|
|
49175
50853
|
// src/daemon/capability/domain-capabilities.ts
|
|
49176
|
-
import { z as
|
|
50854
|
+
import { z as z28 } from "zod";
|
|
49177
50855
|
function toResult(error) {
|
|
49178
50856
|
if (error instanceof DomainError) {
|
|
49179
50857
|
return { kind: "failed", code: error.code, message: error.message };
|
|
@@ -49194,7 +50872,7 @@ async function invoke(action) {
|
|
|
49194
50872
|
return toResult(error);
|
|
49195
50873
|
}
|
|
49196
50874
|
}
|
|
49197
|
-
var none =
|
|
50875
|
+
var none = z28.object({});
|
|
49198
50876
|
function createDomainCapabilities(domain) {
|
|
49199
50877
|
return [
|
|
49200
50878
|
{
|
|
@@ -49272,6 +50950,41 @@ function createDomainCapabilities(domain) {
|
|
|
49272
50950
|
return domain.skills.validate(parsed, parsed.skillId);
|
|
49273
50951
|
})
|
|
49274
50952
|
},
|
|
50953
|
+
{
|
|
50954
|
+
name: "skills.search",
|
|
50955
|
+
description: "Search installed local skills across all workspaces; returns stable skill ids with workspace/provider scope.",
|
|
50956
|
+
authority: "readonly",
|
|
50957
|
+
input: SkillsSearchInputSchema,
|
|
50958
|
+
handler: (input) => invoke(async () => {
|
|
50959
|
+
const parsed = SkillsSearchInputSchema.parse(input);
|
|
50960
|
+
return {
|
|
50961
|
+
results: await domain.skillSearch.search(parsed.query, {
|
|
50962
|
+
limit: parsed.limit ?? 10
|
|
50963
|
+
})
|
|
50964
|
+
};
|
|
50965
|
+
})
|
|
50966
|
+
},
|
|
50967
|
+
{
|
|
50968
|
+
name: "skills.duplicates",
|
|
50969
|
+
description: "List content-duplicate skill groups from the search index (contentHash groups with more than one canonical entry).",
|
|
50970
|
+
authority: "readonly",
|
|
50971
|
+
input: z28.object({}).strict(),
|
|
50972
|
+
handler: (input) => invoke(async () => {
|
|
50973
|
+
z28.object({}).strict().parse(input);
|
|
50974
|
+
return { groups: await domain.skillSearch.duplicates() };
|
|
50975
|
+
})
|
|
50976
|
+
},
|
|
50977
|
+
{
|
|
50978
|
+
name: "skills.search_config.open",
|
|
50979
|
+
description: "Open the server-owned skill search config file (search-config.toml) with the system default editor.",
|
|
50980
|
+
authority: "readonly",
|
|
50981
|
+
input: z28.object({}).strict(),
|
|
50982
|
+
handler: (input) => invoke(async () => {
|
|
50983
|
+
z28.object({}).strict().parse(input);
|
|
50984
|
+
await domain.searchConfigOpener(searchConfigPath());
|
|
50985
|
+
return { opened: true };
|
|
50986
|
+
})
|
|
50987
|
+
},
|
|
49275
50988
|
{
|
|
49276
50989
|
name: "skills.update.check",
|
|
49277
50990
|
description: "Compare skills-CLI lock hashes against upstream (read-only).",
|
|
@@ -49598,7 +51311,7 @@ function createMcpProposalStore(registry) {
|
|
|
49598
51311
|
}
|
|
49599
51312
|
|
|
49600
51313
|
// src/daemon/steward/jsonrpc-stdio.ts
|
|
49601
|
-
import { spawn as
|
|
51314
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
49602
51315
|
function parseFrame2(raw) {
|
|
49603
51316
|
let value;
|
|
49604
51317
|
try {
|
|
@@ -49797,7 +51510,7 @@ var StdioJsonRpcClient = class {
|
|
|
49797
51510
|
}
|
|
49798
51511
|
};
|
|
49799
51512
|
function defaultStdioSpawn(command, args, options2) {
|
|
49800
|
-
return
|
|
51513
|
+
return spawn6(command, args, { cwd: options2.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
49801
51514
|
}
|
|
49802
51515
|
function parseCommandSpec(spec) {
|
|
49803
51516
|
const trimmed = spec.trim();
|
|
@@ -50146,182 +51859,19 @@ function createFixtureHarnessAdapter(options2 = {}) {
|
|
|
50146
51859
|
}
|
|
50147
51860
|
|
|
50148
51861
|
// src/daemon/workspace-registry/index.ts
|
|
50149
|
-
import
|
|
50150
|
-
import
|
|
50151
|
-
|
|
50152
|
-
// src/daemon/provider-roots.ts
|
|
50153
|
-
import fs20 from "node:fs";
|
|
50154
|
-
import os5 from "node:os";
|
|
50155
|
-
import path23 from "node:path";
|
|
50156
|
-
function requireProvider(providerId) {
|
|
50157
|
-
const provider = providerCatalogEntry(providerId);
|
|
50158
|
-
if (!provider) throw new DomainError("NOT_FOUND", `Provider not found: ${providerId}`);
|
|
50159
|
-
return provider;
|
|
50160
|
-
}
|
|
50161
|
-
function globalProviderRoot(provider) {
|
|
50162
|
-
if (provider.globalPath === null) return null;
|
|
50163
|
-
const home = os5.homedir();
|
|
50164
|
-
const override = globalHomeOverride(provider.id);
|
|
50165
|
-
if (override) return path23.join(override, "skills");
|
|
50166
|
-
if (provider.id === "openclaw") return openClawRoot(home);
|
|
50167
|
-
if (provider.globalPath.startsWith(".config/")) {
|
|
50168
|
-
const configHome = process.env.XDG_CONFIG_HOME?.trim() || path23.join(home, ".config");
|
|
50169
|
-
return path23.join(configHome, provider.globalPath.slice(".config/".length));
|
|
50170
|
-
}
|
|
50171
|
-
return path23.join(home, provider.globalPath);
|
|
50172
|
-
}
|
|
50173
|
-
function importedProviderRoot(workspaceRoot, provider) {
|
|
50174
|
-
const root = path23.resolve(workspaceRoot, provider.workspacePath);
|
|
50175
|
-
const relative2 = path23.relative(workspaceRoot, root);
|
|
50176
|
-
if (relative2.startsWith("..") || path23.isAbsolute(relative2)) {
|
|
50177
|
-
throw new Error(`Provider root escaped Workspace: ${provider.id}`);
|
|
50178
|
-
}
|
|
50179
|
-
return root;
|
|
50180
|
-
}
|
|
50181
|
-
function providerRootAvailable(root) {
|
|
50182
|
-
if (!root) return false;
|
|
50183
|
-
try {
|
|
50184
|
-
return fs20.statSync(root).isDirectory();
|
|
50185
|
-
} catch {
|
|
50186
|
-
return false;
|
|
50187
|
-
}
|
|
50188
|
-
}
|
|
50189
|
-
function globalHomeOverride(providerId) {
|
|
50190
|
-
const variable = providerId === "codex" ? "CODEX_HOME" : providerId === "claude-code" ? "CLAUDE_CONFIG_DIR" : providerId === "mistral-vibe" ? "VIBE_HOME" : providerId === "hermes-agent" ? "HERMES_HOME" : providerId === "autohand-code" ? "AUTOHAND_HOME" : providerId === "grok" ? "GROK_HOME" : null;
|
|
50191
|
-
const value = variable ? process.env[variable]?.trim() : void 0;
|
|
50192
|
-
return value || null;
|
|
50193
|
-
}
|
|
50194
|
-
function openClawRoot(home) {
|
|
50195
|
-
for (const directory of [".openclaw", ".clawdbot", ".moltbot"]) {
|
|
50196
|
-
if (providerRootAvailable(path23.join(home, directory)))
|
|
50197
|
-
return path23.join(home, directory, "skills");
|
|
50198
|
-
}
|
|
50199
|
-
return path23.join(home, ".openclaw", "skills");
|
|
50200
|
-
}
|
|
50201
|
-
|
|
50202
|
-
// src/daemon/workspace-registry/persistence.ts
|
|
50203
|
-
import fs21 from "node:fs";
|
|
50204
|
-
import path25 from "node:path";
|
|
50205
|
-
|
|
50206
|
-
// src/daemon/workspace-registry/state.ts
|
|
50207
|
-
import path24 from "node:path";
|
|
50208
|
-
import { z as z24 } from "zod";
|
|
50209
|
-
var StoredWorkspaceSchema = z24.object({
|
|
50210
|
-
id: ImportedWorkspaceIdSchema,
|
|
50211
|
-
label: z24.string().trim().min(1),
|
|
50212
|
-
path: z24.string().min(1).refine(path24.isAbsolute, "Workspace path must be absolute.").refine((value) => path24.normalize(value) === value, "Workspace path must be normalized.")
|
|
50213
|
-
}).strict();
|
|
50214
|
-
var WorkspaceRegistryStateSchema = z24.object({
|
|
50215
|
-
schemaVersion: z24.literal(2),
|
|
50216
|
-
activeId: WorkspaceIdSchema,
|
|
50217
|
-
workspaces: z24.array(StoredWorkspaceSchema)
|
|
50218
|
-
}).strict().superRefine((state, context) => {
|
|
50219
|
-
const ids = /* @__PURE__ */ new Set();
|
|
50220
|
-
const paths = /* @__PURE__ */ new Set();
|
|
50221
|
-
for (const [index, workspace] of state.workspaces.entries()) {
|
|
50222
|
-
if (workspace.id !== opaquePathId("ws", workspace.path)) {
|
|
50223
|
-
context.addIssue({
|
|
50224
|
-
code: "custom",
|
|
50225
|
-
message: "Workspace ID does not match its path.",
|
|
50226
|
-
path: ["workspaces", index, "id"]
|
|
50227
|
-
});
|
|
50228
|
-
}
|
|
50229
|
-
if (ids.has(workspace.id)) {
|
|
50230
|
-
context.addIssue({
|
|
50231
|
-
code: "custom",
|
|
50232
|
-
message: `Duplicate Workspace ID: ${workspace.id}`,
|
|
50233
|
-
path: ["workspaces", index, "id"]
|
|
50234
|
-
});
|
|
50235
|
-
}
|
|
50236
|
-
if (paths.has(workspace.path)) {
|
|
50237
|
-
context.addIssue({
|
|
50238
|
-
code: "custom",
|
|
50239
|
-
message: `Duplicate Workspace path: ${workspace.path}`,
|
|
50240
|
-
path: ["workspaces", index, "path"]
|
|
50241
|
-
});
|
|
50242
|
-
}
|
|
50243
|
-
ids.add(workspace.id);
|
|
50244
|
-
paths.add(workspace.path);
|
|
50245
|
-
}
|
|
50246
|
-
if (state.activeId !== GLOBAL_WORKSPACE_ID && !ids.has(state.activeId)) {
|
|
50247
|
-
context.addIssue({
|
|
50248
|
-
code: "custom",
|
|
50249
|
-
message: `Active Workspace is not registered: ${state.activeId}`,
|
|
50250
|
-
path: ["activeId"]
|
|
50251
|
-
});
|
|
50252
|
-
}
|
|
50253
|
-
});
|
|
50254
|
-
function emptyRegistryState() {
|
|
50255
|
-
return { schemaVersion: 2, activeId: GLOBAL_WORKSPACE_ID, workspaces: [] };
|
|
50256
|
-
}
|
|
50257
|
-
function importWorkspace(state, entry) {
|
|
50258
|
-
const existing = state.workspaces.find((workspace) => workspace.path === entry.path);
|
|
50259
|
-
if (existing) return { state, entry: existing, changed: false };
|
|
50260
|
-
const collision = state.workspaces.find((workspace) => workspace.id === entry.id);
|
|
50261
|
-
if (collision) {
|
|
50262
|
-
throw new Error(`Workspace ID collision between ${collision.path} and ${entry.path}`);
|
|
50263
|
-
}
|
|
50264
|
-
return {
|
|
50265
|
-
state: {
|
|
50266
|
-
...state,
|
|
50267
|
-
activeId: entry.id,
|
|
50268
|
-
workspaces: [...state.workspaces, entry]
|
|
50269
|
-
},
|
|
50270
|
-
entry,
|
|
50271
|
-
changed: true
|
|
50272
|
-
};
|
|
50273
|
-
}
|
|
50274
|
-
function forgetWorkspace(state, id) {
|
|
50275
|
-
if (!state.workspaces.some((workspace) => workspace.id === id)) return null;
|
|
50276
|
-
return {
|
|
50277
|
-
...state,
|
|
50278
|
-
activeId: state.activeId === id ? GLOBAL_WORKSPACE_ID : state.activeId,
|
|
50279
|
-
workspaces: state.workspaces.filter((workspace) => workspace.id !== id)
|
|
50280
|
-
};
|
|
50281
|
-
}
|
|
50282
|
-
function activateWorkspace(state, id) {
|
|
50283
|
-
if (id !== GLOBAL_WORKSPACE_ID && !state.workspaces.some((workspace) => workspace.id === id)) {
|
|
50284
|
-
return null;
|
|
50285
|
-
}
|
|
50286
|
-
if (state.activeId === id) return state;
|
|
50287
|
-
return { ...state, activeId: id };
|
|
50288
|
-
}
|
|
50289
|
-
|
|
50290
|
-
// src/daemon/workspace-registry/persistence.ts
|
|
50291
|
-
var WORKSPACES_FILE = "workspaces.json";
|
|
50292
|
-
function createWorkspaceRegistryPersistence() {
|
|
50293
|
-
const file = path25.join(appDir(), WORKSPACES_FILE);
|
|
50294
|
-
return {
|
|
50295
|
-
load: () => loadState2(file),
|
|
50296
|
-
commit: (state) => commitState2(file, state)
|
|
50297
|
-
};
|
|
50298
|
-
}
|
|
50299
|
-
function loadState2(file) {
|
|
50300
|
-
if (!fs21.existsSync(file)) return emptyRegistryState();
|
|
50301
|
-
let source;
|
|
50302
|
-
try {
|
|
50303
|
-
source = fs21.readFileSync(file, "utf8");
|
|
50304
|
-
} catch (error) {
|
|
50305
|
-
throw new Error(
|
|
50306
|
-
`Cannot read workspace registry ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
50307
|
-
);
|
|
50308
|
-
}
|
|
50309
|
-
return safeParseJson(source, WorkspaceRegistryStateSchema) ?? emptyRegistryState();
|
|
50310
|
-
}
|
|
50311
|
-
function commitState2(file, state) {
|
|
50312
|
-
const validated = WorkspaceRegistryStateSchema.parse(state);
|
|
50313
|
-
fs21.mkdirSync(path25.dirname(file), { recursive: true, mode: 448 });
|
|
50314
|
-
atomicWriteUtf8(file, `${JSON.stringify(validated, null, 2)}
|
|
50315
|
-
`);
|
|
50316
|
-
}
|
|
51862
|
+
import fs29 from "node:fs";
|
|
51863
|
+
import path33 from "node:path";
|
|
50317
51864
|
|
|
50318
51865
|
// src/daemon/workspace-registry/projection.ts
|
|
50319
|
-
import
|
|
51866
|
+
import os6 from "node:os";
|
|
51867
|
+
import path32 from "node:path";
|
|
50320
51868
|
function providerCountKey(workspaceId, providerId) {
|
|
50321
51869
|
return `${workspaceId}:${providerId}`;
|
|
50322
51870
|
}
|
|
50323
|
-
|
|
50324
|
-
|
|
51871
|
+
var SKIP_PLUGINS_PATH = path32.join(os6.tmpdir(), `.skill-creator-skip-plugins-${process.pid}`);
|
|
51872
|
+
async function scanWorkspaceSnapshot(state, listSkills2) {
|
|
51873
|
+
const counts = /* @__PURE__ */ new Map();
|
|
51874
|
+
const skillKeys = /* @__PURE__ */ new Map();
|
|
50325
51875
|
const workspaces = [
|
|
50326
51876
|
{ id: GLOBAL_WORKSPACE_ID, directory: null, global: true },
|
|
50327
51877
|
...state.workspaces.map((workspace) => ({
|
|
@@ -50330,56 +51880,33 @@ async function countWorkspaceSnapshot(state, countSkills) {
|
|
|
50330
51880
|
global: false
|
|
50331
51881
|
}))
|
|
50332
51882
|
];
|
|
51883
|
+
const tasks = [];
|
|
50333
51884
|
for (const workspace of workspaces) {
|
|
51885
|
+
const keySet = /* @__PURE__ */ new Set();
|
|
51886
|
+
skillKeys.set(workspace.id, keySet);
|
|
50334
51887
|
for (const provider of PROVIDER_CATALOG) {
|
|
50335
51888
|
const root = workspace.global ? globalProviderRoot(provider) : workspace.directory ? importedProviderRoot(workspace.directory, provider) : null;
|
|
50336
51889
|
const providerId = provider.id;
|
|
50337
|
-
const
|
|
51890
|
+
const scan2 = root && providerRootAvailable(root) ? listSkills2({
|
|
50338
51891
|
customDirs: [root],
|
|
50339
51892
|
customProvider: providerId,
|
|
50340
51893
|
scanDefaultDirs: false,
|
|
50341
|
-
all: true
|
|
50342
|
-
|
|
51894
|
+
all: true,
|
|
51895
|
+
...provider.id === "claude-code" ? {} : { claudePluginsFile: SKIP_PLUGINS_PATH, claudePluginsRoot: SKIP_PLUGINS_PATH }
|
|
51896
|
+
}) : Promise.resolve([]);
|
|
50343
51897
|
tasks.push(
|
|
50344
|
-
|
|
51898
|
+
scan2.then((entries) => {
|
|
51899
|
+
counts.set(providerCountKey(workspace.id, providerId), entries.length);
|
|
51900
|
+
const canonicalRoot = root === null ? "" : safeCanonical(root) ?? root;
|
|
51901
|
+
for (const entry of entries) {
|
|
51902
|
+
keySet.add(`${canonicalRoot}${path32.sep}${entry.directoryName}`);
|
|
51903
|
+
}
|
|
51904
|
+
})
|
|
50345
51905
|
);
|
|
50346
51906
|
}
|
|
50347
51907
|
}
|
|
50348
|
-
|
|
50349
|
-
}
|
|
50350
|
-
async function collectWorkspaceSkillKeys(state, listSkills2) {
|
|
50351
|
-
const tasks = [];
|
|
50352
|
-
const workspaces = [
|
|
50353
|
-
{ id: GLOBAL_WORKSPACE_ID, directory: null, global: true },
|
|
50354
|
-
...state.workspaces.map((workspace) => ({
|
|
50355
|
-
id: workspace.id,
|
|
50356
|
-
directory: availableDirectory(workspace.path),
|
|
50357
|
-
global: false
|
|
50358
|
-
}))
|
|
50359
|
-
];
|
|
50360
|
-
for (const workspace of workspaces) {
|
|
50361
|
-
const keySet = /* @__PURE__ */ new Set();
|
|
50362
|
-
tasks.push(listWorkspaceKeys(workspace, listSkills2, keySet));
|
|
50363
|
-
}
|
|
50364
|
-
return new Map(await Promise.all(tasks));
|
|
50365
|
-
}
|
|
50366
|
-
async function listWorkspaceKeys(workspace, listSkills2, keySet) {
|
|
50367
|
-
for (const provider of PROVIDER_CATALOG) {
|
|
50368
|
-
const root = workspace.global ? globalProviderRoot(provider) : workspace.directory ? importedProviderRoot(workspace.directory, provider) : null;
|
|
50369
|
-
if (!root || !providerRootAvailable(root)) continue;
|
|
50370
|
-
const providerId = provider.id;
|
|
50371
|
-
const entries = await listSkills2({
|
|
50372
|
-
customDirs: [root],
|
|
50373
|
-
customProvider: providerId,
|
|
50374
|
-
scanDefaultDirs: false,
|
|
50375
|
-
all: true
|
|
50376
|
-
});
|
|
50377
|
-
const canonicalRoot = safeCanonical(root) ?? root;
|
|
50378
|
-
for (const entry of entries) {
|
|
50379
|
-
keySet.add(`${canonicalRoot}${path26.sep}${entry.directoryName}`);
|
|
50380
|
-
}
|
|
50381
|
-
}
|
|
50382
|
-
return [workspace.id, keySet];
|
|
51908
|
+
await Promise.all(tasks);
|
|
51909
|
+
return { counts, skillKeys };
|
|
50383
51910
|
}
|
|
50384
51911
|
function projectWorkspaceSnapshot(state, counts, skillKeys) {
|
|
50385
51912
|
const global3 = projectGlobalWorkspace(state.activeId, counts, skillKeys);
|
|
@@ -50461,9 +51988,7 @@ function createWorkspaceRegistry(options2 = {}) {
|
|
|
50461
51988
|
const persistence = createWorkspaceRegistryPersistence();
|
|
50462
51989
|
let state = persistence.load();
|
|
50463
51990
|
let revision = 0;
|
|
50464
|
-
const
|
|
50465
|
-
const countSkills = options2.countSkills ?? countCcskiSkills;
|
|
50466
|
-
const listSkills2 = options2.listSkills ?? (usingDefaultCounter ? listCcskiSkills : void 0);
|
|
51991
|
+
const listSkills2 = options2.listSkills ?? listCcskiSkills;
|
|
50467
51992
|
const commit = (next) => {
|
|
50468
51993
|
if (next === state) return;
|
|
50469
51994
|
persistence.commit(next);
|
|
@@ -50476,9 +52001,7 @@ function createWorkspaceRegistry(options2 = {}) {
|
|
|
50476
52001
|
const observedRevision = revision;
|
|
50477
52002
|
const snapshot = state;
|
|
50478
52003
|
try {
|
|
50479
|
-
const counts = await
|
|
50480
|
-
if (revision !== observedRevision) continue;
|
|
50481
|
-
const skillKeys = listSkills2 ? await collectWorkspaceSkillKeys(snapshot, listSkills2) : void 0;
|
|
52004
|
+
const { counts, skillKeys } = await scanWorkspaceSnapshot(snapshot, listSkills2);
|
|
50482
52005
|
if (revision !== observedRevision) continue;
|
|
50483
52006
|
return projectWorkspaceSnapshot(snapshot, counts, skillKeys);
|
|
50484
52007
|
} catch (error) {
|
|
@@ -50487,17 +52010,17 @@ function createWorkspaceRegistry(options2 = {}) {
|
|
|
50487
52010
|
}
|
|
50488
52011
|
},
|
|
50489
52012
|
import(directory, label) {
|
|
50490
|
-
const resolvedPath =
|
|
50491
|
-
if (!
|
|
52013
|
+
const resolvedPath = path33.resolve(directory);
|
|
52014
|
+
if (!fs29.existsSync(resolvedPath)) {
|
|
50492
52015
|
throw new DomainError("NOT_FOUND", "Workspace directory was not found.");
|
|
50493
52016
|
}
|
|
50494
|
-
if (!
|
|
52017
|
+
if (!fs29.statSync(resolvedPath).isDirectory()) {
|
|
50495
52018
|
throw new DomainError("INVALID_OPERATION", "Workspace path must be a directory.");
|
|
50496
52019
|
}
|
|
50497
52020
|
const canonicalPath = canonicalDirectory(resolvedPath);
|
|
50498
52021
|
const entry = {
|
|
50499
52022
|
id: ImportedWorkspaceIdSchema.parse(opaquePathId("ws", canonicalPath)),
|
|
50500
|
-
label: label?.trim() ||
|
|
52023
|
+
label: label?.trim() || path33.basename(canonicalPath) || canonicalPath,
|
|
50501
52024
|
path: canonicalPath
|
|
50502
52025
|
};
|
|
50503
52026
|
const result = importWorkspace(state, entry);
|
|
@@ -50555,12 +52078,9 @@ function createWorkspaceRegistry(options2 = {}) {
|
|
|
50555
52078
|
}
|
|
50556
52079
|
};
|
|
50557
52080
|
}
|
|
50558
|
-
async function countCcskiSkills(options2) {
|
|
50559
|
-
return (await listSkills(options2)).length;
|
|
50560
|
-
}
|
|
50561
52081
|
async function listCcskiSkills(options2) {
|
|
50562
52082
|
const skills = await listSkills(options2);
|
|
50563
|
-
return skills.map((skill) => ({ directoryName:
|
|
52083
|
+
return skills.map((skill) => ({ directoryName: path33.basename(skill.path) }));
|
|
50564
52084
|
}
|
|
50565
52085
|
function providerScope(target, workspaceKind, workspaceLabel, directory, includeDisabled) {
|
|
50566
52086
|
return {
|
|
@@ -50592,6 +52112,7 @@ function createDaemonDomain(workspaces = createWorkspaceRegistry(), options2 = {
|
|
|
50592
52112
|
const modelCatalog = createModelCatalogService();
|
|
50593
52113
|
const agentFiles = createAgentFilesService();
|
|
50594
52114
|
const skillsCliProbe = options2.skillsCliProbe ?? createSkillsCliProbe();
|
|
52115
|
+
void skillsCliProbe.probe().catch(() => void 0);
|
|
50595
52116
|
const skills = createSkillService(workspaces, { skillsCliProbe });
|
|
50596
52117
|
const agentSessions = createAgentSessionsService({
|
|
50597
52118
|
kernel: () => kernelHostRef.handle,
|
|
@@ -50627,6 +52148,8 @@ ${info3.content.slice(0, 2e5)}`
|
|
|
50627
52148
|
const repository = createRepositoryService(workspaces, skills);
|
|
50628
52149
|
const creator = createCreatorService(workspaces, skills);
|
|
50629
52150
|
const skillIntelligence = createSkillIntelligenceService(skills, creator);
|
|
52151
|
+
const searchConfigOpener = options2.searchConfigOpener ?? platformOpenFile;
|
|
52152
|
+
const dialog = options2.dialog ?? createDialogService();
|
|
50630
52153
|
const domain = {
|
|
50631
52154
|
workspaces,
|
|
50632
52155
|
skills,
|
|
@@ -50635,6 +52158,9 @@ ${info3.content.slice(0, 2e5)}`
|
|
|
50635
52158
|
sourceRegistry: createSourceRegistry(),
|
|
50636
52159
|
skillsCliProbe,
|
|
50637
52160
|
skillsUpdate: createSkillsUpdateService(workspaces, skills, skillsCliProbe, repository),
|
|
52161
|
+
skillSearch: createSkillSearchService(),
|
|
52162
|
+
searchConfigOpener,
|
|
52163
|
+
dialog,
|
|
50638
52164
|
acpBridge: createAcpBridgeService(workspaces),
|
|
50639
52165
|
skillIntelligence,
|
|
50640
52166
|
steward: createStewardService(workspaces, skills, skillIntelligence, {
|
|
@@ -50666,7 +52192,7 @@ ${info3.content.slice(0, 2e5)}`
|
|
|
50666
52192
|
}
|
|
50667
52193
|
|
|
50668
52194
|
// src/daemon/ipc-server.ts
|
|
50669
|
-
import
|
|
52195
|
+
import fs31 from "node:fs";
|
|
50670
52196
|
import net2 from "node:net";
|
|
50671
52197
|
|
|
50672
52198
|
// src/shared/socket-liveness.ts
|
|
@@ -50693,10 +52219,10 @@ function socketAcceptsConnections(socketPathname, timeoutMs = DEFAULT_SOCKET_PRO
|
|
|
50693
52219
|
}
|
|
50694
52220
|
|
|
50695
52221
|
// src/daemon/log.ts
|
|
50696
|
-
import
|
|
52222
|
+
import fs30 from "node:fs";
|
|
50697
52223
|
function log(message) {
|
|
50698
52224
|
try {
|
|
50699
|
-
|
|
52225
|
+
fs30.appendFileSync(daemonLogPath(), `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
|
|
50700
52226
|
`);
|
|
50701
52227
|
} catch {
|
|
50702
52228
|
}
|
|
@@ -50723,8 +52249,8 @@ var IpcServer = class {
|
|
|
50723
52249
|
/** 获取单实例 socket,仅清理已确认失活的 Unix 路径。 */
|
|
50724
52250
|
async start() {
|
|
50725
52251
|
const directory = runDir();
|
|
50726
|
-
|
|
50727
|
-
if (process.platform !== "win32")
|
|
52252
|
+
fs31.mkdirSync(directory, { recursive: true, mode: 448 });
|
|
52253
|
+
if (process.platform !== "win32") fs31.chmodSync(directory, 448);
|
|
50728
52254
|
const server = await this.bindSocket(true);
|
|
50729
52255
|
if (!server) return false;
|
|
50730
52256
|
this.server = server;
|
|
@@ -50775,7 +52301,7 @@ var IpcServer = class {
|
|
|
50775
52301
|
}
|
|
50776
52302
|
);
|
|
50777
52303
|
if (result.server) {
|
|
50778
|
-
if (process.platform !== "win32")
|
|
52304
|
+
if (process.platform !== "win32") fs31.chmodSync(socket, 384);
|
|
50779
52305
|
log(`ipc listening on ${socket}`);
|
|
50780
52306
|
return result.server;
|
|
50781
52307
|
}
|
|
@@ -50785,7 +52311,7 @@ var IpcServer = class {
|
|
|
50785
52311
|
}
|
|
50786
52312
|
if (!allowStaleCleanup || await socketAcceptsConnections(socket)) return null;
|
|
50787
52313
|
try {
|
|
50788
|
-
|
|
52314
|
+
fs31.unlinkSync(socket);
|
|
50789
52315
|
} catch (error) {
|
|
50790
52316
|
const code = error instanceof Error && "code" in error ? error.code : void 0;
|
|
50791
52317
|
if (code !== "ENOENT") return null;
|
|
@@ -50866,9 +52392,9 @@ var IpcServer = class {
|
|
|
50866
52392
|
};
|
|
50867
52393
|
|
|
50868
52394
|
// src/daemon/web-server.ts
|
|
50869
|
-
import { existsSync as existsSync2, promises as
|
|
52395
|
+
import { existsSync as existsSync2, promises as fs32 } from "node:fs";
|
|
50870
52396
|
import http from "node:http";
|
|
50871
|
-
import
|
|
52397
|
+
import path34 from "node:path";
|
|
50872
52398
|
import { RPCHandler } from "@orpc/server/ws";
|
|
50873
52399
|
import { WebSocketServer } from "ws";
|
|
50874
52400
|
|
|
@@ -50903,6 +52429,21 @@ function createRpcRouter(deps) {
|
|
|
50903
52429
|
validate: rpc.skills.validate.handler(
|
|
50904
52430
|
async ({ input }) => domain.skills.validate(input, input.skillId)
|
|
50905
52431
|
),
|
|
52432
|
+
// skill-search-integration C1:daemon 长驻检索单例(索引 IO 故障经错误边界透传)。
|
|
52433
|
+
search: rpc.skills.search.handler(async ({ input }) => ({
|
|
52434
|
+
results: await domain.skillSearch.search(input.query, { limit: input.limit ?? 10 })
|
|
52435
|
+
})),
|
|
52436
|
+
// search-robustness R3:在系统默认编辑器打开 server-owned search-config.toml。
|
|
52437
|
+
searchConfig: {
|
|
52438
|
+
open: rpc.skills.searchConfig.open.handler(async () => {
|
|
52439
|
+
await domain.searchConfigOpener(searchConfigPath());
|
|
52440
|
+
return { opened: true };
|
|
52441
|
+
})
|
|
52442
|
+
},
|
|
52443
|
+
// search-duplicates P1:内容重复组投影(索引事实,排序冻结)。
|
|
52444
|
+
duplicates: rpc.skills.duplicates.handler(async () => ({
|
|
52445
|
+
groups: await domain.skillSearch.duplicates()
|
|
52446
|
+
})),
|
|
50906
52447
|
update: {
|
|
50907
52448
|
check: rpc.skills.update.check.handler(async ({ input }) => {
|
|
50908
52449
|
const discovered = await domain.skills.list(input, true);
|
|
@@ -50925,7 +52466,8 @@ function createRpcRouter(deps) {
|
|
|
50925
52466
|
})),
|
|
50926
52467
|
setActive: rpc.workspace.setActive.handler(({ input }) => ({
|
|
50927
52468
|
activeId: domain.workspaces.activate(input.id)
|
|
50928
|
-
}))
|
|
52469
|
+
})),
|
|
52470
|
+
pickDirectory: rpc.workspace.pickDirectory.handler(async () => domain.dialog.pickDirectory())
|
|
50929
52471
|
},
|
|
50930
52472
|
creator: {
|
|
50931
52473
|
save: rpc.creator.save.handler(({ input }) => domain.creator.save(input)),
|
|
@@ -51439,19 +52981,19 @@ var WebServer = class {
|
|
|
51439
52981
|
async serveStatic(response, pathname) {
|
|
51440
52982
|
let relativePath = decodeURIComponent(pathname.slice(1));
|
|
51441
52983
|
if (!relativePath || relativePath.endsWith("/")) relativePath += "index.html";
|
|
51442
|
-
const root =
|
|
51443
|
-
let file =
|
|
51444
|
-
if (
|
|
52984
|
+
const root = path34.resolve(this.options.webuiDir);
|
|
52985
|
+
let file = path34.resolve(root, relativePath);
|
|
52986
|
+
if (path34.relative(root, file).startsWith("..")) {
|
|
51445
52987
|
response.writeHead(403).end();
|
|
51446
52988
|
return;
|
|
51447
52989
|
}
|
|
51448
|
-
if (!existsSync2(file)) file =
|
|
52990
|
+
if (!existsSync2(file)) file = path34.join(root, "index.html");
|
|
51449
52991
|
if (!existsSync2(file)) {
|
|
51450
52992
|
response.writeHead(404).end("not found");
|
|
51451
52993
|
return;
|
|
51452
52994
|
}
|
|
51453
|
-
const extension =
|
|
51454
|
-
const data = await
|
|
52995
|
+
const extension = path34.extname(file);
|
|
52996
|
+
const data = await fs32.readFile(file);
|
|
51455
52997
|
response.writeHead(200, {
|
|
51456
52998
|
"content-type": MIME[extension] ?? "application/octet-stream",
|
|
51457
52999
|
"cache-control": extension === ".html" ? "no-cache" : "public, max-age=3600"
|
|
@@ -51461,7 +53003,7 @@ var WebServer = class {
|
|
|
51461
53003
|
};
|
|
51462
53004
|
|
|
51463
53005
|
// src/daemon/steward/dsh-session-binder.ts
|
|
51464
|
-
import
|
|
53006
|
+
import fs33 from "node:fs/promises";
|
|
51465
53007
|
import {
|
|
51466
53008
|
createAssistantMessage,
|
|
51467
53009
|
createToolResultMessage,
|
|
@@ -51501,7 +53043,7 @@ function createDshSessionBinder(options2) {
|
|
|
51501
53043
|
if (!host) return { ok: false, failure: { kind: "HOST_UNAVAILABLE" } };
|
|
51502
53044
|
const surface = narrowHostSurface(host.ctx);
|
|
51503
53045
|
if ("kind" in surface) return { ok: false, failure: surface };
|
|
51504
|
-
const canonical = await
|
|
53046
|
+
const canonical = await fs33.realpath(input.workspaceDir);
|
|
51505
53047
|
const workspace = await surface.workspaceRegistry.resolveByPath(canonical) ?? await surface.workspaceRegistry.create(
|
|
51506
53048
|
canonical,
|
|
51507
53049
|
input.workspaceTitle ?? canonical.split("/").pop() ?? canonical
|
|
@@ -51643,7 +53185,7 @@ function summarizeToolResult(call) {
|
|
|
51643
53185
|
|
|
51644
53186
|
// src/daemon/mcp/skill-creator-mcp.ts
|
|
51645
53187
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/server";
|
|
51646
|
-
import { z as
|
|
53188
|
+
import { z as z29 } from "zod";
|
|
51647
53189
|
function mcpToolName(capabilityName) {
|
|
51648
53190
|
return capabilityName.replace(/\./g, "_");
|
|
51649
53191
|
}
|
|
@@ -51708,7 +53250,7 @@ function createSkillCreatorMcpServer(deps) {
|
|
|
51708
53250
|
proposeName,
|
|
51709
53251
|
{
|
|
51710
53252
|
description: `Propose: ${descriptor.description} A human approves it in the Skill Creator UI before execution.`,
|
|
51711
|
-
inputSchema:
|
|
53253
|
+
inputSchema: z29.object(shape2)
|
|
51712
53254
|
},
|
|
51713
53255
|
async (args) => proposeResult(deps.proposals.create(descriptor.name, args))
|
|
51714
53256
|
);
|
|
@@ -51730,7 +53272,7 @@ function createSkillCreatorMcpServer(deps) {
|
|
|
51730
53272
|
if (shape) {
|
|
51731
53273
|
server.registerTool(
|
|
51732
53274
|
toolName,
|
|
51733
|
-
{ description: descriptor.description, inputSchema:
|
|
53275
|
+
{ description: descriptor.description, inputSchema: z29.object(shape) },
|
|
51734
53276
|
async (args) => withCard(
|
|
51735
53277
|
descriptor.name,
|
|
51736
53278
|
await deps.capabilities.call(descriptor.name, args, MCP_PRINCIPAL),
|
|
@@ -51785,13 +53327,13 @@ function createSkillCreatorMcpServer(deps) {
|
|
|
51785
53327
|
}
|
|
51786
53328
|
|
|
51787
53329
|
// src/daemon/tray-host.ts
|
|
51788
|
-
import
|
|
51789
|
-
import
|
|
53330
|
+
import fs35 from "node:fs";
|
|
53331
|
+
import path36 from "node:path";
|
|
51790
53332
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
51791
53333
|
|
|
51792
53334
|
// src/daemon/app-icon.ts
|
|
51793
|
-
import
|
|
51794
|
-
import
|
|
53335
|
+
import fs34 from "node:fs";
|
|
53336
|
+
import path35 from "node:path";
|
|
51795
53337
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
51796
53338
|
var LINUX_ICON_SIZES = [16, 32, 48, 64, 128, 256, 512];
|
|
51797
53339
|
function resolveAppIcon(webuiDir, platform2 = process.platform) {
|
|
@@ -51840,7 +53382,7 @@ function resolveAppIcon(webuiDir, platform2 = process.platform) {
|
|
|
51840
53382
|
if (platform2 === "linux") {
|
|
51841
53383
|
const assets = LINUX_ICON_SIZES.flatMap((size) => {
|
|
51842
53384
|
const iconPath = resolvePackagedIconPath(
|
|
51843
|
-
|
|
53385
|
+
path35.join("linux", `${size}x${size}`, "app-icon.png"),
|
|
51844
53386
|
webuiDir
|
|
51845
53387
|
);
|
|
51846
53388
|
return iconPath === null ? [] : [
|
|
@@ -51867,31 +53409,31 @@ function resolveNativeVariantPair(lightFileName, darkFileName, webuiDir) {
|
|
|
51867
53409
|
return [lightPath, darkPath];
|
|
51868
53410
|
}
|
|
51869
53411
|
function resolveNativeAppIconPath(fileName, webuiDir) {
|
|
51870
|
-
const here =
|
|
53412
|
+
const here = path35.dirname(fileURLToPath3(import.meta.url));
|
|
51871
53413
|
const candidates = [
|
|
51872
|
-
webuiDir ?
|
|
51873
|
-
|
|
51874
|
-
|
|
51875
|
-
|
|
51876
|
-
|
|
53414
|
+
webuiDir ? path35.join(webuiDir, "icons", "app-icon", fileName) : null,
|
|
53415
|
+
path35.join(here, "webui", "icons", "app-icon", fileName),
|
|
53416
|
+
path35.join(here, "..", "resources", "app-icon", fileName),
|
|
53417
|
+
path35.join(here, "..", "..", "resources", "app-icon", fileName),
|
|
53418
|
+
path35.join(process.cwd(), "resources", "app-icon", fileName)
|
|
51877
53419
|
];
|
|
51878
53420
|
return firstExistingFile(candidates);
|
|
51879
53421
|
}
|
|
51880
53422
|
function resolvePackagedIconPath(fileName, webuiDir) {
|
|
51881
|
-
const here =
|
|
53423
|
+
const here = path35.dirname(fileURLToPath3(import.meta.url));
|
|
51882
53424
|
const candidates = [
|
|
51883
|
-
webuiDir ?
|
|
51884
|
-
|
|
51885
|
-
|
|
51886
|
-
|
|
51887
|
-
|
|
51888
|
-
|
|
53425
|
+
webuiDir ? path35.join(webuiDir, "icons", fileName) : null,
|
|
53426
|
+
path35.join(here, "webui", "icons", fileName),
|
|
53427
|
+
path35.join(here, "..", "..", "webui", "build", "icons", fileName),
|
|
53428
|
+
path35.join(here, "..", "..", "webui", "static", "icons", fileName),
|
|
53429
|
+
path35.join(process.cwd(), "webui", "build", "icons", fileName),
|
|
53430
|
+
path35.join(process.cwd(), "webui", "static", "icons", fileName)
|
|
51889
53431
|
];
|
|
51890
53432
|
return firstExistingFile(candidates);
|
|
51891
53433
|
}
|
|
51892
53434
|
function firstExistingFile(candidates) {
|
|
51893
53435
|
for (const candidate of candidates) {
|
|
51894
|
-
if (candidate &&
|
|
53436
|
+
if (candidate && fs34.existsSync(candidate)) return candidate;
|
|
51895
53437
|
}
|
|
51896
53438
|
return null;
|
|
51897
53439
|
}
|
|
@@ -52264,17 +53806,17 @@ function resolveTrayIconPath(webuiDir) {
|
|
|
52264
53806
|
return resolvePackagedIconPath2("monochrome-mini.png", webuiDir);
|
|
52265
53807
|
}
|
|
52266
53808
|
function resolvePackagedIconPath2(fileName, webuiDir) {
|
|
52267
|
-
const here =
|
|
53809
|
+
const here = path36.dirname(fileURLToPath4(import.meta.url));
|
|
52268
53810
|
const candidates = [
|
|
52269
|
-
webuiDir ?
|
|
52270
|
-
|
|
52271
|
-
|
|
52272
|
-
|
|
52273
|
-
|
|
52274
|
-
|
|
53811
|
+
webuiDir ? path36.join(webuiDir, "icons", fileName) : null,
|
|
53812
|
+
path36.join(here, "webui", "icons", fileName),
|
|
53813
|
+
path36.join(here, "..", "..", "webui", "build", "icons", fileName),
|
|
53814
|
+
path36.join(here, "..", "..", "webui", "static", "icons", fileName),
|
|
53815
|
+
path36.join(process.cwd(), "webui", "build", "icons", fileName),
|
|
53816
|
+
path36.join(process.cwd(), "webui", "static", "icons", fileName)
|
|
52275
53817
|
];
|
|
52276
53818
|
for (const candidate of candidates) {
|
|
52277
|
-
if (candidate &&
|
|
53819
|
+
if (candidate && fs35.existsSync(candidate)) return candidate;
|
|
52278
53820
|
}
|
|
52279
53821
|
return null;
|
|
52280
53822
|
}
|
|
@@ -52362,7 +53904,7 @@ async function safeCall(label, operation) {
|
|
|
52362
53904
|
}
|
|
52363
53905
|
|
|
52364
53906
|
// src/daemon/index.ts
|
|
52365
|
-
var __dirname =
|
|
53907
|
+
var __dirname = path37.dirname(fileURLToPath5(import.meta.url));
|
|
52366
53908
|
var SHUTDOWN_GRACE_MS = 1e3;
|
|
52367
53909
|
var SHUTDOWN_TASK_TIMEOUT_MS = 2e3;
|
|
52368
53910
|
async function settleTeardown(label, action) {
|
|
@@ -52391,20 +53933,20 @@ async function settleTeardown(label, action) {
|
|
|
52391
53933
|
}
|
|
52392
53934
|
}
|
|
52393
53935
|
function resolveWebuiDir(override) {
|
|
52394
|
-
if (override) return
|
|
53936
|
+
if (override) return path37.resolve(override);
|
|
52395
53937
|
const candidates = [
|
|
52396
|
-
|
|
53938
|
+
path37.join(__dirname, "webui"),
|
|
52397
53939
|
// dist/webui (bundled)
|
|
52398
|
-
|
|
53940
|
+
path37.join(__dirname, "..", "..", "webui", "static"),
|
|
52399
53941
|
// source dev: Vite static assets
|
|
52400
|
-
|
|
53942
|
+
path37.join(__dirname, "..", "..", "webui", "build"),
|
|
52401
53943
|
// source production build
|
|
52402
|
-
|
|
52403
|
-
|
|
52404
|
-
|
|
53944
|
+
path37.join(process.cwd(), "dist", "webui"),
|
|
53945
|
+
path37.join(process.cwd(), "webui", "static"),
|
|
53946
|
+
path37.join(process.cwd(), "webui", "build")
|
|
52405
53947
|
];
|
|
52406
53948
|
for (const c of candidates) {
|
|
52407
|
-
if (
|
|
53949
|
+
if (fs36.existsSync(c)) return c;
|
|
52408
53950
|
}
|
|
52409
53951
|
return candidates[0];
|
|
52410
53952
|
}
|
|
@@ -52529,7 +54071,9 @@ async function bootDaemon(opts) {
|
|
|
52529
54071
|
// ACP 子进程池:有界杀光所有 agent 子进程,避免孤儿进程泄漏。
|
|
52530
54072
|
settleTeardown("acp bridge", () => domain.acpBridge.dispose()),
|
|
52531
54073
|
// steward:取消活动 run、回收隔离 execution root、dispose backend adapter。
|
|
52532
|
-
settleTeardown("steward service", () => domain.steward.dispose())
|
|
54074
|
+
settleTeardown("steward service", () => domain.steward.dispose()),
|
|
54075
|
+
// skill search:关闭 provider root watchers(persistent:false 之外的显式回收)。
|
|
54076
|
+
settleTeardown("skill search", () => domain.skillSearch.dispose())
|
|
52533
54077
|
];
|
|
52534
54078
|
await Promise.allSettled(tasks);
|
|
52535
54079
|
} finally {
|
|
@@ -52589,6 +54133,7 @@ async function bootDaemon(opts) {
|
|
|
52589
54133
|
await Promise.allSettled([settleTeardown("late tray host", async () => host?.destroy())]);
|
|
52590
54134
|
} else {
|
|
52591
54135
|
handlesRef.trayHost = host;
|
|
54136
|
+
if (result.tray) domain.dialog.attach(result.tray);
|
|
52592
54137
|
status.tray = result.window ? "mounted" : webMode && result.tray ? "web" : "headless";
|
|
52593
54138
|
if (result.failure) {
|
|
52594
54139
|
status.trayError = `[${result.failure.kind}@${result.failure.stage}] ${result.failure.cause instanceof Error ? result.failure.cause.message : String(result.failure.cause)}`;
|
|
@@ -52611,21 +54156,21 @@ async function bootDaemon(opts) {
|
|
|
52611
54156
|
}
|
|
52612
54157
|
|
|
52613
54158
|
// src/shared/package-version.ts
|
|
52614
|
-
import
|
|
52615
|
-
import
|
|
54159
|
+
import fs37 from "node:fs";
|
|
54160
|
+
import path38 from "node:path";
|
|
52616
54161
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
52617
|
-
import { z as
|
|
52618
|
-
var PackageVersionSchema =
|
|
54162
|
+
import { z as z30 } from "zod";
|
|
54163
|
+
var PackageVersionSchema = z30.object({ version: z30.string().min(1) });
|
|
52619
54164
|
function readNearestPackageVersion(moduleUrl) {
|
|
52620
|
-
const here =
|
|
54165
|
+
const here = path38.dirname(fileURLToPath6(moduleUrl));
|
|
52621
54166
|
const candidates = [
|
|
52622
|
-
|
|
52623
|
-
|
|
52624
|
-
|
|
54167
|
+
path38.join(here, "package.json"),
|
|
54168
|
+
path38.join(here, "..", "package.json"),
|
|
54169
|
+
path38.join(here, "..", "..", "package.json")
|
|
52625
54170
|
];
|
|
52626
54171
|
for (const candidate of candidates) {
|
|
52627
54172
|
try {
|
|
52628
|
-
const pkg = safeParseJson(
|
|
54173
|
+
const pkg = safeParseJson(fs37.readFileSync(candidate, "utf8"), PackageVersionSchema);
|
|
52629
54174
|
if (pkg) return pkg.version;
|
|
52630
54175
|
} catch {
|
|
52631
54176
|
}
|