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,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Motion levels (CLI-QUOX F3). One setting, four steps, chosen in /config or with QUOX_MOTION:
|
|
3
|
+
*
|
|
4
|
+
* quiet static except the elapsed timer (the old "reduced motion"; QUOX_REDUCED_MOTION=1 still means it)
|
|
5
|
+
* calm the default: exactly what shipped before levels (Orbit & Lock, the status Q, reveals, the ring)
|
|
6
|
+
* expressive calm, plus a short ignition glow when a tool row starts and a handoff dot while delegating
|
|
7
|
+
* cinematic expressive, plus the dragon: it greets you after Orbit & Lock on every launch, and plays a mood
|
|
8
|
+
* for approvals (wide eyes, amber glint), success (a wink) and failure (a puff of smoke)
|
|
9
|
+
*
|
|
10
|
+
* Pure: no process access except through the env argument, no timers. The frame helpers return cells
|
|
11
|
+
* { ch, rgb } like every other motion module, so the terminal and the web draw them the same way.
|
|
12
|
+
*/
|
|
13
|
+
export const LEVELS = ['quiet', 'calm', 'expressive', 'cinematic'];
|
|
14
|
+
export const DEFAULT_LEVEL = 'calm';
|
|
15
|
+
|
|
16
|
+
/** A saved or typed value -> a level, or null. The pre-levels values map across: full = calm, reduced = quiet. */
|
|
17
|
+
export function normaliseLevel(v) {
|
|
18
|
+
const s = String(v ?? '').trim().toLowerCase();
|
|
19
|
+
if (s === 'full') return 'calm';
|
|
20
|
+
if (s === 'reduced') return 'quiet';
|
|
21
|
+
return LEVELS.includes(s) ? s : null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The level in force. QUOX_REDUCED_MOTION=1 and QUOX_TUI_LITE always mean quiet (accessibility wins). */
|
|
25
|
+
export function motionLevel(env = process.env) {
|
|
26
|
+
if (env.QUOX_REDUCED_MOTION === '1' || env.QUOX_TUI_LITE) return 'quiet';
|
|
27
|
+
return normaliseLevel(env.QUOX_MOTION) || DEFAULT_LEVEL;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const atLeast = (level, min) => LEVELS.indexOf(normaliseLevel(level) || DEFAULT_LEVEL) >= LEVELS.indexOf(min);
|
|
31
|
+
|
|
32
|
+
/** What each level turns on. launchEgg: the rare dragon greeting on launch (cinematic greets every time instead). */
|
|
33
|
+
export function levelFeatures(level) {
|
|
34
|
+
const l = normaliseLevel(level) || DEFAULT_LEVEL;
|
|
35
|
+
return {
|
|
36
|
+
animate: l !== 'quiet',
|
|
37
|
+
ignition: atLeast(l, 'expressive'),
|
|
38
|
+
handoff: atLeast(l, 'expressive'),
|
|
39
|
+
introDragon: l === 'cinematic',
|
|
40
|
+
moods: l === 'cinematic',
|
|
41
|
+
launchEgg: l === 'calm' || l === 'expressive',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const clamp = (x, a = 0, b = 1) => Math.max(a, Math.min(b, x));
|
|
46
|
+
|
|
47
|
+
// ---- tool-row ignition glow --------------------------------------------------------------------------------
|
|
48
|
+
export const IGNITION_MS = 700;
|
|
49
|
+
/** Glow 0..1 at ms since a tool row started running: up in 80 ms, then an eased fall to 0 at IGNITION_MS. */
|
|
50
|
+
export function ignitionGlow(age) {
|
|
51
|
+
if (!(age > 0) || age >= IGNITION_MS) return 0;
|
|
52
|
+
if (age <= 80) return age / 80;
|
|
53
|
+
const u = (age - 80) / (IGNITION_MS - 80);
|
|
54
|
+
return (1 - u) ** 2;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ---- handoff dot -------------------------------------------------------------------------------------------
|
|
58
|
+
export const HANDOFF_MS = 900;
|
|
59
|
+
const SKY = [56, 189, 248], VIOLET = [168, 85, 247], DIM = [58, 66, 82];
|
|
60
|
+
const mix = (a, b, t) => a.map((v, i) => Math.round(v + (b[i] - v) * t));
|
|
61
|
+
/**
|
|
62
|
+
* The handoff track at t ms: n cells, a bright dot travelling from Quox (left) to the agent it is handing to
|
|
63
|
+
* (right) once every HANDOFF_MS, with one fading cell behind it. The dot shifts sky to violet as it goes.
|
|
64
|
+
* still: the dot rests at the target (reduced motion never calls this, but a frozen frame must read right).
|
|
65
|
+
*/
|
|
66
|
+
export function handoffFrame(t, n = 6, { mono = false, still = false } = {}) {
|
|
67
|
+
const u = still ? 0.999 : (((t % HANDOFF_MS) + HANDOFF_MS) % HANDOFF_MS) / HANDOFF_MS;
|
|
68
|
+
const at = Math.min(n - 1, Math.floor(u * n));
|
|
69
|
+
return Array.from({ length: n }, (_, i) => {
|
|
70
|
+
const pos = n > 1 ? i / (n - 1) : 1;
|
|
71
|
+
if (i === at) return { ch: '●', rgb: mono ? null : mix(SKY, VIOLET, pos) };
|
|
72
|
+
if (i === at - 1 && !still) return { ch: '•', rgb: mono ? null : mix(DIM, mix(SKY, VIOLET, pos), 0.55) };
|
|
73
|
+
return { ch: '·', rgb: mono ? null : mix(DIM, SKY, 0.25 * clamp(pos)) };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ---- dragon moods (cinematic) ------------------------------------------------------------------------------
|
|
78
|
+
const FAILED_TURN = new Set(['failed', 'unconfirmed']); // the same reading as the sound earcons (sound/mapping.js)
|
|
79
|
+
/**
|
|
80
|
+
* prevState + action + nextState -> the mood the dragon should play, or null. Only accepted state CHANGES
|
|
81
|
+
* count: an approval card appearing (waiting), a running turn finishing (success / failure). An interrupted
|
|
82
|
+
* turn is the human pressing Esc: no reaction.
|
|
83
|
+
*/
|
|
84
|
+
export function moodForEvent(prev, action, next) {
|
|
85
|
+
if (!prev || !action || !next) return null;
|
|
86
|
+
if ((next.approvals?.length || 0) > (prev.approvals?.length || 0)) return 'waiting';
|
|
87
|
+
if (action.type !== 'event' || action.ev?.type !== 'turn.finished' || !prev.turn) return null;
|
|
88
|
+
const status = action.ev.status || 'completed';
|
|
89
|
+
if (FAILED_TURN.has(status)) return 'failure';
|
|
90
|
+
return status === 'completed' ? 'success' : null;
|
|
91
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QUOXON-MOUSE (owner 2026-10-15: "when I click on '◆ QLOOP-ENGINE' enter /say [slot]", "select a chunk of text I
|
|
3
|
+
* typed and delete it"). Optional, OFF by default: mouse tracking makes the terminal send clicks to Quox, so
|
|
4
|
+
* selecting or copying text then needs Shift+drag in most terminals (said when it is turned on).
|
|
5
|
+
*
|
|
6
|
+
* Pure parts only (the app wires them):
|
|
7
|
+
* - MOUSE_ON / MOUSE_OFF: SGR button tracking with drag (1000 + 1002 + 1006). Never any-motion (1003).
|
|
8
|
+
* - createScreenMirror: what Ink has put on the screen, as plain lines. Fed every string Ink writes (term.js), it
|
|
9
|
+
* models exactly Ink's output: text, newlines, eraseLines (frame repaints) and clear-terminal. The live frame is
|
|
10
|
+
* the last write; everything above it is static history. Wrapping is applied at the current width when read, so
|
|
11
|
+
* a resize reflows it the way the terminal does.
|
|
12
|
+
* - screenHit: a click's screen row -> the mirror line under it. Rows are counted up from the cursor (which Ink
|
|
13
|
+
* always leaves on the line after the frame), read with a cursor position report (DSR) at click time, so the
|
|
14
|
+
* map holds whether the frame sits at the bottom of a full screen or halfway down a fresh one.
|
|
15
|
+
* - classifyClick: what that line is (a ◆ sender, a card's answer key label, the shared ask's y/N/c).
|
|
16
|
+
* - composerIndexAt: a click in the composer rows -> a buffer index, through Ink's word wrap.
|
|
17
|
+
*/
|
|
18
|
+
import { graphemes, composerFrom } from './textBuffer.js';
|
|
19
|
+
|
|
20
|
+
/** The logical line (between newlines) holding buffer index `idx` -> { from, to }; a triple-click selects it. */
|
|
21
|
+
export function lineAt(text, idx) {
|
|
22
|
+
const t = String(text);
|
|
23
|
+
const i = Math.max(0, Math.min(idx, t.length));
|
|
24
|
+
const from = t.lastIndexOf('\n', i - 1) + 1;
|
|
25
|
+
const nl = t.indexOf('\n', i);
|
|
26
|
+
return { from, to: nl < 0 ? t.length : nl };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const MOUSE_HINT = 'Click your draft to place the cursor, drag to select (copies), double-click a word, triple-click a line. Shift+drag uses your terminal\'s own selection in most terminals. /mouse off gives native selection back.';
|
|
30
|
+
|
|
31
|
+
export const MOUSE_ON = '\x1b[?1000h\x1b[?1002h\x1b[?1006h';
|
|
32
|
+
export const MOUSE_OFF = '\x1b[?1006l\x1b[?1002l\x1b[?1000l';
|
|
33
|
+
export const DSR = '\x1b[6n';
|
|
34
|
+
/** Two presses on the same cell within this window are a double-click. */
|
|
35
|
+
export const DOUBLE_MS = 400;
|
|
36
|
+
/** How long a click waits for the terminal's cursor report before it is dropped. */
|
|
37
|
+
export const DSR_TIMEOUT_MS = 400;
|
|
38
|
+
|
|
39
|
+
export function mouseOnNote() {
|
|
40
|
+
return 'Mouse on. Click a ◆ sender to reply, click a card\'s y / n / c to answer, click or drag in your draft to place the cursor or select (Backspace deletes, typing replaces, double-click selects a word, triple-click a line). While it is on your terminal sends clicks and the wheel to Quox: hold Shift to select or copy text (Option on macOS Terminal and iTerm2), and use Shift+wheel or the scrollbar to scroll back. /mouse off returns all of it.';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ---- cell widths --------------------------------------------------------------------------------
|
|
44
|
+
const WIDE = /\p{Extended_Pictographic}/u;
|
|
45
|
+
const EAW = /[ᄀ-ᅟ⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-가-힣豈-︰-﹏-⦆¢-₩]/u;
|
|
46
|
+
/** Terminal cells one grapheme takes: 2 for emoji presentation and East Asian wide, else 1. */
|
|
47
|
+
export function cellWidth(g) {
|
|
48
|
+
const cp = g.codePointAt(0);
|
|
49
|
+
if (EAW.test(g)) return 2;
|
|
50
|
+
if (WIDE.test(g) && (cp >= 0x1f000 || g.includes('️'))) return 2;
|
|
51
|
+
return 1;
|
|
52
|
+
}
|
|
53
|
+
const cellsOf = (s) => graphemes(String(s)).reduce((n, x) => n + cellWidth(x.text), 0);
|
|
54
|
+
/** Grapheme index in `gs` covering 0-based cell `col` (gs.length when past the end). */
|
|
55
|
+
function cellToIndex(gs, col) {
|
|
56
|
+
let at = 0;
|
|
57
|
+
for (let i = 0; i < gs.length; i++) { const w = cellWidth(gs[i].text); if (col < at + w) return i; at += w; }
|
|
58
|
+
return gs.length;
|
|
59
|
+
}
|
|
60
|
+
/** 1-based start cell of grapheme index `i`. */
|
|
61
|
+
function indexToCell(gs, i) { let at = 1; for (let k = 0; k < i && k < gs.length; k++) at += cellWidth(gs[k].text); return at; }
|
|
62
|
+
|
|
63
|
+
// ---- screen mirror ------------------------------------------------------------------------------
|
|
64
|
+
const ANSI_RE = /\x1b\[[0-9;?<>=]*[ -\/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[()][0-9A-Za-z]|\x1b[=>78]/g;
|
|
65
|
+
const ERASE_RE = /^((?:\x1b\[2K\x1b\[1A)*\x1b\[2K\x1b\[G)/;
|
|
66
|
+
const CLEAR_RE = /\x1b\[2J|\x1bc/;
|
|
67
|
+
|
|
68
|
+
/** Wrap one plain line into rows of at most `cols` cells (what the terminal shows). */
|
|
69
|
+
function wrapRows(line, cols) {
|
|
70
|
+
if (!(cols > 0)) return [line];
|
|
71
|
+
const gs = graphemes(line);
|
|
72
|
+
if (!gs.length) return [''];
|
|
73
|
+
const out = []; let row = ''; let w = 0;
|
|
74
|
+
for (const g of gs) {
|
|
75
|
+
const cw = cellWidth(g.text);
|
|
76
|
+
if (w + cw > cols) { out.push(row); row = ''; w = 0; }
|
|
77
|
+
row += g.text; w += cw;
|
|
78
|
+
}
|
|
79
|
+
out.push(row);
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function createScreenMirror({ columns = () => null, cap = 3000 } = {}) {
|
|
84
|
+
let lines = ['']; // logical lines; the last one is the cursor line
|
|
85
|
+
let frameLen = 0; // logical lines written by the last write (the live frame, once Ink has written it)
|
|
86
|
+
const append = (text) => {
|
|
87
|
+
const parts = text.replace(ANSI_RE, '').replace(/\r/g, '').split('\n');
|
|
88
|
+
lines[lines.length - 1] += parts[0];
|
|
89
|
+
for (let i = 1; i < parts.length; i++) lines.push(parts[i]);
|
|
90
|
+
if (lines.length > cap) lines = lines.slice(lines.length - cap);
|
|
91
|
+
return parts.length - 1;
|
|
92
|
+
};
|
|
93
|
+
const m = {
|
|
94
|
+
/** One string Ink wrote, before any diffing. */
|
|
95
|
+
write(chunk) {
|
|
96
|
+
let s = String(chunk);
|
|
97
|
+
const clear = s.search(CLEAR_RE);
|
|
98
|
+
if (clear >= 0) {
|
|
99
|
+
const all = [...s.matchAll(/\x1b\[2J|\x1b\[3J|\x1b\[H|\x1bc/g)];
|
|
100
|
+
const last = all[all.length - 1];
|
|
101
|
+
s = s.slice(last.index + last[0].length);
|
|
102
|
+
lines = ['']; frameLen = 0;
|
|
103
|
+
}
|
|
104
|
+
const e = ERASE_RE.exec(s);
|
|
105
|
+
if (e) {
|
|
106
|
+
const n = (e[1].match(/\x1b\[2K/g) || []).length;
|
|
107
|
+
const drop = Math.min(n - 1, lines.length - 1);
|
|
108
|
+
lines.length -= drop;
|
|
109
|
+
lines[lines.length - 1] = '';
|
|
110
|
+
s = s.slice(e[1].length);
|
|
111
|
+
}
|
|
112
|
+
const plain = s.replace(ANSI_RE, '');
|
|
113
|
+
if (!plain) return;
|
|
114
|
+
const rows = append(s);
|
|
115
|
+
if (rows > 0) frameLen = rows;
|
|
116
|
+
},
|
|
117
|
+
/** Test helper: Ink repainting its frame as these lines. */
|
|
118
|
+
frame(fl) { m.write(`\x1b[2K\x1b[G${fl.join('\n')}\n`); },
|
|
119
|
+
/** Raw output we cannot place (a hand-over, a child on the terminal): forget what is above. */
|
|
120
|
+
reset() { lines = ['']; frameLen = 0; },
|
|
121
|
+
/** -> { rows, frameStart, staticRows, frameRows }: wrapped at the current width, cursor line excluded. */
|
|
122
|
+
snapshot() {
|
|
123
|
+
const cols = columns();
|
|
124
|
+
const body = lines.slice(0, -1);
|
|
125
|
+
const fl = Math.min(frameLen, body.length);
|
|
126
|
+
const stat = body.slice(0, body.length - fl).flatMap(l => wrapRows(l, cols));
|
|
127
|
+
const fr = body.slice(body.length - fl).flatMap(l => wrapRows(l, cols));
|
|
128
|
+
return { rows: [...stat, ...fr], frameStart: stat.length, staticRows: stat, frameRows: fr };
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
return m;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** A click at 1-based (x, y) with the cursor on screen row `cursorRow` -> the line under it, or null. */
|
|
135
|
+
export function screenHit(snap, cursorRow, x, y) {
|
|
136
|
+
if (!snap || !(cursorRow > 0)) return null;
|
|
137
|
+
const idx = snap.rows.length - (cursorRow - y);
|
|
138
|
+
if (y >= cursorRow || idx < 0 || idx >= snap.rows.length) return null;
|
|
139
|
+
return {
|
|
140
|
+
text: snap.rows[idx], col: x, inFrame: idx >= snap.frameStart,
|
|
141
|
+
frameRow: idx - snap.frameStart, frameLines: snap.rows.slice(snap.frameStart), rowsBelow: snap.rows.slice(idx + 1)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ---- what was clicked ---------------------------------------------------------------------------
|
|
146
|
+
const SENDER_RE = /^\s*◆ (\S+)( \(no reply address\))?/;
|
|
147
|
+
const CARD_KEY_RE = /(?<![\w/])([ync]) (approve|deny|chat|yes)(?![\w])/g;
|
|
148
|
+
const ASK_KEYS_RE = /(?<![\w/])(y|run)\/(N)\/(c)\s*$/;
|
|
149
|
+
const isAskRow = (r) => /^\s*▶ /.test(r) && (ASK_KEYS_RE.test(r) || /\b[ync] (approve|deny|chat)\b/.test(r));
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* -> { kind: 'sender', slot, who? } | { kind: 'key', key: 'y'|'n'|'c'|'run', source: 'approval'|'ask' } | null.
|
|
153
|
+
* A key needs the click ON its label. A card's labels count only in the live frame (a card is never answered from
|
|
154
|
+
* scrollback); an ask's y/N/c only when no newer ask sits below it on screen.
|
|
155
|
+
*/
|
|
156
|
+
export function classifyClick(hit) {
|
|
157
|
+
if (!hit || typeof hit.text !== 'string') return null;
|
|
158
|
+
const t = hit.text;
|
|
159
|
+
const gs = graphemes(t);
|
|
160
|
+
const s = SENDER_RE.exec(t);
|
|
161
|
+
if (s) return s[2] ? { kind: 'sender', slot: null, who: s[1] } : { kind: 'sender', slot: s[1].toLowerCase() };
|
|
162
|
+
const within = (from, len) => {
|
|
163
|
+
const gi = graphemes(t.slice(0, from)).length;
|
|
164
|
+
const a = indexToCell(gs, gi); const b = a + cellsOf(t.slice(from, from + len));
|
|
165
|
+
return hit.col >= a && hit.col < b;
|
|
166
|
+
};
|
|
167
|
+
if (hit.inFrame) {
|
|
168
|
+
CARD_KEY_RE.lastIndex = 0;
|
|
169
|
+
let m;
|
|
170
|
+
while ((m = CARD_KEY_RE.exec(t))) if (within(m.index, m[0].length)) return { kind: 'key', key: m[1], source: 'approval' };
|
|
171
|
+
}
|
|
172
|
+
const k = ASK_KEYS_RE.exec(t);
|
|
173
|
+
if (k && /^\s*▶ /.test(t)) {
|
|
174
|
+
if ((hit.rowsBelow || []).some(isAskRow)) return null;
|
|
175
|
+
const base = k.index + k[0].indexOf(k[1]);
|
|
176
|
+
const yPos = base, nPos = yPos + k[1].length + 1, cPos = nPos + 2;
|
|
177
|
+
if (within(yPos, k[1].length)) return { kind: 'key', key: k[1] === 'run' ? 'run' : 'y', source: 'ask' };
|
|
178
|
+
if (within(nPos, 1)) return { kind: 'key', key: 'n', source: 'ask' };
|
|
179
|
+
if (within(cPos, 1)) return { kind: 'key', key: 'c', source: 'ask' };
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ---- the composer -------------------------------------------------------------------------------
|
|
185
|
+
const isRule = (r) => /^\s*─/.test(r || '');
|
|
186
|
+
const isPrompt = (r) => /^› /.test(r || '') || r === '›';
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* A click on frame row `row` (0-based), 1-based column `col` -> the buffer index under it, or null when the row is
|
|
190
|
+
* not one of the composer's. The composer is the "› " row right under a rule, down to the next rule.
|
|
191
|
+
*/
|
|
192
|
+
export function composerIndexAt(frameLines, row, col, buf) {
|
|
193
|
+
let top = -1;
|
|
194
|
+
for (let i = frameLines.length - 1; i >= 1; i--) if (isPrompt(frameLines[i]) && isRule(frameLines[i - 1])) { top = i; break; }
|
|
195
|
+
if (top < 0 || row < top) return null;
|
|
196
|
+
let bottom = top;
|
|
197
|
+
while (bottom < frameLines.length && !isRule(frameLines[bottom])) bottom++;
|
|
198
|
+
if (row >= bottom) return null;
|
|
199
|
+
if (!buf.text) return 0;
|
|
200
|
+
const lines = buf.text.split('\n');
|
|
201
|
+
const starts = []; { let o = 0; for (const l of lines) { starts.push(o); o += l.length + 1; } }
|
|
202
|
+
const from = composerFrom(buf);
|
|
203
|
+
let r = top;
|
|
204
|
+
for (let li = from; li < lines.length && r < bottom; li++) {
|
|
205
|
+
const T = graphemes(lines[li]);
|
|
206
|
+
let p = 0; let first = true;
|
|
207
|
+
const rest = () => T.slice(p).some(g => !/\s/.test(g.text)); // trailing spaces never take a row of their own
|
|
208
|
+
while (r < bottom && (first || rest())) {
|
|
209
|
+
const R = graphemes(String(frameLines[r]).replace(/\s+$/, '')).slice(2);
|
|
210
|
+
while (!first && p < T.length && /\s/.test(T[p].text) && R.length && !/\s/.test(R[0].text)) p++;
|
|
211
|
+
const take = Math.min(R.length, T.length - p);
|
|
212
|
+
if (r === row) {
|
|
213
|
+
const gi = Math.min(T.length, p + Math.min(cellToIndex(R, Math.max(0, col - 3)), take));
|
|
214
|
+
return starts[li] + (gi < T.length ? T[gi].index : lines[li].length);
|
|
215
|
+
}
|
|
216
|
+
p += Math.max(take, 0); r++; first = false;
|
|
217
|
+
if (take <= 0) break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return buf.text.length;
|
|
221
|
+
}
|
|
222
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Z2H-R2B: what Quox does when NO AI provider is connected. A turn is answered locally (no server call),
|
|
3
|
+
* with a full friendly card; nothing here names one vendor as the default. "add a host" has a deterministic
|
|
4
|
+
* path that needs no AI.
|
|
5
|
+
*/
|
|
6
|
+
import { CHOICES } from './model.js';
|
|
7
|
+
import { providerState } from './providers.js';
|
|
8
|
+
|
|
9
|
+
export const NO_AI_STATUS = 'no AI connected';
|
|
10
|
+
export const NO_AI_CARD = [
|
|
11
|
+
'No AI is connected yet. Pick one:',
|
|
12
|
+
'/connect openai (recommended), /connect anthropic, /connect ollama <host>, or /connect for the list.'
|
|
13
|
+
];
|
|
14
|
+
export const HOSTS_USAGE = '/hosts add';
|
|
15
|
+
|
|
16
|
+
/** -> true when at least one provider is connected, false when readable and none is, null when nothing could be read. */
|
|
17
|
+
export function anyProviderConnected(vault, caps) {
|
|
18
|
+
let known = false;
|
|
19
|
+
for (const c of CHOICES) {
|
|
20
|
+
const st = providerState({ cap: c.cap || c.id, vault: c.vault }, caps, vault).state;
|
|
21
|
+
if (st === 'connected') return true;
|
|
22
|
+
if (st !== 'unknown') known = true;
|
|
23
|
+
}
|
|
24
|
+
return known ? false : null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const asksAddHost = (text) => /^\s*(?:please\s+)?(?:(?:i\s+(?:want|need)\s+to|can\s+you|help\s+me|let'?s)\s+)?(?:add|connect|enrol+|enroll|register|onboard)\s+(?:a|my|another|the)?\s*(?:new\s+)?(?:host|server|machine|node|box)s?\b/i.test(String(text || '').replace(/[.?!]+\s*$/, ''));
|
|
28
|
+
|
|
29
|
+
/** Z2H-R4: "watch my servers" and friends; watching starts with adding a host, which needs no AI. */
|
|
30
|
+
export const asksWatchHosts = (text) => /^\s*(?:please\s+)?(?:(?:i\s+(?:want|need)\s+(?:you\s+)?to|can\s+you|help\s+me)\s+)?(?:watch|monitor|keep\s+an\s+eye\s+on|look\s+after|keep\s+watch\s+(?:on|over))\s+(?:my|our|the)\s+(?:\w+\s+)?(?:servers?|hosts?|machines?|nodes?|boxes|fleet|homelab|infrastructure|infra)\b/i.test(String(text || '').replace(/[.?!]+\s*$/, ''));
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The deterministic add-a-host lines. `base` = the server URL when known. `check` = the result of probing
|
|
34
|
+
* `${base}/install` ({ ok, status }) or null when it could not be read. Z2H-3c: a public install answered 503
|
|
35
|
+
* there (no QuoxAgent bundled), so the curl line we printed failed on the user's host; a failed probe now says
|
|
36
|
+
* so plainly instead of handing out a command that cannot work. Unreadable (null) keeps the old lines.
|
|
37
|
+
*/
|
|
38
|
+
export function addHostLines(base, check = null) {
|
|
39
|
+
const b = String(base || '').replace(/\/+$/, '') || '<your-quox-url>';
|
|
40
|
+
if (check && check.ok === false && Number(check.status) >= 400) {
|
|
41
|
+
return [
|
|
42
|
+
`This Quox cannot enrol hosts yet: its host installer is not available (${b}/install answered HTTP ${check.status}).`,
|
|
43
|
+
'The QuoxAgent installer is not bundled in this install, so the add-a-host command would fail on your machine.',
|
|
44
|
+
'Nothing was changed on either machine.'
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
return [
|
|
48
|
+
'To add a host, no AI needed. On the machine you want to add, run:',
|
|
49
|
+
// REL-423: never `curl | sudo bash`: that pipe exits 0 when the download fails (TLS error, HTTP error) and runs
|
|
50
|
+
// whatever partial body arrived. Download to a temp file with curl -f, then run it, so a failure stops loudly.
|
|
51
|
+
// Two lines (shell continuation) so the TUI, which clips long lines, never cuts the command short.
|
|
52
|
+
`( f=$(mktemp) && trap 'rm -f "$f"' EXIT && curl -fsSL ${b}/install -o "$f" && \\`,
|
|
53
|
+
` sudo bash "$f" --host-id <name> )`,
|
|
54
|
+
'Self-signed certificate? Run quox fleet enrol <name>: it prints this with the certificate pinned.',
|
|
55
|
+
'Then check it arrived with: quox fleet hosts'
|
|
56
|
+
];
|
|
57
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QUOXON objective offer (owner, live 2026-10-25 19:47). Attached to SEER (advisory, no image/X/site tools) the owner asked
|
|
3
|
+
* for a blog article, an image, a publish and a tweet draft, and got "I couldn't". Offer, don't give up: the server sends
|
|
4
|
+
* turn.objective_suggested (from the turn's tool selection and the shape of the ask, never the answer text) and the TUI
|
|
5
|
+
* asks once. It REPLACES the single-turn handoff card (one ask, not two):
|
|
6
|
+
*
|
|
7
|
+
* ⚠ SEER asks: Make this an objective that runs until done?
|
|
8
|
+
* do create objective "<the user's words>"; you approve the plan, then each publish/post step
|
|
9
|
+
* why SEER can't run x post.
|
|
10
|
+
* ▶ Make this an objective that runs until done? y yes · n no
|
|
11
|
+
*
|
|
12
|
+
* y = `quox objective create <goal>` + start (the plan decomposes and lands as an approval card). n does nothing.
|
|
13
|
+
*/
|
|
14
|
+
import { askText } from './ask.js';
|
|
15
|
+
|
|
16
|
+
const clipQ = (s, n) => { const t = String(s ?? '').replace(/\s+/g, ' ').trim(); return t.length > n ? `${t.slice(0, n - 1)}…` : t; };
|
|
17
|
+
|
|
18
|
+
/** The suggestion from a turn event, when it applies to THIS window (attached to the agent it names). Else null. */
|
|
19
|
+
export function objectiveFromEvent(ev, attachedAgent) {
|
|
20
|
+
if (!ev || ev.type !== 'turn.objective_suggested') return null;
|
|
21
|
+
const o = ev.objective_suggested;
|
|
22
|
+
const at = String(attachedAgent || '').toLowerCase();
|
|
23
|
+
if (!o || typeof o !== 'object' || !o.from || !o.goal || !at) return null;
|
|
24
|
+
if (String(o.from).toLowerCase() !== at) return null; // a suggestion about some other agent
|
|
25
|
+
const loop = o.mode === 'loop' && o.loop && Array.isArray(o.loop.capability_scope) && o.loop.capability_scope.length ? { goal_condition: String(o.loop.goal_condition || o.goal), capability_scope: o.loop.capability_scope.map(String), ...(o.loop.plain ? { plain: true } : {}), ...(Number.isInteger(o.loop.max_iterations) ? { max_iterations: o.loop.max_iterations } : {}), ...(o.loop.summary ? { summary: String(o.loop.summary) } : {}), ...(o.loop.until ? { until: String(o.loop.until) } : {}), ...(Number.isInteger(o.loop.every_seconds) ? { every_seconds: o.loop.every_seconds } : {}), ...(Number.isInteger(o.loop.max_duration_seconds) ? { max_duration_seconds: o.loop.max_duration_seconds } : {}), ...(Array.isArray(o.loop.done_check) && o.loop.done_check.length ? { done_check: o.loop.done_check } : {}) } : null; // QLOOP: a ready, engine-valid loop spec
|
|
26
|
+
return { from: at, fromName: String(o.from_name || at.toUpperCase()), goal: String(o.goal), reason: String(o.reason || '').trim(), tools: Array.isArray(o.tools) ? o.tools : [], ...(loop ? { loop } : {}) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The ask line (drawn as the approval-style card). The goal is clipped for the card only, never in what yes creates. */
|
|
30
|
+
export function objectiveAskText(o, cols = 100) {
|
|
31
|
+
if (o.loop && o.loop.plain) { // the user never said "loop": say what it does, in plain words, whole (it wraps; never clipped)
|
|
32
|
+
const mins = o.loop.max_duration_seconds ? Math.max(1, Math.round(o.loop.max_duration_seconds / 60)) : null;
|
|
33
|
+
const q = o.loop.summary ? `${o.loop.summary}?` : `This needs repeated checks: keep going until ${String(o.loop.goal_condition || o.goal).replace(/\s+/g, ' ').trim()}${mins ? `, up to ${mins} minute${mins === 1 ? '' : 's'}` : ''}?`;
|
|
34
|
+
return askText(q, { keys: 'y yes · n just once', who: o.fromName, reason: '', action: `keep going${o.loop.every_seconds ? `, checking about every ${o.loop.every_seconds} seconds,` : ''} using only ${o.loop.capability_scope.join(', ')}; you approve it before it starts` });
|
|
35
|
+
}
|
|
36
|
+
if (o.loop) return askText('Run this as a loop until it is done?', { keys: 'y/N', who: o.fromName, reason: o.reason, action: `create a loop "${clipQ(o.goal, Math.max(20, cols - 70))}" that may use only ${o.loop.capability_scope.join(', ')}; you approve it before it starts` });
|
|
37
|
+
const action = `create objective "${clipQ(o.goal, Math.max(20, cols - 60))}"; you approve the plan, then each publish/post step`;
|
|
38
|
+
return askText('Make this an objective that runs until done?', { keys: 'y/N', who: o.fromName, reason: o.reason, action });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const objectiveStartedNote = (id) => `▶ Objective ${id} started. The plan will land as an approval card.`;
|
|
42
|
+
export const objectiveWatchLine = (id) => `Watch it: quox objective get ${id}`;
|
|
43
|
+
export const objectiveFailedNote = (goal, err) => `Objective not created: ${String(err?.message || err || 'unknown error')}. Retry: quox objective create ${JSON.stringify(goal)} --checkpoint`;
|
|
44
|
+
|
|
45
|
+
// ---- report back into the conversation (owner 2026-10-26) ----
|
|
46
|
+
const TERMINAL = new Set(['completed', 'completed_with_errors', 'failed', 'cancelled', 'aborted']);
|
|
47
|
+
// A finished step's result is the node output JSON; its text is completion.value.
|
|
48
|
+
const textOf = (r) => { const s = String(r ?? ''); if (!s.trimStart().startsWith('{')) return s; try { const o = JSON.parse(s); return typeof o?.completion?.value === 'string' ? o.completion.value : s; } catch { return s; } };
|
|
49
|
+
const reportOf = (tasks) => {
|
|
50
|
+
const done = (tasks || []).filter(t => t.status === 'completed' && textOf(t.result).trim()).sort((a, b) => (a.sequence_num || 0) - (b.sequence_num || 0));
|
|
51
|
+
const pick = [...done].reverse().find(t => /report/i.test(String(t.title || ''))) || done[done.length - 1];
|
|
52
|
+
const text = pick ? textOf(pick.result).trim() : '';
|
|
53
|
+
return text.length > 1500 ? `${text.slice(0, 1499)}…` : text;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** The lines the chat prints when an objective ends: the glyph line, then its report (or what did not complete). */
|
|
57
|
+
export function objectiveFinishedLines(o) {
|
|
58
|
+
const tasks = Array.isArray(o.tasks) ? o.tasks : [];
|
|
59
|
+
const done = tasks.filter(t => t.status === 'completed').length;
|
|
60
|
+
const title = String(o.title || o.id || '').replace(/\s+/g, ' ').slice(0, 80);
|
|
61
|
+
const lines = [`◎ OBJECTIVE │ ${title} │ ${o.status}, ${done}/${tasks.length} steps`];
|
|
62
|
+
const report = reportOf(tasks);
|
|
63
|
+
if (report) lines.push(...report.split('\n').map(l => l.trimEnd()).filter(Boolean));
|
|
64
|
+
if (o.status !== 'completed' && o.error_message) lines.push(String(o.error_message).slice(0, 400));
|
|
65
|
+
return lines;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Poll an objective while this window is open and say how it ended. get() -> the objective with its tasks; a failed read is
|
|
70
|
+
* retried, never fatal. Resolves { finished, status }. Gives up quietly at maxMs or when stopped() (the inbox item covers a closed window).
|
|
71
|
+
*/
|
|
72
|
+
export async function watchObjective({ get, say, intervalMs = 8000, maxMs = 6 * 3600 * 1000, stopped = () => false } = {}) {
|
|
73
|
+
const t0 = Date.now();
|
|
74
|
+
while (Date.now() - t0 < maxMs && !stopped()) {
|
|
75
|
+
try {
|
|
76
|
+
const o = await get();
|
|
77
|
+
if (o && TERMINAL.has(o.status)) { for (const l of objectiveFinishedLines(o)) say(l); return { finished: true, status: o.status }; }
|
|
78
|
+
} catch { /* a transient read: try again */ }
|
|
79
|
+
await new Promise(r => setTimeout(r, intervalMs));
|
|
80
|
+
}
|
|
81
|
+
return { finished: false };
|
|
82
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E5 "While you were away": a factual digest since a moment, from GET /api/v1/away (collector). Every
|
|
3
|
+
* item carries the source that reported it and its time; nothing is summarised by a model. Sources the
|
|
4
|
+
* server could not read, or that went silent, come back in `blind` and are always named ("Can't see"),
|
|
5
|
+
* so an empty list never reads as an all clear it cannot vouch for.
|
|
6
|
+
*/
|
|
7
|
+
import { seg, fit, hhmm, plural, boxWidth, parseTs } from './model.js';
|
|
8
|
+
|
|
9
|
+
/** Importance: approvals waiting first (they block work), then alerts, failures, then outcomes. */
|
|
10
|
+
export const RANK = {
|
|
11
|
+
approval_waiting: 0, alert: 1, turn_failed: 2, objective_failed: 2, workflow_failed: 2,
|
|
12
|
+
approval_resolved: 3, objective_finished: 4, workflow_finished: 4, decision: 5
|
|
13
|
+
};
|
|
14
|
+
const GLYPH = {
|
|
15
|
+
approval_waiting: ['⚠ ', 'warn'], alert: ['⚠ ', 'warn'], turn_failed: ['✖ ', 'error'], objective_failed: ['✖ ', 'error'],
|
|
16
|
+
workflow_failed: ['✖ ', 'error'], approval_resolved: ['✔ ', 'ok'], objective_finished: ['✔ ', 'ok'],
|
|
17
|
+
workflow_finished: ['✔ ', 'ok'], decision: ['◆ ', 'indigo']
|
|
18
|
+
};
|
|
19
|
+
const rankOf = (it) => RANK[it?.kind] ?? 6;
|
|
20
|
+
|
|
21
|
+
export function rankItems(items) {
|
|
22
|
+
return [...(items || [])].sort((a, b) => rankOf(a) - rankOf(b) || (parseTs(b.ts) ?? 0) - (parseTs(a.ts) ?? 0));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** "5h", "45m", "2d" for the away window. */
|
|
26
|
+
export function awayFor(sinceTs, now) {
|
|
27
|
+
const t = parseTs(sinceTs);
|
|
28
|
+
if (t == null) return '';
|
|
29
|
+
const m = Math.max(0, Math.round((now - t) / 60000));
|
|
30
|
+
if (m < 60) return `${m}m`;
|
|
31
|
+
const h = Math.round(m / 60);
|
|
32
|
+
return h < 48 ? `${h}h` : `${Math.round(h / 24)}d`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** d: { since, items, blind, activity, compact?, max?, now } */
|
|
36
|
+
export function awayCard(d, cols) {
|
|
37
|
+
const now = d.now ?? Date.now();
|
|
38
|
+
const w = boxWidth(cols, 96), inner = w - 4;
|
|
39
|
+
const items = rankItems(d.items);
|
|
40
|
+
const max = d.compact ? (d.max ?? 5) : items.length;
|
|
41
|
+
const shown = items.slice(0, max);
|
|
42
|
+
const rows = [];
|
|
43
|
+
const win = awayFor(d.since, now);
|
|
44
|
+
if (!items.length) rows.push([seg(d.standing && d.standing.count > 0 ? 'Nothing new needed you.' : 'Nothing needed you.', 'muted')]);
|
|
45
|
+
for (const it of shown) {
|
|
46
|
+
const [g, role] = GLYPH[it.kind] || ['· ', 'muted'];
|
|
47
|
+
const meta = `${it.source || '?'} · ${hhmm(it.ts) || 'time unknown'}`;
|
|
48
|
+
if (d.compact) {
|
|
49
|
+
rows.push([seg(g, role), seg(fit(it.title, Math.max(8, inner - 2 - meta.length - 2)), 'value'), seg(` ${meta}`, 'muted')]);
|
|
50
|
+
} else {
|
|
51
|
+
rows.push([seg(g, role), seg(fit(it.title, inner - 2), 'value', rankOf(it) === 0)]);
|
|
52
|
+
rows.push([seg(' '), seg(fit(meta, inner - 2), 'muted')]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (items.length > shown.length) rows.push([seg(`+${items.length - shown.length} more · /away`, 'muted')]);
|
|
56
|
+
if (d.standing && d.standing.count > 0) rows.push([seg(fit(`${plural(d.standing.count, 'older item')} still need${d.standing.count === 1 ? 's' : ''} you · quox lucid`, inner), 'muted')]);
|
|
57
|
+
const act = d.activity;
|
|
58
|
+
if (act && act.total > 0 && !d.compact) {
|
|
59
|
+
const top = (act.agents || []).slice(0, 4).map(a => `${a.agent} ${a.count}`).join(', ');
|
|
60
|
+
rows.push([seg(' ')]);
|
|
61
|
+
rows.push([seg(fit(`${act.partial ? 'at least ' : ''}${plural(act.total, 'agent action')}${top ? `: ${top}` : ''}${act.source ? ` (${act.source})` : ''}`, inner), 'muted')]);
|
|
62
|
+
}
|
|
63
|
+
const blind = d.blind || [];
|
|
64
|
+
if (blind.length && d.compact) {
|
|
65
|
+
const what = blind.map(b => (b.reason ? `${b.source} (${b.reason})` : b.source)).join(', ');
|
|
66
|
+
rows.push([seg(fit(`Can't see: ${what}`, inner), 'faint')]);
|
|
67
|
+
} else if (blind.length) {
|
|
68
|
+
rows.push([seg("Can't see:", 'faint')]);
|
|
69
|
+
for (const b of blind) rows.push([seg(' '), seg(fit(`${b.source}${b.reason ? `: ${b.reason}` : ''}`, inner - 2), 'faint')]);
|
|
70
|
+
}
|
|
71
|
+
return { box: { width: w, title: `While you were away${win ? ` (${win})` : ''}`, titleRole: 'value', right: d.compact ? '' : `since ${hhmm(d.since)}`, rightRole: 'muted', border: items.some(i => rankOf(i) === 0) ? 'warn' : 'muted' }, rows };
|
|
72
|
+
}
|
|
@@ -11,6 +11,12 @@ import { fileURLToPath } from 'node:url';
|
|
|
11
11
|
import { findSkill } from '../skills.js';
|
|
12
12
|
import { packFrom } from './ladder.js';
|
|
13
13
|
import { slotFor } from './agent.js';
|
|
14
|
+
import { listFailure } from '../session.js';
|
|
15
|
+
import { resolveEndpoint } from '../../api/endpoints.js';
|
|
16
|
+
import { fleetFetchers } from './fleet.js';
|
|
17
|
+
import { healthFetchers } from './health.js';
|
|
18
|
+
import { proveFetchers } from './prove.js';
|
|
19
|
+
import { replayFetchers } from './replay.js';
|
|
14
20
|
|
|
15
21
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
16
22
|
const ASSISTANTS_DIR = path.resolve(HERE, '../../../../src/config/entities/assistants');
|
|
@@ -31,7 +37,7 @@ export function bundledIdentities(dir = ASSISTANTS_DIR) {
|
|
|
31
37
|
return out;
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
export function createObjectsApi({ client, orgId = () => null, home = os.homedir(), identities = bundledIdentities }) {
|
|
40
|
+
export function createObjectsApi({ client, orgId = () => null, home = os.homedir(), identities = bundledIdentities, profile = null }) {
|
|
35
41
|
const get = (service, p, query, timeout = 12000) => client.get(p, { service, query, retries: 0, timeout });
|
|
36
42
|
const post = (service, p, body, timeout = 8000) => client.post(p, { service, body, retries: 0, timeout });
|
|
37
43
|
const org = () => { const o = orgId(); return typeof o === 'string' ? o : null; };
|
|
@@ -66,7 +72,11 @@ export function createObjectsApi({ client, orgId = () => null, home = os.homedir
|
|
|
66
72
|
return out;
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
|
|
75
|
+
const self = {
|
|
76
|
+
...fleetFetchers({ get, org, listOf, why, resolveEndpoint }),
|
|
77
|
+
...healthFetchers({ get, org, listOf, why, resolveEndpoint }),
|
|
78
|
+
...replayFetchers({ get, listOf, why, verifyReceipt: (id, t) => verifyReceipt(id, t), aeeGet: (id) => aeeGet(id) }),
|
|
79
|
+
...proveFetchers({ get, listOf, resolveEndpoint, self: { proof: (t) => self.proof(t) } }),
|
|
70
80
|
/** E1 */
|
|
71
81
|
async skill(name) {
|
|
72
82
|
const all = listOf(await get('collector', '/quoxflow/skills', { orgId: org() || undefined }), 'data', 'skills');
|
|
@@ -110,14 +120,15 @@ export function createObjectsApi({ client, orgId = () => null, home = os.homedir
|
|
|
110
120
|
/** E4 */
|
|
111
121
|
async agent(name) {
|
|
112
122
|
const q = String(name || '').trim().toLowerCase();
|
|
113
|
-
let toolMap = null, toolErr = null;
|
|
114
|
-
try { toolMap = (await client.get('/api/v1/tools/agents', { service: 'collector',
|
|
123
|
+
let toolMap = null, toolErr = null, toolFail = null;
|
|
124
|
+
try { toolMap = (await client.get('/api/v1/tools/agents', { service: 'collector', retries: 0, timeout: 10000 }))?.agentTools || null; } catch (e) { toolErr = why(e); toolFail = e; }
|
|
115
125
|
let orgAgents = [];
|
|
116
126
|
try { if (org()) orgAgents = listOf(await get('auth', `/api/orgs/${org()}/agents`, { includeTools: 'false' }), 'agents', 'data'); } catch { /* optional */ }
|
|
117
127
|
const known = new Set([...Object.keys(toolMap || {}), ...idents().keys(), ...orgAgents.map(a => a.agent_id)]);
|
|
118
128
|
let id = [...known].find(k => k.toLowerCase() === q);
|
|
119
129
|
if (!id) id = [...known].find(k => String(nameOf(k)).toLowerCase() === q) || orgAgents.find(a => String(a.name || '').toLowerCase() === q)?.agent_id;
|
|
120
|
-
if (!id) return { error: `
|
|
130
|
+
if (!id && toolFail) return { error: `Couldn't check agents: ${listFailure(toolFail, profile)}`, tone: 'warn' }; // a failed list is a failure, never "no such agent"
|
|
131
|
+
if (!id) return { error: `No agent called "${name}".`, tone: 'warn' };
|
|
121
132
|
const record = orgAgents.find(a => a.agent_id === id) || null;
|
|
122
133
|
const ident = idents().get(id) || {};
|
|
123
134
|
let slot = null;
|
|
@@ -167,4 +178,5 @@ export function createObjectsApi({ client, orgId = () => null, home = os.homedir
|
|
|
167
178
|
return { data: { items } };
|
|
168
179
|
}
|
|
169
180
|
};
|
|
181
|
+
return self;
|
|
170
182
|
}
|