@ory/opencode 0.1.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 ADDED
@@ -0,0 +1,100 @@
1
+ # Ory Agent Plugin: OpenCode
2
+
3
+ [Ory](https://ory.com) bundled into [OpenCode](https://opencode.ai): skills and markdown 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 OpenCode runs.
4
+
5
+ ## Install
6
+
7
+ OpenCode loads plugins listed in the `plugin` array of `opencode.json` and fetches them from npm on next launch:
8
+
9
+ ```json
10
+ { "plugin": ["@ory/opencode"] }
11
+ ```
12
+
13
+ Or use the Ory installer to register the plugin **and** the Ory MCP server in one step, with no prior `npm install` required:
14
+
15
+ ```bash
16
+ npx @ory/opencode install # writes opencode.json + MCP config
17
+ npx @ory/opencode install --project-dir <path>
18
+ npx @ory/opencode uninstall
19
+ ```
20
+
21
+ Either flow drops the Ory skill catalog into `.opencode/skills/` and the local-stack commands into `.opencode/commands/ory/`.
22
+
23
+ ## Developer experience
24
+
25
+ This plugin is a productivity layer for Ory itself. You don't need a real Ory project, an account, or any prior Ory experience to start using it.
26
+
27
+ ### Skills for scaffolding Ory into your application
28
+
29
+ Ask OpenCode to add Ory auth to your codebase. Each skill is a vetted, end-to-end playbook:
30
+
31
+ - **`ory-auth-setup`**: full project setup. Install the Ory CLI, create an Ory Network project, add Ory Elements, configure the SDK, build the auth pages, wire session middleware.
32
+ - **`ory-login-flow`**: login, registration, recovery, verification, and settings pages with Ory Elements. Next.js App Router and React SPA variants.
33
+ - **`ory-social-login`**: Google, GitHub, Apple, Microsoft, Discord, and other OIDC providers with Jsonnet data mappers.
34
+ - **`ory-local-dev`**: drive the local Ory stack (below) from within OpenCode to prototype and test against without a remote project.
35
+
36
+ Skills are versioned with the plugin so guidance stays in sync as Ory APIs evolve.
37
+
38
+ ### Ory MCP server
39
+
40
+ Bundled and registered by the Ory installer. It exposes the Ory CLI and the Ory Network REST API as MCP tools so OpenCode can manage identities, OAuth2 clients, projects, permission tuples, and configuration without ever leaving the chat. Useful for seeding test data, verifying a scaffolded integration, or running one-off admin tasks.
41
+
42
+ ### Local Ory stack in one command
43
+
44
+ ```
45
+ /ory:local-up # start a local Ory instance in Docker
46
+ /ory:local-down # tear it all down
47
+ ```
48
+
49
+ `local up` runs a local Ory instance in Docker, covering everything the plugin and your scaffolded application need. It also brings up a login UI on `:3000` and Jaeger on `:16686`, all reachable through `http://localhost:4000`. A test user identity is seeded and the credentials are printed for you. Use it to:
50
+
51
+ - **Learn Ory hands-on** without signing up for a hosted project.
52
+ - **Prototype** flows (login, social, MFA, recovery, permission tuples) against a real Ory backend in your local dev loop.
53
+ - **Test** an auth integration end-to-end before pushing anything to a real environment.
54
+ - **Develop** your application against the same identity, OAuth2, and permission surfaces you'll ship with.
55
+
56
+ Point `ORY_PROJECT_URL` at `http://localhost:4000` (or run `npx ory-opencode configure`) and the security features below run against the local stack.
57
+
58
+ ## Configure
59
+
60
+ ```bash
61
+ npx ory-opencode configure --project-url https://<id>.projects.oryapis.com --api-key ory_pat_...
62
+ ```
63
+
64
+ Config is saved to `~/.config/ory-agent-plugins/config.json` and shared across every Ory agent plugin on the machine. Without it the plugin still loads cleanly and runs in **pass-through mode**: skills and commands work, but nothing is blocked.
65
+
66
+ ## Agent security (Argus)
67
+
68
+ Once the plugin is pointed at an Ory project (local or hosted), OpenCode's session and every tool call are governed by Ory.
69
+
70
+ - **Authentication.** Two identities. The human at the keyboard (the **user**) authenticates interactively via Ory Identities when `ORY_AUTH_GATE=1`; tokens refresh and a `user.auth` audit span is emitted at every session start. The OpenCode process (the **agent**) gets its own OAuth2 identity, self-registered via Dynamic Client Registration on first run.
71
+ - **Authorization.** When OpenCode prompts for permission to use a tool, the plugin checks Ory Permissions against the user's subject and returns `allow` / `deny`. A parallel pre-tool check is recorded as part of the audit trail. MCP tool calls additionally get a server-level check.
72
+ - **Audit.** Every decision (allow, deny, fallback) is recorded as a structured trace span: NDJSON file output and/or OTLP/HTTP export to Jaeger, Honeycomb, Grafana, and similar collectors. The user-to-agent delegation is written to Ory as a Zanzibar tuple so "agent X acting on behalf of user Y" stays queryable after tokens expire.
73
+
74
+ The plugin is **fail-open** on its own infrastructure failures (network errors, rate limits, missing config), so enforcement is only as strong as your tuples; grant explicit `invoke` relations for the tools each user should be able to run.
75
+
76
+ ## Programmatic usage
77
+
78
+ ```typescript
79
+ import { createOryPlugin } from "@ory/opencode";
80
+
81
+ const plugin = createOryPlugin();
82
+ ```
83
+
84
+ ## CLI
85
+
86
+ ```
87
+ npx ory-opencode install | uninstall [--project-dir <path>]
88
+ npx ory-opencode configure [--project-url <url>] [--api-key <key>] [--audit-only]
89
+ npx ory-opencode agent <status|unregister> Manage the agent's OAuth2 identity
90
+ npx ory-opencode local <up|down|status|seed|logs|env|configure|reset>
91
+ npx ory-opencode status [--project-dir <path>]
92
+ ```
93
+
94
+ ## Links
95
+
96
+ - [ory.com](https://ory.com)
97
+
98
+ ## License
99
+
100
+ Apache-2.0
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Materialize / clean the Ory skill catalog and slash commands for the
3
+ * OpenCode plugin. Skills land at `<projectDir>/.opencode/skills/<name>/SKILL.md`
4
+ * (OpenCode auto-discovers them) and commands at
5
+ * `<projectDir>/.opencode/commands/ory/<slug>.md` so they're exposed under
6
+ * the `ory` namespace.
7
+ */
8
+ export declare function installOpencodeOryAssets(projectDir: string): void;
9
+ export declare function uninstallOpencodeOryAssets(projectDir: string): void;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ /**
3
+ * Materialize / clean the Ory skill catalog and slash commands for the
4
+ * OpenCode plugin. Skills land at `<projectDir>/.opencode/skills/<name>/SKILL.md`
5
+ * (OpenCode auto-discovers them) and commands at
6
+ * `<projectDir>/.opencode/commands/ory/<slug>.md` so they're exposed under
7
+ * the `ory` namespace.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.installOpencodeOryAssets = installOpencodeOryAssets;
44
+ exports.uninstallOpencodeOryAssets = uninstallOpencodeOryAssets;
45
+ const fs = __importStar(require("node:fs"));
46
+ const path = __importStar(require("node:path"));
47
+ const argus_1 = require("@ory/argus");
48
+ const RENDER_OPTS = {
49
+ binName: "ory-opencode",
50
+ packageName: "@ory/opencode",
51
+ };
52
+ const CMD_SUBDIR = "ory";
53
+ function opencodeRoot(projectDir) {
54
+ return path.join(projectDir, ".opencode");
55
+ }
56
+ function installOpencodeOryAssets(projectDir) {
57
+ const root = opencodeRoot(projectDir);
58
+ fs.mkdirSync(root, { recursive: true });
59
+ const skillsDir = path.join(root, "skills");
60
+ fs.rmSync(skillsDir, { recursive: true, force: true });
61
+ (0, argus_1.writeSkillTree)(skillsDir, (0, argus_1.renderOrySkills)("opencode", RENDER_OPTS));
62
+ const cmdDir = path.join(root, "commands", CMD_SUBDIR);
63
+ fs.rmSync(cmdDir, { recursive: true, force: true });
64
+ fs.mkdirSync(cmdDir, { recursive: true });
65
+ for (const cmd of (0, argus_1.renderOryCommands)("opencode", RENDER_OPTS)) {
66
+ fs.writeFileSync(path.join(cmdDir, `${cmd.slug}.md`), (0, argus_1.commandToFrontmatterMarkdown)(cmd));
67
+ }
68
+ }
69
+ function safeRmEmpty(dir) {
70
+ try {
71
+ if (fs.existsSync(dir) && fs.readdirSync(dir).length === 0) {
72
+ fs.rmdirSync(dir);
73
+ }
74
+ }
75
+ catch {
76
+ /* ignore */
77
+ }
78
+ }
79
+ function uninstallOpencodeOryAssets(projectDir) {
80
+ const root = opencodeRoot(projectDir);
81
+ if (!fs.existsSync(root))
82
+ return;
83
+ const skillsDir = path.join(root, "skills");
84
+ if (fs.existsSync(skillsDir)) {
85
+ (0, argus_1.removeSkillDirs)(skillsDir, argus_1.ORY_SKILL_NAMES);
86
+ safeRmEmpty(skillsDir);
87
+ }
88
+ const cmdDir = path.join(root, "commands", CMD_SUBDIR);
89
+ if (fs.existsSync(cmdDir)) {
90
+ for (const slug of argus_1.ORY_COMMAND_SLUGS) {
91
+ const f = path.join(cmdDir, `${slug}.md`);
92
+ if (fs.existsSync(f))
93
+ fs.unlinkSync(f);
94
+ }
95
+ safeRmEmpty(cmdDir);
96
+ safeRmEmpty(path.join(root, "commands"));
97
+ }
98
+ safeRmEmpty(root);
99
+ }
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Main CLI for @ory/opencode.
4
+ *
5
+ * Usage:
6
+ * npx ory-opencode install [--project-dir <path>] Register plugin in opencode.json
7
+ * npx ory-opencode uninstall Remove plugin from opencode.json
8
+ * npx ory-opencode status Show plugin status
9
+ */
10
+ export {};
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * Main CLI for @ory/opencode.
5
+ *
6
+ * Usage:
7
+ * npx ory-opencode install [--project-dir <path>] Register plugin in opencode.json
8
+ * npx ory-opencode uninstall Remove plugin from opencode.json
9
+ * npx ory-opencode status Show plugin status
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ const path = __importStar(require("node:path"));
46
+ const fs = __importStar(require("node:fs"));
47
+ const argus_1 = require("@ory/argus");
48
+ const assets_js_1 = require("./assets.js");
49
+ function main() {
50
+ const [command, ...args] = process.argv.slice(2);
51
+ switch (command) {
52
+ case "install":
53
+ install(args);
54
+ break;
55
+ case "uninstall":
56
+ uninstall(args);
57
+ break;
58
+ case "configure":
59
+ (0, argus_1.runConfigureCommand)("ory-opencode", args);
60
+ break;
61
+ case "agent":
62
+ (0, argus_1.runAgentCommand)("ory-opencode", args).then((code) => process.exit(code), (err) => {
63
+ console.error(err.message ?? err);
64
+ process.exit(1);
65
+ });
66
+ break;
67
+ case "status":
68
+ status(args);
69
+ break;
70
+ case "local":
71
+ (0, argus_1.runLocalCommand)("ory-opencode", args).catch((err) => {
72
+ console.error(err.message ?? err);
73
+ process.exit(1);
74
+ });
75
+ break;
76
+ case "help":
77
+ case "--help":
78
+ case "-h":
79
+ case undefined:
80
+ help();
81
+ break;
82
+ default:
83
+ console.error(`Unknown command: ${command}`);
84
+ help();
85
+ process.exit(1);
86
+ }
87
+ }
88
+ function parseProjectDir(args) {
89
+ const idx = args.indexOf("--project-dir");
90
+ if (idx !== -1 && args[idx + 1])
91
+ return args[idx + 1];
92
+ return process.cwd();
93
+ }
94
+ const PLUGIN_MODULE = "@ory/opencode";
95
+ const MCP_SERVER_NAME = "ory";
96
+ const MCP_SERVER_COMMAND = [
97
+ "npx",
98
+ "--package=@ory/mcp-server",
99
+ "ory-mcp-server",
100
+ ];
101
+ function install(args) {
102
+ const projectDir = parseProjectDir(args);
103
+ const configPath = path.join(projectDir, "opencode.json");
104
+ const config = (0, argus_1.readJsonFile)(configPath);
105
+ const plugin = (config.plugin ?? []).filter((p) => (typeof p === "string" ? p : p[0]) !== PLUGIN_MODULE);
106
+ plugin.push(PLUGIN_MODULE);
107
+ config.plugin = plugin;
108
+ // OpenCode's config schema uses `mcp` (singular) with a `local` connector
109
+ // shape `{ type: "local", command: [...] }` — not the generic
110
+ // `mcpServers` / `{command, args}` form used by Claude Code.
111
+ const mcp = (config.mcp ?? {});
112
+ mcp[MCP_SERVER_NAME] = { type: "local", command: MCP_SERVER_COMMAND };
113
+ config.mcp = mcp;
114
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
115
+ (0, assets_js_1.installOpencodeOryAssets)(projectDir);
116
+ console.log(`Ory plugin and MCP server registered in ${configPath}`);
117
+ console.log(` Plugin: ${PLUGIN_MODULE}`);
118
+ console.log(` MCP server: ${MCP_SERVER_NAME} (${MCP_SERVER_COMMAND.join(" ")})`);
119
+ console.log(` Skills + commands installed under ${path.join(projectDir, ".opencode")}`);
120
+ (0, argus_1.printEnvHelp)("ory-opencode");
121
+ }
122
+ function uninstall(args) {
123
+ const projectDir = parseProjectDir(args);
124
+ const configPath = path.join(projectDir, "opencode.json");
125
+ if (!fs.existsSync(configPath)) {
126
+ console.log("No opencode.json found. Nothing to uninstall.");
127
+ return;
128
+ }
129
+ const config = (0, argus_1.readJsonFile)(configPath);
130
+ const plugin = (config.plugin ?? []).filter((p) => (typeof p === "string" ? p : p[0]) !== PLUGIN_MODULE);
131
+ if (plugin.length === 0) {
132
+ delete config.plugin;
133
+ }
134
+ else {
135
+ config.plugin = plugin;
136
+ }
137
+ const mcp = (config.mcp ?? {});
138
+ delete mcp[MCP_SERVER_NAME];
139
+ if (Object.keys(mcp).length === 0) {
140
+ delete config.mcp;
141
+ }
142
+ else {
143
+ config.mcp = mcp;
144
+ }
145
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
146
+ (0, assets_js_1.uninstallOpencodeOryAssets)(projectDir);
147
+ console.log(`Ory plugin and MCP server removed from ${configPath}`);
148
+ }
149
+ function status(args) {
150
+ const projectDir = parseProjectDir(args);
151
+ const configPath = path.join(projectDir, "opencode.json");
152
+ console.log("Ory Agent Plugin Status (OpenCode)");
153
+ console.log("===================================");
154
+ console.log("");
155
+ // OpenCode-specific: check plugin registration
156
+ const config = (0, argus_1.readJsonFile)(configPath);
157
+ const moduleName = "@ory/opencode";
158
+ const plugin = (config.plugin ?? []);
159
+ const oryPlugin = plugin.find((p) => (typeof p === "string" ? p : p[0]) === moduleName);
160
+ console.log("Configuration:");
161
+ console.log(` Config file: ${configPath}`);
162
+ console.log(` Plugin registered: ${oryPlugin ? "yes" : "NO"}`);
163
+ if (oryPlugin) {
164
+ console.log(` Module: ${typeof oryPlugin === "string" ? oryPlugin : oryPlugin[0]}`);
165
+ }
166
+ // Shared Ory config
167
+ console.log("");
168
+ console.log("Ory Configuration:");
169
+ const resolved = (0, argus_1.resolveConfig)();
170
+ const oryConfigPath = (0, argus_1.getConfigPath)();
171
+ console.log(` Config file: ${oryConfigPath}`);
172
+ console.log(` Project URL: ${resolved.projectUrl ?? "NOT SET"} [source: ${resolved.projectUrlSource}]`);
173
+ console.log(` API Key: ${resolved.apiKey ? "(set)" : "NOT SET"} [source: ${resolved.apiKeySource}]`);
174
+ (0, argus_1.printEnvironment)();
175
+ (0, argus_1.printLogTail)();
176
+ }
177
+ function help() {
178
+ console.log(`
179
+ ory-opencode — Ory plugin for OpenCode
180
+
181
+ Usage:
182
+ npx ory-opencode <command> [options]
183
+
184
+ Commands:
185
+ install [--project-dir <path>] Register the Ory plugin in opencode.json
186
+ uninstall [--project-dir <path>] Remove the Ory plugin from opencode.json
187
+ configure Set or view Ory project URL and API key
188
+ status [--project-dir <path>] Show plugin configuration and status
189
+ local <cmd> Manage local Ory dev environment (up, down, status, seed, ...)
190
+
191
+ After installing, the plugin hooks into these OpenCode lifecycle events:
192
+ session.created Verify agent session via Ory Identities
193
+ tool.execute.before Check tool permissions via Ory Permissions
194
+ tool.execute.after Audit log tool executions
195
+ permission.ask Authorize actions via Ory Permissions
196
+
197
+ Examples:
198
+ npx ory-opencode install # Register in current directory
199
+ npx ory-opencode status # Check configuration
200
+ npx ory-opencode uninstall # Remove plugin
201
+ `);
202
+ }
203
+ main();
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Setup CLI for the Ory OpenCode plugin.
4
+ *
5
+ * Usage:
6
+ * npx ory-opencode-setup # Auto-detect and configure
7
+ * npx ory-opencode-setup --project-dir /path/to/repo
8
+ * npx ory-opencode-setup --print # Print config to stdout without writing
9
+ * npx ory-opencode-setup --uninstall # Remove Ory plugin from config
10
+ */
11
+ export {};
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * Setup CLI for the Ory OpenCode plugin.
5
+ *
6
+ * Usage:
7
+ * npx ory-opencode-setup # Auto-detect and configure
8
+ * npx ory-opencode-setup --project-dir /path/to/repo
9
+ * npx ory-opencode-setup --print # Print config to stdout without writing
10
+ * npx ory-opencode-setup --uninstall # Remove Ory plugin from config
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 PLUGIN_MODULE = "@ory/opencode";
51
+ const MCP_SERVER_NAME = "ory";
52
+ const MCP_SERVER_COMMAND = [
53
+ "npx",
54
+ "--package=@ory/mcp-server",
55
+ "ory-mcp-server",
56
+ ];
57
+ // OpenCode's config schema uses `mcp` (singular) with a `local` connector
58
+ // shape `{ type: "local", command: [...] }`, not the generic `mcpServers`
59
+ // helper from core.
60
+ function mergeOpencodeMcp(existing) {
61
+ const merged = { ...existing };
62
+ const mcp = (merged.mcp ?? {});
63
+ mcp[MCP_SERVER_NAME] = { type: "local", command: MCP_SERVER_COMMAND };
64
+ merged.mcp = mcp;
65
+ return merged;
66
+ }
67
+ function removeOpencodeMcp(existing) {
68
+ const merged = { ...existing };
69
+ const mcp = (merged.mcp ?? {});
70
+ delete mcp[MCP_SERVER_NAME];
71
+ if (Object.keys(mcp).length === 0) {
72
+ delete merged.mcp;
73
+ }
74
+ else {
75
+ merged.mcp = mcp;
76
+ }
77
+ return merged;
78
+ }
79
+ function getConfigPath(args) {
80
+ return path.join(args.projectDir, "opencode.json");
81
+ }
82
+ function mergePlugin(existing) {
83
+ const merged = { ...existing };
84
+ const plugin = (merged.plugin ?? []).filter((p) => (typeof p === "string" ? p : p[0]) !== PLUGIN_MODULE);
85
+ plugin.push(PLUGIN_MODULE);
86
+ merged.plugin = plugin;
87
+ return merged;
88
+ }
89
+ function removePlugin(existing) {
90
+ const merged = { ...existing };
91
+ const plugin = (merged.plugin ?? []).filter((p) => (typeof p === "string" ? p : p[0]) !== PLUGIN_MODULE);
92
+ if (plugin.length === 0) {
93
+ delete merged.plugin;
94
+ }
95
+ else {
96
+ merged.plugin = plugin;
97
+ }
98
+ return merged;
99
+ }
100
+ function main() {
101
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
102
+ (0, argus_1.printSetupHelp)("ory-opencode-setup", "OpenCode");
103
+ process.exit(0);
104
+ }
105
+ const args = (0, argus_1.parseSetupArgs)();
106
+ if (args.print) {
107
+ console.log(JSON.stringify({ plugin: [PLUGIN_MODULE] }, null, 2));
108
+ return;
109
+ }
110
+ const configPath = getConfigPath(args);
111
+ const existing = (0, argus_1.readJsonFile)(configPath);
112
+ if (args.uninstall) {
113
+ if (!fs.existsSync(configPath)) {
114
+ console.log("No opencode.json found. Nothing to uninstall.");
115
+ return;
116
+ }
117
+ let updated = removePlugin(existing);
118
+ updated = removeOpencodeMcp(updated);
119
+ (0, argus_1.writeJsonFile)(configPath, updated);
120
+ (0, assets_js_1.uninstallOpencodeOryAssets)(args.projectDir);
121
+ console.log(`Removed Ory plugin and MCP server from ${configPath}`);
122
+ console.log(`Removed Ory skills and commands from ${path.join(args.projectDir, ".opencode")}`);
123
+ return;
124
+ }
125
+ let merged = mergePlugin(existing);
126
+ merged = mergeOpencodeMcp(merged);
127
+ (0, argus_1.writeJsonFile)(configPath, merged);
128
+ (0, assets_js_1.installOpencodeOryAssets)(args.projectDir);
129
+ console.log(`Ory plugin and MCP server configured in ${configPath}`);
130
+ console.log(`Ory skills and commands installed to ${path.join(args.projectDir, ".opencode")}`);
131
+ (0, argus_1.printNextSteps)("OpenCode", "npx ory-opencode-setup --uninstall");
132
+ }
133
+ main();
@@ -0,0 +1,4 @@
1
+ import { createOryPlugin } from "./plugin.js";
2
+ export { createOryPlugin };
3
+ declare const _default: import("./types.js").PluginModule;
4
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createOryPlugin = void 0;
4
+ const plugin_js_1 = require("./plugin.js");
5
+ Object.defineProperty(exports, "createOryPlugin", { enumerable: true, get: function () { return plugin_js_1.createOryPlugin; } });
6
+ // OpenCode loads plugins via ESM import() and expects either a default-exported
7
+ // Plugin function or a PluginModule object with a .server property.
8
+ // eslint-disable-next-line import/no-default-export
9
+ exports.default = (0, plugin_js_1.createOryPlugin)();
@@ -0,0 +1,24 @@
1
+ import { OryAgentClient, ensureUserAuthenticated, ensureAgentIdentity } from "@ory/argus";
2
+ import type { PluginModule } from "./types.js";
3
+ export interface CreateOryPluginDeps {
4
+ /** Test injection point for the user auth gate. */
5
+ authGate?: typeof ensureUserAuthenticated;
6
+ /** Test injection point for the agent identity gate. */
7
+ agentGate?: typeof ensureAgentIdentity;
8
+ }
9
+ /**
10
+ * Create the Ory plugin module for OpenCode.
11
+ *
12
+ * OpenCode plugins export { server: Plugin } where Plugin receives
13
+ * context and returns a Hooks object. Hook functions use a mutating
14
+ * (input, output) => Promise<void> pattern.
15
+ *
16
+ * The `server` factory has no return-value channel for blocking, so the
17
+ * auth gate runs in advisory mode at startup — it emits the audit span,
18
+ * refreshes tokens, and may launch a browser flow when interactive, but
19
+ * cannot prevent the session from starting.
20
+ */
21
+ export declare function createOryPlugin(clientOrConfig?: OryAgentClient | {
22
+ projectUrl: string;
23
+ apiKey?: string;
24
+ }, deps?: CreateOryPluginDeps): PluginModule;
package/dist/plugin.js ADDED
@@ -0,0 +1,382 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createOryPlugin = createOryPlugin;
4
+ const argus_1 = require("@ory/argus");
5
+ /**
6
+ * Create the Ory plugin module for OpenCode.
7
+ *
8
+ * OpenCode plugins export { server: Plugin } where Plugin receives
9
+ * context and returns a Hooks object. Hook functions use a mutating
10
+ * (input, output) => Promise<void> pattern.
11
+ *
12
+ * The `server` factory has no return-value channel for blocking, so the
13
+ * auth gate runs in advisory mode at startup — it emits the audit span,
14
+ * refreshes tokens, and may launch a browser flow when interactive, but
15
+ * cannot prevent the session from starting.
16
+ */
17
+ function createOryPlugin(clientOrConfig, deps = {}) {
18
+ return {
19
+ id: "ory",
20
+ server: async (_input, options) => {
21
+ const client = clientOrConfig instanceof argus_1.OryAgentClient
22
+ ? clientOrConfig
23
+ : clientOrConfig
24
+ ? new argus_1.OryAgentClient({
25
+ ...clientOrConfig,
26
+ harness: "opencode",
27
+ })
28
+ : argus_1.OryAgentClient.fromEnv("opencode");
29
+ // Log session status on startup (never blocks)
30
+ await verifyOnStartup(client, deps);
31
+ return {
32
+ config: createConfigHandler(client),
33
+ "chat.message": createChatMessageHandler(client),
34
+ "command.execute.before": createCommandBeforeHandler(client),
35
+ "permission.ask": createPermissionHandler(client, options),
36
+ "tool.execute.before": createToolBeforeHandler(client),
37
+ "tool.execute.after": createToolAfterHandler(client),
38
+ };
39
+ },
40
+ };
41
+ }
42
+ // ─── config ────────────────────────────────────────────────────────
43
+ function createConfigHandler(client) {
44
+ return async (_input) => {
45
+ client.logger.info("lifecycle.config", {
46
+ projectUrl: (0, argus_1.resolveConfig)().projectUrl,
47
+ auditOnly: (0, argus_1.resolveConfig)().auditOnly,
48
+ });
49
+ client.tracer.record("config.resolve", "ok", {
50
+ attributes: {
51
+ projectUrl: (0, argus_1.resolveConfig)().projectUrl,
52
+ auditOnly: (0, argus_1.resolveConfig)().auditOnly,
53
+ },
54
+ });
55
+ };
56
+ }
57
+ // ─── chat.message ──────────────────────────────────────────────────
58
+ function createChatMessageHandler(client) {
59
+ return async (input, _output) => {
60
+ client.tracer.setContext({
61
+ traceId: (0, argus_1.deriveTraceId)(input.sessionID),
62
+ sessionId: input.sessionID,
63
+ });
64
+ client.logger.info("lifecycle.chat_message", {
65
+ sessionID: input.sessionID,
66
+ messageID: input.messageID,
67
+ agent: input.agent,
68
+ providerID: input.model?.providerID,
69
+ modelID: input.model?.modelID,
70
+ });
71
+ client.tracer.record("user.prompt", "ok", {
72
+ attributes: {
73
+ messageID: input.messageID,
74
+ agent: input.agent,
75
+ providerID: input.model?.providerID,
76
+ modelID: input.model?.modelID,
77
+ },
78
+ });
79
+ };
80
+ }
81
+ // ─── command.execute.before ────────────────────────────────────────
82
+ function createCommandBeforeHandler(client) {
83
+ return async (input, _output) => {
84
+ if (input.sessionID) {
85
+ client.tracer.setContext({
86
+ traceId: (0, argus_1.deriveTraceId)(input.sessionID),
87
+ sessionId: input.sessionID,
88
+ });
89
+ }
90
+ client.logger.info("lifecycle.command_execute_before", {
91
+ command: input.name,
92
+ sessionID: input.sessionID,
93
+ });
94
+ client.tracer.record("hook.receive", "ok", {
95
+ attributes: {
96
+ event: "command.execute.before",
97
+ command: input.name,
98
+ },
99
+ });
100
+ };
101
+ }
102
+ // ─── Session verification on startup ────────────────────────────────
103
+ async function verifyOnStartup(client, deps) {
104
+ // Run the user auth gate. The OpenCode `server` factory has no block
105
+ // primitive, so allowBlock is false — the gate emits the user.auth
106
+ // audit span, refreshes tokens, and may prompt the user, but never
107
+ // prevents the session from starting. When ORY_AUTH_GATE is unset the
108
+ // gate is a no-op (mode === "disabled") and we fall through to legacy
109
+ // logging.
110
+ const userGate = deps.authGate ?? argus_1.ensureUserAuthenticated;
111
+ const decision = await userGate(client, {
112
+ binName: "ory-opencode",
113
+ harness: "opencode",
114
+ allowBlock: false,
115
+ });
116
+ // Resolve the agent identity (machine credentials). Never blocks;
117
+ // attaches the agent's bearer token to outgoing Ory API calls.
118
+ const agentGate = deps.agentGate ?? argus_1.ensureAgentIdentity;
119
+ await agentGate(client, { projectUrl: (0, argus_1.resolveConfig)().projectUrl, harness: "opencode" });
120
+ if (decision.mode !== "disabled") {
121
+ return;
122
+ }
123
+ const resolved = (0, argus_1.resolveConfig)();
124
+ if (resolved.auditOnly) {
125
+ client.logger.info("config.audit_only", {
126
+ message: "Audit-only mode enabled. Auth and permission checks are disabled.",
127
+ });
128
+ return;
129
+ }
130
+ if (!resolved.projectUrl) {
131
+ client.logger.warn("config.not_configured", {
132
+ message: "Ory plugin is not configured. Auth and permission checks are disabled. " +
133
+ "Run 'npx ory-opencode configure' to connect to an Ory project.",
134
+ });
135
+ return;
136
+ }
137
+ const sessionToken = process.env.ORY_SESSION_TOKEN;
138
+ const oauth2Token = process.env.ORY_OAUTH2_TOKEN;
139
+ if (sessionToken) {
140
+ try {
141
+ const session = await client.verifySession(sessionToken);
142
+ if (!session.active) {
143
+ client.logger.warn("session.inactive", {
144
+ message: "Ory session is not active. Re-authenticate to enable auth checks.",
145
+ });
146
+ }
147
+ }
148
+ catch (err) {
149
+ const oryErr = err;
150
+ client.logger.warn("session.verify_failed", {
151
+ code: isOryError(err) ? oryErr.code : "unknown",
152
+ message: err instanceof Error ? err.message : String(err),
153
+ });
154
+ }
155
+ return;
156
+ }
157
+ if (oauth2Token) {
158
+ try {
159
+ const tokenInfo = await client.introspectToken(oauth2Token);
160
+ if (!tokenInfo.active) {
161
+ client.logger.warn("oauth2.token_inactive", {
162
+ message: "Ory OAuth2 token is not active. Obtain a new token to enable auth checks.",
163
+ });
164
+ return;
165
+ }
166
+ client.logger.info("oauth2.session_authenticated", {
167
+ clientId: tokenInfo.clientId,
168
+ subject: tokenInfo.subject,
169
+ });
170
+ }
171
+ catch (err) {
172
+ const oryErr = err;
173
+ client.logger.warn("oauth2.introspect_failed", {
174
+ code: isOryError(err) ? oryErr.code : "unknown",
175
+ message: err instanceof Error ? err.message : String(err),
176
+ });
177
+ }
178
+ return;
179
+ }
180
+ client.logger.warn("session.no_credentials", {
181
+ message: "Neither ORY_SESSION_TOKEN nor ORY_OAUTH2_TOKEN is set. " +
182
+ "Skipping authentication.",
183
+ });
184
+ }
185
+ // ─── permission.ask ─────────────────────────────────────────────────
186
+ function createPermissionHandler(client, _options) {
187
+ return async (input, output) => {
188
+ client.logger.info("lifecycle.permission_ask", {
189
+ toolID: input.toolID,
190
+ title: input.title,
191
+ });
192
+ // In audit-only mode, defer to user prompt
193
+ if ((0, argus_1.resolveConfig)().auditOnly) {
194
+ return;
195
+ }
196
+ const subject = (0, argus_1.resolveUserSubject)(client, "agent:opencode");
197
+ const mcpTool = (0, argus_1.parseMcpToolGeneric)(input.toolID);
198
+ try {
199
+ if (mcpTool) {
200
+ const mcpResult = await (0, argus_1.checkMcpPermission)(client, mcpTool, {
201
+ subject,
202
+ spanAttributes: { toolName: input.toolID },
203
+ });
204
+ output.status = mcpResult.allowed ? "allow" : "deny";
205
+ return;
206
+ }
207
+ const result = await client.checkPermission({
208
+ namespace: resolveNamespace(),
209
+ object: input.toolID,
210
+ relation: "use",
211
+ ...subject,
212
+ }, { spanAttributes: { toolName: input.toolID } });
213
+ output.status = result.allowed ? "allow" : "deny";
214
+ }
215
+ catch (err) {
216
+ if (isOryError(err) && (err.code === "network_error" || err.code === "rate_limited")) {
217
+ client.logger.warn("permission.fallback", {
218
+ toolID: input.toolID,
219
+ code: err.code,
220
+ });
221
+ // Leave output.status as "ask" (default)
222
+ return;
223
+ }
224
+ // For other errors, also fall back to ask
225
+ output.status = "ask";
226
+ }
227
+ };
228
+ }
229
+ // ─── tool.execute.before ────────────────────────────────────────────
230
+ function createToolBeforeHandler(client) {
231
+ return async (input, output) => {
232
+ client.logger.info("lifecycle.tool_execute_before", {
233
+ tool: input.tool,
234
+ sessionID: input.sessionID,
235
+ callID: input.callID,
236
+ });
237
+ // Set trace context for this tool invocation
238
+ client.tracer.setContext({
239
+ traceId: (0, argus_1.deriveTraceId)(input.sessionID),
240
+ sessionId: input.sessionID,
241
+ });
242
+ const inputSummary = (0, argus_1.summarizeToolInput)(input.tool, output.args);
243
+ // In audit-only mode, log the invocation but skip permission checks
244
+ if ((0, argus_1.resolveConfig)().auditOnly) {
245
+ client.tracer.record("tool.invoke", "ok", {
246
+ attributes: { toolName: input.tool, ...inputSummary },
247
+ });
248
+ return;
249
+ }
250
+ const subject = (0, argus_1.resolveUserSubject)(client, input.sessionID ? `session:${input.sessionID}` : "agent:opencode");
251
+ const subjectId = (0, argus_1.subjectLabel)(subject);
252
+ const mcpTool = (0, argus_1.parseMcpToolGeneric)(input.tool);
253
+ try {
254
+ if (mcpTool) {
255
+ const mcpResult = await (0, argus_1.checkMcpPermission)(client, mcpTool, {
256
+ subject,
257
+ spanAttributes: { toolName: input.tool },
258
+ });
259
+ if (!mcpResult.allowed) {
260
+ client.tracer.record("tool.block", "denied", {
261
+ attributes: { toolName: input.tool, mcpServer: mcpTool.serverName, mcpTool: mcpTool.toolName, ...inputSummary, ...(0, argus_1.alertAttributes)(false) },
262
+ });
263
+ const message = (0, argus_1.formatDenialMessage)({
264
+ tool: input.tool,
265
+ subjectId,
266
+ mcp: mcpTool,
267
+ });
268
+ client.logger.warn("tool.denied", {
269
+ tool: input.tool,
270
+ subjectId,
271
+ message,
272
+ });
273
+ // OpenCode has no native block signal in tool.execute.before;
274
+ // throwing aborts execution (documented .env-protection pattern)
275
+ // and surfaces error.message back to the agent.
276
+ throw new argus_1.OryDenialError({
277
+ tool: input.tool,
278
+ subjectId,
279
+ mcp: mcpTool,
280
+ });
281
+ }
282
+ client.tracer.record("tool.invoke", "ok", {
283
+ attributes: { toolName: input.tool, mcpServer: mcpTool.serverName, mcpTool: mcpTool.toolName, ...inputSummary },
284
+ });
285
+ return;
286
+ }
287
+ const namespace = resolveNamespace();
288
+ const result = await client.checkPermission({
289
+ namespace,
290
+ object: input.tool,
291
+ relation: "use",
292
+ ...subject,
293
+ }, { spanAttributes: { toolName: input.tool } });
294
+ if (!result.allowed) {
295
+ client.tracer.record("tool.block", "denied", {
296
+ attributes: { toolName: input.tool, ...inputSummary, allowed: result.allowed, ...(0, argus_1.alertAttributes)(false) },
297
+ });
298
+ const message = (0, argus_1.formatDenialMessage)({
299
+ tool: input.tool,
300
+ subjectId,
301
+ namespace,
302
+ });
303
+ client.logger.warn("tool.denied", {
304
+ tool: input.tool,
305
+ subjectId,
306
+ message,
307
+ });
308
+ throw new argus_1.OryDenialError({
309
+ tool: input.tool,
310
+ subjectId,
311
+ namespace,
312
+ });
313
+ }
314
+ client.tracer.record("tool.invoke", "ok", {
315
+ attributes: { toolName: input.tool, ...inputSummary, allowed: result.allowed },
316
+ });
317
+ }
318
+ catch (err) {
319
+ if (err instanceof argus_1.OryDenialError)
320
+ throw err;
321
+ handlePermissionError(err, input.tool, client);
322
+ }
323
+ };
324
+ }
325
+ // ─── tool.execute.after ─────────────────────────────────────────────
326
+ function createToolAfterHandler(client) {
327
+ return async (input, output) => {
328
+ client.logger.info("lifecycle.tool_execute_after", {
329
+ tool: input.tool,
330
+ sessionID: input.sessionID,
331
+ callID: input.callID,
332
+ });
333
+ // Set trace context for this tool completion
334
+ client.tracer.setContext({
335
+ traceId: (0, argus_1.deriveTraceId)(input.sessionID),
336
+ sessionId: input.sessionID,
337
+ });
338
+ client.tracer.record("tool.complete", "ok", {
339
+ attributes: {
340
+ toolName: input.tool,
341
+ ...(0, argus_1.summarizeToolInput)(input.tool, input.args),
342
+ ...(0, argus_1.summarizeToolOutput)(input.tool, {
343
+ title: output.title,
344
+ output: output.output,
345
+ }),
346
+ },
347
+ });
348
+ };
349
+ }
350
+ // ─── Helpers ────────────────────────────────────────────────────────
351
+ function resolveNamespace() {
352
+ return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools";
353
+ }
354
+ function isOryError(err) {
355
+ return (typeof err === "object" &&
356
+ err !== null &&
357
+ "code" in err &&
358
+ "message" in err);
359
+ }
360
+ function handlePermissionError(err, tool, client) {
361
+ if (isOryError(err)) {
362
+ if (err.code === "network_error" || err.code === "rate_limited") {
363
+ client.logger.warn("permission.fallback", {
364
+ tool,
365
+ code: err.code,
366
+ message: "Failing open",
367
+ });
368
+ return;
369
+ }
370
+ client.logger.error("permission.check.error", {
371
+ tool,
372
+ code: err.code,
373
+ message: err.message,
374
+ });
375
+ }
376
+ else {
377
+ client.logger.error("permission.check.error", {
378
+ tool,
379
+ error: err instanceof Error ? err.message : String(err),
380
+ });
381
+ }
382
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * OpenCode plugin types.
3
+ *
4
+ * OpenCode plugins export { server: Plugin } where Plugin is a function
5
+ * that receives context and returns a Hooks object. Hook functions use
6
+ * a mutating (input, output) pattern.
7
+ *
8
+ * See: @opencode-ai/plugin for canonical types.
9
+ */
10
+ export interface PluginInput {
11
+ client: unknown;
12
+ project: {
13
+ id: string;
14
+ path: string;
15
+ [key: string]: unknown;
16
+ };
17
+ directory: string;
18
+ worktree: string;
19
+ serverUrl: URL;
20
+ }
21
+ export type PluginOptions = Record<string, unknown>;
22
+ export type Plugin = (input: PluginInput, options?: PluginOptions) => Promise<Hooks>;
23
+ export interface PluginModule {
24
+ id?: string;
25
+ server: Plugin;
26
+ }
27
+ export interface Permission {
28
+ toolID: string;
29
+ title: string;
30
+ description: string;
31
+ metadata: Record<string, unknown>;
32
+ }
33
+ export interface ChatMessageInput {
34
+ sessionID: string;
35
+ messageID?: string;
36
+ agent?: string;
37
+ model?: {
38
+ providerID?: string;
39
+ modelID?: string;
40
+ };
41
+ }
42
+ export interface CommandExecuteBeforeInput {
43
+ name: string;
44
+ sessionID?: string;
45
+ args?: unknown;
46
+ }
47
+ export interface Hooks {
48
+ event?: (input: {
49
+ event: unknown;
50
+ }) => Promise<void>;
51
+ /** Fires when OpenCode loads its configuration. Observer only. */
52
+ config?: (input: Record<string, unknown>) => Promise<void>;
53
+ /** Fires on every user message. Observer only — useful for per-turn audit. */
54
+ "chat.message"?: (input: ChatMessageInput, output: Record<string, unknown>) => Promise<void>;
55
+ /** Fires before a slash command runs. Observer only. */
56
+ "command.execute.before"?: (input: CommandExecuteBeforeInput, output: Record<string, unknown>) => Promise<void>;
57
+ "permission.ask"?: (input: Permission, output: {
58
+ status: "ask" | "deny" | "allow";
59
+ }) => Promise<void>;
60
+ "tool.execute.before"?: (input: {
61
+ tool: string;
62
+ sessionID: string;
63
+ callID: string;
64
+ }, output: {
65
+ args: unknown;
66
+ }) => Promise<void>;
67
+ "tool.execute.after"?: (input: {
68
+ tool: string;
69
+ sessionID: string;
70
+ callID: string;
71
+ args: unknown;
72
+ }, output: {
73
+ title: string;
74
+ output: string;
75
+ metadata: unknown;
76
+ }) => Promise<void>;
77
+ }
package/dist/types.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ /**
3
+ * OpenCode plugin types.
4
+ *
5
+ * OpenCode plugins export { server: Plugin } where Plugin is a function
6
+ * that receives context and returns a Hooks object. Hook functions use
7
+ * a mutating (input, output) pattern.
8
+ *
9
+ * See: @opencode-ai/plugin for canonical types.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@ory/opencode",
3
+ "version": "0.1.0",
4
+ "description": "Ory plugin for OpenCode: scaffolding skills, a local Ory instance, and authentication, authorization, and audit for every tool call",
5
+ "license": "Apache-2.0",
6
+ "homepage": "https://ory.com",
7
+ "keywords": [
8
+ "ory",
9
+ "opencode",
10
+ "hooks",
11
+ "plugin",
12
+ "identity",
13
+ "identity-management",
14
+ "iam",
15
+ "authentication",
16
+ "authorization",
17
+ "access-control",
18
+ "permissions",
19
+ "rbac",
20
+ "zanzibar",
21
+ "oauth",
22
+ "oauth2",
23
+ "openid-connect",
24
+ "oidc",
25
+ "session",
26
+ "mfa",
27
+ "sso",
28
+ "audit",
29
+ "audit-log",
30
+ "compliance",
31
+ "agent",
32
+ "ai-agent",
33
+ "agent-security",
34
+ "guardrails",
35
+ "llm",
36
+ "tracing",
37
+ "distributed-tracing",
38
+ "observability",
39
+ "kratos",
40
+ "keto",
41
+ "hydra"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "main": "dist/index.js",
47
+ "types": "dist/index.d.ts",
48
+ "exports": {
49
+ ".": {
50
+ "types": "./dist/index.d.ts",
51
+ "default": "./dist/index.js"
52
+ }
53
+ },
54
+ "bin": {
55
+ "opencode": "dist/cli/main.js",
56
+ "ory-opencode": "dist/cli/main.js",
57
+ "ory-opencode-setup": "dist/cli/setup.js"
58
+ },
59
+ "files": [
60
+ "dist",
61
+ "!dist/dev",
62
+ "!dist/**/*.tsbuildinfo"
63
+ ],
64
+ "dependencies": {
65
+ "@ory/argus": "0.1.0"
66
+ },
67
+ "engines": {
68
+ "node": ">=24"
69
+ },
70
+ "scripts": {
71
+ "build": "tsc",
72
+ "clean": "rm -rf dist *.tsbuildinfo",
73
+ "test": "vitest run",
74
+ "test:watch": "vitest",
75
+ "typecheck": "tsc --noEmit",
76
+ "dev": "node dist/dev/launcher.js"
77
+ }
78
+ }