codex-webstrapper 0.1.1 → 0.1.5
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 +29 -4
- package/bin/codex-webstrap.sh +100 -15
- package/package.json +3 -2
- package/src/assets.mjs +2 -1
- package/src/message-router.mjs +179 -2
- package/src/server.mjs +12 -0
package/README.md
CHANGED
|
@@ -75,6 +75,8 @@ This project provides near-parity by emulation/bridging, not by removing those d
|
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
77
|
codex-webstrap [--port <n>] [--bind <ip>] [--open] [--token-file <path>] [--codex-app <path>]
|
|
78
|
+
codex-webstrapper [--port <n>] [--bind <ip>] [--open] [--token-file <path>] [--codex-app <path>]
|
|
79
|
+
codex-webstrapper open [--port <n>] [--bind <ip>] [--token-file <path>] [--copy]
|
|
78
80
|
```
|
|
79
81
|
|
|
80
82
|
### Environment Overrides
|
|
@@ -112,7 +114,12 @@ codex-webstrap [--port <n>] [--bind <ip>] [--open] [--token-file <path>] [--code
|
|
|
112
114
|
- macOS
|
|
113
115
|
- Node.js 20+
|
|
114
116
|
- Installed Codex app bundle at `/Applications/Codex.app` (or pass `--codex-app`)
|
|
115
|
-
|
|
117
|
+
|
|
118
|
+
By default, webstrapper runs the app-server via the bundled desktop CLI at:
|
|
119
|
+
- `/Applications/Codex.app/Contents/Resources/codex`
|
|
120
|
+
|
|
121
|
+
Optional override:
|
|
122
|
+
- `CODEX_CLI_PATH=/custom/codex`
|
|
116
123
|
|
|
117
124
|
### Install
|
|
118
125
|
|
|
@@ -131,7 +138,7 @@ npm install -g codex-webstrapper
|
|
|
131
138
|
With global install:
|
|
132
139
|
|
|
133
140
|
```bash
|
|
134
|
-
codex-
|
|
141
|
+
codex-webstrapper --port 8080 --bind 127.0.0.1
|
|
135
142
|
```
|
|
136
143
|
|
|
137
144
|
From local checkout:
|
|
@@ -143,7 +150,19 @@ From local checkout:
|
|
|
143
150
|
Optional auto-open:
|
|
144
151
|
|
|
145
152
|
```bash
|
|
146
|
-
codex-
|
|
153
|
+
codex-webstrapper --open
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Generate/open the full auth URL from your persisted token:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
codex-webstrapper open
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Copy the full auth URL (including token) to macOS clipboard:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
codex-webstrapper open --copy
|
|
147
166
|
```
|
|
148
167
|
|
|
149
168
|
## Authentication Model
|
|
@@ -155,6 +174,12 @@ codex-webstrap --open
|
|
|
155
174
|
open "http://127.0.0.1:8080/__webstrapper/auth?token=$(cat ~/.codex-webstrap/token)"
|
|
156
175
|
```
|
|
157
176
|
|
|
177
|
+
Or use the helper command:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
codex-webstrapper open
|
|
181
|
+
```
|
|
182
|
+
|
|
158
183
|
3. Server sets `cw_session` cookie (`HttpOnly`, `SameSite=Lax`, scoped to `/`).
|
|
159
184
|
4. UI and bridge endpoints require a valid session cookie.
|
|
160
185
|
|
|
@@ -232,7 +257,7 @@ Codex setup-script compatible command:
|
|
|
232
257
|
- Codex app not found
|
|
233
258
|
- Pass `--codex-app /path/to/Codex.app`.
|
|
234
259
|
- `codex` CLI spawn failures
|
|
235
|
-
- Ensure
|
|
260
|
+
- Ensure the bundled CLI exists in your Codex app install, or set `CODEX_CLI_PATH`.
|
|
236
261
|
|
|
237
262
|
## License
|
|
238
263
|
|
package/bin/codex-webstrap.sh
CHANGED
|
@@ -17,40 +17,89 @@ OPEN_FLAG="0"
|
|
|
17
17
|
TOKEN_FILE="${CODEX_WEBSTRAP_TOKEN_FILE:-}"
|
|
18
18
|
CODEX_APP="${CODEX_WEBSTRAP_CODEX_APP:-}"
|
|
19
19
|
INTERNAL_WS_PORT="${CODEX_WEBSTRAP_INTERNAL_WS_PORT:-38080}"
|
|
20
|
+
COPY_FLAG="0"
|
|
21
|
+
COMMAND="serve"
|
|
22
|
+
|
|
23
|
+
DEFAULT_TOKEN_FILE="${HOME}/.codex-webstrap/token"
|
|
24
|
+
if [[ -z "$TOKEN_FILE" ]]; then
|
|
25
|
+
TOKEN_FILE="$DEFAULT_TOKEN_FILE"
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
print_usage() {
|
|
29
|
+
cat <<USAGE
|
|
30
|
+
Usage:
|
|
31
|
+
$(basename "$0") [--port <n>] [--bind <ip>] [--open] [--token-file <path>] [--codex-app <path>]
|
|
32
|
+
$(basename "$0") open [--port <n>] [--bind <ip>] [--token-file <path>] [--copy]
|
|
33
|
+
|
|
34
|
+
Commands:
|
|
35
|
+
open Build the full auth URL and open it in the browser.
|
|
36
|
+
|
|
37
|
+
Options for open:
|
|
38
|
+
--copy Copy full auth URL to clipboard with pbcopy instead of launching browser.
|
|
39
|
+
|
|
40
|
+
Env overrides:
|
|
41
|
+
CODEX_WEBSTRAP_PORT
|
|
42
|
+
CODEX_WEBSTRAP_BIND
|
|
43
|
+
CODEX_WEBSTRAP_TOKEN_FILE
|
|
44
|
+
CODEX_WEBSTRAP_CODEX_APP
|
|
45
|
+
CODEX_WEBSTRAP_INTERNAL_WS_PORT
|
|
46
|
+
USAGE
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
require_value() {
|
|
50
|
+
local flag="$1"
|
|
51
|
+
local value="${2:-}"
|
|
52
|
+
if [[ -z "$value" ]]; then
|
|
53
|
+
echo "Missing value for ${flag}" >&2
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if [[ $# -gt 0 ]]; then
|
|
59
|
+
case "$1" in
|
|
60
|
+
open)
|
|
61
|
+
COMMAND="open"
|
|
62
|
+
shift
|
|
63
|
+
;;
|
|
64
|
+
--help|-h|help)
|
|
65
|
+
print_usage
|
|
66
|
+
exit 0
|
|
67
|
+
;;
|
|
68
|
+
esac
|
|
69
|
+
fi
|
|
20
70
|
|
|
21
71
|
while [[ $# -gt 0 ]]; do
|
|
22
72
|
case "$1" in
|
|
23
73
|
--port)
|
|
74
|
+
require_value "$1" "${2:-}"
|
|
24
75
|
PORT="$2"
|
|
25
76
|
shift 2
|
|
26
77
|
;;
|
|
27
78
|
--bind)
|
|
79
|
+
require_value "$1" "${2:-}"
|
|
28
80
|
BIND="$2"
|
|
29
81
|
shift 2
|
|
30
82
|
;;
|
|
31
|
-
--open)
|
|
32
|
-
OPEN_FLAG="1"
|
|
33
|
-
shift
|
|
34
|
-
;;
|
|
35
83
|
--token-file)
|
|
84
|
+
require_value "$1" "${2:-}"
|
|
36
85
|
TOKEN_FILE="$2"
|
|
37
86
|
shift 2
|
|
38
87
|
;;
|
|
88
|
+
--copy)
|
|
89
|
+
COPY_FLAG="1"
|
|
90
|
+
shift
|
|
91
|
+
;;
|
|
92
|
+
--open)
|
|
93
|
+
OPEN_FLAG="1"
|
|
94
|
+
shift
|
|
95
|
+
;;
|
|
39
96
|
--codex-app)
|
|
97
|
+
require_value "$1" "${2:-}"
|
|
40
98
|
CODEX_APP="$2"
|
|
41
99
|
shift 2
|
|
42
100
|
;;
|
|
43
|
-
--help|-h)
|
|
44
|
-
|
|
45
|
-
Usage: $(basename "$0") [--port <n>] [--bind <ip>] [--open] [--token-file <path>] [--codex-app <path>]
|
|
46
|
-
|
|
47
|
-
Env overrides:
|
|
48
|
-
CODEX_WEBSTRAP_PORT
|
|
49
|
-
CODEX_WEBSTRAP_BIND
|
|
50
|
-
CODEX_WEBSTRAP_TOKEN_FILE
|
|
51
|
-
CODEX_WEBSTRAP_CODEX_APP
|
|
52
|
-
CODEX_WEBSTRAP_INTERNAL_WS_PORT
|
|
53
|
-
USAGE
|
|
101
|
+
--help|-h|help)
|
|
102
|
+
print_usage
|
|
54
103
|
exit 0
|
|
55
104
|
;;
|
|
56
105
|
*)
|
|
@@ -60,6 +109,42 @@ USAGE
|
|
|
60
109
|
esac
|
|
61
110
|
done
|
|
62
111
|
|
|
112
|
+
if [[ "$COMMAND" != "open" && "$COPY_FLAG" == "1" ]]; then
|
|
113
|
+
echo "--copy can only be used with the 'open' command" >&2
|
|
114
|
+
exit 1
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
if [[ "$COMMAND" == "open" ]]; then
|
|
118
|
+
if [[ ! -f "$TOKEN_FILE" ]]; then
|
|
119
|
+
echo "Token file not found: $TOKEN_FILE" >&2
|
|
120
|
+
exit 1
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
TOKEN="$(tr -d '\r\n' < "$TOKEN_FILE")"
|
|
124
|
+
if [[ -z "$TOKEN" ]]; then
|
|
125
|
+
echo "Token file is empty: $TOKEN_FILE" >&2
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
ENCODED_TOKEN="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1] || ""))' "$TOKEN")"
|
|
130
|
+
AUTH_URL="http://${BIND}:${PORT}/__webstrapper/auth?token=${ENCODED_TOKEN}"
|
|
131
|
+
|
|
132
|
+
if [[ "$COPY_FLAG" == "1" ]]; then
|
|
133
|
+
if ! command -v pbcopy >/dev/null 2>&1; then
|
|
134
|
+
echo "pbcopy not found in PATH" >&2
|
|
135
|
+
exit 1
|
|
136
|
+
fi
|
|
137
|
+
printf '%s' "$AUTH_URL" | pbcopy
|
|
138
|
+
printf 'Copied auth URL to clipboard.\n'
|
|
139
|
+
printf '%s\n' "$AUTH_URL"
|
|
140
|
+
else
|
|
141
|
+
open "$AUTH_URL"
|
|
142
|
+
printf 'Opened auth URL in browser.\n'
|
|
143
|
+
printf '%s\n' "$AUTH_URL"
|
|
144
|
+
fi
|
|
145
|
+
exit 0
|
|
146
|
+
fi
|
|
147
|
+
|
|
63
148
|
export CODEX_WEBSTRAP_PORT="$PORT"
|
|
64
149
|
export CODEX_WEBSTRAP_BIND="$BIND"
|
|
65
150
|
export CODEX_WEBSTRAP_TOKEN_FILE="$TOKEN_FILE"
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-webstrapper",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Web wrapper for Codex desktop assets with bridge + token auth",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
8
|
-
"codex-webstrap": "./bin/codex-webstrap.sh"
|
|
8
|
+
"codex-webstrap": "./bin/codex-webstrap.sh",
|
|
9
|
+
"codex-webstrapper": "./bin/codex-webstrap.sh"
|
|
9
10
|
},
|
|
10
11
|
"files": [
|
|
11
12
|
"bin/",
|
package/src/assets.mjs
CHANGED
|
@@ -37,7 +37,8 @@ export function resolveCodexAppPaths(explicitCodexAppPath) {
|
|
|
37
37
|
const resourcesPath = path.join(appPath, "Contents", "Resources");
|
|
38
38
|
const asarPath = path.join(resourcesPath, "app.asar");
|
|
39
39
|
const infoPlistPath = path.join(appPath, "Contents", "Info.plist");
|
|
40
|
-
|
|
40
|
+
const codexCliPath = path.join(resourcesPath, "codex");
|
|
41
|
+
return { appPath, resourcesPath, asarPath, infoPlistPath, codexCliPath };
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
export async function ensureCodexAppExists(paths) {
|
package/src/message-router.mjs
CHANGED
|
@@ -880,6 +880,7 @@ export class MessageRouter {
|
|
|
880
880
|
}
|
|
881
881
|
|
|
882
882
|
let payload = {};
|
|
883
|
+
let status = 200;
|
|
883
884
|
switch (endpoint) {
|
|
884
885
|
case "get-global-state": {
|
|
885
886
|
const key = params?.key;
|
|
@@ -1018,7 +1019,7 @@ export class MessageRouter {
|
|
|
1018
1019
|
};
|
|
1019
1020
|
break;
|
|
1020
1021
|
case "local-environments":
|
|
1021
|
-
payload =
|
|
1022
|
+
payload = await this._resolveLocalEnvironments(params);
|
|
1022
1023
|
break;
|
|
1023
1024
|
case "has-custom-cli-executable":
|
|
1024
1025
|
payload = { hasCustomCliExecutable: false };
|
|
@@ -1050,6 +1051,11 @@ export class MessageRouter {
|
|
|
1050
1051
|
};
|
|
1051
1052
|
break;
|
|
1052
1053
|
}
|
|
1054
|
+
case "git-push": {
|
|
1055
|
+
payload = await this._handleGitPush(params);
|
|
1056
|
+
status = payload.ok ? 200 : 500;
|
|
1057
|
+
break;
|
|
1058
|
+
}
|
|
1053
1059
|
case "git-merge-base": {
|
|
1054
1060
|
const gitRoot = typeof params?.gitRoot === "string" && params.gitRoot.length > 0
|
|
1055
1061
|
? params.gitRoot
|
|
@@ -1115,7 +1121,7 @@ export class MessageRouter {
|
|
|
1115
1121
|
this._sendFetchJson(ws, {
|
|
1116
1122
|
requestId,
|
|
1117
1123
|
url: message.url,
|
|
1118
|
-
status
|
|
1124
|
+
status,
|
|
1119
1125
|
payload
|
|
1120
1126
|
});
|
|
1121
1127
|
return true;
|
|
@@ -1333,6 +1339,55 @@ export class MessageRouter {
|
|
|
1333
1339
|
};
|
|
1334
1340
|
}
|
|
1335
1341
|
|
|
1342
|
+
async _handleGitPush(params) {
|
|
1343
|
+
const cwd = typeof params?.cwd === "string" && params.cwd.length > 0
|
|
1344
|
+
? params.cwd
|
|
1345
|
+
: process.cwd();
|
|
1346
|
+
const remote = typeof params?.remote === "string" && params.remote.trim().length > 0
|
|
1347
|
+
? params.remote.trim()
|
|
1348
|
+
: null;
|
|
1349
|
+
const branch = typeof params?.branch === "string" && params.branch.trim().length > 0
|
|
1350
|
+
? params.branch.trim()
|
|
1351
|
+
: null;
|
|
1352
|
+
|
|
1353
|
+
const args = ["-C", cwd, "push"];
|
|
1354
|
+
if (params?.force === true || params?.forceWithLease === true) {
|
|
1355
|
+
args.push("--force-with-lease");
|
|
1356
|
+
}
|
|
1357
|
+
if (params?.setUpstream === true) {
|
|
1358
|
+
args.push("--set-upstream");
|
|
1359
|
+
}
|
|
1360
|
+
if (remote) {
|
|
1361
|
+
args.push(remote);
|
|
1362
|
+
}
|
|
1363
|
+
if (branch) {
|
|
1364
|
+
args.push(branch);
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
const result = await this._runCommand("git", args, {
|
|
1368
|
+
cwd,
|
|
1369
|
+
timeoutMs: 120_000,
|
|
1370
|
+
allowNonZero: true
|
|
1371
|
+
});
|
|
1372
|
+
|
|
1373
|
+
if (result.ok) {
|
|
1374
|
+
return {
|
|
1375
|
+
ok: true,
|
|
1376
|
+
code: result.code,
|
|
1377
|
+
stdout: result.stdout || "",
|
|
1378
|
+
stderr: result.stderr || ""
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
return {
|
|
1383
|
+
ok: false,
|
|
1384
|
+
code: result.code,
|
|
1385
|
+
error: result.error || result.stderr || "git push failed",
|
|
1386
|
+
stdout: result.stdout || "",
|
|
1387
|
+
stderr: result.stderr || ""
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1336
1391
|
async _runCommand(command, args, { timeoutMs = 5_000, allowNonZero = false, cwd = process.cwd() } = {}) {
|
|
1337
1392
|
return new Promise((resolve) => {
|
|
1338
1393
|
const child = spawn(command, args, {
|
|
@@ -1471,6 +1526,128 @@ export class MessageRouter {
|
|
|
1471
1526
|
return trimmed.replace(/\/+$/, "");
|
|
1472
1527
|
}
|
|
1473
1528
|
|
|
1529
|
+
async _resolveLocalEnvironments(params) {
|
|
1530
|
+
const workspaceRoot = this._resolveLocalEnvironmentWorkspaceRoot(params?.workspaceRoot);
|
|
1531
|
+
if (!workspaceRoot) {
|
|
1532
|
+
return { environments: [] };
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
const configDir = path.join(workspaceRoot, ".codex", "environments");
|
|
1536
|
+
let entries;
|
|
1537
|
+
try {
|
|
1538
|
+
entries = await fs.readdir(configDir, { withFileTypes: true });
|
|
1539
|
+
} catch {
|
|
1540
|
+
return { environments: [] };
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
const configFiles = entries
|
|
1544
|
+
.filter((entry) => entry.isFile() && /^environment(?:-\d+)?\.toml$/i.test(entry.name))
|
|
1545
|
+
.map((entry) => entry.name)
|
|
1546
|
+
.sort((left, right) => {
|
|
1547
|
+
const leftIsDefault = left.toLowerCase() === "environment.toml";
|
|
1548
|
+
const rightIsDefault = right.toLowerCase() === "environment.toml";
|
|
1549
|
+
if (leftIsDefault && !rightIsDefault) {
|
|
1550
|
+
return -1;
|
|
1551
|
+
}
|
|
1552
|
+
if (!leftIsDefault && rightIsDefault) {
|
|
1553
|
+
return 1;
|
|
1554
|
+
}
|
|
1555
|
+
return left.localeCompare(right, undefined, { numeric: true, sensitivity: "base" });
|
|
1556
|
+
});
|
|
1557
|
+
|
|
1558
|
+
if (configFiles.length === 0) {
|
|
1559
|
+
return { environments: [] };
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
const environments = await Promise.all(configFiles.map(async (fileName) => {
|
|
1563
|
+
const configPath = path.join(configDir, fileName);
|
|
1564
|
+
try {
|
|
1565
|
+
const raw = await fs.readFile(configPath, "utf8");
|
|
1566
|
+
const environment = this._parseLocalEnvironmentConfig(raw, configPath);
|
|
1567
|
+
return {
|
|
1568
|
+
type: "success",
|
|
1569
|
+
configPath,
|
|
1570
|
+
environment
|
|
1571
|
+
};
|
|
1572
|
+
} catch (error) {
|
|
1573
|
+
return {
|
|
1574
|
+
type: "error",
|
|
1575
|
+
configPath,
|
|
1576
|
+
error: {
|
|
1577
|
+
message: toErrorMessage(error)
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
}));
|
|
1582
|
+
|
|
1583
|
+
return { environments };
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
_resolveLocalEnvironmentWorkspaceRoot(root) {
|
|
1587
|
+
const normalized = this._normalizeWorkspaceRoot(root);
|
|
1588
|
+
if (normalized) {
|
|
1589
|
+
return path.resolve(normalized);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
const activeRoot = this._normalizeWorkspaceRoot(this.activeWorkspaceRoots?.[0]);
|
|
1593
|
+
if (activeRoot) {
|
|
1594
|
+
return path.resolve(activeRoot);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
return this.defaultWorkspaceRoot ? path.resolve(this.defaultWorkspaceRoot) : null;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
_parseLocalEnvironmentConfig(raw, configPath) {
|
|
1601
|
+
const name = this._parseTomlString(raw, "name") || path.basename(configPath, ".toml");
|
|
1602
|
+
const versionRaw = this._parseTomlNumber(raw, "version");
|
|
1603
|
+
const setupScript = this._parseTomlStringInSection(raw, "setup", "script") || "";
|
|
1604
|
+
|
|
1605
|
+
return {
|
|
1606
|
+
version: Number.isInteger(versionRaw) ? versionRaw : 1,
|
|
1607
|
+
name,
|
|
1608
|
+
setup: {
|
|
1609
|
+
script: setupScript
|
|
1610
|
+
},
|
|
1611
|
+
actions: []
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
_parseTomlString(raw, key) {
|
|
1616
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1617
|
+
const pattern = new RegExp(`^\\s*${escapedKey}\\s*=\\s*(?:"([^"]*)"|'([^']*)')\\s*$`, "m");
|
|
1618
|
+
const match = raw.match(pattern);
|
|
1619
|
+
if (!match) {
|
|
1620
|
+
return null;
|
|
1621
|
+
}
|
|
1622
|
+
return match[1] ?? match[2] ?? null;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
_parseTomlNumber(raw, key) {
|
|
1626
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1627
|
+
const pattern = new RegExp(`^\\s*${escapedKey}\\s*=\\s*(-?\\d+)\\s*$`, "m");
|
|
1628
|
+
const match = raw.match(pattern);
|
|
1629
|
+
if (!match) {
|
|
1630
|
+
return null;
|
|
1631
|
+
}
|
|
1632
|
+
const value = Number.parseInt(match[1], 10);
|
|
1633
|
+
return Number.isNaN(value) ? null : value;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
_parseTomlStringInSection(raw, sectionName, key) {
|
|
1637
|
+
const escapedSection = sectionName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1638
|
+
const sectionPattern = new RegExp(`^\\s*\\[${escapedSection}\\]\\s*$`, "m");
|
|
1639
|
+
const sectionMatch = sectionPattern.exec(raw);
|
|
1640
|
+
if (!sectionMatch) {
|
|
1641
|
+
return null;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
const sectionStart = sectionMatch.index + sectionMatch[0].length;
|
|
1645
|
+
const rest = raw.slice(sectionStart);
|
|
1646
|
+
const nextSectionMatch = rest.match(/^\s*\[[^\]]+\]\s*$/m);
|
|
1647
|
+
const sectionBody = nextSectionMatch ? rest.slice(0, nextSectionMatch.index) : rest;
|
|
1648
|
+
return this._parseTomlString(sectionBody, key);
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1474
1651
|
_loadPersistedGlobalState() {
|
|
1475
1652
|
if (!this.globalStatePath) {
|
|
1476
1653
|
return;
|
package/src/server.mjs
CHANGED
|
@@ -148,8 +148,20 @@ async function main() {
|
|
|
148
148
|
});
|
|
149
149
|
const patchedIndexHtml = await buildPatchedIndexHtml(assetBundle.indexPath);
|
|
150
150
|
|
|
151
|
+
let codexCliPath = process.env.CODEX_CLI_PATH || codexPaths.codexCliPath;
|
|
152
|
+
if (!process.env.CODEX_CLI_PATH) {
|
|
153
|
+
const bundledCliExists = await fs.access(codexPaths.codexCliPath).then(() => true).catch(() => false);
|
|
154
|
+
if (!bundledCliExists) {
|
|
155
|
+
codexCliPath = "codex";
|
|
156
|
+
logger.warn("Bundled Codex CLI not found, falling back to PATH", {
|
|
157
|
+
bundledCliPath: codexPaths.codexCliPath
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
151
162
|
const appServer = new AppServerManager({
|
|
152
163
|
internalPort: config.internalWsPort,
|
|
164
|
+
codexCliPath,
|
|
153
165
|
logger: createLogger("app-server")
|
|
154
166
|
});
|
|
155
167
|
|