From 20ef35c8e3c20840b5a9a4af6a3bc5b017bbe9ea Mon Sep 17 00:00:00 2001 From: Dorian Date: Wed, 4 Mar 2026 12:15:33 +0000 Subject: [PATCH] feat(archy): extend useArchy with wallet/files context, add ArchyAppsGrid component - Add wallet (Lightning balance, channels) and files (Nextcloud) context categories to useArchy composable with AI prompt injection - Create archy-apps.ts data file mapping all 18 Archy services - Build ArchyAppsGrid component with live status from bridge - Wire ArchyAppsGrid into ContentPanel when embedded in Archy Co-Authored-By: Claude Opus 4.6 --- .../src/components/content/ArchyAppsGrid.vue | 138 ++++++++++++++ .../src/components/content/ContentPanel.vue | 6 + packages/app/src/composables/useArchy.ts | 56 ++++++ packages/app/src/data/archy-apps.ts | 180 ++++++++++++++++++ 4 files changed, 380 insertions(+) create mode 100644 packages/app/src/components/content/ArchyAppsGrid.vue create mode 100644 packages/app/src/data/archy-apps.ts diff --git a/packages/app/src/components/content/ArchyAppsGrid.vue b/packages/app/src/components/content/ArchyAppsGrid.vue new file mode 100644 index 00000000..33a2b711 --- /dev/null +++ b/packages/app/src/components/content/ArchyAppsGrid.vue @@ -0,0 +1,138 @@ + + + diff --git a/packages/app/src/components/content/ContentPanel.vue b/packages/app/src/components/content/ContentPanel.vue index b1aaf1c8..38176181 100644 --- a/packages/app/src/components/content/ContentPanel.vue +++ b/packages/app/src/components/content/ContentPanel.vue @@ -187,6 +187,9 @@ :query="panelQuery" :hero-image="panelMagazineHeroImage ?? undefined" /> + getRendererForContentType('film')) @@ -247,6 +252,7 @@ const songRenderer = computed(() => getRendererForContentType('song')) const { isDark } = useTheme() const favoritesStore = useFavoritesStore() +const { isEmbedded: isArchyEmbedded } = useArchy() const { panelOpen, diff --git a/packages/app/src/composables/useArchy.ts b/packages/app/src/composables/useArchy.ts index 8dc8a8f8..c650efca 100644 --- a/packages/app/src/composables/useArchy.ts +++ b/packages/app/src/composables/useArchy.ts @@ -19,6 +19,21 @@ interface ArchyNetworkInfo { connected?: boolean } +export interface ArchyWalletInfo { + balanceSats?: number + channelCount?: number + totalCapacitySats?: number + nodePubkey?: string +} + +export interface ArchyFileEntry { + name: string + path: string + size?: number + modified?: string + type: 'file' | 'folder' +} + // Singleton reactive state (shared across all components using this composable) const isEmbedded = ref(false) const isInitialized = ref(false) @@ -27,6 +42,8 @@ const accentColor = ref(null) const installedApps = ref([]) const systemInfo = ref({}) const networkInfo = ref({}) +const walletInfo = ref({}) +const fileList = ref([]) let cleanups: (() => void)[] = [] /** @@ -98,6 +115,26 @@ export function useArchy() { ) } + if (cats.includes('wallet')) { + fetches.push( + archyBridge.requestContext('wallet').then((res) => { + if (res.permitted && res.data) { + walletInfo.value = res.data as ArchyWalletInfo + } + }).catch(() => {}), + ) + } + + if (cats.includes('files')) { + fetches.push( + archyBridge.requestContext('files').then((res) => { + if (res.permitted && Array.isArray(res.data)) { + fileList.value = res.data as ArchyFileEntry[] + } + }).catch(() => {}), + ) + } + await Promise.all(fetches) } @@ -141,6 +178,23 @@ export function useArchy() { sections.push(`**Network:** ${net.connected ? 'Connected' : 'Disconnected'}`) } + if (permissions.value.includes('wallet') && walletInfo.value.balanceSats !== undefined) { + const w = walletInfo.value + const balance = w.balanceSats! + const parts = [`Balance: ${balance.toLocaleString()} sats`] + if (w.channelCount !== undefined) parts.push(`${w.channelCount} channels`) + if (w.totalCapacitySats !== undefined) parts.push(`Total capacity: ${w.totalCapacitySats.toLocaleString()} sats`) + if (w.nodePubkey) parts.push(`Pubkey: ${w.nodePubkey.slice(0, 8)}...`) + sections.push(`**Lightning Wallet:** ${parts.join(' | ')}`) + } + + if (permissions.value.includes('files') && fileList.value.length > 0) { + const files = fileList.value + const recent = files.slice(0, 20) + const fileNames = recent.map((f) => f.name).join(', ') + sections.push(`**Files:** ${files.length} files in Nextcloud. Recent: ${fileNames}`) + } + if (sections.length === 0) return '' return `\n\n**Archy Node Context** (this user is running AIUI on their Archipelago node):\n${sections.join('\n')}\n\nYou can help the user manage their node. Available actions: open an app (open-app), install an app (install-app), navigate in Archy (navigate). When recommending apps, check if they're already installed.` @@ -162,6 +216,8 @@ export function useArchy() { installedApps: readonly(installedApps), systemInfo: readonly(systemInfo), networkInfo: readonly(networkInfo), + walletInfo: readonly(walletInfo), + fileList: readonly(fileList), init, destroy, refreshContext, diff --git a/packages/app/src/data/archy-apps.ts b/packages/app/src/data/archy-apps.ts new file mode 100644 index 00000000..cc6e271c --- /dev/null +++ b/packages/app/src/data/archy-apps.ts @@ -0,0 +1,180 @@ +export interface ArchyAppMeta { + id: string + name: string + description: string + icon: string + category: 'bitcoin' | 'lightning' | 'storage' | 'social' | 'tools' | 'monitoring' | 'ai' | 'identity' + defaultPort?: number + deepLink: string +} + +export const ARCHY_APPS: ArchyAppMeta[] = [ + // ─── Bitcoin & Lightning ──────────────────────────────────────── + { + id: 'bitcoin-core', + name: 'Bitcoin Core', + description: 'Full Bitcoin node — blockchain validation, UTXO set, fee estimates', + icon: '₿', + category: 'bitcoin', + defaultPort: 8332, + deepLink: '/app/bitcoin-core', + }, + { + id: 'lnd', + name: 'LND', + description: 'Lightning Network Daemon — channels, payments, invoices', + icon: '⚡', + category: 'lightning', + defaultPort: 8080, + deepLink: '/app/lnd', + }, + { + id: 'core-lightning', + name: 'Core Lightning', + description: 'C-Lightning implementation with plugin ecosystem', + icon: '⚡', + category: 'lightning', + defaultPort: 9735, + deepLink: '/app/core-lightning', + }, + { + id: 'btcpay-server', + name: 'BTCPay Server', + description: 'Self-hosted payment processor for Bitcoin and Lightning', + icon: '🛒', + category: 'bitcoin', + defaultPort: 23001, + deepLink: '/app/btcpay-server', + }, + { + id: 'mempool', + name: 'Mempool', + description: 'Blockchain explorer — visualize transactions, fees, blocks', + icon: '🔍', + category: 'bitcoin', + defaultPort: 3006, + deepLink: '/app/mempool', + }, + { + id: 'fedimint', + name: 'Fedimint', + description: 'Federated Chaumian e-cash — community custody', + icon: '🏦', + category: 'bitcoin', + deepLink: '/app/fedimint', + }, + // ─── Storage & Files ──────────────────────────────────────────── + { + id: 'nextcloud', + name: 'Nextcloud', + description: 'Files, notes, contacts, calendar — self-hosted cloud', + icon: '☁️', + category: 'storage', + defaultPort: 8443, + deepLink: '/app/nextcloud', + }, + { + id: 'immich', + name: 'Immich', + description: 'Photo & video management with ML tagging', + icon: '📸', + category: 'storage', + defaultPort: 2283, + deepLink: '/app/immich', + }, + // ─── Social & Nostr ───────────────────────────────────────────── + { + id: 'nostr-rs-relay', + name: 'nostr-rs-relay', + description: 'High-performance Nostr relay in Rust', + icon: '🟣', + category: 'social', + defaultPort: 7000, + deepLink: '/app/nostr-rs-relay', + }, + { + id: 'strfry', + name: 'strfry', + description: 'C++ Nostr relay — fast event processing', + icon: '🟣', + category: 'social', + deepLink: '/app/strfry', + }, + // ─── Tools ────────────────────────────────────────────────────── + { + id: 'home-assistant', + name: 'Home Assistant', + description: 'Smart home automation and control', + icon: '🏠', + category: 'tools', + defaultPort: 8123, + deepLink: '/app/home-assistant', + }, + { + id: 'searxng', + name: 'SearXNG', + description: 'Privacy-respecting metasearch engine', + icon: '🔎', + category: 'tools', + defaultPort: 8888, + deepLink: '/app/searxng', + }, + { + id: 'penpot', + name: 'Penpot', + description: 'Open-source design tool — prototyping and collaboration', + icon: '🎨', + category: 'tools', + deepLink: '/app/penpot', + }, + { + id: 'onlyoffice', + name: 'OnlyOffice', + description: 'Document editing — docs, spreadsheets, presentations', + icon: '📝', + category: 'tools', + deepLink: '/app/onlyoffice', + }, + { + id: 'meshtastic', + name: 'Meshtastic', + description: 'Off-grid mesh networking over LoRa radios', + icon: '📡', + category: 'tools', + deepLink: '/app/meshtastic', + }, + // ─── Monitoring ───────────────────────────────────────────────── + { + id: 'grafana', + name: 'Grafana', + description: 'Monitoring dashboards — system metrics and alerts', + icon: '📊', + category: 'monitoring', + defaultPort: 3000, + deepLink: '/app/grafana', + }, + // ─── AI ───────────────────────────────────────────────────────── + { + id: 'ollama', + name: 'Ollama', + description: 'Run LLMs locally — Llama, Mistral, Gemma', + icon: '🧠', + category: 'ai', + defaultPort: 11434, + deepLink: '/app/ollama', + }, + // ─── Identity ─────────────────────────────────────────────────── + { + id: 'did-wallet', + name: 'DID Wallet', + description: 'Decentralized identity — Web5 verifiable credentials', + icon: '🪪', + category: 'identity', + deepLink: '/app/did-wallet', + }, +] + +/** Look up an Archy app by its ID */ +export function getArchyApp(id: string): ArchyAppMeta | undefined { + return ARCHY_APPS.find((a) => a.id === id) +}