@ory/argus 0.11.1 → 0.12.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.
- package/dist/agent-auth.d.ts +23 -0
- package/dist/agent-auth.js +42 -1
- package/dist/auth.js +76 -3
- package/dist/cli-invocation.d.ts +34 -0
- package/dist/cli-invocation.js +60 -0
- package/dist/cli.d.ts +15 -5
- package/dist/cli.js +161 -53
- package/dist/client.d.ts +27 -0
- package/dist/client.js +85 -5
- package/dist/config.d.ts +61 -0
- package/dist/config.js +81 -5
- package/dist/dev.d.ts +8 -0
- package/dist/dev.js +12 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +17 -3
- package/dist/interactive-setup.d.ts +123 -0
- package/dist/interactive-setup.js +1203 -0
- package/dist/local/cli.js +12 -11
- package/dist/local/index.d.ts +1 -1
- package/dist/local/index.js +3 -1
- package/dist/local/manager.js +1 -1
- package/dist/permissions-cli.js +55 -19
- package/dist/registry/config.js +7 -0
- package/dist/setup.d.ts +43 -3
- package/dist/setup.js +116 -23
- package/dist/status-cli.d.ts +2 -1
- package/dist/status-cli.js +34 -13
- package/dist/subject.d.ts +8 -5
- package/dist/subject.js +9 -6
- package/dist/tool-catalog.d.ts +2 -2
- package/dist/tool-catalog.js +14 -6
- package/dist/ui.d.ts +144 -0
- package/dist/ui.js +270 -0
- package/dist/uninstall.d.ts +47 -0
- package/dist/uninstall.js +90 -0
- package/dist/user-login.d.ts +5 -3
- package/dist/user-login.js +13 -9
- package/package.json +3 -2
package/dist/ui.d.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny, dependency-free terminal-UI kit for the interactive install flow.
|
|
3
|
+
*
|
|
4
|
+
* The install wizard is the one place a plugin talks to a human at a real
|
|
5
|
+
* terminal, so it's worth making it look the part: colored status glyphs,
|
|
6
|
+
* a framed intro banner, aligned key/value summaries, and highlighted menus.
|
|
7
|
+
* Everything here degrades gracefully — colors and Unicode glyphs are emitted
|
|
8
|
+
* only when the output is an interactive TTY that opts in, and collapse to
|
|
9
|
+
* plain ASCII otherwise. That keeps the output copy-paste-clean in logs and,
|
|
10
|
+
* crucially, byte-for-byte stable under test (where stdout isn't a TTY), so
|
|
11
|
+
* the wizard's plain-text content can still be asserted on.
|
|
12
|
+
*
|
|
13
|
+
* No external dependency: adding a prompt/print library to `@ory/argus` would
|
|
14
|
+
* push that weight onto every harness plugin that depends on core. A handful
|
|
15
|
+
* of ANSI escapes is all this needs.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Whether ANSI color/style codes should be emitted. Honors the `NO_COLOR`
|
|
19
|
+
* convention and `FORCE_COLOR`, treats `TERM=dumb` as uncolored, and otherwise
|
|
20
|
+
* follows whether stdout is an interactive TTY. Evaluated per call so tests and
|
|
21
|
+
* piped output (no TTY) stay plain without any global setup.
|
|
22
|
+
*/
|
|
23
|
+
export declare function colorEnabled(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Color depth the terminal can render, used to pick the best representation of
|
|
26
|
+
* the brand palette (see {@link brand}):
|
|
27
|
+
*
|
|
28
|
+
* 3 — 24-bit truecolor (`COLORTERM=truecolor|24bit`, or `FORCE_COLOR=3`): the
|
|
29
|
+
* exact Ory hex values are emitted.
|
|
30
|
+
* 2 — 256-color (`TERM` contains `256`, or `FORCE_COLOR=2`): the nearest
|
|
31
|
+
* xterm-256 index.
|
|
32
|
+
* 1 — basic 16-color: the nearest ANSI SGR code.
|
|
33
|
+
* 0 — color disabled (see {@link colorEnabled}).
|
|
34
|
+
*
|
|
35
|
+
* Apple Terminal, for instance, advertises `xterm-256color` but not truecolor,
|
|
36
|
+
* so it lands on level 2 and still gets a recognizably-indigo accent.
|
|
37
|
+
*/
|
|
38
|
+
export declare function colorLevel(): 0 | 1 | 2 | 3;
|
|
39
|
+
/** ANSI text stylers. Each is a no-op passthrough when color is disabled. */
|
|
40
|
+
export declare const style: {
|
|
41
|
+
bold: (s: string) => string;
|
|
42
|
+
dim: (s: string) => string;
|
|
43
|
+
italic: (s: string) => string;
|
|
44
|
+
underline: (s: string) => string;
|
|
45
|
+
red: (s: string) => string;
|
|
46
|
+
green: (s: string) => string;
|
|
47
|
+
yellow: (s: string) => string;
|
|
48
|
+
blue: (s: string) => string;
|
|
49
|
+
magenta: (s: string) => string;
|
|
50
|
+
cyan: (s: string) => string;
|
|
51
|
+
gray: (s: string) => string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The Ory brand palette (from the 09/25 brand guidelines) as terminal stylers.
|
|
55
|
+
* `indigo` (#4F46E5) is the primary accent used for the banner frame, menu
|
|
56
|
+
* keys, prompts, and copyable commands; the others tint status lines. Values
|
|
57
|
+
* are the guideline hex codes with hand-picked xterm-256 / ANSI-16 fallbacks.
|
|
58
|
+
*/
|
|
59
|
+
export declare const brand: {
|
|
60
|
+
/** Ory Indigo #4F46E5 — primary accent. */
|
|
61
|
+
indigo: (s: string) => string;
|
|
62
|
+
/** Green #22C55E — success. */
|
|
63
|
+
green: (s: string) => string;
|
|
64
|
+
/** Yellow #FACC15 — warning. */
|
|
65
|
+
yellow: (s: string) => string;
|
|
66
|
+
/** Rose #F43F5E — error. */
|
|
67
|
+
rose: (s: string) => string;
|
|
68
|
+
};
|
|
69
|
+
/** Status/decoration glyphs, with ASCII fallbacks for legacy terminals. */
|
|
70
|
+
export declare const glyph: {
|
|
71
|
+
success: string;
|
|
72
|
+
warning: string;
|
|
73
|
+
error: string;
|
|
74
|
+
info: string;
|
|
75
|
+
pending: string;
|
|
76
|
+
bullet: string;
|
|
77
|
+
arrow: string;
|
|
78
|
+
pointer: string;
|
|
79
|
+
gutter: string;
|
|
80
|
+
};
|
|
81
|
+
/** Print a blank line. */
|
|
82
|
+
export declare function blank(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Ring the terminal bell (BEL) to pull the user's attention back to the
|
|
85
|
+
* terminal — e.g. right after a browser step, when their focus is still on the
|
|
86
|
+
* browser and the next action is in the terminal. Most terminals turn this into
|
|
87
|
+
* a dock bounce, tab badge, or attention flag. Written to stderr and only when
|
|
88
|
+
* stderr is an interactive TTY, so it's a no-op when piped or under test.
|
|
89
|
+
*/
|
|
90
|
+
export declare function bell(): void;
|
|
91
|
+
/**
|
|
92
|
+
* Print a framed intro banner with a bold title and optional dim subtitle.
|
|
93
|
+
* The frame width tracks the longest line's *plain* length so color codes
|
|
94
|
+
* never throw off the alignment.
|
|
95
|
+
*/
|
|
96
|
+
export declare function banner(title: string, subtitle?: string): void;
|
|
97
|
+
/** A bold section heading, preceded by a blank line to separate steps. */
|
|
98
|
+
export declare function heading(text: string): void;
|
|
99
|
+
/** A success line in Ory green (task completed). */
|
|
100
|
+
export declare function success(text: string): void;
|
|
101
|
+
/** An informational line accented in Ory indigo. */
|
|
102
|
+
export declare function info(text: string): void;
|
|
103
|
+
/** A dim "work in progress" line. */
|
|
104
|
+
export declare function step(text: string): void;
|
|
105
|
+
/** A warning line in Ory yellow, routed to stderr via console.warn. */
|
|
106
|
+
export declare function warning(text: string): void;
|
|
107
|
+
/** A continuation/detail warning line indented under a preceding warning. */
|
|
108
|
+
export declare function warnDetail(text: string): void;
|
|
109
|
+
/** An indented bullet under the current section. */
|
|
110
|
+
export declare function bullet(text: string): void;
|
|
111
|
+
/** An indented, dimmed hint line. */
|
|
112
|
+
export declare function hint(text: string): void;
|
|
113
|
+
/** An indented, indigo-highlighted shell command the user can copy-paste. */
|
|
114
|
+
export declare function command(text: string): void;
|
|
115
|
+
/** Inline indigo style for a command referenced mid-sentence. */
|
|
116
|
+
export declare function inlineCommand(text: string): string;
|
|
117
|
+
/**
|
|
118
|
+
* Print an aligned key/value summary. Labels are dimmed and right-padded to a
|
|
119
|
+
* common width (measured on plain text); values are emphasized. Skips pairs
|
|
120
|
+
* whose value is null/undefined so callers can pass optional rows inline.
|
|
121
|
+
*/
|
|
122
|
+
export declare function summary(pairs: Array<[string, string | undefined | null]>): void;
|
|
123
|
+
export interface MenuItem {
|
|
124
|
+
/** The value the user types to pick this item (e.g. "1"). */
|
|
125
|
+
key: string;
|
|
126
|
+
/** Short bold label. */
|
|
127
|
+
label: string;
|
|
128
|
+
/** Dim one-line description shown after the label. */
|
|
129
|
+
description?: string;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Print a numbered menu. The typed key is highlighted, the label bold, and the
|
|
133
|
+
* description dimmed — the choice itself is still made by the caller's prompt,
|
|
134
|
+
* so this only renders the options.
|
|
135
|
+
*/
|
|
136
|
+
export declare function menu(items: MenuItem[]): void;
|
|
137
|
+
/**
|
|
138
|
+
* Build a styled prompt string for readline. The question is bold, preceded by
|
|
139
|
+
* a pointer glyph, and an optional default/hint is dimmed. Callers keep full
|
|
140
|
+
* control of the answer parsing — this only affects presentation.
|
|
141
|
+
*/
|
|
142
|
+
export declare function promptLine(question: string, opts?: {
|
|
143
|
+
hint?: string;
|
|
144
|
+
}): string;
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tiny, dependency-free terminal-UI kit for the interactive install flow.
|
|
4
|
+
*
|
|
5
|
+
* The install wizard is the one place a plugin talks to a human at a real
|
|
6
|
+
* terminal, so it's worth making it look the part: colored status glyphs,
|
|
7
|
+
* a framed intro banner, aligned key/value summaries, and highlighted menus.
|
|
8
|
+
* Everything here degrades gracefully — colors and Unicode glyphs are emitted
|
|
9
|
+
* only when the output is an interactive TTY that opts in, and collapse to
|
|
10
|
+
* plain ASCII otherwise. That keeps the output copy-paste-clean in logs and,
|
|
11
|
+
* crucially, byte-for-byte stable under test (where stdout isn't a TTY), so
|
|
12
|
+
* the wizard's plain-text content can still be asserted on.
|
|
13
|
+
*
|
|
14
|
+
* No external dependency: adding a prompt/print library to `@ory/argus` would
|
|
15
|
+
* push that weight onto every harness plugin that depends on core. A handful
|
|
16
|
+
* of ANSI escapes is all this needs.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.glyph = exports.brand = exports.style = void 0;
|
|
20
|
+
exports.colorEnabled = colorEnabled;
|
|
21
|
+
exports.colorLevel = colorLevel;
|
|
22
|
+
exports.blank = blank;
|
|
23
|
+
exports.bell = bell;
|
|
24
|
+
exports.banner = banner;
|
|
25
|
+
exports.heading = heading;
|
|
26
|
+
exports.success = success;
|
|
27
|
+
exports.info = info;
|
|
28
|
+
exports.step = step;
|
|
29
|
+
exports.warning = warning;
|
|
30
|
+
exports.warnDetail = warnDetail;
|
|
31
|
+
exports.bullet = bullet;
|
|
32
|
+
exports.hint = hint;
|
|
33
|
+
exports.command = command;
|
|
34
|
+
exports.inlineCommand = inlineCommand;
|
|
35
|
+
exports.summary = summary;
|
|
36
|
+
exports.menu = menu;
|
|
37
|
+
exports.promptLine = promptLine;
|
|
38
|
+
/**
|
|
39
|
+
* Whether ANSI color/style codes should be emitted. Honors the `NO_COLOR`
|
|
40
|
+
* convention and `FORCE_COLOR`, treats `TERM=dumb` as uncolored, and otherwise
|
|
41
|
+
* follows whether stdout is an interactive TTY. Evaluated per call so tests and
|
|
42
|
+
* piped output (no TTY) stay plain without any global setup.
|
|
43
|
+
*/
|
|
44
|
+
function colorEnabled() {
|
|
45
|
+
if (process.env.FORCE_COLOR === "0")
|
|
46
|
+
return false;
|
|
47
|
+
if (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== "0")
|
|
48
|
+
return true;
|
|
49
|
+
if (process.env.NO_COLOR !== undefined && process.env.NO_COLOR !== "")
|
|
50
|
+
return false;
|
|
51
|
+
if (process.env.TERM === "dumb")
|
|
52
|
+
return false;
|
|
53
|
+
return Boolean(process.stdout.isTTY);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Color depth the terminal can render, used to pick the best representation of
|
|
57
|
+
* the brand palette (see {@link brand}):
|
|
58
|
+
*
|
|
59
|
+
* 3 — 24-bit truecolor (`COLORTERM=truecolor|24bit`, or `FORCE_COLOR=3`): the
|
|
60
|
+
* exact Ory hex values are emitted.
|
|
61
|
+
* 2 — 256-color (`TERM` contains `256`, or `FORCE_COLOR=2`): the nearest
|
|
62
|
+
* xterm-256 index.
|
|
63
|
+
* 1 — basic 16-color: the nearest ANSI SGR code.
|
|
64
|
+
* 0 — color disabled (see {@link colorEnabled}).
|
|
65
|
+
*
|
|
66
|
+
* Apple Terminal, for instance, advertises `xterm-256color` but not truecolor,
|
|
67
|
+
* so it lands on level 2 and still gets a recognizably-indigo accent.
|
|
68
|
+
*/
|
|
69
|
+
function colorLevel() {
|
|
70
|
+
if (!colorEnabled())
|
|
71
|
+
return 0;
|
|
72
|
+
// An explicit FORCE_COLOR tier wins over ambient terminal hints.
|
|
73
|
+
const forced = process.env.FORCE_COLOR;
|
|
74
|
+
if (forced === "3")
|
|
75
|
+
return 3;
|
|
76
|
+
if (forced === "2")
|
|
77
|
+
return 2;
|
|
78
|
+
if (forced === "1")
|
|
79
|
+
return 1;
|
|
80
|
+
const colorterm = process.env.COLORTERM ?? "";
|
|
81
|
+
if (colorterm === "truecolor" || colorterm === "24bit")
|
|
82
|
+
return 3;
|
|
83
|
+
if (/-?256(color)?/.test(process.env.TERM ?? ""))
|
|
84
|
+
return 2;
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Whether to use Unicode box-drawing/glyph characters. Legacy Windows consoles
|
|
89
|
+
* (cmd.exe) render these poorly, so fall back to ASCII there unless we're in a
|
|
90
|
+
* modern terminal (Windows Terminal, VS Code) that handles them fine.
|
|
91
|
+
*/
|
|
92
|
+
function unicodeEnabled() {
|
|
93
|
+
if (process.platform !== "win32")
|
|
94
|
+
return true;
|
|
95
|
+
return Boolean(process.env.WT_SESSION) || process.env.TERM_PROGRAM === "vscode";
|
|
96
|
+
}
|
|
97
|
+
function wrap(open, close) {
|
|
98
|
+
return (s) => (colorEnabled() ? `\x1b[${open}m${s}\x1b[${close}m` : s);
|
|
99
|
+
}
|
|
100
|
+
/** ANSI text stylers. Each is a no-op passthrough when color is disabled. */
|
|
101
|
+
exports.style = {
|
|
102
|
+
bold: wrap(1, 22),
|
|
103
|
+
dim: wrap(2, 22),
|
|
104
|
+
italic: wrap(3, 23),
|
|
105
|
+
underline: wrap(4, 24),
|
|
106
|
+
red: wrap(31, 39),
|
|
107
|
+
green: wrap(32, 39),
|
|
108
|
+
yellow: wrap(33, 39),
|
|
109
|
+
blue: wrap(34, 39),
|
|
110
|
+
magenta: wrap(35, 39),
|
|
111
|
+
cyan: wrap(36, 39),
|
|
112
|
+
gray: wrap(90, 39),
|
|
113
|
+
};
|
|
114
|
+
/** Wrap text in the best available representation of a {@link BrandColor}. */
|
|
115
|
+
function paint(color) {
|
|
116
|
+
return (s) => {
|
|
117
|
+
const level = colorLevel();
|
|
118
|
+
if (level === 0)
|
|
119
|
+
return s;
|
|
120
|
+
const open = level === 3
|
|
121
|
+
? `38;2;${color.rgb[0]};${color.rgb[1]};${color.rgb[2]}`
|
|
122
|
+
: level === 2
|
|
123
|
+
? `38;5;${color.ansi256}`
|
|
124
|
+
: `${color.ansi16}`;
|
|
125
|
+
return `\x1b[${open}m${s}\x1b[39m`;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The Ory brand palette (from the 09/25 brand guidelines) as terminal stylers.
|
|
130
|
+
* `indigo` (#4F46E5) is the primary accent used for the banner frame, menu
|
|
131
|
+
* keys, prompts, and copyable commands; the others tint status lines. Values
|
|
132
|
+
* are the guideline hex codes with hand-picked xterm-256 / ANSI-16 fallbacks.
|
|
133
|
+
*/
|
|
134
|
+
exports.brand = {
|
|
135
|
+
/** Ory Indigo #4F46E5 — primary accent. */
|
|
136
|
+
indigo: paint({ rgb: [79, 70, 229], ansi256: 63, ansi16: 94 }),
|
|
137
|
+
/** Green #22C55E — success. */
|
|
138
|
+
green: paint({ rgb: [34, 197, 94], ansi256: 78, ansi16: 32 }),
|
|
139
|
+
/** Yellow #FACC15 — warning. */
|
|
140
|
+
yellow: paint({ rgb: [250, 204, 21], ansi256: 220, ansi16: 33 }),
|
|
141
|
+
/** Rose #F43F5E — error. */
|
|
142
|
+
rose: paint({ rgb: [244, 63, 94], ansi256: 204, ansi16: 31 }),
|
|
143
|
+
};
|
|
144
|
+
/** Status/decoration glyphs, with ASCII fallbacks for legacy terminals. */
|
|
145
|
+
exports.glyph = {
|
|
146
|
+
success: unicodeEnabled() ? "✓" : "+",
|
|
147
|
+
warning: unicodeEnabled() ? "▲" : "!",
|
|
148
|
+
error: unicodeEnabled() ? "✗" : "x",
|
|
149
|
+
info: unicodeEnabled() ? "◇" : "*",
|
|
150
|
+
pending: unicodeEnabled() ? "○" : "o",
|
|
151
|
+
bullet: unicodeEnabled() ? "•" : "-",
|
|
152
|
+
arrow: unicodeEnabled() ? "→" : "->",
|
|
153
|
+
pointer: unicodeEnabled() ? "❯" : ">",
|
|
154
|
+
gutter: unicodeEnabled() ? "│" : "|",
|
|
155
|
+
};
|
|
156
|
+
const boxChars = unicodeEnabled()
|
|
157
|
+
? { tl: "╭", tr: "╮", bl: "╰", br: "╯", h: "─", v: "│" }
|
|
158
|
+
: { tl: "+", tr: "+", bl: "+", br: "+", h: "-", v: "|" };
|
|
159
|
+
/** Print a blank line. */
|
|
160
|
+
function blank() {
|
|
161
|
+
console.log("");
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Ring the terminal bell (BEL) to pull the user's attention back to the
|
|
165
|
+
* terminal — e.g. right after a browser step, when their focus is still on the
|
|
166
|
+
* browser and the next action is in the terminal. Most terminals turn this into
|
|
167
|
+
* a dock bounce, tab badge, or attention flag. Written to stderr and only when
|
|
168
|
+
* stderr is an interactive TTY, so it's a no-op when piped or under test.
|
|
169
|
+
*/
|
|
170
|
+
function bell() {
|
|
171
|
+
if (process.stderr.isTTY)
|
|
172
|
+
process.stderr.write("\x07");
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Print a framed intro banner with a bold title and optional dim subtitle.
|
|
176
|
+
* The frame width tracks the longest line's *plain* length so color codes
|
|
177
|
+
* never throw off the alignment.
|
|
178
|
+
*/
|
|
179
|
+
function banner(title, subtitle) {
|
|
180
|
+
const rows = [title, ...(subtitle ? [subtitle] : [])];
|
|
181
|
+
const width = Math.max(...rows.map((r) => r.length));
|
|
182
|
+
const rule = boxChars.h.repeat(width + 2);
|
|
183
|
+
console.log("");
|
|
184
|
+
console.log(exports.brand.indigo(boxChars.tl + rule + boxChars.tr));
|
|
185
|
+
rows.forEach((text, i) => {
|
|
186
|
+
const pad = " ".repeat(width - text.length);
|
|
187
|
+
const content = i === 0 ? exports.style.bold(exports.brand.indigo(text)) : exports.style.dim(text);
|
|
188
|
+
console.log(exports.brand.indigo(boxChars.v) + " " + content + pad + " " + exports.brand.indigo(boxChars.v));
|
|
189
|
+
});
|
|
190
|
+
console.log(exports.brand.indigo(boxChars.bl + rule + boxChars.br));
|
|
191
|
+
}
|
|
192
|
+
/** A bold section heading, preceded by a blank line to separate steps. */
|
|
193
|
+
function heading(text) {
|
|
194
|
+
console.log("");
|
|
195
|
+
console.log(exports.style.bold(text));
|
|
196
|
+
}
|
|
197
|
+
/** A success line in Ory green (task completed). */
|
|
198
|
+
function success(text) {
|
|
199
|
+
console.log(`${exports.brand.green(exports.glyph.success)} ${text}`);
|
|
200
|
+
}
|
|
201
|
+
/** An informational line accented in Ory indigo. */
|
|
202
|
+
function info(text) {
|
|
203
|
+
console.log(`${exports.brand.indigo(exports.glyph.info)} ${text}`);
|
|
204
|
+
}
|
|
205
|
+
/** A dim "work in progress" line. */
|
|
206
|
+
function step(text) {
|
|
207
|
+
console.log(`${exports.style.dim(exports.glyph.pending)} ${text}`);
|
|
208
|
+
}
|
|
209
|
+
/** A warning line in Ory yellow, routed to stderr via console.warn. */
|
|
210
|
+
function warning(text) {
|
|
211
|
+
console.warn(`${exports.brand.yellow(exports.glyph.warning)} ${text}`);
|
|
212
|
+
}
|
|
213
|
+
/** A continuation/detail warning line indented under a preceding warning. */
|
|
214
|
+
function warnDetail(text) {
|
|
215
|
+
console.warn(` ${exports.style.dim(text)}`);
|
|
216
|
+
}
|
|
217
|
+
/** An indented bullet under the current section. */
|
|
218
|
+
function bullet(text) {
|
|
219
|
+
console.log(` ${exports.style.dim(exports.glyph.bullet)} ${text}`);
|
|
220
|
+
}
|
|
221
|
+
/** An indented, dimmed hint line. */
|
|
222
|
+
function hint(text) {
|
|
223
|
+
console.log(` ${exports.style.dim(text)}`);
|
|
224
|
+
}
|
|
225
|
+
/** An indented, indigo-highlighted shell command the user can copy-paste. */
|
|
226
|
+
function command(text) {
|
|
227
|
+
console.log(` ${exports.brand.indigo(text)}`);
|
|
228
|
+
}
|
|
229
|
+
/** Inline indigo style for a command referenced mid-sentence. */
|
|
230
|
+
function inlineCommand(text) {
|
|
231
|
+
return exports.brand.indigo(text);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Print an aligned key/value summary. Labels are dimmed and right-padded to a
|
|
235
|
+
* common width (measured on plain text); values are emphasized. Skips pairs
|
|
236
|
+
* whose value is null/undefined so callers can pass optional rows inline.
|
|
237
|
+
*/
|
|
238
|
+
function summary(pairs) {
|
|
239
|
+
const rows = pairs.filter((p) => p[1] != null);
|
|
240
|
+
if (rows.length === 0)
|
|
241
|
+
return;
|
|
242
|
+
const width = Math.max(...rows.map(([label]) => label.length));
|
|
243
|
+
for (const [label, value] of rows) {
|
|
244
|
+
const pad = " ".repeat(width - label.length);
|
|
245
|
+
console.log(` ${exports.style.dim(label)}${pad} ${value}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Print a numbered menu. The typed key is highlighted, the label bold, and the
|
|
250
|
+
* description dimmed — the choice itself is still made by the caller's prompt,
|
|
251
|
+
* so this only renders the options.
|
|
252
|
+
*/
|
|
253
|
+
function menu(items) {
|
|
254
|
+
for (const item of items) {
|
|
255
|
+
const key = exports.brand.indigo(`${item.key})`);
|
|
256
|
+
const label = exports.style.bold(item.label);
|
|
257
|
+
const desc = item.description ? exports.style.dim(` ${item.description}`) : "";
|
|
258
|
+
console.log(` ${key} ${label}${desc}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Build a styled prompt string for readline. The question is bold, preceded by
|
|
263
|
+
* a pointer glyph, and an optional default/hint is dimmed. Callers keep full
|
|
264
|
+
* control of the answer parsing — this only affects presentation.
|
|
265
|
+
*/
|
|
266
|
+
function promptLine(question, opts = {}) {
|
|
267
|
+
const pointer = exports.brand.indigo(exports.glyph.pointer);
|
|
268
|
+
const hintPart = opts.hint ? exports.style.dim(` ${opts.hint}`) : "";
|
|
269
|
+
return `${pointer} ${exports.style.bold(question)}${hintPart} `;
|
|
270
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential teardown shared by every plugin's `uninstall` command.
|
|
3
|
+
*
|
|
4
|
+
* Uninstalling a plugin removes all *authentication* state from the shared
|
|
5
|
+
* config file while preserving *configuration* (project URL, OAuth2 client
|
|
6
|
+
* id, audit mode, permission mode, user-login flag). The intent is that a
|
|
7
|
+
* later re-install re-authenticates from scratch — the user logs in again
|
|
8
|
+
* and the agent registers a fresh dynamic client — rather than silently
|
|
9
|
+
* inheriting stale credentials.
|
|
10
|
+
*
|
|
11
|
+
* What is removed:
|
|
12
|
+
* - the user's persisted OAuth2 tokens (`user`)
|
|
13
|
+
* - the agent's dynamic-registration credentials and every sub-agent
|
|
14
|
+
* registration (`agent`)
|
|
15
|
+
* - the operator-supplied static agent API key (`apiKey`)
|
|
16
|
+
* - any dangling PKCE-flight lock
|
|
17
|
+
*
|
|
18
|
+
* Before clearing local state, each dynamically-registered client (the
|
|
19
|
+
* agent and each sub-agent) is revoked server-side via RFC 7592 so the
|
|
20
|
+
* Ory project isn't left with orphaned OAuth2 clients accumulating across
|
|
21
|
+
* install/uninstall cycles. Revocation is best-effort: failures warn and
|
|
22
|
+
* local state is cleared regardless.
|
|
23
|
+
*/
|
|
24
|
+
export interface ClearCredentialsOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Best-effort RFC 7592 `DELETE` of the agent + sub-agent dynamic clients
|
|
27
|
+
* before clearing local state. Default: `true`. Set `false` to clear
|
|
28
|
+
* local state only (offline / no network).
|
|
29
|
+
*/
|
|
30
|
+
revoke?: boolean;
|
|
31
|
+
/** Sink for progress and warning lines. Default: `console.log`. */
|
|
32
|
+
log?: (message: string) => void;
|
|
33
|
+
}
|
|
34
|
+
export interface ClearCredentialsResult {
|
|
35
|
+
/** Whether any credential was present before clearing. */
|
|
36
|
+
hadCredentials: boolean;
|
|
37
|
+
/** Number of dynamic clients revoked server-side (includes already-gone). */
|
|
38
|
+
revoked: number;
|
|
39
|
+
/** Number of dynamic clients whose server-side revoke failed. */
|
|
40
|
+
revokeFailures: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Remove all authentication state from the shared config file, revoking
|
|
44
|
+
* dynamically-registered clients server-side first. Preserves every
|
|
45
|
+
* non-credential configuration value. Never throws.
|
|
46
|
+
*/
|
|
47
|
+
export declare function clearCredentialsForUninstall(options?: ClearCredentialsOptions): Promise<ClearCredentialsResult>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Credential teardown shared by every plugin's `uninstall` command.
|
|
4
|
+
*
|
|
5
|
+
* Uninstalling a plugin removes all *authentication* state from the shared
|
|
6
|
+
* config file while preserving *configuration* (project URL, OAuth2 client
|
|
7
|
+
* id, audit mode, permission mode, user-login flag). The intent is that a
|
|
8
|
+
* later re-install re-authenticates from scratch — the user logs in again
|
|
9
|
+
* and the agent registers a fresh dynamic client — rather than silently
|
|
10
|
+
* inheriting stale credentials.
|
|
11
|
+
*
|
|
12
|
+
* What is removed:
|
|
13
|
+
* - the user's persisted OAuth2 tokens (`user`)
|
|
14
|
+
* - the agent's dynamic-registration credentials and every sub-agent
|
|
15
|
+
* registration (`agent`)
|
|
16
|
+
* - the operator-supplied static agent API key (`apiKey`)
|
|
17
|
+
* - any dangling PKCE-flight lock
|
|
18
|
+
*
|
|
19
|
+
* Before clearing local state, each dynamically-registered client (the
|
|
20
|
+
* agent and each sub-agent) is revoked server-side via RFC 7592 so the
|
|
21
|
+
* Ory project isn't left with orphaned OAuth2 clients accumulating across
|
|
22
|
+
* install/uninstall cycles. Revocation is best-effort: failures warn and
|
|
23
|
+
* local state is cleared regardless.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.clearCredentialsForUninstall = clearCredentialsForUninstall;
|
|
27
|
+
const config_js_1 = require("./config.js");
|
|
28
|
+
const agent_auth_js_1 = require("./agent-auth.js");
|
|
29
|
+
const auth_store_js_1 = require("./auth-store.js");
|
|
30
|
+
/**
|
|
31
|
+
* Remove all authentication state from the shared config file, revoking
|
|
32
|
+
* dynamically-registered clients server-side first. Preserves every
|
|
33
|
+
* non-credential configuration value. Never throws.
|
|
34
|
+
*/
|
|
35
|
+
async function clearCredentialsForUninstall(options = {}) {
|
|
36
|
+
const log = options.log ?? ((msg) => console.log(msg));
|
|
37
|
+
const revoke = options.revoke !== false;
|
|
38
|
+
const config = (0, config_js_1.loadConfig)();
|
|
39
|
+
const dynamic = config.agent?.dynamic;
|
|
40
|
+
const subAgents = config.agent?.subAgents ?? {};
|
|
41
|
+
const subAgentEntries = Object.entries(subAgents);
|
|
42
|
+
const hadCredentials = Boolean(config.user || dynamic || subAgentEntries.length > 0 || config.apiKey);
|
|
43
|
+
if (!hadCredentials) {
|
|
44
|
+
log("No stored Ory credentials to remove.");
|
|
45
|
+
return { hadCredentials: false, revoked: 0, revokeFailures: 0 };
|
|
46
|
+
}
|
|
47
|
+
let revoked = 0;
|
|
48
|
+
let revokeFailures = 0;
|
|
49
|
+
if (revoke) {
|
|
50
|
+
const targets = [];
|
|
51
|
+
if (dynamic) {
|
|
52
|
+
targets.push({
|
|
53
|
+
label: "agent client",
|
|
54
|
+
clientId: dynamic.clientId,
|
|
55
|
+
result: () => (0, agent_auth_js_1.revokeAgentDynamicClient)(dynamic),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
for (const [type, creds] of subAgentEntries) {
|
|
59
|
+
targets.push({
|
|
60
|
+
label: `sub-agent client (${type})`,
|
|
61
|
+
clientId: creds.clientId,
|
|
62
|
+
result: () => (0, agent_auth_js_1.revokeAgentDynamicClient)(creds),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
for (const target of targets) {
|
|
66
|
+
const outcome = await target.result();
|
|
67
|
+
if (outcome.status === "deleted") {
|
|
68
|
+
revoked++;
|
|
69
|
+
log(` Revoked ${target.label} ${target.clientId}.`);
|
|
70
|
+
}
|
|
71
|
+
else if (outcome.status === "skipped") {
|
|
72
|
+
// No management credentials to DELETE with — nothing we can do
|
|
73
|
+
// server-side. Not counted as a failure.
|
|
74
|
+
log(` Could not revoke ${target.label} ${target.clientId} server-side (${outcome.message}). Clearing local state anyway.`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
revokeFailures++;
|
|
78
|
+
const detail = typeof outcome.httpStatus === "number"
|
|
79
|
+
? `HTTP ${outcome.httpStatus}`
|
|
80
|
+
: (outcome.message ?? "unknown error");
|
|
81
|
+
log(` Warning: server-side revoke of ${target.label} ${target.clientId} failed (${detail}). Clearing local state anyway.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Drop every credential block; keep all configuration values.
|
|
86
|
+
(0, config_js_1.saveConfig)({ user: null, agent: null, apiKey: null });
|
|
87
|
+
(0, auth_store_js_1.clearPkceFlightLock)();
|
|
88
|
+
log("Removed stored Ory credentials (user login, agent identity, API key).");
|
|
89
|
+
return { hadCredentials: true, revoked, revokeFailures };
|
|
90
|
+
}
|
package/dist/user-login.d.ts
CHANGED
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
* Every terminal path emits exactly one `user.auth` trace span so that
|
|
18
18
|
* the audit trail is complete regardless of outcome.
|
|
19
19
|
*
|
|
20
|
-
* The whole flow is a no-op (mode `disabled`) unless
|
|
21
|
-
* `ORY_USER_LOGIN` env var
|
|
22
|
-
* `on`
|
|
20
|
+
* The whole flow is a no-op (mode `disabled`) unless user login is
|
|
21
|
+
* enabled — either via the `ORY_USER_LOGIN` env var (values `1`, `true`,
|
|
22
|
+
* `yes`, `on`) or persisted in the shared config file (`userLogin: true`,
|
|
23
|
+
* set with `configure --user-login`). The env var overrides the config
|
|
24
|
+
* file when set.
|
|
23
25
|
*
|
|
24
26
|
* This authenticates the *user* (the human at the keyboard). The
|
|
25
27
|
* separate agent identity (the AI process making the calls) is resolved
|
package/dist/user-login.js
CHANGED
|
@@ -18,9 +18,11 @@
|
|
|
18
18
|
* Every terminal path emits exactly one `user.auth` trace span so that
|
|
19
19
|
* the audit trail is complete regardless of outcome.
|
|
20
20
|
*
|
|
21
|
-
* The whole flow is a no-op (mode `disabled`) unless
|
|
22
|
-
* `ORY_USER_LOGIN` env var
|
|
23
|
-
* `on`
|
|
21
|
+
* The whole flow is a no-op (mode `disabled`) unless user login is
|
|
22
|
+
* enabled — either via the `ORY_USER_LOGIN` env var (values `1`, `true`,
|
|
23
|
+
* `yes`, `on`) or persisted in the shared config file (`userLogin: true`,
|
|
24
|
+
* set with `configure --user-login`). The env var overrides the config
|
|
25
|
+
* file when set.
|
|
24
26
|
*
|
|
25
27
|
* This authenticates the *user* (the human at the keyboard). The
|
|
26
28
|
* separate agent identity (the AI process making the calls) is resolved
|
|
@@ -32,12 +34,14 @@ exports.ensureAuthenticated = void 0;
|
|
|
32
34
|
exports.ensureUserAuthenticated = ensureUserAuthenticated;
|
|
33
35
|
const config_js_1 = require("./config.js");
|
|
34
36
|
const cli_js_1 = require("./cli.js");
|
|
37
|
+
const cli_invocation_js_1 = require("./cli-invocation.js");
|
|
35
38
|
const auth_store_js_1 = require("./auth-store.js");
|
|
36
39
|
const auth_js_1 = require("./auth.js");
|
|
37
|
-
const ENABLED_VALUES = new Set(["1", "true", "yes", "on"]);
|
|
38
40
|
function isUserLoginEnabled() {
|
|
39
|
-
|
|
40
|
-
|
|
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;
|
|
41
45
|
}
|
|
42
46
|
function clientId() {
|
|
43
47
|
// resolveConfig already merges env (ORY_OAUTH2_CLIENT_ID) with the
|
|
@@ -71,7 +75,7 @@ async function ensureUserAuthenticated(client, options) {
|
|
|
71
75
|
const decision = {
|
|
72
76
|
proceed: true,
|
|
73
77
|
mode: "disabled",
|
|
74
|
-
reason: "
|
|
78
|
+
reason: "User login is not enabled (ORY_USER_LOGIN env / userLogin config)",
|
|
75
79
|
};
|
|
76
80
|
recordAuthSpan(client, decision);
|
|
77
81
|
return decision;
|
|
@@ -218,8 +222,8 @@ async function runUserLogin(client, options) {
|
|
|
218
222
|
return {
|
|
219
223
|
proceed: !options.allowBlock,
|
|
220
224
|
mode: "skipped",
|
|
221
|
-
reason: "ORY_OAUTH2_CLIENT_ID is not set; cannot start browser login (run `
|
|
222
|
-
options.binName +
|
|
225
|
+
reason: "ORY_OAUTH2_CLIENT_ID is not set; cannot start browser login (run `" +
|
|
226
|
+
(0, cli_invocation_js_1.oryNpx)(options.binName) +
|
|
223
227
|
" configure --oauth2-client-id <id>` after registering a public OAuth2 client " +
|
|
224
228
|
"in your Ory project with all four loopback redirect URIs: " +
|
|
225
229
|
"http://127.0.0.1:47823..47826/callback)",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/argus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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",
|
|
@@ -72,7 +72,8 @@
|
|
|
72
72
|
"@opentelemetry/resources": "^2.8.0",
|
|
73
73
|
"@opentelemetry/sdk-trace-base": "^2.8.0",
|
|
74
74
|
"@ory/client": "^1.22.37",
|
|
75
|
-
"open": "^11.0.0"
|
|
75
|
+
"open": "^11.0.0",
|
|
76
|
+
"reo-census": "^1.2.8"
|
|
76
77
|
},
|
|
77
78
|
"engines": {
|
|
78
79
|
"node": ">=22"
|