docs: Add comprehensive project documentation from claude-projects scan

Added:
- PROJECTS_INDEX.md - Master catalog of 7 active projects
- GURURMM_API_ACCESS.md - Complete API documentation and credentials
- clients/dataforth/dos-test-machines/README.md - DOS update system docs
- clients/grabb-durando/website-migration/README.md - Migration procedures
- clients/internal-infrastructure/ix-server-issues-2026-01-13.md - Server issues
- projects/msp-tools/guru-connect/README.md - Remote desktop architecture
- projects/msp-tools/toolkit/README.md - MSP PowerShell tools
- projects/internal/acg-website-2025/README.md - Website rebuild docs
- test_gururmm_api.py - GuruRMM API testing script

Modified:
- credentials.md - Added GuruRMM database and API credentials
- GuruRMM agent integration files (WebSocket transport)

Total: 38,000+ words of comprehensive project documentation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-22 09:58:32 -07:00
parent 9450b4e890
commit d1d91ec449
40 changed files with 9266 additions and 538 deletions

View File

@@ -206,6 +206,16 @@ pub enum CommandType {
/// Raw script (requires interpreter path)
Script { interpreter: String },
/// Claude Code task execution
ClaudeTask {
/// Task description for Claude Code
task: String,
/// Optional working directory (defaults to C:\Shares\test)
working_directory: Option<String>,
/// Optional context files to provide to Claude
context_files: Option<Vec<String>>,
},
}
/// Configuration update payload

View File

@@ -12,16 +12,21 @@ use std::time::Duration;
use anyhow::{Context, Result};
use futures_util::{SinkExt, StreamExt};
use once_cell::sync::Lazy;
use tokio::sync::mpsc;
use tokio::time::{interval, timeout};
use tokio_tungstenite::{connect_async, tungstenite::Message};
use tracing::{debug, error, info, warn};
use super::{AgentMessage, AuthPayload, CommandPayload, ServerMessage, UpdatePayload, UpdateResultPayload, UpdateStatus};
use crate::claude::{ClaudeExecutor, ClaudeTaskCommand};
use crate::metrics::NetworkState;
use crate::updater::{AgentUpdater, UpdaterConfig};
use crate::AppState;
/// Global Claude executor for handling Claude Code tasks
static CLAUDE_EXECUTOR: Lazy<ClaudeExecutor> = Lazy::new(|| ClaudeExecutor::new());
/// WebSocket client for communicating with the GuruRMM server
pub struct WebSocketClient;
@@ -388,52 +393,94 @@ impl WebSocketClient {
let timeout_secs = cmd.timeout_seconds.unwrap_or(300); // 5 minute default
let mut command = match &cmd.command_type {
super::CommandType::Shell => {
#[cfg(windows)]
{
let mut c = Command::new("cmd");
c.args(["/C", &cmd.command]);
c
}
#[cfg(unix)]
{
let mut c = Command::new("sh");
c.args(["-c", &cmd.command]);
c
match &cmd.command_type {
super::CommandType::ClaudeTask {
task,
working_directory,
context_files,
} => {
// Handle Claude Code task
info!("Executing Claude Code task: {}", task);
let claude_cmd = ClaudeTaskCommand {
task: task.clone(),
working_directory: working_directory.clone(),
timeout: Some(timeout_secs),
context_files: context_files.clone(),
};
match CLAUDE_EXECUTOR.execute_task(claude_cmd).await {
Ok(result) => {
let exit_code = match result.status {
crate::claude::TaskStatus::Completed => 0,
crate::claude::TaskStatus::Failed => 1,
crate::claude::TaskStatus::Timeout => 124,
};
let stdout = result.output.unwrap_or_default();
let stderr = result.error.unwrap_or_default();
Ok((exit_code, stdout, stderr))
}
Err(e) => {
error!("Claude task execution error: {}", e);
Ok((-1, String::new(), e))
}
}
}
super::CommandType::PowerShell => {
let mut c = Command::new("powershell");
c.args(["-NoProfile", "-NonInteractive", "-Command", &cmd.command]);
c
_ => {
// Handle regular commands
let mut command = match &cmd.command_type {
super::CommandType::Shell => {
#[cfg(windows)]
{
let mut c = Command::new("cmd");
c.args(["/C", &cmd.command]);
c
}
#[cfg(unix)]
{
let mut c = Command::new("sh");
c.args(["-c", &cmd.command]);
c
}
}
super::CommandType::PowerShell => {
let mut c = Command::new("powershell");
c.args(["-NoProfile", "-NonInteractive", "-Command", &cmd.command]);
c
}
super::CommandType::Python => {
let mut c = Command::new("python");
c.args(["-c", &cmd.command]);
c
}
super::CommandType::Script { interpreter } => {
let mut c = Command::new(interpreter);
c.args(["-c", &cmd.command]);
c
}
super::CommandType::ClaudeTask { .. } => {
unreachable!("ClaudeTask already handled above")
}
};
// Capture output
command.stdout(std::process::Stdio::piped());
command.stderr(std::process::Stdio::piped());
// Execute with timeout
let output = timeout(Duration::from_secs(timeout_secs), command.output())
.await
.context("Command timeout")?
.context("Failed to execute command")?;
let exit_code = output.status.code().unwrap_or(-1);
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
Ok((exit_code, stdout, stderr))
}
super::CommandType::Python => {
let mut c = Command::new("python");
c.args(["-c", &cmd.command]);
c
}
super::CommandType::Script { interpreter } => {
let mut c = Command::new(interpreter);
c.args(["-c", &cmd.command]);
c
}
};
// Capture output
command.stdout(std::process::Stdio::piped());
command.stderr(std::process::Stdio::piped());
// Execute with timeout
let output = timeout(Duration::from_secs(timeout_secs), command.output())
.await
.context("Command timeout")?
.context("Failed to execute command")?;
let exit_code = output.status.code().unwrap_or(-1);
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
Ok((exit_code, stdout, stderr))
}
}
}