@notiamo/runner 0.1.2 → 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 +79 -6
- package/dist/cli.js +1098 -260
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -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
|
}
|
|
@@ -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) {
|
|
@@ -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);
|
|
@@ -29655,7 +29655,11 @@ function paths(dir) {
|
|
|
29655
29655
|
log: path.join(dir, "runner.log"),
|
|
29656
29656
|
claudeConfigDir: path.join(dir, "claude"),
|
|
29657
29657
|
projectsDir: path.join(dir, "projects"),
|
|
29658
|
-
stateFile: path.join(dir, "state.json")
|
|
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")
|
|
29659
29663
|
};
|
|
29660
29664
|
}
|
|
29661
29665
|
function loadConfig(dir = defaultDir()) {
|
|
@@ -29693,8 +29697,8 @@ function parseArgs(argv) {
|
|
|
29693
29697
|
}
|
|
29694
29698
|
|
|
29695
29699
|
// src/commands.ts
|
|
29696
|
-
import { readFileSync as
|
|
29697
|
-
import
|
|
29700
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
29701
|
+
import os6 from "node:os";
|
|
29698
29702
|
import { createInterface } from "node:readline/promises";
|
|
29699
29703
|
import { Writable as Writable2 } from "node:stream";
|
|
29700
29704
|
|
|
@@ -29736,6 +29740,10 @@ var NotiamoApi = class {
|
|
|
29736
29740
|
done(runId, body) {
|
|
29737
29741
|
return this.json("POST", `/api/runner/runs/${encodeURIComponent(runId)}/done`, body);
|
|
29738
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
|
+
}
|
|
29739
29747
|
/** Uploads a gzip backup of what the run changed. */
|
|
29740
29748
|
async backup(runId, gzip) {
|
|
29741
29749
|
await this.call("POST", `/api/runner/runs/${encodeURIComponent(runId)}/backup`, gzip, "application/gzip");
|
|
@@ -29758,10 +29766,91 @@ var NotiamoApi = class {
|
|
|
29758
29766
|
|
|
29759
29767
|
// src/engine/claude.ts
|
|
29760
29768
|
import * as child_process from "node:child_process";
|
|
29769
|
+
import os2 from "node:os";
|
|
29761
29770
|
var MAX_TEXT = 2e3;
|
|
29762
29771
|
var KILL_GRACE_MS = 1e4;
|
|
29763
29772
|
var CLOSE_GRACE_MS = 2e3;
|
|
29764
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
|
+
}
|
|
29765
29854
|
function claudeEngine(deps = {}) {
|
|
29766
29855
|
const spawn4 = deps.spawn ?? child_process.spawn;
|
|
29767
29856
|
const bin = deps.claudeBin ?? "claude";
|
|
@@ -29782,13 +29871,22 @@ function claudeEngine(deps = {}) {
|
|
|
29782
29871
|
// tokens never show on the command line (`ps`). --mcp-config is variadic: the next argument must be a --flag.
|
|
29783
29872
|
"--mcp-config",
|
|
29784
29873
|
JSON.stringify(mcpConfig(o)),
|
|
29785
|
-
// Only Notiamo's servers:
|
|
29874
|
+
// Only Notiamo's servers: no .mcp.json of the cloned repo or of the config dir may load.
|
|
29786
29875
|
"--strict-mcp-config",
|
|
29787
|
-
|
|
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"
|
|
29788
29885
|
];
|
|
29789
29886
|
const engineEnv = (o) => ({
|
|
29790
|
-
...process.env,
|
|
29887
|
+
...baseEnv(deps.env ?? process.env, windows),
|
|
29791
29888
|
...o.env,
|
|
29889
|
+
...AGENT_GIT_ENV,
|
|
29792
29890
|
CLAUDE_CONFIG_DIR: o.claudeConfigDir,
|
|
29793
29891
|
NOTIAMO_RUN_TOKEN: o.mcp.token,
|
|
29794
29892
|
...o.helper ? { NOTIAMO_WP_TOKEN: o.helper.token } : {}
|
|
@@ -29799,6 +29897,7 @@ function claudeEngine(deps = {}) {
|
|
|
29799
29897
|
reported: false,
|
|
29800
29898
|
result: false,
|
|
29801
29899
|
resultError: void 0,
|
|
29900
|
+
sandboxError: false,
|
|
29802
29901
|
limitAt: void 0,
|
|
29803
29902
|
timedOut: false,
|
|
29804
29903
|
spawnError: void 0,
|
|
@@ -29881,7 +29980,9 @@ function claudeEngine(deps = {}) {
|
|
|
29881
29980
|
}
|
|
29882
29981
|
lines(child.stdout, onLine);
|
|
29883
29982
|
lines(child.stderr, (l) => {
|
|
29884
|
-
if (l.trim())
|
|
29983
|
+
if (!l.trim()) return;
|
|
29984
|
+
if (isSandboxUnavailable(l)) st.sandboxError = true;
|
|
29985
|
+
onText("error", l, true);
|
|
29885
29986
|
});
|
|
29886
29987
|
for (const s of [child.stdout, child.stderr]) s?.on("error", (e) => emit({ kind: "error", text: cut(`stream error: ${e.message}`) }));
|
|
29887
29988
|
const kill = (sig) => {
|
|
@@ -29908,6 +30009,12 @@ function claudeEngine(deps = {}) {
|
|
|
29908
30009
|
clearTimeout(timer);
|
|
29909
30010
|
clearTimeout(killTimer);
|
|
29910
30011
|
clearTimeout(closeTimer);
|
|
30012
|
+
if (!windows && child.pid) {
|
|
30013
|
+
try {
|
|
30014
|
+
processKill(-child.pid, "SIGKILL");
|
|
30015
|
+
} catch {
|
|
30016
|
+
}
|
|
30017
|
+
}
|
|
29911
30018
|
const usage = { costUsd: st.costUsd, inputTokens: st.inputTokens, outputTokens: st.outputTokens, sessionId: st.sessionId };
|
|
29912
30019
|
const succeeded = st.result && !st.resultError;
|
|
29913
30020
|
if (st.timedOut) return resolve({ outcome: "failed", error: "timeout", ...usage });
|
|
@@ -29916,6 +30023,7 @@ function claudeEngine(deps = {}) {
|
|
|
29916
30023
|
if (st.resultError) return resolve({ outcome: "failed", error: st.resultError, ...usage });
|
|
29917
30024
|
if (st.reported) return resolve({ outcome: "reported", ...usage });
|
|
29918
30025
|
if (st.result) return resolve({ outcome: "ended", ...usage });
|
|
30026
|
+
if (code !== 0 && st.sandboxError) return resolve({ outcome: "failed", error: "sandbox_unavailable", ...usage });
|
|
29919
30027
|
if (code !== 0) return resolve({ outcome: "failed", error: `claude exited with code ${code}`, ...usage });
|
|
29920
30028
|
resolve({ outcome: "ended", ...usage });
|
|
29921
30029
|
};
|
|
@@ -30044,15 +30152,15 @@ async function reportRun(mcp, args, fetchImpl) {
|
|
|
30044
30152
|
// src/exec.ts
|
|
30045
30153
|
import { spawn as spawn2 } from "node:child_process";
|
|
30046
30154
|
var realExec = async (file2, args, opts) => {
|
|
30047
|
-
const r = await run(file2, args, opts?.env, false);
|
|
30048
|
-
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);
|
|
30049
30157
|
return r;
|
|
30050
30158
|
};
|
|
30051
|
-
function run(file2, args, env, shell) {
|
|
30159
|
+
function run(file2, args, env, shell, timeout) {
|
|
30052
30160
|
return new Promise((resolve) => {
|
|
30053
30161
|
let stdout = "";
|
|
30054
30162
|
let stderr = "";
|
|
30055
|
-
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" } : {} });
|
|
30056
30164
|
child.stdout.on("data", (d) => {
|
|
30057
30165
|
stdout += d.toString();
|
|
30058
30166
|
});
|
|
@@ -30067,7 +30175,259 @@ var quoteForCmd = (a) => /^[\w.:/\\=-]+$/.test(a) ? a : `"${a.replace(/"/g, '""'
|
|
|
30067
30175
|
|
|
30068
30176
|
// src/init.ts
|
|
30069
30177
|
import { spawn as spawn3 } from "node:child_process";
|
|
30070
|
-
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
|
+
}
|
|
30071
30431
|
|
|
30072
30432
|
// src/service/darwin.ts
|
|
30073
30433
|
var darwin_exports = {};
|
|
@@ -30079,8 +30439,8 @@ __export(darwin_exports, {
|
|
|
30079
30439
|
uninstall: () => uninstall
|
|
30080
30440
|
});
|
|
30081
30441
|
import { setTimeout as delay } from "node:timers/promises";
|
|
30082
|
-
import { mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync2, existsSync } from "node:fs";
|
|
30083
|
-
import
|
|
30442
|
+
import { mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
30443
|
+
import path3 from "node:path";
|
|
30084
30444
|
|
|
30085
30445
|
// src/service/common.ts
|
|
30086
30446
|
var startArgs = (profile) => profile ? ["start", "--profile", profile] : ["start"];
|
|
@@ -30095,7 +30455,7 @@ function must(what, r) {
|
|
|
30095
30455
|
|
|
30096
30456
|
// src/service/darwin.ts
|
|
30097
30457
|
var label = (profile) => profile ? `com.notiamo.runner.${profile}` : "com.notiamo.runner";
|
|
30098
|
-
var plistPath = (home, profile) =>
|
|
30458
|
+
var plistPath = (home, profile) => path3.join(home, "Library", "LaunchAgents", `${label(profile)}.plist`);
|
|
30099
30459
|
var domain = (uid) => `gui/${uid}`;
|
|
30100
30460
|
var BOOTSTRAP_TRIES = 5;
|
|
30101
30461
|
var BOOTSTRAP_RETRY_MS = 500;
|
|
@@ -30135,7 +30495,7 @@ ${env}
|
|
|
30135
30495
|
}
|
|
30136
30496
|
async function install(o) {
|
|
30137
30497
|
const file2 = plistPath(o.home, o.profile);
|
|
30138
|
-
mkdirSync2(
|
|
30498
|
+
mkdirSync2(path3.dirname(file2), { recursive: true });
|
|
30139
30499
|
writeFileSync2(file2, renderPlist({ nodePath: o.nodePath, cliPath: o.cliPath, log: o.paths.log, env: o.env, profile: o.profile }));
|
|
30140
30500
|
await o.exec("launchctl", ["bootout", domain(o.uid), file2]);
|
|
30141
30501
|
const sleep = o.sleep ?? ((ms) => delay(ms));
|
|
@@ -30156,7 +30516,7 @@ async function uninstall(o) {
|
|
|
30156
30516
|
rmSync(plistPath(o.home, o.profile), { force: true });
|
|
30157
30517
|
}
|
|
30158
30518
|
async function status(o) {
|
|
30159
|
-
if (!
|
|
30519
|
+
if (!existsSync2(plistPath(o.home, o.profile))) return "not_installed";
|
|
30160
30520
|
const r = await o.exec("launchctl", ["print", `${domain(o.uid)}/${label(o.profile)}`]);
|
|
30161
30521
|
return r.code === 0 ? "running" : "stopped";
|
|
30162
30522
|
}
|
|
@@ -30170,10 +30530,10 @@ __export(linux_exports, {
|
|
|
30170
30530
|
stop: () => stop2,
|
|
30171
30531
|
uninstall: () => uninstall2
|
|
30172
30532
|
});
|
|
30173
|
-
import { existsSync as
|
|
30174
|
-
import
|
|
30533
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
30534
|
+
import path4 from "node:path";
|
|
30175
30535
|
var unit = (profile) => profile ? `notiamo-runner-${profile}` : "notiamo-runner";
|
|
30176
|
-
var unitPath = (home, profile) =>
|
|
30536
|
+
var unitPath = (home, profile) => path4.join(home, ".config", "systemd", "user", `${unit(profile)}.service`);
|
|
30177
30537
|
var q = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
30178
30538
|
function renderUnit(o) {
|
|
30179
30539
|
const env = Object.entries(serviceEnv(o.env)).map(([k, v]) => `Environment=${q(`${k}=${v}`)}`).join("\n");
|
|
@@ -30195,7 +30555,7 @@ WantedBy=default.target
|
|
|
30195
30555
|
}
|
|
30196
30556
|
async function install2(o) {
|
|
30197
30557
|
const file2 = unitPath(o.home, o.profile);
|
|
30198
|
-
mkdirSync3(
|
|
30558
|
+
mkdirSync3(path4.dirname(file2), { recursive: true });
|
|
30199
30559
|
writeFileSync3(file2, renderUnit({ nodePath: o.nodePath, cliPath: o.cliPath, log: o.paths.log, env: o.env, profile: o.profile }));
|
|
30200
30560
|
must("systemctl --user daemon-reload", await o.exec("systemctl", ["--user", "daemon-reload"]));
|
|
30201
30561
|
must("systemctl --user enable", await o.exec("systemctl", ["--user", "enable", unit(o.profile)]));
|
|
@@ -30211,7 +30571,7 @@ async function uninstall2(o) {
|
|
|
30211
30571
|
await o.exec("systemctl", ["--user", "daemon-reload"]);
|
|
30212
30572
|
}
|
|
30213
30573
|
async function status2(o) {
|
|
30214
|
-
if (!
|
|
30574
|
+
if (!existsSync3(unitPath(o.home, o.profile))) return "not_installed";
|
|
30215
30575
|
const r = await o.exec("systemctl", ["--user", "is-active", unit(o.profile)]);
|
|
30216
30576
|
return r.stdout.trim() === "active" ? "running" : "stopped";
|
|
30217
30577
|
}
|
|
@@ -30262,6 +30622,7 @@ var serviceStatus = async (o) => impl(o).status(withUid(o));
|
|
|
30262
30622
|
|
|
30263
30623
|
// src/init.ts
|
|
30264
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.";
|
|
30265
30626
|
var runInteractive = (file2, args, env) => new Promise((resolve, reject) => {
|
|
30266
30627
|
const child = spawn3(file2, args, { stdio: "inherit", env, shell: process.platform === "win32" });
|
|
30267
30628
|
child.on("error", reject);
|
|
@@ -30281,11 +30642,27 @@ async function runInit(deps) {
|
|
|
30281
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();
|
|
30282
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.");
|
|
30283
30644
|
const host = normalizeHost(await ask("Notiamo address: ", DEFAULT_HOST));
|
|
30284
|
-
const hostname3 = deps.hostname ??
|
|
30645
|
+
const hostname3 = deps.hostname ?? os4.hostname();
|
|
30285
30646
|
const name = await ask("Name of this runner: ", hostname3);
|
|
30286
30647
|
const version2 = await deps.exec("claude", ["--version"], { env });
|
|
30287
30648
|
if (version2.code !== 0) throw new Error("Claude Code is not installed. Install it first: https://claude.com/claude-code");
|
|
30288
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
|
+
}
|
|
30289
30666
|
try {
|
|
30290
30667
|
await new NotiamoApi({ host, token }, deps.fetch).hello({ version: VERSION, os: platform, engine: "claude", engineVersion });
|
|
30291
30668
|
} catch (e) {
|
|
@@ -30299,13 +30676,18 @@ async function runInit(deps) {
|
|
|
30299
30676
|
}
|
|
30300
30677
|
const check2 = await deps.exec("claude", ["-p", "say ok"], { env: claudeEnv });
|
|
30301
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
|
+
}
|
|
30302
30684
|
saveConfig({ host, token, name, dataDir: dir, engine: "claude" }, dir);
|
|
30303
30685
|
await installService({
|
|
30304
30686
|
platform,
|
|
30305
30687
|
profile: deps.profile,
|
|
30306
30688
|
paths: p,
|
|
30307
30689
|
exec: deps.exec,
|
|
30308
|
-
home: deps.home ??
|
|
30690
|
+
home: deps.home ?? os4.homedir(),
|
|
30309
30691
|
env,
|
|
30310
30692
|
nodePath: deps.nodePath ?? process.execPath,
|
|
30311
30693
|
cliPath: deps.cliPath ?? process.argv[1]
|
|
@@ -30315,10 +30697,10 @@ async function runInit(deps) {
|
|
|
30315
30697
|
|
|
30316
30698
|
// src/log.ts
|
|
30317
30699
|
import { appendFileSync, mkdirSync as mkdirSync4, renameSync, statSync } from "node:fs";
|
|
30318
|
-
import
|
|
30700
|
+
import path5 from "node:path";
|
|
30319
30701
|
var MAX_BYTES = 10 * 1024 * 1024;
|
|
30320
30702
|
function createLogger(file2) {
|
|
30321
|
-
mkdirSync4(
|
|
30703
|
+
mkdirSync4(path5.dirname(file2), { recursive: true, mode: 448 });
|
|
30322
30704
|
const write = (level, msg) => {
|
|
30323
30705
|
try {
|
|
30324
30706
|
if (statSync(file2).size > MAX_BYTES) renameSync(file2, file2 + ".1");
|
|
@@ -30331,18 +30713,110 @@ function createLogger(file2) {
|
|
|
30331
30713
|
}
|
|
30332
30714
|
|
|
30333
30715
|
// src/loop.ts
|
|
30334
|
-
import { mkdirSync as
|
|
30335
|
-
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";
|
|
30336
30718
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
30337
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
|
+
|
|
30338
30810
|
// src/run.ts
|
|
30339
|
-
import { mkdirSync as
|
|
30340
|
-
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";
|
|
30341
30813
|
|
|
30342
30814
|
// src/project-folder.ts
|
|
30343
30815
|
import { execFile } from "node:child_process";
|
|
30344
|
-
import {
|
|
30345
|
-
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";
|
|
30346
30820
|
import { promisify } from "node:util";
|
|
30347
30821
|
var execFileAsync = async (file2, args, opts) => {
|
|
30348
30822
|
const { stdout, stderr } = await promisify(execFile)(file2, args, {
|
|
@@ -30354,25 +30828,182 @@ var execFileAsync = async (file2, args, opts) => {
|
|
|
30354
30828
|
});
|
|
30355
30829
|
return { stdout: String(stdout), stderr: String(stderr) };
|
|
30356
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
|
+
}
|
|
30357
30845
|
var CLONE_TIMEOUT_MS = 10 * 6e4;
|
|
30358
30846
|
var GIT_TIMEOUT_MS = 2 * 6e4;
|
|
30359
|
-
|
|
30360
|
-
|
|
30361
|
-
|
|
30362
|
-
|
|
30363
|
-
if (
|
|
30364
|
-
|
|
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 });
|
|
30863
|
+
} else {
|
|
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 });
|
|
30866
|
+
}
|
|
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"]);
|
|
30365
30881
|
} else {
|
|
30366
|
-
|
|
30367
|
-
await exec("git", ["clone",
|
|
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 });
|
|
30368
30884
|
}
|
|
30369
|
-
const { stdout } = await
|
|
30885
|
+
const { stdout } = await gitIn(folder, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
30370
30886
|
if (stdout.trim() !== g.branch) {
|
|
30371
|
-
await
|
|
30372
|
-
await
|
|
30373
|
-
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;
|
|
30374
31006
|
}
|
|
30375
|
-
return folder;
|
|
30376
31007
|
}
|
|
30377
31008
|
|
|
30378
31009
|
// src/scrub.ts
|
|
@@ -30444,12 +31075,12 @@ function resolveRoute(siteUrl, route) {
|
|
|
30444
31075
|
return { error: "route is not a valid URL path" };
|
|
30445
31076
|
}
|
|
30446
31077
|
if (url2.origin !== site.origin || !url2.pathname.startsWith(prefix + "/wp/v2/")) return { error: "route must stay under /wp/v2/" };
|
|
30447
|
-
const
|
|
30448
|
-
return { path:
|
|
31078
|
+
const path13 = url2.pathname.slice(prefix.length);
|
|
31079
|
+
return { path: path13, route: path13 + url2.search, query: url2.searchParams };
|
|
30449
31080
|
}
|
|
30450
31081
|
|
|
30451
31082
|
// src/wp/backup.ts
|
|
30452
|
-
import
|
|
31083
|
+
import path8 from "node:path";
|
|
30453
31084
|
|
|
30454
31085
|
// src/wp/transport.ts
|
|
30455
31086
|
var shellQuote = (a) => /^[A-Za-z0-9_\-.,/:=@%+]+$/.test(a) ? a : `'${a.replace(/'/g, `'\\''`)}'`;
|
|
@@ -30468,7 +31099,7 @@ async function backupBefore(t, root) {
|
|
|
30468
31099
|
const wp = await dump(`{ wp db export - --path=${shellQuote(root)} || echo ${FAILED} >&2; } | gzip`);
|
|
30469
31100
|
if (wp === "overflow") throw new Error("backup_failed: the database dump is over 200 MB");
|
|
30470
31101
|
if (wp) return { dump: wp, how: "wp-cli" };
|
|
30471
|
-
const config2 = await t.getFile(
|
|
31102
|
+
const config2 = await t.getFile(path8.posix.join(root, "wp-config.php")).catch(() => null);
|
|
30472
31103
|
const db = config2 ? dbCredentials(config2) : null;
|
|
30473
31104
|
if (!db) throw new Error("backup_failed: no wp-cli export and no database credentials in wp-config.php");
|
|
30474
31105
|
const [host, port] = splitHost(db.host);
|
|
@@ -30600,7 +31231,7 @@ async function ftpTransport(conn) {
|
|
|
30600
31231
|
// src/wp/helper.ts
|
|
30601
31232
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
30602
31233
|
import http from "node:http";
|
|
30603
|
-
import
|
|
31234
|
+
import path9 from "node:path";
|
|
30604
31235
|
|
|
30605
31236
|
// ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/helpers/util.js
|
|
30606
31237
|
var util;
|
|
@@ -30976,8 +31607,8 @@ function getErrorMap() {
|
|
|
30976
31607
|
|
|
30977
31608
|
// ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/helpers/parseUtil.js
|
|
30978
31609
|
var makeIssue = (params) => {
|
|
30979
|
-
const { data, path:
|
|
30980
|
-
const fullPath = [...
|
|
31610
|
+
const { data, path: path13, errorMaps, issueData } = params;
|
|
31611
|
+
const fullPath = [...path13, ...issueData.path || []];
|
|
30981
31612
|
const fullIssue = {
|
|
30982
31613
|
...issueData,
|
|
30983
31614
|
path: fullPath
|
|
@@ -31092,11 +31723,11 @@ var errorUtil;
|
|
|
31092
31723
|
|
|
31093
31724
|
// ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/types.js
|
|
31094
31725
|
var ParseInputLazyPath = class {
|
|
31095
|
-
constructor(parent, value,
|
|
31726
|
+
constructor(parent, value, path13, key) {
|
|
31096
31727
|
this._cachedPath = [];
|
|
31097
31728
|
this.parent = parent;
|
|
31098
31729
|
this.data = value;
|
|
31099
|
-
this._path =
|
|
31730
|
+
this._path = path13;
|
|
31100
31731
|
this._key = key;
|
|
31101
31732
|
}
|
|
31102
31733
|
get path() {
|
|
@@ -35051,10 +35682,10 @@ function mergeDefs(...defs) {
|
|
|
35051
35682
|
function cloneDef(schema) {
|
|
35052
35683
|
return mergeDefs(schema._zod.def);
|
|
35053
35684
|
}
|
|
35054
|
-
function getElementAtPath(obj,
|
|
35055
|
-
if (!
|
|
35685
|
+
function getElementAtPath(obj, path13) {
|
|
35686
|
+
if (!path13)
|
|
35056
35687
|
return obj;
|
|
35057
|
-
return
|
|
35688
|
+
return path13.reduce((acc, key) => acc?.[key], obj);
|
|
35058
35689
|
}
|
|
35059
35690
|
function promiseAllObject(promisesObj) {
|
|
35060
35691
|
const keys = Object.keys(promisesObj);
|
|
@@ -35394,11 +36025,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
35394
36025
|
}
|
|
35395
36026
|
return false;
|
|
35396
36027
|
}
|
|
35397
|
-
function prefixIssues(
|
|
36028
|
+
function prefixIssues(path13, issues) {
|
|
35398
36029
|
return issues.map((iss) => {
|
|
35399
36030
|
var _a3;
|
|
35400
36031
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
35401
|
-
iss.path.unshift(
|
|
36032
|
+
iss.path.unshift(path13);
|
|
35402
36033
|
return iss;
|
|
35403
36034
|
});
|
|
35404
36035
|
}
|
|
@@ -35860,16 +36491,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
35860
36491
|
}
|
|
35861
36492
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
35862
36493
|
const fieldErrors = { _errors: [] };
|
|
35863
|
-
const processError = (error63,
|
|
36494
|
+
const processError = (error63, path13 = []) => {
|
|
35864
36495
|
for (const issue2 of error63.issues) {
|
|
35865
36496
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
35866
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
36497
|
+
issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
|
|
35867
36498
|
} else if (issue2.code === "invalid_key") {
|
|
35868
|
-
processError({ issues: issue2.issues }, [...
|
|
36499
|
+
processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
|
|
35869
36500
|
} else if (issue2.code === "invalid_element") {
|
|
35870
|
-
processError({ issues: issue2.issues }, [...
|
|
36501
|
+
processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
|
|
35871
36502
|
} else {
|
|
35872
|
-
const fullpath = [...
|
|
36503
|
+
const fullpath = [...path13, ...issue2.path];
|
|
35873
36504
|
if (fullpath.length === 0) {
|
|
35874
36505
|
fieldErrors._errors.push(mapper(issue2));
|
|
35875
36506
|
} else {
|
|
@@ -35908,17 +36539,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
35908
36539
|
}
|
|
35909
36540
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
35910
36541
|
const result = { errors: [] };
|
|
35911
|
-
const processError = (error63,
|
|
36542
|
+
const processError = (error63, path13 = []) => {
|
|
35912
36543
|
var _a3;
|
|
35913
36544
|
for (const issue2 of error63.issues) {
|
|
35914
36545
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
35915
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
36546
|
+
issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
|
|
35916
36547
|
} else if (issue2.code === "invalid_key") {
|
|
35917
|
-
processError({ issues: issue2.issues }, [...
|
|
36548
|
+
processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
|
|
35918
36549
|
} else if (issue2.code === "invalid_element") {
|
|
35919
|
-
processError({ issues: issue2.issues }, [...
|
|
36550
|
+
processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
|
|
35920
36551
|
} else {
|
|
35921
|
-
const fullpath = [...
|
|
36552
|
+
const fullpath = [...path13, ...issue2.path];
|
|
35922
36553
|
if (fullpath.length === 0) {
|
|
35923
36554
|
result.errors.push(mapper(issue2));
|
|
35924
36555
|
continue;
|
|
@@ -35957,8 +36588,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
35957
36588
|
}
|
|
35958
36589
|
function toDotPath(_path) {
|
|
35959
36590
|
const segs = [];
|
|
35960
|
-
const
|
|
35961
|
-
for (const seg of
|
|
36591
|
+
const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
36592
|
+
for (const seg of path13) {
|
|
35962
36593
|
if (typeof seg === "number")
|
|
35963
36594
|
segs.push(`[${seg}]`);
|
|
35964
36595
|
else if (typeof seg === "symbol")
|
|
@@ -51509,11 +52140,11 @@ function normalizeObjectSchema(schema) {
|
|
|
51509
52140
|
}
|
|
51510
52141
|
return void 0;
|
|
51511
52142
|
}
|
|
51512
|
-
function getDotPath(
|
|
51513
|
-
if (
|
|
52143
|
+
function getDotPath(path13) {
|
|
52144
|
+
if (path13.length === 0) {
|
|
51514
52145
|
return "object root";
|
|
51515
52146
|
}
|
|
51516
|
-
return
|
|
52147
|
+
return path13.reduce((acc, seg, index) => {
|
|
51517
52148
|
if (index === 0) {
|
|
51518
52149
|
return String(seg);
|
|
51519
52150
|
}
|
|
@@ -53740,13 +54371,13 @@ function resolveRef(ref, ctx) {
|
|
|
53740
54371
|
if (!ref.startsWith("#")) {
|
|
53741
54372
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
53742
54373
|
}
|
|
53743
|
-
const
|
|
53744
|
-
if (
|
|
54374
|
+
const path13 = ref.slice(1).split("/").filter(Boolean);
|
|
54375
|
+
if (path13.length === 0) {
|
|
53745
54376
|
return ctx.rootSchema;
|
|
53746
54377
|
}
|
|
53747
54378
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
53748
|
-
if (
|
|
53749
|
-
const key =
|
|
54379
|
+
if (path13[0] === defsKey) {
|
|
54380
|
+
const key = path13[1] === void 0 ? void 0 : decodeJSONPointerSegment(path13[1]);
|
|
53750
54381
|
if (!key || !ctx.defs[key]) {
|
|
53751
54382
|
throw new Error(`Reference not found: ${ref}`);
|
|
53752
54383
|
}
|
|
@@ -62194,7 +62825,7 @@ function buildServer(t) {
|
|
|
62194
62825
|
if (!t.allow.files && !t.allow.all) return fail("the files group is off for this project");
|
|
62195
62826
|
if (t.root === null) return fail("wordpress_root_not_found: the WordPress folder was not found on the server");
|
|
62196
62827
|
const bad = pathRefusal(p, t.allow.all);
|
|
62197
|
-
return bad ? fail(bad) :
|
|
62828
|
+
return bad ? fail(bad) : path9.posix.join(t.root, p);
|
|
62198
62829
|
};
|
|
62199
62830
|
server.registerTool("wp_get_file", {
|
|
62200
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).",
|
|
@@ -62266,9 +62897,9 @@ function apiRefusal(route, query, method, body, allow) {
|
|
|
62266
62897
|
}
|
|
62267
62898
|
function pathRefusal(p, all) {
|
|
62268
62899
|
if (p.startsWith("/") || p.includes("\\") || p.includes("\0")) return "path must be relative to the WordPress folder";
|
|
62269
|
-
const norm =
|
|
62900
|
+
const norm = path9.posix.normalize(p);
|
|
62270
62901
|
if (norm === ".." || norm.startsWith("../") || p.split("/").includes("..")) return "path must stay inside the WordPress folder";
|
|
62271
|
-
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`;
|
|
62272
62903
|
return null;
|
|
62273
62904
|
}
|
|
62274
62905
|
function publishedLink(json3) {
|
|
@@ -62280,7 +62911,7 @@ var cut2 = (s) => s.length > MAX_OUTPUT ? `${s.slice(0, MAX_OUTPUT)}
|
|
|
62280
62911
|
[cut at ${MAX_OUTPUT} characters]` : s;
|
|
62281
62912
|
|
|
62282
62913
|
// src/wp/root.ts
|
|
62283
|
-
import
|
|
62914
|
+
import path10 from "node:path";
|
|
62284
62915
|
var FTP_CANDIDATES = [".", "public_html", "www", "htdocs", "httpdocs", "html", "web"];
|
|
62285
62916
|
async function findRoot(t) {
|
|
62286
62917
|
if (t.run) {
|
|
@@ -62288,10 +62919,10 @@ async function findRoot(t) {
|
|
|
62288
62919
|
if (home?.code === 0) return ".";
|
|
62289
62920
|
const found = await t.run(["find", ".", "-maxdepth", "3", "-name", "wp-config.php"]).catch(() => null);
|
|
62290
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];
|
|
62291
|
-
return first ?
|
|
62922
|
+
return first ? path10.posix.normalize(path10.posix.dirname(first)) : null;
|
|
62292
62923
|
}
|
|
62293
62924
|
for (const dir of FTP_CANDIDATES) {
|
|
62294
|
-
if (await t.getFile(
|
|
62925
|
+
if (await t.getFile(path10.posix.join(dir, "wp-config.php")).catch(() => null) !== null) return dir;
|
|
62295
62926
|
}
|
|
62296
62927
|
return null;
|
|
62297
62928
|
}
|
|
@@ -62390,29 +63021,45 @@ async function runOne(spec, deps) {
|
|
|
62390
63021
|
return ms <= 0 ? 0 : Math.max(1, Math.ceil(ms / 6e4));
|
|
62391
63022
|
};
|
|
62392
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
|
+
}
|
|
62393
63029
|
const secrets = [
|
|
62394
63030
|
spec.mcp.accessToken,
|
|
62395
63031
|
spec.mcp.refreshToken,
|
|
62396
63032
|
spec.wordpress?.apiPassword,
|
|
62397
63033
|
spec.wordpress?.ssh?.privateKey,
|
|
62398
63034
|
spec.wordpress?.ssh?.password,
|
|
62399
|
-
spec.wordpress?.ftp?.password
|
|
63035
|
+
spec.wordpress?.ftp?.password,
|
|
63036
|
+
...tokenForms(spec.github?.token)
|
|
62400
63037
|
].filter((s) => Boolean(s));
|
|
63038
|
+
const gitToken = spec.github ? githubToken(spec.runId, spec.github, api, secrets, now) : void 0;
|
|
62401
63039
|
const totals = { costUsd: 0, inputTokens: 0, outputTokens: 0 };
|
|
62402
63040
|
const events = transcript(spec.runId, api, log, secrets, now, totals);
|
|
62403
63041
|
let helper;
|
|
62404
63042
|
let tempDir;
|
|
63043
|
+
let settingsFile;
|
|
63044
|
+
let clone2;
|
|
62405
63045
|
let pendingDone = true;
|
|
63046
|
+
const slot = deps.slot ?? 0;
|
|
63047
|
+
let started = false;
|
|
62406
63048
|
try {
|
|
63049
|
+
if (!/^[\w-]+$/.test(spec.projectId)) throw new Error("runner_error");
|
|
62407
63050
|
let cwd;
|
|
62408
63051
|
if (spec.kind === "github" && spec.github) {
|
|
62409
|
-
|
|
63052
|
+
started = true;
|
|
63053
|
+
clone2 = await prepareGithubFolder({ ...deps.paths, slot }, spec.projectId, spec.github, gitToken, deps.exec);
|
|
63054
|
+
cwd = clone2.folder;
|
|
62410
63055
|
} else {
|
|
62411
|
-
|
|
62412
|
-
|
|
62413
|
-
|
|
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-"));
|
|
62414
63060
|
if (deps.helperFactory) helper = await deps.helperFactory(spec);
|
|
62415
63061
|
}
|
|
63062
|
+
settingsFile = writeSettings(spec, deps, cwd);
|
|
62416
63063
|
const base = {
|
|
62417
63064
|
cwd,
|
|
62418
63065
|
systemPrompt: spec.systemPrompt,
|
|
@@ -62420,7 +63067,8 @@ async function runOne(spec, deps) {
|
|
|
62420
63067
|
helper: helper ? { url: helper.url, token: helper.token } : void 0,
|
|
62421
63068
|
env: {},
|
|
62422
63069
|
maxMinutes: spec.maxRunMinutes,
|
|
62423
|
-
claudeConfigDir: deps.paths.claudeConfigDir
|
|
63070
|
+
claudeConfigDir: deps.paths.claudeConfigDir,
|
|
63071
|
+
settingsFile
|
|
62424
63072
|
};
|
|
62425
63073
|
log.info(`run ${spec.runId}: start`);
|
|
62426
63074
|
const timedOut = (sessionId) => ({ outcome: "failed", error: "timeout", costUsd: 0, inputTokens: 0, outputTokens: 0, sessionId });
|
|
@@ -62433,10 +63081,26 @@ async function runOne(spec, deps) {
|
|
|
62433
63081
|
if (result.sessionId) totals.sessionId = result.sessionId;
|
|
62434
63082
|
const site = skipped ? void 0 : await helper?.checkSite?.();
|
|
62435
63083
|
await events.flush(totals);
|
|
62436
|
-
|
|
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
|
+
}
|
|
62437
63101
|
const answer = await sendDone(input2);
|
|
62438
63102
|
log.info(`run ${spec.runId}: done ${input2.outcome}${input2.error ? ` ${input2.error}` : ""}`);
|
|
62439
|
-
if (!answer.followUp || skipped) {
|
|
63103
|
+
if (!answer.followUp || skipped || pushFailed) {
|
|
62440
63104
|
pendingDone = false;
|
|
62441
63105
|
break;
|
|
62442
63106
|
}
|
|
@@ -62456,7 +63120,7 @@ async function runOne(spec, deps) {
|
|
|
62456
63120
|
log.error(`run ${spec.runId}: ${message}`);
|
|
62457
63121
|
if (pendingDone && helper?.checkSite) {
|
|
62458
63122
|
const site = await helper.checkSite().catch((e) => {
|
|
62459
|
-
log.warn(`run ${spec.runId}: site check failed: ${
|
|
63123
|
+
log.warn(`run ${spec.runId}: site check failed: ${errorText2(e)}`);
|
|
62460
63124
|
return "ok";
|
|
62461
63125
|
});
|
|
62462
63126
|
if (site !== "ok") message = site;
|
|
@@ -62464,20 +63128,98 @@ async function runOne(spec, deps) {
|
|
|
62464
63128
|
if (pendingDone) {
|
|
62465
63129
|
const usage = events.usage();
|
|
62466
63130
|
await events.flush();
|
|
62467
|
-
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)}`));
|
|
62468
63132
|
}
|
|
62469
63133
|
} finally {
|
|
62470
63134
|
events.stop();
|
|
62471
|
-
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
|
+
}
|
|
62472
63167
|
if (tempDir) {
|
|
62473
63168
|
try {
|
|
62474
|
-
|
|
63169
|
+
rmSync5(tempDir, { recursive: true, force: true });
|
|
62475
63170
|
} catch (e) {
|
|
62476
|
-
log.warn(`run ${spec.runId}: could not remove ${tempDir}: ${
|
|
63171
|
+
log.warn(`run ${spec.runId}: could not remove ${tempDir}: ${errorText2(e)}`);
|
|
62477
63172
|
}
|
|
62478
63173
|
}
|
|
62479
63174
|
}
|
|
62480
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
|
+
}
|
|
62481
63223
|
function wordpressHelperFactory(deps) {
|
|
62482
63224
|
const { log } = deps;
|
|
62483
63225
|
return async (spec) => {
|
|
@@ -62490,7 +63232,7 @@ function wordpressHelperFactory(deps) {
|
|
|
62490
63232
|
const backup = await backupBefore(transport, root);
|
|
62491
63233
|
if (backup.dump) {
|
|
62492
63234
|
log.info(`run ${spec.runId}: database backed up with ${backup.how} (${backup.dump.byteLength} bytes gzip)`);
|
|
62493
|
-
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)}`));
|
|
62494
63236
|
} else {
|
|
62495
63237
|
log.warn(`run ${spec.runId}: no database backup (${transport ? "WordPress folder not found" : "no SSH"})`);
|
|
62496
63238
|
}
|
|
@@ -62548,12 +63290,12 @@ async function doneWithRetry(runId, input2, api, log, sleep) {
|
|
|
62548
63290
|
const retryable = !(e instanceof ApiError) || e.status >= 500;
|
|
62549
63291
|
const wait = DONE_BACKOFF_MS[attempt];
|
|
62550
63292
|
if (!retryable || wait === void 0) throw e;
|
|
62551
|
-
log.warn(`run ${runId}: done failed (${
|
|
63293
|
+
log.warn(`run ${runId}: done failed (${errorText2(e)}); retrying in ${wait / 1e3} s`);
|
|
62552
63294
|
await sleep(wait);
|
|
62553
63295
|
}
|
|
62554
63296
|
}
|
|
62555
63297
|
}
|
|
62556
|
-
var
|
|
63298
|
+
var errorText2 = (e) => e instanceof Error ? e.message : String(e);
|
|
62557
63299
|
function doneInput(r, totals, secrets) {
|
|
62558
63300
|
const usage = { ...totals };
|
|
62559
63301
|
if (r.outcome === "ended") return { outcome: "failed", error: "no_report", ...usage };
|
|
@@ -62579,7 +63321,7 @@ function transcript(runId, api, log, secrets, now, totals) {
|
|
|
62579
63321
|
try {
|
|
62580
63322
|
await api.events(runId, body);
|
|
62581
63323
|
} catch (err) {
|
|
62582
|
-
log.warn(`run ${runId}: dropped ${body.lines.length} event lines: ${
|
|
63324
|
+
log.warn(`run ${runId}: dropped ${body.lines.length} event lines: ${errorText2(err)}`);
|
|
62583
63325
|
}
|
|
62584
63326
|
}
|
|
62585
63327
|
};
|
|
@@ -62612,72 +63354,144 @@ function transcript(runId, api, log, secrets, now, totals) {
|
|
|
62612
63354
|
}
|
|
62613
63355
|
|
|
62614
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
|
+
}
|
|
62615
63365
|
var HELLO_MS = 6e4;
|
|
63366
|
+
var SLOT_FOLDERS = 3;
|
|
62616
63367
|
var NETWORK_BACKOFF_MS = 5e3;
|
|
62617
63368
|
var NEXT_WAIT_SECONDS = 25;
|
|
62618
63369
|
async function startLoop(deps) {
|
|
62619
63370
|
const { api, log, signal } = deps;
|
|
62620
63371
|
const sleep = deps.sleep ?? ((ms) => delay2(ms, void 0, { signal }).catch(() => {
|
|
62621
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
|
+
};
|
|
62622
63397
|
const leftover = readState(deps.paths.stateFile);
|
|
62623
|
-
|
|
62624
|
-
log.warn(`run ${
|
|
62625
|
-
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(() => {
|
|
62626
63401
|
});
|
|
62627
|
-
clearState(deps.paths.stateFile);
|
|
62628
63402
|
}
|
|
62629
|
-
|
|
62630
|
-
|
|
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
|
+
};
|
|
62631
63432
|
await hello();
|
|
62632
|
-
const timer = setInterval(() => void hello(), HELLO_MS);
|
|
63433
|
+
const timer = setInterval(() => void hello(), deps.helloEveryMs ?? HELLO_MS);
|
|
62633
63434
|
timer.unref();
|
|
62634
63435
|
try {
|
|
62635
63436
|
while (!signal?.aborted) {
|
|
63437
|
+
if (running.size >= settings.runsAtOnce) {
|
|
63438
|
+
await Promise.race([...running.values()].map((r) => r.ended));
|
|
63439
|
+
continue;
|
|
63440
|
+
}
|
|
62636
63441
|
let spec;
|
|
62637
63442
|
try {
|
|
62638
63443
|
spec = await api.next(NEXT_WAIT_SECONDS, signal);
|
|
62639
63444
|
} catch (e) {
|
|
62640
63445
|
if (signal?.aborted) break;
|
|
62641
|
-
log.warn(`next failed: ${
|
|
63446
|
+
log.warn(`next failed: ${errorText3(e)}`);
|
|
62642
63447
|
await sleep(NETWORK_BACKOFF_MS);
|
|
62643
63448
|
continue;
|
|
62644
63449
|
}
|
|
62645
63450
|
if (!spec) continue;
|
|
62646
|
-
|
|
62647
|
-
|
|
62648
|
-
|
|
62649
|
-
|
|
62650
|
-
}
|
|
62651
|
-
try {
|
|
62652
|
-
await runOne(spec, { engine: deps.engine, api, paths: deps.paths, log, helperFactory: deps.helperFactory });
|
|
62653
|
-
} catch (e) {
|
|
62654
|
-
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;
|
|
62655
63455
|
}
|
|
62656
|
-
|
|
62657
|
-
|
|
62658
|
-
|
|
62659
|
-
|
|
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;
|
|
62660
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
|
+
});
|
|
62661
63470
|
}
|
|
63471
|
+
await Promise.all([...running.values()].map((r) => r.ended));
|
|
62662
63472
|
} finally {
|
|
62663
63473
|
clearInterval(timer);
|
|
62664
63474
|
}
|
|
62665
63475
|
}
|
|
62666
|
-
var
|
|
63476
|
+
var errorText3 = (e) => e instanceof Error ? e.message : String(e);
|
|
62667
63477
|
function readState(file2) {
|
|
62668
63478
|
try {
|
|
62669
|
-
const s = JSON.parse(
|
|
62670
|
-
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] : [];
|
|
62671
63482
|
} catch {
|
|
62672
|
-
return
|
|
63483
|
+
return [];
|
|
62673
63484
|
}
|
|
62674
63485
|
}
|
|
62675
|
-
function writeState(file2,
|
|
62676
|
-
|
|
62677
|
-
|
|
62678
|
-
|
|
62679
|
-
|
|
62680
|
-
|
|
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);
|
|
62681
63495
|
}
|
|
62682
63496
|
|
|
62683
63497
|
// src/commands.ts
|
|
@@ -62687,7 +63501,7 @@ var serviceOptions = (profile) => ({
|
|
|
62687
63501
|
profile,
|
|
62688
63502
|
paths: paths(profileDir(profile)),
|
|
62689
63503
|
exec: realExec,
|
|
62690
|
-
home:
|
|
63504
|
+
home: os6.homedir(),
|
|
62691
63505
|
env: process.env,
|
|
62692
63506
|
nodePath: process.execPath,
|
|
62693
63507
|
cliPath: process.argv[1]
|
|
@@ -62724,6 +63538,20 @@ async function start({ profile } = {}) {
|
|
|
62724
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({});
|
|
62725
63539
|
const v = fake ? null : await realExec("claude", ["--version"], { env: { ...process.env, CLAUDE_CONFIG_DIR: p.claudeConfigDir } });
|
|
62726
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
|
+
};
|
|
62727
63555
|
const ctrl = new AbortController();
|
|
62728
63556
|
const stop5 = (signal) => {
|
|
62729
63557
|
if (ctrl.signal.aborted) return;
|
|
@@ -62734,12 +63562,15 @@ async function start({ profile } = {}) {
|
|
|
62734
63562
|
process.on("SIGTERM", () => stop5("SIGTERM"));
|
|
62735
63563
|
process.on("SIGINT", () => stop5("SIGINT"));
|
|
62736
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)`);
|
|
62737
63566
|
if (process.stdout.isTTY) console.log(`Runner ${cfg.name} is waiting for runs from ${cfg.host}. Logs: ${p.log}`);
|
|
62738
63567
|
const api = new NotiamoApi(cfg);
|
|
62739
63568
|
await startLoop({
|
|
62740
63569
|
api,
|
|
62741
63570
|
engine,
|
|
62742
63571
|
paths: p,
|
|
63572
|
+
sandbox,
|
|
63573
|
+
settings,
|
|
62743
63574
|
log,
|
|
62744
63575
|
version: VERSION,
|
|
62745
63576
|
os: process.platform,
|
|
@@ -62780,10 +63611,17 @@ function logs({ profile } = {}) {
|
|
|
62780
63611
|
const lines2 = tail(paths(profileDir(profile)).log, 50);
|
|
62781
63612
|
console.log(lines2.length ? lines2.join("\n") : "No log yet.");
|
|
62782
63613
|
}
|
|
63614
|
+
function hostOf(url2) {
|
|
63615
|
+
try {
|
|
63616
|
+
return new URL(url2).hostname;
|
|
63617
|
+
} catch {
|
|
63618
|
+
return url2;
|
|
63619
|
+
}
|
|
63620
|
+
}
|
|
62783
63621
|
function tail(file2, n) {
|
|
62784
63622
|
let text;
|
|
62785
63623
|
try {
|
|
62786
|
-
text =
|
|
63624
|
+
text = readFileSync4(file2, "utf8");
|
|
62787
63625
|
} catch {
|
|
62788
63626
|
return [];
|
|
62789
63627
|
}
|