nexrall-code 0.5.46 → 0.5.48
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/dist/index.js +464 -96
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3156,6 +3156,8 @@ var require_auth = __commonJS({
|
|
|
3156
3156
|
exports.clearAuth = clearAuth2;
|
|
3157
3157
|
exports.isAuthenticated = isAuthenticated3;
|
|
3158
3158
|
exports.getToken = getToken;
|
|
3159
|
+
exports.getRefreshToken = getRefreshToken;
|
|
3160
|
+
exports.updateTokens = updateTokens;
|
|
3159
3161
|
var fs9 = __importStar(__require("fs"));
|
|
3160
3162
|
var path6 = __importStar(__require("path"));
|
|
3161
3163
|
var os6 = __importStar(__require("os"));
|
|
@@ -3205,6 +3207,34 @@ var require_auth = __commonJS({
|
|
|
3205
3207
|
const auth = loadAuth();
|
|
3206
3208
|
return auth?.token ?? null;
|
|
3207
3209
|
}
|
|
3210
|
+
function getRefreshToken() {
|
|
3211
|
+
const envToken = process.env.NEXRALL_TOKEN;
|
|
3212
|
+
if (envToken && envToken.trim().length > 0)
|
|
3213
|
+
return null;
|
|
3214
|
+
const auth = loadAuth();
|
|
3215
|
+
const rt = auth?.refreshToken;
|
|
3216
|
+
return typeof rt === "string" && rt.length > 0 ? rt : null;
|
|
3217
|
+
}
|
|
3218
|
+
function updateTokens(token, refreshToken) {
|
|
3219
|
+
let existing = {};
|
|
3220
|
+
try {
|
|
3221
|
+
if (fs9.existsSync(CONFIG_FILE)) {
|
|
3222
|
+
const parsed = JSON.parse(fs9.readFileSync(CONFIG_FILE, "utf-8"));
|
|
3223
|
+
if (typeof parsed === "object" && parsed !== null)
|
|
3224
|
+
existing = parsed;
|
|
3225
|
+
}
|
|
3226
|
+
} catch {
|
|
3227
|
+
}
|
|
3228
|
+
ensureConfigDir();
|
|
3229
|
+
const next = {
|
|
3230
|
+
...existing,
|
|
3231
|
+
token,
|
|
3232
|
+
// Keep the previous refresh token if the server didn't send a new one, rather
|
|
3233
|
+
// than deleting the only means of refreshing again.
|
|
3234
|
+
...refreshToken ? { refreshToken } : {}
|
|
3235
|
+
};
|
|
3236
|
+
fs9.writeFileSync(CONFIG_FILE, JSON.stringify(next, null, 2), { mode: 384 });
|
|
3237
|
+
}
|
|
3208
3238
|
}
|
|
3209
3239
|
});
|
|
3210
3240
|
|
|
@@ -9842,6 +9872,7 @@ var require_client = __commonJS({
|
|
|
9842
9872
|
exports.chooseFinalContent = chooseFinalContent;
|
|
9843
9873
|
exports.streamChat = streamChat;
|
|
9844
9874
|
exports.cancelTurn = cancelTurn;
|
|
9875
|
+
exports.revokeRefreshToken = revokeRefreshToken2;
|
|
9845
9876
|
exports.getBalance = getBalance3;
|
|
9846
9877
|
exports.exchangeVscodeCode = exchangeVscodeCode;
|
|
9847
9878
|
exports.login = login2;
|
|
@@ -9892,6 +9923,49 @@ var require_client = __commonJS({
|
|
|
9892
9923
|
Authorization: `Bearer ${token}`
|
|
9893
9924
|
};
|
|
9894
9925
|
}
|
|
9926
|
+
var _refreshInFlight = null;
|
|
9927
|
+
async function refreshAccessToken() {
|
|
9928
|
+
if (_refreshInFlight)
|
|
9929
|
+
return _refreshInFlight;
|
|
9930
|
+
_refreshInFlight = (async () => {
|
|
9931
|
+
const refreshToken = (0, index_1.getRefreshToken)();
|
|
9932
|
+
if (!refreshToken)
|
|
9933
|
+
return false;
|
|
9934
|
+
try {
|
|
9935
|
+
const res = await (0, node_fetch_1.default)(`${exports.API_BASE}/api/auth/refresh`, {
|
|
9936
|
+
method: "POST",
|
|
9937
|
+
headers: { "Content-Type": "application/json" },
|
|
9938
|
+
body: JSON.stringify({ refreshToken })
|
|
9939
|
+
});
|
|
9940
|
+
if (!res.ok)
|
|
9941
|
+
return false;
|
|
9942
|
+
const data = await res.json();
|
|
9943
|
+
if (!data.token)
|
|
9944
|
+
return false;
|
|
9945
|
+
(0, index_1.updateTokens)(data.token, data.refreshToken);
|
|
9946
|
+
return true;
|
|
9947
|
+
} catch {
|
|
9948
|
+
return false;
|
|
9949
|
+
}
|
|
9950
|
+
})();
|
|
9951
|
+
try {
|
|
9952
|
+
return await _refreshInFlight;
|
|
9953
|
+
} finally {
|
|
9954
|
+
_refreshInFlight = null;
|
|
9955
|
+
}
|
|
9956
|
+
}
|
|
9957
|
+
function isExpiredTokenResponse(status, body) {
|
|
9958
|
+
if (status !== 401 && status !== 403)
|
|
9959
|
+
return false;
|
|
9960
|
+
try {
|
|
9961
|
+
const parsed = JSON.parse(body);
|
|
9962
|
+
if (parsed?.error && typeof parsed.error === "object")
|
|
9963
|
+
return false;
|
|
9964
|
+
return typeof parsed?.error === "string" && /token|auth|expired/i.test(parsed.error);
|
|
9965
|
+
} catch {
|
|
9966
|
+
return false;
|
|
9967
|
+
}
|
|
9968
|
+
}
|
|
9895
9969
|
var MAX_RETRIES = 5;
|
|
9896
9970
|
var RETRY_BASE_MS = 1e3;
|
|
9897
9971
|
var RETRY_MAX_MS = 3e4;
|
|
@@ -9974,14 +10048,43 @@ var require_client = __commonJS({
|
|
|
9974
10048
|
onEvent({ type: "retry_resolved" });
|
|
9975
10049
|
}
|
|
9976
10050
|
};
|
|
10051
|
+
const prepareRestart = async (reason) => {
|
|
10052
|
+
await cancelTurn(turnId);
|
|
10053
|
+
turnId = (0, crypto_1.randomUUID)();
|
|
10054
|
+
resuming = false;
|
|
10055
|
+
serverResumable = false;
|
|
10056
|
+
lastEventId = 0;
|
|
10057
|
+
carryText = [];
|
|
10058
|
+
carryToolUse = [];
|
|
10059
|
+
const hadRendered = emittedAnythingAcrossAttempts;
|
|
10060
|
+
const renderedChars = emittedCharsAcrossAttempts;
|
|
10061
|
+
emittedAnythingAcrossAttempts = false;
|
|
10062
|
+
emittedCharsAcrossAttempts = 0;
|
|
10063
|
+
if (hadRendered && !allowRestartAfterRender)
|
|
10064
|
+
return false;
|
|
10065
|
+
if (hadRendered) {
|
|
10066
|
+
onEvent({ type: "stream_restart", reason, discardedChars: renderedChars });
|
|
10067
|
+
}
|
|
10068
|
+
return true;
|
|
10069
|
+
};
|
|
10070
|
+
const bodySaysNotResumable = (body) => {
|
|
10071
|
+
try {
|
|
10072
|
+
return JSON.parse(body)?.notResumable === true;
|
|
10073
|
+
} catch {
|
|
10074
|
+
return false;
|
|
10075
|
+
}
|
|
10076
|
+
};
|
|
9977
10077
|
async function runAttempt() {
|
|
9978
10078
|
let response;
|
|
9979
10079
|
let lastErr;
|
|
9980
10080
|
let inFlightWaits = 0;
|
|
9981
10081
|
let errorBodyOverride = null;
|
|
9982
10082
|
let forbiddenExhausted = false;
|
|
10083
|
+
let refreshedThisAttempt = false;
|
|
10084
|
+
let sessionExpired = false;
|
|
9983
10085
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
9984
10086
|
try {
|
|
10087
|
+
errorBodyOverride = null;
|
|
9985
10088
|
response = await (0, node_fetch_1.default)(...buildFetchArgs());
|
|
9986
10089
|
if (response.status === 429 && attempt < MAX_RETRIES && canRetry()) {
|
|
9987
10090
|
reportRetry("Rate limited by the API \u2014 retrying");
|
|
@@ -9994,9 +10097,30 @@ var require_client = __commonJS({
|
|
|
9994
10097
|
await sleepWithinBudget(backoffMs(attempt));
|
|
9995
10098
|
continue;
|
|
9996
10099
|
}
|
|
10100
|
+
if (response.status === 401 || response.status === 403) {
|
|
10101
|
+
const bodyText = await response.text().catch(() => "");
|
|
10102
|
+
if (isExpiredTokenResponse(response.status, bodyText)) {
|
|
10103
|
+
if (!refreshedThisAttempt) {
|
|
10104
|
+
refreshedThisAttempt = true;
|
|
10105
|
+
if (await refreshAccessToken()) {
|
|
10106
|
+
reportRetry("Session expired \u2014 signing you back in");
|
|
10107
|
+
attempt--;
|
|
10108
|
+
continue;
|
|
10109
|
+
}
|
|
10110
|
+
}
|
|
10111
|
+
sessionExpired = true;
|
|
10112
|
+
errorBodyOverride = JSON.stringify({
|
|
10113
|
+
// Replaces the backend's bare "Invalid or expired token", which reads
|
|
10114
|
+
// like a bug rather than something the user can act on.
|
|
10115
|
+
error: "Your session has expired. Run `nex login` to sign in again."
|
|
10116
|
+
});
|
|
10117
|
+
break;
|
|
10118
|
+
}
|
|
10119
|
+
errorBodyOverride = bodyText;
|
|
10120
|
+
}
|
|
9997
10121
|
const FORBIDDEN_RETRY_LIMIT = 2;
|
|
9998
10122
|
if (response.status === 403) {
|
|
9999
|
-
const bodyText = await response.text().catch(() => "");
|
|
10123
|
+
const bodyText = errorBodyOverride ?? await response.text().catch(() => "");
|
|
10000
10124
|
let isUpstreamForbidden = false;
|
|
10001
10125
|
try {
|
|
10002
10126
|
const parsed = JSON.parse(bodyText);
|
|
@@ -10016,35 +10140,22 @@ var require_client = __commonJS({
|
|
|
10016
10140
|
errorBodyOverride = bodyText;
|
|
10017
10141
|
break;
|
|
10018
10142
|
}
|
|
10019
|
-
if (response.status
|
|
10020
|
-
await response.text().catch(() => "");
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
throw new Error("Connection lost and this turn could no longer be resumed. Send your message again.");
|
|
10034
|
-
}
|
|
10035
|
-
if (hadRendered) {
|
|
10036
|
-
onEvent({
|
|
10037
|
-
type: "stream_restart",
|
|
10038
|
-
reason: "Connection lost too long to resume",
|
|
10039
|
-
discardedChars: renderedChars
|
|
10040
|
-
});
|
|
10041
|
-
}
|
|
10042
|
-
if (attempt < MAX_RETRIES && canRetry()) {
|
|
10043
|
-
reportRetry("Could not resume \u2014 restarting this turn");
|
|
10044
|
-
await sleepWithinBudget(backoffMs(attempt));
|
|
10045
|
-
continue;
|
|
10143
|
+
if (resuming && response.status >= 400 && response.status < 500) {
|
|
10144
|
+
const body = errorBodyOverride ?? await response.text().catch(() => "");
|
|
10145
|
+
if (response.status === 410 || response.status === 404 || bodySaysNotResumable(body)) {
|
|
10146
|
+
const canRestart = await prepareRestart("Connection lost too long to resume");
|
|
10147
|
+
if (!canRestart) {
|
|
10148
|
+
throw new Error("Connection lost and this turn could no longer be resumed. Send your message again.");
|
|
10149
|
+
}
|
|
10150
|
+
if (attempt < MAX_RETRIES && canRetry()) {
|
|
10151
|
+
reportRetry("Could not resume \u2014 restarting this turn");
|
|
10152
|
+
await sleepWithinBudget(backoffMs(attempt));
|
|
10153
|
+
continue;
|
|
10154
|
+
}
|
|
10155
|
+
errorBodyOverride = JSON.stringify({ error: "Could not resume this turn \u2014 send your message again." });
|
|
10156
|
+
break;
|
|
10046
10157
|
}
|
|
10047
|
-
errorBodyOverride =
|
|
10158
|
+
errorBodyOverride = body;
|
|
10048
10159
|
break;
|
|
10049
10160
|
}
|
|
10050
10161
|
if (response.status === 409) {
|
|
@@ -10096,7 +10207,12 @@ var require_client = __commonJS({
|
|
|
10096
10207
|
} catch {
|
|
10097
10208
|
errMsg = errText || errMsg;
|
|
10098
10209
|
}
|
|
10099
|
-
throw Object.assign(new Error(errMsg), {
|
|
10210
|
+
throw Object.assign(new Error(errMsg), {
|
|
10211
|
+
status: response.status,
|
|
10212
|
+
balance,
|
|
10213
|
+
...forbiddenExhausted ? { retryable: true } : {},
|
|
10214
|
+
...sessionExpired ? { authExpired: true } : {}
|
|
10215
|
+
});
|
|
10100
10216
|
}
|
|
10101
10217
|
if (!response.body) {
|
|
10102
10218
|
throw new Error("Response body is null");
|
|
@@ -10321,6 +10437,8 @@ var require_client = __commonJS({
|
|
|
10321
10437
|
stream.destroy?.();
|
|
10322
10438
|
reject(tagTransient(new Error(message)));
|
|
10323
10439
|
} else {
|
|
10440
|
+
clearInterval(heartbeatWatchdog);
|
|
10441
|
+
stream.destroy?.();
|
|
10324
10442
|
onEvent({ type: "error", message });
|
|
10325
10443
|
reject(new Error(message));
|
|
10326
10444
|
}
|
|
@@ -10381,27 +10499,10 @@ var require_client = __commonJS({
|
|
|
10381
10499
|
const e2 = err;
|
|
10382
10500
|
if (e2.retryable && sAttempt < MAX_RETRIES && canRetry()) {
|
|
10383
10501
|
if (e2.forceRestart) {
|
|
10384
|
-
await
|
|
10385
|
-
|
|
10386
|
-
resuming = false;
|
|
10387
|
-
serverResumable = false;
|
|
10388
|
-
lastEventId = 0;
|
|
10389
|
-
carryText = [];
|
|
10390
|
-
carryToolUse = [];
|
|
10391
|
-
const hadRendered = emittedAnythingAcrossAttempts;
|
|
10392
|
-
const renderedChars = emittedCharsAcrossAttempts;
|
|
10393
|
-
emittedAnythingAcrossAttempts = false;
|
|
10394
|
-
emittedCharsAcrossAttempts = 0;
|
|
10395
|
-
if (hadRendered && !allowRestartAfterRender) {
|
|
10502
|
+
const canRestart = await prepareRestart(err.message || "Could not resume \u2014 restarting this turn");
|
|
10503
|
+
if (!canRestart) {
|
|
10396
10504
|
throw new Error("Connection lost and this turn could no longer be resumed. Send your message again.");
|
|
10397
10505
|
}
|
|
10398
|
-
if (hadRendered) {
|
|
10399
|
-
onEvent({
|
|
10400
|
-
type: "stream_restart",
|
|
10401
|
-
reason: err.message || "Could not resume \u2014 restarting this turn",
|
|
10402
|
-
discardedChars: renderedChars
|
|
10403
|
-
});
|
|
10404
|
-
}
|
|
10405
10506
|
reportRetry("Could not resume \u2014 restarting this turn");
|
|
10406
10507
|
await sleepWithinBudget(backoffMs(sAttempt));
|
|
10407
10508
|
continue;
|
|
@@ -10445,11 +10546,30 @@ var require_client = __commonJS({
|
|
|
10445
10546
|
} catch {
|
|
10446
10547
|
}
|
|
10447
10548
|
}
|
|
10549
|
+
async function revokeRefreshToken2() {
|
|
10550
|
+
const refreshToken = (0, index_1.getRefreshToken)();
|
|
10551
|
+
if (!refreshToken)
|
|
10552
|
+
return;
|
|
10553
|
+
try {
|
|
10554
|
+
await (0, node_fetch_1.default)(`${exports.API_BASE}/api/auth/logout`, {
|
|
10555
|
+
method: "POST",
|
|
10556
|
+
headers: { "Content-Type": "application/json" },
|
|
10557
|
+
body: JSON.stringify({ refreshToken })
|
|
10558
|
+
});
|
|
10559
|
+
} catch {
|
|
10560
|
+
}
|
|
10561
|
+
}
|
|
10448
10562
|
async function getBalance3() {
|
|
10449
|
-
const
|
|
10450
|
-
|
|
10451
|
-
|
|
10452
|
-
|
|
10563
|
+
const fetchOnce = () => (0, node_fetch_1.default)(`${exports.API_BASE}/api/code/balance`, { method: "GET", headers: authHeaders() });
|
|
10564
|
+
let response = await fetchOnce();
|
|
10565
|
+
if (response.status === 401 || response.status === 403) {
|
|
10566
|
+
const body = await response.text().catch(() => "");
|
|
10567
|
+
if (isExpiredTokenResponse(response.status, body) && await refreshAccessToken()) {
|
|
10568
|
+
response = await fetchOnce();
|
|
10569
|
+
} else {
|
|
10570
|
+
throw new Error(`API error ${response.status}: ${body}`);
|
|
10571
|
+
}
|
|
10572
|
+
}
|
|
10453
10573
|
if (!response.ok) {
|
|
10454
10574
|
const errText = await response.text();
|
|
10455
10575
|
throw new Error(`API error ${response.status}: ${errText}`);
|
|
@@ -10470,7 +10590,11 @@ var require_client = __commonJS({
|
|
|
10470
10590
|
const data = await response.json();
|
|
10471
10591
|
if (!data.token)
|
|
10472
10592
|
throw new Error("No token in exchange response");
|
|
10473
|
-
return {
|
|
10593
|
+
return {
|
|
10594
|
+
token: data.token,
|
|
10595
|
+
...data.refreshToken ? { refreshToken: data.refreshToken } : {},
|
|
10596
|
+
email: data.user?.email ?? ""
|
|
10597
|
+
};
|
|
10474
10598
|
}
|
|
10475
10599
|
async function login2(email, password) {
|
|
10476
10600
|
const response = await (0, node_fetch_1.default)(`${exports.API_BASE}/api/auth/login/email`, {
|
|
@@ -10486,7 +10610,11 @@ var require_client = __commonJS({
|
|
|
10486
10610
|
if (!data.token) {
|
|
10487
10611
|
throw new Error("Login response missing token");
|
|
10488
10612
|
}
|
|
10489
|
-
return {
|
|
10613
|
+
return {
|
|
10614
|
+
token: data.token,
|
|
10615
|
+
...data.refreshToken ? { refreshToken: data.refreshToken } : {},
|
|
10616
|
+
email: data.email ?? email
|
|
10617
|
+
};
|
|
10490
10618
|
}
|
|
10491
10619
|
}
|
|
10492
10620
|
});
|
|
@@ -12606,13 +12734,7 @@ Resolve the conflict (remove <<<<<<< / ======= / >>>>>>> markers and keep the in
|
|
|
12606
12734
|
} catch {
|
|
12607
12735
|
}
|
|
12608
12736
|
}
|
|
12609
|
-
|
|
12610
|
-
if (existingMode !== void 0) {
|
|
12611
|
-
try {
|
|
12612
|
-
fs9.chmodSync(resolved, existingMode);
|
|
12613
|
-
} catch {
|
|
12614
|
-
}
|
|
12615
|
-
}
|
|
12737
|
+
atomicWrite(resolved, content, existingMode);
|
|
12616
12738
|
const bytes = Buffer.byteLength(content, "utf-8");
|
|
12617
12739
|
const lines = content.split("\n").length;
|
|
12618
12740
|
if (isNew) {
|
|
@@ -13326,14 +13448,52 @@ ${globalCapMatches(output)}` : "";
|
|
|
13326
13448
|
}
|
|
13327
13449
|
return { mode: stat2.mode, content: fs9.readFileSync(resolved, "utf-8") };
|
|
13328
13450
|
}
|
|
13329
|
-
function
|
|
13330
|
-
|
|
13331
|
-
fs9.writeFileSync(tmp, data, "utf-8");
|
|
13451
|
+
function atomicWrite(resolved, data, mode) {
|
|
13452
|
+
let target = resolved;
|
|
13332
13453
|
try {
|
|
13333
|
-
fs9.
|
|
13454
|
+
if (fs9.lstatSync(resolved).isSymbolicLink())
|
|
13455
|
+
target = fs9.realpathSync(resolved);
|
|
13334
13456
|
} catch {
|
|
13335
13457
|
}
|
|
13336
|
-
|
|
13458
|
+
const tmp = `${target}.nexrall_tmp_${process.pid}`;
|
|
13459
|
+
try {
|
|
13460
|
+
fs9.writeFileSync(tmp, data, "utf-8");
|
|
13461
|
+
} catch (err) {
|
|
13462
|
+
const code = err.code;
|
|
13463
|
+
if (code === "EACCES" || code === "EPERM" || code === "EROFS") {
|
|
13464
|
+
fs9.writeFileSync(target, data, "utf-8");
|
|
13465
|
+
if (mode !== void 0) {
|
|
13466
|
+
try {
|
|
13467
|
+
fs9.chmodSync(target, mode);
|
|
13468
|
+
} catch {
|
|
13469
|
+
}
|
|
13470
|
+
}
|
|
13471
|
+
return;
|
|
13472
|
+
}
|
|
13473
|
+
try {
|
|
13474
|
+
fs9.rmSync(tmp, { force: true });
|
|
13475
|
+
} catch {
|
|
13476
|
+
}
|
|
13477
|
+
throw err;
|
|
13478
|
+
}
|
|
13479
|
+
try {
|
|
13480
|
+
if (mode !== void 0) {
|
|
13481
|
+
try {
|
|
13482
|
+
fs9.chmodSync(tmp, mode);
|
|
13483
|
+
} catch {
|
|
13484
|
+
}
|
|
13485
|
+
}
|
|
13486
|
+
fs9.renameSync(tmp, target);
|
|
13487
|
+
} catch (err) {
|
|
13488
|
+
try {
|
|
13489
|
+
fs9.rmSync(tmp, { force: true });
|
|
13490
|
+
} catch {
|
|
13491
|
+
}
|
|
13492
|
+
throw err;
|
|
13493
|
+
}
|
|
13494
|
+
}
|
|
13495
|
+
function atomicWritePreservingMode(resolved, data, mode) {
|
|
13496
|
+
atomicWrite(resolved, data, mode);
|
|
13337
13497
|
}
|
|
13338
13498
|
function normalizeLF(s2) {
|
|
13339
13499
|
return s2.replace(/\r\n/g, "\n");
|
|
@@ -14738,6 +14898,10 @@ var require_loop = __commonJS({
|
|
|
14738
14898
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14739
14899
|
exports.VERIFY_CMD_RE = exports.WRITE_TOOL_NAMES = void 0;
|
|
14740
14900
|
exports.resolveMaxIterations = resolveMaxIterations;
|
|
14901
|
+
exports.createLimiter = createLimiter;
|
|
14902
|
+
exports.extractSubTaskText = extractSubTaskText;
|
|
14903
|
+
exports.capSubTaskText = capSubTaskText;
|
|
14904
|
+
exports.summariseSubTaskProgress = summariseSubTaskProgress;
|
|
14741
14905
|
exports.contextWindowFor = contextWindowFor2;
|
|
14742
14906
|
exports.compactionThresholds = compactionThresholds2;
|
|
14743
14907
|
exports.estimateBodyBytes = estimateBodyBytes2;
|
|
@@ -14900,6 +15064,29 @@ var require_loop = __commonJS({
|
|
|
14900
15064
|
_fileLocks.delete(absPath);
|
|
14901
15065
|
}
|
|
14902
15066
|
}
|
|
15067
|
+
var MAX_CONCURRENT_SUBTASKS = (() => {
|
|
15068
|
+
const raw = Number(process.env.NEXRALL_MAX_CONCURRENT_SUBTASKS);
|
|
15069
|
+
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 4;
|
|
15070
|
+
})();
|
|
15071
|
+
function createLimiter(max) {
|
|
15072
|
+
let active = 0;
|
|
15073
|
+
const queue = [];
|
|
15074
|
+
const release2 = () => {
|
|
15075
|
+
active--;
|
|
15076
|
+
queue.shift()?.();
|
|
15077
|
+
};
|
|
15078
|
+
return async (fn) => {
|
|
15079
|
+
if (active >= max)
|
|
15080
|
+
await new Promise((resolve3) => queue.push(resolve3));
|
|
15081
|
+
active++;
|
|
15082
|
+
try {
|
|
15083
|
+
return await fn();
|
|
15084
|
+
} finally {
|
|
15085
|
+
release2();
|
|
15086
|
+
}
|
|
15087
|
+
};
|
|
15088
|
+
}
|
|
15089
|
+
var _subTaskLimit = createLimiter(MAX_CONCURRENT_SUBTASKS);
|
|
14903
15090
|
function humanDescription(name, input) {
|
|
14904
15091
|
switch (name) {
|
|
14905
15092
|
case "read_file":
|
|
@@ -14981,16 +15168,71 @@ var require_loop = __commonJS({
|
|
|
14981
15168
|
return `Use tool: ${name}`;
|
|
14982
15169
|
}
|
|
14983
15170
|
}
|
|
14984
|
-
var MAX_TASK_DEPTH =
|
|
15171
|
+
var MAX_TASK_DEPTH = 1;
|
|
14985
15172
|
var _subTaskCounter = 0;
|
|
14986
15173
|
var SUBTASK_TIMEOUT_MS = Number(process.env.NEXRALL_SUBTASK_TIMEOUT_MS) > 0 ? Number(process.env.NEXRALL_SUBTASK_TIMEOUT_MS) : 10 * 60 * 1e3;
|
|
15174
|
+
var SUBTASK_MAX = 48e3;
|
|
15175
|
+
function sliceSafeEnd(s2, max) {
|
|
15176
|
+
if (s2.length <= max)
|
|
15177
|
+
return s2;
|
|
15178
|
+
let end = max;
|
|
15179
|
+
const code = s2.charCodeAt(end - 1);
|
|
15180
|
+
if (code >= 55296 && code <= 56319)
|
|
15181
|
+
end--;
|
|
15182
|
+
return s2.slice(0, end);
|
|
15183
|
+
}
|
|
15184
|
+
function sliceSafeStart(s2, from) {
|
|
15185
|
+
if (from <= 0)
|
|
15186
|
+
return s2;
|
|
15187
|
+
let start = from;
|
|
15188
|
+
const code = s2.charCodeAt(start);
|
|
15189
|
+
if (code >= 56320 && code <= 57343)
|
|
15190
|
+
start++;
|
|
15191
|
+
return s2.slice(start);
|
|
15192
|
+
}
|
|
15193
|
+
function extractSubTaskText(messages, preferLast = true) {
|
|
15194
|
+
const assistants = messages.filter((m2) => m2.role === "assistant");
|
|
15195
|
+
const textOf = (m2) => (m2?.content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("").trim();
|
|
15196
|
+
if (preferLast)
|
|
15197
|
+
return textOf(assistants[assistants.length - 1]);
|
|
15198
|
+
return assistants.map(textOf).filter(Boolean).join("\n\n").trim();
|
|
15199
|
+
}
|
|
15200
|
+
function capSubTaskText(text, max = SUBTASK_MAX) {
|
|
15201
|
+
if (text.length <= max)
|
|
15202
|
+
return text;
|
|
15203
|
+
const head = sliceSafeEnd(text, Math.floor(max * 0.6));
|
|
15204
|
+
const tail = sliceSafeStart(text, text.length - Math.floor(max * 0.4));
|
|
15205
|
+
return `${head}
|
|
15206
|
+
|
|
15207
|
+
[\u2026 sub-task output truncated (${text.length} chars) \u2014 kept the beginning and end \u2026]
|
|
15208
|
+
|
|
15209
|
+
${tail}`;
|
|
15210
|
+
}
|
|
15211
|
+
function summariseSubTaskProgress(messages) {
|
|
15212
|
+
const toolNames = [];
|
|
15213
|
+
for (const m2 of messages) {
|
|
15214
|
+
if (m2.role !== "assistant" || !Array.isArray(m2.content))
|
|
15215
|
+
continue;
|
|
15216
|
+
for (const b of m2.content) {
|
|
15217
|
+
if (b?.type === "tool_use" && typeof b.name === "string")
|
|
15218
|
+
toolNames.push(b.name);
|
|
15219
|
+
}
|
|
15220
|
+
}
|
|
15221
|
+
if (toolNames.length === 0)
|
|
15222
|
+
return "";
|
|
15223
|
+
const counts = /* @__PURE__ */ new Map();
|
|
15224
|
+
for (const n of toolNames)
|
|
15225
|
+
counts.set(n, (counts.get(n) ?? 0) + 1);
|
|
15226
|
+
const inventory = [...counts.entries()].sort((a, b) => b[1] - a[1]).map(([name, n]) => n > 1 ? `${name} \xD7${n}` : name).join(", ");
|
|
15227
|
+
return `Tool calls completed before it was stopped (${toolNames.length} total): ${inventory}.`;
|
|
15228
|
+
}
|
|
14987
15229
|
async function runSubTask(input, options, agentTypes) {
|
|
14988
15230
|
const prompt2 = typeof input.prompt === "string" ? input.prompt.trim() : "";
|
|
14989
15231
|
if (!prompt2)
|
|
14990
15232
|
return { error: "task tool requires a non-empty prompt" };
|
|
14991
15233
|
const depth = options._depth ?? 0;
|
|
14992
15234
|
if (depth >= MAX_TASK_DEPTH) {
|
|
14993
|
-
return { error:
|
|
15235
|
+
return { error: "Sub-agents cannot spawn further sub-agents. Do this work directly, or report back so the main agent can delegate it." };
|
|
14994
15236
|
}
|
|
14995
15237
|
const requestedType = typeof input.subagent_type === "string" ? input.subagent_type : "";
|
|
14996
15238
|
const agent = (0, agentTypes_1.findAgentType)(agentTypes, requestedType);
|
|
@@ -15064,22 +15306,36 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
15064
15306
|
onThinkingProgress: (tok) => options.onThinkingProgress?.(tok)
|
|
15065
15307
|
});
|
|
15066
15308
|
if (subAbort.aborted && !options.abortSignal?.aborted) {
|
|
15067
|
-
|
|
15068
|
-
|
|
15069
|
-
|
|
15070
|
-
|
|
15071
|
-
|
|
15072
|
-
|
|
15073
|
-
|
|
15074
|
-
|
|
15075
|
-
|
|
15076
|
-
|
|
15077
|
-
|
|
15078
|
-
|
|
15079
|
-
|
|
15080
|
-
|
|
15309
|
+
const mins = Math.round(SUBTASK_TIMEOUT_MS / 6e4);
|
|
15310
|
+
const partial = capSubTaskText(extractSubTaskText(result, false));
|
|
15311
|
+
const progress = summariseSubTaskProgress(result);
|
|
15312
|
+
const sections = [
|
|
15313
|
+
`Sub-task STOPPED after ${mins} minutes without completing \u2014 treat the following as PARTIAL, unverified work, not a finished answer.`,
|
|
15314
|
+
progress,
|
|
15315
|
+
partial ? `Partial output before it was stopped:
|
|
15316
|
+
|
|
15317
|
+
${partial}` : "",
|
|
15318
|
+
"Do NOT simply re-run the same sub-task: build on what is above, or split the remaining work into smaller, more focused sub-tasks."
|
|
15319
|
+
].filter(Boolean);
|
|
15320
|
+
return { error: sections.join("\n\n") };
|
|
15321
|
+
}
|
|
15322
|
+
const text = capSubTaskText(extractSubTaskText(result, true));
|
|
15081
15323
|
return { output: text || "(sub-task completed with no text output)" };
|
|
15082
15324
|
} catch (err) {
|
|
15325
|
+
const salvaged = (0, types_1.salvageHistory)(err);
|
|
15326
|
+
if (salvaged) {
|
|
15327
|
+
const partial = capSubTaskText(extractSubTaskText(salvaged, false));
|
|
15328
|
+
const progress = summariseSubTaskProgress(salvaged);
|
|
15329
|
+
const sections = [
|
|
15330
|
+
`Sub-task FAILED before completing: ${err.message}`,
|
|
15331
|
+
progress,
|
|
15332
|
+
partial ? `Partial output before the failure:
|
|
15333
|
+
|
|
15334
|
+
${partial}` : "",
|
|
15335
|
+
"Treat the above as PARTIAL, unverified work. Build on it rather than re-running the whole sub-task."
|
|
15336
|
+
].filter(Boolean);
|
|
15337
|
+
return { error: sections.join("\n\n") };
|
|
15338
|
+
}
|
|
15083
15339
|
return { error: `Sub-task failed: ${err.message}` };
|
|
15084
15340
|
} finally {
|
|
15085
15341
|
clearTimeout(timer);
|
|
@@ -15678,7 +15934,7 @@ Continue the work from here.` }] });
|
|
|
15678
15934
|
if (!permitted) {
|
|
15679
15935
|
result = { error: "Permission denied by user" };
|
|
15680
15936
|
} else if (name === "task") {
|
|
15681
|
-
result = await runSubTask(input, options, agentTypes);
|
|
15937
|
+
result = depth === 0 ? await _subTaskLimit(() => runSubTask(input, options, agentTypes)) : await runSubTask(input, options, agentTypes);
|
|
15682
15938
|
} else {
|
|
15683
15939
|
const pre = runToolHooks(hooks.PreToolUse, "PreToolUse", name, input, options.workDir);
|
|
15684
15940
|
if (pre.block) {
|
|
@@ -51904,6 +52160,7 @@ async function logoutCommand() {
|
|
|
51904
52160
|
console.log();
|
|
51905
52161
|
return;
|
|
51906
52162
|
}
|
|
52163
|
+
await (0, import_code_core.revokeRefreshToken)();
|
|
51907
52164
|
(0, import_code_core.clearAuth)();
|
|
51908
52165
|
console.log(source_default.green(" \u2713 Logged out."));
|
|
51909
52166
|
console.log();
|
|
@@ -62255,6 +62512,8 @@ var use_app_default = useApp;
|
|
|
62255
62512
|
|
|
62256
62513
|
// ../../node_modules/.pnpm/ink@7.1.1_@types+react@19.2.18_react@19.2.8/node_modules/ink/build/hooks/use-stdout.js
|
|
62257
62514
|
var import_react25 = __toESM(require_react(), 1);
|
|
62515
|
+
var useStdout = () => (0, import_react25.useContext)(StdoutContext_default);
|
|
62516
|
+
var use_stdout_default = useStdout;
|
|
62258
62517
|
|
|
62259
62518
|
// ../../node_modules/.pnpm/ink@7.1.1_@types+react@19.2.18_react@19.2.8/node_modules/ink/build/hooks/use-stderr.js
|
|
62260
62519
|
var import_react26 = __toESM(require_react(), 1);
|
|
@@ -62276,6 +62535,21 @@ var import_react31 = __toESM(require_react(), 1);
|
|
|
62276
62535
|
|
|
62277
62536
|
// ../../node_modules/.pnpm/ink@7.1.1_@types+react@19.2.18_react@19.2.8/node_modules/ink/build/hooks/use-window-size.js
|
|
62278
62537
|
var import_react32 = __toESM(require_react(), 1);
|
|
62538
|
+
var useWindowSize = () => {
|
|
62539
|
+
const { stdout } = use_stdout_default();
|
|
62540
|
+
const [size, setSize] = (0, import_react32.useState)(() => getWindowSize(stdout));
|
|
62541
|
+
(0, import_react32.useEffect)(() => {
|
|
62542
|
+
const onResize = () => {
|
|
62543
|
+
setSize(getWindowSize(stdout));
|
|
62544
|
+
};
|
|
62545
|
+
stdout.on("resize", onResize);
|
|
62546
|
+
return () => {
|
|
62547
|
+
stdout.off("resize", onResize);
|
|
62548
|
+
};
|
|
62549
|
+
}, [stdout]);
|
|
62550
|
+
return size;
|
|
62551
|
+
};
|
|
62552
|
+
var use_window_size_default = useWindowSize;
|
|
62279
62553
|
|
|
62280
62554
|
// ../../node_modules/.pnpm/ink@7.1.1_@types+react@19.2.18_react@19.2.8/node_modules/ink/build/hooks/use-box-metrics.js
|
|
62281
62555
|
var import_react33 = __toESM(require_react(), 1);
|
|
@@ -62378,6 +62652,7 @@ var DEFAULT_PROMPT = colors.primary.bold("> ");
|
|
|
62378
62652
|
var App2 = ({ onReady }) => {
|
|
62379
62653
|
const [items, setItems] = (0, import_react35.useState)([]);
|
|
62380
62654
|
const [footer, setFooterState] = (0, import_react35.useState)({ mode: "auto", autoApprove: false });
|
|
62655
|
+
const { columns } = use_window_size_default();
|
|
62381
62656
|
const [inputEnabled, setInputEnabledState] = (0, import_react35.useState)(true);
|
|
62382
62657
|
const [line, setLineState] = (0, import_react35.useState)("");
|
|
62383
62658
|
const [prompt2, setPrompt] = (0, import_react35.useState)(DEFAULT_PROMPT);
|
|
@@ -62446,7 +62721,7 @@ var App2 = ({ onReady }) => {
|
|
|
62446
62721
|
import_react35.default.useEffect(() => {
|
|
62447
62722
|
onReady({ print, setFooter, setLive, askLine, onLine, setInputEnabled, close: () => exit() });
|
|
62448
62723
|
}, []);
|
|
62449
|
-
return /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react35.default.createElement(Static, { items }, (item) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: item.id }, item.content)), live ? /* @__PURE__ */ import_react35.default.createElement(Text, null, live) : null, /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true }, "\u2500".repeat(Math.max(1,
|
|
62724
|
+
return /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react35.default.createElement(Static, { items }, (item) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: item.id }, item.content)), live ? /* @__PURE__ */ import_react35.default.createElement(Text, null, live) : null, /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true }, "\u2500".repeat(Math.max(1, columns - 1))), inputEnabled ? /* @__PURE__ */ import_react35.default.createElement(Box_default, null, /* @__PURE__ */ import_react35.default.createElement(Text, null, prompt2), /* @__PURE__ */ import_react35.default.createElement(Text, null, line), /* @__PURE__ */ import_react35.default.createElement(Text, { inverse: true }, " ")) : /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true }, " (working\u2026)"), /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true }, footerText(footer)));
|
|
62450
62725
|
};
|
|
62451
62726
|
function startInkTerminal() {
|
|
62452
62727
|
if (handle)
|
|
@@ -62519,7 +62794,7 @@ var InkReadlineAdapter = class extends EventEmitter3 {
|
|
|
62519
62794
|
};
|
|
62520
62795
|
|
|
62521
62796
|
// src/commands/chat.ts
|
|
62522
|
-
var CLI_VERSION = "0.5.
|
|
62797
|
+
var CLI_VERSION = "0.5.48";
|
|
62523
62798
|
var MODEL_LABELS = {
|
|
62524
62799
|
turbo: "Nexrall Turbo",
|
|
62525
62800
|
pro: "Nexrall Pro",
|
|
@@ -62604,28 +62879,83 @@ ${content}`);
|
|
|
62604
62879
|
}
|
|
62605
62880
|
return parts.length > 0 ? parts.join("\n\n---\n\n") : void 0;
|
|
62606
62881
|
}
|
|
62882
|
+
var THINK_TAIL_CHARS = 48;
|
|
62883
|
+
function composeStatusLine(opts) {
|
|
62884
|
+
const timer = source_default.dim(` (${opts.elapsedSec}s)`);
|
|
62885
|
+
const hint = opts.hint !== false && !opts.detail && opts.elapsedSec >= 15 ? source_default.dim(" \xB7 still running, no output yet is normal for quiet commands") : "";
|
|
62886
|
+
const detail = opts.detail ? source_default.dim(` ${opts.detail}`) : "";
|
|
62887
|
+
return `${source_default.cyan(opts.frame)} ${source_default.dim(opts.text)}${timer}${detail}${hint}`;
|
|
62888
|
+
}
|
|
62889
|
+
function formatProgressTokens(tokens) {
|
|
62890
|
+
if (!Number.isFinite(tokens) || tokens <= 0)
|
|
62891
|
+
return "";
|
|
62892
|
+
return tokens >= 1e3 ? `~${(tokens / 1e3).toFixed(1)}k tokens` : `~${tokens} tokens`;
|
|
62893
|
+
}
|
|
62607
62894
|
var Spinner = class {
|
|
62608
62895
|
interval = null;
|
|
62609
62896
|
frames = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
|
|
62610
62897
|
i = 0;
|
|
62611
62898
|
startedAt = 0;
|
|
62612
62899
|
baseText = "";
|
|
62613
|
-
|
|
62900
|
+
// Extra live detail appended after the elapsed timer (e.g. "~1.2k tokens").
|
|
62901
|
+
// Kept separate from `baseText` and mutated through setDetail() so a stream of
|
|
62902
|
+
// progress events can refresh it WITHOUT restarting the spinner — calling
|
|
62903
|
+
// start() again would reset `startedAt` and the elapsed counter would sit at 0s
|
|
62904
|
+
// forever, which is precisely the "is it frozen?" signal this class exists to
|
|
62905
|
+
// remove.
|
|
62906
|
+
detail = "";
|
|
62907
|
+
// Whether the reassurance hint applies. Off for phases where slowness is
|
|
62908
|
+
// already explained by the detail text (e.g. a live token counter is itself
|
|
62909
|
+
// proof of life, so "no output yet is normal" would be noise).
|
|
62910
|
+
hintEnabled = true;
|
|
62911
|
+
start(text, opts) {
|
|
62614
62912
|
this.stop();
|
|
62615
62913
|
this.baseText = text;
|
|
62914
|
+
this.detail = "";
|
|
62915
|
+
this.hintEnabled = opts?.hint !== false;
|
|
62616
62916
|
this.startedAt = Date.now();
|
|
62617
|
-
this.
|
|
62618
|
-
|
|
62619
|
-
|
|
62620
|
-
|
|
62621
|
-
|
|
62622
|
-
|
|
62623
|
-
|
|
62917
|
+
this.render();
|
|
62918
|
+
this.interval = setInterval(() => this.render(), 100);
|
|
62919
|
+
}
|
|
62920
|
+
/**
|
|
62921
|
+
* Replace the trailing detail text in place, keeping the elapsed timer running.
|
|
62922
|
+
*
|
|
62923
|
+
* No-op when the spinner isn't active: progress events can arrive a tick after
|
|
62924
|
+
* something else (a tool row, the first text delta) legitimately stopped it, and
|
|
62925
|
+
* resurrecting the spinner there would fight the printed output for the status line.
|
|
62926
|
+
*/
|
|
62927
|
+
setDetail(detail) {
|
|
62928
|
+
if (this.interval === null)
|
|
62929
|
+
return;
|
|
62930
|
+
if (this.detail === detail)
|
|
62931
|
+
return;
|
|
62932
|
+
this.detail = detail;
|
|
62933
|
+
this.render();
|
|
62934
|
+
}
|
|
62935
|
+
/** Swap the label without resetting the elapsed timer (phase change within one turn). */
|
|
62936
|
+
setText(text) {
|
|
62937
|
+
if (this.interval === null)
|
|
62938
|
+
return;
|
|
62939
|
+
if (this.baseText === text)
|
|
62940
|
+
return;
|
|
62941
|
+
this.baseText = text;
|
|
62942
|
+
this.render();
|
|
62943
|
+
}
|
|
62944
|
+
render() {
|
|
62945
|
+
getInkTerminal()?.setLive(composeStatusLine({
|
|
62946
|
+
frame: this.frames[this.i % this.frames.length],
|
|
62947
|
+
text: this.baseText,
|
|
62948
|
+
elapsedSec: Math.floor((Date.now() - this.startedAt) / 1e3),
|
|
62949
|
+
detail: this.detail,
|
|
62950
|
+
hint: this.hintEnabled
|
|
62951
|
+
}));
|
|
62952
|
+
this.i++;
|
|
62624
62953
|
}
|
|
62625
62954
|
stop() {
|
|
62626
62955
|
if (this.interval !== null) {
|
|
62627
62956
|
clearInterval(this.interval);
|
|
62628
62957
|
this.interval = null;
|
|
62958
|
+
this.detail = "";
|
|
62629
62959
|
getInkTerminal()?.setLive("");
|
|
62630
62960
|
}
|
|
62631
62961
|
}
|
|
@@ -62701,8 +63031,26 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrall
|
|
|
62701
63031
|
let toolStartTime = 0;
|
|
62702
63032
|
let lastToolName = "";
|
|
62703
63033
|
let thinkingTokens = 0;
|
|
63034
|
+
let liveThinkTail = "";
|
|
63035
|
+
const pushStatus = () => {
|
|
63036
|
+
const parts = [];
|
|
63037
|
+
const tok = formatProgressTokens(thinkingTokens);
|
|
63038
|
+
if (tok)
|
|
63039
|
+
parts.push(tok);
|
|
63040
|
+
if (liveThinkTail)
|
|
63041
|
+
parts.push(liveThinkTail);
|
|
63042
|
+
spinner.setDetail(parts.join(" \xB7 "));
|
|
63043
|
+
};
|
|
63044
|
+
const resumeWorking = (label = "working\u2026") => {
|
|
63045
|
+
if (abortSignal.aborted)
|
|
63046
|
+
return;
|
|
63047
|
+
thinkingTokens = 0;
|
|
63048
|
+
liveThinkTail = "";
|
|
63049
|
+
spinner.start(label, { hint: false });
|
|
63050
|
+
};
|
|
62704
63051
|
setMode(mode);
|
|
62705
63052
|
console.log();
|
|
63053
|
+
spinner.start("thinking\u2026", { hint: false });
|
|
62706
63054
|
const result = await (0, import_code_core3.runAgentLoop)(messages, {
|
|
62707
63055
|
workDir,
|
|
62708
63056
|
model: modelAlias,
|
|
@@ -62710,10 +63058,26 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrall
|
|
|
62710
63058
|
nexrallMd,
|
|
62711
63059
|
mode,
|
|
62712
63060
|
effort,
|
|
63061
|
+
// The backend streams a cumulative output-token count (routes/code.js's
|
|
63062
|
+
// sendProgress, throttled to ≤5/s) covering thinking, visible text AND
|
|
63063
|
+
// tool-argument JSON. This used to be stored in a variable and rendered only
|
|
63064
|
+
// at message_complete — i.e. after the turn was already over — so during the
|
|
63065
|
+
// long phase it exists to describe, it showed nothing at all.
|
|
62713
63066
|
onThinkingProgress: (tokens) => {
|
|
62714
63067
|
thinkingTokens = tokens;
|
|
63068
|
+
pushStatus();
|
|
62715
63069
|
},
|
|
62716
|
-
|
|
63070
|
+
// Live thinking text. Previously a hard no-op with the note "shown on
|
|
63071
|
+
// onThinking" — but onThinking only fires at message_complete, so a long
|
|
63072
|
+
// reasoning phase rendered nothing whatsoever until it had finished. Show a
|
|
63073
|
+
// short rolling tail on the status line so the user can see it actively
|
|
63074
|
+
// reasoning, then let onThinking print the proper summary block at the end.
|
|
63075
|
+
onThinkingDelta: (text) => {
|
|
63076
|
+
if (abortSignal.aborted)
|
|
63077
|
+
return;
|
|
63078
|
+
const flat = text.replace(/\s+/g, " ");
|
|
63079
|
+
liveThinkTail = (liveThinkTail + flat).slice(-THINK_TAIL_CHARS);
|
|
63080
|
+
pushStatus();
|
|
62717
63081
|
},
|
|
62718
63082
|
onThinking: (text) => {
|
|
62719
63083
|
if (abortSignal.aborted)
|
|
@@ -62721,6 +63085,8 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrall
|
|
|
62721
63085
|
spinner.stop();
|
|
62722
63086
|
console.log(formatThinking(text, thinkingTokens));
|
|
62723
63087
|
thinkingTokens = 0;
|
|
63088
|
+
liveThinkTail = "";
|
|
63089
|
+
resumeWorking();
|
|
62724
63090
|
},
|
|
62725
63091
|
onText: (text) => {
|
|
62726
63092
|
if (abortSignal.aborted)
|
|
@@ -62739,6 +63105,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrall
|
|
|
62739
63105
|
mdRender.flush();
|
|
62740
63106
|
spinner.stop();
|
|
62741
63107
|
console.log(source_default.dim(` ${text}`));
|
|
63108
|
+
resumeWorking();
|
|
62742
63109
|
},
|
|
62743
63110
|
onToolUse: (name, input) => {
|
|
62744
63111
|
if (abortSignal.aborted)
|
|
@@ -62769,6 +63136,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrall
|
|
|
62769
63136
|
const durationMs = Date.now() - toolStartTime;
|
|
62770
63137
|
console.log(formatToolResult(lastToolName, res, durationMs));
|
|
62771
63138
|
toolStartTime = 0;
|
|
63139
|
+
resumeWorking();
|
|
62772
63140
|
},
|
|
62773
63141
|
// Ignore a `partial` report: it belongs to a cut-short attempt that was restarted,
|
|
62774
63142
|
// and the replacement attempt reports the turn's real totals. Letting it through
|
|
@@ -63971,7 +64339,7 @@ function pluginListCommand() {
|
|
|
63971
64339
|
|
|
63972
64340
|
// src/index.ts
|
|
63973
64341
|
var program2 = new Command();
|
|
63974
|
-
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.
|
|
64342
|
+
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.48");
|
|
63975
64343
|
program2.command("auth").description("Login to your Nexrall account").action(authCommand);
|
|
63976
64344
|
program2.command("logout").description("Log out of your Nexrall account").action(logoutCommand);
|
|
63977
64345
|
program2.command("update").description("Update nex to the latest version").option("-c, --check", "Check for updates without installing").option("-y, --yes", "Skip confirmation prompt").action(async (opts) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexrall-code",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.48",
|
|
4
4
|
"description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"react": "^19.2.8",
|
|
42
42
|
"readline": "^1.3.0",
|
|
43
43
|
"string-width": "^7.2.0",
|
|
44
|
-
"@nexrall/code-core": "1.4.
|
|
44
|
+
"@nexrall/code-core": "1.4.23"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@aws-sdk/client-s3": "^3.600.0",
|