oh-my-codex 0.12.6 → 0.13.1
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 +5 -5
- package/Cargo.toml +1 -1
- package/README.md +39 -6
- package/crates/omx-explore/src/main.rs +300 -3
- package/dist/adapt/__tests__/foundation.test.d.ts +2 -0
- package/dist/adapt/__tests__/foundation.test.d.ts.map +1 -0
- package/dist/adapt/__tests__/foundation.test.js +171 -0
- package/dist/adapt/__tests__/foundation.test.js.map +1 -0
- package/dist/adapt/__tests__/hermes.test.d.ts +2 -0
- package/dist/adapt/__tests__/hermes.test.d.ts.map +1 -0
- package/dist/adapt/__tests__/hermes.test.js +137 -0
- package/dist/adapt/__tests__/hermes.test.js.map +1 -0
- package/dist/adapt/contracts.d.ts +157 -0
- package/dist/adapt/contracts.d.ts.map +1 -0
- package/dist/adapt/contracts.js +10 -0
- package/dist/adapt/contracts.js.map +1 -0
- package/dist/adapt/hermes.d.ts +83 -0
- package/dist/adapt/hermes.d.ts.map +1 -0
- package/dist/adapt/hermes.js +371 -0
- package/dist/adapt/hermes.js.map +1 -0
- package/dist/adapt/index.d.ts +14 -0
- package/dist/adapt/index.d.ts.map +1 -0
- package/dist/adapt/index.js +293 -0
- package/dist/adapt/index.js.map +1 -0
- package/dist/adapt/openclaw.d.ts +7 -0
- package/dist/adapt/openclaw.d.ts.map +1 -0
- package/dist/adapt/openclaw.js +299 -0
- package/dist/adapt/openclaw.js.map +1 -0
- package/dist/adapt/paths.d.ts +3 -0
- package/dist/adapt/paths.d.ts.map +1 -0
- package/dist/adapt/paths.js +15 -0
- package/dist/adapt/paths.js.map +1 -0
- package/dist/adapt/registry.d.ts +4 -0
- package/dist/adapt/registry.d.ts.map +1 -0
- package/dist/adapt/registry.js +48 -0
- package/dist/adapt/registry.js.map +1 -0
- package/dist/cli/__tests__/adapt-help.test.d.ts +2 -0
- package/dist/cli/__tests__/adapt-help.test.d.ts.map +1 -0
- package/dist/cli/__tests__/adapt-help.test.js +39 -0
- package/dist/cli/__tests__/adapt-help.test.js.map +1 -0
- package/dist/cli/__tests__/adapt.test.d.ts +2 -0
- package/dist/cli/__tests__/adapt.test.d.ts.map +1 -0
- package/dist/cli/__tests__/adapt.test.js +62 -0
- package/dist/cli/__tests__/adapt.test.js.map +1 -0
- package/dist/cli/__tests__/cleanup.test.js +61 -2
- package/dist/cli/__tests__/cleanup.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +85 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/explore-windows-diagnostics.test.d.ts +2 -0
- package/dist/cli/__tests__/explore-windows-diagnostics.test.d.ts.map +1 -0
- package/dist/cli/__tests__/explore-windows-diagnostics.test.js +17 -0
- package/dist/cli/__tests__/explore-windows-diagnostics.test.js.map +1 -0
- package/dist/cli/__tests__/explore.test.js +69 -0
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +225 -8
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/nested-help-routing.test.js +1 -0
- package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-prd-deep-interview.test.js +5 -0
- package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-prd-smoke.test.d.ts +2 -0
- package/dist/cli/__tests__/ralph-prd-smoke.test.d.ts.map +1 -0
- package/dist/cli/__tests__/ralph-prd-smoke.test.js +167 -0
- package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -0
- package/dist/cli/__tests__/ralph.test.js +103 -1
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +60 -0
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
- package/dist/cli/adapt.d.ts +6 -0
- package/dist/cli/adapt.d.ts.map +1 -0
- package/dist/cli/adapt.js +135 -0
- package/dist/cli/adapt.js.map +1 -0
- package/dist/cli/cleanup.d.ts +3 -1
- package/dist/cli/cleanup.d.ts.map +1 -1
- package/dist/cli/cleanup.js +57 -6
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/doctor.d.ts +6 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +72 -3
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.d.ts +2 -0
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +14 -0
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +4 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +52 -18
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/ralph.d.ts +3 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +83 -0
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +4 -2
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/tmux-hook.d.ts.map +1 -1
- package/dist/cli/tmux-hook.js +2 -1
- package/dist/cli/tmux-hook.js.map +1 -1
- package/dist/config/__tests__/codex-hooks.test.js +26 -2
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts +2 -1
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +23 -2
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +20 -2
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +32 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +2 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-modules.test.js +6 -0
- package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +124 -0
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +2 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js +10 -1
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +21 -0
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +9 -0
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/extensibility/sdk/tmux.d.ts.map +1 -1
- package/dist/hooks/extensibility/sdk/tmux.js +2 -1
- package/dist/hooks/extensibility/sdk/tmux.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +12 -3
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +2 -0
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +9 -0
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +5 -0
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +116 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/index.d.ts +1 -1
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +5 -3
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.js +1 -1
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +6 -5
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/tmux.d.ts +1 -1
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +9 -3
- package/dist/hud/tmux.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +23 -1
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +1 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +3 -11
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +18 -0
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js +1 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
- package/dist/notifications/__tests__/config.test.js +38 -1
- package/dist/notifications/__tests__/config.test.js.map +1 -1
- package/dist/notifications/__tests__/tmux-detector.test.js +1 -1
- package/dist/notifications/__tests__/tmux-detector.test.js.map +1 -1
- package/dist/notifications/tmux-detector.d.ts +1 -0
- package/dist/notifications/tmux-detector.d.ts.map +1 -1
- package/dist/notifications/tmux-detector.js +6 -3
- package/dist/notifications/tmux-detector.js.map +1 -1
- package/dist/notifications/tmux.d.ts.map +1 -1
- package/dist/notifications/tmux.js +20 -38
- package/dist/notifications/tmux.js.map +1 -1
- package/dist/openclaw/__tests__/config.test.js +97 -10
- package/dist/openclaw/__tests__/config.test.js.map +1 -1
- package/dist/openclaw/config.d.ts +19 -1
- package/dist/openclaw/config.d.ts.map +1 -1
- package/dist/openclaw/config.js +220 -20
- package/dist/openclaw/config.js.map +1 -1
- package/dist/planning/artifacts.d.ts +7 -0
- package/dist/planning/artifacts.d.ts.map +1 -1
- package/dist/planning/artifacts.js +18 -4
- package/dist/planning/artifacts.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +818 -11
- 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 +87 -36
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +336 -0
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts +1 -0
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +10 -2
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +1 -1
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +78 -4
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/tmux-hook-engine.d.ts.map +1 -1
- package/dist/scripts/tmux-hook-engine.js +9 -7
- package/dist/scripts/tmux-hook-engine.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +38 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +2 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/skill-active.d.ts +1 -1
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +8 -4
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +1 -1
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.js +3 -3
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/team/__tests__/leader-activity.test.js +28 -0
- package/dist/team/__tests__/leader-activity.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +42 -0
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worktree.test.js +29 -0
- package/dist/team/__tests__/worktree.test.js.map +1 -1
- package/dist/team/leader-activity.d.ts.map +1 -1
- package/dist/team/leader-activity.js +23 -1
- package/dist/team/leader-activity.js.map +1 -1
- package/dist/team/worktree.d.ts.map +1 -1
- package/dist/team/worktree.js +17 -1
- package/dist/team/worktree.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +6 -1
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/__tests__/platform-command.test.js +19 -1
- package/dist/utils/__tests__/platform-command.test.js.map +1 -1
- package/dist/utils/paths.d.ts +2 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +4 -0
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/platform-command.d.ts +1 -0
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +40 -23
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js +1 -1
- package/dist/verification/__tests__/pr-check-workflow.test.d.ts +2 -0
- package/dist/verification/__tests__/pr-check-workflow.test.d.ts.map +1 -0
- package/dist/verification/__tests__/pr-check-workflow.test.js +27 -0
- package/dist/verification/__tests__/pr-check-workflow.test.js.map +1 -0
- package/package.json +1 -1
- package/skills/ralph/SKILL.md +7 -0
- package/skills/ralph-init/SKILL.md +2 -1
- package/src/scripts/__tests__/codex-native-hook.test.ts +1053 -94
- package/src/scripts/codex-native-hook.ts +127 -31
- package/src/scripts/codex-native-pre-post.ts +376 -0
- package/src/scripts/notify-hook/auto-nudge.ts +12 -2
- package/src/scripts/notify-hook/team-worker.ts +1 -1
- package/src/scripts/notify-hook/tmux-injection.ts +93 -3
- package/src/scripts/tmux-hook-engine.ts +9 -7
- package/templates/AGENTS.md +3 -1
package/Cargo.lock
CHANGED
|
@@ -32,11 +32,11 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
|
|
32
32
|
|
|
33
33
|
[[package]]
|
|
34
34
|
name = "omx-explore-harness"
|
|
35
|
-
version = "0.
|
|
35
|
+
version = "0.13.1"
|
|
36
36
|
|
|
37
37
|
[[package]]
|
|
38
38
|
name = "omx-mux"
|
|
39
|
-
version = "0.
|
|
39
|
+
version = "0.13.1"
|
|
40
40
|
dependencies = [
|
|
41
41
|
"serde",
|
|
42
42
|
"serde_json",
|
|
@@ -44,7 +44,7 @@ dependencies = [
|
|
|
44
44
|
|
|
45
45
|
[[package]]
|
|
46
46
|
name = "omx-runtime"
|
|
47
|
-
version = "0.
|
|
47
|
+
version = "0.13.1"
|
|
48
48
|
dependencies = [
|
|
49
49
|
"omx-mux",
|
|
50
50
|
"omx-runtime-core",
|
|
@@ -53,7 +53,7 @@ dependencies = [
|
|
|
53
53
|
|
|
54
54
|
[[package]]
|
|
55
55
|
name = "omx-runtime-core"
|
|
56
|
-
version = "0.
|
|
56
|
+
version = "0.13.1"
|
|
57
57
|
dependencies = [
|
|
58
58
|
"fs2",
|
|
59
59
|
"serde",
|
|
@@ -62,7 +62,7 @@ dependencies = [
|
|
|
62
62
|
|
|
63
63
|
[[package]]
|
|
64
64
|
name = "omx-sparkshell"
|
|
65
|
-
version = "0.
|
|
65
|
+
version = "0.13.1"
|
|
66
66
|
dependencies = [
|
|
67
67
|
"omx-mux",
|
|
68
68
|
]
|
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -72,6 +72,7 @@ $team 3:executor "execute the approved plan in parallel"
|
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
That is the main path.
|
|
75
|
+
Before you treat the runtime as ready, run the quick-start smoke test below: `omx doctor` verifies the install shape, while `omx exec` proves the active Codex runtime can actually authenticate and complete a model call from the current environment.
|
|
75
76
|
Start OMX strongly, clarify first when needed, approve the plan, then choose `$team` for coordinated parallel execution or `$ralph` for the persistent completion loop.
|
|
76
77
|
|
|
77
78
|
## What OMX is for
|
|
@@ -90,12 +91,22 @@ If you want plain Codex with no extra workflow layer, you probably do not need O
|
|
|
90
91
|
|
|
91
92
|
- Node.js 20+
|
|
92
93
|
- Codex CLI installed: `npm install -g @openai/codex`
|
|
93
|
-
- Codex auth configured
|
|
94
|
+
- Codex auth configured and visible in the same shell/profile that will run OMX
|
|
94
95
|
- `tmux` on macOS/Linux if you want the recommended durable team runtime
|
|
95
96
|
- `psmux` on native Windows only if you intentionally want the less-supported Windows team path
|
|
96
97
|
|
|
97
98
|
### A good first session
|
|
98
99
|
|
|
100
|
+
After install, check both boundaries:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
omx doctor
|
|
104
|
+
codex login status
|
|
105
|
+
omx exec --skip-git-repo-check -C . "Reply with exactly OMX-EXEC-OK"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`omx doctor` catches missing OMX files, hooks, and runtime prerequisites. The real smoke test catches auth, profile, and provider/base-URL problems that only appear when Codex performs an actual request.
|
|
109
|
+
|
|
99
110
|
Launch OMX the recommended way:
|
|
100
111
|
|
|
101
112
|
```bash
|
|
@@ -135,10 +146,12 @@ Most users should think of OMX as **better task routing + better workflow + bett
|
|
|
135
146
|
## Start here if you are new
|
|
136
147
|
|
|
137
148
|
1. Run `omx setup`
|
|
138
|
-
2.
|
|
139
|
-
3.
|
|
140
|
-
4.
|
|
141
|
-
5.
|
|
149
|
+
2. Run `omx doctor`
|
|
150
|
+
3. Run a real execution smoke test: `codex login status` and `omx exec --skip-git-repo-check -C . "Reply with exactly OMX-EXEC-OK"`
|
|
151
|
+
4. Launch with `omx --madmax --high`
|
|
152
|
+
5. Use `$deep-interview "..."` when the request or boundaries are still unclear
|
|
153
|
+
6. Use `$ralplan "..."` to approve the plan and review tradeoffs
|
|
154
|
+
7. Choose `$team` for coordinated parallel execution or `$ralph` for persistent completion loops
|
|
142
155
|
|
|
143
156
|
## Recommended workflow
|
|
144
157
|
|
|
@@ -177,7 +190,7 @@ These are operator/support surfaces:
|
|
|
177
190
|
- `omx setup` installs prompts, skills, AGENTS scaffolding, `.codex/config.toml`, and OMX-managed native Codex hooks in `.codex/hooks.json`
|
|
178
191
|
- setup refresh preserves non-OMX hook entries in `.codex/hooks.json` and only rewrites OMX-managed wrappers
|
|
179
192
|
- `omx uninstall` removes OMX-managed wrappers from `.codex/hooks.json` but keeps the file when user hooks remain
|
|
180
|
-
- `omx doctor` verifies the install when something seems wrong
|
|
193
|
+
- `omx doctor` verifies the install when something seems wrong; it does not prove that the active Codex profile can make an authenticated model call
|
|
181
194
|
- `omx hud --watch` is a monitoring/status surface, not the primary user workflow
|
|
182
195
|
|
|
183
196
|
For non-team sessions, native Codex hooks are now the canonical lifecycle surface:
|
|
@@ -187,6 +200,24 @@ For non-team sessions, native Codex hooks are now the canonical lifecycle surfac
|
|
|
187
200
|
|
|
188
201
|
See [Codex native hook mapping](./docs/codex-native-hooks.md) for the current native / fallback matrix.
|
|
189
202
|
|
|
203
|
+
|
|
204
|
+
### Troubleshooting false-green readiness
|
|
205
|
+
|
|
206
|
+
A green `omx doctor` means the install and local runtime wiring look sane. If real execution still fails, check the environment Codex actually uses:
|
|
207
|
+
|
|
208
|
+
- Run `codex login status` and `omx exec --skip-git-repo-check -C . "Reply with exactly OMX-EXEC-OK"` from the same shell/profile that will launch OMX.
|
|
209
|
+
- In custom HOME, profile, container, or service shells, confirm the active `~/.codex` (or `CODEX_HOME`) is the one with the expected auth and config. Do not assume your normal user `~/.codex` is visible there.
|
|
210
|
+
- If you depend on a local OpenAI-compatible proxy, confirm the active `~/.codex/config.toml` includes the expected `openai_base_url`; otherwise a proxy-issued key can be sent to the default endpoint and fail with `401 Unauthorized`, `Missing bearer or basic authentication in header`, or `Incorrect API key provided`.
|
|
211
|
+
- If `omx doctor --team` or resume reports a stale team such as `resume_blocker` or a missing tmux session, clean the dead runtime state before retrying:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
omx team shutdown <team-name> --force --confirm-issues
|
|
215
|
+
omx cancel
|
|
216
|
+
omx doctor --team
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Only use the forced team shutdown for a team you have confirmed is dead or intentionally abandoned.
|
|
220
|
+
|
|
190
221
|
### Explore and sparkshell
|
|
191
222
|
|
|
192
223
|
- `omx explore --prompt "..."` is for read-only repository lookup
|
|
@@ -220,6 +251,7 @@ omx wiki refresh --json
|
|
|
220
251
|
|
|
221
252
|
`omx team` works best on macOS/Linux with `tmux`.
|
|
222
253
|
Native Windows remains a secondary path, and WSL2 is generally the better choice if you want a Windows-hosted setup.
|
|
254
|
+
On native Windows, OMX accepts `psmux` as the tmux-compatible binary for the existing tmux-backed paths it already uses.
|
|
223
255
|
|
|
224
256
|
| Platform | Install |
|
|
225
257
|
| --- | --- |
|
|
@@ -250,6 +282,7 @@ If this happens, try:
|
|
|
250
282
|
- [Skills reference](./docs/skills.html)
|
|
251
283
|
- [Codex native hook mapping](./docs/codex-native-hooks.md)
|
|
252
284
|
- [Integrations](./docs/integrations.html)
|
|
285
|
+
- [Troubleshooting execution readiness](./docs/troubleshooting.md)
|
|
253
286
|
- [OpenClaw / notification gateway guide](./docs/openclaw-integration.md)
|
|
254
287
|
- [Contributing](./CONTRIBUTING.md)
|
|
255
288
|
- [Changelog](./CHANGELOG.md)
|
|
@@ -12,6 +12,8 @@ const CODEX_BIN_ENV: &str = "OMX_EXPLORE_CODEX_BIN";
|
|
|
12
12
|
const HARNESS_ROOT_ENV: &str = "OMX_EXPLORE_ROOT";
|
|
13
13
|
const INTERNAL_DIRECT_WRAPPER_FLAG: &str = "--internal-allowlist-direct";
|
|
14
14
|
const INTERNAL_SHELL_WRAPPER_FLAG: &str = "--internal-allowlist-shell";
|
|
15
|
+
const WINDOWS_UNSUPPORTED_ALLOWLIST_MESSAGE: &str =
|
|
16
|
+
"omx explore built-in harness is not ready on Windows because its allowlist runtime relies on POSIX sh/bash wrappers. Set OMX_EXPLORE_BIN to a compatible custom harness, prefer `omx sparkshell` for shell-native read-only lookups, or run `omx doctor` for readiness details.";
|
|
15
17
|
|
|
16
18
|
const ALLOWED_DIRECT_COMMANDS: &[&str] = &[
|
|
17
19
|
"rg", "grep", "ls", "find", "wc", "cat", "head", "tail", "pwd", "printf",
|
|
@@ -270,7 +272,12 @@ fn resolve_codex_binary() -> String {
|
|
|
270
272
|
}
|
|
271
273
|
|
|
272
274
|
fn codex_launch_for_binary(codex_binary: &str) -> Option<CodexLaunch> {
|
|
273
|
-
let
|
|
275
|
+
let codex_path = Path::new(codex_binary);
|
|
276
|
+
if let Some(launch) = codex_launch_for_posix_node_shim(codex_path) {
|
|
277
|
+
return Some(launch);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
let interpreter = read_shebang_interpreter(codex_path)?;
|
|
274
281
|
let (program, mut leading_args) = resolve_shebang_launch(&interpreter)?;
|
|
275
282
|
leading_args.push(codex_binary.to_string());
|
|
276
283
|
Some(CodexLaunch {
|
|
@@ -279,6 +286,21 @@ fn codex_launch_for_binary(codex_binary: &str) -> Option<CodexLaunch> {
|
|
|
279
286
|
})
|
|
280
287
|
}
|
|
281
288
|
|
|
289
|
+
fn codex_launch_for_posix_node_shim(codex_path: &Path) -> Option<CodexLaunch> {
|
|
290
|
+
let shebang = read_shebang_interpreter(codex_path)?;
|
|
291
|
+
if !is_posix_shell_shebang(&shebang) {
|
|
292
|
+
return None;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
let script = read_to_string(codex_path).ok()?;
|
|
296
|
+
let entrypoint = resolve_posix_node_shim_entrypoint(codex_path, &script)?;
|
|
297
|
+
let program = resolve_posix_node_shim_program(codex_path, &script)?;
|
|
298
|
+
Some(CodexLaunch {
|
|
299
|
+
program: program.display().to_string(),
|
|
300
|
+
leading_args: vec![entrypoint.display().to_string()],
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
|
|
282
304
|
fn read_shebang_interpreter(path: &Path) -> Option<String> {
|
|
283
305
|
let file = File::open(path).ok()?;
|
|
284
306
|
let mut reader = BufReader::new(file);
|
|
@@ -293,6 +315,85 @@ fn read_shebang_interpreter(path: &Path) -> Option<String> {
|
|
|
293
315
|
.map(ToOwned::to_owned)
|
|
294
316
|
}
|
|
295
317
|
|
|
318
|
+
fn is_posix_shell_shebang(shebang: &str) -> bool {
|
|
319
|
+
let parts: Vec<&str> = shebang.split_whitespace().collect();
|
|
320
|
+
let interpreter = *parts.first().unwrap_or(&"");
|
|
321
|
+
if interpreter.ends_with("/env") {
|
|
322
|
+
return parts
|
|
323
|
+
.get(1)
|
|
324
|
+
.is_some_and(|target| is_posix_shell_name(target));
|
|
325
|
+
}
|
|
326
|
+
is_posix_shell_name(interpreter)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
fn is_posix_shell_name(value: &str) -> bool {
|
|
330
|
+
matches!(
|
|
331
|
+
Path::new(value).file_name().and_then(|name| name.to_str()),
|
|
332
|
+
Some("sh" | "bash" | "dash" | "ash" | "ksh" | "zsh")
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
fn resolve_posix_node_shim_entrypoint(codex_path: &Path, script: &str) -> Option<PathBuf> {
|
|
337
|
+
let basedir = codex_path.parent()?;
|
|
338
|
+
quoted_shell_segments(script)
|
|
339
|
+
.into_iter()
|
|
340
|
+
.filter_map(|segment| strip_basedir_prefix(&segment).map(ToOwned::to_owned))
|
|
341
|
+
.map(|relative| normalize_path(basedir.join(relative)))
|
|
342
|
+
.find(|candidate| is_node_entrypoint(candidate))
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
fn resolve_posix_node_shim_program(codex_path: &Path, script: &str) -> Option<PathBuf> {
|
|
346
|
+
let basedir = codex_path.parent()?;
|
|
347
|
+
if quoted_shell_segments(script)
|
|
348
|
+
.into_iter()
|
|
349
|
+
.any(|segment| matches!(strip_basedir_prefix(&segment), Some("node")))
|
|
350
|
+
{
|
|
351
|
+
let sibling_node = basedir.join("node");
|
|
352
|
+
if is_usable_host_command(&sibling_node) {
|
|
353
|
+
return Some(sibling_node);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
resolve_host_command("node")
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
fn quoted_shell_segments(script: &str) -> Vec<String> {
|
|
360
|
+
let mut segments = Vec::new();
|
|
361
|
+
let mut active_quote: Option<char> = None;
|
|
362
|
+
let mut current = String::new();
|
|
363
|
+
|
|
364
|
+
for ch in script.chars() {
|
|
365
|
+
if let Some(quote) = active_quote {
|
|
366
|
+
if ch == quote {
|
|
367
|
+
segments.push(std::mem::take(&mut current));
|
|
368
|
+
active_quote = None;
|
|
369
|
+
} else {
|
|
370
|
+
current.push(ch);
|
|
371
|
+
}
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if ch == '\'' || ch == '"' {
|
|
376
|
+
active_quote = Some(ch);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
segments
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
fn strip_basedir_prefix(segment: &str) -> Option<&str> {
|
|
384
|
+
segment
|
|
385
|
+
.strip_prefix("$basedir/")
|
|
386
|
+
.or_else(|| segment.strip_prefix("${basedir}/"))
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
fn is_node_entrypoint(path: &Path) -> bool {
|
|
390
|
+
path.is_file()
|
|
391
|
+
&& matches!(
|
|
392
|
+
path.extension().and_then(|value| value.to_str()),
|
|
393
|
+
Some("js" | "cjs" | "mjs")
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
|
|
296
397
|
fn resolve_shebang_launch(shebang: &str) -> Option<(String, Vec<String>)> {
|
|
297
398
|
let parts: Vec<&str> = shebang.split_whitespace().collect();
|
|
298
399
|
let interpreter = *parts.first()?;
|
|
@@ -366,6 +467,9 @@ fn compose_exec_prompt(user_prompt: &str, prompt_contract: &str) -> String {
|
|
|
366
467
|
}
|
|
367
468
|
|
|
368
469
|
fn prepare_allowlist_environment() -> Result<AllowlistEnvironment, String> {
|
|
470
|
+
if let Some(message) = allowlist_platform_diagnostic(env::consts::OS) {
|
|
471
|
+
return Err(message.to_string());
|
|
472
|
+
}
|
|
369
473
|
let root = temp_allowlist_dir()?;
|
|
370
474
|
let bin_dir = root.path.join("bin");
|
|
371
475
|
create_dir_all(&bin_dir).map_err(|err| {
|
|
@@ -412,6 +516,13 @@ fn prepare_allowlist_environment() -> Result<AllowlistEnvironment, String> {
|
|
|
412
516
|
})
|
|
413
517
|
}
|
|
414
518
|
|
|
519
|
+
fn allowlist_platform_diagnostic(os: &str) -> Option<&'static str> {
|
|
520
|
+
if os.eq_ignore_ascii_case("windows") {
|
|
521
|
+
return Some(WINDOWS_UNSUPPORTED_ALLOWLIST_MESSAGE);
|
|
522
|
+
}
|
|
523
|
+
None
|
|
524
|
+
}
|
|
525
|
+
|
|
415
526
|
fn temp_allowlist_dir() -> Result<TempDirGuard, String> {
|
|
416
527
|
let dir = env::temp_dir().join(format!(
|
|
417
528
|
"omx-explore-allowlist-{}-{}",
|
|
@@ -469,20 +580,39 @@ fn build_direct_wrapper(self_exe: &Path, command: &str) -> Result<String, String
|
|
|
469
580
|
|
|
470
581
|
fn resolve_host_command(command: &str) -> Option<PathBuf> {
|
|
471
582
|
let candidate = Path::new(command);
|
|
472
|
-
if candidate.is_absolute() && candidate
|
|
583
|
+
if candidate.is_absolute() && is_usable_host_command(candidate) {
|
|
473
584
|
return Some(candidate.to_path_buf());
|
|
474
585
|
}
|
|
475
586
|
|
|
476
587
|
let path = env::var_os("PATH")?;
|
|
477
588
|
for entry in env::split_paths(&path) {
|
|
478
589
|
let resolved = entry.join(command);
|
|
479
|
-
if resolved
|
|
590
|
+
if is_usable_host_command(&resolved) {
|
|
480
591
|
return Some(resolved);
|
|
481
592
|
}
|
|
482
593
|
}
|
|
483
594
|
None
|
|
484
595
|
}
|
|
485
596
|
|
|
597
|
+
fn is_usable_host_command(path: &Path) -> bool {
|
|
598
|
+
let metadata = match path.metadata() {
|
|
599
|
+
Ok(metadata) => metadata,
|
|
600
|
+
Err(_) => return false,
|
|
601
|
+
};
|
|
602
|
+
if !metadata.is_file() {
|
|
603
|
+
return false;
|
|
604
|
+
}
|
|
605
|
+
#[cfg(unix)]
|
|
606
|
+
{
|
|
607
|
+
use std::os::unix::fs::PermissionsExt;
|
|
608
|
+
metadata.permissions().mode() & 0o111 != 0
|
|
609
|
+
}
|
|
610
|
+
#[cfg(not(unix))]
|
|
611
|
+
{
|
|
612
|
+
true
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
486
616
|
fn shell_quote(value: &str) -> String {
|
|
487
617
|
format!("'{}'", value.replace('\'', "'\"'\"'"))
|
|
488
618
|
}
|
|
@@ -885,6 +1015,7 @@ mod tests {
|
|
|
885
1015
|
|
|
886
1016
|
#[test]
|
|
887
1017
|
fn codex_launch_for_env_node_shebang_uses_host_node_absolute_path() {
|
|
1018
|
+
let _guard = env_lock();
|
|
888
1019
|
let root = temp_allowlist_dir().expect("temp root");
|
|
889
1020
|
let script_path = root.path.join("codex-script");
|
|
890
1021
|
write(&script_path, b"#!/usr/bin/env node\nconsole.log(\"ok\");\n").expect("write script");
|
|
@@ -896,6 +1027,162 @@ mod tests {
|
|
|
896
1027
|
assert_eq!(launch.leading_args, vec![script_path.display().to_string()]);
|
|
897
1028
|
}
|
|
898
1029
|
|
|
1030
|
+
#[cfg(unix)]
|
|
1031
|
+
#[test]
|
|
1032
|
+
fn codex_launch_for_posix_package_manager_shim_uses_host_node_and_entrypoint() {
|
|
1033
|
+
let _guard = env_lock();
|
|
1034
|
+
let root = temp_allowlist_dir().expect("temp root");
|
|
1035
|
+
let host_bin = root.path.join("host-bin");
|
|
1036
|
+
let shim_dir = root.path.join("node_modules").join(".bin");
|
|
1037
|
+
let entrypoint = root
|
|
1038
|
+
.path
|
|
1039
|
+
.join("node_modules")
|
|
1040
|
+
.join("@openai")
|
|
1041
|
+
.join("codex")
|
|
1042
|
+
.join("bin")
|
|
1043
|
+
.join("codex.js");
|
|
1044
|
+
create_dir_all(&host_bin).expect("create host bin");
|
|
1045
|
+
create_dir_all(&shim_dir).expect("create shim dir");
|
|
1046
|
+
create_dir_all(entrypoint.parent().expect("entrypoint parent"))
|
|
1047
|
+
.expect("create entrypoint dir");
|
|
1048
|
+
|
|
1049
|
+
let fake_node = host_bin.join("node");
|
|
1050
|
+
write_executable(&fake_node, "#!/bin/sh\nexit 0\n").expect("write fake node");
|
|
1051
|
+
write(&entrypoint, "console.log('ok');\n").expect("write entrypoint");
|
|
1052
|
+
|
|
1053
|
+
let shim_path = shim_dir.join("codex");
|
|
1054
|
+
write_executable(
|
|
1055
|
+
&shim_path,
|
|
1056
|
+
r#"#!/bin/sh
|
|
1057
|
+
basedir=$(dirname "$0")
|
|
1058
|
+
if [ -x "$basedir/node" ]; then
|
|
1059
|
+
exec "$basedir/node" "$basedir/../@openai/codex/bin/codex.js" "$@"
|
|
1060
|
+
fi
|
|
1061
|
+
exec node "$basedir/../@openai/codex/bin/codex.js" "$@"
|
|
1062
|
+
"#,
|
|
1063
|
+
)
|
|
1064
|
+
.expect("write shim");
|
|
1065
|
+
|
|
1066
|
+
let original_path = env::var_os("PATH");
|
|
1067
|
+
unsafe {
|
|
1068
|
+
env::set_var("PATH", &host_bin);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
let launch =
|
|
1072
|
+
codex_launch_for_binary(shim_path.to_str().expect("shim path")).expect("launch config");
|
|
1073
|
+
|
|
1074
|
+
match original_path {
|
|
1075
|
+
Some(value) => unsafe { env::set_var("PATH", value) },
|
|
1076
|
+
None => unsafe { env::remove_var("PATH") },
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
assert_eq!(launch.program, fake_node.display().to_string());
|
|
1080
|
+
assert_eq!(launch.leading_args, vec![entrypoint.display().to_string()]);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
#[cfg(unix)]
|
|
1084
|
+
#[test]
|
|
1085
|
+
fn resolve_host_command_skips_directory_and_non_executable_path_entries() {
|
|
1086
|
+
let _guard = env_lock();
|
|
1087
|
+
let root = temp_allowlist_dir().expect("temp root");
|
|
1088
|
+
let bad_bin = root.path.join("bad-bin");
|
|
1089
|
+
let blocked_file_bin = root.path.join("blocked-file-bin");
|
|
1090
|
+
let good_bin = root.path.join("good-bin");
|
|
1091
|
+
create_dir_all(&bad_bin).expect("create bad bin");
|
|
1092
|
+
create_dir_all(&blocked_file_bin).expect("create blocked file bin");
|
|
1093
|
+
create_dir_all(&good_bin).expect("create good bin");
|
|
1094
|
+
|
|
1095
|
+
let blocked_directory = bad_bin.join("node");
|
|
1096
|
+
create_dir_all(&blocked_directory).expect("create blocked directory");
|
|
1097
|
+
|
|
1098
|
+
let blocked_node = blocked_file_bin.join("node");
|
|
1099
|
+
write(&blocked_node, "#!/bin/sh\nexit 0\n").expect("write blocked file node");
|
|
1100
|
+
let executable_node = good_bin.join("node");
|
|
1101
|
+
write_executable(&executable_node, "#!/bin/sh\nexit 0\n").expect("write executable node");
|
|
1102
|
+
|
|
1103
|
+
#[cfg(unix)]
|
|
1104
|
+
{
|
|
1105
|
+
use std::fs;
|
|
1106
|
+
use std::os::unix::fs::PermissionsExt;
|
|
1107
|
+
let mut perms = fs::metadata(&blocked_node)
|
|
1108
|
+
.expect("stat blocked node")
|
|
1109
|
+
.permissions();
|
|
1110
|
+
perms.set_mode(0o644);
|
|
1111
|
+
fs::set_permissions(&blocked_node, perms).expect("chmod blocked node");
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
let original_path = env::var_os("PATH");
|
|
1115
|
+
unsafe {
|
|
1116
|
+
env::set_var(
|
|
1117
|
+
"PATH",
|
|
1118
|
+
env::join_paths([
|
|
1119
|
+
bad_bin.as_path(),
|
|
1120
|
+
blocked_file_bin.as_path(),
|
|
1121
|
+
good_bin.as_path(),
|
|
1122
|
+
])
|
|
1123
|
+
.expect("join path"),
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
let resolved = resolve_host_command("node");
|
|
1128
|
+
|
|
1129
|
+
match original_path {
|
|
1130
|
+
Some(value) => unsafe { env::set_var("PATH", value) },
|
|
1131
|
+
None => unsafe { env::remove_var("PATH") },
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
assert_eq!(resolved, Some(executable_node));
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
#[cfg(unix)]
|
|
1138
|
+
#[test]
|
|
1139
|
+
fn codex_launch_for_env_node_shebang_skips_non_executable_earlier_node_entry() {
|
|
1140
|
+
let _guard = env_lock();
|
|
1141
|
+
let root = temp_allowlist_dir().expect("temp root");
|
|
1142
|
+
let bad_bin = root.path.join("bad-bin");
|
|
1143
|
+
let good_bin = root.path.join("good-bin");
|
|
1144
|
+
create_dir_all(&bad_bin).expect("create bad bin");
|
|
1145
|
+
create_dir_all(&good_bin).expect("create good bin");
|
|
1146
|
+
|
|
1147
|
+
let blocked_node = bad_bin.join("node");
|
|
1148
|
+
write(&blocked_node, "#!/bin/sh\nexit 0\n").expect("write blocked node");
|
|
1149
|
+
let executable_node = good_bin.join("node");
|
|
1150
|
+
write_executable(&executable_node, "#!/bin/sh\nexit 0\n").expect("write executable node");
|
|
1151
|
+
|
|
1152
|
+
#[cfg(unix)]
|
|
1153
|
+
{
|
|
1154
|
+
use std::fs;
|
|
1155
|
+
use std::os::unix::fs::PermissionsExt;
|
|
1156
|
+
let mut perms = fs::metadata(&blocked_node)
|
|
1157
|
+
.expect("stat blocked node")
|
|
1158
|
+
.permissions();
|
|
1159
|
+
perms.set_mode(0o644);
|
|
1160
|
+
fs::set_permissions(&blocked_node, perms).expect("chmod blocked node");
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
let script_path = root.path.join("codex-script");
|
|
1164
|
+
write(&script_path, b"#!/usr/bin/env node\nconsole.log(\"ok\");\n").expect("write script");
|
|
1165
|
+
|
|
1166
|
+
let original_path = env::var_os("PATH");
|
|
1167
|
+
unsafe {
|
|
1168
|
+
env::set_var(
|
|
1169
|
+
"PATH",
|
|
1170
|
+
env::join_paths([bad_bin.as_path(), good_bin.as_path()]).expect("join path"),
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
let launch = codex_launch_for_binary(script_path.to_str().expect("script path"))
|
|
1175
|
+
.expect("launch config");
|
|
1176
|
+
|
|
1177
|
+
match original_path {
|
|
1178
|
+
Some(value) => unsafe { env::set_var("PATH", value) },
|
|
1179
|
+
None => unsafe { env::remove_var("PATH") },
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
assert_eq!(launch.program, executable_node.display().to_string());
|
|
1183
|
+
assert_eq!(launch.leading_args, vec![script_path.display().to_string()]);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
899
1186
|
#[cfg(unix)]
|
|
900
1187
|
fn create_host_bin_with_commands(commands: &[&str]) -> (TempDirGuard, PathBuf) {
|
|
901
1188
|
let root = temp_allowlist_dir().expect("temp root");
|
|
@@ -972,6 +1259,7 @@ mod tests {
|
|
|
972
1259
|
#[cfg(unix)]
|
|
973
1260
|
#[test]
|
|
974
1261
|
fn prepare_allowlist_environment_preserves_present_command_wrapper_execution() {
|
|
1262
|
+
let _guard = env_lock();
|
|
975
1263
|
let self_exe = env::current_exe().expect("current exe");
|
|
976
1264
|
let pwd = resolve_host_command("pwd").expect("host pwd path");
|
|
977
1265
|
|
|
@@ -1047,6 +1335,15 @@ mod tests {
|
|
|
1047
1335
|
);
|
|
1048
1336
|
}
|
|
1049
1337
|
|
|
1338
|
+
#[test]
|
|
1339
|
+
fn allowlist_platform_diagnostic_blocks_windows_with_actionable_guidance() {
|
|
1340
|
+
let diagnostic = allowlist_platform_diagnostic("windows").expect("windows diagnostic");
|
|
1341
|
+
|
|
1342
|
+
assert!(diagnostic.contains("not ready on Windows"));
|
|
1343
|
+
assert!(diagnostic.contains("OMX_EXPLORE_BIN"));
|
|
1344
|
+
assert!(allowlist_platform_diagnostic("linux").is_none());
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1050
1347
|
#[test]
|
|
1051
1348
|
fn validate_direct_command_covers_additional_head_wc_and_tail_rejections() {
|
|
1052
1349
|
assert!(validate_direct_command("head", &["-".into()]).is_err());
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foundation.test.d.ts","sourceRoot":"","sources":["../../../src/adapt/__tests__/foundation.test.ts"],"names":[],"mappings":""}
|