This commit is contained in:
2025-08-21 12:05:20 +02:00
parent 9b2b7283c0
commit 9642922445
32 changed files with 281 additions and 281 deletions

View File

@@ -19,7 +19,7 @@ println(actor_spec)
// )! // )!
actor_module := generator.generate_actor_module(actor_spec, actor_module := generator.generate_actor_module(actor_spec,
interfaces: [ .http] interfaces: [.http]
)! )!
actor_module.write(example_dir, actor_module.write(example_dir,

View File

@@ -9,9 +9,9 @@ import freeflowuniverse.herolib.core.pathlib
// } // }
mut args2 := generator.GeneratorArgs{ mut args2 := generator.GeneratorArgs{
path: '~/code/github/freeflowuniverse/herolib/lib/develop/heroprompt' path: '~/code/github/freeflowuniverse/herolib/lib/develop/heroprompt'
force: true force: true
} }
generator.scan(args2)! generator.scan(args2)!
// mut args := generator.GeneratorArgs{ // mut args := generator.GeneratorArgs{

View File

@@ -12,7 +12,9 @@ println('Total countries loaded: ${all_countries.len}')
// --- Example: Print the first few countries --- // --- Example: Print the first few countries ---
println('\n--- First 5 Countries ---') println('\n--- First 5 Countries ---')
for i, country in all_countries { for i, country in all_countries {
if i >= 5 { break } if i >= 5 {
break
}
println(country.str()) println(country.str())
} }
@@ -44,7 +46,7 @@ println('Found ${eu_countries.len} European countries.')
// --- Example: Using the helper function --- // --- Example: Using the helper function ---
println('\n--- Using helper function to find Japan ---') println('\n--- Using helper function to find Japan ---')
japan := countries.find_country_by_iso('JP') or { japan := countries.find_country_by_iso('JP') or {
println('Error finding Japan: ${err}') println('Error finding Japan: ${err}')
return return
} }
println('Found Japan: ${japan.str()}') println('Found Japan: ${japan.str()}')

View File

@@ -9,8 +9,8 @@ import os
// )! // )!
mut workspace := heroprompt.get( mut workspace := heroprompt.get(
name: 'example_ws' name: 'example_ws'
path: '${os.home_dir()}/code/github/freeflowuniverse/herolib' path: '${os.home_dir()}/code/github/freeflowuniverse/herolib'
create: true create: true
)! )!
@@ -19,7 +19,9 @@ println('selected (initial): ${workspace.selected_children()}')
// Add a directory and a file // Add a directory and a file
workspace.add_dir(path: '${os.home_dir()}/code/github/freeflowuniverse/herolib/docker')! workspace.add_dir(path: '${os.home_dir()}/code/github/freeflowuniverse/herolib/docker')!
workspace.add_file(path: '${os.home_dir()}/code/github/freeflowuniverse/herolib/docker/docker_ubuntu_install.sh')! workspace.add_file(
path: '${os.home_dir()}/code/github/freeflowuniverse/herolib/docker/docker_ubuntu_install.sh'
)!
println('selected (after add): ${workspace.selected_children()}') println('selected (after add): ${workspace.selected_children()}')
// Build a prompt from current selection (should be empty now) // Build a prompt from current selection (should be empty now)
@@ -39,7 +41,9 @@ println('selected (after remove): ${workspace.selected_children()}')
// List workspaces (names only) // List workspaces (names only)
mut all := heroprompt.list_workspaces() or { []&heroprompt.Workspace{} } mut all := heroprompt.list_workspaces() or { []&heroprompt.Workspace{} }
mut names := []string{} mut names := []string{}
for w in all { names << w.name } for w in all {
names << w.name
}
println('workspaces: ${names}') println('workspaces: ${names}')
// Optionally delete the example workspace // Optionally delete the example workspace

View File

@@ -8,7 +8,7 @@ println(installer)
installer.start()! installer.start()!
$dbg; // $dbg;
mut r := mycelium.inspect()! mut r := mycelium.inspect()!
println(r) println(r)

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run #!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.lib.lang.codewalker
import freeflowuniverse.herolib.lib.lang.codewalker
import freeflowuniverse.herolib.core.pathlib import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.osal.core as osal import freeflowuniverse.herolib.osal.core as osal

View File

@@ -1,10 +1,10 @@
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run #!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
import os import os
import freeflowuniverse.herolib.core.code { Alias, Struct } import freeflowuniverse.herolib.core.code { Alias }
import freeflowuniverse.herolib.core.pathlib import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.schemas.openrpc import freeflowuniverse.herolib.schemas.openrpc
import freeflowuniverse.herolib.schemas.openrpc.codegen {generate_model} import freeflowuniverse.herolib.schemas.openrpc.codegen { generate_model }
const doc_path = '${os.dir(@FILE)}/testdata/openrpc.json' const doc_path = '${os.dir(@FILE)}/testdata/openrpc.json'
@@ -18,8 +18,7 @@ assert model[0] is Alias
pet_id := model[0] as Alias pet_id := model[0] as Alias
assert pet_id.name == 'PetId' assert pet_id.name == 'PetId'
println(pet_id) println(pet_id)
$dbg; // $dbg;
// assert pet_id.typ.symbol == 'int' // assert pet_id.typ.symbol == 'int'

View File

@@ -5,10 +5,8 @@ import json
import net.http { Method } import net.http { Method }
// https://cassiomolin.com/2016/09/09/which-http-status-codes-are-cacheable/ // https://cassiomolin.com/2016/09/09/which-http-status-codes-are-cacheable/
const ( const default_cacheable_codes = [200, 203, 204, 206, 300, 404, 405, 410, 414, 501]
default_cacheable_codes = [200, 203, 204, 206, 300, 404, 405, 410, 414, 501] const unsafe_http_methods = [Method.put, .patch, .post, .delete]
unsafe_http_methods = [Method.put, .patch, .post, .delete]
)
pub struct CacheConfig { pub struct CacheConfig {
pub mut: pub mut:

View File

@@ -1,10 +1,8 @@
module countries module countries
// --- Embed the data file content at compile time --- // --- Embed the data file content at compile time ---
// The path is relative to the location of this `factory.v` file. // The path is relative to the location of this `factory.v` file.
// get_all_countries parses the country data embedded in the executable. // get_all_countries parses the country data embedded in the executable.
// It returns a list of Country structs. // It returns a list of Country structs.
pub fn get_all_countries() ![]Country { pub fn get_all_countries() ![]Country {
@@ -26,7 +24,8 @@ pub fn get_all_countries() ![]Country {
// --- Identify and skip the header --- // --- Identify and skip the header ---
// Check if this line looks like the header (contains key identifiers) // Check if this line looks like the header (contains key identifiers)
if !found_header && trimmed_line.contains('ISO') && trimmed_line.contains('Country') && trimmed_line.contains('CurrencyCode') { if !found_header && trimmed_line.contains('ISO') && trimmed_line.contains('Country')
&& trimmed_line.contains('CurrencyCode') {
found_header = true found_header = true
continue // Skip the header line itself continue // Skip the header line itself
} }
@@ -37,29 +36,28 @@ pub fn get_all_countries() ![]Country {
// Use strings.split to split the line by tab character // Use strings.split to split the line by tab character
fields := line.split_by_space() fields := line.split_by_space()
// --- Create Country struct instance --- // --- Create Country struct instance ---
// Map fields by index based on the header structure. // Map fields by index based on the header structure.
// Handle potential out-of-bounds or missing data gracefully using `or {}`. // Handle potential out-of-bounds or missing data gracefully using `or {}`.
// The last field might sometimes be empty or missing in the data, handle it // The last field might sometimes be empty or missing in the data, handle it
iso_field := fields[0] or { '' } iso_field := fields[0] or { '' }
iso3_field := fields[1] or { '' } iso3_field := fields[1] or { '' }
iso_numeric_field := fields[2] or { '' } iso_numeric_field := fields[2] or { '' }
fips_field := fields[3] or { '' } fips_field := fields[3] or { '' }
country_name_field := fields[4] or { '' } country_name_field := fields[4] or { '' }
capital_field := fields[5] or { '' } capital_field := fields[5] or { '' }
area_sqkm_field := fields[6] or { '' } area_sqkm_field := fields[6] or { '' }
population_field := fields[7] or { '' } population_field := fields[7] or { '' }
continent_field := fields[8] or { '' } continent_field := fields[8] or { '' }
tld_field := fields[9] or { '' } tld_field := fields[9] or { '' }
currency_code_field := fields[10] or { '' } currency_code_field := fields[10] or { '' }
currency_name_field := fields[11] or { '' } currency_name_field := fields[11] or { '' }
phone_field := fields[12] or { '' } phone_field := fields[12] or { '' }
postal_format_field := fields[13] or { '' } postal_format_field := fields[13] or { '' }
postal_regex_field := fields[14] or { '' } postal_regex_field := fields[14] or { '' }
languages_field := fields[15] or { '' } languages_field := fields[15] or { '' }
geonameid_field := fields[16] or { '' } geonameid_field := fields[16] or { '' }
neighbours_field := fields[17] or { '' } neighbours_field := fields[17] or { '' }
equiv_fips_code_field := fields[18] or { '' } equiv_fips_code_field := fields[18] or { '' }
country := Country{ country := Country{
@@ -93,11 +91,11 @@ pub fn get_all_countries() ![]Country {
// Optional: Helper function to find a country by ISO code // Optional: Helper function to find a country by ISO code
pub fn find_country_by_iso(iso_code string) !Country { pub fn find_country_by_iso(iso_code string) !Country {
countries := get_all_countries()! countries := get_all_countries()!
for country in countries { for country in countries {
if country.iso == iso_code { if country.iso == iso_code {
return country return country
} }
} }
return error('Country with ISO code "${iso_code}" not found') return error('Country with ISO code "${iso_code}" not found')
} }

View File

@@ -2,15 +2,15 @@ module codewalker
pub struct CWError { pub struct CWError {
pub: pub:
message string message string
linenr int linenr int
category string category string
} }
pub struct FMError { pub struct FMError {
pub: pub:
message string message string
linenr int //is optional linenr int // is optional
category string category string
filename string filename string
} }

View File

@@ -37,5 +37,5 @@ pub mut:
pub struct GitRepoConfig { pub struct GitRepoConfig {
pub mut: pub mut:
remote_check_period int = 3600*24*7 // seconds = 7d remote_check_period int = 3600 * 24 * 7 // seconds = 7d
} }

View File

@@ -113,8 +113,8 @@ pub fn install_multi(args_ InstallArgs) ! {
// herolib.hero_install(reset: args.reset)! // herolib.hero_install(reset: args.reset)!
// } // }
// 'caddy' { // 'caddy' {
// caddy.install(reset: args.reset)! // caddy.install(reset: args.reset)!
// caddy.configure_examples()! // caddy.configure_examples()!
// } // }
// 'chrome' { // 'chrome' {
// chrome.install(reset: args.reset, uninstall: args.uninstall)! // chrome.install(reset: args.reset, uninstall: args.uninstall)!

View File

@@ -19,15 +19,15 @@ fn startupcmd() ![]startupmanager.ZProcessNewArgs {
mut peers_str := installer.peers.join(' ') mut peers_str := installer.peers.join(' ')
mut tun_name := 'tun${installer.tun_nr}' mut tun_name := 'tun${installer.tun_nr}'
mut cmd:='mycelium --key-file ${osal.hero_path()!}/cfg/priv_key.bin --peers ${peers_str} --tun-name ${tun_name}' mut cmd := 'mycelium --key-file ${osal.hero_path()!}/cfg/priv_key.bin --peers ${peers_str} --tun-name ${tun_name}'
if core.is_osx()! { if core.is_osx()! {
cmd = "sudo ${cmd}" cmd = 'sudo ${cmd}'
} }
res << startupmanager.ZProcessNewArgs{ res << startupmanager.ZProcessNewArgs{
name: 'mycelium' name: 'mycelium'
startuptype: .zinit startuptype: .zinit
cmd: cmd cmd: cmd
env: { env: {
'HOME': os.home_dir() 'HOME': os.home_dir()
} }

View File

@@ -218,10 +218,7 @@ pub fn (mut self MyceliumInstaller) start() ! {
start_pre()! start_pre()!
for zprocess in startupcmd()! { for zprocess in startupcmd()! {
mut sm := startupmanager_get(zprocess.startuptype)! mut sm := startupmanager_get(zprocess.startuptype)!
console.print_debug('starting mycelium_installer with ${zprocess.startuptype}...') console.print_debug('starting mycelium_installer with ${zprocess.startuptype}...')
@@ -229,7 +226,6 @@ pub fn (mut self MyceliumInstaller) start() ! {
sm.new(zprocess)! sm.new(zprocess)!
sm.start(zprocess.name)! sm.start(zprocess.name)!
} }
start_post()! start_post()!

View File

@@ -139,19 +139,19 @@ fn destroy() ! {
osal.execute_silent(cmd) or {} osal.execute_silent(cmd) or {}
mut zinit_factory := zinit_lib.Zinit{} mut zinit_factory := zinit_lib.Zinit{}
if zinit_factory.exists('dagu') { // if zinit_factory.exists('dagu') {
zinit_factory.stop('dagu')! or { return error('Could not stop dagu service due to: ${err}') } // zinit_factory.stop('dagu')! or { return error('Could not stop dagu service due to: ${err}') }
zinit_factory.delete('dagu')! or { // zinit_factory.delete('dagu')! or {
return error('Could not delete dagu service due to: ${err}') // return error('Could not delete dagu service due to: ${err}')
} // }
} // }
if zinit_factory.exists('dagu_scheduler') { // if zinit_factory.exists('dagu_scheduler') {
zinit_factory.stop('dagu_scheduler')! or { // zinit_factory.stop('dagu_scheduler')! or {
return error('Could not stop dagu_scheduler service due to: ${err}') // return error('Could not stop dagu_scheduler service due to: ${err}')
} // }
zinit_factory.delete('dagu_scheduler')! or { // zinit_factory.delete('dagu_scheduler')! or {
return error('Could not delete dagu_scheduler service due to: ${err}') // return error('Could not delete dagu_scheduler service due to: ${err}')
} // }
} // }
} }

View File

@@ -154,16 +154,16 @@ fn destroy() ! {
return error('failed to uninstall garage_s3: ${res.output}') return error('failed to uninstall garage_s3: ${res.output}')
} }
mut zinit_factory := zinit_lib.Zinit{} // mut zinit_factory := zinit_lib.Zinit{}
if zinit_factory.exists('garage_s3') { // if zinit_factory.exists('garage_s3'){
zinit_factory.stop('garage_s3')! or { // zinit_factory.stop('garage_s3') or {
return error('Could not stop garage_s3 service due to: ${err}') // return error('Could not stop garage_s3 service due to: ${err}')
} // }
zinit_factory.delete('garage_s3')! or { // zinit_factory.delete('garage_s3') or {
return error('Could not delete garage_s3 service due to: ${err}') // return error('Could not delete garage_s3 service due to: ${err}')
} // }
} // }
console.print_header('garage_s3 is uninstalled') console.print_header('garage_s3 is uninstalled')
} }

View File

@@ -65,4 +65,4 @@ pub fn (py PythonEnv) restore_from_lock() ! {
' '
osal.exec(cmd: cmd)! osal.exec(cmd: cmd)!
console.print_debug('Successfully restored from lock file') console.print_debug('Successfully restored from lock file')
} }

View File

@@ -15,12 +15,12 @@ pub mut:
@[params] @[params]
pub struct PythonEnvArgs { pub struct PythonEnvArgs {
pub mut: pub mut:
name string = 'default' name string = 'default'
reset bool reset bool
python_version string = '3.11' python_version string = '3.11'
dependencies []string dependencies []string
dev_dependencies []string dev_dependencies []string
description string = 'A Python project managed by Herolib' description string = 'A Python project managed by Herolib'
} }
pub fn new(args_ PythonEnvArgs) !PythonEnv { pub fn new(args_ PythonEnvArgs) !PythonEnv {
@@ -47,14 +47,14 @@ pub fn new(args_ PythonEnvArgs) !PythonEnv {
// Check if the Python environment exists and is properly configured // Check if the Python environment exists and is properly configured
pub fn (py PythonEnv) exists() bool { pub fn (py PythonEnv) exists() bool {
return os.exists('${py.path.path}/.venv/bin/activate') && return os.exists('${py.path.path}/.venv/bin/activate')
os.exists('${py.path.path}/pyproject.toml') && os.exists('${py.path.path}/pyproject.toml')
} }
// Initialize the Python environment using uv // Initialize the Python environment using uv
pub fn (mut py PythonEnv) init_env(args PythonEnvArgs) ! { pub fn (mut py PythonEnv) init_env(args PythonEnvArgs) ! {
console.print_green('Initializing Python environment at: ${py.path.path}') console.print_green('Initializing Python environment at: ${py.path.path}')
// Remove existing environment if reset is requested // Remove existing environment if reset is requested
if args.reset && py.path.exists() { if args.reset && py.path.exists() {
console.print_debug('Removing existing environment for reset') console.print_debug('Removing existing environment for reset')
@@ -69,13 +69,13 @@ pub fn (mut py PythonEnv) init_env(args PythonEnvArgs) ! {
// Generate project files from templates // Generate project files from templates
template_args := TemplateArgs{ template_args := TemplateArgs{
name: py.name name: py.name
python_version: args.python_version python_version: args.python_version
dependencies: args.dependencies dependencies: args.dependencies
dev_dependencies: args.dev_dependencies dev_dependencies: args.dev_dependencies
description: args.description description: args.description
} }
py.generate_all_templates(template_args)! py.generate_all_templates(template_args)!
// Initialize uv project // Initialize uv project
@@ -84,12 +84,12 @@ pub fn (mut py PythonEnv) init_env(args PythonEnvArgs) ! {
uv venv --python ${args.python_version} uv venv --python ${args.python_version}
' '
osal.exec(cmd: cmd)! osal.exec(cmd: cmd)!
// Sync dependencies if any are specified // Sync dependencies if any are specified
if args.dependencies.len > 0 || args.dev_dependencies.len > 0 { if args.dependencies.len > 0 || args.dev_dependencies.len > 0 {
py.sync()! py.sync()!
} }
console.print_debug('Python environment initialization complete') console.print_debug('Python environment initialization complete')
} }
@@ -109,20 +109,20 @@ pub fn (py PythonEnv) add_dependencies(packages []string, dev bool) ! {
if packages.len == 0 { if packages.len == 0 {
return return
} }
console.print_debug('Adding Python packages: ${packages.join(", ")}') console.print_debug('Adding Python packages: ${packages.join(', ')}')
packages_str := packages.join(' ') packages_str := packages.join(' ')
mut cmd := ' mut cmd := '
cd ${py.path.path} cd ${py.path.path}
uv add ${packages_str}' uv add ${packages_str}'
if dev { if dev {
cmd += ' --dev' cmd += ' --dev'
} }
osal.exec(cmd: cmd)! osal.exec(cmd: cmd)!
console.print_debug('Successfully added packages: ${packages.join(", ")}') console.print_debug('Successfully added packages: ${packages.join(', ')}')
} }
// Remove dependencies from the project // Remove dependencies from the project
@@ -130,20 +130,20 @@ pub fn (py PythonEnv) remove_dependencies(packages []string, dev bool) ! {
if packages.len == 0 { if packages.len == 0 {
return return
} }
console.print_debug('Removing Python packages: ${packages.join(", ")}') console.print_debug('Removing Python packages: ${packages.join(', ')}')
packages_str := packages.join(' ') packages_str := packages.join(' ')
mut cmd := ' mut cmd := '
cd ${py.path.path} cd ${py.path.path}
uv remove ${packages_str}' uv remove ${packages_str}'
if dev { if dev {
cmd += ' --dev' cmd += ' --dev'
} }
osal.exec(cmd: cmd)! osal.exec(cmd: cmd)!
console.print_debug('Successfully removed packages: ${packages.join(", ")}') console.print_debug('Successfully removed packages: ${packages.join(', ')}')
} }
// Legacy pip method for backward compatibility - now uses uv add // Legacy pip method for backward compatibility - now uses uv add
@@ -189,4 +189,4 @@ pub fn (py PythonEnv) run(command string) !osal.Job {
${command} ${command}
' '
return osal.exec(cmd: cmd)! return osal.exec(cmd: cmd)!
} }

View File

@@ -4,13 +4,13 @@ import freeflowuniverse.herolib.ui.console
fn test_python_env_creation() { fn test_python_env_creation() {
console.print_debug('Testing Python environment creation') console.print_debug('Testing Python environment creation')
// Test basic environment creation // Test basic environment creation
py := new(name: 'test_env') or { py := new(name: 'test_env') or {
console.print_stderr('Failed to create Python environment: ${err}') console.print_stderr('Failed to create Python environment: ${err}')
panic(err) panic(err)
} }
assert py.name == 'test_env' assert py.name == 'test_env'
assert py.path.path.contains('test_env') assert py.path.path.contains('test_env')
console.print_debug(' Environment creation test passed') console.print_debug(' Environment creation test passed')
@@ -18,97 +18,97 @@ fn test_python_env_creation() {
fn test_python_env_with_dependencies() { fn test_python_env_with_dependencies() {
console.print_debug('Testing Python environment with dependencies') console.print_debug('Testing Python environment with dependencies')
// Test environment with initial dependencies // Test environment with initial dependencies
py := new( py := new(
name: 'test_deps' name: 'test_deps'
dependencies: ['requests', 'click'] dependencies: ['requests', 'click']
dev_dependencies: ['pytest', 'black'] dev_dependencies: ['pytest', 'black']
reset: true reset: true
) or { ) or {
console.print_stderr('Failed to create Python environment with dependencies: ${err}') console.print_stderr('Failed to create Python environment with dependencies: ${err}')
panic(err) panic(err)
} }
assert py.exists() assert py.exists()
console.print_debug(' Environment with dependencies test passed') console.print_debug(' Environment with dependencies test passed')
} }
fn test_python_package_management() { fn test_python_package_management() {
console.print_debug('Testing package management') console.print_debug('Testing package management')
py := new(name: 'test_packages', reset: true) or { py := new(name: 'test_packages', reset: true) or {
console.print_stderr('Failed to create Python environment: ${err}') console.print_stderr('Failed to create Python environment: ${err}')
panic(err) panic(err)
} }
// Test adding packages // Test adding packages
py.add_dependencies(['ipython'], false) or { py.add_dependencies(['ipython'], false) or {
console.print_stderr('Failed to add dependencies: ${err}') console.print_stderr('Failed to add dependencies: ${err}')
panic(err) panic(err)
} }
// Test legacy pip method // Test legacy pip method
py.pip('requests') or { py.pip('requests') or {
console.print_stderr('Failed to install via pip method: ${err}') console.print_stderr('Failed to install via pip method: ${err}')
panic(err) panic(err)
} }
console.print_debug(' Package management test passed') console.print_debug(' Package management test passed')
} }
fn test_python_freeze_functionality() { fn test_python_freeze_functionality() {
console.print_debug('Testing freeze functionality') console.print_debug('Testing freeze functionality')
py := new( py := new(
name: 'test_freeze' name: 'test_freeze'
dependencies: ['click'] dependencies: ['click']
reset: true reset: true
) or { ) or {
console.print_stderr('Failed to create Python environment: ${err}') console.print_stderr('Failed to create Python environment: ${err}')
panic(err) panic(err)
} }
// Test freeze // Test freeze
requirements := py.freeze() or { requirements := py.freeze() or {
console.print_stderr('Failed to freeze requirements: ${err}') console.print_stderr('Failed to freeze requirements: ${err}')
panic(err) panic(err)
} }
assert requirements.len > 0 assert requirements.len > 0
console.print_debug(' Freeze functionality test passed') console.print_debug(' Freeze functionality test passed')
} }
fn test_python_template_generation() { fn test_python_template_generation() {
console.print_debug('Testing template generation') console.print_debug('Testing template generation')
py := new(name: 'test_templates', reset: true) or { py := new(name: 'test_templates', reset: true) or {
console.print_stderr('Failed to create Python environment: ${err}') console.print_stderr('Failed to create Python environment: ${err}')
panic(err) panic(err)
} }
// Check that pyproject.toml was generated // Check that pyproject.toml was generated
pyproject_exists := py.path.file_exists('pyproject.toml') pyproject_exists := py.path.file_exists('pyproject.toml')
assert pyproject_exists assert pyproject_exists
// Check that shell scripts were generated // Check that shell scripts were generated
env_script_exists := py.path.file_exists('env.sh') env_script_exists := py.path.file_exists('env.sh')
install_script_exists := py.path.file_exists('install.sh') install_script_exists := py.path.file_exists('install.sh')
assert env_script_exists assert env_script_exists
assert install_script_exists assert install_script_exists
console.print_debug(' Template generation test passed') console.print_debug(' Template generation test passed')
} }
// Main test function that runs all tests // Main test function that runs all tests
fn test_python() { fn test_python() {
console.print_header('Running Python module tests') console.print_header('Running Python module tests')
test_python_env_creation() test_python_env_creation()
test_python_env_with_dependencies() test_python_env_with_dependencies()
test_python_package_management() test_python_package_management()
test_python_freeze_functionality() test_python_freeze_functionality()
test_python_template_generation() test_python_template_generation()
console.print_green('🎉 All Python module tests passed!') console.print_green('🎉 All Python module tests passed!')
} }

View File

@@ -28,16 +28,16 @@ pub fn (py PythonEnv) python_shell() ! {
// Open IPython if available, fallback to regular Python // Open IPython if available, fallback to regular Python
pub fn (py PythonEnv) ipython_shell() ! { pub fn (py PythonEnv) ipython_shell() ! {
console.print_green('Opening IPython shell for environment: ${py.name}') console.print_green('Opening IPython shell for environment: ${py.name}')
// Check if IPython is available // Check if IPython is available
check_cmd := ' check_cmd := '
cd ${py.path.path} cd ${py.path.path}
source .venv/bin/activate source .venv/bin/activate
python -c "import IPython" python -c "import IPython"
' '
check_result := osal.exec(cmd: check_cmd, raise_error: false)! check_result := osal.exec(cmd: check_cmd, raise_error: false)!
mut shell_cmd := '' mut shell_cmd := ''
if check_result.exit_code == 0 { if check_result.exit_code == 0 {
shell_cmd = ' shell_cmd = '
@@ -53,7 +53,7 @@ pub fn (py PythonEnv) ipython_shell() ! {
python python
' '
} }
osal.execute_interactive(shell_cmd)! osal.execute_interactive(shell_cmd)!
} }
@@ -76,4 +76,4 @@ pub fn (py PythonEnv) uv_run(command string) !osal.Job {
uv ${command} uv ${command}
' '
return osal.exec(cmd: cmd)! return osal.exec(cmd: cmd)!
} }

View File

@@ -7,41 +7,41 @@ import os
@[params] @[params]
pub struct TemplateArgs { pub struct TemplateArgs {
pub mut: pub mut:
name string = 'herolib-python-project' name string = 'herolib-python-project'
version string = '0.1.0' version string = '0.1.0'
description string = 'A Python project managed by Herolib' description string = 'A Python project managed by Herolib'
python_version string = '3.11' python_version string = '3.11'
dependencies []string dependencies []string
dev_dependencies []string dev_dependencies []string
scripts map[string]string scripts map[string]string
} }
// generate_pyproject_toml creates a pyproject.toml file from template // generate_pyproject_toml creates a pyproject.toml file from template
pub fn (mut py PythonEnv) generate_pyproject_toml(args TemplateArgs) ! { pub fn (mut py PythonEnv) generate_pyproject_toml(args TemplateArgs) ! {
template_path := '${@VMODROOT}/lang/python/templates/pyproject.toml' template_path := '${@VMODROOT}/lang/python/templates/pyproject.toml'
mut template_content := os.read_file(template_path)! mut template_content := os.read_file(template_path)!
// Format dependencies // Format dependencies
mut deps := []string{} mut deps := []string{}
for dep in args.dependencies { for dep in args.dependencies {
deps << ' "${dep}",' deps << ' "${dep}",'
} }
dependencies_str := deps.join('\n') dependencies_str := deps.join('\n')
// Format dev dependencies // Format dev dependencies
mut dev_deps := []string{} mut dev_deps := []string{}
for dep in args.dev_dependencies { for dep in args.dev_dependencies {
dev_deps << ' "${dep}",' dev_deps << ' "${dep}",'
} }
dev_dependencies_str := dev_deps.join('\n') dev_dependencies_str := dev_deps.join('\n')
// Format scripts // Format scripts
mut scripts := []string{} mut scripts := []string{}
for name, command in args.scripts { for name, command in args.scripts {
scripts << '${name} = "${command}"' scripts << '${name} = "${command}"'
} }
scripts_str := scripts.join('\n') scripts_str := scripts.join('\n')
// Replace template variables // Replace template variables
content := template_content content := template_content
.replace('@{name}', args.name) .replace('@{name}', args.name)
@@ -51,7 +51,7 @@ pub fn (mut py PythonEnv) generate_pyproject_toml(args TemplateArgs) ! {
.replace('@{dependencies}', dependencies_str) .replace('@{dependencies}', dependencies_str)
.replace('@{dev_dependencies}', dev_dependencies_str) .replace('@{dev_dependencies}', dev_dependencies_str)
.replace('@{scripts}', scripts_str) .replace('@{scripts}', scripts_str)
// Write to project directory // Write to project directory
mut pyproject_file := py.path.file_get_new('pyproject.toml')! mut pyproject_file := py.path.file_get_new('pyproject.toml')!
pyproject_file.write(content)! pyproject_file.write(content)!
@@ -61,10 +61,10 @@ pub fn (mut py PythonEnv) generate_pyproject_toml(args TemplateArgs) ! {
pub fn (mut py PythonEnv) generate_env_script(args TemplateArgs) ! { pub fn (mut py PythonEnv) generate_env_script(args TemplateArgs) ! {
template_path := '${@VMODROOT}/lang/python/templates/env.sh' template_path := '${@VMODROOT}/lang/python/templates/env.sh'
mut template_content := os.read_file(template_path)! mut template_content := os.read_file(template_path)!
content := template_content content := template_content
.replace('@{python_version}', args.python_version) .replace('@{python_version}', args.python_version)
mut env_file := py.path.file_get_new('env.sh')! mut env_file := py.path.file_get_new('env.sh')!
env_file.write(content)! env_file.write(content)!
os.chmod(env_file.path, 0o755)! os.chmod(env_file.path, 0o755)!
@@ -74,11 +74,11 @@ pub fn (mut py PythonEnv) generate_env_script(args TemplateArgs) ! {
pub fn (mut py PythonEnv) generate_install_script(args TemplateArgs) ! { pub fn (mut py PythonEnv) generate_install_script(args TemplateArgs) ! {
template_path := '${@VMODROOT}/lang/python/templates/install.sh' template_path := '${@VMODROOT}/lang/python/templates/install.sh'
mut template_content := os.read_file(template_path)! mut template_content := os.read_file(template_path)!
content := template_content content := template_content
.replace('@{name}', args.name) .replace('@{name}', args.name)
.replace('@{python_version}', args.python_version) .replace('@{python_version}', args.python_version)
mut install_file := py.path.file_get_new('install.sh')! mut install_file := py.path.file_get_new('install.sh')!
install_file.write(content)! install_file.write(content)!
os.chmod(install_file.path, 0o755)! os.chmod(install_file.path, 0o755)!
@@ -123,10 +123,18 @@ source env.sh
## Dependencies ## Dependencies
### Production ### Production
${if args.dependencies.len > 0 { '- ' + args.dependencies.join('\n- ') } else { 'None' }} ${if args.dependencies.len > 0 {
'- ' + args.dependencies.join('\n- ')
} else {
'None'
}}
### Development ### Development
${if args.dev_dependencies.len > 0 { '- ' + args.dev_dependencies.join('\n- ') } else { 'None' }} ${if args.dev_dependencies.len > 0 {
'- ' + args.dev_dependencies.join('\n- ')
} else {
'None'
}}
' '
mut readme_file := py.path.file_get_new('README.md')! mut readme_file := py.path.file_get_new('README.md')!
@@ -139,4 +147,4 @@ pub fn (mut py PythonEnv) generate_all_templates(args TemplateArgs) ! {
py.generate_env_script(args)! py.generate_env_script(args)!
py.generate_install_script(args)! py.generate_install_script(args)!
py.generate_readme(args)! py.generate_readme(args)!
} }

View File

@@ -76,11 +76,11 @@ pub fn (mut sm StartupManager) new(args ZProcessNewArgs) ! {
// Map ZProcessNewArgs to zinit.ServiceConfig // Map ZProcessNewArgs to zinit.ServiceConfig
mut service_config := zinit.ServiceConfig{ mut service_config := zinit.ServiceConfig{
exec: args.cmd exec: args.cmd
test: args.cmd_test // Direct mapping test: args.cmd_test // Direct mapping
oneshot: args.oneshot // Use the oneshot flag directly oneshot: args.oneshot // Use the oneshot flag directly
after: args.after // Direct mapping after: args.after // Direct mapping
log: "ring" log: 'ring'
env: args.env // Direct mapping env: args.env // Direct mapping
dir: args.workdir // Direct mapping dir: args.workdir // Direct mapping
shutdown_timeout: 0 // Default, or add to ZProcessNewArgs if needed shutdown_timeout: 0 // Default, or add to ZProcessNewArgs if needed

View File

@@ -2,7 +2,6 @@ module datamodel
import time import time
// NodeTotalSim represents the aggregated data for a node simulation, including hardware specs, pricing, and location. // NodeTotalSim represents the aggregated data for a node simulation, including hardware specs, pricing, and location.
pub struct NodeTotalSim { pub struct NodeTotalSim {
pub mut: pub mut:
@@ -23,14 +22,14 @@ pub fn (n NodeSim) total() !NodeTotalSim {
} }
mut total := NodeTotalSim{ mut total := NodeTotalSim{
id: n.id id: n.id
cost: n.cost cost: n.cost
deliverytime: time.now() // Placeholder; replace with actual logic if needed deliverytime: time.now() // Placeholder; replace with actual logic if needed
inca_reward: 0 // Placeholder; compute from policy if available inca_reward: 0 // Placeholder; compute from policy if available
reputation: 50 // Default; compute from uptime/history reputation: 50 // Default; compute from uptime/history
uptime: n.uptime uptime: n.uptime
price_simulation: 0.0 price_simulation: 0.0
capacity: NodeCapacity{} capacity: NodeCapacity{}
} }
// Aggregate from compute slices // Aggregate from compute slices

View File

@@ -3,46 +3,46 @@ module datamodel
@[heap] @[heap]
pub struct Node { pub struct Node {
pub mut: pub mut:
id int id int
nodegroupid int nodegroupid int
uptime int // 0..100 uptime int // 0..100
computeslices []ComputeSlice computeslices []ComputeSlice
storageslices []StorageSlice storageslices []StorageSlice
devices DeviceInfo devices DeviceInfo
country string // 2 letter code as specified in lib/data/countries/data/countryInfo.txt, use that library for validation country string // 2 letter code as specified in lib/data/countries/data/countryInfo.txt, use that library for validation
capacity NodeCapacity // Hardware capacity details capacity NodeCapacity // Hardware capacity details
provisiontime u32 //lets keep it simple and compatible provisiontime u32 // lets keep it simple and compatible
} }
pub struct DeviceInfo { pub struct DeviceInfo {
pub mut: pub mut:
vendor string vendor string
storage []StorageDevice storage []StorageDevice
memory []MemoryDevice memory []MemoryDevice
cpu []CPUDevice cpu []CPUDevice
gpu []GPUDevice gpu []GPUDevice
network []NetworkDevice network []NetworkDevice
} }
pub struct StorageDevice { pub struct StorageDevice {
pub mut: pub mut:
id string //can be used in node id string // can be used in node
size_gb f64 // Size of the storage device in gigabytes size_gb f64 // Size of the storage device in gigabytes
description string // Description of the storage device description string // Description of the storage device
} }
pub struct MemoryDevice { pub struct MemoryDevice {
pub mut: pub mut:
id string //can be used in node id string // can be used in node
size_gb f64 // Size of the memory device in gigabytes size_gb f64 // Size of the memory device in gigabytes
description string // Description of the memory device description string // Description of the memory device
} }
pub struct CPUDevice { pub struct CPUDevice {
pub mut: pub mut:
id string //can be used in node id string // can be used in node
cores int // Number of CPU cores cores int // Number of CPU cores
passmark int passmark int
description string // Description of the CPU description string // Description of the CPU
cpu_brand string // Brand of the CPU cpu_brand string // Brand of the CPU
cpu_version string // Version of the CPU cpu_version string // Version of the CPU
@@ -50,19 +50,18 @@ pub mut:
pub struct GPUDevice { pub struct GPUDevice {
pub mut: pub mut:
id string //can be used in node id string // can be used in node
cores int // Number of GPU cores cores int // Number of GPU cores
memory_gb f64 // Size of the GPU memory in gigabytes memory_gb f64 // Size of the GPU memory in gigabytes
description string // Description of the GPU description string // Description of the GPU
gpu_brand string gpu_brand string
gpu_version string gpu_version string
} }
pub struct NetworkDevice { pub struct NetworkDevice {
pub mut: pub mut:
id string //can be used in node id string // can be used in node
speed_mbps int // Network speed in Mbps speed_mbps int // Network speed in Mbps
description string // Description of the network device description string // Description of the network device
} }
@@ -84,5 +83,5 @@ pub mut:
} }
fn (mut n Node) check() ! { fn (mut n Node) check() ! {
//todo // todo
} }

View File

@@ -1,31 +1,30 @@
module datamodel module datamodel
// is a root object, is the only obj farmer needs to configure in the UI, this defines how slices will be created
@[heap] @[heap]
//is a root object, is the only obj farmer needs to configure in the UI, this defines how slices will be created
pub struct NodeGroup { pub struct NodeGroup {
pub mut: pub mut:
id u32 id u32
farmerid u32 //link back to farmer who owns the nodegroup, is a user? farmerid u32 // link back to farmer who owns the nodegroup, is a user?
secret string //only visible by farmer, in future encrypted, used to boot a node secret string // only visible by farmer, in future encrypted, used to boot a node
description string description string
slapolicy SLAPolicy slapolicy SLAPolicy
pricingpolicy PricingPolicy pricingpolicy PricingPolicy
compute_slice_normalized_pricing_cc f64 //pricing in CC - cloud credit, per 2GB node slice compute_slice_normalized_pricing_cc f64 // pricing in CC - cloud credit, per 2GB node slice
storage_slice_normalized_pricing_cc f64 //pricing in CC - cloud credit, per 1GB storage slice storage_slice_normalized_pricing_cc f64 // pricing in CC - cloud credit, per 1GB storage slice
reputation int = 50 //between 0 and 100, earned over time reputation int = 50 // between 0 and 100, earned over time
uptime int //between 0 and 100, set by system, farmer has no ability to set this uptime int // between 0 and 100, set by system, farmer has no ability to set this
} }
pub struct SLAPolicy { pub struct SLAPolicy {
pub mut: pub mut:
sla_uptime int //should +90 sla_uptime int // should +90
sla_bandwidth_mbit int //minimal mbits we can expect avg over 1h per node, 0 means we don't guarantee sla_bandwidth_mbit int // minimal mbits we can expect avg over 1h per node, 0 means we don't guarantee
sla_penalty int //0-100, percent of money given back in relation to month if sla breached, e.g. 200 means we return 2 months worth of rev if sla missed sla_penalty int // 0-100, percent of money given back in relation to month if sla breached, e.g. 200 means we return 2 months worth of rev if sla missed
} }
pub struct PricingPolicy { pub struct PricingPolicy {
pub mut: pub mut:
marketplace_year_discounts []int = [30,40,50] //e.g. 30,40,50 means if user has more CC in wallet than 1 year utilization on all his purchaes then this provider gives 30%, 2Y 40%, ... marketplace_year_discounts []int = [30, 40, 50] // e.g. 30,40,50 means if user has more CC in wallet than 1 year utilization on all his purchaes then this provider gives 30%, 2Y 40%, ...
volume_discounts []int = [10,20,30] //e.g. 10,20,30 volume_discounts []int = [10, 20, 30] // e.g. 10,20,30
} }

View File

@@ -3,5 +3,5 @@ module datamodel
pub struct NodeSim { pub struct NodeSim {
Node Node
pub mut: pub mut:
cost f64 //free cost f64 // free
} }

View File

@@ -1,32 +1,29 @@
module datamodel module datamodel
//typically 1GB of memory, but can be adjusted based based on size of machine // typically 1GB of memory, but can be adjusted based based on size of machine
pub struct ComputeSlice { pub struct ComputeSlice {
pub mut: pub mut:
nodeid u32 //the node in the grid, there is an object describing the node nodeid u32 // the node in the grid, there is an object describing the node
id int //the id of the slice in the node id int // the id of the slice in the node
mem_gb f64 mem_gb f64
storage_gb f64 storage_gb f64
passmark int passmark int
vcores int vcores int
cpu_oversubscription int cpu_oversubscription int
storage_oversubscription int storage_oversubscription int
price_range []f64 = [0.0, 0.0] price_range []f64 = [0.0, 0.0]
gpus u8 //nr of GPU's see node to know what GPU's are gpus u8 // nr of GPU's see node to know what GPU's are
price_cc f64 //price per slice (even if the grouped one) price_cc f64 // price per slice (even if the grouped one)
pricing_policy PricingPolicy pricing_policy PricingPolicy
sla_policy SLAPolicy sla_policy SLAPolicy
} }
//1GB of storage // 1GB of storage
pub struct StorageSlice { pub struct StorageSlice {
pub mut: pub mut:
nodeid u32 //the node in the grid nodeid u32 // the node in the grid
id int //the id of the slice in the node, are tracked in the node itself id int // the id of the slice in the node, are tracked in the node itself
price_cc f64 //price per slice (even if the grouped one) price_cc f64 // price per slice (even if the grouped one)
pricing_policy PricingPolicy pricing_policy PricingPolicy
sla_policy SLAPolicy sla_policy SLAPolicy
} }

View File

@@ -2,7 +2,6 @@ module datamodelsimulator
import time import time
// NodeTotalSim represents the aggregated data for a node simulation, including hardware specs, pricing, and location. // NodeTotalSim represents the aggregated data for a node simulation, including hardware specs, pricing, and location.
pub struct NodeTotalSim { pub struct NodeTotalSim {
pub mut: pub mut:
@@ -23,14 +22,14 @@ pub fn (n NodeSim) total() !NodeTotalSim {
} }
mut total := NodeTotalSim{ mut total := NodeTotalSim{
id: n.id id: n.id
cost: n.cost cost: n.cost
deliverytime: time.now() // Placeholder; replace with actual logic if needed deliverytime: time.now() // Placeholder; replace with actual logic if needed
inca_reward: 0 // Placeholder; compute from policy if available inca_reward: 0 // Placeholder; compute from policy if available
reputation: 50 // Default; compute from uptime/history reputation: 50 // Default; compute from uptime/history
uptime: n.uptime uptime: n.uptime
price_simulation: 0.0 price_simulation: 0.0
capacity: NodeCapacity{} capacity: NodeCapacity{}
} }
// Aggregate from compute slices // Aggregate from compute slices

View File

@@ -8,7 +8,7 @@ pub fn play(mut plbook PlayBook) !map[string]&Node {
mut nodesdict := map[string]&Node{} mut nodesdict := map[string]&Node{}
for action in actions2 { for action in actions2 {
echo("TODO: Implement action handling for ${action.name}") echo('TODO: Implement action handling for ${action.name}')
} }
return nodesdict return nodesdict
} }

View File

@@ -30,8 +30,8 @@ pub mut:
pub fn new(args FactoryArgs) !&App { pub fn new(args FactoryArgs) !&App {
base := os.dir(@FILE) base := os.dir(@FILE)
mut app := App{ mut app := App{
title: args.title title: args.title
port: args.port port: args.port
base_path: base base_path: base
} }
// Serve static assets from this module at /static // Serve static assets from this module at /static
@@ -62,10 +62,10 @@ fn render_index(app &App) string {
} }
fn render_index_fallback(app &App) string { fn render_index_fallback(app &App) string {
return '<!doctype html>\n<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>' return
+ html_escape(app.title) '<!doctype html>\n<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>' +
+ '</title><link rel="stylesheet" href="/static/css/main.css"></head><body><div class="container"><h1>' html_escape(app.title) +
+ html_escape(app.title) '</title><link rel="stylesheet" href="/static/css/main.css"></head><body><div class="container"><h1>' +
+ '</h1><p>Heroprompt server is running.</p></div><script src="/static/js/main.js"></script></body></html>' html_escape(app.title) +
'</h1><p>Heroprompt server is running.</p></div><script src="/static/js/main.js"></script></body></html>'
} }

View File

@@ -3,7 +3,6 @@ module ui
import veb import veb
import os import os
// Public Context type for veb // Public Context type for veb
pub struct Context { pub struct Context {
veb.Context veb.Context
@@ -33,18 +32,17 @@ pub mut:
pub struct App { pub struct App {
veb.StaticHandler veb.StaticHandler
pub mut: pub mut:
title string = "default" title string = 'default'
menu []MenuItem menu []MenuItem
port int = 7711 port int = 7711
} }
// Start the webserver (blocking) // Start the webserver (blocking)
pub fn start(args WebArgs) !{ pub fn start(args WebArgs) ! {
mut app := App{ mut app := App{
title: args.title, title: args.title
menu: args.menu, menu: args.menu
port: args.port port: args.port
} }
veb.run[App, Context](mut app, app.port) veb.run[App, Context](mut app, app.port)
} }

View File

@@ -8,7 +8,11 @@ fn menu_html(items []MenuItem, depth int, prefix string) string {
if it.children.len > 0 { if it.children.len > 0 {
// expandable group // expandable group
out << '<div class="item">' out << '<div class="item">'
out << '<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" data-bs-toggle="collapse" href="#${id}" role="button" aria-expanded="${if depth == 0 { 'true' } else { 'false' }}" aria-controls="${id}">' out << '<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" data-bs-toggle="collapse" href="#${id}" role="button" aria-expanded="${if depth == 0 {
'true'
} else {
'false'
}}" aria-controls="${id}">'
out << '<span>${it.title}</span><span class="chev">&rsaquo;</span>' out << '<span>${it.title}</span><span class="chev">&rsaquo;</span>'
out << '</a>' out << '</a>'
out << '<div class="collapse ${if depth == 0 { 'show' } else { '' }}" id="${id}">' out << '<div class="collapse ${if depth == 0 { 'show' } else { '' }}" id="${id}">'
@@ -24,4 +28,3 @@ fn menu_html(items []MenuItem, depth int, prefix string) string {
} }
return out.join('\n') return out.join('\n')
} }