atris 3.15.57 → 3.16.1
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/AGENTS.md +2 -2
- package/GETTING_STARTED.md +1 -1
- package/PERSONA.md +4 -4
- package/README.md +12 -11
- package/atris/skills/copy-editor/SKILL.md +30 -4
- package/atris/skills/improve/SKILL.md +18 -20
- package/atris/wiki/concepts/agent-activation-contract.md +5 -3
- package/atris/wiki/concepts/workspace-initialization-contract.md +4 -4
- package/atris/wiki/index.md +1 -0
- package/ax +522 -73
- package/bin/atris.js +78 -44
- package/commands/align.js +0 -14
- package/commands/apps.js +102 -1
- package/commands/autopilot.js +628 -31
- package/commands/brain.js +219 -34
- package/commands/brainstorm.js +0 -829
- package/commands/compile.js +569 -0
- package/commands/computer.js +0 -60
- package/commands/improve.js +501 -0
- package/commands/integrations.js +233 -71
- package/commands/lesson.js +44 -0
- package/commands/member.js +4498 -226
- package/commands/mission.js +302 -27
- package/commands/now.js +89 -1
- package/commands/probe.js +366 -0
- package/commands/radar.js +181 -56
- package/commands/recap.js +203 -0
- package/commands/skill.js +6 -2
- package/commands/soul.js +0 -4
- package/commands/task.js +5587 -499
- package/commands/terminal.js +14 -10
- package/commands/wiki.js +87 -1
- package/commands/workflow.js +288 -73
- package/commands/worktree.js +52 -15
- package/commands/xp.js +6 -65
- package/lib/auto-accept-certified.js +294 -0
- package/lib/file-ops.js +0 -184
- package/lib/member-alive.js +232 -0
- package/lib/policy-lessons.js +280 -0
- package/lib/receipt-evidence.js +64 -0
- package/lib/state-detection.js +75 -1
- package/lib/task-db.js +568 -16
- package/lib/task-proof.js +43 -0
- package/package.json +1 -1
- package/utils/auth.js +13 -4
- package/commands/research.js +0 -52
- package/lib/section-merge.js +0 -196
package/bin/atris.js
CHANGED
|
@@ -67,8 +67,8 @@ const ensureValidCredentials = (opts) => _ensureValidCredentials(apiRequestJson,
|
|
|
67
67
|
const fetchMyAgents = (token) => _fetchMyAgents(token, apiRequestJson);
|
|
68
68
|
const displayAccountSummary = () => _displayAccountSummary(apiRequestJson);
|
|
69
69
|
|
|
70
|
-
// Run update check in background (non-blocking)
|
|
71
|
-
// Skip for
|
|
70
|
+
// Run update check in background (non-blocking).
|
|
71
|
+
// Skip for machine-readable JSON and help commands to avoid corrupting stdout.
|
|
72
72
|
let updateCheckPromise = null;
|
|
73
73
|
const updateCommand = process.argv[2];
|
|
74
74
|
const updateArgs = process.argv.slice(3);
|
|
@@ -78,7 +78,8 @@ const helpRequested = updateCommand === 'help'
|
|
|
78
78
|
|| updateArgs.includes('--help')
|
|
79
79
|
|| updateArgs.includes('-h')
|
|
80
80
|
|| updateArgs[0] === 'help';
|
|
81
|
-
const
|
|
81
|
+
const jsonRequested = updateArgs.includes('--json');
|
|
82
|
+
const skipUpdateCheck = Boolean(process.env.ATRIS_SKIP_UPDATE_CHECK || process.env.NO_UPDATE_NOTIFIER || helpRequested || jsonRequested);
|
|
82
83
|
if (!skipUpdateCheck && (!updateCommand || (updateCommand && !['version', 'update'].includes(updateCommand)))) {
|
|
83
84
|
updateCheckPromise = checkForUpdates()
|
|
84
85
|
.then((updateInfo) => {
|
|
@@ -117,15 +118,6 @@ const isBusinessSyncSafetyCommand = command === 'sync'
|
|
|
117
118
|
|| firstCommandArg === 'resolve'
|
|
118
119
|
);
|
|
119
120
|
|
|
120
|
-
// Keep APP.md app-pack operations independent from the heavier workspace boot
|
|
121
|
-
// path so `atris apps --json` stays machine-readable for agents.
|
|
122
|
-
if (command === 'apps') {
|
|
123
|
-
const subcommand = process.argv[3];
|
|
124
|
-
const args = process.argv.slice(4);
|
|
125
|
-
require('../commands/apps').appsCommand(subcommand, ...args);
|
|
126
|
-
process.exit(0);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
121
|
// Auto-sync skills only for commands that modify workspace state
|
|
130
122
|
if (['init', 'update', 'upgrade'].includes(command) || (command === 'sync' && !isBusinessSyncSafetyCommand)) {
|
|
131
123
|
try {
|
|
@@ -336,6 +328,7 @@ function showHelp() {
|
|
|
336
328
|
console.log(' radar - Show live agents joined with tasks, missions, and worktrees');
|
|
337
329
|
console.log(' ctop - Show a process-first live agent CPU/memory view');
|
|
338
330
|
console.log(' status - See local work and completions (`atris status <business>` for remote)');
|
|
331
|
+
console.log(' recap - What your AI team did, in plain English (--share for paste-ready)');
|
|
339
332
|
console.log(' xp - Show Career XP and contribution graph');
|
|
340
333
|
console.log(' analytics - Show recent productivity from journals');
|
|
341
334
|
console.log(' search - Search journal history (atris search <keyword>)');
|
|
@@ -346,7 +339,7 @@ function showHelp() {
|
|
|
346
339
|
console.log(' release - Tag release, bump version, create GitHub release, draft /launch');
|
|
347
340
|
console.log(' learn - Project learnings (patterns, pitfalls, preferences)');
|
|
348
341
|
console.log(' brain - Compile MAP/TODO/wiki/state into a loadable agent brain');
|
|
349
|
-
console.log(' lesson - Append a one-line lesson to atris/lessons.md');
|
|
342
|
+
console.log(' lesson - Append a one-line lesson to atris/lessons.md (mine: distill receipts/episodes/scorecards into policy lessons)');
|
|
350
343
|
console.log(' ingest - Local-first wiki ingest into atris/wiki/');
|
|
351
344
|
console.log(' query - Local-first wiki query against atris/wiki/');
|
|
352
345
|
console.log(' lint - Local-first wiki lint for atris/wiki/');
|
|
@@ -355,6 +348,7 @@ function showHelp() {
|
|
|
355
348
|
console.log('Optional helpers:');
|
|
356
349
|
console.log(' brainstorm - Explore ideas conversationally before planning');
|
|
357
350
|
console.log(' autopilot - Guided loop that can clarify TODOs and run plan → do → review');
|
|
351
|
+
console.log(' improve - Run one paid RL tick (POST /api/improve, deducts credits)');
|
|
358
352
|
console.log(' worktree - Isolated Git worktrees plus guarded ship/merge for parallel agents');
|
|
359
353
|
console.log(' visualize - Generate a Slack/deck-ready visual from a prompt');
|
|
360
354
|
console.log('');
|
|
@@ -364,6 +358,13 @@ function showHelp() {
|
|
|
364
358
|
console.log(' experiments run <slug> - Execute a pack or record an Endstate receipt');
|
|
365
359
|
console.log(' experiments benchmark [m] - Run validate/runtime experiment benchmarks');
|
|
366
360
|
console.log('');
|
|
361
|
+
console.log('Compile loop (learn like AI, run like code):');
|
|
362
|
+
console.log(' compile record <name> - Append an execution record (--input/--output)');
|
|
363
|
+
console.log(' compile build <name> - Compile records into a deterministic run.js');
|
|
364
|
+
console.log(' compile backtest <name> - Replay all records, score accuracy vs gate');
|
|
365
|
+
console.log(' compile promote <name> - Activate (gate: accuracy >= threshold)');
|
|
366
|
+
console.log(' compile exec <name> - Run the compiled process token-free');
|
|
367
|
+
console.log('');
|
|
367
368
|
console.log('Quick commands:');
|
|
368
369
|
console.log(' atris - Load context and start (natural language)');
|
|
369
370
|
console.log(' next - Auto-advance to next step');
|
|
@@ -491,14 +492,18 @@ function showDoHelp() {
|
|
|
491
492
|
|
|
492
493
|
function showReviewHelp() {
|
|
493
494
|
console.log('');
|
|
494
|
-
console.log('Usage: atris review [--
|
|
495
|
+
console.log('Usage: atris review [--limit N|--all|--json] [--full|--execute]');
|
|
495
496
|
console.log('');
|
|
496
497
|
console.log('Description:');
|
|
497
|
-
console.log('
|
|
498
|
-
console.log('
|
|
499
|
-
console.log('
|
|
498
|
+
console.log(' Show the certified Review queue: proof-ready work waiting for');
|
|
499
|
+
console.log(' human accept or revise. Human accept is the AgentXP gate.');
|
|
500
|
+
console.log(' Use --full/--verbose for the legacy Validator prompt.');
|
|
500
501
|
console.log('');
|
|
501
502
|
console.log('Options:');
|
|
503
|
+
console.log(' --limit N Show at most N certified review rows.');
|
|
504
|
+
console.log(' --all Show all certified review rows.');
|
|
505
|
+
console.log(' --json Emit the task-backed review queue as JSON.');
|
|
506
|
+
console.log(' --group-by Group certified rows by tag, owner, or source.');
|
|
502
507
|
console.log(' --execute Run in agent mode via Atris cloud (requires login + agent).');
|
|
503
508
|
console.log(' --full Print full spec/context dumps (verbose copy/paste).');
|
|
504
509
|
console.log(' --verbose Alias for --full.');
|
|
@@ -769,9 +774,9 @@ if (command === '2' && ['fast', 'pro'].includes(String(firstCommandArg || '').to
|
|
|
769
774
|
const knownCommands = ['init', 'log', 'now', 'radar', 'ctop', 'status', 'analytics', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', 'plan', 'do', 'review', 'release',
|
|
770
775
|
'activate', '_activate', 'agent', 'chat', 'console', 'serve', 'login', 'logout', 'whoami', 'switch', 'use', 'accounts', '_resolve', '_profile-email', '_switch-session', 'shell-init', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
|
|
771
776
|
'clean', 'verify', 'search', 'skill', 'member', 'codex-goal', 'app', 'apps', 'learn', 'lesson', 'plugin', 'experiments', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'live', 'align', 'terminal', 'computer', 'diff', 'business', 'sync',
|
|
772
|
-
'ingest', 'query', 'lint', 'loop', 'task', 'mission', 'worktree', 'aeo', 'xp', 'play', 'gm', 'x',
|
|
777
|
+
'ingest', 'query', 'lint', 'loop', 'task', 'mission', 'probe', 'worktree', 'aeo', 'improve', 'xp', 'play', 'gm', 'x', 'recap',
|
|
773
778
|
'gmail', 'calendar', 'twitter', 'slack', 'imessage', 'integrations', 'setup', 'clean-workspace', 'cw',
|
|
774
|
-
'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet'];
|
|
779
|
+
'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'compile'];
|
|
775
780
|
|
|
776
781
|
// Check if command is an atris.md spec file - triggers welcome visualization
|
|
777
782
|
function isSpecFile(cmd) {
|
|
@@ -1044,8 +1049,7 @@ async function interactiveEntry(userInput) {
|
|
|
1044
1049
|
|
|
1045
1050
|
// ASCII Welcome Visualization
|
|
1046
1051
|
function showWelcomeVisualization() {
|
|
1047
|
-
const {
|
|
1048
|
-
const { getLogPath, ensureLogDirectory, createLogFile } = require('../lib/journal');
|
|
1052
|
+
const { getTaskCounts } = require('../lib/state-detection');
|
|
1049
1053
|
const cwd = process.cwd();
|
|
1050
1054
|
const atrisDir = path.join(cwd, 'atris');
|
|
1051
1055
|
const projectName = path.basename(cwd);
|
|
@@ -1054,21 +1058,13 @@ function showWelcomeVisualization() {
|
|
|
1054
1058
|
let filesIndexed = 0;
|
|
1055
1059
|
let tasksInBacklog = 0;
|
|
1056
1060
|
let tasksInProgress = 0;
|
|
1061
|
+
let tasksInReview = 0;
|
|
1062
|
+
let tasksCertified = 0;
|
|
1057
1063
|
let journalEntries = 0;
|
|
1058
1064
|
let hasMap = false;
|
|
1059
1065
|
let isInitialized = fs.existsSync(atrisDir);
|
|
1060
1066
|
|
|
1061
1067
|
if (isInitialized) {
|
|
1062
|
-
// Auto-create today's journal if missing
|
|
1063
|
-
try {
|
|
1064
|
-
ensureLogDirectory();
|
|
1065
|
-
const { logFile, dateFormatted } = getLogPath();
|
|
1066
|
-
if (!fs.existsSync(logFile)) {
|
|
1067
|
-
createLogFile(logFile, dateFormatted);
|
|
1068
|
-
}
|
|
1069
|
-
} catch {
|
|
1070
|
-
// Silently fail - don't block welcome display
|
|
1071
|
-
}
|
|
1072
1068
|
// Check MAP.md
|
|
1073
1069
|
const mapPath = path.join(atrisDir, 'MAP.md');
|
|
1074
1070
|
if (fs.existsSync(mapPath)) {
|
|
@@ -1079,15 +1075,15 @@ function showWelcomeVisualization() {
|
|
|
1079
1075
|
filesIndexed = fileRefs ? fileRefs.length : 0;
|
|
1080
1076
|
}
|
|
1081
1077
|
|
|
1082
|
-
//
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1078
|
+
// Task lane counts — DB truth first, TODO.md parse as fallback
|
|
1079
|
+
try {
|
|
1080
|
+
const counts = getTaskCounts(atrisDir);
|
|
1081
|
+
tasksInBacklog = counts.backlog;
|
|
1082
|
+
tasksInProgress = counts.active;
|
|
1083
|
+
tasksInReview = counts.review;
|
|
1084
|
+
tasksCertified = counts.reviewCertified;
|
|
1085
|
+
} catch {
|
|
1086
|
+
// Silently fail - show 0 tasks if reading fails
|
|
1091
1087
|
}
|
|
1092
1088
|
|
|
1093
1089
|
// Count journal entries today
|
|
@@ -1136,13 +1132,19 @@ function showWelcomeVisualization() {
|
|
|
1136
1132
|
console.log(` │ 📄 Spec: atris.md v${CLI_VERSION.padEnd(18)}│`);
|
|
1137
1133
|
console.log(` │ 🗺️ Map: ${hasMap ? (filesIndexed + ' files indexed').padEnd(26) : 'not generated yet'.padEnd(26)}│`);
|
|
1138
1134
|
console.log(` │ 📋 Tasks: ${(tasksInBacklog + ' backlog, ' + tasksInProgress + ' active').padEnd(26)}│`);
|
|
1135
|
+
if (tasksInReview > 0) {
|
|
1136
|
+
const reviewText = tasksCertified > 0
|
|
1137
|
+
? `${tasksInReview} waiting (${tasksCertified} certified)`
|
|
1138
|
+
: `${tasksInReview} waiting`;
|
|
1139
|
+
console.log(` │ ⏳ Review: ${reviewText.padEnd(26)}│`);
|
|
1140
|
+
}
|
|
1139
1141
|
console.log(` │ 📝 Journal: ${(journalEntries + ' entries today').padEnd(26)}│`);
|
|
1140
1142
|
console.log(' │ │');
|
|
1141
1143
|
console.log(' │ ┌──────────────────────────────────┐ │');
|
|
1142
1144
|
console.log(' │ │ MAP.md ←──── YOU ARE HERE │ │');
|
|
1143
1145
|
console.log(' │ │ ↓ │ │');
|
|
1144
|
-
const taskText = `${tasksInBacklog}
|
|
1145
|
-
console.log(` │ │ TODO.md ←── ${taskText.padEnd(
|
|
1146
|
+
const taskText = `${tasksInBacklog} task${tasksInBacklog === 1 ? '' : 's'} waiting`;
|
|
1147
|
+
console.log(` │ │ TODO.md ←── ${taskText.padEnd(20)}│ │`);
|
|
1146
1148
|
console.log(' │ │ ↓ │ │');
|
|
1147
1149
|
console.log(' │ │ navigator → executor → validator│ │');
|
|
1148
1150
|
console.log(' │ └──────────────────────────────────┘ │');
|
|
@@ -1150,7 +1152,11 @@ function showWelcomeVisualization() {
|
|
|
1150
1152
|
console.log(' └──────────────────────────────────────────┘');
|
|
1151
1153
|
}
|
|
1152
1154
|
console.log('');
|
|
1153
|
-
|
|
1155
|
+
if (tasksCertified > 0) {
|
|
1156
|
+
console.log(` Ready. ${tasksCertified} certified await accept — run 'atris task reviews'.`);
|
|
1157
|
+
} else {
|
|
1158
|
+
console.log(` Ready. Run 'atris plan' to start.`);
|
|
1159
|
+
}
|
|
1154
1160
|
console.log('');
|
|
1155
1161
|
}
|
|
1156
1162
|
|
|
@@ -1179,6 +1185,11 @@ if (command === 'init') {
|
|
|
1179
1185
|
Promise.resolve(require('../commands/mission').missionCommand(process.argv.slice(3)))
|
|
1180
1186
|
.then(() => process.exit(0))
|
|
1181
1187
|
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
1188
|
+
} else if (command === 'probe') {
|
|
1189
|
+
// Chat-lane probe (TRR-22): one real /atris2/turn over the full tool relay.
|
|
1190
|
+
Promise.resolve(require('../commands/probe').probeCommand(process.argv.slice(3)))
|
|
1191
|
+
.then((code) => process.exit(code || 0))
|
|
1192
|
+
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
1182
1193
|
} else if (command === 'worktree') {
|
|
1183
1194
|
Promise.resolve(require('../commands/worktree').worktreeCommand(process.argv.slice(3)))
|
|
1184
1195
|
.then((code) => process.exit(code || 0))
|
|
@@ -1197,6 +1208,11 @@ if (command === 'init') {
|
|
|
1197
1208
|
Promise.resolve(require('../commands/aeo').run(process.argv.slice(3)))
|
|
1198
1209
|
.then(() => process.exit(0))
|
|
1199
1210
|
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
1211
|
+
} else if (command === 'improve') {
|
|
1212
|
+
// Improve: one paid RL tick via POST /api/improve (deducts credits), local autopilot fallback.
|
|
1213
|
+
Promise.resolve(require('../commands/improve').run(process.argv.slice(3)))
|
|
1214
|
+
.then((code) => process.exit(typeof code === 'number' ? code : 0))
|
|
1215
|
+
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
1200
1216
|
} else if (command === 'brain') {
|
|
1201
1217
|
Promise.resolve()
|
|
1202
1218
|
.then(() => require('../commands/brain').brainCommand(process.argv.slice(3)))
|
|
@@ -1243,6 +1259,8 @@ if (command === 'init') {
|
|
|
1243
1259
|
process.exit(0);
|
|
1244
1260
|
}
|
|
1245
1261
|
require('../commands/now').nowAtris(args);
|
|
1262
|
+
} else if (command === 'recap') {
|
|
1263
|
+
require('../commands/recap').recapAtris(process.argv.slice(3));
|
|
1246
1264
|
} else if (command === 'activate') {
|
|
1247
1265
|
const args = process.argv.slice(3);
|
|
1248
1266
|
if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
@@ -1622,6 +1640,14 @@ if (command === 'init') {
|
|
|
1622
1640
|
integrationsStatus()
|
|
1623
1641
|
.then(() => process.exit(0))
|
|
1624
1642
|
.catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
|
|
1643
|
+
} else if (command === 'apps') {
|
|
1644
|
+
// Keep APP.md app-pack operations independent from the heavier workspace boot
|
|
1645
|
+
// path so `atris apps --json` stays machine-readable for agents.
|
|
1646
|
+
const subcommand = process.argv[3];
|
|
1647
|
+
const args = process.argv.slice(4);
|
|
1648
|
+
Promise.resolve(require('../commands/apps').appsCommand(subcommand, ...args))
|
|
1649
|
+
.then(() => process.exit(0))
|
|
1650
|
+
.catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
|
|
1625
1651
|
} else if (command === 'learn') {
|
|
1626
1652
|
const subcommand = process.argv[3];
|
|
1627
1653
|
const args = process.argv.slice(4);
|
|
@@ -1637,7 +1663,9 @@ if (command === 'init') {
|
|
|
1637
1663
|
} else if (command === 'member') {
|
|
1638
1664
|
const subcommand = process.argv[3];
|
|
1639
1665
|
const args = process.argv.slice(4);
|
|
1640
|
-
require('../commands/member').memberCommand(subcommand, ...args)
|
|
1666
|
+
Promise.resolve(require('../commands/member').memberCommand(subcommand, ...args))
|
|
1667
|
+
.then(() => process.exit(0))
|
|
1668
|
+
.catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
|
|
1641
1669
|
} else if (command === 'app') {
|
|
1642
1670
|
const subcommand = process.argv[3];
|
|
1643
1671
|
const args = process.argv.slice(4);
|
|
@@ -1745,6 +1773,12 @@ if (command === 'init') {
|
|
|
1745
1773
|
const subcommand = process.argv[3];
|
|
1746
1774
|
const args = process.argv.slice(4);
|
|
1747
1775
|
require('../commands/experiments').experimentsCommand(subcommand, ...args);
|
|
1776
|
+
} else if (command === 'compile') {
|
|
1777
|
+
const subcommand = process.argv[3];
|
|
1778
|
+
const args = process.argv.slice(4);
|
|
1779
|
+
Promise.resolve(require('../commands/compile').compileCommand(subcommand, ...args))
|
|
1780
|
+
.then(() => process.exit(process.exitCode || 0))
|
|
1781
|
+
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
1748
1782
|
} else if (command === 'receipt' || command === 'proof' || command === 'openclaw') {
|
|
1749
1783
|
const subcommand = process.argv[3];
|
|
1750
1784
|
const args = process.argv.slice(4);
|
package/commands/align.js
CHANGED
|
@@ -173,20 +173,6 @@ async function walkCloud(token, businessId, workspaceId) {
|
|
|
173
173
|
return { files: out, errors };
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
/**
|
|
177
|
-
* Get cloud file content (for hash computation when /files doesn't return hashes).
|
|
178
|
-
*/
|
|
179
|
-
async function fetchCloudFileHash(token, businessId, workspaceId, filePath) {
|
|
180
|
-
const result = await apiRequestJson(
|
|
181
|
-
`/business/${businessId}/workspaces/${workspaceId}/file?path=${encodeURIComponent(filePath)}`,
|
|
182
|
-
{ method: 'GET', token }
|
|
183
|
-
);
|
|
184
|
-
if (!result.ok) return null;
|
|
185
|
-
const content = result.data && result.data.content;
|
|
186
|
-
if (typeof content !== 'string') return null;
|
|
187
|
-
return hashContent(Buffer.from(content, 'utf-8'));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
176
|
/**
|
|
191
177
|
* Wake the EC2 computer and wait until it's running.
|
|
192
178
|
* Returns the endpoint URL or null on timeout.
|
package/commands/apps.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { spawnSync } = require('child_process');
|
|
4
|
+
const { ensureValidCredentials } = require('../utils/auth');
|
|
5
|
+
const { apiRequestJson } = require('../utils/api');
|
|
6
|
+
|
|
7
|
+
const CLOUD_LOAD_COMMANDS = new Set(['load', 'cloud', 'mine']);
|
|
4
8
|
|
|
5
9
|
function findAppsPackRoot(startDir = process.cwd()) {
|
|
6
10
|
const explicit = process.env.ATRIS_APPS_PACK;
|
|
@@ -25,6 +29,7 @@ function appsUsageLines() {
|
|
|
25
29
|
'',
|
|
26
30
|
'Commands:',
|
|
27
31
|
' list [--json] List available local apps',
|
|
32
|
+
' load [--filter kind] [--json] Load owned cloud apps from Atris',
|
|
28
33
|
' run <slug> [--lines N] Run an app and print data/latest.md or --json status',
|
|
29
34
|
' owner <slug> [--json] Show owner view: launch, usage, learning, next actions',
|
|
30
35
|
' status [--json] Show local app health',
|
|
@@ -156,7 +161,99 @@ function runPackScript(packRoot, script, args) {
|
|
|
156
161
|
process.exit(result.status ?? 0);
|
|
157
162
|
}
|
|
158
163
|
|
|
159
|
-
function
|
|
164
|
+
function normalizeCloudAppsPayload(data) {
|
|
165
|
+
if (Array.isArray(data)) return data;
|
|
166
|
+
if (Array.isArray(data?.apps)) return data.apps;
|
|
167
|
+
if (Array.isArray(data?.items)) return data.items;
|
|
168
|
+
if (Array.isArray(data?.data)) return data.data;
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function compactCloudApp(app) {
|
|
173
|
+
const slug = app?.slug || app?.id || app?.name || 'unknown';
|
|
174
|
+
return {
|
|
175
|
+
id: app?.id || null,
|
|
176
|
+
name: app?.name || slug,
|
|
177
|
+
slug,
|
|
178
|
+
description: app?.description || null,
|
|
179
|
+
template: app?.template || app?.template_slug || null,
|
|
180
|
+
status: app?.status || app?.health || null,
|
|
181
|
+
last_run: app?.last_run || app?.lastRun || app?.last_run_at || null,
|
|
182
|
+
next_run: app?.next_run || app?.nextRun || app?.next_run_at || null,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function printCloudApps(apps, filter) {
|
|
187
|
+
const suffix = filter ? ` (${filter})` : '';
|
|
188
|
+
if (apps.length === 0) {
|
|
189
|
+
console.log(`No cloud apps found${suffix}.`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
console.log(`Cloud apps${suffix}:`);
|
|
193
|
+
for (const app of apps) {
|
|
194
|
+
const name = String(app.name || app.slug || 'Untitled app');
|
|
195
|
+
const slug = String(app.slug || app.id || 'unknown');
|
|
196
|
+
const status = app.status ? ` status=${app.status}` : '';
|
|
197
|
+
const template = app.template ? ` template=${app.template}` : '';
|
|
198
|
+
console.log(` ${slug.padEnd(24)} ${name}${status}${template}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function printAppsLoadHelp() {
|
|
203
|
+
console.log('');
|
|
204
|
+
console.log('Usage: atris apps load [--filter <template|paid|free>] [--json]');
|
|
205
|
+
console.log('');
|
|
206
|
+
console.log('Load owned cloud apps from Atris. Requires `atris login`.');
|
|
207
|
+
console.log('');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function loadCloudApps(rawArgs) {
|
|
211
|
+
const args = [...rawArgs];
|
|
212
|
+
if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
213
|
+
printAppsLoadHelp();
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const json = args.includes('--json');
|
|
217
|
+
if (json) args.splice(args.indexOf('--json'), 1);
|
|
218
|
+
let filter = popOption(args, '--filter', null);
|
|
219
|
+
if (!filter && args[0] && !args[0].startsWith('-')) filter = args.shift();
|
|
220
|
+
if (args.length > 0) {
|
|
221
|
+
exitAppsError(`Unknown apps load option: ${args[0]}`, json, { usage: true, code: 2 });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const ensured = await ensureValidCredentials(apiRequestJson);
|
|
225
|
+
if (ensured.error || !ensured.credentials?.token) {
|
|
226
|
+
exitAppsError('Not logged in. Run: atris login', json, {
|
|
227
|
+
extra: { login: 'atris login' },
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const query = filter ? `?filter=${encodeURIComponent(filter)}` : '';
|
|
232
|
+
const result = await apiRequestJson(`/apps${query}`, {
|
|
233
|
+
method: 'GET',
|
|
234
|
+
token: ensured.credentials.token,
|
|
235
|
+
});
|
|
236
|
+
if (!result.ok) {
|
|
237
|
+
exitAppsError(`Failed to load cloud apps: ${result.error || result.status || 'request failed'}`, json, {
|
|
238
|
+
extra: { status: result.status || null },
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const apps = normalizeCloudAppsPayload(result.data).map(compactCloudApp);
|
|
243
|
+
if (json) {
|
|
244
|
+
console.log(JSON.stringify({
|
|
245
|
+
ok: true,
|
|
246
|
+
source: 'cloud',
|
|
247
|
+
filter: filter || null,
|
|
248
|
+
count: apps.length,
|
|
249
|
+
apps,
|
|
250
|
+
}, null, 2));
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
printCloudApps(apps, filter || null);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function appsCommand(subcommand, ...rawArgs) {
|
|
160
257
|
const jsonRequested = wantsJson(subcommand, rawArgs);
|
|
161
258
|
const normalized = normalizeInvocation(subcommand, rawArgs);
|
|
162
259
|
subcommand = normalized.subcommand;
|
|
@@ -166,6 +263,10 @@ function appsCommand(subcommand, ...rawArgs) {
|
|
|
166
263
|
printAppsHelp();
|
|
167
264
|
return;
|
|
168
265
|
}
|
|
266
|
+
if (CLOUD_LOAD_COMMANDS.has(subcommand)) {
|
|
267
|
+
await loadCloudApps(rawArgs);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
169
270
|
const packRoot = findAppsPackRoot();
|
|
170
271
|
if (!packRoot) {
|
|
171
272
|
if (jsonRequested) {
|