@ory/opencode 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 +11 -8
- package/dist/cli/main.js +7 -0
- package/dist/plugin.d.ts +4 -4
- package/dist/plugin.js +13 -114
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -21,12 +21,14 @@ Run one command. It installs the plugin and walks you through connecting:
|
|
|
21
21
|
npx -y -p @ory/opencode ory-opencode install
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
It opens a guided setup **in your browser** where you pick how to connect — the default takes just a click:
|
|
25
25
|
|
|
26
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
27
|
- **Local** — run a complete Ory on your laptop with Docker. No account, no signup, no keys. Great for trying it out.
|
|
28
28
|
- **Audit-only** — skip Ory entirely and just log what OpenCode does.
|
|
29
29
|
|
|
30
|
+
> No browser available (CI, SSH, headless)? The same walkthrough runs right in your terminal instead — or force it with `--no-web`.
|
|
31
|
+
|
|
30
32
|
That's it. Confirm everything landed with:
|
|
31
33
|
|
|
32
34
|
```bash
|
|
@@ -68,6 +70,7 @@ If Ory is ever unreachable, the plugin gets out of the way and lets OpenCode kee
|
|
|
68
70
|
Everything the plugin does is observable out of the box — no configuration required:
|
|
69
71
|
|
|
70
72
|
- **Status at a glance.** `npx -y -p @ory/opencode ory-opencode status` shows what's configured, who's signed in, how many built-in tools your permissions cover, and the most recent tool-call activity.
|
|
73
|
+
- **Live dashboard.** `npx -y -p @ory/opencode ory-opencode 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.
|
|
71
74
|
- **Live traces.** Every tool call is recorded as an OpenTelemetry-style span. Watch them stream as the agent works:
|
|
72
75
|
|
|
73
76
|
```bash
|
|
@@ -85,7 +88,7 @@ When the watch-mode logs look right, turn on blocking with one command (setup al
|
|
|
85
88
|
npx -y -p @ory/opencode ory-opencode permissions enforce
|
|
86
89
|
```
|
|
87
90
|
|
|
88
|
-
Now a denied tool is actually blocked and OpenCode shows why. In watch mode, OpenCode's own permission prompt is left in place for you to answer; only an explicit allow or a block-mode deny changes that. 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 OpenCode in chat, e.g. *"grant me use of the bash tool."*
|
|
91
|
+
Now a denied tool is actually blocked and OpenCode shows why. In watch mode, OpenCode's own permission prompt is left in place for you to answer; only an explicit allow or a block-mode deny changes that. 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 OpenCode in chat, e.g. *"grant me use of the bash tool."*
|
|
89
92
|
|
|
90
93
|
## Also: add login to your own app
|
|
91
94
|
|
|
@@ -100,11 +103,10 @@ The guided setup covers most people. For scripted or CI setups, or to point at a
|
|
|
100
103
|
```bash
|
|
101
104
|
npx -y -p @ory/opencode ory-opencode configure \
|
|
102
105
|
--project-url https://<slug>.projects.oryapis.com \
|
|
103
|
-
--oauth2-client-id <sign-in client id>
|
|
104
|
-
--user-login
|
|
106
|
+
--oauth2-client-id <sign-in client id>
|
|
105
107
|
```
|
|
106
108
|
|
|
107
|
-
OpenCode'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`. The equivalent env vars are `ORY_PROJECT_URL`, `ORY_OAUTH2_CLIENT_ID`,
|
|
109
|
+
OpenCode'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`. 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`, `ORY_OAUTH2_CLIENT_ID`, and `ORY_AGENT_API_KEY`.
|
|
108
110
|
|
|
109
111
|
<details>
|
|
110
112
|
<summary>Create the sign-in client by hand</summary>
|
|
@@ -113,7 +115,7 @@ The guided setup normally does this. To do it yourself, create a **public** OAut
|
|
|
113
115
|
|
|
114
116
|
```bash
|
|
115
117
|
ory create oauth2-client --project <project-id> \
|
|
116
|
-
--name "
|
|
118
|
+
--name "Ory Agent Security · user login (PKCE)" \
|
|
117
119
|
--grant-type authorization_code,refresh_token \
|
|
118
120
|
--response-type code \
|
|
119
121
|
--scope openid,offline_access \
|
|
@@ -133,11 +135,12 @@ With nothing configured, the plugin still loads and runs in **pass-through mode*
|
|
|
133
135
|
## Commands
|
|
134
136
|
|
|
135
137
|
```
|
|
136
|
-
ory-opencode install | uninstall Install/remove; --reconfigure re-runs setup, --no-configure skips it
|
|
138
|
+
ory-opencode install | uninstall Install/remove; --reconfigure re-runs setup, --no-web forces the terminal wizard, --no-configure skips it
|
|
137
139
|
ory-opencode status Show configuration, identities, permission coverage, recent activity
|
|
140
|
+
ory-opencode dashboard Open the live browser dashboard (status + service health + Change stack)
|
|
138
141
|
ory-opencode watch Tail the live trace stream (OTel spans)
|
|
139
142
|
ory-opencode permissions <cmd> status | bootstrap | observe (watch) | enforce (block)
|
|
140
|
-
ory-opencode configure <flags> Point at a project by hand (--project-url, --oauth2-client-id, --
|
|
143
|
+
ory-opencode configure <flags> Point at a project by hand (--project-url, --oauth2-client-id, --audit-only)
|
|
141
144
|
ory-opencode agent <status|unregister> Manage OpenCode's own auto-created identity
|
|
142
145
|
ory-opencode local <up|down|status|…> Run / manage a local Ory in Docker
|
|
143
146
|
```
|
package/dist/cli/main.js
CHANGED
|
@@ -95,6 +95,12 @@ function main() {
|
|
|
95
95
|
case "watch":
|
|
96
96
|
(0, argus_1.runWatchCommand)("opencode", args);
|
|
97
97
|
break;
|
|
98
|
+
case "dashboard":
|
|
99
|
+
(0, argus_1.runDashboardCommand)("ory-opencode", "opencode", args).then((code) => process.exit(code), (err) => {
|
|
100
|
+
console.error(err.message ?? err);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
});
|
|
103
|
+
break;
|
|
98
104
|
case "version":
|
|
99
105
|
case "--version":
|
|
100
106
|
case "-v":
|
|
@@ -227,6 +233,7 @@ Commands:
|
|
|
227
233
|
local <cmd> Manage local Ory dev environment
|
|
228
234
|
(up, down, status, seed, logs, env, configure, reset)
|
|
229
235
|
status [--project-dir <path>] Show plugin configuration and status
|
|
236
|
+
dashboard Open the Ory Agent dashboard (system health + configuration)
|
|
230
237
|
watch [trace-file] Tail the trace stream (OTel spans) live
|
|
231
238
|
version Show version and the ory-agent-plugins build commit
|
|
232
239
|
|
package/dist/plugin.d.ts
CHANGED
|
@@ -13,10 +13,10 @@ export interface CreateOryPluginDeps {
|
|
|
13
13
|
* context and returns a Hooks object. Hook functions use a mutating
|
|
14
14
|
* (input, output) => Promise<void> pattern.
|
|
15
15
|
*
|
|
16
|
-
* The
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* The user login runs on every session at startup — it emits the audit
|
|
17
|
+
* span, refreshes tokens, and may launch a browser flow when interactive.
|
|
18
|
+
* It never blocks the session; enforcement is governed solely by
|
|
19
|
+
* permissionMode at tool-call time.
|
|
20
20
|
*/
|
|
21
21
|
export declare function createOryPlugin(clientOrConfig?: OryAgentClient | {
|
|
22
22
|
projectUrl: string;
|
package/dist/plugin.js
CHANGED
|
@@ -9,10 +9,10 @@ const argus_1 = require("@ory/argus");
|
|
|
9
9
|
* context and returns a Hooks object. Hook functions use a mutating
|
|
10
10
|
* (input, output) => Promise<void> pattern.
|
|
11
11
|
*
|
|
12
|
-
* The
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* The user login runs on every session at startup — it emits the audit
|
|
13
|
+
* span, refreshes tokens, and may launch a browser flow when interactive.
|
|
14
|
+
* It never blocks the session; enforcement is governed solely by
|
|
15
|
+
* permissionMode at tool-call time.
|
|
16
16
|
*/
|
|
17
17
|
function createOryPlugin(clientOrConfig, deps = {}) {
|
|
18
18
|
return {
|
|
@@ -79,92 +79,25 @@ function createChatMessageHandler(client) {
|
|
|
79
79
|
}
|
|
80
80
|
// ─── Session verification on startup ────────────────────────────────
|
|
81
81
|
async function verifyOnStartup(client, deps) {
|
|
82
|
-
// Run the user login. The
|
|
83
|
-
// primitive, so allowBlock is false — the flow emits the user.auth
|
|
82
|
+
// Run the user login on every session. The flow emits the user.auth
|
|
84
83
|
// audit span, refreshes tokens, and may prompt the user, but never
|
|
85
|
-
// prevents the session from starting
|
|
86
|
-
//
|
|
87
|
-
// logging.
|
|
84
|
+
// prevents the session from starting — enforcement is governed solely
|
|
85
|
+
// by permissionMode at tool-call time.
|
|
88
86
|
const userGate = deps.userLogin ?? argus_1.ensureUserAuthenticated;
|
|
89
|
-
|
|
87
|
+
await userGate(client, {
|
|
90
88
|
binName: "ory-opencode",
|
|
91
89
|
harness: "opencode",
|
|
92
|
-
allowBlock: false,
|
|
93
90
|
});
|
|
94
91
|
// Resolve the agent identity (machine credentials). Never blocks;
|
|
95
92
|
// attaches the agent's bearer token to outgoing Ory API calls.
|
|
96
93
|
const agentGate = deps.agentGate ?? argus_1.ensureAgentIdentity;
|
|
97
94
|
await agentGate(client, { projectUrl: (0, argus_1.resolveConfig)().projectUrl, harness: "opencode" });
|
|
98
95
|
// Record the user→agent delegation so the audit trail captures that
|
|
99
|
-
// this user authorized this agent for this session.
|
|
100
|
-
// requires both principals to be populated, and
|
|
101
|
-
// and swallowed (fail-open — delegation tracking is
|
|
102
|
-
// enforcement).
|
|
103
|
-
await
|
|
104
|
-
if (decision.mode !== "disabled") {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
const resolved = (0, argus_1.resolveConfig)();
|
|
108
|
-
if (resolved.auditOnly) {
|
|
109
|
-
client.logger.info("config.audit_only", {
|
|
110
|
-
message: "Audit-only mode enabled. Auth and permission checks are disabled.",
|
|
111
|
-
});
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (!resolved.projectUrl) {
|
|
115
|
-
client.logger.warn("config.not_configured", {
|
|
116
|
-
message: "Ory plugin is not configured. Auth and permission checks are disabled. " +
|
|
117
|
-
"Run 'npx ory-opencode configure' to connect to an Ory project.",
|
|
118
|
-
});
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
const sessionToken = process.env.ORY_SESSION_TOKEN;
|
|
122
|
-
const oauth2Token = process.env.ORY_OAUTH2_TOKEN;
|
|
123
|
-
if (sessionToken) {
|
|
124
|
-
try {
|
|
125
|
-
const session = await client.verifySession(sessionToken);
|
|
126
|
-
if (!session.active) {
|
|
127
|
-
client.logger.warn("session.inactive", {
|
|
128
|
-
message: "Ory session is not active. Re-authenticate to enable auth checks.",
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
catch (err) {
|
|
133
|
-
const oryErr = err;
|
|
134
|
-
client.logger.warn("session.verify_failed", {
|
|
135
|
-
code: isOryError(err) ? oryErr.code : "unknown",
|
|
136
|
-
message: err instanceof Error ? err.message : String(err),
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
if (oauth2Token) {
|
|
142
|
-
try {
|
|
143
|
-
const tokenInfo = await client.introspectToken(oauth2Token);
|
|
144
|
-
if (!tokenInfo.active) {
|
|
145
|
-
client.logger.warn("oauth2.token_inactive", {
|
|
146
|
-
message: "Ory OAuth2 token is not active. Obtain a new token to enable auth checks.",
|
|
147
|
-
});
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
client.logger.info("oauth2.session_authenticated", {
|
|
151
|
-
clientId: tokenInfo.clientId,
|
|
152
|
-
subject: tokenInfo.subject,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
catch (err) {
|
|
156
|
-
const oryErr = err;
|
|
157
|
-
client.logger.warn("oauth2.introspect_failed", {
|
|
158
|
-
code: isOryError(err) ? oryErr.code : "unknown",
|
|
159
|
-
message: err instanceof Error ? err.message : String(err),
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
client.logger.warn("session.no_credentials", {
|
|
165
|
-
message: "Neither ORY_SESSION_TOKEN nor ORY_OAUTH2_TOKEN is set. " +
|
|
166
|
-
"Skipping authentication.",
|
|
167
|
-
});
|
|
96
|
+
// this user authorized this agent for this session. Written at most once
|
|
97
|
+
// per install. Best-effort: requires both principals to be populated, and
|
|
98
|
+
// any failure is logged and swallowed (fail-open — delegation tracking is
|
|
99
|
+
// for audit, not enforcement).
|
|
100
|
+
await (0, argus_1.writeUserDelegatesAgent)(client);
|
|
168
101
|
}
|
|
169
102
|
// ─── permission.ask ─────────────────────────────────────────────────
|
|
170
103
|
function createPermissionHandler(client, _options) {
|
|
@@ -423,40 +356,6 @@ function createToolAfterHandler(client) {
|
|
|
423
356
|
function resolveNamespace() {
|
|
424
357
|
return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools";
|
|
425
358
|
}
|
|
426
|
-
/**
|
|
427
|
-
* Write the user→agent delegation tuple. Idempotent and fail-open:
|
|
428
|
-
* requires both principal subjects to be populated; any error (including
|
|
429
|
-
* an unconfigured projectUrl, which manifests as a network_error) is
|
|
430
|
-
* logged and swallowed. Purely audit-trail data — does not affect
|
|
431
|
-
* enforcement.
|
|
432
|
-
*/
|
|
433
|
-
async function recordUserDelegatesAgent(client) {
|
|
434
|
-
const user = client.userPrincipal.subject;
|
|
435
|
-
const agent = client.agentPrincipal.subject;
|
|
436
|
-
if (!user || !agent) {
|
|
437
|
-
client.logger.debug("delegation.skip", {
|
|
438
|
-
reason: "missing principal",
|
|
439
|
-
hasUser: !!user,
|
|
440
|
-
hasAgent: !!agent,
|
|
441
|
-
});
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
try {
|
|
445
|
-
await client.createRelationship({
|
|
446
|
-
namespace: resolveNamespace(),
|
|
447
|
-
object: `agent:${agent}`,
|
|
448
|
-
relation: "delegate",
|
|
449
|
-
subjectId: `user:${user}`,
|
|
450
|
-
}, { spanAttributes: { delegation: "user-to-agent" } });
|
|
451
|
-
}
|
|
452
|
-
catch (err) {
|
|
453
|
-
const oryErr = err;
|
|
454
|
-
client.logger.warn("delegation.user_to_agent.failed", {
|
|
455
|
-
code: oryErr.code,
|
|
456
|
-
message: oryErr.message,
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
359
|
/**
|
|
461
360
|
* Map an Ory permission decision onto OpenCode's `permission.ask` output.
|
|
462
361
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Ory plugin for OpenCode: 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",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
],
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"reo-census": "^1.2.8",
|
|
67
|
-
"@ory/argus": "0.
|
|
67
|
+
"@ory/argus": "0.14.0"
|
|
68
68
|
},
|
|
69
69
|
"engines": {
|
|
70
70
|
"node": ">=22"
|