atris 3.15.42 → 3.15.44

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.
@@ -542,6 +542,8 @@ function parseComputerOptions(argv) {
542
542
  let model = process.env.ATRIS_CLOUD_MODEL || null;
543
543
  let businessSlug = null;
544
544
  let workspaceId = null;
545
+ let waitForResult = true;
546
+ let message = null;
545
547
 
546
548
  for (let i = 0; i < argv.length; i++) {
547
549
  const arg = argv[i];
@@ -585,6 +587,19 @@ function parseComputerOptions(argv) {
585
587
  model = arg.split('=', 2)[1] || null;
586
588
  continue;
587
589
  }
590
+ if (arg === '--message' && argv[i + 1]) {
591
+ message = argv[i + 1];
592
+ i++;
593
+ continue;
594
+ }
595
+ if (arg.startsWith('--message=')) {
596
+ message = arg.slice('--message='.length);
597
+ continue;
598
+ }
599
+ if (arg === '--no-wait' || arg === '--async') {
600
+ waitForResult = false;
601
+ continue;
602
+ }
588
603
  positional.push(arg);
589
604
  }
590
605
 
@@ -601,6 +616,8 @@ function parseComputerOptions(argv) {
601
616
  model: model || null,
602
617
  businessSlug: businessSlug ? String(businessSlug).trim() : null,
603
618
  workspaceId: workspaceId ? String(workspaceId).trim() : null,
619
+ waitForResult,
620
+ message,
604
621
  },
605
622
  };
606
623
  }
@@ -609,6 +626,7 @@ function parseComputerCreateArgs(argv = []) {
609
626
  const nameParts = [];
610
627
  let businessSlug = null;
611
628
  let help = false;
629
+ let setDefault = false;
612
630
 
613
631
  for (let i = 0; i < argv.length; i++) {
614
632
  const arg = argv[i];
@@ -625,6 +643,10 @@ function parseComputerCreateArgs(argv = []) {
625
643
  businessSlug = arg.split('=', 2)[1] || null;
626
644
  continue;
627
645
  }
646
+ if (arg === '--set-default') {
647
+ setDefault = true;
648
+ continue;
649
+ }
628
650
  nameParts.push(arg);
629
651
  }
630
652
 
@@ -632,6 +654,7 @@ function parseComputerCreateArgs(argv = []) {
632
654
  name: nameParts.join(' ').trim(),
633
655
  businessSlug: businessSlug ? String(businessSlug).trim() : null,
634
656
  help,
657
+ setDefault,
635
658
  };
636
659
  }
637
660
 
@@ -1137,19 +1160,58 @@ async function resolveBusinessOwnerForCreate(token, businessSlug = null) {
1137
1160
  return resolveBusinessContext(token);
1138
1161
  }
1139
1162
 
1140
- function rememberCreatedComputer(ctx, workspace, endpoint = null) {
1163
+ function businessSelector(ctx) {
1164
+ return ctx?.slug || ctx?.businessId || '<business>';
1165
+ }
1166
+
1167
+ function apiFailureDetail(result) {
1168
+ return String(result?.errorMessage || result?.error || result?.data?.detail || result?.status || 'Request failed');
1169
+ }
1170
+
1171
+ function printComputerCommandFailure(result, ctx = null) {
1172
+ const detail = apiFailureDetail(result);
1173
+ console.error(detail);
1174
+ if (result?.status === 409) {
1175
+ const mismatch = extractAttachedWorkspaceMismatch(detail, result?.data);
1176
+ const targetWorkspace = mismatch?.requestedWorkspaceId || ctx?.workspaceId || '<workspace-id>';
1177
+ console.error(`Run: atris computer activate --business ${businessSelector(ctx)} --workspace ${targetWorkspace}`);
1178
+ }
1179
+ }
1180
+
1181
+ function rememberBusinessWorkspace(ctx, workspaceId, options = {}) {
1182
+ const slug = ctx.slug || (ctx.businessName || '').toLowerCase().replace(/\s+/g, '-');
1183
+ if (!slug || !workspaceId) return;
1184
+ const businesses = loadBusinesses();
1185
+ const existing = businesses[slug] || {};
1186
+ businesses[slug] = {
1187
+ ...existing,
1188
+ business_id: ctx.businessId,
1189
+ workspace_id: workspaceId,
1190
+ name: ctx.businessName,
1191
+ slug,
1192
+ computer_name: options.computerName || existing.computer_name,
1193
+ endpoint: options.endpoint || existing.endpoint,
1194
+ added_at: existing.added_at || new Date().toISOString(),
1195
+ updated_at: new Date().toISOString(),
1196
+ };
1197
+ saveBusinesses(businesses);
1198
+ }
1199
+
1200
+ function rememberCreatedComputer(ctx, workspace, endpoint = null, options = {}) {
1141
1201
  const slug = ctx.slug || (ctx.businessName || '').toLowerCase().replace(/\s+/g, '-');
1142
1202
  if (!slug) return;
1143
1203
  const businesses = loadBusinesses();
1204
+ const existing = businesses[slug] || {};
1205
+ const shouldSetDefault = Boolean(options.setDefault || !existing.workspace_id);
1144
1206
  businesses[slug] = {
1145
- ...(businesses[slug] || {}),
1207
+ ...existing,
1146
1208
  business_id: ctx.businessId,
1147
- workspace_id: workspace.id,
1209
+ workspace_id: shouldSetDefault ? workspace.id : existing.workspace_id,
1148
1210
  name: ctx.businessName,
1149
1211
  slug,
1150
- computer_name: workspace.name,
1151
- endpoint: endpoint || undefined,
1152
- added_at: businesses[slug]?.added_at || new Date().toISOString(),
1212
+ computer_name: shouldSetDefault ? workspace.name : existing.computer_name,
1213
+ endpoint: shouldSetDefault ? (endpoint || existing.endpoint) : existing.endpoint,
1214
+ added_at: existing.added_at || new Date().toISOString(),
1153
1215
  updated_at: new Date().toISOString(),
1154
1216
  };
1155
1217
  saveBusinesses(businesses);
@@ -1176,6 +1238,40 @@ async function runBusinessTerminalCommand(token, ctx, command, timeout = 30) {
1176
1238
  );
1177
1239
  }
1178
1240
 
1241
+ async function listBusinessWorkspaces(token, ctx) {
1242
+ const result = await apiRequestJson(`/business/${ctx.businessId}/workspaces`, {
1243
+ method: 'GET',
1244
+ token,
1245
+ timeoutMs: 15000,
1246
+ retries: 0,
1247
+ });
1248
+ return result.ok && Array.isArray(result.data) ? result.data : [];
1249
+ }
1250
+
1251
+ function formatWorkspaceRef(workspace) {
1252
+ if (!workspace) return '-';
1253
+ return workspace.name ? `${workspace.name} (${workspace.id})` : workspace.id;
1254
+ }
1255
+
1256
+ async function probeAttachedWorkspace(token, ctx) {
1257
+ const result = await apiRequestJson(
1258
+ `/business/${ctx.businessId}/workspaces/${ctx.workspaceId}/terminal`,
1259
+ {
1260
+ method: 'POST',
1261
+ token,
1262
+ body: { command: 'printf "ATRIS_STATUS_OK\\n"', timeout: 5 },
1263
+ timeoutMs: 8000,
1264
+ retries: 0,
1265
+ }
1266
+ );
1267
+ if (result.ok) return { workspaceId: ctx.workspaceId, health: 'ready' };
1268
+ const mismatch = extractAttachedWorkspaceMismatch(apiFailureDetail(result), result.data);
1269
+ if (mismatch?.attachedWorkspaceId) {
1270
+ return { workspaceId: mismatch.attachedWorkspaceId, health: 'workspace_mismatch', result };
1271
+ }
1272
+ return { workspaceId: null, health: 'degraded', result };
1273
+ }
1274
+
1179
1275
  async function bootstrapBusinessComputerRuntime(token, ctx, boundary = 'computer-wake') {
1180
1276
  if (!ctx?.businessId || !ctx?.workspaceId) {
1181
1277
  return { ok: false, skipped: true, reason: 'missing_workspace' };
@@ -1399,6 +1495,23 @@ async function computerStatus(token, ctx = null) {
1399
1495
  console.log(` ${icon} Computer: ${status}`);
1400
1496
  console.log(` Business: ${ctx.businessName}`);
1401
1497
  if (d.endpoint) console.log(` Endpoint: ${d.endpoint}`);
1498
+ const workspaces = await listBusinessWorkspaces(token, ctx);
1499
+ const defaultWorkspace = workspaces.find((workspace) => workspace.is_default);
1500
+ const targetWorkspace = workspaces.find((workspace) => workspace.id === ctx.workspaceId) || (ctx.workspaceId ? { id: ctx.workspaceId } : null);
1501
+ console.log(` Default workspace: ${formatWorkspaceRef(defaultWorkspace)}`);
1502
+ console.log(` Target workspace: ${formatWorkspaceRef(targetWorkspace)}`);
1503
+ if (status === 'running' && d.endpoint && ctx.workspaceId) {
1504
+ const attached = await probeAttachedWorkspace(token, ctx);
1505
+ const attachedWorkspace = workspaces.find((workspace) => workspace.id === attached.workspaceId) || (attached.workspaceId ? { id: attached.workspaceId } : null);
1506
+ console.log(` Attached workspace: ${formatWorkspaceRef(attachedWorkspace)}`);
1507
+ if (attached.health === 'workspace_mismatch') {
1508
+ printComputerCommandFailure(attached.result, ctx);
1509
+ } else if (attached.health !== 'ready') {
1510
+ console.log(` Health: degraded (${apiFailureDetail(attached.result)})`);
1511
+ } else {
1512
+ console.log(' Health: ready');
1513
+ }
1514
+ }
1402
1515
  return;
1403
1516
  }
1404
1517
 
@@ -1472,7 +1585,7 @@ async function computerCreate(token, args = [], defaults = {}) {
1472
1585
  options.businessSlug = defaults.businessSlug;
1473
1586
  }
1474
1587
  if (options.help || !options.name) {
1475
- console.log('Usage: atris computer create <name> --business <slug>');
1588
+ console.log('Usage: atris computer create <name> --business <slug> [--set-default]');
1476
1589
  console.log('');
1477
1590
  console.log('Create a business computer, activate it, and wake it in one command.');
1478
1591
  console.log('');
@@ -1537,7 +1650,9 @@ async function computerCreate(token, args = [], defaults = {}) {
1537
1650
  const status = endpoint
1538
1651
  ? 'running'
1539
1652
  : (wake.data?.status || (activate.ok ? 'activated' : 'warming_up'));
1540
- rememberCreatedComputer(ctx, { ...workspace, id: workspaceId, name: workspace.name || options.name }, endpoint);
1653
+ rememberCreatedComputer(ctx, { ...workspace, id: workspaceId, name: workspace.name || options.name }, endpoint, {
1654
+ setDefault: options.setDefault,
1655
+ });
1541
1656
  await bootstrapBusinessComputerRuntime(token, { ...ctx, workspaceId }, 'computer-create');
1542
1657
 
1543
1658
  const appBase = getAppBaseUrl();
@@ -1548,8 +1663,14 @@ async function computerCreate(token, args = [], defaults = {}) {
1548
1663
  console.log(` Status: ${status}`);
1549
1664
  if (endpoint) console.log(` Endpoint: ${endpoint}`);
1550
1665
  console.log(` Dashboard: ${appBase}/dashboard/gm/${ctx.businessId}`);
1551
- console.log('');
1552
1666
  const owner = ctx.slug || ctx.businessId;
1667
+ if (options.setDefault || !ctx.workspaceId) {
1668
+ console.log(` Default: now ${workspaceId}`);
1669
+ } else {
1670
+ console.log(` Default: unchanged (${ctx.workspaceId})`);
1671
+ console.log(` Switch default: atris computer activate --business ${owner} --workspace ${workspaceId}`);
1672
+ }
1673
+ console.log('');
1553
1674
  console.log('Start here:');
1554
1675
  console.log(` atris computer --business ${owner} --workspace ${workspaceId}`);
1555
1676
  console.log('');
@@ -1565,6 +1686,37 @@ async function computerCreate(token, args = [], defaults = {}) {
1565
1686
  console.log(` atris computer sleep --business ${owner} --workspace ${workspaceId}`);
1566
1687
  }
1567
1688
 
1689
+ async function computerActivate(token, ctx = null) {
1690
+ if (!ctx?.businessId || !ctx?.workspaceId) {
1691
+ console.error('Usage: atris computer activate --business <slug> --workspace <id>');
1692
+ process.exitCode = 1;
1693
+ return;
1694
+ }
1695
+
1696
+ const result = await apiRequestJson(`/business/${ctx.businessId}/workspaces/${ctx.workspaceId}/activate`, {
1697
+ method: 'POST',
1698
+ token,
1699
+ body: {},
1700
+ });
1701
+ if (!result.ok) {
1702
+ printComputerCommandFailure(result, ctx);
1703
+ process.exitCode = 1;
1704
+ return;
1705
+ }
1706
+
1707
+ const workspaces = await listBusinessWorkspaces(token, ctx);
1708
+ const workspace = workspaces.find((row) => row.id === ctx.workspaceId) || { id: ctx.workspaceId };
1709
+ rememberBusinessWorkspace(ctx, ctx.workspaceId, {
1710
+ computerName: workspace.name,
1711
+ endpoint: result.data?.endpoint,
1712
+ });
1713
+
1714
+ console.log(`Activated workspace ${ctx.workspaceId} for ${ctx.businessName}.`);
1715
+ if (workspace.name) console.log(` Name: ${workspace.name}`);
1716
+ if (result.data?.endpoint) console.log(` Endpoint: ${result.data.endpoint}`);
1717
+ console.log(` CLI default: ${ctx.workspaceId}`);
1718
+ }
1719
+
1568
1720
  async function computerSleep(token, ctx = null) {
1569
1721
  if (ctx) {
1570
1722
  console.log(`Sleeping computer for ${ctx.businessName}...`);
@@ -1724,11 +1876,7 @@ async function computerRun(token, command, ctx = null) {
1724
1876
  }
1725
1877
  );
1726
1878
  if (!result.ok) {
1727
- if (result.status === 409 || (result.errorMessage || '').includes('running')) {
1728
- console.error('Computer is off. Run: atris computer wake');
1729
- } else {
1730
- console.error(`Failed: ${result.errorMessage || result.status}`);
1731
- }
1879
+ printComputerCommandFailure(result, ctx);
1732
1880
  return;
1733
1881
  }
1734
1882
  const d = result.data || {};
@@ -1746,11 +1894,7 @@ async function computerRun(token, command, ctx = null) {
1746
1894
  body: { command },
1747
1895
  });
1748
1896
  if (!result.ok) {
1749
- if (result.status === 409 || (result.errorMessage || '').includes('running')) {
1750
- console.error('Computer is off. Run: atris computer wake');
1751
- } else {
1752
- console.error(`Failed: ${result.errorMessage || result.status}`);
1753
- }
1897
+ printComputerCommandFailure(result);
1754
1898
  return;
1755
1899
  }
1756
1900
  const d = result.data;
@@ -2057,18 +2201,21 @@ async function computerExec(token, prompt, ctx = null, options = {}) {
2057
2201
  console.error(' Computer did not become ready in time.');
2058
2202
  return;
2059
2203
  }
2204
+ const worker = activeWorker(options.worker);
2205
+ console.log(` Lane: ${formatWorkerName(worker)} ${formatCloudSelection({ worker, model: options.model })}`);
2060
2206
  const result = await apiRequestJson(`/business/${ctx.businessId}/chat`, {
2061
2207
  method: 'POST',
2062
2208
  token,
2063
2209
  body: {
2064
2210
  message: prompt,
2065
2211
  workspace_id: ctx.workspaceId,
2066
- ...(options.worker ? { worker: options.worker } : {}),
2212
+ worker,
2067
2213
  ...(options.model ? { model: options.model } : {}),
2068
2214
  ...(options.systemPrompt ? { system_prompt: options.systemPrompt } : {}),
2069
2215
  ...(options.allowedTools ? { allowed_tools: options.allowedTools } : {}),
2070
2216
  },
2071
2217
  timeoutMs: 40000,
2218
+ retries: 0,
2072
2219
  });
2073
2220
  if (!result.ok) {
2074
2221
  const fallback = await runBusinessPromptViaRunnerProxy(token, ctx, prompt, options);
@@ -2094,8 +2241,13 @@ async function computerExec(token, prompt, ctx = null, options = {}) {
2094
2241
  const base = getApiBaseUrl();
2095
2242
  console.log(` Execution: ${data.execution_id}`);
2096
2243
  console.log(` Session: ${data.session_id}`);
2097
- console.log(` Stream: ${base}/business/${ctx.businessId}/chat/stream?execution_id=${data.execution_id}&workspace_id=${ctx.workspaceId}`);
2098
- console.log(' Use the stream URL to watch progress.');
2244
+ if (options.waitForResult === false) {
2245
+ console.log(` Stream: ${base}/business/${ctx.businessId}/chat/stream?execution_id=${data.execution_id}&workspace_id=${ctx.workspaceId}`);
2246
+ console.log(' Use the stream URL to watch progress.');
2247
+ return;
2248
+ }
2249
+ const streamed = await streamBusinessChatResult(token, ctx, data.execution_id, null);
2250
+ if (!streamed.ok) process.exitCode = 1;
2099
2251
  return;
2100
2252
  }
2101
2253
 
@@ -2274,6 +2426,7 @@ async function streamBusinessChatResult(token, ctx, executionId, rl = null) {
2274
2426
  let cancelling = false;
2275
2427
  let cancelPromise = null;
2276
2428
  let sawVisibleOutput = false;
2429
+ let terminalStatus = null;
2277
2430
 
2278
2431
  const requestCancel = async () => {
2279
2432
  if (cancelling) return;
@@ -2302,9 +2455,8 @@ async function streamBusinessChatResult(token, ctx, executionId, rl = null) {
2302
2455
  }
2303
2456
  };
2304
2457
 
2305
- if (rl) {
2306
- rl.on('SIGINT', onSigint);
2307
- }
2458
+ const sigintTarget = rl || process;
2459
+ sigintTarget.on('SIGINT', onSigint);
2308
2460
 
2309
2461
  console.log(ui.dim('Running on cloud. Ctrl-C interrupts this run.'));
2310
2462
 
@@ -2319,7 +2471,7 @@ async function streamBusinessChatResult(token, ctx, executionId, rl = null) {
2319
2471
  if (!events.ok) {
2320
2472
  if (++errors >= 5) {
2321
2473
  console.error('\nLost connection to AI computer.');
2322
- return;
2474
+ return { ok: false, status: 'connection_lost' };
2323
2475
  }
2324
2476
  continue;
2325
2477
  }
@@ -2343,25 +2495,27 @@ async function streamBusinessChatResult(token, ctx, executionId, rl = null) {
2343
2495
  }
2344
2496
  } else if (event.type === 'error') {
2345
2497
  if (event.error) console.error(`\n${event.error}`);
2498
+ terminalStatus = 'error';
2346
2499
  done = true;
2347
2500
  break;
2348
2501
  } else if (event.type === 'complete') {
2502
+ terminalStatus = 'completed';
2349
2503
  done = true;
2350
2504
  break;
2351
2505
  }
2352
2506
  }
2353
2507
 
2354
- if (done || ['completed', 'error', 'failed'].includes(events.data?.status)) {
2508
+ if (done || ['completed', 'error', 'failed', 'cancelled'].includes(events.data?.status)) {
2355
2509
  if (!process.stdout.write('\n')) {
2356
2510
  // no-op: keep line handling stable
2357
2511
  }
2358
- return;
2512
+ if (!sawVisibleOutput && events.data?.status === 'completed') console.log('(no result)');
2513
+ const finalStatus = terminalStatus || events.data?.status || (done ? 'completed' : 'unknown');
2514
+ return { ok: finalStatus === 'completed', status: finalStatus };
2359
2515
  }
2360
2516
  }
2361
2517
  } finally {
2362
- if (rl) {
2363
- rl.removeListener('SIGINT', onSigint);
2364
- }
2518
+ sigintTarget.removeListener('SIGINT', onSigint);
2365
2519
  }
2366
2520
  }
2367
2521
 
@@ -2431,10 +2585,12 @@ async function computerChat(token, ctx, initialOptions = {}) {
2431
2585
  ? appendSystemPrompt(initialOptions.systemPrompt, CODEOPS_WORKFLOW_PROMPT)
2432
2586
  : initialOptions.systemPrompt;
2433
2587
  let sessionId = `biz-${ctx.businessId.slice(0, 8)}-${Date.now().toString(36)}`;
2588
+ const pipedInput = initialOptions.message != null ? null : await readPipedStdin();
2589
+ const scriptedInput = initialOptions.message != null ? String(initialOptions.message) : pipedInput;
2434
2590
  printCloudWordmark();
2435
2591
  const selection = await chooseCloudLane(token, ctx, initialOptions);
2436
2592
  if (selection.cancelled) return;
2437
- let worker = selection.worker || null;
2593
+ let worker = activeWorker(selection.worker);
2438
2594
  let model = selection.model || null;
2439
2595
  let awaitingLoginCode = false;
2440
2596
  let billingLabel = await describeBillingMode(token, ctx, worker);
@@ -2452,6 +2608,43 @@ async function computerChat(token, ctx, initialOptions = {}) {
2452
2608
  printCloudStartPanel(ctx, worker, model, billingLabel, authSummary);
2453
2609
  }
2454
2610
 
2611
+ if (scriptedInput !== null) {
2612
+ for (const rawLine of scriptedInput.split(/\r?\n/)) {
2613
+ const line = String(rawLine || '').trim();
2614
+ if (!line) continue;
2615
+ if (line === '/exit' || line === '/quit') break;
2616
+ if (line.startsWith('/run ')) {
2617
+ await computerRun(token, line.slice(5), ctx);
2618
+ continue;
2619
+ }
2620
+ if (line === '/pwd') {
2621
+ await computerRun(token, 'pwd', ctx);
2622
+ continue;
2623
+ }
2624
+ if (line === '/audit' || line.startsWith('/audit ')) {
2625
+ const rawLimit = line.split(/\s+/, 2)[1];
2626
+ const limit = rawLimit ? Number.parseInt(rawLimit, 10) : 10;
2627
+ await computerAudit(token, ctx, Number.isFinite(limit) ? limit : 10);
2628
+ continue;
2629
+ }
2630
+ if (line.startsWith('/')) {
2631
+ const command = line.split(/\s+/, 1)[0];
2632
+ if (!KNOWN_CHAT_COMMANDS.has(command)) {
2633
+ console.log(`Unknown command: ${command}`);
2634
+ console.log('Type /help for commands, or remove the slash to ask the model.');
2635
+ }
2636
+ continue;
2637
+ }
2638
+ sessionId = await sendBusinessChat(token, ctx, line, sessionId, false, null, {
2639
+ worker,
2640
+ model,
2641
+ systemPrompt: chatSystemPrompt,
2642
+ allowedTools: initialOptions.allowedTools,
2643
+ });
2644
+ }
2645
+ return;
2646
+ }
2647
+
2455
2648
  const rl = readline.createInterface({
2456
2649
  input: process.stdin,
2457
2650
  output: process.stdout,
@@ -3080,11 +3273,14 @@ async function runComputer() {
3080
3273
  console.log(' --workspace Select a specific workspace/computer id');
3081
3274
  console.log(' --worker Cloud worker override: claude | openai');
3082
3275
  console.log(' --model Cloud model override');
3276
+ console.log(' --no-wait Start exec and print stream URL without waiting');
3083
3277
  console.log(' claude|codex Legacy local console backends');
3084
3278
  console.log('');
3085
3279
  console.log('Cloud commands:');
3086
3280
  console.log(' create <name> Create and wake an extra business computer');
3281
+ console.log(' activate Attach EC2 to --workspace and remember it as the default');
3087
3282
  console.log(' chat Interactive cloud workspace chat');
3283
+ console.log(' chat --message Send one non-interactive message and print the reply');
3088
3284
  console.log(' Ctrl-C during a cloud run interrupts it');
3089
3285
  console.log(' /start shows the beginner flow');
3090
3286
  console.log(' /status shows lane, Claude auth, and billing');
@@ -3196,6 +3392,7 @@ async function runComputer() {
3196
3392
  case 'chat': return computerChat(token, ctx, cloudOptions);
3197
3393
  case 'card': return computerCard(args.slice(1));
3198
3394
  case 'proof': return computerProof(token, ctx, cloudOptions);
3395
+ case 'activate': return computerActivate(token, ctx);
3199
3396
  case 'status': return computerStatus(token, ctx);
3200
3397
  case 'up':
3201
3398
  case 'wake': return computerWake(token, ctx);
package/commands/play.js CHANGED
@@ -273,6 +273,16 @@ function globalSyncCommands(player) {
273
273
  ];
274
274
  }
275
275
 
276
+ function handleSourceLine(state) {
277
+ const source = state.player_source || 'unknown';
278
+ if (source === 'flag') return 'Handle source: --as/--player.';
279
+ if (source === 'env') return 'Handle source: ATRIS_PLAYER/ATRIS_USERNAME.';
280
+ if (source === 'atris_account' || source === 'atris_session' || source === 'atris_profile') {
281
+ return `Handle source: ${source.replace(/_/g, ' ')}.`;
282
+ }
283
+ return 'Handle source: inferred. To choose one, run atris play --as <handle> or ATRIS_PLAYER=<handle> atris play.';
284
+ }
285
+
276
286
  function ensureStarterMission(taskDb, db, workspaceRoot, player, tasks, args = []) {
277
287
  if (hasFlag(args, '--no-seed')) return { tasks, seeded: null };
278
288
  if (selectMission(tasks, player)) return { tasks, seeded: null };
@@ -418,6 +428,7 @@ function render(state) {
418
428
  console.log('');
419
429
  console.log('AgentXP Mode');
420
430
  console.log(`Player ${state.player} | Workspace ${state.workspace_name}`);
431
+ console.log(handleSourceLine(state));
421
432
  console.log('');
422
433
 
423
434
  if (!state.mission) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "3.15.42",
3
+ "version": "3.15.44",
4
4
  "main": "bin/atris.js",
5
5
  "bin": {
6
6
  "atris": "bin/atris.js",