@ory/amp 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 +120 -0
- package/dist/cli/assets.d.ts +18 -0
- package/dist/cli/assets.js +91 -0
- package/dist/cli/main.d.ts +12 -0
- package/dist/cli/main.js +192 -0
- package/dist/cli/setup.d.ts +32 -0
- package/dist/cli/setup.js +197 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +15 -0
- package/dist/permission.d.ts +46 -0
- package/dist/permission.js +201 -0
- package/dist/plugin.d.ts +42 -0
- package/dist/plugin.js +217 -0
- package/dist/types.d.ts +176 -0
- package/dist/types.js +45 -0
- package/package.json +87 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Ory Agent Plugin: Amp
|
|
2
|
+
|
|
3
|
+
[Ory](https://ory.com) bundled into [Amp](https://ampcode.com) (Sourcegraph's coding agent): skills 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 Amp runs.
|
|
4
|
+
|
|
5
|
+
You don't need an Ory account or any prior Ory experience to start.
|
|
6
|
+
|
|
7
|
+
## New to Ory?
|
|
8
|
+
|
|
9
|
+
[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:
|
|
10
|
+
|
|
11
|
+
- **Ory Elements** are prebuilt, themeable UI components for the auth pages. The scaffolding skills wire them into your app for you.
|
|
12
|
+
- **The local Ory stack** is a complete Ory running on your laptop in Docker — no account, no signup, no API key.
|
|
13
|
+
|
|
14
|
+
## What this plugin does
|
|
15
|
+
|
|
16
|
+
Two independent things, and you can use either on its own:
|
|
17
|
+
|
|
18
|
+
1. **Build auth into your app.** Have Amp 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.
|
|
19
|
+
2. **Govern the agent itself.** Authenticate Amp's own session and authorize every tool it runs against Ory Permissions, with a full audit trail. See [Agent security](#agent-security).
|
|
20
|
+
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
- [Amp](https://ampcode.com) installed
|
|
24
|
+
- Node.js **≥ 22**
|
|
25
|
+
- [Docker](https://docs.docker.com/get-docker/) (only needed for the local Ory stack)
|
|
26
|
+
- macOS or Linux. Windows works via WSL2.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx -y -p @ory/amp ory-amp install
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`install` performs two registrations and materializes the Ory skills:
|
|
35
|
+
|
|
36
|
+
1. Registers the Ory **permission delegate** in Amp's settings (`~/.config/amp/settings.json`) under the flat top-level `amp.permissions` key as `{ tool: "*", action: "delegate", to: "ory-amp-permission" }` — this is the blocking gate.
|
|
37
|
+
2. Writes the **in-process plugin** loader at `<project>/.amp/plugins/ory.ts` so Amp's Bun TypeScript runtime discovers it for session auth and audit tracing.
|
|
38
|
+
|
|
39
|
+
The Ory skill playbooks land under `<project>/.agents/skills/` (Amp's project skills directory).
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx -y -p @ory/amp ory-amp status # confirm delegate + plugin registration
|
|
43
|
+
npx -y -p @ory/amp ory-amp uninstall # remove the delegate, plugin, and skills
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`status` prints configuration, user and agent identity, per-tool permission coverage, whether the delegate and in-process plugin are registered, and a tail of recent debug logs.
|
|
47
|
+
|
|
48
|
+
## Quickstart (≈ 3 minutes)
|
|
49
|
+
|
|
50
|
+
From any project where you'd like Ory authentication, inside Amp:
|
|
51
|
+
|
|
52
|
+
1. **Start a local Ory instance.** Ask Amp *"start the local Ory stack"* or run `/local-up`. A banner prints the seeded test user's email and password — note them.
|
|
53
|
+
2. **Scaffold Ory into your project.** Ask Amp *"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.
|
|
54
|
+
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.
|
|
55
|
+
|
|
56
|
+
Continue to [Agent security](#agent-security) when you're ready to enforce.
|
|
57
|
+
|
|
58
|
+
## How the integration works
|
|
59
|
+
|
|
60
|
+
Amp's Ory integration is a **hybrid** of two mechanisms:
|
|
61
|
+
|
|
62
|
+
- **Blocking gate — a permission delegate helper (`ory-amp-permission`).** Registered in `amp.permissions`, Amp invokes it per tool call with the tool params on stdin and decides by **exit code**: `0` = allow, `1` = ask, `≥2` = reject (stderr is surfaced to the model). This is the only part that can block. Amp enforces a 10-second delegate timeout, so the Ory check fails open (allows) on slow or unreachable backends.
|
|
63
|
+
- **Advisory in-process plugin (`.amp/plugins/ory.ts`).** Discovered by Amp's Bun runtime, it handles session-start auth (advisory — it cannot hard-block at session start) and post-tool audit tracing.
|
|
64
|
+
|
|
65
|
+
The phases map as:
|
|
66
|
+
|
|
67
|
+
- **Session start** (in-process plugin) — run the user and agent auth gates; advisory.
|
|
68
|
+
- **Per-tool decision** (delegate) — check the tool against Ory Permissions; in enforce mode a deny exits `2` (reject), in observe mode it records a `permission.observe_deny` span and allows (exit `0`).
|
|
69
|
+
- **Post-tool** (in-process plugin) — trace-only audit spans.
|
|
70
|
+
|
|
71
|
+
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.
|
|
72
|
+
|
|
73
|
+
## Agent security
|
|
74
|
+
|
|
75
|
+
Once pointed at an Ory project (local or hosted), Amp's session and every tool call can be governed by Ory.
|
|
76
|
+
|
|
77
|
+
- **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 Amp process (the **agent**) gets its own OAuth2 identity via [Dynamic Client Registration (RFC 7591)](https://datatracker.ietf.org/doc/html/rfc7591) on first run.
|
|
78
|
+
- **Authorization.** Before any tool runs, the delegate checks [Ory Permissions](https://www.ory.com/docs/keto) (Zanzibar-style relations) against the user's subject and rejects on `deny`.
|
|
79
|
+
- **Audit.** Every decision is recorded as a structured trace span (NDJSON file and/or OTLP export).
|
|
80
|
+
|
|
81
|
+
### Permission modes: observe → enforce
|
|
82
|
+
|
|
83
|
+
After install the integration 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.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Grant the current user `use` on every built-in tool (idempotent):
|
|
87
|
+
npx -y -p @ory/amp ory-amp permissions bootstrap
|
|
88
|
+
|
|
89
|
+
# See allowed/denied per tool:
|
|
90
|
+
npx -y -p @ory/amp ory-amp permissions status
|
|
91
|
+
|
|
92
|
+
# Turn on hard blocking once the observe-mode logs look right:
|
|
93
|
+
npx -y -p @ory/amp ory-amp permissions enforce
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Switch back any time with `permissions observe`. To disable Ory entirely (audit logging only), run `ory-amp configure --audit-only`.
|
|
97
|
+
|
|
98
|
+
## Pointing at a real Ory project
|
|
99
|
+
|
|
100
|
+
The Quickstart uses the local stack. To point at a hosted [Ory Network](https://console.ory.sh) project:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npx -y -p @ory/amp ory-amp configure \
|
|
104
|
+
--project-url https://<id>.projects.oryapis.com \
|
|
105
|
+
--oauth2-client-id <public OAuth2 client id>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`--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`.
|
|
109
|
+
|
|
110
|
+
Without configuration the plugin still loads cleanly and runs in **pass-through mode**: skills work, but nothing is blocked.
|
|
111
|
+
|
|
112
|
+
## Links
|
|
113
|
+
|
|
114
|
+
- [Ory documentation](https://www.ory.com/docs/)
|
|
115
|
+
- [Amp documentation](https://ampcode.com)
|
|
116
|
+
- [Repo README](../../README.md) and [AGENTS.md](../../AGENTS.md) — full env-var and permission-mode reference
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
Apache-2.0
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Materialize / clean the Ory skill catalog and command playbooks for the
|
|
3
|
+
* Amp plugin.
|
|
4
|
+
*
|
|
5
|
+
* Amp discovers project skills under `<projectDir>/.agents/skills/<name>/SKILL.md`
|
|
6
|
+
* (note `.agents`, not `.amp`; the amp-specific path is the user-global
|
|
7
|
+
* `~/.config/amp/skills/`). It has no project-scoped slash-command system, so
|
|
8
|
+
* the local-stack commands (local-up, local-down, temporal-up) are wrapped
|
|
9
|
+
* with `commandToSkill` and written alongside the guide skills as
|
|
10
|
+
* user-invocable skills — the same approach Codex and OpenClaw use.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Write all Ory guide skills plus the command playbooks (wrapped as
|
|
14
|
+
* skills) under `<projectDir>/.agents/skills/`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function installAmpOryAssets(projectDir: string): void;
|
|
17
|
+
/** Remove every skill + command-skill this plugin installed. */
|
|
18
|
+
export declare function uninstallAmpOryAssets(projectDir: string): void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Materialize / clean the Ory skill catalog and command playbooks for the
|
|
4
|
+
* Amp plugin.
|
|
5
|
+
*
|
|
6
|
+
* Amp discovers project skills under `<projectDir>/.agents/skills/<name>/SKILL.md`
|
|
7
|
+
* (note `.agents`, not `.amp`; the amp-specific path is the user-global
|
|
8
|
+
* `~/.config/amp/skills/`). It has no project-scoped slash-command system, so
|
|
9
|
+
* the local-stack commands (local-up, local-down, temporal-up) are wrapped
|
|
10
|
+
* with `commandToSkill` and written alongside the guide skills as
|
|
11
|
+
* user-invocable skills — the same approach Codex and OpenClaw use.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.installAmpOryAssets = installAmpOryAssets;
|
|
48
|
+
exports.uninstallAmpOryAssets = uninstallAmpOryAssets;
|
|
49
|
+
const fs = __importStar(require("node:fs"));
|
|
50
|
+
const path = __importStar(require("node:path"));
|
|
51
|
+
const argus_1 = require("@ory/argus");
|
|
52
|
+
const RENDER_OPTS = {
|
|
53
|
+
binName: "ory-amp",
|
|
54
|
+
packageName: "@ory/amp",
|
|
55
|
+
};
|
|
56
|
+
/** Amp's project skills discovery directory: `<projectDir>/.agents/skills`. */
|
|
57
|
+
function skillsRoot(projectDir) {
|
|
58
|
+
return path.join(projectDir, ".agents", "skills");
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Write all Ory guide skills plus the command playbooks (wrapped as
|
|
62
|
+
* skills) under `<projectDir>/.agents/skills/`.
|
|
63
|
+
*/
|
|
64
|
+
function installAmpOryAssets(projectDir) {
|
|
65
|
+
const skillsDir = skillsRoot(projectDir);
|
|
66
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
67
|
+
const guideSkills = (0, argus_1.renderOrySkills)("amp", RENDER_OPTS);
|
|
68
|
+
const commandSkills = (0, argus_1.renderOryCommands)("amp", RENDER_OPTS).map(argus_1.commandToSkill);
|
|
69
|
+
(0, argus_1.writeSkillTree)(skillsDir, [...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 — leave the dir if removal fails. */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Remove every skill + command-skill this plugin installed. */
|
|
82
|
+
function uninstallAmpOryAssets(projectDir) {
|
|
83
|
+
const skillsDir = skillsRoot(projectDir);
|
|
84
|
+
if (!fs.existsSync(skillsDir))
|
|
85
|
+
return;
|
|
86
|
+
(0, argus_1.removeSkillDirs)(skillsDir, argus_1.ORY_SKILL_NAMES);
|
|
87
|
+
(0, argus_1.removeSkillDirs)(skillsDir, argus_1.ORY_COMMAND_SKILL_NAMES);
|
|
88
|
+
safeRmEmpty(skillsDir);
|
|
89
|
+
// Clean up the parent `.agents` dir if we emptied it.
|
|
90
|
+
safeRmEmpty(path.dirname(skillsDir));
|
|
91
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Main CLI for @ory/amp.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx ory-amp install [--project-dir <path>] Register delegate + in-process plugin
|
|
7
|
+
* npx ory-amp uninstall [--project-dir <path>] Remove Ory entries
|
|
8
|
+
* npx ory-amp configure Set or view Ory credentials
|
|
9
|
+
* npx ory-amp setup [--project-dir <path>] Write config directly (fallback)
|
|
10
|
+
* npx ory-amp status Show plugin status
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Main CLI for @ory/amp.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx ory-amp install [--project-dir <path>] Register delegate + in-process plugin
|
|
8
|
+
* npx ory-amp uninstall [--project-dir <path>] Remove Ory entries
|
|
9
|
+
* npx ory-amp configure Set or view Ory credentials
|
|
10
|
+
* npx ory-amp setup [--project-dir <path>] Write config directly (fallback)
|
|
11
|
+
* npx ory-amp status Show plugin status
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
48
|
+
const os = __importStar(require("node:os"));
|
|
49
|
+
const fs = __importStar(require("node:fs"));
|
|
50
|
+
const argus_1 = require("@ory/argus");
|
|
51
|
+
function main() {
|
|
52
|
+
const [command, ...args] = process.argv.slice(2);
|
|
53
|
+
switch (command) {
|
|
54
|
+
case "install":
|
|
55
|
+
install(args);
|
|
56
|
+
postInstallPermissions("ory-amp", "amp").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-amp", args);
|
|
66
|
+
break;
|
|
67
|
+
case "agent":
|
|
68
|
+
(0, argus_1.runAgentCommand)("ory-amp", 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-amp", "amp", args).then((code) => process.exit(code), (err) => {
|
|
75
|
+
console.error(err.message ?? err);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
break;
|
|
79
|
+
case "setup":
|
|
80
|
+
require("./setup.js");
|
|
81
|
+
break;
|
|
82
|
+
case "status":
|
|
83
|
+
status(args).then(() => process.exit(0), (err) => {
|
|
84
|
+
console.error(err.message ?? err);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
});
|
|
87
|
+
break;
|
|
88
|
+
case "local":
|
|
89
|
+
(0, argus_1.runLocalCommand)("ory-amp", args).catch((err) => {
|
|
90
|
+
console.error(err.message ?? err);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
});
|
|
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
|
+
/**
|
|
107
|
+
* Amp has no native plugin-registration CLI, so install delegates to the
|
|
108
|
+
* setup writer, which registers the permission delegate in Amp's settings
|
|
109
|
+
* and writes the in-process plugin loader at `.amp/plugins/ory.ts`.
|
|
110
|
+
*/
|
|
111
|
+
function install(args) {
|
|
112
|
+
process.argv = ["node", "setup.js", ...args];
|
|
113
|
+
require("./setup.js");
|
|
114
|
+
(0, argus_1.printEnvHelp)("ory-amp");
|
|
115
|
+
}
|
|
116
|
+
function uninstall(args) {
|
|
117
|
+
process.argv = ["node", "setup.js", "--uninstall", ...args];
|
|
118
|
+
require("./setup.js");
|
|
119
|
+
}
|
|
120
|
+
async function postInstallPermissions(binName, harness) {
|
|
121
|
+
const bootstrapped = await (0, argus_1.maybeAutoBootstrap)(binName, harness);
|
|
122
|
+
(0, argus_1.printPermissionsOnboardingHelp)(binName, harness, {
|
|
123
|
+
bootstrappedAutomatically: bootstrapped,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function parseProjectDir(args) {
|
|
127
|
+
const idx = args.indexOf("--project-dir");
|
|
128
|
+
if (idx !== -1 && args[idx + 1])
|
|
129
|
+
return args[idx + 1];
|
|
130
|
+
return process.cwd();
|
|
131
|
+
}
|
|
132
|
+
async function status(args) {
|
|
133
|
+
const projectDir = parseProjectDir(args);
|
|
134
|
+
// Setup writes the delegate to Amp's user-global settings file (the
|
|
135
|
+
// `--settings-file` default); the in-process loader lives under the
|
|
136
|
+
// project's `.amp/plugins/`.
|
|
137
|
+
const settingsPath = path.join(os.homedir(), ".config", "amp", "settings.json");
|
|
138
|
+
const pluginFile = path.join(projectDir, ".amp", "plugins", "ory.ts");
|
|
139
|
+
await (0, argus_1.runStatusCommand)("ory-amp", "amp", {
|
|
140
|
+
title: "Amp",
|
|
141
|
+
printPluginSection: () => {
|
|
142
|
+
console.log("Permission delegate & plugin:");
|
|
143
|
+
console.log(` Settings file: ${settingsPath}`);
|
|
144
|
+
let delegateRegistered = false;
|
|
145
|
+
try {
|
|
146
|
+
if (fs.existsSync(settingsPath)) {
|
|
147
|
+
const raw = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
148
|
+
const perms = Array.isArray(raw["amp.permissions"])
|
|
149
|
+
? raw["amp.permissions"]
|
|
150
|
+
: [];
|
|
151
|
+
delegateRegistered = perms.some((p) => p?.to === "ory-amp-permission");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
/* ignore parse errors */
|
|
156
|
+
}
|
|
157
|
+
console.log(` Delegate: ${delegateRegistered ? "registered (ory-amp-permission)" : "NOT registered"}`);
|
|
158
|
+
console.log(` In-process plugin: ${fs.existsSync(pluginFile) ? `installed (${pluginFile})` : "not installed"}`);
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function help() {
|
|
163
|
+
console.log(`
|
|
164
|
+
ory-amp — Ory plugin for Amp (Sourcegraph's coding agent)
|
|
165
|
+
|
|
166
|
+
Usage:
|
|
167
|
+
npx -y -p @ory/amp ory-amp <command> [options]
|
|
168
|
+
|
|
169
|
+
Commands:
|
|
170
|
+
install [--project-dir <path>] Register the permission delegate and in-process plugin
|
|
171
|
+
uninstall [--project-dir <path>] Remove the Ory delegate, plugin, and skills
|
|
172
|
+
configure Set or view Ory project URL and API key
|
|
173
|
+
agent <status|unregister> Manage the agent's OAuth2 (DCR) identity
|
|
174
|
+
permissions <cmd> Manage permission mode and tool permissions
|
|
175
|
+
(status, bootstrap, observe, enforce)
|
|
176
|
+
setup [--project-dir <path>] Write config directly (fallback)
|
|
177
|
+
status [--project-dir <path>] Show plugin configuration and status
|
|
178
|
+
local <cmd> Manage local Ory dev environment
|
|
179
|
+
(up, down, status, seed, logs, env, configure, reset)
|
|
180
|
+
|
|
181
|
+
After installing, the Ory integration runs in two parts:
|
|
182
|
+
Permission delegate (ory-amp-permission) Authorize each tool call (blocking)
|
|
183
|
+
In-process plugin (.amp/plugins/ory.ts) Session auth + audit tracing
|
|
184
|
+
|
|
185
|
+
Examples:
|
|
186
|
+
npx -y -p @ory/amp ory-amp install
|
|
187
|
+
npx -y -p @ory/amp ory-amp configure --project-url https://<id>.projects.oryapis.com
|
|
188
|
+
npx -y -p @ory/amp ory-amp permissions bootstrap
|
|
189
|
+
npx -y -p @ory/amp ory-amp status
|
|
190
|
+
`);
|
|
191
|
+
}
|
|
192
|
+
main();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Setup CLI for the Ory Amp plugin.
|
|
4
|
+
*
|
|
5
|
+
* Performs two installs:
|
|
6
|
+
* (a) Registers the permission delegate in Amp's settings under
|
|
7
|
+
* `amp.permissions` as `{ tool: "*", action: "delegate",
|
|
8
|
+
* to: "ory-amp-permission" }` — this is the blocking gate.
|
|
9
|
+
* (b) Writes the in-process plugin loader at
|
|
10
|
+
* `<projectDir>/.amp/plugins/ory.ts` so Amp's Bun TypeScript runtime
|
|
11
|
+
* discovers it for session auth + audit tracing.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* npx ory-amp-setup # Auto-detect and configure
|
|
15
|
+
* npx ory-amp-setup --project-dir /path/to/repo
|
|
16
|
+
* npx ory-amp-setup --print # Print config to stdout without writing
|
|
17
|
+
* npx ory-amp-setup --uninstall # Remove Ory entries from settings + plugins
|
|
18
|
+
*
|
|
19
|
+
* Verified against the installed `amp` binary: the default settings file is
|
|
20
|
+
* `~/.config/amp/settings.json` (the `--settings-file` default), with a flat
|
|
21
|
+
* top-level `amp.permissions` array of `{ tool, action, to }`. Amp also ships
|
|
22
|
+
* a native `amp permissions add <action> <tool> --to <program>` command (with
|
|
23
|
+
* `-w/--workspace` for project scope); the equivalent registration is:
|
|
24
|
+
*
|
|
25
|
+
* amp permissions add delegate '*' --to ory-amp-permission
|
|
26
|
+
*
|
|
27
|
+
* This setup writes the same entry into settings.json directly so the flow
|
|
28
|
+
* works without shelling out. In-process plugins are discovered as direct
|
|
29
|
+
* `*.ts` files under `.amp/plugins/` (project) or `~/.config/amp/plugins/`
|
|
30
|
+
* (system), executed by Bun's TypeScript runtime.
|
|
31
|
+
*/
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Setup CLI for the Ory Amp plugin.
|
|
5
|
+
*
|
|
6
|
+
* Performs two installs:
|
|
7
|
+
* (a) Registers the permission delegate in Amp's settings under
|
|
8
|
+
* `amp.permissions` as `{ tool: "*", action: "delegate",
|
|
9
|
+
* to: "ory-amp-permission" }` — this is the blocking gate.
|
|
10
|
+
* (b) Writes the in-process plugin loader at
|
|
11
|
+
* `<projectDir>/.amp/plugins/ory.ts` so Amp's Bun TypeScript runtime
|
|
12
|
+
* discovers it for session auth + audit tracing.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* npx ory-amp-setup # Auto-detect and configure
|
|
16
|
+
* npx ory-amp-setup --project-dir /path/to/repo
|
|
17
|
+
* npx ory-amp-setup --print # Print config to stdout without writing
|
|
18
|
+
* npx ory-amp-setup --uninstall # Remove Ory entries from settings + plugins
|
|
19
|
+
*
|
|
20
|
+
* Verified against the installed `amp` binary: the default settings file is
|
|
21
|
+
* `~/.config/amp/settings.json` (the `--settings-file` default), with a flat
|
|
22
|
+
* top-level `amp.permissions` array of `{ tool, action, to }`. Amp also ships
|
|
23
|
+
* a native `amp permissions add <action> <tool> --to <program>` command (with
|
|
24
|
+
* `-w/--workspace` for project scope); the equivalent registration is:
|
|
25
|
+
*
|
|
26
|
+
* amp permissions add delegate '*' --to ory-amp-permission
|
|
27
|
+
*
|
|
28
|
+
* This setup writes the same entry into settings.json directly so the flow
|
|
29
|
+
* works without shelling out. In-process plugins are discovered as direct
|
|
30
|
+
* `*.ts` files under `.amp/plugins/` (project) or `~/.config/amp/plugins/`
|
|
31
|
+
* (system), executed by Bun's TypeScript runtime.
|
|
32
|
+
*/
|
|
33
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
34
|
+
if (k2 === undefined) k2 = k;
|
|
35
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
36
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
37
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
38
|
+
}
|
|
39
|
+
Object.defineProperty(o, k2, desc);
|
|
40
|
+
}) : (function(o, m, k, k2) {
|
|
41
|
+
if (k2 === undefined) k2 = k;
|
|
42
|
+
o[k2] = m[k];
|
|
43
|
+
}));
|
|
44
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
45
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
46
|
+
}) : function(o, v) {
|
|
47
|
+
o["default"] = v;
|
|
48
|
+
});
|
|
49
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
50
|
+
var ownKeys = function(o) {
|
|
51
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
52
|
+
var ar = [];
|
|
53
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
54
|
+
return ar;
|
|
55
|
+
};
|
|
56
|
+
return ownKeys(o);
|
|
57
|
+
};
|
|
58
|
+
return function (mod) {
|
|
59
|
+
if (mod && mod.__esModule) return mod;
|
|
60
|
+
var result = {};
|
|
61
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
62
|
+
__setModuleDefault(result, mod);
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
})();
|
|
66
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
67
|
+
const fs = __importStar(require("node:fs"));
|
|
68
|
+
const os = __importStar(require("node:os"));
|
|
69
|
+
const path = __importStar(require("node:path"));
|
|
70
|
+
const argus_1 = require("@ory/argus");
|
|
71
|
+
const assets_js_1 = require("./assets.js");
|
|
72
|
+
const PACKAGE_NAME = "@ory/amp";
|
|
73
|
+
const DELEGATE_BIN = "ory-amp-permission";
|
|
74
|
+
/** Direct *.ts loader Amp's Bun runtime discovers under .amp/plugins/. */
|
|
75
|
+
const PLUGIN_FILE = "ory.ts";
|
|
76
|
+
/**
|
|
77
|
+
* Read the plugin's own version, walking up from dist/cli to the package
|
|
78
|
+
* root. Used to version-pin the npx invocation written into the in-process
|
|
79
|
+
* plugin loader and any delegate references.
|
|
80
|
+
*/
|
|
81
|
+
function readPackageVersion() {
|
|
82
|
+
const pkgPath = path.resolve(__dirname, "..", "..", "package.json");
|
|
83
|
+
const raw = fs.readFileSync(pkgPath, "utf-8");
|
|
84
|
+
const parsed = JSON.parse(raw);
|
|
85
|
+
if (!parsed.version) {
|
|
86
|
+
throw new Error(`Could not read version from ${pkgPath}`);
|
|
87
|
+
}
|
|
88
|
+
return parsed.version;
|
|
89
|
+
}
|
|
90
|
+
function getSettingsPath(_args) {
|
|
91
|
+
// Verified: Amp's default settings file is the user-global
|
|
92
|
+
// ~/.config/amp/settings.json (the `--settings-file` default). Amp's own
|
|
93
|
+
// permission entries are global by default; `-w/--workspace` would scope
|
|
94
|
+
// them to the project. We follow the verified default.
|
|
95
|
+
return path.join(os.homedir(), ".config", "amp", "settings.json");
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The `amp.permissions` delegate entry that routes every tool ("*") through
|
|
99
|
+
* the Ory permission helper. Amp invokes the named program with the tool
|
|
100
|
+
* params on stdin and reads the decision from the exit code.
|
|
101
|
+
*
|
|
102
|
+
* Verified against the binary: a flat top-level `amp.permissions` array of
|
|
103
|
+
* `{ tool, action, to }` entries.
|
|
104
|
+
*/
|
|
105
|
+
function delegateEntry() {
|
|
106
|
+
return { tool: "*", action: "delegate", to: DELEGATE_BIN };
|
|
107
|
+
}
|
|
108
|
+
function isOryDelegate(entry) {
|
|
109
|
+
return (typeof entry === "object" &&
|
|
110
|
+
entry !== null &&
|
|
111
|
+
entry.action === "delegate" &&
|
|
112
|
+
entry.to === DELEGATE_BIN);
|
|
113
|
+
}
|
|
114
|
+
function mergePermissions(existing) {
|
|
115
|
+
const merged = { ...existing };
|
|
116
|
+
const perms = (Array.isArray(merged["amp.permissions"])
|
|
117
|
+
? merged["amp.permissions"]
|
|
118
|
+
: []).filter((e) => !isOryDelegate(e));
|
|
119
|
+
perms.push(delegateEntry());
|
|
120
|
+
merged["amp.permissions"] = perms;
|
|
121
|
+
return merged;
|
|
122
|
+
}
|
|
123
|
+
function removePermissions(existing) {
|
|
124
|
+
const merged = { ...existing };
|
|
125
|
+
const perms = (Array.isArray(merged["amp.permissions"])
|
|
126
|
+
? merged["amp.permissions"]
|
|
127
|
+
: []).filter((e) => !isOryDelegate(e));
|
|
128
|
+
if (perms.length === 0) {
|
|
129
|
+
delete merged["amp.permissions"];
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
merged["amp.permissions"] = perms;
|
|
133
|
+
}
|
|
134
|
+
return merged;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The in-process plugin loader written at `.amp/plugins/ory.ts`. Amp's Bun
|
|
138
|
+
* TypeScript runtime discovers direct `*.ts` files under `.amp/plugins/`, so
|
|
139
|
+
* this re-exports the installed `@ory/amp` package's default-exported plugin
|
|
140
|
+
* factory. Version-pinned in the header so the on-disk loader is traceable to
|
|
141
|
+
* the package the settings were written against.
|
|
142
|
+
*/
|
|
143
|
+
function pluginLoaderSource(version) {
|
|
144
|
+
return (`// Generated by ${PACKAGE_NAME}@${version} — do not edit by hand.\n` +
|
|
145
|
+
`// Re-exports the Ory in-process plugin for Amp's .amp/plugins/ loader.\n` +
|
|
146
|
+
`export { default } from "${PACKAGE_NAME}";\n`);
|
|
147
|
+
}
|
|
148
|
+
function getPluginFile(args) {
|
|
149
|
+
return path.join(args.projectDir, ".amp", "plugins", PLUGIN_FILE);
|
|
150
|
+
}
|
|
151
|
+
function main() {
|
|
152
|
+
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
153
|
+
(0, argus_1.printSetupHelp)("ory-amp-setup", "Amp");
|
|
154
|
+
process.exit(0);
|
|
155
|
+
}
|
|
156
|
+
const args = (0, argus_1.parseSetupArgs)();
|
|
157
|
+
const version = readPackageVersion();
|
|
158
|
+
if (args.print) {
|
|
159
|
+
console.log(JSON.stringify({ "amp.permissions": [delegateEntry()] }, null, 2));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const settingsPath = getSettingsPath(args);
|
|
163
|
+
const pluginFile = getPluginFile(args);
|
|
164
|
+
const skillsDir = path.join(args.projectDir, ".agents", "skills");
|
|
165
|
+
const existing = (0, argus_1.readJsonFile)(settingsPath);
|
|
166
|
+
if (args.uninstall) {
|
|
167
|
+
if (fs.existsSync(settingsPath)) {
|
|
168
|
+
const updated = removePermissions(existing);
|
|
169
|
+
(0, argus_1.writeJsonFile)(settingsPath, updated);
|
|
170
|
+
console.log(`Removed Ory permission delegate from ${settingsPath}`);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
console.log("No Amp settings file found. Nothing to remove from settings.");
|
|
174
|
+
}
|
|
175
|
+
if (fs.existsSync(pluginFile)) {
|
|
176
|
+
fs.rmSync(pluginFile, { force: true });
|
|
177
|
+
console.log(`Removed Ory in-process plugin from ${pluginFile}`);
|
|
178
|
+
}
|
|
179
|
+
(0, assets_js_1.uninstallAmpOryAssets)(args.projectDir);
|
|
180
|
+
console.log(`Removed Ory skills from ${skillsDir}`);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// (a) Register the permission delegate (blocking gate).
|
|
184
|
+
const merged = mergePermissions(existing);
|
|
185
|
+
(0, argus_1.writeJsonFile)(settingsPath, merged);
|
|
186
|
+
// (b) Write the in-process plugin loader (auth + tracing).
|
|
187
|
+
fs.mkdirSync(path.dirname(pluginFile), { recursive: true });
|
|
188
|
+
fs.writeFileSync(pluginFile, pluginLoaderSource(version));
|
|
189
|
+
// (c) Materialize the Ory skill catalog.
|
|
190
|
+
(0, assets_js_1.installAmpOryAssets)(args.projectDir);
|
|
191
|
+
console.log(`Ory permission delegate registered in ${settingsPath}`);
|
|
192
|
+
console.log(` Delegate: ${DELEGATE_BIN} (tool: "*", action: delegate)`);
|
|
193
|
+
console.log(`Ory in-process plugin written to ${pluginFile}`);
|
|
194
|
+
console.log(`Ory skills installed under ${skillsDir}`);
|
|
195
|
+
(0, argus_1.printNextSteps)("Amp", "npx ory-amp-setup --uninstall");
|
|
196
|
+
}
|
|
197
|
+
main();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createOryPlugin } from "./plugin.js";
|
|
2
|
+
export { createOryPlugin };
|
|
3
|
+
export type { CreateOryPluginDeps } from "./plugin.js";
|
|
4
|
+
export { decideToolPermission, extractToolName, type DelegateDecision } from "./permission.js";
|
|
5
|
+
export type { AmpPlugin, AmpPluginApi, AmpPluginEventContext, AmpEvent, AmpEventMap, AmpDelegateInput, AmpToolCallResult, AmpSessionStartEvent, AmpToolResultEvent, } from "./types.js";
|
|
6
|
+
declare const _default: import("./types.js").AmpPlugin;
|
|
7
|
+
export default _default;
|