Initial commit - ViewEngine.Console sample

This commit is contained in:
2025-12-28 18:25:44 +00:00
commit f5d7701fd2
4 changed files with 420 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
# Build results
bin/
obj/
[Dd]ebug/
[Rr]elease/
# Visual Studio
.vs/
*.user
*.suo
# JetBrains Rider
.idea/
# OS files
.DS_Store
Thumbs.db
# User secrets
appsettings.*.json
!appsettings.json

226
Program.cs Executable file
View File

@@ -0,0 +1,226 @@
using ViewEngine.Client;
using ViewEngine.Client.Models;
namespace ViewEngine.Console;
/// <summary>
/// Demo console application showing how to use the ViewEngine.Client library
/// </summary>
class Program
{
static async Task Main(string[] args)
{
System.Console.WriteLine("╔══════════════════════════════════════════════════════════╗");
System.Console.WriteLine("║ ViewEngine Client Library Demo ║");
System.Console.WriteLine("║ Demonstrates using ViewEngine.Client NuGet package ║");
System.Console.WriteLine("╚══════════════════════════════════════════════════════════╝");
System.Console.WriteLine();
// Get API key from command line or prompt
string? apiKey;
if (args.Length > 0)
{
apiKey = args[0];
}
else
{
System.Console.Write("Enter your API key: ");
apiKey = System.Console.ReadLine();
}
if (string.IsNullOrWhiteSpace(apiKey))
{
System.Console.WriteLine("❌ Error: API key is required");
System.Console.WriteLine();
System.Console.WriteLine("Usage:");
System.Console.WriteLine(" dotnet run <api-key>");
System.Console.WriteLine(" OR");
System.Console.WriteLine(" dotnet run (you'll be prompted for the API key)");
return;
}
try
{
await RunDemoAsync(apiKey);
}
catch (Exception ex)
{
System.Console.WriteLine($"❌ Error: {ex.Message}");
System.Console.WriteLine();
System.Console.WriteLine("Stack trace:");
System.Console.WriteLine(ex.StackTrace);
}
System.Console.WriteLine();
System.Console.WriteLine("Press any key to exit...");
System.Console.ReadKey();
}
static async Task RunDemoAsync(string apiKey)
{
// Create the ViewEngine client
using var client = new ViewEngineClient(apiKey);
System.Console.WriteLine("🔍 Step 1: Discovering available MCP tools...");
System.Console.WriteLine();
try
{
var tools = await client.ListToolsAsync();
System.Console.WriteLine($"✅ Tools response received");
System.Console.WriteLine($" {tools}");
}
catch (Exception ex)
{
System.Console.WriteLine($"⚠️ Could not list tools: {ex.Message}");
}
System.Console.WriteLine();
System.Console.WriteLine("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
System.Console.WriteLine();
// Get URL to retrieve
System.Console.Write("Enter a URL to retrieve (or press Enter for example.com): ");
var url = System.Console.ReadLine();
if (string.IsNullOrWhiteSpace(url))
{
url = "https://example.com";
}
System.Console.WriteLine();
System.Console.Write("Force fresh retrieval? (y/n, default: n - use cache if available): ");
var forceRefreshInput = System.Console.ReadLine()?.ToLower();
var forceRefresh = forceRefreshInput == "y";
System.Console.WriteLine();
System.Console.Write("Generate AI summary? (y/n, default: n): ");
var summaryInput = System.Console.ReadLine()?.ToLower();
var generateSummary = summaryInput == "y";
System.Console.WriteLine();
System.Console.WriteLine($"🌐 Step 2: Submitting retrieval request for {url}...");
if (forceRefresh)
{
System.Console.WriteLine(" (Forcing fresh retrieval, bypassing cache)");
}
if (generateSummary)
{
System.Console.WriteLine(" (AI summary will be generated)");
}
System.Console.WriteLine();
// Submit the retrieval request
var request = new SubmitRetrievalRequest
{
Url = url,
ForceRefresh = forceRefresh,
GenerateSummary = generateSummary,
TimeoutSeconds = 60,
Priority = 5
};
var submitResponse = await client.SubmitRetrievalAsync(request);
System.Console.WriteLine($"✅ Request submitted successfully!");
System.Console.WriteLine($" Request ID: {submitResponse.RequestId}");
System.Console.WriteLine($" Status: {submitResponse.Status}");
System.Console.WriteLine($" Estimated wait: {submitResponse.EstimatedWaitTimeSeconds}s");
System.Console.WriteLine();
System.Console.WriteLine("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
System.Console.WriteLine();
System.Console.WriteLine("⏳ Step 3: Polling for results...");
System.Console.WriteLine();
// Poll for completion
RetrievalStatusResponse status;
int attempt = 0;
const int maxAttempts = 60;
do
{
attempt++;
await Task.Delay(2000);
status = await client.GetRetrievalStatusAsync(submitResponse.RequestId);
System.Console.WriteLine($" [{attempt}/{maxAttempts}] Status: {status.Status} - {status.Message}");
if (status.Status == "failed" || status.Status == "canceled")
{
System.Console.WriteLine($" Error: {status.Error}");
return;
}
} while (status.Status != "complete" && attempt < maxAttempts);
if (status.Status != "complete")
{
System.Console.WriteLine("⚠️ Timeout: Maximum polling attempts reached");
return;
}
System.Console.WriteLine();
System.Console.WriteLine($"✅ Retrieval completed!");
System.Console.WriteLine($" URL: {status.Url}");
System.Console.WriteLine($" Completed at: {status.CompletedAt}");
// Download the page content
System.Console.WriteLine();
System.Console.WriteLine("⬇️ Step 4: Downloading page content...");
var pageData = await client.GetPageContentAsync(submitResponse.RequestId);
System.Console.WriteLine();
System.Console.WriteLine("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
System.Console.WriteLine("📄 Page Content:");
System.Console.WriteLine("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
System.Console.WriteLine($" Title: {pageData.Title}");
System.Console.WriteLine($" URL: {pageData.Url}");
System.Console.WriteLine($" Meta Description: {pageData.MetaDescription ?? "(none)"}");
System.Console.WriteLine($" Favicon: {pageData.FaviconUrl ?? "(none)"}");
System.Console.WriteLine($" Routes: {pageData.Routes?.Count ?? 0} links");
System.Console.WriteLine($" Body Routes: {pageData.BodyRoutes?.Count ?? 0} links");
// Display HTTP status code (new feature!)
if (pageData.HttpStatusCode.HasValue)
{
System.Console.WriteLine($" HTTP Status: {pageData.HttpStatusCode}");
if (!pageData.IsSuccess)
{
System.Console.WriteLine($" ⚠️ Warning: Page returned error status");
System.Console.WriteLine($" IsClientError (4xx): {pageData.IsClientError}");
System.Console.WriteLine($" IsServerError (5xx): {pageData.IsServerError}");
}
}
if (!string.IsNullOrEmpty(pageData.Summary))
{
System.Console.WriteLine();
System.Console.WriteLine("🤖 AI Summary:");
System.Console.WriteLine($" {pageData.Summary}");
}
System.Console.WriteLine();
System.Console.WriteLine("📝 Body preview (first 300 chars):");
var bodyPreview = pageData.Body?.Length > 300
? pageData.Body.Substring(0, 300) + "..."
: pageData.Body ?? "(empty)";
System.Console.WriteLine($" {bodyPreview}");
if (pageData.Routes?.Count > 0)
{
System.Console.WriteLine();
System.Console.WriteLine("🔗 Top 5 Routes:");
foreach (var route in pageData.Routes.Take(5))
{
var adFlag = route.IsPotentialAd ? " [AD]" : "";
System.Console.WriteLine($" • {route.Text}: {route.Url}{adFlag}");
}
}
System.Console.WriteLine();
System.Console.WriteLine("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
System.Console.WriteLine();
System.Console.WriteLine("✅ Demo completed successfully!");
}
}

159
README.md Executable file
View File

@@ -0,0 +1,159 @@
# ViewEngine Console Demo
This console application demonstrates how to use the ViewEngine REST API with an API key.
## What it does
1. **Discovers MCP Tools** - Calls `GET /mcp/tools` to list available endpoints
2. **Submits Retrieve Request** - Calls `POST /mcp/retrieve` to start a web page retrieval job
3. **Polls for Results** - Calls `GET /mcp/retrieve/{requestId}` repeatedly until the job completes
4. **Downloads Content** - Optionally downloads the retrieved page data
## Prerequisites
1. **ViewEngine.API must be running** on `http://localhost:5072`
2. **You need an API key** from the web application:
- Log in to the web app at http://localhost:5072
- Go to Settings → API Keys
- Create a new API key
- Copy the key (it's only shown once!)
## How to Run
### Option 1: With command-line argument
```bash
cd ViewEngine.Console
dotnet run <your-api-key>
```
### Option 2: Interactive mode
```bash
cd ViewEngine.Console
dotnet run
```
You'll be prompted to enter your API key.
## Example Output
```
╔══════════════════════════════════════════════════════════╗
║ ViewEngine REST API Demo ║
║ Demonstrates using the MCP endpoints with an API key ║
╚══════════════════════════════════════════════════════════╝
Enter your API key: ak_********************************
🔍 Step 1: Discovering available MCP tools...
✅ Found 2 available tools:
• retrieve_url: Retrieve a web page and extract its content...
• get_retrieve_status: Check the status of a retrieval job...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Enter a URL to retrieve (or press Enter for example.com):
🌐 Step 2: Submitting retrieval request for https://example.com...
✅ Request submitted successfully!
Request ID: 12345678-1234-1234-1234-123456789012
Status: queued
Estimated wait: 30s
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⏳ Step 3: Polling for results (this may take a while)...
[1/60] Status: queued - Retrieval queued. Waiting for available feeder.
[2/60] Status: processing - Retrieval in progress...
[3/60] Status: complete - Retrieval completed successfully.
✅ Retrieval completed!
Status: complete
URL: https://example.com
Completed at: 2025-01-15 10:30:45
📄 Content available:
Page Data URL: https://storage.example.com/...
Content Hash: abc123...
Artifacts: screenshot, thumbnail
Metrics: load_time_ms, dom_size
Download page content? (y/n): y
⬇️ Downloading page content...
📄 Page Content (first 500 chars):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{
"url": "https://example.com",
"title": "Example Domain",
"text_content": "This domain is for use in illustrative examples...",
"html": "<html>...</html>",
...
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Demo completed successfully!
Press any key to exit...
```
## API Endpoints Used
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/v1/mcp/tools` | GET | List available MCP tools |
| `/v1/mcp/retrieve` | POST | Submit a page retrieval request |
| `/v1/mcp/retrieve/{id}` | GET | Get retrieval status and results |
## Authentication
All requests include the API key in the `X-API-Key` header:
```http
X-API-Key: ak_your-api-key-here
```
## Troubleshooting
### "API key is required"
You forgot to provide an API key. Run with `dotnet run <your-api-key>` or enter it when prompted.
### "Error: Unauthorized"
Your API key is invalid or has been revoked. Create a new one in the web app.
### "API not responding"
Make sure ViewEngine.API is running on http://localhost:5072
### "Timeout: Maximum polling attempts reached"
The retrieval job took longer than expected. This usually means:
- No feeder clients are online to process the job
- The feeder client crashed or couldn't complete the job
- Check the API logs for more details
## Code Structure
The console app is organized into clear methods:
- `Main()` - Entry point, handles API key input
- `RunDemoAsync()` - Orchestrates the demo flow
- `GetMcpToolsAsync()` - Calls GET /v1/mcp/tools
- `SubmitRetrieveRequestAsync()` - Calls POST /v1/mcp/retrieve
- `PollForResultsAsync()` - Repeatedly calls GET /v1/mcp/retrieve/{id}
- `DownloadPageDataAsync()` - Downloads the retrieved content
## Next Steps
This demo shows the MCP endpoints, but ViewEngine also has:
- **Ingest API** (`/v1/ingest/*`) - Submit retrieval jobs programmatically
- **Feeder API** (`/v1/feeders/*`) - For feeder client applications
- **Billing API** (`/v1/billing/*`) - Check earnings and pricing
See the API documentation for more details.

14
ViewEngine.Console.csproj Normal file
View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ViewEngine.Client" Version="1.2.1" />
</ItemGroup>
</Project>