quoxcli 0.5.0 → 0.6.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/CHANGELOG.md +65 -1
- package/lib/api/endpoints/admin.js +21 -0
- package/lib/api/endpoints/calendarq.js +8 -0
- package/lib/api/endpoints/governance.js +7 -0
- package/lib/api/endpoints/inboxq.js +39 -0
- package/lib/api/endpoints/index.js +4 -0
- package/lib/api/endpoints/lens.js +34 -0
- package/lib/api/endpoints/lucid.js +18 -0
- package/lib/api/endpoints/mcp.js +35 -0
- package/lib/api/endpoints/mon.js +6 -7
- package/lib/api/endpoints/notesq.js +2 -0
- package/lib/api/endpoints/quoxplan.js +51 -0
- package/lib/api/endpoints/v1.js +57 -0
- package/lib/commands/actions.js +51 -13
- package/lib/commands/admin.js +86 -0
- package/lib/commands/approvals.js +5 -1
- package/lib/commands/calendar.js +27 -0
- package/lib/commands/chat.js +1 -1
- package/lib/commands/contacts.js +2 -0
- package/lib/commands/fleet.js +164 -0
- package/lib/commands/fleetRecipe.js +174 -0
- package/lib/commands/lens.js +268 -0
- package/lib/commands/lucid.js +6 -0
- package/lib/commands/lucidGoals.js +423 -0
- package/lib/commands/mail.js +287 -7
- package/lib/commands/mcp.js +190 -0
- package/lib/commands/mon.js +14 -0
- package/lib/commands/monitoring.js +73 -1
- package/lib/commands/notesSeal.js +380 -22
- package/lib/commands/offload.js +51 -10
- package/lib/commands/plan.js +507 -54
- package/lib/commands/research.js +78 -0
- package/lib/commands/settings.js +7 -1
- package/lib/commands/volt.js +6 -1
- package/lib/commands/wazuh.js +74 -9
- package/lib/commands/wazuhIntake.js +55 -6
- package/lib/core/commandRegistrars.js +2 -0
- package/lib/core/config.js +14 -1
- package/lib/core/offload.js +83 -0
- package/lib/core/output.js +29 -4
- package/lib/core/selfUpdate.js +2 -0
- package/lib/core/version.js +15 -2
- package/lib/dragon/select.js +2 -2
- package/lib/quoxon/api.js +4 -0
- package/lib/quoxon/app.js +258 -49
- package/lib/quoxon/approvalsView.js +3 -1
- package/lib/quoxon/ask.js +4 -2
- package/lib/quoxon/bang.js +7 -1
- package/lib/quoxon/commandShort.js +1 -1
- package/lib/quoxon/commands.js +5 -5
- package/lib/quoxon/delegationDragon.js +49 -0
- package/lib/quoxon/dnd.js +47 -0
- package/lib/quoxon/dragonCompanion.js +24 -12
- package/lib/quoxon/events.js +12 -1
- package/lib/quoxon/extensions.js +2 -0
- package/lib/quoxon/foreignText.js +49 -0
- package/lib/quoxon/inboxView.js +5 -5
- package/lib/quoxon/launcher.js +2 -1
- package/lib/quoxon/loopCard.js +2 -1
- package/lib/quoxon/main.js +8 -2
- package/lib/quoxon/onboarding/dockerOffer.js +3 -1
- package/lib/quoxon/onboarding/view.js +1 -1
- package/lib/quoxon/render.js +5 -2
- package/lib/quoxon/resend.js +14 -0
- package/lib/quoxon/rows.js +3 -1
- package/lib/quoxon/secretShape.js +2 -0
- package/lib/quoxon/slash/broadcast.js +30 -6
- package/lib/quoxon/slash/broadcastRun.js +12 -6
- package/lib/quoxon/slash/cliLeaves.json +54 -0
- package/lib/quoxon/slash/dashboard.js +2 -0
- package/lib/quoxon/slash/listView.js +2 -1
- package/lib/quoxon/slash/palette.js +41 -0
- package/lib/quoxon/slash/paletteController.js +6 -2
- package/lib/quoxon/slash/restart.js +2 -0
- package/lib/quoxon/slotChat.js +7 -3
- package/lib/quoxon/slotRun.js +2 -0
- package/lib/quoxon/webPty.js +15 -0
- package/lib/quoxon/whatsnew.jsonl +7 -0
- package/lib/render/planBrief.js +614 -0
- package/lib/toolOutputShape.js +6 -3
- package/package.json +1 -1
- package/vendor/dragonlab/SOURCE +3 -3
- package/vendor/dragonlab/design/architect/v2/architect-master.svg +67 -0
- package/vendor/dragonlab/design/architect/v2/character.mjs +83 -0
- package/vendor/dragonlab/design/architect/v2/review.mjs +35 -0
- package/vendor/dragonlab/design/architect/v2/watch.mjs +56 -0
- package/vendor/dragonlab/design/archivist/v2/archivist-master.svg +77 -0
- package/vendor/dragonlab/design/archivist/v3/character.mjs +56 -0
- package/vendor/dragonlab/design/archivist/v4/character.mjs +46 -0
- package/vendor/dragonlab/design/archivist/v4/review.mjs +35 -0
- package/vendor/dragonlab/design/archivist/v4/watch.mjs +56 -0
- package/vendor/dragonlab/design/argus/v2/character.mjs +69 -0
- package/vendor/dragonlab/design/argus/v3/argus-master.svg +68 -0
- package/vendor/dragonlab/design/argus/v3/character.mjs +63 -0
- package/vendor/dragonlab/design/argus/v3/review.mjs +35 -0
- package/vendor/dragonlab/design/argus/v3/watch.mjs +56 -0
- package/vendor/dragonlab/design/daedalus/v2/character.mjs +49 -0
- package/vendor/dragonlab/design/daedalus/v3/daedalus-master.svg +65 -0
- package/vendor/dragonlab/design/daedalus/v4/character.mjs +47 -0
- package/vendor/dragonlab/design/daedalus/v5/character.mjs +66 -0
- package/vendor/dragonlab/design/daedalus/v6/character.mjs +32 -0
- package/vendor/dragonlab/design/daedalus/v6/review.mjs +35 -0
- package/vendor/dragonlab/design/daedalus/v6/watch.mjs +56 -0
- package/vendor/dragonlab/design/janus/v1/janus-master.svg +70 -0
- package/vendor/dragonlab/design/janus/v2/character.mjs +56 -0
- package/vendor/dragonlab/design/janus/v3/character.mjs +7 -0
- package/vendor/dragonlab/design/janus/v4/character.mjs +150 -0
- package/vendor/dragonlab/design/janus/v5/character.mjs +55 -0
- package/vendor/dragonlab/design/janus/v5/review.mjs +35 -0
- package/vendor/dragonlab/design/janus/v5/watch.mjs +56 -0
- package/vendor/dragonlab/design/roady/v1/roady-master.svg +67 -0
- package/vendor/dragonlab/design/roady/v2/character.mjs +69 -0
- package/vendor/dragonlab/design/roady/v3/character.mjs +91 -0
- package/vendor/dragonlab/design/roady/v3/review.mjs +35 -0
- package/vendor/dragonlab/design/roady/v3/watch.mjs +56 -0
- package/vendor/dragonlab/design/weaver/v3/weaver-master.svg +57 -0
- package/vendor/dragonlab/design/weaver/v4/character.mjs +56 -0
- package/vendor/dragonlab/design/weaver/v5/character.mjs +52 -0
- package/vendor/dragonlab/design/weaver/v6/character.mjs +88 -0
- package/vendor/dragonlab/design/weaver/v6/review.mjs +35 -0
- package/vendor/dragonlab/design/weaver/v6/watch.mjs +56 -0
- package/vendor/dragonlab/integration/companions-v1/architect.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/archivist.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/argus.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/characters.mjs +86 -0
- package/vendor/dragonlab/integration/companions-v1/daedalus.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/janus.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/roady.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/view.mjs +4 -1
- package/vendor/dragonlab/integration/companions-v1/weaver.mjs +2 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `quox lens`: QuoxLens, apply a declared-stance Lens to a target text.
|
|
3
|
+
* Collector routes under /api/v1/lens (services/collector/routes/quoxlens.js), behind
|
|
4
|
+
* requireServiceOrUserAuth + requirePluginEntitlement('quoxlens').
|
|
5
|
+
*
|
|
6
|
+
* quox lens list every Lens, its stance, measured lift, unproven flag
|
|
7
|
+
* quox lens show <id> one Lens in full (blind spots, finding classes, body)
|
|
8
|
+
* quox lens apply <lensId> --file <path|-> apply one Lens to text from a file or stdin (-)
|
|
9
|
+
* [--target-label <s>] a human label for the target (artifact title)
|
|
10
|
+
* quox lens panel --file <path|-> the Critic panel: several Lenses, repeated runs, run consensus
|
|
11
|
+
* [--lenses a,b] [--runs N] [--min-agreement K] [--depth quick|standard|thorough]
|
|
12
|
+
* [--target-label <s>] [--allow-unproven]
|
|
13
|
+
*
|
|
14
|
+
* All honour --json / -o json. An error body is not data: a non-2xx (400 unknown lens,
|
|
15
|
+
* 402 LICENSE_REQUIRED, 404, 409, 422) is reported with its status and exits 1.
|
|
16
|
+
*/
|
|
17
|
+
import { readFileSync } from 'node:fs';
|
|
18
|
+
import { createHash } from 'node:crypto';
|
|
19
|
+
import { resolveEndpoint } from '../api/endpoints.js';
|
|
20
|
+
|
|
21
|
+
const isJson = (program) => program.opts().output === 'json';
|
|
22
|
+
const printJson = (d) => console.log(JSON.stringify(d, null, 2));
|
|
23
|
+
|
|
24
|
+
async function readTarget(file, ctx) {
|
|
25
|
+
if (file === '-') {
|
|
26
|
+
const input = ctx.stdin || process.stdin;
|
|
27
|
+
let buf = '';
|
|
28
|
+
for await (const chunk of input) buf += chunk;
|
|
29
|
+
return buf;
|
|
30
|
+
}
|
|
31
|
+
return readFileSync(file, 'utf-8');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Stable per text, so finding fingerprints (lens + artifact.id + class + location) stay
|
|
35
|
+
// stable when the same target is re-checked.
|
|
36
|
+
function artifactIdFor(text) {
|
|
37
|
+
return `cli-${createHash('sha256').update(text).digest('hex').slice(0, 16)}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function describeError(err) {
|
|
41
|
+
const body = err.body || {};
|
|
42
|
+
const parts = [`HTTP ${err.status || err.statusCode}`];
|
|
43
|
+
if (body.error && !String(err.message || '').includes(body.error)) parts.push(body.error);
|
|
44
|
+
if (err.message) parts.push(err.message);
|
|
45
|
+
const detail = body.detail || err.details;
|
|
46
|
+
if (detail && typeof detail === 'string' && !String(err.message || '').includes(detail)) parts.push(`(${detail})`);
|
|
47
|
+
if (body.plugin) parts.push(`[plugin: ${body.plugin}]`);
|
|
48
|
+
return parts.join(' ');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const PANEL_DEPTHS = ['quick', 'standard', 'thorough'];
|
|
52
|
+
// One lens call can take up to 5 minutes server-side and a panel runs lenses x runs calls in sequence.
|
|
53
|
+
const PANEL_CALL_WAIT_MS = 5.5 * 60 * 1000;
|
|
54
|
+
const PANEL_WAIT_CAP_MS = 30 * 60 * 1000;
|
|
55
|
+
|
|
56
|
+
function parseWholeNumber(value, flag, min, max) {
|
|
57
|
+
if (value === undefined) return { value: undefined };
|
|
58
|
+
const n = Number(value);
|
|
59
|
+
if (!/^\d+$/.test(String(value).trim()) || !Number.isInteger(n) || n < min || n > max) {
|
|
60
|
+
return { error: `${flag} must be a whole number from ${min} to ${max}, got "${value}"` };
|
|
61
|
+
}
|
|
62
|
+
return { value: n };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function formatUsage(verdict) {
|
|
66
|
+
const u = verdict && verdict.usage;
|
|
67
|
+
if (!u) return `not available (${(verdict && verdict.usage_reason) || 'no reason given'})`;
|
|
68
|
+
const parts = [`input ${u.input_tokens}`, `output ${u.output_tokens}`];
|
|
69
|
+
if (u.cache_creation_input_tokens) parts.push(`cache write ${u.cache_creation_input_tokens}`);
|
|
70
|
+
if (u.cache_read_input_tokens) parts.push(`cache read ${u.cache_read_input_tokens}`);
|
|
71
|
+
const calls = `${u.calls} model call${u.calls === 1 ? '' : 's'}`;
|
|
72
|
+
const price = typeof u.list_price_usd === 'number' ? `, list price $${u.list_price_usd.toFixed(4)}` : '';
|
|
73
|
+
return `${parts.join(', ')} tokens over ${calls}${price}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function registerLensCommands(program, ctx) {
|
|
77
|
+
const { client, auth } = ctx;
|
|
78
|
+
|
|
79
|
+
// An error body is not data: a non-2xx is reported with its status and exits 1 (returns null).
|
|
80
|
+
async function guarded(request) {
|
|
81
|
+
if (auth && typeof auth.requireAuth === 'function') auth.requireAuth();
|
|
82
|
+
try {
|
|
83
|
+
return await request();
|
|
84
|
+
} catch (err) {
|
|
85
|
+
if (err && (err.status || err.statusCode)) {
|
|
86
|
+
console.error(`QuoxLens request failed: ${describeError(err)}`);
|
|
87
|
+
process.exitCode = 1;
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const lens = program
|
|
95
|
+
.command('lens')
|
|
96
|
+
.description('QuoxLens: apply a declared-stance Lens to a target and get evidence-quoted findings');
|
|
97
|
+
|
|
98
|
+
lens
|
|
99
|
+
.command('list')
|
|
100
|
+
.description('List every Lens with its stance, measured lift and unproven flag')
|
|
101
|
+
.action(async () => {
|
|
102
|
+
const ep = resolveEndpoint('lens.list');
|
|
103
|
+
const data = await guarded(() => client.get(ep.path, { service: ep.service }));
|
|
104
|
+
if (data === null) return;
|
|
105
|
+
const lenses = Array.isArray(data.lenses) ? data.lenses : [];
|
|
106
|
+
if (isJson(program)) { printJson(lenses); return; }
|
|
107
|
+
if (!lenses.length) { console.log('No lenses installed.'); return; }
|
|
108
|
+
for (const l of lenses) {
|
|
109
|
+
if (l.error) { console.log(`${l.id} [${l.category || '-'}] ERROR: ${l.error}`); continue; }
|
|
110
|
+
const proof = l.unproven ? 'unproven' : 'measured';
|
|
111
|
+
const ver = l.version ? ` v${l.version}` : '';
|
|
112
|
+
console.log(`${l.id}${ver} [${l.category || '-'}] ${proof} ${l.name || ''}`);
|
|
113
|
+
if (l.description) console.log(` ${l.description}`);
|
|
114
|
+
}
|
|
115
|
+
console.log(`\n${lenses.length} lens${lenses.length === 1 ? '' : 'es'}`);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
lens
|
|
119
|
+
.command('show <id>')
|
|
120
|
+
.description('Show one Lens in full: blind spots, finding classes, prohibited, eval results, body')
|
|
121
|
+
.action(async (id) => {
|
|
122
|
+
const ep = resolveEndpoint('lens.get', { id });
|
|
123
|
+
const data = await guarded(() => client.get(ep.path, { service: ep.service }));
|
|
124
|
+
if (data === null) return;
|
|
125
|
+
if (isJson(program)) { printJson(data); return; }
|
|
126
|
+
const list = (a) => (Array.isArray(a) && a.length ? a.join(', ') : '-');
|
|
127
|
+
console.log(`${data.name || data.id} (${data.id})`);
|
|
128
|
+
if (data.description) console.log(data.description);
|
|
129
|
+
console.log(`version: ${data.version || '-'}`);
|
|
130
|
+
console.log(`kind: ${data.kind || '-'}`);
|
|
131
|
+
if (data.edition) console.log(`edition: ${data.edition}`);
|
|
132
|
+
if (data.author) console.log(`author: ${data.author}`);
|
|
133
|
+
console.log(`unproven: ${data.unproven ? 'yes (no measured lift)' : 'no'}`);
|
|
134
|
+
console.log(`tags: ${list(data.tags)}`);
|
|
135
|
+
console.log(`finding classes: ${list(data.finding_classes)}`);
|
|
136
|
+
console.log(`blind spots: ${list(data.blind_spots)}`);
|
|
137
|
+
console.log(`prohibited: ${list(data.prohibited)}`);
|
|
138
|
+
console.log(`eval results: ${Array.isArray(data.results) ? data.results.length : 0}`);
|
|
139
|
+
if (data.body) console.log(`\n${data.body}`);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
lens
|
|
143
|
+
.command('apply <lensId>')
|
|
144
|
+
.description('Apply one Lens to target text from a file, or stdin with --file -')
|
|
145
|
+
.requiredOption('--file <path>', 'read the target text from this file, or - for stdin')
|
|
146
|
+
.option('--target-label <label>', 'a human label for the target (sent as the artifact title)')
|
|
147
|
+
.action(async (lensId, options) => {
|
|
148
|
+
let text;
|
|
149
|
+
try {
|
|
150
|
+
text = await readTarget(options.file, ctx);
|
|
151
|
+
} catch (err) {
|
|
152
|
+
console.error(`Could not read target ${options.file === '-' ? 'from stdin' : `file ${options.file}`}: ${err.message}`);
|
|
153
|
+
process.exitCode = 1;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (!text || !text.trim()) {
|
|
157
|
+
console.error('Not applied: the target text is empty. Nothing was sent.');
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const artifact = { kind: 'text', id: artifactIdFor(text), content: text };
|
|
162
|
+
if (options.targetLabel) artifact.title = options.targetLabel;
|
|
163
|
+
const ep = resolveEndpoint('lens.apply');
|
|
164
|
+
const body = { lens_id: lensId, mode: 'single', artifact };
|
|
165
|
+
// A lens apply is one model call (up to 5 min on the server); the client default (30s) gave up on real runs.
|
|
166
|
+
const data = await guarded(() => client.post(ep.path, { service: ep.service, body, timeout: PANEL_CALL_WAIT_MS, retries: 0 }));
|
|
167
|
+
if (data === null) return;
|
|
168
|
+
if (isJson(program)) { printJson(data); return; }
|
|
169
|
+
|
|
170
|
+
const v = data.verdict || {};
|
|
171
|
+
const a = data.applied || {};
|
|
172
|
+
console.log(`lens: ${a.lens_id || lensId}${a.version ? ` v${a.version}` : ''}${a.unproven ? ' (unproven: no measured lift)' : ''}`);
|
|
173
|
+
if (artifact.title) console.log(`target: ${artifact.title}`);
|
|
174
|
+
console.log(`disposition: ${v.disposition || '-'}`);
|
|
175
|
+
console.log(`confidence: ${v.confidence ?? '-'}`);
|
|
176
|
+
if (a.model) console.log(`model: ${a.model}`);
|
|
177
|
+
if (v.summary) console.log(`\n${v.summary}`);
|
|
178
|
+
const findings = Array.isArray(data.findings) ? data.findings : [];
|
|
179
|
+
console.log(`\nfindings: ${findings.length}`);
|
|
180
|
+
for (const f of findings) {
|
|
181
|
+
console.log(` [${f.severity || '-'}] ${f.class || '-'}: ${f.claim || ''}`);
|
|
182
|
+
for (const e of Array.isArray(f.evidence) ? f.evidence : []) {
|
|
183
|
+
if (e && e.quote) console.log(` "${e.quote}"${e.at ? ` (${e.at})` : ''}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (Array.isArray(a.blind_spots) && a.blind_spots.length) console.log(`\nblind spots: ${a.blind_spots.join(', ')}`);
|
|
187
|
+
if (data.evidence && data.evidence.corr) console.log(`evidence: ${data.evidence.corr}`);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
lens
|
|
191
|
+
.command('panel')
|
|
192
|
+
.description('The Critic panel: several Lenses over one target, repeated runs; keeps findings that recur across runs')
|
|
193
|
+
.requiredOption('--file <path>', 'read the target text from this file, or - for stdin')
|
|
194
|
+
.option('--lenses <ids>', 'comma-separated lens ids, 2 to 5 (server default: honesty-and-claims,enterprise-compliance-auditor)')
|
|
195
|
+
.option('--runs <n>', 'how many times to run the panel, 1 to 5 (server default 1)')
|
|
196
|
+
.option('--min-agreement <k>', 'runs a finding must appear in to be kept (server default: a majority of runs)')
|
|
197
|
+
.option('--depth <depth>', `depth profile: ${PANEL_DEPTHS.join(', ')} (server default standard)`)
|
|
198
|
+
.option('--target-label <label>', 'a human label for the target (sent as the artifact title)')
|
|
199
|
+
.option('--allow-unproven', 'run lenses with no measured lift anyway (the result says so)')
|
|
200
|
+
.action(async (options) => {
|
|
201
|
+
const runs = parseWholeNumber(options.runs, '--runs', 1, 5);
|
|
202
|
+
const minAgreement = parseWholeNumber(options.minAgreement, '--min-agreement', 1, 5);
|
|
203
|
+
const bad = runs.error || minAgreement.error
|
|
204
|
+
|| (options.depth !== undefined && !PANEL_DEPTHS.includes(options.depth) ? `--depth must be one of ${PANEL_DEPTHS.join(', ')}, got "${options.depth}"` : null);
|
|
205
|
+
if (bad) {
|
|
206
|
+
console.error(`Not run: ${bad}. Nothing was sent.`);
|
|
207
|
+
process.exitCode = 1;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
let text;
|
|
211
|
+
try {
|
|
212
|
+
text = await readTarget(options.file, ctx);
|
|
213
|
+
} catch (err) {
|
|
214
|
+
console.error(`Could not read target ${options.file === '-' ? 'from stdin' : `file ${options.file}`}: ${err.message}`);
|
|
215
|
+
process.exitCode = 1;
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (!text || !text.trim()) {
|
|
219
|
+
console.error('Not run: the target text is empty. Nothing was sent.');
|
|
220
|
+
process.exitCode = 1;
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const artifact = { kind: 'text', id: artifactIdFor(text), content: text };
|
|
224
|
+
if (options.targetLabel) artifact.title = options.targetLabel;
|
|
225
|
+
const body = { artifact };
|
|
226
|
+
const lensIds = options.lenses ? options.lenses.split(',').map((s) => s.trim()).filter(Boolean) : null;
|
|
227
|
+
if (lensIds) body.lens_ids = lensIds;
|
|
228
|
+
if (runs.value !== undefined) body.runs = runs.value;
|
|
229
|
+
if (minAgreement.value !== undefined) body.min_agreement = minAgreement.value;
|
|
230
|
+
if (options.depth) body.depth = options.depth;
|
|
231
|
+
if (options.allowUnproven) body.allow_unproven = true;
|
|
232
|
+
|
|
233
|
+
const calls = (lensIds ? lensIds.length : 2) * (runs.value || 1);
|
|
234
|
+
const timeout = Math.min(calls * PANEL_CALL_WAIT_MS, PANEL_WAIT_CAP_MS);
|
|
235
|
+
const ep = resolveEndpoint('lens.panel');
|
|
236
|
+
// retries: 0, a panel is a paid model run; a retried POST would run it twice.
|
|
237
|
+
const data = await guarded(() => client.post(ep.path, { service: ep.service, body, timeout, retries: 0 }));
|
|
238
|
+
if (data === null) return;
|
|
239
|
+
if (isJson(program)) { printJson(data); return; }
|
|
240
|
+
|
|
241
|
+
const v = data.verdict || {};
|
|
242
|
+
const lensList = (Array.isArray(data.lenses) ? data.lenses : []).map((l) => `${l.id}${l.version ? ` v${l.version}` : ''}${l.unproven ? ' (unproven)' : ''}`);
|
|
243
|
+
console.log(`lenses: ${lensList.join(', ') || '-'}`);
|
|
244
|
+
if (artifact.title) console.log(`target: ${artifact.title}`);
|
|
245
|
+
console.log(`disposition: ${v.disposition || '-'}`);
|
|
246
|
+
console.log(`runs: ${data.runs_completed ?? '-'} of ${data.runs_requested ?? '-'} completed, a finding needs ${data.min_agreement ?? '-'} to be kept${data.depth ? `, depth ${data.depth}` : ''}`);
|
|
247
|
+
if (data.partial) console.log(`PARTIAL: stopped early (${data.stop_reason || 'unknown'}${data.stop_detail ? `: ${data.stop_detail}` : ''})`);
|
|
248
|
+
if (Array.isArray(data.unproven_lenses) && data.unproven_lenses.length) console.log(`unproven: ${data.unproven_lenses.join(', ')} (no measured lift; run because --allow-unproven was given)`);
|
|
249
|
+
if (v.summary) console.log(`\n${v.summary}`);
|
|
250
|
+
const findings = Array.isArray(data.findings) ? data.findings : [];
|
|
251
|
+
console.log(`\nfindings: ${findings.length}`);
|
|
252
|
+
for (const f of findings) {
|
|
253
|
+
const agreement = f.agreement ? `${f.agreement.label || `${f.agreement.runs}/${f.agreement.of}`} runs` : '-';
|
|
254
|
+
const by = Array.isArray(f.lens_ids) && f.lens_ids.length ? ` [${f.lens_ids.join(', ')}]` : '';
|
|
255
|
+
console.log(` [${f.severity || '-'}] ${f.class || '-'}: ${f.claim || ''} (${agreement})${by}`);
|
|
256
|
+
for (const e of Array.isArray(f.evidence) ? f.evidence : []) {
|
|
257
|
+
if (e && e.quote) console.log(` "${e.quote}"${e.at ? ` (${e.at})` : ''}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const below = Array.isArray(data.below_agreement) ? data.below_agreement : [];
|
|
261
|
+
if (below.length) {
|
|
262
|
+
console.log(`\nbelow agreement (not kept): ${below.length}`);
|
|
263
|
+
for (const f of below) console.log(` ${f.class || '-'}: ${f.claim || ''} (${f.agreement ? f.agreement.label : '-'} runs)`);
|
|
264
|
+
}
|
|
265
|
+
console.log(`\nusage: ${formatUsage(v)}`);
|
|
266
|
+
if (data.evidence && data.evidence.corr) console.log(`evidence: ${data.evidence.corr}`);
|
|
267
|
+
});
|
|
268
|
+
}
|
package/lib/commands/lucid.js
CHANGED
|
@@ -9,12 +9,14 @@
|
|
|
9
9
|
* quox lucid teach <target> <field>=<value> | lessons | forget <id>
|
|
10
10
|
* quox lucid herald [set key=value... | test] when LUCID speaks first, and what it has said
|
|
11
11
|
* quox lucid blind sources live/silent/failing/never heard, and what doesn't add up
|
|
12
|
+
* quox lucid goals ... your goals and decisions, so LUCID ranks against them (see lucidGoals.js)
|
|
12
13
|
*/
|
|
13
14
|
import { formatOutput, printSuccess } from '../core/output.js';
|
|
14
15
|
import { line as glyphLine, printEvent } from '../core/quoxglyph.js';
|
|
15
16
|
import { resolveEndpoint } from '../api/endpoints.js';
|
|
16
17
|
import { confirmDestructive } from '../core/confirm.js';
|
|
17
18
|
import { resolveOrgId } from './helpers.js';
|
|
19
|
+
import { registerLucidGoalsCommands, registerLucidInsightsCommands, registerLucidBriefCommand } from './lucidGoals.js';
|
|
18
20
|
|
|
19
21
|
const SURFACE = 'cli';
|
|
20
22
|
|
|
@@ -505,6 +507,10 @@ export function registerLucidCommands(program, ctx) {
|
|
|
505
507
|
if (st.length) console.log(`Stats: ${st.map(([k, v]) => `${k}=${Array.isArray(v) ? v.join(',') : v}`).join(' · ')}`);
|
|
506
508
|
});
|
|
507
509
|
|
|
510
|
+
registerLucidGoalsCommands(lu, program, ctx);
|
|
511
|
+
registerLucidInsightsCommands(lu, program, ctx);
|
|
512
|
+
registerLucidBriefCommand(lu, program, ctx);
|
|
513
|
+
|
|
508
514
|
// ---- herald: when LUCID reaches out on its own ----
|
|
509
515
|
const LEVELS = { l1_inbox: 'filed in your Approvals inbox', l2_slot: 'nudged you in your slot', l3_call: 'called you', resolved: 'resolved' };
|
|
510
516
|
const levelWords = (l) => LEVELS[l] || l || 'not yet reached out';
|