steward-cli 0.11.0 → 0.12.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 +1 -1
- package/dist/cli.js +557 -278
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Steward
|
|
2
2
|
|
|
3
|
-
[](https://github.com/sapirrior/steward/releases)
|
|
4
4
|
[](https://www.npmjs.com/package/steward-cli)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://bun.sh)
|
package/dist/cli.js
CHANGED
|
@@ -44696,15 +44696,6 @@ function wrapVisualLineWithCursor(text, maxCols, targetCharOffset, hangingIndent
|
|
|
44696
44696
|
}
|
|
44697
44697
|
function emitCurrentLine() {
|
|
44698
44698
|
const activeStyles = getActiveStyleCodes();
|
|
44699
|
-
if (activeBg && currentWidth < maxCols) {
|
|
44700
|
-
const remainingCols = maxCols - currentWidth;
|
|
44701
|
-
currentTokens.push({
|
|
44702
|
-
type: "char",
|
|
44703
|
-
value: " ".repeat(remainingCols),
|
|
44704
|
-
width: remainingCols
|
|
44705
|
-
});
|
|
44706
|
-
currentWidth = maxCols;
|
|
44707
|
-
}
|
|
44708
44699
|
let lineStr = currentTokens.map((t) => t.value).join("");
|
|
44709
44700
|
if (activeStyles.length > 0 && !lineStr.endsWith("\x1B[0m")) {
|
|
44710
44701
|
lineStr += "\x1B[0m";
|
|
@@ -52415,23 +52406,12 @@ function formatToolStatus(options) {
|
|
|
52415
52406
|
const maxOutLen = Math.max(10, fullTermWidth - 6);
|
|
52416
52407
|
const truncatedSummary = summaryText.length > maxOutLen ? `${summaryText.slice(0, maxOutLen - 1)}…` : summaryText;
|
|
52417
52408
|
lines.push(` ${source_default.dim("└ ")}${source_default.white(truncatedSummary)}`);
|
|
52418
|
-
const
|
|
52419
|
-
const diffAddBg = themeBgColor(theme.diffAddBG);
|
|
52420
|
-
const rawDiffDelBg = theme.diffDeleteBG;
|
|
52421
|
-
const rawDiffAddBg = theme.diffAddBG;
|
|
52409
|
+
const maxContentWidth = Math.max(30, Math.min(fullTermWidth - 4, Math.floor(fullTermWidth * 0.85)));
|
|
52422
52410
|
for (let i = 1;i < outputLines.length; i++) {
|
|
52423
|
-
const
|
|
52424
|
-
const
|
|
52425
|
-
const
|
|
52426
|
-
|
|
52427
|
-
if (isDelete || isAdd) {
|
|
52428
|
-
const bgFn = isDelete ? diffDeleteBg : diffAddBg;
|
|
52429
|
-
const visLen = 3 + stringWidth(plain);
|
|
52430
|
-
const padLen = Math.max(0, fullTermWidth - visLen);
|
|
52431
|
-
lines.push(bgFn(` ${line}${" ".repeat(padLen)}`));
|
|
52432
|
-
} else {
|
|
52433
|
-
lines.push(` ${line}`);
|
|
52434
|
-
}
|
|
52411
|
+
const rawLine = outputLines[i] ?? "";
|
|
52412
|
+
const prefixed = ` ${rawLine}`;
|
|
52413
|
+
const wrapped = wrapVisualLine(prefixed, maxContentWidth, " ");
|
|
52414
|
+
lines.push(...wrapped);
|
|
52435
52415
|
}
|
|
52436
52416
|
}
|
|
52437
52417
|
}
|
|
@@ -71099,7 +71079,8 @@ var writeFileTool = {
|
|
|
71099
71079
|
summarize: (args, result) => {
|
|
71100
71080
|
const lines = result?.linesWritten ?? 0;
|
|
71101
71081
|
const filePath = args.file_path;
|
|
71102
|
-
const
|
|
71082
|
+
const isNew = result?.isNew ?? true;
|
|
71083
|
+
const summary = isNew ? `Created ${filePath} (${lines} line${lines === 1 ? "" : "s"})` : `Wrote ${lines} line${lines === 1 ? "" : "s"} to ${filePath}`;
|
|
71103
71084
|
const contentLines = args.content.split(/\r?\n/);
|
|
71104
71085
|
if (contentLines.length === 0 || contentLines.length === 1 && !contentLines[0]) {
|
|
71105
71086
|
return summary;
|
|
@@ -71107,7 +71088,7 @@ var writeFileTool = {
|
|
|
71107
71088
|
const cap = 50;
|
|
71108
71089
|
const shown = contentLines.slice(0, cap);
|
|
71109
71090
|
const padWidth = String(contentLines.length).length;
|
|
71110
|
-
const detail = shown.map((l, i) => `${String(i + 1).padStart(padWidth)} ${l}`).join(`
|
|
71091
|
+
const detail = shown.map((l, i) => `${source_default.dim(String(i + 1).padStart(padWidth))} ${source_default.white(l)}`).join(`
|
|
71111
71092
|
`);
|
|
71112
71093
|
const overflow = contentLines.length > cap ? `
|
|
71113
71094
|
… (${contentLines.length - cap} more lines)` : "";
|
|
@@ -71233,6 +71214,237 @@ import {
|
|
|
71233
71214
|
writeSync as writeSync6
|
|
71234
71215
|
} from "node:fs";
|
|
71235
71216
|
import { dirname as dirname6, join as join8 } from "node:path";
|
|
71217
|
+
|
|
71218
|
+
// src/utils/diff.ts
|
|
71219
|
+
function tokenizeLines(content) {
|
|
71220
|
+
if (!content)
|
|
71221
|
+
return [];
|
|
71222
|
+
const tokens = [];
|
|
71223
|
+
const regex = /([^\r\n]*)(\r?\n)?/g;
|
|
71224
|
+
let match;
|
|
71225
|
+
while ((match = regex.exec(content)) !== null) {
|
|
71226
|
+
if (match.index === content.length)
|
|
71227
|
+
break;
|
|
71228
|
+
const text = match[1] ?? "";
|
|
71229
|
+
const ending = match[2] ?? "";
|
|
71230
|
+
tokens.push({ text, ending });
|
|
71231
|
+
if (!ending && match.index + text.length === content.length)
|
|
71232
|
+
break;
|
|
71233
|
+
}
|
|
71234
|
+
return tokens;
|
|
71235
|
+
}
|
|
71236
|
+
function tokenizeWords(text) {
|
|
71237
|
+
if (!text)
|
|
71238
|
+
return [];
|
|
71239
|
+
const matches = text.match(/[\w]+|[^\w\s]+|\s+/g);
|
|
71240
|
+
return matches ?? [text];
|
|
71241
|
+
}
|
|
71242
|
+
function myersDiff(oldArr, newArr, equals = (a, b) => a === b) {
|
|
71243
|
+
const n = oldArr.length;
|
|
71244
|
+
const m = newArr.length;
|
|
71245
|
+
if (n === 0 && m === 0)
|
|
71246
|
+
return [];
|
|
71247
|
+
if (n === 0)
|
|
71248
|
+
return newArr.map((newItem, i) => ({ kind: "insert", newItem, newIndex: i }));
|
|
71249
|
+
if (m === 0)
|
|
71250
|
+
return oldArr.map((oldItem, i) => ({ kind: "delete", oldItem, oldIndex: i }));
|
|
71251
|
+
const max = n + m;
|
|
71252
|
+
const v = { 1: 0 };
|
|
71253
|
+
const trace = [];
|
|
71254
|
+
for (let d = 0;d <= max; d++) {
|
|
71255
|
+
trace.push({ ...v });
|
|
71256
|
+
for (let k = -d;k <= d; k += 2) {
|
|
71257
|
+
let x = k === -d || k !== d && (v[k - 1] ?? -1) < (v[k + 1] ?? -1) ? v[k + 1] ?? 0 : (v[k - 1] ?? 0) + 1;
|
|
71258
|
+
let y = x - k;
|
|
71259
|
+
while (x < n && y < m && equals(oldArr[x], newArr[y])) {
|
|
71260
|
+
x++;
|
|
71261
|
+
y++;
|
|
71262
|
+
}
|
|
71263
|
+
v[k] = x;
|
|
71264
|
+
if (x >= n && y >= m) {
|
|
71265
|
+
const ops = [];
|
|
71266
|
+
let cx = n;
|
|
71267
|
+
let cy = m;
|
|
71268
|
+
for (let cd = d;cd > 0; cd--) {
|
|
71269
|
+
const prevV = trace[cd];
|
|
71270
|
+
const pk = cx - cy;
|
|
71271
|
+
const prevK = pk === -cd || pk !== cd && (prevV[pk - 1] ?? -1) < (prevV[pk + 1] ?? -1) ? pk + 1 : pk - 1;
|
|
71272
|
+
const prevX = prevV[prevK] ?? 0;
|
|
71273
|
+
const prevY = prevX - prevK;
|
|
71274
|
+
while (cx > prevX + (prevK === pk + 1 ? 0 : 1) && cy > prevY + (prevK === pk + 1 ? 1 : 0)) {
|
|
71275
|
+
cx--;
|
|
71276
|
+
cy--;
|
|
71277
|
+
ops.push({
|
|
71278
|
+
kind: "equal",
|
|
71279
|
+
oldItem: oldArr[cx],
|
|
71280
|
+
newItem: newArr[cy],
|
|
71281
|
+
oldIndex: cx,
|
|
71282
|
+
newIndex: cy
|
|
71283
|
+
});
|
|
71284
|
+
}
|
|
71285
|
+
if (prevK === pk + 1) {
|
|
71286
|
+
cy--;
|
|
71287
|
+
ops.push({ kind: "insert", newItem: newArr[cy], newIndex: cy });
|
|
71288
|
+
} else {
|
|
71289
|
+
cx--;
|
|
71290
|
+
ops.push({ kind: "delete", oldItem: oldArr[cx], oldIndex: cx });
|
|
71291
|
+
}
|
|
71292
|
+
}
|
|
71293
|
+
while (cx > 0 && cy > 0) {
|
|
71294
|
+
cx--;
|
|
71295
|
+
cy--;
|
|
71296
|
+
ops.push({
|
|
71297
|
+
kind: "equal",
|
|
71298
|
+
oldItem: oldArr[cx],
|
|
71299
|
+
newItem: newArr[cy],
|
|
71300
|
+
oldIndex: cx,
|
|
71301
|
+
newIndex: cy
|
|
71302
|
+
});
|
|
71303
|
+
}
|
|
71304
|
+
ops.reverse();
|
|
71305
|
+
return ops;
|
|
71306
|
+
}
|
|
71307
|
+
}
|
|
71308
|
+
}
|
|
71309
|
+
return [];
|
|
71310
|
+
}
|
|
71311
|
+
function computeWordDiff(oldLine, newLine) {
|
|
71312
|
+
const oldWords = tokenizeWords(oldLine);
|
|
71313
|
+
const newWords = tokenizeWords(newLine);
|
|
71314
|
+
const ops = myersDiff(oldWords, newWords);
|
|
71315
|
+
const oldSpans = [];
|
|
71316
|
+
const newSpans = [];
|
|
71317
|
+
for (const op of ops) {
|
|
71318
|
+
if (op.kind === "equal") {
|
|
71319
|
+
const text = op.oldItem ?? "";
|
|
71320
|
+
oldSpans.push({ text, kind: "equal" });
|
|
71321
|
+
newSpans.push({ text, kind: "equal" });
|
|
71322
|
+
} else if (op.kind === "delete") {
|
|
71323
|
+
oldSpans.push({ text: op.oldItem ?? "", kind: "deletion" });
|
|
71324
|
+
} else if (op.kind === "insert") {
|
|
71325
|
+
newSpans.push({ text: op.newItem ?? "", kind: "addition" });
|
|
71326
|
+
}
|
|
71327
|
+
}
|
|
71328
|
+
return { oldSpans, newSpans };
|
|
71329
|
+
}
|
|
71330
|
+
function buildUnifiedDiff(before, after, contextLines = 3) {
|
|
71331
|
+
const oldTokens = tokenizeLines(before);
|
|
71332
|
+
const newTokens = tokenizeLines(after);
|
|
71333
|
+
if (before === after) {
|
|
71334
|
+
return { hunks: [], oldLineCount: oldTokens.length, newLineCount: newTokens.length };
|
|
71335
|
+
}
|
|
71336
|
+
const ops = myersDiff(oldTokens, newTokens, (a, b) => a.text === b.text && a.ending === b.ending);
|
|
71337
|
+
const editIndices = ops.map((op, i) => op.kind !== "equal" ? i : -1).filter((i) => i !== -1);
|
|
71338
|
+
if (editIndices.length === 0) {
|
|
71339
|
+
return { hunks: [], oldLineCount: oldTokens.length, newLineCount: newTokens.length };
|
|
71340
|
+
}
|
|
71341
|
+
const hunkRanges = [];
|
|
71342
|
+
let currentRange = {
|
|
71343
|
+
startIdx: Math.max(0, editIndices[0] - contextLines),
|
|
71344
|
+
endIdx: Math.min(ops.length - 1, editIndices[0] + contextLines)
|
|
71345
|
+
};
|
|
71346
|
+
for (let i = 1;i < editIndices.length; i++) {
|
|
71347
|
+
const editIdx = editIndices[i];
|
|
71348
|
+
const opStart = Math.max(0, editIdx - contextLines);
|
|
71349
|
+
const opEnd = Math.min(ops.length - 1, editIdx + contextLines);
|
|
71350
|
+
if (opStart <= currentRange.endIdx) {
|
|
71351
|
+
currentRange.endIdx = Math.max(currentRange.endIdx, opEnd);
|
|
71352
|
+
} else {
|
|
71353
|
+
hunkRanges.push(currentRange);
|
|
71354
|
+
currentRange = { startIdx: opStart, endIdx: opEnd };
|
|
71355
|
+
}
|
|
71356
|
+
}
|
|
71357
|
+
hunkRanges.push(currentRange);
|
|
71358
|
+
const hunks = [];
|
|
71359
|
+
for (const range of hunkRanges) {
|
|
71360
|
+
const lines = [];
|
|
71361
|
+
let oldCount = 0;
|
|
71362
|
+
let newCount = 0;
|
|
71363
|
+
let oldStart = null;
|
|
71364
|
+
let newStart = null;
|
|
71365
|
+
for (let i = range.startIdx;i <= range.endIdx; i++) {
|
|
71366
|
+
const op = ops[i];
|
|
71367
|
+
if (op.kind === "equal") {
|
|
71368
|
+
const oIdx = op.oldIndex + 1;
|
|
71369
|
+
const nIdx = op.newIndex + 1;
|
|
71370
|
+
if (oldStart === null)
|
|
71371
|
+
oldStart = oIdx;
|
|
71372
|
+
if (newStart === null)
|
|
71373
|
+
newStart = nIdx;
|
|
71374
|
+
lines.push({
|
|
71375
|
+
kind: "context",
|
|
71376
|
+
text: op.oldItem.text,
|
|
71377
|
+
ending: op.oldItem.ending,
|
|
71378
|
+
oldLineNumber: oIdx,
|
|
71379
|
+
newLineNumber: nIdx
|
|
71380
|
+
});
|
|
71381
|
+
oldCount++;
|
|
71382
|
+
newCount++;
|
|
71383
|
+
} else if (op.kind === "delete") {
|
|
71384
|
+
const oIdx = op.oldIndex + 1;
|
|
71385
|
+
if (oldStart === null)
|
|
71386
|
+
oldStart = oIdx;
|
|
71387
|
+
lines.push({
|
|
71388
|
+
kind: "deletion",
|
|
71389
|
+
text: op.oldItem.text,
|
|
71390
|
+
ending: op.oldItem.ending,
|
|
71391
|
+
oldLineNumber: oIdx
|
|
71392
|
+
});
|
|
71393
|
+
oldCount++;
|
|
71394
|
+
} else if (op.kind === "insert") {
|
|
71395
|
+
const nIdx = op.newIndex + 1;
|
|
71396
|
+
if (newStart === null)
|
|
71397
|
+
newStart = nIdx;
|
|
71398
|
+
lines.push({
|
|
71399
|
+
kind: "addition",
|
|
71400
|
+
text: op.newItem.text,
|
|
71401
|
+
ending: op.newItem.ending,
|
|
71402
|
+
newLineNumber: nIdx
|
|
71403
|
+
});
|
|
71404
|
+
newCount++;
|
|
71405
|
+
}
|
|
71406
|
+
}
|
|
71407
|
+
let l = 0;
|
|
71408
|
+
while (l < lines.length) {
|
|
71409
|
+
if (lines[l]?.kind === "deletion") {
|
|
71410
|
+
const delStart = l;
|
|
71411
|
+
while (l < lines.length && lines[l]?.kind === "deletion")
|
|
71412
|
+
l++;
|
|
71413
|
+
const delEnd = l;
|
|
71414
|
+
const addStart = l;
|
|
71415
|
+
while (l < lines.length && lines[l]?.kind === "addition")
|
|
71416
|
+
l++;
|
|
71417
|
+
const addEnd = l;
|
|
71418
|
+
const delCount = delEnd - delStart;
|
|
71419
|
+
const addCount = addEnd - addStart;
|
|
71420
|
+
const pairCount = Math.min(delCount, addCount);
|
|
71421
|
+
for (let p = 0;p < pairCount; p++) {
|
|
71422
|
+
const delLine = lines[delStart + p];
|
|
71423
|
+
const addLine = lines[addStart + p];
|
|
71424
|
+
const { oldSpans, newSpans } = computeWordDiff(delLine.text, addLine.text);
|
|
71425
|
+
delLine.spans = oldSpans;
|
|
71426
|
+
addLine.spans = newSpans;
|
|
71427
|
+
}
|
|
71428
|
+
} else {
|
|
71429
|
+
l++;
|
|
71430
|
+
}
|
|
71431
|
+
}
|
|
71432
|
+
hunks.push({
|
|
71433
|
+
oldStart: oldStart ?? (oldTokens.length === 0 ? 0 : 1),
|
|
71434
|
+
oldCount,
|
|
71435
|
+
newStart: newStart ?? (newTokens.length === 0 ? 0 : 1),
|
|
71436
|
+
newCount,
|
|
71437
|
+
lines
|
|
71438
|
+
});
|
|
71439
|
+
}
|
|
71440
|
+
return {
|
|
71441
|
+
hunks,
|
|
71442
|
+
oldLineCount: oldTokens.length,
|
|
71443
|
+
newLineCount: newTokens.length
|
|
71444
|
+
};
|
|
71445
|
+
}
|
|
71446
|
+
|
|
71447
|
+
// src/tools/edit-file/index.ts
|
|
71236
71448
|
var editFileInputSchema = object({
|
|
71237
71449
|
file_path: string2().describe("The path of the file to edit (relative to current working directory or absolute inside trusted workspace)."),
|
|
71238
71450
|
old_string: string2().describe("The exact string to find and replace in the file."),
|
|
@@ -71260,27 +71472,54 @@ var editFileTool = {
|
|
|
71260
71472
|
const added = result?.addedLines ?? 0;
|
|
71261
71473
|
const removed = result?.removedLines ?? 0;
|
|
71262
71474
|
const summary = `Added ${added} line${added === 1 ? "" : "s"}, removed ${removed} line${removed === 1 ? "" : "s"}`;
|
|
71263
|
-
const oldLines = args.old_string.split(/\r?\n/);
|
|
71264
|
-
const newLines = args.new_string.split(/\r?\n/);
|
|
71265
71475
|
if (added === 0 && removed === 0)
|
|
71266
71476
|
return summary;
|
|
71267
|
-
const
|
|
71268
|
-
const padWidth = String(Math.max(oldLines.length, newLines.length)).length;
|
|
71477
|
+
const diff = buildUnifiedDiff(args.old_string, args.new_string, 2);
|
|
71269
71478
|
const theme = getTheme();
|
|
71270
71479
|
const deleteStyle = (str) => themeBgColor(theme.diffDeleteBG)(themeColor2(theme.diffDeleteFG)(str));
|
|
71271
71480
|
const addStyle = (str) => themeBgColor(theme.diffAddBG)(themeColor2(theme.diffAddFG)(str));
|
|
71272
|
-
const
|
|
71273
|
-
|
|
71274
|
-
|
|
71275
|
-
|
|
71276
|
-
|
|
71277
|
-
|
|
71278
|
-
|
|
71279
|
-
|
|
71280
|
-
|
|
71281
|
-
|
|
71282
|
-
|
|
71283
|
-
|
|
71481
|
+
const contextStyle = (str) => source_default.dim(str);
|
|
71482
|
+
let maxLineNum = 1;
|
|
71483
|
+
for (const hunk of diff.hunks) {
|
|
71484
|
+
for (const line of hunk.lines) {
|
|
71485
|
+
if (line.oldLineNumber)
|
|
71486
|
+
maxLineNum = Math.max(maxLineNum, line.oldLineNumber);
|
|
71487
|
+
if (line.newLineNumber)
|
|
71488
|
+
maxLineNum = Math.max(maxLineNum, line.newLineNumber);
|
|
71489
|
+
}
|
|
71490
|
+
}
|
|
71491
|
+
const padWidth = Math.max(1, String(maxLineNum).length);
|
|
71492
|
+
const diffLines = [];
|
|
71493
|
+
const cap = 30;
|
|
71494
|
+
let count = 0;
|
|
71495
|
+
let overflow = 0;
|
|
71496
|
+
for (const hunk of diff.hunks) {
|
|
71497
|
+
for (const line of hunk.lines) {
|
|
71498
|
+
if (count >= cap) {
|
|
71499
|
+
overflow++;
|
|
71500
|
+
continue;
|
|
71501
|
+
}
|
|
71502
|
+
count++;
|
|
71503
|
+
if (line.kind === "deletion") {
|
|
71504
|
+
const numStr = String(line.oldLineNumber ?? "").padStart(padWidth, " ");
|
|
71505
|
+
const body = line.spans ? "-" + line.spans.map((s) => s.kind === "deletion" ? source_default.bold(s.text) : s.text).join("") : `-${line.text}`;
|
|
71506
|
+
diffLines.push(deleteStyle(`${numStr} ${body}`));
|
|
71507
|
+
} else if (line.kind === "addition") {
|
|
71508
|
+
const numStr = String(line.newLineNumber ?? "").padStart(padWidth, " ");
|
|
71509
|
+
const body = line.spans ? "+" + line.spans.map((s) => s.kind === "addition" ? source_default.bold(s.text) : s.text).join("") : `+${line.text}`;
|
|
71510
|
+
diffLines.push(addStyle(`${numStr} ${body}`));
|
|
71511
|
+
} else {
|
|
71512
|
+
const numStr = String(line.newLineNumber ?? line.oldLineNumber ?? "").padStart(padWidth, " ");
|
|
71513
|
+
diffLines.push(contextStyle(`${numStr} ${line.text}`));
|
|
71514
|
+
}
|
|
71515
|
+
}
|
|
71516
|
+
}
|
|
71517
|
+
if (overflow > 0) {
|
|
71518
|
+
diffLines.push(source_default.dim(` … (${overflow} more lines)`));
|
|
71519
|
+
}
|
|
71520
|
+
return diffLines.length > 0 ? `${summary}
|
|
71521
|
+
${diffLines.join(`
|
|
71522
|
+
`)}` : summary;
|
|
71284
71523
|
},
|
|
71285
71524
|
execute: async (args, context) => {
|
|
71286
71525
|
const { absolutePath: targetPath, relativePath } = resolveDirectMutationPath(context.cwd, args.file_path);
|
|
@@ -72440,7 +72679,7 @@ var taskKillTool = {
|
|
|
72440
72679
|
// package.json
|
|
72441
72680
|
var package_default = {
|
|
72442
72681
|
name: "steward-cli",
|
|
72443
|
-
version: "0.
|
|
72682
|
+
version: "0.12.0",
|
|
72444
72683
|
description: "Interactive AI engineering assistant for the terminal with human-in-the-loop control across local and frontier models.",
|
|
72445
72684
|
author: "sapirrior",
|
|
72446
72685
|
license: "MIT",
|
|
@@ -118861,6 +119100,9 @@ class StatusBar extends Component {
|
|
|
118861
119100
|
});
|
|
118862
119101
|
}
|
|
118863
119102
|
}
|
|
119103
|
+
setUpdateStatus(updateStatus) {
|
|
119104
|
+
this.setState({ updateStatus });
|
|
119105
|
+
}
|
|
118864
119106
|
showWarning(warningText, durationMs = 4000) {
|
|
118865
119107
|
if (this.warningTimer) {
|
|
118866
119108
|
clearTimeout(this.warningTimer);
|
|
@@ -118888,7 +119130,16 @@ class StatusBar extends Component {
|
|
|
118888
119130
|
const theme = getTheme();
|
|
118889
119131
|
const termWidth = width ?? process.stdout.columns ?? 80;
|
|
118890
119132
|
const maxCols = Math.max(1, termWidth);
|
|
118891
|
-
const {
|
|
119133
|
+
const {
|
|
119134
|
+
model,
|
|
119135
|
+
usage,
|
|
119136
|
+
isBusy,
|
|
119137
|
+
exitPending,
|
|
119138
|
+
warning,
|
|
119139
|
+
voiceState,
|
|
119140
|
+
voiceDurationSec,
|
|
119141
|
+
updateStatus
|
|
119142
|
+
} = this.state;
|
|
118892
119143
|
let left = "";
|
|
118893
119144
|
if (warning) {
|
|
118894
119145
|
left = themeColor2(theme.warning)(warning);
|
|
@@ -118904,6 +119155,24 @@ class StatusBar extends Component {
|
|
|
118904
119155
|
} else if (exitPending) {
|
|
118905
119156
|
const errColor = themeColor2(theme.error);
|
|
118906
119157
|
left = `${errColor("▸ ")}${source_default.dim("Press ")}${errColor("Ctrl+C")}${source_default.dim(" again to exit")}`;
|
|
119158
|
+
} else if (updateStatus && updateStatus.state !== "idle") {
|
|
119159
|
+
if (updateStatus.state === "checking") {
|
|
119160
|
+
left = source_default.white(updateStatus.message ?? "Checking for updates...");
|
|
119161
|
+
} else if (updateStatus.state === "no-updates") {
|
|
119162
|
+
left = source_default.white(updateStatus.message ?? "No updates found");
|
|
119163
|
+
} else if (updateStatus.state === "available") {
|
|
119164
|
+
left = source_default.white(updateStatus.message ?? `Found version v${updateStatus.version ?? ""}`);
|
|
119165
|
+
} else if (updateStatus.state === "downloading") {
|
|
119166
|
+
left = source_default.white(updateStatus.message ?? `Downloading v${updateStatus.version ?? ""}...`);
|
|
119167
|
+
} else if (updateStatus.state === "ready") {
|
|
119168
|
+
left = source_default.white.bold(updateStatus.message ?? "Update complete · Restart to apply");
|
|
119169
|
+
} else if (updateStatus.state === "error" && updateStatus.message) {
|
|
119170
|
+
left = themeColor2(theme.error)(updateStatus.message);
|
|
119171
|
+
} else if (isBusy) {
|
|
119172
|
+
left = source_default.dim("esc to interrupt");
|
|
119173
|
+
} else {
|
|
119174
|
+
left = source_default.dim("? for shortcuts");
|
|
119175
|
+
}
|
|
118907
119176
|
} else if (isBusy) {
|
|
118908
119177
|
left = source_default.dim("esc to interrupt");
|
|
118909
119178
|
} else {
|
|
@@ -120545,235 +120814,6 @@ class BashPermissionDock extends Component {
|
|
|
120545
120814
|
}
|
|
120546
120815
|
}
|
|
120547
120816
|
|
|
120548
|
-
// src/utils/diff.ts
|
|
120549
|
-
function tokenizeLines(content) {
|
|
120550
|
-
if (!content)
|
|
120551
|
-
return [];
|
|
120552
|
-
const tokens = [];
|
|
120553
|
-
const regex = /([^\r\n]*)(\r?\n)?/g;
|
|
120554
|
-
let match;
|
|
120555
|
-
while ((match = regex.exec(content)) !== null) {
|
|
120556
|
-
if (match.index === content.length)
|
|
120557
|
-
break;
|
|
120558
|
-
const text = match[1] ?? "";
|
|
120559
|
-
const ending = match[2] ?? "";
|
|
120560
|
-
tokens.push({ text, ending });
|
|
120561
|
-
if (!ending && match.index + text.length === content.length)
|
|
120562
|
-
break;
|
|
120563
|
-
}
|
|
120564
|
-
return tokens;
|
|
120565
|
-
}
|
|
120566
|
-
function tokenizeWords(text) {
|
|
120567
|
-
if (!text)
|
|
120568
|
-
return [];
|
|
120569
|
-
const matches = text.match(/[\w]+|[^\w\s]+|\s+/g);
|
|
120570
|
-
return matches ?? [text];
|
|
120571
|
-
}
|
|
120572
|
-
function myersDiff(oldArr, newArr, equals = (a, b) => a === b) {
|
|
120573
|
-
const n = oldArr.length;
|
|
120574
|
-
const m = newArr.length;
|
|
120575
|
-
if (n === 0 && m === 0)
|
|
120576
|
-
return [];
|
|
120577
|
-
if (n === 0)
|
|
120578
|
-
return newArr.map((newItem, i) => ({ kind: "insert", newItem, newIndex: i }));
|
|
120579
|
-
if (m === 0)
|
|
120580
|
-
return oldArr.map((oldItem, i) => ({ kind: "delete", oldItem, oldIndex: i }));
|
|
120581
|
-
const max = n + m;
|
|
120582
|
-
const v = { 1: 0 };
|
|
120583
|
-
const trace = [];
|
|
120584
|
-
for (let d = 0;d <= max; d++) {
|
|
120585
|
-
trace.push({ ...v });
|
|
120586
|
-
for (let k = -d;k <= d; k += 2) {
|
|
120587
|
-
let x = k === -d || k !== d && (v[k - 1] ?? -1) < (v[k + 1] ?? -1) ? v[k + 1] ?? 0 : (v[k - 1] ?? 0) + 1;
|
|
120588
|
-
let y = x - k;
|
|
120589
|
-
while (x < n && y < m && equals(oldArr[x], newArr[y])) {
|
|
120590
|
-
x++;
|
|
120591
|
-
y++;
|
|
120592
|
-
}
|
|
120593
|
-
v[k] = x;
|
|
120594
|
-
if (x >= n && y >= m) {
|
|
120595
|
-
const ops = [];
|
|
120596
|
-
let cx = n;
|
|
120597
|
-
let cy = m;
|
|
120598
|
-
for (let cd = d;cd > 0; cd--) {
|
|
120599
|
-
const prevV = trace[cd];
|
|
120600
|
-
const pk = cx - cy;
|
|
120601
|
-
const prevK = pk === -cd || pk !== cd && (prevV[pk - 1] ?? -1) < (prevV[pk + 1] ?? -1) ? pk + 1 : pk - 1;
|
|
120602
|
-
const prevX = prevV[prevK] ?? 0;
|
|
120603
|
-
const prevY = prevX - prevK;
|
|
120604
|
-
while (cx > prevX + (prevK === pk + 1 ? 0 : 1) && cy > prevY + (prevK === pk + 1 ? 1 : 0)) {
|
|
120605
|
-
cx--;
|
|
120606
|
-
cy--;
|
|
120607
|
-
ops.push({
|
|
120608
|
-
kind: "equal",
|
|
120609
|
-
oldItem: oldArr[cx],
|
|
120610
|
-
newItem: newArr[cy],
|
|
120611
|
-
oldIndex: cx,
|
|
120612
|
-
newIndex: cy
|
|
120613
|
-
});
|
|
120614
|
-
}
|
|
120615
|
-
if (prevK === pk + 1) {
|
|
120616
|
-
cy--;
|
|
120617
|
-
ops.push({ kind: "insert", newItem: newArr[cy], newIndex: cy });
|
|
120618
|
-
} else {
|
|
120619
|
-
cx--;
|
|
120620
|
-
ops.push({ kind: "delete", oldItem: oldArr[cx], oldIndex: cx });
|
|
120621
|
-
}
|
|
120622
|
-
}
|
|
120623
|
-
while (cx > 0 && cy > 0) {
|
|
120624
|
-
cx--;
|
|
120625
|
-
cy--;
|
|
120626
|
-
ops.push({
|
|
120627
|
-
kind: "equal",
|
|
120628
|
-
oldItem: oldArr[cx],
|
|
120629
|
-
newItem: newArr[cy],
|
|
120630
|
-
oldIndex: cx,
|
|
120631
|
-
newIndex: cy
|
|
120632
|
-
});
|
|
120633
|
-
}
|
|
120634
|
-
ops.reverse();
|
|
120635
|
-
return ops;
|
|
120636
|
-
}
|
|
120637
|
-
}
|
|
120638
|
-
}
|
|
120639
|
-
return [];
|
|
120640
|
-
}
|
|
120641
|
-
function computeWordDiff(oldLine, newLine) {
|
|
120642
|
-
const oldWords = tokenizeWords(oldLine);
|
|
120643
|
-
const newWords = tokenizeWords(newLine);
|
|
120644
|
-
const ops = myersDiff(oldWords, newWords);
|
|
120645
|
-
const oldSpans = [];
|
|
120646
|
-
const newSpans = [];
|
|
120647
|
-
for (const op of ops) {
|
|
120648
|
-
if (op.kind === "equal") {
|
|
120649
|
-
const text = op.oldItem ?? "";
|
|
120650
|
-
oldSpans.push({ text, kind: "equal" });
|
|
120651
|
-
newSpans.push({ text, kind: "equal" });
|
|
120652
|
-
} else if (op.kind === "delete") {
|
|
120653
|
-
oldSpans.push({ text: op.oldItem ?? "", kind: "deletion" });
|
|
120654
|
-
} else if (op.kind === "insert") {
|
|
120655
|
-
newSpans.push({ text: op.newItem ?? "", kind: "addition" });
|
|
120656
|
-
}
|
|
120657
|
-
}
|
|
120658
|
-
return { oldSpans, newSpans };
|
|
120659
|
-
}
|
|
120660
|
-
function buildUnifiedDiff(before, after, contextLines = 3) {
|
|
120661
|
-
const oldTokens = tokenizeLines(before);
|
|
120662
|
-
const newTokens = tokenizeLines(after);
|
|
120663
|
-
if (before === after) {
|
|
120664
|
-
return { hunks: [], oldLineCount: oldTokens.length, newLineCount: newTokens.length };
|
|
120665
|
-
}
|
|
120666
|
-
const ops = myersDiff(oldTokens, newTokens, (a, b) => a.text === b.text && a.ending === b.ending);
|
|
120667
|
-
const editIndices = ops.map((op, i) => op.kind !== "equal" ? i : -1).filter((i) => i !== -1);
|
|
120668
|
-
if (editIndices.length === 0) {
|
|
120669
|
-
return { hunks: [], oldLineCount: oldTokens.length, newLineCount: newTokens.length };
|
|
120670
|
-
}
|
|
120671
|
-
const hunkRanges = [];
|
|
120672
|
-
let currentRange = {
|
|
120673
|
-
startIdx: Math.max(0, editIndices[0] - contextLines),
|
|
120674
|
-
endIdx: Math.min(ops.length - 1, editIndices[0] + contextLines)
|
|
120675
|
-
};
|
|
120676
|
-
for (let i = 1;i < editIndices.length; i++) {
|
|
120677
|
-
const editIdx = editIndices[i];
|
|
120678
|
-
const opStart = Math.max(0, editIdx - contextLines);
|
|
120679
|
-
const opEnd = Math.min(ops.length - 1, editIdx + contextLines);
|
|
120680
|
-
if (opStart <= currentRange.endIdx) {
|
|
120681
|
-
currentRange.endIdx = Math.max(currentRange.endIdx, opEnd);
|
|
120682
|
-
} else {
|
|
120683
|
-
hunkRanges.push(currentRange);
|
|
120684
|
-
currentRange = { startIdx: opStart, endIdx: opEnd };
|
|
120685
|
-
}
|
|
120686
|
-
}
|
|
120687
|
-
hunkRanges.push(currentRange);
|
|
120688
|
-
const hunks = [];
|
|
120689
|
-
for (const range of hunkRanges) {
|
|
120690
|
-
const lines = [];
|
|
120691
|
-
let oldCount = 0;
|
|
120692
|
-
let newCount = 0;
|
|
120693
|
-
let oldStart = null;
|
|
120694
|
-
let newStart = null;
|
|
120695
|
-
for (let i = range.startIdx;i <= range.endIdx; i++) {
|
|
120696
|
-
const op = ops[i];
|
|
120697
|
-
if (op.kind === "equal") {
|
|
120698
|
-
const oIdx = op.oldIndex + 1;
|
|
120699
|
-
const nIdx = op.newIndex + 1;
|
|
120700
|
-
if (oldStart === null)
|
|
120701
|
-
oldStart = oIdx;
|
|
120702
|
-
if (newStart === null)
|
|
120703
|
-
newStart = nIdx;
|
|
120704
|
-
lines.push({
|
|
120705
|
-
kind: "context",
|
|
120706
|
-
text: op.oldItem.text,
|
|
120707
|
-
ending: op.oldItem.ending,
|
|
120708
|
-
oldLineNumber: oIdx,
|
|
120709
|
-
newLineNumber: nIdx
|
|
120710
|
-
});
|
|
120711
|
-
oldCount++;
|
|
120712
|
-
newCount++;
|
|
120713
|
-
} else if (op.kind === "delete") {
|
|
120714
|
-
const oIdx = op.oldIndex + 1;
|
|
120715
|
-
if (oldStart === null)
|
|
120716
|
-
oldStart = oIdx;
|
|
120717
|
-
lines.push({
|
|
120718
|
-
kind: "deletion",
|
|
120719
|
-
text: op.oldItem.text,
|
|
120720
|
-
ending: op.oldItem.ending,
|
|
120721
|
-
oldLineNumber: oIdx
|
|
120722
|
-
});
|
|
120723
|
-
oldCount++;
|
|
120724
|
-
} else if (op.kind === "insert") {
|
|
120725
|
-
const nIdx = op.newIndex + 1;
|
|
120726
|
-
if (newStart === null)
|
|
120727
|
-
newStart = nIdx;
|
|
120728
|
-
lines.push({
|
|
120729
|
-
kind: "addition",
|
|
120730
|
-
text: op.newItem.text,
|
|
120731
|
-
ending: op.newItem.ending,
|
|
120732
|
-
newLineNumber: nIdx
|
|
120733
|
-
});
|
|
120734
|
-
newCount++;
|
|
120735
|
-
}
|
|
120736
|
-
}
|
|
120737
|
-
let l = 0;
|
|
120738
|
-
while (l < lines.length) {
|
|
120739
|
-
if (lines[l]?.kind === "deletion") {
|
|
120740
|
-
const delStart = l;
|
|
120741
|
-
while (l < lines.length && lines[l]?.kind === "deletion")
|
|
120742
|
-
l++;
|
|
120743
|
-
const delEnd = l;
|
|
120744
|
-
const addStart = l;
|
|
120745
|
-
while (l < lines.length && lines[l]?.kind === "addition")
|
|
120746
|
-
l++;
|
|
120747
|
-
const addEnd = l;
|
|
120748
|
-
const delCount = delEnd - delStart;
|
|
120749
|
-
const addCount = addEnd - addStart;
|
|
120750
|
-
const pairCount = Math.min(delCount, addCount);
|
|
120751
|
-
for (let p = 0;p < pairCount; p++) {
|
|
120752
|
-
const delLine = lines[delStart + p];
|
|
120753
|
-
const addLine = lines[addStart + p];
|
|
120754
|
-
const { oldSpans, newSpans } = computeWordDiff(delLine.text, addLine.text);
|
|
120755
|
-
delLine.spans = oldSpans;
|
|
120756
|
-
addLine.spans = newSpans;
|
|
120757
|
-
}
|
|
120758
|
-
} else {
|
|
120759
|
-
l++;
|
|
120760
|
-
}
|
|
120761
|
-
}
|
|
120762
|
-
hunks.push({
|
|
120763
|
-
oldStart: oldStart ?? (oldTokens.length === 0 ? 0 : 1),
|
|
120764
|
-
oldCount,
|
|
120765
|
-
newStart: newStart ?? (newTokens.length === 0 ? 0 : 1),
|
|
120766
|
-
newCount,
|
|
120767
|
-
lines
|
|
120768
|
-
});
|
|
120769
|
-
}
|
|
120770
|
-
return {
|
|
120771
|
-
hunks,
|
|
120772
|
-
oldLineCount: oldTokens.length,
|
|
120773
|
-
newLineCount: newTokens.length
|
|
120774
|
-
};
|
|
120775
|
-
}
|
|
120776
|
-
|
|
120777
120817
|
// src/tui/components/docks/FilePermissionDock.tsx
|
|
120778
120818
|
class FilePermissionDock extends Component {
|
|
120779
120819
|
wrap = true;
|
|
@@ -120903,8 +120943,7 @@ class FilePermissionDock extends Component {
|
|
|
120903
120943
|
const wrapped = prefixedBlock(firstPrefix, bodyText, {
|
|
120904
120944
|
continuationPrefix: contPrefix,
|
|
120905
120945
|
width: maxCols,
|
|
120906
|
-
bg: diffDeleteBg
|
|
120907
|
-
padToWidth: true
|
|
120946
|
+
bg: diffDeleteBg
|
|
120908
120947
|
});
|
|
120909
120948
|
renderedRows.push(...wrapped);
|
|
120910
120949
|
} else if (line.kind === "addition") {
|
|
@@ -120915,8 +120954,7 @@ class FilePermissionDock extends Component {
|
|
|
120915
120954
|
const wrapped = prefixedBlock(firstPrefix, bodyText, {
|
|
120916
120955
|
continuationPrefix: contPrefix,
|
|
120917
120956
|
width: maxCols,
|
|
120918
|
-
bg: diffAddBg
|
|
120919
|
-
padToWidth: true
|
|
120957
|
+
bg: diffAddBg
|
|
120920
120958
|
});
|
|
120921
120959
|
renderedRows.push(...wrapped);
|
|
120922
120960
|
} else {
|
|
@@ -122049,6 +122087,230 @@ class VoiceController {
|
|
|
122049
122087
|
this.setState("idle");
|
|
122050
122088
|
}
|
|
122051
122089
|
}
|
|
122090
|
+
// src/services/updater/service.ts
|
|
122091
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
122092
|
+
var DEFAULT_REPO = "sapirrior/steward";
|
|
122093
|
+
function parseSemver(v) {
|
|
122094
|
+
const clean = v.replace(/^v/i, "").trim().split("-")[0] ?? "";
|
|
122095
|
+
const parts = clean.split(".").map((p) => parseInt(p, 10) || 0);
|
|
122096
|
+
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
122097
|
+
}
|
|
122098
|
+
function isNewerVersion(current, latest) {
|
|
122099
|
+
const [cMaj, cMin, cPat] = parseSemver(current);
|
|
122100
|
+
const [lMaj, lMin, lPat] = parseSemver(latest);
|
|
122101
|
+
if (lMaj > cMaj)
|
|
122102
|
+
return true;
|
|
122103
|
+
if (lMaj < cMaj)
|
|
122104
|
+
return false;
|
|
122105
|
+
if (lMin > cMin)
|
|
122106
|
+
return true;
|
|
122107
|
+
if (lMin < cMin)
|
|
122108
|
+
return false;
|
|
122109
|
+
return lPat > cPat;
|
|
122110
|
+
}
|
|
122111
|
+
|
|
122112
|
+
class AutoUpdaterService {
|
|
122113
|
+
currentVersion;
|
|
122114
|
+
repo;
|
|
122115
|
+
state = "idle";
|
|
122116
|
+
options;
|
|
122117
|
+
checkTimer = null;
|
|
122118
|
+
intervalTimer = null;
|
|
122119
|
+
isUpdating = false;
|
|
122120
|
+
constructor(options = {}) {
|
|
122121
|
+
this.options = options;
|
|
122122
|
+
this.currentVersion = options.currentVersion || package_default.version || "0.0.0";
|
|
122123
|
+
this.repo = options.repo || DEFAULT_REPO;
|
|
122124
|
+
}
|
|
122125
|
+
getState() {
|
|
122126
|
+
return this.state;
|
|
122127
|
+
}
|
|
122128
|
+
getRepo() {
|
|
122129
|
+
return this.repo;
|
|
122130
|
+
}
|
|
122131
|
+
getCurrentVersion() {
|
|
122132
|
+
return this.currentVersion;
|
|
122133
|
+
}
|
|
122134
|
+
get latestReleaseApi() {
|
|
122135
|
+
return `https://api.github.com/repos/${this.repo}/releases/latest`;
|
|
122136
|
+
}
|
|
122137
|
+
get installShUrl() {
|
|
122138
|
+
return `https://raw.githubusercontent.com/${this.repo}/main/installer/install.sh`;
|
|
122139
|
+
}
|
|
122140
|
+
get installPs1Url() {
|
|
122141
|
+
return `https://raw.githubusercontent.com/${this.repo}/main/installer/install.ps1`;
|
|
122142
|
+
}
|
|
122143
|
+
setState(state, info) {
|
|
122144
|
+
this.state = state;
|
|
122145
|
+
this.options.onStatusChange?.(state, info);
|
|
122146
|
+
}
|
|
122147
|
+
async checkForUpdates() {
|
|
122148
|
+
if (process.env.STEWARD_NO_UPDATE_CHECK === "1" || process.env.STEWARD_NO_UPDATE_CHECK === "true") {
|
|
122149
|
+
return null;
|
|
122150
|
+
}
|
|
122151
|
+
try {
|
|
122152
|
+
this.setState("checking", { message: "Checking for updates..." });
|
|
122153
|
+
const controller = new AbortController;
|
|
122154
|
+
const timeout = setTimeout(() => controller.abort(), this.options.timeoutMs ?? 1e4);
|
|
122155
|
+
const response = await fetch(this.latestReleaseApi, {
|
|
122156
|
+
headers: {
|
|
122157
|
+
"User-Agent": `steward-cli/${this.currentVersion}`,
|
|
122158
|
+
Accept: "application/vnd.github.v3+json"
|
|
122159
|
+
},
|
|
122160
|
+
signal: controller.signal
|
|
122161
|
+
});
|
|
122162
|
+
clearTimeout(timeout);
|
|
122163
|
+
if (!response.ok) {
|
|
122164
|
+
this.setState("idle");
|
|
122165
|
+
return null;
|
|
122166
|
+
}
|
|
122167
|
+
const data = await response.json();
|
|
122168
|
+
const rawTag = data.tag_name ?? "";
|
|
122169
|
+
const latestVersion = rawTag.replace(/^v/i, "").trim();
|
|
122170
|
+
if (!latestVersion) {
|
|
122171
|
+
this.setState("idle");
|
|
122172
|
+
return null;
|
|
122173
|
+
}
|
|
122174
|
+
const hasUpdate = isNewerVersion(this.currentVersion, latestVersion);
|
|
122175
|
+
const updateInfo = {
|
|
122176
|
+
currentVersion: this.currentVersion,
|
|
122177
|
+
latestVersion,
|
|
122178
|
+
hasUpdate,
|
|
122179
|
+
tag: rawTag,
|
|
122180
|
+
releaseUrl: data.html_url
|
|
122181
|
+
};
|
|
122182
|
+
if (hasUpdate) {
|
|
122183
|
+
this.setState("available", {
|
|
122184
|
+
version: latestVersion,
|
|
122185
|
+
message: `Found version v${latestVersion}`
|
|
122186
|
+
});
|
|
122187
|
+
} else {
|
|
122188
|
+
this.setState("no-updates", { message: "No updates found" });
|
|
122189
|
+
setTimeout(() => {
|
|
122190
|
+
if (this.state === "no-updates") {
|
|
122191
|
+
this.setState("idle");
|
|
122192
|
+
}
|
|
122193
|
+
}, 3000);
|
|
122194
|
+
}
|
|
122195
|
+
return updateInfo;
|
|
122196
|
+
} catch (err) {
|
|
122197
|
+
logError(err, { component: "AutoUpdaterService", method: "checkForUpdates" });
|
|
122198
|
+
this.setState("idle");
|
|
122199
|
+
return null;
|
|
122200
|
+
}
|
|
122201
|
+
}
|
|
122202
|
+
async installUpdate(version) {
|
|
122203
|
+
if (this.isUpdating)
|
|
122204
|
+
return false;
|
|
122205
|
+
this.isUpdating = true;
|
|
122206
|
+
this.setState("downloading", {
|
|
122207
|
+
version,
|
|
122208
|
+
message: `Downloading v${version}...`
|
|
122209
|
+
});
|
|
122210
|
+
try {
|
|
122211
|
+
const isWindows = process.platform === "win32";
|
|
122212
|
+
let childProcess;
|
|
122213
|
+
if (isWindows) {
|
|
122214
|
+
childProcess = spawn2("powershell", [
|
|
122215
|
+
"-NoProfile",
|
|
122216
|
+
"-ExecutionPolicy",
|
|
122217
|
+
"Bypass",
|
|
122218
|
+
"-Command",
|
|
122219
|
+
`irm ${this.installPs1Url} | iex`
|
|
122220
|
+
], {
|
|
122221
|
+
stdio: "ignore",
|
|
122222
|
+
windowsHide: true
|
|
122223
|
+
});
|
|
122224
|
+
} else {
|
|
122225
|
+
childProcess = spawn2("bash", ["-c", `curl -fsSL ${this.installShUrl} | bash`], {
|
|
122226
|
+
stdio: "ignore",
|
|
122227
|
+
env: {
|
|
122228
|
+
...process.env,
|
|
122229
|
+
HOME: process.env.HOME || "",
|
|
122230
|
+
PATH: process.env.PATH || ""
|
|
122231
|
+
}
|
|
122232
|
+
});
|
|
122233
|
+
}
|
|
122234
|
+
const success = await new Promise((resolve) => {
|
|
122235
|
+
const timeout = setTimeout(() => {
|
|
122236
|
+
try {
|
|
122237
|
+
childProcess.kill();
|
|
122238
|
+
} catch {}
|
|
122239
|
+
resolve(false);
|
|
122240
|
+
}, 300000);
|
|
122241
|
+
childProcess.on("exit", (code) => {
|
|
122242
|
+
clearTimeout(timeout);
|
|
122243
|
+
resolve(code === 0);
|
|
122244
|
+
});
|
|
122245
|
+
childProcess.on("error", () => {
|
|
122246
|
+
clearTimeout(timeout);
|
|
122247
|
+
resolve(false);
|
|
122248
|
+
});
|
|
122249
|
+
});
|
|
122250
|
+
if (success) {
|
|
122251
|
+
this.setState("ready", {
|
|
122252
|
+
version,
|
|
122253
|
+
message: `Update complete · Restart to apply`
|
|
122254
|
+
});
|
|
122255
|
+
return true;
|
|
122256
|
+
} else {
|
|
122257
|
+
this.setState("error", {
|
|
122258
|
+
version,
|
|
122259
|
+
message: `Update to v${version} failed`
|
|
122260
|
+
});
|
|
122261
|
+
setTimeout(() => {
|
|
122262
|
+
if (this.state === "error") {
|
|
122263
|
+
this.setState("idle");
|
|
122264
|
+
}
|
|
122265
|
+
}, 4000);
|
|
122266
|
+
return false;
|
|
122267
|
+
}
|
|
122268
|
+
} catch (err) {
|
|
122269
|
+
logError(err, { component: "AutoUpdaterService", method: "installUpdate" });
|
|
122270
|
+
this.setState("error", { version, message: "Update failed" });
|
|
122271
|
+
setTimeout(() => {
|
|
122272
|
+
if (this.state === "error") {
|
|
122273
|
+
this.setState("idle");
|
|
122274
|
+
}
|
|
122275
|
+
}, 4000);
|
|
122276
|
+
return false;
|
|
122277
|
+
} finally {
|
|
122278
|
+
this.isUpdating = false;
|
|
122279
|
+
}
|
|
122280
|
+
}
|
|
122281
|
+
startBackgroundCheck(initialDelayMs = 2000, intervalMs) {
|
|
122282
|
+
if (this.checkTimer)
|
|
122283
|
+
clearTimeout(this.checkTimer);
|
|
122284
|
+
if (this.intervalTimer)
|
|
122285
|
+
clearInterval(this.intervalTimer);
|
|
122286
|
+
const performCheck = async () => {
|
|
122287
|
+
const update = await this.checkForUpdates();
|
|
122288
|
+
if (update && update.hasUpdate) {
|
|
122289
|
+
setTimeout(async () => {
|
|
122290
|
+
await this.installUpdate(update.latestVersion);
|
|
122291
|
+
}, 1200);
|
|
122292
|
+
}
|
|
122293
|
+
};
|
|
122294
|
+
this.checkTimer = setTimeout(async () => {
|
|
122295
|
+
this.checkTimer = null;
|
|
122296
|
+
await performCheck();
|
|
122297
|
+
const recurringInterval = intervalMs ?? this.options.checkIntervalMs;
|
|
122298
|
+
if (recurringInterval && recurringInterval > 0) {
|
|
122299
|
+
this.intervalTimer = setInterval(performCheck, recurringInterval);
|
|
122300
|
+
}
|
|
122301
|
+
}, initialDelayMs);
|
|
122302
|
+
}
|
|
122303
|
+
dispose() {
|
|
122304
|
+
if (this.checkTimer) {
|
|
122305
|
+
clearTimeout(this.checkTimer);
|
|
122306
|
+
this.checkTimer = null;
|
|
122307
|
+
}
|
|
122308
|
+
if (this.intervalTimer) {
|
|
122309
|
+
clearInterval(this.intervalTimer);
|
|
122310
|
+
this.intervalTimer = null;
|
|
122311
|
+
}
|
|
122312
|
+
}
|
|
122313
|
+
}
|
|
122052
122314
|
// src/tui/app.ts
|
|
122053
122315
|
class TUIApp {
|
|
122054
122316
|
engine;
|
|
@@ -122064,6 +122326,7 @@ class TUIApp {
|
|
|
122064
122326
|
ctrlCTimer = null;
|
|
122065
122327
|
isBusy = false;
|
|
122066
122328
|
voiceController;
|
|
122329
|
+
autoUpdater;
|
|
122067
122330
|
permissionQueue;
|
|
122068
122331
|
constructor(options = {}) {
|
|
122069
122332
|
this.cwd = options.cwd ?? process.cwd();
|
|
@@ -122126,6 +122389,20 @@ class TUIApp {
|
|
|
122126
122389
|
this.statusBar.showWarning(warning);
|
|
122127
122390
|
}
|
|
122128
122391
|
});
|
|
122392
|
+
this.autoUpdater = new AutoUpdaterService({
|
|
122393
|
+
currentVersion: options.version,
|
|
122394
|
+
onStatusChange: (state, info) => {
|
|
122395
|
+
if (state === "idle") {
|
|
122396
|
+
this.statusBar.setUpdateStatus(undefined);
|
|
122397
|
+
} else {
|
|
122398
|
+
this.statusBar.setUpdateStatus({
|
|
122399
|
+
state,
|
|
122400
|
+
version: info?.version,
|
|
122401
|
+
message: info?.message
|
|
122402
|
+
});
|
|
122403
|
+
}
|
|
122404
|
+
}
|
|
122405
|
+
});
|
|
122129
122406
|
this.permissionQueue = new PermissionQueue({
|
|
122130
122407
|
onShow: (item) => {
|
|
122131
122408
|
if (this.activeModal)
|
|
@@ -122222,6 +122499,7 @@ class TUIApp {
|
|
|
122222
122499
|
this.engine.mount(this.streamingView, { kind: "custom" });
|
|
122223
122500
|
this.engine.mount(this.promptInput, { keepCursorVisible: true, kind: "input" });
|
|
122224
122501
|
this.engine.mount(this.statusBar, { kind: "custom" });
|
|
122502
|
+
this.autoUpdater.startBackgroundCheck(3000);
|
|
122225
122503
|
this.engine.addInputListener((chunk) => {
|
|
122226
122504
|
const action = parseKeyInput(chunk);
|
|
122227
122505
|
if (this.voiceController.isActive) {
|
|
@@ -122657,6 +122935,7 @@ class TUIApp {
|
|
|
122657
122935
|
this.permissionQueue.clear();
|
|
122658
122936
|
this.session.shutdown().catch(() => {});
|
|
122659
122937
|
this.voiceController.dispose();
|
|
122938
|
+
this.autoUpdater.dispose();
|
|
122660
122939
|
this.engine.cleanupSync();
|
|
122661
122940
|
if (this.onExitCallback) {
|
|
122662
122941
|
this.onExitCallback();
|
package/package.json
CHANGED