@letrquan/book 0.2.0 → 0.4.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3771 -40
- package/README.md +135 -1311
- package/dist/additional-roots-VAWRQ625.js +32 -0
- package/dist/{app-MOXXWEOY.js → app-J7BVPSUH.js} +9648 -5509
- package/dist/app-J7BVPSUH.js.map +1 -0
- package/dist/chunk-2AAOF5B5.js +28 -0
- package/dist/chunk-2AAOF5B5.js.map +1 -0
- package/dist/chunk-32WBCUAN.js +660 -0
- package/dist/chunk-32WBCUAN.js.map +1 -0
- package/dist/chunk-4DKHLZRI.js +120 -0
- package/dist/chunk-4DKHLZRI.js.map +1 -0
- package/dist/{chunk-CAGOB2N4.js → chunk-62EEAUID.js} +6 -4
- package/dist/chunk-62EEAUID.js.map +1 -0
- package/dist/chunk-AHC7OU75.js +1124 -0
- package/dist/chunk-AHC7OU75.js.map +1 -0
- package/dist/{chunk-E75IPST5.js → chunk-AJZWLKLQ.js} +73 -70
- package/dist/chunk-AJZWLKLQ.js.map +1 -0
- package/dist/chunk-B5FC3MYZ.js +395 -0
- package/dist/chunk-B5FC3MYZ.js.map +1 -0
- package/dist/{chunk-KWHQ2XXL.js → chunk-DNFQTSMJ.js} +55 -13
- package/dist/chunk-DNFQTSMJ.js.map +1 -0
- package/dist/{chunk-J7MIZ5CA.js → chunk-E5WLQSKA.js} +719 -89
- package/dist/chunk-E5WLQSKA.js.map +1 -0
- package/dist/chunk-ECLC4CIJ.js +1181 -0
- package/dist/chunk-ECLC4CIJ.js.map +1 -0
- package/dist/chunk-EEX6QNXV.js +127 -0
- package/dist/chunk-EEX6QNXV.js.map +1 -0
- package/dist/chunk-EFXVSQ5J.js +931 -0
- package/dist/chunk-EFXVSQ5J.js.map +1 -0
- package/dist/{chunk-V4BOYR52.js → chunk-ENLB7NYV.js} +1304 -326
- package/dist/chunk-ENLB7NYV.js.map +1 -0
- package/dist/chunk-FPR7SKIE.js +6926 -0
- package/dist/chunk-FPR7SKIE.js.map +1 -0
- package/dist/{chunk-DGMQEI6Y.js → chunk-HFB74CLM.js} +30 -9
- package/dist/chunk-HFB74CLM.js.map +1 -0
- package/dist/{chunk-5RDRAO4B.js → chunk-HOZXKOE3.js} +37 -1
- package/dist/chunk-HOZXKOE3.js.map +1 -0
- package/dist/chunk-IDYWN37T.js +36 -0
- package/dist/chunk-IDYWN37T.js.map +1 -0
- package/dist/chunk-J4F2QLM2.js +273 -0
- package/dist/chunk-J4F2QLM2.js.map +1 -0
- package/dist/chunk-JGF5HLSI.js +1065 -0
- package/dist/chunk-JGF5HLSI.js.map +1 -0
- package/dist/{chunk-XIFFTATU.js → chunk-KDNWMKXV.js} +7166 -4237
- package/dist/chunk-KDNWMKXV.js.map +1 -0
- package/dist/chunk-KXAYUIKP.js +90 -0
- package/dist/chunk-KXAYUIKP.js.map +1 -0
- package/dist/{chunk-3U5IBM24.js → chunk-MEOB2DZA.js} +12 -2
- package/dist/chunk-MEOB2DZA.js.map +1 -0
- package/dist/{chunk-WKBO5R4O.js → chunk-MV5PQNIA.js} +2 -2
- package/dist/chunk-MV5PQNIA.js.map +1 -0
- package/dist/chunk-N2G4Q7ME.js +2672 -0
- package/dist/chunk-N2G4Q7ME.js.map +1 -0
- package/dist/chunk-OJBMDM5H.js +299 -0
- package/dist/chunk-OJBMDM5H.js.map +1 -0
- package/dist/{chunk-URLQAXTR.js → chunk-PTDC7ZUP.js} +2 -2
- package/dist/chunk-PTDC7ZUP.js.map +1 -0
- package/dist/{chunk-574C6MQR.js → chunk-REASC2QG.js} +173 -64
- package/dist/chunk-REASC2QG.js.map +1 -0
- package/dist/{chunk-7N4J557C.js → chunk-RT5SATF7.js} +1 -1
- package/dist/chunk-RT5SATF7.js.map +1 -0
- package/dist/chunk-SBTU5BEC.js +114 -0
- package/dist/chunk-SBTU5BEC.js.map +1 -0
- package/dist/{chunk-5KLVH3PY.js → chunk-SWPDKLNM.js} +5 -3
- package/dist/chunk-SWPDKLNM.js.map +1 -0
- package/dist/chunk-UCFJ43OS.js +520 -0
- package/dist/chunk-UCFJ43OS.js.map +1 -0
- package/dist/{chunk-VZQXX3WW.js → chunk-W7SLYS3M.js} +80 -16
- package/dist/chunk-W7SLYS3M.js.map +1 -0
- package/dist/{chunk-ANSJPYGI.js → chunk-WV3O5PGX.js} +3 -3
- package/dist/chunk-WV3O5PGX.js.map +1 -0
- package/dist/chunk-XHAO3K7I.js +18 -0
- package/dist/chunk-XHAO3K7I.js.map +1 -0
- package/dist/chunk-XJRJVXJR.js +1050 -0
- package/dist/chunk-XJRJVXJR.js.map +1 -0
- package/dist/chunk-YIL6HTJX.js +663 -0
- package/dist/chunk-YIL6HTJX.js.map +1 -0
- package/dist/{chunk-24AXE6SP.js → chunk-ZPSZZUWF.js} +1 -1
- package/dist/chunk-ZPSZZUWF.js.map +1 -0
- package/dist/{command-approvals-6Y57VCWZ.js → command-approvals-ZRY66LCW.js} +7 -5
- package/dist/context-C3RAP6K5.js +41 -0
- package/dist/{hook-approvals-CTUFKEJT.js → hook-approvals-DPQD5H65.js} +6 -5
- package/dist/index.js +947 -203
- package/dist/index.js.map +1 -1
- package/dist/{interactive-assets-CCAAWVKH.js → interactive-assets-73AQO5WP.js} +18 -9
- package/dist/interactive-assets-73AQO5WP.js.map +1 -0
- package/dist/job-runner.js +166 -111
- package/dist/job-runner.js.map +1 -1
- package/dist/job-supervisor.d.ts +2 -0
- package/dist/job-supervisor.js +121 -0
- package/dist/job-supervisor.js.map +1 -0
- package/dist/loader-DQNPDKOX.js +27 -0
- package/dist/{mcp-approvals-WNPH65BK.js → mcp-approvals-27LJYADQ.js} +6 -5
- package/dist/{mcp-config-YIJIES2C.js → mcp-config-GXSODNCM.js} +4 -3
- package/dist/memory-store-I6Z54VUN.js +51 -0
- package/dist/{model-window-store-YFVNXCIP.js → model-window-store-6VKXXTPJ.js} +9 -4
- package/dist/openai-compatible-IPUNBJ53.js +16 -0
- package/dist/permission-approvals-Q5IMFEZI.js +15 -0
- package/dist/permissions-7Y35X6HZ.js +62 -0
- package/dist/provider-approvals-5AGH3RVT.js +76 -0
- package/dist/sandbox-N2RY72GN.js +42 -0
- package/dist/{scrollback-LUKLIBVI.js → scrollback-GD76GNM4.js} +44 -26
- package/dist/scrollback-GD76GNM4.js.map +1 -0
- package/dist/sdk.d.ts +2739 -2069
- package/dist/sdk.js +77 -42
- package/dist/sdk.js.map +1 -1
- package/dist/settings-loader-KDBH45AQ.js +43 -0
- package/dist/settings-loader-KDBH45AQ.js.map +1 -0
- package/dist/{settings-removed-JWWPT4WA.js → settings-removed-3ATWJEF3.js} +2 -2
- package/dist/settings-removed-3ATWJEF3.js.map +1 -0
- package/dist/shell-selection-Z5WORZU4.js +31 -0
- package/dist/shell-selection-Z5WORZU4.js.map +1 -0
- package/package.json +25 -24
- package/dist/app-MOXXWEOY.js.map +0 -1
- package/dist/chunk-24AXE6SP.js.map +0 -1
- package/dist/chunk-3U5IBM24.js.map +0 -1
- package/dist/chunk-574C6MQR.js.map +0 -1
- package/dist/chunk-5GDZ22YP.js +0 -449
- package/dist/chunk-5GDZ22YP.js.map +0 -1
- package/dist/chunk-5KLVH3PY.js.map +0 -1
- package/dist/chunk-5RDRAO4B.js.map +0 -1
- package/dist/chunk-7N4J557C.js.map +0 -1
- package/dist/chunk-A7JLBK2Y.js +0 -3506
- package/dist/chunk-A7JLBK2Y.js.map +0 -1
- package/dist/chunk-ANSJPYGI.js.map +0 -1
- package/dist/chunk-BTZZUCLI.js +0 -264
- package/dist/chunk-BTZZUCLI.js.map +0 -1
- package/dist/chunk-C7D4NJ2D.js +0 -217
- package/dist/chunk-C7D4NJ2D.js.map +0 -1
- package/dist/chunk-CAGOB2N4.js.map +0 -1
- package/dist/chunk-DGMQEI6Y.js.map +0 -1
- package/dist/chunk-E75IPST5.js.map +0 -1
- package/dist/chunk-EYGD6X2T.js +0 -180
- package/dist/chunk-EYGD6X2T.js.map +0 -1
- package/dist/chunk-GTVB4HEM.js +0 -765
- package/dist/chunk-GTVB4HEM.js.map +0 -1
- package/dist/chunk-J7MIZ5CA.js.map +0 -1
- package/dist/chunk-KWHQ2XXL.js.map +0 -1
- package/dist/chunk-L6LK2QIQ.js +0 -657
- package/dist/chunk-L6LK2QIQ.js.map +0 -1
- package/dist/chunk-MAQXCUR4.js +0 -1007
- package/dist/chunk-MAQXCUR4.js.map +0 -1
- package/dist/chunk-S4XL7HOM.js +0 -402
- package/dist/chunk-S4XL7HOM.js.map +0 -1
- package/dist/chunk-URLQAXTR.js.map +0 -1
- package/dist/chunk-V4BOYR52.js.map +0 -1
- package/dist/chunk-VZQXX3WW.js.map +0 -1
- package/dist/chunk-WKBO5R4O.js.map +0 -1
- package/dist/chunk-XIFFTATU.js.map +0 -1
- package/dist/context-ZC5IFEAX.js +0 -25
- package/dist/interactive-assets-CCAAWVKH.js.map +0 -1
- package/dist/loader-4IDS2RIE.js +0 -15
- package/dist/openai-compatible-RHZUBHV6.js +0 -10
- package/dist/permission-approvals-76PVREFD.js +0 -14
- package/dist/permissions-CHCKTFQQ.js +0 -28
- package/dist/sandbox-CNVAQDA7.js +0 -23
- package/dist/scrollback-LUKLIBVI.js.map +0 -1
- package/dist/settings-loader-M2QP5TX2.js +0 -21
- package/dist/shell-selection-V6QAYXPX.js +0 -17
- package/patches/ink+6.8.0.patch +0 -13
- package/scripts/apply-ink-patch.mjs +0 -29
- package/scripts/ink-patch.mjs +0 -59
- package/scripts/verify-ink-patch.mjs +0 -11
- /package/dist/{command-approvals-6Y57VCWZ.js.map → additional-roots-VAWRQ625.js.map} +0 -0
- /package/dist/{context-ZC5IFEAX.js.map → command-approvals-ZRY66LCW.js.map} +0 -0
- /package/dist/{hook-approvals-CTUFKEJT.js.map → context-C3RAP6K5.js.map} +0 -0
- /package/dist/{loader-4IDS2RIE.js.map → hook-approvals-DPQD5H65.js.map} +0 -0
- /package/dist/{mcp-approvals-WNPH65BK.js.map → loader-DQNPDKOX.js.map} +0 -0
- /package/dist/{mcp-config-YIJIES2C.js.map → mcp-approvals-27LJYADQ.js.map} +0 -0
- /package/dist/{model-window-store-YFVNXCIP.js.map → mcp-config-GXSODNCM.js.map} +0 -0
- /package/dist/{openai-compatible-RHZUBHV6.js.map → memory-store-I6Z54VUN.js.map} +0 -0
- /package/dist/{permission-approvals-76PVREFD.js.map → model-window-store-6VKXXTPJ.js.map} +0 -0
- /package/dist/{permissions-CHCKTFQQ.js.map → openai-compatible-IPUNBJ53.js.map} +0 -0
- /package/dist/{sandbox-CNVAQDA7.js.map → permission-approvals-Q5IMFEZI.js.map} +0 -0
- /package/dist/{settings-loader-M2QP5TX2.js.map → permissions-7Y35X6HZ.js.map} +0 -0
- /package/dist/{settings-removed-JWWPT4WA.js.map → provider-approvals-5AGH3RVT.js.map} +0 -0
- /package/dist/{shell-selection-V6QAYXPX.js.map → sandbox-N2RY72GN.js.map} +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// src/frontmatter.ts
|
|
2
|
+
function frontMatterClose(lines) {
|
|
3
|
+
if (lines[0]?.trim() !== "---") return -1;
|
|
4
|
+
return lines.findIndex((line, index) => index > 0 && /^(?:---|\.\.\.)\s*$/.test(line));
|
|
5
|
+
}
|
|
6
|
+
var FRONT_MATTER_KEY = /^[^\s#:-][^:]*:(?:\s|$)/;
|
|
7
|
+
var FRONT_MATTER_STRICT_KEY = /^[a-z_][\w-]*:(?:\s|$)/;
|
|
8
|
+
function markdownContentStart(lines) {
|
|
9
|
+
const close = frontMatterClose(lines);
|
|
10
|
+
if (close < 0) return 0;
|
|
11
|
+
const block = lines.slice(1, close);
|
|
12
|
+
const first = block.find((line) => line.trim().length > 0 && !line.startsWith("#"));
|
|
13
|
+
if (first === void 0 || !FRONT_MATTER_KEY.test(first)) return 0;
|
|
14
|
+
const runs = [[]];
|
|
15
|
+
for (const line of block) {
|
|
16
|
+
if (line.trim().length === 0) runs.push([]);
|
|
17
|
+
else runs[runs.length - 1].push(line);
|
|
18
|
+
}
|
|
19
|
+
for (const [position, run] of runs.filter((lines2) => lines2.length > 0).entries()) {
|
|
20
|
+
const comments = run.some((line) => line.startsWith("#"));
|
|
21
|
+
const strict = comments && (position > 0 || run[0].startsWith("#"));
|
|
22
|
+
const key = strict ? FRONT_MATTER_STRICT_KEY : FRONT_MATTER_KEY;
|
|
23
|
+
let keys = 0;
|
|
24
|
+
for (const line of run) {
|
|
25
|
+
if (line.startsWith("#")) continue;
|
|
26
|
+
if (key.test(line)) keys++;
|
|
27
|
+
else if (!/^\s+\S/.test(line) && !/^-(?:\s|$)/.test(line)) return 0;
|
|
28
|
+
}
|
|
29
|
+
if (comments && keys === 0) return 0;
|
|
30
|
+
}
|
|
31
|
+
return close + 1;
|
|
32
|
+
}
|
|
33
|
+
function parseFrontmatter(raw) {
|
|
34
|
+
const normalized = raw.replace(/\r\n?/g, "\n");
|
|
35
|
+
const lines = normalized.split("\n");
|
|
36
|
+
if (lines[0]?.trim() !== "---") {
|
|
37
|
+
return { body: raw, frontmatter: {} };
|
|
38
|
+
}
|
|
39
|
+
const endIdx = lines.indexOf("---", 1);
|
|
40
|
+
if (endIdx === -1) {
|
|
41
|
+
return { body: raw, frontmatter: {} };
|
|
42
|
+
}
|
|
43
|
+
const fmLines = lines.slice(1, endIdx);
|
|
44
|
+
const body = lines.slice(endIdx + 1).join("\n").trim();
|
|
45
|
+
const frontmatter = {};
|
|
46
|
+
let currentKey = null;
|
|
47
|
+
let currentArray = [];
|
|
48
|
+
for (const line of fmLines) {
|
|
49
|
+
const trimmed = line.trim();
|
|
50
|
+
if (!trimmed) continue;
|
|
51
|
+
const arrayMatch = trimmed.match(/^-\s+(.+)$/);
|
|
52
|
+
if (arrayMatch && currentKey) {
|
|
53
|
+
currentArray.push(arrayMatch[1]);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (currentKey) {
|
|
57
|
+
if (currentArray.length > 0) {
|
|
58
|
+
frontmatter[currentKey] = currentArray;
|
|
59
|
+
}
|
|
60
|
+
currentKey = null;
|
|
61
|
+
currentArray = [];
|
|
62
|
+
}
|
|
63
|
+
const kvMatch = trimmed.match(/^([a-z][a-zA-Z0-9_-]*)\s*:\s*(.*)$/);
|
|
64
|
+
if (kvMatch) {
|
|
65
|
+
const key = kvMatch[1];
|
|
66
|
+
const value = kvMatch[2].trim();
|
|
67
|
+
if (value === "") {
|
|
68
|
+
currentKey = key;
|
|
69
|
+
currentArray = [];
|
|
70
|
+
} else {
|
|
71
|
+
const unquoted = value.replace(/^["'](.*)["']$/, "$1");
|
|
72
|
+
if ((key === "tools" || key === "allowed-tools") && unquoted.startsWith("[") && unquoted.endsWith("]")) {
|
|
73
|
+
frontmatter[key] = unquoted.slice(1, -1).split(",").map((item) => item.trim().replace(/^["'](.*)["']$/, "$1")).filter(Boolean);
|
|
74
|
+
} else {
|
|
75
|
+
frontmatter[key] = unquoted;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (currentKey && currentArray.length > 0) {
|
|
81
|
+
frontmatter[currentKey] = currentArray;
|
|
82
|
+
}
|
|
83
|
+
return { body, frontmatter };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export {
|
|
87
|
+
markdownContentStart,
|
|
88
|
+
parseFrontmatter
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=chunk-KXAYUIKP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/frontmatter.ts"],"sourcesContent":["/**\n * The index of the line that closes the front matter a `---` first line\n * opens: the next `---`, or YAML's `...` document end, trailing spaces\n * allowed. -1 when the first line opens none or nothing closes it.\n * The Markdown outline's rule; parseFrontmatter keeps the exact `---` that\n * command, skill and memory files are written with.\n */\nexport function frontMatterClose(lines: readonly string[]): number {\n if (lines[0]?.trim() !== '---') return -1;\n return lines.findIndex((line, index) => index > 0 && /^(?:---|\\.\\.\\.)\\s*$/.test(line));\n}\n\n// A front-matter key is anything up to a colon: `title:`, `\"quoted key\":`,\n// `my key:`, `$schema:`, `título:`.\nconst FRONT_MATTER_KEY = /^[^\\s#:-][^:]*:(?:\\s|$)/;\n\n// A key a `#` comment may sit beside outside the block's first run: a YAML\n// identifier (`title:`, `allowed-tools:`). Beside a looser label\n// (`Summary: what this covers`) a `#` line is more likely a heading.\nconst FRONT_MATTER_STRICT_KEY = /^[a-z_][\\w-]*:(?:\\s|$)/;\n\n/**\n * Where a Markdown document's content starts: after its front matter, or at\n * line 0. A leading `---` may be a horizontal rule instead, so the lines up to\n * the closing delimiter count as front matter only when they read as YAML:\n * the first line that is not a `#` comment is a `key:` line, and every run of\n * lines between blank lines holds `key:`, indented and `- ` lines. `#` lines\n * are comments among the keys of the first run, as they always were. A `#`\n * line that opens the block, or sits in a later run, is a comment only beside\n * YAML identifier keys (`title:`). Alone in its run, or beside\n * `Summary: what this covers`, it is a heading, and the block is a rule\n * followed by text: dropping a real heading costs more than listing a comment.\n */\nexport function markdownContentStart(lines: readonly string[]): number {\n const close = frontMatterClose(lines);\n if (close < 0) return 0;\n const block = lines.slice(1, close);\n const first = block.find((line) => line.trim().length > 0 && !line.startsWith('#'));\n if (first === undefined || !FRONT_MATTER_KEY.test(first)) return 0;\n const runs: string[][] = [[]];\n for (const line of block) {\n if (line.trim().length === 0) runs.push([]);\n else runs[runs.length - 1].push(line);\n }\n for (const [position, run] of runs.filter((lines) => lines.length > 0).entries()) {\n const comments = run.some((line) => line.startsWith('#'));\n const strict = comments && (position > 0 || run[0].startsWith('#'));\n const key = strict ? FRONT_MATTER_STRICT_KEY : FRONT_MATTER_KEY;\n let keys = 0;\n for (const line of run) {\n if (line.startsWith('#')) continue;\n if (key.test(line)) keys++;\n else if (!/^\\s+\\S/.test(line) && !/^-(?:\\s|$)/.test(line)) return 0;\n }\n if (comments && keys === 0) return 0;\n }\n return close + 1;\n}\n\n/**\n * Minimal YAML frontmatter parser shared by commands, skills, and subagents.\n *\n * Handles string values, array values (lines starting with \"-\"), and quoted\n * values. Returns a {body, frontmatter} pair. If no frontmatter is found\n * (missing opening or closing ---), the entire input is treated as the body.\n */\nexport function parseFrontmatter(raw: string): {\n body: string;\n frontmatter: Record<string, unknown>;\n} {\n // Normalize line endings before looking for the closing delimiter. Skill\n // packages are commonly authored on Windows and use CRLF; comparing a raw\n // `---\\r` line with the delimiter used to make otherwise valid metadata look\n // like an unparsed body.\n const normalized = raw.replace(/\\r\\n?/g, '\\n');\n const lines = normalized.split('\\n');\n // Must start with ---\n if (lines[0]?.trim() !== '---') {\n return { body: raw, frontmatter: {} };\n }\n const endIdx = lines.indexOf('---', 1);\n if (endIdx === -1) {\n // No closing ---; treat entire file as body.\n return { body: raw, frontmatter: {} };\n }\n const fmLines = lines.slice(1, endIdx);\n const body = lines\n .slice(endIdx + 1)\n .join('\\n')\n .trim();\n const frontmatter: Record<string, unknown> = {};\n let currentKey: string | null = null;\n let currentArray: string[] = [];\n\n for (const line of fmLines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n // Array item: \"- value\"\n const arrayMatch = trimmed.match(/^-\\s+(.+)$/);\n if (arrayMatch && currentKey) {\n currentArray.push(arrayMatch[1]);\n continue;\n }\n\n // Flush any pending array.\n if (currentKey) {\n if (currentArray.length > 0) {\n frontmatter[currentKey] = currentArray;\n }\n currentKey = null;\n currentArray = [];\n }\n\n // key: value\n const kvMatch = trimmed.match(/^([a-z][a-zA-Z0-9_-]*)\\s*:\\s*(.*)$/);\n if (kvMatch) {\n const key = kvMatch[1];\n const value = kvMatch[2].trim();\n // Could be the start of an array (value empty or starts the array on next line).\n if (value === '') {\n currentKey = key;\n currentArray = [];\n } else {\n // Unquote if quoted.\n const unquoted = value.replace(/^[\"'](.*)[\"']$/, '$1');\n if (\n (key === 'tools' || key === 'allowed-tools') &&\n unquoted.startsWith('[') &&\n unquoted.endsWith(']')\n ) {\n frontmatter[key] = unquoted\n .slice(1, -1)\n .split(',')\n .map((item) => item.trim().replace(/^[\"'](.*)[\"']$/, '$1'))\n .filter(Boolean);\n } else {\n frontmatter[key] = unquoted;\n }\n }\n }\n }\n // Flush any trailing array.\n if (currentKey && currentArray.length > 0) {\n frontmatter[currentKey] = currentArray;\n }\n\n return { body, frontmatter };\n}\n"],"mappings":"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","names":["lines"]}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createUncGuard
|
|
3
|
+
} from "./chunk-EEX6QNXV.js";
|
|
1
4
|
import {
|
|
2
5
|
resolveBookHome
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZPSZZUWF.js";
|
|
4
7
|
|
|
5
8
|
// src/mcp-config.ts
|
|
6
9
|
import { readFileSync, existsSync } from "fs";
|
|
@@ -121,7 +124,14 @@ function resolveMcpServerList(workspace, options = {}) {
|
|
|
121
124
|
{ path: join(workspace, ".mcp.json"), source: "project" }
|
|
122
125
|
];
|
|
123
126
|
const servers = /* @__PURE__ */ new Map();
|
|
127
|
+
const reachesUnc = createUncGuard();
|
|
124
128
|
for (const { path, source } of sources) {
|
|
129
|
+
if (source === "project" && reachesUnc(path)) {
|
|
130
|
+
if (!reachesUnc(workspace)) {
|
|
131
|
+
onDiagnostic({ level: "warn", message: `Skipped MCP config on a network path: ${path}` });
|
|
132
|
+
}
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
125
135
|
if (!existsSync(path)) continue;
|
|
126
136
|
let parsed;
|
|
127
137
|
try {
|
|
@@ -210,4 +220,4 @@ export {
|
|
|
210
220
|
loadMcpConfig,
|
|
211
221
|
formatMcpServerCommand
|
|
212
222
|
};
|
|
213
|
-
//# sourceMappingURL=chunk-
|
|
223
|
+
//# sourceMappingURL=chunk-MEOB2DZA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp-config.ts"],"sourcesContent":["import { readFileSync, existsSync } from 'fs';\nimport { createUncGuard } from './unc-path.js';\nimport { join } from 'path';\nimport { resolveBookHome } from './book-home.js';\n\nexport type McpTransportType = 'stdio' | 'http' | 'sse';\n\nexport interface McpServerConfig {\n /** Explicit transport. Omitted means stdio when `command` is present. */\n type?: McpTransportType;\n /** Executable for stdio servers. */\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n /** Optional working directory for stdio servers. */\n cwd?: string;\n /** Endpoint for HTTP/SSE servers. */\n url?: string;\n /** Additional request headers for HTTP/SSE servers. Values are never shown in diagnostics. */\n headers?: Record<string, string>;\n}\n\nconst SAFE_SERVER_NAME = /^[A-Za-z0-9_-]+$/;\n\nexport function isValidMcpServerName(name: string): boolean {\n return SAFE_SERVER_NAME.test(name) && !name.includes('__');\n}\n\ninterface McpConfigDocument {\n mcpServers?: Record<string, unknown>;\n}\n\n/** Where a server declaration came from; project declarations require approval. */\nexport type McpServerSource = 'user' | 'project';\n\nexport interface ResolvedMcpServer {\n name: string;\n config: McpServerConfig;\n source: McpServerSource;\n /** Config file that defined (or, on a name collision, last overrode) this server. */\n path: string;\n}\n\nexport interface McpDiagnostic {\n level: 'warn';\n message: string;\n server?: string;\n}\n\nexport type McpDiagnosticSink = (diagnostic: McpDiagnostic) => void;\n\n/** Preserves the historical stderr warning behavior for headless/SDK hosts. */\nexport const consoleMcpDiagnosticSink: McpDiagnosticSink = (diagnostic) => {\n console.warn(`⚠ ${diagnostic.message}`);\n};\n\nexport interface ResolveMcpServersOptions {\n home?: string;\n onDiagnostic?: McpDiagnosticSink;\n /** Environment used for `${VAR}` interpolation; defaults to process.env. */\n env?: NodeJS.ProcessEnv;\n}\n\ninterface ExpandedValue {\n value: string;\n missing: string[];\n}\n\n/**\n * Expand the small interpolation language used by the de-facto mcpServers\n * config format. We deliberately support only `${NAME}` and `${NAME:-default}`\n * here; shell syntax is never evaluated.\n *\n * A project `.mcp.json` is repository-controlled, and its `env`, `headers`, `url` and `command`\n * can name any variable of this process (#418 audit). The expansion only builds a config in\n * memory: it reaches a child process or a request only for a server `mcp-approvals.ts` approved\n * against a fingerprint of the expanded values, `connectMcpServers` skips project servers it was\n * given no approved list for, and every disclosure of a config is redacted.\n */\nfunction expandValue(raw: string, env: NodeJS.ProcessEnv): ExpandedValue {\n const missing = new Set<string>();\n const value = raw.replace(\n /\\$\\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\\}/g,\n (_match, name: string, fallback?: string) => {\n const configured = env[name];\n if (configured !== undefined) return configured;\n if (fallback !== undefined) return fallback;\n missing.add(name);\n return '';\n },\n );\n return { value, missing: [...missing] };\n}\n\nfunction expandRecord(\n raw: unknown,\n env: NodeJS.ProcessEnv,\n): { value: Record<string, string>; missing: string[]; invalid: string[] } {\n const value: Record<string, string> = {};\n const missing = new Set<string>();\n const invalid: string[] = [];\n if (raw === undefined) return { value, missing: [], invalid };\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n return { value, missing: [], invalid: ['expected an object'] };\n }\n for (const [key, item] of Object.entries(raw as Record<string, unknown>)) {\n if (typeof item !== 'string') {\n invalid.push(`${key} must be a string`);\n continue;\n }\n const expanded = expandValue(item, env);\n value[key] = expanded.value;\n for (const name of expanded.missing) missing.add(name);\n }\n return { value, missing: [...missing], invalid };\n}\n\nfunction normalizeServerConfig(\n name: string,\n raw: unknown,\n env: NodeJS.ProcessEnv,\n): { config?: McpServerConfig; error?: string } {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n return { error: 'expected an object' };\n }\n const source = raw as Record<string, unknown>;\n const requestedType = source.type ?? source.transport;\n const type =\n requestedType === undefined\n ? source.url !== undefined\n ? 'http'\n : 'stdio'\n : String(requestedType).toLowerCase();\n if (!['stdio', 'http', 'sse'].includes(type)) {\n return { error: `unsupported transport type \"${String(requestedType)}\"` };\n }\n\n const commandExpanded =\n typeof source.command === 'string' ? expandValue(source.command, env) : undefined;\n const urlExpanded = typeof source.url === 'string' ? expandValue(source.url, env) : undefined;\n const missing = new Set([...(commandExpanded?.missing ?? []), ...(urlExpanded?.missing ?? [])]);\n const args: string[] = [];\n if (source.args !== undefined) {\n if (!Array.isArray(source.args)) return { error: 'args must be an array' };\n for (const [index, item] of source.args.entries()) {\n if (typeof item !== 'string') return { error: `args[${index}] must be a string` };\n const expanded = expandValue(item, env);\n args.push(expanded.value);\n for (const variable of expanded.missing) missing.add(variable);\n }\n }\n const envValues = expandRecord(source.env, env);\n const headerValues = expandRecord(source.headers, env);\n for (const variable of [...envValues.missing, ...headerValues.missing]) missing.add(variable);\n if (envValues.invalid.length > 0) return { error: `env ${envValues.invalid.join(', ')}` };\n if (headerValues.invalid.length > 0)\n return { error: `headers ${headerValues.invalid.join(', ')}` };\n if (missing.size > 0)\n return { error: `missing environment variable(s): ${[...missing].join(', ')}` };\n\n if (type === 'stdio') {\n if (!commandExpanded?.value.trim())\n return { error: 'stdio server requires a non-empty command' };\n const cwd = typeof source.cwd === 'string' ? expandValue(source.cwd, env) : undefined;\n if (cwd?.missing.length)\n return { error: `missing environment variable(s): ${cwd.missing.join(', ')}` };\n return {\n config: {\n type: 'stdio',\n command: commandExpanded.value,\n args,\n env: envValues.value,\n ...(cwd?.value ? { cwd: cwd.value } : {}),\n },\n };\n }\n\n if (!urlExpanded?.value.trim()) return { error: `${type} server requires a non-empty url` };\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(urlExpanded.value);\n } catch {\n return { error: 'url must be a valid absolute URL' };\n }\n if (!['http:', 'https:'].includes(parsedUrl.protocol)) {\n return { error: 'url must use http:// or https://' };\n }\n if (parsedUrl.username || parsedUrl.password)\n return { error: 'url must not contain credentials' };\n return {\n config: {\n type: type as 'http' | 'sse',\n url: parsedUrl.toString(),\n headers: headerValues.value,\n },\n };\n}\n\n/**\n * Resolve the declared MCP servers with provenance.\n *\n * Sources, later wins on a name collision: <BOOK_HOME>/.book/mcp.json (user),\n * then <workspace>/.mcp.json (project). A project entry that overrides a\n * user-global name keeps `source: 'project'` so it cannot inherit the trust of\n * the name it shadows.\n */\nexport function resolveMcpServerList(\n workspace: string,\n options: ResolveMcpServersOptions = {},\n): ResolvedMcpServer[] {\n const onDiagnostic = options.onDiagnostic ?? consoleMcpDiagnosticSink;\n const env = options.env ?? process.env;\n const bookHome = options.home ? join(options.home, '.book') : resolveBookHome();\n const sources: Array<{ path: string; source: McpServerSource }> = [\n { path: join(bookHome, 'mcp.json'), source: 'user' },\n { path: join(workspace, '.mcp.json'), source: 'project' },\n ];\n const servers = new Map<string, ResolvedMcpServer>();\n const reachesUnc = createUncGuard();\n\n for (const { path, source } of sources) {\n // A project file on a network (UNC) path is not opened (#579): it counts as absent. Said\n // only of a link out of a local workspace: one on a share loads no project file at all.\n if (source === 'project' && reachesUnc(path)) {\n if (!reachesUnc(workspace)) {\n onDiagnostic({ level: 'warn', message: `Skipped MCP config on a network path: ${path}` });\n }\n continue;\n }\n if (!existsSync(path)) continue;\n let parsed: McpConfigDocument;\n try {\n parsed = JSON.parse(readFileSync(path, 'utf-8')) as McpConfigDocument;\n } catch {\n onDiagnostic({ level: 'warn', message: `Failed to load MCP config: ${path}` });\n continue;\n }\n if (!parsed || typeof parsed !== 'object' || !parsed.mcpServers) continue;\n if (typeof parsed.mcpServers !== 'object' || Array.isArray(parsed.mcpServers)) {\n onDiagnostic({ level: 'warn', message: `Invalid mcpServers object: ${path}` });\n continue;\n }\n for (const [name, rawConfig] of Object.entries(parsed.mcpServers)) {\n if (!isValidMcpServerName(name)) {\n onDiagnostic({\n level: 'warn',\n message: `Ignoring invalid MCP server name: ${name} (${path}); use letters, numbers, hyphens, or underscores, without a double underscore`,\n server: name,\n });\n continue;\n }\n const normalized = normalizeServerConfig(name, rawConfig, env);\n if (!normalized.config) {\n onDiagnostic({\n level: 'warn',\n message: `Ignoring invalid MCP server config: ${name} (${path}): ${normalized.error ?? 'invalid configuration'}`,\n server: name,\n });\n continue;\n }\n servers.set(name, {\n name,\n source,\n path,\n config: normalized.config,\n });\n }\n }\n\n return [...servers.values()];\n}\n\n/** Merged name → config view used by doctor and ambient-run fingerprinting. */\nexport function loadMcpConfig(workspace: string, home?: string): Record<string, McpServerConfig> {\n const servers: Record<string, McpServerConfig> = {};\n for (const server of resolveMcpServerList(workspace, { home })) {\n servers[server.name] = server.config;\n }\n return servers;\n}\n\n/** Render one server as the exact command line a host is being asked to trust. */\nexport function formatMcpServerCommand(config: McpServerConfig): string {\n const sensitiveValues = [\n ...Object.values(config.env ?? {}),\n ...Object.values(config.headers ?? {}),\n ]\n .filter((value) => value.length >= 4)\n .sort((left, right) => right.length - left.length);\n const redact = (value: string): string => {\n let rendered = value;\n for (const sensitive of sensitiveValues)\n rendered = rendered.replaceAll(sensitive, '<redacted>');\n return rendered;\n };\n if (config.type === 'http' || config.type === 'sse' || (!config.command && config.url)) {\n const headers = Object.keys(config.headers ?? {}).sort();\n let endpoint = config.url ?? '';\n let queryConfigured = false;\n try {\n const url = new URL(endpoint);\n queryConfigured = Boolean(url.search);\n url.search = '';\n url.hash = '';\n endpoint = url.toString();\n } catch {\n endpoint = redact(endpoint);\n }\n const metadata = [\n ...(queryConfigured ? ['query configured'] : []),\n ...(headers.length ? [`headers: ${headers.join(', ')}`] : []),\n ];\n return `${config.type === 'sse' ? 'sse' : 'http'} ${endpoint}${metadata.length ? ` (${metadata.join('; ')})` : ''}`;\n }\n return [config.command ?? '', ...(config.args ?? [])].filter(Boolean).map(redact).join(' ');\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
updateWorkspaceTrust
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-HFB74CLM.js";
|
|
4
4
|
|
|
5
5
|
// src/permission-approvals.ts
|
|
6
6
|
function evaluateProjectAllowRule(store, rule) {
|
|
@@ -31,4 +31,4 @@ export {
|
|
|
31
31
|
partitionProjectAllowRules,
|
|
32
32
|
persistProjectAllowRuleChoice
|
|
33
33
|
};
|
|
34
|
-
//# sourceMappingURL=chunk-
|
|
34
|
+
//# sourceMappingURL=chunk-MV5PQNIA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/permission-approvals.ts"],"sourcesContent":["/**\n * Trust decisions for permission allow-rules declared by a repository.\n *\n * `permissions.allow` entries accumulate across settings layers, so a rule in a\n * cloned repository's checked-in `.book/settings.json` would otherwise widen the\n * effective allow list — reaching the same outcome as the `bypassPermissions`\n * default mode that project layers are already forbidden to select. Once merged\n * a rule carries no provenance, so nothing downstream can tell a repository's\n * grant from the user's own.\n *\n * Project-declared allow rules therefore require a one-time per-workspace\n * decision, keyed by the exact rule text and recorded in the user-global trust\n * store, outside the workspace, where nothing the repository ships can reach it\n * (see `workspace-trust.ts`).\n *\n * `ask` and `deny` need no gate: they only ever restrict.\n */\nimport type { ProjectAllowRuleChoice } from './settings.js';\nimport { updateWorkspaceTrust } from './workspace-trust.js';\n\n/** Recorded decisions, keyed by the exact rule text. */\nexport type ProjectAllowRuleStore = Record<string, ProjectAllowRuleChoice>;\n\nexport type ProjectAllowRuleState = ProjectAllowRuleChoice | 'unknown';\n\nexport function evaluateProjectAllowRule(\n store: ProjectAllowRuleStore | undefined,\n rule: string,\n): ProjectAllowRuleState {\n return store?.[rule] ?? 'unknown';\n}\n\nexport interface ProjectAllowRulePartition {\n /** Approved by the user: these join the resolved allow list. */\n approved: string[];\n /** No decision recorded: withheld until the user makes one. */\n pending: string[];\n /** Explicitly refused: withheld, and not re-offered. */\n rejected: string[];\n}\n\nexport function partitionProjectAllowRules(\n rules: readonly string[],\n store: ProjectAllowRuleStore | undefined,\n): ProjectAllowRulePartition {\n const partition: ProjectAllowRulePartition = { approved: [], pending: [], rejected: [] };\n for (const rule of rules) {\n const state = evaluateProjectAllowRule(store, rule);\n if (state === 'approved') partition.approved.push(rule);\n else if (state === 'rejected') partition.rejected.push(rule);\n else partition.pending.push(rule);\n }\n return partition;\n}\n\n/**\n * Record an approve/reject decision for one rule, leaving every other recorded\n * decision — in this workspace and in every other — untouched.\n */\nexport function persistProjectAllowRuleChoice(\n workspace: string,\n rule: string,\n choice: ProjectAllowRuleChoice,\n options: { trustStorePath?: string } = {},\n): { ok: boolean; error?: string } {\n return updateWorkspaceTrust(\n workspace,\n (trust) => {\n trust.permissionAllowRules[rule] = choice;\n },\n options.trustStorePath,\n );\n}\n"],"mappings":";;;;;AAyBO,SAAS,yBACd,OACA,MACuB;AACvB,SAAO,QAAQ,IAAI,KAAK;AAC1B;AAWO,SAAS,2BACd,OACA,OAC2B;AAC3B,QAAM,YAAuC,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC,GAAG,UAAU,CAAC,EAAE;AACvF,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,yBAAyB,OAAO,IAAI;AAClD,QAAI,UAAU,WAAY,WAAU,SAAS,KAAK,IAAI;AAAA,aAC7C,UAAU,WAAY,WAAU,SAAS,KAAK,IAAI;AAAA,QACtD,WAAU,QAAQ,KAAK,IAAI;AAAA,EAClC;AACA,SAAO;AACT;AAMO,SAAS,8BACd,WACA,MACA,QACA,UAAuC,CAAC,GACP;AACjC,SAAO;AAAA,IACL;AAAA,IACA,CAAC,UAAU;AACT,YAAM,qBAAqB,IAAI,IAAI;AAAA,IACrC;AAAA,IACA,QAAQ;AAAA,EACV;AACF;","names":[]}
|