@agimon-ai/po-core 0.0.0-alpha.21 → 0.0.0-alpha.23
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/dist/apiContracts.cjs +10 -0
- package/dist/apiContracts.cjs.map +1 -1
- package/dist/apiContracts.mjs +10 -0
- package/dist/apiContracts.mjs.map +1 -1
- package/dist/headlessServer.cjs +12 -84
- package/dist/headlessServer.cjs.map +1 -1
- package/dist/headlessServer.d.mts.map +1 -1
- package/dist/headlessServer.mjs +13 -85
- package/dist/headlessServer.mjs.map +1 -1
- package/dist/mcpProjection.cjs +1 -0
- package/dist/mcpProjection.d.mts +2 -2
- package/dist/mcpProjection.mjs +1 -1
- package/dist/native.cjs +0 -1
- package/dist/native.d.mts +3 -3
- package/dist/native.mjs +2 -2
- package/dist/packageApiServer.cjs +39 -4
- package/dist/packageApiServer.cjs.map +1 -1
- package/dist/packageApiServer.d.mts.map +1 -1
- package/dist/packageApiServer.mjs +34 -1
- package/dist/packageApiServer.mjs.map +1 -1
- package/dist/platform.cjs +3 -0
- package/dist/platform.d.mts +2 -1
- package/dist/platform.mjs +2 -1
- package/dist/src/constants/sessionFiles.cjs +3 -0
- package/dist/src/constants/sessionFiles.cjs.map +1 -1
- package/dist/src/constants/sessionFiles.mjs +3 -1
- package/dist/src/constants/sessionFiles.mjs.map +1 -1
- package/dist/src/schemas/httpApiContracts.cjs +10 -0
- package/dist/src/schemas/httpApiContracts.cjs.map +1 -1
- package/dist/src/schemas/httpApiContracts.d.mts.map +1 -1
- package/dist/src/schemas/httpApiContracts.mjs +10 -0
- package/dist/src/schemas/httpApiContracts.mjs.map +1 -1
- package/dist/src/schemas/mcpProjection.cjs +11 -1
- package/dist/src/schemas/mcpProjection.cjs.map +1 -1
- package/dist/src/schemas/mcpProjection.d.mts +8 -3
- package/dist/src/schemas/mcpProjection.d.mts.map +1 -1
- package/dist/src/schemas/mcpProjection.mjs +11 -2
- package/dist/src/schemas/mcpProjection.mjs.map +1 -1
- package/dist/src/schemas/mcpSession.d.mts +1 -1
- package/dist/src/services/fileAccess/accessibleFile.cjs +101 -0
- package/dist/src/services/fileAccess/accessibleFile.cjs.map +1 -0
- package/dist/src/services/fileAccess/accessibleFile.mjs +98 -0
- package/dist/src/services/fileAccess/accessibleFile.mjs.map +1 -0
- package/dist/src/services/fileAccess/index.cjs +34 -0
- package/dist/src/services/fileAccess/index.cjs.map +1 -0
- package/dist/src/services/fileAccess/index.d.mts +22 -0
- package/dist/src/services/fileAccess/index.d.mts.map +1 -0
- package/dist/src/services/fileAccess/index.mjs +29 -0
- package/dist/src/services/fileAccess/index.mjs.map +1 -0
- package/dist/src/web/types/webPlugin.d.mts +13 -0
- package/dist/src/web/types/webPlugin.d.mts.map +1 -1
- package/dist/web.d.mts +2 -2
- package/dist/webCompositions.cjs +43 -2
- package/dist/webCompositions.cjs.map +1 -1
- package/dist/webCompositions.d.mts +22 -0
- package/dist/webCompositions.d.mts.map +1 -1
- package/dist/webCompositions.mjs +43 -2
- package/dist/webCompositions.mjs.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_sessionFiles = require("../../constants/sessionFiles.cjs");
|
|
3
|
+
let node_fs = require("node:fs");
|
|
4
|
+
node_fs = require_runtime.__toESM(node_fs, 1);
|
|
5
|
+
let node_path = require("node:path");
|
|
6
|
+
node_path = require_runtime.__toESM(node_path, 1);
|
|
7
|
+
//#region src/services/fileAccess/accessibleFile.ts
|
|
8
|
+
function inside(root, filePath) {
|
|
9
|
+
const contained = node_path.default.relative(root, filePath);
|
|
10
|
+
return contained !== ".." && !contained.startsWith(`..${node_path.default.sep}`) && !node_path.default.isAbsolute(contained);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Reads one file under an open folder, with byte ranges and a version the reader can pin. A relative
|
|
14
|
+
* path resolves against `cwd`. Both the file and each root are resolved through symlinks first, so a
|
|
15
|
+
* link inside an open folder cannot reach outside it.
|
|
16
|
+
*/
|
|
17
|
+
async function readAccessibleFile(input) {
|
|
18
|
+
const { range, expectedVersion } = input;
|
|
19
|
+
if (!input.path || input.path.includes("\0")) return Response.json({ error: "Invalid session file path." }, { status: 400 });
|
|
20
|
+
let filePath;
|
|
21
|
+
try {
|
|
22
|
+
filePath = await node_fs.default.promises.realpath(node_path.default.resolve(input.cwd, input.path));
|
|
23
|
+
} catch {
|
|
24
|
+
return Response.json({ error: "Session file not found." }, { status: 404 });
|
|
25
|
+
}
|
|
26
|
+
if (!(await Promise.all(input.roots.map((root) => node_fs.default.promises.realpath(root).catch(() => void 0)))).some((root) => root !== void 0 && inside(root, filePath))) return Response.json({ error: "Session file is outside the folders this session may open." }, { status: 403 });
|
|
27
|
+
let handle;
|
|
28
|
+
try {
|
|
29
|
+
handle = await node_fs.default.promises.open(filePath, "r");
|
|
30
|
+
} catch {
|
|
31
|
+
return Response.json({ error: "Session file not found." }, { status: 404 });
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const stat = await handle.stat();
|
|
35
|
+
if (!stat.isFile()) return Response.json({ error: "Session file not found." }, { status: 404 });
|
|
36
|
+
if (stat.size > 26214400) return Response.json({ error: "Session file is too large." }, { status: 413 });
|
|
37
|
+
const version = `"${stat.dev}-${stat.ino}-${stat.size}-${stat.mtimeMs}-${stat.ctimeMs}"`;
|
|
38
|
+
const headers = new Headers({
|
|
39
|
+
"content-type": require_sessionFiles.SESSION_FILE_CONTENT_TYPES[node_path.default.extname(filePath).toLowerCase()] ?? "application/octet-stream",
|
|
40
|
+
"cache-control": "no-store",
|
|
41
|
+
"x-content-type-options": "nosniff",
|
|
42
|
+
"accept-ranges": "bytes",
|
|
43
|
+
etag: version
|
|
44
|
+
});
|
|
45
|
+
if (expectedVersion !== void 0 && expectedVersion !== version) return new Response(null, {
|
|
46
|
+
status: 412,
|
|
47
|
+
headers
|
|
48
|
+
});
|
|
49
|
+
let start = 0;
|
|
50
|
+
let end = stat.size - 1;
|
|
51
|
+
if (range !== void 0) {
|
|
52
|
+
const match = /^bytes=(\d*)-(\d*)$/u.exec(range);
|
|
53
|
+
if (!match || !match[1] && !match[2] || [match[1], match[2]].some((value) => value !== "" && !Number.isSafeInteger(Number(value))) || stat.size === 0) {
|
|
54
|
+
headers.set("content-range", `bytes */${stat.size}`);
|
|
55
|
+
return new Response(null, {
|
|
56
|
+
status: 416,
|
|
57
|
+
headers
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (!match[1]) start = Math.max(0, stat.size - Number(match[2]));
|
|
61
|
+
else {
|
|
62
|
+
start = Number(match[1]);
|
|
63
|
+
if (match[2]) end = Math.min(Number(match[2]), end);
|
|
64
|
+
}
|
|
65
|
+
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || start > end) {
|
|
66
|
+
headers.set("content-range", `bytes */${stat.size}`);
|
|
67
|
+
return new Response(null, {
|
|
68
|
+
status: 416,
|
|
69
|
+
headers
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
headers.set("content-range", `bytes ${start}-${end}/${stat.size}`);
|
|
73
|
+
}
|
|
74
|
+
const bytes = new Uint8Array(Math.max(0, end - start + 1));
|
|
75
|
+
let read = 0;
|
|
76
|
+
while (read < bytes.length) {
|
|
77
|
+
const part = await handle.read(bytes, read, bytes.length - read, start + read);
|
|
78
|
+
if (part.bytesRead === 0) return new Response(null, {
|
|
79
|
+
status: 412,
|
|
80
|
+
headers
|
|
81
|
+
});
|
|
82
|
+
read += part.bytesRead;
|
|
83
|
+
}
|
|
84
|
+
const after = await handle.stat();
|
|
85
|
+
if (after.size !== stat.size || after.mtimeMs !== stat.mtimeMs || after.ctimeMs !== stat.ctimeMs) return new Response(null, {
|
|
86
|
+
status: 412,
|
|
87
|
+
headers
|
|
88
|
+
});
|
|
89
|
+
headers.set("content-length", String(bytes.length));
|
|
90
|
+
return new Response(bytes, {
|
|
91
|
+
status: range === void 0 ? 200 : 206,
|
|
92
|
+
headers
|
|
93
|
+
});
|
|
94
|
+
} finally {
|
|
95
|
+
await handle.close();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
exports.readAccessibleFile = readAccessibleFile;
|
|
100
|
+
|
|
101
|
+
//# sourceMappingURL=accessibleFile.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibleFile.cjs","names":["path","fs","SESSION_FILE_CONTENT_TYPES"],"sources":["../../../../src/services/fileAccess/accessibleFile.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n MAX_SESSION_FILE_BYTES,\n SESSION_FILE_CONTENT_TYPES,\n SESSION_FILE_DEFAULT_CONTENT_TYPE,\n} from '../../constants/sessionFiles';\n\nfunction inside(root: string, filePath: string): boolean {\n const contained = path.relative(root, filePath);\n return contained !== '..' && !contained.startsWith(`..${path.sep}`) && !path.isAbsolute(contained);\n}\n\n/**\n * Reads one file under an open folder, with byte ranges and a version the reader can pin. A relative\n * path resolves against `cwd`. Both the file and each root are resolved through symlinks first, so a\n * link inside an open folder cannot reach outside it.\n */\nexport async function readAccessibleFile(input: {\n roots: readonly string[];\n cwd: string;\n path: string | null;\n range?: string;\n expectedVersion?: string;\n}): Promise<Response> {\n const { range, expectedVersion } = input;\n if (!input.path || input.path.includes('\\0'))\n return Response.json({ error: 'Invalid session file path.' }, { status: 400 });\n let filePath: string;\n try {\n filePath = await fs.promises.realpath(path.resolve(input.cwd, input.path));\n } catch {\n return Response.json({ error: 'Session file not found.' }, { status: 404 });\n }\n // A root that no longer exists opens nothing.\n const roots = await Promise.all(input.roots.map((root) => fs.promises.realpath(root).catch(() => undefined)));\n if (!roots.some((root) => root !== undefined && inside(root, filePath)))\n return Response.json({ error: 'Session file is outside the folders this session may open.' }, { status: 403 });\n let handle: fs.promises.FileHandle;\n try {\n handle = await fs.promises.open(filePath, 'r');\n } catch {\n return Response.json({ error: 'Session file not found.' }, { status: 404 });\n }\n try {\n const stat = await handle.stat();\n if (!stat.isFile()) return Response.json({ error: 'Session file not found.' }, { status: 404 });\n if (stat.size > MAX_SESSION_FILE_BYTES)\n return Response.json({ error: 'Session file is too large.' }, { status: 413 });\n const version = `\"${stat.dev}-${stat.ino}-${stat.size}-${stat.mtimeMs}-${stat.ctimeMs}\"`;\n const headers = new Headers({\n 'content-type':\n SESSION_FILE_CONTENT_TYPES[path.extname(filePath).toLowerCase()] ?? SESSION_FILE_DEFAULT_CONTENT_TYPE,\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n 'accept-ranges': 'bytes',\n etag: version,\n });\n if (expectedVersion !== undefined && expectedVersion !== version)\n return new Response(null, { status: 412, headers });\n let start = 0;\n let end = stat.size - 1;\n if (range !== undefined) {\n const match = /^bytes=(\\d*)-(\\d*)$/u.exec(range);\n if (\n !match ||\n (!match[1] && !match[2]) ||\n [match[1], match[2]].some((value) => value !== '' && !Number.isSafeInteger(Number(value))) ||\n stat.size === 0\n ) {\n headers.set('content-range', `bytes */${stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n if (!match[1]) start = Math.max(0, stat.size - Number(match[2]));\n else {\n start = Number(match[1]);\n if (match[2]) end = Math.min(Number(match[2]), end);\n }\n if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || start > end) {\n headers.set('content-range', `bytes */${stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n headers.set('content-range', `bytes ${start}-${end}/${stat.size}`);\n }\n const bytes = new Uint8Array(Math.max(0, end - start + 1));\n let read = 0;\n while (read < bytes.length) {\n const part = await handle.read(bytes, read, bytes.length - read, start + read);\n if (part.bytesRead === 0) return new Response(null, { status: 412, headers });\n read += part.bytesRead;\n }\n const after = await handle.stat();\n if (after.size !== stat.size || after.mtimeMs !== stat.mtimeMs || after.ctimeMs !== stat.ctimeMs)\n return new Response(null, { status: 412, headers });\n headers.set('content-length', String(bytes.length));\n return new Response(bytes, { status: range === undefined ? 200 : 206, headers });\n } finally {\n await handle.close();\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { SESSION_FILE_CONTENT_TYPES } from "../../constants/sessionFiles.mjs";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/services/fileAccess/accessibleFile.ts
|
|
5
|
+
function inside(root, filePath) {
|
|
6
|
+
const contained = path.relative(root, filePath);
|
|
7
|
+
return contained !== ".." && !contained.startsWith(`..${path.sep}`) && !path.isAbsolute(contained);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Reads one file under an open folder, with byte ranges and a version the reader can pin. A relative
|
|
11
|
+
* path resolves against `cwd`. Both the file and each root are resolved through symlinks first, so a
|
|
12
|
+
* link inside an open folder cannot reach outside it.
|
|
13
|
+
*/
|
|
14
|
+
async function readAccessibleFile(input) {
|
|
15
|
+
const { range, expectedVersion } = input;
|
|
16
|
+
if (!input.path || input.path.includes("\0")) return Response.json({ error: "Invalid session file path." }, { status: 400 });
|
|
17
|
+
let filePath;
|
|
18
|
+
try {
|
|
19
|
+
filePath = await fs.promises.realpath(path.resolve(input.cwd, input.path));
|
|
20
|
+
} catch {
|
|
21
|
+
return Response.json({ error: "Session file not found." }, { status: 404 });
|
|
22
|
+
}
|
|
23
|
+
if (!(await Promise.all(input.roots.map((root) => fs.promises.realpath(root).catch(() => void 0)))).some((root) => root !== void 0 && inside(root, filePath))) return Response.json({ error: "Session file is outside the folders this session may open." }, { status: 403 });
|
|
24
|
+
let handle;
|
|
25
|
+
try {
|
|
26
|
+
handle = await fs.promises.open(filePath, "r");
|
|
27
|
+
} catch {
|
|
28
|
+
return Response.json({ error: "Session file not found." }, { status: 404 });
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const stat = await handle.stat();
|
|
32
|
+
if (!stat.isFile()) return Response.json({ error: "Session file not found." }, { status: 404 });
|
|
33
|
+
if (stat.size > 26214400) return Response.json({ error: "Session file is too large." }, { status: 413 });
|
|
34
|
+
const version = `"${stat.dev}-${stat.ino}-${stat.size}-${stat.mtimeMs}-${stat.ctimeMs}"`;
|
|
35
|
+
const headers = new Headers({
|
|
36
|
+
"content-type": SESSION_FILE_CONTENT_TYPES[path.extname(filePath).toLowerCase()] ?? "application/octet-stream",
|
|
37
|
+
"cache-control": "no-store",
|
|
38
|
+
"x-content-type-options": "nosniff",
|
|
39
|
+
"accept-ranges": "bytes",
|
|
40
|
+
etag: version
|
|
41
|
+
});
|
|
42
|
+
if (expectedVersion !== void 0 && expectedVersion !== version) return new Response(null, {
|
|
43
|
+
status: 412,
|
|
44
|
+
headers
|
|
45
|
+
});
|
|
46
|
+
let start = 0;
|
|
47
|
+
let end = stat.size - 1;
|
|
48
|
+
if (range !== void 0) {
|
|
49
|
+
const match = /^bytes=(\d*)-(\d*)$/u.exec(range);
|
|
50
|
+
if (!match || !match[1] && !match[2] || [match[1], match[2]].some((value) => value !== "" && !Number.isSafeInteger(Number(value))) || stat.size === 0) {
|
|
51
|
+
headers.set("content-range", `bytes */${stat.size}`);
|
|
52
|
+
return new Response(null, {
|
|
53
|
+
status: 416,
|
|
54
|
+
headers
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (!match[1]) start = Math.max(0, stat.size - Number(match[2]));
|
|
58
|
+
else {
|
|
59
|
+
start = Number(match[1]);
|
|
60
|
+
if (match[2]) end = Math.min(Number(match[2]), end);
|
|
61
|
+
}
|
|
62
|
+
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || start > end) {
|
|
63
|
+
headers.set("content-range", `bytes */${stat.size}`);
|
|
64
|
+
return new Response(null, {
|
|
65
|
+
status: 416,
|
|
66
|
+
headers
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
headers.set("content-range", `bytes ${start}-${end}/${stat.size}`);
|
|
70
|
+
}
|
|
71
|
+
const bytes = new Uint8Array(Math.max(0, end - start + 1));
|
|
72
|
+
let read = 0;
|
|
73
|
+
while (read < bytes.length) {
|
|
74
|
+
const part = await handle.read(bytes, read, bytes.length - read, start + read);
|
|
75
|
+
if (part.bytesRead === 0) return new Response(null, {
|
|
76
|
+
status: 412,
|
|
77
|
+
headers
|
|
78
|
+
});
|
|
79
|
+
read += part.bytesRead;
|
|
80
|
+
}
|
|
81
|
+
const after = await handle.stat();
|
|
82
|
+
if (after.size !== stat.size || after.mtimeMs !== stat.mtimeMs || after.ctimeMs !== stat.ctimeMs) return new Response(null, {
|
|
83
|
+
status: 412,
|
|
84
|
+
headers
|
|
85
|
+
});
|
|
86
|
+
headers.set("content-length", String(bytes.length));
|
|
87
|
+
return new Response(bytes, {
|
|
88
|
+
status: range === void 0 ? 200 : 206,
|
|
89
|
+
headers
|
|
90
|
+
});
|
|
91
|
+
} finally {
|
|
92
|
+
await handle.close();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
export { readAccessibleFile };
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=accessibleFile.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibleFile.mjs","names":[],"sources":["../../../../src/services/fileAccess/accessibleFile.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n MAX_SESSION_FILE_BYTES,\n SESSION_FILE_CONTENT_TYPES,\n SESSION_FILE_DEFAULT_CONTENT_TYPE,\n} from '../../constants/sessionFiles';\n\nfunction inside(root: string, filePath: string): boolean {\n const contained = path.relative(root, filePath);\n return contained !== '..' && !contained.startsWith(`..${path.sep}`) && !path.isAbsolute(contained);\n}\n\n/**\n * Reads one file under an open folder, with byte ranges and a version the reader can pin. A relative\n * path resolves against `cwd`. Both the file and each root are resolved through symlinks first, so a\n * link inside an open folder cannot reach outside it.\n */\nexport async function readAccessibleFile(input: {\n roots: readonly string[];\n cwd: string;\n path: string | null;\n range?: string;\n expectedVersion?: string;\n}): Promise<Response> {\n const { range, expectedVersion } = input;\n if (!input.path || input.path.includes('\\0'))\n return Response.json({ error: 'Invalid session file path.' }, { status: 400 });\n let filePath: string;\n try {\n filePath = await fs.promises.realpath(path.resolve(input.cwd, input.path));\n } catch {\n return Response.json({ error: 'Session file not found.' }, { status: 404 });\n }\n // A root that no longer exists opens nothing.\n const roots = await Promise.all(input.roots.map((root) => fs.promises.realpath(root).catch(() => undefined)));\n if (!roots.some((root) => root !== undefined && inside(root, filePath)))\n return Response.json({ error: 'Session file is outside the folders this session may open.' }, { status: 403 });\n let handle: fs.promises.FileHandle;\n try {\n handle = await fs.promises.open(filePath, 'r');\n } catch {\n return Response.json({ error: 'Session file not found.' }, { status: 404 });\n }\n try {\n const stat = await handle.stat();\n if (!stat.isFile()) return Response.json({ error: 'Session file not found.' }, { status: 404 });\n if (stat.size > MAX_SESSION_FILE_BYTES)\n return Response.json({ error: 'Session file is too large.' }, { status: 413 });\n const version = `\"${stat.dev}-${stat.ino}-${stat.size}-${stat.mtimeMs}-${stat.ctimeMs}\"`;\n const headers = new Headers({\n 'content-type':\n SESSION_FILE_CONTENT_TYPES[path.extname(filePath).toLowerCase()] ?? SESSION_FILE_DEFAULT_CONTENT_TYPE,\n 'cache-control': 'no-store',\n 'x-content-type-options': 'nosniff',\n 'accept-ranges': 'bytes',\n etag: version,\n });\n if (expectedVersion !== undefined && expectedVersion !== version)\n return new Response(null, { status: 412, headers });\n let start = 0;\n let end = stat.size - 1;\n if (range !== undefined) {\n const match = /^bytes=(\\d*)-(\\d*)$/u.exec(range);\n if (\n !match ||\n (!match[1] && !match[2]) ||\n [match[1], match[2]].some((value) => value !== '' && !Number.isSafeInteger(Number(value))) ||\n stat.size === 0\n ) {\n headers.set('content-range', `bytes */${stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n if (!match[1]) start = Math.max(0, stat.size - Number(match[2]));\n else {\n start = Number(match[1]);\n if (match[2]) end = Math.min(Number(match[2]), end);\n }\n if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || start > end) {\n headers.set('content-range', `bytes */${stat.size}`);\n return new Response(null, { status: 416, headers });\n }\n headers.set('content-range', `bytes ${start}-${end}/${stat.size}`);\n }\n const bytes = new Uint8Array(Math.max(0, end - start + 1));\n let read = 0;\n while (read < bytes.length) {\n const part = await handle.read(bytes, read, bytes.length - read, start + read);\n if (part.bytesRead === 0) return new Response(null, { status: 412, headers });\n read += part.bytesRead;\n }\n const after = await handle.stat();\n if (after.size !== stat.size || after.mtimeMs !== stat.mtimeMs || after.ctimeMs !== stat.ctimeMs)\n return new Response(null, { status: 412, headers });\n headers.set('content-length', String(bytes.length));\n return new Response(bytes, { status: range === undefined ? 200 : 206, headers });\n } finally {\n await handle.close();\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_accessibleFile = require("./accessibleFile.cjs");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
node_path = require_runtime.__toESM(node_path, 1);
|
|
5
|
+
//#region src/services/fileAccess/index.ts
|
|
6
|
+
const PO_FILE_ACCESS_SERVICE = "po/file-access";
|
|
7
|
+
function createFileAccess() {
|
|
8
|
+
const entries = [];
|
|
9
|
+
return {
|
|
10
|
+
register(directory) {
|
|
11
|
+
if (directory.includes("\0") || !node_path.default.isAbsolute(directory)) throw new Error("File access opens absolute folders only.");
|
|
12
|
+
const entry = node_path.default.resolve(directory);
|
|
13
|
+
entries.push(entry);
|
|
14
|
+
let open = true;
|
|
15
|
+
return () => {
|
|
16
|
+
if (!open) return;
|
|
17
|
+
open = false;
|
|
18
|
+
entries.splice(entries.indexOf(entry), 1);
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
roots: () => [...new Set(entries)]
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** The session's file access, absent on a host older than this service. */
|
|
25
|
+
function readPoFileAccess(context) {
|
|
26
|
+
return context.get(PO_FILE_ACCESS_SERVICE);
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
exports.PO_FILE_ACCESS_SERVICE = PO_FILE_ACCESS_SERVICE;
|
|
30
|
+
exports.createFileAccess = createFileAccess;
|
|
31
|
+
exports.readAccessibleFile = require_accessibleFile.readAccessibleFile;
|
|
32
|
+
exports.readPoFileAccess = readPoFileAccess;
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["path"],"sources":["../../../../src/services/fileAccess/index.ts"],"sourcesContent":["import path from 'node:path';\n\nimport type { Context } from '@deepseek-ai/cordis';\n\nexport { readAccessibleFile } from './accessibleFile';\n\nexport const PO_FILE_ACCESS_SERVICE = 'po/file-access';\n\n/**\n * The folders a person may open from any paired client: the session's own, its workspace and temp\n * folder, and those an extension created and registered. Reads only; nothing here grants writes.\n */\nexport interface PoFileAccess {\n /** Opens `directory` and everything under it for reading; the returned function closes it again. */\n register(directory: string): () => void;\n /** The directories open now, each once, in registration order. */\n roots(): readonly string[];\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/file-access': PoFileAccess;\n }\n}\n\nexport function createFileAccess(): PoFileAccess {\n const entries: string[] = [];\n return {\n register(directory) {\n if (directory.includes('\\0') || !path.isAbsolute(directory))\n throw new Error('File access opens absolute folders only.');\n const entry = path.resolve(directory);\n entries.push(entry);\n let open = true;\n return () => {\n if (!open) return;\n open = false;\n entries.splice(entries.indexOf(entry), 1);\n };\n },\n roots: () => [...new Set(entries)],\n };\n}\n\n/** The session's file access, absent on a host older than this service. */\nexport function readPoFileAccess(context: Context): PoFileAccess | undefined {\n return context.get(PO_FILE_ACCESS_SERVICE) as PoFileAccess | undefined;\n}\n"],"mappings":";;;;;AAMA,MAAa,yBAAyB;AAmBtC,SAAgB,mBAAiC;CAC/C,MAAM,UAAoB,CAAC;CAC3B,OAAO;EACL,SAAS,WAAW;GAClB,IAAI,UAAU,SAAS,IAAI,KAAK,CAACA,UAAAA,QAAK,WAAW,SAAS,GACxD,MAAM,IAAI,MAAM,0CAA0C;GAC5D,MAAM,QAAQA,UAAAA,QAAK,QAAQ,SAAS;GACpC,QAAQ,KAAK,KAAK;GAClB,IAAI,OAAO;GACX,aAAa;IACX,IAAI,CAAC,MAAM;IACX,OAAO;IACP,QAAQ,OAAO,QAAQ,QAAQ,KAAK,GAAG,CAAC;GAC1C;EACF;EACA,aAAa,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC;CACnC;AACF;;AAGA,SAAgB,iBAAiB,SAA4C;CAC3E,OAAO,QAAQ,IAAI,sBAAsB;AAC3C"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
//#region src/services/fileAccess/index.d.ts
|
|
3
|
+
export declare const PO_FILE_ACCESS_SERVICE = "po/file-access";
|
|
4
|
+
/**
|
|
5
|
+
* The folders a person may open from any paired client: the session's own, its workspace and temp
|
|
6
|
+
* folder, and those an extension created and registered. Reads only; nothing here grants writes.
|
|
7
|
+
*/
|
|
8
|
+
export interface PoFileAccess {
|
|
9
|
+
/** Opens `directory` and everything under it for reading; the returned function closes it again. */
|
|
10
|
+
register(directory: string): () => void;
|
|
11
|
+
/** The directories open now, each once, in registration order. */
|
|
12
|
+
roots(): readonly string[];
|
|
13
|
+
}
|
|
14
|
+
declare module '@deepseek-ai/cordis' {
|
|
15
|
+
interface Context {
|
|
16
|
+
'po/file-access': PoFileAccess;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** The session's file access, absent on a host older than this service. */
|
|
20
|
+
export declare function readPoFileAccess(context: Context): PoFileAccess | undefined;
|
|
21
|
+
//#endregion
|
|
22
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/services/fileAccess/index.ts"],"mappings":";;qBAMa;;;;;iBAMI;;EAEf,SAAS;;EAET;;;YAIU;IACR,kBAAkB;;;;wBAwBN,iBAAiB,SAAS,UAAU"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { readAccessibleFile } from "./accessibleFile.mjs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
//#region src/services/fileAccess/index.ts
|
|
4
|
+
const PO_FILE_ACCESS_SERVICE = "po/file-access";
|
|
5
|
+
function createFileAccess() {
|
|
6
|
+
const entries = [];
|
|
7
|
+
return {
|
|
8
|
+
register(directory) {
|
|
9
|
+
if (directory.includes("\0") || !path.isAbsolute(directory)) throw new Error("File access opens absolute folders only.");
|
|
10
|
+
const entry = path.resolve(directory);
|
|
11
|
+
entries.push(entry);
|
|
12
|
+
let open = true;
|
|
13
|
+
return () => {
|
|
14
|
+
if (!open) return;
|
|
15
|
+
open = false;
|
|
16
|
+
entries.splice(entries.indexOf(entry), 1);
|
|
17
|
+
};
|
|
18
|
+
},
|
|
19
|
+
roots: () => [...new Set(entries)]
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** The session's file access, absent on a host older than this service. */
|
|
23
|
+
function readPoFileAccess(context) {
|
|
24
|
+
return context.get(PO_FILE_ACCESS_SERVICE);
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { PO_FILE_ACCESS_SERVICE, createFileAccess, readAccessibleFile, readPoFileAccess };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/fileAccess/index.ts"],"sourcesContent":["import path from 'node:path';\n\nimport type { Context } from '@deepseek-ai/cordis';\n\nexport { readAccessibleFile } from './accessibleFile';\n\nexport const PO_FILE_ACCESS_SERVICE = 'po/file-access';\n\n/**\n * The folders a person may open from any paired client: the session's own, its workspace and temp\n * folder, and those an extension created and registered. Reads only; nothing here grants writes.\n */\nexport interface PoFileAccess {\n /** Opens `directory` and everything under it for reading; the returned function closes it again. */\n register(directory: string): () => void;\n /** The directories open now, each once, in registration order. */\n roots(): readonly string[];\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/file-access': PoFileAccess;\n }\n}\n\nexport function createFileAccess(): PoFileAccess {\n const entries: string[] = [];\n return {\n register(directory) {\n if (directory.includes('\\0') || !path.isAbsolute(directory))\n throw new Error('File access opens absolute folders only.');\n const entry = path.resolve(directory);\n entries.push(entry);\n let open = true;\n return () => {\n if (!open) return;\n open = false;\n entries.splice(entries.indexOf(entry), 1);\n };\n },\n roots: () => [...new Set(entries)],\n };\n}\n\n/** The session's file access, absent on a host older than this service. */\nexport function readPoFileAccess(context: Context): PoFileAccess | undefined {\n return context.get(PO_FILE_ACCESS_SERVICE) as PoFileAccess | undefined;\n}\n"],"mappings":";;;AAMA,MAAa,yBAAyB;AAmBtC,SAAgB,mBAAiC;CAC/C,MAAM,UAAoB,CAAC;CAC3B,OAAO;EACL,SAAS,WAAW;GAClB,IAAI,UAAU,SAAS,IAAI,KAAK,CAAC,KAAK,WAAW,SAAS,GACxD,MAAM,IAAI,MAAM,0CAA0C;GAC5D,MAAM,QAAQ,KAAK,QAAQ,SAAS;GACpC,QAAQ,KAAK,KAAK;GAClB,IAAI,OAAO;GACX,aAAa;IACX,IAAI,CAAC,MAAM;IACX,OAAO;IACP,QAAQ,OAAO,QAAQ,QAAQ,KAAK,GAAG,CAAC;GAC1C;EACF;EACA,aAAa,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC;CACnC;AACF;;AAGA,SAAgB,iBAAiB,SAA4C;CAC3E,OAAO,QAAQ,IAAI,sBAAsB;AAC3C"}
|
|
@@ -49,6 +49,13 @@ export interface SlotDataFill<Data = unknown> {
|
|
|
49
49
|
order: number;
|
|
50
50
|
data: Data;
|
|
51
51
|
}
|
|
52
|
+
/** What a panel puts in its native tab screen's top bar, under and beside the tab label. */
|
|
53
|
+
export interface TabHeader {
|
|
54
|
+
/** One quiet line under the label, such as `Image · 18 KB`. */
|
|
55
|
+
subtitle?: string;
|
|
56
|
+
/** One trailing control, usually an icon button. */
|
|
57
|
+
action?: ReactNode;
|
|
58
|
+
}
|
|
52
59
|
/**
|
|
53
60
|
* A tab a plugin opens at runtime for one session, beside the declared ones:
|
|
54
61
|
* the reader closes it, and it goes with the session or the page. The host
|
|
@@ -242,6 +249,12 @@ export interface WebPluginSlotProps {
|
|
|
242
249
|
/** Opens the tab for the focused session, or focuses it when one with the same id is already open. */
|
|
243
250
|
openTransientTab: (tab: TransientTab) => void;
|
|
244
251
|
closeTransientTab: (tabId: string) => void;
|
|
252
|
+
/**
|
|
253
|
+
* Fills the native top bar of the tab screen this panel is shown in: a quiet line under
|
|
254
|
+
* the tab label and one trailing action. Absent outside a native tab screen and in older
|
|
255
|
+
* hosts, where a panel draws its own header instead.
|
|
256
|
+
*/
|
|
257
|
+
setTabHeader?: (header: TabHeader) => void;
|
|
245
258
|
/**
|
|
246
259
|
* The tab a file path opens, or undefined when no installed plugin can show
|
|
247
260
|
* it. A component that names a file it is certain about, such as a tool
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webPlugin.d.mts","names":[],"sources":["../../../../src/web/types/webPlugin.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;YA8BY,sBAAsB,mBAAmB,OAAO;;;;;;;;;;iBAU3C,gBAAgB;EAC/B;;EAEA,OAAO,iBAAiB;;;iBAGT,aAAa;EAC5B;EACA;EACA;EACA,MAAM;;;;;;;;
|
|
1
|
+
{"version":3,"file":"webPlugin.d.mts","names":[],"sources":["../../../../src/web/types/webPlugin.ts"],"mappings":"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"}
|
package/dist/web.d.mts
CHANGED
|
@@ -4,7 +4,7 @@ import { PoMcpWidgetProps } from "./src/web/types/mcpWidget.mjs";
|
|
|
4
4
|
import { WebTemplateContribution, WebTemplateHeaderOptions, WebTemplateRail, WebTemplateRailActions, WebTemplateRailDirectoryListing, WebTemplateRailPendingSetup, WebTemplateRailResult, WebTemplateRailSession, WebTemplateRailWorkspace } from "./src/web/types/template.mjs";
|
|
5
5
|
import { NativeTemplateContribution, NativeTemplateScreens, NativeTemplateSessionsActions, NativeTemplateSessionsProps } from "./src/web/types/nativeTemplate.mjs";
|
|
6
6
|
import { ModelContext, ModelContextAbortSignal, ModelContextBinding, ModelContextExecutionOptions, ModelContextRegistrationOptions, ModelContextTool, ModelContextToolChangeEvent, ModelContextToolChangeListener, ModelContextToolDescriptor } from "./src/web/types/modelContext.mjs";
|
|
7
|
-
import { ActivityGroupContribution, CaptureStatusEvent, ComposerCapture, ComposerSubmission, ContextAction, ContextActionContribution, ContextActionRunContext, DockFaceContribution, FileLinkSource, GlobalStore, LeaderBindingContribution, LeaderKeyContribution, MinorModeContribution, PaletteCommandContext, PaletteCommandContribution, RepositorySettingsPanelContribution, RepositorySettingsPanelProps, RepositorySettingsRepository, SelectionAxisContribution, SessionChannelContribution, SessionFrameSender, SessionRecords, SessionStore, SessionStoreChannel, SettingsFieldContribution, SettingsFieldOption, SettingsPanelContribution, SettingsPanelProps, SettingsSectionContribution, SettingsSectionGroup, SlotDataFill, SlotDeclaration, SlotFillContribution, SurfaceContribution, TabContribution, ThreadViewOptions, ToolCompletionEvent, ToolMessageRenderProps, ToolPromptContribution, ToolPromptDialog, ToolPromptRenderProps, ToolRendererContribution, ToolResultView, TransientTab, UserMessageActionContribution, UserMessageActionRunContext, WebPluginContextInventoryItem, WebPluginContextItem, WebPluginContributions, WebPluginDefinition, WebPluginMount, WebPluginNewSessionContext, WebPluginRuntime, WebPluginScope, WebPluginSessionContext, WebPluginSlotProps, WorkspaceRecords, WorkspaceStore } from "./src/web/types/webPlugin.mjs";
|
|
7
|
+
import { ActivityGroupContribution, CaptureStatusEvent, ComposerCapture, ComposerSubmission, ContextAction, ContextActionContribution, ContextActionRunContext, DockFaceContribution, FileLinkSource, GlobalStore, LeaderBindingContribution, LeaderKeyContribution, MinorModeContribution, PaletteCommandContext, PaletteCommandContribution, RepositorySettingsPanelContribution, RepositorySettingsPanelProps, RepositorySettingsRepository, SelectionAxisContribution, SessionChannelContribution, SessionFrameSender, SessionRecords, SessionStore, SessionStoreChannel, SettingsFieldContribution, SettingsFieldOption, SettingsPanelContribution, SettingsPanelProps, SettingsSectionContribution, SettingsSectionGroup, SlotDataFill, SlotDeclaration, SlotFillContribution, SurfaceContribution, TabContribution, TabHeader, ThreadViewOptions, ToolCompletionEvent, ToolMessageRenderProps, ToolPromptContribution, ToolPromptDialog, ToolPromptRenderProps, ToolRendererContribution, ToolResultView, TransientTab, UserMessageActionContribution, UserMessageActionRunContext, WebPluginContextInventoryItem, WebPluginContextItem, WebPluginContributions, WebPluginDefinition, WebPluginMount, WebPluginNewSessionContext, WebPluginRuntime, WebPluginScope, WebPluginSessionContext, WebPluginSlotProps, WorkspaceRecords, WorkspaceStore } from "./src/web/types/webPlugin.mjs";
|
|
8
8
|
import { ActivityGroupFile, ChannelFile, ContextActionFile, DockFaceFile, FillFile, LeaderBindingFile, MinorModeFile, PaletteCommandFile, SelectionAxisFile, SettingsPanelFile, SettingsSectionFile, SlotFile, TabFile, ToolRendererFile, UserMessageActionFile, WebTemplateFile } from "./src/web/types/extensionFile.mjs";
|
|
9
9
|
import { defineActivityGroup, defineChannelFile, defineContextAction, defineDockFace, defineFileLinks, defineFill, defineLeaderBinding, defineMinorModeFile, definePaletteCommand, defineRepositorySettingsPanel, defineSelectionAxis, defineSessionChannel, defineSettingsPanel, defineSettingsSection, defineSlot, defineSlotFile, defineTab, defineToolRenderer, defineUserMessageAction, defineWebLifecycle, defineWebPlugin, defineWebTemplate } from "./src/extensions/webPlugin.mjs";
|
|
10
10
|
import { defineGlobalStore } from "./src/web/models/globalStore.mjs";
|
|
@@ -19,4 +19,4 @@ import { parseNativeTemplate, parseWebTemplate } from "./src/web/schemas/templat
|
|
|
19
19
|
import { HubAdvertisement, RegistryDirInput } from "./src/web/types/sessionRegistry.mjs";
|
|
20
20
|
import { HUB_ADVERTISEMENT_VERSION, REGISTRY_DIR_ENV, hubAdvertisementPath, parseHubAdvertisement, resolveRegistryDir } from "./src/web/services/sessionRegistry/index.mjs";
|
|
21
21
|
import { defineMcpWidget } from "./src/web/services/mcpWidget.mjs";
|
|
22
|
-
export { type ActivityGroupContribution, type ActivityGroupFile, type ApiCallInit, type ApiClient, type ApiClientOptions, type ApiMethod, type ApiParams, type ApiQuery, type ApiResponseOf, type ApiResult, type ApiRouteMethod, type ApiRouteSpec, type ApiRoutes, type ApiScopeAddress, type ApiScopeClient, type ApiScopeName, type ApiStreamMethod, type ApiTransport, BUNDLE_ASSET_POLICY_PATH, BUNDLE_ASSET_POLICY_VERSION, type BundleAssetPolicy, type CaptureStatusEvent, type ChannelFile, type ComposerCapture, type ComposerSubmission, type ContextAction, type ContextActionContribution, type ContextActionFile, type ContextActionRunContext, type DockFaceContribution, type DockFaceFile, type FileLinkSource, type FillFile, type GlobalStore, HOME_ENTITY_LIMIT, HOME_KINDS, HOME_PAGE_LIMIT, HUB_ADVERTISEMENT_VERSION, HomeContribution, HomeCoverage, HomeEntity, HomeItem, HomeKind, HomeProjection, HomeRequest, HomeSnapshot, HomeSourceStatus, type HubAdvertisement, type LeaderBindingContribution, type LeaderBindingFile, type LeaderKeyContribution, type MinorModeContribution, type MinorModeFile, type ModelContext, type ModelContextAbortSignal, type ModelContextBinding, type ModelContextExecutionOptions, type ModelContextRegistrationOptions, type ModelContextTool, type ModelContextToolChangeEvent, type ModelContextToolChangeListener, type ModelContextToolDescriptor, type NativeTemplateContribution, type NativeTemplateScreens, type NativeTemplateSessionsActions, type NativeTemplateSessionsProps, type PaletteCommandContext, type PaletteCommandContribution, type PaletteCommandFile, type PoMcpWidgetProps, REGISTRY_DIR_ENV, type RegistryDirInput, type RepositorySettingsPanelContribution, type RepositorySettingsPanelProps, type RepositorySettingsRepository, SETTINGS_BASE_PATH, type SelectionAxisContribution, type SelectionAxisFile, type SessionChannelContribution, type SessionFrameSender, type SessionRecords, type SessionStore, type SessionStoreChannel, type SettingsFieldContribution, type SettingsFieldOption, type SettingsPanelContribution, type SettingsPanelFile, type SettingsPanelProps, type SettingsSectionContribution, type SettingsSectionFile, type SettingsSectionGroup, type SlotDataFill, type SlotDeclaration, type SlotFile, type SlotFillContribution, type SurfaceContribution, type TabContribution, type TabFile, type ThreadViewOptions, type ToolCompletionEvent, type ToolMessageRenderProps, type ToolPromptContribution, type ToolPromptDialog, type ToolPromptRenderProps, type ToolRendererContribution, type ToolRendererFile, type ToolResultView, type TransientTab, type UserMessageActionContribution, type UserMessageActionFile, type UserMessageActionRunContext, type WebPluginContextInventoryItem, type WebPluginContextItem, type WebPluginContributions, type WebPluginDefinition, type WebPluginMount, type WebPluginNewSessionContext, type WebPluginRuntime, type WebPluginScope, type WebPluginSessionContext, type WebPluginSlotProps, type WebTemplateContribution, type WebTemplateFile, type WebTemplateHeaderOptions, type WebTemplateRail, type WebTemplateRailActions, type WebTemplateRailDirectoryListing, type WebTemplateRailPendingSetup, type WebTemplateRailResult, type WebTemplateRailSession, type WebTemplateRailWorkspace, type WorkspaceRecords, type WorkspaceStore, apiQueryString, apiResponse, applyPathParams, bindNativeApiTransport, bindSessionApiWorkspace, createApiClient, defineActivityGroup, defineApiRoutes, defineChannelFile, defineContextAction, defineDockFace, defineFileLinks, defineFill, defineGlobalStore, defineLeaderBinding, defineMcpWidget, defineMinorModeFile, definePaletteCommand, defineRepositorySettingsPanel, defineSelectionAxis, defineSessionChannel, defineSessionStore, defineSettingsPanel, defineSettingsSection, defineSlot, defineSlotFile, defineTab, defineToolRenderer, defineUserMessageAction, defineWebLifecycle, defineWebPlugin, defineWebTemplate, defineWorkspaceStore, globalApiPath, hubAdvertisementPath, isNativeApiTarget, nativeApiTransport, parseBundleAssetPolicy, parseHomeContribution, parseHomeEntity, parseHomeProjection, parseHomeSnapshot, parseHubAdvertisement, parseNativeTemplate, parseWebTemplate, pluginApiBase, pluginApiUrl, resolveRegistryDir, scopeApiRoot, sessionApiAddress, sessionApiPath, toolResultText, toolResultTextLines, workspaceApiPath };
|
|
22
|
+
export { type ActivityGroupContribution, type ActivityGroupFile, type ApiCallInit, type ApiClient, type ApiClientOptions, type ApiMethod, type ApiParams, type ApiQuery, type ApiResponseOf, type ApiResult, type ApiRouteMethod, type ApiRouteSpec, type ApiRoutes, type ApiScopeAddress, type ApiScopeClient, type ApiScopeName, type ApiStreamMethod, type ApiTransport, BUNDLE_ASSET_POLICY_PATH, BUNDLE_ASSET_POLICY_VERSION, type BundleAssetPolicy, type CaptureStatusEvent, type ChannelFile, type ComposerCapture, type ComposerSubmission, type ContextAction, type ContextActionContribution, type ContextActionFile, type ContextActionRunContext, type DockFaceContribution, type DockFaceFile, type FileLinkSource, type FillFile, type GlobalStore, HOME_ENTITY_LIMIT, HOME_KINDS, HOME_PAGE_LIMIT, HUB_ADVERTISEMENT_VERSION, HomeContribution, HomeCoverage, HomeEntity, HomeItem, HomeKind, HomeProjection, HomeRequest, HomeSnapshot, HomeSourceStatus, type HubAdvertisement, type LeaderBindingContribution, type LeaderBindingFile, type LeaderKeyContribution, type MinorModeContribution, type MinorModeFile, type ModelContext, type ModelContextAbortSignal, type ModelContextBinding, type ModelContextExecutionOptions, type ModelContextRegistrationOptions, type ModelContextTool, type ModelContextToolChangeEvent, type ModelContextToolChangeListener, type ModelContextToolDescriptor, type NativeTemplateContribution, type NativeTemplateScreens, type NativeTemplateSessionsActions, type NativeTemplateSessionsProps, type PaletteCommandContext, type PaletteCommandContribution, type PaletteCommandFile, type PoMcpWidgetProps, REGISTRY_DIR_ENV, type RegistryDirInput, type RepositorySettingsPanelContribution, type RepositorySettingsPanelProps, type RepositorySettingsRepository, SETTINGS_BASE_PATH, type SelectionAxisContribution, type SelectionAxisFile, type SessionChannelContribution, type SessionFrameSender, type SessionRecords, type SessionStore, type SessionStoreChannel, type SettingsFieldContribution, type SettingsFieldOption, type SettingsPanelContribution, type SettingsPanelFile, type SettingsPanelProps, type SettingsSectionContribution, type SettingsSectionFile, type SettingsSectionGroup, type SlotDataFill, type SlotDeclaration, type SlotFile, type SlotFillContribution, type SurfaceContribution, type TabContribution, type TabFile, type TabHeader, type ThreadViewOptions, type ToolCompletionEvent, type ToolMessageRenderProps, type ToolPromptContribution, type ToolPromptDialog, type ToolPromptRenderProps, type ToolRendererContribution, type ToolRendererFile, type ToolResultView, type TransientTab, type UserMessageActionContribution, type UserMessageActionFile, type UserMessageActionRunContext, type WebPluginContextInventoryItem, type WebPluginContextItem, type WebPluginContributions, type WebPluginDefinition, type WebPluginMount, type WebPluginNewSessionContext, type WebPluginRuntime, type WebPluginScope, type WebPluginSessionContext, type WebPluginSlotProps, type WebTemplateContribution, type WebTemplateFile, type WebTemplateHeaderOptions, type WebTemplateRail, type WebTemplateRailActions, type WebTemplateRailDirectoryListing, type WebTemplateRailPendingSetup, type WebTemplateRailResult, type WebTemplateRailSession, type WebTemplateRailWorkspace, type WorkspaceRecords, type WorkspaceStore, apiQueryString, apiResponse, applyPathParams, bindNativeApiTransport, bindSessionApiWorkspace, createApiClient, defineActivityGroup, defineApiRoutes, defineChannelFile, defineContextAction, defineDockFace, defineFileLinks, defineFill, defineGlobalStore, defineLeaderBinding, defineMcpWidget, defineMinorModeFile, definePaletteCommand, defineRepositorySettingsPanel, defineSelectionAxis, defineSessionChannel, defineSessionStore, defineSettingsPanel, defineSettingsSection, defineSlot, defineSlotFile, defineTab, defineToolRenderer, defineUserMessageAction, defineWebLifecycle, defineWebPlugin, defineWebTemplate, defineWorkspaceStore, globalApiPath, hubAdvertisementPath, isNativeApiTarget, nativeApiTransport, parseBundleAssetPolicy, parseHomeContribution, parseHomeEntity, parseHomeProjection, parseHomeSnapshot, parseHubAdvertisement, parseNativeTemplate, parseWebTemplate, pluginApiBase, pluginApiUrl, resolveRegistryDir, scopeApiRoot, sessionApiAddress, sessionApiPath, toolResultText, toolResultTextLines, workspaceApiPath };
|
package/dist/webCompositions.cjs
CHANGED
|
@@ -11,6 +11,19 @@ node_path = require_runtime.__toESM(node_path, 1);
|
|
|
11
11
|
let _agimon_ai_po_web_security = require("@agimon-ai/po-web-security");
|
|
12
12
|
//#region src/services/webCompositions/index.ts
|
|
13
13
|
const absolute = (file) => file.startsWith("/") ? file : `/${file}`;
|
|
14
|
+
/**
|
|
15
|
+
* The file a signed manifest path names below `root`. Manifest segments are percent-encoded
|
|
16
|
+
* (`@` and `+` in pnpm paths); the decoded file must still resolve inside `root`.
|
|
17
|
+
*/
|
|
18
|
+
function assetFile(root, assetPath) {
|
|
19
|
+
try {
|
|
20
|
+
const base = node_path.default.resolve(root);
|
|
21
|
+
const file = node_path.default.resolve(base, ...assetPath.slice(1).split("/").map(decodeURIComponent));
|
|
22
|
+
return file.startsWith(`${base}${node_path.default.sep}`) && !file.includes("\0") ? file : void 0;
|
|
23
|
+
} catch {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
14
27
|
/** The build's plugin copies for exactly the given slots, in slot order. */
|
|
15
28
|
function slotPlugins(build, slots) {
|
|
16
29
|
return slots.flatMap((slot) => build.plugins.filter((plugin) => plugin.slot === slot));
|
|
@@ -41,6 +54,7 @@ function createWebCompositions(stateDirectory, onNotice) {
|
|
|
41
54
|
let shell;
|
|
42
55
|
const mounts = /* @__PURE__ */ new Map();
|
|
43
56
|
const nativeMounts = /* @__PURE__ */ new Map();
|
|
57
|
+
const apps = /* @__PURE__ */ new Map();
|
|
44
58
|
const references = /* @__PURE__ */ new Map();
|
|
45
59
|
const keyOf = (mount) => mount.scope === "global" ? "global" : mount.scope === "workspace" ? `workspace:${mount.workspaceId}` : `session:${mount.sessionId}`;
|
|
46
60
|
const release = (id) => {
|
|
@@ -126,6 +140,28 @@ function createWebCompositions(stateDirectory, onNotice) {
|
|
|
126
140
|
if (previous) release(previous.id);
|
|
127
141
|
},
|
|
128
142
|
/**
|
|
143
|
+
* Publishes one platform's app bundle under the `app` profile, so it can never be served or
|
|
144
|
+
* verified as a composition. A new key releases the previous bundle; a failure leaves it serving.
|
|
145
|
+
*/
|
|
146
|
+
publishApp(platform, build) {
|
|
147
|
+
const id = sha256(`app:${platform}:${build.key}`);
|
|
148
|
+
const previous = apps.get(platform);
|
|
149
|
+
if (previous?.id === id) return;
|
|
150
|
+
const published = publication.publish(id, build.directory, "app");
|
|
151
|
+
if (!published) return;
|
|
152
|
+
const revision = published.signed.manifest.revision;
|
|
153
|
+
const base = `/api/web-plugins/${id}/${String(revision)}`;
|
|
154
|
+
apps.set(platform, {
|
|
155
|
+
id,
|
|
156
|
+
revision,
|
|
157
|
+
manifestUrl: `${base}/manifest`,
|
|
158
|
+
rawAssetBaseUrl: `${base}/assets`,
|
|
159
|
+
fingerprint: build.fingerprint
|
|
160
|
+
});
|
|
161
|
+
if (previous !== void 0) publication.release(previous.id);
|
|
162
|
+
},
|
|
163
|
+
apps: () => Object.fromEntries(apps),
|
|
164
|
+
/**
|
|
129
165
|
* Signs `po/host-channel-key/v1` with the shell publication's signer, the key the pairing QR pins
|
|
130
166
|
* as `s`. Undefined until the shell is published.
|
|
131
167
|
*/
|
|
@@ -143,7 +179,9 @@ function createWebCompositions(stateDirectory, onNotice) {
|
|
|
143
179
|
if (!shell || Number(shellAsset[1]) !== shell.signed.manifest.revision) return new Response(null, { status: 404 });
|
|
144
180
|
const asset = shell.signed.manifest.assets.find((entry) => entry.path === shellAsset[2]);
|
|
145
181
|
if (!asset) return new Response(null, { status: 404 });
|
|
146
|
-
const
|
|
182
|
+
const file = assetFile(shell.assetsDir, asset.path);
|
|
183
|
+
if (!file) return new Response(null, { status: 404 });
|
|
184
|
+
const bytes = await node_fs.default.promises.readFile(file);
|
|
147
185
|
return new Response(request.method === "HEAD" ? null : bytes, { headers: {
|
|
148
186
|
"content-type": asset.contentType,
|
|
149
187
|
"x-content-type-options": "nosniff"
|
|
@@ -158,7 +196,9 @@ function createWebCompositions(stateDirectory, onNotice) {
|
|
|
158
196
|
const assetPath = match[3].slice(6);
|
|
159
197
|
const asset = bundle.signed.manifest.assets.find((candidate) => candidate.path === assetPath);
|
|
160
198
|
if (!asset) return Response.json({ error: "Plugin asset not found." }, { status: 404 });
|
|
161
|
-
const
|
|
199
|
+
const file = assetFile(bundle.assetsDir, asset.path);
|
|
200
|
+
if (!file) return Response.json({ error: "Plugin asset not found." }, { status: 404 });
|
|
201
|
+
const bytes = await node_fs.default.promises.readFile(file);
|
|
162
202
|
return new Response(request.method === "HEAD" ? null : bytes, { headers: {
|
|
163
203
|
"content-type": asset.contentType,
|
|
164
204
|
"cache-control": "private, max-age=31536000, immutable",
|
|
@@ -168,6 +208,7 @@ function createWebCompositions(stateDirectory, onNotice) {
|
|
|
168
208
|
close() {
|
|
169
209
|
mounts.clear();
|
|
170
210
|
nativeMounts.clear();
|
|
211
|
+
apps.clear();
|
|
171
212
|
references.clear();
|
|
172
213
|
publication.close();
|
|
173
214
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webCompositions.cjs","names":["crypto","createPluginBundlePublication","NATIVE_ENTRY_PATH","HOST_CHANNEL_KEY_TAG","fs","path"],"sources":["../src/services/webCompositions/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { NATIVE_ENTRY_PATH } from '@agimon-ai/po-web-security';\n\nimport { HOST_CHANNEL_KEY_TAG } from '../../constants/remote';\nimport type { PoApiMount, PoNativeComposition, PoWebComposition } from '../../exports/packageApi';\nimport { createPluginBundlePublication, type PublishedBundle } from '../bundlePublication';\n\n/** One plugin copy in a global web build: its slot entry and the files that entry can load. */\nexport interface GlobalWebBuildPlugin {\n pluginId: string;\n /** The copy's identity: the pluginId for a host plugin, else the pluginId plus a package hash. */\n slot: string;\n packageDir: string;\n isHost: boolean;\n /** The slot entry module below `pluginsDirectory`. */\n entry: string;\n /** Every file below `pluginsDirectory` the entry reaches through static and dynamic imports, plus its stylesheet. */\n files: readonly string[];\n /** The stylesheets of the entry's static import closure. */\n css: readonly string[];\n}\n\n/** One global web build: the shell, the shared plugin composition, and one stylesheet per plugin copy. */\nexport interface GlobalWebBuild {\n /** Content key of the build; equal keys hold identical bytes. */\n key: string;\n /** Signed cockpit shell directory. */\n webDirectory: string;\n /** Shared plugin composition directory, published once for every mount. */\n pluginsDirectory: string;\n /** Every plugin copy in the build, in build order. */\n plugins: readonly GlobalWebBuildPlugin[];\n /** Content-hashed stylesheet paths below `pluginsDirectory`, keyed by slot. */\n pluginStyles: Readonly<Record<string, string>>;\n /** Stylesheet a mount without plugin styles still loads, for the host shell and fallback template. */\n baseStyle?: string;\n}\n\n/** One global native build: a single signed CommonJS composition shared by every mount. */\nexport interface GlobalNativeBuild {\n /** Content key of the build; equal keys hold identical bytes. */\n key: string;\n /** Directory holding exactly `/composition.js`. */\n pluginsDirectory: string;\n}\n\nconst absolute = (file: string): string => (file.startsWith('/') ? file : `/${file}`);\n\n/** The build's plugin copies for exactly the given slots, in slot order. */\nfunction slotPlugins(build: GlobalWebBuild, slots: readonly string[]): GlobalWebBuildPlugin[] {\n return slots.flatMap((slot) => build.plugins.filter((plugin) => plugin.slot === slot));\n}\n\n/**\n * The stylesheets of exactly the given slots, each loaded once: the stylesheets their code\n * imports first, then each slot's own stylesheet in slot order.\n */\nexport function mountStylePaths(build: GlobalWebBuild, slots: readonly string[]): string[] {\n const paths = slots.map((slot) => build.pluginStyles[slot]).filter((style) => style !== undefined);\n if (paths.length === 0 && build.baseStyle !== undefined) paths.push(build.baseStyle);\n const imported = slotPlugins(build, slots).flatMap((plugin) => plugin.css);\n return [...new Set([...imported, ...paths].map(absolute))];\n}\n\n/** Every file the given slots can load: their reachable files and static stylesheets, each once. */\nexport function mountAssetPaths(build: GlobalWebBuild, slots: readonly string[]): string[] {\n return [\n ...new Set(\n slotPlugins(build, slots)\n .flatMap((plugin) => [...plugin.files, ...plugin.css])\n .map(absolute),\n ),\n ];\n}\n\nconst sha256 = (value: string): string => crypto.createHash('sha256').update(value).digest('hex');\n\n/**\n * Signs the global build once and hands each mount a composition naming its own plugins.\n *\n * Every mount of one build shares a publication id, so the bytes are counted per mount and\n * released only when the last mount leaves or moves to another build.\n */\nexport function createWebCompositions(stateDirectory: string, onNotice: (message: string) => void) {\n const publication = createPluginBundlePublication(stateDirectory, onNotice);\n let shell: PublishedBundle | undefined;\n const mounts = new Map<string, PoWebComposition>();\n const nativeMounts = new Map<string, PoNativeComposition>();\n const references = new Map<string, number>();\n const keyOf = (mount: PoApiMount): string =>\n mount.scope === 'global'\n ? 'global'\n : mount.scope === 'workspace'\n ? `workspace:${mount.workspaceId}`\n : `session:${mount.sessionId}`;\n const release = (id: string): void => {\n const count = (references.get(id) ?? 0) - 1;\n if (count > 0) {\n references.set(id, count);\n return;\n }\n references.delete(id);\n publication.release(id);\n };\n return {\n publishShell(build: GlobalWebBuild): void {\n shell = publication.publish(sha256(`shell:${build.key}`), build.webDirectory);\n if (!shell) throw new Error('Could not publish the global web shell.');\n },\n shellTrust: () =>\n shell === undefined ? undefined : { publicKey: shell.signed.publicKey, revision: shell.signed.manifest.revision },\n publish(\n mount: PoApiMount,\n build: GlobalWebBuild,\n channels: readonly string[],\n slots: readonly string[],\n ): PoWebComposition | undefined {\n const id = sha256(`plugins:${build.key}`);\n const published = publication.publish(id, build.pluginsDirectory);\n if (!published) throw new Error(`Could not publish ${keyOf(mount)} web composition.`);\n const previous = mounts.get(keyOf(mount));\n if (previous?.id !== id) references.set(id, (references.get(id) ?? 0) + 1);\n const revision = published.signed.manifest.revision;\n const base = `/api/web-plugins/${id}/${String(revision)}`;\n const signedPaths = new Set(published.signed.manifest.assets.map((asset) => asset.path));\n const composition: PoWebComposition = {\n id,\n scope: mount.scope,\n revision,\n manifestUrl: `${base}/manifest`,\n rawAssetBaseUrl: `${base}/assets`,\n verifiedAssetBaseUrl: `/verified-plugins/${id}/${String(revision)}`,\n pluginEntries: slotPlugins(build, slots).map((plugin): [string, string] => [\n plugin.slot,\n absolute(plugin.entry),\n ]),\n assetPaths: mountAssetPaths(build, slots).filter((file) => signedPaths.has(file)),\n stylePaths: mountStylePaths(build, slots).filter((style) => signedPaths.has(style)),\n channels: [...channels],\n plugins: [...slots],\n };\n mounts.set(keyOf(mount), composition);\n if (previous !== undefined && previous.id !== id) release(previous.id);\n return composition;\n },\n get: (mount: PoApiMount) => mounts.get(keyOf(mount)),\n remove: (mount: PoApiMount) => {\n const previous = mounts.get(keyOf(mount));\n mounts.delete(keyOf(mount));\n if (previous) release(previous.id);\n },\n /**\n * Publishes the native composition for one mount. Signed under the `native` profile, so it can\n * never be served or verified as a web bundle. Shares reference counting with web publications.\n */\n publishNative(\n mount: PoApiMount,\n build: GlobalNativeBuild,\n channels: readonly string[],\n pluginIds: readonly string[],\n ): PoNativeComposition {\n const id = sha256(`native:${build.key}`);\n const published = publication.publish(id, build.pluginsDirectory, 'native');\n if (!published) throw new Error(`Could not publish ${keyOf(mount)} native composition.`);\n const previous = nativeMounts.get(keyOf(mount));\n if (previous?.id !== id) references.set(id, (references.get(id) ?? 0) + 1);\n const revision = published.signed.manifest.revision;\n const base = `/api/web-plugins/${id}/${String(revision)}`;\n const composition: PoNativeComposition = {\n id,\n scope: mount.scope,\n revision,\n manifestUrl: `${base}/manifest`,\n rawAssetBaseUrl: `${base}/assets`,\n entryPath: NATIVE_ENTRY_PATH,\n channels: [...channels],\n plugins: [...pluginIds],\n };\n nativeMounts.set(keyOf(mount), composition);\n if (previous !== undefined && previous.id !== id) release(previous.id);\n return composition;\n },\n getNative: (mount: PoApiMount) => nativeMounts.get(keyOf(mount)),\n removeNative: (mount: PoApiMount) => {\n const previous = nativeMounts.get(keyOf(mount));\n nativeMounts.delete(keyOf(mount));\n if (previous) release(previous.id);\n },\n /**\n * Signs `po/host-channel-key/v1` with the shell publication's signer, the key the pairing QR pins\n * as `s`. Undefined until the shell is published.\n */\n attestHost: (fields: readonly string[]): string | undefined =>\n shell === undefined ? undefined : publication.attest(HOST_CHANNEL_KEY_TAG, fields),\n publicKey: () => publication.publicKey(),\n async request(request: Request): Promise<Response | undefined> {\n const url = new URL(request.url);\n if (\n (url.pathname === '/bundle-manifest.json' || url.pathname.startsWith('/bundle-assets/')) &&\n request.method !== 'GET' &&\n request.method !== 'HEAD'\n )\n return new Response(null, { status: 405 });\n if (url.pathname === '/bundle-manifest.json') {\n if (!shell) return new Response(null, { status: 404 });\n return Response.json(shell.signed);\n }\n const shellAsset = /^\\/bundle-assets\\/(\\d+)(\\/.*)$/u.exec(url.pathname);\n if (shellAsset) {\n if (!shell || Number(shellAsset[1]) !== shell.signed.manifest.revision)\n return new Response(null, { status: 404 });\n const asset = shell.signed.manifest.assets.find((entry) => entry.path === shellAsset[2]);\n if (!asset) return new Response(null, { status: 404 });\n const bytes = await fs.promises.readFile(path.join(shell.assetsDir, asset.path));\n return new Response(request.method === 'HEAD' ? null : bytes, {\n headers: { 'content-type': asset.contentType, 'x-content-type-options': 'nosniff' },\n });\n }\n const match = /^\\/api\\/web-plugins\\/([a-f0-9]{64})\\/(\\d+)\\/(manifest|assets(?:\\/.*)?)$/u.exec(url.pathname);\n if (!match) return undefined;\n if (request.method !== 'GET' && request.method !== 'HEAD') return new Response(null, { status: 405 });\n const bundle = publication.get(match[1], Number(match[2]));\n if (!bundle) return Response.json({ error: 'Plugin generation not found.' }, { status: 404 });\n if (match[3] === 'manifest') return Response.json(bundle.signed);\n const assetPath = match[3].slice('assets'.length);\n const asset = bundle.signed.manifest.assets.find((candidate) => candidate.path === assetPath);\n if (!asset) return Response.json({ error: 'Plugin asset not found.' }, { status: 404 });\n const bytes = await fs.promises.readFile(path.join(bundle.assetsDir, asset.path));\n return new Response(request.method === 'HEAD' ? null : bytes, {\n headers: {\n 'content-type': asset.contentType,\n 'cache-control': 'private, max-age=31536000, immutable',\n 'x-content-type-options': 'nosniff',\n },\n });\n },\n close() {\n mounts.clear();\n nativeMounts.clear();\n references.clear();\n publication.close();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;AAiDA,MAAM,YAAY,SAA0B,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI;;AAG9E,SAAS,YAAY,OAAuB,OAAkD;CAC5F,OAAO,MAAM,SAAS,SAAS,MAAM,QAAQ,QAAQ,WAAW,OAAO,SAAS,IAAI,CAAC;AACvF;;;;;AAMA,SAAgB,gBAAgB,OAAuB,OAAoC;CACzF,MAAM,QAAQ,MAAM,KAAK,SAAS,MAAM,aAAa,KAAK,CAAC,CAAC,QAAQ,UAAU,UAAU,KAAA,CAAS;CACjG,IAAI,MAAM,WAAW,KAAK,MAAM,cAAc,KAAA,GAAW,MAAM,KAAK,MAAM,SAAS;CACnF,MAAM,WAAW,YAAY,OAAO,KAAK,CAAC,CAAC,SAAS,WAAW,OAAO,GAAG;CACzE,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,UAAU,GAAG,KAAK,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC;AAC3D;;AAGA,SAAgB,gBAAgB,OAAuB,OAAoC;CACzF,OAAO,CACL,GAAG,IAAI,IACL,YAAY,OAAO,KAAK,CAAC,CACtB,SAAS,WAAW,CAAC,GAAG,OAAO,OAAO,GAAG,OAAO,GAAG,CAAC,CAAC,CACrD,IAAI,QAAQ,CACjB,CACF;AACF;AAEA,MAAM,UAAU,UAA0BA,YAAAA,QAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;;;;;;;AAQhG,SAAgB,sBAAsB,gBAAwB,UAAqC;CACjG,MAAM,cAAcC,cAAAA,8BAA8B,gBAAgB,QAAQ;CAC1E,IAAI;CACJ,MAAM,yBAAS,IAAI,IAA8B;CACjD,MAAM,+BAAe,IAAI,IAAiC;CAC1D,MAAM,6BAAa,IAAI,IAAoB;CAC3C,MAAM,SAAS,UACb,MAAM,UAAU,WACZ,WACA,MAAM,UAAU,cACd,aAAa,MAAM,gBACnB,WAAW,MAAM;CACzB,MAAM,WAAW,OAAqB;EACpC,MAAM,SAAS,WAAW,IAAI,EAAE,KAAK,KAAK;EAC1C,IAAI,QAAQ,GAAG;GACb,WAAW,IAAI,IAAI,KAAK;GACxB;EACF;EACA,WAAW,OAAO,EAAE;EACpB,YAAY,QAAQ,EAAE;CACxB;CACA,OAAO;EACL,aAAa,OAA6B;GACxC,QAAQ,YAAY,QAAQ,OAAO,SAAS,MAAM,KAAK,GAAG,MAAM,YAAY;GAC5E,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,yCAAyC;EACvE;EACA,kBACE,UAAU,KAAA,IAAY,KAAA,IAAY;GAAE,WAAW,MAAM,OAAO;GAAW,UAAU,MAAM,OAAO,SAAS;EAAS;EAClH,QACE,OACA,OACA,UACA,OAC8B;GAC9B,MAAM,KAAK,OAAO,WAAW,MAAM,KAAK;GACxC,MAAM,YAAY,YAAY,QAAQ,IAAI,MAAM,gBAAgB;GAChE,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,qBAAqB,MAAM,KAAK,EAAE,kBAAkB;GACpF,MAAM,WAAW,OAAO,IAAI,MAAM,KAAK,CAAC;GACxC,IAAI,UAAU,OAAO,IAAI,WAAW,IAAI,KAAK,WAAW,IAAI,EAAE,KAAK,KAAK,CAAC;GACzE,MAAM,WAAW,UAAU,OAAO,SAAS;GAC3C,MAAM,OAAO,oBAAoB,GAAG,GAAG,OAAO,QAAQ;GACtD,MAAM,cAAc,IAAI,IAAI,UAAU,OAAO,SAAS,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC;GACvF,MAAM,cAAgC;IACpC;IACA,OAAO,MAAM;IACb;IACA,aAAa,GAAG,KAAK;IACrB,iBAAiB,GAAG,KAAK;IACzB,sBAAsB,qBAAqB,GAAG,GAAG,OAAO,QAAQ;IAChE,eAAe,YAAY,OAAO,KAAK,CAAC,CAAC,KAAK,WAA6B,CACzE,OAAO,MACP,SAAS,OAAO,KAAK,CACvB,CAAC;IACD,YAAY,gBAAgB,OAAO,KAAK,CAAC,CAAC,QAAQ,SAAS,YAAY,IAAI,IAAI,CAAC;IAChF,YAAY,gBAAgB,OAAO,KAAK,CAAC,CAAC,QAAQ,UAAU,YAAY,IAAI,KAAK,CAAC;IAClF,UAAU,CAAC,GAAG,QAAQ;IACtB,SAAS,CAAC,GAAG,KAAK;GACpB;GACA,OAAO,IAAI,MAAM,KAAK,GAAG,WAAW;GACpC,IAAI,aAAa,KAAA,KAAa,SAAS,OAAO,IAAI,QAAQ,SAAS,EAAE;GACrE,OAAO;EACT;EACA,MAAM,UAAsB,OAAO,IAAI,MAAM,KAAK,CAAC;EACnD,SAAS,UAAsB;GAC7B,MAAM,WAAW,OAAO,IAAI,MAAM,KAAK,CAAC;GACxC,OAAO,OAAO,MAAM,KAAK,CAAC;GAC1B,IAAI,UAAU,QAAQ,SAAS,EAAE;EACnC;;;;;EAKA,cACE,OACA,OACA,UACA,WACqB;GACrB,MAAM,KAAK,OAAO,UAAU,MAAM,KAAK;GACvC,MAAM,YAAY,YAAY,QAAQ,IAAI,MAAM,kBAAkB,QAAQ;GAC1E,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,qBAAqB,MAAM,KAAK,EAAE,qBAAqB;GACvF,MAAM,WAAW,aAAa,IAAI,MAAM,KAAK,CAAC;GAC9C,IAAI,UAAU,OAAO,IAAI,WAAW,IAAI,KAAK,WAAW,IAAI,EAAE,KAAK,KAAK,CAAC;GACzE,MAAM,WAAW,UAAU,OAAO,SAAS;GAC3C,MAAM,OAAO,oBAAoB,GAAG,GAAG,OAAO,QAAQ;GACtD,MAAM,cAAmC;IACvC;IACA,OAAO,MAAM;IACb;IACA,aAAa,GAAG,KAAK;IACrB,iBAAiB,GAAG,KAAK;IACzB,WAAWC,2BAAAA;IACX,UAAU,CAAC,GAAG,QAAQ;IACtB,SAAS,CAAC,GAAG,SAAS;GACxB;GACA,aAAa,IAAI,MAAM,KAAK,GAAG,WAAW;GAC1C,IAAI,aAAa,KAAA,KAAa,SAAS,OAAO,IAAI,QAAQ,SAAS,EAAE;GACrE,OAAO;EACT;EACA,YAAY,UAAsB,aAAa,IAAI,MAAM,KAAK,CAAC;EAC/D,eAAe,UAAsB;GACnC,MAAM,WAAW,aAAa,IAAI,MAAM,KAAK,CAAC;GAC9C,aAAa,OAAO,MAAM,KAAK,CAAC;GAChC,IAAI,UAAU,QAAQ,SAAS,EAAE;EACnC;;;;;EAKA,aAAa,WACX,UAAU,KAAA,IAAY,KAAA,IAAY,YAAY,OAAOC,eAAAA,sBAAsB,MAAM;EACnF,iBAAiB,YAAY,UAAU;EACvC,MAAM,QAAQ,SAAiD;GAC7D,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;GAC/B,KACG,IAAI,aAAa,2BAA2B,IAAI,SAAS,WAAW,iBAAiB,MACtF,QAAQ,WAAW,SACnB,QAAQ,WAAW,QAEnB,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;GAC3C,IAAI,IAAI,aAAa,yBAAyB;IAC5C,IAAI,CAAC,OAAO,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;IACrD,OAAO,SAAS,KAAK,MAAM,MAAM;GACnC;GACA,MAAM,aAAa,kCAAkC,KAAK,IAAI,QAAQ;GACtE,IAAI,YAAY;IACd,IAAI,CAAC,SAAS,OAAO,WAAW,EAAE,MAAM,MAAM,OAAO,SAAS,UAC5D,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;IAC3C,MAAM,QAAQ,MAAM,OAAO,SAAS,OAAO,MAAM,UAAU,MAAM,SAAS,WAAW,EAAE;IACvF,IAAI,CAAC,OAAO,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;IACrD,MAAM,QAAQ,MAAMC,QAAAA,QAAG,SAAS,SAASC,UAAAA,QAAK,KAAK,MAAM,WAAW,MAAM,IAAI,CAAC;IAC/E,OAAO,IAAI,SAAS,QAAQ,WAAW,SAAS,OAAO,OAAO,EAC5D,SAAS;KAAE,gBAAgB,MAAM;KAAa,0BAA0B;IAAU,EACpF,CAAC;GACH;GACA,MAAM,QAAQ,2EAA2E,KAAK,IAAI,QAAQ;GAC1G,IAAI,CAAC,OAAO,OAAO,KAAA;GACnB,IAAI,QAAQ,WAAW,SAAS,QAAQ,WAAW,QAAQ,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;GACpG,MAAM,SAAS,YAAY,IAAI,MAAM,IAAI,OAAO,MAAM,EAAE,CAAC;GACzD,IAAI,CAAC,QAAQ,OAAO,SAAS,KAAK,EAAE,OAAO,+BAA+B,GAAG,EAAE,QAAQ,IAAI,CAAC;GAC5F,IAAI,MAAM,OAAO,YAAY,OAAO,SAAS,KAAK,OAAO,MAAM;GAC/D,MAAM,YAAY,MAAM,EAAE,CAAC,MAAM,CAAe;GAChD,MAAM,QAAQ,OAAO,OAAO,SAAS,OAAO,MAAM,cAAc,UAAU,SAAS,SAAS;GAC5F,IAAI,CAAC,OAAO,OAAO,SAAS,KAAK,EAAE,OAAO,0BAA0B,GAAG,EAAE,QAAQ,IAAI,CAAC;GACtF,MAAM,QAAQ,MAAMD,QAAAA,QAAG,SAAS,SAASC,UAAAA,QAAK,KAAK,OAAO,WAAW,MAAM,IAAI,CAAC;GAChF,OAAO,IAAI,SAAS,QAAQ,WAAW,SAAS,OAAO,OAAO,EAC5D,SAAS;IACP,gBAAgB,MAAM;IACtB,iBAAiB;IACjB,0BAA0B;GAC5B,EACF,CAAC;EACH;EACA,QAAQ;GACN,OAAO,MAAM;GACb,aAAa,MAAM;GACnB,WAAW,MAAM;GACjB,YAAY,MAAM;EACpB;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"webCompositions.cjs","names":["path","crypto","createPluginBundlePublication","NATIVE_ENTRY_PATH","HOST_CHANNEL_KEY_TAG","fs"],"sources":["../src/services/webCompositions/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { NATIVE_ENTRY_PATH } from '@agimon-ai/po-web-security';\n\nimport { HOST_CHANNEL_KEY_TAG } from '../../constants/remote';\nimport type { PoApiMount, PoNativeComposition, PoWebComposition } from '../../exports/packageApi';\nimport { createPluginBundlePublication, type PublishedBundle } from '../bundlePublication';\n\n/** One plugin copy in a global web build: its slot entry and the files that entry can load. */\nexport interface GlobalWebBuildPlugin {\n pluginId: string;\n /** The copy's identity: the pluginId for a host plugin, else the pluginId plus a package hash. */\n slot: string;\n packageDir: string;\n isHost: boolean;\n /** The slot entry module below `pluginsDirectory`. */\n entry: string;\n /** Every file below `pluginsDirectory` the entry reaches through static and dynamic imports, plus its stylesheet. */\n files: readonly string[];\n /** The stylesheets of the entry's static import closure. */\n css: readonly string[];\n}\n\n/** One global web build: the shell, the shared plugin composition, and one stylesheet per plugin copy. */\nexport interface GlobalWebBuild {\n /** Content key of the build; equal keys hold identical bytes. */\n key: string;\n /** Signed cockpit shell directory. */\n webDirectory: string;\n /** Shared plugin composition directory, published once for every mount. */\n pluginsDirectory: string;\n /** Every plugin copy in the build, in build order. */\n plugins: readonly GlobalWebBuildPlugin[];\n /** Content-hashed stylesheet paths below `pluginsDirectory`, keyed by slot. */\n pluginStyles: Readonly<Record<string, string>>;\n /** Stylesheet a mount without plugin styles still loads, for the host shell and fallback template. */\n baseStyle?: string;\n}\n\n/** One global native build: a single signed CommonJS composition shared by every mount. */\nexport interface GlobalNativeBuild {\n /** Content key of the build; equal keys hold identical bytes. */\n key: string;\n /** Directory holding exactly `/composition.js`. */\n pluginsDirectory: string;\n}\n\n/** One platform's over-the-air app bundle that `po sync` exported from a source checkout. */\nexport interface NativeAppBuild {\n key: string;\n /** Directory holding `/app.json`, the `/bundle/` parts and the bundle's assets. */\n directory: string;\n /** Native fingerprint of the binaries the bundle runs on. */\n fingerprint: string;\n}\n\n/** A published app bundle as `/api/compositions` offers it to phones. */\nexport interface PoNativeApp {\n id: string;\n revision: number;\n manifestUrl: string;\n rawAssetBaseUrl: string;\n fingerprint: string;\n}\n\nconst absolute = (file: string): string => (file.startsWith('/') ? file : `/${file}`);\n\n/**\n * The file a signed manifest path names below `root`. Manifest segments are percent-encoded\n * (`@` and `+` in pnpm paths); the decoded file must still resolve inside `root`.\n */\nfunction assetFile(root: string, assetPath: string): string | undefined {\n try {\n const base = path.resolve(root);\n const file = path.resolve(base, ...assetPath.slice(1).split('/').map(decodeURIComponent));\n return file.startsWith(`${base}${path.sep}`) && !file.includes('\\0') ? file : undefined;\n } catch {\n return undefined; // A malformed escape names no file.\n }\n}\n\n/** The build's plugin copies for exactly the given slots, in slot order. */\nfunction slotPlugins(build: GlobalWebBuild, slots: readonly string[]): GlobalWebBuildPlugin[] {\n return slots.flatMap((slot) => build.plugins.filter((plugin) => plugin.slot === slot));\n}\n\n/**\n * The stylesheets of exactly the given slots, each loaded once: the stylesheets their code\n * imports first, then each slot's own stylesheet in slot order.\n */\nexport function mountStylePaths(build: GlobalWebBuild, slots: readonly string[]): string[] {\n const paths = slots.map((slot) => build.pluginStyles[slot]).filter((style) => style !== undefined);\n if (paths.length === 0 && build.baseStyle !== undefined) paths.push(build.baseStyle);\n const imported = slotPlugins(build, slots).flatMap((plugin) => plugin.css);\n return [...new Set([...imported, ...paths].map(absolute))];\n}\n\n/** Every file the given slots can load: their reachable files and static stylesheets, each once. */\nexport function mountAssetPaths(build: GlobalWebBuild, slots: readonly string[]): string[] {\n return [\n ...new Set(\n slotPlugins(build, slots)\n .flatMap((plugin) => [...plugin.files, ...plugin.css])\n .map(absolute),\n ),\n ];\n}\n\nconst sha256 = (value: string): string => crypto.createHash('sha256').update(value).digest('hex');\n\n/**\n * Signs the global build once and hands each mount a composition naming its own plugins.\n *\n * Every mount of one build shares a publication id, so the bytes are counted per mount and\n * released only when the last mount leaves or moves to another build.\n */\nexport function createWebCompositions(stateDirectory: string, onNotice: (message: string) => void) {\n const publication = createPluginBundlePublication(stateDirectory, onNotice);\n let shell: PublishedBundle | undefined;\n const mounts = new Map<string, PoWebComposition>();\n const nativeMounts = new Map<string, PoNativeComposition>();\n const apps = new Map<string, PoNativeApp>();\n const references = new Map<string, number>();\n const keyOf = (mount: PoApiMount): string =>\n mount.scope === 'global'\n ? 'global'\n : mount.scope === 'workspace'\n ? `workspace:${mount.workspaceId}`\n : `session:${mount.sessionId}`;\n const release = (id: string): void => {\n const count = (references.get(id) ?? 0) - 1;\n if (count > 0) {\n references.set(id, count);\n return;\n }\n references.delete(id);\n publication.release(id);\n };\n return {\n publishShell(build: GlobalWebBuild): void {\n shell = publication.publish(sha256(`shell:${build.key}`), build.webDirectory);\n if (!shell) throw new Error('Could not publish the global web shell.');\n },\n shellTrust: () =>\n shell === undefined ? undefined : { publicKey: shell.signed.publicKey, revision: shell.signed.manifest.revision },\n publish(\n mount: PoApiMount,\n build: GlobalWebBuild,\n channels: readonly string[],\n slots: readonly string[],\n ): PoWebComposition | undefined {\n const id = sha256(`plugins:${build.key}`);\n const published = publication.publish(id, build.pluginsDirectory);\n if (!published) throw new Error(`Could not publish ${keyOf(mount)} web composition.`);\n const previous = mounts.get(keyOf(mount));\n if (previous?.id !== id) references.set(id, (references.get(id) ?? 0) + 1);\n const revision = published.signed.manifest.revision;\n const base = `/api/web-plugins/${id}/${String(revision)}`;\n const signedPaths = new Set(published.signed.manifest.assets.map((asset) => asset.path));\n const composition: PoWebComposition = {\n id,\n scope: mount.scope,\n revision,\n manifestUrl: `${base}/manifest`,\n rawAssetBaseUrl: `${base}/assets`,\n verifiedAssetBaseUrl: `/verified-plugins/${id}/${String(revision)}`,\n pluginEntries: slotPlugins(build, slots).map((plugin): [string, string] => [\n plugin.slot,\n absolute(plugin.entry),\n ]),\n assetPaths: mountAssetPaths(build, slots).filter((file) => signedPaths.has(file)),\n stylePaths: mountStylePaths(build, slots).filter((style) => signedPaths.has(style)),\n channels: [...channels],\n plugins: [...slots],\n };\n mounts.set(keyOf(mount), composition);\n if (previous !== undefined && previous.id !== id) release(previous.id);\n return composition;\n },\n get: (mount: PoApiMount) => mounts.get(keyOf(mount)),\n remove: (mount: PoApiMount) => {\n const previous = mounts.get(keyOf(mount));\n mounts.delete(keyOf(mount));\n if (previous) release(previous.id);\n },\n /**\n * Publishes the native composition for one mount. Signed under the `native` profile, so it can\n * never be served or verified as a web bundle. Shares reference counting with web publications.\n */\n publishNative(\n mount: PoApiMount,\n build: GlobalNativeBuild,\n channels: readonly string[],\n pluginIds: readonly string[],\n ): PoNativeComposition {\n const id = sha256(`native:${build.key}`);\n const published = publication.publish(id, build.pluginsDirectory, 'native');\n if (!published) throw new Error(`Could not publish ${keyOf(mount)} native composition.`);\n const previous = nativeMounts.get(keyOf(mount));\n if (previous?.id !== id) references.set(id, (references.get(id) ?? 0) + 1);\n const revision = published.signed.manifest.revision;\n const base = `/api/web-plugins/${id}/${String(revision)}`;\n const composition: PoNativeComposition = {\n id,\n scope: mount.scope,\n revision,\n manifestUrl: `${base}/manifest`,\n rawAssetBaseUrl: `${base}/assets`,\n entryPath: NATIVE_ENTRY_PATH,\n channels: [...channels],\n plugins: [...pluginIds],\n };\n nativeMounts.set(keyOf(mount), composition);\n if (previous !== undefined && previous.id !== id) release(previous.id);\n return composition;\n },\n getNative: (mount: PoApiMount) => nativeMounts.get(keyOf(mount)),\n removeNative: (mount: PoApiMount) => {\n const previous = nativeMounts.get(keyOf(mount));\n nativeMounts.delete(keyOf(mount));\n if (previous) release(previous.id);\n },\n /**\n * Publishes one platform's app bundle under the `app` profile, so it can never be served or\n * verified as a composition. A new key releases the previous bundle; a failure leaves it serving.\n */\n publishApp(platform: string, build: NativeAppBuild): void {\n const id = sha256(`app:${platform}:${build.key}`);\n const previous = apps.get(platform);\n if (previous?.id === id) return;\n const published = publication.publish(id, build.directory, 'app');\n if (!published) return;\n const revision = published.signed.manifest.revision;\n const base = `/api/web-plugins/${id}/${String(revision)}`;\n apps.set(platform, {\n id,\n revision,\n manifestUrl: `${base}/manifest`,\n rawAssetBaseUrl: `${base}/assets`,\n fingerprint: build.fingerprint,\n });\n if (previous !== undefined) publication.release(previous.id);\n },\n apps: (): Record<string, PoNativeApp> => Object.fromEntries(apps),\n /**\n * Signs `po/host-channel-key/v1` with the shell publication's signer, the key the pairing QR pins\n * as `s`. Undefined until the shell is published.\n */\n attestHost: (fields: readonly string[]): string | undefined =>\n shell === undefined ? undefined : publication.attest(HOST_CHANNEL_KEY_TAG, fields),\n publicKey: () => publication.publicKey(),\n async request(request: Request): Promise<Response | undefined> {\n const url = new URL(request.url);\n if (\n (url.pathname === '/bundle-manifest.json' || url.pathname.startsWith('/bundle-assets/')) &&\n request.method !== 'GET' &&\n request.method !== 'HEAD'\n )\n return new Response(null, { status: 405 });\n if (url.pathname === '/bundle-manifest.json') {\n if (!shell) return new Response(null, { status: 404 });\n return Response.json(shell.signed);\n }\n const shellAsset = /^\\/bundle-assets\\/(\\d+)(\\/.*)$/u.exec(url.pathname);\n if (shellAsset) {\n if (!shell || Number(shellAsset[1]) !== shell.signed.manifest.revision)\n return new Response(null, { status: 404 });\n const asset = shell.signed.manifest.assets.find((entry) => entry.path === shellAsset[2]);\n if (!asset) return new Response(null, { status: 404 });\n const file = assetFile(shell.assetsDir, asset.path);\n if (!file) return new Response(null, { status: 404 });\n const bytes = await fs.promises.readFile(file);\n return new Response(request.method === 'HEAD' ? null : bytes, {\n headers: { 'content-type': asset.contentType, 'x-content-type-options': 'nosniff' },\n });\n }\n const match = /^\\/api\\/web-plugins\\/([a-f0-9]{64})\\/(\\d+)\\/(manifest|assets(?:\\/.*)?)$/u.exec(url.pathname);\n if (!match) return undefined;\n if (request.method !== 'GET' && request.method !== 'HEAD') return new Response(null, { status: 405 });\n const bundle = publication.get(match[1], Number(match[2]));\n if (!bundle) return Response.json({ error: 'Plugin generation not found.' }, { status: 404 });\n if (match[3] === 'manifest') return Response.json(bundle.signed);\n const assetPath = match[3].slice('assets'.length);\n const asset = bundle.signed.manifest.assets.find((candidate) => candidate.path === assetPath);\n if (!asset) return Response.json({ error: 'Plugin asset not found.' }, { status: 404 });\n const file = assetFile(bundle.assetsDir, asset.path);\n if (!file) return Response.json({ error: 'Plugin asset not found.' }, { status: 404 });\n const bytes = await fs.promises.readFile(file);\n return new Response(request.method === 'HEAD' ? null : bytes, {\n headers: {\n 'content-type': asset.contentType,\n 'cache-control': 'private, max-age=31536000, immutable',\n 'x-content-type-options': 'nosniff',\n },\n });\n },\n close() {\n mounts.clear();\n nativeMounts.clear();\n apps.clear();\n references.clear();\n publication.close();\n },\n };\n}\n"],"mappings":"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"}
|