@ory/pi 0.10.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 +129 -0
- package/dist/cli/assets.d.ts +14 -0
- package/dist/cli/assets.js +88 -0
- package/dist/cli/loader.d.ts +41 -0
- package/dist/cli/loader.js +104 -0
- package/dist/cli/main.d.ts +11 -0
- package/dist/cli/main.js +195 -0
- package/dist/cli/setup.d.ts +34 -0
- package/dist/cli/setup.js +98 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +11 -0
- package/dist/plugin.d.ts +42 -0
- package/dist/plugin.js +319 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.js +26 -0
- package/package.json +92 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Ory Agent Plugin: Pi
|
|
2
|
+
|
|
3
|
+
[Ory](https://ory.com) bundled into [Pi](https://pi.dev) ([badlogic/pi-mono](https://github.com/badlogic/pi-mono), a minimal coding agent): skills and commands that scaffold Ory authentication into your codebase, a local Ory stack you can spin up in one command, and (when pointed at an Ory project) authentication, authorization, and audit for every tool Pi runs.
|
|
4
|
+
|
|
5
|
+
You don't need an Ory account or any prior Ory experience to start.
|
|
6
|
+
|
|
7
|
+
The extension contract is verified against `@earendil-works/pi-coding-agent` v0.80.2 — the `session_start`/`tool_call`/`tool_result` events, the `tool_call` block signature (`{ block?, reason? }`), the `~/.pi/agent` config root, the `.pi/extensions/` auto-discovery loader, and the `.pi/skills/` location all match.
|
|
8
|
+
|
|
9
|
+
## New to Ory?
|
|
10
|
+
|
|
11
|
+
[Ory](https://www.ory.com/docs/) is an open-source identity and access platform — login, registration, sessions, social sign-in, multi-factor auth, and fine-grained permissions, so you don't have to build any of that yourself. Two things make it easy to try with no prior experience:
|
|
12
|
+
|
|
13
|
+
- **Ory Elements** are prebuilt, themeable UI components for the auth pages. The scaffolding skills wire them into your app for you.
|
|
14
|
+
- **The local Ory stack** is a complete Ory running on your laptop in Docker — no account, no signup, no API key.
|
|
15
|
+
|
|
16
|
+
## What this plugin does
|
|
17
|
+
|
|
18
|
+
Two independent things, and you can use either on its own:
|
|
19
|
+
|
|
20
|
+
1. **Build auth into your app.** Have Pi scaffold Ory login, registration, social sign-in, and permissions into the project you're working on, backed by the local stack. This needs nothing but Docker.
|
|
21
|
+
2. **Govern the agent itself.** Authenticate Pi's own session and authorize every tool it runs against Ory Permissions, with a full audit trail. See [Agent security](#agent-security).
|
|
22
|
+
|
|
23
|
+
## Prerequisites
|
|
24
|
+
|
|
25
|
+
- [Pi](https://pi.dev) installed (`@earendil-works/pi-coding-agent`)
|
|
26
|
+
- Node.js **≥ 22**
|
|
27
|
+
- [Docker](https://docs.docker.com/get-docker/) (only needed for the local Ory stack)
|
|
28
|
+
- macOS or Linux. Windows works via WSL2.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx -y -p @ory/pi ory-pi install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`install` drops a tiny loader file at `<project>/.pi/extensions/ory.js` (project scope, the default) — Pi auto-discovers every `*.js`/`*.ts` file under `.pi/extensions/` and runs its default export as an extension factory, so the loader simply re-exports the installed `@ory/pi` package's factory (`export { default } from "@ory/pi";`). No `settings.json` entry is needed. With `--global` the loader lands at `~/.pi/agent/extensions/ory.js` instead. Install also materializes the Ory skills and commands under `<project>/.pi/skills`.
|
|
37
|
+
|
|
38
|
+
The plugin must be resolvable from the project's `node_modules`, so install `@ory/pi` first (`npm install @ory/pi` or add it to your project's dependencies) before running `ory-pi install`.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx -y -p @ory/pi ory-pi install --global # install the loader for all projects
|
|
42
|
+
npx -y -p @ory/pi ory-pi status # confirm the loader + hooks
|
|
43
|
+
npx -y -p @ory/pi ory-pi uninstall # remove the loader and skills
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`status` prints configuration, user and agent identity, per-tool permission coverage, whether the extension loader is installed, the hooks it wires up, and a tail of recent debug logs.
|
|
47
|
+
|
|
48
|
+
### Alternative: install via Pi's package manager
|
|
49
|
+
|
|
50
|
+
If you prefer Pi to manage the extension, point its package installer at the npm package:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pi install npm:@ory/pi
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This records the package under `settings.packages` and reads the `pi.extensions` manifest from `@ory/pi`'s `package.json` (which points at `dist/index.js`, the default-exported factory). Either path leads to the same extension; pick whichever fits your workflow.
|
|
57
|
+
|
|
58
|
+
## Quickstart (≈ 3 minutes)
|
|
59
|
+
|
|
60
|
+
From any project where you'd like Ory authentication, inside Pi:
|
|
61
|
+
|
|
62
|
+
1. **Start a local Ory instance.** Ask Pi *"start the local Ory stack"* or run `/local-up`. A banner prints the seeded test user's email and password — note them.
|
|
63
|
+
2. **Scaffold Ory into your project.** Ask Pi *"add Ory auth to this app"* (the `ory-auth-setup` skill). It installs Ory Elements, wires the SDK, and generates the login / registration / recovery / settings pages, all targeting the local stack.
|
|
64
|
+
3. **Sign in.** Start your app, visit the login page, and sign in with the seeded credentials. You now have a real Ory session backed by a real Ory stack — locally, offline, zero configuration.
|
|
65
|
+
|
|
66
|
+
Continue to [Agent security](#agent-security) when you're ready to enforce.
|
|
67
|
+
|
|
68
|
+
## How the integration works
|
|
69
|
+
|
|
70
|
+
Pi loads in-process **TypeScript/JavaScript extensions** auto-discovered from `.pi/extensions/` (project) and `~/.pi/agent/extensions/` (global), loaded with [jiti](https://github.com/unjs/jiti). Each file's **default export** is invoked as a factory function at startup; the installed loader re-exports `@ory/pi`'s default factory.
|
|
71
|
+
|
|
72
|
+
The phases map as:
|
|
73
|
+
|
|
74
|
+
- **Session start** (factory body) — run the user and agent auth gates. Advisory only: Pi's factory has no return-value channel to hard-block at session start, so user login refreshes tokens and emits an audit span but the session always proceeds.
|
|
75
|
+
- **Per-tool decision** — the factory registers a `pi.on("tool_call")` handler that checks the tool against Ory Permissions. In enforce mode a deny **blocks by returning `{ block: true, reason }`**; in observe mode it records a `permission.observe_deny` span and allows.
|
|
76
|
+
- **Post-tool** — a `pi.on("tool_result")` handler records a `tool.complete` trace span. Trace-only.
|
|
77
|
+
|
|
78
|
+
**Pi excludes MCP and sub-agents by design**, so this plugin has **no MCP server registration and no sub-agent identity path** — unlike the Claude Code or Continue plugins, there is nothing to register beyond the extension itself.
|
|
79
|
+
|
|
80
|
+
The plugin is **fail-open** on its own infrastructure failures (network errors, rate limits, missing config): the agent always starts, and enforcement is only as strong as your permission grants.
|
|
81
|
+
|
|
82
|
+
## Agent security
|
|
83
|
+
|
|
84
|
+
Once pointed at an Ory project (local or hosted), Pi's session and every tool call can be governed by Ory.
|
|
85
|
+
|
|
86
|
+
- **Authentication.** The human at the keyboard (the **user**) authenticates interactively via Ory Identities when user login is on (`ORY_USER_LOGIN=true`, off by default — browser PKCE flow on first session, persisted thereafter). The Pi process (the **agent**) gets its own OAuth2 identity via [Dynamic Client Registration (RFC 7591)](https://datatracker.ietf.org/doc/html/rfc7591) on first run.
|
|
87
|
+
- **Authorization.** Before any tool runs, the `tool_call` handler checks [Ory Permissions](https://www.ory.com/docs/keto) (Zanzibar-style relations) against the user's subject and blocks on `deny`.
|
|
88
|
+
- **Audit.** Every decision is recorded as a structured trace span (NDJSON file and/or OTLP export).
|
|
89
|
+
|
|
90
|
+
### Permission modes: observe → enforce
|
|
91
|
+
|
|
92
|
+
After install the plugin runs in **observe mode**: every tool call is checked, but a deny is recorded as a `permission.observe_deny` audit span and the tool runs anyway.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Grant the current user `use` on every built-in tool (idempotent):
|
|
96
|
+
npx -y -p @ory/pi ory-pi permissions bootstrap
|
|
97
|
+
|
|
98
|
+
# See allowed/denied per tool:
|
|
99
|
+
npx -y -p @ory/pi ory-pi permissions status
|
|
100
|
+
|
|
101
|
+
# Turn on hard blocking once the observe-mode logs look right:
|
|
102
|
+
npx -y -p @ory/pi ory-pi permissions enforce
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Switch back any time with `permissions observe`. To disable Ory entirely (audit logging only), run `ory-pi configure --audit-only`.
|
|
106
|
+
|
|
107
|
+
## Pointing at a real Ory project
|
|
108
|
+
|
|
109
|
+
The Quickstart uses the local stack. To point at a hosted [Ory Network](https://console.ory.sh) project:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx -y -p @ory/pi ory-pi configure \
|
|
113
|
+
--project-url https://<id>.projects.oryapis.com \
|
|
114
|
+
--oauth2-client-id <public OAuth2 client id>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`--oauth2-client-id` is required whenever `--project-url` is set — the user PKCE flow needs a public OAuth2 client registered with the four loopback redirect URIs (`http://127.0.0.1:47823..47826/callback`). See the [repo README](../../README.md) and [`AGENTS.md`](../../AGENTS.md) for the full environment-variable reference and permission-mode semantics. Config is shared across every Ory agent plugin at `~/.config/ory-agent-plugins/config.json`.
|
|
118
|
+
|
|
119
|
+
Without configuration the plugin still loads cleanly and runs in **pass-through mode**: skills and commands work, but nothing is blocked.
|
|
120
|
+
|
|
121
|
+
## Links
|
|
122
|
+
|
|
123
|
+
- [Ory documentation](https://www.ory.com/docs/)
|
|
124
|
+
- [Pi repository](https://github.com/badlogic/pi-mono)
|
|
125
|
+
- [Repo README](../../README.md) and [AGENTS.md](../../AGENTS.md) — full env-var and permission-mode reference
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
Apache-2.0
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Materialize / clean the Ory skill catalog and command-skills for the Pi
|
|
3
|
+
* plugin. The guide skills plus the local-stack commands (rendered as
|
|
4
|
+
* user-invocable skills) land at `<projectDir>/.pi/skills/<name>/SKILL.md`.
|
|
5
|
+
*
|
|
6
|
+
* Pi has a skills system but no native slash-command system to rely on, so —
|
|
7
|
+
* like Codex and OpenClaw — the commands are wrapped with `commandToSkill` and
|
|
8
|
+
* written alongside the guide skills.
|
|
9
|
+
*
|
|
10
|
+
* Skills live under `<projectDir>/.pi/skills/`, verified against
|
|
11
|
+
* @earendil-works/pi-coding-agent v0.80.2 (the `.pi` config dir).
|
|
12
|
+
*/
|
|
13
|
+
export declare function installPiOryAssets(projectDir: string): void;
|
|
14
|
+
export declare function uninstallPiOryAssets(projectDir: string): void;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Materialize / clean the Ory skill catalog and command-skills for the Pi
|
|
4
|
+
* plugin. The guide skills plus the local-stack commands (rendered as
|
|
5
|
+
* user-invocable skills) land at `<projectDir>/.pi/skills/<name>/SKILL.md`.
|
|
6
|
+
*
|
|
7
|
+
* Pi has a skills system but no native slash-command system to rely on, so —
|
|
8
|
+
* like Codex and OpenClaw — the commands are wrapped with `commandToSkill` and
|
|
9
|
+
* written alongside the guide skills.
|
|
10
|
+
*
|
|
11
|
+
* Skills live under `<projectDir>/.pi/skills/`, verified against
|
|
12
|
+
* @earendil-works/pi-coding-agent v0.80.2 (the `.pi` config dir).
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.installPiOryAssets = installPiOryAssets;
|
|
49
|
+
exports.uninstallPiOryAssets = uninstallPiOryAssets;
|
|
50
|
+
const fs = __importStar(require("node:fs"));
|
|
51
|
+
const path = __importStar(require("node:path"));
|
|
52
|
+
const argus_1 = require("@ory/argus");
|
|
53
|
+
const RENDER_OPTS = {
|
|
54
|
+
binName: "ory-pi",
|
|
55
|
+
packageName: "@ory/pi",
|
|
56
|
+
};
|
|
57
|
+
const CONFIG_DIR = ".pi";
|
|
58
|
+
function piRoot(projectDir) {
|
|
59
|
+
return path.join(projectDir, CONFIG_DIR);
|
|
60
|
+
}
|
|
61
|
+
function skillsDir(projectDir) {
|
|
62
|
+
return path.join(piRoot(projectDir), "skills");
|
|
63
|
+
}
|
|
64
|
+
function installPiOryAssets(projectDir) {
|
|
65
|
+
const dir = skillsDir(projectDir);
|
|
66
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
67
|
+
const guideSkills = (0, argus_1.renderOrySkills)("pi", RENDER_OPTS);
|
|
68
|
+
const commandSkills = (0, argus_1.renderOryCommands)("pi", RENDER_OPTS).map(argus_1.commandToSkill);
|
|
69
|
+
(0, argus_1.writeSkillTree)(dir, [...guideSkills, ...commandSkills]);
|
|
70
|
+
}
|
|
71
|
+
function safeRmEmpty(dir) {
|
|
72
|
+
try {
|
|
73
|
+
if (fs.existsSync(dir) && fs.readdirSync(dir).length === 0) {
|
|
74
|
+
fs.rmdirSync(dir);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
/* ignore */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function uninstallPiOryAssets(projectDir) {
|
|
82
|
+
const dir = skillsDir(projectDir);
|
|
83
|
+
if (fs.existsSync(dir)) {
|
|
84
|
+
(0, argus_1.removeSkillDirs)(dir, [...argus_1.ORY_SKILL_NAMES, ...argus_1.ORY_COMMAND_SKILL_NAMES]);
|
|
85
|
+
safeRmEmpty(dir);
|
|
86
|
+
}
|
|
87
|
+
safeRmEmpty(piRoot(projectDir));
|
|
88
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared logic for installing / removing the Ory extension loader file.
|
|
3
|
+
*
|
|
4
|
+
* Pi auto-discovers in-process extensions from `<cwd>/.pi/extensions/` (project
|
|
5
|
+
* scope) and `~/.pi/agent/extensions/` (global scope): each `*.ts`/`*.js` file
|
|
6
|
+
* placed there is loaded via jiti and its default export is invoked as the
|
|
7
|
+
* extension factory. A bare module id in `settings.json`'s `extensions` array is
|
|
8
|
+
* NOT loaded — those entries are resolved as filesystem paths. So we drop a
|
|
9
|
+
* tiny loader file that re-exports the installed `@ory/pi` package's default
|
|
10
|
+
* factory; Pi discovers the file, jiti resolves `@ory/pi` from `node_modules`,
|
|
11
|
+
* and the factory runs. No `settings.json` entry is required.
|
|
12
|
+
*
|
|
13
|
+
* Verified against @earendil-works/pi-coding-agent v0.80.2 (the extension
|
|
14
|
+
* loader's `discoverAndLoadExtensions` / `discoverExtensionsInDir`).
|
|
15
|
+
*/
|
|
16
|
+
export declare const EXTENSION_MODULE = "@ory/pi";
|
|
17
|
+
export declare const LOADER_FILENAME = "ory.js";
|
|
18
|
+
export declare const LOADER_CONTENTS = "// Ory extension loader for Pi. Re-exports @ory/pi's default factory so Pi's\n// extension auto-discovery (which scans .pi/extensions/ for *.js files) can\n// load it. Generated by 'ory-pi install' / 'ory-pi-setup' \u2014 safe to delete.\nexport { default } from \"@ory/pi\";\n";
|
|
19
|
+
/** Resolve the `.pi/extensions/` dir for the requested scope. */
|
|
20
|
+
export declare function getExtensionsDir(opts: {
|
|
21
|
+
global?: boolean;
|
|
22
|
+
projectDir: string;
|
|
23
|
+
}): string;
|
|
24
|
+
/** Full path to the loader file for the requested scope. */
|
|
25
|
+
export declare function getLoaderPath(opts: {
|
|
26
|
+
global?: boolean;
|
|
27
|
+
projectDir: string;
|
|
28
|
+
}): string;
|
|
29
|
+
/** Write the loader file, creating `.pi/extensions/` as needed. */
|
|
30
|
+
export declare function writeOryLoader(opts: {
|
|
31
|
+
global?: boolean;
|
|
32
|
+
projectDir: string;
|
|
33
|
+
}): string;
|
|
34
|
+
/**
|
|
35
|
+
* Remove the loader file (and the `.pi/extensions/` dir if it's left empty).
|
|
36
|
+
* Returns true if a loader file was present and removed.
|
|
37
|
+
*/
|
|
38
|
+
export declare function removeOryLoader(opts: {
|
|
39
|
+
global?: boolean;
|
|
40
|
+
projectDir: string;
|
|
41
|
+
}): boolean;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared logic for installing / removing the Ory extension loader file.
|
|
4
|
+
*
|
|
5
|
+
* Pi auto-discovers in-process extensions from `<cwd>/.pi/extensions/` (project
|
|
6
|
+
* scope) and `~/.pi/agent/extensions/` (global scope): each `*.ts`/`*.js` file
|
|
7
|
+
* placed there is loaded via jiti and its default export is invoked as the
|
|
8
|
+
* extension factory. A bare module id in `settings.json`'s `extensions` array is
|
|
9
|
+
* NOT loaded — those entries are resolved as filesystem paths. So we drop a
|
|
10
|
+
* tiny loader file that re-exports the installed `@ory/pi` package's default
|
|
11
|
+
* factory; Pi discovers the file, jiti resolves `@ory/pi` from `node_modules`,
|
|
12
|
+
* and the factory runs. No `settings.json` entry is required.
|
|
13
|
+
*
|
|
14
|
+
* Verified against @earendil-works/pi-coding-agent v0.80.2 (the extension
|
|
15
|
+
* loader's `discoverAndLoadExtensions` / `discoverExtensionsInDir`).
|
|
16
|
+
*/
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.LOADER_CONTENTS = exports.LOADER_FILENAME = exports.EXTENSION_MODULE = void 0;
|
|
52
|
+
exports.getExtensionsDir = getExtensionsDir;
|
|
53
|
+
exports.getLoaderPath = getLoaderPath;
|
|
54
|
+
exports.writeOryLoader = writeOryLoader;
|
|
55
|
+
exports.removeOryLoader = removeOryLoader;
|
|
56
|
+
const fs = __importStar(require("node:fs"));
|
|
57
|
+
const os = __importStar(require("node:os"));
|
|
58
|
+
const path = __importStar(require("node:path"));
|
|
59
|
+
exports.EXTENSION_MODULE = "@ory/pi";
|
|
60
|
+
exports.LOADER_FILENAME = "ory.js";
|
|
61
|
+
exports.LOADER_CONTENTS = `// Ory extension loader for Pi. Re-exports @ory/pi's default factory so Pi's
|
|
62
|
+
// extension auto-discovery (which scans .pi/extensions/ for *.js files) can
|
|
63
|
+
// load it. Generated by 'ory-pi install' / 'ory-pi-setup' — safe to delete.
|
|
64
|
+
export { default } from "@ory/pi";
|
|
65
|
+
`;
|
|
66
|
+
/** Resolve the `.pi/extensions/` dir for the requested scope. */
|
|
67
|
+
function getExtensionsDir(opts) {
|
|
68
|
+
return opts.global
|
|
69
|
+
? path.join(os.homedir(), ".pi", "agent", "extensions")
|
|
70
|
+
: path.join(opts.projectDir, ".pi", "extensions");
|
|
71
|
+
}
|
|
72
|
+
/** Full path to the loader file for the requested scope. */
|
|
73
|
+
function getLoaderPath(opts) {
|
|
74
|
+
return path.join(getExtensionsDir(opts), exports.LOADER_FILENAME);
|
|
75
|
+
}
|
|
76
|
+
/** Write the loader file, creating `.pi/extensions/` as needed. */
|
|
77
|
+
function writeOryLoader(opts) {
|
|
78
|
+
const dir = getExtensionsDir(opts);
|
|
79
|
+
const loaderPath = path.join(dir, exports.LOADER_FILENAME);
|
|
80
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
81
|
+
fs.writeFileSync(loaderPath, exports.LOADER_CONTENTS);
|
|
82
|
+
return loaderPath;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Remove the loader file (and the `.pi/extensions/` dir if it's left empty).
|
|
86
|
+
* Returns true if a loader file was present and removed.
|
|
87
|
+
*/
|
|
88
|
+
function removeOryLoader(opts) {
|
|
89
|
+
const dir = getExtensionsDir(opts);
|
|
90
|
+
const loaderPath = path.join(dir, exports.LOADER_FILENAME);
|
|
91
|
+
const existed = fs.existsSync(loaderPath);
|
|
92
|
+
if (existed) {
|
|
93
|
+
fs.unlinkSync(loaderPath);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
if (fs.existsSync(dir) && fs.readdirSync(dir).length === 0) {
|
|
97
|
+
fs.rmdirSync(dir);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
/* ignore */
|
|
102
|
+
}
|
|
103
|
+
return existed;
|
|
104
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Main CLI for @ory/pi.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx ory-pi install [--project-dir <path>] [--global] Install the Ory extension loader into .pi/extensions/
|
|
7
|
+
* npx ory-pi uninstall Remove the Ory extension loader
|
|
8
|
+
* npx ory-pi configure Set or view Ory project URL and API key
|
|
9
|
+
* npx ory-pi status Show plugin status
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Main CLI for @ory/pi.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx ory-pi install [--project-dir <path>] [--global] Install the Ory extension loader into .pi/extensions/
|
|
8
|
+
* npx ory-pi uninstall Remove the Ory extension loader
|
|
9
|
+
* npx ory-pi configure Set or view Ory project URL and API key
|
|
10
|
+
* npx ory-pi status Show plugin status
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
const fs = __importStar(require("node:fs"));
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
48
|
+
const argus_1 = require("@ory/argus");
|
|
49
|
+
const assets_js_1 = require("./assets.js");
|
|
50
|
+
const loader_js_1 = require("./loader.js");
|
|
51
|
+
function main() {
|
|
52
|
+
const [command, ...args] = process.argv.slice(2);
|
|
53
|
+
switch (command) {
|
|
54
|
+
case "install":
|
|
55
|
+
install(args);
|
|
56
|
+
postInstallPermissions("ory-pi", "pi").then(() => process.exit(0), (err) => {
|
|
57
|
+
console.error(err.message ?? err);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
});
|
|
60
|
+
break;
|
|
61
|
+
case "uninstall":
|
|
62
|
+
uninstall(args);
|
|
63
|
+
break;
|
|
64
|
+
case "configure":
|
|
65
|
+
(0, argus_1.runConfigureCommand)("ory-pi", args);
|
|
66
|
+
break;
|
|
67
|
+
case "agent":
|
|
68
|
+
(0, argus_1.runAgentCommand)("ory-pi", args).then((code) => process.exit(code), (err) => {
|
|
69
|
+
console.error(err.message ?? err);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
});
|
|
72
|
+
break;
|
|
73
|
+
case "permissions":
|
|
74
|
+
(0, argus_1.runPermissionsCommand)("ory-pi", "pi", args).then((code) => process.exit(code), (err) => {
|
|
75
|
+
console.error(err.message ?? err);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
break;
|
|
79
|
+
case "status":
|
|
80
|
+
status(args).then(() => process.exit(0), (err) => {
|
|
81
|
+
console.error(err.message ?? err);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|
|
84
|
+
break;
|
|
85
|
+
case "local":
|
|
86
|
+
(0, argus_1.runLocalCommand)("ory-pi", args).catch((err) => {
|
|
87
|
+
console.error(err.message ?? err);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
});
|
|
90
|
+
break;
|
|
91
|
+
case "watch":
|
|
92
|
+
(0, argus_1.runWatchCommand)(args);
|
|
93
|
+
break;
|
|
94
|
+
case "help":
|
|
95
|
+
case "--help":
|
|
96
|
+
case "-h":
|
|
97
|
+
case undefined:
|
|
98
|
+
help();
|
|
99
|
+
break;
|
|
100
|
+
default:
|
|
101
|
+
console.error(`Unknown command: ${command}`);
|
|
102
|
+
help();
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function postInstallPermissions(binName, harness) {
|
|
107
|
+
const bootstrapped = await (0, argus_1.maybeAutoBootstrap)(binName, harness);
|
|
108
|
+
(0, argus_1.printPermissionsOnboardingHelp)(binName, harness, {
|
|
109
|
+
bootstrappedAutomatically: bootstrapped,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function parseProjectDir(args) {
|
|
113
|
+
const idx = args.indexOf("--project-dir");
|
|
114
|
+
if (idx !== -1 && args[idx + 1])
|
|
115
|
+
return args[idx + 1];
|
|
116
|
+
return process.cwd();
|
|
117
|
+
}
|
|
118
|
+
function isGlobal(args) {
|
|
119
|
+
return args.includes("--global") || args.includes("-g");
|
|
120
|
+
}
|
|
121
|
+
// Pi auto-discovers extensions from `<cwd>/.pi/extensions/` (project) and
|
|
122
|
+
// `~/.pi/agent/extensions/` (global): each `*.js`/`*.ts` file there is loaded
|
|
123
|
+
// via jiti and its default export is the factory. We drop a loader file that
|
|
124
|
+
// re-exports @ory/pi's default factory. No settings.json entry is needed.
|
|
125
|
+
// Verified against @earendil-works/pi-coding-agent v0.80.2. See ./loader.ts.
|
|
126
|
+
function loaderOpts(args) {
|
|
127
|
+
return { global: isGlobal(args), projectDir: parseProjectDir(args) };
|
|
128
|
+
}
|
|
129
|
+
function install(args) {
|
|
130
|
+
const projectDir = parseProjectDir(args);
|
|
131
|
+
const loaderPath = (0, loader_js_1.writeOryLoader)(loaderOpts(args));
|
|
132
|
+
(0, assets_js_1.installPiOryAssets)(projectDir);
|
|
133
|
+
console.log(`Ory extension loader installed at ${loaderPath}`);
|
|
134
|
+
console.log(` Re-exports the default factory from ${loader_js_1.EXTENSION_MODULE}`);
|
|
135
|
+
console.log(` Skills + commands installed under ${path.join(projectDir, ".pi", "skills")}`);
|
|
136
|
+
(0, argus_1.printEnvHelp)("ory-pi");
|
|
137
|
+
}
|
|
138
|
+
function uninstall(args) {
|
|
139
|
+
const projectDir = parseProjectDir(args);
|
|
140
|
+
const removed = (0, loader_js_1.removeOryLoader)(loaderOpts(args));
|
|
141
|
+
console.log(removed
|
|
142
|
+
? "Ory extension loader removed."
|
|
143
|
+
: "No Ory extension loader found. Nothing to uninstall.");
|
|
144
|
+
(0, assets_js_1.uninstallPiOryAssets)(projectDir);
|
|
145
|
+
}
|
|
146
|
+
async function status(args) {
|
|
147
|
+
const loaderPath = (0, loader_js_1.getLoaderPath)(loaderOpts(args));
|
|
148
|
+
await (0, argus_1.runStatusCommand)("ory-pi", "pi", {
|
|
149
|
+
title: "Pi",
|
|
150
|
+
printPluginSection: () => {
|
|
151
|
+
const installed = fs.existsSync(loaderPath);
|
|
152
|
+
console.log("Extension & hooks:");
|
|
153
|
+
console.log(` Loader file: ${loaderPath}`);
|
|
154
|
+
console.log(` Extension installed: ${installed ? "yes" : "NO"}`);
|
|
155
|
+
if (installed) {
|
|
156
|
+
console.log(` Re-exports: ${loader_js_1.EXTENSION_MODULE} (default factory)`);
|
|
157
|
+
}
|
|
158
|
+
console.log(` Blocking support: yes (tool_call can deny tool execution)`);
|
|
159
|
+
console.log(` Hooks registered: session-start (factory body), tool_call, tool_result`);
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function help() {
|
|
164
|
+
console.log(`
|
|
165
|
+
ory-pi — Ory plugin for Pi (the minimal coding agent)
|
|
166
|
+
|
|
167
|
+
Usage:
|
|
168
|
+
npx -y -p @ory/pi ory-pi <command> [options]
|
|
169
|
+
|
|
170
|
+
Commands:
|
|
171
|
+
install [--project-dir <path>] [--global] Install the Ory extension loader into .pi/extensions/
|
|
172
|
+
uninstall [--project-dir <path>] [--global] Remove the Ory extension loader
|
|
173
|
+
configure Set or view Ory project URL and API key
|
|
174
|
+
agent <status|unregister> Manage the agent's OAuth2 (DCR) identity
|
|
175
|
+
permissions <cmd> Manage permission mode and tool permissions
|
|
176
|
+
(status, bootstrap, observe, enforce)
|
|
177
|
+
local <cmd> Manage local Ory dev environment
|
|
178
|
+
(up, down, status, seed, logs, env, configure, reset)
|
|
179
|
+
status [--project-dir <path>] Show plugin configuration and status
|
|
180
|
+
watch [trace-file] Watch live trace output
|
|
181
|
+
|
|
182
|
+
After installing, the plugin hooks into these Pi lifecycle events:
|
|
183
|
+
session-start (factory body) Verify session / resolve agent identity via Ory
|
|
184
|
+
tool_call Check tool permissions via Ory Permissions (can block)
|
|
185
|
+
tool_result Record tool completion trace
|
|
186
|
+
|
|
187
|
+
Examples:
|
|
188
|
+
npx -y -p @ory/pi ory-pi install
|
|
189
|
+
npx -y -p @ory/pi ory-pi configure --project-url https://<id>.projects.oryapis.com --api-key ory_pat_...
|
|
190
|
+
npx -y -p @ory/pi ory-pi permissions bootstrap
|
|
191
|
+
npx -y -p @ory/pi ory-pi local up
|
|
192
|
+
npx -y -p @ory/pi ory-pi status
|
|
193
|
+
`);
|
|
194
|
+
}
|
|
195
|
+
main();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Setup CLI for the Ory Pi plugin.
|
|
4
|
+
*
|
|
5
|
+
* Pi auto-discovers in-process extensions from `<cwd>/.pi/extensions/` (project
|
|
6
|
+
* scope) and `~/.pi/agent/extensions/` (global scope): every `*.ts`/`*.js` file
|
|
7
|
+
* placed there is loaded via jiti and its default export is invoked as the
|
|
8
|
+
* extension factory. A bare module id in `settings.json`'s `extensions` array is
|
|
9
|
+
* NOT loaded — those entries are resolved as filesystem paths.
|
|
10
|
+
*
|
|
11
|
+
* So this writer drops a tiny loader file that re-exports the installed
|
|
12
|
+
* `@ory/pi` package's default factory:
|
|
13
|
+
*
|
|
14
|
+
* // .pi/extensions/ory.js
|
|
15
|
+
* export { default } from "@ory/pi";
|
|
16
|
+
*
|
|
17
|
+
* Pi discovers the file, jiti resolves `@ory/pi` from `node_modules`, and the
|
|
18
|
+
* factory runs. Project scope writes `<projectDir>/.pi/extensions/ory.js`;
|
|
19
|
+
* global scope (`--global`) writes `~/.pi/agent/extensions/ory.js`. No
|
|
20
|
+
* `settings.json` entry is required — discovery alone enables the extension.
|
|
21
|
+
*
|
|
22
|
+
* Pi excludes MCP by design, so there is NO MCP server block.
|
|
23
|
+
*
|
|
24
|
+
* Usage:
|
|
25
|
+
* npx ory-pi-setup # Install loader into project .pi/extensions/
|
|
26
|
+
* npx ory-pi-setup --global # Install loader into ~/.pi/agent/extensions/
|
|
27
|
+
* npx ory-pi-setup --project-dir /path/to/repo
|
|
28
|
+
* npx ory-pi-setup --print # Print the loader contents to stdout
|
|
29
|
+
* npx ory-pi-setup --uninstall # Remove the Ory loader
|
|
30
|
+
*
|
|
31
|
+
* Verified against @earendil-works/pi-coding-agent v0.80.2 (the extension
|
|
32
|
+
* loader's `discoverAndLoadExtensions` / `discoverExtensionsInDir`).
|
|
33
|
+
*/
|
|
34
|
+
export {};
|