@ory/argus 1.2.3 → 1.3.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 +25 -0
- package/assets/skills/ory-build-agent/SKILL.md +1 -1
- package/assets/skills/ory-deploy-agent/SKILL.md +315 -0
- package/assets/skills/ory-deploy-openai-e2b/SKILL.md +289 -0
- package/assets/skills/ory-e2b-sandbox/SKILL.md +10 -6
- package/assets/skills/ory-temporal-worker/SKILL.md +4 -4
- package/dist/adapters.d.ts +11 -3
- package/dist/adapters.js +55 -13
- package/dist/agent-auth.d.ts +18 -7
- package/dist/agent-auth.js +120 -10
- package/dist/auth-store.d.ts +3 -3
- package/dist/auth-store.js +56 -12
- package/dist/auth.d.ts +1 -1
- package/dist/auth.js +1 -1
- package/dist/bash-parser.js +212 -47
- package/dist/build-info.json +4 -4
- package/dist/cli.js +5 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.js +24 -7
- package/dist/config.d.ts +23 -3
- package/dist/config.js +202 -18
- package/dist/contract-suite.d.ts +0 -8
- package/dist/contract-suite.js +0 -1
- package/dist/event-reporter.d.ts +1 -0
- package/dist/event-reporter.js +19 -2
- package/dist/hook-timeout.d.ts +2 -2
- package/dist/hook-timeout.js +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +13 -6
- package/dist/lifecycle.js +2 -1
- package/dist/local/configs.d.ts +2 -4
- package/dist/local/configs.js +2 -4
- package/dist/local/manager.js +4 -4
- package/dist/local/seed.d.ts +4 -4
- package/dist/local/seed.js +8 -10
- package/dist/permissions.d.ts +3 -3
- package/dist/permissions.js +26 -1
- package/dist/runtime-credential.d.ts +35 -0
- package/dist/runtime-credential.js +252 -1
- package/dist/runtime.d.ts +19 -12
- package/dist/runtime.js +69 -21
- package/dist/setup.js +3 -3
- package/dist/skills.d.ts +3 -5
- package/dist/skills.js +14 -5
- package/dist/subject.d.ts +2 -3
- package/dist/subject.js +10 -11
- package/dist/testing.d.ts +17 -0
- package/dist/testing.js +53 -0
- package/dist/tool-catalog.d.ts +17 -15
- package/dist/tool-catalog.js +239 -52
- package/dist/user-login.js +1 -1
- package/package.json +1 -1
package/dist/bash-parser.js
CHANGED
|
@@ -46,12 +46,75 @@ const LITERAL_LEAF_TYPES = new Set(["word", "string_content", "raw_string"]);
|
|
|
46
46
|
*/
|
|
47
47
|
const WRAPPERS = new Set([
|
|
48
48
|
"sudo", "doas", "env", "nohup", "setsid", "command", "exec", "nice",
|
|
49
|
-
"ionice", "chrt", "stdbuf", "time", "timeout", "xargs",
|
|
49
|
+
"ionice", "chrt", "stdbuf", "time", "timeout", "xargs", "builtin",
|
|
50
50
|
]);
|
|
51
|
-
/**
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
/** Wrapper options whose following token is an option value, not a command. */
|
|
52
|
+
const WRAPPER_OPTIONS_WITH_VALUES = {
|
|
53
|
+
doas: new Set(["-a", "-C", "-u"]),
|
|
54
|
+
sudo: new Set([
|
|
55
|
+
"-u", "--user", "-g", "--group", "-h", "--host", "-p", "--prompt",
|
|
56
|
+
"-C", "--close-from", "-T", "--command-timeout", "-R", "--chroot",
|
|
57
|
+
"-D", "--chdir", "-r", "--role", "-t", "--type",
|
|
58
|
+
]),
|
|
59
|
+
env: new Set([
|
|
60
|
+
"-u", "--unset", "-C", "--chdir",
|
|
61
|
+
]),
|
|
62
|
+
nice: new Set(["-n", "--adjustment"]),
|
|
63
|
+
exec: new Set(["-a"]),
|
|
64
|
+
ionice: new Set([
|
|
65
|
+
"-c", "--class", "-n", "--classdata", "-p", "--pid", "-P", "--pgid",
|
|
66
|
+
"-u", "--uid",
|
|
67
|
+
]),
|
|
68
|
+
timeout: new Set(["-s", "--signal", "-k", "--kill-after"]),
|
|
69
|
+
stdbuf: new Set(["-i", "--input", "-o", "--output", "-e", "--error"]),
|
|
70
|
+
xargs: new Set([
|
|
71
|
+
"-a", "--arg-file", "-E", "--eof", "-I", "--replace", "-L",
|
|
72
|
+
"--max-lines", "-n", "--max-args", "-P", "--max-procs", "-s",
|
|
73
|
+
"--max-chars",
|
|
74
|
+
]),
|
|
75
|
+
};
|
|
76
|
+
/** Common standalone options that do not consume the following command token. */
|
|
77
|
+
const WRAPPER_OPTIONS_WITHOUT_VALUES = {
|
|
78
|
+
doas: new Set(["-L", "-n", "-s"]),
|
|
79
|
+
sudo: new Set([
|
|
80
|
+
"-A", "--askpass", "-b", "--background", "-E", "--preserve-env",
|
|
81
|
+
"-H", "--set-home", "-K", "-k", "--reset-timestamp", "-N",
|
|
82
|
+
"--no-update", "-n", "--non-interactive", "-S", "--stdin", "-V",
|
|
83
|
+
"--version", "-v", "--validate",
|
|
84
|
+
]),
|
|
85
|
+
env: new Set([
|
|
86
|
+
"-i", "--ignore-environment", "-0", "--null", "-v", "--debug",
|
|
87
|
+
"--ignore-signal", "--default-signal", "--block-signal",
|
|
88
|
+
]),
|
|
89
|
+
command: new Set(["-p", "-v", "-V"]),
|
|
90
|
+
chrt: new Set([
|
|
91
|
+
"-a", "--all-tasks", "-b", "--batch", "-d", "--deadline", "-f", "--fifo",
|
|
92
|
+
"-i", "--idle", "-m", "--max", "-o", "--other", "-r", "--rr",
|
|
93
|
+
]),
|
|
94
|
+
exec: new Set(["-c", "-l"]),
|
|
95
|
+
setsid: new Set(["-c", "--ctty", "-f", "--fork", "-w", "--wait"]),
|
|
96
|
+
time: new Set(["-p"]),
|
|
97
|
+
timeout: new Set(["--foreground", "--preserve-status", "-v", "--verbose"]),
|
|
98
|
+
xargs: new Set([
|
|
99
|
+
"-0", "--null", "-r", "--no-run-if-empty", "-t", "--verbose", "-x",
|
|
100
|
+
"--exit",
|
|
101
|
+
]),
|
|
102
|
+
};
|
|
103
|
+
const WRAPPER_OPTIONS_WITH_ATTACHED_VALUES = {
|
|
104
|
+
doas: /^-[aCu].+/,
|
|
105
|
+
sudo: /^-[ughCpTDRrt].+/,
|
|
106
|
+
nice: /^-(?:n.+|\d+)$/,
|
|
107
|
+
exec: /^-a.+/,
|
|
108
|
+
ionice: /^-[cnpPu].+/,
|
|
109
|
+
timeout: /^-[sk].+/,
|
|
110
|
+
stdbuf: /^-[ioe].+/,
|
|
111
|
+
xargs: /^-[ELnPs].+/,
|
|
112
|
+
};
|
|
113
|
+
/** Wrappers with a required positional operand before the wrapped command. */
|
|
114
|
+
const WRAPPER_POSITIONAL_OPERANDS = {
|
|
115
|
+
timeout: 1,
|
|
116
|
+
chrt: 1,
|
|
117
|
+
};
|
|
55
118
|
/** Shells whose `-c "<script>"` argument is re-parsed as a nested command. */
|
|
56
119
|
const SHELL_DASH_C = new Set(["sh", "bash", "dash", "zsh", "ksh", "ash"]);
|
|
57
120
|
/** Cap re-parse recursion (`sh -c "sh -c …"`) so a pathological input can't loop. */
|
|
@@ -122,6 +185,8 @@ function lastLeaf(node) {
|
|
|
122
185
|
function literalText(node) {
|
|
123
186
|
switch (node.type) {
|
|
124
187
|
case "word":
|
|
188
|
+
// Unquoted globs depend on the runtime filesystem and are not literal.
|
|
189
|
+
return /[*?[]/.test(node.text) ? null : node.text;
|
|
125
190
|
case "number":
|
|
126
191
|
return node.text;
|
|
127
192
|
case "raw_string":
|
|
@@ -150,10 +215,10 @@ function literalText(node) {
|
|
|
150
215
|
function resolveCommandName(nameNode) {
|
|
151
216
|
const child = nameNode.namedChild(0);
|
|
152
217
|
if (!child)
|
|
153
|
-
return basename(nameNode.text);
|
|
218
|
+
return /[*?[]/.test(nameNode.text) ? null : basename(nameNode.text);
|
|
154
219
|
switch (child.type) {
|
|
155
220
|
case "word":
|
|
156
|
-
return basename(child.text);
|
|
221
|
+
return /[*?[]/.test(child.text) ? null : basename(child.text);
|
|
157
222
|
case "raw_string":
|
|
158
223
|
return basename(stripQuotes(child.text));
|
|
159
224
|
case "simple_expansion":
|
|
@@ -227,20 +292,55 @@ function collectRedirects(cmd) {
|
|
|
227
292
|
* {@link ShellCommand} for that command (and recursing through nested wrappers,
|
|
228
293
|
* e.g. `sudo env A=1 curl`).
|
|
229
294
|
*/
|
|
230
|
-
function descendWrapper(wrapper, args, pipeline, state) {
|
|
231
|
-
|
|
295
|
+
function descendWrapper(wrapper, args, pipeline, parser, depth, state) {
|
|
296
|
+
let positionalOperands = WRAPPER_POSITIONAL_OPERANDS[wrapper] ?? 0;
|
|
297
|
+
let optionsEnded = false;
|
|
232
298
|
for (let i = 0; i < args.length; i++) {
|
|
233
299
|
const txt = literalText(args[i]);
|
|
234
300
|
if (txt === null) {
|
|
235
301
|
state.tooComplex = true;
|
|
236
302
|
return;
|
|
237
303
|
}
|
|
238
|
-
if (
|
|
304
|
+
if (wrapper === "env" && /^--s[^=]*=/.test(txt)) {
|
|
305
|
+
state.tooComplex = true;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (wrapper === "env" && (txt === "-S" || txt === "--split-string")) {
|
|
309
|
+
// env's split-string language is not shell syntax. Parsing it with the
|
|
310
|
+
// Bash grammar can reinterpret comments and quoting, so fail closed.
|
|
311
|
+
state.tooComplex = true;
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (!optionsEnded && txt.startsWith("-")) {
|
|
315
|
+
if (txt === "--") {
|
|
316
|
+
optionsEnded = true;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (txt.includes("="))
|
|
320
|
+
continue;
|
|
321
|
+
if (WRAPPER_OPTIONS_WITH_ATTACHED_VALUES[wrapper]?.test(txt))
|
|
322
|
+
continue;
|
|
323
|
+
if (WRAPPER_OPTIONS_WITH_VALUES[wrapper]?.has(txt)) {
|
|
324
|
+
if (!args[i + 1] || literalText(args[i + 1]) === null) {
|
|
325
|
+
state.tooComplex = true;
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
i++;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (!WRAPPER_OPTIONS_WITHOUT_VALUES[wrapper]?.has(txt)) {
|
|
332
|
+
// An unknown option may consume the next token. Do not guess which
|
|
333
|
+
// token is the command because a wrong guess would silently bypass it.
|
|
334
|
+
state.tooComplex = true;
|
|
335
|
+
}
|
|
239
336
|
continue;
|
|
337
|
+
}
|
|
240
338
|
if (/^[A-Za-z_]\w*=/.test(txt))
|
|
241
339
|
continue;
|
|
242
|
-
if (
|
|
340
|
+
if (positionalOperands > 0) {
|
|
341
|
+
positionalOperands--;
|
|
243
342
|
continue;
|
|
343
|
+
}
|
|
244
344
|
const wrapped = basename(txt);
|
|
245
345
|
if (wrapped.length === 0)
|
|
246
346
|
return;
|
|
@@ -252,11 +352,92 @@ function descendWrapper(wrapper, args, pipeline, state) {
|
|
|
252
352
|
redirects: [],
|
|
253
353
|
pipeline,
|
|
254
354
|
});
|
|
255
|
-
if (
|
|
256
|
-
|
|
355
|
+
if (SHELL_DASH_C.has(wrapped) && state.pipelineIds.has(pipeline)) {
|
|
356
|
+
if (shellReadsPipelineInput(rest))
|
|
357
|
+
state.tooComplex = true;
|
|
358
|
+
}
|
|
359
|
+
if (WRAPPERS.has(wrapped)) {
|
|
360
|
+
descendWrapper(wrapped, rest, pipeline, parser, depth, state);
|
|
361
|
+
}
|
|
362
|
+
inspectLiteralScriptInvocation(wrapped, rest, parser, state, depth);
|
|
257
363
|
return;
|
|
258
364
|
}
|
|
259
365
|
}
|
|
366
|
+
function shellReadsPipelineInput(args) {
|
|
367
|
+
for (let i = 0; i < args.length; i++) {
|
|
368
|
+
const arg = literalText(args[i]);
|
|
369
|
+
if (arg === null)
|
|
370
|
+
return true;
|
|
371
|
+
if (arg === "--")
|
|
372
|
+
return i + 1 >= args.length;
|
|
373
|
+
if (arg === "-O" || arg === "-o" || arg === "--init-file" || arg === "--rcfile") {
|
|
374
|
+
i++;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (arg.startsWith("-")) {
|
|
378
|
+
if (/^-[^-]*c/.test(arg))
|
|
379
|
+
return false;
|
|
380
|
+
if (/^-[^-]*s/.test(arg))
|
|
381
|
+
return true;
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
const normalized = arg.startsWith("/")
|
|
385
|
+
? `/${arg.split("/").reduce((parts, part) => {
|
|
386
|
+
if (!part || part === ".")
|
|
387
|
+
return parts;
|
|
388
|
+
if (part === "..")
|
|
389
|
+
parts.pop();
|
|
390
|
+
else
|
|
391
|
+
parts.push(part);
|
|
392
|
+
return parts;
|
|
393
|
+
}, []).join("/")}`
|
|
394
|
+
: arg;
|
|
395
|
+
if (arg === "-"
|
|
396
|
+
|| normalized === "/dev/stdin"
|
|
397
|
+
|| /^\/dev\/fd\/0+$/.test(normalized)
|
|
398
|
+
|| /^\/proc\/self\/fd\/0+$/.test(normalized))
|
|
399
|
+
return true;
|
|
400
|
+
// A positional operand is a script file, so pipeline stdin is data rather
|
|
401
|
+
// than shell source and does not hide additional command words.
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
function inspectLiteralScriptInvocation(name, args, parser, state, depth) {
|
|
407
|
+
if (SHELL_DASH_C.has(name)) {
|
|
408
|
+
for (let i = 0; i < args.length; i++) {
|
|
409
|
+
const text = literalText(args[i]);
|
|
410
|
+
if (text === null) {
|
|
411
|
+
state.tooComplex = true;
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
if (text === "--" || !text.startsWith("-"))
|
|
415
|
+
break;
|
|
416
|
+
if (text === "-O" || text === "-o" || text === "--init-file" || text === "--rcfile") {
|
|
417
|
+
i++;
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
if (/^-[^-]*c/.test(text)) {
|
|
421
|
+
const target = args[i + 1];
|
|
422
|
+
const script = target ? literalText(target) : null;
|
|
423
|
+
if (script === null)
|
|
424
|
+
state.tooComplex = true;
|
|
425
|
+
else
|
|
426
|
+
reparse(script, parser, state, depth + 1);
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (name === "eval") {
|
|
432
|
+
const parts = args.map(literalText);
|
|
433
|
+
if (parts.length === 0 || parts.some((part) => part === null)) {
|
|
434
|
+
state.tooComplex = true;
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
reparse(parts.join(" "), parser, state, depth + 1);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
260
441
|
/** Recursively walk the tree, building the command model. */
|
|
261
442
|
function walk(node, pipeline, parser, state, depth, treeDepth) {
|
|
262
443
|
// Bound recursion so a maliciously deep AST can't overflow the stack (which
|
|
@@ -267,6 +448,7 @@ function walk(node, pipeline, parser, state, depth, treeDepth) {
|
|
|
267
448
|
}
|
|
268
449
|
if (node.type === "pipeline") {
|
|
269
450
|
const id = state.pipelineCounter++;
|
|
451
|
+
state.pipelineIds.add(id);
|
|
270
452
|
for (const child of node.children)
|
|
271
453
|
walk(child, id, parser, state, depth, treeDepth + 1);
|
|
272
454
|
return;
|
|
@@ -291,41 +473,15 @@ function walk(node, pipeline, parser, state, depth, treeDepth) {
|
|
|
291
473
|
redirects: collectRedirects(node),
|
|
292
474
|
pipeline: pid,
|
|
293
475
|
});
|
|
294
|
-
if (name && WRAPPERS.has(name))
|
|
295
|
-
descendWrapper(name, args, pid, state);
|
|
296
|
-
if (name && SHELL_DASH_C.has(name)) {
|
|
297
|
-
const flagIdx = args.findIndex((a) => {
|
|
298
|
-
const t = literalText(a);
|
|
299
|
-
return t !== null && t.startsWith("-") && t.includes("c");
|
|
300
|
-
});
|
|
301
|
-
if (flagIdx >= 0) {
|
|
302
|
-
const target = args[flagIdx + 1];
|
|
303
|
-
if (!target)
|
|
304
|
-
state.tooComplex = true;
|
|
305
|
-
else {
|
|
306
|
-
const script = literalText(target);
|
|
307
|
-
if (script === null)
|
|
308
|
-
state.tooComplex = true;
|
|
309
|
-
else
|
|
310
|
-
reparse(script, parser, state, depth + 1);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
476
|
+
if (name && WRAPPERS.has(name)) {
|
|
477
|
+
descendWrapper(name, args, pid, parser, depth, state);
|
|
313
478
|
}
|
|
314
|
-
if (name
|
|
315
|
-
|
|
316
|
-
let dynamic = false;
|
|
317
|
-
for (const a of args) {
|
|
318
|
-
const t = literalText(a);
|
|
319
|
-
if (t === null)
|
|
320
|
-
dynamic = true;
|
|
321
|
-
else
|
|
322
|
-
parts.push(t);
|
|
323
|
-
}
|
|
324
|
-
if (dynamic || parts.length === 0)
|
|
479
|
+
if (name && SHELL_DASH_C.has(name) && state.pipelineIds.has(pid)) {
|
|
480
|
+
if (shellReadsPipelineInput(args))
|
|
325
481
|
state.tooComplex = true;
|
|
326
|
-
else
|
|
327
|
-
reparse(parts.join(" "), parser, state, depth + 1);
|
|
328
482
|
}
|
|
483
|
+
if (name)
|
|
484
|
+
inspectLiteralScriptInvocation(name, args, parser, state, depth);
|
|
329
485
|
// Descend into arguments for nested command substitutions ($(curl …)).
|
|
330
486
|
for (const child of node.children)
|
|
331
487
|
walk(child, -1, parser, state, depth, treeDepth + 1);
|
|
@@ -337,7 +493,11 @@ function walk(node, pipeline, parser, state, depth, treeDepth) {
|
|
|
337
493
|
}
|
|
338
494
|
/** Parse a nested script string and fold its commands into `state`. */
|
|
339
495
|
function reparse(script, parser, state, depth) {
|
|
340
|
-
if (depth > MAX_REPARSE_DEPTH
|
|
496
|
+
if (depth > MAX_REPARSE_DEPTH) {
|
|
497
|
+
state.tooComplex = true;
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (script.trim().length === 0)
|
|
341
501
|
return;
|
|
342
502
|
const tree = parser.parse(script);
|
|
343
503
|
if (!tree) {
|
|
@@ -370,7 +530,12 @@ async function extractShellCommands(command) {
|
|
|
370
530
|
}
|
|
371
531
|
if (!tree)
|
|
372
532
|
return { commands: [], tooComplex: true };
|
|
373
|
-
const state = {
|
|
533
|
+
const state = {
|
|
534
|
+
commands: [],
|
|
535
|
+
tooComplex: false,
|
|
536
|
+
pipelineCounter: 0,
|
|
537
|
+
pipelineIds: new Set(),
|
|
538
|
+
};
|
|
374
539
|
if (command.includes("\\\n"))
|
|
375
540
|
state.tooComplex = true;
|
|
376
541
|
if (tree.rootNode.hasError)
|
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"repo": "ory-agent-plugins",
|
|
3
|
-
"commit": "
|
|
4
|
-
"commitShort": "
|
|
3
|
+
"commit": "7f6431efd4116e683b164cf2c7300cb44d0c4dcb",
|
|
4
|
+
"commitShort": "7f6431e",
|
|
5
5
|
"branch": "main",
|
|
6
|
-
"commitDate": "2026-09-
|
|
6
|
+
"commitDate": "2026-09-28T11:06:31-07:00",
|
|
7
7
|
"dirty": false,
|
|
8
|
-
"builtAt": "2026-09-
|
|
8
|
+
"builtAt": "2026-09-28T18:10:11.171Z"
|
|
9
9
|
}
|
package/dist/cli.js
CHANGED
|
@@ -300,6 +300,11 @@ function printEnvironment() {
|
|
|
300
300
|
],
|
|
301
301
|
["ORY_USER_SUBJECT_ID", process.env.ORY_USER_SUBJECT_ID, false],
|
|
302
302
|
["ORY_AGENT_SUBJECT_ID", process.env.ORY_AGENT_SUBJECT_ID, false],
|
|
303
|
+
[
|
|
304
|
+
"ORY_AGENT_ROOT_DELEGATION",
|
|
305
|
+
process.env.ORY_AGENT_ROOT_DELEGATION ? "(set)" : undefined,
|
|
306
|
+
false,
|
|
307
|
+
],
|
|
303
308
|
["ORY_AGENT_DEBUG", process.env.ORY_AGENT_DEBUG, false],
|
|
304
309
|
["ORY_AGENT_LOG_FILE", process.env.ORY_AGENT_LOG_FILE, false],
|
|
305
310
|
];
|
package/dist/client.d.ts
CHANGED
|
@@ -93,6 +93,14 @@ export interface RecordDelegationResult {
|
|
|
93
93
|
/** Root-first chain, e.g. `["User:<id>", "agent:<join>"]`. */
|
|
94
94
|
delegationChain: string[];
|
|
95
95
|
}
|
|
96
|
+
/** Administrator-provisioned runtime metadata resolved from Agent Security. */
|
|
97
|
+
export interface RuntimeContext {
|
|
98
|
+
authenticated: true;
|
|
99
|
+
subjectId: string;
|
|
100
|
+
rootDelegation: string;
|
|
101
|
+
deploymentId: string;
|
|
102
|
+
agentType: string;
|
|
103
|
+
}
|
|
96
104
|
/**
|
|
97
105
|
* A principal known to the client. Either the human user (interactive
|
|
98
106
|
* PKCE login) or the agent (machine credentials).
|
|
@@ -111,6 +119,7 @@ export declare class OryAgentClient {
|
|
|
111
119
|
readonly logger: DebugLogger;
|
|
112
120
|
private readonly eventReporter?;
|
|
113
121
|
private _runtimeCredential?;
|
|
122
|
+
private _runtimeContext?;
|
|
114
123
|
private readonly _runtimeCredentialsBySession;
|
|
115
124
|
private readonly _runtimeCredentialsByPrincipalKey;
|
|
116
125
|
private sessionCache;
|
|
@@ -146,6 +155,10 @@ export declare class OryAgentClient {
|
|
|
146
155
|
/** Active noninteractive broker credential. It is process memory only. */
|
|
147
156
|
get runtimeCredential(): RuntimeCredential | undefined;
|
|
148
157
|
get hasRuntimeCredential(): boolean;
|
|
158
|
+
/** Process-local deployment context. It is never persisted or copied to the environment. */
|
|
159
|
+
get runtimeContext(): RuntimeContext | undefined;
|
|
160
|
+
get runtimeRootDelegation(): string | undefined;
|
|
161
|
+
setRuntimeContext(context: RuntimeContext): void;
|
|
149
162
|
setRuntimeCredential(credential: RuntimeCredential | undefined): void;
|
|
150
163
|
/** Retain an actor-scoped credential for deferred event delivery without activating it globally. */
|
|
151
164
|
rememberRuntimeCredential(credential: RuntimeCredential): void;
|
|
@@ -251,6 +264,8 @@ export declare class OryAgentClient {
|
|
|
251
264
|
agentToken?: string;
|
|
252
265
|
runtimeCredential?: RuntimeCredential;
|
|
253
266
|
signal?: AbortSignal;
|
|
267
|
+
/** Authenticate without a user bearer for a preprovisioned root lineage. */
|
|
268
|
+
preprovisionedRoot?: boolean;
|
|
254
269
|
}): Promise<RecordDelegationResult>;
|
|
255
270
|
/**
|
|
256
271
|
* Activity attributes describing which principals are attached to the
|
package/dist/client.js
CHANGED
|
@@ -81,6 +81,7 @@ class OryAgentClient {
|
|
|
81
81
|
logger;
|
|
82
82
|
eventReporter;
|
|
83
83
|
_runtimeCredential;
|
|
84
|
+
_runtimeContext;
|
|
84
85
|
_runtimeCredentialsBySession = new Map();
|
|
85
86
|
_runtimeCredentialsByPrincipalKey = new Map();
|
|
86
87
|
sessionCache = new Map();
|
|
@@ -341,6 +342,17 @@ class OryAgentClient {
|
|
|
341
342
|
get hasRuntimeCredential() {
|
|
342
343
|
return this.runtimeCredential !== undefined;
|
|
343
344
|
}
|
|
345
|
+
/** Process-local deployment context. It is never persisted or copied to the environment. */
|
|
346
|
+
get runtimeContext() {
|
|
347
|
+
return this._runtimeContext ? { ...this._runtimeContext } : undefined;
|
|
348
|
+
}
|
|
349
|
+
get runtimeRootDelegation() {
|
|
350
|
+
return this._runtimeContext?.rootDelegation;
|
|
351
|
+
}
|
|
352
|
+
setRuntimeContext(context) {
|
|
353
|
+
this._runtimeContext = { ...context };
|
|
354
|
+
this.setAgentPrincipal({ subject: context.subjectId });
|
|
355
|
+
}
|
|
344
356
|
setRuntimeCredential(credential) {
|
|
345
357
|
const sessionId = this.sessionId?.trim();
|
|
346
358
|
if (sessionId) {
|
|
@@ -441,7 +453,10 @@ class OryAgentClient {
|
|
|
441
453
|
emitActivity: false,
|
|
442
454
|
signal: controller.signal,
|
|
443
455
|
}));
|
|
444
|
-
if (identity.
|
|
456
|
+
if (identity.runtimeCredential) {
|
|
457
|
+
this.rememberRuntimeCredential(identity.runtimeCredential);
|
|
458
|
+
}
|
|
459
|
+
else if (identity.subject && identity.token) {
|
|
445
460
|
this._eventPrincipalsByKey.set((0, event_reporter_js_1.eventPrincipalKey)(identity.subject), {
|
|
446
461
|
subject: identity.subject,
|
|
447
462
|
token: identity.token,
|
|
@@ -814,28 +829,30 @@ class OryAgentClient {
|
|
|
814
829
|
};
|
|
815
830
|
const activity = this.logger.startActivity("delegation.record", { delegationKind: input.kind, ...options?.activityAttributes }, sessionId);
|
|
816
831
|
const url = `${this.agentSecurityUrl.replace(/\/+$/, "")}/agent-security/v1/delegations:record`;
|
|
817
|
-
//
|
|
818
|
-
//
|
|
832
|
+
// Interactive lineage uses separate user and runtime proofs. A validated
|
|
833
|
+
// preprovisioned root has no user bearer; its acting runtime authenticates
|
|
834
|
+
// directly in Authorization.
|
|
819
835
|
const userToken = this.userPrincipalForSession(sessionId).token;
|
|
820
836
|
const runtimeCredential = options?.runtimeCredential ?? (options?.agentToken
|
|
821
837
|
? (0, runtime_credential_js_1.talosRuntimeCredential)(options.agentToken)
|
|
822
838
|
: this.runtimeCredential);
|
|
823
839
|
try {
|
|
824
|
-
if (!
|
|
840
|
+
if (!runtimeCredential || (!userToken && !options?.preprovisionedRoot)) {
|
|
825
841
|
throw httpError(403, "User token and runtime credential are required");
|
|
826
842
|
}
|
|
843
|
+
const runtimeOnly = options?.preprovisionedRoot === true;
|
|
827
844
|
const res = await (0, runtime_credential_js_1.authenticatedRuntimeRequest)(runtimeCredential, {
|
|
828
845
|
url,
|
|
829
846
|
init: {
|
|
830
847
|
method: "POST",
|
|
831
848
|
headers: {
|
|
832
849
|
"content-type": "application/json",
|
|
833
|
-
authorization: `Bearer ${userToken}
|
|
850
|
+
...(!runtimeOnly && userToken ? { authorization: `Bearer ${userToken}` } : {}),
|
|
834
851
|
},
|
|
835
852
|
body: JSON.stringify(body),
|
|
836
853
|
signal: options?.signal ?? AbortSignal.timeout(30_000),
|
|
837
854
|
},
|
|
838
|
-
authorizationHeader: "x-ory-agent-authorization",
|
|
855
|
+
authorizationHeader: runtimeOnly ? "authorization" : "x-ory-agent-authorization",
|
|
839
856
|
fetch: options?.fetchImpl,
|
|
840
857
|
});
|
|
841
858
|
if (!res.ok) {
|
|
@@ -1104,7 +1121,7 @@ function rehydrateUserPrincipal(client) {
|
|
|
1104
1121
|
// without persisting it — so without this branch `ORY_USER_OAUTH2_TOKEN`
|
|
1105
1122
|
// authenticated the session and then silently stopped
|
|
1106
1123
|
// applying on every subsequent event. On a subprocess harness that left the
|
|
1107
|
-
// tool-call client with no user credential for delegation or
|
|
1124
|
+
// tool-call client with no user credential for delegation or Talos enrollment.
|
|
1108
1125
|
const env = (0, auth_store_js_1.readEnvUserCredential)();
|
|
1109
1126
|
const tokens = (0, auth_store_js_1.loadTokens)();
|
|
1110
1127
|
if (env.token) {
|
package/dist/config.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface OryOAuth2Tokens {
|
|
|
12
12
|
expiresAt: number;
|
|
13
13
|
/** Subject (sub claim) the token was issued for. */
|
|
14
14
|
subject?: string;
|
|
15
|
-
/** Client ID the token was issued under.
|
|
15
|
+
/** Client ID the token was issued under. Persisted to bind refreshes to that client. */
|
|
16
16
|
clientId?: string;
|
|
17
17
|
/** @deprecated Accepted in memory for older callers; never persisted. */
|
|
18
18
|
idToken?: string;
|
|
@@ -104,6 +104,14 @@ export interface OryAgentDynamicCredentials {
|
|
|
104
104
|
* bounded so a long-lived install's config doesn't grow without limit.
|
|
105
105
|
*/
|
|
106
106
|
export declare const DELEGATION_NODE_HISTORY = 10;
|
|
107
|
+
/** Opaque, administrator-provisioned root delegation node for headless agents. */
|
|
108
|
+
export declare const AGENT_ROOT_DELEGATION_ENV_VAR = "ORY_AGENT_ROOT_DELEGATION";
|
|
109
|
+
/** Validate an opaque broker node without interpreting its server-owned format. */
|
|
110
|
+
export declare function validateAgentRootDelegation(raw: string | undefined): {
|
|
111
|
+
value?: string;
|
|
112
|
+
warning?: string;
|
|
113
|
+
};
|
|
114
|
+
export declare function readAgentRootDelegation(env?: NodeJS.ProcessEnv, warnings?: string[]): string | undefined;
|
|
107
115
|
/**
|
|
108
116
|
* Credentials for the AI agent principal (machine identity). Every harness
|
|
109
117
|
* session owns a distinct DCR client, and every typed sub-agent in that session
|
|
@@ -183,7 +191,7 @@ export interface OryPluginConfig {
|
|
|
183
191
|
projectUrl?: string;
|
|
184
192
|
/**
|
|
185
193
|
* Canonical Ory Agent Security broker URL. Broker calls resolve this value
|
|
186
|
-
* independently from {@link projectUrl}, which remains the
|
|
194
|
+
* independently from {@link projectUrl}, which remains the project and user-login origin.
|
|
187
195
|
* Older configs fall back to `projectUrl` until this key is persisted.
|
|
188
196
|
*/
|
|
189
197
|
agentSecurityUrl?: string;
|
|
@@ -250,7 +258,7 @@ export interface OryPluginConfig {
|
|
|
250
258
|
}
|
|
251
259
|
/**
|
|
252
260
|
* Single OS-agnostic data directory for *all* Ory agent plugin state:
|
|
253
|
-
* the shared config file, persisted DCR credentials, and any harness
|
|
261
|
+
* the shared config file, persisted legacy DCR credentials, and any harness
|
|
254
262
|
* asset directories. One place per platform — never split across
|
|
255
263
|
* `~/.ory/` and `~/.config/`.
|
|
256
264
|
*
|
|
@@ -317,6 +325,18 @@ export declare function saveConfig(update: Partial<{
|
|
|
317
325
|
* atomic via write-temp + rename.
|
|
318
326
|
*/
|
|
319
327
|
export declare function mutateConfig(mutator: (current: OryPluginConfig) => OryPluginConfig | undefined): void;
|
|
328
|
+
export interface AsyncConfigMutation<T> {
|
|
329
|
+
/** New config to persist while the lock is still held. Omit for a read-only result. */
|
|
330
|
+
config?: OryPluginConfig;
|
|
331
|
+
value: T;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Async read-modify-write transaction using the same config.json.lock as
|
|
335
|
+
* mutateConfig. The lock remains held while the callback awaits, which lets
|
|
336
|
+
* token refresh serialize its read/check/network/write sequence across
|
|
337
|
+
* JavaScript and Go processes without blocking the JavaScript event loop.
|
|
338
|
+
*/
|
|
339
|
+
export declare function mutateConfigAsync<T>(mutator: (current: OryPluginConfig) => Promise<AsyncConfigMutation<T>>): Promise<T>;
|
|
320
340
|
/**
|
|
321
341
|
* True when `value` looks like an Ory project **id** (a UUID) rather than a
|
|
322
342
|
* project **slug**. Project slugs are human-readable tokens (e.g.
|