feat: admit signed education workbench module

This commit is contained in:
冰朔 2026-08-19 03:17:00 +08:00
commit df5e8f8e3c
24 changed files with 4986 additions and 20 deletions

View file

@ -268,6 +268,22 @@ dependencies = [
"system-deps",
]
[[package]]
name = "calamine"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138646b9af2c5d7f1804ea4bf93afc597737d2bd4f7341d67c48b03316976eb1"
dependencies = [
"byteorder",
"chrono",
"codepage",
"encoding_rs",
"log",
"quick-xml 0.31.0",
"serde",
"zip 2.4.2",
]
[[package]]
name = "camino"
version = "1.2.5"
@ -393,6 +409,15 @@ dependencies = [
"cc",
]
[[package]]
name = "codepage"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48f68d061bc2828ae826206326e61251aca94c1e4a5305cf52d9138639c918b4"
dependencies = [
"encoding_rs",
]
[[package]]
name = "combine"
version = "4.6.7"
@ -570,6 +595,27 @@ dependencies = [
"syn 2.0.115",
]
[[package]]
name = "csv"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
dependencies = [
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782"
dependencies = [
"memchr",
]
[[package]]
name = "ctor"
version = "0.8.0"
@ -890,6 +936,15 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
[[package]]
name = "encoding_rs"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@ -1495,7 +1550,10 @@ name = "hololake-native-desktop"
version = "0.5.0"
dependencies = [
"base64 0.22.1",
"calamine",
"csv",
"dirs",
"encoding_rs",
"fs2",
"futures-util",
"interprocess",
@ -1503,6 +1561,7 @@ dependencies = [
"reqwest",
"ring",
"rusqlite",
"rust_xlsxwriter",
"serde",
"serde_json",
"tauri",
@ -2834,7 +2893,7 @@ checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85"
dependencies = [
"base64 0.22.1",
"indexmap 2.14.0",
"quick-xml",
"quick-xml 0.41.0",
"serde",
"time",
]
@ -2981,6 +3040,16 @@ dependencies = [
"psl-types",
]
[[package]]
name = "quick-xml"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
dependencies = [
"encoding_rs",
"memchr",
]
[[package]]
name = "quick-xml"
version = "0.41.0"
@ -3351,6 +3420,17 @@ dependencies = [
"smallvec",
]
[[package]]
name = "rust_xlsxwriter"
version = "0.64.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5f47f5318c1e512e57c07781559367577b1eb9618325cf1574cd30d38b112c5"
dependencies = [
"lazy_static",
"regex",
"zip 0.6.6",
]
[[package]]
name = "rustc-hash"
version = "2.1.3"
@ -4311,7 +4391,7 @@ dependencies = [
"tokio",
"url",
"windows-sys 0.60.2",
"zip",
"zip 4.6.1",
]
[[package]]
@ -5847,6 +5927,35 @@ dependencies = [
"syn 2.0.115",
]
[[package]]
name = "zip"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [
"byteorder",
"crc32fast",
"crossbeam-utils",
"flate2",
]
[[package]]
name = "zip"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50"
dependencies = [
"arbitrary",
"crc32fast",
"crossbeam-utils",
"displaydoc",
"flate2",
"indexmap 2.14.0",
"memchr",
"thiserror 2.0.20",
"zopfli",
]
[[package]]
name = "zip"
version = "4.6.1"
@ -5864,3 +5973,15 @@ name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
[[package]]
name = "zopfli"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
dependencies = [
"bumpalo",
"crc32fast",
"log",
"simd-adler32",
]

View file

@ -21,6 +21,9 @@ interprocess = "2.4.2"
ring = "0.17"
rusqlite = { version = "0.31", features = ["bundled"] }
base64 = "0.22"
calamine = { version = "=0.26.1", features = ["dates"] }
csv = "=1.3.0"
encoding_rs = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri = { version = "=2.10.2", features = ["devtools"] }
@ -31,6 +34,7 @@ tauri-plugin-dialog = "=2.7.2"
uuid = { version = "1", features = ["v4"] }
url = "2"
reqwest = { version = "0.13.2", default-features = false, features = ["cookies", "form", "json", "rustls", "stream"] }
rust_xlsxwriter = "=0.64.2"
tokio = { version = "1", features = ["time"] }
futures-util = "0.3"
minisign-verify = "0.2.5"

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,8 @@ mod code_repo_login;
mod direct_local_broker;
mod direct_local_session;
mod dynamic_capability_routing;
mod education_translation;
mod education_workspace;
mod enterprise_work_channel;
mod glp_envelope;
mod gls_bootstrap_compiler;

View file

@ -45,6 +45,13 @@ const PERSONA_CHANNEL_BODY_PACKAGE: &[u8] = include_bytes!(
const PERSONA_CHANNEL_BODY_SIGNATURE: &str = include_str!(
"../../fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod.sig"
);
const EDUCATION_WORKBENCH_NUMBER: &str = "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001";
const EDUCATION_WORKBENCH_PACKAGE: &[u8] = include_bytes!(
"../../fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod"
);
const EDUCATION_WORKBENCH_SIGNATURE: &str = include_str!(
"../../fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod.sig"
);
struct BundledModuleSource {
module_number: &'static str,
@ -68,6 +75,11 @@ const BUNDLED_MODULES: &[BundledModuleSource] = &[
package: PERSONA_CHANNEL_BODY_PACKAGE,
signature: PERSONA_CHANNEL_BODY_SIGNATURE,
},
BundledModuleSource {
module_number: EDUCATION_WORKBENCH_NUMBER,
package: EDUCATION_WORKBENCH_PACKAGE,
signature: EDUCATION_WORKBENCH_SIGNATURE,
},
];
#[derive(Debug, Deserialize)]
@ -1513,7 +1525,7 @@ mod tests {
assert_eq!(package.manifest.adapter, "channel-workbench-v1");
assert_eq!(package.manifest.permissions.len(), 4);
assert!(is_sha256(&digest));
assert_eq!(BUNDLED_MODULES.len(), 3);
assert_eq!(BUNDLED_MODULES.len(), 4);
}
#[test]
@ -1534,6 +1546,25 @@ mod tests {
assert!(is_sha256(&digest));
}
#[test]
fn bundled_education_workbench_is_an_official_signed_module() {
let (_, package, digest) = read_verified_package_bytes(
EDUCATION_WORKBENCH_PACKAGE,
EDUCATION_WORKBENCH_SIGNATURE,
RELEASE_TRUST_RAW,
)
.unwrap();
assert_eq!(package.manifest.module_number, EDUCATION_WORKBENCH_NUMBER);
assert_eq!(package.manifest.registration_class, "OFFICIAL_LIGHTHOUSE");
assert_eq!(package.manifest.adapter, "education-workbench-v1");
assert_eq!(package.manifest.permissions.len(), 8);
assert_eq!(
package.payload["adapterConfig"]["importDefaultScope"],
"UNASSIGNED"
);
assert!(is_sha256(&digest));
}
#[cfg(unix)]
#[test]
fn symlinked_package_input_is_rejected_before_signature_processing() {

View file

@ -53,7 +53,7 @@ fn validate_tree() -> Result<(), String> {
|| tree.record_id != "HLP-UNIFIED-NUMBER-TREE-001"
|| tree.state != "MACHINE_COMPILED_STARTUP_ENFORCED"
|| tree.root_number != "HLP-NUMBER-WORLD-ROOT-001"
|| tree.route_count != 106
|| tree.route_count != 124
|| tree.routes.len() != tree.route_count
|| !tree.invariants.number_is_stable_coordinate_not_authority
|| !tree.invariants.path_is_unique_navigation

View file

@ -934,7 +934,7 @@ mod tests {
#[test]
fn registry_is_closed_and_contains_every_migrated_command() {
let registry = load_registry().unwrap();
assert_eq!(registry.operations.len(), 84);
assert_eq!(registry.operations.len(), 102);
assert!(!registry.runtime.legacy_direct_commands_allowed);
}

View file

@ -167,6 +167,67 @@ pub(crate) async fn dispatch(
"channel_growth::update_channel_growth_sharing" => {
json(crate::channel_growth::update_channel_growth_sharing(app, input(&payload)?).await?)
}
"education_workspace::get_education_workspace_snapshot" => {
json(crate::education_workspace::get_education_workspace_snapshot(app).await?)
}
"education_workspace::create_education_document" => json(
crate::education_workspace::create_education_document(app, input(&payload)?).await?,
),
"education_workspace::read_education_document" => {
json(crate::education_workspace::read_education_document(app, input(&payload)?).await?)
}
"education_workspace::save_education_document" => {
json(crate::education_workspace::save_education_document(app, input(&payload)?).await?)
}
"education_workspace::archive_education_document" => json(
crate::education_workspace::archive_education_document(app, input(&payload)?).await?,
),
"education_workspace::create_education_table" => {
json(crate::education_workspace::create_education_table(app, input(&payload)?).await?)
}
"education_workspace::read_education_table" => {
json(crate::education_workspace::read_education_table(app, input(&payload)?).await?)
}
"education_workspace::save_education_table" => {
json(crate::education_workspace::save_education_table(app, input(&payload)?).await?)
}
"education_workspace::archive_education_table" => {
json(crate::education_workspace::archive_education_table(app, input(&payload)?).await?)
}
"education_workspace::assign_imported_table_to_education" => json(
crate::education_workspace::assign_imported_table_to_education(app, input(&payload)?)
.await?,
),
"education_translation::import_education_tables_from_dialog" => {
json(crate::education_translation::import_education_tables_from_dialog(app).await?)
}
"education_translation::export_education_table_to_dialog" => json(
crate::education_translation::export_education_table_to_dialog(app, input(&payload)?)
.await?,
),
"education_translation::get_education_recognition_capability" => {
json(crate::education_translation::get_education_recognition_capability(app)?)
}
"education_workspace::create_education_automation_rule" => json(
crate::education_workspace::create_education_automation_rule(app, input(&payload)?)
.await?,
),
"education_workspace::save_education_automation_rule" => json(
crate::education_workspace::save_education_automation_rule(app, input(&payload)?)
.await?,
),
"education_workspace::archive_education_automation_rule" => json(
crate::education_workspace::archive_education_automation_rule(app, input(&payload)?)
.await?,
),
"education_workspace::preview_education_automation_rule" => json(
crate::education_workspace::preview_education_automation_rule(app, input(&payload)?)
.await?,
),
"education_workspace::execute_education_automation_rule" => json(
crate::education_workspace::execute_education_automation_rule(app, input(&payload)?)
.await?,
),
"local_development_bridge::acquire_development_write_lane" => json(
crate::local_development_bridge::acquire_development_write_lane(app, input(&payload)?)
.await?,