@ory/pi 0.13.8 → 0.14.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 +10 -7
- package/dist/cli/main.js +7 -0
- package/dist/plugin.d.ts +3 -2
- package/dist/plugin.js +10 -112
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -27,12 +27,14 @@ Then run one command. It installs the plugin and walks you through connecting:
|
|
|
27
27
|
npx -y -p @ory/pi ory-pi install
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
The installer drops a tiny loader at `<project>/.pi/extensions/ory.js` so Pi discovers the plugin, adds the Ory skills under `<project>/.pi/skills`, and then
|
|
30
|
+
The installer drops a tiny loader at `<project>/.pi/extensions/ory.js` so Pi discovers the plugin, adds the Ory skills under `<project>/.pi/skills`, and then opens a guided setup **in your browser** where you pick how to connect — the default takes just a click:
|
|
31
31
|
|
|
32
32
|
- **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.
|
|
33
33
|
- **Local** — run a complete Ory on your laptop with Docker. No account, no signup, no keys. Great for trying it out.
|
|
34
34
|
- **Audit-only** — skip Ory entirely and just log what Pi does.
|
|
35
35
|
|
|
36
|
+
> No browser available (CI, SSH, headless)? The same walkthrough runs right in your terminal instead — or force it with `--no-web`.
|
|
37
|
+
|
|
36
38
|
That's it. Confirm everything landed with:
|
|
37
39
|
|
|
38
40
|
```bash
|
|
@@ -73,6 +75,7 @@ If Ory is ever unreachable, the plugin gets out of the way and lets Pi keep work
|
|
|
73
75
|
Everything the plugin does is observable out of the box — no configuration required:
|
|
74
76
|
|
|
75
77
|
- **Status at a glance.** `npx -y -p @ory/pi ory-pi status` shows what's configured, who's signed in, how many built-in tools your permissions cover, and the most recent tool-call activity.
|
|
78
|
+
- **Live dashboard.** `npx -y -p @ory/pi ory-pi 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.
|
|
76
79
|
- **Live traces.** Every tool call is recorded as an OpenTelemetry-style span. Watch them stream as the agent works:
|
|
77
80
|
|
|
78
81
|
```bash
|
|
@@ -90,7 +93,7 @@ When the watch-mode logs look right, turn on blocking with one command (setup al
|
|
|
90
93
|
npx -y -p @ory/pi ory-pi permissions enforce
|
|
91
94
|
```
|
|
92
95
|
|
|
93
|
-
Now a denied tool is actually blocked and Pi shows why (the plugin returns `{ block: true, reason }` on Pi's `tool_call` event). 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.
|
|
96
|
+
Now a denied tool is actually blocked and Pi shows why (the plugin returns `{ block: true, reason }` on Pi's `tool_call` event). 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.
|
|
94
97
|
|
|
95
98
|
## Also: add login to your own app
|
|
96
99
|
|
|
@@ -105,22 +108,22 @@ The guided setup covers most people. For scripted or CI setups, or to point at a
|
|
|
105
108
|
```bash
|
|
106
109
|
npx -y -p @ory/pi ory-pi configure \
|
|
107
110
|
--project-url https://<slug>.projects.oryapis.com \
|
|
108
|
-
--oauth2-client-id <sign-in client id>
|
|
109
|
-
--user-login
|
|
111
|
+
--oauth2-client-id <sign-in client id>
|
|
110
112
|
```
|
|
111
113
|
|
|
112
|
-
Pi'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 public sign-in client, registered with the four loopback URLs `http://127.0.0.1:47823..47826/callback`; the guided setup makes it for you. For logging-only with no checks, use `--audit-only`. The equivalent env vars are `ORY_PROJECT_URL
|
|
114
|
+
Pi'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 public sign-in client, registered with the four loopback URLs `http://127.0.0.1:47823..47826/callback`; the guided setup makes it for you. For logging-only with no checks, use `--audit-only`. The interactive user login runs every session (always on, non-blocking) and only needs `ORY_OAUTH2_CLIENT_ID` to complete the browser flow. The equivalent env vars are `ORY_PROJECT_URL` and `ORY_OAUTH2_CLIENT_ID`.
|
|
113
115
|
|
|
114
116
|
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.
|
|
115
117
|
|
|
116
118
|
## Commands
|
|
117
119
|
|
|
118
120
|
```
|
|
119
|
-
ory-pi install | uninstall Install/remove; --reconfigure re-runs setup, --no-
|
|
121
|
+
ory-pi install | uninstall Install/remove; --reconfigure re-runs setup, --no-web forces the terminal wizard, --no-configure skips it
|
|
120
122
|
ory-pi status Show configuration, identities, permission coverage, recent activity
|
|
123
|
+
ory-pi dashboard Open the live browser dashboard (status + service health + Change stack)
|
|
121
124
|
ory-pi watch Tail the live trace stream (OTel spans)
|
|
122
125
|
ory-pi permissions <cmd> status | bootstrap | observe (watch) | enforce (block)
|
|
123
|
-
ory-pi configure <flags> Point at a project by hand (--project-url, --oauth2-client-id, --
|
|
126
|
+
ory-pi configure <flags> Point at a project by hand (--project-url, --oauth2-client-id, --audit-only)
|
|
124
127
|
ory-pi agent <status|unregister> Manage Pi's own auto-created identity
|
|
125
128
|
```
|
|
126
129
|
|
package/dist/cli/main.js
CHANGED
|
@@ -97,6 +97,12 @@ function main() {
|
|
|
97
97
|
case "watch":
|
|
98
98
|
(0, argus_1.runWatchCommand)("pi", args);
|
|
99
99
|
break;
|
|
100
|
+
case "dashboard":
|
|
101
|
+
(0, argus_1.runDashboardCommand)("ory-pi", "pi", args).then((code) => process.exit(code), (err) => {
|
|
102
|
+
console.error(err.message ?? err);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
});
|
|
105
|
+
break;
|
|
100
106
|
case "version":
|
|
101
107
|
case "--version":
|
|
102
108
|
case "-v":
|
|
@@ -182,6 +188,7 @@ Commands:
|
|
|
182
188
|
local <cmd> Manage local Ory dev environment
|
|
183
189
|
(up, down, status, seed, logs, env, configure, reset)
|
|
184
190
|
status [--project-dir <path>] Show plugin configuration and status
|
|
191
|
+
dashboard Open the Ory Agent dashboard (system health + configuration)
|
|
185
192
|
watch [trace-file] Watch live trace output
|
|
186
193
|
version Show version and the ory-agent-plugins build commit
|
|
187
194
|
|
package/dist/plugin.d.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Pi loads a default-exported factory `(pi) => void | Promise<void>` from a
|
|
5
5
|
* TypeScript extension module (via jiti) and awaits it before startup
|
|
6
6
|
* continues. We treat the factory body as the session-start hook: it runs the
|
|
7
|
-
* user gate (
|
|
8
|
-
*
|
|
7
|
+
* user gate (which runs every session and never blocks — enforcement is
|
|
8
|
+
* governed solely by permissionMode at tool-call time), the agent gate, and
|
|
9
|
+
* writes the user→agent delegation tuple.
|
|
9
10
|
*
|
|
10
11
|
* The blocking permission decision lives in the `tool_call` hook, which CAN
|
|
11
12
|
* block by returning `{ block: true, reason }` (like OpenClaw's
|
package/dist/plugin.js
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* Pi loads a default-exported factory `(pi) => void | Promise<void>` from a
|
|
6
6
|
* TypeScript extension module (via jiti) and awaits it before startup
|
|
7
7
|
* continues. We treat the factory body as the session-start hook: it runs the
|
|
8
|
-
* user gate (
|
|
9
|
-
*
|
|
8
|
+
* user gate (which runs every session and never blocks — enforcement is
|
|
9
|
+
* governed solely by permissionMode at tool-call time), the agent gate, and
|
|
10
|
+
* writes the user→agent delegation tuple.
|
|
10
11
|
*
|
|
11
12
|
* The blocking permission decision lives in the `tool_call` hook, which CAN
|
|
12
13
|
* block by returning `{ block: true, reason }` (like OpenClaw's
|
|
@@ -54,92 +55,22 @@ async function runSessionStart(client, deps) {
|
|
|
54
55
|
client.logger.info("lifecycle.session_start", { harness: "pi" });
|
|
55
56
|
client.tracer.setContext({ traceId: (0, argus_1.deriveTraceId)("pi"), sessionId: "pi" });
|
|
56
57
|
client.tracer.record("session.start", "ok", { attributes: {} });
|
|
57
|
-
// Run the user login.
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
// no-op (mode === "disabled") and we fall through to legacy verification.
|
|
58
|
+
// Run the user login. It runs every session and never blocks the session
|
|
59
|
+
// from starting — the flow emits the user.auth audit span, refreshes tokens,
|
|
60
|
+
// and may prompt when interactive. Enforcement is governed solely by
|
|
61
|
+
// permissionMode at tool-call time.
|
|
62
62
|
const userGate = deps.userLogin ?? argus_1.ensureUserAuthenticated;
|
|
63
|
-
|
|
63
|
+
await userGate(client, {
|
|
64
64
|
binName: "ory-pi",
|
|
65
65
|
harness: "pi",
|
|
66
|
-
allowBlock: false,
|
|
67
66
|
});
|
|
68
67
|
// Resolve the agent identity (machine credentials). Never blocks; attaches
|
|
69
68
|
// the agent's bearer token to outgoing Ory API calls.
|
|
70
69
|
const agentGate = deps.agentGate ?? argus_1.ensureAgentIdentity;
|
|
71
70
|
await agentGate(client, { projectUrl: (0, argus_1.resolveConfig)().projectUrl, harness: "pi" });
|
|
72
71
|
// Once both principals are populated, write the user→agent delegation tuple.
|
|
73
|
-
//
|
|
74
|
-
await
|
|
75
|
-
if (decision.mode !== "disabled") {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
const resolved = (0, argus_1.resolveConfig)();
|
|
79
|
-
if (resolved.auditOnly) {
|
|
80
|
-
client.logger.info("config.audit_only", {
|
|
81
|
-
message: "Audit-only mode enabled. Auth and permission checks are disabled.",
|
|
82
|
-
});
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
if (!resolved.projectUrl) {
|
|
86
|
-
client.logger.warn("config.not_configured", {
|
|
87
|
-
message: "Ory plugin is not configured. Auth and permission checks are disabled. " +
|
|
88
|
-
"Run 'npx ory-pi configure' to connect to an Ory project.",
|
|
89
|
-
});
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
const sessionToken = process.env.ORY_SESSION_TOKEN;
|
|
93
|
-
const oauth2Token = process.env.ORY_OAUTH2_TOKEN;
|
|
94
|
-
if (sessionToken) {
|
|
95
|
-
await verifySessionToken(client, sessionToken);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
if (oauth2Token) {
|
|
99
|
-
await verifyOAuth2Token(client, oauth2Token);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
client.logger.warn("session.no_credentials", {
|
|
103
|
-
message: "Neither ORY_SESSION_TOKEN nor ORY_OAUTH2_TOKEN is set. " +
|
|
104
|
-
"Skipping authentication.",
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
async function verifySessionToken(client, token) {
|
|
108
|
-
try {
|
|
109
|
-
const session = await client.verifySession(token);
|
|
110
|
-
if (!session.active) {
|
|
111
|
-
client.logger.warn("session.inactive", {
|
|
112
|
-
message: "Ory session is not active. Re-authenticate to enable auth checks.",
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
catch (err) {
|
|
117
|
-
client.logger.warn("session.verify_failed", {
|
|
118
|
-
code: isOryError(err) ? err.code : "unknown",
|
|
119
|
-
message: err instanceof Error ? err.message : String(err),
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
async function verifyOAuth2Token(client, token) {
|
|
124
|
-
try {
|
|
125
|
-
const tokenInfo = await client.introspectToken(token);
|
|
126
|
-
if (!tokenInfo.active) {
|
|
127
|
-
client.logger.warn("oauth2.token_inactive", {
|
|
128
|
-
message: "Ory OAuth2 token is not active. Obtain a new token to enable auth checks.",
|
|
129
|
-
});
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
client.logger.info("oauth2.session_authenticated", {
|
|
133
|
-
clientId: tokenInfo.clientId,
|
|
134
|
-
subject: tokenInfo.subject,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
catch (err) {
|
|
138
|
-
client.logger.warn("oauth2.introspect_failed", {
|
|
139
|
-
code: isOryError(err) ? err.code : "unknown",
|
|
140
|
-
message: err instanceof Error ? err.message : String(err),
|
|
141
|
-
});
|
|
142
|
-
}
|
|
72
|
+
// Written at most once per install + fail-open: audit-trail data only.
|
|
73
|
+
await (0, argus_1.writeUserDelegatesAgent)(client);
|
|
143
74
|
}
|
|
144
75
|
// ─── tool_call (pre-execution, blocking) ───────────────────────────
|
|
145
76
|
function createToolCallHandler(client) {
|
|
@@ -284,36 +215,3 @@ function handlePermissionError(err, toolName, client) {
|
|
|
284
215
|
// gate() vocabulary. The errored permission.check span carries the audit
|
|
285
216
|
// trail for the failed check itself.
|
|
286
217
|
}
|
|
287
|
-
/**
|
|
288
|
-
* Write the user→agent delegation tuple. Idempotent and fail-open: requires
|
|
289
|
-
* both principal subjects to be populated; any error (including an
|
|
290
|
-
* unconfigured projectUrl, which manifests as a network_error) is logged and
|
|
291
|
-
* swallowed. Purely audit-trail data — does not affect enforcement.
|
|
292
|
-
*/
|
|
293
|
-
async function recordUserDelegatesAgent(client) {
|
|
294
|
-
const user = client.userPrincipal.subject;
|
|
295
|
-
const agent = client.agentPrincipal.subject;
|
|
296
|
-
if (!user || !agent) {
|
|
297
|
-
client.logger.debug("delegation.skip", {
|
|
298
|
-
reason: "missing principal",
|
|
299
|
-
hasUser: !!user,
|
|
300
|
-
hasAgent: !!agent,
|
|
301
|
-
});
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
try {
|
|
305
|
-
await client.createRelationship({
|
|
306
|
-
namespace: resolveNamespace(),
|
|
307
|
-
object: `agent:${agent}`,
|
|
308
|
-
relation: "delegate",
|
|
309
|
-
subjectId: `user:${user}`,
|
|
310
|
-
}, { spanAttributes: { delegation: "user-to-agent" } });
|
|
311
|
-
}
|
|
312
|
-
catch (err) {
|
|
313
|
-
const oryErr = err;
|
|
314
|
-
client.logger.warn("delegation.user_to_agent.failed", {
|
|
315
|
-
code: oryErr.code,
|
|
316
|
-
message: oryErr.message,
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Ory plugin for Pi (the minimal coding agent): scaffolding skills, a local Ory instance, and authentication, authorization, and audit for every tool call",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://ory.com",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
],
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"reo-census": "^1.2.8",
|
|
76
|
-
"@ory/argus": "0.
|
|
76
|
+
"@ory/argus": "0.14.0"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"typescript": "^6.0.2",
|