quoxcli 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +2 -0
- package/bin/quox.js +99 -126
- package/lib/api/endpoints/actions.js +15 -0
- package/lib/api/endpoints/admin.js +46 -0
- package/lib/api/endpoints/agent-schedules.js +26 -0
- package/lib/api/endpoints/agents.js +7 -0
- package/lib/api/endpoints/assistants.js +12 -0
- package/lib/api/endpoints/away.js +15 -0
- package/lib/api/endpoints/blog.js +16 -0
- package/lib/api/endpoints/chat.js +14 -0
- package/lib/api/endpoints/contactsq.js +6 -0
- package/lib/api/endpoints/conversations.js +14 -0
- package/lib/api/endpoints/devServerRegistry.js +25 -0
- package/lib/api/endpoints/engagement.js +26 -0
- package/lib/api/endpoints/evidence.js +20 -0
- package/lib/api/endpoints/executions.js +13 -0
- package/lib/api/endpoints/fetchAllowlist.js +27 -0
- package/lib/api/endpoints/files.js +14 -0
- package/lib/api/endpoints/hat.js +44 -0
- package/lib/api/endpoints/inbox.js +20 -0
- package/lib/api/endpoints/index.js +26 -0
- package/lib/api/endpoints/loops.js +26 -0
- package/lib/api/endpoints/memory.js +19 -0
- package/lib/api/endpoints/mind.js +13 -0
- package/lib/api/endpoints/mon.js +179 -0
- package/lib/api/endpoints/objectives.js +12 -0
- package/lib/api/endpoints/plugins.js +18 -0
- package/lib/api/endpoints/presence.js +33 -0
- package/lib/api/endpoints/quoxplan.js +7 -0
- package/lib/api/endpoints/quoxseo.js +12 -0
- package/lib/api/endpoints/router.js +17 -0
- package/lib/api/endpoints/safety.js +7 -0
- package/lib/api/endpoints/server.js +21 -0
- package/lib/api/endpoints/setup.js +6 -0
- package/lib/api/endpoints/skills.js +20 -0
- package/lib/api/endpoints/stepup.js +20 -0
- package/lib/api/endpoints/teams.js +2 -2
- package/lib/api/endpoints/triggers.js +26 -0
- package/lib/api/endpoints/v1.js +120 -0
- package/lib/api/endpoints/ward.js +7 -0
- package/lib/api/endpoints/workflows.js +7 -0
- package/lib/api/endpoints/x.js +8 -0
- package/lib/commands/actions.js +131 -0
- package/lib/commands/admin.js +16 -0
- package/lib/commands/agent.js +170 -27
- package/lib/commands/agentAttach.js +140 -13
- package/lib/commands/approvals.js +78 -1
- package/lib/commands/aside.js +24 -0
- package/lib/commands/ask.js +146 -0
- package/lib/commands/assistant.js +59 -4
- package/lib/commands/auth.js +8 -2
- package/lib/commands/backup.js +8 -4
- package/lib/commands/blog.js +110 -0
- package/lib/commands/brain.js +105 -14
- package/lib/commands/build.js +19 -5
- package/lib/commands/chat.js +180 -26
- package/lib/commands/chatCompanion.js +27 -0
- package/lib/commands/chatState.js +45 -0
- package/lib/commands/claude.js +35 -38
- package/lib/commands/contacts.js +134 -4
- package/lib/commands/contactsSendAt.js +21 -0
- package/lib/commands/conversation.js +35 -4
- package/lib/commands/dashboard.js +43 -0
- package/lib/commands/department.js +28 -7
- package/lib/commands/devResident.js +15 -3
- package/lib/commands/devSupervisor.js +13 -1
- package/lib/commands/discord.js +75 -39
- package/lib/commands/done.js +98 -28
- package/lib/commands/engagement.js +107 -6
- package/lib/commands/evidence.js +154 -0
- package/lib/commands/ext.js +23 -0
- package/lib/commands/fetchAllowlist.js +71 -0
- package/lib/commands/file.js +38 -1
- package/lib/commands/fleet.js +336 -4
- package/lib/commands/governance.js +12 -3
- package/lib/commands/gripe.js +44 -13
- package/lib/commands/hat.js +254 -9
- package/lib/commands/helpers.js +18 -1
- package/lib/commands/hyperion.js +58 -0
- package/lib/commands/image.js +68 -0
- package/lib/commands/inbox.js +74 -23
- package/lib/commands/kuma.js +1050 -0
- package/lib/commands/kumaSla.js +174 -0
- package/lib/commands/licence.js +52 -155
- package/lib/commands/loop.js +291 -0
- package/lib/commands/lucid.js +10 -4
- package/lib/commands/memory.js +85 -8
- package/lib/commands/mind.js +71 -0
- package/lib/commands/mon.js +69 -2
- package/lib/commands/monitoring.js +1 -1
- package/lib/commands/motion.js +2 -2
- package/lib/commands/nucleus.js +48 -17
- package/lib/commands/objective.js +59 -1
- package/lib/commands/offload.js +89 -0
- package/lib/commands/onboard.js +2 -1
- package/lib/commands/org.js +30 -3
- package/lib/commands/peek.js +28 -0
- package/lib/commands/plan.js +38 -0
- package/lib/commands/plugin.js +110 -0
- package/lib/commands/presence.js +89 -0
- package/lib/commands/provider.js +217 -0
- package/lib/commands/qlarity.js +19 -3
- package/lib/commands/quoxchat.js +13 -4
- package/lib/commands/quoxseo.js +69 -0
- package/lib/commands/reach.js +2 -1
- package/lib/commands/research.js +79 -0
- package/lib/commands/router.js +77 -0
- package/lib/commands/rows.js +367 -0
- package/lib/commands/run.js +82 -10
- package/lib/commands/scope.js +2 -2
- package/lib/commands/server.js +405 -0
- package/lib/commands/setup.js +33 -7
- package/lib/commands/shortcut.js +41 -0
- package/lib/commands/skills.js +68 -9
- package/lib/commands/slot.js +260 -7
- package/lib/commands/status.js +1 -1
- package/lib/commands/team.js +3 -1
- package/lib/commands/tips.js +23 -0
- package/lib/commands/tool.js +133 -45
- package/lib/commands/trace.js +12 -7
- package/lib/commands/trigger.js +95 -0
- package/lib/commands/update.js +46 -0
- package/lib/commands/vault.js +31 -8
- package/lib/commands/vm.js +237 -0
- package/lib/commands/voice.js +34 -3
- package/lib/commands/volt.js +45 -3
- package/lib/commands/walkthrough.js +32 -0
- package/lib/commands/watch.js +73 -21
- package/lib/commands/welcome.js +14 -0
- package/lib/commands/workflow.js +189 -32
- package/lib/commands/x.js +13 -4
- package/lib/commands/youtube.js +172 -0
- package/lib/core/actingAs.js +2 -2
- package/lib/core/actionItems.js +115 -0
- package/lib/core/activeSummary.js +186 -0
- package/lib/core/agentAlias.js +2 -0
- package/lib/core/agentFamilies.js +7 -0
- package/lib/core/aside.js +54 -0
- package/lib/core/askDecision.js +60 -0
- package/lib/core/auth.js +164 -13
- package/lib/core/callerSource.js +1 -1
- package/lib/core/cliBuild.js +62 -0
- package/lib/core/client.js +152 -35
- package/lib/core/commandRegistrars.js +130 -0
- package/lib/core/config.js +143 -25
- package/lib/core/customHats.js +168 -0
- package/lib/core/doneEngineFetch.js +8 -0
- package/lib/core/errors.js +329 -20
- package/lib/core/findingReconcile.js +90 -0
- package/lib/core/global-flags.js +105 -5
- package/lib/core/governedSession.js +1 -3
- package/lib/core/hyperion.js +102 -0
- package/lib/core/inboxResolution.js +65 -0
- package/lib/core/lazyCommands.js +4 -1
- package/lib/core/liveSessionsReport.js +1 -1
- package/lib/core/loopGlyph.js +97 -0
- package/lib/core/offload.js +533 -0
- package/lib/core/org-override.js +1 -1
- package/lib/core/orgResolve.js +70 -0
- package/lib/core/output.js +11 -11
- package/lib/core/peek.js +126 -0
- package/lib/core/profileUsers.js +51 -0
- package/lib/core/promptCancel.js +18 -0
- package/lib/core/repoPath.js +11 -0
- package/lib/core/residentSeed.js +3 -1
- package/lib/core/rootOptions.js +39 -0
- package/lib/core/router.js +168 -0
- package/lib/core/selfUpdate.js +188 -0
- package/lib/core/sessionHeartbeat.js +12 -8
- package/lib/core/sessionsOverview.js +64 -0
- package/lib/core/shortcut.js +83 -0
- package/lib/core/slotKeeper.js +248 -3
- package/lib/core/slotWake.js +120 -0
- package/lib/core/supervisorEngine.js +71 -2
- package/lib/core/version.js +1 -1
- package/lib/core/vm.js +366 -0
- package/lib/core/vmTui.js +86 -0
- package/lib/core/walkthrough.js +35 -0
- package/lib/dragon/companionView.js +34 -0
- package/lib/dragon/frames.js +118 -0
- package/lib/dragon/jobs.js +123 -0
- package/lib/dragon/renderWorker.js +15 -0
- package/lib/dragon/select.js +72 -0
- package/lib/evidence/bundleExport.js +47 -0
- package/lib/evidence/bundleVerify.js +77 -0
- package/lib/hyperion/deck.js +29 -0
- package/lib/lucid/LucidCard.jsx +68 -0
- package/lib/lucid/LucidIntro.jsx +45 -58
- package/lib/lucid/cards.js +86 -0
- package/lib/lucid/markdown.js +81 -0
- package/lib/lucid/motion.js +56 -0
- package/lib/quoxon/EXTENSIONS.md +23 -0
- package/lib/quoxon/activity.js +14 -2
- package/lib/quoxon/aocl.js +117 -0
- package/lib/quoxon/api.js +690 -25
- package/lib/quoxon/app.js +3586 -250
- package/lib/quoxon/approvalFacts.js +96 -0
- package/lib/quoxon/approvalSentence.js +44 -0
- package/lib/quoxon/approvalsView.js +384 -0
- package/lib/quoxon/asideView.js +40 -0
- package/lib/quoxon/ask.js +121 -0
- package/lib/quoxon/away.js +52 -0
- package/lib/quoxon/bang.js +224 -0
- package/lib/quoxon/bulkRisk.js +17 -0
- package/lib/quoxon/capabilities.js +13 -6
- package/lib/quoxon/cells.js +53 -0
- package/lib/quoxon/commandShort.js +124 -0
- package/lib/quoxon/commands.js +429 -39
- package/lib/quoxon/connect.js +210 -23
- package/lib/quoxon/connectCard.js +19 -2
- package/lib/quoxon/consent.js +86 -0
- package/lib/quoxon/copy.js +4 -3
- package/lib/quoxon/countdownLine.js +20 -0
- package/lib/quoxon/deck.js +30 -0
- package/lib/quoxon/demo.autoplay.js +29 -0
- package/lib/quoxon/demo.fixtures.js +1 -1
- package/lib/quoxon/demo.js +46 -3
- package/lib/quoxon/devObserver.js +20 -0
- package/lib/quoxon/dividerAocl.js +192 -0
- package/lib/quoxon/dividerRule.js +57 -0
- package/lib/quoxon/dragonCompanion.js +255 -0
- package/lib/quoxon/dragonModes.js +151 -0
- package/lib/quoxon/dragonMoments.js +82 -0
- package/lib/quoxon/dragonReads.js +68 -0
- package/lib/quoxon/dragonView.js +41 -0
- package/lib/quoxon/earlyInput.js +40 -0
- package/lib/quoxon/echo.js +15 -0
- package/lib/quoxon/egg.js +2 -2
- package/lib/quoxon/events.js +266 -26
- package/lib/quoxon/extensions.js +276 -0
- package/lib/quoxon/feedback.js +50 -0
- package/lib/quoxon/gallery.js +21 -7
- package/lib/quoxon/handoffOffer.js +78 -0
- package/lib/quoxon/hatSwitch.js +37 -0
- package/lib/quoxon/headless.js +161 -0
- package/lib/quoxon/hostUrls.js +41 -0
- package/lib/quoxon/inboxView.js +79 -0
- package/lib/quoxon/indicator.js +6 -2
- package/lib/quoxon/installCli.js +79 -0
- package/lib/quoxon/intent.js +12 -1
- package/lib/quoxon/keys.js +51 -8
- package/lib/quoxon/launcher.js +103 -0
- package/lib/quoxon/loopCard.js +456 -0
- package/lib/quoxon/loopCards.js +180 -0
- package/lib/quoxon/loopStream.js +87 -0
- package/lib/quoxon/main.js +101 -160
- package/lib/quoxon/markdown.js +45 -21
- package/lib/quoxon/markdownSettle.js +19 -0
- package/lib/quoxon/matrixDomains.json +46 -0
- package/lib/quoxon/mentions.js +90 -0
- package/lib/quoxon/mind.js +103 -0
- package/lib/quoxon/mode.js +46 -0
- package/lib/quoxon/model.js +132 -26
- package/lib/quoxon/modelPrefs.js +345 -0
- package/lib/quoxon/motion/clock.js +3 -2
- package/lib/quoxon/motion/level.js +91 -0
- package/lib/quoxon/mouse.js +222 -0
- package/lib/quoxon/noai.js +57 -0
- package/lib/quoxon/objectiveOffer.js +82 -0
- package/lib/quoxon/objects/away.js +72 -0
- package/lib/quoxon/objects/fetch.js +17 -5
- package/lib/quoxon/objects/fleet.js +149 -0
- package/lib/quoxon/objects/health.js +186 -0
- package/lib/quoxon/objects/index.js +46 -7
- package/lib/quoxon/objects/mirror.js +137 -0
- package/lib/quoxon/objects/model.js +33 -0
- package/lib/quoxon/objects/peek.js +17 -0
- package/lib/quoxon/objects/proof.js +12 -4
- package/lib/quoxon/objects/prove.js +130 -0
- package/lib/quoxon/objects/replay.js +125 -0
- package/lib/quoxon/objects/sitrep.js +58 -0
- package/lib/quoxon/objects/view.js +11 -0
- package/lib/quoxon/onboarding/copy.js +32 -4
- package/lib/quoxon/onboarding/credential.js +17 -4
- package/lib/quoxon/onboarding/dockerOffer.js +45 -0
- package/lib/quoxon/onboarding/failureCause.js +33 -0
- package/lib/quoxon/onboarding/finish.js +15 -0
- package/lib/quoxon/onboarding/index.js +34 -7
- package/lib/quoxon/onboarding/installRunner.js +64 -5
- package/lib/quoxon/onboarding/preflight.js +17 -3
- package/lib/quoxon/onboarding/probe.js +65 -0
- package/lib/quoxon/onboarding/shortcutOffer.js +28 -0
- package/lib/quoxon/onboarding/view.js +76 -19
- package/lib/quoxon/orgLabel.js +10 -0
- package/lib/quoxon/orgSwitch.js +59 -0
- package/lib/quoxon/ownBroadcast.js +22 -0
- package/lib/quoxon/palette.js +43 -0
- package/lib/quoxon/planSteps.js +51 -0
- package/lib/quoxon/print.js +12 -5
- package/lib/quoxon/profileSwitch.js +53 -0
- package/lib/quoxon/promptHistory.js +41 -0
- package/lib/quoxon/providers.js +48 -0
- package/lib/quoxon/qart.js +20 -51
- package/lib/quoxon/qdragon.js +410 -0
- package/lib/quoxon/qdragonFly.js +62 -0
- package/lib/quoxon/qdragonScenes.js +282 -0
- package/lib/quoxon/qeyes.js +213 -0
- package/lib/quoxon/qmotion.js +75 -37
- package/lib/quoxon/render.js +740 -124
- package/lib/quoxon/resend.js +72 -0
- package/lib/quoxon/roster.js +117 -17
- package/lib/quoxon/route.js +6 -5
- package/lib/quoxon/rows.js +954 -0
- package/lib/quoxon/rowsStore.js +80 -0
- package/lib/quoxon/screenContext.js +150 -0
- package/lib/quoxon/screenFrames.js +254 -0
- package/lib/quoxon/serverChip.js +42 -0
- package/lib/quoxon/session.js +182 -2
- package/lib/quoxon/sessionDead.js +17 -0
- package/lib/quoxon/signin.js +125 -0
- package/lib/quoxon/slash/agents.js +112 -0
- package/lib/quoxon/slash/argSources.js +65 -0
- package/lib/quoxon/slash/args.js +65 -0
- package/lib/quoxon/slash/automate.js +89 -0
- package/lib/quoxon/slash/broadcast.js +140 -0
- package/lib/quoxon/slash/broadcastRun.js +108 -0
- package/lib/quoxon/slash/card.js +30 -0
- package/lib/quoxon/slash/challenge.js +114 -0
- package/lib/quoxon/slash/cliCommand.js +94 -0
- package/lib/quoxon/slash/cliLeaves.json +1023 -0
- package/lib/quoxon/slash/compact.js +1 -1
- package/lib/quoxon/slash/confirm.js +30 -0
- package/lib/quoxon/slash/crystallize.js +69 -0
- package/lib/quoxon/slash/dashboard.js +136 -0
- package/lib/quoxon/slash/draft.js +54 -0
- package/lib/quoxon/slash/evidenceCmd.js +34 -0
- package/lib/quoxon/slash/hatWalk.js +206 -0
- package/lib/quoxon/slash/inverses.js +77 -0
- package/lib/quoxon/slash/killer.js +24 -0
- package/lib/quoxon/slash/killerApi.js +16 -0
- package/lib/quoxon/slash/ledger.js +13 -8
- package/lib/quoxon/slash/listView.js +3 -1
- package/lib/quoxon/slash/modelCmd.js +261 -0
- package/lib/quoxon/slash/palette.js +22 -8
- package/lib/quoxon/slash/paletteController.js +117 -7
- package/lib/quoxon/slash/panels.js +21 -6
- package/lib/quoxon/slash/picker.js +20 -2
- package/lib/quoxon/slash/qpeCmd.js +35 -0
- package/lib/quoxon/slash/restart.js +95 -13
- package/lib/quoxon/slash/resume.js +18 -1
- package/lib/quoxon/slash/rowCmd.js +157 -0
- package/lib/quoxon/slash/rowWalk.js +175 -0
- package/lib/quoxon/slash/settings.js +16 -6
- package/lib/quoxon/slash/spawn.js +177 -0
- package/lib/quoxon/slash/stepUp.js +274 -0
- package/lib/quoxon/slash/stepUpCard.js +60 -0
- package/lib/quoxon/slash/stepUpCmds.js +78 -0
- package/lib/quoxon/slash/todoCmd.js +122 -0
- package/lib/quoxon/slash/undoApi.js +38 -0
- package/lib/quoxon/slash/undoCmd.js +49 -0
- package/lib/quoxon/slash/undoWish.js +15 -0
- package/lib/quoxon/slash/watch.js +164 -0
- package/lib/quoxon/slash/wish.js +23 -0
- package/lib/quoxon/slash/wishCmd.js +32 -0
- package/lib/quoxon/slotChat.js +300 -0
- package/lib/quoxon/slotFormat.js +195 -0
- package/lib/quoxon/slotRun.js +131 -0
- package/lib/quoxon/sound/mapping.js +2 -1
- package/lib/quoxon/stray.js +22 -0
- package/lib/quoxon/suggestCommand.js +90 -0
- package/lib/quoxon/term.js +46 -11
- package/lib/quoxon/textBuffer.js +176 -2
- package/lib/quoxon/theme.js +58 -25
- package/lib/quoxon/tips.js +433 -0
- package/lib/quoxon/toolCards.js +30 -0
- package/lib/quoxon/toolError.js +41 -0
- package/lib/quoxon/transcript.js +42 -4
- package/lib/quoxon/web/qface.js +18 -10
- package/lib/quoxon/welcome.js +1 -1
- package/lib/quoxon/whatsnew.js +136 -0
- package/lib/quoxon/whatsnew.jsonl +134 -0
- package/lib/quoxon/windowOrg.js +123 -0
- package/lib/server/devServers.js +137 -0
- package/lib/server/diag.js +129 -0
- package/lib/server/doctor.js +71 -0
- package/lib/server/install.js +149 -0
- package/lib/server/pluginEnable.js +161 -0
- package/lib/server/skew.js +56 -0
- package/lib/server/snapshot.js +131 -0
- package/lib/server/status.js +128 -0
- package/lib/server/sys.js +65 -0
- package/lib/server/uninstall.js +63 -0
- package/lib/server/update.js +234 -0
- package/lib/toolOutputShape.js +315 -0
- package/lib/tui/views/HyperionDeck.jsx +79 -0
- package/lib/tui/views/LucidDeck.jsx +209 -217
- package/package.json +5 -1
- package/vendor/dragonlab/SOURCE +4 -0
- package/vendor/dragonlab/design/cipher/v1/cipher-master.svg +46 -0
- package/vendor/dragonlab/design/cipher/v2/character.mjs +49 -0
- package/vendor/dragonlab/design/cipher/v2/review.mjs +33 -0
- package/vendor/dragonlab/design/cipher/v2/watch.mjs +56 -0
- package/vendor/dragonlab/design/drago/v1/character.mjs +19 -0
- package/vendor/dragonlab/design/drago/v1/drago-master.svg +56 -0
- package/vendor/dragonlab/design/drago/v2/character.mjs +87 -0
- package/vendor/dragonlab/design/drago/v4/character.mjs +67 -0
- package/vendor/dragonlab/design/drago/v4/profile.mjs +35 -0
- package/vendor/dragonlab/design/drago/v4/review.mjs +33 -0
- package/vendor/dragonlab/design/drago/v4/watch.mjs +56 -0
- package/vendor/dragonlab/design/hyperion/v1/hyperion-master.svg +56 -0
- package/vendor/dragonlab/design/hyperion/v2/character.mjs +79 -0
- package/vendor/dragonlab/design/hyperion/v2/review.mjs +33 -0
- package/vendor/dragonlab/design/hyperion/v2/watch.mjs +56 -0
- package/vendor/dragonlab/design/lucid/recovery/arrival.mjs +22 -0
- package/vendor/dragonlab/design/lucid/recovery/character.mjs +106 -0
- package/vendor/dragonlab/design/lucid/recovery/layout.mjs +50 -0
- package/vendor/dragonlab/design/lucid/recovery/lucid-master.svg +78 -0
- package/vendor/dragonlab/design/lucid/recovery/motion.mjs +42 -0
- package/vendor/dragonlab/design/lucid/recovery/render.mjs +121 -0
- package/vendor/dragonlab/design/lucid/recovery/review.mjs +68 -0
- package/vendor/dragonlab/design/lucid/recovery/verify.mjs +84 -0
- package/vendor/dragonlab/design/lucid/recovery/watch.mjs +56 -0
- package/vendor/dragonlab/design/seer/v1/seer-master.svg +53 -0
- package/vendor/dragonlab/design/seer/v2/character.mjs +88 -0
- package/vendor/dragonlab/design/seer/v3/character.mjs +33 -0
- package/vendor/dragonlab/design/seer/v4/character.mjs +66 -0
- package/vendor/dragonlab/design/seer/v4/review.mjs +33 -0
- package/vendor/dragonlab/design/seer/v4/watch.mjs +56 -0
- package/vendor/dragonlab/design/stage3/companion-review.mjs +28 -0
- package/vendor/dragonlab/design/stage3/sentinel-art.js +39 -0
- package/vendor/dragonlab/design/stage3/sentinel-braille.js +216 -0
- package/vendor/dragonlab/design/stage3/sentinel-character.js +257 -0
- package/vendor/dragonlab/design/stage3/sentinel-icon.js +103 -0
- package/vendor/dragonlab/design/stage3/sentinel-warden-eyes.js +95 -0
- package/vendor/dragonlab/design/stage3/svg-coverage.js +148 -0
- package/vendor/dragonlab/factory/svg-rig.mjs +63 -0
- package/vendor/dragonlab/integration/companions-v1/characters.mjs +88 -0
- package/vendor/dragonlab/integration/companions-v1/cipher.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/companion.mjs +63 -0
- package/vendor/dragonlab/integration/companions-v1/drago.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/hyperion.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/lucid.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/seer.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/sentinel.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/view.mjs +28 -0
- package/vendor/dragonlab/integration/hyperion-v3/activity.mjs +76 -0
- package/vendor/dragonlab/lib/power-effects.js +294 -0
- package/vendor/dragonlab/lib/power-registry.js +74 -0
- package/vendor/dragonlab/vendor/quox-dragon/qart.js +348 -0
- package/vendor/dragonlab/vendor/quox-dragon/qeyes.js +213 -0
- package/lib/core/websiteSession.js +0 -148
package/lib/commands/discord.js
CHANGED
|
@@ -25,14 +25,10 @@ const QDP = process.env.QDP_CLI_PATH
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Per-verb usage, mirrored from qdp.mjs's own header doc block (bin/qdp.mjs
|
|
28
|
-
* in the quox-discord-pro repo)
|
|
29
|
-
* own
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* straight through to qdp — which silently ran the verb instead of showing
|
|
33
|
-
* help. Worst case for a mutating verb (bind/unbind/provision): "--help"
|
|
34
|
-
* would have actually bound/unbound/provisioned a channel. This map is
|
|
35
|
-
* checked BEFORE qdp is ever spawned so that can't happen (quox#603).
|
|
28
|
+
* in the quox-discord-pro repo), shown in each subcommand's --help. qdp has no
|
|
29
|
+
* --help handling of its own; before quox#1077 this command hand-rolled arg
|
|
30
|
+
* dispatch and had to intercept --help itself (quox#603). Commander
|
|
31
|
+
* subcommands now answer --help before any action runs.
|
|
36
32
|
*/
|
|
37
33
|
const VERB_USAGE = {
|
|
38
34
|
status: 'qdp status daemon liveness, instances, binding count',
|
|
@@ -56,41 +52,81 @@ const VERB_USAGE = {
|
|
|
56
52
|
+ ' --dry-run print exactly what it WOULD do, change nothing',
|
|
57
53
|
};
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
/**
|
|
56
|
+
* qdp verbs that only READ (quox#1077). Each was checked against bin/qdp.mjs:
|
|
57
|
+
* status/instances/bindings print config.json, the pid file and the systemd
|
|
58
|
+
* state; gaps GETs the open support-gap inbox items; health reads the pid and
|
|
59
|
+
* systemd state; doctor GETs the control API /health and Discord's channel,
|
|
60
|
+
* webhook and @me endpoints. None issues PUT/POST/PATCH/DELETE or writes a
|
|
61
|
+
* file. bind, unbind and provision PUT/DELETE bindings and create channels.
|
|
62
|
+
*/
|
|
63
|
+
export const DISCORD_READ_VERBS = ['status', 'instances', 'bindings', 'gaps', 'health', 'doctor'];
|
|
64
|
+
export const DISCORD_WRITE_VERBS = ['bind', 'unbind', 'provision'];
|
|
65
|
+
|
|
66
|
+
// No "Usage:" line here: the matrix's --help walker (scripts/matrix/cli-tree.mjs) reads one as the command's usage.
|
|
67
|
+
const GENERAL_USAGE = Object.values(VERB_USAGE).map((u) => ` ${u}`).join('\n')
|
|
61
68
|
+ '\n\nRun `quox discord <verb> --help` for a single verb\'s usage.';
|
|
62
69
|
|
|
63
|
-
function
|
|
64
|
-
if (
|
|
65
|
-
process.
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
function runQdp(verb, args = []) {
|
|
71
|
+
if (!fs.existsSync(QDP)) {
|
|
72
|
+
process.stderr.write(
|
|
73
|
+
`Discord Pro control CLI (qdp) not found at ${QDP}\n` +
|
|
74
|
+
'Set QDP_CLI_PATH if it lives elsewhere, or install the quox-discord-pro daemon on this host.\n',
|
|
75
|
+
);
|
|
76
|
+
process.exit(2);
|
|
68
77
|
}
|
|
78
|
+
const r = spawnSync(process.execPath, [QDP, verb, ...args], { stdio: 'inherit' });
|
|
79
|
+
if (r.error) {
|
|
80
|
+
process.stderr.write(`qdp failed to run: ${r.error.message}\n`);
|
|
81
|
+
process.exit(2);
|
|
82
|
+
}
|
|
83
|
+
process.exit(r.status ?? 2);
|
|
69
84
|
}
|
|
70
85
|
|
|
86
|
+
const VERB_DESC = { unbind: 'remove a channel binding', doctor: 'per-binding view/webhook/liveness check (exits non-zero if any binding is broken)' };
|
|
87
|
+
const firstLine = (verb) => VERB_DESC[verb] || VERB_USAGE[verb].split('\n')[0].replace(/^qdp \S+(?: [<[][^\]>]*[>\]])*\s+/, '').trim();
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* One commander subcommand per qdp verb (quox#1077). It used to be a single
|
|
91
|
+
* `discord [args...]` leaf, so the CLI leaf policy could only class the whole
|
|
92
|
+
* group, and it classed it write: a read like `quox discord bindings` was held
|
|
93
|
+
* for approval. Per-verb leaves let reads be read and keep bind/unbind/provision
|
|
94
|
+
* write. Commander's own --help runs before any action, so `--help` can never
|
|
95
|
+
* reach qdp and run a mutating verb (quox#603).
|
|
96
|
+
*/
|
|
71
97
|
export function registerDiscordCommands(program) {
|
|
72
|
-
program
|
|
73
|
-
.command('discord
|
|
74
|
-
.description('Quox Discord Pro daemon: status | instances | bindings |
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
const discord = program
|
|
99
|
+
.command('discord')
|
|
100
|
+
.description('Quox Discord Pro daemon: status | instances | bindings | gaps | health | doctor | bind | unbind | provision')
|
|
101
|
+
.addHelpText('after', `\nPer-verb usage (each verb shells straight through to qdp):\n${GENERAL_USAGE}`);
|
|
102
|
+
|
|
103
|
+
for (const verb of DISCORD_READ_VERBS) {
|
|
104
|
+
discord
|
|
105
|
+
.command(verb)
|
|
106
|
+
.description(firstLine(verb) || verb)
|
|
107
|
+
.addHelpText('after', `\nqdp usage:\n ${VERB_USAGE[verb]}`)
|
|
108
|
+
.action(() => runQdp(verb));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const writeArgs = { bind: '<channelId> <mode> [pairs...]', unbind: '<channelId>', provision: '<agentId> [args...]' };
|
|
112
|
+
for (const verb of DISCORD_WRITE_VERBS) {
|
|
113
|
+
const leaf = discord
|
|
114
|
+
.command(`${verb} ${writeArgs[verb]}`);
|
|
115
|
+
// Only provision has a dry run in qdp. Declaring it here keeps it in --help and lets the
|
|
116
|
+
// root guard refuse `discord bind|unbind --dry-run` instead of binding for real.
|
|
117
|
+
if (verb === 'provision') leaf.option('--dry-run', 'print exactly what it WOULD do, change nothing');
|
|
118
|
+
leaf
|
|
119
|
+
.description(firstLine(verb) || verb)
|
|
120
|
+
.addHelpText('after', `\nqdp usage:\n ${VERB_USAGE[verb]}`)
|
|
121
|
+
// qdp parses its own options (provision --mode/--host/...), so they pass through unchanged.
|
|
122
|
+
.allowUnknownOption(true)
|
|
123
|
+
.action((...a) => {
|
|
124
|
+
const cmd = a[a.length - 1];
|
|
125
|
+
const args = [...cmd.args];
|
|
126
|
+
// --dry-run is also a root quox flag, so commander hands it to the root and it never reaches
|
|
127
|
+
// cmd.args: a requested dry run of provision used to provision for real. Forward it explicitly.
|
|
128
|
+
if (cmd.optsWithGlobals().dryRun && !args.includes('--dry-run')) args.push('--dry-run');
|
|
129
|
+
runQdp(verb, args);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
96
132
|
}
|
package/lib/commands/done.js
CHANGED
|
@@ -55,6 +55,7 @@ import path from 'node:path';
|
|
|
55
55
|
import {
|
|
56
56
|
resolveFetchDestination,
|
|
57
57
|
fetchEngine,
|
|
58
|
+
inspectInstall,
|
|
58
59
|
OPT_GAUGE_DIR,
|
|
59
60
|
userLocalGaugeDir,
|
|
60
61
|
legacyGaugeDir,
|
|
@@ -88,19 +89,21 @@ export function resolveEnginePath({ env = process.env, existsSync = fs.existsSyn
|
|
|
88
89
|
|
|
89
90
|
const ENGINE = resolveEnginePath();
|
|
90
91
|
|
|
91
|
-
function
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
function engineMissingMessage(engine) {
|
|
93
|
+
return `DoneEngine not found at ${engine}\n`
|
|
94
|
+
+ 'Self-heal: `quox done fetch` pulls the engine bundle straight from '
|
|
95
|
+
+ 'the collector, no human in the loop. Otherwise `quox dev enable` '
|
|
96
|
+
+ 'delivers it to /opt/quoxgauge as part of full dev-server '
|
|
97
|
+
+ 'provisioning. Or set QUOX_DONEENGINE_PATH to a checkout.\n';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function runEngine(args, engine = ENGINE) {
|
|
101
|
+
if (!fs.existsSync(engine)) {
|
|
102
|
+
process.stderr.write(engineMissingMessage(engine));
|
|
100
103
|
// 2, not 1: a missing engine is "cannot tell", not "refused".
|
|
101
104
|
return 2;
|
|
102
105
|
}
|
|
103
|
-
const r = spawnSync(process.execPath, [
|
|
106
|
+
const r = spawnSync(process.execPath, [engine, ...args], { stdio: 'inherit' });
|
|
104
107
|
if (r.error) {
|
|
105
108
|
process.stderr.write(`DoneEngine failed to run: ${r.error.message}\n`);
|
|
106
109
|
return 2;
|
|
@@ -108,6 +111,23 @@ function runEngine(args) {
|
|
|
108
111
|
return r.status ?? 2;
|
|
109
112
|
}
|
|
110
113
|
|
|
114
|
+
/**
|
|
115
|
+
* VISIBILITY GATE (#988 follow-on). Public installs do not ship DoneEngine
|
|
116
|
+
* (its bundle is built from the private quox-gauge repo and delivered to dev
|
|
117
|
+
* servers by `quox dev enable`), so listing `quox done` in their help
|
|
118
|
+
* advertises a command that can only fail. It is listed only when an engine
|
|
119
|
+
* is installed locally, decided from the filesystem alone with the same
|
|
120
|
+
* resolution the runner uses: building help never makes a network call.
|
|
121
|
+
* QUOX_HELP_INCLUDE_GATED=1 lists it regardless; the generators that scrape
|
|
122
|
+
* `--help` into the command trees set it so those trees do not depend on
|
|
123
|
+
* which host ran them.
|
|
124
|
+
*/
|
|
125
|
+
export function isDoneEngineInstalled({ env = process.env, home = os.homedir(), fsOps } = {}) {
|
|
126
|
+
const ops = fsOps || { existsSync: fs.existsSync, readdirSync: fs.readdirSync };
|
|
127
|
+
const engine = resolveEnginePath({ env, existsSync: ops.existsSync, home });
|
|
128
|
+
return inspectInstall(path.dirname(engine), ops).healthy;
|
|
129
|
+
}
|
|
130
|
+
|
|
111
131
|
function defaultExecFn(cmd, args) {
|
|
112
132
|
return spawnSync(cmd, args, { encoding: 'utf8' });
|
|
113
133
|
}
|
|
@@ -140,8 +160,56 @@ function defaultFsOps() {
|
|
|
140
160
|
}
|
|
141
161
|
|
|
142
162
|
export function registerDoneCommands(program, ctx = {}) {
|
|
163
|
+
const env = ctx.env || process.env;
|
|
164
|
+
const home = ctx.home || os.homedir();
|
|
165
|
+
// Tests inject fsOps/env/home; the real CLI keeps the import-time ENGINE.
|
|
166
|
+
const injected = !!(ctx.fsOps || ctx.env || ctx.home);
|
|
167
|
+
const currentEngine = () => (injected
|
|
168
|
+
? resolveEnginePath({ env, existsSync: (ctx.fsOps || fs).existsSync, home })
|
|
169
|
+
: ENGINE);
|
|
170
|
+
const visible = env.QUOX_HELP_INCLUDE_GATED === '1'
|
|
171
|
+
|| isDoneEngineInstalled({ env, home, fsOps: ctx.fsOps });
|
|
172
|
+
|
|
173
|
+
const fetchDeps = () => ({
|
|
174
|
+
execFn: ctx.execFn || defaultExecFn,
|
|
175
|
+
isWritable: ctx.isWritable || defaultIsWritable,
|
|
176
|
+
fsOps: ctx.fsOps || defaultFsOps(),
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Resolve a runnable engine for a subcommand. Present: use it, no network.
|
|
181
|
+
* Absent: try the existing fetch path once. A collector 404
|
|
182
|
+
* (DONEENGINE_NOT_SHIPPED) means this is a public install: refuse with that
|
|
183
|
+
* message and exit 2, having changed nothing. A bundle that IS available
|
|
184
|
+
* (a dev collector) installs and the freshly fetched engine runs.
|
|
185
|
+
*/
|
|
186
|
+
async function ensureEngine() {
|
|
187
|
+
const engine = currentEngine();
|
|
188
|
+
const exists = (ctx.fsOps || fs).existsSync(engine);
|
|
189
|
+
if (exists || !ctx.client || env.QUOX_DONEENGINE_PATH) return { engine };
|
|
190
|
+
const { execFn, isWritable, fsOps } = fetchDeps();
|
|
191
|
+
const { dest, needsSudo } = resolveFetchDestination({ env, isWritable, execFn });
|
|
192
|
+
try {
|
|
193
|
+
await fetchEngine({ dest, needsSudo, force: false, client: ctx.client, execFn, fsOps });
|
|
194
|
+
return { engine: path.join(dest, 'gauge.mjs') };
|
|
195
|
+
} catch (err) {
|
|
196
|
+
if (err.code === 'DONEENGINE_NOT_SHIPPED') {
|
|
197
|
+
process.stderr.write(`quox done: ${err.message}\n`);
|
|
198
|
+
return { code: 2 };
|
|
199
|
+
}
|
|
200
|
+
process.stderr.write(`quox done: engine fetch failed: ${err.message}\n`);
|
|
201
|
+
return { engine };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function runGuarded(args) {
|
|
206
|
+
const r = await ensureEngine();
|
|
207
|
+
if (r.code !== undefined) return r.code;
|
|
208
|
+
return runEngine(args, r.engine);
|
|
209
|
+
}
|
|
210
|
+
|
|
143
211
|
const done = program
|
|
144
|
-
.command('done')
|
|
212
|
+
.command('done', { hidden: !visible })
|
|
145
213
|
.description('Quox DoneEngine: is this actually done, and what is the evidence?');
|
|
146
214
|
|
|
147
215
|
done
|
|
@@ -150,7 +218,7 @@ export function registerDoneCommands(program, ctx = {}) {
|
|
|
150
218
|
.option('--pack <id>', 'rule pack to judge against', 'launch-spine')
|
|
151
219
|
.option('--json', 'structured verdict for agents')
|
|
152
220
|
.option('--root <path>', 'judge <subject> in this repo, reading its own STATUS map with the selected pack (e.g. a plugin with --pack plugin)')
|
|
153
|
-
.action((subject, options) => {
|
|
221
|
+
.action(async (subject, options) => {
|
|
154
222
|
const args = ['--can-ship', subject || 'all', '--pack', options.pack];
|
|
155
223
|
if (options.root) {
|
|
156
224
|
if (!subject) {
|
|
@@ -161,17 +229,17 @@ export function registerDoneCommands(program, ctx = {}) {
|
|
|
161
229
|
args.push('--subject', subject, '--root', path.resolve(options.root));
|
|
162
230
|
}
|
|
163
231
|
if (options.json) args.push('--json');
|
|
164
|
-
process.exitCode =
|
|
232
|
+
process.exitCode = await runGuarded(args);
|
|
165
233
|
});
|
|
166
234
|
|
|
167
235
|
done
|
|
168
236
|
.command('checklist [pack]')
|
|
169
237
|
.description('The builder checklist GENERATED from a pack (every gate the engine judges); --check verifies a doc that embeds it')
|
|
170
238
|
.option('--check <file>', 'exit 1 if the checklist block in this file has drifted from the pack')
|
|
171
|
-
.action((pack, options) => {
|
|
239
|
+
.action(async (pack, options) => {
|
|
172
240
|
const args = ['--checklist', pack || 'plugin'];
|
|
173
241
|
if (options.check) args.push('--check', path.resolve(options.check));
|
|
174
|
-
process.exitCode =
|
|
242
|
+
process.exitCode = await runGuarded(args);
|
|
175
243
|
});
|
|
176
244
|
|
|
177
245
|
done
|
|
@@ -180,12 +248,12 @@ export function registerDoneCommands(program, ctx = {}) {
|
|
|
180
248
|
.option('--json', 'machine-readable, includes raw deploy state')
|
|
181
249
|
.option('--dod', 'per-surface gates, split by evidence class')
|
|
182
250
|
.option('--no-deploy', 'skip docker/git; Deployed Ready becomes unknown, which is not a pass')
|
|
183
|
-
.action((options) => {
|
|
251
|
+
.action(async (options) => {
|
|
184
252
|
const args = [];
|
|
185
253
|
if (options.json) args.push('--json');
|
|
186
254
|
if (options.dod) args.push('--dod');
|
|
187
255
|
if (options.deploy === false) args.push('--no-deploy');
|
|
188
|
-
process.exitCode =
|
|
256
|
+
process.exitCode = await runGuarded(args);
|
|
189
257
|
});
|
|
190
258
|
|
|
191
259
|
done
|
|
@@ -193,25 +261,27 @@ export function registerDoneCommands(program, ctx = {}) {
|
|
|
193
261
|
.description('One rule pack against its subjects: BOTH finish lines, build and deployed')
|
|
194
262
|
.option('--snapshot', 'append this reading to the snapshot history')
|
|
195
263
|
.option('--org <id>', 'measure FOR this org: plan evidence reads as it, the snapshot is stamped with it (default: the pack\'s own org field)')
|
|
196
|
-
.action((pack, options) => {
|
|
264
|
+
.action(async (pack, options) => {
|
|
197
265
|
const args = pack ? ['--pack', pack] : ['--launch'];
|
|
198
266
|
if (options.snapshot) args.push('--snapshot');
|
|
199
267
|
if (options.org) args.push('--org', options.org);
|
|
200
|
-
process.exitCode =
|
|
268
|
+
process.exitCode = await runGuarded(args);
|
|
201
269
|
});
|
|
202
270
|
|
|
203
271
|
done
|
|
204
272
|
.command('history [pack]')
|
|
205
273
|
.description('Movement between snapshots, regressions listed first')
|
|
206
|
-
.action((pack) => {
|
|
207
|
-
process.exitCode =
|
|
274
|
+
.action(async (pack) => {
|
|
275
|
+
process.exitCode = await runGuarded(pack ? ['--history', '--pack', pack] : ['--history']);
|
|
208
276
|
});
|
|
209
277
|
|
|
210
278
|
done
|
|
211
279
|
.command('packs')
|
|
212
280
|
.description('List available rule packs')
|
|
213
|
-
.action(() => {
|
|
214
|
-
const
|
|
281
|
+
.action(async () => {
|
|
282
|
+
const r = await ensureEngine();
|
|
283
|
+
if (r.code !== undefined) { process.exitCode = r.code; return; }
|
|
284
|
+
const dir = path.join(path.dirname(r.engine), 'packs');
|
|
215
285
|
if (!fs.existsSync(dir)) {
|
|
216
286
|
process.stderr.write(
|
|
217
287
|
`No packs directory at ${dir}\n` +
|
|
@@ -246,13 +316,11 @@ export function registerDoneCommands(program, ctx = {}) {
|
|
|
246
316
|
process.exitCode = 2;
|
|
247
317
|
return;
|
|
248
318
|
}
|
|
249
|
-
const execFn
|
|
250
|
-
const isWritable = ctx.isWritable || defaultIsWritable;
|
|
251
|
-
const fsOps = ctx.fsOps || defaultFsOps();
|
|
319
|
+
const { execFn, isWritable, fsOps } = fetchDeps();
|
|
252
320
|
|
|
253
321
|
const { dest, reason, needsSudo } = resolveFetchDestination({
|
|
254
322
|
destOption: options.dest,
|
|
255
|
-
env
|
|
323
|
+
env,
|
|
256
324
|
isWritable,
|
|
257
325
|
execFn,
|
|
258
326
|
});
|
|
@@ -267,7 +335,9 @@ export function registerDoneCommands(program, ctx = {}) {
|
|
|
267
335
|
}
|
|
268
336
|
} catch (err) {
|
|
269
337
|
process.stderr.write(`quox done fetch failed: ${err.message}\n`);
|
|
270
|
-
|
|
338
|
+
// A public install's 404 is "this install has no DoneEngine", not a
|
|
339
|
+
// transient failure: exit 2 like every other "cannot tell" here.
|
|
340
|
+
process.exitCode = err.code === 'DONEENGINE_NOT_SHIPPED' ? 2 : 1;
|
|
271
341
|
}
|
|
272
342
|
});
|
|
273
343
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* quox engagement create <name> --environment-tier <tier> [--risk-ceiling <tier>]
|
|
5
5
|
* quox engagement list
|
|
6
6
|
* quox engagement show <id>
|
|
7
|
+
* quox engagement verify <id>
|
|
7
8
|
* quox engagement add-target <id> <type> <value> [--verification-method <method>]
|
|
8
9
|
* quox engagement verify-target <id> <targetId>
|
|
9
10
|
*
|
|
@@ -20,6 +21,7 @@ import { writeFileSync } from 'node:fs';
|
|
|
20
21
|
import { formatOutput, formatDetail, printSuccess, printInfo } from '../core/output.js';
|
|
21
22
|
import { events, printEvent, line as glyphLine } from '../core/quoxglyph.js';
|
|
22
23
|
import { resolveEndpoint } from '../api/endpoints.js';
|
|
24
|
+
import { gatherEvidence, evidenceChanged } from '../core/findingReconcile.js';
|
|
23
25
|
|
|
24
26
|
export function registerEngagementCommands(program, ctx) {
|
|
25
27
|
const { client, auth } = ctx;
|
|
@@ -80,6 +82,31 @@ export function registerEngagementCommands(program, ctx) {
|
|
|
80
82
|
}, { format: program.opts().output, emptyMessage: 'No targets added yet.' });
|
|
81
83
|
});
|
|
82
84
|
|
|
85
|
+
engagement
|
|
86
|
+
.command('verify <id>')
|
|
87
|
+
.description('Verify the WARD witness for an engagement (recompute receipt + Ed25519 tip signature)')
|
|
88
|
+
.action(async (id) => {
|
|
89
|
+
auth.requireAuth();
|
|
90
|
+
const ep = resolveEndpoint('engagement.verify', { id });
|
|
91
|
+
const data = await client.get(ep.path, { service: ep.service });
|
|
92
|
+
if (program.opts().output === 'json') {
|
|
93
|
+
console.log(JSON.stringify(data, null, 2));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const ok = data.pass === true;
|
|
97
|
+
console.log(ok ? '✓ WITNESS VERIFIED' : '✗ NOT VERIFIED');
|
|
98
|
+
console.log(` engagement ${data.engagement_id || id}`);
|
|
99
|
+
console.log(` witnessed ${data.witnessed ? 'yes' : 'no'}`);
|
|
100
|
+
console.log(` chain ${data.chainId || '-'}`);
|
|
101
|
+
if (data.seq != null) console.log(` entry #${data.seq}`);
|
|
102
|
+
if (data.chainHash) console.log(` chain hash ${String(data.chainHash).slice(0, 40)}`);
|
|
103
|
+
console.log(` chain recompute ${data.chainHashRecomputed ? 'ok' : 'FAILED'}`);
|
|
104
|
+
const sig = data.signature || {};
|
|
105
|
+
console.log(` tip signature ${sig.status}${sig.keyId ? ` (key ${String(sig.keyId).slice(0, 16)})` : ''}`);
|
|
106
|
+
if (data.signedTip && data.signedTip.tipId) console.log(` signed tip ${data.signedTip.tipId} (seq ${data.signedTip.tipSeq})`);
|
|
107
|
+
if (!ok && data.reason) console.log(` reason ${data.reason}`);
|
|
108
|
+
});
|
|
109
|
+
|
|
83
110
|
engagement
|
|
84
111
|
.command('set-gripe-target <id>')
|
|
85
112
|
.description('Set which repo a finding filed from this engagement goes to, and which org GitHub connector token files it. Lets a customer-site engagement file into the customer repo with the customer org token. Pass --clear (or --repo "") to fall back to the platform default (quoxai/quox) and the engagement own org.')
|
|
@@ -121,10 +148,13 @@ export function registerEngagementCommands(program, ctx) {
|
|
|
121
148
|
const data = await client.post(ep.path, { service: ep.service, body });
|
|
122
149
|
printSuccess(`Target "${value}" added to engagement ${id}.`);
|
|
123
150
|
if (data.target.verification_token) {
|
|
151
|
+
// #1007: a url target is challenged on its hostname (dns_txt) or at its own origin (well_known_file)
|
|
152
|
+
const stored = data.target.value || value;
|
|
153
|
+
const url = data.target.type === 'url' ? (() => { try { return new URL(stored); } catch { return null; } })() : null;
|
|
124
154
|
if (data.target.verification_method === 'dns_txt') {
|
|
125
|
-
console.log(` Add a DNS TXT record on ${
|
|
155
|
+
console.log(` Add a DNS TXT record on ${url ? url.hostname : stored}: quox-verify=${data.target.verification_token}`);
|
|
126
156
|
} else if (data.target.verification_method === 'well_known_file') {
|
|
127
|
-
console.log(` Serve this content at https://${
|
|
157
|
+
console.log(` Serve this content at ${url ? url.origin : `https://${stored}`}/.well-known/quox-verification: ${data.target.verification_token}`);
|
|
128
158
|
}
|
|
129
159
|
console.log(` Then run: quox engagement verify-target ${id} ${data.target.id}`);
|
|
130
160
|
}
|
|
@@ -172,6 +202,7 @@ export function registerEngagementCommands(program, ctx) {
|
|
|
172
202
|
console.log(JSON.stringify(data.output, null, 2));
|
|
173
203
|
} else {
|
|
174
204
|
console.log(` Failed (allowed: ${data.allowed}): ${data.error}`);
|
|
205
|
+
process.exitCode = 1; // #1007: a refusal is not success, for scripts as much as people
|
|
175
206
|
}
|
|
176
207
|
});
|
|
177
208
|
|
|
@@ -185,6 +216,7 @@ export function registerEngagementCommands(program, ctx) {
|
|
|
185
216
|
const data = await client.post(ep.path, { service: ep.service, body: { target: hostTarget, add: options.add === true } });
|
|
186
217
|
if (!data.success) {
|
|
187
218
|
console.log(` Failed (allowed: ${data.allowed}): ${data.error}`);
|
|
219
|
+
process.exitCode = 1; // #1007: a refusal is not success, for scripts as much as people
|
|
188
220
|
return;
|
|
189
221
|
}
|
|
190
222
|
const discovered = data.discovered || [];
|
|
@@ -234,6 +266,7 @@ export function registerEngagementCommands(program, ctx) {
|
|
|
234
266
|
console.log(` Poll: quox engagement job ${data.job_id}`);
|
|
235
267
|
} else {
|
|
236
268
|
console.log(` Failed (allowed: ${data.allowed}): ${data.error}`);
|
|
269
|
+
process.exitCode = 1; // #1007: a refusal is not success, for scripts as much as people
|
|
237
270
|
}
|
|
238
271
|
});
|
|
239
272
|
|
|
@@ -372,11 +405,16 @@ export function registerEngagementCommands(program, ctx) {
|
|
|
372
405
|
}
|
|
373
406
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
374
407
|
const rootOutput = program.opts().output;
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
408
|
+
// quox#919: the global --json reaches options.output as 'json' via
|
|
409
|
+
// mergeShadowedGlobals; an output-format name is never a file path.
|
|
410
|
+
const asPath = (v) => (v && !KNOWN_OUTPUT_FORMATS.has(v) ? v : null);
|
|
411
|
+
const outFile = asPath(options.output) || asPath(rootOutput) || `quox-security-${id}.pdf`;
|
|
378
412
|
writeFileSync(outFile, buf);
|
|
379
|
-
|
|
413
|
+
if (rootOutput === 'json') {
|
|
414
|
+
console.log(JSON.stringify({ success: true, file: outFile, bytes: buf.length }));
|
|
415
|
+
} else {
|
|
416
|
+
printSuccess(`Report saved to ${outFile} (${buf.length} bytes).`);
|
|
417
|
+
}
|
|
380
418
|
});
|
|
381
419
|
|
|
382
420
|
async function raiseIssue(findingId) {
|
|
@@ -564,6 +602,69 @@ export function registerEngagementCommands(program, ctx) {
|
|
|
564
602
|
printSuccess(`Schedule "${scheduleId}" deleted.`);
|
|
565
603
|
});
|
|
566
604
|
|
|
605
|
+
// SITREP-FINDINGS: reconcile open findings against their issue and fix. Evidence is gathered
|
|
606
|
+
// here (git + gh on this host); the collector decides and keeps the audit trail. Resolves only
|
|
607
|
+
// on a closed issue AND a fix in the running build; a critical is never auto-resolved.
|
|
608
|
+
engagement
|
|
609
|
+
.command('reconcile [findingId]')
|
|
610
|
+
.description('Reconcile open findings against their linked issue and fix commit (landed on main, deployed in the running build). Resolves only when the issue is closed AND the fix is deployed; a critical never auto-resolves. Every change is audited')
|
|
611
|
+
.option('--link <ref...>', 'link evidence: <findingId>=<owner/repo#N | #N | commit-sha> (repeatable)')
|
|
612
|
+
.option('--repo-dir <dir>', 'git checkout to read origin/main from (default: ~/quox-dashboard)')
|
|
613
|
+
.option('--build-dir <dir>', 'the tree the running build was built from (default: ~/.quox-build/quox-dashboard)')
|
|
614
|
+
.option('--issue-repo <owner/repo>', 'repo a bare #N refers to', 'quoxai/quox')
|
|
615
|
+
.option('--no-fetch', 'do not fetch origin/main first')
|
|
616
|
+
.option('--dry-run', 'show the evidence without recording anything')
|
|
617
|
+
.action(async (findingId, options) => {
|
|
618
|
+
auth.requireAuth();
|
|
619
|
+
const { spawnSync } = await import('node:child_process');
|
|
620
|
+
const run = (cmd, args) => {
|
|
621
|
+
const r = spawnSync(cmd, args, { encoding: 'utf8', timeout: 30000 });
|
|
622
|
+
return { ok: r.status === 0, out: String(r.stdout || '') };
|
|
623
|
+
};
|
|
624
|
+
const home = (await import('node:os')).homedir();
|
|
625
|
+
options.repoDir = options.repoDir || `${home}/quox-dashboard`;
|
|
626
|
+
options.buildDir = options.buildDir || `${home}/.quox-build/quox-dashboard`;
|
|
627
|
+
const links = {};
|
|
628
|
+
for (const l of options.link || []) {
|
|
629
|
+
const m = /^(FND_[0-9A-Z]+)=(.+)$/.exec(String(l));
|
|
630
|
+
if (!m) throw new Error(`--link must look like FND_...=<issue or sha>, got "${l}"`);
|
|
631
|
+
const v = m[2].trim();
|
|
632
|
+
links[m[1]] = { ...(links[m[1]] || {}), ...(/^[0-9a-f]{7,40}$/i.test(v) ? { commit: v } : { issue: v }) };
|
|
633
|
+
}
|
|
634
|
+
if (options.fetch !== false) run('git', ['-C', options.repoDir, 'fetch', '--quiet', 'origin', 'main']);
|
|
635
|
+
const ep = resolveEndpoint('engagement.findings.open', {});
|
|
636
|
+
const all = (await client.get(ep.path, { service: ep.service })).findings || [];
|
|
637
|
+
const findings = findingId ? all.filter((f) => f.id === findingId) : all;
|
|
638
|
+
if (findingId && !findings.length) throw new Error(`Finding "${findingId}" is not open in this org`);
|
|
639
|
+
const rows = [];
|
|
640
|
+
for (const f of findings) {
|
|
641
|
+
const ev = gatherEvidence(f, links[f.id] || {}, { repoDir: options.repoDir, buildDir: options.buildDir, issueRepo: options.issueRepo, run });
|
|
642
|
+
const row = { id: f.id, severity: f.severity, status: f.status, issue: ev.issue_ref ? `${ev.issue_ref}${ev.issue_state ? ` (${ev.issue_state})` : ''}` : '',
|
|
643
|
+
fix: ev.fix_commit ? `${ev.fix_commit.slice(0, 9)} ${ev.fix_state}` : '', outcome: '' };
|
|
644
|
+
if (!Object.keys(ev).length) { row.outcome = 'no evidence'; rows.push(row); continue; }
|
|
645
|
+
if (!evidenceChanged(f, ev)) { row.outcome = 'unchanged'; rows.push(row); continue; }
|
|
646
|
+
if (options.dryRun) { row.outcome = 'would record'; rows.push(row); continue; }
|
|
647
|
+
const rep = resolveEndpoint('engagement.findings.reconcile', { findingId: f.id });
|
|
648
|
+
const data = await client.post(rep.path, { service: rep.service, body: ev });
|
|
649
|
+
row.outcome = `${data.outcome}: ${data.reason}`;
|
|
650
|
+
row.status = data.finding?.status || row.status;
|
|
651
|
+
rows.push(row);
|
|
652
|
+
}
|
|
653
|
+
formatOutput({ columns: ['id', 'severity', 'status', 'issue', 'fix', 'outcome'], headers: ['Finding', 'Severity', 'Status', 'Issue', 'Fix', 'Outcome'], rows },
|
|
654
|
+
{ format: program.opts().output, emptyMessage: 'No open findings.' });
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
engagement
|
|
658
|
+
.command('reconciliations <findingId>')
|
|
659
|
+
.description('Show the reconciliation audit trail of one finding (what evidence was recorded, when, by whom, and the outcome)')
|
|
660
|
+
.action(async (findingId) => {
|
|
661
|
+
auth.requireAuth();
|
|
662
|
+
const ep = resolveEndpoint('engagement.findings.reconciliations', { findingId });
|
|
663
|
+
const data = await client.get(ep.path, { service: ep.service });
|
|
664
|
+
formatOutput({ columns: ['created_at', 'outcome', 'reason', 'actor', 'evidence'], headers: ['When', 'Outcome', 'Reason', 'Actor', 'Evidence'], rows: data.reconciliations || [] },
|
|
665
|
+
{ format: program.opts().output, emptyMessage: 'No reconciliations recorded.' });
|
|
666
|
+
});
|
|
667
|
+
|
|
567
668
|
engagement
|
|
568
669
|
.command('sync-issues <id>')
|
|
569
670
|
.description('Reconcile this engagement\'s filed findings against their GitHub issue state. A closed issue auto-resolves its finding. Honest no-op if GitHub is not configured')
|