@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.
- package/README.md +31 -45
- package/assets/commands/temporal-up.md +1 -1
- package/assets/skills/auth-setup/SKILL.md +1 -1
- package/assets/skills/local-dev/SKILL.md +17 -7
- package/assets/skills/ory-build-agent/SKILL.md +43 -97
- package/assets/skills/ory-e2b-sandbox/SKILL.md +18 -18
- package/assets/skills/ory-temporal-worker/SKILL.md +38 -42
- package/assets/skills/permissions-onboarding/SKILL.md +131 -104
- package/dist/adapters.d.ts +93 -30
- package/dist/adapters.js +464 -136
- package/dist/agent-auth.d.ts +258 -68
- package/dist/agent-auth.js +998 -202
- package/dist/auth-store.d.ts +37 -2
- package/dist/auth-store.js +37 -3
- package/dist/auth.d.ts +40 -4
- package/dist/auth.js +247 -19
- package/dist/bash-parser.d.ts +98 -0
- package/dist/bash-parser.js +396 -0
- package/dist/branding.d.ts +128 -0
- package/dist/branding.js +151 -0
- package/dist/build-info.json +4 -4
- package/dist/cli-invocation.d.ts +1 -1
- package/dist/cli-invocation.js +2 -1
- package/dist/cli.d.ts +20 -29
- package/dist/cli.js +271 -278
- package/dist/client.d.ts +175 -138
- package/dist/client.js +672 -391
- package/dist/config.d.ts +249 -57
- package/dist/config.js +486 -62
- package/dist/context.d.ts +10 -0
- package/dist/context.js +21 -0
- package/dist/contract-suite.d.ts +8 -8
- package/dist/contract-suite.js +88 -69
- package/dist/denial.d.ts +36 -3
- package/dist/denial.js +79 -10
- package/dist/event-reporter.d.ts +77 -0
- package/dist/event-reporter.js +776 -0
- package/dist/external-registrations-main.d.ts +10 -0
- package/dist/external-registrations-main.js +38 -0
- package/dist/external-registrations.d.ts +79 -0
- package/dist/external-registrations.js +188 -0
- package/dist/help-cli.d.ts +39 -0
- package/dist/help-cli.js +55 -0
- package/dist/hook-timeout.d.ts +64 -0
- package/dist/hook-timeout.js +88 -0
- package/dist/index.d.ts +31 -19
- package/dist/index.js +182 -31
- package/dist/lifecycle.d.ts +3 -3
- package/dist/lifecycle.js +38 -6
- package/dist/local/cli.js +11 -6
- package/dist/local/configs.d.ts +74 -18
- package/dist/local/configs.js +291 -84
- package/dist/local/health.d.ts +14 -0
- package/dist/local/health.js +50 -4
- package/dist/local/index.d.ts +2 -2
- package/dist/local/index.js +24 -10
- package/dist/local/manager.d.ts +20 -1
- package/dist/local/manager.js +160 -39
- package/dist/local/ports.d.ts +158 -0
- package/dist/local/ports.js +443 -0
- package/dist/local/seed.d.ts +22 -25
- package/dist/local/seed.js +88 -56
- package/dist/logger.d.ts +54 -25
- package/dist/logger.js +329 -63
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +10 -5
- package/dist/mirror-bootstrap.d.ts +48 -0
- package/dist/mirror-bootstrap.js +254 -0
- package/dist/opl.d.ts +289 -0
- package/dist/opl.js +446 -0
- package/dist/permission-mode.d.ts +87 -0
- package/dist/permission-mode.js +307 -0
- package/dist/permissions-cli.d.ts +13 -49
- package/dist/permissions-cli.js +154 -348
- package/dist/permissions.d.ts +148 -38
- package/dist/permissions.js +591 -45
- package/dist/post-install.d.ts +33 -0
- package/dist/post-install.js +127 -0
- package/dist/read-credential.d.ts +65 -0
- package/dist/read-credential.js +86 -0
- package/dist/registry/cli.js +5 -2
- package/dist/registry/config.d.ts +0 -17
- package/dist/registry/config.js +0 -23
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +2 -2
- package/dist/registry/manager.d.ts +4 -21
- package/dist/registry/manager.js +83 -55
- package/dist/runtime-credential.d.ts +140 -0
- package/dist/runtime-credential.js +572 -0
- package/dist/runtime.d.ts +408 -0
- package/dist/runtime.js +748 -0
- package/dist/setup.d.ts +23 -28
- package/dist/setup.js +57 -84
- package/dist/status-cli.d.ts +29 -13
- package/dist/status-cli.js +124 -144
- package/dist/status-data.d.ts +195 -0
- package/dist/status-data.js +333 -0
- package/dist/status-system.d.ts +24 -0
- package/dist/status-system.js +56 -0
- package/dist/subject.d.ts +126 -20
- package/dist/subject.js +215 -30
- package/dist/testing.d.ts +74 -38
- package/dist/testing.js +185 -68
- package/dist/tool-catalog.d.ts +53 -11
- package/dist/tool-catalog.js +164 -13
- package/dist/tool-metadata.d.ts +7 -6
- package/dist/tool-metadata.js +6 -5
- package/dist/types.d.ts +11 -1
- package/dist/uninstall.d.ts +74 -19
- package/dist/uninstall.js +224 -49
- package/dist/user-login.d.ts +22 -16
- package/dist/user-login.js +67 -96
- package/dist/watch-cli.d.ts +6 -0
- package/dist/watch-cli.js +217 -0
- package/package.json +3 -11
- package/dist/dev.d.ts +0 -103
- package/dist/dev.js +0 -584
- package/dist/interactive-setup.d.ts +0 -165
- package/dist/interactive-setup.js +0 -1546
- package/dist/local/jaeger-main.d.ts +0 -13
- package/dist/local/jaeger-main.js +0 -85
- package/dist/local/jaeger.d.ts +0 -50
- package/dist/local/jaeger.js +0 -162
- package/dist/otel/exporter.d.ts +0 -17
- package/dist/otel/exporter.js +0 -12
- package/dist/otel/index.d.ts +0 -2
- package/dist/otel/index.js +0 -8
- package/dist/otel/otlp.d.ts +0 -103
- package/dist/otel/otlp.js +0 -385
- package/dist/tracer.d.ts +0 -190
- package/dist/tracer.js +0 -481
- package/dist/watch-sandbox.d.ts +0 -9
- package/dist/watch-sandbox.js +0 -81
package/dist/user-login.js
CHANGED
|
@@ -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
|
|
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,14 +15,20 @@
|
|
|
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`
|
|
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
|
-
* The
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* The flow runs on **every** session and is **never** blocking: it
|
|
22
|
+
* establishes or refreshes the user identity for attribution, delegation,
|
|
23
|
+
* and permission-subject resolution, but a failure to authenticate never
|
|
24
|
+
* stops the session. Enforcement is governed solely by `permissionMode`
|
|
25
|
+
* (observe/enforce) at tool-call time — see `checkAndDecide`. The one
|
|
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
|
|
30
|
+
* proceeds; under `enforce` a missing user identity naturally denies at
|
|
31
|
+
* the tool (no matching tuples), and under `observe` it is audited.
|
|
26
32
|
*
|
|
27
33
|
* This authenticates the *user* (the human at the keyboard). The
|
|
28
34
|
* separate agent identity (the AI process making the calls) is resolved
|
|
@@ -34,22 +40,15 @@ exports.ensureAuthenticated = void 0;
|
|
|
34
40
|
exports.ensureUserAuthenticated = ensureUserAuthenticated;
|
|
35
41
|
const config_js_1 = require("./config.js");
|
|
36
42
|
const cli_js_1 = require("./cli.js");
|
|
37
|
-
const cli_invocation_js_1 = require("./cli-invocation.js");
|
|
38
43
|
const auth_store_js_1 = require("./auth-store.js");
|
|
39
44
|
const auth_js_1 = require("./auth.js");
|
|
40
|
-
function isUserLoginEnabled() {
|
|
41
|
-
// Resolves ORY_USER_LOGIN (env, when set) over the persisted
|
|
42
|
-
// `userLogin` config flag. Centralized in resolveConfig so the gate,
|
|
43
|
-
// status CLI, and configure command stay in lockstep.
|
|
44
|
-
return (0, config_js_1.resolveConfig)().userLogin;
|
|
45
|
-
}
|
|
46
45
|
function clientId() {
|
|
47
46
|
// resolveConfig already merges env (ORY_OAUTH2_CLIENT_ID) with the
|
|
48
47
|
// persisted value, env winning. Centralizing here keeps the user gate,
|
|
49
48
|
// status CLI, and configure command in lockstep on resolution order.
|
|
50
49
|
return (0, config_js_1.resolveConfig)().oauth2ClientId;
|
|
51
50
|
}
|
|
52
|
-
function
|
|
51
|
+
function recordAuthActivity(client, decision) {
|
|
53
52
|
const status = decision.mode === "ok" || decision.mode === "refreshed" || decision.mode === "env_token"
|
|
54
53
|
? "ok"
|
|
55
54
|
: decision.mode === "declined"
|
|
@@ -57,7 +56,7 @@ function recordAuthSpan(client, decision) {
|
|
|
57
56
|
: decision.mode === "error"
|
|
58
57
|
? "error"
|
|
59
58
|
: "skipped";
|
|
60
|
-
client.
|
|
59
|
+
client.logger.activity("user.auth", status, {
|
|
61
60
|
attributes: {
|
|
62
61
|
mode: decision.mode,
|
|
63
62
|
reason: decision.reason,
|
|
@@ -71,34 +70,25 @@ function recordAuthSpan(client, decision) {
|
|
|
71
70
|
* to authenticate the human user. Always returns a decision; never throws.
|
|
72
71
|
*/
|
|
73
72
|
async function ensureUserAuthenticated(client, options) {
|
|
74
|
-
if (!isUserLoginEnabled()) {
|
|
75
|
-
const decision = {
|
|
76
|
-
proceed: true,
|
|
77
|
-
mode: "disabled",
|
|
78
|
-
reason: "User login is not enabled (ORY_USER_LOGIN env / userLogin config)",
|
|
79
|
-
};
|
|
80
|
-
recordAuthSpan(client, decision);
|
|
81
|
-
return decision;
|
|
82
|
-
}
|
|
83
73
|
try {
|
|
84
74
|
const decision = await runUserLogin(client, options);
|
|
85
75
|
attachUserPrincipal(client, decision);
|
|
86
|
-
|
|
76
|
+
recordAuthActivity(client, decision);
|
|
87
77
|
return decision;
|
|
88
78
|
}
|
|
89
79
|
catch (err) {
|
|
90
80
|
const decision = {
|
|
91
|
-
proceed:
|
|
81
|
+
proceed: true,
|
|
92
82
|
mode: "error",
|
|
93
83
|
reason: err instanceof Error ? err.message : String(err),
|
|
94
84
|
};
|
|
95
|
-
|
|
85
|
+
recordAuthActivity(client, decision);
|
|
96
86
|
return decision;
|
|
97
87
|
}
|
|
98
88
|
}
|
|
99
89
|
/**
|
|
100
90
|
* Attach the resolved user identity to the client so downstream
|
|
101
|
-
* permission checks and
|
|
91
|
+
* permission checks and activity events can reference it. Only fires for
|
|
102
92
|
* modes that actually authenticated a user — declined/skipped/error
|
|
103
93
|
* leave the principal alone.
|
|
104
94
|
*/
|
|
@@ -120,69 +110,43 @@ function attachUserPrincipal(client, decision) {
|
|
|
120
110
|
return;
|
|
121
111
|
}
|
|
122
112
|
if (decision.mode === "env_token") {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
process.env.ORY_SESSION_TOKEN ??
|
|
127
|
-
process.env.ORY_OAUTH2_TOKEN;
|
|
128
|
-
client.setUserPrincipal({
|
|
129
|
-
...(subject ? { subject } : {}),
|
|
130
|
-
...(token ? { token } : {}),
|
|
131
|
-
});
|
|
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)());
|
|
132
116
|
}
|
|
133
117
|
}
|
|
134
118
|
async function runUserLogin(client, options) {
|
|
135
119
|
const ttyCheck = options.isTtyAvailableFn ?? cli_js_1.isTtyAvailable;
|
|
136
120
|
const tty = ttyCheck();
|
|
137
121
|
let resolved = (0, config_js_1.resolveConfig)();
|
|
138
|
-
|
|
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) {
|
|
139
126
|
return {
|
|
140
127
|
proceed: true,
|
|
141
|
-
mode: "
|
|
142
|
-
reason:
|
|
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`,
|
|
143
130
|
};
|
|
144
131
|
}
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (!url) {
|
|
157
|
-
return {
|
|
158
|
-
proceed: !options.allowBlock,
|
|
159
|
-
mode: "declined",
|
|
160
|
-
reason: "User declined to enter an Ory project URL",
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
resolved = (0, config_js_1.resolveConfig)();
|
|
164
|
-
if (!resolved.projectUrl) {
|
|
165
|
-
return {
|
|
166
|
-
proceed: !options.allowBlock,
|
|
167
|
-
mode: "error",
|
|
168
|
-
reason: "Project URL was entered but config did not persist",
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// 2. Pre-supplied env tokens short-circuit the browser flow (CI, scripted
|
|
173
|
-
// runs). ORY_USER_* are the documented names; the legacy single-identity
|
|
174
|
-
// ORY_SESSION_TOKEN / ORY_OAUTH2_TOKEN are still honored.
|
|
175
|
-
if (process.env.ORY_USER_SESSION_TOKEN ||
|
|
176
|
-
process.env.ORY_USER_OAUTH2_TOKEN ||
|
|
177
|
-
process.env.ORY_SESSION_TOKEN ||
|
|
178
|
-
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) {
|
|
179
143
|
return {
|
|
180
144
|
proceed: true,
|
|
181
145
|
mode: "env_token",
|
|
182
|
-
reason: "
|
|
146
|
+
reason: "OAuth2 access token supplied via environment",
|
|
183
147
|
};
|
|
184
148
|
}
|
|
185
|
-
//
|
|
149
|
+
// 2. Persisted tokens.
|
|
186
150
|
const existing = (0, auth_store_js_1.loadTokens)();
|
|
187
151
|
if (existing && !(0, auth_store_js_1.isExpired)(existing)) {
|
|
188
152
|
return {
|
|
@@ -197,7 +161,7 @@ async function runUserLogin(client, options) {
|
|
|
197
161
|
if (cid) {
|
|
198
162
|
try {
|
|
199
163
|
const refreshed = await (0, auth_store_js_1.refreshAndSave)({
|
|
200
|
-
projectUrl
|
|
164
|
+
projectUrl,
|
|
201
165
|
clientId: cid,
|
|
202
166
|
current: existing,
|
|
203
167
|
});
|
|
@@ -216,19 +180,9 @@ async function runUserLogin(client, options) {
|
|
|
216
180
|
}
|
|
217
181
|
}
|
|
218
182
|
}
|
|
219
|
-
//
|
|
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.
|
|
220
185
|
const cid = clientId();
|
|
221
|
-
if (!cid) {
|
|
222
|
-
return {
|
|
223
|
-
proceed: !options.allowBlock,
|
|
224
|
-
mode: "skipped",
|
|
225
|
-
reason: "ORY_OAUTH2_CLIENT_ID is not set; cannot start browser login (run `" +
|
|
226
|
-
(0, cli_invocation_js_1.oryNpx)(options.binName) +
|
|
227
|
-
" configure --oauth2-client-id <id>` after registering a public OAuth2 client " +
|
|
228
|
-
"in your Ory project with all four loopback redirect URIs: " +
|
|
229
|
-
"http://127.0.0.1:47823..47826/callback)",
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
186
|
// No TTY check here on purpose: PKCE only needs a browser that can reach
|
|
233
187
|
// the loopback callback port, not stdin. `pkceLogin` prints the
|
|
234
188
|
// authorize URL to stderr so users in non-TTY environments (SSH without
|
|
@@ -236,7 +190,8 @@ async function runUserLogin(client, options) {
|
|
|
236
190
|
// complete sign-in by pasting the URL into any browser that can reach
|
|
237
191
|
// 127.0.0.1. Truly unattended runs (CI=true) are short-circuited by
|
|
238
192
|
// `pkceLogin` itself via `detectHeadless`, and operators can bypass
|
|
239
|
-
// login entirely
|
|
193
|
+
// login entirely by supplying `ORY_USER_OAUTH2_TOKEN`, and enforcement
|
|
194
|
+
// consequences are governed by `permissionMode`, not by this gate.
|
|
240
195
|
// PKCE-flight lock so concurrent processes share a single browser flow.
|
|
241
196
|
const lock = (0, auth_store_js_1.tryAcquirePkceFlightLock)();
|
|
242
197
|
if (!lock) {
|
|
@@ -251,7 +206,7 @@ async function runUserLogin(client, options) {
|
|
|
251
206
|
};
|
|
252
207
|
}
|
|
253
208
|
return {
|
|
254
|
-
proceed:
|
|
209
|
+
proceed: true,
|
|
255
210
|
mode: "skipped",
|
|
256
211
|
reason: "Another agent process is mid-login; gave up waiting",
|
|
257
212
|
};
|
|
@@ -259,7 +214,7 @@ async function runUserLogin(client, options) {
|
|
|
259
214
|
try {
|
|
260
215
|
const loginFn = options.loginFn ?? auth_js_1.pkceLogin;
|
|
261
216
|
const outcome = await loginFn({
|
|
262
|
-
projectUrl
|
|
217
|
+
projectUrl,
|
|
263
218
|
clientId: cid,
|
|
264
219
|
});
|
|
265
220
|
if (outcome.kind === "ok") {
|
|
@@ -272,15 +227,19 @@ async function runUserLogin(client, options) {
|
|
|
272
227
|
};
|
|
273
228
|
}
|
|
274
229
|
return {
|
|
275
|
-
proceed:
|
|
230
|
+
proceed: true,
|
|
276
231
|
mode: declineToMode(outcome.reason),
|
|
277
|
-
reason: declineMessage(outcome.reason),
|
|
232
|
+
reason: appendDetail(declineMessage(outcome.reason), outcome.detail),
|
|
278
233
|
};
|
|
279
234
|
}
|
|
280
235
|
finally {
|
|
281
236
|
lock.release();
|
|
282
237
|
}
|
|
283
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
|
+
}
|
|
284
243
|
function declineToMode(reason) {
|
|
285
244
|
switch (reason) {
|
|
286
245
|
case "user_denied":
|
|
@@ -293,6 +252,8 @@ function declineToMode(reason) {
|
|
|
293
252
|
return "skipped";
|
|
294
253
|
case "state_mismatch":
|
|
295
254
|
case "token_exchange_failed":
|
|
255
|
+
case "invalid_scope":
|
|
256
|
+
case "oauth_error":
|
|
296
257
|
return "error";
|
|
297
258
|
}
|
|
298
259
|
}
|
|
@@ -301,7 +262,13 @@ function declineMessage(reason) {
|
|
|
301
262
|
case "headless":
|
|
302
263
|
return "Browser login skipped: environment is headless (CI/SSH/no DISPLAY)";
|
|
303
264
|
case "timeout":
|
|
304
|
-
|
|
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");
|
|
305
272
|
case "user_denied":
|
|
306
273
|
return "User declined the OAuth2 consent screen";
|
|
307
274
|
case "state_mismatch":
|
|
@@ -314,6 +281,10 @@ function declineMessage(reason) {
|
|
|
314
281
|
return "Login was aborted by the caller";
|
|
315
282
|
case "token_exchange_failed":
|
|
316
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";
|
|
317
288
|
}
|
|
318
289
|
}
|
|
319
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.
|
|
3
|
+
"version": "1.0.0",
|
|
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,19 +59,13 @@
|
|
|
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
|
-
"
|
|
68
|
+
"web-tree-sitter": "^0.26.11"
|
|
77
69
|
},
|
|
78
70
|
"engines": {
|
|
79
71
|
"node": ">=22"
|