@ory/openclaw 0.13.9 → 1.0.0

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 [OpenClaw](https://github.com/openclaw/openclaw), powered by [Ory](https://ory.com).
4
4
 
5
- **Security.** OpenClaw 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; OpenClaw 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
+ OpenClaw 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, an MCP 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,163 +18,126 @@ Security and developer experience for [OpenClaw](https://github.com/openclaw/ope
15
18
 
16
19
  ## Get started
17
20
 
18
- 1. **Build auth into your app.** Have OpenClaw scaffold Ory login, registration, social sign-in, and permissions into the project you're working on, backed by the local stack. This is the Quickstart below it needs nothing but Docker.
19
- 2. **Govern the agent itself.** Authenticate OpenClaw's own session and authorize every tool it runs against Ory Permissions, with a full audit trail. See [Agent security](#agent-security).
20
-
21
- If you're just exploring, do the Quickstart first.
22
-
23
- ## Prerequisites
24
-
25
- - [OpenClaw](https://github.com/openclaw/openclaw) installed and configured
26
- - Node.js **≥ 22**
27
- - [Docker](https://docs.docker.com/get-docker/) (only needed for the local Ory stack)
28
- - macOS or Linux. Windows works via WSL2.
29
-
30
- ## Install
31
-
32
- OpenClaw discovers plugins by their manifest id (`ory-agent-security`) under the nested `plugins` object in `.openclaw/config.json` — it does not scan `node_modules`, so the plugin's location has to be registered. Two ways to do that:
33
-
34
- **Recommended — OpenClaw's plugin ecosystem.** Copies the plugin into OpenClaw's managed extensions directory and enables it by id:
21
+ OpenClaw loads in-process plugins by module name plus an entry in `.openclaw/config.json` it doesn't scan `node_modules`, so the plugin has to be registered. Install the package, then register it.:
35
22
 
36
23
  ```bash
37
- openclaw plugins install npm:@ory/openclaw
38
- openclaw plugins enable ory-agent-security
24
+ npm install @ory/openclaw # so OpenClaw can resolve the package
25
+ npx -y -p @ory/openclaw ory-openclaw install # register + enable in .openclaw/config.json
39
26
  ```
40
27
 
41
- **Or let the Ory CLI write the config.** This registers the installed package directory on `plugins.load.paths` and sets `plugins.entries.ory-agent-security.enabled = true`, and also installs the Ory skills and MCP server. Install the package into the project first so it has a stable path for OpenClaw to load:
42
-
43
- ```bash
44
- npm install @ory/openclaw # so OpenClaw can resolve the package
45
- npx @ory/openclaw install # register + enable in .openclaw/config.json
46
- npx @ory/openclaw install --project-dir <path>
47
- npx @ory/openclaw uninstall
48
- Run one command. It installs the plugin and walks you through connecting:
28
+ Registering also lays down the Ory skills (via `skills.load.extraDirs`) and the Ory MCP server. Confirm everything landed with:
49
29
 
50
30
  ```bash
51
- npx -y -p @ory/openclaw ory-openclaw install
31
+ npx -y -p @ory/openclaw ory-openclaw status
52
32
  ```
53
33
 
54
- OpenClaw loads plugins that run inside its own process, so the installer registers the plugin in `.openclaw/config.json` (along with its skills and the Ory MCP server), then walks you through connecting. You'll be asked how you want to connect **press Enter for the default**:
34
+ `status` is your one-stop check: what's configured, who's signed in, which tools are covered by permissions, plugin and skill registration, and recent activity. Until you connect Agent Security, the identity and permission rows say so and name what's missing.
55
35
 
56
- - **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.
57
- - **Local** — run a complete Ory on your laptop with Docker. No account, no signup, no keys. Great for trying it out.
58
- - **Audit-only** — skip Ory entirely and just log what OpenClaw does.
36
+ > Prefer OpenClaw's plugin ecosystem? Run `openclaw plugins install npm:@ory/openclaw` then `openclaw plugins enable ory-agent-security`. That registers the plugin the same way, with Agent Security not connected. Run `npx -y -p @ory/openclaw ory-openclaw install` afterwards to lay down the Ory skills and MCP server. Point the installer at another project with `install --project-dir <path>`.
59
37
 
60
- That's it. Confirm everything landed with:
38
+ ## Skills and commands
61
39
 
62
- ```bash
63
- npx -y -p @ory/openclaw ory-openclaw status
64
- ```
65
-
66
- `status` is your one-stop check: what's configured, who's signed in, which tools are covered by permissions, plugin and skill registration, and recent activity. Anything not set up yet shows as `(unset)`.
40
+ Installing the plugin drops the full Ory playbook catalog into OpenClaw. **Skills** are model-invoked — say what you want in plain language, or pick one from OpenClaw's skill picker.
67
41
 
68
- Re-run install with `--reconfigure` to change your connection later, or `--no-configure` to skip the wizard.
42
+ | Skill | What it does for you |
43
+ |---|---|
44
+ | `ory-auth-setup` | Adds a complete auth system to your app — login, registration, recovery, verification, settings — on [Ory Elements](https://github.com/ory/elements) |
45
+ | `ory-login-flow` | Builds just the pages, wired to Ory's self-service flows |
46
+ | `ory-social-login` | "Sign in with…" for Google, GitHub, Apple, Microsoft, Discord, Slack, GitLab, Facebook |
47
+ | `ory-local-dev` | Develops and tests login/permission flows against a local Ory — no project, no account, offline |
48
+ | `ory-permissions-onboarding` | Walks a fresh install from observe mode to enforced per-tool permissions without getting blocked |
49
+ | `ory-build-agent` | Drops `@ory/argus` into an agent *you* own — Claude Agent SDK, OpenAI Agents, Mastra, Vercel AI, LangGraph/PydanticAI |
50
+ | `ory-build-integration` | Wires Ory into your app: Action webhooks, JWT validation at a gateway, live event streams |
51
+ | `ory-contribute-integration` | Authors and submits an integration to the public `ory/integrates` registry |
52
+ | `ory-e2b-sandbox` | Scaffolds an E2B sandbox template that boots with this plugin preinstalled |
53
+ | `ory-temporal-worker` | Scaffolds a Temporal TypeScript worker where every Activity is authenticated, authorized, and audited |
69
54
 
70
- <details>
71
- <summary>Register the plugin by hand</summary>
55
+ The local stack has its own playbooks — ask for them by name:
72
56
 
73
- OpenClaw finds in-process plugins by module name from `node_modules` plus an entry in `.openclaw/config.json`. To register it without the guided setup:
57
+ | Command | What it does |
58
+ |---|---|
59
+ | `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 |
60
+ | `ory-local-down` | Stops it, keeping your data volumes |
61
+ | `ory-temporal-up` | Starts a local Temporal dev server for the `ory-temporal-worker` scaffold |
74
62
 
75
- ```bash
76
- npx @ory/openclaw install # registers in .openclaw/config.json
77
- npx @ory/openclaw install --project-dir <path>
78
- npx @ory/openclaw uninstall
79
- ```
63
+ 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`.
80
64
 
81
- This registers the plugin and its skills but skips the connect step. Run `ory-openclaw install` (or `configure`) in a terminal afterwards to connect.
65
+ A built-in **Ory MCP server** rounds it out OpenClaw can manage identities, projects, and permissions straight from chat.
82
66
 
83
- </details>
67
+ So: ask OpenClaw *"add Ory login to this app"* and it scaffolds the pages, starts a local Ory, and wires them together.
84
68
 
85
69
  ## What you get
86
70
 
87
- Once connected, every tool OpenClaw runs is governed by Ory three things happen automatically:
71
+ Out of the box, every tool OpenClaw runs produces a privacy-safe structured activity event in the unified local log.
72
+
73
+ Once you connect to Ory Agent Security, two more things happen automatically:
88
74
 
89
- - **Who's driving.** You sign in once in your browser; the OpenClaw process gets its own identity too, registered automatically on first run. No tokens to copy around, and the "who acted on whose behalf" trail stays queryable later.
90
- - **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.
91
- - **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.
75
+ - **Who's driving.** You sign in once in your browser; each OpenClaw session and typed sub-agent gets its own identity, registered automatically on first use. No tokens to copy around, and the "who acted on whose behalf" trail stays queryable later.
76
+ - **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.
92
77
 
93
78
  If Ory is ever unreachable, the plugin gets out of the way and lets OpenClaw keep working — so it can't lock you out.
94
79
 
95
- > **One nuance:** because the plugin runs inside OpenClaw's own process, it can't hard-block at the moment a session opens. Browser sign-in still runs there and the record stays correct — but the session always proceeds. Per-tool checks are unaffected: when a tool is about to run, a denial blocks it for real.
80
+ > **One nuance:** because the plugin runs inside OpenClaw's own process, it can't hard-block at the moment a session opens. Browser sign-in still runs there and the record stays correct — but the session always proceeds. Per-tool checks are unaffected: when a tool is about to run, a denial blocks it for real (the plugin returns `{ block: true }`).
96
81
 
97
82
  ### See what's happening
98
83
 
99
- Everything the plugin does is observable out of the box — no configuration required:
84
+ Everything the plugin does is observable out of the box:
100
85
 
101
- - **Status at a glance.** `npx -y -p @ory/openclaw ory-openclaw status` shows what's configured, who's signed in, how many built-in tools your permissions cover, and the most recent tool-call activity.
102
- - **Live traces.** Every tool call is recorded as an OpenTelemetry-style span. Watch them stream as the agent works:
86
+ - **Activity log.** Privacy-safe activity is always appended to `~/.config/ory-agent-plugins/openclaw/ory-agent-debug.log`. View events, decisions, and errors live with:
103
87
 
104
88
  ```bash
105
89
  npx -y -p @ory/openclaw ory-openclaw watch
106
90
  ```
107
91
 
108
- Spans are also written to `~/.config/ory-agent-plugins/openclaw/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.
109
- - **Debug log.** For a verbose play-by-play, set `ORY_AGENT_DEBUG=true`; structured logs land in `~/.config/ory-agent-plugins/openclaw/ory-agent-debug.log`.
92
+ Set `ORY_AGENT_LOG_FILE` to override the path; set it empty to disable file persistence.
93
+ - **Live debug.** Launch OpenClaw 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.
110
94
 
111
95
  ### Ready to enforce?
112
96
 
113
- When the watch-mode logs look right, turn on blocking with one command (setup already granted you the built-in tools):
97
+ 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.
114
98
 
115
99
  ```bash
116
- npx -y -p @ory/openclaw ory-openclaw permissions enforce
100
+ npx -y -p @ory/openclaw ory-openclaw permissions # what the project grants, and the live mode
117
101
  ```
118
102
 
119
- Now a denied tool is actually blocked and OpenClaw shows why. Go back to watch mode anytime with `permissions observe`. Use `permissions status` to see what's covered and `permissions bootstrap` to (re-)grant the built-in tools — or just ask OpenClaw in chat, e.g. *"grant me use of the execute_command tool."*
120
-
121
- ## Also: add login to your own app
103
+ Then a denied tool is actually blocked and OpenClaw shows why.
122
104
 
123
- Beyond securing OpenClaw, the plugin helps you build Ory into whatever you're working on. Ask OpenClaw *"add Ory login to this app"* and it scaffolds the login, registration, recovery, and settings pages (using [Ory Elements](https://github.com/ory/elements)) wired to a local Ory — no signup or keys 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`.
105
+ ## Connect to Ory Agent Security
124
106
 
125
- Bundled **skills** (just ask in plain language, or pick one from OpenClaw's skill picker) 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 MCP server** lets OpenClaw manage identities, projects, and permissions straight from chat.
107
+ Copy the connection details from the [Ory Console](https://console.ory.sh) under **Agent Security**:
126
108
 
127
- ## Configure by hand (CI / advanced)
128
-
129
- The guided setup covers most people. For scripted or CI setups, or to point at an existing Ory Network project, configure 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.
109
+ | Value | Flag | Environment variable |
110
+ |---|---|---|
111
+ | Project URL | `--project-url` | `ORY_PROJECT_URL` |
112
+ | Agent Security URL | `--agent-security-url` | `ORY_AGENT_SECURITY_URL` |
113
+ | Sign-in client id override (default `ory-agent-security-login`) | `--oauth2-client-id` | `ORY_OAUTH2_CLIENT_ID` |
130
114
 
131
115
  ```bash
132
116
  npx -y -p @ory/openclaw ory-openclaw configure \
133
117
  --project-url https://<slug>.projects.oryapis.com \
134
- --oauth2-client-id <sign-in client id> \
135
- --user-login
118
+ --agent-security-url https://agents.console.ory.com
136
119
  ```
137
120
 
138
- OpenClaw'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`.
139
-
140
- <details>
141
- <summary>Create the sign-in client by hand</summary>
142
-
143
- 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 so sign-in survives a busy port:
121
+ `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`.
144
122
 
145
- ```bash
146
- ory create oauth2-client --project <project-id> \
147
- --name "ory-agent-plugin" \
148
- --grant-type authorization_code,refresh_token \
149
- --response-type code \
150
- --scope openid,offline_access \
151
- --token-endpoint-auth-method none \
152
- --redirect-uri http://127.0.0.1:47823/callback \
153
- --redirect-uri http://127.0.0.1:47824/callback \
154
- --redirect-uri http://127.0.0.1:47825/callback \
155
- --redirect-uri http://127.0.0.1:47826/callback
156
- ```
123
+ **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.
157
124
 
158
- 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.
125
+ **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 OpenClaw session registers its own identity automatically on first use.
159
126
 
160
- </details>
127
+ 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.
161
128
 
162
- With nothing configured, the plugin still loads and runs in **pass-through mode**: skills and logging work, but no checks run and nothing is blocked. Perfectly fine if you only want the app-building features.
129
+ 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.
163
130
 
164
131
  ## Commands
165
132
 
166
133
  ```
167
- ory-openclaw install | uninstall Install/remove; --reconfigure re-runs setup, --no-configure skips it
134
+ ory-openclaw install | uninstall Install (add --project-url to also connect Agent Security) / remove; --project-dir <path> targets another project
168
135
  ory-openclaw status Show configuration, identities, permission coverage, recent activity
169
- ory-openclaw watch Tail the live trace stream (OTel spans)
170
- ory-openclaw permissions <cmd> status | bootstrap | observe (watch) | enforce (block)
171
- ory-openclaw configure <flags> Point at a project by hand (--project-url, --oauth2-client-id, --user-login, --audit-only)
136
+ ory-openclaw permissions <cmd> status (read-only; grants + posture live in the Ory Console)
137
+ ory-openclaw configure <flags> Connect a project (--project-url) or --disconnect
172
138
  ory-openclaw agent <status|unregister> Manage OpenClaw's own auto-created identity
173
139
  ory-openclaw local <up|down|status|…> Run / manage a local Ory in Docker
174
- ory-openclaw watch [<trace-file>] Live-tail the trace of decisions as they happen
140
+ ory-openclaw version Print plugin, core, and Node versions (--json for machine-readable)
175
141
  ```
176
142
 
177
143
  All prefixed with `npx -y -p @ory/openclaw`.
@@ -179,9 +145,8 @@ All prefixed with `npx -y -p @ory/openclaw`.
179
145
  ## Troubleshooting
180
146
 
181
147
  - **`local-up` fails** — make sure Docker is running and ports `4000`, `4100`, `4455`, and `16686` are free.
182
- - **Browser sign-in loops** — reset with `ory-openclaw agent unregister` and try again.
183
- - **Install ran but never asked how to connect** — you're almost certainly on a stale `npx` cache. `npx -p @ory/openclaw` (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/openclaw ory-openclaw version`. To force the current release, clear the cache and reinstall: `rm -rf ~/.npm/_npx` then `npx -y -p @ory/openclaw ory-openclaw install`. Pinning an exact version (`@ory/openclaw@<version>`) also bypasses the cached copy.
184
- - **Want to see what's happening** — `npx -y -p @ory/openclaw ory-openclaw status` for a snapshot, `npx -y -p @ory/openclaw ory-openclaw 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/openclaw/` (see [See what's happening](#see-whats-happening)).
148
+ - **Browser sign-in loops** (after connecting) — reset with `ory-openclaw agent unregister` and try again.
149
+ - **Running an older CLI than expected** — `npx -p @ory/openclaw` (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/openclaw ory-openclaw version`. To force the current release, clear the cache and reinstall: `rm -rf ~/.npm/_npx` then `npx -y -p @ory/openclaw ory-openclaw install`. Pinning an exact version (`@ory/openclaw@<version>`) also bypasses the cached copy.
185
150
 
186
151
  ## Learn more
187
152
 
@@ -7,6 +7,5 @@
7
7
  * npx ory-openclaw uninstall Remove plugin from OpenClaw config
8
8
  * npx ory-openclaw configure Set or view Ory project URL and API key
9
9
  * npx ory-openclaw status Show plugin status
10
- * npx ory-openclaw watch [trace-file] Watch live trace output
11
10
  */
12
11
  export {};
package/dist/cli/main.js CHANGED
@@ -8,7 +8,6 @@
8
8
  * npx ory-openclaw uninstall Remove plugin from OpenClaw config
9
9
  * npx ory-openclaw configure Set or view Ory project URL and API key
10
10
  * npx ory-openclaw status Show plugin status
11
- * npx ory-openclaw watch [trace-file] Watch live trace output
12
11
  */
13
12
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
13
  if (k2 === undefined) k2 = k;
@@ -53,8 +52,18 @@ const CONFIG_DIR = ".openclaw";
53
52
  const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
54
53
  function main() {
55
54
  const [command, ...args] = process.argv.slice(2);
55
+ // `--help` after a matched subcommand: print usage, do nothing else.
56
+ // Without this the switch below ignores the flag and `install --help`
57
+ // performs a real install (#221).
58
+ if ((0, argus_1.shouldPrintHelp)(command, args))
59
+ return help();
56
60
  switch (command) {
57
61
  case "install":
62
+ if (args.includes("--print")) {
63
+ process.argv = ["node", "setup.js", ...args];
64
+ require("./setup.js");
65
+ break;
66
+ }
58
67
  (0, argus_1.beginDeferNextSteps)();
59
68
  install(args);
60
69
  (0, argus_1.runPostInstall)("ory-openclaw", "openclaw", args).then(() => process.exit(0), (err) => {
@@ -64,7 +73,10 @@ function main() {
64
73
  break;
65
74
  case "uninstall":
66
75
  uninstall(args);
67
- (0, argus_1.clearCredentialsForUninstall)().then(() => process.exit(0), (err) => {
76
+ (0, argus_1.clearCredentialsForUninstall)({
77
+ harness: "openclaw",
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-openclaw", args);
74
86
  break;
75
87
  case "agent":
76
- (0, argus_1.runAgentCommand)("ory-openclaw", args).then((code) => process.exit(code), (err) => {
88
+ (0, argus_1.runAgentCommand)("ory-openclaw", "openclaw", args).then((code) => process.exit(code), (err) => {
77
89
  console.error(err.message ?? err);
78
90
  process.exit(1);
79
91
  });
@@ -90,15 +102,15 @@ function main() {
90
102
  process.exit(1);
91
103
  });
92
104
  break;
105
+ case "watch":
106
+ (0, argus_1.runWatchCommand)("ory-openclaw", "openclaw", args).then((code) => process.exit(code), (err) => { console.error(err.message ?? err); process.exit(1); });
107
+ break;
93
108
  case "local":
94
109
  (0, argus_1.runLocalCommand)("ory-openclaw", args).catch((err) => {
95
110
  console.error(err.message ?? err);
96
111
  process.exit(1);
97
112
  });
98
113
  break;
99
- case "watch":
100
- (0, argus_1.runWatchCommand)("openclaw", args);
101
- break;
102
114
  case "version":
103
115
  case "--version":
104
116
  case "-v":
@@ -136,11 +148,33 @@ function install(args) {
136
148
  const configPath = getOpenClawConfigPath(projectDir);
137
149
  ensureConfigDir(projectDir);
138
150
  const config = (0, argus_1.readJsonFile)(configPath);
139
- const merged = (0, plugin_config_js_1.registerPluginInConfig)(config, projectDir);
151
+ // OpenClaw loads the plugin in-process from a directory on
152
+ // `plugins.load.paths`, so the runtime dir *is* the registration — there is
153
+ // no hook subprocess and no hook shim.
154
+ const runtime = (0, argus_1.wireRuntime)({
155
+ harness: "openclaw",
156
+ packageName: "@ory/openclaw",
157
+ packageRoot: PACKAGE_ROOT,
158
+ installCommand: "npx -y -p @ory/openclaw ory-openclaw install",
159
+ args,
160
+ entryRelPath: "dist/index.js",
161
+ inProcess: true,
162
+ });
163
+ console.log(runtime.target.kind === "linked"
164
+ ? `Runtime: linked to ${runtime.target.packageDir} (dev)`
165
+ : `Runtime: ${runtime.target.packageName}@${runtime.target.version} in ${runtime.target.storeDir}`);
166
+ for (const pruned of runtime.prunedStores) {
167
+ console.log(` Removed stale runtime: ${pruned}`);
168
+ }
169
+ let merged = (0, plugin_config_js_1.registerPluginInConfig)(config, runtime.target.packageDir);
170
+ if (runtime.mcpServer) {
171
+ merged = (0, argus_1.mergeMcpServer)(merged, runtime.mcpServer);
172
+ }
140
173
  fs.writeFileSync(configPath, JSON.stringify(merged, null, 2) + "\n");
141
174
  (0, assets_js_1.installOpenclawOryAssets)(projectDir);
142
175
  console.log(`Ory plugin registered in ${configPath}`);
143
176
  console.log(` Plugin id: ${plugin_config_js_1.PLUGIN_ID}`);
177
+ console.log(` Loads from: ${runtime.target.packageDir}`);
144
178
  console.log(` Skills + commands installed under ${path.join(projectDir, CONFIG_DIR, "skills")}`);
145
179
  (0, argus_1.printEnvHelp)("ory-openclaw");
146
180
  }
@@ -153,9 +187,14 @@ function uninstall(args) {
153
187
  }
154
188
  (0, assets_js_1.uninstallOpenclawOryAssets)(projectDir);
155
189
  const config = (0, argus_1.readJsonFile)(configPath);
156
- const updated = (0, plugin_config_js_1.unregisterPluginFromConfig)(config, projectDir);
190
+ let updated = (0, plugin_config_js_1.unregisterPluginFromConfig)(config);
191
+ updated = (0, argus_1.removeMcpServer)(updated);
157
192
  fs.writeFileSync(configPath, JSON.stringify(updated, null, 2) + "\n");
158
193
  console.log(`Ory plugin and skills removed from ${configPath}`);
194
+ (0, argus_1.removeRuntimeWiring)("openclaw");
195
+ for (const pruned of (0, argus_1.pruneRuntimeStores)()) {
196
+ console.log(` Removed runtime: ${pruned}`);
197
+ }
159
198
  }
160
199
  async function status(args) {
161
200
  const projectDir = parseProjectDir(args);
@@ -188,22 +227,21 @@ Commands:
188
227
  uninstall [--project-dir <path>] Remove the Ory plugin from OpenClaw config
189
228
  configure Set or view Ory project URL and API key
190
229
  agent <cmd> Manage the agent's OAuth2 (DCR) identity (status, unregister)
191
- permissions <cmd> Manage permission mode and tool permissions (status, bootstrap, observe, enforce)
230
+ permissions Show the live permission mode and per-tool coverage
192
231
  status [--project-dir <path>] Show plugin configuration and status
193
- watch [trace-file] Watch live trace output
232
+ watch [--json] [--lines <count>] Follow the live activity/debug log
194
233
  version Show version and the ory-agent-plugins build commit
195
234
  local <cmd> Manage local Ory dev environment (up, down, status, seed, ...)
196
235
 
197
236
  After installing, the plugin hooks into these OpenClaw lifecycle events:
198
237
  session_start Verify agent session via Ory Identities
199
238
  before_tool_call Check tool permissions via Ory Permissions (can block)
200
- after_tool_call Record tool completion trace
239
+ after_tool_call Record tool completion activity
201
240
 
202
241
  Examples:
203
242
  npx -y -p @ory/openclaw ory-openclaw install # Register in current directory
204
- npx -y -p @ory/openclaw ory-openclaw configure --project-url https://my.oryapis.com --api-key ory_pat_...
243
+ npx -y -p @ory/openclaw ory-openclaw configure --project-url https://my.oryapis.com --agent-security-url https://agents.console.ory.com
205
244
  npx -y -p @ory/openclaw ory-openclaw status # Check configuration
206
- npx -y -p @ory/openclaw ory-openclaw watch # Watch trace output in real-time
207
245
  npx -y -p @ory/openclaw ory-openclaw uninstall # Remove plugin
208
246
  `);
209
247
  }
@@ -30,23 +30,27 @@ type Rec = Record<string, unknown>;
30
30
  * Resolve the directory that contains the plugin's `openclaw.plugin.json`
31
31
  * manifest, so it can be registered on `plugins.load.paths`.
32
32
  *
33
- * Prefer the copy installed in the target project's `node_modules` that path
34
- * is stable across sessions. Fall back to the package root of the currently
35
- * running CLI (best effort; under a bare `npx` run this is the ephemeral npx
36
- * cache, which is why the README recommends `npm install` or the native
37
- * `openclaw plugins install` flow).
33
+ * This is the runtime directory the install resolved: a materialized store
34
+ * under the shared data dir, or a linked workspace build. Both are stable
35
+ * across sessions, which the previous candidates were not the project's
36
+ * `node_modules` disappears with a `rm -rf`, and the running CLI's own package
37
+ * root is npx's ephemeral cache under the documented `npx -p @ory/openclaw`
38
+ * invocation.
39
+ *
40
+ * Falls back to the wiring recorded in the runtime manifest when no runtime is
41
+ * passed (the uninstall path, which must find the same path install wrote).
38
42
  */
39
- export declare function resolvePluginRoot(projectDir: string): string;
43
+ export declare function resolvePluginRoot(runtimeDir?: string): string;
40
44
  /**
41
45
  * Non-destructively register + enable the Ory plugin in an OpenClaw config
42
46
  * object. Returns a new config; does not mutate the input.
43
47
  */
44
- export declare function registerPluginInConfig(config: Rec, projectDir: string): Rec;
48
+ export declare function registerPluginInConfig(config: Rec, runtimeDir?: string): Rec;
45
49
  /**
46
50
  * Non-destructively remove the Ory plugin from an OpenClaw config object,
47
51
  * leaving any other plugins' config intact. Returns a new config.
48
52
  */
49
- export declare function unregisterPluginFromConfig(config: Rec, projectDir: string): Rec;
53
+ export declare function unregisterPluginFromConfig(config: Rec, runtimeDir?: string): Rec;
50
54
  /** True when the plugin is enabled under the current nested schema. */
51
55
  export declare function isPluginRegistered(config: Rec): boolean;
52
56
  export {};
@@ -61,8 +61,8 @@ exports.resolvePluginRoot = resolvePluginRoot;
61
61
  exports.registerPluginInConfig = registerPluginInConfig;
62
62
  exports.unregisterPluginFromConfig = unregisterPluginFromConfig;
63
63
  exports.isPluginRegistered = isPluginRegistered;
64
- const fs = __importStar(require("node:fs"));
65
64
  const path = __importStar(require("node:path"));
65
+ const argus_1 = require("@ory/argus");
66
66
  /** Plugin manifest id (from `openclaw.plugin.json`). */
67
67
  exports.PLUGIN_ID = "ory-agent-security";
68
68
  /** npm module name — used only to migrate away the legacy flat-array shape. */
@@ -77,30 +77,36 @@ function asRec(value) {
77
77
  * Resolve the directory that contains the plugin's `openclaw.plugin.json`
78
78
  * manifest, so it can be registered on `plugins.load.paths`.
79
79
  *
80
- * Prefer the copy installed in the target project's `node_modules` that path
81
- * is stable across sessions. Fall back to the package root of the currently
82
- * running CLI (best effort; under a bare `npx` run this is the ephemeral npx
83
- * cache, which is why the README recommends `npm install` or the native
84
- * `openclaw plugins install` flow).
80
+ * This is the runtime directory the install resolved: a materialized store
81
+ * under the shared data dir, or a linked workspace build. Both are stable
82
+ * across sessions, which the previous candidates were not the project's
83
+ * `node_modules` disappears with a `rm -rf`, and the running CLI's own package
84
+ * root is npx's ephemeral cache under the documented `npx -p @ory/openclaw`
85
+ * invocation.
86
+ *
87
+ * Falls back to the wiring recorded in the runtime manifest when no runtime is
88
+ * passed (the uninstall path, which must find the same path install wrote).
85
89
  */
86
- function resolvePluginRoot(projectDir) {
87
- const inProject = path.join(projectDir, "node_modules", exports.PLUGIN_MODULE);
88
- if (fs.existsSync(path.join(inProject, "openclaw.plugin.json"))) {
89
- return path.resolve(inProject);
90
- }
91
- // dist/cli/plugin-config.js -> package root
90
+ function resolvePluginRoot(runtimeDir) {
91
+ if (runtimeDir)
92
+ return path.resolve(runtimeDir);
93
+ const wired = (0, argus_1.getRuntimeWiring)("openclaw");
94
+ if (wired)
95
+ return path.resolve(wired.packageDir);
96
+ // Nothing wired (uninstall before install, or a manifest that was wiped):
97
+ // fall back to the running CLI so an existing registration still matches.
92
98
  return path.resolve(__dirname, "..", "..");
93
99
  }
94
100
  /**
95
101
  * Non-destructively register + enable the Ory plugin in an OpenClaw config
96
102
  * object. Returns a new config; does not mutate the input.
97
103
  */
98
- function registerPluginInConfig(config, projectDir) {
104
+ function registerPluginInConfig(config, runtimeDir) {
99
105
  const next = { ...config };
100
106
  // asRec drops any legacy flat `plugins: [...]` array — that shape was never
101
107
  // read by current OpenClaw, so nothing functional is lost by migrating it.
102
108
  const plugins = asRec(next.plugins);
103
- const root = resolvePluginRoot(projectDir);
109
+ const root = resolvePluginRoot(runtimeDir);
104
110
  // 1. Discovery — add the package dir to plugins.load.paths (deduped).
105
111
  const load = asRec(plugins.load);
106
112
  const paths = Array.isArray(load.paths)
@@ -129,7 +135,7 @@ function registerPluginInConfig(config, projectDir) {
129
135
  * Non-destructively remove the Ory plugin from an OpenClaw config object,
130
136
  * leaving any other plugins' config intact. Returns a new config.
131
137
  */
132
- function unregisterPluginFromConfig(config, projectDir) {
138
+ function unregisterPluginFromConfig(config, runtimeDir) {
133
139
  const next = { ...config };
134
140
  if (next.plugins == null)
135
141
  return next;
@@ -143,7 +149,7 @@ function unregisterPluginFromConfig(config, projectDir) {
143
149
  return next;
144
150
  }
145
151
  const plugins = asRec(next.plugins);
146
- const root = resolvePluginRoot(projectDir);
152
+ const root = resolvePluginRoot(runtimeDir);
147
153
  // Remove our load path.
148
154
  const load = asRec(plugins.load);
149
155
  if (Array.isArray(load.paths)) {
package/dist/cli/setup.js CHANGED
@@ -65,7 +65,8 @@ function main() {
65
65
  }
66
66
  const args = (0, argus_1.parseSetupArgs)();
67
67
  if (args.print) {
68
- console.log(JSON.stringify((0, plugin_config_js_1.registerPluginInConfig)({}, args.projectDir), null, 2));
68
+ // Nothing is resolved or written on a --print run.
69
+ console.log(JSON.stringify((0, plugin_config_js_1.registerPluginInConfig)({}, "<resolved runtime>"), null, 2));
69
70
  return;
70
71
  }
71
72
  const configPath = getConfigPath(args);
@@ -76,15 +77,30 @@ function main() {
76
77
  return;
77
78
  }
78
79
  (0, assets_js_1.uninstallOpenclawOryAssets)(args.projectDir);
79
- let updated = (0, plugin_config_js_1.unregisterPluginFromConfig)(existing, args.projectDir);
80
+ let updated = (0, plugin_config_js_1.unregisterPluginFromConfig)(existing);
80
81
  updated = (0, argus_1.removeMcpServer)(updated);
81
82
  (0, argus_1.writeJsonFile)(configPath, updated);
82
83
  console.log(`Removed Ory plugin, MCP server, and skills from ${configPath}`);
84
+ (0, argus_1.removeRuntimeWiring)("openclaw");
85
+ for (const pruned of (0, argus_1.pruneRuntimeStores)()) {
86
+ console.log(` Removed runtime: ${pruned}`);
87
+ }
83
88
  return;
84
89
  }
85
90
  ensureConfigDir(args);
86
- let merged = (0, plugin_config_js_1.registerPluginInConfig)(existing, args.projectDir);
87
- merged = (0, argus_1.mergeMcpServer)(merged);
91
+ const runtime = (0, argus_1.wireRuntime)({
92
+ harness: "openclaw",
93
+ packageName: "@ory/openclaw",
94
+ packageRoot: path.resolve(__dirname, "..", ".."),
95
+ installCommand: "npx -y -p @ory/openclaw ory-openclaw install",
96
+ args: process.argv.slice(2),
97
+ entryRelPath: "dist/index.js",
98
+ inProcess: true,
99
+ });
100
+ let merged = (0, plugin_config_js_1.registerPluginInConfig)(existing, runtime.target.packageDir);
101
+ if (runtime.mcpServer) {
102
+ merged = (0, argus_1.mergeMcpServer)(merged, runtime.mcpServer);
103
+ }
88
104
  (0, argus_1.writeJsonFile)(configPath, merged);
89
105
  (0, assets_js_1.installOpenclawOryAssets)(args.projectDir);
90
106
  console.log(`Ory plugin and MCP server configured in ${configPath}`);
@@ -99,7 +115,10 @@ main();
99
115
  // stored Ory credentials. When required by the plugin's main CLI, that
100
116
  // command owns the purge, so the `require.main` guard prevents a double run.
101
117
  if (require.main === module && process.argv.includes("--uninstall")) {
102
- (0, argus_1.clearCredentialsForUninstall)().then(() => process.exit(0), (err) => {
118
+ (0, argus_1.clearCredentialsForUninstall)({
119
+ harness: "openclaw",
120
+ purge: process.argv.includes("--purge"),
121
+ }).then(() => process.exit(0), (err) => {
103
122
  console.error(err.message ?? err);
104
123
  process.exit(1);
105
124
  });
package/dist/plugin.d.ts CHANGED
@@ -1,28 +1,10 @@
1
- import { OryAgentClient, ensureUserAuthenticated, ensureAgentIdentity, ensureSubAgentIdentity } from "@ory/argus";
1
+ import { OryAgentClient, ensureAgentIdentity, ensureSubAgentIdentity, ensureUserAuthenticated } from "@ory/argus";
2
2
  import type { OpenClawPluginEntry } from "./types.js";
3
3
  export interface CreateOryPluginDeps {
4
- /** Test injection point for the user login flow. */
5
4
  userLogin?: typeof ensureUserAuthenticated;
6
- /** Test injection point for the agent identity gate. */
7
5
  agentGate?: typeof ensureAgentIdentity;
8
- /** Test injection point for the sub-agent identity resolver. */
9
6
  subAgentGate?: typeof ensureSubAgentIdentity;
10
7
  }
11
- /**
12
- * Create the Ory plugin entry for OpenClaw.
13
- *
14
- * OpenClaw plugins export { id, name, register(api) } where register
15
- * receives a PluginApi for registering hooks. The `before_tool_call`
16
- * hook can return `{ block: true }` to deny tool execution — this
17
- * makes permission checks enforceable, unlike harnesses that can only
18
- * log denials.
19
- *
20
- * The `session_start` hook returns `Promise<void>` so user login is
21
- * advisory at session start: the flow still runs to emit the audit span,
22
- * refresh tokens, and prompt for the project URL when interactive, but
23
- * cannot hard-block the session.
24
- */
25
8
  export declare function createOryPlugin(clientOrConfig?: OryAgentClient | {
26
9
  projectUrl: string;
27
- apiKey?: string;
28
10
  }, deps?: CreateOryPluginDeps): OpenClawPluginEntry;