@ory/argus 0.14.0 → 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 (150) hide show
  1. package/README.md +31 -46
  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 +39 -91
  6. package/assets/skills/ory-e2b-sandbox/SKILL.md +18 -17
  7. package/assets/skills/ory-temporal-worker/SKILL.md +31 -33
  8. package/assets/skills/permissions-onboarding/SKILL.md +131 -104
  9. package/dist/adapters.d.ts +88 -32
  10. package/dist/adapters.js +443 -164
  11. package/dist/agent-auth.d.ts +226 -67
  12. package/dist/agent-auth.js +951 -205
  13. package/dist/auth-store.d.ts +37 -2
  14. package/dist/auth-store.js +37 -3
  15. package/dist/auth.d.ts +33 -4
  16. package/dist/auth.js +163 -20
  17. package/dist/bash-parser.d.ts +98 -0
  18. package/dist/bash-parser.js +396 -0
  19. package/dist/branding.d.ts +77 -16
  20. package/dist/branding.js +93 -23
  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 -239
  26. package/dist/client.d.ts +175 -138
  27. package/dist/client.js +672 -391
  28. package/dist/config.d.ts +217 -54
  29. package/dist/config.js +461 -42
  30. package/dist/context.d.ts +10 -0
  31. package/dist/context.js +21 -0
  32. package/dist/contract-suite.d.ts +3 -5
  33. package/dist/contract-suite.js +75 -47
  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 +28 -23
  47. package/dist/index.js +173 -58
  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.js +7 -7
  54. package/dist/local/index.d.ts +2 -2
  55. package/dist/local/index.js +24 -10
  56. package/dist/local/manager.d.ts +20 -1
  57. package/dist/local/manager.js +159 -36
  58. package/dist/local/ports.d.ts +158 -0
  59. package/dist/local/ports.js +443 -0
  60. package/dist/local/seed.d.ts +13 -5
  61. package/dist/local/seed.js +62 -38
  62. package/dist/logger.d.ts +54 -25
  63. package/dist/logger.js +329 -63
  64. package/dist/mcp.d.ts +2 -2
  65. package/dist/mcp.js +10 -5
  66. package/dist/mirror-bootstrap.d.ts +48 -0
  67. package/dist/mirror-bootstrap.js +254 -0
  68. package/dist/opl.d.ts +289 -0
  69. package/dist/opl.js +446 -0
  70. package/dist/permission-mode.d.ts +87 -0
  71. package/dist/permission-mode.js +307 -0
  72. package/dist/permissions-cli.d.ts +13 -49
  73. package/dist/permissions-cli.js +154 -348
  74. package/dist/permissions.d.ts +148 -38
  75. package/dist/permissions.js +591 -45
  76. package/dist/post-install.d.ts +33 -0
  77. package/dist/post-install.js +127 -0
  78. package/dist/read-credential.d.ts +65 -0
  79. package/dist/read-credential.js +86 -0
  80. package/dist/registry/cli.js +5 -2
  81. package/dist/registry/config.d.ts +0 -17
  82. package/dist/registry/config.js +0 -23
  83. package/dist/registry/index.d.ts +1 -1
  84. package/dist/registry/index.js +2 -2
  85. package/dist/registry/manager.d.ts +4 -21
  86. package/dist/registry/manager.js +21 -37
  87. package/dist/runtime-credential.d.ts +140 -0
  88. package/dist/runtime-credential.js +572 -0
  89. package/dist/runtime.d.ts +408 -0
  90. package/dist/runtime.js +748 -0
  91. package/dist/setup.d.ts +23 -47
  92. package/dist/setup.js +59 -116
  93. package/dist/skills.js +0 -7
  94. package/dist/status-cli.d.ts +27 -11
  95. package/dist/status-cli.js +121 -115
  96. package/dist/status-data.d.ts +116 -17
  97. package/dist/status-data.js +121 -38
  98. package/dist/subject.d.ts +126 -20
  99. package/dist/subject.js +215 -30
  100. package/dist/testing.d.ts +74 -38
  101. package/dist/testing.js +185 -68
  102. package/dist/tool-catalog.d.ts +53 -11
  103. package/dist/tool-catalog.js +164 -13
  104. package/dist/tool-metadata.d.ts +7 -6
  105. package/dist/tool-metadata.js +6 -5
  106. package/dist/types.d.ts +11 -1
  107. package/dist/uninstall.d.ts +71 -34
  108. package/dist/uninstall.js +217 -85
  109. package/dist/user-login.d.ts +9 -10
  110. package/dist/user-login.js +56 -75
  111. package/dist/watch-cli.d.ts +6 -0
  112. package/dist/watch-cli.js +217 -0
  113. package/package.json +4 -27
  114. package/assets/commands/dashboard.md +0 -34
  115. package/dist/dashboard-cli.d.ts +0 -8
  116. package/dist/dashboard-cli.js +0 -70
  117. package/dist/dev.d.ts +0 -103
  118. package/dist/dev.js +0 -583
  119. package/dist/interactive-setup.d.ts +0 -286
  120. package/dist/interactive-setup.js +0 -1734
  121. package/dist/local/jaeger-main.d.ts +0 -13
  122. package/dist/local/jaeger-main.js +0 -85
  123. package/dist/local/jaeger.d.ts +0 -50
  124. package/dist/local/jaeger.js +0 -162
  125. package/dist/otel/exporter.d.ts +0 -17
  126. package/dist/otel/exporter.js +0 -12
  127. package/dist/otel/index.d.ts +0 -2
  128. package/dist/otel/index.js +0 -8
  129. package/dist/otel/otlp.d.ts +0 -103
  130. package/dist/otel/otlp.js +0 -385
  131. package/dist/project-api-key.d.ts +0 -69
  132. package/dist/project-api-key.js +0 -147
  133. package/dist/setup-actions.d.ts +0 -232
  134. package/dist/setup-actions.js +0 -507
  135. package/dist/tracer.d.ts +0 -190
  136. package/dist/tracer.js +0 -481
  137. package/dist/watch-sandbox.d.ts +0 -9
  138. package/dist/watch-sandbox.js +0 -81
  139. package/dist/web/api.d.ts +0 -33
  140. package/dist/web/api.js +0 -294
  141. package/dist/web/launch.d.ts +0 -11
  142. package/dist/web/launch.js +0 -96
  143. package/dist/web/server.d.ts +0 -20
  144. package/dist/web/server.js +0 -233
  145. package/dist/web/types.d.ts +0 -65
  146. package/dist/web/types.js +0 -2
  147. package/dist/webapp/assets/index-Wucl4SZs.css +0 -1
  148. package/dist/webapp/assets/index-m-GtEdq0.js +0 -49
  149. package/dist/webapp/favicon.ico +0 -0
  150. package/dist/webapp/index.html +0 -15
package/dist/cli.js CHANGED
@@ -38,8 +38,6 @@ exports.printOryConfig = printOryConfig;
38
38
  exports.printEnvironment = printEnvironment;
39
39
  exports.printLogTail = printLogTail;
40
40
  exports.printEnvHelp = printEnvHelp;
41
- exports.printTraceTail = printTraceTail;
42
- exports.runWatchCommand = runWatchCommand;
43
41
  exports.isTtyAvailable = isTtyAvailable;
44
42
  exports.promptOnTty = promptOnTty;
45
43
  exports.promptForProjectUrl = promptForProjectUrl;
@@ -49,31 +47,34 @@ const fs = __importStar(require("node:fs"));
49
47
  const readline = __importStar(require("node:readline"));
50
48
  const config_js_1 = require("./config.js");
51
49
  const agent_auth_js_1 = require("./agent-auth.js");
52
- const tracer_js_1 = require("./tracer.js");
50
+ const logger_js_1 = require("./logger.js");
53
51
  const cli_invocation_js_1 = require("./cli-invocation.js");
54
52
  const client_js_1 = require("./client.js");
55
53
  /**
56
54
  * Shared `configure` command implementation for all plugin CLIs.
57
55
  *
58
- * Parses --project-url, --api-key, and --oauth2-client-id from args.
59
- * If none is provided, shows the current configuration. Otherwise saves
60
- * the provided values.
56
+ * `configure` only ever touches the **security** half of a plugin. The
57
+ * developer-experience half skills, commands, the local Ory stack, the MCP
58
+ * server, local activity logging — is installed unconditionally and has nothing to
59
+ * configure, so it is never mentioned here as an alternative or a fallback.
61
60
  *
62
- * When `--project-url` is provided, requires the OAuth2 client id to be
63
- * resolvable from one of: the `--oauth2-client-id` flag, the
64
- * `ORY_OAUTH2_CLIENT_ID` env var, or an already-persisted value in the
65
- * shared config. The PKCE browser flow (the user login runs every session)
66
- * can't self-register that client surfacing the requirement here keeps
67
- * users from a silently-broken setup later.
61
+ * Connecting takes the project URL and canonical broker origin from the Ory
62
+ * Console (Agent Security). The public OAuth2 client id defaults to the reserved
63
+ * Agent Security login client and can be overridden for custom deployments.
64
+ * Existing configurations use the project URL as a legacy broker fallback.
65
+ * `--disconnect` clears all three stored connection values.
66
+ *
67
+ * With no arguments, prints what is currently resolved.
68
68
  */
69
69
  function runConfigureCommand(binName, args) {
70
70
  let projectUrl;
71
+ let agentSecurityUrl;
71
72
  let projectId;
72
- let apiKey;
73
73
  let oauth2ClientId;
74
- let auditOnly = false;
75
- // `undefined` = not specified; a string sets it; empty string ("") clears it
76
- // (via --no-user-subject-namespace) to fall back to direct SubjectID.
74
+ let disconnect = false;
75
+ // `undefined` = not specified; a string overrides the default `User`
76
+ // namespace; empty string ("") clears the override (via
77
+ // --no-user-subject-namespace) so the default `User` namespace applies.
77
78
  let userSubjectNamespace;
78
79
  for (let i = 0; i < args.length; i++) {
79
80
  switch (args[i]) {
@@ -86,14 +87,14 @@ function runConfigureCommand(binName, args) {
86
87
  case "--project-id":
87
88
  projectId = args[++i];
88
89
  break;
89
- case "--api-key":
90
- apiKey = args[++i];
90
+ case "--agent-security-url":
91
+ agentSecurityUrl = args[++i];
91
92
  break;
92
93
  case "--oauth2-client-id":
93
94
  oauth2ClientId = args[++i];
94
95
  break;
95
- case "--audit-only":
96
- auditOnly = true;
96
+ case "--disconnect":
97
+ disconnect = true;
97
98
  break;
98
99
  case "--user-subject-namespace":
99
100
  userSubjectNamespace = args[++i];
@@ -104,10 +105,10 @@ function runConfigureCommand(binName, args) {
104
105
  }
105
106
  }
106
107
  if (!projectUrl &&
108
+ !agentSecurityUrl &&
107
109
  !projectId &&
108
- !apiKey &&
109
110
  !oauth2ClientId &&
110
- !auditOnly &&
111
+ !disconnect &&
111
112
  userSubjectNamespace === undefined) {
112
113
  const resolved = (0, config_js_1.resolveConfig)();
113
114
  const configPath = (0, config_js_1.getConfigPath)();
@@ -116,135 +117,163 @@ function runConfigureCommand(binName, args) {
116
117
  console.log("");
117
118
  console.log(`Config file: ${configPath}${fs.existsSync(configPath) ? "" : " (not created yet)"}`);
118
119
  console.log(`Project URL: ${resolved.projectUrl ?? "(not set)"} [${resolved.projectUrlSource}]`);
119
- console.log(`Project ID: ${resolved.projectId ?? "(not set)"} [${resolved.projectIdSource}]`);
120
- console.log(`API Key: ${resolved.apiKey ? "(set)" : "(not set)"} [${resolved.apiKeySource}]`);
120
+ console.log(`Agent Security URL: ${resolved.agentSecurityUrl ?? "(not set)"} [${resolved.agentSecurityUrlSource}]`);
121
121
  console.log(`OAuth2 Client ID: ${resolved.oauth2ClientId ?? "(not set)"} [${resolved.oauth2ClientIdSource}]`);
122
- console.log(`Audit Only: ${resolved.auditOnly ? "yes" : "no"}`);
123
- console.log(`User subject ns: ${resolved.userSubjectNamespace ?? "(direct subject id)"} [${resolved.userSubjectNamespaceSource}]`);
122
+ console.log(`Agent Security: ${resolved.security.connected ? "connected" : `not connected (${(0, config_js_1.describeSecurityGap)(resolved.security)})`}`);
124
123
  console.log("");
125
- console.log("To configure:");
126
- console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]`);
124
+ console.log(`Project ID: ${resolved.projectId ?? "(not set)"} [${resolved.projectIdSource}]`);
125
+ console.log(`User subject ns: ${resolved.userSubjectNamespace ?? "User (default)"} [${resolved.userSubjectNamespaceSource}]`);
127
126
  console.log("");
128
- console.log("To address the user as a SubjectSet in permission checks (must match your tuples):");
129
- console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --user-subject-namespace User (clear with --no-user-subject-namespace)`);
127
+ if (resolved.security.connected) {
128
+ console.log("Sign-in and permission checks are running. Tool grants, explicit blocks,");
129
+ console.log("and the observe/enforce posture are managed in the Ory Console (Agent Security).");
130
+ console.log("");
131
+ console.log("To disconnect (skills, commands, the local stack, and activity logging keep working):");
132
+ console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --disconnect`);
133
+ }
134
+ else {
135
+ console.log("Skills, commands, the local Ory stack, the MCP server, and local activity logging are");
136
+ console.log("installed and working — they don't need a project. Only sign-in and permission");
137
+ console.log("checks are off.");
138
+ console.log("");
139
+ console.log("To turn them on, supply the project and canonical Agent Security origin:");
140
+ console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --project-url <URL> --agent-security-url <URL>`);
141
+ }
130
142
  console.log("");
131
- console.log("To enable audit logging only (no auth or permission checks):");
132
- console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --audit-only`);
143
+ console.log("Users are addressed as SubjectSets (default namespace 'User'). To override the namespace (must match how the project's permissions were written):");
144
+ console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --user-subject-namespace <Namespace> (reset to 'User' with --no-user-subject-namespace)`);
133
145
  console.log("");
134
146
  console.log("Or set environment variables:");
135
147
  console.log(" export ORY_PROJECT_URL=https://your-project.projects.oryapis.com");
148
+ console.log(" export ORY_AGENT_SECURITY_URL=https://agents.console.ory.com");
136
149
  console.log(" # ORY_SDK_URL (the Ory Console 'Get Started' name) is accepted as an alias.");
137
150
  console.log(" # ORY_PROJECT_ID=<project uuid> is stored for admin operations (not an SDK URL).");
138
- console.log(" export ORY_OAUTH2_CLIENT_ID=<public OAuth2 client id>");
139
- console.log(" export ORY_AGENT_API_KEY=ory_pat_...");
151
+ console.log(` # ORY_OAUTH2_CLIENT_ID defaults to ${config_js_1.DEFAULT_OAUTH2_CLIENT_ID}.`);
140
152
  return;
141
153
  }
142
- if (auditOnly) {
143
- // Audit-only mode: set the kill switch (disables auth and checks entirely).
144
- (0, config_js_1.saveConfig)({ auditOnly: true });
154
+ if (disconnect) {
155
+ // Clear both required values. Everything else the plugin does is
156
+ // unaffected this turns off sign-in and permission checks, nothing more.
157
+ (0, config_js_1.saveConfig)({ projectUrl: null, agentSecurityUrl: null, oauth2ClientId: null });
145
158
  const configPath = (0, config_js_1.getConfigPath)();
146
159
  console.log(`Configuration saved to ${configPath}`);
147
- console.log(" Mode: audit-only (logging enabled, auth and permission checks disabled)");
160
+ console.log(" Ory Agent Security: disconnected (no sign-in, no permission checks)");
148
161
  console.log("");
149
- console.log("This configuration is shared across all Ory agent plugins.");
162
+ console.log("Skills, commands, the local Ory stack, the MCP server, and local activity logging");
163
+ console.log("are unaffected and keep working.");
164
+ if (process.env.ORY_PROJECT_URL || process.env.ORY_SDK_URL || process.env.ORY_AGENT_SECURITY_URL || process.env.ORY_OAUTH2_CLIENT_ID) {
165
+ console.log("");
166
+ console.log("Note: ORY_PROJECT_URL / ORY_SDK_URL / ORY_AGENT_SECURITY_URL /");
167
+ console.log("ORY_OAUTH2_CLIENT_ID are set in your");
168
+ console.log("environment and take precedence over the config file — unset them to stay");
169
+ console.log("disconnected.");
170
+ }
150
171
  return;
151
172
  }
152
- // When --project-url / --sdk-url is provided we need a usable OAuth2 client
153
- // id for the user PKCE flow block the configure with a clear error if none
154
- // is reachable from the flag, env, or already-persisted config. A bare
155
- // --project-id sets an admin identifier only (not an SDK URL), so it doesn't
156
- // trigger this gate on its own.
157
- if (projectUrl) {
158
- const persisted = (0, config_js_1.loadConfig)().oauth2ClientId;
159
- const envClientId = process.env.ORY_OAUTH2_CLIENT_ID;
160
- const resolved = oauth2ClientId ?? envClientId ?? persisted;
161
- if (!resolved) {
162
- console.error("Error: --project-url requires an OAuth2 client id for the user PKCE flow.\n" +
173
+ // Agent Security needs the project and broker origin. The login client uses
174
+ // the reserved default unless an explicit flag, environment variable, or
175
+ // persisted override is present. A bare --project-id is an admin identifier
176
+ // only and doesn't connect anything, so it doesn't trigger this.
177
+ if (agentSecurityUrl) {
178
+ try {
179
+ const parsed = new URL(agentSecurityUrl);
180
+ if ((parsed.protocol !== "https:" && parsed.origin !== config_js_1.LOCAL_AGENT_SECURITY_URL) ||
181
+ !parsed.hostname ||
182
+ parsed.username ||
183
+ parsed.password ||
184
+ (parsed.pathname !== "" && parsed.pathname !== "/") ||
185
+ parsed.search ||
186
+ parsed.hash) {
187
+ throw new Error("invalid origin");
188
+ }
189
+ agentSecurityUrl = parsed.origin;
190
+ }
191
+ catch {
192
+ console.error("Error: --agent-security-url must be an HTTPS origin without credentials, path, query, or fragment " +
193
+ `(for example ${config_js_1.DEFAULT_AGENT_SECURITY_URL} or ${config_js_1.LOCAL_AGENT_SECURITY_URL}).`);
194
+ process.exit(1);
195
+ }
196
+ }
197
+ if (projectUrl || agentSecurityUrl || oauth2ClientId) {
198
+ const persisted = (0, config_js_1.loadConfig)();
199
+ const resolvedUrl = projectUrl ?? process.env.ORY_PROJECT_URL ?? process.env.ORY_SDK_URL ?? persisted.projectUrl;
200
+ if (!resolvedUrl) {
201
+ console.error("Error: Ory Agent Security needs --project-url <URL>.\n" +
202
+ "\n" +
203
+ "Copy the project URL from the Ory Console under Agent Security:\n" +
204
+ ` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --project-url <URL>\n` +
163
205
  "\n" +
164
- "Provide one with --oauth2-client-id <id>, or set ORY_OAUTH2_CLIENT_ID in your\n" +
165
- "environment. The client must be a public OAuth2 client (no client secret,\n" +
166
- "token_endpoint_auth_method=none) registered in your Ory project with all four\n" +
167
- "loopback redirect URIs:\n" +
168
- " http://127.0.0.1:47823/callback\n" +
169
- " http://127.0.0.1:47824/callback\n" +
170
- " http://127.0.0.1:47825/callback\n" +
171
- " http://127.0.0.1:47826/callback\n" +
206
+ "(--agent-security-url and --oauth2-client-id override their production defaults.)\n" +
172
207
  "\n" +
173
- "Create it with:\n" +
174
- " ory create oauth2-client --project <project-id> \\\n" +
175
- " --name \"ory-agent-plugin\" \\\n" +
176
- " --grant-type authorization_code,refresh_token \\\n" +
177
- " --response-type code \\\n" +
178
- " --scope openid,offline_access \\\n" +
179
- " --token-endpoint-auth-method none \\\n" +
180
- " --redirect-uri http://127.0.0.1:47823/callback \\\n" +
181
- " --redirect-uri http://127.0.0.1:47824/callback \\\n" +
182
- " --redirect-uri http://127.0.0.1:47825/callback \\\n" +
183
- " --redirect-uri http://127.0.0.1:47826/callback\n" +
208
+ "Connecting needs no workspace privilege. The plugin never provisions the project itself.\n" +
184
209
  "\n" +
185
- "If you only want audit logging (no auth or permission checks), use:\n" +
186
- ` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --audit-only`);
210
+ "Nothing was changed. The plugin's skills, commands, local Ory stack, MCP\n" +
211
+ "server, and local activity logging work without a project and are unaffected.");
187
212
  process.exit(1);
188
213
  }
214
+ if (projectUrl && !agentSecurityUrl && !process.env.ORY_AGENT_SECURITY_URL?.trim() && !persisted.agentSecurityUrl) {
215
+ agentSecurityUrl = config_js_1.DEFAULT_AGENT_SECURITY_URL;
216
+ }
189
217
  }
190
218
  const update = {};
191
219
  if (projectUrl)
192
220
  update.projectUrl = projectUrl;
221
+ if (agentSecurityUrl)
222
+ update.agentSecurityUrl = agentSecurityUrl;
193
223
  if (projectId)
194
224
  update.projectId = projectId;
195
- if (apiKey) {
196
- update.apiKey = apiKey;
197
- // A hand-supplied key has no known token id; drop any stale id so uninstall
198
- // never tries to delete a different token by an id that no longer matches.
199
- update.apiKeyId = null;
200
- }
201
225
  if (oauth2ClientId)
202
226
  update.oauth2ClientId = oauth2ClientId;
203
- // A non-empty namespace sets the SubjectSet namespace; the empty string
204
- // (from --no-user-subject-namespace) clears it (null ⇒ delete the key).
227
+ // A non-empty namespace overrides the default `User` SubjectSet namespace; the
228
+ // empty string (from --no-user-subject-namespace) clears the override (null ⇒
229
+ // delete the key) so the `User` default applies.
205
230
  if (userSubjectNamespace !== undefined) {
206
231
  update.userSubjectNamespace = userSubjectNamespace === "" ? null : userSubjectNamespace;
207
232
  }
208
- // Configuring an actual connection (a slug-based URL, api key, or client id)
209
- // implies leaving audit-only mode. A bare project id is only an admin
210
- // identifier — it doesn't connect anything on its own — so it doesn't flip
211
- // the flag.
212
- if (projectUrl || apiKey || oauth2ClientId)
213
- update.auditOnly = false;
214
233
  (0, config_js_1.saveConfig)(update);
215
234
  const configPath = (0, config_js_1.getConfigPath)();
216
235
  console.log(`Configuration saved to ${configPath}`);
217
236
  if (projectUrl)
218
237
  console.log(` Project URL: ${projectUrl}`);
238
+ if (agentSecurityUrl)
239
+ console.log(` Agent Security URL: ${agentSecurityUrl}`);
219
240
  if (projectId)
220
241
  console.log(` Project ID: ${projectId}`);
221
- if (apiKey)
222
- console.log(` API Key: (set)`);
223
242
  if (oauth2ClientId)
224
243
  console.log(` OAuth2 Client ID: ${oauth2ClientId}`);
225
244
  if (userSubjectNamespace !== undefined)
226
- console.log(` User subject namespace: ${userSubjectNamespace === "" ? "(cleared direct subject id)" : userSubjectNamespace}`);
245
+ console.log(` User subject namespace: ${userSubjectNamespace === "" ? "(reset to default 'User')" : userSubjectNamespace}`);
246
+ // Report the resulting posture rather than assuming the write connected
247
+ // anything: `--project-id` on its own changes no part of it.
248
+ const security = (0, config_js_1.resolveConfig)().security;
249
+ console.log(` Ory Agent Security: ${security.connected ? "connected — sign-in and permission checks run on the next session" : `not connected (${(0, config_js_1.describeSecurityGap)(security)})`}`);
227
250
  console.log("");
228
251
  console.log("This configuration is shared across all Ory agent plugins.");
229
- console.log("Environment variables (ORY_PROJECT_URL, ORY_AGENT_API_KEY, ORY_OAUTH2_CLIENT_ID) take precedence when set.");
252
+ console.log("Environment variables (ORY_PROJECT_URL, ORY_AGENT_SECURITY_URL, ORY_OAUTH2_CLIENT_ID) take precedence when set.");
230
253
  }
231
254
  /**
232
- * Print the "Configuration:" block showing Ory project URL and API key status.
255
+ * Print the "Configuration:" block showing the resolved Ory connection.
233
256
  */
234
257
  function printOryConfig() {
235
258
  const resolved = (0, config_js_1.resolveConfig)();
236
259
  const configPath = (0, config_js_1.getConfigPath)();
237
- const namespace = process.env.ORY_PERMISSION_NAMESPACE || "AgentTools";
260
+ const namespace = process.env.ORY_PERMISSION_NAMESPACE || "AgentTool";
238
261
  console.log("Configuration:");
239
262
  console.log(` Config file: ${configPath}`);
240
- console.log(` Mode: ${resolved.auditOnly ? "audit-only" : "full"}`);
263
+ console.log(` Agent Security: ${resolved.security.connected
264
+ ? "connected (sign-in + permission checks + activity logging)"
265
+ : `not connected — ${(0, config_js_1.describeSecurityGap)(resolved.security)} (local activity logging only)`}`);
241
266
  console.log(` Project URL: ${resolved.projectUrl ?? "NOT SET"} [source: ${resolved.projectUrlSource}]`);
242
- console.log(` Project ID: ${resolved.projectId ?? "NOT SET"} [source: ${resolved.projectIdSource}]`);
243
- console.log(` API Key: ${resolved.apiKey ? "(set)" : "NOT SET"} [source: ${resolved.apiKeySource}]`);
267
+ console.log(` Agent Security: ${resolved.agentSecurityUrl ?? "NOT SET"} [source: ${resolved.agentSecurityUrlSource}]`);
244
268
  console.log(` OAuth2 Client: ${resolved.oauth2ClientId ?? "NOT SET"} [source: ${resolved.oauth2ClientIdSource}]`);
245
- console.log(` Permission mode: ${resolved.permissionMode} [source: ${resolved.permissionModeSource}]`);
269
+ console.log(` Project ID: ${resolved.projectId ?? "NOT SET"} [source: ${resolved.projectIdSource}]`);
270
+ // Only meaningful while connected — with no checks running there is no deny
271
+ // to have a posture about.
272
+ if (resolved.security.connected) {
273
+ console.log(` Permission mode: ${resolved.permissionMode} [source: ${resolved.permissionModeSource}]`);
274
+ }
246
275
  console.log(` Namespace: ${namespace}`);
247
- console.log(` User subject ns: ${resolved.userSubjectNamespace ?? "(direct subject id)"} [source: ${resolved.userSubjectNamespaceSource}]`);
276
+ console.log(` User subject ns: ${resolved.userSubjectNamespace ?? "User (default)"} [source: ${resolved.userSubjectNamespaceSource}]`);
248
277
  }
249
278
  /**
250
279
  * Print the "Environment:" block showing Ory-related environment variables.
@@ -255,32 +284,20 @@ function printEnvironment() {
255
284
  const vars = [
256
285
  ["ORY_PROJECT_URL", process.env.ORY_PROJECT_URL, false],
257
286
  ["ORY_SDK_URL", process.env.ORY_SDK_URL, false],
287
+ ["ORY_AGENT_SECURITY_URL", process.env.ORY_AGENT_SECURITY_URL, false],
258
288
  ["ORY_PROJECT_ID", process.env.ORY_PROJECT_ID, false],
259
- [
260
- "ORY_AGENT_API_KEY",
261
- process.env.ORY_AGENT_API_KEY ? "(set)" : undefined,
262
- false,
263
- ],
264
289
  ["ORY_AGENT_CLIENT_ID", process.env.ORY_AGENT_CLIENT_ID, false],
265
290
  ["ORY_OAUTH2_CLIENT_ID", process.env.ORY_OAUTH2_CLIENT_ID, false],
266
291
  [
267
- "ORY_USER_SESSION_TOKEN",
268
- process.env.ORY_USER_SESSION_TOKEN ? "(set)" : undefined,
292
+ "ORY_USER_OAUTH2_TOKEN",
293
+ process.env.ORY_USER_OAUTH2_TOKEN ? "(set)" : undefined,
269
294
  false,
270
295
  ],
271
296
  ["ORY_USER_SUBJECT_ID", process.env.ORY_USER_SUBJECT_ID, false],
272
297
  ["ORY_AGENT_SUBJECT_ID", process.env.ORY_AGENT_SUBJECT_ID, false],
273
298
  ["ORY_AGENT_DEBUG", process.env.ORY_AGENT_DEBUG, false],
274
299
  ["ORY_AGENT_LOG_FILE", process.env.ORY_AGENT_LOG_FILE, false],
275
- ["ORY_AGENT_TRACE_FILE", process.env.ORY_AGENT_TRACE_FILE, false],
276
300
  ];
277
- if (process.env.ORY_API_KEY && !process.env.ORY_AGENT_API_KEY) {
278
- vars.splice(2, 0, [
279
- "ORY_API_KEY",
280
- "(set, DEPRECATED — use ORY_AGENT_API_KEY)",
281
- false,
282
- ]);
283
- }
284
301
  for (const [name, value, required] of vars) {
285
302
  const display = value
286
303
  ? value
@@ -292,12 +309,11 @@ function printEnvironment() {
292
309
  }
293
310
  }
294
311
  /**
295
- * Print the last 5 debug log entries. When `harness` is given the path is
312
+ * Print the last 10 unified activity/debug log entries. When `harness` is given the path is
296
313
  * resolved the same way the plugin does at runtime — the default per-harness
297
314
  * data-dir location when `ORY_AGENT_LOG_FILE` is unset — so `status` finds the
298
- * log without the reader hand-setting env vars. The debug log only exists when
299
- * a session ran with `ORY_AGENT_DEBUG=true`; when it doesn't, this prints a
300
- * one-line pointer on how to turn it on rather than staying silent.
315
+ * log without the reader hand-setting env vars. Activity is always filed;
316
+ * verbose diagnostic entries are included only when `ORY_AGENT_DEBUG=true`.
301
317
  */
302
318
  function printLogTail(harness) {
303
319
  const logFile = harness
@@ -306,12 +322,11 @@ function printLogTail(harness) {
306
322
  if (logFile && fs.existsSync(logFile)) {
307
323
  const lines = fs.readFileSync(logFile, "utf-8").trim().split("\n");
308
324
  console.log("");
309
- console.log(`Debug log: ${logFile} (${lines.length} events)`);
310
- console.log("Last 5 events:");
311
- for (const line of lines.slice(-5)) {
325
+ console.log(`Activity log: ${logFile} (${lines.length} events)`);
326
+ console.log("Last 10 events:");
327
+ for (const line of lines.slice(-10)) {
312
328
  try {
313
- const entry = JSON.parse(line);
314
- console.log(` ${entry.timestamp} [${entry.level}] ${entry.event}`);
329
+ console.log(" " + (0, logger_js_1.formatLogEntry)(JSON.parse(line)));
315
330
  }
316
331
  catch {
317
332
  console.log(` ${line.slice(0, 120)}`);
@@ -321,8 +336,8 @@ function printLogTail(harness) {
321
336
  else if (harness) {
322
337
  console.log("");
323
338
  console.log(logFile
324
- ? `Debug log: none yet — run a session with ORY_AGENT_DEBUG=true to write ${logFile}`
325
- : "Debug log: disabled (ORY_AGENT_LOG_FILE set empty)");
339
+ ? `Activity log: none yet — events land in ${logFile} once a session runs`
340
+ : "Activity log: disabled (ORY_AGENT_LOG_FILE set empty)");
326
341
  }
327
342
  }
328
343
  /**
@@ -334,79 +349,19 @@ function printEnvHelp(binName) {
334
349
  console.log("");
335
350
  console.log(" Option 1 — Save to config file (persists across sessions):");
336
351
  console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --project-url https://your-project.projects.oryapis.com \\`);
337
- console.log(" --oauth2-client-id <public OAuth2 client id> \\");
338
- console.log(" --api-key ory_pat_...");
352
+ console.log(" --agent-security-url https://agents.console.ory.com \\");
353
+ console.log(" --oauth2-client-id <public OAuth2 client id>");
339
354
  console.log("");
340
355
  console.log(" Option 2 — Set environment variables:");
341
356
  console.log(" export ORY_PROJECT_URL=https://your-project.projects.oryapis.com");
357
+ console.log(" export ORY_AGENT_SECURITY_URL=https://agents.console.ory.com");
342
358
  console.log(" export ORY_OAUTH2_CLIENT_ID=<public OAuth2 client id>");
343
- console.log(" export ORY_AGENT_API_KEY=ory_pat_...");
359
+ console.log("");
360
+ console.log(" Both values come from the Ory Console (Agent Security), which provisions the");
361
+ console.log(" project's login client, permission model, grants, and enforce posture.");
344
362
  console.log("");
345
363
  console.log("If neither is set when a session starts, the agent will prompt for configuration.");
346
364
  }
347
- /**
348
- * Print the last 5 trace spans. When `harness` is given the path is resolved
349
- * the same way the plugin does at runtime — the default per-harness data-dir
350
- * location when `ORY_AGENT_TRACE_FILE` is unset — so `status` shows recent
351
- * activity out of the box (traces are written on every recorded span). When no
352
- * spans have been recorded yet, prints a one-line pointer at the trace file so
353
- * the reader knows where to look (and how to watch it live).
354
- */
355
- function printTraceTail(harness) {
356
- const traceFile = harness
357
- ? (0, client_js_1.resolveTraceFilePath)(harness)
358
- : process.env.ORY_AGENT_TRACE_FILE;
359
- if (traceFile && fs.existsSync(traceFile)) {
360
- const lines = fs.readFileSync(traceFile, "utf-8").trim().split("\n");
361
- console.log("");
362
- console.log(`Trace file: ${traceFile} (${lines.length} spans)`);
363
- console.log("Last 5 spans:");
364
- for (const line of lines.slice(-5)) {
365
- try {
366
- const span = JSON.parse(line);
367
- console.log(" " + (0, tracer_js_1.formatSpan)(span));
368
- }
369
- catch {
370
- console.log(` ${line.slice(0, 120)}`);
371
- }
372
- }
373
- }
374
- else if (harness) {
375
- console.log("");
376
- console.log(traceFile
377
- ? `Trace file: none yet — spans land in ${traceFile} once a session runs a tool`
378
- : "Trace file: disabled (ORY_AGENT_TRACE_FILE set empty)");
379
- }
380
- }
381
- /**
382
- * Run the live trace watcher. Tails the NDJSON trace file and prints
383
- * formatted spans to stdout as they arrive. Blocks until interrupted.
384
- */
385
- function runWatchCommand(harness, args) {
386
- // Precedence: explicit arg → resolved default (honors ORY_AGENT_TRACE_FILE,
387
- // falling back to the per-harness data-dir path). The resolver returns
388
- // undefined only when tracing is explicitly disabled (ORY_AGENT_TRACE_FILE
389
- // set empty), so that's the one case that dead-ends here.
390
- const traceFile = args[0] || (0, client_js_1.resolveTraceFilePath)(harness);
391
- if (!traceFile) {
392
- console.error("Trace file is disabled (ORY_AGENT_TRACE_FILE is set empty).\n" +
393
- "Unset it, or pass a path: watch <trace-file>\n");
394
- process.exit(1);
395
- }
396
- console.log(`Watching traces: ${traceFile}`);
397
- if (!fs.existsSync(traceFile)) {
398
- console.log("(file doesn't exist yet — waiting for the first span…)");
399
- }
400
- console.log("Press Ctrl+C to stop.\n");
401
- const stop = (0, tracer_js_1.watchTraceFile)(traceFile, (span) => {
402
- console.log((0, tracer_js_1.formatSpan)(span));
403
- });
404
- process.on("SIGINT", () => {
405
- stop();
406
- console.log("\nStopped watching.");
407
- process.exit(0);
408
- });
409
- }
410
365
  /**
411
366
  * Returns true if `/dev/tty` can be opened for reading. Use this before
412
367
  * attempting to prompt the user — the harness has already taken stdin so
@@ -490,8 +445,9 @@ async function promptForProjectUrl(binName) {
490
445
  process.stderr.write(`\nIgnoring invalid project URL "${answer}".\n\n`);
491
446
  return null;
492
447
  }
493
- (0, config_js_1.saveConfig)({ projectUrl: url, auditOnly: false });
494
- process.stderr.write(`\nSaved project URL to ${(0, config_js_1.getConfigPath)()}.\n\n`);
448
+ (0, config_js_1.saveConfig)({ projectUrl: url, agentSecurityUrl: config_js_1.DEFAULT_AGENT_SECURITY_URL });
449
+ process.stderr.write(`\nSaved project URL to ${(0, config_js_1.getConfigPath)()}.\n`);
450
+ process.stderr.write("\n");
495
451
  return url;
496
452
  }
497
453
  /**
@@ -510,21 +466,19 @@ async function interactiveConfigPrompt(binName) {
510
466
  " Ory Agent Plugin — Configuration Required",
511
467
  "--------------------------------------------",
512
468
  "",
513
- "The Ory agent plugin is installed but not yet configured.",
514
- "Run one of the following commands in another terminal:",
469
+ "Ory Agent Security is not connected, so no sign-in or permission checks run.",
470
+ "To turn them on, run this in another terminal:",
515
471
  "",
516
- " Connect to Ory (enables auth & permission checks):",
517
- ` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]`,
518
- "",
519
- " Or, enable audit logging only (no auth or permission checks):",
520
- ` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --audit-only`,
472
+ ` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --project-url <URL>`,
473
+ " (pass --agent-security-url to override the production broker)",
521
474
  "",
522
475
  `Config is saved to ${configPath}`,
523
476
  "and shared across all agent plugins.",
524
477
  "",
525
478
  "You can also set environment variables (requires session restart):",
526
479
  " export ORY_PROJECT_URL=https://your-project.projects.oryapis.com",
527
- " export ORY_AGENT_API_KEY=ory_pat_...",
480
+ " export ORY_AGENT_SECURITY_URL=https://agents.console.ory.com",
481
+ " export ORY_OAUTH2_CLIENT_ID=<public OAuth2 client id>",
528
482
  "",
529
483
  ].join("\n");
530
484
  process.stderr.write(message + "\n");
@@ -552,12 +506,12 @@ async function interactiveConfigPrompt(binName) {
552
506
  }
553
507
  // Re-read config from disk (env vars set in another shell won't be visible)
554
508
  const resolved = (0, config_js_1.resolveConfig)();
555
- if (resolved.projectUrl || resolved.auditOnly) {
556
- process.stderr.write("\nConfiguration detected! Continuing session.\n\n");
509
+ if (resolved.security.connected) {
510
+ process.stderr.write("\nOry Agent Security is connected. Continuing session.\n\n");
557
511
  return true;
558
512
  }
559
- process.stderr.write("\nNo configuration detected. Session blocked.\n");
560
- process.stderr.write("Run one of the configure commands above and start a new session.\n\n");
513
+ process.stderr.write(`\nOry Agent Security is still not connected (${(0, config_js_1.describeSecurityGap)(resolved.security)}).\n`);
514
+ process.stderr.write("Continuing without sign-in or permission checks.\n\n");
561
515
  return false;
562
516
  }
563
517
  /**
@@ -568,71 +522,149 @@ async function interactiveConfigPrompt(binName) {
568
522
  * - `unregister` — best-effort RFC 7592 DELETE on the registered
569
523
  * client, then clear the persisted block.
570
524
  *
525
+ * Each installed harness owns its own agent identity, so both subcommands act
526
+ * on `harness`'s registration by default. `--all` widens them to every
527
+ * harness registered against the shared config.
528
+ *
571
529
  * Returns the process exit code (0 on success, non-zero on usage error).
572
530
  */
573
- async function runAgentCommand(binName, args) {
531
+ async function runAgentCommand(binName, harness, args) {
574
532
  const sub = args[0];
575
533
  if (!sub || sub === "--help" || sub === "-h") {
576
- console.log(`Usage: ${binName} agent <subcommand>`);
534
+ console.log(`Usage: ${binName} agent <subcommand> [--all]`);
577
535
  console.log("");
578
536
  console.log("Subcommands:");
579
- console.log(" status Show the registered agent OAuth2 client (if any)");
580
- console.log(" unregister Delete the registered agent client and clear local state");
537
+ console.log(" status Show the registered agent OAuth2 clients (one per session)");
538
+ console.log(" unregister Delete the registered agent clients and clear local state");
539
+ console.log("");
540
+ console.log("Options:");
541
+ console.log(` --all Act on every harness's agent client, not just ${harness}'s`);
581
542
  return sub ? 0 : 1;
582
543
  }
544
+ const all = args.includes("--all");
583
545
  switch (sub) {
584
546
  case "status":
585
- runAgentStatus();
547
+ runAgentStatus(harness, all);
586
548
  return 0;
587
549
  case "unregister":
588
- return await runAgentUnregister();
550
+ return await runAgentUnregister(harness, all);
589
551
  default:
590
552
  console.error(`Unknown agent subcommand: ${sub}`);
591
553
  console.error(`Run "${binName} agent --help" for usage.`);
592
554
  return 1;
593
555
  }
594
556
  }
595
- function runAgentStatus() {
596
- const persisted = (0, agent_auth_js_1.loadAgentDynamicCredentials)();
557
+ /**
558
+ * The agent registrations a command should act on — one per harness install.
559
+ * `--all` widens to every harness. Sorted newest-registration first.
560
+ */
561
+ function selectAgentRegistrations(harness, all) {
562
+ const byHarness = (0, agent_auth_js_1.loadAgentDynamicCredentialsByHarness)();
563
+ const entries = [];
564
+ for (const [key, bySession] of Object.entries(byHarness)) {
565
+ if (!all && key !== harness)
566
+ continue;
567
+ for (const [sessionKey, credentials] of Object.entries(bySession)) {
568
+ entries.push({ harness: key, sessionKey, credentials });
569
+ }
570
+ }
571
+ return entries.sort((a, b) => b.credentials.registeredAt - a.credentials.registeredAt ||
572
+ a.harness.localeCompare(b.harness));
573
+ }
574
+ function runAgentStatus(harness, all) {
575
+ const registrations = selectAgentRegistrations(harness, all);
597
576
  console.log("Agent identity (Dynamic Client Registration)");
598
577
  console.log("=============================================");
599
578
  console.log("");
600
- if (!persisted) {
601
- console.log(" No registered agent client. The harness will register one on its");
602
- console.log(" next session start using the user's bearer (or");
579
+ if (registrations.length === 0) {
580
+ console.log(all
581
+ ? " No registered agent clients for any harness. Each install registers"
582
+ : ` No registered agent client for ${harness}. Each install registers`);
583
+ console.log(" one on its first session start, using the user's bearer (or");
603
584
  console.log(" ORY_AGENT_REGISTRATION_TOKEN) as the initial access token.");
604
585
  return;
605
586
  }
606
- console.log(` client_id: ${persisted.clientId}`);
607
- console.log(` client_secret: ${persisted.clientSecret ? "(set)" : "(not set — public client)"}`);
608
- console.log(` registered_at: ${new Date(persisted.registeredAt * 1000).toISOString()}`);
609
- console.log(` project_url: ${persisted.projectUrl}`);
610
- if (persisted.harness)
611
- console.log(` harness: ${persisted.harness}`);
612
- if (persisted.registrationClientUri) {
613
- console.log(` management_uri: ${persisted.registrationClientUri}`);
614
- }
615
- if (persisted.registrationAccessToken) {
616
- console.log(` registration_token: (set — used to manage/delete this client)`);
587
+ for (const { harness: key, sessionKey, credentials: persisted } of registrations) {
588
+ console.log(` harness: ${key}`);
589
+ console.log(` session: ${sessionKey}`);
590
+ console.log(` client_id: ${persisted.clientId}`);
591
+ console.log(` client_secret: ${persisted.clientSecret ? "(set)" : "(not set — public client)"}`);
592
+ console.log(` registered_at: ${new Date(persisted.registeredAt * 1000).toISOString()}`);
593
+ console.log(` project_url: ${persisted.projectUrl}`);
594
+ if (persisted.registrationClientUri) {
595
+ console.log(` management_uri: ${persisted.registrationClientUri}`);
596
+ }
597
+ if (persisted.registrationAccessToken) {
598
+ console.log(` registration_token: (set — used to manage/delete this client)`);
599
+ }
600
+ console.log("");
617
601
  }
618
602
  }
619
- async function runAgentUnregister() {
620
- const persisted = (0, agent_auth_js_1.loadAgentDynamicCredentials)();
621
- if (!persisted) {
622
- console.log("No registered agent client to unregister. Nothing to do.");
623
- return 0;
603
+ async function runAgentUnregister(harness, all) {
604
+ const targets = [];
605
+ const children = (0, agent_auth_js_1.loadSubAgentDynamicCredentialsByHarness)();
606
+ for (const [key, bySession] of Object.entries(children)) {
607
+ if (!all && key !== harness)
608
+ continue;
609
+ for (const [sessionKey, byType] of Object.entries(bySession)) {
610
+ for (const [type, credentials] of Object.entries(byType)) {
611
+ targets.push({
612
+ label: `${key}/${sessionKey}/${type}`,
613
+ credentials,
614
+ clear: () => (0, agent_auth_js_1.clearSubAgentDynamicCredentials)(key, sessionKey, type),
615
+ });
616
+ }
617
+ }
624
618
  }
625
- const outcome = await (0, agent_auth_js_1.revokeAgentDynamicClient)(persisted);
626
- if (outcome.status === "skipped") {
627
- console.warn("Warning: no registration_access_token was persisted, so we cannot DELETE the server-side client. Clearing local state only.");
619
+ for (const { harness: key, sessionKey, credentials } of selectAgentRegistrations(harness, all)) {
620
+ targets.push({
621
+ label: `${key}/${sessionKey}`,
622
+ credentials,
623
+ clear: () => (0, agent_auth_js_1.clearAgentDynamicCredentials)(key, sessionKey),
624
+ });
628
625
  }
629
- else if (outcome.status === "failed") {
630
- const detail = typeof outcome.httpStatus === "number"
631
- ? `returned HTTP ${outcome.httpStatus}`
632
- : `failed (${outcome.message})`;
633
- console.warn(`Warning: server-side DELETE ${detail}. Local state will be cleared anyway.`);
626
+ for (const credentials of (0, agent_auth_js_1.loadRetiredCredentials)()) {
627
+ if (!all && credentials.harness !== harness)
628
+ continue;
629
+ targets.push({
630
+ label: `${credentials.harness ?? "unknown"}/retired`,
631
+ credentials,
632
+ clear: () => undefined,
633
+ retired: true,
634
+ });
635
+ }
636
+ if (targets.length === 0) {
637
+ console.log(all
638
+ ? "No registered agent clients to unregister. Nothing to do."
639
+ : `No registered agent client for ${harness} to unregister. Nothing to do.`);
640
+ return 0;
641
+ }
642
+ const retiredIds = [];
643
+ const seen = new Set();
644
+ for (const target of targets) {
645
+ const { label, credentials: persisted } = target;
646
+ if (seen.has(persisted.clientId)) {
647
+ target.clear();
648
+ if (target.retired)
649
+ retiredIds.push(persisted.clientId);
650
+ continue;
651
+ }
652
+ seen.add(persisted.clientId);
653
+ const outcome = await (0, agent_auth_js_1.revokeAgentDynamicClient)(persisted);
654
+ if (outcome.status === "skipped") {
655
+ console.warn(`Warning: no registration_access_token was persisted for ${label}, so we cannot DELETE the server-side client. Clearing local state only.`);
656
+ }
657
+ else if (outcome.status === "failed") {
658
+ const detail = typeof outcome.httpStatus === "number"
659
+ ? `returned HTTP ${outcome.httpStatus}`
660
+ : `failed (${outcome.message})`;
661
+ console.warn(`Warning: server-side DELETE for ${label} ${detail}. Local state will be cleared anyway.`);
662
+ }
663
+ target.clear();
664
+ if (target.retired)
665
+ retiredIds.push(persisted.clientId);
666
+ console.log(`Unregistered agent client ${persisted.clientId} (${label}).`);
634
667
  }
635
- (0, agent_auth_js_1.clearAgentDynamicCredentials)();
636
- console.log(`Unregistered agent client ${persisted.clientId}.`);
668
+ (0, agent_auth_js_1.clearRetiredCredentials)(retiredIds);
637
669
  return 0;
638
670
  }