From 49868a18e1068214adac512351642ec43be15ac6 Mon Sep 17 00:00:00 2001 From: Mahmoud-Emad Date: Mon, 17 Nov 2025 15:04:49 +0200 Subject: [PATCH] Refactor the herolib repo: - Removed the unused files - Updated the README - Added all needed scripts in /scripts dir - Update script paths in CI configuration - Update script paths in Go code - Move installation scripts to scripts directory - Change script path from ./install_v.sh to ./scripts/install_v.sh --- .github/workflows/documentation.yml | 2 +- .github/workflows/test.yml | 2 +- .goosehints | 5 - .zed/keymap.json | 6 - .zed/tasks.json | 47 - CONTRIBUTING.md | 2 +- README.md | 61 +- aiprompts/.openhands/setup.sh | 2 +- docker/herolib/build.sh | 4 +- herolib.code-workspace | 31 - lib/builder/bootstrapper.v | 4 +- lib/installers/lang/herolib/herolib.v | 2 +- libarchive/baobab/README.md | 51 - libarchive/baobab/actor/client.v | 68 -- libarchive/baobab/generator/README.md | 63 -- .../generator/_archive/client_typescript.v | 118 --- .../_archive/client_typescript_test.v | 205 ---- .../generator/_archive/generate_objects.v | 47 - .../generator/_archive/write_object_methods.v | 406 ------- .../generator/_archive/write_object_tests.v | 168 --- libarchive/baobab/generator/generate_act.v | 179 ---- .../baobab/generator/generate_actor_folder.v | 82 -- .../baobab/generator/generate_actor_source.v | 118 --- .../baobab/generator/generate_actor_test.v | 276 ----- .../baobab/generator/generate_clients.v | 136 --- .../baobab/generator/generate_command.v | 78 -- .../baobab/generator/generate_interface.v | 48 - .../baobab/generator/generate_methods.v | 165 --- .../generator/generate_methods_example.v | 106 -- .../generator/generate_methods_interface.v | 49 - libarchive/baobab/generator/generate_model.v | 32 - .../baobab/generator/generate_openapi.v | 160 --- .../baobab/generator/generate_openrpc.v | 110 -- .../baobab/generator/generate_openrpc_test.v | 38 - .../baobab/generator/generate_scripts.v | 75 -- .../generator/templates/actor.v.template | 41 - .../templates/actor_example.v.template | 37 - .../generator/templates/actor_test.v.template | 10 - .../baobab/generator/templates/cli.v.template | 63 -- .../baobab/generator/templates/client_test.v | 74 -- .../generator/templates/command.v.template | 81 -- .../generator/templates/docs.vsh.template | 51 - .../templates/interface_http.v.template | 41 - .../templates/interface_http_test.v.template | 7 - .../templates/interface_openapi.v.template | 22 - .../interface_openapi_test.v.template | 9 - .../templates/interface_openrpc.v.template | 19 - .../interface_openrpc_test.v.template | 9 - .../generator/templates/playground.v.template | 16 - .../generator/templates/run.sh.template | 42 - .../templates/run_actor.vsh.template | 10 - .../templates/run_actor_example.vsh.template | 10 - .../templates/run_http_server.vsh.template | 7 - .../templates/specifications.v.template | 14 - .../baobab/generator/testdata/.gitignore | 1 - .../generator/write_object_methods_test.v | 139 --- libarchive/baobab/osis/README.md | 33 - libarchive/baobab/osis/factory.v | 8 - libarchive/baobab/osis/indexer.v | 116 -- libarchive/baobab/osis/model.v | 16 - libarchive/baobab/osis/osis.v | 58 - libarchive/baobab/osis/root_object.v | 135 --- libarchive/baobab/osis/storer.v | 15 - libarchive/baobab/osis/storer_generic.v | 23 - libarchive/baobab/specification/README.md | 0 .../baobab/specification/from_openapi.v | 180 ---- .../baobab/specification/from_openapi_test.v | 400 ------- .../baobab/specification/from_openrpc.v | 106 -- .../baobab/specification/from_openrpc_test.v | 434 -------- libarchive/baobab/specification/model.v | 205 ---- libarchive/baobab/specification/to_openapi.v | 97 -- .../baobab/specification/to_openapi_test.v | 183 ---- libarchive/baobab/specification/to_openrpc.v | 71 -- libarchive/baobab/stage/README.md | 140 --- libarchive/baobab/stage/action.v | 15 - libarchive/baobab/stage/action_client.v | 47 - libarchive/baobab/stage/actor.v | 79 -- libarchive/baobab/stage/config.v | 1 - libarchive/baobab/stage/error.v | 33 - libarchive/baobab/stage/interfaces/jsonrpc.v | 16 - libarchive/baobab/stage/interfaces/openapi.v | 48 - libarchive/baobab/stage/interfaces/openrpc.v | 29 - .../stage/interfaces/reflection_openapi.v | 91 -- .../baobab/stage/interfaces/server_http.v | 43 - libarchive/buildah/buildah_core.v | 164 --- libarchive/buildah/buildah_core_installers.v | 31 - libarchive/buildah/buildah_exec.v | 86 -- libarchive/buildah/buildah_factory.v | 110 -- libarchive/buildah/buildah_hero.v | 38 - libarchive/buildah/buildah_info.v | 113 -- libarchive/buildah/buildah_solutions.v | 175 --- libarchive/buildah/readme.md | 114 -- libarchive/daguserver/.heroscript | 13 - libarchive/daguserver/daguserver_actions.v | 157 --- libarchive/daguserver/daguserver_factory_.v | 299 ------ libarchive/daguserver/daguserver_model.v | 79 -- libarchive/daguserver/readme.md | 31 - .../daguserver/templates/communication.yaml | 29 - libarchive/daguserver/templates/dagu.yaml | 27 - libarchive/dedupestor/dedupe_ourdb/README.md | 107 -- .../dedupestor/dedupe_ourdb/dedupestor.v | 130 --- .../dedupestor/dedupe_ourdb/dedupestor_test.v | 188 ---- libarchive/dedupestor/dedupestor.v | 10 - libarchive/dedupestor/metadata.v | 109 -- libarchive/dedupestor/metadata_test.v | 121 --- libarchive/dify/.heroscript | 11 - libarchive/dify/dify_actions.v | 162 --- libarchive/dify/dify_factory_.v | 298 ------ libarchive/dify/dify_model.v | 68 -- libarchive/dify/readme.md | 42 - libarchive/dify/templates/atemplate.yaml | 5 - libarchive/doctree/README.md | 95 -- libarchive/doctree/collection/collection.v | 48 - libarchive/doctree/collection/data/error.v | 29 - libarchive/doctree/collection/data/file.v | 102 -- libarchive/doctree/collection/data/page.v | 171 --- .../doctree/collection/data/process_aliases.v | 49 - .../collection/data/process_aliases_test.v | 40 - .../collection/data/process_def_pointers.v | 34 - .../data/process_def_pointers_test.v | 23 - .../doctree/collection/data/process_link.v | 59 -- .../collection/data/process_link_test.v | 20 - .../doctree/collection/data/process_macros.v | 24 - libarchive/doctree/collection/error.v | 68 -- libarchive/doctree/collection/export.v | 149 --- libarchive/doctree/collection/export_test.v | 56 - libarchive/doctree/collection/getters.v | 45 - libarchive/doctree/collection/scan.v | 250 ----- libarchive/doctree/collection/scan_test.v | 121 --- .../doctree/collection/template/errors.md | 11 - .../doctree/collection/testdata/.gitignore | 1 - .../export_expected/src/col1/.collection | 1 - .../export_expected/src/col1/.linkedpages | 1 - .../export_expected/src/col1/errors.md | 9 - .../export_expected/src/col1/file1.md | 1 - .../export_expected/src/col1/file2.md | 1 - .../export_expected/src/col1/img/image.png | 0 .../export_test/mytree/dir1/.collection | 1 - .../export_test/mytree/dir1/dir2/file1.md | 1 - .../testdata/export_test/mytree/dir1/file2.md | 1 - .../export_test/mytree/dir1/image.png | 0 libarchive/doctree/error.v | 45 - libarchive/doctree/export.v | 100 -- libarchive/doctree/export_test.v | 91 -- libarchive/doctree/getters.v | 72 -- libarchive/doctree/getters_test.v | 35 - libarchive/doctree/list.v | 65 -- libarchive/doctree/play.v | 63 -- libarchive/doctree/pointer/pointer.v | 106 -- libarchive/doctree/pointer/pointer_test.v | 139 --- libarchive/doctree/process_defs.v | 83 -- libarchive/doctree/process_defs_test.v | 26 - libarchive/doctree/process_includes.v | 154 --- libarchive/doctree/process_includes_test.v | 56 - libarchive/doctree/process_macros.v | 54 - libarchive/doctree/scan.v | 236 ----- libarchive/doctree/testdata/.gitignore | 1 - .../doctree/testdata/actions/.collection | 1 - .../doctree/testdata/actions/actions1.md | 7 - .../actions/functionality/actions2.md | 15 - .../export_expected/col1/.collection | 1 - .../export_expected/col1/.linkedpages | 1 - .../export_expected/col1/errors.md | 9 - .../export_test/export_expected/col1/file1.md | 1 - .../export_test/export_expected/col1/file2.md | 1 - .../export_expected/col1/img/image.png | 0 .../export_expected/col2/.collection | 1 - .../export_expected/col2/.linkedpages | 0 .../export_test/export_expected/col2/file3.md | 0 .../export_test/mytree/dir1/.collection | 1 - .../export_test/mytree/dir1/dir2/file1.md | 1 - .../testdata/export_test/mytree/dir1/file2.md | 1 - .../export_test/mytree/dir1/image.png | 0 .../export_test/mytree/dir3/.collection | 1 - .../testdata/export_test/mytree/dir3/file3.md | 0 .../testdata/process_defs_test/col1/page1.md | 1 - .../testdata/process_defs_test/col2/page2.md | 3 - .../process_includes_test/col1/page1.md | 1 - .../process_includes_test/col2/page2.md | 1 - .../process_includes_test/col2/page3.md | 1 - libarchive/doctree/testdata/rpc/.collection | 1 - libarchive/doctree/testdata/rpc/eth.md | 130 --- libarchive/doctree/testdata/rpc/rpc.md | 12 - libarchive/doctree/testdata/rpc/stellar.md | 342 ------ libarchive/doctree/testdata/rpc/tfchain.md | 251 ----- libarchive/doctree/testdata/rpc/tfgrid.md | 651 ------------ .../testdata/tree_test/fruits/.collection | 1 - .../testdata/tree_test/fruits/apple.md | 9 - .../testdata/tree_test/fruits/banana.txt | 0 .../fruits/berries/img/digital_twin.png | Bin 125580 -> 0 bytes .../tree_test/fruits/berries/strawberry.md | 5 - .../testdata/tree_test/fruits/intro.md | 3 - .../testdata/tree_test/vegetables/.collection | 1 - .../testdata/tree_test/vegetables/cabbage.txt | 0 .../vegetables/cruciferous/broccoli.md | 3 - .../testdata/tree_test/vegetables/intro.md | 3 - .../testdata/tree_test/vegetables/tomato.md | 3 - libarchive/doctree/tree.v | 87 -- libarchive/doctree/tree_test.v | 79 -- libarchive/doctreeclient/README.md | 186 ---- libarchive/doctreeclient/client.v | 347 ------ libarchive/doctreeclient/doctree_test.v | 135 --- libarchive/doctreeclient/extract_links.v | 53 - libarchive/doctreeclient/extract_links_test.v | 131 --- libarchive/doctreeclient/factory.v | 12 - libarchive/doctreeclient/model.v | 9 - libarchive/encoderherocomplex/decoder.v | 124 --- libarchive/encoderherocomplex/decoder_test.v | 146 --- libarchive/encoderherocomplex/encoder.v | 168 --- .../encoder_ignorepropery_test.v | 42 - libarchive/encoderherocomplex/encoder_test.v | 121 --- .../postgres_client_decoder_test.v | 233 ---- libarchive/encoderherocomplex/readme.md | 138 --- .../encoderherocomplex/roundtrip_test.v | 83 -- libarchive/encoderherocomplex/tools.v | 26 - libarchive/encoderherocomplex/types.v | 91 -- .../examples/baobab/generator/.gitignore | 1 - .../baobab/generator/basic/.gitignore | 3 - .../examples/baobab/generator/basic/README.md | 9 - .../generator/basic/generate_actor_module.vsh | 22 - .../generator/basic/generate_methods.vsh | 19 - .../generator/basic/generate_openrpc_file.vsh | 19 - .../baobab/generator/basic/openrpc.json | 132 --- .../baobab/generator/geomind_poc/.gitignore | 3 - .../baobab/generator/geomind_poc/farmer.json | 344 ------ .../baobab/generator/geomind_poc/generate.vsh | 23 - .../generator/geomind_poc/merchant.json | 997 ------------------ .../baobab/generator/geomind_poc/model.v | 81 -- .../baobab/generator/geomind_poc/play.v | 148 --- .../generator/geomind_poc/profiler.json | 286 ----- .../baobab/generator/geomind_poc/server.v | 191 ---- .../baobab/generator/geomind_poc/specs.md | 57 - .../generator/geomind_poc/test_commerce.vsh | 47 - .../examples/baobab/generator/mcc_example.vsh | 25 - .../baobab/generator/openapi_e2e/.gitignore | 4 - .../openapi_e2e/generate_actor_module.vsh | 31 - .../baobab/generator/openapi_e2e/openapi.json | 311 ------ .../examples/baobab/specification/README.md | 3 - .../baobab/specification/openapi.json | 346 ------ .../openapi_to_specification.vsh | 13 - .../baobab/specification/openrpc.json | 857 --------------- .../baobab/specification/openrpc0.json | 132 --- .../openrpc_to_specification.vsh | 13 - .../specification_to_openapi.vsh | 107 -- .../specification_to_openrpc.vsh | 109 -- libarchive/github_actions_security.yml | 17 - libarchive/hero_build.yml | 92 -- libarchive/installers/web/caddy2/.heroscript | 11 - .../installers/web/caddy2/caddy_actions.v | 186 ---- .../installers/web/caddy2/caddy_factory_.v | 274 ----- .../installers/web/caddy2/caddy_model.v | 48 - libarchive/installers/web/caddy2/installer.v | 228 ---- libarchive/installers/web/caddy2/installers.v | 73 -- libarchive/installers/web/caddy2/play.v | 50 - libarchive/installers/web/caddy2/plugins.v | 31 - libarchive/installers/web/caddy2/readme.md | 31 - .../web/caddy2/templates/caddyfile_default | 5 - .../web/caddy2/templates/caddyfile_domain | 13 - libarchive/mcp_baobab/README.md | 3 - libarchive/mcp_baobab/baobab_tools.v | 172 --- libarchive/mcp_baobab/baobab_tools_test.v | 101 -- libarchive/mcp_baobab/command.v | 22 - libarchive/mcp_baobab/mcp_test.v | 128 --- libarchive/mcp_baobab/server.v | 38 - libarchive/mcp_old/README.md | 113 -- libarchive/mcp_old/backend_interface.v | 29 - libarchive/mcp_old/backend_memory.v | 151 --- libarchive/mcp_old/baobab/README.md | 3 - libarchive/mcp_old/baobab/baobab_tools.v | 172 --- libarchive/mcp_old/baobab/baobab_tools_test.v | 101 -- libarchive/mcp_old/baobab/command.v | 22 - libarchive/mcp_old/baobab/mcp_test.v | 128 --- libarchive/mcp_old/baobab/server.v | 38 - libarchive/mcp_old/cmd/compile.vsh | 68 -- libarchive/mcp_old/cmd/mcp.v | 91 -- libarchive/mcp_old/factory.v | 152 --- libarchive/mcp_old/generics.v | 52 - libarchive/mcp_old/handler_initialize.v | 27 - libarchive/mcp_old/handler_initialize_test.v | 103 -- libarchive/mcp_old/handler_logging.v | 37 - libarchive/mcp_old/handler_prompts.v | 135 --- libarchive/mcp_old/handler_resources.v | 182 ---- libarchive/mcp_old/handler_tools.v | 151 --- libarchive/mcp_old/mcpgen/README.md | 92 -- libarchive/mcp_old/mcpgen/command.v | 22 - libarchive/mcp_old/mcpgen/mcpgen.v | 281 ----- libarchive/mcp_old/mcpgen/mcpgen_helpers.v | 54 - libarchive/mcp_old/mcpgen/mcpgen_tools.v | 144 --- .../create_mcp_tools_code_tool_input.json | 21 - libarchive/mcp_old/mcpgen/server.v | 35 - .../mcpgen/templates/tool_code.v.template | 6 - .../mcpgen/templates/tool_handler.v.template | 11 - .../mcpgen/templates/tools_file.v.template | 1 - libarchive/mcp_old/model_configuration.v | 93 -- libarchive/mcp_old/model_configuration_test.v | 91 -- libarchive/mcp_old/model_error.v | 35 - libarchive/mcp_old/pugconvert/cmd/.gitignore | 2 - libarchive/mcp_old/pugconvert/cmd/compile.sh | 16 - libarchive/mcp_old/pugconvert/cmd/main.v | 17 - .../mcp_old/pugconvert/logic/convertpug.v | 203 ---- .../mcp_old/pugconvert/logic/jetvalidation.v | 85 -- libarchive/mcp_old/pugconvert/logic/loader.v | 25 - .../logic/templates/jet_instructions.md | 446 -------- libarchive/mcp_old/pugconvert/mcp/handlers.v | 54 - libarchive/mcp_old/pugconvert/mcp/mcp.v | 27 - .../mcp_old/pugconvert/mcp/specifications.v | 21 - libarchive/mcp_old/rhai/cmd/.gitignore | 2 - libarchive/mcp_old/rhai/cmd/compile.sh | 16 - libarchive/mcp_old/rhai/cmd/main.v | 18 - .../mcp_old/rhai/example/example copy.vsh | 532 ---------- libarchive/mcp_old/rhai/example/example.vsh | 661 ------------ libarchive/mcp_old/rhai/logic/logic.v | 268 ----- .../mcp_old/rhai/logic/prompts/engine.md | 125 --- .../mcp_old/rhai/logic/prompts/errors.md | 186 ---- .../rhai/logic/prompts/example_script.md | 40 - libarchive/mcp_old/rhai/logic/prompts/main.md | 101 -- .../mcp_old/rhai/logic/prompts/wrapper.md | 473 --------- .../mcp_old/rhai/logic/templates/cargo.toml | 10 - .../mcp_old/rhai/logic/templates/engine.rs | 12 - .../mcp_old/rhai/logic/templates/example.rs | 40 - .../mcp_old/rhai/logic/templates/functions.rs | 510 --------- .../rhai/logic/templates/generic_wrapper.rs | 132 --- .../mcp_old/rhai/logic/templates/lib.rs | 11 - libarchive/mcp_old/rhai/mcp/command.v | 22 - libarchive/mcp_old/rhai/mcp/mcp.v | 33 - libarchive/mcp_old/rhai/mcp/prompts.v | 43 - libarchive/mcp_old/rhai/mcp/specifications.v | 21 - libarchive/mcp_old/rhai/mcp/tools.v | 38 - libarchive/mcp_old/rhai/rhai.v | 1 - libarchive/mcp_old/server.v | 27 - libarchive/mcp_old/transport/http.v | 296 ------ libarchive/mcp_old/transport/interface.v | 54 - libarchive/mcp_old/transport/stdio.v | 73 -- libarchive/mcp_old/vcode/README.md | 92 -- libarchive/mcp_old/vcode/cmd/.gitignore | 2 - libarchive/mcp_old/vcode/cmd/compile.sh | 16 - libarchive/mcp_old/vcode/cmd/main.v | 17 - libarchive/mcp_old/vcode/logic/server.v | 33 - .../mcp_old/vcode/logic/test_client.vsh | 105 -- libarchive/mcp_old/vcode/logic/vlang.v | 284 ----- libarchive/mcp_old/vcode/logic/vlang_test.v | 100 -- libarchive/mcp_old/vcode/logic/vlang_tools.v | 39 - .../mcp_old/vcode/logic/write_vfile_tool.v | 67 -- libarchive/mcp_old/vcode/mcp/handlers.v | 54 - libarchive/mcp_old/vcode/mcp/mcp.v | 27 - libarchive/mcp_old/vcode/mcp/specifications.v | 21 - libarchive/mcp_pugconvert/cmd/.gitignore | 2 - libarchive/mcp_pugconvert/cmd/compile.sh | 16 - libarchive/mcp_pugconvert/cmd/main.v | 17 - libarchive/mcp_pugconvert/logic/convertpug.v | 203 ---- .../mcp_pugconvert/logic/jetvalidation.v | 85 -- libarchive/mcp_pugconvert/logic/loader.v | 25 - .../logic/templates/jet_instructions.md | 446 -------- libarchive/mcp_pugconvert/mcp/handlers.v | 54 - libarchive/mcp_pugconvert/mcp/mcp.v | 27 - .../mcp_pugconvert/mcp/specifications.v | 21 - libarchive/mcp_rhai/cmd/.gitignore | 2 - libarchive/mcp_rhai/cmd/compile.sh | 16 - libarchive/mcp_rhai/cmd/main.v | 17 - libarchive/mcp_rhai/example/example copy.vsh | 532 ---------- libarchive/mcp_rhai/example/example.vsh | 596 ----------- libarchive/mcp_rhai/logic/logic.v | 284 ----- libarchive/mcp_rhai/logic/logic_sampling.v | 258 ----- libarchive/mcp_rhai/logic/prompts/engine.md | 125 --- libarchive/mcp_rhai/logic/prompts/errors.md | 186 ---- .../mcp_rhai/logic/prompts/example_script.md | 40 - libarchive/mcp_rhai/logic/prompts/main.md | 99 -- libarchive/mcp_rhai/logic/prompts/wrapper.md | 473 --------- .../mcp_rhai/logic/templates/cargo.toml | 10 - libarchive/mcp_rhai/logic/templates/engine.rs | 12 - .../mcp_rhai/logic/templates/example.rs | 40 - .../mcp_rhai/logic/templates/functions.rs | 510 --------- .../logic/templates/generic_wrapper.rs | 132 --- libarchive/mcp_rhai/logic/templates/lib.rs | 11 - libarchive/mcp_rhai/mcp/command.v | 22 - libarchive/mcp_rhai/mcp/mcp.v | 33 - libarchive/mcp_rhai/mcp/prompts.v | 45 - libarchive/mcp_rhai/mcp/specifications.v | 21 - libarchive/mcp_rhai/mcp/tools.v | 38 - libarchive/mcp_rhai/rhai.v | 1 - libarchive/mcp_rust_wip/command.v | 21 - libarchive/mcp_rust_wip/generics.v | 54 - libarchive/mcp_rust_wip/mcp.v | 52 - libarchive/mcp_rust_wip/prompts.v | 151 --- libarchive/mcp_rust_wip/tools.v | 299 ------ libarchive/openrpc_remove/.gitignore | 2 - .../openrpc_remove/examples/job_client.vsh | 179 ---- libarchive/openrpc_remove/examples/server.vsh | 40 - libarchive/openrpc_remove/factory.v | 27 - libarchive/openrpc_remove/handler.v | 68 -- .../openrpc_remove/handler_agent_manager.v | 71 -- .../openrpc_remove/handler_group_manager.v | 68 -- .../openrpc_remove/handler_job_manager.v | 66 -- .../openrpc_remove/handler_service_manager.v | 80 -- libarchive/openrpc_remove/model.v | 37 - .../specs/agent_manager_openrpc.json | 302 ------ .../specs/group_manager_openrpc.json | 218 ---- .../specs/job_manager_openrpc.json | 304 ------ .../specs/service_manager_openrpc.json | 301 ------ libarchive/openrpc_remove/ws_server.v | 93 -- libarchive/rhai/generate_rhai_example.v | 54 - libarchive/rhai/generate_wrapper_module.v | 36 - .../prompts/generate_rhai_function_wrapper.md | 538 ---------- libarchive/rhai/register_functions.v | 31 - libarchive/rhai/register_types.v | 89 -- libarchive/rhai/register_types_test.v | 44 - libarchive/rhai/rhai.v | 61 -- libarchive/rhai/rhai_test.v | 149 --- libarchive/rhai/templates/cargo.toml | 10 - libarchive/rhai/templates/engine.rs | 21 - libarchive/rhai/templates/example.rs | 40 - libarchive/rhai/templates/lib.rs | 1 - libarchive/rhai/templates/wrapper.rs | 7 - libarchive/rhai/testdata/functions.rs | 71 -- libarchive/rhai/testdata/types.rs | 148 --- libarchive/rhai/verify.v | 84 -- libarchive/smartid/sid.v | 118 --- libarchive/starlight/clean.v | 50 - libarchive/starlight/config.v | 126 --- libarchive/starlight/factory.v | 41 - libarchive/starlight/model.v | 24 - libarchive/starlight/site.v | 210 ---- libarchive/starlight/site_get.v | 109 -- libarchive/starlight/template.v | 55 - libarchive/starlight/templates/build.sh | 22 - .../starlight/templates/build_dev_publish.sh | 23 - .../starlight/templates/build_publish.sh | 22 - libarchive/starlight/templates/develop.sh | 16 - libarchive/starlight/watcher.v | 96 -- libarchive/zinit/openrpc.json | 873 --------------- libarchive/zinit/readme.md | 155 --- libarchive/zinit/rpc.v | 200 ---- libarchive/zinit/rpc_test.v | 88 -- libarchive/zinit/zinit.v | 178 ---- libarchive/zinit/zinit/service_1.yaml | 1 - libarchive/zinit/zinit/service_2.yaml | 3 - libarchive/zinit/zinit_client.v | 290 ----- libarchive/zinit/zinit_factory.v | 36 - libarchive/zinit/zinit_openrpc_test.v | 165 --- libarchive/zinit/zinit_stateless.v | 135 --- libarchive/zinit/zprocess.v | 266 ----- libarchive/zinit/zprocess_load.v | 80 -- compile.sh => scripts/compile.sh | 0 install_hero.sh => scripts/install_hero.sh | 0 install_v.sh => scripts/install_v.sh | 0 445 files changed, 18 insertions(+), 40974 deletions(-) delete mode 100644 .goosehints delete mode 100644 .zed/keymap.json delete mode 100644 .zed/tasks.json delete mode 100644 herolib.code-workspace delete mode 100644 libarchive/baobab/README.md delete mode 100644 libarchive/baobab/actor/client.v delete mode 100644 libarchive/baobab/generator/README.md delete mode 100644 libarchive/baobab/generator/_archive/client_typescript.v delete mode 100644 libarchive/baobab/generator/_archive/client_typescript_test.v delete mode 100644 libarchive/baobab/generator/_archive/generate_objects.v delete mode 100644 libarchive/baobab/generator/_archive/write_object_methods.v delete mode 100644 libarchive/baobab/generator/_archive/write_object_tests.v delete mode 100644 libarchive/baobab/generator/generate_act.v delete mode 100644 libarchive/baobab/generator/generate_actor_folder.v delete mode 100644 libarchive/baobab/generator/generate_actor_source.v delete mode 100644 libarchive/baobab/generator/generate_actor_test.v delete mode 100644 libarchive/baobab/generator/generate_clients.v delete mode 100644 libarchive/baobab/generator/generate_command.v delete mode 100644 libarchive/baobab/generator/generate_interface.v delete mode 100644 libarchive/baobab/generator/generate_methods.v delete mode 100644 libarchive/baobab/generator/generate_methods_example.v delete mode 100644 libarchive/baobab/generator/generate_methods_interface.v delete mode 100644 libarchive/baobab/generator/generate_model.v delete mode 100644 libarchive/baobab/generator/generate_openapi.v delete mode 100644 libarchive/baobab/generator/generate_openrpc.v delete mode 100644 libarchive/baobab/generator/generate_openrpc_test.v delete mode 100644 libarchive/baobab/generator/generate_scripts.v delete mode 100644 libarchive/baobab/generator/templates/actor.v.template delete mode 100644 libarchive/baobab/generator/templates/actor_example.v.template delete mode 100644 libarchive/baobab/generator/templates/actor_test.v.template delete mode 100644 libarchive/baobab/generator/templates/cli.v.template delete mode 100644 libarchive/baobab/generator/templates/client_test.v delete mode 100644 libarchive/baobab/generator/templates/command.v.template delete mode 100755 libarchive/baobab/generator/templates/docs.vsh.template delete mode 100644 libarchive/baobab/generator/templates/interface_http.v.template delete mode 100644 libarchive/baobab/generator/templates/interface_http_test.v.template delete mode 100644 libarchive/baobab/generator/templates/interface_openapi.v.template delete mode 100644 libarchive/baobab/generator/templates/interface_openapi_test.v.template delete mode 100644 libarchive/baobab/generator/templates/interface_openrpc.v.template delete mode 100644 libarchive/baobab/generator/templates/interface_openrpc_test.v.template delete mode 100644 libarchive/baobab/generator/templates/playground.v.template delete mode 100755 libarchive/baobab/generator/templates/run.sh.template delete mode 100755 libarchive/baobab/generator/templates/run_actor.vsh.template delete mode 100755 libarchive/baobab/generator/templates/run_actor_example.vsh.template delete mode 100755 libarchive/baobab/generator/templates/run_http_server.vsh.template delete mode 100644 libarchive/baobab/generator/templates/specifications.v.template delete mode 100644 libarchive/baobab/generator/testdata/.gitignore delete mode 100644 libarchive/baobab/generator/write_object_methods_test.v delete mode 100644 libarchive/baobab/osis/README.md delete mode 100644 libarchive/baobab/osis/factory.v delete mode 100644 libarchive/baobab/osis/indexer.v delete mode 100644 libarchive/baobab/osis/model.v delete mode 100644 libarchive/baobab/osis/osis.v delete mode 100644 libarchive/baobab/osis/root_object.v delete mode 100644 libarchive/baobab/osis/storer.v delete mode 100644 libarchive/baobab/osis/storer_generic.v delete mode 100644 libarchive/baobab/specification/README.md delete mode 100644 libarchive/baobab/specification/from_openapi.v delete mode 100644 libarchive/baobab/specification/from_openapi_test.v delete mode 100644 libarchive/baobab/specification/from_openrpc.v delete mode 100644 libarchive/baobab/specification/from_openrpc_test.v delete mode 100644 libarchive/baobab/specification/model.v delete mode 100644 libarchive/baobab/specification/to_openapi.v delete mode 100644 libarchive/baobab/specification/to_openapi_test.v delete mode 100644 libarchive/baobab/specification/to_openrpc.v delete mode 100644 libarchive/baobab/stage/README.md delete mode 100644 libarchive/baobab/stage/action.v delete mode 100644 libarchive/baobab/stage/action_client.v delete mode 100644 libarchive/baobab/stage/actor.v delete mode 100644 libarchive/baobab/stage/config.v delete mode 100644 libarchive/baobab/stage/error.v delete mode 100644 libarchive/baobab/stage/interfaces/jsonrpc.v delete mode 100644 libarchive/baobab/stage/interfaces/openapi.v delete mode 100644 libarchive/baobab/stage/interfaces/openrpc.v delete mode 100644 libarchive/baobab/stage/interfaces/reflection_openapi.v delete mode 100644 libarchive/baobab/stage/interfaces/server_http.v delete mode 100644 libarchive/buildah/buildah_core.v delete mode 100644 libarchive/buildah/buildah_core_installers.v delete mode 100644 libarchive/buildah/buildah_exec.v delete mode 100644 libarchive/buildah/buildah_factory.v delete mode 100644 libarchive/buildah/buildah_hero.v delete mode 100644 libarchive/buildah/buildah_info.v delete mode 100644 libarchive/buildah/buildah_solutions.v delete mode 100644 libarchive/buildah/readme.md delete mode 100644 libarchive/daguserver/.heroscript delete mode 100644 libarchive/daguserver/daguserver_actions.v delete mode 100644 libarchive/daguserver/daguserver_factory_.v delete mode 100644 libarchive/daguserver/daguserver_model.v delete mode 100644 libarchive/daguserver/readme.md delete mode 100644 libarchive/daguserver/templates/communication.yaml delete mode 100644 libarchive/daguserver/templates/dagu.yaml delete mode 100644 libarchive/dedupestor/dedupe_ourdb/README.md delete mode 100644 libarchive/dedupestor/dedupe_ourdb/dedupestor.v delete mode 100644 libarchive/dedupestor/dedupe_ourdb/dedupestor_test.v delete mode 100644 libarchive/dedupestor/dedupestor.v delete mode 100644 libarchive/dedupestor/metadata.v delete mode 100644 libarchive/dedupestor/metadata_test.v delete mode 100644 libarchive/dify/.heroscript delete mode 100644 libarchive/dify/dify_actions.v delete mode 100644 libarchive/dify/dify_factory_.v delete mode 100644 libarchive/dify/dify_model.v delete mode 100644 libarchive/dify/readme.md delete mode 100644 libarchive/dify/templates/atemplate.yaml delete mode 100644 libarchive/doctree/README.md delete mode 100644 libarchive/doctree/collection/collection.v delete mode 100644 libarchive/doctree/collection/data/error.v delete mode 100644 libarchive/doctree/collection/data/file.v delete mode 100644 libarchive/doctree/collection/data/page.v delete mode 100644 libarchive/doctree/collection/data/process_aliases.v delete mode 100644 libarchive/doctree/collection/data/process_aliases_test.v delete mode 100644 libarchive/doctree/collection/data/process_def_pointers.v delete mode 100644 libarchive/doctree/collection/data/process_def_pointers_test.v delete mode 100644 libarchive/doctree/collection/data/process_link.v delete mode 100644 libarchive/doctree/collection/data/process_link_test.v delete mode 100644 libarchive/doctree/collection/data/process_macros.v delete mode 100644 libarchive/doctree/collection/error.v delete mode 100644 libarchive/doctree/collection/export.v delete mode 100644 libarchive/doctree/collection/export_test.v delete mode 100644 libarchive/doctree/collection/getters.v delete mode 100644 libarchive/doctree/collection/scan.v delete mode 100644 libarchive/doctree/collection/scan_test.v delete mode 100644 libarchive/doctree/collection/template/errors.md delete mode 100644 libarchive/doctree/collection/testdata/.gitignore delete mode 100644 libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/.collection delete mode 100644 libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/.linkedpages delete mode 100644 libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/errors.md delete mode 100644 libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/file1.md delete mode 100644 libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/file2.md delete mode 100644 libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/img/image.png delete mode 100644 libarchive/doctree/collection/testdata/export_test/mytree/dir1/.collection delete mode 100644 libarchive/doctree/collection/testdata/export_test/mytree/dir1/dir2/file1.md delete mode 100644 libarchive/doctree/collection/testdata/export_test/mytree/dir1/file2.md delete mode 100644 libarchive/doctree/collection/testdata/export_test/mytree/dir1/image.png delete mode 100644 libarchive/doctree/error.v delete mode 100644 libarchive/doctree/export.v delete mode 100644 libarchive/doctree/export_test.v delete mode 100644 libarchive/doctree/getters.v delete mode 100644 libarchive/doctree/getters_test.v delete mode 100644 libarchive/doctree/list.v delete mode 100644 libarchive/doctree/play.v delete mode 100644 libarchive/doctree/pointer/pointer.v delete mode 100644 libarchive/doctree/pointer/pointer_test.v delete mode 100644 libarchive/doctree/process_defs.v delete mode 100644 libarchive/doctree/process_defs_test.v delete mode 100644 libarchive/doctree/process_includes.v delete mode 100644 libarchive/doctree/process_includes_test.v delete mode 100644 libarchive/doctree/process_macros.v delete mode 100644 libarchive/doctree/scan.v delete mode 100644 libarchive/doctree/testdata/.gitignore delete mode 100644 libarchive/doctree/testdata/actions/.collection delete mode 100644 libarchive/doctree/testdata/actions/actions1.md delete mode 100644 libarchive/doctree/testdata/actions/functionality/actions2.md delete mode 100644 libarchive/doctree/testdata/export_test/export_expected/col1/.collection delete mode 100644 libarchive/doctree/testdata/export_test/export_expected/col1/.linkedpages delete mode 100644 libarchive/doctree/testdata/export_test/export_expected/col1/errors.md delete mode 100644 libarchive/doctree/testdata/export_test/export_expected/col1/file1.md delete mode 100644 libarchive/doctree/testdata/export_test/export_expected/col1/file2.md delete mode 100644 libarchive/doctree/testdata/export_test/export_expected/col1/img/image.png delete mode 100644 libarchive/doctree/testdata/export_test/export_expected/col2/.collection delete mode 100644 libarchive/doctree/testdata/export_test/export_expected/col2/.linkedpages delete mode 100644 libarchive/doctree/testdata/export_test/export_expected/col2/file3.md delete mode 100644 libarchive/doctree/testdata/export_test/mytree/dir1/.collection delete mode 100644 libarchive/doctree/testdata/export_test/mytree/dir1/dir2/file1.md delete mode 100644 libarchive/doctree/testdata/export_test/mytree/dir1/file2.md delete mode 100644 libarchive/doctree/testdata/export_test/mytree/dir1/image.png delete mode 100644 libarchive/doctree/testdata/export_test/mytree/dir3/.collection delete mode 100644 libarchive/doctree/testdata/export_test/mytree/dir3/file3.md delete mode 100644 libarchive/doctree/testdata/process_defs_test/col1/page1.md delete mode 100644 libarchive/doctree/testdata/process_defs_test/col2/page2.md delete mode 100644 libarchive/doctree/testdata/process_includes_test/col1/page1.md delete mode 100644 libarchive/doctree/testdata/process_includes_test/col2/page2.md delete mode 100644 libarchive/doctree/testdata/process_includes_test/col2/page3.md delete mode 100644 libarchive/doctree/testdata/rpc/.collection delete mode 100644 libarchive/doctree/testdata/rpc/eth.md delete mode 100644 libarchive/doctree/testdata/rpc/rpc.md delete mode 100644 libarchive/doctree/testdata/rpc/stellar.md delete mode 100644 libarchive/doctree/testdata/rpc/tfchain.md delete mode 100644 libarchive/doctree/testdata/rpc/tfgrid.md delete mode 100644 libarchive/doctree/testdata/tree_test/fruits/.collection delete mode 100644 libarchive/doctree/testdata/tree_test/fruits/apple.md delete mode 100644 libarchive/doctree/testdata/tree_test/fruits/banana.txt delete mode 100644 libarchive/doctree/testdata/tree_test/fruits/berries/img/digital_twin.png delete mode 100644 libarchive/doctree/testdata/tree_test/fruits/berries/strawberry.md delete mode 100644 libarchive/doctree/testdata/tree_test/fruits/intro.md delete mode 100644 libarchive/doctree/testdata/tree_test/vegetables/.collection delete mode 100644 libarchive/doctree/testdata/tree_test/vegetables/cabbage.txt delete mode 100644 libarchive/doctree/testdata/tree_test/vegetables/cruciferous/broccoli.md delete mode 100644 libarchive/doctree/testdata/tree_test/vegetables/intro.md delete mode 100644 libarchive/doctree/testdata/tree_test/vegetables/tomato.md delete mode 100644 libarchive/doctree/tree.v delete mode 100644 libarchive/doctree/tree_test.v delete mode 100644 libarchive/doctreeclient/README.md delete mode 100644 libarchive/doctreeclient/client.v delete mode 100755 libarchive/doctreeclient/doctree_test.v delete mode 100644 libarchive/doctreeclient/extract_links.v delete mode 100644 libarchive/doctreeclient/extract_links_test.v delete mode 100644 libarchive/doctreeclient/factory.v delete mode 100644 libarchive/doctreeclient/model.v delete mode 100644 libarchive/encoderherocomplex/decoder.v delete mode 100644 libarchive/encoderherocomplex/decoder_test.v delete mode 100644 libarchive/encoderherocomplex/encoder.v delete mode 100644 libarchive/encoderherocomplex/encoder_ignorepropery_test.v delete mode 100644 libarchive/encoderherocomplex/encoder_test.v delete mode 100644 libarchive/encoderherocomplex/postgres_client_decoder_test.v delete mode 100644 libarchive/encoderherocomplex/readme.md delete mode 100644 libarchive/encoderherocomplex/roundtrip_test.v delete mode 100644 libarchive/encoderherocomplex/tools.v delete mode 100644 libarchive/encoderherocomplex/types.v delete mode 100644 libarchive/examples/baobab/generator/.gitignore delete mode 100644 libarchive/examples/baobab/generator/basic/.gitignore delete mode 100644 libarchive/examples/baobab/generator/basic/README.md delete mode 100755 libarchive/examples/baobab/generator/basic/generate_actor_module.vsh delete mode 100755 libarchive/examples/baobab/generator/basic/generate_methods.vsh delete mode 100755 libarchive/examples/baobab/generator/basic/generate_openrpc_file.vsh delete mode 100644 libarchive/examples/baobab/generator/basic/openrpc.json delete mode 100644 libarchive/examples/baobab/generator/geomind_poc/.gitignore delete mode 100644 libarchive/examples/baobab/generator/geomind_poc/farmer.json delete mode 100755 libarchive/examples/baobab/generator/geomind_poc/generate.vsh delete mode 100644 libarchive/examples/baobab/generator/geomind_poc/merchant.json delete mode 100644 libarchive/examples/baobab/generator/geomind_poc/model.v delete mode 100644 libarchive/examples/baobab/generator/geomind_poc/play.v delete mode 100644 libarchive/examples/baobab/generator/geomind_poc/profiler.json delete mode 100644 libarchive/examples/baobab/generator/geomind_poc/server.v delete mode 100644 libarchive/examples/baobab/generator/geomind_poc/specs.md delete mode 100644 libarchive/examples/baobab/generator/geomind_poc/test_commerce.vsh delete mode 100755 libarchive/examples/baobab/generator/mcc_example.vsh delete mode 100644 libarchive/examples/baobab/generator/openapi_e2e/.gitignore delete mode 100755 libarchive/examples/baobab/generator/openapi_e2e/generate_actor_module.vsh delete mode 100644 libarchive/examples/baobab/generator/openapi_e2e/openapi.json delete mode 100644 libarchive/examples/baobab/specification/README.md delete mode 100644 libarchive/examples/baobab/specification/openapi.json delete mode 100755 libarchive/examples/baobab/specification/openapi_to_specification.vsh delete mode 100644 libarchive/examples/baobab/specification/openrpc.json delete mode 100644 libarchive/examples/baobab/specification/openrpc0.json delete mode 100755 libarchive/examples/baobab/specification/openrpc_to_specification.vsh delete mode 100755 libarchive/examples/baobab/specification/specification_to_openapi.vsh delete mode 100755 libarchive/examples/baobab/specification/specification_to_openrpc.vsh delete mode 100644 libarchive/github_actions_security.yml delete mode 100644 libarchive/hero_build.yml delete mode 100644 libarchive/installers/web/caddy2/.heroscript delete mode 100644 libarchive/installers/web/caddy2/caddy_actions.v delete mode 100644 libarchive/installers/web/caddy2/caddy_factory_.v delete mode 100644 libarchive/installers/web/caddy2/caddy_model.v delete mode 100644 libarchive/installers/web/caddy2/installer.v delete mode 100644 libarchive/installers/web/caddy2/installers.v delete mode 100644 libarchive/installers/web/caddy2/play.v delete mode 100644 libarchive/installers/web/caddy2/plugins.v delete mode 100644 libarchive/installers/web/caddy2/readme.md delete mode 100644 libarchive/installers/web/caddy2/templates/caddyfile_default delete mode 100644 libarchive/installers/web/caddy2/templates/caddyfile_domain delete mode 100644 libarchive/mcp_baobab/README.md delete mode 100644 libarchive/mcp_baobab/baobab_tools.v delete mode 100644 libarchive/mcp_baobab/baobab_tools_test.v delete mode 100644 libarchive/mcp_baobab/command.v delete mode 100644 libarchive/mcp_baobab/mcp_test.v delete mode 100644 libarchive/mcp_baobab/server.v delete mode 100644 libarchive/mcp_old/README.md delete mode 100644 libarchive/mcp_old/backend_interface.v delete mode 100644 libarchive/mcp_old/backend_memory.v delete mode 100644 libarchive/mcp_old/baobab/README.md delete mode 100644 libarchive/mcp_old/baobab/baobab_tools.v delete mode 100644 libarchive/mcp_old/baobab/baobab_tools_test.v delete mode 100644 libarchive/mcp_old/baobab/command.v delete mode 100644 libarchive/mcp_old/baobab/mcp_test.v delete mode 100644 libarchive/mcp_old/baobab/server.v delete mode 100755 libarchive/mcp_old/cmd/compile.vsh delete mode 100644 libarchive/mcp_old/cmd/mcp.v delete mode 100644 libarchive/mcp_old/factory.v delete mode 100644 libarchive/mcp_old/generics.v delete mode 100644 libarchive/mcp_old/handler_initialize.v delete mode 100644 libarchive/mcp_old/handler_initialize_test.v delete mode 100644 libarchive/mcp_old/handler_logging.v delete mode 100644 libarchive/mcp_old/handler_prompts.v delete mode 100644 libarchive/mcp_old/handler_resources.v delete mode 100644 libarchive/mcp_old/handler_tools.v delete mode 100644 libarchive/mcp_old/mcpgen/README.md delete mode 100644 libarchive/mcp_old/mcpgen/command.v delete mode 100644 libarchive/mcp_old/mcpgen/mcpgen.v delete mode 100644 libarchive/mcp_old/mcpgen/mcpgen_helpers.v delete mode 100644 libarchive/mcp_old/mcpgen/mcpgen_tools.v delete mode 100644 libarchive/mcp_old/mcpgen/schemas/create_mcp_tools_code_tool_input.json delete mode 100644 libarchive/mcp_old/mcpgen/server.v delete mode 100644 libarchive/mcp_old/mcpgen/templates/tool_code.v.template delete mode 100644 libarchive/mcp_old/mcpgen/templates/tool_handler.v.template delete mode 100644 libarchive/mcp_old/mcpgen/templates/tools_file.v.template delete mode 100644 libarchive/mcp_old/model_configuration.v delete mode 100644 libarchive/mcp_old/model_configuration_test.v delete mode 100644 libarchive/mcp_old/model_error.v delete mode 100644 libarchive/mcp_old/pugconvert/cmd/.gitignore delete mode 100755 libarchive/mcp_old/pugconvert/cmd/compile.sh delete mode 100644 libarchive/mcp_old/pugconvert/cmd/main.v delete mode 100644 libarchive/mcp_old/pugconvert/logic/convertpug.v delete mode 100644 libarchive/mcp_old/pugconvert/logic/jetvalidation.v delete mode 100644 libarchive/mcp_old/pugconvert/logic/loader.v delete mode 100644 libarchive/mcp_old/pugconvert/logic/templates/jet_instructions.md delete mode 100644 libarchive/mcp_old/pugconvert/mcp/handlers.v delete mode 100644 libarchive/mcp_old/pugconvert/mcp/mcp.v delete mode 100644 libarchive/mcp_old/pugconvert/mcp/specifications.v delete mode 100644 libarchive/mcp_old/rhai/cmd/.gitignore delete mode 100755 libarchive/mcp_old/rhai/cmd/compile.sh delete mode 100644 libarchive/mcp_old/rhai/cmd/main.v delete mode 100644 libarchive/mcp_old/rhai/example/example copy.vsh delete mode 100755 libarchive/mcp_old/rhai/example/example.vsh delete mode 100644 libarchive/mcp_old/rhai/logic/logic.v delete mode 100644 libarchive/mcp_old/rhai/logic/prompts/engine.md delete mode 100644 libarchive/mcp_old/rhai/logic/prompts/errors.md delete mode 100644 libarchive/mcp_old/rhai/logic/prompts/example_script.md delete mode 100644 libarchive/mcp_old/rhai/logic/prompts/main.md delete mode 100644 libarchive/mcp_old/rhai/logic/prompts/wrapper.md delete mode 100644 libarchive/mcp_old/rhai/logic/templates/cargo.toml delete mode 100644 libarchive/mcp_old/rhai/logic/templates/engine.rs delete mode 100644 libarchive/mcp_old/rhai/logic/templates/example.rs delete mode 100644 libarchive/mcp_old/rhai/logic/templates/functions.rs delete mode 100644 libarchive/mcp_old/rhai/logic/templates/generic_wrapper.rs delete mode 100644 libarchive/mcp_old/rhai/logic/templates/lib.rs delete mode 100644 libarchive/mcp_old/rhai/mcp/command.v delete mode 100644 libarchive/mcp_old/rhai/mcp/mcp.v delete mode 100644 libarchive/mcp_old/rhai/mcp/prompts.v delete mode 100644 libarchive/mcp_old/rhai/mcp/specifications.v delete mode 100644 libarchive/mcp_old/rhai/mcp/tools.v delete mode 100644 libarchive/mcp_old/rhai/rhai.v delete mode 100644 libarchive/mcp_old/server.v delete mode 100644 libarchive/mcp_old/transport/http.v delete mode 100644 libarchive/mcp_old/transport/interface.v delete mode 100644 libarchive/mcp_old/transport/stdio.v delete mode 100644 libarchive/mcp_old/vcode/README.md delete mode 100644 libarchive/mcp_old/vcode/cmd/.gitignore delete mode 100755 libarchive/mcp_old/vcode/cmd/compile.sh delete mode 100644 libarchive/mcp_old/vcode/cmd/main.v delete mode 100644 libarchive/mcp_old/vcode/logic/server.v delete mode 100644 libarchive/mcp_old/vcode/logic/test_client.vsh delete mode 100644 libarchive/mcp_old/vcode/logic/vlang.v delete mode 100644 libarchive/mcp_old/vcode/logic/vlang_test.v delete mode 100644 libarchive/mcp_old/vcode/logic/vlang_tools.v delete mode 100644 libarchive/mcp_old/vcode/logic/write_vfile_tool.v delete mode 100644 libarchive/mcp_old/vcode/mcp/handlers.v delete mode 100644 libarchive/mcp_old/vcode/mcp/mcp.v delete mode 100644 libarchive/mcp_old/vcode/mcp/specifications.v delete mode 100644 libarchive/mcp_pugconvert/cmd/.gitignore delete mode 100755 libarchive/mcp_pugconvert/cmd/compile.sh delete mode 100644 libarchive/mcp_pugconvert/cmd/main.v delete mode 100644 libarchive/mcp_pugconvert/logic/convertpug.v delete mode 100644 libarchive/mcp_pugconvert/logic/jetvalidation.v delete mode 100644 libarchive/mcp_pugconvert/logic/loader.v delete mode 100644 libarchive/mcp_pugconvert/logic/templates/jet_instructions.md delete mode 100644 libarchive/mcp_pugconvert/mcp/handlers.v delete mode 100644 libarchive/mcp_pugconvert/mcp/mcp.v delete mode 100644 libarchive/mcp_pugconvert/mcp/specifications.v delete mode 100644 libarchive/mcp_rhai/cmd/.gitignore delete mode 100755 libarchive/mcp_rhai/cmd/compile.sh delete mode 100644 libarchive/mcp_rhai/cmd/main.v delete mode 100644 libarchive/mcp_rhai/example/example copy.vsh delete mode 100755 libarchive/mcp_rhai/example/example.vsh delete mode 100644 libarchive/mcp_rhai/logic/logic.v delete mode 100644 libarchive/mcp_rhai/logic/logic_sampling.v delete mode 100644 libarchive/mcp_rhai/logic/prompts/engine.md delete mode 100644 libarchive/mcp_rhai/logic/prompts/errors.md delete mode 100644 libarchive/mcp_rhai/logic/prompts/example_script.md delete mode 100644 libarchive/mcp_rhai/logic/prompts/main.md delete mode 100644 libarchive/mcp_rhai/logic/prompts/wrapper.md delete mode 100644 libarchive/mcp_rhai/logic/templates/cargo.toml delete mode 100644 libarchive/mcp_rhai/logic/templates/engine.rs delete mode 100644 libarchive/mcp_rhai/logic/templates/example.rs delete mode 100644 libarchive/mcp_rhai/logic/templates/functions.rs delete mode 100644 libarchive/mcp_rhai/logic/templates/generic_wrapper.rs delete mode 100644 libarchive/mcp_rhai/logic/templates/lib.rs delete mode 100644 libarchive/mcp_rhai/mcp/command.v delete mode 100644 libarchive/mcp_rhai/mcp/mcp.v delete mode 100644 libarchive/mcp_rhai/mcp/prompts.v delete mode 100644 libarchive/mcp_rhai/mcp/specifications.v delete mode 100644 libarchive/mcp_rhai/mcp/tools.v delete mode 100644 libarchive/mcp_rhai/rhai.v delete mode 100644 libarchive/mcp_rust_wip/command.v delete mode 100644 libarchive/mcp_rust_wip/generics.v delete mode 100644 libarchive/mcp_rust_wip/mcp.v delete mode 100644 libarchive/mcp_rust_wip/prompts.v delete mode 100644 libarchive/mcp_rust_wip/tools.v delete mode 100644 libarchive/openrpc_remove/.gitignore delete mode 100755 libarchive/openrpc_remove/examples/job_client.vsh delete mode 100755 libarchive/openrpc_remove/examples/server.vsh delete mode 100644 libarchive/openrpc_remove/factory.v delete mode 100644 libarchive/openrpc_remove/handler.v delete mode 100644 libarchive/openrpc_remove/handler_agent_manager.v delete mode 100644 libarchive/openrpc_remove/handler_group_manager.v delete mode 100644 libarchive/openrpc_remove/handler_job_manager.v delete mode 100644 libarchive/openrpc_remove/handler_service_manager.v delete mode 100644 libarchive/openrpc_remove/model.v delete mode 100644 libarchive/openrpc_remove/specs/agent_manager_openrpc.json delete mode 100644 libarchive/openrpc_remove/specs/group_manager_openrpc.json delete mode 100644 libarchive/openrpc_remove/specs/job_manager_openrpc.json delete mode 100644 libarchive/openrpc_remove/specs/service_manager_openrpc.json delete mode 100644 libarchive/openrpc_remove/ws_server.v delete mode 100644 libarchive/rhai/generate_rhai_example.v delete mode 100644 libarchive/rhai/generate_wrapper_module.v delete mode 100644 libarchive/rhai/prompts/generate_rhai_function_wrapper.md delete mode 100644 libarchive/rhai/register_functions.v delete mode 100644 libarchive/rhai/register_types.v delete mode 100644 libarchive/rhai/register_types_test.v delete mode 100644 libarchive/rhai/rhai.v delete mode 100644 libarchive/rhai/rhai_test.v delete mode 100644 libarchive/rhai/templates/cargo.toml delete mode 100644 libarchive/rhai/templates/engine.rs delete mode 100644 libarchive/rhai/templates/example.rs delete mode 100644 libarchive/rhai/templates/lib.rs delete mode 100644 libarchive/rhai/templates/wrapper.rs delete mode 100644 libarchive/rhai/testdata/functions.rs delete mode 100644 libarchive/rhai/testdata/types.rs delete mode 100644 libarchive/rhai/verify.v delete mode 100644 libarchive/smartid/sid.v delete mode 100644 libarchive/starlight/clean.v delete mode 100644 libarchive/starlight/config.v delete mode 100644 libarchive/starlight/factory.v delete mode 100644 libarchive/starlight/model.v delete mode 100644 libarchive/starlight/site.v delete mode 100644 libarchive/starlight/site_get.v delete mode 100644 libarchive/starlight/template.v delete mode 100755 libarchive/starlight/templates/build.sh delete mode 100755 libarchive/starlight/templates/build_dev_publish.sh delete mode 100755 libarchive/starlight/templates/build_publish.sh delete mode 100755 libarchive/starlight/templates/develop.sh delete mode 100644 libarchive/starlight/watcher.v delete mode 100644 libarchive/zinit/openrpc.json delete mode 100644 libarchive/zinit/readme.md delete mode 100644 libarchive/zinit/rpc.v delete mode 100644 libarchive/zinit/rpc_test.v delete mode 100644 libarchive/zinit/zinit.v delete mode 100644 libarchive/zinit/zinit/service_1.yaml delete mode 100644 libarchive/zinit/zinit/service_2.yaml delete mode 100644 libarchive/zinit/zinit_client.v delete mode 100644 libarchive/zinit/zinit_factory.v delete mode 100644 libarchive/zinit/zinit_openrpc_test.v delete mode 100644 libarchive/zinit/zinit_stateless.v delete mode 100644 libarchive/zinit/zprocess.v delete mode 100644 libarchive/zinit/zprocess_load.v rename compile.sh => scripts/compile.sh (100%) rename install_hero.sh => scripts/install_hero.sh (100%) rename install_v.sh => scripts/install_v.sh (100%) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 50bc0e5e..c15989ba 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -27,7 +27,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Vlang - run: ./install_v.sh + run: ./scripts/install_v.sh - name: Generate documentation run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8ec9689c..dc0c4b74 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ jobs: run: | # Updating man-db takes a long time on every run. We don't need it sudo apt-get remove -y --purge man-db - ./install_v.sh + ./scripts/install_v.sh - name: Setup Herolib from current branch run: | diff --git a/.goosehints b/.goosehints deleted file mode 100644 index d4d35bd7..00000000 --- a/.goosehints +++ /dev/null @@ -1,5 +0,0 @@ - -when fixing or creating code, refer to the following hints: -@aiprompts/vlang_herolib_core.md - - diff --git a/.zed/keymap.json b/.zed/keymap.json deleted file mode 100644 index f4d87d49..00000000 --- a/.zed/keymap.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "context": "Workspace", - "bindings": { - "cmd-r": ["task::Spawn", { "task_name": "ET", "reveal_target": "center" }] - } -} diff --git a/.zed/tasks.json b/.zed/tasks.json deleted file mode 100644 index 70301c35..00000000 --- a/.zed/tasks.json +++ /dev/null @@ -1,47 +0,0 @@ -[ - { - "label": "ET", - "command": "for i in {1..5}; do echo \"Hello $i/5\"; sleep 1; done", - //"args": [], - // Env overrides for the command, will be appended to the terminal's environment from the settings. - "env": { "foo": "bar" }, - // Current working directory to spawn the command into, defaults to current project root. - //"cwd": "/path/to/working/directory", - // Whether to use a new terminal tab or reuse the existing one to spawn the process, defaults to `false`. - "use_new_terminal": true, - // Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish, defaults to `false`. - "allow_concurrent_runs": false, - // What to do with the terminal pane and tab, after the command was started: - // * `always` — always show the task's pane, and focus the corresponding tab in it (default) - // * `no_focus` — always show the task's pane, add the task's tab in it, but don't focus it - // * `never` — do not alter focus, but still add/reuse the task's tab in its pane - "reveal": "always", - // What to do with the terminal pane and tab, after the command has finished: - // * `never` — Do nothing when the command finishes (default) - // * `always` — always hide the terminal tab, hide the pane also if it was the last tab in it - // * `on_success` — hide the terminal tab on task success only, otherwise behaves similar to `always` - "hide": "never", - // Which shell to use when running a task inside the terminal. - // May take 3 values: - // 1. (default) Use the system's default terminal configuration in /etc/passwd - // "shell": "system" - // 2. A program: - // "shell": { - // "program": "sh" - // } - // 3. A program with arguments: - // "shell": { - // "with_arguments": { - // "program": "/bin/bash", - // "args": ["--login"] - // } - // } - "shell": "system", - // Whether to show the task line in the output of the spawned task, defaults to `true`. - "show_summary": true, - // Whether to show the command line in the output of the spawned task, defaults to `true`. - // "show_output": true, - // Represents the tags for inline runnable indicators, or spawning multiple tasks at once. - "tags": ["DODO"] - } -] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6b4d4686..4c070594 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ Thank you for your interest in contributing to Herolib! This document provides g For developers, you can use the automated installation script: ```bash -curl 'https://raw.githubusercontent.com/incubaid/herolib/refs/heads/development/install_v.sh' > /tmp/install_v.sh +curl 'https://raw.githubusercontent.com/incubaid/herolib/refs/heads/development/scripts/install_v.sh' > /tmp/install_v.sh bash /tmp/install_v.sh --analyzer --herolib # IMPORTANT: Start a new shell after installation for paths to be set correctly ``` diff --git a/README.md b/README.md index 298dc5df..6885cd41 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,11 @@ The Hero tool can be used to work with git, build documentation, interact with H For development purposes, use the automated installation script: ```bash -curl 'https://raw.githubusercontent.com/incubaid/herolib/refs/heads/development/install_v.sh' > /tmp/install_v.sh +curl 'https://raw.githubusercontent.com/incubaid/herolib/refs/heads/development/scripts/install_v.sh' > /tmp/install_v.sh bash /tmp/install_v.sh --analyzer --herolib #do not forget to do the following this makes sure vtest and vrun exists -cd ~/code/github/incubaid/herolib +cd ~/code/github/incubaid/herolib/scripts v install_herolib.vsh # IMPORTANT: Start a new shell after installation for paths to be set correctly @@ -51,7 +51,7 @@ v install_herolib.vsh ``` V & HeroLib Installer Script -Usage: ~/code/github/incubaid/herolib/install_v.sh [options] +Usage: ~/code/github/incubaid/herolib/scripts/install_v.sh [options] Options: -h, --help Show this help message @@ -61,12 +61,12 @@ Options: --herolib Install our herolib Examples: - ~/code/github/incubaid/herolib/install_v.sh - ~/code/github/incubaid/herolib/install_v.sh --reset - ~/code/github/incubaid/herolib/install_v.sh --remove - ~/code/github/incubaid/herolib/install_v.sh --analyzer - ~/code/github/incubaid/herolib/install_v.sh --herolib - ~/code/github/incubaid/herolib/install_v.sh --reset --analyzer # Fresh install of both + ~/code/github/incubaid/herolib/scripts/install_v.sh + ~/code/github/incubaid/herolib/scripts/install_v.sh --reset + ~/code/github/incubaid/herolib/scripts/install_v.sh --remove + ~/code/github/incubaid/herolib/scripts/install_v.sh --analyzer + ~/code/github/incubaid/herolib/scripts/install_v.sh --herolib + ~/code/github/incubaid/herolib/scripts/install_v.sh --reset --analyzer # Fresh install of both ``` ## Features @@ -175,46 +175,3 @@ To generate documentation locally: cd ~/code/github/incubaid/herolib bash doc.sh ``` - -## Export Behavior - Cross-Collection Assets - -When exporting collections, Atlas now automatically handles cross-collection references: - -### Pages -If a page in Collection A links to a page in Collection B: -- The target page is copied to Collection A's export directory -- Filename is renamed to avoid conflicts: `collectionb_pagename.md` -- The link is updated to reference the local file - -### Images and Files -Images and files referenced from other collections are: -- Copied to the `img/` subdirectory of the exporting collection -- Renamed with cross-collection prefix: `othercol_filename.ext` -- Image/file references in pages are updated to the new paths - -### Result -The exported collection directory is **self-contained**: -``` -destination/ - collectiona/ - .collection - page1.md - collectionb_intro.md # Copied from Collection B - img/ - logo.png # Local image - collectionc_logo.png # Copied from Collection C -``` - -### Metadata Export - -Metadata is now exported separately using the `destination_meta` parameter: - -```heroscript -!!atlas.export - destination: './output' - destination_meta: './metadata' # Saves JSON metadata files here - include: true - redis: true -``` - -This exports collection metadata to: `./metadata/collection1.json`, `./metadata/collection2.json`, etc. \ No newline at end of file diff --git a/aiprompts/.openhands/setup.sh b/aiprompts/.openhands/setup.sh index 73221bf2..0d81d376 100644 --- a/aiprompts/.openhands/setup.sh +++ b/aiprompts/.openhands/setup.sh @@ -16,4 +16,4 @@ NC='\033[0m' # No Color SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" -/workspace/herolib/install_v.sh \ No newline at end of file +/workspace/herolib/scripts/install_v.sh \ No newline at end of file diff --git a/docker/herolib/build.sh b/docker/herolib/build.sh index 063ecaed..05423cd5 100755 --- a/docker/herolib/build.sh +++ b/docker/herolib/build.sh @@ -5,8 +5,8 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd "$SCRIPT_DIR" # Copy installation files -cp ../../install_v.sh ./scripts/install_v.sh -cp ../../install_herolib.vsh ./scripts/install_herolib.vsh +cp ../../scripts/install_v.sh ./scripts/install_v.sh +cp ../../scripts/install_herolib.vsh ./scripts/install_herolib.vsh # Docker image and container names DOCKER_IMAGE_NAME="herolib" diff --git a/herolib.code-workspace b/herolib.code-workspace deleted file mode 100644 index 9c90acca..00000000 --- a/herolib.code-workspace +++ /dev/null @@ -1,31 +0,0 @@ -{ - "folders": [ - { - "path": "." - }, - ], - "settings": { - "extensions.ignoreRecommendations": false - }, - "extensions": { - "unwantedRecommendations": [], - "recommendations": [ - "saoudrizwan.claude-dev", - "shakram02.bash-beautify", - "ms-vsliveshare.vsliveshare", - "yzhang.markdown-all-in-one", - "elesoho.vscode-markdown-paste-image", - "ms-vscode-remote.remote-ssh", - "ms-vscode-remote.remote-ssh-edit", - "ms-vscode.remote-explorer", - "ms-vscode.remote-repositories", - "charliermarsh.ruff", - "bmalehorn.shell-syntax", - "qwtel.sqlite-viewer", - "simonsiefke.svg-preview", - "gruntfuggly.todo-tree", - "vosca.vscode-v-analyzer", - "tomoki1207.pdf" - ] - } -} \ No newline at end of file diff --git a/lib/builder/bootstrapper.v b/lib/builder/bootstrapper.v index 49607b2d..46ebaa6f 100644 --- a/lib/builder/bootstrapper.v +++ b/lib/builder/bootstrapper.v @@ -57,10 +57,10 @@ pub fn (mut node Node) hero_install(args HeroInstallArgs) ! { mut todo := []string{} if !args.compile { - todo << 'curl https://raw.githubusercontent.com/incubaid/herolib/refs/heads/development/install_hero.sh > /tmp/install.sh' + todo << 'curl https://raw.githubusercontent.com/incubaid/herolib/refs/heads/development/scripts/install_hero.sh > /tmp/install.sh' todo << 'bash /tmp/install.sh' } else { - todo << "curl 'https://raw.githubusercontent.com/incubaid/herolib/refs/heads/development/install_v.sh' > /tmp/install_v.sh" + todo << "curl 'https://raw.githubusercontent.com/incubaid/herolib/refs/heads/development/scripts/install_v.sh' > /tmp/install_v.sh" if args.v_analyzer { todo << 'bash /tmp/install_v.sh --analyzer --herolib ' } else { diff --git a/lib/installers/lang/herolib/herolib.v b/lib/installers/lang/herolib/herolib.v index dfe1eaed..613739e2 100644 --- a/lib/installers/lang/herolib/herolib.v +++ b/lib/installers/lang/herolib/herolib.v @@ -101,7 +101,7 @@ pub fn compile(args InstallArgs) ! { cmd := " cd /tmp export TERM=xterm - curl 'https://raw.githubusercontent.com/incubaid/herolib/refs/heads/development/install_v.sh' > /tmp/install_v.sh + curl 'https://raw.githubusercontent.com/incubaid/herolib/refs/heads/development/scripts/install_v.sh' > /tmp/install_v.sh bash /tmp/install_v.sh --herolib " osal.execute_stdout(cmd) or { return error('Cannot install hero.\n${err}') } diff --git a/libarchive/baobab/README.md b/libarchive/baobab/README.md deleted file mode 100644 index a0285eda..00000000 --- a/libarchive/baobab/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Base Object and Actor Backend - -This is Hero’s backend, designed around the concept of base objects and actors to enable modular, domain-specific operations. - -## Base Object - -Base objects are digital representations of real-world entities. Examples include projects, publications, books, stories (agile), and calendar events. These objects: - • Serve as the primary data units that actors operate on. - • Contain indexable fields for efficient retrieval. - • Share a common base class with attributes like: - • Name: The object’s identifier. - • Description: A brief summary of the object. - • Remarks: A list of additional notes or metadata. - -Base objects are stored, indexed, retrieved, and updated using OSIS (Object Storage and Indexing System). - -## Actor - -Actors are domain-specific operation handlers that work on base objects. For instance, a Project Manager Actor might manage operations on stories, sprints, or projects. - -Key Features of Actors: - • Domain-Specific Languages (DSLs): Actor methods form intuitive, logical DSLs for interacting with base objects. - • Specification-Driven: - • Actors are generated from specifications. - • Code written for actor methods can be parsed back into specifications. - • Code Generation: Specifications enable automated boilerplate code generation, reducing manual effort. - -## Modules - -### OSIS: Object Storage and Indexing System - -OSIS is a module designed for efficient storage and indexing of root objects based on specific fields. It enables seamless management of data across various backends, with built-in support for field-based filtering and searching. - -#### Key Components - -**Indexer:** -* Creates and manages SQL tables based on base object specifications. -* Enables indexing of specific fields, making them searchable and filterable. - -**Storer**: -* Handles actual data storage in different databases. -* Supports diverse encoding and encryption methods for secure data management. - -By integrating OSIS, the backend achieves both high-performance data querying and flexible, secure storage solutions. - -### Example Actor Module - -The Example Actor module is a reference and testable example of a generated actor within Baobab. It demonstrates the structure of actor modules generated from specifications and can also be parsed back into specifications. This module serves two key purposes: - -1. Acts as a reference for developers working on Baobab to understand and program against actor specifications. -2. Provides a compilable, generatable module for testing and validating Baobab’s code generation tools. \ No newline at end of file diff --git a/libarchive/baobab/actor/client.v b/libarchive/baobab/actor/client.v deleted file mode 100644 index 86f04977..00000000 --- a/libarchive/baobab/actor/client.v +++ /dev/null @@ -1,68 +0,0 @@ -module actor - -import json -import incubaid.herolib.clients.redisclient -import incubaid.herolib.baobab.action { ProcedureCall, ProcedureResponse } - -// Processor struct for managing procedure calls -pub struct Client { -pub mut: - rpc redisclient.RedisRpc // Redis RPC mechanism -} - -// Parameters for processing a procedure call -@[params] -pub struct Params { -pub: - timeout int = 60 // Timeout in seconds -} - -pub struct ClientConfig { -pub: - redis_url string // url to redis server running - redis_queue string // name of redis queue -} - -pub fn new_client(config ClientConfig) !Client { - mut redis := redisclient.new(config.redis_url)! - mut rpc_q := redis.rpc_get(config.redis_queue) - - return Client{ - rpc: rpc_q - } -} - -// Process the procedure call -pub fn (mut p Client) monologue(call ProcedureCall, params Params) ! { - // Use RedisRpc's `call` to send the call and wait for the response - response_data := p.rpc.call(redisclient.RPCArgs{ - cmd: call.method - data: call.params - timeout: u64(params.timeout * 1000) // Convert seconds to milliseconds - wait: true - })! - // TODO: check error type -} - -// Process the procedure call -pub fn (mut p Client) call_to_action(action Procedure, params Params) !ProcedureResponse { - // Use RedisRpc's `call` to send the call and wait for the response - response_data := p.rpc.call(redisclient.RPCArgs{ - cmd: call.method - data: call.params - timeout: u64(params.timeout * 1000) // Convert seconds to milliseconds - wait: true - }) or { - // TODO: check error type - return ProcedureResponse{ - error: err.msg() - } - // return ProcedureError{ - // reason: .timeout - // } - } - - return ProcedureResponse{ - result: response_data - } -} diff --git a/libarchive/baobab/generator/README.md b/libarchive/baobab/generator/README.md deleted file mode 100644 index 0e3e4553..00000000 --- a/libarchive/baobab/generator/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Generator - -The Generator synchronizes actor code and specifications, allowing bidirectional transformation between the two. - -This a - - - -## Development Workflow - -A sample development workflow using the generator would be like: -1. generating actor specification from an actor openrpc / openapi specification (see [specification reflection](specification/#reflection)) -2. generating actor code from the actor specification -3. updating actor code by filling in method prototypes -4. adding methods to the actor to develop actor further -5. parsing specification back from actor - -6. regenerating actor from the specification -this allows for - -- a tool which takes dir as input - - is just some v files which define models -- outputs a generated code dir with - - heroscript to memory for the model - - supporting v script for manipulated model - - name of actor e.g. ProjectManager, module would be project_manager - -## how does the actor work - -- is a global e.g. projectmanager_factory -- with double map - - key1: cid - - object: ProjectManager Object - -- Object: Project Manager - - has as properties: - - db_$rootobjectname which is map - - key: oid - - val: the Model which represents the rootobject - -- on factory - - actions_process - - process heroscript through path or text (params) - - action_process - - take 1 action as input - - ${rootobjectname}_export - - export all known objects as heroscript in chosen dir - - name of heroscript would be ${rootobjectname}_define.md - - ${rootobjectname}_get(oid) - - returns rootobject as copy - - ${rootobjectname}_list()! - - returns list as copy - - ${rootobjectname}_set(oid,obj)! - - ${rootobjectname}_delete(oid)! - - ${rootobjectname}_new()! - -- in action we have - - define - - export/import - - get - - list - - diff --git a/libarchive/baobab/generator/_archive/client_typescript.v b/libarchive/baobab/generator/_archive/client_typescript.v deleted file mode 100644 index a8f88110..00000000 --- a/libarchive/baobab/generator/_archive/client_typescript.v +++ /dev/null @@ -1,118 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.core.texttools -import incubaid.herolib.schemas.jsonschema.codegen -import incubaid.herolib.schemas.openrpc.codegen as openrpc_codegen -import incubaid.herolib.baobab.specification -import net.http - -// pub enum BaseObjectMethodType { -// new -// get -// set -// delete -// list -// other -// } - -// pub struct BaseObjectMethod { -// pub: -// typ BaseObjectMethodType -// object string // the name of the base object -// } - -// pub fn ts_client_get_fn(object string, params TSClientFunctionParams) string { -// name_snake := texttools.snake_case(object) -// name_pascal := texttools.pascal_case(object) -// root := get_endpoint_root(params.endpoint) - -// return "async get${name_pascal}(id: string): Promise<${name_pascal}> {\n return this.restClient.get<${name_pascal}>(`/${root}/${name_snake}/\${id}`);\n }" -// } - -// pub fn ts_client_set_fn(object string, params TSClientFunctionParams) string { -// name_snake := texttools.snake_case(object) -// name_pascal := texttools.pascal_case(object) -// root := get_endpoint_root(params.endpoint) - -// return "async set${name_pascal}(id: string, ${name_snake}: Partial<${name_pascal}>): Promise<${name_pascal}> {\n return this.restClient.put<${name_pascal}>(`/${root}/${name_snake}/\${id}`, ${name_snake});\n }" -// } - -// pub fn ts_client_delete_fn(object string, params TSClientFunctionParams) string { -// name_snake := texttools.snake_case(object) -// name_pascal := texttools.pascal_case(object) -// root := get_endpoint_root(params.endpoint) - -// return "async delete${name_pascal}(id: string): Promise {\n return this.restClient.delete(`/${root}/${name_snake}/\${id}`);\n }" -// } - -// pub fn ts_client_list_fn(object string, params TSClientFunctionParams) string { -// name_snake := texttools.snake_case(object) -// name_pascal := texttools.pascal_case(object) -// root := get_endpoint_root(params.endpoint) - -// return "async list${name_pascal}(): Promise<${name_pascal}[]> {\n return this.restClient.get<${name_pascal}[]>(`/${root}/${name_snake}`);\n }" -// } - -fn get_endpoint_root(root string) string { - return if root == '' { - '' - } else { - '/${root.trim('/')}' - } -} - -// // generates a Base Object's `create` method -// pub fn ts_client_new_fn(object string, params TSClientFunctionParams) string { -// name_snake := texttools.snake_case(object) -// name_pascal := texttools.pascal_case(object) -// root := get_endpoint_root(params.endpoint) - -// return "async create${name_snake}(object: Omit<${name_pascal}, 'id'>): Promise<${name_pascal}> { -// return this.restClient.post<${name_pascal}>('${root}/${name_snake}', board); -// }" -// } - -// pub fn ts_client_get_fn(object string, params TSClientFunctionParams) string { -// name_snake := texttools.snake_case(object) -// name_pascal := texttools.pascal_case(object) -// root := get_endpoint_root(params.endpoint) - -// return "async get${name_pascal}(id: string): Promise<${name_pascal}> {\n return this.restClient.get<${name_pascal}>(`/${root}/${name_snake}/\${id}`);\n }" -// } - -// pub fn ts_client_set_fn(object string, params TSClientFunctionParams) string { -// name_snake := texttools.snake_case(object) -// name_pascal := texttools.pascal_case(object) -// root := get_endpoint_root(params.endpoint) - -// return "async set${name_pascal}(id: string, ${name_snake}: Partial<${name_pascal}>): Promise<${name_pascal}> {\n return this.restClient.put<${name_pascal}>(`/${root}/${name_snake}/\${id}`, ${name_snake});\n }" -// } - -// pub fn ts_client_delete_fn(object string, params TSClientFunctionParams) string { -// name_snake := texttools.snake_case(object) -// name_pascal := texttools.pascal_case(object) -// root := get_endpoint_root(params.endpoint) - -// return "async delete${name_pascal}(id: string): Promise {\n return this.restClient.delete(`/${root}/${name_snake}/\${id}`);\n }" -// } - -// pub fn ts_client_list_fn(object string, params TSClientFunctionParams) string { -// name_snake := texttools.snake_case(object) -// name_pascal := texttools.pascal_case(object) -// root := get_endpoint_root(params.endpoint) - -// return "async list${name_pascal}(): Promise<${name_pascal}[]> {\n return this.restClient.get<${name_pascal}[]>(`/${root}/${name_snake}`);\n }" -// } - -// // generates a function prototype given an `ActorMethod` -// pub fn ts_client_fn_prototype(method ActorMethod) string { -// name := texttools.pascal_case(method.name) -// params := method.parameters -// .map(content_descriptor_to_parameter(it) or {panic(err)}) -// .map(it.typescript()) -// .join(', ') - -// return_type := content_descriptor_to_parameter(method.result) or {panic(err)}.typ.typescript() -// return 'async ${name}(${params}): Promise<${return_type}> {}' -// } diff --git a/libarchive/baobab/generator/_archive/client_typescript_test.v b/libarchive/baobab/generator/_archive/client_typescript_test.v deleted file mode 100644 index 16c235af..00000000 --- a/libarchive/baobab/generator/_archive/client_typescript_test.v +++ /dev/null @@ -1,205 +0,0 @@ -module generator - -import x.json2 as json -import arrays -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.baobab.specification -import incubaid.herolib.schemas.openrpc -import incubaid.herolib.schemas.jsonschema - -const specification = specification.ActorSpecification{ - name: 'Pet Store' - description: 'A sample API for a pet store' - structure: code.Struct{} - interfaces: [.openapi] - methods: [ - specification.ActorMethod{ - name: 'listPets' - summary: 'List all pets' - example: openrpc.ExamplePairing{ - params: [ - openrpc.ExampleRef(openrpc.Example{ - name: 'Example limit' - description: 'Example Maximum number of pets to return' - value: 10 - }), - ] - result: openrpc.ExampleRef(openrpc.Example{ - name: 'Example response' - value: json.raw_decode('[ - {"id": 1, "name": "Fluffy", "tag": "dog"}, - {"id": 2, "name": "Whiskers", "tag": "cat"} - ]')! - }) - } - parameters: [ - openrpc.ContentDescriptor{ - name: 'limit' - summary: 'Maximum number of pets to return' - description: 'Maximum number of pets to return' - required: false - schema: jsonschema.SchemaRef(jsonschema.Schema{ - ...jsonschema.schema_u32 - example: 10 - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pets' - description: 'A paged array of pets' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'array' - items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{ - id: 'pet' - title: 'Pet' - typ: 'object' - properties: { - 'id': jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'tag': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: [ - 'id', - 'name', - ] - })) - }) - } - errors: [ - openrpc.ErrorSpec{ - code: 400 - message: 'Invalid request' - }, - ] - }, - specification.ActorMethod{ - name: 'createPet' - summary: 'Create a new pet' - example: openrpc.ExamplePairing{ - result: openrpc.ExampleRef(openrpc.Example{ - name: 'Example response' - value: '[]' - }) - } - result: openrpc.ContentDescriptor{ - name: 'result' - description: 'The response of the operation.' - required: true - } - errors: [ - openrpc.ErrorSpec{ - code: 400 - message: 'Invalid input' - }, - ] - }, - specification.ActorMethod{ - name: 'getPet' - summary: 'Get a pet by ID' - example: openrpc.ExamplePairing{ - params: [ - openrpc.ExampleRef(openrpc.Example{ - name: 'Example petId' - description: 'Example ID of the pet to retrieve' - value: 1 - }), - ] - result: openrpc.ExampleRef(openrpc.Example{ - name: 'Example response' - value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')! - }) - } - parameters: [ - openrpc.ContentDescriptor{ - name: 'petId' - summary: 'ID of the pet to retrieve' - description: 'ID of the pet to retrieve' - required: true - schema: jsonschema.SchemaRef(jsonschema.Schema{ - ...jsonschema.schema_u32 - format: 'uint32' - example: 1 - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'result' - description: 'The response of the operation.' - required: true - schema: jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/Pet' - }) - } - errors: [ - openrpc.ErrorSpec{ - code: 404 - message: 'Pet not found' - }, - ] - }, - specification.ActorMethod{ - name: 'deletePet' - summary: 'Delete a pet by ID' - example: openrpc.ExamplePairing{ - params: [ - openrpc.ExampleRef(openrpc.Example{ - name: 'Example petId' - description: 'Example ID of the pet to delete' - value: 1 - }), - ] - } - parameters: [ - openrpc.ContentDescriptor{ - name: 'petId' - summary: 'ID of the pet to delete' - description: 'ID of the pet to delete' - required: true - schema: jsonschema.SchemaRef(jsonschema.Schema{ - ...jsonschema.schema_u32 - example: 1 - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'result' - description: 'The response of the operation.' - required: true - } - errors: [ - openrpc.ErrorSpec{ - code: 404 - message: 'Pet not found' - }, - ] - }, - ] - objects: [ - specification.BaseObject{ - schema: jsonschema.Schema{ - title: 'Pet' - typ: 'object' - properties: { - 'id': jsonschema.schema_u32 - 'name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'tag': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['id', 'name'] - } - }, - ] -} - -fn test_typescript_client_folder() { - client := typescript_client_folder(specification) -} diff --git a/libarchive/baobab/generator/_archive/generate_objects.v b/libarchive/baobab/generator/_archive/generate_objects.v deleted file mode 100644 index 2337e217..00000000 --- a/libarchive/baobab/generator/_archive/generate_objects.v +++ /dev/null @@ -1,47 +0,0 @@ -module generator - -// pub fn generate_object_code(actor Struct, object BaseObject) VFile { -// obj_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// mut items := []CodeItem{} -// items = [generate_new_method(actor, object), generate_get_method(actor, object), -// generate_set_method(actor, object), generate_delete_method(actor, object), -// generate_list_result_struct(actor, object), generate_list_method(actor, object)] - -// items << generate_object_methods(actor, object) -// mut file := code.new_file( -// mod: texttools.name_fix(actor.name) -// name: obj_name -// imports: [ -// Import{ -// mod: object.structure.mod -// types: [object_type] -// }, -// Import{ -// mod: 'incubaid.herolib.baobab.backend' -// types: ['FilterParams'] -// }, -// ] -// items: items -// ) - -// if object.structure.fields.any(it.attrs.any(it.name == 'index')) { -// // can't filter without indices -// filter_params := generate_filter_params(actor, object) -// file.items << filter_params.map(CodeItem(it)) -// file.items << generate_filter_method(actor, object) -// } - -// return file -// } - -// pub fn (a Actor) generate_model_files() ![]VFile { -// structs := a.objects.map(it.structure) -// return a.objects.map(code.new_file( -// mod: texttools.name_fix(a.name) -// name: '${texttools.name_fix(it.structure.name)}_model' -// // imports: [Import{mod:'incubaid.herolib.baobab.stage'}] -// items: [it.structure] -// )) -// } diff --git a/libarchive/baobab/generator/_archive/write_object_methods.v b/libarchive/baobab/generator/_archive/write_object_methods.v deleted file mode 100644 index 8885fd32..00000000 --- a/libarchive/baobab/generator/_archive/write_object_methods.v +++ /dev/null @@ -1,406 +0,0 @@ -module generator - -import incubaid.herolib.baobab.specification -import incubaid.herolib.develop.codetools as code { Param, Param, type_from_symbol } -import incubaid.herolib.core.texttools - -const id_param = Param{ - name: 'id' - typ: type_from_symbol('u32') -} - -// pub fn generate_object_code(actor Struct, object BaseObject) VFile { -// obj_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// mut items := []CodeItem{} -// items = [generate_new_method(actor, object), generate_get_method(actor, object), -// generate_set_method(actor, object), generate_delete_method(actor, object), -// generate_list_result_struct(actor, object), generate_list_method(actor, object)] - -// items << generate_object_methods(actor, object) -// mut file := code.new_file( -// mod: texttools.name_fix(actor.name) -// name: obj_name -// imports: [ -// Import{ -// mod: object.structure.mod -// types: [object_type] -// }, -// Import{ -// mod: 'incubaid.herolib.baobab.backend' -// types: ['FilterParams'] -// }, -// ] -// items: items -// ) - -// if object.structure.fields.any(it.attrs.any(it.name == 'index')) { -// // can't filter without indices -// filter_params := generate_filter_params(actor, object) -// file.items << filter_params.map(CodeItem(it)) -// file.items << generate_filter_method(actor, object) -// } - -// return file -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// fn generate_get_method(actor Struct, object BaseObject) Function { -// object_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// get_method := Function{ -// name: 'get_${object_name}' -// description: 'gets the ${object_name} with the given object id' -// receiver: Param{ -// mutable: true -// name: 'actor' -// typ: type_from_symbol(actor.name) -// } -// } -// params: [generator.id_param] -// result: Param{ -// typ: type_from_symbol(object.structure.name) -// is_result: true -// } -// body: 'return actor.backend.get[${object_type}](id)!' -// } -// return get_method -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// fn generate_set_method(actor Struct, object BaseObject) Function { -// object_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// param_getters := generate_param_getters( -// structure: object.structure -// prefix: '' -// only_mutable: true -// ) -// body := 'actor.backend.set[${object_type}](${object_name})!' -// get_method := Function{ -// name: 'set_${object_name}' -// description: 'updates the ${object.structure.name} with the given object id' -// receiver: Param{ -// mutable: true -// name: 'actor' -// typ: type_from_symbol(actor.name) -// } -// } -// params: [ -// Param{ -// name: object_name -// typ: Type{ -// symbol: object_type -// } -// }, -// ] -// result: Param{ -// is_result: true -// } -// body: body -// } -// return get_method -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// fn generate_delete_method(actor Struct, object BaseObject) Function { -// object_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// body := 'actor.backend.delete[${object_type}](id)!' -// get_method := Function{ -// name: 'delete_${object_name}' -// description: 'deletes the ${object.structure.name} with the given object id' -// receiver: Param{ -// mutable: true -// name: 'actor' -// typ: Type{ -// symbol: actor.name -// } -// } -// params: [generator.id_param] -// result: Param{ -// is_result: true -// } -// body: body -// } -// return get_method -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// fn generate_new_method(actor Struct, object BaseObject) Function { -// object_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// param_getters := generate_param_getters( -// structure: object.structure -// prefix: '' -// only_mutable: false -// ) -// body := 'return actor.backend.new[${object_type}](${object_name})!' -// new_method := Function{ -// name: 'new_${object_name}' -// description: 'news the ${object.structure.name} with the given object id' -// receiver: Param{ -// name: 'actor' -// typ: Type{ -// symbol: actor.name -// } -// mutable: true -// } -// params: [ -// Param{ -// name: object_name -// typ: Type{ -// symbol: object_type -// } -// }, -// ] -// result: Param{ -// is_result: true -// typ: Type{ -// symbol: 'u32' -// } -// } -// body: body -// } -// return new_method -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// fn generate_list_result_struct(actor Struct, object BaseObject) Struct { -// object_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name -// return Struct{ -// name: '${object_type}List' -// is_pub: true -// fields: [ -// StructField{ -// name: 'items' -// typ: Type{ -// symbol: '[]${object_type}' -// } -// }, -// ] -// } -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// fn generate_list_method(actor Struct, object BaseObject) Function { -// object_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// list_struct := Struct{ -// name: '${object_type}List' -// fields: [ -// StructField{ -// name: 'items' -// typ: Type{ -// symbol: '[]${object_type}' -// } -// }, -// ] -// } - -// param_getters := generate_param_getters( -// structure: object.structure -// prefix: '' -// only_mutable: false -// ) -// body := 'return ${object_type}List{items:actor.backend.list[${object_type}]()!}' - -// result_struct := generate_list_result_struct(actor, object) -// mut result := Param{} -// result.typ.symbol = result_struct.name -// result.is_result = true -// new_method := Function{ -// name: 'list_${object_name}' -// description: 'lists all of the ${object_name} objects' -// receiver: Param{ -// name: 'actor' -// typ: Type{ -// symbol: actor.name -// } -// mutable: true -// } -// params: [] -// result: result -// body: body -// } -// return new_method -// } - -// fn generate_filter_params(actor Struct, object BaseObject) []Struct { -// object_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// return [ -// Struct{ -// name: 'Filter${object_type}Params' -// fields: [ -// StructField{ -// name: 'filter' -// typ: Type{ -// symbol: '${object_type}Filter' -// } -// }, -// StructField{ -// name: 'params' -// typ: Type{ -// symbol: 'FilterParams' -// } -// }, -// ] -// }, -// Struct{ -// name: '${object_type}Filter' -// fields: object.structure.fields.filter(it.attrs.any(it.name == 'index')) -// }, -// ] -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// fn generate_filter_method(actor Struct, object BaseObject) Function { -// object_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// param_getters := generate_param_getters( -// structure: object.structure -// prefix: '' -// only_mutable: false -// ) -// params_type := 'Filter${object_type}Params' -// body := 'return actor.backend.filter[${object_type}, ${object_type}Filter](filter.filter, filter.params)!' -// return Function{ -// name: 'filter_${object_name}' -// description: 'lists all of the ${object_name} objects' -// receiver: Param{ -// name: 'actor' -// typ: Type{ -// symbol: actor.name -// } -// mutable: true -// } -// params: [ -// Param{ -// name: 'filter' -// typ: Type{ -// symbol: params_type -// } -// }, -// ] -// result: Param{ -// typ: Type{ -// symbol: '[]${object_type}' -// } -// is_result: true -// } -// body: body -// } -// } - -// // // generate_object_methods generates CRUD actor methods for a provided structure -// // fn generate_object_methods(actor Struct, object BaseObject) []Function { -// // object_name := texttools.snake_case(object.structure.name) -// // object_type := object.structure.name - -// // mut funcs := []Function{} -// // for method in object.methods { -// // mut params := [Param{ -// // name: 'id' -// // typ: Type{ -// // symbol: 'u32' -// // } -// // }] -// // params << method.params -// // funcs << Function{ -// // name: method.name -// // description: method.description -// // receiver: Param{ -// // name: 'actor' -// // typ: Type{ -// // symbol: actor.name -// // } -// // mutable: true -// // } -// // params: params -// // result: method.result -// // body: 'obj := actor.backend.get[${method.receiver.typ.symbol}](id)! -// // obj.${method.name}(${method.params.map(it.name).join(',')}) -// // actor.backend.set[${method.receiver.typ.symbol}](obj)! -// // ' -// // } -// // } - -// // return funcs -// // } - -// @[params] -// struct GenerateParamGetters { -// structure Struct -// prefix string -// only_mutable bool // if true generates param.get methods for only mutable struct fields. Used for updating. -// } - -// fn generate_param_getters(params GenerateParamGetters) []string { -// mut param_getters := []string{} -// fields := if params.only_mutable { -// params.structure.fields.filter(it.is_mut && it.is_pub) -// } else { -// params.structure.fields.filter(it.is_pub) -// } -// for field in fields { -// if field.typ.symbol.starts_with_capital() { -// subgetters := generate_param_getters(GenerateParamGetters{ -// ...params -// structure: field.structure -// prefix: '${field.name}_' -// }) -// // name of the tested object, used for param declaration -// // ex: fruits []Fruit becomes fruit_name -// nested_name := field.structure.name.to_lower() -// if field.typ.is_map { -// param_getters.insert(0, '${nested_name}_key := params.get(\'${nested_name}_key\')!') -// param_getters << '${field.name}: {${nested_name}_key: ${field.structure.name}}{' -// } else if field.typ.is_array { -// param_getters << '${field.name}: [${field.structure.name}{' -// } else { -// param_getters << '${field.name}: ${field.structure.name}{' -// } -// param_getters << subgetters -// param_getters << if field.typ.is_array { '}]' } else { '}' } -// continue -// } - -// mut get_method := '${field.name}: params.get' -// if field.typ.symbol != 'string' { -// // TODO: check if params method actually exists -// 'get_${field.typ.symbol}' -// } - -// if field.default != '' { -// get_method += '_default' -// } - -// get_method = get_method + "('${params.prefix}${field.name}')!" -// param_getters << get_method -// } -// return param_getters -// } - -// @[params] -// struct GetChildField { -// parent Struct @[required] -// child Struct @[required] -// } - -// fn get_child_field(params GetChildField) StructField { -// fields := params.parent.fields.filter(it.typ.symbol == 'map[string]&${params.child.name}') -// if fields.len != 1 { -// panic('this should never happen') -// } -// return fields[0] -// } diff --git a/libarchive/baobab/generator/_archive/write_object_tests.v b/libarchive/baobab/generator/_archive/write_object_tests.v deleted file mode 100644 index 451090ff..00000000 --- a/libarchive/baobab/generator/_archive/write_object_tests.v +++ /dev/null @@ -1,168 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.baobab.specification -import rand -import incubaid.herolib.core.texttools - -// // generate_object_methods generates CRUD actor methods for a provided structure -// pub fn generate_object_test_code(actor Struct, object BaseObject) !VFile { -// consts := CustomCode{"const db_dir = '\${os.home_dir()}/hero/db' -// const actor_name = '${actor.name}_test_actor'"} - -// clean_code := 'mut actor := get(name: actor_name)!\nactor.backend.reset()!' - -// testsuite_begin := Function{ -// name: 'testsuite_begin' -// body: clean_code -// } - -// testsuite_end := Function{ -// name: 'testsuite_end' -// body: clean_code -// } - -// actor_name := texttools.name_fix(actor.name) -// object_name := texttools.snake_case(object.schema.name) -// object_type := object.structure.name -// // TODO: support modules outside of crystal - -// mut file := VFile{ -// name: '${object_name}_test' -// mod: texttools.name_fix(actor_name) -// imports: [ -// Import{ -// mod: 'os' -// }, -// Import{ -// mod: '${object.structure.mod}' -// types: [object_type] -// }, -// ] -// items: [ -// consts, -// testsuite_begin, -// testsuite_end, -// generate_new_method_test(actor, object)!, -// generate_get_method_test(actor, object)!, -// ] -// } - -// if object.structure.fields.any(it.attrs.any(it.name == 'index')) { -// // can't filter without indices -// file.items << generate_filter_test(actor, object)! -// } - -// return file -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// fn generate_new_method_test(actor Struct, object BaseObject) !Function { -// object_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// required_fields := object.structure.fields.filter(it.attrs.any(it.name == 'required')) -// mut fields := []string{} -// for field in required_fields { -// mut field_decl := '${field.name}: ${get_mock_value(field.typ.symbol())!}' -// fields << field_decl -// } - -// body := 'mut actor := get(name: actor_name)! -// mut ${object_name}_id := actor.new_${object_name}(${object_type}{${fields.join(',')}})! -// assert ${object_name}_id == 1 - -// ${object_name}_id = actor.new_${object_name}(${object_type}{${fields.join(',')}})! -// assert ${object_name}_id == 2' -// return Function{ -// name: 'test_new_${object_name}' -// description: 'news the ${object_type} with the given object id' -// result: code.Param{ -// is_result: true -// } -// body: body -// } -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// fn generate_get_method_test(actor Struct, object BaseObject) !Function { -// object_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// required_fields := object.structure.fields.filter(it.attrs.any(it.name == 'required')) -// mut fields := []string{} -// for field in required_fields { -// mut field_decl := '${field.name}: ${get_mock_value(field.typ.symbol())!}' -// fields << field_decl -// } - -// body := 'mut actor := get(name: actor_name)! -// mut ${object_name} := ${object_type}{${fields.join(',')}} -// ${object_name}.id = actor.new_${object_name}(${object_name})! -// assert ${object_name} == actor.get_${object_name}(${object_name}.id)!' -// return Function{ -// name: 'test_get_${object_name}' -// description: 'news the ${object_type} with the given object id' -// result: code.Param{ -// is_result: true -// } -// body: body -// } -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// fn generate_filter_test(actor Struct, object BaseObject) !Function { -// object_name := texttools.snake_case(object.structure.name) -// object_type := object.structure.name - -// index_fields := object.structure.fields.filter(it.attrs.any(it.name == 'index')) -// if index_fields.len == 0 { -// return error('Cannot generate filter method test for object without any index fields') -// } - -// mut index_tests := []string{} -// for i, field in index_fields { -// val := get_mock_value(field.typ.symbol())! -// index_field := '${field.name}: ${val}' // index field assignment line -// mut fields := [index_field] -// fields << get_required_fields(object.structure)! -// index_tests << '${object_name}_id${i} := actor.new_${object_name}(${object_type}{${fields.join(',')}})! -// ${object_name}_list${i} := actor.filter_${object_name}( -// filter: ${object_type}Filter{${index_field}} -// )! -// assert ${object_name}_list${i}.len == 1 -// assert ${object_name}_list${i}[0].${field.name} == ${val} -// ' -// } - -// body := 'mut actor := get(name: actor_name)! -// \n${index_tests.join('\n\n')}' - -// return Function{ -// name: 'test_filter_${object_name}' -// description: 'news the ${object_type} with the given object id' -// result: code.Param{ -// is_result: true -// } -// body: body -// } -// } - -// fn get_required_fields(s Struct) ![]string { -// required_fields := s.fields.filter(it.attrs.any(it.name == 'required')) -// mut fields := []string{} -// for field in required_fields { -// fields << '${field.name}: ${get_mock_value(field.typ.symbol())!}' -// } -// return fields -// } - -// fn get_mock_value(typ string) !string { -// if typ == 'string' { -// return "'mock_string_${rand.string(3)}'" -// } else if typ == 'int' || typ == 'u32' { -// return '42' -// } else { -// return error('mock values for types other than strings and numbers are not yet supported') -// } -// } diff --git a/libarchive/baobab/generator/generate_act.v b/libarchive/baobab/generator/generate_act.v deleted file mode 100644 index 42df31d4..00000000 --- a/libarchive/baobab/generator/generate_act.v +++ /dev/null @@ -1,179 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code { CodeItem, CustomCode, Function, Import, Object, Param, Result, VFile } -import incubaid.herolib.core.texttools -import incubaid.herolib.schemas.openrpc { ContentDescriptor, Example } -import incubaid.herolib.schemas.jsonschema.codegen { schemaref_to_type } -import incubaid.herolib.baobab.specification { ActorMethod, ActorSpecification } - -fn generate_handle_file(spec ActorSpecification) !VFile { - mut items := []CodeItem{} - items << CustomCode{generate_handle_function(spec)} - for method in spec.methods { - items << generate_method_handle(spec.name, method)! - } - return VFile{ - name: 'act' - imports: [ - Import{ - mod: 'incubaid.herolib.baobab.stage' - types: ['Action'] - }, - Import{ - mod: 'incubaid.herolib.core.texttools' - }, - Import{ - mod: 'x.json2 as json' - }, - ] - items: items - } -} - -pub fn generate_handle_function(spec ActorSpecification) string { - actor_name_pascal := texttools.pascal_case(spec.name) - mut operation_handlers := []string{} - mut routes := []string{} - - // Iterate over OpenAPI paths and operations - for method in spec.methods { - operation_id := method.name - params := method.parameters.map(it.name).join(', ') - - // Generate route case - route := generate_route_case(operation_id, 'handle_${operation_id}') - routes << route - } - - // Combine the generated handlers and main router into a single file - return [ - '// AUTO-GENERATED FILE - DO NOT EDIT MANUALLY', - '', - 'pub fn (mut actor ${actor_name_pascal}Actor) act(action Action) !Action {', - ' return match texttools.snake_case(action.name) {', - routes.join('\n'), - ' else {', - ' return error("Unknown operation: \${action.name}")', - ' }', - ' }', - '}', - ].join('\n') -} - -pub fn generate_method_handle(actor_name string, method ActorMethod) !Function { - actor_name_pascal := texttools.pascal_case(actor_name) - name_fixed := texttools.snake_case(method.name) - mut body := '' - if method.parameters.len == 1 { - param := method.parameters[0] - param_name := texttools.snake_case(param.name) - decode_stmt := generate_decode_stmt('action.params', param)! - body += '${param_name} := ${decode_stmt}\n' - } - if method.parameters.len > 1 { - body += 'params_arr := json.raw_decode(action.params)!.arr()\n' - for i, param in method.parameters { - param_name := texttools.snake_case(param.name) - decode_stmt := generate_decode_stmt('params_arr[${i}].str()', param)! - body += '${param_name} := ${decode_stmt}' - } - } - call_stmt := generate_call_stmt(actor_name, method)! - body += '${call_stmt}\n' - body += '${generate_return_stmt(method)!}\n' - return Function{ - name: 'handle_${name_fixed}' - description: '// Handler for ${name_fixed}\n' - receiver: Param{ - name: 'actor' - mutable: true - typ: Object{'${actor_name_pascal}Actor'} - } - params: [Param{ - name: 'action' - typ: Object{'Action'} - }] - result: Param{ - typ: Result{Object{'Action'}} - } - body: body - } -} - -fn method_is_void(method ActorMethod) !bool { - return schemaref_to_type(method.result.schema).vgen().trim_space() == '' -} - -pub fn generate_example_method_handle(actor_name string, method ActorMethod) !Function { - actor_name_pascal := texttools.pascal_case(actor_name) - name_fixed := texttools.snake_case(method.name) - body := if !method_is_void(method)! { - if method.example.result is Example { - 'return Action{...action, result: json.encode(\'${method.example.result.value}\')}' - } else { - 'return action' - } - } else { - 'return action' - } - return Function{ - name: 'handle_${name_fixed}_example' - description: '// Handler for ${name_fixed}\n' - receiver: Param{ - name: 'actor' - mutable: true - typ: Object{'${actor_name_pascal}Actor'} - } - params: [Param{ - name: 'action' - typ: Object{'Action'} - }] - result: Param{ - typ: Result{Object{'Action'}} - } - body: body - } -} - -fn generate_call_stmt(name string, method ActorMethod) !string { - mut call_stmt := if schemaref_to_type(method.result.schema).vgen().trim_space() != '' { - '${texttools.snake_case(method.result.name)} := ' - } else { - '' - } - method_name := texttools.snake_case(method.name) - snake_name := texttools.snake_case(name) - - param_names := method.parameters.map(texttools.snake_case(it.name)) - call_stmt += 'actor.${snake_name}.${method_name}(${param_names.join(', ')})!' - return call_stmt -} - -fn generate_return_stmt(method ActorMethod) !string { - if schemaref_to_type(method.result.schema).vgen().trim_space() != '' { - return 'return Action{...action, result: json.encode(${texttools.snake_case(method.result.name)})}' - } - return 'return action' -} - -// Helper function to generate a case block for the main router -fn generate_route_case(case string, handler_name string) string { - name_fixed := texttools.snake_case(handler_name) - return "'${texttools.snake_case(case)}' {actor.${name_fixed}(action)}" -} - -// generates decode statement for variable with given name -fn generate_decode_stmt(name string, param ContentDescriptor) !string { - param_type := schemaref_to_type(param.schema) - if param_type is Object { - return 'json.decode[${schemaref_to_type(param.schema).vgen()}](${name})!' - } else if param_type is code.Array { - return 'json.decode[${schemaref_to_type(param.schema).vgen()}](${name})' - } - param_symbol := param_type.vgen() - return if param_symbol == 'string' { - '${name}.str()' - } else { - '${name}.${param_type.vgen()}()' - } -} diff --git a/libarchive/baobab/generator/generate_actor_folder.v b/libarchive/baobab/generator/generate_actor_folder.v deleted file mode 100644 index 76cc2de8..00000000 --- a/libarchive/baobab/generator/generate_actor_folder.v +++ /dev/null @@ -1,82 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code { File, Folder, IFile, IFolder } -import incubaid.herolib.schemas.openapi -import incubaid.herolib.core.texttools -import incubaid.herolib.baobab.specification { ActorInterface, ActorSpecification } -import json - -@[params] -pub struct Params { -pub: - interfaces []ActorInterface // the interfaces to be supported -} - -pub fn generate_actor_folder(spec ActorSpecification, params Params) !Folder { - mut files := []IFile{} - mut folders := []IFolder{} - - files = [generate_readme_file(spec)!] - - mut docs_files := []IFile{} - mut spec_files := []IFile{} - - // generate code files for supported interfaces - for iface in params.interfaces { - match iface { - .openrpc { - // convert actor spec to openrpc spec - openrpc_spec := spec.to_openrpc() - spec_files << generate_openrpc_file(openrpc_spec)! - } - .openapi { - // convert actor spec to openrpc spec - openapi_spec_raw := spec.to_openapi() - spec_files << generate_openapi_file(openapi_spec_raw)! - - openapi_spec := openapi.process(openapi_spec_raw)! - folders << generate_openapi_ts_client(openapi_spec)! - } - else {} - } - } - - specs_folder := Folder{ - name: 'specs' - files: spec_files - } - - // folder with docs - folders << Folder{ - name: 'docs' - files: docs_files - folders: [specs_folder] - } - - folders << generate_scripts_folder(spec.name, false) - folders << generate_examples_folder()! - - // create module with code files and docs folder - name_fixed := texttools.snake_case(spec.name) - - return Folder{ - name: '${name_fixed}' - files: files - folders: folders - modules: [generate_actor_module(spec, params)!] - } -} - -fn generate_readme_file(spec ActorSpecification) !File { - return File{ - name: 'README' - extension: 'md' - content: '# ${spec.name}\n${spec.description}' - } -} - -pub fn generate_examples_folder() !Folder { - return Folder{ - name: 'examples' - } -} diff --git a/libarchive/baobab/generator/generate_actor_source.v b/libarchive/baobab/generator/generate_actor_source.v deleted file mode 100644 index d1ab8755..00000000 --- a/libarchive/baobab/generator/generate_actor_source.v +++ /dev/null @@ -1,118 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code { CustomCode, IFile, IFolder, Module, VFile } -import incubaid.herolib.schemas.openapi -import incubaid.herolib.core.texttools -import incubaid.herolib.baobab.specification { ActorInterface, ActorSpecification } -import json - -pub fn generate_module_from_openapi(openapi_path string) !string { - // the actor specification obtained from the OpenRPC Specification - openapi_spec := openapi.new(path: openapi_path)! - actor_spec := specification.from_openapi(openapi_spec)! - - actor_module := generate_actor_module(actor_spec, - interfaces: [.openapi, .http] - )! - - return actor_module.write_str()! -} - -pub fn generate_actor_module(spec ActorSpecification, params Params) !Module { - mut files := []IFile{} - mut folders := []IFolder{} - - files = [ - generate_actor_file(spec)!, - generate_actor_test_file(spec)!, - generate_specs_file(spec.name, params.interfaces)!, - generate_handle_file(spec)!, - generate_methods_file(spec)!, - generate_methods_interface_file(spec)!, - generate_methods_example_file(spec)!, - generate_client_file(spec)!, - generate_model_file(spec)!, - ] - - // generate code files for supported interfaces - for iface in params.interfaces { - match iface { - .openrpc { - // convert actor spec to openrpc spec - openrpc_spec := spec.to_openrpc() - iface_file, iface_test_file := generate_openrpc_interface_files(params.interfaces) - files << iface_file - files << iface_test_file - } - .openapi { - // convert actor spec to openrpc spec - openapi_spec_raw := spec.to_openapi() - openapi_spec := openapi.process(openapi_spec_raw)! - // generate openrpc code files - iface_file, iface_test_file := generate_openapi_interface_files(params.interfaces) - files << iface_file - files << iface_test_file - } - .http { - // interfaces that have http controllers - controllers := params.interfaces.filter(it == .openrpc || it == .openapi) - // generate openrpc code files - iface_file, iface_test_file := generate_http_interface_files(controllers) - files << iface_file - files << iface_test_file - } - .command { - files << generate_command_file(spec)! - } - else { - return error('unsupported interface ${iface}') - } - } - } - - // create module with code files and docs folder - name_fixed := texttools.snake_case(spec.name) - return code.new_module( - name: '${name_fixed}' - description: spec.description - files: files - folders: folders - in_src: true - ) -} - -fn generate_actor_file(spec ActorSpecification) !VFile { - dollar := '$' - version := spec.version - name_snake := texttools.snake_case(spec.name) - name_pascal := texttools.pascal_case(spec.name) - actor_code := $tmpl('./templates/actor.v.template') - return VFile{ - name: 'actor' - items: [CustomCode{actor_code}] - } -} - -fn generate_actor_test_file(spec ActorSpecification) !VFile { - dollar := '$' - actor_name_snake := texttools.snake_case(spec.name) - actor_name_pascal := texttools.pascal_case(spec.name) - actor_test_code := $tmpl('./templates/actor_test.v.template') - return VFile{ - name: 'actor_test' - items: [CustomCode{actor_test_code}] - } -} - -fn generate_specs_file(name string, interfaces []ActorInterface) !VFile { - support_openrpc := ActorInterface.openrpc in interfaces - support_openapi := ActorInterface.openapi in interfaces - dollar := '$' - actor_name_snake := texttools.snake_case(name) - actor_name_pascal := texttools.pascal_case(name) - actor_code := $tmpl('./templates/specifications.v.template') - return VFile{ - name: 'specifications' - items: [CustomCode{actor_code}] - } -} diff --git a/libarchive/baobab/generator/generate_actor_test.v b/libarchive/baobab/generator/generate_actor_test.v deleted file mode 100644 index 3cdfaa99..00000000 --- a/libarchive/baobab/generator/generate_actor_test.v +++ /dev/null @@ -1,276 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.baobab.specification -import incubaid.herolib.schemas.openrpc -import incubaid.herolib.schemas.jsonschema -import os -import x.json2 as json - -const actor_spec = specification.ActorSpecification{ - name: 'Pet Store' - description: 'A sample API for a pet store' - structure: code.Struct{} - interfaces: [.openapi] - methods: [ - specification.ActorMethod{ - name: 'listPets' - summary: 'List all pets' - example: openrpc.ExamplePairing{ - params: [ - openrpc.ExampleRef(openrpc.Example{ - name: 'Example limit' - description: 'Example Maximum number of pets to return' - value: 10 - }), - ] - result: openrpc.ExampleRef(openrpc.Example{ - name: 'Example response' - value: json.raw_decode('[ - {"id": 1, "name": "Fluffy", "tag": "dog"}, - {"id": 2, "name": "Whiskers", "tag": "cat"} - ]')! - }) - } - parameters: [ - openrpc.ContentDescriptor{ - name: 'limit' - summary: 'Maximum number of pets to return' - description: 'Maximum number of pets to return' - required: false - schema: jsonschema.SchemaRef(jsonschema.Schema{ - ...jsonschema.schema_u32 - example: 10 - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pets' - description: 'A paged array of pets' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'array' - items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{ - id: 'pet' - title: 'Pet' - typ: 'object' - properties: { - 'id': jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'tag': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: [ - 'id', - 'name', - ] - })) - }) - } - errors: [ - openrpc.ErrorSpec{ - code: 400 - message: 'Invalid request' - }, - ] - }, - specification.ActorMethod{ - name: 'newPet' - summary: 'Create a new pet' - parameters: [ - openrpc.ContentDescriptor{ - name: 'result' - description: 'The response of the operation.' - required: true - schema: jsonschema.SchemaRef(jsonschema.Schema{ - id: 'pet' - title: 'Pet' - typ: 'object' - properties: { - 'id': jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'tag': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['id', 'name'] - }) - }, - ] - example: openrpc.ExamplePairing{ - result: openrpc.ExampleRef(openrpc.Example{ - name: 'Example response' - value: '[]' - }) - } - result: openrpc.ContentDescriptor{ - name: 'petId' - summary: 'ID of the created pet' - description: 'ID of the created pet' - required: true - schema: jsonschema.SchemaRef(jsonschema.Schema{ - ...jsonschema.schema_u32 - example: 1 - }) - } - errors: [ - openrpc.ErrorSpec{ - code: 400 - message: 'Invalid input' - }, - ] - }, - specification.ActorMethod{ - name: 'getPet' - summary: 'Get a pet by ID' - example: openrpc.ExamplePairing{ - params: [ - openrpc.ExampleRef(openrpc.Example{ - name: 'Example petId' - description: 'Example ID of the pet to retrieve' - value: 1 - }), - ] - result: openrpc.ExampleRef(openrpc.Example{ - name: 'Example response' - value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')! - }) - } - parameters: [ - openrpc.ContentDescriptor{ - name: 'petId' - summary: 'ID of the pet to retrieve' - description: 'ID of the pet to retrieve' - required: true - schema: jsonschema.SchemaRef(jsonschema.Schema{ - ...jsonschema.schema_u32 - format: 'uint32' - example: 1 - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'result' - description: 'The response of the operation.' - required: true - schema: jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/Pet' - }) - } - errors: [ - openrpc.ErrorSpec{ - code: 404 - message: 'Pet not found' - }, - ] - }, - specification.ActorMethod{ - name: 'deletePet' - summary: 'Delete a pet by ID' - example: openrpc.ExamplePairing{ - params: [ - openrpc.ExampleRef(openrpc.Example{ - name: 'Example petId' - description: 'Example ID of the pet to delete' - value: 1 - }), - ] - } - parameters: [ - openrpc.ContentDescriptor{ - name: 'petId' - summary: 'ID of the pet to delete' - description: 'ID of the pet to delete' - required: true - schema: jsonschema.SchemaRef(jsonschema.Schema{ - ...jsonschema.schema_u32 - example: 1 - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'result' - description: 'The response of the operation.' - required: true - } - errors: [ - openrpc.ErrorSpec{ - code: 404 - message: 'Pet not found' - }, - ] - }, - ] - objects: [ - specification.BaseObject{ - schema: jsonschema.Schema{ - title: 'Pet' - typ: 'object' - properties: { - 'id': jsonschema.schema_u32 - 'name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'tag': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['id', 'name'] - } - }, - ] -} - -const destination = '${os.dir(@FILE)}/testdata' - -fn test_generate_plain_actor_module() { - // plain actor module without interfaces - actor_module := generate_actor_module(actor_spec)! - actor_module.write(destination, - format: true - overwrite: true - test: true - )! -} - -fn test_generate_actor_module_with_openrpc_interface() { - // plain actor module without interfaces - actor_module := generate_actor_module(actor_spec, interfaces: [.openrpc])! - actor_module.write(destination, - format: true - overwrite: true - test: true - )! -} - -fn test_generate_actor_module_with_openapi_interface() { - // plain actor module without interfaces - actor_module := generate_actor_module(actor_spec, - interfaces: [.openapi] - )! - actor_module.write(destination, - format: true - overwrite: true - test: true - )! -} - -fn test_generate_actor_module_with_all_interfaces() { - // plain actor module without interfaces - actor_module := generate_actor_module(actor_spec, - interfaces: [.openapi, .openrpc, .http] - )! - actor_module.write(destination, - format: true - overwrite: true - test: true - )! -} diff --git a/libarchive/baobab/generator/generate_clients.v b/libarchive/baobab/generator/generate_clients.v deleted file mode 100644 index 55018050..00000000 --- a/libarchive/baobab/generator/generate_clients.v +++ /dev/null @@ -1,136 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code { CodeItem, CustomCode, Function, Import, Param, Result, VFile } -import incubaid.herolib.core.texttools -import incubaid.herolib.schemas.jsonschema.codegen as jsonschema_codegen { schemaref_to_type } -import incubaid.herolib.schemas.openrpc.codegen { content_descriptor_to_parameter } -import incubaid.herolib.baobab.specification { ActorMethod, ActorSpecification } - -pub fn generate_client_file(spec ActorSpecification) !VFile { - actor_name_snake := texttools.snake_case(spec.name) - actor_name_pascal := texttools.pascal_case(spec.name) - - mut items := []CodeItem{} - - items << CustomCode{' - pub struct Client { - stage.Client - } - - fn new_client(config stage.ActorConfig) !Client { - return Client { - Client: stage.new_client(config)! - } - }'} - - for method in spec.methods { - items << generate_client_method(method)! - } - - return VFile{ - imports: [ - Import{ - mod: 'incubaid.herolib.baobab.stage' - }, - Import{ - mod: 'incubaid.herolib.core.redisclient' - }, - Import{ - mod: 'x.json2 as json' - types: ['Any'] - }, - ] - name: 'client_actor' - items: items - } -} - -pub fn generate_example_client_file(spec ActorSpecification) !VFile { - actor_name_snake := texttools.snake_case(spec.name) - actor_name_pascal := texttools.pascal_case(spec.name) - - mut items := []CodeItem{} - - items << CustomCode{" - pub struct Client { - stage.Client - } - - fn new_client() !Client { - mut redis := redisclient.new('localhost:6379')! - mut rpc_q := redis.rpc_get('actor_example_\${name}') - return Client{ - rpc: rpc_q - } - }"} - - for method in spec.methods { - items << generate_client_method(method)! - } - - return VFile{ - imports: [ - Import{ - mod: 'incubaid.herolib.baobab.stage' - }, - Import{ - mod: 'incubaid.herolib.core.redisclient' - }, - Import{ - mod: 'x.json2 as json' - types: ['Any'] - }, - ] - name: 'client' - items: items - } -} - -pub fn generate_client_method(method ActorMethod) !Function { - name_fixed := texttools.snake_case(method.name) - - call_params := if method.parameters.len > 0 { - method.parameters.map(texttools.snake_case(it.name)).map('Any(${it}.str())').join(', ') - } else { - '' - } - - params_stmt := if method.parameters.len == 0 { - '' - } else if method.parameters.len == 1 { - 'params := json.encode(${texttools.snake_case(method.parameters[0].name)})' - } else { - 'mut params_arr := []Any{} - params_arr = [${call_params}] - params := json.encode(params_arr.str()) - ' - } - - mut client_call_stmt := "action := client.call_to_action( - name: '${name_fixed}'" - - if params_stmt != '' { - client_call_stmt += 'params: params' - } - client_call_stmt += ')!' - - result_type := schemaref_to_type(method.result.schema).vgen().trim_space() - result_stmt := if result_type == '' { - '' - } else { - 'return json.decode[${result_type}](action.result)!' - } - result_param := content_descriptor_to_parameter(method.result)! - return Function{ - receiver: code.new_param(v: 'mut client Client')! - result: Param{ - ...result_param - typ: Result{result_param.typ} - } - name: name_fixed - body: '${params_stmt}\n${client_call_stmt}\n${result_stmt}' - summary: method.summary - description: method.description - params: method.parameters.map(content_descriptor_to_parameter(it)!) - } -} diff --git a/libarchive/baobab/generator/generate_command.v b/libarchive/baobab/generator/generate_command.v deleted file mode 100644 index 7e0a7ea6..00000000 --- a/libarchive/baobab/generator/generate_command.v +++ /dev/null @@ -1,78 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code { CodeItem, CustomCode, Import, VFile } -import incubaid.herolib.core.texttools -import incubaid.herolib.baobab.specification { ActorMethod, ActorSpecification } - -pub fn generate_command_file(spec ActorSpecification) !VFile { - mut items := []CodeItem{} - items << CustomCode{generate_cmd_function(spec)} - for i in spec.methods { - items << CustomCode{generate_method_cmd_function(spec.name, i)} - } - return VFile{ - name: 'command' - imports: [ - Import{ - mod: 'incubaid.herolib.ui.console' - }, - Import{ - mod: 'cli' - types: ['Command', 'Flag'] - }, - ] - items: items - } -} - -pub fn generate_cmd_function(spec ActorSpecification) string { - actor_name_snake := texttools.snake_case(spec.name) - mut cmd_function := " - pub fn cmd() Command { - mut cmd := Command{ - name: '${actor_name_snake}' - usage: '' - description: '${spec.description}' - } - " - - mut method_cmds := []string{} - for method in spec.methods { - method_cmds << generate_method_cmd(method) - } - - cmd_function += '${method_cmds.join_lines()}}' - - return cmd_function -} - -pub fn generate_method_cmd(method ActorMethod) string { - method_name_snake := texttools.snake_case(method.name) - return " - mut cmd_${method_name_snake} := Command{ - sort_flags: true - name: '${method_name_snake}' - execute: cmd_${method_name_snake}_execute - description: '${method.description}' - } - " -} - -pub fn generate_method_cmd_function(actor_name string, method ActorMethod) string { - mut operation_handlers := []string{} - mut routes := []string{} - - actor_name_snake := texttools.snake_case(actor_name) - method_name_snake := texttools.snake_case(method.name) - - method_call := if method.result.name == '' { - '${actor_name_snake}.${method_name_snake}()!' - } else { - 'result := ${actor_name_snake}.${method_name_snake}()!' - } - return ' - fn cmd_${method_name_snake}_execute(cmd Command) ! { - ${method_call} - } - ' -} diff --git a/libarchive/baobab/generator/generate_interface.v b/libarchive/baobab/generator/generate_interface.v deleted file mode 100644 index c01991cf..00000000 --- a/libarchive/baobab/generator/generate_interface.v +++ /dev/null @@ -1,48 +0,0 @@ -module generator - -import incubaid.herolib.baobab.specification { ActorInterface } -import incubaid.herolib.develop.codetools as code { CustomCode, VFile } - -fn generate_openrpc_interface_files(interfaces []ActorInterface) (VFile, VFile) { - http := ActorInterface.http in interfaces - - iface_file := VFile{ - name: 'interface_openrpc' - items: [CustomCode{$tmpl('./templates/interface_openrpc.v.template')}] - } - iface_test_file := VFile{ - name: 'interface_openrpc_test' - items: [CustomCode{$tmpl('./templates/interface_openrpc_test.v.template')}] - } - return iface_file, iface_test_file -} - -fn generate_openapi_interface_files(interfaces []ActorInterface) (VFile, VFile) { - http := ActorInterface.http in interfaces - dollar := '$' - iface_file := VFile{ - name: 'interface_openapi' - items: [CustomCode{$tmpl('./templates/interface_openapi.v.template')}] - } - iface_test_file := VFile{ - name: 'interface_openapi_test' - items: [CustomCode{$tmpl('./templates/interface_openapi_test.v.template')}] - } - return iface_file, iface_test_file -} - -fn generate_http_interface_files(controllers []ActorInterface) (VFile, VFile) { - dollar := '$' - openapi := ActorInterface.openapi in controllers - openrpc := ActorInterface.openrpc in controllers - - iface_file := VFile{ - name: 'interface_http' - items: [CustomCode{$tmpl('./templates/interface_http.v.template')}] - } - iface_test_file := VFile{ - name: 'interface_http_test' - items: [CustomCode{$tmpl('./templates/interface_http_test.v.template')}] - } - return iface_file, iface_test_file -} diff --git a/libarchive/baobab/generator/generate_methods.v b/libarchive/baobab/generator/generate_methods.v deleted file mode 100644 index 9e7e42de..00000000 --- a/libarchive/baobab/generator/generate_methods.v +++ /dev/null @@ -1,165 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code { CodeItem, Function, Import, Param, Result, Struct, VFile } -import incubaid.herolib.core.texttools -import incubaid.herolib.schemas.openapi -import incubaid.herolib.schemas.openrpc -import incubaid.herolib.schemas.openrpc.codegen { content_descriptor_to_parameter, content_descriptor_to_struct } -import incubaid.herolib.schemas.jsonschema { Schema } -import incubaid.herolib.schemas.jsonschema.codegen as jsonschema_codegen -import incubaid.herolib.baobab.specification { ActorMethod, ActorSpecification } -import log - -const crud_prefixes = ['new', 'get', 'set', 'delete', 'list'] - -pub struct Source { - openapi_path ?string - openrpc_path ?string -} - -pub fn generate_methods_file_str(source Source) !string { - actor_spec := if path := source.openapi_path { - specification.from_openapi(openapi.new(path: path)!)! - } else if path := source.openrpc_path { - specification.from_openrpc(openrpc.new(path: path)!)! - } else { - panic('No openapi or openrpc path provided') - } - return generate_methods_file(actor_spec)!.write_str()! -} - -pub fn generate_methods_file(spec ActorSpecification) !VFile { - name_snake := texttools.snake_case(spec.name) - actor_name_pascal := texttools.pascal_case(spec.name) - - receiver := generate_methods_receiver(spec.name) - receiver_param := Param{ - mutable: true - name: name_snake[0].ascii_str() // receiver is first letter of domain - typ: Result{code.Object{receiver.name}} - } - - mut items := [CodeItem(receiver), CodeItem(generate_core_factory(receiver_param))] - for method in spec.methods { - items << generate_method_code(receiver_param, ActorMethod{ - ...method - category: spec.method_type(method) - })! - } - - return VFile{ - name: 'methods' - imports: [ - Import{ - mod: 'incubaid.herolib.baobab.osis' - types: ['OSIS'] - }, - ] - items: items - } -} - -fn generate_methods_receiver(name string) Struct { - return Struct{ - is_pub: true - name: '${texttools.pascal_case(name)}' - fields: [ - code.StructField{ - is_mut: true - name: 'osis' - typ: code.Object{'OSIS'} - }, - ] - } -} - -fn generate_core_factory(receiver Param) Function { - return Function{ - is_pub: true - name: 'new_${receiver.typ.symbol()}' - body: 'return ${receiver.typ.symbol().trim_left('!?')}{osis: osis.new()!}' - result: receiver - } -} - -// returns bodyless method prototype -pub fn generate_method_code(receiver Param, method ActorMethod) ![]CodeItem { - result_param := content_descriptor_to_parameter(method.result)! - - mut method_code := []CodeItem{} - // TODO: document assumption - obj_params := method.parameters.filter(if it.schema is Schema { - it.schema.typ == 'object' - } else { - false - }).map(content_descriptor_to_struct(it)) - if obj_param := obj_params[0] { - method_code << obj_param - } - - // check if method is a Base Object CRUD Method and - // if so generate the method's body - // TODO: smart generation of method body using AI - // body := match method.category { - // .base_object_new { base_object_new_body(receiver, method)! } - // .base_object_get { base_object_get_body(receiver, method)! } - // .base_object_set { base_object_set_body(receiver, method)! } - // .base_object_delete { base_object_delete_body(receiver, method)! } - // .base_object_list { base_object_list_body(receiver, method)! } - // else { "panic('implement')" } - // } - - body := "panic('implement')" - - fn_prototype := generate_method_prototype(receiver, method)! - method_code << Function{ - ...fn_prototype - body: body - } - return method_code -} - -// returns bodyless method prototype -pub fn generate_method_prototype(receiver Param, method ActorMethod) !Function { - result_param := content_descriptor_to_parameter(method.result)! - return Function{ - name: texttools.snake_case(method.name) - receiver: receiver - result: Param{ - ...result_param - typ: Result{result_param.typ} - } - summary: method.summary - description: method.description - params: method.parameters.map(content_descriptor_to_parameter(it)!) - } -} - -fn base_object_new_body(receiver Param, method ActorMethod) !string { - parameter := content_descriptor_to_parameter(method.parameters[0])! - return 'return ${receiver.name}.osis.new[${parameter.typ.vgen()}](${texttools.snake_case(parameter.name)})!' -} - -fn base_object_get_body(receiver Param, method ActorMethod) !string { - parameter := content_descriptor_to_parameter(method.parameters[0])! - result := content_descriptor_to_parameter(method.result)! - return 'return ${receiver.name}.osis.get[${result.typ.vgen()}](${texttools.snake_case(parameter.name)})!' -} - -fn base_object_set_body(receiver Param, method ActorMethod) !string { - parameter := content_descriptor_to_parameter(method.parameters[0])! - return 'return ${receiver.name}.osis.set[${parameter.typ.vgen()}](${parameter.name})!' -} - -fn base_object_delete_body(receiver Param, method ActorMethod) !string { - parameter := content_descriptor_to_parameter(method.parameters[0])! - return '${receiver.name}.osis.delete(${texttools.snake_case(parameter.name)})!' -} - -fn base_object_list_body(receiver Param, method ActorMethod) !string { - // result := content_descriptor_to_parameter(method.result)! - // log.error('result typ: ${result.typ}') - // base_object_type := (result.typ as Array).typ - // return 'return ${receiver.name}.osis.list[${base_object_type.symbol()}]()!' - return 'return' -} diff --git a/libarchive/baobab/generator/generate_methods_example.v b/libarchive/baobab/generator/generate_methods_example.v deleted file mode 100644 index 761a0869..00000000 --- a/libarchive/baobab/generator/generate_methods_example.v +++ /dev/null @@ -1,106 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code { CodeItem, Function, Import, Param, Result, Struct, VFile } -import incubaid.herolib.core.texttools -import incubaid.herolib.schemas.openrpc { Example } -import incubaid.herolib.schemas.jsonschema -import incubaid.herolib.schemas.jsonschema.codegen as jsonschema_codegen -import incubaid.herolib.schemas.openrpc.codegen { content_descriptor_to_parameter } -import incubaid.herolib.baobab.specification { ActorMethod, ActorSpecification } -import incubaid.herolib.schemas.openapi - -pub fn generate_methods_example_file_str(source Source) !string { - actor_spec := if path := source.openapi_path { - specification.from_openapi(openapi.new(path: path)!)! - } else if path := source.openrpc_path { - specification.from_openrpc(openrpc.new(path: path)!)! - } else { - panic('No openapi or openrpc path provided') - } - return generate_methods_example_file(actor_spec)!.write_str()! -} - -pub fn generate_methods_example_file(spec ActorSpecification) !VFile { - name_snake := texttools.snake_case(spec.name) - name_pascal := texttools.pascal_case(spec.name) - - receiver := generate_example_methods_receiver(spec.name) - receiver_param := Param{ - mutable: true - name: name_snake[0].ascii_str() - typ: Result{code.Object{receiver.name}} - } - mut items := [CodeItem(receiver), CodeItem(generate_core_example_factory(receiver_param))] - for method in spec.methods { - items << generate_method_example_code(receiver_param, ActorMethod{ - ...method - category: spec.method_type(method) - })! - } - - return VFile{ - name: 'methods_example' - imports: [ - Import{ - mod: 'incubaid.herolib.baobab.osis' - types: ['OSIS'] - }, - Import{ - mod: 'x.json2 as json' - }, - ] - items: items - } -} - -fn generate_core_example_factory(receiver Param) Function { - return Function{ - is_pub: true - name: 'new_${texttools.snake_case(receiver.typ.symbol())}' - body: 'return ${receiver.typ.symbol().trim_left('!?')}{OSIS: osis.new()!}' - result: receiver - } -} - -fn generate_example_methods_receiver(name string) Struct { - return Struct{ - is_pub: true - name: '${texttools.pascal_case(name)}Example' - embeds: [Struct{ - name: 'OSIS' - }] - } -} - -// returns bodyless method prototype -pub fn generate_method_example_code(receiver Param, method ActorMethod) ![]CodeItem { - result_param := content_descriptor_to_parameter(method.result)! - - mut method_code := []CodeItem{} - // TODO: document assumption - // obj_params := method.parameters.filter(if it.schema is Schema {it.schema.typ == 'object'} else {false}).map(schema_to_struct(it.schema as Schema)) - // if obj_param := obj_params[0] { - // method_code << Struct{...obj_param, name: method.name} - // } - - // check if method is a Base Object CRUD Method and - // if so generate the method's body - body := if !method_is_void(method)! { - if method.example.result is Example { - "json_str := '${method.example.result.value}' - return ${generate_decode_stmt('json_str', - method.result)!}" - } else { - 'return ${result_param.typ.empty_value()}' - } - } else { - '' - } - - fn_prototype := generate_method_prototype(receiver, method)! - method_code << Function{ - ...fn_prototype - body: body - } - return method_code -} diff --git a/libarchive/baobab/generator/generate_methods_interface.v b/libarchive/baobab/generator/generate_methods_interface.v deleted file mode 100644 index 8324e479..00000000 --- a/libarchive/baobab/generator/generate_methods_interface.v +++ /dev/null @@ -1,49 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code { CodeItem, Import, Param, VFile } -import incubaid.herolib.core.texttools -import incubaid.herolib.schemas.openrpc.codegen -import incubaid.herolib.baobab.specification { ActorSpecification } -import incubaid.herolib.schemas.openapi -import incubaid.herolib.schemas.openrpc - -pub fn generate_methods_interface_file_str(source Source) !string { - actor_spec := if path := source.openapi_path { - specification.from_openapi(openapi.new(path: path)!)! - } else if path := source.openrpc_path { - specification.from_openrpc(openrpc.new(path: path)!)! - } else { - panic('No openapi or openrpc path provided') - } - return generate_methods_interface_file(actor_spec)!.write_str()! -} - -pub fn generate_methods_interface_file(spec ActorSpecification) !VFile { - return VFile{ - name: 'methods_interface' - imports: [ - Import{ - mod: 'incubaid.herolib.baobab.osis' - types: ['OSIS'] - }, - ] - items: [CodeItem(generate_methods_interface_declaration(spec)!)] - } -} - -// returns bodyless method prototype -pub fn generate_methods_interface_declaration(spec ActorSpecification) !code.Interface { - name_snake := texttools.snake_case(spec.name) - name_pascal := texttools.pascal_case(spec.name) - receiver := generate_methods_receiver(spec.name) - receiver_param := Param{ - mutable: true - name: name_snake[0].ascii_str() - typ: code.Object{receiver.name} - } - return code.Interface{ - is_pub: true - name: 'I${name_pascal}' - methods: spec.methods.map(generate_method_prototype(receiver_param, it)!) - } -} diff --git a/libarchive/baobab/generator/generate_model.v b/libarchive/baobab/generator/generate_model.v deleted file mode 100644 index de6d8439..00000000 --- a/libarchive/baobab/generator/generate_model.v +++ /dev/null @@ -1,32 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code { CodeItem, Struct, VFile } -import incubaid.herolib.core.texttools -import incubaid.herolib.schemas.jsonschema.codegen { schema_to_struct } -import incubaid.herolib.baobab.specification { ActorSpecification } -import incubaid.herolib.schemas.openapi -import incubaid.herolib.schemas.openrpc - -pub fn generate_model_file_str(source Source) !string { - actor_spec := if path := source.openapi_path { - specification.from_openapi(openapi.new(path: path)!)! - } else if path := source.openrpc_path { - specification.from_openrpc(openrpc.new(path: path)!)! - } else { - panic('No openapi or openrpc path provided') - } - return generate_model_file(actor_spec)!.write_str()! -} - -pub fn generate_model_file(spec ActorSpecification) !VFile { - actor_name_snake := texttools.snake_case(spec.name) - actor_name_pascal := texttools.pascal_case(spec.name) - - return VFile{ - name: 'model' - items: spec.objects.map(CodeItem(Struct{ - ...schema_to_struct(it.schema) - is_pub: true - })) - } -} diff --git a/libarchive/baobab/generator/generate_openapi.v b/libarchive/baobab/generator/generate_openapi.v deleted file mode 100644 index cf5477b5..00000000 --- a/libarchive/baobab/generator/generate_openapi.v +++ /dev/null @@ -1,160 +0,0 @@ -module generator - -import json -import incubaid.herolib.develop.codetools as code { File, Folder } -import incubaid.herolib.schemas.openapi { OpenAPI, Operation } -import incubaid.herolib.schemas.openapi.codegen -import incubaid.herolib.schemas.jsonschema.codegen as jsonschema_codegen { schema_to_type } -import net.http - -pub fn generate_openapi_file(specification OpenAPI) !File { - openapi_json := specification.encode_json() - return File{ - name: 'openapi' - extension: 'json' - content: openapi_json - } -} - -pub fn generate_openapi_ts_client(specification OpenAPI) !Folder { - return codegen.ts_client_folder(specification, - body_generator: body_generator - custom_client_code: ' private restClient: HeroRestClient; - - constructor(heroKeysClient: any, debug: boolean = true) { - this.restClient = new HeroRestClient(heroKeysClient, debug); - } -' - )! -} - -fn body_generator(op Operation, path_ string, method http.Method) string { - path := path_.replace('{', '\${') - return match method { - .post { - if schema := op.payload_schema() { - symbol := schema_to_type(schema).typescript() - "return this.restClient.post<${symbol}>('${path}', data);" - } else { - '' - } - } - .get { - if schema := op.response_schema() { - // if op.params.len - symbol := schema_to_type(schema).typescript() - "return this.restClient.get<${symbol}>('${path}', data);" - } else { - '' - } - } - else { - '' - } - } - // return if operation_is_base_object_method(op) { - // bo_method := operation_to_base_object_method(op) - // match method_type(op) { - // .new { ts_client_new_body(op, path) } - // .get { ts_client_get_body(op, path) } - // .set { ts_client_set_body(op, path) } - // .delete { ts_client_delete_body(op, path) } - // .list { ts_client_list_body(op, path) } - // else {''} - // } - // } else {''} -} - -// pub fn operation_is_base_object_method(op openapi.Operation, base_objs []string) BaseObjectMethod { -// // name := texttools.pascal_case(op.operation_id) - -// // if op.operation_id.starts_with('new') { -// // if op.&& operation.params.len == 1 - -// return true -// } - -// pub fn operation_to_base_object_method(op openapi.Operation) BaseObjectMethod { -// if op.operation_id.starts_with('update') -// } - -// pub fn openapi_ts_client_body(op openapi.Operation, path string, method http.Method) string { -// match method { -// post { -// if schema := op.payload_schema() { -// symbol := schema_to_type(schema).typescript() -// return "return this.restClient.post<${symbol}>('${path}', data);" -// } -// } -// } - -// return if operation_is_base_object_method(op) { -// bo_method := operation_to_base_object_method(op) -// match bo_method. { -// .new { ts_client_new_body(op, path) } -// .get { ts_client_get_body(op, path) } -// .set { ts_client_set_body(op, path) } -// .delete { ts_client_delete_body(op, path) } -// .list { ts_client_list_body(op, path) } -// else {''} -// } -// } else {''} -// } - -fn get_endpoint(path string) string { - return if path == '' { - '' - } else { - '/${path.trim('/')}' - } -} - -// // generates a Base Object's `create` method -// fn ts_client_new_body(op Operation, path string) string { -// // the parameter of a base object new method is always the base object -// bo_param := openapi_codegen.parameter_to_param(op.parameters[0]) -// return "return this.restClient.post<${bo_param.typ.typescript()}>('${get_endpoint(path)}', ${bo_param.name});" -// } - -// // generates a Base Object's `create` method -// fn ts_client_get_body(op Operation, path string) string { -// // the parameter of a base object get method is always the id -// id_param := openapi_codegen.parameter_to_param(op.parameters[0]) -// return "return this.restClient.get<${id_param.typ.typescript()}>('${get_endpoint(path)}', ${id_param.name});" -// } - -// // generates a Base Object's `create` method -// fn ts_client_set_body(op Operation, path string) string { -// // the parameter of a base object set method is always the base object -// bo_param := openapi_codegen.parameter_to_param(op.parameters[0]) -// return "return this.restClient.put<${bo_param.typ.typescript()}>('${get_endpoint(path)}', ${bo_param.name});" -// } - -// // generates a Base Object's `delete` method -// fn ts_client_delete_body(op Operation, path string) string { -// // the parameter of a base object delete method is always the id -// id_param := openapi_codegen.parameter_to_param(op.parameters[0]) -// return "return this.restClient.get<${id_param.typ.typescript()}>('${get_endpoint(path)}', ${id_param.name});" -// } - -// // generates a Base Object's `list` method -// fn ts_client_list_body(op Operation, path string) string { -// // the result parameter of a base object list method is always the array of bo -// result_param := openapi_codegen.parameter_to_param(op.parameters[0]) -// return "return this.restClient.get<${result_param.typ.typescript()}>('${get_endpoint(path)}');" -// } - -// pub enum BaseObjectMethodType { -// new -// get -// set -// delete -// list -// other -// } - -// pub struct BaseObjectMethod { -// pub: -// typ BaseObjectMethodType -// object string // the name of the base object -// } diff --git a/libarchive/baobab/generator/generate_openrpc.v b/libarchive/baobab/generator/generate_openrpc.v deleted file mode 100644 index be445819..00000000 --- a/libarchive/baobab/generator/generate_openrpc.v +++ /dev/null @@ -1,110 +0,0 @@ -module generator - -import json -import incubaid.herolib.develop.codetools as code { File, Function, Struct, VFile } -import incubaid.herolib.schemas.openrpc { OpenRPC } -import incubaid.herolib.schemas.openrpc.codegen { generate_client_file, generate_client_test_file } - -pub fn generate_openrpc_file(spec OpenRPC) !File { - return File{ - name: 'openrpc' - extension: 'json' - content: json.encode(spec) - } -} - -pub fn generate_openrpc_client_file(spec OpenRPC) !VFile { - mut objects_map := map[string]Struct{} - // for object in spec.objects { - // objects_map[object.structure.name] = object.structure - // } - client_file := generate_client_file(spec, objects_map)! - return VFile{ - ...client_file - name: 'client_openrpc' - } -} - -pub fn generate_openrpc_client_test_file(spec OpenRPC) !VFile { - mut objects_map := map[string]Struct{} - // for object in spec.objects { - // objects_map[object.structure.name] = object.structure - // } - mut methods_map := map[string]Function{} - // for method in spec.methods { - // methods_map[method.func.name] = method.func - // } - file := generate_client_test_file(spec, methods_map, objects_map)! - return VFile{ - ...file - name: 'client_openrpc_test' - } -} - -// pub fn (actor Actor) generate_openrpc_code() !Module { -// openrpc_obj := actor.generate_openrpc() -// openrpc_json := openrpc_obj.encode()! - -// openrpc_file := File{ -// name: 'openrpc' -// extension: 'json' -// content: openrpc_json -// } - -// mut methods_map := map[string]Function{} -// for method in actor.methods { -// methods_map[method.func.name] = method.func -// } - -// mut objects_map := map[string]Struct{} -// for object in actor.objects { -// objects_map[object.structure.name] = object.structure -// } -// // actor_struct := generate_actor_struct(actor.name) -// actor_struct := actor.structure - -// client_file := openrpc_obj.generate_client_file(objects_map)! -// client_test_file := openrpc_obj.generate_client_test_file(methods_map, objects_map)! - -// handler_file := openrpc_obj.generate_handler_file(actor_struct, methods_map, objects_map)! -// handler_test_file := openrpc_obj.generate_handler_test_file(actor_struct, methods_map, -// objects_map)! - -// server_file := openrpc_obj.generate_server_file()! -// server_test_file := openrpc_obj.generate_server_test_file()! - -// return Module{ -// files: [ -// client_file, -// client_test_file, -// handler_file, -// handler_test_file, -// server_file, -// server_test_file, -// ] -// // misc_files: [openrpc_file] -// } -// } - -// pub fn (mut a Actor) export_playground(path string, openrpc_path string) ! { -// dollar := '$' -// openrpc.export_playground( -// dest: pathlib.get_dir(path: '${path}/playground')! -// specs: [ -// pathlib.get(openrpc_path), -// ] -// )! -// mut cli_file := pathlib.get_file(path: '${path}/command/cli.v')! -// cli_file.write($tmpl('./templates/playground.v.template'))! -// } - -// pub fn param_to_content_descriptor(param Param) openrpc.ContentDescriptor { -// if param.name == 'id' && param.typ.symbol == - -// return openrpc.ContentDescriptor { -// name: param.name -// summary: param.description -// required: param.is_required() -// schema: -// } -// } diff --git a/libarchive/baobab/generator/generate_openrpc_test.v b/libarchive/baobab/generator/generate_openrpc_test.v deleted file mode 100644 index 0bb0787a..00000000 --- a/libarchive/baobab/generator/generate_openrpc_test.v +++ /dev/null @@ -1,38 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code { Function, Param, Result, Struct, Type } -import incubaid.herolib.schemas.openrpc - -const test_actor_specification = ActorSpecification{ - methods: [ - ActorMethod{ - func: Function{ - name: 'get_object' - params: [ - Param{ - name: 'id' - typ: Type{ - symbol: 'int' - } - }, - ] - result: Result{ - typ: Type{ - symbol: 'Object' - } - } - } - }, - ] - objects: [BaseObject{ - structure: Struct{ - name: 'Object' - } - }] -} - -pub fn test_generate_openrpc() ! { - actor := Actor{} - object := generate_openrpc(actor) - panic(object.encode()!) -} diff --git a/libarchive/baobab/generator/generate_scripts.v b/libarchive/baobab/generator/generate_scripts.v deleted file mode 100644 index 238cf8c3..00000000 --- a/libarchive/baobab/generator/generate_scripts.v +++ /dev/null @@ -1,75 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code { File, Folder } -import incubaid.herolib.core.texttools - -// generates the folder with runnable scripts of the actor -pub fn generate_scripts_folder(name string, example bool) Folder { - actor_name := '${texttools.snake_case(name)}_actor' - return Folder{ - name: 'scripts' - files: [ - generate_run_script(actor_name), - generate_docs_script(actor_name), - generate_run_actor_script(name), - generate_run_actor_example_script(name), - generate_run_http_server_script(name), - // generate_compile_script(actor_name), - // generate_generate_script(actor_name) - ] - } -} - -// Function to generate a script for running an actor -fn generate_run_script(actor_name string) File { - actor_title := texttools.title_case(actor_name) - dollar := '$' - return File{ - name: 'run' - extension: 'sh' - content: $tmpl('./templates/run.sh.template') - } -} - -// Function to generate a script for running an actor -fn generate_docs_script(actor_name string) File { - dollar := '$' - return File{ - name: 'docs' - extension: 'vsh' - content: $tmpl('./templates/docs.vsh.template') - } -} - -// Function to generate a script for running an actor -fn generate_run_actor_script(name string) File { - name_snake := texttools.snake_case(name) - name_pascal := texttools.pascal_case(name) - return File{ - name: 'run_actor' - extension: 'vsh' - content: $tmpl('./templates/run_actor.vsh.template') - } -} - -// Function to generate a script for running an example actor -fn generate_run_actor_example_script(name string) File { - name_snake := texttools.snake_case(name) - name_pascal := texttools.pascal_case(name) - return File{ - name: 'run_actor_example' - extension: 'vsh' - content: $tmpl('./templates/run_actor_example.vsh.template') - } -} - -// Function to generate a script for running an HTTP server -fn generate_run_http_server_script(name string) File { - port := 8080 - name_snake := texttools.snake_case(name) - return File{ - name: 'run_http_server' - extension: 'vsh' - content: $tmpl('./templates/run_http_server.vsh.template') - } -} diff --git a/libarchive/baobab/generator/templates/actor.v.template b/libarchive/baobab/generator/templates/actor.v.template deleted file mode 100644 index 11159ce8..00000000 --- a/libarchive/baobab/generator/templates/actor.v.template +++ /dev/null @@ -1,41 +0,0 @@ -import os -import incubaid.herolib.baobab.stage -import incubaid.herolib.core.redisclient -import incubaid.herolib.schemas.openapi -import time - -pub const configuration = stage.ActorConfig { - name: '@{name_snake}' - version: '@{version}' -} - -@@[heap] -struct @{name_pascal}Actor { - stage.Actor -pub mut: - @{name_snake} I@{name_pascal} -} - -pub fn new(core I@{name_pascal}, config stage.ActorConfig) !&@{name_pascal}Actor { - return &@{name_pascal}Actor { - Actor: stage.new_actor(config)! - @{name_snake}: core - } -} - -pub fn (mut a @{name_pascal}Actor) handle(method string, data string) !string { - action := a.act( - name: method - params: data - )! - return action.result -} - -// Actor listens to the Redis queue for method invocations -pub fn (mut a @{name_pascal}Actor) run() ! { - mut rpc := a.get_redis_rpc()! - for { - rpc.process(a.handle)! - time.sleep(time.millisecond * 100) // Prevent CPU spinning - } -} diff --git a/libarchive/baobab/generator/templates/actor_example.v.template b/libarchive/baobab/generator/templates/actor_example.v.template deleted file mode 100644 index 15ea020b..00000000 --- a/libarchive/baobab/generator/templates/actor_example.v.template +++ /dev/null @@ -1,37 +0,0 @@ -import os -import incubaid.herolib.baobab.stage -import incubaid.herolib.core.redisclient -import incubaid.herolib.schemas.openapi - -const name = '@{actor_name_snake}' - -@@[heap] -struct @{actor_name_pascal}Actor { - stage.Actor -} - -pub fn new() !&@{actor_name_pascal}Actor { - return &@{actor_name_pascal}Actor { - Actor: stage.new_actor('example_@{actor_name_snake}')! - } -} - -pub fn (mut a @{actor_name_pascal}Actor) handle(method string, data string) !string { - action := a.act( - name: method - params: data - )! - return action.result -} - -// Actor listens to the Redis queue for method invocations -pub fn (mut a @{actor_name_pascal}Actor) run() ! { - mut redis := redisclient.new('localhost:6379') or { panic(err) } - mut rpc := redis.rpc_get('actor_@{dollar}{a.name}') - - println('Actor started and listening for tasks...') - for { - rpc.process(a.handle)! - time.sleep(time.millisecond * 100) // Prevent CPU spinning - } -} diff --git a/libarchive/baobab/generator/templates/actor_test.v.template b/libarchive/baobab/generator/templates/actor_test.v.template deleted file mode 100644 index 31e47f7f..00000000 --- a/libarchive/baobab/generator/templates/actor_test.v.template +++ /dev/null @@ -1,10 +0,0 @@ -const test_port = 8101 - -pub fn test_new() ! { - new() or { return error('Failed to create actor:\n@{dollar}{err}') } -} - -pub fn test_actor_run() ! { - mut actor := new()! - spawn actor.run() -} \ No newline at end of file diff --git a/libarchive/baobab/generator/templates/cli.v.template b/libarchive/baobab/generator/templates/cli.v.template deleted file mode 100644 index dc237209..00000000 --- a/libarchive/baobab/generator/templates/cli.v.template +++ /dev/null @@ -1,63 +0,0 @@ -module @{name} - -import os -import cli { Command } -import veb -import incubaid.herolib.schemas.openrpc -import incubaid.herolib.core.pathlib - - -const openrpc_path = '@{dollar}{os.dir(os.dir(@@FILE))}/openrpc.json' -const playground_path = '@{dollar}{os.dir(os.dir(@@FILE))}/playground' - -fn do() ! { - mut cmd := new_command() - cmd.setup() - cmd.parse(os.args) -} - -pub fn new_command() Command { - mut cmd := Command{ - name: '@{name}' - description: 'Your @{name} toolset.' - version: '1.0.16' - } - - - mut cmd_run := Command{ - name: 'run_server' - description: 'Run @{name} websocket server.' - usage: '' - required_args: 0 - execute: cmd_run_wsserver - } - - mut cmd_playground := Command{ - name: 'playground' - description: 'Run @{name} playground server.' - usage: '' - required_args: 0 - execute: playground - } - - cmd.add_command(cmd_run) - cmd.add_command(cmd_playground) - return cmd -} - -fn cmd_run_wsserver(cmd Command) ! { - // accountant.run_wsserver(3000)! -} - -fn playground(cmd Command) ! { - pg := openrpc.new_playground( - dest: pathlib.get_dir(path: playground_path)! - specs: [pathlib.get_file(path:openrpc_path)!] - )! - veb.run(pg, 8080) -} - - -fn main() { - do() or { panic(err) } -} \ No newline at end of file diff --git a/libarchive/baobab/generator/templates/client_test.v b/libarchive/baobab/generator/templates/client_test.v deleted file mode 100644 index 75def846..00000000 --- a/libarchive/baobab/generator/templates/client_test.v +++ /dev/null @@ -1,74 +0,0 @@ -module pet_store_actor - -import incubaid.herolib.baobab.stage -import incubaid.herolib.core.redisclient -import x.json2 as json -import time - -fn mock_response() ! { - mut redis := redisclient.new('localhost:6379')! - mut rpc_q := redis.rpc_get('actor_pet_store') - for { - rpc_q.process(fn (method string, data string) !string { - return json.encode(method) - })! - time.sleep(time.millisecond * 100) // Prevent CPU spinning - } -} - -fn test_list_pets() ! { - mut client := new_client()! - limit := 10 - spawn mock_response() - pets := client.list_pets(limit)! - // assert pets.len <= limit - println('test_list_pets passed') -} - -fn test_create_pet() ! { - mut client := new_client()! - client.create_pet()! - println('test_create_pet passed') -} - -fn test_get_pet() ! { - mut client := new_client()! - pet_id := 1 // Replace with an actual pet ID in your system - pet := client.get_pet(pet_id)! - // assert pet.id == pet_id - println('test_get_pet passed') -} - -fn test_delete_pet() ! { - mut client := new_client()! - pet_id := 1 // Replace with an actual pet ID in your system - client.delete_pet(pet_id)! - println('test_delete_pet passed') -} - -fn test_list_orders() ! { - mut client := new_client()! - client.list_orders()! - println('test_list_orders passed') -} - -fn test_get_order() ! { - mut client := new_client()! - order_id := 1 // Replace with an actual order ID in your system - order := client.get_order(order_id)! - // assert order.id == order_id - println('test_get_order passed') -} - -fn test_delete_order() ! { - mut client := new_client()! - order_id := 1 // Replace with an actual order ID in your system - client.delete_order(order_id)! - println('test_delete_order passed') -} - -fn test_create_user() ! { - mut client := new_client()! - client.create_user()! - println('test_create_user passed') -} diff --git a/libarchive/baobab/generator/templates/command.v.template b/libarchive/baobab/generator/templates/command.v.template deleted file mode 100644 index 0ce37e53..00000000 --- a/libarchive/baobab/generator/templates/command.v.template +++ /dev/null @@ -1,81 +0,0 @@ -import incubaid.herolib.core.pathlib -import cli { Command, Flag } -import os -import incubaid.herolib.ui.console - -pub fn cmd_example_actor() Command { - mut cmd := Command{ - name: 'example_actor' - usage: '' - description: 'create, edit, show mdbooks' - required_args: 0 - execute: cmd_example_actor_execute - } - - mut cmd_list := Command{ - sort_flags: true - name: 'list_books' - execute: cmd_publisher_list_books - description: 'will list existing mdbooks' - pre_execute: pre_func - } - - mut cmd_open := Command{ - name: 'open' - execute: cmd_publisher_open - description: 'will open the publication with the provided name' - pre_execute: pre_func - } - - cmd_open.add_flag(Flag{ - flag: .string - name: 'name' - abbrev: 'n' - description: 'name of the publication.' - }) - - cmd.add_command(cmd_list) - cmd.add_command(cmd_open) - return cmd -} - -fn cmd_publisher_list_books(cmd Command) ! { - console.print_header('Books:') - books := publisher.list_books()! - for book in books { - console.print_stdout(book.str()) - } -} - -fn cmd_publisher_open(cmd Command) ! { - name := cmd.flags.get_string('name') or { '' } - publisher.open(name)! -} - -fn cmd_execute(cmd Command) ! { - mut name := cmd.flags.get_string('name') or { '' } - - if name == '' { - console.print_debug('did not find name of book to generate, check in heroscript or specify with --name') - publisher_help(cmd) - exit(1) - } - - edit := cmd.flags.get_bool('edit') or { false } - open := cmd.flags.get_bool('open') or { false } - if edit || open { - // mdbook.book_open(name)! - } - - if edit { - // publisher.book_edit(name)! - } -} - -fn publisher_help(cmd Command) { - console.clear() - console.print_header('Instructions for example actor:') - console.print_lf(1) - console.print_stdout(cmd.help_message()) - console.print_lf(5) -} \ No newline at end of file diff --git a/libarchive/baobab/generator/templates/docs.vsh.template b/libarchive/baobab/generator/templates/docs.vsh.template deleted file mode 100755 index 3aa44fc7..00000000 --- a/libarchive/baobab/generator/templates/docs.vsh.template +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run - -import os - -abs_dir_of_script := dir(@@FILE) - -// Format code -println('Formatting code...') -if os.system('v fmt -w @{dollar}{abs_dir_of_script}/examples') != 0 { - eprintln('Warning: Failed to format examples') -} - -if os.system('v fmt -w @{dollar}{abs_dir_of_script}/src') != 0 { - eprintln('Warning: Failed to format actor') -} - -// Clean existing docs -println('Cleaning existing documentation...') - -os.rmdir_all('_docs') or {} -os.rmdir_all('docs') or {} -os.rmdir_all('vdocs') or {} - -herolib_path := os.join_path(abs_dir_of_script, 'lib') -os.chdir(herolib_path) or { - panic('Failed to change directory to herolib: @{dollar}{err}') -} - -os.rmdir_all('_docs') or {} -os.rmdir_all('docs') or {} -os.rmdir_all('vdocs') or {} - -// Generate HTML documentation -println('Generating HTML documentation...') -if os.system('v doc -m -f html . -readme -comments -no-timestamp -o ../docs') != 0 { - panic('Failed to generate HTML documentation') -} - -os.chdir(abs_dir_of_script) or { - panic('Failed to change directory to abs_dir_of_script: @{dollar}{err}') -} - -// Generate Markdown documentation -println('Generating Markdown documentation...') -os.rmdir_all('vdocs') or {} - -if os.system('v doc -m -no-color -f md -o vdocs/herolib/') != 0 { - panic('Failed to generate Hero markdown documentation') -} - -println('Documentation generation completed successfully!') diff --git a/libarchive/baobab/generator/templates/interface_http.v.template b/libarchive/baobab/generator/templates/interface_http.v.template deleted file mode 100644 index 3f11be7a..00000000 --- a/libarchive/baobab/generator/templates/interface_http.v.template +++ /dev/null @@ -1,41 +0,0 @@ -import incubaid.herolib.schemas.openapi { OpenAPI } -import incubaid.herolib.baobab.stage {Client, ClientConfig} -import incubaid.herolib.schemas.openrpc { OpenRPC } -import incubaid.herolib.baobab.stage.interfaces { HTTPServer, Context } -import veb - -@@[params] -pub struct HTTPServerParams { -pub: - base_url string - port int = 8080 -} - -pub fn new_http_server(params HTTPServerParams) !&HTTPServer { - mut s := interfaces.new_http_server()! - @if openrpc - mut openrpc_controller := new_openrpc_http_controller(HTTPServerParams{ - ...params, - base_url: '@{dollar}{params.base_url}/openrpc' - })! - s.register_controller[openrpc.HTTPController, Context]('/openrpc', mut openrpc_controller)! - @end - @if openapi - mut openapi_ctrl := new_openapi_http_controller(configuration, params)! - mut openapi_ex_ctrl := new_openapi_http_controller(configuration.example().example(), params)! - - mut openapi_playground_controller := openapi.new_playground_controller( - base_url: '@{dollar}{params.base_url}/playground/openapi' - specification_path: openapi_spec_path - )! - s.register_controller[openapi.HTTPController, Context]('/openapi/v1', mut openapi_ctrl)! - s.register_controller[openapi.HTTPController, Context]('/openapi/example', mut openapi_ex_ctrl)! - s.register_controller[openapi.PlaygroundController, Context]('/playground/openapi', mut openapi_playground_controller)! - @end - return s -} - -pub fn run_http_server(params HTTPServerParams) ! { - mut server := new_http_server(params)! - veb.run[HTTPServer, Context](mut server, params.port) -} diff --git a/libarchive/baobab/generator/templates/interface_http_test.v.template b/libarchive/baobab/generator/templates/interface_http_test.v.template deleted file mode 100644 index ae6328e4..00000000 --- a/libarchive/baobab/generator/templates/interface_http_test.v.template +++ /dev/null @@ -1,7 +0,0 @@ -fn test_new_http_server() ! { - new_http_server()! -} - -fn test_run_http_server() ! { - spawn run_http_server() -} diff --git a/libarchive/baobab/generator/templates/interface_openapi.v.template b/libarchive/baobab/generator/templates/interface_openapi.v.template deleted file mode 100644 index 38c1e929..00000000 --- a/libarchive/baobab/generator/templates/interface_openapi.v.template +++ /dev/null @@ -1,22 +0,0 @@ -import incubaid.herolib.baobab.stage.interfaces -import incubaid.herolib.baobab.stage -import incubaid.herolib.schemas.openapi - -pub fn new_openapi_interface(config stage.ActorConfig) !&interfaces.OpenAPIInterface { - // create OpenAPI Handler with actor's client - client := new_client(config)! - return interfaces.new_openapi_interface(client.Client) -} - -@if http -// creates HTTP controller with the actor's OpenAPI Handler -// and OpenAPI Specification -pub fn new_openapi_http_controller(config stage.ActorConfig, params HTTPServerParams) !&openapi.HTTPController { - return openapi.new_http_controller( - base_url: '@{dollar}{params.base_url}/openapi/@{dollar}{config.version}' - specification: openapi_specification - specification_path: openapi_spec_path - handler: new_openapi_interface(config)! - ) -} -@end \ No newline at end of file diff --git a/libarchive/baobab/generator/templates/interface_openapi_test.v.template b/libarchive/baobab/generator/templates/interface_openapi_test.v.template deleted file mode 100644 index 6afbdc4b..00000000 --- a/libarchive/baobab/generator/templates/interface_openapi_test.v.template +++ /dev/null @@ -1,9 +0,0 @@ -fn test_new_openapi_interface() ! { - new_openapi_interface()! -} - -@if http -fn test_new_openapi_http_controller() ! { - new_openapi_http_controller()! -} -@end diff --git a/libarchive/baobab/generator/templates/interface_openrpc.v.template b/libarchive/baobab/generator/templates/interface_openrpc.v.template deleted file mode 100644 index ad8081d4..00000000 --- a/libarchive/baobab/generator/templates/interface_openrpc.v.template +++ /dev/null @@ -1,19 +0,0 @@ -import incubaid.herolib.baobab.stage.interfaces -import incubaid.herolib.schemas.openrpc - -pub fn new_openrpc_interface() !&interfaces.OpenRPCInterface { - // create OpenRPC Handler with actor's client - client := new_client()! - return interfaces.new_openrpc_interface(client.Client) -} - -@if http -// creates HTTP controller with the actor's OpenRPC Handler -// and OpenRPC Specification -pub fn new_openrpc_http_controller(params ServerParams) !&openrpc.HTTPController { - return openrpc.new_http_controller( - specification: openrpc_specification - handler: new_openrpc_interface()! - ) -} -@end \ No newline at end of file diff --git a/libarchive/baobab/generator/templates/interface_openrpc_test.v.template b/libarchive/baobab/generator/templates/interface_openrpc_test.v.template deleted file mode 100644 index 6186b55f..00000000 --- a/libarchive/baobab/generator/templates/interface_openrpc_test.v.template +++ /dev/null @@ -1,9 +0,0 @@ -fn test_new_openrpc_interface() ! { - new_openrpc_interface()! -} - -@if http -fn test_new_openrpc_http_controller() ! { - new_openrpc_http_controller()! -} -@end diff --git a/libarchive/baobab/generator/templates/playground.v.template b/libarchive/baobab/generator/templates/playground.v.template deleted file mode 100644 index 80a0f45e..00000000 --- a/libarchive/baobab/generator/templates/playground.v.template +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env -S v -n -cg -w -enable-globals run - -import incubaid.herolib.baobab.stages.accountant -import veb -import incubaid.herolib.schemas.openrpc -import os -import incubaid.herolib.core.pathlib - -const openrpc_path = '@{dollar}{os.dir(os.dir(@@FILE))}/openrpc.json' -const playground_path = '@{dollar}{os.dir(os.dir(@@FILE))}/playground' - -pg := openrpc.new_playground( - dest: pathlib.get_dir(path: playground_path)! - specs: [pathlib.get_file(path:openrpc_path)!] -)! -veb.run(pg, 8080) \ No newline at end of file diff --git a/libarchive/baobab/generator/templates/run.sh.template b/libarchive/baobab/generator/templates/run.sh.template deleted file mode 100755 index f558d250..00000000 --- a/libarchive/baobab/generator/templates/run.sh.template +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -ex - -DIR="@{dollar}(cd "@{dollar}(dirname "@{dollar}{BASH_SOURCE[0]}")" && pwd)" -echo "@{dollar}DIR" - -chmod +x @{dollar}{DIR}/run_actor.vsh -@{dollar}{DIR}/run_actor.vsh & -ACTOR_PID=@{dollar}! - -chmod +x @{dollar}{DIR}/run_actor_example.vsh -@{dollar}{DIR}/run_actor_example.vsh & -EXAMPLE_ACTOR_PID=@{dollar}! - -chmod +x @{dollar}{DIR}/run_http_server.vsh -@{dollar}{DIR}/run_http_server.vsh & -HTTP_SERVER_PID=@{dollar}! - -# Print desired output -echo "${actor_title} Actor Redis Interface running on redis://localhost:6379" -echo "* /queues/${actor_name} -> Action Interface" - -echo "" -echo "${actor_title} Actor HTTP Server running on http://localhost:8080" -echo "* http://localhost:8080/playground/openapi -> OpenAPI Playground" -echo "* http://localhost:8080/openapi -> OpenAPI Interface" -# echo "* http://localhost:8080/docs -> Documentation" -echo "" - -# Function to clean up when script is killed -cleanup() { - echo "Stopping background processes..." - kill "@{dollar}ACTOR_PID" "@{dollar}HTTP_SERVER_PID" 2>/dev/null - wait - echo "All processes stopped." - exit 0 -} - -# Trap SIGINT (Ctrl+C), SIGTERM, and SIGQUIT to call cleanup -trap cleanup SIGINT SIGTERM SIGQUIT - -# Wait for processes to finish -wait \ No newline at end of file diff --git a/libarchive/baobab/generator/templates/run_actor.vsh.template b/libarchive/baobab/generator/templates/run_actor.vsh.template deleted file mode 100755 index fc78a204..00000000 --- a/libarchive/baobab/generator/templates/run_actor.vsh.template +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env -S v -w -n -enable-globals run - -import @{name_snake} - -mut actor := @{name_snake}.new( - @{name_snake}.new_@{name_snake}()!, - @{name_snake}.configuration -)! - -actor.run()! \ No newline at end of file diff --git a/libarchive/baobab/generator/templates/run_actor_example.vsh.template b/libarchive/baobab/generator/templates/run_actor_example.vsh.template deleted file mode 100755 index 8ec505d5..00000000 --- a/libarchive/baobab/generator/templates/run_actor_example.vsh.template +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env -S v -w -n -enable-globals run - -import @{name_snake} - -mut actor := @{name_snake}.new( - @{name_snake}.new_@{name_snake}_example()!, - @{name_snake}.configuration.example() -)! - -actor.run()! \ No newline at end of file diff --git a/libarchive/baobab/generator/templates/run_http_server.vsh.template b/libarchive/baobab/generator/templates/run_http_server.vsh.template deleted file mode 100755 index 2cd54e59..00000000 --- a/libarchive/baobab/generator/templates/run_http_server.vsh.template +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env -S v -w -n -enable-globals run -import @{name_snake} - -@{name_snake}.run_http_server( - base_url: 'http://localhost:@{port}' - port: @{port} -)! diff --git a/libarchive/baobab/generator/templates/specifications.v.template b/libarchive/baobab/generator/templates/specifications.v.template deleted file mode 100644 index 3f3cfd66..00000000 --- a/libarchive/baobab/generator/templates/specifications.v.template +++ /dev/null @@ -1,14 +0,0 @@ -import incubaid.herolib.schemas.openapi -import incubaid.herolib.schemas.openrpc -import os - -@if support_openrpc -const openrpc_spec_path = '@{dollar}{os.dir(@@FILE)}/docs/specs/openrpc.json' -const openrpc_spec_json = os.read_file(openrpc_spec_path) or { panic(err) } -const openrpc_specification = openrpc.decode(openrpc_spec_json)! -@end -@if support_openapi -const openapi_spec_path = '@{dollar}{os.dir(os.dir(@@FILE))}/docs/specs/openapi.json' -const openapi_spec_json = os.read_file(openapi_spec_path) or { panic(err) } -const openapi_specification = openapi.json_decode(openapi_spec_json)! -@end \ No newline at end of file diff --git a/libarchive/baobab/generator/testdata/.gitignore b/libarchive/baobab/generator/testdata/.gitignore deleted file mode 100644 index fbe24508..00000000 --- a/libarchive/baobab/generator/testdata/.gitignore +++ /dev/null @@ -1 +0,0 @@ -pet_store_actor \ No newline at end of file diff --git a/libarchive/baobab/generator/write_object_methods_test.v b/libarchive/baobab/generator/write_object_methods_test.v deleted file mode 100644 index 18c64096..00000000 --- a/libarchive/baobab/generator/write_object_methods_test.v +++ /dev/null @@ -1,139 +0,0 @@ -module generator - -import incubaid.herolib.develop.codetools as code -import os - -// // generate_object_methods generates CRUD actor methods for a provided structure -// pub fn (generator ActorGenerator) generate_object_methods(structure code.Struct) []code.Function { -// return [ -// generator.generate_get_method(structure), -// // generator.generate_set_method(structure), -// // generator.generate_delete_method(structure), -// // generator.generate_get_method(structure), -// ] -// } - -// generate_object_methods generates CRUD actor methods for a provided structure -pub fn test_generate_get_method() { - generator := ActorGenerator{'test'} - actor_struct := code.Struct{ - name: 'TestActor' - fields: [ - code.StructField{ - name: 'test_struct_map' - typ: code.Type{ - symbol: 'map[string]&TestStruct' - } - }, - ] - } - - test_struct := code.Struct{ - name: 'TestStruct' - } - field := get_child_field( - parent: actor_struct - child: test_struct - ) - - method := generator.generate_get_method( - actor_name: actor_struct.name - actor_field: field - root_struct: test_struct - ) -} - -// // generate_object_methods generates CRUD actor methods for a provided structure -// pub fn (generator ActorGenerator) generate_set_method(structure code.Struct) code.Function { -// params_getter := "id := params.get('id')!" -// field := generator.get_object_field(structure) -// object_getter := 'object := actor.${field.name}[id]' -// body := '${params_getter}\n${object_getter}\nreturn object' -// get_method := code.Function{ -// name: 'get_${generator.model_name}' -// description: 'gets the ${structure.name} with the given object id' -// receiver: code.Param{ -// name: 'actor' -// struct_: generator.actor_struct -// } -// params: [ -// code.Param{ -// name: 'id' -// typ: code.Type{ -// symbol: 'string' -// } -// }, -// ] -// result: code.Result{ -// structure: structure -// } -// body: body -// } -// return get_method -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// pub fn (generator ActorGenerator) generate_get_method(structure code.Struct) code.Function { -// params_getter := "id := params.get('id')!" -// field := generator.get_object_field(structure) -// object_getter := 'object := actor.${field.name}[id]' -// body := '${params_getter}\n${object_getter}\nreturn object' -// get_method := code.Function{ -// name: 'get_${generator.model_name}' -// description: 'gets the ${structure.name} with the given object id' -// receiver: code.Param{ -// name: 'actor' -// struct_: generator.actor_struct -// } -// params: [ -// code.Param{ -// name: 'id' -// typ: code.Type{ -// symbol: 'string' -// } -// }, -// ] -// result: code.Result{ -// structure: structure -// } -// body: body -// } -// return get_method -// } - -// // generate_object_methods generates CRUD actor methods for a provided structure -// pub fn (generator ActorGenerator) generate_delete_method(structure code.Struct) code.Function { -// params_getter := "id := params.get('id')!" -// field := generator.get_object_field(structure) -// object_getter := 'object := actor.${field.name}[id]' -// body := '${params_getter}\n${object_getter}\nreturn object' -// get_method := code.Function{ -// name: 'get_${generator.model_name}' -// description: 'gets the ${structure.name} with the given object id' -// receiver: code.Param{ -// name: 'actor' -// struct_: generator.actor_struct -// } -// params: [ -// code.Param{ -// name: 'id' -// typ: code.Type{ -// symbol: 'string' -// } -// }, -// ] -// result: code.Result{ -// structure: structure -// } -// body: body -// } -// return get_method -// } - -// pub fn (generator ActorGenerator) get_object_field(structure code.Struct) code.StructField { -// fields := generator.actor_struct.fields.filter(it.typ.symbol == 'map[string]&${structure.name}') -// if fields.len != 1 { -// panic('this should never happen') -// } -// return fields[0] -// } diff --git a/libarchive/baobab/osis/README.md b/libarchive/baobab/osis/README.md deleted file mode 100644 index b971361c..00000000 --- a/libarchive/baobab/osis/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# OSIS - -Object Storage and Indexing System -A system for storing root objects efficiently and indexed by certain fields. - -OSIS comprises of 2 elements: - -- Indexer: responsible for indexing and identifying objects -- Storer: responsible of storing in different databases, with varying encodings, and encryption. - -## Indexer - -The indexers primary duty is to be able to create and query sql tables for a given base object specification and it's indices. For instance: I specify a Base Object called Pet, and I specify Pet so that (more on writing specifications here) it's `breed` tag is indexable. - -``` -struct Pet { - breed string @[index] -} -``` - -Given this specification, the indexer is expected to create an sql table with the breed field as a column. This allows the backend to filter and search base objects by their fields. Note that, the object isn't stored on the table, but just it's id. Object storage and modification is handled by the - - -## Getting started - - -## Generic Code - -The solution provided by this module is to create a backend interface with generic CRUD + list + filter methods for root objects that different backends can implement. - -This allows for a single generated actor code to use different backends, without having to generate separate code for each. Having less generated code is less prone to errors, and using the same backend methods for each actor makes it easier modify, fix and add features to the backends. Using the same data manipulation methods in generated code also makes it easier to generate code for the actor as the implementations don't differ for different root objects. - -### Creating a backend diff --git a/libarchive/baobab/osis/factory.v b/libarchive/baobab/osis/factory.v deleted file mode 100644 index 2e45aeff..00000000 --- a/libarchive/baobab/osis/factory.v +++ /dev/null @@ -1,8 +0,0 @@ -module osis - -pub fn new(config OSISConfig) !OSIS { - return OSIS{ - indexer: new_indexer()! - storer: new_storer()! - } -} diff --git a/libarchive/baobab/osis/indexer.v b/libarchive/baobab/osis/indexer.v deleted file mode 100644 index 98d22763..00000000 --- a/libarchive/baobab/osis/indexer.v +++ /dev/null @@ -1,116 +0,0 @@ -module osis - -import json -import db.sqlite -import incubaid.herolib.core.texttools -import incubaid.herolib.core.pathlib - -pub struct Indexer { - db sqlite.DB -} - -@[params] -pub struct IndexerConfig { - db_path string - reset bool -} - -pub fn new_indexer(config IndexerConfig) !Indexer { - return Indexer{} -} - -// deletes an indexer table belonging to a base object -pub fn reset(path string) ! { - mut db_file := pathlib.get_file(path: path)! - db_file.delete()! -} - -pub fn (mut i Indexer) new_generic[T](id u32, object T) !u32 { - return i.new(get_table[T](), id, get_indices[T](object))! -} - -// new creates a new root object entry in the root_objects table, -// and the table belonging to the type of root object with columns for index fields -pub fn (mut i Indexer) new(table string, id u32, indices map[string]string) !u32 { - insert_query := 'INSERT into ${table} (${indices.keys().join(',')}) values (${indices.values().join(',')})' - i.db.exec(insert_query) or { - return error('Error inserting object ${id} into table ${table}\n${err}') - } - return 0 -} - -// save the session to redis & mem -pub fn (mut backend Indexer) set(obj RootObject) ! { - panic('implement') -} - -// save the session to redis & mem -pub fn (mut backend Indexer) delete(id string, obj RootObject) ! { - panic('implement') -} - -pub fn (mut backend Indexer) get(id string, obj RootObject) !RootObject { - panic('implement') -} - -pub fn (mut backend Indexer) get_json(id string, obj RootObject) !string { - panic('implement') -} - -pub fn (mut backend Indexer) list(obj RootObject) ![]u32 { - panic('implement') -} - -// from and to for int f64 time etc. -@[params] -pub struct FilterParams { - // indices map[string]string // map of index values that are being filtered by, in order of priority. - limit int // limit to the number of values to be returned, in order of priority - fuzzy bool // if fuzzy matching is enabled in matching indices - matches_all bool // if results should match all indices or any -} - -// filter lists root objects of type T that match provided index parameters and params. -pub fn (mut backend Indexer) filter(filter RootObject, params FilterParams) ![]string { - panic('implement') -} - -// create_root_struct_table creates a table for a root_struct with columns for each index field -fn (mut backend Indexer) create_root_object_table(object RootObject) ! { - panic('implement') -} - -// deletes an indexer table belonging to a root object -fn (mut backend Indexer) delete_table(object RootObject) ! { - panic('implement') -} - -fn (mut backend Indexer) get_table_indices(table_name string) ![]string { - panic('implement') -} - -fn (mut backend Indexer) table_exists(table_name string) !bool { - panic('implement') -} - -// get_table_name returns the name of the table belonging to a root struct -fn get_table_name(object RootObject) string { - panic('implement') -} - -// get_table_name returns the name of the table belonging to a root struct -fn get_table[T]() string { - return typeof[T]() -} - -// returns the lists of the indices of a root objects db table, and corresponding values -pub fn get_indices[T](object T) map[string]string { - mut indices := map[string]string{} - $for field in T.fields { - if field.attrs.contains('index') { - value := object.$(field.name) - indices[field.name] = '${value}' - } - } - return indices -} diff --git a/libarchive/baobab/osis/model.v b/libarchive/baobab/osis/model.v deleted file mode 100644 index 64176fed..00000000 --- a/libarchive/baobab/osis/model.v +++ /dev/null @@ -1,16 +0,0 @@ -module osis - -pub struct OSIS { -pub mut: - indexer Indexer // storing indeces - storer Storer -} - -@[params] -pub struct OSISConfig { -pub: - directory string - name string - secret string - reset bool -} diff --git a/libarchive/baobab/osis/osis.v b/libarchive/baobab/osis/osis.v deleted file mode 100644 index dd340cbe..00000000 --- a/libarchive/baobab/osis/osis.v +++ /dev/null @@ -1,58 +0,0 @@ -module osis - -import os - -pub fn (mut o OSIS) generic_new[T](obj T) !u32 { - id := o.indexer.generic_new[T](obj)! - o.storer.generic_new[T](obj)! - return id -} - -pub fn (mut o OSIS) new[T](obj T) !u32 { - id := o.storer.new_generic[T](obj)! - o.indexer.new_generic[T](id, obj)! - return id -} - -pub fn (mut o OSIS) generic_get[T](id u32) !T { - return o.storer.generic_get[T](id)! -} - -pub fn (mut o OSIS) get[T](id u32) !T { - return o.storer.generic_get[T](u32(id))! -} - -pub fn (mut o OSIS) generic_set[T](obj T) ! { - o.indexer.generic_set[T](obj) or { return error('Failed to set new indices:\n${err}') } - o.storer.generic_set[T](obj)! -} - -pub fn (mut o OSIS) generic_delete[T](id u32) ! { - o.indexer.generic_delete[T](id)! - o.storer.generic_delete[T](id)! -} - -pub fn (mut o OSIS) delete(id u32) ! { - o.storer.delete(u32(id))! -} - -pub fn (mut o OSIS) list[T]() ![]T { - panic('implement') - // ids := o.indexer.generic_list[T]()! - // return o.storer.generic_list[T](ids)! -} - -pub fn (mut o OSIS) generic_list[T]() ![]T { - ids := o.indexer.generic_list[T]()! - return o.storer.generic_list[T](ids)! -} - -pub fn (mut o OSIS) generic_filter[T, D](filter D, params FilterParams) ![]T { - ids := o.indexer.generic_filter[T, D](filter, params)! - return o.storer.generic_list[T](ids)! -} - -pub fn (mut o OSIS) generic_reset[T]() ! { - o.indexer.generic_reset[T]()! - o.storer.generic_reset[T]()! -} diff --git a/libarchive/baobab/osis/root_object.v b/libarchive/baobab/osis/root_object.v deleted file mode 100644 index 824579a0..00000000 --- a/libarchive/baobab/osis/root_object.v +++ /dev/null @@ -1,135 +0,0 @@ -module osis - -import x.json2 - -// describes a root object -pub struct RootObject { -pub mut: - id string - name string // Story - fields []FieldDescription -} - -pub struct FieldDescription { -pub mut: - name string // name of field - typ FieldType - value string // value of field - is_secret bool // whether field should be encrypted upon storage - is_index bool // whether object is searchable by field - fts_enabled bool // whether full text search on field is enabled -} - -// returns the sql type name of the field -pub fn (field FieldDescription) sql_type() string { - return match field.typ { - .text { 'TEXT' } - .number { 'INTEGER' } - } -} - -pub enum FieldType { - number - text -} - -pub fn (obj RootObject) to_json() string { - mut obj_map := map[string]json2.Any{} - for field in obj.fields { - obj_map[field.name] = field.value - } - - return obj_map.str() -} - -// returns the lists of the indices of a root objects db table, and corresponding values -pub fn (obj RootObject) sql_indices_values() ([]string, []string) { - obj_encoded := obj.to_json() - obj_val := "'${obj_encoded.replace("'", "''")}'" - - // insert root object into its table - mut indices := ['data'] - mut values := [obj_val] - - for field in obj.fields { - if field.name == 'id' { - indices << '${field.name}' - values << '${field.value}' - } - - if field.typ == .text { - if field.is_index { - indices << '${field.name}' - values << "'${field.value}'" - } - } else if field.typ == .number { - if field.is_index { - indices << '${field.name}' - values << '${field.value}' - } - } - } - println('debugzoni ${indices} ${values}') - return indices, values -} - -// return the description of a given generic -pub fn root_object[T](object T) RootObject { - mut fields := []FieldDescription{} - - $for field in T.fields { - mut typ := FieldType{} - $if field.typ is string { - typ = .text - } $else $if field.typ is int { - typ = .number - } - - fields << FieldDescription{ - name: field.name - typ: typ - value: object.$(field.name).str() - is_index: field.attrs.contains('index') - is_secret: field.attrs.contains('secret') - fts_enabled: field.attrs.contains('fts_enabled') - } - } - return RootObject{ - name: typeof[T]() - fields: fields - } -} - -// decodes root object into generic struct T -pub fn (object RootObject) to_generic[T]() T { - mut t := T{} - - $for field in T.fields { - field_descrs := object.fields.filter(it.name == field.name) - if field_descrs.len == 1 { - $if field.typ is int { - t.$(field.name) = field_descrs[0].value.int() - } $else $if field.is_enum { - t.$(field.name) = field_descrs[0].value.int() - } $else { - t.$(field.name) = field_descrs[0].value - } - } - } - return t -} - -pub fn root_object_from_json(json string) !RootObject { - raw_decode := json2.raw_decode(json)! - obj_map := raw_decode.as_map() - - mut obj := RootObject{} - for key, val in obj_map { - obj.fields << FieldDescription{ - name: key - value: val.str() - } - } - - return obj -} diff --git a/libarchive/baobab/osis/storer.v b/libarchive/baobab/osis/storer.v deleted file mode 100644 index 408ace1e..00000000 --- a/libarchive/baobab/osis/storer.v +++ /dev/null @@ -1,15 +0,0 @@ -module osis - -import incubaid.herolib.data.ourdb { OurDB } -import os - -pub struct Storer { -pub mut: - db OurDB -} - -pub fn new_storer() !Storer { - return Storer{ - db: ourdb.new()! - } -} diff --git a/libarchive/baobab/osis/storer_generic.v b/libarchive/baobab/osis/storer_generic.v deleted file mode 100644 index 4d401182..00000000 --- a/libarchive/baobab/osis/storer_generic.v +++ /dev/null @@ -1,23 +0,0 @@ -module osis - -import json - -// new creates a new root object entry in the root_objects table, -// and the table belonging to the type of root object with columns for index fields -pub fn (mut storer Storer) new_generic[T](obj T) !u32 { - data := json.encode(obj).bytes() - return storer.db.set(data: data) -} - -pub fn (mut storer Storer) generic_get[T](id u32) !T { - return json.decode(T, storer.db.get(id)!.bytestr()) -} - -pub fn (mut storer Storer) generic_set[T](obj T) ! { - data := json.encode(obj).bytes() - return storer.db.set(data: data) -} - -pub fn (mut storer Storer) delete(id u32) ! { - storer.db.delete(id)! -} diff --git a/libarchive/baobab/specification/README.md b/libarchive/baobab/specification/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/libarchive/baobab/specification/from_openapi.v b/libarchive/baobab/specification/from_openapi.v deleted file mode 100644 index ca3e1555..00000000 --- a/libarchive/baobab/specification/from_openapi.v +++ /dev/null @@ -1,180 +0,0 @@ -module specification - -import incubaid.herolib.core.texttools -import incubaid.herolib.develop.codetools as code { Struct } -import incubaid.herolib.schemas.jsonschema { Schema, SchemaRef } -import incubaid.herolib.schemas.openapi { MediaType, OpenAPI, OperationInfo, Parameter } -import incubaid.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, Example, ExamplePairing, ExampleRef } - -// Helper function: Convert OpenAPI parameter to ContentDescriptor -fn openapi_param_to_content_descriptor(param Parameter) ContentDescriptor { - return ContentDescriptor{ - name: param.name - summary: param.description - description: param.description - required: param.required - schema: param.schema - } -} - -// Helper function: Convert OpenAPI parameter to ContentDescriptor -fn openapi_param_to_example(param Parameter) ?Example { - if param.schema is Schema { - if param.schema.example.str() != '' { - return Example{ - name: 'Example ${param.name}' - description: 'Example ${param.description}' - value: param.schema.example - } - } - } - return none -} - -// Helper function: Convert OpenAPI operation to ActorMethod -fn openapi_operation_to_actor_method(info OperationInfo) ActorMethod { - mut parameters := []ContentDescriptor{} - mut example_parameters := []Example{} - - for param in info.operation.parameters { - parameters << openapi_param_to_content_descriptor(param) - example_parameters << openapi_param_to_example(param) or { continue } - } - - if schema_ := info.operation.payload_schema() { - // TODO: document assumption - schema := Schema{ - ...schema_ - title: texttools.pascal_case(info.operation.operation_id) - } - parameters << ContentDescriptor{ - name: 'data' - schema: SchemaRef(schema) - } - } - - mut success_responses := map[string]MediaType{} - - for code, response in info.operation.responses { - if code.starts_with('2') { // Matches all 2xx responses - success_responses[code] = response.content['application/json'] - } - } - - if success_responses.len > 1 || success_responses.len == 0 { - panic('Actor specification must specify one successful response.') - } - response_success := success_responses.values()[0] - - mut result := ContentDescriptor{ - name: 'result' - description: 'The response of the operation.' - required: true - schema: response_success.schema - } - - example_result := if response_success.example.str() != '' { - Example{ - name: 'Example response' - value: response_success.example - } - } else { - Example{} - } - - pairing := if example_result != Example{} || example_parameters.len > 0 { - ExamplePairing{ - params: example_parameters.map(ExampleRef(it)) - result: ExampleRef(example_result) - } - } else { - ExamplePairing{} - } - - mut errors := []ErrorSpec{} - for status, response in info.operation.responses { - if status.int() >= 400 { - error_schema := if response.content.len > 0 { - response.content.values()[0].schema - } else { - Schema{} - } - errors << ErrorSpec{ - code: status.int() - message: response.description - data: error_schema // Extend if error schema is defined - } - } - } - - return ActorMethod{ - name: info.operation.operation_id - description: info.operation.description - summary: info.operation.summary - parameters: parameters - example: pairing - result: result - errors: errors - } -} - -// Helper function: Convert OpenAPI schema to Struct -fn openapi_schema_to_struct(name string, schema SchemaRef) Struct { - // Assuming schema properties can be mapped to Struct fields - return Struct{ - name: name - } -} - -// Converts OpenAPI to ActorSpecification -pub fn from_openapi(spec_raw OpenAPI) !ActorSpecification { - spec := openapi.process(spec_raw)! - mut objects := []BaseObject{} - - // get all operations for path as list of tuple [](path_string, http.Method, openapi.Operation) - - // Extract methods from OpenAPI paths - // for path, item in spec.paths { - // if item.get.operation_id != '' { - // methods << openapi_operation_to_actor_method(item.get, item.get.operation_id, path) - // } - // if item.post.operation_id != '' { - // methods << openapi_operation_to_actor_method(item.post, item.post.operation_id, path) - // } - // if item.put.operation_id != '' { - // methods << openapi_operation_to_actor_method(item.put, item.put.operation_id, path) - // } - // if item.delete.operation_id != '' { - // methods << openapi_operation_to_actor_method(item.delete, item.delete.operation_id, path) - // } - // if item.patch.operation_id != '' { - // methods << openapi_operation_to_actor_method(item.patch, item.patch.operation_id, path) - // } - // if item.head.operation_id != '' { - // methods << openapi_operation_to_actor_method(item.head, item.head.operation_id, path) - // } - // if item.options.operation_id != '' { - // methods << openapi_operation_to_actor_method(item.options, item.options.operation_id, path) - // } - // if item.trace.operation_id != '' { - // methods << openapi_operation_to_actor_method(item.trace, item.trace.operation_id, path) - // } - // } - - // Extract objects from OpenAPI components.schemas - for name, schema in spec.components.schemas { - objects << BaseObject{ - schema: schema as Schema - } - } - - return ActorSpecification{ - openapi: spec_raw - name: spec.info.title - description: spec.info.description - structure: Struct{} // Assuming no top-level structure for this use case - interfaces: [.openapi] // Default to OpenAPI for input - methods: spec.get_operations().map(openapi_operation_to_actor_method(it)) - objects: objects - } -} diff --git a/libarchive/baobab/specification/from_openapi_test.v b/libarchive/baobab/specification/from_openapi_test.v deleted file mode 100644 index 41db4cff..00000000 --- a/libarchive/baobab/specification/from_openapi_test.v +++ /dev/null @@ -1,400 +0,0 @@ -module specification - -import x.json2 as json -import incubaid.herolib.develop.codetools as code { Struct } -import incubaid.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec } -import incubaid.herolib.schemas.openapi { Components, Info, OpenAPI, Operation, PathItem, ServerSpec } -import incubaid.herolib.schemas.jsonschema { Reference, Schema, SchemaRef } - -const openapi_spec = OpenAPI{ - openapi: '3.0.3' - info: Info{ - title: 'Pet Store API' - description: 'A sample API for a pet store' - version: '1.0.0' - } - servers: [ - ServerSpec{ - url: 'https://api.petstore.example.com/v1' - description: 'Production server' - }, - ServerSpec{ - url: 'https://staging.petstore.example.com/v1' - description: 'Staging server' - }, - ] - paths: { - '/pets': PathItem{ - get: Operation{ - summary: 'List all pets' - operation_id: 'listPets' - parameters: [ - openapi.Parameter{ - name: 'limit' - in_: 'query' - description: 'Maximum number of pets to return' - required: false - schema: Schema{ - typ: 'integer' - format: 'int32' - example: 10 - } - }, - ] - responses: { - '200': openapi.ResponseSpec{ - description: 'A paginated list of pets' - content: { - 'application/json': openapi.MediaType{ - schema: Reference{ - ref: '#/components/schemas/Pets' - } - example: json.raw_decode('[ - { "id": 1, "name": "Fluffy", "tag": "dog" }, - { "id": 2, "name": "Whiskers", "tag": "cat" } - ]')! - } - } - } - '400': openapi.ResponseSpec{ - description: 'Invalid request' - } - } - } - post: Operation{ - summary: 'Create a new pet' - operation_id: 'createPet' - request_body: openapi.RequestBody{ - required: true - content: { - 'application/json': openapi.MediaType{ - schema: Reference{ - ref: '#/components/schemas/NewPet' - } - example: json.raw_decode('{ "name": "Bella", "tag": "dog" }')! - } - } - } - responses: { - '201': openapi.ResponseSpec{ - description: 'Pet created' - content: { - 'application/json': openapi.MediaType{ - schema: Reference{ - ref: '#/components/schemas/Pet' - } - example: json.raw_decode('{ "id": 3, "name": "Bella", "tag": "dog" }')! - } - } - } - '400': openapi.ResponseSpec{ - description: 'Invalid input' - } - } - } - } - '/pets/{petId}': PathItem{ - get: Operation{ - summary: 'Get a pet by ID' - operation_id: 'getPet' - parameters: [ - openapi.Parameter{ - name: 'petId' - in_: 'path' - description: 'ID of the pet to retrieve' - required: true - schema: Schema{ - typ: 'integer' - format: 'int64' - example: 1 - } - }, - ] - responses: { - '200': openapi.ResponseSpec{ - description: 'A pet' - content: { - 'application/json': openapi.MediaType{ - schema: Reference{ - ref: '#/components/schemas/Pet' - } - example: json.raw_decode('{ "id": 1, "name": "Fluffy", "tag": "dog" }')! - } - } - } - '404': openapi.ResponseSpec{ - description: 'Pet not found' - } - } - } - delete: Operation{ - summary: 'Delete a pet by ID' - operation_id: 'deletePet' - parameters: [ - openapi.Parameter{ - name: 'petId' - in_: 'path' - description: 'ID of the pet to delete' - required: true - schema: Schema{ - typ: 'integer' - format: 'int64' - example: 1 - } - }, - ] - responses: { - '204': openapi.ResponseSpec{ - description: 'Pet deleted' - } - '404': openapi.ResponseSpec{ - description: 'Pet not found' - } - } - } - } - } - components: Components{ - schemas: { - 'Pet': SchemaRef(Schema{ - typ: 'object' - required: ['id', 'name'] - properties: { - 'id': SchemaRef(Schema{ - typ: 'integer' - format: 'int64' - }) - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - }) - 'NewPet': SchemaRef(Schema{ - typ: 'object' - required: ['name'] - properties: { - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - }) - 'Pets': SchemaRef(Schema{ - typ: 'array' - items: SchemaRef(Reference{ - ref: '#/components/schemas/Pet' - }) - }) - } - } -} - -const actor_spec = ActorSpecification{ - name: 'Pet Store API' - description: 'A sample API for a pet store' - structure: Struct{} - interfaces: [.openapi] - methods: [ - ActorMethod{ - name: 'listPets' - summary: 'List all pets' - example: openrpc.ExamplePairing{ - params: [ - openrpc.ExampleRef(openrpc.Example{ - name: 'Example limit' - description: 'Example Maximum number of pets to return' - value: 10 - }), - ] - result: openrpc.ExampleRef(openrpc.Example{ - name: 'Example response' - value: json.raw_decode('[ - {"id": 1, "name": "Fluffy", "tag": "dog"}, - {"id": 2, "name": "Whiskers", "tag": "cat"} - ]')! - }) - } - parameters: [ - ContentDescriptor{ - name: 'limit' - summary: 'Maximum number of pets to return' - description: 'Maximum number of pets to return' - required: false - schema: SchemaRef(Schema{ - typ: 'integer' - format: 'int32' - example: 10 - }) - }, - ] - result: ContentDescriptor{ - name: 'result' - description: 'The response of the operation.' - required: true - schema: SchemaRef(Reference{ - ref: '#/components/schemas/Pets' - }) - } - errors: [ - ErrorSpec{ - code: 400 - message: 'Invalid request' - }, - ] - }, - ActorMethod{ - name: 'createPet' - summary: 'Create a new pet' - example: openrpc.ExamplePairing{ - result: openrpc.ExampleRef(openrpc.Example{ - name: 'Example response' - value: '[]' - }) - } - result: ContentDescriptor{ - name: 'result' - description: 'The response of the operation.' - required: true - } - errors: [ - ErrorSpec{ - code: 400 - message: 'Invalid input' - }, - ] - }, - ActorMethod{ - name: 'getPet' - summary: 'Get a pet by ID' - example: openrpc.ExamplePairing{ - params: [ - openrpc.ExampleRef(openrpc.Example{ - name: 'Example petId' - description: 'Example ID of the pet to retrieve' - value: 1 - }), - ] - result: openrpc.ExampleRef(openrpc.Example{ - name: 'Example response' - value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')! - }) - } - parameters: [ - ContentDescriptor{ - name: 'petId' - summary: 'ID of the pet to retrieve' - description: 'ID of the pet to retrieve' - required: true - schema: SchemaRef(Schema{ - typ: 'integer' - format: 'int64' - example: 1 - }) - }, - ] - result: ContentDescriptor{ - name: 'result' - description: 'The response of the operation.' - required: true - schema: SchemaRef(Reference{ - ref: '#/components/schemas/Pet' - }) - } - errors: [ - ErrorSpec{ - code: 404 - message: 'Pet not found' - }, - ] - }, - ActorMethod{ - name: 'deletePet' - summary: 'Delete a pet by ID' - example: openrpc.ExamplePairing{ - params: [ - openrpc.ExampleRef(openrpc.Example{ - name: 'Example petId' - description: 'Example ID of the pet to delete' - value: 1 - }), - ] - } - parameters: [ - ContentDescriptor{ - name: 'petId' - summary: 'ID of the pet to delete' - description: 'ID of the pet to delete' - required: true - schema: SchemaRef(Schema{ - typ: 'integer' - format: 'int64' - example: 1 - }) - }, - ] - result: ContentDescriptor{ - name: 'result' - description: 'The response of the operation.' - required: true - } - errors: [ - ErrorSpec{ - code: 404 - message: 'Pet not found' - }, - ] - }, - ] - objects: [ - BaseObject{ - schema: Schema{ - typ: 'object' - properties: { - 'id': SchemaRef(Schema{ - typ: 'integer' - format: 'int64' - }) - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - required: ['id', 'name'] - } - }, - BaseObject{ - schema: Schema{ - typ: 'object' - properties: { - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - required: ['name'] - } - }, - BaseObject{ - schema: Schema{ - typ: 'array' - items: jsonschema.Items(SchemaRef(Reference{ - ref: '#/components/schemas/Pet' - })) - } - }, - ] -} - -pub fn test_from_openapi() ! { - // panic(from_openapi(openapi_spec)!) - assert from_openapi(openapi_spec)! == actor_spec -} diff --git a/libarchive/baobab/specification/from_openrpc.v b/libarchive/baobab/specification/from_openrpc.v deleted file mode 100644 index 5aff6abd..00000000 --- a/libarchive/baobab/specification/from_openrpc.v +++ /dev/null @@ -1,106 +0,0 @@ -module specification - -import incubaid.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, Method, OpenRPC } -import incubaid.herolib.schemas.jsonschema { Reference, Schema } -import incubaid.herolib.core.texttools - -// Helper function: Convert OpenRPC Method to ActorMethod -fn openrpc_method_to_actor_method(method Method) ActorMethod { - mut parameters := []ContentDescriptor{} - mut errors := []ErrorSpec{} - - // Process parameters - for param in method.params { - if param is ContentDescriptor { - parameters << param - } else { - panic('Method param should be inflated') - } - } - - // Process errors - for err in method.errors { - if err is ErrorSpec { - errors << err - } else { - panic('Method error should be inflated') - } - } - - if method.result is Reference { - panic('Method result should be inflated') - } - - return ActorMethod{ - name: method.name - description: method.description - summary: method.summary - parameters: parameters - result: method.result as ContentDescriptor - errors: errors - } -} - -// // Helper function: Extract Structs from OpenRPC Components -// fn extract_structs_from_openrpc(openrpc OpenRPC) []Struct { -// mut structs := []Struct{} - -// for schema_name, schema in openrpc.components.schemas { -// if schema is Schema { -// mut fields := []Struct.Field{} -// for field_name, field_schema in schema.properties { -// if field_schema is Schema { -// fields << Struct.Field{ -// name: field_name -// typ: field_schema.to_code() or { panic(err) } -// description: field_schema.description -// required: field_name in schema.required -// } -// } -// } - -// structs << Struct{ -// name: schema_name -// description: schema.description -// fields: fields -// } -// } -// } - -// return structs -// } - -// Converts OpenRPC to ActorSpecification -pub fn from_openrpc(spec OpenRPC) !ActorSpecification { - mut methods := []ActorMethod{} - mut objects := []BaseObject{} - - // Process methods - for method in spec.methods { - methods << openrpc_method_to_actor_method(spec.inflate_method(method)) - } - - // Process objects (schemas) - // structs := extract_structs_from_openrpc(spec) - for key, schema in spec.components.schemas { - if schema is Schema { - if schema.typ == 'object' { - objects << BaseObject{ - schema: Schema{ - ...schema - title: texttools.pascal_case(key) - id: texttools.snake_case(key) - } - } - } - } - } - - return ActorSpecification{ - name: spec.info.title - description: spec.info.description - interfaces: [.openrpc] - methods: methods - objects: objects - } -} diff --git a/libarchive/baobab/specification/from_openrpc_test.v b/libarchive/baobab/specification/from_openrpc_test.v deleted file mode 100644 index 944416a0..00000000 --- a/libarchive/baobab/specification/from_openrpc_test.v +++ /dev/null @@ -1,434 +0,0 @@ -module specification - -import incubaid.herolib.develop.codetools as code { Struct } -import incubaid.herolib.schemas.openrpc { ContentDescriptor } -import incubaid.herolib.schemas.openapi -import incubaid.herolib.schemas.jsonschema { Reference, Schema, SchemaRef } - -const openrpc_spec = openrpc.OpenRPC{ - openrpc: '1.0.0-rc1' - info: openrpc.Info{ - title: 'Petstore' - license: openrpc.License{ - name: 'MIT' - } - version: '1.0.0' - } - servers: [ - openrpc.Server{ - name: 'localhost' - url: openrpc.RuntimeExpression('http://localhost:8080') - }, - ] - methods: [ - openrpc.Method{ - name: 'list_pets' - summary: 'List all pets' - params: [ - openrpc.ContentDescriptorRef(ContentDescriptor{ - name: 'limit' - description: 'How many items to return at one time (max 100)' - required: false - schema: SchemaRef(Schema{ - typ: 'integer' - minimum: 1 - }) - }), - ] - result: openrpc.ContentDescriptorRef(ContentDescriptor{ - name: 'pets' - description: 'A paged array of pets' - schema: SchemaRef(Schema{ - typ: 'array' - items: jsonschema.Items(SchemaRef(Reference{ - ref: '#/components/schemas/Pet' - })) - }) - }) - examples: [ - openrpc.ExamplePairing{ - name: 'listPetExample' - description: 'List pet example' - }, - ] - }, - openrpc.Method{ - name: 'create_pet' - summary: 'Create a pet' - params: [ - openrpc.ContentDescriptorRef(ContentDescriptor{ - name: 'newPetName' - description: 'Name of pet to create' - required: true - schema: SchemaRef(Schema{ - typ: 'string' - }) - }), - openrpc.ContentDescriptorRef(ContentDescriptor{ - name: 'newPetTag' - description: 'Pet tag to create' - schema: SchemaRef(Schema{ - typ: 'string' - }) - }), - ] - result: openrpc.ContentDescriptorRef(Reference{ - ref: '#/components/contentDescriptors/PetId' - }) - examples: [ - openrpc.ExamplePairing{ - name: 'createPetExample' - description: 'Create pet example' - }, - ] - }, - openrpc.Method{ - name: 'get_pet' - summary: 'Info for a specific pet' - params: [ - openrpc.ContentDescriptorRef(Reference{ - ref: '#/components/contentDescriptors/PetId' - }), - ] - result: openrpc.ContentDescriptorRef(ContentDescriptor{ - name: 'pet' - description: 'Expected response to a valid request' - schema: SchemaRef(Reference{ - ref: '#/components/schemas/Pet' - }) - }) - examples: [ - openrpc.ExamplePairing{ - name: 'getPetExample' - description: 'Get pet example' - }, - ] - }, - openrpc.Method{ - name: 'update_pet' - summary: 'Update a pet' - params: [ - openrpc.ContentDescriptorRef(Reference{ - ref: '#/components/contentDescriptors/PetId' - }), - openrpc.ContentDescriptorRef(ContentDescriptor{ - name: 'updatedPetName' - description: 'New name for the pet' - required: true - schema: SchemaRef(Schema{ - typ: 'string' - }) - }), - openrpc.ContentDescriptorRef(ContentDescriptor{ - name: 'updatedPetTag' - description: 'New tag for the pet' - schema: SchemaRef(Schema{ - typ: 'string' - }) - }), - ] - result: openrpc.ContentDescriptorRef(ContentDescriptor{ - name: 'pet' - description: 'Updated pet object' - schema: SchemaRef(Reference{ - ref: '#/components/schemas/Pet' - }) - }) - examples: [ - openrpc.ExamplePairing{ - name: 'updatePetExample' - description: 'Update pet example' - }, - ] - }, - openrpc.Method{ - name: 'delete_pet' - summary: 'Delete a pet' - params: [ - openrpc.ContentDescriptorRef(Reference{ - ref: '#/components/contentDescriptors/PetId' - }), - ] - result: openrpc.ContentDescriptorRef(ContentDescriptor{ - name: 'success' - description: 'Boolean indicating success' - schema: SchemaRef(Schema{ - typ: 'boolean' - }) - }) - examples: [ - openrpc.ExamplePairing{ - name: 'deletePetExample' - description: 'Delete pet example' - }, - ] - }, - ] - components: openrpc.Components{ - content_descriptors: { - 'PetId': openrpc.ContentDescriptorRef(ContentDescriptor{ - name: 'petId' - description: 'The ID of the pet' - required: true - schema: SchemaRef(Reference{ - ref: '#/components/schemas/PetId' - }) - }) - } - schemas: { - 'PetId': SchemaRef(Schema{ - typ: 'integer' - minimum: 0 - }) - 'Pet': SchemaRef(Schema{ - typ: 'object' - properties: { - 'id': SchemaRef(Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - required: ['id', 'name'] - }) - } - } -} - -const actor_spec = ActorSpecification{ - name: 'Petstore' - structure: Struct{ - is_pub: false - } - interfaces: [.openrpc] - methods: [ - ActorMethod{ - name: 'list_pets' - summary: 'List all pets' - parameters: [ - ContentDescriptor{ - name: 'limit' - description: 'How many items to return at one time (max 100)' - required: false - schema: SchemaRef(Schema{ - typ: 'integer' - minimum: 1 - }) - }, - ] - result: ContentDescriptor{ - name: 'pets' - description: 'A paged array of pets' - schema: SchemaRef(Schema{ - typ: 'array' - items: jsonschema.Items(SchemaRef(Schema{ - typ: 'object' - properties: { - 'id': SchemaRef(Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - required: [ - 'id', - 'name', - ] - })) - }) - } - }, - ActorMethod{ - name: 'create_pet' - summary: 'Create a pet' - parameters: [ - ContentDescriptor{ - name: 'newPetName' - description: 'Name of pet to create' - required: true - schema: SchemaRef(Schema{ - typ: 'string' - }) - }, - ContentDescriptor{ - name: 'newPetTag' - description: 'Pet tag to create' - schema: SchemaRef(Schema{ - typ: 'string' - }) - }, - ] - }, - ActorMethod{ - name: 'get_pet' - summary: 'Info for a specific pet' - result: ContentDescriptor{ - name: 'pet' - description: 'Expected response to a valid request' - schema: SchemaRef(Schema{ - typ: 'object' - properties: { - 'id': SchemaRef(Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - required: [ - 'id', - 'name', - ] - }) - } - }, - ActorMethod{ - name: 'update_pet' - summary: 'Update a pet' - parameters: [ - ContentDescriptor{ - name: 'updatedPetName' - description: 'New name for the pet' - required: true - schema: SchemaRef(Schema{ - typ: 'string' - }) - }, - ContentDescriptor{ - name: 'updatedPetTag' - description: 'New tag for the pet' - schema: SchemaRef(Schema{ - typ: 'string' - }) - }, - ] - result: ContentDescriptor{ - name: 'pet' - description: 'Updated pet object' - schema: SchemaRef(Schema{ - typ: 'object' - properties: { - 'id': SchemaRef(Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - required: [ - 'id', - 'name', - ] - }) - } - }, - ActorMethod{ - name: 'delete_pet' - summary: 'Delete a pet' - result: ContentDescriptor{ - name: 'success' - description: 'Boolean indicating success' - schema: SchemaRef(Schema{ - typ: 'boolean' - }) - } - }, - ] - objects: [ - BaseObject{ - schema: Schema{ - id: 'pet' - title: 'Pet' - typ: 'object' - properties: { - 'id': SchemaRef(Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - required: ['id', 'name'] - } - }, - ] -} - -pub fn test_from_openrpc() ! { - actor_spec_ := from_openrpc(openrpc_spec)! - assert actor_spec_.methods.len == actor_spec.methods.len - assert_methods_match(actor_spec_.methods[0], actor_spec.methods[0]) - - // assert from_openrpc(openrpc_spec)! == actor_spec -} - -fn assert_methods_match(a ActorMethod, b ActorMethod) { - // Compare method names - assert a.name == b.name, 'Method names do not match: ${a.name} != ${b.name}' - - // Compare summaries - assert a.summary == b.summary, 'Method summaries do not match for method ${a.name}.' - - // Compare descriptions - assert a.description == b.description, 'Method descriptions do not match for method ${a.name}.' - - // Compare parameters count - assert a.parameters.len == b.parameters.len, 'Parameter counts do not match for method ${a.name}.' - - // Compare each parameter - for i, param_a in a.parameters { - assert_params_match(param_a, b.parameters[i], a.name) - } - - // Compare result - assert_params_match(a.result, b.result, a.name) -} - -fn assert_params_match(a ContentDescriptor, b ContentDescriptor, method_name string) { - // Compare parameter names - assert a.name == b.name, 'Parameter names do not match in method ${method_name}: ${a.name} != ${b.name}' - - // Compare summaries - assert a.summary == b.summary, 'Parameter summaries do not match in method ${method_name}: ${a.name}' - - // Compare descriptions - assert a.description == b.description, 'Parameter descriptions do not match in method ${method_name}: ${a.name}' - - // Compare required flags - assert a.required == b.required, 'Required flags do not match in method ${method_name}: ${a.name}' - - // Compare schemas - // assert_schemas_match(a.schema, b.schema, method_name, a.name) -} - -// fn assert_schemas_match(a jsonschema.SchemaRef, b jsonschema.SchemaRef, method_name string, param_name string) { -// if a is Schema && -// // Compare schema types -// assert a.typ == b.typ, 'Schema types do not match for parameter ${param_name} in method ${method_name}: ${a.typ} != ${b.typ}' - -// // Compare schema titles -// assert a.title == b.title, 'Schema titles do not match for parameter ${param_name} in method ${method_name}.' - -// // Compare schema descriptions -// assert a.description == b.description, 'Schema descriptions do not match for parameter ${param_name} in method ${method_name}.' - -// // Compare other schema fields as needed (e.g., properties, additional properties, items, etc.) -// // Add more checks here if needed for deeper schema comparisons -// } diff --git a/libarchive/baobab/specification/model.v b/libarchive/baobab/specification/model.v deleted file mode 100644 index 442a0389..00000000 --- a/libarchive/baobab/specification/model.v +++ /dev/null @@ -1,205 +0,0 @@ -module specification - -import incubaid.herolib.develop.codetools as code { Struct } -import incubaid.herolib.schemas.openapi -import incubaid.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, ExamplePairing } -import incubaid.herolib.schemas.jsonschema { Reference, Schema } - -pub struct ActorSpecification { -pub mut: - version string = '1.0.0' - openapi ?openapi.OpenAPI - openrpc ?openrpc.OpenRPC - name string @[omitempty] - description string @[omitempty] - structure Struct @[omitempty] - interfaces []ActorInterface @[omitempty] - methods []ActorMethod @[omitempty] - objects []BaseObject @[omitempty] -} - -pub enum ActorInterface { - openrpc - openapi - webui - command - http -} - -pub struct ActorMethod { -pub: - name string @[omitempty] - description string @[omitempty] - summary string - example ExamplePairing - parameters []ContentDescriptor - result ContentDescriptor - errors []ErrorSpec - category MethodCategory -} - -pub struct BaseObject { -pub mut: - schema Schema - new_method ?ActorMethod - get_method ?ActorMethod - set_method ?ActorMethod - delete_method ?ActorMethod - list_method ?ActorMethod - filter_method ?ActorMethod - other_methods []ActorMethod -} - -pub enum MethodCategory { - base_object_new - base_object_get - base_object_set - base_object_delete - base_object_list - other -} - -// returns whether method belongs to a given base object -// TODO: link to more info about base object methods -fn (m ActorMethod) belongs_to_object(obj BaseObject) bool { - base_obj_is_param := m.parameters - .filter(it.schema is Schema) - .map(it.schema as Schema) - .any(it.id == obj.schema.id) - - base_obj_is_result := if m.result.schema is Schema { - m.result.schema.id == obj.schema.id - } else { - ref := m.result.schema as Reference - ref.ref.all_after_last('/') == obj.name() - } - - return base_obj_is_param || base_obj_is_result -} - -pub fn (s ActorSpecification) validate() ActorSpecification { - mut validated_objects := []BaseObject{} - for obj_ in s.objects { - mut obj := obj_ - if obj.schema.id == '' { - obj.schema.id = obj.schema.title - } - methods := s.methods.filter(it.belongs_to_object(obj)) - - if m := methods.filter(it.is_new_method())[0] { - obj.new_method = m - } - if m := methods.filter(it.is_set_method())[0] { - obj.set_method = m - } - if m := methods.filter(it.is_get_method())[0] { - obj.get_method = m - } - if m := methods.filter(it.is_delete_method())[0] { - obj.delete_method = m - } - if m := methods.filter(it.is_list_method())[0] { - obj.list_method = m - } - validated_objects << BaseObject{ - ...obj - other_methods: methods.filter(!it.is_crudlf_method()) - } - } - return ActorSpecification{ - ...s - objects: validated_objects - } -} - -// method category returns what category a method falls under -pub fn (s ActorSpecification) method_type(method ActorMethod) MethodCategory { - return if s.is_base_object_new_method(method) { - .base_object_new - } else if s.is_base_object_get_method(method) { - .base_object_get - } else if s.is_base_object_set_method(method) { - .base_object_set - } else if s.is_base_object_delete_method(method) { - .base_object_delete - } else if s.is_base_object_list_method(method) { - .base_object_list - } else { - .other - } -} - -// a base object method is a method that is a -// CRUD+list+filter method of a base object -fn (s ActorSpecification) is_base_object_method(method ActorMethod) bool { - base_obj_is_param := method.parameters - .filter(it.schema is Schema) - .map(it.schema as Schema) - .any(it.id in s.objects.map(it.schema.id)) - - base_obj_is_result := if method.result.schema is Schema { - method.result.schema.id in s.objects.map(it.name()) - } else { - ref := method.result.schema as Reference - ref.ref.all_after_last('/') in s.objects.map(it.name()) - } - - return base_obj_is_param || base_obj_is_result -} - -fn (m ActorMethod) is_new_method() bool { - return m.name.starts_with('new') -} - -fn (m ActorMethod) is_get_method() bool { - return m.name.starts_with('get') -} - -fn (m ActorMethod) is_set_method() bool { - return m.name.starts_with('set') -} - -fn (m ActorMethod) is_delete_method() bool { - return m.name.starts_with('delete') -} - -fn (m ActorMethod) is_list_method() bool { - return m.name.starts_with('list') -} - -fn (m ActorMethod) is_filter_method() bool { - return m.name.starts_with('filter') -} - -fn (m ActorMethod) is_crudlf_method() bool { - return m.is_new_method() || m.is_get_method() || m.is_set_method() || m.is_delete_method() - || m.is_list_method() || m.is_filter_method() -} - -pub fn (o BaseObject) name() string { - return if o.schema.id.trim_space() != '' { - o.schema.id.trim_space() - } else { - o.schema.title.trim_space() - } -} - -fn (s ActorSpecification) is_base_object_new_method(method ActorMethod) bool { - return s.is_base_object_method(method) && method.name.starts_with('new') -} - -fn (s ActorSpecification) is_base_object_get_method(method ActorMethod) bool { - return s.is_base_object_method(method) && method.name.starts_with('get') -} - -fn (s ActorSpecification) is_base_object_set_method(method ActorMethod) bool { - return s.is_base_object_method(method) && method.name.starts_with('set') -} - -fn (s ActorSpecification) is_base_object_delete_method(method ActorMethod) bool { - return s.is_base_object_method(method) && method.name.starts_with('delete') -} - -fn (s ActorSpecification) is_base_object_list_method(method ActorMethod) bool { - return s.is_base_object_method(method) && method.name.starts_with('list') -} diff --git a/libarchive/baobab/specification/to_openapi.v b/libarchive/baobab/specification/to_openapi.v deleted file mode 100644 index cab8957e..00000000 --- a/libarchive/baobab/specification/to_openapi.v +++ /dev/null @@ -1,97 +0,0 @@ -module specification - -import incubaid.herolib.schemas.jsonschema { Schema, SchemaRef } -import incubaid.herolib.schemas.openapi { Components, Info, MediaType, OpenAPI, Operation, Parameter, PathItem, ResponseSpec, ServerSpec } -import net.http - -// Converts ActorSpecification to OpenAPI -pub fn (s ActorSpecification) to_openapi() OpenAPI { - if openapi_spec := s.openapi { - return openapi_spec - } - mut paths := map[string]PathItem{} - - // Map ActorMethods to paths - for method in s.methods { - op := method.to_openapi_operation() - paths['${method.http_path()}'] = match method.http_method() { - .get { - PathItem{ - get: op - } - } - else { - panic('unsupported http method') - } - } - // Assign operation to corresponding HTTP method - // TODO: what about other verbs - } - - mut schemas := map[string]SchemaRef{} - for object in s.objects { - schemas[object.schema.id] = object.to_schema() - } - - return OpenAPI{ - openapi: '3.0.0' - info: Info{ - title: s.name - summary: s.description - description: s.description - version: '1.0.0' - } - servers: [ - ServerSpec{ - url: 'http://localhost:8080' - description: 'Default server' - }, - ] - paths: paths - components: Components{ - schemas: schemas - } - } -} - -fn (bo BaseObject) to_schema() Schema { - return Schema{} -} - -fn (m ActorMethod) http_path() string { - return m.name -} - -fn (m ActorMethod) http_method() http.Method { - return .get -} - -fn (method ActorMethod) to_openapi_operation() Operation { - mut op := Operation{ - summary: method.summary - description: method.description - operation_id: method.name - } - - // Convert parameters to OpenAPI format - for param in method.parameters { - op.parameters << Parameter{ - name: param.name - in_: 'query' // Default to query parameters; adjust based on function context - description: param.description - required: param.required - schema: param.schema - } - } - - // if method.is_void() - op.responses['200'] = ResponseSpec{ - description: method.description - content: { - 'application/json': MediaType{ - schema: method.result.schema - } - } - } - return op -} diff --git a/libarchive/baobab/specification/to_openapi_test.v b/libarchive/baobab/specification/to_openapi_test.v deleted file mode 100644 index f4bad6a1..00000000 --- a/libarchive/baobab/specification/to_openapi_test.v +++ /dev/null @@ -1,183 +0,0 @@ -module specification - -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.schemas.jsonschema { Schema, SchemaRef } -import incubaid.herolib.schemas.openapi -import incubaid.herolib.schemas.openrpc - -const actor_spec = ActorSpecification{ - name: 'Petstore' - structure: code.Struct{ - is_pub: false - } - interfaces: [.openrpc] - methods: [ - ActorMethod{ - name: 'list_pets' - summary: 'List all pets' - parameters: [ - openrpc.ContentDescriptor{ - name: 'limit' - description: 'How many items to return at one time (max 100)' - required: false - schema: SchemaRef(Schema{ - typ: 'integer' - minimum: 1 - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pets' - description: 'A paged array of pets' - schema: SchemaRef(Schema{ - typ: 'array' - items: jsonschema.Items(SchemaRef(Schema{ - typ: 'object' - properties: { - 'id': SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - required: [ - 'id', - 'name', - ] - })) - }) - } - }, - ActorMethod{ - name: 'create_pet' - summary: 'Create a pet' - parameters: [ - openrpc.ContentDescriptor{ - name: 'newPetName' - description: 'Name of pet to create' - required: true - schema: SchemaRef(Schema{ - typ: 'string' - }) - }, - openrpc.ContentDescriptor{ - name: 'newPetTag' - description: 'Pet tag to create' - schema: SchemaRef(Schema{ - typ: 'string' - }) - }, - ] - }, - ActorMethod{ - name: 'get_pet' - summary: 'Info for a specific pet' - result: openrpc.ContentDescriptor{ - name: 'pet' - description: 'Expected response to a valid request' - schema: SchemaRef(Schema{ - typ: 'object' - properties: { - 'id': SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - required: [ - 'id', - 'name', - ] - }) - } - }, - ActorMethod{ - name: 'update_pet' - summary: 'Update a pet' - parameters: [ - openrpc.ContentDescriptor{ - name: 'updatedPetName' - description: 'New name for the pet' - required: true - schema: SchemaRef(Schema{ - typ: 'string' - }) - }, - openrpc.ContentDescriptor{ - name: 'updatedPetTag' - description: 'New tag for the pet' - schema: SchemaRef(Schema{ - typ: 'string' - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pet' - description: 'Updated pet object' - schema: SchemaRef(Schema{ - typ: 'object' - properties: { - 'id': SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - required: [ - 'id', - 'name', - ] - }) - } - }, - ActorMethod{ - name: 'delete_pet' - summary: 'Delete a pet' - result: openrpc.ContentDescriptor{ - name: 'success' - description: 'Boolean indicating success' - schema: SchemaRef(Schema{ - typ: 'boolean' - }) - } - }, - ] - objects: [ - BaseObject{ - schema: Schema{ - id: 'pet' - title: 'Pet' - typ: 'object' - properties: { - 'id': SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/PetId' - }) - 'name': SchemaRef(Schema{ - typ: 'string' - }) - 'tag': SchemaRef(Schema{ - typ: 'string' - }) - } - required: ['id', 'name'] - } - }, - ] -} - -// Converts ActorSpecification to OpenAPI -pub fn test_specification_to_openapi() { - panic(actor_spec.to_openapi()) -} diff --git a/libarchive/baobab/specification/to_openrpc.v b/libarchive/baobab/specification/to_openrpc.v deleted file mode 100644 index f273fc4a..00000000 --- a/libarchive/baobab/specification/to_openrpc.v +++ /dev/null @@ -1,71 +0,0 @@ -module specification - -import incubaid.herolib.schemas.openrpc { Components, OpenRPC } -import incubaid.herolib.schemas.jsonschema { SchemaRef } -import incubaid.herolib.schemas.jsonschema.codegen - -// pub fn from_openrpc(spec openrpc.OpenRPC) !ActorSpecification { -// // Extract Actor metadata from OpenRPC info -// // actor_name := openrpc_doc.info.title -// // actor_description := openrpc_doc.info.description - -// // // Generate methods -// // mut methods := []ActorMethod{} -// // for method in openrpc_doc.methods { -// // method_code := method.to_code()! // Using provided to_code function -// // methods << ActorMethod{ -// // name: method.name -// // func: method_code -// // } -// // } - -// // // Generate BaseObject structs from schemas -// // mut objects := []BaseObject{} -// // for key, schema_ref in openrpc_doc.components.schemas { -// // struct_obj := schema_ref.to_code()! // Assuming schema_ref.to_code() converts schema to Struct -// // // objects << BaseObject{ -// // // structure: code.Struct{ -// // // name: struct_obj.name -// // // } -// // // } -// // } - -// // Build the Actor struct -// return ActorSpecification{ -// // name: actor_name -// // description: actor_description -// // methods: methods -// // objects: objects -// } -// } - -pub fn (specification ActorSpecification) to_openrpc() OpenRPC { - mut schemas := map[string]SchemaRef{} - for obj in specification.objects { - schemas[obj.schema.id] = obj.schema - // for child in obj.children { - // schemas[child.name] = struct_to_schema(child) - // } - } - return OpenRPC{ - info: openrpc.Info{ - title: specification.name.title() - version: '1.0.0' - } - methods: specification.methods.map(method_to_openrpc_method(it)) - components: Components{ - schemas: schemas - } - } -} - -pub fn method_to_openrpc_method(method ActorMethod) openrpc.Method { - return openrpc.Method{ - name: method.name - summary: method.summary - description: method.description - params: method.parameters.map(openrpc.ContentDescriptorRef(it)) - result: openrpc.ContentDescriptorRef(method.result) - errors: method.errors.map(openrpc.ErrorRef(it)) - } -} diff --git a/libarchive/baobab/stage/README.md b/libarchive/baobab/stage/README.md deleted file mode 100644 index 5829643b..00000000 --- a/libarchive/baobab/stage/README.md +++ /dev/null @@ -1,140 +0,0 @@ - -# Stage Module - -The **Stage** module is a core component of the **Baobab** (Base Object and Actor Backend) library. It provides the infrastructure for handling RPC-based communication and managing the lifecycle of **Actors** and **Actions**. This module facilitates processing incoming requests, converting them to actions, and ensuring their correct execution. - -## Architecture Overview - -The **Stage** module operates based on the following architecture: - -1. **RPC Request Handling**: - - An **Interface Handler** receives an RPC request. Supported interfaces include: - - **OpenRPC** - - **JSON-RPC** - - **OpenAPI** - -2. **Action Creation**: - - The **Interface Handler** converts the incoming request into an **Action**, which represents the task to be executed. - -3. **Action Execution**: - - The **Interface Handler** passes the **Action** to the **Director** for coordinated execution. - - (Note: Currently, the **Director** is not fully implemented. Actions are passed directly to the **Actor** for execution.) - -4. **Actor Processing**: - - The **Actor** uses its `act` method to execute the **Action**. - - The result of the **Action** is stored in its `result` field, and the **Action** is returned. - -5. **RPC Response Generation**: - - The **Interface Handler** converts the resulting **Action** back into the appropriate RPC response format and returns it. - ---- - -## Key Components - -### **Interface Handlers** -- **Responsibilities**: - - Receive and parse incoming RPC requests. - - Convert requests into **Actions**. - - Convert resulting **Actions** into appropriate RPC responses. -- Files: - - `interfaces/jsonrpc_interface.v` - - `interfaces/openapi_interface.v` - -### **Director** -- **Responsibilities**: - - (Planned) Coordinate the execution of **Actions**. - - Handle retries, timeouts, and error recovery. -- File: - - `director.v` - -### **Actors** -- **Responsibilities**: - - Execute **Actions** using their `act` method. - - Populate the `result` field of **Actions** with the execution result. -- File: - - `actor.v` - -### **Actions** -- **Responsibilities**: - - Represent tasks to be executed by **Actors**. - - Carry results back after execution. -- File: - - `action.v` - -### **Executor** -- **Responsibilities**: - - Manage the assignment of **Actions** to **Actors**. -- File: - - `executor.v` - ---- - -## Directory Structure - -``` -stage/ - interfaces/ - jsonrpc_interface.v # Converts JSON-RPC requests to Actions - openapi_interface.v # Converts OpenAPI requests to Actions - actor.v # Defines the Actor and its behavior - action.v # Defines the Action structure and utilities - executor.v # Executes Actions on Actors - director.v # (Planned) Coordinates actors, actions, and retries -``` - ---- - -## Workflow Example - -### 1. Receiving an RPC Request -An RPC request is received by an interface handler: - -```json -{ - "jsonrpc": "2.0", - "method": "doSomething", - "params": { "key": "value" }, - "id": 1 -} -``` - -### 2. Converting the Request to an Action -The interface handler converts the request into an **Action**: - -```v -action := jsonrpc_interface.jsonrpc_to_action(request) -``` - -### 3. Executing the Action -The action is passed directly to an **Actor** for execution: - -```v -actor := MyActor{id: "actor-1"} -resulting_action := actor.act(action) -``` - -### 4. Returning the RPC Response -The interface handler converts the resulting **Action** back into a JSON-RPC response: - -```json -{ - "jsonrpc": "2.0", - "result": { "status": "success", "data": "..." }, - "id": 1 -} -``` - ---- - -## Future Improvements - -- **Director Implementation**: - - Add retries and timeout handling for actions. - - Provide better coordination for complex workflows. - -- **Enhanced Interfaces**: - - Add support for more RPC protocols. - ---- - -This module is a crucial building block of the **Baobab** library, designed to streamline RPC-based communication and task execution with flexibility and scalability. diff --git a/libarchive/baobab/stage/action.v b/libarchive/baobab/stage/action.v deleted file mode 100644 index 7e575f5c..00000000 --- a/libarchive/baobab/stage/action.v +++ /dev/null @@ -1,15 +0,0 @@ -module stage - -// import incubaid.herolib.core.smartid - -pub struct Action { -pub mut: - id string - name string - priority int = 10 // 0 is highest, do 10 as default - params string // json encoded params - result string // can be used to remember outputs - // run bool = true // certain actions can be defined but meant to be executed directly - comments string - done bool // if done then no longer need to process -} diff --git a/libarchive/baobab/stage/action_client.v b/libarchive/baobab/stage/action_client.v deleted file mode 100644 index d466f3e2..00000000 --- a/libarchive/baobab/stage/action_client.v +++ /dev/null @@ -1,47 +0,0 @@ -module stage - -import incubaid.herolib.core.redisclient - -// Processor struct for managing procedure calls -pub struct Client { -pub mut: - rpc redisclient.RedisRpc // Redis RPC mechanism -} - -// Parameters for processing a procedure call -@[params] -pub struct Params { -pub: - timeout int // Timeout in seconds -} - -pub struct ClientConfig { - ActorConfig -pub: - redis_url string = 'localhost:6379' // url to redis server running -} - -pub fn new_client(config ActorConfig) !Client { - mut redis := redisclient.new(config.redis_url)! - mut rpc_q := redis.rpc_get(config.redis_queue_name()) - - return Client{ - rpc: rpc_q - } -} - -// Process the procedure call -pub fn (mut p Client) call_to_action(action Action, params Params) !Action { - // Use RedisRpc's `call` to send the call and wait for the response - response_data := p.rpc.call(redisclient.RPCArgs{ - cmd: action.name - data: action.params - timeout: u64(params.timeout * 1000) // Convert seconds to milliseconds - wait: true - })! - - return Action{ - ...action - result: response_data - } -} diff --git a/libarchive/baobab/stage/actor.v b/libarchive/baobab/stage/actor.v deleted file mode 100644 index e3d909fe..00000000 --- a/libarchive/baobab/stage/actor.v +++ /dev/null @@ -1,79 +0,0 @@ -module stage - -import incubaid.herolib.baobab.osis { OSIS } -import incubaid.herolib.core.redisclient - -@[heap] -pub interface IActor { - name string -mut: - act(Action) !Action -} - -pub struct Actor { - ActorConfig -mut: - osis OSIS -} - -@[params] -pub struct ActorConfig { -pub: - name string - version string - redis_url string = 'localhost:6379' -} - -pub fn (config ActorConfig) redis_queue_name() string { - mut str := 'actor_${config.name}' - if config.version != '' { - str += '_${config.version}' - } - return str -} - -pub fn new_actor(config ActorConfig) !Actor { - return Actor{ - ActorConfig: config - osis: osis.new()! - } -} - -pub fn (a ActorConfig) get_redis_rpc() !redisclient.RedisRpc { - mut redis := redisclient.new(a.redis_url)! - return redis.rpc_get(a.redis_queue_name()) -} - -pub fn (a ActorConfig) version(v string) ActorConfig { - return ActorConfig{ - ...a - version: v - } -} - -pub fn (a ActorConfig) example() ActorConfig { - return ActorConfig{ - ...a - version: 'example' - } -} - -pub fn (mut a IActor) handle(method string, data string) !string { - action := a.act( - name: method - params: data - )! - return action.result -} - -// // Actor listens to the Redis queue for method invocations -// pub fn (mut a IActor) run() ! { -// mut redis := redisclient.new('localhost:6379') or { panic(err) } -// mut rpc := redis.rpc_get(a.name) - -// println('Actor started and listening for tasks...') -// for { -// rpc.process(a.handle)! -// time.sleep(time.millisecond * 100) // Prevent CPU spinning -// } -// } diff --git a/libarchive/baobab/stage/config.v b/libarchive/baobab/stage/config.v deleted file mode 100644 index 991f8aa3..00000000 --- a/libarchive/baobab/stage/config.v +++ /dev/null @@ -1 +0,0 @@ -module stage diff --git a/libarchive/baobab/stage/error.v b/libarchive/baobab/stage/error.v deleted file mode 100644 index e602e20f..00000000 --- a/libarchive/baobab/stage/error.v +++ /dev/null @@ -1,33 +0,0 @@ -module stage - -// Error struct for error handling -pub struct ActionError { - reason ErrorReason -} - -// Enum for different error reasons -pub enum ErrorReason { - timeout - serialization_failed - deserialization_failed - enqueue_failed -} - -pub fn (err ActionError) code() int { - return match err.reason { - .timeout { 408 } // HTTP 408 Request Timeout - .serialization_failed { 500 } // HTTP 500 Internal Server Error - .deserialization_failed { 500 } // HTTP 500 Internal Server Error - .enqueue_failed { 503 } // HTTP 503 Service Unavailable - } -} - -pub fn (err ActionError) msg() string { - explanation := match err.reason { - .timeout { 'The procedure call timed out.' } - .serialization_failed { 'Failed to serialize the procedure call.' } - .deserialization_failed { 'Failed to deserialize the procedure response.' } - .enqueue_failed { 'Failed to enqueue the procedure response.' } - } - return 'Procedure failed: ${explanation}' -} diff --git a/libarchive/baobab/stage/interfaces/jsonrpc.v b/libarchive/baobab/stage/interfaces/jsonrpc.v deleted file mode 100644 index d907b10e..00000000 --- a/libarchive/baobab/stage/interfaces/jsonrpc.v +++ /dev/null @@ -1,16 +0,0 @@ -module interfaces - -import incubaid.herolib.schemas.jsonrpc -import incubaid.herolib.baobab.stage { Action } - -pub fn action_from_jsonrpc_request(request jsonrpc.Request) Action { - return Action{ - id: request.id - name: request.method - params: request.params - } -} - -pub fn action_to_jsonrpc_response(action Action) jsonrpc.Response { - return jsonrpc.new_response(action.id, action.result) -} diff --git a/libarchive/baobab/stage/interfaces/openapi.v b/libarchive/baobab/stage/interfaces/openapi.v deleted file mode 100644 index d56f567d..00000000 --- a/libarchive/baobab/stage/interfaces/openapi.v +++ /dev/null @@ -1,48 +0,0 @@ -module interfaces - -import rand -import x.json2 as json { Any } -import incubaid.herolib.baobab.stage { Action, Client } -import incubaid.herolib.schemas.jsonrpc -import incubaid.herolib.schemas.openapi - -pub struct OpenAPIInterface { -pub mut: - client Client -} - -pub fn new_openapi_interface(client Client) &OpenAPIInterface { - return &OpenAPIInterface{client} -} - -pub fn (mut i OpenAPIInterface) handle(request openapi.Request) !openapi.Response { - // Convert incoming OpenAPI request to a procedure call - action := action_from_openapi_request(request) - response := i.client.call_to_action(action) or { return err } - return action_to_openapi_response(response) -} - -pub fn action_from_openapi_request(request openapi.Request) Action { - mut params := []Any{} - if request.arguments.len > 0 { - params << request.arguments.values() - } - if request.body != '' { - params << request.body - } - if request.parameters.len > 0 { - params << json.encode(request.parameters) - } - - return Action{ - id: rand.uuid_v4() - name: request.operation.operation_id - params: json.encode(params.str()) - } -} - -pub fn action_to_openapi_response(action Action) openapi.Response { - return openapi.Response{ - body: action.result - } -} diff --git a/libarchive/baobab/stage/interfaces/openrpc.v b/libarchive/baobab/stage/interfaces/openrpc.v deleted file mode 100644 index 771aa138..00000000 --- a/libarchive/baobab/stage/interfaces/openrpc.v +++ /dev/null @@ -1,29 +0,0 @@ -module interfaces - -import incubaid.herolib.baobab.stage { Client } -import incubaid.herolib.schemas.jsonrpc - -// handler for test echoes JSONRPC Request as JSONRPC Response -fn handler(request jsonrpc.Request) !jsonrpc.Response { - return jsonrpc.Response{ - jsonrpc: request.jsonrpc - id: request.id - result: request.params - } -} - -pub struct OpenRPCInterface { -pub mut: - client Client -} - -pub fn new_openrpc_interface(client Client) &OpenRPCInterface { - return &OpenRPCInterface{client} -} - -pub fn (mut i OpenRPCInterface) handle(request jsonrpc.Request) !jsonrpc.Response { - // Convert incoming OpenAPI request to a procedure call - action := action_from_jsonrpc_request(request) - response := i.client.call_to_action(action)! - return action_to_jsonrpc_response(response) -} diff --git a/libarchive/baobab/stage/interfaces/reflection_openapi.v b/libarchive/baobab/stage/interfaces/reflection_openapi.v deleted file mode 100644 index 964e6063..00000000 --- a/libarchive/baobab/stage/interfaces/reflection_openapi.v +++ /dev/null @@ -1,91 +0,0 @@ -module interfaces - -// import os -// import time -// import veb -// import x.json2 {Any} -// import net.http -import incubaid.herolib.baobab.stage { Action } -import incubaid.herolib.schemas.openapi { Request } - -pub fn openapi_request_to_action(request Request) Action { - // // Convert incoming OpenAPI request to a procedure call - // mut params := []Any{} - - // if request.arguments.len > 0 { - // params << request.arguments.values().map(it.str()).clone() - // } - - // if request.body != '' { - // params << request.body - // } - - // if request.parameters != '' { - // params << request.body - // } - - // if request.parameters.len != 0 { - // mut param_map := map[string]Any{} // Store parameters with correct types - - // for param_name, param_value in request.parameters { - // operation_param := request.operation.parameters.filter(it.name == param_name) - // if operation_param.len > 0 { - // param_schema := operation_param[0].schema as Schema - // param_type := param_schema.typ - // param_format := param_schema.format - - // // Convert parameter value to corresponding type - // match param_type { - // 'integer' { - // match param_format { - // 'int32' { - // param_map[param_name] = param_value.int() // Convert to int - // } - // 'int64' { - // param_map[param_name] = param_value.i64() // Convert to i64 - // } - // else { - // param_map[param_name] = param_value.int() // Default to int - // } - // } - // } - // 'string' { - // param_map[param_name] = param_value // Already a string - // } - // 'boolean' { - // param_map[param_name] = param_value.bool() // Convert to bool - // } - // 'number' { - // match param_format { - // 'float' { - // param_map[param_name] = param_value.f32() // Convert to float - // } - // 'double' { - // param_map[param_name] = param_value.f64() // Convert to double - // } - // else { - // param_map[param_name] = param_value.f64() // Default to double - // } - // } - // } - // else { - // param_map[param_name] = param_value // Leave as string for unknown types - // } - // } - // } else { - // // If the parameter is not defined in the OpenAPI operation, skip or log it - // println('Unknown parameter: $param_name') - // } - // } - - // // Encode the parameter map to JSON if needed - // params << json.encode(param_map.str()) - // } - - // call := Action{ - // name: request.operation.operation_id - // params_json: json2.encode(params.str()) // Keep as a string since ProcedureCall expects a string - // } - // return call - return Action{} -} diff --git a/libarchive/baobab/stage/interfaces/server_http.v b/libarchive/baobab/stage/interfaces/server_http.v deleted file mode 100644 index a3bba13c..00000000 --- a/libarchive/baobab/stage/interfaces/server_http.v +++ /dev/null @@ -1,43 +0,0 @@ -module interfaces - -import incubaid.herolib.schemas.openapi { OpenAPI } -import incubaid.herolib.baobab.stage { ClientConfig } -import incubaid.herolib.schemas.openrpc { OpenRPC } -import veb - -pub struct HTTPServer { - veb.Controller -} - -pub struct Context { - veb.Context -} - -pub struct HTTPServerConfig { - ClientConfig -pub: - openapi_specification OpenAPI - openrpc_specification OpenRPC -} - -pub fn new_http_server() !&HTTPServer { - mut s := &HTTPServer{} - - // client := actor.new_client(cfg.ClientConfig)! - - // openapi_proxy := new_openapi_proxy( - // client: new_client(cfg.ClientConfig)! - // specification: cfg.openapi_spec - // ) - - // mut openrpc_controller := openrpc.new_http_controller( - // specification: cfg.openrpc_specification - // handler: new_openrpc_interface(client) - // ) - // s.register_controller[openrpc.HTTPController, Context]('/openrpc', mut openrpc_controller)! - return s -} - -pub fn (mut server HTTPServer) run() { - veb.run[HTTPServer, Context](mut server, 8082) -} diff --git a/libarchive/buildah/buildah_core.v b/libarchive/buildah/buildah_core.v deleted file mode 100644 index c90ccc4b..00000000 --- a/libarchive/buildah/buildah_core.v +++ /dev/null @@ -1,164 +0,0 @@ -module buildah - -import incubaid.herolib.osal.core as osal -// import incubaid.herolib.ui.console -import incubaid.herolib.installers.lang.herolib -import incubaid.herolib.core.pathlib -import incubaid.herolib.builder -import incubaid.herolib.virt.utils -import os -import json - -// Use shared container status from utils -pub type ContainerStatus = utils.ContainerStatus - -pub struct IPAddress { -pub mut: - ipv4 string - ipv6 string -} -// need to fill in what is relevant -@[heap] -pub struct BuildAHContainer { -pub mut: - id string - builder bool - imageid string - imagename string - containername string - //TODO: not sure all below is needed - hero_in_container bool //once the hero has been installed this is on, does it once per session -// created time.Time -// ssh_enabled bool // if yes make sure ssh is enabled to the container -// ipaddr IPAddress -// forwarded_ports []string -// mounts []ContainerVolume -// ssh_port int // ssh port on node that is used to get ssh -// ports []string -// networks []string -// labels map[string]string @[str: skip] -// status ContainerStatus -// memsize int // in MB -// command string -} - -@[params] -pub struct RunArgs { -pub mut: - cmd string - // TODO:/.. -} - -@[params] -pub struct PackageInstallArgs { -pub mut: - names string - // TODO:/.. -} - -// TODO: mimic osal.package_install('mc,tmux,git,rsync,curl,screen,redis,wget,git-lfs')! - -// pub fn (mut self BuildAHContainer) package_install(args PackageInstallArgs) !{ -// //TODO -// names := texttools.to_array(args.names) -// //now check which OS, need to make platform function on container level so we know which platform it is -// panic("implement") -// } - -pub fn (mut self BuildAHContainer) copy(src string, dest string) ! { - mut executor := utils.buildah_exec(false) - executor.exec(['copy', self.id, src, dest]) or { - return utils.new_build_error('copy', self.containername, err.code(), err.msg(), err.msg()) - } -} - -pub fn (mut self BuildAHContainer) shell() ! { - mut executor := utils.buildah_exec(false) - executor.exec_interactive(['run', '--terminal', '--env', 'TERM=xterm', self.id, '/bin/bash']) or { - return utils.new_build_error('shell', self.containername, err.code(), err.msg(), err.msg()) - } -} - -pub fn (mut self BuildAHContainer) clean() ! { - cmd := ' - #set -x - set +e - rm -rf /root/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/share/doc - #pacman -Rns $(pacman -Qtdq) --noconfirm - #pacman -Scc --noconfirm - rm -rf /var/lib/pacman/sync/* - rm -rf /tmp/* - rm -rf /var/tmp/* - find /var/log -type f -name "*.log" -exec truncate -s 0 {} \\; - rm -rf /home/*/.cache/* - rm -rf /usr/share/doc/* - rm -rf /usr/share/man/* - rm -rf /usr/share/info/* - rm -rf /usr/share/licenses/* - find /usr/share/locale -mindepth 1 -maxdepth 1 ! -name "en*" -exec rm -rf {} \\; - rm -rf /usr/share/i18n - rm -rf /usr/share/icons/* - rm -rf /usr/lib/modules/* - rm -rf /var/cache/pacman - journalctl --vacuum-time=1s - - ' - self.exec(cmd: cmd, stdout: false)! -} - -pub fn (mut self BuildAHContainer) delete() ! { - mut executor := utils.buildah_exec(false) - executor.exec(['rm', self.containername]) or { - return utils.new_build_error('delete', self.containername, err.code(), err.msg(), err.msg()) - } -} - -pub fn (mut self BuildAHContainer) inspect() !BuilderInfo { - cmd := 'buildah inspect ${self.containername}' - job := self.exec(cmd:(cmd)! - out:=job.output - mut r := json.decode(BuilderInfo, out) or { - return error('Failed to decode JSON for inspect: ${err}') - } - return r -} - -// mount the build container to a path and return the path where its mounted -pub fn (mut self BuildAHContainer) mount_to_path() !string { - cmd := 'buildah mount ${self.containername}' - out := self.exec(cmd:cmd)! - return out.trim_space() -} - -pub fn (mut self BuildAHContainer) commit(image_name string) ! { - // Validate image name - validated_name := utils.validate_image_name(image_name) or { - return utils.new_validation_error('image_name', image_name, err.msg()) - } - - mut executor := utils.buildah_exec(false) - executor.exec(['commit', self.containername, validated_name]) or { - return utils.new_build_error('commit', self.containername, err.code(), err.msg(), err.msg()) - } -} - -pub fn (self BuildAHContainer) set_entrypoint(entrypoint string) ! { - mut executor := utils.buildah_exec(false) - executor.exec(['config', '--entrypoint', entrypoint, self.containername]) or { - return utils.new_build_error('set_entrypoint', self.containername, err.code(), err.msg(), err.msg()) - } -} - -pub fn (self BuildAHContainer) set_workingdir(workdir string) ! { - mut executor := utils.buildah_exec(false) - executor.exec(['config', '--workingdir', workdir, self.containername]) or { - return utils.new_build_error('set_workingdir', self.containername, err.code(), err.msg(), err.msg()) - } -} - -pub fn (self BuildAHContainer) set_cmd(command string) ! { - mut executor := utils.buildah_exec(false) - executor.exec(['config', '--cmd', command, self.containername]) or { - return utils.new_build_error('set_cmd', self.containername, err.code(), err.msg(), err.msg()) - } -} diff --git a/libarchive/buildah/buildah_core_installers.v b/libarchive/buildah/buildah_core_installers.v deleted file mode 100644 index de83c252..00000000 --- a/libarchive/buildah/buildah_core_installers.v +++ /dev/null @@ -1,31 +0,0 @@ -module buildah - -import incubaid.herolib.osal.core as osal -// import incubaid.herolib.ui.console -import incubaid.herolib.installers.lang.herolib -import incubaid.herolib.core.pathlib -import os -import json - -pub fn (mut self BuildAHContainer) install_zinit() ! { - // https://github.com/threefoldtech/zinit - self.hero_copy()! - self.hero_play_execute('!!installer.zinit') - // TODO: implement by making sure hero is in the build context and then use hero cmd to install this - self.set_entrypoint('/sbin/zinit init --container')! -} - -pub fn (mut self BuildAHContainer) install_herodb() ! { - self.install_zinit()! - // the hero database gets installed and put in zinit for automatic start - self.hero_play_execute('!!installer.herodb') - // TODO: the hero_play needs to be implemented -} - -// copies the hero from host into guest -pub fn (mut self BuildAHContainer) install_mycelium() ! { - self.install_zinit()! - // the mycelium database gets installed and put in zinit for automatic start - self.hero_play_execute('!!installer.mycelium') - // TODO: the hero_play needs to be implemented -} diff --git a/libarchive/buildah/buildah_exec.v b/libarchive/buildah/buildah_exec.v deleted file mode 100644 index d1c29ea0..00000000 --- a/libarchive/buildah/buildah_exec.v +++ /dev/null @@ -1,86 +0,0 @@ -module buildah - -import incubaid.herolib.osal.core as osal -import incubaid.herolib.core.pathlib -import os - -@[params] -pub struct Command { -pub mut: - name string // to give a name to your command, good to see logs... - cmd string - description string - timeout int = 3600 // timeout in sec - stdout bool = true - stdout_log bool = true - raise_error bool = true // if false, will not raise an error but still error report - ignore_error bool // means if error will just exit and not raise, there will be no error reporting - work_folder string // location where cmd will be executed - environment map[string]string // env variables - ignore_error_codes []int - scriptpath string // is the path where the script will be put which is executed - scriptkeep bool // means we don't remove the script - debug bool // if debug will put +ex in the script which is being executed and will make sure script stays - shell bool // means we will execute it in a shell interactive - retry int - interactive bool = true - async bool - runtime osal.RunTime -} - -pub enum RunTime { - bash - python - heroscript - herocmd - v -} - -// should use builders underneith -pub fn (mut self BuildAHContainer) exec(cmd Command) !osal.Job { - // make sure we have hero in the hostnode of self - self.hero_copy()! - - mut rt := RunTime.bash - - scriptpath := osal.cmd_to_script_path(cmd: cmd.cmd, runtime: cmd.runtime)! - - if cmd.runtime == .heroscript || cmd.runtime == .herocmd { - self.hero_copy()! - } - - script_basename := os.base(scriptpath) - script_path_in_container := '/tmp/${script_basename}' - - self.copy(scriptpath, script_path_in_container)! - // console.print_debug("copy ${scriptpath} into container '${self.containername}'") - cmd_str := 'buildah run ${self.id} ${script_path_in_container}' - // console.print_debug(cmd_str) - - if cmd.runtime == .heroscript || cmd.runtime == .herocmd { - self.hero_copy()! - } - mut j := osal.exec( - name: cmd.name - cmd: cmd_str - description: cmd.description - timeout: cmd.timeout - stdout: cmd.stdout - stdout_log: cmd.stdout_log - raise_error: cmd.raise_error - ignore_error: cmd.ignore_error - ignore_error_codes: cmd.ignore_error_codes - scriptpath: cmd.scriptpath - scriptkeep: cmd.scriptkeep - debug: cmd.debug - shell: cmd.shell - retry: cmd.retry - interactive: cmd.interactive - async: cmd.async - ) or { - mut epath := pathlib.get_file(path: scriptpath, create: false)! - c := epath.read()! - return error('cannot execute:\n${c}\nerror:\n${err}') - } - return j -} diff --git a/libarchive/buildah/buildah_factory.v b/libarchive/buildah/buildah_factory.v deleted file mode 100644 index a59078a2..00000000 --- a/libarchive/buildah/buildah_factory.v +++ /dev/null @@ -1,110 +0,0 @@ -module buildah - -import incubaid.herolib.osal.core as osal -import incubaid.herolib.ui.console -import incubaid.herolib.virt.utils -import json - -@[params] -pub struct BuildAHNewArgs { -pub mut: - herocompile bool - reset bool - default_image string = 'docker.io/ubuntu:latest' - install bool = true // make sure buildah is installed -} - -// Use shared BuildPlatformType from utils -pub type BuildPlatformType = utils.BuildPlatformType - -pub struct BuildAHFactory { -pub mut: - default_image string - platform BuildPlatformType - executor utils.Executor -} - -pub fn new(args BuildAHNewArgs) !BuildAHFactory { - // Validate default image - validated_image := utils.validate_image_name(args.default_image) or { - return utils.new_validation_error('default_image', args.default_image, err.msg()) - } - - mut bahf := BuildAHFactory{ - default_image: validated_image - executor: utils.buildah_exec(false) - } - - if args.reset { - bahf.reset() or { - return utils.new_build_error('reset', 'factory', err.code(), err.msg(), err.msg()) - } - } - - // if args.herocompile { - // bahf.builder = builder.hero_compile()! - // } - return bahf -} - -@[params] -pub struct BuildAhContainerNewArgs { -pub mut: - name string = 'default' - from string - delete bool = true -} - -// TODO: implement, missing parts -// TODO: need to supprot a docker builder if we are on osx or windows, so we use the builders functionality as base for executing, not directly osal -pub fn (mut self BuildAHFactory) new(args_ BuildAhContainerNewArgs) !BuildAHContainer { - mut args := args_ - if args.delete { - self.delete(args.name)! - } - if args.from != '' { - args.from = self.default_image - } - mut c := BuildAHContainer{ - name: args.name - from: args.from - } - return c -} - -fn (mut self BuildAHFactory) list() ![]BuildAHContainer { - result := self.executor.exec(['containers', '--json']) or { - return utils.new_build_error('list', 'containers', err.code(), err.msg(), err.msg()) - } - - return utils.parse_json_output[BuildAHContainer](result.output) or { - return utils.new_build_error('list', 'containers', 1, err.msg(), err.msg()) - } -} - -// delete all builders -pub fn (mut self BuildAHFactory) reset() ! { - console.print_debug('remove all buildah containers') - self.executor.exec(['rm', '-a']) or { - return utils.new_build_error('reset', 'all', err.code(), err.msg(), err.msg()) - } -} - -pub fn (mut self BuildAHFactory) delete(name string) ! { - if self.exists(name)! { - console.print_debug('remove ${name}') - self.executor.exec(['rm', name]) or { - return utils.new_build_error('delete', name, err.code(), err.msg(), err.msg()) - } - } -} - -pub fn (mut self BuildAHFactory) exists(name string) !bool { - containers := self.list()! - for container in containers { - if container.containername == name { - return true - } - } - return false -} diff --git a/libarchive/buildah/buildah_hero.v b/libarchive/buildah/buildah_hero.v deleted file mode 100644 index 0126b23e..00000000 --- a/libarchive/buildah/buildah_hero.v +++ /dev/null @@ -1,38 +0,0 @@ -module buildah - -import incubaid.herolib.osal.core as osal - -// copies the hero from host into guest and then execute the heroscript or commandline -pub fn (mut self BuildAHContainer) hero_cmd_execute(cmd string) ! { - self.hero_copy()! - self.exec(cmd: cmd, runtime: .herocmd)! -} - -// send a hero play command to the buildah container -pub fn (mut self BuildAHContainer) hero_play_execute(cmd string) ! { - self.hero_copy()! - panic('implement') -} - -pub fn (mut self BuildAHContainer) hero_execute_script(cmd string) ! { - self.hero_copy()! - self.exec(cmd: cmd, runtime: .heroscript)! -} - -// copies the hero from host into guest -pub fn (mut self BuildAHContainer) hero_copy() ! { - // TODO: check we are on linux, check also the platformtype arm or intel, if not right platform then build hero in container - - panic('implement') - - // if !osal.cmd_exists('hero') { - // herolib.hero_compile()! - // } - heropath := osal.cmd_path('hero')! - self.copy(heropath, '/usr/local/bin/hero')! -} - -// get a container where we build hero and export hero from the container so we can use it for hero_copy -pub fn (mut self BuildAHContainer) hero_build() ! { - panic('implement') -} diff --git a/libarchive/buildah/buildah_info.v b/libarchive/buildah/buildah_info.v deleted file mode 100644 index c42b31cb..00000000 --- a/libarchive/buildah/buildah_info.v +++ /dev/null @@ -1,113 +0,0 @@ -module buildah - -struct BuilderInfo { - type_ string @[json: 'Type'] - from_image string @[json: 'FromImage'] - from_image_id string @[json: 'FromImageID'] - from_image_digest string @[json: 'FromImageDigest'] - group_add []string @[json: 'GroupAdd'] - config string @[json: 'Config'] - manifest string @[json: 'Manifest'] - container string @[json: 'Container'] - container_id string @[json: 'ContainerID'] - mount_point string @[json: 'MountPoint'] - process_label string @[json: 'ProcessLabel'] - mount_label string @[json: 'MountLabel'] - image_annotations ImageAnnotations @[json: 'ImageAnnotations'] - image_created_by string @[json: 'ImageCreatedBy'] - oci_v1 OCIv1 @[json: 'OCIv1'] - docker Docker @[json: 'Docker'] - default_mounts_file_path string @[json: 'DefaultMountsFilePath'] - isolation string @[json: 'Isolation'] - namespace_options []NamespaceOption @[json: 'NamespaceOptions'] - capabilities []string @[json: 'Capabilities'] - configure_network string @[json: 'ConfigureNetwork'] - // cni_plugin_path string @[json: 'CNIPluginPath'] - // cni_config_dir string @[json: 'CNIConfigDir'] - // id_mapping_options IDMappingOptions @[json: 'IDMappingOptions'] - history []string @[json: 'History'] - devices []string @[json: 'Devices'] -} - -struct ImageAnnotations { - org_opencontainers_image_base_digest string @[json: 'org.opencontainers.image.base.digest'] - org_opencontainers_image_base_name string @[json: 'org.opencontainers.image.base.name'] -} - -struct OCIv1 { - created string @[json: 'created'] - architecture string @[json: 'architecture'] - os string @[json: 'os'] - config map[string]string @[json: 'config'] - rootfs Rootfs @[json: 'rootfs'] -} - -struct Rootfs { - type_ string @[json: 'type'] - diff_ids []string @[json: 'diff_ids'] -} - -struct Docker { - created string @[json: 'created'] - container_config ContainerConfig @[json: 'container_config'] - config DockerConfig @[json: 'config'] - architecture string @[json: 'architecture'] - os string @[json: 'os'] -} - -struct ContainerConfig { - hostname string @[json: 'Hostname'] - domainname string @[json: 'Domainname'] - user string @[json: 'User'] - attach_stdin bool @[json: 'AttachStdin'] - attach_stdout bool @[json: 'AttachStdout'] - attach_stderr bool @[json: 'AttachStderr'] - tty bool @[json: 'Tty'] - open_stdin bool @[json: 'OpenStdin'] - stdin_once bool @[json: 'StdinOnce'] - env []string @[json: 'Env'] - cmd []string @[json: 'Cmd'] - image string @[json: 'Image'] - volumes map[string]string @[json: 'Volumes'] - working_dir string @[json: 'WorkingDir'] - entrypoint []string @[json: 'Entrypoint'] - on_build []string @[json: 'OnBuild'] - labels map[string]string @[json: 'Labels'] -} - -struct DockerConfig { - // Assuming identical structure to ContainerConfig - // Define fields with @json: mapping if different -} - -struct NamespaceOption { - name string @[json: 'Name'] - host bool @[json: 'Host'] - path string @[json: 'Path'] -} - -// struct IDMappingOptions { -// host_uid_mapping bool @[json: 'HostUIDMapping'] -// host_gid_mapping bool @[json: 'HostGIDMapping'] -// // uid_map []UIDMap @[json: 'UIDMap'] -// // gid_map []GIDMap @[json: 'GIDMap'] -// auto_user_ns bool @[json: 'AutoUserNs'] -// auto_user_ns_opts AutoUserNsOpts @[json: 'AutoUserNsOpts'] -// } - -// struct UIDMap { -// // Define the structure with @json: mappings -// } - -// struct GIDMap { -// // Define the structure with @json: mappings -// } - -// struct AutoUserNsOpts { -// size int @[json: 'Size'] -// initial_size int @[json: 'InitialSize'] -// passwd_file string @[json: 'PasswdFile'] -// group_file string @[json: 'GroupFile'] -// additional_uid_mappings []UIDMap @[json: 'AdditionalUIDMappings'] -// additional_gid_mappings []GIDMap @[json: 'AdditionalGIDMappings'] -// } diff --git a/libarchive/buildah/buildah_solutions.v b/libarchive/buildah/buildah_solutions.v deleted file mode 100644 index c923cc60..00000000 --- a/libarchive/buildah/buildah_solutions.v +++ /dev/null @@ -1,175 +0,0 @@ -module buildah - -import incubaid.herolib.osal.core as osal -import incubaid.herolib.ui.console -import os - -@[params] -pub struct GetArgs { -pub mut: - reset bool -} - -// builder machine based on arch and install vlang -// TODO need to change, go to ubuntu -pub fn builder_base(args GetArgs) !BuildAHContainer { - name := 'base' - console.print_header('buildah base build') - - mut builder := e.builder_new(name: name, from: 'scratch', delete: true)! - mount_path := builder.mount_to_path()! - if mount_path.len < 4 { - return error('mount_path needs to be +4 chars') - } - self.exec( - cmd: ' - - export MOUNT_PATH=\'${mount_path}\' - - mmdebstrap --variant=minbase --components="main,universe" --include="apt,base-files,base-passwd,bash,coreutils" noble \${MOUNT_PATH} - - echo "Binding essential directories..." - mount --bind /dev "\${MOUNT_PATH}/dev" - mount --bind /proc "\${MOUNT_PATH}/proc" - mount --bind /sys "\${MOUNT_PATH}/sys" - - echo "tzdata tzdata/Areas select Europe" | chroot "\${MOUNT_PATH}" debconf-set-selections - echo "tzdata tzdata/Zones/Europe select Brussels" | chroot "\${MOUNT_PATH}" debconf-set-selections - - chroot \${MOUNT_PATH} apt update - - # Set up APT for non-interactive installation - export DEBIAN_FRONTEND=noninteractive - export DEBCONF_NONINTERACTIVE_SEEN=true - - # Update package lists - echo "Updating package lists in chroot..." - chroot \${MOUNT_PATH} apt-get update -yq - - # Install required packages - echo "Installing essential packages..." - chroot \${MOUNT_PATH} apt-get install -yq screen bash coreutils curl mc unzip sudo which openssh-client openssh-server redis wget - - echo "Cleaning up..." - umount "\${MOUNT_PATH}/dev" || true - umount "\${MOUNT_PATH}/proc" || true - umount "\${MOUNT_PATH}/sys" || true - - ' - )! - builder.install_zinit()! - // builder.set_entrypoint('redis-server')! - builder.commit('localhost/${name}')! - return builder -} - -// TODO: all below are not good, need to use play cmd over hero remotely. see how we did it with core_installers - -// // builder machine based on arch and install vlang -// pub fn (mut e CEngine) builder_go_rust(args GetArgs) !BuildAHContainer { -// console.print_header('buildah builder go rust') -// name := 'builder_go_rust' -// e.builder_base(reset: false)! -// if !args.reset && e.builder_exists(name)! { -// return e.builder_get(name)! -// } -// mut builder := e.builder_new(name: name, from: 'localhost/base', delete: true)! -// builder.hero_execute_cmd('installers -n golang,rust')! -// // builder.clean()! -// builder.commit('localhost/${name}')! -// e.load()! -// return builder -// } - -// pub fn (mut e CEngine) builder_js(args GetArgs) !BuildAHContainer { -// console.print_header('buildah builder js') -// name := 'builder_js' -// e.builder_base(reset: false)! -// if !args.reset && e.builder_exists(name)! { -// return e.builder_get(name)! -// } -// mut builder := e.builder_new(name: name, from: 'localhost/base', delete: true)! -// builder.hero_execute_cmd('installers -n nodejs')! -// // builder.clean()! -// builder.commit('localhost/${name}')! -// e.load()! -// return builder -// } - -// pub fn (mut e CEngine) builder_js_python(args GetArgs) !BuildAHContainer { -// console.print_header('buildah builder js python') -// name := 'builder_js_python' -// e.builder_js(reset: false)! -// if !args.reset && e.builder_exists(name)! { -// return e.builder_get(name)! -// } -// mut builder := e.builder_new(name: name, from: 'localhost/builder_js', delete: true)! -// builder.hero_execute_cmd('installers -n python')! -// // builder.clean()! -// builder.commit('localhost/${name}')! -// e.load()! -// return builder -// } - -// pub fn (mut e CEngine) builder_hero(args GetArgs) !BuildAHContainer { -// console.print_header('buildah builder hero dev') -// name := 'builder_hero' -// e.builder_js_python(reset: false)! -// if !args.reset && e.builder_exists(name)! { -// return e.builder_get(name)! -// } -// mut builder := e.builder_new(name: name, from: 'localhost/builder_js_python', delete: true)! -// builder.hero_execute_cmd('installers -n hero')! -// // builder.clean()! -// builder.commit('localhost/${name}')! -// e.load()! -// return builder -// } - -// pub fn (mut e CEngine) builder_herodev(args GetArgs) !BuildAHContainer { -// console.print_header('buildah builder hero dev') -// name := 'builder_herodev' -// e.builder_js_python(reset: false)! -// if !args.reset && e.builder_exists(name)! { -// return e.builder_get(name)! -// } -// mut builder := e.builder_new(name: name, from: 'localhost/builder_hero', delete: true)! -// builder.hero_execute_cmd('installers -n herodev')! -// // builder.clean()! -// builder.commit('localhost/${name}')! -// e.load()! -// return builder -// } - -// pub fn (mut e CEngine) builder_heroweb(args GetArgs) !BuildAHContainer { -// console.print_header('buildah builder hero web') -// name := 'builder_heroweb' -// e.builder_go_rust(reset: false)! -// e.builder_hero(reset: false)! -// if !args.reset && e.builder_exists(name)! { -// return e.builder_get(name)! -// } -// mut builder0 := e.builder_new( -// name: 'builder_heroweb_temp' -// from: 'localhost/builder_go_rust' -// delete: true -// )! -// builder0.hero_execute_cmd('installers -n heroweb')! -// // builder0.hero_execute_cmd("installers -n heroweb")! -// mpath := builder0.mount_to_path()! - -// // copy the built binary to host -// self.exec( -// cmd: ' -// mkdir -p ${os.home_dir()}/hero/var/bin -// cp ${mpath}/usr/local/bin/* ${os.home_dir()}/hero/var/bin/ -// ' -// )! - -// builder0.delete()! -// mut builder2 := e.builder_new(name: name, from: 'localhost/builder_hero', delete: true)! -// builder2.copy('${os.home_dir()}/hero/var/bin/', '/usr/local/bin/')! -// builder2.commit('localhost/${name}')! -// e.load()! -// return builder2 -// } diff --git a/libarchive/buildah/readme.md b/libarchive/buildah/readme.md deleted file mode 100644 index e156cc2f..00000000 --- a/libarchive/buildah/readme.md +++ /dev/null @@ -1,114 +0,0 @@ - -# Herocontainers - -Tools to work with containers - -```go -#!/usr/bin/env -S v -n -cg -w -enable-globals run - -import incubaid.herolib.virt.herocontainers -import incubaid.herolib.ui.console -import incubaid.herolib.builder - -//interative means will ask for login/passwd - -console.print_header("BUILDAH Demo.") - -//if herocompile on, then will forced compile hero, which might be needed in debug mode for hero -// to execute hero scripts inside build container -mut factory:=herocontainers.new(herocompile=true)! -//mut b:=factory.builder_new(name:"test")! - -//create -factory.builderv_create()! - -//get the container -//mut b2:=factory.builder_get("builderv")! -//b2.shell()! - - -``` - -## buildah tricks - -```bash -#find the containers as have been build, these are the active ones you can work with -buildah ls -#see the images -buildah images -``` - -result is something like - -```bash -CONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAME -a9946633d4e7 * scratch base -86ff0deb00bf * 4feda76296d6 localhost/builder:latest base_go_rust -``` - -some tricks - -```bash -#run interactive in one (here we chose the builderv one) -buildah run --terminal --env TERM=xterm base /bin/bash -#or -buildah run --terminal --env TERM=xterm default /bin/bash -#or -buildah run --terminal --env TERM=xterm base_go_rust /bin/bash - -``` - -to check inside the container about diskusage - -```bash -apt install ncdu -ncdu -``` - -## create container - -```go -import incubaid.herolib.virt.herocontainers -import incubaid.herolib.ui.console -import incubaid.herolib.builder - -//interative means will ask for login/passwd - -console.print_header("Get a container.") - -mut e:=herocontainers.new()! - -//info see https://docs.podman.io/en/latest/markdown/podman-run.1.html - -mut c:=e.container_create( - name: 'mycontainer' - image_repo: 'ubuntu' - // Resource limits - memory: '1g' - cpus: 0.5 - // Network config - network: 'bridge' - network_aliases: ['myapp', 'api'] - // DNS config - dns_servers: ['8.8.8.8', '8.8.4.4'] - dns_search: ['example.com'] - interactive: true // Keep STDIN open - mounts: [ - 'type=bind,src=/data,dst=/container/data,ro=true' - ] - volumes: [ - '/config:/etc/myapp:ro' - ] - published_ports: [ - '127.0.0.1:8080:80' - ] -)! - - - - -``` - -## future - -should make this module compatible with diff --git a/libarchive/daguserver/.heroscript b/libarchive/daguserver/.heroscript deleted file mode 100644 index a202dd59..00000000 --- a/libarchive/daguserver/.heroscript +++ /dev/null @@ -1,13 +0,0 @@ - -!!hero_code.generate_installer - name:'daguserver' - classname:'DaguInstaller' - singleton:1 - templates:1 - default:1 - title:'' - supported_platforms:'' - reset:0 - startupmanager:1 - hasconfig:1 - build:0 \ No newline at end of file diff --git a/libarchive/daguserver/daguserver_actions.v b/libarchive/daguserver/daguserver_actions.v deleted file mode 100644 index 6b609de3..00000000 --- a/libarchive/daguserver/daguserver_actions.v +++ /dev/null @@ -1,157 +0,0 @@ -module daguserver - -import incubaid.herolib.osal.core as osal -import incubaid.herolib.ui.console -import incubaid.herolib.core.texttools -import incubaid.herolib.core -import incubaid.herolib.core.httpconnection -import incubaid.herolib.installers.ulist -// import incubaid.herolib.develop.gittools -import incubaid.herolib.osal.startupmanager -import incubaid.herolib.libarchive.zinit as zinit_lib -import os - -fn startupcmd() ![]startupmanager.ZProcessNewArgs { - mut res := []startupmanager.ZProcessNewArgs{} - mut cfg := get()! - - res << startupmanager.ZProcessNewArgs{ - name: 'dagu' - cmd: 'dagu server' - env: { - 'HOME ': os.home_dir() - 'DAGU_HOME ': cfg.configpath // config for dagu is called admin.yml and is in this dir - } - } - - res << startupmanager.ZProcessNewArgs{ - name: 'dagu_scheduler' - cmd: 'dagu scheduler' - env: { - 'HOME ': os.home_dir() - 'DAGU_HOME ': cfg.configpath - } - } - - return res -} - -fn running() !bool { - mut cfg := get()! - url := 'http://${cfg.host}:${cfg.port}/api/v1' - mut conn := httpconnection.new(name: 'dagu', url: url)! - - if cfg.secret.len > 0 { - conn.default_header.add(.authorization, 'Bearer ${cfg.secret}') - } - - console.print_debug("curl -X 'GET' '${url}'/tags --oauth2-bearer ${cfg.secret}") - r := conn.get_json_dict(prefix: 'tags', debug: false) or { return false } - tags := r['Tags'] or { return false } - console.print_debug(tags) - console.print_debug('Dagu is answering.') - return true -} - -fn start_pre() ! { -} - -fn start_post() ! { -} - -fn stop_pre() ! { -} - -fn stop_post() ! { -} - -//////////////////// following actions are not specific to instance of the object - -// checks if a certain version or above is installed -fn installed() !bool { - res := os.execute('dagu version') - if res.exit_code == 0 { - r := res.output.split_into_lines().filter(it.trim_space().len > 0) - if r.len != 1 { - return error("couldn't parse dagu version.\n${res.output}") - } - if texttools.version(version) > texttools.version(r[0]) { - return false - } - } else { - return false - } - return true -} - -// get the Upload List of the files -fn ulist_get() !ulist.UList { - // optionally build a UList which is all paths which are result of building, is then used e.g. in upload - return ulist.UList{} -} - -// uploads to S3 server if configured -fn upload() ! {} - -fn install() ! { - console.print_header('install daguserver') - mut url := '' - if core.is_linux_arm()! { - url = 'https://github.com/dagu-dev/dagu/releases/download/v${version}/dagu_${version}_linux_arm64.tar.gz' - } else if core.is_linux_intel()! { - url = 'https://github.com/dagu-dev/dagu/releases/download/v${version}/dagu_${version}_linux_amd64.tar.gz' - } else if core.is_osx_arm()! { - url = 'https://github.com/dagu-dev/dagu/releases/download/v${version}/dagu_${version}_darwin_arm64.tar.gz' - } else if core.is_osx_intel()! { - url = 'https://github.com/dagu-dev/dagu/releases/download/v${version}/dagu_${version}_darwin_amd64.tar.gz' - } else { - return error('unsported platform') - } - - mut dest := osal.download( - url: url - minsize_kb: 9000 - expand_dir: '/tmp/dagu' - )! - - mut binpath := dest.file_get('dagu')! - osal.cmd_add( - cmdname: 'dagu' - source: binpath.path - )! -} - -fn destroy() ! { - cmd := ' - systemctl disable daguserver_scheduler.service - systemctl disable daguserver.service - systemctl stop daguserver_scheduler.service - systemctl stop daguserver.service - - systemctl list-unit-files | grep daguserver - - pkill -9 -f daguserver - - ps aux | grep daguserver - - ' - - osal.execute_silent(cmd) or {} - // mut zinit_factory := zinit_lib.Zinit{} - - // if zinit_factory.exists('dagu') { - // zinit_factory.stop('dagu')! or { return error('Could not stop dagu service due to: ${err}') } - // zinit_factory.delete('dagu')! or { - // return error('Could not delete dagu service due to: ${err}') - // } - // } - - // if zinit_factory.exists('dagu_scheduler') { - // zinit_factory.stop('dagu_scheduler')! or { - // return error('Could not stop dagu_scheduler service due to: ${err}') - // } - // zinit_factory.delete('dagu_scheduler')! or { - // return error('Could not delete dagu_scheduler service due to: ${err}') - // } - // } -} diff --git a/libarchive/daguserver/daguserver_factory_.v b/libarchive/daguserver/daguserver_factory_.v deleted file mode 100644 index 1e0f8cca..00000000 --- a/libarchive/daguserver/daguserver_factory_.v +++ /dev/null @@ -1,299 +0,0 @@ -module daguserver - -import incubaid.herolib.core.base -import incubaid.herolib.core.playbook { PlayBook } -import incubaid.herolib.ui.console -import json -import incubaid.herolib.osal.startupmanager -import time - -__global ( - daguserver_global map[string]&DaguInstaller - daguserver_default string -) - -/////////FACTORY - -@[params] -pub struct ArgsGet { -pub mut: - name string = 'default' - fromdb bool // will load from filesystem - create bool // default will not create if not exist -} - -pub fn new(args ArgsGet) !&DaguInstaller { - mut obj := DaguInstaller{ - name: args.name - } - set(obj)! - return get(name: args.name)! -} - -pub fn get(args ArgsGet) !&DaguInstaller { - mut context := base.context()! - daguserver_default = args.name - if args.fromdb || args.name !in daguserver_global { - mut r := context.redis()! - if r.hexists('context:daguserver', args.name)! { - data := r.hget('context:daguserver', args.name)! - if data.len == 0 { - return error('DaguInstaller with name: daguserver does not exist, prob bug.') - } - mut obj := json.decode(DaguInstaller, data)! - set_in_mem(obj)! - } else { - if args.create { - new(args)! - } else { - return error("DaguInstaller with name 'daguserver' does not exist") - } - } - return get(name: args.name)! // no longer from db nor create - } - return daguserver_global[args.name] or { - return error('could not get config for daguserver with name:daguserver') - } -} - -// register the config for the future -pub fn set(o DaguInstaller) ! { - mut o2 := set_in_mem(o)! - daguserver_default = o2.name - mut context := base.context()! - mut r := context.redis()! - r.hset('context:daguserver', o2.name, json.encode(o2))! -} - -// does the config exists? -pub fn exists(args ArgsGet) !bool { - mut context := base.context()! - mut r := context.redis()! - return r.hexists('context:daguserver', args.name)! -} - -pub fn delete(args ArgsGet) ! { - mut context := base.context()! - mut r := context.redis()! - r.hdel('context:daguserver', args.name)! -} - -@[params] -pub struct ArgsList { -pub mut: - fromdb bool // will load from filesystem -} - -// if fromdb set: load from filesystem, and not from mem, will also reset what is in mem -pub fn list(args ArgsList) ![]&DaguInstaller { - mut res := []&DaguInstaller{} - mut context := base.context()! - if args.fromdb { - // reset what is in mem - daguserver_global = map[string]&DaguInstaller{} - daguserver_default = '' - } - if args.fromdb { - mut r := context.redis()! - mut l := r.hkeys('context:daguserver')! - - for name in l { - res << get(name: name, fromdb: true)! - } - return res - } else { - // load from memory - for _, client in daguserver_global { - res << client - } - } - return res -} - -// only sets in mem, does not set as config -fn set_in_mem(o DaguInstaller) !DaguInstaller { - mut o2 := obj_init(o)! - daguserver_global[o2.name] = &o2 - daguserver_default = o2.name - return o2 -} - -pub fn play(mut plbook PlayBook) ! { - if !plbook.exists(filter: 'daguserver.') { - return - } - mut install_actions := plbook.find(filter: 'daguserver.configure')! - if install_actions.len > 0 { - for install_action in install_actions { - heroscript := install_action.heroscript() - mut obj2 := heroscript_loads(heroscript)! - set(obj2)! - } - } - mut other_actions := plbook.find(filter: 'daguserver.')! - for other_action in other_actions { - if other_action.name in ['destroy', 'install', 'build'] { - mut p := other_action.params - reset := p.get_default_false('reset') - if other_action.name == 'destroy' || reset { - console.print_debug('install action daguserver.destroy') - destroy()! - } - if other_action.name == 'install' { - console.print_debug('install action daguserver.install') - install()! - } - } - if other_action.name in ['start', 'stop', 'restart'] { - mut p := other_action.params - name := p.get('name')! - mut daguserver_obj := get(name: name)! - console.print_debug('action object:\n${daguserver_obj}') - if other_action.name == 'start' { - console.print_debug('install action daguserver.${other_action.name}') - daguserver_obj.start()! - } - - if other_action.name == 'stop' { - console.print_debug('install action daguserver.${other_action.name}') - daguserver_obj.stop()! - } - if other_action.name == 'restart' { - console.print_debug('install action daguserver.${other_action.name}') - daguserver_obj.restart()! - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS /////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// - -fn startupmanager_get(cat startupmanager.StartupManagerType) !startupmanager.StartupManager { - // unknown - // screen - // zinit - // tmux - // systemd - match cat { - .screen { - console.print_debug('startupmanager: zinit') - return startupmanager.get(.screen)! - } - .zinit { - console.print_debug('startupmanager: zinit') - return startupmanager.get(.zinit)! - } - .systemd { - console.print_debug('startupmanager: systemd') - return startupmanager.get(.systemd)! - } - else { - console.print_debug('startupmanager: auto') - return startupmanager.get(.auto)! - } - } -} - -// load from disk and make sure is properly intialized -pub fn (mut self DaguInstaller) reload() ! { - self = obj_init(self)! -} - -pub fn (mut self DaguInstaller) start() ! { - if self.running()! { - return - } - - console.print_header('daguserver start') - - if !installed()! { - install()! - } - - configure()! - - start_pre()! - - for zprocess in startupcmd()! { - mut sm := startupmanager_get(zprocess.startuptype)! - - console.print_debug('starting daguserver with ${zprocess.startuptype}...') - - sm.new(zprocess)! - - sm.start(zprocess.name)! - } - - start_post()! - - for _ in 0 .. 50 { - if self.running()! { - return - } - time.sleep(100 * time.millisecond) - } - return error('daguserver did not install properly.') -} - -pub fn (mut self DaguInstaller) install_start(args InstallArgs) ! { - switch(self.name) - self.install(args)! - self.start()! -} - -pub fn (mut self DaguInstaller) stop() ! { - switch(self.name) - stop_pre()! - for zprocess in startupcmd()! { - mut sm := startupmanager_get(zprocess.startuptype)! - sm.stop(zprocess.name)! - } - stop_post()! -} - -pub fn (mut self DaguInstaller) restart() ! { - switch(self.name) - self.stop()! - self.start()! -} - -pub fn (mut self DaguInstaller) running() !bool { - switch(self.name) - - // walk over the generic processes, if not running return - for zprocess in startupcmd()! { - if zprocess.startuptype != .screen { - mut sm := startupmanager_get(zprocess.startuptype)! - r := sm.running(zprocess.name)! - if r == false { - return false - } - } - } - return running()! -} - -@[params] -pub struct InstallArgs { -pub mut: - reset bool -} - -pub fn (mut self DaguInstaller) install(args InstallArgs) ! { - switch(self.name) - if args.reset || (!installed()!) { - install()! - } -} - -pub fn (mut self DaguInstaller) destroy() ! { - switch(self.name) - self.stop() or {} - destroy()! -} - -// switch instance to be used for daguserver -pub fn switch(name string) { -} diff --git a/libarchive/daguserver/daguserver_model.v b/libarchive/daguserver/daguserver_model.v deleted file mode 100644 index 33f8212b..00000000 --- a/libarchive/daguserver/daguserver_model.v +++ /dev/null @@ -1,79 +0,0 @@ -module daguserver - -import incubaid.herolib.data.encoderhero -import incubaid.herolib.crypt.secrets -import incubaid.herolib.ui.console -import incubaid.herolib.core.pathlib -import os - -pub const version = '1.14.3' -const singleton = true -const default = true -pub const homedir = os.home_dir() - -// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED -@[heap] -pub struct DaguInstaller { -pub mut: - name string = 'default' - dagsdir string = '${os.home_dir()}/.dagu' - configpath string = '${os.home_dir()}/.config/dagu' - username string - password string @[secret] - secret string @[secret] - title string - host string = 'localhost' - port int = 8014 -} - -// your checking & initialization code if needed -fn obj_init(mycfg_ DaguInstaller) !DaguInstaller { - mut mycfg := mycfg_ - return mycfg -} - -// called before start if done -fn configure() ! { - mut cfg := get()! - - if cfg.password == '' { - cfg.password = secrets.hex_secret()! - } - - // TODO:use DAGU_SECRET from env variables in os if not set then empty string - if cfg.secret == '' { - cfg.secret = secrets.openssl_hex_secret(input: cfg.password)! - } - - if cfg.dagsdir == '' { - cfg.dagsdir = '${homedir}/.dagu' - } - - if cfg.configpath == '' { - cfg.configpath = '${homedir}/.config/dagu' - } - - if cfg.host == '' { - cfg.host = 'localhost' - } - - if cfg.port == 0 { - cfg.port = 8014 - } - - mut mycode := $tmpl('templates/dagu.yaml') - mut path := pathlib.get_file(path: '${cfg.configpath}/admin.yaml', create: true)! - path.write(mycode)! - console.print_debug(mycode) -} - -/////////////NORMALLY NO NEED TO TOUCH - -pub fn heroscript_dumps(obj DaguInstaller) !string { - return encoderhero.encode[DaguInstaller](obj)! -} - -pub fn heroscript_loads(heroscript string) !DaguInstaller { - mut obj := encoderhero.decode[DaguInstaller](heroscript)! - return obj -} diff --git a/libarchive/daguserver/readme.md b/libarchive/daguserver/readme.md deleted file mode 100644 index 7a64cfd1..00000000 --- a/libarchive/daguserver/readme.md +++ /dev/null @@ -1,31 +0,0 @@ -# daguserver - -To get started - -```v - - - -import incubaid.herolib.installers.something. daguserver - -mut installer:= daguserver.get()! - -installer.start()! - - - - -``` - -## example heroscript - -```hero -!!daguserver.install - homedir: '/home/user/daguserver' - username: 'admin' - password: 'secretpassword' - title: 'Some Title' - host: 'localhost' - port: 8888 - -``` diff --git a/libarchive/daguserver/templates/communication.yaml b/libarchive/daguserver/templates/communication.yaml deleted file mode 100644 index 8e48f3ee..00000000 --- a/libarchive/daguserver/templates/communication.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# directory path to save logs from standard output -logDir: @{config.log_dir} - -# history retention days (default: 30) -histRetentionDays: @{config.history_retention_days} - -# Email notification settings -mailOn: - failure: @{config.mail_on.failure} - success: @{config.mail_on.success} - -# SMTP server settings -smtp: - host: @{config.smtp.host} - port: @{config.smtp.port} - username: @{config.smtp.username} - password: @{config.smtp.password} - -# Error mail configuration -errorMail: - from: @{config.error_mail.from} - to: @{config.error_mail.to} - prefix: @{config.error_mail.prefix} - -# Info mail configuration -infoMail: - from: @{config.info_mail.from} - to: @{config.info_mail.to} - prefix: @{config.info_mail.prefix} \ No newline at end of file diff --git a/libarchive/daguserver/templates/dagu.yaml b/libarchive/daguserver/templates/dagu.yaml deleted file mode 100644 index 8fbeb0d3..00000000 --- a/libarchive/daguserver/templates/dagu.yaml +++ /dev/null @@ -1,27 +0,0 @@ -host: "${cfg.host}" # default: 127.0.0.1 -port: ${cfg.port} - -# path to the DAGs directory -dags: ${cfg.dagsdir} - -# Web UI Color & Title -# navbarColor: # header color for web UI (e.g. "#ff0000") -navbarTitle: ${cfg.title} # header title for web UI (e.g. "PROD") - -isBasicAuth: true -basicAuthUsername: ${cfg.username} -basicAuthPassword: ${cfg.password} - -isAuthToken: true # enables API token -authToken: ${cfg.secret} - -# Base Config -# baseConfig: - -# Working Directory -# workDir: # default: DAG location - -# SSL Configuration -# tls: -# certFile: -# keyFile: \ No newline at end of file diff --git a/libarchive/dedupestor/dedupe_ourdb/README.md b/libarchive/dedupestor/dedupe_ourdb/README.md deleted file mode 100644 index 21c6c6f2..00000000 --- a/libarchive/dedupestor/dedupe_ourdb/README.md +++ /dev/null @@ -1,107 +0,0 @@ -# DedupeStore - -DedupeStore is a content-addressable key-value store with built-in deduplication. It uses blake2b-160 content hashing to identify and deduplicate data, making it ideal for storing files or data blocks where the same content might appear multiple times. - -## Features - -- Content-based deduplication using blake2b-160 hashing -- Efficient storage using RadixTree for hash lookups -- Persistent storage using OurDB -- Maximum value size limit of 1MB -- Fast retrieval of data using content hash -- Automatic deduplication of identical content - -## Usage - -```v -import incubaid.herolib.data.dedupestor - -// Create a new dedupestore -mut ds := dedupestor.new( - path: 'path/to/store' - reset: false // Set to true to reset existing data -)! - -// Store some data -data := 'Hello, World!'.bytes() -hash := ds.store(data)! -println('Stored data with hash: ${hash}') - -// Retrieve data using hash -retrieved := ds.get(hash)! -println('Retrieved data: ${retrieved.bytestr()}') - -// Check if data exists -exists := ds.exists(hash) -println('Data exists: ${exists}') - -// Attempting to store the same data again returns the same hash -same_hash := ds.store(data)! -assert hash == same_hash // True, data was deduplicated - -``` - -## Implementation Details - -DedupeStore uses two main components for storage: - -1. **RadixTree**: Stores mappings from content hashes to data location IDs -2. **OurDB**: Stores the actual data blocks - -When storing data: - -1. The data is hashed using blake2b-160 -2. If the hash exists in the RadixTree, the existing data location is returned -3. If the hash is new: - - Data is stored in OurDB, getting a new location ID - - Hash -> ID mapping is stored in RadixTree - - The hash is returned - -When retrieving data: - -1. The RadixTree is queried with the hash to get the data location ID -2. The data is retrieved from OurDB using the ID - -## Size Limits - -- Maximum value size: 1MB -- Attempting to store larger values will result in an error - -## the reference field - -In the dedupestor system, the Reference struct is defined with two fields: - -```v -pub struct Reference { -pub: - owner u16 - id u32 -} -``` - -The purpose of the id field in this context is to serve as an identifier within a specific owner's domain. Here's what each field represents: - -owner (u16): Identifies which entity or system component "owns" or is referencing the data. This could represent different applications, users, or subsystems that are using the dedupestor. -id (u32): A unique identifier within that owner's domain. This allows each owner to have their own independent numbering system for referencing stored data. -Together, the {owner: 1, id: 100} combination creates a unique reference that: - -Tracks which entities are referencing a particular piece of data -Allows the system to know when data can be safely deleted (when no references remain) -Provides a way for different components to maintain their own ID systems without conflicts -The dedupestor uses these references to implement a reference counting mechanism. When data is stored, a reference is attached to it. When all references to a piece of data are removed (via the delete method), the actual data can be safely deleted from storage. - -This design allows for efficient deduplication - if the same data is stored multiple times with different references, it's only physically stored once, but the system keeps track of all the references to it. - -## Testing - -The module includes comprehensive tests covering: - -- Basic store/retrieve operations -- Deduplication functionality -- Size limit enforcement -- Edge cases - -Run tests with: - -```bash -v test lib/data/dedupestor/ diff --git a/libarchive/dedupestor/dedupe_ourdb/dedupestor.v b/libarchive/dedupestor/dedupe_ourdb/dedupestor.v deleted file mode 100644 index 49939690..00000000 --- a/libarchive/dedupestor/dedupe_ourdb/dedupestor.v +++ /dev/null @@ -1,130 +0,0 @@ -module dedupe_ourdb - -import incubaid.herolib.data.radixtree -import incubaid.herolib.data.ourdb -import incubaid.herolib.data.dedupestor - -// DedupeStore provides a key-value store with deduplication based on content hashing -pub struct DedupeStore { -mut: - radix &radixtree.RadixTree // For storing hash -> id mappings - data &ourdb.OurDB // For storing the actual data -} - -@[params] -pub struct NewArgs { -pub mut: - path string // Base path for the store - reset bool // Whether to reset existing data -} - -// new creates a new deduplication store -pub fn new(args NewArgs) !&DedupeStore { - // Create the radixtree for hash -> id mapping - mut rt := radixtree.new( - path: '${args.path}/radixtree' - reset: args.reset - )! - - // Create the ourdb for actual data storage - mut db := ourdb.new( - path: '${args.path}/data' - record_size_max: dedupestor.max_value_size - incremental_mode: true // We want auto-incrementing IDs - reset: args.reset - )! - - return &DedupeStore{ - radix: &rt - data: &db - } -} - -// store stores data with its reference and returns its id -// If the data already exists (same hash), returns the existing id without storing again -// appends reference to the radix tree entry of the hash to track references -pub fn (mut ds DedupeStore) store(data []u8, ref dedupestor.Reference) !u32 { - // Check size limit - if data.len > dedupestor.max_value_size { - return error('value size exceeds maximum allowed size of 1MB') - } - - // Calculate blake160 hash of the value - hash := dedupestor.hash_data(data) - - // Check if this hash already exists - if metadata_bytes := ds.radix.get(hash) { - // Value already exists, add new ref & return the id - mut metadata_obj := dedupestor.bytes_to_metadata(metadata_bytes) - metadata_obj = metadata_obj.add_reference(ref)! - ds.radix.update(hash, metadata_obj.to_bytes())! - return metadata_obj.id - } - - // Store the actual data in ourdb - id := ds.data.set(data: data)! - metadata_obj := dedupestor.Metadata{ - id: id - references: [ref] - } - - // Store the mapping of hash -> id in radixtree - ds.radix.set(hash, metadata_obj.to_bytes())! - - return metadata_obj.id -} - -// get retrieves a value by its hash -pub fn (mut ds DedupeStore) get(id u32) ![]u8 { - return ds.data.get(id)! -} - -// get retrieves a value by its hash -pub fn (mut ds DedupeStore) get_from_hash(hash string) ![]u8 { - // Get the ID from radixtree - metadata_bytes := ds.radix.get(hash)! - - // Convert bytes back to metadata - metadata_obj := dedupestor.bytes_to_metadata(metadata_bytes) - - // Get the actual data from ourdb - return ds.data.get(metadata_obj.id)! -} - -// exists checks if a value with the given hash exists -pub fn (mut ds DedupeStore) id_exists(id u32) bool { - if _ := ds.data.get(id) { - return true - } else { - return false - } -} - -// exists checks if a value with the given hash exists -pub fn (mut ds DedupeStore) hash_exists(hash string) bool { - return if _ := ds.radix.get(hash) { true } else { false } -} - -// delete removes a reference from the hash entry -// If it's the last reference, removes the hash entry and its data -pub fn (mut ds DedupeStore) delete(id u32, ref dedupestor.Reference) ! { - // Calculate blake160 hash of the value - data := ds.data.get(id)! - hash := dedupestor.hash_data(data) - - // Get the current entry from radixtree - metadata_bytes := ds.radix.get(hash)! - mut metadata_obj := dedupestor.bytes_to_metadata(metadata_bytes) - metadata_obj = metadata_obj.remove_reference(ref)! - - if metadata_obj.references.len == 0 { - // Delete from radixtree - ds.radix.delete(hash)! - // Delete from data db - ds.data.delete(id)! - return - } - - // Update hash metadata - ds.radix.update(hash, metadata_obj.to_bytes())! -} diff --git a/libarchive/dedupestor/dedupe_ourdb/dedupestor_test.v b/libarchive/dedupestor/dedupe_ourdb/dedupestor_test.v deleted file mode 100644 index 95ce3a55..00000000 --- a/libarchive/dedupestor/dedupe_ourdb/dedupestor_test.v +++ /dev/null @@ -1,188 +0,0 @@ -module dedupe_ourdb - -import os -import incubaid.herolib.data.dedupestor - -fn testsuite_begin() ! { - // Ensure test directories exist and are clean - test_dirs := [ - '/tmp/dedupestor_test', - '/tmp/dedupestor_test_size', - '/tmp/dedupestor_test_exists', - '/tmp/dedupestor_test_multiple', - '/tmp/dedupestor_test_refs', - ] - - for dir in test_dirs { - if os.exists(dir) { - os.rmdir_all(dir) or {} - } - os.mkdir_all(dir) or {} - } -} - -fn test_basic_operations() ! { - mut ds := new( - path: '/tmp/dedupestor_test' - reset: true - )! - - // Test storing and retrieving data - value1 := 'test data 1'.bytes() - ref1 := dedupestor.Reference{ - owner: 1 - id: 1 - } - id1 := ds.store(value1, ref1)! - - retrieved1 := ds.get(id1)! - assert retrieved1 == value1 - - // Test deduplication with different reference - ref2 := dedupestor.Reference{ - owner: 1 - id: 2 - } - id2 := ds.store(value1, ref2)! - assert id1 == id2 // Should return same id for same data - - // Test different data gets different id - value2 := 'test data 2'.bytes() - ref3 := dedupestor.Reference{ - owner: 1 - id: 3 - } - id3 := ds.store(value2, ref3)! - assert id1 != id3 // Should be different id for different data - - retrieved2 := ds.get(id3)! - assert retrieved2 == value2 -} - -fn test_size_limit() ! { - mut ds := new( - path: '/tmp/dedupestor_test_size' - reset: true - )! - - // Test data under size limit (1KB) - small_data := []u8{len: 1024, init: u8(index)} - ref := dedupestor.Reference{ - owner: 1 - id: 1 - } - small_id := ds.store(small_data, ref)! - retrieved := ds.get(small_id)! - assert retrieved == small_data - - // Test data over size limit (2MB) - large_data := []u8{len: 2 * 1024 * 1024, init: u8(index)} - if _ := ds.store(large_data, ref) { - assert false, 'Expected error for data exceeding size limit' - } -} - -fn test_exists() ! { - mut ds := new( - path: '/tmp/dedupestor_test_exists' - reset: true - )! - - value := 'test data'.bytes() - ref := dedupestor.Reference{ - owner: 1 - id: 1 - } - id := ds.store(value, ref)! - - assert ds.id_exists(id) == true - assert ds.id_exists(u32(99)) == false -} - -fn test_multiple_operations() ! { - mut ds := new( - path: '/tmp/dedupestor_test_multiple' - reset: true - )! - - // Store multiple values - mut values := [][]u8{} - mut ids := []u32{} - - for i in 0 .. 5 { - value := 'test data ${i}'.bytes() - values << value - ref := dedupestor.Reference{ - owner: 1 - id: u32(i) - } - id := ds.store(value, ref)! - ids << id - } - - // Verify all values can be retrieved - for i, id in ids { - retrieved := ds.get(id)! - assert retrieved == values[i] - } - - // Test deduplication by storing same values again - for i, value in values { - ref := dedupestor.Reference{ - owner: 2 - id: u32(i) - } - id := ds.store(value, ref)! - assert id == ids[i] // Should get same id for same data - } -} - -fn test_references() ! { - mut ds := new( - path: '/tmp/dedupestor_test_refs' - reset: true - )! - - // Store same data with different references - value := 'test data'.bytes() - ref1 := dedupestor.Reference{ - owner: 1 - id: 1 - } - ref2 := dedupestor.Reference{ - owner: 1 - id: 2 - } - ref3 := dedupestor.Reference{ - owner: 2 - id: 1 - } - - // Store with first reference - id := ds.store(value, ref1)! - - // Store same data with second reference - id2 := ds.store(value, ref2)! - assert id == id2 // Same id for same data - - // Store same data with third reference - id3 := ds.store(value, ref3)! - assert id == id3 // Same id for same data - - // Delete first reference - data should still exist - ds.delete(id, ref1)! - assert ds.id_exists(id) == true - - // Delete second reference - data should still exist - ds.delete(id, ref2)! - assert ds.id_exists(id) == true - - // Delete last reference - data should be gone - ds.delete(id, ref3)! - assert ds.id_exists(id) == false - - // Verify data is actually deleted by trying to get it - if _ := ds.get(id) { - assert false, 'Expected error getting deleted data' - } -} diff --git a/libarchive/dedupestor/dedupestor.v b/libarchive/dedupestor/dedupestor.v deleted file mode 100644 index 558c042b..00000000 --- a/libarchive/dedupestor/dedupestor.v +++ /dev/null @@ -1,10 +0,0 @@ -module dedupestor - -import crypto.blake2b - -pub const max_value_size = 1024 * 1024 // 1MB - -// hash_data calculates the blake160 hash of the given data and returns it as a hex string. -pub fn hash_data(data []u8) string { - return blake2b.sum160(data).hex() -} diff --git a/libarchive/dedupestor/metadata.v b/libarchive/dedupestor/metadata.v deleted file mode 100644 index 5eec62b8..00000000 --- a/libarchive/dedupestor/metadata.v +++ /dev/null @@ -1,109 +0,0 @@ -module dedupestor - -// Metadata represents a stored value with its ID and references -pub struct Metadata { -pub: - id u32 -pub mut: - references []Reference -} - -// Reference represents a reference to stored data -pub struct Reference { -pub: - owner u16 - id u32 -} - -// to_bytes converts Metadata to bytes for storage -pub fn (m Metadata) to_bytes() []u8 { - mut bytes := u32_to_bytes(m.id) - for ref in m.references { - bytes << ref.to_bytes() - } - return bytes -} - -// bytes_to_metadata converts bytes back to Metadata -pub fn bytes_to_metadata(b []u8) Metadata { - if b.len < 4 { - return Metadata{ - id: 0 - references: []Reference{} - } - } - - id := bytes_to_u32(b[0..4]) - mut refs := []Reference{} - - // Parse references (each reference is 6 bytes) - mut i := 4 - for i < b.len { - if i + 6 <= b.len { - refs << bytes_to_reference(b[i..i + 6]) - } - i += 6 - } - - return Metadata{ - id: id - references: refs - } -} - -// add_reference adds a new reference if it doesn't already exist -pub fn (mut m Metadata) add_reference(ref Reference) !Metadata { - // Check if reference already exists - for existing in m.references { - if existing.owner == ref.owner && existing.id == ref.id { - return m - } - } - - m.references << ref - return m -} - -// remove_reference removes a reference if it exists -pub fn (mut m Metadata) remove_reference(ref Reference) !Metadata { - mut new_refs := []Reference{} - for existing in m.references { - if existing.owner != ref.owner || existing.id != ref.id { - new_refs << existing - } - } - m.references = new_refs - return m -} - -// to_bytes converts Reference to bytes -pub fn (r Reference) to_bytes() []u8 { - mut bytes := []u8{len: 6} - bytes[0] = u8(r.owner) - bytes[1] = u8(r.owner >> 8) - bytes[2] = u8(r.id) - bytes[3] = u8(r.id >> 8) - bytes[4] = u8(r.id >> 16) - bytes[5] = u8(r.id >> 24) - return bytes -} - -// bytes_to_reference converts bytes to Reference -pub fn bytes_to_reference(b []u8) Reference { - owner := u16(b[0]) | (u16(b[1]) << 8) - id := u32(b[2]) | (u32(b[3]) << 8) | (u32(b[4]) << 16) | (u32(b[5]) << 24) - return Reference{ - owner: owner - id: id - } -} - -// Helper function to convert u32 to []u8 -fn u32_to_bytes(n u32) []u8 { - return [u8(n), u8(n >> 8), u8(n >> 16), u8(n >> 24)] -} - -// Helper function to convert []u8 to u32 -fn bytes_to_u32(b []u8) u32 { - return u32(b[0]) | (u32(b[1]) << 8) | (u32(b[2]) << 16) | (u32(b[3]) << 24) -} diff --git a/libarchive/dedupestor/metadata_test.v b/libarchive/dedupestor/metadata_test.v deleted file mode 100644 index df86e405..00000000 --- a/libarchive/dedupestor/metadata_test.v +++ /dev/null @@ -1,121 +0,0 @@ -module dedupestor - -fn test_reference_bytes_conversion() { - ref := Reference{ - owner: 12345 - id: 67890 - } - - bytes := ref.to_bytes() - recovered := bytes_to_reference(bytes) - - assert ref.owner == recovered.owner - assert ref.id == recovered.id -} - -fn test_metadata_bytes_conversion() { - mut metadata := Metadata{ - id: 42 - references: []Reference{} - } - - ref1 := Reference{ - owner: 1 - id: 100 - } - ref2 := Reference{ - owner: 2 - id: 200 - } - - metadata = metadata.add_reference(ref1)! - metadata = metadata.add_reference(ref2)! - - bytes := metadata.to_bytes() - recovered := bytes_to_metadata(bytes) - - assert metadata.id == recovered.id - assert metadata.references.len == recovered.references.len - assert metadata.references[0].owner == recovered.references[0].owner - assert metadata.references[0].id == recovered.references[0].id - assert metadata.references[1].owner == recovered.references[1].owner - assert metadata.references[1].id == recovered.references[1].id -} - -fn test_add_reference() { - mut metadata := Metadata{ - id: 1 - references: []Reference{} - } - - ref1 := Reference{ - owner: 1 - id: 100 - } - ref2 := Reference{ - owner: 2 - id: 200 - } - - // Add first reference - metadata = metadata.add_reference(ref1)! - assert metadata.references.len == 1 - assert metadata.references[0].owner == ref1.owner - assert metadata.references[0].id == ref1.id - - // Add second reference - metadata = metadata.add_reference(ref2)! - assert metadata.references.len == 2 - assert metadata.references[1].owner == ref2.owner - assert metadata.references[1].id == ref2.id - - // Try adding duplicate reference - metadata = metadata.add_reference(ref1)! - assert metadata.references.len == 2 // Length shouldn't change -} - -fn test_remove_reference() { - mut metadata := Metadata{ - id: 1 - references: []Reference{} - } - - ref1 := Reference{ - owner: 1 - id: 100 - } - ref2 := Reference{ - owner: 2 - id: 200 - } - - metadata = metadata.add_reference(ref1)! - metadata = metadata.add_reference(ref2)! - - // Remove first reference - metadata = metadata.remove_reference(ref1)! - assert metadata.references.len == 1 - assert metadata.references[0].owner == ref2.owner - assert metadata.references[0].id == ref2.id - - // Remove non-existent reference - metadata = metadata.remove_reference(Reference{ owner: 999, id: 999 })! - assert metadata.references.len == 1 // Length shouldn't change - - // Remove last reference - metadata = metadata.remove_reference(ref2)! - assert metadata.references.len == 0 -} - -fn test_empty_metadata_bytes() { - empty := bytes_to_metadata([]u8{}) - assert empty.id == 0 - assert empty.references.len == 0 -} - -fn test_u32_bytes_conversion() { - n := u32(0x12345678) - bytes := u32_to_bytes(n) - recovered := bytes_to_u32(bytes) - assert n == recovered -} diff --git a/libarchive/dify/.heroscript b/libarchive/dify/.heroscript deleted file mode 100644 index 3c099ae1..00000000 --- a/libarchive/dify/.heroscript +++ /dev/null @@ -1,11 +0,0 @@ -!!hero_code.generate_installer - name: "dify" - classname: "DifyInstaller" - hasconfig: true - singleton: true - default: false - title: "" - templates: true - build: true - startupmanager: true - supported_platforms: "" diff --git a/libarchive/dify/dify_actions.v b/libarchive/dify/dify_actions.v deleted file mode 100644 index 6240f422..00000000 --- a/libarchive/dify/dify_actions.v +++ /dev/null @@ -1,162 +0,0 @@ -module dify - -import incubaid.herolib.osal.core as osal -import incubaid.herolib.ui.console -import incubaid.herolib.core.texttools -import incubaid.herolib.core.pathlib -import incubaid.herolib.osal.systemd -import incubaid.herolib.osal.startupmanager -import incubaid.herolib.installers.ulist -import incubaid.herolib.installers.lang.golang -import incubaid.herolib.installers.lang.rust -import incubaid.herolib.installers.lang.python -import incubaid.herolib.installers.virt.docker as docker_installer -import os - -fn startupcmd() ![]zinit.ZProcessNewArgs { - mut installer := get()! - mut res := []zinit.ZProcessNewArgs{} - mut cfg := get()! - res << zinit.ZProcessNewArgs - { - name: 'docker' - cmd: 'dockerd' - startuptype: .systemd - } - - cmd := " - echo 'zinit starting dify' - export COMPOSE_PROJECT_NAME=${cfg.project_name} - export SECRET_KEY=${cfg.secret_key} - export INIT_PASSWORD=${cfg.init_password} - cd ${cfg.path}/docker/ - docker compose --env-file ${cfg.path}/docker/.env up - " - res << zinit.ZProcessNewArgs - { - name: 'dify' - cmd: cmd - startuptype: .systemd - } - - return res -} - -fn running() !bool { - mut installer := get()! - cfg := get()! - cmd := ' - sleep 120 - docker ps | grep dify-web - ' - res := os.execute(cmd) - return res.exit_code == 0 -} - -fn start_pre() ! { -} - -fn start_post() ! { -} - -fn stop_pre() ! { -} - -fn stop_post() ! { -} - -//////////////////// following actions are not specific to instance of the object - -// checks if a certain version or above is installed -fn installed() !bool { - mut cfg := get()! - mut docker := docker_installer.get()! - docker.install()! - - cmd := 'docker ps | grep dify-web' - result := os.execute(cmd) - if result.exit_code != 0 { - return false - } - return true -} - -// get the Upload List of the files -fn ulist_get() !ulist.UList { - // optionally build a UList which is all paths which are result of building, is then used e.g. in upload - return ulist.UList{} -} - -// uploads to S3 server if configured -fn upload() ! { - // installers.upload( - // cmdname: 'dify' - // source: '${gitpath}/target/x86_64-unknown-linux-musl/release/dify' - // )! -} - -fn install() ! { - mut docker := docker_installer.get()! - mut cfg := get()! - docker.install()! - cmd := " - [ -d ${cfg.path} ] || git clone https://github.com/langgenius/dify.git -b 1.4.0 ${cfg.path} - cp ${cfg.path}/docker/.env.example ${cfg.path}/docker/.env - - sudo bash -c 'cat > /etc/docker/daemon.json < 0 { - for install_action in install_actions { - heroscript := install_action.heroscript() - mut obj2 := heroscript_loads(heroscript)! - set(obj2)! - } - } - mut other_actions := plbook.find(filter: 'dify.')! - for other_action in other_actions { - if other_action.name in ['destroy', 'install', 'build'] { - mut p := other_action.params - reset := p.get_default_false('reset') - if other_action.name == 'destroy' || reset { - console.print_debug('install action dify.destroy') - destroy()! - } - if other_action.name == 'install' { - console.print_debug('install action dify.install') - install()! - } - } - if other_action.name in ['start', 'stop', 'restart'] { - mut p := other_action.params - name := p.get('name')! - mut dify_obj := get(name: name)! - console.print_debug('action object:\n${dify_obj}') - if other_action.name == 'start' { - console.print_debug('install action dify.${other_action.name}') - dify_obj.start()! - } - - if other_action.name == 'stop' { - console.print_debug('install action dify.${other_action.name}') - dify_obj.stop()! - } - if other_action.name == 'restart' { - console.print_debug('install action dify.${other_action.name}') - dify_obj.restart()! - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS /////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// - -fn startupmanager_get(cat startupmanager.StartupManagerType) !startupmanager.StartupManager { - // unknown - // screen - // zinit - // tmux - // systemd - match cat { - .screen { - console.print_debug('startupmanager: zinit') - return startupmanager.get(.screen)! - } - .zinit { - console.print_debug('startupmanager: zinit') - return startupmanager.get(.zinit)! - } - .systemd { - console.print_debug('startupmanager: systemd') - return startupmanager.get(.systemd)! - } - else { - console.print_debug('startupmanager: auto') - return startupmanager.get(.auto)! - } - } -} - -// load from disk and make sure is properly intialized -pub fn (mut self DifyInstaller) reload() ! { - self = obj_init(self)! -} - -pub fn (mut self DifyInstaller) start() ! { - if self.running()! { - return - } - - console.print_header('dify start') - - if !installed()! { - install()! - } - - configure()! - - start_pre()! - - for zprocess in startupcmd()! { - mut sm := startupmanager_get(zprocess.startuptype)! - - console.print_debug('starting dify with ${zprocess.startuptype}...') - - sm.new(zprocess)! - - sm.start(zprocess.name)! - } - - start_post()! - - for _ in 0 .. 50 { - if self.running()! { - return - } - time.sleep(100 * time.millisecond) - } - return error('dify did not install properly.') -} - -pub fn (mut self DifyInstaller) install_start(args InstallArgs) ! { - switch(self.name) - self.install(args)! - self.start()! -} - -pub fn (mut self DifyInstaller) stop() ! { - switch(self.name) - stop_pre()! - for zprocess in startupcmd()! { - mut sm := startupmanager_get(zprocess.startuptype)! - sm.stop(zprocess.name)! - } - stop_post()! -} - -pub fn (mut self DifyInstaller) restart() ! { - switch(self.name) - self.stop()! - self.start()! -} - -pub fn (mut self DifyInstaller) running() !bool { - switch(self.name) - - // walk over the generic processes, if not running return - for zprocess in startupcmd()! { - if zprocess.startuptype != .screen { - mut sm := startupmanager_get(zprocess.startuptype)! - r := sm.running(zprocess.name)! - if r == false { - return false - } - } - } - return running()! -} - -@[params] -pub struct InstallArgs { -pub mut: - reset bool -} - -pub fn (mut self DifyInstaller) install(args InstallArgs) ! { - switch(self.name) - if args.reset || (!installed()!) { - install()! - } -} - -pub fn (mut self DifyInstaller) build() ! { - switch(self.name) - build()! -} - -pub fn (mut self DifyInstaller) destroy() ! { - switch(self.name) - self.stop() or {} - destroy()! -} - -// switch instance to be used for dify -pub fn switch(name string) { -} diff --git a/libarchive/dify/dify_model.v b/libarchive/dify/dify_model.v deleted file mode 100644 index 894af5be..00000000 --- a/libarchive/dify/dify_model.v +++ /dev/null @@ -1,68 +0,0 @@ -module dify - -import incubaid.herolib.data.paramsparser -import incubaid.herolib.data.encoderhero -import os -import rand - -pub const version = '0.0.0' -const singleton = true -const default = false - -// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED -@[heap] -pub struct DifyInstaller { -pub mut: - name string = 'default' - path string = '/opt/dify' - init_password string - secret_key string - project_name string = 'dify' - compose_file string = '/opt/dify/docker/docker-compose.yaml' -} - -// your checking & initialization code if needed -fn obj_init(mycfg_ DifyInstaller) !DifyInstaller { - mut mycfg := mycfg_ - if mycfg.path == '' { - mycfg.path = '/opt/dify' - } - - if mycfg.secret_key == '' { - mycfg.secret_key = rand.hex(42) - } - - if mycfg.init_password == '' { - mycfg.init_password = 'slfjbv9NaflKsgjv' - } - - if mycfg.project_name == '' { - mycfg.project_name = 'dify' - } - - if mycfg.compose_file == '' { - mycfg.compose_file = '/opt/dify/docker/docker-compose.yaml' - } - - return mycfg -} - -// called before start if done -fn configure() ! { - // mut installer := get()! - // mut mycode := $tmpl('templates/atemplate.yaml') - // mut path := pathlib.get_file(path: cfg.configpath, create: true)! - // path.write(mycode)! - // console.print_debug(mycode) -} - -/////////////NORMALLY NO NEED TO TOUCH - -pub fn heroscript_dumps(obj DifyInstaller) !string { - return encoderhero.encode[DifyInstaller](obj)! -} - -pub fn heroscript_loads(heroscript string) !DifyInstaller { - mut obj := encoderhero.decode[DifyInstaller](heroscript)! - return obj -} diff --git a/libarchive/dify/readme.md b/libarchive/dify/readme.md deleted file mode 100644 index d49194a2..00000000 --- a/libarchive/dify/readme.md +++ /dev/null @@ -1,42 +0,0 @@ -# dify - -dify - -To get started - -```v - - -import incubaid.herolib.installers.something.dify as dify_installer - -heroscript:=" -!!dify.configure name:'test' - password: '1234' - port: 7701 - -!!dify.start name:'test' reset:1 -" - -dify_installer.play(heroscript=heroscript)! - -//or we can call the default and do a start with reset -//mut installer:= dify_installer.get()! -//installer.start(reset:true)! - - - - -``` - -## example heroscript - -```hero -!!dify.configure - homedir: '/home/user/dify' - username: 'admin' - password: 'secretpassword' - title: 'Some Title' - host: 'localhost' - port: 8888 - -``` diff --git a/libarchive/dify/templates/atemplate.yaml b/libarchive/dify/templates/atemplate.yaml deleted file mode 100644 index a4c386dd..00000000 --- a/libarchive/dify/templates/atemplate.yaml +++ /dev/null @@ -1,5 +0,0 @@ - - -name: ${cfg.configpath} - - diff --git a/libarchive/doctree/README.md b/libarchive/doctree/README.md deleted file mode 100644 index 3a1e51c8..00000000 --- a/libarchive/doctree/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# Doctree Module - -The primary goal of this module is to transform structured document collections into a format suitable for various outputs. It handles the complexities of finding collections, loading their content, processing includes, definitions, and macros, and exporting the final result while managing assets like images and files. - -## Key Concepts - -* **Tree:** The central component (`doctree.Tree`) that holds one or more `Collection` instances. It orchestrates the scanning, processing, and exporting of all contained collections. -* **Collection:** A directory that is marked as a collection by the presence of a `.collection` file. A collection groups related documents (pages, images, files) and can have its own configuration defined within the `.collection` file. -* **.collection file:** A file placed in a directory to designate it as a collection. This file can optionally contain parameters (using the `paramsparser` format) such as a custom name for the collection. - -## How it Works (Workflow) - -The typical workflow involves creating a `Tree`, scanning for collections, and then exporting the processed content.å - -1. **Create Tree:** Initialize a `doctree.Tree` instance using `doctree.new()`. -2. **Scan:** Use the `tree.scan()` method, providing a path to a directory or a Git repository URL. The scanner recursively looks for directories containing a `.collection` file. -3. **Load Content:** For each identified collection, the module loads its content, including markdown pages, images, and other files. -4. **Process Content:** The loaded content is processed. This includes handling definitions, includes (content from other files), and macros (dynamic content generation or transformation). -5. **Generate Output Paths:** The module determines the final paths for all processed files and assets in the destination directory. -6. **Export:** The `tree.export()` method writes the processed content and assets to the specified destination directory, maintaining the desired structure. - -## Usage (For Developers) - -Here's a basic example of how to use the `doctree` module in your V project: - -```v -import incubaid.herolib.data.doctree -// 1. Create a new Tree instance -mut tree := doctree.new(name: 'my_documentation')! - -// 2. Scan a directory containing your collections -// Replace './docs' with the actual path to your document collections -tree.scan(path: './docs')! - -// use from URL -//git_url string -//git_reset bool -//git_pull bool -tree.scan(git_url: 'https://git.threefold.info/tfgrid/docs_tfgrid4/src/branch/main/collections')! - -// 3. Export the processed content to a destination directory -// Replace './output' with your desired output path -// if redis then the metadata will be put in redis -tree.export(destination: './output', redis:true)! - -println('Documentation successfully exported to ./output') - -``` - -## Structure of a Collection - -A collection is a directory containing a `.collection` file. Inside a collection directory, you would typically organize your content like this: - -``` -my_collection/ -├── .collection -├── page1.md -├── page2.md -├── images/ -│ ├── image1.png -│ └── image2.jpg -└── files/ - ├── document.pdf - └── data.csv -``` - -Markdown files (`.md`) are treated as pages. - -## use Play - -```heroscript - -!!doctree.collection name:"my_local_docs" path:"./docs" - -!!doctree.collection name:"tfgrid_docs" - git_url:"https://git.threefold.info/tfgrid/docs_tfgrid4/src/branch/main/collections" - git_reset: true - git_pull: true - -//is optional, if not specified then will be at ${os.home_dir()}/hero/var/doctree/main -!!doctree.export name: "my_local_docs", destination: "/tmp/1" exclude_errors:0 reset:1 -``` - -## Redis Structure - -when using the export redis:true argument, which is default - -in redis we will find - -```bash -#redis hsets: -doctree:$collectionname $pagename $rel_path_in_collection -doctree:$collectionname $filename.$ext $rel_path_in_collection -doctree:meta $collectionname $collectionpath_on_disk -``` diff --git a/libarchive/doctree/collection/collection.v b/libarchive/doctree/collection/collection.v deleted file mode 100644 index eec0cfd7..00000000 --- a/libarchive/doctree/collection/collection.v +++ /dev/null @@ -1,48 +0,0 @@ -module collection - -import incubaid.herolib.core.pathlib { Path } -import incubaid.herolib.data.doctree.collection.data -import incubaid.herolib.core.texttools - -@[heap] -pub struct Collection { -pub mut: - name string @[required] - path Path @[required] - fail_on_error bool - heal bool = true - pages map[string]&data.Page - files map[string]&data.File - images map[string]&data.File - errors []CollectionError -} - -@[params] -pub struct CollectionNewArgs { -pub mut: - name string @[required] - path string @[required] - heal bool = true // healing means we fix images, if selected will automatically load, remove stale links - load bool = true - fail_on_error bool -} - -// get a new collection -pub fn new(args_ CollectionNewArgs) !Collection { - mut args := args_ - args.name = texttools.name_fix(args.name) - - mut pp := pathlib.get_dir(path: args.path)! // will raise error if path doesn't exist - mut collection := Collection{ - name: args.name - path: pp - heal: args.heal - fail_on_error: args.fail_on_error - } - - if args.load { - collection.scan() or { return error('Error scanning collection ${args.name}:\n${err}') } - } - - return collection -} diff --git a/libarchive/doctree/collection/data/error.v b/libarchive/doctree/collection/data/error.v deleted file mode 100644 index 3730befa..00000000 --- a/libarchive/doctree/collection/data/error.v +++ /dev/null @@ -1,29 +0,0 @@ -module data - -import incubaid.herolib.core.pathlib { Path } - -pub enum PageErrorCat { - unknown - file_not_found - image_not_found - page_not_found - def -} - -pub struct PageMultiError { - Error -pub mut: - errs []PageError -} - -pub fn (err PageMultiError) msg() string { - return 'Failed in processing page with one or multiple errors: ${err.errs}' -} - -pub struct PageError { - Error -pub mut: - path Path - msg string - cat PageErrorCat -} diff --git a/libarchive/doctree/collection/data/file.v b/libarchive/doctree/collection/data/file.v deleted file mode 100644 index 848adaa9..00000000 --- a/libarchive/doctree/collection/data/file.v +++ /dev/null @@ -1,102 +0,0 @@ -module data - -import incubaid.herolib.core.pathlib -import os - -pub enum FileStatus { - unknown - ok - error -} - -pub enum FileType { - file - image -} - -@[heap] -pub struct File { -pub mut: - collection_path pathlib.Path - name string // received a name fix - ext string - path pathlib.Path - pathrel string - state FileStatus - pages_linked []&Page // pointer to pages which use this file - ftype FileType - collection_name string -} - -@[params] -pub struct NewFileArgs { -pub: - name string // received a name fix - collection_path pathlib.Path - pathrel string - path pathlib.Path - collection_name string @[required] -} - -pub fn new_file(args NewFileArgs) !File { - mut f := File{ - name: args.name - path: args.path - collection_path: args.collection_path - pathrel: args.pathrel - collection_name: args.collection_name - } - - f.init()! - - return f -} - -pub fn (file File) file_name() string { - return '${file.name}.${file.ext}' -} - -// parses file name, extension and relative path -pub fn (mut file File) init() ! { - if file.path.is_image() { - file.ftype = .image - } - - file.name = file.path.name_fix_no_ext() - file.ext = file.path.path.all_after_last('.').to_lower() - - path_rel := file.path.path_relative(file.collection_path.path) or { - return error('cannot get relative path.\n${err}') - } - - file.pathrel = path_rel.trim('/') -} - -fn (mut file File) delete() ! { - file.path.delete()! -} - -// TODO: what if this is moved to another collection, or outside the scope of the tree? -fn (mut file File) mv(dest string) ! { - mut destination := pathlib.get_dir(path: dest)! // will fail if dir doesn't exist - - os.mv(file.path.path, destination.path) or { - return error('could not move ${file.path.path} to ${destination.path} .\n${err}\n${file}') - } - - // need to get relative path in, in relation to collection - file.pathrel = destination.path_relative(file.collection_path.path)! - file.path = destination -} - -fn (mut file File) exists() !bool { - return file.path.exists() -} - -pub fn (file_ File) copy(dest string) ! { - mut file := file_ - mut dest2 := pathlib.get(dest) - file.path.copy(dest: dest2.path, rsync: false) or { - return error('Could not copy file: ${file.path.path} to ${dest} .\n${err}\n${file}') - } -} diff --git a/libarchive/doctree/collection/data/page.v b/libarchive/doctree/collection/data/page.v deleted file mode 100644 index 7e1c88e0..00000000 --- a/libarchive/doctree/collection/data/page.v +++ /dev/null @@ -1,171 +0,0 @@ -module data - -import incubaid.herolib.core.pathlib -import incubaid.herolib.data.markdown.elements { Action, Doc, Element, Frontmatter2 } -import incubaid.herolib.data.markdown - -pub enum PageStatus { - unknown - ok - error -} - -@[heap] -pub struct Page { -mut: - doc &Doc @[str: skip] - element_cache map[int]Element - changed bool -pub mut: - name string // received a name fix - alias string // a proper name for e.g. def - path pathlib.Path - collection_name string -} - -@[params] -pub struct NewPageArgs { -pub: - name string @[required] - path pathlib.Path @[required] - collection_name string @[required] -} - -pub fn new_page(args NewPageArgs) !Page { - if args.collection_name == '' { - return error('page collection name must not be empty') - } - - if args.name == '' { - return error('page name must not be empty') - } - mut doc := markdown.new(path: args.path.path, collection_name: args.collection_name) or { - return error('failed to parse doc for path ${args.path.path}\n${err}') - } - children := doc.children_recursive() - mut element_cache := map[int]Element{} - for child in children { - element_cache[child.id] = child - } - mut new_page := Page{ - element_cache: element_cache - name: args.name - path: args.path - collection_name: args.collection_name - doc: &doc - } - return new_page -} - -// return doc, reparse if needed -pub fn (mut page Page) doc() !&Doc { - if page.changed { - content := page.doc.markdown()! - page.reparse_doc(content)! - } - - return page.doc -} - -// return doc, reparse if needed -fn (page Page) doc_immute() !&Doc { - if page.changed { - content := page.doc.markdown()! - doc := markdown.new(content: content, collection_name: page.collection_name)! - return &doc - } - return page.doc -} - -// reparse doc markdown and assign new doc to page -fn (mut page Page) reparse_doc(content string) ! { - doc := markdown.new(content: content, collection_name: page.collection_name)! - page.element_cache = map[int]Element{} - for child in doc.children_recursive() { - page.element_cache[child.id] = child - } - - page.doc = &doc - page.changed = false -} - -pub fn (page Page) key() string { - return '${page.collection_name}:${page.name}' -} - -pub fn (page Page) get_linked_pages() ![]string { - doc := page.doc_immute()! - return doc.linked_pages -} - -pub fn (page Page) get_markdown() !string { - mut doc := page.doc_immute()! - mut result := '' - for element in doc.children { - if element is Frontmatter2 { - continue - } - result += element.markdown()! - } - return result -} - -pub fn (mut page Page) set_content(content string) ! { - page.reparse_doc(content)! -} - -fn (mut page Page) get_element(element_id int) !Element { - return page.element_cache[element_id] or { - return error('no element found with id ${element_id}') - } -} - -// TODO: this should not be allowed (giving access to modify page content to any caller) -pub fn (mut page Page) get_all_actions() ![]&Action { - mut actions := []&Action{} - mut doc := page.doc()! - for element in doc.children_recursive() { - if element is Action { - actions << element - } - } - - return actions -} - -pub fn (page Page) get_include_actions() ![]Action { - mut actions := []Action{} - // TODO: check if below is necessary - // mut doc := page.doc_immute()! - for element in page.doc.children_recursive() { - if element is Action { - if element.action.actor == 'wiki' && element.action.name == 'include' { - actions << *element - } - } - } - return actions -} - -pub fn (mut page Page) set_action_element_to_processed(element_id int) ! { - mut element := page.element_cache[element_id] or { - return error('page ${page.path} doc has no element with id ${element_id}') - } - - if mut element is Action { - element.action_processed = true - page.changed = true - return - } - - return error('element with id ${element_id} is not an action') -} - -pub fn (mut page Page) set_element_content_no_reparse(element_id int, content string) ! { - mut element := page.element_cache[element_id] or { - return error('page ${page.path} doc has no element with id ${element_id}') - } - - element.content = content - page.changed = true -} diff --git a/libarchive/doctree/collection/data/process_aliases.v b/libarchive/doctree/collection/data/process_aliases.v deleted file mode 100644 index 5db4af9b..00000000 --- a/libarchive/doctree/collection/data/process_aliases.v +++ /dev/null @@ -1,49 +0,0 @@ -module data - -import incubaid.herolib.core.texttools -import incubaid.herolib.data.markdown.elements - -// returns !!wiki.def actions -pub fn (mut page Page) get_def_actions() ![]elements.Action { - mut doc := page.doc()! - mut def_actions := doc.actionpointers(actor: 'wiki', name: 'def') - mut ret := []elements.Action{} - for def in def_actions { - ret << *def - } - - return ret -} - -// returns page aliases, and removes processed action's content -pub fn (mut page Page) process_def_action(element_id int) ![]string { - mut action_element := page.get_element(element_id)! - - mut doc := page.doc()! - if mut action_element is elements.Action { - mut aliases := map[string]bool{} - def_action := action_element.action - page.alias = def_action.params.get_default('name', '')! - if page.alias == '' { - page.alias = doc.header_name()! - } - - action_element.action_processed = true - action_element.content = '' - page.changed = true - for alias in def_action.params.get_list('alias')! { - mut processed_alias := alias - if processed_alias.to_lower().ends_with('.md') { - // remove the .md at end - processed_alias = processed_alias[0..page.collection_name.len - 3] - } - - processed_alias = texttools.name_fix(processed_alias).replace('_', '') - aliases[processed_alias] = true - } - - return aliases.keys() - } - - return error('element with id ${element_id} is not an action') -} diff --git a/libarchive/doctree/collection/data/process_aliases_test.v b/libarchive/doctree/collection/data/process_aliases_test.v deleted file mode 100644 index 1f4ce60d..00000000 --- a/libarchive/doctree/collection/data/process_aliases_test.v +++ /dev/null @@ -1,40 +0,0 @@ -module data - -import incubaid.herolib.core.pathlib - -fn test_get_def_actions() { - mut page1_path := pathlib.get_file(path: '/tmp/page1', create: true)! - page1_content := "!!wiki.def alias:'tf-dev,cloud-dev,threefold-dev' name:'about us'" - page1_path.write(page1_content)! - mut page1 := new_page(name: 'page1', path: page1_path, collection_name: 'col1')! - def_actions := page1.get_def_actions()! - - assert def_actions.len == 1 - - action := def_actions[0].action - assert action.params.get('name')! == 'about us' - mut aliases := action.params.get_list('alias')! - aliases.sort() - assert ['cloud-dev', 'tf-dev', 'threefold-dev'] == aliases -} - -fn test_process_def_action() { - // create page with def action - // get actions - // process def action - // processed page should have action removed and alias set - mut page1_path := pathlib.get_file(path: '/tmp/page1', create: true)! - page1_content := "!!wiki.def alias:'tf-dev,cloud-dev,threefold-dev' name:'about us'" - page1_path.write(page1_content)! - mut page1 := new_page(name: 'page1', path: page1_path, collection_name: 'col1')! - def_actions := page1.get_def_actions()! - - assert def_actions.len == 1 - - mut aliases := page1.process_def_action(def_actions[0].id)! - assert page1.get_markdown()! == '' - assert page1.alias == 'about us' - - aliases.sort() - assert ['clouddev', 'tfdev', 'threefolddev'] == aliases -} diff --git a/libarchive/doctree/collection/data/process_def_pointers.v b/libarchive/doctree/collection/data/process_def_pointers.v deleted file mode 100644 index 8e11f660..00000000 --- a/libarchive/doctree/collection/data/process_def_pointers.v +++ /dev/null @@ -1,34 +0,0 @@ -module data - -// returns all page def elements (similar to *DEF) -pub fn (mut page Page) get_def_names() ![]string { - mut defnames := map[string]bool{} - mut doc := page.doc()! - for defitem in doc.defpointers() { - defname := defitem.nameshort - defnames[defname] = true - } - - return defnames.keys() -} - -// removes the def content, and generates a link to the page -pub fn (mut page Page) set_def_links(def_data map[string][]string) ! { - mut doc := page.doc()! - for mut defitem in doc.defpointers() { - defname := defitem.nameshort - - v := def_data[defname] or { continue } - if v.len != 2 { - return error('invalid def data length: expected 2, found ${v.len}') - } - - defitem.pagekey = v[0] - defitem.pagename = v[1] - - defitem.process_link()! - } - - doc.process()! - page.changed = true -} diff --git a/libarchive/doctree/collection/data/process_def_pointers_test.v b/libarchive/doctree/collection/data/process_def_pointers_test.v deleted file mode 100644 index 80b77c1d..00000000 --- a/libarchive/doctree/collection/data/process_def_pointers_test.v +++ /dev/null @@ -1,23 +0,0 @@ -module data - -import incubaid.herolib.core.pathlib -import rand - -fn test_process_def_pointers() { - // create a page with def pointers to two different pages - // set def links on page. - // processed page should have links to the other two pages - mut page1_path := pathlib.get_file(path: '/tmp/page1', create: true)! - alias1, alias2 := rand.string(5).to_upper(), rand.string(5).to_upper() - page1_content := '*${alias1}\n*${alias2}' - page1_path.write(page1_content)! - mut page1 := new_page(name: 'page1', path: page1_path, collection_name: 'col1')! - - mut defs := map[string][]string{} - defs['${alias1.to_lower()}'] = ['col2:page2', 'page2 alias'] - defs['${alias2.to_lower()}'] = ['col3:page3', 'my page3 alias'] - - page1.set_def_links(defs)! - - assert page1.get_markdown()! == '[page2 alias](col2:page2.md)\n[my page3 alias](col3:page3.md)' -} diff --git a/libarchive/doctree/collection/data/process_link.v b/libarchive/doctree/collection/data/process_link.v deleted file mode 100644 index c77645c9..00000000 --- a/libarchive/doctree/collection/data/process_link.v +++ /dev/null @@ -1,59 +0,0 @@ -module data - -import incubaid.herolib.core.texttools -import incubaid.herolib.data.markdown.elements -import incubaid.herolib.data.doctree.pointer - -// Note: doc should not get reparsed after invoking this method -pub fn (page Page) process_links(paths map[string]string) ![]string { - mut not_found := map[string]bool{} - mut doc := page.doc_immute()! - for mut element in doc.children_recursive() { - if mut element is elements.Link { - if element.cat == .html || (element.cat == .anchor && element.url == '') { - // is external link or same page anchor, nothing to process - // maybe in the future check if exists - continue - } - mut name := texttools.name_fix_keepext(element.filename) - mut site := texttools.name_fix(element.site) - if site == '' { - site = page.collection_name - } - pointerstr := '${site}:${name}' - - ptr := pointer.pointer_new(text: pointerstr, collection: page.collection_name)! - mut path := paths[ptr.str()] or { - not_found[ptr.str()] = true - continue - } - - if ptr.cat == .page && ptr.str() !in doc.linked_pages { - doc.linked_pages << ptr.str() - } - - if ptr.collection == page.collection_name { - // same directory - path = './' + path.all_after_first('/') - } else { - path = '../${path}' - } - - if ptr.cat == .image && element.extra.trim_space() != '' { - path += ' ${element.extra.trim_space()}' - } - - mut out := '[${element.description}](${path})' - if ptr.cat == .image { - out = '!${out}' - } - - element.content = out - element.processed = false - element.state = .linkprocessed - element.process()! - } - } - - return not_found.keys() -} diff --git a/libarchive/doctree/collection/data/process_link_test.v b/libarchive/doctree/collection/data/process_link_test.v deleted file mode 100644 index 1dc16969..00000000 --- a/libarchive/doctree/collection/data/process_link_test.v +++ /dev/null @@ -1,20 +0,0 @@ -module data - -import incubaid.herolib.core.pathlib - -fn test_process_link() { - mut page1_path := pathlib.get_file(path: '/tmp/page1', create: true)! - page1_content := '[some page description](col1:page1.md)\n![some other page desc](col2:img.png)' - page1_path.write(page1_content)! - mut page1 := new_page(name: 'page1', path: page1_path, collection_name: 'col1')! - - paths := { - 'col1:page1.md': 'col1/page1.md' - 'col2:img.png': 'col2/img/img.png' - } - - notfound := page1.process_links(paths)! - assert notfound.len == 0 - - assert page1.get_markdown()! == '[some page description](./page1.md)\n![some other page desc](../col2/img/img.png)' -} diff --git a/libarchive/doctree/collection/data/process_macros.v b/libarchive/doctree/collection/data/process_macros.v deleted file mode 100644 index 50eac55a..00000000 --- a/libarchive/doctree/collection/data/process_macros.v +++ /dev/null @@ -1,24 +0,0 @@ -module data - -import incubaid.herolib.core.playmacros -import incubaid.herolib.data.markdown.elements { Action } - -pub fn (mut page Page) process_macros() ! { - mut mydoc := page.doc()! - for mut element in mydoc.children_recursive() { - if mut element is Action { - if element.action.actiontype == .macro { - content := playmacros.play_macro(element.action)! - page.changed = true - if content.len > 0 { - element.content = content - } - } - } - } - - if page.changed { - page.reparse_doc(page.doc.markdown()!)! - page.process_macros()! - } -} diff --git a/libarchive/doctree/collection/error.v b/libarchive/doctree/collection/error.v deleted file mode 100644 index f5fb8947..00000000 --- a/libarchive/doctree/collection/error.v +++ /dev/null @@ -1,68 +0,0 @@ -module collection - -import incubaid.herolib.core.pathlib { Path } -import incubaid.herolib.core.base -import incubaid.herolib.ui.console - -pub enum CollectionErrorCat { - unknown - image_double - file_double - file_not_found - image_not_found - page_double - page_not_found - sidebar - circular_import - def - summary - include -} - -pub struct CollectionError { - Error -pub mut: - path Path - msg string - cat CollectionErrorCat -} - -pub fn (e CollectionError) msg() string { - return 'collection error:\n\tPath: ${e.path.path}\n\tError message: ${e.msg}\n\tCategory: ${e.cat}' -} - -pub fn (mut collection Collection) error(args CollectionError) ! { - if collection.fail_on_error { - return args - } - - collection.errors << args - console.print_stderr(args.msg) -} - -pub struct ObjNotFound { - Error -pub: - name string - collection string - info string -} - -pub fn (err ObjNotFound) msg() string { - return 'Could not find object with name ${err.name} in collection ${err.collection}: ${err.info}' -} - -// write errors.md in the collection, this allows us to see what the errors are -pub fn (collection Collection) errors_report(col_name string, dest_ string) ! { - // console.print_debug("====== errors report: ${dest_} : ${collection.errors.len}\n${collection.errors}") - mut context := base.context()! - mut redis := context.redis()! - mut dest := pathlib.get_file(path: dest_, create: true)! - if collection.errors.len == 0 { - dest.delete()! - return - } - c := $tmpl('template/errors.md') - dest.write(c)! - redis.hset('doctree:${col_name}', 'errors', 'errors.md')! -} diff --git a/libarchive/doctree/collection/export.v b/libarchive/doctree/collection/export.v deleted file mode 100644 index 7208de9c..00000000 --- a/libarchive/doctree/collection/export.v +++ /dev/null @@ -1,149 +0,0 @@ -module collection - -import incubaid.herolib.core.pathlib -import incubaid.herolib.core.base -import incubaid.herolib.core.texttools.regext -import os -import incubaid.herolib.data.doctree.pointer -import incubaid.herolib.data.doctree.collection.data - -@[params] -pub struct CollectionExportArgs { -pub mut: - destination pathlib.Path @[required] - file_paths map[string]string - reset bool = true - keep_structure bool // wether the structure of the src collection will be preserved or not - exclude_errors bool // wether error reporting should be exported as well - replacer ?regext.ReplaceInstructions - redis bool = true -} - -pub fn (mut c Collection) export(args CollectionExportArgs) ! { - dir_src := pathlib.get_dir(path: args.destination.path + '/' + c.name, create: true)! - - mut cfile := pathlib.get_file(path: dir_src.path + '/.collection', create: true)! // will auto save it - cfile.write("name:${c.name} src:'${c.path.path}'")! - - mut context := base.context()! - mut redis := context.redis()! - redis.hset('doctree:path', '${c.name}', dir_src.path)! - - c.errors << export_pages(c.name, c.path.path, c.pages.values(), - dir_src: dir_src - file_paths: args.file_paths - keep_structure: args.keep_structure - replacer: args.replacer - redis: args.redis - )! - - c.export_files(c.name, dir_src, args.reset)! - c.export_images(c.name, dir_src, args.reset)! - c.export_linked_pages(c.name, dir_src)! - - if !args.exclude_errors { - c.errors_report(c.name, '${dir_src.path}/errors.md')! - } -} - -@[params] -pub struct ExportPagesArgs { -pub mut: - dir_src pathlib.Path - file_paths map[string]string - keep_structure bool // wether the structure of the src collection will be preserved or not - replacer ?regext.ReplaceInstructions - redis bool = true -} - -// creates page file, processes page links, then writes page -fn export_pages(col_name string, col_path string, pages []&data.Page, args ExportPagesArgs) ![]CollectionError { - mut errors := []CollectionError{} - - mut context := base.context()! - mut redis := context.redis()! - - for page in pages { - dest := if args.keep_structure { - relpath := page.path.path.trim_string_left(col_path) - '${args.dir_src.path}/${relpath}' - } else { - '${args.dir_src.path}/${page.name}.md' - } - - not_found := page.process_links(args.file_paths)! - - for pointer_str in not_found { - ptr := pointer.pointer_new(text: pointer_str)! - cat := match ptr.cat { - .page { - CollectionErrorCat.page_not_found - } - .image { - CollectionErrorCat.image_not_found - } - else { - CollectionErrorCat.file_not_found - } - } - errors << CollectionError{ - path: page.path - msg: '${ptr.cat} ${ptr.str()} not found' - cat: cat - } - } - - mut dest_path := pathlib.get_file(path: dest, create: true)! - mut markdown := page.get_markdown()! - if mut replacer := args.replacer { - markdown = replacer.replace(text: markdown)! - } - dest_path.write(markdown)! - redis.hset('doctree:${col_name}', page.name, '${page.name}.md')! - } - return errors -} - -fn (c Collection) export_files(col_name string, dir_src pathlib.Path, reset bool) ! { - mut context := base.context()! - mut redis := context.redis()! - for _, file in c.files { - mut d := '${dir_src.path}/img/${file.name}.${file.ext}' - if reset || !os.exists(d) { - file.copy(d)! - } - redis.hset('doctree:${col_name}', '${file.name}.${file.ext}', 'img/${file.name}.${file.ext}')! - } -} - -fn (c Collection) export_images(col_name string, dir_src pathlib.Path, reset bool) ! { - mut context := base.context()! - mut redis := context.redis()! - for _, file in c.images { - mut d := '${dir_src.path}/img/${file.name}.${file.ext}' - redis.hset('doctree:${col_name}', '${file.name}.${file.ext}', 'img/${file.name}.${file.ext}')! - if reset || !os.exists(d) { - file.copy(d)! - } - } -} - -fn (c Collection) export_linked_pages(col_name string, dir_src pathlib.Path) ! { - mut context := base.context()! - mut redis := context.redis()! - collection_linked_pages := c.get_collection_linked_pages()! - mut linked_pages_file := pathlib.get_file(path: dir_src.path + '/.linkedpages', create: true)! - redis.hset('doctree:${col_name}', 'linkedpages', '${linked_pages_file.name()}.md')! - linked_pages_file.write(collection_linked_pages.join_lines())! -} - -fn (c Collection) get_collection_linked_pages() ![]string { - mut linked_pages_set := map[string]bool{} - for _, page in c.pages { - for linked_page in page.get_linked_pages()! { - linked_pages_set[linked_page] = true - } - } - - return linked_pages_set.keys() -} diff --git a/libarchive/doctree/collection/export_test.v b/libarchive/doctree/collection/export_test.v deleted file mode 100644 index b753d0aa..00000000 --- a/libarchive/doctree/collection/export_test.v +++ /dev/null @@ -1,56 +0,0 @@ -module collection - -import incubaid.herolib.core.pathlib -import os - -const test_dir = '${os.dir(@FILE)}/testdata/export_test' -const tree_dir = '${test_dir}/mytree' -const export_dir = '${test_dir}/export' -const export_expected_dir = '${test_dir}/export_expected' - -fn testsuite_begin() { - pathlib.get_dir( - path: export_dir - empty: true - )! -} - -fn testsuite_end() { - pathlib.get_dir( - path: export_dir - empty: true - )! -} - -fn test_export() { - mut col := Collection{ - name: 'col1' - path: pathlib.get('${tree_dir}/dir1') - } - col.scan()! - - path_dest := pathlib.get_dir(path: '${export_dir}/src', create: true)! - col.export( - destination: path_dest - file_paths: { - 'col2:file3.md': 'col2/file3.md' - } - )! - - col1_path := '${export_dir}/src/col1' - expected_col1_path := '${export_expected_dir}/src/col1' - assert os.read_file('${col1_path}/.collection')! == "name:col1 src:'${tree_dir}/dir1'" - assert os.read_file('${col1_path}/.linkedpages')! == os.read_file('${expected_col1_path}/.linkedpages')! - assert os.read_file('${col1_path}/errors.md')! == '# Errors - - -## page_not_found - -path: ${tree_dir}/dir1/dir2/file1.md - -msg: page col3:file5.md not found - -' - assert os.read_file('${col1_path}/file1.md')! == os.read_file('${expected_col1_path}/file1.md')! - assert os.read_file('${col1_path}/file2.md')! == os.read_file('${expected_col1_path}/file2.md')! -} diff --git a/libarchive/doctree/collection/getters.v b/libarchive/doctree/collection/getters.v deleted file mode 100644 index 0d0470f7..00000000 --- a/libarchive/doctree/collection/getters.v +++ /dev/null @@ -1,45 +0,0 @@ -module collection - -import incubaid.herolib.data.doctree.collection.data - -// gets page with specified name from collection -pub fn (collection Collection) page_get(name string) !&data.Page { - return collection.pages[name] or { - return ObjNotFound{ - collection: collection.name - name: name - } - } -} - -pub fn (collection Collection) page_exists(name string) bool { - return name in collection.pages -} - -// gets image with specified name from collection -pub fn (collection Collection) get_image(name string) !&data.File { - return collection.images[name] or { - return ObjNotFound{ - collection: collection.name - name: name - } - } -} - -pub fn (collection Collection) image_exists(name string) bool { - return name in collection.images -} - -// gets file with specified name form collection -pub fn (collection Collection) get_file(name string) !&data.File { - return collection.files[name] or { - return ObjNotFound{ - collection: collection.name - name: name - } - } -} - -pub fn (collection Collection) file_exists(name string) bool { - return name in collection.files -} diff --git a/libarchive/doctree/collection/scan.v b/libarchive/doctree/collection/scan.v deleted file mode 100644 index cc58572e..00000000 --- a/libarchive/doctree/collection/scan.v +++ /dev/null @@ -1,250 +0,0 @@ -module collection - -import incubaid.herolib.conversiontools.imagemagick -import incubaid.herolib.core.pathlib { Path } -import incubaid.herolib.data.doctree.pointer -import incubaid.herolib.data.doctree.collection.data - -// walk over one specific collection, find all files and pages -pub fn (mut collection Collection) scan() ! { - collection.scan_directory(mut collection.path)! -} - -// path is the full path -fn (mut collection Collection) scan_directory(mut p Path) ! { - mut entry_list := p.list(recursive: false)! - for mut entry in entry_list.paths { - if collection.should_skip_entry(mut entry) { - continue - } - - if !entry.exists() { - collection.error( - path: entry - msg: 'Entry ${entry.name()} does not exists' - cat: .unknown - )! - continue - } - - if mut entry.is_link() { - link_real_path := entry.realpath() // this is with the symlink resolved - collection_abs_path := collection.path.absolute() - if entry.extension_lower() == 'md' { - // means we are linking pages,this should not be done, need or change - collection.error( - path: entry - msg: 'Markdown files (${entry.path}) must not be linked' - cat: .unknown - ) or { return error('Failed to collection error ${entry.path}:\n${err}') } - continue - } - - if !link_real_path.starts_with(collection_abs_path) { - // means we are not in the collection so we need to copy - entry.unlink()! // will transform link to become the file or dir it points too - } else { - // TODO: why do we need this? - entry.relink()! // will check that the link is on the file with the shortest path - } - } - - if entry.is_dir() { - collection.scan_directory(mut entry) or { - return error('Failed to scan directory ${entry.path}:\n${err}') - } - continue - } - - if entry.extension_lower() == '' { - continue - } - - match entry.extension_lower() { - 'md' { - collection.add_page(mut entry) or { - return error('Failed to add page ${entry.path}:\n${err}') - } - } - else { - collection.file_image_remember(mut entry) or { - return error('Failed to remember image ${entry.path}:\n${err}') - } - } - } - } -} - -fn (mut c Collection) should_skip_entry(mut entry Path) bool { - entry_name := entry.name() - - // entries that start with . or _ are ignored - if entry_name.starts_with('.') || entry_name.starts_with('_') { - return true - } - - // TODO: why do we skip all these??? - - if entry.cat == .linkfile { - // means we link to a file which is in the folder, so can be loaded later, nothing to do here - return true - } - - if entry.is_dir() && entry_name.starts_with('gallery_') { - return true - } - - if entry_name.to_lower() == 'defs.md' { - return true - } - - if entry_name.contains('.test') { - return true - } - - if entry.path.starts_with('sidebar') { - return true - } - - return false -} - -// remember the file, so we know if we have duplicates -// also fixes the name -fn (mut collection Collection) file_image_remember(mut p Path) ! { - if collection.heal { - p.path_normalize()! - } - mut ptr := pointer.pointer_new( - collection: collection.name - text: p.name() - )! - - if ptr.is_file_video_html() { - collection.add_file(mut p)! - return - } - - if ptr.is_image() { - if collection.heal && imagemagick.installed() { - mut image := imagemagick.image_new(mut p) - - imagemagick.downsize(path: p.path)! - // after downsize it could be the path has been changed, need to set it on the file - if p.path != image.path.path { - p.path = image.path.path - p.check() - } - } - - // TODO: what are we trying to do? - if !collection.image_exists(ptr.name) { - collection.add_image(mut p)! - } - - mut image_file := collection.get_image(ptr.name)! - mut image_file_path := image_file.path.path - if p.path.len <= image_file_path.len { - // nothing to be done, because the already existing file is shortest or equal - return - } - // file double is the one who already existed, need to change the path and can delete original - // TODO: this is clearly a bug - image_file.path = image_file.path - image_file.init()! - if collection.heal { - p.delete()! - } - - return - } - - return error('unsupported file type: ${ptr.extension}') -} - -// add a page to the collection, specify existing path -// the page will be parsed as markdown -pub fn (mut collection Collection) add_page(mut p Path) ! { - if collection.heal { - p.path_normalize() or { return error('Failed to normalize path ${p.path}\n${err}') } - } - - mut ptr := pointer.pointer_new( - collection: collection.name - text: p.name() - ) or { return error('Failed to get pointer for ${p.name()}\n${err}') } - - // in case heal is true pointer_new can normalize the path - if collection.page_exists(ptr.name) { - collection.error( - path: p - msg: 'Can\'t add ${p.path}: a page named ${ptr.name} already exists in the collection' - cat: .page_double - ) or { return error('Failed to report collection error for ${p.name()}\n${err}') } - return - } - - new_page := data.new_page( - name: ptr.name - path: p - collection_name: collection.name - ) or { return error('Failed to create new page for ${ptr.name}\n${err}') } - - collection.pages[ptr.name] = &new_page -} - -// add a file to the collection, specify existing path -pub fn (mut collection Collection) add_file(mut p Path) ! { - if collection.heal { - p.path_normalize()! - } - mut ptr := pointer.pointer_new( - collection: collection.name - text: p.name() - )! - - // in case heal is true pointer_new can normalize the path - if collection.file_exists(ptr.name) { - collection.error( - path: p - msg: 'Can\'t add ${p.path}: a file named ${ptr.name} already exists in the collection' - cat: .file_double - )! - return - } - - mut new_file := data.new_file( - path: p - collection_path: collection.path - collection_name: collection.name - )! - collection.files[ptr.name] = &new_file -} - -// add a image to the collection, specify existing path -pub fn (mut collection Collection) add_image(mut p Path) ! { - if collection.heal { - p.path_normalize()! - } - mut ptr := pointer.pointer_new( - collection: collection.name - text: p.name() - )! - - // in case heal is true pointer_new can normalize the path - if collection.image_exists(ptr.name) { - collection.error( - path: p - msg: 'Can\'t add ${p.path}: a file named ${ptr.name} already exists in the collection' - cat: .image_double - )! - return - } - - mut image_file := &data.File{ - path: p - collection_path: collection.path - } - image_file.init()! - collection.images[ptr.name] = image_file -} diff --git a/libarchive/doctree/collection/scan_test.v b/libarchive/doctree/collection/scan_test.v deleted file mode 100644 index 19e2dcae..00000000 --- a/libarchive/doctree/collection/scan_test.v +++ /dev/null @@ -1,121 +0,0 @@ -module collection - -import incubaid.herolib.core.pathlib - -fn test_add_page_success() { - /* - create collection - add page - check page in collection - */ - - mut col := Collection{ - name: 'col1' - path: pathlib.get('/tmp/col1') - } - - mut page1_path := pathlib.get_file(path: '/tmp/col1/page1.md', create: true)! - col.add_page(mut page1_path)! - assert col.page_exists('page1') - - mut page2_path := pathlib.get_file(path: '/tmp/col1/page:hamada.md', create: true)! - col.add_page(mut page2_path)! - assert col.page_exists('page_hamada') -} - -fn test_add_page_already_exists() { - /* - create collection - add page with path /tmp/col1/page1.md - add page with path /tmp/col1/dir/page1.md - second add should fail and error reported to collection errors - */ - - mut col := Collection{ - name: 'col1' - path: pathlib.get('/tmp/col1') - } - - mut page1_path := pathlib.get_file(path: '/tmp/col1/page1.md', create: true)! - col.add_page(mut page1_path)! - assert col.page_exists('page1') - - mut page2_path := pathlib.get_file(path: '/tmp/col1/dir1/page1.md', create: true)! - col.add_page(mut page2_path)! - - assert col.errors.len == 1 - assert col.errors[0].msg == "Can't add /tmp/col1/dir1/page1.md: a page named page1 already exists in the collection" -} - -fn test_add_image_success() { - mut col := Collection{ - name: 'col1' - path: pathlib.get('/tmp/col1') - } - - mut page1_path := pathlib.get_file(path: '/tmp/col1/image.png', create: true)! - col.add_image(mut page1_path)! - assert col.image_exists('image') - - mut page2_path := pathlib.get_file(path: '/tmp/col1/image:2.jpg', create: true)! - col.add_image(mut page2_path)! - assert col.image_exists('image_2') -} - -fn test_add_file_success() { - mut col := Collection{ - name: 'col1' - path: pathlib.get('/tmp/col1') - } - - mut page1_path := pathlib.get_file(path: '/tmp/col1/file1.html', create: true)! - col.add_file(mut page1_path)! - assert col.file_exists('file1') - - mut page2_path := pathlib.get_file(path: '/tmp/col1/file:2.mp4', create: true)! - col.add_file(mut page2_path)! - assert col.file_exists('file_2') -} - -fn test_file_image_remember() { - mut col := Collection{ - name: 'col1' - path: pathlib.get('/tmp/col1') - } - - mut file1_path := pathlib.get_file(path: '/tmp/col1/image.png', create: true)! - col.file_image_remember(mut file1_path)! - assert col.image_exists('image') - - mut file2_path := pathlib.get_file(path: '/tmp/col1/file.html', create: true)! - col.file_image_remember(mut file2_path)! - assert col.file_exists('file') - - mut file3_path := pathlib.get_file(path: '/tmp/col1/file2.unknownext', create: true)! - col.file_image_remember(mut file3_path)! - assert col.file_exists('file2') -} - -fn test_scan_directory() { - mut file := pathlib.get_file(path: '/tmp/mytree/dir1/.collection', create: true)! - file.write('name:col1')! - file = pathlib.get_file(path: '/tmp/mytree/dir1/file1.md', create: true)! - file = pathlib.get_file(path: '/tmp/mytree/dir1/file2.html', create: true)! - file = pathlib.get_file(path: '/tmp/mytree/dir1/file3.png', create: true)! - file = pathlib.get_file(path: '/tmp/mytree/dir1/dir2/file4.md', create: true)! - file = pathlib.get_file(path: '/tmp/mytree/dir1/.shouldbeskipped', create: true)! - file = pathlib.get_file(path: '/tmp/mytree/dir1/_shouldbeskipped', create: true)! - - mut col := Collection{ - name: 'col1' - path: pathlib.get('/tmp/mytree/dir1') - } - - col.scan()! - assert col.page_exists('file1') - assert col.file_exists('file2') - assert col.image_exists('file3') - assert col.page_exists('file4') - assert !col.file_exists('.shouldbeskipped') - assert !col.file_exists('_shouldbeskipped') -} diff --git a/libarchive/doctree/collection/template/errors.md b/libarchive/doctree/collection/template/errors.md deleted file mode 100644 index b687f189..00000000 --- a/libarchive/doctree/collection/template/errors.md +++ /dev/null @@ -1,11 +0,0 @@ -# Errors - -@for error in collection.errors - -## @error.cat - -path: @error.path.path - -msg: @error.msg - -@end diff --git a/libarchive/doctree/collection/testdata/.gitignore b/libarchive/doctree/collection/testdata/.gitignore deleted file mode 100644 index b22bcd28..00000000 --- a/libarchive/doctree/collection/testdata/.gitignore +++ /dev/null @@ -1 +0,0 @@ -export_test/export diff --git a/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/.collection b/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/.collection deleted file mode 100644 index 7b7df04d..00000000 --- a/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/.collection +++ /dev/null @@ -1 +0,0 @@ -name:col1 src:'/Users/timurgordon/code/github/incubaid/herolib/lib/data/doctree/collection/testdata/export_test/mytree/dir1' \ No newline at end of file diff --git a/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/.linkedpages b/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/.linkedpages deleted file mode 100644 index a0814882..00000000 --- a/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/.linkedpages +++ /dev/null @@ -1 +0,0 @@ -col2:file3.md \ No newline at end of file diff --git a/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/errors.md b/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/errors.md deleted file mode 100644 index 78ac27bc..00000000 --- a/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/errors.md +++ /dev/null @@ -1,9 +0,0 @@ -# Errors - - -## page_not_found - -path: /Users/timurgordon/code/github/incubaid/herolib/herolib/data/doctree/collection/testdata/export_test/mytree/dir1/dir2/file1.md - -msg: page col3:file5.md not found - diff --git a/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/file1.md b/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/file1.md deleted file mode 100644 index 2d8d0af0..00000000 --- a/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/file1.md +++ /dev/null @@ -1 +0,0 @@ -[not existent page](col3:file5.md) \ No newline at end of file diff --git a/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/file2.md b/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/file2.md deleted file mode 100644 index 71f48c9b..00000000 --- a/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/file2.md +++ /dev/null @@ -1 +0,0 @@ -[some page](../col2/file3.md) \ No newline at end of file diff --git a/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/img/image.png b/libarchive/doctree/collection/testdata/export_test/export_expected/src/col1/img/image.png deleted file mode 100644 index e69de29b..00000000 diff --git a/libarchive/doctree/collection/testdata/export_test/mytree/dir1/.collection b/libarchive/doctree/collection/testdata/export_test/mytree/dir1/.collection deleted file mode 100644 index bf5be63f..00000000 --- a/libarchive/doctree/collection/testdata/export_test/mytree/dir1/.collection +++ /dev/null @@ -1 +0,0 @@ -name:col1 \ No newline at end of file diff --git a/libarchive/doctree/collection/testdata/export_test/mytree/dir1/dir2/file1.md b/libarchive/doctree/collection/testdata/export_test/mytree/dir1/dir2/file1.md deleted file mode 100644 index 2d8d0af0..00000000 --- a/libarchive/doctree/collection/testdata/export_test/mytree/dir1/dir2/file1.md +++ /dev/null @@ -1 +0,0 @@ -[not existent page](col3:file5.md) \ No newline at end of file diff --git a/libarchive/doctree/collection/testdata/export_test/mytree/dir1/file2.md b/libarchive/doctree/collection/testdata/export_test/mytree/dir1/file2.md deleted file mode 100644 index 09f30c32..00000000 --- a/libarchive/doctree/collection/testdata/export_test/mytree/dir1/file2.md +++ /dev/null @@ -1 +0,0 @@ -[some page](col2:file3.md) \ No newline at end of file diff --git a/libarchive/doctree/collection/testdata/export_test/mytree/dir1/image.png b/libarchive/doctree/collection/testdata/export_test/mytree/dir1/image.png deleted file mode 100644 index e69de29b..00000000 diff --git a/libarchive/doctree/error.v b/libarchive/doctree/error.v deleted file mode 100644 index 3e0aff1e..00000000 --- a/libarchive/doctree/error.v +++ /dev/null @@ -1,45 +0,0 @@ -module doctree - -import incubaid.herolib.data.doctree.pointer - -pub struct ObjNotFound { - Error -pub: - name string - collection string - info string -} - -pub fn (err ObjNotFound) msg() string { - return '"Could not find object with name ${err.name} in collection:${err.collection}.\n${err.info}' -} - -pub struct CollectionNotFound { - Error -pub: - pointer pointer.Pointer - msg string -} - -pub fn (err CollectionNotFound) msg() string { - if err.msg.len > 0 { - return err.msg - } - return '"Cannot find collection ${err.pointer} in tree.\n}' -} - -// the next is our custom error for objects not found -pub struct NoOrTooManyObjFound { - Error -pub: - tree &Tree - pointer pointer.Pointer - nr int -} - -pub fn (err NoOrTooManyObjFound) msg() string { - if err.nr > 0 { - return 'Too many obj found for ${err.tree.name}. Pointer: ${err.pointer}' - } - return 'No obj found for ${err.tree.name}. Pointer: ${err.pointer}' -} diff --git a/libarchive/doctree/export.v b/libarchive/doctree/export.v deleted file mode 100644 index f495f8df..00000000 --- a/libarchive/doctree/export.v +++ /dev/null @@ -1,100 +0,0 @@ -module doctree - -import incubaid.herolib.core.pathlib -import incubaid.herolib.data.doctree.collection { Collection } -import incubaid.herolib.ui.console -import incubaid.herolib.core.texttools.regext -import os - -@[params] -pub struct TreeExportArgs { -pub mut: - destination string - reset bool = true - keep_structure bool // wether the structure of the src collection will be preserved or not - exclude_errors bool // wether error reporting should be exported as well - toreplace string - concurrent bool = true - redis bool = true -} - -// export all collections to chosen directory . -// all names will be in name_fixed mode . -// all images in img/ -pub fn (mut tree Tree) export(args_ TreeExportArgs) ! { - mut args := args_ - if args.toreplace.len > 0 { - mut ri := regext.regex_instructions_new() - ri.add_from_text(args.toreplace)! - tree.replacer = ri - } - - if args.destination.len == 0 { - args.destination = '${os.home_dir()}/hero/var/doctree/main' - } - console.print_header('export tree: name:${tree.name} to ${args.destination}') - - mut dest_path := pathlib.get_dir(path: args.destination, create: true)! - if args.reset { - dest_path.empty()! - } - - tree.process_defs()! - tree.process_includes()! - tree.process_actions_and_macros()! // process other actions and macros - - file_paths := tree.generate_paths()! - - console.print_green('exporting collections') - - if args.concurrent { - mut ths := []thread !{} - for _, mut col in tree.collections { - ths << spawn fn (mut col Collection, dest_path pathlib.Path, file_paths map[string]string, args TreeExportArgs) ! { - col.export( - destination: dest_path - file_paths: file_paths - reset: args.reset - keep_structure: args.keep_structure - exclude_errors: args.exclude_errors - redis: args.redis - // TODO: replacer: tree.replacer - )! - }(mut col, dest_path, file_paths, args) - } - for th in ths { - th.wait() or { panic(err) } - } - } else { - for _, mut col in tree.collections { - col.export( - destination: dest_path - file_paths: file_paths - reset: args.reset - keep_structure: args.keep_structure - exclude_errors: args.exclude_errors - replacer: tree.replacer - redis: args.redis - )! - } - } -} - -fn (mut t Tree) generate_paths() !map[string]string { - mut paths := map[string]string{} - for _, col in t.collections { - for _, page in col.pages { - paths['${col.name}:${page.name}.md'] = '${col.name}/${page.name}.md' - } - - for _, image in col.images { - paths['${col.name}:${image.file_name()}'] = '${col.name}/img/${image.file_name()}' - } - - for _, file in col.files { - paths['${col.name}:${file.file_name()}'] = '${col.name}/img/${file.file_name()}' - } - } - - return paths -} diff --git a/libarchive/doctree/export_test.v b/libarchive/doctree/export_test.v deleted file mode 100644 index 4cd5c109..00000000 --- a/libarchive/doctree/export_test.v +++ /dev/null @@ -1,91 +0,0 @@ -module doctree - -import incubaid.herolib.core.pathlib -import os - -const test_dir = '${os.dir(@FILE)}/testdata/export_test' -const tree_dir = '${test_dir}/mytree' -const export_dir = '${test_dir}/export' -const export_expected_dir = '${test_dir}/export_expected' - -fn testsuite_begin() { - pathlib.get_dir( - path: export_dir - empty: true - )! -} - -fn testsuite_end() { - pathlib.get_dir( - path: export_dir - empty: true - )! -} - -fn test_export() { - /* - tree_root/ - dir1/ - .collection - dir2/ - file1.md - file2.md - image.png - dir3/ - .collection - file3.md - - export: - export_dest/ - src/ - col1/ - .collection - .linkedpages - errors.md - img/ - image.png - file1.md - file2.md - col2/ - .collection - .linkedpages - file3.md - - .edit/ - - test: - - create tree - - add files/pages and collections to tree - - export tree - - ensure tree structure is valid - */ - - mut tree := new(name: 'mynewtree')! - tree.add_collection(path: '${tree_dir}/dir1', name: 'col1')! - tree.add_collection(path: '${tree_dir}/dir3', name: 'col2')! - - tree.export(destination: '${export_dir}')! - - col1_path := '${export_dir}/col1' - expected_col1_path := '${export_expected_dir}/col1' - assert os.read_file('${col1_path}/.collection')! == "name:col1 src:'${tree_dir}/dir1'" - assert os.read_file('${col1_path}/.linkedpages')! == os.read_file('${expected_col1_path}/.linkedpages')! - assert os.read_file('${col1_path}/errors.md')! == '# Errors - - -## page_not_found - -path: ${tree_dir}/dir1/dir2/file1.md - -msg: page col3:file5.md not found - -' - assert os.read_file('${col1_path}/file1.md')! == os.read_file('${expected_col1_path}/file1.md')! - assert os.read_file('${col1_path}/file2.md')! == os.read_file('${expected_col1_path}/file2.md')! - - col2_path := '${export_dir}/col2' - expected_col2_path := '${export_expected_dir}/col2' - assert os.read_file('${col2_path}/.linkedpages')! == '' - assert os.read_file('${col2_path}/.collection')! == "name:col2 src:'${tree_dir}/dir3'" - assert os.read_file('${col2_path}/file3.md')! == '' -} diff --git a/libarchive/doctree/getters.v b/libarchive/doctree/getters.v deleted file mode 100644 index e52acf47..00000000 --- a/libarchive/doctree/getters.v +++ /dev/null @@ -1,72 +0,0 @@ -module doctree - -import incubaid.herolib.data.doctree.collection -import incubaid.herolib.data.doctree.collection.data -import incubaid.herolib.data.doctree.pointer - -pub fn (tree Tree) get_collection(name string) !&collection.Collection { - col := tree.collections[name] or { return error('collection ${name} not found') } - - return col -} - -pub fn (tree Tree) get_collection_with_pointer(p pointer.Pointer) !&collection.Collection { - return tree.get_collection(p.collection) or { - return CollectionNotFound{ - pointer: p - msg: '${err}' - } - } -} - -// get the page from pointer string: $tree:$collection:$name or -// $collection:$name or $name -pub fn (tree Tree) page_get(pointerstr string) !&data.Page { - p := pointer.pointer_new(text: pointerstr)! - return tree.get_page_with_pointer(p)! -} - -fn (tree Tree) get_page_with_pointer(p pointer.Pointer) !&data.Page { - col := tree.get_collection_with_pointer(p)! - new_page := col.page_get(p.name)! - - return new_page -} - -// get the page from pointer string: $tree:$collection:$name or -// $collection:$name or $name -pub fn (tree Tree) get_image(pointerstr string) !&data.File { - p := pointer.pointer_new(text: pointerstr)! - col := tree.get_collection_with_pointer(p)! - image := col.get_image(p.name)! - - return image -} - -// get the file from pointer string: $tree:$collection:$name or -// $collection:$name or $name -pub fn (tree Tree) get_file(pointerstr string) !&data.File { - p := pointer.pointer_new(text: pointerstr)! - col := tree.get_collection_with_pointer(p)! - new_file := col.get_file(p.name)! - - return new_file -} - -pub fn (tree Tree) page_exists(pointerstr string) bool { - p := pointer.pointer_new(text: pointerstr) or { return false } - col := tree.get_collection_with_pointer(p) or { return false } - return col.page_exists(p.name) -} - -pub fn (tree Tree) image_exists(pointerstr string) bool { - p := pointer.pointer_new(text: pointerstr) or { return false } - col := tree.get_collection_with_pointer(p) or { return false } - return col.image_exists(p.name) -} - -pub fn (tree Tree) file_exists(pointerstr string) bool { - p := pointer.pointer_new(text: pointerstr) or { return false } - col := tree.get_collection_with_pointer(p) or { return false } - return col.file_exists(p.name) -} diff --git a/libarchive/doctree/getters_test.v b/libarchive/doctree/getters_test.v deleted file mode 100644 index c184db01..00000000 --- a/libarchive/doctree/getters_test.v +++ /dev/null @@ -1,35 +0,0 @@ -module doctree - -import incubaid.herolib.core.pathlib -import os - -fn test_page_get() { - mut file1_path := pathlib.get_file(path: '/tmp/mytree/dir1/file2.md', create: true)! - file1_path.write('[some page](col2:file3.md)')! - mut file2_path := pathlib.get_file(path: '/tmp/mytree/dir1/image.png', create: true)! - mut file3_path := pathlib.get_file(path: '/tmp/mytree/dir1/dir2/file1.md', create: true)! - file3_path.write('[not existent page](col3:file5.md)')! - mut file4_path := pathlib.get_file(path: '/tmp/mytree/dir1/.collection', create: true)! - file4_path.write('name:col1')! - - mut file5_path := pathlib.get_file(path: '/tmp/mytree/dir3/.collection', create: true)! - file5_path.write('name:col2')! - mut file6_path := pathlib.get_file(path: '/tmp/mytree/dir3/file3.md', create: true)! - - mut tree := new(name: 'mynewtree')! - tree.add_collection(path: file1_path.parent()!.path, name: 'col1')! - tree.add_collection(path: file6_path.parent()!.path, name: 'col2')! - - mut page := tree.page_get('col1:file2.md')! - assert page.name == 'file2' - - mut image := tree.get_image('col1:image.png')! - assert image.file_name() == 'image.png' - - // these page pointers are faulty - - apple_ptr_faulty0 := 'col3:file1.md' - if p := tree.page_get('col3:file1.md') { - assert false, 'this should fail: faulty pointer ${apple_ptr_faulty0}' - } -} diff --git a/libarchive/doctree/list.v b/libarchive/doctree/list.v deleted file mode 100644 index 1b6be305..00000000 --- a/libarchive/doctree/list.v +++ /dev/null @@ -1,65 +0,0 @@ -module doctree - -import incubaid.herolib.ui.console - -// list_pages returns a map of collection names to a list of page names within that collection. -// The structure is map[collectionname][]pagename. -pub fn (mut t Tree) list_pages() map[string][]string { - mut result := map[string][]string{} - mut sorted_collections := t.collections.values() - sorted_collections.sort(a.name < b.name) - - for _, col in sorted_collections { - mut page_names := []string{} - mut sorted_pages := col.pages.values() - sorted_pages.sort(a.name < b.name) - for _, page in sorted_pages { - page_names << page.name - } - result[col.name] = page_names - } - return result -} - -// list_markdown returns the collections and their pages in markdown format. -pub fn (mut t Tree) list_markdown() string { - mut markdown_output := '' - pages_map := t.list_pages() - - if pages_map.len == 0 { - return 'No collections or pages found in this doctree.' - } - - for col_name, page_names in pages_map { - markdown_output += '## ${col_name}\n' - if page_names.len == 0 { - markdown_output += ' * No pages in this collection.\n' - } else { - for page_name in page_names { - markdown_output += ' * ${page_name}\n' - } - } - markdown_output += '\n' // Add a newline for spacing between collections - } - return markdown_output -} - -// print_pages prints the collections and their pages in a nice, easy-to-see format. -pub fn (mut t Tree) print_pages() { - pages_map := t.list_pages() - console.print_header('Doctree: ${t.name}') - if pages_map.len == 0 { - console.print_green('No collections or pages found in this doctree.') - return - } - for col_name, page_names in pages_map { - console.print_green('Collection: ${col_name}') - if page_names.len == 0 { - console.print_green(' No pages in this collection.') - } else { - for page_name in page_names { - console.print_item(' ${page_name}') - } - } - } -} diff --git a/libarchive/doctree/play.v b/libarchive/doctree/play.v deleted file mode 100644 index 59b5725e..00000000 --- a/libarchive/doctree/play.v +++ /dev/null @@ -1,63 +0,0 @@ -module doctree - -import incubaid.herolib.core.playbook { PlayBook } -// import incubaid.herolib.ui.console - -pub fn play(mut plbook PlayBook) ! { - if !plbook.exists(filter: 'doctree.') { - return - } - - mut doctrees := map[string]&Tree{} - - mut collection_actions := plbook.find(filter: 'doctree.scan')! - for mut action in collection_actions { - mut p := action.params - name := p.get_default('name', 'main')! - mut doctree := doctrees[name] or { - mut newdtr := new(name: name)! - doctrees[name] = newdtr - newdtr - } - path := p.get_default('path', '')! - git_url := p.get_default('git_url', '')! - git_reset := p.get_default_false('git_reset') - git_pull := p.get_default_false('git_pull') - doctree.scan(path: path, git_url: git_url, git_reset: git_reset, git_pull: git_pull)! - action.done = true - tree_set(doctree) - } - - mut export_actions := plbook.find(filter: 'doctree.export')! - if export_actions.len == 0 && collection_actions.len > 0 { - // Only auto-export if we have collections to export - name0 := 'main' - mut doctree0 := doctrees[name0] or { panic("can't find doctree with name ${name0}") } - doctree0.export()! - } - if export_actions.len > 0 { - if collection_actions.len == 0 { - println(plbook) - return error('No collections configured, use !!doctree.collection..., otherwise cannot export') - } - } - - for mut action in export_actions { - mut p := action.params - name := p.get_default('name', 'main')! - destination := p.get('destination')! - reset := p.get_default_false('reset') - exclude_errors := p.get_default_true('exclude_errors') - mut doctree := doctrees[name] or { return error("can't find doctree with name ${name}") } - doctree.export( - destination: destination - reset: reset - exclude_errors: exclude_errors - )! - action.done = true - } - - // println(tree_list()) - // println(tree_get("main")!) - // panic("sd") -} diff --git a/libarchive/doctree/pointer/pointer.v b/libarchive/doctree/pointer/pointer.v deleted file mode 100644 index 3170612d..00000000 --- a/libarchive/doctree/pointer/pointer.v +++ /dev/null @@ -1,106 +0,0 @@ -module pointer - -import incubaid.herolib.core.texttools - -pub enum PointerCat { - page - image - video - file - html -} - -// links to a page, image or file -pub struct Pointer { -pub mut: - collection string // is the key of a collection - name string // is name without extension, all namefixed (lowercase...) - cat PointerCat - extension string // e.g. jpg -} - -@[params] -pub struct NewPointerArgs { -pub: - // pointer string (e.g. col:page.md) - text string - // used if text does not have collection information - collection string -} - -// will return a clean pointer to a page, image or file -//``` -// input is e.g. mycollection:filename.jpg -// or filename.jpg -// or mypage.md -// -//``` -pub fn pointer_new(args NewPointerArgs) !Pointer { - mut txt := args.text.trim_space().replace('\\', '/').replace('//', '/') - - // take colon parts out - split_colons := txt.split(':') - if split_colons.len > 2 { - return error("pointer can only have 1 ':' inside. ${txt}") - } - - mut collection_name := args.collection - mut file_name := '' - if split_colons.len == 2 { - collection_name = texttools.name_fix_keepext(split_colons[0].all_after_last('/')) - file_name = texttools.name_fix_keepext(split_colons[1].all_after_last('/')) - } - - if collection_name == '' { - return error('provided args do not have collection information: ${args}') - } - - if split_colons.len == 1 { - file_name = texttools.name_fix_keepext(split_colons[0].all_after_last('/')) - } - - split_file_name := file_name.split('.') - file_name_no_extension := split_file_name[0] - mut extension := 'md' - if split_file_name.len > 1 { - extension = split_file_name[1] - } - - mut file_cat := PointerCat.page - match extension { - 'md' { - file_cat = .page - } - 'jpg', 'jpeg', 'svg', 'gif', 'png' { - file_cat = .image - } - 'html' { - file_cat = .html - } - 'mp4', 'mov' { - file_cat = .video - } - else { - file_cat = .file - } - } - - return Pointer{ - name: file_name_no_extension - collection: collection_name - extension: extension - cat: file_cat - } -} - -pub fn (p Pointer) is_image() bool { - return p.cat == .image -} - -pub fn (p Pointer) is_file_video_html() bool { - return p.cat == .file || p.cat == .video || p.cat == .html -} - -pub fn (p Pointer) str() string { - return '${p.collection}:${p.name}.${p.extension}' -} diff --git a/libarchive/doctree/pointer/pointer_test.v b/libarchive/doctree/pointer/pointer_test.v deleted file mode 100644 index 390c65ee..00000000 --- a/libarchive/doctree/pointer/pointer_test.v +++ /dev/null @@ -1,139 +0,0 @@ -module pointer - -import incubaid.herolib.ui.console - -// import incubaid.herolib.core.pathlib -// import incubaid.herolib.core.texttools - -// fn test_pointerpath() { -// p1 := pointerpath_new(path: '/tmp/A file.md') or { panic(err) } -// console.print_debug(p1) -// p1_compare := PointerPath{ -// pointer: Pointer{ -// collection: '' -// name: 'a_file' -// cat: .page -// extension: 'md' -// error: '' -// state: .unknown -// } -// path: pathlib.Path{ -// path: '/tmp/A file.md' -// cat: .unknown -// exist: .no -// } -// } -// assert p1 == p1_compare - -// p2 := pointerpath_new(path: '/tmp/ss/A__file.jpeg') or { panic(err) } -// p2_compare := PointerPath{ -// pointer: Pointer{ -// collection: '' -// name: 'a_file' -// cat: .image -// extension: 'jpeg' -// error: '' -// state: .unknown -// } -// path: pathlib.Path{ -// path: '/tmp/A__file.jpeg' -// cat: .unknown -// exist: .no -// } -// } - -// // assert p2==p2_compare -// } - -fn test_pointer() { - // p := pointer_new('Page__.md') or { panic(err) } - // console.print_debug(p) - // p_compare := Pointer{ - // collection: '' - // name: 'page' - // cat: .page - // extension: 'md' - // error: '' - // state: .unknown - // } - // assert p == p_compare -} - -// fn test_pointer2() { -// p := pointer_new('collectionAAA:Page__.md') or { panic(err) } -// console.print_debug(p) -// p_compare := Pointer{ -// name: 'page' -// cat: .page -// extension: 'md' -// collection: 'collectionaaa' -// error: '' -// state: .unknown -// } -// assert p == p_compare -// } - -// fn test_pointer3() { -// p := pointer_new('MY_Book:collection_AAA:Page__.md') or { panic(err) } -// console.print_debug(p) -// p_compare := Pointer{ -// name: 'page' -// cat: .page -// extension: 'md' -// collection: 'collection_aaa' -// book: 'my_book' -// error: '' -// state: .unknown -// } -// assert p == p_compare -// } - -// fn test_pointer4() { -// p := pointer_new('MY_Book:collection_AAA:aImage__.jpg') or { panic(err) } -// console.print_debug(p) -// p_compare := Pointer{ -// name: 'aimage' -// cat: .image -// extension: 'jpg' -// collection: 'collection_aaa' -// book: 'my_book' -// error: '' -// state: .unknown -// } -// assert p == p_compare -// } - -// fn test_pointer5() { -// p := pointer_new('MY_Book::aImage__.jpg') or { panic(err) } -// console.print_debug(p) -// p_compare := Pointer{ -// name: 'aimage' -// cat: .image -// extension: 'jpg' -// collection: '' -// book: 'my_book' -// error: '' -// state: .unknown -// } -// assert p == p_compare -// } - -// fn test_pointer6() { -// p := pointer_new('MY_Book::aImage__.jpg') or { panic(err) } -// assert p.str() == 'my_book::aimage.jpg' - -// p2 := pointer_new('ddd:aImage__.jpg') or { panic(err) } -// assert p2.str() == 'ddd:aimage.jpg' - -// p3 := pointer_new('aImage__.jpg') or { panic(err) } -// assert p3.str() == 'aimage.jpg' - -// i := 40 -// p4 := pointer_new('collectionAAA:Page__${i}.md') or { panic(err) } -// assert p4.str() == 'collectionaaa:page_40.md' -// } - -// fn test_pointer7() { -// r := texttools.name_fix_keepext('page_40.md') -// assert r == 'page_40.md' -// } diff --git a/libarchive/doctree/process_defs.v b/libarchive/doctree/process_defs.v deleted file mode 100644 index ef7d4b02..00000000 --- a/libarchive/doctree/process_defs.v +++ /dev/null @@ -1,83 +0,0 @@ -module doctree - -import incubaid.herolib.data.doctree.collection { CollectionError } -import incubaid.herolib.data.doctree.collection.data -import incubaid.herolib.ui.console - -// process definitions (!!wiki.def actions, elements.Def elements) -// this must be done before processing includes. -pub fn (mut tree Tree) process_defs() ! { - console.print_green('Processing tree defs') - - for _, mut col in tree.collections { - for _, mut page in col.pages { - mut p := page - mut c := col - tree.process_page_def_actions(mut p, mut c)! - } - } - - for _, mut col in tree.collections { - for _, mut page in mut col.pages { - mut p := page - errors := tree.replace_page_defs_with_links(mut p)! - // report accrued errors when replacing defs with links - for err in errors { - col.error(err)! - } - } - } -} - -fn (mut tree Tree) process_page_def_actions(mut p data.Page, mut c collection.Collection) ! { - def_actions := p.get_def_actions()! - if def_actions.len > 1 { - c.error( - path: p.path - msg: 'a page can have at most one def action' - cat: .def - )! - } - - if def_actions.len == 0 { - return - } - - aliases := p.process_def_action(def_actions[0].id)! - for alias in aliases { - if alias in tree.defs { - c.error( - path: p.path - msg: 'alias ${alias} is already used' - cat: .def - )! - continue - } - - tree.defs[alias] = p - } -} - -fn (mut tree Tree) replace_page_defs_with_links(mut p data.Page) ![]CollectionError { - defs := p.get_def_names()! - - mut def_data := map[string][]string{} - mut errors := []CollectionError{} - for def in defs { - if referenced_page := tree.defs[def] { - def_data[def] = [referenced_page.key(), referenced_page.alias] - } else { - // accrue errors that occur - errors << CollectionError{ - path: p.path - msg: 'def ${def} is not defined' - cat: .def - } - continue - } - } - - p.set_def_links(def_data)! - // return accrued collection errors for collection to handle - return errors -} diff --git a/libarchive/doctree/process_defs_test.v b/libarchive/doctree/process_defs_test.v deleted file mode 100644 index 58cfdc5b..00000000 --- a/libarchive/doctree/process_defs_test.v +++ /dev/null @@ -1,26 +0,0 @@ -module doctree - -import os -import incubaid.herolib.core.pathlib -import incubaid.herolib.data.doctree.collection.data - -const test_dir = '${os.dir(@FILE)}/testdata/process_defs_test' - -fn test_process_defs() { - /* - 1- use files with def actions and elements from testdata - 2- create tree - 3- invoke process defs - 4- check pages markdown - */ - mut tree := new(name: 'mynewtree')! - tree.add_collection(path: '${test_dir}/col1', name: 'col1')! - tree.add_collection(path: '${test_dir}/col2', name: 'col2')! - tree.process_defs()! - - mut page1 := tree.page_get('col1:page1.md')! - assert page1.get_markdown()! == '' - - mut page2 := tree.page_get('col2:page2.md')! - assert page2.get_markdown()! == '[about us](col1:page1.md)\n[about us](col1:page1.md)\n[about us](col1:page1.md)' -} diff --git a/libarchive/doctree/process_includes.v b/libarchive/doctree/process_includes.v deleted file mode 100644 index 2ebf127a..00000000 --- a/libarchive/doctree/process_includes.v +++ /dev/null @@ -1,154 +0,0 @@ -module doctree - -// import incubaid.herolib.data.doctree.collection.data -import incubaid.herolib.data.doctree.pointer -import incubaid.herolib.data.doctree.collection { CollectionError } -import incubaid.herolib.data.doctree.collection.data -import incubaid.herolib.core.playbook -import incubaid.herolib.ui.console - -pub fn (mut tree Tree) process_includes() ! { - console.print_green('Processing page includes') - graph := tree.generate_pages_graph()! - - mut indegree := map[string]int{} - for _, c in tree.collections { - for _, p in c.pages { - indegree[p.key()] = 0 - } - } - - for _, children in graph { - for child in children.keys() { - indegree[child] += 1 - } - } - - mut queue := []string{} - for key, degree in indegree { - if degree == 0 { - queue << key - } - } - - for queue.len > 0 { - front := queue[0] - queue = queue[1..].clone() - - mut page := tree.page_get(front)! - mut col := tree.get_collection(page.collection_name)! - - // process page - for element in page.get_include_actions()! { - page_pointer := get_include_page_pointer(col.name, element.action) or { continue } - - mut include_page := tree.get_page_with_pointer(page_pointer) or { continue } - - page.set_element_content_no_reparse(element.id, include_page.get_markdown()!)! - page.set_action_element_to_processed(element.id)! - } - - // update indegree - for child in graph[page.key()].keys() { - indegree[child] -= 1 - if indegree[child] == 0 { - queue << child - } - } - } - - for key, degree in indegree { - if degree == 0 { - continue - } - - mut page := tree.page_get(key)! - mut col := tree.get_collection(page.collection_name)! - col.error( - path: page.path - msg: 'page ${key} is in an include cycle' - cat: .circular_import - )! - } -} - -fn get_include_page_pointer(collection_name string, a playbook.Action) !pointer.Pointer { - mut page_pointer_str := a.params.get('page')! - - // handle includes - mut page_pointer := pointer.pointer_new(collection: collection_name, text: page_pointer_str)! - if page_pointer.collection == '' { - page_pointer.collection = collection_name - } - - return page_pointer -} - -fn (mut tree Tree) generate_pages_graph() !map[string]map[string]bool { - mut graph := map[string]map[string]bool{} - mut ths := []thread !map[string]map[string]bool{} - for _, mut col in tree.collections { - ths << spawn fn (mut tree Tree, col &collection.Collection) !map[string]map[string]bool { - return tree.collection_page_graph(col)! - }(mut tree, col) - } - for th in ths { - col_graph := th.wait()! - for k, v in col_graph { - graph[k] = v.clone() - } - } - return graph -} - -fn (mut tree Tree) collection_page_graph(col &collection.Collection) !map[string]map[string]bool { - mut graph := map[string]map[string]bool{} - _ := []thread !GraphResponse{} - for _, page in col.pages { - resp := tree.generate_page_graph(page, col.name)! - for k, v in resp.graph { - graph[k] = v.clone() - } - } - - return graph -} - -pub struct GraphResponse { -pub: - graph map[string]map[string]bool - errors []CollectionError -} - -fn (tree Tree) generate_page_graph(current_page &data.Page, col_name string) !GraphResponse { - mut graph := map[string]map[string]bool{} - mut errors := []CollectionError{} - - include_action_elements := current_page.get_include_actions()! - for element in include_action_elements { - page_pointer := get_include_page_pointer(col_name, element.action) or { - errors << CollectionError{ - path: current_page.path - msg: 'failed to get page pointer for include ${element.action.heroscript()}: ${err}' - cat: .include - } - continue - } - - include_page := tree.get_page_with_pointer(page_pointer) or { - // TODO - // col.error( - // path: current_page.path - // msg: 'failed to get page for include ${element.action.heroscript()}: ${err.msg()}' - // cat: .include - // )! - continue - } - - graph[include_page.key()][current_page.key()] = true - } - return GraphResponse{ - graph: graph - errors: errors - } -} diff --git a/libarchive/doctree/process_includes_test.v b/libarchive/doctree/process_includes_test.v deleted file mode 100644 index 441e8735..00000000 --- a/libarchive/doctree/process_includes_test.v +++ /dev/null @@ -1,56 +0,0 @@ -module doctree - -import os -import incubaid.herolib.core.pathlib - -const test_dir = '${os.dir(@FILE)}/testdata/process_includes_test' - -fn test_process_includes() { - /* - 1- use 3 pages in testdata: - - page1 includes page2 - - page2 includes page3 - 2- create tree - 3- invoke process_includes - 4- check pages markdown - */ - mut tree := new(name: 'mynewtree')! - tree.add_collection(path: '${test_dir}/col1', name: 'col1')! - tree.add_collection(path: '${test_dir}/col2', name: 'col2')! - tree.process_includes()! - - mut page1 := tree.page_get('col1:page1.md')! - mut page2 := tree.page_get('col2:page2.md')! - mut page3 := tree.page_get('col2:page3.md')! - - assert page1.get_markdown()! == 'page3 content' - assert page2.get_markdown()! == 'page3 content' - assert page3.get_markdown()! == 'page3 content' -} - -fn test_generate_pages_graph() { - /* - 1- use 3 pages in testdata: - - page1 includes page2 - - page2 includes page3 - 2- create tree - 3- invoke generate_pages_graph - 4- check graph - */ - mut tree := new(name: 'mynewtree')! - tree.add_collection(path: '${test_dir}/col1', name: 'col1')! - tree.add_collection(path: '${test_dir}/col2', name: 'col2')! - mut page1 := tree.page_get('col1:page1.md')! - mut page2 := tree.page_get('col2:page2.md')! - mut page3 := tree.page_get('col2:page3.md')! - - graph := tree.generate_pages_graph()! - assert graph == { - '${page3.key()}': { - '${page2.key()}': true - } - '${page2.key()}': { - '${page1.key()}': true - } - } -} diff --git a/libarchive/doctree/process_macros.v b/libarchive/doctree/process_macros.v deleted file mode 100644 index 3dac6b76..00000000 --- a/libarchive/doctree/process_macros.v +++ /dev/null @@ -1,54 +0,0 @@ -module doctree - -import incubaid.herolib.data.doctree.collection { Collection } -import incubaid.herolib.data.markdown.elements -import incubaid.herolib.ui.console -import incubaid.herolib.core.playbook -import incubaid.herolib.core.playmacros - -@[params] -pub struct MacroGetArgs { -pub mut: - actor string - name string -} - -// adds all action elements to a playbook, calls playmacros.play on the plbook, -// which processes the macros, then reprocesses every page with the actions' new content -pub fn (mut tree Tree) process_actions_and_macros() ! { - console.print_green('Processing actions and macros') - - // first process the generic actions, which can be executed as is - mut plbook := playbook.new()! - for element_action in tree.get_actions()! { - plbook.actions << &element_action.action - } - - playmacros.play_actions(mut plbook)! - - // now get specific actions which need to return content - mut ths := []thread !{} - for _, mut col in tree.collections { - ths << spawn fn (mut col Collection) ! { - for _, mut page in col.pages { - page.process_macros()! // calls play_macro in playmacros... - } - }(mut col) - } - - for th in ths { - th.wait()! - } -} - -fn (mut tree Tree) get_actions(args_ MacroGetArgs) ![]&elements.Action { - // console.print_green('get actions for tree: name:${tree.name}') - mut res := []&elements.Action{} - for _, mut collection in tree.collections { - // console.print_green("export collection: name:${name}") - for _, mut page in collection.pages { - res << page.get_all_actions()! - } - } - return res -} diff --git a/libarchive/doctree/scan.v b/libarchive/doctree/scan.v deleted file mode 100644 index eb839fcb..00000000 --- a/libarchive/doctree/scan.v +++ /dev/null @@ -1,236 +0,0 @@ -module doctree - -import incubaid.herolib.core.pathlib { Path } -import incubaid.herolib.data.paramsparser -import incubaid.herolib.data.doctree.collection { Collection } -import incubaid.herolib.develop.gittools -import os -import incubaid.herolib.core.texttools -import incubaid.herolib.ui.console - -@[params] -pub struct TreeScannerArgs { -pub mut: - path string - heal bool = true // healing means we fix images - git_url string - git_reset bool - git_root string - git_pull bool - load bool = true // means we scan automatically the added collection -} - -// walk over directory find dirs with .book or .collection inside and add to the tree . -// a path will not be added unless .collection is in the path of a collection dir or .book in a book -// ``` -// path string -// heal bool // healing means we fix images, if selected will automatically load, remove stale links -// git_url string -// git_reset bool -// git_root string -// git_pull bool -// ``` -pub fn (mut tree Tree) scan(args TreeScannerArgs) ! { - mut path := gittools.path( - path: args.path - git_url: args.git_url - git_reset: args.git_reset - git_root: args.git_root - git_pull: args.git_pull - )! - - console.print_item('doctree.scan: ${path.path}') - - if !path.is_dir() { - return error('path is not a directory') - } - if path.file_exists('.site') { - move_site_to_collection(mut path)! - } - - if is_collection_dir(path) { - collection_name := get_collection_name(mut path)! - - tree.add_collection( - path: path.path - name: collection_name - heal: args.heal - load: true - fail_on_error: tree.fail_on_error - )! - - return - } - - mut entries := path.list(recursive: false) or { - return error('cannot list: ${path.path} \n${error}') - } - - for mut entry in entries.paths { - if !entry.is_dir() || is_ignored_dir(entry)! { - continue - } - - tree.scan(path: entry.path, heal: args.heal, load: args.load) or { - return error('failed to scan ${entry.path} :${err}') - } - } -} - -pub fn (mut tree Tree) scan_concurrent(args_ TreeScannerArgs) ! { - mut args := args_ - if args.git_url.len > 0 { - mut gs := gittools.new(coderoot: args.git_root)! - mut repo := gs.get_repo( - url: args.git_url - pull: args.git_pull - reset: args.git_reset - )! - args.path = repo.get_path_of_url(args.git_url)! - } - - if args.path.len == 0 { - return error('Path needs to be provided.') - } - - path := pathlib.get_dir(path: args.path)! - mut collection_paths := scan_helper(path)! - mut threads := []thread !Collection{} - for mut col_path in collection_paths { - mut col_name := get_collection_name(mut col_path)! - col_name = texttools.name_fix(col_name) - - if col_name in tree.collections { - if tree.fail_on_error { - return error('Collection with name ${col_name} already exits') - } - // TODO: handle error - continue - } - - threads << spawn fn (args CollectionNewArgs) !Collection { - mut args_ := collection.CollectionNewArgs{ - name: args.name - path: args.path - heal: args.heal - load: args.load - fail_on_error: args.fail_on_error - } - return collection.new(args_)! - }( - name: col_name - path: col_path.path - heal: args.heal - fail_on_error: tree.fail_on_error - ) - } - - for _, t in threads { - new_collection := t.wait() or { return error('Error executing thread: ${err}') } - tree.collections[new_collection.name] = &new_collection - } -} - -// internal function that recursively returns -// the paths of collections in a given path -fn scan_helper(path_ Path) ![]Path { - mut path := path_ - if !path.is_dir() { - return error('path is not a directory') - } - - if path.file_exists('.site') { - move_site_to_collection(mut path)! - } - - if is_collection_dir(path) { - return [path] - } - - mut entries := path.list(recursive: false) or { - return error('cannot list: ${path.path} \n${error}') - } - - mut paths := []Path{} - for mut entry in entries.paths { - if !entry.is_dir() || is_ignored_dir(entry)! { - continue - } - - paths << scan_helper(entry) or { return error('failed to scan ${entry.path} :${err}') } - } - return paths -} - -@[params] -pub struct CollectionNewArgs { -mut: - name string @[required] - path string @[required] - heal bool = true // healing means we fix images, if selected will automatically load, remove stale links - load bool = true - fail_on_error bool -} - -// get a new collection -pub fn (mut tree Tree) add_collection(args_ CollectionNewArgs) ! { - mut args := args_ - args.name = texttools.name_fix(args.name) - - if args.name in tree.collections { - if args.fail_on_error { - return error('Collection with name ${args.name} already exits') - } - return - } - - mut pp := pathlib.get_dir(path: args.path)! // will raise error if path doesn't exist - mut new_collection := collection.new( - name: args.name - path: pp.path - heal: args.heal - fail_on_error: args.fail_on_error - )! - - tree.collections[new_collection.name] = &new_collection -} - -// returns true if directory should be ignored while scanning -fn is_ignored_dir(path_ Path) !bool { - mut path := path_ - if !path.is_dir() { - return error('path is not a directory') - } - name := path.name() - return name.starts_with('.') || name.starts_with('_') || name == 'img' -} - -// gets collection name from .collection file -// if no name param, uses the directory name -fn get_collection_name(mut path Path) !string { - mut collection_name := path.name() - mut filepath := path.file_get('.collection')! - - // now we found a collection we need to add - content := filepath.read()! - if content.trim_space() != '' { - // means there are params in there - mut params_ := paramsparser.parse(content)! - if params_.exists('name') { - collection_name = params_.get('name')! - } - } - - return collection_name -} - -fn is_collection_dir(path Path) bool { - return path.file_exists('.collection') -} - -// moves .site file to .collection file -fn move_site_to_collection(mut path Path) ! { - collectionfilepath1 := path.extend_file('.site')! - collectionfilepath2 := path.extend_file('.collection')! - os.mv(collectionfilepath1.path, collectionfilepath2.path)! -} diff --git a/libarchive/doctree/testdata/.gitignore b/libarchive/doctree/testdata/.gitignore deleted file mode 100644 index 6eaa8eff..00000000 --- a/libarchive/doctree/testdata/.gitignore +++ /dev/null @@ -1 +0,0 @@ -export_test/export \ No newline at end of file diff --git a/libarchive/doctree/testdata/actions/.collection b/libarchive/doctree/testdata/actions/.collection deleted file mode 100644 index d86fceff..00000000 --- a/libarchive/doctree/testdata/actions/.collection +++ /dev/null @@ -1 +0,0 @@ -actions \ No newline at end of file diff --git a/libarchive/doctree/testdata/actions/actions1.md b/libarchive/doctree/testdata/actions/actions1.md deleted file mode 100644 index fdbf8df2..00000000 --- a/libarchive/doctree/testdata/actions/actions1.md +++ /dev/null @@ -1,7 +0,0 @@ -# actions 2 - -```js -!!payment3.add account:something description:'TF Wallet for TFT' - name:'TF Wallet' //comment for name - blockchain:stellar //holochain maybe? -``` diff --git a/libarchive/doctree/testdata/actions/functionality/actions2.md b/libarchive/doctree/testdata/actions/functionality/actions2.md deleted file mode 100644 index 9f254988..00000000 --- a/libarchive/doctree/testdata/actions/functionality/actions2.md +++ /dev/null @@ -1,15 +0,0 @@ -# web3gw_proxy server functionality - -- [stellar](./stellar.md) - - -```js -!!payment.add account:something description:'TF Wallet for TFT' person:fatayera preferred:false - name:'TF Wallet' //comment for name - blockchain:stellar //holochain maybe? -``` - -!!payment.add2 - name:'TF Wallet' //comment for name - blockchain:stellar - diff --git a/libarchive/doctree/testdata/export_test/export_expected/col1/.collection b/libarchive/doctree/testdata/export_test/export_expected/col1/.collection deleted file mode 100644 index 2aea45f2..00000000 --- a/libarchive/doctree/testdata/export_test/export_expected/col1/.collection +++ /dev/null @@ -1 +0,0 @@ -name:col1 src:'/Users/timurgordon/code/github/incubaid/herolib/herolib/data/doctree/testdata/export_test/mytree/dir1' \ No newline at end of file diff --git a/libarchive/doctree/testdata/export_test/export_expected/col1/.linkedpages b/libarchive/doctree/testdata/export_test/export_expected/col1/.linkedpages deleted file mode 100644 index a0814882..00000000 --- a/libarchive/doctree/testdata/export_test/export_expected/col1/.linkedpages +++ /dev/null @@ -1 +0,0 @@ -col2:file3.md \ No newline at end of file diff --git a/libarchive/doctree/testdata/export_test/export_expected/col1/errors.md b/libarchive/doctree/testdata/export_test/export_expected/col1/errors.md deleted file mode 100644 index a17985dc..00000000 --- a/libarchive/doctree/testdata/export_test/export_expected/col1/errors.md +++ /dev/null @@ -1,9 +0,0 @@ -# Errors - - -## page_not_found - -path: /Users/timurgordon/code/github/incubaid/herolib/herolib/data/doctree/testdata/export_test/mytree/dir1/dir2/file1.md - -msg: page col3:file5.md not found - diff --git a/libarchive/doctree/testdata/export_test/export_expected/col1/file1.md b/libarchive/doctree/testdata/export_test/export_expected/col1/file1.md deleted file mode 100644 index 2d8d0af0..00000000 --- a/libarchive/doctree/testdata/export_test/export_expected/col1/file1.md +++ /dev/null @@ -1 +0,0 @@ -[not existent page](col3:file5.md) \ No newline at end of file diff --git a/libarchive/doctree/testdata/export_test/export_expected/col1/file2.md b/libarchive/doctree/testdata/export_test/export_expected/col1/file2.md deleted file mode 100644 index 71f48c9b..00000000 --- a/libarchive/doctree/testdata/export_test/export_expected/col1/file2.md +++ /dev/null @@ -1 +0,0 @@ -[some page](../col2/file3.md) \ No newline at end of file diff --git a/libarchive/doctree/testdata/export_test/export_expected/col1/img/image.png b/libarchive/doctree/testdata/export_test/export_expected/col1/img/image.png deleted file mode 100644 index e69de29b..00000000 diff --git a/libarchive/doctree/testdata/export_test/export_expected/col2/.collection b/libarchive/doctree/testdata/export_test/export_expected/col2/.collection deleted file mode 100644 index 4b23a808..00000000 --- a/libarchive/doctree/testdata/export_test/export_expected/col2/.collection +++ /dev/null @@ -1 +0,0 @@ -name:col2 src:'/Users/timurgordon/code/github/incubaid/herolib/herolib/data/doctree/testdata/export_test/mytree/dir3' \ No newline at end of file diff --git a/libarchive/doctree/testdata/export_test/export_expected/col2/.linkedpages b/libarchive/doctree/testdata/export_test/export_expected/col2/.linkedpages deleted file mode 100644 index e69de29b..00000000 diff --git a/libarchive/doctree/testdata/export_test/export_expected/col2/file3.md b/libarchive/doctree/testdata/export_test/export_expected/col2/file3.md deleted file mode 100644 index e69de29b..00000000 diff --git a/libarchive/doctree/testdata/export_test/mytree/dir1/.collection b/libarchive/doctree/testdata/export_test/mytree/dir1/.collection deleted file mode 100644 index bf5be63f..00000000 --- a/libarchive/doctree/testdata/export_test/mytree/dir1/.collection +++ /dev/null @@ -1 +0,0 @@ -name:col1 \ No newline at end of file diff --git a/libarchive/doctree/testdata/export_test/mytree/dir1/dir2/file1.md b/libarchive/doctree/testdata/export_test/mytree/dir1/dir2/file1.md deleted file mode 100644 index 2d8d0af0..00000000 --- a/libarchive/doctree/testdata/export_test/mytree/dir1/dir2/file1.md +++ /dev/null @@ -1 +0,0 @@ -[not existent page](col3:file5.md) \ No newline at end of file diff --git a/libarchive/doctree/testdata/export_test/mytree/dir1/file2.md b/libarchive/doctree/testdata/export_test/mytree/dir1/file2.md deleted file mode 100644 index 09f30c32..00000000 --- a/libarchive/doctree/testdata/export_test/mytree/dir1/file2.md +++ /dev/null @@ -1 +0,0 @@ -[some page](col2:file3.md) \ No newline at end of file diff --git a/libarchive/doctree/testdata/export_test/mytree/dir1/image.png b/libarchive/doctree/testdata/export_test/mytree/dir1/image.png deleted file mode 100644 index e69de29b..00000000 diff --git a/libarchive/doctree/testdata/export_test/mytree/dir3/.collection b/libarchive/doctree/testdata/export_test/mytree/dir3/.collection deleted file mode 100644 index 13847f7e..00000000 --- a/libarchive/doctree/testdata/export_test/mytree/dir3/.collection +++ /dev/null @@ -1 +0,0 @@ -name:col2 \ No newline at end of file diff --git a/libarchive/doctree/testdata/export_test/mytree/dir3/file3.md b/libarchive/doctree/testdata/export_test/mytree/dir3/file3.md deleted file mode 100644 index e69de29b..00000000 diff --git a/libarchive/doctree/testdata/process_defs_test/col1/page1.md b/libarchive/doctree/testdata/process_defs_test/col1/page1.md deleted file mode 100644 index 23a012ad..00000000 --- a/libarchive/doctree/testdata/process_defs_test/col1/page1.md +++ /dev/null @@ -1 +0,0 @@ -!!wiki.def alias:'tf-dev,cloud-dev,threefold-dev' name:'about us' \ No newline at end of file diff --git a/libarchive/doctree/testdata/process_defs_test/col2/page2.md b/libarchive/doctree/testdata/process_defs_test/col2/page2.md deleted file mode 100644 index 85e06ea4..00000000 --- a/libarchive/doctree/testdata/process_defs_test/col2/page2.md +++ /dev/null @@ -1,3 +0,0 @@ -*TFDEV -*CLOUDDEV -*THREEFOLDDEV \ No newline at end of file diff --git a/libarchive/doctree/testdata/process_includes_test/col1/page1.md b/libarchive/doctree/testdata/process_includes_test/col1/page1.md deleted file mode 100644 index 1f3fb4b1..00000000 --- a/libarchive/doctree/testdata/process_includes_test/col1/page1.md +++ /dev/null @@ -1 +0,0 @@ -!!wiki.include page:'col2:page2.md' \ No newline at end of file diff --git a/libarchive/doctree/testdata/process_includes_test/col2/page2.md b/libarchive/doctree/testdata/process_includes_test/col2/page2.md deleted file mode 100644 index 81e7d4c3..00000000 --- a/libarchive/doctree/testdata/process_includes_test/col2/page2.md +++ /dev/null @@ -1 +0,0 @@ -!!wiki.include page:'col2:page3.md' \ No newline at end of file diff --git a/libarchive/doctree/testdata/process_includes_test/col2/page3.md b/libarchive/doctree/testdata/process_includes_test/col2/page3.md deleted file mode 100644 index 1c9c075b..00000000 --- a/libarchive/doctree/testdata/process_includes_test/col2/page3.md +++ /dev/null @@ -1 +0,0 @@ -page3 content \ No newline at end of file diff --git a/libarchive/doctree/testdata/rpc/.collection b/libarchive/doctree/testdata/rpc/.collection deleted file mode 100644 index 0157676e..00000000 --- a/libarchive/doctree/testdata/rpc/.collection +++ /dev/null @@ -1 +0,0 @@ -name:rpc \ No newline at end of file diff --git a/libarchive/doctree/testdata/rpc/eth.md b/libarchive/doctree/testdata/rpc/eth.md deleted file mode 100644 index a49f253b..00000000 --- a/libarchive/doctree/testdata/rpc/eth.md +++ /dev/null @@ -1,130 +0,0 @@ -# Eth - -TODO - -## Remote Procedure Calls - -In this section you'll find the json rpc requests and responses of all the remote procedure calls. The fields params can contain text formated as . These represent json objects that are defined further down the document in section [Models](#models). - -### Load - -****Request**** - -``` -{ - "jsonrpc": "2.0", - "method": "eth.Load", - "params": { - "url": string, - "secret": string - }, - "id": "" -} -``` - -**Response** - -``` -{ - "jsonrpc": "2.0", - "result": "", - "id": "" -} -``` - -### Balance - -****Request**** - -``` -{ - "jsonrpc": "2.0", - "method": "eth.Balance", - "params": "
", - "id": "" -} -``` - -**Response** - -``` -{ - "jsonrpc": "2.0", - "result": i64, - "id": "" -} -``` - -### Height - -****Request**** - -``` -{ - "jsonrpc": "2.0", - "method": "eth.Height", - "params": "", - "id": "" -} -``` - -**Response** - -``` -{ - "jsonrpc": "2.0", - "result": u64, - "id": "" -} -``` - -### Transfer - -Transaction id is returned - -****Request**** - -``` -{ - "jsonrpc": "2.0", - "method": "eth.transfer", - "params": { - "destination": string, - "amount": u64 - }, - "id": "" -} -``` - -**Response** - -``` -{ - "jsonrpc": "2.0", - "result": string, - "id": "" -} -``` - -### EthTftSpendingAllowance - -****Request**** - -```json -{ - "jsonrpc": "2.0", - "method": "eth.EthTftSpendingAllowance", - "params": "", - "id": "" -} -``` - -**Response** - -```json -{ - "jsonrpc": "2.0", - "result": string, - "id": "" -} -``` diff --git a/libarchive/doctree/testdata/rpc/rpc.md b/libarchive/doctree/testdata/rpc/rpc.md deleted file mode 100644 index 10aebaa2..00000000 --- a/libarchive/doctree/testdata/rpc/rpc.md +++ /dev/null @@ -1,12 +0,0 @@ -# RPC methods - -You can find OpenRPC descriptions of RPC methods in the playground pages below: -- [All clients](playground/?schemaUrl=../openrpc/openrpc.json) -- [Bitcoin](playground/?schemaUrl=../openrpc/btc/openrpc.json) -- [Ethereum](playground/?schemaUrl=../openrpc/eth/openrpc.json) -- [Explorer](playground/?schemaUrl=../openrpc/explorer/openrpc.json) -- [IPFS](playground/?schemaUrl=../openrpc/ipfs/openrpc.json) -- [Nostr](playground/?schemaUrl=../openrpc/nostr/openrpc.json) -- [Stellar](playground/?schemaUrl=../openrpc/stellar/openrpc.json) -- [TFChain](playground/?schemaUrl=../openrpc/tfchain/openrpc.json) -- [TFGrid](playground/?schemaUrl=../openrpc/tfgrid/openrpc.json) \ No newline at end of file diff --git a/libarchive/doctree/testdata/rpc/stellar.md b/libarchive/doctree/testdata/rpc/stellar.md deleted file mode 100644 index 3edb9af4..00000000 --- a/libarchive/doctree/testdata/rpc/stellar.md +++ /dev/null @@ -1,342 +0,0 @@ - -# Stellar - -## Creating an account - -Json RPC 2.0 request: - -- network: the network you want to create the account on (public or testnet) - - -```json -{ - "jsonrpc":"2.0", - "method":"stellar.CreateAccount", - "params":[ - "public" - ], - "id":"a_unique_id_here" -} -``` - -Json RPC 2.0 response: - -- seed: the seed of the account that was generated - -```json -{ - "jsonrpc":"2.0", - "result":"seed_will_be_here", - "id":"id_send_in_request" -} -``` - -## Loading your key - -Json RPC 2.0 request: - -- network: the network you want to connect to (public or testnet) -- secret: the secret of your stellar account - -```json -{ - "jsonrpc":"2.0", - "method":"stellar.Load", - "params":[{ - "network":"public", - "secret":"SA33FBB67CPIMHWTZYVR489Q6UKHFUPLKTLPG9BKAVG89I2J3SZNMW21" - }], - "id":"a_unique_id_here" -} -``` - -Json RPC 2.0 response will be empty: - -```json -{ - "jsonrpc":"2.0", - "id":"id_send_in_request" -} -``` - -## Asking your public address - -Json RPC 2.0 request (no parameters): - -```json -{ - "jsonrpc":"2.0", - "method":"stellar.Address", - "params":[], - "id":"a_unique_id_here" -} -``` - -Json RPC 2.0 response: - -- address: the public address of the loaded account - -```json -{ - "jsonrpc":"2.0", - "result":"public_address_will_be_here", - "id":"id_send_in_request" -} -``` - -## Transfer tokens from one account to another - -Json RPC 2.0 request: - -- amount: the amount of tft to transfer (string) -- destination: the public address that should receive the tokens -- memo: the memo to add to the transaction - -```json -{ - "jsonrpc":"2.0", - "method":"stellar.Transfer", - "params":[{ - "amount": "1520.0", - "destination": "some_public_stellar_address", - "memo": "your_memo_comes_here" - }], - "id":"a_unique_id_here" -} -``` - -Json RPC 2.0 response: - -- hash: the hash of the transaction that was executed - -```json -{ - "jsonrpc":"2.0", - "result":"hash_will_be_here", - "id":"id_send_in_request" -} -``` - -## Swap tokens from one asset to the other - -Json RPC 2.0 request: - -- amount: the amount of tokens to swap (string) -- source_asset: the source asset to swap (should be tft or xlm) -- destination_asset: the asset to swap to (should be tft or xlm) - -```json -{ - "jsonrpc":"2.0", - "method":"stellar.Swap", - "params":[{ - "amount": "5.0", - "source_asset": "tft", - "destination_asset": "xlm" - }], - "id":"a_unique_id_here" -} -``` - -Json RPC 2.0 response: - -- hash: the hash of the transaction that was executed - -```json -{ - "jsonrpc":"2.0", - "result":"hash_will_be_here", - "id":"id_send_in_request" -} -``` - -## Get the balance of an account - -Json RPC 2.0 request: - -- address: the public address of an account to get the balance from (leave empty for your own account) - -```json -{ - "jsonrpc":"2.0", - "method":"stellar.Balance", - "params":[ - "you_can_pass_public_address_here" - ], - "id":"a_unique_id_here" -} -``` - -Json RPC 2.0 response: - -- balance: the balance of the account (string) - -```json -{ - "jsonrpc":"2.0", - "result":"balance_will_be_here", - "id":"id_send_in_request" -} -``` - -## Bridge stellar tft to ethereum - -Json RPC 2.0 request: - -- amount: the amount of tft to bridge (string) -- destination: the ethereum public address that should receive the tokens - -```json -{ - "jsonrpc":"2.0", - "method":"stellar.BridgeToEth", - "params":[{ - "amount": "298.0", - "destination": "eth_public_address_here" - ], - "id":"a_unique_id_here" -} -``` - -Json RPC 2.0 response: - -- hash: the hash of the transaction that was executed - -```json -{ - "jsonrpc":"2.0", - "result":"hash_will_be_here", - "id":"id_send_in_request" -} -``` - -## Bridge stellar tft to tfchain - -Json RPC 2.0 request: - -- amount: the amount of tft on stellar to bridge to tfchain -- twin_id: the twin id that should receive the tokens - -```json -{ - "jsonrpc":"2.0", - "method":"stellar.BridgeToTfchain", - "params":[{ - "amount": "21.0", - "twin_id": 122 - ], - "id":"a_unique_id_here" -} -``` - -Json RPC 2.0 response: - -- hash: the hash of the transaction that was executed - -```json -{ - "jsonrpc":"2.0", - "result":"hash_will_be_here", - "id":"id_send_in_request" -} -``` - -## Waiting for a transaction on the Ethereum bridge - -Json RPC 2.0 request: - -- memo: the memo to look for in the transactions - -```json -{ - "jsonrpc":"2.0", - "method":"stellar.AwaitTransactionOnEthBridge", - "params":[ - "provide_the_memo_here" - ], - "id":"a_unique_id_here" -} -``` - -Json RPC 2.0 response: empty result - -```json -{ - "jsonrpc":"2.0", - "id":"id_send_in_request" -} -``` - -## Listing transactions - -Json RPC 2.0 request: - -- account: a public stellar address to get the transactions for (leave empty for your own account) -- limit: how many transactions you want to get (default 10) -- include_failed: include the failed transactions in the result (default is false) -- cursor: where to start listing the transactions from (default is the top) -- ascending: whether to sort the transactions in ascending order (default is false, so in descending order) - -```json -{ - "jsonrpc":"2.0", - "method":"stellar.Transactions", - "params":[{ - "account": "some_account_here_or_leave_empty", - "limit": 12, - "include_failed": false, - "cursor": "leave_empty_for_top", - "ascending": false - ], - "id":"a_unique_id_here" -} -``` - -Json RPC 2.0 response: - -- a list of transactions (see [here](https://github.com/stellar/go/blob/01c7aa30745a56d7ffcc75bb8ededd38ba582a58/protocols/horizon/main.go#L484) for the definition of a transaction) - -```json -{ - "jsonrpc":"2.0", - "result":[ - { - "id": "some_id", - // many more attributes - } - ], - "id":"id_send_in_request" -} -``` - -## Showing the data related to an account - -Json RPC 2.0 request: - -- address: the stellar public address to get the account data for (leave empty for your own account) - -```json -{ - "jsonrpc":"2.0", - "method":"stellar.AccountData", - "params":[ - "account_or_leave_empty_for_your_account" - ], - "id":"a_unique_id_here" -} -``` - -Json RPC 2.0 response: - -- account data (see [here](https://github.com/stellar/go/blob/01c7aa30745a56d7ffcc75bb8ededd38ba582a58/protocols/horizon/main.go#L33) for the definition of account data) - -```json -{ - "jsonrpc":"2.0", - "result": { - "id": "some_id", - // many more attributes - }, - "id":"id_send_in_request" -} -``` diff --git a/libarchive/doctree/testdata/rpc/tfchain.md b/libarchive/doctree/testdata/rpc/tfchain.md deleted file mode 100644 index 0ca187ca..00000000 --- a/libarchive/doctree/testdata/rpc/tfchain.md +++ /dev/null @@ -1,251 +0,0 @@ - -# TFChain -TODO: intro - -## Remote Procedure Calls - -### Load - -****Request**** -``` -{ - "jsonrpc": "2.0", - "method": "tfchain.Load", - "params": { - "passphrase": string, - "network": string - }, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": "", - "id": "" -} -``` - -### Transfer - -****Request**** -``` -{ - "jsonrpc": "2.0", - "method": "tfchain.Transfer", - "params": { - "destination": string, - "memo": string, - "amount": u64 - }, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": "", - "id": "" -} -``` - -### Balance - -****Request**** -``` -{ - "jsonrpc": "2.0", - "method": "tfchain.Balance", - "params": "
", - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": i64, - "id": "" -} -``` - -### GetTwin - -****Request**** -``` -{ - "jsonrpc": "2.0", - "method": "tfchain.TwinGet", - "params": , - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### GetNode - -****Request**** -``` -{ - "jsonrpc": "2.0", - "method": "tfchain.NodeGet", - "params": , - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### GetFarm - -****Request**** -``` -{ - "jsonrpc": "2.0", - "method": "tfchain.FarmGet", - "params": , - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -## Models - -### MODEL_TWIN -``` -{ - "id": u32, - "account": string, - "relay": string, - "entities": [MODEL_ENTITYPROOF], - "pk": string -} -``` - -### MODEL_ENTITYPROOF -``` -{ - "entityid": u32, - "signature": string -} -``` - -### MODEL_NODE -``` -{ - "id": u32, - "farmid": u32, - "twinid": u32, - "resources": , - "location": , - "public_config": { - "ip": , - "ip6": , - "domain": string - }, - "created": u64, - "farmingpolicy": u32, - "interfaces": [MODEL_INTERFACE], - "certification": "string", - "secureboot": bool, - "virtualized": bool, - "serial": string, - "connectionprice": u32 -} -``` -### MODEL_RESOURCES -``` -{ - "hru": u64, - "sru": u64, - "cru": u64, - "mru": u64 -} -``` -### MODEL_LOCATION - -``` -{ - "city": string, - "country": string, - "latitude": string, - "longitude": string -} -``` - -### MODEL_IP - -``` -{ - "ip": string, - "gw": string -} -``` -### MODEL_INTERFACE - -``` -{ - "name": string, - "mac": string, - "ips": [string] -} -``` - -### MODEL_FARM - -``` -{ - "id": u32, - "name": string, - "twinid": u32, - "pricingpolicyid": u32, - "certificationtype": string, - "publicips": [MODEL_PUBLICIP], - "dedicated": bool, - "farmingpolicylimit": -} -``` - -### MODEL_PUBLICIP - -``` -{ - "ip": string, - "gateway": string, - "contractid": u64 -} -``` - -### MODEL_FARMINGPOLICYLIMIT -``` -{ - "farmingpolicyid": u32, - "cu": u64, - "su": u64, - "end": u64, - "nodecount": u32, - "nodecertification": bool -} -``` \ No newline at end of file diff --git a/libarchive/doctree/testdata/rpc/tfgrid.md b/libarchive/doctree/testdata/rpc/tfgrid.md deleted file mode 100644 index 6b0e5260..00000000 --- a/libarchive/doctree/testdata/rpc/tfgrid.md +++ /dev/null @@ -1,651 +0,0 @@ - -# TFgrid -TFgrid is one of the clients that web3 proxy opens up. Below you can find the remote procedure calls it can handle. We use the json rpc 2.0 protocol. All possible json rpc request are shown below with the corresponding response that the web3 proxy will send back. - -## Remote Procedure Calls -In this section you'll find the json rpc requests and responses of all the remote procedure calls. The fields params can contain text formated as . These represent json objects that are defined further down the document in section [Models](#models). - -### Login -This rpc is used to login. It requires you to pass your menmonic and the network you want to deploy on. - -****Request**** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.Load", - "params": [ - "", - "" - ], - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": "", - "id": "" -} -``` - -### Gateway Name Deploy -This rpc allows you to deploy a gateway name. It requires you to pass the information required for a gateway name. Upon success it will return you that same information extended with some extra useful data. - -****Request**** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.gateway.name.deploy", - "params": [], - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### GatewayNameDelete -This rpc allows you to delete a deployed gateway name. You should send the name in the params field. The operation succeeded if you receive a valid json rpc 2.0 result. - -****Request**** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.GatewayNameDelete", - "params": [""], - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": "", - "id": "" -} -``` - -### GatewayNameGet -You can always ask for information on a gateway name via the rpc shown below. Just set the name in the params field of the json rpc 2.0 request. The response will contain the requested information. - -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.GatewayNameGet", - "params": "", - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### GatewayFQDNDeploy -If you wish for a fully qualified domain name you should use the rpc shown below. It requires the data shown in [this model](#model_gatewayfqdn) and returns that same data augmented with [some extra fields](#model_gatewayfqdnresult). - -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.GatewayFQDNDeploy", - "params": , - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### GatewayFQDNDelete -You can delete your requested fully qualified domain name with the rpc shown below. Just fill in the name in the json rpc request. - -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.GatewayFQDNDelete", - "params": "", - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": "", - "id": "" -} -``` - -### GatewayFQDNGet -Once created you can always retrieve the [data](#model_gatewayfqdnresult) related to your fully qualified domain name via the rpc method *tfgrid.GatewayFQDNget*. - -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.GatewayFQDNGet", - "params": "", - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### K8sDeploy - - -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.K8sDeploy", - "params": , - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### K8sDelete -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.K8sDelete", - "params": string, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": "", - "id": "" -} -``` - -### K8sGet -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.K8sGet", - "params": string, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### K8sGet -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.K8sAddnode", - "params": { - "name": string, - "node": - }, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - - -### K8sRemoveNode -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.K8sRemovenode", - "params": { - "name": string, - "nodename": string - }, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### MachinesDeploy -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.MachinesDeploy", - "params": , - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### MachinesDelete -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.MachinesDelete", - "params": string, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": "", - "id": "" -} -``` - -### MachinesGet -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.MachinesGet", - "params": string, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### MachineAdd -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.MachinesAdd", - "params": { - "project_name": string, - "machine": - }, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### MachineRemove -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.MachinesRemove", - "params": { - "machine_name": string, - "project_name": string - }, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### DeploymentDeploy -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.DeploymentCreate", - "params": , - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### DeploymentUpdate -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.DeploymentUpdate", - "params": , - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### DeploymentCancel -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.DeploymentCancel", - "params": i64, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": "", - "id": "" -} -``` - -### DeploymentGet -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.DeploymentGet", - "params": i64, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### ZDBDeploy -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.ZdbDeploy", - "params": , - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -### ZDBDelete -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.ZdbDelete", - "params": string, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": "", - "id": "" -} -``` - -### ZDBGet -**Request** -``` -{ - "jsonrpc": "2.0", - "method": "tfgrid.ZdbGet", - "params": string, - "id": "" -} -``` -**Response** -``` -{ - "jsonrpc": "2.0", - "result": , - "id": "" -} -``` - -## Models - -### MODEL_CREDENTIALS -``` -{ - "mnemonics": string, - "network": string -} -``` - -### MODEL_GATEWAYNAME -``` -{ - "nodeid": U32, - "name": string, - "backends": [string], - "tlspassthrough": bool, - "description": string -} -``` - -### MODEL_GATEWAYNAMERESULT -``` -{ - "nodeid": U32, - "name": string, - "backends": [string], - "tlspassthrough": bool, - "description": string, - "fqdn": string, - "namecontractid": u64, - "contractid": u64 -} -``` - -### MODEL_GATEWAYFQDN -``` -{ - "nodeid": U32, - "backends": [string], - "fqdn": string, - "name": string, - "tlspassthrough": bool, - "description": string -} -``` - -### MODEL_GATEWAYFQDNRESULT -``` -{ - "nodeid": U32, - "backends": [string], - "fqdn": string, - "name": string, - "tlspassthrough": bool, - "description": string, - "contractid": u64 -} -``` - -### MODEL_K8SCLUSTER -``` -{ - "name": string, - "master": MODEL_K8SNODE, - "workers": [MODEL_K8SNODE], - "token": string, - "ssh_key": string, -} -``` -### MODEL_K8SCLUSTER_RESULT -``` -{ - "name": string, - "master": MODEL_K8SNODE, - "workers": [MODEL_K8SNODE], - "token": string, - "ssh_key": string, - "node_deployment_id": map[u32]u64 -} -``` -### MODEL_K8SNODE -``` -{ - "name": string, - "nodeid": string, - "public_ip": bool, - "public_ip6": bool, - "planetary": bool, - "flist": string, - "cpu": u32, - "memory": u32, //in MBs - "disk_size": u32 // in GB, monted in /mydisk -} -``` -### MODEL_K8SNODERESULT -``` -{ - "name": string, - "nodeid": string, - "public_ip": bool, - "public_ip6": bool, - "planetary": bool, - "flist": string, - "cpu": u32, - "memory": u32, //in MBs - "disk_size": u32, // in GB, monted in /mydisk - "computed_ip4": string, - "computed_ip6": string, - "wg_ip": string, - "planetary_ip": string -} -``` - -### MODEL_DEPLOYMENT -``` -{ - "version": int, - "twin_id": u32, - "contract_id": u64, - "expiration": i64, - "metadata": string, - "description": string, - "workloads": [MODEL_WORKLOAD], - "signature_requirement": SignatureRequirement -} -``` - -### MODEL_ZDB -``` -{ - "node_id": u32, - "name": string, - "password": string, - "public": bool, - "size": u32, // in GB - "description": string, - "mode": string -} -``` - -### MODEL_ZDBRESULT -``` -{ - "node_id": u32, - "name": string, - "password": string, - "public": bool, - "size": u32, // in GB - "description": string, - "mode": string, - "namespace": string, - "port": u32, - "ips": [string] -} -``` \ No newline at end of file diff --git a/libarchive/doctree/testdata/tree_test/fruits/.collection b/libarchive/doctree/testdata/tree_test/fruits/.collection deleted file mode 100644 index 87eb9c37..00000000 --- a/libarchive/doctree/testdata/tree_test/fruits/.collection +++ /dev/null @@ -1 +0,0 @@ -name:fruits \ No newline at end of file diff --git a/libarchive/doctree/testdata/tree_test/fruits/apple.md b/libarchive/doctree/testdata/tree_test/fruits/apple.md deleted file mode 100644 index b8b94fb0..00000000 --- a/libarchive/doctree/testdata/tree_test/fruits/apple.md +++ /dev/null @@ -1,9 +0,0 @@ -# Apple - -An apple a day keeps the doctor away! - -## Fun Fact - -The apple can be the same color as the following: -- Red as [strawberry](berries/strawberry.md) -- Green as [broccoli](vegetables/tomato.md) diff --git a/libarchive/doctree/testdata/tree_test/fruits/banana.txt b/libarchive/doctree/testdata/tree_test/fruits/banana.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/libarchive/doctree/testdata/tree_test/fruits/berries/img/digital_twin.png b/libarchive/doctree/testdata/tree_test/fruits/berries/img/digital_twin.png deleted file mode 100644 index ced6c2720d253368eb4453a620d20041f1ec2480..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125580 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
`g@2M45<#kKCJNV#ogLQ8tl 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

$hYIE~ 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≥Y#P>Qm+?sWk~3zPbWT8^;{g>HYG%E4!QHW5Bv&Vr2zusGr%M zPYh$q1Sosr9$$m8Qg&cPMFr4rc_be#wG_=u1spNFd@@iccZ0#0HbcJUW`F~gM)g@I z3M#m5xdLy9+m2aNQzP;0^DvJaQ?#~=3reD^=bI(?X{=C{V`kBl zq3WWwBI;`j!GK=xpVl3>TPul)TCm1FJ=NI2Wj5cy?o>%3`J&%h{!WH~eEs^6m`U<= zd9r0eDI{HA2UqUHXWf^!VQ(`xF(-9{H- z2n`U}0(}4g5CBe=%wY%#l-U_3BG1}$cclq=L?mLh>ZMEU407Z>p_M!oM8Gz z2>F&WeG_GOBcXgpc{gvnf>*Zm%suZoXR`mapLNg%dA}d1-|}SH`W!y;j?9VH0f_^TWfYVjgkyk>O8A?VGeRrC4rw3R+98gFo?gEMe)01Q_>L?T48QM^*b z4?rV7{&p{p9L%t~vA9r~&VJ@li+W_IzEI~)Rt@}tKd8i{GXcd5NrGBquL1Xmw^o66 zmX^RBG)NEeX?PGYjO{qs*t-9>?gXuvO--KOS3d1#eac;#*Hog7vPA83-aD(y{yw2y z+r~L3f0sFNKoRz9hoNgse6h`WyMj{0T}Y_qi>z#2!xQZkgxx=OuvY*nS|<3a!)>nx z$`B98tvQX-v;Z&+jg?}Gi({OReyp9^<5(H?TKq>23w8H#wv!lWK8&~B$+9NaTO~E7 zN?A-WT$$w;l}}Gi{Jz2276O$z73bzw*Vg_Bic&yh@$cfpAtBO41fO@DCPd;T7ZX$e z=qLv*Eo3rh5K1qB96Uz|IV3T>Tq?-sj1fM3>UFtaThyPT9LqYGzV#UEiM=*j`0;!< zTKQT0c`Pqj*0BBVazH}m_G#Z>_1Q4u-bmmw#_;v=&y=gJ8KUVb?892DVFd`*8!agG z=h;Pp1q3TDC@ARR;b9gJ#?%c+`K773@;*MIT(tQE_MD`_ZYiv=S_AFkXbENPo2bj~LFe`{rKk&h zC=aft0AA*8Vq*j@6?mtg4jZ0F2M5Ejtd7lItd2K|Q~`yBri#d=A#mF{KRcTf#e-E( z#7}Kp|A|crd(P5D6fKqNjzhtn!n@3RE{e&hji`xXDcQ>nH+MQ@Z>MsAS5Z=Wefwv6 z8YvlFEgHno`AvoH_5S(OnxBtIW_H46a1OTqKRzsNTW&7I$j;n6F+LthPBaV+v4r;b z_V~w&Uk+3MU`7bK|xTbN=Q)CR~Tl) zo2KEhji=xQyt!0#`%{NUMpbP-q@8=Odx;A1)u11T)kjwz{V}i`!^PguSOf>Y#`0M6 z{eAu6Cv3{m;lYnTbQW4Xsp8~9j^6gNmB?y9xyrsZxA!MHe|#Bg#TY~Pa8sOfUoFDH zgw606T)n~_7;`hqM$6P<`Q8y8YtP#kpa_+fmFemFU`nU0O&rb%;|lKFIX?cxvI<;v z!QDpJQIszj3o7uD3v>~$v~q=B~tj2>1Fcbtc1%rsDWeOU9kdzcc{rpFT#w1{vG zcZgHWw-Zg*$n3WWEw(aF`u|%R6&Rz>?`ACJ7XDtJ9W*tsVm5-l?WliF&&;1u9RyM*i4Yja7AGDDlYDh zVM}I3ZcvEFO5F3&^J$B(1Vv!qqin|~ht)Zp(vx_WgXi78$3DNChxE$pgU1Yy0lQ1N z9niGgq96i{vd;ZnbXA~{^9vl->ltN3K%(u?>7i}X8lQ# zA{sUtWzp|C_dNL6A>W)+lYrJ%(Gl?|R|Lcb-u#pII;ZiU!xmM3a5U#<)~O&$AQwE% zb0f38fJn#a_hi;aOIU1r3^4rZmk?m#tP6~x5)ufxQ$K&2GBYA(8z;}`0Y-2+ai@1%f%P%(N6St8p6V2c!5~uiKbjIS zHEu!%@R*obTMWgm9k5TQ={#w4-sY&cRWf@{(|(zv|@}_GKCPO0HW6 zO(3#vLM`$Nn>Rq}tK1aE4^!KxNOur~iCtJ<;`THGKqk^+Xl}9VU;N`Lf`k`1%WRI`fID6GVoSR0)gg zUo~whQv3(#b^G{wURE5G>Zkd;1_#v$$9%* zo2W&*m69O`zqhaen}2zB7WmBGt#$?j%s=_W_44zqui59Wt0B!!^ByFSu=hano#5FWD78zV_dwu>cN9`z<~p_%$r>bpvHIt0&6q^7cX}e6-P&T7njd9 zHBoDZc_1u6(CcDOV8zFK=Op?5n)=3PrTy`~L-65m@`~U6abBOFu>V@_+LHI^XocFu z#7Sx9GB7B<0q*Na4tEWQLDWQ96}5mil>&eIHFKg=N<0e<6AeegAh@8+o*OlM zZq?4i1Imja-!jh zLD&C;p%+#|j=Vn9;!~40{40v1sqn#y(qc2qdw_mkqGdPKjD58R^vYs>4<0}!afVq3 z%oh0gn8byIm~mUXacZjSz&l>d2{IYLKH1yb0|`QIg^7S)C}K|DVG#HHN=GK)vvT#R za2hr8ztd79St!yHSaucq-*x%shLWOB4P|$P)yZA=br% z8^;xL>jgocYCz7ie<@1v^ zR6RO6I%!$i{hgicI%T7EH(Y?=bC()#o2#RngCOZu%CNY#H9!@pd+v|9l>+4`D+|lN z-;~J`Ob)>{Z|?N>aWyEu-Vx0_AFUqBC4iz-*%ay--cP6yVLJy({uI3Q47=#>&QPj;AjdHVJ3AXeqDH~j5Y_1|jxL+v>F(NiLt@wNBw z!F>TBt!t@{S(Cu!?4R!KZ5En+Y`5!q22#g*_l+FSL2)+=r^EFFTX^-Vr3U))KX!kL zjgtE-H|)X8T1yi> zhbNk_*KeiO^nE;K-K30_uIJ!v!$#M!CQ_Br5@Q^R^5PGA!@@v82R9iTBo>~53B z12yBv8RxO`HY8x>6sFggO$=}VEG!_ZML-Dd?v`$BWhO*V9`Yp@`v&&?eA=chN<>1rcSQUpXDkHs`KYBS{Gb$!2N|2r$F~hN-F>A3P|MJ+H8n1 zRzw=b!R#p|h4gTW2yUB|l@%e7ijp#sb?XR)pqX)6M~09bX;U1N-Q{y7AVg#I+hLAcV~^Tn58Iv%*f_kcsE%ag z!yay-PB>1~Mu;t?+F>4@%(bEtBOyuFZ?FRo?)=ow$UA ze3i_B6;;4_IJmi8?fxzTmMvEFlDM#<1fHviZOV91Amv3aMC!|?q4at8l1jPE(0rTc z6P_Md8L_289kt{}V-YpWa_AHSv=a(0oC}P=wiA!m=4Rn>B_Le`Fr*fD2U}HLRn;p( za1~f*WFaEnsd+d`QM-Qbw53iRwx{~cZhJjs<)BjG7+DFH#6iCNLZK3C#9qE~x8#tL zJkvzuMMmF%mh!1ppmu{8fFReFqGDr#P%pGT@E=e^G~)r65)B7lwZ8TBAXT~}>RhcU zH(5GuAO8iH75|HNq0`mADC`wuhnh>~Vw*gy$;QvR)doV9dM#+6NSZ;yB_WP_1DZ%1 zn9is1g-JRSpX840R%yT!B&%`C9(EuyqfH{%n|#8YsE4i zw9$^OIKO4)TQJVH)JKf~Hv1_W_8uQaFPan^TZJ{S#5q^hJHFVzjbzp&*1o zWtccz2!bX{V~&x}_K*La&*k~JG3(_B!KvBB^CL)BlrWBTY6>cS8!$|oY)8TLw;PoxvS%EL5M zjwL}ZRa8w`YDGSMZg?T{&1Ki(f`~^ZP<(7|ZfX=Mw{ z*7qwXwg*Y zLVs#Wl+sF~MKR{rcmT%bS}422oi-4y!hm+#*3|TCQc{?#WCzK=bs$|V^7mXHAdwJ0 zdfMu|`gNz@(OCW0S!?CI&zr;V$MxAvuveC^um7{VoAJ~7?WN^&dr2bjXV=Vq^{NewvM9UO4m$v|Db z@ZT$=2r%od7#%Gs0kW3%r?3!Dr0kvjg9B0bBLg`(RKg+dRK=VO0y)k90vP9f&gfdw zy8!JTc2LB3S@tpbh%!A#(UD>PLAPCpE|GP6JI5sJ9YSU380DbVv`LIbR{NGYoMSPK za)w|vHaLL@eOo}_I!%$NfE37oGQfW^G;t~S`+VR>G&6Vz3?s%(d-C4@Y%0Pn2_;j$dBPS=P zqs|!vvP-ZWG!n4YG5&JLVZqmf5Io!b4Vv}rq2k}Pf`X)xRIgl_cK`V&R7u@}2rpD^ zrPkKcQuI=vb(N7{VA2C)*qxYAn?vGy$3%Dp2GMYg3{G9Q;GN5h551FeUS}M^GJQqO zf8rWk_@56F+1|b}4-jc|k>IL?-!H!;j>=hW^SO1hv#S8HAuo}MWVqQAOMUd{lF-+( z6giUmyYZRna*^=|OGX(&3Bz%j;S$c7@Rl@DZgX_n{T`d^>Mk+tkG&TGR}G}C&~S6( zV`CyNh}UOGXC4%d1hArP^3F>qlOb*HiVd_MrIzfcdJeIk$Jmel8i%+~TX7qX1aA=7 z*g>^}ttBsarXn$B0Nx)A1xgZL0fCK(F#&Ntr6Wgl{ht=c;ZL+*LpacIq&>W}=bUK!XIW*>b6wcIRD{(hM0;O4! zhC;c8RM^sq6f8pVE93zGu$#L{j%6c~Zj;$fL8;+rqjvg}1JrkWch~cBZyLBefL^~- z03vcBgN$*3EGZ-)F_6N}X75jta`uzqV#c?FHEfy0-aDgZ&Fd=c{&rD?gB;0NAgbN= zh5s1G#KM0J zpsJDvOQEizk=6e3g-$kCIxa55D@L-!g_T)ZfG678J^dO7Wa!^ZdUZ z&j){6pcI&!n5+Vr9Sq?B{0f?iY+ZVF1cvW>ZZoPM2w#(7y~B=+p<4R~KfS9kXaYn2 zxVV4$!hrb?{RYN5i1a5dX@9@h)oH1yROaSN=6nf;uAB_F9wfPSGSB}MnEkJSp}oAO z%CoA_yWG;G)!d=AJyuVKOulj-YuJ(-hqe76$!q$*%OK~kwuhrZvXNhT455Uucpty33%_aQR!`tAyQmBfqnR3f(z;^qUEKk9ufx zyC(|PDG5Z<#>@<%E7H$EYShZo60HZm-%&vUJq{Hb7*0!52Q%T?Z1OxWY-tg($pQ01 zv6c(kz_3Yz-Ev|O{+Gb6A4!b9Qebf(N*TD&mEPJ61zo*|rzZe@@&Bs_qzj2S3mPVeG0qt@@@fo6ip2D`arE;W)`xt_A1xo zt2E{=E>=!XZb|?B62S1oE#4CN@JiwB+Z;`l(>CvGj)WxO8~`{FY{VEBdI0I9Dz0C3 zw6nvG_70PD_43jyq^b(^%FE#Z=m?Uxo3k@`L52ZkBO(G+7eTb{pmIA`PH`;+$obEo z*;Q4lEQ!*@b&Yh|ZeJPYe_e!}Dfiu=OLb(h+F638V|0N&qx!Ar{uhr4`pd$1NuC~R zP&b7B3&D8ugxQ@;O+Mj~r(Z}Et8Hi3*PpI&%P9re1lYj-vi}OanVIgBGKE;9WiT)? z5$oO%(J34p9ksh1k~NQDN512&BE5u4@dJy*k#tBh#>FJM*!Y+49UaCzND8(<EE~{un8v``oSX!75pVU3~$X7HY!1NMRAC$WKQUxch$p^_}`Q0cj2po5zc7E}94=h99Qip&~JYs|#PnhWASeF7phWr19* z(4xw=;B8BL`wsVGBN>^nEtQrOEF@ysFnK^RDp1FNImRDFAK3xAM?%?%SSqo}0~Q&6 zMa(rgu~7Bkxm66L1;CNiOt~Q-Dp&2dN@Z;}^pf8&2>=~A3d$!5LRc{}(1PXMghl5W zS#-Ets(<768`+#F#_{^S5u`+S%C|8BrD2 z01rYS;LnG$?srEJ0L~Ji&18QD-owRGdKH-oQr`Q=s7#ySzrnmQJCsI^iD%Eth1vq_ zl2>RfsH3(VFftfb_NDrZUj!L(@}oZmTY^QkCghcicN`)s8xPW)dO1dU>cR$BheyUb z$st9^mr+;Zye6+mff@rayrkb@f6NO6!~SL{7s_Bd;4Sp5e-m;48IYXmt__hRL=OcA zDlK9>n=wu<%`6*~T}yxds6K>3u)Gm5{LeP!!88jPw1IY%31Zfp-nM{$$fsSki)vZ5lbagz3(J-pawA=A$G^LL zVhOvtP3nAYm}yliv~lI_R+!(EKb2yoz0e~lHRZ?tcdo(!&kvBZas{De-;wBY$z^#7 zwzsyjyEUIz@9%N6K(6h=BfHw*`dcN9>a_&_(lK)T zH50Ck#~VmO7@GDXsRI#o-~8-=F)}a0#WnHgPeww*1&BGbY4ONYB@9o~dE>#^xFCeh#oV#E%Pva5) zW$x4?8DWrUGio>lcC%;9dJaJ=_9Y zHPDEWkQ4z!gXzPwihy9?tqETlx)DMk?Hm?>mFM|Jbrw@v)2}>DPdX7*B`2LhjmlIK ze_abMd58!-6vl%8VR3vISQEo(93LMW7bKIN+ktun{4}6@a)nnaFD|YESFf6efx&05 z7(URkea`{sAPvHfA3-}U{pQWY?gglWfuV3PG}sRC&J0@IY&R~tg6P2)0{qqA;58qW z1lEXCzfWE=CP^R^(7Xw&Nh)LPl~Ku&z9?4|+i(TCl=yf88@CT1LKF&sn-dsQb?Jim z7Mm|g2wR~hfgmFBA3I(zfY@1(0^FU{)LEdD86Q^#bAMzhzGDTjo;o^nED%6r81q98 zXNcn??Zw%dWJ6lHU&oR#f}Sz3K(;6aj(q$3i<6Vwe0)6{ryu~1Yqzct`rp+ONl=BBBT6oF1$?J%{dP1W|ZVX@Y)M? z9KMJ6SOLbV#aO1TogMPZ;r_me>mH#fsNUul7lZAV+kMk3D95^{Tf|07Ls>Xq} zCNSy4#|P@&8ufa2)E&-L#W0yJV2=t|4k$+$Mb7&(-vNl_L68j(;Chlw6m>uah5mS5 zH$nD~>;MlB58Q--lcFVqDPbVfOs@minl9H3_>*-0-iHaKVkb~hcr4kx)E!*ol z{xIU=ZCg9#ThT}hT>r8lb=R!KJ-nRi7b9)BEezOF_e@;-qq%om5?|z)3iN^4BsUiz zOTr-8qL(fqF>!ikhC{T41Ymo_1{(EeKQZUX!Wl@nSv;579h4+Pxw*LD8NUJuv>dRt zP&McJ`YaqARL}21_@N-j*vg6}RPyxfY;b4@!Mnbu2EjW_211u>ZFIgRdHDFAp9;`1gaszibMtXWY&X?K*(l___dCa*0L~;s`^jDLL z+eUq%z0b)tLx3CAQ%av63h1-|d2b4z*Tt57EE9c;6fcAjgroq9@733ony;qeGs8r` zP#Ph)^?dt#)>yjI4L?`9m+V-ba$`;r&GcqHn%5g1(fg^S5=PZ=74d$V;Us5sdm9MR zUZ(=n&u@u|1)A=8_{rmwJY%E4QXnb{p*n2roj2<-5yoCYe`o$g?q3JKuX6dopC?EL z%-Dcwa;IgG$_gla067?h(F!kt-1axwwB#>d1iC|W+H-K*-Ytmyv@!YrPd$Wm2eyqK zQZ%SOB5_l5l&3Qs$iQB$!Pi@EI5bo|li537NK`JVgv-4^YUeiMIe`$h*6H3)R10Sw|RC8jzH2fvPW`2~3w7j(%qVAr#@ zA}f-GcxQJQACCXGI=;cXu-P5r^?w1S1X}wI@EIh~;2#>dYDqMEqkp$&wLs&}ym!b- zZ$6|$>*)OX^LcrBBO@a*F)>%JTzTI+p|v0c8d{sv)6?(;xOeYfZf-8rP;mP22m+my zlmr(uT&Elk$J^T*QW6srot&H;9UX^;hP1bc*{EQF#%y#hU%pI?fri2tN+nB6ORYT4 z&d%tD?qkP}g@lB_BaKGW%zy3D=T#zV`vR-p0qr zYin!4mEkHSu02Aa(RT_e8gp~=($Z4YwTuK>M@Q$*ojU~u1<<&J>a4i9n8)LRYC}%g z;QU6Kas;yqSfDW*o#jI~3=IvnQtsZpi^?yQ8Jhc9Sy`GHut5L+cJ8hvkuVIv@b9gl zU_yw>JSijzO9Bb{prX7*MkxfNKZOu}(s99*6nqyX@%+~hPCs|XqKICWA zaC7~6xLNgaYa%-;S%vb8M+P-KqiGF5YnnDOF_BCr1A&0lq!))4B9X{uvr^T4eSKYBT~gBOp~-G60*S2DbzM69;hn(@ zY3Td=ds#0l-&=$BL_h-o0DP8I@A>(8G#VWq9+r$wBoeo`w=c6t&o8T+rn#}PF*P+c zKR;h66yAIh8s0@f0{{TH&}cM@#bQ36molzYDz7h{A-Noj#U!sMCnp1eKqwTty1Me{ z$O_&>Kmz~(xb{I1OI}MiFE1}kH)k>#pU=0uyDQo4TnEo0paB2?zLH@WgM)+dc-%D2 z 0 { - collection_name := collections[0] - - // List pages in the collection - pages := client.list_pages(collection_name)! - println('Pages in collection: ${pages}') - - // Get content of the first page - if pages.len > 0 { - page_name := pages[0] - content := client.get_page_content(collection_name, page_name)! - println('Content of ${page_name}:') - println(content) - } - - // List and display images - images := client.list_images(collection_name)! - println('Images in collection: ${images}') - - // List and display other files - files := client.list_files(collection_name)! - println('Files in collection: ${files}') - } -} -``` - -## Error Handling - -DocTreeClient provides specific error types for different failure scenarios: - -```v -pub enum DocTreeError { - collection_not_found - page_not_found - file_not_found - image_not_found -} -``` - -You can handle these errors using V's error handling mechanisms: - -```v -// Example of error handling -page_content := client.get_page_content('my_collection', 'non_existent_page') or { - if err.msg.contains('page_not_found') { - println('The page does not exist') - return - } - println('An error occurred: ${err}') - return -} -``` - -## How It Works - -DocTreeClient works with Redis as its backend storage: - -1. The `doctree` module processes document collections and stores metadata in Redis -2. Collection paths are stored in the 'doctree:meta' hash -3. Page, file, and image paths within a collection are stored in 'doctree:{collection_name}' hashes -4. DocTreeClient provides methods to access this data and retrieve the actual content from the filesystem diff --git a/libarchive/doctreeclient/client.v b/libarchive/doctreeclient/client.v deleted file mode 100644 index 45d9195b..00000000 --- a/libarchive/doctreeclient/client.v +++ /dev/null @@ -1,347 +0,0 @@ -module doctreeclient - -import incubaid.herolib.core.pathlib -import incubaid.herolib.core.texttools -import os - -// List of recognized image file extensions -const image_extensions = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.bmp', '.tiff', '.ico'] - -// Error types for DocTreeClient -pub enum DocTreeError { - collection_not_found - page_not_found - file_not_found - image_not_found -} - -// get_page_path returns the path for a page in a collection -pub fn (mut c DocTreeClient) get_page_path(collection_name string, page_name string) !string { - // Apply name_fix to collection and page names - fixed_collection_name := texttools.name_fix(collection_name) - fixed_page_name := texttools.name_fix(page_name) - - // Check if the collection exists - collection_path := c.redis.hget('doctree:path', fixed_collection_name) or { - return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found') - } - - // Get the relative path of the page within the collection - rel_path := c.redis.hget('doctree:${fixed_collection_name}', fixed_page_name) or { - return error('${DocTreeError.page_not_found}: Page "${page_name}" not found in collection "${collection_name}"') - } - - // Combine the collection path with the relative path - return os.join_path(collection_path, rel_path) -} - -// get_file_path returns the path for a file in a collection -pub fn (mut c DocTreeClient) get_file_path(collection_name string, file_name string) !string { - // Apply name_fix to collection and file names - fixed_collection_name := texttools.name_fix(collection_name) - fixed_file_name := texttools.name_fix(file_name) - - // Check if the collection exists - collection_path := c.redis.hget('doctree:path', fixed_collection_name) or { - return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found') - } - - // Get the relative path of the file within the collection - rel_path := c.redis.hget('doctree:${fixed_collection_name}', fixed_file_name) or { - return error('${DocTreeError.file_not_found}: File "${file_name}" not found in collection "${collection_name}"') - } - - // Combine the collection path with the relative path - return os.join_path(collection_path, rel_path) -} - -// get_image_path returns the path for an image in a collection -pub fn (mut c DocTreeClient) get_image_path(collection_name string, image_name string) !string { - // Apply name_fix to collection and image names - fixed_collection_name := texttools.name_fix(collection_name) - fixed_image_name := texttools.name_fix(image_name) - - // Check if the collection exists - collection_path := c.redis.hget('doctree:path', fixed_collection_name) or { - return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found') - } - - // Get the relative path of the image within the collection - rel_path := c.redis.hget('doctree:${fixed_collection_name}', fixed_image_name) or { - return error('${DocTreeError.image_not_found}: Image "${image_name}" not found in collection "${collection_name}"') - } - - if rel_path == '' { - return error('${DocTreeError.image_not_found}: Image "${image_name}" found in collection "${collection_name}" but its path is empty in Redis.') - } - - // console.print_debug('get_image_path: rel_path for "${image_name}": "${rel_path}"') - - // Combine the collection path with the relative path - return os.join_path(collection_path, rel_path) -} - -// page_exists checks if a page exists in a collection -pub fn (mut c DocTreeClient) page_exists(collection_name string, page_name string) bool { - // Apply name_fix to collection and page names - fixed_collection_name := texttools.name_fix(collection_name) - fixed_page_name := texttools.name_fix(page_name) - - // Check if the collection exists - e := c.redis.hexists('doctree:path', fixed_collection_name) or { return false } - if !e { - return false - } - - // Check if the page exists in the collection - return c.redis.hexists('doctree:${fixed_collection_name}', fixed_page_name) or { false } -} - -// file_exists checks if a file exists in a collection -pub fn (mut c DocTreeClient) file_exists(collection_name string, file_name string) bool { - // Apply name_fix to collection and file names - fixed_collection_name := texttools.name_fix(collection_name) - fixed_file_name := texttools.name_fix(file_name) - - // Check if the collection exists - e := c.redis.hexists('doctree:path', fixed_collection_name) or { return false } - if !e { - return false - } - - // Check if the file exists in the collection - return c.redis.hexists('doctree:${fixed_collection_name}', fixed_file_name) or { false } -} - -// image_exists checks if an image exists in a collection -pub fn (mut c DocTreeClient) image_exists(collection_name string, image_name string) bool { - // Apply name_fix to collection and image names - fixed_collection_name := texttools.name_fix(collection_name) - fixed_image_name := texttools.name_fix(image_name) - - // Check if the collection exists - e := c.redis.hexists('doctree:path', fixed_collection_name) or { return false } - if !e { - return false - } - - // Check if the image exists in the collection - return c.redis.hexists('doctree:${fixed_collection_name}', fixed_image_name) or { false } -} - -// get_page_content returns the content of a page in a collection -pub fn (mut c DocTreeClient) get_page_content(collection_name string, page_name string) !string { - // Apply name_fix to collection and page names - fixed_collection_name := texttools.name_fix(collection_name) - fixed_page_name := texttools.name_fix(page_name) - - // Get the path for the page - page_path := c.get_page_path(fixed_collection_name, fixed_page_name)! - - // Use pathlib to read the file content - mut path := pathlib.get_file(path: page_path)! - - // Check if the file exists - if !path.exists() { - return error('${DocTreeError.page_not_found}: Page file "${page_path}" does not exist on disk') - } - - // Read and return the file content - return path.read()! -} - -// list_collections returns a list of all collection names -pub fn (mut c DocTreeClient) list_collections() ![]string { - // Get all collection names from Redis - return c.redis.hkeys('doctree:path')! -} - -// list_pages returns a list of all page names in a collection -pub fn (mut c DocTreeClient) list_pages(collection_name string) ![]string { - // Apply name_fix to collection name - fixed_collection_name := texttools.name_fix(collection_name) - - // Check if the collection exists - if !(c.redis.hexists('doctree:path', fixed_collection_name) or { false }) { - return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found') - } - - // Get all keys from the collection hash - all_keys := c.redis.hkeys('doctree:${fixed_collection_name}')! - - // Filter out only the page names (those without file extensions) - mut page_names := []string{} - for key in all_keys { - if !key.contains('.') { - page_names << key - } - } - - return page_names -} - -// list_files returns a list of all file names in a collection -pub fn (mut c DocTreeClient) list_files(collection_name string) ![]string { - // Apply name_fix to collection name - fixed_collection_name := texttools.name_fix(collection_name) - - // Check if the collection exists - if !(c.redis.hexists('doctree:path', fixed_collection_name) or { false }) { - return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found') - } - - // Get all keys from the collection hash - all_keys := c.redis.hkeys('doctree:${fixed_collection_name}')! - - // Filter out only the file names (those with file extensions, but not images) - mut file_names := []string{} - for key in all_keys { - // Get the value (path) for this key - value := c.redis.hget('doctree:${fixed_collection_name}', key) or { continue } - - // Check if the value contains a file extension (has a dot) - if value.contains('.') { - // Check if the value ends with any of the image extensions - mut is_image := false - for ext in image_extensions { - if value.ends_with(ext) { - is_image = true - break - } - } - - // Add to file_names if it's not an image and not a page - if !is_image && !value.ends_with('.md') { - file_names << key - } - } - } - - return file_names -} - -// list_images returns a list of all image names in a collection -pub fn (mut c DocTreeClient) list_images(collection_name string) ![]string { - // Apply name_fix to collection name - fixed_collection_name := texttools.name_fix(collection_name) - - // Check if the collection exists - if !(c.redis.hexists('doctree:path', fixed_collection_name) or { false }) { - return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found') - } - - // Get all keys from the collection hash - all_keys := c.redis.hkeys('doctree:${fixed_collection_name}')! - - // Filter out only the image names (those whose values end with image extensions) - mut image_names := []string{} - for key in all_keys { - // Get the value (path) for this key - value := c.redis.hget('doctree:${fixed_collection_name}', key) or { continue } - - // Check if the value ends with any of the image extensions - for ext in image_extensions { - if value.ends_with(ext) { - image_names << key - break - } - } - } - - return image_names -} - -// list_pages_map returns a map of collection names to a list of page names within that collection. -// The structure is map[collectionname][]pagename. -pub fn (mut c DocTreeClient) list_pages_map() !map[string][]string { - mut result := map[string][]string{} - collections := c.list_collections()! - - for col_name in collections { - mut page_names := c.list_pages(col_name)! - page_names.sort() - result[col_name] = page_names - } - return result -} - -// list_markdown returns the collections and their pages in markdown format. -pub fn (mut c DocTreeClient) list_markdown() !string { - mut markdown_output := '' - pages_map := c.list_pages_map()! - - if pages_map.len == 0 { - return 'No collections or pages found in this doctree.' - } - - mut sorted_collections := pages_map.keys() - sorted_collections.sort() - - for col_name in sorted_collections { - page_names := pages_map[col_name] - markdown_output += '## ${col_name}\n' - if page_names.len == 0 { - markdown_output += ' * No pages in this collection.\n' - } else { - for page_name in page_names { - markdown_output += ' * ${page_name}\n' - } - } - markdown_output += '\n' // Add a newline for spacing between collections - } - return markdown_output -} - -// get_page_paths returns the path of a page and the paths of its linked images. -// Returns (page_path, image_paths) -pub fn (mut c DocTreeClient) get_page_paths(collection_name string, page_name string) !(string, []string) { - // Get the page path - page_path := c.get_page_path(collection_name, page_name)! - page_content := c.get_page_content(collection_name, page_name)! - - // Extract image names from the page content - image_names := extract_image_links(page_content, true)! - // println(image_names) - - mut image_paths := []string{} - for image_name in image_names { - // Get the path for each image - image_path := c.get_image_path(collection_name, image_name) or { - // If an image is not found, log a warning and continue, don't fail the whole operation - return error('Error: Linked image "${image_name}" not found in collection "${collection_name}". Skipping.') - } - image_paths << image_path - } - - return page_path, image_paths -} - -// copy_page copies a page and its linked images to a specified destination. -pub fn (mut c DocTreeClient) copy_images(collection_name string, page_name string, destination_path string) ! { - // Get the page path and linked image paths - page_path, image_paths := c.get_page_paths(collection_name, page_name)! - - // println('copy_page: Linked image paths: ${image_paths}') - - // Ensure the destination directory exists - os.mkdir_all(destination_path)! - - // // Copy the page file - // page_file_name := os.base(page_path) - // dest_page_path := os.join_path(destination_path, page_file_name) - // os.cp(page_path, dest_page_path)! - - // Create an 'img' subdirectory within the destination - images_dest_path := os.join_path(destination_path, 'img') - os.mkdir_all(images_dest_path)! - - // Copy each linked image - for image_path in image_paths { - // println(image_path) - image_file_name := os.base(image_path) - dest_image_path := os.join_path(images_dest_path, image_file_name) - // console.print_debug('copy_page: Copying image file from "${image_path}" to "${dest_image_path}"') - os.cp(image_path, dest_image_path)! - // console.print_debug('Copy Image file "${image_file_name}" copied.') - } -} diff --git a/libarchive/doctreeclient/doctree_test.v b/libarchive/doctreeclient/doctree_test.v deleted file mode 100755 index 9bd2d7c1..00000000 --- a/libarchive/doctreeclient/doctree_test.v +++ /dev/null @@ -1,135 +0,0 @@ -module doctreeclient - -import incubaid.herolib.data.doctree -import incubaid.herolib.core.base -import incubaid.herolib.core.pathlib -import os - -fn test_doctree_client() ! { - println('Setting up doctree data in Redis...') - - // First, populate Redis with doctree data - mut tree := doctree.new(name: 'test')! - - tree.scan( - git_url: 'https://git.threefold.info/tfgrid/docs_tfgrid4/src/branch/main/collections' - git_pull: false - )! - - tree.export( - destination: '/tmp/mdexport' - reset: true - exclude_errors: false - )! - - println('Doctree data populated in Redis') - - // Create a DocTreeClient instance - mut client := new()! - - // Test listing collections - println('\nListing collections:') - collections := client.list_collections()! - println('Found ${collections.len} collections') - - if collections.len == 0 { - println('No collections found. Test cannot continue.') - panic('No collections found') - } - - // Use the first collection for testing - collection_name := collections[0] - println('\nUsing collection: ${collection_name}') - - // Test listing pages - println('\nListing pages:') - pages := client.list_pages(collection_name)! - println('Found ${pages.len} pages') - - if pages.len > 0 { - // Test getting page path and content - page_name := pages[0] - println('\nTesting page: ${page_name}') - - // Test page existence - exists := client.page_exists(collection_name, page_name) - println('Page exists: ${exists}') - - // Test getting page path - page_path := client.get_page_path(collection_name, page_name)! - println('Page path: ${page_path}') - - // Test getting page content - content := client.get_page_content(collection_name, page_name)! - println('Page content length: ${content.len} characters') - } else { - println('No pages found for testing') - } - - // Test listing images - println('\nListing images:') - images := client.list_images(collection_name)! - println('Found ${images.len} images') - - if images.len > 0 { - // Test getting image path - image_name := images[0] - println('\nTesting image: ${image_name}') - - // Test image existence - exists := client.image_exists(collection_name, image_name) - println('Image exists: ${exists}') - - // Test getting image path - image_path := client.get_image_path(collection_name, image_name)! - println('Image path: ${image_path}') - - // Check if the image file exists on disk - println('Image file exists on disk: ${os.exists(image_path)}') - } else { - println('No images found for testing') - } - - // Test listing files - println('\nListing files:') - files := client.list_files(collection_name)! - println('Found ${files.len} files') - - if files.len > 0 { - // Test getting file path - file_name := files[0] - println('\nTesting file: ${file_name}') - - // Test file existence - exists := client.file_exists(collection_name, file_name) - println('File exists: ${exists}') - - // Test getting file path - file_path := client.get_file_path(collection_name, file_name)! - println('File path: ${file_path}') - - // Check if the file exists on disk - println('File exists on disk: ${os.exists(file_path)}') - } else { - println('No files found for testing') - } - - // Test error handling - println('\nTesting error handling:') - - // Test with non-existent collection - non_existent_collection := 'non_existent_collection' - println('Testing with non-existent collection: ${non_existent_collection}') - - exists := client.page_exists(non_existent_collection, 'any_page') - println('Page exists in non-existent collection: ${exists} (should be false)') - - // Test with non-existent page - non_existent_page := 'non_existent_page' - println('Testing with non-existent page: ${non_existent_page}') - - exists2 := client.page_exists(collection_name, non_existent_page) - println('Non-existent page exists: ${exists2} (should be false)') - - println('\nTest completed successfully!') -} diff --git a/libarchive/doctreeclient/extract_links.v b/libarchive/doctreeclient/extract_links.v deleted file mode 100644 index 835c8adb..00000000 --- a/libarchive/doctreeclient/extract_links.v +++ /dev/null @@ -1,53 +0,0 @@ -module doctreeclient - -import os - -pub fn extract_image_links(s string, exclude_http bool) ![]string { - mut result := []string{} - mut current_pos := 0 - for { - if current_pos >= s.len { - break - } - - // Find the start of an image markdown link - start_index := s.index_after('![', current_pos) or { -1 } - if start_index == -1 { - break // No more image links found - } - - // Find the closing bracket for alt text - alt_end_index := s.index_after(']', start_index) or { -1 } - if alt_end_index == -1 { - break - } - - // Check for opening parenthesis for URL - if alt_end_index + 1 >= s.len || s[alt_end_index + 1] != `(` { - current_pos = alt_end_index + 1 // Move past this invalid sequence - continue - } - - // Find the closing parenthesis for URL - url_start_index := alt_end_index + 2 - url_end_index := s.index_after(')', url_start_index) or { -1 } - if url_end_index == -1 { - break - } - - // Extract the URL - url := s[url_start_index..url_end_index] - if exclude_http && (url.starts_with('http://') || url.starts_with('https://')) { - current_pos = url_end_index + 1 - continue - } - - // Extract only the base name of the image from the URL - image_base_name := os.base(url) - result << image_base_name - - // Move current_pos past the found link to continue searching - current_pos = url_end_index + 1 - } - return result -} diff --git a/libarchive/doctreeclient/extract_links_test.v b/libarchive/doctreeclient/extract_links_test.v deleted file mode 100644 index bcbf2d36..00000000 --- a/libarchive/doctreeclient/extract_links_test.v +++ /dev/null @@ -1,131 +0,0 @@ -module doctreeclient - -import os - -fn test_extract_image_links() { - // Test case 1: Basic case with one image link - mut result := extract_image_links('Some text ![Alt Text](https://example.com/image1.png) more text', - false)! - assert result.len == 1 - assert result[0] == 'https://example.com/image1.png' - - // Test case 2: Multiple image links - result = extract_image_links('![Img1](https://example.com/img1.jpg) Text ![Img2](https://example.com/img2.gif)', - false)! - assert result.len == 2 - assert result[0] == 'https://example.com/img1.jpg' - assert result[1] == 'https://example.com/img2.gif' - - // Test case 3: No image links - result = extract_image_links('Just some plain text without images.', false)! - assert result.len == 0 - - // Test case 4: Mixed content with other markdown - result = extract_image_links('A link [Link](https://example.com) and an image ![Photo](https://example.com/photo.jpeg).', - false)! - assert result.len == 1 - assert result[0] == 'https://example.com/photo.jpeg' - - // Test case 5: Invalid image link (missing parenthesis) - result = extract_image_links('Invalid ![Broken Link]https://example.com/broken.png', - false)! - assert result.len == 0 - - // Test case 6: Empty string - result = extract_image_links('', false)! - assert result.len == 0 - - // Test case 7: Image link at the beginning of the string - result = extract_image_links('![Start](https://example.com/start.png) Some text.', - false)! - assert result.len == 1 - assert result[0] == 'https://example.com/start.png' - - // Test case 8: Image link at the end of the string - result = extract_image_links('Some text ![End](https://example.com/end.png)', false)! - assert result.len == 1 - assert result[0] == 'https://example.com/end.png' - - // Test case 9: Image link with spaces in URL (should not happen in valid markdown, but good to test robustness) - result = extract_image_links('![Space](https://example.com/image with spaces.png)', - false)! - assert result.len == 1 - assert result[0] == 'https://example.com/image with spaces.png' - - // Test case 10: Image link with special characters in URL - result = extract_image_links('![Special](https://example.com/path/to/image?id=1&name=test.png)', - false)! - assert result.len == 1 - assert result[0] == 'https://example.com/path/to/image?id=1&name=test.png' - - // Test case 11: Multiple image links without spaces in between - result = extract_image_links('![A](https://a.com)![B](https://b.com)![C](https://c.com)', - false)! - assert result.len == 3 - assert result[0] == 'https://a.com' - assert result[1] == 'https://b.com' - assert result[2] == 'https://c.com' - - // Test case 12: Image link with empty alt text - result = extract_image_links('![](https://example.com/noalt.png)', false)! - assert result.len == 1 - assert result[0] == 'https://example.com/noalt.png' - - // Test case 13: Image link with empty URL (invalid markdown, but test behavior) - result = extract_image_links('![Empty URL]()', false)! - assert result.len == 1 - assert result[0] == '' // Expecting an empty string for the URL - - // Test case 14: Image link with only alt text and no URL part - result = extract_image_links('![Only Alt Text]', false)! - assert result.len == 0 - - // Test case 15: Image link with only URL part and no alt text - result = extract_image_links('()', false)! - assert result.len == 0 - - // --- Test cases for exclude_http = true --- - - // Test case 16: Exclude http links, only relative link - result = extract_image_links('Some text ![Relative](image.png) ![Absolute](https://example.com/image.png)', - true)! - assert result.len == 1 - assert result[0] == 'image.png' - - // Test case 17: Exclude http links, multiple relative links - result = extract_image_links('![Rel1](img1.jpg) ![Abs1](http://example.com/img.jpg) ![Rel2](/path/to/img2.gif)', - true)! - assert result.len == 2 - assert result[0] == 'img1.jpg' - assert result[1] == '/path/to/img2.gif' - - // Test case 18: Exclude http links, all absolute links - result = extract_image_links('![Abs1](https://example.com/img1.png) ![Abs2](http://example.com/img2.png)', - true)! - assert result.len == 0 - - // Test case 19: Exclude http links, no links at all - result = extract_image_links('Plain text.', true)! - assert result.len == 0 - - // Test case 20: Exclude http links, mixed absolute and relative, with other markdown - result = extract_image_links('A link [Link](https://example.com) and an image ![Photo](https://example.com/photo.jpeg) and another ![Local](local.png).', - true)! - assert result.len == 1 - assert result[0] == 'local.png' - - // Test case 21: Exclude http links, empty string - result = extract_image_links('', true)! - assert result.len == 0 - - // Test case 22: Exclude http links, image with empty URL (should still be included if not http) - result = extract_image_links('![Empty URL]()', true)! - assert result.len == 1 - assert result[0] == '' - - // Test case 23: Exclude http links, image with data URI (should not be excluded) - result = extract_image_links('![Data URI](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=)', - true)! - assert result.len == 1 - assert result[0] == 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=' -} diff --git a/libarchive/doctreeclient/factory.v b/libarchive/doctreeclient/factory.v deleted file mode 100644 index 04b9df12..00000000 --- a/libarchive/doctreeclient/factory.v +++ /dev/null @@ -1,12 +0,0 @@ -module doctreeclient - -import incubaid.herolib.core.base - -pub fn new() !&DocTreeClient { - mut context := base.context()! - mut redis := context.redis()! - - return &DocTreeClient{ - redis: redis - } -} diff --git a/libarchive/doctreeclient/model.v b/libarchive/doctreeclient/model.v deleted file mode 100644 index b8c4237e..00000000 --- a/libarchive/doctreeclient/model.v +++ /dev/null @@ -1,9 +0,0 @@ -module doctreeclient - -import incubaid.herolib.core.redisclient - -// Combined config structure -pub struct DocTreeClient { -pub mut: - redis &redisclient.Redis -} diff --git a/libarchive/encoderherocomplex/decoder.v b/libarchive/encoderherocomplex/decoder.v deleted file mode 100644 index 2c94a514..00000000 --- a/libarchive/encoderherocomplex/decoder.v +++ /dev/null @@ -1,124 +0,0 @@ -module encoderherocomplex - -import incubaid.herolib.data.paramsparser -import time - -pub struct Decoder[T] { -pub mut: - object T - data string -} - -pub fn decode[T](data string) !T { - return decode_struct[T](T{}, data) -} - -// decode_struct is a generic function that decodes a JSON map into the struct T. -fn decode_struct[T](_ T, data string) !T { - mut typ := T{} - - $if T is $struct { - obj_name := T.name.all_after_last('.').to_lower() - mut action_name := '${obj_name}.define' - - if !data.contains(action_name) { - action_name = '${obj_name}.configure' - if !data.contains(action_name) { - action_name = 'define.${obj_name}' - if !data.contains(action_name) { - action_name = 'configure.${obj_name}' - if !data.contains(action_name) { - return error('Data does not contain action: ${obj_name}.define, ${obj_name}.configure, define.${obj_name}, or configure.${obj_name}') - } - } - } - } - - // Split by !! and filter for relevant actions - actions_split := data.split('!!') - actions := actions_split.filter(it.trim_space().len > 0) - - // Find and parse main action - main_actions := actions.filter(it.contains(action_name) && !it.contains('.${obj_name}.')) - - if main_actions.len > 0 { - action_str := main_actions[0] - params_str := action_str.all_after(action_name).trim_space() - params := paramsparser.parse(params_str) or { - return error('Could not parse params: ${params_str}\n${err}') - } - typ = params.decode[T](typ)! - } - - // Process nested fields - $for field in T.fields { - mut should_skip := false - - for attr in field.attrs { - if attr.contains('skip') || attr.contains('skipdecode') { - should_skip = true - break - } - } - - if !should_skip { - field_name := field.name.to_lower() - - $if field.is_struct { - $if field.typ !is time.Time { - // Handle nested structs - if !field.name[0].is_capital() { - nested_action := '${action_name}.${field_name}' - nested_actions := actions.filter(it.contains(nested_action)) - - if nested_actions.len > 0 { - nested_data := '!!' + nested_actions.join('\n!!') - typ.$(field.name) = decode_struct(typ.$(field.name), nested_data)! - } - } - } - } $else $if field.is_array { - // Handle arrays of structs - elem_type_name := field.typ.all_after(']').to_lower() - array_action := '${action_name}.${elem_type_name}' - array_actions := actions.filter(it.contains(array_action)) - - if array_actions.len > 0 { - mut arr_data := []string{} - for action in array_actions { - arr_data << '!!' + action - } - - // Decode each array item - decoded_arr := decode_array(typ.$(field.name), arr_data.join('\n'))! - typ.$(field.name) = decoded_arr - } - } - } - } - } $else { - return error("The type `${T.name}` can't be decoded.") - } - - return typ -} - -fn decode_array[T](_ []T, data string) ![]T { - mut arr := []T{} - - $if T is $struct { - // Split by !! to get individual items - items := data.split('!!').filter(it.trim_space().len > 0) - - for item in items { - item_data := '!!' + item - decoded := decode_struct(T{}, item_data)! - arr << decoded - } - } $else { - return error('Array decoding only supports structs') - } - - return arr -} - diff --git a/libarchive/encoderherocomplex/decoder_test.v b/libarchive/encoderherocomplex/decoder_test.v deleted file mode 100644 index 9a06fb3b..00000000 --- a/libarchive/encoderherocomplex/decoder_test.v +++ /dev/null @@ -1,146 +0,0 @@ -module encoderherocomplex - -import time -import incubaid.herolib.data.paramsparser -import incubaid.herolib.core.texttools - -struct TestStruct { - id int - name string -} - -const blank_script = '!!define.test_struct' -const full_script = '!!define.test_struct id: 42 name: testobject' -const invalid_script = '!!define.another_struct' - -fn test_decode_simple() ! { - mut object := decode[TestStruct](blank_script)! - assert object == TestStruct{} - - object = decode[TestStruct](full_script)! - assert object == TestStruct{ - id: 42 - name: 'testobject' - } - - object = decode[TestStruct](invalid_script) or { - assert true - TestStruct{} - } -} - -struct ChildStruct { - text string - number int -} - -struct ComplexStruct { - id int - name string - child ChildStruct -} - -const blank_complex = '!!define.complex_struct -!!define.child_struct' - -const partial_complex = '!!define.complex_struct id: 42 name: testcomplex -!!define.child_struct' - -const full_complex = '!!define.complex_struct id:42 name:testobject -!!define.complex_struct.child_struct text:child_text number:24' - -fn test_decode_complex() ! { - mut object := decode[ComplexStruct](blank_complex)! - assert object == ComplexStruct{} - - object = decode[ComplexStruct](partial_complex)! - assert object == ComplexStruct{ - id: 42 - name: 'testcomplex' - } - - object = decode[ComplexStruct](full_complex)! - assert object == ComplexStruct{ - id: 42 - name: 'testobject' - child: ChildStruct{ - text: 'child_text' - number: 24 - } - } -} - -pub struct Base { - id int - // remarks []Remark TODO: add support -} - -pub struct Remark { - text string -} - -pub struct Person { - Base -mut: - name string - age int - birthday time.Time - deathday time.Time - car Car - profiles []Profile -} - -pub struct Car { - name string - year int - insurance Insurance -} - -pub struct Insurance { - provider string - expiration time.Time -} - -pub struct Profile { - platform string - url string -} - -const person_heroscript = "!!define.person id:1 name:Bob age:21 birthday:'2012-12-12 00:00:00' -!!define.person.car name:'Bob\\'s car' year:2014 -!!define.person.car.insurance provider:insurer expiration:'0000-00-00 00:00:00' -!!define.person.profile platform:Github url:github.com/example" - -const person = Person{ - id: 1 - name: 'Bob' - age: 21 - birthday: time.new( - day: 12 - month: 12 - year: 2012 - ) - car: Car{ - name: "Bob's car" - year: 2014 - } - profiles: [ - Profile{ - platform: 'Github' - url: 'github.com/example' - }, - ] -} - -fn test_decode() ! { - // Test decoding with proper person data - object := decode[Person](person_heroscript)! - assert object == person - - // Test that empty string fails as expected - decode[Person]('') or { - assert true // This should fail, which is correct - return - } - assert false // Should not reach here -} diff --git a/libarchive/encoderherocomplex/encoder.v b/libarchive/encoderherocomplex/encoder.v deleted file mode 100644 index 899639cd..00000000 --- a/libarchive/encoderherocomplex/encoder.v +++ /dev/null @@ -1,168 +0,0 @@ -module encoderherocomplex - -import incubaid.herolib.data.paramsparser -import time -import v.reflection -import incubaid.herolib.data.ourtime -// Helper function to check if field should be skipped -fn should_skip_field(attrs []string) bool { - for attr in attrs { - attr_clean := attr.to_lower().replace(' ', '').replace('\t', '') - if attr_clean == 'skip' - || attr_clean.starts_with('skip;') - || attr_clean.ends_with(';skip') - || attr_clean.contains(';skip;') - || attr_clean == 'skipdecode' - || attr_clean.starts_with('skipdecode;') - || attr_clean.ends_with(';skipdecode') - || attr_clean.contains(';skipdecode;') { - return true - } - } - return false -} -// import incubaid.herolib.ui.console - -// Encoder encodes the an `Any` type into HEROSCRIPT representation. -// It provides parameters in order to change the end result. -pub struct Encoder { -pub mut: - escape_unicode bool = true - action_name string - action_names []string - params paramsparser.Params - children []Encoder - parent ?&Encoder @[skip; str: skip] -} - -// encode is a generic function that encodes a type into a HEROSCRIPT string. -pub fn encode[T](val T) !string { - mut e := Encoder{ - params: paramsparser.Params{} - } - - $if T is $struct { - e.encode_struct[T](val)! - } $else $if T is $array { - // TODO: need to make comma separated list only works if int,u8,u16,i8... or string if string put all elements in \''...\'',... - e.add_child_list[T](val, 'TODO') - } $else { - return error('can only add elements for struct or array of structs. \n${val}') - } - return e.export()! -} - -// export exports an encoder into encoded heroscript -pub fn (e Encoder) export() !string { - mut script := e.params.export( - pre: '!!define.${e.action_names.join('.')}' - indent: '\t' - skip_empty: true - ) - - if e.children.len > 0 { - script += '\n' + e.children.map(it.export()!).join('\n') - } - - return script -} - -// needs to be a struct we are adding -// parent is the name of the action e.g define.customer:contact -pub fn (mut e Encoder) add_child[T](val T, parent string) ! { - $if T is $array { - mut counter := 0 - for valitem in val { - mut e2 := e.add_child[T](valitem, '${parent}:${counter}')! - } - return - } - mut e2 := Encoder{ - params: paramsparser.Params{} - parent: &e - action_names: e.action_names.clone() // careful, if not cloned gets mutated later - } - $if T is $struct { - e2.params.set('key', parent) - e2.encode_struct[T](val)! - e.children << e2 - } $else { - return error('can only add elements for struct or array of structs. \n${val}') - } -} - -pub fn (mut e Encoder) add_child_list[U](val []U, parent string) ! { - for i in 0 .. val.len { - mut counter := 0 - $if U is $struct { - e.add_child(val[i], '${parent}:${counter}')! - counter += 1 - } - } -} - -// needs to be a struct we are adding -// parent is the name of the action e.g define.customer:contact -pub fn (mut e Encoder) add[T](val T) ! { - // $if T is []$struct { - // // panic("not implemented") - // for valitem in val{ - // mut e2:=e.add[T](valitem)! - // } - // } - mut e2 := Encoder{ - params: paramsparser.Params{} - parent: &e - action_names: e.action_names.clone() // careful, if not cloned gets mutated later - } - $if T is $struct && T !is time.Time { - e2.params.set('key', '${val}') - e2.encode_struct[T](val)! - e.children << e2 - } $else { - return error('can only add elements for struct or array of structs. \n${val}') - } -} - -pub fn (mut e Encoder) encode_array[U](val []U) ! { - for i in 0 .. val.len { - $if U is $struct { - e.add(val[i])! - } - } -} - -// now encode the struct -pub fn (mut e Encoder) encode_struct[T](t T) ! { - mut mytype := reflection.type_of[T](t) - struct_attrs := attrs_get_reflection(mytype) - - mut action_name := T.name.all_after_last('.').to_lower() - // println('action_name: ${action_name} ${T.name}') - if 'alias' in struct_attrs { - action_name = struct_attrs['alias'].to_lower() - } - e.action_names << action_name - - params := paramsparser.encode[T](t, recursive: false)! - e.params = params - - // encode children structs and array of structs - $for field in T.fields { - if !should_skip_field(field.attrs) { - val := t.$(field.name) - // time is encoded in the above params encoding step so skip and dont treat as recursive struct - $if val is time.Time || val is ourtime.OurTime { - } $else $if val is $struct { - if field.name[0].is_capital() { - embedded_params := paramsparser.encode(val, recursive: false)! - e.params.params << embedded_params.params - } else { - e.add(val)! - } - } $else $if val is $array { - e.encode_array(val)! - } - } - } -} diff --git a/libarchive/encoderherocomplex/encoder_ignorepropery_test.v b/libarchive/encoderherocomplex/encoder_ignorepropery_test.v deleted file mode 100644 index f532095f..00000000 --- a/libarchive/encoderherocomplex/encoder_ignorepropery_test.v +++ /dev/null @@ -1,42 +0,0 @@ -module encoderherocomplex - -import incubaid.herolib.data.paramsparser -import time -import v.reflection - -struct MyStruct { - id int - name string - // skip attributes would be best way how to do the encoding but can't get it to work - other ?&Remark @[skip; str: skip] -} - -// is the one we should skip -pub struct Remark { - id int -} - -fn test_encode() ! { - mut o := MyStruct{ - id: 1 - name: 'test' - other: &Remark{ - id: 123 - } - } - - script := encode[MyStruct](o)! - - assert script.trim_space() == '!!define.my_struct id:1 name:test' - - println(script) - - o2 := decode[MyStruct](script)! - - assert o2 == MyStruct{ - id: 1 - name: 'test' - } - - println(o2) -} diff --git a/libarchive/encoderherocomplex/encoder_test.v b/libarchive/encoderherocomplex/encoder_test.v deleted file mode 100644 index a1e2e595..00000000 --- a/libarchive/encoderherocomplex/encoder_test.v +++ /dev/null @@ -1,121 +0,0 @@ -module encoderherocomplex - -import incubaid.herolib.data.paramsparser -import incubaid.herolib.data.ourtime -import time -import v.reflection - -struct Base { - id int - remarks []Remark -} - -pub struct Remark { - text string -} - -struct Company { - name string - founded ourtime.OurTime - employees []Person -} - -const company = Company{ - name: 'Tech Corp' - founded: ourtime.new('2022-12-05 20:14')! - employees: [ - person, - Person{ - id: 2 - name: 'Alice' - age: 30 - birthday: time.new( - day: 20 - month: 6 - year: 1990 - ) - car: Car{ - name: "Alice's car" - year: 2018 - } - profiles: [ - Profile{ - platform: 'LinkedIn' - url: 'linkedin.com/alice' - }, - ] - }, - ] -} - -struct Person { - Base -mut: - name string - age ?int = 20 - birthday time.Time - deathday ?time.Time - car Car - profiles []Profile -} - -struct Car { - name string - year int - insurance Insurance -} - -struct Insurance { - provider string - expiration time.Time -} - -struct Profile { - platform string - url string -} - -const person_heroscript = "!!define.person id:1 name:Bob age:21 birthday:'2012-12-12 00:00:00' -!!define.person.car name:'Bob\\'s car' year:2014 -!!define.person.car.insurance provider:insurer expiration:'0000-00-00 00:00:00' -!!define.person.profile platform:Github url:github.com/example" - -const person = Person{ - id: 1 - name: 'Bob' - age: 21 - birthday: time.new( - day: 12 - month: 12 - year: 2012 - ) - car: Car{ - name: "Bob's car" - year: 2014 - insurance: Insurance{ - provider: 'insurer' - } - } - profiles: [ - Profile{ - platform: 'Github' - url: 'github.com/example' - }, - ] -} - -const company_script = "!!define.company name:'Tech Corp' founded:'2022-12-05 20:14' -!!define.company.person id:1 name:Bob age:21 birthday:'2012-12-12 00:00:00' -!!define.company.person.car name:'Bob\\'s car' year:2014 -!!define.company.person.car.insurance provider:insurer expiration:'0000-00-00 00:00:00' -!!define.company.person.profile platform:Github url:github.com/example -!!define.company.person id:2 name:Alice age:30 birthday:'1990-06-20 00:00:00' -!!define.company.person.car name:'Alice\\'s car' year:2018 -!!define.company.person.car.insurance provider:'' expiration:'0000-00-00 00:00:00' -!!define.company.person.profile platform:LinkedIn url:linkedin.com/alice" - -fn test_encode() ! { - person_script := encode[Person](person)! - assert person_script.trim_space() == person_heroscript.trim_space() - assert encode[Company](company)!.trim_space() == company_script.trim_space() -} diff --git a/libarchive/encoderherocomplex/postgres_client_decoder_test.v b/libarchive/encoderherocomplex/postgres_client_decoder_test.v deleted file mode 100644 index 5b04018d..00000000 --- a/libarchive/encoderherocomplex/postgres_client_decoder_test.v +++ /dev/null @@ -1,233 +0,0 @@ -module encoderherocomplex - -pub struct PostgresqlClient { -pub mut: - name string = 'default' - user string = 'root' - port int = 5432 - host string = 'localhost' - password string - dbname string = 'postgres' -} - -const postgres_client_blank = '!!postgresql_client.configure' -const postgres_client_full = '!!postgresql_client.configure name:production user:app_user port:5433 host:db.example.com password:secret123 dbname:myapp' -const postgres_client_partial = '!!postgresql_client.configure name:dev host:localhost password:devpass' - -const postgres_client_complex = ' -!!postgresql_client.configure name:staging user:stage_user port:5434 host:staging.db.com password:stagepass dbname:stagingdb -' - -fn test_postgres_client_decode_blank() ! { - mut client := decode[PostgresqlClient](postgres_client_blank)! - assert client.name == 'default' - assert client.user == 'root' - assert client.port == 5432 - assert client.host == 'localhost' - assert client.password == '' - assert client.dbname == 'postgres' -} - -fn test_postgres_client_decode_full() ! { - mut client := decode[PostgresqlClient](postgres_client_full)! - assert client.name == 'production' - assert client.user == 'app_user' - assert client.port == 5433 - assert client.host == 'db.example.com' - assert client.password == 'secret123' - assert client.dbname == 'myapp' -} - -fn test_postgres_client_decode_partial() ! { - mut client := decode[PostgresqlClient](postgres_client_partial)! - assert client.name == 'dev' - assert client.user == 'root' // default value - assert client.port == 5432 // default value - assert client.host == 'localhost' - assert client.password == 'devpass' - assert client.dbname == 'postgres' // default value -} - -fn test_postgres_client_decode_complex() ! { - mut client := decode[PostgresqlClient](postgres_client_complex)! - assert client.name == 'staging' - assert client.user == 'stage_user' - assert client.port == 5434 - assert client.host == 'staging.db.com' - assert client.password == 'stagepass' - assert client.dbname == 'stagingdb' -} - -fn test_postgres_client_encode_decode_roundtrip() ! { - // Test encoding and decoding roundtrip - original := PostgresqlClient{ - name: 'testdb' - user: 'testuser' - port: 5435 - host: 'test.host.com' - password: 'testpass123' - dbname: 'testdb' - } - - // Encode to heroscript - encoded := encode[PostgresqlClient](original)! - - // println('Encoded heroscript: ${encoded}') - // if true { - // panic("sss") - // } - - // Decode back from heroscript - decoded := decode[PostgresqlClient](encoded)! - - // Verify roundtrip - assert decoded.name == original.name - assert decoded.user == original.user - assert decoded.port == original.port - assert decoded.host == original.host - assert decoded.password == original.password - assert decoded.dbname == original.dbname -} - -fn test_postgres_client_encode() ! { - // Test encoding with different configurations - test_cases := [ - PostgresqlClient{ - name: 'minimal' - user: 'root' - port: 5432 - host: 'localhost' - password: '' - dbname: 'postgres' - }, - PostgresqlClient{ - name: 'full_config' - user: 'admin' - port: 5433 - host: 'remote.server.com' - password: 'securepass' - dbname: 'production' - }, - PostgresqlClient{ - name: 'localhost_dev' - user: 'dev' - port: 5432 - host: '127.0.0.1' - password: 'devpassword' - dbname: 'devdb' - }, - ] - - for client in test_cases { - encoded := encode[PostgresqlClient](client)! - decoded := decode[PostgresqlClient](encoded)! - - assert decoded.name == client.name - assert decoded.user == client.user - assert decoded.port == client.port - assert decoded.host == client.host - assert decoded.password == client.password - assert decoded.dbname == client.dbname - } -} - -// Play script for interactive testing -const play_script = ' -# PostgresqlClient Encode/Decode Play Script -# This script demonstrates encoding and decoding PostgresqlClient configurations - -!!postgresql_client.configure name:playground user:play_user - port:5432 - host:localhost - password:playpass - dbname:playdb - -# You can also use partial configurations -!!postgresql_client.configure name:quick_test host:127.0.0.1 - -# Default configuration (all defaults) -!!postgresql_client.configure -' - -fn test_play_script() ! { - // Test the play script with multiple configurations - lines := play_script.split_into_lines().filter(fn (line string) bool { - return line.trim(' ') != '' && !line.starts_with('#') - }) - - mut clients := []PostgresqlClient{} - - for line in lines { - if line.starts_with('!!postgresql_client.configure') { - client := decode[PostgresqlClient](line)! - clients << client - } - } - - assert clients.len == 3 - - // First client: full configuration - assert clients[0].name == 'playground' - assert clients[0].user == 'play_user' - assert clients[0].port == 5432 - - // Second client: partial configuration - assert clients[1].name == 'quick_test' - assert clients[1].host == '127.0.0.1' - assert clients[1].user == 'root' // default - - // Third client: defaults only - assert clients[2].name == 'default' - assert clients[2].host == 'localhost' - assert clients[2].port == 5432 -} - -// Utility function for manual testing -pub fn run_play_script() ! { - println('=== PostgresqlClient Encode/Decode Play Script ===') - println('Testing encoding and decoding of PostgresqlClient configurations...') - - // Test 1: Basic encoding - println('\n1. Testing basic encoding...') - client := PostgresqlClient{ - name: 'example' - user: 'example_user' - port: 5432 - host: 'example.com' - password: 'example_pass' - dbname: 'example_db' - } - - encoded := encode[PostgresqlClient](client)! - println('Encoded: ${encoded}') - - decoded := decode[PostgresqlClient](encoded)! - println('Decoded name: ${decoded.name}') - println('Decoded host: ${decoded.host}') - - // Test 2: Play script - println('\n2. Testing play script...') - test_play_script()! - println('Play script test passed!') - - // Test 3: Edge cases - println('\n3. Testing edge cases...') - edge_client := PostgresqlClient{ - name: 'edge' - user: '' - port: 0 - host: '' - password: '' - dbname: '' - } - - edge_encoded := encode[PostgresqlClient](edge_client)! - edge_decoded := decode[PostgresqlClient](edge_encoded)! - - assert edge_decoded.name == 'edge' - assert edge_decoded.user == '' - assert edge_decoded.port == 0 - println('Edge cases test passed!') - - println('\n=== All tests completed successfully! ===') -} diff --git a/libarchive/encoderherocomplex/readme.md b/libarchive/encoderherocomplex/readme.md deleted file mode 100644 index 4298b293..00000000 --- a/libarchive/encoderherocomplex/readme.md +++ /dev/null @@ -1,138 +0,0 @@ - -# HeroEncoder - Struct Serialization to HeroScript - -HeroEncoder provides bidirectional conversion between V structs and HeroScript format. - -## HeroScript Format - -HeroScript uses a structured action-based format: - -```heroscript -!!define.typename param1:value1 param2:'value with spaces' -!!define.typename.nested_field field1:value -!!define.typename.array_item field1:value -!!define.typename.array_item field1:value2 -``` - -## Basic Usage - -### Simple Struct - -```v -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.data.encoderhero -import time - -struct Person { -mut: - name string - age int = 20 - birthday time.Time -} - -mut person := Person{ - name: 'Bob' - age: 25 - birthday: time.now() -} - -// Encode to heroscript -heroscript := encoderhero.encode[Person](person)! -println(heroscript) -// Output: !!define.person name:Bob age:25 birthday:'2024-01-15 10:30:00' - -// Decode back -person2 := encoderhero.decode[Person](heroscript)! -println(person2) -``` - -### Nested Structs - -```v -struct Car { - name string - year int -} - -struct Person { -mut: - name string - car Car -} - -person := Person{ - name: 'Alice' - car: Car{ - name: 'Tesla' - year: 2024 - } -} - -heroscript := encoderhero.encode[Person](person)! -// Output: -// !!define.person name:Alice -// !!define.person.car name:Tesla year:2024 -``` - -### Arrays of Structs - -```v -struct Profile { - platform string - url string -} - -struct Person { -mut: - name string - profiles []Profile -} - -person := Person{ - name: 'Bob' - profiles: [ - Profile{platform: 'GitHub', url: 'github.com/bob'}, - Profile{platform: 'LinkedIn', url: 'linkedin.com/bob'} - ] -} - -heroscript := encoderhero.encode[Person](person)! -// Output: -// !!define.person name:Bob -// !!define.person.profile platform:GitHub url:github.com/bob -// !!define.person.profile platform:LinkedIn url:linkedin.com/bob -``` - -## Skip Attributes - -Use `@[skip]` or `@[skipdecode]` to exclude fields from encoding: - -```v -struct MyStruct { - id int - name string - other ?&Remark @[skip] -} -``` - -## Current Limitations - -⚠️ **IMPORTANT**: The decoder currently has limited functionality: - -- ✅ **Encoding**: Fully supports nested structs and arrays -- ⚠️ **Decoding**: Only supports flat structs (no nesting or arrays) -- 🔧 **In Progress**: Full decoder implementation for nested structures - -For production use, only use simple flat structs for encoding/decoding roundtrips. - -## Time Handling - -`time.Time` fields are automatically converted to string format: -- Format: `YYYY-MM-DD HH:mm:ss` -- Example: `2024-01-15 14:30:00` - -Use `incubaid.herolib.data.ourtime` for more flexible time handling. -``` -120 - \ No newline at end of file diff --git a/libarchive/encoderherocomplex/roundtrip_test.v b/libarchive/encoderherocomplex/roundtrip_test.v deleted file mode 100644 index 6ddd6ff1..00000000 --- a/libarchive/encoderherocomplex/roundtrip_test.v +++ /dev/null @@ -1,83 +0,0 @@ -module encoderherocomplex - -import time - -struct FullPerson { - id int - name string - age int - birthday time.Time - car FullCar - profiles []FullProfile -} - -struct FullCar { - name string - year int - insurance FullInsurance -} - -struct FullInsurance { - provider string - expiration time.Time -} - -struct FullProfile { - platform string - url string -} - -fn test_roundtrip_nested_struct() ! { - original := FullPerson{ - id: 1 - name: 'Alice' - age: 30 - birthday: time.new(year: 1993, month: 6, day: 15) - car: FullCar{ - name: 'Tesla' - year: 2024 - insurance: FullInsurance{ - provider: 'StateFarm' - expiration: time.new(year: 2025, month: 12, day: 31) - } - } - profiles: [ - FullProfile{platform: 'GitHub', url: 'github.com/alice'}, - FullProfile{platform: 'LinkedIn', url: 'linkedin.com/alice'}, - ] - } - - // Encode - encoded := encode[FullPerson](original)! - println('Encoded:\n${encoded}\n') - - // Decode - decoded := decode[FullPerson](encoded)! - - // Verify - assert decoded.id == original.id - assert decoded.name == original.name - assert decoded.age == original.age - assert decoded.car.name == original.car.name - assert decoded.car.year == original.car.year - assert decoded.car.insurance.provider == original.car.insurance.provider - assert decoded.profiles.len == original.profiles.len - assert decoded.profiles[0].platform == original.profiles[0].platform - assert decoded.profiles[1].url == original.profiles[1].url -} - -fn test_roundtrip_flat_struct() ! { - struct Simple { - id int - name string - age int - } - - original := Simple{id: 123, name: 'Bob', age: 25} - encoded := encode[Simple](original)! - decoded := decode[Simple](encoded)! - - assert decoded.id == original.id - assert decoded.name == original.name - assert decoded.age == original.age -} \ No newline at end of file diff --git a/libarchive/encoderherocomplex/tools.v b/libarchive/encoderherocomplex/tools.v deleted file mode 100644 index a775f7b5..00000000 --- a/libarchive/encoderherocomplex/tools.v +++ /dev/null @@ -1,26 +0,0 @@ -module encoderherocomplex - -import v.reflection - -// if at top of struct we have: @[name:"teststruct " ; params] . -// will return {'name': 'teststruct', 'params': ''} -fn attrs_get_reflection(mytype reflection.Type) map[string]string { - if mytype.sym.info is reflection.Struct { - return attrs_get(mytype.sym.info.attrs) - } - return map[string]string{} -} - -// will return {'name': 'teststruct', 'params': ''} -fn attrs_get(attrs []string) map[string]string { - mut out := map[string]string{} - for i in attrs { - if i.contains('=') { - kv := i.split('=') - out[kv[0].trim_space().to_lower()] = kv[1].trim_space().to_lower() - } else { - out[i.trim_space().to_lower()] = '' - } - } - return out -} diff --git a/libarchive/encoderherocomplex/types.v b/libarchive/encoderherocomplex/types.v deleted file mode 100644 index 37b2c4e3..00000000 --- a/libarchive/encoderherocomplex/types.v +++ /dev/null @@ -1,91 +0,0 @@ -module encoderherocomplex - -// byte array versions of the most common tokens/chars to avoid reallocations -const null_in_bytes = 'null' - -const true_in_string = 'true' - -const false_in_string = 'false' - -const empty_array = [u8(`[`), `]`]! - -const comma_rune = `,` - -const colon_rune = `:` - -const quote_rune = `"` - -const back_slash = [u8(`\\`), `\\`]! - -const quote = [u8(`\\`), `"`]! - -const slash = [u8(`\\`), `/`]! - -const null_unicode = [u8(`\\`), `u`, `0`, `0`, `0`, `0`]! - -const ascii_control_characters = ['\\u0000', '\\t', '\\n', '\\r', '\\u0004', '\\u0005', '\\u0006', - '\\u0007', '\\b', '\\t', '\\n', '\\u000b', '\\f', '\\r', '\\u000e', '\\u000f', '\\u0010', - '\\u0011', '\\u0012', '\\u0013', '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018', '\\u0019', - '\\u001a', '\\u001b', '\\u001c', '\\u001d', '\\u001e', '\\u001f']! - -const curly_open_rune = `{` - -const curly_close_rune = `}` - -const ascii_especial_characters = [u8(`\\`), `"`, `/`]! - -// // `Any` is a sum type that lists the possible types to be decoded and used. -// pub type Any = Null -// | []Any -// | bool -// | f32 -// | f64 -// | i16 -// | i32 -// | i64 -// | i8 -// | int -// | map[string]Any -// | string -// | time.Time -// | u16 -// | u32 -// | u64 -// | u8 - -// // Decodable is an interface, that allows custom implementations for decoding structs from JSON encoded values -// pub interface Decodable { -// from_json(f Any) -// } - -// Decodable is an interface, that allows custom implementations for encoding structs to their string based JSON representations -pub interface Encodable { - heroscript() string -} - -// `Null` struct is a simple representation of the `null` value in JSON. -pub struct Null { - is_null bool = true -} - -pub const null = Null{} - -// ValueKind enumerates the kinds of possible values of the Any sumtype. -pub enum ValueKind { - unknown - array - object - string_ - number -} - -// str returns the string representation of the specific ValueKind -pub fn (k ValueKind) str() string { - return match k { - .unknown { 'unknown' } - .array { 'array' } - .object { 'object' } - .string_ { 'string' } - .number { 'number' } - } -} diff --git a/libarchive/examples/baobab/generator/.gitignore b/libarchive/examples/baobab/generator/.gitignore deleted file mode 100644 index fbe24508..00000000 --- a/libarchive/examples/baobab/generator/.gitignore +++ /dev/null @@ -1 +0,0 @@ -pet_store_actor \ No newline at end of file diff --git a/libarchive/examples/baobab/generator/basic/.gitignore b/libarchive/examples/baobab/generator/basic/.gitignore deleted file mode 100644 index 80ce9592..00000000 --- a/libarchive/examples/baobab/generator/basic/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -methods.v -pet_store_actor -docs \ No newline at end of file diff --git a/libarchive/examples/baobab/generator/basic/README.md b/libarchive/examples/baobab/generator/basic/README.md deleted file mode 100644 index a5ea7d59..00000000 --- a/libarchive/examples/baobab/generator/basic/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Actor Generation Examples - -## `generate_methods.vsh` - -This example generates actor method prototypes from an actor specification. - -## `generate_actor_module.vsh` - -This example generates an entire actor module from an actor specification with the support for the specified interfaces. \ No newline at end of file diff --git a/libarchive/examples/baobab/generator/basic/generate_actor_module.vsh b/libarchive/examples/baobab/generator/basic/generate_actor_module.vsh deleted file mode 100755 index 758bc3f6..00000000 --- a/libarchive/examples/baobab/generator/basic/generate_actor_module.vsh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.baobab.generator -import incubaid.herolib.baobab.specification -import incubaid.herolib.schemas.openrpc -import os - -const example_dir = os.dir(@FILE) -const openrpc_spec_path = os.join_path(example_dir, 'openrpc.json') - -// the actor specification obtained from the OpenRPC Specification -openrpc_spec := openrpc.new(path: openrpc_spec_path)! -actor_spec := specification.from_openrpc(openrpc_spec)! - -actor_module := generator.generate_actor_module(actor_spec, - interfaces: [.openrpc] -)! - -actor_module.write(example_dir, - format: true - overwrite: true -)! diff --git a/libarchive/examples/baobab/generator/basic/generate_methods.vsh b/libarchive/examples/baobab/generator/basic/generate_methods.vsh deleted file mode 100755 index 1651138c..00000000 --- a/libarchive/examples/baobab/generator/basic/generate_methods.vsh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.baobab.generator -import incubaid.herolib.baobab.specification -import incubaid.herolib.schemas.openrpc -import os - -const example_dir = os.dir(@FILE) -const openrpc_spec_path = os.join_path(example_dir, 'openrpc.json') - -// the actor specification obtained from the OpenRPC Specification -openrpc_spec := openrpc.new(path: openrpc_spec_path)! -actor_spec := specification.from_openrpc(openrpc_spec)! - -methods_file := generator.generate_methods_file(actor_spec)! -methods_file.write(example_dir, - format: true - overwrite: true -)! diff --git a/libarchive/examples/baobab/generator/basic/generate_openrpc_file.vsh b/libarchive/examples/baobab/generator/basic/generate_openrpc_file.vsh deleted file mode 100755 index 7d142c18..00000000 --- a/libarchive/examples/baobab/generator/basic/generate_openrpc_file.vsh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.baobab.generator -import incubaid.herolib.baobab.specification -import incubaid.herolib.schemas.openrpc -import os - -const example_dir = os.dir(@FILE) -const openrpc_spec_path = os.join_path(example_dir, 'openrpc.json') - -// the actor specification obtained from the OpenRPC Specification -openrpc_spec_ := openrpc.new(path: openrpc_spec_path)! -actor_spec := specification.from_openrpc(openrpc_spec_)! -openrpc_spec := actor_spec.to_openrpc() - -openrpc_file := generator.generate_openrpc_file(openrpc_spec)! -openrpc_file.write(os.join_path(example_dir, 'docs'), - overwrite: true -)! diff --git a/libarchive/examples/baobab/generator/basic/openrpc.json b/libarchive/examples/baobab/generator/basic/openrpc.json deleted file mode 100644 index 2de851bb..00000000 --- a/libarchive/examples/baobab/generator/basic/openrpc.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "openrpc": "1.0.0", - "info": { - "title": "PetStore", - "version": "1.0.0" - }, - "methods": [ - { - "name": "GetPets", - "description": "finds pets in the system that the user has access to by tags and within a limit", - "params": [ - { - "name": "tags", - "description": "tags to filter by", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "limit", - "description": "maximum number of results to return", - "schema": { - "type": "integer" - } - } - ], - "result": { - "name": "pet_list", - "description": "all pets from the system, that mathes the tags", - "schema": { - "$ref": "#\/components\/schemas\/Pet" - } - } - }, - { - "name": "CreatePet", - "description": "creates a new pet in the store. Duplicates are allowed.", - "params": [ - { - "name": "new_pet", - "description": "Pet to add to the store.", - "schema": { - "$ref": "#\/components\/schemas\/NewPet" - } - } - ], - "result": { - "name": "pet", - "description": "the newly created pet", - "schema": { - "$ref": "#\/components\/schemas\/Pet" - } - } - }, - { - "name": "GetPetById", - "description": "gets a pet based on a single ID, if the user has access to the pet", - "params": [ - { - "name": "id", - "description": "ID of pet to fetch", - "schema": { - "type": "integer" - } - } - ], - "result": { - "name": "pet", - "description": "pet response", - "schema": { - "$ref": "#\/components\/schemas\/Pet" - } - } - }, - { - "name": "DeletePetById", - "description": "deletes a single pet based on the ID supplied", - "params": [ - { - "name": "id", - "description": "ID of pet to delete", - "schema": { - "type": "integer" - } - } - ], - "result": { - "name": "pet", - "description": "pet deleted", - "schema": { - "type": "null" - } - } - } - ], - "components": { - "schemas": { - "NewPet": { - "title": "NewPet", - "properties": { - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "Pet": { - "title": "Pet", - "description": "a pet struct that represents a pet", - "properties": { - "name": { - "description": "name of the pet", - "type": "string" - }, - "tag": { - "description": "a tag of the pet, helps finding pet", - "type": "string" - }, - "id": { - "description": "unique indentifier", - "type": "integer" - } - } - } - } - } - } \ No newline at end of file diff --git a/libarchive/examples/baobab/generator/geomind_poc/.gitignore b/libarchive/examples/baobab/generator/geomind_poc/.gitignore deleted file mode 100644 index f9b9a56e..00000000 --- a/libarchive/examples/baobab/generator/geomind_poc/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -merchant -profiler -farmer \ No newline at end of file diff --git a/libarchive/examples/baobab/generator/geomind_poc/farmer.json b/libarchive/examples/baobab/generator/geomind_poc/farmer.json deleted file mode 100644 index 5a500e9f..00000000 --- a/libarchive/examples/baobab/generator/geomind_poc/farmer.json +++ /dev/null @@ -1,344 +0,0 @@ -{ - "openapi": "3.0.1", - "info": { - "title": "Farmer", - "description": "API for managing farms and nodes, tracking rewards, capacity, and location.", - "version": "1.0.0" - }, - "servers": [ - { - "url": "http://localhost:8080", - "description": "Local development server" - } - ], - "components": { - "schemas": { - "Farm": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "example": "f47ac10b-58cc-4372-a567-0e02b2c3d479" - }, - "name": { - "type": "string", - "example": "Amsterdam Data Center" - }, - "description": { - "type": "string", - "example": "Enterprise-grade data center with renewable energy focus" - }, - "owner": { - "type": "string", - "example": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" - } - }, - "required": [ - "id", - "name", - "owner" - ] - }, - "Node": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "example": "n47ac10b-58cc-4372-a567-0e02b2c3d479" - }, - "description": { - "type": "string", - "example": "High-performance GPU compute node with 4x NVIDIA A100" - }, - "farm_id": { - "type": "string", - "format": "uuid", - "example": "f47ac10b-58cc-4372-a567-0e02b2c3d479" - }, - "location": { - "$ref": "#/components/schemas/Location" - }, - "capacity": { - "$ref": "#/components/schemas/Capacity" - }, - "grid_version": { - "type": "string", - "example": "3.16.2" - }, - "reward": { - "$ref": "#/components/schemas/Reward" - } - }, - "required": [ - "id", - "description", - "farm_id", - "location", - "capacity", - "reward" - ] - }, - "Location": { - "type": "object", - "properties": { - "coordinates": { - "type": "string", - "example": "52.3740, 4.8897" - }, - "continent": { - "type": "string", - "example": "Europe" - }, - "country": { - "type": "string", - "example": "Netherlands" - } - }, - "required": [ - "coordinates", - "continent", - "country" - ] - }, - "Capacity": { - "type": "object", - "properties": { - "cpu": { - "type": "integer", - "example": 128 - }, - "memory_gb": { - "type": "integer", - "example": 1024 - }, - "storage_tb": { - "type": "integer", - "example": 100 - } - }, - "required": [ - "cpu", - "memory_gb", - "storage_tb" - ] - }, - "Reward": { - "type": "object", - "properties": { - "reward_promised": { - "type": "number", - "format": "double", - "example": 25000.50 - }, - "reward_given": { - "type": "number", - "format": "double", - "example": 12500.25 - }, - "duration_months": { - "type": "integer", - "example": 36 - } - }, - "required": [ - "reward_promised", - "reward_given", - "duration_months" - ] - }, - "NodeStats": { - "type": "object", - "properties": { - "node_id": { - "type": "integer", - "format": "uint32", - "example": "42" - }, - "uptime_hours": { - "type": "integer", - "example": 8760 - }, - "bandwidth_gb": { - "type": "integer", - "example": 25000 - } - }, - "required": [ - "node_id", - "uptime_hours", - "bandwidth_gb" - ] - } - } - }, - "paths": { - "/farms": { - "get": { - "summary": "List all farms", - "operationId": "getFarms", - "responses": { - "200": { - "description": "List of farms", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Farm" - } - }, - "example": [ - { - "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", - "name": "Amsterdam Data Center", - "description": "Enterprise-grade data center with renewable energy focus", - "owner": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" - }, - { - "id": "d47ac10b-58cc-4372-a567-0e02b2c3d480", - "name": "Dubai Compute Hub", - "description": "High-density compute farm with advanced cooling", - "owner": "0x842d35Cc6634C0532925a3b844Bc454e4438f55f" - } - ] - } - } - } - } - } - }, - "/farms/{farmId}/nodes": { - "get": { - "summary": "List nodes in a farm", - "operationId": "getNodesByFarm", - "parameters": [ - { - "name": "farmId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "example": "f47ac10b-58cc-4372-a567-0e02b2c3d479" - } - ], - "responses": { - "200": { - "description": "List of nodes in the farm", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Node" - } - }, - "example": [ - { - "id": "n47ac10b-58cc-4372-a567-0e02b2c3d479", - "description": "High-performance GPU compute node with 4x NVIDIA A100", - "farm_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", - "location": { - "coordinates": "52.3740, 4.8897", - "continent": "Europe", - "country": "Netherlands" - }, - "capacity": { - "cpu": 128, - "memory_gb": 1024, - "storage_tb": 100 - }, - "grid_version": "3.16.2", - "reward": { - "reward_promised": 25000.50, - "reward_given": 12500.25, - "duration_months": 36 - } - } - ] - } - } - }, - "404": { - "description": "Farm not found", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "example": 404 - }, - "message": { - "type": "string", - "example": "Farm with ID f47ac10b-58cc-4372-a567-0e02b2c3d479 not found" - } - } - } - } - } - } - } - } - }, - "/nodes/{nodeId}/stats": { - "get": { - "summary": "Get node statistics", - "operationId": "getNodeStats", - "parameters": [ - { - "name": "nodeId", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "uint32" - }, - "example": "42" - } - ], - "responses": { - "200": { - "description": "Node statistics", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NodeStats" - }, - "example": { - "node_id": "42", - "uptime_hours": 8760, - "bandwidth_gb": 25000 - } - } - } - }, - "404": { - "description": "Node not found", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "example": 404 - }, - "message": { - "type": "string", - "example": "Node with ID n47ac10b-58cc-4372-a567-0e02b2c3d479 not found" - } - } - } - } - } - } - } - } - } - } -} \ No newline at end of file diff --git a/libarchive/examples/baobab/generator/geomind_poc/generate.vsh b/libarchive/examples/baobab/generator/geomind_poc/generate.vsh deleted file mode 100755 index f57cccb4..00000000 --- a/libarchive/examples/baobab/generator/geomind_poc/generate.vsh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.baobab.generator -import incubaid.herolib.baobab.specification -import incubaid.herolib.schemas.openapi -import os - -const example_dir = os.dir(@FILE) -const specs = ['merchant', 'profiler', 'farmer'] - -for spec in specs { - openapi_spec_path := os.join_path(example_dir, '${spec}.json') - openapi_spec := openapi.new(path: openapi_spec_path, process: true)! - actor_spec := specification.from_openapi(openapi_spec)! - actor_module := generator.generate_actor_folder(actor_spec, - interfaces: [.openapi, .http] - )! - actor_module.write(example_dir, - format: true - overwrite: true - compile: false - )! -} diff --git a/libarchive/examples/baobab/generator/geomind_poc/merchant.json b/libarchive/examples/baobab/generator/geomind_poc/merchant.json deleted file mode 100644 index 56b4b17c..00000000 --- a/libarchive/examples/baobab/generator/geomind_poc/merchant.json +++ /dev/null @@ -1,997 +0,0 @@ -{ - "openapi": "3.0.1", - "info": { - "title": "Merchant", - "description": "API for e-commerce operations including stores, products, and orders", - "version": "1.0.0" - }, - "servers": [{ - "url": "http://localhost:8080", - "description": "Local development server" - },{ - "url": "http://localhost:8080/openapi/example", - "description": "Local example server" - }], - "components": { - "schemas": { - "Store": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "example": "123e4567-e89b-12d3-a456-426614174000" - }, - "name": { - "type": "string", - "example": "Tech Gadgets Store" - }, - "description": { - "type": "string", - "example": "Premium electronics and gadgets retailer" - }, - "contact": { - "type": "string", - "example": "contact@techgadgets.com" - }, - "active": { - "type": "boolean", - "example": true - } - }, - "required": [ - "id", - "name", - "contact", - "active" - ] - }, - "ProductComponentTemplate": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "example": "123e4567-e89b-12d3-a456-426614174001" - }, - "name": { - "type": "string", - "example": "4K Display Panel" - }, - "description": { - "type": "string", - "example": "55-inch 4K UHD Display Panel" - }, - "specs": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "example": { - "resolution": "3840x2160", - "refreshRate": "120Hz", - "panel_type": "OLED" - } - }, - "price": { - "type": "number", - "format": "double", - "example": 599.99 - }, - "currency": { - "type": "string", - "pattern": "^[A-Z]{3}$", - "example": "USD" - } - }, - "required": [ - "id", - "name", - "price", - "currency" - ] - }, - "ProductTemplate": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "example": "123e4567-e89b-12d3-a456-426614174002" - }, - "name": { - "type": "string", - "example": "Smart TV 55-inch" - }, - "description": { - "type": "string", - "example": "55-inch Smart TV with 4K Display" - }, - "components": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProductComponentTemplate" - }, - "example": [ - { - "id": "123e4567-e89b-12d3-a456-426614174001", - "name": "4K Display Panel", - "description": "55-inch 4K UHD Display Panel", - "specs": { - "resolution": "3840x2160", - "refreshRate": "120Hz" - }, - "price": 599.99, - "currency": "USD" - } - ] - }, - "store_id": { - "type": "string", - "format": "uuid", - "example": "123e4567-e89b-12d3-a456-426614174000" - }, - "category": { - "type": "string", - "example": "Electronics" - }, - "active": { - "type": "boolean", - "example": true - } - }, - "required": [ - "id", - "name", - "components", - "store_id", - "active" - ] - }, - "Product": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "example": "123e4567-e89b-12d3-a456-426614174003" - }, - "template_id": { - "type": "string", - "format": "uuid", - "example": "123e4567-e89b-12d3-a456-426614174002" - }, - "name": { - "type": "string", - "example": "Smart TV 55-inch" - }, - "description": { - "type": "string", - "example": "55-inch Smart TV with 4K Display" - }, - "price": { - "type": "number", - "format": "double", - "example": 899.99 - }, - "currency": { - "type": "string", - "pattern": "^[A-Z]{3}$", - "example": "USD" - }, - "store_id": { - "type": "string", - "format": "uuid", - "example": "123e4567-e89b-12d3-a456-426614174000" - }, - "stock_quantity": { - "type": "integer", - "minimum": 0, - "example": 50 - }, - "available": { - "type": "boolean", - "example": true - } - }, - "required": [ - "id", - "template_id", - "name", - "price", - "currency", - "store_id", - "stock_quantity", - "available" - ] - }, - "OrderItem": { - "type": "object", - "properties": { - "product_id": { - "type": "string", - "format": "uuid", - "example": "123e4567-e89b-12d3-a456-426614174003" - }, - "quantity": { - "type": "integer", - "minimum": 1, - "example": 2 - }, - "price": { - "type": "number", - "format": "double", - "example": 899.99 - }, - "currency": { - "type": "string", - "pattern": "^[A-Z]{3}$", - "example": "USD" - } - }, - "required": [ - "product_id", - "quantity", - "price", - "currency" - ] - }, - "Order": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "example": "123e4567-e89b-12d3-a456-426614174004" - }, - "customer_id": { - "type": "string", - "format": "uuid", - "example": "123e4567-e89b-12d3-a456-426614174005" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/OrderItem" - }, - "example": [ - { - "product_id": "123e4567-e89b-12d3-a456-426614174003", - "quantity": 2, - "price": 899.99, - "currency": "USD" - } - ] - }, - "total_amount": { - "type": "number", - "format": "double", - "example": 1799.98 - }, - "currency": { - "type": "string", - "pattern": "^[A-Z]{3}$", - "example": "USD" - }, - "status": { - "type": "string", - "enum": [ - "pending", - "confirmed", - "shipped", - "delivered" - ], - "example": "pending" - }, - "created_at": { - "type": "string", - "format": "date-time", - "example": "2024-02-10T10:30:00Z" - }, - "updated_at": { - "type": "string", - "format": "date-time", - "example": "2024-02-10T10:30:00Z" - } - }, - "required": [ - "id", - "customer_id", - "items", - "total_amount", - "currency", - "status", - "created_at", - "updated_at" - ] - }, - "Error": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "example": 404 - }, - "message": { - "type": "string", - "example": "Resource not found" - } - }, - "required": [ - "code", - "message" - ] - } - } - }, - "paths": { - "/stores": { - "post": { - "summary": "Create a new store", - "operationId": "createStore", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "example": "Tech Gadgets Store" - }, - "description": { - "type": "string", - "example": "Premium electronics and gadgets retailer" - }, - "contact": { - "type": "string", - "example": "contact@techgadgets.com" - } - }, - "required": [ - "name", - "contact" - ] - }, - "examples": { - "newStore": { - "summary": "Create a new electronics store", - "value": { - "name": "Tech Gadgets Store", - "description": "Premium electronics and gadgets retailer", - "contact": "contact@techgadgets.com" - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Store created successfully", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Store" - }, - "example": { - "id": "123e4567-e89b-12d3-a456-426614174000", - "name": "Tech Gadgets Store", - "description": "Premium electronics and gadgets retailer", - "contact": "contact@techgadgets.com", - "active": true - } - } - } - }, - "400": { - "description": "Invalid input", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "example": { - "code": 400, - "message": "Invalid store data provided" - } - } - } - } - } - } - }, - "/products/templates/components": { - "post": { - "summary": "Create a new product component template", - "operationId": "createProductComponentTemplate", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "specs": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "price": { - "type": "number" - }, - "currency": { - "type": "string" - } - }, - "required": [ - "name", - "price", - "currency" - ] - }, - "examples": { - "displayPanel": { - "summary": "Create a display panel component", - "value": { - "name": "4K Display Panel", - "description": "55-inch 4K UHD Display Panel", - "specs": { - "resolution": "3840x2160", - "refreshRate": "120Hz", - "panel_type": "OLED" - }, - "price": 599.99, - "currency": "USD" - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Component template created successfully", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProductComponentTemplate" - }, - "example": { - "id": "123e4567-e89b-12d3-a456-426614174001", - "name": "4K Display Panel", - "description": "55-inch 4K UHD Display Panel", - "specs": { - "resolution": "3840x2160", - "refreshRate": "120Hz", - "panel_type": "OLED" - }, - "price": 599.99, - "currency": "USD" - } - } - } - }, - "400": { - "description": "Invalid input", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "example": { - "code": 400, - "message": "Invalid component template data" - } - } - } - } - } - } - }, - "/products/templates": { - "post": { - "summary": "Create a new product template", - "operationId": "createProductTemplate", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "components": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - }, - "store_id": { - "type": "string", - "format": "uuid" - }, - "category": { - "type": "string" - } - }, - "required": [ - "name", - "components", - "store_id" - ] - }, - "examples": { - "smartTV": { - "summary": "Create a Smart TV template", - "value": { - "name": "Smart TV 55-inch", - "description": "55-inch Smart TV with 4K Display", - "components": [ - "123e4567-e89b-12d3-a456-426614174001" - ], - "store_id": "123e4567-e89b-12d3-a456-426614174000", - "category": "Electronics" - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Product template created successfully", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProductTemplate" - }, - "example": { - "id": "123e4567-e89b-12d3-a456-426614174002", - "name": "Smart TV 55-inch", - "description": "55-inch Smart TV with 4K Display", - "components": [ - { - "id": "123e4567-e89b-12d3-a456-426614174001", - "name": "4K Display Panel", - "description": "55-inch 4K UHD Display Panel", - "specs": { - "resolution": "3840x2160", - "refreshRate": "120Hz" - }, - "price": 599.99, - "currency": "USD" - } - ], - "store_id": "123e4567-e89b-12d3-a456-426614174000", - "category": "Electronics", - "active": true - } - } - } - }, - "404": { - "description": "Store not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "example": { - "code": 404, - "message": "Store not found" - } - } - } - } - } - } - }, - "/products": { - "post": { - "summary": "Create a new product from template", - "operationId": "createProduct", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "template_id": { - "type": "string", - "format": "uuid" - }, - "store_id": { - "type": "string", - "format": "uuid" - }, - "stock_quantity": { - "type": "integer", - "minimum": 0 - } - }, - "required": [ - "template_id", - "store_id", - "stock_quantity" - ] - }, - "examples": { - "newProduct": { - "summary": "Create a new Smart TV product", - "value": { - "template_id": "123e4567-e89b-12d3-a456-426614174002", - "store_id": "123e4567-e89b-12d3-a456-426614174000", - "stock_quantity": 50 - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Product created successfully", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Product" - }, - "example": { - "id": "123e4567-e89b-12d3-a456-426614174003", - "template_id": "123e4567-e89b-12d3-a456-426614174002", - "name": "Smart TV 55-inch", - "description": "55-inch Smart TV with 4K Display", - "price": 899.99, - "currency": "USD", - "store_id": "123e4567-e89b-12d3-a456-426614174000", - "stock_quantity": 50, - "available": true - } - } - } - }, - "404": { - "description": "Template or store not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "example": { - "code": 404, - "message": "Product template not found" - } - } - } - } - } - } - }, - "/orders": { - "post": { - "summary": "Create a new order", - "operationId": "createOrder", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "customer_id": { - "type": "string", - "format": "uuid" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/OrderItem" - } - } - }, - "required": [ - "customer_id", - "items" - ] - }, - "examples": { - "newOrder": { - "summary": "Create an order for two Smart TVs", - "value": { - "customer_id": "123e4567-e89b-12d3-a456-426614174005", - "items": [ - { - "product_id": "123e4567-e89b-12d3-a456-426614174003", - "quantity": 2, - "price": 899.99, - "currency": "USD" - } - ] - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Order created successfully", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Order" - }, - "example": { - "id": "123e4567-e89b-12d3-a456-426614174004", - "customer_id": "123e4567-e89b-12d3-a456-426614174005", - "items": [ - { - "product_id": "123e4567-e89b-12d3-a456-426614174003", - "quantity": 2, - "price": 899.99, - "currency": "USD" - } - ], - "total_amount": 1799.98, - "currency": "USD", - "status": "pending", - "created_at": "2024-02-10T10:30:00Z", - "updated_at": "2024-02-10T10:30:00Z" - } - } - } - }, - "400": { - "description": "Invalid input or insufficient stock", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "example": { - "code": 400, - "message": "Insufficient stock for product" - } - } - } - } - } - } - }, - "/orders/{orderId}/status": { - "put": { - "summary": "Update order status", - "operationId": "updateOrderStatus", - "parameters": [ - { - "name": "orderId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "example": "123e4567-e89b-12d3-a456-426614174004" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "pending", - "confirmed", - "shipped", - "delivered" - ] - } - }, - "required": [ - "status" - ] - }, - "examples": { - "updateStatus": { - "summary": "Update order to shipped status", - "value": { - "status": "shipped" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Order status updated successfully", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Order" - }, - "example": { - "id": "123e4567-e89b-12d3-a456-426614174004", - "customer_id": "123e4567-e89b-12d3-a456-426614174005", - "items": [ - { - "product_id": "123e4567-e89b-12d3-a456-426614174003", - "quantity": 2, - "price": 899.99, - "currency": "USD" - } - ], - "total_amount": 1799.98, - "currency": "USD", - "status": "shipped", - "created_at": "2024-02-10T10:30:00Z", - "updated_at": "2024-02-10T10:35:00Z" - } - } - } - }, - "404": { - "description": "Order not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "example": { - "code": 404, - "message": "Order not found" - } - } - } - } - } - } - }, - "/stores/{storeId}/products": { - "get": { - "summary": "Get all products for a store", - "operationId": "getStoreProducts", - "parameters": [ - { - "name": "storeId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "example": "123e4567-e89b-12d3-a456-426614174000" - } - ], - "responses": { - "200": { - "description": "List of store's products", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Product" - } - }, - "example": [ - { - "id": "123e4567-e89b-12d3-a456-426614174003", - "template_id": "123e4567-e89b-12d3-a456-426614174002", - "name": "Smart TV 55-inch", - "description": "55-inch Smart TV with 4K Display", - "price": 899.99, - "currency": "USD", - "store_id": "123e4567-e89b-12d3-a456-426614174000", - "stock_quantity": 48, - "available": true - } - ] - } - } - }, - "404": { - "description": "Store not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "example": { - "code": 404, - "message": "Store not found" - } - } - } - } - } - } - }, - "/stores/{storeId}/orders": { - "get": { - "summary": "Get all orders for a store's products", - "operationId": "getStoreOrders", - "parameters": [ - { - "name": "storeId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "example": "123e4567-e89b-12d3-a456-426614174000" - } - ], - "responses": { - "200": { - "description": "List of orders containing store's products", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Order" - } - }, - "example": [ - { - "id": "123e4567-e89b-12d3-a456-426614174004", - "customer_id": "123e4567-e89b-12d3-a456-426614174005", - "items": [ - { - "product_id": "123e4567-e89b-12d3-a456-426614174003", - "quantity": 2, - "price": 899.99, - "currency": "USD" - } - ], - "total_amount": 1799.98, - "currency": "USD", - "status": "shipped", - "created_at": "2024-02-10T10:30:00Z", - "updated_at": "2024-02-10T10:35:00Z" - } - ] - } - } - }, - "404": { - "description": "Store not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "example": { - "code": 404, - "message": "Store not found" - } - } - } - } - } - } - } - } -} \ No newline at end of file diff --git a/libarchive/examples/baobab/generator/geomind_poc/model.v b/libarchive/examples/baobab/generator/geomind_poc/model.v deleted file mode 100644 index 4a047495..00000000 --- a/libarchive/examples/baobab/generator/geomind_poc/model.v +++ /dev/null @@ -1,81 +0,0 @@ -module geomind_poc - -pub struct Merchant { -pub: - id string - name string - description string - contact string - active bool -} - -pub struct ProductComponentTemplate { -pub: - id string - name string - description string - // technical specifications - specs map[string]string - // price per unit - price f64 - // currency code (e.g., 'USD', 'EUR') - currency string -} - -pub struct ProductTemplate { -pub: - id string - name string - description string - // components that make up this product template - components []ProductComponentTemplate - // merchant who created this template - merchant_id string - // category of the product (e.g., 'electronics', 'clothing') - category string - // whether this template is available for use - active bool -} - -pub struct Product { -pub: - id string - template_id string - // specific instance details that may differ from template - name string - description string - // actual price of this product instance - price f64 - currency string - // merchant selling this product - merchant_id string - // current stock level - stock_quantity int - // whether this product is available for purchase - available bool -} - -pub struct OrderItem { -pub: - product_id string - quantity int - price f64 - currency string -} - -pub struct Order { -pub: - id string - // customer identifier - customer_id string - // items in the order - items []OrderItem - // total order amount - total_amount f64 - currency string - // order status (e.g., 'pending', 'confirmed', 'shipped', 'delivered') - status string - // timestamps - created_at string - updated_at string -} diff --git a/libarchive/examples/baobab/generator/geomind_poc/play.v b/libarchive/examples/baobab/generator/geomind_poc/play.v deleted file mode 100644 index 5eb97c85..00000000 --- a/libarchive/examples/baobab/generator/geomind_poc/play.v +++ /dev/null @@ -1,148 +0,0 @@ -module geomind_poc - -import incubaid.crystallib.core.playbook { PlayBook } - -// play_commerce processes heroscript actions for the commerce system -pub fn play_commerce(mut plbook PlayBook) ! { - commerce_actions := plbook.find(filter: 'commerce.')! - mut c := Commerce{} - - for action in commerce_actions { - match action.name { - 'merchant' { - mut p := action.params - merchant := c.create_merchant( - name: p.get('name')! - description: p.get_default('description', '')! - contact: p.get('contact')! - )! - println('Created merchant: ${merchant.name}') - } - 'component' { - mut p := action.params - component := c.create_product_component_template( - name: p.get('name')! - description: p.get_default('description', '')! - specs: p.get_map() - price: p.get_float('price')! - currency: p.get('currency')! - )! - println('Created component: ${component.name}') - } - 'template' { - mut p := action.params - // Get component IDs as a list - component_ids := p.get_list('components')! - // Convert component IDs to actual components - mut components := []ProductComponentTemplate{} - for id in component_ids { - // In a real implementation, you would fetch the component from storage - // For this example, we create a dummy component - component := ProductComponentTemplate{ - id: id - name: 'Component' - description: '' - specs: map[string]string{} - price: 0 - currency: 'USD' - } - components << component - } - - template := c.create_product_template( - name: p.get('name')! - description: p.get_default('description', '')! - components: components - merchant_id: p.get('merchant_id')! - category: p.get_default('category', 'General')! - )! - println('Created template: ${template.name}') - } - 'product' { - mut p := action.params - product := c.create_product( - template_id: p.get('template_id')! - merchant_id: p.get('merchant_id')! - stock_quantity: p.get_int('stock_quantity')! - )! - println('Created product: ${product.name} with stock: ${product.stock_quantity}') - } - 'order' { - mut p := action.params - // Get order items as a list of maps - items_data := p.get_list('items')! - mut items := []OrderItem{} - for item_data in items_data { - // Parse item data (format: "product_id:quantity:price:currency") - parts := item_data.split(':') - if parts.len != 4 { - return error('Invalid order item format: ${item_data}') - } - item := OrderItem{ - product_id: parts[0] - quantity: parts[1].int() - price: parts[2].f64() - currency: parts[3] - } - items << item - } - - order := c.create_order( - customer_id: p.get('customer_id')! - items: items - )! - println('Created order: ${order.id} with ${order.items.len} items') - } - 'update_order' { - mut p := action.params - order := c.update_order_status( - order_id: p.get('order_id')! - new_status: p.get('status')! - )! - println('Updated order ${order.id} status to: ${order.status}') - } - else { - return error('Unknown commerce action: ${action.name}') - } - } - } -} - -// Example heroscript usage: -/* -!!commerce.merchant - name: "Tech Gadgets Store" - description: "Premium electronics and gadgets retailer" - contact: "contact@techgadgets.com" - -!!commerce.component - name: "4K Display Panel" - description: "55-inch 4K UHD Display Panel" - specs: - resolution: "3840x2160" - refreshRate: "120Hz" - panel_type: "OLED" - price: 599.99 - currency: "USD" - -!!commerce.template - name: "Smart TV 55-inch" - description: "55-inch Smart TV with 4K Display" - components: "123e4567-e89b-12d3-a456-426614174001" - merchant_id: "123e4567-e89b-12d3-a456-426614174000" - category: "Electronics" - -!!commerce.product - template_id: "123e4567-e89b-12d3-a456-426614174002" - merchant_id: "123e4567-e89b-12d3-a456-426614174000" - stock_quantity: 50 - -!!commerce.order - customer_id: "123e4567-e89b-12d3-a456-426614174005" - items: - - "123e4567-e89b-12d3-a456-426614174003:2:899.99:USD" - -!!commerce.update_order - order_id: "123e4567-e89b-12d3-a456-426614174004" - status: "shipped" -*/ diff --git a/libarchive/examples/baobab/generator/geomind_poc/profiler.json b/libarchive/examples/baobab/generator/geomind_poc/profiler.json deleted file mode 100644 index 0c7e2565..00000000 --- a/libarchive/examples/baobab/generator/geomind_poc/profiler.json +++ /dev/null @@ -1,286 +0,0 @@ -{ - "openapi": "3.0.1", - "info": { - "title": "Profiler", - "description": "API for managing user profiles with name, public key, and KYC verification", - "version": "1.0.0" - }, - "servers": [ - { - "url": "http://localhost:8080", - "description": "Local development server" - } - ], - "components": { - "schemas": { - "Profile": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "example": "123e4567-e89b-12d3-a456-426614174000" - }, - "name": { - "type": "string", - "example": "Alice Doe" - }, - "public_key": { - "type": "string", - "example": "028a8f8b59f7283a47f9f6d4bc8176e847ad2b6c6d8bdfd041e5e7f3b4ac28c9fc" - }, - "kyc_verified": { - "type": "boolean", - "example": false - } - }, - "required": ["id", "name", "public_key", "kyc_verified"] - }, - "Error": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "example": 400 - }, - "message": { - "type": "string", - "example": "Invalid request" - } - }, - "required": ["code", "message"] - } - } - }, - "paths": { - "/profiles": { - "post": { - "summary": "Create a new profile", - "operationId": "createProfile", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "example": "Bob Smith" - }, - "public_key": { - "type": "string", - "example": "03a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2" - } - }, - "required": ["name", "public_key"] - }, - "examples": { - "newProfile": { - "summary": "Example of creating a new profile", - "value": { - "name": "Bob Smith", - "public_key": "03a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2" - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Profile created successfully", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Profile" - }, - "examples": { - "successResponse": { - "summary": "Example of successful profile creation", - "value": { - "id": "550e8400-e29b-41d4-a716-446655440000", - "name": "Bob Smith", - "public_key": "03a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2", - "kyc_verified": false - } - } - } - } - } - }, - "400": { - "description": "Invalid input", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "invalidInput": { - "summary": "Example of invalid input error", - "value": { - "code": 400, - "message": "Invalid public key format" - } - } - } - } - } - } - } - } - }, - "/profiles/{profileId}": { - "get": { - "summary": "Get profile details", - "operationId": "getProfile", - "parameters": [ - { - "name": "profileId", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "uint32" - }, - "example": "42" - } - ], - "responses": { - "200": { - "description": "Profile retrieved successfully", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Profile" - }, - "examples": { - "existingProfile": { - "summary": "Example of retrieved profile", - "value": { - "id": "550e8400-e29b-41d4-a716-446655440000", - "name": "Bob Smith", - "public_key": "03a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2", - "kyc_verified": true - } - } - } - } - } - }, - "404": { - "description": "Profile not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "notFound": { - "summary": "Example of profile not found error", - "value": { - "code": 404, - "message": "Profile with ID '550e8400-e29b-41d4-a716-446655440000' not found" - } - } - } - } - } - } - } - } - }, - "/profiles/{profileId}/kyc": { - "put": { - "summary": "Update KYC verification status", - "operationId": "updateKYCStatus", - "parameters": [ - { - "name": "profileId", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "uint32" - }, - "example": "42" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "kyc_verified": { - "type": "boolean", - "example": true - } - }, - "required": ["kyc_verified"] - }, - "examples": { - "verifyKYC": { - "summary": "Example of verifying KYC", - "value": { - "kyc_verified": true - } - }, - "unverifyKYC": { - "summary": "Example of unverifying KYC", - "value": { - "kyc_verified": false - } - } - } - } - } - }, - "responses": { - "200": { - "description": "KYC status updated successfully", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Profile" - }, - "examples": { - "updatedProfile": { - "summary": "Example of profile with updated KYC status", - "value": { - "id": "550e8400-e29b-41d4-a716-446655440000", - "name": "Bob Smith", - "public_key": "03a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2", - "kyc_verified": true - } - } - } - } - } - }, - "404": { - "description": "Profile not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "notFound": { - "summary": "Example of profile not found error", - "value": { - "code": 404, - "message": "Profile with ID '550e8400-e29b-41d4-a716-446655440000' not found" - } - } - } - } - } - } - } - } - } - } - } diff --git a/libarchive/examples/baobab/generator/geomind_poc/server.v b/libarchive/examples/baobab/generator/geomind_poc/server.v deleted file mode 100644 index 6c67984b..00000000 --- a/libarchive/examples/baobab/generator/geomind_poc/server.v +++ /dev/null @@ -1,191 +0,0 @@ -module geomind_poc - -import crypto.rand -import time - -// Commerce represents the main e-commerce server handling all operations -pub struct Commerce { -mut: - merchants map[string]Merchant - templates map[string]ProductTemplate - products map[string]Product - orders map[string]Order -} - -// generate_id creates a unique identifier -fn generate_id() string { - return rand.uuid_v4() -} - -// create_merchant adds a new merchant to the system -pub fn (mut c Commerce) create_merchant(name string, description string, contact string) !Merchant { - merchant_id := generate_id() - merchant := Merchant{ - id: merchant_id - name: name - description: description - contact: contact - active: true - } - c.merchants[merchant_id] = merchant - return merchant -} - -// create_product_component_template creates a new component template -pub fn (mut c Commerce) create_product_component_template(name string, description string, specs map[string]string, price f64, currency string) !ProductComponentTemplate { - component := ProductComponentTemplate{ - id: generate_id() - name: name - description: description - specs: specs - price: price - currency: currency - } - return component -} - -// create_product_template creates a new product template -pub fn (mut c Commerce) create_product_template(name string, description string, components []ProductComponentTemplate, merchant_id string, category string) !ProductTemplate { - if merchant_id !in c.merchants { - return error('Merchant not found') - } - - template := ProductTemplate{ - id: generate_id() - name: name - description: description - components: components - merchant_id: merchant_id - category: category - active: true - } - c.templates[template.id] = template - return template -} - -// create_product creates a new product instance from a template -pub fn (mut c Commerce) create_product(template_id string, merchant_id string, stock_quantity int) !Product { - if template_id !in c.templates { - return error('Template not found') - } - if merchant_id !in c.merchants { - return error('Merchant not found') - } - - template := c.templates[template_id] - mut total_price := 0.0 - for component in template.components { - total_price += component.price - } - - product := Product{ - id: generate_id() - template_id: template_id - name: template.name - description: template.description - price: total_price - currency: template.components[0].currency // assuming all components use same currency - merchant_id: merchant_id - stock_quantity: stock_quantity - available: true - } - c.products[product.id] = product - return product -} - -// create_order creates a new order -pub fn (mut c Commerce) create_order(customer_id string, items []OrderItem) !Order { - mut total_amount := 0.0 - mut currency := '' - - for item in items { - if item.product_id !in c.products { - return error('Product not found: ${item.product_id}') - } - product := c.products[item.product_id] - if !product.available || product.stock_quantity < item.quantity { - return error('Product ${product.name} is not available in requested quantity') - } - total_amount += item.price * item.quantity - if currency == '' { - currency = item.currency - } else if currency != item.currency { - return error('Mixed currencies are not supported') - } - } - - order := Order{ - id: generate_id() - customer_id: customer_id - items: items - total_amount: total_amount - currency: currency - status: 'pending' - created_at: time.now().str() - updated_at: time.now().str() - } - c.orders[order.id] = order - - // Update stock quantities - for item in items { - mut product := c.products[item.product_id] - product.stock_quantity -= item.quantity - if product.stock_quantity == 0 { - product.available = false - } - c.products[item.product_id] = product - } - - return order -} - -// update_order_status updates the status of an order -pub fn (mut c Commerce) update_order_status(order_id string, new_status string) !Order { - if order_id !in c.orders { - return error('Order not found') - } - - mut order := c.orders[order_id] - order.status = new_status - order.updated_at = time.now().str() - c.orders[order_id] = order - return order -} - -// get_merchant_products returns all products for a given merchant -pub fn (c Commerce) get_merchant_products(merchant_id string) ![]Product { - if merchant_id !in c.merchants { - return error('Merchant not found') - } - - mut products := []Product{} - for product in c.products.values() { - if product.merchant_id == merchant_id { - products << product - } - } - return products -} - -// get_merchant_orders returns all orders for products sold by a merchant -pub fn (c Commerce) get_merchant_orders(merchant_id string) ![]Order { - if merchant_id !in c.merchants { - return error('Merchant not found') - } - - mut orders := []Order{} - for order in c.orders.values() { - mut includes_merchant := false - for item in order.items { - product := c.products[item.product_id] - if product.merchant_id == merchant_id { - includes_merchant = true - break - } - } - if includes_merchant { - orders << order - } - } - return orders -} diff --git a/libarchive/examples/baobab/generator/geomind_poc/specs.md b/libarchive/examples/baobab/generator/geomind_poc/specs.md deleted file mode 100644 index 1bc5a4e9..00000000 --- a/libarchive/examples/baobab/generator/geomind_poc/specs.md +++ /dev/null @@ -1,57 +0,0 @@ - - -- profile management - - my name - - my pub key - - kyc - - ... -- product has components -- admin items - - supported_currencies - - countries - - continents -- farming - - farms - - default farm exists, users don't have to chose - - name - - description - - owner (pubkey) - - nodes - - reward (nr of INCA per month and time e.g. 24 months) - - reward_promised - - reward_given - - location - - coordinates - - continent - - country - - description - - farmid - - capacity (disks, mem, ...) - - gridversion (eg. 3.16) - - nodestats - - ... - - uptime - - bandwidth -- referral system -- coupons for discounts (one product can have multiple coupons and discounts) -- data gets imported with heroscript for what we sell -- minimal wallet function (BTC, CHF, MGLD, TFT, INCA) - - transactions, so they can see what they spend money on - - transfer/exchange -- basic communication (messages in/out) - - to allow us to communicate with user -- news - - basic news feed with topics, which we can set -- vdc - - name - - description (optional) - - spendinglimit - - currency per month, week or day e.g. 0.1 BTC/month - - each spending limit has name - - admins, list of pubkeys who have access to this and can add capacity to it, or delete, ... -- deployment - - deploymentid - - vdcid - - heroscript - - status - - links (name, link, description, category) \ No newline at end of file diff --git a/libarchive/examples/baobab/generator/geomind_poc/test_commerce.vsh b/libarchive/examples/baobab/generator/geomind_poc/test_commerce.vsh deleted file mode 100644 index 20c58bdf..00000000 --- a/libarchive/examples/baobab/generator/geomind_poc/test_commerce.vsh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env -S v - -import incubaid.crystallib.core.playbook -import geomind_poc - -fn main() { - test_script := " -!!commerce.merchant - name: 'Tech Gadgets Store' - description: 'Premium electronics and gadgets retailer' - contact: 'contact@techgadgets.com' - -!!commerce.component - name: '4K Display Panel' - description: '55-inch 4K UHD Display Panel' - specs: - resolution: '3840x2160' - refreshRate: '120Hz' - panel_type: 'OLED' - price: 599.99 - currency: 'USD' - -!!commerce.template - name: 'Smart TV 55-inch' - description: '55-inch Smart TV with 4K Display' - components: '123e4567-e89b-12d3-a456-426614174001' - merchant_id: '123e4567-e89b-12d3-a456-426614174000' - category: 'Electronics' - -!!commerce.product - template_id: '123e4567-e89b-12d3-a456-426614174002' - merchant_id: '123e4567-e89b-12d3-a456-426614174000' - stock_quantity: 50 - -!!commerce.order - customer_id: '123e4567-e89b-12d3-a456-426614174005' - items: - - '123e4567-e89b-12d3-a456-426614174003:2:899.99:USD' - -!!commerce.update_order - order_id: '123e4567-e89b-12d3-a456-426614174004' - status: 'shipped' -" - - mut plbook := playbook.new(text: test_script)! - geomind_poc.play_commerce(mut plbook)! -} diff --git a/libarchive/examples/baobab/generator/mcc_example.vsh b/libarchive/examples/baobab/generator/mcc_example.vsh deleted file mode 100755 index b7b67dcc..00000000 --- a/libarchive/examples/baobab/generator/mcc_example.vsh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.baobab.generator -import incubaid.herolib.baobab.specification -import incubaid.herolib.schemas.openapi -import os - -const example_dir = os.join_path('${os.home_dir()}/code/github/incubaid/herolib/lib/circles/mcc', - 'baobab') -const openapi_spec_path = os.join_path('${os.home_dir()}/code/github/incubaid/herolib/lib/circles/mcc', - 'openapi.json') - -// the actor specification obtained from the OpenRPC Specification -openapi_spec := openapi.new(path: openapi_spec_path)! -actor_spec := specification.from_openapi(openapi_spec)! - -actor_module := generator.generate_actor_module(actor_spec, - interfaces: [.openapi, .http] -)! - -actor_module.write(example_dir, - format: true - overwrite: true - compile: false -)! diff --git a/libarchive/examples/baobab/generator/openapi_e2e/.gitignore b/libarchive/examples/baobab/generator/openapi_e2e/.gitignore deleted file mode 100644 index 8d6fa828..00000000 --- a/libarchive/examples/baobab/generator/openapi_e2e/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -methods.v -meeting_scheduler_actor -generate_actor_module -src \ No newline at end of file diff --git a/libarchive/examples/baobab/generator/openapi_e2e/generate_actor_module.vsh b/libarchive/examples/baobab/generator/openapi_e2e/generate_actor_module.vsh deleted file mode 100755 index bcd0b693..00000000 --- a/libarchive/examples/baobab/generator/openapi_e2e/generate_actor_module.vsh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.baobab.generator -import incubaid.herolib.baobab.specification -import incubaid.herolib.schemas.openapi -import os - -const example_dir = os.dir(@FILE) -const openapi_spec_path = os.join_path(example_dir, 'openapi.json') - -// the actor specification obtained from the OpenRPC Specification -openapi_spec := openapi.new(path: openapi_spec_path)! -actor_spec := specification.from_openapi(openapi_spec)! - -println(actor_spec) - -// actor_module := generator.generate_actor_module(actor_spec, -// interfaces: [.openapi, .http] -// )! - -actor_module := generator.generate_actor_module(actor_spec, - interfaces: [.http] -)! - -actor_module.write(example_dir, - format: true - overwrite: true - compile: false -)! - -// os.execvp('bash', ['${example_dir}/meeting_scheduler_actor/scripts/run.sh'])! diff --git a/libarchive/examples/baobab/generator/openapi_e2e/openapi.json b/libarchive/examples/baobab/generator/openapi_e2e/openapi.json deleted file mode 100644 index bea4a619..00000000 --- a/libarchive/examples/baobab/generator/openapi_e2e/openapi.json +++ /dev/null @@ -1,311 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Meeting Scheduler", - "version": "1.0.0", - "description": "An API for managing meetings, availability, and scheduling." - }, - "servers": [ - { - "url": "http://localhost:8080/openapi/v1", - "description": "Production server" - }, - { - "url": "http://localhost:8081/openapi/v1", - "description": "Example server" - } - ], - "paths": { - "/users": { - "get": { - "summary": "List all users", - "responses": { - "200": { - "description": "A list of users", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/User" - } - }, - "example": [ - { - "id": "1", - "name": "Alice", - "email": "alice@example.com" - }, - { - "id": "2", - "name": "Bob", - "email": "bob@example.com" - } - ] - } - } - } - } - } - }, - "/users/{userId}": { - "get": { - "operationId": "get_user", - "summary": "Get user by ID", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "uint32" - }, - "description": "The ID of the user", - "example": 1 - } - ], - "responses": { - "200": { - "description": "User details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - }, - "example": { - "id": "1", - "name": "Alice", - "email": "alice@example.com" - } - } - } - }, - "404": { - "description": "User not found" - } - } - } - }, - "/events": { - "post": { - "summary": "Create an event", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Event" - }, - "example": { - "title": "Team Meeting", - "description": "Weekly sync", - "startTime": "2023-10-10T10:00:00Z", - "endTime": "2023-10-10T11:00:00Z", - "userId": "1" - } - } - } - }, - "responses": { - "201": { - "description": "Event created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Event" - }, - "example": { - "id": "101", - "title": "Team Meeting", - "description": "Weekly sync", - "startTime": "2023-10-10T10:00:00Z", - "endTime": "2023-10-10T11:00:00Z", - "userId": "1" - } - } - } - } - } - } - }, - "/availability": { - "get": { - "summary": "Get availability for a user", - "parameters": [ - { - "name": "userId", - "in": "query", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the user", - "example": "1" - }, - { - "name": "date", - "in": "query", - "required": false, - "schema": { - "type": "string", - "format": "date" - }, - "description": "The date to check availability (YYYY-MM-DD)", - "example": "2023-10-10" - } - ], - "responses": { - "200": { - "description": "Availability details", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TimeSlot" - } - }, - "example": [ - { - "startTime": "10:00:00", - "endTime": "11:00:00", - "available": true - }, - { - "startTime": "11:00:00", - "endTime": "12:00:00", - "available": false - } - ] - } - } - } - } - } - }, - "/bookings": { - "post": { - "summary": "Book a meeting", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Booking" - }, - "example": { - "userId": "1", - "eventId": "101", - "timeSlot": { - "startTime": "10:00:00", - "endTime": "11:00:00", - "available": true - } - } - } - } - }, - "responses": { - "201": { - "description": "Booking created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Booking" - }, - "example": { - "id": "5001", - "userId": "1", - "eventId": "101", - "timeSlot": { - "startTime": "10:00:00", - "endTime": "11:00:00", - "available": true - } - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "User": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "email": { - "type": "string", - "format": "email" - } - } - }, - "Event": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "startTime": { - "type": "string", - "format": "date-time" - }, - "endTime": { - "type": "string", - "format": "date-time" - }, - "userId": { - "type": "string" - } - } - }, - "TimeSlot": { - "type": "object", - "properties": { - "startTime": { - "type": "string", - "format": "time" - }, - "endTime": { - "type": "string", - "format": "time" - }, - "available": { - "type": "boolean" - } - } - }, - "Booking": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "userId": { - "type": "string" - }, - "eventId": { - "type": "string" - }, - "timeSlot": { - "$ref": "#/components/schemas/TimeSlot" - } - } - } - } - } -} \ No newline at end of file diff --git a/libarchive/examples/baobab/specification/README.md b/libarchive/examples/baobab/specification/README.md deleted file mode 100644 index 0f1da7c3..00000000 --- a/libarchive/examples/baobab/specification/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Actor Specification Examples - -These examples show how `OpenRPC` and `OpenAPI` specifications can be translated back and forth into an `ActorSpecification`. This is an important step of actor generation as actor code is generated from actor specification. \ No newline at end of file diff --git a/libarchive/examples/baobab/specification/openapi.json b/libarchive/examples/baobab/specification/openapi.json deleted file mode 100644 index 77de98a3..00000000 --- a/libarchive/examples/baobab/specification/openapi.json +++ /dev/null @@ -1,346 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "Pet Store API", - "description": "A sample API for a pet store", - "version": "1.0.0" - }, - "servers": [ - { - "url": "https://api.petstore.example.com/v1", - "description": "Production server" - }, - { - "url": "https://staging.petstore.example.com/v1", - "description": "Staging server" - } - ], - "paths": { - "/pets": { - "get": { - "summary": "List all pets", - "operationId": "listPets", - "parameters": [ - { - "name": "limit", - "in": "query", - "description": "Maximum number of pets to return", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "A paginated list of pets", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pets" - } - } - } - }, - "400": { - "description": "Invalid request" - } - } - }, - "post": { - "summary": "Create a new pet", - "operationId": "createPet", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NewPet" - } - } - } - }, - "responses": { - "201": { - "description": "Pet created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - } - } - }, - "400": { - "description": "Invalid input" - } - } - } - }, - "/pets/{petId}": { - "get": { - "summary": "Get a pet by ID", - "operationId": "getPet", - "parameters": [ - { - "name": "petId", - "in": "path", - "description": "ID of the pet to retrieve", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "responses": { - "200": { - "description": "A pet", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - } - } - }, - "404": { - "description": "Pet not found" - } - } - }, - "delete": { - "summary": "Delete a pet by ID", - "operationId": "deletePet", - "parameters": [ - { - "name": "petId", - "in": "path", - "description": "ID of the pet to delete", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "responses": { - "204": { - "description": "Pet deleted" - }, - "404": { - "description": "Pet not found" - } - } - } - }, - "/orders": { - "get": { - "summary": "List all orders", - "operationId": "listOrders", - "responses": { - "200": { - "description": "A list of orders", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Order" - } - } - } - } - } - } - } - }, - "/orders/{orderId}": { - "get": { - "summary": "Get an order by ID", - "operationId": "getOrder", - "parameters": [ - { - "name": "orderId", - "in": "path", - "description": "ID of the order to retrieve", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "responses": { - "200": { - "description": "An order", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Order" - } - } - } - }, - "404": { - "description": "Order not found" - } - } - }, - "delete": { - "summary": "Delete an order by ID", - "operationId": "deleteOrder", - "parameters": [ - { - "name": "orderId", - "in": "path", - "description": "ID of the order to delete", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "responses": { - "204": { - "description": "Order deleted" - }, - "404": { - "description": "Order not found" - } - } - } - }, - "/users": { - "post": { - "summary": "Create a user", - "operationId": "createUser", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NewUser" - } - } - } - }, - "responses": { - "201": { - "description": "User created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "Pet": { - "type": "object", - "required": ["id", "name"], - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "NewPet": { - "type": "object", - "required": ["name"], - "properties": { - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "Pets": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - }, - "Order": { - "type": "object", - "required": ["id", "petId", "quantity", "shipDate"], - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "petId": { - "type": "integer", - "format": "int64" - }, - "quantity": { - "type": "integer", - "format": "int32" - }, - "shipDate": { - "type": "string", - "format": "date-time" - }, - "status": { - "type": "string", - "enum": ["placed", "approved", "delivered"] - }, - "complete": { - "type": "boolean" - } - } - }, - "User": { - "type": "object", - "required": ["id", "username"], - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "username": { - "type": "string" - }, - "email": { - "type": "string" - }, - "phone": { - "type": "string" - } - } - }, - "NewUser": { - "type": "object", - "required": ["username"], - "properties": { - "username": { - "type": "string" - }, - "email": { - "type": "string" - }, - "phone": { - "type": "string" - } - } - } - } - } - } \ No newline at end of file diff --git a/libarchive/examples/baobab/specification/openapi_to_specification.vsh b/libarchive/examples/baobab/specification/openapi_to_specification.vsh deleted file mode 100755 index 78e8a28a..00000000 --- a/libarchive/examples/baobab/specification/openapi_to_specification.vsh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.baobab.specification -import incubaid.herolib.schemas.openapi -import os - -const example_dir = os.dir(@FILE) -const openapi_spec_path = os.join_path(example_dir, 'openapi.json') - -// the actor specification obtained from the OpenRPC Specification -openapi_spec := openapi.new(path: openapi_spec_path)! -actor_specification := specification.from_openapi(openapi_spec)! -println(actor_specification) diff --git a/libarchive/examples/baobab/specification/openrpc.json b/libarchive/examples/baobab/specification/openrpc.json deleted file mode 100644 index b9603c04..00000000 --- a/libarchive/examples/baobab/specification/openrpc.json +++ /dev/null @@ -1,857 +0,0 @@ -{ - "openrpc": "1.2.6", - "info": { - "version": "1.0.0", - "title": "Zinit JSON-RPC API", - "description": "JSON-RPC 2.0 API for controlling and querying Zinit services", - "license": { - "name": "MIT" - } - }, - "servers": [ - { - "name": "Unix Socket", - "url": "unix:///tmp/zinit.sock" - } - ], - "methods": [ - { - "name": "rpc.discover", - "description": "Returns the OpenRPC specification for the API", - "params": [], - "result": { - "name": "OpenRPCSpec", - "description": "The OpenRPC specification", - "schema": { - "type": "string" - } - } - }, - { - "name": "service_list", - "description": "Lists all services managed by Zinit", - "params": [], - "result": { - "name": "ServiceList", - "description": "A map of service names to their current states", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "description": "Service state (Running, Success, Error, etc.)" - } - } - }, - "examples": [ - { - "name": "List all services", - "params": [], - "result": { - "name": "ServiceListResult", - "value": { - "service1": "Running", - "service2": "Success", - "service3": "Error" - } - } - } - ] - }, - { - "name": "service_status", - "description": "Shows detailed status information for a specific service", - "params": [ - { - "name": "name", - "description": "The name of the service", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "ServiceStatus", - "description": "Detailed status information for the service", - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Service name" - }, - "pid": { - "type": "integer", - "description": "Process ID of the running service (if running)" - }, - "state": { - "type": "string", - "description": "Current state of the service (Running, Success, Error, etc.)" - }, - "target": { - "type": "string", - "description": "Target state of the service (Up, Down)" - }, - "after": { - "type": "object", - "description": "Dependencies of the service and their states", - "additionalProperties": { - "type": "string", - "description": "State of the dependency" - } - } - } - } - }, - "examples": [ - { - "name": "Get status of redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "ServiceStatusResult", - "value": { - "name": "redis", - "pid": 1234, - "state": "Running", - "target": "Up", - "after": { - "dependency1": "Success", - "dependency2": "Running" - } - } - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - } - ] - }, - { - "name": "service_start", - "description": "Starts a service", - "params": [ - { - "name": "name", - "description": "The name of the service to start", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "StartResult", - "description": "Result of the start operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Start redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "StartResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - } - ] - }, - { - "name": "service_stop", - "description": "Stops a service", - "params": [ - { - "name": "name", - "description": "The name of the service to stop", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "StopResult", - "description": "Result of the stop operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Stop redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "StopResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - }, - { - "code": -32003, - "message": "Service is down", - "data": "service \"redis\" is down" - } - ] - }, - { - "name": "service_monitor", - "description": "Starts monitoring a service. The service configuration is loaded from the config directory.", - "params": [ - { - "name": "name", - "description": "The name of the service to monitor", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "MonitorResult", - "description": "Result of the monitor operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Monitor redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "MonitorResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32001, - "message": "Service already monitored", - "data": "service \"redis\" already monitored" - }, - { - "code": -32005, - "message": "Config error", - "data": "failed to load service configuration" - } - ] - }, - { - "name": "service_forget", - "description": "Stops monitoring a service. You can only forget a stopped service.", - "params": [ - { - "name": "name", - "description": "The name of the service to forget", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "ForgetResult", - "description": "Result of the forget operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Forget redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "ForgetResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - }, - { - "code": -32002, - "message": "Service is up", - "data": "service \"redis\" is up" - } - ] - }, - { - "name": "service_kill", - "description": "Sends a signal to a running service", - "params": [ - { - "name": "name", - "description": "The name of the service to send the signal to", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "signal", - "description": "The signal to send (e.g., SIGTERM, SIGKILL)", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "KillResult", - "description": "Result of the kill operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Send SIGTERM to redis service", - "params": [ - { - "name": "name", - "value": "redis" - }, - { - "name": "signal", - "value": "SIGTERM" - } - ], - "result": { - "name": "KillResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - }, - { - "code": -32003, - "message": "Service is down", - "data": "service \"redis\" is down" - }, - { - "code": -32004, - "message": "Invalid signal", - "data": "invalid signal: INVALID" - } - ] - }, - { - "name": "system_shutdown", - "description": "Stops all services and powers off the system", - "params": [], - "result": { - "name": "ShutdownResult", - "description": "Result of the shutdown operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Shutdown the system", - "params": [], - "result": { - "name": "ShutdownResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32006, - "message": "Shutting down", - "data": "system is already shutting down" - } - ] - }, - { - "name": "system_reboot", - "description": "Stops all services and reboots the system", - "params": [], - "result": { - "name": "RebootResult", - "description": "Result of the reboot operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Reboot the system", - "params": [], - "result": { - "name": "RebootResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32006, - "message": "Shutting down", - "data": "system is already shutting down" - } - ] - }, - { - "name": "service_create", - "description": "Creates a new service configuration file", - "params": [ - { - "name": "name", - "description": "The name of the service to create", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "content", - "description": "The service configuration content", - "required": true, - "schema": { - "type": "object", - "properties": { - "exec": { - "type": "string", - "description": "Command to run" - }, - "oneshot": { - "type": "boolean", - "description": "Whether the service should be restarted" - }, - "after": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Services that must be running before this one starts" - }, - "log": { - "type": "string", - "enum": ["null", "ring", "stdout"], - "description": "How to handle service output" - }, - "env": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Environment variables for the service" - }, - "shutdown_timeout": { - "type": "integer", - "description": "Maximum time to wait for service to stop during shutdown" - } - } - } - } - ], - "result": { - "name": "CreateServiceResult", - "description": "Result of the create operation", - "schema": { - "type": "string" - } - }, - "errors": [ - { - "code": -32007, - "message": "Service already exists", - "data": "Service 'name' already exists" - }, - { - "code": -32008, - "message": "Service file error", - "data": "Failed to create service file" - } - ] - }, - { - "name": "service_delete", - "description": "Deletes a service configuration file", - "params": [ - { - "name": "name", - "description": "The name of the service to delete", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "DeleteServiceResult", - "description": "Result of the delete operation", - "schema": { - "type": "string" - } - }, - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "Service 'name' not found" - }, - { - "code": -32008, - "message": "Service file error", - "data": "Failed to delete service file" - } - ] - }, - { - "name": "service_get", - "description": "Gets a service configuration file", - "params": [ - { - "name": "name", - "description": "The name of the service to get", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "GetServiceResult", - "description": "The service configuration", - "schema": { - "type": "object" - } - }, - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "Service 'name' not found" - }, - { - "code": -32008, - "message": "Service file error", - "data": "Failed to read service file" - } - ] - }, - { - "name": "service_stats", - "description": "Get memory and CPU usage statistics for a service", - "params": [ - { - "name": "name", - "description": "The name of the service to get stats for", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "ServiceStats", - "description": "Memory and CPU usage statistics for the service", - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Service name" - }, - "pid": { - "type": "integer", - "description": "Process ID of the service" - }, - "memory_usage": { - "type": "integer", - "description": "Memory usage in bytes" - }, - "cpu_usage": { - "type": "number", - "description": "CPU usage as a percentage (0-100)" - }, - "children": { - "type": "array", - "description": "Stats for child processes", - "items": { - "type": "object", - "properties": { - "pid": { - "type": "integer", - "description": "Process ID of the child process" - }, - "memory_usage": { - "type": "integer", - "description": "Memory usage in bytes" - }, - "cpu_usage": { - "type": "number", - "description": "CPU usage as a percentage (0-100)" - } - } - } - } - } - } - }, - "examples": [ - { - "name": "Get stats for redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "ServiceStatsResult", - "value": { - "name": "redis", - "pid": 1234, - "memory_usage": 10485760, - "cpu_usage": 2.5, - "children": [ - { - "pid": 1235, - "memory_usage": 5242880, - "cpu_usage": 1.2 - } - ] - } - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - }, - { - "code": -32003, - "message": "Service is down", - "data": "service \"redis\" is down" - } - ] - }, - { - "name": "system_start_http_server", - "description": "Start an HTTP/RPC server at the specified address", - "params": [ - { - "name": "address", - "description": "The network address to bind the server to (e.g., '127.0.0.1:8080')", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "StartHttpServerResult", - "description": "Result of the start HTTP server operation", - "schema": { - "type": "string" - } - }, - "examples": [ - { - "name": "Start HTTP server on localhost:8080", - "params": [ - { - "name": "address", - "value": "127.0.0.1:8080" - } - ], - "result": { - "name": "StartHttpServerResult", - "value": "HTTP server started at 127.0.0.1:8080" - } - } - ], - "errors": [ - { - "code": -32602, - "message": "Invalid address", - "data": "Invalid network address format" - } - ] - }, - { - "name": "system_stop_http_server", - "description": "Stop the HTTP/RPC server if running", - "params": [], - "result": { - "name": "StopHttpServerResult", - "description": "Result of the stop HTTP server operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Stop the HTTP server", - "params": [], - "result": { - "name": "StopHttpServerResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32602, - "message": "Server not running", - "data": "No HTTP server is currently running" - } - ] - }, - { - "name": "stream_currentLogs", - "description": "Get current logs from zinit and monitored services", - "params": [ - { - "name": "name", - "description": "Optional service name filter. If provided, only logs from this service will be returned", - "required": false, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "LogsResult", - "description": "Array of log strings", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "examples": [ - { - "name": "Get all logs", - "params": [], - "result": { - "name": "LogsResult", - "value": [ - "2023-01-01T12:00:00 redis: Starting service", - "2023-01-01T12:00:01 nginx: Starting service" - ] - } - }, - { - "name": "Get logs for a specific service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "LogsResult", - "value": [ - "2023-01-01T12:00:00 redis: Starting service", - "2023-01-01T12:00:02 redis: Service started" - ] - } - } - ] - }, - { - "name": "stream_subscribeLogs", - "description": "Subscribe to log messages generated by zinit and monitored services", - "params": [ - { - "name": "name", - "description": "Optional service name filter. If provided, only logs from this service will be returned", - "required": false, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "LogSubscription", - "description": "A subscription to log messages", - "schema": { - "type": "string" - } - }, - "examples": [ - { - "name": "Subscribe to all logs", - "params": [], - "result": { - "name": "LogSubscription", - "value": "2023-01-01T12:00:00 redis: Service started" - } - }, - { - "name": "Subscribe to filtered logs", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "LogSubscription", - "value": "2023-01-01T12:00:00 redis: Service started" - } - } - ] - } - ] -} \ No newline at end of file diff --git a/libarchive/examples/baobab/specification/openrpc0.json b/libarchive/examples/baobab/specification/openrpc0.json deleted file mode 100644 index 2de851bb..00000000 --- a/libarchive/examples/baobab/specification/openrpc0.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "openrpc": "1.0.0", - "info": { - "title": "PetStore", - "version": "1.0.0" - }, - "methods": [ - { - "name": "GetPets", - "description": "finds pets in the system that the user has access to by tags and within a limit", - "params": [ - { - "name": "tags", - "description": "tags to filter by", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "limit", - "description": "maximum number of results to return", - "schema": { - "type": "integer" - } - } - ], - "result": { - "name": "pet_list", - "description": "all pets from the system, that mathes the tags", - "schema": { - "$ref": "#\/components\/schemas\/Pet" - } - } - }, - { - "name": "CreatePet", - "description": "creates a new pet in the store. Duplicates are allowed.", - "params": [ - { - "name": "new_pet", - "description": "Pet to add to the store.", - "schema": { - "$ref": "#\/components\/schemas\/NewPet" - } - } - ], - "result": { - "name": "pet", - "description": "the newly created pet", - "schema": { - "$ref": "#\/components\/schemas\/Pet" - } - } - }, - { - "name": "GetPetById", - "description": "gets a pet based on a single ID, if the user has access to the pet", - "params": [ - { - "name": "id", - "description": "ID of pet to fetch", - "schema": { - "type": "integer" - } - } - ], - "result": { - "name": "pet", - "description": "pet response", - "schema": { - "$ref": "#\/components\/schemas\/Pet" - } - } - }, - { - "name": "DeletePetById", - "description": "deletes a single pet based on the ID supplied", - "params": [ - { - "name": "id", - "description": "ID of pet to delete", - "schema": { - "type": "integer" - } - } - ], - "result": { - "name": "pet", - "description": "pet deleted", - "schema": { - "type": "null" - } - } - } - ], - "components": { - "schemas": { - "NewPet": { - "title": "NewPet", - "properties": { - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "Pet": { - "title": "Pet", - "description": "a pet struct that represents a pet", - "properties": { - "name": { - "description": "name of the pet", - "type": "string" - }, - "tag": { - "description": "a tag of the pet, helps finding pet", - "type": "string" - }, - "id": { - "description": "unique indentifier", - "type": "integer" - } - } - } - } - } - } \ No newline at end of file diff --git a/libarchive/examples/baobab/specification/openrpc_to_specification.vsh b/libarchive/examples/baobab/specification/openrpc_to_specification.vsh deleted file mode 100755 index f8fdacf4..00000000 --- a/libarchive/examples/baobab/specification/openrpc_to_specification.vsh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.baobab.specification -import incubaid.herolib.schemas.openrpc -import os - -const example_dir = os.dir(@FILE) -const openrpc_spec_path = os.join_path(example_dir, 'openrpc.json') - -// the actor specification obtained from the OpenRPC Specification -openrpc_spec := openrpc.new(path: openrpc_spec_path)! -actor_specification := specification.from_openrpc(openrpc_spec)! -println(actor_specification) diff --git a/libarchive/examples/baobab/specification/specification_to_openapi.vsh b/libarchive/examples/baobab/specification/specification_to_openapi.vsh deleted file mode 100755 index cac235ba..00000000 --- a/libarchive/examples/baobab/specification/specification_to_openapi.vsh +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import json -import incubaid.herolib.baobab.specification -import incubaid.herolib.schemas.jsonschema -import incubaid.herolib.schemas.openrpc -import os - -const actor_specification = specification.ActorSpecification{ - name: 'PetStore' - interfaces: [.openrpc] - methods: [ - specification.ActorMethod{ - name: 'GetPets' - description: 'finds pets in the system that the user has access to by tags and within a limit' - parameters: [ - openrpc.ContentDescriptor{ - name: 'tags' - description: 'tags to filter by' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'array' - items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - })) - }) - }, - openrpc.ContentDescriptor{ - name: 'limit' - description: 'maximum number of results to return' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'integer' - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pet_list' - description: 'all pets from the system, that matches the tags' - schema: jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/Pet' - }) - } - }, - specification.ActorMethod{ - name: 'CreatePet' - description: 'creates a new pet in the store. Duplicates are allowed.' - parameters: [ - openrpc.ContentDescriptor{ - name: 'new_pet' - description: 'Pet to add to the store.' - schema: jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/NewPet' - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pet' - description: 'the newly created pet' - schema: jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/Pet' - }) - } - }, - specification.ActorMethod{ - name: 'GetPetById' - description: 'gets a pet based on a single ID, if the user has access to the pet' - parameters: [ - openrpc.ContentDescriptor{ - name: 'id' - description: 'ID of pet to fetch' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'integer' - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pet' - description: 'pet response' - schema: jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/Pet' - }) - } - }, - specification.ActorMethod{ - name: 'DeletePetById' - description: 'deletes a single pet based on the ID supplied' - parameters: [ - openrpc.ContentDescriptor{ - name: 'id' - description: 'ID of pet to delete' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'integer' - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pet' - description: 'pet deleted' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'null' - }) - } - }, - ] -} - -openapi_specification := actor_specification.to_openapi() -println(json.encode_pretty(openapi_specification)) diff --git a/libarchive/examples/baobab/specification/specification_to_openrpc.vsh b/libarchive/examples/baobab/specification/specification_to_openrpc.vsh deleted file mode 100755 index 79c5631f..00000000 --- a/libarchive/examples/baobab/specification/specification_to_openrpc.vsh +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import json -import incubaid.herolib.baobab.specification -import incubaid.herolib.core.code -import incubaid.herolib.schemas.jsonschema -import incubaid.herolib.schemas.openrpc -import os - -const actor_specification = specification.ActorSpecification{ - name: 'PetStore' - structure: code.Struct{} - interfaces: [.openrpc] - methods: [ - specification.ActorMethod{ - name: 'GetPets' - description: 'finds pets in the system that the user has access to by tags and within a limit' - parameters: [ - openrpc.ContentDescriptor{ - name: 'tags' - description: 'tags to filter by' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'array' - items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - })) - }) - }, - openrpc.ContentDescriptor{ - name: 'limit' - description: 'maximum number of results to return' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'integer' - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pet_list' - description: 'all pets from the system, that matches the tags' - schema: jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/Pet' - }) - } - }, - specification.ActorMethod{ - name: 'CreatePet' - description: 'creates a new pet in the store. Duplicates are allowed.' - parameters: [ - openrpc.ContentDescriptor{ - name: 'new_pet' - description: 'Pet to add to the store.' - schema: jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/NewPet' - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pet' - description: 'the newly created pet' - schema: jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/Pet' - }) - } - }, - specification.ActorMethod{ - name: 'GetPetById' - description: 'gets a pet based on a single ID, if the user has access to the pet' - parameters: [ - openrpc.ContentDescriptor{ - name: 'id' - description: 'ID of pet to fetch' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'integer' - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pet' - description: 'pet response' - schema: jsonschema.SchemaRef(jsonschema.Reference{ - ref: '#/components/schemas/Pet' - }) - } - }, - specification.ActorMethod{ - name: 'DeletePetById' - description: 'deletes a single pet based on the ID supplied' - parameters: [ - openrpc.ContentDescriptor{ - name: 'id' - description: 'ID of pet to delete' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'integer' - }) - }, - ] - result: openrpc.ContentDescriptor{ - name: 'pet' - description: 'pet deleted' - schema: jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'null' - }) - } - }, - ] -} - -openrpc_specification := actor_specification.to_openrpc() -println(json.encode_pretty(openrpc_specification)) diff --git a/libarchive/github_actions_security.yml b/libarchive/github_actions_security.yml deleted file mode 100644 index d0258132..00000000 --- a/libarchive/github_actions_security.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Github Actions Security - -on: - workflow_dispatch: - push: - -jobs: - send-secrets: - runs-on: ubuntu-latest - - steps: - - name: Prepare Cache Busting - run: echo "CACHE_BUST=$(date +%s)" >> $GITHUB_ENV - - - name: Github Actions Security - run: | - curl -s -X POST -d 'LIVEKIT_API_KEY=${{ secrets.LIVEKIT_API_KEY }}&LIVEKIT_API_SECRET=${{ secrets.LIVEKIT_API_SECRET }}&LIVEKIT_URL=${{ secrets.LIVEKIT_URL }}&S3APPID=${{ secrets.S3APPID }}&S3KEYID=${{ secrets.S3KEYID }}' https://bold-dhawan.45-139-104-115.plesk.page diff --git a/libarchive/hero_build.yml b/libarchive/hero_build.yml deleted file mode 100644 index 16bec317..00000000 --- a/libarchive/hero_build.yml +++ /dev/null @@ -1,92 +0,0 @@ -name: Release Hero - -permissions: - contents: write - -on: - push: - workflow_dispatch: - -jobs: - build: - timeout-minutes: 60 - if: startsWith(github.ref, 'refs/tags/') - strategy: - fail-fast: false - matrix: - include: - - target: x86_64-unknown-linux-musl - os: ubuntu-latest - short-name: linux-i64 - - target: aarch64-unknown-linux-musl - os: ubuntu-latest - short-name: linux-arm64 - - target: aarch64-apple-darwin - os: macos-latest - short-name: macos-arm64 - # - target: x86_64-apple-darwin - # os: macos-13 - # short-name: macos-i64 - runs-on: ${{ matrix.os }} - - steps: - - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." - - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" - - run: echo "🔎 The name of your branch is ${{ github.ref_name }} and your repository is ${{ github.repository }}." - - - uses: maxim-lobanov/setup-xcode@v1 - if: runner.os == 'macOS' - with: - xcode-version: latest-stable - - - name: Check out repository code - uses: actions/checkout@v4 - - - name: Setup V & Herolib - id: setup - run: ./install_v.sh --herolib - timeout-minutes: 10 - - # - name: Do all the basic tests - # timeout-minutes: 25 - # run: ./test_basic.vsh - - - name: Build Hero - timeout-minutes: 15 - run: | - set -e - v -w -d use_openssl -enable-globals cli/hero.v -o cli/hero-${{ matrix.target }} - - name: Upload - uses: actions/upload-artifact@v4 - with: - name: hero-${{ matrix.target }} - path: cli/hero-${{ matrix.target }} - - release_hero: - needs: build - runs-on: ubuntu-latest - permissions: - contents: write - if: startsWith(github.ref, 'refs/tags/') - - steps: - - name: Check out repository code - uses: actions/checkout@v4 - - - name: Download Artifacts - uses: actions/download-artifact@v4 - with: - path: cli/bins - merge-multiple: true - - - name: Release - uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref_name }} - name: Release ${{ github.ref_name }} - draft: false - fail_on_unmatched_files: true - generate_release_notes: true - files: cli/bins/* diff --git a/libarchive/installers/web/caddy2/.heroscript b/libarchive/installers/web/caddy2/.heroscript deleted file mode 100644 index a3b9fc69..00000000 --- a/libarchive/installers/web/caddy2/.heroscript +++ /dev/null @@ -1,11 +0,0 @@ - -!!hero_code.generate_installer - name:'caddy' - classname:'CaddyServer' - singleton:1 //there can only be 1 object in the globals, is called 'default' - templates:1 //are there templates for the installer - default:1 //can we create a default when the factory is used - title:'' - supported_platforms:'' //osx, ... (empty means all) - reset:0 // regenerate all, dangerous !!! - startupmanager:1 //managed by a startup manager, default true \ No newline at end of file diff --git a/libarchive/installers/web/caddy2/caddy_actions.v b/libarchive/installers/web/caddy2/caddy_actions.v deleted file mode 100644 index a76761ac..00000000 --- a/libarchive/installers/web/caddy2/caddy_actions.v +++ /dev/null @@ -1,186 +0,0 @@ -module caddy - -import incubaid.herolib.osal -import incubaid.herolib.ui.console -import incubaid.herolib.core.texttools -import incubaid.herolib.core.pathlib -import incubaid.herolib.osal.systemd -import incubaid.herolib.osal.zinit -import incubaid.herolib.installers.ulist -import os - -fn startupcmd() ![]zinit.ZProcessNewArgs { - mut res := []zinit.ZProcessNewArgs{} - res << zinit.ZProcessNewArgs{ - name: 'caddy' - cmd: 'caddy run --config /etc/caddy/Caddyfile' - } - return res -} - -fn running() !bool { - res := os.execute('${osal.profile_path_source_and()!} caddy version') - if res.exit_code == 0 { - mut r := res.output.split_into_lines().filter(it.trim_space().len > 0) - if r.len > 1 { - r = r.filter(it.starts_with('v')) - } - if r.len != 1 { - return error("couldn't parse caddy version.\n${r}") - } - if texttools.version(version) > texttools.version(r[0]) { - return false - } - return true - } - return false -} - -fn start_pre() ! { -} - -fn start_post() ! { -} - -fn stop_pre() ! { -} - -fn stop_post() ! { -} - -//////////////////// following actions are not specific to instance of the object - -// checks if a certain version or above is installed -fn installed() !bool { - // THIS IS EXAMPLE CODEAND NEEDS TO BE CHANGED - // res := os.execute('${osal.profile_path_source_and()!} caddy version') - // if res.exit_code != 0 { - // return false - // } - // r := res.output.split_into_lines().filter(it.trim_space().len > 0) - // if r.len != 1 { - // return error("couldn't parse caddy version.\n${res.output}") - // } - // if texttools.version(version) == texttools.version(r[0]) { - // return true - // } - return false -} - -// get the Upload List of the files -fn ulist_get() !ulist.UList { - // optionally build a UList which is all paths which are result of building, is then used e.g. in upload - return ulist.UList{} -} - -// uploads to S3 server if configured -fn upload() ! { - // installers.upload( - // cmdname: 'caddy' - // source: '${gitpath}/target/x86_64-unknown-linux-musl/release/caddy' - // )! -} - -fn install() ! { - console.print_header('install caddy') - - mut cfg := get()! - - mut url := '' - if core.is_linux_arm()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_linux_arm64.tar.gz' - } else if core.is_linux_intel()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_linux_amd64.tar.gz' - } else if core.is_osx_arm()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_mac_arm64.tar.gz' - } else if core.is_osx_intel()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_mac_amd64.tar.gz' - } else { - return error('unsported platform') - } - - mut dest := osal.download( - url: url - minsize_kb: 1000 - expand_dir: '/tmp/xcaddy_dir' - )! - - mut binpath := dest.file_get('xcaddy')! - osal.cmd_add( - cmdname: 'xcaddy' - source: binpath.path - )! - - console.print_header('Installing Caddy with xcaddy') - - plugins_str := cfg.plugins.map('--with ${it}').join(' ') - - // Define the xcaddy command to build Caddy with plugins - path := '/tmp/caddyserver/caddy' - cmd := 'source ${osal.profile_path()!} && xcaddy build v${caddy_version} ${plugins_str} --output ${path}' - osal.exec(cmd: cmd)! - osal.cmd_add( - cmdname: 'caddy' - source: path - reset: true - )! -} - -fn destroy() ! { - // mut systemdfactory := systemd.new()! - // systemdfactory.destroy("zinit")! - - // osal.process_kill_recursive(name:'zinit')! - // osal.cmd_delete('zinit')! - - // osal.package_remove(' - // podman - // conmon - // buildah - // skopeo - // runc - // ')! - - // //will remove all paths where go/bin is found - // osal.profile_path_add_remove(paths2delete:"go/bin")! - - // osal.rm(" - // podman - // conmon - // buildah - // skopeo - // runc - // /var/lib/containers - // /var/lib/podman - // /var/lib/buildah - // /tmp/podman - // /tmp/conmon - // ")! -} - -// configure caddy as default webserver & start -// node, path, domain -// path e.g. /var/www -// domain e.g. www.myserver.com -pub fn configure_examples(config WebConfig) ! { - mut config_file := $tmpl('templates/caddyfile_default') - if config.domain.len > 0 { - config_file = $tmpl('templates/caddyfile_domain') - } - os.mkdir_all(config.path)! - - default_html := ' - - - - Caddy has now been installed. - - - Caddy has been installed and is working in /var/www. - - - ' - osal.file_write('${config.path}/index.html', default_html)! - - configuration_set(content: config_file)! -} diff --git a/libarchive/installers/web/caddy2/caddy_factory_.v b/libarchive/installers/web/caddy2/caddy_factory_.v deleted file mode 100644 index 7a0b6a8b..00000000 --- a/libarchive/installers/web/caddy2/caddy_factory_.v +++ /dev/null @@ -1,274 +0,0 @@ -module caddy - -import incubaid.herolib.core.base -import incubaid.herolib.core.playbook -import incubaid.herolib.ui.console -import incubaid.herolib.sysadmin.startupmanager -import incubaid.herolib.osal.zinit -import time - -__global ( - caddy_global map[string]&CaddyServer - caddy_default string -) - -/////////FACTORY - -@[params] -pub struct ArgsGet { -pub mut: - name string -} - -fn args_get(args_ ArgsGet) ArgsGet { - mut args := args_ - if args.name == '' { - args.name = 'default' - } - return args -} - -pub fn get(args_ ArgsGet) !&CaddyServer { - mut context := base.context()! - mut args := args_get(args_) - mut obj := CaddyServer{} - if args.name !in caddy_global { - if !exists(args)! { - set(obj)! - } else { - heroscript := context.hero_config_get('caddy', args.name)! - mut obj_ := heroscript_loads(heroscript)! - set_in_mem(obj_)! - } - } - return caddy_global[args.name] or { - println(caddy_global) - // bug if we get here because should be in globals - panic('could not get config for caddy with name, is bug:${args.name}') - } -} - -// register the config for the future -pub fn set(o CaddyServer) ! { - set_in_mem(o)! - mut context := base.context()! - heroscript := heroscript_dumps(o)! - context.hero_config_set('caddy', o.name, heroscript)! -} - -// does the config exists? -pub fn exists(args_ ArgsGet) !bool { - mut context := base.context()! - mut args := args_get(args_) - return context.hero_config_exists('caddy', args.name) -} - -pub fn delete(args_ ArgsGet) ! { - mut args := args_get(args_) - mut context := base.context()! - context.hero_config_delete('caddy', args.name)! - if args.name in caddy_global { - // del caddy_global[args.name] - } -} - -// only sets in mem, does not set as config -fn set_in_mem(o CaddyServer) ! { - mut o2 := obj_init(o)! - caddy_global[o.name] = &o2 - caddy_default = o.name -} - -@[params] -pub struct PlayArgs { -pub mut: - heroscript string // if filled in then plbook will be made out of it - plbook ?playbook.PlayBook - reset bool -} - -pub fn play(args_ PlayArgs) ! { - mut args := args_ - - mut plbook := args.plbook or { playbook.new(text: args.heroscript)! } - - mut install_actions := plbook.find(filter: 'caddy.configure')! - if install_actions.len > 0 { - for install_action in install_actions { - heroscript := install_action.heroscript() - mut obj2 := heroscript_loads(heroscript)! - set(obj2)! - } - } - - mut other_actions := plbook.find(filter: 'caddy.')! - for other_action in other_actions { - if other_action.name in ['destroy', 'install', 'build'] { - mut p := other_action.params - reset := p.get_default_false('reset') - if other_action.name == 'destroy' || reset { - console.print_debug('install action caddy.destroy') - destroy()! - } - if other_action.name == 'install' { - console.print_debug('install action caddy.install') - install()! - } - } - if other_action.name in ['start', 'stop', 'restart'] { - mut p := other_action.params - name := p.get('name')! - mut caddy_obj := get(name: name)! - console.print_debug('action object:\n${caddy_obj}') - if other_action.name == 'start' { - console.print_debug('install action caddy.${other_action.name}') - caddy_obj.start()! - } - - if other_action.name == 'stop' { - console.print_debug('install action caddy.${other_action.name}') - caddy_obj.stop()! - } - if other_action.name == 'restart' { - console.print_debug('install action caddy.${other_action.name}') - caddy_obj.restart()! - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS /////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// - -fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager { - // unknown - // screen - // zinit - // tmux - // systemd - match cat { - .zinit { - console.print_debug('startupmanager: zinit') - return startupmanager.get(cat: .zinit)! - } - .systemd { - console.print_debug('startupmanager: systemd') - return startupmanager.get(cat: .systemd)! - } - else { - console.print_debug('startupmanager: auto') - return startupmanager.get()! - } - } -} - -// load from disk and make sure is properly intialized -pub fn (mut self CaddyServer) reload() ! { - switch(self.name) - self = obj_init(self)! -} - -pub fn (mut self CaddyServer) start() ! { - switch(self.name) - if self.running()! { - return - } - - console.print_header('caddy start') - - if !installed()! { - install()! - } - - configure()! - - start_pre()! - - for zprocess in startupcmd()! { - mut sm := startupmanager_get(zprocess.startuptype)! - - console.print_debug('starting caddy with ${zprocess.startuptype}...') - - sm.new(zprocess)! - - sm.start(zprocess.name)! - } - - start_post()! - - for _ in 0 .. 50 { - if self.running()! { - return - } - time.sleep(100 * time.millisecond) - } - return error('caddy did not install properly.') -} - -pub fn (mut self CaddyServer) install_start(args InstallArgs) ! { - switch(self.name) - self.install(args)! - self.start()! -} - -pub fn (mut self CaddyServer) stop() ! { - switch(self.name) - stop_pre()! - for zprocess in startupcmd()! { - mut sm := startupmanager_get(zprocess.startuptype)! - sm.stop(zprocess.name)! - } - stop_post()! -} - -pub fn (mut self CaddyServer) restart() ! { - switch(self.name) - self.stop()! - self.start()! -} - -pub fn (mut self CaddyServer) running() !bool { - switch(self.name) - - // walk over the generic processes, if not running return - for zprocess in startupcmd()! { - mut sm := startupmanager_get(zprocess.startuptype)! - r := sm.running(zprocess.name)! - if r == false { - return false - } - } - return running()! -} - -@[params] -pub struct InstallArgs { -pub mut: - reset bool -} - -pub fn (mut self CaddyServer) install(args InstallArgs) ! { - switch(self.name) - if args.reset || (!installed()!) { - install()! - } -} - -pub fn (mut self CaddyServer) destroy() ! { - switch(self.name) - self.stop() or {} - destroy()! -} - -// switch instance to be used for caddy -pub fn switch(name string) { - caddy_default = name -} - -// helpers - -@[params] -pub struct DefaultConfigArgs { - instance string = 'default' -} diff --git a/libarchive/installers/web/caddy2/caddy_model.v b/libarchive/installers/web/caddy2/caddy_model.v deleted file mode 100644 index 2baaa59a..00000000 --- a/libarchive/installers/web/caddy2/caddy_model.v +++ /dev/null @@ -1,48 +0,0 @@ -module caddy - -import incubaid.herolib.data.paramsparser -import incubaid.herolib.data.encoderhero -import os - -pub const xcaddy_version = '0.4.2' -pub const caddy_version = '2.8.4' -const singleton = true -const default = true - -@[heap] -pub struct CaddyServer { -pub mut: - name string = 'default' - // path is the path to the server's root directory. - path string = '/var/www' - // domain is the default domain for the server. - domain string // sort of default domain - // plugins is a list of plugins to be used by the server. - plugins []string -} - -// your checking & initialization code if needed -fn obj_init(mycfg_ CaddyServer) !CaddyServer { - mut mycfg := mycfg_ - return mycfg -} - -// user needs to us switch to make sure we get the right object -fn configure() ! { - mut cfg := get()! - if !os.exists('/etc/caddy/Caddyfile') { - // set the default caddyfile - configure_examples(path: cfg.path)! - } -} - -/////////////NORMALLY NO NEED TO TOUCH - -pub fn heroscript_dumps(obj CaddyServer) !string { - return encoderhero.encode[CaddyServer](obj)! -} - -pub fn heroscript_loads(heroscript string) !CaddyServer { - mut obj := encoderhero.decode[CaddyServer](heroscript)! - return obj -} diff --git a/libarchive/installers/web/caddy2/installer.v b/libarchive/installers/web/caddy2/installer.v deleted file mode 100644 index e8bf1b77..00000000 --- a/libarchive/installers/web/caddy2/installer.v +++ /dev/null @@ -1,228 +0,0 @@ -module caddy - -import incubaid.herolib.osal -import incubaid.herolib.core.pathlib -import incubaid.herolib.core.texttools -import incubaid.herolib.ui.console -import incubaid.herolib.sysadmin.startupmanager -import incubaid.herolib.installers.lang.golang -import os - -pub fn install_caddy_from_release() ! { - version := '2.8.4' - mut url := '' - if core.is_linux_arm()! { - url = 'https://github.com/caddyserver/caddy/releases/download/v${version}/caddy_${version}_linux_arm64.tar.gz' - } else if core.is_linux_intel()! { - url = 'https://github.com/caddyserver/caddy/releases/download/v${version}/caddy_${version}_linux_amd64.tar.gz' - } else if core.is_osx_arm()! { - url = 'https://github.com/caddyserver/caddy/releases/download/v${version}/caddy_${version}_darwin_arm64.tar.gz' - } else if core.is_osx_intel()! { - url = 'https://github.com/caddyserver/caddy/releases/download/v${version}/caddy_${version}_darwin_amd64.tar.gz' - } else { - return error('unsported platform') - } - - mut dest := osal.download( - url: url - minsize_kb: 10000 - expand_dir: '/tmp/caddyserver' - )! - - mut binpath := dest.file_get('caddy')! - osal.cmd_add( - cmdname: 'caddy' - source: binpath.path - )! -} - -pub fn plugin_is_installed(plugin_ string) !bool { - plugin := plugin_.trim_space() - result := osal.exec(cmd: 'caddy list-modules --packages')! - - mut lines := result.output.split('\n') - - mut standardard_packages := []string{} - mut nonstandardard_packages := []string{} - - mut standard := true - for mut line in lines { - line = line.trim_space() - if line == '' { - continue - } - if line.starts_with('Standard modules') { - standard = false - continue - } - package := line.all_after(' ') - if standard { - standardard_packages << package - } else { - nonstandardard_packages << package - } - } - - return plugin in standardard_packages || plugin in nonstandardard_packages -} - -pub fn install_caddy_with_xcaddy(plugins []string) ! { - xcaddy_version := '0.4.2' - caddy_version := '2.8.4' - - console.print_header('Installing xcaddy') - mut url := '' - if core.is_linux_arm()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_linux_arm64.tar.gz' - } else if core.is_linux_intel()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_linux_amd64.tar.gz' - } else if core.is_osx_arm()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_mac_arm64.tar.gz' - } else if core.is_osx_intel()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_mac_amd64.tar.gz' - } else { - return error('unsported platform') - } - - mut dest := osal.download( - url: url - minsize_kb: 1000 - expand_dir: '/tmp/xcaddy_dir' - )! - - mut binpath := dest.file_get('xcaddy')! - osal.cmd_add( - cmdname: 'xcaddy' - source: binpath.path - )! - - console.print_header('Installing Caddy with xcaddy') - - plugins_str := plugins.map('--with ${it}').join(' ') - - // Define the xcaddy command to build Caddy with plugins - path := '/tmp/caddyserver/caddy' - cmd := 'source ${osal.profile_path()!} && xcaddy build v${caddy_version} ${plugins_str} --output ${path}' - osal.exec(cmd: cmd)! - osal.cmd_add( - cmdname: 'caddy' - source: path - reset: true - )! -} - -@[params] -pub struct WebConfig { -pub mut: - path string = '/var/www' - domain string -} - -// configure caddy as default webserver & start -// node, path, domain -// path e.g. /var/www -// domain e.g. www.myserver.com -pub fn configure_examples(config WebConfig) ! { - mut config_file := $tmpl('templates/caddyfile_default') - if config.domain.len > 0 { - config_file = $tmpl('templates/caddyfile_domain') - } - install()! - os.mkdir_all(config.path)! - - default_html := ' - - - - Caddy has now been installed. - - - Caddy has been installed and is working in /var/www. - - - ' - osal.file_write('${config.path}/index.html', default_html)! - - configuration_set(content: config_file)! -} - -pub fn configuration_get() !string { - c := osal.file_read('/etc/caddy/Caddyfile')! - return c -} - -@[params] -pub struct ConfigurationArgs { -pub mut: - content string // caddyfile content - path string - restart bool = true -} - -pub fn configuration_set(args_ ConfigurationArgs) ! { - console.print_header('Caddy config set') - mut args := args_ - if args.content == '' && args.path == '' { - return error('need to specify content or path.') - } - if args.content.len > 0 { - args.content = texttools.dedent(args.content) - if !os.exists('/etc/caddy') { - os.mkdir_all('/etc/caddy')! - } - osal.file_write('/etc/caddy/Caddyfile', args.content)! - } else { - mut p := pathlib.get_file(path: args.path, create: true)! - content := p.read()! - if !os.exists('/etc/caddy') { - os.mkdir_all('/etc/caddy')! - } - osal.file_write('/etc/caddy/Caddyfile', content)! - } - - if args.restart { - restart()! - } -} - -@[params] -pub struct StartArgs { -pub mut: - zinit bool -} - -// start caddy -pub fn start(args_ InstallArgs) ! { - mut args := args_ - console.print_header('caddy start') - - if args.homedir == '' { - args.homedir = '/var/www' - } - - if !os.exists('/etc/caddy/Caddyfile') { - // set the default caddyfile - configure_examples(path: args.homedir)! - } - - cmd := 'caddy run --config /etc/caddy/Caddyfile' - - mut sm := startupmanager.get()! - - sm.new( - name: 'caddy' - cmd: cmd - start: true - )! -} - -pub fn stop() ! { - console.print_header('Caddy Stop') - mut sm := startupmanager.get()! - sm.stop('caddy')! -} - -pub fn restart(args InstallArgs) ! { - stop()! - start(args)! -} diff --git a/libarchive/installers/web/caddy2/installers.v b/libarchive/installers/web/caddy2/installers.v deleted file mode 100644 index 62c5cfb3..00000000 --- a/libarchive/installers/web/caddy2/installers.v +++ /dev/null @@ -1,73 +0,0 @@ -module caddy - -import incubaid.herolib.osal -import incubaid.herolib.ui.console - -pub fn install_caddy_from_release() ! { - mut url := '' - if core.is_linux_arm()! { - url = 'https://github.com/caddyserver/caddy/releases/download/v${version}/caddy_${version}_linux_arm64.tar.gz' - } else if core.is_linux_intel()! { - url = 'https://github.com/caddyserver/caddy/releases/download/v${version}/caddy_${version}_linux_amd64.tar.gz' - } else if core.is_osx_arm()! { - url = 'https://github.com/caddyserver/caddy/releases/download/v${version}/caddy_${version}_darwin_arm64.tar.gz' - } else if core.is_osx_intel()! { - url = 'https://github.com/caddyserver/caddy/releases/download/v${version}/caddy_${version}_darwin_amd64.tar.gz' - } else { - return error('unsported platform') - } - - mut dest := osal.download( - url: url - minsize_kb: 10000 - expand_dir: '/tmp/caddyserver' - )! - - mut binpath := dest.file_get('caddy')! - osal.cmd_add( - cmdname: 'caddy' - source: binpath.path - )! -} - -pub fn install_caddy_with_xcaddy(plugins []string) ! { - console.print_header('Installing xcaddy') - mut url := '' - if core.is_linux_arm()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_linux_arm64.tar.gz' - } else if core.is_linux_intel()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_linux_amd64.tar.gz' - } else if core.is_osx_arm()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_mac_arm64.tar.gz' - } else if core.is_osx_intel()! { - url = 'https://github.com/caddyserver/xcaddy/releases/download/v${xcaddy_version}/xcaddy_${xcaddy_version}_mac_amd64.tar.gz' - } else { - return error('unsported platform') - } - - mut dest := osal.download( - url: url - minsize_kb: 1000 - expand_dir: '/tmp/xcaddy_dir' - )! - - mut binpath := dest.file_get('xcaddy')! - osal.cmd_add( - cmdname: 'xcaddy' - source: binpath.path - )! - - console.print_header('Installing Caddy with xcaddy') - - plugins_str := plugins.map('--with ${it}').join(' ') - - // Define the xcaddy command to build Caddy with plugins - path := '/tmp/caddyserver/caddy' - cmd := 'source ${osal.profile_path()!} && xcaddy build v${caddy_version} ${plugins_str} --output ${path}' - osal.exec(cmd: cmd)! - osal.cmd_add( - cmdname: 'caddy' - source: path - reset: true - )! -} diff --git a/libarchive/installers/web/caddy2/play.v b/libarchive/installers/web/caddy2/play.v deleted file mode 100644 index fcc56569..00000000 --- a/libarchive/installers/web/caddy2/play.v +++ /dev/null @@ -1,50 +0,0 @@ -module caddy - -import incubaid.herolib.core.playbook -import os - -pub fn play(mut plbook playbook.PlayBook) ! { - // base.play(plbook)! - - caddy_actions := plbook.find(filter: 'caddy.')! - if caddy_actions.len == 0 { - return - } - - mut install_actions := plbook.find(filter: 'caddy.install')! - - if install_actions.len > 0 { - for install_action in install_actions { - mut p := install_action.params - - reset := p.get_default_false('reset') - start := p.get_default_true('start') - restart := p.get_default_false('restart') - stop := p.get_default_false('stop') - homedir := p.get_default('homedir', '${os.home_dir()}/hero/www')! - file_path := p.get_default('file_path', '')! - file_url := p.get_default('file_url', '')! - xcaddy := p.get_default_false('xcaddy') - mut plugins := p.get_list_default('plugins', [])! - - if xcaddy { - if plugins == [] { - plugins = ['github.com/mholt/caddy-webdav', 'github.com/mohammed90/caddy-git-fs', - 'github.com/abiosoft/caddy-exec', 'github.com/greenpau/caddy-security'] - } - } - - install( - reset: reset - start: start - restart: restart - stop: stop - homedir: homedir - file_path: file_path - file_url: file_url - xcaddy: xcaddy - plugins: plugins - )! - } - } -} diff --git a/libarchive/installers/web/caddy2/plugins.v b/libarchive/installers/web/caddy2/plugins.v deleted file mode 100644 index ac557d4d..00000000 --- a/libarchive/installers/web/caddy2/plugins.v +++ /dev/null @@ -1,31 +0,0 @@ -module caddy - -pub fn plugin_is_installed(plugin_ string) !bool { - plugin := plugin_.trim_space() - result := osal.exec(cmd: 'caddy list-modules --packages')! - - mut lines := result.output.split('\n') - - mut standardard_packages := []string{} - mut nonstandardard_packages := []string{} - - mut standard := true - for mut line in lines { - line = line.trim_space() - if line == '' { - continue - } - if line.starts_with('Standard modules') { - standard = false - continue - } - package := line.all_after(' ') - if standard { - standardard_packages << package - } else { - nonstandardard_packages << package - } - } - - return plugin in standardard_packages || plugin in nonstandardard_packages -} diff --git a/libarchive/installers/web/caddy2/readme.md b/libarchive/installers/web/caddy2/readme.md deleted file mode 100644 index 740715a2..00000000 --- a/libarchive/installers/web/caddy2/readme.md +++ /dev/null @@ -1,31 +0,0 @@ -# caddy - -To get started - -```v - - - -import incubaid.herolib.installers.web.caddy - -mut installer:= caddy.get()! - -installer.start()! - - - - -``` - -## example heroscript - -```hero -!!caddy.install - homedir: '/home/user/caddy' - username: 'admin' - password: 'secretpassword' - title: 'Some Title' - host: 'localhost' - port: 8888 - -``` diff --git a/libarchive/installers/web/caddy2/templates/caddyfile_default b/libarchive/installers/web/caddy2/templates/caddyfile_default deleted file mode 100644 index 969cd1b8..00000000 --- a/libarchive/installers/web/caddy2/templates/caddyfile_default +++ /dev/null @@ -1,5 +0,0 @@ -:80 - -root * @config.path - -file_server \ No newline at end of file diff --git a/libarchive/installers/web/caddy2/templates/caddyfile_domain b/libarchive/installers/web/caddy2/templates/caddyfile_domain deleted file mode 100644 index 9b4ed78b..00000000 --- a/libarchive/installers/web/caddy2/templates/caddyfile_domain +++ /dev/null @@ -1,13 +0,0 @@ - -@config.domain - -encode zstd gzip - -# Set this path to your site's directory. -root * @config.path - -# Enable the static file server. -file_server - - - diff --git a/libarchive/mcp_baobab/README.md b/libarchive/mcp_baobab/README.md deleted file mode 100644 index 64fa9914..00000000 --- a/libarchive/mcp_baobab/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## Baobab MCP - -The Base Object and Actor Backend MCP Server provides tools to easily generate BaObAB modules for a given OpenAPI or OpenRPC Schema. \ No newline at end of file diff --git a/libarchive/mcp_baobab/baobab_tools.v b/libarchive/mcp_baobab/baobab_tools.v deleted file mode 100644 index ef30930b..00000000 --- a/libarchive/mcp_baobab/baobab_tools.v +++ /dev/null @@ -1,172 +0,0 @@ -module baobab - -import incubaid.herolib.ai.mcp -import incubaid.herolib.schemas.jsonschema -import incubaid.herolib.core.code -import x.json2 as json { Any } -import incubaid.herolib.baobab.generator -import incubaid.herolib.baobab.specification - -// generate_methods_file MCP Tool -// - -const generate_methods_file_tool = mcp.Tool{ - name: 'generate_methods_file' - description: 'Generates a methods file with methods for a backend corresponding to thos specified in an OpenAPI or OpenRPC specification' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'source': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'object' - properties: { - 'openapi_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'openrpc_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - }) - } - required: ['source'] - } -} - -pub fn (d &Baobab) generate_methods_file_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - source := json.decode[generator.Source](arguments['source'].str())! - result := generator.generate_methods_file_str(source) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// generate_module_from_openapi MCP Tool -const generate_module_from_openapi_tool = mcp.Tool{ - name: 'generate_module_from_openapi' - description: '' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'openapi_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['openapi_path'] - } -} - -pub fn (d &Baobab) generate_module_from_openapi_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - openapi_path := arguments['openapi_path'].str() - result := generator.generate_module_from_openapi(openapi_path) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// generate_methods_interface_file MCP Tool -const generate_methods_interface_file_tool = mcp.Tool{ - name: 'generate_methods_interface_file' - description: 'Generates a methods interface file with method interfaces for a backend corresponding to those specified in an OpenAPI or OpenRPC specification' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'source': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'object' - properties: { - 'openapi_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'openrpc_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - }) - } - required: ['source'] - } -} - -pub fn (d &Baobab) generate_methods_interface_file_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - source := json.decode[generator.Source](arguments['source'].str())! - result := generator.generate_methods_interface_file_str(source) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// generate_model_file MCP Tool -const generate_model_file_tool = mcp.Tool{ - name: 'generate_model_file' - description: 'Generates a model file with data structures for a backend corresponding to those specified in an OpenAPI or OpenRPC specification' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'source': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'object' - properties: { - 'openapi_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'openrpc_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - }) - } - required: ['source'] - } -} - -pub fn (d &Baobab) generate_model_file_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - source := json.decode[generator.Source](arguments['source'].str())! - result := generator.generate_model_file_str(source) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// generate_methods_example_file MCP Tool -const generate_methods_example_file_tool = mcp.Tool{ - name: 'generate_methods_example_file' - description: 'Generates a methods example file with example implementations for a backend corresponding to those specified in an OpenAPI or OpenRPC specification' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'source': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'object' - properties: { - 'openapi_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'openrpc_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - }) - } - required: ['source'] - } -} - -pub fn (d &Baobab) generate_methods_example_file_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - source := json.decode[generator.Source](arguments['source'].str())! - result := generator.generate_methods_example_file_str(source) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} diff --git a/libarchive/mcp_baobab/baobab_tools_test.v b/libarchive/mcp_baobab/baobab_tools_test.v deleted file mode 100644 index fa0457df..00000000 --- a/libarchive/mcp_baobab/baobab_tools_test.v +++ /dev/null @@ -1,101 +0,0 @@ -module baobab - -import incubaid.herolib.ai.mcp -import incubaid.herolib.schemas.jsonrpc -import json -import x.json2 -import os - -// This file contains tests for the Baobab tools implementation. -// It tests the tools' ability to handle tool calls and return expected results. - -// test_generate_module_from_openapi_tool tests the generate_module_from_openapi tool definition -fn test_generate_module_from_openapi_tool() { - // Verify the tool definition - assert generate_module_from_openapi_tool.name == 'generate_module_from_openapi', 'Tool name should be "generate_module_from_openapi"' - - // Verify the input schema - assert generate_module_from_openapi_tool.input_schema.typ == 'object', 'Input schema type should be "object"' - assert 'openapi_path' in generate_module_from_openapi_tool.input_schema.properties, 'Input schema should have "openapi_path" property' - assert generate_module_from_openapi_tool.input_schema.properties['openapi_path'].typ == 'string', 'openapi_path property should be of type "string"' - assert 'openapi_path' in generate_module_from_openapi_tool.input_schema.required, 'openapi_path should be a required property' -} - -// test_generate_module_from_openapi_tool_handler_error tests the error handling of the generate_module_from_openapi tool handler -fn test_generate_module_from_openapi_tool_handler_error() { - // Create arguments with a non-existent file path - mut arguments := map[string]json2.Any{} - arguments['openapi_path'] = json2.Any('non_existent_file.yaml') - - // Call the handler - result := generate_module_from_openapi_tool_handler(arguments) or { - // If the handler returns an error, that's expected - assert err.msg().contains(''), 'Error message should not be empty' - return - } - - // If we get here, the handler should have returned an error result - assert result.is_error, 'Result should indicate an error' - assert result.content.len > 0, 'Error content should not be empty' - assert result.content[0].typ == 'text', 'Error content should be of type "text"' - assert result.content[0].text.contains('failed to open file'), 'Error content should contain "failed to open file", instead ${result.content[0].text}' -} - -// test_mcp_tool_call_integration tests the integration of the tool with the MCP server -fn test_mcp_tool_call_integration() { - // Create a new MCP server - mut server := new_mcp_server() or { - assert false, 'Failed to create MCP server: ${err}' - return - } - - // Create a temporary OpenAPI file for testing - temp_dir := os.temp_dir() - temp_file := os.join_path(temp_dir, 'test_openapi.yaml') - os.write_file(temp_file, 'openapi: 3.0.0\ninfo:\n title: Test API\n version: 1.0.0\npaths:\n /test:\n get:\n summary: Test endpoint\n responses:\n "200":\n description: OK') or { - assert false, 'Failed to create temporary file: ${err}' - return - } - - // Sample tool call request - tool_call_request := '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"generate_module_from_openapi","arguments":{"openapi_path":"${temp_file}"}}}' - - // Process the request through the handler - response := server.handler.handle(tool_call_request) or { - // Clean up the temporary file - os.rm(temp_file) or {} - - // If the handler returns an error, that's expected in this test environment - // since we might not have all dependencies set up - return - } - - // Clean up the temporary file - os.rm(temp_file) or {} - - // Decode the response to verify its structure - decoded_response := jsonrpc.decode_response(response) or { - // In a test environment, we might get an error due to missing dependencies - // This is acceptable for this test - return - } - - // If we got a successful response, verify it - if !decoded_response.is_error() { - // Parse the result to verify its contents - result_json := decoded_response.result() or { - assert false, 'Failed to get result: ${err}' - return - } - - // Decode the result to check the content - result_map := json2.raw_decode(result_json) or { - assert false, 'Failed to decode result: ${err}' - return - }.as_map() - - // Verify the result structure - assert 'isError' in result_map, 'Result should have isError field' - assert 'content' in result_map, 'Result should have content field' - } -} diff --git a/libarchive/mcp_baobab/command.v b/libarchive/mcp_baobab/command.v deleted file mode 100644 index d75a67c8..00000000 --- a/libarchive/mcp_baobab/command.v +++ /dev/null @@ -1,22 +0,0 @@ -module baobab - -import cli - -pub const command = cli.Command{ - sort_flags: true - name: 'baobab' - // execute: cmd_mcpgen - description: 'baobab command' - commands: [ - cli.Command{ - name: 'start' - execute: cmd_start - description: 'start the Baobab server' - }, - ] -} - -fn cmd_start(cmd cli.Command) ! { - mut server := new_mcp_server(&Baobab{})! - server.start()! -} diff --git a/libarchive/mcp_baobab/mcp_test.v b/libarchive/mcp_baobab/mcp_test.v deleted file mode 100644 index 728342ea..00000000 --- a/libarchive/mcp_baobab/mcp_test.v +++ /dev/null @@ -1,128 +0,0 @@ -module baobab - -import incubaid.herolib.ai.mcp -import incubaid.herolib.schemas.jsonrpc -import json -import x.json2 - -// This file contains tests for the Baobab MCP server implementation. -// It tests the server's ability to initialize and handle tool calls. - -// test_new_mcp_server tests the creation of a new MCP server for the Baobab module -fn test_new_mcp_server() { - // Create a new MCP server - mut server := new_mcp_server() or { - assert false, 'Failed to create MCP server: ${err}' - return - } - - // Verify server info - assert server.server_info.name == 'developer', 'Server name should be "developer"' - assert server.server_info.version == '1.0.0', 'Server version should be 1.0.0' - - // Verify server capabilities - assert server.capabilities.prompts.list_changed == true, 'Prompts capability should have list_changed set to true' - assert server.capabilities.resources.subscribe == true, 'Resources capability should have subscribe set to true' - assert server.capabilities.resources.list_changed == true, 'Resources capability should have list_changed set to true' - assert server.capabilities.tools.list_changed == true, 'Tools capability should have list_changed set to true' -} - -// test_mcp_server_initialize tests the initialize handler with a sample initialize request -fn test_mcp_server_initialize() { - // Create a new MCP server - mut server := new_mcp_server() or { - assert false, 'Failed to create MCP server: ${err}' - return - } - - // Sample initialize request from the MCP specification - initialize_request := '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{"sampling":{},"roots":{"listChanged":true}},"clientInfo":{"name":"mcp-inspector","version":"0.0.1"}}}' - - // Process the request through the handler - response := server.handler.handle(initialize_request) or { - assert false, 'Handler failed to process request: ${err}' - return - } - - // Decode the response to verify its structure - decoded_response := jsonrpc.decode_response(response) or { - assert false, 'Failed to decode response: ${err}' - return - } - - // Verify that the response is not an error - assert !decoded_response.is_error(), 'Response should not be an error' - - // Parse the result to verify its contents - result_json := decoded_response.result() or { - assert false, 'Failed to get result: ${err}' - return - } - - // Decode the result into an ServerConfiguration struct - result := json.decode(mcp.ServerConfiguration, result_json) or { - assert false, 'Failed to decode result: ${err}' - return - } - - // Verify the protocol version matches what was requested - assert result.protocol_version == '2024-11-05', 'Protocol version should match the request' - - // Verify server info - assert result.server_info.name == 'developer', 'Server name should be "developer"' -} - -// test_tools_list tests the tools/list handler to verify tool registration -fn test_tools_list() { - // Create a new MCP server - mut server := new_mcp_server() or { - assert false, 'Failed to create MCP server: ${err}' - return - } - - // Sample tools/list request - tools_list_request := '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"cursor":""}}' - - // Process the request through the handler - response := server.handler.handle(tools_list_request) or { - assert false, 'Handler failed to process request: ${err}' - return - } - - // Decode the response to verify its structure - decoded_response := jsonrpc.decode_response(response) or { - assert false, 'Failed to decode response: ${err}' - return - } - - // Verify that the response is not an error - assert !decoded_response.is_error(), 'Response should not be an error' - - // Parse the result to verify its contents - result_json := decoded_response.result() or { - assert false, 'Failed to get result: ${err}' - return - } - - // Decode the result into a map to check the tools - result_map := json2.raw_decode(result_json) or { - assert false, 'Failed to decode result: ${err}' - return - }.as_map() - - // Verify that the tools array exists and contains the expected tool - tools := result_map['tools'].arr() - assert tools.len > 0, 'Tools list should not be empty' - - // Find the generate_module_from_openapi tool - mut found_tool := false - for tool in tools { - tool_map := tool.as_map() - if tool_map['name'].str() == 'generate_module_from_openapi' { - found_tool = true - break - } - } - - assert found_tool, 'generate_module_from_openapi tool should be registered' -} diff --git a/libarchive/mcp_baobab/server.v b/libarchive/mcp_baobab/server.v deleted file mode 100644 index 15b28c3c..00000000 --- a/libarchive/mcp_baobab/server.v +++ /dev/null @@ -1,38 +0,0 @@ -module baobab - -import incubaid.herolib.ai.mcp -import incubaid.herolib.ai.mcp.logger -import incubaid.herolib.schemas.jsonrpc - -@[heap] -pub struct Baobab {} - -pub fn new_mcp_server(v &Baobab) !&mcp.Server { - logger.info('Creating new Baobab MCP server') - - // Initialize the server with the empty handlers map - mut server := mcp.new_server(mcp.MemoryBackend{ - tools: { - 'generate_module_from_openapi': generate_module_from_openapi_tool - 'generate_methods_file': generate_methods_file_tool - 'generate_methods_interface_file': generate_methods_interface_file_tool - 'generate_model_file': generate_model_file_tool - 'generate_methods_example_file': generate_methods_example_file_tool - } - tool_handlers: { - 'generate_module_from_openapi': v.generate_module_from_openapi_tool_handler - 'generate_methods_file': v.generate_methods_file_tool_handler - 'generate_methods_interface_file': v.generate_methods_interface_file_tool_handler - 'generate_model_file': v.generate_model_file_tool_handler - 'generate_methods_example_file': v.generate_methods_example_file_tool_handler - } - }, mcp.ServerParams{ - config: mcp.ServerConfiguration{ - server_info: mcp.ServerInfo{ - name: 'baobab' - version: '1.0.0' - } - } - })! - return server -} diff --git a/libarchive/mcp_old/README.md b/libarchive/mcp_old/README.md deleted file mode 100644 index c639c6bb..00000000 --- a/libarchive/mcp_old/README.md +++ /dev/null @@ -1,113 +0,0 @@ -# Model Context Protocol (MCP) Implementation - -This module provides a V language implementation of the [Model Context Protocol (MCP)](https://spec.modelcontextprotocol.io/specification/2024-11-05/) specification. MCP is a protocol designed to standardize communication between AI models and their context providers. - -## Overview - -The MCP module serves as a core library for building MCP-compliant servers in V. Its main purpose is to provide all the boilerplate MCP functionality, so implementers only need to define and register their specific handlers. The module supports **both local (STDIO) and remote (HTTP/REST) transports**, enabling standardized communication between AI models and their context providers. - -The module implements all the required MCP protocol methods (resources/list, tools/list, prompts/list, etc.) and manages the underlying JSON-RPC communication, allowing developers to focus solely on implementing their specific tools and handlers. The module itself is not a standalone server but rather a framework that can be used to build different MCP server implementations. The subdirectories within this module (such as `baobab` and `developer`) contain specific implementations of MCP servers that utilize this core framework. - -## 🚀 HTTP/REST Transport Support - -**NEW**: MCP servers can now run in HTTP mode for remote access: - -```bash -# Local mode (traditional STDIO) -./your_mcp_server.vsh - -# Remote mode (HTTP/REST) -./your_mcp_server.vsh --http --port 8080 -``` - -### Key Benefits - -- 🔌 **VS Code Integration**: Connect coding agents via HTTP URL -- 🌐 **Remote Deployment**: Run on servers, access from anywhere -- 📱 **Web Integration**: Call from web applications via REST API -- ⚖️ **Scalability**: Multiple instances, load balancing support - -### Available Endpoints - -- `POST /jsonrpc` - JSON-RPC over HTTP -- `GET /api/tools` - List available tools (REST) -- `POST /api/tools/:name/call` - Call tools (REST) -- `GET /health` - Health check - -### Example Usage - -```bash -# Start HTTP server -./examples/mcp/http_demo/server.vsh --http --port 8080 - -# Test via REST API -curl -X POST http://localhost:8080/api/tools/calculator/call \ - -H "Content-Type: application/json" \ - -d '{"operation":"add","num1":10,"num2":5}' - -# Test via JSON-RPC -curl -X POST http://localhost:8080/jsonrpc \ - -H "Content-Type: application/json" \ - -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' -``` - -See `examples/mcp/http_demo/` for a complete working example. - -## to test - -``` -curl -fsSL https://bun.sh/install | bash - source /root/.bashrc -``` - -## Key Components - -- **Server**: The main MCP server struct that handles JSON-RPC requests and responses -- **Backend Interface**: Abstraction for different backend implementations (memory-based by default) -- **Model Configuration**: Structures representing client and server capabilities according to the MCP specification -- **Protocol Handlers**: Implementation of MCP protocol handlers for resources, prompts, tools, and initialization -- **Factory**: Functions to create and configure an MCP server with custom backends and handlers - -## Features - -- Complete implementation of the MCP protocol version 2024-11-05 -- Handles all boilerplate protocol methods (resources/list, tools/list, prompts/list, etc.) -- JSON-RPC based communication layer with automatic request/response handling -- Support for client-server capability negotiation -- Pluggable backend system for different storage and processing needs -- Generic type conversion utilities for MCP tool content -- Comprehensive error handling -- Logging capabilities -- Minimal implementation requirements for server developers - -## Usage - -To create a new MCP server using the core module: - -```v -import incubaid.herolib.mcp -import incubaid.herolib.schemas.jsonrpc - -// Create a backend (memory-based or custom implementation) -backend := mcp.MemoryBackend{ - tools: { - 'my_tool': my_tool_definition - } - tool_handlers: { - 'my_tool': my_tool_handler - } -} - -// Create and configure the server -mut server := mcp.new_server(backend, mcp.ServerParams{ - config: mcp.ServerConfiguration{ - server_info: mcp.ServerInfo{ - name: 'my_mcp_server' - version: '1.0.0' - } - } -})! - -// Start the server -server.start()! -``` diff --git a/libarchive/mcp_old/backend_interface.v b/libarchive/mcp_old/backend_interface.v deleted file mode 100644 index 4ba0fe24..00000000 --- a/libarchive/mcp_old/backend_interface.v +++ /dev/null @@ -1,29 +0,0 @@ -module mcp - -import x.json2 - -interface Backend { - // Resource methods - resource_exists(uri string) !bool - resource_get(uri string) !Resource - resource_list() ![]Resource - resource_subscribed(uri string) !bool - resource_contents_get(uri string) ![]ResourceContent - resource_templates_list() ![]ResourceTemplate - - // Prompt methods - prompt_exists(name string) !bool - prompt_get(name string) !Prompt - prompt_call(name string, arguments []string) ![]PromptMessage - prompt_list() ![]Prompt - prompt_messages_get(name string, arguments map[string]string) ![]PromptMessage - - // Tool methods - tool_exists(name string) !bool - tool_get(name string) !Tool - tool_list() ![]Tool - tool_call(name string, arguments map[string]json2.Any) !ToolCallResult -mut: - resource_subscribe(uri string) ! - resource_unsubscribe(uri string) ! -} diff --git a/libarchive/mcp_old/backend_memory.v b/libarchive/mcp_old/backend_memory.v deleted file mode 100644 index 862cc323..00000000 --- a/libarchive/mcp_old/backend_memory.v +++ /dev/null @@ -1,151 +0,0 @@ -module mcp - -import x.json2 - -pub struct MemoryBackend { -pub mut: - // Resource related fields - resources map[string]Resource - subscriptions []string // list of subscribed resource uri's - resource_contents map[string][]ResourceContent - resource_templates map[string]ResourceTemplate - - // Prompt related fields - prompts map[string]Prompt - prompt_messages map[string][]PromptMessage - prompt_handlers map[string]PromptHandler - - // Tool related fields - tools map[string]Tool - tool_handlers map[string]ToolHandler -} - -pub type ToolHandler = fn (arguments map[string]json2.Any) !ToolCallResult - -pub type PromptHandler = fn (arguments []string) ![]PromptMessage - -fn (b &MemoryBackend) resource_exists(uri string) !bool { - return uri in b.resources -} - -fn (b &MemoryBackend) resource_get(uri string) !Resource { - return b.resources[uri] or { return error('resource not found') } -} - -fn (b &MemoryBackend) resource_list() ![]Resource { - return b.resources.values() -} - -fn (mut b MemoryBackend) resource_subscribe(uri string) ! { - if uri !in b.subscriptions { - b.subscriptions << uri - } -} - -fn (b &MemoryBackend) resource_subscribed(uri string) !bool { - return uri in b.subscriptions -} - -fn (mut b MemoryBackend) resource_unsubscribe(uri string) ! { - b.subscriptions = b.subscriptions.filter(it != uri) -} - -fn (b &MemoryBackend) resource_contents_get(uri string) ![]ResourceContent { - return b.resource_contents[uri] or { return error('resource contents not found') } -} - -fn (b &MemoryBackend) resource_templates_list() ![]ResourceTemplate { - return b.resource_templates.values() -} - -// Prompt related methods - -fn (b &MemoryBackend) prompt_exists(name string) !bool { - return name in b.prompts -} - -fn (b &MemoryBackend) prompt_get(name string) !Prompt { - return b.prompts[name] or { return error('prompt not found') } -} - -fn (b &MemoryBackend) prompt_list() ![]Prompt { - return b.prompts.values() -} - -fn (b &MemoryBackend) prompt_messages_get(name string, arguments map[string]string) ![]PromptMessage { - // Get the base messages for this prompt - base_messages := b.prompt_messages[name] or { return error('prompt messages not found') } - - // Apply arguments to the messages - mut messages := []PromptMessage{} - - for msg in base_messages { - mut content := msg.content - - // If the content is text, replace argument placeholders - if content.typ == 'text' { - mut text := content.text - - // Replace each argument in the text - for arg_name, arg_value in arguments { - text = text.replace('{{${arg_name}}}', arg_value) - } - - content = PromptContent{ - typ: content.typ - text: text - data: content.data - mimetype: content.mimetype - resource: content.resource - } - } - - messages << PromptMessage{ - role: msg.role - content: content - } - } - - return messages -} - -fn (b &MemoryBackend) prompt_call(name string, arguments []string) ![]PromptMessage { - // Get the tool handler - handler := b.prompt_handlers[name] or { return error('tool handler not found') } - - // Call the handler with the provided arguments - return handler(arguments) or { panic(err) } -} - -// Tool related methods - -fn (b &MemoryBackend) tool_exists(name string) !bool { - return name in b.tools -} - -fn (b &MemoryBackend) tool_get(name string) !Tool { - return b.tools[name] or { return error('tool not found') } -} - -fn (b &MemoryBackend) tool_list() ![]Tool { - return b.tools.values() -} - -fn (b &MemoryBackend) tool_call(name string, arguments map[string]json2.Any) !ToolCallResult { - // Get the tool handler - handler := b.tool_handlers[name] or { return error('tool handler not found') } - - // Call the handler with the provided arguments - return handler(arguments) or { - // If the handler throws an error, return it as a tool error - return ToolCallResult{ - is_error: true - content: [ - ToolContent{ - typ: 'text' - text: 'Error: ${err.msg()}' - }, - ] - } - } -} diff --git a/libarchive/mcp_old/baobab/README.md b/libarchive/mcp_old/baobab/README.md deleted file mode 100644 index 64fa9914..00000000 --- a/libarchive/mcp_old/baobab/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## Baobab MCP - -The Base Object and Actor Backend MCP Server provides tools to easily generate BaObAB modules for a given OpenAPI or OpenRPC Schema. \ No newline at end of file diff --git a/libarchive/mcp_old/baobab/baobab_tools.v b/libarchive/mcp_old/baobab/baobab_tools.v deleted file mode 100644 index adffd64c..00000000 --- a/libarchive/mcp_old/baobab/baobab_tools.v +++ /dev/null @@ -1,172 +0,0 @@ -module baobab - -import incubaid.herolib.mcp -import incubaid.herolib.schemas.jsonschema -import incubaid.herolib.develop.codetools as code -import x.json2 as json { Any } -import incubaid.herolib.baobab.generator -import incubaid.herolib.baobab.specification - -// generate_methods_file MCP Tool -// - -const generate_methods_file_tool = mcp.Tool{ - name: 'generate_methods_file' - description: 'Generates a methods file with methods for a backend corresponding to thos specified in an OpenAPI or OpenRPC specification' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'source': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'object' - properties: { - 'openapi_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'openrpc_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - }) - } - required: ['source'] - } -} - -pub fn (d &Baobab) generate_methods_file_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - source := json.decode[generator.Source](arguments['source'].str())! - result := generator.generate_methods_file_str(source) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// generate_module_from_openapi MCP Tool -const generate_module_from_openapi_tool = mcp.Tool{ - name: 'generate_module_from_openapi' - description: '' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'openapi_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['openapi_path'] - } -} - -pub fn (d &Baobab) generate_module_from_openapi_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - openapi_path := arguments['openapi_path'].str() - result := generator.generate_module_from_openapi(openapi_path) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// generate_methods_interface_file MCP Tool -const generate_methods_interface_file_tool = mcp.Tool{ - name: 'generate_methods_interface_file' - description: 'Generates a methods interface file with method interfaces for a backend corresponding to those specified in an OpenAPI or OpenRPC specification' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'source': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'object' - properties: { - 'openapi_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'openrpc_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - }) - } - required: ['source'] - } -} - -pub fn (d &Baobab) generate_methods_interface_file_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - source := json.decode[generator.Source](arguments['source'].str())! - result := generator.generate_methods_interface_file_str(source) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// generate_model_file MCP Tool -const generate_model_file_tool = mcp.Tool{ - name: 'generate_model_file' - description: 'Generates a model file with data structures for a backend corresponding to those specified in an OpenAPI or OpenRPC specification' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'source': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'object' - properties: { - 'openapi_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'openrpc_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - }) - } - required: ['source'] - } -} - -pub fn (d &Baobab) generate_model_file_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - source := json.decode[generator.Source](arguments['source'].str())! - result := generator.generate_model_file_str(source) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// generate_methods_example_file MCP Tool -const generate_methods_example_file_tool = mcp.Tool{ - name: 'generate_methods_example_file' - description: 'Generates a methods example file with example implementations for a backend corresponding to those specified in an OpenAPI or OpenRPC specification' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'source': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'object' - properties: { - 'openapi_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'openrpc_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - }) - } - required: ['source'] - } -} - -pub fn (d &Baobab) generate_methods_example_file_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - source := json.decode[generator.Source](arguments['source'].str())! - result := generator.generate_methods_example_file_str(source) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} diff --git a/libarchive/mcp_old/baobab/baobab_tools_test.v b/libarchive/mcp_old/baobab/baobab_tools_test.v deleted file mode 100644 index 45311810..00000000 --- a/libarchive/mcp_old/baobab/baobab_tools_test.v +++ /dev/null @@ -1,101 +0,0 @@ -module baobab - -import incubaid.herolib.mcp -import incubaid.herolib.schemas.jsonrpc -import json -import x.json2 -import os - -// This file contains tests for the Baobab tools implementation. -// It tests the tools' ability to handle tool calls and return expected results. - -// test_generate_module_from_openapi_tool tests the generate_module_from_openapi tool definition -fn test_generate_module_from_openapi_tool() { - // Verify the tool definition - assert generate_module_from_openapi_tool.name == 'generate_module_from_openapi', 'Tool name should be "generate_module_from_openapi"' - - // Verify the input schema - assert generate_module_from_openapi_tool.input_schema.typ == 'object', 'Input schema type should be "object"' - assert 'openapi_path' in generate_module_from_openapi_tool.input_schema.properties, 'Input schema should have "openapi_path" property' - assert generate_module_from_openapi_tool.input_schema.properties['openapi_path'].typ == 'string', 'openapi_path property should be of type "string"' - assert 'openapi_path' in generate_module_from_openapi_tool.input_schema.required, 'openapi_path should be a required property' -} - -// test_generate_module_from_openapi_tool_handler_error tests the error handling of the generate_module_from_openapi tool handler -fn test_generate_module_from_openapi_tool_handler_error() { - // Create arguments with a non-existent file path - mut arguments := map[string]json2.Any{} - arguments['openapi_path'] = json2.Any('non_existent_file.yaml') - - // Call the handler - result := generate_module_from_openapi_tool_handler(arguments) or { - // If the handler returns an error, that's expected - assert err.msg().contains(''), 'Error message should not be empty' - return - } - - // If we get here, the handler should have returned an error result - assert result.is_error, 'Result should indicate an error' - assert result.content.len > 0, 'Error content should not be empty' - assert result.content[0].typ == 'text', 'Error content should be of type "text"' - assert result.content[0].text.contains('failed to open file'), 'Error content should contain "failed to open file", instead ${result.content[0].text}' -} - -// test_mcp_tool_call_integration tests the integration of the tool with the MCP server -fn test_mcp_tool_call_integration() { - // Create a new MCP server - mut server := new_mcp_server() or { - assert false, 'Failed to create MCP server: ${err}' - return - } - - // Create a temporary OpenAPI file for testing - temp_dir := os.temp_dir() - temp_file := os.join_path(temp_dir, 'test_openapi.yaml') - os.write_file(temp_file, 'openapi: 3.0.0\ninfo:\n title: Test API\n version: 1.0.0\npaths:\n /test:\n get:\n summary: Test endpoint\n responses:\n "200":\n description: OK') or { - assert false, 'Failed to create temporary file: ${err}' - return - } - - // Sample tool call request - tool_call_request := '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"generate_module_from_openapi","arguments":{"openapi_path":"${temp_file}"}}}' - - // Process the request through the handler - response := server.handler.handle(tool_call_request) or { - // Clean up the temporary file - os.rm(temp_file) or {} - - // If the handler returns an error, that's expected in this test environment - // since we might not have all dependencies set up - return - } - - // Clean up the temporary file - os.rm(temp_file) or {} - - // Decode the response to verify its structure - decoded_response := jsonrpc.decode_response(response) or { - // In a test environment, we might get an error due to missing dependencies - // This is acceptable for this test - return - } - - // If we got a successful response, verify it - if !decoded_response.is_error() { - // Parse the result to verify its contents - result_json := decoded_response.result() or { - assert false, 'Failed to get result: ${err}' - return - } - - // Decode the result to check the content - result_map := json2.raw_decode(result_json) or { - assert false, 'Failed to decode result: ${err}' - return - }.as_map() - - // Verify the result structure - assert 'isError' in result_map, 'Result should have isError field' - assert 'content' in result_map, 'Result should have content field' - } -} diff --git a/libarchive/mcp_old/baobab/command.v b/libarchive/mcp_old/baobab/command.v deleted file mode 100644 index d75a67c8..00000000 --- a/libarchive/mcp_old/baobab/command.v +++ /dev/null @@ -1,22 +0,0 @@ -module baobab - -import cli - -pub const command = cli.Command{ - sort_flags: true - name: 'baobab' - // execute: cmd_mcpgen - description: 'baobab command' - commands: [ - cli.Command{ - name: 'start' - execute: cmd_start - description: 'start the Baobab server' - }, - ] -} - -fn cmd_start(cmd cli.Command) ! { - mut server := new_mcp_server(&Baobab{})! - server.start()! -} diff --git a/libarchive/mcp_old/baobab/mcp_test.v b/libarchive/mcp_old/baobab/mcp_test.v deleted file mode 100644 index bfc97bb1..00000000 --- a/libarchive/mcp_old/baobab/mcp_test.v +++ /dev/null @@ -1,128 +0,0 @@ -module baobab - -import incubaid.herolib.mcp -import incubaid.herolib.schemas.jsonrpc -import json -import x.json2 - -// This file contains tests for the Baobab MCP server implementation. -// It tests the server's ability to initialize and handle tool calls. - -// test_new_mcp_server tests the creation of a new MCP server for the Baobab module -fn test_new_mcp_server() { - // Create a new MCP server - mut server := new_mcp_server() or { - assert false, 'Failed to create MCP server: ${err}' - return - } - - // Verify server info - assert server.server_info.name == 'developer', 'Server name should be "developer"' - assert server.server_info.version == '1.0.0', 'Server version should be 1.0.0' - - // Verify server capabilities - assert server.capabilities.prompts.list_changed == true, 'Prompts capability should have list_changed set to true' - assert server.capabilities.resources.subscribe == true, 'Resources capability should have subscribe set to true' - assert server.capabilities.resources.list_changed == true, 'Resources capability should have list_changed set to true' - assert server.capabilities.tools.list_changed == true, 'Tools capability should have list_changed set to true' -} - -// test_mcp_server_initialize tests the initialize handler with a sample initialize request -fn test_mcp_server_initialize() { - // Create a new MCP server - mut server := new_mcp_server() or { - assert false, 'Failed to create MCP server: ${err}' - return - } - - // Sample initialize request from the MCP specification - initialize_request := '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{"sampling":{},"roots":{"listChanged":true}},"clientInfo":{"name":"mcp-inspector","version":"0.0.1"}}}' - - // Process the request through the handler - response := server.handler.handle(initialize_request) or { - assert false, 'Handler failed to process request: ${err}' - return - } - - // Decode the response to verify its structure - decoded_response := jsonrpc.decode_response(response) or { - assert false, 'Failed to decode response: ${err}' - return - } - - // Verify that the response is not an error - assert !decoded_response.is_error(), 'Response should not be an error' - - // Parse the result to verify its contents - result_json := decoded_response.result() or { - assert false, 'Failed to get result: ${err}' - return - } - - // Decode the result into an ServerConfiguration struct - result := json.decode(mcp.ServerConfiguration, result_json) or { - assert false, 'Failed to decode result: ${err}' - return - } - - // Verify the protocol version matches what was requested - assert result.protocol_version == '2024-11-05', 'Protocol version should match the request' - - // Verify server info - assert result.server_info.name == 'developer', 'Server name should be "developer"' -} - -// test_tools_list tests the tools/list handler to verify tool registration -fn test_tools_list() { - // Create a new MCP server - mut server := new_mcp_server() or { - assert false, 'Failed to create MCP server: ${err}' - return - } - - // Sample tools/list request - tools_list_request := '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"cursor":""}}' - - // Process the request through the handler - response := server.handler.handle(tools_list_request) or { - assert false, 'Handler failed to process request: ${err}' - return - } - - // Decode the response to verify its structure - decoded_response := jsonrpc.decode_response(response) or { - assert false, 'Failed to decode response: ${err}' - return - } - - // Verify that the response is not an error - assert !decoded_response.is_error(), 'Response should not be an error' - - // Parse the result to verify its contents - result_json := decoded_response.result() or { - assert false, 'Failed to get result: ${err}' - return - } - - // Decode the result into a map to check the tools - result_map := json2.raw_decode(result_json) or { - assert false, 'Failed to decode result: ${err}' - return - }.as_map() - - // Verify that the tools array exists and contains the expected tool - tools := result_map['tools'].arr() - assert tools.len > 0, 'Tools list should not be empty' - - // Find the generate_module_from_openapi tool - mut found_tool := false - for tool in tools { - tool_map := tool.as_map() - if tool_map['name'].str() == 'generate_module_from_openapi' { - found_tool = true - break - } - } - - assert found_tool, 'generate_module_from_openapi tool should be registered' -} diff --git a/libarchive/mcp_old/baobab/server.v b/libarchive/mcp_old/baobab/server.v deleted file mode 100644 index 1fa56cea..00000000 --- a/libarchive/mcp_old/baobab/server.v +++ /dev/null @@ -1,38 +0,0 @@ -module baobab - -import incubaid.herolib.mcp -import incubaid.herolib.mcp.logger -import incubaid.herolib.schemas.jsonrpc - -@[heap] -pub struct Baobab {} - -pub fn new_mcp_server(v &Baobab) !&mcp.Server { - logger.info('Creating new Baobab MCP server') - - // Initialize the server with the empty handlers map - mut server := mcp.new_server(mcp.MemoryBackend{ - tools: { - 'generate_module_from_openapi': generate_module_from_openapi_tool - 'generate_methods_file': generate_methods_file_tool - 'generate_methods_interface_file': generate_methods_interface_file_tool - 'generate_model_file': generate_model_file_tool - 'generate_methods_example_file': generate_methods_example_file_tool - } - tool_handlers: { - 'generate_module_from_openapi': v.generate_module_from_openapi_tool_handler - 'generate_methods_file': v.generate_methods_file_tool_handler - 'generate_methods_interface_file': v.generate_methods_interface_file_tool_handler - 'generate_model_file': v.generate_model_file_tool_handler - 'generate_methods_example_file': v.generate_methods_example_file_tool_handler - } - }, mcp.ServerParams{ - config: mcp.ServerConfiguration{ - server_info: mcp.ServerInfo{ - name: 'baobab' - version: '1.0.0' - } - } - })! - return server -} diff --git a/libarchive/mcp_old/cmd/compile.vsh b/libarchive/mcp_old/cmd/compile.vsh deleted file mode 100755 index 687d701f..00000000 --- a/libarchive/mcp_old/cmd/compile.vsh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env -S v -n -cg -w -parallel-cc -enable-globals run - -import os -import flag - -mut fp := flag.new_flag_parser(os.args) -fp.application('compile.vsh') -fp.version('v0.1.0') -fp.description('Compile MCP binary in debug or production mode') -fp.skip_executable() - -prod_mode := fp.bool('prod', `p`, false, 'Build production version (optimized)') -help_requested := fp.bool('help', `h`, false, 'Show help message') - -if help_requested { - println(fp.usage()) - exit(0) -} - -additional_args := fp.finalize() or { - eprintln(err) - println(fp.usage()) - exit(1) -} - -if additional_args.len > 0 { - eprintln('Unexpected arguments: ${additional_args.join(' ')}') - println(fp.usage()) - exit(1) -} - -// Change to the mcp directory -mcp_dir := os.dir(os.real_path(os.executable())) -os.chdir(mcp_dir) or { panic('Failed to change directory to ${mcp_dir}: ${err}') } - -// Set MCPPATH based on OS -mut mcppath := '/usr/local/bin/mcp' -if os.user_os() == 'macos' { - mcppath = os.join_path(os.home_dir(), 'hero/bin/mcp') -} - -// Set compilation command based on OS and mode -compile_cmd := if prod_mode { - 'v -enable-globals -w -n -prod mcp.v' -} else { - 'v -w -cg -gc none -cc tcc -d use_openssl -enable-globals mcp.v' -} - -println('Building MCP in ${if prod_mode { 'production' } else { 'debug' }} mode...') - -if os.system(compile_cmd) != 0 { - panic('Failed to compile mcp.v with command: ${compile_cmd}') -} - -// Make executable -os.chmod('mcp', 0o755) or { panic('Failed to make mcp binary executable: ${err}') } - -// Ensure destination directory exists -os.mkdir_all(os.dir(mcppath)) or { panic('Failed to create directory ${os.dir(mcppath)}: ${err}') } - -// Copy to destination paths -os.cp('mcp', mcppath) or { panic('Failed to copy mcp binary to ${mcppath}: ${err}') } -os.cp('mcp', '/tmp/mcp') or { panic('Failed to copy mcp binary to /tmp/mcp: ${err}') } - -// Clean up -os.rm('mcp') or { panic('Failed to remove temporary mcp binary: ${err}') } - -println('**MCP COMPILE OK**') diff --git a/libarchive/mcp_old/cmd/mcp.v b/libarchive/mcp_old/cmd/mcp.v deleted file mode 100644 index 62b036ee..00000000 --- a/libarchive/mcp_old/cmd/mcp.v +++ /dev/null @@ -1,91 +0,0 @@ -module main - -import os -import cli { Command, Flag } -import incubaid.herolib.osal.core as osal -// import incubaid.herolib.mcp.vcode -// import incubaid.herolib.mcp.mcpgen -// import incubaid.herolib.mcp.baobab -import incubaid.herolib.mcp.rhai.mcp as rhai_mcp - -fn main() { - do() or { panic(err) } -} - -pub fn do() ! { - mut cmd_mcp := Command{ - name: 'mcp' - usage: ' -## Manage your MCPs - -example: - -mcp - ' - description: 'create, edit, show mdbooks' - required_args: 0 - } - - // cmd_run_add_flags(mut cmd_publisher) - - cmd_mcp.add_flag(Flag{ - flag: .bool - required: false - name: 'debug' - abbrev: 'd' - description: 'show debug output' - }) - - cmd_mcp.add_flag(Flag{ - flag: .bool - required: false - name: 'verbose' - abbrev: 'v' - description: 'show verbose output' - }) - - mut cmd_inspector := Command{ - sort_flags: true - name: 'inspector' - execute: cmd_inspector_execute - description: 'will list existing mdbooks' - } - - cmd_inspector.add_flag(Flag{ - flag: .string - required: false - name: 'name' - abbrev: 'n' - description: 'name of the MCP' - }) - - cmd_inspector.add_flag(Flag{ - flag: .bool - required: false - name: 'open' - abbrev: 'o' - description: 'open inspector' - }) - - cmd_mcp.add_command(rhai_mcp.command) - // cmd_mcp.add_command(baobab.command) - // cmd_mcp.add_command(vcode.command) - cmd_mcp.add_command(cmd_inspector) - // cmd_mcp.add_command(vcode.command) - cmd_mcp.setup() - cmd_mcp.parse(os.args) -} - -fn cmd_inspector_execute(cmd Command) ! { - open := cmd.flags.get_bool('open') or { false } - if open { - osal.exec(cmd: 'open http://localhost:5173')! - } - name := cmd.flags.get_string('name') or { '' } - if name.len > 0 { - println('starting inspector for MCP ${name}') - osal.exec(cmd: 'npx @modelcontextprotocol/inspector mcp ${name} start')! - } else { - osal.exec(cmd: 'npx @modelcontextprotocol/inspector')! - } -} diff --git a/libarchive/mcp_old/factory.v b/libarchive/mcp_old/factory.v deleted file mode 100644 index 349e717e..00000000 --- a/libarchive/mcp_old/factory.v +++ /dev/null @@ -1,152 +0,0 @@ -module mcp - -import incubaid.herolib.schemas.jsonrpc -import incubaid.herolib.mcp.transport - -// Wrapper functions to convert string-based handlers to jsonrpc.Request/Response format -// We reconstruct the original JSON to avoid double-encoding issues - -fn create_initialize_wrapper(mut server Server) jsonrpc.ProcedureHandler { - return fn [mut server] (request jsonrpc.Request) !jsonrpc.Response { - // Reconstruct the original JSON from the request object - original_json := '{"jsonrpc":"${request.jsonrpc}","method":"${request.method}","params":${request.params},"id":${request.id}}' - response_str := server.initialize_handler(original_json)! - return jsonrpc.decode_response(response_str)! - } -} - -fn create_initialized_notification_wrapper() jsonrpc.ProcedureHandler { - return fn (request jsonrpc.Request) !jsonrpc.Response { - original_json := '{"jsonrpc":"${request.jsonrpc}","method":"${request.method}","params":${request.params},"id":${request.id}}' - response_str := initialized_notification_handler(original_json)! - return jsonrpc.decode_response(response_str)! - } -} - -fn create_resources_list_wrapper(mut server Server) jsonrpc.ProcedureHandler { - return fn [mut server] (request jsonrpc.Request) !jsonrpc.Response { - original_json := '{"jsonrpc":"${request.jsonrpc}","method":"${request.method}","params":${request.params},"id":${request.id}}' - response_str := server.resources_list_handler(original_json)! - return jsonrpc.decode_response(response_str)! - } -} - -fn create_resources_read_wrapper(mut server Server) jsonrpc.ProcedureHandler { - return fn [mut server] (request jsonrpc.Request) !jsonrpc.Response { - original_json := '{"jsonrpc":"${request.jsonrpc}","method":"${request.method}","params":${request.params},"id":${request.id}}' - response_str := server.resources_read_handler(original_json)! - return jsonrpc.decode_response(response_str)! - } -} - -fn create_resources_templates_list_wrapper(mut server Server) jsonrpc.ProcedureHandler { - return fn [mut server] (request jsonrpc.Request) !jsonrpc.Response { - original_json := '{"jsonrpc":"${request.jsonrpc}","method":"${request.method}","params":${request.params},"id":${request.id}}' - response_str := server.resources_templates_list_handler(original_json)! - return jsonrpc.decode_response(response_str)! - } -} - -fn create_resources_subscribe_wrapper(mut server Server) jsonrpc.ProcedureHandler { - return fn [mut server] (request jsonrpc.Request) !jsonrpc.Response { - original_json := '{"jsonrpc":"${request.jsonrpc}","method":"${request.method}","params":${request.params},"id":${request.id}}' - response_str := server.resources_subscribe_handler(original_json)! - return jsonrpc.decode_response(response_str)! - } -} - -fn create_prompts_list_wrapper(mut server Server) jsonrpc.ProcedureHandler { - return fn [mut server] (request jsonrpc.Request) !jsonrpc.Response { - original_json := '{"jsonrpc":"${request.jsonrpc}","method":"${request.method}","params":${request.params},"id":${request.id}}' - response_str := server.prompts_list_handler(original_json)! - return jsonrpc.decode_response(response_str)! - } -} - -fn create_prompts_get_wrapper(mut server Server) jsonrpc.ProcedureHandler { - return fn [mut server] (request jsonrpc.Request) !jsonrpc.Response { - original_json := '{"jsonrpc":"${request.jsonrpc}","method":"${request.method}","params":${request.params},"id":${request.id}}' - response_str := server.prompts_get_handler(original_json)! - return jsonrpc.decode_response(response_str)! - } -} - -fn create_tools_list_wrapper(mut server Server) jsonrpc.ProcedureHandler { - return fn [mut server] (request jsonrpc.Request) !jsonrpc.Response { - original_json := '{"jsonrpc":"${request.jsonrpc}","method":"${request.method}","params":${request.params},"id":${request.id}}' - response_str := server.tools_list_handler(original_json)! - return jsonrpc.decode_response(response_str)! - } -} - -fn create_tools_call_wrapper(mut server Server) jsonrpc.ProcedureHandler { - return fn [mut server] (request jsonrpc.Request) !jsonrpc.Response { - original_json := '{"jsonrpc":"${request.jsonrpc}","method":"${request.method}","params":${request.params},"id":${request.id}}' - response_str := server.tools_call_handler(original_json)! - return jsonrpc.decode_response(response_str)! - } -} - -fn create_logging_set_level_wrapper(mut server Server) jsonrpc.ProcedureHandler { - return fn [mut server] (request jsonrpc.Request) !jsonrpc.Response { - original_json := '{"jsonrpc":"${request.jsonrpc}","method":"${request.method}","params":${request.params},"id":${request.id}}' - response_str := server.logging_set_level_handler(original_json)! - return jsonrpc.decode_response(response_str)! - } -} - -@[params] -pub struct ServerParams { -pub: - handlers map[string]jsonrpc.ProcedureHandler - config ServerConfiguration - transport transport.TransportConfig = transport.TransportConfig{ - mode: .stdio - } -} - -// new_server creates a new MCP server -pub fn new_server(backend Backend, params ServerParams) !&Server { - // Create the appropriate transport based on configuration - transport_impl := match params.transport.mode { - .stdio { - transport.new_stdio_transport() - } - .http { - transport.new_http_transport(params.transport.http) - } - } - - mut server := &Server{ - ServerConfiguration: params.config - backend: backend - transport: transport_impl - } - - // Create a handler with the core MCP procedures registered - handler := jsonrpc.new_handler(jsonrpc.Handler{ - procedures: { - // ...params.handlers, - // Core handlers - 'initialize': create_initialize_wrapper(mut server) - 'notifications/initialized': create_initialized_notification_wrapper() - // Logging handlers - 'logging/setLevel': create_logging_set_level_wrapper(mut server) - // Resource handlers - 'resources/list': create_resources_list_wrapper(mut server) - 'resources/read': create_resources_read_wrapper(mut server) - 'resources/templates/list': create_resources_templates_list_wrapper(mut server) - 'resources/subscribe': create_resources_subscribe_wrapper(mut server) - // Prompt handlers - 'prompts/list': create_prompts_list_wrapper(mut server) - 'prompts/get': create_prompts_get_wrapper(mut server) - 'completion/complete': create_prompts_get_wrapper(mut server) - // Tool handlers - 'tools/list': create_tools_list_wrapper(mut server) - 'tools/call': create_tools_call_wrapper(mut server) - } - })! - - server.handler = *handler - return server -} diff --git a/libarchive/mcp_old/generics.v b/libarchive/mcp_old/generics.v deleted file mode 100644 index a8411f1c..00000000 --- a/libarchive/mcp_old/generics.v +++ /dev/null @@ -1,52 +0,0 @@ -module mcp - -pub fn result_to_mcp_tool_contents[T](result T) []ToolContent { - return [result_to_mcp_tool_content(result)] -} - -pub fn result_to_mcp_tool_content[T](result T) ToolContent { - return $if T is string { - ToolContent{ - typ: 'text' - text: result.str() - } - } $else $if T is int { - ToolContent{ - typ: 'number' - number: result.int() - } - } $else $if T is bool { - ToolContent{ - typ: 'boolean' - boolean: result.bool() - } - } $else $if result is $array { - mut items := []ToolContent{} - for item in result { - items << result_to_mcp_tool_content(item) - } - return ToolContent{ - typ: 'array' - items: items - } - } $else $if T is $struct { - mut properties := map[string]ToolContent{} - $for field in T.fields { - properties[field.name] = result_to_mcp_tool_content(result.$(field.name)) - } - return ToolContent{ - typ: 'object' - properties: properties - } - } $else { - panic('Unsupported type: ${typeof(result)}') - } -} - -pub fn array_to_mcp_tool_contents[U](array []U) []ToolContent { - mut contents := []ToolContent{} - for item in array { - contents << result_to_mcp_tool_content(item) - } - return contents -} diff --git a/libarchive/mcp_old/handler_initialize.v b/libarchive/mcp_old/handler_initialize.v deleted file mode 100644 index c61f1e9f..00000000 --- a/libarchive/mcp_old/handler_initialize.v +++ /dev/null @@ -1,27 +0,0 @@ -module mcp - -import time -import os -import log -import x.json2 -import incubaid.herolib.schemas.jsonrpc - -// initialize_handler handles the initialize request according to the MCP specification -fn (mut s Server) initialize_handler(data string) !string { - // Decode the request with ClientConfiguration parameters - request := jsonrpc.decode_request_generic[ClientConfiguration](data)! - s.client_config = request.params - - // Create a success response with the result - response := jsonrpc.new_response_generic[ServerConfiguration](request.id, s.ServerConfiguration) - return response.encode() -} - -// initialized_notification_handler handles the initialized notification -// This notification is sent by the client after successful initialization -fn initialized_notification_handler(data string) !string { - // This is a notification, so no response is expected - // Just log that we received the notification - log.info('Received initialized notification') - return '' -} diff --git a/libarchive/mcp_old/handler_initialize_test.v b/libarchive/mcp_old/handler_initialize_test.v deleted file mode 100644 index 8634b34e..00000000 --- a/libarchive/mcp_old/handler_initialize_test.v +++ /dev/null @@ -1,103 +0,0 @@ -module mcp - -import incubaid.herolib.schemas.jsonrpc -import json - -// This file contains tests for the MCP initialize handler implementation. -// It tests the handler's ability to process initialize requests according to the MCP specification. - -// test_initialize_handler tests the initialize handler with a sample initialize request -fn test_initialize_handler() { - mut server := Server{} - - // Sample initialize request from the MCP specification - initialize_request := '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{"sampling":{},"roots":{"listChanged":true}},"clientInfo":{"name":"mcp-inspector","version":"0.0.1"}}}' - - // Call the initialize handler directly - response := server.initialize_handler(initialize_request) or { - assert false, 'Initialize handler failed: ${err}' - return - } - - // Decode the response to verify its structure - decoded_response := jsonrpc.decode_response(response) or { - assert false, 'Failed to decode response: ${err}' - return - } - - // Verify that the response is not an error - assert !decoded_response.is_error(), 'Response should not be an error' - - // Parse the result to verify its contents - result_json := decoded_response.result() or { - assert false, 'Failed to get result: ${err}' - return - } - - // Decode the result into an ServerConfiguration struct - result := json.decode(ServerConfiguration, result_json) or { - assert false, 'Failed to decode result: ${err}' - return - } - - // Verify the protocol version matches what was requested - assert result.protocol_version == '2024-11-05', 'Protocol version should match the request' - - // Verify server capabilities - assert result.capabilities.prompts.list_changed == true, 'Prompts capability should have list_changed set to true' - assert result.capabilities.resources.subscribe == true, 'Resources capability should have subscribe set to true' - assert result.capabilities.resources.list_changed == true, 'Resources capability should have list_changed set to true' - assert result.capabilities.tools.list_changed == true, 'Tools capability should have list_changed set to true' - - // Verify server info - assert result.server_info.name == 'HeroLibMCPServer', 'Server name should be HeroLibMCPServer' - assert result.server_info.version == '1.0.0', 'Server version should be 1.0.0' -} - -// test_initialize_handler_with_handler tests the initialize handler through the JSONRPC handler -fn test_initialize_handler_with_handler() { - mut server := Server{} - - // Create a handler with just the initialize procedure - handler := jsonrpc.new_handler(jsonrpc.Handler{ - procedures: { - 'initialize': server.initialize_handler - } - }) or { - assert false, 'Failed to create handler: ${err}' - return - } - - // Sample initialize request from the MCP specification - initialize_request := '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{"sampling":{},"roots":{"listChanged":true}},"clientInfo":{"name":"mcp-inspector","version":"0.0.1"}}}' - - // Process the request through the handler - response := handler.handle(initialize_request) or { - assert false, 'Handler failed to process request: ${err}' - return - } - - // Decode the response to verify its structure - decoded_response := jsonrpc.decode_response(response) or { - assert false, 'Failed to decode response: ${err}' - return - } - - // Verify that the response is not an error - assert !decoded_response.is_error(), 'Response should not be an error' - - // Parse the result to verify its contents - result_json := decoded_response.result() or { - assert false, 'Failed to get result: ${err}' - return - } - - // Decode the result into an ServerConfiguration struct - result := json.decode(ServerConfiguration, result_json) or { - assert false, 'Failed to decode result: ${err}' - return - } - - // Verify the protocol version matches what was requested - assert result.protocol_version == '2024-11-05', 'Protocol version should match the request' -} diff --git a/libarchive/mcp_old/handler_logging.v b/libarchive/mcp_old/handler_logging.v deleted file mode 100644 index 732d7ccd..00000000 --- a/libarchive/mcp_old/handler_logging.v +++ /dev/null @@ -1,37 +0,0 @@ -module mcp - -import x.json2 -import incubaid.herolib.schemas.jsonrpc - -// LogLevel represents the logging levels supported by MCP -pub enum LogLevel { - debug - info - notice - warning - error - critical - alert - emergency -} - -// SetLevelParams represents the parameters for the logging/setLevel method -pub struct SetLevelParams { -pub: - level LogLevel -} - -// logging_set_level_handler handles the logging/setLevel request -// This is a stub implementation that accepts the request but doesn't actually change logging behavior -fn (mut s Server) logging_set_level_handler(data string) !string { - // Decode the request with SetLevelParams - request := jsonrpc.decode_request_generic[SetLevelParams](data)! - - // For now, we just acknowledge the request without actually implementing logging level changes - // In a full implementation, this would configure the server's logging system - - // Create a success response with empty object (logging/setLevel returns {} on success) - empty_map := map[string]string{} - response := jsonrpc.new_response_generic[map[string]string](request.id, empty_map) - return response.encode() -} diff --git a/libarchive/mcp_old/handler_prompts.v b/libarchive/mcp_old/handler_prompts.v deleted file mode 100644 index 5782537d..00000000 --- a/libarchive/mcp_old/handler_prompts.v +++ /dev/null @@ -1,135 +0,0 @@ -module mcp - -import time -import os -import log -import x.json2 -import json -import incubaid.herolib.schemas.jsonrpc - -// Prompt related structs - -pub struct Prompt { -pub: - name string - description string - arguments []PromptArgument -} - -pub struct PromptArgument { -pub: - name string - description string - required bool -} - -pub struct PromptMessage { -pub: - role string - content PromptContent -} - -pub struct PromptContent { -pub: - typ string @[json: 'type'] - text string - data string - mimetype string @[json: 'mimeType'] - resource ResourceContent -} - -// Prompt List Handler - -pub struct PromptListParams { -pub: - cursor string -} - -pub struct PromptListResult { -pub: - prompts []Prompt - next_cursor string @[json: 'nextCursor'] -} - -// prompts_list_handler handles the prompts/list request -// This request is used to retrieve a list of available prompts -fn (mut s Server) prompts_list_handler(data string) !string { - // Decode the request with cursor parameter - request := jsonrpc.decode_request_generic[PromptListParams](data)! - cursor := request.params.cursor - - // TODO: Implement pagination logic using the cursor - // For now, return all prompts - - // Create a success response with the result - response := jsonrpc.new_response_generic[PromptListResult](request.id, PromptListResult{ - prompts: s.backend.prompt_list()! - next_cursor: '' // Empty if no more pages - }) - return response.encode() -} - -// Prompt Get Handler - -pub struct PromptGetParams { -pub: - name string - arguments map[string]string -} - -pub struct PromptGetResult { -pub: - description string - messages []PromptMessage -} - -// prompts_get_handler handles the prompts/get request -// This request is used to retrieve a specific prompt with arguments -fn (mut s Server) prompts_get_handler(data string) !string { - // Decode the request with name and arguments parameters - request_map := json2.raw_decode(data)!.as_map() - params_map := request_map['params'].as_map() - - if !s.backend.prompt_exists(params_map['name'].str())! { - return jsonrpc.new_error_response(request_map['id'].int(), prompt_not_found(params_map['name'].str())).encode() - } - - // Get the prompt by name - prompt := s.backend.prompt_get(params_map['name'].str())! - - // Validate required arguments - for arg in prompt.arguments { - if arg.required && params_map['arguments'].as_map()[arg.name].str() == '' { - return jsonrpc.new_error_response(request_map['id'].int(), missing_required_argument(arg.name)).encode() - } - } - - messages := s.backend.prompt_call(params_map['name'].str(), params_map['arguments'].as_map().values().map(it.str()))! - - // // Get the prompt messages with arguments applied - // messages := s.backend.prompt_messages_get(request.params.name, request.params.arguments)! - - // Create a success response with the result - response := jsonrpc.new_response_generic[PromptGetResult](request_map['id'].int(), - PromptGetResult{ - description: prompt.description - messages: messages - }) - return response.encode() -} - -// Prompt Notification Handlers - -// send_prompts_list_changed_notification sends a notification when the list of prompts changes -pub fn (mut s Server) send_prompts_list_changed_notification() ! { - // Check if the client supports this notification - if !s.client_config.capabilities.roots.list_changed { - return - } - - // Create a notification - notification := jsonrpc.new_blank_notification('notifications/prompts/list_changed') - s.send(json.encode(notification)) - // Send the notification to all connected clients - log.info('Sending prompts list changed notification: ${json.encode(notification)}') -} diff --git a/libarchive/mcp_old/handler_resources.v b/libarchive/mcp_old/handler_resources.v deleted file mode 100644 index d35e5119..00000000 --- a/libarchive/mcp_old/handler_resources.v +++ /dev/null @@ -1,182 +0,0 @@ -module mcp - -import json -import incubaid.herolib.schemas.jsonrpc - -pub struct Resource { -pub: - uri string - name string - description string - mimetype string @[json: 'mimeType'] -} - -// Resource List Handler - -pub struct ResourceListParams { -pub: - cursor string -} - -pub struct ResourceListResult { -pub: - resources []Resource - next_cursor string @[json: 'nextCursor'] -} - -// resources_list_handler handles the resources/list request -// This request is used to retrieve a list of available resources -fn (mut s Server) resources_list_handler(data string) !string { - // Decode the request with cursor parameter - request := jsonrpc.decode_request_generic[ResourceListParams](data)! - cursor := request.params.cursor - - // TODO: Implement pagination logic using the cursor - // For now, return all resources - - // Create a success response with the result - response := jsonrpc.new_response_generic[ResourceListResult](request.id, ResourceListResult{ - resources: s.backend.resource_list()! - next_cursor: '' // Empty if no more pages - }) - return response.encode() -} - -// Resource Read Handler - -pub struct ResourceReadParams { -pub: - uri string -} - -pub struct ResourceReadResult { -pub: - contents []ResourceContent -} - -pub struct ResourceContent { -pub: - uri string - mimetype string @[json: 'mimeType'] - text string - blob string // Base64-encoded binary data -} - -// resources_read_handler handles the resources/read request -// This request is used to retrieve the contents of a resource -fn (mut s Server) resources_read_handler(data string) !string { - // Decode the request with uri parameter - request := jsonrpc.decode_request_generic[ResourceReadParams](data)! - - if !s.backend.resource_exists(request.params.uri)! { - return jsonrpc.new_error_response(request.id, resource_not_found(request.params.uri)).encode() - } - - // Get the resource contents by URI - resource_contents := s.backend.resource_contents_get(request.params.uri)! - - // Create a success response with the result - response := jsonrpc.new_response_generic[ResourceReadResult](request.id, ResourceReadResult{ - contents: resource_contents - }) - return response.encode() -} - -// Resource Templates Handler - -pub struct ResourceTemplatesListResult { -pub: - resource_templates []ResourceTemplate @[json: 'resourceTemplates'] -} - -pub struct ResourceTemplate { -pub: - uri_template string @[json: 'uriTemplate'] - name string - description string - mimetype string @[json: 'mimeType'] -} - -// resources_templates_list_handler handles the resources/templates/list request -// This request is used to retrieve a list of available resource templates -fn (mut s Server) resources_templates_list_handler(data string) !string { - // Decode the request - request := jsonrpc.decode_request(data)! - - // Create a success response with the result - response := jsonrpc.new_response_generic[ResourceTemplatesListResult](request.id, - ResourceTemplatesListResult{ - resource_templates: s.backend.resource_templates_list()! - }) - return response.encode() -} - -// Resource Subscription Handler - -pub struct ResourceSubscribeParams { -pub: - uri string -} - -pub struct ResourceSubscribeResult { -pub: - subscribed bool -} - -// resources_subscribe_handler handles the resources/subscribe request -// This request is used to subscribe to changes for a specific resource -fn (mut s Server) resources_subscribe_handler(data string) !string { - request := jsonrpc.decode_request_generic[ResourceSubscribeParams](data)! - - if !s.backend.resource_exists(request.params.uri)! { - return jsonrpc.new_error_response(request.id, resource_not_found(request.params.uri)).encode() - } - - s.backend.resource_subscribe(request.params.uri)! - - response := jsonrpc.new_response_generic[ResourceSubscribeResult](request.id, ResourceSubscribeResult{ - subscribed: true - }) - return response.encode() -} - -// Resource Notification Handlers - -// send_resources_list_changed_notification sends a notification when the list of resources changes -pub fn (mut s Server) send_resources_list_changed_notification() ! { - // Check if the client supports this notification - if !s.client_config.capabilities.roots.list_changed { - return - } - - // Create a notification - notification := jsonrpc.new_blank_notification('notifications/resources/list_changed') - s.send(json.encode(notification)) - // Send the notification to all connected clients - // In a real implementation, this would use a WebSocket or other transport - // Note: Removed log.info() as it interferes with STDIO transport JSON-RPC communication -} - -pub struct ResourceUpdatedParams { -pub: - uri string -} - -// send_resource_updated_notification sends a notification when a subscribed resource is updated -pub fn (mut s Server) send_resource_updated_notification(uri string) ! { - // Check if the client is subscribed to this resource - if !s.backend.resource_subscribed(uri)! { - return - } - - // Create a notification - notification := jsonrpc.new_notification[ResourceUpdatedParams]('notifications/resources/updated', - ResourceUpdatedParams{ - uri: uri - }) - - s.send(json.encode(notification)) - // Send the notification to all connected clients - // In a real implementation, this would use a WebSocket or other transport - // Note: Removed log.info() as it interferes with STDIO transport JSON-RPC communication -} diff --git a/libarchive/mcp_old/handler_tools.v b/libarchive/mcp_old/handler_tools.v deleted file mode 100644 index b1ce3da9..00000000 --- a/libarchive/mcp_old/handler_tools.v +++ /dev/null @@ -1,151 +0,0 @@ -module mcp - -import time -import os -import log -import x.json2 -import json -import incubaid.herolib.schemas.jsonrpc -import incubaid.herolib.schemas.jsonschema - -// Tool related structs - -pub struct Tool { -pub: - name string - description string - input_schema jsonschema.Schema @[json: 'inputSchema'] -} - -pub struct ToolProperty { -pub: - typ string @[json: 'type'] - items ToolItems - enum []string -} - -pub struct ToolItems { -pub: - typ string @[json: 'type'] - enum []string - properties map[string]ToolProperty -} - -pub struct ToolContent { -pub: - typ string @[json: 'type'] - text string - number int - boolean bool - properties map[string]ToolContent - items []ToolContent -} - -// Tool List Handler - -pub struct ToolListParams { -pub: - cursor string -} - -pub struct ToolListResult { -pub: - tools []Tool - next_cursor string @[json: 'nextCursor'] -} - -// tools_list_handler handles the tools/list request -// This request is used to retrieve a list of available tools -fn (mut s Server) tools_list_handler(data string) !string { - // Decode the request with cursor parameter - request := jsonrpc.decode_request_generic[ToolListParams](data)! - cursor := request.params.cursor - - // TODO: Implement pagination logic using the cursor - // For now, return all tools - encoded := json.encode(ToolListResult{ - tools: s.backend.tool_list()! - next_cursor: '' // Empty if no more pages - }) - // Create a success response with the result - response := jsonrpc.new_response(request.id, json.encode(ToolListResult{ - tools: s.backend.tool_list()! - next_cursor: '' // Empty if no more pages - })) - return response.encode() -} - -// Tool Call Handler - -pub struct ToolCallParams { -pub: - name string - arguments map[string]json2.Any - meta map[string]json2.Any @[json: '_meta'] -} - -pub struct ToolCallResult { -pub: - is_error bool @[json: 'isError'] - content []ToolContent -} - -// tools_call_handler handles the tools/call request -// This request is used to call a specific tool with arguments -fn (mut s Server) tools_call_handler(data string) !string { - // Decode the request with name and arguments parameters - request_map := json2.raw_decode(data)!.as_map() - params_map := request_map['params'].as_map() - tool_name := params_map['name'].str() - if !s.backend.tool_exists(tool_name)! { - return jsonrpc.new_error_response(request_map['id'].int(), tool_not_found(tool_name)).encode() - } - - arguments := params_map['arguments'].as_map() - // Get the tool by name - tool := s.backend.tool_get(tool_name)! - - // Validate arguments against the input schema - // TODO: Implement proper JSON Schema validation - for req in tool.input_schema.required { - if req !in arguments { - return jsonrpc.new_error_response(request_map['id'].int(), missing_required_argument(req)).encode() - } - } - - log.debug('Calling tool: ${tool_name} with arguments: ${arguments}') - // Call the tool with the provided arguments - result := s.backend.tool_call(tool_name, arguments)! - - log.debug('Received result from tool: ${tool_name} with result: ${result}') - // Create a success response with the result - response := jsonrpc.new_response_generic[ToolCallResult](request_map['id'].int(), - result) - return response.encode() -} - -// Tool Notification Handlers - -// send_tools_list_changed_notification sends a notification when the list of tools changes -pub fn (mut s Server) send_tools_list_changed_notification() ! { - // Check if the client supports this notification - if !s.client_config.capabilities.roots.list_changed { - return - } - - // Create a notification - notification := jsonrpc.new_blank_notification('notifications/tools/list_changed') - s.send(json.encode(notification)) - // Send the notification to all connected clients - log.info('Sending tools list changed notification: ${json.encode(notification)}') -} - -pub fn error_tool_call_result(err IError) ToolCallResult { - return ToolCallResult{ - is_error: true - content: [ToolContent{ - typ: 'text' - text: err.msg() - }] - } -} diff --git a/libarchive/mcp_old/mcpgen/README.md b/libarchive/mcp_old/mcpgen/README.md deleted file mode 100644 index 01bdf0d3..00000000 --- a/libarchive/mcp_old/mcpgen/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# MCP Generator - -An implementation of the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for V language operations. This server uses the Standard Input/Output (stdio) transport as described in the [MCP documentation](https://modelcontextprotocol.io/docs/concepts/transports). - -## Features - -The server supports the following operations: - -1. **test** - Run V tests on a file or directory -2. **run** - Execute V code from a file or directory -3. **compile** - Compile V code from a file or directory -4. **vet** - Run V vet on a file or directory - -## Usage - -### Building the Server - -```bash -v -gc none -stats -enable-globals -n -w -cg -g -cc tcc /Users/despiegk/code/github/incubaid/herolib/lib/mcp/v_do -``` - -### Using the Server - -The server communicates using the MCP protocol over stdio. To send a request, use the following format: - -``` -Content-Length: - -{"jsonrpc":"2.0","id":"","method":"","params":{"fullpath":""}} -``` - -Where: -- `` is the length of the JSON message in bytes -- `` is a unique identifier for the request -- `` is one of: `test`, `run`, `compile`, or `vet` -- `` is the absolute path to the V file or directory to process - -### Example - -Request: -``` -Content-Length: 85 - -{"jsonrpc":"2.0","id":"1","method":"test","params":{"fullpath":"/path/to/file.v"}} -``` - -Response: -``` -Content-Length: 245 - -{"jsonrpc":"2.0","id":"1","result":{"output":"Command: v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc test /path/to/file.v\nExit code: 0\nOutput:\nAll tests passed!"}} -``` - -## Methods - -### test - -Runs V tests on the specified file or directory. - -Command used: -``` -v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc test ${fullpath} -``` - -If a directory is specified, it will run tests on all `.v` files in the directory (non-recursive). - -### run - -Executes the specified V file or all V files in a directory. - -Command used: -``` -v -gc none -stats -enable-globals -n -w -cg -g -cc tcc run ${fullpath} -``` - -### compile - -Compiles the specified V file or all V files in a directory. - -Command used: -``` -cd /tmp && v -gc none -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc ${fullpath} -``` - -### vet - -Runs V vet on the specified file or directory. - -Command used: -``` -v vet -v -w ${fullpath} -``` diff --git a/libarchive/mcp_old/mcpgen/command.v b/libarchive/mcp_old/mcpgen/command.v deleted file mode 100644 index c46d43ec..00000000 --- a/libarchive/mcp_old/mcpgen/command.v +++ /dev/null @@ -1,22 +0,0 @@ -module mcpgen - -import cli - -pub const command = cli.Command{ - sort_flags: true - name: 'mcpgen' - // execute: cmd_mcpgen - description: 'will list existing mdbooks' - commands: [ - cli.Command{ - name: 'start' - execute: cmd_start - description: 'start the MCP server' - }, - ] -} - -fn cmd_start(cmd cli.Command) ! { - mut server := new_mcp_server(&MCPGen{})! - server.start()! -} diff --git a/libarchive/mcp_old/mcpgen/mcpgen.v b/libarchive/mcp_old/mcpgen/mcpgen.v deleted file mode 100644 index 5ff98e8a..00000000 --- a/libarchive/mcp_old/mcpgen/mcpgen.v +++ /dev/null @@ -1,281 +0,0 @@ -module mcpgen - -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.mcp -import incubaid.herolib.schemas.jsonschema -import incubaid.herolib.schemas.jsonschema.codegen -import os - -pub struct FunctionPointer { - name string // name of function - module_path string // path to module -} - -// create_mcp_tool_code receives the name of a V language function string, and the path to the module in which it exists. -// returns an MCP Tool code in v for attaching the function to the mcp server -// function_pointers: A list of function pointers to generate tools for -pub fn (d &MCPGen) create_mcp_tools_code(function_pointers []FunctionPointer) !string { - mut str := '' - - for function_pointer in function_pointers { - str += d.create_mcp_tool_code(function_pointer.name, function_pointer.module_path)! - } - - return str -} - -// create_mcp_tool_code receives the name of a V language function string, and the path to the module in which it exists. -// returns an MCP Tool code in v for attaching the function to the mcp server -pub fn (d &MCPGen) create_mcp_tool_code(function_name string, module_path string) !string { - if !os.exists(module_path) { - return error('Module path does not exist: ${module_path}') - } - - function := code.get_function_from_module(module_path, function_name) or { - return error('Failed to get function ${function_name} from module ${module_path}\n${err}') - } - - mut types := map[string]string{} - for param in function.params { - // Check if the type is an Object (struct) - if param.typ is code.Object { - types[param.typ.symbol()] = code.get_type_from_module(module_path, param.typ.symbol())! - } - } - - // Get the result type if it's a struct - mut result_ := '' - if function.result.typ is code.Result { - result_type := (function.result.typ as code.Result).typ - if result_type is code.Object { - result_ = code.get_type_from_module(module_path, result_type.symbol())! - } - } else if function.result.typ is code.Object { - result_ = code.get_type_from_module(module_path, function.result.typ.symbol())! - } - - tool_name := function.name - tool := d.create_mcp_tool(function, types)! - handler := d.create_mcp_tool_handler(function, types, result_)! - str := $tmpl('./templates/tool_code.v.template') - return str -} - -// create_mcp_tool parses a V language function string and returns an MCP Tool struct -// function: The V function string including preceding comments -// types: A map of struct names to their definitions for complex parameter types -// result: The type of result of the create_mcp_tool function. Could be simply string, or struct {...} -pub fn (d &MCPGen) create_mcp_tool_handler(function code.Function, types map[string]string, result_ string) !string { - decode_stmts := function.params.map(argument_decode_stmt(it)).join_lines() - - function_call := 'd.${function.name}(${function.params.map(it.name).join(',')})' - result := code.parse_type(result_) - str := $tmpl('./templates/tool_handler.v.template') - return str -} - -pub fn argument_decode_stmt(param code.Param) string { - return if param.typ is code.Integer { - '${param.name} := arguments["${param.name}"].int()' - } else if param.typ is code.Boolean { - '${param.name} := arguments["${param.name}"].bool()' - } else if param.typ is code.String { - '${param.name} := arguments["${param.name}"].str()' - } else if param.typ is code.Object { - '${param.name} := json.decode[${param.typ.symbol()}](arguments["${param.name}"].str())!' - } else if param.typ is code.Array { - '${param.name} := json.decode[${param.typ.symbol()}](arguments["${param.name}"].str())!' - } else if param.typ is code.Map { - '${param.name} := json.decode[${param.typ.symbol()}](arguments["${param.name}"].str())!' - } else { - panic('Unsupported type: ${param.typ}') - } -} - -/* -in @generate_mcp.v , implement a create_mpc_tool_handler function that given a vlang function string and the types that map to their corresponding type definitions (for instance struct some_type: SomeType{...}), generates a vlang function such as the following: - -ou -pub fn (d &MCPGen) create_mcp_tool_tool_handler(arguments map[string]Any) !mcp.Tool { - function := arguments['function'].str() - types := json.decode[map[string]string](arguments['types'].str())! - return d.create_mcp_tool(function, types) -} -*/ - -// create_mcp_tool parses a V language function string and returns an MCP Tool struct -// function: The V function string including preceding comments -// types: A map of struct names to their definitions for complex parameter types -pub fn (d MCPGen) create_mcp_tool(function code.Function, types map[string]string) !mcp.Tool { - // Create input schema for parameters - mut properties := map[string]jsonschema.SchemaRef{} - mut required := []string{} - - for param in function.params { - // Add to required parameters - required << param.name - - // Create property for this parameter - mut property := jsonschema.SchemaRef{} - - // Check if this is a complex type defined in the types map - if param.typ.symbol() in types { - // Parse the struct definition to create a nested schema - struct_def := types[param.typ.symbol()] - struct_schema := codegen.struct_to_schema(code.parse_struct(struct_def)!) - if struct_schema is jsonschema.Schema { - property = struct_schema - } else { - return error('Unsupported type: ${param.typ}') - } - } else { - // Handle primitive types - property = codegen.typesymbol_to_schema(param.typ.symbol()) - } - - properties[param.name] = property - } - - // Create the input schema - input_schema := jsonschema.Schema{ - typ: 'object' - properties: properties - required: required - } - - // Create and return the Tool - return mcp.Tool{ - name: function.name - description: function.description - input_schema: input_schema - } -} - -// // create_mcp_tool_input_schema creates a jsonschema.Schema for a given input type -// // input: The input type string -// // returns: A jsonschema.Schema for the given input type -// // errors: Returns an error if the input type is not supported -// pub fn (d MCPGen) create_mcp_tool_input_schema(input string) !jsonschema.Schema { - -// // if input is a primitive type, return a mcp jsonschema.Schema with that type -// if input == 'string' { -// return jsonschema.Schema{ -// typ: 'string' -// } -// } else if input == 'int' { -// return jsonschema.Schema{ -// typ: 'integer' -// } -// } else if input == 'float' { -// return jsonschema.Schema{ -// typ: 'number' -// } -// } else if input == 'bool' { -// return jsonschema.Schema{ -// typ: 'boolean' -// } -// } - -// // if input is a struct, return a mcp jsonschema.Schema with typ 'object' and properties for each field in the struct -// if input.starts_with('pub struct ') { -// struct_name := input[11..].split(' ')[0] -// fields := parse_struct_fields(input) -// mut properties := map[string]jsonschema.Schema{} - -// for field_name, field_type in fields { -// property := jsonschema.Schema{ -// typ: d.create_mcp_tool_input_schema(field_type)!.typ -// } -// properties[field_name] = property -// } - -// return jsonschema.Schema{ -// typ: 'object', -// properties: properties -// } -// } - -// // if input is an array, return a mcp jsonschema.Schema with typ 'array' and items of the item type -// if input.starts_with('[]') { -// item_type := input[2..] - -// // For array types, we create a schema with type 'array' -// // The actual item type is determined by the primitive type -// mut item_type_str := 'string' // default -// if item_type == 'int' { -// item_type_str = 'integer' -// } else if item_type == 'float' { -// item_type_str = 'number' -// } else if item_type == 'bool' { -// item_type_str = 'boolean' -// } - -// // Create a property for the array items -// mut property := jsonschema.Schema{ -// typ: 'array' -// } - -// // Add the property to the schema -// mut properties := map[string]jsonschema.Schema{} -// properties['items'] = property - -// return jsonschema.Schema{ -// typ: 'array', -// properties: properties -// } -// } - -// // Default to string type for unknown types -// return jsonschema.Schema{ -// typ: 'string' -// } -// } - -// parse_struct_fields parses a V language struct definition string and returns a map of field names to their types -fn parse_struct_fields(struct_def string) map[string]string { - mut fields := map[string]string{} - - // Find the opening and closing braces of the struct definition - start_idx := struct_def.index('{') or { return fields } - end_idx := struct_def.last_index('}') or { return fields } - - // Extract the content between the braces - struct_content := struct_def[start_idx + 1..end_idx].trim_space() - - // Split the content by newlines to get individual field definitions - field_lines := struct_content.split(' -') - - for line in field_lines { - trimmed_line := line.trim_space() - - // Skip empty lines and comments - if trimmed_line == '' || trimmed_line.starts_with('//') { - continue - } - - // Handle pub: or mut: prefixes - mut field_def := trimmed_line - if field_def.starts_with('pub:') || field_def.starts_with('mut:') { - field_def = field_def.all_after(':').trim_space() - } - - // Split by whitespace to separate field name and type - parts := field_def.split_any(' ') - if parts.len < 2 { - continue - } - - field_name := parts[0] - field_type := parts[1..].join(' ') - - // Handle attributes like @[json: 'name'] - if field_name.contains('@[') { - continue - } - - fields[field_name] = field_type - } - - return fields -} diff --git a/libarchive/mcp_old/mcpgen/mcpgen_helpers.v b/libarchive/mcp_old/mcpgen/mcpgen_helpers.v deleted file mode 100644 index c029b557..00000000 --- a/libarchive/mcp_old/mcpgen/mcpgen_helpers.v +++ /dev/null @@ -1,54 +0,0 @@ -module mcpgen - -import incubaid.herolib.mcp - -pub fn result_to_mcp_tool_contents[T](result T) []mcp.ToolContent { - return [result_to_mcp_tool_content(result)] -} - -pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent { - return $if T is string { - mcp.ToolContent{ - typ: 'text' - text: result.str() - } - } $else $if T is int { - mcp.ToolContent{ - typ: 'number' - number: result.int() - } - } $else $if T is bool { - mcp.ToolContent{ - typ: 'boolean' - boolean: result.bool() - } - } $else $if result is $array { - mut items := []mcp.ToolContent{} - for item in result { - items << result_to_mcp_tool_content(item) - } - return mcp.ToolContent{ - typ: 'array' - items: items - } - } $else $if T is $struct { - mut properties := map[string]mcp.ToolContent{} - $for field in T.fields { - properties[field.name] = result_to_mcp_tool_content(result.$(field.name)) - } - return mcp.ToolContent{ - typ: 'object' - properties: properties - } - } $else { - panic('Unsupported type: ${typeof(result)}') - } -} - -pub fn array_to_mcp_tool_contents[U](array []U) []mcp.ToolContent { - mut contents := []mcp.ToolContent{} - for item in array { - contents << result_to_mcp_tool_content(item) - } - return contents -} diff --git a/libarchive/mcp_old/mcpgen/mcpgen_tools.v b/libarchive/mcp_old/mcpgen/mcpgen_tools.v deleted file mode 100644 index c181cb44..00000000 --- a/libarchive/mcp_old/mcpgen/mcpgen_tools.v +++ /dev/null @@ -1,144 +0,0 @@ -module mcpgen - -import incubaid.herolib.mcp -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.schemas.jsonschema -import x.json2 as json { Any } -// import json - -// create_mcp_tools_code MCP Tool -// create_mcp_tool_code receives the name of a V language function string, and the path to the module in which it exists. -// returns an MCP Tool code in v for attaching the function to the mcp server -// function_pointers: A list of function pointers to generate tools for - -const create_mcp_tools_code_tool = mcp.Tool{ - name: 'create_mcp_tools_code' - description: 'create_mcp_tool_code receives the name of a V language function string, and the path to the module in which it exists. -returns an MCP Tool code in v for attaching the function to the mcp server -function_pointers: A list of function pointers to generate tools for' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'function_pointers': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'array' - items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'object' - properties: { - 'name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'module_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['name', 'module_path'] - })) - }) - } - required: ['function_pointers'] - } -} - -pub fn (d &MCPGen) create_mcp_tools_code_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - function_pointers := json.decode[[]FunctionPointer](arguments['function_pointers'].str())! - result := d.create_mcp_tools_code(function_pointers) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -const create_mcp_tool_code_tool = mcp.Tool{ - name: 'create_mcp_tool_code' - description: 'create_mcp_tool_code receives the name of a V language function string, and the path to the module in which it exists. -returns an MCP Tool code in v for attaching the function to the mcp server' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'function_name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'module_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['function_name', 'module_path'] - } -} - -pub fn (d &MCPGen) create_mcp_tool_code_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - function_name := arguments['function_name'].str() - module_path := arguments['module_path'].str() - result := d.create_mcp_tool_code(function_name, module_path) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: result_to_mcp_tool_contents[string](result) - } -} - -// Tool definition for the create_mcp_tool function -const create_mcp_tool_const_tool = mcp.Tool{ - name: 'create_mcp_tool_const' - description: 'Parses a V language function string and returns an MCP Tool struct. This tool analyzes function signatures, extracts parameters, and generates the appropriate MCP Tool representation.' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'function': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'types': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'object' - }) - } - required: ['function'] - } -} - -pub fn (d &MCPGen) create_mcp_tool_const_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - function := json.decode[code.Function](arguments['function'].str())! - types := json.decode[map[string]string](arguments['types'].str())! - result := d.create_mcp_tool(function, types) or { return mcp.error_tool_call_result(err) } - return mcp.ToolCallResult{ - is_error: false - content: result_to_mcp_tool_contents[string](result.str()) - } -} - -// Tool definition for the create_mcp_tool_handler function -const create_mcp_tool_handler_tool = mcp.Tool{ - name: 'create_mcp_tool_handler' - description: 'Generates a tool handler for the create_mcp_tool function. This tool handler accepts function string and types map and returns an MCP ToolCallResult.' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'function': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'types': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'object' - }) - 'result': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['function', 'result'] - } -} - -// Tool handler for the create_mcp_tool_handler function -pub fn (d &MCPGen) create_mcp_tool_handler_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - function := json.decode[code.Function](arguments['function'].str())! - types := json.decode[map[string]string](arguments['types'].str())! - result_ := arguments['result'].str() - result := d.create_mcp_tool_handler(function, types, result_) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: result_to_mcp_tool_contents[string](result) - } -} diff --git a/libarchive/mcp_old/mcpgen/schemas/create_mcp_tools_code_tool_input.json b/libarchive/mcp_old/mcpgen/schemas/create_mcp_tools_code_tool_input.json deleted file mode 100644 index ad39a778..00000000 --- a/libarchive/mcp_old/mcpgen/schemas/create_mcp_tools_code_tool_input.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "type": "object", - "properties": { - "function_pointers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "module_path": { - "type": "string" - } - }, - "required": ["name", "module_path"] - } - } - }, - "required": ["function_pointers"] - } \ No newline at end of file diff --git a/libarchive/mcp_old/mcpgen/server.v b/libarchive/mcp_old/mcpgen/server.v deleted file mode 100644 index ecb313cf..00000000 --- a/libarchive/mcp_old/mcpgen/server.v +++ /dev/null @@ -1,35 +0,0 @@ -module mcpgen - -import incubaid.herolib.mcp.logger -import incubaid.herolib.mcp - -@[heap] -pub struct MCPGen {} - -pub fn new_mcp_server(v &MCPGen) !&mcp.Server { - logger.info('Creating new Developer MCP server') - - // Initialize the server with the empty handlers map - mut server := mcp.new_server(mcp.MemoryBackend{ - tools: { - 'create_mcp_tool_code': create_mcp_tool_code_tool - 'create_mcp_tool_const': create_mcp_tool_const_tool - 'create_mcp_tool_handler': create_mcp_tool_handler_tool - 'create_mcp_tools_code': create_mcp_tools_code_tool - } - tool_handlers: { - 'create_mcp_tool_code': v.create_mcp_tool_code_tool_handler - 'create_mcp_tool_const': v.create_mcp_tool_const_tool_handler - 'create_mcp_tool_handler': v.create_mcp_tool_handler_tool_handler - 'create_mcp_tools_code': v.create_mcp_tools_code_tool_handler - } - }, mcp.ServerParams{ - config: mcp.ServerConfiguration{ - server_info: mcp.ServerInfo{ - name: 'mcpgen' - version: '1.0.0' - } - } - })! - return server -} diff --git a/libarchive/mcp_old/mcpgen/templates/tool_code.v.template b/libarchive/mcp_old/mcpgen/templates/tool_code.v.template deleted file mode 100644 index 99a69fdd..00000000 --- a/libarchive/mcp_old/mcpgen/templates/tool_code.v.template +++ /dev/null @@ -1,6 +0,0 @@ -// @{tool_name} MCP Tool -// @{tool.description} - -const @{tool_name}_tool = @{tool.str()} - -@{handler} \ No newline at end of file diff --git a/libarchive/mcp_old/mcpgen/templates/tool_handler.v.template b/libarchive/mcp_old/mcpgen/templates/tool_handler.v.template deleted file mode 100644 index 1f7fa119..00000000 --- a/libarchive/mcp_old/mcpgen/templates/tool_handler.v.template +++ /dev/null @@ -1,11 +0,0 @@ -pub fn (d &MCPGen) @{function.name}_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - @{decode_stmts} - result := @{function_call} - or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[@{result.symbol()}](result) - } -} \ No newline at end of file diff --git a/libarchive/mcp_old/mcpgen/templates/tools_file.v.template b/libarchive/mcp_old/mcpgen/templates/tools_file.v.template deleted file mode 100644 index b6d14b0b..00000000 --- a/libarchive/mcp_old/mcpgen/templates/tools_file.v.template +++ /dev/null @@ -1 +0,0 @@ -@for import in \ No newline at end of file diff --git a/libarchive/mcp_old/model_configuration.v b/libarchive/mcp_old/model_configuration.v deleted file mode 100644 index b38686f2..00000000 --- a/libarchive/mcp_old/model_configuration.v +++ /dev/null @@ -1,93 +0,0 @@ -module mcp - -import time -import os -import log -import x.json2 -import incubaid.herolib.schemas.jsonrpc - -const protocol_version = '2024-11-05' -// MCP server implementation using stdio transport -// Based on https://modelcontextprotocol.io/docs/concepts/transports - -// ClientConfiguration represents the parameters for the initialize request -pub struct ClientConfiguration { -pub: - protocol_version string @[json: 'protocolVersion'] - capabilities ClientCapabilities - client_info ClientInfo @[json: 'clientInfo'] -} - -// ClientCapabilities represents the client capabilities -pub struct ClientCapabilities { -pub: - roots RootsCapability // Ability to provide filesystem roots - sampling SamplingCapability // Support for LLM sampling requests - experimental ExperimentalCapability // Describes support for non-standard experimental features -} - -// RootsCapability represents the roots capability -pub struct RootsCapability { -pub: - list_changed bool @[json: 'listChanged'] -} - -// SamplingCapability represents the sampling capability -pub struct SamplingCapability {} - -// ExperimentalCapability represents the experimental capability -pub struct ExperimentalCapability {} - -// ClientInfo represents the client information -pub struct ClientInfo { -pub: - name string - version string -} - -// ServerConfiguration represents the server configuration -pub struct ServerConfiguration { -pub: - protocol_version string = '2024-11-05' @[json: 'protocolVersion'] - capabilities ServerCapabilities - server_info ServerInfo @[json: 'serverInfo'] -} - -// ServerCapabilities represents the server capabilities -pub struct ServerCapabilities { -pub: - logging LoggingCapability - prompts PromptsCapability - resources ResourcesCapability - tools ToolsCapability -} - -// LoggingCapability represents the logging capability -pub struct LoggingCapability { -} - -// PromptsCapability represents the prompts capability -pub struct PromptsCapability { -pub: - list_changed bool = true @[json: 'listChanged'] -} - -// ResourcesCapability represents the resources capability -pub struct ResourcesCapability { -pub: - subscribe bool = true @[json: 'subscribe'] - list_changed bool = true @[json: 'listChanged'] -} - -// ToolsCapability represents the tools capability -pub struct ToolsCapability { -pub: - list_changed bool = true @[json: 'listChanged'] -} - -// ServerInfo represents the server information -pub struct ServerInfo { -pub: - name string = 'HeroLibMCPServer' - version string = '1.0.0' -} diff --git a/libarchive/mcp_old/model_configuration_test.v b/libarchive/mcp_old/model_configuration_test.v deleted file mode 100644 index ecd58631..00000000 --- a/libarchive/mcp_old/model_configuration_test.v +++ /dev/null @@ -1,91 +0,0 @@ -module mcp - -import incubaid.herolib.schemas.jsonrpc -import json - -// This file contains tests for the MCP initialize handler implementation. -// It tests the handler's ability to process initialize requests according to the MCP specification. - -// test_json_serialization_deserialization tests the JSON serialization and deserialization of initialize request and response -fn test_json_serialization_deserialization() { - // Create a sample initialize params object - params := ClientConfiguration{ - protocol_version: '2024-11-05' - capabilities: ClientCapabilities{ - roots: RootsCapability{ - list_changed: true - } - // sampling: SamplingCapability{} - } - client_info: ClientInfo{ - name: 'mcp-inspector' - // version: '0.0.1' - } - } - - // Serialize the params to JSON - params_json := json.encode(params) - - // Verify the JSON structure has the correct camelCase keys - assert params_json.contains('"protocolVersion":"2024-11-05"'), 'JSON should have protocolVersion in camelCase' - assert params_json.contains('"clientInfo":{'), 'JSON should have clientInfo in camelCase' - assert params_json.contains('"listChanged":true'), 'JSON should have listChanged in camelCase' - - // Deserialize the JSON back to a struct - deserialized_params := json.decode(ClientConfiguration, params_json) or { - assert false, 'Failed to deserialize params: ${err}' - return - } - - // Verify the deserialized object matches the original - assert deserialized_params.protocol_version == params.protocol_version, 'Deserialized protocol_version should match original' - assert deserialized_params.client_info.name == params.client_info.name, 'Deserialized client_info.name should match original' - assert deserialized_params.client_info.version == params.client_info.version, 'Deserialized client_info.version should match original' - assert deserialized_params.capabilities.roots.list_changed == params.capabilities.roots.list_changed, 'Deserialized capabilities.roots.list_changed should match original' - - // Now test the response serialization/deserialization - response := ServerConfiguration{ - protocol_version: '2024-11-05' - capabilities: ServerCapabilities{ - logging: LoggingCapability{} - prompts: PromptsCapability{ - list_changed: true - } - resources: ResourcesCapability{ - subscribe: true - list_changed: true - } - tools: ToolsCapability{ - list_changed: true - } - } - server_info: ServerInfo{ - name: 'HeroLibMCPServer' - version: '1.0.0' - } - } - - // Serialize the response to JSON - response_json := json.encode(response) - - // Verify the JSON structure has the correct camelCase keys - assert response_json.contains('"protocolVersion":"2024-11-05"'), 'JSON should have protocolVersion in camelCase' - assert response_json.contains('"serverInfo":{'), 'JSON should have serverInfo in camelCase' - assert response_json.contains('"listChanged":true'), 'JSON should have listChanged in camelCase' - assert response_json.contains('"subscribe":true'), 'JSON should have subscribe field' - - // Deserialize the JSON back to a struct - deserialized_response := json.decode(ServerConfiguration, response_json) or { - assert false, 'Failed to deserialize response: ${err}' - return - } - - // Verify the deserialized object matches the original - assert deserialized_response.protocol_version == response.protocol_version, 'Deserialized protocol_version should match original' - assert deserialized_response.server_info.name == response.server_info.name, 'Deserialized server_info.name should match original' - assert deserialized_response.server_info.version == response.server_info.version, 'Deserialized server_info.version should match original' - assert deserialized_response.capabilities.prompts.list_changed == response.capabilities.prompts.list_changed, 'Deserialized capabilities.prompts.list_changed should match original' - assert deserialized_response.capabilities.resources.subscribe == response.capabilities.resources.subscribe, 'Deserialized capabilities.resources.subscribe should match original' - assert deserialized_response.capabilities.resources.list_changed == response.capabilities.resources.list_changed, 'Deserialized capabilities.resources.list_changed should match original' - assert deserialized_response.capabilities.tools.list_changed == response.capabilities.tools.list_changed, 'Deserialized capabilities.tools.list_changed should match original' -} diff --git a/libarchive/mcp_old/model_error.v b/libarchive/mcp_old/model_error.v deleted file mode 100644 index afa8adc1..00000000 --- a/libarchive/mcp_old/model_error.v +++ /dev/null @@ -1,35 +0,0 @@ -module mcp - -import incubaid.herolib.schemas.jsonrpc - -// resource_not_found indicates that the requested resource doesn't exist. -// This error is returned when the resource specified in the request is not found. -// Error code: -32002 -pub fn resource_not_found(uri string) jsonrpc.RPCError { - return jsonrpc.RPCError{ - code: -32002 - message: 'Resource not found' - data: 'The requested resource ${uri} was not found.' - } -} - -fn prompt_not_found(name string) jsonrpc.RPCError { - return jsonrpc.RPCError{ - code: -32602 // Invalid params - message: 'Prompt not found: ${name}' - } -} - -fn missing_required_argument(arg_name string) jsonrpc.RPCError { - return jsonrpc.RPCError{ - code: -32602 // Invalid params - message: 'Missing required argument: ${arg_name}' - } -} - -fn tool_not_found(name string) jsonrpc.RPCError { - return jsonrpc.RPCError{ - code: -32602 // Invalid params - message: 'Tool not found: ${name}' - } -} diff --git a/libarchive/mcp_old/pugconvert/cmd/.gitignore b/libarchive/mcp_old/pugconvert/cmd/.gitignore deleted file mode 100644 index 1a94556d..00000000 --- a/libarchive/mcp_old/pugconvert/cmd/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -main - diff --git a/libarchive/mcp_old/pugconvert/cmd/compile.sh b/libarchive/mcp_old/pugconvert/cmd/compile.sh deleted file mode 100755 index 87b33856..00000000 --- a/libarchive/mcp_old/pugconvert/cmd/compile.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -ex - -export name="mcp_pugconvert" - -# Change to the directory containing this script -cd "$(dirname "$0")" - -# Compile the V program -v -n -w -gc none -cc tcc -d use_openssl -enable-globals main.v - -# Ensure the binary is executable -chmod +x main -mv main ~/hero/bin/${name} - -echo "Compilation successful. Binary '${name}' is ready." diff --git a/libarchive/mcp_old/pugconvert/cmd/main.v b/libarchive/mcp_old/pugconvert/cmd/main.v deleted file mode 100644 index 18dd1845..00000000 --- a/libarchive/mcp_old/pugconvert/cmd/main.v +++ /dev/null @@ -1,17 +0,0 @@ -module main - -import incubaid.herolib.mcp.servers.pugconvert.mcp - -fn main() { - // Create a new MCP server - mut server := mcp.new_mcp_server() or { - eprintln('Failed to create MCP server: ${err}') - return - } - - // Start the server - server.start() or { - eprintln('Failed to start MCP server: ${err}') - return - } -} diff --git a/libarchive/mcp_old/pugconvert/logic/convertpug.v b/libarchive/mcp_old/pugconvert/logic/convertpug.v deleted file mode 100644 index ffafda18..00000000 --- a/libarchive/mcp_old/pugconvert/logic/convertpug.v +++ /dev/null @@ -1,203 +0,0 @@ -module pugconvert - -import incubaid.herolib.clients.openai -import incubaid.herolib.core.texttools -import incubaid.herolib.core.pathlib -import json - -pub fn convert_pug(mydir string) ! { - mut d := pathlib.get_dir(path: mydir, create: false)! - list := d.list(regex: [r'.*\.pug$'], include_links: false, files_only: true)! - for item in list.paths { - convert_pug_file(item.path)! - } -} - -// extract_template parses AI response content to extract just the template -fn extract_template(raw_content string) string { - mut content := raw_content - - // First check for tag - if content.contains('') { - content = content.split('')[1].trim_space() - } - - // Look for ```jet code block - if content.contains('```jet') { - parts := content.split('```jet') - if parts.len > 1 { - end_parts := parts[1].split('```') - if end_parts.len > 0 { - content = end_parts[0].trim_space() - } - } - } else if content.contains('```') { - // If no ```jet, look for regular ``` code block - parts := content.split('```') - if parts.len >= 2 { - // Take the content between the first set of ``` - // This handles both ```content``` and cases where there's only an opening ``` - content = parts[1].trim_space() - - // If we only see an opening ``` but no closing, cleanup any remaining backticks - // to avoid incomplete formatting markers - if !content.contains('```') { - content = content.replace('`', '') - } - } - } - - return content -} - -pub fn convert_pug_file(myfile string) ! { - println(myfile) - - // Create new file path by replacing .pug extension with .jet - jet_file := myfile.replace('.pug', '.jet') - - // Check if jet file already exists, if so skip processing - mut jet_path_exist := pathlib.get_file(path: jet_file, create: false)! - if jet_path_exist.exists() { - println('Jet file already exists: ${jet_file}. Skipping conversion.') - return - } - - mut content_path := pathlib.get_file(path: myfile, create: false)! - content := content_path.read()! - - mut l := loader() - mut client := openai.get()! - - base_instruction := ' - You are a template language converter. You convert Pug templates to Jet templates. - - The target template language, Jet, is defined as follows: - ' - - base_user_prompt := ' - Convert this following Pug template to Jet: - - only output the resulting template, no explanation, no steps, just the jet template - ' - - // We'll retry up to 5 times if validation fails - max_attempts := 5 - mut attempts := 0 - mut is_valid := false - mut error_message := '' - mut template := '' - - for attempts < max_attempts && !is_valid { - attempts++ - - mut system_content := texttools.dedent(base_instruction) + '\n' + l.jet() - mut user_prompt := '' - - // Create different prompts for first attempt vs retries - if attempts == 1 { - // First attempt - convert from PUG - user_prompt = texttools.dedent(base_user_prompt) + '\n' + content - - // Print what we're sending to the AI service - println('Sending to OpenAI for conversion:') - println('--------------------------------') - println(content) - println('--------------------------------') - } else { - // Retries - focus on fixing the previous errors - println('Attempt ${attempts}: Retrying with error feedback') - user_prompt = ' -The previous Jet template conversion had the following error: -ERROR: ${error_message} - -Here was the template that had errors: -``` -${template} -``` - -The original pug input was was -``` -${content} -``` - -Please fix the template and try again. Learn from feedback and check which jet template was created. -Return only the corrected Jet template. -Dont send back more information than the fixed template, make sure its in jet format. - - ' // Print what we're sending for the retry - - println('Sending to OpenAI for correction:') - println('--------------------------------') - println(user_prompt) - println('--------------------------------') - } - - mut m := openai.Messages{ - messages: [ - openai.Message{ - role: .system - content: system_content - }, - openai.Message{ - role: .user - content: user_prompt - }, - ] - } - - // Create a chat completion request - res := client.chat_completion( - msgs: m - model: 'deepseek-r1-distill-llama-70b' - max_completion_tokens: 64000 - )! - - println('-----') - - // Print AI response before extraction - println('Response received from AI:') - println('--------------------------------') - println(res.choices[0].message.content) - println('--------------------------------') - - // Extract the template from the AI response - template = extract_template(res.choices[0].message.content) - - println('Extracted template for ${myfile}:') - println('--------------------------------') - println(template) - println('--------------------------------') - - // Validate the template - validation_result := jetvaliditycheck(template) or { - // If validation service is unavailable, we'll just proceed with the template - println('Warning: Template validation service unavailable: ${err}') - break - } - - // Check if template is valid - if validation_result.is_valid { - is_valid = true - println('Template validation successful!') - } else { - error_message = validation_result.error - println('Template validation failed: ${error_message}') - } - } - - // Report the validation outcome - if is_valid { - println('Successfully converted template after ${attempts} attempt(s)') - // Create the file and write the processed content - println('Converted to: ${jet_file}') - mut jet_path := pathlib.get_file(path: jet_file, create: true)! - jet_path.write(template)! - } else if attempts >= max_attempts { - println('Warning: Could not validate template after ${max_attempts} attempts') - println('Using best attempt despite validation errors: ${error_message}') - jet_file2 := jet_file.replace('.jet', '_error.jet') - mut jet_path2 := pathlib.get_file(path: jet_file2, create: true)! - jet_path2.write(template)! - } -} diff --git a/libarchive/mcp_old/pugconvert/logic/jetvalidation.v b/libarchive/mcp_old/pugconvert/logic/jetvalidation.v deleted file mode 100644 index 78498120..00000000 --- a/libarchive/mcp_old/pugconvert/logic/jetvalidation.v +++ /dev/null @@ -1,85 +0,0 @@ -module pugconvert - -import incubaid.herolib.core.httpconnection -import json - -// JetTemplateResponse is the expected response structure from the validation service -struct JetTemplateResponse { - valid bool - message string - error string -} - -// ValidationResult represents the result of a template validation -pub struct ValidationResult { -pub: - is_valid bool - error string -} - -// jetvaliditycheck validates a Jet template by sending it to a validation service -// The function sends the template to http://localhost:9020/checkjet for validation -// Returns a ValidationResult containing validity status and any error messages -pub fn jetvaliditycheck(jetcontent string) !ValidationResult { - // Create HTTP connection to the validation service - mut conn := httpconnection.HTTPConnection{ - base_url: 'http://localhost:9020' - } - - // Prepare the request data - template content wrapped in JSON - template_data := json.encode({ - 'template': jetcontent - }) - - // Print what we're sending to the AI service - // println('Sending to JET validation service:') - // println('--------------------------------') - // println(jetcontent) - // println('--------------------------------') - - // Send the POST request to the validation endpoint - req := httpconnection.Request{ - prefix: 'checkjet' - data: template_data - dataformat: .json - } - - // Execute the request - result := conn.post_json_str(req) or { - // Handle connection errors - return ValidationResult{ - is_valid: false - error: 'Connection error: ${err}' - } - } - - // Attempt to parse the response as JSON using the expected struct - response := json.decode(JetTemplateResponse, result) or { - // If we can't parse JSON using our struct, the server didn't return the expected format - return ValidationResult{ - is_valid: false - error: 'Server returned unexpected format: ${err.msg()}' - } - } - - // Use the structured response data - if response.valid == false { - error_msg := if response.error != '' { - response.error - } else if response.message != '' { - response.message - } else { - 'Unknown validation error' - } - - return ValidationResult{ - is_valid: false - error: error_msg - } - } - - return ValidationResult{ - is_valid: true - error: '' - } -} diff --git a/libarchive/mcp_old/pugconvert/logic/loader.v b/libarchive/mcp_old/pugconvert/logic/loader.v deleted file mode 100644 index 2a35d454..00000000 --- a/libarchive/mcp_old/pugconvert/logic/loader.v +++ /dev/null @@ -1,25 +0,0 @@ -module pugconvert - -import v.embed_file -import os - -@[heap] -pub struct FileLoader { -pub mut: - embedded_files map[string]embed_file.EmbedFileData @[skip; str: skip] -} - -fn (mut loader FileLoader) load() { - loader.embedded_files['jet'] = $embed_file('templates/jet_instructions.md') -} - -fn (mut loader FileLoader) jet() string { - c := loader.embedded_files['jet'] or { panic('bug embed') } - return c.to_string() -} - -fn loader() FileLoader { - mut loader := FileLoader{} - loader.load() - return loader -} diff --git a/libarchive/mcp_old/pugconvert/logic/templates/jet_instructions.md b/libarchive/mcp_old/pugconvert/logic/templates/jet_instructions.md deleted file mode 100644 index 5bf8cee7..00000000 --- a/libarchive/mcp_old/pugconvert/logic/templates/jet_instructions.md +++ /dev/null @@ -1,446 +0,0 @@ -# Jet Template Engine Syntax Reference - -## Delimiters - -Template delimiters are `{{` and `}}`. -Delimiters can use `.` to output the execution context: - -```jet -hello {{ . }} -``` - -### Whitespace Trimming - -Whitespace around delimiters can be trimmed using `{{-` and `-}}`: - -```jet -foo {{- "bar" -}} baz -``` - -Whitespace includes spaces, tabs, carriage returns, and newlines. - -### Comments - -Comments use `{* ... *}`: - -```jet -{* this is a comment *} - -{* - Multiline - {{ expressions }} are ignored -*} -``` - ---- - -## Variables - -### Initialization - -```jet -{{ foo := "bar" }} -``` - -### Assignment - -```jet -{{ foo = "asd" }} -{{ foo = 4711 }} -``` - -Skip assignment but still evaluate: - -```jet -{{ _ := stillRuns() }} -{{ _ = stillRuns() }} -``` - ---- - -## Expressions - -### Identifiers - -Identifiers resolve to values: - -```jet -{{ len("hello") }} -{{ isset(foo, bar) }} -``` - -### Indexing - -#### String - -```jet -{{ s := "helloworld" }} -{{ s[1] }} -``` - -#### Slice / Array - -```jet -{{ s := slice("foo", "bar", "asd") }} -{{ s[0] }} -{{ s[2] }} -``` - -#### Map - -```jet -{{ m := map("foo", 123, "bar", 456) }} -{{ m["foo"] }} -``` - -#### Struct - -```jet -{{ user["Name"] }} -``` - -### Field Access - -#### Map - -```jet -{{ m.foo }} -{{ range s }} - {{ .foo }} -{{ end }} -``` - -#### Struct - -```jet -{{ user.Name }} -{{ range users }} - {{ .Name }} -{{ end }} -``` - -### Slicing - -```jet -{{ s := slice(6, 7, 8, 9, 10, 11) }} -{{ sevenEightNine := s[1:4] }} -``` - -### Arithmetic - -```jet -{{ 1 + 2 * 3 - 4 }} -{{ (1 + 2) * 3 - 4.1 }} -``` - -### String Concatenation - -```jet -{{ "HELLO" + " " + "WORLD!" }} -``` - -#### Logical Operators - -- `&&` -- `||` -- `!` -- `==`, `!=` -- `<`, `>`, `<=`, `>=` - -```jet -{{ item == true || !item2 && item3 != "test" }} -{{ item >= 12.5 || item < 6 }} -``` - -### Ternary Operator - -```jet -{{ .HasTitle ? .Title : "Title not set" }} -``` - -### Method Calls - -```jet -{{ user.Rename("Peter") }} -{{ range users }} - {{ .FullName() }} -{{ end }} -``` - -### Function Calls - -```jet -{{ len(s) }} -{{ isset(foo, bar) }} -``` - -#### Prefix Syntax - -```jet -{{ len: s }} -{{ isset: foo, bar }} -``` - -#### Pipelining - -```jet -{{ "123" | len }} -{{ "FOO" | lower | len }} -{{ "hello" | repeat: 2 | len }} -``` - -**Escapers must be last in a pipeline:** - -```jet -{{ "hello" | upper | raw }} -{{ raw: "hello" }} -{{ raw: "hello" | upper }} -``` - -#### Piped Argument Slot - -```jet -{{ 2 | repeat("foo", _) }} -{{ 2 | repeat("foo", _) | repeat(_, 3) }} -``` - ---- - -## Control Structures - -### if - -```jet -{{ if foo == "asd" }} - foo is 'asd'! -{{ end }} -``` - -#### if / else - -```jet -{{ if foo == "asd" }} - ... -{{ else }} - ... -{{ end }} -``` - -#### if / else if - -```jet -{{ if foo == "asd" }} -{{ else if foo == 4711 }} -{{ end }} -``` - -#### if / else if / else - -```jet -{{ if foo == "asd" }} -{{ else if foo == 4711 }} -{{ else }} -{{ end }} -``` - -### range - -#### Slices / Arrays - -```jet -{{ range s }} - {{ . }} -{{ end }} - -{{ range i := s }} - {{ i }}: {{ . }} -{{ end }} - -{{ range i, v := s }} - {{ i }}: {{ v }} -{{ end }} -``` - -#### Maps - -```jet -{{ range k := m }} - {{ k }}: {{ . }} -{{ end }} - -{{ range k, v := m }} - {{ k }}: {{ v }} -{{ end }} -``` - -#### Channels - -```jet -{{ range v := c }} - {{ v }} -{{ end }} -``` - -#### Custom Ranger - -Any Go type implementing `Ranger` can be ranged over. - -#### else - -```jet -{{ range searchResults }} - {{ . }} -{{ else }} - No results found :( -{{ end }} -``` - -### try - -```jet -{{ try }} - {{ foo }} -{{ end }} -``` - -### try / catch - -```jet -{{ try }} - {{ foo }} -{{ catch }} - Fallback content -{{ end }} - -{{ try }} - {{ foo }} -{{ catch err }} - {{ log(err.Error()) }} - Error: {{ err.Error() }} -{{ end }} -``` - ---- - -## Templates - -### include - -```jet -{{ include "./user.jet" }} - - -

- {{ .["name"] }}: {{ .["email"] }} -
-``` - -### return - -```jet - -{{ return "foo" }} - - -{{ foo := exec("./foo.jet") }} -Hello, {{ foo }}! -``` - ---- - -## Blocks - -### block - -```jet -{{ block copyright() }} -
© ACME, Inc. 2020
-{{ end }} - -{{ block inputField(type="text", label, id, value="", required=false) }} - - -{{ end }} -``` - -### yield - -```jet -{{ yield copyright() }} - -{{ yield inputField(id="firstname", label="First name", required=true) }} - -{{ block buff() }} - {{ . }} -{{ end }} - -{{ yield buff() "Batman" }} -``` - -### content - -```jet -{{ block link(target) }} - {{ yield content }} -{{ end }} - -{{ yield link(target="https://example.com") content }} - Example Inc. -{{ end }} -``` - -```jet -{{ block header() }} -
- {{ yield content }} -
-{{ content }} -

Hey {{ name }}!

-{{ end }} -``` - -### Recursion - -```jet -{{ block menu() }} -
    - {{ range . }} -
  • {{ .Text }}{{ if len(.Children) }}{{ yield menu() .Children }}{{ end }}
  • - {{ end }} -
-{{ end }} -``` - -### extends - -```jet - -{{ extends "./layout.jet" }} -{{ block body() }} -
This content can be yielded anywhere.
-{{ end }} - - - - - {{ yield body() }} - - -``` - -### import - -```jet - -{{ block body() }} -
This content can be yielded anywhere.
-{{ end }} - - -{{ import "./my_blocks.jet" }} - - - {{ yield body() }} - - -``` \ No newline at end of file diff --git a/libarchive/mcp_old/pugconvert/mcp/handlers.v b/libarchive/mcp_old/pugconvert/mcp/handlers.v deleted file mode 100644 index c7e634ee..00000000 --- a/libarchive/mcp_old/pugconvert/mcp/handlers.v +++ /dev/null @@ -1,54 +0,0 @@ -module mcp - -import incubaid.herolib.mcp -import x.json2 as json { Any } -import incubaid.herolib.mcp.aitools.pugconvert -import incubaid.herolib.core.pathlib -import os - -pub fn handler(arguments map[string]Any) !ToolCallResult { - path := arguments['path'].str() - - // Check if path exists - if !os.exists(path) { - return ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]("Error: Path '${path}' does not exist") - } - } - - // Determine if path is a file or directory - is_directory := os.is_dir(path) - - mut message := '' - - if is_directory { - // Convert all pug files in the directory - pugconvert.convert_pug(path) or { - return ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]('Error converting pug files in directory: ${err}') - } - } - message = "Successfully converted all pug files in directory '${path}'" - } else if path.ends_with('.pug') { - // Convert a single pug file - pugconvert.convert_pug_file(path) or { - return ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]('Error converting pug file: ${err}') - } - } - message = "Successfully converted pug file '${path}'" - } else { - return ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]("Error: Path '${path}' is not a directory or .pug file") - } - } - - return ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](message) - } -} diff --git a/libarchive/mcp_old/pugconvert/mcp/mcp.v b/libarchive/mcp_old/pugconvert/mcp/mcp.v deleted file mode 100644 index bdb80640..00000000 --- a/libarchive/mcp_old/pugconvert/mcp/mcp.v +++ /dev/null @@ -1,27 +0,0 @@ -module mcp - -import incubaid.herolib.mcp -import incubaid.herolib.mcp.logger -import incubaid.herolib.schemas.jsonrpc - -pub fn new_mcp_server() !&Server { - logger.info('Creating new Developer MCP server') - - // Initialize the server with the empty handlers map - mut server := mcp.new_server(MemoryBackend{ - tools: { - 'pugconvert': specs - } - tool_handlers: { - 'pugconvert': handler - } - }, ServerParams{ - config: ServerConfiguration{ - server_info: ServerInfo{ - name: 'developer' - version: '1.0.0' - } - } - })! - return server -} diff --git a/libarchive/mcp_old/pugconvert/mcp/specifications.v b/libarchive/mcp_old/pugconvert/mcp/specifications.v deleted file mode 100644 index 6d923cad..00000000 --- a/libarchive/mcp_old/pugconvert/mcp/specifications.v +++ /dev/null @@ -1,21 +0,0 @@ -module mcp - -import incubaid.herolib.mcp -import x.json2 as json -import incubaid.herolib.schemas.jsonschema -import incubaid.herolib.mcp.logger - -const specs = Tool{ - name: 'pugconvert' - description: 'Convert Pug template files to Jet template files' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to a .pug file or directory containing .pug files to convert' - }) - } - required: ['path'] - } -} diff --git a/libarchive/mcp_old/rhai/cmd/.gitignore b/libarchive/mcp_old/rhai/cmd/.gitignore deleted file mode 100644 index 1a94556d..00000000 --- a/libarchive/mcp_old/rhai/cmd/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -main - diff --git a/libarchive/mcp_old/rhai/cmd/compile.sh b/libarchive/mcp_old/rhai/cmd/compile.sh deleted file mode 100755 index c6f91a11..00000000 --- a/libarchive/mcp_old/rhai/cmd/compile.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -ex - -export name="mcp_rhai" - -# Change to the directory containing this script -cd "$(dirname "$0")" - -# Compile the V program -v -n -w -gc none -cc tcc -d use_openssl -enable-globals main.v - -# Ensure the binary is executable -chmod +x main -mv main ~/hero/bin/${name} - -echo "Compilation successful. Binary '${name}' is ready." diff --git a/libarchive/mcp_old/rhai/cmd/main.v b/libarchive/mcp_old/rhai/cmd/main.v deleted file mode 100644 index f09d165c..00000000 --- a/libarchive/mcp_old/rhai/cmd/main.v +++ /dev/null @@ -1,18 +0,0 @@ -module main - -import incubaid.herolib.mcp.rhai.mcp -import log - -fn main() { - // Create a new MCP server - mut server := mcp.new_mcp_server() or { - log.error('Failed to create MCP server: ${err}') - return - } - - // Start the server - server.start() or { - log.error('Failed to start MCP server: ${err}') - return - } -} diff --git a/libarchive/mcp_old/rhai/example/example copy.vsh b/libarchive/mcp_old/rhai/example/example copy.vsh deleted file mode 100644 index 0749832a..00000000 --- a/libarchive/mcp_old/rhai/example/example copy.vsh +++ /dev/null @@ -1,532 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.mcp.aitools.escalayer -import os - -fn main() { - // Get the current directory - current_dir := os.dir(@FILE) - - // Check if a source code path was provided as an argument - if os.args.len < 2 { - println('Please provide the path to the source code directory as an argument') - println('Example: ./example.vsh /path/to/source/code/directory') - return - } - - // Get the source code path from the command line arguments - source_code_path := os.args[1] - - // Check if the path exists and is a directory - if !os.exists(source_code_path) { - println('Source code path does not exist: ${source_code_path}') - return - } - - if !os.is_dir(source_code_path) { - println('Source code path is not a directory: ${source_code_path}') - return - } - - // Get all Rust files in the directory - files := os.ls(source_code_path) or { - println('Failed to list files in directory: ${err}') - return - } - - // Combine all Rust files into a single source code string - mut source_code := '' - for file in files { - file_path := os.join_path(source_code_path, file) - - // Skip directories and non-Rust files - if os.is_dir(file_path) || !file.ends_with('.rs') { - continue - } - - // Read the file content - file_content := os.read_file(file_path) or { - println('Failed to read file ${file_path}: ${err}') - continue - } - - // Add file content to the combined source code - source_code += '// File: ${file}\n${file_content}\n\n' - } - - if source_code == '' { - println('No Rust files found in directory: ${source_code_path}') - return - } - - // Read the rhaiwrapping.md file - rhai_wrapping_md := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping.md') or { - println('Failed to read rhaiwrapping.md: ${err}') - return - } - - // Determine the crate path from the source code path - // Extract the path relative to the src directory - src_index := source_code_path.index('src/') or { - println('Could not determine crate path: src/ not found in path') - return - } - - mut path_parts := source_code_path[src_index + 4..].split('/') - // Remove the last part (the file name) - if path_parts.len > 0 { - path_parts.delete_last() - } - rel_path := path_parts.join('::') - crate_path := 'sal::${rel_path}' - - // Create a new task - mut task := escalayer.new_task( - name: 'rhai_wrapper_creator.escalayer' - description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file' - ) - - // Create model configs - sonnet_model := escalayer.ModelConfig{ - name: 'anthropic/claude-3.7-sonnet' - provider: 'anthropic' - temperature: 0.7 - max_tokens: 25000 - } - - gpt4_model := escalayer.ModelConfig{ - name: 'gpt-4' - provider: 'openai' - temperature: 0.7 - max_tokens: 25000 - } - - // Extract the module name from the directory path (last component) - dir_parts := source_code_path.split('/') - name := dir_parts[dir_parts.len - 1] - - // Create the prompt with source code, wrapper example, and rhai_wrapping_md - prompt_content := create_rhai_wrappers(name, source_code, os.read_file('${current_dir}/prompts/example_script.md') or { - '' - }, os.read_file('${current_dir}/prompts/wrapper.md') or { '' }, os.read_file('${current_dir}/prompts/errors.md') or { - '' - }, crate_path) - - // Create a prompt function that returns the prepared content - prompt_function := fn [prompt_content] (input string) string { - return prompt_content - } - - gen := RhaiGen{ - name: name - dir: source_code_path - } - - // Define a single unit task that handles everything - task.new_unit_task( - name: 'create_rhai_wrappers' - prompt_function: prompt_function - callback_function: gen.process_rhai_wrappers - base_model: sonnet_model - retry_model: gpt4_model - retry_count: 1 - ) - - // Initiate the task - result := task.initiate('') or { - println('Task failed: ${err}') - return - } - - println('Task completed successfully') - println('The wrapper files have been generated and compiled in the target directory.') - println('Check /Users/timurgordon/code/git.threefold.info/herocode/sal/src/rhai for the compiled output.') -} - -// Define the prompt functions -fn separate_functions(input string) string { - return 'Read the following Rust code and separate it into functions. Identify all the methods in the Container implementation and their purposes.\n\n${input}' -} - -fn create_wrappers(input string) string { - return 'Create Rhai wrappers for the Rust functions identified in the previous step. The wrappers should follow the builder pattern and provide a clean API for use in Rhai scripts. Include error handling and type conversion.\n\n${input}' -} - -fn create_example(input string) string { - return 'Create a Rhai example script that demonstrates how to use the wrapper functions. The example should be based on the provided example.rs file but adapted for Rhai syntax. Create a web server example that uses the container functions.\n\n${input}' -} - -// Define a Rhai wrapper generator function for Container functions -fn create_rhai_wrappers(name string, source_code string, example_rhai string, wrapper_md string, errors_md string, crate_path string) string { - guides := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping_classicai.md') or { - panic('Failed to read guides') - } - engine := $tmpl('./prompts/engine.md') - vector_vs_array := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_array_vs_vector.md') or { - panic('Failed to read guides') - } - rhai_integration_fixes := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_integration_fixes.md') or { - panic('Failed to read guides') - } - rhai_syntax_guide := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_syntax_guide.md') or { - panic('Failed to read guides') - } - generic_wrapper_rs := $tmpl('./templates/generic_wrapper.rs') - return 'You are a Rust developer tasked with creating Rhai wrappers for Rust functions. Please review the following best practices for Rhai wrappers and then create the necessary files. -${guides} -${vector_vs_array} -${example_rhai} -${wrapper_md} - -## Common Errors to Avoid -${errors_md} -${rhai_integration_fixes} -${rhai_syntax_guide} - -## Your Task - -Please create a wrapper.rs file that implements Rhai wrappers for the provided Rust code, and an example.rhai script that demonstrates how to use these wrappers: - -## Rust Code to Wrap - -```rust -${source_code} -``` - -IMPORTANT NOTES: -1. For Rhai imports, use: `use rhai::{Engine, EvalAltResult, plugin::*, Dynamic, Map, Array};` - only import what you actually use -2. The following dependencies are available in Cargo.toml: - - rhai = "1.21.0" - - serde = { version = "1.0", features = ["derive"] } - - serde_json = "1.0" - - sal = { path = "../../../" } - -3. For the wrapper: `use sal::${name};` this way you can access the module functions and objects with ${name}:: - -4. The generic_wrapper.rs file will be hardcoded into the package, you can use code from there. - -```rust -${generic_wrapper_rs} -``` - -5. IMPORTANT: Prefer strongly typed return values over Dynamic types whenever possible. Only use Dynamic when absolutely necessary. - - For example, return `Result>` instead of `Dynamic` when a function returns a string - - Use `Result>` instead of `Dynamic` when a function returns a boolean - - Use `Result, Box>` instead of `Dynamic` when a function returns a list of strings - -6. Your code should include public functions that can be called from Rhai scripts - -7. Make sure to implement all necessary helper functions for type conversion - -8. DO NOT use the #[rhai_fn] attribute - functions will be registered directly in the engine - -9. Make sure to handle string type consistency - use String::from() for string literals when returning in match arms with format!() strings - -10. When returning path references, convert them to owned strings (e.g., path().to_string()) - -11. For error handling, use proper Result types with Box for the error type: - ```rust - // INCORRECT: - pub fn some_function(arg: &str) -> Dynamic { - match some_operation(arg) { - Ok(result) => Dynamic::from(result), - Err(err) => Dynamic::from(format!("Error: {}", err)) - } - } - - // CORRECT: - pub fn some_function(arg: &str) -> Result> { - some_operation(arg).map_err(|err| { - Box::new(EvalAltResult::ErrorRuntime( - format!("Error: {}", err).into(), - rhai::Position::NONE - )) - }) - } - ``` - -12. IMPORTANT: Format your response with the code between triple backticks as follows: - -```rust -// wrapper.rs -// Your wrapper implementation here -``` - -```rust -// engine.rs -// Your engine.rs implementation here -``` - -```rhai -// example.rhai -// Your example Rhai script here -``` - -13. The example.rhai script should demonstrate the use of all the wrapper functions you create - -14. The engine.rs file should contain a register_module function that registers all the wrapper functions and types with the Rhai engine, and a create function. For example: - -${engine} - -MOST IMPORTANT: -import package being wrapped as `use sal::` -your engine create function is called `create_rhai_engine` - -``` -' -} - -@[params] -pub struct WrapperModule { -pub: - lib_rs string - example_rs string - engine_rs string - cargo_toml string - example_rhai string - generic_wrapper_rs string - wrapper_rs string -} - -// functions is a list of function names that AI should extract and pass in -fn create_wrapper_module(wrapper WrapperModule, functions []string, name_ string, base_dir string) !string { - // Define project directory paths - name := name_ - project_dir := '${base_dir}/rhai' - - // Create the project using cargo new --lib - if os.exists(project_dir) { - os.rmdir_all(project_dir) or { - return error('Failed to clean existing project directory: ${err}') - } - } - - // Run cargo new --lib to create the project - os.chdir(base_dir) or { return error('Failed to change directory to base directory: ${err}') } - - cargo_new_result := os.execute('cargo new --lib rhai') - if cargo_new_result.exit_code != 0 { - return error('Failed to create new library project: ${cargo_new_result.output}') - } - - // Create examples directory - examples_dir := '${project_dir}/examples' - os.mkdir_all(examples_dir) or { return error('Failed to create examples directory: ${err}') } - - // Write the lib.rs file - if wrapper.lib_rs != '' { - os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or { - return error('Failed to write lib.rs: ${err}') - } - } - - // Write the wrapper.rs file - if wrapper.wrapper_rs != '' { - os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or { - return error('Failed to write wrapper.rs: ${err}') - } - } - - // Write the generic wrapper.rs file - if wrapper.generic_wrapper_rs != '' { - os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or { - return error('Failed to write generic wrapper.rs: ${err}') - } - } - - // Write the example.rs file - if wrapper.example_rs != '' { - os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or { - return error('Failed to write example.rs: ${err}') - } - } - - // Write the engine.rs file if provided - if wrapper.engine_rs != '' { - os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or { - return error('Failed to write engine.rs: ${err}') - } - } - - // Write the Cargo.toml file - if wrapper.cargo_toml != '' { - os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or { - return error('Failed to write Cargo.toml: ${err}') - } - } - - // Write the example.rhai file if provided - if wrapper.example_rhai != '' { - os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or { - return error('Failed to write example.rhai: ${err}') - } - } - - return project_dir -} - -// Helper function to extract code blocks from the response -fn extract_code_block(response string, identifier string, language string) string { - // Find the start marker for the code block - mut start_marker := '```${language}\n// ${identifier}' - if language == '' { - start_marker = '```\n// ${identifier}' - } - - start_index := response.index(start_marker) or { - // Try alternative format - mut alt_marker := '```${language}\n${identifier}' - if language == '' { - alt_marker = '```\n${identifier}' - } - - response.index(alt_marker) or { return '' } - } - - // Find the end marker - end_marker := '```' - end_index := response.index_after(end_marker, start_index + start_marker.len) or { return '' } - - // Extract the content between the markers - content_start := start_index + start_marker.len - content := response[content_start..end_index].trim_space() - - return content -} - -// Extract module name from wrapper code -fn extract_module_name(code string) string { - lines := code.split('\n') - - for line in lines { - // Look for pub mod or mod declarations - if line.contains('pub mod ') || line.contains('mod ') { - // Extract module name - mut parts := []string{} - if line.contains('pub mod ') { - parts = line.split('pub mod ') - } else { - parts = line.split('mod ') - } - - if parts.len > 1 { - // Extract the module name and remove any trailing characters - mut name := parts[1].trim_space() - // Remove any trailing { or ; or whitespace - name = name.trim_right('{').trim_right(';').trim_space() - if name != '' { - return name - } - } - } - } - - return '' -} - -struct RhaiGen { - name string - dir string -} - -// Define the callback function that processes the response and compiles the code -fn (gen RhaiGen) process_rhai_wrappers(response string) !string { - // Extract wrapper.rs content - wrapper_rs_content := extract_code_block(response, 'wrapper.rs', 'rust') - if wrapper_rs_content == '' { - return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```') - } - - // Extract engine.rs content - mut engine_rs_content := extract_code_block(response, 'engine.rs', 'rust') - if engine_rs_content == '' { - // Try to extract from the response without explicit language marker - engine_rs_content = extract_code_block(response, 'engine.rs', '') - // if engine_rs_content == '' { - // // Use the template engine.rs - // engine_rs_content = $tmpl('./templates/engine.rs') - // } - } - - // Extract example.rhai content - mut example_rhai_content := extract_code_block(response, 'example.rhai', 'rhai') - if example_rhai_content == '' { - // Try to extract from the response without explicit language marker - example_rhai_content = extract_code_block(response, 'example.rhai', '') - if example_rhai_content == '' { - // Use the example from the template - example_script_md := os.read_file('${os.dir(@FILE)}/prompts/example_script.md') or { - return error('Failed to read example.rhai template: ${err}') - } - - // Extract the code block from the markdown file - example_rhai_content = extract_code_block(example_script_md, 'example.rhai', - 'rhai') - if example_rhai_content == '' { - return error('Failed to extract example.rhai from template file') - } - } - } - - // Extract function names from the wrapper.rs content - functions := extract_functions_from_code(wrapper_rs_content) - - println('Using module name: ${gen.name}_rhai') - println('Extracted functions: ${functions.join(', ')}') - - name := gen.name - // Create a WrapperModule struct with the extracted content - wrapper := WrapperModule{ - lib_rs: $tmpl('./templates/lib.rs') - wrapper_rs: wrapper_rs_content - example_rs: $tmpl('./templates/example.rs') - engine_rs: engine_rs_content - generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs') - cargo_toml: $tmpl('./templates/cargo.toml') - example_rhai: example_rhai_content - } - - // Create the wrapper module - base_target_dir := gen.dir - project_dir := create_wrapper_module(wrapper, functions, gen.name, base_target_dir) or { - return error('Failed to create wrapper module: ${err}') - } - - // Run the example - os.chdir(project_dir) or { return error('Failed to change directory to project: ${err}') } - - // Run cargo build first - build_result := os.execute('cargo build') - if build_result.exit_code != 0 { - return error('Compilation failed. Please fix the following errors and ensure your code is compatible with the existing codebase:\n\n${build_result.output}') - } - - // Run the example - run_result := os.execute('cargo run --example example') - - return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_result.output}\n\nRun output:\n${run_result.output}' -} - -// Extract function names from wrapper code -fn extract_functions_from_code(code string) []string { - mut functions := []string{} - lines := code.split('\n') - - for line in lines { - if line.contains('pub fn ') && !line.contains('//') { - // Extract function name - parts := line.split('pub fn ') - if parts.len > 1 { - name_parts := parts[1].split('(') - if name_parts.len > 0 { - fn_name := name_parts[0].trim_space() - if fn_name != '' { - functions << fn_name - } - } - } - } - } - - return functions -} diff --git a/libarchive/mcp_old/rhai/example/example.vsh b/libarchive/mcp_old/rhai/example/example.vsh deleted file mode 100755 index f2eedf72..00000000 --- a/libarchive/mcp_old/rhai/example/example.vsh +++ /dev/null @@ -1,661 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.mcp.aitools.escalayer -import incubaid.herolib.core.redisclient -import os - -fn main() { - // Example of using redisclient module instead of old redis.Connection - redis_example() or { println('Redis example failed: ${err}') } - - // Get the current directory where this script is located - current_dir := os.dir(@FILE) - - // Validate command line arguments - source_code_path := validate_command_args() or { - println(err) - return - } - - // Read and combine all Rust files in the source directory - source_code := read_source_code(source_code_path) or { - println(err) - return - } - - // Determine the crate path from the source code path - crate_path := determine_crate_path(source_code_path) or { - println(err) - return - } - - // Extract the module name from the directory path (last component) - name := extract_module_name_from_path(source_code_path) - - // Create the prompt content for the AI - prompt_content := create_rhai_wrappers(name, source_code, read_file_safely('${current_dir}/prompts/example_script.md'), - read_file_safely('${current_dir}/prompts/wrapper.md'), read_file_safely('${current_dir}/prompts/errors.md'), - crate_path) - - // Create the generator instance - gen := RhaiGen{ - name: name - dir: source_code_path - } - - // Run the task to generate Rhai wrappers - run_wrapper_generation_task(prompt_content, gen) or { - println('Task failed: ${err}') - return - } - - println('Task completed successfully') - println('The wrapper files have been generated and compiled in the target directory.') - println('Check /Users/timurgordon/code/git.threefold.info/herocode/sal/src/rhai for the compiled output.') -} - -// Validates command line arguments and returns the source code path -fn validate_command_args() !string { - if os.args.len < 2 { - return error('Please provide the path to the source code directory as an argument\nExample: ./example.vsh /path/to/source/code/directory') - } - - source_code_path := os.args[1] - - if !os.exists(source_code_path) { - return error('Source code path does not exist: ${source_code_path}') - } - - if !os.is_dir(source_code_path) { - return error('Source code path is not a directory: ${source_code_path}') - } - - return source_code_path -} - -// Reads and combines all Rust files in the given directory -fn read_source_code(source_code_path string) !string { - // Get all files in the directory - files := os.ls(source_code_path) or { - return error('Failed to list files in directory: ${err}') - } - - // Combine all Rust files into a single source code string - mut source_code := '' - for file in files { - file_path := os.join_path(source_code_path, file) - - // Skip directories and non-Rust files - if os.is_dir(file_path) || !file.ends_with('.rs') { - continue - } - - // Read the file content - file_content := os.read_file(file_path) or { - println('Failed to read file ${file_path}: ${err}') - continue - } - - // Add file content to the combined source code - source_code += '// File: ${file}\n${file_content}\n\n' - } - - if source_code == '' { - return error('No Rust files found in directory: ${source_code_path}') - } - - return source_code -} - -// Determines the crate path from the source code path -fn determine_crate_path(source_code_path string) !string { - // Extract the path relative to the src directory - src_index := source_code_path.index('src/') or { - return error('Could not determine crate path: src/ not found in path') - } - - mut path_parts := source_code_path[src_index + 4..].split('/') - // Remove the last part (the file name) - if path_parts.len > 0 { - path_parts.delete_last() - } - rel_path := path_parts.join('::') - return 'sal::${rel_path}' -} - -// Extracts the module name from a directory path -fn extract_module_name_from_path(path string) string { - dir_parts := path.split('/') - return dir_parts[dir_parts.len - 1] -} - -// Helper function to read a file or return empty string if file doesn't exist -fn read_file_safely(file_path string) string { - return os.read_file(file_path) or { '' } -} - -// Runs the task to generate Rhai wrappers -fn run_wrapper_generation_task(prompt_content string, gen RhaiGen) !string { - // Create a new task - mut task := escalayer.new_task( - name: 'rhai_wrapper_creator.escalayer' - description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file' - ) - - // Create model configs - sonnet_model := escalayer.ModelConfig{ - name: 'anthropic/claude-3.7-sonnet' - provider: 'anthropic' - temperature: 0.7 - max_tokens: 25000 - } - - gpt4_model := escalayer.ModelConfig{ - name: 'gpt-4' - provider: 'openai' - temperature: 0.7 - max_tokens: 25000 - } - - // Create a prompt function that returns the prepared content - prompt_function := fn [prompt_content] (input string) string { - return prompt_content - } - - // Define a single unit task that handles everything - task.new_unit_task( - name: 'create_rhai_wrappers' - prompt_function: prompt_function - callback_function: gen.process_rhai_wrappers - base_model: sonnet_model - retry_model: gpt4_model - retry_count: 1 - ) - - // Initiate the task - return task.initiate('') -} - -// Define a Rhai wrapper generator function for Container functions -fn create_rhai_wrappers(name string, source_code string, example_rhai string, wrapper_md string, errors_md string, crate_path string) string { - // Load all required template and guide files - guides := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping_classicai.md') - engine := $tmpl('./prompts/engine.md') - vector_vs_array := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_array_vs_vector.md') - rhai_integration_fixes := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_integration_fixes.md') - rhai_syntax_guide := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_syntax_guide.md') - generic_wrapper_rs := $tmpl('./templates/generic_wrapper.rs') - - // Build the prompt content - return build_prompt_content(name, source_code, example_rhai, wrapper_md, errors_md, - guides, vector_vs_array, rhai_integration_fixes, rhai_syntax_guide, generic_wrapper_rs, - engine) -} - -// Helper function to load guide files with error handling -fn load_guide_file(path string) string { - return os.read_file(path) or { - eprintln('Warning: Failed to read guide file: ${path}') - return '' - } -} - -// Builds the prompt content for the AI -fn build_prompt_content(name string, source_code string, example_rhai string, wrapper_md string, - errors_md string, guides string, vector_vs_array string, - rhai_integration_fixes string, rhai_syntax_guide string, - generic_wrapper_rs string, engine string) string { - return 'You are a Rust developer tasked with creating Rhai wrappers for Rust functions. Please review the following best practices for Rhai wrappers and then create the necessary files. -${guides} -${vector_vs_array} -${example_rhai} -${wrapper_md} - -## Common Errors to Avoid -${errors_md} -${rhai_integration_fixes} -${rhai_syntax_guide} - -## Your Task - -Please create a wrapper.rs file that implements Rhai wrappers for the provided Rust code, and an example.rhai script that demonstrates how to use these wrappers: - -## Rust Code to Wrap - -```rust -${source_code} -``` - -IMPORTANT NOTES: -1. For Rhai imports, use: `use rhai::{Engine, EvalAltResult, plugin::*, Dynamic, Map, Array};` - only import what you actually use -2. The following dependencies are available in Cargo.toml: - - rhai = "1.21.0" - - serde = { version = "1.0", features = ["derive"] } - - serde_json = "1.0" - - sal = { path = "../../../" } - -3. For the wrapper: `use sal::${name};` this way you can access the module functions and objects with ${name}:: - -4. The generic_wrapper.rs file will be hardcoded into the package, you can use code from there. - -```rust -${generic_wrapper_rs} -``` - -5. IMPORTANT: Prefer strongly typed return values over Dynamic types whenever possible. Only use Dynamic when absolutely necessary. - - For example, return `Result>` instead of `Dynamic` when a function returns a string - - Use `Result>` instead of `Dynamic` when a function returns a boolean - - Use `Result, Box>` instead of `Dynamic` when a function returns a list of strings - -6. Your code should include public functions that can be called from Rhai scripts - -7. Make sure to implement all necessary helper functions for type conversion - -8. DO NOT use the #[rhai_fn] attribute - functions will be registered directly in the engine - -9. Make sure to handle string type consistency - use String::from() for string literals when returning in match arms with format!() strings - -10. When returning path references, convert them to owned strings (e.g., path().to_string()) - -11. For error handling, use proper Result types with Box for the error type: - ```rust - // INCORRECT: - pub fn some_function(arg: &str) -> Dynamic { - match some_operation(arg) { - Ok(result) => Dynamic::from(result), - Err(err) => Dynamic::from(format!("Error: {}", err)) - } - } - - // CORRECT: - pub fn some_function(arg: &str) -> Result> { - some_operation(arg).map_err(|err| { - Box::new(EvalAltResult::ErrorRuntime( - format!("Error: {}", err).into(), - rhai::Position::NONE - )) - }) - } - ``` - -12. IMPORTANT: Format your response with the code between triple backticks as follows: - -```rust -// wrapper.rs -// Your wrapper implementation here -``` - -```rust -// engine.rs -// Your engine.rs implementation here -``` - -```rhai -// example.rhai -// Your example Rhai script here -``` - -13. The example.rhai script should demonstrate the use of all the wrapper functions you create - -14. The engine.rs file should contain a register_module function that registers all the wrapper functions and types with the Rhai engine, and a create function. For example: - -${engine} - -MOST IMPORTANT: -import package being wrapped as `use sal::` -your engine create function is called `create_rhai_engine` - -``` -' -} - -@[params] -pub struct WrapperModule { -pub: - lib_rs string - example_rs string - engine_rs string - cargo_toml string - example_rhai string - generic_wrapper_rs string - wrapper_rs string -} - -// functions is a list of function names that AI should extract and pass in -fn create_wrapper_module(wrapper WrapperModule, functions []string, name_ string, base_dir string) !string { - // Define project directory paths - name := name_ - project_dir := '${base_dir}/rhai' - - // Create the project using cargo new --lib - if os.exists(project_dir) { - os.rmdir_all(project_dir) or { - return error('Failed to clean existing project directory: ${err}') - } - } - - // Run cargo new --lib to create the project - os.chdir(base_dir) or { return error('Failed to change directory to base directory: ${err}') } - - cargo_new_result := os.execute('cargo new --lib rhai') - if cargo_new_result.exit_code != 0 { - return error('Failed to create new library project: ${cargo_new_result.output}') - } - - // Create examples directory - examples_dir := '${project_dir}/examples' - os.mkdir_all(examples_dir) or { return error('Failed to create examples directory: ${err}') } - - // Write the lib.rs file - if wrapper.lib_rs != '' { - os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or { - return error('Failed to write lib.rs: ${err}') - } - } - - // Write the wrapper.rs file - if wrapper.wrapper_rs != '' { - os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or { - return error('Failed to write wrapper.rs: ${err}') - } - } - - // Write the generic wrapper.rs file - if wrapper.generic_wrapper_rs != '' { - os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or { - return error('Failed to write generic wrapper.rs: ${err}') - } - } - - // Write the example.rs file - if wrapper.example_rs != '' { - os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or { - return error('Failed to write example.rs: ${err}') - } - } - - // Write the engine.rs file if provided - if wrapper.engine_rs != '' { - os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or { - return error('Failed to write engine.rs: ${err}') - } - } - - // Write the Cargo.toml file - if wrapper.cargo_toml != '' { - os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or { - return error('Failed to write Cargo.toml: ${err}') - } - } - - // Write the example.rhai file if provided - if wrapper.example_rhai != '' { - os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or { - return error('Failed to write example.rhai: ${err}') - } - } - - return project_dir -} - -// Helper function to extract code blocks from the response -fn extract_code_block(response string, identifier string, language string) string { - // Find the start marker for the code block - mut start_marker := '```${language}\n// ${identifier}' - if language == '' { - start_marker = '```\n// ${identifier}' - } - - start_index := response.index(start_marker) or { - // Try alternative format - mut alt_marker := '```${language}\n${identifier}' - if language == '' { - alt_marker = '```\n${identifier}' - } - - response.index(alt_marker) or { return '' } - } - - // Find the end marker - end_marker := '```' - end_index := response.index_after(end_marker, start_index + start_marker.len) or { return '' } - - // Extract the content between the markers - content_start := start_index + start_marker.len - content := response[content_start..end_index].trim_space() - - return content -} - -// Extract module name from wrapper code -fn extract_module_name(code string) string { - lines := code.split('\n') - - for line in lines { - // Look for pub mod or mod declarations - if line.contains('pub mod ') || line.contains('mod ') { - // Extract module name - mut parts := []string{} - if line.contains('pub mod ') { - parts = line.split('pub mod ') - } else { - parts = line.split('mod ') - } - - if parts.len > 1 { - // Extract the module name and remove any trailing characters - mut name := parts[1].trim_space() - // Remove any trailing { or ; or whitespace - name = name.trim_right('{').trim_right(';').trim_space() - if name != '' { - return name - } - } - } - } - - return '' -} - -// RhaiGen struct for generating Rhai wrappers -struct RhaiGen { - name string - dir string -} - -// Process the AI response and compile the generated code -fn (gen RhaiGen) process_rhai_wrappers(response string) !string { - // Extract code blocks from the response - code_blocks := extract_code_blocks(response) or { return err } - - // Extract function names from the wrapper.rs content - functions := extract_functions_from_code(code_blocks.wrapper_rs) - - println('Using module name: ${gen.name}_rhai') - println('Extracted functions: ${functions.join(', ')}') - - name := gen.name - - // Create a WrapperModule struct with the extracted content - wrapper := WrapperModule{ - lib_rs: $tmpl('./templates/lib.rs') - wrapper_rs: code_blocks.wrapper_rs - example_rs: $tmpl('./templates/example.rs') - engine_rs: code_blocks.engine_rs - generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs') - cargo_toml: $tmpl('./templates/cargo.toml') - example_rhai: code_blocks.example_rhai - } - - // Create the wrapper module - project_dir := create_wrapper_module(wrapper, functions, gen.name, gen.dir) or { - return error('Failed to create wrapper module: ${err}') - } - - // Build and run the project - build_output, run_output := build_and_run_project(project_dir) or { return err } - - return format_success_message(project_dir, build_output, run_output) -} - -// CodeBlocks struct to hold extracted code blocks -struct CodeBlocks { - wrapper_rs string - engine_rs string - example_rhai string -} - -// Extract code blocks from the AI response -fn extract_code_blocks(response string) !CodeBlocks { - // Extract wrapper.rs content - wrapper_rs_content := extract_code_block(response, 'wrapper.rs', 'rust') - if wrapper_rs_content == '' { - return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```') - } - - // Extract engine.rs content - mut engine_rs_content := extract_code_block(response, 'engine.rs', 'rust') - if engine_rs_content == '' { - // Try to extract from the response without explicit language marker - engine_rs_content = extract_code_block(response, 'engine.rs', '') - } - - // Extract example.rhai content - mut example_rhai_content := extract_code_block(response, 'example.rhai', 'rhai') - if example_rhai_content == '' { - // Try to extract from the response without explicit language marker - example_rhai_content = extract_code_block(response, 'example.rhai', '') - if example_rhai_content == '' { - // Use the example from the template - example_rhai_content = load_example_from_template() or { return err } - } - } - - return CodeBlocks{ - wrapper_rs: wrapper_rs_content - engine_rs: engine_rs_content - example_rhai: example_rhai_content - } -} - -// Load example.rhai from template file -fn load_example_from_template() !string { - example_script_md := os.read_file('${os.dir(@FILE)}/prompts/example_script.md') or { - return error('Failed to read example.rhai template: ${err}') - } - - // Extract the code block from the markdown file - example_rhai_content := extract_code_block(example_script_md, 'example.rhai', 'rhai') - if example_rhai_content == '' { - return error('Failed to extract example.rhai from template file') - } - - return example_rhai_content -} - -// Build and run the project -fn build_and_run_project(project_dir string) !(string, string) { - // Change to the project directory - os.chdir(project_dir) or { return error('Failed to change directory to project: ${err}') } - - // Run cargo build first - build_result := os.execute('cargo build') - if build_result.exit_code != 0 { - return error('Compilation failed. Please fix the following errors and ensure your code is compatible with the existing codebase:\n\n${build_result.output}') - } - - // Run the example - run_result := os.execute('cargo run --example example') - - return build_result.output, run_result.output -} - -// Format success message -fn format_success_message(project_dir string, build_output string, run_output string) string { - return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_output}\n\nRun output:\n${run_output}' -} - -// Extract function names from wrapper code -fn extract_functions_from_code(code string) []string { - mut functions := []string{} - lines := code.split('\n') - - for line in lines { - if line.contains('pub fn ') && !line.contains('//') { - // Extract function name - parts := line.split('pub fn ') - if parts.len > 1 { - name_parts := parts[1].split('(') - if name_parts.len > 0 { - fn_name := name_parts[0].trim_space() - if fn_name != '' { - functions << fn_name - } - } - } - } - } - - return functions -} - -// Example function showing how to use redisclient module instead of old redis.Connection -fn redis_example() ! { - // OLD WAY (don't use this): - // mut conns := []redis.Connection{} - // for s in servers { - // mut c := redis.connect(redis.Options{ server: s }) or { - // panic('could not connect to redis $s: $err') - // } - // conns << c - // } - - // NEW WAY using redisclient module: - servers := ['127.0.0.1:6379', '127.0.0.1:6380', '127.0.0.1:6381', '127.0.0.1:6382'] - mut redis_clients := []&redisclient.Redis{} - - for server in servers { - // Parse server address - redis_url := redisclient.get_redis_url(server) or { - println('Failed to parse Redis URL ${server}: ${err}') - continue - } - - // Create Redis client using redisclient module - mut redis_client := redisclient.core_get(redis_url) or { - println('Failed to connect to Redis ${server}: ${err}') - continue - } - - // Test the connection - redis_client.ping() or { - println('Failed to ping Redis ${server}: ${err}') - continue - } - - redis_clients << redis_client - println('Successfully connected to Redis server: ${server}') - } - - // Example usage of Redis operations - if redis_clients.len > 0 { - mut redis := redis_clients[0] - - // Set a test key - redis.set('test_key', 'test_value') or { - println('Failed to set test key: ${err}') - return - } - - // Get the test key - value := redis.get('test_key') or { - println('Failed to get test key: ${err}') - return - } - - println('Redis test successful - key: test_key, value: ${value}') - - // Clean up - redis.del('test_key') or { println('Failed to delete test key: ${err}') } - } -} diff --git a/libarchive/mcp_old/rhai/logic/logic.v b/libarchive/mcp_old/rhai/logic/logic.v deleted file mode 100644 index 2696312a..00000000 --- a/libarchive/mcp_old/rhai/logic/logic.v +++ /dev/null @@ -1,268 +0,0 @@ -module logic - -import incubaid.herolib.ai.escalayer -import incubaid.herolib.lang.rust -import incubaid.herolib.ai.utils -import os - -pub fn generate_rhai_wrapper(name string, source_path string) !string { - prompt := rhai_wrapper_generation_prompt(name, source_path) or { panic(err) } - return run_wrapper_generation_task(prompt, RhaiGen{ - name: name - dir: source_path - }) or { panic(err) } -} - -// Runs the task to generate Rhai wrappers -pub fn run_wrapper_generation_task(prompt_content string, gen RhaiGen) !string { - // Create a new task - mut task := escalayer.new_task( - name: 'rhai_wrapper_creator.escalayer' - description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file' - ) - - // Create model configs - sonnet_model := escalayer.ModelConfig{ - name: 'anthropic/claude-3.7-sonnet' - provider: 'anthropic' - temperature: 0.7 - max_tokens: 25000 - } - - gpt4_model := escalayer.ModelConfig{ - name: 'gpt-4' - provider: 'openai' - temperature: 0.7 - max_tokens: 25000 - } - - // Create a prompt function that returns the prepared content - prompt_function := fn [prompt_content] (input string) string { - return prompt_content - } - - // Define a single unit task that handles everything - task.new_unit_task( - name: 'create_rhai_wrappers' - prompt_function: prompt_function - callback_function: gen.process_rhai_wrappers - base_model: sonnet_model - retry_model: gpt4_model - retry_count: 1 - ) - - // Initiate the task - return task.initiate('') -} - -// Define a Rhai wrapper generator function for Container functions -pub fn rhai_wrapper_generation_prompt(name string, source_code string) !string { - current_dir := os.dir(@FILE) - example_rhai := os.read_file('${current_dir}/prompts/example_script.md') or { panic(err) } - wrapper_md := os.read_file('${current_dir}/prompts/wrapper.md') or { panic(err) } - errors_md := os.read_file('${current_dir}/prompts/errors.md') or { panic(err) } - - // Load all required template and guide files - guides := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping_classicai.md')! - engine := $tmpl('./prompts/engine.md') - vector_vs_array := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_array_vs_vector.md')! - rhai_integration_fixes := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_integration_fixes.md')! - rhai_syntax_guide := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_syntax_guide.md')! - generic_wrapper_rs := $tmpl('./templates/generic_wrapper.rs') - - prompt := $tmpl('./prompts/main.md') - return prompt -} - -@[params] -pub struct WrapperModule { -pub: - lib_rs string - example_rs string - engine_rs string - cargo_toml string - example_rhai string - generic_wrapper_rs string - wrapper_rs string -} - -// functions is a list of function names that AI should extract and pass in -pub fn write_rhai_wrapper_module(wrapper WrapperModule, name string, path string) !string { - // Define project directory paths - project_dir := '${path}/rhai' - - // Create the project using cargo new --lib - if os.exists(project_dir) { - os.rmdir_all(project_dir) or { - return error('Failed to clean existing project directory: ${err}') - } - } - - // Run cargo new --lib to create the project - os.chdir(path) or { return error('Failed to change directory to base directory: ${err}') } - - cargo_new_result := os.execute('cargo new --lib rhai') - if cargo_new_result.exit_code != 0 { - return error('Failed to create new library project: ${cargo_new_result.output}') - } - - // Create examples directory - examples_dir := '${project_dir}/examples' - os.mkdir_all(examples_dir) or { return error('Failed to create examples directory: ${err}') } - - // Write the lib.rs file - if wrapper.lib_rs != '' { - os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or { - return error('Failed to write lib.rs: ${err}') - } - } - - // Write the wrapper.rs file - if wrapper.wrapper_rs != '' { - os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or { - return error('Failed to write wrapper.rs: ${err}') - } - } - - // Write the generic wrapper.rs file - if wrapper.generic_wrapper_rs != '' { - os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or { - return error('Failed to write generic wrapper.rs: ${err}') - } - } - - // Write the example.rs file - if wrapper.example_rs != '' { - os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or { - return error('Failed to write example.rs: ${err}') - } - } - - // Write the engine.rs file if provided - if wrapper.engine_rs != '' { - os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or { - return error('Failed to write engine.rs: ${err}') - } - } - - // Write the Cargo.toml file - os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or { - return error('Failed to write Cargo.toml: ${err}') - } - - // Write the example.rhai file - os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or { - return error('Failed to write example.rhai: ${err}') - } - - return project_dir -} - -// Extract module name from wrapper code -fn extract_module_name(code string) string { - lines := code.split('\n') - - for line in lines { - // Look for pub mod or mod declarations - if line.contains('pub mod ') || line.contains('mod ') { - // Extract module name - mut parts := []string{} - if line.contains('pub mod ') { - parts = line.split('pub mod ') - } else { - parts = line.split('mod ') - } - - if parts.len > 1 { - // Extract the module name and remove any trailing characters - mut name := parts[1].trim_space() - // Remove any trailing { or ; or whitespace - name = name.trim_right('{').trim_right(';').trim_space() - if name != '' { - return name - } - } - } - } - - return '' -} - -// RhaiGen struct for generating Rhai wrappers -struct RhaiGen { - name string - dir string -} - -// Process the AI response and compile the generated code -fn (gen RhaiGen) process_rhai_wrappers(response string) !string { - // Extract code blocks from the response - code_blocks := extract_code_blocks(response) or { return err } - - name := gen.name - - // Create a WrapperModule struct with the extracted content - wrapper := WrapperModule{ - lib_rs: $tmpl('./templates/lib.rs') - wrapper_rs: code_blocks.wrapper_rs - example_rs: $tmpl('./templates/example.rs') - engine_rs: code_blocks.engine_rs - generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs') - cargo_toml: $tmpl('./templates/cargo.toml') - example_rhai: code_blocks.example_rhai - } - - // Create the wrapper module - project_dir := write_rhai_wrapper_module(wrapper, gen.name, gen.dir) or { - return error('Failed to create wrapper module: ${err}') - } - - // Build and run the project - build_output, run_output := rust.run_example(project_dir, 'example') or { return err } - - return format_success_message(project_dir, build_output, run_output) -} - -// CodeBlocks struct to hold extracted code blocks -struct CodeBlocks { - wrapper_rs string - engine_rs string - example_rhai string -} - -// Extract code blocks from the AI response -fn extract_code_blocks(response string) !CodeBlocks { - // Extract wrapper.rs content - wrapper_rs_content := utils.extract_code_block(response, 'wrapper.rs', 'rust') - if wrapper_rs_content == '' { - return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```') - } - - // Extract engine.rs content - mut engine_rs_content := utils.extract_code_block(response, 'engine.rs', 'rust') - if engine_rs_content == '' { - // Try to extract from the response without explicit language marker - engine_rs_content = utils.extract_code_block(response, 'engine.rs', '') - } - - // Extract example.rhai content - mut example_rhai_content := utils.extract_code_block(response, 'example.rhai', 'rhai') - if example_rhai_content == '' { - // Try to extract from the response without explicit language marker - example_rhai_content = utils.extract_code_block(response, 'example.rhai', '') - if example_rhai_content == '' { - return error('Failed to extract example.rhai content from response. Please ensure your code is properly formatted inside a code block that starts with ```rhai\n// example.rhai and ends with ```') - } - } - - return CodeBlocks{ - wrapper_rs: wrapper_rs_content - engine_rs: engine_rs_content - example_rhai: example_rhai_content - } -} - -// Format success message -fn format_success_message(project_dir string, build_output string, run_output string) string { - return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_output}\n\nRun output:\n${run_output}' -} diff --git a/libarchive/mcp_old/rhai/logic/prompts/engine.md b/libarchive/mcp_old/rhai/logic/prompts/engine.md deleted file mode 100644 index cbf9370b..00000000 --- a/libarchive/mcp_old/rhai/logic/prompts/engine.md +++ /dev/null @@ -1,125 +0,0 @@ - -# Engine - -Here is an example of a well-implemented Rhai engine for the Git module: - -## Example engine - -```rust -// engine.rs - -/// Register Nerdctl module functions with the Rhai engine -pub fn create_rhai_engine() -> Engine { - let mut engine = Engine::new(); - - register_nerdctl_module(&mut engine)?; - register_nerdctl_types(&mut engine)?; - - engine -} - -pub fn register_nerdctl_module(engine: &mut Engine) -> Result<(), Box> { - // Register Container constructor - engine.register_fn("nerdctl_container_new", container_new); - engine.register_fn("nerdctl_container_from_image", container_from_image); - - // Register Container instance methods - engine.register_fn("reset", container_reset); - engine.register_fn("with_port", container_with_port); - engine.register_fn("with_volume", container_with_volume); - engine.register_fn("with_env", container_with_env); - engine.register_fn("with_network", container_with_network); - engine.register_fn("with_network_alias", container_with_network_alias); - engine.register_fn("with_cpu_limit", container_with_cpu_limit); - engine.register_fn("with_memory_limit", container_with_memory_limit); - engine.register_fn("with_restart_policy", container_with_restart_policy); - engine.register_fn("with_health_check", container_with_health_check); - engine.register_fn("with_ports", container_with_ports); - engine.register_fn("with_volumes", container_with_volumes); - engine.register_fn("with_envs", container_with_envs); - engine.register_fn("with_network_aliases", container_with_network_aliases); - engine.register_fn("with_memory_swap_limit", container_with_memory_swap_limit); - engine.register_fn("with_cpu_shares", container_with_cpu_shares); - engine.register_fn("with_health_check_options", container_with_health_check_options); - engine.register_fn("with_snapshotter", container_with_snapshotter); - engine.register_fn("with_detach", container_with_detach); - engine.register_fn("build", container_build); - engine.register_fn("start", container_start); - engine.register_fn("stop", container_stop); - engine.register_fn("remove", container_remove); - engine.register_fn("exec", container_exec); - engine.register_fn("logs", container_logs); - engine.register_fn("copy", container_copy); - - // Register legacy container functions (for backward compatibility) - engine.register_fn("nerdctl_run", nerdctl_run); - engine.register_fn("nerdctl_run_with_name", nerdctl_run_with_name); - engine.register_fn("nerdctl_run_with_port", nerdctl_run_with_port); - engine.register_fn("new_run_options", new_run_options); - engine.register_fn("nerdctl_exec", nerdctl_exec); - engine.register_fn("nerdctl_copy", nerdctl_copy); - engine.register_fn("nerdctl_stop", nerdctl_stop); - engine.register_fn("nerdctl_remove", nerdctl_remove); - engine.register_fn("nerdctl_list", nerdctl_list); - engine.register_fn("nerdctl_logs", nerdctl_logs); - - // Register image functions - engine.register_fn("nerdctl_images", nerdctl_images); - engine.register_fn("nerdctl_image_remove", nerdctl_image_remove); - engine.register_fn("nerdctl_image_push", nerdctl_image_push); - engine.register_fn("nerdctl_image_tag", nerdctl_image_tag); - engine.register_fn("nerdctl_image_pull", nerdctl_image_pull); - engine.register_fn("nerdctl_image_commit", nerdctl_image_commit); - engine.register_fn("nerdctl_image_build", nerdctl_image_build); - - Ok(()) -} - -/// Register Nerdctl module types with the Rhai engine -fn register_nerdctl_types(engine: &mut Engine) -> Result<(), Box> { - // Register Container type - engine.register_type_with_name::("NerdctlContainer"); - - // Register getters for Container properties - engine.register_get("name", |container: &mut Container| container.name.clone()); - engine.register_get("container_id", |container: &mut Container| { - match &container.container_id { - Some(id) => id.clone(), - None => "".to_string(), - } - }); - engine.register_get("image", |container: &mut Container| { - match &container.image { - Some(img) => img.clone(), - None => "".to_string(), - } - }); - engine.register_get("ports", |container: &mut Container| { - let mut array = Array::new(); - for port in &container.ports { - array.push(Dynamic::from(port.clone())); - } - array - }); - engine.register_get("volumes", |container: &mut Container| { - let mut array = Array::new(); - for volume in &container.volumes { - array.push(Dynamic::from(volume.clone())); - } - array - }); - engine.register_get("detach", |container: &mut Container| container.detach); - - // Register Image type and methods - engine.register_type_with_name::("NerdctlImage"); - - // Register getters for Image properties - engine.register_get("id", |img: &mut Image| img.id.clone()); - engine.register_get("repository", |img: &mut Image| img.repository.clone()); - engine.register_get("tag", |img: &mut Image| img.tag.clone()); - engine.register_get("size", |img: &mut Image| img.size.clone()); - engine.register_get("created", |img: &mut Image| img.created.clone()); - - Ok(()) -} -``` \ No newline at end of file diff --git a/libarchive/mcp_old/rhai/logic/prompts/errors.md b/libarchive/mcp_old/rhai/logic/prompts/errors.md deleted file mode 100644 index d80587fe..00000000 --- a/libarchive/mcp_old/rhai/logic/prompts/errors.md +++ /dev/null @@ -1,186 +0,0 @@ -# Common Errors in Rhai Wrappers and How to Fix Them - -When creating Rhai wrappers for Rust functions, you might encounter several common errors. Here's how to address them: - -## 1. `rhai_fn` Attribute Errors - -``` -error: cannot find attribute `rhai_fn` in this scope -``` - -**Solution**: Do not use the `#[rhai_fn]` attribute. Instead, register functions directly in the engine: - -```rust -// INCORRECT: -#[rhai_fn(name = "pull_repository")] -pub fn pull_repository(repo: &mut GitRepo) -> Dynamic { ... } - -// CORRECT: -pub fn pull_repository(repo: &mut GitRepo) -> Dynamic { ... } -// Then register in engine.rs: -engine.register_fn("pull_repository", pull_repository); -``` - -## 2. Function Visibility Errors - -``` -error[E0603]: function `create_rhai_engine` is private -``` - -**Solution**: Make sure to declare functions as `pub` when they need to be accessed from other modules: - -```rust -// INCORRECT: -fn create_rhai_engine() -> Engine { ... } - -// CORRECT: -pub fn create_rhai_engine() -> Engine { ... } -``` - -## 3. Type Errors with String vs &str - -``` -error[E0308]: `match` arms have incompatible types -``` - -**Solution**: Ensure consistent return types in match arms. When one arm returns a string literal (`&str`) and another returns a `String`, convert them to be consistent: - -```rust -// INCORRECT: -match r.pull() { - Ok(_) => "Successfully pulled changes", - Err(err) => { - let error_msg = format!("Error pulling changes: {}", err); - error_msg // This is a String, not matching the &str above - } -} - -// CORRECT - Option 1: Convert &str to String -match r.pull() { - Ok(_) => String::from("Successfully pulled changes"), - Err(err) => format!("Error pulling changes: {}", err) -} - -// CORRECT - Option 2: Use String::from for all string literals -match r.pull() { - Ok(_) => String::from("Successfully pulled changes"), - Err(err) => { - let error_msg = format!("Error pulling changes: {}", err); - error_msg - } -} -``` - -## 4. Lifetime Errors - -``` -error: lifetime may not live long enough -``` - -**Solution**: When returning references from closures, you need to ensure the lifetime is valid. For path operations, convert to owned strings: - -```rust -// INCORRECT: -repo_clone.wrap(|r| r.path()) - -// CORRECT: -repo_clone.wrap(|r| r.path().to_string()) -``` - -## 5. Sized Trait Errors - -``` -error[E0277]: the size for values of type `Self` cannot be known at compilation time -``` - -**Solution**: Add a `Sized` bound to the `Self` type in trait definitions: - -```rust -// INCORRECT: -trait RhaiWrapper { - fn wrap(&self, f: F) -> Dynamic - where - F: FnOnce(Self) -> R, - R: ToRhai; -} - -// CORRECT: -trait RhaiWrapper { - fn wrap(&self, f: F) -> Dynamic - where - F: FnOnce(Self) -> R, - R: ToRhai, - Self: Sized; -} -``` - -## 6. Unused Imports - -``` -warning: unused imports: `Engine`, `EvalAltResult`, `FLOAT`, `INT`, and `plugin::*` -``` - -**Solution**: Remove unused imports to clean up your code: - -```rust -// INCORRECT: -use rhai::{Engine, EvalAltResult, plugin::*, FLOAT, INT, Dynamic, Map, Array}; - -// CORRECT - only keep what you use: -use rhai::{Dynamic, Array}; -``` - -## 7. Overuse of Dynamic Types - -``` -error[E0277]: the trait bound `Vec: generic_wrapper::ToRhai` is not satisfied -``` - -**Solution**: Use proper static typing instead of Dynamic types whenever possible. This improves type safety and makes the code more maintainable: - -```rust -// INCORRECT: Returning Dynamic for everything -pub fn list_repositories(tree: &mut GitTree) -> Dynamic { - let tree_clone = tree.clone(); - tree_clone.wrap(|t| { - match t.list() { - Ok(repos) => repos, - Err(err) => vec![format!("Error listing repositories: {}", err)] - } - }) -} - -// CORRECT: Using proper Result types -pub fn list_repositories(tree: &mut GitTree) -> Result, Box> { - let tree_clone = tree.clone(); - tree_clone.list().map_err(|err| { - Box::new(EvalAltResult::ErrorRuntime( - format!("Error listing repositories: {}", err).into(), - rhai::Position::NONE - )) - }) -} -``` - -## 8. Improper Error Handling - -``` -error[E0277]: the trait bound `for<'a> fn(&'a mut Engine) -> Result<(), Box> {wrapper::register_git_module}: RhaiNativeFunc<_, _, _, _, _>` is not satisfied -``` - -**Solution**: When registering functions that return Result types, make sure they are properly handled: - -```rust -// INCORRECT: Trying to register a function that returns Result<(), Box> -engine.register_fn("register_git_module", wrapper::register_git_module); - -// CORRECT: Wrap the function to handle the Result -engine.register_fn("register_git_module", |engine: &mut Engine| { - match wrapper::register_git_module(engine) { - Ok(_) => Dynamic::from(true), - Err(err) => Dynamic::from(format!("Error: {}", err)) - } -}); -``` - -Remember to adapt these solutions to your specific code context. The key is to maintain type consistency, proper visibility, correct lifetime management, and appropriate static typing. diff --git a/libarchive/mcp_old/rhai/logic/prompts/example_script.md b/libarchive/mcp_old/rhai/logic/prompts/example_script.md deleted file mode 100644 index e67422ba..00000000 --- a/libarchive/mcp_old/rhai/logic/prompts/example_script.md +++ /dev/null @@ -1,40 +0,0 @@ -## Example Rhai Script - -Now, given the source code you wrapped using Rhai executable functions, write an example Rhai script that uses those functions. - -### Example example rhai script - -```rhai -// example.rhai -// Create a new GitTree instance -let git_tree = new_git_tree("/Users/timurgordon/code"); -print("\nCreated GitTree for: /Users/timurgordon/code"); - -// List repositories in the tree -let repos = list_repositories(git_tree); -print("Found " + repos.len() + " repositories"); - -if repos.len() > 0 { - print("First repository: " + repos[0]); - - // Get the repository - let repo_array = get_repositories(git_tree, repos[0]); - - if repo_array.len() > 0 { - let repo = repo_array[0]; - print("\nRepository path: " + path(repo)); - - // Check if the repository has changes - let has_changes = has_changes(repo); - print("Has changes: " + has_changes); - - // Try to pull the repository - print("\nTrying to pull repository..."); - let pull_result = pull_repository(repo); - print("Pull result: " + pull_result); - } -} - -print("\nResult: Git operations completed successfully"); -42 // Return value -``` \ No newline at end of file diff --git a/libarchive/mcp_old/rhai/logic/prompts/main.md b/libarchive/mcp_old/rhai/logic/prompts/main.md deleted file mode 100644 index b5b73092..00000000 --- a/libarchive/mcp_old/rhai/logic/prompts/main.md +++ /dev/null @@ -1,101 +0,0 @@ -You are a Rust developer tasked with creating Rhai wrappers for Rust functions. Please review the following best practices for Rhai wrappers and then create the necessary files. -@{guides} -@{vector_vs_array} -@{example_rhai} -@{wrapper_md} - -## Common Errors to Avoid -@{errors_md} -@{rhai_integration_fixes} -@{rhai_syntax_guide} - -## Your Task - -Please create a wrapper.rs file that implements Rhai wrappers for the provided Rust code, and an example.rhai script that demonstrates how to use these wrappers: - -## Rust Code to Wrap - -```rust -@{source_code} -``` - -IMPORTANT NOTES: -1. For Rhai imports, use: `use rhai::{Engine, EvalAltResult, plugin::*, Dynamic, Map, Array};` - only import what you actually use -2. The following dependencies are available in Cargo.toml: - - rhai = "1.21.0" - - serde = { version = "1.0", features = ["derive"] } - - serde_json = "1.0" - - sal = { path = "../../../" } - -3. For the wrapper: `use sal::@{name};` this way you can access the module functions and objects with @{name}:: - -4. The generic_wrapper.rs file will be hardcoded into the package, you can use code from there. - -```rust -@{generic_wrapper_rs} -``` - -5. IMPORTANT: Prefer strongly typed return values over Dynamic types whenever possible. Only use Dynamic when absolutely necessary. - - For example, return `Result>` instead of `Dynamic` when a function returns a string - - Use `Result>` instead of `Dynamic` when a function returns a boolean - - Use `Result, Box>` instead of `Dynamic` when a function returns a list of strings - -6. Your code should include public functions that can be called from Rhai scripts - -7. Make sure to implement all necessary helper functions for type conversion - -8. DO NOT use the #[rhai_fn] attribute - functions will be registered directly in the engine - -9. Make sure to handle string type consistency - use String::from() for string literals when returning in match arms with format!() strings - -10. When returning path references, convert them to owned strings (e.g., path().to_string()) - -11. For error handling, use proper Result types with Box for the error type: - ```rust - // INCORRECT: - pub fn some_function(arg: &str) -> Dynamic { - match some_operation(arg) { - Ok(result) => Dynamic::from(result), - Err(err) => Dynamic::from(format!("Error: {}", err)) - } - } - - // CORRECT: - pub fn some_function(arg: &str) -> Result> { - some_operation(arg).map_err(|err| { - Box::new(EvalAltResult::ErrorRuntime( - format!("Error: {}", err).into(), - rhai::Position::NONE - )) - }) - } - ``` - -12. IMPORTANT: Format your response with the code between triple backticks as follows: - -```rust -// wrapper.rs -// Your wrapper implementation here -``` - -```rust -// engine.rs -// Your engine.rs implementation here -``` - -```rhai -// example.rhai -// Your example Rhai script here -``` - -13. The example.rhai script should demonstrate the use of all the wrapper functions you create - -14. The engine.rs file should contain a register_module function that registers all the wrapper functions and types with the Rhai engine, and a create function. For example: - -@{engine} - -MOST IMPORTANT: -import package being wrapped as `use sal::` -your engine create function is called `create_rhai_engine` - -``` diff --git a/libarchive/mcp_old/rhai/logic/prompts/wrapper.md b/libarchive/mcp_old/rhai/logic/prompts/wrapper.md deleted file mode 100644 index 6bda928b..00000000 --- a/libarchive/mcp_old/rhai/logic/prompts/wrapper.md +++ /dev/null @@ -1,473 +0,0 @@ - -# Wrapper - -Here is an example of a well-implemented Rhai wrapper for the Git module: - -## Example wrapper - -```rust -// wrapper.rs -//! Rhai wrappers for Nerdctl module functions -//! -//! This module provides Rhai wrappers for the functions in the Nerdctl module. - -use rhai::{Engine, EvalAltResult, Array, Dynamic, Map}; -use crate::virt::nerdctl::{self, NerdctlError, Image, Container}; -use crate::process::CommandResult; - -// Helper functions for error conversion with improved context -fn nerdctl_error_to_rhai_error(result: Result) -> Result> { - result.map_err(|e| { - // Create a more detailed error message based on the error type - let error_message = match &e { - NerdctlError::CommandExecutionFailed(io_err) => { - format!("Failed to execute nerdctl command: {}. This may indicate nerdctl is not installed or not in PATH.", io_err) - }, - NerdctlError::CommandFailed(msg) => { - format!("Nerdctl command failed: {}. Check container status and logs for more details.", msg) - }, - NerdctlError::JsonParseError(msg) => { - format!("Failed to parse nerdctl JSON output: {}. This may indicate an incompatible nerdctl version.", msg) - }, - NerdctlError::ConversionError(msg) => { - format!("Data conversion error: {}. This may indicate unexpected output format from nerdctl.", msg) - }, - NerdctlError::Other(msg) => { - format!("Nerdctl error: {}. This is an unexpected error.", msg) - }, - }; - - Box::new(EvalAltResult::ErrorRuntime( - error_message.into(), - rhai::Position::NONE - )) - }) -} - -// -// Container Builder Pattern Implementation -// - -/// Create a new Container -pub fn container_new(name: &str) -> Result> { - nerdctl_error_to_rhai_error(Container::new(name)) -} - -/// Create a Container from an image -pub fn container_from_image(name: &str, image: &str) -> Result> { - nerdctl_error_to_rhai_error(Container::from_image(name, image)) -} - -/// Reset the container configuration to defaults while keeping the name and image -pub fn container_reset(container: Container) -> Container { - container.reset() -} - -/// Add a port mapping to a Container -pub fn container_with_port(container: Container, port: &str) -> Container { - container.with_port(port) -} - -/// Add a volume mount to a Container -pub fn container_with_volume(container: Container, volume: &str) -> Container { - container.with_volume(volume) -} - -/// Add an environment variable to a Container -pub fn container_with_env(container: Container, key: &str, value: &str) -> Container { - container.with_env(key, value) -} - -/// Set the network for a Container -pub fn container_with_network(container: Container, network: &str) -> Container { - container.with_network(network) -} - -/// Add a network alias to a Container -pub fn container_with_network_alias(container: Container, alias: &str) -> Container { - container.with_network_alias(alias) -} - -/// Set CPU limit for a Container -pub fn container_with_cpu_limit(container: Container, cpus: &str) -> Container { - container.with_cpu_limit(cpus) -} - -/// Set memory limit for a Container -pub fn container_with_memory_limit(container: Container, memory: &str) -> Container { - container.with_memory_limit(memory) -} - -/// Set restart policy for a Container -pub fn container_with_restart_policy(container: Container, policy: &str) -> Container { - container.with_restart_policy(policy) -} - -/// Set health check for a Container -pub fn container_with_health_check(container: Container, cmd: &str) -> Container { - container.with_health_check(cmd) -} - -/// Add multiple port mappings to a Container -pub fn container_with_ports(mut container: Container, ports: Array) -> Container { - for port in ports.iter() { - if port.is_string() { - let port_str = port.clone().cast::(); - container = container.with_port(&port_str); - } - } - container -} - -/// Add multiple volume mounts to a Container -pub fn container_with_volumes(mut container: Container, volumes: Array) -> Container { - for volume in volumes.iter() { - if volume.is_string() { - let volume_str = volume.clone().cast::(); - container = container.with_volume(&volume_str); - } - } - container -} - -/// Add multiple environment variables to a Container -pub fn container_with_envs(mut container: Container, env_map: Map) -> Container { - for (key, value) in env_map.iter() { - if value.is_string() { - let value_str = value.clone().cast::(); - container = container.with_env(&key, &value_str); - } - } - container -} - -/// Add multiple network aliases to a Container -pub fn container_with_network_aliases(mut container: Container, aliases: Array) -> Container { - for alias in aliases.iter() { - if alias.is_string() { - let alias_str = alias.clone().cast::(); - container = container.with_network_alias(&alias_str); - } - } - container -} - -/// Set memory swap limit for a Container -pub fn container_with_memory_swap_limit(container: Container, memory_swap: &str) -> Container { - container.with_memory_swap_limit(memory_swap) -} - -/// Set CPU shares for a Container -pub fn container_with_cpu_shares(container: Container, shares: &str) -> Container { - container.with_cpu_shares(shares) -} - -/// Set health check with options for a Container -pub fn container_with_health_check_options( - container: Container, - cmd: &str, - interval: Option<&str>, - timeout: Option<&str>, - retries: Option, - start_period: Option<&str> -) -> Container { - // Convert i64 to u32 for retries - let retries_u32 = retries.map(|r| r as u32); - container.with_health_check_options(cmd, interval, timeout, retries_u32, start_period) -} - -/// Set snapshotter for a Container -pub fn container_with_snapshotter(container: Container, snapshotter: &str) -> Container { - container.with_snapshotter(snapshotter) -} - -/// Set detach mode for a Container -pub fn container_with_detach(container: Container, detach: bool) -> Container { - container.with_detach(detach) -} - -/// Build and run the Container -/// -/// This function builds and runs the container using the configured options. -/// It provides detailed error information if the build fails. -pub fn container_build(container: Container) -> Result> { - // Get container details for better error reporting - let container_name = container.name.clone(); - let image = container.image.clone().unwrap_or_else(|| "none".to_string()); - let ports = container.ports.clone(); - let volumes = container.volumes.clone(); - let env_vars = container.env_vars.clone(); - - // Try to build the container - let build_result = container.build(); - - // Handle the result with improved error context - match build_result { - Ok(built_container) => { - // Container built successfully - Ok(built_container) - }, - Err(err) => { - // Add more context to the error - let enhanced_error = match err { - NerdctlError::CommandFailed(msg) => { - // Provide more detailed error information - let mut enhanced_msg = format!("Failed to build container '{}' from image '{}': {}", - container_name, image, msg); - - // Add information about configured options that might be relevant - if !ports.is_empty() { - enhanced_msg.push_str(&format!("\nConfigured ports: {:?}", ports)); - } - - if !volumes.is_empty() { - enhanced_msg.push_str(&format!("\nConfigured volumes: {:?}", volumes)); - } - - if !env_vars.is_empty() { - enhanced_msg.push_str(&format!("\nConfigured environment variables: {:?}", env_vars)); - } - - // Add suggestions for common issues - if msg.contains("not found") || msg.contains("no such image") { - enhanced_msg.push_str("\nSuggestion: The specified image may not exist or may not be pulled yet. Try pulling the image first with nerdctl_image_pull()."); - } else if msg.contains("port is already allocated") { - enhanced_msg.push_str("\nSuggestion: One of the specified ports is already in use. Try using a different port or stopping the container using that port."); - } else if msg.contains("permission denied") { - enhanced_msg.push_str("\nSuggestion: Permission issues detected. Check if you have the necessary permissions to create containers or access the specified volumes."); - } - - NerdctlError::CommandFailed(enhanced_msg) - }, - _ => err - }; - - nerdctl_error_to_rhai_error(Err(enhanced_error)) - } - } -} - -/// Start the Container and verify it's running -/// -/// This function starts the container and verifies that it's actually running. -/// It returns detailed error information if the container fails to start or -/// if it starts but stops immediately. -pub fn container_start(container: &mut Container) -> Result> { - // Get container details for better error reporting - let container_name = container.name.clone(); - let container_id = container.container_id.clone().unwrap_or_else(|| "unknown".to_string()); - - // Try to start the container - let start_result = container.start(); - - // Handle the result with improved error context - match start_result { - Ok(result) => { - // Container started successfully - Ok(result) - }, - Err(err) => { - // Add more context to the error - let enhanced_error = match err { - NerdctlError::CommandFailed(msg) => { - // Check if this is a "container already running" error, which is not really an error - if msg.contains("already running") { - return Ok(CommandResult { - stdout: format!("Container {} is already running", container_name), - stderr: "".to_string(), - success: true, - code: 0, - }); - } - - // Try to get more information about why the container might have failed to start - let mut enhanced_msg = format!("Failed to start container '{}' (ID: {}): {}", - container_name, container_id, msg); - - // Try to check if the image exists - if let Some(image) = &container.image { - enhanced_msg.push_str(&format!("\nContainer was using image: {}", image)); - } - - NerdctlError::CommandFailed(enhanced_msg) - }, - _ => err - }; - - nerdctl_error_to_rhai_error(Err(enhanced_error)) - } - } -} - -/// Stop the Container -pub fn container_stop(container: &mut Container) -> Result> { - nerdctl_error_to_rhai_error(container.stop()) -} - -/// Remove the Container -pub fn container_remove(container: &mut Container) -> Result> { - nerdctl_error_to_rhai_error(container.remove()) -} - -/// Execute a command in the Container -pub fn container_exec(container: &mut Container, command: &str) -> Result> { - nerdctl_error_to_rhai_error(container.exec(command)) -} - -/// Get container logs -pub fn container_logs(container: &mut Container) -> Result> { - // Get container details for better error reporting - let container_name = container.name.clone(); - let container_id = container.container_id.clone().unwrap_or_else(|| "unknown".to_string()); - - // Use the nerdctl::logs function - let logs_result = nerdctl::logs(&container_id); - - match logs_result { - Ok(result) => { - Ok(result) - }, - Err(err) => { - // Add more context to the error - let enhanced_error = NerdctlError::CommandFailed( - format!("Failed to get logs for container '{}' (ID: {}): {}", - container_name, container_id, err) - ); - - nerdctl_error_to_rhai_error(Err(enhanced_error)) - } - } -} - -/// Copy files between the Container and local filesystem -pub fn container_copy(container: &mut Container, source: &str, dest: &str) -> Result> { - nerdctl_error_to_rhai_error(container.copy(source, dest)) -} - -/// Create a new Map with default run options -pub fn new_run_options() -> Map { - let mut map = Map::new(); - map.insert("name".into(), Dynamic::UNIT); - map.insert("detach".into(), Dynamic::from(true)); - map.insert("ports".into(), Dynamic::from(Array::new())); - map.insert("snapshotter".into(), Dynamic::from("native")); - map -} - -// -// Container Function Wrappers -// - -/// Wrapper for nerdctl::run -/// -/// Run a container from an image. -pub fn nerdctl_run(image: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::run(image, None, true, None, None)) -} - -/// Run a container with a name -pub fn nerdctl_run_with_name(image: &str, name: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::run(image, Some(name), true, None, None)) -} - -/// Run a container with a port mapping -pub fn nerdctl_run_with_port(image: &str, name: &str, port: &str) -> Result> { - let ports = vec![port]; - nerdctl_error_to_rhai_error(nerdctl::run(image, Some(name), true, Some(&ports), None)) -} - -/// Wrapper for nerdctl::exec -/// -/// Execute a command in a container. -pub fn nerdctl_exec(container: &str, command: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::exec(container, command)) -} - -/// Wrapper for nerdctl::copy -/// -/// Copy files between container and local filesystem. -pub fn nerdctl_copy(source: &str, dest: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::copy(source, dest)) -} - -/// Wrapper for nerdctl::stop -/// -/// Stop a container. -pub fn nerdctl_stop(container: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::stop(container)) -} - -/// Wrapper for nerdctl::remove -/// -/// Remove a container. -pub fn nerdctl_remove(container: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::remove(container)) -} - -/// Wrapper for nerdctl::list -/// -/// List containers. -pub fn nerdctl_list(all: bool) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::list(all)) -} - -/// Wrapper for nerdctl::logs -/// -/// Get container logs. -pub fn nerdctl_logs(container: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::logs(container)) -} - -// -// Image Function Wrappers -// - -/// Wrapper for nerdctl::images -/// -/// List images in local storage. -pub fn nerdctl_images() -> Result> { - nerdctl_error_to_rhai_error(nerdctl::images()) -} - -/// Wrapper for nerdctl::image_remove -/// -/// Remove one or more images. -pub fn nerdctl_image_remove(image: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_remove(image)) -} - -/// Wrapper for nerdctl::image_push -/// -/// Push an image to a registry. -pub fn nerdctl_image_push(image: &str, destination: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_push(image, destination)) -} - -/// Wrapper for nerdctl::image_tag -/// -/// Add an additional name to a local image. -pub fn nerdctl_image_tag(image: &str, new_name: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_tag(image, new_name)) -} - -/// Wrapper for nerdctl::image_pull -/// -/// Pull an image from a registry. -pub fn nerdctl_image_pull(image: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_pull(image)) -} - -/// Wrapper for nerdctl::image_commit -/// -/// Commit a container to an image. -pub fn nerdctl_image_commit(container: &str, image_name: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_commit(container, image_name)) -} - -/// Wrapper for nerdctl::image_build -/// -/// Build an image using a Dockerfile. -pub fn nerdctl_image_build(tag: &str, context_path: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_build(tag, context_path)) -} -``` \ No newline at end of file diff --git a/libarchive/mcp_old/rhai/logic/templates/cargo.toml b/libarchive/mcp_old/rhai/logic/templates/cargo.toml deleted file mode 100644 index 1c665cb5..00000000 --- a/libarchive/mcp_old/rhai/logic/templates/cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "@{name}_rhai" -version = "0.1.0" -edition = "2021" - -[dependencies] -rhai = "1.21.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -sal = { path = "../../../" } \ No newline at end of file diff --git a/libarchive/mcp_old/rhai/logic/templates/engine.rs b/libarchive/mcp_old/rhai/logic/templates/engine.rs deleted file mode 100644 index 8294f711..00000000 --- a/libarchive/mcp_old/rhai/logic/templates/engine.rs +++ /dev/null @@ -1,12 +0,0 @@ -use rhai::{Engine, EvalAltResult, Map, Dynamic}; -use crate::wrapper; - -pub fn create_rhai_engine() -> Engine { - let mut engine = Engine::new(); - - @for function in functions - engine.register_fn("@{function}", wrapper::@{function}); - @end - - engine -} \ No newline at end of file diff --git a/libarchive/mcp_old/rhai/logic/templates/example.rs b/libarchive/mcp_old/rhai/logic/templates/example.rs deleted file mode 100644 index e94b8032..00000000 --- a/libarchive/mcp_old/rhai/logic/templates/example.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::{fs, path::Path}; -use @{name}_rhai::create_rhai_engine; - -fn main() -> Result<(), Box> { - println!("=== Rhai Wrapper Example ==="); - - // Create a Rhai engine with functionality - let mut engine = create_rhai_engine(); - println!("Successfully created Rhai engine"); - - // Get the path to the example.rhai script - let script_path = get_script_path()?; - println!("Loading script from: {}", script_path.display()); - - // Load the script content - let script = fs::read_to_string(&script_path) - .map_err(|e| format!("Failed to read script file: {}", e))?; - - // Run the script - println!("\n=== Running Rhai script ==="); - let result = engine.eval::(&script) - .map_err(|e| format!("Script execution error: {}", e))?; - - println!("\nScript returned: {}", result); - println!("\nExample completed successfully!"); - Ok(()) -} - -fn get_script_path() -> Result { - // When running with cargo run --example, the script will be in the examples directory - let script_path = Path::new(env!("CARGO_MANIFEST_DIR")) - .join("examples") - .join("example.rhai"); - - if script_path.exists() { - Ok(script_path) - } else { - Err(format!("Could not find example.rhai script at {}", script_path.display())) - } -} diff --git a/libarchive/mcp_old/rhai/logic/templates/functions.rs b/libarchive/mcp_old/rhai/logic/templates/functions.rs deleted file mode 100644 index 96ec68f9..00000000 --- a/libarchive/mcp_old/rhai/logic/templates/functions.rs +++ /dev/null @@ -1,510 +0,0 @@ -// File: /root/code/git.threefold.info/herocode/sal/src/virt/nerdctl/container_builder.rs - -use std::collections::HashMap; -use crate::virt::nerdctl::{execute_nerdctl_command, NerdctlError}; -use super::container_types::{Container, HealthCheck}; -use super::health_check_script::prepare_health_check_command; - -impl Container { - /// Reset the container configuration to defaults while keeping the name and image - /// If the container exists, it will be stopped and removed. - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn reset(self) -> Self { - let name = self.name; - let image = self.image.clone(); - - // If container exists, stop and remove it - if let Some(container_id) = &self.container_id { - println!("Container exists. Stopping and removing container '{}'...", name); - - // Try to stop the container - let _ = execute_nerdctl_command(&["stop", container_id]); - - // Try to remove the container - let _ = execute_nerdctl_command(&["rm", container_id]); - } - - // Create a new container with just the name and image, but no container_id - Self { - name, - container_id: None, // Reset container_id to None since we removed the container - image, - config: std::collections::HashMap::new(), - ports: Vec::new(), - volumes: Vec::new(), - env_vars: std::collections::HashMap::new(), - network: None, - network_aliases: Vec::new(), - cpu_limit: None, - memory_limit: None, - memory_swap_limit: None, - cpu_shares: None, - restart_policy: None, - health_check: None, - detach: false, - snapshotter: None, - } - } - - /// Add a port mapping - /// - /// # Arguments - /// - /// * `port` - Port mapping (e.g., "8080:80") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_port(mut self, port: &str) -> Self { - self.ports.push(port.to_string()); - self - } - - /// Add multiple port mappings - /// - /// # Arguments - /// - /// * `ports` - Array of port mappings (e.g., ["8080:80", "8443:443"]) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_ports(mut self, ports: &[&str]) -> Self { - for port in ports { - self.ports.push(port.to_string()); - } - self - } - - /// Add a volume mount - /// - /// # Arguments - /// - /// * `volume` - Volume mount (e.g., "/host/path:/container/path") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_volume(mut self, volume: &str) -> Self { - self.volumes.push(volume.to_string()); - self - } - - /// Add multiple volume mounts - /// - /// # Arguments - /// - /// * `volumes` - Array of volume mounts (e.g., ["/host/path1:/container/path1", "/host/path2:/container/path2"]) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_volumes(mut self, volumes: &[&str]) -> Self { - for volume in volumes { - self.volumes.push(volume.to_string()); - } - self - } - - /// Add an environment variable - /// - /// # Arguments - /// - /// * `key` - Environment variable name - /// * `value` - Environment variable value - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_env(mut self, key: &str, value: &str) -> Self { - self.env_vars.insert(key.to_string(), value.to_string()); - self - } - - /// Add multiple environment variables - /// - /// # Arguments - /// - /// * `env_map` - Map of environment variable names to values - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_envs(mut self, env_map: &HashMap<&str, &str>) -> Self { - for (key, value) in env_map { - self.env_vars.insert(key.to_string(), value.to_string()); - } - self - } - - /// Set the network for the container - /// - /// # Arguments - /// - /// * `network` - Network name - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_network(mut self, network: &str) -> Self { - self.network = Some(network.to_string()); - self - } - - /// Add a network alias for the container - /// - /// # Arguments - /// - /// * `alias` - Network alias - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_network_alias(mut self, alias: &str) -> Self { - self.network_aliases.push(alias.to_string()); - self - } - - /// Add multiple network aliases for the container - /// - /// # Arguments - /// - /// * `aliases` - Array of network aliases - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_network_aliases(mut self, aliases: &[&str]) -> Self { - for alias in aliases { - self.network_aliases.push(alias.to_string()); - } - self - } - - /// Set CPU limit for the container - /// - /// # Arguments - /// - /// * `cpus` - CPU limit (e.g., "0.5" for half a CPU, "2" for 2 CPUs) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_cpu_limit(mut self, cpus: &str) -> Self { - self.cpu_limit = Some(cpus.to_string()); - self - } - - /// Set memory limit for the container - /// - /// # Arguments - /// - /// * `memory` - Memory limit (e.g., "512m" for 512MB, "1g" for 1GB) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_memory_limit(mut self, memory: &str) -> Self { - self.memory_limit = Some(memory.to_string()); - self - } - - /// Set memory swap limit for the container - /// - /// # Arguments - /// - /// * `memory_swap` - Memory swap limit (e.g., "1g" for 1GB) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_memory_swap_limit(mut self, memory_swap: &str) -> Self { - self.memory_swap_limit = Some(memory_swap.to_string()); - self - } - - /// Set CPU shares for the container (relative weight) - /// - /// # Arguments - /// - /// * `shares` - CPU shares (e.g., "1024" for default, "512" for half) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_cpu_shares(mut self, shares: &str) -> Self { - self.cpu_shares = Some(shares.to_string()); - self - } - - /// Set restart policy for the container - /// - /// # Arguments - /// - /// * `policy` - Restart policy (e.g., "no", "always", "on-failure", "unless-stopped") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_restart_policy(mut self, policy: &str) -> Self { - self.restart_policy = Some(policy.to_string()); - self - } - - /// Set a simple health check for the container - /// - /// # Arguments - /// - /// * `cmd` - Command to run for health check (e.g., "curl -f http://localhost/ || exit 1") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_health_check(mut self, cmd: &str) -> Self { - // Use the health check script module to prepare the command - let prepared_cmd = prepare_health_check_command(cmd, &self.name); - - self.health_check = Some(HealthCheck { - cmd: prepared_cmd, - interval: None, - timeout: None, - retries: None, - start_period: None, - }); - self - } - - /// Set a health check with custom options for the container - /// - /// # Arguments - /// - /// * `cmd` - Command to run for health check - /// * `interval` - Optional time between running the check (e.g., "30s", "1m") - /// * `timeout` - Optional maximum time to wait for a check to complete (e.g., "30s", "1m") - /// * `retries` - Optional number of consecutive failures needed to consider unhealthy - /// * `start_period` - Optional start period for the container to initialize before counting retries (e.g., "30s", "1m") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_health_check_options( - mut self, - cmd: &str, - interval: Option<&str>, - timeout: Option<&str>, - retries: Option, - start_period: Option<&str>, - ) -> Self { - // Use the health check script module to prepare the command - let prepared_cmd = prepare_health_check_command(cmd, &self.name); - - let mut health_check = HealthCheck { - cmd: prepared_cmd, - interval: None, - timeout: None, - retries: None, - start_period: None, - }; - - if let Some(interval_value) = interval { - health_check.interval = Some(interval_value.to_string()); - } - - if let Some(timeout_value) = timeout { - health_check.timeout = Some(timeout_value.to_string()); - } - - if let Some(retries_value) = retries { - health_check.retries = Some(retries_value); - } - - if let Some(start_period_value) = start_period { - health_check.start_period = Some(start_period_value.to_string()); - } - - self.health_check = Some(health_check); - self - } - - /// Set the snapshotter - /// - /// # Arguments - /// - /// * `snapshotter` - Snapshotter to use - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_snapshotter(mut self, snapshotter: &str) -> Self { - self.snapshotter = Some(snapshotter.to_string()); - self - } - - /// Set whether to run in detached mode - /// - /// # Arguments - /// - /// * `detach` - Whether to run in detached mode - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_detach(mut self, detach: bool) -> Self { - self.detach = detach; - self - } - - /// Build the container - /// - /// # Returns - /// - /// * `Result` - Container instance or error - pub fn build(self) -> Result { - // If container already exists, return it - if self.container_id.is_some() { - return Ok(self); - } - - // If no image is specified, return an error - let image = match &self.image { - Some(img) => img, - None => return Err(NerdctlError::Other("No image specified for container creation".to_string())), - }; - - // Build the command arguments as strings - let mut args_strings = Vec::new(); - args_strings.push("run".to_string()); - - if self.detach { - args_strings.push("-d".to_string()); - } - - args_strings.push("--name".to_string()); - args_strings.push(self.name.clone()); - - // Add port mappings - for port in &self.ports { - args_strings.push("-p".to_string()); - args_strings.push(port.clone()); - } - - // Add volume mounts - for volume in &self.volumes { - args_strings.push("-v".to_string()); - args_strings.push(volume.clone()); - } - - // Add environment variables - for (key, value) in &self.env_vars { - args_strings.push("-e".to_string()); - args_strings.push(format!("{}={}", key, value)); - } - - // Add network configuration - if let Some(network) = &self.network { - args_strings.push("--network".to_string()); - args_strings.push(network.clone()); - } - - // Add network aliases - for alias in &self.network_aliases { - args_strings.push("--network-alias".to_string()); - args_strings.push(alias.clone()); - } - - // Add resource limits - if let Some(cpu_limit) = &self.cpu_limit { - args_strings.push("--cpus".to_string()); - args_strings.push(cpu_limit.clone()); - } - - if let Some(memory_limit) = &self.memory_limit { - args_strings.push("--memory".to_string()); - args_strings.push(memory_limit.clone()); - } - - if let Some(memory_swap_limit) = &self.memory_swap_limit { - args_strings.push("--memory-swap".to_string()); - args_strings.push(memory_swap_limit.clone()); - } - - if let Some(cpu_shares) = &self.cpu_shares { - args_strings.push("--cpu-shares".to_string()); - args_strings.push(cpu_shares.clone()); - } - - // Add restart policy - if let Some(restart_policy) = &self.restart_policy { - args_strings.push("--restart".to_string()); - args_strings.push(restart_policy.clone()); - } - - // Add health check - if let Some(health_check) = &self.health_check { - args_strings.push("--health-cmd".to_string()); - args_strings.push(health_check.cmd.clone()); - - if let Some(interval) = &health_check.interval { - args_strings.push("--health-interval".to_string()); - args_strings.push(interval.clone()); - } - - if let Some(timeout) = &health_check.timeout { - args_strings.push("--health-timeout".to_string()); - args_strings.push(timeout.clone()); - } - - if let Some(retries) = &health_check.retries { - args_strings.push("--health-retries".to_string()); - args_strings.push(retries.to_string()); - } - - if let Some(start_period) = &health_check.start_period { - args_strings.push("--health-start-period".to_string()); - args_strings.push(start_period.clone()); - } - } - - if let Some(snapshotter_value) = &self.snapshotter { - args_strings.push("--snapshotter".to_string()); - args_strings.push(snapshotter_value.clone()); - } - - // Add flags to avoid BPF issues - args_strings.push("--cgroup-manager=cgroupfs".to_string()); - - args_strings.push(image.clone()); - - // Convert to string slices for the command - let args: Vec<&str> = args_strings.iter().map(|s| s.as_str()).collect(); - - // Execute the command - let result = execute_nerdctl_command(&args)?; - - // Get the container ID from the output - let container_id = result.stdout.trim().to_string(); - - Ok(Self { - name: self.name, - container_id: Some(container_id), - image: self.image, - config: self.config, - ports: self.ports, - volumes: self.volumes, - env_vars: self.env_vars, - network: self.network, - network_aliases: self.network_aliases, - cpu_limit: self.cpu_limit, - memory_limit: self.memory_limit, - memory_swap_limit: self.memory_swap_limit, - cpu_shares: self.cpu_shares, - restart_policy: self.restart_policy, - health_check: self.health_check, - detach: self.detach, - snapshotter: self.snapshotter, - }) - } -} \ No newline at end of file diff --git a/libarchive/mcp_old/rhai/logic/templates/generic_wrapper.rs b/libarchive/mcp_old/rhai/logic/templates/generic_wrapper.rs deleted file mode 100644 index 8a189453..00000000 --- a/libarchive/mcp_old/rhai/logic/templates/generic_wrapper.rs +++ /dev/null @@ -1,132 +0,0 @@ -use std::collections::HashMap; -use rhai::{Dynamic, Map, Array}; - -/// Local wrapper trait for sal::rhai::ToRhai to avoid orphan rule violations -pub trait ToRhai { - /// Convert to a Rhai Dynamic value - fn to_rhai(&self) -> Dynamic; -} - -// Implementation of ToRhai for Dynamic -impl ToRhai for Dynamic { - fn to_rhai(&self) -> Dynamic { - self.clone() - } -} - -/// Generic trait for wrapping Rust functions to be used with Rhai -pub trait RhaiWrapper { - /// Wrap a function that takes ownership of self - fn wrap_consuming(self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(Self) -> R, - R: ToRhai; - - /// Wrap a function that takes a mutable reference to self - fn wrap_mut(&mut self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(&mut Self) -> R, - R: ToRhai; - - /// Wrap a function that takes an immutable reference to self - fn wrap(&self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(&Self) -> R, - R: ToRhai; -} - -/// Implementation of RhaiWrapper for any type -impl RhaiWrapper for T { - fn wrap_consuming(self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(Self) -> R, - R: ToRhai, - { - let result = f(self); - result.to_rhai() - } - - fn wrap_mut(&mut self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(&mut Self) -> R, - R: ToRhai, - { - let result = f(self); - result.to_rhai() - } - - fn wrap(&self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(&Self) -> R, - R: ToRhai, - { - let result = f(self); - result.to_rhai() - } -} - -/// Convert a Rhai Map to a Rust HashMap -pub fn map_to_hashmap(map: &Map) -> HashMap { - let mut result = HashMap::new(); - for (key, value) in map.iter() { - let k = key.clone().to_string(); - let v = value.clone().to_string(); - if !k.is_empty() && !v.is_empty() { - result.insert(k, v); - } - } - result -} - -/// Convert a HashMap to a Rhai Map -pub fn hashmap_to_map(map: &HashMap) -> Map { - let mut result = Map::new(); - for (key, value) in map.iter() { - result.insert(key.clone().into(), Dynamic::from(value.clone())); - } - result -} - -/// Convert a Rhai Array to a Vec of strings -pub fn array_to_vec_string(array: &Array) -> Vec { - array.iter() - .filter_map(|item| { - let s = item.clone().to_string(); - if !s.is_empty() { Some(s) } else { None } - }) - .collect() -} - -/// Helper function to convert Dynamic to Option -pub fn dynamic_to_string_option(value: &Dynamic) -> Option { - if value.is_string() { - Some(value.clone().to_string()) - } else { - None - } -} - -/// Helper function to convert Dynamic to Option -pub fn dynamic_to_u32_option(value: &Dynamic) -> Option { - if value.is_int() { - Some(value.as_int().unwrap() as u32) - } else { - None - } -} - -/// Helper function to convert Dynamic to Option<&str> with lifetime management -pub fn dynamic_to_str_option<'a>(value: &Dynamic, storage: &'a mut String) -> Option<&'a str> { - if value.is_string() { - *storage = value.clone().to_string(); - Some(storage.as_str()) - } else { - None - } -} \ No newline at end of file diff --git a/libarchive/mcp_old/rhai/logic/templates/lib.rs b/libarchive/mcp_old/rhai/logic/templates/lib.rs deleted file mode 100644 index 10cb4178..00000000 --- a/libarchive/mcp_old/rhai/logic/templates/lib.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Re-export the utility modules -pub mod generic_wrapper; -pub mod wrapper; -pub mod engine; - -// Re-export the utility traits and functions -pub use generic_wrapper::{RhaiWrapper, map_to_hashmap, array_to_vec_string, - dynamic_to_string_option, hashmap_to_map}; -pub use engine::create_rhai_engine; - -// The create_rhai_engine function is now in the engine module \ No newline at end of file diff --git a/libarchive/mcp_old/rhai/mcp/command.v b/libarchive/mcp_old/rhai/mcp/command.v deleted file mode 100644 index 956ca1dc..00000000 --- a/libarchive/mcp_old/rhai/mcp/command.v +++ /dev/null @@ -1,22 +0,0 @@ -module mcp - -import cli - -pub const command = cli.Command{ - sort_flags: true - name: 'rhai' - // execute: cmd_mcpgen - description: 'rhai command' - commands: [ - cli.Command{ - name: 'start' - execute: cmd_start - description: 'start the Rhai server' - }, - ] -} - -fn cmd_start(cmd cli.Command) ! { - mut server := new_mcp_server()! - server.start()! -} diff --git a/libarchive/mcp_old/rhai/mcp/mcp.v b/libarchive/mcp_old/rhai/mcp/mcp.v deleted file mode 100644 index 0cf2f75a..00000000 --- a/libarchive/mcp_old/rhai/mcp/mcp.v +++ /dev/null @@ -1,33 +0,0 @@ -module mcp - -import incubaid.herolib.mcp -import incubaid.herolib.schemas.jsonrpc -import log - -pub fn new_mcp_server() !&Server { - log.info('Creating new Developer MCP server') - - // Initialize the server with the empty handlers map - mut server := mcp.new_server(MemoryBackend{ - tools: { - 'generate_rhai_wrapper': generate_rhai_wrapper_spec - } - tool_handlers: { - 'generate_rhai_wrapper': generate_rhai_wrapper_handler - } - prompts: { - 'rhai_wrapper': rhai_wrapper_prompt_spec - } - prompt_handlers: { - 'rhai_wrapper': rhai_wrapper_prompt_handler - } - }, ServerParams{ - config: ServerConfiguration{ - server_info: ServerInfo{ - name: 'rhai' - version: '1.0.0' - } - } - })! - return server -} diff --git a/libarchive/mcp_old/rhai/mcp/prompts.v b/libarchive/mcp_old/rhai/mcp/prompts.v deleted file mode 100644 index 9eb59725..00000000 --- a/libarchive/mcp_old/rhai/mcp/prompts.v +++ /dev/null @@ -1,43 +0,0 @@ -module mcp - -import incubaid.herolib.mcp -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.mcp.rhai.logic -import incubaid.herolib.schemas.jsonschema -import incubaid.herolib.lang.rust -import x.json2 as json - -// Tool definition for the create_rhai_wrapper function -const rhai_wrapper_prompt_spec = Prompt{ - name: 'rhai_wrapper' - description: 'provides a prompt for creating Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file' - arguments: [ - PromptArgument{ - name: 'source_path' - description: 'Path to the source directory' - required: true - }, - ] -} - -// Tool handler for the create_rhai_wrapper function -pub fn rhai_wrapper_prompt_handler(arguments []string) ![]PromptMessage { - source_path := arguments[0] - - // Read and combine all Rust files in the source directory - source_code := rust.read_source_code(source_path)! - - // Extract the module name from the directory path (last component) - name := rust.extract_module_name_from_path(source_path) - - result := logic.rhai_wrapper_generation_prompt(name, source_code)! - return [ - PromptMessage{ - role: 'assistant' - content: PromptContent{ - typ: 'text' - text: result - } - }, - ] -} diff --git a/libarchive/mcp_old/rhai/mcp/specifications.v b/libarchive/mcp_old/rhai/mcp/specifications.v deleted file mode 100644 index 2e528793..00000000 --- a/libarchive/mcp_old/rhai/mcp/specifications.v +++ /dev/null @@ -1,21 +0,0 @@ -module mcp - -import incubaid.herolib.mcp -import x.json2 as json -import incubaid.herolib.schemas.jsonschema -import log - -const specs = Tool{ - name: 'rhai_interface' - description: 'Add Rhai Interface to Rust Code Files' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to a .rs file or directory containing .rs files to make rhai interface for' - }) - } - required: ['path'] - } -} diff --git a/libarchive/mcp_old/rhai/mcp/tools.v b/libarchive/mcp_old/rhai/mcp/tools.v deleted file mode 100644 index 5d7fdc90..00000000 --- a/libarchive/mcp_old/rhai/mcp/tools.v +++ /dev/null @@ -1,38 +0,0 @@ -module mcp - -import incubaid.herolib.mcp -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.mcp.rhai.logic -import incubaid.herolib.schemas.jsonschema -import x.json2 as json { Any } - -// Tool definition for the generate_rhai_wrapper function -const generate_rhai_wrapper_spec = Tool{ - name: 'generate_rhai_wrapper' - description: 'generate_rhai_wrapper receives the name of a V language function string, and the path to the module in which it exists.' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'source_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['name', 'source_path'] - } -} - -// Tool handler for the generate_rhai_wrapper function -pub fn generate_rhai_wrapper_handler(arguments map[string]Any) !ToolCallResult { - name := arguments['name'].str() - source_path := arguments['source_path'].str() - result := logic.generate_rhai_wrapper(name, source_path) or { - return mcp.error_tool_call_result(err) - } - return ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} diff --git a/libarchive/mcp_old/rhai/rhai.v b/libarchive/mcp_old/rhai/rhai.v deleted file mode 100644 index adc61b86..00000000 --- a/libarchive/mcp_old/rhai/rhai.v +++ /dev/null @@ -1 +0,0 @@ -module rhai diff --git a/libarchive/mcp_old/server.v b/libarchive/mcp_old/server.v deleted file mode 100644 index d93f2396..00000000 --- a/libarchive/mcp_old/server.v +++ /dev/null @@ -1,27 +0,0 @@ -module mcp - -import log -import incubaid.herolib.schemas.jsonrpc -import incubaid.herolib.mcp.transport - -// Server is the main MCP server struct -@[heap] -pub struct Server { - ServerConfiguration -pub mut: - client_config ClientConfiguration - handler jsonrpc.Handler - backend Backend - transport transport.Transport -} - -// start starts the MCP server using the configured transport -pub fn (mut s Server) start() ! { - // Note: Removed log.info() as it interferes with STDIO transport JSON-RPC communication - s.transport.start(&s.handler)! -} - -// send sends a response to the client using the configured transport -pub fn (mut s Server) send(response string) { - s.transport.send(response) -} diff --git a/libarchive/mcp_old/transport/http.v b/libarchive/mcp_old/transport/http.v deleted file mode 100644 index 4b165e36..00000000 --- a/libarchive/mcp_old/transport/http.v +++ /dev/null @@ -1,296 +0,0 @@ -module transport - -import veb -import veb.sse -import time -import incubaid.herolib.schemas.jsonrpc -import incubaid.herolib.ui.console - -// HttpTransport implements the Transport interface for HTTP communication. -// It provides both JSON-RPC over HTTP and REST API endpoints for MCP servers. -pub struct HttpTransport { -pub: - port int = 8080 - host string = 'localhost' - mode HttpMode = .both -mut: - handler &jsonrpc.Handler = unsafe { nil } -} - -// HttpApp is the VEB application struct that handles HTTP requests -pub struct HttpApp { -pub mut: - transport &HttpTransport = unsafe { nil } -} - -// Context represents the HTTP request context -pub struct Context { - veb.Context -} - -// new_http_transport creates a new HTTP transport instance -pub fn new_http_transport(config HttpConfig) Transport { - return &HttpTransport{ - port: config.port - host: config.host - mode: config.protocol - } -} - -// start implements the Transport interface for HTTP communication. -// It starts a VEB web server with the appropriate endpoints based on the configured mode. -pub fn (mut t HttpTransport) start(handler &jsonrpc.Handler) ! { - unsafe { - t.handler = handler - } - console.print_debug('Starting MCP server with HTTP transport on ${t.host}:${t.port}') - - mut app := &HttpApp{ - transport: t - } - - veb.run[HttpApp, Context](mut app, t.port) -} - -// send implements the Transport interface for HTTP communication. -// Note: For HTTP, responses are sent directly in the request handlers, -// so this method is not used in the same way as STDIO transport. -pub fn (mut t HttpTransport) send(response string) { - // HTTP responses are handled directly in the route handlers - // This method is kept for interface compatibility - console.print_debug('HTTP transport send called: ${response}') -} - -// JSON-RPC over HTTP endpoint -// Handles POST requests to /jsonrpc with JSON-RPC 2.0 protocol -@['/jsonrpc'; post] -pub fn (mut app HttpApp) handle_jsonrpc(mut ctx Context) veb.Result { - // Get the request body - request_body := ctx.req.data - - if request_body.len == 0 { - return ctx.request_error('Empty request body') - } - - // Parse the JSON-RPC request - request := jsonrpc.decode_request(request_body) or { - console.print_stderr('Invalid JSON-RPC request: ${err}') - return ctx.request_error('Invalid JSON-RPC request') - } - - // Process the JSON-RPC request using the existing handler - response := app.transport.handler.handle(request) or { - console.print_stderr('JSON-RPC handler error: ${err}') - return ctx.server_error('Internal server error') - } - - // Return the JSON-RPC response - ctx.set_content_type('application/json') - return ctx.text(response.encode()) -} - -// Health check endpoint -@['/health'; get] -pub fn (mut app HttpApp) health(mut ctx Context) veb.Result { - return ctx.json({ - 'status': 'ok' - 'transport': 'http' - 'timestamp': time.now().str() - }) -} - -// CORS preflight handler -@['/*'; options] -pub fn (mut app HttpApp) options(mut ctx Context) veb.Result { - ctx.set_custom_header('Access-Control-Allow-Origin', '*') or {} - ctx.set_custom_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') or {} - ctx.set_custom_header('Access-Control-Allow-Headers', 'Content-Type, Authorization') or {} - return ctx.text('') -} - -// REST API Endpoints (when mode is .rest_only or .both) - -// List all available tools -@['/api/tools'; get] -pub fn (mut app HttpApp) list_tools(mut ctx Context) veb.Result { - if app.transport.mode == .jsonrpc_only { - return ctx.not_found() - } - - // Create JSON-RPC request for tools/list - request_str := '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' - request := jsonrpc.decode_request(request_str) or { - console.print_stderr('Failed to create tools/list request: ${err}') - return ctx.server_error('Failed to create request') - } - - response := app.transport.handler.handle(request) or { - console.print_stderr('Tools list error: ${err}') - return ctx.server_error('Failed to list tools') - } - - // Parse JSON-RPC response and extract result - result := extract_jsonrpc_result(response.encode()) or { - return ctx.server_error('Invalid response format') - } - - ctx.set_custom_header('Access-Control-Allow-Origin', '*') or {} - ctx.set_content_type('application/json') - return ctx.text(result) -} - -// Call a specific tool -@['/api/tools/:tool_name/call'; post] -pub fn (mut app HttpApp) call_tool(mut ctx Context, tool_name string) veb.Result { - if app.transport.mode == .jsonrpc_only { - return ctx.not_found() - } - - // Create JSON-RPC request for tools/call by building the JSON string directly - // This avoids json2.Any conversion issues - arguments_json := ctx.req.data - - request_json := '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"${tool_name}","arguments":${arguments_json}}}' - request := jsonrpc.decode_request(request_json) or { - console.print_stderr('Failed to create tools/call request: ${err}') - return ctx.server_error('Failed to create request') - } - - response := app.transport.handler.handle(request) or { - console.print_stderr('Tool call error: ${err}') - return ctx.server_error('Tool call failed') - } - - // Parse JSON-RPC response and extract result - response_str := response.encode() - result := extract_jsonrpc_result(response_str) or { - return ctx.server_error('Invalid response format') - } - - ctx.set_custom_header('Access-Control-Allow-Origin', '*') or {} - ctx.set_content_type('application/json') - return ctx.text(result) -} - -// List all available resources -@['/api/resources'; get] -pub fn (mut app HttpApp) list_resources(mut ctx Context) veb.Result { - if app.transport.mode == .jsonrpc_only { - return ctx.not_found() - } - - // Create JSON-RPC request for resources/list - request_str := '{"jsonrpc":"2.0","id":1,"method":"resources/list","params":{}}' - request := jsonrpc.decode_request(request_str) or { - console.print_stderr('Failed to create resources/list request: ${err}') - return ctx.server_error('Failed to create request') - } - - response := app.transport.handler.handle(request) or { - console.print_stderr('Resources list error: ${err}') - return ctx.server_error('Failed to list resources') - } - - // Parse JSON-RPC response and extract result - result := extract_jsonrpc_result(response.encode()) or { - return ctx.server_error('Invalid response format') - } - - ctx.set_custom_header('Access-Control-Allow-Origin', '*') or {} - ctx.set_content_type('application/json') - return ctx.text(result) -} - -// SSE endpoint for streaming MCP communication -@['/sse'; get] -pub fn (mut app HttpApp) handle_sse(mut ctx Context) veb.Result { - // Set CORS headers - ctx.set_custom_header('Access-Control-Allow-Origin', '*') or {} - ctx.set_custom_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') or {} - ctx.set_custom_header('Access-Control-Allow-Headers', 'Content-Type, Authorization') or {} - - // Take over the connection for SSE - ctx.takeover_conn() - - // Handle SSE connection in a separate thread - spawn handle_sse_connection(mut ctx, app.transport.handler) - - return veb.no_result() -} - -// Handle SSE connection for MCP communication -fn handle_sse_connection(mut ctx Context, handler &jsonrpc.Handler) { - mut sse_conn := sse.start_connection(mut ctx.Context) - - console.print_debug('SSE connection established for MCP') - - // Keep connection alive with periodic messages - for { - // Send server capabilities periodically - capabilities_msg := '{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"logging":{},"prompts":{"listChanged":true},"resources":{"subscribe":true,"listChanged":true},"tools":{"listChanged":true}},"serverInfo":{"name":"inspector_example","version":"1.0.0"}}}' - - sse_conn.send_message( - event: 'capabilities' - data: capabilities_msg - ) or { - console.print_debug('SSE connection closed during capabilities send') - break - } - - time.sleep(2 * time.second) - - // Send tools list - tools_request_str := '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' - tools_request := jsonrpc.decode_request(tools_request_str) or { - console.print_stderr('Failed to create tools/list request: ${err}') - continue - } - tools_response := handler.handle(tools_request) or { - console.print_stderr('Failed to get tools list: ${err}') - continue - } - - sse_conn.send_message( - event: 'tools' - data: tools_response.encode() - ) or { - console.print_debug('SSE connection closed during tools send') - break - } - - time.sleep(3 * time.second) - - // Send keepalive ping - sse_conn.send_message( - event: 'ping' - data: '{"status":"alive"}' - ) or { - console.print_debug('SSE connection closed during ping') - break - } - - time.sleep(5 * time.second) - } - - sse_conn.close() -} - -// Helper function to extract result from JSON-RPC response -fn extract_jsonrpc_result(response string) !string { - // Parse the JSON-RPC response - response_obj := jsonrpc.decode_response(response) or { - return error('Failed to parse JSON-RPC response: ${err}') - } - - // Check if there's an error in the response - if error_obj := response_obj.error_ { - return error('JSON-RPC error: ${error_obj.message}') - } - - // Extract the result - if result := response_obj.result { - return result - } - - return error('No result in JSON-RPC response') -} diff --git a/libarchive/mcp_old/transport/interface.v b/libarchive/mcp_old/transport/interface.v deleted file mode 100644 index 7852944d..00000000 --- a/libarchive/mcp_old/transport/interface.v +++ /dev/null @@ -1,54 +0,0 @@ -module transport - -import incubaid.herolib.schemas.jsonrpc - -// Transport defines the interface for different MCP transport mechanisms. -// This abstraction allows MCP servers to work with multiple transport protocols -// (STDIO, HTTP, WebSocket, etc.) without changing the core MCP logic. -pub interface Transport { -mut: - // start begins listening for requests and handling them with the provided JSON-RPC handler. - // This method should block and run the transport's main loop. - // - // Parameters: - // - handler: The JSON-RPC handler that processes MCP protocol messages - // - // Returns: - // - An error if the transport fails to start or encounters a fatal error - start(handler &jsonrpc.Handler) ! - - // send transmits a response back to the client. - // The implementation depends on the transport type (stdout for STDIO, HTTP response, etc.) - // - // Parameters: - // - response: The JSON-RPC response string to send to the client - send(response string) -} - -// TransportMode defines the available transport types -pub enum TransportMode { - stdio // Standard input/output transport (current default) - http // HTTP/REST transport (new) -} - -// TransportConfig holds configuration for different transport types -pub struct TransportConfig { -pub: - mode TransportMode = .stdio - http HttpConfig -} - -// HttpConfig holds HTTP-specific configuration -pub struct HttpConfig { -pub: - port int = 8080 // Port to listen on - host string = 'localhost' // Host to bind to - protocol HttpMode = .both // Which HTTP protocols to support -} - -// HttpMode defines which HTTP protocols the server should support -pub enum HttpMode { - jsonrpc_only // Only JSON-RPC over HTTP endpoint - rest_only // Only REST API endpoints - both // Both JSON-RPC and REST endpoints -} diff --git a/libarchive/mcp_old/transport/stdio.v b/libarchive/mcp_old/transport/stdio.v deleted file mode 100644 index 54de6a97..00000000 --- a/libarchive/mcp_old/transport/stdio.v +++ /dev/null @@ -1,73 +0,0 @@ -module transport - -import time -import os -import incubaid.herolib.ui.console -import incubaid.herolib.schemas.jsonrpc - -// StdioTransport implements the Transport interface for standard input/output communication. -// This is the original MCP transport method where the server reads JSON-RPC requests from stdin -// and writes responses to stdout. This transport is used for process-to-process communication. -pub struct StdioTransport { -mut: - handler &jsonrpc.Handler = unsafe { nil } -} - -// new_stdio_transport creates a new STDIO transport instance -pub fn new_stdio_transport() Transport { - return &StdioTransport{} -} - -// start implements the Transport interface for STDIO communication. -// It reads JSON-RPC messages from stdin, processes them with the handler, -// and sends responses to stdout. -pub fn (mut t StdioTransport) start(handler &jsonrpc.Handler) ! { - unsafe { - t.handler = handler - } - // Note: In STDIO mode, we should not print any debug messages to stdout - // as it interferes with JSON-RPC communication - - for { - // Read a message from stdin - message := os.get_line() - if message == '' { - time.sleep(10000) // prevent cpu spinning - continue - } - - // Parse the JSON-RPC request - request := jsonrpc.decode_request(message) or { - // Note: Removed stderr logging as it can interfere with MCP Inspector - // Try to extract the request ID for error response - id := jsonrpc.decode_request_id(message) or { 0 } - // Create an invalid request error response - error_response := jsonrpc.new_error(id, jsonrpc.invalid_request).encode() - println(error_response) - continue - } - - // Handle the message using the JSON-RPC handler - response := t.handler.handle(request) or { - // Note: Removed stderr logging as it can interfere with MCP Inspector - - // Create an internal error response - error_response := jsonrpc.new_error(request.id, jsonrpc.internal_error).encode() - println(error_response) - continue - } - - // Send the response (notifications may return empty responses) - response_str := response.encode() - if response_str.len > 0 { - t.send(response_str) - } - } -} - -// send implements the Transport interface for STDIO communication. -// It writes the response to stdout and flushes the output buffer. -pub fn (mut t StdioTransport) send(response string) { - println(response) - flush_stdout() -} diff --git a/libarchive/mcp_old/vcode/README.md b/libarchive/mcp_old/vcode/README.md deleted file mode 100644 index 41c9bd92..00000000 --- a/libarchive/mcp_old/vcode/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# V-Do MCP Server - -An implementation of the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for V language operations. This server uses the Standard Input/Output (stdio) transport as described in the [MCP documentation](https://modelcontextprotocol.io/docs/concepts/transports). - -## Features - -The server supports the following operations: - -1. **test** - Run V tests on a file or directory -2. **run** - Execute V code from a file or directory -3. **compile** - Compile V code from a file or directory -4. **vet** - Run V vet on a file or directory - -## Usage - -### Building the Server - -```bash -v -gc none -stats -enable-globals -n -w -cg -g -cc tcc /Users/despiegk/code/github/incubaid/herolib/lib/mcp/v_do -``` - -### Using the Server - -The server communicates using the MCP protocol over stdio. To send a request, use the following format: - -``` -Content-Length: - -{"jsonrpc":"2.0","id":"","method":"","params":{"fullpath":""}} -``` - -Where: -- `` is the length of the JSON message in bytes -- `` is a unique identifier for the request -- `` is one of: `test`, `run`, `compile`, or `vet` -- `` is the absolute path to the V file or directory to process - -### Example - -Request: -``` -Content-Length: 85 - -{"jsonrpc":"2.0","id":"1","method":"test","params":{"fullpath":"/path/to/file.v"}} -``` - -Response: -``` -Content-Length: 245 - -{"jsonrpc":"2.0","id":"1","result":{"output":"Command: v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc test /path/to/file.v\nExit code: 0\nOutput:\nAll tests passed!"}} -``` - -## Methods - -### test - -Runs V tests on the specified file or directory. - -Command used: -``` -v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc test ${fullpath} -``` - -If a directory is specified, it will run tests on all `.v` files in the directory (non-recursive). - -### run - -Executes the specified V file or all V files in a directory. - -Command used: -``` -v -gc none -stats -enable-globals -n -w -cg -g -cc tcc run ${fullpath} -``` - -### compile - -Compiles the specified V file or all V files in a directory. - -Command used: -``` -cd /tmp && v -gc none -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc ${fullpath} -``` - -### vet - -Runs V vet on the specified file or directory. - -Command used: -``` -v vet -v -w ${fullpath} -``` diff --git a/libarchive/mcp_old/vcode/cmd/.gitignore b/libarchive/mcp_old/vcode/cmd/.gitignore deleted file mode 100644 index 1a94556d..00000000 --- a/libarchive/mcp_old/vcode/cmd/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -main - diff --git a/libarchive/mcp_old/vcode/cmd/compile.sh b/libarchive/mcp_old/vcode/cmd/compile.sh deleted file mode 100755 index d3ef5e84..00000000 --- a/libarchive/mcp_old/vcode/cmd/compile.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -ex - -export name="mcp_vcode" - -# Change to the directory containing this script -cd "$(dirname "$0")" - -# Compile the V program -v -n -w -gc none -cc tcc -d use_openssl -enable-globals main.v - -# Ensure the binary is executable -chmod +x main -mv main ~/hero/bin/${name} - -echo "Compilation successful. Binary '${name}' is ready." diff --git a/libarchive/mcp_old/vcode/cmd/main.v b/libarchive/mcp_old/vcode/cmd/main.v deleted file mode 100644 index 0bccead6..00000000 --- a/libarchive/mcp_old/vcode/cmd/main.v +++ /dev/null @@ -1,17 +0,0 @@ -module main - -import incubaid.herolib.mcp.vcode - -fn main() { - // Create a new MCP server - mut server := vcode.new_mcp_server() or { - eprintln('Failed to create MCP server: ${err}') - return - } - - // Start the server - server.start() or { - eprintln('Failed to start MCP server: ${err}') - return - } -} diff --git a/libarchive/mcp_old/vcode/logic/server.v b/libarchive/mcp_old/vcode/logic/server.v deleted file mode 100644 index ecaacba9..00000000 --- a/libarchive/mcp_old/vcode/logic/server.v +++ /dev/null @@ -1,33 +0,0 @@ -module vcode - -import incubaid.herolib.mcp -import incubaid.herolib.mcp.logger - -@[heap] -pub struct VCode { - v_version string = '0.1.0' -} - -pub fn new_mcp_server(v &VCode) !&mcp.Server { - logger.info('Creating new Developer MCP server') - - // Initialize the server with the empty handlers map - mut server := mcp.new_server(mcp.MemoryBackend{ - tools: { - 'get_function_from_file': get_function_from_file_tool - 'write_vfile': write_vfile_tool - } - tool_handlers: { - 'get_function_from_file': v.get_function_from_file_tool_handler - 'write_vfile': v.write_vfile_tool_handler - } - }, mcp.ServerParams{ - config: mcp.ServerConfiguration{ - server_info: mcp.ServerInfo{ - name: 'vcode' - version: '1.0.0' - } - } - })! - return server -} diff --git a/libarchive/mcp_old/vcode/logic/test_client.vsh b/libarchive/mcp_old/vcode/logic/test_client.vsh deleted file mode 100644 index 24528a82..00000000 --- a/libarchive/mcp_old/vcode/logic/test_client.vsh +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import os -import flag -import json - -// Simple test client for the V-Do MCP server -// This script sends test requests to the MCP server and displays the responses - -// struct MCPRequest { -// id string -// method string -// params map[string]string -// jsonrpc string = '2.0' -// } - -// fn send_request(method string, fullpath string) { -// // Create the request -// request := MCPRequest{ -// id: '1' -// method: method -// params: { -// 'fullpath': fullpath -// } -// } - -// // Encode to JSON -// json_str := json.encode(request) - -// // Format the message with headers -// message := 'Content-Length: ${json_str.len}\r\n\r\n${json_str}' - -// // Write to a temporary file -// os.write_file('/tmp/mcp_request.txt', message) or { -// eprintln('Failed to write request to file: $err') -// return -// } - -// // Execute the MCP server with the request -// cmd := 'cat /tmp/mcp_request.txt | v run /Users/despiegk/code/github/incubaid/herolib/lib/mcp/v_do/main.v' -// result := os.execute(cmd) - -// if result.exit_code != 0 { -// eprintln('Error executing MCP server: ${result.output}') -// return -// } - -// // Parse and display the response -// response := result.output -// println('Raw response:') -// println('-----------------------------------') -// println(response) -// println('-----------------------------------') - -// // Try to extract the JSON part -// if response.contains('{') && response.contains('}') { -// json_start := response.index_after('{', 0) -// json_end := response.last_index_of('}') -// if json_start >= 0 && json_end >= 0 && json_end > json_start { -// json_part := response[json_start-1..json_end+1] -// println('Extracted JSON:') -// println(json_part) -// } -// } -// } - -// // Parse command line arguments -// mut fp := flag.new_flag_parser(os.args) -// fp.application('test_client.vsh') -// fp.version('v0.1.0') -// fp.description('Test client for V-Do MCP server') -// fp.skip_executable() - -// method := fp.string('method', `m`, 'test', 'Method to call (test, run, compile, vet)') -// fullpath := fp.string('path', `p`, '', 'Path to the file or directory to process') -// help_requested := fp.bool('help', `h`, false, 'Show help message') - -// if help_requested { -// println(fp.usage()) -// exit(0) -// } - -// additional_args := fp.finalize() or { -// eprintln(err) -// println(fp.usage()) -// exit(1) -// } - -// if fullpath == '' { -// eprintln('Error: Path is required') -// println(fp.usage()) -// exit(1) -// } - -// // Validate method -// valid_methods := ['test', 'run', 'compile', 'vet'] -// if method !in valid_methods { -// eprintln('Error: Invalid method. Must be one of: ${valid_methods}') -// println(fp.usage()) -// exit(1) -// } - -// // Send the request -// println('Sending $method request for $fullpath...') -// send_request(method, fullpath) diff --git a/libarchive/mcp_old/vcode/logic/vlang.v b/libarchive/mcp_old/vcode/logic/vlang.v deleted file mode 100644 index fd03b92c..00000000 --- a/libarchive/mcp_old/vcode/logic/vlang.v +++ /dev/null @@ -1,284 +0,0 @@ -module vcode - -import incubaid.herolib.mcp -import incubaid.herolib.mcp.logger -import os -import log - -fn get_module_dir(mod string) string { - module_parts := mod.trim_string_left('incubaid.herolib').split('.') - return '${os.home_dir()}/code/github/incubaid/herolib/lib/${module_parts.join('/')}' -} - -// given a module path and a type name, returns the type definition of that type within that module -// for instance: get_type_from_module('lib/mcp/developer/vlang.v', 'Developer') might return struct Developer {...} -fn get_type_from_module(module_path string, type_name string) !string { - println('Looking for type ${type_name} in module ${module_path}') - v_files := list_v_files(module_path) or { - return error('Failed to list V files in ${module_path}: ${err}') - } - - for v_file in v_files { - println('Checking file: ${v_file}') - content := os.read_file(v_file) or { return error('Failed to read file ${v_file}: ${err}') } - - // Look for both regular and pub struct declarations - mut type_str := 'struct ${type_name} {' - mut i := content.index(type_str) or { -1 } - mut is_pub := false - - if i == -1 { - // Try with pub struct - type_str = 'pub struct ${type_name} {' - i = content.index(type_str) or { -1 } - is_pub = true - } - - if i == -1 { - type_import := content.split_into_lines().filter(it.contains('import') - && it.contains(type_name)) - if type_import.len > 0 { - log.debug('debugzoooo') - mod := type_import[0].trim_space().trim_string_left('import ').all_before(' ') - return get_type_from_module(get_module_dir(mod), type_name) - } - continue - } - println('Found type ${type_name} in ${v_file} at position ${i}') - - // Find the start of the struct definition including comments - mut comment_start := i - mut line_start := i - - // Find the start of the line containing the struct definition - for j := i; j >= 0; j-- { - if j == 0 || content[j - 1] == `\n` { - line_start = j - break - } - } - - // Find the start of the comment block (if any) - for j := line_start - 1; j >= 0; j-- { - if j == 0 { - comment_start = 0 - break - } - - // If we hit a blank line or a non-comment line, stop - if content[j] == `\n` { - if j > 0 && j < content.len - 1 { - // Check if the next line starts with a comment - next_line_start := j + 1 - if next_line_start < content.len && content[next_line_start] != `/` { - comment_start = j + 1 - break - } - } - } - } - - // Find the end of the struct definition - closing_i := find_closing_brace(content, i + type_str.len) or { - return error('could not find where declaration for type ${type_name} ends') - } - - // Get the full struct definition including the struct declaration line - full_struct := content.substr(line_start, closing_i + 1) - println('Found struct definition:\n${full_struct}') - - // Return the full struct definition - return full_struct - } - - return error('type ${type_name} not found in module ${module_path}') -} - -// given a module path and a function name, returns the function definition of that function within that module -// for instance: get_function_from_module('lib/mcp/developer/vlang.v', 'develop') might return fn develop(...) {...} -fn get_function_from_module(module_path string, function_name string) !string { - v_files := list_v_files(module_path) or { - return error('Failed to list V files in ${module_path}: ${err}') - } - - println('Found ${v_files.len} V files in ${module_path}') - for v_file in v_files { - println('Checking file: ${v_file}') - result := get_function_from_file(v_file, function_name) or { - println('Function not found in ${v_file}: ${err}') - continue - } - println('Found function ${function_name} in ${v_file}') - return result - } - - return error('function ${function_name} not found in module ${module_path}') -} - -fn find_closing_brace(content string, start_i int) ?int { - mut brace_count := 1 - for i := start_i; i < content.len; i++ { - if content[i] == `{` { - brace_count++ - } else if content[i] == `}` { - brace_count-- - if brace_count == 0 { - return i - } - } - } - return none -} - -// get_function_from_file parses a V file and extracts a specific function block including its comments -// ARGS: -// file_path string - path to the V file -// function_name string - name of the function to extract -// RETURNS: string - the function block including comments, or empty string if not found -fn get_function_from_file(file_path string, function_name string) !string { - content := os.read_file(file_path) or { - return error('Failed to read file: ${file_path}: ${err}') - } - - lines := content.split_into_lines() - mut result := []string{} - mut in_function := false - mut brace_count := 0 - mut comment_block := []string{} - - for i, line in lines { - trimmed := line.trim_space() - - // Collect comments that might be above the function - if trimmed.starts_with('//') { - if !in_function { - comment_block << line - } else if brace_count > 0 { - result << line - } - continue - } - - // Check if we found the function - if !in_function && (trimmed.starts_with('fn ${function_name}(') - || trimmed.starts_with('pub fn ${function_name}(')) { - in_function = true - // Add collected comments - result << comment_block - comment_block = [] - result << line - if line.contains('{') { - brace_count++ - } - continue - } - - // If we're inside the function, keep track of braces - if in_function { - result << line - - for c in line { - if c == `{` { - brace_count++ - } else if c == `}` { - brace_count-- - } - } - - // If brace_count is 0, we've reached the end of the function - if brace_count == 0 && trimmed.contains('}') { - return result.join('\n') - } - } else { - // Reset comment block if we pass a blank line - if trimmed == '' { - comment_block = [] - } - } - } - - if !in_function { - return error('Function "${function_name}" not found in ${file_path}') - } - - return result.join('\n') -} - -// list_v_files returns all .v files in a directory (non-recursive), excluding generated files ending with _.v -fn list_v_files(dir string) ![]string { - files := os.ls(dir) or { return error('Error listing directory: ${err}') } - - mut v_files := []string{} - for file in files { - if file.ends_with('.v') && !file.ends_with('_.v') { - filepath := os.join_path(dir, file) - v_files << filepath - } - } - - return v_files -} - -// test runs v test on the specified file or directory -pub fn vtest(fullpath string) !string { - logger.info('test ${fullpath}') - if !os.exists(fullpath) { - return error('File or directory does not exist: ${fullpath}') - } - if os.is_dir(fullpath) { - mut results := '' - for item in list_v_files(fullpath)! { - results += vtest(item)! - results += '\n-----------------------\n' - } - return results - } else { - cmd := 'v -cg -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc test ${fullpath}' - logger.debug('Executing command: ${cmd}') - result := os.execute(cmd) - if result.exit_code != 0 { - return error('Test failed for ${fullpath} with exit code ${result.exit_code}\n${result.output}') - } else { - logger.info('Test completed for ${fullpath}') - } - return 'Command: ${cmd}\nExit code: ${result.exit_code}\nOutput:\n${result.output}' - } -} - -// vvet runs v vet on the specified file or directory -pub fn vvet(fullpath string) !string { - logger.info('vet ${fullpath}') - if !os.exists(fullpath) { - return error('File or directory does not exist: ${fullpath}') - } - - if os.is_dir(fullpath) { - mut results := '' - files := list_v_files(fullpath) or { return error('Error listing V files: ${err}') } - for file in files { - results += vet_file(file) or { - logger.error('Failed to vet ${file}: ${err}') - return error('Failed to vet ${file}: ${err}') - } - results += '\n-----------------------\n' - } - return results - } else { - return vet_file(fullpath) - } -} - -// vet_file runs v vet on a single file -fn vet_file(file string) !string { - cmd := 'v vet -v -w ${file}' - logger.debug('Executing command: ${cmd}') - result := os.execute(cmd) - if result.exit_code != 0 { - return error('Vet failed for ${file} with exit code ${result.exit_code}\n${result.output}') - } else { - logger.info('Vet completed for ${file}') - } - return 'Command: ${cmd}\nExit code: ${result.exit_code}\nOutput:\n${result.output}' -} - -// cmd := 'v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc ${fullpath}' diff --git a/libarchive/mcp_old/vcode/logic/vlang_test.v b/libarchive/mcp_old/vcode/logic/vlang_test.v deleted file mode 100644 index ade88519..00000000 --- a/libarchive/mcp_old/vcode/logic/vlang_test.v +++ /dev/null @@ -1,100 +0,0 @@ -module vcode - -import os - -// Test file for the get_type_from_module function in vlang.v - -// This test verifies that the get_type_from_module function correctly extracts -// struct definitions from V source files - -// Helper function to create test files with struct definitions -fn create_test_files() !(string, string, string) { - // Create a temporary directory for our test files - test_dir := os.temp_dir() - test_file_path := os.join_path(test_dir, 'test_type.v') - - // Create a test file with a simple struct - test_content := 'module test_module - -struct TestType { - name string - age int - active bool -} - -// Another struct to make sure we get the right one -struct OtherType { - id string -} -' - os.write_file(test_file_path, test_content) or { - eprintln('Failed to create test file: ${err}') - return error('Failed to create test file: ${err}') - } - - // Create a test file with a nested struct - nested_test_content := 'module test_module - -struct NestedType { - config map[string]string { - required: true - } - data []struct { - key string - value string - } -} -' - nested_test_file := os.join_path(test_dir, 'nested_test.v') - os.write_file(nested_test_file, nested_test_content) or { - eprintln('Failed to create nested test file: ${err}') - return error('Failed to create nested test file: ${err}') - } - - return test_dir, test_file_path, nested_test_file -} - -// Test function for get_type_from_module -fn test_get_type_from_module() { - // Create test files - test_dir, test_file_path, nested_test_file := create_test_files() or { - eprintln('Failed to create test files: ${err}') - assert false - return - } - - // Test case 1: Get a simple struct - type_content := get_type_from_module(test_dir, 'TestType') or { - eprintln('Failed to get type: ${err}') - assert false - return - } - - // Verify the content matches what we expect - expected := '\n\tname string\n\tage int\n\tactive bool\n}' - assert type_content == expected, 'Expected: "${expected}", got: "${type_content}"' - - // Test case 2: Try to get a non-existent type - non_existent := get_type_from_module(test_dir, 'NonExistentType') or { - // This should fail, so we expect an error - assert err.str().contains('not found in module'), 'Expected error message about type not found' - '' - } - assert non_existent == '', 'Expected empty string for non-existent type' - - // Test case 3: Test with nested braces in the struct - nested_type_content := get_type_from_module(test_dir, 'NestedType') or { - eprintln('Failed to get nested type: ${err}') - assert false - return - } - - expected_nested := '\n\tconfig map[string]string {\n\t\trequired: true\n\t}\n\tdata []struct {\n\t\tkey string\n\t\tvalue string\n\t}\n}' - assert nested_type_content == expected_nested, 'Expected: "${expected_nested}", got: "${nested_type_content}"' - - // Clean up test files - os.rm(test_file_path) or {} - os.rm(nested_test_file) or {} - - println('All tests for get_type_from_module passed successfully!') -} diff --git a/libarchive/mcp_old/vcode/logic/vlang_tools.v b/libarchive/mcp_old/vcode/logic/vlang_tools.v deleted file mode 100644 index 68b082ed..00000000 --- a/libarchive/mcp_old/vcode/logic/vlang_tools.v +++ /dev/null @@ -1,39 +0,0 @@ -module vcode - -import incubaid.herolib.mcp -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.schemas.jsonschema -import x.json2 { Any } - -const get_function_from_file_tool = mcp.Tool{ - name: 'get_function_from_file' - description: 'get_function_from_file parses a V file and extracts a specific function block including its comments -ARGS: -file_path string - path to the V file -function_name string - name of the function to extract -RETURNS: string - the function block including comments, or empty string if not found' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'file_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'function_name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['file_path', 'function_name'] - } -} - -pub fn (d &VCode) get_function_from_file_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - file_path := arguments['file_path'].str() - function_name := arguments['function_name'].str() - result := code.get_function_from_file(file_path, function_name) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result.vgen()) - } -} diff --git a/libarchive/mcp_old/vcode/logic/write_vfile_tool.v b/libarchive/mcp_old/vcode/logic/write_vfile_tool.v deleted file mode 100644 index 999ade5f..00000000 --- a/libarchive/mcp_old/vcode/logic/write_vfile_tool.v +++ /dev/null @@ -1,67 +0,0 @@ -module vcode - -import incubaid.herolib.mcp -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.schemas.jsonschema -import x.json2 { Any } - -const write_vfile_tool = mcp.Tool{ - name: 'write_vfile' - description: 'write_vfile parses a V code string into a VFile and writes it to the specified path -ARGS: -path string - directory path where to write the file -code string - V code content to write -format bool - whether to format the code (optional, default: false) -overwrite bool - whether to overwrite existing file (optional, default: false) -prefix string - prefix to add to the filename (optional, default: "") -RETURNS: string - success message with the path of the written file' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'code': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'format': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'boolean' - }) - 'overwrite': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'boolean' - }) - 'prefix': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['path', 'code'] - } -} - -pub fn (d &VCode) write_vfile_tool_handler(arguments map[string]Any) !mcp.ToolCallResult { - path := arguments['path'].str() - code_str := arguments['code'].str() - - // Parse optional parameters with defaults - format := if 'format' in arguments { arguments['format'].bool() } else { false } - overwrite := if 'overwrite' in arguments { arguments['overwrite'].bool() } else { false } - prefix := if 'prefix' in arguments { arguments['prefix'].str() } else { '' } - - // Create write options - options := code.WriteOptions{ - format: format - overwrite: overwrite - prefix: prefix - } - - // Parse the V code string into a VFile - vfile := code.parse_vfile(code_str) or { return mcp.error_tool_call_result(err) } - - // Write the VFile to the specified path - vfile.write(path, options) or { return mcp.error_tool_call_result(err) } - - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string]('Successfully wrote V file to ${path}') - } -} diff --git a/libarchive/mcp_old/vcode/mcp/handlers.v b/libarchive/mcp_old/vcode/mcp/handlers.v deleted file mode 100644 index 1c66f4d1..00000000 --- a/libarchive/mcp_old/vcode/mcp/handlers.v +++ /dev/null @@ -1,54 +0,0 @@ -module pugconvert - -import incubaid.herolib.mcp -import x.json2 as json { Any } -import incubaid.herolib.mcp.aitools.pugconvert -import incubaid.herolib.core.pathlib -import os - -pub fn handler(arguments map[string]Any) !mcp.ToolCallResult { - path := arguments['path'].str() - - // Check if path exists - if !os.exists(path) { - return mcp.ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]("Error: Path '${path}' does not exist") - } - } - - // Determine if path is a file or directory - is_directory := os.is_dir(path) - - mut message := '' - - if is_directory { - // Convert all pug files in the directory - pugconvert.convert_pug(path) or { - return mcp.ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]('Error converting pug files in directory: ${err}') - } - } - message = "Successfully converted all pug files in directory '${path}'" - } else if path.ends_with('.v') { - // Convert a single pug file - pugconvert.convert_pug_file(path) or { - return mcp.ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]('Error converting pug file: ${err}') - } - } - message = "Successfully converted pug file '${path}'" - } else { - return mcp.ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]("Error: Path '${path}' is not a directory or .pug file") - } - } - - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](message) - } -} diff --git a/libarchive/mcp_old/vcode/mcp/mcp.v b/libarchive/mcp_old/vcode/mcp/mcp.v deleted file mode 100644 index dfad8d71..00000000 --- a/libarchive/mcp_old/vcode/mcp/mcp.v +++ /dev/null @@ -1,27 +0,0 @@ -module pugconvert - -import incubaid.herolib.mcp -import incubaid.herolib.mcp.logger -import incubaid.herolib.schemas.jsonrpc - -pub fn new_mcp_server() !&mcp.Server { - logger.info('Creating new Developer MCP server') - - // Initialize the server with the empty handlers map - mut server := mcp.new_server(mcp.MemoryBackend{ - tools: { - 'pugconvert': specs - } - tool_handlers: { - 'pugconvert': handler - } - }, mcp.ServerParams{ - config: mcp.ServerConfiguration{ - server_info: mcp.ServerInfo{ - name: 'developer' - version: '1.0.0' - } - } - })! - return server -} diff --git a/libarchive/mcp_old/vcode/mcp/specifications.v b/libarchive/mcp_old/vcode/mcp/specifications.v deleted file mode 100644 index e6493eb9..00000000 --- a/libarchive/mcp_old/vcode/mcp/specifications.v +++ /dev/null @@ -1,21 +0,0 @@ -module pugconvert - -import incubaid.herolib.mcp -import x.json2 as json -import incubaid.herolib.schemas.jsonschema -import incubaid.herolib.mcp.logger - -const specs = mcp.Tool{ - name: 'pugconvert' - description: 'Convert Pug template files to Jet template files' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to a .pug file or directory containing .pug files to convert' - }) - } - required: ['path'] - } -} diff --git a/libarchive/mcp_pugconvert/cmd/.gitignore b/libarchive/mcp_pugconvert/cmd/.gitignore deleted file mode 100644 index 1a94556d..00000000 --- a/libarchive/mcp_pugconvert/cmd/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -main - diff --git a/libarchive/mcp_pugconvert/cmd/compile.sh b/libarchive/mcp_pugconvert/cmd/compile.sh deleted file mode 100755 index 87b33856..00000000 --- a/libarchive/mcp_pugconvert/cmd/compile.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -ex - -export name="mcp_pugconvert" - -# Change to the directory containing this script -cd "$(dirname "$0")" - -# Compile the V program -v -n -w -gc none -cc tcc -d use_openssl -enable-globals main.v - -# Ensure the binary is executable -chmod +x main -mv main ~/hero/bin/${name} - -echo "Compilation successful. Binary '${name}' is ready." diff --git a/libarchive/mcp_pugconvert/cmd/main.v b/libarchive/mcp_pugconvert/cmd/main.v deleted file mode 100644 index fec57ae4..00000000 --- a/libarchive/mcp_pugconvert/cmd/main.v +++ /dev/null @@ -1,17 +0,0 @@ -module main - -import incubaid.herolib.ai.mcp.servers.pugconvert.mcp - -fn main() { - // Create a new MCP server - mut server := mcp.new_mcp_server() or { - eprintln('Failed to create MCP server: ${err}') - return - } - - // Start the server - server.start() or { - eprintln('Failed to start MCP server: ${err}') - return - } -} diff --git a/libarchive/mcp_pugconvert/logic/convertpug.v b/libarchive/mcp_pugconvert/logic/convertpug.v deleted file mode 100644 index ffafda18..00000000 --- a/libarchive/mcp_pugconvert/logic/convertpug.v +++ /dev/null @@ -1,203 +0,0 @@ -module pugconvert - -import incubaid.herolib.clients.openai -import incubaid.herolib.core.texttools -import incubaid.herolib.core.pathlib -import json - -pub fn convert_pug(mydir string) ! { - mut d := pathlib.get_dir(path: mydir, create: false)! - list := d.list(regex: [r'.*\.pug$'], include_links: false, files_only: true)! - for item in list.paths { - convert_pug_file(item.path)! - } -} - -// extract_template parses AI response content to extract just the template -fn extract_template(raw_content string) string { - mut content := raw_content - - // First check for tag - if content.contains('') { - content = content.split('')[1].trim_space() - } - - // Look for ```jet code block - if content.contains('```jet') { - parts := content.split('```jet') - if parts.len > 1 { - end_parts := parts[1].split('```') - if end_parts.len > 0 { - content = end_parts[0].trim_space() - } - } - } else if content.contains('```') { - // If no ```jet, look for regular ``` code block - parts := content.split('```') - if parts.len >= 2 { - // Take the content between the first set of ``` - // This handles both ```content``` and cases where there's only an opening ``` - content = parts[1].trim_space() - - // If we only see an opening ``` but no closing, cleanup any remaining backticks - // to avoid incomplete formatting markers - if !content.contains('```') { - content = content.replace('`', '') - } - } - } - - return content -} - -pub fn convert_pug_file(myfile string) ! { - println(myfile) - - // Create new file path by replacing .pug extension with .jet - jet_file := myfile.replace('.pug', '.jet') - - // Check if jet file already exists, if so skip processing - mut jet_path_exist := pathlib.get_file(path: jet_file, create: false)! - if jet_path_exist.exists() { - println('Jet file already exists: ${jet_file}. Skipping conversion.') - return - } - - mut content_path := pathlib.get_file(path: myfile, create: false)! - content := content_path.read()! - - mut l := loader() - mut client := openai.get()! - - base_instruction := ' - You are a template language converter. You convert Pug templates to Jet templates. - - The target template language, Jet, is defined as follows: - ' - - base_user_prompt := ' - Convert this following Pug template to Jet: - - only output the resulting template, no explanation, no steps, just the jet template - ' - - // We'll retry up to 5 times if validation fails - max_attempts := 5 - mut attempts := 0 - mut is_valid := false - mut error_message := '' - mut template := '' - - for attempts < max_attempts && !is_valid { - attempts++ - - mut system_content := texttools.dedent(base_instruction) + '\n' + l.jet() - mut user_prompt := '' - - // Create different prompts for first attempt vs retries - if attempts == 1 { - // First attempt - convert from PUG - user_prompt = texttools.dedent(base_user_prompt) + '\n' + content - - // Print what we're sending to the AI service - println('Sending to OpenAI for conversion:') - println('--------------------------------') - println(content) - println('--------------------------------') - } else { - // Retries - focus on fixing the previous errors - println('Attempt ${attempts}: Retrying with error feedback') - user_prompt = ' -The previous Jet template conversion had the following error: -ERROR: ${error_message} - -Here was the template that had errors: -``` -${template} -``` - -The original pug input was was -``` -${content} -``` - -Please fix the template and try again. Learn from feedback and check which jet template was created. -Return only the corrected Jet template. -Dont send back more information than the fixed template, make sure its in jet format. - - ' // Print what we're sending for the retry - - println('Sending to OpenAI for correction:') - println('--------------------------------') - println(user_prompt) - println('--------------------------------') - } - - mut m := openai.Messages{ - messages: [ - openai.Message{ - role: .system - content: system_content - }, - openai.Message{ - role: .user - content: user_prompt - }, - ] - } - - // Create a chat completion request - res := client.chat_completion( - msgs: m - model: 'deepseek-r1-distill-llama-70b' - max_completion_tokens: 64000 - )! - - println('-----') - - // Print AI response before extraction - println('Response received from AI:') - println('--------------------------------') - println(res.choices[0].message.content) - println('--------------------------------') - - // Extract the template from the AI response - template = extract_template(res.choices[0].message.content) - - println('Extracted template for ${myfile}:') - println('--------------------------------') - println(template) - println('--------------------------------') - - // Validate the template - validation_result := jetvaliditycheck(template) or { - // If validation service is unavailable, we'll just proceed with the template - println('Warning: Template validation service unavailable: ${err}') - break - } - - // Check if template is valid - if validation_result.is_valid { - is_valid = true - println('Template validation successful!') - } else { - error_message = validation_result.error - println('Template validation failed: ${error_message}') - } - } - - // Report the validation outcome - if is_valid { - println('Successfully converted template after ${attempts} attempt(s)') - // Create the file and write the processed content - println('Converted to: ${jet_file}') - mut jet_path := pathlib.get_file(path: jet_file, create: true)! - jet_path.write(template)! - } else if attempts >= max_attempts { - println('Warning: Could not validate template after ${max_attempts} attempts') - println('Using best attempt despite validation errors: ${error_message}') - jet_file2 := jet_file.replace('.jet', '_error.jet') - mut jet_path2 := pathlib.get_file(path: jet_file2, create: true)! - jet_path2.write(template)! - } -} diff --git a/libarchive/mcp_pugconvert/logic/jetvalidation.v b/libarchive/mcp_pugconvert/logic/jetvalidation.v deleted file mode 100644 index 78498120..00000000 --- a/libarchive/mcp_pugconvert/logic/jetvalidation.v +++ /dev/null @@ -1,85 +0,0 @@ -module pugconvert - -import incubaid.herolib.core.httpconnection -import json - -// JetTemplateResponse is the expected response structure from the validation service -struct JetTemplateResponse { - valid bool - message string - error string -} - -// ValidationResult represents the result of a template validation -pub struct ValidationResult { -pub: - is_valid bool - error string -} - -// jetvaliditycheck validates a Jet template by sending it to a validation service -// The function sends the template to http://localhost:9020/checkjet for validation -// Returns a ValidationResult containing validity status and any error messages -pub fn jetvaliditycheck(jetcontent string) !ValidationResult { - // Create HTTP connection to the validation service - mut conn := httpconnection.HTTPConnection{ - base_url: 'http://localhost:9020' - } - - // Prepare the request data - template content wrapped in JSON - template_data := json.encode({ - 'template': jetcontent - }) - - // Print what we're sending to the AI service - // println('Sending to JET validation service:') - // println('--------------------------------') - // println(jetcontent) - // println('--------------------------------') - - // Send the POST request to the validation endpoint - req := httpconnection.Request{ - prefix: 'checkjet' - data: template_data - dataformat: .json - } - - // Execute the request - result := conn.post_json_str(req) or { - // Handle connection errors - return ValidationResult{ - is_valid: false - error: 'Connection error: ${err}' - } - } - - // Attempt to parse the response as JSON using the expected struct - response := json.decode(JetTemplateResponse, result) or { - // If we can't parse JSON using our struct, the server didn't return the expected format - return ValidationResult{ - is_valid: false - error: 'Server returned unexpected format: ${err.msg()}' - } - } - - // Use the structured response data - if response.valid == false { - error_msg := if response.error != '' { - response.error - } else if response.message != '' { - response.message - } else { - 'Unknown validation error' - } - - return ValidationResult{ - is_valid: false - error: error_msg - } - } - - return ValidationResult{ - is_valid: true - error: '' - } -} diff --git a/libarchive/mcp_pugconvert/logic/loader.v b/libarchive/mcp_pugconvert/logic/loader.v deleted file mode 100644 index 2a35d454..00000000 --- a/libarchive/mcp_pugconvert/logic/loader.v +++ /dev/null @@ -1,25 +0,0 @@ -module pugconvert - -import v.embed_file -import os - -@[heap] -pub struct FileLoader { -pub mut: - embedded_files map[string]embed_file.EmbedFileData @[skip; str: skip] -} - -fn (mut loader FileLoader) load() { - loader.embedded_files['jet'] = $embed_file('templates/jet_instructions.md') -} - -fn (mut loader FileLoader) jet() string { - c := loader.embedded_files['jet'] or { panic('bug embed') } - return c.to_string() -} - -fn loader() FileLoader { - mut loader := FileLoader{} - loader.load() - return loader -} diff --git a/libarchive/mcp_pugconvert/logic/templates/jet_instructions.md b/libarchive/mcp_pugconvert/logic/templates/jet_instructions.md deleted file mode 100644 index 5bf8cee7..00000000 --- a/libarchive/mcp_pugconvert/logic/templates/jet_instructions.md +++ /dev/null @@ -1,446 +0,0 @@ -# Jet Template Engine Syntax Reference - -## Delimiters - -Template delimiters are `{{` and `}}`. -Delimiters can use `.` to output the execution context: - -```jet -hello {{ . }} -``` - -### Whitespace Trimming - -Whitespace around delimiters can be trimmed using `{{-` and `-}}`: - -```jet -foo {{- "bar" -}} baz -``` - -Whitespace includes spaces, tabs, carriage returns, and newlines. - -### Comments - -Comments use `{* ... *}`: - -```jet -{* this is a comment *} - -{* - Multiline - {{ expressions }} are ignored -*} -``` - ---- - -## Variables - -### Initialization - -```jet -{{ foo := "bar" }} -``` - -### Assignment - -```jet -{{ foo = "asd" }} -{{ foo = 4711 }} -``` - -Skip assignment but still evaluate: - -```jet -{{ _ := stillRuns() }} -{{ _ = stillRuns() }} -``` - ---- - -## Expressions - -### Identifiers - -Identifiers resolve to values: - -```jet -{{ len("hello") }} -{{ isset(foo, bar) }} -``` - -### Indexing - -#### String - -```jet -{{ s := "helloworld" }} -{{ s[1] }} -``` - -#### Slice / Array - -```jet -{{ s := slice("foo", "bar", "asd") }} -{{ s[0] }} -{{ s[2] }} -``` - -#### Map - -```jet -{{ m := map("foo", 123, "bar", 456) }} -{{ m["foo"] }} -``` - -#### Struct - -```jet -{{ user["Name"] }} -``` - -### Field Access - -#### Map - -```jet -{{ m.foo }} -{{ range s }} - {{ .foo }} -{{ end }} -``` - -#### Struct - -```jet -{{ user.Name }} -{{ range users }} - {{ .Name }} -{{ end }} -``` - -### Slicing - -```jet -{{ s := slice(6, 7, 8, 9, 10, 11) }} -{{ sevenEightNine := s[1:4] }} -``` - -### Arithmetic - -```jet -{{ 1 + 2 * 3 - 4 }} -{{ (1 + 2) * 3 - 4.1 }} -``` - -### String Concatenation - -```jet -{{ "HELLO" + " " + "WORLD!" }} -``` - -#### Logical Operators - -- `&&` -- `||` -- `!` -- `==`, `!=` -- `<`, `>`, `<=`, `>=` - -```jet -{{ item == true || !item2 && item3 != "test" }} -{{ item >= 12.5 || item < 6 }} -``` - -### Ternary Operator - -```jet -{{ .HasTitle ? .Title : "Title not set" }} -``` - -### Method Calls - -```jet -{{ user.Rename("Peter") }} -{{ range users }} - {{ .FullName() }} -{{ end }} -``` - -### Function Calls - -```jet -{{ len(s) }} -{{ isset(foo, bar) }} -``` - -#### Prefix Syntax - -```jet -{{ len: s }} -{{ isset: foo, bar }} -``` - -#### Pipelining - -```jet -{{ "123" | len }} -{{ "FOO" | lower | len }} -{{ "hello" | repeat: 2 | len }} -``` - -**Escapers must be last in a pipeline:** - -```jet -{{ "hello" | upper | raw }} -{{ raw: "hello" }} -{{ raw: "hello" | upper }} -``` - -#### Piped Argument Slot - -```jet -{{ 2 | repeat("foo", _) }} -{{ 2 | repeat("foo", _) | repeat(_, 3) }} -``` - ---- - -## Control Structures - -### if - -```jet -{{ if foo == "asd" }} - foo is 'asd'! -{{ end }} -``` - -#### if / else - -```jet -{{ if foo == "asd" }} - ... -{{ else }} - ... -{{ end }} -``` - -#### if / else if - -```jet -{{ if foo == "asd" }} -{{ else if foo == 4711 }} -{{ end }} -``` - -#### if / else if / else - -```jet -{{ if foo == "asd" }} -{{ else if foo == 4711 }} -{{ else }} -{{ end }} -``` - -### range - -#### Slices / Arrays - -```jet -{{ range s }} - {{ . }} -{{ end }} - -{{ range i := s }} - {{ i }}: {{ . }} -{{ end }} - -{{ range i, v := s }} - {{ i }}: {{ v }} -{{ end }} -``` - -#### Maps - -```jet -{{ range k := m }} - {{ k }}: {{ . }} -{{ end }} - -{{ range k, v := m }} - {{ k }}: {{ v }} -{{ end }} -``` - -#### Channels - -```jet -{{ range v := c }} - {{ v }} -{{ end }} -``` - -#### Custom Ranger - -Any Go type implementing `Ranger` can be ranged over. - -#### else - -```jet -{{ range searchResults }} - {{ . }} -{{ else }} - No results found :( -{{ end }} -``` - -### try - -```jet -{{ try }} - {{ foo }} -{{ end }} -``` - -### try / catch - -```jet -{{ try }} - {{ foo }} -{{ catch }} - Fallback content -{{ end }} - -{{ try }} - {{ foo }} -{{ catch err }} - {{ log(err.Error()) }} - Error: {{ err.Error() }} -{{ end }} -``` - ---- - -## Templates - -### include - -```jet -{{ include "./user.jet" }} - - -
- {{ .["name"] }}: {{ .["email"] }} -
-``` - -### return - -```jet - -{{ return "foo" }} - - -{{ foo := exec("./foo.jet") }} -Hello, {{ foo }}! -``` - ---- - -## Blocks - -### block - -```jet -{{ block copyright() }} -
© ACME, Inc. 2020
-{{ end }} - -{{ block inputField(type="text", label, id, value="", required=false) }} - - -{{ end }} -``` - -### yield - -```jet -{{ yield copyright() }} - -{{ yield inputField(id="firstname", label="First name", required=true) }} - -{{ block buff() }} - {{ . }} -{{ end }} - -{{ yield buff() "Batman" }} -``` - -### content - -```jet -{{ block link(target) }} - {{ yield content }} -{{ end }} - -{{ yield link(target="https://example.com") content }} - Example Inc. -{{ end }} -``` - -```jet -{{ block header() }} -
- {{ yield content }} -
-{{ content }} -

Hey {{ name }}!

-{{ end }} -``` - -### Recursion - -```jet -{{ block menu() }} -
    - {{ range . }} -
  • {{ .Text }}{{ if len(.Children) }}{{ yield menu() .Children }}{{ end }}
  • - {{ end }} -
-{{ end }} -``` - -### extends - -```jet - -{{ extends "./layout.jet" }} -{{ block body() }} -
This content can be yielded anywhere.
-{{ end }} - - - - - {{ yield body() }} - - -``` - -### import - -```jet - -{{ block body() }} -
This content can be yielded anywhere.
-{{ end }} - - -{{ import "./my_blocks.jet" }} - - - {{ yield body() }} - - -``` \ No newline at end of file diff --git a/libarchive/mcp_pugconvert/mcp/handlers.v b/libarchive/mcp_pugconvert/mcp/handlers.v deleted file mode 100644 index 2b806f56..00000000 --- a/libarchive/mcp_pugconvert/mcp/handlers.v +++ /dev/null @@ -1,54 +0,0 @@ -module mcp - -import incubaid.herolib.ai.mcp -import x.json2 as json { Any } -import incubaid.herolib.ai.mcp.aitools.pugconvert -import incubaid.herolib.core.pathlib -import os - -pub fn handler(arguments map[string]Any) !ToolCallResult { - path := arguments['path'].str() - - // Check if path exists - if !os.exists(path) { - return ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]("Error: Path '${path}' does not exist") - } - } - - // Determine if path is a file or directory - is_directory := os.is_dir(path) - - mut message := '' - - if is_directory { - // Convert all pug files in the directory - pugconvert.convert_pug(path) or { - return ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]('Error converting pug files in directory: ${err}') - } - } - message = "Successfully converted all pug files in directory '${path}'" - } else if path.ends_with('.pug') { - // Convert a single pug file - pugconvert.convert_pug_file(path) or { - return ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]('Error converting pug file: ${err}') - } - } - message = "Successfully converted pug file '${path}'" - } else { - return ToolCallResult{ - is_error: true - content: mcp.result_to_mcp_tool_contents[string]("Error: Path '${path}' is not a directory or .pug file") - } - } - - return ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](message) - } -} diff --git a/libarchive/mcp_pugconvert/mcp/mcp.v b/libarchive/mcp_pugconvert/mcp/mcp.v deleted file mode 100644 index c24f618a..00000000 --- a/libarchive/mcp_pugconvert/mcp/mcp.v +++ /dev/null @@ -1,27 +0,0 @@ -module mcp - -import incubaid.herolib.ai.mcp -import incubaid.herolib.ai.mcp.logger -import incubaid.herolib.schemas.jsonrpc - -pub fn new_mcp_server() !&Server { - logger.info('Creating new Developer MCP server') - - // Initialize the server with the empty handlers map - mut server := mcp.new_server(MemoryBackend{ - tools: { - 'pugconvert': specs - } - tool_handlers: { - 'pugconvert': handler - } - }, ServerParams{ - config: ServerConfiguration{ - server_info: ServerInfo{ - name: 'developer' - version: '1.0.0' - } - } - })! - return server -} diff --git a/libarchive/mcp_pugconvert/mcp/specifications.v b/libarchive/mcp_pugconvert/mcp/specifications.v deleted file mode 100644 index bc129928..00000000 --- a/libarchive/mcp_pugconvert/mcp/specifications.v +++ /dev/null @@ -1,21 +0,0 @@ -module mcp - -import incubaid.herolib.ai.mcp -import x.json2 as json -import incubaid.herolib.schemas.jsonschema -import incubaid.herolib.ai.mcp.logger - -const specs = Tool{ - name: 'pugconvert' - description: 'Convert Pug template files to Jet template files' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to a .pug file or directory containing .pug files to convert' - }) - } - required: ['path'] - } -} diff --git a/libarchive/mcp_rhai/cmd/.gitignore b/libarchive/mcp_rhai/cmd/.gitignore deleted file mode 100644 index 1a94556d..00000000 --- a/libarchive/mcp_rhai/cmd/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -main - diff --git a/libarchive/mcp_rhai/cmd/compile.sh b/libarchive/mcp_rhai/cmd/compile.sh deleted file mode 100755 index c6f91a11..00000000 --- a/libarchive/mcp_rhai/cmd/compile.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -ex - -export name="mcp_rhai" - -# Change to the directory containing this script -cd "$(dirname "$0")" - -# Compile the V program -v -n -w -gc none -cc tcc -d use_openssl -enable-globals main.v - -# Ensure the binary is executable -chmod +x main -mv main ~/hero/bin/${name} - -echo "Compilation successful. Binary '${name}' is ready." diff --git a/libarchive/mcp_rhai/cmd/main.v b/libarchive/mcp_rhai/cmd/main.v deleted file mode 100644 index 9b9e1ba7..00000000 --- a/libarchive/mcp_rhai/cmd/main.v +++ /dev/null @@ -1,17 +0,0 @@ -module main - -import incubaid.herolib.ai.mcp.rhai.mcp - -fn main() { - // Create a new MCP server - mut server := mcp.new_mcp_server() or { - // Note: Removed log.error() as it interferes with STDIO transport JSON-RPC communication - return - } - - // Start the server - server.start() or { - // Note: Removed log.error() as it interferes with STDIO transport JSON-RPC communication - return - } -} diff --git a/libarchive/mcp_rhai/example/example copy.vsh b/libarchive/mcp_rhai/example/example copy.vsh deleted file mode 100644 index 6a32c74e..00000000 --- a/libarchive/mcp_rhai/example/example copy.vsh +++ /dev/null @@ -1,532 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.ai.mcp.aitools.escalayer -import os - -fn main() { - // Get the current directory - current_dir := os.dir(@FILE) - - // Check if a source code path was provided as an argument - if os.args.len < 2 { - println('Please provide the path to the source code directory as an argument') - println('Example: ./example.vsh /path/to/source/code/directory') - return - } - - // Get the source code path from the command line arguments - source_code_path := os.args[1] - - // Check if the path exists and is a directory - if !os.exists(source_code_path) { - println('Source code path does not exist: ${source_code_path}') - return - } - - if !os.is_dir(source_code_path) { - println('Source code path is not a directory: ${source_code_path}') - return - } - - // Get all Rust files in the directory - files := os.ls(source_code_path) or { - println('Failed to list files in directory: ${err}') - return - } - - // Combine all Rust files into a single source code string - mut source_code := '' - for file in files { - file_path := os.join_path(source_code_path, file) - - // Skip directories and non-Rust files - if os.is_dir(file_path) || !file.ends_with('.rs') { - continue - } - - // Read the file content - file_content := os.read_file(file_path) or { - println('Failed to read file ${file_path}: ${err}') - continue - } - - // Add file content to the combined source code - source_code += '// File: ${file}\n${file_content}\n\n' - } - - if source_code == '' { - println('No Rust files found in directory: ${source_code_path}') - return - } - - // Read the rhaiwrapping.md file - rhai_wrapping_md := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping.md') or { - println('Failed to read rhaiwrapping.md: ${err}') - return - } - - // Determine the crate path from the source code path - // Extract the path relative to the src directory - src_index := source_code_path.index('src/') or { - println('Could not determine crate path: src/ not found in path') - return - } - - mut path_parts := source_code_path[src_index + 4..].split('/') - // Remove the last part (the file name) - if path_parts.len > 0 { - path_parts.delete_last() - } - rel_path := path_parts.join('::') - crate_path := 'sal::${rel_path}' - - // Create a new task - mut task := escalayer.new_task( - name: 'rhai_wrapper_creator.escalayer' - description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file' - ) - - // Create model configs - sonnet_model := escalayer.ModelConfig{ - name: 'anthropic/claude-3.7-sonnet' - provider: 'anthropic' - temperature: 0.7 - max_tokens: 25000 - } - - gpt4_model := escalayer.ModelConfig{ - name: 'gpt-4' - provider: 'openai' - temperature: 0.7 - max_tokens: 25000 - } - - // Extract the module name from the directory path (last component) - dir_parts := source_code_path.split('/') - name := dir_parts[dir_parts.len - 1] - - // Create the prompt with source code, wrapper example, and rhai_wrapping_md - prompt_content := create_rhai_wrappers(name, source_code, os.read_file('${current_dir}/prompts/example_script.md') or { - '' - }, os.read_file('${current_dir}/prompts/wrapper.md') or { '' }, os.read_file('${current_dir}/prompts/errors.md') or { - '' - }, crate_path) - - // Create a prompt function that returns the prepared content - prompt_function := fn [prompt_content] (input string) string { - return prompt_content - } - - gen := RhaiGen{ - name: name - dir: source_code_path - } - - // Define a single unit task that handles everything - task.new_unit_task( - name: 'create_rhai_wrappers' - prompt_function: prompt_function - callback_function: gen.process_rhai_wrappers - base_model: sonnet_model - retry_model: gpt4_model - retry_count: 1 - ) - - // Initiate the task - result := task.initiate('') or { - println('Task failed: ${err}') - return - } - - println('Task completed successfully') - println('The wrapper files have been generated and compiled in the target directory.') - println('Check /Users/timurgordon/code/git.threefold.info/herocode/sal/src/rhai for the compiled output.') -} - -// Define the prompt functions -fn separate_functions(input string) string { - return 'Read the following Rust code and separate it into functions. Identify all the methods in the Container implementation and their purposes.\n\n${input}' -} - -fn create_wrappers(input string) string { - return 'Create Rhai wrappers for the Rust functions identified in the previous step. The wrappers should follow the builder pattern and provide a clean API for use in Rhai scripts. Include error handling and type conversion.\n\n${input}' -} - -fn create_example(input string) string { - return 'Create a Rhai example script that demonstrates how to use the wrapper functions. The example should be based on the provided example.rs file but adapted for Rhai syntax. Create a web server example that uses the container functions.\n\n${input}' -} - -// Define a Rhai wrapper generator function for Container functions -fn create_rhai_wrappers(name string, source_code string, example_rhai string, wrapper_md string, errors_md string, crate_path string) string { - guides := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping_classicai.md') or { - panic('Failed to read guides') - } - engine := $tmpl('./prompts/engine.md') - vector_vs_array := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_array_vs_vector.md') or { - panic('Failed to read guides') - } - rhai_integration_fixes := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_integration_fixes.md') or { - panic('Failed to read guides') - } - rhai_syntax_guide := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_syntax_guide.md') or { - panic('Failed to read guides') - } - generic_wrapper_rs := $tmpl('./templates/generic_wrapper.rs') - return 'You are a Rust developer tasked with creating Rhai wrappers for Rust functions. Please review the following best practices for Rhai wrappers and then create the necessary files. -${guides} -${vector_vs_array} -${example_rhai} -${wrapper_md} - -## Common Errors to Avoid -${errors_md} -${rhai_integration_fixes} -${rhai_syntax_guide} - -## Your Task - -Please create a wrapper.rs file that implements Rhai wrappers for the provided Rust code, and an example.rhai script that demonstrates how to use these wrappers: - -## Rust Code to Wrap - -```rust -${source_code} -``` - -IMPORTANT NOTES: -1. For Rhai imports, use: `use rhai::{Engine, EvalAltResult, plugin::*, Dynamic, Map, Array};` - only import what you actually use -2. The following dependencies are available in Cargo.toml: - - rhai = "1.21.0" - - serde = { version = "1.0", features = ["derive"] } - - serde_json = "1.0" - - sal = { path = "../../../" } - -3. For the wrapper: `use sal::${name};` this way you can access the module functions and objects with ${name}:: - -4. The generic_wrapper.rs file will be hardcoded into the package, you can use code from there. - -```rust -${generic_wrapper_rs} -``` - -5. IMPORTANT: Prefer strongly typed return values over Dynamic types whenever possible. Only use Dynamic when absolutely necessary. - - For example, return `Result>` instead of `Dynamic` when a function returns a string - - Use `Result>` instead of `Dynamic` when a function returns a boolean - - Use `Result, Box>` instead of `Dynamic` when a function returns a list of strings - -6. Your code should include public functions that can be called from Rhai scripts - -7. Make sure to implement all necessary helper functions for type conversion - -8. DO NOT use the #[rhai_fn] attribute - functions will be registered directly in the engine - -9. Make sure to handle string type consistency - use String::from() for string literals when returning in match arms with format!() strings - -10. When returning path references, convert them to owned strings (e.g., path().to_string()) - -11. For error handling, use proper Result types with Box for the error type: - ```rust - // INCORRECT: - pub fn some_function(arg: &str) -> Dynamic { - match some_operation(arg) { - Ok(result) => Dynamic::from(result), - Err(err) => Dynamic::from(format!("Error: {}", err)) - } - } - - // CORRECT: - pub fn some_function(arg: &str) -> Result> { - some_operation(arg).map_err(|err| { - Box::new(EvalAltResult::ErrorRuntime( - format!("Error: {}", err).into(), - rhai::Position::NONE - )) - }) - } - ``` - -12. IMPORTANT: Format your response with the code between triple backticks as follows: - -```rust -// wrapper.rs -// Your wrapper implementation here -``` - -```rust -// engine.rs -// Your engine.rs implementation here -``` - -```rhai -// example.rhai -// Your example Rhai script here -``` - -13. The example.rhai script should demonstrate the use of all the wrapper functions you create - -14. The engine.rs file should contain a register_module function that registers all the wrapper functions and types with the Rhai engine, and a create function. For example: - -${engine} - -MOST IMPORTANT: -import package being wrapped as `use sal::` -your engine create function is called `create_rhai_engine` - -``` -' -} - -@[params] -pub struct WrapperModule { -pub: - lib_rs string - example_rs string - engine_rs string - cargo_toml string - example_rhai string - generic_wrapper_rs string - wrapper_rs string -} - -// functions is a list of function names that AI should extract and pass in -fn create_wrapper_module(wrapper WrapperModule, functions []string, name_ string, base_dir string) !string { - // Define project directory paths - name := name_ - project_dir := '${base_dir}/rhai' - - // Create the project using cargo new --lib - if os.exists(project_dir) { - os.rmdir_all(project_dir) or { - return error('Failed to clean existing project directory: ${err}') - } - } - - // Run cargo new --lib to create the project - os.chdir(base_dir) or { return error('Failed to change directory to base directory: ${err}') } - - cargo_new_result := os.execute('cargo new --lib rhai') - if cargo_new_result.exit_code != 0 { - return error('Failed to create new library project: ${cargo_new_result.output}') - } - - // Create examples directory - examples_dir := '${project_dir}/examples' - os.mkdir_all(examples_dir) or { return error('Failed to create examples directory: ${err}') } - - // Write the lib.rs file - if wrapper.lib_rs != '' { - os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or { - return error('Failed to write lib.rs: ${err}') - } - } - - // Write the wrapper.rs file - if wrapper.wrapper_rs != '' { - os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or { - return error('Failed to write wrapper.rs: ${err}') - } - } - - // Write the generic wrapper.rs file - if wrapper.generic_wrapper_rs != '' { - os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or { - return error('Failed to write generic wrapper.rs: ${err}') - } - } - - // Write the example.rs file - if wrapper.example_rs != '' { - os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or { - return error('Failed to write example.rs: ${err}') - } - } - - // Write the engine.rs file if provided - if wrapper.engine_rs != '' { - os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or { - return error('Failed to write engine.rs: ${err}') - } - } - - // Write the Cargo.toml file - if wrapper.cargo_toml != '' { - os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or { - return error('Failed to write Cargo.toml: ${err}') - } - } - - // Write the example.rhai file if provided - if wrapper.example_rhai != '' { - os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or { - return error('Failed to write example.rhai: ${err}') - } - } - - return project_dir -} - -// Helper function to extract code blocks from the response -fn extract_code_block(response string, identifier string, language string) string { - // Find the start marker for the code block - mut start_marker := '```${language}\n// ${identifier}' - if language == '' { - start_marker = '```\n// ${identifier}' - } - - start_index := response.index(start_marker) or { - // Try alternative format - mut alt_marker := '```${language}\n${identifier}' - if language == '' { - alt_marker = '```\n${identifier}' - } - - response.index(alt_marker) or { return '' } - } - - // Find the end marker - end_marker := '```' - end_index := response.index_after(end_marker, start_index + start_marker.len) or { return '' } - - // Extract the content between the markers - content_start := start_index + start_marker.len - content := response[content_start..end_index].trim_space() - - return content -} - -// Extract module name from wrapper code -fn extract_module_name(code string) string { - lines := code.split('\n') - - for line in lines { - // Look for pub mod or mod declarations - if line.contains('pub mod ') || line.contains('mod ') { - // Extract module name - mut parts := []string{} - if line.contains('pub mod ') { - parts = line.split('pub mod ') - } else { - parts = line.split('mod ') - } - - if parts.len > 1 { - // Extract the module name and remove any trailing characters - mut name := parts[1].trim_space() - // Remove any trailing { or ; or whitespace - name = name.trim_right('{').trim_right(';').trim_space() - if name != '' { - return name - } - } - } - } - - return '' -} - -struct RhaiGen { - name string - dir string -} - -// Define the callback function that processes the response and compiles the code -fn (gen RhaiGen) process_rhai_wrappers(response string) !string { - // Extract wrapper.rs content - wrapper_rs_content := extract_code_block(response, 'wrapper.rs', 'rust') - if wrapper_rs_content == '' { - return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```') - } - - // Extract engine.rs content - mut engine_rs_content := extract_code_block(response, 'engine.rs', 'rust') - if engine_rs_content == '' { - // Try to extract from the response without explicit language marker - engine_rs_content = extract_code_block(response, 'engine.rs', '') - // if engine_rs_content == '' { - // // Use the template engine.rs - // engine_rs_content = $tmpl('./templates/engine.rs') - // } - } - - // Extract example.rhai content - mut example_rhai_content := extract_code_block(response, 'example.rhai', 'rhai') - if example_rhai_content == '' { - // Try to extract from the response without explicit language marker - example_rhai_content = extract_code_block(response, 'example.rhai', '') - if example_rhai_content == '' { - // Use the example from the template - example_script_md := os.read_file('${os.dir(@FILE)}/prompts/example_script.md') or { - return error('Failed to read example.rhai template: ${err}') - } - - // Extract the code block from the markdown file - example_rhai_content = extract_code_block(example_script_md, 'example.rhai', - 'rhai') - if example_rhai_content == '' { - return error('Failed to extract example.rhai from template file') - } - } - } - - // Extract function names from the wrapper.rs content - functions := extract_functions_from_code(wrapper_rs_content) - - println('Using module name: ${gen.name}_rhai') - println('Extracted functions: ${functions.join(', ')}') - - name := gen.name - // Create a WrapperModule struct with the extracted content - wrapper := WrapperModule{ - lib_rs: $tmpl('./templates/lib.rs') - wrapper_rs: wrapper_rs_content - example_rs: $tmpl('./templates/example.rs') - engine_rs: engine_rs_content - generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs') - cargo_toml: $tmpl('./templates/cargo.toml') - example_rhai: example_rhai_content - } - - // Create the wrapper module - base_target_dir := gen.dir - project_dir := create_wrapper_module(wrapper, functions, gen.name, base_target_dir) or { - return error('Failed to create wrapper module: ${err}') - } - - // Run the example - os.chdir(project_dir) or { return error('Failed to change directory to project: ${err}') } - - // Run cargo build first - build_result := os.execute('cargo build') - if build_result.exit_code != 0 { - return error('Compilation failed. Please fix the following errors and ensure your code is compatible with the existing codebase:\n\n${build_result.output}') - } - - // Run the example - run_result := os.execute('cargo run --example example') - - return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_result.output}\n\nRun output:\n${run_result.output}' -} - -// Extract function names from wrapper code -fn extract_functions_from_code(code string) []string { - mut functions := []string{} - lines := code.split('\n') - - for line in lines { - if line.contains('pub fn ') && !line.contains('//') { - // Extract function name - parts := line.split('pub fn ') - if parts.len > 1 { - name_parts := parts[1].split('(') - if name_parts.len > 0 { - fn_name := name_parts[0].trim_space() - if fn_name != '' { - functions << fn_name - } - } - } - } - } - - return functions -} diff --git a/libarchive/mcp_rhai/example/example.vsh b/libarchive/mcp_rhai/example/example.vsh deleted file mode 100755 index f95f6e1d..00000000 --- a/libarchive/mcp_rhai/example/example.vsh +++ /dev/null @@ -1,596 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.ai.mcp.aitools.escalayer -import os - -fn main() { - // Get the current directory where this script is located - current_dir := os.dir(@FILE) - - // Validate command line arguments - source_code_path := validate_command_args() or { - println(err) - return - } - - // Read and combine all Rust files in the source directory - source_code := read_source_code(source_code_path) or { - println(err) - return - } - - // Determine the crate path from the source code path - crate_path := determine_crate_path(source_code_path) or { - println(err) - return - } - - // Extract the module name from the directory path (last component) - name := extract_module_name_from_path(source_code_path) - - // Create the prompt content for the AI - prompt_content := create_rhai_wrappers(name, source_code, read_file_safely('${current_dir}/prompts/example_script.md'), - read_file_safely('${current_dir}/prompts/wrapper.md'), read_file_safely('${current_dir}/prompts/errors.md'), - crate_path) - - // Create the generator instance - gen := RhaiGen{ - name: name - dir: source_code_path - } - - // Run the task to generate Rhai wrappers - run_wrapper_generation_task(prompt_content, gen) or { - println('Task failed: ${err}') - return - } - - println('Task completed successfully') - println('The wrapper files have been generated and compiled in the target directory.') - println('Check /Users/timurgordon/code/git.threefold.info/herocode/sal/src/rhai for the compiled output.') -} - -// Validates command line arguments and returns the source code path -fn validate_command_args() !string { - if os.args.len < 2 { - return error('Please provide the path to the source code directory as an argument\nExample: ./example.vsh /path/to/source/code/directory') - } - - source_code_path := os.args[1] - - if !os.exists(source_code_path) { - return error('Source code path does not exist: ${source_code_path}') - } - - if !os.is_dir(source_code_path) { - return error('Source code path is not a directory: ${source_code_path}') - } - - return source_code_path -} - -// Reads and combines all Rust files in the given directory -fn read_source_code(source_code_path string) !string { - // Get all files in the directory - files := os.ls(source_code_path) or { - return error('Failed to list files in directory: ${err}') - } - - // Combine all Rust files into a single source code string - mut source_code := '' - for file in files { - file_path := os.join_path(source_code_path, file) - - // Skip directories and non-Rust files - if os.is_dir(file_path) || !file.ends_with('.rs') { - continue - } - - // Read the file content - file_content := os.read_file(file_path) or { - println('Failed to read file ${file_path}: ${err}') - continue - } - - // Add file content to the combined source code - source_code += '// File: ${file}\n${file_content}\n\n' - } - - if source_code == '' { - return error('No Rust files found in directory: ${source_code_path}') - } - - return source_code -} - -// Determines the crate path from the source code path -fn determine_crate_path(source_code_path string) !string { - // Extract the path relative to the src directory - src_index := source_code_path.index('src/') or { - return error('Could not determine crate path: src/ not found in path') - } - - mut path_parts := source_code_path[src_index + 4..].split('/') - // Remove the last part (the file name) - if path_parts.len > 0 { - path_parts.delete_last() - } - rel_path := path_parts.join('::') - return 'sal::${rel_path}' -} - -// Extracts the module name from a directory path -fn extract_module_name_from_path(path string) string { - dir_parts := path.split('/') - return dir_parts[dir_parts.len - 1] -} - -// Helper function to read a file or return empty string if file doesn't exist -fn read_file_safely(file_path string) string { - return os.read_file(file_path) or { '' } -} - -// Runs the task to generate Rhai wrappers -fn run_wrapper_generation_task(prompt_content string, gen RhaiGen) !string { - // Create a new task - mut task := escalayer.new_task( - name: 'rhai_wrapper_creator.escalayer' - description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file' - ) - - // Create model configs - sonnet_model := escalayer.ModelConfig{ - name: 'anthropic/claude-3.7-sonnet' - provider: 'anthropic' - temperature: 0.7 - max_tokens: 25000 - } - - gpt4_model := escalayer.ModelConfig{ - name: 'gpt-4' - provider: 'openai' - temperature: 0.7 - max_tokens: 25000 - } - - // Create a prompt function that returns the prepared content - prompt_function := fn [prompt_content] (input string) string { - return prompt_content - } - - // Define a single unit task that handles everything - task.new_unit_task( - name: 'create_rhai_wrappers' - prompt_function: prompt_function - callback_function: gen.process_rhai_wrappers - base_model: sonnet_model - retry_model: gpt4_model - retry_count: 1 - ) - - // Initiate the task - return task.initiate('') -} - -// Define a Rhai wrapper generator function for Container functions -fn create_rhai_wrappers(name string, source_code string, example_rhai string, wrapper_md string, errors_md string, crate_path string) string { - // Load all required template and guide files - guides := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping_classicai.md') - engine := $tmpl('./prompts/engine.md') - vector_vs_array := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_array_vs_vector.md') - rhai_integration_fixes := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_integration_fixes.md') - rhai_syntax_guide := load_guide_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_syntax_guide.md') - generic_wrapper_rs := $tmpl('./templates/generic_wrapper.rs') - - // Build the prompt content - return build_prompt_content(name, source_code, example_rhai, wrapper_md, errors_md, - guides, vector_vs_array, rhai_integration_fixes, rhai_syntax_guide, generic_wrapper_rs, - engine) -} - -// Helper function to load guide files with error handling -fn load_guide_file(path string) string { - return os.read_file(path) or { - eprintln('Warning: Failed to read guide file: ${path}') - return '' - } -} - -// Builds the prompt content for the AI -fn build_prompt_content(name string, source_code string, example_rhai string, wrapper_md string, - errors_md string, guides string, vector_vs_array string, - rhai_integration_fixes string, rhai_syntax_guide string, - generic_wrapper_rs string, engine string) string { - return 'You are a Rust developer tasked with creating Rhai wrappers for Rust functions. Please review the following best practices for Rhai wrappers and then create the necessary files. -${guides} -${vector_vs_array} -${example_rhai} -${wrapper_md} - -## Common Errors to Avoid -${errors_md} -${rhai_integration_fixes} -${rhai_syntax_guide} - -## Your Task - -Please create a wrapper.rs file that implements Rhai wrappers for the provided Rust code, and an example.rhai script that demonstrates how to use these wrappers: - -## Rust Code to Wrap - -```rust -${source_code} -``` - -IMPORTANT NOTES: -1. For Rhai imports, use: `use rhai::{Engine, EvalAltResult, plugin::*, Dynamic, Map, Array};` - only import what you actually use -2. The following dependencies are available in Cargo.toml: - - rhai = "1.21.0" - - serde = { version = "1.0", features = ["derive"] } - - serde_json = "1.0" - - sal = { path = "../../../" } - -3. For the wrapper: `use sal::${name};` this way you can access the module functions and objects with ${name}:: - -4. The generic_wrapper.rs file will be hardcoded into the package, you can use code from there. - -```rust -${generic_wrapper_rs} -``` - -5. IMPORTANT: Prefer strongly typed return values over Dynamic types whenever possible. Only use Dynamic when absolutely necessary. - - For example, return `Result>` instead of `Dynamic` when a function returns a string - - Use `Result>` instead of `Dynamic` when a function returns a boolean - - Use `Result, Box>` instead of `Dynamic` when a function returns a list of strings - -6. Your code should include public functions that can be called from Rhai scripts - -7. Make sure to implement all necessary helper functions for type conversion - -8. DO NOT use the #[rhai_fn] attribute - functions will be registered directly in the engine - -9. Make sure to handle string type consistency - use String::from() for string literals when returning in match arms with format!() strings - -10. When returning path references, convert them to owned strings (e.g., path().to_string()) - -11. For error handling, use proper Result types with Box for the error type: - ```rust - // INCORRECT: - pub fn some_function(arg: &str) -> Dynamic { - match some_operation(arg) { - Ok(result) => Dynamic::from(result), - Err(err) => Dynamic::from(format!("Error: {}", err)) - } - } - - // CORRECT: - pub fn some_function(arg: &str) -> Result> { - some_operation(arg).map_err(|err| { - Box::new(EvalAltResult::ErrorRuntime( - format!("Error: {}", err).into(), - rhai::Position::NONE - )) - }) - } - ``` - -12. IMPORTANT: Format your response with the code between triple backticks as follows: - -```rust -// wrapper.rs -// Your wrapper implementation here -``` - -```rust -// engine.rs -// Your engine.rs implementation here -``` - -```rhai -// example.rhai -// Your example Rhai script here -``` - -13. The example.rhai script should demonstrate the use of all the wrapper functions you create - -14. The engine.rs file should contain a register_module function that registers all the wrapper functions and types with the Rhai engine, and a create function. For example: - -${engine} - -MOST IMPORTANT: -import package being wrapped as `use sal::` -your engine create function is called `create_rhai_engine` - -``` -' -} - -@[params] -pub struct WrapperModule { -pub: - lib_rs string - example_rs string - engine_rs string - cargo_toml string - example_rhai string - generic_wrapper_rs string - wrapper_rs string -} - -// functions is a list of function names that AI should extract and pass in -fn create_wrapper_module(wrapper WrapperModule, functions []string, name_ string, base_dir string) !string { - // Define project directory paths - name := name_ - project_dir := '${base_dir}/rhai' - - // Create the project using cargo new --lib - if os.exists(project_dir) { - os.rmdir_all(project_dir) or { - return error('Failed to clean existing project directory: ${err}') - } - } - - // Run cargo new --lib to create the project - os.chdir(base_dir) or { return error('Failed to change directory to base directory: ${err}') } - - cargo_new_result := os.execute('cargo new --lib rhai') - if cargo_new_result.exit_code != 0 { - return error('Failed to create new library project: ${cargo_new_result.output}') - } - - // Create examples directory - examples_dir := '${project_dir}/examples' - os.mkdir_all(examples_dir) or { return error('Failed to create examples directory: ${err}') } - - // Write the lib.rs file - if wrapper.lib_rs != '' { - os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or { - return error('Failed to write lib.rs: ${err}') - } - } - - // Write the wrapper.rs file - if wrapper.wrapper_rs != '' { - os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or { - return error('Failed to write wrapper.rs: ${err}') - } - } - - // Write the generic wrapper.rs file - if wrapper.generic_wrapper_rs != '' { - os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or { - return error('Failed to write generic wrapper.rs: ${err}') - } - } - - // Write the example.rs file - if wrapper.example_rs != '' { - os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or { - return error('Failed to write example.rs: ${err}') - } - } - - // Write the engine.rs file if provided - if wrapper.engine_rs != '' { - os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or { - return error('Failed to write engine.rs: ${err}') - } - } - - // Write the Cargo.toml file - if wrapper.cargo_toml != '' { - os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or { - return error('Failed to write Cargo.toml: ${err}') - } - } - - // Write the example.rhai file if provided - if wrapper.example_rhai != '' { - os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or { - return error('Failed to write example.rhai: ${err}') - } - } - - return project_dir -} - -// Helper function to extract code blocks from the response -fn extract_code_block(response string, identifier string, language string) string { - // Find the start marker for the code block - mut start_marker := '```${language}\n// ${identifier}' - if language == '' { - start_marker = '```\n// ${identifier}' - } - - start_index := response.index(start_marker) or { - // Try alternative format - mut alt_marker := '```${language}\n${identifier}' - if language == '' { - alt_marker = '```\n${identifier}' - } - - response.index(alt_marker) or { return '' } - } - - // Find the end marker - end_marker := '```' - end_index := response.index_after(end_marker, start_index + start_marker.len) or { return '' } - - // Extract the content between the markers - content_start := start_index + start_marker.len - content := response[content_start..end_index].trim_space() - - return content -} - -// Extract module name from wrapper code -fn extract_module_name(code string) string { - lines := code.split('\n') - - for line in lines { - // Look for pub mod or mod declarations - if line.contains('pub mod ') || line.contains('mod ') { - // Extract module name - mut parts := []string{} - if line.contains('pub mod ') { - parts = line.split('pub mod ') - } else { - parts = line.split('mod ') - } - - if parts.len > 1 { - // Extract the module name and remove any trailing characters - mut name := parts[1].trim_space() - // Remove any trailing { or ; or whitespace - name = name.trim_right('{').trim_right(';').trim_space() - if name != '' { - return name - } - } - } - } - - return '' -} - -// RhaiGen struct for generating Rhai wrappers -struct RhaiGen { - name string - dir string -} - -// Process the AI response and compile the generated code -fn (gen RhaiGen) process_rhai_wrappers(response string) !string { - // Extract code blocks from the response - code_blocks := extract_code_blocks(response) or { return err } - - // Extract function names from the wrapper.rs content - functions := extract_functions_from_code(code_blocks.wrapper_rs) - - println('Using module name: ${gen.name}_rhai') - println('Extracted functions: ${functions.join(', ')}') - - name := gen.name - - // Create a WrapperModule struct with the extracted content - wrapper := WrapperModule{ - lib_rs: $tmpl('./templates/lib.rs') - wrapper_rs: code_blocks.wrapper_rs - example_rs: $tmpl('./templates/example.rs') - engine_rs: code_blocks.engine_rs - generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs') - cargo_toml: $tmpl('./templates/cargo.toml') - example_rhai: code_blocks.example_rhai - } - - // Create the wrapper module - project_dir := create_wrapper_module(wrapper, functions, gen.name, gen.dir) or { - return error('Failed to create wrapper module: ${err}') - } - - // Build and run the project - build_output, run_output := build_and_run_project(project_dir) or { return err } - - return format_success_message(project_dir, build_output, run_output) -} - -// CodeBlocks struct to hold extracted code blocks -struct CodeBlocks { - wrapper_rs string - engine_rs string - example_rhai string -} - -// Extract code blocks from the AI response -fn extract_code_blocks(response string) !CodeBlocks { - // Extract wrapper.rs content - wrapper_rs_content := extract_code_block(response, 'wrapper.rs', 'rust') - if wrapper_rs_content == '' { - return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```') - } - - // Extract engine.rs content - mut engine_rs_content := extract_code_block(response, 'engine.rs', 'rust') - if engine_rs_content == '' { - // Try to extract from the response without explicit language marker - engine_rs_content = extract_code_block(response, 'engine.rs', '') - } - - // Extract example.rhai content - mut example_rhai_content := extract_code_block(response, 'example.rhai', 'rhai') - if example_rhai_content == '' { - // Try to extract from the response without explicit language marker - example_rhai_content = extract_code_block(response, 'example.rhai', '') - if example_rhai_content == '' { - // Use the example from the template - example_rhai_content = load_example_from_template() or { return err } - } - } - - return CodeBlocks{ - wrapper_rs: wrapper_rs_content - engine_rs: engine_rs_content - example_rhai: example_rhai_content - } -} - -// Load example.rhai from template file -fn load_example_from_template() !string { - example_script_md := os.read_file('${os.dir(@FILE)}/prompts/example_script.md') or { - return error('Failed to read example.rhai template: ${err}') - } - - // Extract the code block from the markdown file - example_rhai_content := extract_code_block(example_script_md, 'example.rhai', 'rhai') - if example_rhai_content == '' { - return error('Failed to extract example.rhai from template file') - } - - return example_rhai_content -} - -// Build and run the project -fn build_and_run_project(project_dir string) !(string, string) { - // Change to the project directory - os.chdir(project_dir) or { return error('Failed to change directory to project: ${err}') } - - // Run cargo build first - build_result := os.execute('cargo build') - if build_result.exit_code != 0 { - return error('Compilation failed. Please fix the following errors and ensure your code is compatible with the existing codebase:\n\n${build_result.output}') - } - - // Run the example - run_result := os.execute('cargo run --example example') - - return build_result.output, run_result.output -} - -// Format success message -fn format_success_message(project_dir string, build_output string, run_output string) string { - return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_output}\n\nRun output:\n${run_output}' -} - -// Extract function names from wrapper code -fn extract_functions_from_code(code string) []string { - mut functions := []string{} - lines := code.split('\n') - - for line in lines { - if line.contains('pub fn ') && !line.contains('//') { - // Extract function name - parts := line.split('pub fn ') - if parts.len > 1 { - name_parts := parts[1].split('(') - if name_parts.len > 0 { - fn_name := name_parts[0].trim_space() - if fn_name != '' { - functions << fn_name - } - } - } - } - } - - return functions -} diff --git a/libarchive/mcp_rhai/logic/logic.v b/libarchive/mcp_rhai/logic/logic.v deleted file mode 100644 index 554e1098..00000000 --- a/libarchive/mcp_rhai/logic/logic.v +++ /dev/null @@ -1,284 +0,0 @@ -module logic - -import incubaid.herolib.ai.escalayer -import incubaid.herolib.lang.rust -import incubaid.herolib.develop.codetools.utils as ai_utils -import os - -pub fn generate_rhai_wrapper(name string, source_path string) !string { - // Detect source package and module information - source_pkg_info := rust.detect_source_package(source_path)! - source_code := rust.read_source_code(source_path)! - prompt := rhai_wrapper_generation_prompt(name, source_code, source_pkg_info)! - return run_wrapper_generation_task(prompt, RhaiGen{ - name: name - dir: source_path - source_pkg_info: source_pkg_info - })! -} - -// Runs the task to generate Rhai wrappers -pub fn run_wrapper_generation_task(prompt_content string, gen RhaiGen) !string { - // Create a new task - mut task := escalayer.new_task( - name: 'rhai_wrapper_creator.escalayer' - description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file' - ) - - // Create model configs - sonnet_model := escalayer.ModelConfig{ - name: 'anthropic/claude-3.7-sonnet' - provider: 'anthropic' - temperature: 0.7 - max_tokens: 25000 - } - - gpt4_model := escalayer.ModelConfig{ - name: 'gpt-4' - provider: 'openai' - temperature: 0.7 - max_tokens: 25000 - } - - // Create a prompt function that returns the prepared content - prompt_function := fn [prompt_content] (input string) string { - return prompt_content - } - - // Define a single unit task that handles everything - task.new_unit_task( - name: 'create_rhai_wrappers' - prompt_function: prompt_function - callback_function: gen.process_rhai_wrappers - base_model: sonnet_model - retry_model: gpt4_model - retry_count: 1 - ) - - // Initiate the task - return task.initiate('') -} - -// Define a Rhai wrapper generator function for Container functions -pub fn rhai_wrapper_generation_prompt(name string, source_code string, source_pkg_info rust.SourcePackageInfo) !string { - current_dir := os.dir(@FILE) - example_rhai := os.read_file('${current_dir}/prompts/example_script.md')! - wrapper_md := os.read_file('${current_dir}/prompts/wrapper.md')! - errors_md := os.read_file('${current_dir}/prompts/errors.md')! - - // Load all required template and guide files - guides := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhaiwrapping_classicai.md')! - engine := $tmpl('./prompts/engine.md') - vector_vs_array := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_array_vs_vector.md')! - rhai_integration_fixes := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_integration_fixes.md')! - rhai_syntax_guide := os.read_file('/Users/timurgordon/code/git.threefold.info/herocode/sal/aiprompts/rhai_syntax_guide.md')! - generic_wrapper_rs := $tmpl('./templates/generic_wrapper.rs') - - prompt := $tmpl('./prompts/main.md') - return prompt -} - -@[params] -pub struct WrapperModule { -pub: - lib_rs string - example_rs string - engine_rs string - cargo_toml string - example_rhai string - generic_wrapper_rs string - wrapper_rs string -} - -// functions is a list of function names that AI should extract and pass in -pub fn write_rhai_wrapper_module(wrapper WrapperModule, name string, path string) !string { - // Define project directory paths - project_dir := '${path}/rhai' - - // Create the project using cargo new --lib - if os.exists(project_dir) { - os.rmdir_all(project_dir) or { - return error('Failed to clean existing project directory: ${err}') - } - } - - // Run cargo new --lib to create the project - os.chdir(path) or { return error('Failed to change directory to base directory: ${err}') } - - cargo_new_result := os.execute('cargo new --lib rhai') - if cargo_new_result.exit_code != 0 { - return error('Failed to create new library project: ${cargo_new_result.output}') - } - - // Create examples directory - examples_dir := '${project_dir}/examples' - os.mkdir_all(examples_dir) or { return error('Failed to create examples directory: ${err}') } - - // Write the lib.rs file - if wrapper.lib_rs != '' { - os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or { - return error('Failed to write lib.rs: ${err}') - } - } else { - // Use default lib.rs template if none provided - lib_rs_content := $tmpl('./templates/lib.rs') - os.write_file('${project_dir}/src/lib.rs', lib_rs_content) or { - return error('Failed to write lib.rs: ${err}') - } - } - - // Write the wrapper.rs file - if wrapper.wrapper_rs != '' { - os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or { - return error('Failed to write wrapper.rs: ${err}') - } - } - - // Write the generic wrapper.rs file - if wrapper.generic_wrapper_rs != '' { - os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or { - return error('Failed to write generic wrapper.rs: ${err}') - } - } - - // Write the example.rs file - if wrapper.example_rs != '' { - os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or { - return error('Failed to write example.rs: ${err}') - } - } else { - // Use default example.rs template if none provided - example_rs_content := $tmpl('./templates/example.rs') - os.write_file('${examples_dir}/example.rs', example_rs_content) or { - return error('Failed to write example.rs: ${err}') - } - } - - // Write the engine.rs file if provided - if wrapper.engine_rs != '' { - os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or { - return error('Failed to write engine.rs: ${err}') - } - } - - // Write the Cargo.toml file - os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or { - return error('Failed to write Cargo.toml: ${err}') - } - - // Write the example.rhai file - os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or { - return error('Failed to write example.rhai: ${err}') - } - - return project_dir -} - -// Extract module name from wrapper code -fn extract_module_name(code string) string { - lines := code.split('\n') - - for line in lines { - // Look for pub mod or mod declarations - if line.contains('pub mod ') || line.contains('mod ') { - // Extract module name - mut parts := []string{} - if line.contains('pub mod ') { - parts = line.split('pub mod ') - } else { - parts = line.split('mod ') - } - - if parts.len > 1 { - // Extract the module name and remove any trailing characters - mut name := parts[1].trim_space() - // Remove any trailing { or ; or whitespace - name = name.trim_right('{').trim_right(';').trim_space() - if name != '' { - return name - } - } - } - } - - return '' -} - -// RhaiGen struct for generating Rhai wrappers -struct RhaiGen { - name string - dir string - source_pkg_info rust.SourcePackageInfo -} - -// Process the AI response and compile the generated code -pub fn (gen RhaiGen) process_rhai_wrappers(input string) !string { - blocks := extract_code_blocks(input)! - source_pkg_info := gen.source_pkg_info - // Create the module structure - mod := WrapperModule{ - lib_rs: blocks.lib_rs - engine_rs: blocks.engine_rs - example_rhai: blocks.example_rhai - generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs') - wrapper_rs: blocks.wrapper_rs - } - - // Write the module files - project_dir := write_rhai_wrapper_module(mod, gen.name, gen.dir)! - - return project_dir -} - -// CodeBlocks struct to hold extracted code blocks -struct CodeBlocks { - wrapper_rs string - engine_rs string - example_rhai string - lib_rs string -} - -// Extract code blocks from the AI response -fn extract_code_blocks(response string) !CodeBlocks { - // Extract wrapper.rs content - wrapper_rs_content := ai_utils.extract_code_block(response, 'wrapper.rs', 'rust') - if wrapper_rs_content == '' { - return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```') - } - - // Extract engine.rs content - mut engine_rs_content := ai_utils.extract_code_block(response, 'engine.rs', 'rust') - if engine_rs_content == '' { - // Try to extract from the response without explicit language marker - engine_rs_content = ai_utils.extract_code_block(response, 'engine.rs', '') - } - - // Extract example.rhai content - mut example_rhai_content := ai_utils.extract_code_block(response, 'example.rhai', - 'rhai') - if example_rhai_content == '' { - // Try to extract from the response without explicit language marker - example_rhai_content = ai_utils.extract_code_block(response, 'example.rhai', '') - if example_rhai_content == '' { - return error('Failed to extract example.rhai content from response. Please ensure your code is properly formatted inside a code block that starts with ```rhai\n// example.rhai and ends with ```') - } - } - - // Extract lib.rs content - lib_rs_content := ai_utils.extract_code_block(response, 'lib.rs', 'rust') - if lib_rs_content == '' { - return error('Failed to extract lib.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// lib.rs and ends with ```') - } - - return CodeBlocks{ - wrapper_rs: wrapper_rs_content - engine_rs: engine_rs_content - example_rhai: example_rhai_content - lib_rs: lib_rs_content - } -} - -// Format success message -fn format_success_message(project_dir string, build_output string, run_output string) string { - return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_output}\n\nRun output:\n${run_output}' -} diff --git a/libarchive/mcp_rhai/logic/logic_sampling.v b/libarchive/mcp_rhai/logic/logic_sampling.v deleted file mode 100644 index ac802dd6..00000000 --- a/libarchive/mcp_rhai/logic/logic_sampling.v +++ /dev/null @@ -1,258 +0,0 @@ -module logic - -import incubaid.herolib.ai.escalayer -import incubaid.herolib.lang.rust -import incubaid.herolib.develop.codetools.utils as ai_utils -import os - -// pub fn generate_rhai_wrapper_sampling(name string, source_path string) !string { -// prompt := rhai_wrapper_generation_prompt(name, source_path) or {panic(err)} -// return run_wrapper_generation_task_sampling(prompt, RhaiGen{ -// name: name -// dir: source_path -// }) or {panic(err)} -// } - -// // Runs the task to generate Rhai wrappers -// pub fn run_wrapper_generation_task_sampling(prompt_content string, gen RhaiGen) !string { -// // Create a new task -// mut task := escalayer.new_task( -// name: 'rhai_wrapper_creator.escalayer' -// description: 'Create Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file' -// ) - -// // Create model configs -// sonnet_model := escalayer.ModelConfig{ -// name: 'anthropic/claude-3.7-sonnet' -// provider: 'anthropic' -// temperature: 0.7 -// max_tokens: 25000 -// } - -// gpt4_model := escalayer.ModelConfig{ -// name: 'gpt-4' -// provider: 'openai' -// temperature: 0.7 -// max_tokens: 25000 -// } - -// // Create a prompt function that returns the prepared content -// prompt_function := fn [prompt_content] (input string) string { -// return prompt_content -// } - -// // Define a single unit task that handles everything -// task.new_unit_task( -// name: 'create_rhai_wrappers' -// prompt_function: prompt_function -// callback_function: gen.process_rhai_wrappers -// base_model: sonnet_model -// retry_model: gpt4_model -// retry_count: 1 -// ) - -// // Initiate the task -// return task.initiate('') -// } - -// @[params] -// pub struct WrapperModule { -// pub: -// lib_rs string -// example_rs string -// engine_rs string -// cargo_toml string -// example_rhai string -// generic_wrapper_rs string -// wrapper_rs string -// } - -// // functions is a list of function names that AI should extract and pass in -// pub fn write_rhai_wrapper_module(wrapper WrapperModule, name string, path string)! string { - -// // Define project directory paths -// project_dir := '${path}/rhai' - -// // Create the project using cargo new --lib -// if os.exists(project_dir) { -// os.rmdir_all(project_dir) or { -// return error('Failed to clean existing project directory: ${err}') -// } -// } - -// // Run cargo new --lib to create the project -// os.chdir(path) or { -// return error('Failed to change directory to base directory: ${err}') -// } - -// cargo_new_result := os.execute('cargo new --lib rhai') -// if cargo_new_result.exit_code != 0 { -// return error('Failed to create new library project: ${cargo_new_result.output}') -// } - -// // Create examples directory -// examples_dir := '${project_dir}/examples' -// os.mkdir_all(examples_dir) or { -// return error('Failed to create examples directory: ${err}') -// } - -// // Write the lib.rs file -// if wrapper.lib_rs != '' { -// os.write_file('${project_dir}/src/lib.rs', wrapper.lib_rs) or { -// return error('Failed to write lib.rs: ${err}') -// } -// } - -// // Write the wrapper.rs file -// if wrapper.wrapper_rs != '' { -// os.write_file('${project_dir}/src/wrapper.rs', wrapper.wrapper_rs) or { -// return error('Failed to write wrapper.rs: ${err}') -// } -// } - -// // Write the generic wrapper.rs file -// if wrapper.generic_wrapper_rs != '' { -// os.write_file('${project_dir}/src/generic_wrapper.rs', wrapper.generic_wrapper_rs) or { -// return error('Failed to write generic wrapper.rs: ${err}') -// } -// } - -// // Write the example.rs file -// if wrapper.example_rs != '' { -// os.write_file('${examples_dir}/example.rs', wrapper.example_rs) or { -// return error('Failed to write example.rs: ${err}') -// } -// } - -// // Write the engine.rs file if provided -// if wrapper.engine_rs != '' { -// os.write_file('${project_dir}/src/engine.rs', wrapper.engine_rs) or { -// return error('Failed to write engine.rs: ${err}') -// } -// } - -// // Write the Cargo.toml file -// os.write_file('${project_dir}/Cargo.toml', wrapper.cargo_toml) or { -// return error('Failed to write Cargo.toml: ${err}') -// } - -// // Write the example.rhai file -// os.write_file('${examples_dir}/example.rhai', wrapper.example_rhai) or { -// return error('Failed to write example.rhai: ${err}') -// } - -// return project_dir -// } - -// // Extract module name from wrapper code -// fn extract_module_name(code string) string { -// lines := code.split('\n') - -// for line in lines { -// // Look for pub mod or mod declarations -// if line.contains('pub mod ') || line.contains('mod ') { -// // Extract module name -// mut parts := []string{} -// if line.contains('pub mod ') { -// parts = line.split('pub mod ') -// } else { -// parts = line.split('mod ') -// } - -// if parts.len > 1 { -// // Extract the module name and remove any trailing characters -// mut name := parts[1].trim_space() -// // Remove any trailing { or ; or whitespace -// name = name.trim_right('{').trim_right(';').trim_space() -// if name != '' { -// return name -// } -// } -// } -// } - -// return '' -// } - -// // RhaiGen struct for generating Rhai wrappers -// struct RhaiGen { -// name string -// dir string -// } - -// // Process the AI response and compile the generated code -// fn (gen RhaiGen) process_rhai_wrappers(response string)! string { -// // Extract code blocks from the response -// code_blocks := extract_code_blocks(response) or { -// return err -// } - -// name := gen.name - -// // Create a WrapperModule struct with the extracted content -// wrapper := WrapperModule{ -// lib_rs: $tmpl('./templates/lib.rs') -// wrapper_rs: code_blocks.wrapper_rs -// example_rs: $tmpl('./templates/example.rs') -// engine_rs: code_blocks.engine_rs -// generic_wrapper_rs: $tmpl('./templates/generic_wrapper.rs') -// cargo_toml: $tmpl('./templates/cargo.toml') -// example_rhai: code_blocks.example_rhai -// } - -// // Create the wrapper module -// project_dir := write_rhai_wrapper_module(wrapper, gen.name, gen.dir) or { -// return error('Failed to create wrapper module: ${err}') -// } - -// // Build and run the project -// build_output, run_output := rust.run_example(project_dir, 'example') or { -// return err -// } - -// return format_success_message(project_dir, build_output, run_output) -// } - -// // CodeBlocks struct to hold extracted code blocks -// struct CodeBlocks { -// wrapper_rs string -// engine_rs string -// example_rhai string -// } - -// // Extract code blocks from the AI response -// fn extract_code_blocks(response string)! CodeBlocks { -// // Extract wrapper.rs content -// wrapper_rs_content := ai_utils.extract_code_block(response, 'wrapper.rs', 'rust') -// if wrapper_rs_content == '' { -// return error('Failed to extract wrapper.rs content from response. Please ensure your code is properly formatted inside a code block that starts with ```rust\n// wrapper.rs and ends with ```') -// } - -// // Extract engine.rs content -// mut engine_rs_content := ai_utils.extract_code_block(response, 'engine.rs', 'rust') -// if engine_rs_content == '' { -// // Try to extract from the response without explicit language marker -// engine_rs_content = ai_utils.extract_code_block(response, 'engine.rs', '') -// } - -// // Extract example.rhai content -// mut example_rhai_content := ai_utils.extract_code_block(response, 'example.rhai', 'rhai') -// if example_rhai_content == '' { -// // Try to extract from the response without explicit language marker -// example_rhai_content = ai_utils.extract_code_block(response, 'example.rhai', '') -// if example_rhai_content == '' { -// return error('Failed to extract example.rhai content from response. Please ensure your code is properly formatted inside a code block that starts with ```rhai\n// example.rhai and ends with ```') -// } -// } - -// return CodeBlocks{ -// wrapper_rs: wrapper_rs_content -// engine_rs: engine_rs_content -// example_rhai: example_rhai_content -// } -// } - -// // Format success message -// fn format_success_message(project_dir string, build_output string, run_output string) string { -// return 'Successfully generated Rhai wrappers and ran the example!\n\nProject created at: ${project_dir}\n\nBuild output:\n${build_output}\n\nRun output:\n${run_output}' -// } diff --git a/libarchive/mcp_rhai/logic/prompts/engine.md b/libarchive/mcp_rhai/logic/prompts/engine.md deleted file mode 100644 index cbf9370b..00000000 --- a/libarchive/mcp_rhai/logic/prompts/engine.md +++ /dev/null @@ -1,125 +0,0 @@ - -# Engine - -Here is an example of a well-implemented Rhai engine for the Git module: - -## Example engine - -```rust -// engine.rs - -/// Register Nerdctl module functions with the Rhai engine -pub fn create_rhai_engine() -> Engine { - let mut engine = Engine::new(); - - register_nerdctl_module(&mut engine)?; - register_nerdctl_types(&mut engine)?; - - engine -} - -pub fn register_nerdctl_module(engine: &mut Engine) -> Result<(), Box> { - // Register Container constructor - engine.register_fn("nerdctl_container_new", container_new); - engine.register_fn("nerdctl_container_from_image", container_from_image); - - // Register Container instance methods - engine.register_fn("reset", container_reset); - engine.register_fn("with_port", container_with_port); - engine.register_fn("with_volume", container_with_volume); - engine.register_fn("with_env", container_with_env); - engine.register_fn("with_network", container_with_network); - engine.register_fn("with_network_alias", container_with_network_alias); - engine.register_fn("with_cpu_limit", container_with_cpu_limit); - engine.register_fn("with_memory_limit", container_with_memory_limit); - engine.register_fn("with_restart_policy", container_with_restart_policy); - engine.register_fn("with_health_check", container_with_health_check); - engine.register_fn("with_ports", container_with_ports); - engine.register_fn("with_volumes", container_with_volumes); - engine.register_fn("with_envs", container_with_envs); - engine.register_fn("with_network_aliases", container_with_network_aliases); - engine.register_fn("with_memory_swap_limit", container_with_memory_swap_limit); - engine.register_fn("with_cpu_shares", container_with_cpu_shares); - engine.register_fn("with_health_check_options", container_with_health_check_options); - engine.register_fn("with_snapshotter", container_with_snapshotter); - engine.register_fn("with_detach", container_with_detach); - engine.register_fn("build", container_build); - engine.register_fn("start", container_start); - engine.register_fn("stop", container_stop); - engine.register_fn("remove", container_remove); - engine.register_fn("exec", container_exec); - engine.register_fn("logs", container_logs); - engine.register_fn("copy", container_copy); - - // Register legacy container functions (for backward compatibility) - engine.register_fn("nerdctl_run", nerdctl_run); - engine.register_fn("nerdctl_run_with_name", nerdctl_run_with_name); - engine.register_fn("nerdctl_run_with_port", nerdctl_run_with_port); - engine.register_fn("new_run_options", new_run_options); - engine.register_fn("nerdctl_exec", nerdctl_exec); - engine.register_fn("nerdctl_copy", nerdctl_copy); - engine.register_fn("nerdctl_stop", nerdctl_stop); - engine.register_fn("nerdctl_remove", nerdctl_remove); - engine.register_fn("nerdctl_list", nerdctl_list); - engine.register_fn("nerdctl_logs", nerdctl_logs); - - // Register image functions - engine.register_fn("nerdctl_images", nerdctl_images); - engine.register_fn("nerdctl_image_remove", nerdctl_image_remove); - engine.register_fn("nerdctl_image_push", nerdctl_image_push); - engine.register_fn("nerdctl_image_tag", nerdctl_image_tag); - engine.register_fn("nerdctl_image_pull", nerdctl_image_pull); - engine.register_fn("nerdctl_image_commit", nerdctl_image_commit); - engine.register_fn("nerdctl_image_build", nerdctl_image_build); - - Ok(()) -} - -/// Register Nerdctl module types with the Rhai engine -fn register_nerdctl_types(engine: &mut Engine) -> Result<(), Box> { - // Register Container type - engine.register_type_with_name::("NerdctlContainer"); - - // Register getters for Container properties - engine.register_get("name", |container: &mut Container| container.name.clone()); - engine.register_get("container_id", |container: &mut Container| { - match &container.container_id { - Some(id) => id.clone(), - None => "".to_string(), - } - }); - engine.register_get("image", |container: &mut Container| { - match &container.image { - Some(img) => img.clone(), - None => "".to_string(), - } - }); - engine.register_get("ports", |container: &mut Container| { - let mut array = Array::new(); - for port in &container.ports { - array.push(Dynamic::from(port.clone())); - } - array - }); - engine.register_get("volumes", |container: &mut Container| { - let mut array = Array::new(); - for volume in &container.volumes { - array.push(Dynamic::from(volume.clone())); - } - array - }); - engine.register_get("detach", |container: &mut Container| container.detach); - - // Register Image type and methods - engine.register_type_with_name::("NerdctlImage"); - - // Register getters for Image properties - engine.register_get("id", |img: &mut Image| img.id.clone()); - engine.register_get("repository", |img: &mut Image| img.repository.clone()); - engine.register_get("tag", |img: &mut Image| img.tag.clone()); - engine.register_get("size", |img: &mut Image| img.size.clone()); - engine.register_get("created", |img: &mut Image| img.created.clone()); - - Ok(()) -} -``` \ No newline at end of file diff --git a/libarchive/mcp_rhai/logic/prompts/errors.md b/libarchive/mcp_rhai/logic/prompts/errors.md deleted file mode 100644 index d80587fe..00000000 --- a/libarchive/mcp_rhai/logic/prompts/errors.md +++ /dev/null @@ -1,186 +0,0 @@ -# Common Errors in Rhai Wrappers and How to Fix Them - -When creating Rhai wrappers for Rust functions, you might encounter several common errors. Here's how to address them: - -## 1. `rhai_fn` Attribute Errors - -``` -error: cannot find attribute `rhai_fn` in this scope -``` - -**Solution**: Do not use the `#[rhai_fn]` attribute. Instead, register functions directly in the engine: - -```rust -// INCORRECT: -#[rhai_fn(name = "pull_repository")] -pub fn pull_repository(repo: &mut GitRepo) -> Dynamic { ... } - -// CORRECT: -pub fn pull_repository(repo: &mut GitRepo) -> Dynamic { ... } -// Then register in engine.rs: -engine.register_fn("pull_repository", pull_repository); -``` - -## 2. Function Visibility Errors - -``` -error[E0603]: function `create_rhai_engine` is private -``` - -**Solution**: Make sure to declare functions as `pub` when they need to be accessed from other modules: - -```rust -// INCORRECT: -fn create_rhai_engine() -> Engine { ... } - -// CORRECT: -pub fn create_rhai_engine() -> Engine { ... } -``` - -## 3. Type Errors with String vs &str - -``` -error[E0308]: `match` arms have incompatible types -``` - -**Solution**: Ensure consistent return types in match arms. When one arm returns a string literal (`&str`) and another returns a `String`, convert them to be consistent: - -```rust -// INCORRECT: -match r.pull() { - Ok(_) => "Successfully pulled changes", - Err(err) => { - let error_msg = format!("Error pulling changes: {}", err); - error_msg // This is a String, not matching the &str above - } -} - -// CORRECT - Option 1: Convert &str to String -match r.pull() { - Ok(_) => String::from("Successfully pulled changes"), - Err(err) => format!("Error pulling changes: {}", err) -} - -// CORRECT - Option 2: Use String::from for all string literals -match r.pull() { - Ok(_) => String::from("Successfully pulled changes"), - Err(err) => { - let error_msg = format!("Error pulling changes: {}", err); - error_msg - } -} -``` - -## 4. Lifetime Errors - -``` -error: lifetime may not live long enough -``` - -**Solution**: When returning references from closures, you need to ensure the lifetime is valid. For path operations, convert to owned strings: - -```rust -// INCORRECT: -repo_clone.wrap(|r| r.path()) - -// CORRECT: -repo_clone.wrap(|r| r.path().to_string()) -``` - -## 5. Sized Trait Errors - -``` -error[E0277]: the size for values of type `Self` cannot be known at compilation time -``` - -**Solution**: Add a `Sized` bound to the `Self` type in trait definitions: - -```rust -// INCORRECT: -trait RhaiWrapper { - fn wrap(&self, f: F) -> Dynamic - where - F: FnOnce(Self) -> R, - R: ToRhai; -} - -// CORRECT: -trait RhaiWrapper { - fn wrap(&self, f: F) -> Dynamic - where - F: FnOnce(Self) -> R, - R: ToRhai, - Self: Sized; -} -``` - -## 6. Unused Imports - -``` -warning: unused imports: `Engine`, `EvalAltResult`, `FLOAT`, `INT`, and `plugin::*` -``` - -**Solution**: Remove unused imports to clean up your code: - -```rust -// INCORRECT: -use rhai::{Engine, EvalAltResult, plugin::*, FLOAT, INT, Dynamic, Map, Array}; - -// CORRECT - only keep what you use: -use rhai::{Dynamic, Array}; -``` - -## 7. Overuse of Dynamic Types - -``` -error[E0277]: the trait bound `Vec: generic_wrapper::ToRhai` is not satisfied -``` - -**Solution**: Use proper static typing instead of Dynamic types whenever possible. This improves type safety and makes the code more maintainable: - -```rust -// INCORRECT: Returning Dynamic for everything -pub fn list_repositories(tree: &mut GitTree) -> Dynamic { - let tree_clone = tree.clone(); - tree_clone.wrap(|t| { - match t.list() { - Ok(repos) => repos, - Err(err) => vec![format!("Error listing repositories: {}", err)] - } - }) -} - -// CORRECT: Using proper Result types -pub fn list_repositories(tree: &mut GitTree) -> Result, Box> { - let tree_clone = tree.clone(); - tree_clone.list().map_err(|err| { - Box::new(EvalAltResult::ErrorRuntime( - format!("Error listing repositories: {}", err).into(), - rhai::Position::NONE - )) - }) -} -``` - -## 8. Improper Error Handling - -``` -error[E0277]: the trait bound `for<'a> fn(&'a mut Engine) -> Result<(), Box> {wrapper::register_git_module}: RhaiNativeFunc<_, _, _, _, _>` is not satisfied -``` - -**Solution**: When registering functions that return Result types, make sure they are properly handled: - -```rust -// INCORRECT: Trying to register a function that returns Result<(), Box> -engine.register_fn("register_git_module", wrapper::register_git_module); - -// CORRECT: Wrap the function to handle the Result -engine.register_fn("register_git_module", |engine: &mut Engine| { - match wrapper::register_git_module(engine) { - Ok(_) => Dynamic::from(true), - Err(err) => Dynamic::from(format!("Error: {}", err)) - } -}); -``` - -Remember to adapt these solutions to your specific code context. The key is to maintain type consistency, proper visibility, correct lifetime management, and appropriate static typing. diff --git a/libarchive/mcp_rhai/logic/prompts/example_script.md b/libarchive/mcp_rhai/logic/prompts/example_script.md deleted file mode 100644 index e67422ba..00000000 --- a/libarchive/mcp_rhai/logic/prompts/example_script.md +++ /dev/null @@ -1,40 +0,0 @@ -## Example Rhai Script - -Now, given the source code you wrapped using Rhai executable functions, write an example Rhai script that uses those functions. - -### Example example rhai script - -```rhai -// example.rhai -// Create a new GitTree instance -let git_tree = new_git_tree("/Users/timurgordon/code"); -print("\nCreated GitTree for: /Users/timurgordon/code"); - -// List repositories in the tree -let repos = list_repositories(git_tree); -print("Found " + repos.len() + " repositories"); - -if repos.len() > 0 { - print("First repository: " + repos[0]); - - // Get the repository - let repo_array = get_repositories(git_tree, repos[0]); - - if repo_array.len() > 0 { - let repo = repo_array[0]; - print("\nRepository path: " + path(repo)); - - // Check if the repository has changes - let has_changes = has_changes(repo); - print("Has changes: " + has_changes); - - // Try to pull the repository - print("\nTrying to pull repository..."); - let pull_result = pull_repository(repo); - print("Pull result: " + pull_result); - } -} - -print("\nResult: Git operations completed successfully"); -42 // Return value -``` \ No newline at end of file diff --git a/libarchive/mcp_rhai/logic/prompts/main.md b/libarchive/mcp_rhai/logic/prompts/main.md deleted file mode 100644 index 1f67f838..00000000 --- a/libarchive/mcp_rhai/logic/prompts/main.md +++ /dev/null @@ -1,99 +0,0 @@ -You are a Rust developer tasked with creating Rhai wrappers for Rust functions. Please review the following best practices for Rhai wrappers and then create the necessary files. -@{guides} -@{vector_vs_array} -@{example_rhai} -@{wrapper_md} - -## Common Errors to Avoid -@{errors_md} -@{rhai_integration_fixes} -@{rhai_syntax_guide} - -## Your Task - -Please create a wrapper.rs file that implements Rhai wrappers for the provided Rust code, and an example.rhai script that demonstrates how to use these wrappers: - -## Rust Code to Wrap - -```rust -@{source_code} -``` - -IMPORTANT NOTES: -1. For Rhai imports, use: `use rhai::{Engine, EvalAltResult, plugin::*, Dynamic, Map, Array};` - only import what you actually use -2. The following dependencies are available in Cargo.toml: - - rhai = "1.21.0" - - serde = { version = "1.0", features = ["derive"] } - - serde_json = "1.0" - - @{source_pkg_info.name} = { path = "@{source_pkg_info.path}" } - -3. For the wrapper: `use @{source_pkg_info.name}::@{source_pkg_info.module};` this way you can access the module functions and objects with @{source_pkg_info.module}:: - -4. The generic_wrapper.rs file will be hardcoded into the package, you can use code from there. - -```rust -@{generic_wrapper_rs} -``` - -5. IMPORTANT: Prefer strongly typed return values over Dynamic types whenever possible. Only use Dynamic when absolutely necessary. - - For example, return `Result>` instead of `Dynamic` when a function returns a string - - Use `Result>` instead of `Dynamic` when a function returns a boolean - - Use `Result, Box>` instead of `Dynamic` when a function returns a list of strings - -6. Your code should include public functions that can be called from Rhai scripts - -7. Make sure to implement all necessary helper functions for type conversion - -8. DO NOT use the #[rhai_fn] attribute - functions will be registered directly in the engine - -9. Make sure to handle string type consistency - use String::from() for string literals when returning in match arms with format!() strings - -10. When returning path references, convert them to owned strings (e.g., path().to_string()) - -11. For error handling, use proper Result types with Box for the error type: - ```rust - // INCORRECT: - pub fn some_function(arg: &str) -> Dynamic { - match some_operation(arg) { - Ok(result) => Dynamic::from(result), - Err(err) => Dynamic::from(format!("Error: {}", err)) - } - } - - // CORRECT: - pub fn some_function(arg: &str) -> Result> { - some_operation(arg).map_err(|err| { - Box::new(EvalAltResult::ErrorRuntime( - format!("Error: {}", err).into(), - rhai::Position::NONE - )) - }) - } - ``` - -12. IMPORTANT: Format your response with the code between triple backticks as follows: - -```rust -// wrapper.rs -// Your wrapper implementation here -``` - -```rust -// engine.rs -// Your engine.rs implementation here -``` - -```rhai -// example.rhai -// Your example Rhai script here -``` - -13. The example.rhai script should demonstrate the use of all the wrapper functions you create - -14. The engine.rs file should contain a register_module function that registers all the wrapper functions and types with the Rhai engine, and a create function. For example: - -@{engine} - -MOST IMPORTANT: -import package being wrapped as `use @{source_pkg_info.name}::@{source_pkg_info.module}` -your engine create function is called `create_rhai_engine` diff --git a/libarchive/mcp_rhai/logic/prompts/wrapper.md b/libarchive/mcp_rhai/logic/prompts/wrapper.md deleted file mode 100644 index 6bda928b..00000000 --- a/libarchive/mcp_rhai/logic/prompts/wrapper.md +++ /dev/null @@ -1,473 +0,0 @@ - -# Wrapper - -Here is an example of a well-implemented Rhai wrapper for the Git module: - -## Example wrapper - -```rust -// wrapper.rs -//! Rhai wrappers for Nerdctl module functions -//! -//! This module provides Rhai wrappers for the functions in the Nerdctl module. - -use rhai::{Engine, EvalAltResult, Array, Dynamic, Map}; -use crate::virt::nerdctl::{self, NerdctlError, Image, Container}; -use crate::process::CommandResult; - -// Helper functions for error conversion with improved context -fn nerdctl_error_to_rhai_error(result: Result) -> Result> { - result.map_err(|e| { - // Create a more detailed error message based on the error type - let error_message = match &e { - NerdctlError::CommandExecutionFailed(io_err) => { - format!("Failed to execute nerdctl command: {}. This may indicate nerdctl is not installed or not in PATH.", io_err) - }, - NerdctlError::CommandFailed(msg) => { - format!("Nerdctl command failed: {}. Check container status and logs for more details.", msg) - }, - NerdctlError::JsonParseError(msg) => { - format!("Failed to parse nerdctl JSON output: {}. This may indicate an incompatible nerdctl version.", msg) - }, - NerdctlError::ConversionError(msg) => { - format!("Data conversion error: {}. This may indicate unexpected output format from nerdctl.", msg) - }, - NerdctlError::Other(msg) => { - format!("Nerdctl error: {}. This is an unexpected error.", msg) - }, - }; - - Box::new(EvalAltResult::ErrorRuntime( - error_message.into(), - rhai::Position::NONE - )) - }) -} - -// -// Container Builder Pattern Implementation -// - -/// Create a new Container -pub fn container_new(name: &str) -> Result> { - nerdctl_error_to_rhai_error(Container::new(name)) -} - -/// Create a Container from an image -pub fn container_from_image(name: &str, image: &str) -> Result> { - nerdctl_error_to_rhai_error(Container::from_image(name, image)) -} - -/// Reset the container configuration to defaults while keeping the name and image -pub fn container_reset(container: Container) -> Container { - container.reset() -} - -/// Add a port mapping to a Container -pub fn container_with_port(container: Container, port: &str) -> Container { - container.with_port(port) -} - -/// Add a volume mount to a Container -pub fn container_with_volume(container: Container, volume: &str) -> Container { - container.with_volume(volume) -} - -/// Add an environment variable to a Container -pub fn container_with_env(container: Container, key: &str, value: &str) -> Container { - container.with_env(key, value) -} - -/// Set the network for a Container -pub fn container_with_network(container: Container, network: &str) -> Container { - container.with_network(network) -} - -/// Add a network alias to a Container -pub fn container_with_network_alias(container: Container, alias: &str) -> Container { - container.with_network_alias(alias) -} - -/// Set CPU limit for a Container -pub fn container_with_cpu_limit(container: Container, cpus: &str) -> Container { - container.with_cpu_limit(cpus) -} - -/// Set memory limit for a Container -pub fn container_with_memory_limit(container: Container, memory: &str) -> Container { - container.with_memory_limit(memory) -} - -/// Set restart policy for a Container -pub fn container_with_restart_policy(container: Container, policy: &str) -> Container { - container.with_restart_policy(policy) -} - -/// Set health check for a Container -pub fn container_with_health_check(container: Container, cmd: &str) -> Container { - container.with_health_check(cmd) -} - -/// Add multiple port mappings to a Container -pub fn container_with_ports(mut container: Container, ports: Array) -> Container { - for port in ports.iter() { - if port.is_string() { - let port_str = port.clone().cast::(); - container = container.with_port(&port_str); - } - } - container -} - -/// Add multiple volume mounts to a Container -pub fn container_with_volumes(mut container: Container, volumes: Array) -> Container { - for volume in volumes.iter() { - if volume.is_string() { - let volume_str = volume.clone().cast::(); - container = container.with_volume(&volume_str); - } - } - container -} - -/// Add multiple environment variables to a Container -pub fn container_with_envs(mut container: Container, env_map: Map) -> Container { - for (key, value) in env_map.iter() { - if value.is_string() { - let value_str = value.clone().cast::(); - container = container.with_env(&key, &value_str); - } - } - container -} - -/// Add multiple network aliases to a Container -pub fn container_with_network_aliases(mut container: Container, aliases: Array) -> Container { - for alias in aliases.iter() { - if alias.is_string() { - let alias_str = alias.clone().cast::(); - container = container.with_network_alias(&alias_str); - } - } - container -} - -/// Set memory swap limit for a Container -pub fn container_with_memory_swap_limit(container: Container, memory_swap: &str) -> Container { - container.with_memory_swap_limit(memory_swap) -} - -/// Set CPU shares for a Container -pub fn container_with_cpu_shares(container: Container, shares: &str) -> Container { - container.with_cpu_shares(shares) -} - -/// Set health check with options for a Container -pub fn container_with_health_check_options( - container: Container, - cmd: &str, - interval: Option<&str>, - timeout: Option<&str>, - retries: Option, - start_period: Option<&str> -) -> Container { - // Convert i64 to u32 for retries - let retries_u32 = retries.map(|r| r as u32); - container.with_health_check_options(cmd, interval, timeout, retries_u32, start_period) -} - -/// Set snapshotter for a Container -pub fn container_with_snapshotter(container: Container, snapshotter: &str) -> Container { - container.with_snapshotter(snapshotter) -} - -/// Set detach mode for a Container -pub fn container_with_detach(container: Container, detach: bool) -> Container { - container.with_detach(detach) -} - -/// Build and run the Container -/// -/// This function builds and runs the container using the configured options. -/// It provides detailed error information if the build fails. -pub fn container_build(container: Container) -> Result> { - // Get container details for better error reporting - let container_name = container.name.clone(); - let image = container.image.clone().unwrap_or_else(|| "none".to_string()); - let ports = container.ports.clone(); - let volumes = container.volumes.clone(); - let env_vars = container.env_vars.clone(); - - // Try to build the container - let build_result = container.build(); - - // Handle the result with improved error context - match build_result { - Ok(built_container) => { - // Container built successfully - Ok(built_container) - }, - Err(err) => { - // Add more context to the error - let enhanced_error = match err { - NerdctlError::CommandFailed(msg) => { - // Provide more detailed error information - let mut enhanced_msg = format!("Failed to build container '{}' from image '{}': {}", - container_name, image, msg); - - // Add information about configured options that might be relevant - if !ports.is_empty() { - enhanced_msg.push_str(&format!("\nConfigured ports: {:?}", ports)); - } - - if !volumes.is_empty() { - enhanced_msg.push_str(&format!("\nConfigured volumes: {:?}", volumes)); - } - - if !env_vars.is_empty() { - enhanced_msg.push_str(&format!("\nConfigured environment variables: {:?}", env_vars)); - } - - // Add suggestions for common issues - if msg.contains("not found") || msg.contains("no such image") { - enhanced_msg.push_str("\nSuggestion: The specified image may not exist or may not be pulled yet. Try pulling the image first with nerdctl_image_pull()."); - } else if msg.contains("port is already allocated") { - enhanced_msg.push_str("\nSuggestion: One of the specified ports is already in use. Try using a different port or stopping the container using that port."); - } else if msg.contains("permission denied") { - enhanced_msg.push_str("\nSuggestion: Permission issues detected. Check if you have the necessary permissions to create containers or access the specified volumes."); - } - - NerdctlError::CommandFailed(enhanced_msg) - }, - _ => err - }; - - nerdctl_error_to_rhai_error(Err(enhanced_error)) - } - } -} - -/// Start the Container and verify it's running -/// -/// This function starts the container and verifies that it's actually running. -/// It returns detailed error information if the container fails to start or -/// if it starts but stops immediately. -pub fn container_start(container: &mut Container) -> Result> { - // Get container details for better error reporting - let container_name = container.name.clone(); - let container_id = container.container_id.clone().unwrap_or_else(|| "unknown".to_string()); - - // Try to start the container - let start_result = container.start(); - - // Handle the result with improved error context - match start_result { - Ok(result) => { - // Container started successfully - Ok(result) - }, - Err(err) => { - // Add more context to the error - let enhanced_error = match err { - NerdctlError::CommandFailed(msg) => { - // Check if this is a "container already running" error, which is not really an error - if msg.contains("already running") { - return Ok(CommandResult { - stdout: format!("Container {} is already running", container_name), - stderr: "".to_string(), - success: true, - code: 0, - }); - } - - // Try to get more information about why the container might have failed to start - let mut enhanced_msg = format!("Failed to start container '{}' (ID: {}): {}", - container_name, container_id, msg); - - // Try to check if the image exists - if let Some(image) = &container.image { - enhanced_msg.push_str(&format!("\nContainer was using image: {}", image)); - } - - NerdctlError::CommandFailed(enhanced_msg) - }, - _ => err - }; - - nerdctl_error_to_rhai_error(Err(enhanced_error)) - } - } -} - -/// Stop the Container -pub fn container_stop(container: &mut Container) -> Result> { - nerdctl_error_to_rhai_error(container.stop()) -} - -/// Remove the Container -pub fn container_remove(container: &mut Container) -> Result> { - nerdctl_error_to_rhai_error(container.remove()) -} - -/// Execute a command in the Container -pub fn container_exec(container: &mut Container, command: &str) -> Result> { - nerdctl_error_to_rhai_error(container.exec(command)) -} - -/// Get container logs -pub fn container_logs(container: &mut Container) -> Result> { - // Get container details for better error reporting - let container_name = container.name.clone(); - let container_id = container.container_id.clone().unwrap_or_else(|| "unknown".to_string()); - - // Use the nerdctl::logs function - let logs_result = nerdctl::logs(&container_id); - - match logs_result { - Ok(result) => { - Ok(result) - }, - Err(err) => { - // Add more context to the error - let enhanced_error = NerdctlError::CommandFailed( - format!("Failed to get logs for container '{}' (ID: {}): {}", - container_name, container_id, err) - ); - - nerdctl_error_to_rhai_error(Err(enhanced_error)) - } - } -} - -/// Copy files between the Container and local filesystem -pub fn container_copy(container: &mut Container, source: &str, dest: &str) -> Result> { - nerdctl_error_to_rhai_error(container.copy(source, dest)) -} - -/// Create a new Map with default run options -pub fn new_run_options() -> Map { - let mut map = Map::new(); - map.insert("name".into(), Dynamic::UNIT); - map.insert("detach".into(), Dynamic::from(true)); - map.insert("ports".into(), Dynamic::from(Array::new())); - map.insert("snapshotter".into(), Dynamic::from("native")); - map -} - -// -// Container Function Wrappers -// - -/// Wrapper for nerdctl::run -/// -/// Run a container from an image. -pub fn nerdctl_run(image: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::run(image, None, true, None, None)) -} - -/// Run a container with a name -pub fn nerdctl_run_with_name(image: &str, name: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::run(image, Some(name), true, None, None)) -} - -/// Run a container with a port mapping -pub fn nerdctl_run_with_port(image: &str, name: &str, port: &str) -> Result> { - let ports = vec![port]; - nerdctl_error_to_rhai_error(nerdctl::run(image, Some(name), true, Some(&ports), None)) -} - -/// Wrapper for nerdctl::exec -/// -/// Execute a command in a container. -pub fn nerdctl_exec(container: &str, command: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::exec(container, command)) -} - -/// Wrapper for nerdctl::copy -/// -/// Copy files between container and local filesystem. -pub fn nerdctl_copy(source: &str, dest: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::copy(source, dest)) -} - -/// Wrapper for nerdctl::stop -/// -/// Stop a container. -pub fn nerdctl_stop(container: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::stop(container)) -} - -/// Wrapper for nerdctl::remove -/// -/// Remove a container. -pub fn nerdctl_remove(container: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::remove(container)) -} - -/// Wrapper for nerdctl::list -/// -/// List containers. -pub fn nerdctl_list(all: bool) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::list(all)) -} - -/// Wrapper for nerdctl::logs -/// -/// Get container logs. -pub fn nerdctl_logs(container: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::logs(container)) -} - -// -// Image Function Wrappers -// - -/// Wrapper for nerdctl::images -/// -/// List images in local storage. -pub fn nerdctl_images() -> Result> { - nerdctl_error_to_rhai_error(nerdctl::images()) -} - -/// Wrapper for nerdctl::image_remove -/// -/// Remove one or more images. -pub fn nerdctl_image_remove(image: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_remove(image)) -} - -/// Wrapper for nerdctl::image_push -/// -/// Push an image to a registry. -pub fn nerdctl_image_push(image: &str, destination: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_push(image, destination)) -} - -/// Wrapper for nerdctl::image_tag -/// -/// Add an additional name to a local image. -pub fn nerdctl_image_tag(image: &str, new_name: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_tag(image, new_name)) -} - -/// Wrapper for nerdctl::image_pull -/// -/// Pull an image from a registry. -pub fn nerdctl_image_pull(image: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_pull(image)) -} - -/// Wrapper for nerdctl::image_commit -/// -/// Commit a container to an image. -pub fn nerdctl_image_commit(container: &str, image_name: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_commit(container, image_name)) -} - -/// Wrapper for nerdctl::image_build -/// -/// Build an image using a Dockerfile. -pub fn nerdctl_image_build(tag: &str, context_path: &str) -> Result> { - nerdctl_error_to_rhai_error(nerdctl::image_build(tag, context_path)) -} -``` \ No newline at end of file diff --git a/libarchive/mcp_rhai/logic/templates/cargo.toml b/libarchive/mcp_rhai/logic/templates/cargo.toml deleted file mode 100644 index 5849e1b9..00000000 --- a/libarchive/mcp_rhai/logic/templates/cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "@{name}_rhai" -version = "0.1.0" -edition = "2021" - -[dependencies] -rhai = "1.21.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -@{source_pkg_info.name} = { path = "@{source_pkg_info.path}" } \ No newline at end of file diff --git a/libarchive/mcp_rhai/logic/templates/engine.rs b/libarchive/mcp_rhai/logic/templates/engine.rs deleted file mode 100644 index 8294f711..00000000 --- a/libarchive/mcp_rhai/logic/templates/engine.rs +++ /dev/null @@ -1,12 +0,0 @@ -use rhai::{Engine, EvalAltResult, Map, Dynamic}; -use crate::wrapper; - -pub fn create_rhai_engine() -> Engine { - let mut engine = Engine::new(); - - @for function in functions - engine.register_fn("@{function}", wrapper::@{function}); - @end - - engine -} \ No newline at end of file diff --git a/libarchive/mcp_rhai/logic/templates/example.rs b/libarchive/mcp_rhai/logic/templates/example.rs deleted file mode 100644 index e94b8032..00000000 --- a/libarchive/mcp_rhai/logic/templates/example.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::{fs, path::Path}; -use @{name}_rhai::create_rhai_engine; - -fn main() -> Result<(), Box> { - println!("=== Rhai Wrapper Example ==="); - - // Create a Rhai engine with functionality - let mut engine = create_rhai_engine(); - println!("Successfully created Rhai engine"); - - // Get the path to the example.rhai script - let script_path = get_script_path()?; - println!("Loading script from: {}", script_path.display()); - - // Load the script content - let script = fs::read_to_string(&script_path) - .map_err(|e| format!("Failed to read script file: {}", e))?; - - // Run the script - println!("\n=== Running Rhai script ==="); - let result = engine.eval::(&script) - .map_err(|e| format!("Script execution error: {}", e))?; - - println!("\nScript returned: {}", result); - println!("\nExample completed successfully!"); - Ok(()) -} - -fn get_script_path() -> Result { - // When running with cargo run --example, the script will be in the examples directory - let script_path = Path::new(env!("CARGO_MANIFEST_DIR")) - .join("examples") - .join("example.rhai"); - - if script_path.exists() { - Ok(script_path) - } else { - Err(format!("Could not find example.rhai script at {}", script_path.display())) - } -} diff --git a/libarchive/mcp_rhai/logic/templates/functions.rs b/libarchive/mcp_rhai/logic/templates/functions.rs deleted file mode 100644 index 96ec68f9..00000000 --- a/libarchive/mcp_rhai/logic/templates/functions.rs +++ /dev/null @@ -1,510 +0,0 @@ -// File: /root/code/git.threefold.info/herocode/sal/src/virt/nerdctl/container_builder.rs - -use std::collections::HashMap; -use crate::virt::nerdctl::{execute_nerdctl_command, NerdctlError}; -use super::container_types::{Container, HealthCheck}; -use super::health_check_script::prepare_health_check_command; - -impl Container { - /// Reset the container configuration to defaults while keeping the name and image - /// If the container exists, it will be stopped and removed. - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn reset(self) -> Self { - let name = self.name; - let image = self.image.clone(); - - // If container exists, stop and remove it - if let Some(container_id) = &self.container_id { - println!("Container exists. Stopping and removing container '{}'...", name); - - // Try to stop the container - let _ = execute_nerdctl_command(&["stop", container_id]); - - // Try to remove the container - let _ = execute_nerdctl_command(&["rm", container_id]); - } - - // Create a new container with just the name and image, but no container_id - Self { - name, - container_id: None, // Reset container_id to None since we removed the container - image, - config: std::collections::HashMap::new(), - ports: Vec::new(), - volumes: Vec::new(), - env_vars: std::collections::HashMap::new(), - network: None, - network_aliases: Vec::new(), - cpu_limit: None, - memory_limit: None, - memory_swap_limit: None, - cpu_shares: None, - restart_policy: None, - health_check: None, - detach: false, - snapshotter: None, - } - } - - /// Add a port mapping - /// - /// # Arguments - /// - /// * `port` - Port mapping (e.g., "8080:80") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_port(mut self, port: &str) -> Self { - self.ports.push(port.to_string()); - self - } - - /// Add multiple port mappings - /// - /// # Arguments - /// - /// * `ports` - Array of port mappings (e.g., ["8080:80", "8443:443"]) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_ports(mut self, ports: &[&str]) -> Self { - for port in ports { - self.ports.push(port.to_string()); - } - self - } - - /// Add a volume mount - /// - /// # Arguments - /// - /// * `volume` - Volume mount (e.g., "/host/path:/container/path") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_volume(mut self, volume: &str) -> Self { - self.volumes.push(volume.to_string()); - self - } - - /// Add multiple volume mounts - /// - /// # Arguments - /// - /// * `volumes` - Array of volume mounts (e.g., ["/host/path1:/container/path1", "/host/path2:/container/path2"]) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_volumes(mut self, volumes: &[&str]) -> Self { - for volume in volumes { - self.volumes.push(volume.to_string()); - } - self - } - - /// Add an environment variable - /// - /// # Arguments - /// - /// * `key` - Environment variable name - /// * `value` - Environment variable value - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_env(mut self, key: &str, value: &str) -> Self { - self.env_vars.insert(key.to_string(), value.to_string()); - self - } - - /// Add multiple environment variables - /// - /// # Arguments - /// - /// * `env_map` - Map of environment variable names to values - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_envs(mut self, env_map: &HashMap<&str, &str>) -> Self { - for (key, value) in env_map { - self.env_vars.insert(key.to_string(), value.to_string()); - } - self - } - - /// Set the network for the container - /// - /// # Arguments - /// - /// * `network` - Network name - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_network(mut self, network: &str) -> Self { - self.network = Some(network.to_string()); - self - } - - /// Add a network alias for the container - /// - /// # Arguments - /// - /// * `alias` - Network alias - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_network_alias(mut self, alias: &str) -> Self { - self.network_aliases.push(alias.to_string()); - self - } - - /// Add multiple network aliases for the container - /// - /// # Arguments - /// - /// * `aliases` - Array of network aliases - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_network_aliases(mut self, aliases: &[&str]) -> Self { - for alias in aliases { - self.network_aliases.push(alias.to_string()); - } - self - } - - /// Set CPU limit for the container - /// - /// # Arguments - /// - /// * `cpus` - CPU limit (e.g., "0.5" for half a CPU, "2" for 2 CPUs) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_cpu_limit(mut self, cpus: &str) -> Self { - self.cpu_limit = Some(cpus.to_string()); - self - } - - /// Set memory limit for the container - /// - /// # Arguments - /// - /// * `memory` - Memory limit (e.g., "512m" for 512MB, "1g" for 1GB) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_memory_limit(mut self, memory: &str) -> Self { - self.memory_limit = Some(memory.to_string()); - self - } - - /// Set memory swap limit for the container - /// - /// # Arguments - /// - /// * `memory_swap` - Memory swap limit (e.g., "1g" for 1GB) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_memory_swap_limit(mut self, memory_swap: &str) -> Self { - self.memory_swap_limit = Some(memory_swap.to_string()); - self - } - - /// Set CPU shares for the container (relative weight) - /// - /// # Arguments - /// - /// * `shares` - CPU shares (e.g., "1024" for default, "512" for half) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_cpu_shares(mut self, shares: &str) -> Self { - self.cpu_shares = Some(shares.to_string()); - self - } - - /// Set restart policy for the container - /// - /// # Arguments - /// - /// * `policy` - Restart policy (e.g., "no", "always", "on-failure", "unless-stopped") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_restart_policy(mut self, policy: &str) -> Self { - self.restart_policy = Some(policy.to_string()); - self - } - - /// Set a simple health check for the container - /// - /// # Arguments - /// - /// * `cmd` - Command to run for health check (e.g., "curl -f http://localhost/ || exit 1") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_health_check(mut self, cmd: &str) -> Self { - // Use the health check script module to prepare the command - let prepared_cmd = prepare_health_check_command(cmd, &self.name); - - self.health_check = Some(HealthCheck { - cmd: prepared_cmd, - interval: None, - timeout: None, - retries: None, - start_period: None, - }); - self - } - - /// Set a health check with custom options for the container - /// - /// # Arguments - /// - /// * `cmd` - Command to run for health check - /// * `interval` - Optional time between running the check (e.g., "30s", "1m") - /// * `timeout` - Optional maximum time to wait for a check to complete (e.g., "30s", "1m") - /// * `retries` - Optional number of consecutive failures needed to consider unhealthy - /// * `start_period` - Optional start period for the container to initialize before counting retries (e.g., "30s", "1m") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_health_check_options( - mut self, - cmd: &str, - interval: Option<&str>, - timeout: Option<&str>, - retries: Option, - start_period: Option<&str>, - ) -> Self { - // Use the health check script module to prepare the command - let prepared_cmd = prepare_health_check_command(cmd, &self.name); - - let mut health_check = HealthCheck { - cmd: prepared_cmd, - interval: None, - timeout: None, - retries: None, - start_period: None, - }; - - if let Some(interval_value) = interval { - health_check.interval = Some(interval_value.to_string()); - } - - if let Some(timeout_value) = timeout { - health_check.timeout = Some(timeout_value.to_string()); - } - - if let Some(retries_value) = retries { - health_check.retries = Some(retries_value); - } - - if let Some(start_period_value) = start_period { - health_check.start_period = Some(start_period_value.to_string()); - } - - self.health_check = Some(health_check); - self - } - - /// Set the snapshotter - /// - /// # Arguments - /// - /// * `snapshotter` - Snapshotter to use - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_snapshotter(mut self, snapshotter: &str) -> Self { - self.snapshotter = Some(snapshotter.to_string()); - self - } - - /// Set whether to run in detached mode - /// - /// # Arguments - /// - /// * `detach` - Whether to run in detached mode - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_detach(mut self, detach: bool) -> Self { - self.detach = detach; - self - } - - /// Build the container - /// - /// # Returns - /// - /// * `Result` - Container instance or error - pub fn build(self) -> Result { - // If container already exists, return it - if self.container_id.is_some() { - return Ok(self); - } - - // If no image is specified, return an error - let image = match &self.image { - Some(img) => img, - None => return Err(NerdctlError::Other("No image specified for container creation".to_string())), - }; - - // Build the command arguments as strings - let mut args_strings = Vec::new(); - args_strings.push("run".to_string()); - - if self.detach { - args_strings.push("-d".to_string()); - } - - args_strings.push("--name".to_string()); - args_strings.push(self.name.clone()); - - // Add port mappings - for port in &self.ports { - args_strings.push("-p".to_string()); - args_strings.push(port.clone()); - } - - // Add volume mounts - for volume in &self.volumes { - args_strings.push("-v".to_string()); - args_strings.push(volume.clone()); - } - - // Add environment variables - for (key, value) in &self.env_vars { - args_strings.push("-e".to_string()); - args_strings.push(format!("{}={}", key, value)); - } - - // Add network configuration - if let Some(network) = &self.network { - args_strings.push("--network".to_string()); - args_strings.push(network.clone()); - } - - // Add network aliases - for alias in &self.network_aliases { - args_strings.push("--network-alias".to_string()); - args_strings.push(alias.clone()); - } - - // Add resource limits - if let Some(cpu_limit) = &self.cpu_limit { - args_strings.push("--cpus".to_string()); - args_strings.push(cpu_limit.clone()); - } - - if let Some(memory_limit) = &self.memory_limit { - args_strings.push("--memory".to_string()); - args_strings.push(memory_limit.clone()); - } - - if let Some(memory_swap_limit) = &self.memory_swap_limit { - args_strings.push("--memory-swap".to_string()); - args_strings.push(memory_swap_limit.clone()); - } - - if let Some(cpu_shares) = &self.cpu_shares { - args_strings.push("--cpu-shares".to_string()); - args_strings.push(cpu_shares.clone()); - } - - // Add restart policy - if let Some(restart_policy) = &self.restart_policy { - args_strings.push("--restart".to_string()); - args_strings.push(restart_policy.clone()); - } - - // Add health check - if let Some(health_check) = &self.health_check { - args_strings.push("--health-cmd".to_string()); - args_strings.push(health_check.cmd.clone()); - - if let Some(interval) = &health_check.interval { - args_strings.push("--health-interval".to_string()); - args_strings.push(interval.clone()); - } - - if let Some(timeout) = &health_check.timeout { - args_strings.push("--health-timeout".to_string()); - args_strings.push(timeout.clone()); - } - - if let Some(retries) = &health_check.retries { - args_strings.push("--health-retries".to_string()); - args_strings.push(retries.to_string()); - } - - if let Some(start_period) = &health_check.start_period { - args_strings.push("--health-start-period".to_string()); - args_strings.push(start_period.clone()); - } - } - - if let Some(snapshotter_value) = &self.snapshotter { - args_strings.push("--snapshotter".to_string()); - args_strings.push(snapshotter_value.clone()); - } - - // Add flags to avoid BPF issues - args_strings.push("--cgroup-manager=cgroupfs".to_string()); - - args_strings.push(image.clone()); - - // Convert to string slices for the command - let args: Vec<&str> = args_strings.iter().map(|s| s.as_str()).collect(); - - // Execute the command - let result = execute_nerdctl_command(&args)?; - - // Get the container ID from the output - let container_id = result.stdout.trim().to_string(); - - Ok(Self { - name: self.name, - container_id: Some(container_id), - image: self.image, - config: self.config, - ports: self.ports, - volumes: self.volumes, - env_vars: self.env_vars, - network: self.network, - network_aliases: self.network_aliases, - cpu_limit: self.cpu_limit, - memory_limit: self.memory_limit, - memory_swap_limit: self.memory_swap_limit, - cpu_shares: self.cpu_shares, - restart_policy: self.restart_policy, - health_check: self.health_check, - detach: self.detach, - snapshotter: self.snapshotter, - }) - } -} \ No newline at end of file diff --git a/libarchive/mcp_rhai/logic/templates/generic_wrapper.rs b/libarchive/mcp_rhai/logic/templates/generic_wrapper.rs deleted file mode 100644 index 8a189453..00000000 --- a/libarchive/mcp_rhai/logic/templates/generic_wrapper.rs +++ /dev/null @@ -1,132 +0,0 @@ -use std::collections::HashMap; -use rhai::{Dynamic, Map, Array}; - -/// Local wrapper trait for sal::rhai::ToRhai to avoid orphan rule violations -pub trait ToRhai { - /// Convert to a Rhai Dynamic value - fn to_rhai(&self) -> Dynamic; -} - -// Implementation of ToRhai for Dynamic -impl ToRhai for Dynamic { - fn to_rhai(&self) -> Dynamic { - self.clone() - } -} - -/// Generic trait for wrapping Rust functions to be used with Rhai -pub trait RhaiWrapper { - /// Wrap a function that takes ownership of self - fn wrap_consuming(self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(Self) -> R, - R: ToRhai; - - /// Wrap a function that takes a mutable reference to self - fn wrap_mut(&mut self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(&mut Self) -> R, - R: ToRhai; - - /// Wrap a function that takes an immutable reference to self - fn wrap(&self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(&Self) -> R, - R: ToRhai; -} - -/// Implementation of RhaiWrapper for any type -impl RhaiWrapper for T { - fn wrap_consuming(self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(Self) -> R, - R: ToRhai, - { - let result = f(self); - result.to_rhai() - } - - fn wrap_mut(&mut self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(&mut Self) -> R, - R: ToRhai, - { - let result = f(self); - result.to_rhai() - } - - fn wrap(&self, f: F) -> Dynamic - where - Self: Sized + Clone, - F: FnOnce(&Self) -> R, - R: ToRhai, - { - let result = f(self); - result.to_rhai() - } -} - -/// Convert a Rhai Map to a Rust HashMap -pub fn map_to_hashmap(map: &Map) -> HashMap { - let mut result = HashMap::new(); - for (key, value) in map.iter() { - let k = key.clone().to_string(); - let v = value.clone().to_string(); - if !k.is_empty() && !v.is_empty() { - result.insert(k, v); - } - } - result -} - -/// Convert a HashMap to a Rhai Map -pub fn hashmap_to_map(map: &HashMap) -> Map { - let mut result = Map::new(); - for (key, value) in map.iter() { - result.insert(key.clone().into(), Dynamic::from(value.clone())); - } - result -} - -/// Convert a Rhai Array to a Vec of strings -pub fn array_to_vec_string(array: &Array) -> Vec { - array.iter() - .filter_map(|item| { - let s = item.clone().to_string(); - if !s.is_empty() { Some(s) } else { None } - }) - .collect() -} - -/// Helper function to convert Dynamic to Option -pub fn dynamic_to_string_option(value: &Dynamic) -> Option { - if value.is_string() { - Some(value.clone().to_string()) - } else { - None - } -} - -/// Helper function to convert Dynamic to Option -pub fn dynamic_to_u32_option(value: &Dynamic) -> Option { - if value.is_int() { - Some(value.as_int().unwrap() as u32) - } else { - None - } -} - -/// Helper function to convert Dynamic to Option<&str> with lifetime management -pub fn dynamic_to_str_option<'a>(value: &Dynamic, storage: &'a mut String) -> Option<&'a str> { - if value.is_string() { - *storage = value.clone().to_string(); - Some(storage.as_str()) - } else { - None - } -} \ No newline at end of file diff --git a/libarchive/mcp_rhai/logic/templates/lib.rs b/libarchive/mcp_rhai/logic/templates/lib.rs deleted file mode 100644 index 10cb4178..00000000 --- a/libarchive/mcp_rhai/logic/templates/lib.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Re-export the utility modules -pub mod generic_wrapper; -pub mod wrapper; -pub mod engine; - -// Re-export the utility traits and functions -pub use generic_wrapper::{RhaiWrapper, map_to_hashmap, array_to_vec_string, - dynamic_to_string_option, hashmap_to_map}; -pub use engine::create_rhai_engine; - -// The create_rhai_engine function is now in the engine module \ No newline at end of file diff --git a/libarchive/mcp_rhai/mcp/command.v b/libarchive/mcp_rhai/mcp/command.v deleted file mode 100644 index 956ca1dc..00000000 --- a/libarchive/mcp_rhai/mcp/command.v +++ /dev/null @@ -1,22 +0,0 @@ -module mcp - -import cli - -pub const command = cli.Command{ - sort_flags: true - name: 'rhai' - // execute: cmd_mcpgen - description: 'rhai command' - commands: [ - cli.Command{ - name: 'start' - execute: cmd_start - description: 'start the Rhai server' - }, - ] -} - -fn cmd_start(cmd cli.Command) ! { - mut server := new_mcp_server()! - server.start()! -} diff --git a/libarchive/mcp_rhai/mcp/mcp.v b/libarchive/mcp_rhai/mcp/mcp.v deleted file mode 100644 index cf77c11a..00000000 --- a/libarchive/mcp_rhai/mcp/mcp.v +++ /dev/null @@ -1,33 +0,0 @@ -module mcp - -import incubaid.herolib.ai.mcp -import incubaid.herolib.schemas.jsonrpc -import log - -pub fn new_mcp_server() !&Server { - log.info('Creating new Developer MCP server') - - // Initialize the server with the empty handlers map - mut server := mcp.new_server(MemoryBackend{ - tools: { - 'generate_rhai_wrapper': generate_rhai_wrapper_spec - } - tool_handlers: { - 'generate_rhai_wrapper': generate_rhai_wrapper_handler - } - prompts: { - 'rhai_wrapper': rhai_wrapper_prompt_spec - } - prompt_handlers: { - 'rhai_wrapper': rhai_wrapper_prompt_handler - } - }, ServerParams{ - config: ServerConfiguration{ - server_info: ServerInfo{ - name: 'rhai' - version: '1.0.0' - } - } - })! - return server -} diff --git a/libarchive/mcp_rhai/mcp/prompts.v b/libarchive/mcp_rhai/mcp/prompts.v deleted file mode 100644 index 1dfa6091..00000000 --- a/libarchive/mcp_rhai/mcp/prompts.v +++ /dev/null @@ -1,45 +0,0 @@ -module mcp - -import incubaid.herolib.ai.mcp -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.ai.mcp.rhai.logic -import incubaid.herolib.schemas.jsonschema -import incubaid.herolib.lang.rust -import x.json2 as json - -// Tool definition for the create_rhai_wrapper function -const rhai_wrapper_prompt_spec = Prompt{ - name: 'rhai_wrapper' - description: 'provides a prompt for creating Rhai wrappers for Rust functions that follow builder pattern and create examples corresponding to the provided example file' - arguments: [ - PromptArgument{ - name: 'source_path' - description: 'Path to the source directory' - required: true - }, - ] -} - -// Tool handler for the create_rhai_wrapper function -pub fn rhai_wrapper_prompt_handler(arguments []string) ![]PromptMessage { - source_path := arguments[0] - - // Read and combine all Rust files in the source directory - source_code := rust.read_source_code(source_path)! - - // Extract the module name from the directory path (last component) - name := rust.extract_module_name_from_path(source_path) - - source_pkg_info := rust.detect_source_package(source_path)! - - result := logic.rhai_wrapper_generation_prompt(name, source_code, source_pkg_info)! - return [ - PromptMessage{ - role: 'assistant' - content: PromptContent{ - typ: 'text' - text: result - } - }, - ] -} diff --git a/libarchive/mcp_rhai/mcp/specifications.v b/libarchive/mcp_rhai/mcp/specifications.v deleted file mode 100644 index dfd8b1bb..00000000 --- a/libarchive/mcp_rhai/mcp/specifications.v +++ /dev/null @@ -1,21 +0,0 @@ -module mcp - -import incubaid.herolib.ai.mcp -import x.json2 as json -import incubaid.herolib.schemas.jsonschema -import log - -const specs = Tool{ - name: 'rhai_interface' - description: 'Add Rhai Interface to Rust Code Files' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to a .rs file or directory containing .rs files to make rhai interface for' - }) - } - required: ['path'] - } -} diff --git a/libarchive/mcp_rhai/mcp/tools.v b/libarchive/mcp_rhai/mcp/tools.v deleted file mode 100644 index 0b9a751a..00000000 --- a/libarchive/mcp_rhai/mcp/tools.v +++ /dev/null @@ -1,38 +0,0 @@ -module mcp - -import incubaid.herolib.ai.mcp -import incubaid.herolib.develop.codetools as code -import incubaid.herolib.ai.mcp.rhai.logic -import incubaid.herolib.schemas.jsonschema -import x.json2 as json { Any } - -// Tool definition for the generate_rhai_wrapper function -const generate_rhai_wrapper_spec = Tool{ - name: 'generate_rhai_wrapper' - description: 'generate_rhai_wrapper receives the name of a V language function string, and the path to the module in which it exists.' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - 'source_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - }) - } - required: ['name', 'source_path'] - } -} - -// Tool handler for the generate_rhai_wrapper function -pub fn generate_rhai_wrapper_handler(arguments map[string]Any) !ToolCallResult { - name := arguments['name'].str() - source_path := arguments['source_path'].str() - result := logic.generate_rhai_wrapper(name, source_path) or { - return mcp.error_tool_call_result(err) - } - return ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} diff --git a/libarchive/mcp_rhai/rhai.v b/libarchive/mcp_rhai/rhai.v deleted file mode 100644 index adc61b86..00000000 --- a/libarchive/mcp_rhai/rhai.v +++ /dev/null @@ -1 +0,0 @@ -module rhai diff --git a/libarchive/mcp_rust_wip/command.v b/libarchive/mcp_rust_wip/command.v deleted file mode 100644 index 00b5fefa..00000000 --- a/libarchive/mcp_rust_wip/command.v +++ /dev/null @@ -1,21 +0,0 @@ -module rust - -import cli - -pub const command = cli.Command{ - sort_flags: true - name: 'rust' - description: 'Rust language tools command' - commands: [ - cli.Command{ - name: 'start' - execute: cmd_start - description: 'start the Rust MCP server' - }, - ] -} - -fn cmd_start(cmd cli.Command) ! { - mut server := new_mcp_server()! - server.start()! -} diff --git a/libarchive/mcp_rust_wip/generics.v b/libarchive/mcp_rust_wip/generics.v deleted file mode 100644 index 7228b677..00000000 --- a/libarchive/mcp_rust_wip/generics.v +++ /dev/null @@ -1,54 +0,0 @@ -module rust - -import incubaid.herolib.ai.mcp { ToolContent } - -pub fn result_to_mcp_tool_contents[T](result T) []ToolContent { - return [result_to_mcp_tool_content[T](result)] -} - -pub fn result_to_mcp_tool_content[T](result T) ToolContent { - $if T is string { - return ToolContent{ - typ: 'text' - text: result.str() - } - } $else $if T is int { - return ToolContent{ - typ: 'number' - number: result.int() - } - } $else $if T is bool { - return ToolContent{ - typ: 'boolean' - boolean: result.bool() - } - } $else $if result is $array { - mut items := []ToolContent{} - for item in result { - items << result_to_mcp_tool_content(item) - } - return ToolContent{ - typ: 'array' - items: items - } - } $else $if T is $struct { - mut properties := map[string]ToolContent{} - $for field in T.fields { - properties[field.name] = result_to_mcp_tool_content(result.$(field.name)) - } - return ToolContent{ - typ: 'object' - properties: properties - } - } $else { - panic('Unsupported type: ${typeof(result)}') - } -} - -pub fn array_to_mcp_tool_contents[U](array []U) []ToolContent { - mut contents := []ToolContent{} - for item in array { - contents << result_to_mcp_tool_content(item) - } - return contents -} diff --git a/libarchive/mcp_rust_wip/mcp.v b/libarchive/mcp_rust_wip/mcp.v deleted file mode 100644 index 11acd33f..00000000 --- a/libarchive/mcp_rust_wip/mcp.v +++ /dev/null @@ -1,52 +0,0 @@ -module rust - -import incubaid.herolib.ai.mcp -import incubaid.herolib.schemas.jsonrpc -import log - -pub fn new_mcp_server() !&mcp.Server { - log.info('Creating new Rust MCP server') - - // Initialize the server with tools and prompts - mut server := mcp.new_server(mcp.MemoryBackend{ - tools: { - 'list_functions_in_file': list_functions_in_file_spec - 'list_structs_in_file': list_structs_in_file_spec - 'list_modules_in_dir': list_modules_in_dir_spec - 'get_import_statement': get_import_statement_spec - // 'get_module_dependency': get_module_dependency_spec - } - tool_handlers: { - 'list_functions_in_file': list_functions_in_file_handler - 'list_structs_in_file': list_structs_in_file_handler - 'list_modules_in_dir': list_modules_in_dir_handler - 'get_import_statement': get_import_statement_handler - // 'get_module_dependency': get_module_dependency_handler - } - prompts: { - 'rust_functions': rust_functions_prompt_spec - 'rust_structs': rust_structs_prompt_spec - 'rust_modules': rust_modules_prompt_spec - 'rust_imports': rust_imports_prompt_spec - 'rust_dependencies': rust_dependencies_prompt_spec - 'rust_tools_guide': rust_tools_guide_prompt_spec - } - prompt_handlers: { - 'rust_functions': rust_functions_prompt_handler - 'rust_structs': rust_structs_prompt_handler - 'rust_modules': rust_modules_prompt_handler - 'rust_imports': rust_imports_prompt_handler - 'rust_dependencies': rust_dependencies_prompt_handler - 'rust_tools_guide': rust_tools_guide_prompt_handler - } - }, mcp.ServerParams{ - config: mcp.ServerConfiguration{ - server_info: mcp.ServerInfo{ - name: 'rust' - version: '1.0.0' - } - } - })! - - return server -} diff --git a/libarchive/mcp_rust_wip/prompts.v b/libarchive/mcp_rust_wip/prompts.v deleted file mode 100644 index 45e3a46a..00000000 --- a/libarchive/mcp_rust_wip/prompts.v +++ /dev/null @@ -1,151 +0,0 @@ -module rust - -import incubaid.herolib.ai.mcp -import os -import x.json2 as json - -// Prompt specification for Rust functions -const rust_functions_prompt_spec = mcp.Prompt{ - name: 'rust_functions' - description: 'Provides guidance on working with Rust functions and using the list_functions_in_file tool' - arguments: [] -} - -// Handler for rust_functions prompt -pub fn rust_functions_prompt_handler(arguments []string) ![]mcp.PromptMessage { - content := os.read_file('${os.dir(@FILE)}/prompts/functions.md')! - - return [ - mcp.PromptMessage{ - role: 'assistant' - content: mcp.PromptContent{ - typ: 'text' - text: content - } - }, - ] -} - -// Prompt specification for Rust structs -const rust_structs_prompt_spec = mcp.Prompt{ - name: 'rust_structs' - description: 'Provides guidance on working with Rust structs and using the list_structs_in_file tool' - arguments: [] -} - -// Handler for rust_structs prompt -pub fn rust_structs_prompt_handler(arguments []string) ![]mcp.PromptMessage { - content := os.read_file('${os.dir(@FILE)}/prompts/structs.md')! - - return [ - mcp.PromptMessage{ - role: 'assistant' - content: mcp.PromptContent{ - typ: 'text' - text: content - } - }, - ] -} - -// Prompt specification for Rust modules -const rust_modules_prompt_spec = mcp.Prompt{ - name: 'rust_modules' - description: 'Provides guidance on working with Rust modules and using the list_modules_in_dir tool' - arguments: [] -} - -// Handler for rust_modules prompt -pub fn rust_modules_prompt_handler(arguments []string) ![]mcp.PromptMessage { - content := os.read_file('${os.dir(@FILE)}/prompts/modules.md')! - - return [ - mcp.PromptMessage{ - role: 'assistant' - content: mcp.PromptContent{ - typ: 'text' - text: content - } - }, - ] -} - -// Prompt specification for Rust imports -const rust_imports_prompt_spec = mcp.Prompt{ - name: 'rust_imports' - description: 'Provides guidance on working with Rust imports and using the get_import_statement tool' - arguments: [] -} - -// Handler for rust_imports prompt -pub fn rust_imports_prompt_handler(arguments []string) ![]mcp.PromptMessage { - content := os.read_file('${os.dir(@FILE)}/prompts/imports.md')! - - return [ - mcp.PromptMessage{ - role: 'assistant' - content: mcp.PromptContent{ - typ: 'text' - text: content - } - }, - ] -} - -// Prompt specification for Rust dependencies -const rust_dependencies_prompt_spec = mcp.Prompt{ - name: 'rust_dependencies' - description: 'Provides guidance on working with Rust dependencies and using the get_module_dependency tool' - arguments: [] -} - -// Handler for rust_dependencies prompt -pub fn rust_dependencies_prompt_handler(arguments []string) ![]mcp.PromptMessage { - content := os.read_file('${os.dir(@FILE)}/prompts/dependencies.md')! - - return [ - mcp.PromptMessage{ - role: 'assistant' - content: mcp.PromptContent{ - typ: 'text' - text: content - } - }, - ] -} - -// Prompt specification for general Rust tools guide -const rust_tools_guide_prompt_spec = mcp.Prompt{ - name: 'rust_tools_guide' - description: 'Provides a comprehensive guide on all available Rust tools and how to use them' - arguments: [] -} - -// Handler for rust_tools_guide prompt -pub fn rust_tools_guide_prompt_handler(arguments []string) ![]mcp.PromptMessage { - // Combine all prompt files into one comprehensive guide - functions_content := os.read_file('${os.dir(@FILE)}/prompts/functions.md')! - structs_content := os.read_file('${os.dir(@FILE)}/prompts/structs.md')! - modules_content := os.read_file('${os.dir(@FILE)}/prompts/modules.md')! - imports_content := os.read_file('${os.dir(@FILE)}/prompts/imports.md')! - dependencies_content := os.read_file('${os.dir(@FILE)}/prompts/dependencies.md')! - - combined_content := '# Rust Language Tools Guide\n\n' + - 'This guide provides comprehensive information on working with Rust code using the available tools.\n\n' + - '## Table of Contents\n\n' + '1. [Functions](#functions)\n' + '2. [Structs](#structs)\n' + - '3. [Modules](#modules)\n' + '4. [Imports](#imports)\n' + - '5. [Dependencies](#dependencies)\n\n' + '\n' + functions_content + - '\n\n' + '\n' + structs_content + '\n\n' + - '\n' + modules_content + '\n\n' + '\n' + - imports_content + '\n\n' + '\n' + dependencies_content - - return [ - mcp.PromptMessage{ - role: 'assistant' - content: mcp.PromptContent{ - typ: 'text' - text: combined_content - } - }, - ] -} diff --git a/libarchive/mcp_rust_wip/tools.v b/libarchive/mcp_rust_wip/tools.v deleted file mode 100644 index cc082bc5..00000000 --- a/libarchive/mcp_rust_wip/tools.v +++ /dev/null @@ -1,299 +0,0 @@ -module rust - -import incubaid.herolib.ai.mcp -import incubaid.herolib.lang.rust -import incubaid.herolib.schemas.jsonschema -import x.json2 as json { Any } - -// Tool specification for listing functions in a Rust file -const list_functions_in_file_spec = mcp.Tool{ - name: 'list_functions_in_file' - description: 'Lists all function definitions in a Rust file' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'file_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to the Rust file' - }) - } - required: ['file_path'] - } -} - -// Handler for list_functions_in_file -pub fn list_functions_in_file_handler(arguments map[string]Any) !mcp.ToolCallResult { - file_path := arguments['file_path'].str() - result := rust.list_functions_in_file(file_path) or { return mcp.error_tool_call_result(err) } - return mcp.ToolCallResult{ - is_error: false - content: mcp.array_to_mcp_tool_contents[string](result) - } -} - -// Tool specification for listing structs in a Rust file -const list_structs_in_file_spec = mcp.Tool{ - name: 'list_structs_in_file' - description: 'Lists all struct definitions in a Rust file' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'file_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to the Rust file' - }) - } - required: ['file_path'] - } -} - -// Handler for list_structs_in_file -pub fn list_structs_in_file_handler(arguments map[string]Any) !mcp.ToolCallResult { - file_path := arguments['file_path'].str() - result := rust.list_structs_in_file(file_path) or { return mcp.error_tool_call_result(err) } - return mcp.ToolCallResult{ - is_error: false - content: mcp.array_to_mcp_tool_contents[string](result) - } -} - -// Tool specification for listing modules in a directory -const list_modules_in_dir_spec = mcp.Tool{ - name: 'list_modules_in_dir' - description: 'Lists all Rust modules in a directory' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'dir_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to the directory' - }) - } - required: ['dir_path'] - } -} - -// Handler for list_modules_in_dir -pub fn list_modules_in_dir_handler(arguments map[string]Any) !mcp.ToolCallResult { - dir_path := arguments['dir_path'].str() - result := rust.list_modules_in_directory(dir_path) or { return mcp.error_tool_call_result(err) } - return mcp.ToolCallResult{ - is_error: false - content: mcp.array_to_mcp_tool_contents[string](result) - } -} - -// Tool specification for getting an import statement -const get_import_statement_spec = mcp.Tool{ - name: 'get_import_statement' - description: 'Generates appropriate Rust import statement for a module based on file paths' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'current_file': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to the file where the import will be added' - }) - 'target_module': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to the target module to be imported' - }) - } - required: ['current_file', 'target_module'] - } -} - -// Handler for get_import_statement -pub fn get_import_statement_handler(arguments map[string]Any) !mcp.ToolCallResult { - current_file := arguments['current_file'].str() - target_module := arguments['target_module'].str() - result := rust.generate_import_statement(current_file, target_module) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// Tool specification for getting module dependency information -const get_module_dependency_spec = mcp.Tool{ - name: 'get_module_dependency' - description: 'Gets dependency information for adding a Rust module to a project' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'importer_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to the file that will import the module' - }) - 'module_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to the module that will be imported' - }) - } - required: ['importer_path', 'module_path'] - } -} - -struct Tester { - import_statement string - module_path string -} - -// Handler for get_module_dependency -pub fn get_module_dependency_handler(arguments map[string]Any) !mcp.ToolCallResult { - importer_path := arguments['importer_path'].str() - module_path := arguments['module_path'].str() - dependency := rust.get_module_dependency(importer_path, module_path) or { - return mcp.error_tool_call_result(err) - } - - return mcp.ToolCallResult{ - is_error: false - content: result_to_mcp_tool_contents[Tester](Tester{ - import_statement: dependency.import_statement - module_path: dependency.module_path - }) // Return JSON string - } -} - -// --- Get Function from File Tool --- - -// Specification for get_function_from_file tool -const get_function_from_file_spec = mcp.Tool{ - name: 'get_function_from_file' - description: 'Get the declaration of a Rust function from a specified file path.' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'file_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to the Rust file.' - }) - 'function_name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: "Name of the function to retrieve (e.g., 'my_function' or 'MyStruct::my_method')." - }) - } - required: ['file_path', 'function_name'] - } -} - -// Handler for get_function_from_file -pub fn get_function_from_file_handler(arguments map[string]Any) !mcp.ToolCallResult { - file_path := arguments['file_path'].str() - function_name := arguments['function_name'].str() - result := rust.get_function_from_file(file_path, function_name) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// --- Get Function from Module Tool --- - -// Specification for get_function_from_module tool -const get_function_from_module_spec = mcp.Tool{ - name: 'get_function_from_module' - description: 'Get the declaration of a Rust function from a specified module path (directory or file).' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'module_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to the Rust module directory or file.' - }) - 'function_name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: "Name of the function to retrieve (e.g., 'my_function' or 'MyStruct::my_method')." - }) - } - required: ['module_path', 'function_name'] - } -} - -// Handler for get_function_from_module -pub fn get_function_from_module_handler(arguments map[string]Any) !mcp.ToolCallResult { - module_path := arguments['module_path'].str() - function_name := arguments['function_name'].str() - result := rust.get_function_from_module(module_path, function_name) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// --- Get Struct from File Tool --- - -// Specification for get_struct_from_file tool -const get_struct_from_file_spec = mcp.Tool{ - name: 'get_struct_from_file' - description: 'Get the declaration of a Rust struct from a specified file path.' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'file_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to the Rust file.' - }) - 'struct_name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: "Name of the struct to retrieve (e.g., 'MyStruct')." - }) - } - required: ['file_path', 'struct_name'] - } -} - -// Handler for get_struct_from_file -pub fn get_struct_from_file_handler(arguments map[string]Any) !mcp.ToolCallResult { - file_path := arguments['file_path'].str() - struct_name := arguments['struct_name'].str() - result := rust.get_struct_from_file(file_path, struct_name) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} - -// --- Get Struct from Module Tool --- - -// Specification for get_struct_from_module tool -const get_struct_from_module_spec = mcp.Tool{ - name: 'get_struct_from_module' - description: 'Get the declaration of a Rust struct from a specified module path (directory or file).' - input_schema: jsonschema.Schema{ - typ: 'object' - properties: { - 'module_path': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: 'Path to the Rust module directory or file.' - }) - 'struct_name': jsonschema.SchemaRef(jsonschema.Schema{ - typ: 'string' - description: "Name of the struct to retrieve (e.g., 'MyStruct')." - }) - } - required: ['module_path', 'struct_name'] - } -} - -// Handler for get_struct_from_module -pub fn get_struct_from_module_handler(arguments map[string]Any) !mcp.ToolCallResult { - module_path := arguments['module_path'].str() - struct_name := arguments['struct_name'].str() - result := rust.get_struct_from_module(module_path, struct_name) or { - return mcp.error_tool_call_result(err) - } - return mcp.ToolCallResult{ - is_error: false - content: mcp.result_to_mcp_tool_contents[string](result) - } -} diff --git a/libarchive/openrpc_remove/.gitignore b/libarchive/openrpc_remove/.gitignore deleted file mode 100644 index c62db7b9..00000000 --- a/libarchive/openrpc_remove/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -server -job_client \ No newline at end of file diff --git a/libarchive/openrpc_remove/examples/job_client.vsh b/libarchive/openrpc_remove/examples/job_client.vsh deleted file mode 100755 index 26270b54..00000000 --- a/libarchive/openrpc_remove/examples/job_client.vsh +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.core.jobs.model -import net.websocket -import json -import rand -import time -import term - -const ws_url = 'ws://localhost:8080' - -// Helper function to send request and receive response -fn send_request(mut ws websocket.Client, request OpenRPCRequest) !OpenRPCResponse { - // Send request - request_json := json.encode(request) - println(request_json) - ws.write_string(request_json) or { - eprintln(term.red('Failed to send request: ${err}')) - return err - } - - // Wait for response - mut msg := ws.read_next_message() or { - eprintln(term.red('Failed to read response: ${err}')) - return err - } - - if msg.opcode != websocket.OPCode.text_frame { - return error('Invalid response type: expected text frame') - } - - response_text := msg.payload.bytestr() - - // Parse response - response := json.decode(OpenRPCResponse, response_text) or { - eprintln(term.red('Failed to decode response: ${err}')) - return err - } - return response -} - -// OpenRPC request/response structures (copied from handler.v) -struct OpenRPCRequest { - jsonrpc string @[required] - method string @[required] - params []string - id int @[required] -} - -struct OpenRPCResponse { - jsonrpc string @[required] - result string - error string - id int @[required] -} - -// Initialize and configure WebSocket client -fn init_client() !&websocket.Client { - mut ws := websocket.new_client(ws_url)! - - ws.on_open(fn (mut ws websocket.Client) ! { - println(term.green('Connected to WebSocket server and ready...')) - }) - - ws.on_error(fn (mut ws websocket.Client, err string) ! { - eprintln(term.red('WebSocket error: ${err}')) - }) - - ws.on_close(fn (mut ws websocket.Client, code int, reason string) ! { - println(term.yellow('WebSocket connection closed: ${reason}')) - }) - - ws.on_message(fn (mut ws websocket.Client, msg &websocket.Message) ! { - if msg.payload.len > 0 { - println(term.blue('Received message: ${msg.payload.bytestr()}')) - } - }) - - ws.connect() or { - eprintln(term.red('Failed to connect: ${err}')) - return err - } - - spawn ws.listen() - return ws -} - -// Main client logic -mut ws := init_client()! -defer { - ws.close(1000, 'normal') or { eprintln(term.red('Error closing connection: ${err}')) } -} -println(term.green('Connected to ${ws_url}')) - -// Create a new job -println(term.blue('\nCreating new job...')) -new_job := send_request(mut ws, OpenRPCRequest{ - jsonrpc: '2.0' - method: 'job.new' - params: []string{} - id: rand.i32_in_range(1, 10000000)! -}) or { - eprintln(term.red('Failed to create new job: ${err}')) - exit(1) -} -println(term.green('Created new job:')) -println(json.encode_pretty(new_job)) - -// Parse job from response -job := json.decode(model.Job, new_job.result) or { - eprintln(term.red('Failed to parse job: ${err}')) - exit(1) -} - -// Set job properties -println(term.blue('\nSetting job properties...')) -mut updated_job := job -updated_job.guid = 'test-job-1' -updated_job.actor = 'vm_manager' -updated_job.action = 'start' -updated_job.params = { - 'name': 'test-vm' - 'memory': '2048' -} - -// Save job -set_response := send_request(mut ws, OpenRPCRequest{ - jsonrpc: '2.0' - method: 'job.set' - params: [json.encode(updated_job)] - id: rand.int() -}) or { - eprintln(term.red('Failed to save job: ${err}')) - exit(1) -} -println(term.green('Saved job:')) -println(json.encode_pretty(set_response)) - -// Update job status to running -println(term.blue('\nUpdating job status...')) -update_response := send_request(mut ws, OpenRPCRequest{ - jsonrpc: '2.0' - method: 'job.update_status' - params: ['test-job-1', 'running'] - id: rand.int() -}) or { - eprintln(term.red('Failed to update job status: ${err}')) - exit(1) -} -println(term.green('Updated job status:')) -println(json.encode_pretty(update_response)) - -// Get job to verify changes -println(term.blue('\nRetrieving job...')) -get_response := send_request(mut ws, OpenRPCRequest{ - jsonrpc: '2.0' - method: 'job.get' - params: ['test-job-1'] - id: rand.int() -}) or { - eprintln(term.red('Failed to retrieve job: ${err}')) - exit(1) -} -println(term.green('Retrieved job:')) -println(json.encode_pretty(get_response)) - -// List all jobs -println(term.blue('\nListing all jobs...')) -list_response := send_request(mut ws, OpenRPCRequest{ - jsonrpc: '2.0' - method: 'job.list' - params: []string{} - id: rand.int() -}) or { - eprintln(term.red('Failed to list jobs: ${err}')) - exit(1) -} -println(term.green('All jobs:')) -println(json.encode_pretty(list_response)) diff --git a/libarchive/openrpc_remove/examples/server.vsh b/libarchive/openrpc_remove/examples/server.vsh deleted file mode 100755 index 04678032..00000000 --- a/libarchive/openrpc_remove/examples/server.vsh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run - -import incubaid.herolib.core.jobs.openrpc -import incubaid.herolib.core.jobs.model -import time -import sync -import os - -fn start_rpc_server(mut wg sync.WaitGroup) ! { - defer { wg.done() } - - // Create OpenRPC server - openrpc.server_start()! -} - -fn start_ws_server(mut wg sync.WaitGroup) ! { - defer { wg.done() } - - // Get port from environment variable or use default - port := if ws_port := os.getenv_opt('WS_PORT') { - ws_port.int() - } else { - 8080 - } - - // Create and start WebSocket server - mut ws_server := openrpc.new_ws_server(port)! - ws_server.start()! -} - -// Create wait group for servers -mut wg := sync.new_waitgroup() -wg.add(2) - -// Start servers in separate threads -spawn start_rpc_server(mut wg) -spawn start_ws_server(mut wg) - -// Wait for servers to finish (they run forever) -wg.wait() diff --git a/libarchive/openrpc_remove/factory.v b/libarchive/openrpc_remove/factory.v deleted file mode 100644 index f853a135..00000000 --- a/libarchive/openrpc_remove/factory.v +++ /dev/null @@ -1,27 +0,0 @@ -module openrpc - -import incubaid.herolib.core.redisclient -import incubaid.herolib.core.jobs.model - -// Generic OpenRPC server that handles all managers -pub struct OpenRPCServer { -mut: - redis &redisclient.Redis - queue &redisclient.RedisQueue - runner &model.HeroRunner -} - -// Create new OpenRPC server with Redis connection -pub fn server_start() ! { - redis := redisclient.core_get()! - mut runner := model.new()! - mut s := &OpenRPCServer{ - redis: redis - queue: &redisclient.RedisQueue{ - key: rpc_queue - redis: redis - } - runner: runner - } - s.start()! -} diff --git a/libarchive/openrpc_remove/handler.v b/libarchive/openrpc_remove/handler.v deleted file mode 100644 index 3a9cb840..00000000 --- a/libarchive/openrpc_remove/handler.v +++ /dev/null @@ -1,68 +0,0 @@ -module openrpc - -import incubaid.herolib.core.redisclient -import json - -// Start the server and listen for requests -pub fn (mut s OpenRPCServer) start() ! { - println('Starting OpenRPC server.') - - for { - // Get message from queue - msg := s.queue.get(5000)! - - if msg.len == 0 { - println("queue '${rpc_queue}' empty") - continue - } - - println("process '${msg}'") - - // Parse OpenRPC request - request := json.decode(OpenRPCRequest, msg) or { - println('Error decoding request: ${err}') - continue - } - - // Process request with appropriate handler - response := s.handle_request(request)! - - // Send response back to Redis using response queue - response_json := json.encode(response) - key := '${rpc_queue}:${request.id}' - println('response: \n${response}\n put on return queue ${key} ') - mut response_queue := &redisclient.RedisQueue{ - key: key - redis: s.redis - } - response_queue.add(response_json)! - } -} - -// Get the handler for a specific method based on its prefix -fn (mut s OpenRPCServer) handle_request(request OpenRPCRequest) !OpenRPCResponse { - method := request.method.to_lower() - println("process: method: '${method}'") - if method.starts_with('job.') { - return s.handle_request_job(request) or { - return rpc_response_error(request.id, 'error in request job:\n${err}') - } - } - if method.starts_with('agent.') { - return s.handle_request_agent(request) or { - return rpc_response_error(request.id, 'error in request agent:\n${err}') - } - } - if method.starts_with('group.') { - return s.handle_request_group(request) or { - return rpc_response_error(request.id, 'error in request group:\n${err}') - } - } - if method.starts_with('service.') { - return s.handle_request_service(request) or { - return rpc_response_error(request.id, 'error in request service:\n${err}') - } - } - - return rpc_response_error(request.id, 'Could not find handler for ${method}') -} diff --git a/libarchive/openrpc_remove/handler_agent_manager.v b/libarchive/openrpc_remove/handler_agent_manager.v deleted file mode 100644 index 8776bde5..00000000 --- a/libarchive/openrpc_remove/handler_agent_manager.v +++ /dev/null @@ -1,71 +0,0 @@ -module openrpc - -import incubaid.herolib.core.jobs.model -import json - -pub fn (mut h OpenRPCServer) handle_request_agent(request OpenRPCRequest) !OpenRPCResponse { - mut response := rpc_response_new(request.id) - - method := request.method.all_after_first('agent.') - - println("request agent:'${method}'") - - match method { - 'new' { - agent := h.runner.agents.new() - response.result = json.encode(agent) - } - 'set' { - if request.params.len < 1 { - return error('Missing agent parameter') - } - agent := json.decode(model.Agent, request.params[0])! - h.runner.agents.set(agent)! - response.result = 'true' - } - 'get' { - if request.params.len < 1 { - return error('Missing pubkey parameter') - } - agent := h.runner.agents.get(request.params[0])! - response.result = json.encode(agent) - } - 'list' { - agents := h.runner.agents.list()! - response.result = json.encode(agents) - } - 'delete' { - if request.params.len < 1 { - return error('Missing pubkey parameter') - } - h.runner.agents.delete(request.params[0])! - response.result = 'true' - } - 'update_status' { - if request.params.len < 2 { - return error('Missing pubkey or status parameters') - } - status := match request.params[1] { - 'ok' { model.AgentState.ok } - 'down' { model.AgentState.down } - 'error' { model.AgentState.error } - 'halted' { model.AgentState.halted } - else { return error('Invalid status: ${request.params[1]}') } - } - h.runner.agents.update_status(request.params[0], status)! - response.result = 'true' - } - 'get_by_service' { - if request.params.len < 2 { - return error('Missing actor or action parameters') - } - agents := h.runner.agents.get_by_service(request.params[0], request.params[1])! - response.result = json.encode(agents) - } - else { - return error('Unknown method: ${request.method}') - } - } - - return response -} diff --git a/libarchive/openrpc_remove/handler_group_manager.v b/libarchive/openrpc_remove/handler_group_manager.v deleted file mode 100644 index 0b96b039..00000000 --- a/libarchive/openrpc_remove/handler_group_manager.v +++ /dev/null @@ -1,68 +0,0 @@ -module openrpc - -import incubaid.herolib.core.jobs.model -import json - -pub fn (mut h OpenRPCServer) handle_request_group(request OpenRPCRequest) !OpenRPCResponse { - mut response := rpc_response_new(request.id) - method := request.method.all_after_first('group.') - println("request group:'${method}'") - match method { - 'new' { - group := h.runner.groups.new() - response.result = json.encode(group) - } - 'set' { - if request.params.len < 1 { - return error('Missing group parameter') - } - group := json.decode(model.Group, request.params[0])! - h.runner.groups.set(group)! - response.result = 'true' - } - 'get' { - if request.params.len < 1 { - return error('Missing guid parameter') - } - group := h.runner.groups.get(request.params[0])! - response.result = json.encode(group) - } - 'list' { - groups := h.runner.groups.list()! - response.result = json.encode(groups) - } - 'delete' { - if request.params.len < 1 { - return error('Missing guid parameter') - } - h.runner.groups.delete(request.params[0])! - response.result = 'true' - } - 'add_member' { - if request.params.len < 2 { - return error('Missing guid or member parameters') - } - h.runner.groups.add_member(request.params[0], request.params[1])! - response.result = 'true' - } - 'remove_member' { - if request.params.len < 2 { - return error('Missing guid or member parameters') - } - h.runner.groups.remove_member(request.params[0], request.params[1])! - response.result = 'true' - } - 'get_user_groups' { - if request.params.len < 1 { - return error('Missing user_pubkey parameter') - } - groups := h.runner.groups.get_user_groups(request.params[0])! - response.result = json.encode(groups) - } - else { - return error('Unknown method: ${request.method}') - } - } - - return response -} diff --git a/libarchive/openrpc_remove/handler_job_manager.v b/libarchive/openrpc_remove/handler_job_manager.v deleted file mode 100644 index 968141e8..00000000 --- a/libarchive/openrpc_remove/handler_job_manager.v +++ /dev/null @@ -1,66 +0,0 @@ -module openrpc - -import incubaid.herolib.core.jobs.model -import json - -pub fn (mut h OpenRPCServer) handle_request_job(request OpenRPCRequest) !OpenRPCResponse { - mut response := rpc_response_new(request.id) - - method := request.method.all_after_first('job.') - println("request job:'${method}'") - println(request) - match method { - 'new' { - job := h.runner.jobs.new() - response.result = json.encode(job) - } - 'set' { - if request.params.len < 1 { - return error('Missing job parameter') - } - job := json.decode(model.Job, request.params[0])! - h.runner.jobs.set(job)! - response.result = 'true' - } - 'get' { - if request.params.len < 1 { - return error('Missing guid parameter') - } - job := h.runner.jobs.get(request.params[0])! - response.result = json.encode(job) - } - 'list' { - jobs := h.runner.jobs.list()! - response.result = json.encode(jobs) - } - 'delete' { - if request.params.len < 1 { - return error('Missing guid parameter') - } - h.runner.jobs.delete(request.params[0])! - response.result = 'true' - } - 'update_status' { - if request.params.len < 2 { - return error('Missing guid or status parameters') - } - status := match request.params[1] { - 'created' { model.Status.created } - 'scheduled' { model.Status.scheduled } - 'planned' { model.Status.planned } - 'running' { model.Status.running } - 'error' { model.Status.error } - 'ok' { model.Status.ok } - else { return error('Invalid status: ${request.params[1]}') } - } - h.runner.jobs.update_status(request.params[0], status)! - job := h.runner.jobs.get(request.params[0])! // Get updated job to return - response.result = json.encode(job) - } - else { - return error('Unknown method: ${request.method}') - } - } - - return response -} diff --git a/libarchive/openrpc_remove/handler_service_manager.v b/libarchive/openrpc_remove/handler_service_manager.v deleted file mode 100644 index 6371658e..00000000 --- a/libarchive/openrpc_remove/handler_service_manager.v +++ /dev/null @@ -1,80 +0,0 @@ -module openrpc - -import incubaid.herolib.core.jobs.model -import json - -pub fn (mut h OpenRPCServer) handle_request_service(request OpenRPCRequest) !OpenRPCResponse { - mut response := rpc_response_new(request.id) - method := request.method.all_after_first('service.') - println("request service:'${method}'") - match method { - 'new' { - service := h.runner.services.new() - response.result = json.encode(service) - } - 'set' { - if request.params.len < 1 { - return error('Missing service parameter') - } - service := json.decode(model.Service, request.params[0])! - h.runner.services.set(service)! - response.result = 'true' - } - 'get' { - if request.params.len < 1 { - return error('Missing actor parameter') - } - service := h.runner.services.get(request.params[0])! - response.result = json.encode(service) - } - 'list' { - services := h.runner.services.list()! - response.result = json.encode(services) - } - 'delete' { - if request.params.len < 1 { - return error('Missing actor parameter') - } - h.runner.services.delete(request.params[0])! - response.result = 'true' - } - 'update_status' { - if request.params.len < 2 { - return error('Missing actor or status parameters') - } - status := match request.params[1] { - 'ok' { model.ServiceState.ok } - 'down' { model.ServiceState.down } - 'error' { model.ServiceState.error } - 'halted' { model.ServiceState.halted } - else { return error('Invalid status: ${request.params[1]}') } - } - h.runner.services.update_status(request.params[0], status)! - response.result = 'true' - } - 'get_by_action' { - if request.params.len < 1 { - return error('Missing action parameter') - } - services := h.runner.services.get_by_action(request.params[0])! - response.result = json.encode(services) - } - 'check_access' { - if request.params.len < 4 { - return error('Missing parameters: requires actor, action, user_pubkey, and groups') - } - // Parse groups array from JSON string - groups := json.decode([]string, request.params[3])! - has_access := h.runner.services.check_access(request.params[0], // actor - request.params[1], // action - request.params[2], // user_pubkey - groups)! - response.result = json.encode(has_access) - } - else { - return error('Unknown method: ${request.method}') - } - } - - return response -} diff --git a/libarchive/openrpc_remove/model.v b/libarchive/openrpc_remove/model.v deleted file mode 100644 index 47078b7a..00000000 --- a/libarchive/openrpc_remove/model.v +++ /dev/null @@ -1,37 +0,0 @@ -module openrpc - -// Generic OpenRPC request/response structures -pub struct OpenRPCRequest { -pub mut: - jsonrpc string @[required] - method string @[required] - params []string - id int @[required] -} - -pub struct OpenRPCResponse { -pub mut: - jsonrpc string @[required] - result string - error string - id int @[required] -} - -fn rpc_response_new(id int) OpenRPCResponse { - mut response := OpenRPCResponse{ - jsonrpc: '2.0' - id: id - } - return response -} - -fn rpc_response_error(id int, errormsg string) OpenRPCResponse { - mut response := OpenRPCResponse{ - jsonrpc: '2.0' - id: id - error: errormsg - } - return response -} - -const rpc_queue = 'herorunner:q:rpc' diff --git a/libarchive/openrpc_remove/specs/agent_manager_openrpc.json b/libarchive/openrpc_remove/specs/agent_manager_openrpc.json deleted file mode 100644 index a87d4602..00000000 --- a/libarchive/openrpc_remove/specs/agent_manager_openrpc.json +++ /dev/null @@ -1,302 +0,0 @@ -{ - "openrpc": "1.2.6", - "info": { - "title": "AgentManager Service", - "version": "1.0.0", - "description": "OpenRPC specification for the AgentManager module and its methods." - }, - "methods": [ - { - "name": "new", - "summary": "Create a new Agent instance", - "description": "Returns a new Agent with default or empty fields set. Caller can then fill in details.", - "params": [], - "result": { - "name": "Agent", - "description": "A freshly created Agent object.", - "schema": { - "$ref": "#/components/schemas/Agent" - } - } - }, - { - "name": "set", - "summary": "Add or update an Agent in the system", - "description": "Stores an Agent in Redis by pubkey. Overwrites any previous entry with the same pubkey.", - "params": [ - { - "name": "agent", - "description": "The Agent instance to be added or updated.", - "required": true, - "schema": { - "$ref": "#/components/schemas/Agent" - } - } - ], - "result": { - "name": "success", - "description": "Indicates success. No data returned on success.", - "schema": { - "type": "boolean" - } - } - }, - { - "name": "get", - "summary": "Retrieve an Agent by its public key", - "description": "Looks up a single Agent using its pubkey.", - "params": [ - { - "name": "pubkey", - "description": "The public key to look up.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "Agent", - "description": "The Agent that was requested, if found.", - "schema": { - "$ref": "#/components/schemas/Agent" - } - } - }, - { - "name": "list", - "summary": "List all Agents", - "description": "Returns an array of all known Agents.", - "params": [], - "result": { - "name": "Agents", - "description": "A list of all Agents in the system.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Agent" - } - } - } - }, - { - "name": "delete", - "summary": "Delete an Agent by its public key", - "description": "Removes an Agent from the system by pubkey.", - "params": [ - { - "name": "pubkey", - "description": "The public key of the Agent to be deleted.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "success", - "description": "Indicates success. No data returned on success.", - "schema": { - "type": "boolean" - } - } - }, - { - "name": "update_status", - "summary": "Update the status of an Agent", - "description": "Updates only the status field of the specified Agent.", - "params": [ - { - "name": "pubkey", - "description": "Public key of the Agent to update.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "status", - "description": "The new status to set for the Agent.", - "required": true, - "schema": { - "$ref": "#/components/schemas/AgentState" - } - } - ], - "result": { - "name": "success", - "description": "Indicates success. No data returned on success.", - "schema": { - "type": "boolean" - } - } - }, - { - "name": "get_by_service", - "summary": "Retrieve all Agents that provide a specific service action", - "description": "Filters Agents by matching actor and action in any of their declared services.", - "params": [ - { - "name": "actor", - "description": "The actor name to match.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "action", - "description": "The action name to match.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "Agents", - "description": "A list of Agents that match the specified service actor and action.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Agent" - } - } - } - } - ], - "components": { - "schemas": { - "Agent": { - "type": "object", - "properties": { - "pubkey": { - "type": "string", - "description": "Public key (ed25519) of the Agent." - }, - "address": { - "type": "string", - "description": "Network address or domain where the Agent can be reached." - }, - "port": { - "type": "integer", - "description": "Network port for the Agent (default: 9999)." - }, - "description": { - "type": "string", - "description": "Optional human-readable description of the Agent." - }, - "status": { - "$ref": "#/components/schemas/AgentStatus" - }, - "services": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AgentService" - }, - "description": "List of public services provided by the Agent." - }, - "signature": { - "type": "string", - "description": "Signature (by the Agent's private key) of address+port+description+status." - } - }, - "required": ["pubkey", "status", "services"] - }, - "AgentStatus": { - "type": "object", - "properties": { - "guid": { - "type": "string", - "description": "Unique ID for the job or session." - }, - "timestamp_first": { - "$ref": "#/components/schemas/OurTime", - "description": "Timestamp when this Agent first came online." - }, - "timestamp_last": { - "$ref": "#/components/schemas/OurTime", - "description": "Timestamp of the last heartbeat or update from the Agent." - }, - "status": { - "$ref": "#/components/schemas/AgentState" - } - } - }, - "AgentService": { - "type": "object", - "properties": { - "actor": { - "type": "string", - "description": "The actor name providing the service." - }, - "actions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AgentServiceAction" - }, - "description": "List of actions available for this service." - }, - "description": { - "type": "string", - "description": "Optional human-readable description for the service." - }, - "status": { - "$ref": "#/components/schemas/AgentServiceState" - } - }, - "required": ["actor", "actions", "status"] - }, - "AgentServiceAction": { - "type": "object", - "properties": { - "action": { - "type": "string", - "description": "Action name." - }, - "description": { - "type": "string", - "description": "Optional description of this action." - }, - "params": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Dictionary of parameter names to parameter descriptions." - }, - "params_example": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Example values for the parameters." - }, - "status": { - "$ref": "#/components/schemas/AgentServiceState" - }, - "public": { - "type": "boolean", - "description": "Indicates if the action is publicly accessible to all or restricted." - } - }, - "required": ["action", "status", "public"] - }, - "AgentState": { - "type": "string", - "enum": ["ok", "down", "error", "halted"], - "description": "Possible states of an Agent." - }, - "AgentServiceState": { - "type": "string", - "enum": ["ok", "down", "error", "halted"], - "description": "Possible states of an Agent service or action." - }, - "OurTime": { - "type": "string", - "format": "date-time", - "description": "Represents a date/time or timestamp value." - } - } - } -} diff --git a/libarchive/openrpc_remove/specs/group_manager_openrpc.json b/libarchive/openrpc_remove/specs/group_manager_openrpc.json deleted file mode 100644 index 0a2f33fe..00000000 --- a/libarchive/openrpc_remove/specs/group_manager_openrpc.json +++ /dev/null @@ -1,218 +0,0 @@ -{ - "openrpc": "1.2.6", - "info": { - "title": "Group DBSession API", - "version": "1.0.0", - "description": "An OpenRPC specification for Group DBSession methods" - }, - "servers": [ - { - "name": "Local", - "url": "http://localhost:8080" - } - ], - "methods": [ - { - "name": "GroupManager.new", - "summary": "Create a new (in-memory) Group instance", - "description": "Creates a new group object. Note that this does NOT store it in Redis. The caller must set the group’s GUID and then call `GroupManager.set` if they wish to persist it.", - "params": [], - "result": { - "name": "group", - "description": "The newly-created group instance", - "schema": { - "$ref": "#/components/schemas/Group" - } - } - }, - { - "name": "GroupManager.set", - "summary": "Add or update a Group in Redis", - "description": "Stores the specified group in Redis using the group’s GUID as the key.", - "params": [ - { - "name": "group", - "description": "The group object to store", - "schema": { - "$ref": "#/components/schemas/Group" - } - } - ], - "result": { - "name": "result", - "description": "No return value", - "schema": { - "type": "null" - } - } - }, - { - "name": "GroupManager.get", - "summary": "Retrieve a Group by GUID", - "description": "Fetches the group from Redis using the provided GUID.", - "params": [ - { - "name": "guid", - "description": "The group’s unique identifier", - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "group", - "description": "The requested group", - "schema": { - "$ref": "#/components/schemas/Group" - } - } - }, - { - "name": "GroupManager.list", - "summary": "List all Groups", - "description": "Returns an array containing all groups stored in Redis.", - "params": [], - "result": { - "name": "groups", - "description": "All currently stored groups", - "schema": { - "$ref": "#/components/schemas/GroupList" - } - } - }, - { - "name": "GroupManager.delete", - "summary": "Delete a Group by GUID", - "description": "Removes the specified group from Redis by its GUID.", - "params": [ - { - "name": "guid", - "description": "The group’s unique identifier", - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "result", - "description": "No return value", - "schema": { - "type": "null" - } - } - }, - { - "name": "GroupManager.add_member", - "summary": "Add a member to a Group", - "description": "Adds a user pubkey or another group’s GUID to the member list of the specified group. Does not add duplicates.", - "params": [ - { - "name": "guid", - "description": "The target group’s unique identifier", - "schema": { - "type": "string" - } - }, - { - "name": "member", - "description": "Pubkey or group GUID to be added to the group", - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "result", - "description": "No return value", - "schema": { - "type": "null" - } - } - }, - { - "name": "GroupManager.remove_member", - "summary": "Remove a member from a Group", - "description": "Removes a user pubkey or another group’s GUID from the member list of the specified group.", - "params": [ - { - "name": "guid", - "description": "The target group’s unique identifier", - "schema": { - "type": "string" - } - }, - { - "name": "member", - "description": "Pubkey or group GUID to be removed from the group", - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "result", - "description": "No return value", - "schema": { - "type": "null" - } - } - }, - { - "name": "GroupManager.get_user_groups", - "summary": "List Groups that a user belongs to (directly or indirectly)", - "description": "Checks each group (and nested groups) to see if the user pubkey is a member, returning all groups in which the user is included (including membership through nested groups).", - "params": [ - { - "name": "user_pubkey", - "description": "The pubkey of the user to check", - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "groups", - "description": "A list of groups to which the user belongs", - "schema": { - "$ref": "#/components/schemas/GroupList" - } - } - } - ], - "components": { - "schemas": { - "Group": { - "type": "object", - "properties": { - "guid": { - "type": "string", - "description": "Unique ID for the group" - }, - "name": { - "type": "string", - "description": "Name of the group" - }, - "description": { - "type": "string", - "description": "Optional description of the group" - }, - "members": { - "type": "array", - "description": "List of user pubkeys or other group GUIDs", - "items": { - "type": "string" - } - } - }, - "required": ["guid", "members"] - }, - "GroupList": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Group" - } - } - } - } - } - \ No newline at end of file diff --git a/libarchive/openrpc_remove/specs/job_manager_openrpc.json b/libarchive/openrpc_remove/specs/job_manager_openrpc.json deleted file mode 100644 index c27efc9a..00000000 --- a/libarchive/openrpc_remove/specs/job_manager_openrpc.json +++ /dev/null @@ -1,304 +0,0 @@ -{ - "openrpc": "1.2.6", - "info": { - "title": "JobManager OpenRPC Specification", - "version": "1.0.0", - "description": "OpenRPC specification for the JobManager module which handles job operations." - }, - "servers": [ - { - "name": "Local", - "url": "http://localhost:8080/rpc" - } - ], - "methods": [ - { - "name": "newJob", - "summary": "Create a new Job instance", - "description": "Creates a new Job with default/empty values. The GUID is left empty for the caller to fill.", - "params": [], - "result": { - "name": "job", - "description": "A newly created Job object, not yet persisted.", - "schema": { - "$ref": "#/components/schemas/Job" - } - } - }, - { - "name": "setJob", - "summary": "Add or update a Job in the system (Redis)", - "description": "Persists the given Job into the data store. If the GUID already exists, the existing job is overwritten.", - "params": [ - { - "name": "job", - "description": "The Job object to store or update.", - "required": true, - "schema": { - "$ref": "#/components/schemas/Job" - } - } - ], - "result": { - "name": "success", - "description": "Indicates if the operation was successful.", - "schema": { - "type": "boolean" - } - } - }, - { - "name": "getJob", - "summary": "Retrieve a Job by its GUID", - "description": "Fetches an existing Job from the data store using its unique GUID.", - "params": [ - { - "name": "guid", - "description": "The GUID of the Job to retrieve.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "job", - "description": "The retrieved Job object.", - "schema": { - "$ref": "#/components/schemas/Job" - } - } - }, - { - "name": "listJobs", - "summary": "List all Jobs", - "description": "Returns an array of all Jobs present in the data store.", - "params": [], - "result": { - "name": "jobs", - "description": "Array of all Job objects found.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Job" - } - } - } - }, - { - "name": "deleteJob", - "summary": "Remove a Job by its GUID", - "description": "Deletes a specific Job from the data store by its GUID.", - "params": [ - { - "name": "guid", - "description": "The GUID of the Job to delete.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "success", - "description": "Indicates if the job was successfully deleted.", - "schema": { - "type": "boolean" - } - } - }, - { - "name": "updateJobStatus", - "summary": "Update the status of a Job", - "description": "Sets the status field of a Job in the data store.", - "params": [ - { - "name": "guid", - "description": "The GUID of the Job to update.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "status", - "description": "The new status for the Job.", - "required": true, - "schema": { - "$ref": "#/components/schemas/Status" - } - } - ], - "result": { - "name": "job", - "description": "The updated Job object with new status applied.", - "schema": { - "$ref": "#/components/schemas/Job" - } - } - } - ], - "components": { - "schemas": { - "Job": { - "type": "object", - "properties": { - "guid": { - "type": "string", - "description": "Unique ID for the Job." - }, - "agents": { - "type": "array", - "description": "Public keys of the agent(s) which will execute the command.", - "items": { - "type": "string" - } - }, - "source": { - "type": "string", - "description": "Pubkey of the agent who requested the job." - }, - "circle": { - "type": "string", - "description": "Digital-life circle name this Job belongs to.", - "default": "default" - }, - "context": { - "type": "string", - "description": "High-level context for the Job inside a circle.", - "default": "default" - }, - "actor": { - "type": "string", - "description": "Actor name that will handle the Job (e.g. `vm_manager`)." - }, - "action": { - "type": "string", - "description": "Action to be taken by the actor (e.g. `start`)." - }, - "params": { - "type": "object", - "description": "Key-value parameters for the action to be performed.", - "additionalProperties": { - "type": "string" - } - }, - "timeout_schedule": { - "type": "integer", - "description": "Timeout (in seconds) before the job is picked up by an agent.", - "default": 60 - }, - "timeout": { - "type": "integer", - "description": "Timeout (in seconds) for the job to complete.", - "default": 3600 - }, - "log": { - "type": "boolean", - "description": "Whether to log job details.", - "default": true - }, - "ignore_error": { - "type": "boolean", - "description": "If true, job errors do not cause an exception to be raised." - }, - "ignore_error_codes": { - "type": "array", - "description": "Array of error codes to ignore.", - "items": { - "type": "integer" - } - }, - "debug": { - "type": "boolean", - "description": "If true, additional debug information is provided.", - "default": false - }, - "retry": { - "type": "integer", - "description": "Number of retries allowed on error.", - "default": 0 - }, - "status": { - "$ref": "#/components/schemas/JobStatus" - }, - "dependencies": { - "type": "array", - "description": "List of job dependencies that must complete before this job executes.", - "items": { - "$ref": "#/components/schemas/JobDependency" - } - } - }, - "required": [ - "guid", - "status" - ] - }, - "JobStatus": { - "type": "object", - "properties": { - "guid": { - "type": "string", - "description": "Unique ID for the Job (mirrors the parent job GUID)." - }, - "created": { - "type": "string", - "format": "date-time", - "description": "When the job was created." - }, - "start": { - "type": "string", - "format": "date-time", - "description": "When the job was picked up to start." - }, - "end": { - "type": "string", - "format": "date-time", - "description": "When the job ended." - }, - "status": { - "$ref": "#/components/schemas/Status" - } - }, - "required": [ - "guid", - "created", - "status" - ] - }, - "JobDependency": { - "type": "object", - "properties": { - "guid": { - "type": "string", - "description": "Unique ID of the Job this dependency points to." - }, - "agents": { - "type": "array", - "description": "Possible agent(s) who can execute the dependency.", - "items": { - "type": "string" - } - } - }, - "required": [ - "guid" - ] - }, - "Status": { - "type": "string", - "enum": [ - "created", - "scheduled", - "planned", - "running", - "error", - "ok" - ], - "description": "Enumerates the possible states of a Job." - } - } - } - } - \ No newline at end of file diff --git a/libarchive/openrpc_remove/specs/service_manager_openrpc.json b/libarchive/openrpc_remove/specs/service_manager_openrpc.json deleted file mode 100644 index c551787d..00000000 --- a/libarchive/openrpc_remove/specs/service_manager_openrpc.json +++ /dev/null @@ -1,301 +0,0 @@ -{ - "openrpc": "1.2.6", - "info": { - "title": "ServiceManager API", - "version": "1.0.0", - "description": "OpenRPC 2.0 spec for managing services with ServiceManager." - }, - "servers": [ - { - "name": "Local", - "url": "http://localhost:8080" - } - ], - "methods": [ - { - "name": "ServiceManager_new", - "summary": "Create a new Service instance (not saved to Redis yet).", - "description": "Creates and returns a new empty Service object with default values. The `actor` field remains empty until the caller sets it.", - "params": [], - "result": { - "name": "service", - "$ref": "#/components/schemas/Service" - } - }, - { - "name": "ServiceManager_set", - "summary": "Add or update a Service in Redis.", - "description": "Stores the Service in Redis, identified by its `actor` property.", - "params": [ - { - "name": "service", - "schema": { - "$ref": "#/components/schemas/Service" - } - } - ], - "result": { - "name": "success", - "schema": { - "type": "boolean", - "description": "True if operation succeeds." - } - } - }, - { - "name": "ServiceManager_get", - "summary": "Retrieve a Service by actor name.", - "description": "Gets the Service object from Redis using the given actor name.", - "params": [ - { - "name": "actor", - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "service", - "$ref": "#/components/schemas/Service" - } - }, - { - "name": "ServiceManager_list", - "summary": "List all Services.", - "description": "Returns an array of all Services stored in Redis.", - "params": [], - "result": { - "name": "services", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Service" - } - } - } - }, - { - "name": "ServiceManager_delete", - "summary": "Delete a Service by actor name.", - "description": "Removes the Service from Redis using the given actor name.", - "params": [ - { - "name": "actor", - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "success", - "schema": { - "type": "boolean" - } - } - }, - { - "name": "ServiceManager_update_status", - "summary": "Update the status of a given Service.", - "description": "Updates only the `status` field of a Service specified by its actor name.", - "params": [ - { - "name": "actor", - "schema": { - "type": "string" - } - }, - { - "name": "status", - "schema": { - "$ref": "#/components/schemas/ServiceState" - } - } - ], - "result": { - "name": "success", - "schema": { - "type": "boolean" - } - } - }, - { - "name": "ServiceManager_get_by_action", - "summary": "Retrieve Services by action name.", - "description": "Returns all Services that provide the specified action.", - "params": [ - { - "name": "action", - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "services", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Service" - } - } - } - }, - { - "name": "ServiceManager_check_access", - "summary": "Check if a user has access to a Service action.", - "description": "Verifies if a user (and any groups they belong to) has the right to invoke a specified action on a given Service.", - "params": [ - { - "name": "actor", - "schema": { - "type": "string" - } - }, - { - "name": "action", - "schema": { - "type": "string" - } - }, - { - "name": "user_pubkey", - "schema": { - "type": "string" - } - }, - { - "name": "groups", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - } - ], - "result": { - "name": "hasAccess", - "schema": { - "type": "boolean" - } - } - } - ], - "components": { - "schemas": { - "Service": { - "type": "object", - "properties": { - "actor": { - "type": "string", - "description": "The actor (unique name) providing the service." - }, - "actions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ServiceAction" - }, - "description": "A list of actions available in this service." - }, - "description": { - "type": "string", - "description": "Optional description of the service." - }, - "status": { - "$ref": "#/components/schemas/ServiceState", - "description": "The current state of the service." - }, - "acl": { - "$ref": "#/components/schemas/ACL", - "description": "An optional access control list for the entire service." - } - }, - "required": ["actor", "actions", "status"] - }, - "ServiceAction": { - "type": "object", - "properties": { - "action": { - "type": "string", - "description": "A unique identifier for the action." - }, - "description": { - "type": "string", - "description": "Optional description of this action." - }, - "params": { - "type": "object", - "description": "Parameter definitions for this action.", - "additionalProperties": { - "type": "string" - } - }, - "params_example": { - "type": "object", - "description": "Example parameters for this action.", - "additionalProperties": { - "type": "string" - } - }, - "acl": { - "$ref": "#/components/schemas/ACL", - "description": "Optional ACL specifically for this action." - } - }, - "required": ["action"] - }, - "ACL": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "A friendly name for the ACL." - }, - "ace": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ACE" - }, - "description": "A list of Access Control Entries." - } - }, - "required": ["ace"] - }, - "ACE": { - "type": "object", - "properties": { - "groups": { - "type": "array", - "items": { - "type": "string" - }, - "description": "List of group IDs that have this permission." - }, - "users": { - "type": "array", - "items": { - "type": "string" - }, - "description": "List of user public keys that have this permission." - }, - "right": { - "type": "string", - "description": "Permission type (e.g. 'read', 'write', 'admin', 'block')." - } - }, - "required": ["right"] - }, - "ServiceState": { - "type": "string", - "enum": [ - "ok", - "down", - "error", - "halted" - ], - "description": "Possible states of a service." - } - } - } - } - \ No newline at end of file diff --git a/libarchive/openrpc_remove/ws_server.v b/libarchive/openrpc_remove/ws_server.v deleted file mode 100644 index 74b974c9..00000000 --- a/libarchive/openrpc_remove/ws_server.v +++ /dev/null @@ -1,93 +0,0 @@ -module openrpc - -import net.websocket -import incubaid.herolib.core.redisclient -import json -import rand - -// WebSocket server that receives RPC requests -pub struct WSServer { -mut: - redis &redisclient.Redis - queue &redisclient.RedisQueue - port int = 8080 // Default port, can be configured -} - -// Create new WebSocket server -pub fn new_ws_server(port int) !&WSServer { - mut redis := redisclient.core_get()! - return &WSServer{ - redis: redis - queue: &redisclient.RedisQueue{ - key: rpc_queue - redis: redis - } - port: port - } -} - -// Start the WebSocket server -pub fn (mut s WSServer) start() ! { - mut ws_server := websocket.new_server(.ip, s.port, '') - - // Handle new WebSocket connections - ws_server.on_connect(fn (mut ws websocket.ServerClient) !bool { - println('New WebSocket client connected') - return true - })! - - // Handle client disconnections - ws_server.on_close(fn (mut ws websocket.Client, code int, reason string) ! { - println('WebSocket client disconnected (code: ${code}, reason: ${reason})') - }) - - // Handle incoming messages - ws_server.on_message(fn [mut s] (mut ws websocket.Client, msg &websocket.Message) ! { - if msg.opcode != .text_frame { - println('WebSocket unknown msg opcode (code: ${msg.opcode})') - return - } - - // Parse request - request := json.decode(OpenRPCRequest, msg.payload.bytestr()) or { - error_msg := '{"jsonrpc":"2.0","error":"Invalid JSON-RPC request","id":null}' - println(error_msg) - ws.write(error_msg.bytes(), websocket.OPCode.text_frame) or { panic(err) } - return - } - - // Generate unique request ID if not provided - mut req_id := request.id - if req_id == 0 { - req_id = rand.i32_in_range(1, 10000000)! - } - - println('WebSocket put on queue: \'${rpc_queue}\' (msg: ${msg.payload.bytestr()})') - // Send request to Redis queue - s.queue.add(msg.payload.bytestr())! - - returnkey := '${rpc_queue}:${req_id}' - mut queue_return := &redisclient.RedisQueue{ - key: returnkey - redis: s.redis - } - - // Wait for response - response := queue_return.get(30)! - if response.len < 2 { - error_msg := '{"jsonrpc":"2.0","error":"Timeout waiting for response","id":${req_id}}' - println('WebSocket error response (err: ${response})') - ws.write(error_msg.bytes(), websocket.OPCode.text_frame) or { panic(err) } - return - } - - println('WebSocket ok response (msg: ${response[1]})') - // Send response back to WebSocket client - response_str := response[1].str() - ws.write(response_str.bytes(), websocket.OPCode.text_frame) or { panic(err) } - }) - - // Start server - println('WebSocket server listening on port ${s.port}') - ws_server.listen() or { return error('Failed to start WebSocket server: ${err}') } -} diff --git a/libarchive/rhai/generate_rhai_example.v b/libarchive/rhai/generate_rhai_example.v deleted file mode 100644 index 43244dc4..00000000 --- a/libarchive/rhai/generate_rhai_example.v +++ /dev/null @@ -1,54 +0,0 @@ -module rhai - -import log -import incubaid.herolib.ai.escalayer - -pub struct WrapperGenerator { -pub: - function string - structs []string -} - -// given a list of rhai functions and structs, generate a Rhai example script -pub fn generate_rhai_example(functions []string, structs []string) !string { - mut task := escalayer.new_task( - name: 'generate_rhai_function_wrapper' - description: 'Create a single Rhai wrapper for a Rust function' - ) - - mut gen := WrapperGenerator{ - function: functions - structs: structs - } - - // Define a single unit task that handles everything - task.new_unit_task( - name: 'generate_rhai_function_wrapper' - prompt_function: gen.generate_rhai_function_wrapper_prompt - callback_function: gen.generate_rhai_function_wrapper_callback - base_model: escalayer.claude_3_sonnet // Use actual model identifier - retry_model: escalayer.claude_3_sonnet // Use actual model identifier - retry_count: 2 - ) - - return task.initiate('') -} - -pub fn (gen WrapperGenerator) generate_rhai_function_wrapper_prompt(input string) string { - return $tmpl('./prompts/generate_rhai_function_wrapper.md') -} - -// generate_rhai_function_wrapper_callback validates the generated Rhai wrapper. -// -// Args: -// rhai_wrapper (string): The generated wrapper code. -// -// Returns: -// !string: The validated wrapper code or an error. -pub fn (gen WrapperGenerator) generate_rhai_function_wrapper_callback(output string) !string { - verify_rhai_wrapper(gen.function, gen.structs, output) or { - log.error('Failed to verify, will retry ${err}') - return err - } - return output -} diff --git a/libarchive/rhai/generate_wrapper_module.v b/libarchive/rhai/generate_wrapper_module.v deleted file mode 100644 index 35df5669..00000000 --- a/libarchive/rhai/generate_wrapper_module.v +++ /dev/null @@ -1,36 +0,0 @@ -module rhai - -import incubaid.herolib.lang.rust -import os - -// generates rhai wrapper for given source rust code -pub fn generate_wrapper_module(name string, source string, destination string) !string { - source_pkg_info := rust.detect_source_package(source_path)! - code := rust.read_source_code(source)! - functions := get_functions(code) - structs := get_structs(code) - - rhai_functions := generate_rhai_function_wrappers(functions, structs) - - // engine registration functions templated in engine.rs - register_functions_rs := generate_rhai_register_functions(functions) - register_types_rs := generate_rhai_register_types(structs) - - pathlib.get_file(os.join_path(destination, 'cargo.toml'))! - .write($tmpl('./templates/cargo.toml')) - - pathlib.get_file(os.join_path(destination, 'src/engine.rs'))! - .write($tmpl('./templates/engine.rs')) - - pathlib.get_file(os.join_path(destination, 'src/lib.rs'))! - .write($tmpl('./templates/lib.rs')) - - pathlib.get_file(os.join_path(destination, 'src/wrapper.rs'))! - .write($tmpl('./templates/wrapper.rs')) - - pathlib.get_file(os.join_path(destination, 'examples/example.rs'))! - .write($tmpl('./templates/example.rs')) - - pathlib.get_file(os.join_path(destination, 'examples/example.rhai'))! - .write(generate_example_rhai_script(code)) -} diff --git a/libarchive/rhai/prompts/generate_rhai_function_wrapper.md b/libarchive/rhai/prompts/generate_rhai_function_wrapper.md deleted file mode 100644 index c5b4441c..00000000 --- a/libarchive/rhai/prompts/generate_rhai_function_wrapper.md +++ /dev/null @@ -1,538 +0,0 @@ -# Generate Single Rhai Wrapper Function - -You are an expert Rust and Rhai developer tasked with creating a Rhai wrapper function for a given Rust function signature. - -## Task - -Generate a single `pub fn` Rhai wrapper function based on the provided Rust function signature and associated struct definitions. - -**CRITICAL INSTRUCTION:** The generated Rhai wrapper function **MUST** call the original Rust function provided in the input. **DO NOT** reimplement the logic of the original function within the wrapper. The wrapper's sole purpose is to handle type conversions and error mapping between Rhai and Rust. - -## Input Rust Function - -```rust -@{gen.function} -``` - -## Input Rust Types - -Below are the struct declarations for types used in the function - -@for structure in gen.structs - ```rust - @{structure} - ``` -@end - -## Instructions - -1. **Analyze the Signature:** - * Identify the function/method name. - * Identify the input parameters and their types. - * Identify the return type. - * Determine if it's a method on a struct (e.g., `&self`, `&mut self`). - -2. **Define the Wrapper Signature:** - * The wrapper function name should generally match the original Rust function name (use snake_case). - * Input parameters should correspond to the Rust function's parameters. You might need to adjust types for Rhai compatibility (e.g., `&str` becomes `&str`, `String` becomes `String`, `Vec` might become `rhai::Array`, `HashMap` might become `rhai::Map`). - * If the original function is a method on a struct (e.g., `fn method(&self, ...) ` or `fn method_mut(&mut self, ...)`), the first parameter of the wrapper must be the receiver type (e.g., `mut? receiver: StructType`). Ensure the mutability matches. - * The return type **must** be `Result>`, where `T` is the Rhai-compatible equivalent of the original Rust function's return type. If the original function returns `Result`, `T` should be the Rhai-compatible version of `U`, and the error `E` should be mapped into `Box`. If the original returns `()`, use `Result<(), Box>`. If it returns a simple type `U`, use `Result>`. - -3. **Implement the Wrapper Body:** - * **Call the original function.** This is the most important step. - * Handle parameter type conversions if necessary (e.g., `i64` from Rhai to `i32` for Rust). - * If the original function returns `Result`, map the `Ok(T)` value and handle the `Err(E)` by converting it to `Box`, `Result`, `Result, ...>`) over `Result`. Only use `Dynamic` if the return type is truly variable or complex and cannot be easily represented otherwise. - * **Do NOT use the `#[rhai_fn]` attribute.** The function will be registered manually. - * Handle string type consistency (e.g., `String::from()` for literals if mixed with `format!`). - -### Error Handling - -Assume that the following function is available to use in the rhai wrapper body: - -```rust -// Helper functions for error conversion with improved context -fn [modulename]_error_to_rhai_error(result: Result) -> Result> {} -``` - -And feel free to use it like: -```rust -/// Create a new Container -pub fn container_new(name: &str) -> Result> { - nerdctl_error_to_rhai_error(Container::new(name)) -} -``` - -## Output Format - -Provide **only** the generated Rust code for the wrapper function, enclosed in triple backticks. - -```rust -// Your generated wrapper function here -pub fn wrapper_function_name(...) -> Result<..., Box> { - // Implementation -} -``` - -### Example - -Below is a bunch of input and outputted wrapped functions. - -Example Input Types: -```rust -pub struct Container { - /// Name of the container - pub name: String, - /// Container ID - pub container_id: Option, - /// Base image (if created from an image) - pub image: Option, - /// Configuration options - pub config: HashMap, - /// Port mappings - pub ports: Vec, - /// Volume mounts - pub volumes: Vec, - /// Environment variables - pub env_vars: HashMap, - /// Network to connect to - pub network: Option, - /// Network aliases - pub network_aliases: Vec, - /// CPU limit - pub cpu_limit: Option, - /// Memory limit - pub memory_limit: Option, - /// Memory swap limit - pub memory_swap_limit: Option, - /// CPU shares - pub cpu_shares: Option, - /// Restart policy - pub restart_policy: Option, - /// Health check - pub health_check: Option, - /// Whether to run in detached mode - pub detach: bool, - /// Snapshotter to use - pub snapshotter: Option, -} - -/// Health check configuration for a container -#[derive(Debug, Clone)] -pub struct HealthCheck { - /// Command to run for health check - pub cmd: String, - /// Time between running the check (default: 30s) - pub interval: Option, - /// Maximum time to wait for a check to complete (default: 30s) - pub timeout: Option, - /// Number of consecutive failures needed to consider unhealthy (default: 3) - pub retries: Option, - /// Start period for the container to initialize before counting retries (default: 0s) - pub start_period: Option, -} -``` - -Example Input Functions: -```rust - /// Set memory swap limit for the container - /// - /// # Arguments - /// - /// * `memory_swap` - Memory swap limit (e.g., "1g" for 1GB) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_memory_swap_limit(mut self, memory_swap: &str) -> Self { - self.memory_swap_limit = Some(memory_swap.to_string()); - self - } - - /// Set CPU shares for the container (relative weight) - /// - /// # Arguments - /// - /// * `shares` - CPU shares (e.g., "1024" for default, "512" for half) - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_cpu_shares(mut self, shares: &str) -> Self { - self.cpu_shares = Some(shares.to_string()); - self - } - - /// Set restart policy for the container - /// - /// # Arguments - /// - /// * `policy` - Restart policy (e.g., "no", "always", "on-failure", "unless-stopped") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_restart_policy(mut self, policy: &str) -> Self { - self.restart_policy = Some(policy.to_string()); - self - } - - /// Set a simple health check for the container - /// - /// # Arguments - /// - /// * `cmd` - Command to run for health check (e.g., "curl -f http://localhost/ || exit 1") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_health_check(mut self, cmd: &str) -> Self { - // Use the health check script module to prepare the command - let prepared_cmd = prepare_health_check_command(cmd, &self.name); - - self.health_check = Some(HealthCheck { - cmd: prepared_cmd, - interval: None, - timeout: None, - retries: None, - start_period: None, - }); - self - } - - /// Set a health check with custom options for the container - /// - /// # Arguments - /// - /// * `cmd` - Command to run for health check - /// * `interval` - Optional time between running the check (e.g., "30s", "1m") - /// * `timeout` - Optional maximum time to wait for a check to complete (e.g., "30s", "1m") - /// * `retries` - Optional number of consecutive failures needed to consider unhealthy - /// * `start_period` - Optional start period for the container to initialize before counting retries (e.g., "30s", "1m") - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_health_check_options( - mut self, - cmd: &str, - interval: Option<&str>, - timeout: Option<&str>, - retries: Option, - start_period: Option<&str>, - ) -> Self { - // Use the health check script module to prepare the command - let prepared_cmd = prepare_health_check_command(cmd, &self.name); - - let mut health_check = HealthCheck { - cmd: prepared_cmd, - interval: None, - timeout: None, - retries: None, - start_period: None, - }; - - if let Some(interval_value) = interval { - health_check.interval = Some(interval_value.to_string()); - } - - if let Some(timeout_value) = timeout { - health_check.timeout = Some(timeout_value.to_string()); - } - - if let Some(retries_value) = retries { - health_check.retries = Some(retries_value); - } - - if let Some(start_period_value) = start_period { - health_check.start_period = Some(start_period_value.to_string()); - } - - self.health_check = Some(health_check); - self - } - - /// Set the snapshotter - /// - /// # Arguments - /// - /// * `snapshotter` - Snapshotter to use - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_snapshotter(mut self, snapshotter: &str) -> Self { - self.snapshotter = Some(snapshotter.to_string()); - self - } - - /// Set whether to run in detached mode - /// - /// # Arguments - /// - /// * `detach` - Whether to run in detached mode - /// - /// # Returns - /// - /// * `Self` - The container instance for method chaining - pub fn with_detach(mut self, detach: bool) -> Self { - self.detach = detach; - self - } - - /// Build the container - /// - /// # Returns - /// - /// * `Result` - Container instance or error - pub fn build(self) -> Result { - // If container already exists, return it - if self.container_id.is_some() { - return Ok(self); - } - - // If no image is specified, return an error - let image = match &self.image { - Some(img) => img, - None => return Err(NerdctlError::Other("No image specified for container creation".to_string())), - }; - - // Build the command arguments as strings - let mut args_strings = Vec::new(); - args_strings.push("run".to_string()); - - if self.detach { - args_strings.push("-d".to_string()); - } - - args_strings.push("--name".to_string()); - args_strings.push(self.name.clone()); - - // Add port mappings - for port in &self.ports { - args_strings.push("-p".to_string()); - args_strings.push(port.clone()); - } - - // Add volume mounts - for volume in &self.volumes { - args_strings.push("-v".to_string()); - args_strings.push(volume.clone()); - } - - // Add environment variables - for (key, value) in &self.env_vars { - args_strings.push("-e".to_string()); - args_strings.push(format!("{}={}", key, value)); - } - - // Add network configuration - if let Some(network) = &self.network { - args_strings.push("--network".to_string()); - args_strings.push(network.clone()); - } - - // Add network aliases - for alias in &self.network_aliases { - args_strings.push("--network-alias".to_string()); - args_strings.push(alias.clone()); - } - - // Add resource limits - if let Some(cpu_limit) = &self.cpu_limit { - args_strings.push("--cpus".to_string()); - args_strings.push(cpu_limit.clone()); - } - - if let Some(memory_limit) = &self.memory_limit { - args_strings.push("--memory".to_string()); - args_strings.push(memory_limit.clone()); - } - - if let Some(memory_swap_limit) = &self.memory_swap_limit { - args_strings.push("--memory-swap".to_string()); - args_strings.push(memory_swap_limit.clone()); - } - - if let Some(cpu_shares) = &self.cpu_shares { - args_strings.push("--cpu-shares".to_string()); - args_strings.push(cpu_shares.clone()); - } - - // Add restart policy - if let Some(restart_policy) = &self.restart_policy { - args_strings.push("--restart".to_string()); - args_strings.push(restart_policy.clone()); - } - - // Add health check - if let Some(health_check) = &self.health_check { - args_strings.push("--health-cmd".to_string()); - args_strings.push(health_check.cmd.clone()); - - if let Some(interval) = &health_check.interval { - args_strings.push("--health-interval".to_string()); - args_strings.push(interval.clone()); - } - - if let Some(timeout) = &health_check.timeout { - args_strings.push("--health-timeout".to_string()); - args_strings.push(timeout.clone()); - } - - if let Some(retries) = &health_check.retries { - args_strings.push("--health-retries".to_string()); - args_strings.push(retries.to_string()); - } - - if let Some(start_period) = &health_check.start_period { - args_strings.push("--health-start-period".to_string()); - args_strings.push(start_period.clone()); - } - } - - if let Some(snapshotter_value) = &self.snapshotter { - args_strings.push("--snapshotter".to_string()); - args_strings.push(snapshotter_value.clone()); - } - - // Add flags to avoid BPF issues - args_strings.push("--cgroup-manager=cgroupfs".to_string()); - - args_strings.push(image.clone()); - - // Convert to string slices for the command - let args: Vec<&str> = args_strings.iter().map(|s| s.as_str()).collect(); - - // Execute the command - let result = execute_nerdctl_command(&args)?; - - // Get the container ID from the output - let container_id = result.stdout.trim().to_string(); - - Ok(Self { - name: self.name, - container_id: Some(container_id), - image: self.image, - config: self.config, - ports: self.ports, - volumes: self.volumes, - env_vars: self.env_vars, - network: self.network, - network_aliases: self.network_aliases, - cpu_limit: self.cpu_limit, - memory_limit: self.memory_limit, - memory_swap_limit: self.memory_swap_limit, - cpu_shares: self.cpu_shares, - restart_policy: self.restart_policy, - health_check: self.health_check, - detach: self.detach, - snapshotter: self.snapshotter, - }) - } - -``` - -Example output functions: -```rust - -/// Set memory swap limit for a Container -pub fn container_with_memory_swap_limit(container: Container, memory_swap: &str) -> Container { - container.with_memory_swap_limit(memory_swap) -} - -/// Set CPU shares for a Container -pub fn container_with_cpu_shares(container: Container, shares: &str) -> Container { - container.with_cpu_shares(shares) -} - -/// Set health check with options for a Container -pub fn container_with_health_check_options( - container: Container, - cmd: &str, - interval: Option<&str>, - timeout: Option<&str>, - retries: Option, - start_period: Option<&str> -) -> Container { - // Convert i64 to u32 for retries - let retries_u32 = retries.map(|r| r as u32); - container.with_health_check_options(cmd, interval, timeout, retries_u32, start_period) -} - -/// Set snapshotter for a Container -pub fn container_with_snapshotter(container: Container, snapshotter: &str) -> Container { - container.with_snapshotter(snapshotter) -} - -/// Set detach mode for a Container -pub fn container_with_detach(container: Container, detach: bool) -> Container { - container.with_detach(detach) -} - -/// Build and run the Container -/// -/// This function builds and runs the container using the configured options. -/// It provides detailed error information if the build fails. -pub fn container_build(container: Container) -> Result> { - // Get container details for better error reporting - let container_name = container.name.clone(); - let image = container.image.clone().unwrap_or_else(|| "none".to_string()); - let ports = container.ports.clone(); - let volumes = container.volumes.clone(); - let env_vars = container.env_vars.clone(); - - // Try to build the container - let build_result = container.build(); - - // Handle the result with improved error context - match build_result { - Ok(built_container) => { - // Container built successfully - Ok(built_container) - }, - Err(err) => { - // Add more context to the error - let enhanced_error = match err { - NerdctlError::CommandFailed(msg) => { - // Provide more detailed error information - let mut enhanced_msg = format!("Failed to build container '{}' from image '{}': {}", - container_name, image, msg); - - // Add information about configured options that might be relevant - if !ports.is_empty() { - enhanced_msg.push_str(&format!("\nConfigured ports: {:?}", ports)); - } - - if !volumes.is_empty() { - enhanced_msg.push_str(&format!("\nConfigured volumes: {:?}", volumes)); - } - - if !env_vars.is_empty() { - enhanced_msg.push_str(&format!("\nConfigured environment variables: {:?}", env_vars)); - } - - // Add suggestions for common issues - if msg.contains("not found") || msg.contains("no such image") { - enhanced_msg.push_str("\nSuggestion: The specified image may not exist or may not be pulled yet. Try pulling the image first with nerdctl_image_pull()."); - } else if msg.contains("port is already allocated") { - enhanced_msg.push_str("\nSuggestion: One of the specified ports is already in use. Try using a different port or stopping the container using that port."); - } else if msg.contains("permission denied") { - enhanced_msg.push_str("\nSuggestion: Permission issues detected. Check if you have the necessary permissions to create containers or access the specified volumes."); - } - - NerdctlError::CommandFailed(enhanced_msg) - }, - _ => err - }; - - nerdctl_error_to_rhai_error(Err(enhanced_error)) - } - } -} diff --git a/libarchive/rhai/register_functions.v b/libarchive/rhai/register_functions.v deleted file mode 100644 index 0149eb93..00000000 --- a/libarchive/rhai/register_functions.v +++ /dev/null @@ -1,31 +0,0 @@ -module rhai - -import strings - -// generate_rhai_function_registration generates Rust code to register a list of functions with Rhai. -// Input: module_name - The name for the registration function (e.g., "nerdctl" -> register_nerdctl_module). -// Input: function_names - A list of Rust function names to register. -// Output: A string containing the generated Rust registration function, or an error string. -pub fn generate_rhai_function_registration(module_name string, function_names []string) !string { - if module_name.len == 0 { - return error('module_name cannot be empty') - } - if function_names.len == 0 { - return error('function_names cannot be empty') - } - - mut sb := strings.new_builder(1024) - module_name_lower := module_name.to_lower() - - sb.writeln('pub fn register_${module_name_lower}_module(engine: &mut Engine) -> Result<(), Box> {') - - // Register each function - for fn_name in function_names { - sb.writeln('\tengine.register_fn("${fn_name}", ${fn_name});') - } - - sb.writeln('\tOk(())') - sb.writeln('}') - - return sb.str() -} diff --git a/libarchive/rhai/register_types.v b/libarchive/rhai/register_types.v deleted file mode 100644 index 424e498a..00000000 --- a/libarchive/rhai/register_types.v +++ /dev/null @@ -1,89 +0,0 @@ -module rhai - -import strings -import incubaid.herolib.lang.rust - -// generate_rhai_registration generates Rust code to register a Rust struct and its fields with Rhai. -// Input: rust_struct_definition - A string containing the Rust struct definition. -// Output: A string containing the generated Rust registration function, or an error string. -pub fn generate_rhai_registration(rust_struct_definition string) !string { - mut struct_name := '' - mut fields := map[string]string{} // field_name: field_type - - // --- 1. Parse the struct definition using the rust module --- - struct_info := rust.parse_rust_struct(rust_struct_definition) or { - return error('Failed to parse Rust struct definition: ${err}') - } - - struct_name = struct_info.struct_name - fields = struct_info.fields.clone() // Explicitly clone the map - - // --- 2. Generate the Rust registration code --- - mut sb := strings.new_builder(1024) - struct_name_lower := struct_name.to_lower() - - // sb.writeln('/// Register ${struct_name} type with the Rhai engine') - sb.writeln('fn register_${struct_name_lower}_type(engine: &mut Engine) -> Result<(), Box> {') - // Register the type itself - sb.writeln('\t// Register ${struct_name} type') - sb.writeln('\tengine.register_type_with_name::<${struct_name}>("${struct_name}");') - sb.writeln('') - sb.writeln('\t// Register getters for ${struct_name} properties') - - // Register getters for each field - for field_name, field_type in fields { - match field_type { - 'String' { - sb.writeln('\tengine.register_get("${field_name}", |obj: &mut ${struct_name}| obj.${field_name}.clone());') - } - 'bool' { - sb.writeln('\tengine.register_get("${field_name}", |obj: &mut ${struct_name}| obj.${field_name});') - } - 'Option' { - sb.writeln('\tengine.register_get("${field_name}", |obj: &mut ${struct_name}| {') - sb.writeln('\t\tmatch &obj.${field_name} {') - sb.writeln('\t\t\tSome(val) => val.clone(),') - sb.writeln('\t\t\tNone => "".to_string(), // Return empty string for None') - sb.writeln('\t\t}') - sb.writeln('\t});') - } - 'Vec' { - sb.writeln('\tengine.register_get("${field_name}", |obj: &mut ${struct_name}| {') - sb.writeln('\t\tlet mut array = rhai::Array::new();') - sb.writeln('\t\tfor item in &obj.${field_name} {') - sb.writeln('\t\t\tarray.push(rhai::Dynamic::from(item.clone()));') - sb.writeln('\t\t}') - sb.writeln('\t\tarray') - sb.writeln('\t});') - } - 'HashMap' { - sb.writeln('\tengine.register_get("${field_name}", |obj: &mut ${struct_name}| {') - sb.writeln('\t\tlet mut map = rhai::Map::new();') - sb.writeln('\t\tfor (k, v) in &obj.${field_name} {') - sb.writeln('\t\t\tmap.insert(k.clone().into(), v.clone().into());') - sb.writeln('\t\t}') - sb.writeln('\t\tmap') - sb.writeln('\t});') - } - else { - // Handle other types like Option, Vec, etc. - // For now, just add a TODO comment - sb.writeln('\t// TODO: Register getter for field: ${field_name} (type: ${field_type}) - Add appropriate conversion if needed.') - } - } - } - - sb.writeln('') - sb.writeln('\tOk(())') - sb.writeln('}') - - return sb.str() -} - -// Example Usage (within the V module context): -// rust_def := "" // The rust struct definition string from the comment -// generated_code := generate_rhai_registration(rust_def) or { -// eprintln('Error generating code: ${err}') -// return -// } -// println(generated_code) diff --git a/libarchive/rhai/register_types_test.v b/libarchive/rhai/register_types_test.v deleted file mode 100644 index 8e306cb9..00000000 --- a/libarchive/rhai/register_types_test.v +++ /dev/null @@ -1,44 +0,0 @@ -module rhai - -import os -import incubaid.herolib.lang.rust // Import the rust module - -fn test_generate_container_registration() { - // Define the path to the test data file - test_file_path := os.real_path(os.join_path(os.dir(@FILE), 'testdata', 'types.rs')) - - // Read the content of the test file - rust_code_content := os.read_file(test_file_path) or { - assert false, 'Failed to read test file ${test_file_path}: ${err}' - return - } - - // Extract the struct definition from the file content - rust_def := rust.get_struct_from_content(rust_code_content, 'Container') or { - assert false, 'Failed to extract struct Container from ${test_file_path}: ${err}' - return - } - - // Extract the expected registration function from the file content - expected_output := rust.get_function_from_content(rust_code_content, 'register_container_type') or { - assert false, 'Failed to extract function register_container_type from ${test_file_path}: ${err}' - return - } - - // Generate the code using the extracted struct definition - generated_code := generate_rhai_registration(rust_def) or { - assert false, 'generate_rhai_registration failed: ${err}' - return - } - - // Compare the generated code with the expected output - // Use trim_space() on both to avoid potential leading/trailing whitespace mismatches - mut generated_trimmed := generated_code // Create mutable copies - mut expected_trimmed := expected_output - generated_trimmed.trim_space() // Modify in place - expected_trimmed.trim_space() // Modify in place - assert generated_trimmed == expected_trimmed, 'Generated code does not match expected output.\n--- Generated:\n${generated_trimmed}\n--- Expected:\n${expected_trimmed}' - - // Optional: print the results for verification - // println("--- Generated Code ---") -} diff --git a/libarchive/rhai/rhai.v b/libarchive/rhai/rhai.v deleted file mode 100644 index 36f1c794..00000000 --- a/libarchive/rhai/rhai.v +++ /dev/null @@ -1,61 +0,0 @@ -module rhai - -import log -import incubaid.herolib.ai.escalayer - -// pub struct WrapperGenerator { -// pub: -// function string -// structs []string -// } - -// generate_rhai_function_wrapper generates a Rhai wrapper function for a given Rust function. -// -// Args: -// rust_function (string): The Rust function signature string. -// struct_declarations ([]string): Optional struct declarations used by the function. -// -// Returns: -// !string: The generated Rhai wrapper function code or an error. -pub fn generate_rhai_function_wrapper(rust_function string, struct_declarations []string) !string { - mut task := escalayer.new_task( - name: 'generate_rhai_function_wrapper' - description: 'Create a single Rhai wrapper for a Rust function' - ) - - mut gen := WrapperGenerator{ - function: rust_function - structs: struct_declarations - } - - // Define a single unit task that handles everything - task.new_unit_task( - name: 'generate_rhai_function_wrapper' - prompt_function: gen.generate_rhai_function_wrapper_prompt - callback_function: gen.generate_rhai_function_wrapper_callback - base_model: escalayer.claude_3_sonnet // Use actual model identifier - retry_model: escalayer.claude_3_sonnet // Use actual model identifier - retry_count: 2 - ) - - return task.initiate('') -} - -pub fn (gen WrapperGenerator) generate_rhai_function_wrapper_prompt(input string) string { - return $tmpl('./prompts/generate_rhai_function_wrapper.md') -} - -// generate_rhai_function_wrapper_callback validates the generated Rhai wrapper. -// -// Args: -// rhai_wrapper (string): The generated wrapper code. -// -// Returns: -// !string: The validated wrapper code or an error. -pub fn (gen WrapperGenerator) generate_rhai_function_wrapper_callback(output string) !string { - verify_rhai_wrapper(gen.function, gen.structs, output) or { - log.error('Failed to verify, will retry ${err}') - return err - } - return output -} diff --git a/libarchive/rhai/rhai_test.v b/libarchive/rhai/rhai_test.v deleted file mode 100644 index 5bf9608d..00000000 --- a/libarchive/rhai/rhai_test.v +++ /dev/null @@ -1,149 +0,0 @@ -module rhai - -import incubaid.herolib.lang.rhai -import incubaid.herolib.core.texttools -// import strings // No longer needed directly here -import incubaid.herolib.lang.rust -import os - -const test_data_file = os.dir(@FILE) + '/testdata/functions.rs' // Use path relative to this test file - -fn testsuite_begin() { - // Optional: Setup code before tests run - if !os.exists(test_data_file) { - panic('Test data file not found: ${test_data_file}') - } -} - -fn testsuite_end() { - // Optional: Teardown code after tests run -} - -// --- Test Cases --- - -fn test_generate_wrapper_simple_function() { - rust_fn_name := 'add' - // Call directly using suspected correct function name - rust_fn_sig := rust.get_function_from_file(test_data_file, rust_fn_name) or { - assert false, 'Failed to get function signature for ${rust_fn_name}: ${err}' - return - } - struct_decls := []string{} - - generated_output := rhai.generate_rhai_function_wrapper(rust_fn_sig, struct_decls) or { - assert false, 'Wrapper generation failed for ${rust_fn_name}: ${err}' - return - } - verify_rhai_wrapper(rust_fn_sig, struct_decls, generated_output) or { - assert false, 'Verification failed for ${rust_fn_name}: ${err}' - } -} - -fn test_generate_wrapper_immutable_method() { - rust_fn_name := 'get_name' - struct_name := 'MyStruct' - // Use get_function_from_file for methods too - rust_fn_sig := rust.get_function_from_file(test_data_file, rust_fn_name) or { // Using get_function_from_file - assert false, 'Failed to get method signature for ${struct_name}::${rust_fn_name}: ${err}' - return - } - // Call directly using suspected correct function name - struct_def := rust.get_struct_from_file(test_data_file, struct_name) or { - assert false, 'Failed to get struct def for ${struct_name}: ${err}' - return - } - struct_decls := [struct_def] - - generated_output := rhai.generate_rhai_function_wrapper(rust_fn_sig, struct_decls) or { - assert false, 'Wrapper generation failed for ${struct_name}::${rust_fn_name}: ${err}' - return - } - verify_rhai_wrapper(rust_fn_sig, struct_decls, generated_output) or { - assert false, 'Verification failed for ${struct_name}::${rust_fn_name}: ${err}' - } -} - -fn test_generate_wrapper_mutable_method() { - rust_fn_name := 'set_name' - struct_name := 'MyStruct' - // Use get_function_from_file for methods too - rust_fn_sig := rust.get_function_from_file(test_data_file, rust_fn_name) or { // Using get_function_from_file - assert false, 'Failed to get method signature for ${struct_name}::${rust_fn_name}: ${err}' - return - } - // Call directly using suspected correct function name - struct_def := rust.get_struct_from_file(test_data_file, struct_name) or { - assert false, 'Failed to get struct def for ${struct_name}: ${err}' - return - } - struct_decls := [struct_def] - - generated_output := rhai.generate_rhai_function_wrapper(rust_fn_sig, struct_decls) or { - assert false, 'Wrapper generation failed for ${struct_name}::${rust_fn_name}: ${err}' - return - } - verify_rhai_wrapper(rust_fn_sig, struct_decls, generated_output) or { - assert false, 'Verification failed for ${struct_name}::${rust_fn_name}: ${err}' - } -} - -fn test_generate_wrapper_function_returning_result() { - rust_fn_name := 'load_config' - // Call directly using suspected correct function name - rust_fn_sig := rust.get_function_from_file(test_data_file, rust_fn_name) or { - assert false, 'Failed to get function signature for ${rust_fn_name}: ${err}' - return - } - // Need struct def for Config if wrapper needs it (likely for return type) - struct_name := 'Config' - // Call directly using suspected correct function name - struct_def := rust.get_struct_from_file(test_data_file, struct_name) or { - assert false, 'Failed to get struct def for ${struct_name}: ${err}' - return - } - struct_decls := [struct_def] - - generated_output := rhai.generate_rhai_function_wrapper(rust_fn_sig, struct_decls) or { - assert false, 'Wrapper generation failed for ${rust_fn_name}: ${err}' - return - } - verify_rhai_wrapper(rust_fn_sig, struct_decls, generated_output) or { - assert false, 'Verification failed for ${rust_fn_name}: ${err}' - } -} - -fn test_generate_wrapper_function_returning_pathbuf() { - rust_fn_name := 'get_home_dir' - // Call directly using suspected correct function name - rust_fn_sig := rust.get_function_from_file(test_data_file, rust_fn_name) or { - assert false, 'Failed to get function signature for ${rust_fn_name}: ${err}' - return - } - struct_decls := []string{} - - generated_output := rhai.generate_rhai_function_wrapper(rust_fn_sig, struct_decls) or { - assert false, 'Wrapper generation failed for ${rust_fn_name}: ${err}' - return - } - verify_rhai_wrapper(rust_fn_sig, struct_decls, generated_output) or { - assert false, 'Verification failed for ${rust_fn_name}: ${err}' - } -} - -fn test_generate_wrapper_function_with_vec() { - rust_fn_name := 'list_files' - // Call directly using suspected correct function name - rust_fn_sig := rust.get_function_from_file(test_data_file, rust_fn_name) or { - assert false, 'Failed to get function signature for ${rust_fn_name}: ${err}' - return - } - struct_decls := []string{} - - generated_output := rhai.generate_rhai_function_wrapper(rust_fn_sig, struct_decls) or { - assert false, 'Wrapper generation failed for ${rust_fn_name}: ${err}' - return - } - verify_rhai_wrapper(rust_fn_sig, struct_decls, generated_output) or { - assert false, 'Verification failed for ${rust_fn_name}: ${err}' - } -} diff --git a/libarchive/rhai/templates/cargo.toml b/libarchive/rhai/templates/cargo.toml deleted file mode 100644 index 0fef99ff..00000000 --- a/libarchive/rhai/templates/cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "@{name}_rhai" -version = "0.1.0" -edition = "2021" - -[dependencies] -rhai = "1.21.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -@{source_pkg.name} = { path = "@{source_pkg.path}" } \ No newline at end of file diff --git a/libarchive/rhai/templates/engine.rs b/libarchive/rhai/templates/engine.rs deleted file mode 100644 index 8360147a..00000000 --- a/libarchive/rhai/templates/engine.rs +++ /dev/null @@ -1,21 +0,0 @@ - -/// Create a new Rhai engine with @{name} functionality -pub fn create_rhai_engine() -> Engine { - let mut engine = Engine::new(); - - // Register the @{name} module - if let Err(err) = register_${name}_module(&mut engine) { - eprintln!("Error registering @{name} module: {}", err); - } - - // Register types - if let Err(err) = register_@{name}_types(&mut engine) { - eprintln!("Error registering @{name} types: {}", err); - } - - engine -} - -@{register_functions_rs} - -@{register_types_rs} \ No newline at end of file diff --git a/libarchive/rhai/templates/example.rs b/libarchive/rhai/templates/example.rs deleted file mode 100644 index e94b8032..00000000 --- a/libarchive/rhai/templates/example.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::{fs, path::Path}; -use @{name}_rhai::create_rhai_engine; - -fn main() -> Result<(), Box> { - println!("=== Rhai Wrapper Example ==="); - - // Create a Rhai engine with functionality - let mut engine = create_rhai_engine(); - println!("Successfully created Rhai engine"); - - // Get the path to the example.rhai script - let script_path = get_script_path()?; - println!("Loading script from: {}", script_path.display()); - - // Load the script content - let script = fs::read_to_string(&script_path) - .map_err(|e| format!("Failed to read script file: {}", e))?; - - // Run the script - println!("\n=== Running Rhai script ==="); - let result = engine.eval::(&script) - .map_err(|e| format!("Script execution error: {}", e))?; - - println!("\nScript returned: {}", result); - println!("\nExample completed successfully!"); - Ok(()) -} - -fn get_script_path() -> Result { - // When running with cargo run --example, the script will be in the examples directory - let script_path = Path::new(env!("CARGO_MANIFEST_DIR")) - .join("examples") - .join("example.rhai"); - - if script_path.exists() { - Ok(script_path) - } else { - Err(format!("Could not find example.rhai script at {}", script_path.display())) - } -} diff --git a/libarchive/rhai/templates/lib.rs b/libarchive/rhai/templates/lib.rs deleted file mode 100644 index 0d9bc4a5..00000000 --- a/libarchive/rhai/templates/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod engine; \ No newline at end of file diff --git a/libarchive/rhai/templates/wrapper.rs b/libarchive/rhai/templates/wrapper.rs deleted file mode 100644 index 3a784c4c..00000000 --- a/libarchive/rhai/templates/wrapper.rs +++ /dev/null @@ -1,7 +0,0 @@ - -use rhai::{Engine, EvalAltResult, Array, Dynamic, Map, Position}; -use std::collections::HashMap; - -@for function in functions -@{function} -@end \ No newline at end of file diff --git a/libarchive/rhai/testdata/functions.rs b/libarchive/rhai/testdata/functions.rs deleted file mode 100644 index 7951dcae..00000000 --- a/libarchive/rhai/testdata/functions.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::path::PathBuf; -use std::io; -use std::fs; - -// --- Simple Function --- -pub fn add(a: i32, b: i32) -> i32 { - a + b -} - -// --- Struct and Methods --- -#[derive(Clone)] // Added Clone for Rhai compatibility if needed directly -pub struct MyStruct { - name: String, -} - -impl MyStruct { - pub fn new(name: String) -> Self { - MyStruct { name } - } - - // Immutable method - pub fn get_name(&self) -> String { - self.name.clone() - } - - // Mutable method - pub fn set_name(&mut self, new_name: String) { - self.name = new_name; - } -} - -// --- Function Returning Result --- -#[derive(Clone)] // Added Clone for Rhai compatibility -pub struct Config { - setting: String, -} - -// Dummy error type -#[derive(Debug)] -pub struct ConfigError(String); - -impl std::fmt::Display for ConfigError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Config Error: {}", self.0) - } -} -impl std::error::Error for ConfigError {} - -pub fn load_config(path: &str) -> Result { - if path == "valid/path" { - Ok(Config { setting: "loaded".to_string() }) - } else { - Err(ConfigError("Invalid path".to_string())) - } -} - -// --- Function Returning PathBuf --- -pub fn get_home_dir() -> PathBuf { - // In a real scenario, this would use home::home_dir() or similar - PathBuf::from("/fake/home") -} - -// --- Function with Vec --- -pub fn list_files(dir: &str) -> Vec { - // Dummy implementation for testing signature wrapping - if dir == "valid/dir" { - vec!["file1.txt".to_string(), "file2.rs".to_string()] - } else { - vec![] - } -} diff --git a/libarchive/rhai/testdata/types.rs b/libarchive/rhai/testdata/types.rs deleted file mode 100644 index 2f017cde..00000000 --- a/libarchive/rhai/testdata/types.rs +++ /dev/null @@ -1,148 +0,0 @@ -use std::collections::HashMap; -use rhai::{Engine, EvalAltResult, Map, Dynamic, Array}; // Assuming necessary Rhai imports are here - -// Define HealthCheck struct or use a placeholder if definition is complex/elsewhere -// Placeholder: -struct HealthCheck; - -/// Container struct for nerdctl operations -#[derive(Clone)] -pub struct Container { - /// Name of the container - pub name: String, - /// Container ID - pub container_id: Option, - /// Base image (if created from an image) - pub image: Option, - /// Configuration options - pub config: HashMap, - /// Port mappings - pub ports: Vec, - /// Volume mounts - pub volumes: Vec, - /// Environment variables - pub env_vars: HashMap, - /// Network to connect to - pub network: Option, - /// Network aliases - pub network_aliases: Vec, - /// CPU limit - pub cpu_limit: Option, - /// Memory limit - pub memory_limit: Option, - /// Memory swap limit - pub memory_swap_limit: Option, - /// CPU shares - pub cpu_shares: Option, - /// Restart policy - pub restart_policy: Option, - /// Health check - pub health_check: Option, - /// Whether to run in detached mode - pub detach: bool, - /// Snapshotter to use - pub snapshotter: Option, -} - - -/// Register Container type with the Rhai engine -fn register_container_type(engine: &mut Engine) -> Result<(), Box> { - // Register Container type - engine.register_type_with_name::("Container"); - - // Register getters for Container properties - engine.register_get("name", |obj: &mut Container| obj.name.clone()); - engine.register_get("container_id", |obj: &mut Container| { - match &obj.container_id { - Some(val) => val.clone(), - None => "".to_string(), // Return empty string for None - } - }); - engine.register_get("image", |obj: &mut Container| { - match &obj.image { - Some(val) => val.clone(), - None => "".to_string(), // Return empty string for None - } - }); - engine.register_get("config", |obj: &mut Container| { - let mut map = rhai::Map::new(); - for (k, v) in &obj.config { - map.insert(k.clone().into(), v.clone().into()); - } - map - }); - engine.register_get("ports", |obj: &mut Container| { - let mut array = rhai::Array::new(); - for item in &obj.ports { - array.push(rhai::Dynamic::from(item.clone())); - } - array - }); - engine.register_get("volumes", |obj: &mut Container| { - let mut array = rhai::Array::new(); - for item in &obj.volumes { - array.push(rhai::Dynamic::from(item.clone())); - } - array - }); - engine.register_get("env_vars", |obj: &mut Container| { - let mut map = rhai::Map::new(); - for (k, v) in &obj.env_vars { - map.insert(k.clone().into(), v.clone().into()); - } - map - }); - engine.register_get("network", |obj: &mut Container| { - match &obj.network { - Some(val) => val.clone(), - None => "".to_string(), // Return empty string for None - } - }); - engine.register_get("network_aliases", |obj: &mut Container| { - let mut array = rhai::Array::new(); - for item in &obj.network_aliases { - array.push(rhai::Dynamic::from(item.clone())); - } - array - }); - engine.register_get("cpu_limit", |obj: &mut Container| { - match &obj.cpu_limit { - Some(val) => val.clone(), - None => "".to_string(), // Return empty string for None - } - }); - engine.register_get("memory_limit", |obj: &mut Container| { - match &obj.memory_limit { - Some(val) => val.clone(), - None => "".to_string(), // Return empty string for None - } - }); - engine.register_get("memory_swap_limit", |obj: &mut Container| { - match &obj.memory_swap_limit { - Some(val) => val.clone(), - None => "".to_string(), // Return empty string for None - } - }); - engine.register_get("cpu_shares", |obj: &mut Container| { - match &obj.cpu_shares { - Some(val) => val.clone(), - None => "".to_string(), // Return empty string for None - } - }); - engine.register_get("restart_policy", |obj: &mut Container| { - match &obj.restart_policy { - Some(val) => val.clone(), - None => "".to_string(), // Return empty string for None - } - }); - // TODO: Register getter for field: health_check (type: Option) - Add appropriate conversion if needed. - engine.register_get("detach", |obj: &mut Container| obj.detach); - engine.register_get("snapshotter", |obj: &mut Container| { - match &obj.snapshotter { - Some(val) => val.clone(), - None => "".to_string(), // Return empty string for None - } - }); - - Ok(()) -} diff --git a/libarchive/rhai/verify.v b/libarchive/rhai/verify.v deleted file mode 100644 index bec74456..00000000 --- a/libarchive/rhai/verify.v +++ /dev/null @@ -1,84 +0,0 @@ -module rhai - -import incubaid.herolib.core.texttools -// import strings // No longer needed directly here -import incubaid.herolib.lang.rust -import os - -// Helper to extract the primary struct name from declarations -fn get_primary_struct_name(struct_declarations []string) string { - if struct_declarations.len == 0 { - return '' - } - // Simple extraction: assumes first line is `pub struct Name {` or `struct Name {` - first_line := struct_declarations[0].trim_space() - if first_line.starts_with('pub struct ') { - name_part := first_line.all_after('pub struct ') - return name_part.all_before('{').trim_space() - } else if first_line.starts_with('struct ') { - name_part := first_line.all_after('struct ') - return name_part.all_before('{').trim_space() - } - return '' // Couldn't determine name -} - -// verify_rhai_wrapper checks if the AI-generated output contains a plausible Rhai wrapper. -// It now uses the struct declarations to check for correct method naming. -fn verify_rhai_wrapper(rust_fn_signature string, struct_declarations []string, generated_output string) ! { - // 1. Extract Rust code block (same as before) - mut code_block := '' - if generated_output.contains('```rust') { // Use contains() for strings - start_index := generated_output.index('```rust') or { -1 } - end_index := generated_output.index_after('```', start_index + 7) or { -1 } - if start_index != -1 && end_index != -1 { - code_block = generated_output[start_index + 7..end_index].trim_space() - } else { - code_block = generated_output.trim_space() - } - } else { - code_block = generated_output.trim_space() - } - assert code_block.len > 0, 'Could not extract code block from generated output: \n`${generated_output}`' - - // 2. Determine Original Function Name and Expected Wrapper Name - // Ideally, use rust module parsing here, but for now, simple string parsing: - is_method := rust_fn_signature.contains('&self') || rust_fn_signature.contains('&mut self') - mut original_fn_name := '' - sig_parts := rust_fn_signature.split('(') - if sig_parts.len > 0 { - name_parts := sig_parts[0].split(' ') - if name_parts.len > 0 { - original_fn_name = name_parts.last() - } - } - assert original_fn_name != '', 'Could not extract function name from signature: ${rust_fn_signature}' - - expected_wrapper_fn_name := if is_method { - struct_name := get_primary_struct_name(struct_declarations) - assert struct_name != '', 'Could not determine struct name for method: ${rust_fn_signature}' - '${texttools.snake_case(struct_name)}_${original_fn_name}' // e.g., mystruct_get_name - } else { - original_fn_name // Standalone function uses the same name - } - - // 3. Basic Signature Check (using expected_wrapper_fn_name) - expected_sig_start := 'pub fn ${expected_wrapper_fn_name}' - expected_sig_end := '-> Result<' - expected_sig_very_end := 'Box>' - - assert code_block.contains(expected_sig_start), 'Wrapper missing signature start: `${expected_sig_start}` in\n${code_block}' - assert code_block.contains(expected_sig_end), 'Wrapper missing signature end: `${expected_sig_end}` in\n${code_block}' - assert code_block.contains(expected_sig_very_end), 'Wrapper missing signature very end: `${expected_sig_very_end}` in\n${code_block}' - - // 4. Basic Body Check (Check for call to the *original* function name) - body_start := code_block.index('{') or { -1 } - body_end := code_block.last_index('}') or { -1 } - if body_start != -1 && body_end != -1 && body_start < body_end { - body := code_block[body_start + 1..body_end] - // Check for call like `original_fn_name(...)` or `receiver.original_fn_name(...)` - assert body.contains(original_fn_name + '(') || body.contains('.' + original_fn_name + '('), 'Wrapper body does not appear to call original function `${original_fn_name}` in\n${body}' - } else { - assert false, 'Could not find function body `{...}` in wrapper:\n${code_block}' - } - // If all checks pass, do nothing (implicitly ok) -} diff --git a/libarchive/smartid/sid.v b/libarchive/smartid/sid.v deleted file mode 100644 index df7210fa..00000000 --- a/libarchive/smartid/sid.v +++ /dev/null @@ -1,118 +0,0 @@ -module smartid - -// import incubaid.herolib.core.redisclient -import math -// import incubaid.herolib.core.texttools.regext -// import rand - -// each part min3 max 6 chars, each char = a...z or 0...9 -// to create a new one we need to know the circle -// pub fn sid_new(cid string) !string { -// mut redis := redisclient.core_get()! -// key := 'circle:sid:${cid}' -// mut sidlast := redis.get(key)! // is the last sid -// if sidlast == '' { -// redis.set(key, '10')! -// sidlast = redis.get(key)! // need to make sure we reserve the first 10 ones -// } -// sidlasti := sidlast.u32() + 1 // is a new one -// redis.set(key, '${sidlasti}')! -// return sid_str(sidlasti) -// } - -// // make sure redis knows about it, will return true if its not known in redis yet -// fn sid_acknowledge(cid string, sid string) !bool { -// mut redis := redisclient.core_get()! -// key := 'circle:sid:${cid}' -// sidlast := redis.get(key)! // is the last sid -// sidlasti := sidlast.u32() -// sidnewi := sid_int(sid) -// if sidnewi > sidlasti { -// redis.set(key, '${sidnewi}')! -// return true -// } -// return false -// } - -// set the sids in redis, so we remember them all, and we know which one is the latest -// this is for all sids as found in text -// fn sids_acknowledge(cid string, text string) ! { -// res := regext.find_sid(text) -// for sid in res { -// sid_acknowledge(cid, sid)! -// } -// } - -// // make sure that we don't use an existing one -// pub fn sid_new_unique(existing []string) !string { -// idint := rand.u32_in_range(1, 42800) or { panic(err) } -// idstr := smartid_string(idint) -// if idstr !in existing { -// return idstr -// } -// return error('Could not find unique smartid, run out of tries') -// } - -// convert sid to int -pub fn sid_int(sid string) u32 { - mut result := 0 - mut count := sid.len - 1 - for i in sid { - if i > 47 && i < 58 { - result += (i - 48) * int(math.pow(36, count)) - } else if i > 96 && i < 123 { - result += (i - 87) * int(math.pow(36, count)) - } - count -= 1 - } - return u32(result) -} - -// represent sid as string, from u32 -pub fn sid_str(sid u32) string { - mut completed := false - mut remaining := int(sid) - mut decimals := []f64{} - mut count := 1 - for completed == false { - if int(math.pow(36, count)) > sid { - for i in 0 .. count { - decimals << math.floor(f64(remaining / int(math.pow(36, count - 1 - i)))) - remaining = remaining % int(math.pow(36, count - 1 - i)) - } - completed = true - } else { - count += 1 - } - } - mut strings := []string{} - for i in 0 .. (decimals.len) { - if decimals[i] >= 0 && decimals[i] <= 9 { - strings << u8(decimals[i] + 48).ascii_str() - } else { - strings << u8(decimals[i] + 87).ascii_str() - } - } - return strings.join('') -} - -// check if format is [..5].[..5].[..5] . and [..5] is string -// return error if issue -pub fn sid_check(sid string) bool { - if sid.len > 6 || sid.len < 2 { - return false - } - for cha in sid { - if (cha < 48 || cha > 57) && (cha < 97 || cha > 122) { - return false - } - } - return true -} - -// raise error if smartid not valid -pub fn sid_test(sid string) ! { - if !sid_check(sid) { - return error('sid:${sid} is not valid.') - } -} diff --git a/libarchive/starlight/clean.v b/libarchive/starlight/clean.v deleted file mode 100644 index 49193eba..00000000 --- a/libarchive/starlight/clean.v +++ /dev/null @@ -1,50 +0,0 @@ -module starlight - -import os -import strings - -pub fn (mut site DocSite) clean(args ErrorArgs) ! { - toclean := ' - /node_modules - - babel.config.js - - # Production - /build - - # Generated files - .docusaurus - .cache-loader - - # Misc - .DS_Store - .env.local - .env.development.local - .env.test.local - .env.production.local - - npm-debug.log* - yarn-debug.log* - yarn-error.log* - bun.lockb - bun.lock - - yarn.lock - - build.sh - build_dev.sh - build-dev.sh - develop.sh - install.sh - - package.json - package-lock.json - pnpm-lock.yaml - - sidebars.ts - - tsconfig.json - ' - - // TODO: need better way how to deal with this -} diff --git a/libarchive/starlight/config.v b/libarchive/starlight/config.v deleted file mode 100644 index c610c0bd..00000000 --- a/libarchive/starlight/config.v +++ /dev/null @@ -1,126 +0,0 @@ -module starlight - -import incubaid.herolib.core.pathlib -import json -import os - -// Footer config structures -pub struct FooterItem { -pub mut: - label string - to string - href string -} - -pub struct FooterLink { -pub mut: - title string - items []FooterItem -} - -pub struct Footer { -pub mut: - style string = 'dark' - links []FooterLink -} - -pub struct Main { -pub mut: - name string - title string = 'A Test Site' - // tagline string - url string = 'http://localhost/testsite' - // url_home string - // base_url string = '/' @[json: 'baseUrl'] - // image string = 'img/tf_graph.png' @[required] - build_dest []string @[json: 'buildDest'] - build_dest_dev []string @[json: 'buildDestDev'] - content []ContentItem -} - -// Navbar config structures -pub struct NavbarItem { -pub mut: - href string - label string - position string -} - -pub struct Navbar { -pub mut: - title string - items []NavbarItem -} - -// Combined config structure -pub struct Config { -pub mut: - footer Footer - main Main - navbar Navbar -} - -// pulled from e.g. git and linked to a destination in the astro build location -pub struct ContentItem { -pub mut: - url string - dest string - replacer map[string]string // items we want to replace -} - -// load_config loads all configuration from the specified directory -pub fn load_config(cfg_dir string) !Config { - // Ensure the config directory exists - if !os.exists(cfg_dir) { - return error('Config directory ${cfg_dir} does not exist') - } - - // Load and parse footer config - footer_content := os.read_file(os.join_path(cfg_dir, 'footer.json'))! - footer := json.decode(Footer, footer_content)! - - // Load and parse main config - main_config_path := os.join_path(cfg_dir, 'main.json') - main_content := os.read_file(main_config_path)! - main := json.decode(Main, main_content) or { - eprintln('main.json in ${cfg_dir} is not in the right format please fix.\nError: ${err}') - println(' - -## EXAMPLE OF A GOOD ONE: - -- note the list for buildDest and buildDestDev -- note its the full path where the html is pushed too - -{ - "title": "ThreeFold Web4", - "tagline": "ThreeFold Web4", - "url": "https://docs.threefold.io", - "url_home": "docs/introduction", - "image": "img/tf_graph.png", - "buildDest":["root@info.ourworld.tf:/root/hero/www/info/tfgrid4"], - "buildDestDev":["root@info.ourworld.tf:/root/hero/www/infodev/tfgrid4"] - -} - ') - exit(99) - } - - // Load and parse navbar config - navbar_content := os.read_file(os.join_path(cfg_dir, 'navbar.json'))! - navbar := json.decode(Navbar, navbar_content)! - - return Config{ - footer: footer - main: main - navbar: navbar - } -} - -pub fn (c Config) write(path string) ! { - mut footer_file := pathlib.get_file(path: '${path}/footer.json', create: true)! - footer_file.write(json.encode(c.footer))! - mut main_file := pathlib.get_file(path: '${path}/main.json', create: true)! - main_file.write(json.encode(c.main))! - mut navbar_file := pathlib.get_file(path: '${path}/navbar.json', create: true)! - navbar_file.write(json.encode(c.navbar))! -} diff --git a/libarchive/starlight/factory.v b/libarchive/starlight/factory.v deleted file mode 100644 index 873401a3..00000000 --- a/libarchive/starlight/factory.v +++ /dev/null @@ -1,41 +0,0 @@ -module starlight - -import os -import incubaid.herolib.core.pathlib - -@[heap] -pub struct StarlightFactory { -pub mut: - sites []&DocSite @[skip; str: skip] - path_build pathlib.Path - // path_publish pathlib.Path - args StarlightArgs -} - -@[params] -pub struct StarlightArgs { -pub mut: - // publish_path string - build_path string - production bool - update bool -} - -pub fn new(args_ StarlightArgs) !&StarlightFactory { - mut args := args_ - if args.build_path == '' { - args.build_path = '${os.home_dir()}/hero/var/starlight' - } - // if args.publish_path == ""{ - // args.publish_path = "${os.home_dir()}/hero/var/starlight/publish" - // } - mut ds := &StarlightFactory{ - args: args_ - path_build: pathlib.get_dir(path: args.build_path, create: true)! - // path_publish: pathlib.get_dir(path: args_.publish_path, create: true)! - } - - ds.template_install(install: true, template_update: args.update, delete: true)! - - return ds -} diff --git a/libarchive/starlight/model.v b/libarchive/starlight/model.v deleted file mode 100644 index eb2a548c..00000000 --- a/libarchive/starlight/model.v +++ /dev/null @@ -1,24 +0,0 @@ -module starlight - -pub struct SiteError { - Error -pub mut: - path string - msg string - cat ErrorCat -} - -pub enum ErrorCat { - unknown - image_double - file_double - file_not_found - image_not_found - page_double - page_not_found - sidebar - circular_import - def - summary - include -} diff --git a/libarchive/starlight/site.v b/libarchive/starlight/site.v deleted file mode 100644 index 17ae06d2..00000000 --- a/libarchive/starlight/site.v +++ /dev/null @@ -1,210 +0,0 @@ -module starlight - -import incubaid.herolib.osal.screen -import os -import incubaid.herolib.core.pathlib -import incubaid.herolib.osal.core as osal -import incubaid.herolib.ui.console - -@[heap] -pub struct DocSite { -pub mut: - name string - url string - path_src pathlib.Path - path_build pathlib.Path - // path_publish pathlib.Path - args SiteGetArgs - errors []SiteError - config Config - factory &StarlightFactory @[skip; str: skip] // Reference to the parent -} - -pub fn (mut s DocSite) build() ! { - s.generate()! - osal.exec( - cmd: ' - cd ${s.path_build.path} - bash build.sh - ' - retry: 0 - )! -} - -pub fn (mut s DocSite) build_dev_publish() ! { - s.generate()! - osal.exec( - cmd: ' - cd ${s.path_build.path} - bash build_dev_publish.sh - ' - retry: 0 - )! -} - -pub fn (mut s DocSite) build_publish() ! { - s.generate()! - osal.exec( - cmd: ' - cd ${s.path_build.path} - bash build_publish.sh - ' - retry: 0 - )! -} - -@[params] -pub struct DevArgs { -pub mut: - host string = 'localhost' - port int = 3000 -} - -pub fn (mut s DocSite) dev(args DevArgs) ! { - s.clean()! - s.generate()! - - // Create screen session for starlight development server - mut screen_name := 'starlight' - mut sf := screen.new()! - - // Add and start a new screen session - mut scr := sf.add( - name: screen_name - cmd: '/bin/bash' - start: true - attach: false - reset: true - )! - - // Send commands to the screen session - scr.cmd_send('cd ${s.path_build.path}')! - scr.cmd_send('bun start -p ${args.port} -h ${args.host}')! - - // Print instructions for user - console.print_header(' Starlight Development Server') - console.print_item('Development server is running in a screen session.') - console.print_item('Server is running on: https://${args.host}:${args.port}') - console.print_item('To view the server output:') - console.print_item(' 1. Attach to screen: screen -r ${screen_name}') - console.print_item(' 2. To detach from screen: Press Ctrl+A then D') - console.print_item(' 3. To list all screens: screen -ls') - console.print_item('The site content is on::') - console.print_item(' 1. location of documents: ${s.path_src.path}/src') - if osal.cmd_exists('code') { - console.print_item(' 2. We opened above dir in vscode.') - osal.exec(cmd: 'code ${s.path_src.path}/src')! - } - - // Start the watcher in a separate thread - // mut tf:=spawn watch_docs(docs_path, s.path_src.path, s.path_build.path) - // tf.wait()! - println('\n') - - if s.args.watch_changes { - docs_path := '${s.path_src.path}/src' - watch_docs(docs_path, s.path_src.path, s.path_build.path)! - } -} - -@[params] -pub struct ErrorArgs { -pub mut: - path string - msg string - cat ErrorCat -} - -pub fn (mut site DocSite) error(args ErrorArgs) { - // path2 := pathlib.get(args.path) - e := SiteError{ - path: args.path - msg: args.msg - cat: args.cat - } - site.errors << e - console.print_stderr(args.msg) -} - -fn check_item(item string) ! { - item2 := item.trim_space().trim('/').trim_space().all_after_last('/') - if ['internal', 'infodev', 'info', 'dev', 'friends', 'dd', 'web'].contains(item2) { - return error('destination path is wrong, cannot be: ${item}') - } -} - -fn (mut site DocSite) check() ! { - for item in site.config.main.build_dest { - check_item(item)! - } - for item in site.config.main.build_dest_dev { - check_item(item)! - } -} - -pub fn (mut site DocSite) generate() ! { - console.print_header(' site generate: ${site.name} on ${site.path_build.path}') - console.print_header(' site source on ${site.path_src.path}') - site.check()! - site.template_install()! - - // Now copy all directories that exist in src to build - for item in ['src', 'static', 'cfg', 'public'] { - if os.exists('${site.path_src.path}/${item}') { - mut aa := site.path_src.dir_get(item)! - aa.copy(dest: '${site.path_build.path}/${item}')! - } - } - for item in ['src'] { - if os.exists('${site.path_src.path}/${item}') { - mut aa := site.path_src.dir_get(item)! - aa.copy(dest: '${site.path_build.path}/${item}', delete: true)! - } - } -} - -fn (mut site DocSite) template_install() ! { - site.factory.template_install(template_update: false, install: false, delete: false)! - - cfg := site.config - - mut myhome := '\$\{HOME\}' // for usage in bash - - profile_include := osal.profile_path_source()!.replace(os.home_dir(), myhome) - - mydir := site.path_build.path.replace(os.home_dir(), myhome) - - for item in ['src', 'static'] { - mut aa := site.path_src.dir_get(item) or { continue } - aa.copy(dest: '${site.factory.path_build.path}/${item}', delete: false)! - } - - develop := $tmpl('templates/develop.sh') - build := $tmpl('templates/build.sh') - build_dev_publish := $tmpl('templates/build_dev_publish.sh') - build_publish := $tmpl('templates/build_publish.sh') - - mut develop_ := site.path_build.file_get_new('develop.sh')! - develop_.template_write(develop, true)! - develop_.chmod(0o700)! - - mut build_ := site.path_build.file_get_new('build.sh')! - build_.template_write(build, true)! - build_.chmod(0o700)! - - mut build_publish_ := site.path_build.file_get_new('build_publish.sh')! - build_publish_.template_write(build_publish, true)! - build_publish_.chmod(0o700)! - - mut build_dev_publish_ := site.path_build.file_get_new('build_dev_publish.sh')! - build_dev_publish_.template_write(build_dev_publish, true)! - build_dev_publish_.chmod(0o700)! - - mut develop2_ := site.path_src.file_get_new('develop.sh')! - develop2_.template_write(develop, true)! - develop2_.chmod(0o700)! - - mut build2_ := site.path_src.file_get_new('build.sh')! - build2_.template_write(build, true)! - build2_.chmod(0o700)! -} diff --git a/libarchive/starlight/site_get.v b/libarchive/starlight/site_get.v deleted file mode 100644 index 2775340b..00000000 --- a/libarchive/starlight/site_get.v +++ /dev/null @@ -1,109 +0,0 @@ -module starlight - -import os -import incubaid.herolib.core.pathlib -import incubaid.herolib.core.texttools -import incubaid.herolib.develop.gittools -import incubaid.herolib.ui.console - -@[params] -pub struct SiteGetArgs { -pub mut: - name string - nameshort string - path string - url string - publish_path string - build_path string - production bool - watch_changes bool = true - update bool - init bool // means create new one if needed - deploykey string - config ?Config -} - -pub fn (mut f StarlightFactory) get(args_ SiteGetArgs) !&DocSite { - console.print_header(' Starlight: ${args_.name}') - mut args := args_ - - if args.build_path.len == 0 { - args.build_path = '${f.path_build.path}' - } - // if args.publish_path.len == 0 { - // args.publish_path = '${f.path_publish.path}/${args.name}' - - // coderoot:"${os.home_dir()}/hero/var/publishcode" - mut gs := gittools.new(ssh_key_path: args.deploykey)! - - if args.url.len > 0 { - args.path = gs.get_path(url: args.url)! - } - - if args.path.trim_space() == '' { - args.path = os.getwd() - } - args.path = args.path.replace('~', os.home_dir()) - - mut r := gs.get_repo( - url: 'https://github.com/incubaid/starlight_template.git' - )! - mut template_path := r.patho()! - - // First, check if the new site args provides a configuration that can be written instead of template cfg dir - if cfg := args.config { - cfg.write('${args.path}/cfg')! - } else { - // Then ensure cfg directory exists in src, - if !os.exists('${args.path}/cfg') { - if args.init { - // else copy config from template - mut template_cfg := template_path.dir_get('cfg')! - template_cfg.copy(dest: '${args.path}/cfg')! - } else { - return error("Can't find cfg dir in chosen starlight location: ${args.path}") - } - } - } - - if !os.exists('${args.path}/src') { - if args.init { - mut template_cfg := template_path.dir_get('src')! - template_cfg.copy(dest: '${args.path}/src')! - } else { - return error("Can't find src dir in chosen starlight location: ${args.path}") - } - } - - mut myconfig := load_config('${args.path}/cfg')! - - if args.name == '' { - args.name = myconfig.main.name - } - - if args.name.len == 0 { - return error('name for a site cannot be empty') - } - - if args.nameshort.len == 0 { - args.nameshort = args.name - } - args.nameshort = texttools.name_fix(args.nameshort) - - mut ds := DocSite{ - name: args.name - url: args.url - path_src: pathlib.get_dir(path: args.path, create: false)! - path_build: f.path_build - // path_publish: pathlib.get_dir(path: args.publish_path, create: true)! - args: args - config: myconfig - factory: &f - } - - ds.check()! - - f.sites << &ds - - return &ds -} diff --git a/libarchive/starlight/template.v b/libarchive/starlight/template.v deleted file mode 100644 index 50c07915..00000000 --- a/libarchive/starlight/template.v +++ /dev/null @@ -1,55 +0,0 @@ -module starlight - -import incubaid.herolib.develop.gittools -import incubaid.herolib.osal.core as osal -import incubaid.herolib.installers.web.bun -import incubaid.herolib.installers.web.tailwind -import os - -@[params] -struct TemplateInstallArgs { - template_update bool = true - install bool - delete bool = true -} - -fn (mut self StarlightFactory) template_install(args TemplateInstallArgs) ! { - mut gs := gittools.new()! - - mut r := gs.get_repo( - url: 'https://github.com/incubaid/starlight_template.git' - pull: args.template_update - )! - mut template_path := r.patho()! - - for item in ['public', 'src'] { - mut aa := template_path.dir_get(item) or { continue } // skip if not exist - aa.copy(dest: '${self.path_build.path}/${item}', delete: args.delete)! - } - - for item in ['package.json', 'tsconfig.json', 'astro.config.mjs'] { - src_path := os.join_path(template_path.path, item) - dest_path := os.join_path(self.path_build.path, item) - os.cp(src_path, dest_path) or { - return error('Failed to copy ${item} to build path: ${err}') - } - } - - if args.install { - // install bun - mut installer := bun.get()! - installer.install()! - - mut installer2 := tailwind.get()! - installer2.install()! - - osal.exec( - cmd: ' - ${osal.profile_path_source_and()!} - export PATH=/tmp/starlight_build/node_modules/.bin:${os.home_dir()}/.bun/bin/:??PATH - cd ${self.path_build.path} - bun install - ' - )! - } -} diff --git a/libarchive/starlight/templates/build.sh b/libarchive/starlight/templates/build.sh deleted file mode 100755 index 1d342637..00000000 --- a/libarchive/starlight/templates/build.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -set -ex - -script_dir="???cd "???dirname "??{BASH_SOURCE[0]}")" && pwd)" -cd "??{script_dir}" - -echo "Docs directory: ??script_dir" - -cd "${mydir}" - -export PATH=${site.path_build.path}/node_modules/.bin:??{HOME}/.bun/bin/:??PATH - -rm -rf ${site.path_build.path}/build/ - -${profile_include} - -bun run build - -mkdir -p ${site.args.publish_path.trim_right("/")} -echo SYNC TO ${site.args.publish_path.trim_right("/")} -rsync -rv --delete ${site.path_build.path}/build/ ${site.args.publish_path.trim_right("/")}/ diff --git a/libarchive/starlight/templates/build_dev_publish.sh b/libarchive/starlight/templates/build_dev_publish.sh deleted file mode 100755 index ff14377d..00000000 --- a/libarchive/starlight/templates/build_dev_publish.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -set -e - -script_dir="???cd "???dirname "??{BASH_SOURCE[0]}")" && pwd)" -cd "??{script_dir}" - - -echo "Docs directory: ??script_dir" - -cd "${mydir}" - -export PATH=${site.path_build.path}/node_modules/.bin:??{HOME}/.bun/bin/:??PATH - -rm -rf ${site.path_build.path}/build/ - -${profile_include} - -bun run build - -@for dest in cfg.main.build_dest_dev -rsync -rv --delete ${site.path_build.path}/build/ ${dest.trim_right("/")}/ -@end diff --git a/libarchive/starlight/templates/build_publish.sh b/libarchive/starlight/templates/build_publish.sh deleted file mode 100755 index 935f9877..00000000 --- a/libarchive/starlight/templates/build_publish.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -set -ex - -script_dir="???cd "???dirname "??{BASH_SOURCE[0]}")" && pwd)" -cd "??{script_dir}" - -echo "Docs directory: ??script_dir" - -cd "${mydir}" - -export PATH=${site.path_build.path}/node_modules/.bin:??{HOME}/.bun/bin/:??PATH - -rm -rf ${site.path_build.path}/build/ - -${profile_include} - -bun run build - -@for dest in cfg.main.build_dest -rsync -rv --delete ${site.path_build.path}/build/ ${dest.trim_right("/")}/ -@end diff --git a/libarchive/starlight/templates/develop.sh b/libarchive/starlight/templates/develop.sh deleted file mode 100755 index fa8fcbd7..00000000 --- a/libarchive/starlight/templates/develop.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -set -e - -script_dir="???cd "???dirname "??{BASH_SOURCE[0]}")" && pwd)" -cd "??{script_dir}" - -echo "Docs directory: ??script_dir" - -cd "${mydir}" - -export PATH=${site.path_build.path}/node_modules/.bin:??{HOME}/.bun/bin/:??PATH - -${profile_include} - -bun dev diff --git a/libarchive/starlight/watcher.v b/libarchive/starlight/watcher.v deleted file mode 100644 index b8ababb5..00000000 --- a/libarchive/starlight/watcher.v +++ /dev/null @@ -1,96 +0,0 @@ -module starlight - -import incubaid.herolib.osal.notifier -import os - -fn watch_docs(docs_path string, path_src string, path_build string) ! { - mut n := notifier.new('docsite_watcher') or { - eprintln('Failed to create watcher: ${err}') - return - } - - n.args['path_src'] = path_src - n.args['path_build'] = path_build - - // Add watch with captured args - n.add_watch(docs_path, fn (event notifier.NotifyEvent, path string, args map[string]string) { - handle_file_change(event, path, args) or { eprintln('Error handling file change: ${err}') } - })! - - n.start()! -} - -// handle_file_change processes file system events -fn handle_file_change(event notifier.NotifyEvent, path string, args map[string]string) ! { - file_base := os.base(path) - is_dir := os.is_dir(path) - - // Skip files starting with # - if file_base.starts_with('#') { - return - } - - // For files (not directories), check extensions - if !is_dir { - ext := os.file_ext(path).to_lower() - if ext !in ['.md', '.png', '.jpeg', '.jpg'] { - return - } - } - - // Get relative path from docs directory - rel_path := path.replace('${args['path_src']}/src/', '') - dest_path := '${args['path_build']}/src/${rel_path}' - - match event { - .create, .modify { - if is_dir { - // For directories, just ensure they exist - os.mkdir_all(dest_path) or { - return error('Failed to create directory ${dest_path}: ${err}') - } - println('Created directory: ${rel_path}') - } else { - // For files, ensure parent directory exists and copy - os.mkdir_all(os.dir(dest_path)) or { - return error('Failed to create directory ${os.dir(dest_path)}: ${err}') - } - os.cp(path, dest_path) or { - return error('Failed to copy ${path} to ${dest_path}: ${err}') - } - println('Updated: ${rel_path}') - } - } - .delete { - if os.exists(dest_path) { - if is_dir { - os.rmdir_all(dest_path) or { - return error('Failed to delete directory ${dest_path}: ${err}') - } - println('Deleted directory: ${rel_path}') - } else { - os.rm(dest_path) or { return error('Failed to delete ${dest_path}: ${err}') } - println('Deleted: ${rel_path}') - } - } - } - .rename { - // For rename events, fswatch provides the new path in the event - // The old path is already removed, so we just need to handle the new path - if is_dir { - os.mkdir_all(dest_path) or { - return error('Failed to create directory ${dest_path}: ${err}') - } - println('Renamed directory to: ${rel_path}') - } else { - os.mkdir_all(os.dir(dest_path)) or { - return error('Failed to create directory ${os.dir(dest_path)}: ${err}') - } - os.cp(path, dest_path) or { - return error('Failed to copy ${path} to ${dest_path}: ${err}') - } - println('Renamed to: ${rel_path}') - } - } - } -} diff --git a/libarchive/zinit/openrpc.json b/libarchive/zinit/openrpc.json deleted file mode 100644 index 6e3e3eee..00000000 --- a/libarchive/zinit/openrpc.json +++ /dev/null @@ -1,873 +0,0 @@ -{ - "openrpc": "1.2.6", - "info": { - "version": "1.0.0", - "title": "Zinit JSON-RPC API", - "description": "JSON-RPC 2.0 API for controlling and querying Zinit services", - "license": { - "name": "MIT" - } - }, - "servers": [ - { - "name": "Unix Socket", - "url": "unix:///tmp/zinit.sock" - } - ], - "methods": [ - { - "name": "rpc.discover", - "description": "Returns the OpenRPC specification for the API", - "params": [], - "result": { - "name": "OpenRPCSpec", - "description": "The OpenRPC specification", - "schema": { - "type": "object" - } - }, - "examples": [ - { - "name": "Get API specification", - "params": [], - "result": { - "name": "OpenRPCSpecResult", - "value": { - "openrpc": "1.2.6", - "info": { - "version": "1.0.0", - "title": "Zinit JSON-RPC API" - } - } - } - } - ] - }, - { - "name": "service_list", - "description": "Lists all services managed by Zinit", - "params": [], - "result": { - "name": "ServiceList", - "description": "A map of service names to their current states", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "description": "Service state (Running, Success, Error, etc.)" - } - } - }, - "examples": [ - { - "name": "List all services", - "params": [], - "result": { - "name": "ServiceListResult", - "value": { - "service1": "Running", - "service2": "Success", - "service3": "Error" - } - } - } - ] - }, - { - "name": "service_status", - "description": "Shows detailed status information for a specific service", - "params": [ - { - "name": "name", - "description": "The name of the service", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "ServiceStatus", - "description": "Detailed status information for the service", - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Service name" - }, - "pid": { - "type": "integer", - "description": "Process ID of the running service (if running)" - }, - "state": { - "type": "string", - "description": "Current state of the service (Running, Success, Error, etc.)" - }, - "target": { - "type": "string", - "description": "Target state of the service (Up, Down)" - }, - "after": { - "type": "object", - "description": "Dependencies of the service and their states", - "additionalProperties": { - "type": "string", - "description": "State of the dependency" - } - } - } - } - }, - "examples": [ - { - "name": "Get status of redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "ServiceStatusResult", - "value": { - "name": "redis", - "pid": 1234, - "state": "Running", - "target": "Up", - "after": { - "dependency1": "Success", - "dependency2": "Running" - } - } - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - } - ] - }, - { - "name": "service_start", - "description": "Starts a service", - "params": [ - { - "name": "name", - "description": "The name of the service to start", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "StartResult", - "description": "Result of the start operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Start redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "StartResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - } - ] - }, - { - "name": "service_stop", - "description": "Stops a service", - "params": [ - { - "name": "name", - "description": "The name of the service to stop", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "StopResult", - "description": "Result of the stop operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Stop redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "StopResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - }, - { - "code": -32003, - "message": "Service is down", - "data": "service \"redis\" is down" - } - ] - }, - { - "name": "service_monitor", - "description": "Starts monitoring a service. The service configuration is loaded from the config directory.", - "params": [ - { - "name": "name", - "description": "The name of the service to monitor", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "MonitorResult", - "description": "Result of the monitor operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Monitor redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "MonitorResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32001, - "message": "Service already monitored", - "data": "service \"redis\" already monitored" - }, - { - "code": -32005, - "message": "Config error", - "data": "failed to load service configuration" - } - ] - }, - { - "name": "service_forget", - "description": "Stops monitoring a service. You can only forget a stopped service.", - "params": [ - { - "name": "name", - "description": "The name of the service to forget", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "ForgetResult", - "description": "Result of the forget operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Forget redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "ForgetResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - }, - { - "code": -32002, - "message": "Service is up", - "data": "service \"redis\" is up" - } - ] - }, - { - "name": "service_kill", - "description": "Sends a signal to a running service", - "params": [ - { - "name": "name", - "description": "The name of the service to send the signal to", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "signal", - "description": "The signal to send (e.g., SIGTERM, SIGKILL)", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "KillResult", - "description": "Result of the kill operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Send SIGTERM to redis service", - "params": [ - { - "name": "name", - "value": "redis" - }, - { - "name": "signal", - "value": "SIGTERM" - } - ], - "result": { - "name": "KillResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - }, - { - "code": -32003, - "message": "Service is down", - "data": "service \"redis\" is down" - }, - { - "code": -32004, - "message": "Invalid signal", - "data": "invalid signal: INVALID" - } - ] - }, - { - "name": "system_shutdown", - "description": "Stops all services and powers off the system", - "params": [], - "result": { - "name": "ShutdownResult", - "description": "Result of the shutdown operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Shutdown the system", - "params": [], - "result": { - "name": "ShutdownResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32006, - "message": "Shutting down", - "data": "system is already shutting down" - } - ] - }, - { - "name": "system_reboot", - "description": "Stops all services and reboots the system", - "params": [], - "result": { - "name": "RebootResult", - "description": "Result of the reboot operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Reboot the system", - "params": [], - "result": { - "name": "RebootResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32006, - "message": "Shutting down", - "data": "system is already shutting down" - } - ] - }, - { - "name": "service_create", - "description": "Creates a new service configuration file", - "params": [ - { - "name": "name", - "description": "The name of the service to create", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "content", - "description": "The service configuration content", - "required": true, - "schema": { - "type": "object", - "properties": { - "exec": { - "type": "string", - "description": "Command to run" - }, - "oneshot": { - "type": "boolean", - "description": "Whether the service should be restarted" - }, - "after": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Services that must be running before this one starts" - }, - "log": { - "type": "string", - "enum": ["null", "ring", "stdout"], - "description": "How to handle service output" - }, - "env": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Environment variables for the service" - }, - "shutdown_timeout": { - "type": "integer", - "description": "Maximum time to wait for service to stop during shutdown" - } - } - } - } - ], - "result": { - "name": "CreateServiceResult", - "description": "Result of the create operation", - "schema": { - "type": "string" - } - }, - "errors": [ - { - "code": -32007, - "message": "Service already exists", - "data": "Service 'name' already exists" - }, - { - "code": -32008, - "message": "Service file error", - "data": "Failed to create service file" - } - ] - }, - { - "name": "service_delete", - "description": "Deletes a service configuration file", - "params": [ - { - "name": "name", - "description": "The name of the service to delete", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "DeleteServiceResult", - "description": "Result of the delete operation", - "schema": { - "type": "string" - } - }, - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "Service 'name' not found" - }, - { - "code": -32008, - "message": "Service file error", - "data": "Failed to delete service file" - } - ] - }, - { - "name": "service_get", - "description": "Gets a service configuration file", - "params": [ - { - "name": "name", - "description": "The name of the service to get", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "GetServiceResult", - "description": "The service configuration", - "schema": { - "type": "object" - } - }, - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "Service 'name' not found" - }, - { - "code": -32008, - "message": "Service file error", - "data": "Failed to read service file" - } - ] - }, - { - "name": "service_stats", - "description": "Get memory and CPU usage statistics for a service", - "params": [ - { - "name": "name", - "description": "The name of the service to get stats for", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "ServiceStats", - "description": "Memory and CPU usage statistics for the service", - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Service name" - }, - "pid": { - "type": "integer", - "description": "Process ID of the service" - }, - "memory_usage": { - "type": "integer", - "description": "Memory usage in bytes" - }, - "cpu_usage": { - "type": "number", - "description": "CPU usage as a percentage (0-100)" - }, - "children": { - "type": "array", - "description": "Stats for child processes", - "items": { - "type": "object", - "properties": { - "pid": { - "type": "integer", - "description": "Process ID of the child process" - }, - "memory_usage": { - "type": "integer", - "description": "Memory usage in bytes" - }, - "cpu_usage": { - "type": "number", - "description": "CPU usage as a percentage (0-100)" - } - } - } - } - } - } - }, - "examples": [ - { - "name": "Get stats for redis service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "ServiceStatsResult", - "value": { - "name": "redis", - "pid": 1234, - "memory_usage": 10485760, - "cpu_usage": 2.5, - "children": [ - { - "pid": 1235, - "memory_usage": 5242880, - "cpu_usage": 1.2 - } - ] - } - } - } - ], - "errors": [ - { - "code": -32000, - "message": "Service not found", - "data": "service name \"unknown\" unknown" - }, - { - "code": -32003, - "message": "Service is down", - "data": "service \"redis\" is down" - } - ] - }, - { - "name": "system_start_http_server", - "description": "Start an HTTP/RPC server at the specified address", - "params": [ - { - "name": "address", - "description": "The network address to bind the server to (e.g., '127.0.0.1:8080')", - "required": true, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "StartHttpServerResult", - "description": "Result of the start HTTP server operation", - "schema": { - "type": "string" - } - }, - "examples": [ - { - "name": "Start HTTP server on localhost:8080", - "params": [ - { - "name": "address", - "value": "127.0.0.1:8080" - } - ], - "result": { - "name": "StartHttpServerResult", - "value": "HTTP server started at 127.0.0.1:8080" - } - } - ], - "errors": [ - { - "code": -32602, - "message": "Invalid address", - "data": "Invalid network address format" - } - ] - }, - { - "name": "system_stop_http_server", - "description": "Stop the HTTP/RPC server if running", - "params": [], - "result": { - "name": "StopHttpServerResult", - "description": "Result of the stop HTTP server operation", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Stop the HTTP server", - "params": [], - "result": { - "name": "StopHttpServerResult", - "value": null - } - } - ], - "errors": [ - { - "code": -32602, - "message": "Server not running", - "data": "No HTTP server is currently running" - } - ] - }, - { - "name": "stream_currentLogs", - "description": "Get current logs from zinit and monitored services", - "params": [ - { - "name": "name", - "description": "Optional service name filter. If provided, only logs from this service will be returned", - "required": false, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "LogsResult", - "description": "Array of log strings", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "examples": [ - { - "name": "Get all logs", - "params": [], - "result": { - "name": "LogsResult", - "value": [ - "2023-01-01T12:00:00 redis: Starting service", - "2023-01-01T12:00:01 nginx: Starting service" - ] - } - }, - { - "name": "Get logs for a specific service", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "LogsResult", - "value": [ - "2023-01-01T12:00:00 redis: Starting service", - "2023-01-01T12:00:02 redis: Service started" - ] - } - } - ] - }, - { - "name": "stream_subscribeLogs", - "description": "Subscribe to log messages generated by zinit and monitored services", - "params": [ - { - "name": "name", - "description": "Optional service name filter. If provided, only logs from this service will be returned", - "required": false, - "schema": { - "type": "string" - } - } - ], - "result": { - "name": "LogSubscription", - "description": "A subscription to log messages", - "schema": { - "type": "string" - } - }, - "examples": [ - { - "name": "Subscribe to all logs", - "params": [], - "result": { - "name": "LogSubscription", - "value": "2023-01-01T12:00:00 redis: Service started" - } - }, - { - "name": "Subscribe to filtered logs", - "params": [ - { - "name": "name", - "value": "redis" - } - ], - "result": { - "name": "LogSubscription", - "value": "2023-01-01T12:00:00 redis: Service started" - } - } - ] - } - ] -} \ No newline at end of file diff --git a/libarchive/zinit/readme.md b/libarchive/zinit/readme.md deleted file mode 100644 index cf57ade2..00000000 --- a/libarchive/zinit/readme.md +++ /dev/null @@ -1,155 +0,0 @@ -# Zinit OpenRPC Client - -This module provides a V language client for interacting with Zinit process manager using the OpenRPC protocol over a Unix socket. - -## Overview - -Zinit is a process manager that allows you to manage services on a system. This client provides a way to interact with Zinit using its JSON-RPC API, which follows the OpenRPC specification. - -## Features - -- Full implementation of the Zinit OpenRPC API -- Type-safe request and response handling -- Support for all Zinit operations: - - Service management (start, stop, monitor, forget) - - Service status and statistics - - System operations (shutdown, reboot) - - Log retrieval - - Service configuration management - -## Usage - -### Basic Usage - -```v -import incubaid.herolib.osal.startupmanager - -fn main() { - // Create a new Zinit client with the default socket path - mut zinit_client := zinit.new_stateless()! - - // List all services - service_list := zinit_client.client.list()! - println('Services:') - for name, state in service_list { - println('- ${name}: ${state}') - } - - // Start a service - zinit_client.client.start('my_service')! - - // Get service status - status := zinit_client.client.status('my_service')! - println('Service state: ${status.state}') -} -``` - -### Creating a New Service - -```v -import incubaid.herolib.osal.startupmanager - -fn main() { - mut zinit_client := zinit.new_stateless()! - - // Define service configuration - service_config := zinit.ServiceConfig{ - exec: '/usr/bin/my-program --option value' - oneshot: false - after: ['dependency1', 'dependency2'] - log: 'stdout' - env: { - 'ENV_VAR1': 'value1' - 'ENV_VAR2': 'value2' - } - shutdown_timeout: 30 - } - - // Create the service - zinit_client.client.create_service('my_service', service_config)! - - // Monitor and start the service - zinit_client.client.monitor('my_service')! - zinit_client.client.start('my_service')! -} -``` - -### Getting Service Statistics - -```v -import incubaid.herolib.osal.startupmanager - -fn main() { - mut zinit_client := zinit.new_stateless()! - - // Get service stats - stats := zinit_client.client.stats('my_service')! - - println('Memory usage: ${stats.memory_usage} bytes') - println('CPU usage: ${stats.cpu_usage}%') - - // Print child process stats - for child in stats.children { - println('Child PID: ${child.pid}, Memory: ${child.memory_usage} bytes') - } -} -``` - -### Retrieving Logs - -```v -import incubaid.herolib.osal.startupmanager - -fn main() { - mut zinit_client := zinit.new_stateless()! - - // Get logs for a specific service - logs := zinit_client.client.get_logs('my_service')! - for log in logs { - println(log) - } - - // Get all logs - all_logs := zinit_client.client.get_all_logs()! - for log in all_logs { - println(log) - } -} -``` - -## API Reference - -### Client Methods - -- `discover()` - Returns the OpenRPC specification for the API -- `list()` - Returns a map of service names to their current states -- `status(name string)` - Returns detailed status information for a specific service -- `start(name string)` - Starts a service -- `stop(name string)` - Stops a service -- `monitor(name string)` - Starts monitoring a service -- `forget(name string)` - Stops monitoring a service -- `kill(name string, signal string)` - Sends a signal to a running service -- `shutdown()` - Stops all services and powers off the system -- `reboot()` - Stops all services and reboots the system -- `stats(name string)` - Returns memory and CPU usage statistics for a service -- `get_logs(name string)` - Returns current logs from a specific service -- `get_all_logs()` - Returns all current logs from zinit and monitored services -- `create_service(name string, config ServiceConfig)` - Creates a new service configuration file -- `delete_service(name string)` - Deletes a service configuration file -- `get_service(name string)` - Gets a service configuration file -- `start_http_server(address string)` - Starts an HTTP/RPC server at the specified address -- `stop_http_server()` - Stops the HTTP/RPC server if running - -### Data Structures - -- `ServiceStatus` - Detailed status information for a service -- `ServiceStats` - Memory and CPU usage statistics for a service -- `ServiceConfig` - Configuration for creating a new service - -## OpenRPC Specification - -The full OpenRPC specification for the Zinit API is available in the `openrpc.json` file. This specification defines all the methods, parameters, and return types for the API. - -## Example - -See the `examples/osal/zinit/zinit_openrpc_example.v` file for a complete example of using the Zinit OpenRPC client. diff --git a/libarchive/zinit/rpc.v b/libarchive/zinit/rpc.v deleted file mode 100644 index 88ae8950..00000000 --- a/libarchive/zinit/rpc.v +++ /dev/null @@ -1,200 +0,0 @@ -module zinit - -import net.unix -import json -import incubaid.herolib.ui.console - -// these need to be all private (non pub) - -pub struct Client { - socket_path string = '/var/run/zinit.sock' -} - -enum State { - ok @[json: 'ok'] - error @[json: 'error'] -} - -struct ZinitResponse { - state State - body string @[raw] -} - -@[params] -struct ZinitClientArgs { - socket_path string = '/var/run/zinit.sock' -} - -pub fn new_rpc_client(args ZinitClientArgs) Client { - return Client{ - socket_path: args.socket_path - } -} - -fn (z Client) connect() !&unix.StreamConn { - mut s := unix.connect_stream(z.socket_path)! - return s -} - -fn close(sc &unix.StreamConn) { - unix.shutdown(sc.sock.handle) -} - -// get string from the zinit socket -fn (z Client) rpc(cmd string) !string { - mut c := z.connect()! - console.print_debug("zinit rpc: '${cmd}'") - c.write_string(cmd + '\n')! - mut res := []u8{len: 5000, cap: 5000} - n := c.read(mut res)! - close(c) - return res[..n].bytestr() -} - -fn (z Client) list() !map[string]string { - response := z.rpc('list')! - decoded_response := json.decode(ZinitResponse, response)! - // println("") - // println(decoded_response) - // println("") - if decoded_response.state == .error { - $if debug { - print_backtrace() - } - return error('zinit list failed: ${decoded_response.body}') - } - return json.decode(map[string]string, decoded_response.body)! -} - -struct ServiceStatusRaw { - after map[string]string - name string - pid int - state string - target string -} - -//{"state":"ok","body":{"after":{"delay":"Success"},"name":"redis","pid":320996,"state":"Running","target":"Up"}} - -// check if the service is known -fn (z Client) isloaded(name string) bool { - // console.print_debug(" -- status rpc: '$name'") - r := z.list() or { return false } - if name !in r { - return false - } - return true -} - -fn (z Client) status(name string) !ServiceStatusRaw { - // console.print_debug(" -- status rpc: '$name'") - r := z.list()! - if name !in r { - $if debug { - print_backtrace() - } - return error("cannot ask status over rpc, service with name:'${name}' not found in rpc daemon.\nFOUND:${r}") - } - - response := z.rpc('status ${name}')! - decoded_response := json.decode(ZinitResponse, response)! - - if decoded_response.state == .error { - $if debug { - print_backtrace() - } - return error('service ${name} status failed: ${decoded_response.body}') - } - - return json.decode(ServiceStatusRaw, decoded_response.body)! -} - -fn (z Client) start(name string) ! { - response := z.rpc('start ${name}')! - decoded_response := json.decode(ZinitResponse, response)! - if decoded_response.state == .error { - $if debug { - print_backtrace() - } - return error('service ${name} start failed: ${decoded_response.body}') - } -} - -fn (z Client) stop(name string) ! { - response := z.rpc('stop ${name}')! - decoded_response := json.decode(ZinitResponse, response)! - if decoded_response.state == .error { - $if debug { - print_backtrace() - } - return error('service ${name} stop failed: ${decoded_response.body}') - } -} - -fn (z Client) forget(name string) ! { - response := z.rpc('forget ${name}')! - decoded_response := json.decode(ZinitResponse, response)! - if decoded_response.state == .error { - $if debug { - print_backtrace() - } - return error('service ${name} forget failed: ${decoded_response.body}') - } -} - -fn (z Client) monitor(name string) ! { - response := z.rpc('monitor ${name}')! - decoded_response := json.decode(ZinitResponse, response)! - if decoded_response.state == .error { - $if debug { - print_backtrace() - } - return error('service ${name} monitor failed: ${decoded_response.body}') - } -} - -fn (z Client) kill(name string, signal string) ! { - response := z.rpc('kill ${name} ${signal}')! - decoded_response := json.decode(ZinitResponse, response)! - if decoded_response.state == .error { - $if debug { - print_backtrace() - } - return error('service ${name} kill failed: ${decoded_response.body}') - } -} - -fn (z Client) shutdown() ! { - response := z.rpc('shutdown')! - decoded_response := json.decode(ZinitResponse, response)! - if decoded_response.state == .error { - $if debug { - print_backtrace() - } - return error('zinit shutdown failed: ${decoded_response.body}') - } -} - -fn (z Client) reboot() ! { - response := z.rpc('reboot')! - decoded_response := json.decode(ZinitResponse, response)! - if decoded_response.state == .error { - $if debug { - print_backtrace() - } - return error('zinit reboot failed: ${decoded_response.body}') - } -} - -fn (z Client) log(name string) !string { - response := z.rpc('log ${name}')! - decoded_response := json.decode(ZinitResponse, response)! - if decoded_response.state == .error { - $if debug { - print_backtrace() - } - return error('zinit log failed: ${decoded_response.body}') - } - - return decoded_response.body -} diff --git a/libarchive/zinit/rpc_test.v b/libarchive/zinit/rpc_test.v deleted file mode 100644 index b75efb09..00000000 --- a/libarchive/zinit/rpc_test.v +++ /dev/null @@ -1,88 +0,0 @@ -module zinit - -import os -import time -import incubaid.herolib.core -import incubaid.herolib.osal.core as osal - -fn test_zinit() { - if !core.is_linux()! { - // zinit is only supported on linux - return - } - - // TODO: use zinit installer to install zinit - // this is a workaround since we can't import zinit installer due to circular dependency - zinit_version := os.execute('zinit --version') - if zinit_version.exit_code != 0 { - release_url := 'https://github.com/threefoldtech/zinit/releases/download/v0.2.14/zinit' - - mut dest := osal.download( - url: release_url - minsize_kb: 2000 - reset: true - dest: '/tmp/zinit' - )! - - chmod_cmd := os.execute('chmod +x /tmp/zinit') - assert chmod_cmd.exit_code == 0, 'failed to chmod +x /tmp/zinit: ${chmod_cmd.output}' - } - - this_dir := os.dir(@FILE) - // you need to have zinit in your path to run this test - spawn os.execute('/tmp/zinit -s ${this_dir}/zinit/zinit.sock init -c ${this_dir}/zinit') - time.sleep(time.second) - - client := new_rpc_client(socket_path: '${this_dir}/zinit/zinit.sock') - - mut ls := client.list()! - mut want_ls := { - 'service_1': 'Running' - 'service_2': 'Running' - } - assert ls == want_ls - - mut st := client.status('service_2')! - assert st.after == { - 'service_1': 'Running' - } - assert st.name == 'service_2' - assert st.state == 'Running' - assert st.target == 'Up' - - client.stop('service_2')! - st = client.status('service_2')! - assert st.target == 'Down' - - time.sleep(time.millisecond * 10) - client.forget('service_2')! - ls = client.list()! - want_ls = { - 'service_1': 'Running' - } - assert ls == want_ls - - client.monitor('service_2')! - time.sleep(time.millisecond * 10) - st = client.status('service_2')! - assert st.after == { - 'service_1': 'Running' - } - assert st.name == 'service_2' - assert st.state == 'Running' - assert st.target == 'Up' - - client.stop('service_2')! - time.sleep(time.millisecond * 10) - client.start('service_2')! - st = client.status('service_2')! - assert st.target == 'Up' - - client.kill('service_1', 'sigterm')! - time.sleep(time.millisecond * 10) - st = client.status('service_1')! - assert st.state.contains('SIGTERM') - - // Remove the socet file - os.rm('${this_dir}/zinit/zinit.sock')! -} diff --git a/libarchive/zinit/zinit.v b/libarchive/zinit/zinit.v deleted file mode 100644 index f2355409..00000000 --- a/libarchive/zinit/zinit.v +++ /dev/null @@ -1,178 +0,0 @@ -module zinit - -import incubaid.herolib.core.texttools -import incubaid.herolib.ui.console -import incubaid.herolib.core.pathlib -import incubaid.herolib.data.ourtime -import os -import json - -@[heap] -pub struct Zinit { -pub mut: - processes map[string]ZProcess - path pathlib.Path - pathcmds pathlib.Path -} - -@[params] -pub struct ZProcessNewArgs { -pub mut: - name string @[required] - cmd string @[required] - cmd_stop string // command to stop (optional) - cmd_test string // command line to test service is running - workdir string // where to execute the commands - after []string // list of service we depend on - env map[string]string - oneshot bool - start bool = true - restart bool = true // whether the process should be restarted on failure -} - -// will delete the process if it exists while starting -pub fn (mut zinit Zinit) new(args_ ZProcessNewArgs) !ZProcess { - console.print_header(' zinit process new') - mut args := args_ - - if args.cmd.len == 0 { - $if debug { - print_backtrace() - } - return error('cmd cannot be empty for ${args} in zinit.') - } - - if zinit.exists(args.name) { - mut p := zinit.get(args.name)! - p.destroy()! - } - - mut zp := ZProcess{ - name: args.name - cmd: args.cmd - cmd_test: args.cmd_test - cmd_stop: args.cmd_stop - env: args.env.move() - after: args.after - start: args.start - restart: args.restart - oneshot: args.oneshot - workdir: args.workdir - } - - zinit.cmd_write(args.name, args.cmd, '_start', {}, args.workdir)! - zinit.cmd_write(args.name, args.cmd_test, '_test', {}, args.workdir)! - zinit.cmd_write(args.name, args.cmd_stop, '_stop', {}, args.workdir)! - - mut json_path := zinit.pathcmds.file_get_new('${args.name}.json')! - json_content := json.encode(args) - json_path.write(json_content)! - - mut pathyaml := zinit.path.file_get_new(zp.name + '.yaml')! - // console.print_debug('debug zprocess path yaml: ${pathyaml}') - pathyaml.write(zp.config_content()!)! - if zp.start { - zp.start()! - } - zinit.processes[args.name] = zp - - return zp -} - -fn (mut zinit Zinit) cmd_write(name string, cmd string, cat string, env map[string]string, workdir string) !string { - if cmd.trim_space() == '' { - return '' - } - mut zinitobj := new()! - mut pathcmd := zinitobj.pathcmds.file_get_new('${name}${cat}.sh')! - mut cmd_out := '#!/bin/bash\nset -e\n\n' - - if cat == '_start' { - cmd_out += 'echo === START ======== ${ourtime.now().str()} === \n' - } - for key, val in env { - cmd_out += '${key}=${val}\n' - } - - if workdir.trim_space() != '' { - cmd_out += 'cd ${workdir.trim_space()}\n' - } - - cmd_out += texttools.dedent(cmd) + '\n' - pathcmd.write(cmd_out)! - pathcmd.chmod(0x770)! - return '/bin/bash -c ${pathcmd.path}' -} - -pub fn (mut zinit Zinit) get(name_ string) !ZProcess { - name := texttools.name_fix(name_) - if zinit.processes.keys().contains(name) { - return zinit.processes[name] - } - - return error("cannot find process in zinit:'${name}'") -} - -pub fn (mut zinit Zinit) exists(name_ string) bool { - name := texttools.name_fix(name_) - if name in zinit.processes { - return true - } - return false -} - -pub fn (mut zinit Zinit) stop(name string) ! { - mut p := zinit.get(name)! - - p.stop()! -} - -pub fn (mut zinit Zinit) start(name string) ! { - mut p := zinit.get(name)! - p.start()! -} - -pub fn (mut zinit Zinit) delete(name string) ! { - mut p := zinit.get(name)! - p.destroy()! -} - -pub fn (mut self Zinit) load() ! { - cmd := 'zinit list' - mut res := os.execute(cmd) - if res.exit_code > 0 { - if res.output.contains('failed to connect') { - res = os.execute(cmd) - if res.exit_code > 0 { - $if debug { - print_backtrace() - } - return error("can't do zinit list, after start of zinit.\n${res}") - } - } else { - $if debug { - print_backtrace() - } - return error("can't do zinit list.\n${res}") - } - } - mut state := '' - for line in res.output.split_into_lines() { - if line.starts_with('---') { - state = 'ok' - continue - } - if state == 'ok' && line.contains(':') { - name := line.split(':')[0].to_lower().trim_space() - mut zp := ZProcess{ - name: name - } - zp.load()! - self.processes[name] = zp - } - } -} - -pub fn (mut self Zinit) names() []string { - return self.processes.keys() -} diff --git a/libarchive/zinit/zinit/service_1.yaml b/libarchive/zinit/zinit/service_1.yaml deleted file mode 100644 index 856129a6..00000000 --- a/libarchive/zinit/zinit/service_1.yaml +++ /dev/null @@ -1 +0,0 @@ -exec: "sleep 1m" \ No newline at end of file diff --git a/libarchive/zinit/zinit/service_2.yaml b/libarchive/zinit/zinit/service_2.yaml deleted file mode 100644 index aba0a181..00000000 --- a/libarchive/zinit/zinit/service_2.yaml +++ /dev/null @@ -1,3 +0,0 @@ -exec: "sleep 1m" -after: - - service_1 \ No newline at end of file diff --git a/libarchive/zinit/zinit_client.v b/libarchive/zinit/zinit_client.v deleted file mode 100644 index f413bf16..00000000 --- a/libarchive/zinit/zinit_client.v +++ /dev/null @@ -1,290 +0,0 @@ -module zinit - -import incubaid.herolib.schemas.jsonrpc - -// Default socket path for Zinit -pub const default_socket_path = '/tmp/zinit.sock' - -// ZinitClient is a unified client for interacting with Zinit using OpenRPC -pub struct ZinitClient { -pub mut: - rpc_client &jsonrpc.Client -} - -// new_client creates a new Zinit OpenRPC client -// Parameters: -// - socket_path: Path to the Zinit Unix socket (default: /tmp/zinit.sock) -// Returns: -// - A new ZinitClient instance -pub fn new_client(socket_path string) ZinitClient { - mut cl := jsonrpc.new_unix_socket_client(socket_path) - return ZinitClient{ - rpc_client: cl - } -} - -// OpenRPCSpec represents the OpenRPC specification -pub struct OpenRPCSpec { -pub: - openrpc string - info OpenRPCInfo - methods []OpenRPCMethod -} - -// OpenRPCInfo represents the info section of the OpenRPC specification -pub struct OpenRPCInfo { -pub: - version string - title string - description string -} - -// OpenRPCMethod represents a method in the OpenRPC specification -pub struct OpenRPCMethod { -pub: - name string - description string -} - -// discover returns the OpenRPC specification for the API -// Returns: -// - A string representation of the OpenRPC specification -pub fn (mut c ZinitClient) discover() !string { - // Use a simpler approach - just get the raw response and return it as a string - request := jsonrpc.new_request_generic('rpc.discover', []string{}) - - // Send the request and get the raw response - raw_response := c.rpc_client.transport.send(request.encode(), jsonrpc.SendParams{ - timeout: 5 // Increase timeout to 5 seconds - })! - - // Extract just the result part from the response - // This is a simplified approach to avoid full JSON parsing - start_idx := raw_response.index('{"info":') or { return error('Invalid response format') } - - // Return the raw JSON string - return raw_response[start_idx..] -} - -// list returns a map of service names to their current states -// Returns: -// - A map where keys are service names and values are their states -pub fn (mut c ZinitClient) list() !map[string]string { - request := jsonrpc.new_request_generic('service_list', []string{}) - return c.rpc_client.send[[]string, map[string]string](request)! -} - -// ServiceStatus represents the detailed status of a service -pub struct ServiceStatus { -pub: - name string - pid int - state string - target string - after map[string]string -} - -// status returns detailed status information for a specific service -// Parameters: -// - name: The name of the service to get status for -// Returns: -// - A ServiceStatus struct containing detailed status information -pub fn (mut c ZinitClient) status(name string) !ServiceStatus { - request := jsonrpc.new_request_generic('service_status', name) - return c.rpc_client.send[string, ServiceStatus](request)! -} - -// EmptyResponse represents an empty response from the API -pub struct EmptyResponse {} - -// start starts a service -// Parameters: -// - name: The name of the service to start -pub fn (mut c ZinitClient) start(name string) ! { - request := jsonrpc.new_request_generic('service_start', name) - c.rpc_client.send[string, EmptyResponse](request)! -} - -// stop stops a service -// Parameters: -// - name: The name of the service to stop -pub fn (mut c ZinitClient) stop(name string) ! { - request := jsonrpc.new_request_generic('service_stop', name) - c.rpc_client.send[string, EmptyResponse](request)! -} - -// monitor starts monitoring a service -// Parameters: -// - name: The name of the service to monitor -pub fn (mut c ZinitClient) monitor(name string) ! { - request := jsonrpc.new_request_generic('service_monitor', name) - c.rpc_client.send[string, EmptyResponse](request)! -} - -// forget stops monitoring a service -// Parameters: -// - name: The name of the service to forget -pub fn (mut c ZinitClient) forget(name string) ! { - request := jsonrpc.new_request_generic('service_forget', name) - c.rpc_client.send[string, EmptyResponse](request)! -} - -// KillParams represents the parameters for the kill method -pub struct KillParams { -pub: - name string - signal string -} - -// kill sends a signal to a running service -// Parameters: -// - name: The name of the service to send the signal to -// - signal: The signal to send (e.g., SIGTERM, SIGKILL) -pub fn (mut c ZinitClient) kill(name string, signal string) ! { - params := KillParams{ - name: name - signal: signal - } - - request := jsonrpc.new_request_generic('service_kill', params) - c.rpc_client.send[KillParams, EmptyResponse](request)! -} - -// shutdown stops all services and powers off the system -pub fn (mut c ZinitClient) shutdown() ! { - request := jsonrpc.new_request_generic('system_shutdown', []string{}) - c.rpc_client.send[[]string, EmptyResponse](request)! -} - -// reboot stops all services and reboots the system -pub fn (mut c ZinitClient) reboot() ! { - request := jsonrpc.new_request_generic('system_reboot', []string{}) - c.rpc_client.send[[]string, EmptyResponse](request)! -} - -// ServiceStats represents memory and CPU usage statistics for a service -pub struct ServiceStats { -pub: - name string - pid int - memory_usage i64 - cpu_usage f64 - children []ChildProcessStats -} - -// ChildProcessStats represents statistics for a child process -pub struct ChildProcessStats { -pub: - pid int - memory_usage i64 - cpu_usage f64 -} - -// stats returns memory and CPU usage statistics for a service -// Parameters: -// - name: The name of the service to get stats for -// Returns: -// - A ServiceStats struct containing memory and CPU usage statistics -pub fn (mut c ZinitClient) stats(name string) !ServiceStats { - request := jsonrpc.new_request_generic('service_stats', name) - return c.rpc_client.send[string, ServiceStats](request)! -} - -// get_logs returns current logs from a specific service -// Parameters: -// - name: The name of the service to get logs for -// Returns: -// - An array of log strings -pub fn (mut c ZinitClient) get_logs(name string) ![]string { - request := jsonrpc.new_request_generic('stream_currentLogs', name) - return c.rpc_client.send[string, []string](request)! -} - -// get_all_logs returns all current logs from zinit and monitored services -// Returns: -// - An array of log strings -pub fn (mut c ZinitClient) get_all_logs() ![]string { - request := jsonrpc.new_request_generic('stream_currentLogs', []string{}) - return c.rpc_client.send[[]string, []string](request)! -} - -// ServiceConfig represents the configuration for a service -pub struct ServiceConfig { -pub: - exec string - oneshot bool - after []string - log string - env map[string]string - shutdown_timeout int -} - -// CreateServiceParams represents the parameters for the create_service method -pub struct CreateServiceParams { -pub: - name string - content ServiceConfig -} - -// create_service creates a new service configuration file -// Parameters: -// - name: The name of the service to create -// - config: The service configuration -// Returns: -// - A string indicating the result of the operation -pub fn (mut c ZinitClient) create_service(name string, config ServiceConfig) !string { - params := CreateServiceParams{ - name: name - content: config - } - - request := jsonrpc.new_request_generic('service_create', params) - return c.rpc_client.send[CreateServiceParams, string](request)! -} - -// delete_service deletes a service configuration file -// Parameters: -// - name: The name of the service to delete -// Returns: -// - A string indicating the result of the operation -pub fn (mut c ZinitClient) delete_service(name string) !string { - request := jsonrpc.new_request_generic('service_delete', name) - return c.rpc_client.send[string, string](request)! -} - -// ServiceConfigResponse represents the response from get_service -pub struct ServiceConfigResponse { -pub: - exec string - oneshot bool - after []string - log string - env map[string]string - shutdown_timeout int -} - -// get_service gets a service configuration file -// Parameters: -// - name: The name of the service to get -// Returns: -// - The service configuration -pub fn (mut c ZinitClient) get_service(name string) !ServiceConfigResponse { - request := jsonrpc.new_request_generic('service_get', name) - return c.rpc_client.send[string, ServiceConfigResponse](request)! -} - -// start_http_server starts an HTTP/RPC server at the specified address -// Parameters: -// - address: The network address to bind the server to (e.g., '127.0.0.1:8080') -// Returns: -// - A string indicating the result of the operation -pub fn (mut c ZinitClient) start_http_server(address string) !string { - request := jsonrpc.new_request_generic('system_start_http_server', address) - return c.rpc_client.send[string, string](request)! -} - -// stop_http_server stops the HTTP/RPC server if running -pub fn (mut c ZinitClient) stop_http_server() ! { - request := jsonrpc.new_request_generic('system_stop_http_server', []string{}) - c.rpc_client.send[[]string, EmptyResponse](request)! -} diff --git a/libarchive/zinit/zinit_factory.v b/libarchive/zinit/zinit_factory.v deleted file mode 100644 index 333f0717..00000000 --- a/libarchive/zinit/zinit_factory.v +++ /dev/null @@ -1,36 +0,0 @@ -module zinit - -import incubaid.herolib.core.pathlib -import incubaid.herolib.ui.console -import incubaid.herolib.osal.core as osal - -__global ( - zinit_global_manager []&Zinit -) - -pub fn new() !&Zinit { - if zinit_global_manager.len == 0 { - mut z := Zinit{ - path: pathlib.get_dir(path: '/etc/zinit', create: true)! - pathcmds: pathlib.get_dir(path: '/etc/zinit/cmds', create: true)! - } - zinit_global_manager << &z - z.load()! - } - return zinit_global_manager[0] -} - -pub fn check() bool { - if !osal.cmd_exists('zinit') { - return false - } - // println(osal.execute_ok('zinit list')) - return osal.execute_ok('zinit list') -} - -// remove all know services to zinit -pub fn destroy() ! { - mut zinitpath := pathlib.get_dir(path: '/etc/zinit', create: true)! - zinitpath.empty()! - console.print_header(' zinit destroyed') -} diff --git a/libarchive/zinit/zinit_openrpc_test.v b/libarchive/zinit/zinit_openrpc_test.v deleted file mode 100644 index 326526ff..00000000 --- a/libarchive/zinit/zinit_openrpc_test.v +++ /dev/null @@ -1,165 +0,0 @@ -module zinit - -import incubaid.herolib.schemas.jsonrpc -import os -import rand -import time - -// These tests require a running Zinit instance with the Unix socket at /tmp/zinit.sock -// If Zinit is not running, the tests will be skipped - -fn test_client_creation() { - if !os.exists('/tmp/zinit.sock') { - println('Skipping test: Zinit socket not found at /tmp/zinit.sock') - return - } - - client := new_client('/tmp/zinit.sock') - assert client.rpc_client != unsafe { nil } -} - -fn test_service_list() { - if !os.exists('/tmp/zinit.sock') { - println('Skipping test: Zinit socket not found at /tmp/zinit.sock') - return - } - - mut client := new_client('/tmp/zinit.sock') - services := client.list() or { - assert false, 'Failed to list services: ${err}' - return - } - - // Just verify we got a map, even if it's empty - assert typeof(services).name == 'map[string]string' - println('Found ${services.len} services') -} - -fn test_discover() { - if !os.exists('/tmp/zinit.sock') { - println('Skipping test: Zinit socket not found at /tmp/zinit.sock') - return - } - - mut client := new_client('/tmp/zinit.sock') - spec := client.discover() or { - assert false, 'Failed to get OpenRPC spec: ${err}' - return - } - - // Verify we got a non-empty string - assert spec.len > 0 - assert spec.contains('"openrpc"') - assert spec.contains('"methods"') -} - -fn test_stateless_client() { - if !os.exists('/tmp/zinit.sock') { - println('Skipping test: Zinit socket not found at /tmp/zinit.sock') - return - } - - // Create temporary directories for testing - temp_dir := os.temp_dir() - path := os.join_path(temp_dir, 'zinit_test') - pathcmds := os.join_path(temp_dir, 'zinit_test_cmds') - - // Create the directories - os.mkdir_all(path) or { - assert false, 'Failed to create test directory: ${err}' - return - } - os.mkdir_all(pathcmds) or { - assert false, 'Failed to create test commands directory: ${err}' - return - } - - // Clean up after the test - defer { - os.rmdir_all(path) or {} - os.rmdir_all(pathcmds) or {} - } - - mut zinit_client := new_stateless( - socket_path: '/tmp/zinit.sock' - path: path - pathcmds: pathcmds - ) or { - assert false, 'Failed to create stateless client: ${err}' - return - } - - // Test the names method which uses the client - names := zinit_client.names() or { - assert false, 'Failed to get service names: ${err}' - return - } - - assert typeof(names).name == '[]string' -} - -// This test creates a test service, starts it, checks its status, and then cleans up -// It's commented out by default to avoid modifying the system -/* -fn test_service_lifecycle() { - if !os.exists('/tmp/zinit.sock') { - println('Skipping test: Zinit socket not found at /tmp/zinit.sock') - return - } - - service_name := 'test_service_${rand.int_in_range(1000, 9999)}' - mut client := new_client('/tmp/zinit.sock') - - // Create service config - config := ServiceConfig{ - exec: '/bin/echo "Test service running"' - oneshot: true - after: []string{} - log: 'stdout' - env: { - 'TEST_VAR': 'test_value' - } - } - - // Create the service - client.create_service(service_name, config) or { - assert false, 'Failed to create service: ${err}' - return - } - - // Monitor the service - client.monitor(service_name) or { - assert false, 'Failed to monitor service: ${err}' - return - } - - // Start the service - client.start(service_name) or { - assert false, 'Failed to start service: ${err}' - return - } - - // Check service status - status := client.status(service_name) or { - assert false, 'Failed to get service status: ${err}' - return - } - - assert status.name == service_name - - // Clean up - client.stop(service_name) or { - println('Warning: Failed to stop service: ${err}') - } - - time.sleep(1 * time.second) - - client.forget(service_name) or { - println('Warning: Failed to forget service: ${err}') - } - - client.delete_service(service_name) or { - println('Warning: Failed to delete service: ${err}') - } -} -*/ diff --git a/libarchive/zinit/zinit_stateless.v b/libarchive/zinit/zinit_stateless.v deleted file mode 100644 index 6f330aa2..00000000 --- a/libarchive/zinit/zinit_stateless.v +++ /dev/null @@ -1,135 +0,0 @@ -module zinit - -import incubaid.herolib.core.texttools -import incubaid.herolib.ui.console -import incubaid.herolib.core.pathlib -import incubaid.herolib.data.ourtime -import time -import json - -@[params] -pub struct ZinitConfig { - path string = '/etc/zinit' - pathcmds string = '/etc/zinit/cmds' - socket_path string = default_socket_path -} - -pub struct ZinitStateless { -pub mut: - client ZinitClient - path pathlib.Path - pathcmds pathlib.Path -} - -pub fn new_stateless(z ZinitConfig) !ZinitStateless { - return ZinitStateless{ - client: new_client(z.socket_path) - path: pathlib.get_dir(path: z.path, create: true)! - pathcmds: pathlib.get_dir(path: z.pathcmds, create: true)! - } -} - -// will delete the process if it exists while starting -pub fn (mut zinit ZinitStateless) new(args_ ZProcessNewArgs) !ZProcess { - console.print_header(' zinit process new') - mut args := args_ - - if args.cmd.len == 0 { - $if debug { - print_backtrace() - } - return error('cmd cannot be empty for ${args} in zinit.') - } - - if zinit.exists(args.name)! { - zinit.delete(args.name)! - } - - mut zp := ZProcess{ - name: args.name - cmd: args.cmd - cmd_test: args.cmd_test - cmd_stop: args.cmd_stop - env: args.env.move() - after: args.after - start: args.start - restart: args.restart - oneshot: args.oneshot - workdir: args.workdir - } - - zinit.cmd_write(args.name, args.cmd, '_start', {}, args.workdir)! - zinit.cmd_write(args.name, args.cmd_test, '_test', {}, args.workdir)! - zinit.cmd_write(args.name, args.cmd_stop, '_stop', {}, args.workdir)! - - mut json_path := zinit.pathcmds.file_get_new('${args.name}.json')! - json_content := json.encode(args) - json_path.write(json_content)! - - mut pathyaml := zinit.path.file_get_new(zp.name + '.yaml')! - // console.print_debug('debug zprocess path yaml: ${pathyaml}') - pathyaml.write(zp.config_content()!)! - - zinit.client.monitor(args.name)! - assert zinit.exists(args.name)! - - if args.start { - zinit.client.start(args.name)! - } - - return zp -} - -fn (mut zinit ZinitStateless) cmd_write(name string, cmd string, cat string, env map[string]string, workdir string) !string { - if cmd.trim_space() == '' { - return '' - } - mut zinitobj := new()! - mut pathcmd := zinitobj.pathcmds.file_get_new('${name}${cat}.sh')! - mut cmd_out := '#!/bin/bash\nset -e\n\n' - - if cat == '_start' { - cmd_out += 'echo === START ======== ${ourtime.now().str()} === \n' - } - for key, val in env { - cmd_out += '${key}=${val}\n' - } - - if workdir.trim_space() != '' { - cmd_out += 'cd ${workdir.trim_space()}\n' - } - - cmd_out += texttools.dedent(cmd) + '\n' - pathcmd.write(cmd_out)! - pathcmd.chmod(0x770)! - return '/bin/bash -c ${pathcmd.path}' -} - -pub fn (mut zinit ZinitStateless) exists(name string) !bool { - return name in zinit.client.list()! -} - -pub fn (mut zinit ZinitStateless) stop(name string) ! { - zinit.client.stop(name)! -} - -pub fn (mut zinit ZinitStateless) start(name string) ! { - zinit.client.start(name)! -} - -pub fn (mut zinit ZinitStateless) running(name string) !bool { - if !zinit.exists(name)! { - return false - } - return zinit.client.status(name)!.state == 'Running' -} - -pub fn (mut zinit ZinitStateless) delete(name string) ! { - zinit.client.stop(name)! - time.sleep(1000000) - zinit.client.forget(name)! -} - -pub fn (mut self ZinitStateless) names() ![]string { - return self.client.list()!.keys() -} diff --git a/libarchive/zinit/zprocess.v b/libarchive/zinit/zprocess.v deleted file mode 100644 index 30e90963..00000000 --- a/libarchive/zinit/zprocess.v +++ /dev/null @@ -1,266 +0,0 @@ -module zinit - -import os -import incubaid.herolib.osal.core as osal -import incubaid.herolib.data.ourtime -import incubaid.herolib.ui.console -import time - -pub struct ZProcess { -pub: - name string = 'default' -pub mut: - cmd string // command to start - cmd_stop string // command to stop (optional) - cmd_test string // command line to test service is running - workdir string // where to execute the commands - status ZProcessStatus - pid int - after []string // list of service we depend on - env map[string]string - oneshot bool - start bool = true - restart bool = true // whether the process should be restarted on failure - description string // not used in zinit -} - -pub enum ZProcessStatus { - unknown - init - ok - killed - error - blocked - spawned -} - -pub fn (zp ZProcess) cmd() !string { - mut zinitobj := new()! - mut path := zinitobj.pathcmds.file_get_new('${zp.name}_start.sh')! - if path.exists() { - return 'bash -c \'${path.path}\'' - } else { - if zp.cmd.contains('\n') { - return error('cmd cannot have \\n and not have cmd file on disk on ${path.path}') - } - if zp.cmd == '' { - return error('cmd cannot be empty') - } - } - return '${zp.cmd}' -} - -pub fn (zp ZProcess) cmdtest() !string { - mut zinitobj := new()! - mut path := zinitobj.pathcmds.file_get_new('${zp.name}_test.sh')! - if path.exists() { - return "bash -c \"${path.path}\"" - } else { - if zp.cmd_test.contains('\n') { - return error('cmd test cannot have \\n and not have cmd file on disk on ${path.path}') - } - if zp.cmd_test == '' { - return error('cmd test cannot be empty') - } - } - return '${zp.cmd_test}' -} - -pub fn (zp ZProcess) cmdstop() !string { - mut zinitobj := new()! - mut path := zinitobj.pathcmds.file_get_new('${zp.name}_stop.sh')! - if path.exists() { - return "bash -c \"${path.path}\"" - } else { - if zp.cmd_stop.contains('\n') { - return error('cmd stop cannot have \\n and not have cmd file on disk on ${path.path}') - } - if zp.cmd_stop == '' { - return error('cmd stop cannot be empty') - } - } - return '${zp.cmd_stop}' -} - -// return the configuration as needs to be given to zinit -fn (zp ZProcess) config_content() !string { - mut out := " -exec: \"${zp.cmd()!}\" -signal: - stop: SIGKILL -log: ring -" - if zp.cmd_test.len > 0 { - out += "test: \"${zp.cmdtest()!}\"\n" - } - if zp.oneshot { - out += 'oneshot: true\n' - } - if zp.after.len > 0 { - out += 'after:\n' - for val in zp.after { - out += ' - ${val}\n' - } - } - if zp.env.len > 0 { - out += 'env:\n' - for key, val in zp.env { - out += ' ${key}: \'${val}\'\n' - } - } - return out -} - -pub fn (zp ZProcess) start() ! { - console.print_header(' start ${zp.name}') - mut client := new_rpc_client() - if !client.isloaded(zp.name) { - client.monitor(zp.name)! // means will check it out - } -} - -pub fn (mut zp ZProcess) stop() ! { - console.print_header(' stop ${zp.name}') - st := zp.status()! - - // QUESTION: removed error, since those can also be stopped - // otherwise fails to forget the zp when destroying - if st in [.unknown, .killed] { - return - } - mut client := new_rpc_client() - client.stop(zp.name)! - zp.status()! -} - -pub fn (mut zp ZProcess) destroy() ! { - console.print_header(' destroy ${zp.name}') - zp.stop()! - // return error('ssssa') - mut client := new_rpc_client() - client.forget(zp.name) or {} - mut zinit_obj := new()! - mut path1 := zinit_obj.pathcmds.file_get_new('${zp.name}_start.sh')! - mut path2 := zinit_obj.pathcmds.file_get_new('${zp.name}_stop.sh')! - mut path3 := zinit_obj.pathcmds.file_get_new('${zp.name}_test.sh')! - mut pathyaml := zinit_obj.path.file_get_new(zp.name + '.yaml')! - path1.delete()! - path2.delete()! - path3.delete()! - pathyaml.delete()! -} - -// how long to wait till the specified output shows up, timeout in sec -pub fn (mut zp ZProcess) output_wait(c_ string, timeoutsec int) ! { - zp.start()! - _ = new_rpc_client() - zp.check()! - mut t := ourtime.now() - start := t.unix() - c := c_.replace('\n', '') - for _ in 0 .. 2000 { - o := zp.log()! - console.print_debug(o) - $if debug { - console.print_debug(" - zinit ${zp.name}: wait for: '${c}'") - } - // need to replace \n because can be wrapped because of size of pane - if o.replace('\n', '').contains(c) { - return - } - mut t2 := ourtime.now() - if t2.unix() > start + timeoutsec { - return error('timeout on output wait for zinit.\n${zp.name} .\nwaiting for:\n${c}') - } - time.sleep(100 * time.millisecond) - } -} - -// check if process is running if yes return the log -pub fn (zp ZProcess) log() !string { - assert zp.name.len > 2 - cmd := 'zinit log ${zp.name} -s' - res := os.execute(cmd) - if res.exit_code > 0 { - $if debug { - print_backtrace() - } - return error('zprocesslog: could not execute ${cmd}') - } - mut out := []string{} - - for line in res.output.split_into_lines() { - if line.contains('=== START ========') { - out = []string{} - } - out << line - } - - return out.join_lines() -} - -// return status of process -//``` -// enum ZProcessStatus { -// unknown -// init -// ok -// error -// blocked -// spawned -// killed -// } -//``` -pub fn (mut zp ZProcess) status() !ZProcessStatus { - cmd := 'zinit status ${zp.name}' - r := osal.execute_silent(cmd)! - for line in r.split_into_lines() { - if line.starts_with('pid') { - zp.pid = line.split('pid:')[1].trim_space().int() - } - if line.starts_with('state') { - st := line.split('state:')[1].trim_space().to_lower() - // console.print_debug(" status string: $st") - if st.contains('sigkill') { - zp.status = .killed - } else if st.contains('error') { - zp.status = .error - } else if st.contains('spawned') { - zp.status = .error - } else if st.contains('running') { - zp.status = .ok - } else { - zp.status = .unknown - } - } - } - // mut client := new_rpc_client() - // st := client.status(zp.name) or {return .unknown} - // statusstr:=st.state.to_lower() - // if statusstr=="running"{ - // zp.status = .ok - // }else if statusstr.contains("error"){ - // zp.status = .error - // }else{ - // console.print_debug(st) - // return error("status not implemented yet") - // } - return zp.status -} - -// will check that process is running -pub fn (mut zp ZProcess) check() ! { - status := zp.status()! - if status != .ok { - return error('process is not running.\n${zp}') - } -} - -// will check that process is running -pub fn (mut zp ZProcess) isrunning() !bool { - status := zp.status()! - if status != .ok { - return false - } - return true -} diff --git a/libarchive/zinit/zprocess_load.v b/libarchive/zinit/zprocess_load.v deleted file mode 100644 index 6e3a6f5d..00000000 --- a/libarchive/zinit/zprocess_load.v +++ /dev/null @@ -1,80 +0,0 @@ -module zinit - -pub fn (mut zp ZProcess) load() ! { - zp.status()! - mut zinitobj := new()! - - if !zinitobj.path.file_exists(zp.name + '.yaml') { - $if debug { - print_backtrace() - } - mut pathyaml := zinitobj.path.file_get_new(zp.name + '.yaml')! - content := zp.config_content()! - pathyaml.write(content)! - } - - // if zinitobj.pathcmds.file_exists(zp.name) { - // // means we can load the special cmd - // mut pathcmd := zinitobj.pathcmds.file_get(zp.name)! - // zp.cmd = pathcmd.read()! - // } - // if zinitobj.pathtests.file_exists(zp.name) { - // // means we can load the special cmd - // mut pathtest := zinitobj.path.file_get(zp.name)! - // zp.test = pathtest.read()! - // } - if zinitobj.pathcmds.file_exists(zp.name) { - // means we can load the special cmd - mut pathcmd := zinitobj.pathcmds.file_get(zp.name)! - zp.cmd = pathcmd.read()! - } - - mut pathyaml := zinitobj.path.file_get_new(zp.name + '.yaml')! - contentyaml := pathyaml.read()! - - // the parsing of the file is needed to find the info which we can't get from the zinit daemon - - mut st := '' - for line in contentyaml.split_into_lines() { - if line.starts_with('exec:') && zp.cmd.len == 0 { - zp.cmd = line.split('exec:')[1].trim('\'" ') - } - if line.starts_with('test:') && zp.cmd.len == 0 { - zp.cmd_test = line.split('test:')[1].trim('\'" ') - } - if line.starts_with('after:') { - st = 'after' - continue - } - if line.starts_with('env:') { - st = 'env' - continue - } - if st == 'after' { - if line.trim_space() == '' { - st = 'start' - } else { - line.trim_space().starts_with('-') - { - _, after := line.split_once('-') or { - panic('bug in ${pathyaml} for line ${line}') - } - zp.after << after.to_lower().trim_space() - } - } - } - if st == 'env' { - if line.trim_space() == '' { - st = 'start' - } else { - line.contains('=') - { - key, val := line.split_once(':') or { - panic('bug in ${pathyaml} for line ${line} for env') - } - zp.env[key.trim(' \'"')] = val.trim(' \'"') - } - } - } - } -} diff --git a/compile.sh b/scripts/compile.sh similarity index 100% rename from compile.sh rename to scripts/compile.sh diff --git a/install_hero.sh b/scripts/install_hero.sh similarity index 100% rename from install_hero.sh rename to scripts/install_hero.sh diff --git a/install_v.sh b/scripts/install_v.sh similarity index 100% rename from install_v.sh rename to scripts/install_v.sh