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,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectivesView — Objectives (autonomous multi-step goals) management view
|
|
3
|
+
*
|
|
4
|
+
* Left panel: DataTable of objectives (title, status, progress)
|
|
5
|
+
* Right panel: DetailPanel with objective detail
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - `a` key approves a decomposed objective's plan (starts execution)
|
|
9
|
+
* - `s` key starts an objective (decomposes into tasks)
|
|
10
|
+
* - Auto-refreshes every 10 seconds via usePolledApi (only while active —
|
|
11
|
+
* never polls in the background when another screen is showing)
|
|
12
|
+
* - After `a`/`s`, an immediate refresh() is fired (decomposition/start
|
|
13
|
+
* happens async server-side — see services/tasks/lib/objectiveDispatch.js
|
|
14
|
+
* — so the list often hasn't caught up yet) and the acted-on row shows a
|
|
15
|
+
* transient "starting…"/"decomposing…" hint until a subsequent fetch
|
|
16
|
+
* (immediate refresh or the next 10s poll) reflects the new status.
|
|
17
|
+
*
|
|
18
|
+
* API (NOT org-scoped in the path — org/user scoping comes from the JWT):
|
|
19
|
+
* GET /api/objectives (service: tasks) -> data.objectives
|
|
20
|
+
* GET /api/objectives/:id (service: tasks) -> objective detail
|
|
21
|
+
* POST /api/objectives/:id/approve-plan (service: tasks)
|
|
22
|
+
* POST /api/objectives/:id/start (service: tasks)
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import React, { useState, useCallback, useEffect } from 'react';
|
|
26
|
+
import { Box, Text, useInput } from 'ink';
|
|
27
|
+
import { Layout } from '../components/Layout.jsx';
|
|
28
|
+
import { DataTable } from '../components/DataTable.jsx';
|
|
29
|
+
import { DetailPanel } from '../components/DetailPanel.jsx';
|
|
30
|
+
import { usePolledApi } from '../hooks/usePolledApi.js';
|
|
31
|
+
import { useTui } from '../app.jsx';
|
|
32
|
+
import { matchKey } from '../keys.js';
|
|
33
|
+
import { colors, styles } from '../theme.js';
|
|
34
|
+
import { resolveEndpoint } from '../../api/endpoints.js';
|
|
35
|
+
|
|
36
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Safely extract the org ID from TUI context.
|
|
40
|
+
*/
|
|
41
|
+
function getOrgId(ctx) {
|
|
42
|
+
return ctx?.auth?.getUser?.()?.org_id
|
|
43
|
+
|| ctx?.config?.getActiveProfile?.()?.auth?.org_id
|
|
44
|
+
|| null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Column definitions ────────────────────────────────────────────────
|
|
48
|
+
const COLUMNS = [
|
|
49
|
+
{ key: 'title', label: 'Title', width: 24 },
|
|
50
|
+
{ key: 'status', label: 'Status', width: 14 },
|
|
51
|
+
{ key: 'progress', label: 'Progress', width: 10 },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Format raw objective data into table rows.
|
|
56
|
+
* `pendingAction` (if present) is `{ id, hint }` for an in-flight
|
|
57
|
+
* start/approve action whose effect hasn't shown up in `objectives` yet.
|
|
58
|
+
*/
|
|
59
|
+
function formatRows(objectives, pendingAction) {
|
|
60
|
+
if (!Array.isArray(objectives)) return [];
|
|
61
|
+
return objectives.map((o) => {
|
|
62
|
+
const isPending = pendingAction && pendingAction.id === o.id;
|
|
63
|
+
const statusText = `${styles.statusDot(o.status || 'unknown')} ${o.status || 'unknown'}`;
|
|
64
|
+
return {
|
|
65
|
+
...o,
|
|
66
|
+
title: o.title || o.name || o.id,
|
|
67
|
+
status: isPending ? `${statusText} (${pendingAction.hint})` : statusText,
|
|
68
|
+
progress: (o.tasks_done != null || o.tasks_total != null)
|
|
69
|
+
? `${o.tasks_done || 0}/${o.tasks_total || 0}`
|
|
70
|
+
: '--',
|
|
71
|
+
_raw: o,
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Build a detail object for the right panel.
|
|
78
|
+
* `pendingAction` (if present and matching this objective) appends the
|
|
79
|
+
* transient "starting…"/"decomposing…" hint to the full-width Status line
|
|
80
|
+
* (the DataTable row's Status column is too narrow to show it in full).
|
|
81
|
+
*/
|
|
82
|
+
function buildDetail(raw, pendingAction) {
|
|
83
|
+
if (!raw) return null;
|
|
84
|
+
const isPending = pendingAction && pendingAction.id === raw.id;
|
|
85
|
+
return {
|
|
86
|
+
'Title': raw.title || raw.name,
|
|
87
|
+
'ID': raw.id,
|
|
88
|
+
'Status': isPending ? `${raw.status} (${pendingAction.hint})` : raw.status,
|
|
89
|
+
'Description': raw.description,
|
|
90
|
+
'Team': raw.team_id,
|
|
91
|
+
'Progress': (raw.tasks_done != null || raw.tasks_total != null)
|
|
92
|
+
? `${raw.tasks_done || 0}/${raw.tasks_total || 0}`
|
|
93
|
+
: '--',
|
|
94
|
+
'Created': raw.created_at,
|
|
95
|
+
'Updated': raw.updated_at,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── Component ─────────────────────────────────────────────────────────
|
|
100
|
+
export function ObjectivesView() {
|
|
101
|
+
const ctx = useTui();
|
|
102
|
+
const { client, screen, showToast, orgVersion } = ctx;
|
|
103
|
+
const isActive = screen === 'objectives';
|
|
104
|
+
const orgId = getOrgId(ctx);
|
|
105
|
+
|
|
106
|
+
const [selected, setSelected] = useState(0);
|
|
107
|
+
const [filter, setFilter] = useState(undefined);
|
|
108
|
+
// In-flight start/approve action: { id, previousStatus, hint }. Cleared
|
|
109
|
+
// once a fetch shows the objective's status has actually moved on, or
|
|
110
|
+
// after a safety timeout if it hasn't.
|
|
111
|
+
const [pendingAction, setPendingAction] = useState(null);
|
|
112
|
+
|
|
113
|
+
// ── Data fetching (auto-refreshes every 10s, only while active) ─────
|
|
114
|
+
const { data, loading, error, refresh } = usePolledApi(
|
|
115
|
+
async () => {
|
|
116
|
+
const res = await client.get(resolveEndpoint('objectives.list').path, { service: resolveEndpoint('objectives.list').service, query: { status: filter } });
|
|
117
|
+
return res?.objectives || res?.items || (Array.isArray(res) ? res : []);
|
|
118
|
+
},
|
|
119
|
+
[client, orgVersion, filter],
|
|
120
|
+
{ intervalMs: 10000, active: isActive }
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const rows = formatRows(data, pendingAction);
|
|
124
|
+
const selectedRow = rows[selected];
|
|
125
|
+
const detail = selectedRow ? buildDetail(selectedRow._raw, pendingAction) : null;
|
|
126
|
+
|
|
127
|
+
// ── Clear the pending hint once the objective's status has moved on ──
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (!pendingAction || !Array.isArray(data)) return;
|
|
130
|
+
const current = data.find((o) => o.id === pendingAction.id);
|
|
131
|
+
if (current && current.status !== pendingAction.previousStatus) {
|
|
132
|
+
setPendingAction(null);
|
|
133
|
+
}
|
|
134
|
+
}, [data, pendingAction]);
|
|
135
|
+
|
|
136
|
+
// Safety timeout: never leave the hint stuck if status genuinely hasn't
|
|
137
|
+
// changed by the time it fires (e.g. decomposition takes longer than this).
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
if (!pendingAction) return;
|
|
140
|
+
const t = setTimeout(() => setPendingAction(null), 15000);
|
|
141
|
+
return () => clearTimeout(t);
|
|
142
|
+
}, [pendingAction]);
|
|
143
|
+
|
|
144
|
+
// ── Approve plan ──────────────────────────────────────────────────
|
|
145
|
+
const approvePlan = useCallback(async () => {
|
|
146
|
+
if (!selectedRow) return;
|
|
147
|
+
const id = selectedRow._raw.id;
|
|
148
|
+
try {
|
|
149
|
+
await client.post(resolveEndpoint('objectives.approve-plan', { id }).path, { service: resolveEndpoint('objectives.approve-plan', { id }).service });
|
|
150
|
+
showToast('Objective started', 'success');
|
|
151
|
+
setPendingAction({ id, previousStatus: selectedRow._raw.status, hint: 'starting…' });
|
|
152
|
+
refresh();
|
|
153
|
+
} catch (err) {
|
|
154
|
+
showToast(err.message, 'error');
|
|
155
|
+
}
|
|
156
|
+
}, [selectedRow, client, refresh, showToast]);
|
|
157
|
+
|
|
158
|
+
// ── Start objective ───────────────────────────────────────────────
|
|
159
|
+
const startObjective = useCallback(async () => {
|
|
160
|
+
if (!selectedRow) return;
|
|
161
|
+
const id = selectedRow._raw.id;
|
|
162
|
+
try {
|
|
163
|
+
await client.post(resolveEndpoint('objectives.start', { id }).path, { service: resolveEndpoint('objectives.start', { id }).service, body: {} });
|
|
164
|
+
showToast('Objective started', 'success');
|
|
165
|
+
setPendingAction({ id, previousStatus: selectedRow._raw.status, hint: 'decomposing…' });
|
|
166
|
+
refresh();
|
|
167
|
+
} catch (err) {
|
|
168
|
+
showToast(err.message, 'error');
|
|
169
|
+
}
|
|
170
|
+
}, [selectedRow, client, refresh, showToast]);
|
|
171
|
+
|
|
172
|
+
// ── Keyboard handling ──────────────────────────────────────────────
|
|
173
|
+
useInput((input, key) => {
|
|
174
|
+
if (!isActive) return;
|
|
175
|
+
|
|
176
|
+
// Manual refresh
|
|
177
|
+
if (matchKey('refresh', input, key)) {
|
|
178
|
+
refresh();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Approve plan
|
|
183
|
+
if (input === 'a') {
|
|
184
|
+
approvePlan();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Start objective
|
|
189
|
+
if (input === 's') {
|
|
190
|
+
startObjective();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
}, { isActive });
|
|
194
|
+
|
|
195
|
+
// ── Footer hint ────────────────────────────────────────────────────
|
|
196
|
+
const footer = 'a: approve s: start r: refresh Esc: back';
|
|
197
|
+
|
|
198
|
+
// ── Render ─────────────────────────────────────────────────────────
|
|
199
|
+
return (
|
|
200
|
+
<Layout
|
|
201
|
+
title="Objectives"
|
|
202
|
+
subtitle={orgId ? `org: ${orgId}` : '(no org)'}
|
|
203
|
+
footer={footer}
|
|
204
|
+
left={
|
|
205
|
+
<Box flexDirection="column">
|
|
206
|
+
{error && (
|
|
207
|
+
<Box marginBottom={1}>
|
|
208
|
+
<Text color={colors.error}>Error: {error.message}</Text>
|
|
209
|
+
</Box>
|
|
210
|
+
)}
|
|
211
|
+
<DataTable
|
|
212
|
+
columns={COLUMNS}
|
|
213
|
+
rows={rows}
|
|
214
|
+
selected={selected}
|
|
215
|
+
onSelectionChange={setSelected}
|
|
216
|
+
loading={loading}
|
|
217
|
+
emptyMessage="No objectives yet — press s to start one, or create via `quox objective create`"
|
|
218
|
+
active={isActive}
|
|
219
|
+
/>
|
|
220
|
+
</Box>
|
|
221
|
+
}
|
|
222
|
+
right={
|
|
223
|
+
<DetailPanel
|
|
224
|
+
title={selectedRow ? (selectedRow._raw.title || selectedRow._raw.id) : undefined}
|
|
225
|
+
data={detail}
|
|
226
|
+
/>
|
|
227
|
+
}
|
|
228
|
+
/>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OrgView — Organization management view
|
|
3
|
+
*
|
|
4
|
+
* Left panel: DataTable of orgs (name, slug, members count)
|
|
5
|
+
* Right panel: DetailPanel with org detail (owner, plan, created)
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - `s` key switches active org (updates config)
|
|
9
|
+
* - Shows current org with a marker in the table
|
|
10
|
+
*
|
|
11
|
+
* API:
|
|
12
|
+
* GET /api/orgs (service: auth) -> data.orgs or data.organizations
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import React, { useState, useCallback } from 'react';
|
|
16
|
+
import { Box, Text, useInput } from 'ink';
|
|
17
|
+
import { Layout } from '../components/Layout.jsx';
|
|
18
|
+
import { DataTable } from '../components/DataTable.jsx';
|
|
19
|
+
import { DetailPanel } from '../components/DetailPanel.jsx';
|
|
20
|
+
import { useApi } from '../hooks/useApi.js';
|
|
21
|
+
import { useTui } from '../app.jsx';
|
|
22
|
+
import { matchKey } from '../keys.js';
|
|
23
|
+
import { colors } from '../theme.js';
|
|
24
|
+
import { resolveEndpoint } from '../../api/endpoints.js';
|
|
25
|
+
|
|
26
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Safely extract the current org ID from TUI context.
|
|
30
|
+
*/
|
|
31
|
+
function getOrgId(ctx) {
|
|
32
|
+
return ctx?.auth?.getUser?.()?.org_id
|
|
33
|
+
|| ctx?.config?.getActiveProfile?.()?.auth?.org_id
|
|
34
|
+
|| null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ── Column definitions ────────────────────────────────────────────────
|
|
38
|
+
const COLUMNS = [
|
|
39
|
+
{ key: 'marker', label: ' ', width: 3 },
|
|
40
|
+
{ key: 'name', label: 'Name', width: 22 },
|
|
41
|
+
{ key: 'slug', label: 'Slug', width: 16 },
|
|
42
|
+
{ key: 'members', label: 'Members', width: 10 },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Format raw org data into table rows.
|
|
47
|
+
* Marks the currently active org with a star.
|
|
48
|
+
*/
|
|
49
|
+
function formatRows(orgs, currentOrgId) {
|
|
50
|
+
if (!Array.isArray(orgs)) return [];
|
|
51
|
+
return orgs.map((o) => {
|
|
52
|
+
const id = o.id || o.org_id;
|
|
53
|
+
const isCurrent = id === currentOrgId;
|
|
54
|
+
return {
|
|
55
|
+
marker: isCurrent ? '\u2605' : ' ',
|
|
56
|
+
name: o.name || o.display_name || id,
|
|
57
|
+
slug: o.slug || '--',
|
|
58
|
+
members: o.member_count != null
|
|
59
|
+
? String(o.member_count)
|
|
60
|
+
: (Array.isArray(o.members) ? String(o.members.length) : '--'),
|
|
61
|
+
_raw: o,
|
|
62
|
+
_isCurrent: isCurrent,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Build a detail object for the right panel.
|
|
69
|
+
*/
|
|
70
|
+
function buildDetail(raw, isCurrent) {
|
|
71
|
+
if (!raw) return null;
|
|
72
|
+
return {
|
|
73
|
+
'Name': raw.name || raw.display_name,
|
|
74
|
+
'ID': raw.id || raw.org_id,
|
|
75
|
+
'Slug': raw.slug,
|
|
76
|
+
'Owner': raw.owner || raw.owner_email || raw.owner_id,
|
|
77
|
+
'Plan': raw.plan || raw.subscription || '--',
|
|
78
|
+
'Members': raw.member_count != null
|
|
79
|
+
? raw.member_count
|
|
80
|
+
: (Array.isArray(raw.members) ? raw.members.length : '--'),
|
|
81
|
+
'Active Org': isCurrent,
|
|
82
|
+
'Created': raw.created_at,
|
|
83
|
+
'Updated': raw.updated_at,
|
|
84
|
+
'Description': raw.description,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Merge an `auth.switch-org` response into the stored auth blob — the shared
|
|
90
|
+
* contract with `quox org switch`. The response carries the new org's info and
|
|
91
|
+
* (critically) an org-scoped access_token; persisting only a local org_id
|
|
92
|
+
* without this token leaves the OLD org's token in place. Exported for tests.
|
|
93
|
+
*/
|
|
94
|
+
export function mergeSwitchedOrgAuth(prevAuth, data, fallbackOrgId) {
|
|
95
|
+
const authData = { ...(prevAuth || {}) };
|
|
96
|
+
if (data?.org) {
|
|
97
|
+
authData.org_id = data.org.id;
|
|
98
|
+
authData.org_name = data.org.name;
|
|
99
|
+
authData.org_slug = data.org.slug;
|
|
100
|
+
authData.org_role = data.role;
|
|
101
|
+
} else {
|
|
102
|
+
authData.org_id = fallbackOrgId;
|
|
103
|
+
}
|
|
104
|
+
if (data?.access_token) authData.access_token = data.access_token;
|
|
105
|
+
return authData;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ── Component ─────────────────────────────────────────────────────────
|
|
109
|
+
export function OrgView() {
|
|
110
|
+
const ctx = useTui();
|
|
111
|
+
const { client, config, screen, onOrgChange } = ctx;
|
|
112
|
+
const isActive = screen === 'orgs';
|
|
113
|
+
const currentOrgId = getOrgId(ctx);
|
|
114
|
+
|
|
115
|
+
const [selected, setSelected] = useState(0);
|
|
116
|
+
const [actionMsg, setActionMsg] = useState(null);
|
|
117
|
+
|
|
118
|
+
// ── Data fetching ───────────────────────────────────────────────────
|
|
119
|
+
const { data, loading, error, refresh } = useApi(
|
|
120
|
+
async () => {
|
|
121
|
+
const res = await client.get(resolveEndpoint('orgs.list').path, { service: resolveEndpoint('orgs.list').service });
|
|
122
|
+
return res?.orgs || res?.organizations || [];
|
|
123
|
+
},
|
|
124
|
+
[client]
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const rows = formatRows(data, currentOrgId);
|
|
128
|
+
const selectedRow = rows[selected];
|
|
129
|
+
const detail = selectedRow
|
|
130
|
+
? buildDetail(selectedRow._raw, selectedRow._isCurrent)
|
|
131
|
+
: null;
|
|
132
|
+
|
|
133
|
+
// ── Switch active org ──────────────────────────────────────────────
|
|
134
|
+
const switchOrg = useCallback(async () => {
|
|
135
|
+
if (!selectedRow) return;
|
|
136
|
+
const raw = selectedRow._raw;
|
|
137
|
+
const newOrgId = raw.id || raw.org_id;
|
|
138
|
+
|
|
139
|
+
if (newOrgId === currentOrgId) {
|
|
140
|
+
setActionMsg('Already the active org');
|
|
141
|
+
setTimeout(() => setActionMsg(null), 2000);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
setActionMsg('Switching…');
|
|
146
|
+
try {
|
|
147
|
+
// Mirror `quox org switch`: POST switch-org to obtain the org-scoped
|
|
148
|
+
// access_token + canonical org info, then persist BOTH. The old code
|
|
149
|
+
// only poked a local org_id, leaving the previous org's token in place
|
|
150
|
+
// so every subsequent refetch hit the new org with the wrong token.
|
|
151
|
+
const ep = resolveEndpoint('auth.switch-org');
|
|
152
|
+
const data = await client.post(ep.path, { service: ep.service, body: { org_id: newOrgId } });
|
|
153
|
+
const nextAuth = mergeSwitchedOrgAuth(config?.getAuth?.(), data, newOrgId);
|
|
154
|
+
config?.setAuth?.(nextAuth);
|
|
155
|
+
|
|
156
|
+
setActionMsg(`Switched to ${data?.org?.name || raw.name || newOrgId}`);
|
|
157
|
+
refresh();
|
|
158
|
+
// Notify org-scoped views so they refetch against the new org.
|
|
159
|
+
onOrgChange?.();
|
|
160
|
+
} catch (err) {
|
|
161
|
+
setActionMsg(`Failed to switch: ${err.message}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
setTimeout(() => setActionMsg(null), 3000);
|
|
165
|
+
}, [selectedRow, currentOrgId, client, config, refresh, onOrgChange]);
|
|
166
|
+
|
|
167
|
+
// ── Keyboard handling ──────────────────────────────────────────────
|
|
168
|
+
useInput((input, key) => {
|
|
169
|
+
if (!isActive) return;
|
|
170
|
+
|
|
171
|
+
// Switch org
|
|
172
|
+
if (input === 's') {
|
|
173
|
+
switchOrg();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Manual refresh
|
|
178
|
+
if (matchKey('refresh', input, key)) {
|
|
179
|
+
refresh();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
}, { isActive });
|
|
183
|
+
|
|
184
|
+
// ── Footer hint ────────────────────────────────────────────────────
|
|
185
|
+
const footer = [
|
|
186
|
+
'\u2191/k \u2193/j Navigate',
|
|
187
|
+
's Switch org',
|
|
188
|
+
'r Refresh',
|
|
189
|
+
'Esc Back',
|
|
190
|
+
].join(' ');
|
|
191
|
+
|
|
192
|
+
// ── Render ─────────────────────────────────────────────────────────
|
|
193
|
+
return (
|
|
194
|
+
<Layout
|
|
195
|
+
title="Organizations"
|
|
196
|
+
subtitle={currentOrgId ? `active: ${currentOrgId}` : '(none active)'}
|
|
197
|
+
footer={footer}
|
|
198
|
+
left={
|
|
199
|
+
<Box flexDirection="column">
|
|
200
|
+
{error && (
|
|
201
|
+
<Box marginBottom={1}>
|
|
202
|
+
<Text color={colors.error}>Error: {error.message}</Text>
|
|
203
|
+
</Box>
|
|
204
|
+
)}
|
|
205
|
+
{actionMsg && (
|
|
206
|
+
<Box marginBottom={1}>
|
|
207
|
+
<Text color={colors.info}>{actionMsg}</Text>
|
|
208
|
+
</Box>
|
|
209
|
+
)}
|
|
210
|
+
<DataTable
|
|
211
|
+
columns={COLUMNS}
|
|
212
|
+
rows={rows}
|
|
213
|
+
selected={selected}
|
|
214
|
+
onSelectionChange={setSelected}
|
|
215
|
+
loading={loading}
|
|
216
|
+
emptyMessage="No organizations — create one via `quox org create`"
|
|
217
|
+
active={isActive}
|
|
218
|
+
/>
|
|
219
|
+
</Box>
|
|
220
|
+
}
|
|
221
|
+
right={
|
|
222
|
+
<DetailPanel
|
|
223
|
+
title={selectedRow ? (selectedRow._raw.name || 'Organization') : undefined}
|
|
224
|
+
data={detail}
|
|
225
|
+
/>
|
|
226
|
+
}
|
|
227
|
+
/>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolView — Tool catalog browser
|
|
3
|
+
*
|
|
4
|
+
* Left panel: DataTable of tools (name, type, category)
|
|
5
|
+
* Right panel: DetailPanel with tool description and parameters
|
|
6
|
+
*
|
|
7
|
+
* API (org-scoped):
|
|
8
|
+
* GET /api/orgs/:orgId/tools (service: auth) -> data.tools
|
|
9
|
+
* Tool detail comes from the list data itself (no separate endpoint needed).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import React, { useState, useEffect } from 'react';
|
|
13
|
+
import { Box, Text, useInput } from 'ink';
|
|
14
|
+
import { Layout } from '../components/Layout.jsx';
|
|
15
|
+
import { DataTable } from '../components/DataTable.jsx';
|
|
16
|
+
import { DetailPanel } from '../components/DetailPanel.jsx';
|
|
17
|
+
import { SearchInput } from '../components/SearchInput.jsx';
|
|
18
|
+
import { useApi } from '../hooks/useApi.js';
|
|
19
|
+
import { useTui } from '../app.jsx';
|
|
20
|
+
import { matchKey } from '../keys.js';
|
|
21
|
+
import { colors } from '../theme.js';
|
|
22
|
+
import { resolveEndpoint } from '../../api/endpoints.js';
|
|
23
|
+
|
|
24
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Safely extract the org ID from TUI context.
|
|
28
|
+
*/
|
|
29
|
+
function getOrgId(ctx) {
|
|
30
|
+
return ctx?.auth?.getUser?.()?.org_id
|
|
31
|
+
|| ctx?.config?.getActiveProfile?.()?.auth?.org_id
|
|
32
|
+
|| null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ── Column definitions ────────────────────────────────────────────────
|
|
36
|
+
const COLUMNS = [
|
|
37
|
+
{ key: 'name', label: 'Name', width: 22 },
|
|
38
|
+
{ key: 'type', label: 'Type', width: 14 },
|
|
39
|
+
{ key: 'category', label: 'Category', width: 14 },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Format raw tool data into table rows.
|
|
44
|
+
*/
|
|
45
|
+
function formatRows(tools) {
|
|
46
|
+
if (!Array.isArray(tools)) return [];
|
|
47
|
+
return tools.map((t) => ({
|
|
48
|
+
name: t.name || t.display_name || t.id,
|
|
49
|
+
type: t.type || '--',
|
|
50
|
+
category: t.category || '--',
|
|
51
|
+
_raw: t,
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Build a detail object for the right panel.
|
|
57
|
+
*/
|
|
58
|
+
function buildDetail(raw) {
|
|
59
|
+
if (!raw) return null;
|
|
60
|
+
const detail = {
|
|
61
|
+
'Name': raw.name || raw.display_name,
|
|
62
|
+
'ID': raw.id,
|
|
63
|
+
'Type': raw.type,
|
|
64
|
+
'Category': raw.category,
|
|
65
|
+
'Description': raw.description,
|
|
66
|
+
'Version': raw.version,
|
|
67
|
+
'Enabled': raw.enabled != null ? raw.enabled : raw.active,
|
|
68
|
+
'Created': raw.created_at,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Parameters
|
|
72
|
+
if (raw.parameters || raw.input_schema) {
|
|
73
|
+
const params = raw.parameters || raw.input_schema;
|
|
74
|
+
if (typeof params === 'object') {
|
|
75
|
+
detail['Parameters'] = params;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Tags
|
|
80
|
+
if (Array.isArray(raw.tags) && raw.tags.length > 0) {
|
|
81
|
+
detail['Tags'] = raw.tags;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return detail;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Filter rows by search term (matches name, type, category).
|
|
89
|
+
*/
|
|
90
|
+
function filterRows(rows, search) {
|
|
91
|
+
if (!search) return rows;
|
|
92
|
+
const term = search.toLowerCase();
|
|
93
|
+
return rows.filter((r) =>
|
|
94
|
+
(r.name || '').toLowerCase().includes(term) ||
|
|
95
|
+
(r.type || '').toLowerCase().includes(term) ||
|
|
96
|
+
(r.category || '').toLowerCase().includes(term)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── Component ─────────────────────────────────────────────────────────
|
|
101
|
+
export function ToolView() {
|
|
102
|
+
const ctx = useTui();
|
|
103
|
+
const { client, screen, setInputMode, orgVersion } = ctx;
|
|
104
|
+
const isActive = screen === 'tools';
|
|
105
|
+
const orgId = getOrgId(ctx);
|
|
106
|
+
|
|
107
|
+
const [selected, setSelected] = useState(0);
|
|
108
|
+
const [search, setSearch] = useState('');
|
|
109
|
+
const [searchActive, setSearchActive] = useState(false);
|
|
110
|
+
|
|
111
|
+
// ── Input-mode registry: claim 'input' while the search box is
|
|
112
|
+
// focused so global shortcuts (e.g. '?') don't steal keystrokes.
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
if (searchActive) setInputMode?.('input');
|
|
115
|
+
else if (isActive) setInputMode?.('nav');
|
|
116
|
+
}, [searchActive, isActive, setInputMode]);
|
|
117
|
+
|
|
118
|
+
// ── Data fetching (refetches when the active org changes) ───────────
|
|
119
|
+
const { data, loading, error, refresh } = useApi(
|
|
120
|
+
async () => {
|
|
121
|
+
if (!orgId) return [];
|
|
122
|
+
const res = await client.get(resolveEndpoint('tools.list', { orgId }).path, { service: resolveEndpoint('tools.list', { orgId }).service });
|
|
123
|
+
return res?.tools || [];
|
|
124
|
+
},
|
|
125
|
+
[client, orgId, orgVersion]
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const allRows = formatRows(data);
|
|
129
|
+
const rows = filterRows(allRows, search);
|
|
130
|
+
const selectedRow = rows[selected];
|
|
131
|
+
const detail = selectedRow ? buildDetail(selectedRow._raw) : null;
|
|
132
|
+
|
|
133
|
+
// ── Keyboard handling ──────────────────────────────────────────────
|
|
134
|
+
useInput((input, key) => {
|
|
135
|
+
if (!isActive) return;
|
|
136
|
+
|
|
137
|
+
// Toggle search focus
|
|
138
|
+
if (matchKey('search', input, key) && !searchActive) {
|
|
139
|
+
setSearchActive(true);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Escape from search
|
|
144
|
+
if (searchActive && matchKey('back', input, key)) {
|
|
145
|
+
setSearchActive(false);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (searchActive) return; // Let SearchInput handle keys
|
|
150
|
+
|
|
151
|
+
// Manual refresh
|
|
152
|
+
if (matchKey('refresh', input, key)) {
|
|
153
|
+
refresh();
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
}, { isActive });
|
|
157
|
+
|
|
158
|
+
// ── Reset selection when search changes ────────────────────────────
|
|
159
|
+
const handleSearchChange = (val) => {
|
|
160
|
+
setSearch(val);
|
|
161
|
+
setSelected(0);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// ── Footer hint ────────────────────────────────────────────────────
|
|
165
|
+
const footer = searchActive
|
|
166
|
+
? 'Type to filter Esc Cancel search'
|
|
167
|
+
: [
|
|
168
|
+
'\u2191/k \u2193/j Navigate',
|
|
169
|
+
'/ Search',
|
|
170
|
+
'r Refresh',
|
|
171
|
+
'Esc Back',
|
|
172
|
+
].join(' ');
|
|
173
|
+
|
|
174
|
+
// ── Render ─────────────────────────────────────────────────────────
|
|
175
|
+
return (
|
|
176
|
+
<Layout
|
|
177
|
+
title="Tools"
|
|
178
|
+
subtitle={orgId ? `${rows.length} tools` : '(no org)'}
|
|
179
|
+
footer={footer}
|
|
180
|
+
left={
|
|
181
|
+
<Box flexDirection="column">
|
|
182
|
+
{/* Search bar */}
|
|
183
|
+
<Box marginBottom={1}>
|
|
184
|
+
<SearchInput
|
|
185
|
+
placeholder="Filter tools..."
|
|
186
|
+
value={search}
|
|
187
|
+
onChange={handleSearchChange}
|
|
188
|
+
active={searchActive}
|
|
189
|
+
/>
|
|
190
|
+
</Box>
|
|
191
|
+
|
|
192
|
+
{error && (
|
|
193
|
+
<Box marginBottom={1}>
|
|
194
|
+
<Text color={colors.error}>Error: {error.message}</Text>
|
|
195
|
+
</Box>
|
|
196
|
+
)}
|
|
197
|
+
{!orgId && !loading && (
|
|
198
|
+
<Box marginBottom={1}>
|
|
199
|
+
<Text color={colors.warning}>No organization selected.</Text>
|
|
200
|
+
</Box>
|
|
201
|
+
)}
|
|
202
|
+
<DataTable
|
|
203
|
+
columns={COLUMNS}
|
|
204
|
+
rows={rows}
|
|
205
|
+
selected={selected}
|
|
206
|
+
onSelectionChange={setSelected}
|
|
207
|
+
loading={loading}
|
|
208
|
+
emptyMessage={search ? `No tools match "${search}" — press Esc to clear the filter` : 'No tools installed — add one via `quox tool create` or `quox tool install`'}
|
|
209
|
+
active={isActive && !searchActive}
|
|
210
|
+
/>
|
|
211
|
+
</Box>
|
|
212
|
+
}
|
|
213
|
+
right={
|
|
214
|
+
<DetailPanel
|
|
215
|
+
title={selectedRow ? (selectedRow._raw.name || selectedRow._raw.id) : undefined}
|
|
216
|
+
data={detail}
|
|
217
|
+
/>
|
|
218
|
+
}
|
|
219
|
+
/>
|
|
220
|
+
);
|
|
221
|
+
}
|