@ory/argus 0.14.0 → 1.0.1

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 +413 -0
  90. package/dist/runtime.js +825 -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
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * 1. Resolve config (env + ~/.config/ory-agent-plugins/config.json).
7
7
  * 2. If no project URL, prompt for one when a TTY is available;
8
- * otherwise skip with an audit span.
8
+ * otherwise skip with an audit event.
9
9
  * 3. If a session/oauth2 token is already in env, short-circuit ok.
10
10
  * 4. If persisted tokens are valid, return ok.
11
11
  * 5. If they are expired with a refresh_token, refresh.
@@ -15,7 +15,7 @@
15
15
  * paste the URL into any browser that can reach 127.0.0.1. Only
16
16
  * truly unattended (CI=true) environments short-circuit early.
17
17
  *
18
- * Every terminal path emits exactly one `user.auth` trace span so that
18
+ * Every terminal path emits exactly one `user.auth` activity event so that
19
19
  * the audit trail is complete regardless of outcome.
20
20
  *
21
21
  * The flow runs on **every** session and is **never** blocking: it
@@ -23,8 +23,10 @@
23
23
  * and permission-subject resolution, but a failure to authenticate never
24
24
  * stops the session. Enforcement is governed solely by `permissionMode`
25
25
  * (observe/enforce) at tool-call time — see `checkAndDecide`. The one
26
- * exception is `auditOnly`, which disables auth entirely (mode
27
- * `audit_only`). When the user can't be authenticated the session still
26
+ * exception is Agent Security not being connected at all (no project URL
27
+ * and OAuth2 client id), in which case there is nothing to sign in to and
28
+ * the flow no-ops with mode `not_connected`. When the user can't be
29
+ * authenticated the session still
28
30
  * proceeds; under `enforce` a missing user identity naturally denies at
29
31
  * the tool (no matching tuples), and under `observe` it is audited.
30
32
  *
@@ -38,7 +40,6 @@ exports.ensureAuthenticated = void 0;
38
40
  exports.ensureUserAuthenticated = ensureUserAuthenticated;
39
41
  const config_js_1 = require("./config.js");
40
42
  const cli_js_1 = require("./cli.js");
41
- const cli_invocation_js_1 = require("./cli-invocation.js");
42
43
  const auth_store_js_1 = require("./auth-store.js");
43
44
  const auth_js_1 = require("./auth.js");
44
45
  function clientId() {
@@ -47,7 +48,7 @@ function clientId() {
47
48
  // status CLI, and configure command in lockstep on resolution order.
48
49
  return (0, config_js_1.resolveConfig)().oauth2ClientId;
49
50
  }
50
- function recordAuthSpan(client, decision) {
51
+ function recordAuthActivity(client, decision) {
51
52
  const status = decision.mode === "ok" || decision.mode === "refreshed" || decision.mode === "env_token"
52
53
  ? "ok"
53
54
  : decision.mode === "declined"
@@ -55,7 +56,7 @@ function recordAuthSpan(client, decision) {
55
56
  : decision.mode === "error"
56
57
  ? "error"
57
58
  : "skipped";
58
- client.tracer.record("user.auth", status, {
59
+ client.logger.activity("user.auth", status, {
59
60
  attributes: {
60
61
  mode: decision.mode,
61
62
  reason: decision.reason,
@@ -72,7 +73,7 @@ async function ensureUserAuthenticated(client, options) {
72
73
  try {
73
74
  const decision = await runUserLogin(client, options);
74
75
  attachUserPrincipal(client, decision);
75
- recordAuthSpan(client, decision);
76
+ recordAuthActivity(client, decision);
76
77
  return decision;
77
78
  }
78
79
  catch (err) {
@@ -81,13 +82,13 @@ async function ensureUserAuthenticated(client, options) {
81
82
  mode: "error",
82
83
  reason: err instanceof Error ? err.message : String(err),
83
84
  };
84
- recordAuthSpan(client, decision);
85
+ recordAuthActivity(client, decision);
85
86
  return decision;
86
87
  }
87
88
  }
88
89
  /**
89
90
  * Attach the resolved user identity to the client so downstream
90
- * permission checks and trace spans can reference it. Only fires for
91
+ * permission checks and activity events can reference it. Only fires for
91
92
  * modes that actually authenticated a user — declined/skipped/error
92
93
  * leave the principal alone.
93
94
  */
@@ -109,69 +110,43 @@ function attachUserPrincipal(client, decision) {
109
110
  return;
110
111
  }
111
112
  if (decision.mode === "env_token") {
112
- const subject = process.env.ORY_USER_SUBJECT_ID;
113
- const token = process.env.ORY_USER_SESSION_TOKEN ??
114
- process.env.ORY_USER_OAUTH2_TOKEN ??
115
- process.env.ORY_SESSION_TOKEN ??
116
- process.env.ORY_OAUTH2_TOKEN;
117
- client.setUserPrincipal({
118
- ...(subject ? { subject } : {}),
119
- ...(token ? { token } : {}),
120
- });
113
+ // Same reader the per-client rehydration uses, so the credential this gate
114
+ // attaches is exactly the one later lifecycle events will pick up.
115
+ client.setUserPrincipal((0, auth_store_js_1.readEnvUserCredential)());
121
116
  }
122
117
  }
123
118
  async function runUserLogin(client, options) {
124
119
  const ttyCheck = options.isTtyAvailableFn ?? cli_js_1.isTtyAvailable;
125
120
  const tty = ttyCheck();
126
121
  let resolved = (0, config_js_1.resolveConfig)();
127
- if (resolved.auditOnly) {
122
+ // Agent Security isn't connected, so there is no project to sign in to.
123
+ // Nothing is lost: the plugin's developer-experience half doesn't need an
124
+ // identity.
125
+ if (!resolved.security.connected) {
128
126
  return {
129
127
  proceed: true,
130
- mode: "audit_only",
131
- reason: "Configured for audit-only mode; user login is a no-op",
128
+ mode: "not_connected",
129
+ reason: `Ory Agent Security is not connected (${(0, config_js_1.describeSecurityGap)(resolved.security)}); user login is a no-op`,
132
130
  };
133
131
  }
134
- // 1. Project URL.
135
- if (!resolved.projectUrl) {
136
- if (!tty) {
137
- return {
138
- proceed: true,
139
- mode: "skipped",
140
- reason: "No ORY_PROJECT_URL configured and no TTY to prompt",
141
- };
142
- }
143
- const promptFn = options.promptForProjectUrlFn ?? cli_js_1.promptForProjectUrl;
144
- const url = await promptFn(options.binName);
145
- if (!url) {
146
- return {
147
- proceed: true,
148
- mode: "declined",
149
- reason: "User declined to enter an Ory project URL",
150
- };
151
- }
152
- resolved = (0, config_js_1.resolveConfig)();
153
- if (!resolved.projectUrl) {
154
- return {
155
- proceed: true,
156
- mode: "error",
157
- reason: "Project URL was entered but config did not persist",
158
- };
159
- }
160
- }
161
- // 2. Pre-supplied env tokens short-circuit the browser flow (CI, scripted
162
- // runs). ORY_USER_* are the documented names; the legacy single-identity
163
- // ORY_SESSION_TOKEN / ORY_OAUTH2_TOKEN are still honored.
164
- if (process.env.ORY_USER_SESSION_TOKEN ||
165
- process.env.ORY_USER_OAUTH2_TOKEN ||
166
- process.env.ORY_SESSION_TOKEN ||
167
- process.env.ORY_OAUTH2_TOKEN) {
132
+ // `security.connected` guarantees both values are non-empty; narrow once
133
+ // here so the rest of the flow doesn't re-check what the gate established.
134
+ const projectUrl = resolved.projectUrl;
135
+ // Past this point both connection values are present, which is why there is
136
+ // no "prompt for a project URL" or "missing client id" branch here: either
137
+ // one absent means not connected, and that is handled above as a normal
138
+ // state rather than something to interrupt the user about.
139
+ // 1. A pre-supplied OAuth2 access token short-circuits the browser flow
140
+ // (CI, scripted runs). `ORY_USER_OAUTH2_TOKEN` is the only accepted
141
+ // variable see `readEnvUserCredential`.
142
+ if ((0, auth_store_js_1.readEnvUserCredential)().token) {
168
143
  return {
169
144
  proceed: true,
170
145
  mode: "env_token",
171
- reason: "Session/OAuth2 token supplied via environment",
146
+ reason: "OAuth2 access token supplied via environment",
172
147
  };
173
148
  }
174
- // 3. Persisted tokens.
149
+ // 2. Persisted tokens.
175
150
  const existing = (0, auth_store_js_1.loadTokens)();
176
151
  if (existing && !(0, auth_store_js_1.isExpired)(existing)) {
177
152
  return {
@@ -186,7 +161,7 @@ async function runUserLogin(client, options) {
186
161
  if (cid) {
187
162
  try {
188
163
  const refreshed = await (0, auth_store_js_1.refreshAndSave)({
189
- projectUrl: resolved.projectUrl,
164
+ projectUrl,
190
165
  clientId: cid,
191
166
  current: existing,
192
167
  });
@@ -205,19 +180,9 @@ async function runUserLogin(client, options) {
205
180
  }
206
181
  }
207
182
  }
208
- // 4. Browser flow. Requires a registered OAuth2 client id.
183
+ // 3. Browser flow. The client id is guaranteed present — it is half of what
184
+ // "connected" means, checked at the top of this function.
209
185
  const cid = clientId();
210
- if (!cid) {
211
- return {
212
- proceed: true,
213
- mode: "skipped",
214
- reason: "ORY_OAUTH2_CLIENT_ID is not set; cannot start browser login (run `" +
215
- (0, cli_invocation_js_1.oryNpx)(options.binName) +
216
- " configure --oauth2-client-id <id>` after registering a public OAuth2 client " +
217
- "in your Ory project with all four loopback redirect URIs: " +
218
- "http://127.0.0.1:47823..47826/callback)",
219
- };
220
- }
221
186
  // No TTY check here on purpose: PKCE only needs a browser that can reach
222
187
  // the loopback callback port, not stdin. `pkceLogin` prints the
223
188
  // authorize URL to stderr so users in non-TTY environments (SSH without
@@ -225,7 +190,7 @@ async function runUserLogin(client, options) {
225
190
  // complete sign-in by pasting the URL into any browser that can reach
226
191
  // 127.0.0.1. Truly unattended runs (CI=true) are short-circuited by
227
192
  // `pkceLogin` itself via `detectHeadless`, and operators can bypass
228
- // login entirely by supplying `ORY_USER_SESSION_TOKEN`, and enforcement
193
+ // login entirely by supplying `ORY_USER_OAUTH2_TOKEN`, and enforcement
229
194
  // consequences are governed by `permissionMode`, not by this gate.
230
195
  // PKCE-flight lock so concurrent processes share a single browser flow.
231
196
  const lock = (0, auth_store_js_1.tryAcquirePkceFlightLock)();
@@ -249,7 +214,7 @@ async function runUserLogin(client, options) {
249
214
  try {
250
215
  const loginFn = options.loginFn ?? auth_js_1.pkceLogin;
251
216
  const outcome = await loginFn({
252
- projectUrl: resolved.projectUrl,
217
+ projectUrl,
253
218
  clientId: cid,
254
219
  });
255
220
  if (outcome.kind === "ok") {
@@ -264,13 +229,17 @@ async function runUserLogin(client, options) {
264
229
  return {
265
230
  proceed: true,
266
231
  mode: declineToMode(outcome.reason),
267
- reason: declineMessage(outcome.reason),
232
+ reason: appendDetail(declineMessage(outcome.reason), outcome.detail),
268
233
  };
269
234
  }
270
235
  finally {
271
236
  lock.release();
272
237
  }
273
238
  }
239
+ /** Append a secret-free diagnostic to a decline message when one is present. */
240
+ function appendDetail(message, detail) {
241
+ return detail ? `${message} — ${detail}` : message;
242
+ }
274
243
  function declineToMode(reason) {
275
244
  switch (reason) {
276
245
  case "user_denied":
@@ -283,6 +252,8 @@ function declineToMode(reason) {
283
252
  return "skipped";
284
253
  case "state_mismatch":
285
254
  case "token_exchange_failed":
255
+ case "invalid_scope":
256
+ case "oauth_error":
286
257
  return "error";
287
258
  }
288
259
  }
@@ -291,7 +262,13 @@ function declineMessage(reason) {
291
262
  case "headless":
292
263
  return "Browser login skipped: environment is headless (CI/SSH/no DISPLAY)";
293
264
  case "timeout":
294
- return "Browser login timed out before the user completed sign-in";
265
+ // Name the consequence, not just the event. This is the one decline the
266
+ // user can fix by simply retrying, and under `enforce` its after-effect
267
+ // (every tool denied, for want of a user identity) is otherwise
268
+ // unattributable to a sign-in that quietly ran out of time (#220).
269
+ return (`Browser login timed out after ${Math.round(auth_js_1.DEFAULT_LOGIN_TIMEOUT_MS / 1000)}s ` +
270
+ "before sign-in completed — the session has no Ory user identity. " +
271
+ "Start a new session to sign in again");
295
272
  case "user_denied":
296
273
  return "User declined the OAuth2 consent screen";
297
274
  case "state_mismatch":
@@ -304,6 +281,10 @@ function declineMessage(reason) {
304
281
  return "Login was aborted by the caller";
305
282
  case "token_exchange_failed":
306
283
  return "Authorization code exchange failed at /oauth2/token";
284
+ case "invalid_scope":
285
+ return "Ory rejected the requested OAuth2 scope (invalid_scope) — the login client isn't allowed it";
286
+ case "oauth_error":
287
+ return "Ory returned an OAuth2 error on the login redirect";
307
288
  }
308
289
  }
309
290
  /**
@@ -0,0 +1,6 @@
1
+ export interface WatchCommandOptions {
2
+ signal?: AbortSignal;
3
+ pollIntervalMs?: number;
4
+ }
5
+ /** Follow one harness's unified activity/debug log until interrupted. */
6
+ export declare function runWatchCommand(binName: string, harness: string, args: readonly string[], options?: WatchCommandOptions): Promise<number>;
@@ -0,0 +1,217 @@
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.runWatchCommand = runWatchCommand;
37
+ const fs = __importStar(require("node:fs"));
38
+ const client_js_1 = require("./client.js");
39
+ const logger_js_1 = require("./logger.js");
40
+ const ui_js_1 = require("./ui.js");
41
+ const DEFAULT_HISTORY_LINES = 10;
42
+ const DEFAULT_POLL_INTERVAL_MS = 250;
43
+ const MAX_HISTORY_BYTES = 64 * 1024;
44
+ function parseWatchArgs(binName, args) {
45
+ let json = false;
46
+ let lines = DEFAULT_HISTORY_LINES;
47
+ for (let index = 0; index < args.length; index++) {
48
+ const arg = args[index];
49
+ if (arg === "--json") {
50
+ json = true;
51
+ continue;
52
+ }
53
+ if (arg === "--lines") {
54
+ const value = Number(args[++index]);
55
+ if (Number.isInteger(value) && value >= 0) {
56
+ lines = value;
57
+ continue;
58
+ }
59
+ }
60
+ console.error(`Usage: ${binName} watch [--json] [--lines <count>]`);
61
+ return undefined;
62
+ }
63
+ return { json, lines };
64
+ }
65
+ const WATCH_LABEL_WIDTH = 13;
66
+ const WATCH_EVENT_WIDTH = 30;
67
+ function colorLabel(label) {
68
+ if (label === "ALLOW" || label === "OK")
69
+ return ui_js_1.brand.green(label);
70
+ if (label === "DENY" || label === "DENIED" || label === "ERROR")
71
+ return ui_js_1.brand.rose(label);
72
+ if (label === "OBSERVE" || label === "FAIL OPEN" || label === "WARN") {
73
+ return ui_js_1.brand.yellow(label);
74
+ }
75
+ return ui_js_1.style.gray(label);
76
+ }
77
+ function detailLabel(detail) {
78
+ const separator = detail.indexOf("=");
79
+ if (separator < 0)
80
+ return detail;
81
+ return `${ui_js_1.style.gray(detail.slice(0, separator))}=${detail.slice(separator + 1)}`;
82
+ }
83
+ function renderEntry(entry) {
84
+ const { time, event, marker, details } = (0, logger_js_1.formatLogEntryParts)(entry);
85
+ const decision = details.find((detail) => detail.startsWith("decision="))?.slice(9);
86
+ const status = details.find((detail) => detail.startsWith("status="))?.slice(7);
87
+ const label = (decision ?? status ?? marker.replaceAll(/[\[\]]/g, "")).replaceAll("_", " ").toUpperCase();
88
+ const remaining = details.filter((detail) => !detail.startsWith("decision=") && !detail.startsWith("status="));
89
+ const labelPadding = " ".repeat(Math.max(1, WATCH_LABEL_WIDTH - label.length));
90
+ const eventPadding = " ".repeat(Math.max(1, WATCH_EVENT_WIDTH - event.length));
91
+ const metadata = remaining.length > 0
92
+ ? remaining.map(detailLabel).join(ui_js_1.style.dim(" | "))
93
+ : "";
94
+ console.log(`${ui_js_1.style.dim(time)} ${colorLabel(label)}${labelPadding}${ui_js_1.style.bold(event)}` +
95
+ `${eventPadding}${metadata}`);
96
+ }
97
+ function renderLine(line, json) {
98
+ if (json) {
99
+ console.log(line);
100
+ return;
101
+ }
102
+ try {
103
+ renderEntry(JSON.parse(line));
104
+ }
105
+ catch {
106
+ console.log(ui_js_1.brand.rose("INVALID") + ` ${line.slice(0, 160)}`);
107
+ }
108
+ }
109
+ function readHistory(logFile, lines, json) {
110
+ const stat = fs.statSync(logFile);
111
+ if (lines === 0 || stat.size === 0)
112
+ return stat.size;
113
+ const start = Math.max(0, stat.size - MAX_HISTORY_BYTES);
114
+ const length = stat.size - start;
115
+ const buffer = Buffer.alloc(length);
116
+ const fd = fs.openSync(logFile, "r");
117
+ try {
118
+ fs.readSync(fd, buffer, 0, length, start);
119
+ }
120
+ finally {
121
+ fs.closeSync(fd);
122
+ }
123
+ let history = buffer.toString("utf8").split("\n");
124
+ if (start > 0)
125
+ history = history.slice(1);
126
+ for (const line of history.filter(Boolean).slice(-lines))
127
+ renderLine(line, json);
128
+ return stat.size;
129
+ }
130
+ function readRange(logFile, start, end) {
131
+ const length = end - start;
132
+ const buffer = Buffer.alloc(length);
133
+ const fd = fs.openSync(logFile, "r");
134
+ try {
135
+ fs.readSync(fd, buffer, 0, length, start);
136
+ }
137
+ finally {
138
+ fs.closeSync(fd);
139
+ }
140
+ return buffer.toString("utf8");
141
+ }
142
+ function delay(ms) {
143
+ return new Promise((resolve) => setTimeout(resolve, ms));
144
+ }
145
+ /** Follow one harness's unified activity/debug log until interrupted. */
146
+ async function runWatchCommand(binName, harness, args, options = {}) {
147
+ const parsed = parseWatchArgs(binName, args);
148
+ if (!parsed)
149
+ return 1;
150
+ const logFile = (0, client_js_1.resolveDebugLogPath)(harness);
151
+ if (!logFile) {
152
+ console.error("Activity/debug logging is disabled because ORY_AGENT_LOG_FILE is empty.");
153
+ return 1;
154
+ }
155
+ const controller = options.signal ? undefined : new AbortController();
156
+ const signal = options.signal ?? controller?.signal;
157
+ const stop = () => controller?.abort();
158
+ if (controller) {
159
+ process.once("SIGINT", stop);
160
+ process.once("SIGTERM", stop);
161
+ }
162
+ if (parsed.json) {
163
+ console.log(`Watching ${logFile} (Ctrl-C to stop)`);
164
+ }
165
+ else {
166
+ console.log(ui_js_1.style.bold("Watching plugin activity"));
167
+ console.log(`${ui_js_1.style.gray("file")} ${logFile}`);
168
+ console.log(`${ui_js_1.style.gray("stop")} Ctrl-C`);
169
+ console.log("");
170
+ }
171
+ let offset = 0;
172
+ let fileIdentity;
173
+ let remainder = "";
174
+ try {
175
+ try {
176
+ const stat = fs.statSync(logFile);
177
+ fileIdentity = `${stat.dev}:${stat.ino}`;
178
+ offset = readHistory(logFile, parsed.lines, parsed.json);
179
+ }
180
+ catch {
181
+ console.log("Waiting for the first plugin event...");
182
+ }
183
+ while (!signal?.aborted) {
184
+ await delay(options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
185
+ if (signal?.aborted || !fs.existsSync(logFile))
186
+ continue;
187
+ try {
188
+ const stat = fs.statSync(logFile);
189
+ const nextIdentity = `${stat.dev}:${stat.ino}`;
190
+ if (fileIdentity !== nextIdentity || stat.size < offset) {
191
+ offset = 0;
192
+ remainder = "";
193
+ fileIdentity = nextIdentity;
194
+ }
195
+ if (stat.size === offset)
196
+ continue;
197
+ const chunk = remainder + readRange(logFile, offset, stat.size);
198
+ offset = stat.size;
199
+ const complete = chunk.split("\n");
200
+ remainder = complete.pop() ?? "";
201
+ for (const line of complete)
202
+ if (line)
203
+ renderLine(line, parsed.json);
204
+ }
205
+ catch {
206
+ // The writer may replace the file between stat and read; retry next poll.
207
+ }
208
+ }
209
+ return 0;
210
+ }
211
+ finally {
212
+ if (controller) {
213
+ process.off("SIGINT", stop);
214
+ process.off("SIGTERM", stop);
215
+ }
216
+ }
217
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ory/argus",
3
- "version": "0.14.0",
3
+ "version": "1.0.1",
4
4
  "description": "Ory Argus: the core API for building authentication, authorization, and audit into AI agent harness plugins, extensions, and custom integrations",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://ory.com",
@@ -36,8 +36,6 @@
36
36
  "agent-security",
37
37
  "guardrails",
38
38
  "llm",
39
- "tracing",
40
- "distributed-tracing",
41
39
  "observability",
42
40
  "kratos",
43
41
  "keto",
@@ -61,40 +59,19 @@
61
59
  "files": [
62
60
  "dist",
63
61
  "assets",
64
- "!dist/dev",
65
62
  "!dist/**/*.tsbuildinfo"
66
63
  ],
67
64
  "dependencies": {
68
- "@opentelemetry/api": "^1.9.0",
69
- "@opentelemetry/core": "^2.8.0",
70
- "@opentelemetry/exporter-trace-otlp-http": "^0.219.0",
71
- "@opentelemetry/exporter-trace-otlp-proto": "^0.219.0",
72
- "@opentelemetry/resources": "^2.8.0",
73
- "@opentelemetry/sdk-trace-base": "^2.8.0",
74
65
  "@ory/client": "^1.22.37",
66
+ "@vscode/tree-sitter-wasm": "^0.3.1",
75
67
  "open": "^11.0.0",
76
- "reo-census": "^1.2.8"
77
- },
78
- "devDependencies": {
79
- "@tailwindcss/vite": "^4.1.0",
80
- "@types/react": "^19.0.0",
81
- "@types/react-dom": "^19.0.0",
82
- "@vitejs/plugin-react": "^4.3.0",
83
- "class-variance-authority": "^0.7.1",
84
- "clsx": "^2.1.1",
85
- "react": "^19.0.0",
86
- "react-dom": "^19.0.0",
87
- "tailwind-merge": "^3.0.0",
88
- "tailwindcss": "^4.1.0",
89
- "vite": "^6.0.0"
68
+ "web-tree-sitter": "^0.26.11"
90
69
  },
91
70
  "engines": {
92
71
  "node": ">=22"
93
72
  },
94
73
  "scripts": {
95
- "build": "tsc && node ./scripts/gen-build-info.mjs && pnpm run build:webapp",
96
- "build:webapp": "vite build --config webapp/vite.config.ts",
97
- "dev:webapp": "node webapp/dev-server.mjs",
74
+ "build": "tsc && node ./scripts/gen-build-info.mjs",
98
75
  "clean": "rm -rf dist *.tsbuildinfo",
99
76
  "test": "vitest run",
100
77
  "test:watch": "vitest",
@@ -1,34 +0,0 @@
1
- # Open the Ory Plugin Dashboard
2
-
3
- Open a live, browser-based control panel for the Ory agent plugin. It shows
4
- the health of the core Ory services the plugin uses (Kratos, Hydra, Keto) and
5
- the plugin's current configuration (project URL, permission mode, audit mode,
6
- user-login flag, namespaces, and the resolved user/agent identities),
7
- refreshing the health status live.
8
-
9
- Run:
10
-
11
- ```bash
12
- {{NPX}} dashboard
13
- ```
14
-
15
- This will:
16
-
17
- 1. Start a small local web server bound to `127.0.0.1`
18
- 2. Open your default browser at the dashboard
19
- 3. Refresh the service health live
20
-
21
- The dashboard is the read-only status view of the Ory Agent web app — the same
22
- local React UI, loopback server, and styleguide that power the guided web
23
- installer. It never changes configuration. Stop it with `Ctrl-C` in the
24
- terminal (or the "Close" button in the page).
25
-
26
- When no browser is available (CI, SSH, headless), or with `--json`, it prints a
27
- machine-readable status report instead:
28
-
29
- ```bash
30
- {{NPX}} dashboard --json
31
- ```
32
-
33
- If the core services show as unreachable, start the local stack with
34
- {{REF_LOCAL_UP}} (or check state with `{{NPX}} local status`).
@@ -1,8 +0,0 @@
1
- /**
2
- * Entry point invoked by each harness CLI's `dashboard` case. Returns the
3
- * intended process exit code; never throws. With `--json` (or when no browser
4
- * can be opened) it prints the report and resolves immediately; otherwise it
5
- * opens the web app in `status` mode and resolves when the SPA requests
6
- * shutdown or the user interrupts (Ctrl+C).
7
- */
8
- export declare function runDashboardCommand(binName: string, harness: string, args: string[]): Promise<number>;
@@ -1,70 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runDashboardCommand = runDashboardCommand;
4
- /**
5
- * Shared `dashboard` CLI implementation. Each harness's `cli/main.ts`
6
- * delegates `<bin> dashboard` here.
7
- *
8
- * The dashboard is the live status view of the plugin, and one of the two
9
- * surfaces of the Ory Agent web app (the other being the guided installer): it
10
- * opens that app in `status` mode via {@link file://./web/launch.ts}'s
11
- * `launchWebDashboard`, reusing the same loopback server + React SPA. The data
12
- * it renders is the shared {@link file://./status-data.ts} `StatusReport`,
13
- * which the dashboard extends with a "System information" section — the health
14
- * of the core Ory services (Kratos, Hydra, Keto) from
15
- * {@link file://./status-system.ts}.
16
- *
17
- * Besides reading status, the dashboard exposes a "Change stack" action that
18
- * hands off to the same guided wizard the installer uses (embedded, starting on
19
- * the stack chooser) so the user can reconfigure which Ory stack the plugin runs
20
- * against — Ory Network, the local stack, or audit-only — without leaving the
21
- * browser. It reuses the shared install API and reloads status on return.
22
- *
23
- * Flags:
24
- * --json Print the status report as JSON and exit (no server). Also the
25
- * automatic fallback when a browser can't be opened (headless / CI).
26
- */
27
- const launch_js_1 = require("./web/launch.js");
28
- const status_data_js_1 = require("./status-data.js");
29
- const cli_invocation_js_1 = require("./cli-invocation.js");
30
- function printDashboardHelp(binName) {
31
- console.log(`Usage: ${binName} dashboard [options]`);
32
- console.log("");
33
- console.log("Open the Ory Agent dashboard in your browser — a live view of the");
34
- console.log("plugin's configuration and the health of the core Ory services (Kratos,");
35
- console.log("Hydra, Keto), refreshing live. It is the status surface of the same web");
36
- console.log("app that powers the guided installer, and its \"Change stack\" action lets");
37
- console.log("you reconfigure which Ory stack the plugin runs against.");
38
- console.log("");
39
- console.log("Options:");
40
- console.log(" --json Print the status report as JSON and exit (no server)");
41
- console.log("");
42
- console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} dashboard`);
43
- }
44
- /**
45
- * Entry point invoked by each harness CLI's `dashboard` case. Returns the
46
- * intended process exit code; never throws. With `--json` (or when no browser
47
- * can be opened) it prints the report and resolves immediately; otherwise it
48
- * opens the web app in `status` mode and resolves when the SPA requests
49
- * shutdown or the user interrupts (Ctrl+C).
50
- */
51
- async function runDashboardCommand(binName, harness, args) {
52
- if (args.includes("--help") || args.includes("-h")) {
53
- printDashboardHelp(binName);
54
- return 0;
55
- }
56
- // `--json`, or any environment where we can't open a browser (CI, no TTY,
57
- // no display), falls back to the machine-readable report on stdout.
58
- if (args.includes("--json") || !(0, launch_js_1.canLaunchBrowser)()) {
59
- const report = await (0, status_data_js_1.collectStatusReport)(harness);
60
- console.log(JSON.stringify(report, null, 2));
61
- return 0;
62
- }
63
- const launched = await (0, launch_js_1.launchWebDashboard)(binName, harness);
64
- if (!launched) {
65
- console.error("Could not start the dashboard server.");
66
- console.error(`Use "${(0, cli_invocation_js_1.oryNpx)(binName)} dashboard --json" for the raw report instead.`);
67
- return 1;
68
- }
69
- return 0;
70
- }