mindvest-atlas 0.6.3 → 0.7.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.6.3",
3
+ "version": "0.7.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": {
@@ -15,9 +15,9 @@ import { requestJson } from '../api/client.js';
15
15
  import { runStream } from '../util/stream-loop.js';
16
16
  import { color, eprintln } from '../util/ui.js';
17
17
  import {
18
- DEFAULT_TIMEOUT_MS, claimRun, computerName, jobEnv, listJobFiles, listRuns, machineId,
18
+ DEFAULT_TIMEOUT_MS, claimRun, computerName, isPaused, jobEnv, listJobFiles, listRuns, machineId,
19
19
  osLabel, readJobFile, releaseRun, staleness, summarize, tail, workspace, workspaceRoot,
20
- writeHelpers,
20
+ setPaused, writeHelpers,
21
21
  } from '../remote-control.js';
22
22
  import fs from 'node:fs';
23
23
  import path from 'node:path';
@@ -98,6 +98,10 @@ async function answer(requestId, payload) {
98
98
  async function handleAsk(ask, root) {
99
99
  if (!ask?.request_id) return;
100
100
  if (ask.op === 'list') return answer(ask.request_id, { ok: true, jobs: listJobFiles(root) });
101
+ if (ask.op === 'pause') {
102
+ const { dir } = workspace(ask.job || '', root);
103
+ return answer(ask.request_id, { ok: true, paused: setPaused(dir, Boolean(ask.paused)) });
104
+ }
101
105
  if (ask.op === 'runs') return answer(ask.request_id, { ok: true, runs: listRuns(root) });
102
106
  if (ask.op === 'read') return answer(ask.request_id, readJobFile(ask.job || '', ask.path || '', root));
103
107
  return answer(ask.request_id, { ok: false, error: 'Unknown request.' });
@@ -114,6 +118,13 @@ async function runJob(job, root, timeoutMs) {
114
118
  }
115
119
 
116
120
  const { dir, entry, argv } = workspace(job.automation_id, root);
121
+ // Paused is the owner saying "not now". A scheduled run that fires anyway
122
+ // would make the switch meaningless; a MANUAL run is the owner asking
123
+ // directly, so it goes through.
124
+ if (isPaused(dir) && job.source !== 'manual') {
125
+ eprintln(color.dim(`· ${name} — paused`));
126
+ return undefined;
127
+ }
117
128
  if (!fs.existsSync(entry)) {
118
129
  // NOT a failure. The schedule fired and this computer took the job; there
119
130
  // was simply nothing to carry out yet. Its owner never sees the code, so
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.6.3';
5
+ export const VERSION = '0.7.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';
@@ -331,7 +331,7 @@ export function listJobFiles(root) {
331
331
  }
332
332
  };
333
333
  walk(path.join(base, job), '');
334
- return { job, files };
334
+ return { job, files, paused: isPaused(path.join(base, job)) };
335
335
  });
336
336
  }
337
337
 
@@ -372,6 +372,31 @@ export function readJobFile(job, rel, root) {
372
372
  * long), which is why the writer stamps both.
373
373
  */
374
374
  export const RUNNING_MARKER = '.running';
375
+ /**
376
+ * Marks a project the owner has switched off.
377
+ *
378
+ * Local, like everything else about a project: pausing is a fact about this
379
+ * computer's copy, and a file is the only form of it that survives the runner
380
+ * restarting and is visible in the folder to anyone wondering why nothing has
381
+ * happened.
382
+ */
383
+ export const PAUSED_MARKER = '.paused';
384
+
385
+ export const isPaused = (dir) => fs.existsSync(path.join(dir, PAUSED_MARKER));
386
+
387
+ /** Switch one project off or back on. Returns the state it ended in. */
388
+ export function setPaused(dir, paused) {
389
+ const m = path.join(dir, PAUSED_MARKER);
390
+ try {
391
+ if (paused) {
392
+ fs.mkdirSync(dir, { recursive: true });
393
+ fs.writeFileSync(m, `${Date.now() / 1000}\n`);
394
+ } else {
395
+ fs.rmSync(m, { force: true });
396
+ }
397
+ } catch { /* best-effort */ }
398
+ return isPaused(dir);
399
+ }
375
400
 
376
401
  export const runningMarker = (dir) => path.join(dir, 'runs', RUNNING_MARKER);
377
402