@zhiman_innies/innies-codex 0.122.18 → 0.122.21

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/bin/innies.js CHANGED
@@ -13,13 +13,26 @@ const DEFAULT_HOME_DIR = ".inniescoder";
13
13
  const INNIES_HOME_ENV_VAR = "INNIES_HOME";
14
14
  const DEFAULT_CATALOG_FILENAME = "catalog.json";
15
15
  const DEFAULT_CONFIG_FILENAME = "config.toml";
16
+ const DEFAULT_STATE_FILENAME = "innies-state.json";
16
17
  const DASHSCOPE_PROVIDER_HEADER = "[model_providers.dashscope]";
18
+ const RESERVED_PROVIDER_HEADERS = Object.freeze([
19
+ "[model_providers.openai]",
20
+ "[model_providers.ollama]",
21
+ "[model_providers.lmstudio]",
22
+ ]);
23
+ const MODEL_SELECTION_STATES = Object.freeze({
24
+ MANAGED_DEFAULT: "managed_default",
25
+ USER_SELECTED: "user_selected",
26
+ });
17
27
  const ROOT_MANAGED_SETTINGS = Object.freeze([
18
28
  ['model_provider', 'model_provider = "dashscope"'],
19
29
  ["model", `model = "${DEFAULT_MODEL}"`],
20
30
  ["model_catalog_json", null],
21
31
  ['model_reasoning_effort', 'model_reasoning_effort = "none"'],
22
32
  ]);
33
+ const LEGACY_MANAGED_GPT_MODEL = "gpt-5.5";
34
+ const LEGACY_MANAGED_GPT_PROVIDER = "openai";
35
+ const LEGACY_MANAGED_GPT_REASONING = "high";
23
36
 
24
37
  const codexHome = resolveInniesHome();
25
38
  process.env[INNIES_HOME_ENV_VAR] = codexHome;
@@ -31,9 +44,15 @@ ensureInniesHomeDefaults(codexHome);
31
44
  await import("./codex.js");
32
45
 
33
46
  function ensureInniesHomeDefaults(homeDir) {
47
+ const state = loadInniesState(homeDir);
34
48
  const catalogPath = path.join(homeDir, DEFAULT_CATALOG_FILENAME);
35
49
  ensureInniesCatalog(catalogPath);
36
- ensureInniesConfig(path.join(homeDir, DEFAULT_CONFIG_FILENAME), catalogPath);
50
+ const nextState = ensureInniesConfig(
51
+ path.join(homeDir, DEFAULT_CONFIG_FILENAME),
52
+ catalogPath,
53
+ state,
54
+ );
55
+ writeInniesState(homeDir, nextState);
37
56
  }
38
57
 
39
58
  function defaultCatalogAssetPath() {
@@ -48,6 +67,39 @@ function writeJsonFile(filePath, value) {
48
67
  fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
49
68
  }
50
69
 
70
+ function stateFilePath(homeDir) {
71
+ return path.join(homeDir, DEFAULT_STATE_FILENAME);
72
+ }
73
+
74
+ function defaultInniesState() {
75
+ return {
76
+ model_selection_state: MODEL_SELECTION_STATES.MANAGED_DEFAULT,
77
+ };
78
+ }
79
+
80
+ function loadInniesState(homeDir) {
81
+ const filePath = stateFilePath(homeDir);
82
+ if (!fs.existsSync(filePath)) {
83
+ return defaultInniesState();
84
+ }
85
+
86
+ try {
87
+ const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
88
+ if (
89
+ parsed &&
90
+ Object.values(MODEL_SELECTION_STATES).includes(parsed.model_selection_state)
91
+ ) {
92
+ return parsed;
93
+ }
94
+ } catch {}
95
+
96
+ return defaultInniesState();
97
+ }
98
+
99
+ function writeInniesState(homeDir, state) {
100
+ writeJsonFile(stateFilePath(homeDir), state);
101
+ }
102
+
51
103
  function ensureInniesCatalog(catalogPath) {
52
104
  const defaultCatalog = loadDefaultCatalog();
53
105
  if (!fs.existsSync(catalogPath)) {
@@ -81,23 +133,25 @@ function shouldReplaceLegacyCatalog(catalog) {
81
133
  }
82
134
 
83
135
  if (slugs.length === 1 && slugs[0] === DEFAULT_MODEL) {
84
- return false;
136
+ return true;
85
137
  }
86
138
 
87
139
  return slugs.some((slug) => LEGACY_MODELS.has(slug));
88
140
  }
89
141
 
90
- function ensureInniesConfig(configPath, catalogPath) {
142
+ function ensureInniesConfig(configPath, catalogPath, state) {
91
143
  if (!fs.existsSync(configPath)) {
92
144
  fs.writeFileSync(configPath, defaultInniesConfig(catalogPath));
93
- return;
145
+ return defaultInniesState();
94
146
  }
95
147
 
96
148
  const existing = fs.readFileSync(configPath, "utf8");
97
- const updated = normalizeInniesConfig(existing, catalogPath);
149
+ const nextState = determineModelSelectionState(existing, state);
150
+ const updated = normalizeInniesConfig(existing, catalogPath, nextState);
98
151
  if (updated !== existing) {
99
152
  fs.writeFileSync(configPath, updated);
100
153
  }
154
+ return nextState;
101
155
  }
102
156
 
103
157
  function defaultInniesConfig(catalogPath) {
@@ -117,22 +171,21 @@ function defaultInniesConfig(catalogPath) {
117
171
  }
118
172
 
119
173
  function resolveInniesHome() {
120
- return process.env[INNIES_HOME_ENV_VAR] || path.join(os.homedir(), DEFAULT_HOME_DIR);
174
+ return path.join(os.homedir(), DEFAULT_HOME_DIR);
121
175
  }
122
176
 
123
- function normalizeInniesConfig(contents, catalogPath) {
177
+ function normalizeInniesConfig(contents, catalogPath, state) {
124
178
  if (contents.trim() === "") {
125
179
  return defaultInniesConfig(catalogPath);
126
180
  }
127
181
 
128
- const managedSettings = ROOT_MANAGED_SETTINGS.map(([key, line]) => [
129
- key,
130
- key === "model_catalog_json"
131
- ? `model_catalog_json = ${JSON.stringify(catalogPath)}`
132
- : line,
133
- ]);
182
+ const isUserSelected =
183
+ state.model_selection_state === MODEL_SELECTION_STATES.USER_SELECTED;
134
184
  const unmanagedContents = stripManagedRootSettings(contents).trim();
135
- let updated = `${managedSettings.map(([, line]) => line).join("\n")}\n`;
185
+ const managedLines = isUserSelected
186
+ ? preservedUserManagedLines(contents, catalogPath)
187
+ : managedDefaultLines(catalogPath);
188
+ let updated = `${managedLines.join("\n")}\n`;
136
189
 
137
190
  if (unmanagedContents !== "") {
138
191
  updated = `${updated}\n${unmanagedContents}\n`;
@@ -140,6 +193,7 @@ function normalizeInniesConfig(contents, catalogPath) {
140
193
  updated = `${updated}\n`;
141
194
  }
142
195
 
196
+ updated = stripReservedProviderBlocks(updated);
143
197
  if (!updated.includes(DASHSCOPE_PROVIDER_HEADER)) {
144
198
  updated = `${updated.trimEnd()}\n\n${defaultDashscopeProviderBlock()}\n`;
145
199
  }
@@ -147,6 +201,93 @@ function normalizeInniesConfig(contents, catalogPath) {
147
201
  return updated;
148
202
  }
149
203
 
204
+ function managedDefaultLines(catalogPath) {
205
+ return ROOT_MANAGED_SETTINGS.map(([key, line]) =>
206
+ key === "model_catalog_json"
207
+ ? `model_catalog_json = ${JSON.stringify(catalogPath)}`
208
+ : line,
209
+ );
210
+ }
211
+
212
+ function preservedUserManagedLines(contents, catalogPath) {
213
+ const preservedModelProvider = readRootSetting(contents, "model_provider");
214
+ const preservedModel = readRootSetting(contents, "model");
215
+ const preservedEffort = readRootSetting(contents, "model_reasoning_effort");
216
+
217
+ return [
218
+ preservedModelProvider ?? 'model_provider = "openai"',
219
+ preservedModel ?? 'model = "gpt-5.5"',
220
+ `model_catalog_json = ${JSON.stringify(catalogPath)}`,
221
+ preservedEffort ?? 'model_reasoning_effort = "high"',
222
+ ];
223
+ }
224
+
225
+ function readRootSetting(contents, key) {
226
+ const settingPattern = new RegExp(`^\\s*${key}\\s*=.*$`, "m");
227
+ return contents.match(settingPattern)?.[0] ?? null;
228
+ }
229
+
230
+ function stripReservedProviderBlocks(contents) {
231
+ const lines = contents.split(/\r?\n/);
232
+ const keptLines = [];
233
+ let skippingReservedBlock = false;
234
+
235
+ for (const line of lines) {
236
+ const trimmed = line.trim();
237
+ const isSectionHeader = /^\[[^\]]+\]$/.test(trimmed);
238
+ if (isSectionHeader) {
239
+ skippingReservedBlock = RESERVED_PROVIDER_HEADERS.includes(trimmed);
240
+ }
241
+ if (!skippingReservedBlock) {
242
+ keptLines.push(line);
243
+ }
244
+ }
245
+
246
+ return keptLines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
247
+ }
248
+
249
+ function determineModelSelectionState(contents, previousState) {
250
+ const currentModel = extractRootSettingValue(contents, "model");
251
+ const currentProvider = extractRootSettingValue(contents, "model_provider");
252
+ if (currentModel == null && currentProvider == null) {
253
+ return previousState;
254
+ }
255
+
256
+ if (
257
+ currentModel === DEFAULT_MODEL &&
258
+ (currentProvider == null || currentProvider === "dashscope")
259
+ ) {
260
+ return previousState;
261
+ }
262
+
263
+ if (
264
+ previousState.model_selection_state === MODEL_SELECTION_STATES.MANAGED_DEFAULT &&
265
+ currentModel === LEGACY_MANAGED_GPT_MODEL &&
266
+ currentProvider === LEGACY_MANAGED_GPT_PROVIDER &&
267
+ extractRootSettingValue(contents, "model_reasoning_effort") ===
268
+ LEGACY_MANAGED_GPT_REASONING
269
+ ) {
270
+ return previousState;
271
+ }
272
+
273
+ if (
274
+ currentModel != null &&
275
+ !LEGACY_MODELS.has(currentModel) &&
276
+ (currentModel !== DEFAULT_MODEL || currentProvider !== "dashscope")
277
+ ) {
278
+ return {
279
+ model_selection_state: MODEL_SELECTION_STATES.USER_SELECTED,
280
+ };
281
+ }
282
+
283
+ return previousState;
284
+ }
285
+
286
+ function extractRootSettingValue(contents, key) {
287
+ const match = contents.match(new RegExp(`^\\s*${key}\\s*=\\s*\"([^\"]*)\"`, "m"));
288
+ return match?.[1] ?? null;
289
+ }
290
+
150
291
  function stripManagedRootSettings(contents) {
151
292
  let stripped = contents;
152
293
  for (const [key] of ROOT_MANAGED_SETTINGS) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhiman_innies/innies-codex",
3
- "version": "0.122.18",
3
+ "version": "0.122.21",
4
4
  "license": "Apache-2.0",
5
5
  "bin": {
6
6
  "innies": "bin/innies.js"
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "packageManager": "pnpm@10.29.3+sha512.498e1fb4cca5aa06c1dcf2611e6fafc50972ffe7189998c409e90de74566444298ffe43e6cd2acdc775ba1aa7cc5e092a8b7054c811ba8c5770f84693d33d2dc",
22
22
  "optionalDependencies": {
23
- "@zhiman_innies/innies-codex-darwin-x64": "0.122.18-darwin-x64",
24
- "@zhiman_innies/innies-codex-darwin-arm64": "0.122.18-darwin-arm64"
23
+ "@zhiman_innies/innies-codex-darwin-x64": "0.122.21-darwin-x64",
24
+ "@zhiman_innies/innies-codex-darwin-arm64": "0.122.21-darwin-arm64"
25
25
  }
26
26
  }