impel-cli 0.18.16 → 0.18.17

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/apps.js +108 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "impel-cli",
3
- "version": "0.18.16",
3
+ "version": "0.18.17",
4
4
  "description": "Prepare isolated Claude and Codex workspaces for every accessible Impel tenant",
5
5
  "type": "module",
6
6
  "bin": {
package/src/apps.js CHANGED
@@ -130,6 +130,10 @@ const MAX_REASONING_LEVEL = { effort: "max", description: "Maximum reasoning dep
130
130
  const ULTRA_REASONING_LEVEL = { effort: "ultra", description: "Maximum reasoning with automatic task delegation" };
131
131
  const FAST_SERVICE_TIER = { id: "priority", name: "Fast", description: "1.5x speed, increased usage" };
132
132
  const FAST_MODE_AUTH_GATE = /([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*)&&!([A-Za-z_$][\w$]*)&&([A-Za-z_$][\w$]*)!=null&&\4\?\.requirements\?\.featureRequirements\?\.fast_mode!==!1/gu;
133
+ const CHATGPT_THREAD_START_PROVIDER = /serviceName:([A-Za-z_$][\w$]*)\.serviceName\?\?this\.options\.defaultServiceName,threadSource:\1\.threadSource===void 0\?`user`:\1\.threadSource/gu;
134
+ const CHATGPT_THREAD_FORK_PROVIDER = /let ([A-Za-z_$][\w$]*)=\{\.\.\.([A-Za-z_$][\w$]*),threadSource:\2\.threadSource===void 0\?`user`:\2\.threadSource\}/gu;
135
+ const CHATGPT_THREAD_RESUME_PROVIDER = /modelProvider:null,cwd:([A-Za-z_$][\w$]*)\?\.cwd\?\?null,approvalPolicy:null,sandbox:null,config:\1\?\.config\?\?null,developerInstructions:\1\?\.developerInstructions\?\?void 0/gu;
136
+ const CHATGPT_STRUCTURED_PROVIDER_FALLBACK = /allowProviderModelFallback:!0/gu;
133
137
  const CHATGPT_RESOURCE_ROOT = "linked_vendor_resources_with_patched_asar";
134
138
  const CLAUDE_PLAN_USAGE_GUARD = "if(!PSe()){cz();return}if(!Ge().hasOrgPolicyBackend())return;if((r=ar())";
135
139
  const IMPEL_CLAUDE_PLAN_USAGE_GUARD = `if(!PSe()){cz();return}${"".padEnd("if(!Ge().hasOrgPolicyBackend())return;".length, " ")}if((r=ar())`;
@@ -271,7 +275,7 @@ export const CURRENT_CONFIG_VERSION = 26;
271
275
  // — which is what made every `impel update` re-trigger macOS permission
272
276
  // prompts. Bump this ONLY when a code change alters the bytes of a built
273
277
  // bundle; leave it alone for changes that don't touch bundle contents.
274
- export const BUNDLE_BUILD_FINGERPRINT = "bundle-2026-07-31.2";
278
+ export const BUNDLE_BUILD_FINGERPRINT = "bundle-2026-08-01.1";
275
279
 
276
280
  /** Parse the tenant's install manifest, or null when absent/corrupt. */
277
281
  export function readTenantManifest(homeDir = os.homedir(), tenantId = null) {
@@ -349,6 +353,10 @@ export function bundleIsCurrent(status, {
349
353
  compatibility.vendorSignaturesPreserved === true
350
354
  && compatibility.resourceRoot === CHATGPT_RESOURCE_ROOT
351
355
  && compatibility.patches?.fastModeForGatewayAuth > 0
356
+ && compatibility.patches?.threadStartProviderOverride === 1
357
+ && compatibility.patches?.threadForkProviderOverride === 1
358
+ && compatibility.patches?.threadResumeProviderOverride === 1
359
+ && compatibility.patches?.structuredProviderFallbackDisabled === 2
352
360
  && compatibility.patches?.tenantDisplayNamePreload === 2
353
361
  )),
354
362
  );
@@ -1729,6 +1737,8 @@ function writeVendoredChatGPTBundleLocked(paths, vendorPath, gatewayUrl, homeDir
1729
1737
  linkVendoredChatGPTResources(staging, vendorBundleName);
1730
1738
  const runtimeAsarPath = path.join(staging, "Contents", "Resources", "app.asar");
1731
1739
  const fastModePatchCount = patchFastModeAuthGate(runtimeAsarPath);
1740
+ const threadProviderPatches = patchChatGPTThreadRouting(runtimeAsarPath);
1741
+ const structuredFallbackPatchCount = patchChatGPTStructuredProviderFallback(runtimeAsarPath);
1732
1742
  const asarHash = asarHeaderHash(runtimeAsarPath);
1733
1743
  const vendorExecutable = path.join(vendorBundle, "Contents", "MacOS", executableName);
1734
1744
  const signingIdentity = resolveVendoredAppSigningIdentity(vendorExecutable);
@@ -1773,6 +1783,10 @@ function writeVendoredChatGPTBundleLocked(paths, vendorPath, gatewayUrl, homeDir
1773
1783
  browserData: paths.chatgpt.browserData,
1774
1784
  patches: {
1775
1785
  fastModeForGatewayAuth: fastModePatchCount,
1786
+ threadStartProviderOverride: threadProviderPatches.start,
1787
+ threadForkProviderOverride: threadProviderPatches.fork,
1788
+ threadResumeProviderOverride: threadProviderPatches.resume,
1789
+ structuredProviderFallbackDisabled: structuredFallbackPatchCount,
1776
1790
  desktopAPIForGatewayAuth: 0,
1777
1791
  tenantDisplayNamePreload: 2,
1778
1792
  },
@@ -1995,6 +2009,99 @@ function patchFastModeAuthGate(asarPath) {
1995
2009
  return applyFixedWidthAsarPatches(asarPath, archive, patches, "ChatGPT/Codex Fast-mode eligibility contract");
1996
2010
  }
1997
2011
 
2012
+ /**
2013
+ * Force every desktop app-server thread request onto the tenant's configured
2014
+ * Impel provider. The pinned renderer passes null for internal metadata turns,
2015
+ * which otherwise lets those turns escape to native OpenAI authentication.
2016
+ */
2017
+ function patchChatGPTThreadRouting(asarPath) {
2018
+ const archive = fs.readFileSync(asarPath);
2019
+ const source = archive.toString("latin1");
2020
+ const provider = JSON.stringify(RUNTIME_BRAND.cli.providerId);
2021
+ const contracts = [
2022
+ {
2023
+ name: "start",
2024
+ pattern: CHATGPT_THREAD_START_PROVIDER,
2025
+ replacement: (match) => (
2026
+ `modelProvider:${provider},serviceName:${match[1]}.serviceName??this.options.defaultServiceName,`
2027
+ + `threadSource:${match[1]}.threadSource??\`user\``
2028
+ ),
2029
+ },
2030
+ {
2031
+ name: "fork",
2032
+ pattern: CHATGPT_THREAD_FORK_PROVIDER,
2033
+ replacement: (match) => (
2034
+ `let ${match[1]}={...${match[2]},modelProvider:${provider},`
2035
+ + `threadSource:${match[2]}.threadSource??\`user\`}`
2036
+ ),
2037
+ },
2038
+ {
2039
+ name: "resume",
2040
+ pattern: CHATGPT_THREAD_RESUME_PROVIDER,
2041
+ replacement: (match) => (
2042
+ `modelProvider:${provider},cwd:${match[1]}?.cwd??null,approvalPolicy:null,sandbox:null,`
2043
+ + `config:${match[1]}?.config??null,developerInstructions:${match[1]}?.developerInstructions`
2044
+ ),
2045
+ },
2046
+ ];
2047
+ const counts = {};
2048
+ const patches = [];
2049
+ for (const contract of contracts) {
2050
+ const matches = [...source.matchAll(contract.pattern)];
2051
+ if (matches.length !== 1) {
2052
+ throw new Error(
2053
+ `ChatGPT/Codex thread-${contract.name} provider patch expected 1 match, found ${matches.length}`,
2054
+ );
2055
+ }
2056
+ const match = matches[0];
2057
+ const replacement = contract.replacement(match);
2058
+ if (replacement.length > match[0].length) {
2059
+ throw new Error(
2060
+ `ChatGPT/Codex thread-${contract.name} provider replacement no longer fits the vendor ASAR contract`,
2061
+ );
2062
+ }
2063
+ patches.push({
2064
+ offset: match.index,
2065
+ original: match[0],
2066
+ replacement: replacement.padEnd(match[0].length, " "),
2067
+ });
2068
+ counts[contract.name] = 1;
2069
+ }
2070
+ applyFixedWidthAsarPatches(
2071
+ asarPath,
2072
+ archive,
2073
+ patches,
2074
+ "ChatGPT/Codex thread-provider routing contract",
2075
+ );
2076
+ return counts;
2077
+ }
2078
+
2079
+ /**
2080
+ * Structured title, description, and classification turns opt into falling
2081
+ * back to another provider in the pinned renderer. Keep those turns fail
2082
+ * closed on Impel by changing the two reviewed boolean literals in place.
2083
+ */
2084
+ function patchChatGPTStructuredProviderFallback(asarPath) {
2085
+ const archive = fs.readFileSync(asarPath);
2086
+ const source = archive.toString("latin1");
2087
+ const matches = [...source.matchAll(CHATGPT_STRUCTURED_PROVIDER_FALLBACK)];
2088
+ if (matches.length !== 2) {
2089
+ throw new Error(
2090
+ `ChatGPT/Codex structured provider-fallback patch expected 2 matches, found ${matches.length}`,
2091
+ );
2092
+ }
2093
+ return applyFixedWidthAsarPatches(
2094
+ asarPath,
2095
+ archive,
2096
+ matches.map((match) => ({
2097
+ offset: match.index,
2098
+ original: match[0],
2099
+ replacement: "allowProviderModelFallback:!1",
2100
+ })),
2101
+ "ChatGPT/Codex structured provider-fallback contract",
2102
+ );
2103
+ }
2104
+
1998
2105
  function applyFixedWidthAsarPatches(asarPath, archive, patches, label) {
1999
2106
  if (patches.length === 0) {
2000
2107
  throw new Error(`this desktop app version has an unsupported ${label}; update impel-cli before installing it`);