oh-my-codex 0.18.14 → 0.18.16
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/Cargo.lock +6 -6
- package/Cargo.toml +1 -1
- package/README.md +10 -7
- package/dist/agents/__tests__/native-config.test.js +37 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +10 -1
- package/dist/agents/native-config.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js +3 -0
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-artifact-ownership.test.js +63 -0
- package/dist/cli/__tests__/doctor-artifact-ownership.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.js +33 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js +131 -2
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +40 -0
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts +2 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts.map +1 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +64 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -0
- package/dist/cli/__tests__/session-search-help.test.js +3 -1
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/session-search.test.js +55 -1
- package/dist/cli/__tests__/session-search.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +35 -0
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +80 -2
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/__tests__/url.test.d.ts +2 -0
- package/dist/cli/__tests__/url.test.d.ts.map +1 -0
- package/dist/cli/__tests__/url.test.js +82 -0
- package/dist/cli/__tests__/url.test.js.map +1 -0
- package/dist/cli/autoresearch-intake.d.ts.map +1 -1
- package/dist/cli/autoresearch-intake.js +2 -0
- package/dist/cli/autoresearch-intake.js.map +1 -1
- package/dist/cli/doctor.d.ts +17 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +203 -38
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +7 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +21 -62
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/session-search.d.ts +6 -0
- package/dist/cli/session-search.d.ts.map +1 -1
- package/dist/cli/session-search.js +104 -4
- package/dist/cli/session-search.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +20 -4
- package/dist/cli/update.js.map +1 -1
- package/dist/cli/url.d.ts +8 -0
- package/dist/cli/url.d.ts.map +1 -0
- package/dist/cli/url.js +69 -0
- package/dist/cli/url.js.map +1 -0
- package/dist/config/__tests__/codex-hooks.test.js +20 -4
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +1 -4
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +363 -0
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +121 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +33 -2
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/types.d.ts +1 -1
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +24 -1
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +53 -0
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +2 -0
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +32 -6
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +11 -5
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/notifications/__tests__/http-client.test.js +9 -0
- package/dist/notifications/__tests__/http-client.test.js.map +1 -1
- package/dist/notifications/http-client.js +1 -1
- package/dist/notifications/http-client.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +600 -7
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +278 -8
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts +1 -0
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +279 -86
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/session-history/__tests__/friction.test.d.ts +2 -0
- package/dist/session-history/__tests__/friction.test.d.ts.map +1 -0
- package/dist/session-history/__tests__/friction.test.js +107 -0
- package/dist/session-history/__tests__/friction.test.js.map +1 -0
- package/dist/session-history/friction.d.ts +68 -0
- package/dist/session-history/friction.d.ts.map +1 -0
- package/dist/session-history/friction.js +365 -0
- package/dist/session-history/friction.js.map +1 -0
- package/dist/sidecar/__tests__/args.test.d.ts +2 -0
- package/dist/sidecar/__tests__/args.test.d.ts.map +1 -0
- package/dist/sidecar/__tests__/args.test.js +18 -0
- package/dist/sidecar/__tests__/args.test.js.map +1 -0
- package/dist/sidecar/index.d.ts.map +1 -1
- package/dist/sidecar/index.js +12 -4
- package/dist/sidecar/index.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +150 -0
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +8 -0
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +108 -8
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/url-reader/__tests__/url-reader.test.d.ts +2 -0
- package/dist/url-reader/__tests__/url-reader.test.d.ts.map +1 -0
- package/dist/url-reader/__tests__/url-reader.test.js +433 -0
- package/dist/url-reader/__tests__/url-reader.test.js.map +1 -0
- package/dist/url-reader/index.d.ts +4 -0
- package/dist/url-reader/index.d.ts.map +1 -0
- package/dist/url-reader/index.js +522 -0
- package/dist/url-reader/index.js.map +1 -0
- package/dist/url-reader/types.d.ts +41 -0
- package/dist/url-reader/types.d.ts.map +1 -0
- package/dist/url-reader/types.js +2 -0
- package/dist/url-reader/types.js.map +1 -0
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +3 -0
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/utils/platform-command.d.ts +7 -1
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +12 -1
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/vscode/__tests__/index.test.d.ts +2 -0
- package/dist/vscode/__tests__/index.test.d.ts.map +1 -0
- package/dist/vscode/__tests__/index.test.js +236 -0
- package/dist/vscode/__tests__/index.test.js.map +1 -0
- package/dist/vscode/index.d.ts +76 -0
- package/dist/vscode/index.d.ts.map +1 -0
- package/dist/vscode/index.js +196 -0
- package/dist/vscode/index.js.map +1 -0
- package/package.json +2 -2
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/src/scripts/__tests__/codex-native-hook.test.ts +692 -24
- package/src/scripts/codex-native-hook.ts +313 -7
- package/src/scripts/notify-hook/team-leader-nudge.ts +298 -91
package/dist/cli/doctor.js
CHANGED
|
@@ -13,7 +13,7 @@ import { parse as parseToml } from "@iarna/toml";
|
|
|
13
13
|
import { getBuiltinExploreHarnessUnsupportedReason, resolvePackagedExploreHarnessCommand, EXPLORE_BIN_ENV, } from "./explore.js";
|
|
14
14
|
import { getPackageRoot } from "../utils/package.js";
|
|
15
15
|
import { hasLegacyOmxTeamRunTable, getModelContextRecommendation, } from "../config/generator.js";
|
|
16
|
-
import { MANAGED_HOOK_EVENTS, buildManagedCodexNativeHookCommand, discoverCodexHookConfigPaths, getManagedCodexHookCommandsForEvent, getMissingManagedCodexHookEvents, hasCodexHooksJsonTopLevelState, } from "../config/codex-hooks.js";
|
|
16
|
+
import { MANAGED_HOOK_EVENTS, buildManagedCodexNativeHookCommand, discoverCodexHookConfigPaths, resolveWindowsPowerShellPath, getManagedCodexHookCommandsForEvent, getMissingManagedCodexHookEvents, hasCodexHooksJsonTopLevelState, } from "../config/codex-hooks.js";
|
|
17
17
|
import { OMX_FIRST_PARTY_MCP_SERVER_NAMES } from "../config/omx-first-party-mcp.js";
|
|
18
18
|
import { getDefaultBridge, isBridgeEnabled } from "../runtime/bridge.js";
|
|
19
19
|
import { OMX_EXPLORE_CMD_ENV, isExploreCommandRoutingEnabled, } from "../hooks/explore-routing.js";
|
|
@@ -163,7 +163,10 @@ export async function doctor(options = {}) {
|
|
|
163
163
|
// Check 2: Node.js version
|
|
164
164
|
checks.push(checkNodeVersion());
|
|
165
165
|
// Check 2.5: Explore harness readiness
|
|
166
|
-
|
|
166
|
+
const exploreRoutingState = await resolveExploreRoutingState(paths.configPath);
|
|
167
|
+
checks.push(checkExploreHarness(process.platform, process.env, {
|
|
168
|
+
exploreRoutingEnabled: exploreRoutingState.enabled,
|
|
169
|
+
}));
|
|
167
170
|
// Check 3: Codex home directory
|
|
168
171
|
checks.push(checkDirectory("Codex home", paths.codexHomeDir));
|
|
169
172
|
// Check 4: Config file
|
|
@@ -187,9 +190,13 @@ export async function doctor(options = {}) {
|
|
|
187
190
|
if (runtimeMirrorCheck)
|
|
188
191
|
checks.push(runtimeMirrorCheck);
|
|
189
192
|
// Check 4.5: Explore routing default
|
|
190
|
-
checks.push(
|
|
193
|
+
checks.push(checkExploreRoutingFromState(exploreRoutingState));
|
|
191
194
|
// Check 4.6: Lore commit guard default
|
|
192
195
|
checks.push(await checkLoreCommitGuard(paths.configPath));
|
|
196
|
+
// Check 4.7: External process guards
|
|
197
|
+
const externalProcessGuardCheck = await checkExternalCodexProcessGuards();
|
|
198
|
+
if (externalProcessGuardCheck)
|
|
199
|
+
checks.push(externalProcessGuardCheck);
|
|
193
200
|
// Check 5: Prompts installed
|
|
194
201
|
checks.push(await checkPrompts(paths.promptsDir, scopeResolution.installMode));
|
|
195
202
|
// Check 6: Skills installed
|
|
@@ -582,7 +589,16 @@ function checkNodeVersion() {
|
|
|
582
589
|
message: `v${process.versions.node} (need >= 20)`,
|
|
583
590
|
};
|
|
584
591
|
}
|
|
585
|
-
export function checkExploreHarness(platform = process.platform, env = process.env) {
|
|
592
|
+
export function checkExploreHarness(platform = process.platform, env = process.env, options = {}) {
|
|
593
|
+
const override = env[EXPLORE_BIN_ENV]?.trim();
|
|
594
|
+
const exploreRoutingEnabled = options.exploreRoutingEnabled ?? isExploreCommandRoutingEnabled(env);
|
|
595
|
+
if (!override && !exploreRoutingEnabled) {
|
|
596
|
+
return {
|
|
597
|
+
name: "Explore Harness",
|
|
598
|
+
status: "pass",
|
|
599
|
+
message: "skipped: omx explore is hard-deprecated and explore routing is disabled by default; use omx sparkshell for shell-native read-only evidence",
|
|
600
|
+
};
|
|
601
|
+
}
|
|
586
602
|
const packageRoot = getPackageRoot();
|
|
587
603
|
const manifestPath = join(packageRoot, "crates", "omx-explore", "Cargo.toml");
|
|
588
604
|
if (!existsSync(manifestPath)) {
|
|
@@ -592,7 +608,6 @@ export function checkExploreHarness(platform = process.platform, env = process.e
|
|
|
592
608
|
message: "Rust harness sources not found in this install (omx explore unavailable until packaged or OMX_EXPLORE_BIN is set)",
|
|
593
609
|
};
|
|
594
610
|
}
|
|
595
|
-
const override = env[EXPLORE_BIN_ENV]?.trim();
|
|
596
611
|
if (override) {
|
|
597
612
|
const resolved = join(packageRoot, override);
|
|
598
613
|
if (existsSync(override) || existsSync(resolved)) {
|
|
@@ -683,6 +698,8 @@ function formatArtifactIssue(repoRoot, issue) {
|
|
|
683
698
|
return `${formatArtifactPath(repoRoot, issue.path)} (${issue.reason}${owner})`;
|
|
684
699
|
}
|
|
685
700
|
function shouldReportOwnerMismatch(uid, currentUid) {
|
|
701
|
+
if (currentUid === 0)
|
|
702
|
+
return false;
|
|
686
703
|
if (typeof uid !== "number")
|
|
687
704
|
return false;
|
|
688
705
|
if (uid === 0)
|
|
@@ -717,8 +734,10 @@ async function collectRepoArtifactOwnershipIssues(repoRoot, options = {}) {
|
|
|
717
734
|
const uid = typeof info.uid === "number" ? info.uid : undefined;
|
|
718
735
|
const gid = typeof info.gid === "number" ? info.gid : undefined;
|
|
719
736
|
let reason = null;
|
|
720
|
-
if (uid === 0)
|
|
721
|
-
|
|
737
|
+
if (uid === 0) {
|
|
738
|
+
if (currentUid !== 0)
|
|
739
|
+
reason = "root-owned";
|
|
740
|
+
}
|
|
722
741
|
else if (shouldReportOwnerMismatch(uid, currentUid))
|
|
723
742
|
reason = "owner-mismatch";
|
|
724
743
|
else {
|
|
@@ -921,10 +940,36 @@ async function checkModelContextRecommendation(configPath) {
|
|
|
921
940
|
return null;
|
|
922
941
|
}
|
|
923
942
|
}
|
|
924
|
-
async function
|
|
925
|
-
const envValue =
|
|
943
|
+
async function resolveExploreRoutingState(configPath, env = process.env) {
|
|
944
|
+
const envValue = env[OMX_EXPLORE_CMD_ENV];
|
|
926
945
|
if (typeof envValue === "string") {
|
|
927
|
-
|
|
946
|
+
return { source: "env", enabled: isExploreCommandRoutingEnabled(env) };
|
|
947
|
+
}
|
|
948
|
+
if (!existsSync(configPath)) {
|
|
949
|
+
return { source: "default", enabled: false, reason: "missing-config" };
|
|
950
|
+
}
|
|
951
|
+
try {
|
|
952
|
+
const content = await readFile(configPath, "utf-8");
|
|
953
|
+
const parsed = parseToml(content);
|
|
954
|
+
const configuredValue = parsed?.shell_environment_policy?.set?.USE_OMX_EXPLORE_CMD ??
|
|
955
|
+
parsed?.env?.USE_OMX_EXPLORE_CMD;
|
|
956
|
+
if (typeof configuredValue === "string") {
|
|
957
|
+
return {
|
|
958
|
+
source: "config",
|
|
959
|
+
enabled: isExploreCommandRoutingEnabled({
|
|
960
|
+
USE_OMX_EXPLORE_CMD: configuredValue,
|
|
961
|
+
}),
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
return { source: "default", enabled: false, reason: "unset" };
|
|
965
|
+
}
|
|
966
|
+
catch {
|
|
967
|
+
return { source: "unreadable", enabled: false };
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function checkExploreRoutingFromState(state) {
|
|
971
|
+
if (state.source === "env") {
|
|
972
|
+
if (state.enabled) {
|
|
928
973
|
return {
|
|
929
974
|
name: "Explore routing",
|
|
930
975
|
status: "warn",
|
|
@@ -937,47 +982,34 @@ async function checkExploreRouting(configPath) {
|
|
|
937
982
|
message: "deprecated compatibility routing disabled by environment override (recommended)",
|
|
938
983
|
};
|
|
939
984
|
}
|
|
940
|
-
if (
|
|
941
|
-
|
|
942
|
-
name: "Explore routing",
|
|
943
|
-
status: "pass",
|
|
944
|
-
message: "deprecated by default (config.toml not found yet)",
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
try {
|
|
948
|
-
const content = await readFile(configPath, "utf-8");
|
|
949
|
-
const parsed = parseToml(content);
|
|
950
|
-
const configuredValue = parsed?.shell_environment_policy?.set?.USE_OMX_EXPLORE_CMD ??
|
|
951
|
-
parsed?.env?.USE_OMX_EXPLORE_CMD;
|
|
952
|
-
if (typeof configuredValue === "string") {
|
|
953
|
-
if (isExploreCommandRoutingEnabled({
|
|
954
|
-
USE_OMX_EXPLORE_CMD: configuredValue,
|
|
955
|
-
})) {
|
|
956
|
-
return {
|
|
957
|
-
name: "Explore routing",
|
|
958
|
-
status: "warn",
|
|
959
|
-
message: 'deprecated compatibility routing enabled in config.toml; set USE_OMX_EXPLORE_CMD = "0" under [shell_environment_policy.set] and use normal Codex repo inspection or omx sparkshell instead',
|
|
960
|
-
};
|
|
961
|
-
}
|
|
985
|
+
if (state.source === "config") {
|
|
986
|
+
if (state.enabled) {
|
|
962
987
|
return {
|
|
963
988
|
name: "Explore routing",
|
|
964
|
-
status: "
|
|
965
|
-
message:
|
|
989
|
+
status: "warn",
|
|
990
|
+
message: 'deprecated compatibility routing enabled in config.toml; set USE_OMX_EXPLORE_CMD = "0" under [shell_environment_policy.set] and use normal Codex repo inspection or omx sparkshell instead',
|
|
966
991
|
};
|
|
967
992
|
}
|
|
968
993
|
return {
|
|
969
994
|
name: "Explore routing",
|
|
970
995
|
status: "pass",
|
|
971
|
-
message: "deprecated
|
|
996
|
+
message: "deprecated compatibility routing disabled in config.toml (recommended)",
|
|
972
997
|
};
|
|
973
998
|
}
|
|
974
|
-
|
|
999
|
+
if (state.source === "unreadable") {
|
|
975
1000
|
return {
|
|
976
1001
|
name: "Explore routing",
|
|
977
1002
|
status: "fail",
|
|
978
1003
|
message: "cannot read config.toml for explore routing check",
|
|
979
1004
|
};
|
|
980
1005
|
}
|
|
1006
|
+
return {
|
|
1007
|
+
name: "Explore routing",
|
|
1008
|
+
status: "pass",
|
|
1009
|
+
message: state.reason === "missing-config"
|
|
1010
|
+
? "deprecated by default (config.toml not found yet)"
|
|
1011
|
+
: "deprecated by default",
|
|
1012
|
+
};
|
|
981
1013
|
}
|
|
982
1014
|
const LORE_COMMIT_GUARD_EXPLICIT_OPT_OUT_VALUES = new Set([
|
|
983
1015
|
"0",
|
|
@@ -1060,6 +1092,117 @@ async function checkLoreCommitGuard(configPath) {
|
|
|
1060
1092
|
function isExplicitLoreCommitGuardOptOut(value) {
|
|
1061
1093
|
return LORE_COMMIT_GUARD_EXPLICIT_OPT_OUT_VALUES.has(value.trim().toLowerCase());
|
|
1062
1094
|
}
|
|
1095
|
+
function decodeBasicXmlEntities(value) {
|
|
1096
|
+
return value
|
|
1097
|
+
.replaceAll("&", "&")
|
|
1098
|
+
.replaceAll("<", "<")
|
|
1099
|
+
.replaceAll(">", ">")
|
|
1100
|
+
.replaceAll(""", '"')
|
|
1101
|
+
.replaceAll("'", "'");
|
|
1102
|
+
}
|
|
1103
|
+
function extractPlistStrings(content) {
|
|
1104
|
+
return [...content.matchAll(/<string>([^<]+)<\/string>/g)].map((match) => decodeBasicXmlEntities(match[1]?.trim() ?? ""));
|
|
1105
|
+
}
|
|
1106
|
+
function extractPlistLabel(content, fallback) {
|
|
1107
|
+
const labelMatch = content.match(/<key>Label<\/key>\s*<string>([^<]+)<\/string>/);
|
|
1108
|
+
return decodeBasicXmlEntities(labelMatch?.[1]?.trim() || fallback);
|
|
1109
|
+
}
|
|
1110
|
+
async function readExistingTextFile(path) {
|
|
1111
|
+
try {
|
|
1112
|
+
return await readFile(path, "utf-8");
|
|
1113
|
+
}
|
|
1114
|
+
catch {
|
|
1115
|
+
return "";
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
function expandLaunchAgentPath(value, homeDir) {
|
|
1119
|
+
if (value === "~")
|
|
1120
|
+
return homeDir;
|
|
1121
|
+
if (value.startsWith("~/"))
|
|
1122
|
+
return join(homeDir, value.slice(2));
|
|
1123
|
+
if (value === "$HOME")
|
|
1124
|
+
return homeDir;
|
|
1125
|
+
if (value.startsWith("$HOME/"))
|
|
1126
|
+
return join(homeDir, value.slice(6));
|
|
1127
|
+
if (value === "${HOME}")
|
|
1128
|
+
return homeDir;
|
|
1129
|
+
if (value.startsWith("${HOME}/"))
|
|
1130
|
+
return join(homeDir, value.slice(8));
|
|
1131
|
+
return value;
|
|
1132
|
+
}
|
|
1133
|
+
function classifyExternalCodexProcessGuard(plistContent, combinedContent) {
|
|
1134
|
+
const lowerPlist = plistContent.toLowerCase();
|
|
1135
|
+
const lower = combinedContent.toLowerCase();
|
|
1136
|
+
if (lower.includes("codex-mcp-child-guard") ||
|
|
1137
|
+
lower.includes("codex_mcp_child_guard") ||
|
|
1138
|
+
combinedContent.includes("CODEX_MCP_GUARD_DEDUPE_APP_CHILDREN") ||
|
|
1139
|
+
(lower.includes("codex app-server") &&
|
|
1140
|
+
lower.includes("pgrep -p") &&
|
|
1141
|
+
lower.includes("kill"))) {
|
|
1142
|
+
return "Codex app-server MCP child dedupe";
|
|
1143
|
+
}
|
|
1144
|
+
if ((lowerPlist.includes("codex-xcode-mcp-guard") ||
|
|
1145
|
+
lowerPlist.includes("codex_xcode_mcp_guard")) &&
|
|
1146
|
+
lower.includes("xcodebuildmcp") &&
|
|
1147
|
+
lower.includes("kill")) {
|
|
1148
|
+
return "XcodeBuildMCP cleanup";
|
|
1149
|
+
}
|
|
1150
|
+
return null;
|
|
1151
|
+
}
|
|
1152
|
+
export async function checkExternalCodexProcessGuards(options = {}) {
|
|
1153
|
+
const platform = options.platform ?? process.platform;
|
|
1154
|
+
if (platform !== "darwin")
|
|
1155
|
+
return null;
|
|
1156
|
+
const homeDir = options.homeDir ?? process.env.HOME;
|
|
1157
|
+
if (!homeDir)
|
|
1158
|
+
return null;
|
|
1159
|
+
const launchAgentsDir = join(homeDir, "Library", "LaunchAgents");
|
|
1160
|
+
if (!existsSync(launchAgentsDir))
|
|
1161
|
+
return null;
|
|
1162
|
+
let entries;
|
|
1163
|
+
try {
|
|
1164
|
+
entries = await readdir(launchAgentsDir);
|
|
1165
|
+
}
|
|
1166
|
+
catch {
|
|
1167
|
+
return null;
|
|
1168
|
+
}
|
|
1169
|
+
const findings = [];
|
|
1170
|
+
for (const entry of entries) {
|
|
1171
|
+
if (!entry.endsWith(".plist"))
|
|
1172
|
+
continue;
|
|
1173
|
+
const plistPath = join(launchAgentsDir, entry);
|
|
1174
|
+
const plistContent = await readExistingTextFile(plistPath);
|
|
1175
|
+
if (plistContent.trim() === "")
|
|
1176
|
+
continue;
|
|
1177
|
+
const loweredPlist = plistContent.toLowerCase();
|
|
1178
|
+
if (!loweredPlist.includes("codex") &&
|
|
1179
|
+
!loweredPlist.includes("mcp") &&
|
|
1180
|
+
!loweredPlist.includes("omx")) {
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
let combinedContent = plistContent;
|
|
1184
|
+
for (const value of extractPlistStrings(plistContent)) {
|
|
1185
|
+
const expandedValue = expandLaunchAgentPath(value, homeDir);
|
|
1186
|
+
if (!expandedValue.startsWith("/"))
|
|
1187
|
+
continue;
|
|
1188
|
+
if (!existsSync(expandedValue))
|
|
1189
|
+
continue;
|
|
1190
|
+
combinedContent += `\n${await readExistingTextFile(expandedValue)}`;
|
|
1191
|
+
}
|
|
1192
|
+
const reason = classifyExternalCodexProcessGuard(plistContent, combinedContent);
|
|
1193
|
+
if (!reason)
|
|
1194
|
+
continue;
|
|
1195
|
+
const label = extractPlistLabel(plistContent, entry);
|
|
1196
|
+
findings.push(`${label} (${reason})`);
|
|
1197
|
+
}
|
|
1198
|
+
if (findings.length === 0)
|
|
1199
|
+
return null;
|
|
1200
|
+
return {
|
|
1201
|
+
name: "External process guards",
|
|
1202
|
+
status: "warn",
|
|
1203
|
+
message: `external LaunchAgent(s) may terminate Codex/MCP helper processes outside OMX setup ownership: ${findings.join(", ")}; unload/remove them before attributing SIGTERM or transport resets to standard OMX MCP configuration`,
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1063
1206
|
function isEnabledTomlValue(value) {
|
|
1064
1207
|
return value === true || (typeof value === "string" && ["1", "true", "yes", "on"].includes(value.trim().toLowerCase()));
|
|
1065
1208
|
}
|
|
@@ -1405,6 +1548,27 @@ export function classifyPostCompactHookStdout(stdout) {
|
|
|
1405
1548
|
};
|
|
1406
1549
|
}
|
|
1407
1550
|
}
|
|
1551
|
+
export function buildPostCompactSmokeSpawnInvocation(expectedCommand, options = {}) {
|
|
1552
|
+
const platform = options.platform ?? process.platform;
|
|
1553
|
+
if (platform === "win32") {
|
|
1554
|
+
return {
|
|
1555
|
+
command: resolveWindowsPowerShellPath(options.env),
|
|
1556
|
+
args: [
|
|
1557
|
+
"-NoProfile",
|
|
1558
|
+
"-ExecutionPolicy",
|
|
1559
|
+
"Bypass",
|
|
1560
|
+
"-Command",
|
|
1561
|
+
expectedCommand,
|
|
1562
|
+
],
|
|
1563
|
+
shell: false,
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
return {
|
|
1567
|
+
command: expectedCommand,
|
|
1568
|
+
args: [],
|
|
1569
|
+
shell: true,
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1408
1572
|
async function checkNativePostCompactHookRuntime(hooksPath, cwd, codexHomeDir) {
|
|
1409
1573
|
if (!existsSync(hooksPath))
|
|
1410
1574
|
return null;
|
|
@@ -1437,7 +1601,8 @@ async function checkNativePostCompactHookRuntime(hooksPath, cwd, codexHomeDir) {
|
|
|
1437
1601
|
cwd: smokeCwd,
|
|
1438
1602
|
session_id: "omx-doctor-postcompact-smoke",
|
|
1439
1603
|
});
|
|
1440
|
-
const
|
|
1604
|
+
const smokeInvocation = buildPostCompactSmokeSpawnInvocation(expectedCommand);
|
|
1605
|
+
const result = spawnSync(smokeInvocation.command, smokeInvocation.args, {
|
|
1441
1606
|
cwd,
|
|
1442
1607
|
encoding: "utf-8",
|
|
1443
1608
|
env: {
|
|
@@ -1445,7 +1610,7 @@ async function checkNativePostCompactHookRuntime(hooksPath, cwd, codexHomeDir) {
|
|
|
1445
1610
|
OMX_NATIVE_HOOK_DOCTOR_SMOKE: "1",
|
|
1446
1611
|
},
|
|
1447
1612
|
input: payload,
|
|
1448
|
-
shell:
|
|
1613
|
+
shell: smokeInvocation.shell,
|
|
1449
1614
|
timeout: 5_000,
|
|
1450
1615
|
});
|
|
1451
1616
|
if (result.error) {
|