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,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity commands: CRUD, search, relate
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { formatOutput, formatDetail, printSuccess } from '../core/output.js';
|
|
6
|
+
import { parseJSON } from './helpers.js';
|
|
7
|
+
import { resolveEndpoint } from '../api/endpoints.js';
|
|
8
|
+
|
|
9
|
+
export function registerEntityCommands(program, ctx) {
|
|
10
|
+
const { client, auth } = ctx;
|
|
11
|
+
|
|
12
|
+
const entity = program
|
|
13
|
+
.command('entity')
|
|
14
|
+
.description('Manage entities in the knowledge graph');
|
|
15
|
+
|
|
16
|
+
entity
|
|
17
|
+
.command('list')
|
|
18
|
+
.description('List entities')
|
|
19
|
+
.option('--type <type>', 'filter by type')
|
|
20
|
+
.option('--limit <n>', 'max results', '50')
|
|
21
|
+
.action(async (options) => {
|
|
22
|
+
auth.requireAuth();
|
|
23
|
+
const query = { limit: options.limit };
|
|
24
|
+
if (options.type) query.type = options.type;
|
|
25
|
+
const data = await client.get(resolveEndpoint('entity.list').path, { service: resolveEndpoint('entity.list').service, query });
|
|
26
|
+
formatOutput({
|
|
27
|
+
// fleet #392: entities has entity_type/first_seen, not type/created_at.
|
|
28
|
+
columns: ['id', 'entity_type', 'name', 'first_seen'],
|
|
29
|
+
headers: ['ID', 'Type', 'Name', 'First Seen'],
|
|
30
|
+
rows: data.entities || data,
|
|
31
|
+
}, { format: program.opts().output });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
entity
|
|
35
|
+
.command('get')
|
|
36
|
+
.description('Get entity details')
|
|
37
|
+
.argument('<id>', 'entity ID')
|
|
38
|
+
.action(async (id) => {
|
|
39
|
+
auth.requireAuth();
|
|
40
|
+
const data = await client.get(resolveEndpoint('entity.get', { id }).path, { service: resolveEndpoint('entity.get', { id }).service });
|
|
41
|
+
formatDetail(data.entity || data, { format: program.opts().output });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
entity
|
|
45
|
+
.command('create')
|
|
46
|
+
.description('Create an entity')
|
|
47
|
+
.requiredOption('--type <type>', 'entity type')
|
|
48
|
+
.requiredOption('--name <name>', 'entity name')
|
|
49
|
+
.option('--metadata <json>', 'metadata as JSON')
|
|
50
|
+
.action(async (options) => {
|
|
51
|
+
auth.requireAuth();
|
|
52
|
+
const body = { type: options.type, name: options.name };
|
|
53
|
+
if (options.metadata) body.metadata = parseJSON(options.metadata, '--metadata');
|
|
54
|
+
await client.post(resolveEndpoint('entity.create').path, { service: resolveEndpoint('entity.create').service, body });
|
|
55
|
+
printSuccess(`Entity "${options.name}" created.`);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
entity
|
|
59
|
+
.command('delete')
|
|
60
|
+
.description('Delete an entity, soft by default (also removes its relations)')
|
|
61
|
+
.argument('<id>', 'entity ID')
|
|
62
|
+
.option('--hard', 'permanently delete, no soft-delete')
|
|
63
|
+
.action(async (id, options) => {
|
|
64
|
+
auth.requireAuth();
|
|
65
|
+
const query = {};
|
|
66
|
+
if (options.hard) query.hard = 'true';
|
|
67
|
+
await client.delete(resolveEndpoint('entity.delete', { id }).path, { service: resolveEndpoint('entity.delete', { id }).service, query });
|
|
68
|
+
printSuccess('Entity deleted.');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
entity
|
|
72
|
+
.command('search')
|
|
73
|
+
.description('Search entities')
|
|
74
|
+
.requiredOption('--query <q>', 'search query')
|
|
75
|
+
.option('--type <type>', 'filter by type')
|
|
76
|
+
.action(async (options) => {
|
|
77
|
+
auth.requireAuth();
|
|
78
|
+
const query = { query: options.query };
|
|
79
|
+
if (options.type) query.type = options.type;
|
|
80
|
+
const data = await client.get(resolveEndpoint('entity.search.list').path, { service: resolveEndpoint('entity.search.list').service, query });
|
|
81
|
+
formatOutput({
|
|
82
|
+
// fleet #392: search returns entity_type and bm25(...) AS rank.
|
|
83
|
+
columns: ['id', 'entity_type', 'name', 'rank'],
|
|
84
|
+
headers: ['ID', 'Type', 'Name', 'Rank'],
|
|
85
|
+
rows: data.entities || data.results || data,
|
|
86
|
+
}, { format: program.opts().output });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
entity
|
|
90
|
+
.command('relate')
|
|
91
|
+
.description('Create a relation between entities')
|
|
92
|
+
.argument('<fromId>', 'source entity ID')
|
|
93
|
+
.argument('<toId>', 'target entity ID')
|
|
94
|
+
.requiredOption('--type <relType>', 'relation type')
|
|
95
|
+
.action(async (fromId, toId, options) => {
|
|
96
|
+
auth.requireAuth();
|
|
97
|
+
await client.post(resolveEndpoint('entity.relation').path, {
|
|
98
|
+
service: resolveEndpoint('entity.relation').service,
|
|
99
|
+
body: { from_id: fromId, to_id: toId, type: options.type },
|
|
100
|
+
});
|
|
101
|
+
printSuccess('Relation created.');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
entity
|
|
105
|
+
.command('unrelate')
|
|
106
|
+
.description('Delete a relation between entities, soft by default')
|
|
107
|
+
.argument('<relationId>', 'relation ID')
|
|
108
|
+
.option('--hard', 'permanently delete, no soft-delete')
|
|
109
|
+
.action(async (relationId, options) => {
|
|
110
|
+
auth.requireAuth();
|
|
111
|
+
const query = {};
|
|
112
|
+
if (options.hard) query.hard = 'true';
|
|
113
|
+
await client.delete(resolveEndpoint('entity.relation.delete', { id: relationId }).path, {
|
|
114
|
+
service: resolveEndpoint('entity.relation.delete', { id: relationId }).service,
|
|
115
|
+
query,
|
|
116
|
+
});
|
|
117
|
+
printSuccess('Relation deleted.');
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `quox facebook` — Facebook Pages plugin verbs, FB-3 (linkedin CLI parity).
|
|
3
|
+
*
|
|
4
|
+
* quox facebook post "<message>" --page <page_id> [--link <url>] [--image <https url>]
|
|
5
|
+
* quox facebook pages [--limit <n>]
|
|
6
|
+
* quox facebook posts --page <page_id> [--limit <n>]
|
|
7
|
+
* quox facebook insights --page <page_id> [--metrics <a,b,c>] [--period <period>]
|
|
8
|
+
* quox facebook lead-forms --page <page_id>
|
|
9
|
+
* quox facebook leads --form <form_id>
|
|
10
|
+
*
|
|
11
|
+
* The write verb executes the facebook_pages connector tool through the
|
|
12
|
+
* governed collector endpoint (POST /api/v1/tools/execute -> connector
|
|
13
|
+
* bridge). The bridge gates it on requiresApproval: it files an owner
|
|
14
|
+
* approval card and returns immediately with
|
|
15
|
+
* { pending:true, status:'awaiting_approval', approvalId, inbox_item_id }.
|
|
16
|
+
* The actual Facebook Graph API write only happens once the owner approves
|
|
17
|
+
* via the dashboard inbox or `quox inbox respond <id> --action approve`.
|
|
18
|
+
* Reads (pages, posts, insights, lead-forms, leads) execute directly.
|
|
19
|
+
*
|
|
20
|
+
* Pages are addressed by numeric Page ID by design: the CLI has no
|
|
21
|
+
* org-scoped default-page lookup yet, so --page/--form are required on
|
|
22
|
+
* every verb rather than silently guessed.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { formatDetail, printSuccess, printInfo } from '../core/output.js';
|
|
26
|
+
import { resolveEndpoint } from '../api/endpoints.js';
|
|
27
|
+
import { resolveOrgId } from './helpers.js';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Execute one facebook_pages connector tool via the governed collector
|
|
31
|
+
* endpoint. A pending-approval response is a SUCCESS shape here (the card
|
|
32
|
+
* was filed); only a non-pending success:false is an error.
|
|
33
|
+
*/
|
|
34
|
+
async function executeFacebookTool({ client, auth, program }, toolName, input) {
|
|
35
|
+
auth.requireAuth();
|
|
36
|
+
const orgId = resolveOrgId(auth, program);
|
|
37
|
+
const ep = resolveEndpoint('v1.tools.execute');
|
|
38
|
+
const body = { tool_name: toolName, input, org_id: orgId };
|
|
39
|
+
const data = await client.post(ep.path, { service: ep.service, body });
|
|
40
|
+
if (data && data.success === false && !data.pending) {
|
|
41
|
+
throw new Error(`${toolName} via POST ${ep.path} returned success:false: ${data.error || 'unknown error'}`);
|
|
42
|
+
}
|
|
43
|
+
return data;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function printAwaitingApproval(action, data) {
|
|
47
|
+
const itemRef = data.inbox_item_id || data.approvalId || 'unknown';
|
|
48
|
+
printSuccess(`${action} filed for owner approval - inbox item ${itemRef} (awaiting approval). Nothing publishes until an approver says yes.`);
|
|
49
|
+
if (data.inbox_item_id) {
|
|
50
|
+
printInfo(`Review and approve: quox inbox get ${data.inbox_item_id} (or: quox inbox respond ${data.inbox_item_id} --action approve)`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function registerFacebookCommands(program, ctx) {
|
|
55
|
+
const { client, auth } = ctx;
|
|
56
|
+
|
|
57
|
+
const facebook = program
|
|
58
|
+
.command('facebook')
|
|
59
|
+
.description('Facebook Pages publishing (organic): governed, HITL-approved via HITL Inbox');
|
|
60
|
+
|
|
61
|
+
// ── facebook post ────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
facebook
|
|
64
|
+
.command('post')
|
|
65
|
+
.description('Draft a Facebook Page post. Files an owner approval card and returns immediately; never posts directly.')
|
|
66
|
+
.argument('<message>', 'post text')
|
|
67
|
+
.requiredOption('--page <page_id>', 'Facebook Page ID to post to')
|
|
68
|
+
.option('--link <url>', 'attach a link URL')
|
|
69
|
+
.option('--image <url>', 'attach an image from an https URL; the Graph API fetches it server-side')
|
|
70
|
+
.action(async (message, options) => {
|
|
71
|
+
const input = { page_id: options.page, message };
|
|
72
|
+
if (options.link) input.link = options.link;
|
|
73
|
+
if (options.image) input.image_url = options.image;
|
|
74
|
+
|
|
75
|
+
const data = await executeFacebookTool({ client, auth, program }, 'facebook_pages_publish_page_post', input);
|
|
76
|
+
|
|
77
|
+
if (program.opts().output === 'json') {
|
|
78
|
+
formatDetail(data, { format: 'json' });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
printAwaitingApproval('Post', data);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// ── facebook pages (read) ────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
facebook
|
|
87
|
+
.command('pages')
|
|
88
|
+
.description('List the Facebook Pages this token can manage (read-only, no approval needed).')
|
|
89
|
+
.option('--limit <n>', 'max pages to return (max 100)', '25')
|
|
90
|
+
.action(async (options) => {
|
|
91
|
+
const limit = Math.min(parseInt(options.limit, 10) || 25, 100);
|
|
92
|
+
const data = await executeFacebookTool({ client, auth, program }, 'facebook_pages_get_pages', { limit });
|
|
93
|
+
|
|
94
|
+
if (program.opts().output === 'json') {
|
|
95
|
+
formatDetail(data, { format: 'json' });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const pages = data.result || [];
|
|
99
|
+
if (pages.length === 0) {
|
|
100
|
+
printInfo('No pages returned.');
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
for (const p of pages) {
|
|
104
|
+
console.log(`${p.id} ${p.name} ${p.category || ''} followers:${p.followers ?? 'unknown'}`);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ── facebook posts (read) ────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
facebook
|
|
111
|
+
.command('posts')
|
|
112
|
+
.description('List recent posts on a Facebook Page (read-only, no approval needed).')
|
|
113
|
+
.requiredOption('--page <page_id>', 'Facebook Page ID')
|
|
114
|
+
.option('--limit <n>', 'number of posts (max 50)', '10')
|
|
115
|
+
.action(async (options) => {
|
|
116
|
+
const limit = Math.min(parseInt(options.limit, 10) || 10, 50);
|
|
117
|
+
const data = await executeFacebookTool({ client, auth, program }, 'facebook_pages_get_page_posts', {
|
|
118
|
+
page_id: options.page,
|
|
119
|
+
limit,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (program.opts().output === 'json') {
|
|
123
|
+
formatDetail(data, { format: 'json' });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const posts = data.result || [];
|
|
127
|
+
if (posts.length === 0) {
|
|
128
|
+
printInfo('No posts returned.');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
for (const p of posts) {
|
|
132
|
+
console.log(`${p.id} ${p.created_time || ''}`);
|
|
133
|
+
console.log(` ${String(p.message || '').replace(/\n/g, '\n ')}`);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// ── facebook insights (read) ─────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
facebook
|
|
140
|
+
.command('insights')
|
|
141
|
+
.description('Show organic Page Insights (impressions, engagement, fans) for a Facebook Page.')
|
|
142
|
+
.requiredOption('--page <page_id>', 'Facebook Page ID')
|
|
143
|
+
.option('--metrics <metrics>', 'comma-separated metric names')
|
|
144
|
+
.option('--period <period>', 'day|week|days_28|month|lifetime')
|
|
145
|
+
.action(async (options) => {
|
|
146
|
+
const input = { page_id: options.page };
|
|
147
|
+
if (options.metrics) input.metrics = options.metrics;
|
|
148
|
+
if (options.period) input.period = options.period;
|
|
149
|
+
const data = await executeFacebookTool({ client, auth, program }, 'facebook_pages_get_page_insights', input);
|
|
150
|
+
|
|
151
|
+
if (program.opts().output === 'json') {
|
|
152
|
+
formatDetail(data, { format: 'json' });
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const series = data.result || [];
|
|
156
|
+
if (series.length === 0) {
|
|
157
|
+
printInfo('No insights returned.');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
for (const m of series) {
|
|
161
|
+
const latest = (m.values || [])[m.values.length - 1];
|
|
162
|
+
console.log(`${m.title || m.name}: ${latest ? JSON.stringify(latest.value) : 'unknown'}`);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// ── facebook lead-forms (read) ───────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
facebook
|
|
169
|
+
.command('lead-forms')
|
|
170
|
+
.description('List the Lead Ads forms attached to a Facebook Page.')
|
|
171
|
+
.requiredOption('--page <page_id>', 'Facebook Page ID')
|
|
172
|
+
.action(async (options) => {
|
|
173
|
+
const data = await executeFacebookTool({ client, auth, program }, 'facebook_pages_get_lead_forms', { page_id: options.page });
|
|
174
|
+
|
|
175
|
+
if (program.opts().output === 'json') {
|
|
176
|
+
formatDetail(data, { format: 'json' });
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const forms = data.result || [];
|
|
180
|
+
if (forms.length === 0) {
|
|
181
|
+
printInfo('No lead forms returned.');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
for (const f of forms) {
|
|
185
|
+
console.log(`${f.id} ${f.name} status:${f.status || 'unknown'} leads:${f.leads_count ?? 'unknown'}`);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// ── facebook leads (read) ────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
facebook
|
|
192
|
+
.command('leads')
|
|
193
|
+
.description('Retrieve submitted leads for a Lead Ads form.')
|
|
194
|
+
.requiredOption('--form <form_id>', 'Lead form ID (from lead-forms)')
|
|
195
|
+
.action(async (options) => {
|
|
196
|
+
const data = await executeFacebookTool({ client, auth, program }, 'facebook_pages_get_leads', { form_id: options.form });
|
|
197
|
+
|
|
198
|
+
if (program.opts().output === 'json') {
|
|
199
|
+
formatDetail(data, { format: 'json' });
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const leads = data.result || [];
|
|
203
|
+
if (leads.length === 0) {
|
|
204
|
+
printInfo('No leads returned.');
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
for (const l of leads) {
|
|
208
|
+
console.log(`${l.id} ${l.created_time || ''}`);
|
|
209
|
+
console.log(` ${JSON.stringify(l.fields || {})}`);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `quox fetch allow` — the URL prefixes this org's agents may GET through the
|
|
3
|
+
* governed web_fetch_allowlisted tool (quox#1171). Default-deny: nothing is
|
|
4
|
+
* fetchable until an org admin adds a prefix. Every call goes through the
|
|
5
|
+
* collector; adding and removing need a human org admin (the server refuses
|
|
6
|
+
* agent tokens).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { resolveEndpoint } from '../api/endpoints.js';
|
|
10
|
+
|
|
11
|
+
async function call(client, id, params = {}, body) {
|
|
12
|
+
const ep = resolveEndpoint(id, params);
|
|
13
|
+
const opts = { service: ep.service };
|
|
14
|
+
if (body) opts.body = body;
|
|
15
|
+
switch (ep.method) {
|
|
16
|
+
case 'POST': return client.post(ep.path, opts);
|
|
17
|
+
case 'DELETE': return client.delete(ep.path, opts);
|
|
18
|
+
default: return client.get(ep.path, opts);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function registerFetchAllowlistCommands(program, ctx) {
|
|
23
|
+
const { client } = ctx;
|
|
24
|
+
|
|
25
|
+
const f = program
|
|
26
|
+
.command('fetch')
|
|
27
|
+
.description('Governed agent fetch: which URLs your agents may GET');
|
|
28
|
+
|
|
29
|
+
const al = f.command('allow')
|
|
30
|
+
.description('The URL prefixes this org allows agents to fetch (default-deny)');
|
|
31
|
+
|
|
32
|
+
al.command('list', { isDefault: true })
|
|
33
|
+
.description('Show the allowed URL prefixes for this org')
|
|
34
|
+
.action(async () => {
|
|
35
|
+
const d = await call(client, 'fetchAllowlist.list');
|
|
36
|
+
if (d.prefixes?.length) {
|
|
37
|
+
process.stdout.write('allowed prefixes:\n');
|
|
38
|
+
for (const e of d.prefixes) process.stdout.write(` ${e.url_prefix}${e.note ? ` (${e.note})` : ''}\n`);
|
|
39
|
+
} else {
|
|
40
|
+
process.stdout.write('none allowed yet: agents cannot fetch anything. Add one with `quox fetch allow add <url-prefix>`\n');
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
al.command('add <url-prefix>')
|
|
45
|
+
.description('Allow agents to GET URLs under a prefix, e.g. https://status.example.com/api/')
|
|
46
|
+
.option('--note <text>', 'why it is allowed, shown alongside it later')
|
|
47
|
+
.action(async (urlPrefix, opts) => {
|
|
48
|
+
try {
|
|
49
|
+
const d = await call(client, 'fetchAllowlist.add', {}, { url_prefix: urlPrefix, note: opts.note });
|
|
50
|
+
process.stdout.write(`allowed ${d.entry.url_prefix}\n`);
|
|
51
|
+
process.stdout.write(` ${d.prefixes.length} prefix(es) now allowed for this org\n`);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (err?.status === 400 && err?.message) {
|
|
54
|
+
process.stderr.write(`Not added: ${err.message}\n`);
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
al.command('remove <url-prefix>')
|
|
63
|
+
.description('Stop allowing a URL prefix')
|
|
64
|
+
.action(async (urlPrefix) => {
|
|
65
|
+
const d = await call(client, 'fetchAllowlist.remove', { prefix: urlPrefix });
|
|
66
|
+
if (d.removed) process.stdout.write(`removed ${urlPrefix}\n`);
|
|
67
|
+
else process.stdout.write(`nothing to remove: ${urlPrefix} was not allowed for this org\n`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default registerFetchAllowlistCommands;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File commands: upload, download, list, delete
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createWriteStream, readFileSync } from 'node:fs';
|
|
6
|
+
import { basename } from 'node:path';
|
|
7
|
+
import { formatOutput, formatDetail, printSuccess } from '../core/output.js';
|
|
8
|
+
import { resolveEndpoint } from '../api/endpoints.js';
|
|
9
|
+
|
|
10
|
+
export function registerFileCommands(program, ctx) {
|
|
11
|
+
const { client, auth } = ctx;
|
|
12
|
+
|
|
13
|
+
const file = program
|
|
14
|
+
.command('file')
|
|
15
|
+
.description('Manage files');
|
|
16
|
+
|
|
17
|
+
file
|
|
18
|
+
.command('list')
|
|
19
|
+
.description('List files')
|
|
20
|
+
.action(async () => {
|
|
21
|
+
auth.requireAuth();
|
|
22
|
+
const data = await client.get(resolveEndpoint('files.list').path, { service: resolveEndpoint('files.list').service });
|
|
23
|
+
formatOutput({
|
|
24
|
+
columns: ['id', 'name', 'size', 'type', 'created_at'],
|
|
25
|
+
headers: ['ID', 'Name', 'Size', 'Type', 'Created'],
|
|
26
|
+
rows: data.files || data,
|
|
27
|
+
}, { format: program.opts().output });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
file
|
|
31
|
+
.command('upload')
|
|
32
|
+
.description('Upload a file')
|
|
33
|
+
.argument('<path>', 'local file path')
|
|
34
|
+
.option('--name <name>', 'override filename')
|
|
35
|
+
.action(async (filePath, options) => {
|
|
36
|
+
auth.requireAuth();
|
|
37
|
+
// fleet #381: node:buffer exports Blob but has NEVER exported
|
|
38
|
+
// FormData (it's a global from undici on Node >= 18). Destructuring
|
|
39
|
+
// it here yielded undefined and shadowed the working global, so
|
|
40
|
+
// `new FormData()` threw "FormData is not a constructor" — this
|
|
41
|
+
// command had never once worked.
|
|
42
|
+
const { Blob } = await import('node:buffer');
|
|
43
|
+
const { readFile } = await import('node:fs/promises');
|
|
44
|
+
|
|
45
|
+
const content = await readFile(filePath);
|
|
46
|
+
const fileName = options.name || basename(filePath);
|
|
47
|
+
|
|
48
|
+
const formData = new FormData();
|
|
49
|
+
// Field name must be 'files' (plural): the files service parses with
|
|
50
|
+
// multer's `upload.array('files', 100)`. Sending 'file' leaves
|
|
51
|
+
// req.files undefined, and the route's `for (const file of req.files)`
|
|
52
|
+
// then throws — measured against the live service as a 500 with zero
|
|
53
|
+
// files stored, vs 200 + 1 stored for 'files'. So this is a genuine
|
|
54
|
+
// second bug: fixing the FormData crash alone would have swapped a
|
|
55
|
+
// client-side crash for a server-side 500, still with no upload.
|
|
56
|
+
formData.append('files', new Blob([content]), fileName);
|
|
57
|
+
|
|
58
|
+
const data = await client.post(resolveEndpoint('files.upload').path, {
|
|
59
|
+
service: resolveEndpoint('files.upload').service,
|
|
60
|
+
body: formData,
|
|
61
|
+
timeout: 120000,
|
|
62
|
+
});
|
|
63
|
+
// The route responds {success, files: [{id, originalName, ...}]}.
|
|
64
|
+
// Report the id the server actually stored, and refuse to claim
|
|
65
|
+
// success when it stored nothing — the old fallback to the local
|
|
66
|
+
// fileName would print "File uploaded" for an empty result set.
|
|
67
|
+
const uploaded = data.files?.[0];
|
|
68
|
+
if (!uploaded?.id) {
|
|
69
|
+
throw new Error(`Upload failed: server accepted the request but stored no file (got: ${JSON.stringify(data).slice(0, 120)})`);
|
|
70
|
+
}
|
|
71
|
+
printSuccess(`File uploaded: ${uploaded.id}${uploaded.originalName ? ` (${uploaded.originalName})` : ''}`);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
file
|
|
75
|
+
.command('download')
|
|
76
|
+
.description('Download a file')
|
|
77
|
+
.argument('<fileId>', 'file ID')
|
|
78
|
+
// fleet #384: this used to be `--output <path>`, which collided with the
|
|
79
|
+
// global `-o, --output <format>` (table|json|csv|quiet) declared in
|
|
80
|
+
// bin/quox.js. Commander binds the value to the PARENT option, so the
|
|
81
|
+
// subcommand's options.output was always undefined and the file-write
|
|
82
|
+
// branch below was unreachable — every download fell through to stdout.
|
|
83
|
+
// Renamed rather than made clever: guessing whether an --output value is
|
|
84
|
+
// "a format" or "a path" is exactly the kind of heuristic that breaks
|
|
85
|
+
// later. --output never worked here, so nothing can depend on it.
|
|
86
|
+
.option('--dest <path>', 'write the file to this path (the global -o/--output sets output FORMAT, not a path)')
|
|
87
|
+
.action(async (fileId, options) => {
|
|
88
|
+
auth.requireAuth();
|
|
89
|
+
const res = await client.get(resolveEndpoint('file.get', { fileId }).path, { service: resolveEndpoint('file.get', { fileId }).service, raw: true });
|
|
90
|
+
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
throw new Error(`Download failed: ${res.status}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const { pipeline } = await import('node:stream/promises');
|
|
96
|
+
|
|
97
|
+
if (options.dest) {
|
|
98
|
+
const writer = createWriteStream(options.dest);
|
|
99
|
+
await pipeline(res.body, writer);
|
|
100
|
+
printSuccess(`Downloaded to ${options.dest}`);
|
|
101
|
+
} else {
|
|
102
|
+
// Stream the raw body. This used to be `res.text()`, which UTF-8
|
|
103
|
+
// decodes: any byte sequence that isn't valid UTF-8 became U+FFFD,
|
|
104
|
+
// so a piped binary came out corrupted AND larger than the original
|
|
105
|
+
// (measured 1.77x on a PNG). Piping the body preserves bytes exactly.
|
|
106
|
+
await pipeline(res.body, process.stdout);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
file
|
|
111
|
+
.command('delete')
|
|
112
|
+
.description('Delete a file')
|
|
113
|
+
.argument('<fileId>', 'file ID')
|
|
114
|
+
.action(async (fileId) => {
|
|
115
|
+
auth.requireAuth();
|
|
116
|
+
await client.delete(resolveEndpoint('file.delete', { fileId }).path, { service: resolveEndpoint('file.delete', { fileId }).service });
|
|
117
|
+
printSuccess('File deleted.');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
file
|
|
121
|
+
.command('stats')
|
|
122
|
+
.description('Show file storage stats')
|
|
123
|
+
.action(async () => {
|
|
124
|
+
auth.requireAuth();
|
|
125
|
+
const data = await client.get(resolveEndpoint('files.stats').path, { service: resolveEndpoint('files.stats').service });
|
|
126
|
+
formatDetail(data.stats || data, { format: program.opts().output, title: 'File Storage Stats' });
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// F01 A': legacy files with no proven owner are hidden from every org until a human instance admin assigns them.
|
|
130
|
+
file
|
|
131
|
+
.command('quarantine')
|
|
132
|
+
.description('List files with no proven owner org (instance admin only)')
|
|
133
|
+
.action(async () => {
|
|
134
|
+
auth.requireAuth();
|
|
135
|
+
const ep = resolveEndpoint('files.quarantine.list');
|
|
136
|
+
const data = await client.get(ep.path, { service: ep.service });
|
|
137
|
+
formatOutput({ columns: ['id', 'name', 'size'], headers: ['ID', 'Name', 'Size'], rows: data.files || [] }, { format: program.opts().output });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
file
|
|
141
|
+
.command('assign')
|
|
142
|
+
.description('Assign a quarantined file to an org (instance admin only; witnessed): <fileId> --org <orgId>, or --from-report <dry-run.json>')
|
|
143
|
+
.argument('[fileId]', 'quarantined file id')
|
|
144
|
+
.option('--org <orgId>', 'org that owns the file')
|
|
145
|
+
.option('--from-report <path>', 'an owner-approved backfill dry-run report: assigns only rows with a derived owner')
|
|
146
|
+
.action(async (fileId, options) => {
|
|
147
|
+
auth.requireAuth();
|
|
148
|
+
let assignments;
|
|
149
|
+
if (options.fromReport) {
|
|
150
|
+
const report = JSON.parse(readFileSync(options.fromReport, 'utf8'));
|
|
151
|
+
assignments = (report.rows || []).filter((r) => r && r.id && r.orgId).map((r) => ({ file_id: r.id, org_id: r.orgId }));
|
|
152
|
+
if (!assignments.length) throw new Error('The report has no rows with a derived owner; nothing to assign.');
|
|
153
|
+
} else if (fileId) {
|
|
154
|
+
if (!options.org) throw new Error('Give the owning org: quox file assign <fileId> --org <orgId>');
|
|
155
|
+
assignments = [{ file_id: fileId, org_id: options.org }];
|
|
156
|
+
} else {
|
|
157
|
+
throw new Error('Give <fileId> --org <orgId>, or --from-report <dry-run.json>');
|
|
158
|
+
}
|
|
159
|
+
const ep = resolveEndpoint('files.quarantine.assign');
|
|
160
|
+
const data = await client.post(ep.path, { service: ep.service, body: { assignments } });
|
|
161
|
+
formatOutput({ columns: ['file_id', 'org_id', 'ok', 'error'], headers: ['File', 'Org', 'Assigned', 'Error'], rows: data.results || [] }, { format: program.opts().output });
|
|
162
|
+
const failed = (data.results || []).filter((r) => !r.ok).length;
|
|
163
|
+
if (failed) { process.exitCode = 1; } else { printSuccess(`Assigned ${assignments.length} file(s).`); }
|
|
164
|
+
});
|
|
165
|
+
}
|