openlearn-next 0.7.3 → 0.7.4
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/assets/{ActivityWorkspaceWidget-5PzyxF6a.js → ActivityWorkspaceWidget-CMN8elwQ.js} +1 -1
- package/dist/assets/{AdminDirectoryView-BCtmQTEK.js → AdminDirectoryView-Dlz7q-xf.js} +1 -1
- package/dist/assets/{AssessmentCenter-B43ojHG-.js → AssessmentCenter-CaoAI9Ke.js} +1 -1
- package/dist/assets/{BatchPickerModal-CbmjInp7.js → BatchPickerModal-CzutdtIz.js} +1 -1
- package/dist/assets/{ClassesView-DEnMaDOt.js → ClassesView-RkFPpWRu.js} +1 -1
- package/dist/assets/{ClassroomCountdownWidget-CHq4983w.js → ClassroomCountdownWidget-gs42lIGW.js} +1 -1
- package/dist/assets/{CloudDriveModal-D2t3Uyzr.js → CloudDriveModal-C6JtK08M.js} +1 -1
- package/dist/assets/{ComputerLabView-YtnoT5Rb.js → ComputerLabView-Dr-g8FrU.js} +1 -1
- package/dist/assets/{CourseManagement-DOXyJ_j-.js → CourseManagement-DNeaJcT9.js} +1 -1
- package/dist/assets/{CourseWizardModal-Dbi6Caw2.js → CourseWizardModal-vw876VJr.js} +1 -1
- package/dist/assets/{Dashboard-2rLu_ZBy.js → Dashboard-C1iYnfCh.js} +1 -1
- package/dist/assets/{ExportWeightModal-BrmAh6VX.js → ExportWeightModal-CTZ-O_wi.js} +1 -1
- package/dist/assets/{HelpView-CVBrKDrd.js → HelpView-wyKldm_R.js} +1 -1
- package/dist/assets/{ImportLessonsModal-PhHtX31R.js → ImportLessonsModal-C09Z-qtx.js} +1 -1
- package/dist/assets/{InteractiveCoursewareViewer-Vi6lLz7a.js → InteractiveCoursewareViewer-V97euiJF.js} +1 -1
- package/dist/assets/{InteractiveWhiteboard-lTzeayX5.js → InteractiveWhiteboard-COLMh_-w.js} +1 -1
- package/dist/assets/{LazyCourseware-KIt0rGtW.js → LazyCourseware-0iFYlRGj.js} +2 -2
- package/dist/assets/{LazyWhiteboard-CCN3pYcW.js → LazyWhiteboard-CcoBIx6V.js} +2 -2
- package/dist/assets/{LessonEditorView-D_GVhHuM.js → LessonEditorView-BWmtKwmP.js} +1 -1
- package/dist/assets/{LiveClassroomView-BRsZ6QEq.js → LiveClassroomView-CaS1DzSS.js} +1 -1
- package/dist/assets/{NotificationDetailModal-tY5rxYCN.js → NotificationDetailModal-4MdNF4tw.js} +1 -1
- package/dist/assets/{PluginView-CedpAW6d.js → PluginView-BqTbAp30.js} +1 -1
- package/dist/assets/{QuizGeneratorModal-bUA002kP.js → QuizGeneratorModal-xTq22vDq.js} +1 -1
- package/dist/assets/{StudentAssignmentView-BhAz6Hfk.js → StudentAssignmentView-Cy_JdPYB.js} +1 -1
- package/dist/assets/{StudentLessonView-DC1yX4kG.js → StudentLessonView-B3fKejCU.js} +1 -1
- package/dist/assets/{StudentView-PHKxIV9T.js → StudentView-CpJsG7FT.js} +2 -2
- package/dist/assets/{SystemResourceLibraryModal-DgXYe6_x.js → SystemResourceLibraryModal-YZ85mv_v.js} +1 -1
- package/dist/assets/{TeacherView-DscJtOJ9.js → TeacherView-B3zhzd5g.js} +2 -2
- package/dist/assets/{TimetableView-D2ozoj_D.js → TimetableView-D_4XmrRN.js} +1 -1
- package/dist/assets/{index-2676X8YB.js → index-BzXADCXZ.js} +5 -5
- package/dist/assets/{pluginParsers-e4PSm2Iy.js → pluginParsers-PzgFzxS9.js} +1 -1
- package/dist/cli-migrator.cjs +18 -1
- package/dist/index.html +1 -1
- package/dist/server.cjs +339 -249
- package/package.json +3 -2
package/dist/server.cjs
CHANGED
|
@@ -1188,12 +1188,12 @@ var init_manifest_schema = __esm({
|
|
|
1188
1188
|
// packages/core/di/api-key-crypto.ts
|
|
1189
1189
|
function resolveEncryptionKeyStorageDir(env = process.env, cwd = process.cwd()) {
|
|
1190
1190
|
if (env.OPENLEARN_DATA_DIR?.trim()) {
|
|
1191
|
-
return
|
|
1191
|
+
return import_node_path4.default.join(import_node_path4.default.resolve(env.OPENLEARN_DATA_DIR.trim()), "storage");
|
|
1192
1192
|
}
|
|
1193
1193
|
if (env.OPENLEARN_DB_PATH?.trim()) {
|
|
1194
|
-
return
|
|
1194
|
+
return import_node_path4.default.join(import_node_path4.default.dirname(import_node_path4.default.resolve(env.OPENLEARN_DB_PATH.trim())), "storage");
|
|
1195
1195
|
}
|
|
1196
|
-
return
|
|
1196
|
+
return import_node_path4.default.join(cwd, "storage");
|
|
1197
1197
|
}
|
|
1198
1198
|
function normalizeKeyHex(raw) {
|
|
1199
1199
|
let value = raw.trim();
|
|
@@ -1214,11 +1214,11 @@ function readProcessEnvKey(env) {
|
|
|
1214
1214
|
return normalizeKeyHex(raw);
|
|
1215
1215
|
}
|
|
1216
1216
|
function readDotEnvKey(cwd) {
|
|
1217
|
-
const envPath =
|
|
1218
|
-
if (!
|
|
1217
|
+
const envPath = import_node_path4.default.resolve(cwd, ".env");
|
|
1218
|
+
if (!import_node_fs4.default.existsSync(envPath)) return null;
|
|
1219
1219
|
let content = "";
|
|
1220
1220
|
try {
|
|
1221
|
-
content =
|
|
1221
|
+
content = import_node_fs4.default.readFileSync(envPath, "utf8");
|
|
1222
1222
|
} catch {
|
|
1223
1223
|
return null;
|
|
1224
1224
|
}
|
|
@@ -1227,11 +1227,11 @@ function readDotEnvKey(cwd) {
|
|
|
1227
1227
|
return normalizeKeyHex(match[1] ?? "");
|
|
1228
1228
|
}
|
|
1229
1229
|
function keyFilePath(storageDir) {
|
|
1230
|
-
return
|
|
1230
|
+
return import_node_path4.default.join(storageDir, ENCRYPTION_KEY_FILE_NAME);
|
|
1231
1231
|
}
|
|
1232
1232
|
function chmodKeyFile(filePath) {
|
|
1233
1233
|
try {
|
|
1234
|
-
|
|
1234
|
+
import_node_fs4.default.chmodSync(filePath, 384);
|
|
1235
1235
|
} catch (err) {
|
|
1236
1236
|
throw new Error(
|
|
1237
1237
|
`\u65E0\u6CD5\u5C06 ${filePath} \u7684\u6743\u9650\u8BBE\u4E3A 0600\uFF08${err instanceof Error ? err.message : err}\uFF09\u3002\u5DF2\u62D2\u7EDD\u7EE7\u7EED\u4F7F\u7528\u8BE5\u5BC6\u94A5\u6587\u4EF6\u3002`
|
|
@@ -1239,8 +1239,8 @@ function chmodKeyFile(filePath) {
|
|
|
1239
1239
|
}
|
|
1240
1240
|
}
|
|
1241
1241
|
function readKeyFile(filePath) {
|
|
1242
|
-
if (!
|
|
1243
|
-
const normalized = normalizeKeyHex(
|
|
1242
|
+
if (!import_node_fs4.default.existsSync(filePath)) return null;
|
|
1243
|
+
const normalized = normalizeKeyHex(import_node_fs4.default.readFileSync(filePath, "utf8"));
|
|
1244
1244
|
if (!normalized) {
|
|
1245
1245
|
throw new EncryptionKeyInvalidError(
|
|
1246
1246
|
`\u5BC6\u94A5\u6587\u4EF6 ${filePath} \u662F\u7A7A\u7684\u3002\u8BF7\u5220\u6389\u5B83\uFF0C\u6216\u5199\u5165\u539F\u6765\u7684 64 \u4F4D hex\u3002\u4E0D\u8981\u8BA9\u7CFB\u7EDF\u53E6\u9020\u4E00\u628A\u94A5\u5319\u3002`
|
|
@@ -1253,9 +1253,9 @@ function keysEqual(left, right) {
|
|
|
1253
1253
|
return import_node_crypto.default.timingSafeEqual(Buffer.from(left, "hex"), Buffer.from(right, "hex"));
|
|
1254
1254
|
}
|
|
1255
1255
|
function persistGeneratedKey(filePath, generated) {
|
|
1256
|
-
|
|
1256
|
+
import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(filePath), { recursive: true });
|
|
1257
1257
|
try {
|
|
1258
|
-
|
|
1258
|
+
import_node_fs4.default.writeFileSync(filePath, `${generated}
|
|
1259
1259
|
`, { mode: 384, flag: "wx" });
|
|
1260
1260
|
} catch (err) {
|
|
1261
1261
|
const code = err && typeof err === "object" && "code" in err ? String(err.code) : "";
|
|
@@ -1291,7 +1291,7 @@ function resolveEncryptionKey(options = {}) {
|
|
|
1291
1291
|
const fromDotEnv = fromProcess ? null : readDotEnvKey(cwd);
|
|
1292
1292
|
const configured = fromProcess ?? fromDotEnv;
|
|
1293
1293
|
const configuredSource = fromProcess ? "env" : fromDotEnv ? "env-file" : null;
|
|
1294
|
-
const fileKey =
|
|
1294
|
+
const fileKey = import_node_fs4.default.existsSync(filePath) ? readKeyFile(filePath) : null;
|
|
1295
1295
|
if (configured && fileKey && !keysEqual(configured, fileKey)) {
|
|
1296
1296
|
throw new EncryptionKeyMismatchError(filePath);
|
|
1297
1297
|
}
|
|
@@ -1400,13 +1400,13 @@ function encryptStoredPlaintextApiKeys(db2) {
|
|
|
1400
1400
|
}
|
|
1401
1401
|
return { updated, skipped };
|
|
1402
1402
|
}
|
|
1403
|
-
var import_node_crypto,
|
|
1403
|
+
var import_node_crypto, import_node_fs4, import_node_path4, ENCRYPTION_KEY_FILE_NAME, AUTO_GENERATED_KEY_WARNING, HEX_64, EncryptionKeyInvalidError, EncryptionKeyMismatchError, cached;
|
|
1404
1404
|
var init_api_key_crypto = __esm({
|
|
1405
1405
|
"packages/core/di/api-key-crypto.ts"() {
|
|
1406
1406
|
"use strict";
|
|
1407
1407
|
import_node_crypto = __toESM(require("node:crypto"), 1);
|
|
1408
|
-
|
|
1409
|
-
|
|
1408
|
+
import_node_fs4 = __toESM(require("node:fs"), 1);
|
|
1409
|
+
import_node_path4 = __toESM(require("node:path"), 1);
|
|
1410
1410
|
ENCRYPTION_KEY_FILE_NAME = "encryption-key";
|
|
1411
1411
|
AUTO_GENERATED_KEY_WARNING = "\u6B63\u5728\u4F7F\u7528\u81EA\u52A8\u751F\u6210\u7684\u52A0\u5BC6\u5BC6\u94A5\uFF08storage/encryption-key\uFF0C\u4E0E bootstrap-credentials.txt \u540C\u76EE\u5F55\uFF0C\u6743\u9650 600\uFF09\u3002\u5EFA\u8BAE\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF ENCRYPTION_KEY \u4E3A\u8BE5\u6587\u4EF6\u4E2D\u7684 64 \u4F4D hex\uFF0C\u5E76\u79BB\u7EBF\u5907\u4EFD\u8BE5\u6587\u4EF6\u3002site-backup \u4E0D\u5305\u542B\u6B64\u6587\u4EF6\u3002\u4E22\u5931\u540E\u65E0\u6CD5\u89E3\u5BC6 AI Provider API Key \u4E0E\u73ED\u7EA7\u4E34\u65F6\u53E3\u4EE4\u3002";
|
|
1412
1412
|
HEX_64 = /^[0-9a-fA-F]{64}$/;
|
|
@@ -1921,49 +1921,49 @@ function getMaxUncompressedSize() {
|
|
|
1921
1921
|
}
|
|
1922
1922
|
function isInsideRoot(root, target) {
|
|
1923
1923
|
if (!root || !target) return false;
|
|
1924
|
-
const rel =
|
|
1924
|
+
const rel = path8.relative(path8.resolve(root), path8.resolve(target));
|
|
1925
1925
|
if (rel === "") return false;
|
|
1926
|
-
if (
|
|
1927
|
-
return rel !== ".." && !rel.startsWith(".." +
|
|
1926
|
+
if (path8.isAbsolute(rel)) return false;
|
|
1927
|
+
return rel !== ".." && !rel.startsWith(".." + path8.sep);
|
|
1928
1928
|
}
|
|
1929
1929
|
function realPathOrNull(target) {
|
|
1930
1930
|
try {
|
|
1931
|
-
return
|
|
1931
|
+
return fs7.realpathSync(target);
|
|
1932
1932
|
} catch {
|
|
1933
1933
|
return null;
|
|
1934
1934
|
}
|
|
1935
1935
|
}
|
|
1936
1936
|
function realPathOrSelf(target) {
|
|
1937
|
-
return realPathOrNull(target) ??
|
|
1937
|
+
return realPathOrNull(target) ?? path8.resolve(target);
|
|
1938
1938
|
}
|
|
1939
1939
|
function getMaxUploadBodyBytes() {
|
|
1940
1940
|
return getMaxUncompressedSize();
|
|
1941
1941
|
}
|
|
1942
1942
|
function readZipBufferFromPath(zipPath) {
|
|
1943
|
-
if (!
|
|
1943
|
+
if (!fs7.existsSync(zipPath)) {
|
|
1944
1944
|
throw new Error(`[install-utils] ZIP \u6587\u4EF6\u4E0D\u5B58\u5728: ${zipPath}`);
|
|
1945
1945
|
}
|
|
1946
|
-
return
|
|
1946
|
+
return fs7.readFileSync(zipPath);
|
|
1947
1947
|
}
|
|
1948
1948
|
function writeBufferToTempZip(buffer, prefix = "plugin-upload-") {
|
|
1949
1949
|
if (!Buffer.isBuffer(buffer) || buffer.length === 0) {
|
|
1950
1950
|
throw new Error("[install-utils] \u7A7A\u7684 ZIP Buffer");
|
|
1951
1951
|
}
|
|
1952
|
-
const zipPath =
|
|
1953
|
-
|
|
1952
|
+
const zipPath = path8.join(os2.tmpdir(), `${prefix}${(0, import_uuid6.v7)()}.zip`);
|
|
1953
|
+
fs7.writeFileSync(zipPath, buffer);
|
|
1954
1954
|
return zipPath;
|
|
1955
1955
|
}
|
|
1956
1956
|
function removeTempZip(zipPath) {
|
|
1957
1957
|
if (!zipPath) return;
|
|
1958
1958
|
try {
|
|
1959
|
-
|
|
1959
|
+
fs7.rmSync(zipPath, { force: true });
|
|
1960
1960
|
} catch {
|
|
1961
1961
|
}
|
|
1962
1962
|
}
|
|
1963
1963
|
function removeTempFile(filePath) {
|
|
1964
1964
|
if (!filePath) return;
|
|
1965
1965
|
try {
|
|
1966
|
-
|
|
1966
|
+
fs7.rmSync(filePath, { force: true });
|
|
1967
1967
|
} catch {
|
|
1968
1968
|
}
|
|
1969
1969
|
}
|
|
@@ -2209,13 +2209,13 @@ async function bundlePlugin(entryCode, resolveDir, outfile) {
|
|
|
2209
2209
|
const esbuild = await import("esbuild");
|
|
2210
2210
|
const allowedRoots = [];
|
|
2211
2211
|
const allowRoot = (dir) => {
|
|
2212
|
-
const lexical =
|
|
2212
|
+
const lexical = path8.resolve(dir);
|
|
2213
2213
|
if (allowedRoots.some((r) => r.lexical === lexical)) return;
|
|
2214
2214
|
allowedRoots.push({ lexical, real: realPathOrSelf(lexical) });
|
|
2215
2215
|
};
|
|
2216
2216
|
allowRoot(resolveDir);
|
|
2217
2217
|
const isPermittedPath = (target) => {
|
|
2218
|
-
const resolved =
|
|
2218
|
+
const resolved = path8.resolve(target);
|
|
2219
2219
|
if (allowedRoots.some((r) => isInsideRoot(r.lexical, resolved))) return true;
|
|
2220
2220
|
const real = realPathOrNull(resolved);
|
|
2221
2221
|
if (!real) return false;
|
|
@@ -2242,14 +2242,14 @@ async function bundlePlugin(entryCode, resolveDir, outfile) {
|
|
|
2242
2242
|
setup(build) {
|
|
2243
2243
|
build.onResolve({ filter: /.*/ }, (args) => {
|
|
2244
2244
|
if (args.path.includes("core/di/interfaces")) {
|
|
2245
|
-
const tsPath =
|
|
2246
|
-
if (
|
|
2247
|
-
allowRoot(
|
|
2245
|
+
const tsPath = path8.resolve(process.cwd(), "packages/core/di/interfaces.ts");
|
|
2246
|
+
if (fs7.existsSync(tsPath)) {
|
|
2247
|
+
allowRoot(path8.dirname(tsPath));
|
|
2248
2248
|
return { path: tsPath };
|
|
2249
2249
|
}
|
|
2250
|
-
const jsPath =
|
|
2251
|
-
if (
|
|
2252
|
-
allowRoot(
|
|
2250
|
+
const jsPath = path8.resolve(process.cwd(), "packages/core/di/interfaces.js");
|
|
2251
|
+
if (fs7.existsSync(jsPath)) {
|
|
2252
|
+
allowRoot(path8.dirname(jsPath));
|
|
2253
2253
|
return { path: jsPath };
|
|
2254
2254
|
}
|
|
2255
2255
|
}
|
|
@@ -2263,8 +2263,8 @@ async function bundlePlugin(entryCode, resolveDir, outfile) {
|
|
|
2263
2263
|
};
|
|
2264
2264
|
}
|
|
2265
2265
|
if (args.path.startsWith(".")) {
|
|
2266
|
-
const importerDir = args.resolveDir ||
|
|
2267
|
-
const candidate =
|
|
2266
|
+
const importerDir = args.resolveDir || path8.resolve(resolveDir);
|
|
2267
|
+
const candidate = path8.resolve(importerDir, args.path);
|
|
2268
2268
|
if (!isPermittedPath(candidate)) {
|
|
2269
2269
|
return {
|
|
2270
2270
|
errors: [
|
|
@@ -2302,7 +2302,7 @@ async function bundlePlugin(entryCode, resolveDir, outfile) {
|
|
|
2302
2302
|
]
|
|
2303
2303
|
});
|
|
2304
2304
|
if (outfile !== void 0) {
|
|
2305
|
-
assertPluginCodeSafe(
|
|
2305
|
+
assertPluginCodeSafe(fs7.readFileSync(outfile, "utf-8"));
|
|
2306
2306
|
return void 0;
|
|
2307
2307
|
}
|
|
2308
2308
|
const bundled = result.outputFiles?.[0]?.text;
|
|
@@ -2316,8 +2316,8 @@ async function extractZipEntries(zip, destDir, maxTotalBytes) {
|
|
|
2316
2316
|
let totalBytes = 0;
|
|
2317
2317
|
for (const [name, file] of Object.entries(zip.files)) {
|
|
2318
2318
|
if (file.dir) continue;
|
|
2319
|
-
const filePath =
|
|
2320
|
-
|
|
2319
|
+
const filePath = path8.join(destDir, name);
|
|
2320
|
+
fs7.mkdirSync(path8.dirname(filePath), { recursive: true });
|
|
2321
2321
|
const content = await file.async("nodebuffer");
|
|
2322
2322
|
totalBytes += content.length;
|
|
2323
2323
|
if (totalBytes > maxTotalBytes) {
|
|
@@ -2325,7 +2325,7 @@ async function extractZipEntries(zip, destDir, maxTotalBytes) {
|
|
|
2325
2325
|
`ZIP bomb prevention: actual uncompressed size ${totalBytes} bytes exceeds limit of ${maxTotalBytes} bytes`
|
|
2326
2326
|
);
|
|
2327
2327
|
}
|
|
2328
|
-
|
|
2328
|
+
fs7.writeFileSync(filePath, content);
|
|
2329
2329
|
}
|
|
2330
2330
|
}
|
|
2331
2331
|
async function validateAndBundleZip(zipPath) {
|
|
@@ -2373,16 +2373,16 @@ async function validateAndBundleZip(zipPath) {
|
|
|
2373
2373
|
throw new Error(`Entry file "${manifest.main}" specified in manifest not found in ZIP package`);
|
|
2374
2374
|
}
|
|
2375
2375
|
const entryCode = await entryFile.async("string");
|
|
2376
|
-
const tmpDir =
|
|
2376
|
+
const tmpDir = path8.join(os2.tmpdir(), `plugin-build-${(0, import_uuid6.v7)()}`);
|
|
2377
2377
|
try {
|
|
2378
|
-
|
|
2378
|
+
fs7.mkdirSync(tmpDir, { recursive: true });
|
|
2379
2379
|
await extractZipEntries(zip, tmpDir, getMaxUncompressedSize());
|
|
2380
|
-
const bundledPath =
|
|
2380
|
+
const bundledPath = path8.join(os2.tmpdir(), `plugin-bundle-${(0, import_uuid6.v7)()}.mjs`);
|
|
2381
2381
|
await bundlePlugin(entryCode, tmpDir, bundledPath);
|
|
2382
2382
|
return { manifest, bundledPath, entryFileName: resolvedMain, package: zip };
|
|
2383
2383
|
} finally {
|
|
2384
2384
|
try {
|
|
2385
|
-
|
|
2385
|
+
fs7.rmSync(tmpDir, { recursive: true, force: true });
|
|
2386
2386
|
} catch {
|
|
2387
2387
|
}
|
|
2388
2388
|
}
|
|
@@ -2390,14 +2390,14 @@ async function validateAndBundleZip(zipPath) {
|
|
|
2390
2390
|
async function extractManifestFromBundle(_bundledCode) {
|
|
2391
2391
|
throw new Error("extractManifestFromBundle is not yet implemented");
|
|
2392
2392
|
}
|
|
2393
|
-
var import_jszip,
|
|
2393
|
+
var import_jszip, fs7, os2, path8, import_uuid6, PluginCodeSecurityError, LEX_SCAN_LIMIT;
|
|
2394
2394
|
var init_install_utils = __esm({
|
|
2395
2395
|
"packages/core/esm-loader/install-utils.ts"() {
|
|
2396
2396
|
"use strict";
|
|
2397
2397
|
import_jszip = __toESM(require("jszip"), 1);
|
|
2398
|
-
|
|
2398
|
+
fs7 = __toESM(require("fs"), 1);
|
|
2399
2399
|
os2 = __toESM(require("os"), 1);
|
|
2400
|
-
|
|
2400
|
+
path8 = __toESM(require("path"), 1);
|
|
2401
2401
|
import_uuid6 = require("uuid");
|
|
2402
2402
|
init_manifest_schema();
|
|
2403
2403
|
PluginCodeSecurityError = class extends Error {
|
|
@@ -2472,7 +2472,7 @@ var init_redaction = __esm({
|
|
|
2472
2472
|
// packages/core/observability/rotating-file-stream.ts
|
|
2473
2473
|
function removeIfExists(filePath) {
|
|
2474
2474
|
try {
|
|
2475
|
-
|
|
2475
|
+
import_node_fs5.default.unlinkSync(filePath);
|
|
2476
2476
|
} catch {
|
|
2477
2477
|
}
|
|
2478
2478
|
}
|
|
@@ -2491,7 +2491,7 @@ function unregisterStream(stream) {
|
|
|
2491
2491
|
function safeRename(from, to) {
|
|
2492
2492
|
try {
|
|
2493
2493
|
removeIfExists(to);
|
|
2494
|
-
|
|
2494
|
+
import_node_fs5.default.renameSync(from, to);
|
|
2495
2495
|
return true;
|
|
2496
2496
|
} catch {
|
|
2497
2497
|
return false;
|
|
@@ -2516,12 +2516,12 @@ function resolveAuditRotationConfig(env = process.env) {
|
|
|
2516
2516
|
const maxFiles = Math.floor(readPositiveInt(env.AUDIT_LOG_MAX_FILES, 20, "AUDIT_LOG_MAX_FILES"));
|
|
2517
2517
|
return { maxBytes: Math.floor(maxSizeMb * 1024 * 1024), maxFiles };
|
|
2518
2518
|
}
|
|
2519
|
-
var
|
|
2519
|
+
var import_node_fs5, import_node_path5, import_node_stream, activeStreams, exitHookInstalled, RotatingFileStream;
|
|
2520
2520
|
var init_rotating_file_stream = __esm({
|
|
2521
2521
|
"packages/core/observability/rotating-file-stream.ts"() {
|
|
2522
2522
|
"use strict";
|
|
2523
|
-
|
|
2524
|
-
|
|
2523
|
+
import_node_fs5 = __toESM(require("node:fs"), 1);
|
|
2524
|
+
import_node_path5 = __toESM(require("node:path"), 1);
|
|
2525
2525
|
import_node_stream = require("node:stream");
|
|
2526
2526
|
activeStreams = /* @__PURE__ */ new Set();
|
|
2527
2527
|
exitHookInstalled = false;
|
|
@@ -2535,7 +2535,7 @@ var init_rotating_file_stream = __esm({
|
|
|
2535
2535
|
this.filePath = options.filePath;
|
|
2536
2536
|
this.maxBytes = options.maxBytes;
|
|
2537
2537
|
this.maxFiles = options.maxFiles;
|
|
2538
|
-
this.dir =
|
|
2538
|
+
this.dir = import_node_path5.default.dirname(this.filePath);
|
|
2539
2539
|
}
|
|
2540
2540
|
/** 当前活动文件路径(测试与排障用) */
|
|
2541
2541
|
get activeFilePath() {
|
|
@@ -2556,7 +2556,7 @@ var init_rotating_file_stream = __esm({
|
|
|
2556
2556
|
if (this.currentSize > 0 && this.currentSize + buf.length > this.maxBytes) {
|
|
2557
2557
|
this.rotate();
|
|
2558
2558
|
}
|
|
2559
|
-
const written =
|
|
2559
|
+
const written = import_node_fs5.default.writeSync(this.fd, buf, 0, buf.length, null);
|
|
2560
2560
|
this.currentSize += written;
|
|
2561
2561
|
callback();
|
|
2562
2562
|
} catch (err) {
|
|
@@ -2576,7 +2576,7 @@ var init_rotating_file_stream = __esm({
|
|
|
2576
2576
|
closeFd() {
|
|
2577
2577
|
if (this.fd !== null) {
|
|
2578
2578
|
try {
|
|
2579
|
-
|
|
2579
|
+
import_node_fs5.default.closeSync(this.fd);
|
|
2580
2580
|
} catch {
|
|
2581
2581
|
}
|
|
2582
2582
|
this.fd = null;
|
|
@@ -2585,11 +2585,11 @@ var init_rotating_file_stream = __esm({
|
|
|
2585
2585
|
}
|
|
2586
2586
|
ensureOpen() {
|
|
2587
2587
|
if (this.fd !== null) return;
|
|
2588
|
-
|
|
2589
|
-
this.fd =
|
|
2588
|
+
import_node_fs5.default.mkdirSync(this.dir, { recursive: true });
|
|
2589
|
+
this.fd = import_node_fs5.default.openSync(this.filePath, "a");
|
|
2590
2590
|
registerStream(this);
|
|
2591
2591
|
try {
|
|
2592
|
-
this.currentSize =
|
|
2592
|
+
this.currentSize = import_node_fs5.default.statSync(this.filePath).size;
|
|
2593
2593
|
} catch {
|
|
2594
2594
|
this.currentSize = 0;
|
|
2595
2595
|
}
|
|
@@ -2603,13 +2603,13 @@ var init_rotating_file_stream = __esm({
|
|
|
2603
2603
|
this.closeFd();
|
|
2604
2604
|
for (let i = this.maxFiles - 1; i >= 1; i -= 1) {
|
|
2605
2605
|
const from = this.historyFilePath(i);
|
|
2606
|
-
if (
|
|
2606
|
+
if (import_node_fs5.default.existsSync(from)) {
|
|
2607
2607
|
safeRename(from, this.historyFilePath(i + 1));
|
|
2608
2608
|
}
|
|
2609
2609
|
}
|
|
2610
2610
|
safeRename(this.filePath, this.historyFilePath(1));
|
|
2611
2611
|
this.currentSize = 0;
|
|
2612
|
-
this.fd =
|
|
2612
|
+
this.fd = import_node_fs5.default.openSync(this.filePath, "a");
|
|
2613
2613
|
}
|
|
2614
2614
|
};
|
|
2615
2615
|
}
|
|
@@ -2640,28 +2640,28 @@ function consoleStream() {
|
|
|
2640
2640
|
function createLogger(component) {
|
|
2641
2641
|
return logger.child({ component });
|
|
2642
2642
|
}
|
|
2643
|
-
var import_pino,
|
|
2643
|
+
var import_pino, import_node_fs6, import_node_path6, defaultLevel, consoleLevel, fileLevel, rootLevel, logDir, logFile, streams, fileLogging, logger;
|
|
2644
2644
|
var init_logger = __esm({
|
|
2645
2645
|
"packages/core/observability/logger.ts"() {
|
|
2646
2646
|
"use strict";
|
|
2647
2647
|
import_pino = __toESM(require("pino"), 1);
|
|
2648
|
-
|
|
2649
|
-
|
|
2648
|
+
import_node_fs6 = __toESM(require("node:fs"), 1);
|
|
2649
|
+
import_node_path6 = __toESM(require("node:path"), 1);
|
|
2650
2650
|
init_redaction();
|
|
2651
2651
|
init_rotating_file_stream();
|
|
2652
2652
|
defaultLevel = process.env.LOG_LEVEL || (process.env.HTTP_ACCESS_LOG === "all" ? "debug" : process.env.NODE_ENV === "production" ? "info" : "debug");
|
|
2653
2653
|
consoleLevel = process.env.LOG_CONSOLE_LEVEL || defaultLevel;
|
|
2654
2654
|
fileLevel = defaultLevel;
|
|
2655
2655
|
rootLevel = minPinoLevel(consoleLevel, fileLevel);
|
|
2656
|
-
logDir =
|
|
2657
|
-
logFile =
|
|
2656
|
+
logDir = import_node_path6.default.resolve(process.env.LOG_DIR || import_node_path6.default.join(process.cwd(), "logs"));
|
|
2657
|
+
logFile = import_node_path6.default.join(logDir, "openlearn.log");
|
|
2658
2658
|
streams = [
|
|
2659
2659
|
{ level: consoleLevel, stream: consoleStream() }
|
|
2660
2660
|
];
|
|
2661
2661
|
fileLogging = false;
|
|
2662
2662
|
try {
|
|
2663
|
-
if (!
|
|
2664
|
-
|
|
2663
|
+
if (!import_node_fs6.default.existsSync(logDir)) import_node_fs6.default.mkdirSync(logDir, { recursive: true });
|
|
2664
|
+
import_node_fs6.default.accessSync(logDir, import_node_fs6.default.constants.W_OK);
|
|
2665
2665
|
fileLogging = true;
|
|
2666
2666
|
} catch (err) {
|
|
2667
2667
|
console.warn(`[logger] \u65E0\u6CD5\u5199\u5165\u65E5\u5FD7\u76EE\u5F55 ${logDir}\uFF0C\u4EC5\u8F93\u51FA\u5230 stdout\u3002`, err instanceof Error ? err.message : err);
|
|
@@ -2787,15 +2787,15 @@ function buildAuditLogger() {
|
|
|
2787
2787
|
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
2788
2788
|
return (0, import_pino2.default)({ level: "silent" }, import_pino2.default.destination(1));
|
|
2789
2789
|
}
|
|
2790
|
-
const logDir2 =
|
|
2790
|
+
const logDir2 = import_node_path7.default.resolve(process.env.LOG_DIR || import_node_path7.default.join(process.cwd(), "logs"));
|
|
2791
2791
|
try {
|
|
2792
|
-
if (!
|
|
2793
|
-
|
|
2792
|
+
if (!import_node_fs7.default.existsSync(logDir2)) import_node_fs7.default.mkdirSync(logDir2, { recursive: true });
|
|
2793
|
+
import_node_fs7.default.accessSync(logDir2, import_node_fs7.default.constants.W_OK);
|
|
2794
2794
|
const rotation = resolveAuditRotationConfig();
|
|
2795
2795
|
streams2.push({
|
|
2796
2796
|
level: "info",
|
|
2797
2797
|
stream: new RotatingFileStream({
|
|
2798
|
-
filePath:
|
|
2798
|
+
filePath: import_node_path7.default.join(logDir2, "audit.log"),
|
|
2799
2799
|
maxBytes: rotation.maxBytes,
|
|
2800
2800
|
maxFiles: rotation.maxFiles
|
|
2801
2801
|
})
|
|
@@ -2820,12 +2820,12 @@ function recordSecurityAudit(event, fields = {}) {
|
|
|
2820
2820
|
}
|
|
2821
2821
|
return record;
|
|
2822
2822
|
}
|
|
2823
|
-
var
|
|
2823
|
+
var import_node_fs7, import_node_path7, import_pino2, testSink, auditLogger;
|
|
2824
2824
|
var init_audit_log = __esm({
|
|
2825
2825
|
"packages/core/observability/audit-log.ts"() {
|
|
2826
2826
|
"use strict";
|
|
2827
|
-
|
|
2828
|
-
|
|
2827
|
+
import_node_fs7 = __toESM(require("node:fs"), 1);
|
|
2828
|
+
import_node_path7 = __toESM(require("node:path"), 1);
|
|
2829
2829
|
import_pino2 = __toESM(require("pino"), 1);
|
|
2830
2830
|
init_logger();
|
|
2831
2831
|
init_request_context();
|
|
@@ -3875,9 +3875,9 @@ var init_http_router = __esm({
|
|
|
3875
3875
|
/**
|
|
3876
3876
|
* 注册指定动词与路径的 Handler
|
|
3877
3877
|
*/
|
|
3878
|
-
route(method,
|
|
3878
|
+
route(method, path46, handler, options = {}) {
|
|
3879
3879
|
const upperMethod = method.toUpperCase();
|
|
3880
|
-
const normalized =
|
|
3880
|
+
const normalized = path46.startsWith("/") ? path46 : "/" + path46;
|
|
3881
3881
|
const { regex, paramNames } = compileRoutePattern(normalized);
|
|
3882
3882
|
this.warnIfUnscoped(upperMethod, normalized, options);
|
|
3883
3883
|
this.routes.push({
|
|
@@ -3890,20 +3890,20 @@ var init_http_router = __esm({
|
|
|
3890
3890
|
scope: options.scope
|
|
3891
3891
|
});
|
|
3892
3892
|
}
|
|
3893
|
-
get(
|
|
3894
|
-
this.route("GET",
|
|
3893
|
+
get(path46, handler, options) {
|
|
3894
|
+
this.route("GET", path46, handler, options);
|
|
3895
3895
|
}
|
|
3896
|
-
post(
|
|
3897
|
-
this.route("POST",
|
|
3896
|
+
post(path46, handler, options) {
|
|
3897
|
+
this.route("POST", path46, handler, options);
|
|
3898
3898
|
}
|
|
3899
|
-
put(
|
|
3900
|
-
this.route("PUT",
|
|
3899
|
+
put(path46, handler, options) {
|
|
3900
|
+
this.route("PUT", path46, handler, options);
|
|
3901
3901
|
}
|
|
3902
|
-
patch(
|
|
3903
|
-
this.route("PATCH",
|
|
3902
|
+
patch(path46, handler, options) {
|
|
3903
|
+
this.route("PATCH", path46, handler, options);
|
|
3904
3904
|
}
|
|
3905
|
-
delete(
|
|
3906
|
-
this.route("DELETE",
|
|
3905
|
+
delete(path46, handler, options) {
|
|
3906
|
+
this.route("DELETE", path46, handler, options);
|
|
3907
3907
|
}
|
|
3908
3908
|
warnIfUnscoped(method, pattern, options) {
|
|
3909
3909
|
if (options.scope) return;
|
|
@@ -3918,19 +3918,19 @@ var init_http_router = __esm({
|
|
|
3918
3918
|
}
|
|
3919
3919
|
stream(methodOrPath, pathOrHandler, maybeHandler) {
|
|
3920
3920
|
if (typeof pathOrHandler === "function") {
|
|
3921
|
-
const
|
|
3921
|
+
const path46 = methodOrPath;
|
|
3922
3922
|
const handler = pathOrHandler;
|
|
3923
|
-
this.registerStream("GET",
|
|
3924
|
-
this.registerStream("POST",
|
|
3923
|
+
this.registerStream("GET", path46, handler);
|
|
3924
|
+
this.registerStream("POST", path46, handler);
|
|
3925
3925
|
} else {
|
|
3926
3926
|
const method = methodOrPath.toUpperCase();
|
|
3927
|
-
const
|
|
3927
|
+
const path46 = pathOrHandler;
|
|
3928
3928
|
const handler = maybeHandler;
|
|
3929
|
-
this.registerStream(method,
|
|
3929
|
+
this.registerStream(method, path46, handler);
|
|
3930
3930
|
}
|
|
3931
3931
|
}
|
|
3932
|
-
registerStream(method,
|
|
3933
|
-
const normalized =
|
|
3932
|
+
registerStream(method, path46, handler) {
|
|
3933
|
+
const normalized = path46.startsWith("/") ? path46 : "/" + path46;
|
|
3934
3934
|
const { regex, paramNames } = compileRoutePattern(normalized);
|
|
3935
3935
|
this.routes.push({
|
|
3936
3936
|
method: method.toUpperCase(),
|
|
@@ -3944,9 +3944,9 @@ var init_http_router = __esm({
|
|
|
3944
3944
|
/**
|
|
3945
3945
|
* 检查指定动词与路径是否为流式路由
|
|
3946
3946
|
*/
|
|
3947
|
-
isStream(method,
|
|
3947
|
+
isStream(method, path46) {
|
|
3948
3948
|
const upperMethod = method.toUpperCase();
|
|
3949
|
-
const normalized =
|
|
3949
|
+
const normalized = path46.startsWith("/") ? path46 : "/" + path46;
|
|
3950
3950
|
for (const entry of this.routes) {
|
|
3951
3951
|
if (entry.method !== upperMethod) continue;
|
|
3952
3952
|
if (entry.regex.test(normalized)) {
|
|
@@ -3958,9 +3958,9 @@ var init_http_router = __esm({
|
|
|
3958
3958
|
/**
|
|
3959
3959
|
* 匹配路由并提取路由参数
|
|
3960
3960
|
*/
|
|
3961
|
-
match(method,
|
|
3961
|
+
match(method, path46) {
|
|
3962
3962
|
const upperMethod = method.toUpperCase();
|
|
3963
|
-
const normalized =
|
|
3963
|
+
const normalized = path46.startsWith("/") ? path46 : "/" + path46;
|
|
3964
3964
|
for (const entry of this.routes) {
|
|
3965
3965
|
if (entry.method !== upperMethod) continue;
|
|
3966
3966
|
const match = normalized.match(entry.regex);
|
|
@@ -4103,13 +4103,13 @@ function checkMethodPolicy(input) {
|
|
|
4103
4103
|
if (tokens.frontendApi && token === tokens.frontendApi) {
|
|
4104
4104
|
if (isReadOnlyMethod(method)) return null;
|
|
4105
4105
|
if (method === "request" && !Array.isArray(args)) return null;
|
|
4106
|
-
const
|
|
4107
|
-
const isHighRisk = HIGH_RISK_PATH_PREFIXES.some((p) =>
|
|
4108
|
-
const isIrreversible = IRREVERSIBLE_PATH_SUFFIXES.some((s) =>
|
|
4109
|
-
const isEntityDelete = method === "del" && ENTITY_DELETE_RE.test(
|
|
4106
|
+
const path46 = extractPathArg(args);
|
|
4107
|
+
const isHighRisk = HIGH_RISK_PATH_PREFIXES.some((p) => path46 === p || path46.startsWith(`${p}/`));
|
|
4108
|
+
const isIrreversible = IRREVERSIBLE_PATH_SUFFIXES.some((s) => path46.endsWith(s));
|
|
4109
|
+
const isEntityDelete = method === "del" && ENTITY_DELETE_RE.test(path46);
|
|
4110
4110
|
if (isHighRisk || isIrreversible || isEntityDelete) {
|
|
4111
4111
|
if (hasCapability(caps, CAP_API_ADMIN)) return null;
|
|
4112
|
-
const why = isIrreversible ? `\u4E0D\u53EF\u9006\u64CD\u4F5C '${
|
|
4112
|
+
const why = isIrreversible ? `\u4E0D\u53EF\u9006\u64CD\u4F5C '${path46}'` : isEntityDelete ? `\u5B9E\u4F53\u7EA7\u5220\u9664 '${path46}'` : `\u5199\u5165\u9AD8\u5371\u8DEF\u5F84 '${path46 || "<\u672A\u63D0\u4F9B\u8DEF\u5F84>"}'`;
|
|
4113
4113
|
return `${why} \u9700\u8981 manifest \u58F0\u660E '${CAP_API_ADMIN}' \u80FD\u529B`;
|
|
4114
4114
|
}
|
|
4115
4115
|
return hasCapability(caps, CAP_API_WRITE) ? null : `\u5199\u64CD\u4F5C\uFF08${method}\uFF09\u9700\u8981 manifest \u58F0\u660E '${CAP_API_WRITE}' \u80FD\u529B\uFF08\u666E\u901A\u53EA\u8BFB\u8C03\u7528\u8BF7\u7528 get\uFF09`;
|
|
@@ -5714,7 +5714,7 @@ function spawnPluginChild(opts) {
|
|
|
5714
5714
|
});
|
|
5715
5715
|
const removeBootstrapFile = () => {
|
|
5716
5716
|
try {
|
|
5717
|
-
|
|
5717
|
+
import_node_fs9.default.unlinkSync(bootstrapFile);
|
|
5718
5718
|
} catch {
|
|
5719
5719
|
}
|
|
5720
5720
|
};
|
|
@@ -5740,25 +5740,25 @@ function isPidAlive(pid) {
|
|
|
5740
5740
|
}
|
|
5741
5741
|
}
|
|
5742
5742
|
function writeBootstrapTempFile(code) {
|
|
5743
|
-
const file =
|
|
5743
|
+
const file = import_node_path10.default.join(
|
|
5744
5744
|
import_node_os.default.tmpdir(),
|
|
5745
5745
|
`${BOOTSTRAP_PREFIX}${process.pid}-${import_node_crypto7.default.randomBytes(8).toString("hex")}${BOOTSTRAP_SUFFIX}`
|
|
5746
5746
|
);
|
|
5747
|
-
|
|
5747
|
+
import_node_fs9.default.writeFileSync(file, code, { encoding: "utf8", mode: 384 });
|
|
5748
5748
|
return file;
|
|
5749
5749
|
}
|
|
5750
5750
|
function cleanupStaleBootstrapFiles(now = Date.now()) {
|
|
5751
5751
|
try {
|
|
5752
5752
|
const tmpDir = import_node_os.default.tmpdir();
|
|
5753
|
-
const files =
|
|
5753
|
+
const files = import_node_fs9.default.readdirSync(tmpDir);
|
|
5754
5754
|
let cleaned = 0;
|
|
5755
5755
|
for (const file of files) {
|
|
5756
5756
|
if (!file.startsWith(BOOTSTRAP_PREFIX) || !file.endsWith(BOOTSTRAP_SUFFIX)) continue;
|
|
5757
|
-
const full =
|
|
5757
|
+
const full = import_node_path10.default.join(tmpDir, file);
|
|
5758
5758
|
const ownerPid = bootstrapOwnerPid(file);
|
|
5759
5759
|
if (ownerPid === null) {
|
|
5760
5760
|
try {
|
|
5761
|
-
if (now -
|
|
5761
|
+
if (now - import_node_fs9.default.statSync(full).mtimeMs < LEGACY_BOOTSTRAP_STALE_MS) continue;
|
|
5762
5762
|
} catch {
|
|
5763
5763
|
continue;
|
|
5764
5764
|
}
|
|
@@ -5766,7 +5766,7 @@ function cleanupStaleBootstrapFiles(now = Date.now()) {
|
|
|
5766
5766
|
continue;
|
|
5767
5767
|
}
|
|
5768
5768
|
try {
|
|
5769
|
-
|
|
5769
|
+
import_node_fs9.default.unlinkSync(full);
|
|
5770
5770
|
cleaned++;
|
|
5771
5771
|
} catch {
|
|
5772
5772
|
}
|
|
@@ -5781,7 +5781,7 @@ function defaultNetPreloadStartDirs() {
|
|
|
5781
5781
|
try {
|
|
5782
5782
|
const url = import_meta5.url;
|
|
5783
5783
|
if (typeof url === "string" && url.startsWith("file:")) {
|
|
5784
|
-
dirs.push(
|
|
5784
|
+
dirs.push(import_node_path10.default.dirname((0, import_node_url3.fileURLToPath)(url)));
|
|
5785
5785
|
}
|
|
5786
5786
|
} catch {
|
|
5787
5787
|
}
|
|
@@ -5793,27 +5793,27 @@ function defaultNetPreloadStartDirs() {
|
|
|
5793
5793
|
function resolveNetPreloadPath(options = {}) {
|
|
5794
5794
|
const cwd = options.cwd ?? process.cwd();
|
|
5795
5795
|
const candidates = [
|
|
5796
|
-
|
|
5797
|
-
|
|
5796
|
+
import_node_path10.default.resolve(cwd, "packages/core/worker-runtime/plugin-net-preload.cjs"),
|
|
5797
|
+
import_node_path10.default.resolve(cwd, "dist/plugin-net-preload.cjs")
|
|
5798
5798
|
];
|
|
5799
5799
|
for (const dir of options.startDirs ?? defaultNetPreloadStartDirs()) {
|
|
5800
|
-
candidates.unshift(
|
|
5800
|
+
candidates.unshift(import_node_path10.default.join(dir, "plugin-net-preload.cjs"));
|
|
5801
5801
|
}
|
|
5802
|
-
const found = candidates.find((candidate) =>
|
|
5802
|
+
const found = candidates.find((candidate) => import_node_fs9.default.existsSync(candidate));
|
|
5803
5803
|
if (!found) {
|
|
5804
5804
|
throw new Error(`[PluginNet] preload script not found. Looked in: ${candidates.join(", ")}`);
|
|
5805
5805
|
}
|
|
5806
5806
|
return found;
|
|
5807
5807
|
}
|
|
5808
|
-
var import_node_child_process, import_node_crypto7,
|
|
5808
|
+
var import_node_child_process, import_node_crypto7, import_node_fs9, import_node_os, import_node_path10, import_node_url3, import_meta5, ENV_ALLOWLIST, ENV_DENYLIST, WORKER_DATA_ENV, BOOTSTRAP_PREFIX, BOOTSTRAP_SUFFIX, LEGACY_BOOTSTRAP_STALE_MS;
|
|
5809
5809
|
var init_child_spawn = __esm({
|
|
5810
5810
|
"packages/core/worker-runtime/child-spawn.ts"() {
|
|
5811
5811
|
"use strict";
|
|
5812
5812
|
import_node_child_process = require("node:child_process");
|
|
5813
5813
|
import_node_crypto7 = __toESM(require("node:crypto"), 1);
|
|
5814
|
-
|
|
5814
|
+
import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
5815
5815
|
import_node_os = __toESM(require("node:os"), 1);
|
|
5816
|
-
|
|
5816
|
+
import_node_path10 = __toESM(require("node:path"), 1);
|
|
5817
5817
|
import_node_url3 = require("node:url");
|
|
5818
5818
|
init_child_transport();
|
|
5819
5819
|
init_child_isolate();
|
|
@@ -5869,12 +5869,12 @@ function quotaExceeded(input) {
|
|
|
5869
5869
|
}
|
|
5870
5870
|
function readProcSample(pid) {
|
|
5871
5871
|
try {
|
|
5872
|
-
const stat =
|
|
5872
|
+
const stat = import_node_fs10.default.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
5873
5873
|
const rest = stat.slice(stat.lastIndexOf(")") + 2).trim().split(/\s+/);
|
|
5874
5874
|
const utime = Number(rest[11]);
|
|
5875
5875
|
const stime = Number(rest[12]);
|
|
5876
5876
|
if (!Number.isFinite(utime) || !Number.isFinite(stime)) return null;
|
|
5877
|
-
const status =
|
|
5877
|
+
const status = import_node_fs10.default.readFileSync(`/proc/${pid}/status`, "utf8");
|
|
5878
5878
|
const rss = /^VmRSS:\s+(\d+)\s+kB/m.exec(status);
|
|
5879
5879
|
return { cpuMs: (utime + stime) / 100 * 1e3, rssKb: rss ? Number(rss[1]) : 0 };
|
|
5880
5880
|
} catch {
|
|
@@ -5917,11 +5917,11 @@ function startProcessQuota(pid, onBreach, env = process.env) {
|
|
|
5917
5917
|
}
|
|
5918
5918
|
};
|
|
5919
5919
|
}
|
|
5920
|
-
var
|
|
5920
|
+
var import_node_fs10, DEFAULT_MAX_RSS_MB, DEFAULT_CPU_QUOTA_MS, DEFAULT_CPU_WINDOW_MS;
|
|
5921
5921
|
var init_plugin_quota = __esm({
|
|
5922
5922
|
"packages/core/worker-runtime/plugin-quota.ts"() {
|
|
5923
5923
|
"use strict";
|
|
5924
|
-
|
|
5924
|
+
import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
5925
5925
|
DEFAULT_MAX_RSS_MB = 512;
|
|
5926
5926
|
DEFAULT_CPU_QUOTA_MS = 8e3;
|
|
5927
5927
|
DEFAULT_CPU_WINDOW_MS = 1e4;
|
|
@@ -8923,9 +8923,9 @@ __export(worker_manager_exports, {
|
|
|
8923
8923
|
function collectSharedReadPaths(pluginDir) {
|
|
8924
8924
|
if (!pluginDir) return [];
|
|
8925
8925
|
const paths = /* @__PURE__ */ new Set();
|
|
8926
|
-
const nodeModulesDir =
|
|
8926
|
+
const nodeModulesDir = import_node_path11.default.join(import_node_path11.default.dirname(pluginDir), "node_modules");
|
|
8927
8927
|
paths.add(nodeModulesDir);
|
|
8928
|
-
const sdkLink =
|
|
8928
|
+
const sdkLink = import_node_path11.default.join(nodeModulesDir, "@openlearn", "plugin-sdk");
|
|
8929
8929
|
try {
|
|
8930
8930
|
if (import_fs7.default.existsSync(sdkLink)) paths.add(import_fs7.default.realpathSync(sdkLink));
|
|
8931
8931
|
} catch {
|
|
@@ -8964,7 +8964,7 @@ function generateBootstrapCode() {
|
|
|
8964
8964
|
const rootPath = process.cwd().replace(/\\/g, "/");
|
|
8965
8965
|
return composeBootstrapCode(`${rootPath}/package.json`);
|
|
8966
8966
|
}
|
|
8967
|
-
var import_node_worker_threads, import_fs7,
|
|
8967
|
+
var import_node_worker_threads, import_fs7, import_node_path11, import_uuid12, BASE_WORKER_SERVICE_TOKENS, ALL_SERVICE_TOKENS, POINTS_SERVICE_TOKENS, MAX_WORKERS, ACTIVATE_TIMEOUT_MS, ACTIVATE_PROGRESS_SLIDE_MS, WorkerRegistry, WorkerManager;
|
|
8968
8968
|
var init_worker_manager = __esm({
|
|
8969
8969
|
"packages/core/worker-runtime/worker-manager.ts"() {
|
|
8970
8970
|
"use strict";
|
|
@@ -8978,7 +8978,7 @@ var init_worker_manager = __esm({
|
|
|
8978
8978
|
init_plugin_limits();
|
|
8979
8979
|
init_thread_isolate();
|
|
8980
8980
|
import_fs7 = __toESM(require("fs"), 1);
|
|
8981
|
-
|
|
8981
|
+
import_node_path11 = __toESM(require("node:path"), 1);
|
|
8982
8982
|
init_transport();
|
|
8983
8983
|
init_plugin_net_proxy();
|
|
8984
8984
|
init_service_host();
|
|
@@ -9383,7 +9383,7 @@ var init_worker_manager = __esm({
|
|
|
9383
9383
|
const encodedBootstrap = Buffer.from(bootstrapCode, "utf-8").toString("base64");
|
|
9384
9384
|
const bootstrapDataUrl = `data:text/javascript;base64,${encodedBootstrap}`;
|
|
9385
9385
|
const allowedTokens = computeAllowedWorkerTokens(manifest, serviceTokens);
|
|
9386
|
-
const resolvedPluginDir = pluginDir && import_fs7.default.existsSync(
|
|
9386
|
+
const resolvedPluginDir = pluginDir && import_fs7.default.existsSync(import_node_path11.default.join(pluginDir, "index.js")) ? pluginDir : void 0;
|
|
9387
9387
|
let approvedNetHosts = [];
|
|
9388
9388
|
try {
|
|
9389
9389
|
approvedNetHosts = listApprovedNetHosts(this.db, [pluginId, manifest.id]);
|
|
@@ -11027,7 +11027,7 @@ function simpleChecksum(text) {
|
|
|
11027
11027
|
}
|
|
11028
11028
|
return hash.toString(16);
|
|
11029
11029
|
}
|
|
11030
|
-
function runMigrations(db2, migrations) {
|
|
11030
|
+
function runMigrations(db2, migrations, options = {}) {
|
|
11031
11031
|
db2.exec(`
|
|
11032
11032
|
CREATE TABLE IF NOT EXISTS _migrations (
|
|
11033
11033
|
name TEXT PRIMARY KEY,
|
|
@@ -11038,6 +11038,23 @@ function runMigrations(db2, migrations) {
|
|
|
11038
11038
|
const appliedRows = db2.prepare("SELECT name, checksum FROM _migrations").all();
|
|
11039
11039
|
const applied = new Map(appliedRows.map((r) => [r.name, r.checksum]));
|
|
11040
11040
|
const byName = new Map(migrations.map((m) => [m.name, m]));
|
|
11041
|
+
const unknown = [...applied.keys()].filter((name) => !byName.has(name));
|
|
11042
|
+
if (unknown.length > 0) {
|
|
11043
|
+
const detail = unknown.map((n) => ` - ${n}`).join("\n");
|
|
11044
|
+
const msg = `[Migration] \u53F0\u8D26\u91CC\u6709 ${unknown.length} \u4E2A\u672C\u6B21\u8FC1\u79FB\u5217\u8868\u672A\u5305\u542B\u7684\u6761\u76EE\uFF1A
|
|
11045
|
+
${detail}
|
|
11046
|
+
` + (options.strictLedger ? ` \u542F\u52A8\u94FE\u8DEF\u5224\u5B9A\u4E3A FATAL\uFF1A
|
|
11047
|
+
\u6210\u56E0\u4E00\uFF1A\u7528\u66F4\u65B0\u7248\u672C\u7684\u5907\u4EFD\u6062\u590D\u4E86\u65E7\u4EE3\u7801\uFF08\u4EE3\u7801\u964D\u7EA7\uFF09\u2014\u2014\u8BF7\u5148\u5347\u7EA7\u4EE3\u7801\u518D\u542F\u52A8\uFF1B
|
|
11048
|
+
\u6210\u56E0\u4E8C\uFF1A\u5DF2\u5E94\u7528\u7684\u8FC1\u79FB\u6587\u4EF6\u88AB\u6539\u540D\u6216\u5220\u9664\u2014\u2014\u8FC1\u79FB\u4E00\u65E6\u5E94\u7528\u4E0D\u53EF\u6539\u540D\uFF0C\u8BF7\u6062\u590D\u6587\u4EF6\u6216\u8865\u4E00\u6761\u65B0\u8FC1\u79FB\u3002
|
|
11049
|
+
\u786E\u5C5E\u6709\u610F\u7684\u964D\u7EA7\u8FD0\u7EF4\uFF1A\u8BBE\u7F6E MIGRATION_ALLOW_UNKNOWN_LEDGER=true \u653E\u884C\uFF08\u4E0D\u63A8\u8350\uFF09\u3002` : ` \uFF08\u975E\u4E25\u683C\u53E3\u5F84\uFF1A\u8FC1\u79FB\u5B50\u96C6\u8C03\u7528\u5C5E\u5408\u6CD5\u7528\u6CD5\uFF0C\u4EC5\u63D0\u793A\uFF09`);
|
|
11050
|
+
if (process.env.MIGRATION_ALLOW_UNKNOWN_LEDGER === "true") {
|
|
11051
|
+
console.warn("[Migration] WARNING: \u672A\u77E5\u53F0\u8D26\u6761\u76EE\u5DF2\u6309 MIGRATION_ALLOW_UNKNOWN_LEDGER \u653E\u884C\uFF1A\n" + msg);
|
|
11052
|
+
} else if (options.strictLedger) {
|
|
11053
|
+
throw new Error(msg);
|
|
11054
|
+
} else {
|
|
11055
|
+
console.warn("[Migration] " + msg);
|
|
11056
|
+
}
|
|
11057
|
+
}
|
|
11041
11058
|
const drift = [];
|
|
11042
11059
|
for (const [name, stored] of applied) {
|
|
11043
11060
|
const migration = byName.get(name);
|
|
@@ -11145,6 +11162,68 @@ function getDefaultMigrationsDir(options = {}) {
|
|
|
11145
11162
|
return import_path.default.resolve(cwd, "migrations");
|
|
11146
11163
|
}
|
|
11147
11164
|
|
|
11165
|
+
// packages/core/db/schema-golden.ts
|
|
11166
|
+
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
11167
|
+
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
11168
|
+
var SCHEMA_GOLDEN_FILENAME = "schema-golden.json";
|
|
11169
|
+
function extractSchemaSnapshot(db2) {
|
|
11170
|
+
const tables = db2.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name").all();
|
|
11171
|
+
const snapshot = {};
|
|
11172
|
+
for (const { name } of tables) {
|
|
11173
|
+
const columns = db2.prepare(`PRAGMA table_info(${JSON.stringify(name)})`).all().map(
|
|
11174
|
+
(c) => c.name
|
|
11175
|
+
);
|
|
11176
|
+
snapshot[name] = columns.sort();
|
|
11177
|
+
}
|
|
11178
|
+
return snapshot;
|
|
11179
|
+
}
|
|
11180
|
+
function verifySchemaAgainstGolden(db2, golden) {
|
|
11181
|
+
const actual = extractSchemaSnapshot(db2);
|
|
11182
|
+
const drifts = [];
|
|
11183
|
+
for (const [table, expectedColumns] of Object.entries(golden.tables)) {
|
|
11184
|
+
const actualColumns = actual[table];
|
|
11185
|
+
if (!actualColumns) {
|
|
11186
|
+
drifts.push({ table, missingColumns: expectedColumns, tableMissing: true });
|
|
11187
|
+
continue;
|
|
11188
|
+
}
|
|
11189
|
+
const actualSet = new Set(actualColumns);
|
|
11190
|
+
const missing = expectedColumns.filter((c) => !actualSet.has(c));
|
|
11191
|
+
if (missing.length > 0) {
|
|
11192
|
+
drifts.push({ table, missingColumns: missing, tableMissing: false });
|
|
11193
|
+
}
|
|
11194
|
+
}
|
|
11195
|
+
return drifts;
|
|
11196
|
+
}
|
|
11197
|
+
function formatSchemaGoldenDrifts(drifts) {
|
|
11198
|
+
return drifts.map(
|
|
11199
|
+
(d) => d.tableMissing ? ` - \u8868 ${d.table} \u6574\u8868\u7F3A\u5931\uFF08\u671F\u671B\u5217\uFF1A${d.missingColumns.join(", ")}\uFF09` : ` - \u8868 ${d.table} \u7F3A\u5217\uFF1A${d.missingColumns.join(", ")}`
|
|
11200
|
+
).join("\n");
|
|
11201
|
+
}
|
|
11202
|
+
function loadSchemaGolden(migrationsDir) {
|
|
11203
|
+
const filePath = import_node_path3.default.join(migrationsDir, SCHEMA_GOLDEN_FILENAME);
|
|
11204
|
+
if (!import_node_fs3.default.existsSync(filePath)) {
|
|
11205
|
+
throw new Error(
|
|
11206
|
+
`[SchemaGolden] FATAL: ${filePath} \u4E0D\u5B58\u5728\u3002\u91D1\u6837\u4E0E\u8FC1\u79FB\u6587\u4EF6\u540C\u76EE\u5F55\u53D1\u5E03\uFF0C\u7F3A\u5931\u8BF4\u660E\u5B89\u88C5\u5305\u4E0D\u5B8C\u6574\uFF1B\u6E90\u7801\u4ED3\u5E93\u91CC\u8BF7\u8FD0\u884C pnpm db:gen-golden \u751F\u6210\u3002`
|
|
11207
|
+
);
|
|
11208
|
+
}
|
|
11209
|
+
try {
|
|
11210
|
+
return JSON.parse(import_node_fs3.default.readFileSync(filePath, "utf8"));
|
|
11211
|
+
} catch (err) {
|
|
11212
|
+
throw new Error(`[SchemaGolden] FATAL: \u91D1\u6837\u6587\u4EF6\u89E3\u6790\u5931\u8D25\uFF08${filePath}\uFF09\uFF1A${String(err)}`, { cause: err });
|
|
11213
|
+
}
|
|
11214
|
+
}
|
|
11215
|
+
function assertSchemaMatchesGolden(db2, migrationsDir) {
|
|
11216
|
+
const golden = loadSchemaGolden(migrationsDir);
|
|
11217
|
+
const drifts = verifySchemaAgainstGolden(db2, golden);
|
|
11218
|
+
if (drifts.length > 0) {
|
|
11219
|
+
throw new Error(
|
|
11220
|
+
`[SchemaGolden] FATAL: \u6570\u636E\u5E93\u7ED3\u6784\u4F4E\u4E8E\u4EE3\u7801\u671F\u671B\uFF08${drifts.length} \u5904\u7F3A\u5931\uFF09\u2014\u2014\u8FC1\u79FB\u53F0\u8D26\u867D\u5DF2\u8FFD\u5E73\uFF0C\u4F46\u5E93\u88AB\u5916\u90E8\u4FEE\u6539\u8FC7\u6216\u4ECE\u65E7\u5907\u4EFD\u505A\u4E86\u90E8\u5206\u6062\u590D\u3002\u7F3A\u5931\u660E\u7EC6\uFF1A
|
|
11221
|
+
${formatSchemaGoldenDrifts(drifts)}
|
|
11222
|
+
\u6062\u590D\u624B\u6BB5\uFF1A\u7528 backups/ \u91CC\u6700\u65B0\u7684 pre_migration_*.db \u5FEB\u7167\u6062\u590D\uFF0C\u6216\u6838\u5BF9\u6700\u8FD1\u4E00\u6B21\u6062\u590D\u64CD\u4F5C\u7684\u6765\u6E90\u3002`
|
|
11223
|
+
);
|
|
11224
|
+
}
|
|
11225
|
+
}
|
|
11226
|
+
|
|
11148
11227
|
// packages/core/db/class-passcode-crypto.ts
|
|
11149
11228
|
var import_node_crypto2 = __toESM(require("node:crypto"), 1);
|
|
11150
11229
|
init_api_key_crypto();
|
|
@@ -11802,7 +11881,16 @@ function initializeDatabase(instance) {
|
|
|
11802
11881
|
if (migrations.length === 0) {
|
|
11803
11882
|
throw new Error(`[DB SSOT] FATAL: \u8FC1\u79FB\u76EE\u5F55 ${migrationsDir} \u91CC\u6CA1\u6709 .sql \u6587\u4EF6\u3002\u62D2\u7EDD\u5728\u6CA1\u6709 schema \u7684\u5E93\u4E0A\u542F\u52A8\u3002`);
|
|
11804
11883
|
}
|
|
11805
|
-
runMigrations(instance, migrations);
|
|
11884
|
+
runMigrations(instance, migrations, { strictLedger: true });
|
|
11885
|
+
if (process.env.VITEST) {
|
|
11886
|
+
try {
|
|
11887
|
+
assertSchemaMatchesGolden(instance, migrationsDir);
|
|
11888
|
+
} catch (err) {
|
|
11889
|
+
console.warn("[SchemaGolden] \u6D4B\u8BD5\u73AF\u5883\u7ED3\u6784\u7EC8\u68C0\u672A\u901A\u8FC7\uFF08\u5FFD\u7565\uFF09\uFF1A", err instanceof Error ? err.message : err);
|
|
11890
|
+
}
|
|
11891
|
+
} else {
|
|
11892
|
+
assertSchemaMatchesGolden(instance, migrationsDir);
|
|
11893
|
+
}
|
|
11806
11894
|
applyStartupDataMigrations(instance);
|
|
11807
11895
|
const apiKeyMigration = encryptStoredPlaintextApiKeys(instance);
|
|
11808
11896
|
if (apiKeyMigration.updated > 0) {
|
|
@@ -14061,10 +14149,10 @@ function parseScoreFields(fields) {
|
|
|
14061
14149
|
if (typeof fields !== "string" || !fields.trim()) return [];
|
|
14062
14150
|
return fields.split(/[\n\r,,;;|]+/).map((f) => f.trim()).filter(Boolean);
|
|
14063
14151
|
}
|
|
14064
|
-
function getNested(raw,
|
|
14152
|
+
function getNested(raw, path46) {
|
|
14065
14153
|
if (raw === null || raw === void 0) return void 0;
|
|
14066
|
-
if (!
|
|
14067
|
-
const parts = String(
|
|
14154
|
+
if (!path46) return void 0;
|
|
14155
|
+
const parts = String(path46).split(".").map((p) => p.trim()).filter(Boolean);
|
|
14068
14156
|
let cursor = raw;
|
|
14069
14157
|
for (let i = 0; i < parts.length; i += 1) {
|
|
14070
14158
|
if (cursor === null || cursor === void 0) return void 0;
|
|
@@ -18655,7 +18743,7 @@ var import_semver2 = __toESM(require("semver"), 1);
|
|
|
18655
18743
|
init_types();
|
|
18656
18744
|
|
|
18657
18745
|
// packages/core/plugin-host/execution-policy.ts
|
|
18658
|
-
var
|
|
18746
|
+
var import_node_path8 = __toESM(require("node:path"), 1);
|
|
18659
18747
|
var import_node_url2 = require("node:url");
|
|
18660
18748
|
init_types();
|
|
18661
18749
|
var import_meta4 = {};
|
|
@@ -18668,21 +18756,21 @@ function builtinPluginsDir() {
|
|
|
18668
18756
|
try {
|
|
18669
18757
|
const url = import_meta4.url;
|
|
18670
18758
|
if (typeof url === "string" && url.startsWith("file:")) {
|
|
18671
|
-
return
|
|
18759
|
+
return import_node_path8.default.resolve(import_node_path8.default.dirname((0, import_node_url2.fileURLToPath)(url)), "../../plugins");
|
|
18672
18760
|
}
|
|
18673
18761
|
} catch {
|
|
18674
18762
|
}
|
|
18675
18763
|
if (typeof __dirname !== "undefined") {
|
|
18676
|
-
return
|
|
18764
|
+
return import_node_path8.default.resolve(__dirname, "../packages/plugins");
|
|
18677
18765
|
}
|
|
18678
|
-
return
|
|
18766
|
+
return import_node_path8.default.resolve(process.cwd(), "packages/plugins");
|
|
18679
18767
|
}
|
|
18680
18768
|
function isInsideBuiltinPluginsDir(sourcePath, builtinDir = builtinPluginsDir()) {
|
|
18681
18769
|
if (!sourcePath) return false;
|
|
18682
|
-
const root =
|
|
18683
|
-
const resolved =
|
|
18684
|
-
const relative2 =
|
|
18685
|
-
return relative2 === "" || !relative2.startsWith("..") && !
|
|
18770
|
+
const root = import_node_path8.default.resolve(builtinDir);
|
|
18771
|
+
const resolved = import_node_path8.default.resolve(sourcePath);
|
|
18772
|
+
const relative2 = import_node_path8.default.relative(root, resolved);
|
|
18773
|
+
return relative2 === "" || !relative2.startsWith("..") && !import_node_path8.default.isAbsolute(relative2);
|
|
18686
18774
|
}
|
|
18687
18775
|
function isTrustedInProcessPlugin(manifestId, env = process.env, sourcePath) {
|
|
18688
18776
|
if (isInsideBuiltinPluginsDir(sourcePath)) return true;
|
|
@@ -19742,7 +19830,7 @@ var PluginHostHttp = class extends PluginHostCore {
|
|
|
19742
19830
|
/**
|
|
19743
19831
|
* 判断指定插件的某个请求路径是否为流式路由 (SSE)
|
|
19744
19832
|
*/
|
|
19745
|
-
isStreamRoute(pluginIdOrManifestId, method,
|
|
19833
|
+
isStreamRoute(pluginIdOrManifestId, method, path46) {
|
|
19746
19834
|
const upperMethod = method.toUpperCase();
|
|
19747
19835
|
const resolvedId = this.resolvePluginUuid(pluginIdOrManifestId);
|
|
19748
19836
|
const instance = this.pluginInstances.get(resolvedId) ?? this.pluginInstances.get(pluginIdOrManifestId);
|
|
@@ -19751,7 +19839,7 @@ var PluginHostHttp = class extends PluginHostCore {
|
|
|
19751
19839
|
for (const r of manifest.api.routes) {
|
|
19752
19840
|
if (r.method.toUpperCase() === upperMethod && r.streaming === true) {
|
|
19753
19841
|
const compiled = compileRoutePattern(r.path);
|
|
19754
|
-
if (compiled.regex.test(
|
|
19842
|
+
if (compiled.regex.test(path46)) {
|
|
19755
19843
|
return true;
|
|
19756
19844
|
}
|
|
19757
19845
|
}
|
|
@@ -19759,14 +19847,14 @@ var PluginHostHttp = class extends PluginHostCore {
|
|
|
19759
19847
|
}
|
|
19760
19848
|
if (!instance) return false;
|
|
19761
19849
|
if (instance.context?.http) {
|
|
19762
|
-
return instance.context.http.isStream(upperMethod,
|
|
19850
|
+
return instance.context.http.isStream(upperMethod, path46);
|
|
19763
19851
|
}
|
|
19764
19852
|
if (instance.workerRef) {
|
|
19765
19853
|
const routes = instance.workerRef.serviceHost.getRegisteredRoutes();
|
|
19766
19854
|
for (const r of routes) {
|
|
19767
19855
|
if (r.method === upperMethod && r.isStream) {
|
|
19768
19856
|
const compiled = compileRoutePattern(r.pattern);
|
|
19769
|
-
if (compiled.regex.test(
|
|
19857
|
+
if (compiled.regex.test(path46)) {
|
|
19770
19858
|
return true;
|
|
19771
19859
|
}
|
|
19772
19860
|
}
|
|
@@ -19839,8 +19927,8 @@ init_manifest_schema();
|
|
|
19839
19927
|
init_context_builder();
|
|
19840
19928
|
|
|
19841
19929
|
// packages/core/plugin-host/declarative-migrator.ts
|
|
19842
|
-
var
|
|
19843
|
-
var
|
|
19930
|
+
var import_node_fs8 = __toESM(require("node:fs"), 1);
|
|
19931
|
+
var import_node_path9 = __toESM(require("node:path"), 1);
|
|
19844
19932
|
init_errors2();
|
|
19845
19933
|
init_plugin_tables();
|
|
19846
19934
|
function ensurePluginDeclaredMigrationsTable(db2) {
|
|
@@ -19856,7 +19944,7 @@ function ensurePluginDeclaredMigrationsTable(db2) {
|
|
|
19856
19944
|
`);
|
|
19857
19945
|
}
|
|
19858
19946
|
function parseMigrationFileName(fileName) {
|
|
19859
|
-
const baseName =
|
|
19947
|
+
const baseName = import_node_path9.default.basename(fileName, ".sql");
|
|
19860
19948
|
const match = baseName.match(/^(\d+)[_-]?(.*)$/);
|
|
19861
19949
|
if (match) {
|
|
19862
19950
|
const version = parseInt(match[1], 10);
|
|
@@ -19865,22 +19953,22 @@ function parseMigrationFileName(fileName) {
|
|
|
19865
19953
|
return { version: 0, baseName };
|
|
19866
19954
|
}
|
|
19867
19955
|
function loadPluginMigrations(migrationsDir) {
|
|
19868
|
-
if (!
|
|
19956
|
+
if (!import_node_fs8.default.existsSync(migrationsDir)) {
|
|
19869
19957
|
return [];
|
|
19870
19958
|
}
|
|
19871
|
-
const stat =
|
|
19959
|
+
const stat = import_node_fs8.default.statSync(migrationsDir);
|
|
19872
19960
|
if (!stat.isDirectory()) {
|
|
19873
19961
|
return [];
|
|
19874
19962
|
}
|
|
19875
|
-
const files =
|
|
19963
|
+
const files = import_node_fs8.default.readdirSync(migrationsDir).filter((f) => f.endsWith(".sql")).sort((a, b) => {
|
|
19876
19964
|
const vA = parseMigrationFileName(a).version;
|
|
19877
19965
|
const vB = parseMigrationFileName(b).version;
|
|
19878
19966
|
if (vA !== vB) return vA - vB;
|
|
19879
19967
|
return a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" });
|
|
19880
19968
|
});
|
|
19881
19969
|
return files.map((file) => {
|
|
19882
|
-
const fullPath =
|
|
19883
|
-
const content =
|
|
19970
|
+
const fullPath = import_node_path9.default.join(migrationsDir, file);
|
|
19971
|
+
const content = import_node_fs8.default.readFileSync(fullPath, "utf8");
|
|
19884
19972
|
const { version, baseName } = parseMigrationFileName(file);
|
|
19885
19973
|
const parsed = parseMigrationSql(baseName, content);
|
|
19886
19974
|
return {
|
|
@@ -20636,27 +20724,27 @@ var import_path8 = __toESM(require("path"), 1);
|
|
|
20636
20724
|
var import_semver3 = __toESM(require("semver"), 1);
|
|
20637
20725
|
|
|
20638
20726
|
// packages/core/version.ts
|
|
20639
|
-
var
|
|
20640
|
-
var
|
|
20727
|
+
var import_node_fs11 = __toESM(require("node:fs"), 1);
|
|
20728
|
+
var import_node_path12 = __toESM(require("node:path"), 1);
|
|
20641
20729
|
var import_node_url5 = require("node:url");
|
|
20642
20730
|
var import_meta6 = {};
|
|
20643
|
-
var FALLBACK_VERSION = "0.7.
|
|
20731
|
+
var FALLBACK_VERSION = "0.7.4";
|
|
20644
20732
|
function readPlatformVersion() {
|
|
20645
20733
|
const bases = [];
|
|
20646
20734
|
if (typeof __dirname !== "undefined") bases.push(__dirname);
|
|
20647
20735
|
try {
|
|
20648
|
-
bases.push(
|
|
20736
|
+
bases.push(import_node_path12.default.dirname((0, import_node_url5.fileURLToPath)(import_meta6.url)));
|
|
20649
20737
|
} catch {
|
|
20650
20738
|
}
|
|
20651
20739
|
for (const base of bases) {
|
|
20652
20740
|
let dir = base;
|
|
20653
20741
|
for (; ; ) {
|
|
20654
20742
|
try {
|
|
20655
|
-
const pkg = JSON.parse(
|
|
20743
|
+
const pkg = JSON.parse(import_node_fs11.default.readFileSync(import_node_path12.default.join(dir, "package.json"), "utf-8"));
|
|
20656
20744
|
if (pkg.name === "openlearn-next" && typeof pkg.version === "string") return pkg.version;
|
|
20657
20745
|
} catch {
|
|
20658
20746
|
}
|
|
20659
|
-
const parent =
|
|
20747
|
+
const parent = import_node_path12.default.dirname(dir);
|
|
20660
20748
|
if (parent === dir) break;
|
|
20661
20749
|
dir = parent;
|
|
20662
20750
|
}
|
|
@@ -21375,8 +21463,8 @@ init_audit_log();
|
|
|
21375
21463
|
|
|
21376
21464
|
// packages/core/plugin-host/dependency-install.ts
|
|
21377
21465
|
var import_node_child_process2 = require("node:child_process");
|
|
21378
|
-
var
|
|
21379
|
-
var
|
|
21466
|
+
var import_node_fs12 = __toESM(require("node:fs"), 1);
|
|
21467
|
+
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
21380
21468
|
var DependencyInstallError = class extends Error {
|
|
21381
21469
|
constructor(message, pluginId, cause) {
|
|
21382
21470
|
super(message);
|
|
@@ -21411,8 +21499,8 @@ function resolveRegistry() {
|
|
|
21411
21499
|
function assertDependenciesResolved(pluginDir, dependencies, pluginId) {
|
|
21412
21500
|
const names = dependencyNames(dependencies);
|
|
21413
21501
|
if (names.length === 0) return;
|
|
21414
|
-
const lockPath =
|
|
21415
|
-
if (!
|
|
21502
|
+
const lockPath = import_node_path13.default.join(pluginDir, "package-lock.json");
|
|
21503
|
+
if (!import_node_fs12.default.existsSync(lockPath)) {
|
|
21416
21504
|
throw new DependencyInstallError(
|
|
21417
21505
|
`\u4F9D\u8D56\u5B89\u88C5\u672A\u4EA7\u51FA package-lock.json \u2014\u2014 \u65E0\u6CD5\u786E\u8BA4\u4F9D\u8D56\u5DF2\u843D\u76D8\u3002pluginId=${pluginId}\uFF0C\u58F0\u660E\u4F9D\u8D56\uFF1A${names.join(", ")}`,
|
|
21418
21506
|
pluginId
|
|
@@ -21420,7 +21508,7 @@ function assertDependenciesResolved(pluginDir, dependencies, pluginId) {
|
|
|
21420
21508
|
}
|
|
21421
21509
|
let lock;
|
|
21422
21510
|
try {
|
|
21423
|
-
lock = JSON.parse(
|
|
21511
|
+
lock = JSON.parse(import_node_fs12.default.readFileSync(lockPath, "utf-8"));
|
|
21424
21512
|
} catch (err) {
|
|
21425
21513
|
throw new DependencyInstallError(
|
|
21426
21514
|
`package-lock.json \u4E0D\u662F\u5408\u6CD5 JSON \u2014\u2014 \u4F9D\u8D56\u5B89\u88C5\u7ED3\u679C\u4E0D\u53EF\u4FE1\u3002pluginId=${pluginId}`,
|
|
@@ -21493,8 +21581,8 @@ function installPluginDependencies(pluginDir, opts, runNpm = import_node_child_p
|
|
|
21493
21581
|
const { pluginId, dependencies, operation = "install" } = opts;
|
|
21494
21582
|
const depMap = toNpmDependencyMap(dependencies);
|
|
21495
21583
|
if (Object.keys(depMap).length === 0) return;
|
|
21496
|
-
const pkgJsonPath =
|
|
21497
|
-
|
|
21584
|
+
const pkgJsonPath = import_node_path13.default.join(pluginDir, "package.json");
|
|
21585
|
+
import_node_fs12.default.writeFileSync(
|
|
21498
21586
|
pkgJsonPath,
|
|
21499
21587
|
JSON.stringify({ name: pluginId, version: "0.0.0", dependencies: depMap }, null, 2),
|
|
21500
21588
|
"utf-8"
|
|
@@ -22763,20 +22851,20 @@ init_install_utils();
|
|
|
22763
22851
|
|
|
22764
22852
|
// packages/core/security/plugin-signature.ts
|
|
22765
22853
|
var import_node_crypto9 = __toESM(require("node:crypto"), 1);
|
|
22766
|
-
var
|
|
22854
|
+
var import_node_fs13 = __toESM(require("node:fs"), 1);
|
|
22767
22855
|
function computeSha256(input) {
|
|
22768
22856
|
const hash = import_node_crypto9.default.createHash("sha256");
|
|
22769
22857
|
if (typeof input === "string") {
|
|
22770
|
-
if (
|
|
22771
|
-
const fd =
|
|
22858
|
+
if (import_node_fs13.default.existsSync(input)) {
|
|
22859
|
+
const fd = import_node_fs13.default.openSync(input, "r");
|
|
22772
22860
|
const buf = Buffer.alloc(64 * 1024);
|
|
22773
22861
|
let bytesRead = 0;
|
|
22774
22862
|
try {
|
|
22775
|
-
while ((bytesRead =
|
|
22863
|
+
while ((bytesRead = import_node_fs13.default.readSync(fd, buf, 0, buf.length, null)) > 0) {
|
|
22776
22864
|
hash.update(buf.subarray(0, bytesRead));
|
|
22777
22865
|
}
|
|
22778
22866
|
} finally {
|
|
22779
|
-
|
|
22867
|
+
import_node_fs13.default.closeSync(fd);
|
|
22780
22868
|
}
|
|
22781
22869
|
const digest4 = hash.digest();
|
|
22782
22870
|
return { hex: digest4.toString("hex"), base64: digest4.toString("base64") };
|
|
@@ -22845,8 +22933,8 @@ async function extractEmbeddedSignature(zipPathOrBuffer) {
|
|
|
22845
22933
|
const JSZip5 = (await import("jszip")).default;
|
|
22846
22934
|
let buffer;
|
|
22847
22935
|
if (typeof zipPathOrBuffer === "string") {
|
|
22848
|
-
if (!
|
|
22849
|
-
buffer =
|
|
22936
|
+
if (!import_node_fs13.default.existsSync(zipPathOrBuffer)) return null;
|
|
22937
|
+
buffer = import_node_fs13.default.readFileSync(zipPathOrBuffer);
|
|
22850
22938
|
} else {
|
|
22851
22939
|
buffer = zipPathOrBuffer;
|
|
22852
22940
|
}
|
|
@@ -22891,9 +22979,9 @@ function resolveTrustedPublicKeys(explicitKeys, env = process.env) {
|
|
|
22891
22979
|
for (const part of remaining.split(/[,;\n]/)) {
|
|
22892
22980
|
const p = part.trim();
|
|
22893
22981
|
if (!p) continue;
|
|
22894
|
-
if (
|
|
22982
|
+
if (import_node_fs13.default.existsSync(p)) {
|
|
22895
22983
|
try {
|
|
22896
|
-
const fileContent =
|
|
22984
|
+
const fileContent = import_node_fs13.default.readFileSync(p, "utf8").trim();
|
|
22897
22985
|
const filePems = fileContent.match(/-----BEGIN [A-Z0-9 ]+-----[\s\S]+?-----END [A-Z0-9 ]+-----/g);
|
|
22898
22986
|
if (filePems) {
|
|
22899
22987
|
for (const fpem of filePems) {
|
|
@@ -22946,7 +23034,7 @@ async function verifyPluginPackage(options) {
|
|
|
22946
23034
|
const JSZip5 = (await import("jszip")).default;
|
|
22947
23035
|
let buffer;
|
|
22948
23036
|
if (typeof packageSource === "string") {
|
|
22949
|
-
buffer =
|
|
23037
|
+
buffer = import_node_fs13.default.readFileSync(packageSource);
|
|
22950
23038
|
} else {
|
|
22951
23039
|
buffer = packageSource;
|
|
22952
23040
|
}
|
|
@@ -23152,11 +23240,11 @@ var PluginDistributionManager = class {
|
|
|
23152
23240
|
return;
|
|
23153
23241
|
}
|
|
23154
23242
|
const filePath = this.pluginHost.getPluginFilePath(this.pluginHost.resolvePluginUuid(pluginId));
|
|
23155
|
-
const
|
|
23156
|
-
if (!
|
|
23243
|
+
const fs40 = await import("node:fs");
|
|
23244
|
+
if (!fs40.existsSync(filePath)) {
|
|
23157
23245
|
throw new Error(`Cannot reload plugin "${pluginId}": source file missing at ${filePath}`);
|
|
23158
23246
|
}
|
|
23159
|
-
const code =
|
|
23247
|
+
const code = fs40.readFileSync(filePath, "utf-8");
|
|
23160
23248
|
await this.pluginHost.reloadPlugin(pluginId, code);
|
|
23161
23249
|
}
|
|
23162
23250
|
async updateFromZip(zipPath, options = {}) {
|
|
@@ -23345,8 +23433,8 @@ var HotReloadController = class {
|
|
|
23345
23433
|
const timer = setTimeout(async () => {
|
|
23346
23434
|
this.debounceTimers.delete(pluginId);
|
|
23347
23435
|
try {
|
|
23348
|
-
const
|
|
23349
|
-
const newSourceCode = await
|
|
23436
|
+
const fs40 = await import("fs/promises");
|
|
23437
|
+
const newSourceCode = await fs40.readFile(filePath, "utf-8");
|
|
23350
23438
|
console.log(`[HotReload] File changed: ${filePath} \u2192 triggering reload for "${pluginId}"`);
|
|
23351
23439
|
await this.pluginHost.reloadPlugin(pluginId, newSourceCode);
|
|
23352
23440
|
} catch (err) {
|
|
@@ -32579,24 +32667,24 @@ ${formatEnvIssues(warnings)}`);
|
|
|
32579
32667
|
}
|
|
32580
32668
|
|
|
32581
32669
|
// server/security/file-permissions.ts
|
|
32582
|
-
var
|
|
32583
|
-
var
|
|
32670
|
+
var import_node_fs14 = __toESM(require("node:fs"), 1);
|
|
32671
|
+
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
32584
32672
|
var FILE_PERMISSION_BASELINE_DOC = "\u4FEE\u590D\uFF1Achmod 600 <file>\uFF08\u76EE\u5F55 chmod 700\uFF09\uFF1B\u8BE6\u89C1 docs/administrator-guide/admin-manual.md \u6587\u4EF6\u6743\u9650\u57FA\u7EBF\u4E00\u8282\u3002";
|
|
32585
32673
|
function modeOf(p) {
|
|
32586
32674
|
try {
|
|
32587
|
-
return
|
|
32675
|
+
return import_node_fs14.default.statSync(p).mode & 511;
|
|
32588
32676
|
} catch {
|
|
32589
32677
|
return null;
|
|
32590
32678
|
}
|
|
32591
32679
|
}
|
|
32592
32680
|
function defaultPaths() {
|
|
32593
|
-
const dbPath2 = process.env.OPENLEARN_DB_PATH ?
|
|
32594
|
-
const backupDir = process.env.BACKUP_DIR ?
|
|
32681
|
+
const dbPath2 = process.env.OPENLEARN_DB_PATH ? import_node_path14.default.resolve(process.cwd(), process.env.OPENLEARN_DB_PATH) : import_node_path14.default.resolve(process.cwd(), "packages/core/db/educational_os.db");
|
|
32682
|
+
const backupDir = process.env.BACKUP_DIR ? import_node_path14.default.resolve(process.cwd(), process.env.BACKUP_DIR) : import_node_path14.default.join(import_node_path14.default.dirname(dbPath2), "backups");
|
|
32595
32683
|
return {
|
|
32596
|
-
envPath:
|
|
32684
|
+
envPath: import_node_path14.default.resolve(process.cwd(), ".env"),
|
|
32597
32685
|
dbPath: dbPath2,
|
|
32598
32686
|
backupDir,
|
|
32599
|
-
encryptionKeyPath:
|
|
32687
|
+
encryptionKeyPath: import_node_path14.default.resolve(process.cwd(), "storage", "encryption-key")
|
|
32600
32688
|
};
|
|
32601
32689
|
}
|
|
32602
32690
|
function checkSensitiveFilePermissions(options = {}) {
|
|
@@ -32627,11 +32715,11 @@ function checkSensitiveFilePermissions(options = {}) {
|
|
|
32627
32715
|
}
|
|
32628
32716
|
if (dirMode !== null) {
|
|
32629
32717
|
let scanned = 0;
|
|
32630
|
-
for (const entry of
|
|
32718
|
+
for (const entry of import_node_fs14.default.readdirSync(backupDir)) {
|
|
32631
32719
|
if (scanned >= maxBackupFiles) break;
|
|
32632
32720
|
if (!entry.endsWith(".db")) continue;
|
|
32633
32721
|
scanned++;
|
|
32634
|
-
checkFile(
|
|
32722
|
+
checkFile(import_node_path14.default.join(backupDir, entry), "600", "backup-file");
|
|
32635
32723
|
}
|
|
32636
32724
|
}
|
|
32637
32725
|
return issues;
|
|
@@ -33531,8 +33619,8 @@ var runOpenAIAgentChat = async (provider, request) => {
|
|
|
33531
33619
|
};
|
|
33532
33620
|
|
|
33533
33621
|
// server/backup-scheduler.ts
|
|
33534
|
-
var
|
|
33535
|
-
var
|
|
33622
|
+
var import_node_fs15 = __toESM(require("node:fs"), 1);
|
|
33623
|
+
var import_node_path15 = __toESM(require("node:path"), 1);
|
|
33536
33624
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
33537
33625
|
var DAY_MS2 = 24 * HOUR_MS;
|
|
33538
33626
|
var WAL_FULL_THRESHOLD_BYTES = 50 * 1024 * 1024;
|
|
@@ -33549,8 +33637,8 @@ function shouldFullCheckpoint(walBytes, threshold = WAL_FULL_THRESHOLD_BYTES) {
|
|
|
33549
33637
|
}
|
|
33550
33638
|
function resolveDbPath(explicit) {
|
|
33551
33639
|
if (explicit) return explicit;
|
|
33552
|
-
if (process.env.OPENLEARN_DB_PATH) return
|
|
33553
|
-
return
|
|
33640
|
+
if (process.env.OPENLEARN_DB_PATH) return import_node_path15.default.resolve(process.cwd(), process.env.OPENLEARN_DB_PATH);
|
|
33641
|
+
return import_node_path15.default.resolve(process.cwd(), "packages/core/db/educational_os.db");
|
|
33554
33642
|
}
|
|
33555
33643
|
function startBackupScheduler(deps) {
|
|
33556
33644
|
const noop = { stop: () => {
|
|
@@ -33578,7 +33666,7 @@ function startBackupScheduler(deps) {
|
|
|
33578
33666
|
try {
|
|
33579
33667
|
let walBytes = 0;
|
|
33580
33668
|
try {
|
|
33581
|
-
walBytes =
|
|
33669
|
+
walBytes = import_node_fs15.default.statSync(dbPath2 + "-wal").size;
|
|
33582
33670
|
} catch {
|
|
33583
33671
|
walBytes = 0;
|
|
33584
33672
|
}
|
|
@@ -33669,9 +33757,9 @@ function isWhitelisted(route, whitelist) {
|
|
|
33669
33757
|
(entry) => entry.method.toUpperCase() === route.method && normalizePath2(entry.path) === normalizePath2(route.path)
|
|
33670
33758
|
);
|
|
33671
33759
|
}
|
|
33672
|
-
function normalizePath2(
|
|
33673
|
-
if (!
|
|
33674
|
-
return
|
|
33760
|
+
function normalizePath2(path46) {
|
|
33761
|
+
if (!path46) return "/";
|
|
33762
|
+
return path46.endsWith("/") && path46.length > 1 ? path46.slice(0, -1) : path46;
|
|
33675
33763
|
}
|
|
33676
33764
|
function auditRouteScopes(app, whitelist = []) {
|
|
33677
33765
|
const declared = [];
|
|
@@ -33849,11 +33937,11 @@ function enforcePasswordChanged(req, res, next) {
|
|
|
33849
33937
|
if (isPasswordChangeGateSkipped()) {
|
|
33850
33938
|
return next();
|
|
33851
33939
|
}
|
|
33852
|
-
const
|
|
33853
|
-
if (!
|
|
33940
|
+
const path46 = req.path || "";
|
|
33941
|
+
if (!path46.startsWith("/api/")) {
|
|
33854
33942
|
return next();
|
|
33855
33943
|
}
|
|
33856
|
-
if (PASSWORD_CHANGE_EXEMPT_PATHS.has(
|
|
33944
|
+
if (PASSWORD_CHANGE_EXEMPT_PATHS.has(path46)) {
|
|
33857
33945
|
return next();
|
|
33858
33946
|
}
|
|
33859
33947
|
const token = getCookieToken(req);
|
|
@@ -33928,8 +34016,8 @@ function csrfGuard(req, res, next) {
|
|
|
33928
34016
|
next();
|
|
33929
34017
|
return;
|
|
33930
34018
|
}
|
|
33931
|
-
const
|
|
33932
|
-
if (EXEMPT_PREFIXES.some((p) =>
|
|
34019
|
+
const path47 = req.path;
|
|
34020
|
+
if (EXEMPT_PREFIXES.some((p) => path47 === p || path47.startsWith(p))) {
|
|
33933
34021
|
next();
|
|
33934
34022
|
return;
|
|
33935
34023
|
}
|
|
@@ -33939,10 +34027,10 @@ function csrfGuard(req, res, next) {
|
|
|
33939
34027
|
const origin = req.headers.origin;
|
|
33940
34028
|
const referer = req.headers.referer;
|
|
33941
34029
|
const host = req.headers.host;
|
|
33942
|
-
const
|
|
34030
|
+
const path46 = req.path;
|
|
33943
34031
|
if (typeof origin === "string" && origin !== "") {
|
|
33944
34032
|
if (origin === "null") {
|
|
33945
|
-
if (!EXEMPT_PREFIXES.some((p) =>
|
|
34033
|
+
if (!EXEMPT_PREFIXES.some((p) => path46 === p || path46.startsWith(p))) {
|
|
33946
34034
|
res.status(403).json({ success: false, code: "FORBIDDEN_CROSS_SITE", error: "Opaque origin write request blocked" });
|
|
33947
34035
|
return;
|
|
33948
34036
|
}
|
|
@@ -33950,7 +34038,7 @@ function csrfGuard(req, res, next) {
|
|
|
33950
34038
|
try {
|
|
33951
34039
|
const originHost = new URL(origin).host;
|
|
33952
34040
|
if (host && originHost.toLowerCase() !== host.toLowerCase()) {
|
|
33953
|
-
if (!EXEMPT_PREFIXES.some((p) =>
|
|
34041
|
+
if (!EXEMPT_PREFIXES.some((p) => path46 === p || path46.startsWith(p))) {
|
|
33954
34042
|
res.status(403).json({
|
|
33955
34043
|
success: false,
|
|
33956
34044
|
code: "FORBIDDEN_CROSS_SITE",
|
|
@@ -33968,7 +34056,7 @@ function csrfGuard(req, res, next) {
|
|
|
33968
34056
|
try {
|
|
33969
34057
|
const refererHost = new URL(referer).host;
|
|
33970
34058
|
if (host && refererHost.toLowerCase() !== host.toLowerCase()) {
|
|
33971
|
-
if (!EXEMPT_PREFIXES.some((p) =>
|
|
34059
|
+
if (!EXEMPT_PREFIXES.some((p) => path46 === p || path46.startsWith(p))) {
|
|
33972
34060
|
res.status(403).json({
|
|
33973
34061
|
success: false,
|
|
33974
34062
|
code: "FORBIDDEN_CROSS_SITE",
|
|
@@ -34021,8 +34109,8 @@ function parseSentryDsn(dsn) {
|
|
|
34021
34109
|
const projectId = url.pathname.split("/").filter(Boolean).pop();
|
|
34022
34110
|
if (!projectId || !/^\d+$/.test(projectId)) return null;
|
|
34023
34111
|
const prefix = url.pathname.split("/").filter(Boolean).slice(0, -1).join("/");
|
|
34024
|
-
const
|
|
34025
|
-
const envelopeUrl = `${url.protocol}//${url.host}${
|
|
34112
|
+
const path46 = prefix ? `/${prefix}` : "";
|
|
34113
|
+
const envelopeUrl = `${url.protocol}//${url.host}${path46}/api/${projectId}/envelope/`;
|
|
34026
34114
|
return { envelopeUrl, publicKey };
|
|
34027
34115
|
} catch {
|
|
34028
34116
|
return null;
|
|
@@ -34462,23 +34550,23 @@ function createSocketIoCors(options) {
|
|
|
34462
34550
|
}
|
|
34463
34551
|
|
|
34464
34552
|
// server/routes/health.ts
|
|
34465
|
-
var
|
|
34466
|
-
var
|
|
34553
|
+
var import_node_fs17 = __toESM(require("node:fs"), 1);
|
|
34554
|
+
var import_node_path17 = __toESM(require("node:path"), 1);
|
|
34467
34555
|
|
|
34468
34556
|
// packages/core/observability/health.ts
|
|
34469
|
-
var
|
|
34470
|
-
var
|
|
34557
|
+
var import_node_fs16 = __toESM(require("node:fs"), 1);
|
|
34558
|
+
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
34471
34559
|
var import_node_crypto13 = require("node:crypto");
|
|
34472
34560
|
function probeStorageWritable(dir) {
|
|
34473
|
-
const probe =
|
|
34561
|
+
const probe = import_node_path16.default.join(dir, `.ready-${process.pid}-${(0, import_node_crypto13.randomBytes)(4).toString("hex")}`);
|
|
34474
34562
|
try {
|
|
34475
|
-
|
|
34476
|
-
|
|
34477
|
-
|
|
34563
|
+
import_node_fs16.default.mkdirSync(dir, { recursive: true });
|
|
34564
|
+
import_node_fs16.default.writeFileSync(probe, "ok", { flag: "w" });
|
|
34565
|
+
import_node_fs16.default.unlinkSync(probe);
|
|
34478
34566
|
return true;
|
|
34479
34567
|
} catch {
|
|
34480
34568
|
try {
|
|
34481
|
-
|
|
34569
|
+
import_node_fs16.default.unlinkSync(probe);
|
|
34482
34570
|
} catch {
|
|
34483
34571
|
}
|
|
34484
34572
|
return false;
|
|
@@ -34486,15 +34574,15 @@ function probeStorageWritable(dir) {
|
|
|
34486
34574
|
}
|
|
34487
34575
|
function listMigrationNames(migrationsDir) {
|
|
34488
34576
|
try {
|
|
34489
|
-
if (!
|
|
34490
|
-
const names =
|
|
34577
|
+
if (!import_node_fs16.default.existsSync(migrationsDir)) return null;
|
|
34578
|
+
const names = import_node_fs16.default.readdirSync(migrationsDir).filter((file) => file.endsWith(".sql")).map((file) => import_node_path16.default.basename(file, ".sql"));
|
|
34491
34579
|
return names.length > 0 ? names : null;
|
|
34492
34580
|
} catch {
|
|
34493
34581
|
return null;
|
|
34494
34582
|
}
|
|
34495
34583
|
}
|
|
34496
34584
|
function migrationRecordName(name) {
|
|
34497
|
-
return
|
|
34585
|
+
return import_node_path16.default.basename(name.replace(/\r/g, "").trim(), ".sql");
|
|
34498
34586
|
}
|
|
34499
34587
|
function migrationsComplete(expected, applied) {
|
|
34500
34588
|
if (!expected) return { ok: false, reason: "migrations" };
|
|
@@ -34586,15 +34674,15 @@ function countPendingMigrations(migrationsDir, appliedMigrations) {
|
|
|
34586
34674
|
function newestBackupAgeSeconds(backupDir, now) {
|
|
34587
34675
|
let names;
|
|
34588
34676
|
try {
|
|
34589
|
-
if (!
|
|
34590
|
-
names =
|
|
34677
|
+
if (!import_node_fs17.default.existsSync(backupDir)) return OPERATIONAL_GAUGE_UNKNOWN;
|
|
34678
|
+
names = import_node_fs17.default.readdirSync(backupDir);
|
|
34591
34679
|
} catch {
|
|
34592
34680
|
return OPERATIONAL_GAUGE_UNKNOWN;
|
|
34593
34681
|
}
|
|
34594
34682
|
let newestMs = Number.NEGATIVE_INFINITY;
|
|
34595
34683
|
for (const name of names) {
|
|
34596
34684
|
try {
|
|
34597
|
-
const stat =
|
|
34685
|
+
const stat = import_node_fs17.default.statSync(import_node_path17.default.join(backupDir, name));
|
|
34598
34686
|
if (!stat.isFile()) continue;
|
|
34599
34687
|
if (stat.mtimeMs > newestMs) newestMs = stat.mtimeMs;
|
|
34600
34688
|
} catch {
|
|
@@ -34626,7 +34714,7 @@ function defaultCountEvents() {
|
|
|
34626
34714
|
function registerHealthRoutes(app, options) {
|
|
34627
34715
|
const startTime = options?.startTime ?? Date.now();
|
|
34628
34716
|
const platformVersion = options?.platformVersion ?? PLATFORM_VERSION;
|
|
34629
|
-
const migrationsDir = options?.migrationsDir ??
|
|
34717
|
+
const migrationsDir = options?.migrationsDir ?? import_node_path17.default.join(process.cwd(), "migrations");
|
|
34630
34718
|
const storageDir = options?.storageDir ?? getStorageDir();
|
|
34631
34719
|
const backupDir = options?.backupDir ?? getBackupsDir();
|
|
34632
34720
|
const countEvents = options?.countEvents ?? defaultCountEvents;
|
|
@@ -34746,8 +34834,8 @@ var import_express2 = __toESM(require("express"), 1);
|
|
|
34746
34834
|
var import_zod2 = require("zod");
|
|
34747
34835
|
|
|
34748
34836
|
// server/services/course-package-assets.ts
|
|
34749
|
-
var
|
|
34750
|
-
var
|
|
34837
|
+
var import_node_fs18 = __toESM(require("node:fs"), 1);
|
|
34838
|
+
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
34751
34839
|
var import_node_crypto14 = __toESM(require("node:crypto"), 1);
|
|
34752
34840
|
var import_jszip3 = __toESM(require("jszip"), 1);
|
|
34753
34841
|
var import_jspdf = require("jspdf");
|
|
@@ -34991,8 +35079,8 @@ async function materializePackageAssets(assets) {
|
|
|
34991
35079
|
const result = /* @__PURE__ */ new Map();
|
|
34992
35080
|
if (assets.length === 0) return result;
|
|
34993
35081
|
const uploadsDir = getUploadsDir();
|
|
34994
|
-
if (!
|
|
34995
|
-
|
|
35082
|
+
if (!import_node_fs18.default.existsSync(uploadsDir)) {
|
|
35083
|
+
import_node_fs18.default.mkdirSync(uploadsDir, { recursive: true });
|
|
34996
35084
|
}
|
|
34997
35085
|
const writtenPaths = [];
|
|
34998
35086
|
try {
|
|
@@ -35014,8 +35102,8 @@ async function materializePackageAssets(assets) {
|
|
|
35014
35102
|
throw new Error(`\u8BFE\u7A0B\u5305\u8D44\u4EA7 "${asset.id}" \u58F0\u660E\u4E3A ${asset.contentType} \u4F46\u5185\u5BB9\u662F\u4E8C\u8FDB\u5236`);
|
|
35015
35103
|
}
|
|
35016
35104
|
const uniqueName = `${Date.now()}-${import_node_crypto14.default.randomBytes(4).toString("hex")}${ext}`;
|
|
35017
|
-
const filePath =
|
|
35018
|
-
|
|
35105
|
+
const filePath = import_node_path18.default.join(uploadsDir, uniqueName);
|
|
35106
|
+
import_node_fs18.default.writeFileSync(filePath, buffer);
|
|
35019
35107
|
writtenPaths.push(filePath);
|
|
35020
35108
|
const slideCount = isEmbeddableAssetType(asset.contentType) ? asset.contentType === "pdf" ? countPdfPages(buffer) : await countPptxSlides(buffer) : 0;
|
|
35021
35109
|
result.set(asset.id, {
|
|
@@ -35029,7 +35117,7 @@ async function materializePackageAssets(assets) {
|
|
|
35029
35117
|
} catch (err) {
|
|
35030
35118
|
for (const filePath of writtenPaths) {
|
|
35031
35119
|
try {
|
|
35032
|
-
|
|
35120
|
+
import_node_fs18.default.unlinkSync(filePath);
|
|
35033
35121
|
} catch (cleanupErr) {
|
|
35034
35122
|
console.warn("[course-package] \u9644\u4EF6\u8865\u507F\u6E05\u7406\u5931\u8D25:", cleanupErr.message);
|
|
35035
35123
|
}
|
|
@@ -40031,7 +40119,7 @@ ${runtimeScripts}` : BRIDGE_SDK_CODE;
|
|
|
40031
40119
|
}
|
|
40032
40120
|
|
|
40033
40121
|
// server/routes/lessons.ts
|
|
40034
|
-
var
|
|
40122
|
+
var import_node_fs19 = __toESM(require("node:fs"), 1);
|
|
40035
40123
|
|
|
40036
40124
|
// server/services/diagnostic-service.ts
|
|
40037
40125
|
var import_crypto12 = require("crypto");
|
|
@@ -41943,8 +42031,8 @@ function registerLessonsRoutes(ctx) {
|
|
|
41943
42031
|
passed += 1;
|
|
41944
42032
|
try {
|
|
41945
42033
|
const dir = getUploadsDir();
|
|
41946
|
-
|
|
41947
|
-
|
|
42034
|
+
import_node_fs19.default.mkdirSync(dir, { recursive: true });
|
|
42035
|
+
import_node_fs19.default.accessSync(dir, import_node_fs19.default.constants.W_OK);
|
|
41948
42036
|
checks.staticResources = { status: "passed", message: "\u8BFE\u4EF6\u5B58\u50A8\u76EE\u5F55\u53EF\u5199" };
|
|
41949
42037
|
passed += 1;
|
|
41950
42038
|
} catch (err) {
|
|
@@ -41977,8 +42065,8 @@ function registerLessonsRoutes(ctx) {
|
|
|
41977
42065
|
}
|
|
41978
42066
|
|
|
41979
42067
|
// server/services/course-package-import.ts
|
|
41980
|
-
var
|
|
41981
|
-
var
|
|
42068
|
+
var import_node_fs20 = __toESM(require("node:fs"), 1);
|
|
42069
|
+
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
41982
42070
|
var import_uuid18 = require("uuid");
|
|
41983
42071
|
|
|
41984
42072
|
// server/services/course-package-identity.ts
|
|
@@ -42253,11 +42341,11 @@ function findReferencedAsset(rawData, assets) {
|
|
|
42253
42341
|
function cleanupMaterializedAssets(materialized) {
|
|
42254
42342
|
const uploadsDir = getUploadsDir();
|
|
42255
42343
|
for (const asset of materialized.values()) {
|
|
42256
|
-
const fileName =
|
|
42257
|
-
const target =
|
|
42344
|
+
const fileName = import_node_path19.default.basename(asset.fileUrl);
|
|
42345
|
+
const target = import_node_path19.default.join(uploadsDir, fileName);
|
|
42258
42346
|
try {
|
|
42259
|
-
if (
|
|
42260
|
-
|
|
42347
|
+
if (import_node_fs20.default.existsSync(target)) {
|
|
42348
|
+
import_node_fs20.default.unlinkSync(target);
|
|
42261
42349
|
}
|
|
42262
42350
|
} catch (err) {
|
|
42263
42351
|
console.warn(`[course-package] \u6E05\u7406\u5B64\u513F\u9644\u4EF6\u5931\u8D25\uFF1A${target}`, err.message);
|
|
@@ -48334,7 +48422,7 @@ async function checkVersion(source, installedVersion) {
|
|
|
48334
48422
|
init_audit_log();
|
|
48335
48423
|
|
|
48336
48424
|
// server/routes/plugin-api-gateway.ts
|
|
48337
|
-
var
|
|
48425
|
+
var import_node_path20 = __toESM(require("node:path"), 1);
|
|
48338
48426
|
init_http_router();
|
|
48339
48427
|
init_interfaces();
|
|
48340
48428
|
var RESERVED_ACTIONS = /* @__PURE__ */ new Set([
|
|
@@ -48508,7 +48596,7 @@ async function pluginApiGatewayMiddleware(req, res, next) {
|
|
|
48508
48596
|
res.status(400).json({ success: false, error: "Path traversal attempt detected" });
|
|
48509
48597
|
return;
|
|
48510
48598
|
}
|
|
48511
|
-
const normalizedSubPath =
|
|
48599
|
+
const normalizedSubPath = import_node_path20.default.posix.normalize("/" + rawSubPath);
|
|
48512
48600
|
if (normalizedSubPath.includes("..")) {
|
|
48513
48601
|
res.status(400).json({ success: false, error: "Path traversal attempt detected" });
|
|
48514
48602
|
return;
|
|
@@ -48945,8 +49033,8 @@ init_fetch_with_retry();
|
|
|
48945
49033
|
|
|
48946
49034
|
// server/services/community-registry.ts
|
|
48947
49035
|
var import_semver7 = __toESM(require("semver"), 1);
|
|
48948
|
-
var
|
|
48949
|
-
var
|
|
49036
|
+
var import_node_fs21 = __toESM(require("node:fs"), 1);
|
|
49037
|
+
var import_node_path21 = __toESM(require("node:path"), 1);
|
|
48950
49038
|
var import_node_os3 = __toESM(require("node:os"), 1);
|
|
48951
49039
|
var import_node_crypto16 = require("node:crypto");
|
|
48952
49040
|
var COMMUNITY_REGISTRY_ENV = "PLUGIN_COMMUNITY_REGISTRY_URL";
|
|
@@ -49168,8 +49256,8 @@ async function downloadPluginPackage(downloadUrl, options = {}) {
|
|
|
49168
49256
|
if (!response.body) {
|
|
49169
49257
|
throw new Error("\u4E0B\u8F7D\u63D2\u4EF6\u5305\u5931\u8D25: \u54CD\u5E94\u4F53\u4E3A\u7A7A");
|
|
49170
49258
|
}
|
|
49171
|
-
const zipPath =
|
|
49172
|
-
const out =
|
|
49259
|
+
const zipPath = import_node_path21.default.join(import_node_os3.default.tmpdir(), `plugin-download-${(0, import_node_crypto16.randomUUID)()}.zip`);
|
|
49260
|
+
const out = import_node_fs21.default.createWriteStream(zipPath);
|
|
49173
49261
|
let received = 0;
|
|
49174
49262
|
try {
|
|
49175
49263
|
for await (const chunk of response.body) {
|
|
@@ -49191,13 +49279,13 @@ async function downloadPluginPackage(downloadUrl, options = {}) {
|
|
|
49191
49279
|
} catch (err) {
|
|
49192
49280
|
out.destroy();
|
|
49193
49281
|
try {
|
|
49194
|
-
|
|
49282
|
+
import_node_fs21.default.rmSync(zipPath, { force: true });
|
|
49195
49283
|
} catch {
|
|
49196
49284
|
}
|
|
49197
49285
|
throw err;
|
|
49198
49286
|
}
|
|
49199
49287
|
if (received === 0) {
|
|
49200
|
-
|
|
49288
|
+
import_node_fs21.default.rmSync(zipPath, { force: true });
|
|
49201
49289
|
throw new Error("\u63D2\u4EF6\u5305\u4E3A\u7A7A");
|
|
49202
49290
|
}
|
|
49203
49291
|
return { zipPath, filename: resolveFilename(response, downloadUrl), bytes: received };
|
|
@@ -57121,8 +57209,10 @@ async function startServer() {
|
|
|
57121
57209
|
if (migrations.length === 0) {
|
|
57122
57210
|
throw new Error(`[Migration] FATAL: ${migrationsDir} \u4E2D\u6CA1\u6709\u8FC1\u79FB\u6587\u4EF6\uFF0C\u62D2\u7EDD\u542F\u52A8\u3002`);
|
|
57123
57211
|
}
|
|
57124
|
-
runMigrations(kernelContainer.db, migrations);
|
|
57212
|
+
runMigrations(kernelContainer.db, migrations, { strictLedger: true });
|
|
57125
57213
|
console.log(`[Migration] \u542F\u52A8\u590D\u67E5\u5B8C\u6210\uFF1A${migrationsDir}\uFF08${migrations.length} \u4E2A\u6587\u4EF6\uFF09`);
|
|
57214
|
+
assertSchemaMatchesGolden(kernelContainer.db, migrationsDir);
|
|
57215
|
+
console.log("[SchemaGolden] \u542F\u52A8\u7EC8\u68C0\u901A\u8FC7\uFF1A\u6570\u636E\u5E93\u7ED3\u6784\u8986\u76D6\u4EE3\u7801\u671F\u671B\u7684\u5168\u90E8\u8868\u4E0E\u5217\u3002");
|
|
57126
57216
|
} catch (err) {
|
|
57127
57217
|
console.error("[Migration] FATAL: \u6570\u636E\u5E93\u8FC1\u79FB\u5931\u8D25\uFF0C\u62D2\u7EDD\u5728\u672A\u5B8C\u6210\u8FC1\u79FB\u7684\u5E93\u4E0A\u63D0\u4F9B\u670D\u52A1\u3002");
|
|
57128
57218
|
console.error(err);
|