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,687 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills commands.
|
|
3
|
+
*
|
|
4
|
+
* `list`/`show` are read-only queries against the quoxflow QSKL
|
|
5
|
+
* (procedural-knowledge Skills / Crystals) library, via the collector's
|
|
6
|
+
* quoxflow proxy — see the comment above each subcommand for the exact
|
|
7
|
+
* route it calls.
|
|
8
|
+
*
|
|
9
|
+
* `run`, `review`, `library`, and the Crystal-lifecycle writes
|
|
10
|
+
* (`from-execution`/`promote-draft`/`rollback-to-version`) added in WP2.3
|
|
11
|
+
* go DIRECT to quoxflow (service: 'quoxflow', port 3200) — the collector
|
|
12
|
+
* does NOT proxy these routes (only the three read-only proxy paths used
|
|
13
|
+
* by `list`/`show` plus evidence/observations are hand-mounted; see
|
|
14
|
+
* cli/lib/api/endpoints/skills.js header for the full route provenance).
|
|
15
|
+
* `library` is the one exception: it's the auth service's per-user saved
|
|
16
|
+
* workflow library (services/auth/routes/user-library.js), not quoxflow.
|
|
17
|
+
*
|
|
18
|
+
* NOTE (deviations from the upstream field names, see quoxflow
|
|
19
|
+
* src/store/quox-skills-store.ts):
|
|
20
|
+
* - The real status field on a skill is `edition`, with values
|
|
21
|
+
* experimental | stable | deprecated | archived (there is no
|
|
22
|
+
* "canonical" value). `--edition` filters on this field directly.
|
|
23
|
+
* - There is no server-side full-text `search` query param on
|
|
24
|
+
* GET /api/quox-skills. `--search` is applied client-side (substring
|
|
25
|
+
* match against name + description) after fetching the (filtered)
|
|
26
|
+
* list from the proxy.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
30
|
+
import path from 'node:path';
|
|
31
|
+
import { formatOutput, formatDetail, printSuccess, printWarning, printInfo } from '../core/output.js';
|
|
32
|
+
import { resolveOrgId, parseJSON } from './helpers.js';
|
|
33
|
+
import { resolveEndpoint } from '../api/endpoints.js';
|
|
34
|
+
import { shortLicenceLabel } from './skillRightsFormat.js';
|
|
35
|
+
import { loadOrBuildManifest, readSkillFrontmatter } from './skillManifest.js';
|
|
36
|
+
|
|
37
|
+
export function registerSkillsCommands(program, ctx) {
|
|
38
|
+
const { client, auth } = ctx;
|
|
39
|
+
|
|
40
|
+
const skills = program
|
|
41
|
+
.command('skills')
|
|
42
|
+
.description('Manage the quoxflow skill library (QSKL Crystals): query, run, review, promote, and your saved library');
|
|
43
|
+
|
|
44
|
+
// ── skills list ──────────────────────────────────────────────────────────
|
|
45
|
+
// GET /quoxflow/skills — collector proxy to quoxflow GET /api/quox-skills
|
|
46
|
+
|
|
47
|
+
skills
|
|
48
|
+
.command('list')
|
|
49
|
+
.description('List skills in the library')
|
|
50
|
+
.option('--edition <edition>', 'filter by edition (experimental|stable|deprecated|archived)')
|
|
51
|
+
.option('--category <category>', 'filter by category')
|
|
52
|
+
.option('--agent <agentId>', 'filter to skills best-suited for an agent')
|
|
53
|
+
.option('--tag <tag>', 'filter by tag')
|
|
54
|
+
.option('--search <q>', 'client-side filter: substring match on name/description')
|
|
55
|
+
.option('--limit <n>', 'max results')
|
|
56
|
+
.option('--order-by <field>', 'sort field (last_used_at|created_at)')
|
|
57
|
+
.option('--order <dir>', 'sort direction (asc|desc)')
|
|
58
|
+
.option('--org <id>', 'org ID (overrides active org)')
|
|
59
|
+
.action(async (options) => {
|
|
60
|
+
auth.requireAuth();
|
|
61
|
+
const query = {};
|
|
62
|
+
query.orgId = options.org || resolveOrgId(auth, program);
|
|
63
|
+
if (options.edition) query.edition = options.edition;
|
|
64
|
+
if (options.category) query.category = options.category;
|
|
65
|
+
if (options.agent) query.agentId = options.agent;
|
|
66
|
+
if (options.tag) query.tag = options.tag;
|
|
67
|
+
if (options.limit) query.limit = options.limit;
|
|
68
|
+
if (options.orderBy) query.orderBy = options.orderBy;
|
|
69
|
+
if (options.order) query.order = options.order;
|
|
70
|
+
|
|
71
|
+
const data = await client.get(resolveEndpoint('quoxflow.skills.list').path, { service: resolveEndpoint('quoxflow.skills.list').service, query });
|
|
72
|
+
let rows = data.data || data.records || data;
|
|
73
|
+
if (!Array.isArray(rows)) rows = [];
|
|
74
|
+
rows = rows.map((r) => ({ ...r, store: 'library' }));
|
|
75
|
+
|
|
76
|
+
// 4.2.4 skills/t3-skill-view: a library skill with an active quox#748 review row (pending or rejected) cannot
|
|
77
|
+
// run, and skill_view refuses it. Say so per skill. A queue read failure leaves runnable unknown, never true.
|
|
78
|
+
try {
|
|
79
|
+
const rq = resolveEndpoint('skills.reviewQueue.list');
|
|
80
|
+
const active = new Map();
|
|
81
|
+
for (const status of ['pending', 'rejected']) {
|
|
82
|
+
const r = await client.get(rq.path, { service: rq.service, query: { status, orgId: query.orgId } });
|
|
83
|
+
const items = r && (r.data || r.records);
|
|
84
|
+
for (const it of Array.isArray(items) ? items : []) if (it && it.skill_id) active.set(it.skill_id, it.status || status);
|
|
85
|
+
}
|
|
86
|
+
rows = rows.map((r) => ({ ...r, review_status: active.get(r.id) || null, runnable: !active.has(r.id) }));
|
|
87
|
+
} catch (err) {
|
|
88
|
+
printWarning(`Skill review queue could not be read, so which skills can run is unknown: ${err.message}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// #1006: crystals (`skills from-execution`) live in quoxflow's crystal store, not this library. Merge them
|
|
92
|
+
// unless a library-only filter is set (crystals have no edition/category/agent fit). A failure there warns,
|
|
93
|
+
// it never sinks the library listing.
|
|
94
|
+
if (!options.edition && !options.category && !options.agent) {
|
|
95
|
+
try {
|
|
96
|
+
const ep = resolveEndpoint('skills.crystals.list');
|
|
97
|
+
const c = await client.get(ep.path, { service: ep.service, query: { orgId: query.orgId } });
|
|
98
|
+
let crystals = c.data || c.skills || c;
|
|
99
|
+
if (!Array.isArray(crystals)) crystals = [];
|
|
100
|
+
if (options.tag) crystals = crystals.filter((r) => Array.isArray(r.tags) && r.tags.includes(options.tag));
|
|
101
|
+
rows = rows.concat(crystals.map((r) => ({ ...r, store: 'crystal' })));
|
|
102
|
+
} catch (err) {
|
|
103
|
+
printWarning(`Crystal store (skills from-execution) could not be read, showing the library only: ${err.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (options.search) {
|
|
108
|
+
const needle = options.search.toLowerCase();
|
|
109
|
+
rows = rows.filter((r) =>
|
|
110
|
+
(r.name || '').toLowerCase().includes(needle) ||
|
|
111
|
+
(r.description || '').toLowerCase().includes(needle)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// SKILLRIGHTS-8: declarations travel with the skill; a missing one is
|
|
116
|
+
// shown as undeclared, never defaulted to a licence.
|
|
117
|
+
rows = rows.map((r) => ({ ...r, licence: shortLicenceLabel(r.license) }));
|
|
118
|
+
formatOutput({
|
|
119
|
+
columns: ['id', 'store', 'name', 'category', 'edition', 'version', 'licence', 'decayState', 'createdAt'],
|
|
120
|
+
headers: ['ID', 'Store', 'Name', 'Category', 'Edition', 'Version', 'Licence', 'Decay', 'Created'],
|
|
121
|
+
rows,
|
|
122
|
+
}, { format: program.opts().output, emptyMessage: 'No skills found.' });
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ── skills show ──────────────────────────────────────────────────────────
|
|
126
|
+
// GET /quoxflow/skills/:id — collector proxy to quoxflow GET /api/quox-skills/:id
|
|
127
|
+
|
|
128
|
+
skills
|
|
129
|
+
.command('show')
|
|
130
|
+
.description('Show skill details')
|
|
131
|
+
.argument('<id>', 'skill ID')
|
|
132
|
+
.option('--org <id>', 'org ID (overrides active org)')
|
|
133
|
+
.action(async (id, options) => {
|
|
134
|
+
auth.requireAuth();
|
|
135
|
+
const query = { orgId: options.org || resolveOrgId(auth, program) };
|
|
136
|
+
let data, store = 'library';
|
|
137
|
+
try {
|
|
138
|
+
data = await client.get(resolveEndpoint('quoxflow.skills.get', { id }).path, { service: resolveEndpoint('quoxflow.skills.get', { id }).service, query });
|
|
139
|
+
} catch (err) {
|
|
140
|
+
if (err.status !== 404) throw err;
|
|
141
|
+
// #1006: not in the library; a crystal from `skills from-execution` lives in the crystal store
|
|
142
|
+
const ep = resolveEndpoint('skills.crystals.get', { id });
|
|
143
|
+
try { data = await client.get(ep.path, { service: ep.service, query }); } catch (err2) { if (err2.status === 404) throw err; throw err2; }
|
|
144
|
+
store = 'crystal';
|
|
145
|
+
}
|
|
146
|
+
formatDetail({ ...(data.data || data), store }, { format: program.opts().output, title: `Skill: ${id}` });
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// ── skills run ───────────────────────────────────────────────────────────
|
|
150
|
+
// POST /api/workflows/:id/execute — quoxflow DIRECT (executions.ts:206).
|
|
151
|
+
// Crystals ARE workflows: this runs a skill/crystal by its (source)
|
|
152
|
+
// workflow id. Sync by default (waits for the result); --async fires and
|
|
153
|
+
// returns immediately with an executionId.
|
|
154
|
+
|
|
155
|
+
skills
|
|
156
|
+
.command('run')
|
|
157
|
+
.description('Execute a skill/crystal (its underlying workflow)')
|
|
158
|
+
.argument('<id>', 'workflow ID (the skill\'s sourceWorkflowId)')
|
|
159
|
+
.option('--data <json>', 'trigger data, as a JSON object string')
|
|
160
|
+
.option('--async', 'fire-and-forget: return immediately with an executionId instead of waiting for the result')
|
|
161
|
+
.option('--org <id>', 'org ID (overrides active org)')
|
|
162
|
+
.action(async (id, options) => {
|
|
163
|
+
auth.requireAuth();
|
|
164
|
+
const orgId = options.org || resolveOrgId(auth, program);
|
|
165
|
+
const body = { orgId };
|
|
166
|
+
if (options.data) body.triggerData = parseJSON(options.data, 'trigger data');
|
|
167
|
+
if (options.async) body.async = true;
|
|
168
|
+
|
|
169
|
+
const ep = resolveEndpoint('skills.run', { id });
|
|
170
|
+
const data = await client.post(ep.path, { service: ep.service, body });
|
|
171
|
+
const result = data.data || data;
|
|
172
|
+
|
|
173
|
+
if (options.async) {
|
|
174
|
+
printSuccess(`Execution started (${result.executionId}).`);
|
|
175
|
+
} else if (result.success === false) {
|
|
176
|
+
printWarning('Execution finished with errors.');
|
|
177
|
+
} else {
|
|
178
|
+
printSuccess('Execution complete.');
|
|
179
|
+
}
|
|
180
|
+
formatDetail(result, { format: program.opts().output });
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// ── skills review ────────────────────────────────────────────────────────
|
|
184
|
+
// QSKL-SAFETY.F review queue — quox-skills.ts:392-553, quoxflow DIRECT.
|
|
185
|
+
// Org-scoped server-side (JWT-primary orgId, 403 on cross-org row access).
|
|
186
|
+
|
|
187
|
+
const review = skills
|
|
188
|
+
.command('review')
|
|
189
|
+
.description('Manage the QSKL-SAFETY skill review queue');
|
|
190
|
+
|
|
191
|
+
review
|
|
192
|
+
.command('list')
|
|
193
|
+
.description('List review queue items (default: pending)')
|
|
194
|
+
.option('--status <status>', 'pending|approved|rejected', 'pending')
|
|
195
|
+
.action(async (options) => {
|
|
196
|
+
auth.requireAuth();
|
|
197
|
+
const orgId = resolveOrgId(auth, program);
|
|
198
|
+
const ep = resolveEndpoint('skills.reviewQueue.list');
|
|
199
|
+
const data = await client.get(ep.path, {
|
|
200
|
+
service: ep.service,
|
|
201
|
+
query: { status: options.status, orgId },
|
|
202
|
+
});
|
|
203
|
+
const rows = data.data || data.records || [];
|
|
204
|
+
|
|
205
|
+
formatOutput({
|
|
206
|
+
// fleet #392: review queue rows carry queued_at.
|
|
207
|
+
columns: ['id', 'skill_id', 'skill_name', 'status', 'reviewer_id', 'queued_at'],
|
|
208
|
+
headers: ['ID', 'Skill ID', 'Skill Name', 'Status', 'Reviewer', 'Created'],
|
|
209
|
+
rows: Array.isArray(rows) ? rows : [],
|
|
210
|
+
}, { format: program.opts().output, emptyMessage: `No ${options.status} review queue items.` });
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
review
|
|
214
|
+
.command('show')
|
|
215
|
+
.description('Show a review queue item (with skill snapshot + latest firewall log)')
|
|
216
|
+
.argument('<qid>', 'review queue item ID')
|
|
217
|
+
.action(async (qid) => {
|
|
218
|
+
auth.requireAuth();
|
|
219
|
+
const ep = resolveEndpoint('skills.reviewQueue.get', { qid });
|
|
220
|
+
const data = await client.get(ep.path, { service: ep.service });
|
|
221
|
+
formatDetail(data.data || data, { format: program.opts().output, title: `Review Queue Item: ${qid}` });
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
review
|
|
225
|
+
.command('approve')
|
|
226
|
+
.description('Approve a review queue item')
|
|
227
|
+
.argument('<qid>', 'review queue item ID')
|
|
228
|
+
.option('--comment <text>', 'optional reviewer comment')
|
|
229
|
+
.action(async (qid, options) => {
|
|
230
|
+
auth.requireAuth();
|
|
231
|
+
const body = {};
|
|
232
|
+
if (options.comment) body.comment = options.comment;
|
|
233
|
+
const ep = resolveEndpoint('skills.reviewQueue.approve', { qid });
|
|
234
|
+
const data = await client.post(ep.path, { service: ep.service, body });
|
|
235
|
+
printSuccess(`Review queue item ${qid} approved.`);
|
|
236
|
+
formatDetail(data.data || data, { format: program.opts().output });
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
review
|
|
240
|
+
.command('reject')
|
|
241
|
+
.description('Reject a review queue item')
|
|
242
|
+
.argument('<qid>', 'review queue item ID')
|
|
243
|
+
.option('--comment <text>', 'optional reviewer comment')
|
|
244
|
+
.action(async (qid, options) => {
|
|
245
|
+
auth.requireAuth();
|
|
246
|
+
const body = {};
|
|
247
|
+
if (options.comment) body.comment = options.comment;
|
|
248
|
+
const ep = resolveEndpoint('skills.reviewQueue.reject', { qid });
|
|
249
|
+
const data = await client.post(ep.path, { service: ep.service, body });
|
|
250
|
+
printSuccess(`Review queue item ${qid} rejected.`);
|
|
251
|
+
formatDetail(data.data || data, { format: program.opts().output });
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// ── skills library ───────────────────────────────────────────────────────
|
|
255
|
+
// Per-org/per-user saved workflow library — auth service (NOT quoxflow),
|
|
256
|
+
// services/auth/routes/user-library.js. Always scoped to the caller
|
|
257
|
+
// (org_id + user_id); another user in the same org cannot see/delete
|
|
258
|
+
// these entries.
|
|
259
|
+
|
|
260
|
+
const library = skills
|
|
261
|
+
.command('library')
|
|
262
|
+
.description('Manage your saved workflow library');
|
|
263
|
+
|
|
264
|
+
library
|
|
265
|
+
.command('list')
|
|
266
|
+
.description('List your saved library entries')
|
|
267
|
+
.option('--source <source>', 'filter by source: curated|n8n-imported|mine')
|
|
268
|
+
.action(async (options) => {
|
|
269
|
+
auth.requireAuth();
|
|
270
|
+
const orgId = resolveOrgId(auth, program);
|
|
271
|
+
const query = {};
|
|
272
|
+
if (options.source) query.source = options.source;
|
|
273
|
+
const ep = resolveEndpoint('user-library.list', { orgId });
|
|
274
|
+
const data = await client.get(ep.path, { service: ep.service, query });
|
|
275
|
+
const entries = data.entries || data || [];
|
|
276
|
+
|
|
277
|
+
formatOutput({
|
|
278
|
+
columns: ['id', 'name', 'source', 'source_workflow_id', 'updated_at'],
|
|
279
|
+
headers: ['ID', 'Name', 'Source', 'Source Workflow', 'Updated'],
|
|
280
|
+
rows: Array.isArray(entries) ? entries : [],
|
|
281
|
+
}, { format: program.opts().output, emptyMessage: 'No saved library entries.' });
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
library
|
|
285
|
+
.command('add')
|
|
286
|
+
.description('Save a workflow definition to your library')
|
|
287
|
+
.requiredOption('--name <name>', 'entry name')
|
|
288
|
+
.option('--definition <json>', 'workflow definition, as a JSON object string')
|
|
289
|
+
.option('--file <path>', 'read the workflow definition from a JSON file (overrides --definition)')
|
|
290
|
+
.option('--description <text>', 'description')
|
|
291
|
+
.option('--source <source>', 'curated|n8n-imported|mine', 'mine')
|
|
292
|
+
.option('--source-workflow-id <id>', 'originating workflow ID')
|
|
293
|
+
.option('--tags <tags>', 'comma-separated tags')
|
|
294
|
+
.action(async (options) => {
|
|
295
|
+
auth.requireAuth();
|
|
296
|
+
const orgId = resolveOrgId(auth, program);
|
|
297
|
+
|
|
298
|
+
let definitionJson;
|
|
299
|
+
if (options.file) {
|
|
300
|
+
definitionJson = parseJSON(readFileSync(options.file, 'utf-8'), 'workflow definition file');
|
|
301
|
+
} else if (options.definition) {
|
|
302
|
+
definitionJson = parseJSON(options.definition, 'workflow definition');
|
|
303
|
+
} else {
|
|
304
|
+
throw new Error('Workflow definition is required. Use --definition <json> or --file <path>.');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const body = { name: options.name, definition_json: definitionJson, source: options.source };
|
|
308
|
+
if (options.description) body.description = options.description;
|
|
309
|
+
if (options.sourceWorkflowId) body.source_workflow_id = options.sourceWorkflowId;
|
|
310
|
+
if (options.tags) body.tags = options.tags.split(',').map(t => t.trim()).filter(Boolean);
|
|
311
|
+
|
|
312
|
+
const ep = resolveEndpoint('user-library.create', { orgId });
|
|
313
|
+
const data = await client.post(ep.path, { service: ep.service, body });
|
|
314
|
+
printSuccess(`Library entry "${options.name}" saved.`);
|
|
315
|
+
formatDetail(data.entry || data, { format: program.opts().output });
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
library
|
|
319
|
+
.command('remove')
|
|
320
|
+
.description('Remove a saved library entry')
|
|
321
|
+
.argument('<id>', 'library entry ID')
|
|
322
|
+
.option('--force', 'skip confirmation')
|
|
323
|
+
.action(async (id, options) => {
|
|
324
|
+
auth.requireAuth();
|
|
325
|
+
const orgId = resolveOrgId(auth, program);
|
|
326
|
+
|
|
327
|
+
if (!options.force && !program.opts().yes) {
|
|
328
|
+
const { default: inquirer } = await import('inquirer');
|
|
329
|
+
const ans = await inquirer.prompt([{
|
|
330
|
+
type: 'confirm',
|
|
331
|
+
name: 'confirm',
|
|
332
|
+
message: `Remove library entry ${id}? This cannot be undone.`,
|
|
333
|
+
default: false,
|
|
334
|
+
}]);
|
|
335
|
+
if (!ans.confirm) {
|
|
336
|
+
printInfo('Cancelled.');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const ep = resolveEndpoint('user-library.delete', { orgId, id });
|
|
342
|
+
await client.delete(ep.path, { service: ep.service });
|
|
343
|
+
printSuccess('Library entry removed.');
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// ── skills from-execution / promote-draft / rollback-to-version ────────
|
|
347
|
+
// Crystal Skill Library lifecycle writes — quoxflow DIRECT
|
|
348
|
+
// (skills.ts, mounted at /api/skills, distinct from the QSKL
|
|
349
|
+
// /api/quox-skills store used by `skills list`/`show`).
|
|
350
|
+
|
|
351
|
+
skills
|
|
352
|
+
.command('from-execution')
|
|
353
|
+
.description('Crystallise a workflow into a skill (idempotent — returns the existing skill if one already exists)')
|
|
354
|
+
.argument('<workflowId>', 'workflow ID to crystallise')
|
|
355
|
+
.option('--name <name>', 'workflow display name (used for the generated skill name)')
|
|
356
|
+
.action(async (workflowId, options) => {
|
|
357
|
+
auth.requireAuth();
|
|
358
|
+
const orgId = resolveOrgId(auth, program);
|
|
359
|
+
const body = { workflowId };
|
|
360
|
+
if (options.name) body.workflowName = options.name;
|
|
361
|
+
|
|
362
|
+
const ep = resolveEndpoint('skills.fromExecution');
|
|
363
|
+
const data = await client.post(ep.path, { service: ep.service, body, query: { orgId } });
|
|
364
|
+
const record = data.data || data;
|
|
365
|
+
printSuccess(`Skill "${record.name || workflowId}" crystallised.`);
|
|
366
|
+
formatDetail(record, { format: program.opts().output });
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
skills
|
|
370
|
+
.command('promote-draft')
|
|
371
|
+
.description('Promote an in-flight task-time draft Crystal to the skill library (requires tier_b)')
|
|
372
|
+
.argument('<executionId>', 'execution ID with an in-flight draft')
|
|
373
|
+
.option('--tier <tier>', 'org tier: tier_a|tier_b (tier_b required to promote)', 'tier_a')
|
|
374
|
+
.action(async (executionId, options) => {
|
|
375
|
+
auth.requireAuth();
|
|
376
|
+
const body = { executionId, orgTier: options.tier };
|
|
377
|
+
|
|
378
|
+
const ep = resolveEndpoint('skills.promoteDraft');
|
|
379
|
+
const data = await client.post(ep.path, { service: ep.service, body });
|
|
380
|
+
const record = data.data || data;
|
|
381
|
+
printSuccess(`Draft for execution ${executionId} promoted.`);
|
|
382
|
+
formatDetail(record, { format: program.opts().output });
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
skills
|
|
386
|
+
.command('rollback-to-version')
|
|
387
|
+
.description('Roll a skill back to a prior version (writes a NEW version copying the target\'s content)')
|
|
388
|
+
.argument('<sourceWorkflowId>', 'source workflow ID')
|
|
389
|
+
.argument('<targetVersion>', 'version number to roll back to', (v) => parseInt(v, 10))
|
|
390
|
+
.action(async (sourceWorkflowId, targetVersion) => {
|
|
391
|
+
auth.requireAuth();
|
|
392
|
+
const orgId = resolveOrgId(auth, program);
|
|
393
|
+
const body = { sourceWorkflowId, targetVersion };
|
|
394
|
+
|
|
395
|
+
const ep = resolveEndpoint('skills.rollbackToVersion');
|
|
396
|
+
const data = await client.post(ep.path, { service: ep.service, body, query: { orgId } });
|
|
397
|
+
const record = data.data || data;
|
|
398
|
+
// #1006: rolling back to the version that is already current is a server-side no-op (it returns that row)
|
|
399
|
+
if (record && Number(record.version) === targetVersion) printInfo(`Already at version ${targetVersion}: nothing was written.`);
|
|
400
|
+
else printSuccess(`Skill rolled back to version ${targetVersion} (written as version ${record?.version ?? '?'}).`);
|
|
401
|
+
formatDetail(record, { format: program.opts().output });
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// ── skills protect ───────────────────────────────────────────────────────
|
|
405
|
+
// SKILLRIGHTS #478 Phase 10. POST /api/v1/skills/protect (collector).
|
|
406
|
+
//
|
|
407
|
+
// Hashes the skill LOCALLY (or reuses the manifest the open `skillrights`
|
|
408
|
+
// CLI already wrote) and sends only that manifest. The collector has the
|
|
409
|
+
// org sign a domain-separated digest, registers the hash in the open
|
|
410
|
+
// SkillRights registry, verifies the receipt, and witnesses the whole
|
|
411
|
+
// thing into the org's evidence chain.
|
|
412
|
+
//
|
|
413
|
+
// What it proves: this exact artefact existed by this time, and this org's
|
|
414
|
+
// key claimed and signed it. NOT legal ownership.
|
|
415
|
+
skills
|
|
416
|
+
.command('protect')
|
|
417
|
+
.description('Sign a skill with the org identity, register it in the open SkillRights registry, and keep the receipt in the org evidence chain')
|
|
418
|
+
.argument('[dir]', 'skill directory', '.')
|
|
419
|
+
.option('--public', 'list the skill publicly (sends name/description to the registry directory)')
|
|
420
|
+
.option('--supersedes <sha256>', 'claimed earlier version this replaces')
|
|
421
|
+
.option('--repository <url>', 'repository URL (public mode only)')
|
|
422
|
+
.action(async (dir, options) => {
|
|
423
|
+
auth.requireAuth();
|
|
424
|
+
const resolvedDir = path.resolve(dir);
|
|
425
|
+
const { manifest, source } = loadOrBuildManifest(resolvedDir);
|
|
426
|
+
const front = readSkillFrontmatter(resolvedDir);
|
|
427
|
+
|
|
428
|
+
const body = { manifest, mode: options.public ? 'public' : 'private' };
|
|
429
|
+
if (options.supersedes) body.supersedes = options.supersedes;
|
|
430
|
+
if (options.public) {
|
|
431
|
+
const meta = {};
|
|
432
|
+
if (front.name) meta.name = front.name;
|
|
433
|
+
if (front.description) meta.description = front.description;
|
|
434
|
+
if (options.repository) meta.repository = options.repository;
|
|
435
|
+
if (Object.keys(meta).length) body.meta = meta;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (!manifest.identifier) {
|
|
439
|
+
printWarning('This skill declares no SkillRights licence (no `license:` in SKILL.md frontmatter). Protecting it anyway; the registration will record no licence.');
|
|
440
|
+
}
|
|
441
|
+
printInfo(`Manifest ${source === 'existing' ? 'read from' : 'computed for'} ${resolvedDir} (${Object.keys(manifest.files).length} files). File contents never leave this machine; the manifest sent carries relative file names and per-file hashes.`);
|
|
442
|
+
|
|
443
|
+
const ep = resolveEndpoint('skills.protect');
|
|
444
|
+
const data = await client.post(ep.path, { service: ep.service, body });
|
|
445
|
+
const p = data.protection || data;
|
|
446
|
+
if (data.created === false) {
|
|
447
|
+
printInfo(`Already protected (identical content): ${p.srid}`);
|
|
448
|
+
} else {
|
|
449
|
+
printSuccess(`Protected. SRID ${p.srid}`);
|
|
450
|
+
}
|
|
451
|
+
printInfo('Proves the artefact existed by the registered time and that your org key claimed and signed it. It does not prove legal ownership.');
|
|
452
|
+
formatDetail({
|
|
453
|
+
srid: p.srid,
|
|
454
|
+
manifest_sha256: p.manifestSha256,
|
|
455
|
+
licence: p.license || '(none declared)',
|
|
456
|
+
mode: p.mode,
|
|
457
|
+
org_key_id: p.orgKeyId,
|
|
458
|
+
registry: p.registryUrl,
|
|
459
|
+
evidence_envelope: p.aeeEnvelopeId || '(not recorded)',
|
|
460
|
+
registered_at: p.receipt?.receipt?.record?.ts,
|
|
461
|
+
}, { format: program.opts().output });
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
// ── skills disclosures ───────────────────────────────────────────────────
|
|
465
|
+
// SKILLRIGHTS #478 Phase 12. GET /api/v1/skills/rights-ledger (collector).
|
|
466
|
+
// The org's rights disclosure ledger: what was distributed and protected,
|
|
467
|
+
// under which declared rights, with the policy in force and evidence
|
|
468
|
+
// counts. Read-only. `-o json` is the export format for handing the
|
|
469
|
+
// disclosure to an auditor. What it does NOT claim: execution/usage
|
|
470
|
+
// events (that ledger does not exist yet) or verified signatures.
|
|
471
|
+
skills
|
|
472
|
+
.command('disclosures')
|
|
473
|
+
.description('Org rights disclosure ledger: distributions + protections under declared rights, policy in force, evidence counts')
|
|
474
|
+
.action(async () => {
|
|
475
|
+
auth.requireAuth();
|
|
476
|
+
const ep = resolveEndpoint('skills.rightsLedger');
|
|
477
|
+
const ledger = await client.get(ep.path, { service: ep.service });
|
|
478
|
+
|
|
479
|
+
if (program.opts().output === 'json') {
|
|
480
|
+
formatDetail(ledger, { format: 'json' });
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const s = ledger.summary || {};
|
|
485
|
+
printInfo(`Rights disclosure for org ${ledger.orgId} (generated ${ledger.generatedAt}). Policy in force: ${ledger.policy || 'unknown'}${ledger.policyError ? ` (policy lookup failed: ${ledger.policyError})` : ''}.`);
|
|
486
|
+
printInfo(`${s.hosts ?? 0} host(s), ${s.skillsDistributed ?? 0} skill(s) distributed (${s.skillsDeclared ?? 0} declared, ${s.skillsUndeclared ?? 0} undeclared), ${s.protections ?? 0} protection(s). Evidence: ${ledger.evidence?.syncEnvelopes ?? 0} sync + ${ledger.evidence?.protectEnvelopes ?? 0} protect envelope(s).`);
|
|
487
|
+
|
|
488
|
+
formatOutput({
|
|
489
|
+
columns: ['hostId', 'installedVersion', 'status', 'rightsSummary'],
|
|
490
|
+
headers: ['Host', 'Pack ver', 'Status', 'Rights'],
|
|
491
|
+
rows: (ledger.distributions || []).map((d) => ({
|
|
492
|
+
hostId: d.hostId,
|
|
493
|
+
installedVersion: d.installedVersion || '?',
|
|
494
|
+
status: d.status,
|
|
495
|
+
rightsSummary: d.rights
|
|
496
|
+
? `${d.rights.declared}/${d.rights.total} declared`
|
|
497
|
+
: (d.rightsUnreadable ? 'UNREADABLE record' : 'no rights record'),
|
|
498
|
+
})),
|
|
499
|
+
}, { format: program.opts().output, emptyMessage: 'No distributions recorded.' });
|
|
500
|
+
|
|
501
|
+
formatOutput({
|
|
502
|
+
columns: ['srid', 'skillName', 'licence', 'mode', 'evidence'],
|
|
503
|
+
headers: ['SRID', 'Skill', 'Licence', 'Mode', 'Evidence'],
|
|
504
|
+
rows: (ledger.protections || []).map((p) => ({
|
|
505
|
+
srid: p.srid,
|
|
506
|
+
skillName: p.skillName || '',
|
|
507
|
+
licence: shortLicenceLabel(p.license),
|
|
508
|
+
mode: p.mode,
|
|
509
|
+
evidence: p.aeeEnvelopeId || '',
|
|
510
|
+
})),
|
|
511
|
+
}, { format: program.opts().output, emptyMessage: 'No protections recorded.' });
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// ── skills protections ───────────────────────────────────────────────────
|
|
515
|
+
// GET /api/v1/skills/protections (collector).
|
|
516
|
+
skills
|
|
517
|
+
.command('protections')
|
|
518
|
+
.description('List this org\'s protected skills (SRID, licence, org key, evidence envelope)')
|
|
519
|
+
.action(async () => {
|
|
520
|
+
auth.requireAuth();
|
|
521
|
+
const ep = resolveEndpoint('skills.protections');
|
|
522
|
+
const data = await client.get(ep.path, { service: ep.service });
|
|
523
|
+
const rows = (data.protections || []).map((p) => ({
|
|
524
|
+
srid: p.srid,
|
|
525
|
+
name: p.skillName || '',
|
|
526
|
+
licence: p.license || '',
|
|
527
|
+
mode: p.mode,
|
|
528
|
+
registered: p.receipt?.receipt?.record?.ts || p.createdAt,
|
|
529
|
+
evidence: p.aeeEnvelopeId || '',
|
|
530
|
+
}));
|
|
531
|
+
formatOutput(rows, {
|
|
532
|
+
format: program.opts().output,
|
|
533
|
+
columns: ['srid', 'name', 'licence', 'mode', 'registered', 'evidence'],
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
// ── skills sealed ────────────────────────────────────────────────────────
|
|
538
|
+
// SKILLRIGHTS #478 Phase 14, sealed execution pilot. Collector routes
|
|
539
|
+
// (server.js /api/v1/skills/sealed[...]). See
|
|
540
|
+
// docs/SEALED_EXECUTION_PILOT_DESIGN.md for the full design and the
|
|
541
|
+
// binding claims language below (section G, verbatim).
|
|
542
|
+
//
|
|
543
|
+
// What sealed execution IS: the model invokes a capability (tool call);
|
|
544
|
+
// the methodology runs on infrastructure the org controls; the caller
|
|
545
|
+
// gets only inputs and outputs, never the skill text. The manifest read
|
|
546
|
+
// back by list/show never contains the sealed content, only its hash.
|
|
547
|
+
//
|
|
548
|
+
// What it is NOT: a guarantee against cloning-by-behaviour, and NOT a
|
|
549
|
+
// reliable filter on invocation output (the executor's overlap check is
|
|
550
|
+
// partial at best). Every surface below says so; do not soften it.
|
|
551
|
+
const sealed = skills
|
|
552
|
+
.command('sealed')
|
|
553
|
+
.description('Manage sealed-execution skills: methodology runs on org infrastructure, callers get inputs/outputs only (pilot)');
|
|
554
|
+
|
|
555
|
+
// The binding sentence, verbatim. Printed on every `sealed add` success
|
|
556
|
+
// (docs/SEALED_EXECUTION_PILOT_DESIGN.md).
|
|
557
|
+
//
|
|
558
|
+
// SUPERSEDED 2026-09-11 (external audit): the original section G wording
|
|
559
|
+
// claimed the text stays out of "every model's context and every provider's
|
|
560
|
+
// training pipeline". The executor sends the plaintext to the executing
|
|
561
|
+
// model, so that half was contradicted on every successful invocation.
|
|
562
|
+
const SEALED_SECTION_G_SENTENCE = "Sealed execution keeps the text of your methodology out of the calling model's context; the executing model and its provider do see it, under that provider's data terms, and nothing here prevents someone who can call the capability from learning what it does.";
|
|
563
|
+
|
|
564
|
+
sealed
|
|
565
|
+
.command('list')
|
|
566
|
+
.description('List sealed skill manifests for this org (manifest only, never the sealed content)')
|
|
567
|
+
.action(async () => {
|
|
568
|
+
auth.requireAuth();
|
|
569
|
+
const ep = resolveEndpoint('skills.sealed.list');
|
|
570
|
+
const data = await client.get(ep.path, { service: ep.service });
|
|
571
|
+
const rows = data.sealed ?? data;
|
|
572
|
+
|
|
573
|
+
formatOutput({
|
|
574
|
+
columns: ['sealedId', 'name', 'contentSha256', 'createdAt'],
|
|
575
|
+
headers: ['Sealed ID', 'Name', 'Content SHA256', 'Created'],
|
|
576
|
+
rows: (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
577
|
+
sealedId: r.sealedId,
|
|
578
|
+
name: r.name,
|
|
579
|
+
contentSha256: (r.contentSha256 || '').slice(0, 12),
|
|
580
|
+
createdAt: r.createdAt,
|
|
581
|
+
})),
|
|
582
|
+
}, { format: program.opts().output, emptyMessage: 'No sealed skills found.' });
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
sealed
|
|
586
|
+
.command('show')
|
|
587
|
+
.description('Show a sealed skill\'s manifest (name, description, input contract, content hash). Manifest only.')
|
|
588
|
+
.argument('<sealedId>', 'sealed skill ID')
|
|
589
|
+
.action(async (sealedId) => {
|
|
590
|
+
auth.requireAuth();
|
|
591
|
+
const ep = resolveEndpoint('skills.sealed.get', { sealedId });
|
|
592
|
+
const data = await client.get(ep.path, { service: ep.service });
|
|
593
|
+
printInfo('Manifest only. The sealed content is never returned by the API.');
|
|
594
|
+
formatDetail(data, { format: program.opts().output, title: `Sealed skill: ${sealedId}` });
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
sealed
|
|
598
|
+
.command('add')
|
|
599
|
+
.description('Seal a local skill: its content is uploaded once, executed on org infrastructure thereafter, and never returned to a caller')
|
|
600
|
+
.argument('<path>', 'skill directory (its SKILL.md is read) or a single file')
|
|
601
|
+
.requiredOption('--name <name>', 'sealed skill name')
|
|
602
|
+
.option('--description <text>', 'description')
|
|
603
|
+
.option('--contract <file>', 'JSON file describing the expected input contract')
|
|
604
|
+
.action(async (target, options) => {
|
|
605
|
+
auth.requireAuth();
|
|
606
|
+
const resolved = path.resolve(target);
|
|
607
|
+
if (!existsSync(resolved)) {
|
|
608
|
+
throw new Error(`No such file or directory: ${resolved}`);
|
|
609
|
+
}
|
|
610
|
+
const isDir = statSync(resolved).isDirectory();
|
|
611
|
+
const contentPath = isDir ? path.join(resolved, 'SKILL.md') : resolved;
|
|
612
|
+
if (!existsSync(contentPath)) {
|
|
613
|
+
throw new Error(`No SKILL.md found in ${resolved}.`);
|
|
614
|
+
}
|
|
615
|
+
const content = readFileSync(contentPath, 'utf8');
|
|
616
|
+
|
|
617
|
+
let inputContract = {};
|
|
618
|
+
if (options.contract) {
|
|
619
|
+
inputContract = parseJSON(readFileSync(options.contract, 'utf8'), 'input contract file');
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const body = { name: options.name, inputContract, content };
|
|
623
|
+
if (options.description) body.description = options.description;
|
|
624
|
+
|
|
625
|
+
try {
|
|
626
|
+
const ep = resolveEndpoint('skills.sealed.create');
|
|
627
|
+
const data = await client.post(ep.path, { service: ep.service, body });
|
|
628
|
+
printSuccess(`Sealed. sealedId ${data.sealedId}`);
|
|
629
|
+
formatDetail(data, { format: program.opts().output, title: 'Sealed skill' });
|
|
630
|
+
printInfo(SEALED_SECTION_G_SENTENCE);
|
|
631
|
+
} catch (err) {
|
|
632
|
+
if (err.status === 503 && err.code === 'sealed_unavailable') {
|
|
633
|
+
throw new Error(
|
|
634
|
+
'sealed_unavailable: this server has no sealed-content encryption key configured. '
|
|
635
|
+
+ 'An org admin must register one before any skill can be sealed.'
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
throw err;
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
sealed
|
|
643
|
+
.command('invoke')
|
|
644
|
+
.description('Invoke a sealed skill: the model gets only the output, never the methodology text. Output filtering is partial, not guaranteed; the caller\'s input still transits the executing provider.')
|
|
645
|
+
.argument('<sealedId>', 'sealed skill ID')
|
|
646
|
+
.option('--input <text>', 'input text')
|
|
647
|
+
.option('--input-file <path>', 'read the input from a file (overrides --input)')
|
|
648
|
+
.action(async (sealedId, options) => {
|
|
649
|
+
auth.requireAuth();
|
|
650
|
+
let input;
|
|
651
|
+
if (options.inputFile) {
|
|
652
|
+
input = readFileSync(options.inputFile, 'utf8');
|
|
653
|
+
} else if (options.input) {
|
|
654
|
+
input = options.input;
|
|
655
|
+
} else {
|
|
656
|
+
throw new Error('Input is required. Use --input <text> or --input-file <path>.');
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
try {
|
|
660
|
+
const ep = resolveEndpoint('skills.sealed.invoke', { sealedId });
|
|
661
|
+
// A sealed invocation is a full LLM run: the collector allows the
|
|
662
|
+
// executor 120s, so the default 30s client timeout hangs up on runs
|
|
663
|
+
// that then complete (and are witnessed) server-side with nobody to
|
|
664
|
+
// receive the output. Proven live on the first pilot drive 2026-09-11
|
|
665
|
+
// (73s run, orphaned output). 150s = executor deadline + margin.
|
|
666
|
+
const data = await client.post(ep.path, { service: ep.service, body: { input }, timeout: 150000 });
|
|
667
|
+
|
|
668
|
+
if (data.refused) {
|
|
669
|
+
printWarning(
|
|
670
|
+
`Output withheld by the disclosure check (${data.error || 'sealed_output_withheld'}). `
|
|
671
|
+
+ `Evidence envelope: ${data.evidence?.envelopeId || '(none)'}.`
|
|
672
|
+
);
|
|
673
|
+
formatDetail(data.evidence || {}, { format: program.opts().output, title: 'Evidence' });
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
printSuccess('Invocation complete.');
|
|
678
|
+
console.log(data.output);
|
|
679
|
+
formatDetail(data.evidence || {}, { format: program.opts().output, title: 'Evidence' });
|
|
680
|
+
} catch (err) {
|
|
681
|
+
if (err.status === 429 || err.status === 502) {
|
|
682
|
+
throw new Error(`Sealed invocation failed: ${err.code}`);
|
|
683
|
+
}
|
|
684
|
+
throw err;
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
}
|