Composer plugin package for provisioning LiteSpeed Web Server and Load Balancer licenses inside the MyAdmin billing system.
composer install # install deps including detain/litespeed-licensing
vendor/bin/phpunit # run all testsNamespace: Detain\MyAdminLiteSpeed\ → src/ · Tests: Detain\MyAdminLiteSpeed\Tests\ → tests/
Key files:
src/Plugin.php— Symfony EventDispatcher hook registration; all hooks registered ingetHooks()src/litespeed.inc.php— procedural activation/deactivation functions loaded viafunction_requirements()src/litespeed_list.php— admin UI page functionlitespeed_list()usingTFTable+add_output()bin/activate_litespeed.php·bin/test_litespeed_licenses.php— manual CLI test scripts
Hook lifecycle (src/Plugin.php::getHooks()):
licenses.settings→getSettings()— registersLITESPEED_USERNAME,LITESPEED_PASSWORD,OUTOFSTOCK_LICENSES_LITESPEEDvia$settings->add_text_setting()/add_password_setting()/add_dropdown_setting()licenses.activate/licenses.reactivate→getActivate()— callsactivate_litespeed_new(); sets serial via$serviceClass->setKey()->setExtra()->save()or sets statuspendinglicenses.deactivate/licenses.deactivate_ip→getDeactivate()— callsdeactivate_litespeed_new($serial)licenses.change_ip→getChangeIp()— cancel old IP then re-activate; logs to$GLOBALS['tf']->historyfunction.requirements→getRequirements()— registerslitespeed.inc.phpfunctions with$loader->add_requirement()ui.menu→getMenu()— adds admin link forchoice=none.litespeed_list
LiteSpeed API clients (two generations in src/litespeed.inc.php):
- Legacy:
new \Detain\LiteSpeed\LiteSpeed(LITESPEED_USERNAME, LITESPEED_PASSWORD)—order(),cancel() - Current:
new \Ganesh\LiteSpeed\LiteSpeedClient(LITESPEED_USERNAME, LITESPEED_PASSWORD, true)—order(),cancel(),getLicenseDetails(),getBalance()
Activation guards in activate_litespeed_new():
- Duplicate check via
getLicenseDetails('IP', $ipAddress)when$lic_check=true - Credit balance check via
getBalance()— abort ifcredit <= 5.00(exceptWS_Fproduct) - Sends admin error email via
(new \MyAdmin\Mail())->adminMail($subject, $body, false, 'admin/licenses_error.tpl')on failure
Logging pattern (use everywhere):
request_log('licenses', false, __FUNCTION__, 'litespeed', 'methodName', [$args], $response);
myadmin_log('licenses', 'info', "Message text", __LINE__, __FILE__);Email on error (deactivation failure):
$smartyE = new TFSmarty();
$smartyE->assign('h1', 'LiteSpeed License Deactivation');
$smartyE->assign('body_rows', $bodyRows);
$msg = $smartyE->fetch('email/client/client_email.tpl');
(new \MyAdmin\Mail())->adminMail($subject, $msg, false, 'client/client_email.tpl');Test suite in tests/ with PHPUnit 9 (config phpunit.xml.dist):
tests/FileExistenceTest.php— assertssrc/Plugin.php,src/litespeed.inc.php,src/litespeed_list.php,composer.json,README.mdexist and contain expected symbolstests/FunctionSignatureTest.php— usesReflectionFunctionto assert parameter names/defaults foractivate_litespeed,activate_litespeed_new,deactivate_litespeed,deactivate_litespeed_newtests/LitespeedListTest.php— string-assertssrc/litespeed_list.phpusesget_module_settings('licenses'),TFTable,add_output()tests/PluginTest.php— usesReflectionClassonPlugin::class; asserts all 8 hook keys, static properties$name/$module/$type, method signatures acceptingGenericEvent
Test bootstrap stubs missing globals: define myadmin_log(), request_log(), chatNotify(), LITESPEED_USERNAME, LITESPEED_PASSWORD constants before loading src/litespeed.inc.php in tests.
- All
Plugin.phpmethods arepublic static functionacceptingGenericEvent $event - Always call
$event->stopPropagation()after handling in activate/deactivate/change_ip handlers - Check
$event['category'] == get_service_define('LITESPEED')before acting in every handler - Module constant:
self::$module = 'licenses' - Product codes:
LSWS(Web Server),LSLB(Load Balancer); tiers: 1/2/4/8-CPU, VPS, Ultra-VPS (WS_F) - Tab indentation (per
.scrutinizer.yml), camelCase params and properties - Commit messages: lowercase descriptive (
fix litespeed activation,add ip change logging)
IMPORTANT: Before every git commit, you MUST ensure Caliber syncs agent configs with the latest code changes.
First, check if the pre-commit hook is already installed:
grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"- If hook-active: the hook handles sync automatically — just commit normally. Tell the user: "Caliber will sync your agent configs automatically via the pre-commit hook."
- If no-hook: run Caliber manually before committing:
- Tell the user: "Caliber: Syncing agent configs with your latest changes..."
- Run:
caliber refresh && git add CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md .agents/ .opencode/ 2>/dev/null - After it completes, briefly tell the user what Caliber updated. Then proceed with the commit.
If caliber is not found, tell the user: "This project uses Caliber for agent config sync. Run /setup-caliber to get set up."
Read CALIBER_LEARNINGS.md for patterns and anti-patterns learned from previous sessions.
These are auto-extracted from real tool usage — treat them as project-specific rules.
This project uses Caliber to keep AI agent configs in sync across Claude Code, Cursor, Copilot, and Codex.
Configs update automatically before each commit via caliber refresh.
If the pre-commit hook is not set up, run /setup-caliber to configure everything automatically.