@ory/argus 0.3.0 → 0.5.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/assets/skills/ory-build-integration/SKILL.md +108 -0
- package/assets/skills/ory-contribute-integration/SKILL.md +164 -0
- package/dist/cli.js +36 -10
- package/dist/config.js +4 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -2
- package/dist/setup.js +12 -5
- package/dist/skills.d.ts +3 -2
- package/dist/skills.js +13 -2
- package/dist/status-cli.d.ts +37 -0
- package/dist/status-cli.js +285 -0
- package/package.json +2 -2
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ory-build-integration
|
|
3
|
+
description: Wire an Ory Network integration into your own application using the ory/integrates template patterns. Use when the user wants to add an Ory integration to their app — an Action webhook handler that fires during a flow, a Console/gateway config to validate Ory JWTs, or an Enterprise live-event-stream consumer — phrases like "wire up an Ory webhook", "add an Ory Action handler to my app", "react to Ory identity events", "validate Ory tokens at my gateway", "build an Ory integration in my project". For contributing an integration back to ory/integrates instead, use ory-contribute-integration.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Build an Ory Integration Into Your App
|
|
7
|
+
|
|
8
|
+
You are helping the user wire an Ory Network integration into **their own
|
|
9
|
+
application**, reusing the proven patterns from the public `ory/integrates`
|
|
10
|
+
repository. There is no contribution, registry, `Maintained by:`, or DCO concern
|
|
11
|
+
here — you fetch the **runnable subset** of a template and adapt it in place.
|
|
12
|
+
|
|
13
|
+
This skill carries the workflow. The template files live in
|
|
14
|
+
`github.com/ory/integrates` (branch `main`); fetch the ones you need so they stay
|
|
15
|
+
current.
|
|
16
|
+
|
|
17
|
+
> **Precondition:** `ory/integrates` must be reachable (public repo). If a fetch
|
|
18
|
+
> fails, tell the user and stop — do not fabricate handler code.
|
|
19
|
+
|
|
20
|
+
## Step 1 — Determine the type and app context
|
|
21
|
+
|
|
22
|
+
Pick the integration type from intent; ask directly if unclear.
|
|
23
|
+
|
|
24
|
+
| Signal from the user | Type |
|
|
25
|
+
|---|---|
|
|
26
|
+
| Transform/enrich an identity at registration; gate or react to a flow **synchronously**; call a vendor API during an Ory flow | `webhook` |
|
|
27
|
+
| Validate Ory JWTs at a gateway; **Console-only** config; no handler code | `config` |
|
|
28
|
+
| React to events **asynchronously**; Ory **Enterprise** Live Event Stream consumer | `http-event` |
|
|
29
|
+
|
|
30
|
+
Also establish: the app's framework/runtime, where the handler will live, and how
|
|
31
|
+
it's deployed (these decide where you write files and how you mount the route).
|
|
32
|
+
|
|
33
|
+
## Step 2 — Fetch the runnable subset
|
|
34
|
+
|
|
35
|
+
Pull only the files the user needs from `_examples/_template-<type>/`. Prefer raw
|
|
36
|
+
fetch so you don't clone the whole repo into the user's project.
|
|
37
|
+
|
|
38
|
+
**Raw file fetch** (base URL
|
|
39
|
+
`https://raw.githubusercontent.com/ory/integrates/main/_examples/_template-<type>/`):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
BASE=https://raw.githubusercontent.com/ory/integrates/main/_examples/_template-webhook
|
|
43
|
+
mkdir -p ory-integration/webhook ory-integration/jsonnet
|
|
44
|
+
curl -fsSL "$BASE/ory-actions.yaml" -o ory-integration/ory-actions.yaml
|
|
45
|
+
curl -fsSL "$BASE/jsonnet/identity.jsonnet" -o ory-integration/jsonnet/identity.jsonnet
|
|
46
|
+
curl -fsSL "$BASE/webhook/server.ts" -o ory-integration/webhook/server.ts
|
|
47
|
+
curl -fsSL "$BASE/webhook/package.json" -o ory-integration/webhook/package.json
|
|
48
|
+
curl -fsSL "$BASE/webhook/tsconfig.json" -o ory-integration/webhook/tsconfig.json
|
|
49
|
+
curl -fsSL "$BASE/webhook/.env.example" -o ory-integration/webhook/.env.example
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Sparse checkout** (if the user prefers a git copy to diff against):
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
git clone --depth 1 --filter=blob:none --sparse https://github.com/ory/integrates /tmp/ory-integrates
|
|
56
|
+
git -C /tmp/ory-integrates sparse-checkout set _examples/_template-webhook
|
|
57
|
+
# then copy the files you want out of /tmp/ory-integrates/_examples/_template-webhook
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Runnable subset per type (skip `registry.entry.yaml.example`, the
|
|
61
|
+
`Maintained by:` README, and lockfiles — `npm install` regenerates the lock):
|
|
62
|
+
|
|
63
|
+
- **webhook** — `ory-actions.yaml`, `jsonnet/identity.jsonnet`,
|
|
64
|
+
`webhook/{server.ts, package.json, tsconfig.json, .env.example}`.
|
|
65
|
+
- **config** — `ory-console-steps.md` (no code; it's a Console walkthrough).
|
|
66
|
+
- **http-event** — `ory-event-stream.yaml`,
|
|
67
|
+
`webhook/{server.ts, idempotency.ts, package.json, tsconfig.json, .env.example}`.
|
|
68
|
+
|
|
69
|
+
Adapt the fetched handler into the user's app: integrate the route into their
|
|
70
|
+
existing server if they have one, or run the `webhook/` server standalone.
|
|
71
|
+
|
|
72
|
+
## Step 3 — Wire it to the user's Ory project
|
|
73
|
+
|
|
74
|
+
- **webhook** — deploy the handler, then create an Ory Action pointing at it using
|
|
75
|
+
`ory-actions.yaml` as the template (set `url` to the deployed handler, set the
|
|
76
|
+
`X-Webhook-Secret` value, and the matching `ORY_WEBHOOK_SECRET` in the
|
|
77
|
+
handler's `.env`). Adjust `jsonnet/identity.jsonnet` to the body shape the
|
|
78
|
+
handler expects. Apply via the Ory Console or the Ory CLI.
|
|
79
|
+
- **config** — follow `ory-console-steps.md`: configure the vendor side, then the
|
|
80
|
+
Ory Console (gateway/JWT validation, provider config, etc.). No code to deploy.
|
|
81
|
+
- **http-event** (Enterprise) — deploy the handler, then configure the
|
|
82
|
+
event-stream target from `ory-event-stream.yaml` (URL with embedded Basic Auth;
|
|
83
|
+
set `BASIC_AUTH_USER` / `BASIC_AUTH_PASSWORD` in `.env` to match) and the event
|
|
84
|
+
filter. Keep the `idempotency.ts` dedupe wired up — delivery is at-least-once.
|
|
85
|
+
|
|
86
|
+
Always verify the signature/secret path: webhook uses the `X-Webhook-Secret`
|
|
87
|
+
header; http-event uses HTTP Basic Auth embedded in the configured URL.
|
|
88
|
+
|
|
89
|
+
## Step 4 — Test locally
|
|
90
|
+
|
|
91
|
+
Stand up Ory and exercise the flow locally before pointing at production:
|
|
92
|
+
|
|
93
|
+
- Use {{REF_LOCAL_DEV}} to run a local Ory stack (Kratos / Keto / Hydra +
|
|
94
|
+
gateway), then trigger the relevant flow and confirm the handler fires.
|
|
95
|
+
- Use {{REF_AUTH_SETUP}} when the app also needs Ory project / SDK setup wired up.
|
|
96
|
+
|
|
97
|
+
For a webhook, hit `GET /health` on the handler, then run the Ory flow and check
|
|
98
|
+
the handler logs. For http-event, emit a test event and confirm both the dedupe
|
|
99
|
+
and the downstream side effect.
|
|
100
|
+
|
|
101
|
+
## What this skill does NOT do
|
|
102
|
+
|
|
103
|
+
- It does not create a contribution to `ory/integrates` (no `registry.entry.yaml`,
|
|
104
|
+
`Maintained by:`, SPDX, or DCO) — use `ory-contribute-integration` for that.
|
|
105
|
+
- It does not invent handler code — it fetches the real templates from
|
|
106
|
+
`ory/integrates`.
|
|
107
|
+
- It does not deploy to the user's infrastructure or modify their Ory project
|
|
108
|
+
without confirmation.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ory-contribute-integration
|
|
3
|
+
description: Author a new Ory Network integration as a contribution to the public ory/integrates repository. Use when the user wants to contribute an integration back to Ory, add an entry to the Ory integrations registry/catalog, open a PR against ory/integrates, or publish a reusable integration for others — phrases like "contribute an Ory integration", "add my integration to ory/integrates", "submit a webhook integration to Ory", "get my integration into the registry". For wiring an integration into your own app instead, use ory-build-integration.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Contribute an Integration to ory/integrates
|
|
7
|
+
|
|
8
|
+
You are helping the user author a new integration and contribute it back to the
|
|
9
|
+
public `ory/integrates` repository ("Sample code and reference configuration for
|
|
10
|
+
integrating Ory Network with third-party products"). The deliverable is a pull
|
|
11
|
+
request against `github.com/ory/integrates`.
|
|
12
|
+
|
|
13
|
+
This skill carries the workflow. The **template files** live in the repo itself —
|
|
14
|
+
fetch them from `github.com/ory/integrates` (branch `main`) rather than
|
|
15
|
+
reproducing them, so they never drift.
|
|
16
|
+
|
|
17
|
+
> **Precondition:** `ory/integrates` must be reachable (it is a public repo). If
|
|
18
|
+
> `gh repo view ory/integrates` or a fetch of a template file fails, tell the
|
|
19
|
+
> user the repo is unreachable and stop — do not fabricate template contents.
|
|
20
|
+
|
|
21
|
+
## Step 0 — Open the "New integration" issue first
|
|
22
|
+
|
|
23
|
+
Ory asks contributors to open a **New integration** issue before the PR, so scope
|
|
24
|
+
is confirmed and the work isn't already in flight. Remind the user to do this
|
|
25
|
+
(GitHub → `ory/integrates` → Issues → "New integration" template) and capture the
|
|
26
|
+
issue number for the PR.
|
|
27
|
+
|
|
28
|
+
## Step 1 — Get a checkout and pick the category
|
|
29
|
+
|
|
30
|
+
The contribution workflow happens inside a checkout of the repo:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
gh repo clone ory/integrates
|
|
34
|
+
cd integrates
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
(or a fork, if the user will PR from their own remote.) Then choose the
|
|
38
|
+
**category folder** the integration belongs in — it must match an existing
|
|
39
|
+
top-level directory (`crm/`, `api-gateways/`, `identity-verification/`, `mfa/`,
|
|
40
|
+
`enterprise-sso/`, …). List them with `ls -d */ | grep -v _examples`.
|
|
41
|
+
|
|
42
|
+
## Step 2 — Determine the integration type
|
|
43
|
+
|
|
44
|
+
Every integration is one of three types. Infer from intent; if unclear, ask
|
|
45
|
+
directly.
|
|
46
|
+
|
|
47
|
+
| Signal from the user | Type | Template |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| Transform/enrich an identity at registration; gate or react to a self-service flow **synchronously**; call a vendor API during an Ory flow | `webhook` | `_examples/_template-webhook/` |
|
|
50
|
+
| Validate Ory JWTs at a gateway; **Console-only** setup; no handler code | `config` | `_examples/_template-config/` |
|
|
51
|
+
| React to events **asynchronously**; Ory Network **Enterprise** Live Event Stream consumer | `http-event` | `_examples/_template-http-event/` |
|
|
52
|
+
|
|
53
|
+
## Step 3 — Copy the template into the category folder
|
|
54
|
+
|
|
55
|
+
From the repo root, copy the whole template directory to
|
|
56
|
+
`<category>/<integration-slug>` (slug is lowercase, hyphenated, matches the dir
|
|
57
|
+
name):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
cp -R _examples/_template-webhook crm/<integration-slug> # webhook
|
|
61
|
+
# or _examples/_template-config / _examples/_template-http-event
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Each template ships these files (confirm with `ls -R <category>/<integration-slug>`):
|
|
65
|
+
|
|
66
|
+
- **webhook** — `README.md`, `ory-actions.yaml`, `jsonnet/identity.jsonnet`,
|
|
67
|
+
`registry.entry.yaml.example`, and a runnable `webhook/` (`server.ts`,
|
|
68
|
+
`package.json`, `package-lock.json`, `tsconfig.json`, `.env.example`).
|
|
69
|
+
- **config** — `README.md`, `ory-console-steps.md`,
|
|
70
|
+
`registry.entry.yaml.example`.
|
|
71
|
+
- **http-event** — `README.md`, `ory-event-stream.yaml`,
|
|
72
|
+
`registry.entry.yaml.example`, and a runnable `webhook/` (`server.ts`,
|
|
73
|
+
`idempotency.ts`, `package.json`, `package-lock.json`, `tsconfig.json`,
|
|
74
|
+
`.env.example`).
|
|
75
|
+
|
|
76
|
+
The copied files are the source of truth — read them and fill in every
|
|
77
|
+
`<placeholder>` and `<!-- comment -->`.
|
|
78
|
+
|
|
79
|
+
## Step 4 — Fill out the integration
|
|
80
|
+
|
|
81
|
+
1. **README.md** — replace `<Integration Name>`, set the `Maintained by:` line
|
|
82
|
+
(`Ory Engineering`, `Community contributors`, or the user's `@handle`), and
|
|
83
|
+
complete: what it does, use case, prerequisites, deploy steps, **Ory Console
|
|
84
|
+
configuration**, troubleshooting. No vendor marketing — factual wiring only.
|
|
85
|
+
2. **Code / config for the type** (see per-type notes below).
|
|
86
|
+
3. **registry.entry.yaml** — rename `registry.entry.yaml.example` →
|
|
87
|
+
`registry.entry.yaml` and fill in every field (`name`, `displayName`,
|
|
88
|
+
`vendor`, `category`, `type`, `maintainedBy`, `description`, `useCase`,
|
|
89
|
+
`coreFunctionality`, `oryMechanism`, `protocol`, `status`; http-event also
|
|
90
|
+
needs `subscribedEvents`). The field comments in the file enumerate the
|
|
91
|
+
allowed enum values.
|
|
92
|
+
4. **Apache-2.0 SPDX header** at the top of every source file you ship, e.g.
|
|
93
|
+
`// SPDX-License-Identifier: Apache-2.0`.
|
|
94
|
+
|
|
95
|
+
### webhook specifics
|
|
96
|
+
- `ory-actions.yaml` is the Ory Action hook config (a `web_hook` with an
|
|
97
|
+
`X-Webhook-Secret` `api_key` auth header). Point `url` at the deployed handler
|
|
98
|
+
and set the shared secret.
|
|
99
|
+
- `jsonnet/identity.jsonnet` is the request-body template — adjust to the shape
|
|
100
|
+
your handler expects.
|
|
101
|
+
- The `webhook/` server must run (`cd webhook && cp .env.example .env && npm
|
|
102
|
+
install && npm start`); it exposes `GET /health` and the `POST` target.
|
|
103
|
+
`ORY_WEBHOOK_SECRET` in `.env` must match the secret in `ory-actions.yaml`.
|
|
104
|
+
|
|
105
|
+
### config specifics
|
|
106
|
+
- No code. Write a real `ory-console-steps.md`: vendor-side setup, the exact Ory
|
|
107
|
+
Console navigation, the fields to configure, a test, and troubleshooting. A
|
|
108
|
+
README-only contribution with no real console steps will be rejected.
|
|
109
|
+
|
|
110
|
+
### http-event specifics
|
|
111
|
+
- `ory-event-stream.yaml` configures the Ory-side event target (URL with embedded
|
|
112
|
+
Basic Auth) and the event filter. List the consumed events under both the YAML
|
|
113
|
+
`events:` and the registry `subscribedEvents:`.
|
|
114
|
+
- The `webhook/` handler authenticates with HTTP Basic Auth, dedupes by SHA-256
|
|
115
|
+
of the body (`idempotency.ts`) because delivery is at-least-once, and always
|
|
116
|
+
returns 200. Live event streams are an **Enterprise** feature — say so in the
|
|
117
|
+
README.
|
|
118
|
+
|
|
119
|
+
## Step 5 — Regenerate the registry
|
|
120
|
+
|
|
121
|
+
From the repo root:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
cd scripts && npm install && cd ..
|
|
125
|
+
node scripts/build-registry.js
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This rewrites the top-level `registry.yaml` from every `registry.entry.yaml`.
|
|
129
|
+
Commit the regenerated `registry.yaml` along with your integration.
|
|
130
|
+
|
|
131
|
+
## Step 6 — Open the PR (DCO + checklist)
|
|
132
|
+
|
|
133
|
+
Walk the CONTRIBUTING checklist before opening the PR:
|
|
134
|
+
|
|
135
|
+
- [ ] Integration is in the correct category folder.
|
|
136
|
+
- [ ] `README.md` covers what it does, prerequisites, deploy, Console config,
|
|
137
|
+
troubleshooting, and a `Maintained by:` line.
|
|
138
|
+
- [ ] Type deliverables present: webhook → `ory-actions.yaml` + `jsonnet/` +
|
|
139
|
+
runnable `webhook/`; config → real `ory-console-steps.md`; http-event →
|
|
140
|
+
`ory-event-stream.yaml` + runnable `webhook/` + `subscribedEvents`.
|
|
141
|
+
- [ ] `registry.entry.yaml` filled and `registry.yaml` regenerated.
|
|
142
|
+
- [ ] Apache-2.0 SPDX header in each source file.
|
|
143
|
+
- [ ] **DCO sign-off on every commit** — `git commit -s`.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
git checkout -b add-<integration-slug>-integration
|
|
147
|
+
git add <category>/<integration-slug> registry.yaml
|
|
148
|
+
git commit -s -m "Add <Integration Name> integration"
|
|
149
|
+
git push -u origin add-<integration-slug>-integration
|
|
150
|
+
gh pr create --repo ory/integrates --title "Add <Integration Name> integration" \
|
|
151
|
+
--body "Closes #<issue-number>. <summary>"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Expect review questions about Ory Network compatibility, webhook security
|
|
155
|
+
(signature verification, secret handling), and README clarity.
|
|
156
|
+
|
|
157
|
+
## What this skill does NOT do
|
|
158
|
+
|
|
159
|
+
- It does not wire an integration into the user's own application — use
|
|
160
|
+
`ory-build-integration` for that (no registry/DCO).
|
|
161
|
+
- It does not run `build-registry.js` or open the PR without confirmation; it
|
|
162
|
+
guides, the user executes.
|
|
163
|
+
- It does not invent template contents — it reads the real files from
|
|
164
|
+
`ory/integrates`.
|
package/dist/cli.js
CHANGED
|
@@ -93,7 +93,7 @@ function runConfigureCommand(binName, args) {
|
|
|
93
93
|
console.log("");
|
|
94
94
|
console.log("Or set environment variables:");
|
|
95
95
|
console.log(" export ORY_PROJECT_URL=https://your-project.projects.oryapis.com");
|
|
96
|
-
console.log(" export
|
|
96
|
+
console.log(" export ORY_AGENT_API_KEY=ory_pat_...");
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
99
|
if (auditOnly) {
|
|
@@ -122,7 +122,7 @@ function runConfigureCommand(binName, args) {
|
|
|
122
122
|
console.log(` API Key: (set)`);
|
|
123
123
|
console.log("");
|
|
124
124
|
console.log("This configuration is shared across all Ory agent plugins.");
|
|
125
|
-
console.log("Environment variables (ORY_PROJECT_URL,
|
|
125
|
+
console.log("Environment variables (ORY_PROJECT_URL, ORY_AGENT_API_KEY) take precedence when set.");
|
|
126
126
|
}
|
|
127
127
|
/**
|
|
128
128
|
* Print the "Configuration:" block showing Ory project URL and API key status.
|
|
@@ -130,11 +130,14 @@ function runConfigureCommand(binName, args) {
|
|
|
130
130
|
function printOryConfig() {
|
|
131
131
|
const resolved = (0, config_js_1.resolveConfig)();
|
|
132
132
|
const configPath = (0, config_js_1.getConfigPath)();
|
|
133
|
+
const namespace = process.env.ORY_PERMISSION_NAMESPACE || "AgentTools";
|
|
133
134
|
console.log("Configuration:");
|
|
134
|
-
console.log(` Config file:
|
|
135
|
-
console.log(` Mode:
|
|
136
|
-
console.log(` Project URL:
|
|
137
|
-
console.log(` API Key:
|
|
135
|
+
console.log(` Config file: ${configPath}`);
|
|
136
|
+
console.log(` Mode: ${resolved.auditOnly ? "audit-only" : "full"}`);
|
|
137
|
+
console.log(` Project URL: ${resolved.projectUrl ?? "NOT SET"} [source: ${resolved.projectUrlSource}]`);
|
|
138
|
+
console.log(` API Key: ${resolved.apiKey ? "(set)" : "NOT SET"} [source: ${resolved.apiKeySource}]`);
|
|
139
|
+
console.log(` Permission mode: ${resolved.permissionMode} [source: ${resolved.permissionModeSource}]`);
|
|
140
|
+
console.log(` Namespace: ${namespace}`);
|
|
138
141
|
}
|
|
139
142
|
/**
|
|
140
143
|
* Print the "Environment:" block showing Ory-related environment variables.
|
|
@@ -143,14 +146,37 @@ function printEnvironment() {
|
|
|
143
146
|
console.log("");
|
|
144
147
|
console.log("Environment:");
|
|
145
148
|
const vars = [
|
|
146
|
-
["
|
|
149
|
+
["ORY_AUTH_GATE", process.env.ORY_AUTH_GATE, false],
|
|
150
|
+
[
|
|
151
|
+
"ORY_AGENT_API_KEY",
|
|
152
|
+
process.env.ORY_AGENT_API_KEY ? "(set)" : undefined,
|
|
153
|
+
false,
|
|
154
|
+
],
|
|
155
|
+
["ORY_AGENT_CLIENT_ID", process.env.ORY_AGENT_CLIENT_ID, false],
|
|
156
|
+
[
|
|
157
|
+
"ORY_USER_SESSION_TOKEN",
|
|
158
|
+
process.env.ORY_USER_SESSION_TOKEN ? "(set)" : undefined,
|
|
159
|
+
false,
|
|
160
|
+
],
|
|
161
|
+
["ORY_USER_SUBJECT_ID", process.env.ORY_USER_SUBJECT_ID, false],
|
|
147
162
|
["ORY_AGENT_SUBJECT_ID", process.env.ORY_AGENT_SUBJECT_ID, false],
|
|
148
163
|
["ORY_AGENT_DEBUG", process.env.ORY_AGENT_DEBUG, false],
|
|
149
164
|
["ORY_AGENT_LOG_FILE", process.env.ORY_AGENT_LOG_FILE, false],
|
|
150
165
|
["ORY_AGENT_TRACE_FILE", process.env.ORY_AGENT_TRACE_FILE, false],
|
|
151
166
|
];
|
|
167
|
+
if (process.env.ORY_API_KEY && !process.env.ORY_AGENT_API_KEY) {
|
|
168
|
+
vars.splice(2, 0, [
|
|
169
|
+
"ORY_API_KEY",
|
|
170
|
+
"(set, DEPRECATED — use ORY_AGENT_API_KEY)",
|
|
171
|
+
false,
|
|
172
|
+
]);
|
|
173
|
+
}
|
|
152
174
|
for (const [name, value, required] of vars) {
|
|
153
|
-
const display = value
|
|
175
|
+
const display = value
|
|
176
|
+
? value
|
|
177
|
+
: required
|
|
178
|
+
? "NOT SET (required)"
|
|
179
|
+
: "not set";
|
|
154
180
|
const icon = value ? "+" : required ? "!" : "-";
|
|
155
181
|
console.log(` [${icon}] ${name}: ${display}`);
|
|
156
182
|
}
|
|
@@ -188,7 +214,7 @@ function printEnvHelp(binName) {
|
|
|
188
214
|
console.log("");
|
|
189
215
|
console.log(" Option 2 — Set environment variables:");
|
|
190
216
|
console.log(" export ORY_PROJECT_URL=https://your-project.projects.oryapis.com");
|
|
191
|
-
console.log(" export
|
|
217
|
+
console.log(" export ORY_AGENT_API_KEY=ory_pat_...");
|
|
192
218
|
console.log("");
|
|
193
219
|
console.log("If neither is set when a session starts, the agent will prompt for configuration.");
|
|
194
220
|
}
|
|
@@ -355,7 +381,7 @@ async function interactiveConfigPrompt(binName) {
|
|
|
355
381
|
"",
|
|
356
382
|
"You can also set environment variables (requires session restart):",
|
|
357
383
|
" export ORY_PROJECT_URL=https://your-project.projects.oryapis.com",
|
|
358
|
-
" export
|
|
384
|
+
" export ORY_AGENT_API_KEY=ory_pat_...",
|
|
359
385
|
"",
|
|
360
386
|
].join("\n");
|
|
361
387
|
process.stderr.write(message + "\n");
|
package/dist/config.js
CHANGED
|
@@ -323,7 +323,10 @@ function writeConfigAtomic(config) {
|
|
|
323
323
|
function resolveConfig() {
|
|
324
324
|
const file = loadConfig();
|
|
325
325
|
const envProjectUrl = process.env.ORY_PROJECT_URL;
|
|
326
|
-
|
|
326
|
+
// ORY_AGENT_API_KEY is the documented name; ORY_API_KEY is the deprecated
|
|
327
|
+
// alias kept for back-compat (agent-auth emits a warning when only the
|
|
328
|
+
// legacy form is set).
|
|
329
|
+
const envApiKey = process.env.ORY_AGENT_API_KEY ?? process.env.ORY_API_KEY;
|
|
327
330
|
const envMode = parsePermissionMode(process.env.ORY_PERMISSION_MODE);
|
|
328
331
|
const permissionMode = envMode ?? file.permissionMode ?? "observe";
|
|
329
332
|
const permissionModeSource = envMode
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { resolveAgentCredentials, ensureAgentIdentity, ensureSubAgentIdentity, f
|
|
|
9
9
|
export { type SessionInfo, type OAuth2TokenInfo, type PermissionCheck, type PermissionResult, type BatchPermissionResult, type OryError, type OryErrorCode, } from "./types.js";
|
|
10
10
|
export { runConfigureCommand, runAgentCommand, printOryConfig, printEnvironment, printLogTail, printEnvHelp, printTraceTail, runWatchCommand, isTtyAvailable, promptOnTty, promptForProjectUrl, interactiveConfigPrompt, } from "./cli.js";
|
|
11
11
|
export { runPermissionsCommand, isUserIdentityCached, maybeAutoBootstrap, printPermissionsOnboardingHelp, } from "./permissions-cli.js";
|
|
12
|
+
export { runStatusCommand, printUserIdentitySection, printAgentIdentitySection, printPermissionsSection, type StatusCommandOptions, } from "./status-cli.js";
|
|
12
13
|
export { parseSetupArgs, readJsonFile, writeJsonFile, isOryHookCommand, resolveHookCommand, matcherHookEntry, mergeMatcherHooks, removeMatcherHooks, flatHookEntry, mergeFlatHooks, removeFlatHooks, printSetupHelp, printNextSteps, resolveMcpServerCommand, mcpServerEntry, mergeMcpServer, removeMcpServer, registerPlugin, unregisterPlugin, type SetupArgs, type HookCommand, type MatcherEntry, } from "./setup.js";
|
|
13
14
|
export { runDevLauncher, type DevLauncherConfig, type InstallContext, } from "./dev.js";
|
|
14
15
|
export { renderOrySkills, renderOryCommands, commandToSkill, commandToToml, commandToFrontmatterMarkdown, commandToPlainMarkdown, toSkillMarkdown, writeSkillTree, removeSkillDirs, ORY_SKILL_NAMES, ORY_COMMAND_SKILL_NAMES, ORY_COMMAND_SLUGS, type RenderedSkill, type RenderedCommand, type RenderProfileOptions, } from "./skills.js";
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.printOryConfig = exports.runAgentCommand = exports.runConfigureCommand = exports.AGENT_TOKEN_EXPIRY_SKEW_SEC = exports.clearSubAgentDynamicCredentials = exports.saveSubAgentDynamicCredentials = exports.loadSubAgentDynamicCredentials = exports.clearAgentDynamicCredentials = exports.saveAgentDynamicCredentials = exports.loadAgentDynamicCredentials = exports.registerAgentClient = exports.fetchClientCredentialsToken = exports.ensureSubAgentIdentity = exports.ensureAgentIdentity = exports.resolveAgentCredentials = exports.ensureAuthenticated = exports.ensureUserAuthenticated = exports.TOKEN_EXPIRY_SKEW_SEC = exports.waitForPeerTokensSync = exports.waitForPeerTokens = exports.clearPkceFlightLock = exports.tryAcquirePkceFlightLock = exports.refreshAndSave = exports.isExpired = exports.clearTokens = exports.saveTokens = exports.loadTokens = exports.DEFAULT_LOGIN_TIMEOUT_MS = exports.LOOPBACK_PORTS = exports.buildAuthorizeUrl = exports.sha256Base64Url = exports.generateCodeVerifier = exports.detectHeadless = exports.refreshAccessToken = exports.pkceLogin = exports.getHarnessDataDir = exports.getDataDir = exports.getConfigPath = exports.mutateConfig = exports.resolveConfig = exports.saveConfig = exports.loadConfig = exports.watchTraceFile = exports.formatSpan = exports.deriveTraceId = exports.ActiveSpan = exports.Tracer = exports.redactLogData = exports.DebugLogger = exports.OryAgentClient = void 0;
|
|
4
|
-
exports.
|
|
5
|
-
exports.parseKeyValueList = exports.otlpExporterFromEnv = exports.OtlpHttpExporter = exports.summarizeToolOutput = exports.summarizeToolInput = exports.OryDenialError = exports.alertAttributes = exports.formatAlertSummary = exports.formatAlertMessage = exports.formatDenialSummary = exports.formatDenialMessage = exports.subjectLabel = exports.resolveUserSubject = exports.checkMcpPermission = exports.parseMcpToolGeneric = exports.parseGeminiMcpTool = exports.parseClaudeCodeMcpTool = exports.getToolCatalog = exports.ALL_TOOLS = exports.KNOWN_HARNESSES = exports.HARNESS_TOOL_CATALOG = exports.applyPermissionMode = exports.checkAndDecide = void 0;
|
|
4
|
+
exports.runLocalCommand = exports.ORY_COMMAND_SLUGS = exports.ORY_COMMAND_SKILL_NAMES = exports.ORY_SKILL_NAMES = exports.removeSkillDirs = exports.writeSkillTree = exports.toSkillMarkdown = exports.commandToPlainMarkdown = exports.commandToFrontmatterMarkdown = exports.commandToToml = exports.commandToSkill = exports.renderOryCommands = exports.renderOrySkills = exports.runDevLauncher = exports.unregisterPlugin = exports.registerPlugin = exports.removeMcpServer = exports.mergeMcpServer = exports.mcpServerEntry = exports.resolveMcpServerCommand = exports.printNextSteps = exports.printSetupHelp = exports.removeFlatHooks = exports.mergeFlatHooks = exports.flatHookEntry = exports.removeMatcherHooks = exports.mergeMatcherHooks = exports.matcherHookEntry = exports.resolveHookCommand = exports.isOryHookCommand = exports.writeJsonFile = exports.readJsonFile = exports.parseSetupArgs = exports.printPermissionsSection = exports.printAgentIdentitySection = exports.printUserIdentitySection = exports.runStatusCommand = exports.printPermissionsOnboardingHelp = exports.maybeAutoBootstrap = exports.isUserIdentityCached = exports.runPermissionsCommand = exports.interactiveConfigPrompt = exports.promptForProjectUrl = exports.promptOnTty = exports.isTtyAvailable = exports.runWatchCommand = exports.printTraceTail = exports.printEnvHelp = exports.printLogTail = exports.printEnvironment = void 0;
|
|
5
|
+
exports.parseKeyValueList = exports.otlpExporterFromEnv = exports.OtlpHttpExporter = exports.summarizeToolOutput = exports.summarizeToolInput = exports.OryDenialError = exports.alertAttributes = exports.formatAlertSummary = exports.formatAlertMessage = exports.formatDenialSummary = exports.formatDenialMessage = exports.subjectLabel = exports.resolveUserSubject = exports.checkMcpPermission = exports.parseMcpToolGeneric = exports.parseGeminiMcpTool = exports.parseClaudeCodeMcpTool = exports.getToolCatalog = exports.ALL_TOOLS = exports.KNOWN_HARNESSES = exports.HARNESS_TOOL_CATALOG = exports.applyPermissionMode = exports.checkAndDecide = exports.runRegistryCommand = exports.DEV_JAEGER_CONTAINER = exports.stopDevJaeger = exports.ensureDevJaeger = void 0;
|
|
6
6
|
var client_js_1 = require("./client.js");
|
|
7
7
|
Object.defineProperty(exports, "OryAgentClient", { enumerable: true, get: function () { return client_js_1.OryAgentClient; } });
|
|
8
8
|
var logger_js_1 = require("./logger.js");
|
|
@@ -76,6 +76,11 @@ Object.defineProperty(exports, "runPermissionsCommand", { enumerable: true, get:
|
|
|
76
76
|
Object.defineProperty(exports, "isUserIdentityCached", { enumerable: true, get: function () { return permissions_cli_js_1.isUserIdentityCached; } });
|
|
77
77
|
Object.defineProperty(exports, "maybeAutoBootstrap", { enumerable: true, get: function () { return permissions_cli_js_1.maybeAutoBootstrap; } });
|
|
78
78
|
Object.defineProperty(exports, "printPermissionsOnboardingHelp", { enumerable: true, get: function () { return permissions_cli_js_1.printPermissionsOnboardingHelp; } });
|
|
79
|
+
var status_cli_js_1 = require("./status-cli.js");
|
|
80
|
+
Object.defineProperty(exports, "runStatusCommand", { enumerable: true, get: function () { return status_cli_js_1.runStatusCommand; } });
|
|
81
|
+
Object.defineProperty(exports, "printUserIdentitySection", { enumerable: true, get: function () { return status_cli_js_1.printUserIdentitySection; } });
|
|
82
|
+
Object.defineProperty(exports, "printAgentIdentitySection", { enumerable: true, get: function () { return status_cli_js_1.printAgentIdentitySection; } });
|
|
83
|
+
Object.defineProperty(exports, "printPermissionsSection", { enumerable: true, get: function () { return status_cli_js_1.printPermissionsSection; } });
|
|
79
84
|
var setup_js_1 = require("./setup.js");
|
|
80
85
|
Object.defineProperty(exports, "parseSetupArgs", { enumerable: true, get: function () { return setup_js_1.parseSetupArgs; } });
|
|
81
86
|
Object.defineProperty(exports, "readJsonFile", { enumerable: true, get: function () { return setup_js_1.readJsonFile; } });
|
package/dist/setup.js
CHANGED
|
@@ -372,8 +372,10 @@ Options:
|
|
|
372
372
|
|
|
373
373
|
Environment variables:
|
|
374
374
|
ORY_PROJECT_URL Your Ory project URL (required at runtime)
|
|
375
|
-
|
|
376
|
-
|
|
375
|
+
ORY_AGENT_API_KEY Agent API key / OAuth2 bearer (preferred name;
|
|
376
|
+
ORY_API_KEY is honored as a deprecated alias)
|
|
377
|
+
ORY_AUTH_GATE Set to "1" to enable the interactive user login gate
|
|
378
|
+
ORY_PERMISSION_MODE "observe" (default) or "enforce" — what to do on deny
|
|
377
379
|
ORY_AGENT_DEBUG Set to "true" for debug logging
|
|
378
380
|
ORY_AGENT_LOG_FILE Path to write debug logs
|
|
379
381
|
ORY_AGENT_SUBJECT_ID Override the subject ID for permission checks
|
|
@@ -387,12 +389,17 @@ function printNextSteps(harnessName, uninstallCmd) {
|
|
|
387
389
|
console.log("Next steps:");
|
|
388
390
|
console.log(" 1. Set required environment variables:");
|
|
389
391
|
console.log(" export ORY_PROJECT_URL=https://your-project.projects.oryapis.com");
|
|
390
|
-
console.log(" export
|
|
392
|
+
console.log(" export ORY_AGENT_API_KEY=ory_pat_...");
|
|
391
393
|
console.log("");
|
|
392
|
-
console.log(" 2.
|
|
394
|
+
console.log(" 2. Turn on the interactive user login gate (opt-in):");
|
|
395
|
+
console.log(" export ORY_AUTH_GATE=1");
|
|
396
|
+
console.log(` Without this, ${harnessName} runs without a human Ory identity attached`);
|
|
397
|
+
console.log(" to the session and permission checks fall back to a session:<id> subject.");
|
|
398
|
+
console.log("");
|
|
399
|
+
console.log(" 3. Optionally enable debug logging:");
|
|
393
400
|
console.log(" export ORY_AGENT_DEBUG=true");
|
|
394
401
|
console.log("");
|
|
395
|
-
console.log(`
|
|
402
|
+
console.log(` 4. Start ${harnessName} in your project directory.`);
|
|
396
403
|
console.log("");
|
|
397
404
|
console.log(`To uninstall: ${uninstallCmd}`);
|
|
398
405
|
}
|
package/dist/skills.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Canonical Ory agent skills and commands.
|
|
3
3
|
*
|
|
4
|
-
* The skill playbooks (auth-setup, login-flow, social-login, local-dev
|
|
4
|
+
* The skill playbooks (auth-setup, login-flow, social-login, local-dev,
|
|
5
|
+
* permissions-onboarding, contribute-integration, build-integration) and the
|
|
5
6
|
* local-stack commands (local-up, local-down) live once, as token-bearing
|
|
6
7
|
* templates under `packages/core/assets/`. Every harness plugin renders them
|
|
7
8
|
* through {@link renderOrySkills} / {@link renderOryCommands}, substituting the
|
|
@@ -50,7 +51,7 @@ export interface RenderProfileOptions {
|
|
|
50
51
|
packageName: string;
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
53
|
-
* Render the
|
|
54
|
+
* Render the guide skills for a harness as `SKILL.md` documents.
|
|
54
55
|
*/
|
|
55
56
|
export declare function renderOrySkills(harness: string, opts: RenderProfileOptions): RenderedSkill[];
|
|
56
57
|
/**
|
package/dist/skills.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Canonical Ory agent skills and commands.
|
|
4
4
|
*
|
|
5
|
-
* The skill playbooks (auth-setup, login-flow, social-login, local-dev
|
|
5
|
+
* The skill playbooks (auth-setup, login-flow, social-login, local-dev,
|
|
6
|
+
* permissions-onboarding, contribute-integration, build-integration) and the
|
|
6
7
|
* local-stack commands (local-up, local-down) live once, as token-bearing
|
|
7
8
|
* templates under `packages/core/assets/`. Every harness plugin renders them
|
|
8
9
|
* through {@link renderOrySkills} / {@link renderOryCommands}, substituting the
|
|
@@ -70,6 +71,16 @@ const SKILL_SOURCES = [
|
|
|
70
71
|
name: "ory-permissions-onboarding",
|
|
71
72
|
file: "skills/permissions-onboarding/SKILL.md",
|
|
72
73
|
},
|
|
74
|
+
{
|
|
75
|
+
id: "contribute-integration",
|
|
76
|
+
name: "ory-contribute-integration",
|
|
77
|
+
file: "skills/ory-contribute-integration/SKILL.md",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "build-integration",
|
|
81
|
+
name: "ory-build-integration",
|
|
82
|
+
file: "skills/ory-build-integration/SKILL.md",
|
|
83
|
+
},
|
|
73
84
|
];
|
|
74
85
|
const COMMAND_SOURCES = [
|
|
75
86
|
{
|
|
@@ -177,7 +188,7 @@ function parseFrontmatter(markdown) {
|
|
|
177
188
|
}
|
|
178
189
|
// ─── Public render API ──────────────────────────────────────────────
|
|
179
190
|
/**
|
|
180
|
-
* Render the
|
|
191
|
+
* Render the guide skills for a harness as `SKILL.md` documents.
|
|
181
192
|
*/
|
|
182
193
|
function renderOrySkills(harness, opts) {
|
|
183
194
|
const profile = buildProfile(harness, opts);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the "User identity" block. Shows the `ORY_AUTH_GATE` state and,
|
|
3
|
+
* when a PKCE token is cached, the resolved subject and expiry.
|
|
4
|
+
*/
|
|
5
|
+
export declare function printUserIdentitySection(): void;
|
|
6
|
+
/**
|
|
7
|
+
* Render the "Agent identity" block. Summarizes the resolved machine
|
|
8
|
+
* credential — static env override, persisted DCR, or unconfigured.
|
|
9
|
+
*/
|
|
10
|
+
export declare function printAgentIdentitySection(): void;
|
|
11
|
+
/**
|
|
12
|
+
* Render the "Permissions" block. Always prints the configured mode and
|
|
13
|
+
* namespace; runs a network probe of the harness's built-in tool catalog
|
|
14
|
+
* only when a project URL is configured *and* a user identity is
|
|
15
|
+
* available (cached PKCE tokens or `ORY_USER_SUBJECT_ID`).
|
|
16
|
+
*/
|
|
17
|
+
export declare function printPermissionsSection(binName: string, harness: string): Promise<void>;
|
|
18
|
+
export interface StatusCommandOptions {
|
|
19
|
+
/** Display title — typically the harness's human-friendly name, e.g. "Claude Code". */
|
|
20
|
+
title: string;
|
|
21
|
+
/**
|
|
22
|
+
* Optional renderer for the "Hooks & plugin" section. Each harness owns
|
|
23
|
+
* the layout for its plugin-specific registration data (extension dir,
|
|
24
|
+
* marketplace plugin ID, hook script path, etc.).
|
|
25
|
+
*/
|
|
26
|
+
printPluginSection?: () => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Compose the unified `status` output for a plugin CLI. Sections render
|
|
30
|
+
* in this order: title → configuration → user identity → agent identity
|
|
31
|
+
* → permissions → hooks & plugin → environment → recent log tail.
|
|
32
|
+
*
|
|
33
|
+
* Each section degrades gracefully — missing project URL, audit-only
|
|
34
|
+
* mode, and empty token caches all surface as inline "n/a" lines rather
|
|
35
|
+
* than aborting the command.
|
|
36
|
+
*/
|
|
37
|
+
export declare function runStatusCommand(binName: string, harness: string, options: StatusCommandOptions): Promise<void>;
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printUserIdentitySection = printUserIdentitySection;
|
|
4
|
+
exports.printAgentIdentitySection = printAgentIdentitySection;
|
|
5
|
+
exports.printPermissionsSection = printPermissionsSection;
|
|
6
|
+
exports.runStatusCommand = runStatusCommand;
|
|
7
|
+
/**
|
|
8
|
+
* Unified `status` CLI implementation. Each harness's `cli/main.ts` `status`
|
|
9
|
+
* subcommand delegates here so onboarding readers see configuration, user
|
|
10
|
+
* identity, agent identity, permissions, and the harness-specific plugin
|
|
11
|
+
* registration block in a single pass.
|
|
12
|
+
*
|
|
13
|
+
* The drill-down commands (`agent status`, `permissions status`) remain
|
|
14
|
+
* unchanged — this composes their inputs without altering their output.
|
|
15
|
+
*/
|
|
16
|
+
const config_js_1 = require("./config.js");
|
|
17
|
+
const auth_store_js_1 = require("./auth-store.js");
|
|
18
|
+
const agent_auth_js_1 = require("./agent-auth.js");
|
|
19
|
+
const client_js_1 = require("./client.js");
|
|
20
|
+
const agent_auth_js_2 = require("./agent-auth.js");
|
|
21
|
+
const subject_js_1 = require("./subject.js");
|
|
22
|
+
const tool_catalog_js_1 = require("./tool-catalog.js");
|
|
23
|
+
const cli_js_1 = require("./cli.js");
|
|
24
|
+
function resolveNamespace() {
|
|
25
|
+
return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools";
|
|
26
|
+
}
|
|
27
|
+
const AUTH_GATE_ENABLED = new Set(["1", "true", "yes", "on"]);
|
|
28
|
+
function isAuthGateOn() {
|
|
29
|
+
const v = process.env.ORY_AUTH_GATE?.toLowerCase();
|
|
30
|
+
return !!v && AUTH_GATE_ENABLED.has(v);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Format a relative duration like "4h 32m" or "12s" for a future deadline.
|
|
34
|
+
* Returns "expired" when the deadline has already passed.
|
|
35
|
+
*/
|
|
36
|
+
function formatExpiresIn(expiresAtSec, nowMs = Date.now()) {
|
|
37
|
+
const remaining = expiresAtSec - Math.floor(nowMs / 1000);
|
|
38
|
+
if (remaining <= 0)
|
|
39
|
+
return "expired";
|
|
40
|
+
const days = Math.floor(remaining / 86400);
|
|
41
|
+
const hours = Math.floor((remaining % 86400) / 3600);
|
|
42
|
+
const minutes = Math.floor((remaining % 3600) / 60);
|
|
43
|
+
if (days > 0)
|
|
44
|
+
return `${days}d ${hours}h`;
|
|
45
|
+
if (hours > 0)
|
|
46
|
+
return `${hours}h ${minutes}m`;
|
|
47
|
+
if (minutes > 0)
|
|
48
|
+
return `${minutes}m`;
|
|
49
|
+
return `${remaining}s`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Render the "User identity" block. Shows the `ORY_AUTH_GATE` state and,
|
|
53
|
+
* when a PKCE token is cached, the resolved subject and expiry.
|
|
54
|
+
*/
|
|
55
|
+
function printUserIdentitySection() {
|
|
56
|
+
const gateOn = isAuthGateOn();
|
|
57
|
+
const tokens = (0, auth_store_js_1.loadTokens)();
|
|
58
|
+
console.log("");
|
|
59
|
+
console.log("User identity (interactive PKCE login):");
|
|
60
|
+
console.log(` Gate: ${gateOn ? "on (ORY_AUTH_GATE)" : "off (set ORY_AUTH_GATE=1 to enable)"}`);
|
|
61
|
+
if (!tokens) {
|
|
62
|
+
console.log(" Token cache: empty");
|
|
63
|
+
if (gateOn) {
|
|
64
|
+
console.log(" Subject: (will resolve at next session start)");
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.log(" Subject: (gate disabled — no PKCE login will run)");
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const expired = (0, auth_store_js_1.isExpired)(tokens);
|
|
72
|
+
if (expired) {
|
|
73
|
+
console.log(` Token cache: stale (${formatExpiresIn(tokens.expiresAt)}) — will refresh on next session start`);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.log(` Token cache: present (expires in ${formatExpiresIn(tokens.expiresAt)})`);
|
|
77
|
+
}
|
|
78
|
+
const subject = tokens.subject ? `user:${tokens.subject}` : "(no subject claim on cached token)";
|
|
79
|
+
console.log(` Subject: ${subject}`);
|
|
80
|
+
if (tokens.clientId) {
|
|
81
|
+
console.log(` Client ID: ${tokens.clientId}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Render the "Agent identity" block. Summarizes the resolved machine
|
|
86
|
+
* credential — static env override, persisted DCR, or unconfigured.
|
|
87
|
+
*/
|
|
88
|
+
function printAgentIdentitySection() {
|
|
89
|
+
console.log("");
|
|
90
|
+
console.log("Agent identity (non-interactive):");
|
|
91
|
+
// Static overrides take precedence — surface them so the reader knows
|
|
92
|
+
// why DCR isn't running.
|
|
93
|
+
if (process.env.ORY_AGENT_API_KEY) {
|
|
94
|
+
console.log(" Source: static API key (ORY_AGENT_API_KEY)");
|
|
95
|
+
if (process.env.ORY_AGENT_SUBJECT_ID) {
|
|
96
|
+
console.log(` Subject: ${process.env.ORY_AGENT_SUBJECT_ID}`);
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (process.env.ORY_AGENT_CLIENT_ID && process.env.ORY_AGENT_CLIENT_SECRET) {
|
|
101
|
+
console.log(" Source: static client_credentials (ORY_AGENT_CLIENT_ID/SECRET)");
|
|
102
|
+
console.log(` Client ID: ${process.env.ORY_AGENT_CLIENT_ID}`);
|
|
103
|
+
const subject = process.env.ORY_AGENT_SUBJECT_ID ?? process.env.ORY_AGENT_CLIENT_ID;
|
|
104
|
+
console.log(` Subject: ${subject}`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const persisted = (0, agent_auth_js_1.loadAgentDynamicCredentials)();
|
|
108
|
+
if (!persisted) {
|
|
109
|
+
console.log(" Source: not registered yet");
|
|
110
|
+
console.log(" Status: first session start will register an OAuth2 client (RFC 7591)");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const resolved = (0, config_js_1.resolveConfig)();
|
|
114
|
+
const mismatched = resolved.projectUrl && persisted.projectUrl !== resolved.projectUrl;
|
|
115
|
+
console.log(" Source: OAuth2 Dynamic Client Registration (RFC 7591)");
|
|
116
|
+
console.log(` Status: registered`);
|
|
117
|
+
console.log(` Client ID: ${persisted.clientId}`);
|
|
118
|
+
const subject = process.env.ORY_AGENT_SUBJECT_ID ?? persisted.clientId;
|
|
119
|
+
console.log(` Subject: ${subject}`);
|
|
120
|
+
console.log(` Registered: ${new Date(persisted.registeredAt * 1000).toISOString()}`);
|
|
121
|
+
if (mismatched) {
|
|
122
|
+
console.log(` Warning: registered against ${persisted.projectUrl}, but current project URL is ${resolved.projectUrl}`);
|
|
123
|
+
console.log(" run `agent unregister` to clear stale credentials");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function probePermissionsCoverage(harness) {
|
|
127
|
+
const resolved = (0, config_js_1.resolveConfig)();
|
|
128
|
+
if (!resolved.projectUrl)
|
|
129
|
+
return undefined;
|
|
130
|
+
const catalog = (0, tool_catalog_js_1.getToolCatalog)(harness);
|
|
131
|
+
if (catalog.length === 0)
|
|
132
|
+
return undefined;
|
|
133
|
+
const tokens = (0, auth_store_js_1.loadTokens)();
|
|
134
|
+
const subjectOverride = process.env.ORY_USER_SUBJECT_ID;
|
|
135
|
+
if (!subjectOverride && (!tokens || (0, auth_store_js_1.isExpired)(tokens))) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
const client = client_js_1.OryAgentClient.fromEnv(harness);
|
|
139
|
+
if (tokens && !(0, auth_store_js_1.isExpired)(tokens)) {
|
|
140
|
+
client.setUserPrincipal({
|
|
141
|
+
subject: tokens.subject,
|
|
142
|
+
token: tokens.accessToken,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
await (0, agent_auth_js_2.ensureAgentIdentity)(client, { projectUrl: resolved.projectUrl }).catch(() => {
|
|
146
|
+
/* best-effort; the probe still runs with whatever credentials we have */
|
|
147
|
+
});
|
|
148
|
+
const subject = (0, subject_js_1.resolveUserSubject)(client);
|
|
149
|
+
const subjectId = (0, subject_js_1.subjectLabel)(subject);
|
|
150
|
+
if (subjectId === "agent:unknown")
|
|
151
|
+
return undefined;
|
|
152
|
+
const namespace = resolveNamespace();
|
|
153
|
+
const result = {
|
|
154
|
+
total: catalog.length,
|
|
155
|
+
allowed: 0,
|
|
156
|
+
denied: 0,
|
|
157
|
+
errored: 0,
|
|
158
|
+
};
|
|
159
|
+
for (const tool of catalog) {
|
|
160
|
+
const check = {
|
|
161
|
+
namespace,
|
|
162
|
+
object: tool,
|
|
163
|
+
relation: "use",
|
|
164
|
+
...subject,
|
|
165
|
+
};
|
|
166
|
+
try {
|
|
167
|
+
const probe = await client.checkPermission(check, {
|
|
168
|
+
spanAttributes: { toolName: tool, source: "status_coverage" },
|
|
169
|
+
});
|
|
170
|
+
if (probe.allowed)
|
|
171
|
+
result.allowed++;
|
|
172
|
+
else
|
|
173
|
+
result.denied++;
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
const ory = err;
|
|
177
|
+
result.errored++;
|
|
178
|
+
result.errorCode = result.errorCode ?? ory.code ?? "unknown";
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Render the "Permissions" block. Always prints the configured mode and
|
|
185
|
+
* namespace; runs a network probe of the harness's built-in tool catalog
|
|
186
|
+
* only when a project URL is configured *and* a user identity is
|
|
187
|
+
* available (cached PKCE tokens or `ORY_USER_SUBJECT_ID`).
|
|
188
|
+
*/
|
|
189
|
+
async function printPermissionsSection(binName, harness) {
|
|
190
|
+
const resolved = (0, config_js_1.resolveConfig)();
|
|
191
|
+
const namespace = resolveNamespace();
|
|
192
|
+
const catalog = (0, tool_catalog_js_1.getToolCatalog)(harness);
|
|
193
|
+
console.log("");
|
|
194
|
+
console.log("Permissions:");
|
|
195
|
+
console.log(` Mode: ${resolved.permissionMode}${formatModeSuffix(resolved.permissionModeSource)}`);
|
|
196
|
+
console.log(` Namespace: ${namespace}`);
|
|
197
|
+
if (resolved.auditOnly) {
|
|
198
|
+
console.log(" Coverage: n/a (audit-only mode — Ory checks disabled)");
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (!resolved.projectUrl) {
|
|
202
|
+
console.log(" Coverage: n/a (no project URL configured)");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (catalog.length === 0) {
|
|
206
|
+
console.log(` Coverage: n/a (no built-in catalog for harness "${harness}")`);
|
|
207
|
+
console.log(` known harnesses: ${tool_catalog_js_1.KNOWN_HARNESSES.join(", ")}`);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const hasUser = !!process.env.ORY_USER_SUBJECT_ID || isUserTokenUsable();
|
|
211
|
+
if (!hasUser) {
|
|
212
|
+
console.log(` Coverage: n/a (no cached user identity — run with ORY_AUTH_GATE=1 once,`);
|
|
213
|
+
console.log(` or set ORY_USER_SUBJECT_ID to probe a known subject)`);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
process.stdout.write(" Coverage: checking…\r");
|
|
217
|
+
const probe = await probePermissionsCoverage(harness);
|
|
218
|
+
// Clear the "checking…" line by overwriting it.
|
|
219
|
+
process.stdout.write(" ".repeat(40) + "\r");
|
|
220
|
+
if (!probe) {
|
|
221
|
+
console.log(" Coverage: unavailable (no user identity could be resolved)");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const { total, allowed, denied, errored } = probe;
|
|
225
|
+
if (errored > 0 && allowed === 0 && denied === 0) {
|
|
226
|
+
console.log(` Coverage: probe failed (${probe.errorCode ?? "unknown"})`);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const status = allowed === total
|
|
230
|
+
? `bootstrapped (${allowed}/${total} built-in tools)`
|
|
231
|
+
: allowed === 0
|
|
232
|
+
? `not bootstrapped (0/${total} built-in tools)`
|
|
233
|
+
: `partial (${allowed}/${total} built-in tools allowed)`;
|
|
234
|
+
console.log(` Coverage: ${status}`);
|
|
235
|
+
if (denied > 0) {
|
|
236
|
+
console.log(` run "npx ${binName} permissions bootstrap" to grant the missing tools`);
|
|
237
|
+
}
|
|
238
|
+
else if (errored > 0) {
|
|
239
|
+
console.log(` ${errored} probe error(s) — run "npx ${binName} permissions status" for details`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function isUserTokenUsable() {
|
|
243
|
+
const tokens = (0, auth_store_js_1.loadTokens)();
|
|
244
|
+
return !!tokens && !(0, auth_store_js_1.isExpired)(tokens);
|
|
245
|
+
}
|
|
246
|
+
function formatModeSuffix(source) {
|
|
247
|
+
switch (source) {
|
|
248
|
+
case "env":
|
|
249
|
+
return " (from ORY_PERMISSION_MODE env)";
|
|
250
|
+
case "config":
|
|
251
|
+
return " (from config — change with `permissions observe|enforce`)";
|
|
252
|
+
case "default":
|
|
253
|
+
return " (default)";
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Compose the unified `status` output for a plugin CLI. Sections render
|
|
258
|
+
* in this order: title → configuration → user identity → agent identity
|
|
259
|
+
* → permissions → hooks & plugin → environment → recent log tail.
|
|
260
|
+
*
|
|
261
|
+
* Each section degrades gracefully — missing project URL, audit-only
|
|
262
|
+
* mode, and empty token caches all surface as inline "n/a" lines rather
|
|
263
|
+
* than aborting the command.
|
|
264
|
+
*/
|
|
265
|
+
async function runStatusCommand(binName, harness, options) {
|
|
266
|
+
const heading = `Ory Agent Plugin Status (${options.title})`;
|
|
267
|
+
console.log(heading);
|
|
268
|
+
console.log("=".repeat(heading.length));
|
|
269
|
+
console.log("");
|
|
270
|
+
(0, cli_js_1.printOryConfig)();
|
|
271
|
+
printUserIdentitySection();
|
|
272
|
+
printAgentIdentitySection();
|
|
273
|
+
await printPermissionsSection(binName, harness);
|
|
274
|
+
if (options.printPluginSection) {
|
|
275
|
+
console.log("");
|
|
276
|
+
options.printPluginSection();
|
|
277
|
+
}
|
|
278
|
+
(0, cli_js_1.printEnvironment)();
|
|
279
|
+
(0, cli_js_1.printLogTail)();
|
|
280
|
+
console.log("");
|
|
281
|
+
console.log(`Drill into any section with:`);
|
|
282
|
+
console.log(` npx ${binName} agent status`);
|
|
283
|
+
console.log(` npx ${binName} permissions status`);
|
|
284
|
+
console.log(` npx ${binName} configure`);
|
|
285
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/argus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Ory Argus: the core API for building authentication, authorization, and audit into AI agent harness plugins, extensions, and custom integrations",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://ory.com",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"open": "^11.0.0"
|
|
70
70
|
},
|
|
71
71
|
"engines": {
|
|
72
|
-
"node": ">=
|
|
72
|
+
"node": ">=22"
|
|
73
73
|
},
|
|
74
74
|
"scripts": {
|
|
75
75
|
"build": "tsc",
|