@notiamo/runner 0.1.1 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +87 -6
  2. package/dist/cli.js +1342 -467
  3. package/package.json +1 -1
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
  }
@@ -1045,8 +1045,8 @@ var require_safePathNames = __commonJS({
1045
1045
  })();
1046
1046
  Object.defineProperty(exports, "__esModule", { value: true });
1047
1047
  exports.isSafePathName = isSafePathName;
1048
- var path11 = __importStar(__require("path"));
1049
- function isSafePathName(name, localPath = path11) {
1048
+ var path13 = __importStar(__require("path"));
1049
+ function isSafePathName(name, localPath = path13) {
1050
1050
  if (!name || name === "." || name === ".." || name.includes("/")) {
1051
1051
  return false;
1052
1052
  }
@@ -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
  }
@@ -1704,8 +1704,8 @@ var require_Client = __commonJS({
1704
1704
  /**
1705
1705
  * Set the working directory.
1706
1706
  */
1707
- async cd(path11) {
1708
- const validPath = await this.protectWhitespace(path11);
1707
+ async cd(path13) {
1708
+ const validPath = await this.protectWhitespace(path13);
1709
1709
  return this.send("CWD " + validPath);
1710
1710
  }
1711
1711
  /**
@@ -1718,8 +1718,8 @@ var require_Client = __commonJS({
1718
1718
  * Get the last modified time of a file. This is not supported by every FTP server, in which case
1719
1719
  * calling this method will throw an exception.
1720
1720
  */
1721
- async lastMod(path11) {
1722
- const validPath = await this.protectWhitespace(path11);
1721
+ async lastMod(path13) {
1722
+ const validPath = await this.protectWhitespace(path13);
1723
1723
  const res = await this.send(`MDTM ${validPath}`);
1724
1724
  const date5 = res.message.slice(4);
1725
1725
  return (0, parseListMLSD_1.parseMLSxDate)(date5);
@@ -1727,8 +1727,8 @@ var require_Client = __commonJS({
1727
1727
  /**
1728
1728
  * Get the size of a file.
1729
1729
  */
1730
- async size(path11) {
1731
- const validPath = await this.protectWhitespace(path11);
1730
+ async size(path13) {
1731
+ const validPath = await this.protectWhitespace(path13);
1732
1732
  const command = `SIZE ${validPath}`;
1733
1733
  const res = await this.send(command);
1734
1734
  const size = parseInt(res.message.slice(4), 10);
@@ -1755,8 +1755,8 @@ var require_Client = __commonJS({
1755
1755
  * You can ignore FTP error return codes which won't throw an exception if e.g.
1756
1756
  * the file doesn't exist.
1757
1757
  */
1758
- async remove(path11, ignoreErrorCodes = false) {
1759
- const validPath = await this.protectWhitespace(path11);
1758
+ async remove(path13, ignoreErrorCodes = false) {
1759
+ const validPath = await this.protectWhitespace(path13);
1760
1760
  if (ignoreErrorCodes) {
1761
1761
  return this.sendIgnoringError(`DELE ${validPath}`);
1762
1762
  }
@@ -1915,8 +1915,8 @@ var require_Client = __commonJS({
1915
1915
  *
1916
1916
  * @param [path] Path to remote file or directory.
1917
1917
  */
1918
- async list(path11 = "") {
1919
- const validPath = await this.protectWhitespace(path11);
1918
+ async list(path13 = "") {
1919
+ const validPath = await this.protectWhitespace(path13);
1920
1920
  let lastError;
1921
1921
  for (const candidate of this.availableListCommands) {
1922
1922
  const command = validPath === "" ? candidate : `${candidate} ${validPath}`;
@@ -2085,21 +2085,21 @@ var require_Client = __commonJS({
2085
2085
  /**
2086
2086
  * Remove an empty directory, will fail if not empty.
2087
2087
  */
2088
- async removeEmptyDir(path11) {
2089
- const validPath = await this.protectWhitespace(path11);
2088
+ async removeEmptyDir(path13) {
2089
+ const validPath = await this.protectWhitespace(path13);
2090
2090
  return this.send(`RMD ${validPath}`);
2091
2091
  }
2092
2092
  /**
2093
2093
  * FTP servers can't handle filenames that have leading whitespace. This method transforms
2094
2094
  * a given path to fix that issue for most cases.
2095
2095
  */
2096
- async protectWhitespace(path11) {
2097
- if (!path11.startsWith(" ")) {
2098
- return path11;
2096
+ async protectWhitespace(path13) {
2097
+ if (!path13.startsWith(" ")) {
2098
+ return path13;
2099
2099
  }
2100
2100
  const pwd = await this.pwd();
2101
2101
  const absolutePathPrefix = pwd.endsWith("/") ? pwd : pwd + "/";
2102
- return absolutePathPrefix + path11;
2102
+ return absolutePathPrefix + path13;
2103
2103
  }
2104
2104
  async _exitAtCurrentDirectory(func) {
2105
2105
  const userDir = await this.pwd();
@@ -2176,11 +2176,11 @@ var require_Client = __commonJS({
2176
2176
  }
2177
2177
  };
2178
2178
  exports.Client = Client3;
2179
- async function ensureLocalDirectory(path11) {
2179
+ async function ensureLocalDirectory(path13) {
2180
2180
  try {
2181
- await fsStat(path11);
2181
+ await fsStat(path13);
2182
2182
  } catch (_a3) {
2183
- await fsMkDir(path11, { recursive: true });
2183
+ await fsMkDir(path13, { recursive: true });
2184
2184
  }
2185
2185
  }
2186
2186
  async function expectedBytesFromLocalFile(fd, options) {
@@ -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) {
@@ -5477,8 +5477,8 @@ var require_utils = __commonJS({
5477
5477
  }
5478
5478
  return ind;
5479
5479
  }
5480
- function removeDotSegments(path11) {
5481
- let input2 = path11;
5480
+ function removeDotSegments(path13) {
5481
+ let input2 = path13;
5482
5482
  const output2 = [];
5483
5483
  let nextSlash = -1;
5484
5484
  let len = 0;
@@ -5887,8 +5887,8 @@ var require_schemes = __commonJS({
5887
5887
  }
5888
5888
  if (wsComponent.resourceName) {
5889
5889
  const queryIndex = wsComponent.resourceName.indexOf("?");
5890
- const path11 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
5891
- wsComponent.path = path11 && path11 !== "/" ? path11 : void 0;
5890
+ const path13 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
5891
+ wsComponent.path = path13 && path13 !== "/" ? path13 : void 0;
5892
5892
  wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
5893
5893
  wsComponent.resourceName = void 0;
5894
5894
  }
@@ -16025,7 +16025,7 @@ var require_keyParser = __commonJS({
16025
16025
  var {
16026
16026
  createDecipheriv,
16027
16027
  createECDH,
16028
- createHash,
16028
+ createHash: createHash2,
16029
16029
  createHmac,
16030
16030
  createSign,
16031
16031
  createVerify,
@@ -16713,11 +16713,11 @@ ${formatted}-----END ${type} KEY-----`;
16713
16713
  );
16714
16714
  }
16715
16715
  const ivSlice = bufferSlice(cipherIV, 0, 8);
16716
- let cipherKey = createHash("md5").update(passphrase).update(ivSlice).digest();
16716
+ let cipherKey = createHash2("md5").update(passphrase).update(ivSlice).digest();
16717
16717
  while (cipherKey.length < encInfo.keyLen) {
16718
16718
  cipherKey = combineBuffers(
16719
16719
  cipherKey,
16720
- createHash("md5").update(cipherKey).update(passphrase).update(ivSlice).digest()
16720
+ createHash2("md5").update(cipherKey).update(passphrase).update(ivSlice).digest()
16721
16721
  );
16722
16722
  }
16723
16723
  if (cipherKey.length > encInfo.keyLen)
@@ -16882,8 +16882,8 @@ ${formatted}-----END ${type} KEY-----`;
16882
16882
  if (encrypted) {
16883
16883
  const encInfo = CIPHER_INFO[cipherName];
16884
16884
  let cipherKey = combineBuffers(
16885
- createHash("sha1").update(PPK_PP1).update(passphrase).digest(),
16886
- createHash("sha1").update(PPK_PP2).update(passphrase).digest()
16885
+ createHash2("sha1").update(PPK_PP1).update(passphrase).digest(),
16886
+ createHash2("sha1").update(PPK_PP2).update(passphrase).digest()
16887
16887
  );
16888
16888
  if (cipherKey.length > encInfo.keyLen)
16889
16889
  cipherKey = bufferSlice(cipherKey, 0, encInfo.keyLen);
@@ -16923,7 +16923,7 @@ ${formatted}-----END ${type} KEY-----`;
16923
16923
  passphrase = EMPTY_PASSPHRASE;
16924
16924
  const calcMAC = createHmac(
16925
16925
  "sha1",
16926
- createHash("sha1").update("putty-private-key-file-mac-key").update(passphrase).digest()
16926
+ createHash2("sha1").update("putty-private-key-file-mac-key").update(passphrase).digest()
16927
16927
  ).update(macData).digest("hex");
16928
16928
  if (calcMAC !== mac3) {
16929
16929
  if (encrypted) {
@@ -17567,11 +17567,11 @@ var require_agent = __commonJS({
17567
17567
  };
17568
17568
  })();
17569
17569
  var WINDOWS_PIPE_REGEX = /^[/\\][/\\]\.[/\\]pipe[/\\].+/;
17570
- function createAgent(path11) {
17571
- if (process.platform === "win32" && !WINDOWS_PIPE_REGEX.test(path11)) {
17572
- return path11 === "pageant" ? new PageantAgent() : new CygwinAgent(path11);
17570
+ function createAgent(path13) {
17571
+ if (process.platform === "win32" && !WINDOWS_PIPE_REGEX.test(path13)) {
17572
+ return path13 === "pageant" ? new PageantAgent() : new CygwinAgent(path13);
17573
17573
  }
17574
- return new OpenSSHAgent(path11);
17574
+ return new OpenSSHAgent(path13);
17575
17575
  }
17576
17576
  var AgentProtocol = (() => {
17577
17577
  const SSH_AGENTC_REQUEST_IDENTITIES = 11;
@@ -19245,7 +19245,7 @@ var require_kex = __commonJS({
19245
19245
  createDiffieHellman,
19246
19246
  createDiffieHellmanGroup,
19247
19247
  createECDH,
19248
- createHash,
19248
+ createHash: createHash2,
19249
19249
  createPublicKey,
19250
19250
  diffieHellman,
19251
19251
  generateKeyPairSync,
@@ -19671,7 +19671,7 @@ var require_kex = __commonJS({
19671
19671
  DISCONNECT_REASON.KEY_EXCHANGE_FAILED
19672
19672
  );
19673
19673
  }
19674
- const hash2 = createHash(this.hashName);
19674
+ const hash2 = createHash2(this.hashName);
19675
19675
  hashString(hash2, isServer ? this._remoteIdentRaw : this._identRaw);
19676
19676
  hashString(hash2, isServer ? this._identRaw : this._remoteIdentRaw);
19677
19677
  hashString(hash2, isServer ? this._remoteKexinit : this._kexinit);
@@ -20679,9 +20679,9 @@ var require_kex = __commonJS({
20679
20679
  function generateKEXVal(len, hashName, secret, exchangeHash, sessionID, char) {
20680
20680
  let ret;
20681
20681
  if (len) {
20682
- let digest = createHash(hashName).update(secret).update(exchangeHash).update(char).update(sessionID).digest();
20682
+ let digest = createHash2(hashName).update(secret).update(exchangeHash).update(char).update(sessionID).digest();
20683
20683
  while (digest.length < len) {
20684
- const chunk = createHash(hashName).update(secret).update(exchangeHash).update(digest).digest();
20684
+ const chunk = createHash2(hashName).update(secret).update(exchangeHash).update(digest).digest();
20685
20685
  const extended2 = Buffer.allocUnsafe(digest.length + chunk.length);
20686
20686
  extended2.set(digest, 0);
20687
20687
  extended2.set(chunk, digest.length);
@@ -22815,17 +22815,17 @@ var require_SFTP = __commonJS({
22815
22815
  // ===========================================================================
22816
22816
  // Client-specific ===========================================================
22817
22817
  // ===========================================================================
22818
- createReadStream(path11, options) {
22818
+ createReadStream(path13, options) {
22819
22819
  if (this.server)
22820
22820
  throw new Error("Client-only method called in server mode");
22821
- return new ReadStream(this, path11, options);
22821
+ return new ReadStream(this, path13, options);
22822
22822
  }
22823
- createWriteStream(path11, options) {
22823
+ createWriteStream(path13, options) {
22824
22824
  if (this.server)
22825
22825
  throw new Error("Client-only method called in server mode");
22826
- return new WriteStream(this, path11, options);
22826
+ return new WriteStream(this, path13, options);
22827
22827
  }
22828
- open(path11, flags_, attrs, cb) {
22828
+ open(path13, flags_, attrs, cb) {
22829
22829
  if (this.server)
22830
22830
  throw new Error("Client-only method called in server mode");
22831
22831
  if (typeof attrs === "function") {
@@ -22844,7 +22844,7 @@ var require_SFTP = __commonJS({
22844
22844
  attrsFlags = attrs.flags;
22845
22845
  attrsLen = attrs.nb;
22846
22846
  }
22847
- const pathLen = Buffer.byteLength(path11);
22847
+ const pathLen = Buffer.byteLength(path13);
22848
22848
  let p = 9;
22849
22849
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen + 4 + 4 + attrsLen);
22850
22850
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -22852,7 +22852,7 @@ var require_SFTP = __commonJS({
22852
22852
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
22853
22853
  writeUInt32BE(buf, reqid, 5);
22854
22854
  writeUInt32BE(buf, pathLen, p);
22855
- buf.utf8Write(path11, p += 4, pathLen);
22855
+ buf.utf8Write(path13, p += 4, pathLen);
22856
22856
  writeUInt32BE(buf, flags, p += pathLen);
22857
22857
  writeUInt32BE(buf, attrsFlags, p += 4);
22858
22858
  if (attrsLen) {
@@ -22983,7 +22983,7 @@ var require_SFTP = __commonJS({
22983
22983
  throw new Error("Client-only method called in server mode");
22984
22984
  fastXfer(fs, this, localPath, remotePath, opts, cb);
22985
22985
  }
22986
- readFile(path11, options, callback_) {
22986
+ readFile(path13, options, callback_) {
22987
22987
  if (this.server)
22988
22988
  throw new Error("Client-only method called in server mode");
22989
22989
  let callback;
@@ -23056,13 +23056,13 @@ var require_SFTP = __commonJS({
23056
23056
  return callback && callback(er, buffer);
23057
23057
  });
23058
23058
  };
23059
- this.open(path11, flag, 438, (er, handle_) => {
23059
+ this.open(path13, flag, 438, (er, handle_) => {
23060
23060
  if (er)
23061
23061
  return callback && callback(er);
23062
23062
  handle = handle_;
23063
23063
  const tryStat = (er2, st) => {
23064
23064
  if (er2) {
23065
- this.stat(path11, (er_, st_) => {
23065
+ this.stat(path13, (er_, st_) => {
23066
23066
  if (er_) {
23067
23067
  return this.close(handle, () => {
23068
23068
  callback && callback(er2);
@@ -23083,7 +23083,7 @@ var require_SFTP = __commonJS({
23083
23083
  this.fstat(handle, tryStat);
23084
23084
  });
23085
23085
  }
23086
- writeFile(path11, data, options, callback_) {
23086
+ writeFile(path13, data, options, callback_) {
23087
23087
  if (this.server)
23088
23088
  throw new Error("Client-only method called in server mode");
23089
23089
  let callback;
@@ -23102,7 +23102,7 @@ var require_SFTP = __commonJS({
23102
23102
  if (options.encoding && !Buffer.isEncoding(options.encoding))
23103
23103
  throw new Error(`Unknown encoding: ${options.encoding}`);
23104
23104
  const flag = options.flag || "w";
23105
- this.open(path11, flag, options.mode, (openErr, handle) => {
23105
+ this.open(path13, flag, options.mode, (openErr, handle) => {
23106
23106
  if (openErr) {
23107
23107
  callback && callback(openErr);
23108
23108
  } else {
@@ -23111,7 +23111,7 @@ var require_SFTP = __commonJS({
23111
23111
  if (position === null) {
23112
23112
  const tryStat = (er, st) => {
23113
23113
  if (er) {
23114
- this.stat(path11, (er_, st_) => {
23114
+ this.stat(path13, (er_, st_) => {
23115
23115
  if (er_) {
23116
23116
  return this.close(handle, () => {
23117
23117
  callback && callback(er);
@@ -23130,7 +23130,7 @@ var require_SFTP = __commonJS({
23130
23130
  }
23131
23131
  });
23132
23132
  }
23133
- appendFile(path11, data, options, callback_) {
23133
+ appendFile(path13, data, options, callback_) {
23134
23134
  if (this.server)
23135
23135
  throw new Error("Client-only method called in server mode");
23136
23136
  let callback;
@@ -23148,12 +23148,12 @@ var require_SFTP = __commonJS({
23148
23148
  throw new TypeError("Bad arguments");
23149
23149
  if (!options.flag)
23150
23150
  options = Object.assign({ flag: "a" }, options);
23151
- this.writeFile(path11, data, options, callback);
23151
+ this.writeFile(path13, data, options, callback);
23152
23152
  }
23153
- exists(path11, cb) {
23153
+ exists(path13, cb) {
23154
23154
  if (this.server)
23155
23155
  throw new Error("Client-only method called in server mode");
23156
- this.stat(path11, (err) => {
23156
+ this.stat(path13, (err) => {
23157
23157
  cb && cb(err ? false : true);
23158
23158
  });
23159
23159
  }
@@ -23196,7 +23196,7 @@ var require_SFTP = __commonJS({
23196
23196
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} RENAME`
23197
23197
  );
23198
23198
  }
23199
- mkdir(path11, attrs, cb) {
23199
+ mkdir(path13, attrs, cb) {
23200
23200
  if (this.server)
23201
23201
  throw new Error("Client-only method called in server mode");
23202
23202
  let flags = 0;
@@ -23210,7 +23210,7 @@ var require_SFTP = __commonJS({
23210
23210
  flags = attrs.flags;
23211
23211
  attrsLen = attrs.nb;
23212
23212
  }
23213
- const pathLen = Buffer.byteLength(path11);
23213
+ const pathLen = Buffer.byteLength(path13);
23214
23214
  let p = 9;
23215
23215
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen + 4 + attrsLen);
23216
23216
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -23218,7 +23218,7 @@ var require_SFTP = __commonJS({
23218
23218
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
23219
23219
  writeUInt32BE(buf, reqid, 5);
23220
23220
  writeUInt32BE(buf, pathLen, p);
23221
- buf.utf8Write(path11, p += 4, pathLen);
23221
+ buf.utf8Write(path13, p += 4, pathLen);
23222
23222
  writeUInt32BE(buf, flags, p += pathLen);
23223
23223
  if (attrsLen) {
23224
23224
  p += 4;
@@ -23234,10 +23234,10 @@ var require_SFTP = __commonJS({
23234
23234
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} MKDIR`
23235
23235
  );
23236
23236
  }
23237
- rmdir(path11, cb) {
23237
+ rmdir(path13, cb) {
23238
23238
  if (this.server)
23239
23239
  throw new Error("Client-only method called in server mode");
23240
- const pathLen = Buffer.byteLength(path11);
23240
+ const pathLen = Buffer.byteLength(path13);
23241
23241
  let p = 9;
23242
23242
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
23243
23243
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -23245,7 +23245,7 @@ var require_SFTP = __commonJS({
23245
23245
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
23246
23246
  writeUInt32BE(buf, reqid, 5);
23247
23247
  writeUInt32BE(buf, pathLen, p);
23248
- buf.utf8Write(path11, p += 4, pathLen);
23248
+ buf.utf8Write(path13, p += 4, pathLen);
23249
23249
  this._requests[reqid] = { cb };
23250
23250
  const isBuffered = sendOrBuffer(this, buf);
23251
23251
  this._debug && this._debug(
@@ -23335,10 +23335,10 @@ var require_SFTP = __commonJS({
23335
23335
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} FSTAT`
23336
23336
  );
23337
23337
  }
23338
- stat(path11, cb) {
23338
+ stat(path13, cb) {
23339
23339
  if (this.server)
23340
23340
  throw new Error("Client-only method called in server mode");
23341
- const pathLen = Buffer.byteLength(path11);
23341
+ const pathLen = Buffer.byteLength(path13);
23342
23342
  let p = 9;
23343
23343
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
23344
23344
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -23346,17 +23346,17 @@ var require_SFTP = __commonJS({
23346
23346
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
23347
23347
  writeUInt32BE(buf, reqid, 5);
23348
23348
  writeUInt32BE(buf, pathLen, p);
23349
- buf.utf8Write(path11, p += 4, pathLen);
23349
+ buf.utf8Write(path13, p += 4, pathLen);
23350
23350
  this._requests[reqid] = { cb };
23351
23351
  const isBuffered = sendOrBuffer(this, buf);
23352
23352
  this._debug && this._debug(
23353
23353
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} STAT`
23354
23354
  );
23355
23355
  }
23356
- lstat(path11, cb) {
23356
+ lstat(path13, cb) {
23357
23357
  if (this.server)
23358
23358
  throw new Error("Client-only method called in server mode");
23359
- const pathLen = Buffer.byteLength(path11);
23359
+ const pathLen = Buffer.byteLength(path13);
23360
23360
  let p = 9;
23361
23361
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
23362
23362
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -23364,17 +23364,17 @@ var require_SFTP = __commonJS({
23364
23364
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
23365
23365
  writeUInt32BE(buf, reqid, 5);
23366
23366
  writeUInt32BE(buf, pathLen, p);
23367
- buf.utf8Write(path11, p += 4, pathLen);
23367
+ buf.utf8Write(path13, p += 4, pathLen);
23368
23368
  this._requests[reqid] = { cb };
23369
23369
  const isBuffered = sendOrBuffer(this, buf);
23370
23370
  this._debug && this._debug(
23371
23371
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} LSTAT`
23372
23372
  );
23373
23373
  }
23374
- opendir(path11, cb) {
23374
+ opendir(path13, cb) {
23375
23375
  if (this.server)
23376
23376
  throw new Error("Client-only method called in server mode");
23377
- const pathLen = Buffer.byteLength(path11);
23377
+ const pathLen = Buffer.byteLength(path13);
23378
23378
  let p = 9;
23379
23379
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
23380
23380
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -23382,14 +23382,14 @@ var require_SFTP = __commonJS({
23382
23382
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
23383
23383
  writeUInt32BE(buf, reqid, 5);
23384
23384
  writeUInt32BE(buf, pathLen, p);
23385
- buf.utf8Write(path11, p += 4, pathLen);
23385
+ buf.utf8Write(path13, p += 4, pathLen);
23386
23386
  this._requests[reqid] = { cb };
23387
23387
  const isBuffered = sendOrBuffer(this, buf);
23388
23388
  this._debug && this._debug(
23389
23389
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} OPENDIR`
23390
23390
  );
23391
23391
  }
23392
- setstat(path11, attrs, cb) {
23392
+ setstat(path13, attrs, cb) {
23393
23393
  if (this.server)
23394
23394
  throw new Error("Client-only method called in server mode");
23395
23395
  let flags = 0;
@@ -23401,7 +23401,7 @@ var require_SFTP = __commonJS({
23401
23401
  } else if (typeof attrs === "function") {
23402
23402
  cb = attrs;
23403
23403
  }
23404
- const pathLen = Buffer.byteLength(path11);
23404
+ const pathLen = Buffer.byteLength(path13);
23405
23405
  let p = 9;
23406
23406
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen + 4 + attrsLen);
23407
23407
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -23409,7 +23409,7 @@ var require_SFTP = __commonJS({
23409
23409
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
23410
23410
  writeUInt32BE(buf, reqid, 5);
23411
23411
  writeUInt32BE(buf, pathLen, p);
23412
- buf.utf8Write(path11, p += 4, pathLen);
23412
+ buf.utf8Write(path13, p += 4, pathLen);
23413
23413
  writeUInt32BE(buf, flags, p += pathLen);
23414
23414
  if (attrsLen) {
23415
23415
  p += 4;
@@ -23469,8 +23469,8 @@ var require_SFTP = __commonJS({
23469
23469
  mtime: toUnixTimestamp(mtime)
23470
23470
  }, cb);
23471
23471
  }
23472
- utimes(path11, atime, mtime, cb) {
23473
- return this.setstat(path11, {
23472
+ utimes(path13, atime, mtime, cb) {
23473
+ return this.setstat(path13, {
23474
23474
  atime: toUnixTimestamp(atime),
23475
23475
  mtime: toUnixTimestamp(mtime)
23476
23476
  }, cb);
@@ -23481,8 +23481,8 @@ var require_SFTP = __commonJS({
23481
23481
  gid
23482
23482
  }, cb);
23483
23483
  }
23484
- chown(path11, uid, gid, cb) {
23485
- return this.setstat(path11, {
23484
+ chown(path13, uid, gid, cb) {
23485
+ return this.setstat(path13, {
23486
23486
  uid,
23487
23487
  gid
23488
23488
  }, cb);
@@ -23492,15 +23492,15 @@ var require_SFTP = __commonJS({
23492
23492
  mode
23493
23493
  }, cb);
23494
23494
  }
23495
- chmod(path11, mode, cb) {
23496
- return this.setstat(path11, {
23495
+ chmod(path13, mode, cb) {
23496
+ return this.setstat(path13, {
23497
23497
  mode
23498
23498
  }, cb);
23499
23499
  }
23500
- readlink(path11, cb) {
23500
+ readlink(path13, cb) {
23501
23501
  if (this.server)
23502
23502
  throw new Error("Client-only method called in server mode");
23503
- const pathLen = Buffer.byteLength(path11);
23503
+ const pathLen = Buffer.byteLength(path13);
23504
23504
  let p = 9;
23505
23505
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
23506
23506
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -23508,7 +23508,7 @@ var require_SFTP = __commonJS({
23508
23508
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
23509
23509
  writeUInt32BE(buf, reqid, 5);
23510
23510
  writeUInt32BE(buf, pathLen, p);
23511
- buf.utf8Write(path11, p += 4, pathLen);
23511
+ buf.utf8Write(path13, p += 4, pathLen);
23512
23512
  this._requests[reqid] = {
23513
23513
  cb: (err, names) => {
23514
23514
  if (typeof cb !== "function")
@@ -23553,10 +23553,10 @@ var require_SFTP = __commonJS({
23553
23553
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} SYMLINK`
23554
23554
  );
23555
23555
  }
23556
- realpath(path11, cb) {
23556
+ realpath(path13, cb) {
23557
23557
  if (this.server)
23558
23558
  throw new Error("Client-only method called in server mode");
23559
- const pathLen = Buffer.byteLength(path11);
23559
+ const pathLen = Buffer.byteLength(path13);
23560
23560
  let p = 9;
23561
23561
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + pathLen);
23562
23562
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -23564,7 +23564,7 @@ var require_SFTP = __commonJS({
23564
23564
  const reqid = this._writeReqid = this._writeReqid + 1 & MAX_REQID;
23565
23565
  writeUInt32BE(buf, reqid, 5);
23566
23566
  writeUInt32BE(buf, pathLen, p);
23567
- buf.utf8Write(path11, p += 4, pathLen);
23567
+ buf.utf8Write(path13, p += 4, pathLen);
23568
23568
  this._requests[reqid] = {
23569
23569
  cb: (err, names) => {
23570
23570
  if (typeof cb !== "function")
@@ -23609,13 +23609,13 @@ var require_SFTP = __commonJS({
23609
23609
  this._debug(`SFTP: Outbound: ${which} posix-rename@openssh.com`);
23610
23610
  }
23611
23611
  }
23612
- ext_openssh_statvfs(path11, cb) {
23612
+ ext_openssh_statvfs(path13, cb) {
23613
23613
  if (this.server)
23614
23614
  throw new Error("Client-only method called in server mode");
23615
23615
  const ext = this._extensions["statvfs@openssh.com"];
23616
23616
  if (!ext || ext !== "2")
23617
23617
  throw new Error("Server does not support this extended request");
23618
- const pathLen = Buffer.byteLength(path11);
23618
+ const pathLen = Buffer.byteLength(path13);
23619
23619
  let p = 9;
23620
23620
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 19 + 4 + pathLen);
23621
23621
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -23625,7 +23625,7 @@ var require_SFTP = __commonJS({
23625
23625
  writeUInt32BE(buf, 19, p);
23626
23626
  buf.utf8Write("statvfs@openssh.com", p += 4, 19);
23627
23627
  writeUInt32BE(buf, pathLen, p += 19);
23628
- buf.utf8Write(path11, p += 4, pathLen);
23628
+ buf.utf8Write(path13, p += 4, pathLen);
23629
23629
  this._requests[reqid] = { extended: "statvfs@openssh.com", cb };
23630
23630
  const isBuffered = sendOrBuffer(this, buf);
23631
23631
  if (this._debug) {
@@ -23711,7 +23711,7 @@ var require_SFTP = __commonJS({
23711
23711
  `SFTP: Outbound: ${isBuffered ? "Buffered" : "Sending"} fsync@openssh.com`
23712
23712
  );
23713
23713
  }
23714
- ext_openssh_lsetstat(path11, attrs, cb) {
23714
+ ext_openssh_lsetstat(path13, attrs, cb) {
23715
23715
  if (this.server)
23716
23716
  throw new Error("Client-only method called in server mode");
23717
23717
  const ext = this._extensions["lsetstat@openssh.com"];
@@ -23726,7 +23726,7 @@ var require_SFTP = __commonJS({
23726
23726
  } else if (typeof attrs === "function") {
23727
23727
  cb = attrs;
23728
23728
  }
23729
- const pathLen = Buffer.byteLength(path11);
23729
+ const pathLen = Buffer.byteLength(path13);
23730
23730
  let p = 9;
23731
23731
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 20 + 4 + pathLen + 4 + attrsLen);
23732
23732
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -23736,7 +23736,7 @@ var require_SFTP = __commonJS({
23736
23736
  writeUInt32BE(buf, 20, p);
23737
23737
  buf.utf8Write("lsetstat@openssh.com", p += 4, 20);
23738
23738
  writeUInt32BE(buf, pathLen, p += 20);
23739
- buf.utf8Write(path11, p += 4, pathLen);
23739
+ buf.utf8Write(path13, p += 4, pathLen);
23740
23740
  writeUInt32BE(buf, flags, p += pathLen);
23741
23741
  if (attrsLen) {
23742
23742
  p += 4;
@@ -23753,13 +23753,13 @@ var require_SFTP = __commonJS({
23753
23753
  this._debug(`SFTP: Outbound: ${status5} lsetstat@openssh.com`);
23754
23754
  }
23755
23755
  }
23756
- ext_openssh_expandPath(path11, cb) {
23756
+ ext_openssh_expandPath(path13, cb) {
23757
23757
  if (this.server)
23758
23758
  throw new Error("Client-only method called in server mode");
23759
23759
  const ext = this._extensions["expand-path@openssh.com"];
23760
23760
  if (ext !== "1")
23761
23761
  throw new Error("Server does not support this extended request");
23762
- const pathLen = Buffer.byteLength(path11);
23762
+ const pathLen = Buffer.byteLength(path13);
23763
23763
  let p = 9;
23764
23764
  const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 23 + 4 + pathLen);
23765
23765
  writeUInt32BE(buf, buf.length - 4, 0);
@@ -23769,7 +23769,7 @@ var require_SFTP = __commonJS({
23769
23769
  writeUInt32BE(buf, 23, p);
23770
23770
  buf.utf8Write("expand-path@openssh.com", p += 4, 23);
23771
23771
  writeUInt32BE(buf, pathLen, p += 20);
23772
- buf.utf8Write(path11, p += 4, pathLen);
23772
+ buf.utf8Write(path13, p += 4, pathLen);
23773
23773
  this._requests[reqid] = {
23774
23774
  cb: (err, names) => {
23775
23775
  if (typeof cb !== "function")
@@ -25047,12 +25047,12 @@ var require_SFTP = __commonJS({
25047
25047
  [REQUEST.LSTAT]: (sftp, payload) => {
25048
25048
  bufferParser.init(payload, 1);
25049
25049
  const reqID = bufferParser.readUInt32BE();
25050
- const path11 = bufferParser.readString(true);
25050
+ const path13 = bufferParser.readString(true);
25051
25051
  bufferParser.clear();
25052
- if (path11 === void 0)
25052
+ if (path13 === void 0)
25053
25053
  return doFatalSFTPError(sftp, "Malformed LSTAT packet");
25054
25054
  sftp._debug && sftp._debug(`SFTP: Inbound: Received LSTAT (id:${reqID})`);
25055
- if (!sftp.emit("LSTAT", reqID, path11)) {
25055
+ if (!sftp.emit("LSTAT", reqID, path13)) {
25056
25056
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
25057
25057
  }
25058
25058
  },
@@ -25071,13 +25071,13 @@ var require_SFTP = __commonJS({
25071
25071
  [REQUEST.SETSTAT]: (sftp, payload) => {
25072
25072
  bufferParser.init(payload, 1);
25073
25073
  const reqID = bufferParser.readUInt32BE();
25074
- const path11 = bufferParser.readString(true);
25074
+ const path13 = bufferParser.readString(true);
25075
25075
  const attrs = readAttrs(sftp._biOpt);
25076
25076
  bufferParser.clear();
25077
25077
  if (attrs === void 0)
25078
25078
  return doFatalSFTPError(sftp, "Malformed SETSTAT packet");
25079
25079
  sftp._debug && sftp._debug(`SFTP: Inbound: Received SETSTAT (id:${reqID})`);
25080
- if (!sftp.emit("SETSTAT", reqID, path11, attrs)) {
25080
+ if (!sftp.emit("SETSTAT", reqID, path13, attrs)) {
25081
25081
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
25082
25082
  }
25083
25083
  },
@@ -25099,12 +25099,12 @@ var require_SFTP = __commonJS({
25099
25099
  [REQUEST.OPENDIR]: (sftp, payload) => {
25100
25100
  bufferParser.init(payload, 1);
25101
25101
  const reqID = bufferParser.readUInt32BE();
25102
- const path11 = bufferParser.readString(true);
25102
+ const path13 = bufferParser.readString(true);
25103
25103
  bufferParser.clear();
25104
- if (path11 === void 0)
25104
+ if (path13 === void 0)
25105
25105
  return doFatalSFTPError(sftp, "Malformed OPENDIR packet");
25106
25106
  sftp._debug && sftp._debug(`SFTP: Inbound: Received OPENDIR (id:${reqID})`);
25107
- if (!sftp.emit("OPENDIR", reqID, path11)) {
25107
+ if (!sftp.emit("OPENDIR", reqID, path13)) {
25108
25108
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
25109
25109
  }
25110
25110
  },
@@ -25123,63 +25123,63 @@ var require_SFTP = __commonJS({
25123
25123
  [REQUEST.REMOVE]: (sftp, payload) => {
25124
25124
  bufferParser.init(payload, 1);
25125
25125
  const reqID = bufferParser.readUInt32BE();
25126
- const path11 = bufferParser.readString(true);
25126
+ const path13 = bufferParser.readString(true);
25127
25127
  bufferParser.clear();
25128
- if (path11 === void 0)
25128
+ if (path13 === void 0)
25129
25129
  return doFatalSFTPError(sftp, "Malformed REMOVE packet");
25130
25130
  sftp._debug && sftp._debug(`SFTP: Inbound: Received REMOVE (id:${reqID})`);
25131
- if (!sftp.emit("REMOVE", reqID, path11)) {
25131
+ if (!sftp.emit("REMOVE", reqID, path13)) {
25132
25132
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
25133
25133
  }
25134
25134
  },
25135
25135
  [REQUEST.MKDIR]: (sftp, payload) => {
25136
25136
  bufferParser.init(payload, 1);
25137
25137
  const reqID = bufferParser.readUInt32BE();
25138
- const path11 = bufferParser.readString(true);
25138
+ const path13 = bufferParser.readString(true);
25139
25139
  const attrs = readAttrs(sftp._biOpt);
25140
25140
  bufferParser.clear();
25141
25141
  if (attrs === void 0)
25142
25142
  return doFatalSFTPError(sftp, "Malformed MKDIR packet");
25143
25143
  sftp._debug && sftp._debug(`SFTP: Inbound: Received MKDIR (id:${reqID})`);
25144
- if (!sftp.emit("MKDIR", reqID, path11, attrs)) {
25144
+ if (!sftp.emit("MKDIR", reqID, path13, attrs)) {
25145
25145
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
25146
25146
  }
25147
25147
  },
25148
25148
  [REQUEST.RMDIR]: (sftp, payload) => {
25149
25149
  bufferParser.init(payload, 1);
25150
25150
  const reqID = bufferParser.readUInt32BE();
25151
- const path11 = bufferParser.readString(true);
25151
+ const path13 = bufferParser.readString(true);
25152
25152
  bufferParser.clear();
25153
- if (path11 === void 0)
25153
+ if (path13 === void 0)
25154
25154
  return doFatalSFTPError(sftp, "Malformed RMDIR packet");
25155
25155
  sftp._debug && sftp._debug(`SFTP: Inbound: Received RMDIR (id:${reqID})`);
25156
- if (!sftp.emit("RMDIR", reqID, path11)) {
25156
+ if (!sftp.emit("RMDIR", reqID, path13)) {
25157
25157
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
25158
25158
  }
25159
25159
  },
25160
25160
  [REQUEST.REALPATH]: (sftp, payload) => {
25161
25161
  bufferParser.init(payload, 1);
25162
25162
  const reqID = bufferParser.readUInt32BE();
25163
- const path11 = bufferParser.readString(true);
25163
+ const path13 = bufferParser.readString(true);
25164
25164
  bufferParser.clear();
25165
- if (path11 === void 0)
25165
+ if (path13 === void 0)
25166
25166
  return doFatalSFTPError(sftp, "Malformed REALPATH packet");
25167
25167
  sftp._debug && sftp._debug(
25168
25168
  `SFTP: Inbound: Received REALPATH (id:${reqID})`
25169
25169
  );
25170
- if (!sftp.emit("REALPATH", reqID, path11)) {
25170
+ if (!sftp.emit("REALPATH", reqID, path13)) {
25171
25171
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
25172
25172
  }
25173
25173
  },
25174
25174
  [REQUEST.STAT]: (sftp, payload) => {
25175
25175
  bufferParser.init(payload, 1);
25176
25176
  const reqID = bufferParser.readUInt32BE();
25177
- const path11 = bufferParser.readString(true);
25177
+ const path13 = bufferParser.readString(true);
25178
25178
  bufferParser.clear();
25179
- if (path11 === void 0)
25179
+ if (path13 === void 0)
25180
25180
  return doFatalSFTPError(sftp, "Malformed STAT packet");
25181
25181
  sftp._debug && sftp._debug(`SFTP: Inbound: Received STAT (id:${reqID})`);
25182
- if (!sftp.emit("STAT", reqID, path11)) {
25182
+ if (!sftp.emit("STAT", reqID, path13)) {
25183
25183
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
25184
25184
  }
25185
25185
  },
@@ -25199,14 +25199,14 @@ var require_SFTP = __commonJS({
25199
25199
  [REQUEST.READLINK]: (sftp, payload) => {
25200
25200
  bufferParser.init(payload, 1);
25201
25201
  const reqID = bufferParser.readUInt32BE();
25202
- const path11 = bufferParser.readString(true);
25202
+ const path13 = bufferParser.readString(true);
25203
25203
  bufferParser.clear();
25204
- if (path11 === void 0)
25204
+ if (path13 === void 0)
25205
25205
  return doFatalSFTPError(sftp, "Malformed READLINK packet");
25206
25206
  sftp._debug && sftp._debug(
25207
25207
  `SFTP: Inbound: Received READLINK (id:${reqID})`
25208
25208
  );
25209
- if (!sftp.emit("READLINK", reqID, path11)) {
25209
+ if (!sftp.emit("READLINK", reqID, path13)) {
25210
25210
  sftp.status(reqID, STATUS_CODE.OP_UNSUPPORTED);
25211
25211
  }
25212
25212
  },
@@ -25277,7 +25277,7 @@ var require_SFTP = __commonJS({
25277
25277
  function roundUpToMultipleOf8(n) {
25278
25278
  return n + 7 & ~7;
25279
25279
  }
25280
- function ReadStream(sftp, path11, options) {
25280
+ function ReadStream(sftp, path13, options) {
25281
25281
  if (options === void 0)
25282
25282
  options = {};
25283
25283
  else if (typeof options === "string")
@@ -25291,7 +25291,7 @@ var require_SFTP = __commonJS({
25291
25291
  options.emitClose = false;
25292
25292
  options.autoDestroy = false;
25293
25293
  ReadableStream2.call(this, options);
25294
- this.path = path11;
25294
+ this.path = path13;
25295
25295
  this.flags = options.flags === void 0 ? "r" : options.flags;
25296
25296
  this.mode = options.mode === void 0 ? 438 : options.mode;
25297
25297
  this.start = options.start;
@@ -25422,7 +25422,7 @@ var require_SFTP = __commonJS({
25422
25422
  },
25423
25423
  configurable: true
25424
25424
  });
25425
- function WriteStream(sftp, path11, options) {
25425
+ function WriteStream(sftp, path13, options) {
25426
25426
  if (options === void 0)
25427
25427
  options = {};
25428
25428
  else if (typeof options === "string")
@@ -25434,7 +25434,7 @@ var require_SFTP = __commonJS({
25434
25434
  options.emitClose = false;
25435
25435
  options.autoDestroy = false;
25436
25436
  WritableStream.call(this, options);
25437
- this.path = path11;
25437
+ this.path = path13;
25438
25438
  this.flags = options.flags === void 0 ? "w" : options.flags;
25439
25439
  this.mode = options.mode === void 0 ? 438 : options.mode;
25440
25440
  this.start = options.start;
@@ -26116,7 +26116,7 @@ var require_client = __commonJS({
26116
26116
  "../../node_modules/.pnpm/ssh2@1.17.0/node_modules/ssh2/lib/client.js"(exports, module) {
26117
26117
  "use strict";
26118
26118
  var {
26119
- createHash,
26119
+ createHash: createHash2,
26120
26120
  getHashes,
26121
26121
  randomFillSync
26122
26122
  } = __require("crypto");
@@ -26338,7 +26338,7 @@ var require_client = __commonJS({
26338
26338
  }
26339
26339
  hostVerifier = (key, verify) => {
26340
26340
  if (hashAlgo)
26341
- key = createHash(hashAlgo).update(key).digest("hex");
26341
+ key = createHash2(hashAlgo).update(key).digest("hex");
26342
26342
  const ret = hashCb(key, verify);
26343
26343
  if (ret !== void 0)
26344
26344
  verify(ret);
@@ -29638,9 +29638,67 @@ 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
+ /** Each run's Claude Code settings file, `<runId>.json`, there only while the run lasts. */
29660
+ runsDir: path.join(dir, "runs"),
29661
+ /** The runner's bare mirrors of the projects' repositories: the only ones that talk to GitHub. */
29662
+ gitDir: path.join(dir, "git")
29663
+ };
29664
+ }
29665
+ function loadConfig(dir = defaultDir()) {
29666
+ let text;
29667
+ try {
29668
+ text = readFileSync(paths(dir).config, "utf8");
29669
+ } catch {
29670
+ throw new Error(`No runner config in ${dir}. Run: notiamo-runner init`);
29671
+ }
29672
+ return JSON.parse(text);
29673
+ }
29674
+ function saveConfig(cfg, dir = defaultDir()) {
29675
+ mkdirSync(dir, { recursive: true, mode: 448 });
29676
+ const file2 = paths(dir).config;
29677
+ writeFileSync(file2, JSON.stringify(cfg, null, 2) + "\n", { mode: 384 });
29678
+ try {
29679
+ chmodSync(file2, 384);
29680
+ } catch {
29681
+ }
29682
+ }
29683
+
29684
+ // src/args.ts
29685
+ function parseArgs(argv) {
29686
+ const out = {};
29687
+ for (let i = 0; i < argv.length; i++) {
29688
+ const a = argv[i];
29689
+ if (a === "--profile" || a.startsWith("--profile=")) {
29690
+ const v = a === "--profile" ? argv[++i] : a.slice("--profile=".length);
29691
+ if (!v || !PROFILE_RE.test(v)) return { error: `--profile needs a name of letters, digits and dashes (1 to 32), not "${v ?? ""}".` };
29692
+ out.profile = v;
29693
+ } else if (out.cmd === void 0) out.cmd = a;
29694
+ else return { error: `Unexpected argument: ${a}` };
29695
+ }
29696
+ return out;
29697
+ }
29698
+
29641
29699
  // src/commands.ts
29642
- import { readFileSync as readFileSync3 } from "node:fs";
29643
- import os3 from "node:os";
29700
+ import { readFileSync as readFileSync4 } from "node:fs";
29701
+ import os6 from "node:os";
29644
29702
  import { createInterface } from "node:readline/promises";
29645
29703
  import { Writable as Writable2 } from "node:stream";
29646
29704
 
@@ -29682,6 +29740,10 @@ var NotiamoApi = class {
29682
29740
  done(runId, body) {
29683
29741
  return this.json("POST", `/api/runner/runs/${encodeURIComponent(runId)}/done`, body);
29684
29742
  }
29743
+ /** A fresh GitHub token for the run's repository, when the spec's one is close to its end. */
29744
+ gitToken(runId) {
29745
+ return this.json("POST", `/api/runner/runs/${encodeURIComponent(runId)}/git-token`, {});
29746
+ }
29685
29747
  /** Uploads a gzip backup of what the run changed. */
29686
29748
  async backup(runId, gzip) {
29687
29749
  await this.call("POST", `/api/runner/runs/${encodeURIComponent(runId)}/backup`, gzip, "application/gzip");
@@ -29702,45 +29764,93 @@ var NotiamoApi = class {
29702
29764
  }
29703
29765
  };
29704
29766
 
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
29767
  // src/engine/claude.ts
29739
29768
  import * as child_process from "node:child_process";
29769
+ import os2 from "node:os";
29740
29770
  var MAX_TEXT = 2e3;
29741
29771
  var KILL_GRACE_MS = 1e4;
29742
29772
  var CLOSE_GRACE_MS = 2e3;
29743
29773
  var cut = (s) => s.length > MAX_TEXT ? s.slice(0, MAX_TEXT) : s;
29774
+ var ENV_ALLOW = [
29775
+ "PATH",
29776
+ "HOME",
29777
+ "USER",
29778
+ "LOGNAME",
29779
+ "SHELL",
29780
+ "LANG",
29781
+ "LC_ALL",
29782
+ "LC_CTYPE",
29783
+ "TERM",
29784
+ "TMPDIR",
29785
+ "TZ",
29786
+ // A machine behind a proxy or with its own CA needs these to reach anything.
29787
+ "HTTP_PROXY",
29788
+ "HTTPS_PROXY",
29789
+ "NO_PROXY",
29790
+ "http_proxy",
29791
+ "https_proxy",
29792
+ "no_proxy",
29793
+ "NODE_EXTRA_CA_CERTS",
29794
+ "SSL_CERT_FILE",
29795
+ "SSL_CERT_DIR"
29796
+ ];
29797
+ var ENV_ALLOW_PREFIX = ["XDG_"];
29798
+ var ENV_ALLOW_WINDOWS = [
29799
+ "PATHEXT",
29800
+ "SYSTEMROOT",
29801
+ "SYSTEMDRIVE",
29802
+ "WINDIR",
29803
+ "COMSPEC",
29804
+ "USERPROFILE",
29805
+ "USERNAME",
29806
+ "HOMEDRIVE",
29807
+ "HOMEPATH",
29808
+ "APPDATA",
29809
+ "LOCALAPPDATA",
29810
+ "PROGRAMDATA",
29811
+ "PROGRAMFILES",
29812
+ "PROGRAMFILES(X86)",
29813
+ "TEMP",
29814
+ "TMP"
29815
+ ];
29816
+ function baseEnv(env, windows) {
29817
+ const out = {};
29818
+ for (const [k, v] of Object.entries(env)) {
29819
+ if (v === void 0) continue;
29820
+ const key = windows ? k.toUpperCase() : k;
29821
+ if (!(ENV_ALLOW.includes(key) || ENV_ALLOW_PREFIX.some((p) => key.startsWith(p)) || windows && ENV_ALLOW_WINDOWS.includes(key))) continue;
29822
+ if (PROXY_URLS.includes(key.toUpperCase())) {
29823
+ const url2 = withoutUserinfo(v);
29824
+ if (url2 !== null) out[k] = url2;
29825
+ continue;
29826
+ }
29827
+ out[k] = v;
29828
+ }
29829
+ return out;
29830
+ }
29831
+ var PROXY_URLS = ["HTTP_PROXY", "HTTPS_PROXY"];
29832
+ function withoutUserinfo(value) {
29833
+ try {
29834
+ const u = new URL(value);
29835
+ u.username = "";
29836
+ u.password = "";
29837
+ return u.toString();
29838
+ } catch {
29839
+ return null;
29840
+ }
29841
+ }
29842
+ var AGENT_GIT_ENV = {
29843
+ GIT_CONFIG_GLOBAL: os2.devNull,
29844
+ GIT_CONFIG_NOSYSTEM: "1",
29845
+ GIT_TERMINAL_PROMPT: "0",
29846
+ GIT_AUTHOR_NAME: "Notiamo agent",
29847
+ GIT_AUTHOR_EMAIL: "agent@notiamo.com",
29848
+ GIT_COMMITTER_NAME: "Notiamo agent",
29849
+ GIT_COMMITTER_EMAIL: "agent@notiamo.com"
29850
+ };
29851
+ function isSandboxUnavailable(line) {
29852
+ return /sandbox/i.test(line) && /(unavailable|not available|stopped being available|failed to (start|initiali[sz]e)|requires wsl2|bubblewrap|bwrap|socat)/i.test(line);
29853
+ }
29744
29854
  function claudeEngine(deps = {}) {
29745
29855
  const spawn4 = deps.spawn ?? child_process.spawn;
29746
29856
  const bin = deps.claudeBin ?? "claude";
@@ -29761,13 +29871,22 @@ function claudeEngine(deps = {}) {
29761
29871
  // tokens never show on the command line (`ps`). --mcp-config is variadic: the next argument must be a --flag.
29762
29872
  "--mcp-config",
29763
29873
  JSON.stringify(mcpConfig(o)),
29764
- // Only Notiamo's servers: permissions are skipped, so no .mcp.json of the cloned repo or of the config dir may load.
29874
+ // Only Notiamo's servers: no .mcp.json of the cloned repo or of the config dir may load.
29765
29875
  "--strict-mcp-config",
29766
- "--dangerously-skip-permissions"
29876
+ // Edits are accepted only inside the working folder; the settings file allows every (sandboxed) Bash command and
29877
+ // Notiamo's tools. Anything else would prompt, and in -p mode a prompt is a refusal.
29878
+ "--permission-mode",
29879
+ "acceptEdits",
29880
+ // The run's settings file (allow and deny rules, read and write block, shell sandbox): only paths and domains, never a secret.
29881
+ ...o.settingsFile ? ["--settings", o.settingsFile] : [],
29882
+ // Only the runner's own user settings (in CLAUDE_CONFIG_DIR): the clone's .claude/settings*.json never load.
29883
+ "--setting-sources",
29884
+ "user"
29767
29885
  ];
29768
29886
  const engineEnv = (o) => ({
29769
- ...process.env,
29887
+ ...baseEnv(deps.env ?? process.env, windows),
29770
29888
  ...o.env,
29889
+ ...AGENT_GIT_ENV,
29771
29890
  CLAUDE_CONFIG_DIR: o.claudeConfigDir,
29772
29891
  NOTIAMO_RUN_TOKEN: o.mcp.token,
29773
29892
  ...o.helper ? { NOTIAMO_WP_TOKEN: o.helper.token } : {}
@@ -29778,6 +29897,7 @@ function claudeEngine(deps = {}) {
29778
29897
  reported: false,
29779
29898
  result: false,
29780
29899
  resultError: void 0,
29900
+ sandboxError: false,
29781
29901
  limitAt: void 0,
29782
29902
  timedOut: false,
29783
29903
  spawnError: void 0,
@@ -29860,7 +29980,9 @@ function claudeEngine(deps = {}) {
29860
29980
  }
29861
29981
  lines(child.stdout, onLine);
29862
29982
  lines(child.stderr, (l) => {
29863
- if (l.trim()) onText("error", l, true);
29983
+ if (!l.trim()) return;
29984
+ if (isSandboxUnavailable(l)) st.sandboxError = true;
29985
+ onText("error", l, true);
29864
29986
  });
29865
29987
  for (const s of [child.stdout, child.stderr]) s?.on("error", (e) => emit({ kind: "error", text: cut(`stream error: ${e.message}`) }));
29866
29988
  const kill = (sig) => {
@@ -29887,6 +30009,12 @@ function claudeEngine(deps = {}) {
29887
30009
  clearTimeout(timer);
29888
30010
  clearTimeout(killTimer);
29889
30011
  clearTimeout(closeTimer);
30012
+ if (!windows && child.pid) {
30013
+ try {
30014
+ processKill(-child.pid, "SIGKILL");
30015
+ } catch {
30016
+ }
30017
+ }
29890
30018
  const usage = { costUsd: st.costUsd, inputTokens: st.inputTokens, outputTokens: st.outputTokens, sessionId: st.sessionId };
29891
30019
  const succeeded = st.result && !st.resultError;
29892
30020
  if (st.timedOut) return resolve({ outcome: "failed", error: "timeout", ...usage });
@@ -29895,6 +30023,7 @@ function claudeEngine(deps = {}) {
29895
30023
  if (st.resultError) return resolve({ outcome: "failed", error: st.resultError, ...usage });
29896
30024
  if (st.reported) return resolve({ outcome: "reported", ...usage });
29897
30025
  if (st.result) return resolve({ outcome: "ended", ...usage });
30026
+ if (code !== 0 && st.sandboxError) return resolve({ outcome: "failed", error: "sandbox_unavailable", ...usage });
29898
30027
  if (code !== 0) return resolve({ outcome: "failed", error: `claude exited with code ${code}`, ...usage });
29899
30028
  resolve({ outcome: "ended", ...usage });
29900
30029
  };
@@ -30023,15 +30152,15 @@ async function reportRun(mcp, args, fetchImpl) {
30023
30152
  // src/exec.ts
30024
30153
  import { spawn as spawn2 } from "node:child_process";
30025
30154
  var realExec = async (file2, args, opts) => {
30026
- const r = await run(file2, args, opts?.env, false);
30027
- if (r.code === 127 && process.platform === "win32") return run(file2, args.map(quoteForCmd), opts?.env, true);
30155
+ const r = await run(file2, args, opts?.env, false, opts?.timeout);
30156
+ if (r.code === 127 && process.platform === "win32") return run(file2, args.map(quoteForCmd), opts?.env, true, opts?.timeout);
30028
30157
  return r;
30029
30158
  };
30030
- function run(file2, args, env, shell) {
30159
+ function run(file2, args, env, shell, timeout) {
30031
30160
  return new Promise((resolve) => {
30032
30161
  let stdout = "";
30033
30162
  let stderr = "";
30034
- const child = spawn2(file2, args, { env: env ?? process.env, shell, stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
30163
+ const child = spawn2(file2, args, { env: env ?? process.env, shell, stdio: ["ignore", "pipe", "pipe"], windowsHide: true, ...timeout ? { timeout, killSignal: "SIGKILL" } : {} });
30035
30164
  child.stdout.on("data", (d) => {
30036
30165
  stdout += d.toString();
30037
30166
  });
@@ -30046,7 +30175,259 @@ var quoteForCmd = (a) => /^[\w.:/\\=-]+$/.test(a) ? a : `"${a.replace(/"/g, '""'
30046
30175
 
30047
30176
  // src/init.ts
30048
30177
  import { spawn as spawn3 } from "node:child_process";
30049
- import os2 from "node:os";
30178
+ import os4 from "node:os";
30179
+
30180
+ // src/sandbox.ts
30181
+ import { accessSync, constants as fsConstants, existsSync, readdirSync, realpathSync } from "node:fs";
30182
+ import os3 from "node:os";
30183
+ import path2 from "node:path";
30184
+ var ENGINE_MIN_VERSION = "2.1.314";
30185
+ var DEFAULT_DOMAINS = ["github.com", "api.github.com", "codeload.github.com", "objects.githubusercontent.com", "registry.npmjs.org"];
30186
+ function versionAtLeast(version2, min) {
30187
+ const parse3 = (v) => /(\d+)\.(\d+)\.(\d+)/.exec(v)?.slice(1).map(Number);
30188
+ const a = version2 ? parse3(version2) : void 0;
30189
+ const b = parse3(min);
30190
+ if (!a || !b) return false;
30191
+ for (let i = 0; i < 3; i++) if (a[i] !== b[i]) return a[i] > b[i];
30192
+ return true;
30193
+ }
30194
+ function engineTooOld(version2) {
30195
+ return !versionAtLeast(version2, ENGINE_MIN_VERSION);
30196
+ }
30197
+ var CREDENTIALS = [
30198
+ ".ssh",
30199
+ ".aws",
30200
+ ".gnupg",
30201
+ ".netrc",
30202
+ ".docker",
30203
+ path2.join(".config", "gh"),
30204
+ ".git-credentials",
30205
+ path2.join(".config", "git"),
30206
+ ".npmrc",
30207
+ ".pypirc",
30208
+ path2.join(".config", "gcloud"),
30209
+ path2.join(".config", "hub")
30210
+ ];
30211
+ var BASH_DENY = [
30212
+ "Bash(security *)",
30213
+ "Bash(git credential*)",
30214
+ "Bash(git-credential*)",
30215
+ "Bash(gh auth*)",
30216
+ "Bash(ssh-add *)",
30217
+ // A `*` may stand anywhere in a Bash rule: these also catch `git -c x=y credential fill`, a helper by its full path, `sh -c '…'`.
30218
+ "Bash(*credential*)",
30219
+ "Bash(*osxkeychain*)",
30220
+ "Bash(*libsecret*)",
30221
+ "Bash(*security find-*)",
30222
+ "Bash(*secret-tool*)",
30223
+ "Bash(*gh auth*)"
30224
+ ];
30225
+ var GIT_HELPERS = [
30226
+ "git-credential-osxkeychain",
30227
+ "git-credential-libsecret",
30228
+ "git-credential-store",
30229
+ "git-credential-cache",
30230
+ "git-credential-manager",
30231
+ "git-credential-manager-core"
30232
+ ];
30233
+ var HELPER_DIRS = [
30234
+ "/Library/Developer/CommandLineTools/usr/libexec/git-core",
30235
+ "/Applications/Xcode.app/Contents/Developer/usr/libexec/git-core",
30236
+ "/usr/libexec/git-core",
30237
+ "/usr/lib/git-core",
30238
+ "/usr/local/share/gcm-core",
30239
+ "/opt/homebrew/share/gcm-core",
30240
+ "/usr/local/bin"
30241
+ ];
30242
+ var KEYCHAIN_TOOLS = {
30243
+ darwin: ["/usr/bin/security", "/usr/bin/osascript"],
30244
+ linux: ["/usr/bin/secret-tool", "/usr/bin/gnome-keyring"]
30245
+ };
30246
+ async function credentialTools(exec, platform = process.platform) {
30247
+ const first = (out) => out.split(/\r?\n/).map((l) => l.trim()).find(Boolean) ?? null;
30248
+ const git = await exec("git", ["--exec-path"]).catch(() => null);
30249
+ const gh = await exec(platform === "win32" ? "where" : "which", ["gh"]).catch(() => null);
30250
+ const ghFound = gh?.code === 0 ? first(gh.stdout) : null;
30251
+ return {
30252
+ gitExecPath: git?.code === 0 ? first(git.stdout) : null,
30253
+ ghPath: ghFound && path2.isAbsolute(ghFound) ? realPath(ghFound) : null
30254
+ };
30255
+ }
30256
+ function findOnPath(name, dirs, platform) {
30257
+ const names = platform === "win32" ? [`${name}.exe`, `${name}.cmd`, name] : [name];
30258
+ for (const d of dirs) {
30259
+ for (const n of names) {
30260
+ const file2 = path2.join(d, n);
30261
+ try {
30262
+ accessSync(file2, fsConstants.X_OK);
30263
+ return realpathSync(file2);
30264
+ } catch {
30265
+ }
30266
+ }
30267
+ }
30268
+ return null;
30269
+ }
30270
+ function machineToolDirs(env = process.env, argv1 = process.argv[1], execPath = process.execPath, platform = process.platform) {
30271
+ const pathDirs = (env.PATH ?? env.Path ?? "").split(path2.delimiter).filter((d) => d && path2.isAbsolute(d));
30272
+ const dirs = pathDirs.map(realPath);
30273
+ for (const bin of ["git", "claude"]) {
30274
+ const found = findOnPath(bin, pathDirs, platform);
30275
+ if (found) dirs.push(path2.dirname(found));
30276
+ }
30277
+ dirs.push(path2.dirname(realPath(execPath)));
30278
+ if (argv1) {
30279
+ const install4 = path2.dirname(realPath(argv1));
30280
+ dirs.push(install4);
30281
+ if (path2.basename(install4) === "dist") dirs.push(path2.dirname(install4));
30282
+ }
30283
+ return [...new Set(dirs)];
30284
+ }
30285
+ function realListEntries(dir) {
30286
+ try {
30287
+ return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() || e.isFile() || e.isSymbolicLink()).map((e) => ({ name: e.name, dir: e.isDirectory() }));
30288
+ } catch {
30289
+ return [];
30290
+ }
30291
+ }
30292
+ function realPath(p) {
30293
+ const abs = path2.resolve(p);
30294
+ const rest = [];
30295
+ for (let cur = abs; ; ) {
30296
+ try {
30297
+ return path2.join(realpathSync(cur), ...rest);
30298
+ } catch {
30299
+ const parent = path2.dirname(cur);
30300
+ if (parent === cur) return abs;
30301
+ rest.unshift(path2.basename(cur));
30302
+ cur = parent;
30303
+ }
30304
+ }
30305
+ }
30306
+ function rulePath(p) {
30307
+ const posix = p.replace(/\\/g, "/").replace(/^([A-Za-z]):/, (_m, d) => `/${d.toLowerCase()}`);
30308
+ return `/${posix}`;
30309
+ }
30310
+ function credentialBinaries(o, toolDirs, listEntries, exists2) {
30311
+ const out = [];
30312
+ const gitReal = /* @__PURE__ */ new Set();
30313
+ const scan = [];
30314
+ const execDir = o.gitExecPath && path2.isAbsolute(o.gitExecPath) ? realPath(o.gitExecPath) : null;
30315
+ for (const d of /* @__PURE__ */ new Set([...execDir ? [execDir] : [], ...toolDirs, ...HELPER_DIRS.map(realPath)])) {
30316
+ const names = listEntries(d).map((e) => e.name);
30317
+ if (names.includes("git")) gitReal.add(realPath(path2.join(d, "git")));
30318
+ const helpers = names.filter((n) => n.startsWith("git-credential-"));
30319
+ scan.push({ dir: d, names: d === execDir ? [.../* @__PURE__ */ new Set([...helpers, ...GIT_HELPERS])] : helpers });
30320
+ }
30321
+ for (const { dir, names } of scan) {
30322
+ for (const n of names) {
30323
+ const own2 = path2.join(dir, n);
30324
+ const real = realPath(own2);
30325
+ out.push(gitReal.has(real) ? own2 : real);
30326
+ }
30327
+ }
30328
+ for (const t of KEYCHAIN_TOOLS[o.platform] ?? []) if (exists2(t)) out.push(realPath(t));
30329
+ if (o.ghPath && path2.isAbsolute(o.ghPath) && !gitReal.has(realPath(o.ghPath))) out.push(realPath(o.ghPath));
30330
+ return out;
30331
+ }
30332
+ function sandboxSettings(o) {
30333
+ const root = realPath(o.runnerRoot);
30334
+ const claudeDir = realPath(o.claudeConfigDir);
30335
+ const runDir = realPath(o.runDir);
30336
+ const home = realPath(o.home ?? os3.homedir());
30337
+ const at = (...parts) => path2.join(...parts);
30338
+ const edit = (p) => `Edit(${rulePath(p)})`;
30339
+ const gitHooks = at(runDir, ".git", "hooks");
30340
+ const gitConfig = at(runDir, ".git", "config");
30341
+ const listEntries = o.listEntries ?? realListEntries;
30342
+ const exists2 = o.exists ?? existsSync;
30343
+ const walked = [];
30344
+ const visited = /* @__PURE__ */ new Set();
30345
+ const holds = (dir, p) => p === dir || p.startsWith(dir.endsWith(path2.sep) ? dir : dir + path2.sep);
30346
+ const denyAllBut = (dir) => {
30347
+ if (visited.has(dir)) return;
30348
+ visited.add(dir);
30349
+ for (const e of listEntries(dir)) {
30350
+ const child = realPath(at(dir, e.name));
30351
+ if (child === runDir) continue;
30352
+ if (holds(child, runDir)) {
30353
+ if (holds(root, child) && child !== root) denyAllBut(child);
30354
+ } else if (child !== path2.parse(child).root) walked.push({ name: child, dir: e.dir });
30355
+ }
30356
+ };
30357
+ denyAllBut(root);
30358
+ const profiles = listEntries(at(root, "profiles")).filter((e) => e.dir).map((e) => at(root, "profiles", e.name));
30359
+ const known = [root, ...profiles].flatMap((d) => [realPath(at(d, "config.json")), realPath(at(d, "state.json"))]);
30360
+ const toolDirs = (o.toolDirs ?? machineToolDirs()).map(realPath);
30361
+ const denyRead = [...walked.map((e) => e.name), ...known];
30362
+ if (exists2(claudeDir)) denyRead.push(claudeDir);
30363
+ for (const c of CREDENTIALS) if (exists2(at(home, c))) denyRead.push(realPath(at(home, c)));
30364
+ denyRead.push(...credentialBinaries(o, toolDirs, listEntries, exists2));
30365
+ const holdsRun = (dir) => holds(dir, runDir);
30366
+ const editTree = (dir) => holdsRun(dir) ? [] : [edit(`${dir}/**`)];
30367
+ const deny = [
30368
+ edit(at(root, "config.json")),
30369
+ edit(at(root, "state.json")),
30370
+ edit(at(root, "runner.log")),
30371
+ ...editTree(at(root, "runs")),
30372
+ edit(at(root, "profiles", "*", "config.json")),
30373
+ edit(at(root, "profiles", "*", "state.json")),
30374
+ edit(`${at(root, "profiles", "*", "runs")}/**`),
30375
+ ...editTree(claudeDir),
30376
+ edit(`${at(root, "profiles", "*", "claude")}/**`),
30377
+ ...[".ssh", ".aws", ".gnupg", ".docker", ".config", ".npm", ".local", ".claude", ...o.platform === "darwin" ? ["Library"] : []].flatMap((d) => editTree(at(home, d))),
30378
+ ...[
30379
+ ".netrc",
30380
+ ".git-credentials",
30381
+ ".npmrc",
30382
+ ".pypirc",
30383
+ ".zshrc",
30384
+ ".zshenv",
30385
+ ".zprofile",
30386
+ ".bashrc",
30387
+ ".bash_profile",
30388
+ ".bash_login",
30389
+ ".profile",
30390
+ ".gitconfig"
30391
+ ].map((f) => edit(at(home, f))),
30392
+ edit(`${gitHooks}/**`),
30393
+ edit(gitConfig),
30394
+ edit(`${at(runDir, ".claude")}/**`),
30395
+ ...walked.flatMap((e) => e.dir ? editTree(e.name) : [edit(e.name)]),
30396
+ ...toolDirs.filter((d) => d !== path2.parse(d).root).flatMap(editTree),
30397
+ ...BASH_DENY
30398
+ ];
30399
+ const allow = [
30400
+ "Bash",
30401
+ "mcp__notiamo__*",
30402
+ "mcp__notiamo-wp__*",
30403
+ `Read(${rulePath(runDir)}/**)`,
30404
+ "Glob",
30405
+ "Grep",
30406
+ `Edit(${rulePath(runDir)}/**)`,
30407
+ "WebFetch(domain:github.com)"
30408
+ ];
30409
+ const permissions = { blockReadsOutsideWorkingDirectories: true, blockWritesOutsideWorkingDirectories: true, allow, deny: [...new Set(deny)] };
30410
+ if (o.platform === "win32") return { permissions };
30411
+ const domains = [...DEFAULT_DOMAINS, o.notiamoHost, ...o.wordpressHost ? [o.wordpressHost] : [], ...o.extraDomains].map((d) => d.trim().toLowerCase()).filter((d) => d.length > 0);
30412
+ return {
30413
+ permissions,
30414
+ sandbox: {
30415
+ enabled: true,
30416
+ failIfUnavailable: true,
30417
+ allowUnsandboxedCommands: false,
30418
+ filesystem: {
30419
+ denyRead: [...new Set(denyRead)],
30420
+ allowWrite: [runDir],
30421
+ denyWrite: [gitHooks, gitConfig]
30422
+ },
30423
+ network: {
30424
+ allowedDomains: [...new Set(domains)],
30425
+ // macOS: lets the engine's shell commands reach the run's local helper on 127.0.0.1 (no effect on Linux).
30426
+ ...o.wordpressHost ? { allowLocalBinding: true } : {}
30427
+ }
30428
+ }
30429
+ };
30430
+ }
30050
30431
 
30051
30432
  // src/service/darwin.ts
30052
30433
  var darwin_exports = {};
@@ -30058,10 +30439,11 @@ __export(darwin_exports, {
30058
30439
  uninstall: () => uninstall
30059
30440
  });
30060
30441
  import { setTimeout as delay } from "node:timers/promises";
30061
- import { mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync2, existsSync } from "node:fs";
30062
- import path2 from "node:path";
30442
+ import { mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "node:fs";
30443
+ import path3 from "node:path";
30063
30444
 
30064
30445
  // src/service/common.ts
30446
+ var startArgs = (profile) => profile ? ["start", "--profile", profile] : ["start"];
30065
30447
  function serviceEnv(env = {}) {
30066
30448
  const out = {};
30067
30449
  for (const k of ["PATH", "NOTIAMO_RUNNER_HOME"]) if (env[k]) out[k] = env[k];
@@ -30072,8 +30454,8 @@ function must(what, r) {
30072
30454
  }
30073
30455
 
30074
30456
  // src/service/darwin.ts
30075
- var LABEL = "com.notiamo.runner";
30076
- var plistPath = (home) => path2.join(home, "Library", "LaunchAgents", `${LABEL}.plist`);
30457
+ var label = (profile) => profile ? `com.notiamo.runner.${profile}` : "com.notiamo.runner";
30458
+ var plistPath = (home, profile) => path3.join(home, "Library", "LaunchAgents", `${label(profile)}.plist`);
30077
30459
  var domain = (uid) => `gui/${uid}`;
30078
30460
  var BOOTSTRAP_TRIES = 5;
30079
30461
  var BOOTSTRAP_RETRY_MS = 500;
@@ -30086,12 +30468,12 @@ function renderPlist(o) {
30086
30468
  <plist version="1.0">
30087
30469
  <dict>
30088
30470
  <key>Label</key>
30089
- <string>${LABEL}</string>
30471
+ <string>${label(o.profile)}</string>
30090
30472
  <key>ProgramArguments</key>
30091
30473
  <array>
30092
30474
  <string>${esc(o.nodePath)}</string>
30093
30475
  <string>${esc(o.cliPath)}</string>
30094
- <string>start</string>
30476
+ ${startArgs(o.profile).map((a) => ` <string>${esc(a)}</string>`).join("\n")}
30095
30477
  </array>
30096
30478
  <key>RunAtLoad</key>
30097
30479
  <true/>
@@ -30112,9 +30494,9 @@ ${env}
30112
30494
  `;
30113
30495
  }
30114
30496
  async function install(o) {
30115
- const file2 = plistPath(o.home);
30116
- mkdirSync2(path2.dirname(file2), { recursive: true });
30117
- writeFileSync2(file2, renderPlist({ nodePath: o.nodePath, cliPath: o.cliPath, log: o.paths.log, env: o.env }));
30497
+ const file2 = plistPath(o.home, o.profile);
30498
+ mkdirSync2(path3.dirname(file2), { recursive: true });
30499
+ writeFileSync2(file2, renderPlist({ nodePath: o.nodePath, cliPath: o.cliPath, log: o.paths.log, env: o.env, profile: o.profile }));
30118
30500
  await o.exec("launchctl", ["bootout", domain(o.uid), file2]);
30119
30501
  const sleep = o.sleep ?? ((ms) => delay(ms));
30120
30502
  for (let i = 1; ; i++) {
@@ -30127,15 +30509,15 @@ async function install(o) {
30127
30509
  }
30128
30510
  }
30129
30511
  async function stop(o) {
30130
- await o.exec("launchctl", ["bootout", domain(o.uid), plistPath(o.home)]);
30512
+ await o.exec("launchctl", ["bootout", domain(o.uid), plistPath(o.home, o.profile)]);
30131
30513
  }
30132
30514
  async function uninstall(o) {
30133
30515
  await stop(o);
30134
- rmSync(plistPath(o.home), { force: true });
30516
+ rmSync(plistPath(o.home, o.profile), { force: true });
30135
30517
  }
30136
30518
  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}`]);
30519
+ if (!existsSync2(plistPath(o.home, o.profile))) return "not_installed";
30520
+ const r = await o.exec("launchctl", ["print", `${domain(o.uid)}/${label(o.profile)}`]);
30139
30521
  return r.code === 0 ? "running" : "stopped";
30140
30522
  }
30141
30523
 
@@ -30148,19 +30530,19 @@ __export(linux_exports, {
30148
30530
  stop: () => stop2,
30149
30531
  uninstall: () => uninstall2
30150
30532
  });
30151
- import { existsSync as existsSync2, mkdirSync as mkdirSync3, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
30152
- import path3 from "node:path";
30153
- var UNIT = "notiamo-runner";
30154
- var unitPath = (home) => path3.join(home, ".config", "systemd", "user", `${UNIT}.service`);
30533
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
30534
+ import path4 from "node:path";
30535
+ var unit = (profile) => profile ? `notiamo-runner-${profile}` : "notiamo-runner";
30536
+ var unitPath = (home, profile) => path4.join(home, ".config", "systemd", "user", `${unit(profile)}.service`);
30155
30537
  var q = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
30156
30538
  function renderUnit(o) {
30157
30539
  const env = Object.entries(serviceEnv(o.env)).map(([k, v]) => `Environment=${q(`${k}=${v}`)}`).join("\n");
30158
30540
  return `[Unit]
30159
- Description=Notiamo runner
30541
+ Description=Notiamo runner${o.profile ? ` (${o.profile})` : ""}
30160
30542
  After=network-online.target
30161
30543
 
30162
30544
  [Service]
30163
- ExecStart=${q(o.nodePath)} ${q(o.cliPath)} start
30545
+ ExecStart=${q(o.nodePath)} ${q(o.cliPath)} ${startArgs(o.profile).join(" ")}
30164
30546
  Restart=always
30165
30547
  RestartSec=5
30166
30548
  StandardOutput=append:${o.log}
@@ -30172,25 +30554,25 @@ WantedBy=default.target
30172
30554
  `;
30173
30555
  }
30174
30556
  async function install2(o) {
30175
- const file2 = unitPath(o.home);
30176
- mkdirSync3(path3.dirname(file2), { recursive: true });
30177
- writeFileSync3(file2, renderUnit({ nodePath: o.nodePath, cliPath: o.cliPath, log: o.paths.log, env: o.env }));
30557
+ const file2 = unitPath(o.home, o.profile);
30558
+ mkdirSync3(path4.dirname(file2), { recursive: true });
30559
+ writeFileSync3(file2, renderUnit({ nodePath: o.nodePath, cliPath: o.cliPath, log: o.paths.log, env: o.env, profile: o.profile }));
30178
30560
  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]));
30561
+ must("systemctl --user enable", await o.exec("systemctl", ["--user", "enable", unit(o.profile)]));
30562
+ must("systemctl --user restart", await o.exec("systemctl", ["--user", "restart", unit(o.profile)]));
30181
30563
  await o.exec("loginctl", ["enable-linger"]);
30182
30564
  }
30183
30565
  async function stop2(o) {
30184
- await o.exec("systemctl", ["--user", "stop", UNIT]);
30566
+ await o.exec("systemctl", ["--user", "stop", unit(o.profile)]);
30185
30567
  }
30186
30568
  async function uninstall2(o) {
30187
- await o.exec("systemctl", ["--user", "disable", "--now", UNIT]);
30188
- rmSync2(unitPath(o.home), { force: true });
30569
+ await o.exec("systemctl", ["--user", "disable", "--now", unit(o.profile)]);
30570
+ rmSync2(unitPath(o.home, o.profile), { force: true });
30189
30571
  await o.exec("systemctl", ["--user", "daemon-reload"]);
30190
30572
  }
30191
30573
  async function status2(o) {
30192
- if (!existsSync2(unitPath(o.home))) return "not_installed";
30193
- const r = await o.exec("systemctl", ["--user", "is-active", UNIT]);
30574
+ if (!existsSync3(unitPath(o.home, o.profile))) return "not_installed";
30575
+ const r = await o.exec("systemctl", ["--user", "is-active", unit(o.profile)]);
30194
30576
  return r.stdout.trim() === "active" ? "running" : "stopped";
30195
30577
  }
30196
30578
 
@@ -30203,24 +30585,24 @@ __export(win32_exports, {
30203
30585
  stop: () => stop3,
30204
30586
  uninstall: () => uninstall3
30205
30587
  });
30206
- var TASK = "Notiamo runner";
30588
+ var task = (profile) => profile ? `Notiamo runner (${profile})` : "Notiamo runner";
30207
30589
  function renderCreateArgs(o) {
30208
- return ["/Create", "/F", "/SC", "ONLOGON", "/RL", "LIMITED", "/TN", TASK, "/TR", `"${o.nodePath}" "${o.cliPath}" start`];
30590
+ return ["/Create", "/F", "/SC", "ONLOGON", "/RL", "LIMITED", "/TN", task(o.profile), "/TR", `"${o.nodePath}" "${o.cliPath}" ${startArgs(o.profile).join(" ")}`];
30209
30591
  }
30210
30592
  async function install3(o) {
30211
30593
  await stop3(o);
30212
30594
  must("schtasks /Create", await o.exec("schtasks", renderCreateArgs(o)));
30213
- must("schtasks /Run", await o.exec("schtasks", ["/Run", "/TN", TASK]));
30595
+ must("schtasks /Run", await o.exec("schtasks", ["/Run", "/TN", task(o.profile)]));
30214
30596
  }
30215
30597
  async function stop3(o) {
30216
- await o.exec("schtasks", ["/End", "/TN", TASK]);
30598
+ await o.exec("schtasks", ["/End", "/TN", task(o.profile)]);
30217
30599
  }
30218
30600
  async function uninstall3(o) {
30219
30601
  await stop3(o);
30220
- await o.exec("schtasks", ["/Delete", "/F", "/TN", TASK]);
30602
+ await o.exec("schtasks", ["/Delete", "/F", "/TN", task(o.profile)]);
30221
30603
  }
30222
30604
  async function status3(o) {
30223
- const r = await o.exec("powershell", ["-NoProfile", "-NonInteractive", "-Command", `(Get-ScheduledTask -TaskName '${TASK}' -ErrorAction Stop).State`]);
30605
+ const r = await o.exec("powershell", ["-NoProfile", "-NonInteractive", "-Command", `(Get-ScheduledTask -TaskName '${task(o.profile)}' -ErrorAction Stop).State`]);
30224
30606
  if (r.code !== 0) return "not_installed";
30225
30607
  return r.stdout.trim() === "Running" ? "running" : "stopped";
30226
30608
  }
@@ -30240,6 +30622,7 @@ var serviceStatus = async (o) => impl(o).status(withUid(o));
30240
30622
 
30241
30623
  // src/init.ts
30242
30624
  var DEFAULT_HOST = "https://my.notiamo.com";
30625
+ var KEYCHAIN_WARNING = "Warning: this user's keychain holds a GitHub login. A run on this machine could reach it. Remove it (Keychain Access, search github.com) or install the runner under a separate macOS user. The runner itself does not need it.";
30243
30626
  var runInteractive = (file2, args, env) => new Promise((resolve, reject) => {
30244
30627
  const child = spawn3(file2, args, { stdio: "inherit", env, shell: process.platform === "win32" });
30245
30628
  child.on("error", reject);
@@ -30250,7 +30633,7 @@ function normalizeHost(input2) {
30250
30633
  return /^https?:\/\//i.test(h) ? h : `https://${h}`;
30251
30634
  }
30252
30635
  async function runInit(deps) {
30253
- const dir = deps.dir ?? defaultDir();
30636
+ const dir = deps.dir ?? profileDir(deps.profile);
30254
30637
  const platform = deps.platform ?? process.platform;
30255
30638
  const p = paths(dir);
30256
30639
  const env = deps.env ?? process.env;
@@ -30259,11 +30642,27 @@ async function runInit(deps) {
30259
30642
  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
30643
  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
30644
  const host = normalizeHost(await ask("Notiamo address: ", DEFAULT_HOST));
30262
- const hostname3 = deps.hostname ?? os2.hostname();
30645
+ const hostname3 = deps.hostname ?? os4.hostname();
30263
30646
  const name = await ask("Name of this runner: ", hostname3);
30264
30647
  const version2 = await deps.exec("claude", ["--version"], { env });
30265
30648
  if (version2.code !== 0) throw new Error("Claude Code is not installed. Install it first: https://claude.com/claude-code");
30266
30649
  const engineVersion = version2.stdout.trim().slice(0, 100) || "unknown";
30650
+ if (engineTooOld(engineVersion)) throw new Error(`Claude Code 2.1.314 or newer is needed (yours: ${engineVersion}). Update it: claude update`);
30651
+ const git = await deps.exec("git", ["--version"], { env });
30652
+ const gitVersion = git.code === 0 ? /git version (\d+)\.(\d+)/.exec(git.stdout) ?? null : null;
30653
+ if (!gitVersion || Number(gitVersion[1]) < 2 || Number(gitVersion[1]) === 2 && Number(gitVersion[2]) < 31) {
30654
+ const yours = git.code === 0 ? git.stdout.trim().replace(/^git version /, "").slice(0, 60) || "unknown" : "not installed";
30655
+ throw new Error(`git 2.31 or newer is needed (yours: ${yours}). Update git.`);
30656
+ }
30657
+ if (platform === "linux") {
30658
+ const bwrap = await deps.exec("which", ["bwrap"], { env });
30659
+ const socat = await deps.exec("which", ["socat"], { env });
30660
+ if (bwrap.code !== 0 || socat.code !== 0) {
30661
+ throw new Error("The sandbox needs bubblewrap and socat. Install them (Debian/Ubuntu: sudo apt install bubblewrap socat), then run init again.");
30662
+ }
30663
+ } else if (platform === "win32") {
30664
+ print("Note: on Windows without WSL2, shell commands of a run are not sandboxed; Claude's own file tools are still limited to the run folder.");
30665
+ }
30267
30666
  try {
30268
30667
  await new NotiamoApi({ host, token }, deps.fetch).hello({ version: VERSION, os: platform, engine: "claude", engineVersion });
30269
30668
  } catch (e) {
@@ -30277,25 +30676,31 @@ async function runInit(deps) {
30277
30676
  }
30278
30677
  const check2 = await deps.exec("claude", ["-p", "say ok"], { env: claudeEnv });
30279
30678
  if (check2.code !== 0) throw new Error("Claude Code is not signed in for the runner.");
30679
+ if (platform === "darwin") {
30680
+ const git2 = await deps.exec("security", ["find-internet-password", "-s", "github.com"], { env, timeout: 1e4 });
30681
+ const gh = await deps.exec("security", ["find-generic-password", "-s", "gh:github.com"], { env, timeout: 1e4 });
30682
+ if (git2.code === 0 || gh.code === 0) print(KEYCHAIN_WARNING);
30683
+ }
30280
30684
  saveConfig({ host, token, name, dataDir: dir, engine: "claude" }, dir);
30281
30685
  await installService({
30282
30686
  platform,
30687
+ profile: deps.profile,
30283
30688
  paths: p,
30284
30689
  exec: deps.exec,
30285
- home: deps.home ?? os2.homedir(),
30690
+ home: deps.home ?? os4.homedir(),
30286
30691
  env,
30287
30692
  nodePath: deps.nodePath ?? process.execPath,
30288
30693
  cliPath: deps.cliPath ?? process.argv[1]
30289
30694
  });
30290
- print(`Runner ${name} is installed and running. Logs: ${p.log}`);
30695
+ print(`Runner ${name}${deps.profile ? ` (profile ${deps.profile})` : ""} is installed and running. Logs: ${p.log}`);
30291
30696
  }
30292
30697
 
30293
30698
  // src/log.ts
30294
30699
  import { appendFileSync, mkdirSync as mkdirSync4, renameSync, statSync } from "node:fs";
30295
- import path4 from "node:path";
30700
+ import path5 from "node:path";
30296
30701
  var MAX_BYTES = 10 * 1024 * 1024;
30297
30702
  function createLogger(file2) {
30298
- mkdirSync4(path4.dirname(file2), { recursive: true, mode: 448 });
30703
+ mkdirSync4(path5.dirname(file2), { recursive: true, mode: 448 });
30299
30704
  const write = (level, msg) => {
30300
30705
  try {
30301
30706
  if (statSync(file2).size > MAX_BYTES) renameSync(file2, file2 + ".1");
@@ -30308,18 +30713,110 @@ function createLogger(file2) {
30308
30713
  }
30309
30714
 
30310
30715
  // src/loop.ts
30311
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync2, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
30312
- import path10 from "node:path";
30716
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
30717
+ import path12 from "node:path";
30313
30718
  import { setTimeout as delay2 } from "node:timers/promises";
30314
30719
 
30720
+ // src/clones.ts
30721
+ import { lstatSync, mkdirSync as mkdirSync5, readdirSync as readdirSync2, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "node:fs";
30722
+ import path6 from "node:path";
30723
+ var lastRunPath = (gitDir, projectId) => path6.join(gitDir, `${projectId}.last-run`);
30724
+ function touchLastRun(gitDir, projectId, now) {
30725
+ mkdirSync5(gitDir, { recursive: true, mode: 448 });
30726
+ const file2 = lastRunPath(gitDir, projectId);
30727
+ rmSync3(file2, { force: true });
30728
+ writeFileSync4(file2, now.toISOString() + "\n", { flag: "wx", mode: 384 });
30729
+ }
30730
+ var DAY_MS = 864e5;
30731
+ var SLOT_RE = /^\d+$/;
30732
+ var ID_RE = /^[\w-]+$/;
30733
+ function removeStaleClones(projectsDir, gitDir, keepDays, busyProjectIds, now, log) {
30734
+ const removed = [];
30735
+ let names;
30736
+ try {
30737
+ names = readdirSync2(projectsDir);
30738
+ } catch (e) {
30739
+ if (e.code !== "ENOENT") log.warn(`project folders not listed: ${errorText(e)}`);
30740
+ return removed;
30741
+ }
30742
+ const cutoff = now.getTime() - keepDays * DAY_MS;
30743
+ const isDir = (p) => {
30744
+ try {
30745
+ return lstatSync(p).isDirectory();
30746
+ } catch {
30747
+ return false;
30748
+ }
30749
+ };
30750
+ const gone = /* @__PURE__ */ new Set();
30751
+ const remove = (folder, label2, id, extra, why) => {
30752
+ try {
30753
+ rmSync3(folder, { recursive: true, force: true });
30754
+ for (const f of extra) rmSync3(f, { force: true });
30755
+ removed.push(label2);
30756
+ gone.add(id);
30757
+ log.info(`removed project folder ${label2}: ${why}`);
30758
+ } catch (e) {
30759
+ log.warn(`project folder ${label2} not removed: ${errorText(e)}`);
30760
+ }
30761
+ };
30762
+ const slots = names.filter((n) => SLOT_RE.test(n) && isDir(path6.join(projectsDir, n)));
30763
+ for (const name of names) {
30764
+ if (SLOT_RE.test(name) || !ID_RE.test(name) || busyProjectIds.has(name)) continue;
30765
+ const folder = path6.join(projectsDir, name);
30766
+ if (!isDir(folder)) continue;
30767
+ remove(folder, name, name, [path6.join(gitDir, `${name}.worktree.json`)], "left by an older runner");
30768
+ }
30769
+ for (const slot of slots) {
30770
+ let ids;
30771
+ try {
30772
+ ids = readdirSync2(path6.join(projectsDir, slot));
30773
+ } catch (e) {
30774
+ log.warn(`slot ${slot} not listed: ${errorText(e)}`);
30775
+ continue;
30776
+ }
30777
+ for (const id of ids) {
30778
+ if (!ID_RE.test(id) || busyProjectIds.has(id)) continue;
30779
+ const folder = path6.join(projectsDir, slot, id);
30780
+ try {
30781
+ const st = lstatSync(folder);
30782
+ if (!st.isDirectory()) continue;
30783
+ let lastUse = st.mtimeMs;
30784
+ try {
30785
+ const marker = lstatSync(lastRunPath(gitDir, id));
30786
+ if (marker.isFile()) lastUse = marker.mtimeMs;
30787
+ } catch {
30788
+ }
30789
+ if (lastUse >= cutoff) continue;
30790
+ } catch (e) {
30791
+ log.warn(`project folder ${slot}/${id} not checked: ${errorText(e)}`);
30792
+ continue;
30793
+ }
30794
+ remove(folder, `${slot}/${id}`, id, [path6.join(gitDir, `${id}.${slot}.worktree.json`)], `unused for more than ${keepDays} days`);
30795
+ }
30796
+ }
30797
+ for (const id of gone) {
30798
+ if (slots.some((s) => isDir(path6.join(projectsDir, s, id)))) continue;
30799
+ try {
30800
+ rmSync3(path6.join(gitDir, `${id}.git`), { recursive: true, force: true });
30801
+ rmSync3(lastRunPath(gitDir, id), { force: true });
30802
+ } catch (e) {
30803
+ log.warn(`mirror of ${id} not removed: ${errorText(e)}`);
30804
+ }
30805
+ }
30806
+ return removed;
30807
+ }
30808
+ var errorText = (e) => e instanceof Error ? e.message : String(e);
30809
+
30315
30810
  // src/run.ts
30316
- import { mkdirSync as mkdirSync6, mkdtempSync, rmSync as rmSync3 } from "node:fs";
30317
- import path9 from "node:path";
30811
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, mkdtempSync, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "node:fs";
30812
+ import path11 from "node:path";
30318
30813
 
30319
30814
  // src/project-folder.ts
30320
30815
  import { execFile } from "node:child_process";
30321
- import { existsSync as existsSync3, mkdirSync as mkdirSync5 } from "node:fs";
30322
- import path5 from "node:path";
30816
+ import { createHash } from "node:crypto";
30817
+ import { existsSync as existsSync4, lstatSync as lstatSync2, mkdirSync as mkdirSync6, readdirSync as readdirSync3, readFileSync as readFileSync2, realpathSync as realpathSync2, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "node:fs";
30818
+ import os5 from "node:os";
30819
+ import path7 from "node:path";
30323
30820
  import { promisify } from "node:util";
30324
30821
  var execFileAsync = async (file2, args, opts) => {
30325
30822
  const { stdout, stderr } = await promisify(execFile)(file2, args, {
@@ -30331,25 +30828,182 @@ var execFileAsync = async (file2, args, opts) => {
30331
30828
  });
30332
30829
  return { stdout: String(stdout), stderr: String(stderr) };
30333
30830
  };
30831
+ var SAFE_GIT = ["-c", "core.hooksPath=/dev/null", "-c", "core.fsmonitor=false"];
30832
+ function gitEnv() {
30833
+ const env = { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: os5.devNull };
30834
+ for (const k of Object.keys(env)) if (/^GIT_CONFIG_(COUNT|KEY_\d+|VALUE_\d+|PARAMETERS)$/.test(k) || k === "GIT_ASKPASS") delete env[k];
30835
+ return env;
30836
+ }
30837
+ function githubAuthEnv(token) {
30838
+ return {
30839
+ ...gitEnv(),
30840
+ GIT_CONFIG_COUNT: "1",
30841
+ GIT_CONFIG_KEY_0: `http.${GITHUB_URL}.extraheader`,
30842
+ GIT_CONFIG_VALUE_0: `AUTHORIZATION: basic ${Buffer.from(`x-access-token:${token}`).toString("base64")}`
30843
+ };
30844
+ }
30334
30845
  var CLONE_TIMEOUT_MS = 10 * 6e4;
30335
30846
  var GIT_TIMEOUT_MS = 2 * 6e4;
30336
- async function prepareGithubFolder(projectsDir, projectId, g, exec = execFileAsync) {
30337
- const folder = path5.join(projectsDir, projectId);
30338
- const env = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
30339
- const git = (args, timeout = GIT_TIMEOUT_MS) => exec("git", ["-C", folder, ...args], { env, timeout });
30340
- if (existsSync3(path5.join(folder, ".git"))) {
30341
- await git(["fetch", "origin", "--prune"]);
30847
+ var barePath = (gitDir, projectId) => path7.join(gitDir, `${projectId}.git`);
30848
+ var projectFolder = (projectsDir, slot, projectId) => path7.join(projectsDir, String(slot), projectId);
30849
+ var GITHUB_URL = "https://github.com/";
30850
+ async function prepareGithubFolder(dirs, projectId, g, token, exec = execFileAsync) {
30851
+ if (!/^[\w-]+$/.test(projectId)) throw new Error("runner_error");
30852
+ if (typeof g.cloneUrl !== "string" || !g.cloneUrl.startsWith(GITHUB_URL) || /\s/.test(g.cloneUrl)) throw new Error("runner_error");
30853
+ const slot = dirs.slot ?? 0;
30854
+ const slotDir = path7.join(dirs.projectsDir, String(slot));
30855
+ const folder = projectFolder(dirs.projectsDir, slot, projectId);
30856
+ const bare = barePath(dirs.gitDir, projectId);
30857
+ const saved = savedSnapshotPath(dirs.gitDir, projectId, slot);
30858
+ const env = gitEnv();
30859
+ const gitIn = (dir, args, timeout = GIT_TIMEOUT_MS) => exec("git", [...SAFE_GIT, "-C", dir, ...args], { env, timeout });
30860
+ if (existsSync4(path7.join(bare, "HEAD"))) {
30861
+ await gitIn(bare, ["remote", "set-url", "--", "origin", g.cloneUrl]);
30862
+ await exec("git", [...SAFE_GIT, "-C", bare, "fetch", "origin", "--prune", "+refs/heads/*:refs/heads/*"], { env: githubAuthEnv(await token()), timeout: CLONE_TIMEOUT_MS });
30342
30863
  } else {
30343
- mkdirSync5(projectsDir, { recursive: true, mode: 448 });
30344
- await exec("git", ["clone", g.cloneUrl, folder], { cwd: projectsDir, env, timeout: CLONE_TIMEOUT_MS });
30864
+ mkdirSync6(dirs.gitDir, { recursive: true, mode: 448 });
30865
+ await exec("git", [...SAFE_GIT, "clone", "--bare", "--", g.cloneUrl, bare], { cwd: dirs.gitDir, env: githubAuthEnv(await token()), timeout: CLONE_TIMEOUT_MS });
30345
30866
  }
30346
- const { stdout } = await git(["rev-parse", "--abbrev-ref", "HEAD"]);
30867
+ if (exists(folder)) {
30868
+ const snap2 = readSavedSnapshot(saved);
30869
+ let ok = snap2 !== null;
30870
+ if (snap2) {
30871
+ try {
30872
+ verifyGit(folder, snap2);
30873
+ } catch {
30874
+ ok = false;
30875
+ }
30876
+ }
30877
+ if (!ok) rmSync4(folder, { recursive: true, force: true });
30878
+ }
30879
+ if (existsSync4(path7.join(folder, ".git"))) {
30880
+ await gitIn(folder, ["fetch", "origin", "--prune"]);
30881
+ } else {
30882
+ mkdirSync6(slotDir, { recursive: true, mode: 448 });
30883
+ await exec("git", [...SAFE_GIT, "clone", "--no-hardlinks", "--", bare, folder], { cwd: slotDir, env, timeout: CLONE_TIMEOUT_MS });
30884
+ }
30885
+ const { stdout } = await gitIn(folder, ["rev-parse", "--abbrev-ref", "HEAD"]);
30347
30886
  if (stdout.trim() !== g.branch) {
30348
- await git(["reset", "--hard"]);
30349
- await git(["clean", "-fd"]);
30350
- await git(["checkout", "-B", g.branch, `origin/${g.fromBranch ?? g.defaultBranch}`]);
30887
+ await gitIn(folder, ["reset", "--hard"]);
30888
+ await gitIn(folder, ["clean", "-fd"]);
30889
+ await gitIn(folder, ["checkout", "-B", g.branch, `origin/${g.fromBranch ?? g.defaultBranch}`]);
30890
+ }
30891
+ const snap = snapshotGit(folder);
30892
+ saveSnapshot(dirs.gitDir, projectId, slot, snap);
30893
+ return { folder, snap };
30894
+ }
30895
+ var savedSnapshotPath = (gitDir, projectId, slot) => path7.join(gitDir, `${projectId}.${slot}.worktree.json`);
30896
+ function saveSnapshot(gitDir, projectId, slot, snap) {
30897
+ if (snap.missing) return;
30898
+ mkdirSync6(gitDir, { recursive: true, mode: 448 });
30899
+ const body = { gitRealPath: snap.real, dev: snap.dev, ino: snap.ino, configHash: snap.configHash };
30900
+ writeFileSync5(savedSnapshotPath(gitDir, projectId, slot), JSON.stringify(body) + "\n", { mode: 384 });
30901
+ }
30902
+ function forgetSnapshot(gitDir, projectId, slot) {
30903
+ rmSync4(savedSnapshotPath(gitDir, projectId, slot), { force: true });
30904
+ }
30905
+ function readSavedSnapshot(file2) {
30906
+ try {
30907
+ const j = JSON.parse(readFileSync2(file2, "utf8"));
30908
+ if (typeof j.gitRealPath !== "string" || typeof j.dev !== "number" || typeof j.ino !== "number" || typeof j.configHash !== "string") return null;
30909
+ return { missing: false, real: j.gitRealPath, dev: j.dev, ino: j.ino, configHash: j.configHash };
30910
+ } catch {
30911
+ return null;
30912
+ }
30913
+ }
30914
+ async function pushBranch(folder, bare, branch, token, exec = execFileAsync) {
30915
+ const env = gitEnv();
30916
+ await exec("git", [...SAFE_GIT, "-C", bare, "fetch", folder, `+refs/heads/${branch}:refs/heads/${branch}`], { env, timeout: GIT_TIMEOUT_MS }).catch(gitError);
30917
+ const auth = githubAuthEnv(await token());
30918
+ await exec("git", [...SAFE_GIT, "-C", bare, "push", "--no-verify", "origin", branch], { env: auth, timeout: GIT_TIMEOUT_MS }).catch(gitError);
30919
+ }
30920
+ function gitError(e) {
30921
+ const stderr = typeof e.stderr === "string" ? e.stderr : "";
30922
+ const line = stderr.split("\n").map((l) => l.trim()).find(Boolean);
30923
+ throw new Error(line ?? "git push failed");
30924
+ }
30925
+ var RepoTamperedError = class extends Error {
30926
+ constructor(detail) {
30927
+ super("repo_tampered");
30928
+ this.detail = detail;
30929
+ }
30930
+ detail;
30931
+ };
30932
+ function snapshotGit(folder) {
30933
+ const dir = path7.join(folder, ".git");
30934
+ let st;
30935
+ try {
30936
+ st = lstatSync2(dir);
30937
+ } catch {
30938
+ return { missing: true };
30939
+ }
30940
+ if (!st.isDirectory()) throw new RepoTamperedError(".git is not a folder");
30941
+ return { missing: false, ino: st.ino, dev: st.dev, real: realpathSync2(dir), configHash: configHash(dir) };
30942
+ }
30943
+ function verifyGit(folder, snap) {
30944
+ const dir = path7.join(folder, ".git");
30945
+ let st;
30946
+ try {
30947
+ st = lstatSync2(dir);
30948
+ } catch {
30949
+ if (snap.missing) return;
30950
+ throw new RepoTamperedError(".git is gone");
30951
+ }
30952
+ if (snap.missing) throw new RepoTamperedError(".git appeared");
30953
+ if (st.isSymbolicLink() || !st.isDirectory()) throw new RepoTamperedError(".git is not a folder");
30954
+ if (st.ino !== snap.ino || st.dev !== snap.dev || realpathSync2(dir) !== snap.real) throw new RepoTamperedError(".git was replaced");
30955
+ if (existsSync4(path7.join(dir, "commondir"))) throw new RepoTamperedError(".git has a commondir file");
30956
+ for (const f of [path7.join("objects", "info", "alternates"), path7.join("objects", "info", "http-alternates"), "worktrees"]) {
30957
+ if (exists(path7.join(dir, f))) throw new RepoTamperedError(`.git/${f} exists`);
30958
+ }
30959
+ const link = findSymlink(dir);
30960
+ if (link) throw new RepoTamperedError(`.git/${link} is a symlink`);
30961
+ if (configHash(dir) !== snap.configHash) throw new RepoTamperedError(".git/config changed");
30962
+ }
30963
+ var WALK_MAX_DEPTH = 12;
30964
+ var WALK_MAX_ENTRIES = 2e5;
30965
+ function findSymlink(dir) {
30966
+ let count = 0;
30967
+ const walk = (rel, depth) => {
30968
+ if (depth > WALK_MAX_DEPTH) throw new RepoTamperedError(`.git is nested too deep: ${rel}`);
30969
+ let entries;
30970
+ try {
30971
+ entries = readdirSync3(path7.join(dir, rel), { withFileTypes: true });
30972
+ } catch {
30973
+ throw new RepoTamperedError(`unreadable entry in .git: ${rel || "."}`);
30974
+ }
30975
+ count += entries.length;
30976
+ if (count > WALK_MAX_ENTRIES) throw new RepoTamperedError(".git has too many entries");
30977
+ for (const e of entries) {
30978
+ const child = rel ? `${rel}/${e.name}` : e.name;
30979
+ if (e.isSymbolicLink()) return child;
30980
+ if (e.isDirectory()) {
30981
+ const found = walk(child, depth + 1);
30982
+ if (found) return found;
30983
+ }
30984
+ }
30985
+ return null;
30986
+ };
30987
+ return walk("", 0);
30988
+ }
30989
+ function configHash(gitDir) {
30990
+ const file2 = path7.join(gitDir, "config");
30991
+ let st;
30992
+ try {
30993
+ st = lstatSync2(file2);
30994
+ } catch {
30995
+ return "none";
30996
+ }
30997
+ if (!st.isFile()) throw new RepoTamperedError(".git/config is not a file");
30998
+ return createHash("sha256").update(readFileSync2(file2)).digest("hex");
30999
+ }
31000
+ function exists(p) {
31001
+ try {
31002
+ lstatSync2(p);
31003
+ return true;
31004
+ } catch {
31005
+ return false;
30351
31006
  }
30352
- return folder;
30353
31007
  }
30354
31008
 
30355
31009
  // src/scrub.ts
@@ -30421,12 +31075,12 @@ function resolveRoute(siteUrl, route) {
30421
31075
  return { error: "route is not a valid URL path" };
30422
31076
  }
30423
31077
  if (url2.origin !== site.origin || !url2.pathname.startsWith(prefix + "/wp/v2/")) return { error: "route must stay under /wp/v2/" };
30424
- const path11 = url2.pathname.slice(prefix.length);
30425
- return { path: path11, route: path11 + url2.search, query: url2.searchParams };
31078
+ const path13 = url2.pathname.slice(prefix.length);
31079
+ return { path: path13, route: path13 + url2.search, query: url2.searchParams };
30426
31080
  }
30427
31081
 
30428
31082
  // src/wp/backup.ts
30429
- import path6 from "node:path";
31083
+ import path8 from "node:path";
30430
31084
 
30431
31085
  // src/wp/transport.ts
30432
31086
  var shellQuote = (a) => /^[A-Za-z0-9_\-.,/:=@%+]+$/.test(a) ? a : `'${a.replace(/'/g, `'\\''`)}'`;
@@ -30445,7 +31099,7 @@ async function backupBefore(t, root) {
30445
31099
  const wp = await dump(`{ wp db export - --path=${shellQuote(root)} || echo ${FAILED} >&2; } | gzip`);
30446
31100
  if (wp === "overflow") throw new Error("backup_failed: the database dump is over 200 MB");
30447
31101
  if (wp) return { dump: wp, how: "wp-cli" };
30448
- const config2 = await t.getFile(path6.posix.join(root, "wp-config.php")).catch(() => null);
31102
+ const config2 = await t.getFile(path8.posix.join(root, "wp-config.php")).catch(() => null);
30449
31103
  const db = config2 ? dbCredentials(config2) : null;
30450
31104
  if (!db) throw new Error("backup_failed: no wp-cli export and no database credentials in wp-config.php");
30451
31105
  const [host, port] = splitHost(db.host);
@@ -30577,7 +31231,7 @@ async function ftpTransport(conn) {
30577
31231
  // src/wp/helper.ts
30578
31232
  import { randomBytes, timingSafeEqual } from "node:crypto";
30579
31233
  import http from "node:http";
30580
- import path7 from "node:path";
31234
+ import path9 from "node:path";
30581
31235
 
30582
31236
  // ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/helpers/util.js
30583
31237
  var util;
@@ -30953,8 +31607,8 @@ function getErrorMap() {
30953
31607
 
30954
31608
  // ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/helpers/parseUtil.js
30955
31609
  var makeIssue = (params) => {
30956
- const { data, path: path11, errorMaps, issueData } = params;
30957
- const fullPath = [...path11, ...issueData.path || []];
31610
+ const { data, path: path13, errorMaps, issueData } = params;
31611
+ const fullPath = [...path13, ...issueData.path || []];
30958
31612
  const fullIssue = {
30959
31613
  ...issueData,
30960
31614
  path: fullPath
@@ -31069,11 +31723,11 @@ var errorUtil;
31069
31723
 
31070
31724
  // ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/types.js
31071
31725
  var ParseInputLazyPath = class {
31072
- constructor(parent, value, path11, key) {
31726
+ constructor(parent, value, path13, key) {
31073
31727
  this._cachedPath = [];
31074
31728
  this.parent = parent;
31075
31729
  this.data = value;
31076
- this._path = path11;
31730
+ this._path = path13;
31077
31731
  this._key = key;
31078
31732
  }
31079
31733
  get path() {
@@ -35028,10 +35682,10 @@ function mergeDefs(...defs) {
35028
35682
  function cloneDef(schema) {
35029
35683
  return mergeDefs(schema._zod.def);
35030
35684
  }
35031
- function getElementAtPath(obj, path11) {
35032
- if (!path11)
35685
+ function getElementAtPath(obj, path13) {
35686
+ if (!path13)
35033
35687
  return obj;
35034
- return path11.reduce((acc, key) => acc?.[key], obj);
35688
+ return path13.reduce((acc, key) => acc?.[key], obj);
35035
35689
  }
35036
35690
  function promiseAllObject(promisesObj) {
35037
35691
  const keys = Object.keys(promisesObj);
@@ -35371,11 +36025,11 @@ function explicitlyAborted(x, startIndex = 0) {
35371
36025
  }
35372
36026
  return false;
35373
36027
  }
35374
- function prefixIssues(path11, issues) {
36028
+ function prefixIssues(path13, issues) {
35375
36029
  return issues.map((iss) => {
35376
36030
  var _a3;
35377
36031
  (_a3 = iss).path ?? (_a3.path = []);
35378
- iss.path.unshift(path11);
36032
+ iss.path.unshift(path13);
35379
36033
  return iss;
35380
36034
  });
35381
36035
  }
@@ -35837,16 +36491,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
35837
36491
  }
35838
36492
  function formatError(error62, mapper = (issue2) => issue2.message) {
35839
36493
  const fieldErrors = { _errors: [] };
35840
- const processError = (error63, path11 = []) => {
36494
+ const processError = (error63, path13 = []) => {
35841
36495
  for (const issue2 of error63.issues) {
35842
36496
  if (issue2.code === "invalid_union" && issue2.errors.length) {
35843
- issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
36497
+ issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
35844
36498
  } else if (issue2.code === "invalid_key") {
35845
- processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
36499
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
35846
36500
  } else if (issue2.code === "invalid_element") {
35847
- processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
36501
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
35848
36502
  } else {
35849
- const fullpath = [...path11, ...issue2.path];
36503
+ const fullpath = [...path13, ...issue2.path];
35850
36504
  if (fullpath.length === 0) {
35851
36505
  fieldErrors._errors.push(mapper(issue2));
35852
36506
  } else {
@@ -35885,17 +36539,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
35885
36539
  }
35886
36540
  function treeifyError(error62, mapper = (issue2) => issue2.message) {
35887
36541
  const result = { errors: [] };
35888
- const processError = (error63, path11 = []) => {
36542
+ const processError = (error63, path13 = []) => {
35889
36543
  var _a3;
35890
36544
  for (const issue2 of error63.issues) {
35891
36545
  if (issue2.code === "invalid_union" && issue2.errors.length) {
35892
- issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
36546
+ issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
35893
36547
  } else if (issue2.code === "invalid_key") {
35894
- processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
36548
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
35895
36549
  } else if (issue2.code === "invalid_element") {
35896
- processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
36550
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
35897
36551
  } else {
35898
- const fullpath = [...path11, ...issue2.path];
36552
+ const fullpath = [...path13, ...issue2.path];
35899
36553
  if (fullpath.length === 0) {
35900
36554
  result.errors.push(mapper(issue2));
35901
36555
  continue;
@@ -35934,8 +36588,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
35934
36588
  }
35935
36589
  function toDotPath(_path) {
35936
36590
  const segs = [];
35937
- const path11 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
35938
- for (const seg of path11) {
36591
+ const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
36592
+ for (const seg of path13) {
35939
36593
  if (typeof seg === "number")
35940
36594
  segs.push(`[${seg}]`);
35941
36595
  else if (typeof seg === "symbol")
@@ -39948,8 +40602,8 @@ var error3 = () => {
39948
40602
  const sizing = getSizing(issue2.origin);
39949
40603
  if (sizing) {
39950
40604
  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}`;
40605
+ const unit2 = getBelarusianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
40606
+ 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
40607
  }
39954
40608
  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
40609
  }
@@ -39958,8 +40612,8 @@ var error3 = () => {
39958
40612
  const sizing = getSizing(issue2.origin);
39959
40613
  if (sizing) {
39960
40614
  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}`;
40615
+ const unit2 = getBelarusianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
40616
+ 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
40617
  }
39964
40618
  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
40619
  }
@@ -42586,8 +43240,8 @@ var error24 = () => {
42586
43240
  const sizing = getSizing(issue2.origin);
42587
43241
  if (sizing) {
42588
43242
  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}`;
43243
+ const unit2 = getArmenianPlural(maxValue, sizing.unit.one, sizing.unit.many);
43244
+ 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
43245
  }
42592
43246
  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
43247
  }
@@ -42596,8 +43250,8 @@ var error24 = () => {
42596
43250
  const sizing = getSizing(issue2.origin);
42597
43251
  if (sizing) {
42598
43252
  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}`;
43253
+ const unit2 = getArmenianPlural(minValue, sizing.unit.one, sizing.unit.many);
43254
+ 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
43255
  }
42602
43256
  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
43257
  }
@@ -43511,18 +44165,18 @@ var error32 = () => {
43511
44165
  const adj = issue2.inclusive ? "\uC774\uD558" : "\uBBF8\uB9CC";
43512
44166
  const suffix = adj === "\uBBF8\uB9CC" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
43513
44167
  const sizing = getSizing(issue2.origin);
43514
- const unit = sizing?.unit ?? "\uC694\uC18C";
44168
+ const unit2 = sizing?.unit ?? "\uC694\uC18C";
43515
44169
  if (sizing)
43516
- return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()}${unit} ${adj}${suffix}`;
44170
+ return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()}${unit2} ${adj}${suffix}`;
43517
44171
  return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()} ${adj}${suffix}`;
43518
44172
  }
43519
44173
  case "too_small": {
43520
44174
  const adj = issue2.inclusive ? "\uC774\uC0C1" : "\uCD08\uACFC";
43521
44175
  const suffix = adj === "\uC774\uC0C1" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
43522
44176
  const sizing = getSizing(issue2.origin);
43523
- const unit = sizing?.unit ?? "\uC694\uC18C";
44177
+ const unit2 = sizing?.unit ?? "\uC694\uC18C";
43524
44178
  if (sizing) {
43525
- return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()}${unit} ${adj}${suffix}`;
44179
+ return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()}${unit2} ${adj}${suffix}`;
43526
44180
  }
43527
44181
  return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()} ${adj}${suffix}`;
43528
44182
  }
@@ -45335,8 +45989,8 @@ var error46 = () => {
45335
45989
  const sizing = getSizing(issue2.origin);
45336
45990
  if (sizing) {
45337
45991
  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}`;
45992
+ const unit2 = getRussianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
45993
+ 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
45994
  }
45341
45995
  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
45996
  }
@@ -45345,8 +45999,8 @@ var error46 = () => {
45345
45999
  const sizing = getSizing(issue2.origin);
45346
46000
  if (sizing) {
45347
46001
  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}`;
46002
+ const unit2 = getRussianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
46003
+ 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
46004
  }
45351
46005
  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
46006
  }
@@ -47432,9 +48086,9 @@ function codePointLengthVar(doc, ctx, accessor, inDoubt) {
47432
48086
  doc.write(`const ${v} = typeof ${accessor} === "string" && ${inDoubt} ? ${cpLen}(${accessor}) : ${accessor}.length;`);
47433
48087
  return v;
47434
48088
  }
47435
- function numericOperand(value, label) {
48089
+ function numericOperand(value, label2) {
47436
48090
  if (typeof value !== "number" || !Number.isFinite(value)) {
47437
- throw new ZodCompileUnsupportedError(`${label} bound of type ${typeof value}`);
48091
+ throw new ZodCompileUnsupportedError(`${label2} bound of type ${typeof value}`);
47438
48092
  }
47439
48093
  return `${value}`;
47440
48094
  }
@@ -51486,11 +52140,11 @@ function normalizeObjectSchema(schema) {
51486
52140
  }
51487
52141
  return void 0;
51488
52142
  }
51489
- function getDotPath(path11) {
51490
- if (path11.length === 0) {
52143
+ function getDotPath(path13) {
52144
+ if (path13.length === 0) {
51491
52145
  return "object root";
51492
52146
  }
51493
- return path11.reduce((acc, seg, index) => {
52147
+ return path13.reduce((acc, seg, index) => {
51494
52148
  if (index === 0) {
51495
52149
  return String(seg);
51496
52150
  }
@@ -53717,13 +54371,13 @@ function resolveRef(ref, ctx) {
53717
54371
  if (!ref.startsWith("#")) {
53718
54372
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
53719
54373
  }
53720
- const path11 = ref.slice(1).split("/").filter(Boolean);
53721
- if (path11.length === 0) {
54374
+ const path13 = ref.slice(1).split("/").filter(Boolean);
54375
+ if (path13.length === 0) {
53722
54376
  return ctx.rootSchema;
53723
54377
  }
53724
54378
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
53725
- if (path11[0] === defsKey) {
53726
- const key = path11[1] === void 0 ? void 0 : decodeJSONPointerSegment(path11[1]);
54379
+ if (path13[0] === defsKey) {
54380
+ const key = path13[1] === void 0 ? void 0 : decodeJSONPointerSegment(path13[1]);
53727
54381
  if (!key || !ctx.defs[key]) {
53728
54382
  throw new Error(`Reference not found: ${ref}`);
53729
54383
  }
@@ -57311,12 +57965,12 @@ var Protocol = class {
57311
57965
  this._taskMessageQueue = _options?.taskMessageQueue;
57312
57966
  if (this._taskStore) {
57313
57967
  this.setRequestHandler(GetTaskRequestSchema, async (request, extra) => {
57314
- const task = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
57315
- if (!task) {
57968
+ const task2 = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
57969
+ if (!task2) {
57316
57970
  throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
57317
57971
  }
57318
57972
  return {
57319
- ...task
57973
+ ...task2
57320
57974
  };
57321
57975
  });
57322
57976
  this.setRequestHandler(GetTaskPayloadRequestSchema, async (request, extra) => {
@@ -57350,15 +58004,15 @@ var Protocol = class {
57350
58004
  await this._transport?.send(queuedMessage.message, { relatedRequestId: extra.requestId });
57351
58005
  }
57352
58006
  }
57353
- const task = await this._taskStore.getTask(taskId, extra.sessionId);
57354
- if (!task) {
58007
+ const task2 = await this._taskStore.getTask(taskId, extra.sessionId);
58008
+ if (!task2) {
57355
58009
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
57356
58010
  }
57357
- if (!isTerminal(task.status)) {
58011
+ if (!isTerminal(task2.status)) {
57358
58012
  await this._waitForTaskUpdate(taskId, extra.signal, extra.sessionId);
57359
58013
  return await handleTaskResult();
57360
58014
  }
57361
- if (isTerminal(task.status)) {
58015
+ if (isTerminal(task2.status)) {
57362
58016
  const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
57363
58017
  this._clearTaskQueue(taskId, extra.sessionId);
57364
58018
  return {
@@ -57389,12 +58043,12 @@ var Protocol = class {
57389
58043
  });
57390
58044
  this.setRequestHandler(CancelTaskRequestSchema, async (request, extra) => {
57391
58045
  try {
57392
- const task = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
57393
- if (!task) {
58046
+ const task2 = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
58047
+ if (!task2) {
57394
58048
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${request.params.taskId}`);
57395
58049
  }
57396
- if (isTerminal(task.status)) {
57397
- throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
58050
+ if (isTerminal(task2.status)) {
58051
+ throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task2.status}`);
57398
58052
  }
57399
58053
  await this._taskStore.updateTaskStatus(request.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
57400
58054
  this._clearTaskQueue(request.params.taskId, extra.sessionId);
@@ -57701,10 +58355,10 @@ var Protocol = class {
57701
58355
  if (isJSONRPCResultResponse(response) && response.result && typeof response.result === "object") {
57702
58356
  const result = response.result;
57703
58357
  if (result.task && typeof result.task === "object") {
57704
- const task = result.task;
57705
- if (typeof task.taskId === "string") {
58358
+ const task2 = result.task;
58359
+ if (typeof task2.taskId === "string") {
57706
58360
  isTaskResponse = true;
57707
- this._taskProgressTokens.set(task.taskId, messageId);
58361
+ this._taskProgressTokens.set(task2.taskId, messageId);
57708
58362
  }
57709
58363
  }
57710
58364
  }
@@ -57755,8 +58409,8 @@ var Protocol = class {
57755
58409
  * @experimental Use `client.experimental.tasks.requestStream()` to access this method.
57756
58410
  */
57757
58411
  async *requestStream(request, resultSchema, options) {
57758
- const { task } = options ?? {};
57759
- if (!task) {
58412
+ const { task: task2 } = options ?? {};
58413
+ if (!task2) {
57760
58414
  try {
57761
58415
  const result = await this.request(request, resultSchema, options);
57762
58416
  yield { type: "result", result };
@@ -57778,18 +58432,18 @@ var Protocol = class {
57778
58432
  throw new McpError(ErrorCode.InternalError, "Task creation did not return a task");
57779
58433
  }
57780
58434
  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") {
58435
+ const task3 = await this.getTask({ taskId }, options);
58436
+ yield { type: "taskStatus", task: task3 };
58437
+ if (isTerminal(task3.status)) {
58438
+ if (task3.status === "completed") {
57785
58439
  const result = await this.getTaskResult({ taskId }, resultSchema, options);
57786
58440
  yield { type: "result", result };
57787
- } else if (task2.status === "failed") {
58441
+ } else if (task3.status === "failed") {
57788
58442
  yield {
57789
58443
  type: "error",
57790
58444
  error: new McpError(ErrorCode.InternalError, `Task ${taskId} failed`)
57791
58445
  };
57792
- } else if (task2.status === "cancelled") {
58446
+ } else if (task3.status === "cancelled") {
57793
58447
  yield {
57794
58448
  type: "error",
57795
58449
  error: new McpError(ErrorCode.InternalError, `Task ${taskId} was cancelled`)
@@ -57797,12 +58451,12 @@ var Protocol = class {
57797
58451
  }
57798
58452
  return;
57799
58453
  }
57800
- if (task2.status === "input_required") {
58454
+ if (task3.status === "input_required") {
57801
58455
  const result = await this.getTaskResult({ taskId }, resultSchema, options);
57802
58456
  yield { type: "result", result };
57803
58457
  return;
57804
58458
  }
57805
- const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
58459
+ const pollInterval = task3.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
57806
58460
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
57807
58461
  options?.signal?.throwIfAborted();
57808
58462
  }
@@ -57819,7 +58473,7 @@ var Protocol = class {
57819
58473
  * Do not use this method to emit notifications! Use notification() instead.
57820
58474
  */
57821
58475
  request(request, resultSchema, options) {
57822
- const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
58476
+ const { relatedRequestId, resumptionToken, onresumptiontoken, task: task2, relatedTask } = options ?? {};
57823
58477
  return new Promise((resolve, reject) => {
57824
58478
  const earlyReject = (error62) => {
57825
58479
  reject(error62);
@@ -57831,7 +58485,7 @@ var Protocol = class {
57831
58485
  if (this._options?.enforceStrictCapabilities === true) {
57832
58486
  try {
57833
58487
  this.assertCapabilityForMethod(request.method);
57834
- if (task) {
58488
+ if (task2) {
57835
58489
  this.assertTaskCapability(request.method);
57836
58490
  }
57837
58491
  } catch (e) {
@@ -57856,10 +58510,10 @@ var Protocol = class {
57856
58510
  }
57857
58511
  };
57858
58512
  }
57859
- if (task) {
58513
+ if (task2) {
57860
58514
  jsonrpcRequest.params = {
57861
58515
  ...jsonrpcRequest.params,
57862
- task
58516
+ task: task2
57863
58517
  };
57864
58518
  }
57865
58519
  if (relatedTask) {
@@ -58154,9 +58808,9 @@ var Protocol = class {
58154
58808
  async _waitForTaskUpdate(taskId, signal, sessionId) {
58155
58809
  let interval = this._options?.defaultTaskPollInterval ?? 1e3;
58156
58810
  try {
58157
- const task = await this._taskStore?.getTask(taskId, sessionId);
58158
- if (task?.pollInterval) {
58159
- interval = task.pollInterval;
58811
+ const task2 = await this._taskStore?.getTask(taskId, sessionId);
58812
+ if (task2?.pollInterval) {
58813
+ interval = task2.pollInterval;
58160
58814
  }
58161
58815
  } catch {
58162
58816
  }
@@ -58188,22 +58842,22 @@ var Protocol = class {
58188
58842
  }, sessionId);
58189
58843
  },
58190
58844
  getTask: async (taskId) => {
58191
- const task = await taskStore.getTask(taskId, sessionId);
58192
- if (!task) {
58845
+ const task2 = await taskStore.getTask(taskId, sessionId);
58846
+ if (!task2) {
58193
58847
  throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
58194
58848
  }
58195
- return task;
58849
+ return task2;
58196
58850
  },
58197
58851
  storeTaskResult: async (taskId, status5, result) => {
58198
58852
  await taskStore.storeTaskResult(taskId, status5, result, sessionId);
58199
- const task = await taskStore.getTask(taskId, sessionId);
58200
- if (task) {
58853
+ const task2 = await taskStore.getTask(taskId, sessionId);
58854
+ if (task2) {
58201
58855
  const notification = TaskStatusNotificationSchema.parse({
58202
58856
  method: "notifications/tasks/status",
58203
- params: task
58857
+ params: task2
58204
58858
  });
58205
58859
  await this.notification(notification);
58206
- if (isTerminal(task.status)) {
58860
+ if (isTerminal(task2.status)) {
58207
58861
  this._cleanupTaskProgressHandler(taskId);
58208
58862
  }
58209
58863
  }
@@ -58212,12 +58866,12 @@ var Protocol = class {
58212
58866
  return taskStore.getTaskResult(taskId, sessionId);
58213
58867
  },
58214
58868
  updateTaskStatus: async (taskId, status5, statusMessage) => {
58215
- const task = await taskStore.getTask(taskId, sessionId);
58216
- if (!task) {
58869
+ const task2 = await taskStore.getTask(taskId, sessionId);
58870
+ if (!task2) {
58217
58871
  throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
58218
58872
  }
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.`);
58873
+ if (isTerminal(task2.status)) {
58874
+ 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
58875
  }
58222
58876
  await taskStore.updateTaskStatus(taskId, status5, statusMessage, sessionId);
58223
58877
  const updatedTask = await taskStore.getTask(taskId, sessionId);
@@ -59293,15 +59947,15 @@ var McpServer = class {
59293
59947
  await Promise.resolve(handler.createTask(taskExtra))
59294
59948
  );
59295
59949
  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") {
59950
+ let task2 = createTaskResult.task;
59951
+ const pollInterval = task2.pollInterval ?? 5e3;
59952
+ while (task2.status !== "completed" && task2.status !== "failed" && task2.status !== "cancelled") {
59299
59953
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
59300
59954
  const updatedTask = await extra.taskStore.getTask(taskId);
59301
59955
  if (!updatedTask) {
59302
59956
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
59303
59957
  }
59304
- task = updatedTask;
59958
+ task2 = updatedTask;
59305
59959
  }
59306
59960
  return await extra.taskStore.getTaskResult(taskId);
59307
59961
  }
@@ -62045,23 +62699,23 @@ function isAllowed(args, allow) {
62045
62699
  const [cmd, sub] = words;
62046
62700
  if (!cmd) return { ok: false, reason: "no command" };
62047
62701
  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` };
62702
+ const label2 = sub ? `wp ${cmd} ${sub}` : `wp ${cmd}`;
62703
+ if (NEVER2.some((r) => matches(r, cmd, sub))) return { ok: false, reason: `${label2} is never allowed unless "Allow everything" is on` };
62050
62704
  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` };
62705
+ if (!rule) return { ok: false, reason: `${label2} is not in the allowed groups` };
62706
+ if (!allow[rule[2]]) return { ok: false, reason: `${label2} needs the ${rule[2]} group, which is off for this project` };
62053
62707
  const rest = words.slice(2);
62054
62708
  if (cmd === "post" && sub !== "meta" && sub !== "term") {
62055
62709
  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` };
62710
+ 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
62711
  }
62058
62712
  if (cmd === "media") {
62059
62713
  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` };
62714
+ if (bad !== void 0) return { ok: false, reason: `${label2} takes http(s) URLs only, not "${bad}": refused unless "Allow everything" is on` };
62061
62715
  }
62062
62716
  if (cmd === "option") {
62063
62717
  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` };
62718
+ if (!name || !OPTIONS.has(name)) return { ok: false, reason: `${label2} ${name ?? ""} is refused: only ${[...OPTIONS].join(", ")} unless "Allow everything" is on` };
62065
62719
  }
62066
62720
  return { ok: true };
62067
62721
  }
@@ -62171,7 +62825,7 @@ function buildServer(t) {
62171
62825
  if (!t.allow.files && !t.allow.all) return fail("the files group is off for this project");
62172
62826
  if (t.root === null) return fail("wordpress_root_not_found: the WordPress folder was not found on the server");
62173
62827
  const bad = pathRefusal(p, t.allow.all);
62174
- return bad ? fail(bad) : path7.posix.join(t.root, p);
62828
+ return bad ? fail(bad) : path9.posix.join(t.root, p);
62175
62829
  };
62176
62830
  server.registerTool("wp_get_file", {
62177
62831
  description: "Reads a file of the WordPress site, by its path relative to the WordPress folder (e.g. wp-content/themes/x/style.css).",
@@ -62204,7 +62858,7 @@ function buildServer(t) {
62204
62858
  if (!verdict.ok) return fail(`refused: ${verdict.reason}`);
62205
62859
  if (t.root === null) return fail("wordpress_root_not_found: the WordPress folder was not found on the server");
62206
62860
  const wp = (extra) => run2(["wp", ...args, ...extra, `--path=${t.root}`, "--no-color"]);
62207
- const show = (label, r2) => `${label}exit ${r2.code}
62861
+ const show = (label2, r2) => `${label2}exit ${r2.code}
62208
62862
  ${r2.stdout.toString("utf8")}${r2.stderr ? `
62209
62863
  ${r2.stderr}` : ""}`;
62210
62864
  t.log.info(`helper wp_run ${scrub(args.slice(0, 2).join(" "), t.secrets)}`);
@@ -62243,9 +62897,9 @@ function apiRefusal(route, query, method, body, allow) {
62243
62897
  }
62244
62898
  function pathRefusal(p, all) {
62245
62899
  if (p.startsWith("/") || p.includes("\\") || p.includes("\0")) return "path must be relative to the WordPress folder";
62246
- const norm = path7.posix.normalize(p);
62900
+ const norm = path9.posix.normalize(p);
62247
62901
  if (norm === ".." || norm.startsWith("../") || p.split("/").includes("..")) return "path must stay inside the WordPress folder";
62248
- if (!all && PROTECTED_FILES.has(path7.posix.basename(norm))) return `refused: ${path7.posix.basename(norm)} is never changed unless "Allow everything" is on`;
62902
+ if (!all && PROTECTED_FILES.has(path9.posix.basename(norm))) return `refused: ${path9.posix.basename(norm)} is never changed unless "Allow everything" is on`;
62249
62903
  return null;
62250
62904
  }
62251
62905
  function publishedLink(json3) {
@@ -62257,7 +62911,7 @@ var cut2 = (s) => s.length > MAX_OUTPUT ? `${s.slice(0, MAX_OUTPUT)}
62257
62911
  [cut at ${MAX_OUTPUT} characters]` : s;
62258
62912
 
62259
62913
  // src/wp/root.ts
62260
- import path8 from "node:path";
62914
+ import path10 from "node:path";
62261
62915
  var FTP_CANDIDATES = [".", "public_html", "www", "htdocs", "httpdocs", "html", "web"];
62262
62916
  async function findRoot(t) {
62263
62917
  if (t.run) {
@@ -62265,10 +62919,10 @@ async function findRoot(t) {
62265
62919
  if (home?.code === 0) return ".";
62266
62920
  const found = await t.run(["find", ".", "-maxdepth", "3", "-name", "wp-config.php"]).catch(() => null);
62267
62921
  const first = found?.stdout.toString("utf8").split("\n").map((l) => l.trim()).filter(Boolean).sort((a, b) => a.split("/").length - b.split("/").length)[0];
62268
- return first ? path8.posix.normalize(path8.posix.dirname(first)) : null;
62922
+ return first ? path10.posix.normalize(path10.posix.dirname(first)) : null;
62269
62923
  }
62270
62924
  for (const dir of FTP_CANDIDATES) {
62271
- if (await t.getFile(path8.posix.join(dir, "wp-config.php")).catch(() => null) !== null) return dir;
62925
+ if (await t.getFile(path10.posix.join(dir, "wp-config.php")).catch(() => null) !== null) return dir;
62272
62926
  }
62273
62927
  return null;
62274
62928
  }
@@ -62367,29 +63021,45 @@ async function runOne(spec, deps) {
62367
63021
  return ms <= 0 ? 0 : Math.max(1, Math.ceil(ms / 6e4));
62368
63022
  };
62369
63023
  const sendDone = (input2) => doneWithRetry(spec.runId, input2, api, log, sleep);
63024
+ if (engineTooOld(deps.sandbox.engineVersion)) {
63025
+ log.error(`run ${spec.runId}: Claude Code ${deps.sandbox.engineVersion ?? "(unknown version)"} is too old; update it (claude update)`);
63026
+ await sendDone({ outcome: "failed", error: "engine_too_old", costUsd: 0, inputTokens: 0, outputTokens: 0 }).catch((e) => log.warn(`run ${spec.runId}: done failed: ${errorText2(e)}`));
63027
+ return;
63028
+ }
62370
63029
  const secrets = [
62371
63030
  spec.mcp.accessToken,
62372
63031
  spec.mcp.refreshToken,
62373
63032
  spec.wordpress?.apiPassword,
62374
63033
  spec.wordpress?.ssh?.privateKey,
62375
63034
  spec.wordpress?.ssh?.password,
62376
- spec.wordpress?.ftp?.password
63035
+ spec.wordpress?.ftp?.password,
63036
+ ...tokenForms(spec.github?.token)
62377
63037
  ].filter((s) => Boolean(s));
63038
+ const gitToken = spec.github ? githubToken(spec.runId, spec.github, api, secrets, now) : void 0;
62378
63039
  const totals = { costUsd: 0, inputTokens: 0, outputTokens: 0 };
62379
63040
  const events = transcript(spec.runId, api, log, secrets, now, totals);
62380
63041
  let helper;
62381
63042
  let tempDir;
63043
+ let settingsFile;
63044
+ let clone2;
62382
63045
  let pendingDone = true;
63046
+ const slot = deps.slot ?? 0;
63047
+ let started = false;
62383
63048
  try {
63049
+ if (!/^[\w-]+$/.test(spec.projectId)) throw new Error("runner_error");
62384
63050
  let cwd;
62385
63051
  if (spec.kind === "github" && spec.github) {
62386
- cwd = await prepareGithubFolder(deps.paths.projectsDir, spec.projectId, spec.github, deps.exec);
63052
+ started = true;
63053
+ clone2 = await prepareGithubFolder({ ...deps.paths, slot }, spec.projectId, spec.github, gitToken, deps.exec);
63054
+ cwd = clone2.folder;
62387
63055
  } else {
62388
- const parent = path9.join(deps.paths.projectsDir, spec.projectId);
62389
- mkdirSync6(parent, { recursive: true, mode: 448 });
62390
- cwd = tempDir = mkdtempSync(path9.join(parent, "run-"));
63056
+ started = true;
63057
+ const parent = projectFolder(deps.paths.projectsDir, slot, spec.projectId);
63058
+ mkdirSync7(parent, { recursive: true, mode: 448 });
63059
+ cwd = tempDir = mkdtempSync(path11.join(parent, "run-"));
62391
63060
  if (deps.helperFactory) helper = await deps.helperFactory(spec);
62392
63061
  }
63062
+ settingsFile = writeSettings(spec, deps, cwd);
62393
63063
  const base = {
62394
63064
  cwd,
62395
63065
  systemPrompt: spec.systemPrompt,
@@ -62397,7 +63067,8 @@ async function runOne(spec, deps) {
62397
63067
  helper: helper ? { url: helper.url, token: helper.token } : void 0,
62398
63068
  env: {},
62399
63069
  maxMinutes: spec.maxRunMinutes,
62400
- claudeConfigDir: deps.paths.claudeConfigDir
63070
+ claudeConfigDir: deps.paths.claudeConfigDir,
63071
+ settingsFile
62401
63072
  };
62402
63073
  log.info(`run ${spec.runId}: start`);
62403
63074
  const timedOut = (sessionId) => ({ outcome: "failed", error: "timeout", costUsd: 0, inputTokens: 0, outputTokens: 0, sessionId });
@@ -62410,10 +63081,26 @@ async function runOne(spec, deps) {
62410
63081
  if (result.sessionId) totals.sessionId = result.sessionId;
62411
63082
  const site = skipped ? void 0 : await helper?.checkSite?.();
62412
63083
  await events.flush(totals);
62413
- const input2 = site && site !== "ok" ? { outcome: "failed", error: site, ...totals } : doneInput(result, totals, secrets);
63084
+ let input2 = site && site !== "ok" ? { outcome: "failed", error: site, ...totals } : doneInput(result, totals, secrets);
63085
+ let pushFailed = false;
63086
+ if (input2.outcome === "reported" && spec.kind === "github" && spec.github) {
63087
+ try {
63088
+ if (clone2) verifyGit(clone2.folder, clone2.snap);
63089
+ await pushBranch(cwd, barePath(deps.paths.gitDir, spec.projectId), spec.github.branch, gitToken, deps.exec);
63090
+ } catch (e) {
63091
+ pushFailed = true;
63092
+ if (e instanceof RepoTamperedError) {
63093
+ log.error(`run ${spec.runId}: not pushed: the run changed the repository's own settings (${e.detail})`);
63094
+ input2 = { outcome: "failed", error: "repo_tampered", ...totals };
63095
+ } else {
63096
+ log.error(`run ${spec.runId}: push failed: ${scrub(errorText2(e), secrets).slice(0, 200)}`);
63097
+ input2 = { outcome: "failed", error: "push_failed", ...totals };
63098
+ }
63099
+ }
63100
+ }
62414
63101
  const answer = await sendDone(input2);
62415
63102
  log.info(`run ${spec.runId}: done ${input2.outcome}${input2.error ? ` ${input2.error}` : ""}`);
62416
- if (!answer.followUp || skipped) {
63103
+ if (!answer.followUp || skipped || pushFailed) {
62417
63104
  pendingDone = false;
62418
63105
  break;
62419
63106
  }
@@ -62433,7 +63120,7 @@ async function runOne(spec, deps) {
62433
63120
  log.error(`run ${spec.runId}: ${message}`);
62434
63121
  if (pendingDone && helper?.checkSite) {
62435
63122
  const site = await helper.checkSite().catch((e) => {
62436
- log.warn(`run ${spec.runId}: site check failed: ${errorText(e)}`);
63123
+ log.warn(`run ${spec.runId}: site check failed: ${errorText2(e)}`);
62437
63124
  return "ok";
62438
63125
  });
62439
63126
  if (site !== "ok") message = site;
@@ -62441,20 +63128,98 @@ async function runOne(spec, deps) {
62441
63128
  if (pendingDone) {
62442
63129
  const usage = events.usage();
62443
63130
  await events.flush();
62444
- await sendDone({ outcome: "failed", error: message, ...usage }).catch((e) => log.warn(`run ${spec.runId}: done failed: ${errorText(e)}`));
63131
+ await sendDone({ outcome: "failed", error: message, ...usage }).catch((e) => log.warn(`run ${spec.runId}: done failed: ${errorText2(e)}`));
62445
63132
  }
62446
63133
  } finally {
62447
63134
  events.stop();
62448
- if (helper) await helper.stop().catch((e) => log.warn(`run ${spec.runId}: helper stop failed: ${errorText(e)}`));
63135
+ if (helper) await helper.stop().catch((e) => log.warn(`run ${spec.runId}: helper stop failed: ${errorText2(e)}`));
63136
+ if (clone2) {
63137
+ try {
63138
+ verifyGit(clone2.folder, clone2.snap);
63139
+ saveSnapshot(deps.paths.gitDir, spec.projectId, slot, clone2.snap);
63140
+ } catch (e) {
63141
+ log.warn(`run ${spec.runId}: removing the project folder: ${e instanceof RepoTamperedError ? e.detail : errorText2(e)}`);
63142
+ try {
63143
+ forgetSnapshot(deps.paths.gitDir, spec.projectId, slot);
63144
+ } catch {
63145
+ }
63146
+ try {
63147
+ rmSync5(clone2.folder, { recursive: true, force: true });
63148
+ } catch (err) {
63149
+ log.warn(`run ${spec.runId}: could not remove ${clone2.folder}: ${errorText2(err)}`);
63150
+ }
63151
+ }
63152
+ }
63153
+ if (started) {
63154
+ try {
63155
+ touchLastRun(deps.paths.gitDir, spec.projectId, now());
63156
+ } catch (e) {
63157
+ log.warn(`run ${spec.runId}: could not write the last-run marker: ${errorText2(e)}`);
63158
+ }
63159
+ }
63160
+ if (settingsFile) {
63161
+ try {
63162
+ rmSync5(settingsFile, { force: true });
63163
+ } catch (e) {
63164
+ log.warn(`run ${spec.runId}: could not remove ${settingsFile}: ${errorText2(e)}`);
63165
+ }
63166
+ }
62449
63167
  if (tempDir) {
62450
63168
  try {
62451
- rmSync3(tempDir, { recursive: true, force: true });
63169
+ rmSync5(tempDir, { recursive: true, force: true });
62452
63170
  } catch (e) {
62453
- log.warn(`run ${spec.runId}: could not remove ${tempDir}: ${errorText(e)}`);
63171
+ log.warn(`run ${spec.runId}: could not remove ${tempDir}: ${errorText2(e)}`);
62454
63172
  }
62455
63173
  }
62456
63174
  }
62457
63175
  }
63176
+ var tokenForms = (token) => token ? [token, Buffer.from(`x-access-token:${token}`).toString("base64")] : [];
63177
+ var TOKEN_MARGIN_MS = 5 * 6e4;
63178
+ function githubToken(runId, g, api, secrets, now) {
63179
+ let token = g.token;
63180
+ let expiresAt = Date.parse(g.tokenExpiresAt);
63181
+ return async () => {
63182
+ if (!token || !Number.isFinite(expiresAt) || expiresAt - TOKEN_MARGIN_MS <= now().getTime()) {
63183
+ const fresh = await api.gitToken(runId);
63184
+ if (typeof fresh?.token !== "string" || !fresh.token) throw new Error("no GitHub token from Notiamo");
63185
+ token = fresh.token;
63186
+ expiresAt = Date.parse(fresh.expiresAt);
63187
+ secrets.push(...tokenForms(token));
63188
+ }
63189
+ return token;
63190
+ };
63191
+ }
63192
+ function writeSettings(spec, deps, cwd) {
63193
+ const { sandbox } = deps;
63194
+ let wordpressHost;
63195
+ if (spec.wordpress) {
63196
+ try {
63197
+ wordpressHost = new URL(spec.wordpress.siteUrl).hostname;
63198
+ } catch {
63199
+ }
63200
+ }
63201
+ if (!/^[\w-]+$/.test(spec.runId)) throw new Error("bad run id");
63202
+ const dir = path11.resolve(deps.paths.runsDir);
63203
+ mkdirSync7(dir, { recursive: true, mode: 448 });
63204
+ const settings = sandboxSettings({
63205
+ platform: sandbox.platform,
63206
+ runnerRoot: sandbox.runnerRoot,
63207
+ claudeConfigDir: deps.paths.claudeConfigDir,
63208
+ runDir: cwd,
63209
+ notiamoHost: sandbox.notiamoHost,
63210
+ extraDomains: sandbox.allowedDomains(),
63211
+ wordpressHost,
63212
+ gitExecPath: sandbox.gitExecPath,
63213
+ ghPath: sandbox.ghPath
63214
+ });
63215
+ const file2 = path11.join(dir, `${spec.runId}.json`);
63216
+ writeFileSync6(file2, JSON.stringify(settings, null, 2) + "\n", { mode: 384 });
63217
+ try {
63218
+ chmodSync2(file2, 384);
63219
+ } catch {
63220
+ }
63221
+ return file2;
63222
+ }
62458
63223
  function wordpressHelperFactory(deps) {
62459
63224
  const { log } = deps;
62460
63225
  return async (spec) => {
@@ -62467,7 +63232,7 @@ function wordpressHelperFactory(deps) {
62467
63232
  const backup = await backupBefore(transport, root);
62468
63233
  if (backup.dump) {
62469
63234
  log.info(`run ${spec.runId}: database backed up with ${backup.how} (${backup.dump.byteLength} bytes gzip)`);
62470
- await uploadDump(deps.api, spec.runId, backup.dump).catch((e) => log.warn(`run ${spec.runId}: backup upload failed: ${errorText(e)}`));
63235
+ await uploadDump(deps.api, spec.runId, backup.dump).catch((e) => log.warn(`run ${spec.runId}: backup upload failed: ${errorText2(e)}`));
62471
63236
  } else {
62472
63237
  log.warn(`run ${spec.runId}: no database backup (${transport ? "WordPress folder not found" : "no SSH"})`);
62473
63238
  }
@@ -62525,12 +63290,12 @@ async function doneWithRetry(runId, input2, api, log, sleep) {
62525
63290
  const retryable = !(e instanceof ApiError) || e.status >= 500;
62526
63291
  const wait = DONE_BACKOFF_MS[attempt];
62527
63292
  if (!retryable || wait === void 0) throw e;
62528
- log.warn(`run ${runId}: done failed (${errorText(e)}); retrying in ${wait / 1e3} s`);
63293
+ log.warn(`run ${runId}: done failed (${errorText2(e)}); retrying in ${wait / 1e3} s`);
62529
63294
  await sleep(wait);
62530
63295
  }
62531
63296
  }
62532
63297
  }
62533
- var errorText = (e) => e instanceof Error ? e.message : String(e);
63298
+ var errorText2 = (e) => e instanceof Error ? e.message : String(e);
62534
63299
  function doneInput(r, totals, secrets) {
62535
63300
  const usage = { ...totals };
62536
63301
  if (r.outcome === "ended") return { outcome: "failed", error: "no_report", ...usage };
@@ -62556,7 +63321,7 @@ function transcript(runId, api, log, secrets, now, totals) {
62556
63321
  try {
62557
63322
  await api.events(runId, body);
62558
63323
  } catch (err) {
62559
- log.warn(`run ${runId}: dropped ${body.lines.length} event lines: ${errorText(err)}`);
63324
+ log.warn(`run ${runId}: dropped ${body.lines.length} event lines: ${errorText2(err)}`);
62560
63325
  }
62561
63326
  }
62562
63327
  };
@@ -62589,81 +63354,154 @@ function transcript(runId, api, log, secrets, now, totals) {
62589
63354
  }
62590
63355
 
62591
63356
  // src/loop.ts
63357
+ var defaultSettings = (notiamoHost) => ({ runsAtOnce: 1, keepClonesDays: 7, allowedDomains: [], notiamoHost });
63358
+ function applyHello(settings, answer, defaultHost) {
63359
+ const d = defaultSettings(defaultHost);
63360
+ settings.runsAtOnce = Number.isInteger(answer.runsAtOnce) && answer.runsAtOnce >= 1 ? answer.runsAtOnce : d.runsAtOnce;
63361
+ settings.keepClonesDays = typeof answer.keepClonesDays === "number" && Number.isFinite(answer.keepClonesDays) && answer.keepClonesDays > 0 ? answer.keepClonesDays : d.keepClonesDays;
63362
+ settings.allowedDomains = Array.isArray(answer.allowedDomains) ? answer.allowedDomains.filter((x) => typeof x === "string" && x.length > 0) : d.allowedDomains;
63363
+ settings.notiamoHost = typeof answer.notiamoHost === "string" && answer.notiamoHost ? answer.notiamoHost : d.notiamoHost;
63364
+ }
62592
63365
  var HELLO_MS = 6e4;
63366
+ var SLOT_FOLDERS = 3;
62593
63367
  var NETWORK_BACKOFF_MS = 5e3;
62594
63368
  var NEXT_WAIT_SECONDS = 25;
62595
63369
  async function startLoop(deps) {
62596
63370
  const { api, log, signal } = deps;
62597
63371
  const sleep = deps.sleep ?? ((ms) => delay2(ms, void 0, { signal }).catch(() => {
62598
63372
  }));
63373
+ const defaultHost = deps.sandbox.notiamoHost;
63374
+ const settings = deps.settings ?? defaultSettings(defaultHost);
63375
+ const clones = deps.clones ?? removeStaleClones;
63376
+ const running = /* @__PURE__ */ new Map();
63377
+ for (let i = 0; i < SLOT_FOLDERS; i++) {
63378
+ try {
63379
+ mkdirSync8(path12.join(deps.paths.projectsDir, String(i)), { recursive: true, mode: 448 });
63380
+ } catch (e) {
63381
+ log.warn(`slot folder ${i} not made: ${errorText3(e)}`);
63382
+ }
63383
+ }
63384
+ const freeSlot = () => {
63385
+ const used = new Set([...running.values()].map((r) => r.slot));
63386
+ let i = 0;
63387
+ while (used.has(i)) i++;
63388
+ return i;
63389
+ };
63390
+ const saveState = () => {
63391
+ try {
63392
+ writeState(deps.paths.stateFile, [...running.keys()]);
63393
+ } catch (e) {
63394
+ log.warn(`state file not written: ${errorText3(e)}`);
63395
+ }
63396
+ };
62599
63397
  const leftover = readState(deps.paths.stateFile);
62600
- if (leftover) {
62601
- log.warn(`run ${leftover}: the runner restarted during it`);
62602
- await api.done(leftover, { outcome: "failed", error: "runner_restarted", costUsd: 0, inputTokens: 0, outputTokens: 0 }).catch(() => {
63398
+ for (const runId of leftover) {
63399
+ log.warn(`run ${runId}: the runner restarted during it`);
63400
+ await api.done(runId, { outcome: "failed", error: "runner_restarted", costUsd: 0, inputTokens: 0, outputTokens: 0 }).catch(() => {
62603
63401
  });
62604
- clearState(deps.paths.stateFile);
62605
63402
  }
62606
- const hello = () => api.hello({ version: deps.version, os: deps.os, engine: deps.engineName ?? "claude", engineVersion: deps.engineVersion }).then(() => {
62607
- }, (e) => log.warn(`hello failed: ${errorText2(e)}`));
63403
+ if (leftover.length) {
63404
+ try {
63405
+ writeState(deps.paths.stateFile, []);
63406
+ } catch (e) {
63407
+ log.warn(`state file not cleared: ${errorText3(e)}`);
63408
+ }
63409
+ }
63410
+ let helloInFlight = false;
63411
+ const hello = async () => {
63412
+ if (helloInFlight) return;
63413
+ helloInFlight = true;
63414
+ try {
63415
+ await helloOnce();
63416
+ } finally {
63417
+ helloInFlight = false;
63418
+ }
63419
+ };
63420
+ const helloOnce = async () => {
63421
+ try {
63422
+ applyHello(settings, await api.hello({ version: deps.version, os: deps.os, engine: deps.engineName ?? "claude", engineVersion: deps.engineVersion }), defaultHost);
63423
+ } catch (e) {
63424
+ log.warn(`hello failed: ${errorText3(e)}`);
63425
+ }
63426
+ try {
63427
+ clones(deps.paths.projectsDir, deps.paths.gitDir, settings.keepClonesDays, new Set([...running.values()].map((r) => r.projectId)), /* @__PURE__ */ new Date(), log);
63428
+ } catch (e) {
63429
+ log.warn(`project folder clean-up failed: ${errorText3(e)}`);
63430
+ }
63431
+ };
62608
63432
  await hello();
62609
- const timer = setInterval(() => void hello(), HELLO_MS);
63433
+ const timer = setInterval(() => void hello(), deps.helloEveryMs ?? HELLO_MS);
62610
63434
  timer.unref();
62611
63435
  try {
62612
63436
  while (!signal?.aborted) {
63437
+ if (running.size >= settings.runsAtOnce) {
63438
+ await Promise.race([...running.values()].map((r) => r.ended));
63439
+ continue;
63440
+ }
62613
63441
  let spec;
62614
63442
  try {
62615
63443
  spec = await api.next(NEXT_WAIT_SECONDS, signal);
62616
63444
  } catch (e) {
62617
63445
  if (signal?.aborted) break;
62618
- log.warn(`next failed: ${errorText2(e)}`);
63446
+ log.warn(`next failed: ${errorText3(e)}`);
62619
63447
  await sleep(NETWORK_BACKOFF_MS);
62620
63448
  continue;
62621
63449
  }
62622
63450
  if (!spec) continue;
62623
- try {
62624
- writeState(deps.paths.stateFile, spec.runId);
62625
- } catch (e) {
62626
- log.warn(`state file not written: ${errorText2(e)}`);
62627
- }
62628
- try {
62629
- await runOne(spec, { engine: deps.engine, api, paths: deps.paths, log, helperFactory: deps.helperFactory });
62630
- } catch (e) {
62631
- log.error(`run ${spec.runId}: ${errorText2(e)}`);
63451
+ const s = spec;
63452
+ if (running.has(s.runId)) {
63453
+ log.warn(`run ${s.runId}: handed out again while it runs here; ignored`);
63454
+ continue;
62632
63455
  }
62633
- try {
62634
- clearState(deps.paths.stateFile);
62635
- } catch (e) {
62636
- log.warn(`state file not cleared: ${errorText2(e)}`);
63456
+ if ([...running.values()].some((r) => r.projectId === s.projectId)) {
63457
+ log.warn(`run ${s.runId}: refused: its project is already running here`);
63458
+ await api.done(s.runId, { outcome: "failed", error: "runner_busy", costUsd: 0, inputTokens: 0, outputTokens: 0 }).catch((e) => log.warn(`run ${s.runId}: done failed: ${errorText3(e)}`));
63459
+ continue;
62637
63460
  }
63461
+ const slot = freeSlot();
63462
+ const entry = { projectId: s.projectId, slot, ended: Promise.resolve() };
63463
+ running.set(s.runId, entry);
63464
+ saveState();
63465
+ const sandbox = { ...deps.sandbox, notiamoHost: settings.notiamoHost, allowedDomains: () => settings.allowedDomains };
63466
+ entry.ended = runOne(s, { engine: deps.engine, api, paths: deps.paths, slot, sandbox, log, helperFactory: deps.helperFactory }).catch((e) => log.error(`run ${s.runId}: ${errorText3(e)}`)).finally(() => {
63467
+ running.delete(s.runId);
63468
+ saveState();
63469
+ });
62638
63470
  }
63471
+ await Promise.all([...running.values()].map((r) => r.ended));
62639
63472
  } finally {
62640
63473
  clearInterval(timer);
62641
63474
  }
62642
63475
  }
62643
- var errorText2 = (e) => e instanceof Error ? e.message : String(e);
63476
+ var errorText3 = (e) => e instanceof Error ? e.message : String(e);
62644
63477
  function readState(file2) {
62645
63478
  try {
62646
- const s = JSON.parse(readFileSync2(file2, "utf8"));
62647
- return typeof s.busyRunId === "string" ? s.busyRunId : null;
63479
+ const s = JSON.parse(readFileSync3(file2, "utf8"));
63480
+ if (Array.isArray(s.busyRunIds)) return s.busyRunIds.filter((x) => typeof x === "string");
63481
+ return typeof s.busyRunId === "string" ? [s.busyRunId] : [];
62648
63482
  } catch {
62649
- return null;
63483
+ return [];
62650
63484
  }
62651
63485
  }
62652
- function writeState(file2, busyRunId) {
62653
- mkdirSync7(path10.dirname(file2), { recursive: true, mode: 448 });
62654
- writeFileSync4(file2, JSON.stringify({ busyRunId }) + "\n", { mode: 384 });
62655
- }
62656
- function clearState(file2) {
62657
- rmSync4(file2, { force: true });
63486
+ function writeState(file2, busyRunIds) {
63487
+ if (!busyRunIds.length) {
63488
+ rmSync6(file2, { force: true });
63489
+ return;
63490
+ }
63491
+ mkdirSync8(path12.dirname(file2), { recursive: true, mode: 448 });
63492
+ const tmp = `${file2}.tmp`;
63493
+ writeFileSync7(tmp, JSON.stringify({ busyRunIds }) + "\n", { mode: 384 });
63494
+ renameSync2(tmp, file2);
62658
63495
  }
62659
63496
 
62660
63497
  // src/commands.ts
62661
63498
  var HARD_STOP_MS = 3e4;
62662
- var serviceOptions = () => ({
63499
+ var serviceOptions = (profile) => ({
62663
63500
  platform: process.platform,
62664
- paths: paths(defaultDir()),
63501
+ profile,
63502
+ paths: paths(profileDir(profile)),
62665
63503
  exec: realExec,
62666
- home: os3.homedir(),
63504
+ home: os6.homedir(),
62667
63505
  env: process.env,
62668
63506
  nodePath: process.execPath,
62669
63507
  cliPath: process.argv[1]
@@ -62688,11 +63526,11 @@ async function prompt(q2, opts = {}) {
62688
63526
  rl.close();
62689
63527
  }
62690
63528
  }
62691
- async function init() {
62692
- await runInit({ prompt, exec: realExec, fetch });
63529
+ async function init({ profile } = {}) {
63530
+ await runInit({ prompt, exec: realExec, fetch, profile });
62693
63531
  }
62694
- async function start() {
62695
- const dir = defaultDir();
63532
+ async function start({ profile } = {}) {
63533
+ const dir = profileDir(profile);
62696
63534
  const cfg = loadConfig(dir);
62697
63535
  const p = paths(dir);
62698
63536
  const log = createLogger(p.log);
@@ -62700,6 +63538,20 @@ async function start() {
62700
63538
  const engine = fake ? fakeEngine([{ kind: "message", text: "Fake engine run." }, { kind: "result", costUsd: 0, inputTokens: 0, outputTokens: 0, sessionId: "fake-session" }], { report: true }) : claudeEngine({});
62701
63539
  const v = fake ? null : await realExec("claude", ["--version"], { env: { ...process.env, CLAUDE_CONFIG_DIR: p.claudeConfigDir } });
62702
63540
  const engineVersion = fake ? "fake" : v?.code === 0 && v.stdout.trim().slice(0, 100) || "unknown";
63541
+ const settings = defaultSettings(hostOf(cfg.host));
63542
+ const tools = await credentialTools(realExec);
63543
+ const sandbox = {
63544
+ platform: process.platform,
63545
+ runnerRoot: defaultDir(),
63546
+ get notiamoHost() {
63547
+ return settings.notiamoHost;
63548
+ },
63549
+ allowedDomains: () => settings.allowedDomains,
63550
+ // The fake engine ignores the settings file: it passes the check.
63551
+ engineVersion: fake ? ENGINE_MIN_VERSION : v?.code === 0 ? engineVersion : null,
63552
+ gitExecPath: tools.gitExecPath,
63553
+ ghPath: tools.ghPath
63554
+ };
62703
63555
  const ctrl = new AbortController();
62704
63556
  const stop5 = (signal) => {
62705
63557
  if (ctrl.signal.aborted) return;
@@ -62710,12 +63562,15 @@ async function start() {
62710
63562
  process.on("SIGTERM", () => stop5("SIGTERM"));
62711
63563
  process.on("SIGINT", () => stop5("SIGINT"));
62712
63564
  log.info(`runner ${cfg.name} ${VERSION} started (${engineVersion}), host ${cfg.host}`);
63565
+ if (engineTooOld(sandbox.engineVersion)) log.warn(`Claude Code ${engineVersion} is older than ${ENGINE_MIN_VERSION}: runs fail until it is updated (claude update)`);
62713
63566
  if (process.stdout.isTTY) console.log(`Runner ${cfg.name} is waiting for runs from ${cfg.host}. Logs: ${p.log}`);
62714
63567
  const api = new NotiamoApi(cfg);
62715
63568
  await startLoop({
62716
63569
  api,
62717
63570
  engine,
62718
63571
  paths: p,
63572
+ sandbox,
63573
+ settings,
62719
63574
  log,
62720
63575
  version: VERSION,
62721
63576
  os: process.platform,
@@ -62725,8 +63580,8 @@ async function start() {
62725
63580
  });
62726
63581
  log.info("stopped");
62727
63582
  }
62728
- async function stop4() {
62729
- const o = serviceOptions();
63583
+ async function stop4({ profile } = {}) {
63584
+ const o = serviceOptions(profile);
62730
63585
  if (await serviceStatus(o) === "not_installed") {
62731
63586
  console.log("The runner service is not installed.");
62732
63587
  return;
@@ -62734,31 +63589,39 @@ async function stop4() {
62734
63589
  await stopService(o);
62735
63590
  console.log("Stopped. The runner starts again at the next login or boot; `notiamo-runner uninstall` removes the service.");
62736
63591
  }
62737
- async function uninstall4() {
62738
- await uninstallService(serviceOptions());
62739
- console.log(`The runner service is removed. Its folder ${defaultDir()} (config, sign-in, projects) stays; delete it to remove everything.`);
63592
+ async function uninstall4({ profile } = {}) {
63593
+ await uninstallService(serviceOptions(profile));
63594
+ console.log(`The runner service is removed. Its folder ${profileDir(profile)} (config, sign-in, projects) stays; delete it to remove everything.`);
62740
63595
  }
62741
- async function status4() {
62742
- const st = await serviceStatus(serviceOptions());
63596
+ async function status4({ profile } = {}) {
63597
+ const st = await serviceStatus(serviceOptions(profile));
62743
63598
  let cfg = null;
62744
63599
  try {
62745
- cfg = loadConfig();
63600
+ cfg = loadConfig(profileDir(profile));
62746
63601
  } catch {
62747
63602
  }
63603
+ if (profile) console.log(`profile: ${profile}`);
62748
63604
  console.log(`installed: ${st === "not_installed" ? "no" : "yes"}`);
62749
63605
  console.log(`running: ${st === "running" ? "yes" : "no"}`);
62750
63606
  console.log(`name: ${cfg?.name ?? "-"}`);
62751
63607
  console.log(`host: ${cfg?.host ?? "-"}`);
62752
- console.log(`last log line: ${tail(paths(defaultDir()).log, 1)[0] ?? "-"}`);
63608
+ console.log(`last log line: ${tail(paths(profileDir(profile)).log, 1)[0] ?? "-"}`);
62753
63609
  }
62754
- function logs() {
62755
- const lines2 = tail(paths(defaultDir()).log, 50);
63610
+ function logs({ profile } = {}) {
63611
+ const lines2 = tail(paths(profileDir(profile)).log, 50);
62756
63612
  console.log(lines2.length ? lines2.join("\n") : "No log yet.");
62757
63613
  }
63614
+ function hostOf(url2) {
63615
+ try {
63616
+ return new URL(url2).hostname;
63617
+ } catch {
63618
+ return url2;
63619
+ }
63620
+ }
62758
63621
  function tail(file2, n) {
62759
63622
  let text;
62760
63623
  try {
62761
- text = readFileSync3(file2, "utf8");
63624
+ text = readFileSync4(file2, "utf8");
62762
63625
  } catch {
62763
63626
  return [];
62764
63627
  }
@@ -62768,7 +63631,7 @@ function tail(file2, n) {
62768
63631
  // src/cli.ts
62769
63632
  var HELP = `notiamo-runner ${VERSION}: runs Notiamo agent runs on this machine with your own Claude Code.
62770
63633
 
62771
- Usage: notiamo-runner <command>
63634
+ Usage: notiamo-runner <command> [--profile <name>]
62772
63635
 
62773
63636
  init Set the runner up: token, Claude Code sign-in, start at login or boot
62774
63637
  start Run in the foreground (what the service runs)
@@ -62776,21 +63639,33 @@ Usage: notiamo-runner <command>
62776
63639
  status Whether the service is installed and running, and the last log line
62777
63640
  logs The last 50 lines of the log
62778
63641
  uninstall Remove the service (the runner's folder stays)
62779
- --version Print the version`;
62780
- async function main(cmd) {
63642
+ --version Print the version
63643
+
63644
+ --profile <name> A second runner on this machine (say "staging"): its own folder, sign-in and service.
63645
+ Give it to every command of that runner.`;
63646
+ async function main(argv) {
63647
+ const { cmd, profile, error: error62 } = parseArgs(argv);
63648
+ if (error62) {
63649
+ console.error(`${error62}
63650
+
63651
+ ${HELP}`);
63652
+ process.exitCode = 1;
63653
+ return;
63654
+ }
63655
+ const o = { profile };
62781
63656
  switch (cmd) {
62782
63657
  case "init":
62783
- return init();
63658
+ return init(o);
62784
63659
  case "start":
62785
- return start();
63660
+ return start(o);
62786
63661
  case "stop":
62787
- return stop4();
63662
+ return stop4(o);
62788
63663
  case "status":
62789
- return status4();
63664
+ return status4(o);
62790
63665
  case "logs":
62791
- return logs();
63666
+ return logs(o);
62792
63667
  case "uninstall":
62793
- return uninstall4();
63668
+ return uninstall4(o);
62794
63669
  case "--version":
62795
63670
  case "-v":
62796
63671
  console.log(`notiamo-runner ${VERSION}`);
@@ -62808,7 +63683,7 @@ ${HELP}`);
62808
63683
  process.exitCode = 1;
62809
63684
  }
62810
63685
  }
62811
- main(process.argv[2]).catch((e) => {
63686
+ main(process.argv.slice(2)).catch((e) => {
62812
63687
  console.error(e instanceof Error ? e.message : String(e));
62813
63688
  process.exitCode = 1;
62814
63689
  });