quoxcli 0.0.0-stage → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +35 -0
- package/README.md +144 -2
- package/bin/quox.js +265 -0
- package/bin/quoxon.js +8 -0
- package/lib/api/endpoints/admin.js +151 -0
- package/lib/api/endpoints/aee.js +38 -0
- package/lib/api/endpoints/agent-attach.js +126 -0
- package/lib/api/endpoints/agent-schedules.js +26 -0
- package/lib/api/endpoints/agents.js +63 -0
- package/lib/api/endpoints/assistants.js +62 -0
- package/lib/api/endpoints/audit.js +14 -0
- package/lib/api/endpoints/auth.js +50 -0
- package/lib/api/endpoints/awareness.js +34 -0
- package/lib/api/endpoints/away.js +15 -0
- package/lib/api/endpoints/backup-schedules.js +20 -0
- package/lib/api/endpoints/backups.js +86 -0
- package/lib/api/endpoints/brain.js +140 -0
- package/lib/api/endpoints/budgets.js +62 -0
- package/lib/api/endpoints/calendarq.js +71 -0
- package/lib/api/endpoints/catalog.js +14 -0
- package/lib/api/endpoints/chat.js +61 -0
- package/lib/api/endpoints/consent.js +26 -0
- package/lib/api/endpoints/contactsq.js +68 -0
- package/lib/api/endpoints/conversations.js +46 -0
- package/lib/api/endpoints/custom-roles.js +92 -0
- package/lib/api/endpoints/divisions.js +56 -0
- package/lib/api/endpoints/domains.js +38 -0
- package/lib/api/endpoints/engagement.js +231 -0
- package/lib/api/endpoints/engine.js +11 -0
- package/lib/api/endpoints/entity.js +50 -0
- package/lib/api/endpoints/executions.js +45 -0
- package/lib/api/endpoints/fetchAllowlist.js +27 -0
- package/lib/api/endpoints/file.js +20 -0
- package/lib/api/endpoints/files.js +40 -0
- package/lib/api/endpoints/github.js +21 -0
- package/lib/api/endpoints/governance.js +43 -0
- package/lib/api/endpoints/hat.js +66 -0
- package/lib/api/endpoints/inbox.js +52 -0
- package/lib/api/endpoints/inboxq.js +120 -0
- package/lib/api/endpoints/index.js +242 -0
- package/lib/api/endpoints/invites.js +19 -0
- package/lib/api/endpoints/llm-policy.js +43 -0
- package/lib/api/endpoints/lucid.js +89 -0
- package/lib/api/endpoints/members.js +32 -0
- package/lib/api/endpoints/memory.js +94 -0
- package/lib/api/endpoints/mfa.js +32 -0
- package/lib/api/endpoints/mind.js +13 -0
- package/lib/api/endpoints/mon.js +184 -0
- package/lib/api/endpoints/notesq.js +43 -0
- package/lib/api/endpoints/objectives.js +68 -0
- package/lib/api/endpoints/orgs.js +38 -0
- package/lib/api/endpoints/plugins.js +152 -0
- package/lib/api/endpoints/powers.js +75 -0
- package/lib/api/endpoints/presence.js +33 -0
- package/lib/api/endpoints/prompts.js +69 -0
- package/lib/api/endpoints/qlarity.js +122 -0
- package/lib/api/endpoints/quoxbox.js +55 -0
- package/lib/api/endpoints/quoxchat.js +54 -0
- package/lib/api/endpoints/quoxflow.js +32 -0
- package/lib/api/endpoints/quoxplan.js +121 -0
- package/lib/api/endpoints/quoxseo.js +114 -0
- package/lib/api/endpoints/reach.js +47 -0
- package/lib/api/endpoints/router.js +17 -0
- package/lib/api/endpoints/runs.js +38 -0
- package/lib/api/endpoints/safety.js +53 -0
- package/lib/api/endpoints/scim.js +46 -0
- package/lib/api/endpoints/scopes.js +14 -0
- package/lib/api/endpoints/security.js +69 -0
- package/lib/api/endpoints/setup.js +90 -0
- package/lib/api/endpoints/skills.js +200 -0
- package/lib/api/endpoints/sso.js +67 -0
- package/lib/api/endpoints/stepup.js +20 -0
- package/lib/api/endpoints/tags.js +32 -0
- package/lib/api/endpoints/teams.js +56 -0
- package/lib/api/endpoints/telephony.js +46 -0
- package/lib/api/endpoints/tg.js +56 -0
- package/lib/api/endpoints/tools.js +50 -0
- package/lib/api/endpoints/traces.js +157 -0
- package/lib/api/endpoints/triggers.js +96 -0
- package/lib/api/endpoints/user-library.js +41 -0
- package/lib/api/endpoints/users.js +20 -0
- package/lib/api/endpoints/v1.js +585 -0
- package/lib/api/endpoints/vault.js +192 -0
- package/lib/api/endpoints/voice.js +22 -0
- package/lib/api/endpoints/volt.js +84 -0
- package/lib/api/endpoints/ward.js +47 -0
- package/lib/api/endpoints/webhooks.js +46 -0
- package/lib/api/endpoints/workflows.js +169 -0
- package/lib/api/endpoints/x.js +32 -0
- package/lib/api/endpoints.js +15 -0
- package/lib/commands/actions.js +61 -0
- package/lib/commands/admin.js +375 -0
- package/lib/commands/aee.js +99 -0
- package/lib/commands/agent.js +337 -0
- package/lib/commands/agentAttach.js +1528 -0
- package/lib/commands/approvals.js +119 -0
- package/lib/commands/aside.js +24 -0
- package/lib/commands/ask.js +144 -0
- package/lib/commands/assistant.js +169 -0
- package/lib/commands/auth.js +464 -0
- package/lib/commands/awareness.js +88 -0
- package/lib/commands/backup.js +377 -0
- package/lib/commands/brain.js +1085 -0
- package/lib/commands/budget.js +241 -0
- package/lib/commands/build.js +170 -0
- package/lib/commands/calendar.js +216 -0
- package/lib/commands/capabilities.js +34 -0
- package/lib/commands/chat.js +295 -0
- package/lib/commands/claude.js +380 -0
- package/lib/commands/config-cmd.js +106 -0
- package/lib/commands/consent.js +84 -0
- package/lib/commands/contacts.js +1016 -0
- package/lib/commands/contactsSelftest.js +132 -0
- package/lib/commands/conversation.js +105 -0
- package/lib/commands/dashboard.js +43 -0
- package/lib/commands/department.js +96 -0
- package/lib/commands/dev.js +620 -0
- package/lib/commands/devResident.js +326 -0
- package/lib/commands/devSupervisor.js +1062 -0
- package/lib/commands/discord.js +132 -0
- package/lib/commands/doctor.js +181 -0
- package/lib/commands/domain.js +114 -0
- package/lib/commands/done.js +347 -0
- package/lib/commands/engagement.js +597 -0
- package/lib/commands/entity.js +119 -0
- package/lib/commands/facebook.js +212 -0
- package/lib/commands/fetchAllowlist.js +71 -0
- package/lib/commands/file.js +165 -0
- package/lib/commands/fleet.js +1189 -0
- package/lib/commands/governance.js +647 -0
- package/lib/commands/gripe.js +355 -0
- package/lib/commands/hat.js +374 -0
- package/lib/commands/helpers.js +96 -0
- package/lib/commands/hyperion.js +58 -0
- package/lib/commands/inbox.js +173 -0
- package/lib/commands/instagram.js +264 -0
- package/lib/commands/integration.js +100 -0
- package/lib/commands/inventory.js +115 -0
- package/lib/commands/invites.js +45 -0
- package/lib/commands/licence.js +139 -0
- package/lib/commands/linkedin.js +178 -0
- package/lib/commands/llm-policy.js +117 -0
- package/lib/commands/llm.js +45 -0
- package/lib/commands/lucid.js +597 -0
- package/lib/commands/mail.js +531 -0
- package/lib/commands/memory.js +361 -0
- package/lib/commands/mind.js +71 -0
- package/lib/commands/mon.js +581 -0
- package/lib/commands/monitoring.js +86 -0
- package/lib/commands/motion.js +22 -0
- package/lib/commands/notes.js +1246 -0
- package/lib/commands/notesBatch.js +160 -0
- package/lib/commands/notesBridges.js +56 -0
- package/lib/commands/notesLinks.js +60 -0
- package/lib/commands/notification.js +57 -0
- package/lib/commands/nucleus.js +123 -0
- package/lib/commands/objective.js +240 -0
- package/lib/commands/offload.js +89 -0
- package/lib/commands/onboard.js +329 -0
- package/lib/commands/open.js +56 -0
- package/lib/commands/org.js +276 -0
- package/lib/commands/peek.js +28 -0
- package/lib/commands/plan.js +944 -0
- package/lib/commands/plugin-doctor.js +1091 -0
- package/lib/commands/plugin.js +649 -0
- package/lib/commands/policy.js +78 -0
- package/lib/commands/powers.js +296 -0
- package/lib/commands/presence.js +89 -0
- package/lib/commands/prompt.js +350 -0
- package/lib/commands/provider.js +159 -0
- package/lib/commands/qlarity.js +706 -0
- package/lib/commands/quoxbox.js +270 -0
- package/lib/commands/quoxchat.js +275 -0
- package/lib/commands/quoxseo.js +378 -0
- package/lib/commands/reach.js +159 -0
- package/lib/commands/role.js +286 -0
- package/lib/commands/router.js +77 -0
- package/lib/commands/rows.js +358 -0
- package/lib/commands/run.js +187 -0
- package/lib/commands/safety.js +107 -0
- package/lib/commands/scim.js +88 -0
- package/lib/commands/scope.js +58 -0
- package/lib/commands/security.js +157 -0
- package/lib/commands/server.js +292 -0
- package/lib/commands/service.js +89 -0
- package/lib/commands/settings.js +99 -0
- package/lib/commands/setup.js +569 -0
- package/lib/commands/skillManifest.js +113 -0
- package/lib/commands/skillRightsFormat.js +38 -0
- package/lib/commands/skills.js +687 -0
- package/lib/commands/slot.js +1451 -0
- package/lib/commands/sso.js +229 -0
- package/lib/commands/status.js +137 -0
- package/lib/commands/tag.js +91 -0
- package/lib/commands/team.js +142 -0
- package/lib/commands/tg.js +240 -0
- package/lib/commands/tiktok.js +218 -0
- package/lib/commands/tips.js +23 -0
- package/lib/commands/tool.js +304 -0
- package/lib/commands/trace.js +530 -0
- package/lib/commands/trigger.js +223 -0
- package/lib/commands/update.js +46 -0
- package/lib/commands/vault.js +1447 -0
- package/lib/commands/vm.js +237 -0
- package/lib/commands/voice.js +213 -0
- package/lib/commands/volt.js +570 -0
- package/lib/commands/walkthrough.js +32 -0
- package/lib/commands/watch.js +167 -0
- package/lib/commands/webhook.js +137 -0
- package/lib/commands/welcome.js +14 -0
- package/lib/commands/workflow.js +660 -0
- package/lib/commands/x.js +448 -0
- package/lib/core/actingAs.js +136 -0
- package/lib/core/activeSummary.js +186 -0
- package/lib/core/aee.js +53 -0
- package/lib/core/agentAlias.js +163 -0
- package/lib/core/approvalContext.js +61 -0
- package/lib/core/aside.js +54 -0
- package/lib/core/askDecision.js +60 -0
- package/lib/core/auth.js +501 -0
- package/lib/core/banner.js +143 -0
- package/lib/core/brainPrime.js +90 -0
- package/lib/core/callerSource.js +129 -0
- package/lib/core/claudeLs.js +94 -0
- package/lib/core/cliBuild.js +62 -0
- package/lib/core/client.js +693 -0
- package/lib/core/commandRegistrars.js +121 -0
- package/lib/core/completions.js +163 -0
- package/lib/core/config.js +669 -0
- package/lib/core/confirm.js +37 -0
- package/lib/core/customHats.js +168 -0
- package/lib/core/deeplinks.js +76 -0
- package/lib/core/doneEngineFetch.js +227 -0
- package/lib/core/errors.js +441 -0
- package/lib/core/global-flags.js +190 -0
- package/lib/core/governedSession.js +402 -0
- package/lib/core/hyperion.js +102 -0
- package/lib/core/inboxResolution.js +65 -0
- package/lib/core/lazyCommands.js +122 -0
- package/lib/core/liveSessionsReport.js +112 -0
- package/lib/core/offload.js +504 -0
- package/lib/core/org-override.js +52 -0
- package/lib/core/orgResolve.js +70 -0
- package/lib/core/output.js +239 -0
- package/lib/core/peek.js +126 -0
- package/lib/core/prodEnv.js +7 -0
- package/lib/core/profileUsers.js +51 -0
- package/lib/core/promptCancel.js +18 -0
- package/lib/core/quoxglyph.js +158 -0
- package/lib/core/repoPath.js +11 -0
- package/lib/core/residentAlarm.js +282 -0
- package/lib/core/residentAuth.js +243 -0
- package/lib/core/residentPump.js +229 -0
- package/lib/core/residentSeed.js +106 -0
- package/lib/core/rootOptions.js +39 -0
- package/lib/core/router.js +168 -0
- package/lib/core/selfUpdate.js +188 -0
- package/lib/core/sessionHeartbeat.js +192 -0
- package/lib/core/sessionsOverview.js +224 -0
- package/lib/core/slotKeeper.js +527 -0
- package/lib/core/slotWake.js +120 -0
- package/lib/core/supervisorEngine.js +933 -0
- package/lib/core/systemdInstaller.js +256 -0
- package/lib/core/version.js +87 -0
- package/lib/core/vm.js +361 -0
- package/lib/core/vmTui.js +86 -0
- package/lib/core/walkthrough.js +35 -0
- package/lib/hyperion/deck.js +29 -0
- package/lib/lucid/LucidCard.jsx +68 -0
- package/lib/lucid/LucidIntro.jsx +92 -0
- package/lib/lucid/blind.js +71 -0
- package/lib/lucid/cards.js +86 -0
- package/lib/lucid/deck.js +32 -0
- package/lib/lucid/fmt.js +139 -0
- package/lib/lucid/markdown.js +81 -0
- package/lib/lucid/motion.js +56 -0
- package/lib/lucid/sitrep.js +173 -0
- package/lib/plugins/loader.js +93 -0
- package/lib/quoxon/activity.js +116 -0
- package/lib/quoxon/aocl.js +117 -0
- package/lib/quoxon/api.js +952 -0
- package/lib/quoxon/app.js +4032 -0
- package/lib/quoxon/approvalFacts.js +94 -0
- package/lib/quoxon/approvalsView.js +325 -0
- package/lib/quoxon/asideView.js +40 -0
- package/lib/quoxon/ask.js +121 -0
- package/lib/quoxon/away.js +52 -0
- package/lib/quoxon/bang.js +224 -0
- package/lib/quoxon/bulkRisk.js +17 -0
- package/lib/quoxon/capabilities.js +82 -0
- package/lib/quoxon/cells.js +53 -0
- package/lib/quoxon/commandShort.js +112 -0
- package/lib/quoxon/commands.js +479 -0
- package/lib/quoxon/connect.js +654 -0
- package/lib/quoxon/connectCard.js +45 -0
- package/lib/quoxon/consent.js +86 -0
- package/lib/quoxon/context.js +68 -0
- package/lib/quoxon/copy.js +34 -0
- package/lib/quoxon/countdownLine.js +11 -0
- package/lib/quoxon/demo.autoplay.js +29 -0
- package/lib/quoxon/demo.fixtures.js +24 -0
- package/lib/quoxon/demo.js +76 -0
- package/lib/quoxon/devObserver.js +20 -0
- package/lib/quoxon/dividerAocl.js +191 -0
- package/lib/quoxon/dividerRule.js +57 -0
- package/lib/quoxon/dragonModes.js +150 -0
- package/lib/quoxon/dragonView.js +41 -0
- package/lib/quoxon/earlyInput.js +40 -0
- package/lib/quoxon/echo.js +15 -0
- package/lib/quoxon/egg.js +36 -0
- package/lib/quoxon/events.js +677 -0
- package/lib/quoxon/feedback.js +50 -0
- package/lib/quoxon/gallery.js +237 -0
- package/lib/quoxon/handoffOffer.js +78 -0
- package/lib/quoxon/hatSwitch.js +37 -0
- package/lib/quoxon/headless.js +161 -0
- package/lib/quoxon/hostUrls.js +41 -0
- package/lib/quoxon/indicator.js +197 -0
- package/lib/quoxon/intent.js +52 -0
- package/lib/quoxon/keys.js +164 -0
- package/lib/quoxon/launcher.js +103 -0
- package/lib/quoxon/main.js +176 -0
- package/lib/quoxon/markdown.js +341 -0
- package/lib/quoxon/markdownSettle.js +19 -0
- package/lib/quoxon/matrixDomains.json +46 -0
- package/lib/quoxon/mentions.js +90 -0
- package/lib/quoxon/mind.js +103 -0
- package/lib/quoxon/mode.js +46 -0
- package/lib/quoxon/model.js +311 -0
- package/lib/quoxon/modelPrefs.js +345 -0
- package/lib/quoxon/motion/clock.js +70 -0
- package/lib/quoxon/motion/color.js +35 -0
- package/lib/quoxon/motion/frame.js +14 -0
- package/lib/quoxon/motion/level.js +91 -0
- package/lib/quoxon/motion/reveal.js +54 -0
- package/lib/quoxon/motion/ring.js +79 -0
- package/lib/quoxon/mouse.js +211 -0
- package/lib/quoxon/noai.js +57 -0
- package/lib/quoxon/objects/agent.js +61 -0
- package/lib/quoxon/objects/away.js +72 -0
- package/lib/quoxon/objects/crystal.js +100 -0
- package/lib/quoxon/objects/fetch.js +182 -0
- package/lib/quoxon/objects/fleet.js +149 -0
- package/lib/quoxon/objects/health.js +186 -0
- package/lib/quoxon/objects/index.js +106 -0
- package/lib/quoxon/objects/ladder.js +71 -0
- package/lib/quoxon/objects/memory.js +44 -0
- package/lib/quoxon/objects/mirror.js +137 -0
- package/lib/quoxon/objects/model.js +118 -0
- package/lib/quoxon/objects/peek.js +17 -0
- package/lib/quoxon/objects/proof.js +110 -0
- package/lib/quoxon/objects/prove.js +130 -0
- package/lib/quoxon/objects/replay.js +125 -0
- package/lib/quoxon/objects/sitrep.js +58 -0
- package/lib/quoxon/objects/skill.js +105 -0
- package/lib/quoxon/objects/view.js +87 -0
- package/lib/quoxon/objects/workflow.js +119 -0
- package/lib/quoxon/observers.js +42 -0
- package/lib/quoxon/onboarding/copy.js +263 -0
- package/lib/quoxon/onboarding/credential.js +108 -0
- package/lib/quoxon/onboarding/detect.js +22 -0
- package/lib/quoxon/onboarding/dockerOffer.js +45 -0
- package/lib/quoxon/onboarding/failureCause.js +33 -0
- package/lib/quoxon/onboarding/finish.js +134 -0
- package/lib/quoxon/onboarding/index.js +94 -0
- package/lib/quoxon/onboarding/installRunner.js +284 -0
- package/lib/quoxon/onboarding/intent.js +53 -0
- package/lib/quoxon/onboarding/journey.js +73 -0
- package/lib/quoxon/onboarding/preflight.js +156 -0
- package/lib/quoxon/onboarding/probe.js +65 -0
- package/lib/quoxon/onboarding/runner.js +58 -0
- package/lib/quoxon/onboarding/view.js +682 -0
- package/lib/quoxon/orgLabel.js +10 -0
- package/lib/quoxon/orgSwitch.js +33 -0
- package/lib/quoxon/ownBroadcast.js +22 -0
- package/lib/quoxon/palette.js +43 -0
- package/lib/quoxon/print.js +98 -0
- package/lib/quoxon/profileSwitch.js +53 -0
- package/lib/quoxon/promptHistory.js +41 -0
- package/lib/quoxon/providers.js +48 -0
- package/lib/quoxon/qart.js +348 -0
- package/lib/quoxon/qdragon.js +410 -0
- package/lib/quoxon/qdragonFly.js +61 -0
- package/lib/quoxon/qdragonScenes.js +282 -0
- package/lib/quoxon/qeyes.js +213 -0
- package/lib/quoxon/qglyph.js +143 -0
- package/lib/quoxon/qhello.js +58 -0
- package/lib/quoxon/qmotion.js +132 -0
- package/lib/quoxon/render.js +1167 -0
- package/lib/quoxon/resend.js +72 -0
- package/lib/quoxon/roster.js +307 -0
- package/lib/quoxon/route.js +39 -0
- package/lib/quoxon/rows.js +711 -0
- package/lib/quoxon/rowsStore.js +80 -0
- package/lib/quoxon/rules.js +130 -0
- package/lib/quoxon/screenContext.js +150 -0
- package/lib/quoxon/screenFrames.js +254 -0
- package/lib/quoxon/session.js +238 -0
- package/lib/quoxon/sessionDead.js +17 -0
- package/lib/quoxon/signin.js +125 -0
- package/lib/quoxon/skills.js +30 -0
- package/lib/quoxon/slash/agents.js +112 -0
- package/lib/quoxon/slash/argSources.js +65 -0
- package/lib/quoxon/slash/args.js +65 -0
- package/lib/quoxon/slash/automate.js +89 -0
- package/lib/quoxon/slash/broadcast.js +140 -0
- package/lib/quoxon/slash/broadcastRun.js +108 -0
- package/lib/quoxon/slash/card.js +30 -0
- package/lib/quoxon/slash/challenge.js +114 -0
- package/lib/quoxon/slash/cliCommand.js +53 -0
- package/lib/quoxon/slash/cliLeaves.json +948 -0
- package/lib/quoxon/slash/compact.js +90 -0
- package/lib/quoxon/slash/confirm.js +30 -0
- package/lib/quoxon/slash/crystallize.js +69 -0
- package/lib/quoxon/slash/dashboard.js +136 -0
- package/lib/quoxon/slash/draft.js +54 -0
- package/lib/quoxon/slash/hatWalk.js +206 -0
- package/lib/quoxon/slash/inverses.js +77 -0
- package/lib/quoxon/slash/killer.js +24 -0
- package/lib/quoxon/slash/killerApi.js +16 -0
- package/lib/quoxon/slash/ledger.js +88 -0
- package/lib/quoxon/slash/listView.js +30 -0
- package/lib/quoxon/slash/modelCmd.js +261 -0
- package/lib/quoxon/slash/palette.js +118 -0
- package/lib/quoxon/slash/paletteController.js +169 -0
- package/lib/quoxon/slash/panels.js +116 -0
- package/lib/quoxon/slash/picker.js +56 -0
- package/lib/quoxon/slash/restart.js +202 -0
- package/lib/quoxon/slash/resume.js +59 -0
- package/lib/quoxon/slash/rowCmd.js +157 -0
- package/lib/quoxon/slash/rowWalk.js +175 -0
- package/lib/quoxon/slash/settings.js +60 -0
- package/lib/quoxon/slash/spawn.js +177 -0
- package/lib/quoxon/slash/stepUp.js +274 -0
- package/lib/quoxon/slash/stepUpCard.js +60 -0
- package/lib/quoxon/slash/stepUpCmds.js +78 -0
- package/lib/quoxon/slash/undoApi.js +38 -0
- package/lib/quoxon/slash/undoCmd.js +49 -0
- package/lib/quoxon/slash/undoWish.js +15 -0
- package/lib/quoxon/slash/watch.js +164 -0
- package/lib/quoxon/slash/wish.js +23 -0
- package/lib/quoxon/slash/wishCmd.js +32 -0
- package/lib/quoxon/slotChat.js +300 -0
- package/lib/quoxon/slotFormat.js +195 -0
- package/lib/quoxon/slotRun.js +131 -0
- package/lib/quoxon/sound/index.js +186 -0
- package/lib/quoxon/sound/mapping.js +120 -0
- package/lib/quoxon/sound/output.js +52 -0
- package/lib/quoxon/sound/synth.js +122 -0
- package/lib/quoxon/stray.js +22 -0
- package/lib/quoxon/suggestCommand.js +59 -0
- package/lib/quoxon/term.js +141 -0
- package/lib/quoxon/textBuffer.js +416 -0
- package/lib/quoxon/theme.js +286 -0
- package/lib/quoxon/tips.js +433 -0
- package/lib/quoxon/toolError.js +41 -0
- package/lib/quoxon/transcript.js +188 -0
- package/lib/quoxon/web/qface.js +89 -0
- package/lib/quoxon/welcome.js +52 -0
- package/lib/quoxon/whatsnew.js +136 -0
- package/lib/quoxon/whatsnew.jsonl +89 -0
- package/lib/quoxon/windowOrg.js +113 -0
- package/lib/server/diag.js +129 -0
- package/lib/server/doctor.js +71 -0
- package/lib/server/install.js +149 -0
- package/lib/server/pluginEnable.js +161 -0
- package/lib/server/skew.js +56 -0
- package/lib/server/snapshot.js +131 -0
- package/lib/server/status.js +128 -0
- package/lib/server/sys.js +65 -0
- package/lib/server/uninstall.js +63 -0
- package/lib/server/update.js +234 -0
- package/lib/toolOutputShape.js +315 -0
- package/lib/tui/app.jsx +289 -0
- package/lib/tui/components/Breadcrumb.jsx +42 -0
- package/lib/tui/components/ChatInput.jsx +68 -0
- package/lib/tui/components/Confirm.jsx +32 -0
- package/lib/tui/components/DataTable.jsx +270 -0
- package/lib/tui/components/DetailPanel.jsx +241 -0
- package/lib/tui/components/HelpOverlay.jsx +67 -0
- package/lib/tui/components/Layout.jsx +129 -0
- package/lib/tui/components/Palette.jsx +141 -0
- package/lib/tui/components/SearchInput.jsx +125 -0
- package/lib/tui/components/Sparkline.jsx +55 -0
- package/lib/tui/components/StatusBar.jsx +133 -0
- package/lib/tui/components/Toast.jsx +52 -0
- package/lib/tui/hooks/useApi.js +44 -0
- package/lib/tui/hooks/useHealth.js +90 -0
- package/lib/tui/hooks/usePolledApi.js +86 -0
- package/lib/tui/hooks/useTerminalSize.js +46 -0
- package/lib/tui/index.js +56 -0
- package/lib/tui/intro/Intro.jsx +332 -0
- package/lib/tui/jsx-loader.js +32 -0
- package/lib/tui/keys.js +87 -0
- package/lib/tui/registry.js +49 -0
- package/lib/tui/theme.js +103 -0
- package/lib/tui/views/AdminView.jsx +398 -0
- package/lib/tui/views/AgentView.jsx +204 -0
- package/lib/tui/views/BackupView.jsx +225 -0
- package/lib/tui/views/BrainView.jsx +223 -0
- package/lib/tui/views/ChatView.jsx +468 -0
- package/lib/tui/views/FilesView.jsx +218 -0
- package/lib/tui/views/FleetView.jsx +234 -0
- package/lib/tui/views/GovernanceView.jsx +453 -0
- package/lib/tui/views/HealthView.jsx +216 -0
- package/lib/tui/views/HyperionDeck.jsx +79 -0
- package/lib/tui/views/InboxView.jsx +228 -0
- package/lib/tui/views/LucidDeck.jsx +719 -0
- package/lib/tui/views/MainMenu.jsx +123 -0
- package/lib/tui/views/MemoryView.jsx +377 -0
- package/lib/tui/views/ObjectivesView.jsx +230 -0
- package/lib/tui/views/OrgView.jsx +229 -0
- package/lib/tui/views/ToolView.jsx +221 -0
- package/lib/tui/views/VaultView.jsx +422 -0
- package/lib/tui/views/WorkflowView.jsx +329 -0
- package/package.json +75 -4
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared non-TTY-safe destructive-action confirmation.
|
|
3
|
+
*
|
|
4
|
+
* fleet #398/#399: several commands (`prompt delete`, `tag delete`, etc.)
|
|
5
|
+
* either lacked a --force/-y escape hatch entirely, or called inquirer
|
|
6
|
+
* unconditionally when neither was set. On a non-interactive shell (no
|
|
7
|
+
* stdin TTY — cron, CI, a piped script) inquirer throws
|
|
8
|
+
* `Error: User force closed the prompt with 13 null` AND leaks
|
|
9
|
+
* `Warning: Detected unsettled top-level await at cli/bin/quox.js:221`,
|
|
10
|
+
* verified live on .131. Checking `process.stdin.isTTY` BEFORE importing
|
|
11
|
+
* inquirer avoids both — fail fast with a clear message instead.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {import('commander').Command} program - root commander program (for global --yes)
|
|
16
|
+
* @param {{ force?: boolean }} options - local command options (for --force)
|
|
17
|
+
* @param {string} message - confirmation prompt text
|
|
18
|
+
* @returns {Promise<boolean>}
|
|
19
|
+
*/
|
|
20
|
+
export async function confirmDestructive(program, options, message) {
|
|
21
|
+
if (options.force === true || program.opts().yes === true) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!process.stdin.isTTY) {
|
|
26
|
+
throw new Error(`${message} — this needs confirmation; re-run with --force (or -y) in a non-interactive shell`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { default: inquirer } = await import('inquirer');
|
|
30
|
+
const ans = await inquirer.prompt([{
|
|
31
|
+
type: 'confirm',
|
|
32
|
+
name: 'confirm',
|
|
33
|
+
message,
|
|
34
|
+
default: false,
|
|
35
|
+
}]);
|
|
36
|
+
return ans.confirm;
|
|
37
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CUSTOM-HATS (owner 2026-10-17): pure helpers that build and edit a custom hat definition.
|
|
3
|
+
* Shared by `quox hat create|edit` (cli/lib/commands/hat.js) and the quox window's /hat new|edit
|
|
4
|
+
* (cli/lib/quoxon). No I/O, no chalk: safe to import anywhere.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** The two tools an orchestrator needs to reach the agents a hat lists. */
|
|
8
|
+
export const DELEGATION_TOOLS = ['delegate_to_agent', 'agent_directory'];
|
|
9
|
+
export const HAT_MODES = ['permissive', 'restrictive'];
|
|
10
|
+
export const HAT_OFFERS = ['members', 'authorized'];
|
|
11
|
+
const HARD_CAP = 128;
|
|
12
|
+
|
|
13
|
+
/** "a, b,,c" -> ['a','b','c'] (trimmed, lowercased, deduped, order kept). */
|
|
14
|
+
export function parseList(v) {
|
|
15
|
+
if (v === undefined || v === null || v === true) return [];
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const raw of String(v).split(',')) {
|
|
18
|
+
const x = raw.trim().toLowerCase();
|
|
19
|
+
if (x && !out.includes(x)) out.push(x);
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Every tool name a hat names: explicit, bundles, on-demand reads. */
|
|
25
|
+
export function memberTools(def) {
|
|
26
|
+
const t = def?.tools || {};
|
|
27
|
+
const names = [
|
|
28
|
+
...(Array.isArray(t.explicit) ? t.explicit : []),
|
|
29
|
+
...(Array.isArray(t.bundles) ? t.bundles.flatMap(b => (Array.isArray(b?.tools) ? b.tools : [])) : []),
|
|
30
|
+
...(Array.isArray(t.on_demand_reads) ? t.on_demand_reads : []),
|
|
31
|
+
];
|
|
32
|
+
return [...new Set(names)];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function emptyDefinition() {
|
|
36
|
+
return {
|
|
37
|
+
schema_version: 1,
|
|
38
|
+
purpose: '',
|
|
39
|
+
agents: { orchestrator: 'quox', specialists: [] },
|
|
40
|
+
tools: { explicit: [], bundles: [], connector_families: [] },
|
|
41
|
+
budgets: { initial_tool_target: 12, max_manifest: 32 },
|
|
42
|
+
transitions: { allowed: 'any', confirm: 'none', privileged: false },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ensureDelegationTools(def) {
|
|
47
|
+
const specialists = def.agents?.specialists || [];
|
|
48
|
+
if (!specialists.length) return [];
|
|
49
|
+
const explicit = def.tools.explicit;
|
|
50
|
+
const added = DELEGATION_TOOLS.filter(t => !explicit.includes(t));
|
|
51
|
+
explicit.unshift(...added);
|
|
52
|
+
return added;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function fitBudgets(def) {
|
|
56
|
+
const b = def.budgets || (def.budgets = {});
|
|
57
|
+
const need = (def.tools.explicit || []).length;
|
|
58
|
+
if (!Number.isInteger(b.max_manifest) || b.max_manifest < need) b.max_manifest = Math.min(HARD_CAP, Math.max(32, need));
|
|
59
|
+
if (!Number.isInteger(b.initial_tool_target) || b.initial_tool_target < 1) b.initial_tool_target = Math.min(12, b.max_manifest);
|
|
60
|
+
if (b.initial_tool_target > b.max_manifest) b.initial_tool_target = b.max_manifest;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Restrictive = the hat AUTHORIZES only its members (+ the delegation pair); this binds every agent
|
|
65
|
+
* working under the hat, the hat's own agents included when delegated to. Permissive = the agent's
|
|
66
|
+
* own grant is the ceiling (the default). A deny list carried from --from is kept either way.
|
|
67
|
+
*/
|
|
68
|
+
export function applyMode(def, mode) {
|
|
69
|
+
if (mode === undefined || mode === null) return;
|
|
70
|
+
if (!HAT_MODES.includes(mode)) throw new Error(`--mode must be ${HAT_MODES.join(' or ')} (got '${mode}')`);
|
|
71
|
+
const deny = Array.isArray(def.authz?.deny) ? def.authz.deny : null;
|
|
72
|
+
if (mode === 'permissive') {
|
|
73
|
+
if (deny) def.authz = { mode: 'permissive', deny };
|
|
74
|
+
else delete def.authz;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
def.authz = { mode: 'restrictive', allow: [...new Set([...memberTools(def), ...DELEGATION_TOOLS])], ...(deny ? { deny } : {}) };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The definition a new custom hat starts from: a copy of --from (or an empty hat), with
|
|
82
|
+
* --agents / --tools REPLACING the lists, the delegation tools added when the hat lists agents,
|
|
83
|
+
* the offer set (default 'members': a custom hat offers its orchestrator only what it lists),
|
|
84
|
+
* and budgets widened to fit. Pure: never mutates base.
|
|
85
|
+
*/
|
|
86
|
+
export function buildCustomDefinition({ base = null, name = '', purpose, agents, tools, mode, offer } = {}) {
|
|
87
|
+
const def = base ? JSON.parse(JSON.stringify(base)) : emptyDefinition();
|
|
88
|
+
def.schema_version = 1;
|
|
89
|
+
def.agents = { orchestrator: 'quox', ...(def.agents || {}) };
|
|
90
|
+
if (!Array.isArray(def.agents.specialists)) def.agents.specialists = [];
|
|
91
|
+
def.tools = { ...(def.tools || {}) };
|
|
92
|
+
def.tools.explicit = Array.isArray(def.tools.explicit) ? [...def.tools.explicit] : [];
|
|
93
|
+
if (agents !== undefined) def.agents.specialists = [...agents];
|
|
94
|
+
if (tools !== undefined) def.tools.explicit = [...tools];
|
|
95
|
+
const autoAdded = ensureDelegationTools(def);
|
|
96
|
+
const o = offer === undefined ? 'members' : offer;
|
|
97
|
+
if (!HAT_OFFERS.includes(o)) throw new Error(`--offer must be ${HAT_OFFERS.join(' or ')} (got '${o}')`);
|
|
98
|
+
def.tools.offer = o;
|
|
99
|
+
if (purpose !== undefined && String(purpose).trim()) def.purpose = String(purpose).trim();
|
|
100
|
+
if (!def.purpose || base) {
|
|
101
|
+
if (purpose === undefined || !String(purpose).trim()) def.purpose = `${name || 'Custom'} work${def.agents.specialists.length ? ` with ${def.agents.specialists.join(', ')}` : ''}`;
|
|
102
|
+
}
|
|
103
|
+
fitBudgets(def);
|
|
104
|
+
applyMode(def, mode);
|
|
105
|
+
return { definition: def, autoAdded };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Apply `quox hat edit` options to a copy of a definition. Returns the new definition and a
|
|
110
|
+
* human list of changes (empty = nothing to do). --agents/--tools replace; --add-* / --remove-*
|
|
111
|
+
* adjust. A restrictive allow list follows tool adds/removes so the hat keeps authorizing exactly
|
|
112
|
+
* its members.
|
|
113
|
+
*/
|
|
114
|
+
export function applyHatEdits(current, opts = {}) {
|
|
115
|
+
const def = JSON.parse(JSON.stringify(current || {}));
|
|
116
|
+
def.agents = { orchestrator: 'quox', ...(def.agents || {}) };
|
|
117
|
+
def.tools = { ...(def.tools || {}) };
|
|
118
|
+
let specialists = Array.isArray(def.agents.specialists) ? [...def.agents.specialists] : [];
|
|
119
|
+
let explicit = Array.isArray(def.tools.explicit) ? [...def.tools.explicit] : [];
|
|
120
|
+
const changes = [];
|
|
121
|
+
const beforeAgents = [...specialists];
|
|
122
|
+
const beforeTools = new Set(memberTools(def));
|
|
123
|
+
|
|
124
|
+
if (opts.agents !== undefined) specialists = [...opts.agents];
|
|
125
|
+
for (const a of opts.addAgents || []) if (!specialists.includes(a)) specialists.push(a);
|
|
126
|
+
if ((opts.removeAgents || []).length) specialists = specialists.filter(a => !opts.removeAgents.includes(a));
|
|
127
|
+
if (opts.tools !== undefined) explicit = [...opts.tools];
|
|
128
|
+
for (const t of opts.addTools || []) if (!explicit.includes(t)) explicit.push(t);
|
|
129
|
+
if ((opts.removeTools || []).length) {
|
|
130
|
+
explicit = explicit.filter(t => !opts.removeTools.includes(t));
|
|
131
|
+
if (Array.isArray(def.tools.bundles)) def.tools.bundles = def.tools.bundles.map(b => ({ ...b, tools: (b.tools || []).filter(t => !opts.removeTools.includes(t)) }));
|
|
132
|
+
}
|
|
133
|
+
def.agents.specialists = specialists;
|
|
134
|
+
def.tools.explicit = explicit;
|
|
135
|
+
const autoAdded = (opts.removeTools || []).some(t => DELEGATION_TOOLS.includes(t)) ? [] : ensureDelegationTools(def);
|
|
136
|
+
|
|
137
|
+
const agentsAdded = specialists.filter(a => !beforeAgents.includes(a));
|
|
138
|
+
const agentsRemoved = beforeAgents.filter(a => !specialists.includes(a));
|
|
139
|
+
if (agentsAdded.length) changes.push(`agents +${agentsAdded.join(', +')}`);
|
|
140
|
+
if (agentsRemoved.length) changes.push(`agents -${agentsRemoved.join(', -')}`);
|
|
141
|
+
const afterTools = new Set(memberTools(def));
|
|
142
|
+
const toolsAdded = [...afterTools].filter(t => !beforeTools.has(t));
|
|
143
|
+
const toolsRemoved = [...beforeTools].filter(t => !afterTools.has(t));
|
|
144
|
+
if (toolsAdded.length) changes.push(`tools +${toolsAdded.join(', +')}`);
|
|
145
|
+
if (toolsRemoved.length) changes.push(`tools -${toolsRemoved.join(', -')}`);
|
|
146
|
+
|
|
147
|
+
if (opts.purpose !== undefined && String(opts.purpose).trim() && String(opts.purpose).trim() !== def.purpose) {
|
|
148
|
+
def.purpose = String(opts.purpose).trim();
|
|
149
|
+
changes.push('purpose');
|
|
150
|
+
}
|
|
151
|
+
if (opts.offer !== undefined) {
|
|
152
|
+
if (!HAT_OFFERS.includes(opts.offer)) throw new Error(`--offer must be ${HAT_OFFERS.join(' or ')} (got '${opts.offer}')`);
|
|
153
|
+
if ((def.tools.offer || 'authorized') !== opts.offer) { def.tools.offer = opts.offer; changes.push(`offer ${opts.offer}`); }
|
|
154
|
+
}
|
|
155
|
+
const prevMode = def.authz?.mode === 'restrictive' ? 'restrictive' : 'permissive';
|
|
156
|
+
if (opts.mode !== undefined) {
|
|
157
|
+
applyMode(def, opts.mode);
|
|
158
|
+
if (opts.mode !== prevMode) changes.push(`mode ${opts.mode}`);
|
|
159
|
+
} else if (prevMode === 'restrictive' && Array.isArray(def.authz?.allow) && (toolsAdded.length || toolsRemoved.length)) {
|
|
160
|
+
const allow = new Set(def.authz.allow);
|
|
161
|
+
for (const t of toolsAdded) allow.add(t);
|
|
162
|
+
for (const t of toolsRemoved) allow.delete(t);
|
|
163
|
+
def.authz = { ...def.authz, allow: [...allow] };
|
|
164
|
+
}
|
|
165
|
+
fitBudgets(def);
|
|
166
|
+
return { definition: def, changes, autoAdded };
|
|
167
|
+
}
|
|
168
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep Links — quox:// URI scheme
|
|
3
|
+
*
|
|
4
|
+
* Every Quox entity has a canonical URI:
|
|
5
|
+
* quox://fleet/docker01
|
|
6
|
+
* quox://workflow/WF_abc123/run/RUN_xyz
|
|
7
|
+
* quox://agent/sentinel
|
|
8
|
+
* quox://aee/ENV_789
|
|
9
|
+
* quox://memory/MEM_123
|
|
10
|
+
*
|
|
11
|
+
* URIs resolve to both a dashboard URL (browser) and a CLI command.
|
|
12
|
+
*
|
|
13
|
+
* CLI-3f (Stream CLI Phase 3)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const ROUTE_MAP = {
|
|
17
|
+
fleet: { dashboard: '/noc?host=', cli: 'bastion host show' },
|
|
18
|
+
agent: { dashboard: '/agents?id=', cli: 'quox agent get' },
|
|
19
|
+
workflow: { dashboard: '/workflows?id=', cli: 'quox workflow get' },
|
|
20
|
+
aee: { dashboard: '/activity?id=', cli: 'quox aee get' },
|
|
21
|
+
memory: { dashboard: '/memory?id=', cli: 'quox memory get' },
|
|
22
|
+
tool: { dashboard: '/tools?id=', cli: 'quox tool get' },
|
|
23
|
+
vault: { dashboard: '/settings/vault?id=', cli: 'quox vault get' },
|
|
24
|
+
session: { dashboard: '/fleet?session=', cli: 'quoxagent agent info' },
|
|
25
|
+
conversation: { dashboard: '/assistants?conv=', cli: 'quox conversation get' },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build a quox:// URI for an entity.
|
|
30
|
+
* @param {string} type - Entity type (fleet, workflow, agent, aee, etc.)
|
|
31
|
+
* @param {string} id - Entity ID
|
|
32
|
+
* @param {string} [subpath] - Optional sub-path (e.g., "run/RUN_xyz")
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
export function buildQuoxUri(type, id, subpath) {
|
|
36
|
+
let uri = `quox://${type}/${id}`;
|
|
37
|
+
if (subpath) uri += `/${subpath}`;
|
|
38
|
+
return uri;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Resolve a quox:// URI to a dashboard URL and CLI command.
|
|
43
|
+
* @param {string} uri - The quox:// URI
|
|
44
|
+
* @param {string} baseUrl - Dashboard base URL (e.g., http://localhost:3000)
|
|
45
|
+
* @returns {{_ref: string, type: string, id: string, dashboardUrl: string, cliCommand: string}|null}
|
|
46
|
+
*/
|
|
47
|
+
export function resolveQuoxUri(uri, baseUrl) {
|
|
48
|
+
if (!uri || !uri.startsWith('quox://')) return null;
|
|
49
|
+
|
|
50
|
+
const path = uri.slice('quox://'.length);
|
|
51
|
+
const parts = path.split('/');
|
|
52
|
+
const type = parts[0];
|
|
53
|
+
const id = parts[1];
|
|
54
|
+
|
|
55
|
+
if (!type || !id) return null;
|
|
56
|
+
|
|
57
|
+
const route = ROUTE_MAP[type];
|
|
58
|
+
if (!route) {
|
|
59
|
+
// Unknown type — best effort
|
|
60
|
+
return {
|
|
61
|
+
_ref: uri,
|
|
62
|
+
type,
|
|
63
|
+
id,
|
|
64
|
+
dashboardUrl: `${baseUrl}/${type}?id=${id}`,
|
|
65
|
+
cliCommand: `quox ${type} get ${id}`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
_ref: uri,
|
|
71
|
+
type,
|
|
72
|
+
id,
|
|
73
|
+
dashboardUrl: `${baseUrl}${route.dashboard}${id}`,
|
|
74
|
+
cliCommand: `${route.cli} ${id}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `quox done fetch` engine (quox#456 follow-on).
|
|
3
|
+
*
|
|
4
|
+
* quox#456 pointed `quox done` at /opt/quoxgauge, which is delivered ONLY by
|
|
5
|
+
* `quox dev enable` (see cli/lib/commands/dev.js and
|
|
6
|
+
* services/collector/lib/devServerRefresh.js buildGaugeRefreshCommand).
|
|
7
|
+
* Enable is correctly human-gated (requireHumanAdmin), so a RESIDENT agent
|
|
8
|
+
* with a missing or stale engine had no self-service path and was stuck
|
|
9
|
+
* waiting on a human forever. The bundle download endpoint is already
|
|
10
|
+
* public (see services/collector/utils/collectorAuthGate.js PUBLIC_EXACT:
|
|
11
|
+
* /download/quoxgauge-bundle.tar.gz), so a resident-driven fetch needs no
|
|
12
|
+
* new permission grant, only this verb.
|
|
13
|
+
*
|
|
14
|
+
* Everything here is pure or takes its side effects (HTTP client, exec,
|
|
15
|
+
* filesystem reads/writes) as injected dependencies, so
|
|
16
|
+
* cli/lib/commands/done.js is a thin wrapper and every path below is driven
|
|
17
|
+
* from tests with no real network call, no real `tar`/`sudo`, and no writes
|
|
18
|
+
* outside a scratch fsOps fake.
|
|
19
|
+
*
|
|
20
|
+
* Extraction shape mirrors the collector's own refresh command exactly:
|
|
21
|
+
* flat `tar -xzf <bundle> -C <dest>`, never into a parent directory.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import os from 'node:os';
|
|
26
|
+
|
|
27
|
+
export const OPT_GAUGE_DIR = '/opt/quoxgauge';
|
|
28
|
+
|
|
29
|
+
export function userLocalGaugeDir(home = os.homedir()) {
|
|
30
|
+
return path.join(home, '.local', 'share', 'quox', 'gauge');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function legacyGaugeDir(home = os.homedir()) {
|
|
34
|
+
return path.join(home, 'quox-gauge');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Decide where a fetched engine should land, and whether getting there
|
|
39
|
+
* needs privilege escalation.
|
|
40
|
+
*
|
|
41
|
+
* Order:
|
|
42
|
+
* 1. --dest (explicit, always wins, never needs sudo)
|
|
43
|
+
* 2. $QUOX_DONEENGINE_HOME (an override for an already server-controlled
|
|
44
|
+
* path, same precedent as QUOX_DONEENGINE_PATH — not a new feature flag)
|
|
45
|
+
* 3. /opt/quoxgauge, if directly writable, or reachable via passwordless
|
|
46
|
+
* sudo (`sudo -n true`) — matching where `quox dev enable` puts it, so
|
|
47
|
+
* a resident does not silently diverge from the host-wide install
|
|
48
|
+
* 4. ~/.local/share/quox/gauge — always-writable fallback
|
|
49
|
+
*
|
|
50
|
+
* `isWritable` and `execFn` are injected so this stays testable without a
|
|
51
|
+
* real filesystem probe or a real `sudo` spawn.
|
|
52
|
+
*/
|
|
53
|
+
export function resolveFetchDestination({ destOption, env = process.env, isWritable, execFn } = {}) {
|
|
54
|
+
if (destOption) {
|
|
55
|
+
return { dest: destOption, reason: '--dest', needsSudo: false };
|
|
56
|
+
}
|
|
57
|
+
if (env.QUOX_DONEENGINE_HOME) {
|
|
58
|
+
return { dest: env.QUOX_DONEENGINE_HOME, reason: 'QUOX_DONEENGINE_HOME override', needsSudo: false };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const directlyWritable = typeof isWritable === 'function' ? isWritable(OPT_GAUGE_DIR) : false;
|
|
62
|
+
if (directlyWritable) {
|
|
63
|
+
return { dest: OPT_GAUGE_DIR, reason: `${OPT_GAUGE_DIR} is writable`, needsSudo: false };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let sudoAvailable = false;
|
|
67
|
+
if (typeof execFn === 'function') {
|
|
68
|
+
try {
|
|
69
|
+
const r = execFn('sudo', ['-n', 'true']);
|
|
70
|
+
sudoAvailable = !!r && r.status === 0;
|
|
71
|
+
} catch {
|
|
72
|
+
sudoAvailable = false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (sudoAvailable) {
|
|
76
|
+
return {
|
|
77
|
+
dest: OPT_GAUGE_DIR,
|
|
78
|
+
reason: `${OPT_GAUGE_DIR} is not directly writable, using passwordless sudo (matches quox dev enable's install)`,
|
|
79
|
+
needsSudo: true,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
dest: userLocalGaugeDir(),
|
|
85
|
+
reason: `${OPT_GAUGE_DIR} is not writable and no passwordless sudo is available; falling back to a user-writable path`,
|
|
86
|
+
needsSudo: false,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Inspect an install directory. Never throws: an unreadable or missing
|
|
92
|
+
* directory just reports unhealthy, it is not itself an error.
|
|
93
|
+
*/
|
|
94
|
+
export function inspectInstall(dest, fsOps) {
|
|
95
|
+
const gaugePath = path.join(dest, 'gauge.mjs');
|
|
96
|
+
const packsPath = path.join(dest, 'packs');
|
|
97
|
+
const gaugeExists = fsOps.existsSync(gaugePath);
|
|
98
|
+
const packsDirExists = fsOps.existsSync(packsPath);
|
|
99
|
+
let packsCount = 0;
|
|
100
|
+
if (packsDirExists) {
|
|
101
|
+
try {
|
|
102
|
+
packsCount = fsOps.readdirSync(packsPath).filter((f) => f.endsWith('.json')).length;
|
|
103
|
+
} catch {
|
|
104
|
+
packsCount = 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
healthy: gaugeExists && packsDirExists && packsCount > 0,
|
|
109
|
+
gaugeExists,
|
|
110
|
+
packsDirExists,
|
|
111
|
+
packsCount,
|
|
112
|
+
gaugePath,
|
|
113
|
+
packsPath,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Fetch the bundle from the collector and extract it flat into `dest`.
|
|
119
|
+
*
|
|
120
|
+
* A healthy existing install is a no-op unless `force` is set. On failure
|
|
121
|
+
* after a previous install existed, the previous install is restored so a
|
|
122
|
+
* resident is never left worse off than it started (leave the previous
|
|
123
|
+
* install intact where possible).
|
|
124
|
+
*
|
|
125
|
+
* `client` is the CLI's existing HTTP client (already injectable in every
|
|
126
|
+
* command via ctx) — reused here rather than a bare fetch so auth headers,
|
|
127
|
+
* retries and service-url resolution stay identical to every other command.
|
|
128
|
+
* `execFn(cmd, args)` is the child-process seam (mirrors ctx.spawnFn in
|
|
129
|
+
* lib/commands/devSupervisor.js): all destination mutation (mkdir/mv/rm/tar)
|
|
130
|
+
* goes through it, prefixed with `sudo -n` when `needsSudo` is set, so the
|
|
131
|
+
* privileged and unprivileged paths share one code path.
|
|
132
|
+
*/
|
|
133
|
+
export async function fetchEngine({ dest, needsSudo, force, client, execFn, fsOps } = {}) {
|
|
134
|
+
if (!force) {
|
|
135
|
+
const existing = inspectInstall(dest, fsOps);
|
|
136
|
+
if (existing.healthy) {
|
|
137
|
+
return {
|
|
138
|
+
status: 'noop',
|
|
139
|
+
dest,
|
|
140
|
+
packsCount: existing.packsCount,
|
|
141
|
+
message: `DoneEngine already installed and healthy at ${dest} (${existing.packsCount} packs). `
|
|
142
|
+
+ 'Nothing to do; pass --force to re-fetch anyway.',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const res = await client.get('/download/quoxgauge-bundle.tar.gz', { service: 'collector', raw: true });
|
|
148
|
+
// #988: a public install's collector has no DoneEngine bundle (dev servers build it from the private quox-gauge
|
|
149
|
+
// repo), so a 404 is a fact about this install, not a transient failure worth retrying.
|
|
150
|
+
if (res && res.status === 404) {
|
|
151
|
+
const err = new Error('DoneEngine is not part of this Quox install: this collector does not carry the DoneEngine bundle '
|
|
152
|
+
+ '(it is delivered to Quox dev servers by `quox dev enable`). Nothing was changed.');
|
|
153
|
+
err.code = 'DONEENGINE_NOT_SHIPPED';
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
156
|
+
if (!res || !res.ok) {
|
|
157
|
+
throw new Error(`Bundle download failed (status ${res ? res.status : 'no response'}) from /download/quoxgauge-bundle.tar.gz`);
|
|
158
|
+
}
|
|
159
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
160
|
+
const tmpFile = fsOps.tmpFile();
|
|
161
|
+
fsOps.writeFileSync(tmpFile, buffer);
|
|
162
|
+
|
|
163
|
+
const run = (cmd, args) => (needsSudo ? execFn('sudo', ['-n', cmd, ...args]) : execFn(cmd, args));
|
|
164
|
+
const requireOk = (r, label) => {
|
|
165
|
+
if (!r || r.status !== 0) {
|
|
166
|
+
throw new Error(`${label} failed: ${(r && (r.stderr || r.stdout)) || 'unknown error'}`);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const hadPrevious = fsOps.existsSync(dest);
|
|
171
|
+
const backupDest = `${dest}.pre-fetch`;
|
|
172
|
+
// A writable dest inside a non-writable parent (e.g. control-owned
|
|
173
|
+
// /opt/quoxgauge under root:root /opt) cannot be renamed to a sibling.
|
|
174
|
+
// Back up its contents into a temp dir instead and mutate dest in place.
|
|
175
|
+
const inPlace = hadPrevious && !needsSudo
|
|
176
|
+
&& typeof fsOps.isWritable === 'function'
|
|
177
|
+
&& !fsOps.isWritable(path.dirname(dest));
|
|
178
|
+
let inPlaceBackup = null;
|
|
179
|
+
const emptyDest = () => run('find', [dest, '-mindepth', '1', '-delete']);
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
if (inPlace) {
|
|
183
|
+
inPlaceBackup = fsOps.mkdtemp();
|
|
184
|
+
requireOk(run('cp', ['-a', `${dest}/.`, `${inPlaceBackup}/`]), 'backing up previous install in place');
|
|
185
|
+
requireOk(emptyDest(), 'emptying previous install');
|
|
186
|
+
} else if (hadPrevious) {
|
|
187
|
+
if (fsOps.existsSync(backupDest)) requireOk(run('rm', ['-rf', backupDest]), 'clearing stale backup');
|
|
188
|
+
requireOk(run('mv', [dest, backupDest]), 'backing up previous install');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
requireOk(run('mkdir', ['-p', dest]), 'creating destination');
|
|
192
|
+
requireOk(run('tar', ['-xzf', tmpFile, '-C', dest]), 'extracting bundle');
|
|
193
|
+
|
|
194
|
+
const verified = inspectInstall(dest, fsOps);
|
|
195
|
+
if (!verified.healthy) {
|
|
196
|
+
throw new Error(
|
|
197
|
+
`Extracted bundle at ${dest} failed verification `
|
|
198
|
+
+ `(gauge.mjs present: ${verified.gaugeExists}, packs: ${verified.packsCount}). `
|
|
199
|
+
+ 'Left the previous install in place where one existed.',
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (inPlace) requireOk(run('rm', ['-rf', inPlaceBackup]), 'cleaning up backup');
|
|
204
|
+
else if (hadPrevious) requireOk(run('rm', ['-rf', backupDest]), 'cleaning up backup');
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
status: 'updated',
|
|
208
|
+
dest,
|
|
209
|
+
packsCount: verified.packsCount,
|
|
210
|
+
message: `DoneEngine installed at ${dest} (${verified.packsCount} packs).`,
|
|
211
|
+
};
|
|
212
|
+
} catch (err) {
|
|
213
|
+
if (inPlaceBackup) {
|
|
214
|
+
emptyDest();
|
|
215
|
+
run('cp', ['-a', `${inPlaceBackup}/.`, `${dest}/`]);
|
|
216
|
+
} else if (hadPrevious && fsOps.existsSync(backupDest)) {
|
|
217
|
+
run('rm', ['-rf', dest]);
|
|
218
|
+
run('mv', [backupDest, dest]);
|
|
219
|
+
}
|
|
220
|
+
throw err;
|
|
221
|
+
} finally {
|
|
222
|
+
if (inPlaceBackup) {
|
|
223
|
+
try { run('rm', ['-rf', inPlaceBackup]); } catch { /* best effort cleanup */ }
|
|
224
|
+
}
|
|
225
|
+
try { fsOps.rmSync(tmpFile, { force: true }); } catch { /* best effort cleanup */ }
|
|
226
|
+
}
|
|
227
|
+
}
|