atris 3.15.23 → 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/commands/xp.js CHANGED
@@ -17,6 +17,7 @@ const CAREER_XP_SESSIONS_DIR = path.join('.atris', 'state', 'career_xp_sessions'
17
17
  const TASK_PROJECTION_FILE = path.join('.atris', 'state', 'tasks.projection.json');
18
18
  const CODEX_STATE_FILE = path.join(os.homedir(), '.codex', 'state_5.sqlite');
19
19
  const AGENT_XP_LABEL = 'AgentXP';
20
+ const AGENTXP_LEADERBOARD_URL = 'https://api.atris.ai/api/agentxp/leaderboard';
20
21
  const LEVEL_XP = 1000;
21
22
  const RECEIPT_CHAIN_VERSION = 'atris.career_xp_receipt_chain.v1';
22
23
  const XP_STATE_FILES = new Set([
@@ -37,14 +38,16 @@ const SEARCH_EXCLUDED_DIRS = new Set([
37
38
  const DEFAULT_SEARCH_DEPTH = 6;
38
39
 
39
40
  function showHelp() {
40
- console.log('Usage: atris xp [card|status|collect|session] [--json] [--workspace <path>] [--all] [--root <path>]');
41
+ console.log('Usage: atris xp [card|status|collect|session|sync] [--json] [--workspace <path>] [--all] [--root <path>]');
41
42
  console.log(' atris xp session [--since today|tonight|YYYY-MM-DD] [--until <time>] [--mission <text>] [--thread <id>] [--no-write]');
43
+ console.log(' atris xp sync [--as <player>] [--local|--all] [--token <token>|logged-in] [--dry-run] [--json]');
42
44
  console.log(' atris xp [--json] [--local] [--workspace <path>] [--operator <name>]');
43
45
  console.log('');
44
46
  console.log('Show your AgentXP graph for the active Atris account.');
45
47
  console.log('Use status to show account-level AgentXP across verified local ledgers.');
46
48
  console.log('Use collect or status --local to project accepted task proof in the current workspace.');
47
49
  console.log('Use session to encapsulate the current work window into a local XP capsule.');
50
+ console.log('Use sync to upload a path-private AgentXP packet to the hosted leaderboard.');
48
51
  console.log('Use status --all to explicitly aggregate verified local XP ledgers across workspaces.');
49
52
  console.log('Use --local to render from proof receipts in the current workspace.');
50
53
  }
@@ -187,6 +190,14 @@ function readFlag(args, name, fallback = null) {
187
190
  return fallback;
188
191
  }
189
192
 
193
+ function readFirstFlag(args, names, fallback = null) {
194
+ for (const name of names) {
195
+ const value = readFlag(args, name, null);
196
+ if (value !== null && value !== undefined && value !== '') return value;
197
+ }
198
+ return fallback;
199
+ }
200
+
190
201
  function readFlagValues(args, names) {
191
202
  const wanted = Array.isArray(names) ? names : [names];
192
203
  const values = [];
@@ -312,6 +323,15 @@ function hashPayload(value) {
312
323
  return sha256(canonicalJson(value));
313
324
  }
314
325
 
326
+ function slugify(value) {
327
+ return String(value || '')
328
+ .trim()
329
+ .toLowerCase()
330
+ .replace(/@.*$/, '')
331
+ .replace(/[^a-z0-9]+/g, '-')
332
+ .replace(/^-+|-+$/g, '');
333
+ }
334
+
315
335
  function readJsonFile(filePath, fallback = null) {
316
336
  if (!fs.existsSync(filePath)) return fallback;
317
337
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
@@ -1418,7 +1438,191 @@ function loadLocalPayload(args) {
1418
1438
  return normalizeLocalScore(JSON.parse(result.stdout), workspace);
1419
1439
  }
1420
1440
 
1441
+ function publicAgentXp(value) {
1442
+ return Math.max(0, Math.min(99, asNumber(value)));
1443
+ }
1444
+
1445
+ function verifiedProjection(projection) {
1446
+ return projection?.integrity_status === 'verified'
1447
+ && projection?.integrity?.status === 'verified';
1448
+ }
1449
+
1450
+ function projectionWorkspaceSummaries(projection) {
1451
+ if (Array.isArray(projection?.workspaces)) {
1452
+ return projection.workspaces.map(workspace => ({
1453
+ name: workspace.name || workspaceName(workspace.workspace_root || 'workspace'),
1454
+ workspace_root_hash: workspace.workspace_root ? sha256(path.resolve(workspace.workspace_root)) : null,
1455
+ included: Boolean(workspace.included),
1456
+ agent_xp: asNumber(workspace.total_xp),
1457
+ receipts_count: asNumber(workspace.receipts_count),
1458
+ integrity_status: workspace.integrity_status || 'unknown',
1459
+ }));
1460
+ }
1461
+
1462
+ const workspaceRoot = projection?.workspace_root || path.resolve(process.cwd());
1463
+ return [{
1464
+ name: workspaceName(workspaceRoot),
1465
+ workspace_root_hash: sha256(path.resolve(workspaceRoot)),
1466
+ included: verifiedProjection(projection),
1467
+ agent_xp: asNumber(projection?.total_agent_xp ?? projection?.total_xp),
1468
+ receipts_count: asNumber(projection?.receipts_count),
1469
+ integrity_status: projection?.integrity_status || projection?.integrity?.status || 'unknown',
1470
+ }];
1471
+ }
1472
+
1473
+ function syncPlayer(args, projection) {
1474
+ const explicit = readFirstFlag(args, ['--as', '--player', '--user', '--operator'], null);
1475
+ return slugify(
1476
+ explicit
1477
+ || process.env.ATRIS_PLAYER
1478
+ || process.env.ATRIS_USERNAME
1479
+ || process.env.ATRIS_PROFILE
1480
+ || process.env.USER
1481
+ || os.userInfo().username
1482
+ || projection?.operator
1483
+ || 'player'
1484
+ ) || 'player';
1485
+ }
1486
+
1487
+ function buildAgentXpSyncPacket(args = []) {
1488
+ const localMode = hasFlag(args, '--local') || hasFlag(args, '--workspace') || hasFlag(args, '--operator');
1489
+ const projectionArgs = args.filter(arg => !['--dry-run', '--no-post', '--packet'].includes(arg));
1490
+ const projection = hasFlag(args, '--all') || !localMode
1491
+ ? collectAllLocalXpProjection(projectionArgs)
1492
+ : collectLocalXpProjection(projectionArgs);
1493
+ const player = syncPlayer(args, projection);
1494
+ const workspaces = projectionWorkspaceSummaries(projection);
1495
+ const totalXp = asNumber(projection.total_agent_xp ?? projection.agent_xp ?? projection.total_xp ?? projection.career_xp);
1496
+ const receiptsCount = asNumber(projection.receipts_count);
1497
+ const eligible = verifiedProjection(projection) && receiptsCount > 0 && totalXp > 0;
1498
+ const publicXp = publicAgentXp(totalXp);
1499
+ const entry = {
1500
+ user_id: player,
1501
+ username: player,
1502
+ agent_xp: publicXp,
1503
+ career_xp: publicXp,
1504
+ current_form: publicXp,
1505
+ ovr: publicXp,
1506
+ level: Math.max(1, asNumber(projection.level, 1)),
1507
+ verified_receipts: receiptsCount,
1508
+ reviewed_tasks: receiptsCount,
1509
+ recent_verified_receipts: receiptsCount,
1510
+ recent_reviewed_tasks: receiptsCount,
1511
+ leaderboard_eligible: eligible,
1512
+ integrity_status: eligible ? 'trusted' : (projection.integrity_status || projection.integrity?.status || 'unknown'),
1513
+ lock_reason: eligible ? null : 'not_enough_trusted_proof',
1514
+ public_adjustment: null,
1515
+ next_move: eligible ? 'Play the next proof-backed AgentXP mission.' : 'Complete one proof-backed AgentXP rep.',
1516
+ };
1517
+ const packet = {
1518
+ schema: 'atris.agentxp_sync_packet.v1',
1519
+ generated_at: new Date().toISOString(),
1520
+ workspace_root_hash: sha256(workspaces.map(item => item.workspace_root_hash || item.name).sort().join(':')),
1521
+ computer: projection.workspace_name || workspaces[0]?.name || 'local',
1522
+ operator: player,
1523
+ privacy: {
1524
+ raw_proofs_included: false,
1525
+ raw_receipts_included: false,
1526
+ contains_absolute_workspace_root: false,
1527
+ public_user_board_omits_lifetime_xp: true,
1528
+ },
1529
+ sync_contract: {
1530
+ anti_bs_rule: 'No accepted proof-backed task episodes means no AgentXP leaderboard movement.',
1531
+ trust_rule: 'Only verified local ledgers are uploaded; raw proof and paths stay local.',
1532
+ },
1533
+ local_evidence: {
1534
+ workspaces,
1535
+ verified_workspace_count: asNumber(projection.verified_workspace_count, verifiedProjection(projection) ? 1 : 0),
1536
+ receipts_count: receiptsCount,
1537
+ integrity_status: projection.integrity?.status || projection.integrity_status || 'unknown',
1538
+ ledger_head_hash: projection.integrity?.head_hash || null,
1539
+ },
1540
+ user_leaderboard: {
1541
+ schema: 'atris.agentxp_user_leaderboard.v1',
1542
+ score_name: AGENT_XP_LABEL,
1543
+ entries: [entry],
1544
+ },
1545
+ };
1546
+ packet.packet_hash = hashPayload(packet);
1547
+ return {
1548
+ schema: 'atris.agentxp_sync_preview.v1',
1549
+ generated_at: new Date().toISOString(),
1550
+ dry_run: true,
1551
+ player,
1552
+ entry,
1553
+ packet,
1554
+ };
1555
+ }
1556
+
1557
+ async function syncAgentXp(args = []) {
1558
+ const preview = buildAgentXpSyncPacket(args);
1559
+ const dryRun = hasFlag(args, '--dry-run') || hasFlag(args, '--no-post') || hasFlag(args, '--packet');
1560
+ if (dryRun) return preview;
1561
+
1562
+ const token = readFlag(args, '--token', process.env.ATRIS_AGENTXP_SYNC_TOKEN || process.env.AGENTXP_SYNC_TOKEN || '');
1563
+ const options = {
1564
+ method: 'POST',
1565
+ body: preview.packet,
1566
+ retries: 0,
1567
+ };
1568
+ if (token) {
1569
+ options.headers = { 'X-AgentXP-Sync-Token': token };
1570
+ } else {
1571
+ const ensured = await ensureValidCredentials(apiRequestJson);
1572
+ if (ensured.error) {
1573
+ throw new Error(`Missing sync auth. Run atris login, or set ATRIS_AGENTXP_SYNC_TOKEN${ensured.detail ? ` (${ensured.detail})` : ''}.`);
1574
+ }
1575
+ options.token = ensured.credentials.token;
1576
+ }
1577
+
1578
+ const response = await apiRequestJson('/agentxp/leaderboard/sync', options);
1579
+ if (!response.ok) {
1580
+ throw new Error(`AgentXP sync failed: ${response.error || response.status}`);
1581
+ }
1582
+ return {
1583
+ schema: 'atris.agentxp_sync_result.v1',
1584
+ generated_at: new Date().toISOString(),
1585
+ dry_run: false,
1586
+ player: preview.player,
1587
+ entry: preview.entry,
1588
+ packet_hash: preview.packet.packet_hash,
1589
+ server: response.data || {},
1590
+ };
1591
+ }
1592
+
1593
+ function renderSync(payload) {
1594
+ const entry = payload.entry || {};
1595
+ console.log('AgentXP Sync');
1596
+ console.log(`Player ${payload.player || entry.username || 'player'} | AgentXP ${formatNumber(entry.agent_xp)} | receipts ${formatNumber(entry.verified_receipts)}`);
1597
+ if (payload.dry_run) {
1598
+ console.log(`Packet ${payload.packet?.packet_hash || 'unhashed'} ready; no network upload ran.`);
1599
+ console.log('Run with ATRIS_AGENTXP_SYNC_TOKEN set to publish to the hosted leaderboard.');
1600
+ console.log(`Leaderboard: ${AGENTXP_LEADERBOARD_URL}`);
1601
+ return;
1602
+ }
1603
+ const server = payload.server || {};
1604
+ console.log(`Uploaded: accepted ${formatNumber(server.accepted_count)} | stored ${formatNumber(server.stored_count)}`);
1605
+ const acceptedUsernames = Array.isArray(server.accepted_usernames)
1606
+ ? server.accepted_usernames.map(value => String(value || '').trim()).filter(Boolean)
1607
+ : [];
1608
+ if (acceptedUsernames.length) {
1609
+ console.log(`Public identity: ${acceptedUsernames.join(', ')}`);
1610
+ }
1611
+ const player = String(payload.player || entry.username || '').trim().toLowerCase();
1612
+ const accepted = acceptedUsernames.map(value => value.toLowerCase());
1613
+ if (server.mapped_to_authenticated_user === true && player && !accepted.includes(player)) {
1614
+ console.log('Login auth mapped this sync to your Atris account.');
1615
+ }
1616
+ console.log(`Packet ${payload.packet_hash}`);
1617
+ console.log(`Leaderboard: ${AGENTXP_LEADERBOARD_URL}`);
1618
+ }
1619
+
1421
1620
  function render(payload) {
1621
+ if (payload.schema === 'atris.agentxp_sync_preview.v1' || payload.schema === 'atris.agentxp_sync_result.v1') {
1622
+ renderSync(payload);
1623
+ return;
1624
+ }
1625
+
1422
1626
  if (payload.schema === 'atris.career_xp_session_capsule.v1') {
1423
1627
  const xp = payload.xp || {};
1424
1628
  const tasks = payload.tasks || {};
@@ -1508,6 +1712,23 @@ async function xpCommand(...args) {
1508
1712
  }
1509
1713
 
1510
1714
  const subcommand = args[0] && !args[0].startsWith('--') ? args[0] : null;
1715
+ if (subcommand === 'sync') {
1716
+ const commandArgs = args.slice(1);
1717
+ let payload;
1718
+ try {
1719
+ payload = await syncAgentXp(commandArgs);
1720
+ } catch (error) {
1721
+ console.error(`Failed to sync AgentXP: ${error.message}`);
1722
+ process.exit(1);
1723
+ }
1724
+ if (args.includes('--json')) {
1725
+ console.log(JSON.stringify(payload, null, 2));
1726
+ return;
1727
+ }
1728
+ render(payload);
1729
+ return;
1730
+ }
1731
+
1511
1732
  if (subcommand === 'session') {
1512
1733
  const commandArgs = args.slice(1);
1513
1734
  let payload;
@@ -1603,6 +1824,8 @@ module.exports = {
1603
1824
  buildCareerXpSessionCapsule,
1604
1825
  collectAllLocalXpProjection,
1605
1826
  collectLocalXpProjection,
1827
+ buildAgentXpSyncPacket,
1828
+ syncAgentXp,
1606
1829
  receiptFromTaskEpisode,
1607
1830
  render,
1608
1831
  };
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "3.15.23",
3
+ "version": "3.15.30",
4
4
  "main": "bin/atris.js",
5
5
  "bin": {
6
- "atris": "bin/atris.js"
6
+ "atris": "bin/atris.js",
7
+ "ax": "ax"
7
8
  },
8
9
  "files": [
10
+ "ax",
9
11
  "bin/",
10
12
  "cli/*.py",
11
13
  "commands/",
@@ -56,6 +58,7 @@
56
58
  ],
57
59
  "scripts": {
58
60
  "test": "node --test",
61
+ "publish:release": "node scripts/publish-atris-release.js",
59
62
  "prepare": "cp scripts/pre-commit .git/hooks/pre-commit 2>/dev/null && chmod +x .git/hooks/pre-commit || true"
60
63
  },
61
64
  "keywords": [