pi-agent-flow 1.8.1 → 1.8.3
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 +4 -30
- package/agents/audit.md +1 -2
- package/agents/build.md +1 -0
- package/agents/craft.md +12 -8
- package/agents/debug.md +2 -2
- package/agents/ideas.md +1 -0
- package/agents/scout.md +1 -0
- package/dist/agents.d.ts +41 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +283 -0
- package/dist/agents.js.map +1 -0
- package/dist/batch/batch-bash.d.ts +87 -0
- package/dist/batch/batch-bash.d.ts.map +1 -0
- package/dist/batch/batch-bash.js +369 -0
- package/dist/batch/batch-bash.js.map +1 -0
- package/dist/batch/constants.d.ts +100 -0
- package/dist/batch/constants.d.ts.map +1 -0
- package/dist/batch/constants.js +15 -0
- package/dist/batch/constants.js.map +1 -0
- package/dist/batch/execute.d.ts +21 -0
- package/dist/batch/execute.d.ts.map +1 -0
- package/dist/batch/execute.js +440 -0
- package/dist/batch/execute.js.map +1 -0
- package/dist/batch/fuzzy-edit.d.ts +29 -0
- package/dist/batch/fuzzy-edit.d.ts.map +1 -0
- package/dist/batch/fuzzy-edit.js +257 -0
- package/dist/batch/fuzzy-edit.js.map +1 -0
- package/dist/batch/index.d.ts +85 -0
- package/dist/batch/index.d.ts.map +1 -0
- package/dist/batch/index.js +422 -0
- package/dist/batch/index.js.map +1 -0
- package/dist/batch/render.d.ts +14 -0
- package/dist/batch/render.d.ts.map +1 -0
- package/dist/batch/render.js +74 -0
- package/dist/batch/render.js.map +1 -0
- package/dist/batch/symbols.d.ts +9 -0
- package/dist/batch/symbols.d.ts.map +1 -0
- package/dist/batch/symbols.js +310 -0
- package/dist/batch/symbols.js.map +1 -0
- package/dist/batch.d.ts +12 -0
- package/dist/batch.d.ts.map +1 -0
- package/dist/batch.js +11 -0
- package/dist/batch.js.map +1 -0
- package/dist/cli-args.d.ts +27 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +265 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/config.d.ts +58 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +296 -0
- package/dist/config.js.map +1 -0
- package/dist/depth.d.ts +25 -0
- package/dist/depth.d.ts.map +1 -0
- package/dist/depth.js +160 -0
- package/dist/depth.js.map +1 -0
- package/dist/executor.d.ts +87 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +295 -0
- package/dist/executor.js.map +1 -0
- package/dist/flow-prompt.d.ts +23 -0
- package/dist/flow-prompt.d.ts.map +1 -0
- package/dist/flow-prompt.js +99 -0
- package/dist/flow-prompt.js.map +1 -0
- package/dist/flow.d.ts +76 -0
- package/dist/flow.d.ts.map +1 -0
- package/dist/flow.js +704 -0
- package/dist/flow.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +327 -0
- package/dist/index.js.map +1 -0
- package/dist/reasoning-strip.d.ts +26 -0
- package/dist/reasoning-strip.d.ts.map +1 -0
- package/dist/reasoning-strip.js +58 -0
- package/dist/reasoning-strip.js.map +1 -0
- package/dist/render-utils.d.ts +42 -0
- package/dist/render-utils.d.ts.map +1 -0
- package/dist/render-utils.js +182 -0
- package/dist/render-utils.js.map +1 -0
- package/dist/render.d.ts +24 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +409 -0
- package/dist/render.js.map +1 -0
- package/dist/runner-events.d.ts +59 -0
- package/dist/runner-events.d.ts.map +1 -0
- package/dist/runner-events.js +539 -0
- package/dist/runner-events.js.map +1 -0
- package/dist/session-mode.d.ts +10 -0
- package/dist/session-mode.d.ts.map +1 -0
- package/dist/session-mode.js +25 -0
- package/dist/session-mode.js.map +1 -0
- package/dist/settings-resolver.d.ts +28 -0
- package/dist/settings-resolver.d.ts.map +1 -0
- package/dist/settings-resolver.js +148 -0
- package/dist/settings-resolver.js.map +1 -0
- package/dist/sliding-prompt.d.ts +40 -0
- package/dist/sliding-prompt.d.ts.map +1 -0
- package/dist/sliding-prompt.js +121 -0
- package/dist/sliding-prompt.js.map +1 -0
- package/dist/snapshot.d.ts +29 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +199 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/structured-output.d.ts +36 -0
- package/dist/structured-output.d.ts.map +1 -0
- package/dist/structured-output.js +244 -0
- package/dist/structured-output.js.map +1 -0
- package/dist/timed-bash.d.ts +45 -0
- package/dist/timed-bash.d.ts.map +1 -0
- package/dist/timed-bash.js +219 -0
- package/dist/timed-bash.js.map +1 -0
- package/dist/tool-utils.d.ts +20 -0
- package/dist/tool-utils.d.ts.map +1 -0
- package/dist/tool-utils.js +38 -0
- package/dist/tool-utils.js.map +1 -0
- package/dist/transitions.d.ts +39 -0
- package/dist/transitions.d.ts.map +1 -0
- package/dist/transitions.js +59 -0
- package/dist/transitions.js.map +1 -0
- package/dist/types.d.ts +207 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +143 -0
- package/dist/types.js.map +1 -0
- package/dist/web-tool.d.ts +35 -0
- package/dist/web-tool.d.ts.map +1 -0
- package/dist/web-tool.js +545 -0
- package/dist/web-tool.js.map +1 -0
- package/package.json +7 -5
- package/src/agents.ts +0 -299
- package/src/ambient.d.ts +0 -107
- package/src/batch/batch-bash.ts +0 -443
- package/src/batch/constants.ts +0 -128
- package/src/batch/execute.ts +0 -551
- package/src/batch/fuzzy-edit.ts +0 -323
- package/src/batch/index.ts +0 -494
- package/src/batch/render.ts +0 -81
- package/src/batch/symbols.ts +0 -341
- package/src/batch.ts +0 -28
- package/src/cli-args.ts +0 -315
- package/src/config.ts +0 -391
- package/src/executor.ts +0 -445
- package/src/flow.ts +0 -834
- package/src/hooks.ts +0 -294
- package/src/index.ts +0 -1132
- package/src/render-utils.ts +0 -205
- package/src/render.ts +0 -524
- package/src/runner-events.ts +0 -692
- package/src/session-mode.ts +0 -33
- package/src/sliding-prompt.ts +0 -144
- package/src/structured-output.ts +0 -195
- package/src/timed-bash.ts +0 -270
- package/src/transitions.ts +0 -86
- package/src/types.ts +0 -386
- package/src/web-tool.ts +0 -663
package/src/batch/fuzzy-edit.ts
DELETED
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* batch — fuzzy matching and edit application.
|
|
3
|
-
*
|
|
4
|
-
* Normalises trailing whitespace to allow inexact text matches, then applies
|
|
5
|
-
* one or more edits to a file's content while preserving offsets.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import * as fs from "node:fs/promises";
|
|
9
|
-
import * as os from "node:os";
|
|
10
|
-
import * as path from "node:path";
|
|
11
|
-
import type { EditReplacement } from "./constants.js";
|
|
12
|
-
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
// Normalisation helpers
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
|
|
17
|
-
export function normalizeToLF(text: string): string {
|
|
18
|
-
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function restoreLineEndings(text: string, ending: string): string {
|
|
22
|
-
return ending === "\r\n" ? text.replace(/\n/g, "\r\n") : text;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function detectLineEnding(content: string): string {
|
|
26
|
-
const crlfIdx = content.indexOf("\r\n");
|
|
27
|
-
const lfIdx = content.indexOf("\n");
|
|
28
|
-
if (lfIdx === -1) return "\n";
|
|
29
|
-
if (crlfIdx === -1) return "\n";
|
|
30
|
-
return crlfIdx < lfIdx ? "\r\n" : "\n";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function stripBom(content: string): { bom: string; text: string } {
|
|
34
|
-
return content.startsWith("\uFEFF")
|
|
35
|
-
? { bom: "\uFEFF", text: content.slice(1) }
|
|
36
|
-
: { bom: "", text: content };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
// Fuzzy matching
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
|
|
43
|
-
function normalizeForMatch(text: string): string {
|
|
44
|
-
return text
|
|
45
|
-
.split("\n")
|
|
46
|
-
.map((line) => line.trimEnd())
|
|
47
|
-
.join("\n");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function buildPositionMap(original: string): number[] {
|
|
51
|
-
const normalized = normalizeForMatch(original);
|
|
52
|
-
const map: number[] = new Array(normalized.length + 1);
|
|
53
|
-
let oi = 0;
|
|
54
|
-
let ni = 0;
|
|
55
|
-
|
|
56
|
-
while (oi < original.length && ni < normalized.length) {
|
|
57
|
-
map[ni] = oi;
|
|
58
|
-
if (original[oi] === normalized[ni]) {
|
|
59
|
-
oi++;
|
|
60
|
-
ni++;
|
|
61
|
-
} else {
|
|
62
|
-
oi++;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
while (ni < normalized.length) {
|
|
67
|
-
map[ni] = oi;
|
|
68
|
-
ni++;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
map[normalized.length] = original.length;
|
|
72
|
-
return map;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function fuzzyFindText(
|
|
76
|
-
content: string,
|
|
77
|
-
oldText: string,
|
|
78
|
-
): { found: boolean; index: number; matchLength: number; isExact: boolean } {
|
|
79
|
-
// Try exact match first
|
|
80
|
-
const exactIndex = content.indexOf(oldText);
|
|
81
|
-
if (exactIndex !== -1) {
|
|
82
|
-
return { found: true, index: exactIndex, matchLength: oldText.length, isExact: true };
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Try trimmed match, returning original indices
|
|
86
|
-
const normalizedContent = normalizeForMatch(content);
|
|
87
|
-
const normalizedOld = normalizeForMatch(oldText);
|
|
88
|
-
const fuzzyIndex = normalizedContent.indexOf(normalizedOld);
|
|
89
|
-
if (fuzzyIndex !== -1) {
|
|
90
|
-
const map = buildPositionMap(content);
|
|
91
|
-
const originalStart = map[fuzzyIndex];
|
|
92
|
-
const originalEnd = map[fuzzyIndex + normalizedOld.length];
|
|
93
|
-
return { found: true, index: originalStart, matchLength: originalEnd - originalStart, isExact: false };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return { found: false, index: -1, matchLength: 0, isExact: false };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function countOccurrences(content: string, oldText: string): number {
|
|
100
|
-
const normalizedContent = normalizeForMatch(content);
|
|
101
|
-
const normalizedOld = normalizeForMatch(oldText);
|
|
102
|
-
let count = 0;
|
|
103
|
-
let pos = 0;
|
|
104
|
-
while (true) {
|
|
105
|
-
const idx = normalizedContent.indexOf(normalizedOld, pos);
|
|
106
|
-
if (idx === -1) break;
|
|
107
|
-
count++;
|
|
108
|
-
pos = idx + normalizedOld.length;
|
|
109
|
-
}
|
|
110
|
-
return count;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function countExactOccurrences(content: string, oldText: string): number {
|
|
114
|
-
let count = 0;
|
|
115
|
-
let pos = 0;
|
|
116
|
-
while (true) {
|
|
117
|
-
const idx = content.indexOf(oldText, pos);
|
|
118
|
-
if (idx === -1) break;
|
|
119
|
-
count++;
|
|
120
|
-
pos = idx + oldText.length;
|
|
121
|
-
}
|
|
122
|
-
return count;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// ---------------------------------------------------------------------------
|
|
126
|
-
// Edit application
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
|
|
129
|
-
function applyFuzzyEdit(
|
|
130
|
-
content: string,
|
|
131
|
-
matchIndex: number,
|
|
132
|
-
matchLength: number,
|
|
133
|
-
oldText: string,
|
|
134
|
-
newText: string,
|
|
135
|
-
): string {
|
|
136
|
-
const before = content.substring(0, matchIndex);
|
|
137
|
-
const after = content.substring(matchIndex + matchLength);
|
|
138
|
-
const matched = content.substring(matchIndex, matchIndex + matchLength);
|
|
139
|
-
|
|
140
|
-
const matchedLines = matched.split("\n");
|
|
141
|
-
const oldLines = oldText.split("\n");
|
|
142
|
-
const newLines = newText.split("\n");
|
|
143
|
-
|
|
144
|
-
const resultLines: string[] = [];
|
|
145
|
-
for (let i = 0; i < newLines.length; i++) {
|
|
146
|
-
const newLine = newLines[i];
|
|
147
|
-
const oldLine = oldLines[i] ?? "";
|
|
148
|
-
const matchedLine = matchedLines[i] ?? "";
|
|
149
|
-
|
|
150
|
-
const oldTrailing = oldLine.length - oldLine.trimEnd().length;
|
|
151
|
-
const matchedTrailing = matchedLine.length - matchedLine.trimEnd().length;
|
|
152
|
-
|
|
153
|
-
if (matchedTrailing > oldTrailing) {
|
|
154
|
-
const extraStart = matchedLine.trimEnd().length + oldTrailing;
|
|
155
|
-
resultLines.push(newLine + matchedLine.slice(extraStart));
|
|
156
|
-
} else {
|
|
157
|
-
resultLines.push(newLine);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return before + resultLines.join("\n") + after;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export function applyEdits(
|
|
165
|
-
content: string,
|
|
166
|
-
edits: EditReplacement[],
|
|
167
|
-
filePath: string,
|
|
168
|
-
): { newContent: string; blocksChanged: number } {
|
|
169
|
-
const normalizedEdits = edits.map((e) => ({
|
|
170
|
-
oldText: normalizeToLF(e.f),
|
|
171
|
-
newText: normalizeToLF(e.r),
|
|
172
|
-
}));
|
|
173
|
-
|
|
174
|
-
// Validate non-empty
|
|
175
|
-
for (let i = 0; i < normalizedEdits.length; i++) {
|
|
176
|
-
if (normalizedEdits[i].oldText.length === 0) {
|
|
177
|
-
throw new Error(`edits[${i}].f (oldText) must not be empty in ${filePath}.`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const baseContent = content;
|
|
182
|
-
|
|
183
|
-
// Match all edits
|
|
184
|
-
interface MatchResult {
|
|
185
|
-
editIndex: number;
|
|
186
|
-
matchIndex: number;
|
|
187
|
-
matchLength: number;
|
|
188
|
-
newText: string;
|
|
189
|
-
oldText: string;
|
|
190
|
-
isExact: boolean;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const matchedEdits: MatchResult[] = [];
|
|
194
|
-
for (let i = 0; i < normalizedEdits.length; i++) {
|
|
195
|
-
const edit = normalizedEdits[i];
|
|
196
|
-
const matchResult = fuzzyFindText(baseContent, edit.oldText);
|
|
197
|
-
|
|
198
|
-
if (!matchResult.found) {
|
|
199
|
-
throw new Error(
|
|
200
|
-
edits.length === 1
|
|
201
|
-
? `Could not find the exact text in ${filePath}. The old text must match exactly including all whitespace and newlines.`
|
|
202
|
-
: `Could not find edits[${i}] in ${filePath}. The f (oldText) must match exactly including all whitespace and newlines.`,
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const occurrences = matchResult.isExact
|
|
207
|
-
? countExactOccurrences(baseContent, edit.oldText)
|
|
208
|
-
: countOccurrences(baseContent, edit.oldText);
|
|
209
|
-
if (occurrences > 1) {
|
|
210
|
-
throw new Error(
|
|
211
|
-
edits.length === 1
|
|
212
|
-
? `Found ${occurrences} occurrences of the text in ${filePath}. The text must be unique. Please provide more context to make it unique.`
|
|
213
|
-
: `Found ${occurrences} occurrences of edits[${i}] in ${filePath}. Each f (oldText) must be unique. Please provide more context to make it unique.`,
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
matchedEdits.push({
|
|
218
|
-
editIndex: i,
|
|
219
|
-
matchIndex: matchResult.index,
|
|
220
|
-
matchLength: matchResult.matchLength,
|
|
221
|
-
newText: edit.newText,
|
|
222
|
-
oldText: edit.oldText,
|
|
223
|
-
isExact: matchResult.isExact,
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Sort by position (ascending)
|
|
228
|
-
matchedEdits.sort((a, b) => a.matchIndex - b.matchIndex);
|
|
229
|
-
|
|
230
|
-
// Check for overlaps
|
|
231
|
-
for (let i = 1; i < matchedEdits.length; i++) {
|
|
232
|
-
const previous = matchedEdits[i - 1];
|
|
233
|
-
const current = matchedEdits[i];
|
|
234
|
-
if (previous.matchIndex + previous.matchLength > current.matchIndex) {
|
|
235
|
-
throw new Error(
|
|
236
|
-
`edits[${previous.editIndex}] and edits[${current.editIndex}] overlap in ${filePath}. Merge them into one edit or target disjoint regions.`,
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Apply edits in reverse order to preserve offsets
|
|
242
|
-
let newContent = baseContent;
|
|
243
|
-
for (let i = matchedEdits.length - 1; i >= 0; i--) {
|
|
244
|
-
const edit = matchedEdits[i];
|
|
245
|
-
if (edit.isExact) {
|
|
246
|
-
newContent =
|
|
247
|
-
newContent.substring(0, edit.matchIndex) +
|
|
248
|
-
edit.newText +
|
|
249
|
-
newContent.substring(edit.matchIndex + edit.matchLength);
|
|
250
|
-
} else {
|
|
251
|
-
newContent = applyFuzzyEdit(
|
|
252
|
-
newContent,
|
|
253
|
-
edit.matchIndex,
|
|
254
|
-
edit.matchLength,
|
|
255
|
-
edit.oldText,
|
|
256
|
-
edit.newText,
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (baseContent === newContent) {
|
|
262
|
-
throw new Error(
|
|
263
|
-
edits.length === 1
|
|
264
|
-
? `No changes made to ${filePath}. The replacement produced identical content.`
|
|
265
|
-
: `No changes made to ${filePath}. The replacements produced identical content.`,
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
newContent,
|
|
271
|
-
blocksChanged: matchedEdits.length,
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// ---------------------------------------------------------------------------
|
|
276
|
-
// Path validation
|
|
277
|
-
// ---------------------------------------------------------------------------
|
|
278
|
-
|
|
279
|
-
export function expandTilde(inputPath: string): string {
|
|
280
|
-
if (inputPath === "~") return os.homedir();
|
|
281
|
-
if (inputPath.startsWith("~/")) return path.join(os.homedir(), inputPath.slice(2));
|
|
282
|
-
return inputPath;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export function isWithinDirectory(child: string, parent: string): boolean {
|
|
286
|
-
if (process.platform === "win32") {
|
|
287
|
-
const childLower = child.toLowerCase();
|
|
288
|
-
const parentLower = parent.toLowerCase();
|
|
289
|
-
if (childLower === parentLower) return true;
|
|
290
|
-
const sep = path.win32.sep;
|
|
291
|
-
const prefix = parentLower.endsWith(sep) ? parentLower : parentLower + sep;
|
|
292
|
-
return childLower.startsWith(prefix);
|
|
293
|
-
}
|
|
294
|
-
if (child === parent) return true;
|
|
295
|
-
if (parent === "/") return child.startsWith("/");
|
|
296
|
-
return child.startsWith(parent + path.sep);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export async function validatePath(inputPath: string, cwd: string): Promise<string> {
|
|
300
|
-
const expandedPath = expandTilde(inputPath);
|
|
301
|
-
return path.resolve(cwd, expandedPath);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// ---------------------------------------------------------------------------
|
|
305
|
-
// Levenshtein distance (used by execute for suggestions)
|
|
306
|
-
// ---------------------------------------------------------------------------
|
|
307
|
-
|
|
308
|
-
export function levenshtein(a: string, b: string): number {
|
|
309
|
-
const m = a.length;
|
|
310
|
-
const n = b.length;
|
|
311
|
-
const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
312
|
-
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
313
|
-
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
314
|
-
for (let i = 1; i <= m; i++) {
|
|
315
|
-
for (let j = 1; j <= n; j++) {
|
|
316
|
-
dp[i][j] =
|
|
317
|
-
a[i - 1] === b[j - 1]
|
|
318
|
-
? dp[i - 1][j - 1]
|
|
319
|
-
: 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return dp[m][n];
|
|
323
|
-
}
|