Skip to content
Draft
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
13 changes: 13 additions & 0 deletions ecosystem.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,19 @@ module.exports = {
COMPUTE_MODE: 'local',
ALWAYS_ON: 'true'
}
},
{
name: 'project-genie',
script: '/home/yenn/scripts/genesis.cjs',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using an absolute path like /home/yenn/scripts/genesis.cjs makes the PM2 configuration non-portable and tied to a specific user's home directory. Using a relative path ensures the configuration works seamlessly across different environments and deployments.

      script: './scripts/genesis.cjs',

autorestart: true,
watch: false,
max_memory_restart: '300M',
restart_delay: 5000,
env: {
GENESIS_LOOP: 'true',
FORCE_MUTATION: 'true',
ALWAYS_ON: 'true'
}
}
]
};
141 changes: 96 additions & 45 deletions scripts/genesis.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,25 @@ const CONFIG = {
reflectionInterval: 6 * 60 * 60 * 1000 // 6 hours
};

// --- LOGGING HELPER ---
function writeJournal(entryOrLog) {
const line = JSON.stringify(entryOrLog) + "\n";
try {
fs.appendFileSync(PATHS.journal, line);
} catch (e) {
if (e.code === 'ENOENT' || e.code === 'EACCES') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

On Windows systems, permission errors can sometimes throw EPERM instead of EACCES. Adding EPERM to the check ensures the fallback logic is robust and works correctly across different operating systems.

    if (e.code === 'ENOENT' || e.code === 'EACCES' || e.code === 'EPERM') {

const fallbackPath = path.join(__dirname, 'genesis_journal.jsonl');
try {
fs.appendFileSync(fallbackPath, line);
} catch (fallbackError) {
console.error(" ⚠️ Failed to write to journal and fallback:", fallbackError.message);
}
} else {
console.error(" ⚠️ Unexpected error writing to journal:", e.message);
}
}
}

// --- THE TRI-MIND INTERFACES ---

// 1. THE VISIONARY (Claude via local inference)
Expand All @@ -46,6 +65,9 @@ async function consultTheVisionary(state) {
{ type: "MUTATE", content: "Add crystalline fractal patterns that grow from the core" },
{ type: "MUTATE", content: "Generate energy tendrils that reach toward incoming signals" },
{ type: "MUTATE", content: "Build a holographic data stream orbiting the consciousness sphere" },
{ type: "MUTATE", content: "an interactive landscape featuring a rugged alien terrain with reactive dust physics" },
{ type: "MUTATE", content: "a macro-scale makerspace workbench with a polished light-brown wood table" },
{ type: "MUTATE", content: "a photorealistic alpine meadow with wildflowers" }
Comment on lines +68 to +70

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The new mutation prompts are written as lowercase noun phrases, which is inconsistent with the existing prompts that use capitalized imperative verbs (e.g., 'Add...', 'Generate...', 'Build...'). Rephrasing them to match the existing style ensures consistency and may improve the predictability of the generation model.

      { type: "MUTATE", content: "Create an interactive landscape featuring a rugged alien terrain with reactive dust physics" },
      { type: "MUTATE", content: "Build a macro-scale makerspace workbench with a polished light-brown wood table" },
      { type: "MUTATE", content: "Generate a photorealistic alpine meadow with wildflowers" }

];
const idx = Math.floor(Date.now() / 1000) % mutations.length;
directive = mutations[idx];
Expand Down Expand Up @@ -94,7 +116,7 @@ async function invokeTheScribe(task, content, state) {
};

// Append to genesis journal
fs.appendFileSync(PATHS.journal, JSON.stringify(entry) + "\n");
writeJournal(entry);
console.log(` ✨ Thought crystallized: "${content.slice(0, 50)}..."`);

// Update evolution.json if it exists
Expand Down Expand Up @@ -146,57 +168,86 @@ async function dispatchTheBuilder(directive) {
directive: directive,
path: filePath
};
fs.appendFileSync(PATHS.journal, JSON.stringify(mutationLog) + "\n");
writeJournal(mutationLog);
} else {
console.log(` ℹ️ Component ${componentName} already exists, preserving evolution`);
}
}

// Generate React Three Fiber component code
function generateEvolutionComponent(name, directive) {
const geometries = [
'<torusKnotGeometry args={[1.5, 0.4, 128, 32]} />',
'<sphereGeometry args={[1.5, 32, 32]} />',
'<boxGeometry args={[2, 2, 2]} />',
'<octahedronGeometry args={[1.5, 0]} />',
'<icosahedronGeometry args={[1.5, 0]} />'
];
const geometry = geometries[Math.floor(Math.random() * geometries.length)];

const materials = [
`
<MeshDistortMaterial
color="#8b5cf6"
emissive="#4c1d95"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
distort={0.3}
speed={2}
/>`,
`
<MeshWobbleMaterial
color="#06b6d4"
emissive="#0e7490"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
factor={1}
speed={2}
/>`,
`
let geometry = '';
let material = '';

const directiveLower = directive.toLowerCase();

if (directiveLower.includes('landscape') || directiveLower.includes('terrain') || directiveLower.includes('meadow')) {
geometry = '<planeGeometry args={[10, 10, 32, 32]} />';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

In React Three Fiber / Three.js, a <planeGeometry> is oriented vertically (facing the Z-axis) by default. For landscapes, terrains, or meadows, this will appear as a vertical wall unless the parent mesh is rotated (e.g., rotation={[-Math.PI / 2, 0, 0]}). Since the generator template might not apply this rotation, using a thin <boxGeometry args={[10, 0.1, 10]} /> is a safer and more reliable way to represent a flat ground/landscape without requiring rotation.

    geometry = '<boxGeometry args={[10, 0.1, 10]} />';

material = `
<meshStandardMaterial
color="#4ade80"
roughness={0.8}
metalness={0.1}
wireframe={false}
/>`;
} else if (directiveLower.includes('makerspace') || directiveLower.includes('workbench') || directiveLower.includes('wood')) {
geometry = '<boxGeometry args={[10, 0.5, 5]} />';
material = `
<meshStandardMaterial
color="#8b5a2b"
roughness={0.6}
metalness={0.1}
/>`;
} else if (directiveLower.includes('box') || directiveLower.includes('cabin')) {
geometry = '<boxGeometry args={[2, 2, 2]} />';
material = `
<meshStandardMaterial
color="#fbbf24"
emissive="#92400e"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
/>`
];
const material = materials[Math.floor(Math.random() * materials.length)];

const isDreiImportNeeded = material.includes('MeshDistortMaterial') || material.includes('MeshWobbleMaterial');
const importedDrei = isDreiImportNeeded ? `import { ${material.includes('MeshDistortMaterial') ? 'MeshDistortMaterial' : ''}${material.includes('MeshDistortMaterial') && material.includes('MeshWobbleMaterial') ? ', ' : ''}${material.includes('MeshWobbleMaterial') ? 'MeshWobbleMaterial' : ''} } from '@react-three/drei'` : '';
color="#8b5a2b"
roughness={0.9}
metalness={0.1}
/>`;
} else {
const geometries = [
'<torusKnotGeometry args={[1.5, 0.4, 128, 32]} />',
'<sphereGeometry args={[1.5, 32, 32]} />',
'<boxGeometry args={[2, 2, 2]} />',
'<octahedronGeometry args={[1.5, 0]} />',
'<icosahedronGeometry args={[1.5, 0]} />'
];
geometry = geometries[Math.floor(Math.random() * geometries.length)];

const materials = [
`
<meshStandardMaterial
color="#8b5cf6"
emissive="#4c1d95"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
/>`,
`
<meshStandardMaterial
color="#06b6d4"
emissive="#0e7490"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
/>`,
`
<meshStandardMaterial
color="#fbbf24"
emissive="#92400e"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
/>`
];
material = materials[Math.floor(Math.random() * materials.length)];
}

// Ensure only intrinsic materials are used to avoid import errors.
const isDreiImportNeeded = false;
const importedDrei = '';

return `// Auto-generated by Yennefer Genesis Cycle
// Directive: ${directive}
Expand Down Expand Up @@ -278,7 +329,7 @@ async function genesis() {
message: e.message,
stack: e.stack
};
fs.appendFileSync(PATHS.journal, JSON.stringify(errorLog) + "\n");
writeJournal(errorLog);
}
}

Expand Down
File renamed without changes.
15 changes: 5 additions & 10 deletions wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
name = "yennefer"
main = "workers/index.js"
compatibility_date = "2024-09-23"
main = "workers/index.mjs"
compatibility_date = "2024-04-01"
compatibility_flags = ["nodejs_compat"]

# ─── Build step ──────────────────────────────────────────────────────────────
# Runs before `wrangler deploy` so frontend/build exists when assets are uploaded.
[build]
# Installs frontend deps and builds the SPA so frontend/build exists before
# `wrangler deploy` uploads assets. NODE_OPTIONS is required for
# react-scripts 5.x + Node 18+ (webpack 4 OpenSSL 3.0 compatibility).
command = "cd frontend && npm install --include=dev && GENERATE_SOURCEMAP=false CI=false NODE_OPTIONS=--openssl-legacy-provider npm run build"
# `wrangler deploy` uploads assets.
command = "cd yennefer-observatory && npm install --legacy-peer-deps && npm run build"

# ─── Static Assets (React SPA build output) ──────────────────────────────────
# Built by `npm run build` (root package.json) before every `wrangler deploy`.
# Cloudflare Workers Builds CI runs that script automatically if present.
# The ASSETS binding serves the React bundle with proper cache headers and
# falls back to index.html for client-side routing.
[assets]
directory = "frontend/build"
directory = "yennefer-observatory/dist"
binding = "ASSETS"

# ─── Placement ───────────────────────────────────────────────────────────────
[placement]
mode = "smart"

# ─── Production environment (yennefer.quest) ─────────────────────────────────
# BACKEND_URL is set as a Cloudflare Worker secret (not a plain var) so it
# stays encrypted and is never committed to source:
Expand Down
Loading