openlearn-next 0.2.5 → 0.2.6
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/README.md +34 -136
- package/dist/assets/{Dashboard-BnF7YAQi.js → Dashboard-Xly67TyD.js} +2 -2
- package/dist/assets/{HelpView-BP581QYZ.js → HelpView-DgFLEuso.js} +2 -2
- package/dist/assets/{InteractiveWhiteboard-D360IHbP.js → InteractiveWhiteboard-DDOKVrHd.js} +8 -8
- package/dist/assets/{LazyWhiteboard-B5JmqHZ5.js → LazyWhiteboard-C9fzRtzL.js} +1 -1
- package/dist/assets/{LessonEditorView-C0czhtmf.js → LessonEditorView-D1nhlQon.js} +1 -1
- package/dist/assets/{LiveClassroomView-CrTY3jhP.js → LiveClassroomView-C1lI4lFd.js} +1 -1
- package/dist/assets/{PluginView-QS2b6nSF.js → PluginView-5TYIP1ze.js} +2 -2
- package/dist/assets/{StudentAssignmentView-YxVJ5wkl.js → StudentAssignmentView-CCGWFVqD.js} +1 -1
- package/dist/assets/{StudentLessonView-D1gYdLM1.js → StudentLessonView-yhRHP8e8.js} +1 -1
- package/dist/assets/{StudentPreviewModal-CG8bgUeI.js → StudentPreviewModal-UpNg1yl0.js} +1 -1
- package/dist/assets/{StudentView-CmSL3cDw.js → StudentView-C1cc6RuC.js} +1 -1
- package/dist/assets/{TeacherView-BqXb0izg.js → TeacherView-CWjbV0x-.js} +1 -1
- package/dist/assets/index-B9cwmoaG.js +129 -0
- package/dist/index.html +1 -1
- package/dist/server.cjs +99 -37
- package/package.json +1 -1
- package/dist/assets/index-BxA9vp--.js +0 -129
package/dist/index.html
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
</script>
|
|
18
|
-
<script type="module" crossorigin src="/assets/index-
|
|
18
|
+
<script type="module" crossorigin src="/assets/index-B9cwmoaG.js"></script>
|
|
19
19
|
<link rel="stylesheet" crossorigin href="/assets/index-CATki2cp.css">
|
|
20
20
|
</head>
|
|
21
21
|
<body>
|
package/dist/server.cjs
CHANGED
|
@@ -2659,6 +2659,21 @@ parentPort.on('message', async function(msg) {
|
|
|
2659
2659
|
},
|
|
2660
2660
|
db: dbApi,
|
|
2661
2661
|
require: function(moduleName) {
|
|
2662
|
+
var BLOCKED_NATIVE_MODULES = [
|
|
2663
|
+
'child_process', 'node:child_process',
|
|
2664
|
+
'fs', 'node:fs', 'fs/promises', 'node:fs/promises',
|
|
2665
|
+
'net', 'node:net',
|
|
2666
|
+
'http', 'node:http', 'https', 'node:https', 'http2', 'node:http2',
|
|
2667
|
+
'dgram', 'node:dgram', 'dns', 'node:dns',
|
|
2668
|
+
'cluster', 'node:cluster',
|
|
2669
|
+
'worker_threads', 'node:worker_threads',
|
|
2670
|
+
'vm', 'node:vm',
|
|
2671
|
+
'v8', 'node:v8',
|
|
2672
|
+
'wasi', 'node:wasi'
|
|
2673
|
+
];
|
|
2674
|
+
if (BLOCKED_NATIVE_MODULES.indexOf(moduleName) !== -1) {
|
|
2675
|
+
throw new Error('[SecurityError] Direct access to Node.js native module "' + moduleName + '" is forbidden in plugin worker sandbox.');
|
|
2676
|
+
}
|
|
2662
2677
|
if (PLUGIN_SHARED_MODULES.indexOf(moduleName) !== -1) {
|
|
2663
2678
|
try {
|
|
2664
2679
|
return requireFn(moduleName);
|
|
@@ -3374,7 +3389,16 @@ var CapabilityGuard = class {
|
|
|
3374
3389
|
constructor() {
|
|
3375
3390
|
this.actorCapabilities = /* @__PURE__ */ new Map();
|
|
3376
3391
|
this.actorCapabilities.set("user-demo", ["*:*:*"]);
|
|
3377
|
-
this.actorCapabilities.set("user-frontend", [
|
|
3392
|
+
this.actorCapabilities.set("user-frontend", [
|
|
3393
|
+
"lesson:*",
|
|
3394
|
+
"whiteboard:*",
|
|
3395
|
+
"management:*",
|
|
3396
|
+
"quiz:*",
|
|
3397
|
+
"vfs:*",
|
|
3398
|
+
"process:*",
|
|
3399
|
+
"plugin:*"
|
|
3400
|
+
]);
|
|
3401
|
+
this.actorCapabilities.set("anonymous", []);
|
|
3378
3402
|
this.actorCapabilities.set("agent-system-0", [
|
|
3379
3403
|
"lesson:write",
|
|
3380
3404
|
"lesson:delete",
|
|
@@ -3388,8 +3412,8 @@ var CapabilityGuard = class {
|
|
|
3388
3412
|
"process:write",
|
|
3389
3413
|
"process:read"
|
|
3390
3414
|
]);
|
|
3391
|
-
this.actorCapabilities.set("teacher-demo", ["lesson
|
|
3392
|
-
this.actorCapabilities.set("student-demo", ["student:write"]);
|
|
3415
|
+
this.actorCapabilities.set("teacher-demo", ["lesson:*", "whiteboard:*", "management:*", "quiz:*", "vfs:*"]);
|
|
3416
|
+
this.actorCapabilities.set("student-demo", ["student:write", "lesson:read", "whiteboard:read"]);
|
|
3393
3417
|
}
|
|
3394
3418
|
grant(actorId, cap) {
|
|
3395
3419
|
const caps = this.actorCapabilities.get(actorId) || [];
|
|
@@ -3402,8 +3426,28 @@ var CapabilityGuard = class {
|
|
|
3402
3426
|
this.actorCapabilities.delete(actorId);
|
|
3403
3427
|
}
|
|
3404
3428
|
check(actorId, requiredCap) {
|
|
3405
|
-
const isAdmin = actorId === "role:administrator" || actorId?.endsWith(":administrator") || actorId === "admin-demo";
|
|
3429
|
+
const isAdmin = actorId === "role:administrator" || actorId?.endsWith(":administrator") || actorId === "admin" || actorId === "usr_admin" || actorId === "admin-demo";
|
|
3406
3430
|
if (isAdmin) return true;
|
|
3431
|
+
if (actorId?.endsWith(":teacher")) {
|
|
3432
|
+
const teacherCaps = ["lesson:*", "whiteboard:*", "management:*", "quiz:*", "vfs:*", "process:*", "plugin:*"];
|
|
3433
|
+
const [reqRes2, reqAct2] = requiredCap.split(":");
|
|
3434
|
+
if (teacherCaps.some((c) => {
|
|
3435
|
+
const [res, act] = c.split(":");
|
|
3436
|
+
return (res === reqRes2 || res === "*") && (act === reqAct2 || act === "*");
|
|
3437
|
+
})) {
|
|
3438
|
+
return true;
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
if (actorId?.endsWith(":student")) {
|
|
3442
|
+
const studentCaps = ["student:write", "lesson:read", "whiteboard:read"];
|
|
3443
|
+
const [reqRes2, reqAct2] = requiredCap.split(":");
|
|
3444
|
+
if (studentCaps.some((c) => {
|
|
3445
|
+
const [res, act] = c.split(":");
|
|
3446
|
+
return (res === reqRes2 || res === "*") && (act === reqAct2 || act === "*");
|
|
3447
|
+
})) {
|
|
3448
|
+
return true;
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3407
3451
|
const caps = this.actorCapabilities.get(actorId) || [];
|
|
3408
3452
|
if (caps.includes("*:*:*") || caps.includes("*")) return true;
|
|
3409
3453
|
if (caps.includes(requiredCap)) return true;
|
|
@@ -3638,7 +3682,7 @@ var classroomToolSchema = import_zod.z.object({
|
|
|
3638
3682
|
icon: import_zod.z.string().optional(),
|
|
3639
3683
|
description: import_zod.z.string().optional(),
|
|
3640
3684
|
commandType: import_zod.z.string().min(1),
|
|
3641
|
-
payload: import_zod.z.record(import_zod.z.unknown()).optional()
|
|
3685
|
+
payload: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
3642
3686
|
});
|
|
3643
3687
|
var contributesSchema = import_zod.z.object({
|
|
3644
3688
|
"classroom.tool": import_zod.z.array(classroomToolSchema).optional(),
|
|
@@ -3665,7 +3709,10 @@ var contributesSchema = import_zod.z.object({
|
|
|
3665
3709
|
label: import_zod.z.string().min(1),
|
|
3666
3710
|
icon: import_zod.z.string().optional()
|
|
3667
3711
|
})).optional()
|
|
3668
|
-
|
|
3712
|
+
// v0.2.6: 锚点槽位(anchor:*)为开放命名空间,由宿主公布锚点 id。
|
|
3713
|
+
// .passthrough() 保留任意 anchor:* 键(运行时透传),避免被 zod 默认 strip。
|
|
3714
|
+
// 锚点条目的运行时校验由前端注册时(ExtensionPointConfig)完成。
|
|
3715
|
+
}).passthrough().optional();
|
|
3669
3716
|
var deploySchema = import_zod.z.object({
|
|
3670
3717
|
script: import_zod.z.string().min(1, { error: "deploy.script \u4E0D\u80FD\u4E3A\u7A7A" }).optional(),
|
|
3671
3718
|
staticRoute: import_zod.z.string().optional(),
|
|
@@ -3689,7 +3736,7 @@ var manifestSchema = import_zod.z.object({
|
|
|
3689
3736
|
pluginDependencies: import_zod.z.array(import_zod.z.string().min(1)).optional(),
|
|
3690
3737
|
provides: import_zod.z.array(import_zod.z.string().min(1)).optional(),
|
|
3691
3738
|
configuration: import_zod.z.object({
|
|
3692
|
-
properties: import_zod.z.record(import_zod.z.object({
|
|
3739
|
+
properties: import_zod.z.record(import_zod.z.string(), import_zod.z.object({
|
|
3693
3740
|
type: import_zod.z.enum(["string", "number", "boolean", "integer"]),
|
|
3694
3741
|
default: import_zod.z.unknown().optional(),
|
|
3695
3742
|
description: import_zod.z.string().optional(),
|
|
@@ -4116,6 +4163,17 @@ db.exec(`
|
|
|
4116
4163
|
status TEXT NOT NULL DEFAULT 'draft',
|
|
4117
4164
|
graded_at INTEGER
|
|
4118
4165
|
);
|
|
4166
|
+
|
|
4167
|
+
-- Performance Indexes
|
|
4168
|
+
CREATE INDEX IF NOT EXISTS idx_whiteboard_lesson ON whiteboard_elements(lesson_id);
|
|
4169
|
+
CREATE INDEX IF NOT EXISTS idx_class_students_class ON class_students(class_id);
|
|
4170
|
+
CREATE INDEX IF NOT EXISTS idx_class_students_student ON class_students(student_id);
|
|
4171
|
+
CREATE INDEX IF NOT EXISTS idx_schedules_class_date ON schedules(class_id, scheduled_date);
|
|
4172
|
+
CREATE INDEX IF NOT EXISTS idx_courseware_attempt_cw_st ON courseware_attempt(courseware_id, student_id);
|
|
4173
|
+
CREATE INDEX IF NOT EXISTS idx_submission_result_attempt ON submission_result(attempt_id);
|
|
4174
|
+
CREATE INDEX IF NOT EXISTS idx_events_type_time ON events(type, timestamp);
|
|
4175
|
+
CREATE INDEX IF NOT EXISTS idx_assignments_class ON assignments(class_id);
|
|
4176
|
+
CREATE INDEX IF NOT EXISTS idx_attendance_schedule ON attendance(schedule_id);
|
|
4119
4177
|
`);
|
|
4120
4178
|
try {
|
|
4121
4179
|
db.exec(`ALTER TABLE plugins ADD COLUMN execution_mode TEXT DEFAULT 'inline'`);
|
|
@@ -4250,7 +4308,7 @@ var VfsPlugin = {
|
|
|
4250
4308
|
"@openlearn/core:IDatabase@^1.0.0"
|
|
4251
4309
|
],
|
|
4252
4310
|
capabilitiesProposed: ["vfs:read", "vfs:write"],
|
|
4253
|
-
engines: { openlearn: "^2.5
|
|
4311
|
+
engines: { openlearn: "^0.2.5" }
|
|
4254
4312
|
},
|
|
4255
4313
|
activate: async (ctx) => {
|
|
4256
4314
|
const commandBus = ctx.services.commandBus;
|
|
@@ -4416,7 +4474,7 @@ var ProcessPlugin = {
|
|
|
4416
4474
|
"@openlearn/core:IDatabase@^1.0.0"
|
|
4417
4475
|
],
|
|
4418
4476
|
capabilitiesProposed: ["process:read", "process:write"],
|
|
4419
|
-
engines: { openlearn: "^2.5
|
|
4477
|
+
engines: { openlearn: "^0.2.5" }
|
|
4420
4478
|
},
|
|
4421
4479
|
activate: async (ctx) => {
|
|
4422
4480
|
const commandBus = ctx.services.commandBus;
|
|
@@ -4547,7 +4605,7 @@ var ManagementPlugin = {
|
|
|
4547
4605
|
"@openlearn/core:IDatabase@^1.0.0"
|
|
4548
4606
|
],
|
|
4549
4607
|
capabilitiesProposed: ["class:read", "class:write", "student:read", "student:write"],
|
|
4550
|
-
engines: { openlearn: "^2.5
|
|
4608
|
+
engines: { openlearn: "^0.2.5" }
|
|
4551
4609
|
},
|
|
4552
4610
|
activate: async (ctx) => {
|
|
4553
4611
|
const commandBus = ctx.services.commandBus;
|
|
@@ -5446,7 +5504,7 @@ var AiSubmitInjectorPlugin = {
|
|
|
5446
5504
|
"@openlearn/core:IAIService@^1.0.0"
|
|
5447
5505
|
],
|
|
5448
5506
|
capabilitiesProposed: [],
|
|
5449
|
-
engines: { openlearn: "^2.5
|
|
5507
|
+
engines: { openlearn: "^0.2.5" }
|
|
5450
5508
|
},
|
|
5451
5509
|
activate: async (ctx) => {
|
|
5452
5510
|
const eventBus = ctx.services.eventBus;
|
|
@@ -5538,7 +5596,7 @@ var BuiltinPlugin = {
|
|
|
5538
5596
|
"@openlearn/core:IPluginHost@^1.0.0"
|
|
5539
5597
|
],
|
|
5540
5598
|
capabilitiesProposed: ["lesson:read", "lesson:write", "whiteboard:read", "whiteboard:write"],
|
|
5541
|
-
engines: { openlearn: "^2.5
|
|
5599
|
+
engines: { openlearn: "^0.2.5" }
|
|
5542
5600
|
},
|
|
5543
5601
|
activate: async (ctx) => {
|
|
5544
5602
|
const commandBus = ctx.services.commandBus;
|
|
@@ -6923,7 +6981,7 @@ var AiPlannerPlugin = {
|
|
|
6923
6981
|
"@openlearn/core:IEventBusService@^1.0.0"
|
|
6924
6982
|
],
|
|
6925
6983
|
capabilitiesProposed: ["process:write", "lesson:write", "assignment:write"],
|
|
6926
|
-
engines: { openlearn: "^2.5
|
|
6984
|
+
engines: { openlearn: "^0.2.5" }
|
|
6927
6985
|
},
|
|
6928
6986
|
activate: async (ctx) => {
|
|
6929
6987
|
const commandBus = ctx.services.commandBus;
|
|
@@ -7109,7 +7167,7 @@ var AssignmentEvalPlugin = {
|
|
|
7109
7167
|
"@openlearn/core:ISemesterGradeService@^1.0.0"
|
|
7110
7168
|
],
|
|
7111
7169
|
capabilitiesProposed: ["lesson:read", "lesson:write"],
|
|
7112
|
-
engines: { openlearn: "^2.5
|
|
7170
|
+
engines: { openlearn: "^0.2.5" }
|
|
7113
7171
|
},
|
|
7114
7172
|
activate: async (ctx) => {
|
|
7115
7173
|
const commandBus = ctx.services.commandBus;
|
|
@@ -7695,7 +7753,7 @@ var ContributionRegistry = class {
|
|
|
7695
7753
|
register(pluginId, contributes) {
|
|
7696
7754
|
this.unregister(pluginId);
|
|
7697
7755
|
for (const [slot, configs] of Object.entries(contributes)) {
|
|
7698
|
-
if (!configs || configs.length === 0) continue;
|
|
7756
|
+
if (!Array.isArray(configs) || configs.length === 0) continue;
|
|
7699
7757
|
if (!this.contributions.has(slot)) {
|
|
7700
7758
|
this.contributions.set(slot, /* @__PURE__ */ new Map());
|
|
7701
7759
|
}
|
|
@@ -8057,7 +8115,7 @@ var PluginRuntimeComposition = class {
|
|
|
8057
8115
|
this.workerManager = workerManager;
|
|
8058
8116
|
this.id = "srv_plugin_runtime_composition";
|
|
8059
8117
|
this.name = "PluginRuntimeComposition";
|
|
8060
|
-
this.version = "0.2.
|
|
8118
|
+
this.version = "0.2.5";
|
|
8061
8119
|
this._isStarted = false;
|
|
8062
8120
|
}
|
|
8063
8121
|
get isStarted() {
|
|
@@ -8111,7 +8169,7 @@ var PluginLifecycleManager = class {
|
|
|
8111
8169
|
this.pluginHost = pluginHost;
|
|
8112
8170
|
this.id = "srv_plugin_lifecycle_manager";
|
|
8113
8171
|
this.name = "PluginLifecycleManager";
|
|
8114
|
-
this.version = "0.2.
|
|
8172
|
+
this.version = "0.2.5";
|
|
8115
8173
|
}
|
|
8116
8174
|
getPluginState(pluginId) {
|
|
8117
8175
|
return this.pluginHost.getPluginState(pluginId);
|
|
@@ -8158,7 +8216,7 @@ var PluginCapabilityGateway = class {
|
|
|
8158
8216
|
this.capabilityRegistry = capabilityRegistry;
|
|
8159
8217
|
this.id = "srv_plugin_capability_gateway";
|
|
8160
8218
|
this.name = "PluginCapabilityGateway";
|
|
8161
|
-
this.version = "0.2.
|
|
8219
|
+
this.version = "0.2.5";
|
|
8162
8220
|
}
|
|
8163
8221
|
listCapabilities() {
|
|
8164
8222
|
const rawCaps = this.capabilityRegistry.listCapabilities();
|
|
@@ -8212,7 +8270,7 @@ var UnifiedExtensionRegistry = class {
|
|
|
8212
8270
|
constructor() {
|
|
8213
8271
|
this.id = "srv_unified_extension_registry";
|
|
8214
8272
|
this.name = "UnifiedExtensionRegistry";
|
|
8215
|
-
this.version = "0.2.
|
|
8273
|
+
this.version = "0.2.5";
|
|
8216
8274
|
this._extensions = /* @__PURE__ */ new Map();
|
|
8217
8275
|
}
|
|
8218
8276
|
registerExtension(category, id, impl, meta) {
|
|
@@ -8317,7 +8375,7 @@ var PluginDistributionManager = class {
|
|
|
8317
8375
|
this.pluginHost = pluginHost;
|
|
8318
8376
|
this.id = "srv_plugin_distribution_manager";
|
|
8319
8377
|
this.name = "PluginDistributionManager";
|
|
8320
|
-
this.version = "0.2.
|
|
8378
|
+
this.version = "0.2.5";
|
|
8321
8379
|
this._repositories = /* @__PURE__ */ new Map();
|
|
8322
8380
|
}
|
|
8323
8381
|
registerRepository(repo) {
|
|
@@ -8406,7 +8464,7 @@ function validatePluginStateTransition(currentState, nextState, pluginId) {
|
|
|
8406
8464
|
}
|
|
8407
8465
|
var ACTIVATION_TIMEOUT_MS = 5e3;
|
|
8408
8466
|
var DEACTIVATION_TIMEOUT_MS = 5e3;
|
|
8409
|
-
var OPENLEARN_VERSION = "0.2.
|
|
8467
|
+
var OPENLEARN_VERSION = "0.2.5";
|
|
8410
8468
|
var PluginHost = class {
|
|
8411
8469
|
constructor(serviceRegistry, esmLoader, db2, pluginsDir) {
|
|
8412
8470
|
this.serviceRegistry = serviceRegistry;
|
|
@@ -10431,7 +10489,7 @@ var HotReloadController = class {
|
|
|
10431
10489
|
}
|
|
10432
10490
|
if (this.enabled) return;
|
|
10433
10491
|
this.enabled = true;
|
|
10434
|
-
await this.fileWatcher.startWatch(this.watchDir, (event) => {
|
|
10492
|
+
await this.fileWatcher.startWatch(this.watchDir, async (event) => {
|
|
10435
10493
|
this.handleFileChange(event.pluginId, event.filePath);
|
|
10436
10494
|
});
|
|
10437
10495
|
console.log("[HotReload] Hot reload enabled \u2014 watching for plugin changes");
|
|
@@ -16020,7 +16078,7 @@ var StartupTimeoutError = class extends PlatformBootstrapError {
|
|
|
16020
16078
|
this.name = "StartupTimeoutError";
|
|
16021
16079
|
}
|
|
16022
16080
|
};
|
|
16023
|
-
var PLATFORM_VERSION = "0.2.
|
|
16081
|
+
var PLATFORM_VERSION = "0.2.5";
|
|
16024
16082
|
var BOOTSTRAP_STAGE_NAMES = Object.freeze([
|
|
16025
16083
|
"Created" /* Created */,
|
|
16026
16084
|
"Configuring" /* Configuring */,
|
|
@@ -16722,10 +16780,10 @@ var PluginCompositionModule = class {
|
|
|
16722
16780
|
const realPluginHost = options?.infrastructureRefs?.get("pluginHost") ?? { name: "PluginHostService", isReady: true, pluginsCount: 0 };
|
|
16723
16781
|
const realContributionRegistry = options?.infrastructureRefs?.get("contributionRegistry") ?? { name: "ContributionRegistry", slotsCount: 0 };
|
|
16724
16782
|
const realRuntimeComposition = options?.infrastructureRefs?.get("runtimeComposition") ?? { name: "PluginRuntimeComposition", isStarted: true };
|
|
16725
|
-
const realLifecycleManager = options?.infrastructureRefs?.get("lifecycleManager") ?? { name: "PluginLifecycleManager", version: "2.5
|
|
16726
|
-
const realCapabilityGateway = options?.infrastructureRefs?.get("capabilityGateway") ?? { name: "PluginCapabilityGateway", version: "2.5
|
|
16727
|
-
const realExtensionRegistry = options?.infrastructureRefs?.get("extensionRegistry") ?? { name: "UnifiedExtensionRegistry", version: "2.5
|
|
16728
|
-
const realDistributionManager = options?.infrastructureRefs?.get("distributionManager") ?? { name: "PluginDistributionManager", version: "2.5
|
|
16783
|
+
const realLifecycleManager = options?.infrastructureRefs?.get("lifecycleManager") ?? { name: "PluginLifecycleManager", version: "0.2.5" };
|
|
16784
|
+
const realCapabilityGateway = options?.infrastructureRefs?.get("capabilityGateway") ?? { name: "PluginCapabilityGateway", version: "0.2.5" };
|
|
16785
|
+
const realExtensionRegistry = options?.infrastructureRefs?.get("extensionRegistry") ?? { name: "UnifiedExtensionRegistry", version: "0.2.5" };
|
|
16786
|
+
const realDistributionManager = options?.infrastructureRefs?.get("distributionManager") ?? { name: "PluginDistributionManager", version: "0.2.5" };
|
|
16729
16787
|
registerOrReplace({
|
|
16730
16788
|
id: "srv_plugin_host",
|
|
16731
16789
|
lifetime: "Singleton",
|
|
@@ -16893,7 +16951,7 @@ var Kernel = class {
|
|
|
16893
16951
|
throw new Error(`[PayloadValidationError] Invalid command payload for ${command.type}: ${errors.join("; ")}`);
|
|
16894
16952
|
}
|
|
16895
16953
|
}
|
|
16896
|
-
const isAdmin = command.actorId === "role:administrator" || command.actorId === "admin" || command.actorId === "usr_admin" || command.actorId === "admin-demo" || command.actorId?.endsWith(":administrator") || command.actorId?.endsWith(":admin")
|
|
16954
|
+
const isAdmin = command.actorId === "role:administrator" || command.actorId === "admin" || command.actorId === "usr_admin" || command.actorId === "admin-demo" || command.actorId?.endsWith(":administrator") || command.actorId?.endsWith(":admin");
|
|
16897
16955
|
if (action.capabilityRequired && !isAdmin) {
|
|
16898
16956
|
const allowed = this.capabilityGuard.check(command.actorId, action.capabilityRequired);
|
|
16899
16957
|
if (!allowed) {
|
|
@@ -18367,10 +18425,10 @@ function getCookieToken(req) {
|
|
|
18367
18425
|
}
|
|
18368
18426
|
function getActorId(req) {
|
|
18369
18427
|
const token = getCookieToken(req);
|
|
18370
|
-
if (!token) return "
|
|
18428
|
+
if (!token) return "anonymous";
|
|
18371
18429
|
try {
|
|
18372
18430
|
const session = getValidSession(token);
|
|
18373
|
-
if (!session) return "
|
|
18431
|
+
if (!session) return "anonymous";
|
|
18374
18432
|
let role = session.subRole || session.role;
|
|
18375
18433
|
if (session.username === "admin" || session.userId === "usr_admin" || role === "admin" || role === "administrator") {
|
|
18376
18434
|
role = "administrator";
|
|
@@ -18378,9 +18436,9 @@ function getActorId(req) {
|
|
|
18378
18436
|
if (role) {
|
|
18379
18437
|
return `user:${session.userId || "demo"}:${role}`;
|
|
18380
18438
|
}
|
|
18381
|
-
return "
|
|
18439
|
+
return "anonymous";
|
|
18382
18440
|
} catch {
|
|
18383
|
-
return "
|
|
18441
|
+
return "anonymous";
|
|
18384
18442
|
}
|
|
18385
18443
|
}
|
|
18386
18444
|
function requireAuth(...roles) {
|
|
@@ -19163,7 +19221,7 @@ function registerOsRoutes(ctx) {
|
|
|
19163
19221
|
res.status(500).json({ error: e.message });
|
|
19164
19222
|
}
|
|
19165
19223
|
});
|
|
19166
|
-
app.post("/api/commands", async (req, res) => {
|
|
19224
|
+
app.post("/api/commands", requireAuth(), async (req, res) => {
|
|
19167
19225
|
try {
|
|
19168
19226
|
const { commandType, payload } = req.body;
|
|
19169
19227
|
const cmd = kernelContainer.commandBus.createCommand(
|
|
@@ -23078,7 +23136,7 @@ function registerGradingRoutes(ctx) {
|
|
|
23078
23136
|
if (!lessonId || !studentId || grade === void 0) {
|
|
23079
23137
|
return res.status(400).json({ success: false, error: "lessonId, studentId, and grade are required" });
|
|
23080
23138
|
}
|
|
23081
|
-
const gradeService = await kernelContainer.
|
|
23139
|
+
const gradeService = await kernelContainer.serviceRegistry.resolve(ISemesterGradeServiceToken);
|
|
23082
23140
|
await gradeService.saveSemesterGrade(lessonId, studentId, Math.round(Number(grade)));
|
|
23083
23141
|
res.json({ success: true });
|
|
23084
23142
|
} catch (e) {
|
|
@@ -23087,7 +23145,7 @@ function registerGradingRoutes(ctx) {
|
|
|
23087
23145
|
});
|
|
23088
23146
|
app.get("/api/classes/:classId/points-dimensions", async (req, res) => {
|
|
23089
23147
|
try {
|
|
23090
|
-
const dimensionRegistry = await kernelContainer.
|
|
23148
|
+
const dimensionRegistry = await kernelContainer.serviceRegistry.resolve(IPointsDimensionRegistryToken);
|
|
23091
23149
|
const dimensions = dimensionRegistry.listDimensions();
|
|
23092
23150
|
res.json({ success: true, dimensions });
|
|
23093
23151
|
} catch (e) {
|
|
@@ -23104,7 +23162,7 @@ function registerGradingRoutes(ctx) {
|
|
|
23104
23162
|
if (!classId || !dimensionId || deltaPoints === void 0 || !reason) {
|
|
23105
23163
|
return res.status(400).json({ success: false, error: "classId, dimensionId, deltaPoints, and reason are required" });
|
|
23106
23164
|
}
|
|
23107
|
-
const ledgerService = await kernelContainer.
|
|
23165
|
+
const ledgerService = await kernelContainer.serviceRegistry.resolve(IPointsLedgerServiceToken);
|
|
23108
23166
|
const logItem = await ledgerService.addPoints(studentId, classId, dimensionId, Number(deltaPoints), reason, pluginId);
|
|
23109
23167
|
kernelContainer.eventBus.publish({
|
|
23110
23168
|
id: logItem.id,
|
|
@@ -23122,7 +23180,7 @@ function registerGradingRoutes(ctx) {
|
|
|
23122
23180
|
try {
|
|
23123
23181
|
const { studentId } = req.params;
|
|
23124
23182
|
const { classId } = req.query;
|
|
23125
|
-
const ledgerService = await kernelContainer.
|
|
23183
|
+
const ledgerService = await kernelContainer.serviceRegistry.resolve(IPointsLedgerServiceToken);
|
|
23126
23184
|
const logs = await ledgerService.getLogs(studentId, classId);
|
|
23127
23185
|
const summary = await ledgerService.getStudentDimensionSummary(studentId, classId || "");
|
|
23128
23186
|
res.json({ success: true, logs, summary });
|
|
@@ -24013,6 +24071,10 @@ function registerPluginsRoutes(ctx) {
|
|
|
24013
24071
|
if (!cleanUrl.endsWith("/chat/completions")) {
|
|
24014
24072
|
cleanUrl = cleanUrl.endsWith("/") ? cleanUrl + "chat/completions" : cleanUrl + "/chat/completions";
|
|
24015
24073
|
}
|
|
24074
|
+
const urlSafety = isSafeExternalUrl(cleanUrl);
|
|
24075
|
+
if (!urlSafety.safe) {
|
|
24076
|
+
return res.status(400).json({ error: `Blocked unsafe AI provider URL: ${urlSafety.reason}` });
|
|
24077
|
+
}
|
|
24016
24078
|
const response = await fetch(cleanUrl, {
|
|
24017
24079
|
method: "POST",
|
|
24018
24080
|
headers: {
|