quoxcli 0.2.0 → 0.4.0
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 +1 -1
- package/README.md +2 -0
- package/bin/quox.js +99 -126
- package/lib/api/endpoints/actions.js +15 -0
- package/lib/api/endpoints/admin.js +46 -0
- package/lib/api/endpoints/agent-schedules.js +26 -0
- package/lib/api/endpoints/agents.js +7 -0
- package/lib/api/endpoints/assistants.js +12 -0
- package/lib/api/endpoints/away.js +15 -0
- package/lib/api/endpoints/blog.js +16 -0
- package/lib/api/endpoints/chat.js +14 -0
- package/lib/api/endpoints/contactsq.js +6 -0
- package/lib/api/endpoints/conversations.js +14 -0
- package/lib/api/endpoints/devServerRegistry.js +25 -0
- package/lib/api/endpoints/engagement.js +26 -0
- package/lib/api/endpoints/evidence.js +20 -0
- package/lib/api/endpoints/executions.js +13 -0
- package/lib/api/endpoints/fetchAllowlist.js +27 -0
- package/lib/api/endpoints/files.js +14 -0
- package/lib/api/endpoints/hat.js +44 -0
- package/lib/api/endpoints/inbox.js +20 -0
- package/lib/api/endpoints/index.js +26 -0
- package/lib/api/endpoints/loops.js +26 -0
- package/lib/api/endpoints/memory.js +19 -0
- package/lib/api/endpoints/mind.js +13 -0
- package/lib/api/endpoints/mon.js +179 -0
- package/lib/api/endpoints/objectives.js +12 -0
- package/lib/api/endpoints/plugins.js +18 -0
- package/lib/api/endpoints/presence.js +33 -0
- package/lib/api/endpoints/quoxplan.js +7 -0
- package/lib/api/endpoints/quoxseo.js +12 -0
- package/lib/api/endpoints/router.js +17 -0
- package/lib/api/endpoints/safety.js +7 -0
- package/lib/api/endpoints/server.js +21 -0
- package/lib/api/endpoints/setup.js +6 -0
- package/lib/api/endpoints/skills.js +20 -0
- package/lib/api/endpoints/stepup.js +20 -0
- package/lib/api/endpoints/teams.js +2 -2
- package/lib/api/endpoints/triggers.js +26 -0
- package/lib/api/endpoints/v1.js +120 -0
- package/lib/api/endpoints/ward.js +7 -0
- package/lib/api/endpoints/workflows.js +7 -0
- package/lib/api/endpoints/x.js +8 -0
- package/lib/commands/actions.js +131 -0
- package/lib/commands/admin.js +16 -0
- package/lib/commands/agent.js +170 -27
- package/lib/commands/agentAttach.js +140 -13
- package/lib/commands/approvals.js +78 -1
- package/lib/commands/aside.js +24 -0
- package/lib/commands/ask.js +146 -0
- package/lib/commands/assistant.js +59 -4
- package/lib/commands/auth.js +8 -2
- package/lib/commands/backup.js +8 -4
- package/lib/commands/blog.js +110 -0
- package/lib/commands/brain.js +105 -14
- package/lib/commands/build.js +19 -5
- package/lib/commands/chat.js +180 -26
- package/lib/commands/chatCompanion.js +27 -0
- package/lib/commands/chatState.js +45 -0
- package/lib/commands/claude.js +35 -38
- package/lib/commands/contacts.js +134 -4
- package/lib/commands/contactsSendAt.js +21 -0
- package/lib/commands/conversation.js +35 -4
- package/lib/commands/dashboard.js +43 -0
- package/lib/commands/department.js +28 -7
- package/lib/commands/devResident.js +15 -3
- package/lib/commands/devSupervisor.js +13 -1
- package/lib/commands/discord.js +75 -39
- package/lib/commands/done.js +98 -28
- package/lib/commands/engagement.js +107 -6
- package/lib/commands/evidence.js +154 -0
- package/lib/commands/ext.js +23 -0
- package/lib/commands/fetchAllowlist.js +71 -0
- package/lib/commands/file.js +38 -1
- package/lib/commands/fleet.js +336 -4
- package/lib/commands/governance.js +12 -3
- package/lib/commands/gripe.js +44 -13
- package/lib/commands/hat.js +254 -9
- package/lib/commands/helpers.js +18 -1
- package/lib/commands/hyperion.js +58 -0
- package/lib/commands/image.js +68 -0
- package/lib/commands/inbox.js +74 -23
- package/lib/commands/kuma.js +1050 -0
- package/lib/commands/kumaSla.js +174 -0
- package/lib/commands/licence.js +52 -155
- package/lib/commands/loop.js +291 -0
- package/lib/commands/lucid.js +10 -4
- package/lib/commands/memory.js +85 -8
- package/lib/commands/mind.js +71 -0
- package/lib/commands/mon.js +69 -2
- package/lib/commands/monitoring.js +1 -1
- package/lib/commands/motion.js +2 -2
- package/lib/commands/nucleus.js +48 -17
- package/lib/commands/objective.js +59 -1
- package/lib/commands/offload.js +89 -0
- package/lib/commands/onboard.js +2 -1
- package/lib/commands/org.js +30 -3
- package/lib/commands/peek.js +28 -0
- package/lib/commands/plan.js +38 -0
- package/lib/commands/plugin.js +110 -0
- package/lib/commands/presence.js +89 -0
- package/lib/commands/provider.js +217 -0
- package/lib/commands/qlarity.js +19 -3
- package/lib/commands/quoxchat.js +13 -4
- package/lib/commands/quoxseo.js +69 -0
- package/lib/commands/reach.js +2 -1
- package/lib/commands/research.js +79 -0
- package/lib/commands/router.js +77 -0
- package/lib/commands/rows.js +367 -0
- package/lib/commands/run.js +82 -10
- package/lib/commands/scope.js +2 -2
- package/lib/commands/server.js +405 -0
- package/lib/commands/setup.js +33 -7
- package/lib/commands/shortcut.js +41 -0
- package/lib/commands/skills.js +68 -9
- package/lib/commands/slot.js +260 -7
- package/lib/commands/status.js +1 -1
- package/lib/commands/team.js +3 -1
- package/lib/commands/tips.js +23 -0
- package/lib/commands/tool.js +133 -45
- package/lib/commands/trace.js +12 -7
- package/lib/commands/trigger.js +95 -0
- package/lib/commands/update.js +46 -0
- package/lib/commands/vault.js +31 -8
- package/lib/commands/vm.js +237 -0
- package/lib/commands/voice.js +34 -3
- package/lib/commands/volt.js +45 -3
- package/lib/commands/walkthrough.js +32 -0
- package/lib/commands/watch.js +73 -21
- package/lib/commands/welcome.js +14 -0
- package/lib/commands/workflow.js +189 -32
- package/lib/commands/x.js +13 -4
- package/lib/commands/youtube.js +172 -0
- package/lib/core/actingAs.js +2 -2
- package/lib/core/actionItems.js +115 -0
- package/lib/core/activeSummary.js +186 -0
- package/lib/core/agentAlias.js +2 -0
- package/lib/core/agentFamilies.js +7 -0
- package/lib/core/aside.js +54 -0
- package/lib/core/askDecision.js +60 -0
- package/lib/core/auth.js +164 -13
- package/lib/core/callerSource.js +1 -1
- package/lib/core/cliBuild.js +62 -0
- package/lib/core/client.js +152 -35
- package/lib/core/commandRegistrars.js +130 -0
- package/lib/core/config.js +143 -25
- package/lib/core/customHats.js +168 -0
- package/lib/core/doneEngineFetch.js +8 -0
- package/lib/core/errors.js +329 -20
- package/lib/core/findingReconcile.js +90 -0
- package/lib/core/global-flags.js +105 -5
- package/lib/core/governedSession.js +1 -3
- package/lib/core/hyperion.js +102 -0
- package/lib/core/inboxResolution.js +65 -0
- package/lib/core/lazyCommands.js +4 -1
- package/lib/core/liveSessionsReport.js +1 -1
- package/lib/core/loopGlyph.js +97 -0
- package/lib/core/offload.js +533 -0
- package/lib/core/org-override.js +1 -1
- package/lib/core/orgResolve.js +70 -0
- package/lib/core/output.js +11 -11
- package/lib/core/peek.js +126 -0
- package/lib/core/profileUsers.js +51 -0
- package/lib/core/promptCancel.js +18 -0
- package/lib/core/repoPath.js +11 -0
- package/lib/core/residentSeed.js +3 -1
- 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 +12 -8
- package/lib/core/sessionsOverview.js +64 -0
- package/lib/core/shortcut.js +83 -0
- package/lib/core/slotKeeper.js +248 -3
- package/lib/core/slotWake.js +120 -0
- package/lib/core/supervisorEngine.js +71 -2
- package/lib/core/version.js +1 -1
- package/lib/core/vm.js +366 -0
- package/lib/core/vmTui.js +86 -0
- package/lib/core/walkthrough.js +35 -0
- package/lib/dragon/companionView.js +34 -0
- package/lib/dragon/frames.js +118 -0
- package/lib/dragon/jobs.js +123 -0
- package/lib/dragon/renderWorker.js +15 -0
- package/lib/dragon/select.js +72 -0
- package/lib/evidence/bundleExport.js +47 -0
- package/lib/evidence/bundleVerify.js +77 -0
- package/lib/hyperion/deck.js +29 -0
- package/lib/lucid/LucidCard.jsx +68 -0
- package/lib/lucid/LucidIntro.jsx +45 -58
- package/lib/lucid/cards.js +86 -0
- package/lib/lucid/markdown.js +81 -0
- package/lib/lucid/motion.js +56 -0
- package/lib/quoxon/EXTENSIONS.md +23 -0
- package/lib/quoxon/activity.js +14 -2
- package/lib/quoxon/aocl.js +117 -0
- package/lib/quoxon/api.js +690 -25
- package/lib/quoxon/app.js +3586 -250
- package/lib/quoxon/approvalFacts.js +96 -0
- package/lib/quoxon/approvalSentence.js +44 -0
- package/lib/quoxon/approvalsView.js +384 -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 +13 -6
- package/lib/quoxon/cells.js +53 -0
- package/lib/quoxon/commandShort.js +124 -0
- package/lib/quoxon/commands.js +429 -39
- package/lib/quoxon/connect.js +210 -23
- package/lib/quoxon/connectCard.js +19 -2
- package/lib/quoxon/consent.js +86 -0
- package/lib/quoxon/copy.js +4 -3
- package/lib/quoxon/countdownLine.js +20 -0
- package/lib/quoxon/deck.js +30 -0
- package/lib/quoxon/demo.autoplay.js +29 -0
- package/lib/quoxon/demo.fixtures.js +1 -1
- package/lib/quoxon/demo.js +46 -3
- package/lib/quoxon/devObserver.js +20 -0
- package/lib/quoxon/dividerAocl.js +192 -0
- package/lib/quoxon/dividerRule.js +57 -0
- package/lib/quoxon/dragonCompanion.js +255 -0
- package/lib/quoxon/dragonModes.js +151 -0
- package/lib/quoxon/dragonMoments.js +82 -0
- package/lib/quoxon/dragonReads.js +68 -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 +2 -2
- package/lib/quoxon/events.js +266 -26
- package/lib/quoxon/extensions.js +276 -0
- package/lib/quoxon/feedback.js +50 -0
- package/lib/quoxon/gallery.js +21 -7
- 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/inboxView.js +79 -0
- package/lib/quoxon/indicator.js +6 -2
- package/lib/quoxon/installCli.js +79 -0
- package/lib/quoxon/intent.js +12 -1
- package/lib/quoxon/keys.js +51 -8
- package/lib/quoxon/launcher.js +103 -0
- package/lib/quoxon/loopCard.js +456 -0
- package/lib/quoxon/loopCards.js +180 -0
- package/lib/quoxon/loopStream.js +87 -0
- package/lib/quoxon/main.js +101 -160
- package/lib/quoxon/markdown.js +45 -21
- 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 +132 -26
- package/lib/quoxon/modelPrefs.js +345 -0
- package/lib/quoxon/motion/clock.js +3 -2
- package/lib/quoxon/motion/level.js +91 -0
- package/lib/quoxon/mouse.js +222 -0
- package/lib/quoxon/noai.js +57 -0
- package/lib/quoxon/objectiveOffer.js +82 -0
- package/lib/quoxon/objects/away.js +72 -0
- package/lib/quoxon/objects/fetch.js +17 -5
- package/lib/quoxon/objects/fleet.js +149 -0
- package/lib/quoxon/objects/health.js +186 -0
- package/lib/quoxon/objects/index.js +46 -7
- package/lib/quoxon/objects/mirror.js +137 -0
- package/lib/quoxon/objects/model.js +33 -0
- package/lib/quoxon/objects/peek.js +17 -0
- package/lib/quoxon/objects/proof.js +12 -4
- 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/view.js +11 -0
- package/lib/quoxon/onboarding/copy.js +32 -4
- package/lib/quoxon/onboarding/credential.js +17 -4
- package/lib/quoxon/onboarding/dockerOffer.js +45 -0
- package/lib/quoxon/onboarding/failureCause.js +33 -0
- package/lib/quoxon/onboarding/finish.js +15 -0
- package/lib/quoxon/onboarding/index.js +34 -7
- package/lib/quoxon/onboarding/installRunner.js +64 -5
- package/lib/quoxon/onboarding/preflight.js +17 -3
- package/lib/quoxon/onboarding/probe.js +65 -0
- package/lib/quoxon/onboarding/shortcutOffer.js +28 -0
- package/lib/quoxon/onboarding/view.js +76 -19
- package/lib/quoxon/orgLabel.js +10 -0
- package/lib/quoxon/orgSwitch.js +59 -0
- package/lib/quoxon/ownBroadcast.js +22 -0
- package/lib/quoxon/palette.js +43 -0
- package/lib/quoxon/planSteps.js +51 -0
- package/lib/quoxon/print.js +12 -5
- 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 +20 -51
- package/lib/quoxon/qdragon.js +410 -0
- package/lib/quoxon/qdragonFly.js +62 -0
- package/lib/quoxon/qdragonScenes.js +282 -0
- package/lib/quoxon/qeyes.js +213 -0
- package/lib/quoxon/qmotion.js +75 -37
- package/lib/quoxon/render.js +740 -124
- package/lib/quoxon/resend.js +72 -0
- package/lib/quoxon/roster.js +117 -17
- package/lib/quoxon/route.js +6 -5
- package/lib/quoxon/rows.js +954 -0
- package/lib/quoxon/rowsStore.js +80 -0
- package/lib/quoxon/screenContext.js +150 -0
- package/lib/quoxon/screenFrames.js +254 -0
- package/lib/quoxon/serverChip.js +42 -0
- package/lib/quoxon/session.js +182 -2
- package/lib/quoxon/sessionDead.js +17 -0
- package/lib/quoxon/signin.js +125 -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 +94 -0
- package/lib/quoxon/slash/cliLeaves.json +1023 -0
- package/lib/quoxon/slash/compact.js +1 -1
- 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/evidenceCmd.js +34 -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 +13 -8
- package/lib/quoxon/slash/listView.js +3 -1
- package/lib/quoxon/slash/modelCmd.js +261 -0
- package/lib/quoxon/slash/palette.js +22 -8
- package/lib/quoxon/slash/paletteController.js +117 -7
- package/lib/quoxon/slash/panels.js +21 -6
- package/lib/quoxon/slash/picker.js +20 -2
- package/lib/quoxon/slash/qpeCmd.js +35 -0
- package/lib/quoxon/slash/restart.js +95 -13
- package/lib/quoxon/slash/resume.js +18 -1
- package/lib/quoxon/slash/rowCmd.js +157 -0
- package/lib/quoxon/slash/rowWalk.js +175 -0
- package/lib/quoxon/slash/settings.js +16 -6
- 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/todoCmd.js +122 -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/mapping.js +2 -1
- package/lib/quoxon/stray.js +22 -0
- package/lib/quoxon/suggestCommand.js +90 -0
- package/lib/quoxon/term.js +46 -11
- package/lib/quoxon/textBuffer.js +176 -2
- package/lib/quoxon/theme.js +58 -25
- package/lib/quoxon/tips.js +433 -0
- package/lib/quoxon/toolCards.js +30 -0
- package/lib/quoxon/toolError.js +41 -0
- package/lib/quoxon/transcript.js +42 -4
- package/lib/quoxon/web/qface.js +18 -10
- package/lib/quoxon/welcome.js +1 -1
- package/lib/quoxon/whatsnew.js +136 -0
- package/lib/quoxon/whatsnew.jsonl +134 -0
- package/lib/quoxon/windowOrg.js +123 -0
- package/lib/server/devServers.js +137 -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/views/HyperionDeck.jsx +79 -0
- package/lib/tui/views/LucidDeck.jsx +209 -217
- package/package.json +5 -1
- package/vendor/dragonlab/SOURCE +4 -0
- package/vendor/dragonlab/design/cipher/v1/cipher-master.svg +46 -0
- package/vendor/dragonlab/design/cipher/v2/character.mjs +49 -0
- package/vendor/dragonlab/design/cipher/v2/review.mjs +33 -0
- package/vendor/dragonlab/design/cipher/v2/watch.mjs +56 -0
- package/vendor/dragonlab/design/drago/v1/character.mjs +19 -0
- package/vendor/dragonlab/design/drago/v1/drago-master.svg +56 -0
- package/vendor/dragonlab/design/drago/v2/character.mjs +87 -0
- package/vendor/dragonlab/design/drago/v4/character.mjs +67 -0
- package/vendor/dragonlab/design/drago/v4/profile.mjs +35 -0
- package/vendor/dragonlab/design/drago/v4/review.mjs +33 -0
- package/vendor/dragonlab/design/drago/v4/watch.mjs +56 -0
- package/vendor/dragonlab/design/hyperion/v1/hyperion-master.svg +56 -0
- package/vendor/dragonlab/design/hyperion/v2/character.mjs +79 -0
- package/vendor/dragonlab/design/hyperion/v2/review.mjs +33 -0
- package/vendor/dragonlab/design/hyperion/v2/watch.mjs +56 -0
- package/vendor/dragonlab/design/lucid/recovery/arrival.mjs +22 -0
- package/vendor/dragonlab/design/lucid/recovery/character.mjs +106 -0
- package/vendor/dragonlab/design/lucid/recovery/layout.mjs +50 -0
- package/vendor/dragonlab/design/lucid/recovery/lucid-master.svg +78 -0
- package/vendor/dragonlab/design/lucid/recovery/motion.mjs +42 -0
- package/vendor/dragonlab/design/lucid/recovery/render.mjs +121 -0
- package/vendor/dragonlab/design/lucid/recovery/review.mjs +68 -0
- package/vendor/dragonlab/design/lucid/recovery/verify.mjs +84 -0
- package/vendor/dragonlab/design/lucid/recovery/watch.mjs +56 -0
- package/vendor/dragonlab/design/seer/v1/seer-master.svg +53 -0
- package/vendor/dragonlab/design/seer/v2/character.mjs +88 -0
- package/vendor/dragonlab/design/seer/v3/character.mjs +33 -0
- package/vendor/dragonlab/design/seer/v4/character.mjs +66 -0
- package/vendor/dragonlab/design/seer/v4/review.mjs +33 -0
- package/vendor/dragonlab/design/seer/v4/watch.mjs +56 -0
- package/vendor/dragonlab/design/stage3/companion-review.mjs +28 -0
- package/vendor/dragonlab/design/stage3/sentinel-art.js +39 -0
- package/vendor/dragonlab/design/stage3/sentinel-braille.js +216 -0
- package/vendor/dragonlab/design/stage3/sentinel-character.js +257 -0
- package/vendor/dragonlab/design/stage3/sentinel-icon.js +103 -0
- package/vendor/dragonlab/design/stage3/sentinel-warden-eyes.js +95 -0
- package/vendor/dragonlab/design/stage3/svg-coverage.js +148 -0
- package/vendor/dragonlab/factory/svg-rig.mjs +63 -0
- package/vendor/dragonlab/integration/companions-v1/characters.mjs +88 -0
- package/vendor/dragonlab/integration/companions-v1/cipher.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/companion.mjs +63 -0
- package/vendor/dragonlab/integration/companions-v1/drago.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/hyperion.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/lucid.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/seer.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/sentinel.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/view.mjs +28 -0
- package/vendor/dragonlab/integration/hyperion-v3/activity.mjs +76 -0
- package/vendor/dragonlab/lib/power-effects.js +294 -0
- package/vendor/dragonlab/lib/power-registry.js +74 -0
- package/vendor/dragonlab/vendor/quox-dragon/qart.js +348 -0
- package/vendor/dragonlab/vendor/quox-dragon/qeyes.js +213 -0
- package/lib/core/websiteSession.js +0 -148
package/lib/commands/hat.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* QuoxHat commands (QUOXHAT #721 b
|
|
2
|
+
* QuoxHat commands (QUOXHAT #721 b; CUSTOM-HATS 2026-10-17).
|
|
3
3
|
*
|
|
4
4
|
* quox hat list
|
|
5
5
|
* quox hat show <id|key> [--rev <n>]
|
|
6
|
-
* quox hat reset <id|key> --yes (audited reset-to-template)
|
|
6
|
+
* quox hat reset <id|key> --yes (audited reset-to-template, built-ins)
|
|
7
|
+
* quox hat upgrade <id|key> [--dry-run|--yes] (3-way merge of an org-edited built-in onto the current template)
|
|
8
|
+
* quox hat create <key> --name "<Name>" [--from <hat>] [--agents a,b] [--tools x,y] [--mode m] [--offer o]
|
|
9
|
+
* quox hat edit <hat> [--name N] [--add-agents|--remove-agents|--agents] [--add-tools|--remove-tools|--tools] [--mode m]
|
|
10
|
+
* quox hat delete <hat> --yes (custom hats only)
|
|
11
|
+
* quox hat use <hat> --conversation <id> [--yes]
|
|
7
12
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* Custom hats (owner 2026-10-17): users who build their own agents organise them under their own
|
|
14
|
+
* hats, so a turn under "security" carries only the security agents, not every agent in the org.
|
|
15
|
+
* create/edit/delete act on CUSTOM hats only; a built-in is reset to its template, or copied with --from.
|
|
16
|
+
* Everything is server-side, org-scoped, revisioned and audited (services/auth/routes/quoxhat.js);
|
|
17
|
+
* who may create or change an org hat is the existing hat-edit permission (org manage-settings),
|
|
18
|
+
* a private hat (--private) only its owner. This file never decides permission itself.
|
|
14
19
|
*
|
|
15
|
-
* Backend: services/auth/routes/quoxhat.js (see cli/lib/api/endpoints/
|
|
20
|
+
* Backend: services/auth/routes/quoxhat.js (see cli/lib/api/endpoints/hat.js).
|
|
16
21
|
*/
|
|
17
22
|
|
|
18
23
|
import chalk from 'chalk';
|
|
@@ -38,6 +43,12 @@ export function templateLabel(hat) {
|
|
|
38
43
|
const latestAuthor = Array.isArray(hat.revisions) && hat.revisions[0] ? hat.revisions[0].created_by : null;
|
|
39
44
|
const customised = typeof latestAuthor === 'string' && !latestAuthor.startsWith('template:');
|
|
40
45
|
if (latest && cur && cur < latest) return `v${cur} (template v${latest} available)`;
|
|
46
|
+
// Merge-upgrade: the last applied 3-way merge (org edits carried onto the new template).
|
|
47
|
+
const u = hat.last_upgrade;
|
|
48
|
+
if (u && u.to_template_version === cur) {
|
|
49
|
+
const k = Number(u.org_changes_kept) || 0;
|
|
50
|
+
return `v${cur}, upgraded from v${u.from_template_version}, ${k} org change${k === 1 ? '' : 's'} kept`;
|
|
51
|
+
}
|
|
41
52
|
return customised ? `v${cur || '?'} (edited)` : `v${cur || '?'}`;
|
|
42
53
|
}
|
|
43
54
|
|
|
@@ -59,6 +70,56 @@ export function findHat(hats, ref) {
|
|
|
59
70
|
return (hats || []).find(h => h.id === r) || (hats || []).find(h => h.key === r) || null;
|
|
60
71
|
}
|
|
61
72
|
|
|
73
|
+
|
|
74
|
+
export { DELEGATION_TOOLS, HAT_MODES, HAT_OFFERS, parseList, memberTools, applyMode, buildCustomDefinition, applyHatEdits } from '../core/customHats.js';
|
|
75
|
+
import { parseList, memberTools, buildCustomDefinition, applyHatEdits } from '../core/customHats.js';
|
|
76
|
+
|
|
77
|
+
/** Which of `ids` no registered or org agent matches. checked=false when neither list could be read. */
|
|
78
|
+
export async function findUnknownAgents(client, orgId, ids) {
|
|
79
|
+
if (!ids.length) return { unknown: [], checked: true };
|
|
80
|
+
const known = new Set();
|
|
81
|
+
let read = 0;
|
|
82
|
+
try {
|
|
83
|
+
const ep = resolveEndpoint('v1.agents.registry');
|
|
84
|
+
const d = await client.get(ep.path, { service: ep.service });
|
|
85
|
+
if (Array.isArray(d?.agents)) { read++; for (const a of d.agents) if (a?.agent_id) known.add(String(a.agent_id).toLowerCase()); }
|
|
86
|
+
} catch { /* named below */ }
|
|
87
|
+
try {
|
|
88
|
+
const ep = resolveEndpoint('agents.list', { orgId });
|
|
89
|
+
const d = await client.get(ep.path, { service: ep.service });
|
|
90
|
+
const rows = Array.isArray(d?.agents) ? d.agents : (Array.isArray(d) ? d : null);
|
|
91
|
+
if (rows) { read++; for (const a of rows) { const id = a?.agent_id || a?.agentId; if (id) known.add(String(id).toLowerCase()); } }
|
|
92
|
+
} catch { /* named below */ }
|
|
93
|
+
if (!read) return { unknown: [], checked: false };
|
|
94
|
+
return { unknown: ids.filter(id => !known.has(id)), checked: true };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isBuiltin(h) { return !!h?.template_key; }
|
|
98
|
+
|
|
99
|
+
const fmtItem = (e) => (e.item !== undefined ? `${e.path}: ${e.item}` : e.path);
|
|
100
|
+
|
|
101
|
+
/** Human preview of a merge upgrade (exported for reuse by other surfaces). */
|
|
102
|
+
export function renderUpgradePreview(key, p) {
|
|
103
|
+
const out = [];
|
|
104
|
+
out.push(chalk.bold(`Hat ${key}: template v${p.from_template_version} -> v${p.to_template_version}`) + chalk.dim(` (from rev ${p.from_revision})`));
|
|
105
|
+
out.push(`Tools added: ${(p.tools_added || []).join(', ') || 'none'}`);
|
|
106
|
+
out.push(`Tools removed: ${(p.tools_removed || []).join(', ') || 'none'}`);
|
|
107
|
+
const changed = (p.template_changed || []).map(c => c.path);
|
|
108
|
+
if (changed.length) out.push(`Template changed: ${changed.join(', ')}`);
|
|
109
|
+
out.push(chalk.bold(`Org changes kept (${(p.org_changes || []).length}):`));
|
|
110
|
+
for (const c of p.org_changes || []) out.push(` ${c.change.padEnd(7)} ${fmtItem(c)}`);
|
|
111
|
+
if (!(p.org_changes || []).length) out.push(' none');
|
|
112
|
+
out.push(chalk.bold(`Conflicts, org choice kept (${(p.conflicts || []).length}):`));
|
|
113
|
+
for (const c of p.conflicts || []) out.push(` ${c.path}${c.reason ? ` (${c.reason})` : ''}`);
|
|
114
|
+
if (!(p.conflicts || []).length) out.push(' none');
|
|
115
|
+
out.push(chalk.bold(`Held, would widen authorization (${(p.held || []).length}):`));
|
|
116
|
+
for (const c of p.held || []) out.push(` ${c.path} ${(c.fields || []).join(', ')}`);
|
|
117
|
+
if (!(p.held || []).length) out.push(' none');
|
|
118
|
+
out.push(`Valid: ${p.valid ? 'yes' : `NO: ${(p.errors || []).join('; ')}`}`);
|
|
119
|
+
out.push(`Digest: ${p.from_digest || '-'} -> ${p.digest}`);
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
|
|
62
123
|
export function registerHatCommands(program, ctx) {
|
|
63
124
|
const { client, auth } = ctx;
|
|
64
125
|
|
|
@@ -154,6 +215,8 @@ export function registerHatCommands(program, ctx) {
|
|
|
154
215
|
out.push(`Enforcement: ${enforcement} (org)`);
|
|
155
216
|
out.push(`Transitions: confirm=${def.transitions?.confirm || 'none'} privileged=${def.transitions?.privileged ? 'yes' : 'no'}`);
|
|
156
217
|
out.push(`Agents: orchestrator=${def.agents?.orchestrator || '-'} specialists=${(def.agents?.specialists || []).join(', ') || 'none'}`);
|
|
218
|
+
out.push(`Offer: ${def.tools?.offer === 'members' ? 'members (an orchestrator turn is offered only this hat\'s tools)' : 'authorized (hat tools first, then the rest of the grant)'}`);
|
|
219
|
+
out.push(`Kind: ${h.template_key ? 'built-in' : 'custom'}${h.visibility === 'private' ? ' (private)' : ''}`);
|
|
157
220
|
out.push(`Budgets: initial_tool_target=${def.budgets?.initial_tool_target ?? '-'} max_manifest=${def.budgets?.max_manifest ?? '-'}`);
|
|
158
221
|
out.push('');
|
|
159
222
|
const explicit = def.tools?.explicit || [];
|
|
@@ -202,4 +265,186 @@ export function registerHatCommands(program, ctx) {
|
|
|
202
265
|
if (program.opts().output === 'json') { console.log(JSON.stringify({ hat: h }, null, 2)); return; }
|
|
203
266
|
console.log(` Ⓠ QUOX │ hat ${h.key} reset to template │ rev ${h.current_revision} · ${templateLabel(h)} ✔`);
|
|
204
267
|
});
|
|
268
|
+
// ── hat upgrade ─────────────────────────────────────────────────────────────
|
|
269
|
+
hat
|
|
270
|
+
.command('upgrade <hat>')
|
|
271
|
+
.description('Carry an org-edited built-in hat onto the current template (3-way merge: org changes kept, conflicts keep the org choice, widening authz changes held); previews unless --yes')
|
|
272
|
+
.option('--dry-run', 'preview the merge only (writes nothing); the default without --yes')
|
|
273
|
+
.option('--yes', 'apply the previewed merge as a new system-authored revision (audited)')
|
|
274
|
+
.action(async (ref, options) => {
|
|
275
|
+
auth.requireAuth();
|
|
276
|
+
if (options.dryRun && options.yes) throw new Error('Pass either --dry-run (preview) or --yes (apply), not both.');
|
|
277
|
+
const orgId = resolveOrgId(auth, program);
|
|
278
|
+
const list = await fetchList(client, orgId);
|
|
279
|
+
const found = findHat(list?.hats, ref);
|
|
280
|
+
if (!found) {
|
|
281
|
+
const keys = (list?.hats || []).map(h => h.key).join(', ') || 'none';
|
|
282
|
+
throw new Error(`No hat '${ref}' in this org. Available: ${keys}`);
|
|
283
|
+
}
|
|
284
|
+
const pep = resolveEndpoint('hat.upgrade.preview', { orgId, hatId: found.id });
|
|
285
|
+
let data;
|
|
286
|
+
try {
|
|
287
|
+
data = await client.get(pep.path, { service: pep.service });
|
|
288
|
+
} catch (err) {
|
|
289
|
+
const code = err?.response?.data?.error || err?.data?.error || err?.body?.error;
|
|
290
|
+
if (code === 'up_to_date') { console.log(` Ⓠ QUOX │ hat ${found.key} upgrade │ already on template v${found.template_version} ✔`); return; }
|
|
291
|
+
throw err;
|
|
292
|
+
}
|
|
293
|
+
const p = data?.preview;
|
|
294
|
+
if (!p) throw new Error(`Upgrade preview for '${ref}' returned nothing`);
|
|
295
|
+
const json = program.opts().output === 'json';
|
|
296
|
+
|
|
297
|
+
if (!options.yes) {
|
|
298
|
+
if (json) { console.log(JSON.stringify({ preview: p, applied: false }, null, 2)); return; }
|
|
299
|
+
console.log(renderUpgradePreview(found.key, p).join('\n'));
|
|
300
|
+
console.log('');
|
|
301
|
+
console.log(` dry run: nothing written. Apply with: quox hat upgrade ${found.key} --yes`);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (!p.valid) throw new Error(`The merged hat does not validate: ${(p.errors || []).join('; ')}`);
|
|
305
|
+
const aep = resolveEndpoint('hat.upgrade', { orgId, hatId: found.id });
|
|
306
|
+
const res = await client.post(aep.path, { service: aep.service, body: { expected_revision: p.from_revision } });
|
|
307
|
+
const h = res?.hat;
|
|
308
|
+
if (!h) throw new Error(`Upgrade of '${ref}' returned no hat`);
|
|
309
|
+
if (json) { console.log(JSON.stringify({ hat: h, preview: res.preview || p, applied: true }, null, 2)); return; }
|
|
310
|
+
console.log(` Ⓠ QUOX │ hat ${h.key} upgraded │ rev ${h.current_revision} · ${templateLabel(h)} ✔`);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// ── hat create (custom) ───────────────────────────────────────────────────────
|
|
314
|
+
hat
|
|
315
|
+
.command('create <key>')
|
|
316
|
+
.description('Create a custom hat: the agents and tools it lists are the only ones a turn under it carries')
|
|
317
|
+
.requiredOption('--name <name>', 'display name, e.g. "Security"')
|
|
318
|
+
.option('--from <hat>', 'start from a copy of this hat (key or id)')
|
|
319
|
+
.option('--agents <ids>', 'comma-separated agent ids this hat puts in scope (registered or your own agents)')
|
|
320
|
+
.option('--tools <names>', 'comma-separated tool names the orchestrator is offered under this hat')
|
|
321
|
+
.option('--mode <mode>', 'permissive (default: each agent keeps its own grant) or restrictive (only the listed tools are authorized, for every agent under the hat)')
|
|
322
|
+
.option('--offer <offer>', 'members (default: offer only this hat\'s tools) or authorized')
|
|
323
|
+
.option('--purpose <text>', 'one line: what this hat is for')
|
|
324
|
+
.option('--private', 'a private hat only you can see and wear (needs no org settings permission)')
|
|
325
|
+
.option('--reason <text>', 'why (kept in the revision history)')
|
|
326
|
+
.action(async (key, options) => {
|
|
327
|
+
auth.requireAuth();
|
|
328
|
+
const orgId = resolveOrgId(auth, program);
|
|
329
|
+
const k = String(key || '').trim().toLowerCase();
|
|
330
|
+
if (!/^[a-z0-9][a-z0-9-]{1,40}$/.test(k)) throw new Error(`Hat key '${key}' must be a lowercase slug (a-z, 0-9, hyphen), 2-41 characters.`);
|
|
331
|
+
const list = await fetchList(client, orgId);
|
|
332
|
+
const hats = list?.hats || [];
|
|
333
|
+
if (findHat(hats, k)) throw new Error(`A hat with key '${k}' already exists in this org. Use quox hat edit ${k}.`);
|
|
334
|
+
let base = null;
|
|
335
|
+
if (options.from) {
|
|
336
|
+
const from = findHat(hats, options.from);
|
|
337
|
+
if (!from) throw new Error(`No hat '${options.from}' to copy. Available: ${hats.map(h => h.key).join(', ') || 'none'}`);
|
|
338
|
+
base = from.definition;
|
|
339
|
+
}
|
|
340
|
+
const agents = options.agents !== undefined ? parseList(options.agents) : undefined;
|
|
341
|
+
const tools = options.tools !== undefined ? parseList(options.tools) : undefined;
|
|
342
|
+
const { definition, autoAdded } = buildCustomDefinition({ base, name: options.name, purpose: options.purpose, agents, tools, mode: options.mode, offer: options.offer });
|
|
343
|
+
const named = definition.agents.specialists;
|
|
344
|
+
const check = await findUnknownAgents(client, orgId, named);
|
|
345
|
+
if (check.unknown.length) throw new Error(`Unknown agent(s): ${check.unknown.join(', ')}. See quox agent list.`);
|
|
346
|
+
const ep = resolveEndpoint('hat.create', { orgId });
|
|
347
|
+
const data = await client.post(ep.path, { service: ep.service, body: { key: k, name: String(options.name).trim(), definition, visibility: options.private ? 'private' : 'org' } });
|
|
348
|
+
const h = data?.hat;
|
|
349
|
+
if (!h) throw new Error(`Create of '${k}' returned no hat`);
|
|
350
|
+
if (program.opts().output === 'json') { console.log(JSON.stringify({ hat: h, warnings: data.warnings || [], auto_added_tools: autoAdded }, null, 2)); return; }
|
|
351
|
+
const d = h.definition || definition;
|
|
352
|
+
console.log(` Ⓠ QUOX │ hat ${h.key} created │ rev ${h.current_revision} · agents ${(d.agents?.specialists || []).join(', ') || 'none'} · ${memberTools(d).length} tool(s) · offer ${d.tools?.offer || 'authorized'} · ${authzMode(d)} ✔`);
|
|
353
|
+
if (autoAdded.length) printInfo(`Added ${autoAdded.join(', ')} so the orchestrator can reach the hat's agents.`);
|
|
354
|
+
if (!check.checked) printInfo('Agent ids were not checked: the agent lists could not be read.');
|
|
355
|
+
if (d.authz?.mode === 'restrictive') printInfo('Restrictive: every agent working under this hat, its own agents included when delegated to, may only run the tools listed here.');
|
|
356
|
+
for (const w of data.warnings || []) printInfo(`warning: ${w}`);
|
|
357
|
+
printInfo(`Wear it: /hat ${h.key} in quox, or quox hat use ${h.key} --conversation <id>.`);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// ── hat edit (custom) ─────────────────────────────────────────────────────────
|
|
361
|
+
hat
|
|
362
|
+
.command('edit <hat>')
|
|
363
|
+
.description('Change a custom hat: add or remove agents and tools, rename, mode (appends an audited revision)')
|
|
364
|
+
.option('--name <name>', 'rename the hat')
|
|
365
|
+
.option('--purpose <text>', 'new one-line purpose')
|
|
366
|
+
.option('--agents <ids>', 'replace the agent list')
|
|
367
|
+
.option('--add-agents <ids>', 'comma-separated agents to add')
|
|
368
|
+
.option('--remove-agents <ids>', 'comma-separated agents to remove')
|
|
369
|
+
.option('--tools <names>', 'replace the explicit tool list')
|
|
370
|
+
.option('--add-tools <names>', 'comma-separated tools to add')
|
|
371
|
+
.option('--remove-tools <names>', 'comma-separated tools to remove')
|
|
372
|
+
.option('--mode <mode>', 'permissive or restrictive')
|
|
373
|
+
.option('--offer <offer>', 'members or authorized')
|
|
374
|
+
.option('--reason <text>', 'why (kept in the revision history)')
|
|
375
|
+
.action(async (ref, options) => {
|
|
376
|
+
auth.requireAuth();
|
|
377
|
+
const orgId = resolveOrgId(auth, program);
|
|
378
|
+
const list = await fetchList(client, orgId);
|
|
379
|
+
const found = findHat(list?.hats, ref);
|
|
380
|
+
if (!found) throw new Error(`No hat '${ref}' in this org. Available: ${(list?.hats || []).map(h => h.key).join(', ') || 'none'}`);
|
|
381
|
+
if (isBuiltin(found)) throw new Error(`'${found.key}' is a built-in hat: quox hat edit changes custom hats only. Reset it with quox hat reset ${found.key} --yes, or make your own from it: quox hat create <key> --name "<Name>" --from ${found.key}.`);
|
|
382
|
+
const opts = {
|
|
383
|
+
agents: options.agents !== undefined ? parseList(options.agents) : undefined,
|
|
384
|
+
addAgents: parseList(options.addAgents),
|
|
385
|
+
removeAgents: parseList(options.removeAgents),
|
|
386
|
+
tools: options.tools !== undefined ? parseList(options.tools) : undefined,
|
|
387
|
+
addTools: parseList(options.addTools),
|
|
388
|
+
removeTools: parseList(options.removeTools),
|
|
389
|
+
mode: options.mode,
|
|
390
|
+
offer: options.offer,
|
|
391
|
+
purpose: options.purpose,
|
|
392
|
+
};
|
|
393
|
+
const { definition, changes } = applyHatEdits(found.definition, opts);
|
|
394
|
+
const rename = options.name !== undefined && String(options.name).trim() !== found.name ? String(options.name).trim() : undefined;
|
|
395
|
+
if (rename !== undefined) changes.unshift(`name "${found.name}" -> "${rename}"`);
|
|
396
|
+
if (!changes.length) throw new Error(`Nothing to change on '${found.key}'.`);
|
|
397
|
+
const newAgents = [...(opts.agents || []), ...opts.addAgents];
|
|
398
|
+
const check = await findUnknownAgents(client, orgId, newAgents);
|
|
399
|
+
if (check.unknown.length) throw new Error(`Unknown agent(s): ${check.unknown.join(', ')}. See quox agent list.`);
|
|
400
|
+
const ep = resolveEndpoint('hat.update', { orgId, hatId: found.id });
|
|
401
|
+
const data = await client.put(ep.path, { service: ep.service, body: { definition, ...(rename !== undefined ? { name: rename } : {}), change_reason: options.reason || changes.join('; ') } });
|
|
402
|
+
const h = data?.hat;
|
|
403
|
+
if (!h) throw new Error(`Edit of '${found.key}' returned no hat`);
|
|
404
|
+
if (program.opts().output === 'json') { console.log(JSON.stringify({ hat: h, changes, warnings: data.warnings || [] }, null, 2)); return; }
|
|
405
|
+
console.log(` Ⓠ QUOX │ hat ${h.key} edited │ rev ${h.current_revision} · ${changes.join(' · ')} ✔`);
|
|
406
|
+
for (const w of data.warnings || []) printInfo(`warning: ${w}`);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// ── hat delete (custom) ───────────────────────────────────────────────────────
|
|
410
|
+
hat
|
|
411
|
+
.command('delete <hat>')
|
|
412
|
+
.description('Delete a custom hat (built-ins cannot be deleted; reset them instead). Audited.')
|
|
413
|
+
.option('--yes', 'confirm: conversations wearing it fall back to the default hat')
|
|
414
|
+
.action(async (ref, options) => {
|
|
415
|
+
auth.requireAuth();
|
|
416
|
+
const orgId = resolveOrgId(auth, program);
|
|
417
|
+
const list = await fetchList(client, orgId);
|
|
418
|
+
const found = findHat(list?.hats, ref);
|
|
419
|
+
if (!found) throw new Error(`No hat '${ref}' in this org. Available: ${(list?.hats || []).map(h => h.key).join(', ') || 'none'}`);
|
|
420
|
+
if (isBuiltin(found)) throw new Error(`'${found.key}' is a built-in hat and cannot be deleted. quox hat reset ${found.key} --yes returns it to its template.`);
|
|
421
|
+
if (!options.yes) throw new Error(`Deleting '${found.key}' moves every conversation wearing it back to the default hat. Re-run with --yes to confirm.`);
|
|
422
|
+
const ep = resolveEndpoint('hat.delete', { orgId, hatId: found.id });
|
|
423
|
+
const data = await client.delete(ep.path, { service: ep.service });
|
|
424
|
+
const d = data?.deleted;
|
|
425
|
+
if (!d) throw new Error(`Delete of '${found.key}' returned no confirmation`);
|
|
426
|
+
if (program.opts().output === 'json') { console.log(JSON.stringify({ deleted: d }, null, 2)); return; }
|
|
427
|
+
console.log(` Ⓠ QUOX │ hat ${d.key} deleted │ was rev ${d.revision} · ${d.bindings_cleared} conversation(s) moved to the default · ${d.user_prefs_cleared} preference(s) cleared ✔`);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// ── hat use ───────────────────────────────────────────────────────────────────
|
|
431
|
+
hat
|
|
432
|
+
.command('use <hat>')
|
|
433
|
+
.description('Wear a hat in one conversation (the same audited switch as /hat <name> in quox)')
|
|
434
|
+
.requiredOption('--conversation <id>', 'the conversation to switch')
|
|
435
|
+
.option('--yes', 'confirm a privileged hat')
|
|
436
|
+
.action(async (ref, options) => {
|
|
437
|
+
auth.requireAuth();
|
|
438
|
+
const orgId = resolveOrgId(auth, program);
|
|
439
|
+
const list = await fetchList(client, orgId);
|
|
440
|
+
const found = findHat(list?.hats, ref);
|
|
441
|
+
if (!found) throw new Error(`No hat '${ref}' in this org. Available: ${(list?.hats || []).map(h => h.key).join(', ') || 'none'}`);
|
|
442
|
+
if (found.definition?.transitions?.privileged && !options.yes) throw new Error(`'${found.key}' is a privileged hat. Re-run with --yes to wear it.`);
|
|
443
|
+
const ep = resolveEndpoint('hat.bind', { orgId, conversationId: options.conversation });
|
|
444
|
+
const data = await client.put(ep.path, { service: ep.service, body: { hat_id: found.id } });
|
|
445
|
+
const b = data?.binding;
|
|
446
|
+
if (!b) throw new Error(`Switch to '${found.key}' returned no binding`);
|
|
447
|
+
if (program.opts().output === 'json') { console.log(JSON.stringify({ binding: b, hat: { id: found.id, key: found.key } }, null, 2)); return; }
|
|
448
|
+
console.log(` Ⓠ QUOX │ conversation ${options.conversation} now wears ${found.key} │ rev ${b.revision} · seq ${b.seq} ✔`);
|
|
449
|
+
});
|
|
205
450
|
}
|
package/lib/commands/helpers.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Shared helpers for command modules
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { QuoxValidationError } from '../core/errors.js';
|
|
5
|
+
import { QuoxValidationError, QuoxNetworkError } from '../core/errors.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Resolve the active org ID from --org flag or stored auth.
|
|
@@ -77,3 +77,20 @@ export function buildListQuery(options) {
|
|
|
77
77
|
if (options.sort) query.sort = options.sort;
|
|
78
78
|
return query;
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Run a request; if it times out, rethrow with what timed out and how to narrow
|
|
83
|
+
* it (still a QuoxNetworkError, so the exit code stays 7). A bare
|
|
84
|
+
* "Request timed out after 30000ms" does not tell the operator which call hung
|
|
85
|
+
* or what to try (quox#806, quox#807).
|
|
86
|
+
*/
|
|
87
|
+
export async function withTimeoutHint(what, hint, fn) {
|
|
88
|
+
try {
|
|
89
|
+
return await fn();
|
|
90
|
+
} catch (err) {
|
|
91
|
+
if (err instanceof QuoxNetworkError && /timed out/i.test(err.message || '')) {
|
|
92
|
+
throw new QuoxNetworkError(`${what} timed out (${err.message}). ${hint}`, err);
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `quox hyperion`: everything HYPERION (the governed Proxmox operator, agent id atlas) is doing in one place.
|
|
3
|
+
* Collector route GET /api/v1/proxmox/activity (org-scoped, read only).
|
|
4
|
+
*
|
|
5
|
+
* quox hyperion [status] [--json] VMs and who holds them, node load, pool counts, last 5 ops
|
|
6
|
+
* quox hyperion deck live full-screen view, refreshes every 10s (q quits, r refreshes)
|
|
7
|
+
*/
|
|
8
|
+
import { line as glyphLine, printEvent } from '../core/quoxglyph.js';
|
|
9
|
+
import { fetchActivity, statusLines, summaryText } from '../core/hyperion.js';
|
|
10
|
+
|
|
11
|
+
export function registerHyperionCommands(program, ctx) {
|
|
12
|
+
const { client, auth } = ctx;
|
|
13
|
+
|
|
14
|
+
const hy = program
|
|
15
|
+
.command('hyperion')
|
|
16
|
+
.description('HYPERION activity: which VMs exist, who holds each and why, node load and recent operations')
|
|
17
|
+
.option('--json', 'print the full JSON response');
|
|
18
|
+
|
|
19
|
+
const wantJson = (cmd) => {
|
|
20
|
+
const o = cmd.optsWithGlobals ? cmd.optsWithGlobals() : cmd.opts();
|
|
21
|
+
return Boolean(o.json) || program.opts().output === 'json';
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// An error body is not data: report the status and stop.
|
|
25
|
+
async function show(cmd) {
|
|
26
|
+
auth.requireAuth();
|
|
27
|
+
let data;
|
|
28
|
+
try {
|
|
29
|
+
data = await fetchActivity(client);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
const status = err && (err.status || err.statusCode);
|
|
32
|
+
if (!status) throw err;
|
|
33
|
+
console.error(`HYPERION request failed: HTTP ${status}${err.message ? ` ${err.message}` : ''}`);
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (wantJson(cmd)) { console.log(JSON.stringify(data, null, 2)); return; }
|
|
38
|
+
printEvent(glyphLine('hyperion', 'activity', { result: summaryText(data) }));
|
|
39
|
+
console.log(statusLines(data).join('\n'));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
hy.action(async (_o, cmd) => show(cmd));
|
|
43
|
+
|
|
44
|
+
hy
|
|
45
|
+
.command('status')
|
|
46
|
+
.description('Same as bare `quox hyperion`')
|
|
47
|
+
.option('--json', 'print the full JSON response')
|
|
48
|
+
.action(async (_o, cmd) => show(cmd));
|
|
49
|
+
|
|
50
|
+
hy
|
|
51
|
+
.command('deck')
|
|
52
|
+
.description('Live full-screen HYPERION deck (refreshes every 10s; r refreshes, q quits)')
|
|
53
|
+
.action(async () => {
|
|
54
|
+
auth.requireAuth();
|
|
55
|
+
const { launchHyperionDeck } = await import('../hyperion/deck.js');
|
|
56
|
+
await launchHyperionDeck({ activity: () => fetchActivity(client) });
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* quox image generate "<prompt>" [--out file] [--provider gemini|openai] [--aspect-ratio 16:9] [--count n]
|
|
3
|
+
*
|
|
4
|
+
* CLI verb first for image generation. It runs the SAME governed tool the chat uses (gemini_image_generate via
|
|
5
|
+
* POST /api/v1/tools/execute), so the provider fallback, the receipt/witnessing and the plain all-fail message are
|
|
6
|
+
* the collector's, not re-implemented here. Providers tried: Gemini, then OpenAI, using only what the org already
|
|
7
|
+
* connected (`quox provider list`). Nothing here adds a provider or a key.
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { printSuccess } from '../core/output.js';
|
|
12
|
+
import { executeGovernedTool } from './tool.js';
|
|
13
|
+
|
|
14
|
+
const EXT = { 'image/png': 'png', 'image/jpeg': 'jpg', 'image/webp': 'webp' };
|
|
15
|
+
|
|
16
|
+
export function registerImageCommands(program, ctx) {
|
|
17
|
+
const { client, auth } = ctx;
|
|
18
|
+
const image = program.command('image').description('Generate images with the providers your org has connected');
|
|
19
|
+
|
|
20
|
+
image
|
|
21
|
+
.command('generate')
|
|
22
|
+
.description('Generate an image from a prompt (tries Gemini, then OpenAI; says which made it)')
|
|
23
|
+
.argument('<prompt>', 'what to draw')
|
|
24
|
+
.option('--out <file>', 'where to save the image (default ./quox-image-<time>.png)')
|
|
25
|
+
.option('--provider <name>', 'use only this provider: gemini or openai (no fall-through)')
|
|
26
|
+
.option('--aspect-ratio <ratio>', 'e.g. 16:9 for a hero, 1:1 for social, 9:16 for stories')
|
|
27
|
+
.option('--count <n>', 'how many images (1-4)', '1')
|
|
28
|
+
.action(async (prompt, options) => {
|
|
29
|
+
const input = { prompt };
|
|
30
|
+
if (options.provider) input.provider = options.provider;
|
|
31
|
+
if (options.aspectRatio) input.aspect_ratio = options.aspectRatio;
|
|
32
|
+
const n = Number(options.count);
|
|
33
|
+
if (n > 1) input.number_of_images = Math.min(n, 4);
|
|
34
|
+
|
|
35
|
+
const data = await executeGovernedTool({ client, auth, program }, 'gemini_image_generate', input, { reason: 'quox image generate' });
|
|
36
|
+
const r = (data && data.result && typeof data.result === 'object') ? data.result : (data || {});
|
|
37
|
+
const failed = (data && data.success === false) || r.success === false;
|
|
38
|
+
|
|
39
|
+
if (program.opts().output === 'json') {
|
|
40
|
+
// Never dump base64 into a terminal or a pipe: report where the files went.
|
|
41
|
+
const out = failed ? r : { ...r, images: undefined };
|
|
42
|
+
if (!failed) out.files = saveImages(r.images, options.out);
|
|
43
|
+
console.log(JSON.stringify({ ...out, ...(failed && !r.error && data.error ? { error: data.error } : {}) }, null, 2));
|
|
44
|
+
if (failed) process.exitCode = 1;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (failed) {
|
|
48
|
+
console.error(r.error || data.error || 'Image generation failed.');
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const files = saveImages(r.images, options.out);
|
|
53
|
+
printSuccess(`Made by ${r.provider_used || 'gemini'}${r.model_used ? ` (${r.model_used})` : ''}: ${files.join(', ')}`);
|
|
54
|
+
if (r.note) console.log(r.note);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function saveImages(images, out) {
|
|
59
|
+
const list = Array.isArray(images) ? images : [];
|
|
60
|
+
const stamp = Date.now();
|
|
61
|
+
return list.map((img, i) => {
|
|
62
|
+
const ext = EXT[img.mime_type] || 'png';
|
|
63
|
+
let file = out || `quox-image-${stamp}.${ext}`;
|
|
64
|
+
if (list.length > 1) { const p = path.parse(file); file = path.join(p.dir, `${p.name}-${i + 1}${p.ext || `.${ext}`}`); }
|
|
65
|
+
fs.writeFileSync(file, Buffer.from(img.base64, 'base64'));
|
|
66
|
+
return file;
|
|
67
|
+
});
|
|
68
|
+
}
|
package/lib/commands/inbox.js
CHANGED
|
@@ -5,39 +5,52 @@
|
|
|
5
5
|
import { formatOutput, formatDetail, printSuccess } from '../core/output.js';
|
|
6
6
|
import { resolveOrgId } from './helpers.js';
|
|
7
7
|
import { resolveEndpoint } from '../api/endpoints.js';
|
|
8
|
+
import { attachWalkthrough } from './walkthrough.js';
|
|
8
9
|
import { getApprovalContext, formatApprovalContext, approvalListColumns } from '../core/approvalContext.js';
|
|
10
|
+
import { QuoxUserError } from '../core/errors.js';
|
|
11
|
+
import { resolutionFromItem, alreadyResolvedError } from '../core/inboxResolution.js';
|
|
12
|
+
import chalk from 'chalk';
|
|
9
13
|
|
|
10
14
|
export function registerInboxCommands(program, ctx) {
|
|
11
15
|
const { client, auth } = ctx;
|
|
12
16
|
|
|
13
|
-
// An agent hands back a tool approvalId (tool_approvals.approval_id),
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
+
// An agent hands back a tool approvalId (tool_approvals.approval_id), stored on the card as
|
|
18
|
+
// aee_correlation_id. The server now resolves it on GET and respond (CLI-ERRORS); this client-side
|
|
19
|
+
// scan stays as the fallback for an older server. A failed scan is reported, never read as "no card".
|
|
20
|
+
async function findByApprovalId(orgId, id) {
|
|
21
|
+
const list = resolveEndpoint('inbox.list', { orgId });
|
|
22
|
+
const data = await client.get(list.path, { service: list.service, query: { status: 'all', kind: 'approval', limit: 200 } });
|
|
23
|
+
const items = (data && (data.items || data)) || [];
|
|
24
|
+
const hit = Array.isArray(items) && items.find((it) => it.aee_correlation_id === id || (it.payload && it.payload.approval_id === id));
|
|
25
|
+
return hit ? hit.id : null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function notFound(id, cause) {
|
|
29
|
+
const err = new QuoxUserError(`No inbox item or approval with id ${id} in this org, open or resolved.`, {
|
|
30
|
+
next: "if it came from another org, switch with `quox org switch <org>`; `quox inbox list --status all` lists this org's items.",
|
|
31
|
+
exitCode: 5, code: 'not_found', status: 404, body: cause?.body,
|
|
32
|
+
});
|
|
33
|
+
return err;
|
|
34
|
+
}
|
|
35
|
+
|
|
17
36
|
async function resolveItemId(orgId, id) {
|
|
18
37
|
const direct = resolveEndpoint('inbox.get', { orgId, itemId: id });
|
|
19
38
|
try {
|
|
20
|
-
await client.get(direct.path, { service: direct.service });
|
|
21
|
-
|
|
39
|
+
const data = await client.get(direct.path, { service: direct.service });
|
|
40
|
+
// the server resolves an approvalId to its card; post to the card's own id
|
|
41
|
+
return (data && data.item && data.item.id) || id;
|
|
22
42
|
} catch (err) {
|
|
23
43
|
if (!err || err.status !== 404) throw err;
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
data = await client.get(list.path, { service: list.service, query: { status: 'all', kind: 'approval', limit: 200 } });
|
|
28
|
-
} catch { data = null; }
|
|
29
|
-
const items = (data && (data.items || data)) || [];
|
|
30
|
-
const hit = Array.isArray(items) && items.find((it) => it.aee_correlation_id === id || (it.payload && it.payload.approval_id === id));
|
|
31
|
-
if (hit) return hit.id;
|
|
32
|
-
err.message += `\nHint: ${id} is not an inbox item id, and no approval card in your recent approvals carries it as an approvalId. `
|
|
33
|
-
+ 'Try `quox inbox list --status all` to find the card.';
|
|
34
|
-
throw err;
|
|
44
|
+
const hit = await findByApprovalId(orgId, id);
|
|
45
|
+
if (hit) return hit;
|
|
46
|
+
throw notFound(id, err);
|
|
35
47
|
}
|
|
36
48
|
}
|
|
37
49
|
|
|
38
50
|
const inbox = program
|
|
39
51
|
.command('inbox')
|
|
40
52
|
.description('Manage inbox and approval requests');
|
|
53
|
+
attachWalkthrough(inbox, ctx, program);
|
|
41
54
|
|
|
42
55
|
inbox
|
|
43
56
|
.command('list')
|
|
@@ -102,14 +115,52 @@ export function registerInboxCommands(program, ctx) {
|
|
|
102
115
|
.action(async (givenId, options) => {
|
|
103
116
|
auth.requireAuth();
|
|
104
117
|
const orgId = resolveOrgId(auth, program);
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
body: { action: options.action, comment: options.comment }
|
|
109
|
-
}
|
|
110
|
-
|
|
118
|
+
const format = program.opts().output;
|
|
119
|
+
const post = (itemId) => {
|
|
120
|
+
const ep = resolveEndpoint('inbox.respond', { orgId, itemId });
|
|
121
|
+
return client.post(ep.path, { service: ep.service, body: { action: options.action, comment: options.comment } });
|
|
122
|
+
};
|
|
123
|
+
// Resolve an approvalId to its card (server-side on a current server, else the client scan). When
|
|
124
|
+
// this profile cannot see the card, still ask the server: it knows WHY (an agent login, another
|
|
125
|
+
// org, already decided) and says so, where "not found" would hide it.
|
|
126
|
+
let itemId = givenId;
|
|
127
|
+
try { itemId = await resolveItemId(orgId, givenId); } catch (err) { if (err?.code !== 'not_found') throw err; }
|
|
128
|
+
let result;
|
|
129
|
+
try {
|
|
130
|
+
result = await post(itemId);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
if (err?.status === 404) throw notFound(givenId, err);
|
|
133
|
+
if (!result) {
|
|
134
|
+
const resolution = await alreadyResolvedFrom(err, orgId, itemId, options.action);
|
|
135
|
+
if (!resolution) throw err;
|
|
136
|
+
if (!resolution.matches) throw alreadyResolvedError(resolution, err);
|
|
137
|
+
if (format === 'json') console.log(JSON.stringify({ success: true, already_resolved: true, ...resolution }));
|
|
138
|
+
else if (format !== 'quiet') console.log(chalk.green(`✔ ${resolution.message}`));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (result?.item?.status === 'expired') {
|
|
143
|
+
throw new QuoxUserError(result.message || 'This approval had expired, so nothing ran.', {
|
|
144
|
+
next: 're-run the original command to ask for a fresh approval.', exitCode: 1, code: 'approval_expired',
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
const PAST = { approve: 'approved', reject: 'rejected', deny: 'denied', defer: 'deferred', resolve: 'resolved', answer: 'answered' };
|
|
148
|
+
printSuccess(`Item ${PAST[options.action] || `${options.action} recorded`}.`);
|
|
111
149
|
});
|
|
112
150
|
|
|
151
|
+
// A respond on a closed card: the server's structured answer, else (older server) read the card.
|
|
152
|
+
async function alreadyResolvedFrom(err, orgId, givenId, action) {
|
|
153
|
+
if (!err || err.status !== 400) return null;
|
|
154
|
+
const body = err.body || {};
|
|
155
|
+
if (body.code === 'already_resolved' && body.resolution) return { ...body.resolution, message: body.message };
|
|
156
|
+
if (!/not open for response/i.test(`${body.error || ''} ${err.message || ''}`)) return null;
|
|
157
|
+
try {
|
|
158
|
+
const ep = resolveEndpoint('inbox.get', { orgId, itemId: givenId });
|
|
159
|
+
const data = await client.get(ep.path, { service: ep.service });
|
|
160
|
+
return resolutionFromItem(data.item || data, action);
|
|
161
|
+
} catch { return null; }
|
|
162
|
+
}
|
|
163
|
+
|
|
113
164
|
inbox
|
|
114
165
|
.command('stats')
|
|
115
166
|
.description('Show inbox statistics')
|