@aexol/spectral 0.9.71 → 0.9.73

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.
@@ -1 +1 @@
1
- {"version":3,"file":"browser-service.d.ts","sourceRoot":"","sources":["../../../src/extensions/browser/browser-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser-service.d.ts","sourceRoot":"","sources":["../../../src/extensions/browser/browser-service.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import { registerSessionResourceCleanup } from "../../sdk/ai/session-resources.js";
2
3
  import { getBrowserConfig } from "./config.js";
3
4
  const _require = createRequire(import.meta.url);
4
5
  let _pw = null;
@@ -19,6 +20,7 @@ function loadPlaywright() {
19
20
  }
20
21
  }
21
22
  let instance = null;
23
+ let unregisterInstanceCleanup = null;
22
24
  export class BrowserService {
23
25
  browser = null;
24
26
  context = null;
@@ -36,10 +38,22 @@ export class BrowserService {
36
38
  static getInstance(config) {
37
39
  if (!instance) {
38
40
  instance = new BrowserService(config);
41
+ unregisterInstanceCleanup?.();
42
+ unregisterInstanceCleanup = registerSessionResourceCleanup(() => {
43
+ const current = instance;
44
+ instance = null;
45
+ unregisterInstanceCleanup?.();
46
+ unregisterInstanceCleanup = null;
47
+ if (!current)
48
+ return;
49
+ void current.close().catch(() => { });
50
+ });
39
51
  }
40
52
  return instance;
41
53
  }
42
54
  static resetInstance() {
55
+ unregisterInstanceCleanup?.();
56
+ unregisterInstanceCleanup = null;
43
57
  instance = null;
44
58
  }
45
59
  static isAvailable() {
@@ -1 +1 @@
1
- {"version":3,"file":"native-extensions.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/extensions/native-extensions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;IACX,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,aAAa,CAAC;IAChD;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,cAAc,EAAE,OAAO,CAAC;IACxB,mCAAmC;IACnC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,EAAE,uBAAuB,EA4GtD,CAAC;AAEF;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CAElF;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAI1G;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;IAClG,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC,CAUD"}
1
+ {"version":3,"file":"native-extensions.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/extensions/native-extensions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;IACX,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,aAAa,CAAC;IAChD;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,cAAc,EAAE,OAAO,CAAC;IACxB,mCAAmC;IACnC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,EAAE,uBAAuB,EAmGtD,CAAC;AAEF;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CAElF;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAI1G;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;IAClG,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC,CAUD"}
@@ -68,15 +68,6 @@ export const NATIVE_EXTENSIONS = [
68
68
  defaultEnabled: false,
69
69
  tags: ["wizard", "skills", "artifacts", "integration"],
70
70
  },
71
- {
72
- id: "openrouter-attribution",
73
- label: "OpenRouter Attribution",
74
- description: "Adds attribution headers for OpenRouter API usage tracking and compliance.",
75
- category: "integration",
76
- entryPath: "src/extensions/openrouter-attribution.ts",
77
- defaultEnabled: true,
78
- tags: ["api", "attribution"],
79
- },
80
71
  {
81
72
  id: "spectral-vision-fallback",
82
73
  label: "Spectral Vision Fallback",
@@ -16,16 +16,13 @@ export declare function normalizeForFuzzyMatch(text: string): string;
16
16
  export interface FuzzyMatchResult {
17
17
  /** Whether a match was found */
18
18
  found: boolean;
19
- /** The index where the match starts (in the content that should be used for replacement) */
19
+ /** The index where the match starts in the original content */
20
20
  index: number;
21
21
  /** Length of the matched text */
22
22
  matchLength: number;
23
23
  /** Whether fuzzy matching was used (false = exact match) */
24
24
  usedFuzzyMatch: boolean;
25
- /**
26
- * The content to use for replacement operations.
27
- * When exact match: original content. When fuzzy match: normalized content.
28
- */
25
+ /** The original content to use for replacement operations. */
29
26
  contentForReplacement: string;
30
27
  }
31
28
  export interface Edit {
@@ -37,10 +34,9 @@ export interface AppliedEditsResult {
37
34
  newContent: string;
38
35
  }
39
36
  /**
40
- * Find oldText in content, trying exact match first, then fuzzy match.
41
- * When fuzzy matching is used, the returned contentForReplacement is the
42
- * fuzzy-normalized version of the content (trailing whitespace stripped,
43
- * Unicode quotes/dashes normalized to ASCII).
37
+ * Find oldText in content, trying exact match first, then tolerant whitespace matching.
38
+ * Fuzzy matches still return indexes into the original content, so replacements preserve
39
+ * untouched text outside the matched span.
44
40
  */
45
41
  export declare function fuzzyFindText(content: string, oldText: string): FuzzyMatchResult;
46
42
  /** Strip UTF-8 BOM if present, return both the BOM (if any) and the text without it */
@@ -1 +1 @@
1
- {"version":3,"file":"edit-diff.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/tools/edit-diff.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE9E;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqB3D;AAED,MAAM,WAAW,gBAAgB;IAChC,gCAAgC;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,4FAA4F;IAC5F,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,cAAc,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,qBAAqB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,IAAI;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CAChB;AASD,MAAM,WAAW,kBAAkB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAsChF;AAED,uFAAuF;AACvF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAEvE;AA8CD;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC5C,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,IAAI,EAAE,EACb,IAAI,EAAE,MAAM,GACV,kBAAkB,CA+DpB;AAED,yCAAyC;AACzC,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,MAAM,CAKnH;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,SAAI,GACd;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAuHxD;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,IAAI,EAAE,EACb,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,cAAc,GAAG,aAAa,CAAC,CAyBzC;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,cAAc,GAAG,aAAa,CAAC,CAEzC"}
1
+ {"version":3,"file":"edit-diff.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/tools/edit-diff.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE9E;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqB3D;AAmHD,MAAM,WAAW,gBAAgB;IAChC,gCAAgC;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,cAAc,EAAE,OAAO,CAAC;IACxB,8DAA8D;IAC9D,qBAAqB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,IAAI;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CAChB;AASD,MAAM,WAAW,kBAAkB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAkBhF;AAED,uFAAuF;AACvF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAEvE;AA4CD;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC5C,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,IAAI,EAAE,EACb,IAAI,EAAE,MAAM,GACV,kBAAkB,CA4DpB;AAED,yCAAyC;AACzC,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,MAAM,CAKnH;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,SAAI,GACd;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAuHxD;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,IAAI,EAAE,EACb,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,cAAc,GAAG,aAAa,CAAC,CAyBzC;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,cAAc,GAAG,aAAa,CAAC,CAEzC"}
@@ -48,29 +48,124 @@ export function normalizeForFuzzyMatch(text) {
48
48
  // U+205F medium math space, U+3000 ideographic space
49
49
  .replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, " "));
50
50
  }
51
+ function allIndexes(content, search) {
52
+ const indexes = [];
53
+ if (search.length === 0)
54
+ return indexes;
55
+ let index = content.indexOf(search);
56
+ while (index !== -1) {
57
+ indexes.push(index);
58
+ index = content.indexOf(search, index + 1);
59
+ }
60
+ return indexes;
61
+ }
62
+ function isTrimmedHorizontalWhitespace(char) {
63
+ return /[\t\f\v \u00A0\u2002-\u200A\u202F\u205F\u3000]/u.test(char);
64
+ }
65
+ function normalizeTrimEndWithMap(text) {
66
+ const out = [];
67
+ const map = [];
68
+ let lineStart = 0;
69
+ for (let i = 0; i <= text.length; i++) {
70
+ if (i !== text.length && text[i] !== "\n")
71
+ continue;
72
+ let lineEnd = i;
73
+ while (lineEnd > lineStart && isTrimmedHorizontalWhitespace(text[lineEnd - 1])) {
74
+ lineEnd--;
75
+ }
76
+ for (let j = lineStart; j < lineEnd; j++) {
77
+ out.push(text[j]);
78
+ map.push(j);
79
+ }
80
+ if (i < text.length) {
81
+ out.push("\n");
82
+ map.push(i);
83
+ }
84
+ lineStart = i + 1;
85
+ }
86
+ return { text: out.join(""), map };
87
+ }
88
+ function dedupeMatches(matches) {
89
+ const seen = new Set();
90
+ return matches.filter((match) => {
91
+ const key = `${match.index}:${match.matchLength}`;
92
+ if (seen.has(key))
93
+ return false;
94
+ seen.add(key);
95
+ return true;
96
+ });
97
+ }
98
+ function findTrimEndMatches(content, oldText) {
99
+ const normalizedContent = normalizeTrimEndWithMap(content);
100
+ const normalizedOldText = normalizeTrimEndWithMap(oldText).text;
101
+ return allIndexes(normalizedContent.text, normalizedOldText).map((index) => {
102
+ const normalizedEnd = index + normalizedOldText.length;
103
+ const start = normalizedContent.map[index] ?? 0;
104
+ const end = normalizedEnd >= normalizedContent.map.length
105
+ ? content.length
106
+ : (normalizedContent.map[normalizedEnd - 1] ?? start) + 1;
107
+ return { index: start, matchLength: Math.max(0, end - start), usedFuzzyMatch: true };
108
+ });
109
+ }
110
+ function getLineRanges(content) {
111
+ const ranges = [];
112
+ let start = 0;
113
+ for (let i = 0; i <= content.length; i++) {
114
+ if (i !== content.length && content[i] !== "\n")
115
+ continue;
116
+ const endWithNewline = i < content.length ? i + 1 : i;
117
+ ranges.push({ start, endNoNewline: i, endWithNewline, text: content.slice(start, i) });
118
+ start = i + 1;
119
+ }
120
+ return ranges;
121
+ }
122
+ function findTrimmedLineMatches(content, oldText) {
123
+ const hasTrailingNewline = oldText.endsWith("\n");
124
+ const oldLines = (hasTrailingNewline ? oldText.slice(0, -1) : oldText).split("\n");
125
+ if (oldLines.length === 0 || (oldLines.length === 1 && oldLines[0].trim().length === 0))
126
+ return [];
127
+ const normalizedOldLines = oldLines.map((line) => normalizeForFuzzyMatch(line).trim());
128
+ const contentLines = getLineRanges(content);
129
+ const matches = [];
130
+ for (let i = 0; i <= contentLines.length - oldLines.length; i++) {
131
+ let matched = true;
132
+ for (let j = 0; j < oldLines.length; j++) {
133
+ const contentLine = normalizeForFuzzyMatch(contentLines[i + j].text).trim();
134
+ if (contentLine !== normalizedOldLines[j]) {
135
+ matched = false;
136
+ break;
137
+ }
138
+ }
139
+ if (!matched)
140
+ continue;
141
+ const start = contentLines[i].start;
142
+ const lastLine = contentLines[i + oldLines.length - 1];
143
+ const end = hasTrailingNewline ? lastLine.endWithNewline : lastLine.endNoNewline;
144
+ matches.push({ index: start, matchLength: end - start, usedFuzzyMatch: true });
145
+ }
146
+ return matches;
147
+ }
148
+ function findTextMatches(content, oldText) {
149
+ const exactMatches = allIndexes(content, oldText).map((index) => ({
150
+ index,
151
+ matchLength: oldText.length,
152
+ usedFuzzyMatch: false,
153
+ }));
154
+ if (exactMatches.length > 0)
155
+ return exactMatches;
156
+ const trimEndMatches = findTrimEndMatches(content, oldText);
157
+ if (trimEndMatches.length > 0)
158
+ return dedupeMatches(trimEndMatches);
159
+ return dedupeMatches(findTrimmedLineMatches(content, oldText));
160
+ }
51
161
  /**
52
- * Find oldText in content, trying exact match first, then fuzzy match.
53
- * When fuzzy matching is used, the returned contentForReplacement is the
54
- * fuzzy-normalized version of the content (trailing whitespace stripped,
55
- * Unicode quotes/dashes normalized to ASCII).
162
+ * Find oldText in content, trying exact match first, then tolerant whitespace matching.
163
+ * Fuzzy matches still return indexes into the original content, so replacements preserve
164
+ * untouched text outside the matched span.
56
165
  */
57
166
  export function fuzzyFindText(content, oldText) {
58
- // Try exact match first
59
- const exactIndex = content.indexOf(oldText);
60
- if (exactIndex !== -1) {
61
- return {
62
- found: true,
63
- index: exactIndex,
64
- matchLength: oldText.length,
65
- usedFuzzyMatch: false,
66
- contentForReplacement: content,
67
- };
68
- }
69
- // Try fuzzy match - work entirely in normalized space
70
- const fuzzyContent = normalizeForFuzzyMatch(content);
71
- const fuzzyOldText = normalizeForFuzzyMatch(oldText);
72
- const fuzzyIndex = fuzzyContent.indexOf(fuzzyOldText);
73
- if (fuzzyIndex === -1) {
167
+ const match = findTextMatches(content, oldText)[0];
168
+ if (!match) {
74
169
  return {
75
170
  found: false,
76
171
  index: -1,
@@ -79,15 +174,12 @@ export function fuzzyFindText(content, oldText) {
79
174
  contentForReplacement: content,
80
175
  };
81
176
  }
82
- // When fuzzy matching, we work in the normalized space for replacement.
83
- // This means the output will have normalized whitespace/quotes/dashes,
84
- // which is acceptable since we're fixing minor formatting differences anyway.
85
177
  return {
86
178
  found: true,
87
- index: fuzzyIndex,
88
- matchLength: fuzzyOldText.length,
89
- usedFuzzyMatch: true,
90
- contentForReplacement: fuzzyContent,
179
+ index: match.index,
180
+ matchLength: match.matchLength,
181
+ usedFuzzyMatch: match.usedFuzzyMatch,
182
+ contentForReplacement: content,
91
183
  };
92
184
  }
93
185
  /** Strip UTF-8 BOM if present, return both the BOM (if any) and the text without it */
@@ -95,9 +187,7 @@ export function stripBom(content) {
95
187
  return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
96
188
  }
97
189
  function countOccurrences(content, oldText) {
98
- const fuzzyContent = normalizeForFuzzyMatch(content);
99
- const fuzzyOldText = normalizeForFuzzyMatch(oldText);
100
- return fuzzyContent.split(fuzzyOldText).length - 1;
190
+ return findTextMatches(content, oldText).length;
101
191
  }
102
192
  function getNotFoundError(path, editIndex, totalEdits) {
103
193
  if (totalEdits === 1) {
@@ -141,10 +231,7 @@ export function applyEditsToNormalizedContent(normalizedContent, edits, path) {
141
231
  throw getEmptyOldTextError(path, i, normalizedEdits.length);
142
232
  }
143
233
  }
144
- const initialMatches = normalizedEdits.map((edit) => fuzzyFindText(normalizedContent, edit.oldText));
145
- const baseContent = initialMatches.some((match) => match.usedFuzzyMatch)
146
- ? normalizeForFuzzyMatch(normalizedContent)
147
- : normalizedContent;
234
+ const baseContent = normalizedContent;
148
235
  const matchedEdits = [];
149
236
  for (let i = 0; i < normalizedEdits.length; i++) {
150
237
  const edit = normalizedEdits[i];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aexol/spectral",
3
- "version": "0.9.71",
3
+ "version": "0.9.73",
4
4
  "description": "AI coding agent for Aexol with relay-based browser access.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -1,12 +0,0 @@
1
- /**
2
- * OpenRouter App Attribution extension.
3
- *
4
- * Adds HTTP headers required by OpenRouter's app attribution system so that
5
- * Aexol / Spectral appears in OpenRouter rankings, analytics, and model
6
- * "Apps" tabs. Without these headers our usage is anonymous to OpenRouter.
7
- *
8
- * @see https://openrouter.ai/docs/app-attribution
9
- */
10
- import type { ExtensionAPI } from "../sdk/coding-agent/index.js";
11
- export default function openrouterAttributionExtension(ext: ExtensionAPI): void;
12
- //# sourceMappingURL=openrouter-attribution.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"openrouter-attribution.d.ts","sourceRoot":"","sources":["../../src/extensions/openrouter-attribution.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,MAAM,CAAC,OAAO,UAAU,8BAA8B,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAU9E"}
@@ -1,19 +0,0 @@
1
- /**
2
- * OpenRouter App Attribution extension.
3
- *
4
- * Adds HTTP headers required by OpenRouter's app attribution system so that
5
- * Aexol / Spectral appears in OpenRouter rankings, analytics, and model
6
- * "Apps" tabs. Without these headers our usage is anonymous to OpenRouter.
7
- *
8
- * @see https://openrouter.ai/docs/app-attribution
9
- */
10
- export default function openrouterAttributionExtension(ext) {
11
- ext.registerProvider("openrouter", {
12
- headers: {
13
- "HTTP-Referer": "https://aexol.ai",
14
- "X-OpenRouter-Title": "Aexol",
15
- "X-OpenRouter-Categories": "cli-agent,cloud-agent",
16
- },
17
- });
18
- process.stderr.write("[openrouter-attribution] Registered OpenRouter app attribution headers.\n");
19
- }