atris 3.12.0 → 3.13.0

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.
@@ -52,8 +52,40 @@ const KNOWN_CHAT_COMMANDS = new Set([
52
52
  '/start',
53
53
  '/status',
54
54
  '/worker',
55
+ '/workflow',
55
56
  ]);
56
57
 
58
+ const CODEOPS_WORKFLOW_PROMPT = `
59
+ ## Atris CodeOps Workflow
60
+
61
+ You are running inside Atris CodeOps with full computer permissions (permission_mode=bypassPermissions).
62
+ Use those permissions to inspect, edit, test, commit, push, and open PRs when the task calls for it.
63
+
64
+ Do not behave like an open-ended chat.
65
+ Every coding or repo operation must follow the scientific workflow:
66
+ OBSERVE -> HYPOTHESIS -> PLAN -> ACTION -> VALIDATION -> EVIDENCE -> NEXT STATE.
67
+
68
+ For a new coding request, first show a concise PLAN with Files, Checks, Risk, and Merge policy.
69
+ If the user has not clearly approved execution, ask for approval before editing.
70
+ If the user explicitly says to execute, proceed after the concise plan.
71
+
72
+ After work, always report:
73
+ - edited_files
74
+ - commands_run
75
+ - validation_result
76
+ - evidence
77
+ - pr_url if any
78
+ - pr_state
79
+ - merge_state
80
+ - next_task
81
+
82
+ Use one of these next states:
83
+ planned, executing, validated, pr_opened, merge_ready, merge_blocked_checks, merge_blocked_policy, merged, failed, needs_human.
84
+
85
+ Never hide failures.
86
+ A blocked check or missing permission is evidence, not success.
87
+ `.trim();
88
+
57
89
  function color(code, value) {
58
90
  if (process.env.NO_COLOR || !process.stdout.isTTY) return String(value);
59
91
  return `\x1b[${code}m${value}\x1b[0m`;
@@ -165,6 +197,7 @@ function printCloudHelp() {
165
197
  console.log(' /start Show the beginner flow again');
166
198
  console.log(' /help Show this menu');
167
199
  console.log(' /status Show cloud computer status');
200
+ console.log(' /workflow Show the CodeOps workflow contract');
168
201
  console.log(' /files [path] List files in the workspace');
169
202
  console.log(' /run <cmd> Run shell without the model');
170
203
  console.log(' /audit [n] Show recent runs, output, and charges');
@@ -201,6 +234,48 @@ function printCloudStartPanel(ctx, worker, model, billingLabel, authSummary = nu
201
234
  console.log(ui.dim('Plain English goes to Atris. Slash commands control the computer.'));
202
235
  }
203
236
 
237
+ function appendSystemPrompt(basePrompt, extraPrompt) {
238
+ if (!extraPrompt) return basePrompt || null;
239
+ if (basePrompt && basePrompt.includes('## Atris CodeOps Workflow')) return basePrompt;
240
+ if (!basePrompt) return extraPrompt;
241
+ return `${String(basePrompt).trim()}\n\n${extraPrompt}`;
242
+ }
243
+
244
+ function codeOpsCloudOptions(options = {}) {
245
+ return {
246
+ ...options,
247
+ worker: options.worker || 'claude',
248
+ mode: 'codeops',
249
+ systemPrompt: appendSystemPrompt(options.systemPrompt, CODEOPS_WORKFLOW_PROMPT),
250
+ };
251
+ }
252
+
253
+ function printCodeOpsStartPanel(ctx, worker, model, billingLabel, authSummary = null) {
254
+ console.log('');
255
+ console.log(ui.bold('Atris CodeOps Computer'));
256
+ console.log(`${ctx.businessName} ${ui.dim('/workspace persists, full permissions enabled')}`);
257
+ console.log(`Lane: ${ui.bold(formatWorkerName(worker))} ${ui.dim(formatCloudSelection({ worker, model }))}`);
258
+ console.log(`Billing: ${billingLabel}`);
259
+ if (authSummary) console.log(`${authSummary.label} ${ui.dim(authSummary.detail)}`);
260
+ console.log(`${ui.green('Workflow locked')} ${ui.dim('observe -> plan -> act -> validate -> evidence -> next')}`);
261
+ console.log('');
262
+ console.log(ui.bold('Start here'));
263
+ console.log(' Type a coding goal in plain English.');
264
+ console.log(' CodeOps will plan first, then execute after approval or explicit proceed language.');
265
+ console.log(' Use /workflow to see the contract, /run for shell, /audit for run history, /exit to leave.');
266
+ console.log('');
267
+ }
268
+
269
+ function printCodeOpsWorkflowContract() {
270
+ console.log('');
271
+ console.log(ui.bold('CodeOps workflow'));
272
+ console.log(' observe -> hypothesis -> plan -> action -> validation -> evidence -> next state');
273
+ console.log('');
274
+ console.log(' Required final evidence: edited_files, commands_run, validation_result, pr_url, pr_state, merge_state, next_task.');
275
+ console.log(' Allowed states: planned, executing, validated, pr_opened, merge_ready, merge_blocked_checks, merge_blocked_policy, merged, failed, needs_human.');
276
+ console.log(' Full permissions stay on; the workflow contract controls how the computer uses them.');
277
+ }
278
+
204
279
  function buildLocalBridgeSystemPrompt(sessionId, localRoot, allowBash) {
205
280
  const endpoint = `/api/cli/sessions/${sessionId}/file-op`;
206
281
  const bashLine = allowBash
@@ -1386,6 +1461,8 @@ async function computerExec(token, prompt, ctx = null, options = {}) {
1386
1461
  workspace_id: ctx.workspaceId,
1387
1462
  ...(options.worker ? { worker: options.worker } : {}),
1388
1463
  ...(options.model ? { model: options.model } : {}),
1464
+ ...(options.systemPrompt ? { system_prompt: options.systemPrompt } : {}),
1465
+ ...(options.allowedTools ? { allowed_tools: options.allowedTools } : {}),
1389
1466
  },
1390
1467
  timeoutMs: 40000,
1391
1468
  });
@@ -1742,6 +1819,10 @@ async function computerChat(token, ctx, initialOptions = {}) {
1742
1819
  return;
1743
1820
  }
1744
1821
 
1822
+ const isCodeOps = initialOptions.mode === 'codeops' || ctx.slug === 'atris-codeops';
1823
+ const chatSystemPrompt = isCodeOps
1824
+ ? appendSystemPrompt(initialOptions.systemPrompt, CODEOPS_WORKFLOW_PROMPT)
1825
+ : initialOptions.systemPrompt;
1745
1826
  let sessionId = `biz-${ctx.businessId.slice(0, 8)}-${Date.now().toString(36)}`;
1746
1827
  printCloudWordmark();
1747
1828
  const selection = await chooseCloudLane(token, ctx, initialOptions);
@@ -1758,12 +1839,16 @@ async function computerChat(token, ctx, initialOptions = {}) {
1758
1839
  return;
1759
1840
  }
1760
1841
 
1761
- printCloudStartPanel(ctx, worker, model, billingLabel, authSummary);
1842
+ if (isCodeOps) {
1843
+ printCodeOpsStartPanel(ctx, worker, model, billingLabel, authSummary);
1844
+ } else {
1845
+ printCloudStartPanel(ctx, worker, model, billingLabel, authSummary);
1846
+ }
1762
1847
 
1763
1848
  const rl = readline.createInterface({
1764
1849
  input: process.stdin,
1765
1850
  output: process.stdout,
1766
- prompt: 'cloud> ',
1851
+ prompt: isCodeOps ? 'codeops> ' : 'cloud> ',
1767
1852
  });
1768
1853
 
1769
1854
  rl.prompt();
@@ -1782,7 +1867,11 @@ async function computerChat(token, ctx, initialOptions = {}) {
1782
1867
  if (line === '/start') {
1783
1868
  billingLabel = await describeBillingMode(token, ctx, worker);
1784
1869
  authSummary = activeWorker(worker) === 'claude' ? await describeClaudeAuth(token, ctx) : null;
1785
- printCloudStartPanel(ctx, worker, model, billingLabel, authSummary);
1870
+ if (isCodeOps) {
1871
+ printCodeOpsStartPanel(ctx, worker, model, billingLabel, authSummary);
1872
+ } else {
1873
+ printCloudStartPanel(ctx, worker, model, billingLabel, authSummary);
1874
+ }
1786
1875
  rl.prompt();
1787
1876
  continue;
1788
1877
  }
@@ -1796,6 +1885,11 @@ async function computerChat(token, ctx, initialOptions = {}) {
1796
1885
  rl.prompt();
1797
1886
  continue;
1798
1887
  }
1888
+ if (line === '/workflow') {
1889
+ printCodeOpsWorkflowContract();
1890
+ rl.prompt();
1891
+ continue;
1892
+ }
1799
1893
  if (line === '/pwd') {
1800
1894
  await computerRun(token, 'pwd', ctx);
1801
1895
  rl.prompt();
@@ -1889,7 +1983,12 @@ async function computerChat(token, ctx, initialOptions = {}) {
1889
1983
  }
1890
1984
  }
1891
1985
 
1892
- sessionId = await sendBusinessChat(token, ctx, line, sessionId, false, rl, { worker, model });
1986
+ sessionId = await sendBusinessChat(token, ctx, line, sessionId, false, rl, {
1987
+ worker,
1988
+ model,
1989
+ systemPrompt: chatSystemPrompt,
1990
+ allowedTools: initialOptions.allowedTools,
1991
+ });
1893
1992
  rl.prompt();
1894
1993
  }
1895
1994
  } catch (error) {
@@ -2318,7 +2417,7 @@ async function runComputer() {
2318
2417
  console.log(' local-byo Open LOCAL BYO Claude mode; Anthropic tokens, no cloud audit');
2319
2418
  console.log(' --cloud Open CLOUD workspace mode in the bound business workspace');
2320
2419
  console.log(' cloud Open CLOUD workspace mode in the bound business workspace');
2321
- console.log(' codeops Open Atris CodeOps cloud workspace if your account has access');
2420
+ console.log(' codeops Open Atris CodeOps workflow computer if your account has access');
2322
2421
  console.log(' --worker Cloud worker override: claude | openai');
2323
2422
  console.log(' --model Cloud model override');
2324
2423
  console.log(' claude|codex Legacy local console backends');
@@ -2351,6 +2450,9 @@ async function runComputer() {
2351
2450
  console.log(' atris computer --cloud --worker openai --model gpt-5.4');
2352
2451
  console.log(' atris computer cloud');
2353
2452
  console.log(' atris computer codeops');
2453
+ console.log(' atris computer codeops status');
2454
+ console.log(' atris computer codeops run "pwd"');
2455
+ console.log(' atris computer codeops exec "Plan a safe repo fix"');
2354
2456
  console.log(' atris computer status');
2355
2457
  console.log(' atris computer wake');
2356
2458
  console.log(' atris computer run "ls -la /workspace"');
@@ -2370,7 +2472,44 @@ async function runComputer() {
2370
2472
  console.error('Ask an Atris CodeOps admin to add you to the atris-codeops business.');
2371
2473
  return;
2372
2474
  }
2373
- await computerChat(token, codeopsCtx, { worker: 'claude', ...cloudOptions });
2475
+ const codeopsOptions = codeOpsCloudOptions(cloudOptions);
2476
+ const codeopsSub = args[1];
2477
+ const codeopsRest = args.slice(2).join(' ');
2478
+ if (!codeopsSub || codeopsSub === 'chat') {
2479
+ await computerChat(token, codeopsCtx, codeopsOptions);
2480
+ return;
2481
+ }
2482
+ switch (codeopsSub) {
2483
+ case '--help':
2484
+ case 'help':
2485
+ console.log('Usage: atris computer codeops [chat|status|wake|sleep|run|grep|ls|cat|exec|audit|workflow]');
2486
+ console.log('');
2487
+ console.log('Examples:');
2488
+ console.log(' atris computer codeops');
2489
+ console.log(' atris computer codeops status');
2490
+ console.log(' atris computer codeops run "pwd && git status --short"');
2491
+ console.log(' atris computer codeops exec "Plan the smallest safe fix, then wait"');
2492
+ return;
2493
+ case 'status': return computerStatus(token, codeopsCtx);
2494
+ case 'wake': return computerWake(token, codeopsCtx);
2495
+ case 'sleep': return computerSleep(token, codeopsCtx);
2496
+ case 'run': return computerRun(token, codeopsRest, codeopsCtx);
2497
+ case 'grep': return computerGrep(token, codeopsRest, codeopsCtx);
2498
+ case 'ls': return computerLs(token, codeopsRest || undefined, codeopsCtx);
2499
+ case 'cat': return computerCat(token, codeopsRest, codeopsCtx);
2500
+ case 'exec': return computerExec(token, codeopsRest, codeopsCtx, codeopsOptions);
2501
+ case 'audit': {
2502
+ const limit = codeopsRest ? Number.parseInt(codeopsRest, 10) : 10;
2503
+ return computerAudit(token, codeopsCtx, Number.isFinite(limit) ? limit : 10);
2504
+ }
2505
+ case 'workflow':
2506
+ printCodeOpsWorkflowContract();
2507
+ return;
2508
+ default:
2509
+ console.error(`Unknown CodeOps subcommand: ${codeopsSub}`);
2510
+ console.log('Run: atris computer codeops help');
2511
+ return;
2512
+ }
2374
2513
  return;
2375
2514
  }
2376
2515
 
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Errors command for Atris CLI — admin dashboard over atris_error_events.
3
+ *
4
+ * Usage:
5
+ * atris errors List errors from last 24h, grouped by signature
6
+ * atris errors --hours 72 Widen the window (max 720h / 30d)
7
+ * atris errors --limit 1000 Raise the raw-event cap for grouping
8
+ * atris errors show <id> Full detail (stack trace, message) for one event
9
+ *
10
+ * Requires admin role on the user row.
11
+ */
12
+
13
+ const { loadCredentials } = require('../utils/auth');
14
+ const { apiRequestJson } = require('../utils/api');
15
+
16
+ function getToken() {
17
+ const creds = loadCredentials();
18
+ if (!creds || !creds.token) {
19
+ console.error('Not logged in. Run: atris login');
20
+ process.exit(1);
21
+ }
22
+ return creds.token;
23
+ }
24
+
25
+ function extractFlag(args, ...names) {
26
+ const remaining = [];
27
+ let value = null;
28
+ for (let i = 0; i < args.length; i++) {
29
+ const a = args[i];
30
+ const eq = a.indexOf('=');
31
+ const key = eq >= 0 ? a.slice(0, eq) : a;
32
+ if (names.includes(key)) {
33
+ value = eq >= 0 ? a.slice(eq + 1) : args[++i];
34
+ } else {
35
+ remaining.push(a);
36
+ }
37
+ }
38
+ return [value, remaining];
39
+ }
40
+
41
+ async function listErrors(args) {
42
+ const [hoursArg, r1] = extractFlag(args, '--hours', '-H');
43
+ const [limitArg, r2] = extractFlag(r1, '--limit', '-L');
44
+ const hours = hoursArg ? parseInt(hoursArg, 10) : 24;
45
+ const limit = limitArg ? parseInt(limitArg, 10) : 500;
46
+
47
+ const token = getToken();
48
+ const result = await apiRequestJson(`/errors?hours=${hours}&limit=${limit}`, {
49
+ method: 'GET',
50
+ token,
51
+ });
52
+
53
+ if (!result.ok) {
54
+ console.error(`Error: ${result.error || 'Failed to fetch errors'}`);
55
+ process.exit(1);
56
+ }
57
+
58
+ const data = result.data || {};
59
+ const groups = data.groups || [];
60
+
61
+ if (groups.length === 0) {
62
+ console.log(`No errors in the last ${hours}h. Clean.`);
63
+ return;
64
+ }
65
+
66
+ console.log(
67
+ `Errors — last ${data.window_hours}h — ` +
68
+ `${data.total_events} events across ${data.unique_signatures} signatures\n`,
69
+ );
70
+
71
+ groups.forEach((g, idx) => {
72
+ const s = g.sample || {};
73
+ const shortId = (s.id || '').substring(0, 8);
74
+ const last = s.created_at ? s.created_at.substring(0, 16).replace('T', ' ') : '';
75
+ const status = s.status_code ? ` [${s.status_code}]` : '';
76
+ const msg = (s.message || '').replace(/\s+/g, ' ').substring(0, 140);
77
+
78
+ console.log(`${idx + 1}. x${g.count} ${g.signature}${status}`);
79
+ if (last) console.log(` last: ${last} UTC latest id: ${shortId}`);
80
+ if (msg) console.log(` "${msg}"`);
81
+ console.log('');
82
+ });
83
+
84
+ console.log(
85
+ `Run \`atris errors show <id>\` for full stack trace of a specific event.`,
86
+ );
87
+ }
88
+
89
+ async function showError(errorId) {
90
+ if (!errorId) {
91
+ console.error('Usage: atris errors show <id>');
92
+ console.error('(id must be a full UUID — get one from `atris errors` output)');
93
+ process.exit(1);
94
+ }
95
+
96
+ const token = getToken();
97
+ const result = await apiRequestJson(`/errors/${encodeURIComponent(errorId)}`, {
98
+ method: 'GET',
99
+ token,
100
+ });
101
+
102
+ if (!result.ok) {
103
+ console.error(`Error: ${result.error || 'Failed to fetch error'}`);
104
+ process.exit(1);
105
+ }
106
+
107
+ const e = result.data || {};
108
+ console.log(`Error ${e.id || errorId}`);
109
+ console.log(` type: ${e.error_type || '?'}`);
110
+ console.log(` method: ${e.request_method || '?'}`);
111
+ console.log(` path: ${e.request_path || '?'}`);
112
+ console.log(` status: ${e.status_code || '?'}`);
113
+ console.log(` when: ${e.created_at || '?'}`);
114
+ console.log(` source: ${e.source || '?'}`);
115
+ if (e.user_id) console.log(` user: ${e.user_id}`);
116
+ console.log('');
117
+ console.log('Message:');
118
+ console.log(' ' + (e.message || '(none)').split('\n').join('\n '));
119
+ console.log('');
120
+ if (e.stack_trace) {
121
+ console.log('Stack trace:');
122
+ console.log(' ' + e.stack_trace.split('\n').join('\n '));
123
+ }
124
+ }
125
+
126
+ function printHelp() {
127
+ console.log('');
128
+ console.log('Usage:');
129
+ console.log(' atris errors List errors from last 24h, grouped');
130
+ console.log(' atris errors --hours 72 Widen the window (max 720h / 30d)');
131
+ console.log(' atris errors --limit 1000 Raise the raw-event cap');
132
+ console.log(' atris errors show <full-uuid> Full detail for one event');
133
+ console.log('');
134
+ console.log('Admin role required.');
135
+ console.log('');
136
+ }
137
+
138
+ async function errorsCommand() {
139
+ const args = process.argv.slice(3);
140
+ const sub = args[0];
141
+
142
+ if (sub === '--help' || sub === '-h' || sub === 'help') {
143
+ printHelp();
144
+ return;
145
+ }
146
+
147
+ if (sub === 'show') {
148
+ await showError(args[1]);
149
+ return;
150
+ }
151
+
152
+ await listErrors(args);
153
+ }
154
+
155
+ module.exports = { errorsCommand };