@notiamo/runner 0.1.0 → 0.1.2

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.
Files changed (3) hide show
  1. package/README.md +8 -0
  2. package/dist/cli.js +263 -222
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -30,6 +30,14 @@ The service is a launchd agent `com.notiamo.runner` on macOS, a Scheduled Task "
30
30
  Windows, and a systemd user unit `notiamo-runner` on Linux. After `npx`, use `npx @notiamo/runner status` (or install
31
31
  the package globally to have `notiamo-runner` on the path).
32
32
 
33
+ ### Two runners on one machine (`--profile`)
34
+
35
+ `npx @notiamo/runner init --profile staging` sets up a second, separate runner: its own folder
36
+ (`~/.notiamo-runner/profiles/staging`: config, Claude sign-in, projects, log) and its own service
37
+ (`com.notiamo.runner.staging`, "Notiamo runner (staging)", `notiamo-runner-staging`). Give `--profile staging` to every
38
+ command of that runner: `status`, `logs`, `stop`, `uninstall`. A name has letters, digits and dashes (1 to 32). Without
39
+ `--profile`, everything is as before.
40
+
33
41
  ## What it is
34
42
 
35
43
  A small program that waits for agent runs your organization queues in Notiamo, then does each run here: it clones
package/dist/cli.js CHANGED
@@ -262,11 +262,11 @@ var require_FtpContext = __commonJS({
262
262
  * request-response situation.
263
263
  */
264
264
  request(command) {
265
- return this.handle(command, (res, task) => {
265
+ return this.handle(command, (res, task2) => {
266
266
  if (res instanceof Error) {
267
- task.reject(res);
267
+ task2.reject(res);
268
268
  } else {
269
- task.resolve(res);
269
+ task2.resolve(res);
270
270
  }
271
271
  });
272
272
  }
@@ -372,12 +372,12 @@ Closing reason: ${this._closingError.stack}`;
372
372
  * @protected
373
373
  */
374
374
  _passToHandler(response) {
375
- const task = this._task;
376
- if (task) {
375
+ const task2 = this._task;
376
+ if (task2) {
377
377
  try {
378
- task.responseHandler(response, task.resolver);
378
+ task2.responseHandler(response, task2.resolver);
379
379
  } catch (err) {
380
- task.resolver.reject(err);
380
+ task2.resolver.reject(err);
381
381
  this.closeWithError(err);
382
382
  }
383
383
  }
@@ -1316,7 +1316,7 @@ var require_transfer = __commonJS({
1316
1316
  /**
1317
1317
  * The data connection has finished the transfer.
1318
1318
  */
1319
- onDataDone(task) {
1319
+ onDataDone(task2) {
1320
1320
  if (this.taskSettled) {
1321
1321
  return;
1322
1322
  }
@@ -1324,19 +1324,19 @@ var require_transfer = __commonJS({
1324
1324
  this.progress.updateAndStop();
1325
1325
  this.ftp.socket.setTimeout(this.ftp.timeout);
1326
1326
  this.dataTransferDone = true;
1327
- this.tryResolve(task);
1327
+ this.tryResolve(task2);
1328
1328
  }
1329
1329
  /**
1330
1330
  * The control connection reports the transfer as finished.
1331
1331
  */
1332
- onControlDone(task, response) {
1332
+ onControlDone(task2, response) {
1333
1333
  this.response = response;
1334
- this.tryResolve(task);
1334
+ this.tryResolve(task2);
1335
1335
  }
1336
1336
  /**
1337
1337
  * An error has been reported and the task should be rejected.
1338
1338
  */
1339
- onError(task, err) {
1339
+ onError(task2, err) {
1340
1340
  if (this.taskSettled) {
1341
1341
  return;
1342
1342
  }
@@ -1345,7 +1345,7 @@ var require_transfer = __commonJS({
1345
1345
  this.progress.updateAndStop();
1346
1346
  this.ftp.socket.setTimeout(this.ftp.timeout);
1347
1347
  this.ftp.dataSocket = void 0;
1348
- task.reject(err);
1348
+ task2.reject(err);
1349
1349
  }
1350
1350
  /**
1351
1351
  * Control connection sent an unexpected request requiring a response from our part. We
@@ -1356,12 +1356,12 @@ var require_transfer = __commonJS({
1356
1356
  const err = new Error(`Unexpected FTP response is requesting an answer: ${response.message}`);
1357
1357
  this.ftp.closeWithError(err);
1358
1358
  }
1359
- tryResolve(task) {
1359
+ tryResolve(task2) {
1360
1360
  const canResolve = this.dataTransferDone && this.response !== void 0;
1361
1361
  if (canResolve) {
1362
1362
  this.taskSettled = true;
1363
1363
  this.ftp.dataSocket = void 0;
1364
- task.resolve(this.response);
1364
+ task2.resolve(this.response);
1365
1365
  }
1366
1366
  }
1367
1367
  };
@@ -1369,9 +1369,9 @@ var require_transfer = __commonJS({
1369
1369
  const resolver = new TransferResolver(config2.ftp, config2.tracker);
1370
1370
  const fullCommand = `${config2.command} ${config2.remotePath}`;
1371
1371
  let transferStarted = false;
1372
- return config2.ftp.handle(fullCommand, (res, task) => {
1372
+ return config2.ftp.handle(fullCommand, (res, task2) => {
1373
1373
  if (res instanceof Error) {
1374
- resolver.onError(task, res);
1374
+ resolver.onError(task2, res);
1375
1375
  } else if (res.code === 150 || res.code === 125) {
1376
1376
  if (transferStarted) {
1377
1377
  return;
@@ -1379,7 +1379,7 @@ var require_transfer = __commonJS({
1379
1379
  transferStarted = true;
1380
1380
  const dataSocket = config2.ftp.dataSocket;
1381
1381
  if (!dataSocket) {
1382
- resolver.onError(task, new Error("Upload should begin but no data connection is available."));
1382
+ resolver.onError(task2, new Error("Upload should begin but no data connection is available."));
1383
1383
  return;
1384
1384
  }
1385
1385
  const canUpload = "getCipher" in dataSocket ? dataSocket.getCipher() !== void 0 : true;
@@ -1388,14 +1388,14 @@ var require_transfer = __commonJS({
1388
1388
  resolver.onDataStart(config2.remotePath, config2.type);
1389
1389
  (0, stream_1.pipeline)(source, dataSocket, (err) => {
1390
1390
  if (err) {
1391
- resolver.onError(task, err);
1391
+ resolver.onError(task2, err);
1392
1392
  } else {
1393
- resolver.onDataDone(task);
1393
+ resolver.onDataDone(task2);
1394
1394
  }
1395
1395
  });
1396
1396
  });
1397
1397
  } else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) {
1398
- resolver.onControlDone(task, res);
1398
+ resolver.onControlDone(task2, res);
1399
1399
  } else if ((0, parseControlResponse_1.positiveIntermediate)(res.code)) {
1400
1400
  resolver.onUnexpectedRequest(res);
1401
1401
  }
@@ -1407,9 +1407,9 @@ var require_transfer = __commonJS({
1407
1407
  }
1408
1408
  const resolver = new TransferResolver(config2.ftp, config2.tracker);
1409
1409
  let transferStarted = false;
1410
- return config2.ftp.handle(config2.command, (res, task) => {
1410
+ return config2.ftp.handle(config2.command, (res, task2) => {
1411
1411
  if (res instanceof Error) {
1412
- resolver.onError(task, res);
1412
+ resolver.onError(task2, res);
1413
1413
  } else if (res.code === 150 || res.code === 125) {
1414
1414
  if (transferStarted) {
1415
1415
  return;
@@ -1417,22 +1417,22 @@ var require_transfer = __commonJS({
1417
1417
  transferStarted = true;
1418
1418
  const dataSocket = config2.ftp.dataSocket;
1419
1419
  if (!dataSocket) {
1420
- resolver.onError(task, new Error("Download should begin but no data connection is available."));
1420
+ resolver.onError(task2, new Error("Download should begin but no data connection is available."));
1421
1421
  return;
1422
1422
  }
1423
1423
  config2.ftp.log(`Downloading from ${(0, netUtils_1.describeAddress)(dataSocket)} (${(0, netUtils_1.describeTLS)(dataSocket)})`);
1424
1424
  resolver.onDataStart(config2.remotePath, config2.type);
1425
1425
  (0, stream_1.pipeline)(dataSocket, destination, (err) => {
1426
1426
  if (err) {
1427
- resolver.onError(task, err);
1427
+ resolver.onError(task2, err);
1428
1428
  } else {
1429
- resolver.onDataDone(task);
1429
+ resolver.onDataDone(task2);
1430
1430
  }
1431
1431
  });
1432
1432
  } else if (res.code === 350) {
1433
1433
  config2.ftp.send("RETR " + config2.remotePath);
1434
1434
  } else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) {
1435
- resolver.onControlDone(task, res);
1435
+ resolver.onControlDone(task2, res);
1436
1436
  } else if ((0, parseControlResponse_1.positiveIntermediate)(res.code)) {
1437
1437
  resolver.onUnexpectedRequest(res);
1438
1438
  }
@@ -1552,13 +1552,13 @@ var require_Client = __commonJS({
1552
1552
  * Handles the first reponse by an FTP server after the socket connection has been established.
1553
1553
  */
1554
1554
  _handleConnectResponse() {
1555
- return this.ftp.handle(void 0, (res, task) => {
1555
+ return this.ftp.handle(void 0, (res, task2) => {
1556
1556
  if (res instanceof Error) {
1557
- task.reject(res);
1557
+ task2.reject(res);
1558
1558
  } else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) {
1559
- task.resolve(res);
1559
+ task2.resolve(res);
1560
1560
  } else {
1561
- task.reject(new FtpContext_1.FTPError(res));
1561
+ task2.reject(new FtpContext_1.FTPError(res));
1562
1562
  }
1563
1563
  });
1564
1564
  }
@@ -1578,13 +1578,13 @@ var require_Client = __commonJS({
1578
1578
  * @param command
1579
1579
  */
1580
1580
  sendIgnoringError(command) {
1581
- return this.ftp.handle(command, (res, task) => {
1581
+ return this.ftp.handle(command, (res, task2) => {
1582
1582
  if (res instanceof FtpContext_1.FTPError) {
1583
- task.resolve({ code: res.code, message: res.message });
1583
+ task2.resolve({ code: res.code, message: res.message });
1584
1584
  } else if (res instanceof Error) {
1585
- task.reject(res);
1585
+ task2.reject(res);
1586
1586
  } else {
1587
- task.resolve(res);
1587
+ task2.resolve(res);
1588
1588
  }
1589
1589
  });
1590
1590
  }
@@ -1609,15 +1609,15 @@ var require_Client = __commonJS({
1609
1609
  */
1610
1610
  login(user = "anonymous", password = "guest") {
1611
1611
  this.ftp.log(`Login security: ${(0, netUtils_1.describeTLS)(this.ftp.socket)}`);
1612
- return this.ftp.handle("USER " + user, (res, task) => {
1612
+ return this.ftp.handle("USER " + user, (res, task2) => {
1613
1613
  if (res instanceof Error) {
1614
- task.reject(res);
1614
+ task2.reject(res);
1615
1615
  } else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) {
1616
- task.resolve(res);
1616
+ task2.resolve(res);
1617
1617
  } else if (res.code === 331) {
1618
1618
  this.ftp.send("PASS " + password);
1619
1619
  } else {
1620
- task.reject(new FtpContext_1.FTPError(res));
1620
+ task2.reject(new FtpContext_1.FTPError(res));
1621
1621
  }
1622
1622
  });
1623
1623
  }
@@ -2667,9 +2667,9 @@ var require_codegen = __commonJS({
2667
2667
  }
2668
2668
  };
2669
2669
  var Label = class extends Node {
2670
- constructor(label) {
2670
+ constructor(label2) {
2671
2671
  super();
2672
- this.label = label;
2672
+ this.label = label2;
2673
2673
  this.names = {};
2674
2674
  }
2675
2675
  render({ _n }) {
@@ -2677,14 +2677,14 @@ var require_codegen = __commonJS({
2677
2677
  }
2678
2678
  };
2679
2679
  var Break = class extends Node {
2680
- constructor(label) {
2680
+ constructor(label2) {
2681
2681
  super();
2682
- this.label = label;
2682
+ this.label = label2;
2683
2683
  this.names = {};
2684
2684
  }
2685
2685
  render({ _n }) {
2686
- const label = this.label ? ` ${this.label}` : "";
2687
- return `break${label};` + _n;
2686
+ const label2 = this.label ? ` ${this.label}` : "";
2687
+ return `break${label2};` + _n;
2688
2688
  }
2689
2689
  };
2690
2690
  var Throw = class extends Node {
@@ -3096,12 +3096,12 @@ var require_codegen = __commonJS({
3096
3096
  return this._endBlockNode(For);
3097
3097
  }
3098
3098
  // `label` statement
3099
- label(label) {
3100
- return this._leafNode(new Label(label));
3099
+ label(label2) {
3100
+ return this._leafNode(new Label(label2));
3101
3101
  }
3102
3102
  // `break` statement
3103
- break(label) {
3104
- return this._leafNode(new Break(label));
3103
+ break(label2) {
3104
+ return this._leafNode(new Break(label2));
3105
3105
  }
3106
3106
  // `return` statement
3107
3107
  return(value) {
@@ -29638,6 +29638,60 @@ var require_lib2 = __commonJS({
29638
29638
  }
29639
29639
  });
29640
29640
 
29641
+ // src/config.ts
29642
+ import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
29643
+ import os from "node:os";
29644
+ import path from "node:path";
29645
+ var defaultDir = () => process.env.NOTIAMO_RUNNER_HOME ?? path.join(os.homedir(), ".notiamo-runner");
29646
+ var PROFILE_RE = /^[a-z0-9][a-z0-9-]{0,31}$/;
29647
+ function profileDir(profile, base = defaultDir()) {
29648
+ if (profile === void 0) return base;
29649
+ if (!PROFILE_RE.test(profile)) throw new Error(`A profile name has letters, digits and dashes only (1 to 32), not "${profile}".`);
29650
+ return path.join(base, "profiles", profile);
29651
+ }
29652
+ function paths(dir) {
29653
+ return {
29654
+ config: path.join(dir, "config.json"),
29655
+ log: path.join(dir, "runner.log"),
29656
+ claudeConfigDir: path.join(dir, "claude"),
29657
+ projectsDir: path.join(dir, "projects"),
29658
+ stateFile: path.join(dir, "state.json")
29659
+ };
29660
+ }
29661
+ function loadConfig(dir = defaultDir()) {
29662
+ let text;
29663
+ try {
29664
+ text = readFileSync(paths(dir).config, "utf8");
29665
+ } catch {
29666
+ throw new Error(`No runner config in ${dir}. Run: notiamo-runner init`);
29667
+ }
29668
+ return JSON.parse(text);
29669
+ }
29670
+ function saveConfig(cfg, dir = defaultDir()) {
29671
+ mkdirSync(dir, { recursive: true, mode: 448 });
29672
+ const file2 = paths(dir).config;
29673
+ writeFileSync(file2, JSON.stringify(cfg, null, 2) + "\n", { mode: 384 });
29674
+ try {
29675
+ chmodSync(file2, 384);
29676
+ } catch {
29677
+ }
29678
+ }
29679
+
29680
+ // src/args.ts
29681
+ function parseArgs(argv) {
29682
+ const out = {};
29683
+ for (let i = 0; i < argv.length; i++) {
29684
+ const a = argv[i];
29685
+ if (a === "--profile" || a.startsWith("--profile=")) {
29686
+ const v = a === "--profile" ? argv[++i] : a.slice("--profile=".length);
29687
+ if (!v || !PROFILE_RE.test(v)) return { error: `--profile needs a name of letters, digits and dashes (1 to 32), not "${v ?? ""}".` };
29688
+ out.profile = v;
29689
+ } else if (out.cmd === void 0) out.cmd = a;
29690
+ else return { error: `Unexpected argument: ${a}` };
29691
+ }
29692
+ return out;
29693
+ }
29694
+
29641
29695
  // src/commands.ts
29642
29696
  import { readFileSync as readFileSync3 } from "node:fs";
29643
29697
  import os3 from "node:os";
@@ -29702,39 +29756,6 @@ var NotiamoApi = class {
29702
29756
  }
29703
29757
  };
29704
29758
 
29705
- // src/config.ts
29706
- import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
29707
- import os from "node:os";
29708
- import path from "node:path";
29709
- var defaultDir = () => process.env.NOTIAMO_RUNNER_HOME ?? path.join(os.homedir(), ".notiamo-runner");
29710
- function paths(dir) {
29711
- return {
29712
- config: path.join(dir, "config.json"),
29713
- log: path.join(dir, "runner.log"),
29714
- claudeConfigDir: path.join(dir, "claude"),
29715
- projectsDir: path.join(dir, "projects"),
29716
- stateFile: path.join(dir, "state.json")
29717
- };
29718
- }
29719
- function loadConfig(dir = defaultDir()) {
29720
- let text;
29721
- try {
29722
- text = readFileSync(paths(dir).config, "utf8");
29723
- } catch {
29724
- throw new Error(`No runner config in ${dir}. Run: notiamo-runner init`);
29725
- }
29726
- return JSON.parse(text);
29727
- }
29728
- function saveConfig(cfg, dir = defaultDir()) {
29729
- mkdirSync(dir, { recursive: true, mode: 448 });
29730
- const file2 = paths(dir).config;
29731
- writeFileSync(file2, JSON.stringify(cfg, null, 2) + "\n", { mode: 384 });
29732
- try {
29733
- chmodSync(file2, 384);
29734
- } catch {
29735
- }
29736
- }
29737
-
29738
29759
  // src/engine/claude.ts
29739
29760
  import * as child_process from "node:child_process";
29740
29761
  var MAX_TEXT = 2e3;
@@ -30062,6 +30083,7 @@ import { mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync2, exist
30062
30083
  import path2 from "node:path";
30063
30084
 
30064
30085
  // src/service/common.ts
30086
+ var startArgs = (profile) => profile ? ["start", "--profile", profile] : ["start"];
30065
30087
  function serviceEnv(env = {}) {
30066
30088
  const out = {};
30067
30089
  for (const k of ["PATH", "NOTIAMO_RUNNER_HOME"]) if (env[k]) out[k] = env[k];
@@ -30072,8 +30094,8 @@ function must(what, r) {
30072
30094
  }
30073
30095
 
30074
30096
  // src/service/darwin.ts
30075
- var LABEL = "com.notiamo.runner";
30076
- var plistPath = (home) => path2.join(home, "Library", "LaunchAgents", `${LABEL}.plist`);
30097
+ var label = (profile) => profile ? `com.notiamo.runner.${profile}` : "com.notiamo.runner";
30098
+ var plistPath = (home, profile) => path2.join(home, "Library", "LaunchAgents", `${label(profile)}.plist`);
30077
30099
  var domain = (uid) => `gui/${uid}`;
30078
30100
  var BOOTSTRAP_TRIES = 5;
30079
30101
  var BOOTSTRAP_RETRY_MS = 500;
@@ -30086,12 +30108,12 @@ function renderPlist(o) {
30086
30108
  <plist version="1.0">
30087
30109
  <dict>
30088
30110
  <key>Label</key>
30089
- <string>${LABEL}</string>
30111
+ <string>${label(o.profile)}</string>
30090
30112
  <key>ProgramArguments</key>
30091
30113
  <array>
30092
30114
  <string>${esc(o.nodePath)}</string>
30093
30115
  <string>${esc(o.cliPath)}</string>
30094
- <string>start</string>
30116
+ ${startArgs(o.profile).map((a) => ` <string>${esc(a)}</string>`).join("\n")}
30095
30117
  </array>
30096
30118
  <key>RunAtLoad</key>
30097
30119
  <true/>
@@ -30112,9 +30134,9 @@ ${env}
30112
30134
  `;
30113
30135
  }
30114
30136
  async function install(o) {
30115
- const file2 = plistPath(o.home);
30137
+ const file2 = plistPath(o.home, o.profile);
30116
30138
  mkdirSync2(path2.dirname(file2), { recursive: true });
30117
- writeFileSync2(file2, renderPlist({ nodePath: o.nodePath, cliPath: o.cliPath, log: o.paths.log, env: o.env }));
30139
+ writeFileSync2(file2, renderPlist({ nodePath: o.nodePath, cliPath: o.cliPath, log: o.paths.log, env: o.env, profile: o.profile }));
30118
30140
  await o.exec("launchctl", ["bootout", domain(o.uid), file2]);
30119
30141
  const sleep = o.sleep ?? ((ms) => delay(ms));
30120
30142
  for (let i = 1; ; i++) {
@@ -30127,15 +30149,15 @@ async function install(o) {
30127
30149
  }
30128
30150
  }
30129
30151
  async function stop(o) {
30130
- await o.exec("launchctl", ["bootout", domain(o.uid), plistPath(o.home)]);
30152
+ await o.exec("launchctl", ["bootout", domain(o.uid), plistPath(o.home, o.profile)]);
30131
30153
  }
30132
30154
  async function uninstall(o) {
30133
30155
  await stop(o);
30134
- rmSync(plistPath(o.home), { force: true });
30156
+ rmSync(plistPath(o.home, o.profile), { force: true });
30135
30157
  }
30136
30158
  async function status(o) {
30137
- if (!existsSync(plistPath(o.home))) return "not_installed";
30138
- const r = await o.exec("launchctl", ["print", `${domain(o.uid)}/${LABEL}`]);
30159
+ if (!existsSync(plistPath(o.home, o.profile))) return "not_installed";
30160
+ const r = await o.exec("launchctl", ["print", `${domain(o.uid)}/${label(o.profile)}`]);
30139
30161
  return r.code === 0 ? "running" : "stopped";
30140
30162
  }
30141
30163
 
@@ -30150,17 +30172,17 @@ __export(linux_exports, {
30150
30172
  });
30151
30173
  import { existsSync as existsSync2, mkdirSync as mkdirSync3, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
30152
30174
  import path3 from "node:path";
30153
- var UNIT = "notiamo-runner";
30154
- var unitPath = (home) => path3.join(home, ".config", "systemd", "user", `${UNIT}.service`);
30175
+ var unit = (profile) => profile ? `notiamo-runner-${profile}` : "notiamo-runner";
30176
+ var unitPath = (home, profile) => path3.join(home, ".config", "systemd", "user", `${unit(profile)}.service`);
30155
30177
  var q = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
30156
30178
  function renderUnit(o) {
30157
30179
  const env = Object.entries(serviceEnv(o.env)).map(([k, v]) => `Environment=${q(`${k}=${v}`)}`).join("\n");
30158
30180
  return `[Unit]
30159
- Description=Notiamo runner
30181
+ Description=Notiamo runner${o.profile ? ` (${o.profile})` : ""}
30160
30182
  After=network-online.target
30161
30183
 
30162
30184
  [Service]
30163
- ExecStart=${q(o.nodePath)} ${q(o.cliPath)} start
30185
+ ExecStart=${q(o.nodePath)} ${q(o.cliPath)} ${startArgs(o.profile).join(" ")}
30164
30186
  Restart=always
30165
30187
  RestartSec=5
30166
30188
  StandardOutput=append:${o.log}
@@ -30172,25 +30194,25 @@ WantedBy=default.target
30172
30194
  `;
30173
30195
  }
30174
30196
  async function install2(o) {
30175
- const file2 = unitPath(o.home);
30197
+ const file2 = unitPath(o.home, o.profile);
30176
30198
  mkdirSync3(path3.dirname(file2), { recursive: true });
30177
- writeFileSync3(file2, renderUnit({ nodePath: o.nodePath, cliPath: o.cliPath, log: o.paths.log, env: o.env }));
30199
+ writeFileSync3(file2, renderUnit({ nodePath: o.nodePath, cliPath: o.cliPath, log: o.paths.log, env: o.env, profile: o.profile }));
30178
30200
  must("systemctl --user daemon-reload", await o.exec("systemctl", ["--user", "daemon-reload"]));
30179
- must("systemctl --user enable", await o.exec("systemctl", ["--user", "enable", UNIT]));
30180
- must("systemctl --user restart", await o.exec("systemctl", ["--user", "restart", UNIT]));
30201
+ must("systemctl --user enable", await o.exec("systemctl", ["--user", "enable", unit(o.profile)]));
30202
+ must("systemctl --user restart", await o.exec("systemctl", ["--user", "restart", unit(o.profile)]));
30181
30203
  await o.exec("loginctl", ["enable-linger"]);
30182
30204
  }
30183
30205
  async function stop2(o) {
30184
- await o.exec("systemctl", ["--user", "stop", UNIT]);
30206
+ await o.exec("systemctl", ["--user", "stop", unit(o.profile)]);
30185
30207
  }
30186
30208
  async function uninstall2(o) {
30187
- await o.exec("systemctl", ["--user", "disable", "--now", UNIT]);
30188
- rmSync2(unitPath(o.home), { force: true });
30209
+ await o.exec("systemctl", ["--user", "disable", "--now", unit(o.profile)]);
30210
+ rmSync2(unitPath(o.home, o.profile), { force: true });
30189
30211
  await o.exec("systemctl", ["--user", "daemon-reload"]);
30190
30212
  }
30191
30213
  async function status2(o) {
30192
- if (!existsSync2(unitPath(o.home))) return "not_installed";
30193
- const r = await o.exec("systemctl", ["--user", "is-active", UNIT]);
30214
+ if (!existsSync2(unitPath(o.home, o.profile))) return "not_installed";
30215
+ const r = await o.exec("systemctl", ["--user", "is-active", unit(o.profile)]);
30194
30216
  return r.stdout.trim() === "active" ? "running" : "stopped";
30195
30217
  }
30196
30218
 
@@ -30203,24 +30225,24 @@ __export(win32_exports, {
30203
30225
  stop: () => stop3,
30204
30226
  uninstall: () => uninstall3
30205
30227
  });
30206
- var TASK = "Notiamo runner";
30228
+ var task = (profile) => profile ? `Notiamo runner (${profile})` : "Notiamo runner";
30207
30229
  function renderCreateArgs(o) {
30208
- return ["/Create", "/F", "/SC", "ONLOGON", "/RL", "LIMITED", "/TN", TASK, "/TR", `"${o.nodePath}" "${o.cliPath}" start`];
30230
+ return ["/Create", "/F", "/SC", "ONLOGON", "/RL", "LIMITED", "/TN", task(o.profile), "/TR", `"${o.nodePath}" "${o.cliPath}" ${startArgs(o.profile).join(" ")}`];
30209
30231
  }
30210
30232
  async function install3(o) {
30211
30233
  await stop3(o);
30212
30234
  must("schtasks /Create", await o.exec("schtasks", renderCreateArgs(o)));
30213
- must("schtasks /Run", await o.exec("schtasks", ["/Run", "/TN", TASK]));
30235
+ must("schtasks /Run", await o.exec("schtasks", ["/Run", "/TN", task(o.profile)]));
30214
30236
  }
30215
30237
  async function stop3(o) {
30216
- await o.exec("schtasks", ["/End", "/TN", TASK]);
30238
+ await o.exec("schtasks", ["/End", "/TN", task(o.profile)]);
30217
30239
  }
30218
30240
  async function uninstall3(o) {
30219
30241
  await stop3(o);
30220
- await o.exec("schtasks", ["/Delete", "/F", "/TN", TASK]);
30242
+ await o.exec("schtasks", ["/Delete", "/F", "/TN", task(o.profile)]);
30221
30243
  }
30222
30244
  async function status3(o) {
30223
- const r = await o.exec("powershell", ["-NoProfile", "-NonInteractive", "-Command", `(Get-ScheduledTask -TaskName '${TASK}' -ErrorAction Stop).State`]);
30245
+ const r = await o.exec("powershell", ["-NoProfile", "-NonInteractive", "-Command", `(Get-ScheduledTask -TaskName '${task(o.profile)}' -ErrorAction Stop).State`]);
30224
30246
  if (r.code !== 0) return "not_installed";
30225
30247
  return r.stdout.trim() === "Running" ? "running" : "stopped";
30226
30248
  }
@@ -30250,13 +30272,13 @@ function normalizeHost(input2) {
30250
30272
  return /^https?:\/\//i.test(h) ? h : `https://${h}`;
30251
30273
  }
30252
30274
  async function runInit(deps) {
30253
- const dir = deps.dir ?? defaultDir();
30275
+ const dir = deps.dir ?? profileDir(deps.profile);
30254
30276
  const platform = deps.platform ?? process.platform;
30255
30277
  const p = paths(dir);
30256
30278
  const env = deps.env ?? process.env;
30257
30279
  const print = deps.print ?? ((s) => console.log(s));
30258
30280
  const ask = async (q2, def) => (await deps.prompt(q2, { default: def })).trim() || def;
30259
- const token = (await deps.prompt("Runner token (from Notiamo: Settings \u203A Agent runs \u203A Runners): ", { hidden: true })).trim();
30281
+ const token = (await deps.prompt("Runner token (made in Notiamo under Settings \u203A Agent runs \u203A Runners; nothing shows while you paste it): ", { hidden: true })).trim();
30260
30282
  if (!token.startsWith("ntr_")) throw new Error("That is not a runner token: it starts with ntr_. Make one in Notiamo: Settings \u203A Agent runs \u203A Runners.");
30261
30283
  const host = normalizeHost(await ask("Notiamo address: ", DEFAULT_HOST));
30262
30284
  const hostname3 = deps.hostname ?? os2.hostname();
@@ -30264,6 +30286,12 @@ async function runInit(deps) {
30264
30286
  const version2 = await deps.exec("claude", ["--version"], { env });
30265
30287
  if (version2.code !== 0) throw new Error("Claude Code is not installed. Install it first: https://claude.com/claude-code");
30266
30288
  const engineVersion = version2.stdout.trim().slice(0, 100) || "unknown";
30289
+ try {
30290
+ await new NotiamoApi({ host, token }, deps.fetch).hello({ version: VERSION, os: platform, engine: "claude", engineVersion });
30291
+ } catch (e) {
30292
+ if (e instanceof ApiError && e.status === 401) throw new Error("The runner token was not accepted.");
30293
+ throw new Error(`Notiamo at ${host} did not answer: ${e instanceof Error ? e.message : String(e)}`);
30294
+ }
30267
30295
  const claudeEnv = { ...env, CLAUDE_CONFIG_DIR: p.claudeConfigDir };
30268
30296
  if (!deps.skipLogin) {
30269
30297
  print("Sign Claude Code in for the runner (its own sign-in, kept apart from yours):");
@@ -30271,15 +30299,10 @@ async function runInit(deps) {
30271
30299
  }
30272
30300
  const check2 = await deps.exec("claude", ["-p", "say ok"], { env: claudeEnv });
30273
30301
  if (check2.code !== 0) throw new Error("Claude Code is not signed in for the runner.");
30274
- try {
30275
- await new NotiamoApi({ host, token }, deps.fetch).hello({ version: VERSION, os: platform, engine: "claude", engineVersion });
30276
- } catch (e) {
30277
- if (e instanceof ApiError && e.status === 401) throw new Error("The runner token was not accepted.");
30278
- throw new Error(`Notiamo at ${host} did not answer: ${e instanceof Error ? e.message : String(e)}`);
30279
- }
30280
30302
  saveConfig({ host, token, name, dataDir: dir, engine: "claude" }, dir);
30281
30303
  await installService({
30282
30304
  platform,
30305
+ profile: deps.profile,
30283
30306
  paths: p,
30284
30307
  exec: deps.exec,
30285
30308
  home: deps.home ?? os2.homedir(),
@@ -30287,7 +30310,7 @@ async function runInit(deps) {
30287
30310
  nodePath: deps.nodePath ?? process.execPath,
30288
30311
  cliPath: deps.cliPath ?? process.argv[1]
30289
30312
  });
30290
- print(`Runner ${name} is installed and running. Logs: ${p.log}`);
30313
+ print(`Runner ${name}${deps.profile ? ` (profile ${deps.profile})` : ""} is installed and running. Logs: ${p.log}`);
30291
30314
  }
30292
30315
 
30293
30316
  // src/log.ts
@@ -39948,8 +39971,8 @@ var error3 = () => {
39948
39971
  const sizing = getSizing(issue2.origin);
39949
39972
  if (sizing) {
39950
39973
  const maxValue = Number(issue2.maximum);
39951
- const unit = getBelarusianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
39952
- return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u0432\u044F\u043B\u0456\u043A\u0456: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435"} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue2.maximum.toString()} ${unit}`;
39974
+ const unit2 = getBelarusianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
39975
+ return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u0432\u044F\u043B\u0456\u043A\u0456: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435"} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue2.maximum.toString()} ${unit2}`;
39953
39976
  }
39954
39977
  return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u0432\u044F\u043B\u0456\u043A\u0456: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435"} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 \u0431\u044B\u0446\u044C ${adj}${issue2.maximum.toString()}`;
39955
39978
  }
@@ -39958,8 +39981,8 @@ var error3 = () => {
39958
39981
  const sizing = getSizing(issue2.origin);
39959
39982
  if (sizing) {
39960
39983
  const minValue = Number(issue2.minimum);
39961
- const unit = getBelarusianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
39962
- return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u043C\u0430\u043B\u044B: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue2.minimum.toString()} ${unit}`;
39984
+ const unit2 = getBelarusianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
39985
+ return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u043C\u0430\u043B\u044B: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue2.minimum.toString()} ${unit2}`;
39963
39986
  }
39964
39987
  return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u043C\u0430\u043B\u044B: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 \u0431\u044B\u0446\u044C ${adj}${issue2.minimum.toString()}`;
39965
39988
  }
@@ -42586,8 +42609,8 @@ var error24 = () => {
42586
42609
  const sizing = getSizing(issue2.origin);
42587
42610
  if (sizing) {
42588
42611
  const maxValue = Number(issue2.maximum);
42589
- const unit = getArmenianPlural(maxValue, sizing.unit.one, sizing.unit.many);
42590
- return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0574\u0565\u056E \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin ?? "\u0561\u0580\u056A\u0565\u0584")} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue2.maximum.toString()} ${unit}`;
42612
+ const unit2 = getArmenianPlural(maxValue, sizing.unit.one, sizing.unit.many);
42613
+ return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0574\u0565\u056E \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin ?? "\u0561\u0580\u056A\u0565\u0584")} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue2.maximum.toString()} ${unit2}`;
42591
42614
  }
42592
42615
  return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0574\u0565\u056E \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin ?? "\u0561\u0580\u056A\u0565\u0584")} \u056C\u056B\u0576\u056B ${adj}${issue2.maximum.toString()}`;
42593
42616
  }
@@ -42596,8 +42619,8 @@ var error24 = () => {
42596
42619
  const sizing = getSizing(issue2.origin);
42597
42620
  if (sizing) {
42598
42621
  const minValue = Number(issue2.minimum);
42599
- const unit = getArmenianPlural(minValue, sizing.unit.one, sizing.unit.many);
42600
- return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0583\u0578\u0584\u0580 \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin)} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue2.minimum.toString()} ${unit}`;
42622
+ const unit2 = getArmenianPlural(minValue, sizing.unit.one, sizing.unit.many);
42623
+ return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0583\u0578\u0584\u0580 \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin)} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue2.minimum.toString()} ${unit2}`;
42601
42624
  }
42602
42625
  return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0583\u0578\u0584\u0580 \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin)} \u056C\u056B\u0576\u056B ${adj}${issue2.minimum.toString()}`;
42603
42626
  }
@@ -43511,18 +43534,18 @@ var error32 = () => {
43511
43534
  const adj = issue2.inclusive ? "\uC774\uD558" : "\uBBF8\uB9CC";
43512
43535
  const suffix = adj === "\uBBF8\uB9CC" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
43513
43536
  const sizing = getSizing(issue2.origin);
43514
- const unit = sizing?.unit ?? "\uC694\uC18C";
43537
+ const unit2 = sizing?.unit ?? "\uC694\uC18C";
43515
43538
  if (sizing)
43516
- return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()}${unit} ${adj}${suffix}`;
43539
+ return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()}${unit2} ${adj}${suffix}`;
43517
43540
  return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()} ${adj}${suffix}`;
43518
43541
  }
43519
43542
  case "too_small": {
43520
43543
  const adj = issue2.inclusive ? "\uC774\uC0C1" : "\uCD08\uACFC";
43521
43544
  const suffix = adj === "\uC774\uC0C1" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
43522
43545
  const sizing = getSizing(issue2.origin);
43523
- const unit = sizing?.unit ?? "\uC694\uC18C";
43546
+ const unit2 = sizing?.unit ?? "\uC694\uC18C";
43524
43547
  if (sizing) {
43525
- return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()}${unit} ${adj}${suffix}`;
43548
+ return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()}${unit2} ${adj}${suffix}`;
43526
43549
  }
43527
43550
  return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()} ${adj}${suffix}`;
43528
43551
  }
@@ -45335,8 +45358,8 @@ var error46 = () => {
45335
45358
  const sizing = getSizing(issue2.origin);
45336
45359
  if (sizing) {
45337
45360
  const maxValue = Number(issue2.maximum);
45338
- const unit = getRussianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
45339
- return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue2.maximum.toString()} ${unit}`;
45361
+ const unit2 = getRussianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
45362
+ return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue2.maximum.toString()} ${unit2}`;
45340
45363
  }
45341
45364
  return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"} \u0431\u0443\u0434\u0435\u0442 ${adj}${issue2.maximum.toString()}`;
45342
45365
  }
@@ -45345,8 +45368,8 @@ var error46 = () => {
45345
45368
  const sizing = getSizing(issue2.origin);
45346
45369
  if (sizing) {
45347
45370
  const minValue = Number(issue2.minimum);
45348
- const unit = getRussianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
45349
- return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue2.minimum.toString()} ${unit}`;
45371
+ const unit2 = getRussianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
45372
+ return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue2.minimum.toString()} ${unit2}`;
45350
45373
  }
45351
45374
  return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin} \u0431\u0443\u0434\u0435\u0442 ${adj}${issue2.minimum.toString()}`;
45352
45375
  }
@@ -47432,9 +47455,9 @@ function codePointLengthVar(doc, ctx, accessor, inDoubt) {
47432
47455
  doc.write(`const ${v} = typeof ${accessor} === "string" && ${inDoubt} ? ${cpLen}(${accessor}) : ${accessor}.length;`);
47433
47456
  return v;
47434
47457
  }
47435
- function numericOperand(value, label) {
47458
+ function numericOperand(value, label2) {
47436
47459
  if (typeof value !== "number" || !Number.isFinite(value)) {
47437
- throw new ZodCompileUnsupportedError(`${label} bound of type ${typeof value}`);
47460
+ throw new ZodCompileUnsupportedError(`${label2} bound of type ${typeof value}`);
47438
47461
  }
47439
47462
  return `${value}`;
47440
47463
  }
@@ -57311,12 +57334,12 @@ var Protocol = class {
57311
57334
  this._taskMessageQueue = _options?.taskMessageQueue;
57312
57335
  if (this._taskStore) {
57313
57336
  this.setRequestHandler(GetTaskRequestSchema, async (request, extra) => {
57314
- const task = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
57315
- if (!task) {
57337
+ const task2 = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
57338
+ if (!task2) {
57316
57339
  throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
57317
57340
  }
57318
57341
  return {
57319
- ...task
57342
+ ...task2
57320
57343
  };
57321
57344
  });
57322
57345
  this.setRequestHandler(GetTaskPayloadRequestSchema, async (request, extra) => {
@@ -57350,15 +57373,15 @@ var Protocol = class {
57350
57373
  await this._transport?.send(queuedMessage.message, { relatedRequestId: extra.requestId });
57351
57374
  }
57352
57375
  }
57353
- const task = await this._taskStore.getTask(taskId, extra.sessionId);
57354
- if (!task) {
57376
+ const task2 = await this._taskStore.getTask(taskId, extra.sessionId);
57377
+ if (!task2) {
57355
57378
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
57356
57379
  }
57357
- if (!isTerminal(task.status)) {
57380
+ if (!isTerminal(task2.status)) {
57358
57381
  await this._waitForTaskUpdate(taskId, extra.signal, extra.sessionId);
57359
57382
  return await handleTaskResult();
57360
57383
  }
57361
- if (isTerminal(task.status)) {
57384
+ if (isTerminal(task2.status)) {
57362
57385
  const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
57363
57386
  this._clearTaskQueue(taskId, extra.sessionId);
57364
57387
  return {
@@ -57389,12 +57412,12 @@ var Protocol = class {
57389
57412
  });
57390
57413
  this.setRequestHandler(CancelTaskRequestSchema, async (request, extra) => {
57391
57414
  try {
57392
- const task = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
57393
- if (!task) {
57415
+ const task2 = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
57416
+ if (!task2) {
57394
57417
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${request.params.taskId}`);
57395
57418
  }
57396
- if (isTerminal(task.status)) {
57397
- throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
57419
+ if (isTerminal(task2.status)) {
57420
+ throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task2.status}`);
57398
57421
  }
57399
57422
  await this._taskStore.updateTaskStatus(request.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
57400
57423
  this._clearTaskQueue(request.params.taskId, extra.sessionId);
@@ -57701,10 +57724,10 @@ var Protocol = class {
57701
57724
  if (isJSONRPCResultResponse(response) && response.result && typeof response.result === "object") {
57702
57725
  const result = response.result;
57703
57726
  if (result.task && typeof result.task === "object") {
57704
- const task = result.task;
57705
- if (typeof task.taskId === "string") {
57727
+ const task2 = result.task;
57728
+ if (typeof task2.taskId === "string") {
57706
57729
  isTaskResponse = true;
57707
- this._taskProgressTokens.set(task.taskId, messageId);
57730
+ this._taskProgressTokens.set(task2.taskId, messageId);
57708
57731
  }
57709
57732
  }
57710
57733
  }
@@ -57755,8 +57778,8 @@ var Protocol = class {
57755
57778
  * @experimental Use `client.experimental.tasks.requestStream()` to access this method.
57756
57779
  */
57757
57780
  async *requestStream(request, resultSchema, options) {
57758
- const { task } = options ?? {};
57759
- if (!task) {
57781
+ const { task: task2 } = options ?? {};
57782
+ if (!task2) {
57760
57783
  try {
57761
57784
  const result = await this.request(request, resultSchema, options);
57762
57785
  yield { type: "result", result };
@@ -57778,18 +57801,18 @@ var Protocol = class {
57778
57801
  throw new McpError(ErrorCode.InternalError, "Task creation did not return a task");
57779
57802
  }
57780
57803
  while (true) {
57781
- const task2 = await this.getTask({ taskId }, options);
57782
- yield { type: "taskStatus", task: task2 };
57783
- if (isTerminal(task2.status)) {
57784
- if (task2.status === "completed") {
57804
+ const task3 = await this.getTask({ taskId }, options);
57805
+ yield { type: "taskStatus", task: task3 };
57806
+ if (isTerminal(task3.status)) {
57807
+ if (task3.status === "completed") {
57785
57808
  const result = await this.getTaskResult({ taskId }, resultSchema, options);
57786
57809
  yield { type: "result", result };
57787
- } else if (task2.status === "failed") {
57810
+ } else if (task3.status === "failed") {
57788
57811
  yield {
57789
57812
  type: "error",
57790
57813
  error: new McpError(ErrorCode.InternalError, `Task ${taskId} failed`)
57791
57814
  };
57792
- } else if (task2.status === "cancelled") {
57815
+ } else if (task3.status === "cancelled") {
57793
57816
  yield {
57794
57817
  type: "error",
57795
57818
  error: new McpError(ErrorCode.InternalError, `Task ${taskId} was cancelled`)
@@ -57797,12 +57820,12 @@ var Protocol = class {
57797
57820
  }
57798
57821
  return;
57799
57822
  }
57800
- if (task2.status === "input_required") {
57823
+ if (task3.status === "input_required") {
57801
57824
  const result = await this.getTaskResult({ taskId }, resultSchema, options);
57802
57825
  yield { type: "result", result };
57803
57826
  return;
57804
57827
  }
57805
- const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
57828
+ const pollInterval = task3.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
57806
57829
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
57807
57830
  options?.signal?.throwIfAborted();
57808
57831
  }
@@ -57819,7 +57842,7 @@ var Protocol = class {
57819
57842
  * Do not use this method to emit notifications! Use notification() instead.
57820
57843
  */
57821
57844
  request(request, resultSchema, options) {
57822
- const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
57845
+ const { relatedRequestId, resumptionToken, onresumptiontoken, task: task2, relatedTask } = options ?? {};
57823
57846
  return new Promise((resolve, reject) => {
57824
57847
  const earlyReject = (error62) => {
57825
57848
  reject(error62);
@@ -57831,7 +57854,7 @@ var Protocol = class {
57831
57854
  if (this._options?.enforceStrictCapabilities === true) {
57832
57855
  try {
57833
57856
  this.assertCapabilityForMethod(request.method);
57834
- if (task) {
57857
+ if (task2) {
57835
57858
  this.assertTaskCapability(request.method);
57836
57859
  }
57837
57860
  } catch (e) {
@@ -57856,10 +57879,10 @@ var Protocol = class {
57856
57879
  }
57857
57880
  };
57858
57881
  }
57859
- if (task) {
57882
+ if (task2) {
57860
57883
  jsonrpcRequest.params = {
57861
57884
  ...jsonrpcRequest.params,
57862
- task
57885
+ task: task2
57863
57886
  };
57864
57887
  }
57865
57888
  if (relatedTask) {
@@ -58154,9 +58177,9 @@ var Protocol = class {
58154
58177
  async _waitForTaskUpdate(taskId, signal, sessionId) {
58155
58178
  let interval = this._options?.defaultTaskPollInterval ?? 1e3;
58156
58179
  try {
58157
- const task = await this._taskStore?.getTask(taskId, sessionId);
58158
- if (task?.pollInterval) {
58159
- interval = task.pollInterval;
58180
+ const task2 = await this._taskStore?.getTask(taskId, sessionId);
58181
+ if (task2?.pollInterval) {
58182
+ interval = task2.pollInterval;
58160
58183
  }
58161
58184
  } catch {
58162
58185
  }
@@ -58188,22 +58211,22 @@ var Protocol = class {
58188
58211
  }, sessionId);
58189
58212
  },
58190
58213
  getTask: async (taskId) => {
58191
- const task = await taskStore.getTask(taskId, sessionId);
58192
- if (!task) {
58214
+ const task2 = await taskStore.getTask(taskId, sessionId);
58215
+ if (!task2) {
58193
58216
  throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
58194
58217
  }
58195
- return task;
58218
+ return task2;
58196
58219
  },
58197
58220
  storeTaskResult: async (taskId, status5, result) => {
58198
58221
  await taskStore.storeTaskResult(taskId, status5, result, sessionId);
58199
- const task = await taskStore.getTask(taskId, sessionId);
58200
- if (task) {
58222
+ const task2 = await taskStore.getTask(taskId, sessionId);
58223
+ if (task2) {
58201
58224
  const notification = TaskStatusNotificationSchema.parse({
58202
58225
  method: "notifications/tasks/status",
58203
- params: task
58226
+ params: task2
58204
58227
  });
58205
58228
  await this.notification(notification);
58206
- if (isTerminal(task.status)) {
58229
+ if (isTerminal(task2.status)) {
58207
58230
  this._cleanupTaskProgressHandler(taskId);
58208
58231
  }
58209
58232
  }
@@ -58212,12 +58235,12 @@ var Protocol = class {
58212
58235
  return taskStore.getTaskResult(taskId, sessionId);
58213
58236
  },
58214
58237
  updateTaskStatus: async (taskId, status5, statusMessage) => {
58215
- const task = await taskStore.getTask(taskId, sessionId);
58216
- if (!task) {
58238
+ const task2 = await taskStore.getTask(taskId, sessionId);
58239
+ if (!task2) {
58217
58240
  throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
58218
58241
  }
58219
- if (isTerminal(task.status)) {
58220
- throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status5}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
58242
+ if (isTerminal(task2.status)) {
58243
+ throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task2.status}" to "${status5}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
58221
58244
  }
58222
58245
  await taskStore.updateTaskStatus(taskId, status5, statusMessage, sessionId);
58223
58246
  const updatedTask = await taskStore.getTask(taskId, sessionId);
@@ -59293,15 +59316,15 @@ var McpServer = class {
59293
59316
  await Promise.resolve(handler.createTask(taskExtra))
59294
59317
  );
59295
59318
  const taskId = createTaskResult.task.taskId;
59296
- let task = createTaskResult.task;
59297
- const pollInterval = task.pollInterval ?? 5e3;
59298
- while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
59319
+ let task2 = createTaskResult.task;
59320
+ const pollInterval = task2.pollInterval ?? 5e3;
59321
+ while (task2.status !== "completed" && task2.status !== "failed" && task2.status !== "cancelled") {
59299
59322
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
59300
59323
  const updatedTask = await extra.taskStore.getTask(taskId);
59301
59324
  if (!updatedTask) {
59302
59325
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
59303
59326
  }
59304
- task = updatedTask;
59327
+ task2 = updatedTask;
59305
59328
  }
59306
59329
  return await extra.taskStore.getTaskResult(taskId);
59307
59330
  }
@@ -62045,23 +62068,23 @@ function isAllowed(args, allow) {
62045
62068
  const [cmd, sub] = words;
62046
62069
  if (!cmd) return { ok: false, reason: "no command" };
62047
62070
  if (allow.all) return { ok: true };
62048
- const label = sub ? `wp ${cmd} ${sub}` : `wp ${cmd}`;
62049
- if (NEVER2.some((r) => matches(r, cmd, sub))) return { ok: false, reason: `${label} is never allowed unless "Allow everything" is on` };
62071
+ const label2 = sub ? `wp ${cmd} ${sub}` : `wp ${cmd}`;
62072
+ if (NEVER2.some((r) => matches(r, cmd, sub))) return { ok: false, reason: `${label2} is never allowed unless "Allow everything" is on` };
62050
62073
  const rule = GROUPS.find((r) => matches([r[0], r[1]], cmd, sub));
62051
- if (!rule) return { ok: false, reason: `${label} is not in the allowed groups` };
62052
- if (!allow[rule[2]]) return { ok: false, reason: `${label} needs the ${rule[2]} group, which is off for this project` };
62074
+ if (!rule) return { ok: false, reason: `${label2} is not in the allowed groups` };
62075
+ if (!allow[rule[2]]) return { ok: false, reason: `${label2} needs the ${rule[2]} group, which is off for this project` };
62053
62076
  const rest = words.slice(2);
62054
62077
  if (cmd === "post" && sub !== "meta" && sub !== "term") {
62055
62078
  const bad = rest.find((w) => !/^\d+$/.test(w));
62056
- if (bad !== void 0) return { ok: false, reason: `${label} takes post ids only, not "${bad}" (a file name is read from the server): refused unless "Allow everything" is on` };
62079
+ if (bad !== void 0) return { ok: false, reason: `${label2} takes post ids only, not "${bad}" (a file name is read from the server): refused unless "Allow everything" is on` };
62057
62080
  }
62058
62081
  if (cmd === "media") {
62059
62082
  const bad = rest.find((w) => !/^https?:\/\//i.test(w));
62060
- if (bad !== void 0) return { ok: false, reason: `${label} takes http(s) URLs only, not "${bad}": refused unless "Allow everything" is on` };
62083
+ if (bad !== void 0) return { ok: false, reason: `${label2} takes http(s) URLs only, not "${bad}": refused unless "Allow everything" is on` };
62061
62084
  }
62062
62085
  if (cmd === "option") {
62063
62086
  const name = rest[0];
62064
- if (!name || !OPTIONS.has(name)) return { ok: false, reason: `${label} ${name ?? ""} is refused: only ${[...OPTIONS].join(", ")} unless "Allow everything" is on` };
62087
+ if (!name || !OPTIONS.has(name)) return { ok: false, reason: `${label2} ${name ?? ""} is refused: only ${[...OPTIONS].join(", ")} unless "Allow everything" is on` };
62065
62088
  }
62066
62089
  return { ok: true };
62067
62090
  }
@@ -62204,7 +62227,7 @@ function buildServer(t) {
62204
62227
  if (!verdict.ok) return fail(`refused: ${verdict.reason}`);
62205
62228
  if (t.root === null) return fail("wordpress_root_not_found: the WordPress folder was not found on the server");
62206
62229
  const wp = (extra) => run2(["wp", ...args, ...extra, `--path=${t.root}`, "--no-color"]);
62207
- const show = (label, r2) => `${label}exit ${r2.code}
62230
+ const show = (label2, r2) => `${label2}exit ${r2.code}
62208
62231
  ${r2.stdout.toString("utf8")}${r2.stderr ? `
62209
62232
  ${r2.stderr}` : ""}`;
62210
62233
  t.log.info(`helper wp_run ${scrub(args.slice(0, 2).join(" "), t.secrets)}`);
@@ -62659,9 +62682,10 @@ function clearState(file2) {
62659
62682
 
62660
62683
  // src/commands.ts
62661
62684
  var HARD_STOP_MS = 3e4;
62662
- var serviceOptions = () => ({
62685
+ var serviceOptions = (profile) => ({
62663
62686
  platform: process.platform,
62664
- paths: paths(defaultDir()),
62687
+ profile,
62688
+ paths: paths(profileDir(profile)),
62665
62689
  exec: realExec,
62666
62690
  home: os3.homedir(),
62667
62691
  env: process.env,
@@ -62675,20 +62699,24 @@ async function prompt(q2, opts = {}) {
62675
62699
  cb();
62676
62700
  } });
62677
62701
  const rl = createInterface({ input: process.stdin, output: output2, terminal: process.stdin.isTTY === true });
62702
+ const text = opts.default ? `${q2.replace(/:\s*$/, "")} [${opts.default}]: ` : q2;
62678
62703
  try {
62679
- process.stdout.write(opts.default ? `${q2.replace(/:\s*$/, "")} [${opts.default}]: ` : q2);
62680
- muted = opts.hidden === true;
62681
- return await rl.question("");
62704
+ if (opts.hidden) {
62705
+ process.stdout.write(text);
62706
+ muted = true;
62707
+ return await rl.question("");
62708
+ }
62709
+ return await rl.question(text);
62682
62710
  } finally {
62683
62711
  if (muted) process.stdout.write("\n");
62684
62712
  rl.close();
62685
62713
  }
62686
62714
  }
62687
- async function init() {
62688
- await runInit({ prompt, exec: realExec, fetch });
62715
+ async function init({ profile } = {}) {
62716
+ await runInit({ prompt, exec: realExec, fetch, profile });
62689
62717
  }
62690
- async function start() {
62691
- const dir = defaultDir();
62718
+ async function start({ profile } = {}) {
62719
+ const dir = profileDir(profile);
62692
62720
  const cfg = loadConfig(dir);
62693
62721
  const p = paths(dir);
62694
62722
  const log = createLogger(p.log);
@@ -62721,8 +62749,8 @@ async function start() {
62721
62749
  });
62722
62750
  log.info("stopped");
62723
62751
  }
62724
- async function stop4() {
62725
- const o = serviceOptions();
62752
+ async function stop4({ profile } = {}) {
62753
+ const o = serviceOptions(profile);
62726
62754
  if (await serviceStatus(o) === "not_installed") {
62727
62755
  console.log("The runner service is not installed.");
62728
62756
  return;
@@ -62730,25 +62758,26 @@ async function stop4() {
62730
62758
  await stopService(o);
62731
62759
  console.log("Stopped. The runner starts again at the next login or boot; `notiamo-runner uninstall` removes the service.");
62732
62760
  }
62733
- async function uninstall4() {
62734
- await uninstallService(serviceOptions());
62735
- console.log(`The runner service is removed. Its folder ${defaultDir()} (config, sign-in, projects) stays; delete it to remove everything.`);
62761
+ async function uninstall4({ profile } = {}) {
62762
+ await uninstallService(serviceOptions(profile));
62763
+ console.log(`The runner service is removed. Its folder ${profileDir(profile)} (config, sign-in, projects) stays; delete it to remove everything.`);
62736
62764
  }
62737
- async function status4() {
62738
- const st = await serviceStatus(serviceOptions());
62765
+ async function status4({ profile } = {}) {
62766
+ const st = await serviceStatus(serviceOptions(profile));
62739
62767
  let cfg = null;
62740
62768
  try {
62741
- cfg = loadConfig();
62769
+ cfg = loadConfig(profileDir(profile));
62742
62770
  } catch {
62743
62771
  }
62772
+ if (profile) console.log(`profile: ${profile}`);
62744
62773
  console.log(`installed: ${st === "not_installed" ? "no" : "yes"}`);
62745
62774
  console.log(`running: ${st === "running" ? "yes" : "no"}`);
62746
62775
  console.log(`name: ${cfg?.name ?? "-"}`);
62747
62776
  console.log(`host: ${cfg?.host ?? "-"}`);
62748
- console.log(`last log line: ${tail(paths(defaultDir()).log, 1)[0] ?? "-"}`);
62777
+ console.log(`last log line: ${tail(paths(profileDir(profile)).log, 1)[0] ?? "-"}`);
62749
62778
  }
62750
- function logs() {
62751
- const lines2 = tail(paths(defaultDir()).log, 50);
62779
+ function logs({ profile } = {}) {
62780
+ const lines2 = tail(paths(profileDir(profile)).log, 50);
62752
62781
  console.log(lines2.length ? lines2.join("\n") : "No log yet.");
62753
62782
  }
62754
62783
  function tail(file2, n) {
@@ -62764,7 +62793,7 @@ function tail(file2, n) {
62764
62793
  // src/cli.ts
62765
62794
  var HELP = `notiamo-runner ${VERSION}: runs Notiamo agent runs on this machine with your own Claude Code.
62766
62795
 
62767
- Usage: notiamo-runner <command>
62796
+ Usage: notiamo-runner <command> [--profile <name>]
62768
62797
 
62769
62798
  init Set the runner up: token, Claude Code sign-in, start at login or boot
62770
62799
  start Run in the foreground (what the service runs)
@@ -62772,21 +62801,33 @@ Usage: notiamo-runner <command>
62772
62801
  status Whether the service is installed and running, and the last log line
62773
62802
  logs The last 50 lines of the log
62774
62803
  uninstall Remove the service (the runner's folder stays)
62775
- --version Print the version`;
62776
- async function main(cmd) {
62804
+ --version Print the version
62805
+
62806
+ --profile <name> A second runner on this machine (say "staging"): its own folder, sign-in and service.
62807
+ Give it to every command of that runner.`;
62808
+ async function main(argv) {
62809
+ const { cmd, profile, error: error62 } = parseArgs(argv);
62810
+ if (error62) {
62811
+ console.error(`${error62}
62812
+
62813
+ ${HELP}`);
62814
+ process.exitCode = 1;
62815
+ return;
62816
+ }
62817
+ const o = { profile };
62777
62818
  switch (cmd) {
62778
62819
  case "init":
62779
- return init();
62820
+ return init(o);
62780
62821
  case "start":
62781
- return start();
62822
+ return start(o);
62782
62823
  case "stop":
62783
- return stop4();
62824
+ return stop4(o);
62784
62825
  case "status":
62785
- return status4();
62826
+ return status4(o);
62786
62827
  case "logs":
62787
- return logs();
62828
+ return logs(o);
62788
62829
  case "uninstall":
62789
- return uninstall4();
62830
+ return uninstall4(o);
62790
62831
  case "--version":
62791
62832
  case "-v":
62792
62833
  console.log(`notiamo-runner ${VERSION}`);
@@ -62804,7 +62845,7 @@ ${HELP}`);
62804
62845
  process.exitCode = 1;
62805
62846
  }
62806
62847
  }
62807
- main(process.argv[2]).catch((e) => {
62848
+ main(process.argv.slice(2)).catch((e) => {
62808
62849
  console.error(e instanceof Error ? e.message : String(e));
62809
62850
  process.exitCode = 1;
62810
62851
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notiamo/runner",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Runs Notiamo agent runs on your own machine with the Claude Code you are already signed in to.",
5
5
  "type": "module",
6
6
  "license": "MIT",