@wavexzore/sandbox 0.1.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/Dockerfile +14 -0
- package/LICENSE +661 -0
- package/NOTICE +3 -0
- package/README.md +153 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/sandbox/cli/install.d.ts +5 -0
- package/dist/sandbox/cli/install.js +335 -0
- package/dist/sandbox/cli/local-store.d.ts +87 -0
- package/dist/sandbox/cli/local-store.js +604 -0
- package/dist/sandbox/cli/opencode-config.d.ts +25 -0
- package/dist/sandbox/cli/opencode-config.js +240 -0
- package/dist/sandbox/cli/path.d.ts +64 -0
- package/dist/sandbox/cli/path.js +127 -0
- package/dist/sandbox/cli/types.d.ts +145 -0
- package/dist/sandbox/cli/types.js +6 -0
- package/dist/sandbox/cli/wavexzore-sandbox.d.ts +65 -0
- package/dist/sandbox/cli/wavexzore-sandbox.js +577 -0
- package/dist/sandbox/core/cli-helper.d.ts +19 -0
- package/dist/sandbox/core/cli-helper.js +64 -0
- package/dist/sandbox/core/docker-archive-utils.d.ts +3 -0
- package/dist/sandbox/core/docker-archive-utils.js +50 -0
- package/dist/sandbox/core/docker-sandbox.d.ts +51 -0
- package/dist/sandbox/core/docker-sandbox.js +675 -0
- package/dist/sandbox/core/edit/filediff.d.ts +16 -0
- package/dist/sandbox/core/edit/filediff.js +21 -0
- package/dist/sandbox/core/edit/index.d.ts +5 -0
- package/dist/sandbox/core/edit/index.js +5 -0
- package/dist/sandbox/core/edit/line-endings.d.ts +4 -0
- package/dist/sandbox/core/edit/line-endings.js +10 -0
- package/dist/sandbox/core/edit/lock.d.ts +1 -0
- package/dist/sandbox/core/edit/lock.js +18 -0
- package/dist/sandbox/core/edit/replace.d.ts +10 -0
- package/dist/sandbox/core/edit/replace.js +14 -0
- package/dist/sandbox/core/edit/replacers.d.ts +15 -0
- package/dist/sandbox/core/edit/replacers.js +241 -0
- package/dist/sandbox/core/logger.d.ts +15 -0
- package/dist/sandbox/core/logger.js +59 -0
- package/dist/sandbox/core/lsp/client.d.ts +63 -0
- package/dist/sandbox/core/lsp/client.js +533 -0
- package/dist/sandbox/core/lsp/config.d.ts +13 -0
- package/dist/sandbox/core/lsp/config.js +36 -0
- package/dist/sandbox/core/lsp/diagnostics.d.ts +12 -0
- package/dist/sandbox/core/lsp/diagnostics.js +65 -0
- package/dist/sandbox/core/lsp/index.d.ts +4 -0
- package/dist/sandbox/core/lsp/index.js +4 -0
- package/dist/sandbox/core/lsp/language.d.ts +24 -0
- package/dist/sandbox/core/lsp/language.js +249 -0
- package/dist/sandbox/core/lsp/manager.d.ts +77 -0
- package/dist/sandbox/core/lsp/manager.js +237 -0
- package/dist/sandbox/core/lsp/tooling.d.ts +14 -0
- package/dist/sandbox/core/lsp/tooling.js +78 -0
- package/dist/sandbox/core/patch-parser.d.ts +23 -0
- package/dist/sandbox/core/patch-parser.js +248 -0
- package/dist/sandbox/core/path-map.d.ts +9 -0
- package/dist/sandbox/core/path-map.js +73 -0
- package/dist/sandbox/core/project-data-storage.d.ts +42 -0
- package/dist/sandbox/core/project-data-storage.js +167 -0
- package/dist/sandbox/core/read/binary.d.ts +4 -0
- package/dist/sandbox/core/read/binary.js +80 -0
- package/dist/sandbox/core/read/format.d.ts +38 -0
- package/dist/sandbox/core/read/format.js +85 -0
- package/dist/sandbox/core/read/index.d.ts +3 -0
- package/dist/sandbox/core/read/index.js +3 -0
- package/dist/sandbox/core/read/permissions.d.ts +7 -0
- package/dist/sandbox/core/read/permissions.js +13 -0
- package/dist/sandbox/core/session-manager.d.ts +29 -0
- package/dist/sandbox/core/session-manager.js +338 -0
- package/dist/sandbox/core/shell/config.d.ts +7 -0
- package/dist/sandbox/core/shell/config.js +82 -0
- package/dist/sandbox/core/shell/output.d.ts +35 -0
- package/dist/sandbox/core/shell/output.js +80 -0
- package/dist/sandbox/core/shell/parser.d.ts +7 -0
- package/dist/sandbox/core/shell/parser.js +122 -0
- package/dist/sandbox/core/shell/permissions.d.ts +13 -0
- package/dist/sandbox/core/shell/permissions.js +33 -0
- package/dist/sandbox/core/shell/workdir.d.ts +4 -0
- package/dist/sandbox/core/shell/workdir.js +19 -0
- package/dist/sandbox/core/stream-utils.d.ts +23 -0
- package/dist/sandbox/core/stream-utils.js +97 -0
- package/dist/sandbox/core/toast.d.ts +47 -0
- package/dist/sandbox/core/toast.js +73 -0
- package/dist/sandbox/core/types.d.ts +159 -0
- package/dist/sandbox/core/types.js +11 -0
- package/dist/sandbox/core/write/bom.d.ts +8 -0
- package/dist/sandbox/core/write/bom.js +15 -0
- package/dist/sandbox/core/write/config.d.ts +14 -0
- package/dist/sandbox/core/write/config.js +188 -0
- package/dist/sandbox/core/write/diagnostics.d.ts +19 -0
- package/dist/sandbox/core/write/diagnostics.js +120 -0
- package/dist/sandbox/core/write/diff.d.ts +7 -0
- package/dist/sandbox/core/write/diff.js +21 -0
- package/dist/sandbox/core/write/formatter.d.ts +16 -0
- package/dist/sandbox/core/write/formatter.js +51 -0
- package/dist/sandbox/core/write/index.d.ts +6 -0
- package/dist/sandbox/core/write/index.js +5 -0
- package/dist/sandbox/core/write/permissions.d.ts +13 -0
- package/dist/sandbox/core/write/permissions.js +19 -0
- package/dist/sandbox/core/write/pipeline.d.ts +48 -0
- package/dist/sandbox/core/write/pipeline.js +229 -0
- package/dist/sandbox/core/write/read-tracker.d.ts +13 -0
- package/dist/sandbox/core/write/read-tracker.js +30 -0
- package/dist/sandbox/git/host-git-manager.d.ts +40 -0
- package/dist/sandbox/git/host-git-manager.js +278 -0
- package/dist/sandbox/git/index.d.ts +5 -0
- package/dist/sandbox/git/index.js +5 -0
- package/dist/sandbox/git/sandbox-git-manager.d.ts +14 -0
- package/dist/sandbox/git/sandbox-git-manager.js +54 -0
- package/dist/sandbox/git/session-git-manager.d.ts +18 -0
- package/dist/sandbox/git/session-git-manager.js +85 -0
- package/dist/sandbox/index.d.ts +205 -0
- package/dist/sandbox/index.js +70 -0
- package/dist/sandbox/plugins/custom-tools.d.ts +203 -0
- package/dist/sandbox/plugins/custom-tools.js +15 -0
- package/dist/sandbox/plugins/session-events.d.ts +10 -0
- package/dist/sandbox/plugins/session-events.js +56 -0
- package/dist/sandbox/plugins/system-transform.d.ts +10 -0
- package/dist/sandbox/plugins/system-transform.js +23 -0
- package/dist/sandbox/tools/bash-output.d.ts +17 -0
- package/dist/sandbox/tools/bash-output.js +35 -0
- package/dist/sandbox/tools/bash-status.d.ts +13 -0
- package/dist/sandbox/tools/bash-status.js +29 -0
- package/dist/sandbox/tools/bash-stop.d.ts +13 -0
- package/dist/sandbox/tools/bash-stop.js +28 -0
- package/dist/sandbox/tools/bash.d.ts +26 -0
- package/dist/sandbox/tools/bash.js +120 -0
- package/dist/sandbox/tools/edit.d.ts +20 -0
- package/dist/sandbox/tools/edit.js +87 -0
- package/dist/sandbox/tools/get-preview-url.d.ts +17 -0
- package/dist/sandbox/tools/get-preview-url.js +16 -0
- package/dist/sandbox/tools/glob.d.ts +17 -0
- package/dist/sandbox/tools/glob.js +23 -0
- package/dist/sandbox/tools/grep.d.ts +17 -0
- package/dist/sandbox/tools/grep.js +23 -0
- package/dist/sandbox/tools/ls.d.ts +17 -0
- package/dist/sandbox/tools/ls.js +21 -0
- package/dist/sandbox/tools/lsp.d.ts +41 -0
- package/dist/sandbox/tools/lsp.js +198 -0
- package/dist/sandbox/tools/multiedit.d.ts +24 -0
- package/dist/sandbox/tools/multiedit.js +83 -0
- package/dist/sandbox/tools/patch.d.ts +14 -0
- package/dist/sandbox/tools/patch.js +260 -0
- package/dist/sandbox/tools/read.d.ts +22 -0
- package/dist/sandbox/tools/read.js +105 -0
- package/dist/sandbox/tools/write.d.ts +16 -0
- package/dist/sandbox/tools/write.js +27 -0
- package/dist/sandbox/tools.d.ts +200 -0
- package/dist/sandbox/tools.js +43 -0
- package/package.json +55 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { diffLines } from 'diff';
|
|
2
|
+
import { createWriteDiff } from '../write/diff.js';
|
|
3
|
+
import { normalizeLineEndings } from './line-endings.js';
|
|
4
|
+
export function createFileDiff(input) {
|
|
5
|
+
let additions = 0;
|
|
6
|
+
let deletions = 0;
|
|
7
|
+
const oldContent = normalizeLineEndings(input.oldContent);
|
|
8
|
+
const newContent = normalizeLineEndings(input.newContent);
|
|
9
|
+
for (const change of diffLines(oldContent, newContent)) {
|
|
10
|
+
if (change.added)
|
|
11
|
+
additions += change.count ?? 0;
|
|
12
|
+
if (change.removed)
|
|
13
|
+
deletions += change.count ?? 0;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
file: input.file,
|
|
17
|
+
patch: createWriteDiff({ oldPath: input.file, newPath: input.file, oldContent, newContent }),
|
|
18
|
+
additions,
|
|
19
|
+
deletions,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function normalizeLineEndings(text) {
|
|
2
|
+
return text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
3
|
+
}
|
|
4
|
+
export function detectLineEnding(text) {
|
|
5
|
+
return text.includes('\r\n') ? '\r\n' : '\n';
|
|
6
|
+
}
|
|
7
|
+
export function convertToLineEnding(text, ending) {
|
|
8
|
+
const normalized = normalizeLineEndings(text);
|
|
9
|
+
return ending === '\r\n' ? normalized.replace(/\n/g, '\r\n') : normalized;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function withFileLock<T>(key: string, fn: () => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const queues = new Map();
|
|
2
|
+
export async function withFileLock(key, fn) {
|
|
3
|
+
const previous = queues.get(key) ?? Promise.resolve();
|
|
4
|
+
let release;
|
|
5
|
+
const current = new Promise((resolve) => {
|
|
6
|
+
release = resolve;
|
|
7
|
+
});
|
|
8
|
+
queues.set(key, previous.then(() => current, () => current));
|
|
9
|
+
await previous.catch(() => undefined);
|
|
10
|
+
try {
|
|
11
|
+
return await fn();
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
release();
|
|
15
|
+
if (queues.get(key) === current)
|
|
16
|
+
queues.delete(key);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { convertToLineEnding, detectLineEnding, normalizeLineEndings } from './line-endings.js';
|
|
2
|
+
import { replaceWithFallbacks } from './replacers.js';
|
|
3
|
+
export function applyOpenCodeEdit(input) {
|
|
4
|
+
if (input.oldString === input.newString) {
|
|
5
|
+
throw new Error('No changes to apply: oldString and newString are identical.');
|
|
6
|
+
}
|
|
7
|
+
if (input.oldString === '')
|
|
8
|
+
return { content: input.newString };
|
|
9
|
+
const ending = detectLineEnding(input.content);
|
|
10
|
+
const normalizedContent = convertToLineEnding(normalizeLineEndings(input.content), ending);
|
|
11
|
+
const oldString = convertToLineEnding(normalizeLineEndings(input.oldString), ending);
|
|
12
|
+
const newString = convertToLineEnding(normalizeLineEndings(input.newString), ending);
|
|
13
|
+
return replaceWithFallbacks(normalizedContent, oldString, newString, input.replaceAll);
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type Replacer = (content: string, find: string) => Generator<string, void, unknown>;
|
|
2
|
+
export declare const SimpleReplacer: Replacer;
|
|
3
|
+
export declare const LineTrimmedReplacer: Replacer;
|
|
4
|
+
export declare const BlockAnchorReplacer: Replacer;
|
|
5
|
+
export declare const WhitespaceNormalizedReplacer: Replacer;
|
|
6
|
+
export declare const IndentationFlexibleReplacer: Replacer;
|
|
7
|
+
export declare const EscapeNormalizedReplacer: Replacer;
|
|
8
|
+
export declare const TrimmedBoundaryReplacer: Replacer;
|
|
9
|
+
export declare const ContextAwareReplacer: Replacer;
|
|
10
|
+
export declare const MultiOccurrenceReplacer: Replacer;
|
|
11
|
+
export declare const REPLACERS: Replacer[];
|
|
12
|
+
export declare function replaceWithFallbacks(content: string, oldString: string, newString: string, replaceAll?: boolean): {
|
|
13
|
+
content: string;
|
|
14
|
+
search: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
function blockAt(lines, start, length) {
|
|
2
|
+
return lines.slice(start, start + length).join('\n');
|
|
3
|
+
}
|
|
4
|
+
function lineOffset(lines, start) {
|
|
5
|
+
let offset = 0;
|
|
6
|
+
for (let i = 0; i < start; i++)
|
|
7
|
+
offset += lines[i].length + 1;
|
|
8
|
+
return offset;
|
|
9
|
+
}
|
|
10
|
+
function sliceByLines(content, lines, start, length) {
|
|
11
|
+
const startOffset = lineOffset(lines, start);
|
|
12
|
+
let endOffset = startOffset;
|
|
13
|
+
for (let i = 0; i < length; i++) {
|
|
14
|
+
endOffset += lines[start + i].length;
|
|
15
|
+
if (i < length - 1)
|
|
16
|
+
endOffset += 1;
|
|
17
|
+
}
|
|
18
|
+
return content.slice(startOffset, endOffset);
|
|
19
|
+
}
|
|
20
|
+
function trimTrailingEmpty(lines) {
|
|
21
|
+
return lines[lines.length - 1] === '' ? lines.slice(0, -1) : lines;
|
|
22
|
+
}
|
|
23
|
+
function normalizeWhitespace(value) {
|
|
24
|
+
return value.replace(/\s+/g, ' ').trim();
|
|
25
|
+
}
|
|
26
|
+
function stripCommonIndent(value) {
|
|
27
|
+
const lines = value.split('\n');
|
|
28
|
+
const nonEmpty = lines.filter((line) => line.trim());
|
|
29
|
+
if (!nonEmpty.length)
|
|
30
|
+
return value;
|
|
31
|
+
const minIndent = Math.min(...nonEmpty.map((line) => line.match(/^\s*/)?.[0].length ?? 0));
|
|
32
|
+
return lines.map((line) => (line.trim() ? line.slice(minIndent) : line)).join('\n');
|
|
33
|
+
}
|
|
34
|
+
function unescapeString(value) {
|
|
35
|
+
return value.replace(/\\(n|t|r|'|"|`|\\|\$)/g, (match, token) => {
|
|
36
|
+
switch (token) {
|
|
37
|
+
case 'n':
|
|
38
|
+
return '\n';
|
|
39
|
+
case 't':
|
|
40
|
+
return '\t';
|
|
41
|
+
case 'r':
|
|
42
|
+
return '\r';
|
|
43
|
+
case "'":
|
|
44
|
+
return "'";
|
|
45
|
+
case '"':
|
|
46
|
+
return '"';
|
|
47
|
+
case '`':
|
|
48
|
+
return '`';
|
|
49
|
+
case '\\':
|
|
50
|
+
return '\\';
|
|
51
|
+
case '$':
|
|
52
|
+
return '$';
|
|
53
|
+
default:
|
|
54
|
+
return match;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function levenshtein(a, b) {
|
|
59
|
+
if (!a || !b)
|
|
60
|
+
return Math.max(a.length, b.length);
|
|
61
|
+
const previous = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
62
|
+
const current = Array(b.length + 1).fill(0);
|
|
63
|
+
for (let i = 1; i <= a.length; i++) {
|
|
64
|
+
current[0] = i;
|
|
65
|
+
for (let j = 1; j <= b.length; j++) {
|
|
66
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
67
|
+
current[j] = Math.min(previous[j] + 1, current[j - 1] + 1, previous[j - 1] + cost);
|
|
68
|
+
}
|
|
69
|
+
for (let j = 0; j <= b.length; j++)
|
|
70
|
+
previous[j] = current[j];
|
|
71
|
+
}
|
|
72
|
+
return previous[b.length];
|
|
73
|
+
}
|
|
74
|
+
function similarity(a, b) {
|
|
75
|
+
const maxLen = Math.max(a.length, b.length);
|
|
76
|
+
if (maxLen === 0)
|
|
77
|
+
return 1;
|
|
78
|
+
return 1 - levenshtein(a, b) / maxLen;
|
|
79
|
+
}
|
|
80
|
+
export const SimpleReplacer = function* (_content, find) {
|
|
81
|
+
yield find;
|
|
82
|
+
};
|
|
83
|
+
export const LineTrimmedReplacer = function* (content, find) {
|
|
84
|
+
const contentLines = content.split('\n');
|
|
85
|
+
const findLines = trimTrailingEmpty(find.split('\n'));
|
|
86
|
+
if (!findLines.length)
|
|
87
|
+
return;
|
|
88
|
+
for (let i = 0; i <= contentLines.length - findLines.length; i++) {
|
|
89
|
+
if (findLines.every((line, index) => contentLines[i + index].trim() === line.trim())) {
|
|
90
|
+
yield sliceByLines(content, contentLines, i, findLines.length);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
export const BlockAnchorReplacer = function* (content, find) {
|
|
95
|
+
const contentLines = content.split('\n');
|
|
96
|
+
const findLines = trimTrailingEmpty(find.split('\n'));
|
|
97
|
+
if (findLines.length < 3)
|
|
98
|
+
return;
|
|
99
|
+
const first = findLines[0].trim();
|
|
100
|
+
const last = findLines[findLines.length - 1].trim();
|
|
101
|
+
let best;
|
|
102
|
+
for (let i = 0; i < contentLines.length; i++) {
|
|
103
|
+
if (contentLines[i].trim() !== first)
|
|
104
|
+
continue;
|
|
105
|
+
for (let j = i + 2; j < contentLines.length; j++) {
|
|
106
|
+
if (contentLines[j].trim() !== last)
|
|
107
|
+
continue;
|
|
108
|
+
const length = j - i + 1;
|
|
109
|
+
const block = blockAt(contentLines, i, length);
|
|
110
|
+
const score = similarity(normalizeWhitespace(block), normalizeWhitespace(findLines.join('\n')));
|
|
111
|
+
if (!best || score > best.score)
|
|
112
|
+
best = { score, start: i, length };
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (best && best.score >= 0.3)
|
|
117
|
+
yield sliceByLines(content, contentLines, best.start, best.length);
|
|
118
|
+
};
|
|
119
|
+
export const WhitespaceNormalizedReplacer = function* (content, find) {
|
|
120
|
+
const normalizedFind = normalizeWhitespace(find);
|
|
121
|
+
const lines = content.split('\n');
|
|
122
|
+
for (const line of lines) {
|
|
123
|
+
if (normalizeWhitespace(line) === normalizedFind)
|
|
124
|
+
yield line;
|
|
125
|
+
}
|
|
126
|
+
const findLines = trimTrailingEmpty(find.split('\n'));
|
|
127
|
+
for (let i = 0; i <= lines.length - findLines.length; i++) {
|
|
128
|
+
const block = blockAt(lines, i, findLines.length);
|
|
129
|
+
if (normalizeWhitespace(block) === normalizedFind)
|
|
130
|
+
yield block;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
export const IndentationFlexibleReplacer = function* (content, find) {
|
|
134
|
+
const normalizedFind = stripCommonIndent(find);
|
|
135
|
+
const lines = content.split('\n');
|
|
136
|
+
const findLines = trimTrailingEmpty(find.split('\n'));
|
|
137
|
+
for (let i = 0; i <= lines.length - findLines.length; i++) {
|
|
138
|
+
const block = blockAt(lines, i, findLines.length);
|
|
139
|
+
if (stripCommonIndent(block) === normalizedFind)
|
|
140
|
+
yield block;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
export const EscapeNormalizedReplacer = function* (content, find) {
|
|
144
|
+
const unescapedFind = unescapeString(find);
|
|
145
|
+
if (content.includes(unescapedFind))
|
|
146
|
+
yield unescapedFind;
|
|
147
|
+
const lines = content.split('\n');
|
|
148
|
+
const findLines = trimTrailingEmpty(unescapedFind.split('\n'));
|
|
149
|
+
for (let i = 0; i <= lines.length - findLines.length; i++) {
|
|
150
|
+
const block = blockAt(lines, i, findLines.length);
|
|
151
|
+
if (unescapeString(block) === unescapedFind)
|
|
152
|
+
yield block;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
export const TrimmedBoundaryReplacer = function* (content, find) {
|
|
156
|
+
const trimmed = find.trim();
|
|
157
|
+
if (trimmed === find)
|
|
158
|
+
return;
|
|
159
|
+
if (content.includes(trimmed))
|
|
160
|
+
yield trimmed;
|
|
161
|
+
const lines = content.split('\n');
|
|
162
|
+
const findLines = trimTrailingEmpty(find.split('\n'));
|
|
163
|
+
for (let i = 0; i <= lines.length - findLines.length; i++) {
|
|
164
|
+
const block = blockAt(lines, i, findLines.length);
|
|
165
|
+
if (block.trim() === trimmed)
|
|
166
|
+
yield block;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
export const ContextAwareReplacer = function* (content, find) {
|
|
170
|
+
const lines = content.split('\n');
|
|
171
|
+
const findLines = trimTrailingEmpty(find.split('\n'));
|
|
172
|
+
if (findLines.length < 3)
|
|
173
|
+
return;
|
|
174
|
+
const first = findLines[0].trim();
|
|
175
|
+
const last = findLines[findLines.length - 1].trim();
|
|
176
|
+
for (let i = 0; i < lines.length; i++) {
|
|
177
|
+
if (lines[i].trim() !== first)
|
|
178
|
+
continue;
|
|
179
|
+
for (let j = i + 2; j < lines.length; j++) {
|
|
180
|
+
if (lines[j].trim() !== last)
|
|
181
|
+
continue;
|
|
182
|
+
const length = j - i + 1;
|
|
183
|
+
if (length !== findLines.length)
|
|
184
|
+
break;
|
|
185
|
+
let total = 0;
|
|
186
|
+
let matched = 0;
|
|
187
|
+
for (let k = 1; k < length - 1; k++) {
|
|
188
|
+
const actual = lines[i + k].trim();
|
|
189
|
+
const expected = findLines[k].trim();
|
|
190
|
+
if (actual || expected) {
|
|
191
|
+
total++;
|
|
192
|
+
if (actual === expected)
|
|
193
|
+
matched++;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (total === 0 || matched / total >= 0.5)
|
|
197
|
+
yield sliceByLines(content, lines, i, length);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
export const MultiOccurrenceReplacer = function* (_content, find) {
|
|
203
|
+
yield find;
|
|
204
|
+
};
|
|
205
|
+
export const REPLACERS = [
|
|
206
|
+
SimpleReplacer,
|
|
207
|
+
LineTrimmedReplacer,
|
|
208
|
+
BlockAnchorReplacer,
|
|
209
|
+
WhitespaceNormalizedReplacer,
|
|
210
|
+
IndentationFlexibleReplacer,
|
|
211
|
+
EscapeNormalizedReplacer,
|
|
212
|
+
TrimmedBoundaryReplacer,
|
|
213
|
+
ContextAwareReplacer,
|
|
214
|
+
MultiOccurrenceReplacer,
|
|
215
|
+
];
|
|
216
|
+
export function replaceWithFallbacks(content, oldString, newString, replaceAll = false) {
|
|
217
|
+
if (oldString === newString)
|
|
218
|
+
throw new Error('No changes to apply: oldString and newString are identical.');
|
|
219
|
+
let foundAny = false;
|
|
220
|
+
const tried = new Set();
|
|
221
|
+
for (const replacer of REPLACERS) {
|
|
222
|
+
for (const search of replacer(content, oldString)) {
|
|
223
|
+
if (!search || tried.has(search))
|
|
224
|
+
continue;
|
|
225
|
+
tried.add(search);
|
|
226
|
+
const index = content.indexOf(search);
|
|
227
|
+
if (index === -1)
|
|
228
|
+
continue;
|
|
229
|
+
foundAny = true;
|
|
230
|
+
if (replaceAll)
|
|
231
|
+
return { content: content.split(search).join(newString), search };
|
|
232
|
+
if (index !== content.lastIndexOf(search))
|
|
233
|
+
continue;
|
|
234
|
+
return { content: content.slice(0, index) + newString + content.slice(index + search.length), search };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (!foundAny) {
|
|
238
|
+
throw new Error('Could not find oldString in the file. It must match exactly, including whitespace, indentation, and line endings.');
|
|
239
|
+
}
|
|
240
|
+
throw new Error('Found multiple matches for oldString. Provide more surrounding context to make the match unique.');
|
|
241
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Daytona Platforms Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import type { LogLevel } from './types.js';
|
|
6
|
+
export declare function setLogFilePath(path: string): void;
|
|
7
|
+
declare class Logger {
|
|
8
|
+
private get logFile();
|
|
9
|
+
log(message: string, level?: LogLevel): void;
|
|
10
|
+
info(message: string): void;
|
|
11
|
+
error(message: string): void;
|
|
12
|
+
warn(message: string): void;
|
|
13
|
+
}
|
|
14
|
+
export declare const logger: Logger;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Daytona Platforms Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Logger class for handling plugin logging
|
|
7
|
+
*/
|
|
8
|
+
import { appendFileSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'fs';
|
|
9
|
+
import { dirname } from 'path';
|
|
10
|
+
import { LOG_LEVEL_INFO, LOG_LEVEL_ERROR, LOG_LEVEL_WARN } from './types.js';
|
|
11
|
+
let logFilePath;
|
|
12
|
+
export function setLogFilePath(path) {
|
|
13
|
+
logFilePath = path;
|
|
14
|
+
}
|
|
15
|
+
class Logger {
|
|
16
|
+
get logFile() {
|
|
17
|
+
if (!logFilePath)
|
|
18
|
+
throw new Error('Logger file path not set. Call setLogFilePath(path) before use.');
|
|
19
|
+
return logFilePath;
|
|
20
|
+
}
|
|
21
|
+
log(message, level = LOG_LEVEL_INFO) {
|
|
22
|
+
// Ensure log directory exists
|
|
23
|
+
try {
|
|
24
|
+
mkdirSync(dirname(this.logFile), { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Directory may already exist, ignore
|
|
28
|
+
}
|
|
29
|
+
// Trim log file if it exceeds 3MB (keep last 1MB)
|
|
30
|
+
try {
|
|
31
|
+
const stats = statSync(this.logFile);
|
|
32
|
+
const maxSize = 3 * 1024 * 1024;
|
|
33
|
+
const keepSize = 1024 * 1024;
|
|
34
|
+
if (stats.size > maxSize) {
|
|
35
|
+
const content = readFileSync(this.logFile, 'utf8');
|
|
36
|
+
const trimmed = content.slice(-keepSize);
|
|
37
|
+
// Drop partial first line so we don't start mid-log
|
|
38
|
+
const firstNewline = trimmed.indexOf('\n');
|
|
39
|
+
writeFileSync(this.logFile, firstNewline >= 0 ? trimmed.slice(firstNewline + 1) : trimmed);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// File may not exist yet, ignore
|
|
44
|
+
}
|
|
45
|
+
const timestamp = new Date().toISOString();
|
|
46
|
+
const logEntry = `[${timestamp}] [${level}] ${message}\n`;
|
|
47
|
+
appendFileSync(this.logFile, logEntry);
|
|
48
|
+
}
|
|
49
|
+
info(message) {
|
|
50
|
+
this.log(message, LOG_LEVEL_INFO);
|
|
51
|
+
}
|
|
52
|
+
error(message) {
|
|
53
|
+
this.log(message, LOG_LEVEL_ERROR);
|
|
54
|
+
}
|
|
55
|
+
warn(message) {
|
|
56
|
+
this.log(message, LOG_LEVEL_WARN);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export const logger = new Logger();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Diagnostic, Location, LocationLink, Position } from 'vscode-languageserver-types';
|
|
2
|
+
import { type HostLspCommand } from './language.js';
|
|
3
|
+
type DiagnosticMode = 'document' | 'full';
|
|
4
|
+
type LocationRequest = {
|
|
5
|
+
filePath: string;
|
|
6
|
+
position: Position;
|
|
7
|
+
};
|
|
8
|
+
export declare class HostLspClient {
|
|
9
|
+
readonly serverID: string;
|
|
10
|
+
readonly root: string;
|
|
11
|
+
private readonly process;
|
|
12
|
+
private readonly connection;
|
|
13
|
+
private readonly initialization?;
|
|
14
|
+
private readonly documents;
|
|
15
|
+
private readonly pushDiagnostics;
|
|
16
|
+
private readonly pullDiagnostics;
|
|
17
|
+
private readonly published;
|
|
18
|
+
private readonly diagnosticRegistrations;
|
|
19
|
+
private readonly registrationListeners;
|
|
20
|
+
private readonly diagnosticListeners;
|
|
21
|
+
private capabilities?;
|
|
22
|
+
private closed;
|
|
23
|
+
private constructor();
|
|
24
|
+
static create(input: {
|
|
25
|
+
serverID: string;
|
|
26
|
+
root: string;
|
|
27
|
+
command: HostLspCommand;
|
|
28
|
+
}): Promise<HostLspClient>;
|
|
29
|
+
get diagnostics(): Map<string, Diagnostic[]>;
|
|
30
|
+
touchFile(filePath: string, diagnostics?: DiagnosticMode): Promise<number>;
|
|
31
|
+
closeFile(filePath: string): Promise<void>;
|
|
32
|
+
hover(input: LocationRequest): Promise<unknown>;
|
|
33
|
+
definition(input: LocationRequest): Promise<Location | Location[] | LocationLink[]>;
|
|
34
|
+
references(input: LocationRequest): Promise<Location[]>;
|
|
35
|
+
implementation(input: LocationRequest): Promise<Location | Location[] | LocationLink[]>;
|
|
36
|
+
prepareCallHierarchy(input: LocationRequest): Promise<unknown[]>;
|
|
37
|
+
incomingCalls(item: unknown): Promise<unknown[]>;
|
|
38
|
+
outgoingCalls(item: unknown): Promise<unknown[]>;
|
|
39
|
+
documentSymbol(filePath: string): Promise<unknown>;
|
|
40
|
+
workspaceSymbol(query: string): Promise<unknown>;
|
|
41
|
+
status(): Promise<{
|
|
42
|
+
id: string;
|
|
43
|
+
root: string;
|
|
44
|
+
status: string;
|
|
45
|
+
openFiles: number;
|
|
46
|
+
}>;
|
|
47
|
+
shutdown(): Promise<void>;
|
|
48
|
+
private registerHandlers;
|
|
49
|
+
private initialize;
|
|
50
|
+
private openOrChange;
|
|
51
|
+
private waitForDiagnostics;
|
|
52
|
+
private requestDiagnosticReport;
|
|
53
|
+
private requestWorkspaceDiagnosticReport;
|
|
54
|
+
private requestDocumentDiagnostics;
|
|
55
|
+
private requestFullDiagnostics;
|
|
56
|
+
private mergeDiagnosticResults;
|
|
57
|
+
private documentDiagnosticIdentifiers;
|
|
58
|
+
private workspaceDiagnosticIdentifiers;
|
|
59
|
+
private waitForFreshPush;
|
|
60
|
+
private waitForRegistrationChange;
|
|
61
|
+
private emitRegistrationChange;
|
|
62
|
+
}
|
|
63
|
+
export {};
|