@ory/gemini-cli 0.1.3 → 0.2.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 +23 -9
- package/dist/cli/main.js +17 -0
- package/dist/handlers.js +51 -26
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -116,6 +116,8 @@ The extension is **fail-open** on its own infrastructure failures (network error
|
|
|
116
116
|
|
|
117
117
|
### Enable enforcement
|
|
118
118
|
|
|
119
|
+
After install the extension runs in **observe mode**: every tool call is checked against Ory Permissions, but a deny is recorded as a `permission.observe_deny` audit span and the tool runs anyway. This lets you see what *would* be blocked before turning on hard blocking.
|
|
120
|
+
|
|
119
121
|
1. **Turn on the user gate.** In your shell:
|
|
120
122
|
|
|
121
123
|
```bash
|
|
@@ -124,19 +126,29 @@ The extension is **fail-open** on its own infrastructure failures (network error
|
|
|
124
126
|
|
|
125
127
|
The next Gemini session opens a browser for PKCE login. Subsequent sessions reuse the persisted token until it expires.
|
|
126
128
|
|
|
127
|
-
2. **
|
|
129
|
+
2. **Bootstrap tuples for the built-in tools.** One idempotent command grants the current user `use` on every tool Gemini ships with (read_file, write_file, shell, …):
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npx -y -p @ory/gemini-cli ory-gemini permissions bootstrap
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
If a user identity is already cached at install time, the installer runs this for you automatically — re-run after adding tools, switching subjects, or changing the namespace.
|
|
136
|
+
|
|
137
|
+
3. **Check coverage.** `permissions status` probes every tool in the harness's catalog and prints allowed / denied per tool:
|
|
128
138
|
|
|
129
139
|
```bash
|
|
130
|
-
ory
|
|
131
|
-
--namespace AgentTools \
|
|
132
|
-
--object Bash \
|
|
133
|
-
--relation invoke \
|
|
134
|
-
--subject-id <your-user-subject-id>
|
|
140
|
+
npx -y -p @ory/gemini-cli ory-gemini permissions status
|
|
135
141
|
```
|
|
136
142
|
|
|
137
|
-
|
|
143
|
+
Add tuples for any MCP server tools or custom commands by hand, or via the Ory MCP server from inside Gemini (*"grant me use on the shell tool"*).
|
|
144
|
+
|
|
145
|
+
4. **Promote to enforce.** Once the observe-mode logs look right, switch over:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npx -y -p @ory/gemini-cli ory-gemini permissions enforce
|
|
149
|
+
```
|
|
138
150
|
|
|
139
|
-
|
|
151
|
+
Denies now block the tool call; Gemini shows the denial reason and the decision is recorded as a `tool.block` trace span with `blocked: true`. Switch back any time with `permissions observe`.
|
|
140
152
|
|
|
141
153
|
## CLI reference
|
|
142
154
|
|
|
@@ -145,6 +157,7 @@ npx -y -p @ory/gemini-cli ory-gemini install [--link
|
|
|
145
157
|
npx -y -p @ory/gemini-cli ory-gemini uninstall
|
|
146
158
|
npx -y -p @ory/gemini-cli ory-gemini configure [--project-url <url>] [--api-key <key>] [--audit-only]
|
|
147
159
|
npx -y -p @ory/gemini-cli ory-gemini agent <status|unregister> Manage the agent's OAuth2 identity
|
|
160
|
+
npx -y -p @ory/gemini-cli ory-gemini permissions <status|bootstrap|observe|enforce>
|
|
148
161
|
npx -y -p @ory/gemini-cli ory-gemini local <up|down|status|seed|logs|env|configure|reset>
|
|
149
162
|
npx -y -p @ory/gemini-cli ory-gemini status
|
|
150
163
|
```
|
|
@@ -152,7 +165,8 @@ npx -y -p @ory/gemini-cli ory-gemini status
|
|
|
152
165
|
Highlights:
|
|
153
166
|
|
|
154
167
|
- `agent status` — show the current persisted DCR identity for the agent.
|
|
155
|
-
- `
|
|
168
|
+
- `permissions observe` / `permissions enforce` — switch between "log denies, allow through" (the install default) and "block denies." `permissions bootstrap` writes `use` tuples for the harness's built-in tools so the promotion path doesn't require hand-writing relationships.
|
|
169
|
+
- `configure --audit-only` — kill switch that disables Ory entirely (no auth, no permission checks; only audit logging of tool invocations). For phased rollouts, prefer `permissions observe` over `--audit-only`.
|
|
156
170
|
- `local seed` / `local env` — reseed the test user, or print env vars for pointing other tools at the local stack.
|
|
157
171
|
|
|
158
172
|
## Troubleshooting
|
package/dist/cli/main.js
CHANGED
|
@@ -56,6 +56,10 @@ function main() {
|
|
|
56
56
|
switch (command) {
|
|
57
57
|
case "install":
|
|
58
58
|
install(args);
|
|
59
|
+
postInstallPermissions("ory-gemini", "gemini-cli").then(() => process.exit(0), (err) => {
|
|
60
|
+
console.error(err.message ?? err);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
});
|
|
59
63
|
break;
|
|
60
64
|
case "uninstall":
|
|
61
65
|
uninstall(args);
|
|
@@ -69,6 +73,12 @@ function main() {
|
|
|
69
73
|
process.exit(1);
|
|
70
74
|
});
|
|
71
75
|
break;
|
|
76
|
+
case "permissions":
|
|
77
|
+
(0, argus_1.runPermissionsCommand)("ory-gemini", "gemini-cli", args).then((code) => process.exit(code), (err) => {
|
|
78
|
+
console.error(err.message ?? err);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
});
|
|
81
|
+
break;
|
|
72
82
|
case "setup":
|
|
73
83
|
require("./setup.js");
|
|
74
84
|
break;
|
|
@@ -155,6 +165,12 @@ function manualSetup(args) {
|
|
|
155
165
|
process.argv = ["node", "setup.js", ...args];
|
|
156
166
|
require("./setup.js");
|
|
157
167
|
}
|
|
168
|
+
async function postInstallPermissions(binName, harness) {
|
|
169
|
+
const bootstrapped = await (0, argus_1.maybeAutoBootstrap)(binName, harness);
|
|
170
|
+
(0, argus_1.printPermissionsOnboardingHelp)(binName, harness, {
|
|
171
|
+
bootstrappedAutomatically: bootstrapped,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
158
174
|
function status() {
|
|
159
175
|
console.log("Ory Agent Plugin Status (Gemini CLI)");
|
|
160
176
|
console.log("====================================");
|
|
@@ -183,6 +199,7 @@ Commands:
|
|
|
183
199
|
install [--link] Install the Ory extension into Gemini CLI
|
|
184
200
|
uninstall Remove the Ory extension from Gemini CLI
|
|
185
201
|
configure Set or view Ory project URL and API key
|
|
202
|
+
permissions <cmd> Manage permission mode and tool tuples (status, bootstrap, observe, enforce)
|
|
186
203
|
setup [--project-dir] Write hooks directly to settings.json (fallback)
|
|
187
204
|
status Show plugin status and configuration
|
|
188
205
|
local <cmd> Manage local Ory dev environment (up, down, status, seed, ...)
|
package/dist/handlers.js
CHANGED
|
@@ -167,42 +167,67 @@ async function handleBeforeTool(input, client) {
|
|
|
167
167
|
subject,
|
|
168
168
|
spanAttributes: { toolName },
|
|
169
169
|
});
|
|
170
|
-
|
|
170
|
+
const mcpAttrs = {
|
|
171
|
+
toolName,
|
|
172
|
+
mcpServer: mcpTool.serverName,
|
|
173
|
+
mcpTool: mcpTool.toolName,
|
|
174
|
+
...inputSummary,
|
|
175
|
+
};
|
|
176
|
+
const decision = (0, argus_1.applyPermissionMode)(client, mcpResult.allowed, {
|
|
177
|
+
object: mcpTool.serverName,
|
|
178
|
+
relation: "use",
|
|
179
|
+
subjectId,
|
|
180
|
+
spanAttributes: mcpAttrs,
|
|
181
|
+
});
|
|
182
|
+
if (decision.kind === "allow") {
|
|
183
|
+
client.tracer.record("tool.invoke", "ok", { attributes: mcpAttrs });
|
|
184
|
+
return {};
|
|
185
|
+
}
|
|
186
|
+
if (decision.kind === "observe") {
|
|
171
187
|
client.tracer.record("tool.block", "denied", {
|
|
172
|
-
attributes: {
|
|
188
|
+
attributes: { ...mcpAttrs, allowed: false, ...(0, argus_1.alertAttributes)(false) },
|
|
189
|
+
});
|
|
190
|
+
client.tracer.record("tool.invoke", "ok", {
|
|
191
|
+
attributes: { ...mcpAttrs, allowed: false, observed: true },
|
|
173
192
|
});
|
|
174
|
-
return {
|
|
175
|
-
decision: "deny",
|
|
176
|
-
reason: (0, argus_1.formatDenialMessage)({ tool: toolName, subjectId, mcp: mcpTool }),
|
|
177
|
-
systemMessage: (0, argus_1.formatDenialSummary)({ tool: toolName, subjectId, mcp: mcpTool }),
|
|
178
|
-
};
|
|
193
|
+
return {};
|
|
179
194
|
}
|
|
180
|
-
client.tracer.record("tool.invoke", "ok", {
|
|
181
|
-
attributes: { toolName, mcpServer: mcpTool.serverName, mcpTool: mcpTool.toolName, ...inputSummary },
|
|
182
|
-
});
|
|
183
|
-
return {};
|
|
184
|
-
}
|
|
185
|
-
const namespace = resolveNamespace();
|
|
186
|
-
const result = await client.checkPermission({
|
|
187
|
-
namespace,
|
|
188
|
-
object: toolName,
|
|
189
|
-
relation: "use",
|
|
190
|
-
...subject,
|
|
191
|
-
}, { spanAttributes: { toolName } });
|
|
192
|
-
if (!result.allowed) {
|
|
193
195
|
client.tracer.record("tool.block", "denied", {
|
|
194
|
-
attributes: {
|
|
196
|
+
attributes: { ...mcpAttrs, allowed: false, ...(0, argus_1.alertAttributes)(true) },
|
|
195
197
|
});
|
|
196
198
|
return {
|
|
197
199
|
decision: "deny",
|
|
198
|
-
reason: (0, argus_1.formatDenialMessage)({ tool: toolName, subjectId,
|
|
199
|
-
systemMessage: (0, argus_1.formatDenialSummary)({ tool: toolName, subjectId,
|
|
200
|
+
reason: (0, argus_1.formatDenialMessage)({ tool: toolName, subjectId, mcp: mcpTool }),
|
|
201
|
+
systemMessage: (0, argus_1.formatDenialSummary)({ tool: toolName, subjectId, mcp: mcpTool }),
|
|
200
202
|
};
|
|
201
203
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
+
const namespace = resolveNamespace();
|
|
205
|
+
const decision = await (0, argus_1.checkAndDecide)(client, { namespace, object: toolName, relation: "use", ...subject }, { spanAttributes: { toolName } });
|
|
206
|
+
if (decision.kind === "fail_open") {
|
|
207
|
+
return handlePermissionError(decision.error, toolName, client);
|
|
208
|
+
}
|
|
209
|
+
const attrs = { toolName, ...inputSummary };
|
|
210
|
+
if (decision.kind === "allow") {
|
|
211
|
+
client.tracer.record("tool.invoke", "ok", { attributes: { ...attrs, allowed: true } });
|
|
212
|
+
return {};
|
|
213
|
+
}
|
|
214
|
+
if (decision.kind === "observe") {
|
|
215
|
+
client.tracer.record("tool.block", "denied", {
|
|
216
|
+
attributes: { ...attrs, allowed: false, ...(0, argus_1.alertAttributes)(false) },
|
|
217
|
+
});
|
|
218
|
+
client.tracer.record("tool.invoke", "ok", {
|
|
219
|
+
attributes: { ...attrs, allowed: false, observed: true },
|
|
220
|
+
});
|
|
221
|
+
return {};
|
|
222
|
+
}
|
|
223
|
+
client.tracer.record("tool.block", "denied", {
|
|
224
|
+
attributes: { ...attrs, allowed: false, ...(0, argus_1.alertAttributes)(true) },
|
|
204
225
|
});
|
|
205
|
-
return {
|
|
226
|
+
return {
|
|
227
|
+
decision: "deny",
|
|
228
|
+
reason: (0, argus_1.formatDenialMessage)({ tool: toolName, subjectId, namespace }),
|
|
229
|
+
systemMessage: (0, argus_1.formatDenialSummary)({ tool: toolName, subjectId, namespace }),
|
|
230
|
+
};
|
|
206
231
|
}
|
|
207
232
|
catch (err) {
|
|
208
233
|
return handlePermissionError(err, toolName, client);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/gemini-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Ory extension for Gemini CLI: 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",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"gemini-extension"
|
|
70
70
|
],
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@ory/argus": "0.
|
|
72
|
+
"@ory/argus": "0.2.0"
|
|
73
73
|
},
|
|
74
74
|
"engines": {
|
|
75
75
|
"node": ">=24"
|