mindvest-atlas 0.62.0 → 0.64.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/package.json +1 -1
- package/src/commands/computer-use.js +9 -1
- package/src/commands/run.js +11 -4
- package/src/commands/screenshot.js +9 -5
- package/src/config.js +1 -1
- package/src/helpers/atlas_env.py +20 -2
- package/src/remote-control.js +28 -0
- package/src/sharing.js +9 -0
package/package.json
CHANGED
|
@@ -27,7 +27,7 @@ import fs from 'node:fs';
|
|
|
27
27
|
import path from 'node:path';
|
|
28
28
|
import {
|
|
29
29
|
workspace, workspaceRoot, venvPython, ambientPython, jobEnv, ensureProjectFiles,
|
|
30
|
-
computerUseAllowed, computerClaim, releaseComputerClaim,
|
|
30
|
+
computerUseAllowed, computerClaim, releaseComputerClaim, askAddress,
|
|
31
31
|
} from '../remote-control.js';
|
|
32
32
|
import { color, println, eprintln } from '../util/ui.js';
|
|
33
33
|
|
|
@@ -254,6 +254,14 @@ export function runInProject(job, code, root) {
|
|
|
254
254
|
const venv = venvPython(dir);
|
|
255
255
|
const python = venv ? [String(venv)] : [...ambientPython()];
|
|
256
256
|
const env = { ...process.env, ...jobEnv(dir), ATLAS_JOB: path.basename(dir) };
|
|
257
|
+
// AND HOW TO REACH THE APP, when this shell was not started by it. Without
|
|
258
|
+
// these, ask(), screenshot() and every dashboard call inside the script refuse
|
|
259
|
+
// with "this has to run from inside Atlas" while Atlas is running (owner,
|
|
260
|
+
// 2026-09-10). Only filled when absent: a run Atlas started keeps its own.
|
|
261
|
+
if (!env.ATLAS_ASK_URL || !env.ATLAS_ASK_TOKEN) {
|
|
262
|
+
const at = askAddress(root);
|
|
263
|
+
if (at) { env.ATLAS_ASK_URL = at.url; env.ATLAS_ASK_TOKEN = at.token; }
|
|
264
|
+
}
|
|
257
265
|
// BEFORE ANYTHING TRIES TO IMPORT THEM. Checked every time and installed only
|
|
258
266
|
// when something is missing, so the cost is one python start on a machine
|
|
259
267
|
// that is already set up.
|
package/src/commands/run.js
CHANGED
|
@@ -24,7 +24,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
24
24
|
import fs from 'node:fs';
|
|
25
25
|
import path from 'node:path';
|
|
26
26
|
import {
|
|
27
|
-
workspace, venvPython, ambientPython, jobEnv, ensureProjectFiles,
|
|
27
|
+
workspace, venvPython, ambientPython, jobEnv, ensureProjectFiles, askAddress,
|
|
28
28
|
} from '../remote-control.js';
|
|
29
29
|
import { color, println, eprintln } from '../util/ui.js';
|
|
30
30
|
import { shellSession } from './computer-use.js';
|
|
@@ -86,11 +86,18 @@ export function runCommand(args) {
|
|
|
86
86
|
// THE BRIDGE BACK TO ATLAS is inherited, never invented. These are handed to
|
|
87
87
|
// a process Atlas starts, so an agent's own shell has them and a person's
|
|
88
88
|
// ordinary terminal does not. Saying so beats a snippet that half works.
|
|
89
|
+
// FILLED IN WHEN THE APP IS RUNNING. It writes its loopback address down while
|
|
90
|
+
// it listens, so a shell Atlas did not start can still reach it. The note below
|
|
91
|
+
// is now only for the case that is actually true: Atlas is not running.
|
|
92
|
+
if (!env.ATLAS_ASK_URL || !env.ATLAS_ASK_TOKEN) {
|
|
93
|
+
const at = askAddress(flags.root);
|
|
94
|
+
if (at) { env.ATLAS_ASK_URL = at.url; env.ATLAS_ASK_TOKEN = at.token; }
|
|
95
|
+
}
|
|
89
96
|
if (!env.ATLAS_ASK_URL || !env.ATLAS_ASK_TOKEN) {
|
|
90
97
|
println(color.dim(
|
|
91
|
-
' Note:
|
|
92
|
-
+ '
|
|
93
|
-
+ '
|
|
98
|
+
' Note: Atlas Studio is not running, so ask(), dashboard and screenshot() '
|
|
99
|
+
+ 'will not work in this shell. Everything else will. Start Atlas Studio if '
|
|
100
|
+
+ 'you need those.'));
|
|
94
101
|
}
|
|
95
102
|
if (!env.ATLAS_JOB) env.ATLAS_JOB = path.basename(dir);
|
|
96
103
|
// ONE SESSION FOR THE WHOLE SHELL, the same one `atlas computer-use` uses.
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// the project's own `screenshots/`, and prints the faults in words — because a
|
|
17
17
|
// picture the agent cannot see is not a check.
|
|
18
18
|
import { color, println, eprintln } from '../util/ui.js';
|
|
19
|
+
import { askAddress } from '../remote-control.js';
|
|
19
20
|
|
|
20
21
|
export async function screenshotCommand(args) {
|
|
21
22
|
const flags = args.flags || {};
|
|
@@ -25,17 +26,20 @@ export async function screenshotCommand(args) {
|
|
|
25
26
|
return 1;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
// Atlas hands these to what it starts, and writes them down while it is
|
|
30
|
+
// running so a terminal it did not start can find them too.
|
|
31
|
+
const at = askAddress(flags.root);
|
|
32
|
+
const url = at?.url;
|
|
33
|
+
const token = at?.token;
|
|
30
34
|
if (!url || !token) {
|
|
31
35
|
// NOT a Local Mode check, whatever it used to say. These variables are
|
|
32
36
|
// handed to a process Atlas starts — a script it runs, or an agent it
|
|
33
37
|
// spawned. Their absence means this command is running somewhere Atlas did
|
|
34
38
|
// not start it, and saying "turn Local Mode on" sent people to a switch
|
|
35
39
|
// that was already on (owner, 2026-09-05).
|
|
36
|
-
eprintln(color.red('
|
|
37
|
-
eprintln(color.dim('
|
|
38
|
-
+ '
|
|
40
|
+
eprintln(color.red('Atlas Studio is not running on this computer. Atlas is what draws the page.'));
|
|
41
|
+
eprintln(color.dim('Start Atlas Studio and try again, or ask for it in a chat. '
|
|
42
|
+
+ 'With nothing running there is nothing to photograph.'));
|
|
39
43
|
return 1;
|
|
40
44
|
}
|
|
41
45
|
|
package/src/config.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
|
|
5
|
-
export const VERSION = '0.
|
|
5
|
+
export const VERSION = '0.64.0';
|
|
6
6
|
export const DEFAULT_BASE_URL = 'https://atlasmcp.finmanagerai.com';
|
|
7
7
|
export const DEFAULT_SCOPE = 'atlas broker';
|
|
8
8
|
export const CLIENT_NAME = 'Atlas CLI';
|
package/src/helpers/atlas_env.py
CHANGED
|
@@ -2432,7 +2432,7 @@ def _sleep_or_stop(seconds, slice_s=1.0):
|
|
|
2432
2432
|
return _still_mine()
|
|
2433
2433
|
|
|
2434
2434
|
|
|
2435
|
-
def watch(instruction, every=5, most=200, hours=8, look=True):
|
|
2435
|
+
def watch(instruction, every=5, most=200, hours=8, look=True, model=""):
|
|
2436
2436
|
"""Watch the screen and act when it is time. Runs until it is done.
|
|
2437
2437
|
|
|
2438
2438
|
THE SHAPE OF A JOB THAT IS MOSTLY WAITING (owner, 2026-09-06: "watch this
|
|
@@ -2465,6 +2465,24 @@ def watch(instruction, every=5, most=200, hours=8, look=True):
|
|
|
2465
2465
|
choosing, and why a model that says "nothing is happening, look again in an
|
|
2466
2466
|
hour" is doing its job rather than being lazy.
|
|
2467
2467
|
|
|
2468
|
+
A SMALL MODEL CAN DO THE WATCHING (owner, 2026-09-10: "a smaller model to
|
|
2469
|
+
watch continuously, then the router is able to route to larger to call tools
|
|
2470
|
+
and do other stuff"). `model=` names who is asked each round, and the round
|
|
2471
|
+
is the cheap half of this: nothing has changed, look again later. It is the
|
|
2472
|
+
ACTING that wants a big model, and the watcher hands that over the same way
|
|
2473
|
+
anything else does, by calling ask() with one:
|
|
2474
|
+
|
|
2475
|
+
watch("Watch the SPY 5-minute chart. Say QUIET and nothing else while "
|
|
2476
|
+
"it is quiet. When the opening range high breaks on rising "
|
|
2477
|
+
"volume, call: "
|
|
2478
|
+
" ask('Break just happened, here is the screen: <path>. Decide "
|
|
2479
|
+
" the trade and place it.', model='claude-opus-5')",
|
|
2480
|
+
every=2, model="claude-haiku-4-5")
|
|
2481
|
+
|
|
2482
|
+
Whoever is watching still has this whole computer, so a watcher that can
|
|
2483
|
+
decide for itself should just act. Route when the decision is worth the
|
|
2484
|
+
bigger model, not on principle. models() lists what this machine can reach.
|
|
2485
|
+
|
|
2468
2486
|
Ends when the model writes DONE on a line by itself, when `most` rounds have
|
|
2469
2487
|
passed, or when `hours` have. Pressing Stop ends it like anything else.
|
|
2470
2488
|
"""
|
|
@@ -2534,7 +2552,7 @@ def watch(instruction, every=5, most=200, hours=8, look=True):
|
|
|
2534
2552
|
else "The screen could not be photographed this round.")
|
|
2535
2553
|
)
|
|
2536
2554
|
try:
|
|
2537
|
-
answer = ask(asked)
|
|
2555
|
+
answer = ask(asked, model=model)
|
|
2538
2556
|
except Exception as err:
|
|
2539
2557
|
answer = ""
|
|
2540
2558
|
seen.append({"round": rounds, "error": str(err)})
|
package/src/remote-control.js
CHANGED
|
@@ -39,6 +39,34 @@ export const ENTRIES = ['main.py', 'main.js', 'main.mjs'];
|
|
|
39
39
|
*/
|
|
40
40
|
export const DEFAULT_TIMEOUT_MS = 1_800_000;
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* HOW TO REACH A RUNNING ATLAS, from a shell it did not start.
|
|
44
|
+
*
|
|
45
|
+
* ATLAS_ASK_URL / ATLAS_ASK_TOKEN are handed to processes Atlas spawns. Anything
|
|
46
|
+
* else on the same machine had no way in at all: `atlas screenshot`, `atlas
|
|
47
|
+
* dashboard` and `atlas run --code` all refused with "this has to run from
|
|
48
|
+
* inside Atlas", with the app running the whole time and nothing the person
|
|
49
|
+
* could do about it (owner, 2026-09-10).
|
|
50
|
+
*
|
|
51
|
+
* The app writes its loopback address down while it is listening, so this is
|
|
52
|
+
* the same server those variables point at, on the same machine, for the same
|
|
53
|
+
* user. The environment still wins when it is set: a run Atlas started should
|
|
54
|
+
* talk to the Atlas that started it.
|
|
55
|
+
*/
|
|
56
|
+
export function askAddress(root) {
|
|
57
|
+
const url = process.env.ATLAS_ASK_URL;
|
|
58
|
+
const token = process.env.ATLAS_ASK_TOKEN;
|
|
59
|
+
if (url && token) return { url, token };
|
|
60
|
+
try {
|
|
61
|
+
const file = path.join(workspaceRoot(root), '.studio', 'ask-server.json');
|
|
62
|
+
const saved = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
63
|
+
if (saved?.url && saved?.token) return { url: String(saved.url), token: String(saved.token) };
|
|
64
|
+
} catch {
|
|
65
|
+
// Not running, or a different machine's home. The caller says so in words.
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
42
70
|
export function workspaceRoot(override) {
|
|
43
71
|
const raw = override || process.env.ATLAS_LOCAL_DIR
|
|
44
72
|
|| process.env.ATLAS_LIBRARY_DIR // what Studio sets
|
package/src/sharing.js
CHANGED
|
@@ -435,6 +435,12 @@ export async function publish(job, root, {
|
|
|
435
435
|
// button has a LINK to offer, not a line of hex.
|
|
436
436
|
share_url: data.share_url,
|
|
437
437
|
name: fields.name,
|
|
438
|
+
// WHAT THE PROJECT IS, KEPT (owner, 2026-09-09: "when publishing changes,
|
|
439
|
+
// it always ask what the project is for — if the user has added this just
|
|
440
|
+
// recall it"). It was sent to the server and forgotten locally, so the
|
|
441
|
+
// publish dialog opened with an empty box on every single release and the
|
|
442
|
+
// author retyped the same sentence or, more often, shipped without one.
|
|
443
|
+
description: fields.description,
|
|
438
444
|
author: true,
|
|
439
445
|
// Where it started, kept for as long as the copy exists. Provenance, not a
|
|
440
446
|
// subscription: the update check follows `package_id`, which is now this
|
|
@@ -525,6 +531,9 @@ export function shareState(job, root) {
|
|
|
525
531
|
mine: Boolean(pin.author),
|
|
526
532
|
author_name: pin.author_name,
|
|
527
533
|
forked_from: pin.forked_from,
|
|
534
|
+
// What this project IS, as last published. The publish dialog seeds its
|
|
535
|
+
// box from here, so a re-release starts with the sentence already in it.
|
|
536
|
+
description: pin.description,
|
|
528
537
|
};
|
|
529
538
|
}
|
|
530
539
|
|