atris 3.15.22 → 3.15.30
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/README.md +53 -0
- package/ax +1083 -0
- package/commands/aeo.js +377 -13
- package/commands/gm.js +25 -9
- package/commands/mission.js +117 -7
- package/commands/play.js +41 -9
- package/commands/sync.js +9 -4
- package/commands/xp.js +224 -1
- package/package.json +5 -2
package/commands/aeo.js
CHANGED
|
@@ -1,24 +1,98 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* atris aeo — AI Engine Optimization commands
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* atris aeo
|
|
4
|
+
* Backend-routed (require EC2):
|
|
5
|
+
* atris aeo init # create entity-graph skeleton
|
|
6
|
+
* atris aeo draft "<topic>" [opts] # generate citation-optimized article
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Local-read against ~/arena/atrisos-backend/atris/features/aeo/proof/:
|
|
9
|
+
* atris aeo log [--engine X] [--limit N] # citation attempt log
|
|
10
|
+
* atris aeo status # engine + proof + buyer summary
|
|
11
|
+
* atris aeo packet <slug> # buyer packet for a surface
|
|
12
|
+
* atris aeo proofs [--filter X] # list proof receipt categories
|
|
10
13
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
+
* Shell out to atrisos-backend/scripts/aeo_*.py:
|
|
15
|
+
* atris aeo discover <source> [...] # discovery audit
|
|
16
|
+
* atris aeo audit <source> [...] # agent-usability audit
|
|
17
|
+
*
|
|
18
|
+
* Backend root resolution: $ATRIS_BACKEND_ROOT or ~/arena/atrisos-backend.
|
|
14
19
|
*/
|
|
15
20
|
|
|
16
21
|
const fs = require('fs');
|
|
22
|
+
const os = require('os');
|
|
17
23
|
const path = require('path');
|
|
24
|
+
const { spawnSync } = require('child_process');
|
|
18
25
|
const { loadCredentials } = require('../utils/auth');
|
|
19
26
|
const { apiRequestJson } = require('../utils/api');
|
|
20
27
|
const { loadBusinesses, saveBusinesses } = require('./business');
|
|
21
28
|
|
|
29
|
+
function resolveBackendRoot() {
|
|
30
|
+
const candidates = [
|
|
31
|
+
process.env.ATRIS_BACKEND_ROOT,
|
|
32
|
+
path.join(os.homedir(), 'arena', 'atrisos-backend'),
|
|
33
|
+
].filter(Boolean);
|
|
34
|
+
for (const root of candidates) {
|
|
35
|
+
if (fs.existsSync(path.join(root, 'atris', 'features', 'aeo'))) return root;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function requireBackendRoot() {
|
|
41
|
+
const root = resolveBackendRoot();
|
|
42
|
+
if (!root) {
|
|
43
|
+
console.error('Cannot find atrisos-backend. Set $ATRIS_BACKEND_ROOT or clone to ~/arena/atrisos-backend.');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
return root;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function readJsonSafe(p) {
|
|
50
|
+
if (!fs.existsSync(p)) return null;
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.error(` warning: malformed JSON at ${p} (${err.message})`);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function pad(s, n) { s = String(s); return s.length >= n ? s.slice(0, n) : s + ' '.repeat(n - s.length); }
|
|
60
|
+
|
|
61
|
+
function assertNoExtras(sub, args, allowedFlags) {
|
|
62
|
+
const allowed = new Set(allowedFlags);
|
|
63
|
+
const unknownFlags = args.filter((a) => a.startsWith('--') && !allowed.has(a));
|
|
64
|
+
const positional = args.filter((a) => !a.startsWith('--'));
|
|
65
|
+
if (unknownFlags.length) {
|
|
66
|
+
console.error(`Unknown flag for aeo ${sub}: ${unknownFlags.join(' ')}. Supported: ${[...allowed].join(' ') || '(none)'}`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
if (positional.length) {
|
|
70
|
+
console.error(`Unexpected argument for aeo ${sub}: ${positional.join(' ')}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function readArg(args, ...keys) {
|
|
76
|
+
for (const k of keys) {
|
|
77
|
+
const eqIdx = args.findIndex((a) => a.startsWith(`${k}=`));
|
|
78
|
+
if (eqIdx !== -1) {
|
|
79
|
+
const v = args[eqIdx].slice(k.length + 1);
|
|
80
|
+
args.splice(eqIdx, 1);
|
|
81
|
+
return v;
|
|
82
|
+
}
|
|
83
|
+
const i = args.findIndex((a) => a === k);
|
|
84
|
+
if (i === -1) continue;
|
|
85
|
+
const v = args[i + 1];
|
|
86
|
+
if (v === undefined || v.startsWith('--')) {
|
|
87
|
+
console.error(`Flag ${k} requires a value.`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
args.splice(i, 2);
|
|
91
|
+
return v;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
22
96
|
function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); }
|
|
23
97
|
|
|
24
98
|
async function ensureAwake(token, businessId, maxWaitSec = 90) {
|
|
@@ -83,13 +157,25 @@ function pickSlug(args) {
|
|
|
83
157
|
|
|
84
158
|
function printHelp() {
|
|
85
159
|
console.log('Usage:');
|
|
86
|
-
console.log('
|
|
87
|
-
console.log('
|
|
160
|
+
console.log(' Backend / EC2:');
|
|
161
|
+
console.log(' atris aeo init [--workspace <slug>]');
|
|
162
|
+
console.log(' atris aeo draft "<topic>" [--workspace <slug>] [--queries q1,q2] [--slug X] [--url URL]');
|
|
163
|
+
console.log('');
|
|
164
|
+
console.log(' Local read (atris/features/aeo/proof/):');
|
|
165
|
+
console.log(' atris aeo log [--engine X] [--limit N] [--json]');
|
|
166
|
+
console.log(' atris aeo status [--json]');
|
|
167
|
+
console.log(' atris aeo packet <slug> [--json]');
|
|
168
|
+
console.log(' atris aeo proofs [--filter X]');
|
|
169
|
+
console.log('');
|
|
170
|
+
console.log(' Script wrappers (scripts/aeo_*.py):');
|
|
171
|
+
console.log(' atris aeo discover <source> [--question Q ...] [--canonical-url URL] [--out-dir DIR]');
|
|
172
|
+
console.log(' atris aeo audit <source> [--baseline B] [--canonical-url URL] [--out-dir DIR]');
|
|
88
173
|
console.log('');
|
|
89
174
|
console.log('Examples:');
|
|
90
|
-
console.log(' atris aeo
|
|
91
|
-
console.log(' atris aeo
|
|
92
|
-
console.log(' atris aeo
|
|
175
|
+
console.log(' atris aeo log --engine perplexity --limit 5');
|
|
176
|
+
console.log(' atris aeo packet pallet');
|
|
177
|
+
console.log(' atris aeo status');
|
|
178
|
+
console.log(' atris aeo discover https://atris.ai/aeo --canonical-url https://atris.ai/aeo');
|
|
93
179
|
}
|
|
94
180
|
|
|
95
181
|
async function aeoInit(args) {
|
|
@@ -183,12 +269,290 @@ async function aeoDraft(args) {
|
|
|
183
269
|
if (data.hint) console.log(` hint: ${data.hint}`);
|
|
184
270
|
}
|
|
185
271
|
|
|
272
|
+
// ---------- LOCAL READ SUBCOMMANDS ----------
|
|
273
|
+
|
|
274
|
+
function loadCitationAttempts(root) {
|
|
275
|
+
const dir = path.join(root, 'atris', 'features', 'aeo', 'proof', 'live-citation-attempts');
|
|
276
|
+
if (!fs.existsSync(dir)) return [];
|
|
277
|
+
const rows = [];
|
|
278
|
+
for (const file of fs.readdirSync(dir)) {
|
|
279
|
+
if (!file.endsWith('.json')) continue;
|
|
280
|
+
const data = readJsonSafe(path.join(dir, file));
|
|
281
|
+
if (!data || !Array.isArray(data.attempts)) continue;
|
|
282
|
+
for (const attempt of data.attempts) {
|
|
283
|
+
if (!attempt || typeof attempt !== 'object') continue;
|
|
284
|
+
const cited = attempt.answer_cites_target_url === true;
|
|
285
|
+
const mentioned = attempt.answer_mentions_target_entity === true;
|
|
286
|
+
let status;
|
|
287
|
+
if (cited) status = 'verified';
|
|
288
|
+
else if (mentioned) status = 'pending';
|
|
289
|
+
else status = 'failed';
|
|
290
|
+
const str = (v) => (typeof v === 'string' ? v : '');
|
|
291
|
+
const arr = (v) => (Array.isArray(v) ? v : []);
|
|
292
|
+
rows.push({
|
|
293
|
+
file,
|
|
294
|
+
attempted_at: str(data.attempted_at),
|
|
295
|
+
engine: str(data.engine),
|
|
296
|
+
prompt_id: str(attempt.prompt_id),
|
|
297
|
+
prompt: str(attempt.exact_prompt),
|
|
298
|
+
target_entity: str(data.target_entity),
|
|
299
|
+
target_urls: arr(data.target_url_candidates),
|
|
300
|
+
answer_evidence_uri: str(attempt.answer_evidence_uri),
|
|
301
|
+
status,
|
|
302
|
+
competitors: arr(attempt.observed_competitor_or_alternative_entities),
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
rows.sort((a, b) => (b.attempted_at || '').localeCompare(a.attempted_at || ''));
|
|
307
|
+
return rows;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function aeoLog(args) {
|
|
311
|
+
const engine = readArg(args, '--engine', '-e');
|
|
312
|
+
const limitRaw = readArg(args, '--limit', '-n');
|
|
313
|
+
assertNoExtras('log', args, ['--json']);
|
|
314
|
+
const wantJson = args.includes('--json');
|
|
315
|
+
let limit = 20;
|
|
316
|
+
if (limitRaw != null) {
|
|
317
|
+
const trimmed = String(limitRaw).trim();
|
|
318
|
+
const parsed = parseInt(trimmed, 10);
|
|
319
|
+
if (!/^[+-]?\d+$/.test(trimmed) || !Number.isFinite(parsed) || parsed < 1) {
|
|
320
|
+
console.error(`Invalid --limit value: "${limitRaw}". Expected a positive integer.`);
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
limit = parsed;
|
|
324
|
+
}
|
|
325
|
+
const root = requireBackendRoot();
|
|
326
|
+
let rows = loadCitationAttempts(root);
|
|
327
|
+
if (engine) rows = rows.filter((r) => r.engine.toLowerCase() === engine.toLowerCase());
|
|
328
|
+
rows = rows.slice(0, limit);
|
|
329
|
+
|
|
330
|
+
if (wantJson) {
|
|
331
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (!rows.length) {
|
|
336
|
+
console.log('No citation attempts found.');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const counts = rows.reduce((acc, r) => { acc[r.status] = (acc[r.status] || 0) + 1; return acc; }, {});
|
|
341
|
+
console.log(`AEO citation log (${rows.length} attempt${rows.length === 1 ? '' : 's'})`);
|
|
342
|
+
console.log(` ${Object.entries(counts).map(([k, v]) => `${k}=${v}`).join(' ')}`);
|
|
343
|
+
console.log('');
|
|
344
|
+
console.log(` ${pad('ts', 22)}${pad('engine', 12)}${pad('prompt_id', 26)}${pad('status', 10)}`);
|
|
345
|
+
console.log(` ${'-'.repeat(70)}`);
|
|
346
|
+
for (const r of rows) {
|
|
347
|
+
console.log(` ${pad(r.attempted_at.slice(0, 19), 22)}${pad(r.engine, 12)}${pad(r.prompt_id, 26)}${pad(r.status, 10)}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function aeoStatus(args) {
|
|
352
|
+
assertNoExtras('status', args, ['--json']);
|
|
353
|
+
const wantJson = args.includes('--json');
|
|
354
|
+
const root = requireBackendRoot();
|
|
355
|
+
const proofRoot = path.join(root, 'atris', 'features', 'aeo', 'proof');
|
|
356
|
+
|
|
357
|
+
const attempts = loadCitationAttempts(root);
|
|
358
|
+
const enginesSeen = new Set(attempts.map((a) => a.engine).filter(Boolean));
|
|
359
|
+
const verified = attempts.filter((a) => a.status === 'verified').length;
|
|
360
|
+
const pending = attempts.filter((a) => a.status === 'pending').length;
|
|
361
|
+
const failed = attempts.filter((a) => a.status === 'failed').length;
|
|
362
|
+
|
|
363
|
+
const packets = [];
|
|
364
|
+
if (fs.existsSync(proofRoot)) {
|
|
365
|
+
for (const entry of fs.readdirSync(proofRoot)) {
|
|
366
|
+
if (!entry.endsWith('-buyer-packet')) continue;
|
|
367
|
+
const p = path.join(proofRoot, entry, 'packet.json');
|
|
368
|
+
const data = readJsonSafe(p);
|
|
369
|
+
if (!data) continue;
|
|
370
|
+
packets.push({
|
|
371
|
+
slug: entry.replace(/-buyer-packet$/, ''),
|
|
372
|
+
surface: data.surface || entry,
|
|
373
|
+
target_url: data.target_url || '',
|
|
374
|
+
baseline: data?.agent_usability?.baseline_score ?? null,
|
|
375
|
+
proposed: data?.agent_usability?.proposed_score ?? null,
|
|
376
|
+
claim_status: data.claim_status || '',
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const operator = readJsonSafe(path.join(proofRoot, 'live-citation-operator', 'live-citation-operator.json'));
|
|
382
|
+
|
|
383
|
+
const proofDirs = fs.existsSync(proofRoot)
|
|
384
|
+
? fs.readdirSync(proofRoot).filter((e) => fs.statSync(path.join(proofRoot, e)).isDirectory()).length
|
|
385
|
+
: 0;
|
|
386
|
+
|
|
387
|
+
if (wantJson) {
|
|
388
|
+
console.log(JSON.stringify({
|
|
389
|
+
backend_root: root,
|
|
390
|
+
proof_dirs: proofDirs,
|
|
391
|
+
citation: { total: attempts.length, verified, pending, failed, engines: [...enginesSeen] },
|
|
392
|
+
packets,
|
|
393
|
+
operator_status: operator?.status || null,
|
|
394
|
+
operator_blocker: operator?.current_blocker || null,
|
|
395
|
+
}, null, 2));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
console.log('Atris AEO status');
|
|
400
|
+
console.log(` backend root: ${root}`);
|
|
401
|
+
console.log(` proof receipts: ${proofDirs} categories`);
|
|
402
|
+
console.log('');
|
|
403
|
+
console.log('Live citations');
|
|
404
|
+
console.log(` attempts: ${attempts.length}`);
|
|
405
|
+
console.log(` verified: ${verified}`);
|
|
406
|
+
console.log(` pending: ${pending}`);
|
|
407
|
+
console.log(` failed: ${failed}`);
|
|
408
|
+
console.log(` engines observed: ${[...enginesSeen].join(', ') || '(none)'}`);
|
|
409
|
+
if (operator) {
|
|
410
|
+
console.log(` operator state: ${operator.status || '?'} (blocker: ${operator.current_blocker || 'none'})`);
|
|
411
|
+
}
|
|
412
|
+
console.log('');
|
|
413
|
+
console.log(`Buyer packets (${packets.length})`);
|
|
414
|
+
for (const p of packets) {
|
|
415
|
+
const delta = p.baseline != null && p.proposed != null ? `${p.baseline} → ${p.proposed}` : '?';
|
|
416
|
+
console.log(` ${pad(p.slug, 16)} ${pad(p.target_url || p.surface, 36)} ${pad(delta, 12)} ${p.claim_status}`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async function aeoPacket(args) {
|
|
421
|
+
const known = new Set(['--json']);
|
|
422
|
+
const positional = [];
|
|
423
|
+
for (const a of args) {
|
|
424
|
+
if (a.startsWith('--')) {
|
|
425
|
+
if (!known.has(a)) {
|
|
426
|
+
console.error(`Unknown flag: ${a}. Supported: --json`);
|
|
427
|
+
process.exit(1);
|
|
428
|
+
}
|
|
429
|
+
} else {
|
|
430
|
+
positional.push(a);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (positional.length === 0) {
|
|
434
|
+
console.error('Missing slug. Usage: atris aeo packet <slug>');
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
if (positional.length > 1) {
|
|
438
|
+
console.error(`Too many arguments: ${positional.join(' ')}. Expected one slug.`);
|
|
439
|
+
process.exit(1);
|
|
440
|
+
}
|
|
441
|
+
const slug = positional[0];
|
|
442
|
+
const wantJson = args.includes('--json');
|
|
443
|
+
const root = requireBackendRoot();
|
|
444
|
+
const file = path.join(root, 'atris', 'features', 'aeo', 'proof', `${slug}-buyer-packet`, 'packet.json');
|
|
445
|
+
const data = readJsonSafe(file);
|
|
446
|
+
if (!data) {
|
|
447
|
+
console.error(`Packet not found: ${file}`);
|
|
448
|
+
process.exit(1);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (wantJson) {
|
|
452
|
+
console.log(JSON.stringify(data, null, 2));
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const u = (data && typeof data.agent_usability === 'object' && data.agent_usability) || {};
|
|
457
|
+
const onlyObjects = (v) => (Array.isArray(v) ? v.filter((x) => x && typeof x === 'object') : []);
|
|
458
|
+
const friction = onlyObjects(u.baseline_friction_points);
|
|
459
|
+
const fixes = onlyObjects(u.fix_backlog);
|
|
460
|
+
console.log(`AEO buyer packet — ${slug}`);
|
|
461
|
+
console.log(` surface: ${data.surface || ''}`);
|
|
462
|
+
console.log(` target url: ${data.target_url || ''}`);
|
|
463
|
+
console.log(` claim status: ${data.claim_status || ''}`);
|
|
464
|
+
console.log(` positioning: ${u.positioning || ''}`);
|
|
465
|
+
console.log('');
|
|
466
|
+
console.log('Agent usability scores');
|
|
467
|
+
console.log(` baseline: ${u.baseline_score ?? '?'}`);
|
|
468
|
+
console.log(` proposed: ${u.proposed_score ?? '?'}`);
|
|
469
|
+
console.log(` delta: ${u.score_delta ?? '?'}`);
|
|
470
|
+
console.log(` verified: ${u.movement_verified ? 'yes' : 'no'}`);
|
|
471
|
+
console.log('');
|
|
472
|
+
console.log(`Baseline friction (${friction.length})`);
|
|
473
|
+
for (const f of friction) {
|
|
474
|
+
console.log(` [${f.severity || '?'}] ${f.stage || '?'}: ${f.missing_artifact || f.id || ''}`);
|
|
475
|
+
}
|
|
476
|
+
console.log('');
|
|
477
|
+
console.log(`Fix backlog (${fixes.length})`);
|
|
478
|
+
for (const f of fixes) {
|
|
479
|
+
console.log(` #${f.priority ?? '?'} ${f.stage || '?'}: ${f.action || ''}`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
async function aeoProofs(args) {
|
|
484
|
+
const filter = readArg(args, '--filter', '-f');
|
|
485
|
+
assertNoExtras('proofs', args, []);
|
|
486
|
+
const root = requireBackendRoot();
|
|
487
|
+
const proofRoot = path.join(root, 'atris', 'features', 'aeo', 'proof');
|
|
488
|
+
if (!fs.existsSync(proofRoot)) {
|
|
489
|
+
console.error(`Proof root not found: ${proofRoot}`);
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
const needle = filter ? filter.toLowerCase() : null;
|
|
493
|
+
const entries = fs.readdirSync(proofRoot)
|
|
494
|
+
.filter((e) => fs.statSync(path.join(proofRoot, e)).isDirectory())
|
|
495
|
+
.filter((e) => !needle || e.toLowerCase().includes(needle))
|
|
496
|
+
.sort();
|
|
497
|
+
console.log(`AEO proof receipts at ${path.relative(process.cwd(), proofRoot)}`);
|
|
498
|
+
console.log('');
|
|
499
|
+
for (const e of entries) {
|
|
500
|
+
const files = fs.readdirSync(path.join(proofRoot, e)).filter((f) => f.endsWith('.json'));
|
|
501
|
+
console.log(` ${pad(e, 44)} ${files.length} file${files.length === 1 ? '' : 's'}`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// ---------- SCRIPT WRAPPERS ----------
|
|
506
|
+
|
|
507
|
+
function runBackendScript(scriptName, args) {
|
|
508
|
+
const root = requireBackendRoot();
|
|
509
|
+
const script = path.join(root, 'scripts', scriptName);
|
|
510
|
+
if (!fs.existsSync(script)) {
|
|
511
|
+
console.error(`Script not found: ${script}`);
|
|
512
|
+
process.exit(1);
|
|
513
|
+
}
|
|
514
|
+
const py = process.env.ATRIS_PYTHON || 'python3';
|
|
515
|
+
const result = spawnSync(py, [script, ...args], { cwd: root, stdio: 'inherit' });
|
|
516
|
+
if (result.error) {
|
|
517
|
+
console.error(`Failed to spawn ${py}: ${result.error.message}`);
|
|
518
|
+
process.exit(1);
|
|
519
|
+
}
|
|
520
|
+
if (result.signal) {
|
|
521
|
+
const signum = os.constants?.signals?.[result.signal] ?? 0;
|
|
522
|
+
console.error(`${scriptName} terminated by signal ${result.signal}`);
|
|
523
|
+
process.exit(128 + signum);
|
|
524
|
+
}
|
|
525
|
+
process.exit(result.status ?? 1);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
async function aeoDiscover(args) {
|
|
529
|
+
if (!args.length || args[0] === '--help' || args[0] === '-h') {
|
|
530
|
+
console.log('Usage: atris aeo discover <source> [--question Q]... [--canonical-url URL] [--out-dir DIR] [--json]');
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
runBackendScript('aeo_discovery_audit.py', args);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async function aeoAudit(args) {
|
|
537
|
+
if (!args.length || args[0] === '--help' || args[0] === '-h') {
|
|
538
|
+
console.log('Usage: atris aeo audit <source> [--baseline B] [--canonical-url URL] [--out-dir DIR] [--task T]');
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
runBackendScript('aeo_agent_usability_audit.py', args);
|
|
542
|
+
}
|
|
543
|
+
|
|
186
544
|
async function run(args = []) {
|
|
187
545
|
const sub = args[0];
|
|
188
546
|
if (!sub || sub === 'help' || sub === '--help' || sub === '-h') return printHelp();
|
|
189
547
|
const rest = args.slice(1);
|
|
190
548
|
if (sub === 'init') return aeoInit(rest);
|
|
191
549
|
if (sub === 'draft') return aeoDraft(rest);
|
|
550
|
+
if (sub === 'log') return aeoLog(rest);
|
|
551
|
+
if (sub === 'status') return aeoStatus(rest);
|
|
552
|
+
if (sub === 'packet') return aeoPacket(rest);
|
|
553
|
+
if (sub === 'proofs') return aeoProofs(rest);
|
|
554
|
+
if (sub === 'discover') return aeoDiscover(rest);
|
|
555
|
+
if (sub === 'audit') return aeoAudit(rest);
|
|
192
556
|
console.error(`Unknown aeo subcommand: ${sub}`);
|
|
193
557
|
printHelp();
|
|
194
558
|
process.exit(1);
|
package/commands/gm.js
CHANGED
|
@@ -4,6 +4,8 @@ const fs = require('fs');
|
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
|
+
const AGENTXP_LEADERBOARD_URL = 'https://api.atris.ai/api/agentxp/leaderboard';
|
|
8
|
+
|
|
7
9
|
const ROLE_PLAYERS_TO_IGNORE = new Set([
|
|
8
10
|
'game-manager',
|
|
9
11
|
'navigator',
|
|
@@ -26,7 +28,7 @@ function showHelp() {
|
|
|
26
28
|
console.log('');
|
|
27
29
|
console.log('Options:');
|
|
28
30
|
console.log(' --manager <id> Manager id. Defaults to game-manager when present.');
|
|
29
|
-
console.log(' --as <id> Alias for --
|
|
31
|
+
console.log(' --as <id> Alias for --player.');
|
|
30
32
|
console.log(' --player <id> Preferred player when seeding a first local mission.');
|
|
31
33
|
console.log(' --workspace <p> Read missions from another Atris workspace.');
|
|
32
34
|
console.log(' --no-seed Do not create a starter player mission.');
|
|
@@ -102,7 +104,7 @@ function teamMembers(workspaceRoot) {
|
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
function inferManager(workspaceRoot, args = []) {
|
|
105
|
-
const explicit = flag(args, '--manager') ||
|
|
107
|
+
const explicit = flag(args, '--manager') || positional(args)[0];
|
|
106
108
|
if (explicit) return { manager: slugify(explicit), source: 'flag' };
|
|
107
109
|
|
|
108
110
|
for (const value of [process.env.ATRIS_GM, process.env.ATRIS_MANAGER, process.env.ATRIS_AGENT_ID]) {
|
|
@@ -160,7 +162,7 @@ function starterMissionPrompt(player) {
|
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
function pickSeedPlayer(workspaceRoot, tasks, args = []) {
|
|
163
|
-
const explicit = flag(args, '--player') || flag(args, '--user');
|
|
165
|
+
const explicit = flag(args, '--player') || flag(args, '--user') || flag(args, '--as');
|
|
164
166
|
if (explicit) return slugify(explicit);
|
|
165
167
|
|
|
166
168
|
const fromTasks = playersFromTasks(tasks);
|
|
@@ -261,19 +263,29 @@ function compactTask(task) {
|
|
|
261
263
|
};
|
|
262
264
|
}
|
|
263
265
|
|
|
264
|
-
function
|
|
266
|
+
function globalSyncCommands(player) {
|
|
267
|
+
return [
|
|
268
|
+
'atris login',
|
|
269
|
+
`atris xp sync --local --as ${player}`,
|
|
270
|
+
];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function nextCommands({ seeded, reviewQueue, missions, players, manager }) {
|
|
265
274
|
if (reviewQueue.length) {
|
|
266
|
-
const
|
|
275
|
+
const task = reviewQueue[0];
|
|
276
|
+
const ref = task.ref;
|
|
277
|
+
const player = task.assigned_to || task.claimed_by || players[0]?.player || 'player';
|
|
267
278
|
return [
|
|
268
279
|
`atris task show ${ref}`,
|
|
269
|
-
`atris task accept ${ref} --proof "<human review>"`,
|
|
270
|
-
`atris task revise ${ref} --note "<what must change>"`,
|
|
280
|
+
`atris task accept ${ref} --as ${player} --proof "<human review>"`,
|
|
281
|
+
`atris task revise ${ref} --as ${player} --note "<what must change>"`,
|
|
282
|
+
...globalSyncCommands(player),
|
|
271
283
|
];
|
|
272
284
|
}
|
|
273
285
|
if (missions.length) {
|
|
274
286
|
const mission = missions[0];
|
|
275
287
|
const player = mission.assigned_to || mission.claimed_by || players[0]?.player || 'player';
|
|
276
|
-
if (mission.status === 'open') return [`atris task claim ${mission.ref} --as ${
|
|
288
|
+
if (mission.status === 'open') return [`atris task claim ${mission.ref} --as ${manager || 'game-manager'}`];
|
|
277
289
|
return [`atris play --as ${player}`];
|
|
278
290
|
}
|
|
279
291
|
if (seeded) return [`atris play --as ${seeded.assigned_to || 'player'}`];
|
|
@@ -297,7 +309,7 @@ function gmState(args = []) {
|
|
|
297
309
|
const reviewQueue = missions.filter(task => task.status === 'review');
|
|
298
310
|
const players = groupPlayers(tasks, workspaceRoot);
|
|
299
311
|
const seeded = compactTask(starter.seeded);
|
|
300
|
-
const commands = nextCommands({ seeded, reviewQueue, missions, players });
|
|
312
|
+
const commands = nextCommands({ seeded, reviewQueue, missions, players, manager: detected.manager });
|
|
301
313
|
|
|
302
314
|
return {
|
|
303
315
|
schema: 'atris.agentxp_gm_mode.v1',
|
|
@@ -318,6 +330,8 @@ function gmState(args = []) {
|
|
|
318
330
|
review_queue: reviewQueue,
|
|
319
331
|
next_commands: commands,
|
|
320
332
|
xp_rule: 'GM can route missions and review proof, but AgentXP still lands only after human accept.',
|
|
333
|
+
global_sync_rule: 'Run atris login once before syncing to the hosted AgentXP leaderboard.',
|
|
334
|
+
leaderboard_url: AGENTXP_LEADERBOARD_URL,
|
|
321
335
|
};
|
|
322
336
|
}
|
|
323
337
|
|
|
@@ -351,6 +365,8 @@ function render(state) {
|
|
|
351
365
|
|
|
352
366
|
console.log('');
|
|
353
367
|
console.log('XP rule: no proof, no AgentXP; accept/revise stays human-gated.');
|
|
368
|
+
console.log('Global sync: run atris login once before hosted leaderboard sync.');
|
|
369
|
+
console.log(`Leaderboard: ${state.leaderboard_url}`);
|
|
354
370
|
console.log('');
|
|
355
371
|
console.log('Next commands:');
|
|
356
372
|
for (const command of state.next_commands) console.log(`- ${command}`);
|