10 Commits
v1.3.6 ... main

Author SHA1 Message Date
976c4d0980 1.5.1
Some checks failed
CI / Type Check & Lint (push) Failing after 15s
Publish to npm / npm-publish (push) Failing after 42s
CI / Build Test (Current Platform) (push) Successful in 1m14s
CI / Build All Platforms (push) Successful in 1m56s
Release / build-and-release (push) Successful in 3m55s
2025-10-29 01:23:04 +00:00
eda652994d fix(scriptindex): Improve script search: use ObjectSorter with weighted results prioritizing slug and name 2025-10-29 01:23:04 +00:00
46f82d2b65 1.5.0
Some checks failed
CI / Type Check & Lint (push) Failing after 42s
Publish to npm / npm-publish (push) Failing after 1m2s
CI / Build Test (Current Platform) (push) Successful in 1m32s
CI / Build All Platforms (push) Successful in 2m8s
Release / build-and-release (push) Successful in 2m56s
2025-10-29 01:18:28 +00:00
90c5f07be4 feat(scripts): Add fuzzy search and type filtering for community scripts; improve scripts CLI output and cache handling 2025-10-29 01:18:28 +00:00
0e7d416048 1.4.2
Some checks failed
CI / Type Check & Lint (push) Successful in 41s
Publish to npm / npm-publish (push) Failing after 1m5s
CI / Build Test (Current Platform) (push) Successful in 1m13s
CI / Build All Platforms (push) Successful in 2m2s
Release / build-and-release (push) Successful in 2m6s
2025-10-28 22:05:50 +00:00
23f41cc152 fix(scriptindex): Handle missing script metadata fields in ScriptIndex.search to prevent crashes 2025-10-28 22:05:50 +00:00
ad7e9b0b46 1.4.1
Some checks failed
CI / Type Check & Lint (push) Successful in 41s
Publish to npm / npm-publish (push) Failing after 1m0s
CI / Build Test (Current Platform) (push) Successful in 1m8s
Release / build-and-release (push) Successful in 1m48s
CI / Build All Platforms (push) Successful in 1m55s
2025-10-28 19:17:11 +00:00
8cf016f0d8 fix(cli): Fallback to unknown when script.slug is missing in scripts list 2025-10-28 19:17:11 +00:00
626cfe30ba 1.4.0
Some checks failed
CI / Type Check & Lint (push) Successful in 43s
Publish to npm / npm-publish (push) Failing after 1m1s
Release / build-and-release (push) Successful in 3m26s
CI / Build Test (Current Platform) (push) Successful in 3m42s
CI / Build All Platforms (push) Failing after 6m35s
2025-10-28 19:03:13 +00:00
45ac9af405 feat(cli): Improve CLI output and logging with colored header, grouped script listings, and ANSI-styled logger 2025-10-28 19:03:13 +00:00
8 changed files with 169 additions and 46 deletions

View File

@@ -1,5 +1,44 @@
# Changelog
## 2025-10-29 - 1.5.1 - fix(scriptindex)
Improve script search: use ObjectSorter with weighted results prioritizing slug and name
- Replaced smartfuzzy.FuzzyMatcher with smartfuzzy.ObjectSorter for multi-field fuzzy searching
- Search now runs across slug, name, and description fields
- Added weighting so slug matches are prioritized, name matches receive a medium boost
- Search returns ordered script objects based on adjusted score
## 2025-10-29 - 1.5.0 - feat(scripts)
Add fuzzy search and type filtering for community scripts; improve scripts CLI output and cache handling
- Integrate @push.rocks/smartfuzzy and use FuzzyMatcher to provide ranked, fuzzy search results for scripts
- Add optional type filtering to scripts search (e.g. type:vm, type:ct, type:pve) and parse a --filter option in the CLI
- Improve scripts CLI output: colored type badges, truncated descriptions, clearer usage/help text and filtered result messaging
- Optimize ScriptIndex.loadCache to avoid reloading when cache is already present
- Update deno.json to include the smartfuzzy dependency and export/import smartfuzzy in plugins
## 2025-10-28 - 1.4.2 - fix(scriptindex)
Handle missing script metadata fields in ScriptIndex.search to prevent crashes
- Add null/undefined checks for name, slug, and description in ScriptIndex.search to avoid runtime exceptions when script metadata is incomplete
- Improves robustness of scripts search against partially populated or malformed cached metadata
## 2025-10-28 - 1.4.1 - fix(cli)
Fallback to 'unknown' when script.slug is missing in scripts list
- Fixes a potential runtime error when listing scripts if a script entry lacks a slug
- Uses a safe fallback ('unknown') before calling padEnd to ensure stable output
- Modified file: ts/moxytool.cli.ts
## 2025-10-28 - 1.4.0 - feat(cli)
Improve CLI output and logging with colored header, grouped script listings, and ANSI-styled logger
- Set smartcli instance version from deno.json to surface the package version in the CLI
- Revamp standard command output with a colored ASCII header, clearer commands list, and improved usage line
- Group script index output by type including Proxmox VE host (pve), Containers (ct), Virtual Machines (vm), and Other
- Enhance scripts listing formatting (slug padding and bullet points) for readability
- Replace timestamped logger messages with ANSI-colored output and icons for error/warn/success/info
## 2025-10-28 - 1.3.6 - fix(deps)
Bump smartcli dependency and add local settings file

View File

@@ -1,6 +1,6 @@
{
"name": "@serve.zone/moxytool",
"version": "1.3.6",
"version": "1.5.1",
"exports": "./mod.ts",
"nodeModulesDir": "auto",
"tasks": {
@@ -49,6 +49,7 @@
"@push.rocks/smartpath": "npm:@push.rocks/smartpath@^5.0.5",
"@push.rocks/smartshell": "npm:@push.rocks/smartshell@^3.2.2",
"@push.rocks/smartexpect": "npm:@push.rocks/smartexpect@^1.0.15",
"@push.rocks/smartfuzzy": "npm:@push.rocks/smartfuzzy@^2.0.0",
"@push.rocks/smartrx": "npm:@push.rocks/smartrx@^3.0.10",
"@push.rocks/smartpromise": "npm:@push.rocks/smartpromise@^4.0.0",
"@push.rocks/smartstring": "npm:@push.rocks/smartstring@^4.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@serve.zone/moxytool",
"version": "1.3.6",
"version": "1.5.1",
"description": "Proxmox administration tool for vGPU setup, VM management, and cluster configuration",
"keywords": [
"proxmox",

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/moxytool',
version: '1.3.6',
version: '1.5.1',
description: 'Proxmox administration tool for vGPU setup, VM management, and cluster configuration'
}

View File

@@ -88,6 +88,11 @@ export class ScriptIndex {
*/
public async loadCache(): Promise<void> {
try {
// Don't reload if already cached
if (this.cache) {
return;
}
if (!Deno) {
throw new Error('Deno runtime not available');
}
@@ -251,23 +256,51 @@ export class ScriptIndex {
}
/**
* Search scripts by query string
* Search scripts by query string with optional type filter
* @param query - Search query
* @param typeFilter - Optional type filter (e.g., 'vm', 'ct', 'pve')
*/
public search(query: string): IScriptMetadata[] {
public search(query: string, typeFilter?: string): IScriptMetadata[] {
if (!this.cache) {
return [];
}
const lowerQuery = query.toLowerCase();
let scripts = this.cache.scripts;
return this.cache.scripts.filter((script) => {
// Search in name, description, and slug
return (
script.name.toLowerCase().includes(lowerQuery) ||
script.slug.toLowerCase().includes(lowerQuery) ||
script.description.toLowerCase().includes(lowerQuery)
);
// Apply type filter if provided
if (typeFilter) {
scripts = scripts.filter((script) => script.type === typeFilter);
}
// Use ObjectSorter for fuzzy searching across multiple fields
const sorter = new plugins.smartfuzzy.ObjectSorter(scripts);
// Search across slug, name, and description
const results = sorter.sort(query, ['slug', 'name', 'description']);
// Post-process to weight results by which field matched
const weightedResults = results.map((result) => {
let weight = 1;
// Boost score if match was in slug (highest priority)
if (result.matches?.some((m) => m.key === 'slug')) {
weight = 3;
}
// Boost if match was in name (medium priority)
else if (result.matches?.some((m) => m.key === 'name')) {
weight = 2;
}
return {
...result,
adjustedScore: (result.score || 0) / weight,
};
});
// Sort by adjusted score and return just the script objects
return weightedResults
.sort((a, b) => (a.adjustedScore || 0) - (b.adjustedScore || 0))
.map((result) => result.item);
}
/**

View File

@@ -3,6 +3,7 @@ import * as paths from './moxytool.paths.ts';
import { logger } from './moxytool.logging.ts';
import { ScriptIndex } from './moxytool.classes.scriptindex.ts';
import { ScriptRunner } from './moxytool.classes.scriptrunner.ts';
import denoConfig from '../deno.json' with { type: 'json' };
export const runCli = async () => {
const smartshellInstance = new plugins.smartshell.Smartshell({
@@ -10,6 +11,7 @@ export const runCli = async () => {
});
const smartcliInstance = new plugins.smartcli.Smartcli();
smartcliInstance.version = denoConfig.version;
// Initialize script index and check if refresh is needed
const scriptIndex = new ScriptIndex();
@@ -31,14 +33,16 @@ export const runCli = async () => {
// Standard command (no arguments)
smartcliInstance.standardCommand().subscribe(async () => {
logger.log('info', 'MOXYTOOL - Proxmox Administration Tool');
logger.log('info', '');
logger.log('info', 'Available commands:');
logger.log('info', '* vgpu-setup - Install and configure Proxmox vGPU support');
logger.log('info', '* scripts - Manage Proxmox community scripts');
logger.log('info', '* update - Update MOXYTOOL to the latest version');
logger.log('info', '');
logger.log('info', 'Usage: moxytool <command> [options]');
console.log('\x1b[1m\x1b[36m╔════════════════════════════════════════════╗\x1b[0m');
console.log('\x1b[1m\x1b[36m║\x1b[0m \x1b[1mMOXYTOOL\x1b[0m - Proxmox Administration \x1b[1m\x1b[36m║\x1b[0m');
console.log('\x1b[1m\x1b[36m╚════════════════════════════════════════════╝\x1b[0m');
console.log('');
console.log('\x1b[1mCommands:\x1b[0m');
console.log(' \x1b[36m►\x1b[0m vgpu-setup Install and configure Proxmox vGPU support');
console.log(' \x1b[36m►\x1b[0m scripts Manage Proxmox community scripts (400+)');
console.log(' \x1b[36m►\x1b[0m update Update MOXYTOOL to the latest version');
console.log('');
console.log('\x1b[2mUsage: moxytool <command> [options]\x1b[0m');
});
// vGPU setup command
@@ -136,17 +140,8 @@ export const runCli = async () => {
logger.log('info', '');
try {
// Get current version from deno.json
const denoJsonPath = plugins.path.join(paths.packageDir, 'deno.json');
let currentVersion = '1.1.0'; // fallback
try {
const denoJsonContent = await Deno.readTextFile(denoJsonPath);
const denoJson = JSON.parse(denoJsonContent);
currentVersion = denoJson.version || currentVersion;
} catch {
// Use fallback version
}
// Get current version from compile-time imported deno.json
const currentVersion = denoConfig.version;
// Fetch latest version from Gitea API
const apiUrl = 'https://code.foss.global/api/v1/repos/serve.zone/moxytool/releases/latest';
@@ -237,8 +232,18 @@ export const runCli = async () => {
logger.log('info', '');
// Group by type
const pveScripts = scripts.filter(s => s.type === 'pve');
const containers = scripts.filter(s => s.type === 'ct');
const vms = scripts.filter(s => s.type === 'vm');
const otherScripts = scripts.filter(s => s.type !== 'pve' && s.type !== 'ct' && s.type !== 'vm');
if (pveScripts.length > 0) {
logger.log('info', 'Proxmox VE Host Scripts:');
pveScripts.forEach(script => {
logger.log('info', `${script.slug.padEnd(25)} - ${script.name}`);
});
logger.log('info', '');
}
if (containers.length > 0) {
logger.log('info', 'Containers (LXC):');
@@ -253,6 +258,15 @@ export const runCli = async () => {
vms.forEach(script => {
logger.log('info', `${script.slug.padEnd(25)} - ${script.name}`);
});
logger.log('info', '');
}
if (otherScripts.length > 0) {
logger.log('info', 'Other:');
otherScripts.forEach(script => {
const slug = script.slug || 'unknown';
logger.log('info', `${slug.padEnd(25)} - ${script.name} (${script.type})`);
});
}
logger.log('info', '');
@@ -266,28 +280,50 @@ export const runCli = async () => {
if (!query) {
logger.log('error', 'Please provide a search query');
logger.log('info', 'Usage: moxytool scripts search <query>');
logger.log('info', 'Usage: moxytool scripts search <query> [--filter type:vm]');
logger.log('info', 'Filters: type:vm, type:ct, type:pve, type:addon');
return;
}
const results = scriptIndex.search(query as string);
// Parse filter option
let typeFilter: string | undefined;
if (argvArg.filter) {
const filterString = argvArg.filter as string;
if (filterString.startsWith('type:')) {
typeFilter = filterString.substring(5);
}
}
const results = scriptIndex.search(query as string, typeFilter);
if (results.length === 0) {
logger.log('warn', `No scripts found matching "${query}"`);
const filterMsg = typeFilter ? ` (filtered by type:${typeFilter})` : '';
logger.log('warn', `No scripts found matching "${query}"${filterMsg}`);
return;
}
logger.log('info', `Found ${results.length} script(s) matching "${query}":`);
const filterMsg = typeFilter ? ` \x1b[2m(filtered by type:${typeFilter})\x1b[0m` : '';
logger.log('info', `Found ${results.length} script(s) matching "\x1b[1m${query}\x1b[0m"${filterMsg}:`);
logger.log('info', '');
results.forEach(script => {
logger.log('info', `${script.slug} (${script.type})`);
logger.log('info', ` ${script.name}`);
logger.log('info', ` ${script.description.substring(0, 80)}...`);
const slug = script.slug || 'unknown';
const description = script.description ? script.description.substring(0, 100) : 'No description available';
// Type badge with colors
let typeBadge = '';
if (script.type === 'ct') typeBadge = '\x1b[36m[LXC]\x1b[0m';
else if (script.type === 'vm') typeBadge = '\x1b[35m[VM]\x1b[0m';
else if (script.type === 'pve') typeBadge = '\x1b[33m[PVE]\x1b[0m';
else typeBadge = `\x1b[2m[${script.type}]\x1b[0m`;
logger.log('info', `\x1b[1m\x1b[36m►\x1b[0m \x1b[1m${slug}\x1b[0m ${typeBadge}`);
logger.log('info', ` \x1b[2m${script.name}\x1b[0m`);
logger.log('info', ` ${description}${script.description && script.description.length > 100 ? '...' : ''}`);
logger.log('info', '');
});
logger.log('info', 'Use "moxytool scripts info <slug>" for more details');
logger.log('info', '\x1b[2mUse "moxytool scripts info <slug>" for more details\x1b[0m');
break;
}

View File

@@ -1,5 +1,19 @@
import * as plugins from './moxytool.plugins.ts';
// ANSI color codes
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
dim: '\x1b[2m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
white: '\x1b[37m',
};
/**
* A simple logger class for MOXYTOOL
*/
@@ -14,22 +28,20 @@ class Logger {
}
public log(level: string, message: string): void {
const timestamp = new Date().toISOString();
switch (level) {
case 'error':
console.error(`[${timestamp}] [ERROR] ${message}`);
console.error(`${colors.red}${message}${colors.reset}`);
break;
case 'warn':
console.warn(`[${timestamp}] [WARN] ${message}`);
console.warn(`${colors.yellow}${message}${colors.reset}`);
break;
case 'ok':
case 'success':
console.log(`[${timestamp}] [OK] ${message}`);
console.log(`${colors.green}${message}${colors.reset}`);
break;
case 'info':
default:
console.log(`[${timestamp}] [INFO] ${message}`);
console.log(message);
break;
}
}

View File

@@ -9,6 +9,7 @@ import * as projectinfo from '@push.rocks/projectinfo';
import * as smartcli from '@push.rocks/smartcli';
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartfile from '@push.rocks/smartfile';
import * as smartfuzzy from '@push.rocks/smartfuzzy';
import * as smartjson from '@push.rocks/smartjson';
import * as smartlog from '@push.rocks/smartlog';
import * as smartlogDestinationLocal from '@push.rocks/smartlog-destination-local';
@@ -21,6 +22,7 @@ export {
smartcli,
smartdelay,
smartfile,
smartfuzzy,
smartjson,
smartlog,
smartlogDestinationLocal,