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
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Backend calls behind /watch, /automate and /crystallize, kept out of api.js (one spread there). */
|
|
2
|
+
import { resolveEndpoint } from '../../api/endpoints.js';
|
|
3
|
+
|
|
4
|
+
export function createKillerApi({ client }) {
|
|
5
|
+
return {
|
|
6
|
+
async watchCreate(body) { const ep = resolveEndpoint('agent-schedules.create'); return client.post(ep.path, { service: ep.service, body }); },
|
|
7
|
+
async watchList() { const ep = resolveEndpoint('agent-schedules.list'); return client.get(ep.path, { service: ep.service, retries: 0 }); },
|
|
8
|
+
async watchStop(id) { const ep = resolveEndpoint('agent-schedules.delete', { id }); return client.delete(ep.path, { service: ep.service }); },
|
|
9
|
+
/** quoxflow-direct, as `quox workflow apply`: -> the stored workflow (has .id). */
|
|
10
|
+
async workflowCreate(def) { const ep = resolveEndpoint('workflows.applyCreate'); const d = await client.post(ep.path, { service: ep.service, body: def }); return d?.data || d; },
|
|
11
|
+
/** As `quox trigger workflow-activate`: registers the workflow's triggers. */
|
|
12
|
+
async workflowActivate(workflowId) { const ep = resolveEndpoint('workflows.activate', { workflowId }); return client.post(ep.path, { service: ep.service, body: {} }); },
|
|
13
|
+
/** QSKL store create. { dryRun: true } asks only for the skill firewall verdict (throws 422 when blocked). */
|
|
14
|
+
async skillCreate(body, { dryRun = false } = {}) { const ep = resolveEndpoint('skills.create'); const d = await client.post(ep.path, { service: ep.service, body, query: dryRun ? { dry_run: 'true' } : undefined }); return d?.data || d; }
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -13,7 +13,8 @@ export function readUsage(u) {
|
|
|
13
13
|
const cacheRead = num(o.cache_read_input_tokens);
|
|
14
14
|
const cacheWrite = num(o.cache_creation_input_tokens);
|
|
15
15
|
const cost = num(o.cost_usd);
|
|
16
|
-
|
|
16
|
+
const costSource = typeof o.cost_source === 'string' ? o.cost_source : null; // 'claude-cli' = that CLI's own estimate
|
|
17
|
+
return { input, output, cacheRead, cacheWrite, cost, costSource, reported: input != null || output != null };
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function createLedger() {
|
|
@@ -23,9 +24,10 @@ export function createLedger() {
|
|
|
23
24
|
const finalize = (usage, convId) => {
|
|
24
25
|
if (!turn) return;
|
|
25
26
|
const t = turn; turn = null;
|
|
26
|
-
const
|
|
27
|
+
const raw = usage ?? t.doneUsage;
|
|
28
|
+
const u = readUsage(t.viaCli && raw && !raw.cost_source ? { ...raw, cost_source: 'claude-cli' } : raw); // the claude CLI path, from a server that does not label it yet
|
|
27
29
|
const key = `${t.provider || '?'}|${t.model || '?'}`;
|
|
28
|
-
const r = models.get(key) || { provider: t.provider, model: t.model, turns: 0, reported: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, costTurns: 0 };
|
|
30
|
+
const r = models.get(key) || { provider: t.provider, model: t.model, turns: 0, reported: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, costTurns: 0, estimate: 0, estimateTurns: 0 };
|
|
29
31
|
r.turns++;
|
|
30
32
|
if (u.reported) {
|
|
31
33
|
r.reported++;
|
|
@@ -33,14 +35,15 @@ export function createLedger() {
|
|
|
33
35
|
const id = convId || t.conversationId;
|
|
34
36
|
if (id) convs.set(id, (convs.get(id) || 0) + (u.input || 0) + (u.output || 0));
|
|
35
37
|
}
|
|
36
|
-
if (u.cost != null) { r.
|
|
38
|
+
if (u.cost != null && u.costSource) { r.estimate += u.cost; r.estimateTurns++; } // reported by a tool, not a price
|
|
39
|
+
else if (u.cost != null) { r.cost += u.cost; r.costTurns++; }
|
|
37
40
|
models.set(key, r);
|
|
38
41
|
};
|
|
39
42
|
return {
|
|
40
43
|
observe(ev) {
|
|
41
44
|
if (!ev || typeof ev !== 'object') return;
|
|
42
45
|
if (ev.type === 'turn.started') turn = { provider: ev.provider || null, model: ev.model || null, conversationId: ev.conversation_id || null, doneUsage: null };
|
|
43
|
-
else if (ev.type === 'done' && turn) turn.doneUsage = ev.usage || (ev.cost_usd != null ? { cost_usd: ev.cost_usd } : null);
|
|
46
|
+
else if (ev.type === 'done' && turn) { turn.doneUsage = ev.usage || (ev.cost_usd != null ? { cost_usd: ev.cost_usd } : null); turn.viaCli = Boolean(ev.cli_session_id); }
|
|
44
47
|
else if (ev.type === 'turn.finished') finalize(ev.usage, ev.conversation_id);
|
|
45
48
|
},
|
|
46
49
|
/** The stream closed: settle a turn whose turn.finished never came (legacy servers send only done). */
|
|
@@ -48,7 +51,7 @@ export function createLedger() {
|
|
|
48
51
|
rows: () => [...models.values()],
|
|
49
52
|
conversationTokens: (id) => (id ? convs.get(id) ?? 0 : 0),
|
|
50
53
|
totals() {
|
|
51
|
-
const t = { turns: 0, reported: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, costTurns: 0 };
|
|
54
|
+
const t = { turns: 0, reported: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, costTurns: 0, estimate: 0, estimateTurns: 0 };
|
|
52
55
|
for (const r of models.values()) for (const k of Object.keys(t)) t[k] += r[k];
|
|
53
56
|
return t;
|
|
54
57
|
}
|
|
@@ -65,7 +68,8 @@ function line(r) {
|
|
|
65
68
|
if (r.cacheRead || r.cacheWrite) parts.push(`cache ${n(r.cacheRead)} read / ${n(r.cacheWrite)} written`);
|
|
66
69
|
}
|
|
67
70
|
if (r.reported < r.turns) parts.push(`${r.turns - r.reported} without usage from the server`);
|
|
68
|
-
parts.push(r.costTurns === r.turns && r.turns ? usd(r.cost) : r.costTurns ? `${usd(r.cost)} for ${r.costTurns} of ${r.turns} turns` : 'cost not reported');
|
|
71
|
+
if (r.costTurns || !r.estimateTurns) parts.push(r.costTurns === r.turns && r.turns ? usd(r.cost) : r.costTurns ? `${usd(r.cost)} for ${r.costTurns} of ${r.turns} turns` : 'cost not reported');
|
|
72
|
+
if (r.estimateTurns) parts.push(`${usd(r.estimate)} claude CLI's own estimate${r.estimateTurns < r.turns ? ` for ${r.estimateTurns} of ${r.turns} turns` : ''}`);
|
|
69
73
|
return parts.join(' · ');
|
|
70
74
|
}
|
|
71
75
|
|
|
@@ -77,7 +81,8 @@ export function costModel(ledger) {
|
|
|
77
81
|
for (const r of rows) out.push({ kind: 'row', key: `${r.provider || '?'} ${r.model || ''}`.trim(), text: line(r) });
|
|
78
82
|
const t = ledger.totals();
|
|
79
83
|
if (rows.length > 1) out.push({ kind: 'row', key: 'Total', text: line(t) });
|
|
80
|
-
if (t.costTurns < t.turns) out.push({ kind: 'dim', text: 'Cost is blank where the server sent tokens but no cost_usd for that provider; Quox does not guess prices.' });
|
|
84
|
+
if (t.costTurns + t.estimateTurns < t.turns) out.push({ kind: 'dim', text: 'Cost is blank where the server sent tokens but no cost_usd for that provider; Quox does not guess prices.' });
|
|
85
|
+
if (t.estimateTurns) out.push({ kind: 'dim', text: "The Claude subscription path shows what the claude CLI reports: its own API-equivalent estimate, passed through as is. It is not a bill." });
|
|
81
86
|
if (t.reported < t.turns) out.push({ kind: 'dim', text: 'Some turns ended without usage (an older server, or a turn that failed or was interrupted).' });
|
|
82
87
|
return out;
|
|
83
88
|
}
|
|
@@ -10,7 +10,9 @@ import { c, clip } from '../theme.js';
|
|
|
10
10
|
const h = React.createElement;
|
|
11
11
|
|
|
12
12
|
export function ListView({ title = null, rows = [], sel = 0, cols = 80, hint = null, empty = null, more = 0 }) {
|
|
13
|
-
|
|
13
|
+
// The key column grows with the window (22 cells minimum, 32 at most) and always leaves a one-cell gap, so a name that fits is shown whole;
|
|
14
|
+
// one that does not gets the ellipsis from truncate-end, never a silent cut.
|
|
15
|
+
const keyW = Math.min(Math.max(22, Math.min(32, Math.floor(cols / 3))), Math.max(8, ...rows.map(r => [...String(r.key || '')].length)) + 1);
|
|
14
16
|
const textW = Math.max(10, cols - keyW - 6);
|
|
15
17
|
return h(Box, { flexDirection: 'column', paddingLeft: 1 },
|
|
16
18
|
title ? h(Text, { color: c('muted'), bold: true }, clip(title, cols - 2)) : null,
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QUOXON-FW6: the /model favourites, default and chooser behaviour, kept out of app.js.
|
|
3
|
+
*
|
|
4
|
+
* Favourites and the default live in the user's server-side preferences (api.modelPrefs and
|
|
5
|
+
* api.setModelPrefs); this module only holds the in-memory copy (`mp.prefs`) and calls the api to
|
|
6
|
+
* save. Provider parity: nothing here knows one provider from another; the connected list and the
|
|
7
|
+
* live model lists decide what is shown.
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
CHOICES,
|
|
11
|
+
connectedChoices,
|
|
12
|
+
providerId,
|
|
13
|
+
choiceId,
|
|
14
|
+
modelLabel,
|
|
15
|
+
normChoice,
|
|
16
|
+
} from '../model.js';
|
|
17
|
+
import {
|
|
18
|
+
normPrefs,
|
|
19
|
+
addFav,
|
|
20
|
+
rmFav,
|
|
21
|
+
setDefault,
|
|
22
|
+
clearDefault,
|
|
23
|
+
toggleFav,
|
|
24
|
+
favLines,
|
|
25
|
+
liveModels,
|
|
26
|
+
checkModelId,
|
|
27
|
+
chooserItems,
|
|
28
|
+
chooserRow,
|
|
29
|
+
currentKey,
|
|
30
|
+
label,
|
|
31
|
+
} from '../modelPrefs.js';
|
|
32
|
+
import { clip } from '../theme.js';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* deps: api, say, command, mc (ref to the stored choice), mp (ref { prefs, touched, warned }),
|
|
36
|
+
* picker, state (ref), bump, cols.
|
|
37
|
+
*/
|
|
38
|
+
export function createModelCmd({ api, say, command, mc, mp, picker, state, bump, cols }) {
|
|
39
|
+
const byId = (id) => CHOICES.find((x) => x.id === id) || null;
|
|
40
|
+
const current = () => currentKey(mc.current, choiceId);
|
|
41
|
+
|
|
42
|
+
/** Optimistic: the in-memory copy changes at once; the server (user preferences) is the truth. */
|
|
43
|
+
async function save(next) {
|
|
44
|
+
mp.current.prefs = next;
|
|
45
|
+
bump();
|
|
46
|
+
if (!api.setModelPrefs) return true;
|
|
47
|
+
const r = await api.setModelPrefs(next);
|
|
48
|
+
if (!r.synced) {
|
|
49
|
+
say(
|
|
50
|
+
`Saved on this machine only (${r.error || 'the server did not take it'}). It is not on ` +
|
|
51
|
+
'the server yet, so it will not follow you to other machines. ' +
|
|
52
|
+
'Making the change again retries.',
|
|
53
|
+
'warn',
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return Boolean(r.synced);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function addOrDefault(sub) {
|
|
60
|
+
const pid = providerId(sub.provider);
|
|
61
|
+
if (!pid) {
|
|
62
|
+
const ids = CHOICES.map((x) => x.id).join(', ');
|
|
63
|
+
return say(`No provider called "${sub.provider}". Use ${ids}.`, 'warn');
|
|
64
|
+
}
|
|
65
|
+
// New models appear before lists update: an id missing from the live list is kept, with a
|
|
66
|
+
// warning.
|
|
67
|
+
const live = await liveModels(api, pid);
|
|
68
|
+
const chk = checkModelId(live.ids, sub.model, byId(pid).name);
|
|
69
|
+
const prefs = mp.current.prefs;
|
|
70
|
+
const r =
|
|
71
|
+
sub.kind === 'fav-add' ? addFav(prefs, pid, chk.model) : setDefault(prefs, pid, chk.model);
|
|
72
|
+
if (r.added === false || r.ok === false) return say(r.text, r.tone);
|
|
73
|
+
await save(r.prefs);
|
|
74
|
+
say(clip(r.text, cols() - 4), r.tone);
|
|
75
|
+
if (chk.note) say(clip(chk.note, cols() - 4), 'warn');
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Handles the favourites / default / chooser forms of /model.
|
|
81
|
+
* -> undefined when `sub` (parseModelSub) is not one of them (the caller carries on), else a
|
|
82
|
+
* promise.
|
|
83
|
+
*/
|
|
84
|
+
function handle(sub) {
|
|
85
|
+
const prefs = mp.current.prefs;
|
|
86
|
+
switch (sub.kind) {
|
|
87
|
+
case 'error':
|
|
88
|
+
return Promise.resolve(say(sub.text, 'warn'));
|
|
89
|
+
case 'fav-list':
|
|
90
|
+
for (const [t, tone] of favLines(prefs, current())) say(clip(t, cols() - 4), tone);
|
|
91
|
+
return Promise.resolve();
|
|
92
|
+
case 'use': {
|
|
93
|
+
const f = prefs.favourites[sub.n - 1];
|
|
94
|
+
if (f) return command(`/model ${f.provider} ${f.model}`);
|
|
95
|
+
return Promise.resolve(say(`No favourite ${sub.n}. /model fav lists them.`, 'warn'));
|
|
96
|
+
}
|
|
97
|
+
case 'default-show':
|
|
98
|
+
return Promise.resolve(
|
|
99
|
+
say(
|
|
100
|
+
prefs.default
|
|
101
|
+
? `Default model: ${label(prefs.default)}. New sessions start on it when its ` +
|
|
102
|
+
'provider is connected. /model default clear removes it.'
|
|
103
|
+
: 'No default model. /model default <provider> <model> sets one ' +
|
|
104
|
+
'(or press d in the /model chooser).',
|
|
105
|
+
),
|
|
106
|
+
);
|
|
107
|
+
case 'default-clear': {
|
|
108
|
+
const c = clearDefault(prefs);
|
|
109
|
+
return save(c.prefs).then(() => say(c.text, c.tone));
|
|
110
|
+
}
|
|
111
|
+
case 'fav-rm': {
|
|
112
|
+
const r = rmFav(prefs, sub.ref);
|
|
113
|
+
return (r.removed ? save(r.prefs) : Promise.resolve()).then(() => say(r.text, r.tone));
|
|
114
|
+
}
|
|
115
|
+
case 'fav-add':
|
|
116
|
+
case 'default-set':
|
|
117
|
+
return addOrDefault(sub);
|
|
118
|
+
case 'chooser':
|
|
119
|
+
return openChooser();
|
|
120
|
+
default:
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** The /model chooser: favourites first, then each CONNECTED provider's live models. */
|
|
126
|
+
async function openChooser() {
|
|
127
|
+
if (!api.connections) return say('Model choice is not available in this mode.', 'warn');
|
|
128
|
+
const [res, caps] = await Promise.all([
|
|
129
|
+
api.connections(),
|
|
130
|
+
api.capabilities ? api.capabilities() : null,
|
|
131
|
+
]);
|
|
132
|
+
const conn = connectedChoices(res, caps);
|
|
133
|
+
if (!conn.length) {
|
|
134
|
+
return say(
|
|
135
|
+
res?.ok === false
|
|
136
|
+
? `Could not read what is connected: ${res.error || 'no answer'}`
|
|
137
|
+
: 'No AI provider is connected yet. /connect openai, /connect anthropic or ' +
|
|
138
|
+
'/connect ollama <url>.',
|
|
139
|
+
'warn',
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
const groups = await Promise.all(
|
|
143
|
+
conn.map(async (c) => {
|
|
144
|
+
const l = await liveModels(api, c.id);
|
|
145
|
+
const note = l.ok ? null : 'could not list its models; Enter uses the server default';
|
|
146
|
+
return { id: c.id, name: c.name, ids: l.ids.slice(0, 200), note };
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
const items = () => chooserItems(mp.current.prefs, groups);
|
|
150
|
+
const hidden = () =>
|
|
151
|
+
mp.current.prefs.favourites.filter((f) => !groups.some((g) => g.id === f.provider)).length;
|
|
152
|
+
const title = () => {
|
|
153
|
+
const now = modelLabel(state.current.ctx, mc.current) || 'auto';
|
|
154
|
+
const def = mp.current.prefs.default ? label(mp.current.prefs.default) : 'none';
|
|
155
|
+
const h = hidden();
|
|
156
|
+
const note = h ? ` · ${h} favourite${h === 1 ? '' : 's'} hidden, provider not connected` : '';
|
|
157
|
+
return `Model · current ${now} · default ${def}${note}`;
|
|
158
|
+
};
|
|
159
|
+
picker.open({
|
|
160
|
+
titleFn: title,
|
|
161
|
+
rowsFn: () => items().map((it) => chooserRow(it, mp.current.prefs, current())),
|
|
162
|
+
hint: '↑↓ select · Enter use · f favourite · d default · Esc close',
|
|
163
|
+
onChar: (ch, sel) => {
|
|
164
|
+
if (ch !== 'f' && ch !== 'd') return undefined;
|
|
165
|
+
const it = items()[sel];
|
|
166
|
+
if (!it || !it.model) {
|
|
167
|
+
say('That row has no model to save. Pick a model row.', 'warn');
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
const m = { provider: it.provider, model: it.model };
|
|
171
|
+
if (ch === 'f') {
|
|
172
|
+
const t = toggleFav(mp.current.prefs, m);
|
|
173
|
+
save(t.prefs);
|
|
174
|
+
say(`${t.added ? 'Added to' : 'Removed from'} favourites: ${label(m)}.`, 'ok');
|
|
175
|
+
} else {
|
|
176
|
+
save(setDefault(mp.current.prefs, m.provider, m.model).prefs);
|
|
177
|
+
say(`Default model: ${label(m)}. New sessions start on it.`, 'ok');
|
|
178
|
+
}
|
|
179
|
+
const next = items();
|
|
180
|
+
const same = (x) =>
|
|
181
|
+
x.kind === it.kind && x.provider === it.provider && x.model === it.model;
|
|
182
|
+
const j = next.findIndex(same);
|
|
183
|
+
return j >= 0 ? j : Math.min(sel, Math.max(0, next.length - 1));
|
|
184
|
+
},
|
|
185
|
+
onPick: (_, i) => {
|
|
186
|
+
const it = items()[i];
|
|
187
|
+
if (it) command(it.model ? `/model ${it.provider} ${it.model}` : `/model ${it.provider}`);
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* New sessions start on the saved default model when its provider is connected. A --provider
|
|
195
|
+
* flag, or a /model typed first, wins. A default on a provider that is not connected is said
|
|
196
|
+
* once and nothing changes. -> a cancel function.
|
|
197
|
+
*/
|
|
198
|
+
function startOnDefault({ flagged, off, dispatchCtx }) {
|
|
199
|
+
if (!api.modelPrefs || off) return () => {};
|
|
200
|
+
let dead = false;
|
|
201
|
+
(async () => {
|
|
202
|
+
let got = null;
|
|
203
|
+
try {
|
|
204
|
+
got = await api.modelPrefs();
|
|
205
|
+
} catch {
|
|
206
|
+
got = null;
|
|
207
|
+
}
|
|
208
|
+
if (dead || !got) return;
|
|
209
|
+
mp.current.prefs = normPrefs(got.prefs);
|
|
210
|
+
bump();
|
|
211
|
+
const def = mp.current.prefs.default;
|
|
212
|
+
if (!def || flagged || mp.current.touched) return;
|
|
213
|
+
let res = null;
|
|
214
|
+
let caps = null;
|
|
215
|
+
try {
|
|
216
|
+
[res, caps] = await Promise.all([
|
|
217
|
+
api.connections?.(),
|
|
218
|
+
api.capabilities ? api.capabilities() : null,
|
|
219
|
+
]);
|
|
220
|
+
} catch {
|
|
221
|
+
return; // cannot tell what is connected: stay quiet, the model is unchanged
|
|
222
|
+
}
|
|
223
|
+
if (dead || mp.current.touched || !res || res.ok === false) return;
|
|
224
|
+
const row = byId(def.provider);
|
|
225
|
+
if (!row) return;
|
|
226
|
+
if (!connectedChoices(res, caps).some((c) => c.id === row.id)) {
|
|
227
|
+
if (!mp.current.warned) {
|
|
228
|
+
mp.current.warned = true;
|
|
229
|
+
say(
|
|
230
|
+
`Your default model ${label(def)} is on ${row.name}, which is not connected, so ` +
|
|
231
|
+
`the model is unchanged. /connect ${row.connect} to use it, or ` +
|
|
232
|
+
'/model default clear.',
|
|
233
|
+
'warn',
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
let host = null;
|
|
239
|
+
if (row.dynamic) {
|
|
240
|
+
try {
|
|
241
|
+
const lst =
|
|
242
|
+
row.id === 'ollama' ? await api.ollamaModels?.() : await api.compatModels?.(row.id);
|
|
243
|
+
host = lst?.host || null;
|
|
244
|
+
} catch {
|
|
245
|
+
host = null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (dead || mp.current.touched) return;
|
|
249
|
+
const via = row.via ? { via: row.via } : {};
|
|
250
|
+
const at = host && !row.hosted ? { host } : {};
|
|
251
|
+
mc.current = normChoice({ provider: row.provider, model: def.model, ...via, ...at });
|
|
252
|
+
dispatchCtx();
|
|
253
|
+
bump();
|
|
254
|
+
})();
|
|
255
|
+
return () => {
|
|
256
|
+
dead = true;
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return { handle, openChooser, startOnDefault, save };
|
|
261
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* never disagree. Only commands that can work here are bright; anything this session cannot run is
|
|
5
5
|
* dim with the reason. Planned commands stay hidden unless typed exactly.
|
|
6
6
|
*/
|
|
7
|
-
import { REGISTRY } from '../commands.js';
|
|
7
|
+
import { REGISTRY, entryVisible } from '../commands.js';
|
|
8
8
|
|
|
9
9
|
export const PALETTE_MAX = 8;
|
|
10
10
|
|
|
@@ -25,7 +25,7 @@ export function unavailableReason(entry, { api = {}, caps = null, blocked = null
|
|
|
25
25
|
const cap = caps ? caps[entry.cap || entry.id] : null;
|
|
26
26
|
if (cap && cap.available === false) return cap.reason || 'not available on this server';
|
|
27
27
|
for (const m of entry.needs || []) if (typeof api[m] !== 'function') return 'not available in this mode';
|
|
28
|
-
if (blocked && entry.server) return '
|
|
28
|
+
if (blocked && entry.server) return '(sign in)';
|
|
29
29
|
return null;
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -44,11 +44,12 @@ export function capabilityMap(res) {
|
|
|
44
44
|
|
|
45
45
|
/** All palette rows for this session, registry order. */
|
|
46
46
|
export function paletteItems(ctx = {}, registry = REGISTRY) {
|
|
47
|
-
return registry.filter(e => e.kind === 'command').map(e => ({
|
|
47
|
+
return registry.filter(e => e.kind === 'command' && entryVisible(e)).map(e => ({
|
|
48
48
|
id: e.id,
|
|
49
49
|
name: '/' + e.id,
|
|
50
50
|
usage: e.usage,
|
|
51
|
-
desc: e.desc,
|
|
51
|
+
desc: e.short || e.desc, // the palette row is short (commandShort.js); the long wording is searched and shown by /help
|
|
52
|
+
longDesc: e.desc,
|
|
52
53
|
aliases: (e.aliases || []).map(a => '/' + a),
|
|
53
54
|
planned: e.status === 'planned',
|
|
54
55
|
disabled: e.status === 'planned' ? 'planned, not built yet' : unavailableReason(e, ctx)
|
|
@@ -73,16 +74,29 @@ export function fuzzy(q, s) {
|
|
|
73
74
|
export function filterPalette(items, query) {
|
|
74
75
|
const q = String(query ?? '').toLowerCase();
|
|
75
76
|
const exact = (it) => it.id === q || it.aliases.includes('/' + q);
|
|
77
|
+
const wordStart = (text) => q.length >= 2 && new RegExp('(^|[^a-z0-9])' + q.replace(/[^a-z0-9]/g, '\\$&')).test(String(text).toLowerCase());
|
|
76
78
|
const scored = [];
|
|
77
79
|
items.forEach((it, idx) => {
|
|
78
80
|
if (it.planned && !exact(it)) return;
|
|
79
|
-
|
|
81
|
+
// Rank: exact, prefix of the name, prefix of an alias, inside a name, a word in the description at a word start,
|
|
82
|
+
// then fuzzy subsequence, then any other mention in the description.
|
|
80
83
|
let best = null;
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
const long = it.longDesc || it.desc;
|
|
85
|
+
const nameSc = fuzzy(q, it.id);
|
|
86
|
+
if (nameSc === 0) best = 0;
|
|
87
|
+
else if (it.aliases.some(a => a.slice(1).toLowerCase().startsWith(q))) best = 1;
|
|
88
|
+
else if (nameSc != null && nameSc < 100) best = nameSc; // a substring of the name (10 + position)
|
|
89
|
+
else {
|
|
90
|
+
for (const a of it.aliases) { const sc = fuzzy(q, a.slice(1)); if (sc != null && sc < 100 && (best == null || sc < best)) best = sc; }
|
|
91
|
+
if (best == null && wordStart(long)) best = 60;
|
|
92
|
+
if (best == null) { // fuzzy subsequence of the name or an alias
|
|
93
|
+
for (const n of [it.id, ...it.aliases.map(a => a.slice(1))]) { const sc = fuzzy(q, n); if (sc != null && (best == null || sc < best)) best = sc; }
|
|
94
|
+
}
|
|
95
|
+
if (best == null && q.length >= 3 && long.toLowerCase().includes(q)) best = 500;
|
|
96
|
+
}
|
|
83
97
|
if (best == null) return;
|
|
84
98
|
if (exact(it)) best = -1;
|
|
85
|
-
if (it.disabled) best += 0.5; // a
|
|
99
|
+
if (it.disabled) best += best >= 10 ? 1000 : 0.5; // a non-prefix match on an unavailable command ranks below every working match; a prefix keeps its place on a tie
|
|
86
100
|
scored.push({ it, best, idx });
|
|
87
101
|
});
|
|
88
102
|
return scored.sort((a, b) => a.best - b.best || a.idx - b.idx).map(x => x.it);
|
|
@@ -4,27 +4,121 @@
|
|
|
4
4
|
* which narrows the list because the draft IS the filter.
|
|
5
5
|
*/
|
|
6
6
|
import { findEntry } from '../commands.js';
|
|
7
|
+
import { signedOutNotice } from '../sessionDead.js';
|
|
7
8
|
import { paletteQuery, paletteItems, filterPalette, completion, paletteView, PALETTE_MAX } from './palette.js';
|
|
9
|
+
import { argContext, levelFor, staticItems, rankArgs, applyArg } from './args.js';
|
|
10
|
+
import { mentionContext, rankMentions, applyMention } from '../mentions.js';
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
const ARG_TTL = 30000; // a fetched list is reused this long
|
|
13
|
+
const ARG_FAIL_TTL = 8000; // a failed fetch is quiet (no rows) and retried after this
|
|
14
|
+
|
|
15
|
+
const startsLike = (q, it) => {
|
|
16
|
+
const w = String(q || '').toLowerCase().replace(/^\//, '');
|
|
17
|
+
return [it.name, ...(it.aliases || [])].some(n => String(n).toLowerCase().replace(/^\//, '').startsWith(w));
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function createPalette({
|
|
21
|
+
getDraft, setDraft, run, say, ctx = () => ({}), source = null, refresh = () => {}, now = Date.now,
|
|
22
|
+
mentions = null // MENTIONS: { cache (mentions.js createMentionCache), local: () => receipt items }
|
|
23
|
+
}) {
|
|
24
|
+
const st = { sel: 0, q: null, dismissed: null, picked: false, aq: null, mq: null };
|
|
25
|
+
const cache = new Map(); // 'src:param' -> { at, ttl, items, pending }
|
|
26
|
+
/** Cached items for an async source; fetches when missing or stale. Never throws or blocks. */
|
|
27
|
+
const fetchItems = (src, param) => {
|
|
28
|
+
if (!source) return [];
|
|
29
|
+
const key = `${src}:${param ?? ''}`;
|
|
30
|
+
const hit = cache.get(key);
|
|
31
|
+
if (hit && (hit.pending || now() - hit.at < hit.ttl)) return hit.items;
|
|
32
|
+
const ent = { at: now(), ttl: ARG_TTL, items: hit?.items ?? [], pending: true };
|
|
33
|
+
cache.set(key, ent);
|
|
34
|
+
Promise.resolve().then(() => source(src, param)).then(
|
|
35
|
+
(items) => {
|
|
36
|
+
ent.items = Array.isArray(items) ? items : [];
|
|
37
|
+
ent.at = now(); ent.pending = false;
|
|
38
|
+
},
|
|
39
|
+
() => { ent.items = []; ent.ttl = ARG_FAIL_TTL; ent.at = now(); ent.pending = false; }
|
|
40
|
+
).then(() => refresh());
|
|
41
|
+
return ent.items;
|
|
42
|
+
};
|
|
43
|
+
/** The argument suggestions for the current draft, or null when it is not at an argument. */
|
|
44
|
+
const argState = () => {
|
|
45
|
+
const d = getDraft();
|
|
46
|
+
const cx = argContext(d);
|
|
47
|
+
if (!cx) return null;
|
|
48
|
+
const lv = levelFor(cx.entry, cx.words);
|
|
49
|
+
if (!lv) return null;
|
|
50
|
+
const param = lv.of === 'partial' ? cx.partial.slice(0, cx.partial.lastIndexOf('/') + 1) : lv.of != null ? cx.words[lv.of] : undefined; // 'partial': the directory typed so far (QUOXON-MESH /send paths)
|
|
51
|
+
const items = [...staticItems(lv), ...(lv.src ? fetchItems(lv.src, param) : [])];
|
|
52
|
+
const rows = rankArgs(items, cx.partial);
|
|
53
|
+
const key = d.slice(0, cx.start) + '\u0000' + cx.partial;
|
|
54
|
+
if (key !== st.aq) { st.aq = key; st.sel = 0; st.picked = false; }
|
|
55
|
+
return { cx, rows };
|
|
56
|
+
};
|
|
57
|
+
const argOpen = () => {
|
|
58
|
+
const d = getDraft();
|
|
59
|
+
if (st.dismissed !== null && st.dismissed !== d) st.dismissed = null;
|
|
60
|
+
if (st.dismissed === d) return null;
|
|
61
|
+
const a = argState();
|
|
62
|
+
return a && a.rows.length ? a : null;
|
|
63
|
+
};
|
|
64
|
+
/** MENTIONS: the @word at the end of the draft and its ranked rows from the session cache, or null. */
|
|
65
|
+
const mentionOpen = () => {
|
|
66
|
+
if (!mentions) return null;
|
|
67
|
+
const d = getDraft();
|
|
68
|
+
const cx = mentionContext(d);
|
|
69
|
+
if (!cx) return null;
|
|
70
|
+
if (st.dismissed !== null && st.dismissed !== d) st.dismissed = null;
|
|
71
|
+
if (st.dismissed === d) return null;
|
|
72
|
+
const rows = rankMentions([...mentions.cache.items(), ...(mentions.local?.() || [])], cx.partial);
|
|
73
|
+
const key = d.slice(0, cx.start) + '\u0000' + cx.partial;
|
|
74
|
+
if (key !== st.mq) { st.mq = key; st.sel = 0; st.picked = false; }
|
|
75
|
+
return rows.length ? { cx, rows } : null;
|
|
76
|
+
};
|
|
11
77
|
const rows = () => {
|
|
12
78
|
const q = paletteQuery(getDraft());
|
|
13
|
-
if (q !== st.q) { st.q = q; st.sel = 0; }
|
|
79
|
+
if (q !== st.q) { st.q = q; st.sel = 0; st.picked = false; }
|
|
14
80
|
return q === null ? [] : filterPalette(paletteItems(ctx()), q);
|
|
15
81
|
};
|
|
16
82
|
const isOpen = () => {
|
|
17
83
|
const d = getDraft();
|
|
18
84
|
if (st.dismissed !== null && st.dismissed !== d) st.dismissed = null;
|
|
19
|
-
|
|
85
|
+
if (paletteQuery(d) === null) return argOpen() !== null || mentionOpen() !== null;
|
|
86
|
+
return st.dismissed !== d;
|
|
20
87
|
};
|
|
88
|
+
/** MENTIONS keys: Up/Down choose, Tab or Enter accept, Esc closes. */
|
|
89
|
+
function onMentionKey(name, m) {
|
|
90
|
+
const n = m.rows.length;
|
|
91
|
+
switch (name) {
|
|
92
|
+
case 'up': st.sel = (st.sel - 1 + n) % n; st.picked = true; return true;
|
|
93
|
+
case 'down': st.sel = (st.sel + 1) % n; st.picked = true; return true;
|
|
94
|
+
case 'escape': st.dismissed = getDraft(); return true;
|
|
95
|
+
case 'tab': case 'enter': setDraft(applyMention(getDraft(), m.cx, m.rows[Math.min(st.sel, n - 1)].value)); return true;
|
|
96
|
+
default: return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
21
99
|
/** -> true when the key was the palette's. */
|
|
100
|
+
function onArgKey(name, a) {
|
|
101
|
+
const n = a.rows.length;
|
|
102
|
+
switch (name) {
|
|
103
|
+
case 'up': st.sel = (st.sel - 1 + n) % n; st.picked = true; return true;
|
|
104
|
+
case 'down': st.sel = (st.sel + 1) % n; st.picked = true; return true;
|
|
105
|
+
case 'escape': st.dismissed = getDraft(); return true;
|
|
106
|
+
case 'tab': case 'enter': {
|
|
107
|
+
if (name === 'enter' && !st.picked) return false; // Enter sends what was typed
|
|
108
|
+
const it = a.rows[Math.min(st.sel, n - 1)];
|
|
109
|
+
setDraft(applyArg(getDraft(), a.cx, it.value));
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
default: return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
22
115
|
function onKey(name) {
|
|
23
116
|
if (!isOpen()) return false;
|
|
117
|
+
if (paletteQuery(getDraft()) === null) { const a = argOpen(); return a ? onArgKey(name, a) : onMentionKey(name, mentionOpen()); }
|
|
24
118
|
const list = rows();
|
|
25
119
|
switch (name) {
|
|
26
|
-
case 'up': if (!list.length) return false; st.sel = (st.sel - 1 + list.length) % list.length; return true;
|
|
27
|
-
case 'down': if (!list.length) return false; st.sel = (st.sel + 1) % list.length; return true;
|
|
120
|
+
case 'up': if (!list.length) return false; st.sel = (st.sel - 1 + list.length) % list.length; st.picked = true; return true;
|
|
121
|
+
case 'down': if (!list.length) return false; st.sel = (st.sel + 1) % list.length; st.picked = true; return true;
|
|
28
122
|
case 'escape': st.dismissed = getDraft(); return true;
|
|
29
123
|
case 'tab': {
|
|
30
124
|
const it = list[st.sel];
|
|
@@ -37,7 +131,10 @@ export function createPalette({ getDraft, setDraft, run, say, ctx = () => ({}) }
|
|
|
37
131
|
if (typed && typed.kind === 'command') return false; // an exact command runs as typed (planned ones say so)
|
|
38
132
|
const it = list[st.sel];
|
|
39
133
|
if (!it) return false; // nothing matches: the normal path says "Unknown command"
|
|
40
|
-
|
|
134
|
+
// A loose (subsequence) match only runs when the user chose it with the arrows. Typing an unknown
|
|
135
|
+
// word and pressing Enter must not run some other command (z2h: "/hat" ran /approvals).
|
|
136
|
+
if (!st.picked && !startsLike(st.q, it)) return false;
|
|
137
|
+
if (it.disabled) { say(signedOutNotice(it.name, it.disabled), 'warn'); return true; }
|
|
41
138
|
setDraft('');
|
|
42
139
|
run(it.name);
|
|
43
140
|
return true;
|
|
@@ -46,6 +143,19 @@ export function createPalette({ getDraft, setDraft, run, say, ctx = () => ({}) }
|
|
|
46
143
|
}
|
|
47
144
|
}
|
|
48
145
|
function view() {
|
|
146
|
+
if (paletteQuery(getDraft()) === null) {
|
|
147
|
+
const a = argOpen();
|
|
148
|
+
if (!a) {
|
|
149
|
+
const mo = mentionOpen();
|
|
150
|
+
if (!mo) return { rows: [], sel: 0, more: 0, empty: null, hint: null };
|
|
151
|
+
const mv = paletteView(mo.rows, st.sel, PALETTE_MAX);
|
|
152
|
+
return { rows: mv.rows.map(it => ({ key: `@${it.value}`, text: `${it.kind}${it.label ? ` · ${it.label}` : ''}` })), sel: mv.sel, more: mv.more, empty: null, hint: '↑↓ select · Tab or Enter accept · Esc close' };
|
|
153
|
+
}
|
|
154
|
+
const v = paletteView(a.rows, st.sel, PALETTE_MAX);
|
|
155
|
+
const rows = v.rows.map(it => ({ key: it.value, text: it.desc || '' }));
|
|
156
|
+
const hint = '↑↓ select · Tab complete · Esc close';
|
|
157
|
+
return { rows, sel: v.sel, more: v.more, empty: null, hint };
|
|
158
|
+
}
|
|
49
159
|
const list = rows();
|
|
50
160
|
const v = paletteView(list, st.sel, PALETTE_MAX);
|
|
51
161
|
return {
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Every value comes from the caller's real data; anything not known is said plainly, never filled in.
|
|
4
4
|
*/
|
|
5
5
|
import { capabilityMap } from './palette.js';
|
|
6
|
+
import { CHOICES } from '../model.js';
|
|
7
|
+
import { providerState, capabilityName } from '../providers.js';
|
|
6
8
|
|
|
7
9
|
const n = (v) => Number(v || 0).toLocaleString('en-US');
|
|
8
10
|
const row = (key, text) => ({ kind: 'row', key, text: String(text) });
|
|
@@ -53,6 +55,11 @@ export function contextModel(d = {}) {
|
|
|
53
55
|
const mem = d.memory;
|
|
54
56
|
out.push(row('Memory', !mem ? '? not checked (/context checks it live)' : mem.ok ? 'connected (persistent memory answers)' : `not reachable: ${mem.error}`));
|
|
55
57
|
out.push(row('Agent', d.agent || 'CommanderQ (quox)'));
|
|
58
|
+
if (d.screen) {
|
|
59
|
+
const l = d.screen.last;
|
|
60
|
+
out.push(row('Terminal', !d.screen.on ? 'off for this session (/context on)' : l ? `${l.bytes} bytes sent last turn: ${l.sections.join(', ') || 'nothing new'}${l.unchanged?.length ? ` (unchanged: ${l.unchanged.join(', ')})` : ''} · /context off stops it` : 'on, sent with your next message · /context off stops it'));
|
|
61
|
+
if (d.screen.on && l?.text) for (const t of l.text.split('\n')) out.push({ kind: 'dim', key: '', text: ' ' + t });
|
|
62
|
+
}
|
|
56
63
|
return out;
|
|
57
64
|
}
|
|
58
65
|
|
|
@@ -83,10 +90,12 @@ export async function chatChecks(api, providerTypes = ['openai', 'anthropic', 'o
|
|
|
83
90
|
}),
|
|
84
91
|
one('AI provider', async () => {
|
|
85
92
|
if (!api.connections) return { status: 'skip', detail: 'not available in this mode' };
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
93
|
+
// The same provider status as /model and /connect (providers.js): the server's capabilities first.
|
|
94
|
+
const [r, caps] = await Promise.all([api.connections(), api.capabilities ? api.capabilities() : null]);
|
|
95
|
+
const ready = CHOICES.filter(c => providerState({ cap: c.cap || c.id, vault: c.vault }, caps, r).state === 'connected').map(c => c.name);
|
|
96
|
+
if (ready.length) return { status: 'pass', detail: `ready: ${ready.join(', ')}` };
|
|
97
|
+
if (!r.ok && !caps?.ok) return { status: 'fail', detail: `could not read the vault: ${r.error}` };
|
|
98
|
+
return { status: 'warn', detail: 'no AI provider is ready; /connect openai' };
|
|
90
99
|
}),
|
|
91
100
|
one('Capabilities', async () => {
|
|
92
101
|
if (!api.capabilities) return { status: 'skip', detail: 'not available in this mode' };
|
|
@@ -94,8 +103,14 @@ export async function chatChecks(api, providerTypes = ['openai', 'anthropic', 'o
|
|
|
94
103
|
if (res && res.ok === false) return res.missing ? { status: 'warn', detail: 'this server has no capability contract yet (GET /api/v1/capabilities)' } : { status: 'fail', detail: res.error || 'failed' };
|
|
95
104
|
const m = capabilityMap(res);
|
|
96
105
|
if (!m) return { status: 'warn', detail: 'unexpected capability response' };
|
|
97
|
-
const
|
|
98
|
-
|
|
106
|
+
const names = (a) => res.capabilities.filter(c => c?.availability === a).map(capabilityName);
|
|
107
|
+
const off = names('unavailable'), setup = names('requires_setup');
|
|
108
|
+
const more = (l) => `${l.slice(0, 4).join(', ')}${l.length > 4 ? ` +${l.length - 4} more` : ''}`;
|
|
109
|
+
const parts = [`${Object.keys(m).length} known`];
|
|
110
|
+
if (off.length) parts.push(`unavailable here: ${more(off)}`);
|
|
111
|
+
if (setup.length) parts.push(`needs setup: ${more(setup)}`);
|
|
112
|
+
if (!off.length && !setup.length) parts.push('all available');
|
|
113
|
+
return { status: off.length ? 'warn' : 'pass', detail: parts.join(', ') };
|
|
99
114
|
})
|
|
100
115
|
]);
|
|
101
116
|
}
|