@pi-unipi/unipi 2.6.1 → 2.6.2
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/package.json +1 -2
- package/packages/ask-user/settings-tui.ts +18 -34
- package/packages/autocomplete/src/constants.ts +0 -15
- package/packages/autocomplete/src/provider.ts +3 -17
- package/packages/btw/extensions/btw.ts +18 -45
- package/packages/compactor/README.md +2 -2
- package/packages/compactor/skills/compactor/SKILL.md +1 -1
- package/packages/compactor/skills/compactor-detail/SKILL.md +3 -5
- package/packages/compactor/skills/compactor-doctor/SKILL.md +1 -1
- package/packages/compactor/skills/compactor-stats/SKILL.md +1 -1
- package/packages/compactor/src/commands/index.ts +47 -78
- package/packages/compactor/src/compaction/brief.ts +161 -90
- package/packages/compactor/src/compaction/build-sections.ts +3 -4
- package/packages/compactor/src/compaction/compact-args.ts +86 -0
- package/packages/compactor/src/compaction/cut.ts +270 -28
- package/packages/compactor/src/compaction/drill-down.ts +261 -0
- package/packages/compactor/src/compaction/format-recall.ts +96 -0
- package/packages/compactor/src/compaction/format.ts +8 -3
- package/packages/compactor/src/compaction/hooks.ts +248 -72
- package/packages/compactor/src/compaction/merge.ts +34 -4
- package/packages/compactor/src/compaction/rank.ts +270 -0
- package/packages/compactor/src/compaction/recall-scope.ts +28 -0
- package/packages/compactor/src/compaction/search-entries.ts +333 -96
- package/packages/compactor/src/compaction/skill-collapse.ts +35 -0
- package/packages/compactor/src/compaction/summarize.ts +37 -6
- package/packages/compactor/src/compaction/token-estimate.ts +104 -0
- package/packages/compactor/src/compaction/touched-files.ts +35 -0
- package/packages/compactor/src/config/manager.ts +2 -27
- package/packages/compactor/src/config/presets.ts +0 -2
- package/packages/compactor/src/config/schema.ts +2 -16
- package/packages/compactor/src/executor/executor.ts +6 -15
- package/packages/compactor/src/executor/runtime.ts +2 -12
- package/packages/compactor/src/index.ts +4 -121
- package/packages/compactor/src/info-screen.ts +3 -10
- package/packages/compactor/src/security/evaluator.ts +0 -53
- package/packages/compactor/src/security/policy.ts +7 -8
- package/packages/compactor/src/session/db.ts +0 -6
- package/packages/compactor/src/tools/ctx-execute-file.ts +0 -5
- package/packages/compactor/src/tools/register.ts +27 -50
- package/packages/compactor/src/tools/vcc-recall.ts +86 -48
- package/packages/compactor/src/tui/settings-overlay.ts +20 -40
- package/packages/compactor/src/types.ts +43 -100
- package/packages/core/constants.ts +0 -28
- package/packages/core/events.ts +1 -83
- package/packages/core/index.ts +1 -0
- package/packages/core/package.json +2 -0
- package/packages/core/tui-overlay.ts +89 -0
- package/packages/core/tui-width.ts +18 -0
- package/packages/core/utils.ts +43 -0
- package/packages/footer/src/help.ts +2 -2
- package/packages/footer/src/index.ts +9 -10
- package/packages/footer/src/presets.ts +1 -27
- package/packages/footer/src/registry/index.ts +0 -23
- package/packages/footer/src/rendering/icons.ts +0 -13
- package/packages/footer/src/rendering/renderer.ts +5 -40
- package/packages/footer/src/rendering/separators.ts +0 -10
- package/packages/footer/src/rendering/theme.ts +1 -15
- package/packages/footer/src/segments/compactor.ts +7 -14
- package/packages/footer/src/segments/core.ts +15 -54
- package/packages/footer/src/segments/kanboard.ts +5 -5
- package/packages/footer/src/segments/mcp.ts +5 -5
- package/packages/footer/src/segments/memory.ts +4 -4
- package/packages/footer/src/segments/notify.ts +2 -2
- package/packages/footer/src/segments/ralph.ts +3 -3
- package/packages/footer/src/segments/status-ext.ts +1 -1
- package/packages/footer/src/segments/workflow.ts +3 -3
- package/packages/footer/src/tps-tracker.ts +0 -8
- package/packages/footer/src/tui/settings-tui.ts +14 -31
- package/packages/footer/src/types.ts +0 -15
- package/packages/image/src/register-providers.ts +0 -11
- package/packages/image/src/tools.ts +0 -3
- package/packages/image/src/tui/model-selector.ts +36 -71
- package/packages/info-screen/config.ts +0 -16
- package/packages/info-screen/core-groups.ts +0 -10
- package/packages/info-screen/index.ts +22 -13
- package/packages/info-screen/registry.ts +1 -72
- package/packages/info-screen/tui/info-overlay.ts +85 -100
- package/packages/info-screen/usage-parser.ts +12 -47
- package/packages/input-shortcuts/src/index.ts +3 -3
- package/packages/input-shortcuts/src/registers.ts +0 -8
- package/packages/input-shortcuts/src/settings-overlay.ts +4 -13
- package/packages/input-shortcuts/src/undo-redo.ts +0 -12
- package/packages/mcp/src/bridge/client.ts +0 -15
- package/packages/mcp/src/config/manager.ts +0 -38
- package/packages/mcp/src/config/schema.ts +0 -27
- package/packages/mcp/src/config/sync.ts +0 -29
- package/packages/mcp/src/index.ts +1 -1
- package/packages/mcp/src/tui/add-overlay.ts +6 -6
- package/packages/mcp/src/types.ts +0 -6
- package/packages/memory/embedding.ts +0 -31
- package/packages/memory/mempalace.ts +0 -16
- package/packages/memory/package.json +2 -6
- package/packages/memory/storage.ts +56 -753
- package/packages/milestone/hooks.ts +1 -6
- package/packages/milestone/milestone.ts +2 -41
- package/packages/notify/events.ts +0 -10
- package/packages/notify/settings.ts +0 -19
- package/packages/notify/tui/gotify-setup.ts +108 -143
- package/packages/notify/tui/ntfy-setup.ts +137 -172
- package/packages/notify/tui/recap-model-selector.ts +36 -71
- package/packages/notify/tui/settings-overlay.ts +50 -86
- package/packages/notify/tui/telegram-setup.ts +48 -79
- package/packages/notify/types.ts +0 -2
- package/packages/ralph/index.ts +11 -35
- package/packages/ralph/ralph-loop.ts +3 -44
- package/packages/subagents/package.json +1 -0
- package/packages/subagents/src/agent-manager.ts +0 -51
- package/packages/subagents/src/agent-runner.ts +6 -80
- package/packages/subagents/src/conversation-viewer.ts +1 -19
- package/packages/subagents/src/core-compat.ts +8 -25
- package/packages/subagents/src/custom-agents.ts +0 -1
- package/packages/subagents/src/index.ts +3 -48
- package/packages/subagents/src/types.ts +0 -11
- package/packages/subagents/src/widget.ts +5 -5
- package/packages/unipi/bundled.js +4330 -7707
- package/packages/unipi/index.ts +6 -3
- package/packages/updater/src/changelog.ts +1 -16
- package/packages/updater/src/index.ts +0 -5
- package/packages/updater/src/installer.ts +4 -26
- package/packages/updater/src/markdown.ts +6 -152
- package/packages/updater/src/settings.ts +0 -15
- package/packages/updater/src/tui/changelog-overlay.ts +42 -234
- package/packages/updater/src/tui/list-detail-overlay.ts +260 -0
- package/packages/updater/src/tui/readme-overlay.ts +53 -210
- package/packages/updater/src/tui/settings-overlay.ts +13 -26
- package/packages/updater/src/version.ts +2 -31
- package/packages/utility/package.json +1 -3
- package/packages/utility/src/commands.ts +4 -5
- package/packages/utility/src/herdr-sync.ts +136 -0
- package/packages/utility/src/index.ts +11 -68
- package/packages/utility/src/lifecycle/cleanup.ts +2 -16
- package/packages/utility/src/{diff/settings.ts → settings.ts} +42 -80
- package/packages/utility/src/tools/env.ts +0 -0
- package/packages/utility/src/tui/name-badge-state.ts +18 -2
- package/packages/utility/src/tui/name-badge.ts +5 -4
- package/packages/utility/src/tui/util-settings-tui.ts +41 -224
- package/packages/web-api/package.json +0 -4
- package/packages/web-api/src/engine/dependencies.ts +13 -134
- package/packages/web-api/src/engine/extract.ts +9 -99
- package/packages/web-api/src/engine/format.ts +0 -75
- package/packages/web-api/src/engine/types.ts +0 -39
- package/packages/web-api/src/index.ts +1 -2
- package/packages/web-api/src/providers/base.ts +0 -10
- package/packages/web-api/src/providers/duckduckgo.ts +0 -1
- package/packages/web-api/src/providers/firecrawl.ts +0 -9
- package/packages/web-api/src/providers/jina-reader.ts +0 -9
- package/packages/web-api/src/providers/jina-search.ts +0 -9
- package/packages/web-api/src/providers/perplexity.ts +0 -9
- package/packages/web-api/src/providers/registry.ts +0 -80
- package/packages/web-api/src/providers/serpapi.ts +0 -9
- package/packages/web-api/src/providers/tavily.ts +0 -9
- package/packages/web-api/src/providers/wigolo-client.ts +0 -30
- package/packages/web-api/src/providers/wigolo.ts +0 -1
- package/packages/web-api/src/settings.ts +17 -77
- package/packages/web-api/src/tools.ts +21 -24
- package/packages/web-api/src/tui/settings-dialog.ts +0 -1
- package/packages/workflow/commands.ts +38 -122
- package/packages/workflow/index.ts +1 -5
- package/packages/cocoindex/README.md +0 -94
- package/packages/cocoindex/bridge.ts +0 -842
- package/packages/cocoindex/commands.ts +0 -175
- package/packages/cocoindex/index.ts +0 -48
- package/packages/cocoindex/installer.ts +0 -397
- package/packages/cocoindex/package.json +0 -49
- package/packages/cocoindex/skills/cocoindex/SKILL.md +0 -88
- package/packages/cocoindex/tools.ts +0 -157
- package/packages/compactor/src/display/diff-renderer.ts +0 -281
- package/packages/compactor/src/display/line-width-safety.ts +0 -28
- package/packages/compactor/src/display/render-utils.ts +0 -52
- package/packages/compactor/src/display/thinking-label.ts +0 -18
- package/packages/compactor/src/display/tool-overrides.ts +0 -136
- package/packages/compactor/src/tools/compact.ts +0 -20
- package/packages/subagents/src/file-lock.ts +0 -102
- package/packages/utility/src/cache/ttl-cache.ts +0 -311
- package/packages/utility/src/diff/highlighter.ts +0 -352
- package/packages/utility/src/diff/parser.ts +0 -191
- package/packages/utility/src/diff/renderer.ts +0 -395
- package/packages/utility/src/diff/theme.ts +0 -316
- package/packages/utility/src/diff/wrapper.ts +0 -339
- package/packages/utility/src/display/capabilities.ts +0 -214
- package/packages/utility/src/display/width.ts +0 -226
- package/packages/utility/src/tools/batch.ts +0 -229
- package/packages/utility/src/tui/badge-settings-tui.ts +0 -388
- package/packages/utility/src/tui/badge-settings.ts +0 -67
- package/packages/utility/src/tui/settings-inspector.ts +0 -303
- package/packages/web-api/src/providers/llm-summarize.ts +0 -71
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @pi-unipi/utility — Width Management Utilities
|
|
3
|
-
*
|
|
4
|
-
* Safe width clamping, line wrapping, and line collapsing.
|
|
5
|
-
* Handles ANSI escape sequences correctly.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { WidthOptions } from "../types.js";
|
|
9
|
-
|
|
10
|
-
/** ANSI escape sequence regex */
|
|
11
|
-
const ANSI_REGEX =
|
|
12
|
-
/\u001b\[[\d;]*[a-zA-Z]|\u001b\][^\u0007]*\u0007|\u001b\[[\d;]*[\u0020-\u002f]*[\u0030-\u007e]/g;
|
|
13
|
-
|
|
14
|
-
/** Strip ANSI escape sequences from text */
|
|
15
|
-
export function stripAnsi(text: string): string {
|
|
16
|
-
return text.replace(ANSI_REGEX, "");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Get visual width of text (excluding ANSI codes) */
|
|
20
|
-
export function visualWidth(text: string): number {
|
|
21
|
-
return stripAnsi(text).length;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Default width options */
|
|
25
|
-
const DEFAULT_WIDTH_OPTS: Required<WidthOptions> = {
|
|
26
|
-
ellipsis: "…",
|
|
27
|
-
breakWords: false,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Clamp text to maxWidth visual characters.
|
|
32
|
-
* Preserves ANSI sequences at the end of truncated text.
|
|
33
|
-
*/
|
|
34
|
-
export function clampWidth(
|
|
35
|
-
text: string,
|
|
36
|
-
maxWidth: number,
|
|
37
|
-
options: WidthOptions = {},
|
|
38
|
-
): string {
|
|
39
|
-
const opts = { ...DEFAULT_WIDTH_OPTS, ...options };
|
|
40
|
-
const plain = stripAnsi(text);
|
|
41
|
-
|
|
42
|
-
if (plain.length <= maxWidth) {
|
|
43
|
-
return text;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Need to truncate while preserving ANSI
|
|
47
|
-
const ellipsisWidth = visualWidth(opts.ellipsis);
|
|
48
|
-
const targetWidth = maxWidth - ellipsisWidth;
|
|
49
|
-
|
|
50
|
-
if (targetWidth <= 0) {
|
|
51
|
-
return opts.ellipsis.slice(0, maxWidth);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Walk through text, tracking ANSI state
|
|
55
|
-
let visualCount = 0;
|
|
56
|
-
let result = "";
|
|
57
|
-
let inAnsi = false;
|
|
58
|
-
let ansiBuffer = "";
|
|
59
|
-
|
|
60
|
-
for (const char of text) {
|
|
61
|
-
if (char === "\u001b") {
|
|
62
|
-
inAnsi = true;
|
|
63
|
-
ansiBuffer = char;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (inAnsi) {
|
|
68
|
-
ansiBuffer += char;
|
|
69
|
-
// Check if ANSI sequence is complete
|
|
70
|
-
if (/[a-zA-Z\u0007]/.test(char) || (ansiBuffer.startsWith("\u001b]") && char === "\u0007")) {
|
|
71
|
-
inAnsi = false;
|
|
72
|
-
result += ansiBuffer;
|
|
73
|
-
ansiBuffer = "";
|
|
74
|
-
}
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (visualCount < targetWidth) {
|
|
79
|
-
result += char;
|
|
80
|
-
visualCount++;
|
|
81
|
-
} else {
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Add any pending ANSI sequences and reset
|
|
87
|
-
if (ansiBuffer) {
|
|
88
|
-
result += ansiBuffer;
|
|
89
|
-
}
|
|
90
|
-
result += "\u001b[0m"; // Reset ANSI
|
|
91
|
-
result += opts.ellipsis;
|
|
92
|
-
|
|
93
|
-
return result;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Wrap text into lines of maxWidth visual characters.
|
|
98
|
-
* Respects word boundaries unless breakWords is true.
|
|
99
|
-
*/
|
|
100
|
-
export function wrapLines(
|
|
101
|
-
text: string,
|
|
102
|
-
maxWidth: number,
|
|
103
|
-
options: WidthOptions = {},
|
|
104
|
-
): string[] {
|
|
105
|
-
const opts = { ...DEFAULT_WIDTH_OPTS, ...options };
|
|
106
|
-
const lines: string[] = [];
|
|
107
|
-
const paragraphs = text.split("\n");
|
|
108
|
-
|
|
109
|
-
for (const paragraph of paragraphs) {
|
|
110
|
-
if (visualWidth(paragraph) <= maxWidth) {
|
|
111
|
-
lines.push(paragraph);
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const words = paragraph.split(/(\s+)/);
|
|
116
|
-
let currentLine = "";
|
|
117
|
-
let currentWidth = 0;
|
|
118
|
-
|
|
119
|
-
for (const word of words) {
|
|
120
|
-
const wordWidth = visualWidth(word);
|
|
121
|
-
|
|
122
|
-
if (wordWidth === 0) {
|
|
123
|
-
// Whitespace-only word
|
|
124
|
-
currentLine += word;
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (currentWidth + wordWidth > maxWidth) {
|
|
129
|
-
if (currentLine) {
|
|
130
|
-
lines.push(currentLine);
|
|
131
|
-
currentLine = "";
|
|
132
|
-
currentWidth = 0;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Word itself might be longer than maxWidth
|
|
136
|
-
if (!opts.breakWords && wordWidth > maxWidth) {
|
|
137
|
-
// Break the long word
|
|
138
|
-
let remaining = word;
|
|
139
|
-
while (visualWidth(remaining) > maxWidth) {
|
|
140
|
-
let chunk = "";
|
|
141
|
-
let chunkWidth = 0;
|
|
142
|
-
for (const char of remaining) {
|
|
143
|
-
const charWidth = visualWidth(char);
|
|
144
|
-
if (chunkWidth + charWidth > maxWidth) {
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
chunk += char;
|
|
148
|
-
chunkWidth += charWidth;
|
|
149
|
-
}
|
|
150
|
-
lines.push(chunk);
|
|
151
|
-
remaining = remaining.slice(chunk.length);
|
|
152
|
-
}
|
|
153
|
-
if (remaining) {
|
|
154
|
-
currentLine = remaining;
|
|
155
|
-
currentWidth = visualWidth(remaining);
|
|
156
|
-
}
|
|
157
|
-
} else {
|
|
158
|
-
currentLine = word;
|
|
159
|
-
currentWidth = wordWidth;
|
|
160
|
-
}
|
|
161
|
-
} else {
|
|
162
|
-
currentLine += word;
|
|
163
|
-
currentWidth += wordWidth;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (currentLine) {
|
|
168
|
-
lines.push(currentLine);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return lines;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Collapse consecutive empty lines down to maxEmpty.
|
|
177
|
-
*/
|
|
178
|
-
export function collapseLines(
|
|
179
|
-
lines: string[],
|
|
180
|
-
maxEmpty: number = 1,
|
|
181
|
-
): string[] {
|
|
182
|
-
const result: string[] = [];
|
|
183
|
-
let emptyCount = 0;
|
|
184
|
-
|
|
185
|
-
for (const line of lines) {
|
|
186
|
-
const isEmpty = stripAnsi(line).trim().length === 0;
|
|
187
|
-
|
|
188
|
-
if (isEmpty) {
|
|
189
|
-
emptyCount++;
|
|
190
|
-
if (emptyCount <= maxEmpty) {
|
|
191
|
-
result.push(line);
|
|
192
|
-
}
|
|
193
|
-
} else {
|
|
194
|
-
emptyCount = 0;
|
|
195
|
-
result.push(line);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return result;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Pad text to target width with spaces.
|
|
204
|
-
* Respects ANSI sequences.
|
|
205
|
-
*/
|
|
206
|
-
export function padWidth(text: string, targetWidth: number): string {
|
|
207
|
-
const currentWidth = visualWidth(text);
|
|
208
|
-
if (currentWidth >= targetWidth) {
|
|
209
|
-
return text;
|
|
210
|
-
}
|
|
211
|
-
return text + " ".repeat(targetWidth - currentWidth);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Center text within target width.
|
|
216
|
-
*/
|
|
217
|
-
export function centerWidth(text: string, targetWidth: number): string {
|
|
218
|
-
const currentWidth = visualWidth(text);
|
|
219
|
-
if (currentWidth >= targetWidth) {
|
|
220
|
-
return text;
|
|
221
|
-
}
|
|
222
|
-
const padding = targetWidth - currentWidth;
|
|
223
|
-
const left = Math.floor(padding / 2);
|
|
224
|
-
const right = padding - left;
|
|
225
|
-
return " ".repeat(left) + text + " ".repeat(right);
|
|
226
|
-
}
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @pi-unipi/utility — Batch Execution Tool
|
|
3
|
-
*
|
|
4
|
-
* Atomic batch of commands + searches with rollback on failure.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
BatchCommand,
|
|
9
|
-
BatchOptions,
|
|
10
|
-
BatchResult,
|
|
11
|
-
BatchReport,
|
|
12
|
-
} from "../types.js";
|
|
13
|
-
|
|
14
|
-
/** Default options */
|
|
15
|
-
const DEFAULTS: Required<BatchOptions> = {
|
|
16
|
-
failFast: true,
|
|
17
|
-
commandTimeoutMs: 30000,
|
|
18
|
-
totalTimeoutMs: 300000,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/** Executor function type — provided by the host environment */
|
|
22
|
-
export type CommandExecutor = (
|
|
23
|
-
command: BatchCommand,
|
|
24
|
-
) => Promise<unknown>;
|
|
25
|
-
|
|
26
|
-
/** Rollback function type */
|
|
27
|
-
export type RollbackFn = (
|
|
28
|
-
results: BatchResult[],
|
|
29
|
-
) => Promise<void>;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Execute a batch of commands atomically.
|
|
33
|
-
*
|
|
34
|
-
* @param commands - Array of commands to execute
|
|
35
|
-
* @param executor - Function that executes a single command
|
|
36
|
-
* @param options - Batch execution options
|
|
37
|
-
* @param rollback - Optional rollback function called on failure
|
|
38
|
-
*/
|
|
39
|
-
export async function executeBatch(
|
|
40
|
-
commands: BatchCommand[],
|
|
41
|
-
executor: CommandExecutor,
|
|
42
|
-
options: BatchOptions = {},
|
|
43
|
-
rollback?: RollbackFn,
|
|
44
|
-
): Promise<BatchReport> {
|
|
45
|
-
const opts = { ...DEFAULTS, ...options };
|
|
46
|
-
const results: BatchResult[] = [];
|
|
47
|
-
const startTime = Date.now();
|
|
48
|
-
|
|
49
|
-
// Total timeout guard
|
|
50
|
-
const totalDeadline = startTime + opts.totalTimeoutMs;
|
|
51
|
-
|
|
52
|
-
for (let i = 0; i < commands.length; i++) {
|
|
53
|
-
const command = commands[i];
|
|
54
|
-
const cmdStart = Date.now();
|
|
55
|
-
|
|
56
|
-
// Check total timeout
|
|
57
|
-
if (Date.now() > totalDeadline) {
|
|
58
|
-
const timeoutResult: BatchResult = {
|
|
59
|
-
command,
|
|
60
|
-
success: false,
|
|
61
|
-
error: `Total batch timeout exceeded (${opts.totalTimeoutMs}ms)`,
|
|
62
|
-
durationMs: Date.now() - cmdStart,
|
|
63
|
-
};
|
|
64
|
-
results.push(timeoutResult);
|
|
65
|
-
|
|
66
|
-
if (opts.failFast) {
|
|
67
|
-
const report = createReport(results, startTime, !!rollback);
|
|
68
|
-
if (rollback) {
|
|
69
|
-
await rollback(results).catch(() => {
|
|
70
|
-
// Best-effort rollback
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
return report;
|
|
74
|
-
}
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Execute with per-command timeout
|
|
79
|
-
try {
|
|
80
|
-
const result = await withTimeout(
|
|
81
|
-
executor(command),
|
|
82
|
-
opts.commandTimeoutMs,
|
|
83
|
-
`Command timeout exceeded (${opts.commandTimeoutMs}ms)`,
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
results.push({
|
|
87
|
-
command,
|
|
88
|
-
success: true,
|
|
89
|
-
result,
|
|
90
|
-
durationMs: Date.now() - cmdStart,
|
|
91
|
-
});
|
|
92
|
-
} catch (err) {
|
|
93
|
-
const errorResult: BatchResult = {
|
|
94
|
-
command,
|
|
95
|
-
success: false,
|
|
96
|
-
error: (err as Error).message,
|
|
97
|
-
durationMs: Date.now() - cmdStart,
|
|
98
|
-
};
|
|
99
|
-
results.push(errorResult);
|
|
100
|
-
|
|
101
|
-
if (opts.failFast) {
|
|
102
|
-
const report = createReport(results, startTime, !!rollback);
|
|
103
|
-
if (rollback) {
|
|
104
|
-
await rollback(results).catch(() => {
|
|
105
|
-
// Best-effort rollback
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
return report;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return createReport(results, startTime, false);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** Create a batch report from results */
|
|
117
|
-
function createReport(
|
|
118
|
-
results: BatchResult[],
|
|
119
|
-
startTime: number,
|
|
120
|
-
rolledBack: boolean,
|
|
121
|
-
): BatchReport {
|
|
122
|
-
const allSuccess = results.every((r) => r.success);
|
|
123
|
-
return {
|
|
124
|
-
success: allSuccess && !rolledBack,
|
|
125
|
-
results,
|
|
126
|
-
totalDurationMs: Date.now() - startTime,
|
|
127
|
-
rolledBack,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** Wrap a promise with a timeout */
|
|
132
|
-
function withTimeout<T>(
|
|
133
|
-
promise: Promise<T>,
|
|
134
|
-
timeoutMs: number,
|
|
135
|
-
message: string,
|
|
136
|
-
): Promise<T> {
|
|
137
|
-
return new Promise((resolve, reject) => {
|
|
138
|
-
const timer = setTimeout(() => {
|
|
139
|
-
reject(new Error(message));
|
|
140
|
-
}, timeoutMs);
|
|
141
|
-
|
|
142
|
-
promise
|
|
143
|
-
.then((value) => {
|
|
144
|
-
clearTimeout(timer);
|
|
145
|
-
resolve(value);
|
|
146
|
-
})
|
|
147
|
-
.catch((err) => {
|
|
148
|
-
clearTimeout(timer);
|
|
149
|
-
reject(err);
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** Format a batch report as markdown */
|
|
155
|
-
export function formatBatchReport(report: BatchReport): string {
|
|
156
|
-
const lines = [
|
|
157
|
-
"## 📦 Batch Execution Report",
|
|
158
|
-
"",
|
|
159
|
-
`**Success:** ${report.success ? "✓ Yes" : "✗ No"}`,
|
|
160
|
-
`**Commands:** ${report.results.length}`,
|
|
161
|
-
`**Duration:** ${report.totalDurationMs}ms`,
|
|
162
|
-
report.rolledBack ? "**Rolled back:** Yes" : "",
|
|
163
|
-
"",
|
|
164
|
-
].filter(Boolean);
|
|
165
|
-
|
|
166
|
-
for (let i = 0; i < report.results.length; i++) {
|
|
167
|
-
const r = report.results[i];
|
|
168
|
-
const icon = r.success ? "✓" : "✗";
|
|
169
|
-
lines.push(
|
|
170
|
-
`### ${i + 1}. ${icon} ${r.command.type}:${r.command.name}`,
|
|
171
|
-
`**Duration:** ${r.durationMs}ms`,
|
|
172
|
-
);
|
|
173
|
-
if (r.success) {
|
|
174
|
-
lines.push(`**Result:** \`${JSON.stringify(r.result).slice(0, 200)}\``);
|
|
175
|
-
} else {
|
|
176
|
-
lines.push(`**Error:** ${r.error}`);
|
|
177
|
-
}
|
|
178
|
-
lines.push("");
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return lines.join("\n");
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/** Create a simple command batch builder */
|
|
185
|
-
export class BatchBuilder {
|
|
186
|
-
private commands: BatchCommand[] = [];
|
|
187
|
-
private opts: BatchOptions = {};
|
|
188
|
-
private rollbackFn?: RollbackFn;
|
|
189
|
-
|
|
190
|
-
/** Add a command to the batch */
|
|
191
|
-
addCommand(name: string, args?: Record<string, unknown>): this {
|
|
192
|
-
this.commands.push({ type: "command", name, args });
|
|
193
|
-
return this;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** Add a tool call to the batch */
|
|
197
|
-
addTool(name: string, args?: Record<string, unknown>): this {
|
|
198
|
-
this.commands.push({ type: "tool", name, args });
|
|
199
|
-
return this;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/** Add a search to the batch */
|
|
203
|
-
addSearch(name: string, args?: Record<string, unknown>): this {
|
|
204
|
-
this.commands.push({ type: "search", name, args });
|
|
205
|
-
return this;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/** Set batch options */
|
|
209
|
-
withOptions(options: BatchOptions): this {
|
|
210
|
-
this.opts = { ...this.opts, ...options };
|
|
211
|
-
return this;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/** Set rollback function */
|
|
215
|
-
withRollback(rollback: RollbackFn): this {
|
|
216
|
-
this.rollbackFn = rollback;
|
|
217
|
-
return this;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/** Execute the batch */
|
|
221
|
-
async execute(executor: CommandExecutor): Promise<BatchReport> {
|
|
222
|
-
return executeBatch(this.commands, executor, this.opts, this.rollbackFn);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/** Get the command list */
|
|
226
|
-
getCommands(): readonly BatchCommand[] {
|
|
227
|
-
return this.commands;
|
|
228
|
-
}
|
|
229
|
-
}
|