quoxcli 0.0.0-stage → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +35 -0
- package/README.md +144 -2
- package/bin/quox.js +265 -0
- package/bin/quoxon.js +8 -0
- package/lib/api/endpoints/admin.js +151 -0
- package/lib/api/endpoints/aee.js +38 -0
- package/lib/api/endpoints/agent-attach.js +126 -0
- package/lib/api/endpoints/agent-schedules.js +26 -0
- package/lib/api/endpoints/agents.js +63 -0
- package/lib/api/endpoints/assistants.js +62 -0
- package/lib/api/endpoints/audit.js +14 -0
- package/lib/api/endpoints/auth.js +50 -0
- package/lib/api/endpoints/awareness.js +34 -0
- package/lib/api/endpoints/away.js +15 -0
- package/lib/api/endpoints/backup-schedules.js +20 -0
- package/lib/api/endpoints/backups.js +86 -0
- package/lib/api/endpoints/brain.js +140 -0
- package/lib/api/endpoints/budgets.js +62 -0
- package/lib/api/endpoints/calendarq.js +71 -0
- package/lib/api/endpoints/catalog.js +14 -0
- package/lib/api/endpoints/chat.js +61 -0
- package/lib/api/endpoints/consent.js +26 -0
- package/lib/api/endpoints/contactsq.js +68 -0
- package/lib/api/endpoints/conversations.js +46 -0
- package/lib/api/endpoints/custom-roles.js +92 -0
- package/lib/api/endpoints/divisions.js +56 -0
- package/lib/api/endpoints/domains.js +38 -0
- package/lib/api/endpoints/engagement.js +231 -0
- package/lib/api/endpoints/engine.js +11 -0
- package/lib/api/endpoints/entity.js +50 -0
- package/lib/api/endpoints/executions.js +45 -0
- package/lib/api/endpoints/fetchAllowlist.js +27 -0
- package/lib/api/endpoints/file.js +20 -0
- package/lib/api/endpoints/files.js +40 -0
- package/lib/api/endpoints/github.js +21 -0
- package/lib/api/endpoints/governance.js +43 -0
- package/lib/api/endpoints/hat.js +66 -0
- package/lib/api/endpoints/inbox.js +52 -0
- package/lib/api/endpoints/inboxq.js +120 -0
- package/lib/api/endpoints/index.js +242 -0
- package/lib/api/endpoints/invites.js +19 -0
- package/lib/api/endpoints/llm-policy.js +43 -0
- package/lib/api/endpoints/lucid.js +89 -0
- package/lib/api/endpoints/members.js +32 -0
- package/lib/api/endpoints/memory.js +94 -0
- package/lib/api/endpoints/mfa.js +32 -0
- package/lib/api/endpoints/mind.js +13 -0
- package/lib/api/endpoints/mon.js +184 -0
- package/lib/api/endpoints/notesq.js +43 -0
- package/lib/api/endpoints/objectives.js +68 -0
- package/lib/api/endpoints/orgs.js +38 -0
- package/lib/api/endpoints/plugins.js +152 -0
- package/lib/api/endpoints/powers.js +75 -0
- package/lib/api/endpoints/presence.js +33 -0
- package/lib/api/endpoints/prompts.js +69 -0
- package/lib/api/endpoints/qlarity.js +122 -0
- package/lib/api/endpoints/quoxbox.js +55 -0
- package/lib/api/endpoints/quoxchat.js +54 -0
- package/lib/api/endpoints/quoxflow.js +32 -0
- package/lib/api/endpoints/quoxplan.js +121 -0
- package/lib/api/endpoints/quoxseo.js +114 -0
- package/lib/api/endpoints/reach.js +47 -0
- package/lib/api/endpoints/router.js +17 -0
- package/lib/api/endpoints/runs.js +38 -0
- package/lib/api/endpoints/safety.js +53 -0
- package/lib/api/endpoints/scim.js +46 -0
- package/lib/api/endpoints/scopes.js +14 -0
- package/lib/api/endpoints/security.js +69 -0
- package/lib/api/endpoints/setup.js +90 -0
- package/lib/api/endpoints/skills.js +200 -0
- package/lib/api/endpoints/sso.js +67 -0
- package/lib/api/endpoints/stepup.js +20 -0
- package/lib/api/endpoints/tags.js +32 -0
- package/lib/api/endpoints/teams.js +56 -0
- package/lib/api/endpoints/telephony.js +46 -0
- package/lib/api/endpoints/tg.js +56 -0
- package/lib/api/endpoints/tools.js +50 -0
- package/lib/api/endpoints/traces.js +157 -0
- package/lib/api/endpoints/triggers.js +96 -0
- package/lib/api/endpoints/user-library.js +41 -0
- package/lib/api/endpoints/users.js +20 -0
- package/lib/api/endpoints/v1.js +585 -0
- package/lib/api/endpoints/vault.js +192 -0
- package/lib/api/endpoints/voice.js +22 -0
- package/lib/api/endpoints/volt.js +84 -0
- package/lib/api/endpoints/ward.js +47 -0
- package/lib/api/endpoints/webhooks.js +46 -0
- package/lib/api/endpoints/workflows.js +169 -0
- package/lib/api/endpoints/x.js +32 -0
- package/lib/api/endpoints.js +15 -0
- package/lib/commands/actions.js +61 -0
- package/lib/commands/admin.js +375 -0
- package/lib/commands/aee.js +99 -0
- package/lib/commands/agent.js +337 -0
- package/lib/commands/agentAttach.js +1528 -0
- package/lib/commands/approvals.js +119 -0
- package/lib/commands/aside.js +24 -0
- package/lib/commands/ask.js +144 -0
- package/lib/commands/assistant.js +169 -0
- package/lib/commands/auth.js +464 -0
- package/lib/commands/awareness.js +88 -0
- package/lib/commands/backup.js +377 -0
- package/lib/commands/brain.js +1085 -0
- package/lib/commands/budget.js +241 -0
- package/lib/commands/build.js +170 -0
- package/lib/commands/calendar.js +216 -0
- package/lib/commands/capabilities.js +34 -0
- package/lib/commands/chat.js +295 -0
- package/lib/commands/claude.js +380 -0
- package/lib/commands/config-cmd.js +106 -0
- package/lib/commands/consent.js +84 -0
- package/lib/commands/contacts.js +1016 -0
- package/lib/commands/contactsSelftest.js +132 -0
- package/lib/commands/conversation.js +105 -0
- package/lib/commands/dashboard.js +43 -0
- package/lib/commands/department.js +96 -0
- package/lib/commands/dev.js +620 -0
- package/lib/commands/devResident.js +326 -0
- package/lib/commands/devSupervisor.js +1062 -0
- package/lib/commands/discord.js +132 -0
- package/lib/commands/doctor.js +181 -0
- package/lib/commands/domain.js +114 -0
- package/lib/commands/done.js +347 -0
- package/lib/commands/engagement.js +597 -0
- package/lib/commands/entity.js +119 -0
- package/lib/commands/facebook.js +212 -0
- package/lib/commands/fetchAllowlist.js +71 -0
- package/lib/commands/file.js +165 -0
- package/lib/commands/fleet.js +1189 -0
- package/lib/commands/governance.js +647 -0
- package/lib/commands/gripe.js +355 -0
- package/lib/commands/hat.js +374 -0
- package/lib/commands/helpers.js +96 -0
- package/lib/commands/hyperion.js +58 -0
- package/lib/commands/inbox.js +173 -0
- package/lib/commands/instagram.js +264 -0
- package/lib/commands/integration.js +100 -0
- package/lib/commands/inventory.js +115 -0
- package/lib/commands/invites.js +45 -0
- package/lib/commands/licence.js +139 -0
- package/lib/commands/linkedin.js +178 -0
- package/lib/commands/llm-policy.js +117 -0
- package/lib/commands/llm.js +45 -0
- package/lib/commands/lucid.js +597 -0
- package/lib/commands/mail.js +531 -0
- package/lib/commands/memory.js +361 -0
- package/lib/commands/mind.js +71 -0
- package/lib/commands/mon.js +581 -0
- package/lib/commands/monitoring.js +86 -0
- package/lib/commands/motion.js +22 -0
- package/lib/commands/notes.js +1246 -0
- package/lib/commands/notesBatch.js +160 -0
- package/lib/commands/notesBridges.js +56 -0
- package/lib/commands/notesLinks.js +60 -0
- package/lib/commands/notification.js +57 -0
- package/lib/commands/nucleus.js +123 -0
- package/lib/commands/objective.js +240 -0
- package/lib/commands/offload.js +89 -0
- package/lib/commands/onboard.js +329 -0
- package/lib/commands/open.js +56 -0
- package/lib/commands/org.js +276 -0
- package/lib/commands/peek.js +28 -0
- package/lib/commands/plan.js +944 -0
- package/lib/commands/plugin-doctor.js +1091 -0
- package/lib/commands/plugin.js +649 -0
- package/lib/commands/policy.js +78 -0
- package/lib/commands/powers.js +296 -0
- package/lib/commands/presence.js +89 -0
- package/lib/commands/prompt.js +350 -0
- package/lib/commands/provider.js +159 -0
- package/lib/commands/qlarity.js +706 -0
- package/lib/commands/quoxbox.js +270 -0
- package/lib/commands/quoxchat.js +275 -0
- package/lib/commands/quoxseo.js +378 -0
- package/lib/commands/reach.js +159 -0
- package/lib/commands/role.js +286 -0
- package/lib/commands/router.js +77 -0
- package/lib/commands/rows.js +358 -0
- package/lib/commands/run.js +187 -0
- package/lib/commands/safety.js +107 -0
- package/lib/commands/scim.js +88 -0
- package/lib/commands/scope.js +58 -0
- package/lib/commands/security.js +157 -0
- package/lib/commands/server.js +292 -0
- package/lib/commands/service.js +89 -0
- package/lib/commands/settings.js +99 -0
- package/lib/commands/setup.js +569 -0
- package/lib/commands/skillManifest.js +113 -0
- package/lib/commands/skillRightsFormat.js +38 -0
- package/lib/commands/skills.js +687 -0
- package/lib/commands/slot.js +1451 -0
- package/lib/commands/sso.js +229 -0
- package/lib/commands/status.js +137 -0
- package/lib/commands/tag.js +91 -0
- package/lib/commands/team.js +142 -0
- package/lib/commands/tg.js +240 -0
- package/lib/commands/tiktok.js +218 -0
- package/lib/commands/tips.js +23 -0
- package/lib/commands/tool.js +304 -0
- package/lib/commands/trace.js +530 -0
- package/lib/commands/trigger.js +223 -0
- package/lib/commands/update.js +46 -0
- package/lib/commands/vault.js +1447 -0
- package/lib/commands/vm.js +237 -0
- package/lib/commands/voice.js +213 -0
- package/lib/commands/volt.js +570 -0
- package/lib/commands/walkthrough.js +32 -0
- package/lib/commands/watch.js +167 -0
- package/lib/commands/webhook.js +137 -0
- package/lib/commands/welcome.js +14 -0
- package/lib/commands/workflow.js +660 -0
- package/lib/commands/x.js +448 -0
- package/lib/core/actingAs.js +136 -0
- package/lib/core/activeSummary.js +186 -0
- package/lib/core/aee.js +53 -0
- package/lib/core/agentAlias.js +163 -0
- package/lib/core/approvalContext.js +61 -0
- package/lib/core/aside.js +54 -0
- package/lib/core/askDecision.js +60 -0
- package/lib/core/auth.js +501 -0
- package/lib/core/banner.js +143 -0
- package/lib/core/brainPrime.js +90 -0
- package/lib/core/callerSource.js +129 -0
- package/lib/core/claudeLs.js +94 -0
- package/lib/core/cliBuild.js +62 -0
- package/lib/core/client.js +693 -0
- package/lib/core/commandRegistrars.js +121 -0
- package/lib/core/completions.js +163 -0
- package/lib/core/config.js +669 -0
- package/lib/core/confirm.js +37 -0
- package/lib/core/customHats.js +168 -0
- package/lib/core/deeplinks.js +76 -0
- package/lib/core/doneEngineFetch.js +227 -0
- package/lib/core/errors.js +441 -0
- package/lib/core/global-flags.js +190 -0
- package/lib/core/governedSession.js +402 -0
- package/lib/core/hyperion.js +102 -0
- package/lib/core/inboxResolution.js +65 -0
- package/lib/core/lazyCommands.js +122 -0
- package/lib/core/liveSessionsReport.js +112 -0
- package/lib/core/offload.js +504 -0
- package/lib/core/org-override.js +52 -0
- package/lib/core/orgResolve.js +70 -0
- package/lib/core/output.js +239 -0
- package/lib/core/peek.js +126 -0
- package/lib/core/prodEnv.js +7 -0
- package/lib/core/profileUsers.js +51 -0
- package/lib/core/promptCancel.js +18 -0
- package/lib/core/quoxglyph.js +158 -0
- package/lib/core/repoPath.js +11 -0
- package/lib/core/residentAlarm.js +282 -0
- package/lib/core/residentAuth.js +243 -0
- package/lib/core/residentPump.js +229 -0
- package/lib/core/residentSeed.js +106 -0
- package/lib/core/rootOptions.js +39 -0
- package/lib/core/router.js +168 -0
- package/lib/core/selfUpdate.js +188 -0
- package/lib/core/sessionHeartbeat.js +192 -0
- package/lib/core/sessionsOverview.js +224 -0
- package/lib/core/slotKeeper.js +527 -0
- package/lib/core/slotWake.js +120 -0
- package/lib/core/supervisorEngine.js +933 -0
- package/lib/core/systemdInstaller.js +256 -0
- package/lib/core/version.js +87 -0
- package/lib/core/vm.js +361 -0
- package/lib/core/vmTui.js +86 -0
- package/lib/core/walkthrough.js +35 -0
- package/lib/hyperion/deck.js +29 -0
- package/lib/lucid/LucidCard.jsx +68 -0
- package/lib/lucid/LucidIntro.jsx +92 -0
- package/lib/lucid/blind.js +71 -0
- package/lib/lucid/cards.js +86 -0
- package/lib/lucid/deck.js +32 -0
- package/lib/lucid/fmt.js +139 -0
- package/lib/lucid/markdown.js +81 -0
- package/lib/lucid/motion.js +56 -0
- package/lib/lucid/sitrep.js +173 -0
- package/lib/plugins/loader.js +93 -0
- package/lib/quoxon/activity.js +116 -0
- package/lib/quoxon/aocl.js +117 -0
- package/lib/quoxon/api.js +952 -0
- package/lib/quoxon/app.js +4032 -0
- package/lib/quoxon/approvalFacts.js +94 -0
- package/lib/quoxon/approvalsView.js +325 -0
- package/lib/quoxon/asideView.js +40 -0
- package/lib/quoxon/ask.js +121 -0
- package/lib/quoxon/away.js +52 -0
- package/lib/quoxon/bang.js +224 -0
- package/lib/quoxon/bulkRisk.js +17 -0
- package/lib/quoxon/capabilities.js +82 -0
- package/lib/quoxon/cells.js +53 -0
- package/lib/quoxon/commandShort.js +112 -0
- package/lib/quoxon/commands.js +479 -0
- package/lib/quoxon/connect.js +654 -0
- package/lib/quoxon/connectCard.js +45 -0
- package/lib/quoxon/consent.js +86 -0
- package/lib/quoxon/context.js +68 -0
- package/lib/quoxon/copy.js +34 -0
- package/lib/quoxon/countdownLine.js +11 -0
- package/lib/quoxon/demo.autoplay.js +29 -0
- package/lib/quoxon/demo.fixtures.js +24 -0
- package/lib/quoxon/demo.js +76 -0
- package/lib/quoxon/devObserver.js +20 -0
- package/lib/quoxon/dividerAocl.js +191 -0
- package/lib/quoxon/dividerRule.js +57 -0
- package/lib/quoxon/dragonModes.js +150 -0
- package/lib/quoxon/dragonView.js +41 -0
- package/lib/quoxon/earlyInput.js +40 -0
- package/lib/quoxon/echo.js +15 -0
- package/lib/quoxon/egg.js +36 -0
- package/lib/quoxon/events.js +677 -0
- package/lib/quoxon/feedback.js +50 -0
- package/lib/quoxon/gallery.js +237 -0
- package/lib/quoxon/handoffOffer.js +78 -0
- package/lib/quoxon/hatSwitch.js +37 -0
- package/lib/quoxon/headless.js +161 -0
- package/lib/quoxon/hostUrls.js +41 -0
- package/lib/quoxon/indicator.js +197 -0
- package/lib/quoxon/intent.js +52 -0
- package/lib/quoxon/keys.js +164 -0
- package/lib/quoxon/launcher.js +103 -0
- package/lib/quoxon/main.js +176 -0
- package/lib/quoxon/markdown.js +341 -0
- package/lib/quoxon/markdownSettle.js +19 -0
- package/lib/quoxon/matrixDomains.json +46 -0
- package/lib/quoxon/mentions.js +90 -0
- package/lib/quoxon/mind.js +103 -0
- package/lib/quoxon/mode.js +46 -0
- package/lib/quoxon/model.js +311 -0
- package/lib/quoxon/modelPrefs.js +345 -0
- package/lib/quoxon/motion/clock.js +70 -0
- package/lib/quoxon/motion/color.js +35 -0
- package/lib/quoxon/motion/frame.js +14 -0
- package/lib/quoxon/motion/level.js +91 -0
- package/lib/quoxon/motion/reveal.js +54 -0
- package/lib/quoxon/motion/ring.js +79 -0
- package/lib/quoxon/mouse.js +211 -0
- package/lib/quoxon/noai.js +57 -0
- package/lib/quoxon/objects/agent.js +61 -0
- package/lib/quoxon/objects/away.js +72 -0
- package/lib/quoxon/objects/crystal.js +100 -0
- package/lib/quoxon/objects/fetch.js +182 -0
- package/lib/quoxon/objects/fleet.js +149 -0
- package/lib/quoxon/objects/health.js +186 -0
- package/lib/quoxon/objects/index.js +106 -0
- package/lib/quoxon/objects/ladder.js +71 -0
- package/lib/quoxon/objects/memory.js +44 -0
- package/lib/quoxon/objects/mirror.js +137 -0
- package/lib/quoxon/objects/model.js +118 -0
- package/lib/quoxon/objects/peek.js +17 -0
- package/lib/quoxon/objects/proof.js +110 -0
- package/lib/quoxon/objects/prove.js +130 -0
- package/lib/quoxon/objects/replay.js +125 -0
- package/lib/quoxon/objects/sitrep.js +58 -0
- package/lib/quoxon/objects/skill.js +105 -0
- package/lib/quoxon/objects/view.js +87 -0
- package/lib/quoxon/objects/workflow.js +119 -0
- package/lib/quoxon/observers.js +42 -0
- package/lib/quoxon/onboarding/copy.js +263 -0
- package/lib/quoxon/onboarding/credential.js +108 -0
- package/lib/quoxon/onboarding/detect.js +22 -0
- package/lib/quoxon/onboarding/dockerOffer.js +45 -0
- package/lib/quoxon/onboarding/failureCause.js +33 -0
- package/lib/quoxon/onboarding/finish.js +134 -0
- package/lib/quoxon/onboarding/index.js +94 -0
- package/lib/quoxon/onboarding/installRunner.js +284 -0
- package/lib/quoxon/onboarding/intent.js +53 -0
- package/lib/quoxon/onboarding/journey.js +73 -0
- package/lib/quoxon/onboarding/preflight.js +156 -0
- package/lib/quoxon/onboarding/probe.js +65 -0
- package/lib/quoxon/onboarding/runner.js +58 -0
- package/lib/quoxon/onboarding/view.js +682 -0
- package/lib/quoxon/orgLabel.js +10 -0
- package/lib/quoxon/orgSwitch.js +33 -0
- package/lib/quoxon/ownBroadcast.js +22 -0
- package/lib/quoxon/palette.js +43 -0
- package/lib/quoxon/print.js +98 -0
- package/lib/quoxon/profileSwitch.js +53 -0
- package/lib/quoxon/promptHistory.js +41 -0
- package/lib/quoxon/providers.js +48 -0
- package/lib/quoxon/qart.js +348 -0
- package/lib/quoxon/qdragon.js +410 -0
- package/lib/quoxon/qdragonFly.js +61 -0
- package/lib/quoxon/qdragonScenes.js +282 -0
- package/lib/quoxon/qeyes.js +213 -0
- package/lib/quoxon/qglyph.js +143 -0
- package/lib/quoxon/qhello.js +58 -0
- package/lib/quoxon/qmotion.js +132 -0
- package/lib/quoxon/render.js +1167 -0
- package/lib/quoxon/resend.js +72 -0
- package/lib/quoxon/roster.js +307 -0
- package/lib/quoxon/route.js +39 -0
- package/lib/quoxon/rows.js +711 -0
- package/lib/quoxon/rowsStore.js +80 -0
- package/lib/quoxon/rules.js +130 -0
- package/lib/quoxon/screenContext.js +150 -0
- package/lib/quoxon/screenFrames.js +254 -0
- package/lib/quoxon/session.js +238 -0
- package/lib/quoxon/sessionDead.js +17 -0
- package/lib/quoxon/signin.js +125 -0
- package/lib/quoxon/skills.js +30 -0
- package/lib/quoxon/slash/agents.js +112 -0
- package/lib/quoxon/slash/argSources.js +65 -0
- package/lib/quoxon/slash/args.js +65 -0
- package/lib/quoxon/slash/automate.js +89 -0
- package/lib/quoxon/slash/broadcast.js +140 -0
- package/lib/quoxon/slash/broadcastRun.js +108 -0
- package/lib/quoxon/slash/card.js +30 -0
- package/lib/quoxon/slash/challenge.js +114 -0
- package/lib/quoxon/slash/cliCommand.js +53 -0
- package/lib/quoxon/slash/cliLeaves.json +948 -0
- package/lib/quoxon/slash/compact.js +90 -0
- package/lib/quoxon/slash/confirm.js +30 -0
- package/lib/quoxon/slash/crystallize.js +69 -0
- package/lib/quoxon/slash/dashboard.js +136 -0
- package/lib/quoxon/slash/draft.js +54 -0
- package/lib/quoxon/slash/hatWalk.js +206 -0
- package/lib/quoxon/slash/inverses.js +77 -0
- package/lib/quoxon/slash/killer.js +24 -0
- package/lib/quoxon/slash/killerApi.js +16 -0
- package/lib/quoxon/slash/ledger.js +88 -0
- package/lib/quoxon/slash/listView.js +30 -0
- package/lib/quoxon/slash/modelCmd.js +261 -0
- package/lib/quoxon/slash/palette.js +118 -0
- package/lib/quoxon/slash/paletteController.js +169 -0
- package/lib/quoxon/slash/panels.js +116 -0
- package/lib/quoxon/slash/picker.js +56 -0
- package/lib/quoxon/slash/restart.js +202 -0
- package/lib/quoxon/slash/resume.js +59 -0
- package/lib/quoxon/slash/rowCmd.js +157 -0
- package/lib/quoxon/slash/rowWalk.js +175 -0
- package/lib/quoxon/slash/settings.js +60 -0
- package/lib/quoxon/slash/spawn.js +177 -0
- package/lib/quoxon/slash/stepUp.js +274 -0
- package/lib/quoxon/slash/stepUpCard.js +60 -0
- package/lib/quoxon/slash/stepUpCmds.js +78 -0
- package/lib/quoxon/slash/undoApi.js +38 -0
- package/lib/quoxon/slash/undoCmd.js +49 -0
- package/lib/quoxon/slash/undoWish.js +15 -0
- package/lib/quoxon/slash/watch.js +164 -0
- package/lib/quoxon/slash/wish.js +23 -0
- package/lib/quoxon/slash/wishCmd.js +32 -0
- package/lib/quoxon/slotChat.js +300 -0
- package/lib/quoxon/slotFormat.js +195 -0
- package/lib/quoxon/slotRun.js +131 -0
- package/lib/quoxon/sound/index.js +186 -0
- package/lib/quoxon/sound/mapping.js +120 -0
- package/lib/quoxon/sound/output.js +52 -0
- package/lib/quoxon/sound/synth.js +122 -0
- package/lib/quoxon/stray.js +22 -0
- package/lib/quoxon/suggestCommand.js +59 -0
- package/lib/quoxon/term.js +141 -0
- package/lib/quoxon/textBuffer.js +416 -0
- package/lib/quoxon/theme.js +286 -0
- package/lib/quoxon/tips.js +433 -0
- package/lib/quoxon/toolError.js +41 -0
- package/lib/quoxon/transcript.js +188 -0
- package/lib/quoxon/web/qface.js +89 -0
- package/lib/quoxon/welcome.js +52 -0
- package/lib/quoxon/whatsnew.js +136 -0
- package/lib/quoxon/whatsnew.jsonl +89 -0
- package/lib/quoxon/windowOrg.js +113 -0
- package/lib/server/diag.js +129 -0
- package/lib/server/doctor.js +71 -0
- package/lib/server/install.js +149 -0
- package/lib/server/pluginEnable.js +161 -0
- package/lib/server/skew.js +56 -0
- package/lib/server/snapshot.js +131 -0
- package/lib/server/status.js +128 -0
- package/lib/server/sys.js +65 -0
- package/lib/server/uninstall.js +63 -0
- package/lib/server/update.js +234 -0
- package/lib/toolOutputShape.js +315 -0
- package/lib/tui/app.jsx +289 -0
- package/lib/tui/components/Breadcrumb.jsx +42 -0
- package/lib/tui/components/ChatInput.jsx +68 -0
- package/lib/tui/components/Confirm.jsx +32 -0
- package/lib/tui/components/DataTable.jsx +270 -0
- package/lib/tui/components/DetailPanel.jsx +241 -0
- package/lib/tui/components/HelpOverlay.jsx +67 -0
- package/lib/tui/components/Layout.jsx +129 -0
- package/lib/tui/components/Palette.jsx +141 -0
- package/lib/tui/components/SearchInput.jsx +125 -0
- package/lib/tui/components/Sparkline.jsx +55 -0
- package/lib/tui/components/StatusBar.jsx +133 -0
- package/lib/tui/components/Toast.jsx +52 -0
- package/lib/tui/hooks/useApi.js +44 -0
- package/lib/tui/hooks/useHealth.js +90 -0
- package/lib/tui/hooks/usePolledApi.js +86 -0
- package/lib/tui/hooks/useTerminalSize.js +46 -0
- package/lib/tui/index.js +56 -0
- package/lib/tui/intro/Intro.jsx +332 -0
- package/lib/tui/jsx-loader.js +32 -0
- package/lib/tui/keys.js +87 -0
- package/lib/tui/registry.js +49 -0
- package/lib/tui/theme.js +103 -0
- package/lib/tui/views/AdminView.jsx +398 -0
- package/lib/tui/views/AgentView.jsx +204 -0
- package/lib/tui/views/BackupView.jsx +225 -0
- package/lib/tui/views/BrainView.jsx +223 -0
- package/lib/tui/views/ChatView.jsx +468 -0
- package/lib/tui/views/FilesView.jsx +218 -0
- package/lib/tui/views/FleetView.jsx +234 -0
- package/lib/tui/views/GovernanceView.jsx +453 -0
- package/lib/tui/views/HealthView.jsx +216 -0
- package/lib/tui/views/HyperionDeck.jsx +79 -0
- package/lib/tui/views/InboxView.jsx +228 -0
- package/lib/tui/views/LucidDeck.jsx +719 -0
- package/lib/tui/views/MainMenu.jsx +123 -0
- package/lib/tui/views/MemoryView.jsx +377 -0
- package/lib/tui/views/ObjectivesView.jsx +230 -0
- package/lib/tui/views/OrgView.jsx +229 -0
- package/lib/tui/views/ToolView.jsx +221 -0
- package/lib/tui/views/VaultView.jsx +422 -0
- package/lib/tui/views/WorkflowView.jsx +329 -0
- package/package.json +75 -4
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout component — header + dual-panel + status bar
|
|
3
|
+
*
|
|
4
|
+
* Provides the two-panel layout (35% left / 65% right) used by most views.
|
|
5
|
+
* Mirrors the QuoxBastion BubbleTea TUI layout pattern.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { Box, Text } from 'ink';
|
|
10
|
+
import { colors } from '../theme.js';
|
|
11
|
+
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
|
12
|
+
import { useTui } from '../app.jsx';
|
|
13
|
+
import { Breadcrumb } from './Breadcrumb.jsx';
|
|
14
|
+
import { SCREEN_TITLES } from '../registry.js';
|
|
15
|
+
|
|
16
|
+
/** Separator width: fills the terminal, with a small margin, floor of 20. */
|
|
17
|
+
function separatorWidth(columns) {
|
|
18
|
+
return Math.max(20, columns - 2);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Build the breadcrumb trail from nav history + the current screen, e.g.
|
|
23
|
+
* ['Main', 'Fleet']. Renders nothing (via Breadcrumb's own empty-path
|
|
24
|
+
* guard) when the trail is trivial — i.e. we're still on the menu.
|
|
25
|
+
*/
|
|
26
|
+
function useBreadcrumbPath() {
|
|
27
|
+
const ctx = useTui();
|
|
28
|
+
const screen = ctx?.screen;
|
|
29
|
+
const history = ctx?.history || [];
|
|
30
|
+
|
|
31
|
+
if (!screen || screen === 'menu') return [];
|
|
32
|
+
|
|
33
|
+
return [...history, screen].map((id) => SCREEN_TITLES[id] || id);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function Layout({ title, subtitle, left, right, footer, children }) {
|
|
37
|
+
const { columns } = useTerminalSize();
|
|
38
|
+
const breadcrumbPath = useBreadcrumbPath();
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Box flexDirection="column" width="100%" height="100%">
|
|
42
|
+
{/* Header */}
|
|
43
|
+
<Box height={1} width="100%">
|
|
44
|
+
<Text color={colors.primary} bold> {title || 'QuoxCLI'}</Text>
|
|
45
|
+
{subtitle && (
|
|
46
|
+
<Text color={colors.dim}> {subtitle}</Text>
|
|
47
|
+
)}
|
|
48
|
+
{breadcrumbPath.length > 0 && (
|
|
49
|
+
<Box marginLeft={2}>
|
|
50
|
+
<Breadcrumb path={breadcrumbPath} />
|
|
51
|
+
</Box>
|
|
52
|
+
)}
|
|
53
|
+
</Box>
|
|
54
|
+
|
|
55
|
+
{/* Separator */}
|
|
56
|
+
<Box height={1} width="100%">
|
|
57
|
+
<Text color={colors.subtle}>{' ' + '\u2500'.repeat(separatorWidth(columns))}</Text>
|
|
58
|
+
</Box>
|
|
59
|
+
|
|
60
|
+
{/* Content area */}
|
|
61
|
+
<Box flexGrow={1} width="100%">
|
|
62
|
+
{children ? (
|
|
63
|
+
children
|
|
64
|
+
) : (
|
|
65
|
+
<>
|
|
66
|
+
{/* Left panel (35%) */}
|
|
67
|
+
<Box width="35%" flexDirection="column" borderStyle="single" borderColor={colors.primary} paddingX={1}>
|
|
68
|
+
{left}
|
|
69
|
+
</Box>
|
|
70
|
+
|
|
71
|
+
{/* Right panel (65%) */}
|
|
72
|
+
<Box width="65%" flexDirection="column" borderStyle="single" borderColor={colors.subtle} paddingX={1}>
|
|
73
|
+
{right}
|
|
74
|
+
</Box>
|
|
75
|
+
</>
|
|
76
|
+
)}
|
|
77
|
+
</Box>
|
|
78
|
+
|
|
79
|
+
{/* Footer hint bar */}
|
|
80
|
+
{footer && (
|
|
81
|
+
<Box height={1} width="100%">
|
|
82
|
+
<Text color={colors.dim}> {footer}</Text>
|
|
83
|
+
</Box>
|
|
84
|
+
)}
|
|
85
|
+
</Box>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Single-panel layout for views like Chat that need full width.
|
|
91
|
+
*/
|
|
92
|
+
export function FullLayout({ title, subtitle, footer, children }) {
|
|
93
|
+
const { columns } = useTerminalSize();
|
|
94
|
+
const breadcrumbPath = useBreadcrumbPath();
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<Box flexDirection="column" width="100%" height="100%">
|
|
98
|
+
{/* Header */}
|
|
99
|
+
<Box height={1} width="100%">
|
|
100
|
+
<Text color={colors.primary} bold> {title || 'QuoxCLI'}</Text>
|
|
101
|
+
{subtitle && (
|
|
102
|
+
<Text color={colors.dim}> {subtitle}</Text>
|
|
103
|
+
)}
|
|
104
|
+
{breadcrumbPath.length > 0 && (
|
|
105
|
+
<Box marginLeft={2}>
|
|
106
|
+
<Breadcrumb path={breadcrumbPath} />
|
|
107
|
+
</Box>
|
|
108
|
+
)}
|
|
109
|
+
</Box>
|
|
110
|
+
|
|
111
|
+
{/* Separator */}
|
|
112
|
+
<Box height={1} width="100%">
|
|
113
|
+
<Text color={colors.subtle}>{' ' + '\u2500'.repeat(separatorWidth(columns))}</Text>
|
|
114
|
+
</Box>
|
|
115
|
+
|
|
116
|
+
{/* Content */}
|
|
117
|
+
<Box flexGrow={1} flexDirection="column" paddingX={1}>
|
|
118
|
+
{children}
|
|
119
|
+
</Box>
|
|
120
|
+
|
|
121
|
+
{/* Footer */}
|
|
122
|
+
{footer && (
|
|
123
|
+
<Box height={1} width="100%">
|
|
124
|
+
<Text color={colors.dim}> {footer}</Text>
|
|
125
|
+
</Box>
|
|
126
|
+
)}
|
|
127
|
+
</Box>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Palette — command palette overlay
|
|
3
|
+
*
|
|
4
|
+
* Opened from app.jsx on Ctrl+K or ':' (nav mode only). Renders in place
|
|
5
|
+
* of the current screen — mirrors HelpOverlay's content-swap pattern, no
|
|
6
|
+
* layout shift. Owns its own keystrokes via its own useInput while open.
|
|
7
|
+
*
|
|
8
|
+
* A text query filters SCREEN_REGISTRY (case-insensitive substring match
|
|
9
|
+
* on title + keywords). Up/Down or Ctrl+P/Ctrl+N move the selection,
|
|
10
|
+
* Enter calls onSelect(id), Esc calls onClose().
|
|
11
|
+
*
|
|
12
|
+
* Props:
|
|
13
|
+
* onSelect(id) - called with the chosen screen id
|
|
14
|
+
* onClose() - called when the user dismisses the palette
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import React, { useState, useMemo } from 'react';
|
|
18
|
+
import { Box, Text, useInput } from 'ink';
|
|
19
|
+
import { SCREEN_REGISTRY } from '../registry.js';
|
|
20
|
+
import { colors } from '../theme.js';
|
|
21
|
+
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
|
22
|
+
|
|
23
|
+
const PREFERRED_WIDTH = 56;
|
|
24
|
+
const MIN_WIDTH = 24;
|
|
25
|
+
const MAX_VISIBLE = 8;
|
|
26
|
+
|
|
27
|
+
/** Case-insensitive substring match against title + keywords. */
|
|
28
|
+
function filterEntries(query) {
|
|
29
|
+
const q = query.trim().toLowerCase();
|
|
30
|
+
if (!q) return SCREEN_REGISTRY;
|
|
31
|
+
return SCREEN_REGISTRY.filter((entry) => {
|
|
32
|
+
if (entry.title.toLowerCase().includes(q)) return true;
|
|
33
|
+
return entry.keywords.some((kw) => kw.toLowerCase().includes(q));
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function Palette({ onSelect, onClose }) {
|
|
38
|
+
const { columns } = useTerminalSize();
|
|
39
|
+
const [query, setQuery] = useState('');
|
|
40
|
+
const [selected, setSelected] = useState(0);
|
|
41
|
+
|
|
42
|
+
const results = useMemo(() => filterEntries(query), [query]);
|
|
43
|
+
|
|
44
|
+
useInput((input, key) => {
|
|
45
|
+
if (key.escape) {
|
|
46
|
+
onClose?.();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (key.return) {
|
|
51
|
+
const entry = results[selected];
|
|
52
|
+
if (entry) onSelect?.(entry.id);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (key.upArrow || (key.ctrl && input === 'p')) {
|
|
57
|
+
setSelected((s) => (results.length === 0 ? 0 : (s > 0 ? s - 1 : results.length - 1)));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (key.downArrow || (key.ctrl && input === 'n')) {
|
|
62
|
+
setSelected((s) => (results.length === 0 ? 0 : (s < results.length - 1 ? s + 1 : 0)));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (key.backspace || key.delete) {
|
|
67
|
+
setQuery((q) => q.slice(0, -1));
|
|
68
|
+
setSelected(0);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Printable characters only — ignore other ctrl/meta/tab combos.
|
|
73
|
+
if (input && !key.ctrl && !key.meta && !key.tab) {
|
|
74
|
+
setQuery((q) => q + input);
|
|
75
|
+
setSelected(0);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const width = Math.max(MIN_WIDTH, Math.min(PREFERRED_WIDTH, columns - 4));
|
|
80
|
+
|
|
81
|
+
// Keep the selected row inside the visible window as the list scrolls.
|
|
82
|
+
const start = Math.max(
|
|
83
|
+
0,
|
|
84
|
+
Math.min(selected - Math.floor(MAX_VISIBLE / 2), Math.max(0, results.length - MAX_VISIBLE))
|
|
85
|
+
);
|
|
86
|
+
const visible = results.slice(start, start + MAX_VISIBLE);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Box flexDirection="column" alignItems="center" width="100%" paddingTop={1}>
|
|
90
|
+
<Box
|
|
91
|
+
flexDirection="column"
|
|
92
|
+
borderStyle="round"
|
|
93
|
+
borderColor={colors.primary}
|
|
94
|
+
paddingX={2}
|
|
95
|
+
paddingY={1}
|
|
96
|
+
width={width}
|
|
97
|
+
>
|
|
98
|
+
{/* Header */}
|
|
99
|
+
<Box justifyContent="center" marginBottom={1}>
|
|
100
|
+
<Text color={colors.primary} bold>Jump to Screen</Text>
|
|
101
|
+
</Box>
|
|
102
|
+
|
|
103
|
+
{/* Query input */}
|
|
104
|
+
<Box borderStyle="single" borderColor={colors.cyberBlue} paddingX={1} marginBottom={1}>
|
|
105
|
+
<Text color={colors.cyberBlue} bold>{'> '}</Text>
|
|
106
|
+
{query ? (
|
|
107
|
+
<Text color={colors.text}>{query}</Text>
|
|
108
|
+
) : (
|
|
109
|
+
<Text color={colors.dim}>Type to filter...</Text>
|
|
110
|
+
)}
|
|
111
|
+
<Text color={colors.cyberBlue}>{'█'}</Text>
|
|
112
|
+
</Box>
|
|
113
|
+
|
|
114
|
+
{/* Results */}
|
|
115
|
+
{visible.length === 0 ? (
|
|
116
|
+
<Text color={colors.dim}>No matching screens</Text>
|
|
117
|
+
) : (
|
|
118
|
+
visible.map((entry, i) => {
|
|
119
|
+
const isSelected = start + i === selected;
|
|
120
|
+
return (
|
|
121
|
+
<Box key={entry.id} height={1}>
|
|
122
|
+
<Text color={isSelected ? colors.primary : colors.subtle}>
|
|
123
|
+
{isSelected ? ' ▶ ' : ' '}
|
|
124
|
+
</Text>
|
|
125
|
+
<Text color={colors.cyberBlue} bold>[{entry.hotkey}] </Text>
|
|
126
|
+
<Text color={isSelected ? colors.text : colors.textMuted} bold={isSelected}>
|
|
127
|
+
{entry.title}
|
|
128
|
+
</Text>
|
|
129
|
+
</Box>
|
|
130
|
+
);
|
|
131
|
+
})
|
|
132
|
+
)}
|
|
133
|
+
|
|
134
|
+
{/* Footer hint */}
|
|
135
|
+
<Box marginTop={1}>
|
|
136
|
+
<Text color={colors.dim}>{'↑/↓'} Navigate Enter Jump Esc Close</Text>
|
|
137
|
+
</Box>
|
|
138
|
+
</Box>
|
|
139
|
+
</Box>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SearchInput — Text input for search/filter operations
|
|
3
|
+
*
|
|
4
|
+
* Uses @inkjs/ui TextInput when available, with a fallback
|
|
5
|
+
* implementation using ink's useInput for basic text entry.
|
|
6
|
+
*
|
|
7
|
+
* Props:
|
|
8
|
+
* placeholder - placeholder text (default: 'Search...')
|
|
9
|
+
* value - current search value
|
|
10
|
+
* onChange - callback when value changes
|
|
11
|
+
* onSubmit - callback when Enter is pressed
|
|
12
|
+
* active - whether input accepts keyboard input (default: true)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import React, { useState, useEffect } from 'react';
|
|
16
|
+
import { Box, Text, useInput } from 'ink';
|
|
17
|
+
import { colors } from '../theme.js';
|
|
18
|
+
|
|
19
|
+
// ── Try to load @inkjs/ui TextInput ────────────────────────────────────
|
|
20
|
+
let InkTextInput = null;
|
|
21
|
+
try {
|
|
22
|
+
const mod = await import('@inkjs/ui');
|
|
23
|
+
InkTextInput = mod.TextInput;
|
|
24
|
+
} catch {
|
|
25
|
+
// Fallback to custom implementation below
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Fallback text input using useInput.
|
|
30
|
+
* Handles printable characters, backspace, and Enter.
|
|
31
|
+
*/
|
|
32
|
+
function FallbackTextInput({ placeholder, value, onChange, onSubmit, active }) {
|
|
33
|
+
useInput((input, key) => {
|
|
34
|
+
if (!active) return;
|
|
35
|
+
|
|
36
|
+
// Enter submits
|
|
37
|
+
if (key.return) {
|
|
38
|
+
if (onSubmit) onSubmit(value);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Backspace / delete
|
|
43
|
+
if (key.backspace || key.delete) {
|
|
44
|
+
if (onChange && value.length > 0) {
|
|
45
|
+
onChange(value.slice(0, -1));
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Escape is handled by parent, not here
|
|
51
|
+
|
|
52
|
+
// Printable characters
|
|
53
|
+
if (input && !key.ctrl && !key.meta && !key.escape && !key.tab) {
|
|
54
|
+
if (onChange) {
|
|
55
|
+
onChange(value + input);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const showPlaceholder = !value || value.length === 0;
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Box>
|
|
64
|
+
<Text color={colors.cyberBlue} bold>/ </Text>
|
|
65
|
+
{showPlaceholder ? (
|
|
66
|
+
<Text color={colors.dim}>{placeholder}</Text>
|
|
67
|
+
) : (
|
|
68
|
+
<Text color={colors.text}>{value}</Text>
|
|
69
|
+
)}
|
|
70
|
+
{active && <Text color={colors.cyberBlue}>{'\u2588'}</Text>}
|
|
71
|
+
</Box>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Wrapper that uses @inkjs/ui TextInput if available.
|
|
77
|
+
*/
|
|
78
|
+
function InkUITextInput({ placeholder, value, onChange, onSubmit, active }) {
|
|
79
|
+
// @inkjs/ui TextInput uses defaultValue + onChange, not controlled value.
|
|
80
|
+
// We track the internal value and sync via onChange.
|
|
81
|
+
const [initialized, setInitialized] = useState(false);
|
|
82
|
+
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
setInitialized(true);
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<Box>
|
|
89
|
+
<Text color={colors.cyberBlue} bold>/ </Text>
|
|
90
|
+
<InkTextInput
|
|
91
|
+
placeholder={placeholder}
|
|
92
|
+
defaultValue={value || ''}
|
|
93
|
+
isDisabled={!active}
|
|
94
|
+
onChange={(newVal) => {
|
|
95
|
+
if (onChange) onChange(newVal);
|
|
96
|
+
}}
|
|
97
|
+
onSubmit={(val) => {
|
|
98
|
+
if (onSubmit) onSubmit(val);
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
</Box>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function SearchInput({
|
|
106
|
+
placeholder = 'Search...',
|
|
107
|
+
value = '',
|
|
108
|
+
onChange,
|
|
109
|
+
onSubmit,
|
|
110
|
+
active = true,
|
|
111
|
+
}) {
|
|
112
|
+
const InputComponent = InkTextInput ? InkUITextInput : FallbackTextInput;
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<Box borderStyle="single" borderColor={active ? colors.cyberBlue : colors.subtle} paddingX={1}>
|
|
116
|
+
<InputComponent
|
|
117
|
+
placeholder={placeholder}
|
|
118
|
+
value={value}
|
|
119
|
+
onChange={onChange}
|
|
120
|
+
onSubmit={onSubmit}
|
|
121
|
+
active={active}
|
|
122
|
+
/>
|
|
123
|
+
</Box>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sparkline — pure presentational block-ramp trend renderer
|
|
3
|
+
*
|
|
4
|
+
* Maps a numeric series onto the unicode block ramp (▁▂▃▄▅▆▇█), scaled
|
|
5
|
+
* between the min/max of the (optionally windowed) series, and renders it
|
|
6
|
+
* as a single <Text> line. Used for btop-style "line went up/down" trends
|
|
7
|
+
* inside dense TUI views (e.g. HealthView).
|
|
8
|
+
*
|
|
9
|
+
* Props:
|
|
10
|
+
* data {number[]} - the series to render (oldest -> newest)
|
|
11
|
+
* width {number} - optional max number of points to show; when the
|
|
12
|
+
* series is longer than width, only the most recent
|
|
13
|
+
* `width` points are rendered (and the ramp is
|
|
14
|
+
* scaled to that visible window)
|
|
15
|
+
* color {string} - optional hex color (defaults to theme cyberBlue)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import React from 'react';
|
|
19
|
+
import { Text } from 'ink';
|
|
20
|
+
import { colors } from '../theme.js';
|
|
21
|
+
|
|
22
|
+
// ── Block ramp (low -> high) ────────────────────────────────────────────
|
|
23
|
+
const RAMP = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
|
|
24
|
+
const MID_CHAR = RAMP[Math.floor(RAMP.length / 2)];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Convert a numeric series into a string of ramp characters.
|
|
28
|
+
* Exported for direct unit testing without rendering ink output.
|
|
29
|
+
*/
|
|
30
|
+
export function sparklineChars(data, width) {
|
|
31
|
+
if (!Array.isArray(data) || data.length === 0) return '';
|
|
32
|
+
|
|
33
|
+
const series = width && data.length > width ? data.slice(-width) : data;
|
|
34
|
+
|
|
35
|
+
const min = Math.min(...series);
|
|
36
|
+
const max = Math.max(...series);
|
|
37
|
+
const range = max - min;
|
|
38
|
+
|
|
39
|
+
if (range === 0) {
|
|
40
|
+
return MID_CHAR.repeat(series.length);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return series
|
|
44
|
+
.map((v) => {
|
|
45
|
+
const ratio = (v - min) / range;
|
|
46
|
+
const idx = Math.min(RAMP.length - 1, Math.floor(ratio * RAMP.length));
|
|
47
|
+
return RAMP[idx];
|
|
48
|
+
})
|
|
49
|
+
.join('');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function Sparkline({ data, width, color }) {
|
|
53
|
+
const chars = sparklineChars(data, width);
|
|
54
|
+
return <Text color={color || colors.cyberBlue}>{chars}</Text>;
|
|
55
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status bar — bottom of the TUI
|
|
3
|
+
*
|
|
4
|
+
* Shows: user email, active org, profile name, current screen, a live
|
|
5
|
+
* service-health dot (polled every 30s), a refresh/staleness indicator,
|
|
6
|
+
* a clock, and key hints.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import React, { useEffect, useState } from 'react';
|
|
10
|
+
import { Box, Text } from 'ink';
|
|
11
|
+
import { colors } from '../theme.js';
|
|
12
|
+
import { useTui } from '../app.jsx';
|
|
13
|
+
import { useHealth } from '../hooks/useHealth.js';
|
|
14
|
+
|
|
15
|
+
// Display labels for each navigable screen id (see MainMenu.jsx MENU_ITEMS).
|
|
16
|
+
const SCREEN_LABELS = {
|
|
17
|
+
menu: 'Menu',
|
|
18
|
+
fleet: 'Fleet',
|
|
19
|
+
agents: 'Agents',
|
|
20
|
+
tools: 'Tools',
|
|
21
|
+
orgs: 'Organizations',
|
|
22
|
+
memory: 'Memory',
|
|
23
|
+
brain: 'Brain',
|
|
24
|
+
workflows: 'Workflows',
|
|
25
|
+
chat: 'Chat',
|
|
26
|
+
admin: 'Admin',
|
|
27
|
+
files: 'Files',
|
|
28
|
+
backups: 'Backups',
|
|
29
|
+
objectives: 'Objectives',
|
|
30
|
+
inbox: 'Inbox',
|
|
31
|
+
vault: 'Vault',
|
|
32
|
+
governance: 'Governance',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const HEALTH_DOT_COLORS = {
|
|
36
|
+
green: colors.success,
|
|
37
|
+
amber: colors.warning,
|
|
38
|
+
red: colors.error,
|
|
39
|
+
grey: colors.dim,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function formatClock(date) {
|
|
43
|
+
const hh = String(date.getHours()).padStart(2, '0');
|
|
44
|
+
const mm = String(date.getMinutes()).padStart(2, '0');
|
|
45
|
+
return `${hh}:${mm}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function formatAge(lastFetched, isStale) {
|
|
49
|
+
if (lastFetched == null) return '';
|
|
50
|
+
if (isStale) return 'stale';
|
|
51
|
+
const secs = Math.max(0, Math.floor((Date.now() - lastFetched) / 1000));
|
|
52
|
+
return `${secs}s`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function StatusBar() {
|
|
56
|
+
const ctx = useTui();
|
|
57
|
+
const config = ctx?.config;
|
|
58
|
+
const client = ctx?.client;
|
|
59
|
+
const screen = ctx?.screen || '';
|
|
60
|
+
|
|
61
|
+
// Try to extract user/org info from auth store
|
|
62
|
+
let userEmail = '';
|
|
63
|
+
let orgName = '';
|
|
64
|
+
let profileName = '';
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const profile = config?.getActiveProfile?.();
|
|
68
|
+
userEmail = profile?.auth?.email || profile?.auth?.user_email || '';
|
|
69
|
+
orgName = profile?.auth?.org_name || profile?.auth?.org_slug || '';
|
|
70
|
+
profileName = config?.getActiveProfileName?.() || '';
|
|
71
|
+
} catch {
|
|
72
|
+
// Config might not be loaded
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const { color: healthColor, loading: healthLoading, lastFetched, isStale } = useHealth(client);
|
|
76
|
+
|
|
77
|
+
// Clock — recomputed on a 30s tick (no 1s timer).
|
|
78
|
+
const [now, setNow] = useState(() => new Date());
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
const id = setInterval(() => setNow(new Date()), 30000);
|
|
81
|
+
return () => clearInterval(id);
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
const screenLabel = SCREEN_LABELS[screen] ?? (screen || '');
|
|
85
|
+
const ageText = formatAge(lastFetched, isStale);
|
|
86
|
+
const dotColor = HEALTH_DOT_COLORS[healthColor] || HEALTH_DOT_COLORS.grey;
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Box height={1} width="100%">
|
|
90
|
+
<Box flexGrow={1}>
|
|
91
|
+
<Text color={colors.primary} bold> QUOX</Text>
|
|
92
|
+
{screenLabel && (
|
|
93
|
+
<>
|
|
94
|
+
<Text color={colors.dim}> | </Text>
|
|
95
|
+
<Text color={colors.accent} bold>{screenLabel}</Text>
|
|
96
|
+
</>
|
|
97
|
+
)}
|
|
98
|
+
<Text color={colors.dim}> | </Text>
|
|
99
|
+
{userEmail ? (
|
|
100
|
+
<>
|
|
101
|
+
<Text color={colors.text}>{userEmail}</Text>
|
|
102
|
+
<Text color={colors.dim}> | </Text>
|
|
103
|
+
</>
|
|
104
|
+
) : (
|
|
105
|
+
<>
|
|
106
|
+
<Text color={colors.warning}>not logged in</Text>
|
|
107
|
+
<Text color={colors.dim}> | </Text>
|
|
108
|
+
</>
|
|
109
|
+
)}
|
|
110
|
+
{orgName && (
|
|
111
|
+
<>
|
|
112
|
+
<Text color={colors.accent}>{orgName}</Text>
|
|
113
|
+
<Text color={colors.dim}> | </Text>
|
|
114
|
+
</>
|
|
115
|
+
)}
|
|
116
|
+
{profileName && (
|
|
117
|
+
<Text color={colors.dim}>{profileName}</Text>
|
|
118
|
+
)}
|
|
119
|
+
</Box>
|
|
120
|
+
<Box>
|
|
121
|
+
{healthLoading && <Text color={colors.dim}>{'⟳ '}</Text>}
|
|
122
|
+
<Text color={dotColor}>{'●'}</Text>
|
|
123
|
+
{ageText && (
|
|
124
|
+
<Text color={isStale ? colors.warning : colors.dim}> {ageText}</Text>
|
|
125
|
+
)}
|
|
126
|
+
<Text color={colors.dim}> {formatClock(now)} </Text>
|
|
127
|
+
<Text color={colors.dim}>
|
|
128
|
+
{screen === 'menu' ? 'q:quit ?:help' : 'Esc:back ?:help q:quit'}
|
|
129
|
+
</Text>
|
|
130
|
+
</Box>
|
|
131
|
+
</Box>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toast — Auto-dismissing notification component
|
|
3
|
+
*
|
|
4
|
+
* Shows a message at the top of the view that auto-dismisses after a timeout.
|
|
5
|
+
* Supports success, error, warning, and info variants.
|
|
6
|
+
*
|
|
7
|
+
* Props:
|
|
8
|
+
* message - notification text
|
|
9
|
+
* type - 'success' | 'error' | 'warning' | 'info' (default: 'info')
|
|
10
|
+
* duration - auto-dismiss ms (default: 3000, 0 = no auto-dismiss)
|
|
11
|
+
* onDismiss- called when the toast is dismissed
|
|
12
|
+
* visible - whether the toast is shown (default: true when message is truthy)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import React, { useEffect } from 'react';
|
|
16
|
+
import { Box, Text } from 'ink';
|
|
17
|
+
import { colors } from '../theme.js';
|
|
18
|
+
|
|
19
|
+
const TYPE_CONFIG = {
|
|
20
|
+
success: { color: colors.success, icon: '\u2714' }, // ✔
|
|
21
|
+
error: { color: colors.error, icon: '\u2718' }, // ✘
|
|
22
|
+
warning: { color: colors.warning, icon: '\u26A0' }, // ⚠
|
|
23
|
+
info: { color: colors.cyberBlue, icon: '\u2139' }, // ℹ
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function Toast({ message, type = 'info', duration = 3000, onDismiss, visible }) {
|
|
27
|
+
const show = visible !== undefined ? visible : !!message;
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!show || duration <= 0) return;
|
|
31
|
+
const timer = setTimeout(() => {
|
|
32
|
+
onDismiss?.();
|
|
33
|
+
}, duration);
|
|
34
|
+
return () => clearTimeout(timer);
|
|
35
|
+
}, [show, duration, onDismiss]);
|
|
36
|
+
|
|
37
|
+
if (!show || !message) return null;
|
|
38
|
+
|
|
39
|
+
const config = TYPE_CONFIG[type] || TYPE_CONFIG.info;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Box
|
|
43
|
+
borderStyle="single"
|
|
44
|
+
borderColor={config.color}
|
|
45
|
+
paddingX={1}
|
|
46
|
+
marginBottom={1}
|
|
47
|
+
>
|
|
48
|
+
<Text color={config.color} bold>{config.icon} </Text>
|
|
49
|
+
<Text color={config.color}>{message}</Text>
|
|
50
|
+
</Box>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useApi — React hook for API calls with loading/error/data state
|
|
3
|
+
*
|
|
4
|
+
* Wraps an async fetch function and manages its lifecycle state.
|
|
5
|
+
* Automatically calls the fetch function on mount and when deps change.
|
|
6
|
+
* Provides a `refresh` function for manual re-fetching.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* const { data, loading, error, refresh } = useApi(
|
|
10
|
+
* () => client.fleet.list(),
|
|
11
|
+
* [client]
|
|
12
|
+
* );
|
|
13
|
+
*
|
|
14
|
+
* @param {Function} fetchFn - async function that returns data
|
|
15
|
+
* @param {Array} deps - dependency array (re-fetch when changed)
|
|
16
|
+
* @returns {{ data: any, loading: boolean, error: Error|null, refresh: Function }}
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
20
|
+
|
|
21
|
+
export function useApi(fetchFn, deps = []) {
|
|
22
|
+
const [data, setData] = useState(null);
|
|
23
|
+
const [loading, setLoading] = useState(true);
|
|
24
|
+
const [error, setError] = useState(null);
|
|
25
|
+
|
|
26
|
+
const refresh = useCallback(async () => {
|
|
27
|
+
setLoading(true);
|
|
28
|
+
setError(null);
|
|
29
|
+
try {
|
|
30
|
+
const result = await fetchFn();
|
|
31
|
+
setData(result);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
setError(err);
|
|
34
|
+
} finally {
|
|
35
|
+
setLoading(false);
|
|
36
|
+
}
|
|
37
|
+
}, deps);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
refresh();
|
|
41
|
+
}, [refresh]);
|
|
42
|
+
|
|
43
|
+
return { data, loading, error, refresh };
|
|
44
|
+
}
|