Initial commit
This commit is contained in:
commit
32839ef049
|
|
@ -0,0 +1,5 @@
|
|||
node_modules/
|
||||
.env
|
||||
*.log
|
||||
.DS_Store
|
||||
|
||||
Binary file not shown.
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"timestamp": "2026-01-28T23:30:00Z",
|
||||
"type": "evening",
|
||||
"markets": {
|
||||
"indices": [
|
||||
{ "symbol": "S&P 500", "value": "7,000", "change": "+0.3%", "direction": "up", "note": "Historic first!" },
|
||||
{ "symbol": "NASDAQ", "value": "23,857", "change": "+0.17%", "direction": "up" },
|
||||
{ "symbol": "NVDA", "value": "+1.5%", "change": "up", "direction": "up", "note": "China AI chip hopes" }
|
||||
],
|
||||
"stocks": [],
|
||||
"commodities": [],
|
||||
"forex": []
|
||||
},
|
||||
"weather": {
|
||||
"status": "clear",
|
||||
"headline": "St. Pete — Clear",
|
||||
"temp": "43°F – 71°F",
|
||||
"details": "No tropical threats. Hurricane season starts June. Thunderstorms in Panhandle — you're clear.",
|
||||
"alerts": []
|
||||
},
|
||||
"techNews": [
|
||||
{
|
||||
"headline": "🦞 Clawdbot → Moltbot (Rename)",
|
||||
"source": "Forbes / The Register",
|
||||
"summary": "Anthropic asked for name change (trademark). Now 'Moltbot' + 'Molty'. Same lobster soul, new shell. Security vuln (proxy misconfig) also fixed."
|
||||
},
|
||||
{
|
||||
"headline": "Claude = Best AI Coding Assistant",
|
||||
"source": "Wikipedia",
|
||||
"summary": "As of Jan 2026, widely considered #1 for coding (with GPT-5.2 close). Went viral over winter holidays."
|
||||
},
|
||||
{
|
||||
"headline": "DeepSeek: Manifold-Constrained Hyper-Connections",
|
||||
"source": "Times of India",
|
||||
"summary": "New training method scales AI without ballooning compute. Direct challenge to 'bigger = smarter' consensus."
|
||||
},
|
||||
{
|
||||
"headline": "Chinese AI Models Hit 15% Global Share",
|
||||
"source": "TrendForce",
|
||||
"summary": "DeepSeek + Alibaba Qwen driving adoption. R1 model (Jan 2025) still disrupting with high performance at low cost."
|
||||
},
|
||||
{
|
||||
"headline": "Anthropic: 'Wake Up to AI Risks'",
|
||||
"source": "The Guardian",
|
||||
"summary": "Dario Amodei warns humanity entering phase that will 'test who we are as a species'. World needs to wake up."
|
||||
},
|
||||
{
|
||||
"headline": "ServiceNow + Anthropic Partnership",
|
||||
"source": "Morningstar",
|
||||
"summary": "Claude is now default model powering ServiceNow Build Agent. Enterprise AI push."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"briefings": [
|
||||
{
|
||||
"id": "172a3946",
|
||||
"date": "2025-01-28",
|
||||
"title": "Afternoon Brief - Jan 28",
|
||||
"weather": "St. Petersburg FL — mild evening",
|
||||
"markets": "Not checked this cycle",
|
||||
"news": "ClawdNode gateway working, notifications + calls flowing",
|
||||
"tasks": "✅ Git user hardened on Zurich VPS\n✅ ClawdNode v0.1 built (10 Kotlin files)\n✅ Gateway protocol fix\n✅ Memory review system\n✅ Claude Code updated (2.1.20 → 2.1.22)\n🔲 ClawdNode: rebuild + test\n🔲 OpenVAS vuln scan\n🔲 Uptime Kuma setup",
|
||||
"summary": "ClawdNode breakthrough — notifications and calls flowing through custom gateway. Phone answered command OK (TelecomManager implementation pending). Flutter styleguide blocking deployment — priority for 7pm sprint.",
|
||||
"created_at": "2026-01-28T22:41:56.65781346Z"
|
||||
},
|
||||
{
|
||||
"id": "f5c12407",
|
||||
"date": "2026-01-29",
|
||||
"title": "Morning Brief - Jan 29, 2026",
|
||||
"weather": "St. Petersburg FL: ☀️ Clear, 47°F now. High 64°F / Low 39°F. No rain expected.",
|
||||
"markets": "/ES 7,023 (+0.2%) | /CL $62.76 (+0.6%) | Gold $5,550 (+3.9% 🚀) | TSLA $431 (+0.1%, pre-mkt +1.8%) | NABL ~$7.50",
|
||||
"news": "• Anthropic revises Claude constitution, hints at AI consciousness (Fortune/TechCrunch)\n• Anthropic seeking $350B valuation (Vice)\n• Moltbot (fka Clawdbot) goes viral - WIRED: \"Taking Over Silicon Valley\"\n• TechCrunch deep-dive on Moltbot published 2 days ago",
|
||||
"summary": "Gold surging nearly 4% on dollar weakness. Fed decision today. Clawdbot/Moltbot getting serious mainstream press coverage.",
|
||||
"created_at": "2026-01-29T14:01:29.389328336Z"
|
||||
},
|
||||
{
|
||||
"id": "bd403f5d",
|
||||
"date": "2026-01-29",
|
||||
"title": "Afternoon Brief - Jan 29",
|
||||
"weather": "n/a",
|
||||
"markets": "n/a",
|
||||
"news": "n/a",
|
||||
"tasks": "✅ Flutter InouText style library complete\n✅ Sora fonts (7 weights) installed\n✅ Azure Files backup POC files created\n✅ Morning brief delivered\n🔲 OpenVAS scan still running\n🔲 Top menu behavior verification pending",
|
||||
"summary": "Productive day. inou Flutter now has centralized typography (InouText.*). Azure Files POC scaffolded but needs Go install to test. OpenVAS scan on home IP in progress.",
|
||||
"created_at": "2026-01-29T22:00:14.776990456Z"
|
||||
},
|
||||
{
|
||||
"id": "8e79d9bb",
|
||||
"date": "2026-01-30",
|
||||
"title": "Morning Brief - Jan 30, 2026",
|
||||
"weather": "St. Pete FL: Weather service timeout - check locally",
|
||||
"markets": "/ES 7,023 (+0.2%) recovering from -0.9% overnight | /CL $62.76 (+0.6%) | Gold $5,050 (-5.7% crash from ATH!) | TSLA $416.56 (-3.2%) | NABL $7.48 (-0.8%)",
|
||||
"news": "🦞 MAJOR: Clawdbot officially rebranded to Moltbot after Anthropic trademark request - 60k+ GitHub stars, NYT Hard Fork podcast coverage today | 📉 Gold crashes 5.7% to $5,000 as Warsh Fed pick strengthens dollar | ⚠️ Security researchers flagging Moltbot exposed admin ports",
|
||||
"summary": "Moltbot rebrand is THE story - viral growth continues despite (or because of?) security concerns. Gold selloff after hitting ATH. Markets mixed on Fed chair pick.",
|
||||
"created_at": "2026-01-30T14:01:29.27006189Z"
|
||||
},
|
||||
{
|
||||
"id": "57f78885",
|
||||
"date": "2026-01-30",
|
||||
"title": "Morning Brief — Jan 30, 2026",
|
||||
"weather": "Service timed out",
|
||||
"markets": "/ES: 7,023 (+0.2%), /CL: $62.76 (+0.6%), Gold: $5,050 (-5.7% crash from ATH), TSLA: $416.56 (-3.2%), NABL: $7.48 (-0.8%)",
|
||||
"news": "🦞 Clawdbot → Moltbot rebrand (Anthropic trademark request). 60k+ GitHub stars, NYT Hard Fork podcast today. Security researchers flagging exposed admin ports and poisoned skills.\n\n📉 Gold -5.7% selloff as Kevin Warsh Fed chair pick strengthens dollar.",
|
||||
"tasks": "S short position winning (fell 5.7% yesterday, Goldman cut PT to $16.50)",
|
||||
"summary": "Gold crashed 5.7% on Fed chair news. Moltbot rebrand is official. Your S short continues to print.",
|
||||
"created_at": "2026-01-30T15:01:11.886794741Z"
|
||||
},
|
||||
{
|
||||
"id": "5dcba631",
|
||||
"date": "2026-01-30",
|
||||
"title": "Afternoon Brief - Jan 30",
|
||||
"weather": "n/a",
|
||||
"markets": "NABL $7.49 (+2.67%)",
|
||||
"news": "Dutch coalition formed (D66/VVD/CDA), PVV in crisis",
|
||||
"tasks": "✅ Chrome relay + K2.5 browser agent working\n✅ Browser SKILL.md + TOOLS.md updated\n✅ Screensaver disabled on james\n🔲 Proton Mail skill (potential project)",
|
||||
"summary": "Browser optimization day. Chrome relay working for authenticated sites, K2.5 agent created for cheap browsing tasks. Email triage discussed - may build proton-mail skill using IMAP to Bridge.",
|
||||
"created_at": "2026-01-30T22:00:11.549426164Z"
|
||||
},
|
||||
{
|
||||
"id": "fe6d3974",
|
||||
"date": "2026-01-31",
|
||||
"title": "Morning Brief - January 31, 2026",
|
||||
"weather": "🥶 COLD SNAP: 49-58°F today, low 36°F tonight (wind chill 27°F!). Windy gusts to 39mph. Unusual for Florida.",
|
||||
"markets": "📉 Fri close: S\u0026P -0.4% (6,039), Nasdaq -0.9%, Dow -0.4% (but Jan positive). TSLA $430 (+3.3%). Gold CRASHED 9% to $4,880 after hitting $5,625 record. Silver -28%. Oil $65.85. BTC ~$83,900. 10Y: 4.25%",
|
||||
"news": "🔥 Trump nominates Kevin Warsh to replace Powell as Fed Chair. OpenClaw rebrand getting major press (Forbes, CNET, IBM, HN frontpage). Anthropic: Claude constitution revamp, seeking $350B valuation.",
|
||||
"summary": "Gold/silver record highs → massive pullback. Fed Chair bombshell. OpenClaw making waves. Cold weekend ahead.",
|
||||
"created_at": "2026-01-31T14:01:07.575988996Z"
|
||||
},
|
||||
{
|
||||
"id": "90f4436a",
|
||||
"date": "2026-01-31",
|
||||
"title": "Afternoon Brief - Jan 31",
|
||||
"weather": "n/a",
|
||||
"markets": "n/a",
|
||||
"news": "OpenClaw 2026.1.30 released",
|
||||
"tasks": "• Mail agent refactored (pure API)\n• inou-mobile project created (6 features)\n• Nuclei scan: 34 info, 0 critical\n• Email review: 5 action items",
|
||||
"summary": "Productive Saturday. Mail agent is now a clean IMAP/SMTP API — I handle all triage. inou-mobile Flutter project bootstrapped with OCR, voice, biometrics. Security scan on inou.com passed clean. 5 emails need your attention (Barclays fraud alert, UPS fees, DigiKey, Cryo-Cell renewal, Health Link invoice).",
|
||||
"created_at": "2026-01-31T22:00:18.214044416Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"deliveries": [
|
||||
{
|
||||
"id": "20a209a9",
|
||||
"carrier": "UPS",
|
||||
"retailer": "Tri-Med",
|
||||
"description": "Tri-Med Medical Supplies Order #3386",
|
||||
"expected_date": "2026-01-27",
|
||||
"status": "delivered",
|
||||
"notes": "Sophia medical supplies. Import fee $43.25 paid.",
|
||||
"created_at": "2026-02-01T06:30:58.42184085Z",
|
||||
"updated_at": "2026-02-01T06:30:58.421840934Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "be53de27",
|
||||
"title": "📧 Mail Agent Complete",
|
||||
"body": "IMAP triage service fully operational. Service running, webhook to /hooks/mail configured, code pushed to git@zurich.inou.com:mail-agent.git",
|
||||
"type": "success",
|
||||
"source": "james",
|
||||
"timestamp": "2026-01-31T12:18:34.029948077Z"
|
||||
},
|
||||
{
|
||||
"id": "258d00e1",
|
||||
"title": "🛡️ SentinelOne (S) Stock",
|
||||
"body": "$14.06 (-0.07%). Near 52-week low ($13.46). YTD +6.3%, 1yr +41.7%. Short position underwater.",
|
||||
"type": "error",
|
||||
"source": "Yahoo Finance",
|
||||
"timestamp": "2026-01-30T16:44:08.903372981Z"
|
||||
},
|
||||
{
|
||||
"id": "eb38d258",
|
||||
"title": "📉 Markets Down",
|
||||
"body": "Dow -400pts, S\u0026P 500 and Nasdaq falling. Warsh expected to get Fed chair nod. Inflation report weighing on investors. 10-yr Treasury yields rising, gold slumping.",
|
||||
"type": "warning",
|
||||
"source": "MarketWatch",
|
||||
"timestamp": "2026-01-30T16:44:08.883164076Z"
|
||||
},
|
||||
{
|
||||
"id": "3bd7d16f",
|
||||
"title": "🥶 Weekend Cold Snap Alert",
|
||||
"body": "St. Petersburg FL: Sat 36-56°F, Sun 35-46°F. Lows in mid-30s. Protect plants/pets.",
|
||||
"type": "warning",
|
||||
"source": "Open-Meteo",
|
||||
"timestamp": "2026-01-30T16:43:56.172216406Z"
|
||||
},
|
||||
{
|
||||
"id": "d9d3de6d",
|
||||
"title": "🦞 OpenClaw Rebrand",
|
||||
"body": "Project formerly known as Clawdbot → Moltbot → now OpenClaw. Hacker News thread active (posted 4 min ago). IBM Think coverage. Wikipedia updated.",
|
||||
"type": "info",
|
||||
"source": "Hacker News/IBM",
|
||||
"timestamp": "2026-01-30T16:43:56.152204714Z"
|
||||
},
|
||||
{
|
||||
"id": "ed66fd37",
|
||||
"title": "🦞 Moltbot → OpenClaw",
|
||||
"body": "Another rebrand! Now openclaw.ai. 100k+ GitHub stars, 2M visitors/week. \"The lobster has molted into its final form.\"",
|
||||
"type": "info",
|
||||
"source": "@openclaw",
|
||||
"timestamp": "2026-01-30T16:08:10.873278893Z"
|
||||
},
|
||||
{
|
||||
"id": "9678ef17",
|
||||
"title": "❄️ Cold Snap This Weekend",
|
||||
"body": "Unusually cold for Florida. Sat Feb 1: 35-46°F. Sun Feb 2: 41-54°F. Bundle up.",
|
||||
"type": "warning",
|
||||
"source": "Weather",
|
||||
"timestamp": "2026-01-30T15:51:08.892460168Z"
|
||||
},
|
||||
{
|
||||
"id": "ff3fa10f",
|
||||
"title": "Moltbot on NYT Hard Fork",
|
||||
"body": "Podcast feature today. 60k+ GitHub stars. Security researchers flagging exposed admin ports and poisoned skills.",
|
||||
"type": "info",
|
||||
"source": "Tech",
|
||||
"timestamp": "2026-01-30T15:45:17.539675069Z"
|
||||
},
|
||||
{
|
||||
"id": "b45e3ba0",
|
||||
"title": "S (SentinelOne) -5.7%",
|
||||
"body": "Goldman cut PT to $16.50 from $19. Weak Q4 guidance ($271M vs $273.2M expected). Your short winning.",
|
||||
"type": "success",
|
||||
"source": "Markets",
|
||||
"timestamp": "2026-01-30T15:45:17.51939529Z"
|
||||
},
|
||||
{
|
||||
"id": "0b604cce",
|
||||
"title": "Gold Crashes -5.7%",
|
||||
"body": "Dropped from ATH ($5,050) as Kevin Warsh Fed chair pick firms up dollar.",
|
||||
"type": "warning",
|
||||
"source": "Markets",
|
||||
"timestamp": "2026-01-30T15:45:17.495471762Z"
|
||||
},
|
||||
{
|
||||
"id": "3a0a49bd",
|
||||
"title": "MoltMobile Ready",
|
||||
"body": "Android app renamed \u0026 restructured. Audio playback, SUPER ATTENTION MODE 🚨, and Remote Browser implemented. Gateway ready — just needs MiniMax API key.",
|
||||
"type": "success",
|
||||
"source": "james",
|
||||
"timestamp": "2026-01-29T23:00:01.092986645Z"
|
||||
},
|
||||
{
|
||||
"id": "e60edc2a",
|
||||
"title": "🦞 Clawdbot → Moltbot",
|
||||
"body": "Anthropic trademark request. Rebrand happened Jan 27. Security vuln (proxy misconfig) also patched. npm install -g moltbot@latest",
|
||||
"type": "warning",
|
||||
"source": "Forbes/Register",
|
||||
"timestamp": "2026-01-28T23:31:04.72383776Z"
|
||||
},
|
||||
{
|
||||
"id": "93f95d6d",
|
||||
"title": "Memory Features Enabled",
|
||||
"body": "memoryFlush + sessionMemory now active. Auto-save before compaction, search all past sessions.",
|
||||
"type": "info",
|
||||
"source": "Config",
|
||||
"timestamp": "2026-01-28T23:28:10.385545581Z"
|
||||
},
|
||||
{
|
||||
"id": "cce42405",
|
||||
"title": "S\u0026P 500 Hits 7,000",
|
||||
"body": "Historic milestone — first time ever. Tech concentration at 34%.",
|
||||
"type": "success",
|
||||
"source": "Markets",
|
||||
"timestamp": "2026-01-28T23:28:10.369853409Z"
|
||||
},
|
||||
{
|
||||
"id": "9315a907",
|
||||
"title": "Dashboard Restructured",
|
||||
"body": "News on left, tasks on right. News API added.",
|
||||
"type": "success",
|
||||
"timestamp": "2026-01-28T23:16:12.826580181Z"
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"title": "Dashboard Created",
|
||||
"body": "Built this dashboard so we can track tasks and I can post updates. You get a Signal ping with a link when something's new.",
|
||||
"type": "info",
|
||||
"timestamp": "2026-01-26T19:50:00Z"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"title": "Screenshot Skill Working",
|
||||
"body": "Screenshot server deployed on Mac Mini. Both instances (James \u0026 CC) can now pull screenshots via HTTP.",
|
||||
"type": "success",
|
||||
"timestamp": "2026-01-26T19:45:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
{
|
||||
"tasks": [
|
||||
{
|
||||
"id": "1",
|
||||
"title": "Flutter Styleguide",
|
||||
"priority": "high",
|
||||
"status": "in-progress",
|
||||
"owner": "johan",
|
||||
"domain": "inou",
|
||||
"notes": "Typography section updated. Still need: Settings, Genetics, Notes, Supplements, Peptides, Empty State sections.",
|
||||
"created_at": "0001-01-01T00:00:00Z",
|
||||
"updated": "2026-01-28T09:25:23Z"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"title": "Screenshot Skill",
|
||||
"priority": "medium",
|
||||
"status": "done",
|
||||
"owner": "james",
|
||||
"domain": "Infrastructure",
|
||||
"notes": "Go server running on Mac Mini :9123. Both James and CC updated.",
|
||||
"created_at": "0001-01-01T00:00:00Z",
|
||||
"updated": "2026-01-28T09:25:23Z"
|
||||
},
|
||||
{
|
||||
"id": "65381e9b",
|
||||
"title": "Flutter Styleguide - match original",
|
||||
"priority": "high",
|
||||
"status": "done",
|
||||
"owner": "james",
|
||||
"domain": "inou",
|
||||
"notes": "Typography fixed to match CSS. Added Settings, Genetics, Supplements, Peptides, Empty State sections.",
|
||||
"created_at": "2026-01-28T09:38:30.886723188Z",
|
||||
"updated": "2026-01-30T01:04:17Z"
|
||||
},
|
||||
{
|
||||
"id": "7e51cdd9",
|
||||
"title": "Apply new styleguide to all pages",
|
||||
"priority": "high",
|
||||
"status": "done",
|
||||
"owner": "james",
|
||||
"domain": "inou",
|
||||
"notes": "Added missing sections to app styleguide:\n- Settings (LLM selector, Units dropdown)\n- Genetics (variants with Ask AI)\n- Notes (photos, timeline entries)\n- Supplements (timing, dosage)\n- Peptides (active/completed cycles)\n- Empty State pattern\n\nBoth design/flutter/ and app/lib/design/ now have complete styleguides matching web.",
|
||||
"created_at": "2026-01-28T09:38:35.046193253Z",
|
||||
"updated": "2026-01-30T01:06:21Z"
|
||||
},
|
||||
{
|
||||
"id": "b31a2233",
|
||||
"title": "Azure Files Backup - new project",
|
||||
"priority": "high",
|
||||
"status": "in-progress",
|
||||
"owner": "james",
|
||||
"domain": "Personal",
|
||||
"notes": "Feature complete! Latest: commit 18ce1fa.\n\n✅ Go 1.22/1.24 installed\n✅ FlatBuffer schema + generated code\n✅ FlatBuffer serializer (full round-trip, ~3μs serialize, ~2μs deserialize)\n✅ Azure SDK integration (real client + upload methods)\n✅ Web UI (Go + htmx + Tailwind)\n✅ Postgres integration (TreeStore methods, connection helper, web UI wired)\n✅ Integration tests (CRUD, tree ops, stats)\n✅ Bug fixes: dirname, scanner path concatenation\n✅ Tree differ: addition detection implemented\n✅ Backup handler: complete (chunking, dedup, XOR hash)\n✅ Restore handler: complete (reassemble, upload to Azure)\n✅ Azure client upload methods (PutFile, CreateDirectory)\n✅ Comprehensive test suite\n✅ All tests passing\n✅ Pushed to git@zurich.inou.com:azure-backup.git\n\n🔲 Azure free trial account (needs Johan)\n🔲 End-to-end test (blocked by Azure)\n\nRun: PATH=/usr/local/go/bin:$PATH go run ./cmd/web --addr=:8080",
|
||||
"created_at": "2026-01-28T09:38:40.823760714Z",
|
||||
"updated": "2026-02-01T01:07:12Z"
|
||||
},
|
||||
{
|
||||
"id": "63b96b35",
|
||||
"text": "Set up OpenVAS vulnerability scanner",
|
||||
"title": "OpenVAS",
|
||||
"priority": "medium",
|
||||
"status": "pending",
|
||||
"owner": "johan",
|
||||
"domain": "Infrastructure",
|
||||
"created_at": "2026-01-28T10:12:17.26905489Z"
|
||||
},
|
||||
{
|
||||
"id": "d5fcdd3e",
|
||||
"text": "Set up Uptime Kuma monitoring",
|
||||
"title": "Uptime Kuma",
|
||||
"priority": "medium",
|
||||
"status": "pending",
|
||||
"owner": "johan",
|
||||
"domain": "Infrastructure",
|
||||
"created_at": "2026-01-28T10:12:17.286579243Z"
|
||||
},
|
||||
{
|
||||
"id": "69acd59d",
|
||||
"title": "Get browsing working for Moltbot",
|
||||
"priority": "high",
|
||||
"status": "done",
|
||||
"owner": "james",
|
||||
"domain": "Infrastructure",
|
||||
"notes": "Browser was not running. Started clawd profile successfully.\n- browser:start works\n- browser:open works\n- browser:snapshot works\n- Tested on example.com and news.ycombinator.com\n\nBrowser runs headless with Playwright Chromium at /home/johan/.cache/ms-playwright/chromium-1208/\nUser data stored at /home/johan/.clawdbot/browser/clawd/user-data",
|
||||
"created_at": "2026-01-28T23:31:51.295564296Z",
|
||||
"updated": "2026-01-30T01:07:02Z"
|
||||
},
|
||||
{
|
||||
"id": "8d01b5df",
|
||||
"title": "Add Let's Encrypt cert for OpenVAS (Zurich)",
|
||||
"priority": "medium",
|
||||
"status": "done",
|
||||
"owner": "james",
|
||||
"domain": "Infrastructure",
|
||||
"notes": "Installed Caddy 2.10.2 as reverse proxy on Zurich server.\n\nSetup:\n- Caddy auto-obtains LE cert for zurich.inou.com\n- Reverse proxies HTTPS :443 → localhost:9392\n- Certificate auto-renews\n\nNew URL: https://zurich.inou.com (no port needed)\nOld URL: https://zurich.inou.com:9392 (still works with cert warning)",
|
||||
"created_at": "2026-01-28T23:36:09.91271142Z",
|
||||
"updated": "2026-01-30T01:08:50Z"
|
||||
},
|
||||
{
|
||||
"id": "13283514",
|
||||
"title": "Configure OpenVAS SOC2 scan for inou.com",
|
||||
"priority": "high",
|
||||
"status": "done",
|
||||
"owner": "james",
|
||||
"domain": "inou",
|
||||
"notes": "Scan started via Task Wizard. Target: inou.com. Task: Immediate scan of IP inou.com (Status: Requested). Full scan running in OpenVAS at https://zurich.inou.com:9392",
|
||||
"created_at": "2026-01-28T23:38:17.837376332Z",
|
||||
"updated": "2026-01-29T10:18:13Z"
|
||||
},
|
||||
{
|
||||
"id": "690de572",
|
||||
"title": "Mail Agent - IMAP triage service",
|
||||
"priority": "medium",
|
||||
"status": "done",
|
||||
"owner": "james",
|
||||
"domain": "Infrastructure",
|
||||
"notes": "✅ COMPLETE\n\n- IMAP API running (port 8025)\n- IMAP IDLE watching INBOX\n- Webhook to /hooks/mail configured\n- I receive new mail and decide: archive, delete, reply, or escalate\n- Systemd service: mail-agent.service\n\nNo L1 model needed — I handle all triage directly.",
|
||||
"created_at": "2026-01-31T01:09:07.185371312Z",
|
||||
"updated": "2026-01-31T12:16:18Z"
|
||||
},
|
||||
{
|
||||
"id": "3d3e6483",
|
||||
"text": "Mikhail FiL plan. Do NOT renew - he is leaving in ~1 month.",
|
||||
"title": "Mint Mobile renewal coming up",
|
||||
"priority": "low",
|
||||
"status": "pending",
|
||||
"owner": "johan",
|
||||
"domain": "Personal",
|
||||
"notes": "Email received Jan 26. This is an upsell for 12-month plan. Let it lapse.",
|
||||
"created_at": "2026-01-31T10:13:26.640600581Z"
|
||||
},
|
||||
{
|
||||
"id": "bdedb0c2",
|
||||
"text": "Shipment has import fees that need payment",
|
||||
"title": "UPS Import Fees Due",
|
||||
"priority": "high",
|
||||
"status": "pending",
|
||||
"owner": "johan",
|
||||
"domain": "Personal",
|
||||
"created_at": "2026-01-31T10:14:40.918414184Z"
|
||||
},
|
||||
{
|
||||
"id": "ad5b1211",
|
||||
"text": "Mobile phlebotomy invoice received - needs payment",
|
||||
"title": "Health Link Phlebotomy Invoice #000046",
|
||||
"priority": "medium",
|
||||
"status": "pending",
|
||||
"owner": "johan",
|
||||
"domain": "Personal",
|
||||
"created_at": "2026-01-31T10:14:40.926185677Z"
|
||||
},
|
||||
{
|
||||
"id": "25f4e8ed",
|
||||
"text": "Add missing headers: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, HSTS, CSP, Referrer-Policy, Permissions-Policy",
|
||||
"title": "Add security headers to Caddy",
|
||||
"priority": "high",
|
||||
"status": "done",
|
||||
"owner": "james",
|
||||
"domain": "Infrastructure",
|
||||
"notes": "Added security headers via Tailscale SSH. Verified with curl.",
|
||||
"created_at": "2026-01-31T23:50:16.714091775Z",
|
||||
"updated": "2026-02-01T00:00:45Z"
|
||||
},
|
||||
{
|
||||
"id": "6aeed4ac",
|
||||
"text": "Set up ~/dev/docs/soc2/nuclei-scans/ with baseline report from Jan 31",
|
||||
"title": "Create nuclei reporting structure",
|
||||
"priority": "medium",
|
||||
"status": "done",
|
||||
"owner": "james",
|
||||
"domain": "Infrastructure",
|
||||
"notes": "Created ~/dev/docs/soc2/nuclei-scans/2026-01/ with baseline report",
|
||||
"created_at": "2026-01-31T23:50:16.731454991Z",
|
||||
"updated": "2026-01-31T23:51:53Z"
|
||||
},
|
||||
{
|
||||
"id": "270424d3",
|
||||
"text": "Cron job for Sundays, critical/high templates only",
|
||||
"title": "Add weekly nuclei light scan",
|
||||
"priority": "low",
|
||||
"status": "done",
|
||||
"owner": "james",
|
||||
"domain": "Infrastructure",
|
||||
"notes": "Added nuclei-weekly cron job (Sundays 10am ET)",
|
||||
"created_at": "2026-01-31T23:50:16.74671578Z",
|
||||
"updated": "2026-01-31T23:51:53Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
module james-dashboard
|
||||
|
||||
go 1.22.5
|
||||
|
||||
require github.com/google/uuid v1.6.0 // indirect
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
|
|
@ -0,0 +1,606 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>James Dashboard</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg: #F8F7F6;
|
||||
--bg-card: #FFFFFF;
|
||||
--border: #E5E2DE;
|
||||
--text: #1C1917;
|
||||
--text-muted: #78716C;
|
||||
--accent: #B45309;
|
||||
--accent-light: #FEF3C7;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--info: #6366f1;
|
||||
--info-light: #EEF2FF;
|
||||
--james: #B45309;
|
||||
--johan: #2563EB;
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: "Sora", -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.5;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.container { max-width: 1400px; margin: 0 auto; }
|
||||
|
||||
.three-column {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.three-column { grid-template-columns: 1fr 1fr; }
|
||||
.column-done { grid-column: span 2; }
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.three-column { grid-template-columns: 1fr; }
|
||||
.column-done { grid-column: span 1; }
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
h1 { font-size: 1.75rem; font-weight: 700; }
|
||||
h1 .accent { color: var(--accent); }
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.updated { font-size: 0.8rem; color: var(--text-muted); }
|
||||
|
||||
/* Agents Bar */
|
||||
.agents-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
padding: 12px 16px;
|
||||
background: var(--bg-card);
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.agents-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.agents-links {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.agent-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
background: var(--bg);
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
transition: background 0.15s, transform 0.1s;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.agent-link:hover {
|
||||
background: var(--accent-light);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.agent-link.default {
|
||||
background: var(--accent-light);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.agent-emoji { font-size: 1rem; }
|
||||
|
||||
.section { margin-bottom: 20px; }
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.section-header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.section-indicator {
|
||||
width: 4px;
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.section-indicator.alerts { background: var(--danger); }
|
||||
.section-indicator.james { background: var(--james); }
|
||||
.section-indicator.johan { background: var(--johan); }
|
||||
.section-indicator.progress { background: var(--info); }
|
||||
.section-indicator.done { background: var(--success); }
|
||||
|
||||
.section-title {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.section-count {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
background: var(--bg);
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--bg-card);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-collapsed .card-content { display: none; }
|
||||
.card-collapsed .collapse-toggle::after { content: '▶'; }
|
||||
|
||||
.collapse-toggle {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
font-size: 0.65rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.collapse-toggle::after { content: '▼'; margin-left: 4px; }
|
||||
|
||||
/* Alerts/News */
|
||||
.alert-item {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.alert-item:last-child { border-bottom: none; }
|
||||
|
||||
.alert-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.alert-title {
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.alert-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.alert-dot.error { background: var(--danger); }
|
||||
.alert-dot.warning { background: var(--accent); }
|
||||
.alert-dot.success { background: var(--success); }
|
||||
.alert-dot.info { background: var(--info); }
|
||||
|
||||
.alert-time {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.alert-body {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.alert-source {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
margin-left: 16px;
|
||||
margin-top: 4px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tasks */
|
||||
.task-item {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.task-item:last-child { border-bottom: none; }
|
||||
|
||||
.task-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
font-weight: 500;
|
||||
font-size: 0.85rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.task-domain {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
background: var(--bg);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.task-notes {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
margin-top: 6px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.task-meta {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-size: 0.65rem;
|
||||
font-weight: 500;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.badge-high { background: var(--danger-light); color: var(--danger); }
|
||||
.badge-medium { background: var(--accent-light); color: var(--accent); }
|
||||
.badge-low { background: var(--bg); color: var(--text-muted); }
|
||||
.badge-done { background: var(--success-light); color: var(--success); }
|
||||
.badge-in-progress { background: var(--info-light); color: var(--info); }
|
||||
.badge-pending { background: var(--bg); color: var(--text-muted); }
|
||||
|
||||
.empty {
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Latest Briefing Banner */
|
||||
.briefing-banner {
|
||||
background: linear-gradient(135deg, #8B5CF6 0%, #6366f1 100%);
|
||||
color: white;
|
||||
padding: 16px 20px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.briefing-banner-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.briefing-banner-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.briefing-banner-time {
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.briefing-banner-summary {
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.9;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
body { padding: 16px; }
|
||||
h1 { font-size: 1.25rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1><span class="accent">James</span> Dashboard</h1>
|
||||
<div class="header-right">
|
||||
<span class="updated" id="last-updated">Loading...</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Latest Briefing Banner -->
|
||||
<div class="briefing-banner" id="briefing-banner">
|
||||
<div class="briefing-banner-header">
|
||||
<span class="briefing-banner-title">Loading briefing...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Agents Quick Links -->
|
||||
<div class="agents-bar" id="agents-bar">
|
||||
<span class="agents-label">Agents:</span>
|
||||
<div class="agents-links" id="agents-links">
|
||||
<span class="loading">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="three-column">
|
||||
<!-- Column 1: Alerts + James Tasks -->
|
||||
<div class="column">
|
||||
<div class="section">
|
||||
<div class="section-header">
|
||||
<div class="section-header-left">
|
||||
<div class="section-indicator alerts"></div>
|
||||
<span class="section-title">Alerts</span>
|
||||
</div>
|
||||
<span class="section-count" id="alerts-count">0</span>
|
||||
</div>
|
||||
<div class="card" id="alerts-container">
|
||||
<div class="empty">No alerts</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">
|
||||
<div class="section-header-left">
|
||||
<div class="section-indicator james"></div>
|
||||
<span class="section-title">James — Pending</span>
|
||||
</div>
|
||||
<span class="section-count" id="james-pending-count">0</span>
|
||||
</div>
|
||||
<div class="card" id="james-pending-container">
|
||||
<div class="empty">No pending tasks</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Column 2: Johan Tasks + In Progress -->
|
||||
<div class="column">
|
||||
<div class="section">
|
||||
<div class="section-header">
|
||||
<div class="section-header-left">
|
||||
<div class="section-indicator johan"></div>
|
||||
<span class="section-title">Johan — Pending</span>
|
||||
</div>
|
||||
<span class="section-count" id="johan-pending-count">0</span>
|
||||
</div>
|
||||
<div class="card" id="johan-pending-container">
|
||||
<div class="empty">No pending tasks</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">
|
||||
<div class="section-header-left">
|
||||
<div class="section-indicator progress"></div>
|
||||
<span class="section-title">In Progress</span>
|
||||
</div>
|
||||
<span class="section-count" id="progress-count">0</span>
|
||||
</div>
|
||||
<div class="card" id="progress-container">
|
||||
<div class="empty">Nothing in progress</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Column 3: Done -->
|
||||
<div class="column column-done">
|
||||
<div class="section">
|
||||
<div class="section-header">
|
||||
<div class="section-header-left">
|
||||
<div class="section-indicator done"></div>
|
||||
<span class="section-title">Done</span>
|
||||
<span class="collapse-toggle" onclick="toggleDone()"></span>
|
||||
</div>
|
||||
<span class="section-count" id="done-count">0</span>
|
||||
</div>
|
||||
<div class="card" id="done-container">
|
||||
<div class="empty">No completed tasks</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let doneCollapsed = false;
|
||||
|
||||
function toggleDone() {
|
||||
doneCollapsed = !doneCollapsed;
|
||||
document.getElementById('done-container').parentElement.classList.toggle('card-collapsed', doneCollapsed);
|
||||
}
|
||||
|
||||
function formatTime(iso) {
|
||||
const d = new Date(iso);
|
||||
const now = new Date();
|
||||
const diff = now - d;
|
||||
|
||||
if (diff < 3600000) return Math.floor(diff / 60000) + 'm ago';
|
||||
if (diff < 86400000) return Math.floor(diff / 3600000) + 'h ago';
|
||||
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
||||
}
|
||||
|
||||
function priorityBadge(priority) {
|
||||
return `<span class="badge badge-${priority}">${priority}</span>`;
|
||||
}
|
||||
|
||||
function ownerBadge(owner) {
|
||||
const color = owner === 'james' ? 'var(--james)' : 'var(--johan)';
|
||||
return `<span class="badge" style="background: ${color}20; color: ${color}">${owner}</span>`;
|
||||
}
|
||||
|
||||
function renderTask(t, showOwner = false) {
|
||||
return `
|
||||
<div class="task-item">
|
||||
<div class="task-header">
|
||||
<div class="task-title">${t.title}</div>
|
||||
${t.domain ? `<span class="task-domain">${t.domain}</span>` : ''}
|
||||
</div>
|
||||
${t.notes ? `<div class="task-notes">${t.notes}</div>` : ''}
|
||||
<div class="task-meta">
|
||||
${priorityBadge(t.priority)}
|
||||
${showOwner ? ownerBadge(t.owner) : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderAlert(n) {
|
||||
return `
|
||||
<div class="alert-item">
|
||||
<div class="alert-header">
|
||||
<div class="alert-title">
|
||||
<span class="alert-dot ${n.type}"></span>
|
||||
${n.title}
|
||||
</div>
|
||||
<span class="alert-time">${formatTime(n.timestamp)}</span>
|
||||
</div>
|
||||
<div class="alert-body">${n.body}</div>
|
||||
${n.source ? `<div class="alert-source">— ${n.source}</div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
const [tasksRes, newsRes, briefingsRes] = await Promise.all([
|
||||
fetch('/api/tasks'),
|
||||
fetch('/api/news'),
|
||||
fetch('/api/briefings')
|
||||
]);
|
||||
|
||||
const tasksData = await tasksRes.json();
|
||||
const newsData = await newsRes.json();
|
||||
const briefingsData = await briefingsRes.json();
|
||||
|
||||
const tasks = tasksData.tasks || [];
|
||||
const news = newsData.items || [];
|
||||
const briefings = briefingsData.briefings || [];
|
||||
|
||||
// Filter tasks by owner and status
|
||||
const jamesPending = tasks.filter(t => t.owner === 'james' && t.status === 'pending');
|
||||
const johanPending = tasks.filter(t => t.owner === 'johan' && t.status === 'pending');
|
||||
const inProgress = tasks.filter(t => t.status === 'in-progress');
|
||||
const done = tasks.filter(t => t.status === 'done').slice(0, 10); // Last 10
|
||||
|
||||
// Filter news to last 24h only
|
||||
const oneDayAgo = Date.now() - 86400000;
|
||||
const recentNews = news.filter(n => new Date(n.timestamp).getTime() > oneDayAgo);
|
||||
|
||||
// Render alerts
|
||||
document.getElementById('alerts-count').textContent = recentNews.length;
|
||||
document.getElementById('alerts-container').innerHTML = recentNews.length
|
||||
? recentNews.map(renderAlert).join('')
|
||||
: '<div class="empty">No alerts in last 24h</div>';
|
||||
|
||||
// Render task groups
|
||||
document.getElementById('james-pending-count').textContent = jamesPending.length;
|
||||
document.getElementById('james-pending-container').innerHTML = jamesPending.length
|
||||
? jamesPending.map(t => renderTask(t)).join('')
|
||||
: '<div class="empty">No pending tasks</div>';
|
||||
|
||||
document.getElementById('johan-pending-count').textContent = johanPending.length;
|
||||
document.getElementById('johan-pending-container').innerHTML = johanPending.length
|
||||
? johanPending.map(t => renderTask(t)).join('')
|
||||
: '<div class="empty">No pending tasks</div>';
|
||||
|
||||
document.getElementById('progress-count').textContent = inProgress.length;
|
||||
document.getElementById('progress-container').innerHTML = inProgress.length
|
||||
? inProgress.map(t => renderTask(t, true)).join('')
|
||||
: '<div class="empty">Nothing in progress</div>';
|
||||
|
||||
document.getElementById('done-count').textContent = done.length;
|
||||
document.getElementById('done-container').innerHTML = done.length
|
||||
? done.map(t => renderTask(t, true)).join('')
|
||||
: '<div class="empty">No completed tasks</div>';
|
||||
|
||||
// Render latest briefing banner
|
||||
if (briefings.length > 0) {
|
||||
const latest = briefings[0];
|
||||
document.getElementById('briefing-banner').innerHTML = `
|
||||
<div class="briefing-banner-header">
|
||||
<span class="briefing-banner-title">📋 ${latest.title}</span>
|
||||
<span class="briefing-banner-time">${formatTime(latest.created_at)}</span>
|
||||
</div>
|
||||
<div class="briefing-banner-summary">${latest.summary || latest.weather + ' • ' + (latest.markets || '').substring(0, 80) + '...'}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
document.getElementById('last-updated').textContent = 'Updated ' + formatTime(new Date().toISOString());
|
||||
|
||||
} catch (e) {
|
||||
console.error('Failed to load data:', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAgents() {
|
||||
try {
|
||||
const res = await fetch('/api/agents');
|
||||
const data = await res.json();
|
||||
const agents = data.agents || [];
|
||||
|
||||
if (agents.length === 0) {
|
||||
document.getElementById('agents-links').innerHTML = '<span class="loading">No agents configured</span>';
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('agents-links').innerHTML = agents.map(a => `
|
||||
<a href="${a.url}" target="_blank" class="agent-link ${a.default ? 'default' : ''}">
|
||||
<span class="agent-emoji">${a.emoji}</span>
|
||||
<span>${a.name}</span>
|
||||
</a>
|
||||
`).join('');
|
||||
} catch (e) {
|
||||
console.error('Failed to load agents:', e);
|
||||
document.getElementById('agents-links').innerHTML = '<span class="loading">Failed to load</span>';
|
||||
}
|
||||
}
|
||||
|
||||
loadAgents();
|
||||
loadData();
|
||||
setInterval(loadData, 30000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
Loading…
Reference in New Issue