@ory/argus 0.13.9 → 1.0.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 (133) hide show
  1. package/README.md +31 -45
  2. package/assets/commands/temporal-up.md +1 -1
  3. package/assets/skills/auth-setup/SKILL.md +1 -1
  4. package/assets/skills/local-dev/SKILL.md +17 -7
  5. package/assets/skills/ory-build-agent/SKILL.md +43 -97
  6. package/assets/skills/ory-e2b-sandbox/SKILL.md +18 -18
  7. package/assets/skills/ory-temporal-worker/SKILL.md +38 -42
  8. package/assets/skills/permissions-onboarding/SKILL.md +131 -104
  9. package/dist/adapters.d.ts +93 -30
  10. package/dist/adapters.js +464 -136
  11. package/dist/agent-auth.d.ts +258 -68
  12. package/dist/agent-auth.js +998 -202
  13. package/dist/auth-store.d.ts +37 -2
  14. package/dist/auth-store.js +37 -3
  15. package/dist/auth.d.ts +40 -4
  16. package/dist/auth.js +247 -19
  17. package/dist/bash-parser.d.ts +98 -0
  18. package/dist/bash-parser.js +396 -0
  19. package/dist/branding.d.ts +128 -0
  20. package/dist/branding.js +151 -0
  21. package/dist/build-info.json +4 -4
  22. package/dist/cli-invocation.d.ts +1 -1
  23. package/dist/cli-invocation.js +2 -1
  24. package/dist/cli.d.ts +20 -29
  25. package/dist/cli.js +271 -278
  26. package/dist/client.d.ts +175 -138
  27. package/dist/client.js +672 -391
  28. package/dist/config.d.ts +249 -57
  29. package/dist/config.js +486 -62
  30. package/dist/context.d.ts +10 -0
  31. package/dist/context.js +21 -0
  32. package/dist/contract-suite.d.ts +8 -8
  33. package/dist/contract-suite.js +88 -69
  34. package/dist/denial.d.ts +36 -3
  35. package/dist/denial.js +79 -10
  36. package/dist/event-reporter.d.ts +77 -0
  37. package/dist/event-reporter.js +776 -0
  38. package/dist/external-registrations-main.d.ts +10 -0
  39. package/dist/external-registrations-main.js +38 -0
  40. package/dist/external-registrations.d.ts +79 -0
  41. package/dist/external-registrations.js +188 -0
  42. package/dist/help-cli.d.ts +39 -0
  43. package/dist/help-cli.js +55 -0
  44. package/dist/hook-timeout.d.ts +64 -0
  45. package/dist/hook-timeout.js +88 -0
  46. package/dist/index.d.ts +31 -19
  47. package/dist/index.js +182 -31
  48. package/dist/lifecycle.d.ts +3 -3
  49. package/dist/lifecycle.js +38 -6
  50. package/dist/local/cli.js +11 -6
  51. package/dist/local/configs.d.ts +74 -18
  52. package/dist/local/configs.js +291 -84
  53. package/dist/local/health.d.ts +14 -0
  54. package/dist/local/health.js +50 -4
  55. package/dist/local/index.d.ts +2 -2
  56. package/dist/local/index.js +24 -10
  57. package/dist/local/manager.d.ts +20 -1
  58. package/dist/local/manager.js +160 -39
  59. package/dist/local/ports.d.ts +158 -0
  60. package/dist/local/ports.js +443 -0
  61. package/dist/local/seed.d.ts +22 -25
  62. package/dist/local/seed.js +88 -56
  63. package/dist/logger.d.ts +54 -25
  64. package/dist/logger.js +329 -63
  65. package/dist/mcp.d.ts +2 -2
  66. package/dist/mcp.js +10 -5
  67. package/dist/mirror-bootstrap.d.ts +48 -0
  68. package/dist/mirror-bootstrap.js +254 -0
  69. package/dist/opl.d.ts +289 -0
  70. package/dist/opl.js +446 -0
  71. package/dist/permission-mode.d.ts +87 -0
  72. package/dist/permission-mode.js +307 -0
  73. package/dist/permissions-cli.d.ts +13 -49
  74. package/dist/permissions-cli.js +154 -348
  75. package/dist/permissions.d.ts +148 -38
  76. package/dist/permissions.js +591 -45
  77. package/dist/post-install.d.ts +33 -0
  78. package/dist/post-install.js +127 -0
  79. package/dist/read-credential.d.ts +65 -0
  80. package/dist/read-credential.js +86 -0
  81. package/dist/registry/cli.js +5 -2
  82. package/dist/registry/config.d.ts +0 -17
  83. package/dist/registry/config.js +0 -23
  84. package/dist/registry/index.d.ts +1 -1
  85. package/dist/registry/index.js +2 -2
  86. package/dist/registry/manager.d.ts +4 -21
  87. package/dist/registry/manager.js +83 -55
  88. package/dist/runtime-credential.d.ts +140 -0
  89. package/dist/runtime-credential.js +572 -0
  90. package/dist/runtime.d.ts +408 -0
  91. package/dist/runtime.js +748 -0
  92. package/dist/setup.d.ts +23 -28
  93. package/dist/setup.js +57 -84
  94. package/dist/status-cli.d.ts +29 -13
  95. package/dist/status-cli.js +124 -144
  96. package/dist/status-data.d.ts +195 -0
  97. package/dist/status-data.js +333 -0
  98. package/dist/status-system.d.ts +24 -0
  99. package/dist/status-system.js +56 -0
  100. package/dist/subject.d.ts +126 -20
  101. package/dist/subject.js +215 -30
  102. package/dist/testing.d.ts +74 -38
  103. package/dist/testing.js +185 -68
  104. package/dist/tool-catalog.d.ts +53 -11
  105. package/dist/tool-catalog.js +164 -13
  106. package/dist/tool-metadata.d.ts +7 -6
  107. package/dist/tool-metadata.js +6 -5
  108. package/dist/types.d.ts +11 -1
  109. package/dist/uninstall.d.ts +74 -19
  110. package/dist/uninstall.js +224 -49
  111. package/dist/user-login.d.ts +22 -16
  112. package/dist/user-login.js +67 -96
  113. package/dist/watch-cli.d.ts +6 -0
  114. package/dist/watch-cli.js +217 -0
  115. package/package.json +3 -11
  116. package/dist/dev.d.ts +0 -103
  117. package/dist/dev.js +0 -584
  118. package/dist/interactive-setup.d.ts +0 -165
  119. package/dist/interactive-setup.js +0 -1546
  120. package/dist/local/jaeger-main.d.ts +0 -13
  121. package/dist/local/jaeger-main.js +0 -85
  122. package/dist/local/jaeger.d.ts +0 -50
  123. package/dist/local/jaeger.js +0 -162
  124. package/dist/otel/exporter.d.ts +0 -17
  125. package/dist/otel/exporter.js +0 -12
  126. package/dist/otel/index.d.ts +0 -2
  127. package/dist/otel/index.js +0 -8
  128. package/dist/otel/otlp.d.ts +0 -103
  129. package/dist/otel/otlp.js +0 -385
  130. package/dist/tracer.d.ts +0 -190
  131. package/dist/tracer.js +0 -481
  132. package/dist/watch-sandbox.d.ts +0 -9
  133. package/dist/watch-sandbox.js +0 -81
@@ -1,22 +1,27 @@
1
1
  "use strict";
2
2
  /**
3
- * Canonical per-harness tool catalog used by `permissions bootstrap` to
4
- * seed Keto tuples granting the current user `use` on each tool.
3
+ * Canonical per-harness tool catalog: the objects an Ory project grants `use`
4
+ * on, and the set `permissions status` probes coverage across.
5
5
  *
6
6
  * These lists cover each harness's built-in tools — the names the harness
7
7
  * passes to the pre-tool-use hook. MCP server tools are intentionally
8
8
  * omitted because they're discovered dynamically per session and can't be
9
- * enumerated at bootstrap time.
9
+ * enumerated ahead of time.
10
10
  *
11
- * The lists are best-effort: harness vendors add and rename tools over
12
- * time. `bootstrap` is idempotent and additive, so re-running it after a
13
- * catalog update is safe.
11
+ * The lists are best-effort: harness vendors add and rename tools over time,
12
+ * so a project's grants may cover more or fewer names than the catalog knows.
14
13
  */
15
14
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.INTERACTIVE_TOOL_CATALOG = exports.ALL_TOOLS = exports.KNOWN_HARNESSES = exports.HARNESS_TOOL_CATALOG = void 0;
15
+ exports.SHELL_COMMAND_CATALOG = exports.SHELL_TOOL_CATALOG = exports.INTERACTIVE_TOOL_CATALOG = exports.ALL_TOOLS = exports.KNOWN_HARNESSES = exports.HARNESS_TOOL_CATALOG = void 0;
17
16
  exports.getToolCatalog = getToolCatalog;
18
17
  exports.getInteractiveToolCatalog = getInteractiveToolCatalog;
19
18
  exports.isInteractiveTool = isInteractiveTool;
19
+ exports.getShellToolCatalog = getShellToolCatalog;
20
+ exports.isShellTool = isShellTool;
21
+ exports.getShellCommandCatalog = getShellCommandCatalog;
22
+ exports.resolveShellCommandNamespace = resolveShellCommandNamespace;
23
+ exports.extractShellCommand = extractShellCommand;
24
+ const opl_js_1 = require("./opl.js");
20
25
  /**
21
26
  * Built-in tool names known to ship with each supported harness.
22
27
  */
@@ -43,7 +48,7 @@ exports.HARNESS_TOOL_CATALOG = {
43
48
  ],
44
49
  // The Claude Agent SDK exposes the same built-in tool surface as Claude
45
50
  // Code (it is the embeddable form of the same runtime), so the catalog
46
- // mirrors claude-code's and `permissions bootstrap` can seed it.
51
+ // mirrors claude-code's.
47
52
  "claude-agent-sdk": [
48
53
  "Read",
49
54
  "Write",
@@ -135,20 +140,36 @@ exports.HARNESS_TOOL_CATALOG = {
135
140
  "search_web",
136
141
  "call_mcp_tool",
137
142
  ],
143
+ // Cursor's `preToolUse` hook serializes `tool_name` as a coarse tool
144
+ // category (Shell, Read, Write, Edit, Delete, List, Search, MCP, Task);
145
+ // fine-grained shell commands and MCP tools arrive on the dedicated
146
+ // `beforeShellExecution` / `beforeMCPExecution` hooks (logged, not gated,
147
+ // to avoid double-gating). Bootstrap seeds the categories the agent gate
148
+ // actually checks. Best-effort — verify against your Cursor version.
149
+ cursor: [
150
+ "Read",
151
+ "Write",
152
+ "Edit",
153
+ "Delete",
154
+ "Shell",
155
+ "List",
156
+ "Search",
157
+ "MCP",
158
+ "Task",
159
+ ],
138
160
  };
139
161
  exports.KNOWN_HARNESSES = Object.keys(exports.HARNESS_TOOL_CATALOG);
140
162
  /**
141
163
  * Tools for a specific harness. Returns the canonical list when known;
142
164
  * empty array for unknown harnesses (callers can fall back to {@link
143
- * ALL_TOOLS} or skip bootstrap with a warning).
165
+ * ALL_TOOLS} or skip the catalog-scoped step with a warning).
144
166
  */
145
167
  function getToolCatalog(harness) {
146
168
  return (exports.HARNESS_TOOL_CATALOG[harness] ?? []);
147
169
  }
148
170
  /**
149
- * Union of every known tool across all harnesses, deduplicated. Used by
150
- * the dev launcher's seed routine, which grants the local user identity
151
- * broad access so the same launcher can run every harness end-to-end.
171
+ * Union of every known tool across all harnesses, deduplicated. Used by local
172
+ * environment seeding to grant broad access across every built-in tool set.
152
173
  */
153
174
  exports.ALL_TOOLS = Array.from(new Set(Object.values(exports.HARNESS_TOOL_CATALOG).flat()));
154
175
  /**
@@ -161,7 +182,7 @@ exports.ALL_TOOLS = Array.from(new Set(Object.values(exports.HARNESS_TOOL_CATALO
161
182
  * the user is about to handle directly.
162
183
  *
163
184
  * Plugins consult this list in their pre-tool gate and short-circuit to
164
- * a single `user.interaction` audit span when a match is found. See
185
+ * a single `user.interaction` audit event when a match is found. See
165
186
  * `gateToolCall` in `permissions.ts`.
166
187
  *
167
188
  * The list is best-effort and additive: operators can extend it at
@@ -183,6 +204,7 @@ exports.INTERACTIVE_TOOL_CATALOG = {
183
204
  amp: [],
184
205
  pi: [],
185
206
  antigravity: [],
207
+ cursor: [],
186
208
  };
187
209
  function parseEnvInteractiveTools() {
188
210
  const raw = process.env.ORY_INTERACTIVE_TOOLS;
@@ -215,3 +237,132 @@ function getInteractiveToolCatalog(harness) {
215
237
  function isInteractiveTool(harness, toolName) {
216
238
  return getInteractiveToolCatalog(harness).includes(toolName);
217
239
  }
240
+ // ─── Shell-command decomposition (issue #76) ───────────────────────
241
+ /**
242
+ * Per-harness name(s) of the shell/exec tool whose input is decomposed into
243
+ * `ShellTool:<word>#use` sub-checks. Validated against each harness's
244
+ * types/handlers. Names vary (`Bash`, `shell`, `exec`, `run_command`, …) so a
245
+ * precise map is used rather than the {@link isBashLikeTool} heuristic.
246
+ */
247
+ exports.SHELL_TOOL_CATALOG = {
248
+ "claude-code": ["Bash"],
249
+ "claude-agent-sdk": ["Bash"],
250
+ codex: ["Bash"],
251
+ // Gemini CLI's hook payload has been observed with both the short `shell`
252
+ // name and the fuller `run_shell_command` id across versions; accept both so
253
+ // decomposition fires regardless. `ORY_SHELL_TOOLS` remains the escape hatch.
254
+ "gemini-cli": ["shell", "run_shell_command"],
255
+ openclaw: ["exec"],
256
+ opencode: ["bash"],
257
+ continue: ["Bash"],
258
+ goose: ["shell"],
259
+ cline: ["execute_command"],
260
+ amp: ["Bash"],
261
+ pi: ["bash"],
262
+ antigravity: ["run_command"],
263
+ };
264
+ function parseEnvList(raw) {
265
+ if (!raw)
266
+ return [];
267
+ return raw
268
+ .split(",")
269
+ .map((v) => v.trim())
270
+ .filter((v) => v.length > 0);
271
+ }
272
+ /**
273
+ * Shell-tool names for a harness, merged with the operator env extension
274
+ * `ORY_SHELL_TOOLS` (same pattern as `ORY_INTERACTIVE_TOOLS`) so a vendor tool
275
+ * rename can be handled without a plugin release.
276
+ */
277
+ function getShellToolCatalog(harness) {
278
+ const builtIn = exports.SHELL_TOOL_CATALOG[harness] ?? [];
279
+ const envExtra = parseEnvList(process.env.ORY_SHELL_TOOLS);
280
+ if (envExtra.length === 0)
281
+ return builtIn;
282
+ return Array.from(new Set([...builtIn, ...envExtra]));
283
+ }
284
+ /** Is the named tool, for this harness, the shell/exec tool to decompose? */
285
+ function isShellTool(harness, toolName) {
286
+ return getShellToolCatalog(harness).includes(toolName);
287
+ }
288
+ /**
289
+ * Broad list of shell command *words* — common dev binaries, sensitive
290
+ * binaries, and builtins — that an Ory project is expected to grant `use` on
291
+ * so enforce mode is practical out of the box; revoke a specific word in the
292
+ * Ory Console to close it. Builtins are included because they are a real
293
+ * bypass surface (`echo "$SECRET" >/dev/tcp/…`, `read`, redirection tricks).
294
+ * `permissions status` reports coverage against this list.
295
+ */
296
+ exports.SHELL_COMMAND_CATALOG = [
297
+ // network / transfer
298
+ "curl", "wget", "nc", "ncat", "socat", "ssh", "scp", "sftp", "rsync", "ftp",
299
+ "telnet",
300
+ // databases
301
+ "psql", "mysql", "mysqldump", "mongo", "mongosh", "redis-cli", "sqlite3",
302
+ // interpreters
303
+ "python", "python3", "node", "ruby", "perl", "php", "deno", "bun",
304
+ // shells / eval
305
+ "sh", "bash", "dash", "zsh", "ksh", "eval", "exec", "source",
306
+ // file read / transform
307
+ "cat", "less", "more", "head", "tail", "grep", "egrep", "fgrep", "sed", "awk",
308
+ "tee", "cut", "sort", "uniq", "tr", "xxd", "od", "strings", "dd", "base64",
309
+ // filesystem mutation
310
+ "rm", "mv", "cp", "ln", "chmod", "chown", "chgrp", "mkdir", "rmdir", "touch",
311
+ "truncate",
312
+ // discovery
313
+ "ls", "find", "which", "stat", "file", "du", "df",
314
+ // vcs / package / cloud / container
315
+ "git", "npm", "npx", "pnpm", "yarn", "pip", "pip3", "docker", "kubectl",
316
+ "helm", "aws", "gcloud", "az", "gh", "terraform",
317
+ // process / wrappers
318
+ "sudo", "env", "xargs", "kill", "pkill", "ps",
319
+ // builtins
320
+ "cd", "echo", "printf", "export", "pwd", "test", "read", "set", "unset",
321
+ "alias", "true", "false", ":",
322
+ ];
323
+ /**
324
+ * The shell command word list, merged with the operator env extension
325
+ * `ORY_SHELL_COMMANDS`.
326
+ */
327
+ function getShellCommandCatalog() {
328
+ const envExtra = parseEnvList(process.env.ORY_SHELL_COMMANDS);
329
+ if (envExtra.length === 0)
330
+ return exports.SHELL_COMMAND_CATALOG;
331
+ return Array.from(new Set([...exports.SHELL_COMMAND_CATALOG, ...envExtra]));
332
+ }
333
+ /**
334
+ * Keto namespace for shell-command sub-tools. Defaults to `ShellTool`,
335
+ * parallel to `mcp_servers` / `mcp_tools`. The object is the bare word (`curl`),
336
+ * relation `use`. Override with `ORY_SHELL_COMMAND_NAMESPACE`.
337
+ */
338
+ function resolveShellCommandNamespace() {
339
+ return process.env.ORY_SHELL_COMMAND_NAMESPACE ?? opl_js_1.DEFAULT_SHELL_NAMESPACE;
340
+ }
341
+ /** Command-input field spellings across harnesses (claude/codex/…, antigravity). */
342
+ const SHELL_COMMAND_FIELDS = [
343
+ "command",
344
+ "cmd",
345
+ "command_line",
346
+ "commandLine",
347
+ "CommandLine",
348
+ ];
349
+ /**
350
+ * Extract the shell command string from a harness's tool-args object — the same
351
+ * object the handler already passes to `summarizeToolInput`. Returns `undefined`
352
+ * when the tool isn't a shell tool for this harness or no command field is
353
+ * present (the gate then skips decomposition and degrades to the top-level
354
+ * `Bash` check — it never breaks).
355
+ */
356
+ function extractShellCommand(harness, toolName, args) {
357
+ if (!isShellTool(harness, toolName))
358
+ return undefined;
359
+ if (!args || typeof args !== "object")
360
+ return undefined;
361
+ const obj = args;
362
+ for (const field of SHELL_COMMAND_FIELDS) {
363
+ const value = obj[field];
364
+ if (typeof value === "string" && value.length > 0)
365
+ return value;
366
+ }
367
+ return undefined;
368
+ }
@@ -2,11 +2,11 @@
2
2
  * Privacy-preserving summarizers for tool input/output.
3
3
  *
4
4
  * Plugins must NEVER record raw tool content (commands, file bodies,
5
- * URLs with query strings, prompts) directly into trace attributes.
5
+ * URLs with query strings, prompts) directly into activity attributes.
6
6
  * These helpers emit only:
7
7
  * - byte size (proxy for "how much data")
8
8
  * - sha256-truncated content hash (for dedup / "we've seen this before")
9
- * - a small set of safe per-tool fields (exit code, file path, URL host)
9
+ * - a small set of safe per-tool fields (exit code and path/host hashes)
10
10
  *
11
11
  * Anything that could reflect content goes through here so the redaction
12
12
  * policy lives in one place.
@@ -17,10 +17,11 @@ export interface ToolInputSummary {
17
17
  /** Bash/shell — not the command itself. */
18
18
  bashCommandBytes?: number;
19
19
  bashCommandHash?: string;
20
- /** File ops — path is metadata, file *contents* are not captured. */
21
- filePath?: string;
22
- /** Network ops — host only, never path or query string. */
23
- urlHost?: string;
20
+ /** File ops — raw paths can contain user/workspace names. */
21
+ filePathBytes?: number;
22
+ filePathHash?: string;
23
+ /** Network ops — even hosts can identify private infrastructure. */
24
+ urlHostHash?: string;
24
25
  }
25
26
  export interface ToolOutputSummary {
26
27
  outputBytes?: number;
@@ -3,11 +3,11 @@
3
3
  * Privacy-preserving summarizers for tool input/output.
4
4
  *
5
5
  * Plugins must NEVER record raw tool content (commands, file bodies,
6
- * URLs with query strings, prompts) directly into trace attributes.
6
+ * URLs with query strings, prompts) directly into activity attributes.
7
7
  * These helpers emit only:
8
8
  * - byte size (proxy for "how much data")
9
9
  * - sha256-truncated content hash (for dedup / "we've seen this before")
10
- * - a small set of safe per-tool fields (exit code, file path, URL host)
10
+ * - a small set of safe per-tool fields (exit code and path/host hashes)
11
11
  *
12
12
  * Anything that could reflect content goes through here so the redaction
13
13
  * policy lives in one place.
@@ -38,18 +38,19 @@ function summarizeToolInput(toolName, input) {
38
38
  result.bashCommandBytes = Buffer.byteLength(command, "utf8");
39
39
  result.bashCommandHash = sha256Prefix(command);
40
40
  }
41
- // File ops: path is metadata. We do NOT capture file content/body fields
41
+ // File ops: summarize paths because they can contain user/workspace names.
42
42
  // (e.g. `content`, `new_string`, `text`, `body`).
43
43
  const filePath = pickString(obj, ["file_path", "filePath", "path"]);
44
44
  if (filePath !== undefined) {
45
- result.filePath = filePath;
45
+ result.filePathBytes = Buffer.byteLength(filePath, "utf8");
46
+ result.filePathHash = sha256Prefix(filePath);
46
47
  }
47
48
  // Network/fetch tools: extract host only, drop path + query string.
48
49
  const url = pickString(obj, ["url", "URL", "endpoint"]);
49
50
  if (url !== undefined) {
50
51
  const host = extractHost(url);
51
52
  if (host)
52
- result.urlHost = host;
53
+ result.urlHostHash = sha256Prefix(host);
53
54
  }
54
55
  }
55
56
  return result;
package/dist/types.d.ts CHANGED
@@ -46,7 +46,17 @@ export interface BatchPermissionResult {
46
46
  checks: PermissionCheck[];
47
47
  checkedAt: string;
48
48
  }
49
- export type OryErrorCode = "session_inactive" | "session_aal2_required" | "forbidden" | "rate_limited" | "not_found" | "network_error" | "unknown";
49
+ export type OryErrorCode = "session_inactive" | "session_aal2_required" | "forbidden" | "rate_limited" | "not_found" | "network_error"
50
+ /**
51
+ * TLS/certificate verification failed — an expired, self-signed, or otherwise
52
+ * untrusted certificate on the project host. Distinct from `network_error`
53
+ * because the posture differs: a socket-level blip is a transport failure and
54
+ * fails open, whereas a certificate failure means we cannot establish *who we
55
+ * are talking to*, so the check result cannot be trusted at all. It is also
56
+ * precisely the failure an active network attacker produces, so under
57
+ * `enforce` it denies rather than passing the tool through.
58
+ */
59
+ | "tls_error" | "unknown";
50
60
  export interface OryError {
51
61
  code: OryErrorCode;
52
62
  status?: number;
@@ -1,27 +1,63 @@
1
1
  /**
2
- * Credential teardown shared by every plugin's `uninstall` command.
2
+ * Config teardown shared by every plugin's `uninstall` command.
3
3
  *
4
- * Uninstalling a plugin removes all *authentication* state from the shared
5
- * config file while preserving *configuration* (project URL, OAuth2 client
6
- * id, audit mode, permission mode, user-login flag). The intent is that a
7
- * later re-install re-authenticates from scratch the user logs in again
8
- * and the agent registers a fresh dynamic client — rather than silently
9
- * inheriting stale credentials.
4
+ * `config.json` is a **single shared file** every installed harness plugin
5
+ * reads, so teardown is scoped: uninstalling one plugin must not knock the
6
+ * others back to an unconfigured state. Two scopes exist, and which one runs is
7
+ * decided from the install registry, not from a flag the caller guesses at:
10
8
  *
11
- * What is removed:
9
+ * - **`harness` scope** — other plugins are still installed. Only the
10
+ * departing harness's own identities go: the agent and sub-agent DCR
11
+ * clients of **every session it retains** are revoked (RFC 7592), and its
12
+ * entries are removed from `agent.dynamic` / `agent.subAgents`. Shared
13
+ * state — `projectUrl`, `oauth2ClientId`, the user's tokens, the permission
14
+ * mode — is left exactly as it was.
15
+ * - **`all` scope** — the departing harness was the last one installed, or
16
+ * the caller passed `purge: true`. Every harness's clients are revoked and
17
+ * the config file is left as an empty object, so a re-install starts from a
18
+ * clean slate rather than inheriting stale state.
19
+ *
20
+ * What an `all` teardown removes:
12
21
  * - the user's persisted OAuth2 tokens (`user`)
13
- * - the agent's dynamic-registration credentials and every sub-agent
14
- * registration (`agent`)
15
- * - the operator-supplied static agent API key (`apiKey`)
22
+ * - every harness's agent and sub-agent registrations, across all sessions
23
+ * (`agent`)
24
+ * - all configuration values (`projectUrl`, `projectId`, `projectName`,
25
+ * `workspaceName`, `oauth2ClientId`, `permissionModeCache`,
26
+ * `userSubjectNamespace`, `installedHarnesses`)
16
27
  * - any dangling PKCE-flight lock
17
28
  *
18
- * Before clearing local state, each dynamically-registered client (the
19
- * agent and each sub-agent) is revoked server-side via RFC 7592 so the
20
- * Ory project isn't left with orphaned OAuth2 clients accumulating across
21
- * install/uninstall cycles. Revocation is best-effort: failures warn and
22
- * local state is cleared regardless.
29
+ * Server-side teardown runs first, best-effort, and covers exactly the
30
+ * resources the plugin itself created: each dynamically-registered OAuth2
31
+ * client in scope (the agent and every sub-agent, for every retained session)
32
+ * is revoked via RFC 7592, so the Ory project isn't left with orphaned clients
33
+ * accumulating across install/uninstall cycles.
34
+ *
35
+ * Nothing in the project's **permission** state is touched. Grants, blocks,
36
+ * delegation edges, and the permission model are provisioned in the Ory
37
+ * Console (Agent Security) and owned there — the plugin has no Keto write
38
+ * path to unwind them with, and deleting an admin's grants on uninstall was
39
+ * never its call to make. The shared user-login PKCE OAuth2 client
40
+ * (`oauth2ClientId`) is likewise left in place: it is a public, identity-free
41
+ * client that every user and every harness install on the project reuses, so
42
+ * deleting it would break other installations. Both are only forgotten
43
+ * locally. Every server-side step is best-effort: failures warn and local
44
+ * state is cleared regardless.
23
45
  */
46
+ import { type OryPluginConfig } from "./config.js";
24
47
  export interface ClearCredentialsOptions {
48
+ /**
49
+ * The harness being uninstalled. When other harness plugins are still
50
+ * installed, teardown is scoped to this one and the shared config survives.
51
+ * Omitted ⇒ the caller can't say whose state this is, so the whole config is
52
+ * wiped (the pre-scoping behavior).
53
+ */
54
+ harness?: string;
55
+ /**
56
+ * Force a full wipe of the shared config and every harness's credentials,
57
+ * even when other plugins are still installed. Backs an explicit
58
+ * `uninstall --purge`. Default: `false`.
59
+ */
60
+ purge?: boolean;
25
61
  /**
26
62
  * Best-effort RFC 7592 `DELETE` of the agent + sub-agent dynamic clients
27
63
  * before clearing local state. Default: `true`. Set `false` to clear
@@ -34,14 +70,33 @@ export interface ClearCredentialsOptions {
34
70
  export interface ClearCredentialsResult {
35
71
  /** Whether any credential was present before clearing. */
36
72
  hadCredentials: boolean;
73
+ /**
74
+ * `harness` — only the departing harness's state was removed and the shared
75
+ * config survives; `all` — everything was wiped (last plugin, or `purge`).
76
+ */
77
+ scope: "harness" | "all";
78
+ /** Harness plugins still installed after this uninstall. */
79
+ remainingHarnesses: string[];
37
80
  /** Number of dynamic clients revoked server-side (includes already-gone). */
38
81
  revoked: number;
39
82
  /** Number of dynamic clients whose server-side revoke failed. */
40
83
  revokeFailures: number;
41
84
  }
42
85
  /**
43
- * Remove all authentication state from the shared config file, revoking
44
- * dynamically-registered clients server-side first. Preserves every
45
- * non-credential configuration value. Never throws.
86
+ * The harness plugins installed against the shared config, reconciled from
87
+ * every source that evidences one.
88
+ *
89
+ * The `installedHarnesses` registry is authoritative, but it only exists on
90
+ * configs written since it was introduced. Treating its absence as "nothing is
91
+ * installed" would make the first uninstall on an upgraded machine wipe the
92
+ * config every other plugin depends on — precisely the failure being fixed. So
93
+ * the runtime manifest (what each harness has wired) and the persisted
94
+ * credentials (which harnesses hold an identity) are folded in as evidence.
95
+ */
96
+ export declare function resolveInstalledHarnesses(config?: OryPluginConfig): string[];
97
+ /**
98
+ * Tear down the departing harness's state, wiping the whole shared config only
99
+ * when nothing else still depends on it. Dynamically-registered clients in
100
+ * scope are revoked server-side first. Never throws.
46
101
  */
47
102
  export declare function clearCredentialsForUninstall(options?: ClearCredentialsOptions): Promise<ClearCredentialsResult>;