codex-copilot-dx 0.6.12 → 0.6.13

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 CHANGED
@@ -118,15 +118,15 @@ discovery tries the compatible local CCDX merge first, and only exact enabled
118
118
  Anthropic model IDs use the isolated CCDX Claude profile. Neither credential is
119
119
  copied into the other application.
120
120
 
121
- CCDX keeps exact built-in recipes for PM Studio 2.9.7 and 2.9.10, and can also
122
- derive a source-bound recipe for a newer macOS build when its signed App layout,
123
- ASAR integrity metadata, and one exact Copilot configuration module remain
124
- structurally compatible. This is best-effort semantic compatibility, not a
125
- promise that every future PM Studio architecture can be patched. A missing or
126
- ambiguous module, changed request transport, unsupported integrity policy, or
127
- unverifiable source is rejected without modifying the App. Authorize the
128
- isolated Claude profile first, quit PM Studio and its updater, then run the
129
- one-time setup command:
121
+ CCDX treats the local PM Studio App selected for setup as the source of truth.
122
+ Its version and build label backup and recovery records; neither they nor the
123
+ installation source or original signature are compatibility admission gates.
124
+ Setup derives a source-bound recipe whenever the local ASAR integrity metadata
125
+ and exactly one compatible Copilot configuration module can be verified. A
126
+ missing or ambiguous module, changed request transport, or unsupported
127
+ integrity policy is rejected without modifying the App. Authorize the isolated
128
+ Claude profile first, quit PM Studio and its updater, then run the one-time
129
+ setup command:
130
130
 
131
131
  ```bash
132
132
  ccdx auth login claude --reauth --github-login <personal-github-login>
@@ -142,29 +142,20 @@ ccdx pms status
142
142
 
143
143
  `ccdx pm-studio status` and `ccdx pm-studio setup` are equivalent long-form aliases.
144
144
 
145
- `ccdx pms setup` performs preflight, source verification, backup, staging,
146
- integrity, signing, and replacement work. It never starts Device Flow, the
147
- adapter, Codex, or PM Studio, and it does not modify PM profiles, cached model
148
- lists, or saved PM tokens. A structurally compatible unknown version with an
149
- intact vendor signature is verified locally. If the upstream release has the
150
- known invalid-signature shape, setup downloads only the exact stable asset from
151
- the fixed official GitHub repository, enforces its API-provided SHA-256 and
152
- size, extracts it privately, and requires the installed App to match that tree
153
- (apart from strictly scoped locale pruning) before any backup or staging write.
154
- Network, hash, tree, structure, partial-patch, running-process, space, and every
155
- other pre-replacement failure leave the installed App unchanged. If final
145
+ `ccdx pms setup` performs local preflight, structural verification, backup,
146
+ staging, integrity, signing, and replacement work. It does not access a PM
147
+ Studio repository, release asset, PAT, or source-signing service. It never
148
+ starts Device Flow, the adapter, Codex, or PM Studio, and it does not modify PM
149
+ profiles, cached model lists, or saved PM tokens. Local fingerprints bind the
150
+ recipe, backup, staged App, and installed App without deciding whether the
151
+ original package is trusted. Structure, partial-patch, running-process, space,
152
+ and every other pre-replacement failure leave the installed App unchanged. If final
156
153
  read-only verification after an atomic replacement detects exceptional
157
154
  filesystem drift, setup does not attempt a second automatic write: it preserves
158
155
  the verified backup, retains any diagnostic stage still present, and prints
159
156
  recovery paths. Repeating setup reuses the version/source-bound verified backup
160
- and is idempotent; status and repeated setup do not redownload the release.
161
-
162
- Some official PM Studio artifacts can fail macOS's vendor-signature verifier
163
- even though their signing metadata remains readable. CCDX does not generally
164
- ignore that failure: exact built-in recipes require their frozen official
165
- fingerprint, while compatible unknown versions require the verified official
166
- GitHub asset and tree comparison described above. The staged and installed
167
- CCDX patch must still pass strict ad-hoc signature verification and match the
157
+ and is idempotent. The staged and installed CCDX patch must still pass strict
158
+ ad-hoc signature verification, use the App's bundle identifier, and match the
168
159
  complete patched-tree record in the backup manifest.
169
160
 
170
161
  The patch uses split origins, not an App-wide proxy. GPT and other non-Claude
@@ -183,9 +174,9 @@ CCDX uses the PM bearer only to fetch or validate official model access; it is
183
174
  never used as the credential for the isolated Claude upstream request. Keep
184
175
  port 2026 local to the machine.
185
176
 
186
- Patching replaces the vendor signature with an ad-hoc signature. PM Studio's
177
+ Patching applies an ad-hoc signature to the staged App. PM Studio's
187
178
  updater is not disabled; an official update may overwrite the patch or may
188
- require reinstalling the official App. Setup prints the verified backup and
179
+ require reinstalling PM Studio. Setup prints the verified backup and
189
180
  manifest paths plus the version-matched restore procedure. Never restore a
190
181
  backup over a different PM Studio version or build.
191
182
 
@@ -202,7 +193,7 @@ BACKUP_APP="<verified-backup-path-from-ccdx-pms-setup>"
202
193
  ```
203
194
 
204
195
  Only when all four values match each other and the version/build recorded in the
205
- source-bound manifest, move the patched App aside, restore the complete vendor
196
+ source-bound manifest, move the patched App aside, restore the complete local
206
197
  bundle, and verify it before launch:
207
198
 
208
199
  ```bash
@@ -214,12 +205,11 @@ mv "/Applications/PM Studio.app" "$PATCHED_APP"
214
205
  ccdx pms status
215
206
  ```
216
207
 
217
- For an official artifact with the documented upstream signature failure, the
218
- App-patch row from `ccdx pms status` must still classify the restored bundle as
219
- verified clean content rather than integrity drift. If the version check, copy,
220
- or both verification paths fail,
208
+ The App-patch row from `ccdx pms status` must classify the restored bundle as
209
+ the recorded clean local content rather than integrity drift. If the version
210
+ check, copy, or either verification path fails,
221
211
  do not launch the restored App. Keep the moved patched bundle at `$PATCHED_APP`
222
- and reinstall the official PM Studio instead of applying a version-mismatched
212
+ and reinstall PM Studio instead of applying a version-mismatched
223
213
  backup. CCDX never runs these restore commands or `sudo` automatically.
224
214
 
225
215
  To change the model used by Codex Auto-review, run:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-copilot-dx",
3
- "version": "0.6.12",
3
+ "version": "0.6.13",
4
4
  "description": "Use Codex Desktop, Claude Code, and Claude App with GitHub Copilot",
5
5
  "author": "Dale Xiao",
6
6
  "type": "module",
package/src/doctor.mjs CHANGED
@@ -599,11 +599,11 @@ export async function collectDoctorChecks({
599
599
  } else if (pm.app.state === "clean") {
600
600
  checks.push({
601
601
  kind: "warn",
602
- message: `PM Studio ${version} is supported but not patched`,
602
+ message: `PM Studio ${version} has a compatible local patch structure but is not patched`,
603
603
  fix: "ccdx pms setup",
604
604
  });
605
605
  } else if (pm.app.state === "unsupported") {
606
- checks.push({ kind: "warn", message: `PM Studio ${version} compatibility cannot be safely verified; no files will be changed` });
606
+ checks.push({ kind: "warn", message: `PM Studio ${version} does not expose one uniquely compatible patch structure; no files will be changed` });
607
607
  } else if (pm.app.state === "drift" || pm.app.state === "error") {
608
608
  checks.push({ kind: "err", message: `PM Studio integrity check failed: ${pm.app.issues.join("; ")}` });
609
609
  }
@@ -8,17 +8,42 @@ export const ELECTRON_ASAR_INTEGRITY_SENTINEL = "AGbevlPCksUGKNL8TSn7wGmJEuJsXb2
8
8
  const PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH = 2_121;
9
9
  const PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE_SHA256 = "3da6245d676015ab6cd26e4d6a0d2f54894a687c370aa419c5ad170533d086aa";
10
10
  const PM_STUDIO_COPILOT_CONFIG_MODULE_ANCHOR = "47024(I,e,t){";
11
+ const PM_STUDIO_COPILOT_CONFIG_MODULE_SENTINEL = `API_ENDPOINT:"${PM_STUDIO_ORIGIN}"`;
11
12
  const PM_STUDIO_COPILOT_CONFIG_MODULE_PATH = "dist/main/main.js";
12
13
  const PM_STUDIO_COMPATIBILITY_ID = "exact-copilot-config-module-v1";
13
14
  const PM_STUDIO_COMPATIBLE_TARGET_MAX_BYTES = 128 * 1024 * 1024;
14
15
  const PM_STUDIO_COMPATIBLE_BLOCK_MAX_COUNT = 16_384;
16
+ const PM_STUDIO_COMPATIBLE_MODULE_PREFIX_MAX_BYTES = 512;
17
+ const PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE = '47024(I,e,t){"use strict";t.d(e,{qn:()=>l});const l={CLIENT_ID:"Iv1.b507a08c87ecfe98",CLIENT_SECRET:void 0,API_ENDPOINT:"https://api.githubcopilot.com",DEVICE_CODE_URL:"https://github.com/login/device/code",ACCESS_TOKEN_URL:"https://github.com/login/oauth/access_token",COPILOT_TOKEN_URL:"https://api.github.com/copilot_internal/v2/token",USER_AGENT:"GitHubCopilotChat/0.26.7",EDITOR_VERSION:"vscode/1.99.3",EDITOR_PLUGIN_VERSION:"copilot-chat/0.26.7",INTEGRATION_ID:"vscode-chat",STANDARD_HEADERS:{Accept:"application/json","Content-Type":"application/json","User-Agent":"GitHubCopilotChat/0.26.7","Editor-Version":"vscode/1.99.3","Editor-Plugin-Version":"copilot-chat/0.26.7","Copilot-Integration-Id":"vscode-chat","X-Request-Id":()=>`req_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}};class c{constructor(){this.config={...l}}static getInstance(){return c.instance||(c.instance=new c),c.instance}getConfig(){return{...this.config}}updateConfig(I){this.config={...this.config,...I}}resetConfig(){this.config={...l}}validateConfig(){return function(){const I=[];return l.CLIENT_ID||I.push("GitHub Copilot Client ID is required"),l.API_ENDPOINT||I.push("GitHub Copilot API endpoint is required"),{valid:0===I.length,errors:I}}()}getStandardHeaders(I){return function(I){const e={Accept:l.STANDARD_HEADERS.Accept,"Content-Type":l.STANDARD_HEADERS["Content-Type"],"User-Agent":l.STANDARD_HEADERS["User-Agent"],"Editor-Version":l.STANDARD_HEADERS["Editor-Version"],"Editor-Plugin-Version":l.STANDARD_HEADERS["Editor-Plugin-Version"],"Copilot-Integration-Id":l.STANDARD_HEADERS["Copilot-Integration-Id"],"X-Request-Id":"function"==typeof l.STANDARD_HEADERS["X-Request-Id"]?l.STANDARD_HEADERS["X-Request-Id"]():l.STANDARD_HEADERS["X-Request-Id"]};return I&&(e.Authorization=`Bearer ${I}`),e}(I)}getCopilotTokenHeaders(I){return function(I){return{Authorization:`Bearer ${I}`,Accept:"application/json","User-Agent":l.USER_AGENT,"Editor-Version":l.EDITOR_VERSION,"Editor-Plugin-Version":l.EDITOR_PLUGIN_VERSION}}(I)}getDeviceCodeHeaders(){return{Accept:"application/json","User-Agent":l.USER_AGENT}}}c.getInstance()}';
18
+
19
+ function copilotConfigAlphaTemplate() {
20
+ // This is a reviewed byte template, not a generic JavaScript normalizer:
21
+ // only the five minifier bindings vary; every other byte remains exact.
22
+ const parts = [];
23
+ const moduleIdEnd = PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE.indexOf("(");
24
+ const pattern = /(?<![$\w])(?:I|e|t|l|c)(?![$\w])/g;
25
+ pattern.lastIndex = moduleIdEnd;
26
+ let offset = moduleIdEnd;
27
+ for (let match = pattern.exec(PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE); match; match = pattern.exec(PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE)) {
28
+ parts.push(PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE.slice(offset, match.index));
29
+ parts.push(Object.freeze({ identifier: match[0] }));
30
+ offset = match.index + match[0].length;
31
+ }
32
+ parts.push(PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE.slice(offset));
33
+ return Object.freeze(parts);
34
+ }
35
+
36
+ const PM_STUDIO_COPILOT_CONFIG_ALPHA_TEMPLATE = copilotConfigAlphaTemplate();
15
37
 
16
- function splitOriginConfigModule() {
17
- const source = `47024(I,e,t){"use strict";t.d(e,{qn:()=>l});const l={CLIENT_ID:"Iv1.b507a08c87ecfe98",CLIENT_SECRET:void 0,API_ENDPOINT:"${PM_STUDIO_ORIGIN}",DEVICE_CODE_URL:"https://github.com/login/device/code",ACCESS_TOKEN_URL:"https://github.com/login/oauth/access_token",COPILOT_TOKEN_URL:"https://api.github.com/copilot_internal/v2/token",USER_AGENT:"GitHubCopilotChat/0.26.7",EDITOR_VERSION:"vscode/1.99.3",EDITOR_PLUGIN_VERSION:"copilot-chat/0.26.7",INTEGRATION_ID:"vscode-chat",STANDARD_HEADERS:{Accept:"application/json","Content-Type":"application/json","User-Agent":"GitHubCopilotChat/0.26.7","Editor-Version":"vscode/1.99.3","Editor-Plugin-Version":"copilot-chat/0.26.7","Copilot-Integration-Id":"vscode-chat","X-Request-Id":()=>\`req_\${Date.now()}_\${Math.random().toString(36).substr(2,9)}\`}};const c=fetch.bind(globalThis),n="${CCDX_PM_STUDIO_ORIGIN}",a="${PM_STUDIO_SPLIT_ORIGIN_MARKER}",h="X-CCDX-PM-Relay",d=new Set,m=I=>I.headers.get(h)===a,q=I=>AbortSignal.any([I,AbortSignal.timeout(750)].filter(Boolean)),f=(I,e=400)=>Response.json({error:{code:I}},{status:e,headers:{[h]:a}});globalThis.fetch=async(I,e)=>{const t=I.url||I+"";if(t===\`\${l.API_ENDPOINT}/models\`){try{const t=await c(\`\${n}/models\`,e);if(t.ok&&m(t))try{let I=await t.clone().json(),e=I.data||I;if(Array.isArray(e)){d.clear();for(const I of e)/anthropic/i.test(I.vendor||I.owned_by)&&d.add(I.id);return t}}catch{}t.body?.cancel?.()?.catch?.(()=>{})}catch(I){if(e?.signal?.aborted)throw e.signal.reason||I}return c(I,e)}const s=t.startsWith(l.API_ENDPOINT)?t.slice(l.API_ENDPOINT.length):"";if("POST"===e?.method&&["/chat/completions","/responses","/embeddings"].includes(s)){let t="";try{t=(JSON.parse(e.body).model||"").trim()}catch{}if(d.has(t)||/^claude-/i.test(t)){if("/chat/completions"!==s)return f("model_not_supported");try{const I=await c(\`\${n}/models\`,{signal:q(e?.signal)}),t=401===I.status&&m(I);await I.text();if(!t)throw 0;const l=await c(\`\${n}\${s}\`,e);if(!m(l)){await l.body?.cancel?.();throw 0}return l}catch(I){if(e?.signal?.aborted)throw e.signal.reason||I;return f("relay_incompatible",503)}}}return c(I,e)}}`;
18
- if (source.length > PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH) {
38
+ function splitOriginConfigModule(
39
+ moduleId = "47024",
40
+ windowLength = PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH,
41
+ ) {
42
+ const source = `${moduleId}(I,e,t){"use strict";t.d(e,{qn:()=>l});const l={CLIENT_ID:"Iv1.b507a08c87ecfe98",CLIENT_SECRET:void 0,API_ENDPOINT:"${PM_STUDIO_ORIGIN}",DEVICE_CODE_URL:"https://github.com/login/device/code",ACCESS_TOKEN_URL:"https://github.com/login/oauth/access_token",COPILOT_TOKEN_URL:"https://api.github.com/copilot_internal/v2/token",USER_AGENT:"GitHubCopilotChat/0.26.7",EDITOR_VERSION:"vscode/1.99.3",EDITOR_PLUGIN_VERSION:"copilot-chat/0.26.7",INTEGRATION_ID:"vscode-chat",STANDARD_HEADERS:{Accept:"application/json","Content-Type":"application/json","User-Agent":"GitHubCopilotChat/0.26.7","Editor-Version":"vscode/1.99.3","Editor-Plugin-Version":"copilot-chat/0.26.7","Copilot-Integration-Id":"vscode-chat","X-Request-Id":()=>\`req_\${Date.now()}_\${Math.random().toString(36).substr(2,9)}\`}};const c=fetch.bind(globalThis),n="${CCDX_PM_STUDIO_ORIGIN}",a="${PM_STUDIO_SPLIT_ORIGIN_MARKER}",h="X-CCDX-PM-Relay",d=new Set,m=I=>I.headers.get(h)===a,q=I=>AbortSignal.any([I,AbortSignal.timeout(750)].filter(Boolean)),f=(I,e=400)=>Response.json({error:{code:I}},{status:e,headers:{[h]:a}});globalThis.fetch=async(I,e)=>{const t=I.url||I+"";if(t===\`\${l.API_ENDPOINT}/models\`){try{const t=await c(\`\${n}/models\`,e);if(t.ok&&m(t))try{let I=await t.clone().json(),e=I.data||I;if(Array.isArray(e)){d.clear();for(const I of e)/anthropic/i.test(I.vendor||I.owned_by)&&d.add(I.id);return t}}catch{}t.body?.cancel?.()?.catch?.(()=>{})}catch(I){if(e?.signal?.aborted)throw e.signal.reason||I}return c(I,e)}const s=t.startsWith(l.API_ENDPOINT)?t.slice(l.API_ENDPOINT.length):"";if("POST"===e?.method&&["/chat/completions","/responses","/embeddings"].includes(s)){let t="";try{t=(JSON.parse(e.body).model||"").trim()}catch{}if(d.has(t)||/^claude-/i.test(t)){if("/chat/completions"!==s)return f("model_not_supported");try{const I=await c(\`\${n}/models\`,{signal:q(e?.signal)}),t=401===I.status&&m(I);await I.text();if(!t)throw 0;const l=await c(\`\${n}\${s}\`,e);if(!m(l)){await l.body?.cancel?.();throw 0}return l}catch(I){if(e?.signal?.aborted)throw e.signal.reason||I;return f("relay_incompatible",503)}}}return c(I,e)}}`;
43
+ if (source.length > windowLength) {
19
44
  throw new Error("PM Studio split-origin module exceeds its exact source window");
20
45
  }
21
- return `${source.slice(0, -1)}${" ".repeat(PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH - source.length)}}`;
46
+ return `${source.slice(0, -1)}${" ".repeat(windowLength - source.length)}}`;
22
47
  }
23
48
 
24
49
  export const PM_STUDIO_SPLIT_ORIGIN_CONFIG_MODULE = splitOriginConfigModule();
@@ -320,6 +345,93 @@ function occurrencePositions(haystack, needle) {
320
345
  return positions;
321
346
  }
322
347
 
348
+ function asciiDigit(byte) {
349
+ return byte >= 48 && byte <= 57;
350
+ }
351
+
352
+ function asciiIdentifierStart(byte) {
353
+ return byte === 36
354
+ || byte === 95
355
+ || (byte >= 65 && byte <= 90)
356
+ || (byte >= 97 && byte <= 122);
357
+ }
358
+
359
+ function matchCopilotConfigAlphaTemplate(source, offset) {
360
+ let moduleIdEnd = offset;
361
+ while (moduleIdEnd < source.length && asciiDigit(source[moduleIdEnd])) moduleIdEnd += 1;
362
+ const moduleIdLength = moduleIdEnd - offset;
363
+ if (moduleIdLength === 0
364
+ || moduleIdLength > 10
365
+ || (moduleIdLength > 1 && source[offset] === 48)) return null;
366
+ const moduleId = source.subarray(offset, moduleIdEnd).toString("ascii");
367
+ if (!Number.isSafeInteger(Number(moduleId))) return null;
368
+
369
+ let cursor = moduleIdEnd;
370
+ const identifiers = new Map();
371
+ const reverseIdentifiers = new Map();
372
+ for (const part of PM_STUDIO_COPILOT_CONFIG_ALPHA_TEMPLATE) {
373
+ if (typeof part === "string") {
374
+ const literal = Buffer.from(part);
375
+ if (cursor + literal.length > source.length
376
+ || !source.subarray(cursor, cursor + literal.length).equals(literal)) return null;
377
+ cursor += literal.length;
378
+ continue;
379
+ }
380
+
381
+ if (!asciiIdentifierStart(source[cursor])
382
+ || asciiIdentifierStart(source[cursor + 1])
383
+ || asciiDigit(source[cursor + 1])) return null;
384
+ const candidate = String.fromCharCode(source[cursor]);
385
+ // The audited module uses single-byte minifier names. Keeping that bound
386
+ // rejects capture-prone "semantic" guesses while allowing permutations.
387
+ const mapped = identifiers.get(part.identifier);
388
+ const reverse = reverseIdentifiers.get(candidate);
389
+ if ((mapped && mapped !== candidate) || (reverse && reverse !== part.identifier)) return null;
390
+ identifiers.set(part.identifier, candidate);
391
+ reverseIdentifiers.set(candidate, part.identifier);
392
+ cursor += 1;
393
+ }
394
+
395
+ if (cursor < source.length && source[cursor] !== 44 && source[cursor] !== 125) return null;
396
+ return {
397
+ offset,
398
+ end: cursor,
399
+ length: cursor - offset,
400
+ moduleId,
401
+ anchor: `${moduleId}(`,
402
+ };
403
+ }
404
+
405
+ function locateCompatibleCopilotConfigModule(source) {
406
+ const sentinel = Buffer.from(PM_STUDIO_COPILOT_CONFIG_MODULE_SENTINEL);
407
+ const matches = new Map();
408
+ let sentinelCount = 0;
409
+ let searchOffset = 0;
410
+ while (searchOffset <= source.length - sentinel.length) {
411
+ const sentinelOffset = source.indexOf(sentinel, searchOffset);
412
+ if (sentinelOffset < 0) break;
413
+ sentinelCount += 1;
414
+ searchOffset = sentinelOffset + sentinel.length;
415
+
416
+ const lowerBound = Math.max(0, sentinelOffset - PM_STUDIO_COMPATIBLE_MODULE_PREFIX_MAX_BYTES);
417
+ for (let offset = lowerBound; offset <= sentinelOffset; offset += 1) {
418
+ if (!asciiDigit(source[offset]) || (offset > 0 && asciiDigit(source[offset - 1]))) continue;
419
+ if (offset > 0 && source[offset - 1] !== 44 && source[offset - 1] !== 123) continue;
420
+ const match = matchCopilotConfigAlphaTemplate(source, offset);
421
+ if (match && sentinelOffset >= match.offset && sentinelOffset < match.end) {
422
+ matches.set(match.offset, match);
423
+ }
424
+ }
425
+ }
426
+ if (sentinelCount === 0) {
427
+ throw incompatibleAsar(`${PM_STUDIO_COPILOT_CONFIG_MODULE_PATH} has no compatible Copilot configuration sentinel`);
428
+ }
429
+ if (matches.size !== 1) {
430
+ throw incompatibleAsar(`${PM_STUDIO_COPILOT_CONFIG_MODULE_PATH} has ${matches.size} alpha-compatible Copilot configuration modules, expected exactly one`);
431
+ }
432
+ return matches.values().next().value;
433
+ }
434
+
323
435
  export function blockSha256(buffer, blockSize) {
324
436
  const size = integer(blockSize, "block size");
325
437
  if (size === 0) throw new Error("Invalid ASAR block size: 0");
@@ -385,22 +497,24 @@ export function createCompatiblePmStudioRecipe(value, options = {}) {
385
497
  throw incompatibleAsar(`${PM_STUDIO_COPILOT_CONFIG_MODULE_PATH} integrity metadata does not match its bytes`);
386
498
  }
387
499
 
388
- const anchorPositions = occurrencePositions(
389
- sourceTarget,
390
- Buffer.from(PM_STUDIO_COPILOT_CONFIG_MODULE_ANCHOR),
391
- );
392
- if (anchorPositions.length !== 1) {
393
- throw incompatibleAsar(`${PM_STUDIO_COPILOT_CONFIG_MODULE_PATH} has ${anchorPositions.length} compatible module anchors, expected exactly one`);
500
+ const compatibleModule = locateCompatibleCopilotConfigModule(sourceTarget);
501
+ const editOffset = compatibleModule.offset;
502
+ const editEnd = compatibleModule.end;
503
+ const sourceEdit = sourceTarget.subarray(editOffset, editEnd);
504
+ const anchorPositions = occurrencePositions(sourceTarget, Buffer.from(compatibleModule.anchor));
505
+ if (anchorPositions.length !== 1 || anchorPositions[0] !== editOffset) {
506
+ throw incompatibleAsar(`${PM_STUDIO_COPILOT_CONFIG_MODULE_PATH} compatible module header is not unique`);
507
+ }
508
+ let replacementSource;
509
+ try {
510
+ replacementSource = splitOriginConfigModule(compatibleModule.moduleId, compatibleModule.length);
511
+ } catch (error) {
512
+ throw incompatibleAsar(error.message);
394
513
  }
395
- const editOffset = anchorPositions[0];
396
- const editEnd = editOffset + PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH;
397
- if (editEnd > sourceTarget.length
398
- || sha256Hex(sourceTarget.subarray(editOffset, editEnd))
399
- !== PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE_SHA256) {
400
- throw incompatibleAsar("PM Studio Copilot configuration module is not the exact compatible source window");
514
+ const replacement = Buffer.from(replacementSource);
515
+ if (replacement.length !== compatibleModule.length) {
516
+ throw incompatibleAsar("PM Studio split-origin replacement does not preserve the compatible module size");
401
517
  }
402
-
403
- const replacement = Buffer.from(PM_STUDIO_SPLIT_ORIGIN_CONFIG_MODULE);
404
518
  const patchedTarget = Buffer.from(sourceTarget);
405
519
  replacement.copy(patchedTarget, editOffset);
406
520
  const patchedTargetSha256 = sha256Hex(patchedTarget);
@@ -436,12 +550,12 @@ export function createCompatiblePmStudioRecipe(value, options = {}) {
436
550
  const edit = Object.freeze({
437
551
  offset: editOffset,
438
552
  absoluteOffset: absoluteOffset + editOffset,
439
- length: PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH,
440
- anchor: PM_STUDIO_COPILOT_CONFIG_MODULE_ANCHOR,
553
+ length: compatibleModule.length,
554
+ anchor: compatibleModule.anchor,
441
555
  anchorCount: 1,
442
- sourceSha256: PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE_SHA256,
556
+ sourceSha256: sha256Hex(sourceEdit),
443
557
  patchedSha256: sha256Hex(replacement),
444
- replacement: PM_STUDIO_SPLIT_ORIGIN_CONFIG_MODULE,
558
+ replacement: replacementSource,
445
559
  });
446
560
  const target = Object.freeze({
447
561
  path: PM_STUDIO_COPILOT_CONFIG_MODULE_PATH,