@ory/goose 0.14.0 → 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 +70 -65
- package/dist/cli/main.js +16 -14
- package/dist/cli/setup.d.ts +4 -4
- package/dist/cli/setup.js +37 -16
- package/dist/handlers.d.ts +2 -8
- package/dist/handlers.js +73 -203
- package/dist/hook.js +15 -5
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Security and developer experience for [Goose](https://github.com/block/goose) (Block's open-source coding agent), powered by [Ory](https://ory.com).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Goose 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
|
-
|
|
7
|
+
One command installs two independent halves:
|
|
8
|
+
|
|
9
|
+
- **Developer experience** — the Ory skill catalog, the local Ory dev stack, 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
|
Verified against the `goose` binary (v1.39.0): the plugin layout, the `hooks.json` schema, and the discovery paths all match Goose's shell-hook plugin contract (see [block/goose#9304](https://github.com/block/goose/pull/9304)).
|
|
10
13
|
|
|
@@ -17,117 +20,121 @@ Verified against the `goose` binary (v1.39.0): the plugin layout, the `hooks.jso
|
|
|
17
20
|
|
|
18
21
|
## Get started
|
|
19
22
|
|
|
20
|
-
Run one command
|
|
23
|
+
Run one command:
|
|
21
24
|
|
|
22
25
|
```bash
|
|
23
26
|
npx -y -p @ory/goose ory-goose install
|
|
24
27
|
```
|
|
25
28
|
|
|
26
|
-
The installer drops a self-contained plugin at `<project>/.agents/plugins/ory/`, where Goose finds it on its own at startup — no `goose configure` step and nothing else in your Goose config gets touched.
|
|
27
|
-
|
|
28
|
-
- **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.
|
|
29
|
-
- **Local** — run a complete Ory on your laptop with Docker. No account, no signup, no keys. Great for trying it out.
|
|
30
|
-
- **Audit-only** — skip Ory entirely and just log what Goose does.
|
|
29
|
+
The installer drops a self-contained plugin at `<project>/.agents/plugins/ory/`, where Goose finds it on its own at startup — no `goose configure` step and nothing else in your Goose config gets touched. Add `--global` to install for every project (at `~/.agents/plugins/ory/`).
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
That's it. Confirm everything landed with:
|
|
31
|
+
Confirm everything landed with:
|
|
35
32
|
|
|
36
33
|
```bash
|
|
37
34
|
npx -y -p @ory/goose ory-goose status
|
|
38
35
|
```
|
|
39
36
|
|
|
40
|
-
`status` is your one-stop check: what's configured, who's signed in, which tools are covered by permissions, whether the plugin is registered (project and global), and recent activity.
|
|
37
|
+
`status` is your one-stop check: what's configured, who's signed in, which tools are covered by permissions, whether the plugin is registered (project and global), and recent activity. Until you connect Agent Security, the identity and permission rows say so and name what's missing.
|
|
38
|
+
|
|
39
|
+
## Skills and commands
|
|
40
|
+
|
|
41
|
+
Installing the plugin drops the full Ory playbook catalog into Goose. **Skills** are vetted playbooks Goose runs when you ask for them in chat — plain language is enough.
|
|
42
|
+
|
|
43
|
+
| Skill | What it does for you |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `ory-auth-setup` | Adds a complete auth system to your app — login, registration, recovery, verification, settings — on [Ory Elements](https://github.com/ory/elements) |
|
|
46
|
+
| `ory-login-flow` | Builds just the pages, wired to Ory's self-service flows |
|
|
47
|
+
| `ory-social-login` | "Sign in with…" for Google, GitHub, Apple, Microsoft, Discord, Slack, GitLab, Facebook |
|
|
48
|
+
| `ory-local-dev` | Develops and tests login/permission flows against a local Ory — no project, no account, offline |
|
|
49
|
+
| `ory-permissions-onboarding` | Walks a fresh install from observe mode to enforced per-tool permissions without getting blocked |
|
|
50
|
+
| `ory-build-agent` | Drops `@ory/argus` into an agent *you* own — Claude Agent SDK, OpenAI Agents, Mastra, Vercel AI, LangGraph/PydanticAI |
|
|
51
|
+
| `ory-build-integration` | Wires Ory into your app: Action webhooks, JWT validation at a gateway, live event streams |
|
|
52
|
+
| `ory-contribute-integration` | Authors and submits an integration to the public `ory/integrates` registry |
|
|
53
|
+
| `ory-e2b-sandbox` | Scaffolds an E2B sandbox template that boots with this plugin preinstalled |
|
|
54
|
+
| `ory-temporal-worker` | Scaffolds a Temporal TypeScript worker where every Activity is authenticated, authorized, and audited |
|
|
55
|
+
|
|
56
|
+
The local stack has its own playbooks — ask for them by name:
|
|
57
|
+
|
|
58
|
+
| Command | What it does |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `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 |
|
|
61
|
+
| `ory-local-down` | Stops it, keeping your data volumes |
|
|
62
|
+
| `ory-temporal-up` | Starts a local Temporal dev server for the `ory-temporal-worker` scaffold |
|
|
41
63
|
|
|
42
|
-
|
|
64
|
+
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`.
|
|
65
|
+
|
|
66
|
+
So: ask Goose *"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
|
-
|
|
70
|
+
Out of the box, every tool Goose 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 (
|
|
49
|
-
- **What it's allowed to do.** Before a tool runs, Ory checks whether it's permitted. It starts in **
|
|
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; Goose (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
|
-
Under the hood, Goose runs the plugin as a small program for each thing it does, handing it the event and reading back a decision. The `hooks.json` file maps that: **SessionStart** signs you in, **PreToolUse** (the one point that can block) runs the permission check, and the rest — **PostToolUse, BeforeReadFile, AfterFileEdit, BeforeShellExecution, Stop, SessionEnd** — just record activity. If Ory is ever unreachable, the plugin gets out of the way and lets Goose keep working, so it can't lock you out.
|
|
77
|
+
Under the hood, Goose runs the plugin as a small program for each thing it does, handing it the event and reading back a decision. The `hooks.json` file maps that: **SessionStart** signs you in (once connected), **PreToolUse** (the one point that can block) runs the permission check, and the rest — **PostToolUse, BeforeReadFile, AfterFileEdit, BeforeShellExecution, Stop, SessionEnd** — just record activity. If Ory is ever unreachable, the plugin gets out of the way and lets Goose 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
|
|
81
|
+
Everything the plugin does is observable out of the box:
|
|
57
82
|
|
|
58
|
-
- **
|
|
59
|
-
- **Live dashboard.** `npx -y -p @ory/goose ory-goose 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/goose/ory-agent-debug.log`. View events, decisions, and errors live with:
|
|
61
84
|
|
|
62
85
|
```bash
|
|
63
86
|
npx -y -p @ory/goose ory-goose watch
|
|
64
87
|
```
|
|
65
88
|
|
|
66
|
-
|
|
67
|
-
- **
|
|
89
|
+
Set `ORY_AGENT_LOG_FILE` to override the path; set it empty to disable file persistence.
|
|
90
|
+
- **Live debug.** Launch Goose 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
|
-
|
|
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/goose ory-goose permissions
|
|
97
|
+
npx -y -p @ory/goose ory-goose permissions # what the project grants, and the live mode
|
|
75
98
|
```
|
|
76
99
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
## Also: add login to your own app
|
|
80
|
-
|
|
81
|
-
Beyond securing Goose, the plugin helps you build Ory into whatever you're working on — backed by a local Ory, so it needs nothing but Docker. Ask Goose in plain language, e.g. *"add Ory auth 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.
|
|
100
|
+
Then a denied tool is actually blocked and Goose shows why.
|
|
82
101
|
|
|
83
|
-
|
|
102
|
+
## Connect to Ory Agent Security
|
|
84
103
|
|
|
85
|
-
|
|
104
|
+
Copy the connection details from the [Ory Console](https://console.ory.sh) under **Agent Security**:
|
|
86
105
|
|
|
87
|
-
|
|
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/goose ory-goose configure \
|
|
91
114
|
--project-url https://<slug>.projects.oryapis.com \
|
|
92
|
-
--
|
|
115
|
+
--agent-security-url https://agents.console.ory.com
|
|
93
116
|
```
|
|
94
117
|
|
|
95
|
-
|
|
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
|
-
|
|
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 so sign-in survives a busy port:
|
|
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
|
-
|
|
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 Goose session registers its own identity automatically on first use.
|
|
116
123
|
|
|
117
|
-
|
|
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
|
-
|
|
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-goose install | uninstall Install
|
|
131
|
+
ory-goose install | uninstall Install (add --project-url to also connect Agent Security) / remove; --global installs for every project
|
|
125
132
|
ory-goose status Show configuration, identities, permission coverage, registration, recent activity
|
|
126
|
-
ory-goose
|
|
127
|
-
ory-goose
|
|
128
|
-
ory-goose permissions <cmd> status | bootstrap | observe (watch) | enforce (block)
|
|
129
|
-
ory-goose configure <flags> Point at a project by hand (--project-url, --oauth2-client-id, --audit-only)
|
|
133
|
+
ory-goose permissions <cmd> status (read-only; grants + posture live in the Ory Console)
|
|
134
|
+
ory-goose configure <flags> Connect a project (--project-url) or --disconnect
|
|
130
135
|
ory-goose agent <status|unregister> Manage Goose's own auto-created identity
|
|
136
|
+
ory-goose local <up|down|status|…> Run / manage a local Ory in Docker
|
|
137
|
+
ory-goose version Print plugin, core, and Node versions (--json for machine-readable)
|
|
131
138
|
```
|
|
132
139
|
|
|
133
140
|
All prefixed with `npx -y -p @ory/goose`.
|
|
@@ -135,16 +142,14 @@ All prefixed with `npx -y -p @ory/goose`.
|
|
|
135
142
|
## Troubleshooting
|
|
136
143
|
|
|
137
144
|
- **The local Ory fails to start** — make sure Docker is running and ports `4000`, `4100`, `4455`, and `16686` are free.
|
|
138
|
-
- **Browser sign-in loops** — reset with `ory-goose agent unregister` and try again.
|
|
139
|
-
- **
|
|
145
|
+
- **Browser sign-in loops** (after connecting) — reset with `ory-goose agent unregister` and try again.
|
|
146
|
+
- **Running an older CLI than expected** — `npx -p @ory/goose` (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/goose ory-goose version`. To force the current release, clear the cache and reinstall: `rm -rf ~/.npm/_npx` then `npx -y -p @ory/goose ory-goose install`. Pinning an exact version (`@ory/goose@<version>`) also bypasses the cached copy.
|
|
140
147
|
- **Goose isn't picking up the plugin** — confirm the tree at `<project>/.agents/plugins/ory/` exists (or re-run the installer), and restart Goose.
|
|
141
|
-
- **Want to see what's happening** — `npx -y -p @ory/goose ory-goose status` for a snapshot, `npx -y -p @ory/goose ory-goose 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/goose/` (see [See what's happening](#see-whats-happening)).
|
|
142
148
|
|
|
143
149
|
## Learn more
|
|
144
150
|
|
|
145
151
|
- [Ory documentation](https://www.ory.com/docs/) · [Ory Console](https://console.ory.sh) · [Ory Elements](https://github.com/ory/elements)
|
|
146
152
|
- [Goose repository](https://github.com/block/goose)
|
|
147
|
-
- [Repo README](../../README.md) and [AGENTS.md](../../AGENTS.md) — full env-var and permission-mode reference
|
|
148
153
|
|
|
149
154
|
## License
|
|
150
155
|
|
package/dist/cli/main.js
CHANGED
|
@@ -57,6 +57,11 @@ const PLUGIN_NAME = "ory";
|
|
|
57
57
|
const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
|
|
58
58
|
function main() {
|
|
59
59
|
const [command, ...args] = process.argv.slice(2);
|
|
60
|
+
// `--help` after a matched subcommand: print usage, do nothing else.
|
|
61
|
+
// Without this the switch below ignores the flag and `install --help`
|
|
62
|
+
// performs a real install (#221).
|
|
63
|
+
if ((0, argus_1.shouldPrintHelp)(command, args))
|
|
64
|
+
return help();
|
|
60
65
|
switch (command) {
|
|
61
66
|
case "install":
|
|
62
67
|
(0, argus_1.beginDeferNextSteps)();
|
|
@@ -68,7 +73,10 @@ function main() {
|
|
|
68
73
|
break;
|
|
69
74
|
case "uninstall":
|
|
70
75
|
uninstall(args);
|
|
71
|
-
(0, argus_1.clearCredentialsForUninstall)(
|
|
76
|
+
(0, argus_1.clearCredentialsForUninstall)({
|
|
77
|
+
harness: "goose",
|
|
78
|
+
purge: args.includes("--purge"),
|
|
79
|
+
}).then(() => process.exit(0), (err) => {
|
|
72
80
|
console.error(err.message ?? err);
|
|
73
81
|
process.exit(1);
|
|
74
82
|
});
|
|
@@ -77,7 +85,7 @@ function main() {
|
|
|
77
85
|
(0, argus_1.runConfigureCommand)("ory-goose", args);
|
|
78
86
|
break;
|
|
79
87
|
case "agent":
|
|
80
|
-
(0, argus_1.runAgentCommand)("ory-goose", args).then((code) => process.exit(code), (err) => {
|
|
88
|
+
(0, argus_1.runAgentCommand)("ory-goose", "goose", args).then((code) => process.exit(code), (err) => {
|
|
81
89
|
console.error(err.message ?? err);
|
|
82
90
|
process.exit(1);
|
|
83
91
|
});
|
|
@@ -97,17 +105,11 @@ function main() {
|
|
|
97
105
|
process.exit(1);
|
|
98
106
|
});
|
|
99
107
|
break;
|
|
100
|
-
case "local":
|
|
101
|
-
(0, argus_1.runLocalCommand)("ory-goose", args).catch((err) => {
|
|
102
|
-
console.error(err.message ?? err);
|
|
103
|
-
process.exit(1);
|
|
104
|
-
});
|
|
105
|
-
break;
|
|
106
108
|
case "watch":
|
|
107
|
-
(0, argus_1.runWatchCommand)("goose", args);
|
|
109
|
+
(0, argus_1.runWatchCommand)("ory-goose", "goose", args).then((code) => process.exit(code), (err) => { console.error(err.message ?? err); process.exit(1); });
|
|
108
110
|
break;
|
|
109
|
-
case "
|
|
110
|
-
(0, argus_1.
|
|
111
|
+
case "local":
|
|
112
|
+
(0, argus_1.runLocalCommand)("ory-goose", args).catch((err) => {
|
|
111
113
|
console.error(err.message ?? err);
|
|
112
114
|
process.exit(1);
|
|
113
115
|
});
|
|
@@ -173,11 +175,11 @@ Commands:
|
|
|
173
175
|
uninstall [--global] Remove the Ory plugin
|
|
174
176
|
configure Set or view Ory project URL and API key
|
|
175
177
|
agent <cmd> Manage the agent's OAuth2 (DCR) identity (status, unregister)
|
|
176
|
-
permissions
|
|
178
|
+
permissions Show the live permission mode and per-tool coverage
|
|
177
179
|
setup [--project-dir] Write the plugin tree directly (advanced)
|
|
178
180
|
status Show plugin status and configuration
|
|
179
|
-
|
|
180
|
-
|
|
181
|
+
watch [--json] [--lines <count>]
|
|
182
|
+
Follow the live activity/debug log
|
|
181
183
|
version Show version and the ory-agent-plugins build commit
|
|
182
184
|
local <cmd> Manage local Ory dev environment (up, down, status, seed, ...)
|
|
183
185
|
|
package/dist/cli/setup.d.ts
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* - `~/.agents/plugins/<name>/` (user-global, with --global)
|
|
8
8
|
*
|
|
9
9
|
* Each plugin dir holds a `plugin.json` manifest and `hooks/hooks.json`
|
|
10
|
-
* describing the hook commands. We render both fresh at install time
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* describing the hook commands. We render both fresh at install time because
|
|
11
|
+
* the hook command points at the runtime shim resolved by this install. No
|
|
12
|
+
* `goose configure`, config edit, or MCP registration is needed for the gate —
|
|
13
|
+
* discovery is purely directory-based.
|
|
14
14
|
*
|
|
15
15
|
* The plugin dir layout (plugin.json + hooks/hooks.json) and discovery paths
|
|
16
16
|
* are verified against goose v1.39.0.
|
package/dist/cli/setup.js
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
* - `~/.agents/plugins/<name>/` (user-global, with --global)
|
|
9
9
|
*
|
|
10
10
|
* Each plugin dir holds a `plugin.json` manifest and `hooks/hooks.json`
|
|
11
|
-
* describing the hook commands. We render both fresh at install time
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* describing the hook commands. We render both fresh at install time because
|
|
12
|
+
* the hook command points at the runtime shim resolved by this install. No
|
|
13
|
+
* `goose configure`, config edit, or MCP registration is needed for the gate —
|
|
14
|
+
* discovery is purely directory-based.
|
|
15
15
|
*
|
|
16
16
|
* The plugin dir layout (plugin.json + hooks/hooks.json) and discovery paths
|
|
17
17
|
* are verified against goose v1.39.0.
|
|
@@ -64,8 +64,8 @@ const path = __importStar(require("node:path"));
|
|
|
64
64
|
const argus_1 = require("@ory/argus");
|
|
65
65
|
const assets_js_1 = require("./assets.js");
|
|
66
66
|
const PACKAGE_NAME = "@ory/goose";
|
|
67
|
-
const HOOK_BIN = "ory-goose-hook";
|
|
68
67
|
const PLUGIN_NAME = "ory";
|
|
68
|
+
const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
|
|
69
69
|
/**
|
|
70
70
|
* Resolve the Goose plugin root: `~/.agents/plugins/ory` with --global, else
|
|
71
71
|
* `<projectDir>/.agents/plugins/ory`.
|
|
@@ -97,19 +97,18 @@ function renderManifest(version) {
|
|
|
97
97
|
return {
|
|
98
98
|
name: PLUGIN_NAME,
|
|
99
99
|
version,
|
|
100
|
-
description: "Ory authentication, authorization, and
|
|
100
|
+
description: "Ory authentication, authorization, and activity auditing for Goose",
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
|
-
* Render hooks/hooks.json. Each event maps to the
|
|
105
|
-
*
|
|
106
|
-
* only event that can block; the rest are observability hooks.
|
|
104
|
+
* Render hooks/hooks.json. Each event maps to the runtime shim resolved at
|
|
105
|
+
* install time, so no package resolution happens per tool call. PreToolUse is
|
|
106
|
+
* the only event that can block; the rest are observability hooks.
|
|
107
107
|
*
|
|
108
108
|
* The hooks.json schema (per-event arrays of `{ type, command }`, optional
|
|
109
109
|
* regex `matcher`/`pattern`) is verified against goose v1.39.0.
|
|
110
110
|
*/
|
|
111
|
-
function renderHooks(
|
|
112
|
-
const command = (0, argus_1.pinHookToLocalRegistry)(`npx -y -p ${PACKAGE_NAME}@${version} ${HOOK_BIN}`);
|
|
111
|
+
function renderHooks(command) {
|
|
113
112
|
const entry = [{ type: "command", command }];
|
|
114
113
|
return {
|
|
115
114
|
hooks: {
|
|
@@ -127,20 +126,22 @@ function renderHooks(version) {
|
|
|
127
126
|
},
|
|
128
127
|
};
|
|
129
128
|
}
|
|
130
|
-
function writePluginTree(pluginRoot, version) {
|
|
129
|
+
function writePluginTree(pluginRoot, version, runtime) {
|
|
131
130
|
fs.mkdirSync(pluginRoot, { recursive: true });
|
|
132
131
|
fs.writeFileSync(path.join(pluginRoot, "plugin.json"), JSON.stringify(renderManifest(version), null, 2) + "\n");
|
|
133
132
|
const hooksDir = path.join(pluginRoot, "hooks");
|
|
134
133
|
fs.mkdirSync(hooksDir, { recursive: true });
|
|
135
|
-
fs.writeFileSync(path.join(hooksDir, "hooks.json"), JSON.stringify(renderHooks(
|
|
134
|
+
fs.writeFileSync(path.join(hooksDir, "hooks.json"), JSON.stringify(renderHooks((0, argus_1.requireHookCommand)(runtime)), null, 2) + "\n");
|
|
136
135
|
(0, assets_js_1.installGooseOryAssets)(pluginRoot);
|
|
137
136
|
}
|
|
138
137
|
function runSetup(args) {
|
|
139
138
|
const version = readPackageVersion();
|
|
140
139
|
if (args.print) {
|
|
140
|
+
// Nothing is resolved or written on a --print run, so the hook command is
|
|
141
|
+
// shown as the shim path it will point at.
|
|
141
142
|
console.log(JSON.stringify({
|
|
142
143
|
"plugin.json": renderManifest(version),
|
|
143
|
-
"hooks/hooks.json": renderHooks(
|
|
144
|
+
"hooks/hooks.json": renderHooks(`node "<runtime shim>"`),
|
|
144
145
|
}, null, 2));
|
|
145
146
|
return;
|
|
146
147
|
}
|
|
@@ -153,9 +154,26 @@ function runSetup(args) {
|
|
|
153
154
|
(0, assets_js_1.uninstallGooseOryAssets)(pluginRoot);
|
|
154
155
|
fs.rmSync(pluginRoot, { recursive: true, force: true });
|
|
155
156
|
console.log(`Removed Ory plugin from ${pluginRoot}`);
|
|
157
|
+
(0, argus_1.removeRuntimeWiring)("goose");
|
|
158
|
+
for (const pruned of (0, argus_1.pruneRuntimeStores)()) {
|
|
159
|
+
console.log(` Removed runtime: ${pruned}`);
|
|
160
|
+
}
|
|
156
161
|
return;
|
|
157
162
|
}
|
|
158
|
-
|
|
163
|
+
const runtime = (0, argus_1.wireRuntime)({
|
|
164
|
+
harness: "goose",
|
|
165
|
+
packageName: PACKAGE_NAME,
|
|
166
|
+
packageRoot: PACKAGE_ROOT,
|
|
167
|
+
installCommand: "npx -y -p @ory/goose ory-goose install",
|
|
168
|
+
args: process.argv.slice(2),
|
|
169
|
+
});
|
|
170
|
+
console.log(runtime.target.kind === "linked"
|
|
171
|
+
? `Runtime: linked to ${runtime.target.packageDir} (dev)`
|
|
172
|
+
: `Runtime: ${runtime.target.packageName}@${runtime.target.version} in ${runtime.target.storeDir}`);
|
|
173
|
+
for (const pruned of runtime.prunedStores) {
|
|
174
|
+
console.log(` Removed stale runtime: ${pruned}`);
|
|
175
|
+
}
|
|
176
|
+
writePluginTree(pluginRoot, version, runtime);
|
|
159
177
|
console.log(`Ory plugin installed to ${pluginRoot}`);
|
|
160
178
|
console.log(` plugin.json + hooks/hooks.json + skills/ written.`);
|
|
161
179
|
(0, argus_1.printNextSteps)("Goose", "npx -y -p @ory/goose ory-goose-setup --uninstall", {
|
|
@@ -176,7 +194,10 @@ if (require.main === module) {
|
|
|
176
194
|
// Direct `-setup --uninstall` path: also clear stored Ory credentials.
|
|
177
195
|
// (The `ory-goose uninstall` command handles this itself.)
|
|
178
196
|
if (process.argv.includes("--uninstall")) {
|
|
179
|
-
(0, argus_1.clearCredentialsForUninstall)(
|
|
197
|
+
(0, argus_1.clearCredentialsForUninstall)({
|
|
198
|
+
harness: "goose",
|
|
199
|
+
purge: process.argv.includes("--purge"),
|
|
200
|
+
}).then(() => process.exit(0), (err) => {
|
|
180
201
|
console.error(err.message ?? err);
|
|
181
202
|
process.exit(1);
|
|
182
203
|
});
|
package/dist/handlers.d.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import { OryAgentClient,
|
|
1
|
+
import { OryAgentClient, ensureAgentIdentity, ensureUserAuthenticated } from "@ory/argus";
|
|
2
2
|
import type { GooseHookInput, GooseHookOutput } from "./types.js";
|
|
3
3
|
export interface HandleHookEventDeps {
|
|
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
6
|
}
|
|
9
|
-
/**
|
|
10
|
-
* Route a Goose hook event to the appropriate Ory integration.
|
|
11
|
-
*
|
|
12
|
-
* Event-name set verified against goose v1.39.0.
|
|
13
|
-
*/
|
|
7
|
+
/** Route a Goose hook event to the appropriate Ory integration. */
|
|
14
8
|
export declare function handleHookEvent(input: GooseHookInput, client: OryAgentClient, deps?: HandleHookEventDeps): Promise<GooseHookOutput>;
|
package/dist/handlers.js
CHANGED
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.handleHookEvent = handleHookEvent;
|
|
4
4
|
const argus_1 = require("@ory/argus");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
* Event-name set verified against goose v1.39.0.
|
|
9
|
-
*/
|
|
5
|
+
const HARNESS = "goose";
|
|
6
|
+
/** Route a Goose hook event to the appropriate Ory integration. */
|
|
10
7
|
async function handleHookEvent(input, client, deps = {}) {
|
|
11
8
|
const event = input.event;
|
|
12
9
|
client.logger.debug("hook.received", {
|
|
@@ -14,216 +11,89 @@ async function handleHookEvent(input, client, deps = {}) {
|
|
|
14
11
|
sessionId: input.session_id,
|
|
15
12
|
toolName: input.tool_name,
|
|
16
13
|
});
|
|
17
|
-
|
|
18
|
-
client.tracer.setContext({
|
|
19
|
-
traceId: (0, argus_1.deriveTraceId)(input.session_id),
|
|
20
|
-
sessionId: input.session_id,
|
|
21
|
-
});
|
|
22
|
-
try {
|
|
14
|
+
return (0, argus_1.withHookContext)(client, { sessionId: input.session_id }, async () => {
|
|
23
15
|
switch (event) {
|
|
24
16
|
case "SessionStart":
|
|
25
|
-
|
|
17
|
+
client.logger.info("lifecycle.session_start", {
|
|
18
|
+
sessionId: input.session_id,
|
|
19
|
+
cwd: input.working_dir,
|
|
20
|
+
});
|
|
21
|
+
await (0, argus_1.sessionStart)(client, { harness: HARNESS, ...deps });
|
|
22
|
+
return {};
|
|
23
|
+
case "PreToolUse": {
|
|
24
|
+
const toolName = input.tool_name ?? "unknown";
|
|
25
|
+
client.logger.info("lifecycle.pre_tool_use", {
|
|
26
|
+
sessionId: input.session_id,
|
|
27
|
+
toolName,
|
|
28
|
+
toolInput: input.tool_input,
|
|
29
|
+
});
|
|
30
|
+
const result = await (0, argus_1.gate)(client, {
|
|
31
|
+
harness: HARNESS,
|
|
32
|
+
toolName,
|
|
33
|
+
toolArgs: input.tool_input,
|
|
34
|
+
subjectFallback: `session:${input.session_id}`,
|
|
35
|
+
});
|
|
36
|
+
return result.blocked
|
|
37
|
+
? { action: "block", message: result.denialMessage }
|
|
38
|
+
: {};
|
|
39
|
+
}
|
|
40
|
+
case "PostToolUse": {
|
|
41
|
+
const toolName = input.tool_name ?? "unknown";
|
|
42
|
+
client.logger.info("lifecycle.post_tool_use", {
|
|
43
|
+
sessionId: input.session_id,
|
|
44
|
+
toolName,
|
|
45
|
+
error: input.error,
|
|
46
|
+
});
|
|
47
|
+
(0, argus_1.complete)(client, {
|
|
48
|
+
toolName,
|
|
49
|
+
input: input.tool_input,
|
|
50
|
+
output: input.tool_output,
|
|
51
|
+
});
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
case "PostToolUseFailure": {
|
|
55
|
+
const toolName = input.tool_name ?? "unknown";
|
|
56
|
+
client.logger.info("lifecycle.post_tool_use", {
|
|
57
|
+
sessionId: input.session_id,
|
|
58
|
+
toolName,
|
|
59
|
+
error: input.error,
|
|
60
|
+
});
|
|
61
|
+
(0, argus_1.fail)(client, { toolName, input: input.tool_input, error: input.error });
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
26
64
|
case "SessionEnd":
|
|
27
65
|
case "Stop":
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return
|
|
66
|
+
client.logger.info("lifecycle.session_end", {
|
|
67
|
+
sessionId: input.session_id,
|
|
68
|
+
event,
|
|
69
|
+
});
|
|
70
|
+
client.logger.activity("session.end", "ok", { attributes: { event } });
|
|
71
|
+
return {};
|
|
34
72
|
case "UserPromptSubmit":
|
|
35
|
-
|
|
73
|
+
client.logger.info("lifecycle.user_prompt_submit", { sessionId: input.session_id });
|
|
74
|
+
client.logger.activity("user.prompt", "ok", { attributes: { event } });
|
|
75
|
+
return {};
|
|
36
76
|
case "BeforeReadFile":
|
|
37
77
|
case "AfterFileEdit":
|
|
38
78
|
case "BeforeShellExecution":
|
|
39
79
|
case "AfterShellExecution":
|
|
40
|
-
|
|
80
|
+
client.logger.info("lifecycle.observe", {
|
|
81
|
+
sessionId: input.session_id,
|
|
82
|
+
event,
|
|
83
|
+
filePath: input.file_path,
|
|
84
|
+
});
|
|
85
|
+
client.logger.activity("notification", "ok", {
|
|
86
|
+
attributes: {
|
|
87
|
+
event,
|
|
88
|
+
...(input.file_path ? { filePath: input.file_path } : {}),
|
|
89
|
+
...(input.command ? { hasCommand: true } : {}),
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
return {};
|
|
41
93
|
default:
|
|
42
94
|
client.logger.debug("hook.passthrough", { event });
|
|
43
|
-
client.
|
|
44
|
-
attributes: { event },
|
|
45
|
-
});
|
|
95
|
+
client.logger.activity("hook.passthrough", "skipped", { attributes: { event } });
|
|
46
96
|
return {};
|
|
47
97
|
}
|
|
48
|
-
}
|
|
49
|
-
finally {
|
|
50
|
-
client.tracer.clearContext();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// ─── SessionStart ───────────────────────────────────────────────────
|
|
54
|
-
async function handleSessionStart(input, client, deps) {
|
|
55
|
-
client.logger.info("lifecycle.session_start", {
|
|
56
|
-
sessionId: input.session_id,
|
|
57
|
-
cwd: input.working_dir,
|
|
58
|
-
});
|
|
59
|
-
client.tracer.record("session.start", "ok", {});
|
|
60
|
-
const userGate = deps.userLogin ?? argus_1.ensureUserAuthenticated;
|
|
61
|
-
// The user login runs every session and never blocks; tool-call
|
|
62
|
-
// enforcement is governed solely by permissionMode.
|
|
63
|
-
await userGate(client, {
|
|
64
|
-
binName: "ory-goose",
|
|
65
|
-
harness: "goose",
|
|
66
|
-
});
|
|
67
|
-
// Resolve the agent identity (machine credentials). Never blocks;
|
|
68
|
-
// attaches the agent's bearer token to outgoing Ory API calls.
|
|
69
|
-
const agentGate = deps.agentGate ?? argus_1.ensureAgentIdentity;
|
|
70
|
-
await agentGate(client, { projectUrl: (0, argus_1.resolveConfig)().projectUrl, harness: "goose" });
|
|
71
|
-
// User → agent delegation tuple (best-effort, audit-trail only). Written
|
|
72
|
-
// at most once per install.
|
|
73
|
-
await (0, argus_1.writeUserDelegatesAgent)(client);
|
|
74
|
-
return {};
|
|
75
|
-
}
|
|
76
|
-
// ─── PreToolUse ────────────────────────────────────────────────────
|
|
77
|
-
async function handlePreToolUse(input, client) {
|
|
78
|
-
const toolName = input.tool_name ?? "unknown";
|
|
79
|
-
client.logger.info("lifecycle.pre_tool_use", {
|
|
80
|
-
sessionId: input.session_id,
|
|
81
|
-
toolName,
|
|
82
|
-
toolInput: input.tool_input,
|
|
83
|
-
});
|
|
84
|
-
const inputSummary = (0, argus_1.summarizeToolInput)(toolName, input.tool_input);
|
|
85
|
-
// In audit-only mode, log the invocation but skip permission checks.
|
|
86
|
-
if ((0, argus_1.resolveConfig)().auditOnly) {
|
|
87
|
-
client.tracer.record("tool.invoke", "ok", {
|
|
88
|
-
attributes: { toolName, ...inputSummary },
|
|
89
|
-
});
|
|
90
|
-
return {};
|
|
91
|
-
}
|
|
92
|
-
const subject = (0, argus_1.resolveUserSubject)(client, `session:${input.session_id}`);
|
|
93
|
-
const subjectId = (0, argus_1.subjectLabel)(subject);
|
|
94
|
-
const namespace = resolveNamespace();
|
|
95
|
-
try {
|
|
96
|
-
const outcome = await (0, argus_1.gateToolCall)(client, {
|
|
97
|
-
harness: "goose",
|
|
98
|
-
toolName,
|
|
99
|
-
check: { namespace, object: toolName, relation: "use", ...subject },
|
|
100
|
-
spanAttributes: { toolName },
|
|
101
|
-
});
|
|
102
|
-
// Interactive tools (operator-extensible via ORY_INTERACTIVE_TOOLS):
|
|
103
|
-
// user.interaction span is already recorded; pass through.
|
|
104
|
-
if (outcome.kind === "interactive") {
|
|
105
|
-
return {};
|
|
106
|
-
}
|
|
107
|
-
const decision = outcome;
|
|
108
|
-
if (decision.kind === "fail_open") {
|
|
109
|
-
return handlePermissionError(decision.error, toolName, client);
|
|
110
|
-
}
|
|
111
|
-
const attrs = { toolName, ...inputSummary };
|
|
112
|
-
const decisionAttrs = decision.spanAttributes;
|
|
113
|
-
if (decision.kind === "allow") {
|
|
114
|
-
client.tracer.record("tool.invoke", "ok", {
|
|
115
|
-
attributes: { ...attrs, ...decisionAttrs, allowed: true },
|
|
116
|
-
});
|
|
117
|
-
return {};
|
|
118
|
-
}
|
|
119
|
-
if (decision.kind === "observe") {
|
|
120
|
-
client.tracer.record("tool.block", "denied", {
|
|
121
|
-
attributes: { ...attrs, ...decisionAttrs, allowed: false, ...(0, argus_1.alertAttributes)(false) },
|
|
122
|
-
});
|
|
123
|
-
client.tracer.record("tool.invoke", "ok", {
|
|
124
|
-
attributes: { ...attrs, ...decisionAttrs, allowed: false, observed: true },
|
|
125
|
-
});
|
|
126
|
-
return {};
|
|
127
|
-
}
|
|
128
|
-
client.tracer.record("tool.block", "denied", {
|
|
129
|
-
attributes: { ...attrs, ...decisionAttrs, allowed: false, ...(0, argus_1.alertAttributes)(true) },
|
|
130
|
-
});
|
|
131
|
-
return {
|
|
132
|
-
action: "block",
|
|
133
|
-
message: (0, argus_1.formatDenialMessage)({ tool: toolName, subjectId, namespace }),
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
catch (err) {
|
|
137
|
-
return handlePermissionError(err, toolName, client);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
// ─── PostToolUse / PostToolUseFailure ──────────────────────────────
|
|
141
|
-
async function handlePostToolUse(input, client) {
|
|
142
|
-
const toolName = input.tool_name ?? "unknown";
|
|
143
|
-
client.logger.info("lifecycle.post_tool_use", {
|
|
144
|
-
sessionId: input.session_id,
|
|
145
|
-
toolName,
|
|
146
|
-
error: input.error,
|
|
147
|
-
});
|
|
148
|
-
const status = input.event === "PostToolUseFailure" ? "error" : "ok";
|
|
149
|
-
client.tracer.record("tool.complete", status, {
|
|
150
|
-
attributes: {
|
|
151
|
-
toolName,
|
|
152
|
-
...(0, argus_1.summarizeToolInput)(toolName, input.tool_input),
|
|
153
|
-
...(0, argus_1.summarizeToolOutput)(toolName, input.tool_output),
|
|
154
|
-
...(input.error ? { error: input.error } : {}),
|
|
155
|
-
},
|
|
156
|
-
});
|
|
157
|
-
return {};
|
|
158
|
-
}
|
|
159
|
-
// ─── SessionEnd / Stop ─────────────────────────────────────────────
|
|
160
|
-
async function handleSessionEnd(input, client) {
|
|
161
|
-
client.logger.info("lifecycle.session_end", {
|
|
162
|
-
sessionId: input.session_id,
|
|
163
|
-
event: input.event,
|
|
164
|
-
});
|
|
165
|
-
client.tracer.record("session.end", "ok", {
|
|
166
|
-
attributes: { event: input.event },
|
|
167
|
-
});
|
|
168
|
-
return {};
|
|
169
|
-
}
|
|
170
|
-
// ─── UserPromptSubmit ──────────────────────────────────────────────
|
|
171
|
-
//
|
|
172
|
-
// User-facing phase. The human is the decision-maker; record an audit
|
|
173
|
-
// span only and never block.
|
|
174
|
-
async function handleUserPromptSubmit(input, client) {
|
|
175
|
-
client.logger.info("lifecycle.user_prompt_submit", {
|
|
176
|
-
sessionId: input.session_id,
|
|
177
|
-
});
|
|
178
|
-
client.tracer.record("user.prompt", "ok", {
|
|
179
|
-
attributes: { event: input.event },
|
|
180
|
-
});
|
|
181
|
-
return {};
|
|
182
|
-
}
|
|
183
|
-
// ─── Trace-only file / shell observation events ────────────────────
|
|
184
|
-
//
|
|
185
|
-
// BeforeReadFile, AfterFileEdit, BeforeShellExecution, AfterShellExecution
|
|
186
|
-
// are fine-grained observability hooks. The PreToolUse gate already
|
|
187
|
-
// authorizes the tool that drives them, so these are audit spans only.
|
|
188
|
-
async function handleTraceOnly(input, client) {
|
|
189
|
-
client.logger.info("lifecycle.observe", {
|
|
190
|
-
sessionId: input.session_id,
|
|
191
|
-
event: input.event,
|
|
192
|
-
filePath: input.file_path,
|
|
193
|
-
});
|
|
194
|
-
client.tracer.record("notification", "ok", {
|
|
195
|
-
attributes: {
|
|
196
|
-
event: input.event,
|
|
197
|
-
...(input.file_path ? { filePath: input.file_path } : {}),
|
|
198
|
-
...(input.command ? { hasCommand: true } : {}),
|
|
199
|
-
},
|
|
200
|
-
});
|
|
201
|
-
return {};
|
|
202
|
-
}
|
|
203
|
-
// ─── Helpers ────────────────────────────────────────────────────────
|
|
204
|
-
function resolveNamespace() {
|
|
205
|
-
return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools";
|
|
206
|
-
}
|
|
207
|
-
function handlePermissionError(oryErr, toolName, client) {
|
|
208
|
-
if (oryErr.code === "network_error") {
|
|
209
|
-
client.logger.warn("permission.network_error", {
|
|
210
|
-
toolName,
|
|
211
|
-
message: "Ory unreachable, failing open",
|
|
212
|
-
});
|
|
213
|
-
return {};
|
|
214
|
-
}
|
|
215
|
-
if (oryErr.code === "rate_limited") {
|
|
216
|
-
client.logger.warn("permission.rate_limited", {
|
|
217
|
-
toolName,
|
|
218
|
-
message: "Ory rate limited, failing open",
|
|
219
|
-
});
|
|
220
|
-
return {};
|
|
221
|
-
}
|
|
222
|
-
// For other errors, also fail open but log prominently.
|
|
223
|
-
client.logger.error("permission.check.error", {
|
|
224
|
-
toolName,
|
|
225
|
-
code: oryErr.code,
|
|
226
|
-
message: oryErr.message,
|
|
227
98
|
});
|
|
228
|
-
return {};
|
|
229
99
|
}
|
package/dist/hook.js
CHANGED
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
14
|
const argus_1 = require("@ory/argus");
|
|
15
15
|
const handlers_js_1 = require("./handlers.js");
|
|
16
|
+
const EVENT_FLUSH_TIMEOUT_MS = 750;
|
|
17
|
+
let client;
|
|
18
|
+
async function flushEvents() {
|
|
19
|
+
try {
|
|
20
|
+
await client?.flushEvents(EVENT_FLUSH_TIMEOUT_MS);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// The hook decision must not depend on activity delivery.
|
|
24
|
+
}
|
|
25
|
+
}
|
|
16
26
|
/**
|
|
17
27
|
* Read all of stdin as a string.
|
|
18
28
|
*
|
|
@@ -46,7 +56,7 @@ function readStdin() {
|
|
|
46
56
|
});
|
|
47
57
|
}
|
|
48
58
|
async function main() {
|
|
49
|
-
|
|
59
|
+
client = argus_1.OryAgentClient.fromEnv("goose");
|
|
50
60
|
const raw = await readStdin();
|
|
51
61
|
let input;
|
|
52
62
|
try {
|
|
@@ -54,7 +64,6 @@ async function main() {
|
|
|
54
64
|
}
|
|
55
65
|
catch {
|
|
56
66
|
client.logger.error("hook.stdin.parse_failed", { raw: raw.slice(0, 200) });
|
|
57
|
-
await client.tracer.shutdown();
|
|
58
67
|
process.exit(0); // Don't block on parse errors
|
|
59
68
|
}
|
|
60
69
|
const output = await (0, handlers_js_1.handleHookEvent)(input, client);
|
|
@@ -63,13 +72,14 @@ async function main() {
|
|
|
63
72
|
// signal is the `action` key; emit it on stdout and exit 2 as a defensive
|
|
64
73
|
// secondary signal — both indicate a hard deny.
|
|
65
74
|
process.stdout.write(JSON.stringify(output));
|
|
66
|
-
await
|
|
75
|
+
await flushEvents();
|
|
67
76
|
process.exit(2);
|
|
68
77
|
}
|
|
69
|
-
await
|
|
78
|
+
await flushEvents();
|
|
70
79
|
process.exit(0);
|
|
71
80
|
}
|
|
72
|
-
main().catch((err) => {
|
|
81
|
+
main().catch(async (err) => {
|
|
73
82
|
process.stderr.write(`[ory-agent] fatal: ${err}\n`);
|
|
83
|
+
await flushEvents();
|
|
74
84
|
process.exit(0); // Fail open
|
|
75
85
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/goose",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Ory plugin for Goose (Block's open-source agent 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",
|
|
@@ -35,8 +35,7 @@
|
|
|
35
35
|
"agent-security",
|
|
36
36
|
"guardrails",
|
|
37
37
|
"llm",
|
|
38
|
-
"
|
|
39
|
-
"distributed-tracing",
|
|
38
|
+
"activity-auditing",
|
|
40
39
|
"observability",
|
|
41
40
|
"kratos",
|
|
42
41
|
"keto",
|
|
@@ -47,6 +46,10 @@
|
|
|
47
46
|
"registry": "https://registry.npmjs.org/",
|
|
48
47
|
"provenance": true
|
|
49
48
|
},
|
|
49
|
+
"reova": {
|
|
50
|
+
"enabled": true,
|
|
51
|
+
"endpoint": "https://telemetry.reo.dev/data"
|
|
52
|
+
},
|
|
50
53
|
"main": "dist/index.js",
|
|
51
54
|
"types": "dist/index.d.ts",
|
|
52
55
|
"exports": {
|
|
@@ -62,12 +65,11 @@
|
|
|
62
65
|
},
|
|
63
66
|
"files": [
|
|
64
67
|
"dist",
|
|
65
|
-
"!dist/dev",
|
|
66
68
|
"!dist/**/*.tsbuildinfo"
|
|
67
69
|
],
|
|
68
70
|
"dependencies": {
|
|
69
|
-
"
|
|
70
|
-
"@ory/argus": "0.
|
|
71
|
+
"reova": "^0.7.0",
|
|
72
|
+
"@ory/argus": "1.0.0"
|
|
71
73
|
},
|
|
72
74
|
"devDependencies": {
|
|
73
75
|
"typescript": "^6.0.2",
|
|
@@ -81,7 +83,6 @@
|
|
|
81
83
|
"clean": "rm -rf dist *.tsbuildinfo",
|
|
82
84
|
"test": "vitest run",
|
|
83
85
|
"test:watch": "vitest",
|
|
84
|
-
"typecheck": "tsc --noEmit"
|
|
85
|
-
"dev": "node dist/dev/launcher.js"
|
|
86
|
+
"typecheck": "tsc --noEmit"
|
|
86
87
|
}
|
|
87
88
|
}
|