mindvest-atlas 0.50.3 → 0.52.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mindvest-atlas",
3
- "version": "0.50.3",
3
+ "version": "0.52.0",
4
4
  "description": "Atlas CLI \u2014 OAuth login, tool calls, and live alert/flow streaming for the Atlas trading API",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,7 +41,7 @@ function versionIn(text) {
41
41
  }
42
42
 
43
43
  /**
44
- * Where Atlas Remote Control keeps the copy it manages for itself.
44
+ * Where Atlas Studio keeps the copy it manages for itself.
45
45
  *
46
46
  * It fetches its own CLI so Local Mode works on a machine with nothing
47
47
  * installed. That copy is a THIRD version to be out of step with the other two,
@@ -102,12 +102,12 @@ export function installedClis() {
102
102
  }
103
103
  }
104
104
 
105
- // The copy Atlas Remote Control manages for itself.
105
+ // The copy Atlas Studio manages for itself.
106
106
  const studio = studioCliDir();
107
107
  try {
108
108
  const pkg = JSON.parse(fs.readFileSync(path.join(studio, 'package.json'), 'utf8'));
109
109
  rows.push({
110
- kind: 'Atlas Remote Control', version: pkg.version || '', where: studio,
110
+ kind: 'Atlas Studio', version: pkg.version || '', where: studio,
111
111
  note: 'the app updates this one itself',
112
112
  });
113
113
  } catch { /* the app has not fetched one */ }
package/src/cli.js CHANGED
@@ -101,7 +101,7 @@ export async function main(rawArgs) {
101
101
  case 'run':
102
102
  return runCommand(withoutCommand(args));
103
103
  // SEEING AND USING THIS COMPUTER, from a shell. It existed only in the
104
- // Python package, and Atlas Remote Control bundles THIS one, so the app
104
+ // Python package, and Atlas Studio bundles THIS one, so the app
105
105
  // answered "Unknown command or tool: computer-use" on the very machine
106
106
  // the command was written for (owner, 2026-09-05).
107
107
  case 'computer-use':
@@ -7,7 +7,7 @@
7
7
  // The agent in a chat is a SHELL agent. Computer use lived only inside project
8
8
  // scripts, so it reasoned its way to the truth and then to a wall.
9
9
  //
10
- // AND THIS IS THE COPY THAT ACTUALLY RUNS. Atlas Remote Control bundles the
10
+ // AND THIS IS THE COPY THAT ACTUALLY RUNS. Atlas Studio bundles the
11
11
  // NODE CLI, so a version of this that existed only in the Python package was a
12
12
  // command the app rejected: "Unknown command or tool: computer-use", on the one
13
13
  // machine it was written for.
@@ -103,6 +103,10 @@ const OPS = {
103
103
  'hide-consoles': ['hide_consoles', 0],
104
104
  // ── A JOB THAT IS MOSTLY WAITING ──────────────────────────────────────
105
105
  watch: ['watch', 1],
106
+ // ── A BROWSER IS AN APPLICATION, NOT A WALL OF PIXELS ─────────────────
107
+ browser: ['browser', 0],
108
+ 'go-to': ['go_to', 1],
109
+ 'find-on-page': ['find_on_page', 1],
106
110
  // ── ONE CALL FOR WHAT USED TO BE FOUR ─────────────────────────────────
107
111
  'scroll-x': ['scroll_x', 1],
108
112
  'shift-scroll': ['shift_scroll', 1],
@@ -311,7 +315,7 @@ export function computerUseCommand(args) {
311
315
  if (sub === 'start') {
312
316
  if (!computerUseAllowed()) {
313
317
  eprintln(color.red('Using the computer is switched off.'));
314
- eprintln(color.dim(' Open Atlas Remote Control > This Computer and turn on '
318
+ eprintln(color.dim(' Open Atlas Studio > This Computer and turn on '
315
319
  + '"Let an agent use this computer", then run this again.'));
316
320
  return 2;
317
321
  }
@@ -321,7 +325,7 @@ export function computerUseCommand(args) {
321
325
  eprintln(color.dim(' There is one mouse, so it is one at a time.'));
322
326
  return 3;
323
327
  }
324
- // TAKING IT IS WHAT OPENS THE WATCH VIEW. Atlas Remote Control watches the
328
+ // TAKING IT IS WHAT OPENS THE WATCH VIEW. Atlas Studio watches the
325
329
  // claim, so there is nothing to ask it: the moment this lands, the strip
326
330
  // goes up and the screen appears. One signal, not two that can disagree.
327
331
  const code = 'import atlas_env as e, json\n'
@@ -331,7 +335,7 @@ export function computerUseCommand(args) {
331
335
  const out = runInProject(job, code);
332
336
  if (out === 0) {
333
337
  println('');
334
- println(color.dim('Watching opened in Atlas Remote Control. Now use it:'));
338
+ println(color.dim('Watching opened in Atlas Studio. Now use it:'));
335
339
  println(color.dim(' atlas computer-use screen'));
336
340
  println(color.dim(' atlas computer-use click <x> <y>'));
337
341
  println(color.dim(' atlas computer-use stop when you are done'));
@@ -3,7 +3,7 @@
3
3
  // The same gap `atlas computer-use` filled, one road over: driving the
4
4
  // dashboard existed in atlas_env and nowhere else, so an agent in a chat could
5
5
  // only reach it by hand-writing the import (owner, 2026-09-06). And it has to
6
- // be in THIS CLI, because Atlas Remote Control bundles this one.
6
+ // be in THIS CLI, because Atlas Studio bundles this one.
7
7
  //
8
8
  // IT IS NOT THE COMPUTER. This drives the project's own window, so the real
9
9
  // pointer never moves, nothing is claimed, no switch is needed, and a click at
@@ -245,7 +245,7 @@ async function handleAsk(ask, root) {
245
245
  const argv = Array.isArray(py) ? [...py] : [String(py)];
246
246
  // THE PROJECT'S OWN atlas_env, which both runners write, so nothing extra
247
247
  // has to be installed for this to work. It used to import the Python CLI
248
- // package, which Atlas Remote Control never installs: live watching would
248
+ // package, which Atlas Studio never installs: live watching would
249
249
  // have failed on every machine running the app.
250
250
  const { dir } = workspace(ask.job || '', root);
251
251
  try { ensureProjectFiles(dir); } catch { /* it may already be right */ }
@@ -334,7 +334,7 @@ async function handleAsk(ask, root) {
334
334
  if (ask.op === 'command') {
335
335
  // A button, a switch or a form in the job's own dashboard, pressed from
336
336
  // anywhere that draws it. It lands as a file for the script to read — the
337
- // same file Atlas Remote Control writes when the press happens in the app,
337
+ // same file Atlas Studio writes when the press happens in the app,
338
338
  // so one button means one thing wherever it is pressed.
339
339
  return answer(ask.request_id, writeCommand(ask.job || '', ask.name || '', ask.payload, root));
340
340
  }
@@ -65,7 +65,7 @@ export async function screenshotCommand(args) {
65
65
  return 1;
66
66
  }
67
67
  } catch (err) {
68
- eprintln(color.red(`Could not reach Atlas Remote Control: ${err.message}`));
68
+ eprintln(color.red(`Could not reach Atlas Studio: ${err.message}`));
69
69
  return 1;
70
70
  }
71
71
 
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.50.3';
5
+ export const VERSION = '0.52.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';
@@ -64,7 +64,7 @@ export async function ask(prompt, { tools = true, fresh = false, timeoutMs = 600
64
64
  if (!url || !token) {
65
65
  throw new Error(
66
66
  "No model is connected on this computer, so this script cannot ask one. " +
67
- "Connect a model in Atlas Remote Control, or do this step in code."
67
+ "Connect a model in Atlas Studio, or do this step in code."
68
68
  );
69
69
  }
70
70
  const res = await fetch(url, {
@@ -127,7 +127,7 @@ async function driveDashboard(op, body = {}) {
127
127
  const token = process.env.ATLAS_ASK_TOKEN || "";
128
128
  if (!url || !token) {
129
129
  throw new Error(
130
- "The dashboard can only be driven from Atlas Remote Control, which is "
130
+ "The dashboard can only be driven from Atlas Studio, which is "
131
131
  + "what draws it. Run this project from the app.",
132
132
  );
133
133
  }
@@ -183,8 +183,7 @@ export const dashboard = {
183
183
  * await computer.type("hello");
184
184
  * await computer.note("signing in");
185
185
  *
186
- * Off until the person turns it on, under This Computer in Atlas Remote
187
- * Control. Read skills/computer-use before using any of it.
186
+ * Off until the person turns it on, under This Computer in Atlas Studio. Read skills/computer-use before using any of it.
188
187
  */
189
188
  async function useComputer(op, args = []) {
190
189
  const { spawn } = await import("node:child_process");
@@ -256,12 +255,12 @@ export const computer = {
256
255
  * Stop cleanly when a secret has not been filled in yet.
257
256
  *
258
257
  * Names what is missing and nothing else: a blank key is the owner's to fill in
259
- * Atlas Remote Control, not something a script should work around.
258
+ * Atlas Studio, not something a script should work around.
260
259
  */
261
260
  export function require_(...names) {
262
261
  const missing = names.filter((n) => !process.env[n]);
263
262
  if (missing.length) {
264
- console.log(`Missing: ${missing.join(", ")}. Add the value in Atlas Remote Control, then run again.`);
263
+ console.log(`Missing: ${missing.join(", ")}. Add the value in Atlas Studio, then run again.`);
265
264
  process.exit(2);
266
265
  }
267
266
  }
@@ -101,7 +101,7 @@ def ask(prompt, tools=True, fresh=False, timeout=600, model=""):
101
101
  if not url or not token:
102
102
  raise RuntimeError(
103
103
  "No model is connected on this computer, so this script cannot ask one. "
104
- "Connect a model in Atlas Remote Control, or do this step in code."
104
+ "Connect a model in Atlas Studio, or do this step in code."
105
105
  )
106
106
  body = {"job": os.environ.get("ATLAS_JOB", ""), "prompt": str(prompt),
107
107
  "tools": bool(tools), "fresh": bool(fresh)}
@@ -284,7 +284,7 @@ class _Dashboard(object):
284
284
  token = os.environ.get("ATLAS_ASK_TOKEN", "")
285
285
  if not url or not token:
286
286
  raise RuntimeError(
287
- "The dashboard can only be driven from Atlas Remote Control, "
287
+ "The dashboard can only be driven from Atlas Studio, "
288
288
  "which is what draws it. Run this project from the app."
289
289
  )
290
290
  job = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
@@ -441,11 +441,11 @@ def require(*names):
441
441
  """Stop cleanly when a secret has not been filled in yet.
442
442
 
443
443
  Names what is missing and nothing else: a blank key is the owner's to fill
444
- in Atlas Remote Control, not something a script should work around.
444
+ in Atlas Studio, not something a script should work around.
445
445
  """
446
446
  missing = [n for n in names if not os.environ.get(n)]
447
447
  if missing:
448
- print("Missing: %s. Add the value in Atlas Remote Control, then run again."
448
+ print("Missing: %s. Add the value in Atlas Studio, then run again."
449
449
  % ", ".join(missing))
450
450
  sys.exit(2)
451
451
 
@@ -497,7 +497,7 @@ def record_trade(symbol, quantity, entry_price, exit_price, opened_at, closed_at
497
497
  #
498
498
  # Seeing the screen, moving the mouse, typing. Everything a person can do at
499
499
  # this machine, which is exactly why it is OFF until somebody says otherwise:
500
- # Atlas Remote Control > This Computer > "Let an agent use this computer". The
500
+ # Atlas Studio > This Computer > "Let an agent use this computer". The
501
501
  # switch sets ATLAS_COMPUTER_USE for the run; without it every call here refuses
502
502
  # and SAYS SO, and the refusal is written down like any other action.
503
503
  #
@@ -512,7 +512,7 @@ def record_trade(symbol, quantity, entry_price, exit_price, opened_at, closed_at
512
512
  # a password is a credential store nobody meant to make.
513
513
 
514
514
  _COMPUTER_OFF = (
515
- "Using the computer is switched off. Open Atlas Remote Control > This "
515
+ "Using the computer is switched off. Open Atlas Studio > This "
516
516
  "Computer and turn on \"Let an agent use this computer\", then run this again."
517
517
  )
518
518
 
@@ -746,12 +746,12 @@ def computer_ready():
746
746
  import pyautogui # noqa: F401
747
747
  except ImportError:
748
748
  # TWO READERS, TWO ANSWERS. A person should never be sent to a shell:
749
- # Atlas Remote Control fetches this when the switch goes on. An agent
749
+ # Atlas Studio fetches this when the switch goes on. An agent
750
750
  # reading the same line HAS a shell, and if this project keeps its own
751
751
  # venv that is the only place the package can go.
752
- return ("This project cannot use the computer yet. In Atlas Remote "
753
- "Control, turn Computer Use off and on and it will fetch what "
754
- "is missing. From a script's own environment: pip install pyautogui.")
752
+ return ("This project cannot use the computer yet. In Atlas Studio, turn "
753
+ "Computer Use off and on and it will fetch what is missing. From "
754
+ "a script's own environment: pip install pyautogui.")
755
755
  if os_name() == "mac":
756
756
  # There is no way to ASK macOS whether permission was granted; the tell
757
757
  # is that a capture comes back with nothing in it. Cheap to check once.
@@ -779,7 +779,7 @@ def _gui():
779
779
  import pyautogui as _p
780
780
  except ImportError:
781
781
  raise RuntimeError(
782
- "This project cannot use the computer yet. In Atlas Remote Control, "
782
+ "This project cannot use the computer yet. In Atlas Studio, "
783
783
  "turn Computer Use off and on and it will fetch what is missing. "
784
784
  "From a script's own environment: pip install pyautogui."
785
785
  )
@@ -868,9 +868,9 @@ def _do(action, fn, detail=None, points=()):
868
868
  # rather than when the window was focused: what matters is where the click
869
869
  # lands, and the front window can change between the two.
870
870
  if action in _ACTS_ON_A_WINDOW and not _MAY_USE_ATLAS["ok"] and _in_the_atlas_window():
871
- _log_action(action, detail, ok=False, error="inside Atlas Remote Control")
871
+ _log_action(action, detail, ok=False, error="inside Atlas Studio")
872
872
  raise RuntimeError(
873
- "Atlas Remote Control is the window in front, and this run has not "
873
+ "Atlas Studio is the window in front, and this run has not "
874
874
  "been asked to work inside it. Typing there means talking to another "
875
875
  "agent, which is not something to start on your own. If the person "
876
876
  "asked for it, call use_atlas_app() first; otherwise bring the right "
@@ -1965,7 +1965,7 @@ def _one_window(title):
1965
1965
  got = hit[0]
1966
1966
  if not _MAY_USE_ATLAS["ok"] and _is_atlas_title(str(getattr(got, "title", ""))):
1967
1967
  raise RuntimeError(
1968
- '"%s" is Atlas Remote Control, which is showing the person what this '
1968
+ '"%s" is Atlas Studio, which is showing the person what this '
1969
1969
  "run is doing. Touching its window is not something to decide on your "
1970
1970
  "own: if they asked for it, call use_atlas_app() first." % got.title
1971
1971
  )
@@ -1976,7 +1976,7 @@ def _one_window(title):
1976
1976
  #:
1977
1977
  #: The title of a console window is the command line of the program in it until
1978
1978
  #: something sets one. So "C:\...\python3.11.exe" is machinery, and so is
1979
- #: "C:\...\Atlas Remote Control.exe" — which a list of interpreter names would
1979
+ #: "C:\...\Atlas Studio.exe" — which a list of interpreter names would
1980
1980
  #: have walked straight past, and that one was the window sitting over the
1981
1981
  #: owner's chart (owner, 2026-09-06: "screen for any console interpreter and
1982
1982
  #: let's stick with background processes for all of it").
@@ -2483,6 +2483,108 @@ def watch(instruction, every=5, most=200, hours=8, look=True):
2483
2483
  "waits": waits, "varied": len(set(waits)) > 1}
2484
2484
 
2485
2485
 
2486
+ #: How a browser names itself at the end of its window title. Used to work out
2487
+ #: WHICH one is in front, because the keys differ in a couple of places and
2488
+ #: guessing wrong sends a keystroke somewhere nobody is looking.
2489
+ _BROWSERS = (
2490
+ ("Google Chrome", "chrome"), ("Microsoft Edge", "edge"), ("Mozilla Firefox", "firefox"),
2491
+ ("Firefox", "firefox"), ("Brave", "brave"), ("Safari", "safari"),
2492
+ ("Opera", "opera"), ("Vivaldi", "vivaldi"), ("Chromium", "chromium"),
2493
+ )
2494
+
2495
+
2496
+ def browser():
2497
+ """Which browser is on screen, and what its window is called.
2498
+
2499
+ {"name": "edge", "title": "SPY — TradingView and 3 more pages — Microsoft Edge"}
2500
+
2501
+ None when there is no browser window. Worth asking once at the start: a
2502
+ browser is not a wall of pixels, it is an application with an address bar
2503
+ and a find bar, and knowing you are in one changes what the fast move is.
2504
+ """
2505
+ def go():
2506
+ try:
2507
+ wins = _all_windows()
2508
+ except Exception:
2509
+ return None
2510
+ for w in wins:
2511
+ title = str(getattr(w, "title", "") or "")
2512
+ for label, name in _BROWSERS:
2513
+ if title.rstrip().endswith(label) or (" - " + label) in title or (" — " + label) in title:
2514
+ return {"name": name, "title": title[:200]}
2515
+ return None
2516
+ return _do("browser", go, {})
2517
+
2518
+
2519
+ def _chord():
2520
+ """ctrl on Windows and Linux, command on a Mac. The one difference that
2521
+ matters for every browser key there is."""
2522
+ import sys as _sys
2523
+ return "command" if _sys.platform == "darwin" else "ctrl"
2524
+
2525
+
2526
+ def go_to(url, window=""):
2527
+ """Go straight to an address. The fastest move a browser offers.
2528
+
2529
+ go_to("https://www.tradingview.com/chart/?symbol=SPY")
2530
+
2531
+ Bring the browser forward, focus the address bar, replace what is in it,
2532
+ press Enter, and wait for the page to settle. One call for what is otherwise
2533
+ five, and none of it involves finding anything by eye.
2534
+
2535
+ NAVIGATING BY ADDRESS BEATS CLICKING THROUGH A SITE, every time. A menu path
2536
+ is four clicks that each have to land, on a layout that moves; an address is
2537
+ one thing that is either right or wrong and says which.
2538
+ """
2539
+ def go():
2540
+ g = _gui()
2541
+ if window:
2542
+ focus_window(window)
2543
+ else:
2544
+ b = browser()
2545
+ if b:
2546
+ focus_window(b["name"])
2547
+ mod = _chord()
2548
+ g.hotkey(mod, "l") # the address bar, everywhere
2549
+ g.hotkey(mod, "a") # and everything in it, so this replaces
2550
+ g.write(str(url), interval=0.01)
2551
+ g.press("enter")
2552
+ out = _do("go_to", go, {"url": str(url)[:200]})
2553
+ # A page is not there when Enter is pressed. Wait for it to start AND stop,
2554
+ # which is what the settle does, rather than a sleep somebody guessed.
2555
+ _settle(6.0)
2556
+ return out
2557
+
2558
+
2559
+ def find_on_page(text, next_times=0):
2560
+ """Find text on the page and leave the view on it.
2561
+
2562
+ find_on_page("Opening Range")
2563
+
2564
+ ctrl+F, type, Enter, Escape — the browser scrolls to the match and the find
2565
+ bar closes, leaving the page where the thing is. That is a way to REACH
2566
+ something without knowing where it is on screen, which is the expensive kind
2567
+ of knowing.
2568
+
2569
+ `next_times` presses Enter again for the second, third match.
2570
+
2571
+ It cannot tell you whether it found anything: the browser says so in its own
2572
+ find bar, which is gone by the time you look. Take a picture after.
2573
+ """
2574
+ def go():
2575
+ g = _gui()
2576
+ mod = _chord()
2577
+ g.hotkey(mod, "f")
2578
+ g.write(str(text), interval=0.01)
2579
+ g.press("enter")
2580
+ for _ in range(max(0, int(next_times))):
2581
+ g.press("enter")
2582
+ g.press("escape")
2583
+ out = _do("find_on_page", go, {"text": len(str(text))})
2584
+ _settle(1.5)
2585
+ return out
2586
+
2587
+
2486
2588
  def scroll_x(amount, x=None, y=None):
2487
2589
  """Scroll SIDEWAYS. Positive goes right, negative left.
2488
2590
 
@@ -2641,7 +2743,7 @@ _HANDOVER = {"to_human": False, "why": ""}
2641
2743
 
2642
2744
  # ── DRIVING ATLAS ITSELF ────────────────────────────────────────────────────
2643
2745
  #
2644
- # Atlas Remote Control is a window like any other, so a run that can use the
2746
+ # Atlas Studio is a window like any other, so a run that can use the
2645
2747
  # computer can use it: open a project, press Run, fix a setting somebody asked
2646
2748
  # about. That is fine and it is useful.
2647
2749
  #
@@ -2652,6 +2754,11 @@ _HANDOVER = {"to_human": False, "why": ""}
2652
2754
  #
2653
2755
  # So: acting inside the Atlas window needs saying so once. Looking never does,
2654
2756
  # and neither does anything in any other window.
2757
+ #: BOTH NAMES, and the old one stays. The app was renamed from Atlas Remote
2758
+ #: Control to Atlas Studio, and a machine running the previous build has a window
2759
+ #: still called the old thing — dropping it would quietly turn off the guard that
2760
+ #: keeps a run from typing into Atlas's own chat, on exactly the installs that
2761
+ #: have not updated yet.
2655
2762
  _ATLAS_WINDOWS = ("atlas remote control", "atlas studio")
2656
2763
  _MAY_USE_ATLAS = {"ok": os.environ.get("ATLAS_MAY_DRIVE_APP", "") == "1"}
2657
2764
  #: The ones that CHANGE something. Moving the pointer and looking do not.
@@ -2665,7 +2772,7 @@ _ACTS_ON_A_WINDOW = (
2665
2772
 
2666
2773
 
2667
2774
  def use_atlas_app(allowed=True):
2668
- """Say the person asked you to work inside Atlas Remote Control itself.
2775
+ """Say the person asked you to work inside Atlas Studio itself.
2669
2776
 
2670
2777
  Only when they actually did. Deciding for yourself that it would be handy is
2671
2778
  the thing this exists to stop, and the first place a run wanders to is the
@@ -2944,8 +3051,8 @@ def stream_frames(send=None, fps=3, width=1920, quality=58, max_s=120):
2944
3051
  frame = _grab_frame(width, quality, _watched_screen())
2945
3052
  if frame is None:
2946
3053
  send({"kind": "error", "message": (
2947
- "This computer is not set up to be watched yet. Open Atlas Remote "
2948
- "Control on it, go to Computer Use, and turn the switch off and on: "
3054
+ "This computer is not set up to be watched yet. Open Atlas Studio "
3055
+ "on it, go to Computer Use, and turn the switch off and on: "
2949
3056
  "it fetches what is missing.")})
2950
3057
  return
2951
3058
  mark = hashlib.md5(frame).digest()
@@ -1022,7 +1022,7 @@ export function readJobFile(job, rel, root) {
1022
1022
  * This half had NO command path at all: `writeCommand` did not exist and the
1023
1023
  * relay had no `command` op, so a custom button pressed from the WEB reached a
1024
1024
  * machine running this CLI and vanished, while the same button pressed in the
1025
- * app worked (Atlas Remote Control writes the file itself). One button, two
1025
+ * app worked (Atlas Studio writes the file itself). One button, two
1026
1026
  * answers, depending on where you pressed it (owner, 2026-09-04: "the agent can
1027
1027
  * wire buttons that read or write local — the web can do this too, it's either
1028
1028
  * or"). Mirrors python/atlas_cli/remote_control.py.
@@ -1511,7 +1511,7 @@ const STARTER_README = (name) => [
1511
1511
  'must stay up says `"mode": "listen"` in atlas.json and the runner keeps it',
1512
1512
  'alive.', '',
1513
1513
  'Nothing here is stored on Atlas servers. The schedule runs on this computer,',
1514
- 'while Atlas Remote Control is open.', '',
1514
+ 'while Atlas Studio is open.', '',
1515
1515
  ].join('\n');
1516
1516
 
1517
1517
  const STARTER_MAIN = (name) => [