@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/local/cli.js CHANGED
@@ -7,6 +7,7 @@
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.runLocalCommand = runLocalCommand;
9
9
  const manager_js_1 = require("./manager.js");
10
+ const cli_invocation_js_1 = require("../cli-invocation.js");
10
11
  /**
11
12
  * Run a `local` subcommand. Call from each plugin CLI's `local` case.
12
13
  *
@@ -57,7 +58,7 @@ function printLocalHelp(binName) {
57
58
  ${binName} local — Run Ory locally for development
58
59
 
59
60
  Usage:
60
- npx ${binName} local <command> [options]
61
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} local <command> [options]
61
62
 
62
63
  Commands:
63
64
  up [--no-seed] Start local Ory services (Kratos, Keto, Hydra, gateway)
@@ -81,15 +82,15 @@ After 'local up', set ORY_PROJECT_URL=http://localhost:4000 to point
81
82
  any Ory agent plugin at the local environment.
82
83
 
83
84
  Examples:
84
- npx ${binName} local up # Start everything and seed test data
85
- npx ${binName} local up --no-seed # Start services without seeding
86
- npx ${binName} local seed # (Re-)seed test data
87
- npx ${binName} local status # Check what's running
88
- npx ${binName} local logs kratos # View Kratos logs
89
- npx ${binName} local logs -f # Follow all logs
90
- npx ${binName} local configure # Save local URL to config
91
- npx ${binName} local env # Print export statements
92
- npx ${binName} local down # Stop services
93
- npx ${binName} local reset # Stop and delete all data
85
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} local up # Start everything and seed test data
86
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} local up --no-seed # Start services without seeding
87
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} local seed # (Re-)seed test data
88
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} local status # Check what's running
89
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} local logs kratos # View Kratos logs
90
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} local logs -f # Follow all logs
91
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} local configure # Save local URL to config
92
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} local env # Print export statements
93
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} local down # Stop services
94
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} local reset # Stop and delete all data
94
95
  `);
95
96
  }
@@ -1,6 +1,6 @@
1
1
  export { runLocalCommand } from "./cli.js";
2
2
  export { localUp, localDown, localStatus, localSeed, localLogs, localReset, localEnv, localConfigure, ensureLocalOryStack, type EnsureLocalOryStackResult, } from "./manager.js";
3
3
  export { checkAllServices, waitForGateway, type ServiceHealth } from "./health.js";
4
- export { seedLocalEnvironment, type SeedResult } from "./seed.js";
4
+ export { seedLocalEnvironment, USER_CLIENT_ID, USER_SUBJECT_NAMESPACE, type SeedResult, } from "./seed.js";
5
5
  export { GATEWAY_URL, GATEWAY_PORT, KRATOS_PUBLIC_PORT, KRATOS_ADMIN_PORT, KETO_READ_PORT, KETO_WRITE_PORT, HYDRA_PUBLIC_PORT, HYDRA_ADMIN_PORT, JAEGER_UI_PORT, JAEGER_OTLP_HTTP_PORT, JAEGER_UI_URL, JAEGER_OTLP_HTTP_URL, } from "./configs.js";
6
6
  export { ensureDevJaeger, stopDevJaeger, DEV_JAEGER_CONTAINER, DEV_JAEGER_IMAGE, type EnsureDevJaegerResult, type StopDevJaegerResult, } from "./jaeger.js";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEV_JAEGER_IMAGE = exports.DEV_JAEGER_CONTAINER = exports.stopDevJaeger = exports.ensureDevJaeger = exports.JAEGER_OTLP_HTTP_URL = exports.JAEGER_UI_URL = exports.JAEGER_OTLP_HTTP_PORT = exports.JAEGER_UI_PORT = exports.HYDRA_ADMIN_PORT = exports.HYDRA_PUBLIC_PORT = exports.KETO_WRITE_PORT = exports.KETO_READ_PORT = exports.KRATOS_ADMIN_PORT = exports.KRATOS_PUBLIC_PORT = exports.GATEWAY_PORT = exports.GATEWAY_URL = exports.seedLocalEnvironment = exports.waitForGateway = exports.checkAllServices = exports.ensureLocalOryStack = exports.localConfigure = exports.localEnv = exports.localReset = exports.localLogs = exports.localSeed = exports.localStatus = exports.localDown = exports.localUp = exports.runLocalCommand = void 0;
3
+ exports.DEV_JAEGER_IMAGE = exports.DEV_JAEGER_CONTAINER = exports.stopDevJaeger = exports.ensureDevJaeger = exports.JAEGER_OTLP_HTTP_URL = exports.JAEGER_UI_URL = exports.JAEGER_OTLP_HTTP_PORT = exports.JAEGER_UI_PORT = exports.HYDRA_ADMIN_PORT = exports.HYDRA_PUBLIC_PORT = exports.KETO_WRITE_PORT = exports.KETO_READ_PORT = exports.KRATOS_ADMIN_PORT = exports.KRATOS_PUBLIC_PORT = exports.GATEWAY_PORT = exports.GATEWAY_URL = exports.USER_SUBJECT_NAMESPACE = exports.USER_CLIENT_ID = exports.seedLocalEnvironment = exports.waitForGateway = exports.checkAllServices = exports.ensureLocalOryStack = exports.localConfigure = exports.localEnv = exports.localReset = exports.localLogs = exports.localSeed = exports.localStatus = exports.localDown = exports.localUp = exports.runLocalCommand = void 0;
4
4
  var cli_js_1 = require("./cli.js");
5
5
  Object.defineProperty(exports, "runLocalCommand", { enumerable: true, get: function () { return cli_js_1.runLocalCommand; } });
6
6
  var manager_js_1 = require("./manager.js");
@@ -18,6 +18,8 @@ Object.defineProperty(exports, "checkAllServices", { enumerable: true, get: func
18
18
  Object.defineProperty(exports, "waitForGateway", { enumerable: true, get: function () { return health_js_1.waitForGateway; } });
19
19
  var seed_js_1 = require("./seed.js");
20
20
  Object.defineProperty(exports, "seedLocalEnvironment", { enumerable: true, get: function () { return seed_js_1.seedLocalEnvironment; } });
21
+ Object.defineProperty(exports, "USER_CLIENT_ID", { enumerable: true, get: function () { return seed_js_1.USER_CLIENT_ID; } });
22
+ Object.defineProperty(exports, "USER_SUBJECT_NAMESPACE", { enumerable: true, get: function () { return seed_js_1.USER_SUBJECT_NAMESPACE; } });
21
23
  var configs_js_1 = require("./configs.js");
22
24
  Object.defineProperty(exports, "GATEWAY_URL", { enumerable: true, get: function () { return configs_js_1.GATEWAY_URL; } });
23
25
  Object.defineProperty(exports, "GATEWAY_PORT", { enumerable: true, get: function () { return configs_js_1.GATEWAY_PORT; } });
@@ -743,7 +743,7 @@ function printSeedResult(result) {
743
743
  console.log("");
744
744
  console.log("Or save the project URL to the shared config (persists across sessions):");
745
745
  console.log("");
746
- console.log(" npx ory-<harness> local configure");
746
+ console.log(" npx -y -p @ory/<harness> ory-<harness> local configure");
747
747
  }
748
748
  function printConnectionInfo() {
749
749
  console.log("Connection Info");
@@ -21,6 +21,39 @@
21
21
  * All four are non-destructive on existing config and idempotent on
22
22
  * Keto: re-running them is always safe.
23
23
  */
24
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ var desc = Object.getOwnPropertyDescriptor(m, k);
27
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
28
+ desc = { enumerable: true, get: function() { return m[k]; } };
29
+ }
30
+ Object.defineProperty(o, k2, desc);
31
+ }) : (function(o, m, k, k2) {
32
+ if (k2 === undefined) k2 = k;
33
+ o[k2] = m[k];
34
+ }));
35
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
36
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
37
+ }) : function(o, v) {
38
+ o["default"] = v;
39
+ });
40
+ var __importStar = (this && this.__importStar) || (function () {
41
+ var ownKeys = function(o) {
42
+ ownKeys = Object.getOwnPropertyNames || function (o) {
43
+ var ar = [];
44
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
45
+ return ar;
46
+ };
47
+ return ownKeys(o);
48
+ };
49
+ return function (mod) {
50
+ if (mod && mod.__esModule) return mod;
51
+ var result = {};
52
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
53
+ __setModuleDefault(result, mod);
54
+ return result;
55
+ };
56
+ })();
24
57
  Object.defineProperty(exports, "__esModule", { value: true });
25
58
  exports.runPermissionsCommand = runPermissionsCommand;
26
59
  exports.isUserIdentityCached = isUserIdentityCached;
@@ -32,6 +65,8 @@ const agent_auth_js_1 = require("./agent-auth.js");
32
65
  const auth_store_js_1 = require("./auth-store.js");
33
66
  const tool_catalog_js_1 = require("./tool-catalog.js");
34
67
  const subject_js_1 = require("./subject.js");
68
+ const cli_invocation_js_1 = require("./cli-invocation.js");
69
+ const ui = __importStar(require("./ui.js"));
35
70
  const ENV_PERMISSION_MODE = "ORY_PERMISSION_MODE";
36
71
  function resolveNamespace() {
37
72
  return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools";
@@ -341,36 +376,37 @@ function isUserIdentityCached() {
341
376
  function printPermissionsOnboardingHelp(binName, harness, opts = {}) {
342
377
  const resolved = (0, config_js_1.resolveConfig)();
343
378
  const catalog = (0, tool_catalog_js_1.getToolCatalog)(harness);
344
- console.log("");
345
- console.log("Permissions:");
346
- console.log(` Mode: ${resolved.permissionMode}${formatModeSource(resolved.permissionModeSource)}`);
347
- console.log(` Other modes: ${otherModes(resolved.permissionMode).join(", ")}`);
379
+ ui.heading("Permissions:");
380
+ ui.summary([
381
+ ["Mode", `${resolved.permissionMode}${formatModeSource(resolved.permissionModeSource)}`],
382
+ ["Other modes", otherModes(resolved.permissionMode).join(", ")],
383
+ ]);
348
384
  if (resolved.auditOnly) {
349
- console.log(" Ory is in audit-only mode (kill switch). Permission checks are disabled.");
385
+ ui.hint("Ory is in audit-only mode (kill switch). Permission checks are disabled.");
350
386
  return;
351
387
  }
352
388
  if (resolved.permissionMode === "observe") {
353
- console.log(" Observe mode: permission denies are logged + traced, but tools still run.");
354
- console.log(" This is the safe default — promote to 'enforce' once your permission set is correct.");
389
+ ui.hint("Observe mode: permission denies are logged + traced, but tools still run.");
390
+ ui.hint("This is the safe default — promote to 'enforce' once your permission set is correct.");
355
391
  }
356
392
  else {
357
- console.log(" Enforce mode: permission denies block tool execution.");
393
+ ui.hint("Enforce mode: permission denies block tool execution.");
358
394
  }
359
395
  if (opts.bootstrappedAutomatically) {
360
- console.log("");
361
- console.log(` Permissions bootstrapped for ${catalog.length} built-in tools.`);
396
+ ui.blank();
397
+ ui.success(`Permissions bootstrapped for ${catalog.length} built-in tools.`);
362
398
  }
363
399
  else if (catalog.length > 0) {
364
- console.log("");
365
- console.log(" Grant the current user 'use' on every built-in tool (idempotent):");
366
- console.log(` npx ${binName} permissions bootstrap`);
400
+ ui.blank();
401
+ ui.bullet("Grant the current user 'use' on every built-in tool (idempotent):");
402
+ ui.command(`${(0, cli_invocation_js_1.oryNpx)(binName)} permissions bootstrap`);
367
403
  }
368
- console.log("");
369
- console.log(" Inspect permission coverage:");
370
- console.log(` npx ${binName} permissions status`);
371
- console.log("");
372
- console.log(" Promote to enforcing once observe-mode logs look right:");
373
- console.log(` npx ${binName} permissions enforce`);
404
+ ui.blank();
405
+ ui.bullet("Inspect permission coverage:");
406
+ ui.command(`${(0, cli_invocation_js_1.oryNpx)(binName)} permissions status`);
407
+ ui.blank();
408
+ ui.bullet("Promote to enforcing once observe-mode logs look right:");
409
+ ui.command(`${(0, cli_invocation_js_1.oryNpx)(binName)} permissions enforce`);
374
410
  }
375
411
  /**
376
412
  * Render the trailing source annotation on a Mode line. Stays silent
@@ -45,6 +45,13 @@ uplinks:
45
45
  cache: false
46
46
 
47
47
  packages:
48
+ # @ory/cli is a transitive dep that lives on npmjs.org — proxy
49
+ # it so installs can resolve it from upstream.
50
+ "@ory/cli":
51
+ access: $all
52
+ publish: $all
53
+ proxy: npmjs
54
+
48
55
  # @ory/client is a transitive Ory SDK dep that lives on npmjs.org — proxy
49
56
  # it so installs can resolve it from upstream.
50
57
  "@ory/client":
package/dist/setup.d.ts CHANGED
@@ -113,6 +113,46 @@ export declare function printSetupHelp(binName: string, harnessName: string, opt
113
113
  globalPath?: string;
114
114
  }): void;
115
115
  /**
116
- * Print the "Next steps" text after successful setup.
117
- */
118
- export declare function printNextSteps(harnessName: string, uninstallCmd: string): void;
116
+ * A deferred "next steps" renderer. Receives whether the interactive wizard
117
+ * actually finished wiring Ory up so it can pick the right closing message:
118
+ * a short "you're all set, launch the harness" summary when everything is
119
+ * already configured, or the full manual env-var setup guidance when it isn't.
120
+ */
121
+ export type NextStepsRenderer = (opts: {
122
+ configured: boolean;
123
+ }) => void;
124
+ /** Begin capturing `printNextSteps` output instead of printing it. */
125
+ export declare function beginDeferNextSteps(): void;
126
+ /**
127
+ * Print any captured next-steps output and stop deferring. `configured` tells
128
+ * the renderer whether the wizard finished configuring Ory (so it can drop the
129
+ * manual setup steps, which would only contradict what's already saved).
130
+ */
131
+ export declare function emitDeferredNextSteps(configured?: boolean): void;
132
+ /**
133
+ * Either run the next-steps printer now, or capture it for later when
134
+ * deferral is active. Harness-specific next-steps printers (e.g. Codex's)
135
+ * route through this so they participate in the same deferral. When run inline
136
+ * (e.g. the standalone setup binary) no wizard configured anything, so the
137
+ * renderer is invoked with `configured: false`.
138
+ */
139
+ export declare function nextStepsSink(fn: NextStepsRenderer): void;
140
+ /**
141
+ * Print the "Next steps" text after successful setup. Deferred to the end of
142
+ * an interactive install via {@link nextStepsSink} (see above). When the wizard
143
+ * already connected Ory, prints a short launch summary instead of the manual
144
+ * env-var setup steps.
145
+ */
146
+ export declare function printNextSteps(harnessName: string, uninstallCmd: string, opts?: NextStepsOpts): void;
147
+ /**
148
+ * Identity a harness passes to {@link printNextSteps} so the "you're all set"
149
+ * message can emit copy-pasteable `status` / `watch` / `permissions` commands
150
+ * and tailor itself to the mode the wizard actually configured (audit-only has
151
+ * no enforcement step). Optional so older callers still get the short summary.
152
+ */
153
+ export interface NextStepsOpts {
154
+ /** CLI bin name, e.g. `ory-claude` — used to render follow-up commands. */
155
+ binName: string;
156
+ /** Harness slug, e.g. `claude-code` — used to read the configured mode. */
157
+ harness: string;
158
+ }
package/dist/setup.js CHANGED
@@ -51,10 +51,16 @@ exports.removeMcpServer = removeMcpServer;
51
51
  exports.registerPlugin = registerPlugin;
52
52
  exports.unregisterPlugin = unregisterPlugin;
53
53
  exports.printSetupHelp = printSetupHelp;
54
+ exports.beginDeferNextSteps = beginDeferNextSteps;
55
+ exports.emitDeferredNextSteps = emitDeferredNextSteps;
56
+ exports.nextStepsSink = nextStepsSink;
54
57
  exports.printNextSteps = printNextSteps;
55
58
  const fs = __importStar(require("node:fs"));
56
59
  const os = __importStar(require("node:os"));
57
60
  const path = __importStar(require("node:path"));
61
+ const cli_invocation_js_1 = require("./cli-invocation.js");
62
+ const config_js_1 = require("./config.js");
63
+ const ui = __importStar(require("./ui.js"));
58
64
  // ─── Arg Parsing ───────────────────────────────────────────────────
59
65
  /**
60
66
  * Parse standard setup CLI arguments from process.argv.
@@ -362,7 +368,7 @@ function printSetupHelp(binName, harnessName, opts) {
362
368
  ${binName} — Configure Ory hooks for ${harnessName}
363
369
 
364
370
  Usage:
365
- npx ${binName} [options]
371
+ ${(0, cli_invocation_js_1.oryNpx)(binName)} [options]
366
372
 
367
373
  Options:
368
374
  --project-dir <path> Project directory (default: cwd)${opts?.supportsGlobal ? `\n --global Install to ${opts.globalPath ?? "global config"} instead of project` : ""}
@@ -377,34 +383,121 @@ Environment variables:
377
383
  ORY_OAUTH2_CLIENT_ID Public OAuth2 client id for the user PKCE flow
378
384
  (required when ORY_USER_LOGIN=true)
379
385
  ORY_USER_LOGIN Set to "true" to enable the interactive user login
386
+ (or persist it with: configure --user-login)
380
387
  ORY_PERMISSION_MODE "observe" (default) or "enforce" — what to do on deny
381
388
  ORY_AGENT_DEBUG Set to "true" for debug logging
382
389
  ORY_AGENT_LOG_FILE Path to write debug logs
383
390
  ORY_AGENT_SUBJECT_ID Override the subject ID for permission checks
384
391
  `);
385
392
  }
393
+ let deferNextStepsActive = false;
394
+ let deferredNextSteps = null;
395
+ /** Begin capturing `printNextSteps` output instead of printing it. */
396
+ function beginDeferNextSteps() {
397
+ deferNextStepsActive = true;
398
+ deferredNextSteps = null;
399
+ }
400
+ /**
401
+ * Print any captured next-steps output and stop deferring. `configured` tells
402
+ * the renderer whether the wizard finished configuring Ory (so it can drop the
403
+ * manual setup steps, which would only contradict what's already saved).
404
+ */
405
+ function emitDeferredNextSteps(configured = false) {
406
+ deferNextStepsActive = false;
407
+ const fn = deferredNextSteps;
408
+ deferredNextSteps = null;
409
+ if (fn)
410
+ fn({ configured });
411
+ }
412
+ /**
413
+ * Either run the next-steps printer now, or capture it for later when
414
+ * deferral is active. Harness-specific next-steps printers (e.g. Codex's)
415
+ * route through this so they participate in the same deferral. When run inline
416
+ * (e.g. the standalone setup binary) no wizard configured anything, so the
417
+ * renderer is invoked with `configured: false`.
418
+ */
419
+ function nextStepsSink(fn) {
420
+ if (deferNextStepsActive) {
421
+ deferredNextSteps = fn;
422
+ return;
423
+ }
424
+ fn({ configured: false });
425
+ }
386
426
  /**
387
- * Print the "Next steps" text after successful setup.
427
+ * Print the "Next steps" text after successful setup. Deferred to the end of
428
+ * an interactive install via {@link nextStepsSink} (see above). When the wizard
429
+ * already connected Ory, prints a short launch summary instead of the manual
430
+ * env-var setup steps.
388
431
  */
389
- function printNextSteps(harnessName, uninstallCmd) {
390
- console.log("");
391
- console.log("Next steps:");
392
- console.log(" 1. Set required environment variables:");
393
- console.log(" export ORY_PROJECT_URL=https://your-project.projects.oryapis.com");
394
- console.log(" export ORY_AGENT_API_KEY=ory_pat_...");
395
- console.log("");
396
- console.log(" 2. Turn on the interactive user login (opt-in):");
397
- console.log(" export ORY_USER_LOGIN=true");
398
- console.log(" export ORY_OAUTH2_CLIENT_ID=<public OAuth2 client id>");
399
- console.log(` Without these, ${harnessName} runs without a human Ory identity attached`);
400
- console.log(" to the session and permission checks fall back to a session:<id> subject.");
401
- console.log(" The OAuth2 client id is the public client (no secret) you registered in");
402
- console.log(" your Ory project with the four loopback redirect URIs.");
403
- console.log("");
404
- console.log(" 3. Optionally enable debug logging:");
405
- console.log(" export ORY_AGENT_DEBUG=true");
406
- console.log("");
407
- console.log(` 4. Start ${harnessName} in your project directory.`);
408
- console.log("");
409
- console.log(`To uninstall: ${uninstallCmd}`);
432
+ function printNextSteps(harnessName, uninstallCmd, opts) {
433
+ nextStepsSink(({ configured }) => configured
434
+ ? printConfiguredNextStepsNow(harnessName, uninstallCmd, opts)
435
+ : printNextStepsNow(harnessName, uninstallCmd));
436
+ }
437
+ /**
438
+ * The closing message when the interactive wizard already configured Ory.
439
+ * Everything the manual steps would ask for is done, so this points the user
440
+ * at the next things they'll actually want: see what Ory is doing (status /
441
+ * live trace watch), promote to enforcement, and turn on debug logging. When
442
+ * no {@link NextStepsOpts} is supplied it degrades to the short launch summary.
443
+ */
444
+ function printConfiguredNextStepsNow(harnessName, uninstallCmd, opts) {
445
+ ui.heading("Next steps:");
446
+ ui.success(`Ory is configured — start ${harnessName} in your project directory.`);
447
+ if (!opts) {
448
+ ui.blank();
449
+ ui.hint(`To uninstall: ${ui.inlineCommand(uninstallCmd)}`);
450
+ return;
451
+ }
452
+ const npx = (0, cli_invocation_js_1.oryNpx)(opts.binName);
453
+ const auditOnly = (0, config_js_1.resolveConfig)().auditOnly;
454
+ if (auditOnly) {
455
+ ui.hint("Audit-only mode: tool calls are traced locally — no checks run, nothing is blocked.");
456
+ ui.blank();
457
+ ui.bullet("1. See what's been traced:");
458
+ ui.command(`${npx} status`);
459
+ ui.hint(` ...or watch it live: ${ui.inlineCommand(`${npx} watch`)}`);
460
+ ui.blank();
461
+ ui.bullet("2. Want more detail? Turn on debug logging:");
462
+ ui.command("export ORY_AGENT_DEBUG=true");
463
+ }
464
+ else {
465
+ ui.hint("It starts in watch mode: every tool call is checked, but nothing is blocked yet.");
466
+ ui.blank();
467
+ ui.bullet("1. See what Ory is doing — config, permission coverage, recent activity:");
468
+ ui.command(`${npx} status`);
469
+ ui.hint(` ...or watch the trace stream live: ${ui.inlineCommand(`${npx} watch`)}`);
470
+ ui.blank();
471
+ ui.bullet("2. Turn on enforcement once the watch-mode traces look right:");
472
+ ui.command(`${npx} permissions enforce`);
473
+ ui.hint(` denied tools are then blocked; return to watch with ${ui.inlineCommand(`${npx} permissions observe`)}`);
474
+ ui.blank();
475
+ ui.bullet("3. Want more detail? Turn on debug logging:");
476
+ ui.command("export ORY_AGENT_DEBUG=true");
477
+ }
478
+ ui.blank();
479
+ ui.hint(`To uninstall: ${ui.inlineCommand(uninstallCmd)}`);
480
+ }
481
+ function printNextStepsNow(harnessName, uninstallCmd) {
482
+ ui.heading("Next steps:");
483
+ ui.bullet("1. Set required environment variables:");
484
+ ui.command("export ORY_PROJECT_URL=https://your-project.projects.oryapis.com");
485
+ ui.command("export ORY_AGENT_API_KEY=ory_pat_...");
486
+ ui.blank();
487
+ ui.bullet("2. Turn on the interactive user login (opt-in). Either persist it:");
488
+ ui.command("configure --user-login --oauth2-client-id <public OAuth2 client id>");
489
+ ui.hint(" ...or set it per-session via env vars:");
490
+ ui.command("export ORY_USER_LOGIN=true");
491
+ ui.command("export ORY_OAUTH2_CLIENT_ID=<public OAuth2 client id>");
492
+ ui.hint(` Without these, ${harnessName} runs without a human Ory identity attached`);
493
+ ui.hint(" to the session and permission checks fall back to a session:<id> subject.");
494
+ ui.hint(" The OAuth2 client id is the public client (no secret) you registered in");
495
+ ui.hint(" your Ory project with the four loopback redirect URIs.");
496
+ ui.blank();
497
+ ui.bullet("3. Optionally enable debug logging:");
498
+ ui.command("export ORY_AGENT_DEBUG=true");
499
+ ui.blank();
500
+ ui.bullet(`4. Start ${harnessName} in your project directory.`);
501
+ ui.blank();
502
+ ui.hint(`To uninstall: ${ui.inlineCommand(uninstallCmd)}`);
410
503
  }
@@ -28,7 +28,8 @@ export interface StatusCommandOptions {
28
28
  /**
29
29
  * Compose the unified `status` output for a plugin CLI. Sections render
30
30
  * in this order: title → configuration → user identity → agent identity
31
- * → permissions → hooks & plugin → environment → recent log tail.
31
+ * → permissions → hooks & plugin → environment → recent activity
32
+ * (traces + debug log).
32
33
  *
33
34
  * Each section degrades gracefully — missing project URL, audit-only
34
35
  * mode, and empty token caches all surface as inline "n/a" lines rather
@@ -21,13 +21,24 @@ const agent_auth_js_2 = require("./agent-auth.js");
21
21
  const subject_js_1 = require("./subject.js");
22
22
  const tool_catalog_js_1 = require("./tool-catalog.js");
23
23
  const cli_js_1 = require("./cli.js");
24
+ const cli_invocation_js_1 = require("./cli-invocation.js");
24
25
  function resolveNamespace() {
25
26
  return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools";
26
27
  }
27
- const USER_LOGIN_ENABLED = new Set(["1", "true", "yes", "on"]);
28
- function isUserLoginEnabled() {
29
- const v = process.env.ORY_USER_LOGIN?.toLowerCase();
30
- return !!v && USER_LOGIN_ENABLED.has(v);
28
+ /**
29
+ * Human-readable annotation for where the resolved user-login flag came
30
+ * from, so `status` readers know whether to look at the env var or the
31
+ * config file to change it.
32
+ */
33
+ function formatUserLoginSource(source) {
34
+ switch (source) {
35
+ case "env":
36
+ return " (ORY_USER_LOGIN env var)";
37
+ case "config":
38
+ return " (saved in config; change with: configure --user-login|--no-user-login)";
39
+ case "default":
40
+ return "";
41
+ }
31
42
  }
32
43
  /**
33
44
  * Format a relative duration like "4h 32m" or "12s" for a future deadline.
@@ -53,11 +64,15 @@ function formatExpiresIn(expiresAtSec, nowMs = Date.now()) {
53
64
  * when a PKCE token is cached, the resolved subject and expiry.
54
65
  */
55
66
  function printUserIdentitySection() {
56
- const loginOn = isUserLoginEnabled();
67
+ const resolved = (0, config_js_1.resolveConfig)();
68
+ const loginOn = resolved.userLogin;
57
69
  const tokens = (0, auth_store_js_1.loadTokens)();
58
70
  console.log("");
59
71
  console.log("User identity (interactive PKCE login):");
60
- console.log(` Login: ${loginOn ? "on (ORY_USER_LOGIN)" : "off (set ORY_USER_LOGIN=true to enable)"}`);
72
+ const loginState = loginOn
73
+ ? `on${formatUserLoginSource(resolved.userLoginSource)}`
74
+ : "off (enable with: configure --user-login, or ORY_USER_LOGIN=true)";
75
+ console.log(` Login: ${loginState}`);
61
76
  if (!tokens) {
62
77
  console.log(" Token cache: empty");
63
78
  if (loginOn) {
@@ -233,10 +248,10 @@ async function printPermissionsSection(binName, harness) {
233
248
  : `partial (${allowed}/${total} built-in tools allowed)`;
234
249
  console.log(` Coverage: ${status}`);
235
250
  if (denied > 0) {
236
- console.log(` run "npx ${binName} permissions bootstrap" to grant the missing tools`);
251
+ console.log(` run "${(0, cli_invocation_js_1.oryNpx)(binName)} permissions bootstrap" to grant the missing tools`);
237
252
  }
238
253
  else if (errored > 0) {
239
- console.log(` ${errored} probe error(s) — run "npx ${binName} permissions status" for details`);
254
+ console.log(` ${errored} probe error(s) — run "${(0, cli_invocation_js_1.oryNpx)(binName)} permissions status" for details`);
240
255
  }
241
256
  }
242
257
  function isUserTokenUsable() {
@@ -256,7 +271,8 @@ function formatModeSuffix(source) {
256
271
  /**
257
272
  * Compose the unified `status` output for a plugin CLI. Sections render
258
273
  * in this order: title → configuration → user identity → agent identity
259
- * → permissions → hooks & plugin → environment → recent log tail.
274
+ * → permissions → hooks & plugin → environment → recent activity
275
+ * (traces + debug log).
260
276
  *
261
277
  * Each section degrades gracefully — missing project URL, audit-only
262
278
  * mode, and empty token caches all surface as inline "n/a" lines rather
@@ -276,10 +292,15 @@ async function runStatusCommand(binName, harness, options) {
276
292
  options.printPluginSection();
277
293
  }
278
294
  (0, cli_js_1.printEnvironment)();
279
- (0, cli_js_1.printLogTail)();
295
+ console.log("");
296
+ console.log("Recent activity:");
297
+ (0, cli_js_1.printTraceTail)(harness);
298
+ (0, cli_js_1.printLogTail)(harness);
299
+ console.log("");
300
+ console.log(` Watch traces live: ${(0, cli_invocation_js_1.oryNpx)(binName)} watch`);
280
301
  console.log("");
281
302
  console.log(`Drill into any section with:`);
282
- console.log(` npx ${binName} agent status`);
283
- console.log(` npx ${binName} permissions status`);
284
- console.log(` npx ${binName} configure`);
303
+ console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} agent status`);
304
+ console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} permissions status`);
305
+ console.log(` ${(0, cli_invocation_js_1.oryNpx)(binName)} configure`);
285
306
  }
package/dist/subject.d.ts CHANGED
@@ -12,9 +12,11 @@
12
12
  * `session:<id>`). Backward-compatible default when no subject
13
13
  * namespace is configured.
14
14
  *
15
- * The selection is driven by `ORY_USER_SUBJECT_NAMESPACE`: when set, the
16
- * resolver builds a SubjectSet using that namespace; when unset, it
17
- * falls back to the direct SubjectID chain.
15
+ * The selection is driven by the user-subject namespace: `ORY_USER_SUBJECT_NAMESPACE`
16
+ * takes precedence, then the client's configured `userSubjectNamespace` (persisted
17
+ * in the shared config, e.g. by the local-stack install path). When a namespace is
18
+ * present the resolver builds a SubjectSet using it; otherwise it falls back to the
19
+ * direct SubjectID chain.
18
20
  */
19
21
  import type { OryAgentClient } from "./client.js";
20
22
  export type UserSubjectRef = {
@@ -44,8 +46,9 @@ export declare function runWithUserSubject<T>(subject: string | undefined, fn: (
44
46
  * legacy `ORY_AGENT_SUBJECT_ID`, then the caller-supplied `fallback`
45
47
  * (typically `session:<id>`).
46
48
  *
47
- * Returns a SubjectSet when `ORY_USER_SUBJECT_NAMESPACE` is set and a
48
- * concrete subject is available; otherwise a direct SubjectID.
49
+ * Returns a SubjectSet when a user-subject namespace is configured (env or
50
+ * client config) and a concrete subject is available; otherwise a direct
51
+ * SubjectID.
49
52
  */
50
53
  export declare function resolveUserSubject(client: OryAgentClient, fallback?: string): UserSubjectRef;
51
54
  /**
package/dist/subject.js CHANGED
@@ -13,9 +13,11 @@
13
13
  * `session:<id>`). Backward-compatible default when no subject
14
14
  * namespace is configured.
15
15
  *
16
- * The selection is driven by `ORY_USER_SUBJECT_NAMESPACE`: when set, the
17
- * resolver builds a SubjectSet using that namespace; when unset, it
18
- * falls back to the direct SubjectID chain.
16
+ * The selection is driven by the user-subject namespace: `ORY_USER_SUBJECT_NAMESPACE`
17
+ * takes precedence, then the client's configured `userSubjectNamespace` (persisted
18
+ * in the shared config, e.g. by the local-stack install path). When a namespace is
19
+ * present the resolver builds a SubjectSet using it; otherwise it falls back to the
20
+ * direct SubjectID chain.
19
21
  */
20
22
  Object.defineProperty(exports, "__esModule", { value: true });
21
23
  exports.runWithUserSubject = runWithUserSubject;
@@ -52,8 +54,9 @@ function runWithUserSubject(subject, fn) {
52
54
  * legacy `ORY_AGENT_SUBJECT_ID`, then the caller-supplied `fallback`
53
55
  * (typically `session:<id>`).
54
56
  *
55
- * Returns a SubjectSet when `ORY_USER_SUBJECT_NAMESPACE` is set and a
56
- * concrete subject is available; otherwise a direct SubjectID.
57
+ * Returns a SubjectSet when a user-subject namespace is configured (env or
58
+ * client config) and a concrete subject is available; otherwise a direct
59
+ * SubjectID.
57
60
  */
58
61
  function resolveUserSubject(client, fallback) {
59
62
  const subject = perCallUserSubject.getStore()
@@ -61,7 +64,7 @@ function resolveUserSubject(client, fallback) {
61
64
  ?? process.env.ORY_USER_SUBJECT_ID
62
65
  ?? process.env.ORY_AGENT_SUBJECT_ID
63
66
  ?? fallback;
64
- const ns = process.env.ORY_USER_SUBJECT_NAMESPACE;
67
+ const ns = process.env.ORY_USER_SUBJECT_NAMESPACE?.trim() || client.userSubjectNamespace;
65
68
  if (subject && ns) {
66
69
  return {
67
70
  subjectSet: { namespace: ns, object: subject, relation: "" },
@@ -17,9 +17,9 @@
17
17
  export declare const HARNESS_TOOL_CATALOG: {
18
18
  readonly "claude-code": readonly ["Read", "Write", "Edit", "Bash", "Glob", "Grep", "WebFetch", "WebSearch", "NotebookEdit", "TodoWrite", "Task"];
19
19
  readonly "claude-agent-sdk": readonly ["Read", "Write", "Edit", "Bash", "Glob", "Grep", "WebFetch", "WebSearch", "NotebookEdit", "TodoWrite", "Task"];
20
- readonly codex: readonly ["shell", "apply_patch", "read_file"];
20
+ readonly codex: readonly ["Bash", "apply_patch"];
21
21
  readonly "gemini-cli": readonly ["read_file", "write_file", "edit_file", "list_files", "search_files", "shell", "web_search"];
22
- readonly openclaw: readonly ["execute_command", "read_file", "write_file", "list_directory", "search_files", "browser"];
22
+ readonly openclaw: readonly ["exec", "read", "write", "edit", "apply_patch", "web_search", "web_fetch", "browser"];
23
23
  readonly opencode: readonly ["read", "write", "edit", "bash", "glob", "grep", "webfetch"];
24
24
  readonly continue: readonly ["Bash", "Read", "Edit", "Write", "Grep", "Glob", "WebSearch"];
25
25
  readonly goose: readonly ["shell", "text_editor", "read_file", "write_file", "list_windows", "screen_capture"];
@@ -50,7 +50,11 @@ exports.HARNESS_TOOL_CATALOG = {
50
50
  "TodoWrite",
51
51
  "Task",
52
52
  ],
53
- codex: ["shell", "apply_patch", "read_file"],
53
+ // Codex serializes its hook `tool_name` as `Bash` (shell) and `apply_patch`
54
+ // (file edits — `Write`/`Edit` are matcher aliases only); MCP tools arrive as
55
+ // `mcp__<server>__<tool>` and aren't enumerable here. Verified against
56
+ // codex 0.143 (see packages/codex/src/types.ts).
57
+ codex: ["Bash", "apply_patch"],
54
58
  "gemini-cli": [
55
59
  "read_file",
56
60
  "write_file",
@@ -60,12 +64,16 @@ exports.HARNESS_TOOL_CATALOG = {
60
64
  "shell",
61
65
  "web_search",
62
66
  ],
67
+ // OpenClaw's built-in tool ids (docs.openclaw.ai/tools): shell is `exec`,
68
+ // file ops are `read`/`write`/`edit`/`apply_patch`, plus web + browser.
63
69
  openclaw: [
64
- "execute_command",
65
- "read_file",
66
- "write_file",
67
- "list_directory",
68
- "search_files",
70
+ "exec",
71
+ "read",
72
+ "write",
73
+ "edit",
74
+ "apply_patch",
75
+ "web_search",
76
+ "web_fetch",
69
77
  "browser",
70
78
  ],
71
79
  opencode: ["read", "write", "edit", "bash", "glob", "grep", "webfetch"],