atris 3.15.31 → 3.15.37
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 +3 -3
- package/ax +479 -21
- package/bin/atris.js +1 -1
- package/commands/aeo.js +377 -13
- package/commands/business.js +21 -2
- package/commands/computer.js +346 -16
- package/commands/gm.js +4 -4
- package/commands/lifecycle.js +115 -0
- package/commands/mission.js +9 -3
- package/commands/play.js +4 -4
- package/commands/pull.js +11 -7
- package/commands/xp.js +342 -13
- package/lib/runtime-bootstrap.js +107 -0
- package/package.json +1 -1
package/commands/computer.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* atris computer create <name> — Create and wake a business computer
|
|
7
7
|
* atris computer wake — Start the computer
|
|
8
8
|
* atris computer sleep — Stop (files persist)
|
|
9
|
+
* atris computer delete — Sleep, confirm, and delete a business computer
|
|
9
10
|
* atris computer card — Show the local computer card
|
|
10
11
|
* atris computer run <command> — Run bash on EC2 (no LLM)
|
|
11
12
|
* atris computer grep <pattern> — Search files on EC2
|
|
@@ -24,6 +25,7 @@ const { apiRequestJson, getApiBaseUrl, getAppBaseUrl } = require('../utils/api')
|
|
|
24
25
|
const { loadBusinesses, saveBusinesses } = require('./business');
|
|
25
26
|
const { consoleCommand, gatherAtrisContext, buildSystemPrompt } = require('./console');
|
|
26
27
|
const { streamSession } = require('./serve');
|
|
28
|
+
const { buildRemoteAtrisBootstrapCommand } = require('../lib/runtime-bootstrap');
|
|
27
29
|
|
|
28
30
|
function getToken() {
|
|
29
31
|
const creds = loadCredentials();
|
|
@@ -150,6 +152,38 @@ function formatBillingMode(worker) {
|
|
|
150
152
|
: 'Claude subscription lane';
|
|
151
153
|
}
|
|
152
154
|
|
|
155
|
+
function extractAttachedWorkspaceMismatch(...values) {
|
|
156
|
+
const text = values
|
|
157
|
+
.filter((value) => value !== null && value !== undefined)
|
|
158
|
+
.map((value) => {
|
|
159
|
+
if (typeof value === 'string') return value;
|
|
160
|
+
try {
|
|
161
|
+
return JSON.stringify(value);
|
|
162
|
+
} catch {
|
|
163
|
+
return String(value);
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
.join('\n');
|
|
167
|
+
const match = text.match(/attached to workspace\s+([a-z0-9-]+)\.\s*Activate workspace\s+([a-z0-9-]+)\s+to switch/i);
|
|
168
|
+
if (!match) return null;
|
|
169
|
+
return {
|
|
170
|
+
attachedWorkspaceId: match[1],
|
|
171
|
+
requestedWorkspaceId: match[2],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function contextForAttachedWorkspaceMismatch(ctx, failure) {
|
|
176
|
+
const mismatch = extractAttachedWorkspaceMismatch(
|
|
177
|
+
failure?.result?.error,
|
|
178
|
+
failure?.result?.errorMessage,
|
|
179
|
+
failure?.result?.data,
|
|
180
|
+
failure?.fallback?.error,
|
|
181
|
+
failure?.fallback?.payload
|
|
182
|
+
);
|
|
183
|
+
if (!mismatch?.attachedWorkspaceId || mismatch.attachedWorkspaceId === ctx?.workspaceId) return null;
|
|
184
|
+
return { ...ctx, workspaceId: mismatch.attachedWorkspaceId };
|
|
185
|
+
}
|
|
186
|
+
|
|
153
187
|
async function describeClaudeAuth(token, ctx) {
|
|
154
188
|
try {
|
|
155
189
|
const status = await fetchBusinessClaudeLoginStatus(token, ctx);
|
|
@@ -506,9 +540,33 @@ function parseComputerOptions(argv) {
|
|
|
506
540
|
const positional = [];
|
|
507
541
|
let worker = process.env.ATRIS_CLOUD_WORKER || null;
|
|
508
542
|
let model = process.env.ATRIS_CLOUD_MODEL || null;
|
|
543
|
+
let businessSlug = null;
|
|
544
|
+
let workspaceId = null;
|
|
509
545
|
|
|
510
546
|
for (let i = 0; i < argv.length; i++) {
|
|
511
547
|
const arg = argv[i];
|
|
548
|
+
if ((arg === '--business' || arg === '-b') && argv[i + 1]) {
|
|
549
|
+
businessSlug = argv[i + 1];
|
|
550
|
+
i++;
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
if (arg.startsWith('--business=')) {
|
|
554
|
+
businessSlug = arg.split('=', 2)[1] || null;
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
if ((arg === '--workspace' || arg === '--workspace-id') && argv[i + 1]) {
|
|
558
|
+
workspaceId = argv[i + 1];
|
|
559
|
+
i++;
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
if (arg.startsWith('--workspace=')) {
|
|
563
|
+
workspaceId = arg.split('=', 2)[1] || null;
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
if (arg.startsWith('--workspace-id=')) {
|
|
567
|
+
workspaceId = arg.split('=', 2)[1] || null;
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
512
570
|
if (arg === '--worker' && argv[i + 1]) {
|
|
513
571
|
worker = argv[i + 1];
|
|
514
572
|
i++;
|
|
@@ -541,6 +599,8 @@ function parseComputerOptions(argv) {
|
|
|
541
599
|
options: {
|
|
542
600
|
worker: worker || null,
|
|
543
601
|
model: model || null,
|
|
602
|
+
businessSlug: businessSlug ? String(businessSlug).trim() : null,
|
|
603
|
+
workspaceId: workspaceId ? String(workspaceId).trim() : null,
|
|
544
604
|
},
|
|
545
605
|
};
|
|
546
606
|
}
|
|
@@ -575,6 +635,29 @@ function parseComputerCreateArgs(argv = []) {
|
|
|
575
635
|
};
|
|
576
636
|
}
|
|
577
637
|
|
|
638
|
+
function parseComputerDeleteArgs(argv = []) {
|
|
639
|
+
const options = { help: false, confirm: null };
|
|
640
|
+
|
|
641
|
+
for (let i = 0; i < argv.length; i++) {
|
|
642
|
+
const arg = argv[i];
|
|
643
|
+
if (arg === '--help' || arg === '-h' || arg === 'help') {
|
|
644
|
+
options.help = true;
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
if (arg === '--confirm' && argv[i + 1]) {
|
|
648
|
+
options.confirm = argv[i + 1];
|
|
649
|
+
i++;
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
if (arg.startsWith('--confirm=')) {
|
|
653
|
+
options.confirm = arg.slice('--confirm='.length);
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
return options;
|
|
659
|
+
}
|
|
660
|
+
|
|
578
661
|
function formatCloudSelection(options = {}) {
|
|
579
662
|
const worker = activeWorker(options.worker);
|
|
580
663
|
const parts = [`worker=${worker}`];
|
|
@@ -954,9 +1037,33 @@ async function resolveBusinessContext(token) {
|
|
|
954
1037
|
return null;
|
|
955
1038
|
}
|
|
956
1039
|
|
|
957
|
-
|
|
1040
|
+
function cachedBusinessContext(slug) {
|
|
1041
|
+
if (!slug) return null;
|
|
1042
|
+
const wanted = String(slug).toLowerCase();
|
|
1043
|
+
const businesses = loadBusinesses();
|
|
1044
|
+
const cached = businesses[slug] || Object.values(businesses).find((entry) => {
|
|
1045
|
+
if (!entry) return false;
|
|
1046
|
+
return String(entry.slug || '').toLowerCase() === wanted
|
|
1047
|
+
|| String(entry.canonical_slug || '').toLowerCase() === wanted
|
|
1048
|
+
|| String(entry.name || '').toLowerCase() === wanted;
|
|
1049
|
+
});
|
|
1050
|
+
if (!cached?.business_id) return null;
|
|
1051
|
+
return {
|
|
1052
|
+
slug: cached.slug || slug,
|
|
1053
|
+
businessId: cached.business_id,
|
|
1054
|
+
workspaceId: cached.workspace_id || null,
|
|
1055
|
+
businessName: cached.name || cached.slug || slug,
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
async function resolveBusinessContextBySlug(token, slug, options = {}) {
|
|
958
1060
|
if (!slug) return null;
|
|
959
1061
|
|
|
1062
|
+
if (options.preferCache) {
|
|
1063
|
+
const cached = cachedBusinessContext(slug);
|
|
1064
|
+
if (cached?.workspaceId) return cached;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
960
1067
|
const businesses = loadBusinesses();
|
|
961
1068
|
const list = await apiRequestJson('/business/', { method: 'GET', token });
|
|
962
1069
|
if (list.ok) {
|
|
@@ -984,6 +1091,21 @@ async function resolveBusinessContextBySlug(token, slug) {
|
|
|
984
1091
|
return null;
|
|
985
1092
|
}
|
|
986
1093
|
|
|
1094
|
+
async function resolveComputerCommandContext(token, options = {}) {
|
|
1095
|
+
if (options.businessSlug || options.workspaceId) {
|
|
1096
|
+
const ctx = options.businessSlug
|
|
1097
|
+
? await resolveBusinessContextBySlug(token, options.businessSlug, { preferCache: true })
|
|
1098
|
+
: await resolveBusinessContext(token);
|
|
1099
|
+
if (!ctx?.businessId) return null;
|
|
1100
|
+
return {
|
|
1101
|
+
...ctx,
|
|
1102
|
+
workspaceId: options.workspaceId || ctx.workspaceId,
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
return resolveBusinessContext(token);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
987
1109
|
async function resolveBusinessOwnerForCreate(token, businessSlug = null) {
|
|
988
1110
|
const wantedSlug = businessSlug ? String(businessSlug).trim() : null;
|
|
989
1111
|
if (wantedSlug) {
|
|
@@ -1054,6 +1176,42 @@ async function runBusinessTerminalCommand(token, ctx, command, timeout = 30) {
|
|
|
1054
1176
|
);
|
|
1055
1177
|
}
|
|
1056
1178
|
|
|
1179
|
+
async function bootstrapBusinessComputerRuntime(token, ctx, boundary = 'computer-wake') {
|
|
1180
|
+
if (!ctx?.businessId || !ctx?.workspaceId) {
|
|
1181
|
+
return { ok: false, skipped: true, reason: 'missing_workspace' };
|
|
1182
|
+
}
|
|
1183
|
+
if (process.env.ATRIS_SKIP_RUNTIME_BOOTSTRAP === '1') {
|
|
1184
|
+
return { ok: true, skipped: true, reason: 'env' };
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
const command = buildRemoteAtrisBootstrapCommand({
|
|
1188
|
+
boundary,
|
|
1189
|
+
businessSlug: ctx.slug || '',
|
|
1190
|
+
businessId: ctx.businessId,
|
|
1191
|
+
workspaceId: ctx.workspaceId,
|
|
1192
|
+
});
|
|
1193
|
+
const result = await runBusinessTerminalCommand(token, ctx, command, 120);
|
|
1194
|
+
if (!result.ok) {
|
|
1195
|
+
console.log(' Runtime: bootstrap could not run.');
|
|
1196
|
+
console.log(` Recovery: atris computer run "npm install -g atris@latest" --business ${ctx.slug || ctx.businessId} --workspace ${ctx.workspaceId}`);
|
|
1197
|
+
return { ok: false, result };
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
const data = result.data || {};
|
|
1201
|
+
const output = String(data.stdout || data.output || data.result || '').trim();
|
|
1202
|
+
const line = output.split('\n').find((entry) => entry.includes('atris_runtime_bootstrap'));
|
|
1203
|
+
const recovery = output.split('\n').find((entry) => entry.startsWith('recovery='));
|
|
1204
|
+
if (line) {
|
|
1205
|
+
console.log(` Runtime: ${line.replace(/^atris_runtime_bootstrap\s*/, '')}`);
|
|
1206
|
+
} else {
|
|
1207
|
+
console.log(' Runtime: Atris bootstrap receipt written.');
|
|
1208
|
+
}
|
|
1209
|
+
if (recovery) {
|
|
1210
|
+
console.log(` Recovery: atris computer run "${recovery.slice('recovery='.length)}" --business ${ctx.slug || ctx.businessId} --workspace ${ctx.workspaceId}`);
|
|
1211
|
+
}
|
|
1212
|
+
return { ok: true, output };
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1057
1215
|
async function readBusinessWorkspaceFile(token, ctx, remotePath, timeoutMs = 15000) {
|
|
1058
1216
|
return apiRequestJson(
|
|
1059
1217
|
`/business/${ctx.businessId}/workspaces/${ctx.workspaceId}/file?path=${encodeURIComponent(remotePath)}`,
|
|
@@ -1217,6 +1375,7 @@ async function ensureBusinessAwake(token, ctx, maxWaitSec = 90) {
|
|
|
1217
1375
|
if (next.ok && next.data && next.data.status === 'running' && next.data.endpoint) {
|
|
1218
1376
|
const elapsed = Math.floor((Date.now() - start) / 1000);
|
|
1219
1377
|
console.log(`awake (${elapsed}s)`);
|
|
1378
|
+
await bootstrapBusinessComputerRuntime(token, ctx, 'computer-auto-wake');
|
|
1220
1379
|
return true;
|
|
1221
1380
|
}
|
|
1222
1381
|
}
|
|
@@ -1287,6 +1446,8 @@ async function computerWake(token, ctx = null) {
|
|
|
1287
1446
|
}
|
|
1288
1447
|
console.log(` Status: ${result.data.status}`);
|
|
1289
1448
|
if (result.data.endpoint) console.log(` Endpoint: ${result.data.endpoint}`);
|
|
1449
|
+
await bootstrapBusinessComputerRuntime(token, ctx, 'computer-wake');
|
|
1450
|
+
console.log(' Computer is awake.');
|
|
1290
1451
|
return;
|
|
1291
1452
|
}
|
|
1292
1453
|
|
|
@@ -1302,10 +1463,14 @@ async function computerWake(token, ctx = null) {
|
|
|
1302
1463
|
}
|
|
1303
1464
|
console.log(` Status: ${result.data.status}`);
|
|
1304
1465
|
console.log(` Endpoint: ${result.data.endpoint}`);
|
|
1466
|
+
console.log(' Computer is awake.');
|
|
1305
1467
|
}
|
|
1306
1468
|
|
|
1307
|
-
async function computerCreate(token, args = []) {
|
|
1469
|
+
async function computerCreate(token, args = [], defaults = {}) {
|
|
1308
1470
|
const options = parseComputerCreateArgs(args);
|
|
1471
|
+
if (!options.businessSlug && defaults.businessSlug) {
|
|
1472
|
+
options.businessSlug = defaults.businessSlug;
|
|
1473
|
+
}
|
|
1309
1474
|
if (options.help || !options.name) {
|
|
1310
1475
|
console.log('Usage: atris computer create <name> --business <slug>');
|
|
1311
1476
|
console.log('');
|
|
@@ -1373,6 +1538,7 @@ async function computerCreate(token, args = []) {
|
|
|
1373
1538
|
? 'running'
|
|
1374
1539
|
: (wake.data?.status || (activate.ok ? 'activated' : 'warming_up'));
|
|
1375
1540
|
rememberCreatedComputer(ctx, { ...workspace, id: workspaceId, name: workspace.name || options.name }, endpoint);
|
|
1541
|
+
await bootstrapBusinessComputerRuntime(token, { ...ctx, workspaceId }, 'computer-create');
|
|
1376
1542
|
|
|
1377
1543
|
const appBase = getAppBaseUrl();
|
|
1378
1544
|
console.log('');
|
|
@@ -1383,9 +1549,20 @@ async function computerCreate(token, args = []) {
|
|
|
1383
1549
|
if (endpoint) console.log(` Endpoint: ${endpoint}`);
|
|
1384
1550
|
console.log(` Dashboard: ${appBase}/dashboard/gm/${ctx.businessId}`);
|
|
1385
1551
|
console.log('');
|
|
1386
|
-
|
|
1387
|
-
console.log(
|
|
1388
|
-
console.log(
|
|
1552
|
+
const owner = ctx.slug || ctx.businessId;
|
|
1553
|
+
console.log('Start here:');
|
|
1554
|
+
console.log(` atris computer --business ${owner} --workspace ${workspaceId}`);
|
|
1555
|
+
console.log('');
|
|
1556
|
+
console.log('Org workspace:');
|
|
1557
|
+
console.log(` cd ~/arena/atris-business/${owner}`);
|
|
1558
|
+
console.log(' atris member activate operator');
|
|
1559
|
+
console.log(' atris member activate validator');
|
|
1560
|
+
console.log('');
|
|
1561
|
+
console.log('If the org workspace does not exist yet:');
|
|
1562
|
+
console.log(` atris business init "${ctx.businessName}"`);
|
|
1563
|
+
console.log('');
|
|
1564
|
+
console.log('Cost control:');
|
|
1565
|
+
console.log(` atris computer sleep --business ${owner} --workspace ${workspaceId}`);
|
|
1389
1566
|
}
|
|
1390
1567
|
|
|
1391
1568
|
async function computerSleep(token, ctx = null) {
|
|
@@ -1401,6 +1578,7 @@ async function computerSleep(token, ctx = null) {
|
|
|
1401
1578
|
return;
|
|
1402
1579
|
}
|
|
1403
1580
|
console.log(' Computer is sleeping. Files persist.');
|
|
1581
|
+
console.log(' No compute cost while sleeping.');
|
|
1404
1582
|
return;
|
|
1405
1583
|
}
|
|
1406
1584
|
|
|
@@ -1415,6 +1593,113 @@ async function computerSleep(token, ctx = null) {
|
|
|
1415
1593
|
return;
|
|
1416
1594
|
}
|
|
1417
1595
|
console.log(' Computer is sleeping. Files persist.');
|
|
1596
|
+
console.log(' No compute cost while sleeping.');
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
function rememberDeletedComputer(ctx) {
|
|
1600
|
+
const businesses = loadBusinesses();
|
|
1601
|
+
let changed = false;
|
|
1602
|
+
for (const [slug, entry] of Object.entries(businesses)) {
|
|
1603
|
+
if (!entry) continue;
|
|
1604
|
+
const sameBusiness = entry.business_id === ctx.businessId || slug === ctx.slug;
|
|
1605
|
+
const sameWorkspace = entry.workspace_id === ctx.workspaceId;
|
|
1606
|
+
if (sameBusiness && sameWorkspace) {
|
|
1607
|
+
delete entry.workspace_id;
|
|
1608
|
+
delete entry.computer_name;
|
|
1609
|
+
delete entry.endpoint;
|
|
1610
|
+
entry.deleted_workspace_id = ctx.workspaceId;
|
|
1611
|
+
entry.updated_at = new Date().toISOString();
|
|
1612
|
+
changed = true;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
if (changed) saveBusinesses(businesses);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
async function confirmComputerDelete(ctx, options) {
|
|
1619
|
+
const expected = `delete ${ctx.workspaceId}`;
|
|
1620
|
+
if (String(options.confirm || '').trim() === expected) return true;
|
|
1621
|
+
|
|
1622
|
+
console.log('');
|
|
1623
|
+
console.log('This will sleep the computer first, then delete the workspace record.');
|
|
1624
|
+
console.log(`Business: ${ctx.businessName}`);
|
|
1625
|
+
console.log(`Workspace: ${ctx.workspaceId}`);
|
|
1626
|
+
console.log(`Type "${expected}" to continue.`);
|
|
1627
|
+
|
|
1628
|
+
if (!useInteractiveTerminalUi()) {
|
|
1629
|
+
console.error(`Confirmation required. Re-run with: --confirm "${expected}"`);
|
|
1630
|
+
return false;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1634
|
+
const answer = String(await questionAsync(rl, 'Confirm: ') || '').trim();
|
|
1635
|
+
rl.close();
|
|
1636
|
+
if (answer === expected) return true;
|
|
1637
|
+
|
|
1638
|
+
console.error('Delete cancelled.');
|
|
1639
|
+
return false;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
async function computerDelete(token, ctx, options = {}, args = []) {
|
|
1643
|
+
const deleteOptions = parseComputerDeleteArgs(args);
|
|
1644
|
+
if (deleteOptions.help) {
|
|
1645
|
+
console.log('Usage: atris computer delete --business <slug> --workspace <workspace-id>');
|
|
1646
|
+
console.log('');
|
|
1647
|
+
console.log('Sleeps the computer first, then deletes the non-default workspace after confirmation.');
|
|
1648
|
+
console.log('');
|
|
1649
|
+
console.log('Examples:');
|
|
1650
|
+
console.log(' atris computer delete --business atris-labs --workspace ws_123');
|
|
1651
|
+
console.log(' atris computer delete --business atris-labs --workspace ws_123 --confirm "delete ws_123"');
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
if (!ctx?.businessId) {
|
|
1656
|
+
console.error('No business found.');
|
|
1657
|
+
console.error('Pass: --business <slug> --workspace <workspace-id>');
|
|
1658
|
+
process.exitCode = 1;
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
if (!options.workspaceId || !ctx.workspaceId) {
|
|
1663
|
+
console.error('Refusing to delete without an explicit workspace id.');
|
|
1664
|
+
console.error('Pass: --workspace <workspace-id>');
|
|
1665
|
+
process.exitCode = 1;
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
const confirmed = await confirmComputerDelete(ctx, deleteOptions);
|
|
1670
|
+
if (!confirmed) {
|
|
1671
|
+
process.exitCode = 1;
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
console.log(`Sleeping computer for ${ctx.businessName}...`);
|
|
1676
|
+
const slept = await apiRequestJson(`/business/${ctx.businessId}/ai-computer/sleep`, {
|
|
1677
|
+
method: 'POST',
|
|
1678
|
+
token,
|
|
1679
|
+
body: {},
|
|
1680
|
+
});
|
|
1681
|
+
if (!slept.ok) {
|
|
1682
|
+
console.error(`Failed to sleep computer: ${slept.errorMessage || slept.error || slept.status}`);
|
|
1683
|
+
process.exitCode = 1;
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
console.log(' Computer is sleeping. Files persist.');
|
|
1688
|
+
console.log(`Deleting workspace ${ctx.workspaceId}...`);
|
|
1689
|
+
const deleted = await apiRequestJson(`/business/${ctx.businessId}/workspaces/${ctx.workspaceId}`, {
|
|
1690
|
+
method: 'DELETE',
|
|
1691
|
+
token,
|
|
1692
|
+
});
|
|
1693
|
+
if (!deleted.ok) {
|
|
1694
|
+
console.error(`Failed to delete computer: ${deleted.errorMessage || deleted.error || deleted.status}`);
|
|
1695
|
+
if (deleted.status === 400) console.error('Default workspaces cannot be deleted.');
|
|
1696
|
+
process.exitCode = 1;
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
rememberDeletedComputer(ctx);
|
|
1701
|
+
console.log(' Computer deleted.');
|
|
1702
|
+
console.log(' Cost gate: sleeping before delete completed.');
|
|
1418
1703
|
}
|
|
1419
1704
|
|
|
1420
1705
|
async function computerRun(token, command, ctx = null) {
|
|
@@ -2105,6 +2390,9 @@ async function sendBusinessChat(token, ctx, message, sessionId, resetContext = f
|
|
|
2105
2390
|
maxTurns: 25,
|
|
2106
2391
|
});
|
|
2107
2392
|
if (!fallback.ok) {
|
|
2393
|
+
if (typeof options.onFailure === 'function') {
|
|
2394
|
+
options.onFailure({ result, fallback });
|
|
2395
|
+
}
|
|
2108
2396
|
console.error(`Failed: ${result.error || result.status}`);
|
|
2109
2397
|
if (fallback.error) {
|
|
2110
2398
|
console.error(`Fallback failed: ${fallback.error}`);
|
|
@@ -2616,12 +2904,33 @@ async function computerProof(token, ctx, initialOptions = {}) {
|
|
|
2616
2904
|
|
|
2617
2905
|
console.log(ui.bold('Run'));
|
|
2618
2906
|
console.log(` prompt: ${prompt}`);
|
|
2619
|
-
|
|
2907
|
+
let activeCtx = ctx;
|
|
2908
|
+
let chatFailure = null;
|
|
2909
|
+
let nextSessionId = await sendBusinessChat(token, activeCtx, prompt, sessionId, true, null, {
|
|
2620
2910
|
worker,
|
|
2621
2911
|
model,
|
|
2622
2912
|
systemPrompt,
|
|
2623
2913
|
localCliSessionId: bridge.sessionId,
|
|
2914
|
+
onFailure: (failure) => {
|
|
2915
|
+
chatFailure = failure;
|
|
2916
|
+
},
|
|
2624
2917
|
});
|
|
2918
|
+
const retryCtx = contextForAttachedWorkspaceMismatch(activeCtx, chatFailure);
|
|
2919
|
+
if (retryCtx) {
|
|
2920
|
+
console.log('');
|
|
2921
|
+
console.log(`Retrying proof against attached workspace ${retryCtx.workspaceId}...`);
|
|
2922
|
+
activeCtx = retryCtx;
|
|
2923
|
+
chatFailure = null;
|
|
2924
|
+
nextSessionId = await sendBusinessChat(token, activeCtx, prompt, `${sessionId}-attached`, true, null, {
|
|
2925
|
+
worker,
|
|
2926
|
+
model,
|
|
2927
|
+
systemPrompt,
|
|
2928
|
+
localCliSessionId: bridge.sessionId,
|
|
2929
|
+
onFailure: (failure) => {
|
|
2930
|
+
chatFailure = failure;
|
|
2931
|
+
},
|
|
2932
|
+
});
|
|
2933
|
+
}
|
|
2625
2934
|
|
|
2626
2935
|
const localPath = path.join(bridge.workingDir, fileName);
|
|
2627
2936
|
let localContent = '';
|
|
@@ -2632,10 +2941,10 @@ async function computerProof(token, ctx, initialOptions = {}) {
|
|
|
2632
2941
|
}
|
|
2633
2942
|
const localOk = localContent === expected;
|
|
2634
2943
|
|
|
2635
|
-
const cloudFile = await readBusinessWorkspaceFile(token,
|
|
2944
|
+
const cloudFile = await readBusinessWorkspaceFile(token, activeCtx, fileName, 15000);
|
|
2636
2945
|
const cloudClear = !cloudFile.ok && cloudFile.status === 404;
|
|
2637
2946
|
|
|
2638
|
-
const audit = await fetchBusinessChatAudit(token,
|
|
2947
|
+
const audit = await fetchBusinessChatAudit(token, activeCtx, 5);
|
|
2639
2948
|
const rows = audit.ok ? (audit.data?.rows || []) : [];
|
|
2640
2949
|
const auditRow = rows.find((row) => row.session_id === nextSessionId || row.preview?.includes(fileName)) || rows[0] || {};
|
|
2641
2950
|
const auditOk = audit.ok && auditRow.status === 'completed' && String(auditRow.result_preview || '').includes('ATRIS COMPUTER PROOF OK');
|
|
@@ -2669,6 +2978,13 @@ async function runComputer() {
|
|
|
2669
2978
|
const sub = args[0];
|
|
2670
2979
|
|
|
2671
2980
|
if (!sub) {
|
|
2981
|
+
if (cloudOptions.businessSlug || cloudOptions.workspaceId) {
|
|
2982
|
+
const token = getToken();
|
|
2983
|
+
const ctx = await resolveComputerCommandContext(token, cloudOptions);
|
|
2984
|
+
await computerChat(token, ctx, cloudOptions);
|
|
2985
|
+
return;
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2672
2988
|
const hasBusinessBinding = Boolean(readBusinessBinding());
|
|
2673
2989
|
const hasLocalHarness = Boolean(findAtrisCodeTerminal());
|
|
2674
2990
|
const surface = await chooseComputerSurface(hasBusinessBinding, hasLocalHarness);
|
|
@@ -2695,7 +3011,7 @@ async function runComputer() {
|
|
|
2695
3011
|
|
|
2696
3012
|
if (sub === '--local' || sub === 'local') {
|
|
2697
3013
|
const token = getToken();
|
|
2698
|
-
const ctx = await
|
|
3014
|
+
const ctx = await resolveComputerCommandContext(token, cloudOptions);
|
|
2699
3015
|
if (ctx) {
|
|
2700
3016
|
await computerLocalAtris(token, ctx, cloudOptions);
|
|
2701
3017
|
return;
|
|
@@ -2706,7 +3022,7 @@ async function runComputer() {
|
|
|
2706
3022
|
|
|
2707
3023
|
if (sub === 'local-atris') {
|
|
2708
3024
|
const token = getToken();
|
|
2709
|
-
const ctx = await
|
|
3025
|
+
const ctx = await resolveComputerCommandContext(token, cloudOptions);
|
|
2710
3026
|
await computerLocalAtris(token, ctx, cloudOptions);
|
|
2711
3027
|
return;
|
|
2712
3028
|
}
|
|
@@ -2760,20 +3076,23 @@ async function runComputer() {
|
|
|
2760
3076
|
console.log(' --cloud Open CLOUD workspace mode in the bound business workspace');
|
|
2761
3077
|
console.log(' cloud Open CLOUD workspace mode in the bound business workspace');
|
|
2762
3078
|
console.log(' codeops Open Atris CodeOps workflow computer if your account has access');
|
|
3079
|
+
console.log(' --business Select a business by slug');
|
|
3080
|
+
console.log(' --workspace Select a specific workspace/computer id');
|
|
2763
3081
|
console.log(' --worker Cloud worker override: claude | openai');
|
|
2764
3082
|
console.log(' --model Cloud model override');
|
|
2765
3083
|
console.log(' claude|codex Legacy local console backends');
|
|
2766
3084
|
console.log('');
|
|
2767
3085
|
console.log('Cloud commands:');
|
|
2768
|
-
console.log(' create <name> Create and wake
|
|
3086
|
+
console.log(' create <name> Create and wake an extra business computer');
|
|
2769
3087
|
console.log(' chat Interactive cloud workspace chat');
|
|
2770
3088
|
console.log(' Ctrl-C during a cloud run interrupts it');
|
|
2771
3089
|
console.log(' /start shows the beginner flow');
|
|
2772
3090
|
console.log(' /status shows lane, Claude auth, and billing');
|
|
2773
3091
|
console.log(' /audit [n] shows recent cloud runs inside chat');
|
|
2774
3092
|
console.log(' status Show computer status');
|
|
2775
|
-
console.log(' wake
|
|
3093
|
+
console.log(' up|wake Start the computer');
|
|
2776
3094
|
console.log(' sleep Stop the computer (files persist)');
|
|
3095
|
+
console.log(' delete Sleep, confirm, and delete a business computer');
|
|
2777
3096
|
console.log(' run <cmd> Run bash on EC2 (no LLM cost)');
|
|
2778
3097
|
console.log(' grep <pattern> Search files on EC2');
|
|
2779
3098
|
console.log(' ls [path] List files');
|
|
@@ -2787,8 +3106,11 @@ async function runComputer() {
|
|
|
2787
3106
|
console.log('Examples:');
|
|
2788
3107
|
console.log(' atris computer');
|
|
2789
3108
|
console.log(' atris computer card --write');
|
|
2790
|
-
console.log(' atris
|
|
2791
|
-
console.log(' atris
|
|
3109
|
+
console.log(' atris business init "My Lab" # first/default computer with Atris + operator');
|
|
3110
|
+
console.log(' atris computer create "Recruiting Computer" --business atris-labs');
|
|
3111
|
+
console.log(' atris computer --business atris-labs --workspace <workspace-id>');
|
|
3112
|
+
console.log(' atris computer sleep --business atris-labs --workspace <workspace-id>');
|
|
3113
|
+
console.log(' atris computer delete --business atris-labs --workspace <workspace-id>');
|
|
2792
3114
|
console.log(' atris computer proof');
|
|
2793
3115
|
console.log(' atris computer local');
|
|
2794
3116
|
console.log(' atris computer codex');
|
|
@@ -2809,7 +3131,11 @@ async function runComputer() {
|
|
|
2809
3131
|
}
|
|
2810
3132
|
|
|
2811
3133
|
const token = getToken();
|
|
2812
|
-
|
|
3134
|
+
if (sub === 'create') {
|
|
3135
|
+
return computerCreate(token, args.slice(1), cloudOptions);
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
const ctx = await resolveComputerCommandContext(token, cloudOptions);
|
|
2813
3139
|
|
|
2814
3140
|
if (sub === 'codeops') {
|
|
2815
3141
|
const codeopsCtx = await resolveBusinessContextBySlug(token, 'atris-codeops');
|
|
@@ -2869,11 +3195,13 @@ async function runComputer() {
|
|
|
2869
3195
|
switch (sub) {
|
|
2870
3196
|
case 'chat': return computerChat(token, ctx, cloudOptions);
|
|
2871
3197
|
case 'card': return computerCard(args.slice(1));
|
|
2872
|
-
case 'create': return computerCreate(token, args.slice(1));
|
|
2873
3198
|
case 'proof': return computerProof(token, ctx, cloudOptions);
|
|
2874
3199
|
case 'status': return computerStatus(token, ctx);
|
|
3200
|
+
case 'up':
|
|
2875
3201
|
case 'wake': return computerWake(token, ctx);
|
|
2876
3202
|
case 'sleep': return computerSleep(token, ctx);
|
|
3203
|
+
case 'delete':
|
|
3204
|
+
case 'rm': return computerDelete(token, ctx, cloudOptions, args.slice(1));
|
|
2877
3205
|
case 'run': return computerRun(token, rest, ctx);
|
|
2878
3206
|
case 'grep': return computerGrep(token, rest, ctx);
|
|
2879
3207
|
case 'ls': return computerLs(token, rest || undefined, ctx);
|
|
@@ -2897,4 +3225,6 @@ module.exports = {
|
|
|
2897
3225
|
buildComputerCard,
|
|
2898
3226
|
renderComputerCard,
|
|
2899
3227
|
renderComputerCardMarkdown,
|
|
3228
|
+
extractAttachedWorkspaceMismatch,
|
|
3229
|
+
contextForAttachedWorkspaceMismatch,
|
|
2900
3230
|
};
|
package/commands/gm.js
CHANGED
|
@@ -5,7 +5,7 @@ const os = require('os');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
7
|
const AGENTXP_LEADERBOARD_URL = 'https://api.atris.ai/api/agentxp/leaderboard';
|
|
8
|
-
const AGENTXP_GLOBAL_SYNC_RULE = '
|
|
8
|
+
const AGENTXP_GLOBAL_SYNC_RULE = 'Run atris login, then sync. Owner-provided sync tokens are guided-demo fallback only.';
|
|
9
9
|
|
|
10
10
|
const ROLE_PLAYERS_TO_IGNORE = new Set([
|
|
11
11
|
'game-manager',
|
|
@@ -266,9 +266,9 @@ function compactTask(task) {
|
|
|
266
266
|
|
|
267
267
|
function globalSyncCommands(player) {
|
|
268
268
|
return [
|
|
269
|
-
`atris xp sync --local --as ${player} --token <owner-provided-token>`,
|
|
270
269
|
'atris login',
|
|
271
|
-
|
|
270
|
+
'atris xp sync --local',
|
|
271
|
+
'atris xp sync --local --token <owner-provided-token>',
|
|
272
272
|
];
|
|
273
273
|
}
|
|
274
274
|
|
|
@@ -367,7 +367,7 @@ function render(state) {
|
|
|
367
367
|
|
|
368
368
|
console.log('');
|
|
369
369
|
console.log('XP rule: no proof, no AgentXP; accept/revise stays human-gated.');
|
|
370
|
-
console.log('Global sync:
|
|
370
|
+
console.log('Global sync: run atris login, then sync; owner tokens are guided-demo fallback only.');
|
|
371
371
|
console.log(`Leaderboard: ${state.leaderboard_url}`);
|
|
372
372
|
console.log('');
|
|
373
373
|
console.log('Next commands:');
|