@ory/codex 0.14.0 → 1.0.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/README.md CHANGED
@@ -2,9 +2,12 @@
2
2
 
3
3
  Security and developer experience for [Codex](https://github.com/openai/codex), powered by [Ory](https://ory.com).
4
4
 
5
- **Security.** Codex runs real actions on your machine — editing files, running shell commands, calling APIs. The plugin gives every session a verifiable identity (you sign in once; Codex and any sub-agents it spawns each get their own), checks every tool call against permissions you control, and records each decision as an audit trace you can ship to your observability stack. It starts in watch mode so nothing is blocked on day one, and if Ory is ever unreachable it steps aside rather than locking you out.
5
+ Codex runs real actions on your machine — editing files, running shell commands, calling APIs. This plugin gives those actions an identity, a permission check, and an audit trail.
6
6
 
7
- **Developer experience.** A single command installs the plugin and walks you through connecting — choose Ory Network, a local Docker stack, or audit-only, and it wires up the project, sign-in client, login, and permissions for you. It also helps you build Ory into your own app: ask in plain language to scaffold login, registration, and recovery pages, run a local Ory, or manage identities and permissions through the bundled MCP server.
7
+ One command installs two independent halves:
8
+
9
+ - **Developer experience** — the Ory skill catalog, the local Ory dev stack, a bundled Ory tool server, and activity logging. No account, no keys, nothing to configure: it works the moment it's installed.
10
+ - **Ory Agent Security** — browser sign-in, brokered permission checks before tool calls, and the delegation trail. Opt-in connection details from the [Ory Console](https://console.ory.sh) switch it on, and it takes nothing away from the half above. See [Connect to Ory Agent Security](#connect-to-ory-agent-security).
8
11
 
9
12
  ## What you'll need
10
13
 
@@ -15,134 +18,136 @@ Security and developer experience for [Codex](https://github.com/openai/codex),
15
18
 
16
19
  ## Get started
17
20
 
18
- Run one command. It installs the plugin and walks you through connecting:
21
+ Run one command:
19
22
 
20
23
  ```bash
21
24
  npx -y -p @ory/codex ory-codex install
22
25
  ```
23
26
 
24
- This registers the Ory plugin with Codex (hooks, skills, and a bundled Ory tool server) and then opens a guided setup **in your browser** where you pick how to connect — the default takes just a click:
25
-
26
- - **Ory Network** *(default)* — sign in, or create a free account, in your browser. The project, keys, permissions, and login are all set up for you. Nothing to configure by hand.
27
- - **Local** — run a complete Ory on your laptop with Docker. No account, no signup, no keys. Great for trying it out.
28
- - **Audit-only** — skip Ory entirely and just log what Codex does.
29
-
30
- > No browser available (CI, SSH, headless)? The same walkthrough runs right in your terminal instead — or force it with `--no-web`.
31
-
32
- That's it. Confirm everything landed with:
27
+ This registers the Ory plugin with Codex (hooks, skills, and a bundled Ory tool server). Confirm everything landed with:
33
28
 
34
29
  ```bash
35
30
  npx -y -p @ory/codex ory-codex status
36
31
  ```
37
32
 
38
- `status` is your one-stop check: what's configured, who's signed in, which tools are covered by permissions, and recent activity. Anything not set up yet shows as `(unset)`.
33
+ `status` is your one-stop check: what's configured, who's signed in, which tools are covered by permissions, and recent activity. Until you connect Agent Security, the identity and permission rows say so and name what's missing.
34
+
35
+ > **First launch: trust the Ory hooks.** Codex treats a freshly installed plugin's hooks as untrusted, so your first Codex session asks you to review and trust them. Activity logging starts once you do; sign-in and per-tool checks only run after you connect to Agent Security. In the TUI, that first check kicks in on your opening turn. If Codex consumed its one-shot session event before the plugin became trusted, the opening prompt starts authentication instead.
36
+
37
+ ## Skills and commands
38
+
39
+ Installing the plugin drops the full Ory playbook catalog into Codex. **Skills** are model-invoked — say what you want in plain language, or pick one from the `/skills` menu.
40
+
41
+ | Skill | What it does for you |
42
+ |---|---|
43
+ | `ory-auth-setup` | Adds a complete auth system to your app — login, registration, recovery, verification, settings — on [Ory Elements](https://github.com/ory/elements) |
44
+ | `ory-login-flow` | Builds just the pages, wired to Ory's self-service flows |
45
+ | `ory-social-login` | "Sign in with…" for Google, GitHub, Apple, Microsoft, Discord, Slack, GitLab, Facebook |
46
+ | `ory-local-dev` | Develops and tests login/permission flows against a local Ory — no project, no account, offline |
47
+ | `ory-permissions-onboarding` | Walks a fresh install from observe mode to enforced per-tool permissions without getting blocked |
48
+ | `ory-build-agent` | Drops `@ory/argus` into an agent *you* own — Claude Agent SDK, OpenAI Agents, Mastra, Vercel AI, LangGraph/PydanticAI |
49
+ | `ory-build-integration` | Wires Ory into your app: Action webhooks, JWT validation at a gateway, live event streams |
50
+ | `ory-contribute-integration` | Authors and submits an integration to the public `ory/integrates` registry |
51
+ | `ory-e2b-sandbox` | Scaffolds an E2B sandbox template that boots with this plugin preinstalled |
52
+ | `ory-temporal-worker` | Scaffolds a Temporal TypeScript worker where every Activity is authenticated, authorized, and audited |
53
+
54
+ The local stack has its own playbooks — ask for them by name:
39
55
 
40
- > **First launch: trust the Ory hooks.** Codex treats a freshly installed plugin's hooks as untrusted, so your first Codex session asks you to review and trust them. Sign-in and per-tool checks only start running once you do. In the TUI, that first check kicks in on your opening turn.
56
+ | Command | What it does |
57
+ |---|---|
58
+ | `ory-local-up` | Starts a local Ory (Identities, OAuth2, Permissions) in Docker and seeds a test user — it prints the email + password to sign in with |
59
+ | `ory-local-down` | Stops it, keeping your data volumes |
60
+ | `ory-temporal-up` | Starts a local Temporal dev server for the `ory-temporal-worker` scaffold |
41
61
 
42
- Re-run install with `--reconfigure` to change your connection later, or `--no-configure` to skip the wizard and connect by hand.
62
+ The local stack runs entirely on your laptop: Ory APIs at `http://localhost:4000`, a login UI on `:4455` (not `:3000`, to dodge Next.js port clashes), and the Ory Console on `:4100`.
63
+
64
+ A built-in **Ory tool server** rounds it out — Codex can manage identities, projects, and permissions straight from chat.
65
+
66
+ So: ask Codex *"add Ory login to this app"* and it scaffolds the pages, starts a local Ory, and wires them together.
43
67
 
44
68
  ## What you get
45
69
 
46
- Once connected, every tool Codex runs is governed by Ory — three things happen automatically:
70
+ Out of the box, every tool Codex runs produces a privacy-safe structured activity event in the unified local log.
71
+
72
+ Once you connect to Ory Agent Security, two more things happen automatically:
47
73
 
48
- - **Who's driving.** You sign in once in your browser (a standard secure browser sign-in — no tokens to copy around). Codex itself registers its own identity automatically the first time it runs. The "who acted on whose behalf" trail stays queryable later.
49
- - **What it's allowed to do.** Before a tool runs, Ory checks whether it's permitted. It starts in **watch mode** — nothing is blocked, you just *see* what would be — so it never gets in your way on day one.
50
- - **A record of everything.** Every decision (allowed, denied, skipped) is logged as a trace you can send to Jaeger, Honeycomb, Grafana, or just a file.
74
+ - **Who's driving.** You sign in once in your browser; Codex (and any sub-agents it spawns) each get their own identity. No tokens to copy around, and the "who acted on whose behalf" trail stays queryable later.
75
+ - **What it's allowed to do.** Before a tool runs, Ory checks whether it's permitted. It starts in **observe mode** — nothing is blocked, you just *see* what would be — so it never gets in your way on day one.
51
76
 
52
- If Ory is ever unreachable, the plugin gets out of the way and lets Codex keep working — so it can't lock you out. That also means enforcement is only as strong as the permissions you grant.
77
+ If Ory is ever unreachable, the plugin gets out of the way and lets Codex keep working — so it can't lock you out.
53
78
 
54
79
  ### See what's happening
55
80
 
56
- Everything the plugin does is observable out of the box — no configuration required:
81
+ Everything the plugin does is observable out of the box:
57
82
 
58
- - **Status at a glance.** `npx -y -p @ory/codex ory-codex status` shows what's configured, who's signed in, how many built-in tools your permissions cover, and the most recent tool-call activity.
59
- - **Live dashboard.** `npx -y -p @ory/codex ory-codex dashboard` opens the same picture in your browser — configuration, identities, permission coverage, Ory service health, and the latest tool-call activity, all refreshing live. From there you can flip **enforcement** on or off, toggle **user login**, and use **Change stack** to reconnect to a different Ory — no CLI required.
60
- - **Live traces.** Every tool call is recorded as an OpenTelemetry-style span. Watch them stream as the agent works:
83
+ - **Activity log.** Privacy-safe activity is always appended to `~/.config/ory-agent-plugins/codex/ory-agent-debug.log`. View events, decisions, and errors live with:
61
84
 
62
85
  ```bash
63
86
  npx -y -p @ory/codex ory-codex watch
64
87
  ```
65
88
 
66
- Spans are also written to `~/.config/ory-agent-plugins/codex/ory-agent-trace.ndjson` (NDJSON, one span per line) — tail that file, or point `OTEL_EXPORTER_OTLP_ENDPOINT` at a collector to ship them straight to Jaeger, Honeycomb, or Grafana.
67
- - **Debug log.** For a verbose play-by-play, set `ORY_AGENT_DEBUG=true`; structured logs land in `~/.config/ory-agent-plugins/codex/ory-agent-debug.log`.
89
+ Set `ORY_AGENT_LOG_FILE` to override the path; set it empty to disable file persistence.
90
+ - **Live debug.** Launch Codex with `ORY_AGENT_DEBUG=true` to add verbose local diagnostics, including raw shell commands, to the watched log and stderr. Secrets are recursively redacted; pass `watch --json` for NDJSON.
68
91
 
69
92
  ### Ready to enforce?
70
93
 
71
- When the watch-mode logs look right, turn on blocking with one command (setup already granted you the built-in tools):
94
+ Once connected, the deny posture lives on the Ory project: when the observe-mode activity looks right, an admin promotes it to **enforce** in the **Ory Console** (Agent Security). Every session reads that posture live, so nothing has to be reinstalled.
72
95
 
73
96
  ```bash
74
- npx -y -p @ory/codex ory-codex permissions enforce
97
+ npx -y -p @ory/codex ory-codex permissions # what the project grants, and the live mode
75
98
  ```
76
99
 
77
- Now a denied tool is actually blocked and Codex shows why. Go back to watch mode anytime with `permissions observe`. Prefer clicking? The dashboard has the same **Enforce** switch — flip it on, or back to watch mode, without touching the CLI. Use `permissions status` to see what's covered and `permissions bootstrap` to (re-)grant the built-in tools — or just ask Codex in chat, e.g. *"grant me use of the shell tool."*
78
-
79
- ## Also: add login to your own app
80
-
81
- Beyond securing Codex, the plugin helps you build Ory into whatever you're working on. Ask Codex *"add Ory login to this app"* — or pick `ory-auth-setup` from the `/skills` menu — and it scaffolds the login, registration, recovery, and settings pages (using [Ory Elements](https://github.com/ory/elements)) wired to a local Ory, so no signup or keys are needed. Start that local Ory with the `ory-local-up` skill (it prints a test email + password to sign in with) and tear it down with `ory-local-down`.
100
+ Then a denied tool is actually blocked and Codex shows why.
82
101
 
83
- Bundled **skills** (ask in plain language, or pick from `/skills`) cover more: `ory-auth-setup`, `ory-login-flow`, `ory-social-login` (Google, GitHub, Apple…), `ory-permissions-onboarding`, and playbooks for wiring Ory into your own agents, E2B sandboxes, or Temporal workers. A built-in **Ory tool server** lets Codex manage identities, projects, and permissions straight from chat.
102
+ ## Connect to Ory Agent Security
84
103
 
85
- ## Configure by hand (CI / advanced)
104
+ Copy the connection details from the [Ory Console](https://console.ory.sh) under **Agent Security**:
86
105
 
87
- The guided setup covers most people. For scripted or CI setups, or to point at an existing Ory Network project, connect directly. Settings are saved to `~/.config/ory-agent-plugins/config.json` and shared across all your Ory agent plugins; environment variables win when both are set.
106
+ | Value | Flag | Environment variable |
107
+ |---|---|---|
108
+ | Project URL | `--project-url` | `ORY_PROJECT_URL` |
109
+ | Agent Security URL | `--agent-security-url` | `ORY_AGENT_SECURITY_URL` |
110
+ | Sign-in client id override (default `ory-agent-security-login`) | `--oauth2-client-id` | `ORY_OAUTH2_CLIENT_ID` |
88
111
 
89
112
  ```bash
90
113
  npx -y -p @ory/codex ory-codex configure \
91
114
  --project-url https://<slug>.projects.oryapis.com \
92
- --oauth2-client-id <sign-in client id>
115
+ --agent-security-url https://agents.console.ory.com
93
116
  ```
94
117
 
95
- Codex's own identity registers itself automatically on first run — nothing to create. The `--oauth2-client-id` is the one piece browser sign-in needs; the guided setup makes it for you, or see below to do it by hand. For logging-only with no checks, use `--audit-only`.
118
+ `install` accepts the same flags, so you can register the plugin **and** connect in one shot (`install --project-url <URL> --agent-security-url <URL>`). The login client defaults to `ory-agent-security-login`; custom deployments can override it with `--oauth2-client-id`. Existing configurations fall back to the project URL when the Agent Security URL is unset. To turn sign-in and checks back off later, use `configure --disconnect`.
96
119
 
97
- <details>
98
- <summary>Create the sign-in client by hand</summary>
99
-
100
- The guided setup normally does this. To do it yourself, create a **public** OAuth2 client (no secret) listing all four loopback URLs — the plugin tries each in turn at runtime so sign-in survives a busy port, and Ory only accepts a callback on a URL you registered:
101
-
102
- ```bash
103
- ory create oauth2-client --project <project-id> \
104
- --name "Ory Agent Security · user login (PKCE)" \
105
- --grant-type authorization_code,refresh_token \
106
- --response-type code \
107
- --scope openid,offline_access \
108
- --token-endpoint-auth-method none \
109
- --redirect-uri http://127.0.0.1:47823/callback \
110
- --redirect-uri http://127.0.0.1:47824/callback \
111
- --redirect-uri http://127.0.0.1:47825/callback \
112
- --redirect-uri http://127.0.0.1:47826/callback
113
- ```
120
+ **Ory Network or OEL.** Either works. For an Ory Network project the URL is `https://<slug>.projects.oryapis.com`; for a self-hosted **Ory Enterprise License** deployment, point `--project-url` at that deployment's base URL and use the sign-in client id from its Agent Security configuration. Everything downstream — sign-in, checks, delegation — is identical.
114
121
 
115
- …or in the [Ory Console](https://console.ory.sh) under *OAuth2* → *Clients* → *Create client* (pick "Public client", set "Authorization Code" + "Refresh Token" grants, scopes `openid offline_access`, paste the four URLs above). Pass the resulting id to `configure --oauth2-client-id`. Running headless with a session token already? Set `ORY_USER_SESSION_TOKEN` and skip the browser step entirely.
122
+ **There is nothing else for you to create.** The sign-in client, the permission model, the per-tool grants and blocks, and the observe/enforce posture are all provisioned in the Console by someone with project access. At runtime the plugin only *reads* permissions — it has no write path into your project, which is why installing it needs no workspace privilege. Each Codex session registers its own identity automatically on first use.
116
123
 
117
- </details>
124
+ Sign-in runs at the start of every session and never blocks — a declined, skipped, or timed-out login simply leaves that session without a user identity.
118
125
 
119
- With nothing configured, the plugin still loads and runs in **pass-through mode**: skills, commands, and logging work, but no checks run and nothing is blocked. Perfectly fine if you only want the app-building features.
126
+ Settings are saved to `~/.config/ory-agent-plugins/config.json` and shared across all your Ory agent plugins; environment variables win when both are set. Running headless with an OAuth2 access token already? Set `ORY_USER_OAUTH2_TOKEN` and the browser step is skipped entirely.
120
127
 
121
128
  ## Commands
122
129
 
123
130
  ```
124
- ory-codex install | uninstall Install/remove; --reconfigure re-runs setup, --no-web forces the terminal wizard, --no-configure skips it
131
+ ory-codex install | uninstall Install (add --project-url to also connect Agent Security) / remove
125
132
  ory-codex status Show configuration, identities, permission coverage, recent activity
126
- ory-codex dashboard Open the live browser dashboard (status + service health + Change stack)
127
- ory-codex watch Tail the live trace stream (OTel spans)
128
- ory-codex permissions <cmd> status | bootstrap | observe (watch) | enforce (block)
129
- ory-codex configure <flags> Point at a project by hand (--project-url, --oauth2-client-id, --audit-only)
133
+ ory-codex permissions <cmd> status (read-only; grants + posture live in the Ory Console)
134
+ ory-codex configure <flags> Connect a project (--project-url) or --disconnect
130
135
  ory-codex agent <status|unregister> Manage Codex's own auto-created identity
131
136
  ory-codex local <up|down|status|…> Run / manage a local Ory in Docker
137
+ ory-codex version Print plugin, core, and Node versions (--json for machine-readable)
132
138
  ```
133
139
 
134
140
  All prefixed with `npx -y -p @ory/codex`.
135
141
 
136
- The local stack runs a complete Ory on your laptop: the Ory APIs at `http://localhost:4000`, a login UI on `:4455` (not :3000, to avoid Next.js port conflicts), the Ory Console on `:4100`, and Jaeger (the trace viewer) on `:16686`.
142
+ The local stack runs a complete Ory on your laptop: the Ory APIs at `http://localhost:4000`, a login UI on `:4455` (not :3000, to avoid Next.js port conflicts), and the Ory Console on `:4100`.
137
143
 
138
144
  ## Troubleshooting
139
145
 
140
146
  - **`local up` fails** — make sure Docker is running and ports `4000`, `4100`, `4455`, and `16686` are free.
141
- - **Browser sign-in loops** — reset with `ory-codex agent unregister` and try again.
142
- - **Install ran but never asked how to connect** — you're almost certainly on a stale `npx` cache. `npx -p @ory/codex` (no version pin) reuses a previously-downloaded copy instead of re-resolving to the latest, so an older CLI whose install predates the setup wizard can run while you believe you're on the current release. The install banner prints the running version; confirm it with `npx -y -p @ory/codex ory-codex version`. To force the current release, clear the cache and reinstall: `rm -rf ~/.npm/_npx` then `npx -y -p @ory/codex ory-codex install`. Pinning an exact version (`@ory/codex@<version>`) also bypasses the cached copy.
147
+ - **Browser sign-in loops** (after connecting) — reset with `ory-codex agent unregister` and try again.
148
+ - **Running an older CLI than expected** — `npx -p @ory/codex` (no version pin) reuses a previously-downloaded copy instead of re-resolving to the latest. The install banner prints the running version; confirm it with `npx -y -p @ory/codex ory-codex version`. To force the current release, clear the cache and reinstall: `rm -rf ~/.npm/_npx` then `npx -y -p @ory/codex ory-codex install`. Pinning an exact version (`@ory/codex@<version>`) also bypasses the cached copy.
143
149
  - **`npm install … ENOVERSIONS`** — if your `~/.npmrc` sets `min-release-age`, npm hides versions newer than that. Override per-call: `npm_config_min_release_age=0 npx -y -p @ory/codex ory-codex install`.
144
150
  - **`codex doctor` says `ory-mcp-server is not resolvable`** — the bundled tool server is fetched on demand via `npx`, so make sure `npm`/`npx` is on your PATH. The first session downloads it; later ones reuse the cache.
145
- - **Want to see what's happening** — `npx -y -p @ory/codex ory-codex status` for a snapshot, `npx -y -p @ory/codex ory-codex watch` for the live trace stream, or set `ORY_AGENT_DEBUG=true` for a verbose log. Traces and logs live under `~/.config/ory-agent-plugins/codex/` (see [See what's happening](#see-whats-happening)).
146
151
 
147
152
  ## Learn more
148
153
 
@@ -7,7 +7,7 @@
7
7
  * npx ory-codex uninstall Remove the plugin via `codex plugin remove`
8
8
  * npx ory-codex configure Set or view Ory project URL and API key
9
9
  * npx ory-codex agent <cmd> Manage the agent's OAuth2 identity
10
- * npx ory-codex permissions <cmd> Manage permission mode and tool permissions
10
+ * npx ory-codex permissions Show permission mode and per-tool coverage
11
11
  * npx ory-codex local <cmd> Manage local Ory dev environment
12
12
  * npx ory-codex status Show plugin status and configuration
13
13
  */
package/dist/cli/main.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * npx ory-codex uninstall Remove the plugin via `codex plugin remove`
9
9
  * npx ory-codex configure Set or view Ory project URL and API key
10
10
  * npx ory-codex agent <cmd> Manage the agent's OAuth2 identity
11
- * npx ory-codex permissions <cmd> Manage permission mode and tool permissions
11
+ * npx ory-codex permissions Show permission mode and per-tool coverage
12
12
  * npx ory-codex local <cmd> Manage local Ory dev environment
13
13
  * npx ory-codex status Show plugin status and configuration
14
14
  */
@@ -53,8 +53,17 @@ const setup_js_1 = require("./setup.js");
53
53
  const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
54
54
  function main() {
55
55
  const [command, ...args] = process.argv.slice(2);
56
+ // `--help` after a matched subcommand: print usage, do nothing else.
57
+ // Without this the switch below ignores the flag and `install --help`
58
+ // performs a real install (#221).
59
+ if ((0, argus_1.shouldPrintHelp)(command, args))
60
+ return help();
56
61
  switch (command) {
57
62
  case "install":
63
+ if (args.includes("--print")) {
64
+ (0, setup_js_1.printInstallPlan)();
65
+ break;
66
+ }
58
67
  (0, argus_1.beginDeferNextSteps)();
59
68
  (0, setup_js_1.install)(args);
60
69
  (0, argus_1.runPostInstall)("ory-codex", "codex", args).then(() => process.exit(0), (err) => {
@@ -64,7 +73,10 @@ function main() {
64
73
  break;
65
74
  case "uninstall":
66
75
  (0, setup_js_1.uninstall)(args);
67
- (0, argus_1.clearCredentialsForUninstall)().then(() => process.exit(0), (err) => {
76
+ (0, argus_1.clearCredentialsForUninstall)({
77
+ harness: "codex",
78
+ purge: args.includes("--purge"),
79
+ }).then(() => process.exit(0), (err) => {
68
80
  console.error(err.message ?? err);
69
81
  process.exit(1);
70
82
  });
@@ -73,7 +85,7 @@ function main() {
73
85
  (0, argus_1.runConfigureCommand)("ory-codex", args);
74
86
  break;
75
87
  case "agent":
76
- (0, argus_1.runAgentCommand)("ory-codex", args).then((code) => process.exit(code), (err) => {
88
+ (0, argus_1.runAgentCommand)("ory-codex", "codex", args).then((code) => process.exit(code), (err) => {
77
89
  console.error(err.message ?? err);
78
90
  process.exit(1);
79
91
  });
@@ -90,17 +102,11 @@ function main() {
90
102
  process.exit(1);
91
103
  });
92
104
  break;
93
- case "local":
94
- (0, argus_1.runLocalCommand)("ory-codex", args).catch((err) => {
95
- console.error(err.message ?? err);
96
- process.exit(1);
97
- });
98
- break;
99
105
  case "watch":
100
- (0, argus_1.runWatchCommand)("codex", args);
106
+ (0, argus_1.runWatchCommand)("ory-codex", "codex", args).then((code) => process.exit(code), (err) => { console.error(err.message ?? err); process.exit(1); });
101
107
  break;
102
- case "dashboard":
103
- (0, argus_1.runDashboardCommand)("ory-codex", "codex", args).then((code) => process.exit(code), (err) => {
108
+ case "local":
109
+ (0, argus_1.runLocalCommand)("ory-codex", args).catch((err) => {
104
110
  console.error(err.message ?? err);
105
111
  process.exit(1);
106
112
  });
@@ -126,6 +132,15 @@ async function status() {
126
132
  await (0, argus_1.runStatusCommand)("ory-codex", "codex", {
127
133
  title: "Codex",
128
134
  printPluginSection: () => {
135
+ // A wiped data dir leaves the harness pointing at a marketplace root
136
+ // that no longer exists, and its plugin surface then fails with an error
137
+ // that never mentions Ory. Say so here, with the repair (#206).
138
+ const dangling = (0, argus_1.findDanglingRegistration)("codex");
139
+ if (dangling) {
140
+ console.log("");
141
+ console.log(` ! ${(0, argus_1.describeDanglingRegistration)(dangling)}`);
142
+ console.log("");
143
+ }
129
144
  console.log("Hooks & plugin:");
130
145
  console.log(` Directory: ${PACKAGE_ROOT}`);
131
146
  console.log(` Hook script: ${fs.existsSync(path.join(PACKAGE_ROOT, "dist", "hook.js")) ? "built" : "NOT BUILT (run pnpm build)"}`);
@@ -144,20 +159,20 @@ Commands:
144
159
  uninstall Remove via \`codex plugin remove\` + \`codex plugin marketplace remove\`
145
160
  configure Set or view Ory project URL and API key
146
161
  agent <cmd> Manage the agent's OAuth2 identity (status, unregister)
147
- permissions <cmd> Manage permission mode and tool permissions
148
- (status, bootstrap, observe, enforce)
162
+ permissions Show the live permission mode and per-tool coverage
149
163
  local <cmd> Manage local Ory dev environment
150
164
  (up, down, status, seed, logs, env, configure, reset)
151
165
  status Show plugin status, config, and recent log lines
152
- dashboard Open the Ory Agent dashboard (system health + configuration)
153
- watch [trace-file] Tail the trace stream (OTel spans) live
166
+ watch [--json] [--lines <count>]
167
+ Follow the live activity/debug log
154
168
  version Show version and the ory-agent-plugins build commit
155
169
 
156
170
  Examples:
157
171
  npx -y -p @ory/codex ory-codex install
158
172
  npx -y -p @ory/codex ory-codex configure --project-url https://<slug>.projects.oryapis.com \\
159
- --api-key ory_pat_...
160
- npx -y -p @ory/codex ory-codex permissions status
173
+ --agent-security-url https://agents.console.ory.com \\
174
+ --oauth2-client-id <id>
175
+ npx -y -p @ory/codex ory-codex permissions
161
176
  npx -y -p @ory/codex ory-codex status
162
177
  npx -y -p @ory/codex ory-codex uninstall
163
178
 
@@ -24,5 +24,6 @@
24
24
  * npx ory-codex-setup --uninstall Remove
25
25
  * npx ory-codex-setup --help Show this help
26
26
  */
27
- export declare function install(_args: string[]): void;
27
+ export declare function install(args: string[]): void;
28
+ export declare function printInstallPlan(): void;
28
29
  export declare function uninstall(_args: string[]): void;
package/dist/cli/setup.js CHANGED
@@ -60,11 +60,13 @@ var __importStar = (this && this.__importStar) || (function () {
60
60
  })();
61
61
  Object.defineProperty(exports, "__esModule", { value: true });
62
62
  exports.install = install;
63
+ exports.printInstallPlan = printInstallPlan;
63
64
  exports.uninstall = uninstall;
64
65
  const node_child_process_1 = require("node:child_process");
65
66
  const fs = __importStar(require("node:fs"));
66
67
  const path = __importStar(require("node:path"));
67
68
  const argus_1 = require("@ory/argus");
69
+ const session_state_js_1 = require("../session-state.js");
68
70
  const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
69
71
  const PACKAGE_VERSION = readPackageVersion();
70
72
  const MARKETPLACE_NAME = "ory";
@@ -81,17 +83,16 @@ const RENDER_OPTS = {
81
83
  */
82
84
  const MARKETPLACE_ROOT = path.join((0, argus_1.getHarnessDataDir)("codex"), "marketplace");
83
85
  const PLUGIN_ROOT = path.join(MARKETPLACE_ROOT, "plugins", PLUGIN_NAME);
84
- /** Hook command that resolves via the npm registry on every invocation. */
85
- const HOOK_COMMAND = "npx -y -p @ory/codex ory-codex-hook";
86
86
  /**
87
87
  * Per-handler timeout (seconds). Codex kills a hook subprocess after this
88
88
  * window (its built-in default is only 5s). SessionStart may run the
89
89
  * interactive user login (PKCE browser flow), which needs a human in the
90
90
  * loop, so it gets a generous window; the tool-lifecycle hooks only make a
91
- * permission check, so a shorter window is plenty.
91
+ * permission check, so a shorter window is plenty. Derived in core so the
92
+ * session-start window cannot drift below the login timeout it must outlast.
92
93
  */
93
- const SESSION_START_TIMEOUT_SEC = 300;
94
- const TOOL_HOOK_TIMEOUT_SEC = 60;
94
+ const SESSION_START_TIMEOUT_SEC = (0, argus_1.sessionStartHookTimeout)("codex", "seconds");
95
+ const TOOL_HOOK_TIMEOUT_SEC = (0, argus_1.toolHookTimeout)("codex", "seconds");
95
96
  function readPackageVersion() {
96
97
  try {
97
98
  const pj = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf-8"));
@@ -180,45 +181,61 @@ function renderCommands() {
180
181
  * occurrence. Emitting the handler flat (without the wrapping group) parses
181
182
  * as a matcher group with zero handlers, so the hook silently never runs.
182
183
  */
183
- function hookGroup(timeoutSec) {
184
+ function hookGroup(command, timeoutSec) {
184
185
  return [
185
186
  {
186
- hooks: [{ type: "command", command: (0, argus_1.pinHookToLocalRegistry)(HOOK_COMMAND), timeout: timeoutSec }],
187
+ hooks: [{ type: "command", command, timeout: timeoutSec }],
187
188
  },
188
189
  ];
189
190
  }
190
191
  /**
191
- * Generate the hooks file. Hook command resolves the bin via `npx`, so the
192
- * path is stable across npm cache evictions and version bumps.
192
+ * Generate the hooks file. Every event points at the runtime shim resolved at
193
+ * install time, so nothing is re-resolved per tool call.
193
194
  *
194
195
  * Codex only loads this file when `plugin.json` declares `hooks` (see the
195
196
  * marketplace skeleton); it is not auto-discovered by filename. Fresh plugin
196
197
  * hooks are also untrusted until the user reviews them in Codex's hook-trust
197
198
  * UI — see `printNextSteps`.
198
199
  */
199
- function writeHooks() {
200
+ function writeHooks(command) {
200
201
  const hooksPath = path.join(PLUGIN_ROOT, "hooks.json");
201
202
  fs.writeFileSync(hooksPath, JSON.stringify({
202
203
  hooks: {
203
- SessionStart: hookGroup(SESSION_START_TIMEOUT_SEC),
204
- PreToolUse: hookGroup(TOOL_HOOK_TIMEOUT_SEC),
205
- PostToolUse: hookGroup(TOOL_HOOK_TIMEOUT_SEC),
206
- PermissionRequest: hookGroup(TOOL_HOOK_TIMEOUT_SEC),
207
- UserPromptSubmit: hookGroup(TOOL_HOOK_TIMEOUT_SEC),
208
- Stop: hookGroup(TOOL_HOOK_TIMEOUT_SEC),
204
+ SessionStart: hookGroup(command, SESSION_START_TIMEOUT_SEC),
205
+ PreToolUse: hookGroup(command, TOOL_HOOK_TIMEOUT_SEC),
206
+ PostToolUse: hookGroup(command, TOOL_HOOK_TIMEOUT_SEC),
207
+ PermissionRequest: hookGroup(command, TOOL_HOOK_TIMEOUT_SEC),
208
+ UserPromptSubmit: hookGroup(command, SESSION_START_TIMEOUT_SEC),
209
+ Stop: hookGroup(command, TOOL_HOOK_TIMEOUT_SEC),
209
210
  },
210
211
  }, null, 2) + "\n");
211
212
  }
213
+ /**
214
+ * Rewrite the marketplace's `.mcp.json` so the Ory server starts from the
215
+ * resolved runtime. The static skeleton ships an `npx` invocation; replacing it
216
+ * here keeps the registry off the runtime path. Removed outright when the
217
+ * runtime has no MCP server — better none than one that cannot start.
218
+ */
219
+ function writeMcpConfig(server) {
220
+ const mcpPath = path.join(PLUGIN_ROOT, ".mcp.json");
221
+ if (!server) {
222
+ fs.rmSync(mcpPath, { force: true });
223
+ return;
224
+ }
225
+ fs.writeFileSync(mcpPath, JSON.stringify({ mcpServers: { ory: server } }, null, 2) + "\n");
226
+ }
212
227
  /**
213
228
  * Assemble the full marketplace tree in the persistent location. Idempotent.
214
229
  */
215
- function assembleMarketplace() {
230
+ function assembleMarketplace(runtime) {
231
+ (0, session_state_js_1.clearCodexSessionStarts)();
216
232
  fs.mkdirSync(MARKETPLACE_ROOT, { recursive: true });
217
233
  copyMarketplaceSkeleton();
218
234
  updatePluginManifest();
219
235
  renderSkills();
220
236
  renderCommands();
221
- writeHooks();
237
+ writeHooks((0, argus_1.requireHookCommand)(runtime));
238
+ writeMcpConfig(runtime.mcpServer);
222
239
  }
223
240
  /**
224
241
  * Register (or refresh) the local marketplace with Codex and install the
@@ -245,20 +262,52 @@ function registerWithCodex() {
245
262
  }
246
263
  }
247
264
  }
248
- function install(_args) {
265
+ function install(args) {
249
266
  if (!checkCodexCli()) {
250
267
  console.error("Error: 'codex' CLI not found in PATH.");
251
268
  console.error("Install Codex first: https://github.com/openai/codex");
252
269
  process.exit(1);
253
270
  }
271
+ const runtime = (0, argus_1.wireRuntime)({
272
+ harness: "codex",
273
+ packageName: "@ory/codex",
274
+ packageRoot: PACKAGE_ROOT,
275
+ installCommand: "npx -y -p @ory/codex ory-codex install",
276
+ args,
277
+ });
278
+ console.log(runtime.target.kind === "linked"
279
+ ? `Runtime: linked to ${runtime.target.packageDir} (dev)`
280
+ : `Runtime: ${runtime.target.packageName}@${runtime.target.version} in ${runtime.target.storeDir}`);
281
+ for (const pruned of runtime.prunedStores) {
282
+ console.log(` Removed stale runtime: ${pruned}`);
283
+ }
254
284
  console.log(`Assembling Ory plugin (skills, commands, hooks, MCP) at:`);
255
285
  console.log(` ${PLUGIN_ROOT}`);
256
- assembleMarketplace();
286
+ assembleMarketplace(runtime);
257
287
  console.log(`Registering marketplace and installing ${PLUGIN_REF}...`);
258
288
  registerWithCodex();
289
+ // Codex stores this path and re-reads it on every run, so a data-dir wipe
290
+ // that skips uninstall would break every `codex plugin` command. Record how
291
+ // to undo the registration so the destructive paths can replay it (#206).
292
+ (0, argus_1.recordExternalRegistration)({
293
+ harness: "codex",
294
+ tool: "codex",
295
+ description: `Codex marketplace "${MARKETPLACE_NAME}"`,
296
+ root: MARKETPLACE_ROOT,
297
+ remove: { command: "codex", args: ["plugin", "marketplace", "remove", MARKETPLACE_NAME] },
298
+ });
259
299
  console.log(` Plugin installed.`);
260
300
  printNextSteps();
261
301
  }
302
+ function printInstallPlan() {
303
+ console.log(JSON.stringify({
304
+ marketplaceRoot: MARKETPLACE_ROOT,
305
+ plugin: PLUGIN_REF,
306
+ hooks: 'node "<runtime shim>"',
307
+ mcpServer: { command: "node", args: ["<MCP runtime shim>"] },
308
+ assets: ["skills/", "commands/"],
309
+ }, null, 2));
310
+ }
262
311
  function uninstall(_args) {
263
312
  if (!checkCodexCli()) {
264
313
  console.error("Error: 'codex' CLI not found in PATH.");
@@ -283,6 +332,12 @@ function uninstall(_args) {
283
332
  fs.rmSync(MARKETPLACE_ROOT, { recursive: true, force: true });
284
333
  console.log(` Removed ${MARKETPLACE_ROOT}`);
285
334
  }
335
+ // Deregistered above, so nothing is left to undo.
336
+ (0, argus_1.clearExternalRegistration)("codex");
337
+ (0, argus_1.removeRuntimeWiring)("codex");
338
+ for (const pruned of (0, argus_1.pruneRuntimeStores)()) {
339
+ console.log(` Removed runtime: ${pruned}`);
340
+ }
286
341
  }
287
342
  function printNextSteps() {
288
343
  (0, argus_1.nextStepsSink)(({ configured }) => configured ? printConfiguredNextStepsNow() : printNextStepsNow());
@@ -301,19 +356,19 @@ function printConfiguredNextStepsNow() {
301
356
  console.log(" review and trust the Ory hooks on first launch; the auth gate and");
302
357
  console.log(" per-tool permission checks only run once they're trusted.");
303
358
  console.log("");
304
- if ((0, argus_1.resolveConfig)().auditOnly) {
305
- console.log(" Audit-only mode: tool calls are traced locally — no checks, nothing blocked.");
359
+ if (!(0, argus_1.isSecurityConnected)()) {
360
+ console.log(" Agent Security not connected: tool activity is recorded locally — no checks, nothing blocked.");
306
361
  console.log("");
307
- console.log(` 1. See what's been traced: ${npx} status (or watch live: ${npx} watch)`);
308
- console.log(" 2. More detail? export ORY_AGENT_DEBUG=true");
362
+ console.log(` 1. See recorded activity: ${npx} status`);
363
+ console.log(" 2. Stream activity live: export ORY_AGENT_DEBUG=true");
309
364
  }
310
365
  else {
311
- console.log(" It starts in watch mode: every tool call is checked, nothing blocked yet.");
366
+ console.log(" It starts in observe mode: every tool call is checked, nothing blocked yet.");
312
367
  console.log("");
313
- console.log(` 1. See what Ory is doing: ${npx} status (or watch live: ${npx} watch)`);
314
- console.log(` 2. Turn on enforcement: ${npx} permissions enforce`);
315
- console.log(` (back to watch: ${npx} permissions observe)`);
316
- console.log(" 3. More detail? export ORY_AGENT_DEBUG=true");
368
+ console.log(` 1. See what Ory is doing: ${npx} status`);
369
+ console.log(" 2. Turn on enforcement: in the Ory Console (Agent Security)");
370
+ console.log(` (see the live mode: ${npx} permissions)`);
371
+ console.log(" 3. Stream activity live: export ORY_AGENT_DEBUG=true");
317
372
  }
318
373
  console.log("");
319
374
  console.log(`To uninstall: ${npx} uninstall`);
@@ -323,18 +378,15 @@ function printNextStepsNow() {
323
378
  console.log("Next steps:");
324
379
  console.log(" 1. (Optional) Point at an Ory project — without this, the plugin");
325
380
  console.log(" runs in pass-through mode (skills work, nothing is blocked):");
326
- console.log(" npx -y -p @ory/codex ory-codex configure --project-url https://<slug>.projects.oryapis.com \\");
327
- console.log(" --oauth2-client-id <public OAuth2 client id>");
328
- console.log(" (`--oauth2-client-id` is required when --project-url is set, because the");
329
- console.log(" user PKCE browser flow needs a pre-registered public client. Add");
330
- console.log(" `--api-key ory_pat_...` only to override the agent's auto-registered identity.)");
381
+ console.log(" npx -y -p @ory/codex ory-codex configure --project-url https://<slug>.projects.oryapis.com");
382
+ console.log(" The broker and public login client use their production defaults;");
383
+ console.log(" --agent-security-url and --oauth2-client-id override them.");
331
384
  console.log("");
332
385
  console.log(" 2. Or spin up the local Ory stack from inside Codex (`/skills` -> `ory-local-up`).");
333
386
  console.log("");
334
- console.log(" 3. Provide the OAuth2 client id for the per-session user login:");
387
+ console.log(" 3. For a custom deployment, override the per-session login client:");
335
388
  console.log(" export ORY_OAUTH2_CLIENT_ID=<public OAuth2 client id>");
336
- console.log(" The user login runs every session; without a client id Codex runs without a");
337
- console.log(" human Ory identity attached and permission checks fall back to a session:<id> subject.");
389
+ console.log(" Otherwise user login uses the reserved ory-agent-security-login client.");
338
390
  console.log("");
339
391
  console.log(" 4. Start a Codex session. Codex treats freshly installed plugin");
340
392
  console.log(" hooks as untrusted, so it will prompt you to review and trust");
@@ -371,7 +423,10 @@ refreshes Codex's cache copy on the next session.
371
423
  uninstall(args);
372
424
  // Direct `-setup --uninstall` path: also clear stored Ory credentials.
373
425
  // (The `ory-codex uninstall` command handles this itself.)
374
- (0, argus_1.clearCredentialsForUninstall)().then(() => process.exit(0), (err) => {
426
+ (0, argus_1.clearCredentialsForUninstall)({
427
+ harness: "codex",
428
+ purge: process.argv.includes("--purge"),
429
+ }).then(() => process.exit(0), (err) => {
375
430
  console.error(err.message ?? err);
376
431
  process.exit(1);
377
432
  });