diff --git a/.github/workflows/aws.yml b/.github/workflows/aws.yml new file mode 100644 index 0000000..3ad415f --- /dev/null +++ b/.github/workflows/aws.yml @@ -0,0 +1,94 @@ +# This workflow will build and push a new container image to Amazon ECR, +# and then will deploy a new task definition to Amazon ECS, when there is a push to the "master" branch. +# +# To use this workflow, you will need to complete the following set-up steps: +# +# 1. Create an ECR repository to store your images. +# For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`. +# Replace the value of the `ECR_REPOSITORY` environment variable in the workflow below with your repository's name. +# Replace the value of the `AWS_REGION` environment variable in the workflow below with your repository's region. +# +# 2. Create an ECS task definition, an ECS cluster, and an ECS service. +# For example, follow the Getting Started guide on the ECS console: +# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun +# Replace the value of the `ECS_SERVICE` environment variable in the workflow below with the name you set for the Amazon ECS service. +# Replace the value of the `ECS_CLUSTER` environment variable in the workflow below with the name you set for the cluster. +# +# 3. Store your ECS task definition as a JSON file in your repository. +# The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`. +# Replace the value of the `ECS_TASK_DEFINITION` environment variable in the workflow below with the path to the JSON file. +# Replace the value of the `CONTAINER_NAME` environment variable in the workflow below with the name of the container +# in the `containerDefinitions` section of the task definition. +# +# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. +# See the documentation for each action used below for the recommended IAM policies for this IAM user, +# and best practices on handling the access key credentials. + +name: Deploy to Amazon ECS + +on: + push: + branches: [ "master" ] + +env: + AWS_REGION: MY_AWS_REGION # set this to your preferred AWS region, e.g. us-west-1 + ECR_REPOSITORY: MY_ECR_REPOSITORY # set this to your Amazon ECR repository name + ECS_SERVICE: MY_ECS_SERVICE # set this to your Amazon ECS service name + ECS_CLUSTER: MY_ECS_CLUSTER # set this to your Amazon ECS cluster name + ECS_TASK_DEFINITION: MY_ECS_TASK_DEFINITION # set this to the path to your Amazon ECS task definition + # file, e.g. .aws/task-definition.json + CONTAINER_NAME: MY_CONTAINER_NAME # set this to the name of the container in the + # containerDefinitions section of your task definition + +permissions: + contents: read + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + environment: production + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + IMAGE_TAG: ${{ github.sha }} + run: | + # Build a docker container and + # push it to ECR so that it can + # be deployed to ECS. + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + + - name: Fill in the new image ID in the Amazon ECS task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@v1 + with: + task-definition: ${{ env.ECS_TASK_DEFINITION }} + container-name: ${{ env.CONTAINER_NAME }} + image: ${{ steps.build-image.outputs.image }} + + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@v1 + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: ${{ env.ECS_SERVICE }} + cluster: ${{ env.ECS_CLUSTER }} + wait-for-service-stability: true diff --git a/halo.js b/halo.js new file mode 100644 index 0000000..3a73944 --- /dev/null +++ b/halo.js @@ -0,0 +1,188 @@ +// HALO Meta-Oracle skeleton script. +// This script defines the HALO core engine (dice roll), plugin hooks, and local storage. +// The detailed symbol definitions (Tarot cards, runes, I Ching, etc.) and deeper synthesis logic +// should be filled in later. + +(function() { + const STORAGE_KEY_PROFILE = 'halo_profile_v1'; + const STORAGE_KEY_READINGS = 'halo_meta_readings_v1'; + // Replace the placeholder with your actual API Gateway invoke URL once deployed. + const API_URL = 'https://YOUR_API_ID.execute-api.YOUR_REGION.amazonaws.com/readings'; + + // Axis definitions + const AXES = ['Body & Hardware','Mind & Narrative','Heart & Relationships','Domain & Magic']; + const VECTORS = ['Ingress','Grow','Stabilize','Release','Transmute','Witness']; + const TIMELINES = [ + 'Right now','Today','This week','This month','This season','This year', + '1–3 years','3–7 years','7–20 years','Lifetime','Generational','Meta' + ]; + const ARCHETYPES = [ + 'The Fool','The Magician','The Healer','The Warrior','The Shield','The Hermit','The Lover', + 'The Trickster','The Phoenix','The Architect','The Bridge','The Teacher','The Explorer', + 'The Mirror','The Guardian','The Key','The Storm','The Weaver','The Sovereign' + ]; + + // Basic utility to roll a die + function rollDie(sides) { + return Math.floor(Math.random() * sides) + 1; + } + + // Load/save profile + function loadProfile() { + try { + return JSON.parse(localStorage.getItem(STORAGE_KEY_PROFILE)) || {}; + } catch (err) { + return {}; + } + } + + function saveProfile(profile) { + localStorage.setItem(STORAGE_KEY_PROFILE, JSON.stringify(profile)); + } + + // Load/save readings + function loadReadings() { + try { + return JSON.parse(localStorage.getItem(STORAGE_KEY_READINGS)) || []; + } catch (err) { + return []; + } + } + + function saveReadings(readings) { + localStorage.setItem(STORAGE_KEY_READINGS, JSON.stringify(readings)); + } + + // Fire-and-forget sync to server if API_URL is set. + async function syncReadingToServer(reading) { + if (!API_URL || API_URL.includes('YOUR_API_ID')) { + return; + } + try { + const payload = { + userId: reading.user?.name || 'anonymous', + reading + }; + await fetch(API_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + } catch (err) { + console.warn('Sync failed', err); + } + } + + // Create a new reading + function createReading(question, profile) { + const timestamp = new Date().toISOString(); + const d4 = rollDie(4); + const d6 = rollDie(6); + const d12 = rollDie(12); + const d20 = rollDie(20); + + const reading = { + id: `${Date.now().toString(36)}-${Math.random().toString(16).slice(2,8)}`, + question: question || '', + created_at: timestamp, + halo: { + axis: d4, + vector: d6, + timeline: d12, + archetype: d20, + dice: { d4, d6, d12, d20 } + }, + user: profile, + // plugin results can be added here in the future + }; + + const readings = loadReadings(); + readings.push(reading); + saveReadings(readings); + syncReadingToServer(reading); + return reading; + } + + // Render functions + function renderHistory() { + const historyEl = document.getElementById('history'); + const readings = loadReadings(); + if (!readings.length) { + historyEl.innerHTML = '

No readings yet.

'; + return; + } + const rows = readings.slice().reverse().map(reading => { + const date = new Date(reading.created_at).toLocaleString(); + const axis = AXES[(reading.halo.axis - 1) % AXES.length]; + const vector = VECTORS[(reading.halo.vector - 1) % VECTORS.length]; + const timeline = TIMELINES[(reading.halo.timeline - 1) % TIMELINES.length]; + const archetype = ARCHETYPES[(reading.halo.archetype - 1) % ARCHETYPES.length]; + return ` + + ${date} + ${axis} + ${vector} + ${timeline} + ${archetype} + + `; + }).join(''); + historyEl.innerHTML = ` + + + + + ${rows} +
WhenAxisVectorTimelineArchetype
+ `; + } + + function renderCurrentReading(reading) { + const container = document.getElementById('current-reading'); + if (!reading) { + container.innerHTML = ''; + return; + } + const halo = reading.halo; + container.innerHTML = ` +

HALO Roll

+

Axis: ${AXES[(halo.axis - 1) % AXES.length]} (${halo.dice.d4})

+

Vector: ${VECTORS[(halo.vector - 1) % VECTORS.length]} (${halo.dice.d6})

+

Timeline: ${TIMELINES[(halo.timeline - 1) % TIMELINES.length]} (${halo.dice.d12})

+

Archetype: ${ARCHETYPES[(halo.archetype - 1) % ARCHETYPES.length]} (${halo.dice.d20})

+

This is a bare-bones skeleton. Interpretations and plugin hooks go here.

+ `; + } + + // Initialize UI + function init() { + const profile = loadProfile(); + document.getElementById('profile-name').value = profile.name || ''; + document.getElementById('profile-sun').value = profile.sun || ''; + document.getElementById('profile-moon').value = profile.moon || ''; + document.getElementById('profile-rising').value = profile.rising || ''; + + document.getElementById('save-profile').addEventListener('click', () => { + const p = { + name: document.getElementById('profile-name').value.trim(), + sun: document.getElementById('profile-sun').value.trim(), + moon: document.getElementById('profile-moon').value.trim(), + rising: document.getElementById('profile-rising').value.trim() + }; + saveProfile(p); + }); + + document.getElementById('roll-btn').addEventListener('click', () => { + const q = document.getElementById('question').value.trim(); + const p = loadProfile(); + const r = createReading(q, p); + renderCurrentReading(r); + renderHistory(); + }); + + renderHistory(); + } + + // run + init(); +})(); \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..1cedf32 --- /dev/null +++ b/index.html @@ -0,0 +1,243 @@ + + + + + + HALO Meta-Oracle – Local Prototype + + + +
+

HALO Meta-Oracle – Local Prototype

+ +
+

1. Profile

+

Optional but helpful—used for tags and flavor text.

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + Stored only in this browser using localStorage. +
+ +
+

2. Ask HALO

+ + + +
+
+ +
+

3. Past Readings (this device)

+

+ Local Spiritual Blockchain prototype – readings are chained with simple hashes. +

+
+
+
+ + + + diff --git a/web/halo (3).js b/web/halo (3).js new file mode 100644 index 0000000..ffe1758 --- /dev/null +++ b/web/halo (3).js @@ -0,0 +1,188 @@ +// HALO Meta-Oracle skeleton script. +// This script defines the HALO core engine (dice roll), plugin hooks, and local storage. +// The detailed symbol definitions (Tarot cards, runes, I Ching, etc.) and deeper synthesis logic +// should be filled in later. + +(function() { + const STORAGE_KEY_PROFILE = 'halo_profile_v1'; + const STORAGE_KEY_READINGS = 'halo_meta_readings_v1'; + // Replace the placeholder with your actual API Gateway invoke URL once deployed. + const API_URL = 'https://YOUR_API_ID.execute-api.YOUR_REGION.amazonaws.com/readings'; + + // Axis definitions + const AXES = ['Body & Hardware','Mind & Narrative','Heart & Relationships','Domain & Magic']; + const VECTORS = ['Ingress','Grow','Stabilize','Release','Transmute','Witness']; + const TIMELINES = [ + 'Right now','Today','This week','This month','This season','This year', + '1–3 years','3–7 years','7–20 years','Lifetime','Generational','Meta' + ]; + const ARCHETYPES = [ + 'The Fool','The Magician','The Healer','The Warrior','The Shield','The Hermit','The Lover', + 'The Trickster','The Phoenix','The Architect','The Bridge','The Teacher','The Explorer', + 'The Mirror','The Guardian','The Key','The Storm','The Weaver','The Sovereign' + ]; + + // Basic utility to roll a die + function rollDie(sides) { + return Math.floor(Math.random() * sides) + 1; + } + + // Load/save profile + function loadProfile() { + try { + return JSON.parse(localStorage.getItem(STORAGE_KEY_PROFILE)) || {}; + } catch (err) { + return {}; + } + } + + function saveProfile(profile) { + localStorage.setItem(STORAGE_KEY_PROFILE, JSON.stringify(profile)); + } + + // Load/save readings + function loadReadings() { + try { + return JSON.parse(localStorage.getItem(STORAGE_KEY_READINGS)) || []; + } catch (err) { + return []; + } + } + + function saveReadings(readings) { + localStorage.setItem(STORAGE_KEY_READINGS, JSON.stringify(readings)); + } + + // Fire-and-forget sync to server if API_URL is set. + async function syncReadingToServer(reading) { + if (!API_URL || API_URL.includes('YOUR_API_ID')) { + return; + } + try { + const payload = { + userId: reading.user?.name || 'anonymous', + reading + }; + await fetch(API_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + } catch (err) { + console.warn('Sync failed', err); + } + } + + // Create a new reading + function createReading(question, profile) { + const timestamp = new Date().toISOString(); + const d4 = rollDie(4); + const d6 = rollDie(6); + const d12 = rollDie(12); + const d20 = rollDie(20); + + const reading = { + id: `${Date.now().toString(36)}-${Math.random().toString(16).slice(2,8)}`, + question: question || '', + created_at: timestamp, + halo: { + axis: d4, + vector: d6, + timeline: d12, + archetype: d20, + dice: { d4, d6, d12, d20 } + }, + user: profile, + // plugin results can be added here in the future + }; + + const readings = loadReadings(); + readings.push(reading); + saveReadings(readings); + syncReadingToServer(reading); + return reading; + } + + // Render functions + function renderHistory() { + const historyEl = document.getElementById('history'); + const readings = loadReadings(); + if (!readings.length) { + historyEl.innerHTML = '

No readings yet.

'; + return; + } + const rows = readings.slice().reverse().map(reading => { + const date = new Date(reading.created_at).toLocaleString(); + const axis = AXES[(reading.halo.axis - 1) % AXES.length]; + const vector = VECTORS[(reading.halo.vector - 1) % VECTORS.length]; + const timeline = TIMELINES[(reading.halo.timeline - 1) % TIMELINES.length]; + const archetype = ARCHETYPES[(reading.halo.archetype - 1) % ARCHETYPES.length]; + return ` + + ${date} + ${axis} + ${vector} + ${timeline} + ${archetype} + + `; + }).join(''); + historyEl.innerHTML = ` + + + + + ${rows} +
WhenAxisVectorTimelineArchetype
+ `; + } + + function renderCurrentReading(reading) { + const container = document.getElementById('current-reading'); + if (!reading) { + container.innerHTML = ''; + return; + } + const halo = reading.halo; + container.innerHTML = ` +

HALO Roll

+

Axis: ${AXES[(halo.axis - 1) % AXES.length]} (${halo.dice.d4})

+

Vector: ${VECTORS[(halo.vector - 1) % VECTORS.length]} (${halo.dice.d6})

+

Timeline: ${TIMELINES[(halo.timeline - 1) % TIMELINES.length]} (${halo.dice.d12})

+

Archetype: ${ARCHETYPES[(halo.archetype - 1) % ARCHETYPES.length]} (${halo.dice.d20})

+

This is a bare-bones skeleton. Interpretations and plugin hooks go here.

+ `; + } + + // Initialize UI + function init() { + const profile = loadProfile(); + document.getElementById('profile-name').value = profile.name || ''; + document.getElementById('profile-sun').value = profile.sun || ''; + document.getElementById('profile-moon').value = profile.moon || ''; + document.getElementById('profile-rising').value = profile.rising || ''; + + document.getElementById('save-profile').addEventListener('click', () => { + const p = { + name: document.getElementById('profile-name').value.trim(), + sun: document.getElementById('profile-sun').value.trim(), + moon: document.getElementById('profile-moon').value.trim(), + rising: document.getElementById('profile-rising').value.trim() + }; + saveProfile(p); + }); + + document.getElementById('roll-btn').addEventListener('click', () => { + const q = document.getElementById('question').value.trim(); + const p = loadProfile(); + const r = createReading(q, p); + renderCurrentReading(r); + renderHistory(); + }); + + renderHistory(); + } + + // run + init(); +})(); diff --git a/web/index (3).html b/web/index (3).html new file mode 100644 index 0000000..1cedf32 --- /dev/null +++ b/web/index (3).html @@ -0,0 +1,243 @@ + + + + + + HALO Meta-Oracle – Local Prototype + + + +
+

HALO Meta-Oracle – Local Prototype

+ +
+

1. Profile

+

Optional but helpful—used for tags and flavor text.

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + Stored only in this browser using localStorage. +
+ +
+

2. Ask HALO

+ + + +
+
+ +
+

3. Past Readings (this device)

+

+ Local Spiritual Blockchain prototype – readings are chained with simple hashes. +

+
+
+
+ + + +