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,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QUOXON-STEPUP: the human step-up PIN, TUI side.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: a typed "y" cannot prove a person typed it (an agent can drive this terminal, and some
|
|
5
|
+
* hold a human profile's CLI login). The SERVER decides what needs a person (agent-raised or high-risk
|
|
6
|
+
* approvals, policy overrides) and refuses with 403 step_up_required. This module only collects a PIN,
|
|
7
|
+
* trades it for a short-lived step-up token (POST /api/me/step-up) and carries that token on the retry.
|
|
8
|
+
* Nothing here is a security boundary on its own: it is the front half of a server-enforced check.
|
|
9
|
+
*
|
|
10
|
+
* The PIN rules, all deliberate:
|
|
11
|
+
* - the digits live in a closure box; they never enter dispatch, the composer buffer, history, the
|
|
12
|
+
* undo/redo stacks, the dev transcript, the observer, or any log; the box is zeroed on every exit
|
|
13
|
+
* - the card renders a COUNT of filled dots, never a digit
|
|
14
|
+
* - 3 wrong tries lock PIN entry for this window (the server also rate-limits per user and locks)
|
|
15
|
+
* - agent logins never get a PIN field: they are told a person must approve
|
|
16
|
+
*/
|
|
17
|
+
import { graphemes } from '../textBuffer.js';
|
|
18
|
+
|
|
19
|
+
export const WINDOW_MS = 10 * 60 * 1000;
|
|
20
|
+
export const MAX_TRIES = 3;
|
|
21
|
+
export const PIN_LEN = 6;
|
|
22
|
+
export const BEAT_MS = 300; // cosmetic floor for the "verifying" beat; the request itself is real
|
|
23
|
+
export const RESULT_HOLD_MS = 900; // how long AUTHORISED / DENIED stays on screen
|
|
24
|
+
export const LOCK_MS = WINDOW_MS; // local lock for this window after MAX_TRIES wrong tries
|
|
25
|
+
export const NEEDS_PERSON = 'needs a person: approve from your phone or a human session';
|
|
26
|
+
export const NO_PIN_LINE = 'Set a PIN in the dashboard: Settings > Account > Approval PIN';
|
|
27
|
+
|
|
28
|
+
/** The PIN digits. Closure only: no accessor but take(), nothing serialisable. */
|
|
29
|
+
export function createPinBox() {
|
|
30
|
+
let d = '';
|
|
31
|
+
return {
|
|
32
|
+
push(t) { for (const ch of String(t || '')) { if (d.length >= PIN_LEN) break; if (ch >= '0' && ch <= '9') d += ch; } return d.length; },
|
|
33
|
+
backspace() { d = d.slice(0, -1); return d.length; },
|
|
34
|
+
clear() { d = ''; },
|
|
35
|
+
count: () => d.length,
|
|
36
|
+
take() { const out = d; d = ''; return out; },
|
|
37
|
+
toJSON: () => '[pin]',
|
|
38
|
+
toString: () => '[pin]',
|
|
39
|
+
[Symbol.for('nodejs.util.inspect.custom')]: () => '[pin]'
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const mmss = (ms) => { const s = Math.max(0, Math.ceil(ms / 1000)); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`; };
|
|
44
|
+
|
|
45
|
+
/** Terminal cell width: emoji-presentation glyphs take two cells. */
|
|
46
|
+
export function cellWidth(s) {
|
|
47
|
+
let w = 0;
|
|
48
|
+
for (const g of graphemes(String(s ?? ''))) w += /\p{Emoji_Presentation}/u.test(g.text) ? 2 : 1;
|
|
49
|
+
return w;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
/** One-line collapse under this many columns. */
|
|
55
|
+
export const NARROW_COLS = 50;
|
|
56
|
+
export const CARD_W = 58;
|
|
57
|
+
/** Below this many columns the card is drawn still: no pulse, no shimmer. */
|
|
58
|
+
export const ANIM_MIN_COLS = 60;
|
|
59
|
+
|
|
60
|
+
/** Word-wrap to `w` cells; a word longer than a line is split, nothing is ever dropped. */
|
|
61
|
+
export function wrapCells(text, w) {
|
|
62
|
+
const out = []; let cur = '';
|
|
63
|
+
const push = () => { if (cur) out.push(cur); cur = ''; };
|
|
64
|
+
for (const word of String(text ?? '').split(/\s+/).filter(Boolean)) {
|
|
65
|
+
let rest = word;
|
|
66
|
+
while (cellWidth(rest) > w) { // hard-split an over-long word
|
|
67
|
+
push(); let part = ''; let n = 0;
|
|
68
|
+
for (const g of graphemes(rest)) { const gw = cellWidth(g.text); if (n + gw > w) break; part += g.text; n += gw; }
|
|
69
|
+
out.push(part); rest = rest.slice(part.length);
|
|
70
|
+
}
|
|
71
|
+
if (!rest) continue;
|
|
72
|
+
if (cur && cellWidth(cur) + 1 + cellWidth(rest) > w) push();
|
|
73
|
+
cur = cur ? `${cur} ${rest}` : rest;
|
|
74
|
+
}
|
|
75
|
+
push();
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Pure: the card as lines of [{ text, role? }] segments. roles: gold (frame, title, filled slots, countdown),
|
|
81
|
+
* prose (ink, empty slots, subject), ok, error, key (the per-digit pulse and the verifying sweep). Never grey.
|
|
82
|
+
* `view` carries counts only, never digits. Optional animation inputs, both plain numbers: `pulse` (slot index
|
|
83
|
+
* just filled) and `tick` (shimmer frame). Without them the card is static, which is what narrow, slow and
|
|
84
|
+
* NO_COLOR terminals get.
|
|
85
|
+
*/
|
|
86
|
+
export function cardLines(view, cols) {
|
|
87
|
+
const { phase, filled = 0, subject = {}, tries = MAX_TRIES, offset = 0, until = 0, now = 0, error = null, pulse = -1, tick = null } = view;
|
|
88
|
+
const shown = phase === 'verifying' || phase === 'ok' ? PIN_LEN : filled; // the box was taken at submit: 6 were typed
|
|
89
|
+
const dots = Array.from({ length: PIN_LEN }, (_, i) => (i < shown ? '●' : '○'));
|
|
90
|
+
const shift = offset ? 1 : 0; // the "tiny shake": one redraw one cell to the right
|
|
91
|
+
const verifyText = '▰▰▰▰ verifying';
|
|
92
|
+
const status = (() => {
|
|
93
|
+
switch (phase) {
|
|
94
|
+
case 'verifying': {
|
|
95
|
+
if (tick == null) return [{ text: verifyText, role: 'gold' }];
|
|
96
|
+
const chars = [...verifyText]; const at = tick % (chars.length + 3);
|
|
97
|
+
return chars.map((ch, i) => ({ text: ch, role: i >= at - 2 && i <= at ? 'text' : 'gold' }));
|
|
98
|
+
}
|
|
99
|
+
case 'ok': return [{ text: '✔ AUTHORISED', role: 'ok' }, { text: ` · human present for ${mmss(until - now)}`, role: 'gold' }];
|
|
100
|
+
case 'denied': return [{ text: `✖ DENIED · ${tries} ${tries === 1 ? 'try' : 'tries'} left`, role: 'error' }];
|
|
101
|
+
case 'locked': return [{ text: '⛔ LOCKED · use your phone or a human session', role: 'error' }];
|
|
102
|
+
case 'nopin': return [{ text: NO_PIN_LINE, role: 'error' }];
|
|
103
|
+
case 'agent': return [{ text: NEEDS_PERSON, role: 'error' }];
|
|
104
|
+
default: return null;
|
|
105
|
+
}
|
|
106
|
+
})();
|
|
107
|
+
const pinSeg = [{ text: 'PIN ', role: 'prose' }, ...dots.flatMap((d, i) => [{ text: d, role: d === '●' ? 'gold' : 'prose' }, { text: i < PIN_LEN - 1 ? ' ' : '', role: 'prose' }])];
|
|
108
|
+
const segW = (segs) => segs.reduce((n, s) => n + cellWidth(s.text), 0);
|
|
109
|
+
const riskSeg = (r) => (r === 'high' || r === 'urgent' ? { text: String(r), role: 'error' } : { text: String(r), role: 'prose' });
|
|
110
|
+
|
|
111
|
+
if (cols < NARROW_COLS) { // one line: no frame, nothing lost that matters
|
|
112
|
+
const head = [{ text: '🔐 ', role: 'gold' }];
|
|
113
|
+
if (status) return [[...head, ...status]];
|
|
114
|
+
return [[...head, ...pinSeg, { text: ' · Esc cancels', role: 'prose' }]];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const W = Math.min(CARD_W, cols);
|
|
118
|
+
const inner = W - 2;
|
|
119
|
+
const row = (segs, sh = 0) => { // │ + content centred + │; `sh` nudges it right (the shake)
|
|
120
|
+
const used = segW(segs);
|
|
121
|
+
const lp = Math.max(0, Math.floor((inner - used) / 2) + sh);
|
|
122
|
+
return [{ text: '│', role: 'gold' }, { text: ' '.repeat(lp), role: 'prose' }, ...segs, { text: ' '.repeat(Math.max(0, inner - used - lp)), role: 'prose' }, { text: '│', role: 'gold' }];
|
|
123
|
+
};
|
|
124
|
+
const blank = () => row([{ text: '', role: 'prose' }]);
|
|
125
|
+
const title = view.title || 'AUTHORISATION REQUIRED';
|
|
126
|
+
const topHead = `╭─ 🔐 ${title} `;
|
|
127
|
+
const top = [{ text: topHead + '─'.repeat(Math.max(1, W - 1 - cellWidth(topHead))) + '╮', role: 'gold' }];
|
|
128
|
+
const footText = phase === 'locked' || phase === 'agent' || phase === 'nopin' ? ' esc closes ' : ' a person must confirm this · Esc cancels ';
|
|
129
|
+
const foot = [{ text: '╰─' + footText + '─'.repeat(Math.max(1, W - 3 - cellWidth(footText))) + '╯', role: 'gold' }];
|
|
130
|
+
|
|
131
|
+
// The six PIN slots: three rows tall, heavy gold border once filled, a teal flash on the digit just typed,
|
|
132
|
+
// a teal sweep while verifying, green ticks when authorised, red when denied.
|
|
133
|
+
const slotRows = () => {
|
|
134
|
+
const rows = [[], [], []];
|
|
135
|
+
for (let i = 0; i < PIN_LEN; i++) {
|
|
136
|
+
const full = i < shown;
|
|
137
|
+
let role = full ? 'gold' : 'prose'; let mid = full ? '◉' : ' ';
|
|
138
|
+
if (phase === 'input' && i === pulse && full) role = 'key';
|
|
139
|
+
if (phase === 'verifying' && tick != null && i === tick % (PIN_LEN + 2)) role = 'key';
|
|
140
|
+
if (phase === 'ok') { role = 'ok'; mid = '✔'; }
|
|
141
|
+
if (phase === 'denied') { role = 'error'; mid = '✖'; }
|
|
142
|
+
const heavy = full || phase === 'denied';
|
|
143
|
+
const [t, m, b] = heavy ? ['┏━━━┓', `┃ ${mid} ┃`, '┗━━━┛'] : ['╭───╮', '│ │', '╰───╯'];
|
|
144
|
+
const gap = i < PIN_LEN - 1 ? ' ' : '';
|
|
145
|
+
rows[0].push({ text: t + gap, role }); rows[1].push({ text: m + gap, role }); rows[2].push({ text: b + gap, role });
|
|
146
|
+
}
|
|
147
|
+
return rows.map((r) => row(r, shift));
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const out = [top, blank()];
|
|
151
|
+
const innerW = inner - 4;
|
|
152
|
+
for (const l of wrapCells(subject.line1 || '', innerW)) out.push(row([{ text: l, role: 'prose' }]));
|
|
153
|
+
if (subject.raisedBy || subject.risk) {
|
|
154
|
+
const segs = [];
|
|
155
|
+
if (subject.raisedBy) segs.push({ text: `raised by ${subject.raisedBy}`, role: 'prose' });
|
|
156
|
+
if (subject.risk) segs.push({ text: subject.raisedBy ? ' · risk ' : 'risk ', role: 'prose' }, riskSeg(subject.risk));
|
|
157
|
+
if (segW(segs) <= innerW) out.push(row(segs));
|
|
158
|
+
else { // too wide for one row: raised-by wraps, risk gets its own row
|
|
159
|
+
if (subject.raisedBy) for (const l of wrapCells(`raised by ${subject.raisedBy}`, innerW)) out.push(row([{ text: l, role: 'prose' }]));
|
|
160
|
+
if (subject.risk) out.push(row([{ text: 'risk ', role: 'prose' }, riskSeg(subject.risk)]));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
out.push(blank());
|
|
164
|
+
if (phase === 'input' || phase === 'denied' || phase === 'verifying') {
|
|
165
|
+
out.push(...slotRows());
|
|
166
|
+
out.push(row([...pinSeg], shift));
|
|
167
|
+
out.push(blank());
|
|
168
|
+
}
|
|
169
|
+
if (phase === 'ok') { out.push(...slotRows()); out.push(blank()); }
|
|
170
|
+
if (phase !== 'input') out.push(row(status, shift));
|
|
171
|
+
if (phase === 'ok') out.push(blank());
|
|
172
|
+
if (error) for (const l of wrapCells(error, innerW)) out.push(row([{ text: l, role: 'error' }]));
|
|
173
|
+
out.push(foot);
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The controller (same shape as the /login and /connect controllers: isOpen / view / onEvent).
|
|
179
|
+
* deps.api { stepUpVerify(pin, {orgId, scopes}) } -> { step_up_token, expires_at, scopes }
|
|
180
|
+
* deps.agentLogin this window is an agent login: no PIN field, ever
|
|
181
|
+
* deps.refresh re-render
|
|
182
|
+
* deps.note (text, tone) -> a line to the transcript pane (never carries digits)
|
|
183
|
+
* deps.emit dispatch({ type: 'stepup.result', ok }) (sound cue; carries no digits)
|
|
184
|
+
*/
|
|
185
|
+
export function createStepUp({ api, agentLogin = false, onAgentLogin = () => {}, refresh = () => {}, note = () => {}, emit = () => {}, settingsUrl = () => null, now = () => Date.now(), sleep = (ms) => new Promise((r) => setTimeout(r, ms)) } = {}) {
|
|
186
|
+
const box = createPinBox();
|
|
187
|
+
let card = null;
|
|
188
|
+
let resolveCard = null;
|
|
189
|
+
let tries = MAX_TRIES;
|
|
190
|
+
let lockedUntil = 0;
|
|
191
|
+
let busy = false;
|
|
192
|
+
const tokens = new Map(); // scope -> { token, until }
|
|
193
|
+
let presentUntil = 0; // set only by /authenticate: the "human present" window shown in the status bar
|
|
194
|
+
|
|
195
|
+
const finish = (result) => { box.clear(); const r = resolveCard; card = null; resolveCard = null; busy = false; refresh(); r?.(result); };
|
|
196
|
+
const live = (scope) => { const t = tokens.get(scope); if (t && t.until > now()) return t; tokens.delete(scope); return null; };
|
|
197
|
+
|
|
198
|
+
async function submit() {
|
|
199
|
+
if (busy || !card) return;
|
|
200
|
+
try { await submitInner(); } finally { busy = false; }
|
|
201
|
+
}
|
|
202
|
+
async function submitInner() {
|
|
203
|
+
const pin = box.take(); // out of the box in one move; the local `pin` is dropped when this function returns
|
|
204
|
+
busy = true; card.phase = 'verifying'; card.filled = 0; refresh();
|
|
205
|
+
const t0 = now();
|
|
206
|
+
let res = null; let err = null;
|
|
207
|
+
try { res = await api.stepUpVerify(pin, { orgId: card.orgId, scopes: card.scopes }); }
|
|
208
|
+
catch (e) { err = e; }
|
|
209
|
+
const waited = now() - t0;
|
|
210
|
+
if (waited < BEAT_MS) await sleep(BEAT_MS - waited); // cosmetic floor only; a slow call is never padded
|
|
211
|
+
if (!card) return;
|
|
212
|
+
if (res?.step_up_token) {
|
|
213
|
+
const until = now() + WINDOW_MS;
|
|
214
|
+
for (const s of res.scopes || card.scopes) tokens.set(s, { token: res.step_up_token, until });
|
|
215
|
+
if (card.window) presentUntil = until;
|
|
216
|
+
card.phase = 'ok'; card.until = until; card.now = now(); refresh(); emit({ type: 'stepup.result', ok: true });
|
|
217
|
+
await sleep(RESULT_HOLD_MS);
|
|
218
|
+
return finish({ ok: true, token: res.step_up_token, scopes: res.scopes || card.scopes });
|
|
219
|
+
}
|
|
220
|
+
const status = err?.status; const code = err?.body?.error || err?.code;
|
|
221
|
+
if (status === 409 || code === 'no_pin_set') { card.phase = 'nopin'; Promise.resolve(settingsUrl()).then((u) => note(u ? `${NO_PIN_LINE} (${u})` : NO_PIN_LINE, 'warn')).catch(() => {}); refresh(); emit({ type: 'stepup.result', ok: false }); return; }
|
|
222
|
+
if (status === 423 || code === 'pin_locked') { lockedUntil = now() + LOCK_MS; card.phase = 'locked'; refresh(); emit({ type: 'stepup.result', ok: false }); return; }
|
|
223
|
+
if (status === 401 || code === 'wrong_pin') {
|
|
224
|
+
tries = Number.isFinite(err?.body?.tries_left) ? Math.min(tries - 1, err.body.tries_left) : tries - 1;
|
|
225
|
+
emit({ type: 'stepup.result', ok: false });
|
|
226
|
+
if (tries <= 0) { lockedUntil = now() + LOCK_MS; card.phase = 'locked'; refresh(); return; }
|
|
227
|
+
card.phase = 'denied'; card.tries = tries; card.offset = 1; refresh();
|
|
228
|
+
await sleep(80); if (!card) return; card.offset = 0; refresh();
|
|
229
|
+
await sleep(RESULT_HOLD_MS - 80); if (!card) return;
|
|
230
|
+
card.phase = 'input'; card.error = null; refresh();
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (status === 403 && (code === 'step_up_human_only')) { card.phase = 'agent'; refresh(); return; }
|
|
234
|
+
card.phase = 'input'; card.error = (err?.message || 'could not verify').replace(/\s+/g, ' ').slice(0, 80); refresh();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
/** Live token for a scope, or null. */
|
|
239
|
+
token: (scope) => live(scope)?.token || null,
|
|
240
|
+
headers: (scope) => { const t = live(scope); return t ? { 'x-step-up-token': t.token } : {}; },
|
|
241
|
+
presentMs: () => Math.max(0, presentUntil - now()),
|
|
242
|
+
drop(scope) { tokens.delete(scope); },
|
|
243
|
+
clear() { tokens.clear(); presentUntil = 0; box.clear(); },
|
|
244
|
+
isOpen: () => Boolean(card),
|
|
245
|
+
view() { return card ? { ...card, filled: box.count(), tries, now: now() } : null; },
|
|
246
|
+
/**
|
|
247
|
+
* Ask for a person. Resolves { ok, token?, reason? } and never throws. Reuses a live token for the scope.
|
|
248
|
+
* opts: { scope: 'approve'|'override', scopes?, orgId?, subject?, window?: true (the /authenticate window) }
|
|
249
|
+
*/
|
|
250
|
+
request({ scope = 'approve', scopes = null, orgId = null, subject = {}, window: asWindow = false, title = null } = {}) {
|
|
251
|
+
if (typeof agentLogin === 'function' ? agentLogin() : agentLogin) { note(`This window is an agent login: ${NEEDS_PERSON}.`, 'warn'); onAgentLogin(); return Promise.resolve({ ok: false, reason: 'agent_login' }); }
|
|
252
|
+
if (!asWindow && live(scope)) return Promise.resolve({ ok: true, token: live(scope).token, scopes: [scope], reused: true });
|
|
253
|
+
if (card) return Promise.resolve({ ok: false, reason: 'busy' });
|
|
254
|
+
if (lockedUntil > now()) { note(`⛔ LOCKED · PIN entry is locked for ${mmss(lockedUntil - now())}. Use your phone or a human session.`, 'error'); return Promise.resolve({ ok: false, reason: 'locked' }); }
|
|
255
|
+
tries = MAX_TRIES;
|
|
256
|
+
box.clear();
|
|
257
|
+
card = { phase: 'input', subject, title, scope, scopes: scopes || (asWindow ? ['approve', 'override'] : [scope]), orgId, window: asWindow, error: null, offset: 0 };
|
|
258
|
+
refresh();
|
|
259
|
+
return new Promise((r) => { resolveCard = r; });
|
|
260
|
+
},
|
|
261
|
+
onEvent(e) {
|
|
262
|
+
if (!card) return false;
|
|
263
|
+
if (busy || card.phase === 'verifying' || card.phase === 'ok' || card.phase === 'denied') return true; // swallowed while the call is in flight and during the DENIED beat
|
|
264
|
+
const ph = card.phase;
|
|
265
|
+
if (ph === 'locked' || ph === 'nopin' || ph === 'agent') { if (e.type === 'key' && (e.name === 'escape' || e.name === 'enter' || e.name === 'ctrl-c')) finish({ ok: false, reason: ph }); return true; }
|
|
266
|
+
if (e.type === 'paste' || e.type === 'text') { box.push(e.text); if (box.count() >= PIN_LEN) submit(); else refresh(); return true; }
|
|
267
|
+
if (e.type !== 'key') return true;
|
|
268
|
+
if (e.name === 'escape' || e.name === 'ctrl-c') { finish({ ok: false, reason: 'cancelled' }); return true; }
|
|
269
|
+
if (e.name === 'backspace') { box.backspace(); refresh(); return true; }
|
|
270
|
+
if (e.name === 'kill-line' || e.name === 'delete-word') { box.clear(); refresh(); return true; }
|
|
271
|
+
return true; // every other key is swallowed: nothing falls through to the composer
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ink face of the step-up PIN card and the "human present" bar. All text comes from cardLines() (pure,
|
|
3
|
+
* counts only, never a digit); this file only paints roles. Roles: gold frame/title/filled dots/countdown,
|
|
4
|
+
* prose for ink, ok green, error red. Never grey.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { Box, Text } from 'ink';
|
|
8
|
+
import { c, mono } from '../theme.js';
|
|
9
|
+
import { cardLines, mmss, ANIM_MIN_COLS } from './stepUp.js';
|
|
10
|
+
|
|
11
|
+
const h = React.createElement;
|
|
12
|
+
|
|
13
|
+
/** Animate only on a roomy, coloured terminal that is keeping up. */
|
|
14
|
+
export const animationOn = (cols, slow = false) => cols >= ANIM_MIN_COLS && !slow && !mono() && process.env.TERM !== 'dumb';
|
|
15
|
+
const FRAME_MS = 90;
|
|
16
|
+
|
|
17
|
+
export function StepUpCard({ view, cols }) {
|
|
18
|
+
const [tick, setTick] = React.useState(0);
|
|
19
|
+
const [pulse, setPulse] = React.useState(-1);
|
|
20
|
+
const [slow, setSlow] = React.useState(false);
|
|
21
|
+
const on = animationOn(cols, slow);
|
|
22
|
+
const phase = view?.phase;
|
|
23
|
+
// verifying shimmer + sweep: one timer, only while verifying. A tick that lands far late means the terminal
|
|
24
|
+
// cannot keep up, so the card goes still for good rather than stutter.
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
if (!on || phase !== 'verifying') return undefined;
|
|
27
|
+
let last = Date.now();
|
|
28
|
+
const t = setInterval(() => {
|
|
29
|
+
const n = Date.now(); if (n - last > FRAME_MS * 4) setSlow(true);
|
|
30
|
+
last = n; setTick((x) => x + 1);
|
|
31
|
+
}, FRAME_MS);
|
|
32
|
+
return () => clearInterval(t);
|
|
33
|
+
}, [on, phase === 'verifying']);
|
|
34
|
+
// per-digit pulse: flash the slot just filled for ~150 ms. Reads only the COUNT of digits, never a digit.
|
|
35
|
+
const filled = view?.filled || 0; const prev = React.useRef(0);
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
const was = prev.current; prev.current = filled;
|
|
38
|
+
if (!on || filled <= was || phase !== 'input') { setPulse(-1); return undefined; }
|
|
39
|
+
setPulse(filled - 1);
|
|
40
|
+
const t = setTimeout(() => setPulse(-1), 150);
|
|
41
|
+
return () => clearTimeout(t);
|
|
42
|
+
}, [filled, on]);
|
|
43
|
+
const lines = cardLines(on ? { ...view, pulse, tick: phase === 'verifying' ? tick : null } : view, cols);
|
|
44
|
+
return h(Box, { flexDirection: 'column', width: cols, marginBottom: 1 }, // QUOXON-1191b: the spacer Box above supplies the one blank row above; this adds the one below
|
|
45
|
+
...lines.map((segs, i) => h(Text, { key: i, wrap: 'truncate-end' },
|
|
46
|
+
...segs.map((s, j) => h(Text, { key: j, color: c(s.role === 'prose' ? 'text' : s.role), bold: (s.role === 'gold' && j === 0 && i === 0) || s.role === 'key' || (s.role === 'ok' && s.text.startsWith('✔ AUTH')) }, s.text)))));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** One gold line above the status bar while the /authenticate window is live; ticks once a second. */
|
|
50
|
+
export function PresentBar({ stepUp, cols }) {
|
|
51
|
+
const [, tick] = React.useReducer((n) => n + 1, 0);
|
|
52
|
+
const left = stepUp.presentMs();
|
|
53
|
+
React.useEffect(() => {
|
|
54
|
+
if (!left) return undefined;
|
|
55
|
+
const t = setInterval(() => tick(), 1000);
|
|
56
|
+
return () => clearInterval(t);
|
|
57
|
+
}, [left > 0]);
|
|
58
|
+
if (!left) return null;
|
|
59
|
+
return h(Box, { width: cols }, h(Text, { wrap: 'truncate-end' }, ' ', h(Text, { color: c('gold'), bold: true }, '◉ human present '), h(Text, { color: c('gold') }, mmss(left)), h(Text, { color: c('text') }, ' · /authenticate renews')));
|
|
60
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QUOXON-STEPUP: /authenticate, /authorise and /override. Logic only; app.js wires the three entries.
|
|
3
|
+
*
|
|
4
|
+
* /authenticate PIN once: "human present" for 10 minutes (gold bar), so approvals and overrides
|
|
5
|
+
* in that window do not ask again
|
|
6
|
+
* /authorise <n> approve item n from /approvals with a PIN (prompts when no live step-up)
|
|
7
|
+
* /override <id|last> <reason> a person with a fresh PIN overrides ONE blocked action; recorded and
|
|
8
|
+
* witnessed server-side. Honest about block types no gate honours yet.
|
|
9
|
+
*/
|
|
10
|
+
import { mmss, NEEDS_PERSON } from './stepUp.js';
|
|
11
|
+
|
|
12
|
+
export const BLOCK_TYPES = ['safety', 'budget', 'aocl', 'ward'];
|
|
13
|
+
export const MIN_REASON = 8;
|
|
14
|
+
export const overrideUsage = '/override <block id|last> <reason> (a person must confirm with a PIN; the reason is recorded)';
|
|
15
|
+
|
|
16
|
+
/** "safety:org:agent:RED" -> "safety". Anything that does not start with a known block type -> null. */
|
|
17
|
+
export const blockTypeOf = (id) => { const t = String(id || '').split(':')[0].toLowerCase(); return BLOCK_TYPES.includes(t) ? t : null; };
|
|
18
|
+
|
|
19
|
+
/** -> { ok:true, blockId, reason } | { ok:false, error } */
|
|
20
|
+
export function parseOverride(args, last = null) {
|
|
21
|
+
const words = String(args || '').trim().split(/\s+/).filter(Boolean);
|
|
22
|
+
if (!words.length) return { ok: false, error: `Usage: ${overrideUsage}` };
|
|
23
|
+
let blockId = words[0];
|
|
24
|
+
if (blockId.toLowerCase() === 'last') {
|
|
25
|
+
if (!last?.blockId) return { ok: false, error: 'No policy block has stopped anything in this window yet. Pass the block id: /override <block id> <reason>' };
|
|
26
|
+
blockId = last.blockId;
|
|
27
|
+
}
|
|
28
|
+
const reason = words.slice(1).join(' ').trim();
|
|
29
|
+
if (reason.length < MIN_REASON) return { ok: false, error: `Give a reason (at least ${MIN_REASON} characters): it is recorded with your name. Usage: ${overrideUsage}` };
|
|
30
|
+
if (!blockTypeOf(blockId)) return { ok: false, error: `"${blockId}" is not a block id (expected ${BLOCK_TYPES.map((t) => `${t}:...`).join(', ')}). Use "last" for the most recent block.` };
|
|
31
|
+
return { ok: true, blockId, blockType: blockTypeOf(blockId), reason };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** What a failed step-up means to the person, in one line. */
|
|
35
|
+
export function declinedText(reason) {
|
|
36
|
+
switch (reason) {
|
|
37
|
+
case 'agent_login': return NEEDS_PERSON;
|
|
38
|
+
case 'cancelled': return 'PIN cancelled, nothing was sent';
|
|
39
|
+
case 'locked': return 'PIN entry is locked: use your phone or a human session';
|
|
40
|
+
case 'nopin': return 'no PIN set: set one in the dashboard (Settings > Account > Approval PIN)';
|
|
41
|
+
case 'busy': return 'a PIN prompt is already open';
|
|
42
|
+
default: return 'a person has not confirmed this';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function createStepUpCmds({ api, stepUp, say, lastBlock = () => null, orgOf = () => null }) {
|
|
47
|
+
return {
|
|
48
|
+
async authenticate() {
|
|
49
|
+
const r = await stepUp.request({ window: true, orgId: orgOf(), subject: { line1: 'confirm a person is at this keyboard' } });
|
|
50
|
+
if (!r.ok) return say(`Not authenticated: ${declinedText(r.reason)}.`, 'warn');
|
|
51
|
+
return say(`✔ AUTHORISED · human present for ${mmss(stepUp.presentMs())}. Approvals and /override will not ask again in that window.`, 'ok');
|
|
52
|
+
},
|
|
53
|
+
/** One override. Returns the server answer or null. */
|
|
54
|
+
async override(args) {
|
|
55
|
+
const p = parseOverride(args, lastBlock());
|
|
56
|
+
if (!p.ok) return say(p.error, 'warn');
|
|
57
|
+
const lb = lastBlock();
|
|
58
|
+
const body = { blockId: p.blockId, blockType: p.blockType, reason: p.reason, agentId: lb?.blockId === p.blockId ? lb.agentId : null, action: lb?.blockId === p.blockId ? lb.text : null };
|
|
59
|
+
const subject = { line1: `override ${p.blockId}`, raisedBy: lb?.agentId ? `${lb.agentId} (blocked)` : null, risk: p.blockType === 'safety' ? 'high' : null };
|
|
60
|
+
const go = () => api.override({ ...body, stepUp: stepUp.headers('override') });
|
|
61
|
+
const ask = async () => {
|
|
62
|
+
const r = await stepUp.request({ scope: 'override', orgId: orgOf(), subject, title: 'OVERRIDE REQUIRES A PERSON' });
|
|
63
|
+
return r.ok ? null : declinedText(r.reason);
|
|
64
|
+
};
|
|
65
|
+
let res;
|
|
66
|
+
try {
|
|
67
|
+
if (!stepUp.token('override')) { const no = await ask(); if (no) return say(`Override not made: ${no}.`, 'warn'); }
|
|
68
|
+
try { res = await go(); }
|
|
69
|
+
catch (e) {
|
|
70
|
+
if (e?.status === 403 && e?.body?.error === 'step_up_required') { stepUp.drop('override'); const no = await ask(); if (no) return say(`Override not made: ${no}.`, 'warn'); res = await go(); }
|
|
71
|
+
else throw e;
|
|
72
|
+
}
|
|
73
|
+
} catch (e) { return say(`Override failed: ${e?.body?.message || e?.message || e}`, 'error'); }
|
|
74
|
+
say(`✔ Override ${res.override_id} recorded for ${p.blockId} by you, reason witnessed (AEE/WARD). ${res.note || ''}`.trim(), res.enforced ? 'ok' : 'warn');
|
|
75
|
+
return res;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Server calls behind /undo. Each throws on a non-2xx (never reads an error body as data). */
|
|
2
|
+
import { resolveEndpoint } from '../../api/endpoints.js';
|
|
3
|
+
|
|
4
|
+
const listOf = (d, ...keys) => { for (const k of keys) if (Array.isArray(d?.[k])) return d[k]; return Array.isArray(d) ? d : []; };
|
|
5
|
+
const receiptOf = (d) => d?.envelope_id || d?.envelopeId || d?.receipt?.envelope_id || d?.receipt?.id || null;
|
|
6
|
+
|
|
7
|
+
export function createUndoOps({ client, orgId = () => null }) {
|
|
8
|
+
return {
|
|
9
|
+
/** The call's envelope, read back: { ok, env } | { ok:false, error }. */
|
|
10
|
+
async envelope(id) {
|
|
11
|
+
try { const d = await client.get(`/aee/${encodeURIComponent(id)}`, { service: 'collector', retries: 0, timeout: 10000 }); return { ok: true, env: d?.envelope || d }; }
|
|
12
|
+
catch (e) { return { ok: false, error: e?.status ? `HTTP ${e.status}` : (e?.message || 'failed') }; }
|
|
13
|
+
},
|
|
14
|
+
async recentMoves() {
|
|
15
|
+
const ep = resolveEndpoint('inboxq.moves.recent');
|
|
16
|
+
return listOf(await client.get(ep.path, { service: ep.service, retries: 0, query: { limit: 20 } }), 'moves');
|
|
17
|
+
},
|
|
18
|
+
async undoMove(moveId) {
|
|
19
|
+
const ep = resolveEndpoint('inboxq.moves.undo');
|
|
20
|
+
return { receipt: receiptOf(await client.post(ep.path, { service: ep.service, retries: 0, body: { moveId } })) };
|
|
21
|
+
},
|
|
22
|
+
async releaseSlot(name) {
|
|
23
|
+
const ep = resolveEndpoint('v1.slots.release', { name });
|
|
24
|
+
const data = await client.post(ep.path, { service: ep.service, retries: 0, body: { force: false, org_id: orgId() || undefined } });
|
|
25
|
+
const after = data?.slot;
|
|
26
|
+
if (after && after.state !== 'free' && !data.promoted) throw new Error(`slot ${name} was not released: the server still shows it ${after.state}${after.holder_session ? ` by ${after.holder_session}` : ''}`);
|
|
27
|
+
return { receipt: receiptOf(data) };
|
|
28
|
+
},
|
|
29
|
+
async listNotes() {
|
|
30
|
+
const ep = resolveEndpoint('notesq.notes.list');
|
|
31
|
+
return listOf(await client.get(ep.path, { service: ep.service, retries: 0 }), 'notes', 'items', 'data');
|
|
32
|
+
},
|
|
33
|
+
async deleteNote(id) {
|
|
34
|
+
const ep = resolveEndpoint('notesq.notes.delete', { id });
|
|
35
|
+
return { receipt: receiptOf(await client.delete(ep.path, { service: ep.service, retries: 0 })) };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** /undo last: find the last receipt-backed state change, explain it, and (when a known inverse binds) reverse it on y. */
|
|
2
|
+
import { lastStateChange, INVERSES, BY_HAND } from './inverses.js';
|
|
3
|
+
|
|
4
|
+
const ago = (ms) => { const s = Math.max(0, Math.round(ms / 1000)); return s < 60 ? `${s}s ago` : s < 3600 ? `${Math.round(s / 60)}m ago` : `${Math.round(s / 3600)}h ago`; };
|
|
5
|
+
const clip = (s, n) => { const t = String(s || '').replace(/\s+/g, ' ').trim(); return t.length > n ? `${t.slice(0, n - 1)}…` : t; };
|
|
6
|
+
|
|
7
|
+
export function createUndoCmd({ api, say, getItems, card, now = () => Date.now() }) {
|
|
8
|
+
return async function undo(arg) {
|
|
9
|
+
const a = String(arg || '').trim().toLowerCase();
|
|
10
|
+
if (a && a !== 'last') return say('Usage: /undo last (bare /undo does the same; Ctrl+_ undoes composer typing)', 'warn');
|
|
11
|
+
const ops = api.undoOps;
|
|
12
|
+
if (!ops) return say('/undo is not available in this mode.', 'warn');
|
|
13
|
+
const call = lastStateChange(getItems());
|
|
14
|
+
if (!call) return say('No Quox action in this conversation has a receipt to reverse yet.', 'dim');
|
|
15
|
+
const when = Number.isFinite(call.startedAt) ? ago(now() - call.startedAt) : 'earlier';
|
|
16
|
+
// The receipt is the evidence of what ran: read it back before offering to reverse anything.
|
|
17
|
+
const rec = await ops.envelope(call.envelopeId);
|
|
18
|
+
if (rec.ok) {
|
|
19
|
+
const p = rec.env?.payload || {};
|
|
20
|
+
if (p.tool_name && p.tool_name !== call.tool) return say(`The receipt ${call.envelopeId} is for ${p.tool_name}, not ${call.tool}, so nothing was offered.`, 'warn');
|
|
21
|
+
if (p.success === false) return say(`The receipt says ${call.tool} did not succeed, so there is nothing to undo.`, 'warn');
|
|
22
|
+
}
|
|
23
|
+
const head = `undo │ ${call.tool} · ${when} · receipt ${call.envelopeId}${rec.ok ? '' : ` (receipt not read (${rec.error}))`}`;
|
|
24
|
+
const inv = INVERSES[call.tool];
|
|
25
|
+
if (!inv) {
|
|
26
|
+
say(head);
|
|
27
|
+
say(`Quox can't reverse ${call.tool} automatically.`, 'warn');
|
|
28
|
+
say(`It did: ${clip(call.resultSummary || call.summary || call.tool, 100)}. By hand: ${BY_HAND[call.tool] || 'check what it changed with /proof and undo that yourself'}.`, 'dim');
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
let bound;
|
|
32
|
+
try { bound = await inv.bind(call, ops); } catch (e) { return say(`Could not work out what to reverse: ${e?.message || e}. Nothing ran.`, 'warn'); }
|
|
33
|
+
if (!bound.ok) {
|
|
34
|
+
say(head);
|
|
35
|
+
say(`Quox can't reverse ${call.tool} automatically here: ${bound.why}.`, 'warn');
|
|
36
|
+
say(`By hand: ${bound.byHand}.`, 'dim');
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
card.ask([head, `It ${inv.did}: ${clip(call.resultSummary || call.summary, 100)}.`, `Undoing it will ${bound.reverse}.`],
|
|
40
|
+
async () => {
|
|
41
|
+
let r;
|
|
42
|
+
try { r = await bound.run(ops); } catch (e) { return say(`NOT undone: ${e?.message || e}`, 'warn'); }
|
|
43
|
+
say(`Undone: ${bound.reverse}.`, 'ok');
|
|
44
|
+
say(r?.receipt ? `new receipt ${r.receipt} (/proof ${r.receipt})` : 'The server returned no receipt id for this reversal, so there is nothing to /proof for it.', 'dim');
|
|
45
|
+
return undefined;
|
|
46
|
+
}, 'Cancelled; nothing was undone.', { question: 'Undo it?' });
|
|
47
|
+
return undefined;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** /undo and /wish factory app.js wires: switch cases plus one answer hook. They share one card (one y/n at a time). */
|
|
2
|
+
import { createCard } from './card.js';
|
|
3
|
+
import { createUndoCmd } from './undoCmd.js';
|
|
4
|
+
import { createWishCmd } from './wishCmd.js';
|
|
5
|
+
|
|
6
|
+
export function createUndoWish({ api, say, getItems, runTurn, draft, now }) {
|
|
7
|
+
const card = createCard({ say, now });
|
|
8
|
+
return {
|
|
9
|
+
undo: createUndoCmd({ api, say, getItems, card, now }),
|
|
10
|
+
wish: createWishCmd({ api, say, card, runTurn, draft }),
|
|
11
|
+
answer: (text) => card.answer(text),
|
|
12
|
+
pending: () => card.pending(),
|
|
13
|
+
touch: () => card.touch()
|
|
14
|
+
};
|
|
15
|
+
}
|