Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ make dev # Start local development
make docker-up # Start with Docker
```

### 🧠 Realise (Production Bootstrap)

```bash
powershell -ExecutionPolicy Bypass -File ./scripts/Bootstrap.ps1
```

- Runs environment checks (Node.js + npm)
- Installs dependencies (root + webapp + optional `apps/api`)
- Initializes `.env` files from examples when missing
- Builds the project (unless `-SkipBuild` is passed)

`No-Drift` completion tag: `v1.0.0-STABLE` @ `2026-04-21T10:30:49.889+00:00`

```mermaid
flowchart LR
cli[src + api] --> core[Root TypeScript runtime]
web[webapp Next.js] --> core
admin[admin Electron] --> web
scripts[scripts/*] --> core
```

## 📦 Deployment Options

**Deploy to any platform in minutes!** See [DEPLOYMENT.md](DEPLOYMENT.md) for comprehensive guides.
Expand Down Expand Up @@ -757,4 +778,4 @@ For support and questions:
- ✅ **Comprehensive Validation** for all API endpoints
- ✅ **Production-Ready** with complete documentation

**Status**: Production-Ready | **Last Updated**: December 2024
**Status**: Production-Ready | **Last Updated**: December 2024
27 changes: 27 additions & 0 deletions apps/api/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# TradeOS API Environment Template
# Copy this file to .env and replace placeholder values.

# Runtime
NODE_ENV=development
PORT=3001

# Database / Prisma
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/tradeos
DIRECT_URL=postgresql://postgres:postgres@localhost:5432/tradeos
PRISMA_LOG_LEVEL=info

# Redis / BullMQ
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
BULLMQ_PREFIX=tradeos
BULLMQ_CONCURRENCY=5

# Farcaster
FARCASTER_API_KEY=your_farcaster_api_key
FARCASTER_HUB_URL=https://hub.farcaster.xyz
FARCASTER_APP_FID=

# Security
JWT_SECRET=replace_with_long_random_value
API_RATE_LIMIT_PER_MINUTE=120
9 changes: 9 additions & 0 deletions packages/engine/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "tradeos-engine"
version = "0.1.0"
edition = "2021"

[lib]
path = "src/lib.rs"

[dependencies]
Comment on lines +1 to +9
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new Rust crate adds logic + unit tests, but the current repo CI (and root package.json scripts) don't appear to run cargo test or even compile packages/engine. As-is, the crate can break without CI noticing. Consider adding a CI step (or a root script) that at least runs cargo test --manifest-path packages/engine/Cargo.toml so the scaffold stays buildable.

Copilot uses AI. Check for mistakes.
3 changes: 3 additions & 0 deletions packages/engine/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod tpl_core;

pub use tpl_core::{parse_line, TplCommand, TplRuntime};
101 changes: 101 additions & 0 deletions packages/engine/src/tpl_core.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use std::collections::HashMap;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TplCommand {
Set(String, String),
AssertEq(String, String),
}

#[derive(Debug, Default)]
pub struct TplRuntime {
state: HashMap<String, String>,
}

impl TplRuntime {
pub fn new() -> Self {
Self::default()
}

pub fn execute(&mut self, command: TplCommand) -> Result<(), String> {
match command {
TplCommand::Set(key, value) => {
self.state.insert(key, value);
Ok(())
}
TplCommand::AssertEq(key, expected) => {
let actual = self
.state
.get(&key)
.ok_or_else(|| format!("missing key: {}", key))?;

if actual == &expected {
Ok(())
} else {
Err(format!(
"assertion failed for {}: expected '{}' but got '{}'",
key, expected, actual
))
}
}
}
}
}

pub fn parse_line(line: &str) -> Result<TplCommand, String> {
let parts: Vec<&str> = line.split_whitespace().collect();

match parts.as_slice() {
["SET", key, value] => Ok(TplCommand::Set((*key).to_string(), (*value).to_string())),
["ASSERT_EQ", key, value] => {
Ok(TplCommand::AssertEq((*key).to_string(), (*value).to_string()))
}
_ => Err(format!("invalid TPL line: {}", line)),
}
}

#[cfg(test)]
mod tests {
use super::{parse_line, TplCommand, TplRuntime};

#[test]
fn parse_line_handles_valid_and_invalid_input() {
assert_eq!(
parse_line("SET mode active").expect("SET should parse"),
TplCommand::Set("mode".to_string(), "active".to_string())
);

assert_eq!(
parse_line("ASSERT_EQ mode active").expect("ASSERT_EQ should parse"),
TplCommand::AssertEq("mode".to_string(), "active".to_string())
);

assert!(parse_line("").is_err());
assert!(parse_line("UNKNOWN a b").is_err());
assert!(parse_line("SET only_key").is_err());
}

#[test]
fn execute_returns_expected_errors() {
let mut runtime = TplRuntime::new();

let missing = runtime
.execute(TplCommand::AssertEq(
"missing".to_string(),
"value".to_string(),
))
.expect_err("missing key should fail");
assert!(missing.contains("missing key"));

runtime
.execute(TplCommand::Set("mode".to_string(), "active".to_string()))
.expect("set should succeed");

let mismatch = runtime
.execute(TplCommand::AssertEq(
"mode".to_string(),
"inactive".to_string(),
))
.expect_err("mismatched values should fail");
assert!(mismatch.contains("expected 'inactive' but got 'active'"));
}
}
87 changes: 87 additions & 0 deletions scripts/Bootstrap.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
Param(
[switch]$SkipBuild
)

$ErrorActionPreference = 'Stop'

function Assert-Command {
param([string]$Name)

if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
throw "Required command '$Name' is not available in PATH."
}
}

function Ensure-SecureJwtSecret {
param([string]$EnvPath)

if (-not (Test-Path $EnvPath)) {
return
}

$content = Get-Content $EnvPath -Raw
$pattern = '(?m)^JWT_SECRET=(.*)$'
$match = [regex]::Match($content, $pattern)

if (-not $match.Success) {
return
}

$current = $match.Groups[1].Value.Trim()
if ($current -and $current -notmatch '(?i)replace_with|your_|changeme|placeholder') {
return
}

$bytes = New-Object byte[] 48
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
$secret = [Convert]::ToBase64String($bytes)
$updated = [regex]::Replace($content, $pattern, "JWT_SECRET=$secret", 1)
Set-Content -Path $EnvPath -Value $updated -NoNewline
Write-Host "[TradeOS Bootstrap] Generated secure JWT_SECRET in $EnvPath"
}

Write-Host '[TradeOS Bootstrap] Validating environment...'
Assert-Command -Name 'node'
Assert-Command -Name 'npm'

Comment on lines +43 to +46
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script advertises environment/tool validation, but it only checks that node/npm exist. Since the repo enforces engines (node >=24, npm >=10), consider validating the versions up-front and failing with a clear message if they don't satisfy the minimums (to avoid confusing npm ci/build failures later).

Copilot uses AI. Check for mistakes.
$root = Split-Path -Parent $PSScriptRoot
Set-Location $root

Write-Host '[TradeOS Bootstrap] Installing root dependencies...'
npm ci --no-audit --no-fund

if (Test-Path "$root/webapp/package.json") {
Write-Host '[TradeOS Bootstrap] Installing webapp dependencies...'
Push-Location "$root/webapp"
npm ci --no-audit --no-fund
Pop-Location
}

if (Test-Path "$root/apps/api/package.json") {
Write-Host '[TradeOS Bootstrap] Installing API dependencies...'
Push-Location "$root/apps/api"
npm ci --no-audit --no-fund
Pop-Location
}

if (-not (Test-Path "$root/.env") -and (Test-Path "$root/.env.example")) {
Copy-Item "$root/.env.example" "$root/.env"
Write-Host '[TradeOS Bootstrap] Created root .env from .env.example'
Ensure-SecureJwtSecret -EnvPath "$root/.env"
}

if (-not (Test-Path "$root/apps/api/.env") -and (Test-Path "$root/apps/api/.env.example")) {
Copy-Item "$root/apps/api/.env.example" "$root/apps/api/.env"
Write-Host '[TradeOS Bootstrap] Created apps/api .env from .env.example'
Comment on lines +67 to +75
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bootstrap currently auto-copies .env.example.env (and apps/api/.env.example.env) verbatim. Since the templates include placeholder secrets (e.g., JWT_SECRET), this makes it easy to accidentally run a deployment with insecure defaults. Consider either (1) generating strong random values for secret fields when creating the .env, or (2) printing a clear, high-visibility warning and aborting until the user confirms they’ve updated secrets.

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +75
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure-SecureJwtSecret is only invoked when a new .env file is created from an example. If a developer already has an existing .env with a placeholder JWT_SECRET, the bootstrap run will leave the insecure placeholder in place. Consider calling Ensure-SecureJwtSecret unconditionally when the env file exists (it already no-ops when the secret looks non-placeholder), so placeholder values are hardened even on re-runs.

Suggested change
Write-Host '[TradeOS Bootstrap] Created root .env from .env.example'
Ensure-SecureJwtSecret -EnvPath "$root/.env"
}
if (-not (Test-Path "$root/apps/api/.env") -and (Test-Path "$root/apps/api/.env.example")) {
Copy-Item "$root/apps/api/.env.example" "$root/apps/api/.env"
Write-Host '[TradeOS Bootstrap] Created apps/api .env from .env.example'
Write-Host '[TradeOS Bootstrap] Created root .env from .env.example'
}
if (Test-Path "$root/.env") {
Ensure-SecureJwtSecret -EnvPath "$root/.env"
}
if (-not (Test-Path "$root/apps/api/.env") -and (Test-Path "$root/apps/api/.env.example")) {
Copy-Item "$root/apps/api/.env.example" "$root/apps/api/.env"
Write-Host '[TradeOS Bootstrap] Created apps/api .env from .env.example'
}
if (Test-Path "$root/apps/api/.env") {

Copilot uses AI. Check for mistakes.
Ensure-SecureJwtSecret -EnvPath "$root/apps/api/.env"
}

if (-not $SkipBuild) {
Write-Host '[TradeOS Bootstrap] Building project...'
npm run build
}

Write-Host '[TradeOS Bootstrap] Next steps:'
Write-Host ' - Backend: npm run start:server'
Write-Host ' - Webapp : npm run start:webapp'
Write-Host '[TradeOS Bootstrap] Complete.'
3 changes: 3 additions & 0 deletions tests/integration/swarm.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
describe.skip('agentic swarm workflow', () => {
it.todo('initializes agents, delegates tasks, and aggregates results using the production swarm/agent implementation');
});
12 changes: 5 additions & 7 deletions webapp/lib/wallet-context-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ import {
} from "@solana/wallet-adapter-react";
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
import {
PhantomWalletAdapter,
SolflareWalletAdapter,
LedgerWalletAdapter,
TorusWalletAdapter,
CoinbaseWalletAdapter,
} from "@solana/wallet-adapter-wallets";
import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom";
import { SolflareWalletAdapter } from "@solana/wallet-adapter-solflare";
import { LedgerWalletAdapter } from "@solana/wallet-adapter-ledger";
import { TorusWalletAdapter } from "@solana/wallet-adapter-torus";
import { CoinbaseWalletAdapter } from "@solana/wallet-adapter-coinbase";
import { clusterApiUrl } from "@solana/web3.js";
import { useWallet as useWalletHook } from "@solana/wallet-adapter-react";
import "@solana/wallet-adapter-react-ui/styles.css";
Expand Down
Loading
Loading