diff --git a/api/extensions.py b/api/extensions.py index 04bec03e..b4b12eda 100644 --- a/api/extensions.py +++ b/api/extensions.py @@ -1575,9 +1575,11 @@ def inject_extension_tags(index_html: str) -> str: else: result = block + result - if script_tags: + if runtime_config["extensions"] or script_tags: body_marker = "" - block = runtime_tag + "\n" + "\n".join(script_tags) + "\n" + block = runtime_tag + "\n" + if script_tags: + block += "\n".join(script_tags) + "\n" if body_marker in result: result = result.replace(body_marker, block + body_marker, 1) else: diff --git a/static/extension_settings.js b/static/extension_settings.js index 1b269d6e..c42af46c 100644 --- a/static/extension_settings.js +++ b/static/extension_settings.js @@ -125,6 +125,7 @@ id:entry.id, name:entry.name, storage_owned:entry.storage_owned, + settings_schema:entry.settings_schema, }); } trustedSeeded=true; @@ -137,7 +138,7 @@ id:entry.id, name:entry.name, storage_owned:trusted.storage_owned===true, - settings_schema:trusted.storage_owned===true?entry.settings_schema:[], + settings_schema:trusted.storage_owned===true?trusted.settings_schema:[], }); } } diff --git a/tests/test_extension_hooks.py b/tests/test_extension_hooks.py index e595c35c..3886f23c 100644 --- a/tests/test_extension_hooks.py +++ b/tests/test_extension_hooks.py @@ -160,6 +160,37 @@ def test_extension_settings_runtime_config_injects_before_extension_scripts(tmp_ assert injected.index("window.__HERMES_EXTENSION_CONFIG__") < injected.index("/extensions/settings-ok.js") +def test_extension_settings_only_manifest_still_injects_runtime_config(tmp_path, monkeypatch): + root = tmp_path / "extensions" + root.mkdir() + (root / "manifest.json").write_text( + """ + { + "extensions": [ + { + "id": "settings-only", + "permissions": {"storage": {"owned": true}}, + "settings_schema": [ + {"key": "flag", "type": "boolean", "default": true} + ] + } + ] + } + """, + encoding="utf-8", + ) + monkeypatch.setenv("HERMES_WEBUI_EXTENSION_DIR", str(root)) + monkeypatch.setenv("HERMES_WEBUI_EXTENSION_MANIFEST", "manifest.json") + + from api.extensions import inject_extension_tags + + injected = inject_extension_tags("") + + assert "window.__HERMES_EXTENSION_CONFIG__" in injected + assert '"id":"settings-only"' in injected + assert injected.index("window.__HERMES_EXTENSION_CONFIG__") < injected.index("") + + def test_extension_route_remains_behind_webui_auth(monkeypatch): monkeypatch.setenv("HERMES_WEBUI_PASSWORD", "test-password") diff --git a/tests/test_extension_settings_runtime.py b/tests/test_extension_settings_runtime.py index abe764e7..2502bd7c 100644 --- a/tests/test_extension_settings_runtime.py +++ b/tests/test_extension_settings_runtime.py @@ -100,6 +100,26 @@ def test_extension_settings_runtime_normalizes_persists_resets_and_clears(): const unknownSettings = window.HermesExtensionSettings.settingsForExtension('unknown.ext'); assert.strictEqual(unknownSettings.setAll({{flag: true}}).ok, false); assert.strictEqual(store.has('hermes.ext.settings.unknown.ext'), false); + + window.HermesExtensionSettings.primeFromStatus({{ + extensions: [{{ + id: 'demo.ext', + name: 'Demo', + storage_owned: true, + settings_schema: [{{key: 'evil', type: 'string', default: ''}}], + }}, {{ + id: 'denied.ext', + name: 'Denied', + storage_owned: false, + settings_schema: [{{key: 'flag', type: 'boolean', default: false}}], + }}] + }}); + + const reprobe = window.HermesExtensionSettings.settingsForExtension('demo.ext'); + assert.deepStrictEqual(reprobe.schema.map(field => field.key), ['flag', 'mode', 'count']); + assert.strictEqual(reprobe.set('evil', 'owned').ok, true); + assert.strictEqual(reprobe.get('evil'), undefined); + assert.strictEqual(store.has('hermes.ext.settings.demo.ext'), false); """ ) _run_node(script)