atris 3.15.43 → 3.15.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/computer.js +229 -32
- package/commands/xp.js +84 -9
- package/package.json +1 -1
package/commands/computer.js
CHANGED
|
@@ -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
|
|
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
|
-
...
|
|
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 ||
|
|
1152
|
-
added_at:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2098
|
-
|
|
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
|
-
|
|
2306
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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/xp.js
CHANGED
|
@@ -10,6 +10,7 @@ const DEFAULT_GRAPH_DAYS = 365;
|
|
|
10
10
|
const INTENSITY_CHARS = [' ', '.', ':', '*', '#'];
|
|
11
11
|
const ROW_LABELS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
12
12
|
const TASK_EPISODES_FILE = path.join('.atris', 'state', 'task_episodes.jsonl');
|
|
13
|
+
const BUSINESS_BINDING_FILE = path.join('.atris', 'business.json');
|
|
13
14
|
const CAREER_XP_RECEIPTS_FILE = path.join('.atris', 'state', 'career_xp_receipts.jsonl');
|
|
14
15
|
const CAREER_XP_PROJECTION_FILE = path.join('.atris', 'state', 'career_xp.projection.json');
|
|
15
16
|
const CAREER_XP_CURSOR_FILE = path.join('.atris', 'state', 'career_xp.cursor.json');
|
|
@@ -1122,6 +1123,24 @@ function workspaceName(workspace) {
|
|
|
1122
1123
|
return path.basename(workspace) || workspace;
|
|
1123
1124
|
}
|
|
1124
1125
|
|
|
1126
|
+
function publicWorkspaceBinding(workspace) {
|
|
1127
|
+
const binding = readJsonFile(path.join(workspace, BUSINESS_BINDING_FILE), null);
|
|
1128
|
+
if (!binding || typeof binding !== 'object') return null;
|
|
1129
|
+
const businessId = String(binding.business_id || binding.id || '').trim();
|
|
1130
|
+
const workspaceId = String(binding.workspace_id || '').trim();
|
|
1131
|
+
const businessSlug = slugify(binding.slug || binding.business_slug || binding.name);
|
|
1132
|
+
const workspaceTemplate = slugify(binding.workspace_template || binding.organization_type || binding.computer_type);
|
|
1133
|
+
if (!businessId && !workspaceId && !businessSlug) return null;
|
|
1134
|
+
return {
|
|
1135
|
+
business_id: businessId || null,
|
|
1136
|
+
workspace_id: workspaceId || null,
|
|
1137
|
+
business_slug: businessSlug || null,
|
|
1138
|
+
workspace_template: workspaceTemplate || null,
|
|
1139
|
+
computer: businessSlug || workspaceName(workspace),
|
|
1140
|
+
computer_slug: businessSlug || slugify(workspaceName(workspace)),
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1125
1144
|
function isVerifiedProjection(projection) {
|
|
1126
1145
|
return projection?.schema === 'atris.career_xp_projection.v1'
|
|
1127
1146
|
&& projection.integrity_status === 'verified'
|
|
@@ -1719,14 +1738,18 @@ function verifiedProjection(projection) {
|
|
|
1719
1738
|
|
|
1720
1739
|
function projectionWorkspaceSummaries(projection) {
|
|
1721
1740
|
if (Array.isArray(projection?.workspaces)) {
|
|
1722
|
-
return projection.workspaces.map(workspace =>
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1741
|
+
return projection.workspaces.map((workspace) => {
|
|
1742
|
+
const workspaceRoot = workspace.workspace_root || '';
|
|
1743
|
+
return {
|
|
1744
|
+
name: workspace.name || workspaceName(workspaceRoot || 'workspace'),
|
|
1745
|
+
workspace_root_hash: workspaceRoot ? sha256(path.resolve(workspaceRoot)) : null,
|
|
1746
|
+
included: Boolean(workspace.included),
|
|
1747
|
+
agent_xp: asNumber(workspace.total_xp),
|
|
1748
|
+
receipts_count: asNumber(workspace.receipts_count),
|
|
1749
|
+
integrity_status: workspace.integrity_status || 'unknown',
|
|
1750
|
+
...(workspaceRoot ? publicWorkspaceBinding(workspaceRoot) : null),
|
|
1751
|
+
};
|
|
1752
|
+
});
|
|
1730
1753
|
}
|
|
1731
1754
|
|
|
1732
1755
|
const workspaceRoot = projection?.workspace_root || path.resolve(process.cwd());
|
|
@@ -1737,9 +1760,43 @@ function projectionWorkspaceSummaries(projection) {
|
|
|
1737
1760
|
agent_xp: asNumber(projection?.total_agent_xp ?? projection?.total_xp),
|
|
1738
1761
|
receipts_count: asNumber(projection?.receipts_count),
|
|
1739
1762
|
integrity_status: projection?.integrity_status || projection?.integrity?.status || 'unknown',
|
|
1763
|
+
...publicWorkspaceBinding(workspaceRoot),
|
|
1740
1764
|
}];
|
|
1741
1765
|
}
|
|
1742
1766
|
|
|
1767
|
+
function uniqueTruthy(values) {
|
|
1768
|
+
return Array.from(new Set(values.map(value => String(value || '').trim()).filter(Boolean))).sort();
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
function syncAttribution(workspaces) {
|
|
1772
|
+
const included = workspaces.filter(workspace => workspace.included !== false);
|
|
1773
|
+
const scoped = included.length ? included : workspaces;
|
|
1774
|
+
const businessIds = uniqueTruthy(scoped.map(workspace => workspace.business_id));
|
|
1775
|
+
const workspaceIds = uniqueTruthy(scoped.map(workspace => workspace.workspace_id));
|
|
1776
|
+
const businessSlugs = uniqueTruthy(scoped.map(workspace => workspace.business_slug || workspace.computer_slug));
|
|
1777
|
+
const templates = uniqueTruthy(scoped.map(workspace => workspace.workspace_template));
|
|
1778
|
+
|
|
1779
|
+
if (businessIds.length > 1) {
|
|
1780
|
+
return { attribution_scope: 'multi_business', computer: 'multiple-workspaces' };
|
|
1781
|
+
}
|
|
1782
|
+
if (businessIds.length === 0 && scoped.length > 1 && businessSlugs.length > 1) {
|
|
1783
|
+
return { attribution_scope: 'multi_workspace', computer: 'multiple-workspaces' };
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
const businessId = businessIds[0] || null;
|
|
1787
|
+
const workspaceId = workspaceIds.length === 1 ? workspaceIds[0] : null;
|
|
1788
|
+
const businessSlug = businessSlugs.length === 1 ? businessSlugs[0] : null;
|
|
1789
|
+
const workspaceTemplate = templates.length === 1 ? templates[0] : null;
|
|
1790
|
+
return {
|
|
1791
|
+
attribution_scope: businessId || businessSlug ? 'business_bound' : 'workspace_only',
|
|
1792
|
+
business_id: businessId,
|
|
1793
|
+
workspace_id: workspaceId,
|
|
1794
|
+
business_slug: businessSlug,
|
|
1795
|
+
workspace_template: workspaceTemplate,
|
|
1796
|
+
computer: businessSlug || scoped[0]?.computer || scoped[0]?.name || 'local',
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1743
1800
|
function credentialHandle(credentials) {
|
|
1744
1801
|
return slugify(
|
|
1745
1802
|
credentials?.username
|
|
@@ -1798,6 +1855,7 @@ function buildAgentXpSyncPacket(args = []) {
|
|
|
1798
1855
|
: collectLocalXpProjection(projectionArgs);
|
|
1799
1856
|
const player = syncPlayer(args, projection);
|
|
1800
1857
|
const workspaces = projectionWorkspaceSummaries(projection);
|
|
1858
|
+
const attribution = syncAttribution(workspaces);
|
|
1801
1859
|
const totalXp = asNumber(projection.total_agent_xp ?? projection.agent_xp ?? projection.total_xp ?? projection.career_xp);
|
|
1802
1860
|
const receiptsCount = asNumber(projection.receipts_count);
|
|
1803
1861
|
const eligible = verifiedProjection(projection) && receiptsCount > 0 && totalXp > 0;
|
|
@@ -1825,7 +1883,12 @@ function buildAgentXpSyncPacket(args = []) {
|
|
|
1825
1883
|
schema: 'atris.agentxp_sync_packet.v1',
|
|
1826
1884
|
generated_at: new Date().toISOString(),
|
|
1827
1885
|
workspace_root_hash: workspaceRootHash,
|
|
1828
|
-
|
|
1886
|
+
attribution_scope: attribution.attribution_scope,
|
|
1887
|
+
business_id: attribution.business_id || null,
|
|
1888
|
+
workspace_id: attribution.workspace_id || null,
|
|
1889
|
+
business_slug: attribution.business_slug || null,
|
|
1890
|
+
workspace_template: attribution.workspace_template || null,
|
|
1891
|
+
computer: attribution.computer || projection.workspace_name || workspaces[0]?.name || 'local',
|
|
1829
1892
|
operator: player,
|
|
1830
1893
|
privacy: {
|
|
1831
1894
|
raw_proofs_included: false,
|
|
@@ -1840,6 +1903,12 @@ function buildAgentXpSyncPacket(args = []) {
|
|
|
1840
1903
|
local_evidence: {
|
|
1841
1904
|
schema: 'atris.agentxp_local_evidence.v1',
|
|
1842
1905
|
workspace_root_hash: workspaceRootHash,
|
|
1906
|
+
attribution_scope: attribution.attribution_scope,
|
|
1907
|
+
business_id: attribution.business_id || null,
|
|
1908
|
+
workspace_id: attribution.workspace_id || null,
|
|
1909
|
+
business_slug: attribution.business_slug || null,
|
|
1910
|
+
workspace_template: attribution.workspace_template || null,
|
|
1911
|
+
computer: attribution.computer || null,
|
|
1843
1912
|
workspaces,
|
|
1844
1913
|
verified_workspace_count: asNumber(projection.verified_workspace_count, verifiedProjection(projection) ? 1 : 0),
|
|
1845
1914
|
receipts_count: receiptsCount,
|
|
@@ -1864,6 +1933,12 @@ function buildAgentXpSyncPacket(args = []) {
|
|
|
1864
1933
|
gm_projection: {
|
|
1865
1934
|
schema: 'atris.gm_xp_projection.v1',
|
|
1866
1935
|
workspace_root_hash: workspaceRootHash,
|
|
1936
|
+
attribution_scope: attribution.attribution_scope,
|
|
1937
|
+
business_id: attribution.business_id || null,
|
|
1938
|
+
workspace_id: attribution.workspace_id || null,
|
|
1939
|
+
business_slug: attribution.business_slug || null,
|
|
1940
|
+
workspace_template: attribution.workspace_template || null,
|
|
1941
|
+
computer: attribution.computer || null,
|
|
1867
1942
|
operator: player,
|
|
1868
1943
|
player_score: {
|
|
1869
1944
|
agent_xp: totalXp,
|