codex-configurator 0.2.0 → 0.2.2

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
@@ -47,6 +47,7 @@ The right-hand pane shows what each setting means, plus a picker when a value ha
47
47
  Deprecated settings are marked with a `[!]` warning marker; only that marker is highlighted.
48
48
  Model picker entries are curated presets maintained by this project.
49
49
  In select lists, `[default]` marks the default option.
50
+ The header banner shows the configurator package version (for example `v0.2.1`) next to the wordmark.
50
51
 
51
52
  ## TOML-aware navigation
52
53
 
@@ -60,6 +61,7 @@ The table view follows TOML structure, with a root catalog of common keys:
60
61
  - Attributes and subattributes are shown in strict alphabetical order.
61
62
  - Unset boolean settings display explicit defaults as `true [default]` or `false [default]`.
62
63
  - For placeholder keys like `<path>`, IDs entered in the UI are normalized under your home directory, and traversal outside home is rejected.
64
+ - New `<path>` entries are written as explicit tables (for example `[projects."/home/me/repo"]`) instead of inline empty objects.
63
65
 
64
66
  - Dotted/table sections become navigable table nodes.
65
67
  - Inline key-value pairs are shown as leaf entries.
@@ -95,12 +97,12 @@ When the log exceeds the size limit, it is rotated to `<log-path>.1` before writ
95
97
 
96
98
  ## Version checks
97
99
 
98
- Codex version checks are disabled by default to avoid executing unknown binaries from `PATH`.
99
- To enable checks, all of the following must be set:
100
+ Codex version checks are always enabled.
101
+ By default, the app runs global `codex` and `npm` from `PATH`.
102
+ You can override either command with:
100
103
 
101
- - `CODEX_CONFIGURATOR_ENABLE_VERSION_CHECK=1`
102
- - `CODEX_CONFIGURATOR_CODEX_BIN=/absolute/path/to/codex`
103
- - `CODEX_CONFIGURATOR_NPM_BIN=/absolute/path/to/npm`
104
+ - `CODEX_CONFIGURATOR_CODEX_BIN=<command-or-path>`
105
+ - `CODEX_CONFIGURATOR_NPM_BIN=<command-or-path>`
104
106
 
105
107
  ## Upstream reference
106
108
 
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { useState, useEffect } from 'react';
4
4
  import { execFile } from 'node:child_process';
5
- import path from 'node:path';
5
+ import { createRequire } from 'node:module';
6
6
  import { render, useInput, useApp, useStdout, Text, Box } from 'ink';
7
7
  import { CONTROL_HINT, EDIT_CONTROL_HINT } from './src/constants.js';
8
8
  import {
@@ -31,6 +31,9 @@ import {
31
31
  import { Header } from './src/components/Header.js';
32
32
  import { ConfigNavigator } from './src/components/ConfigNavigator.js';
33
33
 
34
+ const require = createRequire(import.meta.url);
35
+ const { version: PACKAGE_VERSION = 'unknown' } = require('./package.json');
36
+
34
37
  const computeListViewportHeight = (rows, terminalRows) =>
35
38
  Math.max(4, Math.min(rows.length, Math.min(20, Math.max(4, terminalRows - 14))));
36
39
 
@@ -58,8 +61,6 @@ const isCustomIdTableRow = (pathSegments, row) =>
58
61
 
59
62
  const isInlineTextMode = (mode) => mode === 'text' || mode === 'add-id';
60
63
  const VERSION_COMMAND_TIMEOUT_MS = 3000;
61
- const VERSION_DISABLED_LABEL = 'version check disabled';
62
- const VERSION_CHECK_ENABLED_ENV_VAR = 'CODEX_CONFIGURATOR_ENABLE_VERSION_CHECK';
63
64
  const CODEX_BIN_ENV_VAR = 'CODEX_CONFIGURATOR_CODEX_BIN';
64
65
  const NPM_BIN_ENV_VAR = 'CODEX_CONFIGURATOR_NPM_BIN';
65
66
 
@@ -85,31 +86,15 @@ const runCommand = (command, args = []) =>
85
86
  );
86
87
  });
87
88
 
88
- const getAbsoluteCommandPath = (environmentVariableName) => {
89
- const configuredPath = String(process.env[environmentVariableName] || '').trim();
90
- if (!configuredPath || !path.isAbsolute(configuredPath)) {
91
- return '';
92
- }
93
-
94
- return configuredPath;
89
+ const getConfiguredCommand = (environmentVariableName, fallbackCommand) => {
90
+ const configuredCommand = String(process.env[environmentVariableName] || '').trim();
91
+ return configuredCommand || fallbackCommand;
95
92
  };
96
93
 
97
- const getVersionCommands = () => {
98
- if (process.env[VERSION_CHECK_ENABLED_ENV_VAR] !== '1') {
99
- return null;
100
- }
101
-
102
- const codexCommand = getAbsoluteCommandPath(CODEX_BIN_ENV_VAR);
103
- const npmCommand = getAbsoluteCommandPath(NPM_BIN_ENV_VAR);
104
- if (!codexCommand || !npmCommand) {
105
- return null;
106
- }
107
-
108
- return {
109
- codexCommand,
110
- npmCommand,
111
- };
112
- };
94
+ const getVersionCommands = () => ({
95
+ codexCommand: getConfiguredCommand(CODEX_BIN_ENV_VAR, 'codex'),
96
+ npmCommand: getConfiguredCommand(NPM_BIN_ENV_VAR, 'npm'),
97
+ });
113
98
 
114
99
  const getCodexVersion = async (codexCommand) => {
115
100
  const output = await runCommand(codexCommand, ['--version']);
@@ -157,14 +142,6 @@ const compareVersions = (left, right) => {
157
142
 
158
143
  const getCodexUpdateStatus = async () => {
159
144
  const commands = getVersionCommands();
160
- if (!commands) {
161
- return {
162
- installed: VERSION_DISABLED_LABEL,
163
- latest: 'unknown',
164
- status: '',
165
- };
166
- }
167
-
168
145
  const installedLabel = await getCodexVersion(commands.codexCommand);
169
146
  const installed = normalizeVersion(installedLabel);
170
147
 
@@ -785,7 +762,11 @@ const App = () => {
785
762
  return React.createElement(
786
763
  Box,
787
764
  { flexDirection: 'column', padding: 1 },
788
- React.createElement(Header, { codexVersion, codexVersionStatus }),
765
+ React.createElement(Header, {
766
+ codexVersion,
767
+ codexVersionStatus,
768
+ packageVersion: PACKAGE_VERSION,
769
+ }),
789
770
  React.createElement(ConfigNavigator, {
790
771
  snapshot,
791
772
  pathSegments,
@@ -803,7 +784,11 @@ const App = () => {
803
784
  return React.createElement(
804
785
  Box,
805
786
  { flexDirection: 'column', padding: 1 },
806
- React.createElement(Header, { codexVersion, codexVersionStatus }),
787
+ React.createElement(Header, {
788
+ codexVersion,
789
+ codexVersionStatus,
790
+ packageVersion: PACKAGE_VERSION,
791
+ }),
807
792
  React.createElement(ConfigNavigator, {
808
793
  snapshot,
809
794
  pathSegments,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-configurator",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "TOML-aware Ink TUI for Codex Configurator",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -10,7 +10,7 @@ const WORDMARK = [
10
10
  ' ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝',
11
11
  ];
12
12
 
13
- export const Header = ({ codexVersion, codexVersionStatus }) =>
13
+ export const Header = ({ codexVersion, codexVersionStatus, packageVersion }) =>
14
14
  React.createElement(
15
15
  Box,
16
16
  {
@@ -20,14 +20,34 @@ export const Header = ({ codexVersion, codexVersionStatus }) =>
20
20
  },
21
21
  React.createElement(
22
22
  Box,
23
- { flexDirection: 'column', marginBottom: 1, gap: 0 },
24
- ...WORDMARK.map((line, index) =>
25
- React.createElement(Text, { color: 'magentaBright', bold: true, key: `word-${index}` }, line)
26
- )
27
- ),
23
+ { flexDirection: 'row', marginBottom: 1, gap: 0, alignItems: 'flex-end' },
24
+ React.createElement(
25
+ Box,
26
+ { flexDirection: 'column' },
27
+ ...WORDMARK.map((line, index) =>
28
+ React.createElement(Text, { color: 'magentaBright', bold: true, key: `word-${index}` }, line)
29
+ )
30
+ ),
31
+ React.createElement(
32
+ Box,
33
+ { marginLeft: 1 },
34
+ React.createElement(Text, { color: 'gray', bold: true }, `v${packageVersion || 'unknown'}`)
35
+ )
36
+ ),
28
37
  React.createElement(
29
- Text,
30
- { color: 'gray' },
31
- codexVersionStatus ? `Codex ${codexVersion} (${codexVersionStatus})` : `Codex ${codexVersion}`
38
+ Box,
39
+ { flexDirection: 'row' },
40
+ React.createElement(Text, { color: 'gray' }, `Codex ${codexVersion}`),
41
+ codexVersionStatus
42
+ ? codexVersionStatus === 'up to date'
43
+ ? React.createElement(
44
+ React.Fragment,
45
+ null,
46
+ React.createElement(Text, { color: 'gray' }, ' ('),
47
+ React.createElement(Text, { color: 'green' }, '✓'),
48
+ React.createElement(Text, { color: 'gray' }, ` ${codexVersionStatus})`)
49
+ )
50
+ : React.createElement(Text, { color: 'gray' }, ` (${codexVersionStatus})`)
51
+ : null
32
52
  )
33
53
  );
@@ -171,6 +171,32 @@ export const readConfig = (configPath = CONFIG_PATH) => {
171
171
  };
172
172
 
173
173
  const normalizeFilePath = (outputPath) => outputPath || CONFIG_PATH;
174
+ const EMPTY_PATH_TABLE_MARKER_KEY = '__codex_configurator_empty_path_table_marker__';
175
+
176
+ const cloneForTomlWrite = (value, pathSegments = []) => {
177
+ if (Array.isArray(value)) {
178
+ return value.map((item, index) => cloneForTomlWrite(item, [...pathSegments, String(index)]));
179
+ }
180
+
181
+ if (!isPlainObject(value)) {
182
+ return value;
183
+ }
184
+
185
+ const customPlaceholder = getReferenceCustomIdPlaceholder(pathSegments);
186
+ const clonedEntries = Object.entries(value).map(([key, child]) => {
187
+ if (
188
+ customPlaceholder === '<path>' &&
189
+ isPlainObject(child) &&
190
+ Object.keys(child).length === 0
191
+ ) {
192
+ return [key, { [EMPTY_PATH_TABLE_MARKER_KEY]: undefined }];
193
+ }
194
+
195
+ return [key, cloneForTomlWrite(child, [...pathSegments, key])];
196
+ });
197
+
198
+ return Object.fromEntries(clonedEntries);
199
+ };
174
200
 
175
201
  export const writeConfig = (data, outputPath = CONFIG_PATH) => {
176
202
  const targetPath = normalizeFilePath(outputPath);
@@ -186,7 +212,7 @@ export const writeConfig = (data, outputPath = CONFIG_PATH) => {
186
212
  fs.mkdirSync(directoryPath, { recursive: true, mode: 0o700 });
187
213
  }
188
214
 
189
- const payload = stringify(data);
215
+ const payload = stringify(cloneForTomlWrite(data));
190
216
  let tempFd = null;
191
217
 
192
218
  try {