atris 3.12.1 → 3.14.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.
- package/README.md +39 -14
- package/bin/atris.js +39 -15
- package/commands/business.js +514 -24
- package/commands/computer.js +326 -9
- package/commands/errors.js +155 -0
- package/commands/proof.js +115 -0
- package/commands/pull.js +12 -6
- package/commands/push.js +8 -2
- package/commands/task.js +217 -0
- package/commands/visualize.js +324 -8
- package/lib/task-db.js +288 -0
- package/lib/todo-fallback.js +142 -0
- package/lib/todo.js +99 -184
- package/package.json +2 -2
- package/cli/__pycache__/atris_code.cpython-314.pyc +0 -0
- package/cli/__pycache__/runtime_guard.cpython-312.pyc +0 -0
- package/cli/__pycache__/runtime_guard.cpython-314.pyc +0 -0
package/commands/computer.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* atris computer --cloud — Open CLOUD workspace mode
|
|
6
6
|
* atris computer wake — Start the computer
|
|
7
7
|
* atris computer sleep — Stop (files persist)
|
|
8
|
+
* atris computer card — Show the local computer card
|
|
8
9
|
* atris computer run <command> — Run bash on EC2 (no LLM)
|
|
9
10
|
* atris computer grep <pattern> — Search files on EC2
|
|
10
11
|
* atris computer ls [path] — List files
|
|
@@ -52,8 +53,40 @@ const KNOWN_CHAT_COMMANDS = new Set([
|
|
|
52
53
|
'/start',
|
|
53
54
|
'/status',
|
|
54
55
|
'/worker',
|
|
56
|
+
'/workflow',
|
|
55
57
|
]);
|
|
56
58
|
|
|
59
|
+
const CODEOPS_WORKFLOW_PROMPT = `
|
|
60
|
+
## Atris CodeOps Workflow
|
|
61
|
+
|
|
62
|
+
You are running inside Atris CodeOps with full computer permissions (permission_mode=bypassPermissions).
|
|
63
|
+
Use those permissions to inspect, edit, test, commit, push, and open PRs when the task calls for it.
|
|
64
|
+
|
|
65
|
+
Do not behave like an open-ended chat.
|
|
66
|
+
Every coding or repo operation must follow the scientific workflow:
|
|
67
|
+
OBSERVE -> HYPOTHESIS -> PLAN -> ACTION -> VALIDATION -> EVIDENCE -> NEXT STATE.
|
|
68
|
+
|
|
69
|
+
For a new coding request, first show a concise PLAN with Files, Checks, Risk, and Merge policy.
|
|
70
|
+
If the user has not clearly approved execution, ask for approval before editing.
|
|
71
|
+
If the user explicitly says to execute, proceed after the concise plan.
|
|
72
|
+
|
|
73
|
+
After work, always report:
|
|
74
|
+
- edited_files
|
|
75
|
+
- commands_run
|
|
76
|
+
- validation_result
|
|
77
|
+
- evidence
|
|
78
|
+
- pr_url if any
|
|
79
|
+
- pr_state
|
|
80
|
+
- merge_state
|
|
81
|
+
- next_task
|
|
82
|
+
|
|
83
|
+
Use one of these next states:
|
|
84
|
+
planned, executing, validated, pr_opened, merge_ready, merge_blocked_checks, merge_blocked_policy, merged, failed, needs_human.
|
|
85
|
+
|
|
86
|
+
Never hide failures.
|
|
87
|
+
A blocked check or missing permission is evidence, not success.
|
|
88
|
+
`.trim();
|
|
89
|
+
|
|
57
90
|
function color(code, value) {
|
|
58
91
|
if (process.env.NO_COLOR || !process.stdout.isTTY) return String(value);
|
|
59
92
|
return `\x1b[${code}m${value}\x1b[0m`;
|
|
@@ -165,6 +198,7 @@ function printCloudHelp() {
|
|
|
165
198
|
console.log(' /start Show the beginner flow again');
|
|
166
199
|
console.log(' /help Show this menu');
|
|
167
200
|
console.log(' /status Show cloud computer status');
|
|
201
|
+
console.log(' /workflow Show the CodeOps workflow contract');
|
|
168
202
|
console.log(' /files [path] List files in the workspace');
|
|
169
203
|
console.log(' /run <cmd> Run shell without the model');
|
|
170
204
|
console.log(' /audit [n] Show recent runs, output, and charges');
|
|
@@ -201,6 +235,48 @@ function printCloudStartPanel(ctx, worker, model, billingLabel, authSummary = nu
|
|
|
201
235
|
console.log(ui.dim('Plain English goes to Atris. Slash commands control the computer.'));
|
|
202
236
|
}
|
|
203
237
|
|
|
238
|
+
function appendSystemPrompt(basePrompt, extraPrompt) {
|
|
239
|
+
if (!extraPrompt) return basePrompt || null;
|
|
240
|
+
if (basePrompt && basePrompt.includes('## Atris CodeOps Workflow')) return basePrompt;
|
|
241
|
+
if (!basePrompt) return extraPrompt;
|
|
242
|
+
return `${String(basePrompt).trim()}\n\n${extraPrompt}`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function codeOpsCloudOptions(options = {}) {
|
|
246
|
+
return {
|
|
247
|
+
...options,
|
|
248
|
+
worker: options.worker || 'claude',
|
|
249
|
+
mode: 'codeops',
|
|
250
|
+
systemPrompt: appendSystemPrompt(options.systemPrompt, CODEOPS_WORKFLOW_PROMPT),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function printCodeOpsStartPanel(ctx, worker, model, billingLabel, authSummary = null) {
|
|
255
|
+
console.log('');
|
|
256
|
+
console.log(ui.bold('Atris CodeOps Computer'));
|
|
257
|
+
console.log(`${ctx.businessName} ${ui.dim('/workspace persists, full permissions enabled')}`);
|
|
258
|
+
console.log(`Lane: ${ui.bold(formatWorkerName(worker))} ${ui.dim(formatCloudSelection({ worker, model }))}`);
|
|
259
|
+
console.log(`Billing: ${billingLabel}`);
|
|
260
|
+
if (authSummary) console.log(`${authSummary.label} ${ui.dim(authSummary.detail)}`);
|
|
261
|
+
console.log(`${ui.green('Workflow locked')} ${ui.dim('observe -> plan -> act -> validate -> evidence -> next')}`);
|
|
262
|
+
console.log('');
|
|
263
|
+
console.log(ui.bold('Start here'));
|
|
264
|
+
console.log(' Type a coding goal in plain English.');
|
|
265
|
+
console.log(' CodeOps will plan first, then execute after approval or explicit proceed language.');
|
|
266
|
+
console.log(' Use /workflow to see the contract, /run for shell, /audit for run history, /exit to leave.');
|
|
267
|
+
console.log('');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function printCodeOpsWorkflowContract() {
|
|
271
|
+
console.log('');
|
|
272
|
+
console.log(ui.bold('CodeOps workflow'));
|
|
273
|
+
console.log(' observe -> hypothesis -> plan -> action -> validation -> evidence -> next state');
|
|
274
|
+
console.log('');
|
|
275
|
+
console.log(' Required final evidence: edited_files, commands_run, validation_result, pr_url, pr_state, merge_state, next_task.');
|
|
276
|
+
console.log(' Allowed states: planned, executing, validated, pr_opened, merge_ready, merge_blocked_checks, merge_blocked_policy, merged, failed, needs_human.');
|
|
277
|
+
console.log(' Full permissions stay on; the workflow contract controls how the computer uses them.');
|
|
278
|
+
}
|
|
279
|
+
|
|
204
280
|
function buildLocalBridgeSystemPrompt(sessionId, localRoot, allowBash) {
|
|
205
281
|
const endpoint = `/api/cli/sessions/${sessionId}/file-op`;
|
|
206
282
|
const bashLine = allowBash
|
|
@@ -630,8 +706,8 @@ async function runLocalBridgeOp(token, sessionId, op, timeoutSeconds = 30) {
|
|
|
630
706
|
return data;
|
|
631
707
|
}
|
|
632
708
|
|
|
633
|
-
function readBusinessBinding() {
|
|
634
|
-
const bindingPath = path.join(
|
|
709
|
+
function readBusinessBinding(cwd = process.cwd()) {
|
|
710
|
+
const bindingPath = path.join(cwd, '.atris', 'business.json');
|
|
635
711
|
if (!fs.existsSync(bindingPath)) return null;
|
|
636
712
|
try {
|
|
637
713
|
return JSON.parse(fs.readFileSync(bindingPath, 'utf8'));
|
|
@@ -640,6 +716,160 @@ function readBusinessBinding() {
|
|
|
640
716
|
}
|
|
641
717
|
}
|
|
642
718
|
|
|
719
|
+
function readPackageMeta(cwd = process.cwd()) {
|
|
720
|
+
const packagePath = path.join(cwd, 'package.json');
|
|
721
|
+
if (!fs.existsSync(packagePath)) return null;
|
|
722
|
+
try {
|
|
723
|
+
return JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
724
|
+
} catch {
|
|
725
|
+
return null;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function relIfExists(cwd, target) {
|
|
730
|
+
return fs.existsSync(path.join(cwd, target)) ? target : null;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function detectValidationCommand(cwd = process.cwd(), pkg = null) {
|
|
734
|
+
const meta = pkg || readPackageMeta(cwd);
|
|
735
|
+
const testScript = meta?.scripts?.test;
|
|
736
|
+
if (testScript && !/no test specified/i.test(testScript)) return 'npm test';
|
|
737
|
+
if (fs.existsSync(path.join(cwd, 'pytest.ini')) || fs.existsSync(path.join(cwd, 'pyproject.toml'))) return 'pytest';
|
|
738
|
+
if (fs.existsSync(path.join(cwd, 'Cargo.toml'))) return 'cargo test';
|
|
739
|
+
if (fs.existsSync(path.join(cwd, 'go.mod'))) return 'go test ./...';
|
|
740
|
+
return 'none detected';
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function detectComputerType(cwd = process.cwd(), pkg = null, binding = null) {
|
|
744
|
+
if (binding?.computer_type) return binding.computer_type;
|
|
745
|
+
if (binding?.workspace_type) return binding.workspace_type;
|
|
746
|
+
const meta = pkg || readPackageMeta(cwd);
|
|
747
|
+
if (meta?.bin || fs.existsSync(path.join(cwd, 'bin')) || fs.existsSync(path.join(cwd, 'commands'))) return 'codeops';
|
|
748
|
+
if (fs.existsSync(path.join(cwd, 'docs')) || fs.existsSync(path.join(cwd, 'atris', 'wiki'))) return 'research';
|
|
749
|
+
return 'workspace';
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function buildComputerCard(cwd = process.cwd()) {
|
|
753
|
+
const binding = readBusinessBinding(cwd);
|
|
754
|
+
const pkg = readPackageMeta(cwd);
|
|
755
|
+
const folderName = path.basename(cwd);
|
|
756
|
+
const ownerName = binding?.name || pkg?.name || folderName;
|
|
757
|
+
const ownerType = binding ? 'business' : 'project';
|
|
758
|
+
const computerName = binding?.computer_name || binding?.workspace_name || `${ownerName} computer`;
|
|
759
|
+
const computerType = detectComputerType(cwd, pkg, binding);
|
|
760
|
+
const memory = [
|
|
761
|
+
relIfExists(cwd, 'atris/MAP.md'),
|
|
762
|
+
relIfExists(cwd, 'atris/TODO.md'),
|
|
763
|
+
relIfExists(cwd, 'atris/wiki'),
|
|
764
|
+
relIfExists(cwd, 'atris/logs'),
|
|
765
|
+
].filter(Boolean);
|
|
766
|
+
const artifacts = [
|
|
767
|
+
fs.existsSync(path.join(cwd, 'atris')) ? 'atris/reports/' : null,
|
|
768
|
+
relIfExists(cwd, '.atris/receipts'),
|
|
769
|
+
].filter(Boolean);
|
|
770
|
+
|
|
771
|
+
return {
|
|
772
|
+
ownerName,
|
|
773
|
+
ownerType,
|
|
774
|
+
computerName,
|
|
775
|
+
computerType,
|
|
776
|
+
workspace: cwd,
|
|
777
|
+
loop: 'plan -> do -> review',
|
|
778
|
+
memory,
|
|
779
|
+
validation: detectValidationCommand(cwd, pkg),
|
|
780
|
+
proof: binding ? 'atris computer proof' : 'atris proof run',
|
|
781
|
+
visual: 'atris visualize "<prompt>"',
|
|
782
|
+
artifacts,
|
|
783
|
+
generatedAt: new Date().toISOString(),
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
function renderList(items) {
|
|
788
|
+
return items.length ? items.join(', ') : 'none detected';
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function renderComputerCard(card) {
|
|
792
|
+
return [
|
|
793
|
+
'Atris Computer Card',
|
|
794
|
+
'',
|
|
795
|
+
` Owner: ${card.ownerName} (${card.ownerType})`,
|
|
796
|
+
` Computer: ${card.computerName}`,
|
|
797
|
+
` Type: ${card.computerType}`,
|
|
798
|
+
` Workspace: ${card.workspace}`,
|
|
799
|
+
` Loop: ${card.loop}`,
|
|
800
|
+
` Memory: ${renderList(card.memory)}`,
|
|
801
|
+
` Validate: ${card.validation}`,
|
|
802
|
+
` Proof: ${card.proof}`,
|
|
803
|
+
` Visual: ${card.visual}`,
|
|
804
|
+
` Artifacts: ${renderList(card.artifacts)}`,
|
|
805
|
+
].join('\n');
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function renderComputerCardMarkdown(card) {
|
|
809
|
+
return [
|
|
810
|
+
'# Atris Computer Card',
|
|
811
|
+
'',
|
|
812
|
+
`Generated: ${card.generatedAt}`,
|
|
813
|
+
'',
|
|
814
|
+
`- Owner: ${card.ownerName} (${card.ownerType})`,
|
|
815
|
+
`- Computer: ${card.computerName}`,
|
|
816
|
+
`- Type: ${card.computerType}`,
|
|
817
|
+
`- Workspace: ${card.workspace}`,
|
|
818
|
+
`- Loop: ${card.loop}`,
|
|
819
|
+
`- Memory: ${renderList(card.memory)}`,
|
|
820
|
+
`- Validate: ${card.validation}`,
|
|
821
|
+
`- Proof: ${card.proof}`,
|
|
822
|
+
`- Visual: ${card.visual}`,
|
|
823
|
+
`- Artifacts: ${renderList(card.artifacts)}`,
|
|
824
|
+
'',
|
|
825
|
+
].join('\n');
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function parseComputerCardArgs(args = []) {
|
|
829
|
+
const options = { write: false, out: null, help: false };
|
|
830
|
+
for (let i = 0; i < args.length; i++) {
|
|
831
|
+
const arg = args[i];
|
|
832
|
+
if (arg === '--help' || arg === '-h') options.help = true;
|
|
833
|
+
else if (arg === '--write') options.write = true;
|
|
834
|
+
else if (arg === '--out' && args[i + 1]) options.out = args[++i];
|
|
835
|
+
else if (arg.startsWith('--out=')) options.out = arg.slice('--out='.length);
|
|
836
|
+
}
|
|
837
|
+
return options;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function defaultComputerCardPath(cwd = process.cwd()) {
|
|
841
|
+
if (fs.existsSync(path.join(cwd, 'atris'))) {
|
|
842
|
+
return path.join(cwd, 'atris', 'reports', 'computer-card.md');
|
|
843
|
+
}
|
|
844
|
+
return path.join(cwd, 'computer-card.md');
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function computerCard(args = [], cwd = process.cwd()) {
|
|
848
|
+
const options = parseComputerCardArgs(args);
|
|
849
|
+
if (options.help) {
|
|
850
|
+
console.log('Usage: atris computer card [--write] [--out <path>]');
|
|
851
|
+
console.log('');
|
|
852
|
+
console.log('Show the local owner/computer card for this workspace.');
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
const card = buildComputerCard(cwd);
|
|
857
|
+
console.log(renderComputerCard(card));
|
|
858
|
+
|
|
859
|
+
if (options.write || options.out) {
|
|
860
|
+
const outputPath = options.out
|
|
861
|
+
? (path.isAbsolute(options.out) ? options.out : path.join(cwd, options.out))
|
|
862
|
+
: defaultComputerCardPath(cwd);
|
|
863
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
864
|
+
fs.writeFileSync(outputPath, renderComputerCardMarkdown(card), 'utf8');
|
|
865
|
+
console.log('');
|
|
866
|
+
console.log(`Wrote ${path.relative(cwd, outputPath) || outputPath}`);
|
|
867
|
+
return outputPath;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return card;
|
|
871
|
+
}
|
|
872
|
+
|
|
643
873
|
async function resolveBusinessContext(token) {
|
|
644
874
|
const binding = readBusinessBinding();
|
|
645
875
|
if (!binding) return null;
|
|
@@ -1386,6 +1616,8 @@ async function computerExec(token, prompt, ctx = null, options = {}) {
|
|
|
1386
1616
|
workspace_id: ctx.workspaceId,
|
|
1387
1617
|
...(options.worker ? { worker: options.worker } : {}),
|
|
1388
1618
|
...(options.model ? { model: options.model } : {}),
|
|
1619
|
+
...(options.systemPrompt ? { system_prompt: options.systemPrompt } : {}),
|
|
1620
|
+
...(options.allowedTools ? { allowed_tools: options.allowedTools } : {}),
|
|
1389
1621
|
},
|
|
1390
1622
|
timeoutMs: 40000,
|
|
1391
1623
|
});
|
|
@@ -1742,6 +1974,10 @@ async function computerChat(token, ctx, initialOptions = {}) {
|
|
|
1742
1974
|
return;
|
|
1743
1975
|
}
|
|
1744
1976
|
|
|
1977
|
+
const isCodeOps = initialOptions.mode === 'codeops' || ctx.slug === 'atris-codeops';
|
|
1978
|
+
const chatSystemPrompt = isCodeOps
|
|
1979
|
+
? appendSystemPrompt(initialOptions.systemPrompt, CODEOPS_WORKFLOW_PROMPT)
|
|
1980
|
+
: initialOptions.systemPrompt;
|
|
1745
1981
|
let sessionId = `biz-${ctx.businessId.slice(0, 8)}-${Date.now().toString(36)}`;
|
|
1746
1982
|
printCloudWordmark();
|
|
1747
1983
|
const selection = await chooseCloudLane(token, ctx, initialOptions);
|
|
@@ -1758,12 +1994,16 @@ async function computerChat(token, ctx, initialOptions = {}) {
|
|
|
1758
1994
|
return;
|
|
1759
1995
|
}
|
|
1760
1996
|
|
|
1761
|
-
|
|
1997
|
+
if (isCodeOps) {
|
|
1998
|
+
printCodeOpsStartPanel(ctx, worker, model, billingLabel, authSummary);
|
|
1999
|
+
} else {
|
|
2000
|
+
printCloudStartPanel(ctx, worker, model, billingLabel, authSummary);
|
|
2001
|
+
}
|
|
1762
2002
|
|
|
1763
2003
|
const rl = readline.createInterface({
|
|
1764
2004
|
input: process.stdin,
|
|
1765
2005
|
output: process.stdout,
|
|
1766
|
-
prompt: 'cloud> ',
|
|
2006
|
+
prompt: isCodeOps ? 'codeops> ' : 'cloud> ',
|
|
1767
2007
|
});
|
|
1768
2008
|
|
|
1769
2009
|
rl.prompt();
|
|
@@ -1782,7 +2022,11 @@ async function computerChat(token, ctx, initialOptions = {}) {
|
|
|
1782
2022
|
if (line === '/start') {
|
|
1783
2023
|
billingLabel = await describeBillingMode(token, ctx, worker);
|
|
1784
2024
|
authSummary = activeWorker(worker) === 'claude' ? await describeClaudeAuth(token, ctx) : null;
|
|
1785
|
-
|
|
2025
|
+
if (isCodeOps) {
|
|
2026
|
+
printCodeOpsStartPanel(ctx, worker, model, billingLabel, authSummary);
|
|
2027
|
+
} else {
|
|
2028
|
+
printCloudStartPanel(ctx, worker, model, billingLabel, authSummary);
|
|
2029
|
+
}
|
|
1786
2030
|
rl.prompt();
|
|
1787
2031
|
continue;
|
|
1788
2032
|
}
|
|
@@ -1796,6 +2040,11 @@ async function computerChat(token, ctx, initialOptions = {}) {
|
|
|
1796
2040
|
rl.prompt();
|
|
1797
2041
|
continue;
|
|
1798
2042
|
}
|
|
2043
|
+
if (line === '/workflow') {
|
|
2044
|
+
printCodeOpsWorkflowContract();
|
|
2045
|
+
rl.prompt();
|
|
2046
|
+
continue;
|
|
2047
|
+
}
|
|
1799
2048
|
if (line === '/pwd') {
|
|
1800
2049
|
await computerRun(token, 'pwd', ctx);
|
|
1801
2050
|
rl.prompt();
|
|
@@ -1889,7 +2138,12 @@ async function computerChat(token, ctx, initialOptions = {}) {
|
|
|
1889
2138
|
}
|
|
1890
2139
|
}
|
|
1891
2140
|
|
|
1892
|
-
sessionId = await sendBusinessChat(token, ctx, line, sessionId, false, rl, {
|
|
2141
|
+
sessionId = await sendBusinessChat(token, ctx, line, sessionId, false, rl, {
|
|
2142
|
+
worker,
|
|
2143
|
+
model,
|
|
2144
|
+
systemPrompt: chatSystemPrompt,
|
|
2145
|
+
allowedTools: initialOptions.allowedTools,
|
|
2146
|
+
});
|
|
1893
2147
|
rl.prompt();
|
|
1894
2148
|
}
|
|
1895
2149
|
} catch (error) {
|
|
@@ -2298,6 +2552,11 @@ async function runComputer() {
|
|
|
2298
2552
|
return;
|
|
2299
2553
|
}
|
|
2300
2554
|
|
|
2555
|
+
if (sub === 'card') {
|
|
2556
|
+
computerCard(args.slice(1));
|
|
2557
|
+
return;
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2301
2560
|
if (sub === 'claude' || sub === 'codex') {
|
|
2302
2561
|
computerLocalLegacy(args);
|
|
2303
2562
|
return;
|
|
@@ -2306,6 +2565,15 @@ async function runComputer() {
|
|
|
2306
2565
|
if (sub === '--help') {
|
|
2307
2566
|
console.log('Usage: atris computer [mode|command]');
|
|
2308
2567
|
console.log('');
|
|
2568
|
+
console.log('Atris computers are persistent AI workspaces for scoped jobs.');
|
|
2569
|
+
console.log('');
|
|
2570
|
+
console.log(' Owner = User | Business');
|
|
2571
|
+
console.log(' Owner has many Computers');
|
|
2572
|
+
console.log(' Computer = workspace + files + tools + secrets + memory + agents + validation');
|
|
2573
|
+
console.log('');
|
|
2574
|
+
console.log('Common types: codeops, research, CRM, reporting, recruiting, event ops, support, business ops.');
|
|
2575
|
+
console.log('A business can be a company, lab, collective, community, artist, team, or project.');
|
|
2576
|
+
console.log('');
|
|
2309
2577
|
console.log('First use:');
|
|
2310
2578
|
console.log(' cd ~/arena/atris-business/<business>');
|
|
2311
2579
|
console.log(' atris computer');
|
|
@@ -2313,12 +2581,13 @@ async function runComputer() {
|
|
|
2313
2581
|
console.log('');
|
|
2314
2582
|
console.log('Modes:');
|
|
2315
2583
|
console.log(' (default) Choose CLOUD vs LOCAL when both are available');
|
|
2584
|
+
console.log(' card Show the local owner/computer card, no login required');
|
|
2316
2585
|
console.log(' local Open LOCAL Atris mode; cloud brain edits this folder');
|
|
2317
2586
|
console.log(' proof Run the local-edit + cloud-isolation + audit proof');
|
|
2318
2587
|
console.log(' local-byo Open LOCAL BYO Claude mode; Anthropic tokens, no cloud audit');
|
|
2319
2588
|
console.log(' --cloud Open CLOUD workspace mode in the bound business workspace');
|
|
2320
2589
|
console.log(' cloud Open CLOUD workspace mode in the bound business workspace');
|
|
2321
|
-
console.log(' codeops Open Atris CodeOps
|
|
2590
|
+
console.log(' codeops Open Atris CodeOps workflow computer if your account has access');
|
|
2322
2591
|
console.log(' --worker Cloud worker override: claude | openai');
|
|
2323
2592
|
console.log(' --model Cloud model override');
|
|
2324
2593
|
console.log(' claude|codex Legacy local console backends');
|
|
@@ -2344,6 +2613,8 @@ async function runComputer() {
|
|
|
2344
2613
|
console.log('');
|
|
2345
2614
|
console.log('Examples:');
|
|
2346
2615
|
console.log(' atris computer');
|
|
2616
|
+
console.log(' atris computer card --write');
|
|
2617
|
+
console.log(' atris business init "My Lab" # shared owner + first/default computer');
|
|
2347
2618
|
console.log(' atris computer proof');
|
|
2348
2619
|
console.log(' atris computer local');
|
|
2349
2620
|
console.log(' atris computer codex');
|
|
@@ -2351,6 +2622,9 @@ async function runComputer() {
|
|
|
2351
2622
|
console.log(' atris computer --cloud --worker openai --model gpt-5.4');
|
|
2352
2623
|
console.log(' atris computer cloud');
|
|
2353
2624
|
console.log(' atris computer codeops');
|
|
2625
|
+
console.log(' atris computer codeops status');
|
|
2626
|
+
console.log(' atris computer codeops run "pwd"');
|
|
2627
|
+
console.log(' atris computer codeops exec "Plan a safe repo fix"');
|
|
2354
2628
|
console.log(' atris computer status');
|
|
2355
2629
|
console.log(' atris computer wake');
|
|
2356
2630
|
console.log(' atris computer run "ls -la /workspace"');
|
|
@@ -2370,7 +2644,44 @@ async function runComputer() {
|
|
|
2370
2644
|
console.error('Ask an Atris CodeOps admin to add you to the atris-codeops business.');
|
|
2371
2645
|
return;
|
|
2372
2646
|
}
|
|
2373
|
-
|
|
2647
|
+
const codeopsOptions = codeOpsCloudOptions(cloudOptions);
|
|
2648
|
+
const codeopsSub = args[1];
|
|
2649
|
+
const codeopsRest = args.slice(2).join(' ');
|
|
2650
|
+
if (!codeopsSub || codeopsSub === 'chat') {
|
|
2651
|
+
await computerChat(token, codeopsCtx, codeopsOptions);
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2654
|
+
switch (codeopsSub) {
|
|
2655
|
+
case '--help':
|
|
2656
|
+
case 'help':
|
|
2657
|
+
console.log('Usage: atris computer codeops [chat|status|wake|sleep|run|grep|ls|cat|exec|audit|workflow]');
|
|
2658
|
+
console.log('');
|
|
2659
|
+
console.log('Examples:');
|
|
2660
|
+
console.log(' atris computer codeops');
|
|
2661
|
+
console.log(' atris computer codeops status');
|
|
2662
|
+
console.log(' atris computer codeops run "pwd && git status --short"');
|
|
2663
|
+
console.log(' atris computer codeops exec "Plan the smallest safe fix, then wait"');
|
|
2664
|
+
return;
|
|
2665
|
+
case 'status': return computerStatus(token, codeopsCtx);
|
|
2666
|
+
case 'wake': return computerWake(token, codeopsCtx);
|
|
2667
|
+
case 'sleep': return computerSleep(token, codeopsCtx);
|
|
2668
|
+
case 'run': return computerRun(token, codeopsRest, codeopsCtx);
|
|
2669
|
+
case 'grep': return computerGrep(token, codeopsRest, codeopsCtx);
|
|
2670
|
+
case 'ls': return computerLs(token, codeopsRest || undefined, codeopsCtx);
|
|
2671
|
+
case 'cat': return computerCat(token, codeopsRest, codeopsCtx);
|
|
2672
|
+
case 'exec': return computerExec(token, codeopsRest, codeopsCtx, codeopsOptions);
|
|
2673
|
+
case 'audit': {
|
|
2674
|
+
const limit = codeopsRest ? Number.parseInt(codeopsRest, 10) : 10;
|
|
2675
|
+
return computerAudit(token, codeopsCtx, Number.isFinite(limit) ? limit : 10);
|
|
2676
|
+
}
|
|
2677
|
+
case 'workflow':
|
|
2678
|
+
printCodeOpsWorkflowContract();
|
|
2679
|
+
return;
|
|
2680
|
+
default:
|
|
2681
|
+
console.error(`Unknown CodeOps subcommand: ${codeopsSub}`);
|
|
2682
|
+
console.log('Run: atris computer codeops help');
|
|
2683
|
+
return;
|
|
2684
|
+
}
|
|
2374
2685
|
return;
|
|
2375
2686
|
}
|
|
2376
2687
|
|
|
@@ -2383,6 +2694,7 @@ async function runComputer() {
|
|
|
2383
2694
|
|
|
2384
2695
|
switch (sub) {
|
|
2385
2696
|
case 'chat': return computerChat(token, ctx, cloudOptions);
|
|
2697
|
+
case 'card': return computerCard(args.slice(1));
|
|
2386
2698
|
case 'proof': return computerProof(token, ctx, cloudOptions);
|
|
2387
2699
|
case 'status': return computerStatus(token, ctx);
|
|
2388
2700
|
case 'wake': return computerWake(token, ctx);
|
|
@@ -2405,4 +2717,9 @@ async function runComputer() {
|
|
|
2405
2717
|
}
|
|
2406
2718
|
}
|
|
2407
2719
|
|
|
2408
|
-
module.exports = {
|
|
2720
|
+
module.exports = {
|
|
2721
|
+
runComputer,
|
|
2722
|
+
buildComputerCard,
|
|
2723
|
+
renderComputerCard,
|
|
2724
|
+
renderComputerCardMarkdown,
|
|
2725
|
+
};
|
|
@@ -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 };
|