feat: El port — vessels populated alongside Rust
Adds src/main.el + manifest.el for each vessel in this workspace, ported from the Rust sources during the El consolidation pass on 2026-04-30. Each vessel now has both Rust (legacy) and El (target) sources side-by-side; Rust will be removed once the El paths are verified at runtime, vessel by vessel. Per-vessel work was split across multiple parallel agents reading the Rust to understand intent, then designing idiomatic El. Not 1:1 transliteration. Each ported vessel includes: - manifest.el per spec/language.md \u00a715.1 - src/main.el with the vessel's public surface - Compile verified via dist/platform/elc + cc against the el_runtime Known gaps surfaced during the port (held for follow-up): HMAC-SHA256 and base64 crypto, HTTP status code in handler returns, request headers in handler signatures, subprocess primitives, streaming responses, struct/enum types, browser/JS codegen target. Codegen bug list of 9 items tracked separately. The El sources here are runtime- ready under the canonical C runtime; the gaps are language/runtime extensions still in flight.
This commit is contained in:
BIN
Binary file not shown.
@@ -0,0 +1,311 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t pkey(el_val_t name, el_val_t field);
|
||||
el_val_t put_str(el_val_t name, el_val_t field, el_val_t value);
|
||||
el_val_t put_bool(el_val_t name, el_val_t field, el_val_t value);
|
||||
el_val_t get_str(el_val_t name, el_val_t field);
|
||||
el_val_t get_bool(el_val_t name, el_val_t field);
|
||||
el_val_t index_list(void);
|
||||
el_val_t index_set(el_val_t names);
|
||||
el_val_t index_contains(el_val_t name);
|
||||
el_val_t index_add(el_val_t name);
|
||||
el_val_t register_plugin(el_val_t name, el_val_t version, el_val_t description, el_val_t installed, el_val_t enabled, el_val_t hooks, el_val_t first_party);
|
||||
el_val_t plugin_init(void);
|
||||
el_val_t json_escape_str(el_val_t s);
|
||||
el_val_t quote_json(el_val_t s);
|
||||
el_val_t hooks_to_json(el_val_t hooks_csv);
|
||||
el_val_t plugin_to_json(el_val_t name);
|
||||
el_val_t plugin_list_json(void);
|
||||
el_val_t plugin_get_json(el_val_t name);
|
||||
el_val_t plugin_install(el_val_t name);
|
||||
el_val_t plugin_remove(el_val_t name);
|
||||
el_val_t plugin_enable(el_val_t name);
|
||||
el_val_t plugin_disable(el_val_t name);
|
||||
el_val_t host_main(void);
|
||||
|
||||
el_val_t pkey(el_val_t name, el_val_t field) {
|
||||
return el_str_concat(el_str_concat(el_str_concat(EL_STR("plugin/"), name), EL_STR("/")), field);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t put_str(el_val_t name, el_val_t field, el_val_t value) {
|
||||
state_set(pkey(name, field), value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t put_bool(el_val_t name, el_val_t field, el_val_t value) {
|
||||
if (value) {
|
||||
state_set(pkey(name, field), EL_STR("true"));
|
||||
}
|
||||
if (!value) {
|
||||
state_set(pkey(name, field), EL_STR("false"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t get_str(el_val_t name, el_val_t field) {
|
||||
return state_get(pkey(name, field));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t get_bool(el_val_t name, el_val_t field) {
|
||||
return str_eq(state_get(pkey(name, field)), EL_STR("true"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t index_list(void) {
|
||||
el_val_t raw = state_get(EL_STR("plugins/index"));
|
||||
if (str_eq(raw, EL_STR(""))) {
|
||||
return native_list_empty();
|
||||
}
|
||||
return str_split(raw, EL_STR(","));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t index_set(el_val_t names) {
|
||||
state_set(EL_STR("plugins/index"), list_join(names, EL_STR(",")));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t index_contains(el_val_t name) {
|
||||
el_val_t names = index_list();
|
||||
el_val_t n = native_list_len(names);
|
||||
el_val_t i = 0;
|
||||
while (i < n) {
|
||||
if (str_eq(native_list_get(names, i), name)) {
|
||||
return 1;
|
||||
}
|
||||
i = (i + 1);
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t index_add(el_val_t name) {
|
||||
if (!index_contains(name)) {
|
||||
el_val_t names = index_list();
|
||||
names = native_list_append(names, name);
|
||||
index_set(names);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t register_plugin(el_val_t name, el_val_t version, el_val_t description, el_val_t installed, el_val_t enabled, el_val_t hooks, el_val_t first_party) {
|
||||
put_str(name, EL_STR("name"), name);
|
||||
put_str(name, EL_STR("version"), version);
|
||||
put_str(name, EL_STR("description"), description);
|
||||
put_bool(name, EL_STR("installed"), installed);
|
||||
put_bool(name, EL_STR("enabled"), enabled);
|
||||
put_str(name, EL_STR("hooks"), hooks);
|
||||
put_bool(name, EL_STR("first_party"), first_party);
|
||||
index_add(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t plugin_init(void) {
|
||||
if (str_eq(state_get(EL_STR("plugins/initialised")), EL_STR("true"))) {
|
||||
return 0;
|
||||
}
|
||||
register_plugin(EL_STR("el-theme-dark"), EL_STR("1.0.0"), EL_STR("Dark theme — the default Engram IDE theme."), 1, 1, EL_STR(""), 1);
|
||||
register_plugin(EL_STR("el-theme-light"), EL_STR("1.0.0"), EL_STR("Light theme for high-ambient-light environments."), 0, 0, EL_STR(""), 1);
|
||||
register_plugin(EL_STR("el-fmt"), EL_STR("0.1.0"), EL_STR("Code formatter — auto-formats .el files on save."), 1, 1, EL_STR("on_save"), 1);
|
||||
register_plugin(EL_STR("el-doc"), EL_STR("0.1.0"), EL_STR("Documentation generator — produces HTML docs from type definitions."), 0, 0, EL_STR("custom_tool"), 1);
|
||||
register_plugin(EL_STR("el-test"), EL_STR("0.1.0"), EL_STR("Test runner with inline results displayed in the editor gutter."), 0, 0, EL_STR("on_build_complete,custom_panel"), 1);
|
||||
state_set(EL_STR("plugins/initialised"), EL_STR("true"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t json_escape_str(el_val_t s) {
|
||||
el_val_t chars = native_string_chars(s);
|
||||
el_val_t n = native_list_len(chars);
|
||||
el_val_t out = EL_STR("");
|
||||
el_val_t i = 0;
|
||||
while (i < n) {
|
||||
el_val_t ch = native_list_get(chars, i);
|
||||
if (str_eq(ch, EL_STR("\""))) {
|
||||
out = el_str_concat(out, EL_STR("\\\""));
|
||||
} else {
|
||||
if (str_eq(ch, EL_STR("\\"))) {
|
||||
out = el_str_concat(out, EL_STR("\\\\"));
|
||||
} else {
|
||||
if (str_eq(ch, EL_STR("\n"))) {
|
||||
out = el_str_concat(out, EL_STR("\\n"));
|
||||
} else {
|
||||
out = el_str_concat(out, ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
i = (i + 1);
|
||||
}
|
||||
return out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t quote_json(el_val_t s) {
|
||||
return el_str_concat(el_str_concat(EL_STR("\""), json_escape_str(s)), EL_STR("\""));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hooks_to_json(el_val_t hooks_csv) {
|
||||
if (str_eq(hooks_csv, EL_STR(""))) {
|
||||
return EL_STR("[]");
|
||||
}
|
||||
el_val_t xs = str_split(hooks_csv, EL_STR(","));
|
||||
el_val_t n = native_list_len(xs);
|
||||
el_val_t out = EL_STR("[");
|
||||
el_val_t i = 0;
|
||||
while (i < n) {
|
||||
if (i > 0) {
|
||||
out = el_str_concat(out, EL_STR(","));
|
||||
}
|
||||
out = el_str_concat(out, quote_json(native_list_get(xs, i)));
|
||||
i = (i + 1);
|
||||
}
|
||||
return el_str_concat(out, EL_STR("]"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t plugin_to_json(el_val_t name) {
|
||||
el_val_t parts = native_list_empty();
|
||||
parts = native_list_append(parts, el_str_concat(EL_STR("\"name\":"), quote_json(get_str(name, EL_STR("name")))));
|
||||
parts = native_list_append(parts, el_str_concat(EL_STR("\"version\":"), quote_json(get_str(name, EL_STR("version")))));
|
||||
parts = native_list_append(parts, el_str_concat(EL_STR("\"description\":"), quote_json(get_str(name, EL_STR("description")))));
|
||||
el_val_t installed = get_str(name, EL_STR("installed"));
|
||||
el_val_t enabled = get_str(name, EL_STR("enabled"));
|
||||
el_val_t first_party = get_str(name, EL_STR("first_party"));
|
||||
parts = native_list_append(parts, el_str_concat(EL_STR("\"installed\":"), installed));
|
||||
parts = native_list_append(parts, el_str_concat(EL_STR("\"enabled\":"), enabled));
|
||||
parts = native_list_append(parts, el_str_concat(EL_STR("\"hooks\":"), hooks_to_json(get_str(name, EL_STR("hooks")))));
|
||||
parts = native_list_append(parts, el_str_concat(EL_STR("\"first_party\":"), first_party));
|
||||
return el_str_concat(el_str_concat(EL_STR("{"), list_join(parts, EL_STR(","))), EL_STR("}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t plugin_list_json(void) {
|
||||
plugin_init();
|
||||
el_val_t names = index_list();
|
||||
el_val_t n = native_list_len(names);
|
||||
el_val_t out = EL_STR("[");
|
||||
el_val_t i = 0;
|
||||
while (i < n) {
|
||||
if (i > 0) {
|
||||
out = el_str_concat(out, EL_STR(","));
|
||||
}
|
||||
out = el_str_concat(out, plugin_to_json(native_list_get(names, i)));
|
||||
i = (i + 1);
|
||||
}
|
||||
return el_str_concat(out, EL_STR("]"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t plugin_get_json(el_val_t name) {
|
||||
plugin_init();
|
||||
if (!index_contains(name)) {
|
||||
return EL_STR("null");
|
||||
}
|
||||
return plugin_to_json(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t plugin_install(el_val_t name) {
|
||||
plugin_init();
|
||||
if (!index_contains(name)) {
|
||||
return EL_STR("{\"error\":\"plugin registry is not available (registry URL not configured)\"}");
|
||||
}
|
||||
if (get_bool(name, EL_STR("installed"))) {
|
||||
return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' is already installed\"}"));
|
||||
}
|
||||
put_bool(name, EL_STR("installed"), 1);
|
||||
put_bool(name, EL_STR("enabled"), 1);
|
||||
return plugin_to_json(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t plugin_remove(el_val_t name) {
|
||||
plugin_init();
|
||||
if (!index_contains(name)) {
|
||||
return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' not found\"}"));
|
||||
}
|
||||
if (str_eq(name, EL_STR("el-theme-dark"))) {
|
||||
return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' cannot be removed: it is a required built-in\"}"));
|
||||
}
|
||||
put_bool(name, EL_STR("installed"), 0);
|
||||
put_bool(name, EL_STR("enabled"), 0);
|
||||
return EL_STR("{\"ok\":true}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t plugin_enable(el_val_t name) {
|
||||
plugin_init();
|
||||
if (!index_contains(name)) {
|
||||
return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' not found\"}"));
|
||||
}
|
||||
put_bool(name, EL_STR("enabled"), 1);
|
||||
return EL_STR("{\"ok\":true}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t plugin_disable(el_val_t name) {
|
||||
plugin_init();
|
||||
if (!index_contains(name)) {
|
||||
return el_str_concat(el_str_concat(EL_STR("{\"error\":\"plugin '"), json_escape_str(name)), EL_STR("' not found\"}"));
|
||||
}
|
||||
put_bool(name, EL_STR("enabled"), 0);
|
||||
return EL_STR("{\"ok\":true}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t host_main(void) {
|
||||
el_val_t argv = args();
|
||||
el_val_t n = native_list_len(argv);
|
||||
if (n == 0) {
|
||||
println(plugin_list_json());
|
||||
return 0;
|
||||
}
|
||||
el_val_t cmd = native_list_get(argv, 0);
|
||||
if (str_eq(cmd, EL_STR("list"))) {
|
||||
println(plugin_list_json());
|
||||
return 0;
|
||||
}
|
||||
if (str_eq(cmd, EL_STR("get"))) {
|
||||
if (n >= 2) {
|
||||
println(plugin_get_json(native_list_get(argv, 1)));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (str_eq(cmd, EL_STR("install"))) {
|
||||
if (n >= 2) {
|
||||
println(plugin_install(native_list_get(argv, 1)));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (str_eq(cmd, EL_STR("remove"))) {
|
||||
if (n >= 2) {
|
||||
println(plugin_remove(native_list_get(argv, 1)));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (str_eq(cmd, EL_STR("enable"))) {
|
||||
if (n >= 2) {
|
||||
println(plugin_enable(native_list_get(argv, 1)));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (str_eq(cmd, EL_STR("disable"))) {
|
||||
if (n >= 2) {
|
||||
println(plugin_disable(native_list_get(argv, 1)));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
println(EL_STR("usage: el-plugin-host [list|get|install|remove|enable|disable] <name>"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
el_runtime_init_args(argc, argv);
|
||||
host_main();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
// main.el — el-plugin-host
|
||||
//
|
||||
// Plugin lifecycle manager for el-ide. Pure-El port of the el-plugin-host
|
||||
// Rust crate.
|
||||
//
|
||||
// Plugins are stored in process-global state via state_get / state_set.
|
||||
// Each plugin lives under a key prefix `plugin/<name>/<field>`. The
|
||||
// canonical list of plugin names is maintained at `plugins/index` as a
|
||||
// comma-separated string (we don't have list serialisation in state yet).
|
||||
//
|
||||
// Public API:
|
||||
// plugin_init() — seed first-party plugins (idempotent)
|
||||
// plugin_list_json() -> String — JSON array of all plugins
|
||||
// plugin_get_json(name) -> String — JSON object or "null"
|
||||
// plugin_install(name) -> String — JSON { ok | error }
|
||||
// plugin_remove(name) -> String
|
||||
// plugin_enable(name) -> String
|
||||
// plugin_disable(name) -> String
|
||||
|
||||
// ── State helpers (string K/V) ───────────────────────────────────────────────
|
||||
|
||||
fn pkey(name: String, field: String) -> String {
|
||||
"plugin/" + name + "/" + field
|
||||
}
|
||||
|
||||
fn put_str(name: String, field: String, value: String) -> Void {
|
||||
state_set(pkey(name, field), value)
|
||||
}
|
||||
|
||||
fn put_bool(name: String, field: String, value: Bool) -> Void {
|
||||
if value { state_set(pkey(name, field), "true") }
|
||||
if !value { state_set(pkey(name, field), "false") }
|
||||
}
|
||||
|
||||
fn get_str(name: String, field: String) -> String {
|
||||
state_get(pkey(name, field))
|
||||
}
|
||||
|
||||
fn get_bool(name: String, field: String) -> Bool {
|
||||
str_eq(state_get(pkey(name, field)), "true")
|
||||
}
|
||||
|
||||
// Plugin index: comma-separated list of names at "plugins/index".
|
||||
fn index_list() -> [String] {
|
||||
let raw: String = state_get("plugins/index")
|
||||
if str_eq(raw, "") { return native_list_empty() }
|
||||
str_split(raw, ",")
|
||||
}
|
||||
|
||||
fn index_set(names: [String]) -> Void {
|
||||
state_set("plugins/index", list_join(names, ","))
|
||||
}
|
||||
|
||||
fn index_contains(name: String) -> Bool {
|
||||
let names: [String] = index_list()
|
||||
let n: Int = native_list_len(names)
|
||||
let i: Int = 0
|
||||
while i < n {
|
||||
if str_eq(native_list_get(names, i), name) { return true }
|
||||
let i = i + 1
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn index_add(name: String) -> Void {
|
||||
if !index_contains(name) {
|
||||
let names: [String] = index_list()
|
||||
let names = native_list_append(names, name)
|
||||
index_set(names)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Initialisation ───────────────────────────────────────────────────────────
|
||||
|
||||
fn register_plugin(name: String, version: String, description: String,
|
||||
installed: Bool, enabled: Bool, hooks: String,
|
||||
first_party: Bool) -> Void {
|
||||
put_str(name, "name", name)
|
||||
put_str(name, "version", version)
|
||||
put_str(name, "description", description)
|
||||
put_bool(name, "installed", installed)
|
||||
put_bool(name, "enabled", enabled)
|
||||
put_str(name, "hooks", hooks)
|
||||
put_bool(name, "first_party", first_party)
|
||||
index_add(name)
|
||||
}
|
||||
|
||||
fn plugin_init() -> Void {
|
||||
if str_eq(state_get("plugins/initialised"), "true") {
|
||||
return
|
||||
}
|
||||
register_plugin("el-theme-dark", "1.0.0", "Dark theme — the default Engram IDE theme.", true, true, "", true)
|
||||
register_plugin("el-theme-light", "1.0.0", "Light theme for high-ambient-light environments.", false, false, "", true)
|
||||
register_plugin("el-fmt", "0.1.0", "Code formatter — auto-formats .el files on save.", true, true, "on_save", true)
|
||||
register_plugin("el-doc", "0.1.0", "Documentation generator — produces HTML docs from type definitions.", false, false, "custom_tool", true)
|
||||
register_plugin("el-test", "0.1.0", "Test runner with inline results displayed in the editor gutter.", false, false, "on_build_complete,custom_panel", true)
|
||||
state_set("plugins/initialised", "true")
|
||||
}
|
||||
|
||||
// ── Serialisation ────────────────────────────────────────────────────────────
|
||||
|
||||
fn json_escape_str(s: String) -> String {
|
||||
let chars: [String] = native_string_chars(s)
|
||||
let n: Int = native_list_len(chars)
|
||||
let out: String = ""
|
||||
let i: Int = 0
|
||||
while i < n {
|
||||
let ch: String = native_list_get(chars, i)
|
||||
if ch == "\"" { let out = out + "\\\"" }
|
||||
else { if ch == "\\" { let out = out + "\\\\" }
|
||||
else { if ch == "\n" { let out = out + "\\n" }
|
||||
else { let out = out + ch } } }
|
||||
let i = i + 1
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn quote_json(s: String) -> String {
|
||||
"\"" + json_escape_str(s) + "\""
|
||||
}
|
||||
|
||||
fn hooks_to_json(hooks_csv: String) -> String {
|
||||
if str_eq(hooks_csv, "") { return "[]" }
|
||||
let xs: [String] = str_split(hooks_csv, ",")
|
||||
let n: Int = native_list_len(xs)
|
||||
let out: String = "["
|
||||
let i: Int = 0
|
||||
while i < n {
|
||||
if i > 0 { let out = out + "," }
|
||||
let out = out + quote_json(native_list_get(xs, i))
|
||||
let i = i + 1
|
||||
}
|
||||
out + "]"
|
||||
}
|
||||
|
||||
fn plugin_to_json(name: String) -> String {
|
||||
let parts: [String] = native_list_empty()
|
||||
let parts = native_list_append(parts, "\"name\":" + quote_json(get_str(name, "name")))
|
||||
let parts = native_list_append(parts, "\"version\":" + quote_json(get_str(name, "version")))
|
||||
let parts = native_list_append(parts, "\"description\":" + quote_json(get_str(name, "description")))
|
||||
let installed: String = get_str(name, "installed")
|
||||
let enabled: String = get_str(name, "enabled")
|
||||
let first_party: String = get_str(name, "first_party")
|
||||
let parts = native_list_append(parts, "\"installed\":" + installed)
|
||||
let parts = native_list_append(parts, "\"enabled\":" + enabled)
|
||||
let parts = native_list_append(parts, "\"hooks\":" + hooks_to_json(get_str(name, "hooks")))
|
||||
let parts = native_list_append(parts, "\"first_party\":" + first_party)
|
||||
"{" + list_join(parts, ",") + "}"
|
||||
}
|
||||
|
||||
fn plugin_list_json() -> String {
|
||||
plugin_init()
|
||||
let names: [String] = index_list()
|
||||
let n: Int = native_list_len(names)
|
||||
let out: String = "["
|
||||
let i: Int = 0
|
||||
while i < n {
|
||||
if i > 0 { let out = out + "," }
|
||||
let out = out + plugin_to_json(native_list_get(names, i))
|
||||
let i = i + 1
|
||||
}
|
||||
out + "]"
|
||||
}
|
||||
|
||||
fn plugin_get_json(name: String) -> String {
|
||||
plugin_init()
|
||||
if !index_contains(name) { return "null" }
|
||||
plugin_to_json(name)
|
||||
}
|
||||
|
||||
// ── Mutations ────────────────────────────────────────────────────────────────
|
||||
|
||||
fn plugin_install(name: String) -> String {
|
||||
plugin_init()
|
||||
if !index_contains(name) {
|
||||
return "{\"error\":\"plugin registry is not available (registry URL not configured)\"}"
|
||||
}
|
||||
if get_bool(name, "installed") {
|
||||
return "{\"error\":\"plugin '" + json_escape_str(name) + "' is already installed\"}"
|
||||
}
|
||||
put_bool(name, "installed", true)
|
||||
put_bool(name, "enabled", true)
|
||||
plugin_to_json(name)
|
||||
}
|
||||
|
||||
fn plugin_remove(name: String) -> String {
|
||||
plugin_init()
|
||||
if !index_contains(name) {
|
||||
return "{\"error\":\"plugin '" + json_escape_str(name) + "' not found\"}"
|
||||
}
|
||||
if str_eq(name, "el-theme-dark") {
|
||||
return "{\"error\":\"plugin '" + json_escape_str(name) + "' cannot be removed: it is a required built-in\"}"
|
||||
}
|
||||
put_bool(name, "installed", false)
|
||||
put_bool(name, "enabled", false)
|
||||
"{\"ok\":true}"
|
||||
}
|
||||
|
||||
fn plugin_enable(name: String) -> String {
|
||||
plugin_init()
|
||||
if !index_contains(name) {
|
||||
return "{\"error\":\"plugin '" + json_escape_str(name) + "' not found\"}"
|
||||
}
|
||||
put_bool(name, "enabled", true)
|
||||
"{\"ok\":true}"
|
||||
}
|
||||
|
||||
fn plugin_disable(name: String) -> String {
|
||||
plugin_init()
|
||||
if !index_contains(name) {
|
||||
return "{\"error\":\"plugin '" + json_escape_str(name) + "' not found\"}"
|
||||
}
|
||||
put_bool(name, "enabled", false)
|
||||
"{\"ok\":true}"
|
||||
}
|
||||
|
||||
// ── Standalone CLI ───────────────────────────────────────────────────────────
|
||||
|
||||
fn host_main() -> Void {
|
||||
let argv: [String] = args()
|
||||
let n: Int = native_list_len(argv)
|
||||
if n == 0 {
|
||||
println(plugin_list_json())
|
||||
return
|
||||
}
|
||||
let cmd: String = native_list_get(argv, 0)
|
||||
if str_eq(cmd, "list") { println(plugin_list_json()) return }
|
||||
if str_eq(cmd, "get") {
|
||||
if n >= 2 { println(plugin_get_json(native_list_get(argv, 1))) }
|
||||
return
|
||||
}
|
||||
if str_eq(cmd, "install") {
|
||||
if n >= 2 { println(plugin_install(native_list_get(argv, 1))) }
|
||||
return
|
||||
}
|
||||
if str_eq(cmd, "remove") {
|
||||
if n >= 2 { println(plugin_remove(native_list_get(argv, 1))) }
|
||||
return
|
||||
}
|
||||
if str_eq(cmd, "enable") {
|
||||
if n >= 2 { println(plugin_enable(native_list_get(argv, 1))) }
|
||||
return
|
||||
}
|
||||
if str_eq(cmd, "disable") {
|
||||
if n >= 2 { println(plugin_disable(native_list_get(argv, 1))) }
|
||||
return
|
||||
}
|
||||
println("usage: el-plugin-host [list|get|install|remove|enable|disable] <name>")
|
||||
}
|
||||
|
||||
host_main()
|
||||
Reference in New Issue
Block a user