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,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `quox vm` : drive the HYPERION (agent id atlas) Proxmox tools from the CLI.
|
|
3
|
+
*
|
|
4
|
+
* quox vm list [--node n] [--type qemu|lxc]
|
|
5
|
+
* quox vm info <name|vmid>
|
|
6
|
+
* quox vm start|stop|reboot|shutdown <name|vmid> [--reason t]
|
|
7
|
+
* quox vm doctor <name|vmid> why is it slow or unhealthy? ranked findings with evidence (read only)
|
|
8
|
+
* quox vm snapshot <name|vmid> [--name s] [--reason t]
|
|
9
|
+
* quox vm rehearse <name|vmid> [--check url:..|unit:..|cmd:..] [--keep-twin] [--no-wait] -- <command>
|
|
10
|
+
* quox vm pool status|lease|done <vmid>|config
|
|
11
|
+
*
|
|
12
|
+
* Every action goes through the governed tool route (POST /api/v1/tools/execute).
|
|
13
|
+
* A held call prints its approval id and the `quox approvals result <id>` hint
|
|
14
|
+
* and is never re-submitted. Engine shared with the QUOXON /vm command: lib/core/vm.js.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Option } from 'commander';
|
|
18
|
+
import { resolveOrgId } from './helpers.js';
|
|
19
|
+
import { formatOutput, printInfo, printWarning } from '../core/output.js';
|
|
20
|
+
import {
|
|
21
|
+
VM_ACTIONS, VmPendingError, pendingHint, makeExec, makePoolRest,
|
|
22
|
+
listVms, vmInfo, vmDoctor, doctorLines, vmAction, vmSnapshot, vmRehearse, waitRehearsal, poolStatus, poolLease, poolDone, poolConfig, poolHostKeyLines,
|
|
23
|
+
readPubkey, textOf, payloadOf,
|
|
24
|
+
} from '../core/vm.js';
|
|
25
|
+
|
|
26
|
+
export function registerVmCommands(program, ctx) {
|
|
27
|
+
const { client, auth } = ctx;
|
|
28
|
+
const json = () => program.opts().output === 'json';
|
|
29
|
+
|
|
30
|
+
const vm = program
|
|
31
|
+
.command('vm')
|
|
32
|
+
.description('Proxmox VMs via the HYPERION agent tools (governed: held calls wait for approval)');
|
|
33
|
+
|
|
34
|
+
/** Run one verb: auth, exec, and turn a held call into the approval hint (exit 0, no re-submit). */
|
|
35
|
+
const run = (fn) => async (...args) => {
|
|
36
|
+
auth.requireAuth();
|
|
37
|
+
const exec = makeExec({ client, orgId: resolveOrgId(auth, program) });
|
|
38
|
+
try { await fn(exec, ...args); }
|
|
39
|
+
catch (e) {
|
|
40
|
+
if (!(e instanceof VmPendingError)) throw e;
|
|
41
|
+
if (json()) console.log(JSON.stringify({ pending: true, tool: e.tool, approvalId: e.approvalId, inbox_item_id: e.inboxItemId }));
|
|
42
|
+
else printWarning(pendingHint(e));
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const emit = (data, human) => {
|
|
46
|
+
if (json()) console.log(JSON.stringify(data, null, 2));
|
|
47
|
+
else human();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Read-only cluster views (HYPERION read tools; REST first, Bastion pvesh-read fallback).
|
|
51
|
+
vm.command('cluster')
|
|
52
|
+
.description('Proxmox cluster health: quorum and nodes (read only)')
|
|
53
|
+
.action(run(async (exec) => {
|
|
54
|
+
const p = payloadOf(await exec('proxmox_cluster_status', {}));
|
|
55
|
+
emit(p, () => console.log(textOf(p)));
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
vm.command('ha')
|
|
59
|
+
.description('Proxmox HA status (read only)')
|
|
60
|
+
.action(run(async (exec) => {
|
|
61
|
+
const p = payloadOf(await exec('proxmox_ha_status', {}));
|
|
62
|
+
emit(p, () => console.log(textOf(p)));
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
vm.command('templates')
|
|
66
|
+
.description('Golden templates you can clone from (read only)')
|
|
67
|
+
.action(run(async (exec) => { const p = payloadOf(await exec('proxmox_templates_list', {})); emit(p, () => console.log(textOf(p))); }));
|
|
68
|
+
|
|
69
|
+
vm.command('storage')
|
|
70
|
+
.description('Proxmox storage pools and usage (read only)')
|
|
71
|
+
.action(run(async (exec) => { const p = payloadOf(await exec('proxmox_storage', {})); emit(p, () => console.log(textOf(p))); }));
|
|
72
|
+
|
|
73
|
+
vm.command('node <name>')
|
|
74
|
+
.description('CPU, memory and storage of one Proxmox node (read only)')
|
|
75
|
+
.action(run(async (exec, name) => {
|
|
76
|
+
if (!/^[a-z0-9-]{1,32}$/.test(name)) throw new Error('node name must be lowercase letters, digits or -');
|
|
77
|
+
const p = payloadOf(await exec('proxmox_node_resources', { node: name })); emit(p, () => console.log(textOf(p)));
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
vm.command('resolve <name>')
|
|
81
|
+
.description('Find which node and VMID a VM name lives on (read only)')
|
|
82
|
+
.action(run(async (exec, name) => {
|
|
83
|
+
const p = payloadOf(await exec('proxmox_vm_resolve', /^\d+$/.test(name) ? { vmid: Number(name) } : { name }));
|
|
84
|
+
emit(p, () => console.log(textOf(p)));
|
|
85
|
+
}));
|
|
86
|
+
|
|
87
|
+
vm.command('list')
|
|
88
|
+
.description('List VMs (--type lxc lists containers)')
|
|
89
|
+
.option('--node <name>', 'only this Proxmox node')
|
|
90
|
+
.option('--type <kind>', 'qemu | lxc', undefined)
|
|
91
|
+
.action(run(async (exec, options) => {
|
|
92
|
+
const vms = await listVms(exec, { node: options.node, type: options.type });
|
|
93
|
+
if (json()) { console.log(JSON.stringify(vms.map((v) => v.raw), null, 2)); return; }
|
|
94
|
+
formatOutput(
|
|
95
|
+
{ columns: ['vmid', 'name', 'node', 'type', 'status'], rows: vms.map(({ raw, ...r }) => r) },
|
|
96
|
+
{ emptyMessage: 'No VMs found.' },
|
|
97
|
+
);
|
|
98
|
+
}));
|
|
99
|
+
|
|
100
|
+
vm.command('agents')
|
|
101
|
+
.description('Which running VMs answer on the QEMU guest agent: counts and the VMs that do not (read only)')
|
|
102
|
+
.option('--node <name>', 'only this Proxmox node')
|
|
103
|
+
.option('--limit <n>', 'how many gaps to list (default 25, max 50)')
|
|
104
|
+
.action(run(async (exec, options) => {
|
|
105
|
+
const p = payloadOf(await exec('proxmox_vms', { guest: true, ...(options.node ? { node: options.node } : {}), ...(options.limit ? { limit: Number(options.limit) } : {}) }));
|
|
106
|
+
emit(p, () => {
|
|
107
|
+
printInfo(`Guest agent: ${p.agent_running} of ${p.probed} running VMs answer (${p.not_enabled} not enabled, ${p.not_running} not running, ${p.unknown} unknown; ${p.stopped} stopped VMs not checked)`);
|
|
108
|
+
formatOutput({ columns: ['vmid', 'name', 'node', 'agent'], rows: p.gaps || [] }, { emptyMessage: 'Every running VM answers on the guest agent.' });
|
|
109
|
+
if (p.gaps_truncated) printWarning(`${p.gaps_truncated} more not shown (--limit)`);
|
|
110
|
+
});
|
|
111
|
+
}));
|
|
112
|
+
|
|
113
|
+
vm.command('info <target>')
|
|
114
|
+
.description('Resources and state of one VM (name or vmid)')
|
|
115
|
+
.action(run(async (exec, target) => {
|
|
116
|
+
const { vm: v, resources } = await vmInfo(exec, target);
|
|
117
|
+
emit({ vm: v, resources }, () => {
|
|
118
|
+
printInfo(`${v.name || v.vmid} vmid ${v.vmid} node ${v.node} ${v.type}`);
|
|
119
|
+
console.log(textOf(resources));
|
|
120
|
+
});
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
vm.command('doctor <target>')
|
|
124
|
+
.description('Why is this VM slow or unhealthy? Verdict and ranked findings with evidence and a suggested fix (read only; name or vmid)')
|
|
125
|
+
.action(run(async (exec, target) => {
|
|
126
|
+
const report = await vmDoctor(exec, target);
|
|
127
|
+
emit(report, () => { for (const l of doctorLines(report)) console.log(l); });
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
for (const action of VM_ACTIONS) {
|
|
131
|
+
vm.command(`${action} <target>`)
|
|
132
|
+
.description(`${action[0].toUpperCase()}${action.slice(1)} a VM (name or vmid); write, may need approval`)
|
|
133
|
+
.option('--reason <text>', 'why (recorded as _reason on the call)')
|
|
134
|
+
.action(run(async (exec, target, options) => {
|
|
135
|
+
const { vm: v, result } = await vmAction(exec, action, target, { reason: options.reason });
|
|
136
|
+
emit({ vm: v, action, result }, () => {
|
|
137
|
+
printInfo(`${action} ${v.name || v.vmid} (vmid ${v.vmid} on ${v.node})`);
|
|
138
|
+
console.log(textOf(result));
|
|
139
|
+
});
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
vm.command('snapshot <target>')
|
|
144
|
+
.description('Snapshot a VM (name or vmid); write, may need approval')
|
|
145
|
+
.option('--name <snapshot>', 'snapshot name')
|
|
146
|
+
.option('--reason <text>', 'why (recorded as _reason on the call)')
|
|
147
|
+
.action(run(async (exec, target, options) => {
|
|
148
|
+
const { vm: v, result } = await vmSnapshot(exec, target, { name: options.name, reason: options.reason });
|
|
149
|
+
emit({ vm: v, result }, () => { printInfo(`snapshot ${v.name || v.vmid} (vmid ${v.vmid})`); console.log(textOf(result)); });
|
|
150
|
+
}));
|
|
151
|
+
|
|
152
|
+
vm.command('rehearse <target> <command...>')
|
|
153
|
+
.description('Rehearse a command on a network-isolated twin of a VM (snapshot, full clone, health checks); slow, writes a snapshot and a VM. Put the command after --')
|
|
154
|
+
.option('--check <spec>', 'health check before/after: url:http://127.0.0.1:PORT/path | unit:NAME | cmd:COMMAND (repeatable)', (v, acc) => [...acc, v], [])
|
|
155
|
+
.option('--keep-twin', 'leave the stopped, isolated twin in place')
|
|
156
|
+
.addOption(new Option('--poll-ms <ms>', 'status poll interval in milliseconds (default 10000)').hideHelp())
|
|
157
|
+
.option('--no-wait', 'print the rehearsal id and return; do not wait for the verdict')
|
|
158
|
+
.action(run(async (exec, target, commandWords, options) => {
|
|
159
|
+
const { vm: v, result: started } = await vmRehearse(exec, target, commandWords.join(' '), { checks: options.check, keepTwin: !!options.keepTwin });
|
|
160
|
+
const id = started?.rehearsal_id;
|
|
161
|
+
if (options.wait === false || !id) {
|
|
162
|
+
emit({ vm: v, result: started }, () => { printInfo(`rehearsal of ${v.name || v.vmid} started${id ? `: ${id}` : ''}`); console.log(textOf(started)); });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (started?.state === 'awaiting_approval') {
|
|
166
|
+
if (!json()) printWarning(`${started.hint} The approval card lists every host command; approving it starts the run.`);
|
|
167
|
+
if (!json()) printInfo(`waiting for approval and the run (rehearsal ${id}); Ctrl-C is safe, check later with the proxmox_rehearsal_status tool`);
|
|
168
|
+
} else if (!json()) printInfo(`rehearsal ${id} of ${v.name || v.vmid} (vmid ${v.vmid} on ${v.node}) running; this takes minutes (a full disk copy)`);
|
|
169
|
+
const result = await waitRehearsal(exec, id, { intervalMs: Number(options.pollMs) || undefined });
|
|
170
|
+
emit({ vm: v, result }, () => {
|
|
171
|
+
printInfo(`rehearsal of ${v.name || v.vmid} (vmid ${v.vmid} on ${v.node}): ${String(result?.verdict || 'unknown').toUpperCase()} id ${id}`);
|
|
172
|
+
if (result?.error) printWarning(result.error);
|
|
173
|
+
console.log(textOf(result));
|
|
174
|
+
});
|
|
175
|
+
}));
|
|
176
|
+
|
|
177
|
+
const pool = vm.command('pool').description('VM pool: status, lease, done, config');
|
|
178
|
+
|
|
179
|
+
pool.command('status')
|
|
180
|
+
.description('Pool capacity and current leases')
|
|
181
|
+
.action(run(async (exec) => {
|
|
182
|
+
const p = await poolStatus(makePoolRest({ client }));
|
|
183
|
+
emit(p, () => {
|
|
184
|
+
console.log(textOf(p));
|
|
185
|
+
for (const v of (p && Array.isArray(p.vms) ? p.vms : [])) if (v.ip_stale) console.log(`vmid ${v.vmid}: ${v.ip} (ip may be stale)`);
|
|
186
|
+
});
|
|
187
|
+
}));
|
|
188
|
+
|
|
189
|
+
pool.command('lease')
|
|
190
|
+
.description('Lease a pool VM as you (needs your SSH public key); a warm VM returns at once, a bigger one is built to order (poll pool status)')
|
|
191
|
+
.option('--lessee <name>', 'who is leasing (default $QUOX_SESSION or hostname)')
|
|
192
|
+
.option('--pubkey-file <path>', 'SSH public key file (default ~/.ssh/id_ed25519.pub if present)')
|
|
193
|
+
.option('--cores <n>', 'vCPU cores')
|
|
194
|
+
.option('--memory-gb <n>', 'memory in GB')
|
|
195
|
+
.option('--disk-gb <n>', 'disk in GB')
|
|
196
|
+
.option('--ttl-h <n>', 'hours until the lease expires and the VM is destroyed (default and maximum: the pool lease_ttl_h)')
|
|
197
|
+
.option('--purpose <text>', 'what the VM is for')
|
|
198
|
+
.option('--avx', 'place only on an AVX2 lane (needed for local LLM chat); errors if no lane has avx2, never falls back')
|
|
199
|
+
.option('--node <name>', 'pin the VM to this pool lane (an active, non-denied lane host)')
|
|
200
|
+
.action(run(async (exec, options) => {
|
|
201
|
+
const pubkey = readPubkey(options.pubkeyFile);
|
|
202
|
+
const p = await poolLease(makePoolRest({ client }), {
|
|
203
|
+
lessee: options.lessee, pubkey, cores: options.cores, memoryGb: options.memoryGb,
|
|
204
|
+
diskGb: options.diskGb, ttlH: options.ttlH, purpose: options.purpose, avx: options.avx, node: options.node,
|
|
205
|
+
});
|
|
206
|
+
emit(p, () => { console.log(textOf(p)); for (const line of poolHostKeyLines(p)) console.log(line); });
|
|
207
|
+
}));
|
|
208
|
+
|
|
209
|
+
pool.command('done <vmid>')
|
|
210
|
+
.description('Release a leased pool VM as you; it stops being leasable at once, then is destroyed and the pool re-warms')
|
|
211
|
+
.action(run(async (exec, vmid, options) => {
|
|
212
|
+
const p = await poolDone(makePoolRest({ client }), vmid);
|
|
213
|
+
emit(p, () => console.log(poolDoneLine(p, vmid)));
|
|
214
|
+
}));
|
|
215
|
+
|
|
216
|
+
pool.command('config')
|
|
217
|
+
.description('Configure the pool (JSON settings via --set); org admins only, acting directly')
|
|
218
|
+
.option('--set <json>', 'settings as a JSON object, e.g. {"admin_keys":["ssh-ed25519 AAAA... owner"],"lanes":[{"node":"hyp-01","template_vmid":7630,"avx2":true}]} (omit to send an empty configure)')
|
|
219
|
+
.action(run(async (exec, options) => {
|
|
220
|
+
let input = {};
|
|
221
|
+
if (options.set) {
|
|
222
|
+
try { input = JSON.parse(options.set); } catch { throw new Error('--set must be a JSON object'); }
|
|
223
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new Error('--set must be a JSON object');
|
|
224
|
+
}
|
|
225
|
+
const p = await poolConfig(makePoolRest({ client }), input);
|
|
226
|
+
emit(p, () => console.log(textOf(p)));
|
|
227
|
+
}));
|
|
228
|
+
// `quox vm node reboot|shutdown` is intentionally not registered: the
|
|
229
|
+
// proxmox_node_power tool is not built yet, so the CLI must not advertise it.
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** One plain line for `vm pool done`: 202 {state:'recycling'} (current servers) or {released:{destroyed}} (older ones). */
|
|
233
|
+
export function poolDoneLine(p, vmid) {
|
|
234
|
+
if (p && p.state === 'recycling') return `VM ${p.vmid ?? vmid} is recycling: nobody can lease it now. Destroy and re-warm finish in the background; check with quox vm pool status.`;
|
|
235
|
+
if (p && p.released && p.released.destroyed) return `VM ${p.released.vmid ?? vmid} destroyed; the pool re-warms.`;
|
|
236
|
+
return textOf(p);
|
|
237
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* quox#695: `quox voice numbers list|set|remove`, per-number telephony org
|
|
3
|
+
* binding. Collector routes under /api/v1/telephony/numbers (org-scoped;
|
|
4
|
+
* writes are org-admin only).
|
|
5
|
+
*/
|
|
6
|
+
import { formatOutput, printSuccess } from '../core/output.js';
|
|
7
|
+
import { resolveEndpoint } from '../api/endpoints.js';
|
|
8
|
+
import { QuoxApiError } from '../core/errors.js';
|
|
9
|
+
import { resolveOrgId } from './helpers.js';
|
|
10
|
+
|
|
11
|
+
export function friendlyNumbersError(err) {
|
|
12
|
+
if (err instanceof QuoxApiError) {
|
|
13
|
+
if (err.status === 409) return new QuoxApiError(409, err.code, 'that number belongs to another org', err.detail, err.body);
|
|
14
|
+
if (err.status === 400) return new QuoxApiError(400, err.code, 'use E.164 format, e.g. +447700900123', err.detail, err.body);
|
|
15
|
+
if (err.status === 403) return new QuoxApiError(403, err.code, 'only an org admin can bind numbers', err.detail, err.body);
|
|
16
|
+
}
|
|
17
|
+
return err;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function parseOnOff(value, flag) {
|
|
21
|
+
const v = String(value).trim().toLowerCase();
|
|
22
|
+
if (['on', 'true', 'yes', '1'].includes(v)) return true;
|
|
23
|
+
if (['off', 'false', 'no', '0'].includes(v)) return false;
|
|
24
|
+
throw new Error(`${flag} must be on or off`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function registerVoiceCommands(program, ctx) {
|
|
28
|
+
const { client, auth } = ctx;
|
|
29
|
+
|
|
30
|
+
const voice = program.command('voice').description('QuoxVoice telephony');
|
|
31
|
+
const numbers = voice.command('numbers').description('Bind phone numbers to this org');
|
|
32
|
+
|
|
33
|
+
numbers
|
|
34
|
+
.command('list')
|
|
35
|
+
.description('List numbers bound to the active org')
|
|
36
|
+
.action(async () => {
|
|
37
|
+
auth.requireAuth();
|
|
38
|
+
const ep = resolveEndpoint('telephony.numbers.list');
|
|
39
|
+
const data = await client.get(ep.path, { service: ep.service, query: { org_id: resolveOrgId(auth, program) } });
|
|
40
|
+
const list = data.numbers || [];
|
|
41
|
+
formatOutput(list.map((n) => ({
|
|
42
|
+
NUMBER: n.number,
|
|
43
|
+
LABEL: n.label || '',
|
|
44
|
+
ORG: n.org_id,
|
|
45
|
+
UPDATED: n.updated_at || n.created_at || '',
|
|
46
|
+
})), {
|
|
47
|
+
format: program.opts().output,
|
|
48
|
+
columns: ['NUMBER', 'LABEL', 'ORG', 'UPDATED'],
|
|
49
|
+
emptyMessage: 'No numbers bound. Bind one with `quox voice numbers set <number>`.',
|
|
50
|
+
raw: list,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
numbers
|
|
55
|
+
.command('set <number>')
|
|
56
|
+
.description('Bind a number (E.164) to the active org, optionally with a label and policy')
|
|
57
|
+
.option('--label <text>', 'human label for the number')
|
|
58
|
+
.option('--policy <json>', 'policy as a JSON object (merged over the current policy)')
|
|
59
|
+
.option('--brand <text>', 'policy: brand name the line answers as')
|
|
60
|
+
.option('--opener-name <text>', 'policy: name used in the call opener')
|
|
61
|
+
.option('--ivr <on|off>', 'policy: IVR menu on or off')
|
|
62
|
+
.option('--voice <text>', 'policy: voice to speak with')
|
|
63
|
+
.option('--language <text>', 'policy: language')
|
|
64
|
+
.option('--persona <text>', 'policy: persona')
|
|
65
|
+
.option('--verbosity <quiet|verbose>', 'policy: how chatty the agent is')
|
|
66
|
+
.action(async (number, options) => {
|
|
67
|
+
auth.requireAuth();
|
|
68
|
+
const orgId = resolveOrgId(auth, program);
|
|
69
|
+
const body = { org_id: orgId };
|
|
70
|
+
if (options.label !== undefined) body.label = options.label;
|
|
71
|
+
const flagPolicy = {};
|
|
72
|
+
if (options.brand !== undefined) flagPolicy.brand = options.brand;
|
|
73
|
+
if (options.openerName !== undefined) flagPolicy.opener_name = options.openerName;
|
|
74
|
+
if (options.ivr !== undefined) flagPolicy.ivr = parseOnOff(options.ivr, '--ivr');
|
|
75
|
+
if (options.voice !== undefined) flagPolicy.voice = options.voice;
|
|
76
|
+
if (options.language !== undefined) flagPolicy.language = options.language;
|
|
77
|
+
if (options.persona !== undefined) flagPolicy.persona = options.persona;
|
|
78
|
+
if (options.verbosity !== undefined) flagPolicy.verbosity = options.verbosity;
|
|
79
|
+
let jsonPolicy;
|
|
80
|
+
if (options.policy !== undefined) {
|
|
81
|
+
try { jsonPolicy = JSON.parse(options.policy); } catch { throw new Error('--policy must be valid JSON'); }
|
|
82
|
+
}
|
|
83
|
+
if (jsonPolicy !== undefined || Object.keys(flagPolicy).length > 0) {
|
|
84
|
+
// Read-modify-write so one flag never clobbers the other policy keys.
|
|
85
|
+
const lep = resolveEndpoint('telephony.numbers.list');
|
|
86
|
+
const cur = await client.get(lep.path, { service: lep.service, query: { org_id: orgId } });
|
|
87
|
+
const existing = ((cur && cur.numbers) || []).find((n) => n.number === number);
|
|
88
|
+
body.policy = { ...((existing && existing.policy) || {}), ...(jsonPolicy || {}), ...flagPolicy };
|
|
89
|
+
}
|
|
90
|
+
const ep = resolveEndpoint('telephony.numbers.set', { number });
|
|
91
|
+
let data;
|
|
92
|
+
try {
|
|
93
|
+
data = await client.put(ep.path, { service: ep.service, body });
|
|
94
|
+
} catch (err) { throw friendlyNumbersError(err); }
|
|
95
|
+
if (program.opts().output === 'json') { console.log(JSON.stringify(data, null, 2)); return; }
|
|
96
|
+
printSuccess(`Bound ${(data.number && data.number.number) || number} to this org`);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
numbers
|
|
100
|
+
.command('remove <number>')
|
|
101
|
+
.description('Unbind a number from the active org')
|
|
102
|
+
.action(async (number) => {
|
|
103
|
+
auth.requireAuth();
|
|
104
|
+
const ep = resolveEndpoint('telephony.numbers.remove', { number });
|
|
105
|
+
let data;
|
|
106
|
+
try {
|
|
107
|
+
data = await client.delete(ep.path, { service: ep.service, query: { org_id: resolveOrgId(auth, program) } });
|
|
108
|
+
} catch (err) { throw friendlyNumbersError(err); }
|
|
109
|
+
if (program.opts().output === 'json') { console.log(JSON.stringify(data, null, 2)); return; }
|
|
110
|
+
printSuccess(`Removed ${number}`);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const identities = voice.command('identities').description('Map caller numbers to users (voice on-behalf-of identity)');
|
|
114
|
+
|
|
115
|
+
identities
|
|
116
|
+
.command('list')
|
|
117
|
+
.description('List caller number to user mappings for the active org')
|
|
118
|
+
.action(async () => {
|
|
119
|
+
auth.requireAuth();
|
|
120
|
+
const ep = resolveEndpoint('telephony.identities.list');
|
|
121
|
+
const data = await client.get(ep.path, { service: ep.service, query: { org_id: resolveOrgId(auth, program) } });
|
|
122
|
+
const list = data.identities || [];
|
|
123
|
+
formatOutput(list.map((n) => ({
|
|
124
|
+
NUMBER: n.number,
|
|
125
|
+
USER: n.user_id,
|
|
126
|
+
LABEL: n.label || '',
|
|
127
|
+
UPDATED: n.updated_at || '',
|
|
128
|
+
})), {
|
|
129
|
+
format: program.opts().output,
|
|
130
|
+
columns: ['NUMBER', 'USER', 'LABEL', 'UPDATED'],
|
|
131
|
+
emptyMessage: 'No identities mapped. Add one with `quox voice identities set <number> <userId>`.',
|
|
132
|
+
raw: list,
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
identities
|
|
137
|
+
.command('set <number> <userId>')
|
|
138
|
+
.description('Map a caller number (E.164) to a user in the active org')
|
|
139
|
+
.option('--label <text>', 'human label for the mapping')
|
|
140
|
+
.action(async (number, userId, options) => {
|
|
141
|
+
auth.requireAuth();
|
|
142
|
+
const body = { org_id: resolveOrgId(auth, program), user_id: userId };
|
|
143
|
+
if (options.label !== undefined) body.label = options.label;
|
|
144
|
+
const ep = resolveEndpoint('telephony.identities.set', { number });
|
|
145
|
+
let data;
|
|
146
|
+
try {
|
|
147
|
+
data = await client.put(ep.path, { service: ep.service, body });
|
|
148
|
+
} catch (err) { throw friendlyNumbersError(err); }
|
|
149
|
+
if (program.opts().output === 'json') { console.log(JSON.stringify(data, null, 2)); return; }
|
|
150
|
+
printSuccess(`Mapped ${(data && data.number) || number} to user ${userId}`);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
identities
|
|
154
|
+
.command('remove <number>')
|
|
155
|
+
.description('Remove a caller number mapping from the active org')
|
|
156
|
+
.action(async (number) => {
|
|
157
|
+
auth.requireAuth();
|
|
158
|
+
const ep = resolveEndpoint('telephony.identities.remove', { number });
|
|
159
|
+
let data;
|
|
160
|
+
try {
|
|
161
|
+
data = await client.delete(ep.path, { service: ep.service, query: { org_id: resolveOrgId(auth, program) } });
|
|
162
|
+
} catch (err) { throw friendlyNumbersError(err); }
|
|
163
|
+
if (program.opts().output === 'json') { console.log(JSON.stringify(data, null, 2)); return; }
|
|
164
|
+
printSuccess(`Removed ${number}`);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
voice
|
|
168
|
+
.command('calls')
|
|
169
|
+
.description('List recent calls for the active org (summary + turn count)')
|
|
170
|
+
.option('--limit <n>', 'max calls to return (1-200, default 50)')
|
|
171
|
+
.action(async (options) => {
|
|
172
|
+
auth.requireAuth();
|
|
173
|
+
const ep = resolveEndpoint('voice.calls.list');
|
|
174
|
+
const query = { org_id: resolveOrgId(auth, program) };
|
|
175
|
+
if (options.limit) query.limit = options.limit;
|
|
176
|
+
const data = await client.get(ep.path, { service: ep.service, query });
|
|
177
|
+
const list = data.calls || [];
|
|
178
|
+
formatOutput(list.map((c) => ({
|
|
179
|
+
TIME: c.started_at ? new Date(c.started_at).toISOString() : '',
|
|
180
|
+
DIR: String(c.direction || '').startsWith('out') ? 'out' : 'in',
|
|
181
|
+
PEER: c.peer || '',
|
|
182
|
+
SUMMARY: c.summary || '',
|
|
183
|
+
TURNS: Array.isArray(c.transcript) ? c.transcript.length : 0,
|
|
184
|
+
})), {
|
|
185
|
+
format: program.opts().output,
|
|
186
|
+
columns: ['TIME', 'DIR', 'PEER', 'SUMMARY', 'TURNS'],
|
|
187
|
+
emptyMessage: 'No calls recorded yet.',
|
|
188
|
+
raw: list,
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
voice
|
|
193
|
+
.command('insights')
|
|
194
|
+
.description('Show aggregate call insights for the active org')
|
|
195
|
+
.action(async () => {
|
|
196
|
+
auth.requireAuth();
|
|
197
|
+
const ep = resolveEndpoint('voice.insights.get');
|
|
198
|
+
const data = await client.get(ep.path, { service: ep.service, query: { org_id: resolveOrgId(auth, program) } });
|
|
199
|
+
if (program.opts().output === 'json') { console.log(JSON.stringify(data, null, 2)); return; }
|
|
200
|
+
const rows = [
|
|
201
|
+
{ METRIC: 'Total calls', VALUE: String(data.total_calls ?? 0) },
|
|
202
|
+
{ METRIC: 'Inbound', VALUE: String(data.inbound ?? 0) },
|
|
203
|
+
{ METRIC: 'Outbound', VALUE: String(data.outbound ?? 0) },
|
|
204
|
+
{ METRIC: 'Total turns', VALUE: String(data.total_turns ?? 0) },
|
|
205
|
+
{ METRIC: 'Avg turns', VALUE: String(data.avg_turns ?? 0) },
|
|
206
|
+
{ METRIC: 'Avg duration (s)', VALUE: data.avg_duration_sec == null ? '-' : String(data.avg_duration_sec) },
|
|
207
|
+
{ METRIC: 'Last call', VALUE: data.last_call_at ? new Date(data.last_call_at).toISOString() : '-' },
|
|
208
|
+
];
|
|
209
|
+
formatOutput(rows, { format: program.opts().output, columns: ['METRIC', 'VALUE'], raw: rows });
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export default registerVoiceCommands;
|