@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.
- package/README.md +87 -6
- package/dist/cli.js +1342 -467
- 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,
|
|
265
|
+
return this.handle(command, (res, task2) => {
|
|
266
266
|
if (res instanceof Error) {
|
|
267
|
-
|
|
267
|
+
task2.reject(res);
|
|
268
268
|
} else {
|
|
269
|
-
|
|
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
|
|
376
|
-
if (
|
|
375
|
+
const task2 = this._task;
|
|
376
|
+
if (task2) {
|
|
377
377
|
try {
|
|
378
|
-
|
|
378
|
+
task2.responseHandler(response, task2.resolver);
|
|
379
379
|
} catch (err) {
|
|
380
|
-
|
|
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
|
|
1049
|
-
function isSafePathName(name, localPath =
|
|
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(
|
|
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(
|
|
1327
|
+
this.tryResolve(task2);
|
|
1328
1328
|
}
|
|
1329
1329
|
/**
|
|
1330
1330
|
* The control connection reports the transfer as finished.
|
|
1331
1331
|
*/
|
|
1332
|
-
onControlDone(
|
|
1332
|
+
onControlDone(task2, response) {
|
|
1333
1333
|
this.response = response;
|
|
1334
|
-
this.tryResolve(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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,
|
|
1372
|
+
return config2.ftp.handle(fullCommand, (res, task2) => {
|
|
1373
1373
|
if (res instanceof Error) {
|
|
1374
|
-
resolver.onError(
|
|
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(
|
|
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(
|
|
1391
|
+
resolver.onError(task2, err);
|
|
1392
1392
|
} else {
|
|
1393
|
-
resolver.onDataDone(
|
|
1393
|
+
resolver.onDataDone(task2);
|
|
1394
1394
|
}
|
|
1395
1395
|
});
|
|
1396
1396
|
});
|
|
1397
1397
|
} else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) {
|
|
1398
|
-
resolver.onControlDone(
|
|
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,
|
|
1410
|
+
return config2.ftp.handle(config2.command, (res, task2) => {
|
|
1411
1411
|
if (res instanceof Error) {
|
|
1412
|
-
resolver.onError(
|
|
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(
|
|
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(
|
|
1427
|
+
resolver.onError(task2, err);
|
|
1428
1428
|
} else {
|
|
1429
|
-
resolver.onDataDone(
|
|
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(
|
|
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,
|
|
1555
|
+
return this.ftp.handle(void 0, (res, task2) => {
|
|
1556
1556
|
if (res instanceof Error) {
|
|
1557
|
-
|
|
1557
|
+
task2.reject(res);
|
|
1558
1558
|
} else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) {
|
|
1559
|
-
|
|
1559
|
+
task2.resolve(res);
|
|
1560
1560
|
} else {
|
|
1561
|
-
|
|
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,
|
|
1581
|
+
return this.ftp.handle(command, (res, task2) => {
|
|
1582
1582
|
if (res instanceof FtpContext_1.FTPError) {
|
|
1583
|
-
|
|
1583
|
+
task2.resolve({ code: res.code, message: res.message });
|
|
1584
1584
|
} else if (res instanceof Error) {
|
|
1585
|
-
|
|
1585
|
+
task2.reject(res);
|
|
1586
1586
|
} else {
|
|
1587
|
-
|
|
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,
|
|
1612
|
+
return this.ftp.handle("USER " + user, (res, task2) => {
|
|
1613
1613
|
if (res instanceof Error) {
|
|
1614
|
-
|
|
1614
|
+
task2.reject(res);
|
|
1615
1615
|
} else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) {
|
|
1616
|
-
|
|
1616
|
+
task2.resolve(res);
|
|
1617
1617
|
} else if (res.code === 331) {
|
|
1618
1618
|
this.ftp.send("PASS " + password);
|
|
1619
1619
|
} else {
|
|
1620
|
-
|
|
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(
|
|
1708
|
-
const validPath = await this.protectWhitespace(
|
|
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(
|
|
1722
|
-
const validPath = await this.protectWhitespace(
|
|
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(
|
|
1731
|
-
const validPath = await this.protectWhitespace(
|
|
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(
|
|
1759
|
-
const validPath = await this.protectWhitespace(
|
|
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(
|
|
1919
|
-
const validPath = await this.protectWhitespace(
|
|
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(
|
|
2089
|
-
const validPath = await this.protectWhitespace(
|
|
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(
|
|
2097
|
-
if (!
|
|
2098
|
-
return
|
|
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 +
|
|
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(
|
|
2179
|
+
async function ensureLocalDirectory(path13) {
|
|
2180
2180
|
try {
|
|
2181
|
-
await fsStat(
|
|
2181
|
+
await fsStat(path13);
|
|
2182
2182
|
} catch (_a3) {
|
|
2183
|
-
await fsMkDir(
|
|
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(
|
|
2670
|
+
constructor(label2) {
|
|
2671
2671
|
super();
|
|
2672
|
-
this.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(
|
|
2680
|
+
constructor(label2) {
|
|
2681
2681
|
super();
|
|
2682
|
-
this.label =
|
|
2682
|
+
this.label = label2;
|
|
2683
2683
|
this.names = {};
|
|
2684
2684
|
}
|
|
2685
2685
|
render({ _n }) {
|
|
2686
|
-
const
|
|
2687
|
-
return `break${
|
|
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(
|
|
3100
|
-
return this._leafNode(new Label(
|
|
3099
|
+
label(label2) {
|
|
3100
|
+
return this._leafNode(new Label(label2));
|
|
3101
3101
|
}
|
|
3102
3102
|
// `break` statement
|
|
3103
|
-
break(
|
|
3104
|
-
return this._leafNode(new Break(
|
|
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(
|
|
5481
|
-
let input2 =
|
|
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
|
|
5891
|
-
wsComponent.path =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
16886
|
-
|
|
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
|
-
|
|
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(
|
|
17571
|
-
if (process.platform === "win32" && !WINDOWS_PIPE_REGEX.test(
|
|
17572
|
-
return
|
|
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(
|
|
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 =
|
|
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 =
|
|
20682
|
+
let digest = createHash2(hashName).update(secret).update(exchangeHash).update(char).update(sessionID).digest();
|
|
20683
20683
|
while (digest.length < len) {
|
|
20684
|
-
const chunk =
|
|
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(
|
|
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,
|
|
22821
|
+
return new ReadStream(this, path13, options);
|
|
22822
22822
|
}
|
|
22823
|
-
createWriteStream(
|
|
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,
|
|
22826
|
+
return new WriteStream(this, path13, options);
|
|
22827
22827
|
}
|
|
22828
|
-
open(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
23151
|
+
this.writeFile(path13, data, options, callback);
|
|
23152
23152
|
}
|
|
23153
|
-
exists(
|
|
23153
|
+
exists(path13, cb) {
|
|
23154
23154
|
if (this.server)
|
|
23155
23155
|
throw new Error("Client-only method called in server mode");
|
|
23156
|
-
this.stat(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
23473
|
-
return this.setstat(
|
|
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(
|
|
23485
|
-
return this.setstat(
|
|
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(
|
|
23496
|
-
return this.setstat(
|
|
23495
|
+
chmod(path13, mode, cb) {
|
|
23496
|
+
return this.setstat(path13, {
|
|
23497
23497
|
mode
|
|
23498
23498
|
}, cb);
|
|
23499
23499
|
}
|
|
23500
|
-
readlink(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
25050
|
+
const path13 = bufferParser.readString(true);
|
|
25051
25051
|
bufferParser.clear();
|
|
25052
|
-
if (
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
25102
|
+
const path13 = bufferParser.readString(true);
|
|
25103
25103
|
bufferParser.clear();
|
|
25104
|
-
if (
|
|
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,
|
|
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
|
|
25126
|
+
const path13 = bufferParser.readString(true);
|
|
25127
25127
|
bufferParser.clear();
|
|
25128
|
-
if (
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
25151
|
+
const path13 = bufferParser.readString(true);
|
|
25152
25152
|
bufferParser.clear();
|
|
25153
|
-
if (
|
|
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,
|
|
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
|
|
25163
|
+
const path13 = bufferParser.readString(true);
|
|
25164
25164
|
bufferParser.clear();
|
|
25165
|
-
if (
|
|
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,
|
|
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
|
|
25177
|
+
const path13 = bufferParser.readString(true);
|
|
25178
25178
|
bufferParser.clear();
|
|
25179
|
-
if (
|
|
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,
|
|
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
|
|
25202
|
+
const path13 = bufferParser.readString(true);
|
|
25203
25203
|
bufferParser.clear();
|
|
25204
|
-
if (
|
|
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,
|
|
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,
|
|
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 =
|
|
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,
|
|
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 =
|
|
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 =
|
|
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
|
|
29643
|
-
import
|
|
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:
|
|
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
|
-
|
|
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())
|
|
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
|
|
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
|
|
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
|
|
30076
|
-
var plistPath = (home) =>
|
|
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>${
|
|
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
|
|
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(
|
|
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 (!
|
|
30138
|
-
const r = await o.exec("launchctl", ["print", `${domain(o.uid)}/${
|
|
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
|
|
30152
|
-
import
|
|
30153
|
-
var
|
|
30154
|
-
var unitPath = (home) =>
|
|
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)}
|
|
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(
|
|
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",
|
|
30180
|
-
must("systemctl --user restart", await o.exec("systemctl", ["--user", "restart",
|
|
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",
|
|
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",
|
|
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 (!
|
|
30193
|
-
const r = await o.exec("systemctl", ["--user", "is-active",
|
|
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
|
|
30588
|
+
var task = (profile) => profile ? `Notiamo runner (${profile})` : "Notiamo runner";
|
|
30207
30589
|
function renderCreateArgs(o) {
|
|
30208
|
-
return ["/Create", "/F", "/SC", "ONLOGON", "/RL", "LIMITED", "/TN",
|
|
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",
|
|
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",
|
|
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",
|
|
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 '${
|
|
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 ??
|
|
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 ??
|
|
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 ??
|
|
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
|
|
30700
|
+
import path5 from "node:path";
|
|
30296
30701
|
var MAX_BYTES = 10 * 1024 * 1024;
|
|
30297
30702
|
function createLogger(file2) {
|
|
30298
|
-
mkdirSync4(
|
|
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
|
|
30312
|
-
import
|
|
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
|
|
30317
|
-
import
|
|
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 {
|
|
30322
|
-
import
|
|
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
|
-
|
|
30337
|
-
|
|
30338
|
-
|
|
30339
|
-
|
|
30340
|
-
if (
|
|
30341
|
-
|
|
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
|
-
|
|
30344
|
-
await exec("git", ["clone", g.cloneUrl,
|
|
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
|
-
|
|
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
|
|
30349
|
-
await
|
|
30350
|
-
await
|
|
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
|
|
30425
|
-
return { path:
|
|
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
|
|
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(
|
|
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
|
|
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:
|
|
30957
|
-
const fullPath = [...
|
|
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,
|
|
31726
|
+
constructor(parent, value, path13, key) {
|
|
31073
31727
|
this._cachedPath = [];
|
|
31074
31728
|
this.parent = parent;
|
|
31075
31729
|
this.data = value;
|
|
31076
|
-
this._path =
|
|
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,
|
|
35032
|
-
if (!
|
|
35685
|
+
function getElementAtPath(obj, path13) {
|
|
35686
|
+
if (!path13)
|
|
35033
35687
|
return obj;
|
|
35034
|
-
return
|
|
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(
|
|
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(
|
|
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,
|
|
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 }, [...
|
|
36497
|
+
issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
|
|
35844
36498
|
} else if (issue2.code === "invalid_key") {
|
|
35845
|
-
processError({ issues: issue2.issues }, [...
|
|
36499
|
+
processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
|
|
35846
36500
|
} else if (issue2.code === "invalid_element") {
|
|
35847
|
-
processError({ issues: issue2.issues }, [...
|
|
36501
|
+
processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
|
|
35848
36502
|
} else {
|
|
35849
|
-
const fullpath = [...
|
|
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,
|
|
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 }, [...
|
|
36546
|
+
issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
|
|
35893
36547
|
} else if (issue2.code === "invalid_key") {
|
|
35894
|
-
processError({ issues: issue2.issues }, [...
|
|
36548
|
+
processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
|
|
35895
36549
|
} else if (issue2.code === "invalid_element") {
|
|
35896
|
-
processError({ issues: issue2.issues }, [...
|
|
36550
|
+
processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
|
|
35897
36551
|
} else {
|
|
35898
|
-
const fullpath = [...
|
|
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
|
|
35938
|
-
for (const seg of
|
|
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
|
|
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()} ${
|
|
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
|
|
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()} ${
|
|
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
|
|
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()} ${
|
|
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
|
|
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()} ${
|
|
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
|
|
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()}${
|
|
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
|
|
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()}${
|
|
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
|
|
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()} ${
|
|
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
|
|
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()} ${
|
|
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,
|
|
48089
|
+
function numericOperand(value, label2) {
|
|
47436
48090
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
47437
|
-
throw new ZodCompileUnsupportedError(`${
|
|
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(
|
|
51490
|
-
if (
|
|
52143
|
+
function getDotPath(path13) {
|
|
52144
|
+
if (path13.length === 0) {
|
|
51491
52145
|
return "object root";
|
|
51492
52146
|
}
|
|
51493
|
-
return
|
|
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
|
|
53721
|
-
if (
|
|
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 (
|
|
53726
|
-
const key =
|
|
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
|
|
57315
|
-
if (!
|
|
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
|
-
...
|
|
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
|
|
57354
|
-
if (!
|
|
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(
|
|
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(
|
|
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
|
|
57393
|
-
if (!
|
|
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(
|
|
57397
|
-
throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal 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
|
|
57705
|
-
if (typeof
|
|
58358
|
+
const task2 = result.task;
|
|
58359
|
+
if (typeof task2.taskId === "string") {
|
|
57706
58360
|
isTaskResponse = true;
|
|
57707
|
-
this._taskProgressTokens.set(
|
|
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 (!
|
|
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
|
|
57782
|
-
yield { type: "taskStatus", task:
|
|
57783
|
-
if (isTerminal(
|
|
57784
|
-
if (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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
|
|
58158
|
-
if (
|
|
58159
|
-
interval =
|
|
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
|
|
58192
|
-
if (!
|
|
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
|
|
58849
|
+
return task2;
|
|
58196
58850
|
},
|
|
58197
58851
|
storeTaskResult: async (taskId, status5, result) => {
|
|
58198
58852
|
await taskStore.storeTaskResult(taskId, status5, result, sessionId);
|
|
58199
|
-
const
|
|
58200
|
-
if (
|
|
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:
|
|
58857
|
+
params: task2
|
|
58204
58858
|
});
|
|
58205
58859
|
await this.notification(notification);
|
|
58206
|
-
if (isTerminal(
|
|
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
|
|
58216
|
-
if (!
|
|
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(
|
|
58220
|
-
throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${
|
|
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
|
|
59297
|
-
const pollInterval =
|
|
59298
|
-
while (
|
|
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
|
-
|
|
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
|
|
62049
|
-
if (NEVER2.some((r) => matches(r, cmd, sub))) return { ok: false, reason: `${
|
|
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: `${
|
|
62052
|
-
if (!allow[rule[2]]) return { ok: false, reason: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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) :
|
|
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 = (
|
|
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 =
|
|
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(
|
|
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
|
|
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 ?
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
62389
|
-
|
|
62390
|
-
|
|
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
|
-
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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
|
-
|
|
63169
|
+
rmSync5(tempDir, { recursive: true, force: true });
|
|
62452
63170
|
} catch (e) {
|
|
62453
|
-
log.warn(`run ${spec.runId}: could not remove ${tempDir}: ${
|
|
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: ${
|
|
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 (${
|
|
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
|
|
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: ${
|
|
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
|
-
|
|
62601
|
-
log.warn(`run ${
|
|
62602
|
-
await api.done(
|
|
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
|
-
|
|
62607
|
-
|
|
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: ${
|
|
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
|
-
|
|
62624
|
-
|
|
62625
|
-
|
|
62626
|
-
|
|
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
|
-
|
|
62634
|
-
|
|
62635
|
-
|
|
62636
|
-
|
|
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
|
|
63476
|
+
var errorText3 = (e) => e instanceof Error ? e.message : String(e);
|
|
62644
63477
|
function readState(file2) {
|
|
62645
63478
|
try {
|
|
62646
|
-
const s = JSON.parse(
|
|
62647
|
-
return
|
|
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
|
|
63483
|
+
return [];
|
|
62650
63484
|
}
|
|
62651
63485
|
}
|
|
62652
|
-
function writeState(file2,
|
|
62653
|
-
|
|
62654
|
-
|
|
62655
|
-
|
|
62656
|
-
|
|
62657
|
-
|
|
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
|
-
|
|
63501
|
+
profile,
|
|
63502
|
+
paths: paths(profileDir(profile)),
|
|
62665
63503
|
exec: realExec,
|
|
62666
|
-
home:
|
|
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 =
|
|
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 ${
|
|
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(
|
|
63608
|
+
console.log(`last log line: ${tail(paths(profileDir(profile)).log, 1)[0] ?? "-"}`);
|
|
62753
63609
|
}
|
|
62754
|
-
function logs() {
|
|
62755
|
-
const lines2 = tail(paths(
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
});
|