@ory/argus 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.
Files changed (80) hide show
  1. package/README.md +134 -0
  2. package/assets/commands/local-down.md +19 -0
  3. package/assets/commands/local-up.md +27 -0
  4. package/assets/skills/auth-setup/SKILL.md +279 -0
  5. package/assets/skills/local-dev/SKILL.md +206 -0
  6. package/assets/skills/login-flow/SKILL.md +383 -0
  7. package/assets/skills/social-login/SKILL.md +312 -0
  8. package/dist/agent-auth.d.ts +204 -0
  9. package/dist/agent-auth.js +553 -0
  10. package/dist/auth-gate.d.ts +71 -0
  11. package/dist/auth-gate.js +308 -0
  12. package/dist/auth-store.d.ts +75 -0
  13. package/dist/auth-store.js +261 -0
  14. package/dist/auth.d.ts +93 -0
  15. package/dist/auth.js +323 -0
  16. package/dist/cli.d.ts +73 -0
  17. package/dist/cli.js +484 -0
  18. package/dist/client.d.ts +158 -0
  19. package/dist/client.js +679 -0
  20. package/dist/config.d.ts +135 -0
  21. package/dist/config.js +344 -0
  22. package/dist/denial.d.ts +79 -0
  23. package/dist/denial.js +103 -0
  24. package/dist/dev.d.ts +95 -0
  25. package/dist/dev.js +514 -0
  26. package/dist/index.d.ts +20 -0
  27. package/dist/index.js +137 -0
  28. package/dist/local/cli.d.ts +12 -0
  29. package/dist/local/cli.js +95 -0
  30. package/dist/local/configs.d.ts +89 -0
  31. package/dist/local/configs.js +634 -0
  32. package/dist/local/health.d.ts +32 -0
  33. package/dist/local/health.js +65 -0
  34. package/dist/local/index.d.ts +6 -0
  35. package/dist/local/index.js +38 -0
  36. package/dist/local/jaeger-main.d.ts +13 -0
  37. package/dist/local/jaeger-main.js +85 -0
  38. package/dist/local/jaeger.d.ts +50 -0
  39. package/dist/local/jaeger.js +162 -0
  40. package/dist/local/main.d.ts +7 -0
  41. package/dist/local/main.js +14 -0
  42. package/dist/local/manager.d.ts +45 -0
  43. package/dist/local/manager.js +676 -0
  44. package/dist/local/seed.d.ts +71 -0
  45. package/dist/local/seed.js +237 -0
  46. package/dist/logger.d.ts +29 -0
  47. package/dist/logger.js +139 -0
  48. package/dist/mcp.d.ts +76 -0
  49. package/dist/mcp.js +122 -0
  50. package/dist/otel/exporter.d.ts +17 -0
  51. package/dist/otel/exporter.js +12 -0
  52. package/dist/otel/index.d.ts +2 -0
  53. package/dist/otel/index.js +8 -0
  54. package/dist/otel/otlp-http.d.ts +116 -0
  55. package/dist/otel/otlp-http.js +322 -0
  56. package/dist/registry/cli.d.ts +12 -0
  57. package/dist/registry/cli.js +76 -0
  58. package/dist/registry/config.d.ts +23 -0
  59. package/dist/registry/config.js +80 -0
  60. package/dist/registry/index.d.ts +3 -0
  61. package/dist/registry/index.js +21 -0
  62. package/dist/registry/main.d.ts +7 -0
  63. package/dist/registry/main.js +14 -0
  64. package/dist/registry/manager.d.ts +38 -0
  65. package/dist/registry/manager.js +674 -0
  66. package/dist/setup.d.ts +118 -0
  67. package/dist/setup.js +398 -0
  68. package/dist/skills.d.ts +78 -0
  69. package/dist/skills.js +264 -0
  70. package/dist/subject.d.ts +43 -0
  71. package/dist/subject.js +55 -0
  72. package/dist/tool-metadata.d.ts +41 -0
  73. package/dist/tool-metadata.js +127 -0
  74. package/dist/tracer.d.ts +172 -0
  75. package/dist/tracer.js +452 -0
  76. package/dist/types.d.ts +57 -0
  77. package/dist/types.js +3 -0
  78. package/dist/watch-sandbox.d.ts +9 -0
  79. package/dist/watch-sandbox.js +81 -0
  80. package/package.json +79 -0
package/dist/cli.js ADDED
@@ -0,0 +1,484 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runConfigureCommand = runConfigureCommand;
37
+ exports.printOryConfig = printOryConfig;
38
+ exports.printEnvironment = printEnvironment;
39
+ exports.printLogTail = printLogTail;
40
+ exports.printEnvHelp = printEnvHelp;
41
+ exports.printTraceTail = printTraceTail;
42
+ exports.runWatchCommand = runWatchCommand;
43
+ exports.isTtyAvailable = isTtyAvailable;
44
+ exports.promptOnTty = promptOnTty;
45
+ exports.promptForProjectUrl = promptForProjectUrl;
46
+ exports.interactiveConfigPrompt = interactiveConfigPrompt;
47
+ exports.runAgentCommand = runAgentCommand;
48
+ const fs = __importStar(require("node:fs"));
49
+ const readline = __importStar(require("node:readline"));
50
+ const client_1 = require("@ory/client");
51
+ const config_js_1 = require("./config.js");
52
+ const agent_auth_js_1 = require("./agent-auth.js");
53
+ const tracer_js_1 = require("./tracer.js");
54
+ /**
55
+ * Shared `configure` command implementation for all plugin CLIs.
56
+ *
57
+ * Parses --project-url and --api-key from args. If neither is provided,
58
+ * shows the current configuration. Otherwise saves the provided values.
59
+ */
60
+ function runConfigureCommand(binName, args) {
61
+ let projectUrl;
62
+ let apiKey;
63
+ let auditOnly = false;
64
+ for (let i = 0; i < args.length; i++) {
65
+ switch (args[i]) {
66
+ case "--project-url":
67
+ projectUrl = args[++i];
68
+ break;
69
+ case "--api-key":
70
+ apiKey = args[++i];
71
+ break;
72
+ case "--audit-only":
73
+ auditOnly = true;
74
+ break;
75
+ }
76
+ }
77
+ if (!projectUrl && !apiKey && !auditOnly) {
78
+ const resolved = (0, config_js_1.resolveConfig)();
79
+ const configPath = (0, config_js_1.getConfigPath)();
80
+ console.log("Ory Plugin Configuration");
81
+ console.log("========================");
82
+ console.log("");
83
+ console.log(`Config file: ${configPath}`);
84
+ console.log(`Project URL: ${resolved.projectUrl ?? "(not set)"} [${resolved.projectUrlSource}]`);
85
+ console.log(`API Key: ${resolved.apiKey ? "(set)" : "(not set)"} [${resolved.apiKeySource}]`);
86
+ console.log(`Audit Only: ${resolved.auditOnly ? "yes" : "no"}`);
87
+ console.log("");
88
+ console.log("To configure:");
89
+ console.log(` npx ${binName} configure --project-url <URL> --api-key <KEY>`);
90
+ console.log("");
91
+ console.log("To enable audit logging only (no auth or permission checks):");
92
+ console.log(` npx ${binName} configure --audit-only`);
93
+ console.log("");
94
+ console.log("Or set environment variables:");
95
+ console.log(" export ORY_PROJECT_URL=https://your-project.projects.oryapis.com");
96
+ console.log(" export ORY_API_KEY=ory_pat_...");
97
+ return;
98
+ }
99
+ if (auditOnly) {
100
+ // Audit-only mode: clear projectUrl/apiKey, set auditOnly flag
101
+ (0, config_js_1.saveConfig)({ auditOnly: true });
102
+ const configPath = (0, config_js_1.getConfigPath)();
103
+ console.log(`Configuration saved to ${configPath}`);
104
+ console.log(" Mode: audit-only (logging enabled, auth and permission checks disabled)");
105
+ console.log("");
106
+ console.log("This configuration is shared across all Ory agent plugins.");
107
+ return;
108
+ }
109
+ const update = {};
110
+ if (projectUrl)
111
+ update.projectUrl = projectUrl;
112
+ if (apiKey)
113
+ update.apiKey = apiKey;
114
+ // Clear auditOnly when configuring with a project URL
115
+ update.auditOnly = false;
116
+ (0, config_js_1.saveConfig)(update);
117
+ const configPath = (0, config_js_1.getConfigPath)();
118
+ console.log(`Configuration saved to ${configPath}`);
119
+ if (projectUrl)
120
+ console.log(` Project URL: ${projectUrl}`);
121
+ if (apiKey)
122
+ console.log(` API Key: (set)`);
123
+ console.log("");
124
+ console.log("This configuration is shared across all Ory agent plugins.");
125
+ console.log("Environment variables (ORY_PROJECT_URL, ORY_API_KEY) take precedence when set.");
126
+ }
127
+ /**
128
+ * Print the "Configuration:" block showing Ory project URL and API key status.
129
+ */
130
+ function printOryConfig() {
131
+ const resolved = (0, config_js_1.resolveConfig)();
132
+ const configPath = (0, config_js_1.getConfigPath)();
133
+ console.log("Configuration:");
134
+ console.log(` Config file: ${configPath}`);
135
+ console.log(` Mode: ${resolved.auditOnly ? "audit-only" : "full"}`);
136
+ console.log(` Project URL: ${resolved.projectUrl ?? "NOT SET"} [source: ${resolved.projectUrlSource}]`);
137
+ console.log(` API Key: ${resolved.apiKey ? "(set)" : "NOT SET"} [source: ${resolved.apiKeySource}]`);
138
+ }
139
+ /**
140
+ * Print the "Environment:" block showing Ory-related environment variables.
141
+ */
142
+ function printEnvironment() {
143
+ console.log("");
144
+ console.log("Environment:");
145
+ const vars = [
146
+ ["ORY_SESSION_TOKEN", process.env.ORY_SESSION_TOKEN ? "(set)" : undefined, false],
147
+ ["ORY_AGENT_SUBJECT_ID", process.env.ORY_AGENT_SUBJECT_ID, false],
148
+ ["ORY_AGENT_DEBUG", process.env.ORY_AGENT_DEBUG, false],
149
+ ["ORY_AGENT_LOG_FILE", process.env.ORY_AGENT_LOG_FILE, false],
150
+ ["ORY_AGENT_TRACE_FILE", process.env.ORY_AGENT_TRACE_FILE, false],
151
+ ];
152
+ for (const [name, value, required] of vars) {
153
+ const display = value ? `${value}` : required ? "NOT SET (required)" : "not set";
154
+ const icon = value ? "+" : required ? "!" : "-";
155
+ console.log(` [${icon}] ${name}: ${display}`);
156
+ }
157
+ }
158
+ /**
159
+ * Print the last 5 debug log entries if ORY_AGENT_LOG_FILE is set and exists.
160
+ */
161
+ function printLogTail() {
162
+ const logFile = process.env.ORY_AGENT_LOG_FILE;
163
+ if (logFile && fs.existsSync(logFile)) {
164
+ const lines = fs.readFileSync(logFile, "utf-8").trim().split("\n");
165
+ console.log("");
166
+ console.log(`Debug log: ${logFile} (${lines.length} events)`);
167
+ console.log("Last 5 events:");
168
+ for (const line of lines.slice(-5)) {
169
+ try {
170
+ const entry = JSON.parse(line);
171
+ console.log(` ${entry.timestamp} [${entry.level}] ${entry.event}`);
172
+ }
173
+ catch {
174
+ console.log(` ${line.slice(0, 120)}`);
175
+ }
176
+ }
177
+ }
178
+ }
179
+ /**
180
+ * Print the "Configure your Ory credentials" help text.
181
+ */
182
+ function printEnvHelp(binName) {
183
+ console.log("");
184
+ console.log("Configure your Ory credentials (one of these methods):");
185
+ console.log("");
186
+ console.log(" Option 1 — Save to config file (persists across sessions):");
187
+ console.log(` npx ${binName} configure --project-url https://your-project.projects.oryapis.com --api-key ory_pat_...`);
188
+ console.log("");
189
+ console.log(" Option 2 — Set environment variables:");
190
+ console.log(" export ORY_PROJECT_URL=https://your-project.projects.oryapis.com");
191
+ console.log(" export ORY_API_KEY=ory_pat_...");
192
+ console.log("");
193
+ console.log("If neither is set when a session starts, the agent will prompt for configuration.");
194
+ }
195
+ /**
196
+ * Print the last 5 trace entries if ORY_AGENT_TRACE_FILE is set and exists.
197
+ */
198
+ function printTraceTail() {
199
+ const traceFile = process.env.ORY_AGENT_TRACE_FILE;
200
+ if (traceFile && fs.existsSync(traceFile)) {
201
+ const lines = fs.readFileSync(traceFile, "utf-8").trim().split("\n");
202
+ console.log("");
203
+ console.log(`Trace file: ${traceFile} (${lines.length} spans)`);
204
+ console.log("Last 5 spans:");
205
+ for (const line of lines.slice(-5)) {
206
+ try {
207
+ const span = JSON.parse(line);
208
+ console.log(" " + (0, tracer_js_1.formatSpan)(span));
209
+ }
210
+ catch {
211
+ console.log(` ${line.slice(0, 120)}`);
212
+ }
213
+ }
214
+ }
215
+ }
216
+ /**
217
+ * Run the live trace watcher. Tails the NDJSON trace file and prints
218
+ * formatted spans to stdout as they arrive. Blocks until interrupted.
219
+ */
220
+ function runWatchCommand(args) {
221
+ let traceFile = args[0];
222
+ if (!traceFile) {
223
+ traceFile = process.env.ORY_AGENT_TRACE_FILE ?? "";
224
+ }
225
+ if (!traceFile) {
226
+ console.error("Usage: watch <trace-file>\n" +
227
+ " Or set ORY_AGENT_TRACE_FILE environment variable.\n");
228
+ process.exit(1);
229
+ }
230
+ console.log(`Watching traces: ${traceFile}`);
231
+ console.log("Press Ctrl+C to stop.\n");
232
+ const stop = (0, tracer_js_1.watchTraceFile)(traceFile, (span) => {
233
+ console.log((0, tracer_js_1.formatSpan)(span));
234
+ });
235
+ process.on("SIGINT", () => {
236
+ stop();
237
+ console.log("\nStopped watching.");
238
+ process.exit(0);
239
+ });
240
+ }
241
+ /**
242
+ * Returns true if `/dev/tty` can be opened for reading. Use this before
243
+ * attempting to prompt the user — the harness has already taken stdin so
244
+ * we cannot use `process.stdin`.
245
+ */
246
+ function isTtyAvailable() {
247
+ try {
248
+ const fd = fs.openSync("/dev/tty", "r");
249
+ fs.closeSync(fd);
250
+ return true;
251
+ }
252
+ catch {
253
+ return false;
254
+ }
255
+ }
256
+ /**
257
+ * Prompt the user to enter a value on /dev/tty. Returns the trimmed
258
+ * line, or null if no TTY is available. The prompt is written to stderr
259
+ * — never stdout.
260
+ */
261
+ async function promptOnTty(prompt) {
262
+ let ttyFd;
263
+ try {
264
+ ttyFd = fs.openSync("/dev/tty", "r");
265
+ }
266
+ catch {
267
+ return null;
268
+ }
269
+ const ttyStream = fs.createReadStream("", { fd: ttyFd });
270
+ const rl = readline.createInterface({ input: ttyStream, output: process.stderr });
271
+ try {
272
+ const answer = await new Promise((resolve) => {
273
+ rl.question(prompt, resolve);
274
+ });
275
+ return answer.trim();
276
+ }
277
+ finally {
278
+ rl.close();
279
+ ttyStream.destroy();
280
+ }
281
+ }
282
+ /**
283
+ * Inline prompt for the Ory project URL. Persists the entered URL to the
284
+ * shared config file and returns it. Returns null if the user cancels
285
+ * (empty input) or no TTY is available.
286
+ *
287
+ * This is the structured counterpart to {@link interactiveConfigPrompt},
288
+ * which only blocks until the user runs `configure` in another terminal.
289
+ */
290
+ async function promptForProjectUrl(binName) {
291
+ if (!isTtyAvailable())
292
+ return null;
293
+ const message = [
294
+ "",
295
+ "--------------------------------------------",
296
+ " Ory Agent Plugin — Project URL Required",
297
+ "--------------------------------------------",
298
+ "",
299
+ "No Ory project URL is configured for this session.",
300
+ `Enter your Ory project URL to continue, or press Enter to skip and run \`npx ${binName} configure\` later.`,
301
+ "",
302
+ "Example: https://your-project.projects.oryapis.com",
303
+ "",
304
+ ].join("\n");
305
+ process.stderr.write(message + "\n");
306
+ const answer = await promptOnTty("Project URL: ");
307
+ if (!answer) {
308
+ process.stderr.write("\nNo URL entered. Continuing without authentication.\n\n");
309
+ return null;
310
+ }
311
+ let url;
312
+ try {
313
+ const parsed = new URL(answer);
314
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
315
+ process.stderr.write(`\nIgnoring invalid project URL "${answer}".\n\n`);
316
+ return null;
317
+ }
318
+ url = parsed.toString().replace(/\/$/, "");
319
+ }
320
+ catch {
321
+ process.stderr.write(`\nIgnoring invalid project URL "${answer}".\n\n`);
322
+ return null;
323
+ }
324
+ (0, config_js_1.saveConfig)({ projectUrl: url, auditOnly: false });
325
+ process.stderr.write(`\nSaved project URL to ${(0, config_js_1.getConfigPath)()}.\n\n`);
326
+ return url;
327
+ }
328
+ /**
329
+ * Display a friendly configuration prompt on stderr and block until the
330
+ * user presses Enter. After the user continues, re-checks config from
331
+ * disk and returns true if Ory is now configured.
332
+ *
333
+ * Falls back to non-interactive mode (returns false immediately) when
334
+ * no TTY is available (e.g. CI).
335
+ */
336
+ async function interactiveConfigPrompt(binName) {
337
+ const configPath = (0, config_js_1.getConfigPath)();
338
+ const message = [
339
+ "",
340
+ "--------------------------------------------",
341
+ " Ory Agent Plugin — Configuration Required",
342
+ "--------------------------------------------",
343
+ "",
344
+ "The Ory agent plugin is installed but not yet configured.",
345
+ "Run one of the following commands in another terminal:",
346
+ "",
347
+ " Connect to Ory (enables auth & permission checks):",
348
+ ` npx ${binName} configure --project-url <URL> --api-key <KEY>`,
349
+ "",
350
+ " Or, enable audit logging only (no auth or permission checks):",
351
+ ` npx ${binName} configure --audit-only`,
352
+ "",
353
+ `Config is saved to ${configPath}`,
354
+ "and shared across all agent plugins.",
355
+ "",
356
+ "You can also set environment variables (requires session restart):",
357
+ " export ORY_PROJECT_URL=https://your-project.projects.oryapis.com",
358
+ " export ORY_API_KEY=ory_pat_...",
359
+ "",
360
+ ].join("\n");
361
+ process.stderr.write(message + "\n");
362
+ // Try to open the terminal for blocking user input.
363
+ // stdin is consumed by the harness JSON pipe, so we read /dev/tty directly.
364
+ let ttyFd;
365
+ try {
366
+ ttyFd = fs.openSync("/dev/tty", "r");
367
+ }
368
+ catch {
369
+ // No TTY (CI, piped environment) — can't block for input
370
+ process.stderr.write("No interactive terminal available. Configure Ory and restart the session.\n\n");
371
+ return false;
372
+ }
373
+ const ttyStream = fs.createReadStream("", { fd: ttyFd });
374
+ const rl = readline.createInterface({ input: ttyStream, output: process.stderr });
375
+ try {
376
+ await new Promise((resolve) => {
377
+ rl.question("Press Enter after configuring to continue... ", resolve);
378
+ });
379
+ }
380
+ finally {
381
+ rl.close();
382
+ ttyStream.destroy();
383
+ }
384
+ // Re-read config from disk (env vars set in another shell won't be visible)
385
+ const resolved = (0, config_js_1.resolveConfig)();
386
+ if (resolved.projectUrl || resolved.auditOnly) {
387
+ process.stderr.write("\nConfiguration detected! Continuing session.\n\n");
388
+ return true;
389
+ }
390
+ process.stderr.write("\nNo configuration detected. Session blocked.\n");
391
+ process.stderr.write("Run one of the configure commands above and start a new session.\n\n");
392
+ return false;
393
+ }
394
+ /**
395
+ * Run the `agent <subcommand>` family of CLI commands. These manage the
396
+ * agent's dynamic OAuth2 client registration (RFC 7591). Subcommands:
397
+ *
398
+ * - `status` — print the persisted DCR identity, if any.
399
+ * - `unregister` — best-effort RFC 7592 DELETE on the registered
400
+ * client, then clear the persisted block.
401
+ *
402
+ * Returns the process exit code (0 on success, non-zero on usage error).
403
+ */
404
+ async function runAgentCommand(binName, args) {
405
+ const sub = args[0];
406
+ if (!sub || sub === "--help" || sub === "-h") {
407
+ console.log(`Usage: ${binName} agent <subcommand>`);
408
+ console.log("");
409
+ console.log("Subcommands:");
410
+ console.log(" status Show the registered agent OAuth2 client (if any)");
411
+ console.log(" unregister Delete the registered agent client and clear local state");
412
+ return sub ? 0 : 1;
413
+ }
414
+ switch (sub) {
415
+ case "status":
416
+ runAgentStatus();
417
+ return 0;
418
+ case "unregister":
419
+ return await runAgentUnregister();
420
+ default:
421
+ console.error(`Unknown agent subcommand: ${sub}`);
422
+ console.error(`Run "${binName} agent --help" for usage.`);
423
+ return 1;
424
+ }
425
+ }
426
+ function runAgentStatus() {
427
+ const persisted = (0, agent_auth_js_1.loadAgentDynamicCredentials)();
428
+ console.log("Agent identity (Dynamic Client Registration)");
429
+ console.log("=============================================");
430
+ console.log("");
431
+ if (!persisted) {
432
+ console.log(" No registered agent client. The harness will register one on its");
433
+ console.log(" next session start using the user's bearer (or");
434
+ console.log(" ORY_AGENT_REGISTRATION_TOKEN) as the initial access token.");
435
+ return;
436
+ }
437
+ console.log(` client_id: ${persisted.clientId}`);
438
+ console.log(` client_secret: ${persisted.clientSecret ? "(set)" : "(not set — public client)"}`);
439
+ console.log(` registered_at: ${new Date(persisted.registeredAt * 1000).toISOString()}`);
440
+ console.log(` project_url: ${persisted.projectUrl}`);
441
+ if (persisted.harness)
442
+ console.log(` harness: ${persisted.harness}`);
443
+ if (persisted.registrationClientUri) {
444
+ console.log(` management_uri: ${persisted.registrationClientUri}`);
445
+ }
446
+ if (persisted.registrationAccessToken) {
447
+ console.log(` registration_token: (set — used to manage/delete this client)`);
448
+ }
449
+ }
450
+ async function runAgentUnregister() {
451
+ const persisted = (0, agent_auth_js_1.loadAgentDynamicCredentials)();
452
+ if (!persisted) {
453
+ console.log("No registered agent client to unregister. Nothing to do.");
454
+ return 0;
455
+ }
456
+ if (persisted.registrationClientUri && persisted.registrationAccessToken) {
457
+ const oidc = new client_1.OidcApi(new client_1.Configuration({
458
+ basePath: persisted.projectUrl,
459
+ accessToken: persisted.registrationAccessToken,
460
+ }));
461
+ try {
462
+ await oidc.deleteOidcDynamicClient({ id: persisted.clientId });
463
+ }
464
+ catch (err) {
465
+ const status = err?.response?.status;
466
+ if (status === 404) {
467
+ // Already gone server-side; nothing to warn about.
468
+ }
469
+ else if (typeof status === "number") {
470
+ console.warn(`Warning: server-side DELETE returned HTTP ${status}. Local state will be cleared anyway.`);
471
+ }
472
+ else {
473
+ const msg = err instanceof Error ? err.message : String(err);
474
+ console.warn(`Warning: server-side DELETE failed (${msg}). Local state will be cleared anyway.`);
475
+ }
476
+ }
477
+ }
478
+ else {
479
+ console.warn("Warning: no registration_access_token was persisted, so we cannot DELETE the server-side client. Clearing local state only.");
480
+ }
481
+ (0, agent_auth_js_1.clearAgentDynamicCredentials)();
482
+ console.log(`Unregistered agent client ${persisted.clientId}.`);
483
+ return 0;
484
+ }
@@ -0,0 +1,158 @@
1
+ import { DebugLogger } from "./logger.js";
2
+ import { Tracer } from "./tracer.js";
3
+ import type { SpanExporter } from "./otel/exporter.js";
4
+ import type { SessionInfo, OAuth2TokenInfo, PermissionCheck, PermissionResult, BatchPermissionResult, OryError } from "./types.js";
5
+ export interface OryAgentConfig {
6
+ /** Ory project URL, e.g. https://your-project.projects.oryapis.com */
7
+ projectUrl: string;
8
+ /** Ory API key for admin operations */
9
+ apiKey?: string;
10
+ /** Which harness this client is running in */
11
+ harness: string;
12
+ /** Optional log file path for debug output */
13
+ logFile?: string;
14
+ /** Optional NDJSON trace file path for structured span output */
15
+ traceFile?: string;
16
+ /**
17
+ * Session cache TTL in milliseconds.
18
+ * Set to 0 to disable caching. Default: 60_000 (1 minute).
19
+ */
20
+ sessionCacheTtlMs?: number;
21
+ /** Optional span exporter (OTLP, vendor SDK, …) attached to the tracer. */
22
+ exporter?: SpanExporter;
23
+ }
24
+ /**
25
+ * A principal known to the client. Either the human user (interactive
26
+ * PKCE login) or the agent (machine credentials).
27
+ */
28
+ export interface PrincipalIdentity {
29
+ /** Subject (sub claim or `session:<id>`) — used in audit spans. */
30
+ subject?: string;
31
+ /** Bearer token. Agent's token authenticates outgoing API calls. */
32
+ token?: string;
33
+ }
34
+ export declare class OryAgentClient {
35
+ private frontend;
36
+ private oauth2;
37
+ private permission;
38
+ private relationship;
39
+ private config;
40
+ readonly logger: DebugLogger;
41
+ readonly tracer: Tracer;
42
+ private sessionCache;
43
+ private sessionCacheTtlMs;
44
+ /**
45
+ * The human user — the principal whose permissions we check.
46
+ * Populated by `ensureUserAuthenticated` on session start.
47
+ */
48
+ private _userPrincipal;
49
+ /**
50
+ * The AI agent process — the principal that authenticates outgoing
51
+ * Ory API calls. Populated by `ensureAgentIdentity`.
52
+ */
53
+ private _agentPrincipal;
54
+ constructor(config: OryAgentConfig);
55
+ /** Build a fresh set of Ory API instances using the current agent token. */
56
+ private buildApis;
57
+ /** Snapshot of the current user principal. */
58
+ get userPrincipal(): PrincipalIdentity;
59
+ /** Snapshot of the current agent principal. */
60
+ get agentPrincipal(): PrincipalIdentity;
61
+ /**
62
+ * Set or update the human user principal. The user is the subject of
63
+ * permission checks but does not authenticate outgoing API calls.
64
+ * Pass `{}` (or fields set to undefined) to clear.
65
+ */
66
+ setUserPrincipal(principal: PrincipalIdentity): void;
67
+ /**
68
+ * Set or update the AI agent principal. The agent's token (when
69
+ * present) is used in the Authorization header for all outgoing Ory
70
+ * API calls — so the audit log shows "agent X acting on behalf of
71
+ * user Y". Rebuilds the underlying Ory API instances when the token
72
+ * actually changes so subsequent calls pick it up; otherwise leaves
73
+ * the API instances alone (so test stubs survive a no-op update).
74
+ */
75
+ setAgentPrincipal(principal: PrincipalIdentity): void;
76
+ /**
77
+ * Verify an existing session token. Returns cached result if available.
78
+ */
79
+ verifySession(sessionToken: string): Promise<SessionInfo>;
80
+ /**
81
+ * Introspect an OAuth2 access token. Use this when agents authenticate
82
+ * via client credentials flow instead of session tokens.
83
+ */
84
+ introspectToken(token: string, scope?: string): Promise<OAuth2TokenInfo>;
85
+ /**
86
+ * Check if a subject has permission to perform an action.
87
+ *
88
+ * `options.spanAttributes` are merged into the `permission.check` trace
89
+ * span so callers can attach harness-side context (toolName, mcpServer,
90
+ * etc.) that the wire-level `PermissionCheck` shape doesn't carry.
91
+ */
92
+ checkPermission(check: PermissionCheck, options?: {
93
+ spanAttributes?: Record<string, unknown>;
94
+ }): Promise<PermissionResult>;
95
+ /**
96
+ * Check multiple permissions in a single request.
97
+ *
98
+ * `options.spanAttributes` are merged into the `permission.batch_check`
99
+ * span — same purpose as `checkPermission`'s spanAttributes.
100
+ */
101
+ batchCheckPermissions(checks: PermissionCheck[], options?: {
102
+ spanAttributes?: Record<string, unknown>;
103
+ }): Promise<BatchPermissionResult>;
104
+ /**
105
+ * Create a relation tuple in Keto. Idempotent: a 409 (tuple already
106
+ * exists) is swallowed and reported as success. All other errors are
107
+ * classified and re-thrown — callers wrap in fail-open semantics if
108
+ * the tuple is non-critical (e.g. audit-only delegation tracking).
109
+ *
110
+ * `options.spanAttributes` are merged into the `relationship.create`
111
+ * trace span so callers can attach context like `delegation: "user→agent"`.
112
+ */
113
+ createRelationship(check: PermissionCheck, options?: {
114
+ spanAttributes?: Record<string, unknown>;
115
+ }): Promise<{
116
+ created: boolean;
117
+ alreadyExisted: boolean;
118
+ }>;
119
+ /**
120
+ * Delete a relation tuple in Keto. Missing tuples (404) are treated as
121
+ * success so callers can call this idempotently when unwinding state.
122
+ */
123
+ deleteRelationship(check: PermissionCheck, options?: {
124
+ spanAttributes?: Record<string, unknown>;
125
+ }): Promise<{
126
+ deleted: boolean;
127
+ notFound: boolean;
128
+ }>;
129
+ /**
130
+ * Span attributes describing which principals were attached to the
131
+ * client when the span was started. Useful for correlating audit logs
132
+ * across the user/agent split.
133
+ */
134
+ private principalSpanAttributes;
135
+ /**
136
+ * Classify an error from any Ory API call into a structured OryError.
137
+ */
138
+ classifyError(err: unknown): OryError;
139
+ private isAxiosError;
140
+ /**
141
+ * Create a client from environment variables.
142
+ */
143
+ /**
144
+ * Create a client from environment variables, falling back to the
145
+ * shared config file at ~/.config/ory-agent-plugins/config.json.
146
+ *
147
+ * Resolution order for projectUrl and apiKey:
148
+ * 1. Environment variables (ORY_PROJECT_URL, ORY_API_KEY)
149
+ * 2. Config file (~/.config/ory-agent-plugins/config.json)
150
+ * 3. Placeholder (fail-open pass-through mode)
151
+ */
152
+ static fromEnv(harness: string): OryAgentClient;
153
+ }
154
+ /**
155
+ * Pull the Ory backend request ID from a response headers bag (axios returns
156
+ * lowercase header names). Returns undefined when not present.
157
+ */
158
+ export declare function extractOryRequestId(headers: unknown): string | undefined;