@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/config.js
CHANGED
|
@@ -33,20 +33,49 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LOCAL_AGENT_SECURITY_URL = exports.DEFAULT_AGENT_SECURITY_URL = exports.DEFAULT_OAUTH2_CLIENT_ID = exports.SESSIONLESS_KEY = exports.UNKNOWN_HARNESS_KEY = exports.DELEGATION_NODE_HISTORY = void 0;
|
|
36
37
|
exports.getDataDir = getDataDir;
|
|
37
38
|
exports.getHarnessDataDir = getHarnessDataDir;
|
|
38
39
|
exports.getConfigPath = getConfigPath;
|
|
39
40
|
exports.loadConfig = loadConfig;
|
|
40
|
-
exports.
|
|
41
|
+
exports.listInstalledHarnesses = listInstalledHarnesses;
|
|
42
|
+
exports.registerInstalledHarness = registerInstalledHarness;
|
|
43
|
+
exports.unregisterInstalledHarness = unregisterInstalledHarness;
|
|
41
44
|
exports.saveConfig = saveConfig;
|
|
42
45
|
exports.mutateConfig = mutateConfig;
|
|
43
46
|
exports.looksLikeProjectId = looksLikeProjectId;
|
|
47
|
+
exports.isSecurityConnected = isSecurityConnected;
|
|
48
|
+
exports.describeSecurityGap = describeSecurityGap;
|
|
44
49
|
exports.resolveConfig = resolveConfig;
|
|
45
50
|
exports.configPromptMessage = configPromptMessage;
|
|
46
51
|
const fs = __importStar(require("node:fs"));
|
|
47
52
|
const os = __importStar(require("node:os"));
|
|
48
53
|
const path = __importStar(require("node:path"));
|
|
49
54
|
const cli_invocation_js_1 = require("./cli-invocation.js");
|
|
55
|
+
/**
|
|
56
|
+
* How many sessions' delegation node ids to keep per credential. Only the
|
|
57
|
+
* current session's is ever read; the rest are kept as a small audit tail and
|
|
58
|
+
* bounded so a long-lived install's config doesn't grow without limit.
|
|
59
|
+
*/
|
|
60
|
+
exports.DELEGATION_NODE_HISTORY = 10;
|
|
61
|
+
/**
|
|
62
|
+
* Key used when a credential record carries no harness of its own — only
|
|
63
|
+
* reachable by migrating a config written before credentials were keyed by
|
|
64
|
+
* harness, where the `harness` field had never been persisted. Such a record
|
|
65
|
+
* is never re-used (no harness matches it), but it stays addressable so
|
|
66
|
+
* uninstall can still revoke the server-side client rather than orphaning it.
|
|
67
|
+
*/
|
|
68
|
+
exports.UNKNOWN_HARNESS_KEY = "unknown";
|
|
69
|
+
/**
|
|
70
|
+
* Session key for a caller with no session concept — an Agent SDK integration
|
|
71
|
+
* embedded in a long-running service, or a one-off script. Such a caller gets
|
|
72
|
+
* one stable identity under this key rather than a new client per call, which
|
|
73
|
+
* is the only sensible reading of "per session" when there are no sessions.
|
|
74
|
+
* Credentials migrated from a config written before session keying also land
|
|
75
|
+
* here: the session they belonged to is not recoverable, but the record stays
|
|
76
|
+
* addressable so uninstall can revoke it.
|
|
77
|
+
*/
|
|
78
|
+
exports.SESSIONLESS_KEY = "sessionless";
|
|
50
79
|
/**
|
|
51
80
|
* Single OS-agnostic data directory for *all* Ory agent plugin state:
|
|
52
81
|
* the shared config file, persisted DCR credentials, and any harness
|
|
@@ -117,17 +146,27 @@ function loadConfig() {
|
|
|
117
146
|
const parsed = JSON.parse(raw);
|
|
118
147
|
return {
|
|
119
148
|
projectUrl: typeof parsed.projectUrl === "string" ? parsed.projectUrl : undefined,
|
|
149
|
+
agentSecurityUrl: typeof parsed.agentSecurityUrl === "string" ? parsed.agentSecurityUrl : undefined,
|
|
120
150
|
projectId: typeof parsed.projectId === "string" && parsed.projectId.trim()
|
|
121
151
|
? parsed.projectId.trim()
|
|
122
152
|
: undefined,
|
|
123
|
-
|
|
153
|
+
projectName: typeof parsed.projectName === "string" && parsed.projectName.trim()
|
|
154
|
+
? parsed.projectName.trim()
|
|
155
|
+
: undefined,
|
|
156
|
+
workspaceName: typeof parsed.workspaceName === "string" && parsed.workspaceName.trim()
|
|
157
|
+
? parsed.workspaceName.trim()
|
|
158
|
+
: undefined,
|
|
124
159
|
oauth2ClientId: typeof parsed.oauth2ClientId === "string" ? parsed.oauth2ClientId : undefined,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
160
|
+
// NOTE: a `dxOnly` key written by an older release is deliberately not
|
|
161
|
+
// parsed. Whether Agent Security runs is derived from the presence of
|
|
162
|
+
// `projectUrl` + `oauth2ClientId` (see resolveConfig), never from a
|
|
163
|
+
// stored flag — so the stale key is simply dropped on the next write.
|
|
164
|
+
permissionModeCache: parsePermissionModeCache(parsed),
|
|
128
165
|
userSubjectNamespace: typeof parsed.userSubjectNamespace === "string" && parsed.userSubjectNamespace.trim()
|
|
129
166
|
? parsed.userSubjectNamespace.trim()
|
|
130
167
|
: undefined,
|
|
168
|
+
installedHarnesses: parseInstalledHarnesses(parsed.installedHarnesses),
|
|
169
|
+
delegationAnchors: parseDelegationAnchors(parsed.delegationAnchors),
|
|
131
170
|
user: parseUserCredentials(parsed),
|
|
132
171
|
agent: parseAgentCredentials(parsed),
|
|
133
172
|
};
|
|
@@ -136,47 +175,195 @@ function loadConfig() {
|
|
|
136
175
|
return {};
|
|
137
176
|
}
|
|
138
177
|
}
|
|
178
|
+
function parseDelegationAnchors(value) {
|
|
179
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
180
|
+
return undefined;
|
|
181
|
+
const result = {};
|
|
182
|
+
for (const [harness, rawSessions] of Object.entries(value)) {
|
|
183
|
+
if (!rawSessions || typeof rawSessions !== "object" || Array.isArray(rawSessions))
|
|
184
|
+
continue;
|
|
185
|
+
const sessions = {};
|
|
186
|
+
for (const [session, nodeId] of Object.entries(rawSessions)) {
|
|
187
|
+
if (typeof nodeId === "string" && nodeId.length > 0)
|
|
188
|
+
sessions[session] = nodeId;
|
|
189
|
+
}
|
|
190
|
+
if (Object.keys(sessions).length > 0)
|
|
191
|
+
result[harness] = sessions;
|
|
192
|
+
}
|
|
193
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
194
|
+
}
|
|
139
195
|
function parsePermissionMode(value) {
|
|
140
196
|
return value === "observe" || value === "enforce" ? value : undefined;
|
|
141
197
|
}
|
|
142
|
-
/** Env values that turn the interactive user login on. */
|
|
143
|
-
const USER_LOGIN_ENABLED_VALUES = new Set(["1", "true", "yes", "on"]);
|
|
144
198
|
/**
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* CLI, and configure command resolve it identically.
|
|
199
|
+
* Parse the persisted {@link PermissionModeCache}. Also migrates a legacy
|
|
200
|
+
* top-level `permissionMode` string (the old user-set field) into the cache so
|
|
201
|
+
* an upgrade preserves the last posture until the first server read overwrites
|
|
202
|
+
* it (`fetchedAt: 0` marks it as never-server-confirmed).
|
|
150
203
|
*/
|
|
151
|
-
function
|
|
152
|
-
const raw =
|
|
153
|
-
if (raw
|
|
154
|
-
|
|
155
|
-
|
|
204
|
+
function parsePermissionModeCache(parsed) {
|
|
205
|
+
const raw = parsed.permissionModeCache;
|
|
206
|
+
if (raw && typeof raw === "object") {
|
|
207
|
+
const obj = raw;
|
|
208
|
+
const mode = parsePermissionMode(obj.mode);
|
|
209
|
+
if (mode) {
|
|
210
|
+
const fetchedAt = typeof obj.fetchedAt === "number" ? obj.fetchedAt : 0;
|
|
211
|
+
const scopes = {};
|
|
212
|
+
if (obj.scopes && typeof obj.scopes === "object") {
|
|
213
|
+
for (const [key, value] of Object.entries(obj.scopes)) {
|
|
214
|
+
if (!value || typeof value !== "object")
|
|
215
|
+
continue;
|
|
216
|
+
const entry = value;
|
|
217
|
+
const entryMode = parsePermissionMode(entry.mode);
|
|
218
|
+
if (!entryMode)
|
|
219
|
+
continue;
|
|
220
|
+
scopes[key] = {
|
|
221
|
+
mode: entryMode,
|
|
222
|
+
fetchedAt: typeof entry.fetchedAt === "number" ? entry.fetchedAt : 0,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
mode,
|
|
228
|
+
fetchedAt,
|
|
229
|
+
...(typeof obj.projectUrl === "string" ? { projectUrl: obj.projectUrl } : {}),
|
|
230
|
+
...(Object.keys(scopes).length ? { scopes } : {}),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const legacy = parsePermissionMode(parsed.permissionMode);
|
|
235
|
+
if (legacy)
|
|
236
|
+
return { mode: legacy, fetchedAt: 0 };
|
|
237
|
+
return undefined;
|
|
156
238
|
}
|
|
157
239
|
function parseAgentCredentials(parsed) {
|
|
158
240
|
const raw = parsed.agent;
|
|
159
241
|
if (!raw || typeof raw !== "object")
|
|
160
242
|
return undefined;
|
|
161
243
|
const block = raw;
|
|
162
|
-
const
|
|
244
|
+
const retired = parseRetiredCredentials(block.retired);
|
|
245
|
+
const dynamic = parseAgentDynamicByHarness(block.dynamic);
|
|
163
246
|
const subAgents = parseSubAgentCredentials(block.subAgents);
|
|
164
|
-
if (!dynamic && !subAgents)
|
|
247
|
+
if (!dynamic && !subAgents && retired.length === 0)
|
|
165
248
|
return undefined;
|
|
166
249
|
return {
|
|
167
250
|
...(dynamic ? { dynamic } : {}),
|
|
168
251
|
...(subAgents ? { subAgents } : {}),
|
|
252
|
+
...(retired.length > 0 ? { retired } : {}),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function parseRetiredCredentials(value) {
|
|
256
|
+
if (!Array.isArray(value))
|
|
257
|
+
return [];
|
|
258
|
+
const out = [];
|
|
259
|
+
for (const entry of value) {
|
|
260
|
+
const creds = parseAgentDynamicCredentials(entry);
|
|
261
|
+
if (creds)
|
|
262
|
+
out.push(creds);
|
|
263
|
+
}
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* True when `value` is a credential record itself rather than a map of them.
|
|
268
|
+
* A credential record always carries a string `clientId`; a keyed map never
|
|
269
|
+
* does. This is what lets the parser accept both the current harness-keyed
|
|
270
|
+
* shape and the pre-migration flat shape without a version field.
|
|
271
|
+
*/
|
|
272
|
+
function looksLikeCredentialRecord(value) {
|
|
273
|
+
if (!value || typeof value !== "object")
|
|
274
|
+
return false;
|
|
275
|
+
return typeof value.clientId === "string";
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Parse `agent.dynamic` into `{ [harness]: { [session]: creds } }`.
|
|
279
|
+
*
|
|
280
|
+
* Three shapes have existed, and each is recognizable without a version field
|
|
281
|
+
* because only a credential record carries a string `clientId`:
|
|
282
|
+
*
|
|
283
|
+
* 1. one bare record — pre-harness keying
|
|
284
|
+
* 2. `{ [harness]: creds }` — install-scoped release
|
|
285
|
+
* 3. `{ [harness]: { [session]: creds } }` — current
|
|
286
|
+
*
|
|
287
|
+
* Records without a known session migrate to {@link SESSIONLESS_KEY}.
|
|
288
|
+
*/
|
|
289
|
+
function parseAgentDynamicByHarness(value) {
|
|
290
|
+
if (!value || typeof value !== "object")
|
|
291
|
+
return undefined;
|
|
292
|
+
const result = {};
|
|
293
|
+
const put = (harness, session, creds) => {
|
|
294
|
+
(result[harness] ??= {})[session] = creds;
|
|
169
295
|
};
|
|
296
|
+
// Shape 1: a single bare record, re-keyed under the harness it names.
|
|
297
|
+
if (looksLikeCredentialRecord(value)) {
|
|
298
|
+
const legacy = parseAgentDynamicCredentials(value);
|
|
299
|
+
if (!legacy)
|
|
300
|
+
return undefined;
|
|
301
|
+
put(legacy.harness ?? exports.UNKNOWN_HARNESS_KEY, exports.SESSIONLESS_KEY, legacy);
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
for (const [harness, raw] of Object.entries(value)) {
|
|
305
|
+
// Shape 2: harness → one install-scoped record.
|
|
306
|
+
if (looksLikeCredentialRecord(raw)) {
|
|
307
|
+
const creds = parseAgentDynamicCredentials(raw);
|
|
308
|
+
if (creds)
|
|
309
|
+
put(harness, exports.SESSIONLESS_KEY, creds);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
// Shape 3: harness → session → record.
|
|
313
|
+
if (!raw || typeof raw !== "object")
|
|
314
|
+
continue;
|
|
315
|
+
for (const [session, entry] of Object.entries(raw)) {
|
|
316
|
+
const creds = parseAgentDynamicCredentials(entry);
|
|
317
|
+
if (creds)
|
|
318
|
+
put(harness, session, creds);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
170
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Parse `agent.subAgents` into
|
|
325
|
+
* `{ [harness]: { [session]: { [type]: creds } } }`.
|
|
326
|
+
*
|
|
327
|
+
* As with `agent.dynamic`, three shapes have existed:
|
|
328
|
+
*
|
|
329
|
+
* 1. `{ [type]: creds }` — pre-harness keying
|
|
330
|
+
* 2. `{ [harness]: { [type]: creds } }` — install-scoped release
|
|
331
|
+
* 3. `{ [harness]: { [session]: { [type]: creds } } }` — current
|
|
332
|
+
*/
|
|
171
333
|
function parseSubAgentCredentials(value) {
|
|
172
334
|
if (!value || typeof value !== "object")
|
|
173
335
|
return undefined;
|
|
174
|
-
const entries = value;
|
|
175
336
|
const result = {};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
337
|
+
const put = (harness, session, subAgentType, creds) => {
|
|
338
|
+
((result[harness] ??= {})[session] ??= {})[subAgentType] = creds;
|
|
339
|
+
};
|
|
340
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
341
|
+
// Shape 1: `key` is the sub-agent type; the harness comes from the record.
|
|
342
|
+
if (looksLikeCredentialRecord(raw)) {
|
|
343
|
+
const creds = parseAgentDynamicCredentials(raw);
|
|
344
|
+
if (creds)
|
|
345
|
+
put(creds.harness ?? exports.UNKNOWN_HARNESS_KEY, exports.SESSIONLESS_KEY, key, creds);
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (!raw || typeof raw !== "object")
|
|
349
|
+
continue;
|
|
350
|
+
for (const [inner, entry] of Object.entries(raw)) {
|
|
351
|
+
// Shape 2: `inner` is the sub-agent type, sitting directly under harness.
|
|
352
|
+
if (looksLikeCredentialRecord(entry)) {
|
|
353
|
+
const creds = parseAgentDynamicCredentials(entry);
|
|
354
|
+
if (creds)
|
|
355
|
+
put(key, exports.SESSIONLESS_KEY, inner, creds);
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
// Shape 3: `inner` is the session.
|
|
359
|
+
if (!entry || typeof entry !== "object")
|
|
360
|
+
continue;
|
|
361
|
+
for (const [subAgentType, leaf] of Object.entries(entry)) {
|
|
362
|
+
const creds = parseAgentDynamicCredentials(leaf);
|
|
363
|
+
if (creds)
|
|
364
|
+
put(key, inner, subAgentType, creds);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
180
367
|
}
|
|
181
368
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
182
369
|
}
|
|
@@ -197,8 +384,104 @@ function parseAgentDynamicCredentials(value) {
|
|
|
197
384
|
registeredAt,
|
|
198
385
|
projectUrl: v.projectUrl,
|
|
199
386
|
harness: typeof v.harness === "string" ? v.harness : undefined,
|
|
387
|
+
...(parseDelegationRecord(v.delegation)
|
|
388
|
+
? { delegation: parseDelegationRecord(v.delegation) }
|
|
389
|
+
: {}),
|
|
390
|
+
...(parseDelegationNodeIds(v.delegationNodeIds, v.delegationNodeId)
|
|
391
|
+
? { delegationNodeIds: parseDelegationNodeIds(v.delegationNodeIds, v.delegationNodeId) }
|
|
392
|
+
: {}),
|
|
200
393
|
};
|
|
201
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* Parse the per-session delegation node map, migrating the single
|
|
397
|
+
* `delegationNodeId` field earlier releases wrote.
|
|
398
|
+
*
|
|
399
|
+
* A migrated scalar lands under {@link SESSIONLESS_KEY}: the session it was
|
|
400
|
+
* recorded in was never persisted alongside it, and guessing would attribute a
|
|
401
|
+
* sub-agent to the wrong run. Landing it there means the next session records
|
|
402
|
+
* its own node id rather than inheriting a stale one — the correct outcome,
|
|
403
|
+
* since the node id is cheap to re-obtain (the broker's write is idempotent).
|
|
404
|
+
*/
|
|
405
|
+
function parseDelegationNodeIds(value, legacy) {
|
|
406
|
+
const result = {};
|
|
407
|
+
if (typeof legacy === "string" && legacy.length > 0)
|
|
408
|
+
result[exports.SESSIONLESS_KEY] = legacy;
|
|
409
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
410
|
+
for (const [session, nodeId] of Object.entries(value)) {
|
|
411
|
+
if (typeof nodeId === "string" && nodeId.length > 0)
|
|
412
|
+
result[session] = nodeId;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
416
|
+
}
|
|
417
|
+
function parseDelegationRecord(value) {
|
|
418
|
+
if (!value || typeof value !== "object")
|
|
419
|
+
return undefined;
|
|
420
|
+
const v = value;
|
|
421
|
+
if (typeof v.delegatedBy !== "string" || v.delegatedBy.length === 0)
|
|
422
|
+
return undefined;
|
|
423
|
+
if (v.delegationType !== "user-to-agent" && v.delegationType !== "agent-to-subagent") {
|
|
424
|
+
return undefined;
|
|
425
|
+
}
|
|
426
|
+
return {
|
|
427
|
+
delegatedBy: v.delegatedBy,
|
|
428
|
+
delegationType: v.delegationType,
|
|
429
|
+
...(typeof v.harness === "string" ? { harness: v.harness } : {}),
|
|
430
|
+
...(typeof v.subAgentType === "string" ? { subAgentType: v.subAgentType } : {}),
|
|
431
|
+
delegatedAt: typeof v.delegatedAt === "string" ? v.delegatedAt : "",
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
function parseInstalledHarnesses(value) {
|
|
435
|
+
if (!Array.isArray(value))
|
|
436
|
+
return undefined;
|
|
437
|
+
const names = normalizeHarnessList(value.filter((v) => typeof v === "string"));
|
|
438
|
+
return names.length > 0 ? names : undefined;
|
|
439
|
+
}
|
|
440
|
+
/** De-duplicate, trim, and sort a harness list so the file stays stable. */
|
|
441
|
+
function normalizeHarnessList(names) {
|
|
442
|
+
return [...new Set(names.map((n) => n.trim()).filter((n) => n.length > 0))].sort();
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* The harness plugins recorded as installed against this shared config.
|
|
446
|
+
* Returns an empty array when nothing has been registered yet (which, on a
|
|
447
|
+
* config written before the registry existed, is not the same as "nothing is
|
|
448
|
+
* installed" — callers that must not over-read that should reconcile against
|
|
449
|
+
* the runtime manifest and persisted credentials; see
|
|
450
|
+
* `resolveInstalledHarnesses` in `uninstall.ts`).
|
|
451
|
+
*/
|
|
452
|
+
function listInstalledHarnesses() {
|
|
453
|
+
return loadConfig().installedHarnesses ?? [];
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Record a harness plugin as installed. Idempotent — installing twice leaves
|
|
457
|
+
* one entry. Returns the full list after the addition.
|
|
458
|
+
*/
|
|
459
|
+
function registerInstalledHarness(harness) {
|
|
460
|
+
let result = [];
|
|
461
|
+
mutateConfig((current) => {
|
|
462
|
+
result = normalizeHarnessList([...(current.installedHarnesses ?? []), harness]);
|
|
463
|
+
return { ...current, installedHarnesses: result };
|
|
464
|
+
});
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Forget a harness plugin. Returns the harnesses still installed afterwards —
|
|
469
|
+
* what the uninstall path uses to decide whether the shared config is now
|
|
470
|
+
* unreferenced and safe to wipe.
|
|
471
|
+
*/
|
|
472
|
+
function unregisterInstalledHarness(harness) {
|
|
473
|
+
let result = [];
|
|
474
|
+
mutateConfig((current) => {
|
|
475
|
+
result = normalizeHarnessList(current.installedHarnesses ?? []).filter((h) => h !== harness.trim());
|
|
476
|
+
const next = { ...current };
|
|
477
|
+
if (result.length > 0)
|
|
478
|
+
next.installedHarnesses = result;
|
|
479
|
+
else
|
|
480
|
+
delete next.installedHarnesses;
|
|
481
|
+
return next;
|
|
482
|
+
});
|
|
483
|
+
return result;
|
|
484
|
+
}
|
|
202
485
|
function parseUserCredentials(parsed) {
|
|
203
486
|
// Prefer the new nested shape; fall back to the legacy root-level
|
|
204
487
|
// `oauth2` block so configs written before the user/agent split still
|
|
@@ -224,9 +507,6 @@ function parseOAuth2(value) {
|
|
|
224
507
|
refreshToken: typeof v.refreshToken === "string" ? v.refreshToken : undefined,
|
|
225
508
|
expiresAt: v.expiresAt,
|
|
226
509
|
subject: typeof v.subject === "string" ? v.subject : undefined,
|
|
227
|
-
clientId: typeof v.clientId === "string" ? v.clientId : undefined,
|
|
228
|
-
idToken: typeof v.idToken === "string" ? v.idToken : undefined,
|
|
229
|
-
scope: typeof v.scope === "string" ? v.scope : undefined,
|
|
230
510
|
};
|
|
231
511
|
}
|
|
232
512
|
/**
|
|
@@ -253,7 +533,8 @@ function mutateConfig(mutator) {
|
|
|
253
533
|
try {
|
|
254
534
|
const existing = loadConfig();
|
|
255
535
|
const next = mutator(existing);
|
|
256
|
-
|
|
536
|
+
if (next)
|
|
537
|
+
writeConfigAtomic(next);
|
|
257
538
|
}
|
|
258
539
|
finally {
|
|
259
540
|
releaseLock(lock);
|
|
@@ -372,9 +653,101 @@ function releaseLock(lock) {
|
|
|
372
653
|
function writeConfigAtomic(config) {
|
|
373
654
|
const target = configFile();
|
|
374
655
|
const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
375
|
-
fs.writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n", {
|
|
656
|
+
fs.writeFileSync(tmp, JSON.stringify(compactPersistedConfig(config), null, 2) + "\n", {
|
|
657
|
+
mode: 0o600,
|
|
658
|
+
});
|
|
376
659
|
fs.renameSync(tmp, target);
|
|
377
660
|
}
|
|
661
|
+
/**
|
|
662
|
+
* Keep only state that cannot be reconstructed safely in a later process.
|
|
663
|
+
* This is the one persistence boundary, so old verbose records are compacted
|
|
664
|
+
* by any subsequent config mutation regardless of which subsystem writes.
|
|
665
|
+
*/
|
|
666
|
+
function compactPersistedConfig(config) {
|
|
667
|
+
const userTokens = config.user?.oauth2;
|
|
668
|
+
const user = userTokens
|
|
669
|
+
? {
|
|
670
|
+
oauth2: {
|
|
671
|
+
accessToken: userTokens.accessToken,
|
|
672
|
+
...(userTokens.refreshToken ? { refreshToken: userTokens.refreshToken } : {}),
|
|
673
|
+
expiresAt: userTokens.expiresAt,
|
|
674
|
+
...(userTokens.subject ? { subject: userTokens.subject } : {}),
|
|
675
|
+
},
|
|
676
|
+
}
|
|
677
|
+
: undefined;
|
|
678
|
+
const active = (creds) => ({
|
|
679
|
+
clientId: creds.clientId,
|
|
680
|
+
...(creds.clientSecret ? { clientSecret: creds.clientSecret } : {}),
|
|
681
|
+
...(creds.registrationAccessToken
|
|
682
|
+
? { registrationAccessToken: creds.registrationAccessToken }
|
|
683
|
+
: {}),
|
|
684
|
+
...(creds.registrationClientUri
|
|
685
|
+
? { registrationClientUri: creds.registrationClientUri }
|
|
686
|
+
: {}),
|
|
687
|
+
registeredAt: creds.registeredAt,
|
|
688
|
+
projectUrl: creds.projectUrl,
|
|
689
|
+
...(creds.delegation
|
|
690
|
+
? {
|
|
691
|
+
delegation: {
|
|
692
|
+
delegatedBy: creds.delegation.delegatedBy,
|
|
693
|
+
delegationType: creds.delegation.delegationType,
|
|
694
|
+
},
|
|
695
|
+
}
|
|
696
|
+
: {}),
|
|
697
|
+
...(creds.delegationNodeIds
|
|
698
|
+
? { delegationNodeIds: creds.delegationNodeIds }
|
|
699
|
+
: {}),
|
|
700
|
+
});
|
|
701
|
+
const dynamic = config.agent?.dynamic
|
|
702
|
+
? Object.fromEntries(Object.entries(config.agent.dynamic).map(([harness, sessions]) => [
|
|
703
|
+
harness,
|
|
704
|
+
Object.fromEntries(Object.entries(sessions).map(([session, creds]) => [session, active(creds)])),
|
|
705
|
+
]))
|
|
706
|
+
: undefined;
|
|
707
|
+
const subAgents = config.agent?.subAgents
|
|
708
|
+
? Object.fromEntries(Object.entries(config.agent.subAgents).map(([harness, sessions]) => [
|
|
709
|
+
harness,
|
|
710
|
+
Object.fromEntries(Object.entries(sessions).map(([session, byType]) => [
|
|
711
|
+
session,
|
|
712
|
+
Object.fromEntries(Object.entries(byType).map(([type, creds]) => [type, active(creds)])),
|
|
713
|
+
])),
|
|
714
|
+
]))
|
|
715
|
+
: undefined;
|
|
716
|
+
const retired = config.agent?.retired?.map((creds) => ({
|
|
717
|
+
clientId: creds.clientId,
|
|
718
|
+
projectUrl: creds.projectUrl,
|
|
719
|
+
...(creds.harness ? { harness: creds.harness } : {}),
|
|
720
|
+
...(creds.registrationAccessToken
|
|
721
|
+
? { registrationAccessToken: creds.registrationAccessToken }
|
|
722
|
+
: {}),
|
|
723
|
+
...(creds.registrationClientUri
|
|
724
|
+
? { registrationClientUri: creds.registrationClientUri }
|
|
725
|
+
: {}),
|
|
726
|
+
}));
|
|
727
|
+
const agent = dynamic || subAgents || retired?.length
|
|
728
|
+
? {
|
|
729
|
+
...(dynamic ? { dynamic } : {}),
|
|
730
|
+
...(subAgents ? { subAgents } : {}),
|
|
731
|
+
...(retired?.length ? { retired } : {}),
|
|
732
|
+
}
|
|
733
|
+
: undefined;
|
|
734
|
+
return {
|
|
735
|
+
...(config.projectUrl ? { projectUrl: config.projectUrl } : {}),
|
|
736
|
+
...(config.agentSecurityUrl ? { agentSecurityUrl: config.agentSecurityUrl } : {}),
|
|
737
|
+
...(config.projectId ? { projectId: config.projectId } : {}),
|
|
738
|
+
...(config.oauth2ClientId ? { oauth2ClientId: config.oauth2ClientId } : {}),
|
|
739
|
+
...(config.permissionModeCache ? { permissionModeCache: config.permissionModeCache } : {}),
|
|
740
|
+
...(config.userSubjectNamespace
|
|
741
|
+
? { userSubjectNamespace: config.userSubjectNamespace }
|
|
742
|
+
: {}),
|
|
743
|
+
...(config.installedHarnesses?.length
|
|
744
|
+
? { installedHarnesses: config.installedHarnesses }
|
|
745
|
+
: {}),
|
|
746
|
+
...(config.delegationAnchors ? { delegationAnchors: config.delegationAnchors } : {}),
|
|
747
|
+
...(user ? { user } : {}),
|
|
748
|
+
...(agent ? { agent } : {}),
|
|
749
|
+
};
|
|
750
|
+
}
|
|
378
751
|
/** Matches an Ory project UUID (the value `ORY_PROJECT_ID` carries). */
|
|
379
752
|
const PROJECT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
380
753
|
/**
|
|
@@ -390,6 +763,47 @@ const PROJECT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]
|
|
|
390
763
|
function looksLikeProjectId(value) {
|
|
391
764
|
return PROJECT_ID_RE.test(value.trim());
|
|
392
765
|
}
|
|
766
|
+
/** Reserved public PKCE client provisioned by Ory Agent Security setup. */
|
|
767
|
+
exports.DEFAULT_OAUTH2_CLIENT_ID = "ory-agent-security-login";
|
|
768
|
+
exports.DEFAULT_AGENT_SECURITY_URL = "https://agents.console.ory.com";
|
|
769
|
+
exports.LOCAL_AGENT_SECURITY_URL = "https://agents.console.ory:8080";
|
|
770
|
+
function resolveSecurityConnection(projectUrl, agentSecurityUrl, oauth2ClientId) {
|
|
771
|
+
const missing = [];
|
|
772
|
+
if (!projectUrl?.trim())
|
|
773
|
+
missing.push("projectUrl");
|
|
774
|
+
if (!agentSecurityUrl?.trim())
|
|
775
|
+
missing.push("agentSecurityUrl");
|
|
776
|
+
if (!oauth2ClientId?.trim())
|
|
777
|
+
missing.push("oauth2ClientId");
|
|
778
|
+
return { connected: missing.length === 0, missing };
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* The one predicate the gates ask: is Agent Security connected?
|
|
782
|
+
*
|
|
783
|
+
* Shorthand for `resolveConfig().security.connected`, provided so the many
|
|
784
|
+
* call sites that only need the boolean read as a single question rather than
|
|
785
|
+
* a property walk. When this is false the plugin still runs — it just logs activity
|
|
786
|
+
* locally and gates nothing.
|
|
787
|
+
*/
|
|
788
|
+
function isSecurityConnected() {
|
|
789
|
+
return resolveConfig().security.connected;
|
|
790
|
+
}
|
|
791
|
+
/** The CLI flag / env var a user sets for each required value. */
|
|
792
|
+
const SECURITY_VALUE_LABELS = {
|
|
793
|
+
projectUrl: "--project-url (ORY_PROJECT_URL)",
|
|
794
|
+
agentSecurityUrl: "--agent-security-url (ORY_AGENT_SECURITY_URL)",
|
|
795
|
+
oauth2ClientId: "--oauth2-client-id (ORY_OAUTH2_CLIENT_ID)",
|
|
796
|
+
};
|
|
797
|
+
/**
|
|
798
|
+
* Render why Agent Security isn't running, e.g.
|
|
799
|
+
* `"missing --oauth2-client-id (ORY_OAUTH2_CLIENT_ID)"`. Returns undefined when
|
|
800
|
+
* it *is* connected, so callers can `?? "connected"` at the display site.
|
|
801
|
+
*/
|
|
802
|
+
function describeSecurityGap(security = resolveConfig().security) {
|
|
803
|
+
if (security.connected)
|
|
804
|
+
return undefined;
|
|
805
|
+
return `missing ${security.missing.map((m) => SECURITY_VALUE_LABELS[m]).join(" and ")}`;
|
|
806
|
+
}
|
|
393
807
|
/**
|
|
394
808
|
* Resolve config by checking environment variables first, then the config file.
|
|
395
809
|
* Returns the merged result with the source of each value.
|
|
@@ -404,27 +818,17 @@ function resolveConfig() {
|
|
|
404
818
|
// ORY_PROJECT_URL is the documented name; ORY_SDK_URL is the name the Ory
|
|
405
819
|
// Console "Get Started" panel and the Ory SDKs use — accepted as an alias.
|
|
406
820
|
const envProjectUrl = process.env.ORY_PROJECT_URL?.trim() || process.env.ORY_SDK_URL?.trim() || undefined;
|
|
821
|
+
const envAgentSecurityUrl = process.env.ORY_AGENT_SECURITY_URL?.trim() || undefined;
|
|
407
822
|
const envProjectId = process.env.ORY_PROJECT_ID?.trim() || undefined;
|
|
408
|
-
|
|
409
|
-
// alias kept for back-compat (agent-auth emits a warning when only the
|
|
410
|
-
// legacy form is set).
|
|
411
|
-
const envApiKey = process.env.ORY_AGENT_API_KEY ?? process.env.ORY_API_KEY;
|
|
412
|
-
const envClientId = process.env.ORY_OAUTH2_CLIENT_ID;
|
|
413
|
-
const envMode = parsePermissionMode(process.env.ORY_PERMISSION_MODE);
|
|
414
|
-
const envUserLogin = parseUserLoginEnv();
|
|
823
|
+
const envClientId = process.env.ORY_OAUTH2_CLIENT_ID?.trim() || undefined;
|
|
415
824
|
const envUserSubjectNamespace = process.env.ORY_USER_SUBJECT_NAMESPACE?.trim() || undefined;
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const userLoginSource = envUserLogin !== undefined
|
|
424
|
-
? "env"
|
|
425
|
-
: file.userLogin !== undefined
|
|
426
|
-
? "config"
|
|
427
|
-
: "default";
|
|
825
|
+
// The mode is server-controlled (a Keto permission read per-check by
|
|
826
|
+
// resolvePermissionMode). Here we only surface the last cached value for the
|
|
827
|
+
// synchronous read path; there is no env or local override.
|
|
828
|
+
const permissionMode = file.permissionModeCache?.mode ?? "observe";
|
|
829
|
+
const permissionModeSource = file.permissionModeCache
|
|
830
|
+
? "cache"
|
|
831
|
+
: "default";
|
|
428
832
|
const projectId = envProjectId ?? file.projectId;
|
|
429
833
|
// The SDK URL is only ever an explicit, slug-based URL (from ORY_PROJECT_URL,
|
|
430
834
|
// ORY_SDK_URL, or the config file). The project id is NOT a URL source — it
|
|
@@ -435,16 +839,33 @@ function resolveConfig() {
|
|
|
435
839
|
: file.projectUrl
|
|
436
840
|
? "config"
|
|
437
841
|
: "none";
|
|
842
|
+
// Preserve the project URL fallback only for persisted pre-default configs.
|
|
843
|
+
// New env-only and configure flows use the production default unless they
|
|
844
|
+
// explicitly select staging, local, or a custom broker.
|
|
845
|
+
const legacyAgentSecurityUrl = !envProjectUrl && file.projectUrl?.trim() && !file.agentSecurityUrl ? file.projectUrl.trim() : undefined;
|
|
846
|
+
const agentSecurityUrl = envAgentSecurityUrl ?? file.agentSecurityUrl ?? legacyAgentSecurityUrl ?? exports.DEFAULT_AGENT_SECURITY_URL;
|
|
847
|
+
const agentSecurityUrlSource = envAgentSecurityUrl
|
|
848
|
+
? "env"
|
|
849
|
+
: file.agentSecurityUrl
|
|
850
|
+
? "config"
|
|
851
|
+
: legacyAgentSecurityUrl
|
|
852
|
+
? "projectUrl"
|
|
853
|
+
: "default";
|
|
854
|
+
const fileClientId = file.oauth2ClientId?.trim() || undefined;
|
|
855
|
+
const oauth2ClientId = envClientId ?? fileClientId ?? exports.DEFAULT_OAUTH2_CLIENT_ID;
|
|
438
856
|
return {
|
|
439
857
|
projectUrl,
|
|
858
|
+
agentSecurityUrl,
|
|
440
859
|
projectId,
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
860
|
+
projectName: file.projectName,
|
|
861
|
+
workspaceName: file.workspaceName,
|
|
862
|
+
oauth2ClientId,
|
|
863
|
+
// Agent Security runs exactly when all resolved values it needs are present.
|
|
864
|
+
// The broker origin can resolve through the legacy project URL fallback.
|
|
865
|
+
security: resolveSecurityConnection(projectUrl, agentSecurityUrl, oauth2ClientId),
|
|
444
866
|
permissionMode,
|
|
445
867
|
permissionModeSource,
|
|
446
|
-
|
|
447
|
-
userLoginSource,
|
|
868
|
+
permissionModeCache: file.permissionModeCache,
|
|
448
869
|
userSubjectNamespace: envUserSubjectNamespace ?? file.userSubjectNamespace,
|
|
449
870
|
userSubjectNamespaceSource: envUserSubjectNamespace
|
|
450
871
|
? "env"
|
|
@@ -452,22 +873,25 @@ function resolveConfig() {
|
|
|
452
873
|
? "config"
|
|
453
874
|
: "none",
|
|
454
875
|
projectUrlSource,
|
|
876
|
+
agentSecurityUrlSource,
|
|
455
877
|
projectIdSource: envProjectId ? "env" : file.projectId ? "config" : "none",
|
|
456
|
-
|
|
457
|
-
oauth2ClientIdSource: envClientId ? "env" : file.oauth2ClientId ? "config" : "none",
|
|
878
|
+
oauth2ClientIdSource: envClientId ? "env" : fileClientId ? "config" : "default",
|
|
458
879
|
};
|
|
459
880
|
}
|
|
460
881
|
/**
|
|
461
|
-
* Build the "not
|
|
882
|
+
* Build the "Agent Security is not connected" message for a harness CLI.
|
|
883
|
+
*
|
|
884
|
+
* Deliberately does not frame this as a degraded mode: the plugin is fully
|
|
885
|
+
* installed and its developer-experience half is working. The message states
|
|
886
|
+
* the one thing that is off and the values that turn it on.
|
|
462
887
|
*/
|
|
463
888
|
function configPromptMessage(binName) {
|
|
464
|
-
return ("
|
|
465
|
-
"\n" +
|
|
466
|
-
"
|
|
467
|
-
` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]\n` +
|
|
889
|
+
return ("Ory Agent Security is not connected, so no sign-in or permission checks\n" +
|
|
890
|
+
"run. Everything else the plugin installs — skills, commands, the local Ory\n" +
|
|
891
|
+
"stack, the MCP server, and local activity logging — works as it is.\n" +
|
|
468
892
|
"\n" +
|
|
469
|
-
"
|
|
470
|
-
` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --
|
|
893
|
+
" Connect it with the values from the Ory Console (Agent Security):\n" +
|
|
894
|
+
` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure --project-url <URL>\n` +
|
|
471
895
|
"\n" +
|
|
472
896
|
`Configuration is saved to ${configFile()} and shared across all agent plugins.`);
|
|
473
897
|
}
|