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,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LucidIntro: the ~1.6s startup for `quox lucid deck`.
|
|
3
|
+
*
|
|
4
|
+
* Frames (50ms): the Quoxon particle collapse (particles swarm in and lock into the horned Q, tail last),
|
|
5
|
+
* the β LUCID mark fades in under it, then "tuning inβ¦" resolves into
|
|
6
|
+
* "β LUCID β N sources live Β· M need you" once the first fetch has landed.
|
|
7
|
+
* The fetch is owned by the deck and runs in parallel, never gated on this.
|
|
8
|
+
* onDone fires once: at the natural end, on a hard 2.5s cap, or on a key
|
|
9
|
+
* (the deck calls skip via the `skipSignal` prop).
|
|
10
|
+
*/
|
|
11
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
12
|
+
import { Box, Text } from 'ink';
|
|
13
|
+
import { c, gradient, mono } from '../quoxon/theme.js';
|
|
14
|
+
import { QGlyph } from '../quoxon/qmotion.js';
|
|
15
|
+
import { Clock } from '../quoxon/motion/clock.js';
|
|
16
|
+
import { particleCellsAt, qCells, cellsToAnsi, labSize } from '../quoxon/qart.js';
|
|
17
|
+
|
|
18
|
+
export const FRAME_MS = 50;
|
|
19
|
+
const PARTICLE_MS = 1100; // the collapse; the Q is fully settled ~470ms later
|
|
20
|
+
const MIN_FRAMES = 32; // 1.6s: the Q settles, then the live line shows
|
|
21
|
+
const MAX_FRAMES = 50; // 2.5s cap if the fetch never lands
|
|
22
|
+
|
|
23
|
+
/** Rows of the particle Q at frame `frame` (motion off: the settled Q). Under 12 rows the compact Q. */
|
|
24
|
+
export function introQ(cols, rows, frame, motion = true) {
|
|
25
|
+
const size = labSize(cols, Math.max(0, rows - 8));
|
|
26
|
+
const color = !mono();
|
|
27
|
+
const t = frame * FRAME_MS;
|
|
28
|
+
return cellsToAnsi(motion ? particleCellsAt(size, t, { dur: PARTICLE_MS, color }) : qCells(size, { color }));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function LucidIntro({ data, err, onDone, rows = 24, cols = 100, motion = true }) {
|
|
32
|
+
const [frame, setFrame] = useState(0);
|
|
33
|
+
const doneRef = useRef(false);
|
|
34
|
+
const readyAt = useRef(null);
|
|
35
|
+
const sit = data && data.situation;
|
|
36
|
+
|
|
37
|
+
if (sit && readyAt.current == null) readyAt.current = frame;
|
|
38
|
+
|
|
39
|
+
// The deck's shared clock drives the intro too: one timer, armed only while the intro still has frames to play.
|
|
40
|
+
const clockRef = useRef(null);
|
|
41
|
+
if (!clockRef.current) clockRef.current = new Clock(() => setFrame((f) => f + 1));
|
|
42
|
+
useEffect(() => { if (!doneRef.current) clockRef.current.schedule([{ every: FRAME_MS }]); });
|
|
43
|
+
useEffect(() => () => clockRef.current.stop(), []);
|
|
44
|
+
|
|
45
|
+
const finishAt = readyAt.current != null
|
|
46
|
+
? Math.max(MIN_FRAMES, readyAt.current + 6)
|
|
47
|
+
: (err ? Math.max(MIN_FRAMES, 16) : MAX_FRAMES);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (frame >= finishAt && !doneRef.current) { doneRef.current = true; onDone(); }
|
|
51
|
+
}, [frame, finishAt, onDone]);
|
|
52
|
+
|
|
53
|
+
const qRows = introQ(cols, rows, frame, motion);
|
|
54
|
+
const markOn = frame >= 14;
|
|
55
|
+
const resolved = sit && frame >= 22 && frame >= (readyAt.current ?? 0) + 2;
|
|
56
|
+
const cov = (sit && sit.cannot_see && sit.cannot_see.coverage) || { live: 0, expected: 0 };
|
|
57
|
+
const dots = '.'.repeat((frame % 3) + 1).padEnd(3, ' ');
|
|
58
|
+
|
|
59
|
+
const mark = (
|
|
60
|
+
<Text>
|
|
61
|
+
<QGlyph state="working" ms={Date.now()} quiet={!motion} />
|
|
62
|
+
<Text> </Text>
|
|
63
|
+
{gradient('LUCID').map((g, i) => <Text key={i} bold color={g.color}>{g.ch}</Text>)}
|
|
64
|
+
</Text>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Box flexDirection="column" alignItems="center" justifyContent="center" flexGrow={1} paddingY={1}>
|
|
69
|
+
<Box flexDirection="column" alignItems="center">
|
|
70
|
+
{qRows.map((l, r) => <Text key={r} wrap="truncate">{l}</Text>)}
|
|
71
|
+
</Box>
|
|
72
|
+
<Box marginTop={1}>{markOn ? mark : <Text> </Text>}</Box>
|
|
73
|
+
<Box marginTop={1}>
|
|
74
|
+
{frame < 16 ? <Text> </Text> : resolved ? (
|
|
75
|
+
<Text>
|
|
76
|
+
<QGlyph state={(sit.needs_you || []).length ? 'waiting' : 'resting'} ms={Date.now()} quiet />
|
|
77
|
+
<Text bold color={c('sky')}> LUCID</Text>
|
|
78
|
+
<Text color={c('muted')}> β </Text>
|
|
79
|
+
<Text color={c('ok')}>{cov.live} sources live</Text>
|
|
80
|
+
<Text color={c('muted')}> Β· </Text>
|
|
81
|
+
<Text bold color={(sit.needs_you || []).length ? c('warn') : c('ok')}>{(sit.needs_you || []).length} need you</Text>
|
|
82
|
+
</Text>
|
|
83
|
+
) : err && !sit ? (
|
|
84
|
+
<Text color={c('error')}>LUCID unreachable, still trying</Text>
|
|
85
|
+
) : (
|
|
86
|
+
<Text color={c('muted')}>tuning in{dots}</Text>
|
|
87
|
+
)}
|
|
88
|
+
</Box>
|
|
89
|
+
<Box marginTop={1}><Text color={c('muted')}>any key to skip</Text></Box>
|
|
90
|
+
</Box>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `quox lucid blind`: what LUCID can and cannot hear, and what does not add up.
|
|
3
|
+
* Pure: renderBlind({ situation, feeds, expected }, { now }) -> string (plain text, no colour,
|
|
4
|
+
* so it pastes anywhere and reads the same in a pipe).
|
|
5
|
+
*
|
|
6
|
+
* A source is live, silent (expected but quiet), failing (speaking, but its own read is
|
|
7
|
+
* failing) or never heard. Contradictions list both sides of each mismatch.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
function ago(ms, now) {
|
|
11
|
+
if (ms == null) return '';
|
|
12
|
+
const s = Math.max(0, Math.floor((now - ms) / 1000));
|
|
13
|
+
if (s < 60) return `${s}s ago`;
|
|
14
|
+
const m = Math.floor(s / 60);
|
|
15
|
+
if (m < 60) return `${m}m ago`;
|
|
16
|
+
const h = Math.floor(m / 60);
|
|
17
|
+
if (h < 24) return `${h}h ago`;
|
|
18
|
+
return `${Math.floor(h / 24)}d ago`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const nameOf = (x) => (typeof x === 'string' ? x : (x && (x.feed || x.name)) || '');
|
|
22
|
+
|
|
23
|
+
/** Rows: { source, state, last, note } sorted worst first. */
|
|
24
|
+
export function blindRows({ situation, feeds = [], expected = [] } = {}, now = Date.now()) {
|
|
25
|
+
const cs = (situation && situation.cannot_see) || {};
|
|
26
|
+
const byFeed = new Map(feeds.map((f) => [f.feed, f]));
|
|
27
|
+
const failing = new Map((cs.failing || []).map((x) => [nameOf(x), x]));
|
|
28
|
+
const silent = new Set((cs.silent || []).map(nameOf));
|
|
29
|
+
const unwired = new Set((cs.unwired || []).map(nameOf));
|
|
30
|
+
const names = new Set([...byFeed.keys(), ...expected, ...failing.keys(), ...silent, ...unwired]);
|
|
31
|
+
const rows = [];
|
|
32
|
+
for (const source of names) {
|
|
33
|
+
if (!source) continue;
|
|
34
|
+
const f = byFeed.get(source);
|
|
35
|
+
const last = f && f.last_heard != null ? ago(f.last_heard, now) : '';
|
|
36
|
+
if (failing.has(source)) rows.push({ rank: 0, source, state: 'failing', last, note: (failing.get(source) || {}).error || (f && f.last_error) || '' });
|
|
37
|
+
else if (silent.has(source)) rows.push({ rank: 1, source, state: 'silent', last, note: 'expected, but gone quiet' });
|
|
38
|
+
else if (unwired.has(source) || !f) rows.push({ rank: 2, source, state: 'never heard', last: '', note: 'expected, has never reported' });
|
|
39
|
+
else rows.push({ rank: 3, source, state: 'live', last, note: '' });
|
|
40
|
+
}
|
|
41
|
+
return rows.sort((a, b) => a.rank - b.rank || a.source.localeCompare(b.source));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function renderBlind(data = {}, { now = Date.now() } = {}) {
|
|
45
|
+
const sit = data.situation || {};
|
|
46
|
+
const cov = (sit.cannot_see && sit.cannot_see.coverage) || null;
|
|
47
|
+
const rows = blindRows(data, now);
|
|
48
|
+
const out = [];
|
|
49
|
+
out.push(`SOURCES${cov ? ` (${cov.live} of ${cov.expected} speaking)` : ''}`);
|
|
50
|
+
if (!rows.length) out.push(' I have not heard from any source yet. That is not "all quiet".');
|
|
51
|
+
else {
|
|
52
|
+
const w = Math.max(6, ...rows.map((r) => r.source.length));
|
|
53
|
+
out.push(` ${'SOURCE'.padEnd(w)} ${'STATE'.padEnd(11)} ${'LAST HEARD'.padEnd(10)} NOTE`);
|
|
54
|
+
for (const r of rows) out.push(` ${r.source.padEnd(w)} ${r.state.padEnd(11)} ${r.last.padEnd(10)} ${r.note}`.trimEnd());
|
|
55
|
+
}
|
|
56
|
+
const stale = (sit.cannot_see && sit.cannot_see.stale) || [];
|
|
57
|
+
if (stale.length) out.push('', `Stale claims (source quiet, so these may be out of date): ${stale.map((s) => nameOf({ name: s.subject })).join(', ')}`);
|
|
58
|
+
|
|
59
|
+
const da = sit.doesnt_add_up || [];
|
|
60
|
+
out.push('', `DOESN'T ADD UP${da.length ? ` (${da.length})` : ''}`);
|
|
61
|
+
if (!da.length) out.push(' Nothing I can see contradicts anything else. I can only compare what the sources tell me.');
|
|
62
|
+
da.forEach((x, i) => {
|
|
63
|
+
out.push(` ${i + 1}. ${x.L0}${x.urgent ? ' [TODAY]' : ''}`);
|
|
64
|
+
out.push(` Claimed ${x.claimed}`);
|
|
65
|
+
out.push(` Observed ${x.observed}`);
|
|
66
|
+
if (x.sources && x.sources.length) out.push(` Sources ${x.sources.join(', ')}`);
|
|
67
|
+
if (x.why) out.push(` Why ${x.why}`);
|
|
68
|
+
if (x.how) out.push(` Settle ${x.how}`);
|
|
69
|
+
});
|
|
70
|
+
return `${out.join('\n')}\n`;
|
|
71
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LUCID object cards: pure views for the deck, in the Quoxon object-card shape ({ box, rows } of
|
|
3
|
+
* { text, role } segments, see quoxon/objects/model.js). An area is a tile card; a Needs-you item is a card
|
|
4
|
+
* that opens in place into who / what / why / when and the actions. Nothing here reads a clock or does I/O.
|
|
5
|
+
*
|
|
6
|
+
* Roles are theme roles; the renderer (LucidCard.jsx) also understands 'sel' (the selected action bar).
|
|
7
|
+
* Text is WRAPPED to the card, never clipped, except for a single action line and the command line.
|
|
8
|
+
*/
|
|
9
|
+
import { seg, fit, padRow } from '../quoxon/objects/model.js';
|
|
10
|
+
import { wrap, whenText, ageOf, GLYPH, clip } from './fmt.js';
|
|
11
|
+
|
|
12
|
+
export const URGENCY_ROLE = { now: 'error', today: 'warn', 'this week': 'sky', whenever: 'muted', fyi: 'muted' };
|
|
13
|
+
export const URGENCY_LABEL = { now: 'NOW', today: 'TODAY', 'this week': 'THIS WEEK', whenever: 'WHENEVER', fyi: 'FYI' };
|
|
14
|
+
export const STATE_ROLE = { ok: 'ok', attention: 'warn', unknown: 'muted', blind: 'error' };
|
|
15
|
+
export const STATE_WORD = { ok: 'ok', attention: 'attention', unknown: 'no signal', blind: 'blind' };
|
|
16
|
+
|
|
17
|
+
const nm = (x) => (typeof x === 'string' ? x : (x && (x.label || x.name || x.L0 || x.id)) || '');
|
|
18
|
+
const LW = 8; // label column of the open card
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* An area tile. d: { area, label, state, L0, last, n (needs), active (filter on), selected, focused,
|
|
22
|
+
* expanded, now }. A tile is one status row; the selected tile in a focused pane opens to its sentence.
|
|
23
|
+
*/
|
|
24
|
+
export function lucidArea(d, cols) {
|
|
25
|
+
const w = Math.max(18, cols), inner = w - 4;
|
|
26
|
+
const all = d.area == null;
|
|
27
|
+
const glyph = all ? 'β' : (GLYPH[d.state] || '?');
|
|
28
|
+
const role = all ? 'sky' : (STATE_ROLE[d.state] || 'muted');
|
|
29
|
+
const right = `${d.active ? 'β ' : ''}${d.n ? d.n : 'Β·'}`;
|
|
30
|
+
const rows = [];
|
|
31
|
+
const word = all ? 'everything' : (STATE_WORD[d.state] || 'unknown');
|
|
32
|
+
const age = d.last && d.last.ts != null && d.now != null ? `last ${ageOf(d.last.ts, d.now)}` : '';
|
|
33
|
+
const line = age && [...`${word} Β· ${age}`].length <= inner ? `${word} Β· ${age}` : word;
|
|
34
|
+
rows.push([seg(line, d.selected ? 'value' : role === 'ok' ? 'muted' : role)]);
|
|
35
|
+
if (d.expanded && d.L0) for (const ln of wrap(d.L0, inner)) rows.push([seg(ln, 'value')]);
|
|
36
|
+
const mark = d.selected ? 'βΆ ' : '';
|
|
37
|
+
return {
|
|
38
|
+
box: {
|
|
39
|
+
width: w, title: `${mark}${glyph} ${fit(d.label || d.area || 'All areas', Math.max(4, inner - 8))}`, titleRole: role,
|
|
40
|
+
right, rightRole: d.n ? 'warn' : 'muted', border: d.selected ? 'sky' : 'faint', sel: d.selected ? (d.focused ? 'focus' : 'dim') : null,
|
|
41
|
+
},
|
|
42
|
+
rows,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A Needs-you card. d: { item, areaLabel, isNew, selected, focused, open, mSel, now }.
|
|
48
|
+
* Collapsed: the wrapped sentence. Open: the sentence, who/what/why/when, affects, then the actions with
|
|
49
|
+
* the highlighted one as a bar and its exact command beneath.
|
|
50
|
+
*/
|
|
51
|
+
export function lucidNeed(d, cols) {
|
|
52
|
+
const it = d.item, w = Math.max(24, cols), inner = w - 4;
|
|
53
|
+
const urg = it.urgency || 'whenever';
|
|
54
|
+
const a = it.L1 || {};
|
|
55
|
+
const ms = (it.L2 && it.L2.measures) || [];
|
|
56
|
+
const mark = d.selected ? 'βΆ ' : '';
|
|
57
|
+
const rows = [];
|
|
58
|
+
const text = (d.isNew ? 'β ' : '') + (it.L0 || '');
|
|
59
|
+
wrap(text, inner).forEach((ln, i) => rows.push([seg(ln, d.isNew ? 'ok' : d.selected ? 'value' : 'text', d.selected)]));
|
|
60
|
+
if (d.open) {
|
|
61
|
+
const detail = (lbl, v) => wrap(v, inner - LW).forEach((ln, i) => rows.push([seg(i ? ' '.repeat(LW) : lbl.padEnd(LW), i ? 'muted' : 'label'), seg(ln, 'text')]));
|
|
62
|
+
for (const [lbl, v] of [['Who', a.who], ['What', a.what], ['Why', a.why], ['When', whenText(a.when, d.now)]]) if (v) detail(lbl, v);
|
|
63
|
+
if ((a.affected || []).length) rows.push([seg('Affects'.padEnd(LW), 'label'), seg(clip(a.affected.map(nm).filter(Boolean).join(', '), inner - LW), 'muted')]);
|
|
64
|
+
rows.push(ms.length ? [seg('What you can do', 'key', true)] : [seg('No actions to offer here. Esc closes.', 'muted')]);
|
|
65
|
+
ms.forEach((m, mi) => {
|
|
66
|
+
const on = mi === d.mSel;
|
|
67
|
+
const risk = ` ${m.risk}`;
|
|
68
|
+
const label = fit(m.say || m.label, Math.max(8, inner - 2 - risk.length));
|
|
69
|
+
if (on) rows.push([seg(`β― ${label}${risk}`.padEnd(inner, ' '), 'sel', true)]);
|
|
70
|
+
else rows.push([seg(' ', 'muted'), seg(label, 'text'), seg(risk, m.risk === 'dangerous' ? 'error' : m.risk === 'gated' ? 'warn' : 'muted')]);
|
|
71
|
+
});
|
|
72
|
+
const cmd = ms[d.mSel] && ms[d.mSel].command;
|
|
73
|
+
if (cmd) rows.push([seg(' $ ', 'muted'), seg(clip(cmd, inner - 4), 'code')]);
|
|
74
|
+
else if (ms.some((m) => m.command)) rows.push([seg(' ', 'muted')]);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
box: {
|
|
78
|
+
width: w, title: `${mark}${URGENCY_LABEL[urg] || String(urg).toUpperCase()} Β· ${it.kind || 'item'}`, titleRole: URGENCY_ROLE[urg] || 'muted',
|
|
79
|
+
right: d.areaLabel || '', rightRole: 'muted', border: d.selected ? 'sky' : (urg === 'now' ? 'error' : 'faint'), sel: d.selected ? (d.focused ? 'focus' : 'dim') : null,
|
|
80
|
+
},
|
|
81
|
+
rows,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const cardHeight = (view) => view.rows.length + 2;
|
|
86
|
+
export { padRow };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Launcher for the LUCID deck TUI. Alt screen via fullscreen-ink when the
|
|
3
|
+
* terminal supports raw mode; restores the terminal on every exit path.
|
|
4
|
+
*/
|
|
5
|
+
export async function launchDeck(api, { intervalMs = 10000, noIntro = false } = {}) {
|
|
6
|
+
if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== 'function' || !process.stdout.isTTY) {
|
|
7
|
+
console.error('quox lucid deck needs an interactive terminal. Use `quox lucid --sitrep` for a one-shot snapshot.');
|
|
8
|
+
process.exitCode = 1;
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const { shouldIntro } = await import('./fmt.js');
|
|
12
|
+
const intro = shouldIntro({ noIntro: noIntro || process.argv.includes('--no-intro'), isTTY: true });
|
|
13
|
+
// A long-running TUI must use React's production build: the development build keeps debug
|
|
14
|
+
// records per render and grew the heap steadily (measured flat at 14MB in production).
|
|
15
|
+
if (!process.env.NODE_ENV) process.env.NODE_ENV = 'production';
|
|
16
|
+
await import('../tui/index.js'); // registers the .jsx loader hook
|
|
17
|
+
const React = (await import('react')).default;
|
|
18
|
+
const { LucidDeck } = await import('../tui/views/LucidDeck.jsx');
|
|
19
|
+
const { withFullScreen } = await import('fullscreen-ink');
|
|
20
|
+
const app = withFullScreen(React.createElement(LucidDeck, { api, intervalMs, intro }), { exitOnCtrlC: true });
|
|
21
|
+
const restore = () => { try { process.stdout.write('\x1b[?1049l\x1b[?25h'); } catch { /* best effort */ } };
|
|
22
|
+
process.once('SIGTERM', () => { restore(); process.exit(143); });
|
|
23
|
+
process.once('SIGHUP', () => { restore(); process.exit(129); });
|
|
24
|
+
process.once('exit', restore);
|
|
25
|
+
process.once('uncaughtException', (e) => { restore(); console.error(e); process.exit(1); });
|
|
26
|
+
try {
|
|
27
|
+
await app.start();
|
|
28
|
+
await app.waitUntilExit();
|
|
29
|
+
} finally {
|
|
30
|
+
restore();
|
|
31
|
+
}
|
|
32
|
+
}
|
package/lib/lucid/fmt.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LUCID presentation helpers shared by `quox lucid --sitrep` and the deck TUI.
|
|
3
|
+
* Pure functions, no I/O. Colour is a parameter, never read from globals, so
|
|
4
|
+
* renderers are deterministic under test.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const GLYPH = { ok: 'β', attention: 'β ', unknown: '?', blind: 'β' };
|
|
8
|
+
export const STATE_HEX = { ok: '#22C55E', attention: '#FBBF24', unknown: '#A1A1AA', blind: '#EF4444' };
|
|
9
|
+
export const URGENCY = {
|
|
10
|
+
now: { label: 'NOW', hex: '#EF4444' },
|
|
11
|
+
today: { label: 'TODAY', hex: '#FBBF24' },
|
|
12
|
+
'this week': { label: 'THIS WEEK', hex: '#38BDF8' },
|
|
13
|
+
whenever: { label: 'WHENEVER', hex: '#71717A' },
|
|
14
|
+
fyi: { label: 'FYI', hex: '#71717A' },
|
|
15
|
+
};
|
|
16
|
+
export const PALETTE = {
|
|
17
|
+
brand: '#38BDF8', accent: '#C084FC', text: '#E4E4E7', muted: '#A1A1AA', dim: '#71717A', code: '#7DD3FC',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
21
|
+
export const stripAnsi = (s) => String(s).replace(ANSI_RE, '');
|
|
22
|
+
export const vlen = (s) => [...stripAnsi(s)].length;
|
|
23
|
+
|
|
24
|
+
/** Truncate a plain string to max visible chars with an ellipsis. */
|
|
25
|
+
export function clip(s, max) {
|
|
26
|
+
const a = [...String(s ?? '')];
|
|
27
|
+
if (a.length <= max) return a.join('');
|
|
28
|
+
return max <= 1 ? a.slice(0, max).join('') : a.slice(0, max - 1).join('') + 'β¦';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Word-wrap plain text to width; hard-breaks over-long words. Returns lines. */
|
|
32
|
+
export function wrap(text, width) {
|
|
33
|
+
width = Math.max(4, width);
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const para of String(text ?? '').split('\n')) {
|
|
36
|
+
let cur = '';
|
|
37
|
+
for (let w of para.split(/\s+/).filter(Boolean)) {
|
|
38
|
+
while ([...w].length > width) {
|
|
39
|
+
if (cur) { out.push(cur); cur = ''; }
|
|
40
|
+
out.push([...w].slice(0, width).join(''));
|
|
41
|
+
w = [...w].slice(width).join('');
|
|
42
|
+
}
|
|
43
|
+
if (!cur) cur = w;
|
|
44
|
+
else if ([...cur].length + 1 + [...w].length <= width) cur += ' ' + w;
|
|
45
|
+
else { out.push(cur); cur = w; }
|
|
46
|
+
}
|
|
47
|
+
out.push(cur);
|
|
48
|
+
}
|
|
49
|
+
return out.length ? out : [''];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function padEnd(s, width) {
|
|
53
|
+
const n = width - vlen(s);
|
|
54
|
+
return n > 0 ? s + ' '.repeat(n) : s;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** "3m", "2h", "4d" from a ms timestamp (relative to now). */
|
|
58
|
+
export function ageOf(ms, now = Date.now()) {
|
|
59
|
+
if (ms == null) return '';
|
|
60
|
+
const s = Math.max(0, Math.floor((now - ms) / 1000));
|
|
61
|
+
if (s < 60) return `${s}s`;
|
|
62
|
+
const m = Math.floor(s / 60);
|
|
63
|
+
if (m < 60) return `${m}m`;
|
|
64
|
+
const h = Math.floor(m / 60);
|
|
65
|
+
if (h < 24) return `${h}h`;
|
|
66
|
+
return `${Math.floor(h / 24)}d`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** β°β°β°β°β± meter, `cells` wide, filled proportionally (never rounds a gap to full). */
|
|
70
|
+
export function meter(live, expected, cells = 5) {
|
|
71
|
+
if (!expected) return 'β±'.repeat(cells);
|
|
72
|
+
let f = Math.round((live / expected) * cells);
|
|
73
|
+
if (live < expected && f >= cells) f = cells - 1;
|
|
74
|
+
if (live > 0 && f === 0) f = 1;
|
|
75
|
+
return 'β°'.repeat(f) + 'β±'.repeat(cells - f);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function clockLondon(ms = Date.now()) {
|
|
79
|
+
try {
|
|
80
|
+
return new Intl.DateTimeFormat('en-GB', {
|
|
81
|
+
timeZone: 'Europe/London', hour: '2-digit', minute: '2-digit', hour12: false,
|
|
82
|
+
}).format(new Date(ms));
|
|
83
|
+
} catch { return new Date(ms).toISOString().slice(11, 16); }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Plain-words account of a measure for the "do" line. */
|
|
87
|
+
export function firstMeasure(item) {
|
|
88
|
+
const ms = (item && item.L2 && item.L2.measures) || [];
|
|
89
|
+
return ms[0] || null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function whenText(w, now = Date.now()) {
|
|
93
|
+
if (!w) return '';
|
|
94
|
+
const parts = [];
|
|
95
|
+
if (w.clock) parts.push(w.clock);
|
|
96
|
+
else if (w.since) parts.push(`${ageOf(w.since, now)} ago`);
|
|
97
|
+
if (w.waited_ms != null && !w.clock) parts.push(`waiting ${ageOf(now - w.waited_ms, now)}`);
|
|
98
|
+
return parts.join(' Β· ');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Label for a changed[] entry in plain words. */
|
|
102
|
+
export function changedWords(c) {
|
|
103
|
+
const o = c.outcome ? `${c.outcome}` : (c.to ? `${c.from ? c.from + ' β ' : ''}${c.to}` : '');
|
|
104
|
+
return o;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Blind spot sentence list from cannot_see, names not counts where we have names. */
|
|
108
|
+
export function blindSpots(cs) {
|
|
109
|
+
cs = cs || {};
|
|
110
|
+
const nm = (x) => (typeof x === 'string' ? x : x && (x.feed || x.name || x.id || x.label)) || '';
|
|
111
|
+
const parts = [];
|
|
112
|
+
const silent = (cs.silent || []).map(nm).filter(Boolean);
|
|
113
|
+
const failing = (cs.failing || []).map(nm).filter(Boolean);
|
|
114
|
+
const stale = (cs.stale || []).map(nm).filter(Boolean);
|
|
115
|
+
const unwired = (cs.unwired || []).map(nm).filter(Boolean);
|
|
116
|
+
if (failing.length) parts.push({ k: 'failing', text: `failing: ${failing.join(', ')}` });
|
|
117
|
+
if (silent.length) parts.push({ k: 'silent', text: `silent: ${silent.join(', ')}` });
|
|
118
|
+
if (stale.length) parts.push({ k: 'stale', text: `stale: ${stale.join(', ')}` });
|
|
119
|
+
if (unwired.length) parts.push({ k: 'unwired', text: `not wired up: ${unwired.join(', ')}` });
|
|
120
|
+
return parts;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** HH:MM:SS London clock for the living deck header. */
|
|
124
|
+
export function clockLondonSec(ms = Date.now()) {
|
|
125
|
+
try {
|
|
126
|
+
return new Intl.DateTimeFormat('en-GB', {
|
|
127
|
+
timeZone: 'Europe/London', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false,
|
|
128
|
+
}).format(new Date(ms));
|
|
129
|
+
} catch { return new Date(ms).toISOString().slice(11, 19); }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const SPINNER = ['β ', 'β ', 'β Ή', 'β Έ', 'β Ό', 'β ΄', 'β ¦', 'β §', 'β ', 'β '];
|
|
133
|
+
|
|
134
|
+
/** Whether the startup intro should play. Pure so it is testable. */
|
|
135
|
+
export function shouldIntro({ noIntro = false, env = process.env, isTTY = true } = {}) {
|
|
136
|
+
if (noIntro || !isTTY) return false;
|
|
137
|
+
if (env.NO_COLOR || env.QUOX_TUI_LITE) return false;
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sitrep as chat Markdown (Discord, Matrix, slots): short, scannable, under one Discord message.
|
|
3
|
+
* Owner 2026-10-17: "Format your sitrep" after the spoken paragraph was posted to Discord as a wall of text.
|
|
4
|
+
* Pure: data in, string out. No colour codes, no box drawing (chat clients mangle both).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const URGENCY = { now: 'π΄', today: 'π ', this_week: 'π‘', week: 'π‘', whenever: 'βͺ', fyi: 'βͺ' };
|
|
8
|
+
const STATE = { attention: 'β οΈ', ok: 'β
', partial: 'π‘', unknown: 'β', blind: 'β' };
|
|
9
|
+
export const MAX_CHARS = 1900; // Discord's limit is 2000 per message
|
|
10
|
+
|
|
11
|
+
const clip = (s, n) => {
|
|
12
|
+
const t = String(s || '').replace(/\s+/g, ' ').trim();
|
|
13
|
+
return t.length > n ? `${t.slice(0, n - 1)}β¦` : t;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function age(ms) {
|
|
17
|
+
if (!(ms > 0)) return '';
|
|
18
|
+
const m = Math.floor(ms / 60000);
|
|
19
|
+
if (m < 60) return `${m}m`;
|
|
20
|
+
const h = Math.floor(m / 60);
|
|
21
|
+
if (h < 48) return `${h}h`;
|
|
22
|
+
return `${Math.floor(h / 24)}d`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function clockLondon(now) {
|
|
26
|
+
try {
|
|
27
|
+
return new Intl.DateTimeFormat('en-GB', { timeZone: 'Europe/London', hour: '2-digit', minute: '2-digit', timeZoneName: 'short' }).format(new Date(now));
|
|
28
|
+
} catch { return new Date(now).toISOString().slice(11, 16) + ' UTC'; }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** opts: { now, maxNeeds (default 6), maxChars (default MAX_CHARS) } */
|
|
32
|
+
export function renderSitrepMarkdown(data, opts = {}) {
|
|
33
|
+
const sit = (data && data.situation) || data || {};
|
|
34
|
+
const now = opts.now ?? Date.now();
|
|
35
|
+
const maxNeeds = opts.maxNeeds ?? 6;
|
|
36
|
+
const maxChars = opts.maxChars ?? MAX_CHARS;
|
|
37
|
+
const cov = sit.coverage || (sit.cannot_see && sit.cannot_see.coverage) || { live: 0, expected: 0 };
|
|
38
|
+
const missing = sit.coverage && Array.isArray(sit.coverage.missing) ? sit.coverage.missing : [];
|
|
39
|
+
const needs = Array.isArray(sit.needs_you) ? sit.needs_you : [];
|
|
40
|
+
const low = needs.filter((n) => n.urgency === 'whenever' || n.urgency === 'fyi').length;
|
|
41
|
+
|
|
42
|
+
const out = [];
|
|
43
|
+
out.push(`**β LUCID sitrep** Β· ${clockLondon(now)} Β· ${cov.live}/${cov.expected} sources`);
|
|
44
|
+
out.push('');
|
|
45
|
+
if (!needs.length) {
|
|
46
|
+
out.push('**Needs you:** nothing right now.');
|
|
47
|
+
} else {
|
|
48
|
+
out.push(`**Needs you (${needs.length}${low ? `, ${low} low priority` : ''})**`);
|
|
49
|
+
needs.slice(0, maxNeeds).forEach((n, i) => {
|
|
50
|
+
const w = n.L1 && n.L1.when && n.L1.when.waited_ms;
|
|
51
|
+
const tail = [n.area, age(w)].filter(Boolean).join(', ');
|
|
52
|
+
out.push(`${i + 1}. ${URGENCY[n.urgency] || 'β’'} ${clip(n.L0, 90)}${tail ? ` _(${tail})_` : ''}`);
|
|
53
|
+
});
|
|
54
|
+
if (needs.length > maxNeeds) out.push(`β¦and ${needs.length - maxNeeds} more: \`quox sitrep\``);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const areas = Array.isArray(sit.areas) ? sit.areas : [];
|
|
58
|
+
if (areas.length) {
|
|
59
|
+
out.push('');
|
|
60
|
+
out.push('**At a glance**');
|
|
61
|
+
for (const a of areas) out.push(`${STATE[a.state] || 'β’'} **${a.label || a.area}**: ${clip(a.L0, 80)}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const dau = Array.isArray(sit.doesnt_add_up) ? sit.doesnt_add_up : [];
|
|
65
|
+
if (dau.length) {
|
|
66
|
+
out.push('');
|
|
67
|
+
out.push(`**Doesn't add up (${dau.length}):** ${clip(dau[0].L0, 90)}${dau.length > 1 ? ' β¦' : ''}`);
|
|
68
|
+
}
|
|
69
|
+
out.push('');
|
|
70
|
+
out.push(missing.length ? `**Can't see:** ${missing.join(', ')}` : `**Can't see:** nothing, every source is reporting.`);
|
|
71
|
+
|
|
72
|
+
// Stay inside one chat message: drop "at a glance" rows from the bottom, then needs, never the header.
|
|
73
|
+
let text = out.join('\n');
|
|
74
|
+
while (text.length > maxChars && out.length > 3) {
|
|
75
|
+
const i = out.findLastIndex((l) => /^(β
|β οΈ|π‘|β|β|β’) \*\*/.test(l));
|
|
76
|
+
if (i < 0) break;
|
|
77
|
+
out.splice(i, 1);
|
|
78
|
+
text = out.join('\n');
|
|
79
|
+
}
|
|
80
|
+
return text.length > maxChars ? `${text.slice(0, maxChars - 1)}β¦` : text;
|
|
81
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LUCID deck motion: pure helpers for the deck's ONE clock (quoxon/motion/clock.js).
|
|
3
|
+
*
|
|
4
|
+
* Idle is still. The deck declares a demand each render; the clock keeps one timer for the soonest demand and
|
|
5
|
+
* none at all when nothing asks. Frames (~12 fps) are demanded ONLY while something is visibly moving: a fetch
|
|
6
|
+
* in flight (the Q works), the Q settling after a state change (a short window, then still colour), or a
|
|
7
|
+
* card dropping in. Everything else is a one-shot wake (toast expiry, ghost expiry, the minute tick).
|
|
8
|
+
*
|
|
9
|
+
* LUCID state -> Q state (qglyph.js): needs you = waiting, fetching = working, unreachable = failing,
|
|
10
|
+
* quiet = resting. Failing wins over working so an outage never reads as busy.
|
|
11
|
+
*/
|
|
12
|
+
import { FRAME_MS } from '../quoxon/motion/clock.js';
|
|
13
|
+
|
|
14
|
+
export { FRAME_MS };
|
|
15
|
+
/** After a state change the Q animates this long, then holds a still colour (no timer, no writes). */
|
|
16
|
+
export const SETTLE_MS = 2400;
|
|
17
|
+
/** A card dropping in is revealing for this long from its start (rows decode, then a short afterglow). */
|
|
18
|
+
export const REVEAL_MS = 1500;
|
|
19
|
+
/** Cards of one load start this far apart so the board fills top to bottom. */
|
|
20
|
+
export const STAGGER_MS = 70;
|
|
21
|
+
|
|
22
|
+
export function glyphStateOf({ err = null, fetching = false, needs = 0 } = {}) {
|
|
23
|
+
if (err) return 'failing';
|
|
24
|
+
if (fetching) return 'working';
|
|
25
|
+
return needs > 0 ? 'waiting' : 'resting';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Is the Q moving at `now`? Working moves while it lasts; every other state settles after SETTLE_MS. */
|
|
29
|
+
export function glyphAnimating({ state, since, now, motion }) {
|
|
30
|
+
if (!motion) return false;
|
|
31
|
+
if (state === 'working') return true;
|
|
32
|
+
return since != null && now - since < SETTLE_MS;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Seconds into a card's reveal, or Infinity when settled (or motion is off). A future start is negative. */
|
|
36
|
+
export function revealT(startAt, now, motion) {
|
|
37
|
+
if (!motion || startAt == null) return Infinity;
|
|
38
|
+
const ms = now - startAt;
|
|
39
|
+
return ms >= REVEAL_MS ? Infinity : ms / 1000;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The demands for this render. frames: something is moving right now. at: epoch ms of one-shot wakes
|
|
44
|
+
* (past or null entries are ignored). Without motion there are no frame demands at all.
|
|
45
|
+
*/
|
|
46
|
+
export function deckDemands({ motion = false, frames = false, at = [], now = Date.now() } = {}) {
|
|
47
|
+
const d = [];
|
|
48
|
+
if (motion && frames) d.push({ every: FRAME_MS });
|
|
49
|
+
let soonest = null;
|
|
50
|
+
for (const a of at) if (Number.isFinite(a) && a > now && (soonest == null || a < soonest)) soonest = a;
|
|
51
|
+
if (soonest != null) d.push({ at: soonest });
|
|
52
|
+
return d;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Next wall-clock minute boundary for a displayed time (the injected clock, so tests stay deterministic). */
|
|
56
|
+
export const nextMinuteAt = (wall, shown) => wall + (60000 - (shown % 60000));
|