@ory/argus 0.13.0 → 0.13.2
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/build-info.json +4 -4
- package/dist/interactive-setup.js +158 -68
- package/dist/version-cli.d.ts +7 -0
- package/dist/version-cli.js +7 -1
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"repo": "ory-agent-plugins",
|
|
3
|
-
"commit": "
|
|
4
|
-
"commitShort": "
|
|
3
|
+
"commit": "4961e2084b50f7c37a1c44c93735998394afe12d",
|
|
4
|
+
"commitShort": "4961e20",
|
|
5
5
|
"branch": "main",
|
|
6
|
-
"commitDate": "2026-07-
|
|
6
|
+
"commitDate": "2026-07-14T13:13:40-07:00",
|
|
7
7
|
"dirty": false,
|
|
8
|
-
"builtAt": "2026-07-
|
|
8
|
+
"builtAt": "2026-07-14T20:17:46.098Z"
|
|
9
9
|
}
|
|
@@ -83,6 +83,7 @@ const tool_catalog_js_1 = require("./tool-catalog.js");
|
|
|
83
83
|
const index_js_1 = require("./local/index.js");
|
|
84
84
|
const subject_js_1 = require("./subject.js");
|
|
85
85
|
const permissions_cli_js_1 = require("./permissions-cli.js");
|
|
86
|
+
const version_cli_js_1 = require("./version-cli.js");
|
|
86
87
|
const ui = __importStar(require("./ui.js"));
|
|
87
88
|
/** Loopback redirect URIs the PKCE user login expects on the OAuth2 client. */
|
|
88
89
|
exports.LOOPBACK_REDIRECT_URIS = auth_js_1.LOOPBACK_PORTS.map((port) => `http://127.0.0.1:${port}/callback`);
|
|
@@ -155,6 +156,17 @@ async function installOryCli() {
|
|
|
155
156
|
* open-coded as a local `postInstallPermissions` helper.
|
|
156
157
|
*/
|
|
157
158
|
async function runPostInstall(binName, harness, args = [], deps = {}) {
|
|
159
|
+
// Surface the running CLI version up front. `npx -p @ory/<harness>` (no
|
|
160
|
+
// version pin) will happily reuse a previously-cached install rather than
|
|
161
|
+
// re-resolve to latest, so a user can silently be running an old CLI whose
|
|
162
|
+
// install flow predates newer behavior (e.g. the interactive setup wizard)
|
|
163
|
+
// while believing they're on the current release. Printing the version makes
|
|
164
|
+
// that mismatch visible at a glance; the README's troubleshooting note
|
|
165
|
+
// explains how to clear a stale npx cache.
|
|
166
|
+
const cliVersion = (0, version_cli_js_1.resolveCoreVersion)();
|
|
167
|
+
if (cliVersion !== "unknown") {
|
|
168
|
+
ui.info(`${binName} v${cliVersion}`);
|
|
169
|
+
}
|
|
158
170
|
const result = await runInteractiveSetup(binName, harness, args, deps);
|
|
159
171
|
// The Ory Network wizard path bootstraps permissions itself (via the
|
|
160
172
|
// admin-authenticated `ory` CLI). Only fall back to the DCR-token bootstrap
|
|
@@ -638,15 +650,20 @@ async function completeFullSetup(binName, harness, ctx) {
|
|
|
638
650
|
* The tuples reference the permission namespace (default `AgentTools`), which
|
|
639
651
|
* must be defined in the project's permission model or the write fails with
|
|
640
652
|
* `NotFound`. So we inspect the model first (see {@link inspectPermissionModel})
|
|
641
|
-
* and act on what's actually there
|
|
653
|
+
* and act on what's actually there. We provision in every case *except* when
|
|
654
|
+
* the namespace already exists:
|
|
642
655
|
*
|
|
656
|
+
* - **Namespace already defined** → grant directly, touch nothing.
|
|
643
657
|
* - **No model yet** (a freshly created Ory Network project) → offer to
|
|
644
658
|
* provision a minimal `<namespace>` model (`ory update opl`), then grant.
|
|
645
|
-
* - **
|
|
646
|
-
*
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
*
|
|
659
|
+
* - **Model exists (OPL source fetchable) but lacks `<namespace>`** → offer
|
|
660
|
+
* to *merge* the namespace into the existing OPL and re-upload it, leaving
|
|
661
|
+
* the other namespaces untouched, then grant. Ory Network serves the OPL
|
|
662
|
+
* source at a `location` URL in the permission config, which makes this
|
|
663
|
+
* non-destructive merge possible.
|
|
664
|
+
* - **Model provably exists but its source couldn't be read** → we can't
|
|
665
|
+
* merge and `ory update opl` overwrites the whole OPL, so we refuse to
|
|
666
|
+
* clobber it: print the exact snippet to add and skip the grant.
|
|
650
667
|
*
|
|
651
668
|
* Best-effort throughout: any failure prints a short, actionable note rather
|
|
652
669
|
* than a wall of errors. Observe mode (the install default) keeps tools working
|
|
@@ -658,10 +675,36 @@ async function bootstrapPermissionsViaOry(runner, projectId, harness, subject, p
|
|
|
658
675
|
if (tools.length === 0)
|
|
659
676
|
return;
|
|
660
677
|
// Ensure the namespace the tuples reference exists, or the write NotFounds.
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
678
|
+
// We only skip provisioning when the namespace is already defined; every
|
|
679
|
+
// other state provisions. When we can fetch the existing OPL source we merge
|
|
680
|
+
// our namespace into it (non-destructive); otherwise we create a fresh
|
|
681
|
+
// minimal model. The one case we refuse is a model that provably exists but
|
|
682
|
+
// whose source we couldn't read — overwriting it would clobber it.
|
|
683
|
+
const model = await inspectPermissionModel(runner, projectId, namespace);
|
|
684
|
+
if (model.kind === "merge") {
|
|
685
|
+
// A model exists and we have its OPL source — append our namespace without
|
|
686
|
+
// disturbing the others (prompted).
|
|
687
|
+
ui.heading(`This project has a permission model, but it doesn't define '${namespace}'.`);
|
|
688
|
+
ui.hint("It can be added to the existing model without changing the other namespaces.");
|
|
689
|
+
ui.blank();
|
|
690
|
+
const answer = await prompt(ui.promptLine(`Add '${namespace}' to this project's permission model now?`, {
|
|
691
|
+
hint: "[Y/n]",
|
|
692
|
+
}));
|
|
693
|
+
if (isNo(answer)) {
|
|
694
|
+
printPermissionModelHelp(namespace);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
if (!provisionPermissionModel(runner, projectId, namespace, model.opl)) {
|
|
698
|
+
ui.warning(`Could not add '${namespace}' to the permission model automatically.`);
|
|
699
|
+
printPermissionModelHelp(namespace);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
ui.success(`Added '${namespace}' to the permission model.`);
|
|
703
|
+
}
|
|
704
|
+
else if (model.kind === "empty" || model.kind === "unknown") {
|
|
705
|
+
// No model (or one we couldn't read at all) — provision a minimal one
|
|
706
|
+
// (prompted). Reading the config failing is treated as "nothing there".
|
|
707
|
+
ui.heading(`This project has no '${namespace}' permission model yet —`);
|
|
665
708
|
ui.hint("without it, permission checks can't be granted or enforced.");
|
|
666
709
|
ui.blank();
|
|
667
710
|
const answer = await prompt(ui.promptLine(`Create a minimal '${namespace}' permission model on this project now?`, { hint: "[Y/n]" }));
|
|
@@ -676,16 +719,15 @@ async function bootstrapPermissionsViaOry(runner, projectId, harness, subject, p
|
|
|
676
719
|
}
|
|
677
720
|
ui.success(`Created the '${namespace}' permission model.`);
|
|
678
721
|
}
|
|
679
|
-
else if (
|
|
680
|
-
// A model exists but
|
|
681
|
-
//
|
|
682
|
-
ui.heading(`This project has a permission model, but it doesn't define '${namespace}'
|
|
683
|
-
ui.hint("
|
|
722
|
+
else if (model.kind === "blocked") {
|
|
723
|
+
// A model provably exists but we couldn't fetch its source, so we can't
|
|
724
|
+
// merge and `ory update opl` would overwrite it. Guide instead of clobber.
|
|
725
|
+
ui.heading(`This project has a permission model, but it doesn't define '${namespace}'`);
|
|
726
|
+
ui.hint("and its source couldn't be read, so adding it automatically might overwrite it.");
|
|
684
727
|
printPermissionModelHelp(namespace);
|
|
685
728
|
return;
|
|
686
729
|
}
|
|
687
|
-
// "present" (namespace already defined)
|
|
688
|
-
// model) fall through and attempt the grant best-effort.
|
|
730
|
+
// "present" (namespace already defined) falls through and grants directly.
|
|
689
731
|
const subjectPatch = "subjectSet" in subject
|
|
690
732
|
? {
|
|
691
733
|
subject_set: {
|
|
@@ -715,26 +757,13 @@ async function bootstrapPermissionsViaOry(runner, projectId, harness, subject, p
|
|
|
715
757
|
printPermissionModelHelp(namespace);
|
|
716
758
|
}
|
|
717
759
|
/**
|
|
718
|
-
* Inspect the project's permission model
|
|
719
|
-
*
|
|
720
|
-
*
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
* Network project reports this as an empty list, a `null`, or
|
|
724
|
-
* an absent `namespaces` key — all of which mean "nothing
|
|
725
|
-
* defined yet", so provisioning a minimal model is safe.
|
|
726
|
-
* - `missing` — a model exists and defines *other* namespaces but not this
|
|
727
|
-
* one. `ory update opl` overwrites the whole OPL, so we must
|
|
728
|
-
* not auto-provision (it would clobber the existing model).
|
|
729
|
-
* - `unknown` — the config couldn't be read/parsed; make no assumptions and
|
|
730
|
-
* attempt the grant best-effort.
|
|
731
|
-
*
|
|
732
|
-
* The earlier version only recognized the exact `namespaces: []` shape as
|
|
733
|
-
* "empty", so a fresh project reporting `null`/absent namespaces (or one that
|
|
734
|
-
* already had unrelated namespaces) silently skipped provisioning and the grant
|
|
735
|
-
* then failed with `NotFound`.
|
|
760
|
+
* Inspect the project's permission model. Ory Network reports the config in one
|
|
761
|
+
* of two shapes: `{ namespaces: [] }` (or `null`) for a project with no model,
|
|
762
|
+
* and `{ namespaces: { location: "https://….txt" } }` for an OPL-configured
|
|
763
|
+
* one — where the `.txt` is the full OPL *source*. We fetch that source so a
|
|
764
|
+
* missing namespace can be merged in rather than clobbering the whole model.
|
|
736
765
|
*/
|
|
737
|
-
function inspectPermissionModel(runner, projectId, namespace) {
|
|
766
|
+
async function inspectPermissionModel(runner, projectId, namespace) {
|
|
738
767
|
const r = runner.exec([
|
|
739
768
|
"get",
|
|
740
769
|
"permission-config",
|
|
@@ -744,47 +773,75 @@ function inspectPermissionModel(runner, projectId, namespace) {
|
|
|
744
773
|
"json",
|
|
745
774
|
]);
|
|
746
775
|
if (r.status !== 0)
|
|
747
|
-
return "unknown";
|
|
776
|
+
return { kind: "unknown" };
|
|
748
777
|
const cfg = safeJsonParse(r.stdout);
|
|
749
778
|
if (!cfg || typeof cfg !== "object")
|
|
750
|
-
return "unknown";
|
|
779
|
+
return { kind: "unknown" };
|
|
751
780
|
const ns = cfg.namespaces;
|
|
752
|
-
// A fresh project reports no model as null / an absent key
|
|
781
|
+
// A fresh project reports no model as null / an absent key.
|
|
753
782
|
if (ns == null)
|
|
754
|
-
return "empty";
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
if (ns
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
783
|
+
return { kind: "empty" };
|
|
784
|
+
// OPL-configured form: { namespaces: { location: "https://….txt" } }.
|
|
785
|
+
// The .txt holds the full OPL source, so we can merge into it.
|
|
786
|
+
if (!Array.isArray(ns) && typeof ns === "object") {
|
|
787
|
+
const location = ns.location;
|
|
788
|
+
if (typeof location !== "string" || !location)
|
|
789
|
+
return { kind: "empty" };
|
|
790
|
+
const opl = await fetchOplSource(location);
|
|
791
|
+
// A model exists but its source is unreadable — can't merge, mustn't clobber.
|
|
792
|
+
if (opl == null)
|
|
793
|
+
return { kind: "blocked" };
|
|
794
|
+
return oplDefinesClass(opl, namespace)
|
|
795
|
+
? { kind: "present" }
|
|
796
|
+
: { kind: "merge", opl };
|
|
797
|
+
}
|
|
798
|
+
// Inline compiled form: { namespaces: [{ name, id }, …] } (legacy / self-hosted).
|
|
799
|
+
// We only have names, not source, so a missing namespace can't be merged.
|
|
800
|
+
if (Array.isArray(ns)) {
|
|
801
|
+
if (ns.length === 0)
|
|
802
|
+
return { kind: "empty" };
|
|
803
|
+
const names = ns
|
|
804
|
+
.map((n) => n && typeof n === "object"
|
|
805
|
+
? n.name
|
|
806
|
+
: undefined)
|
|
807
|
+
.filter((n) => typeof n === "string");
|
|
808
|
+
return names.includes(namespace) ? { kind: "present" } : { kind: "blocked" };
|
|
809
|
+
}
|
|
810
|
+
return { kind: "unknown" };
|
|
811
|
+
}
|
|
812
|
+
/** Fetch the OPL source served at a permission-config `location` URL. */
|
|
813
|
+
async function fetchOplSource(url) {
|
|
814
|
+
try {
|
|
815
|
+
const res = await fetch(url);
|
|
816
|
+
if (!res.ok)
|
|
817
|
+
return null;
|
|
818
|
+
return await res.text();
|
|
819
|
+
}
|
|
820
|
+
catch {
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
/** Whether an OPL source defines `class <name>`. */
|
|
825
|
+
function oplDefinesClass(opl, name) {
|
|
826
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
827
|
+
return new RegExp(`\\bclass\\s+${escaped}\\b`).test(opl);
|
|
765
828
|
}
|
|
766
829
|
/**
|
|
767
|
-
* Provision a
|
|
768
|
-
*
|
|
769
|
-
*
|
|
770
|
-
*
|
|
771
|
-
*
|
|
830
|
+
* Provision a permission model that defines `<namespace>` with a `use`
|
|
831
|
+
* relation, via `ory update opl`. The namespace becomes an OPL class, so it
|
|
832
|
+
* must be a valid identifier; if not, we bail (caller guides instead).
|
|
833
|
+
*
|
|
834
|
+
* When `existingOpl` is given, the namespace is appended to that source
|
|
835
|
+
* (preserving the model's other namespaces); otherwise a fresh minimal model
|
|
836
|
+
* is generated. Writes the OPL to a temp file because `ory update opl` reads
|
|
837
|
+
* `--file` only (no stdin). Returns true on success.
|
|
772
838
|
*/
|
|
773
|
-
function provisionPermissionModel(runner, projectId, namespace) {
|
|
839
|
+
function provisionPermissionModel(runner, projectId, namespace, existingOpl) {
|
|
774
840
|
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(namespace))
|
|
775
841
|
return false;
|
|
776
|
-
const opl =
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
"class User implements Namespace {}",
|
|
780
|
-
"",
|
|
781
|
-
`class ${namespace} implements Namespace {`,
|
|
782
|
-
" related: {",
|
|
783
|
-
" use: User[]",
|
|
784
|
-
" }",
|
|
785
|
-
"}",
|
|
786
|
-
"",
|
|
787
|
-
].join("\n");
|
|
842
|
+
const opl = existingOpl !== undefined
|
|
843
|
+
? mergeNamespaceIntoOpl(existingOpl, namespace)
|
|
844
|
+
: freshOpl(namespace);
|
|
788
845
|
let dir;
|
|
789
846
|
try {
|
|
790
847
|
dir = fs.mkdtempSync(path.join(os.tmpdir(), "ory-opl-"));
|
|
@@ -820,6 +877,39 @@ function provisionPermissionModel(runner, projectId, namespace) {
|
|
|
820
877
|
}
|
|
821
878
|
}
|
|
822
879
|
}
|
|
880
|
+
/** A minimal standalone OPL defining `User` and `<namespace>` with `use`. */
|
|
881
|
+
function freshOpl(namespace) {
|
|
882
|
+
return [
|
|
883
|
+
'import { Namespace } from "@ory/permission-namespace-types"',
|
|
884
|
+
"",
|
|
885
|
+
"class User implements Namespace {}",
|
|
886
|
+
"",
|
|
887
|
+
`class ${namespace} implements Namespace {`,
|
|
888
|
+
" related: {",
|
|
889
|
+
" use: User[]",
|
|
890
|
+
" }",
|
|
891
|
+
"}",
|
|
892
|
+
"",
|
|
893
|
+
].join("\n");
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Append a `<namespace>` class (with a `use: User[]` relation) to an existing
|
|
897
|
+
* OPL source, leaving every other namespace untouched. Adds a `User` class too
|
|
898
|
+
* when the source doesn't already define one, since `use` is typed `User[]`.
|
|
899
|
+
*/
|
|
900
|
+
function mergeNamespaceIntoOpl(existingOpl, namespace) {
|
|
901
|
+
let opl = existingOpl.replace(/\s*$/, "\n");
|
|
902
|
+
if (!oplDefinesClass(opl, "User")) {
|
|
903
|
+
opl += "\nclass User implements Namespace {}\n";
|
|
904
|
+
}
|
|
905
|
+
opl +=
|
|
906
|
+
`\nclass ${namespace} implements Namespace {\n` +
|
|
907
|
+
" related: {\n" +
|
|
908
|
+
" use: User[]\n" +
|
|
909
|
+
" }\n" +
|
|
910
|
+
"}\n";
|
|
911
|
+
return opl;
|
|
912
|
+
}
|
|
823
913
|
function printPermissionModelHelp(namespace) {
|
|
824
914
|
// When the namespace is a valid OPL class name, show the exact snippet to
|
|
825
915
|
// paste into the model — that's the concrete "how" behind "define it". The
|
package/dist/version-cli.d.ts
CHANGED
|
@@ -26,6 +26,13 @@ export interface BuildInfo {
|
|
|
26
26
|
* `unknown`.
|
|
27
27
|
*/
|
|
28
28
|
export declare function resolveBuildInfo(): BuildInfo;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the `@ory/argus` core version from this module's own package.json.
|
|
31
|
+
* Exported so the install flow can surface the running CLI version — every
|
|
32
|
+
* `@ory/*` package releases in lockstep, so the core version is the release
|
|
33
|
+
* version of whatever plugin CLI is executing.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveCoreVersion(): string;
|
|
29
36
|
export interface VersionInfo {
|
|
30
37
|
plugin: {
|
|
31
38
|
name: string;
|
package/dist/version-cli.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.resolveBuildInfo = resolveBuildInfo;
|
|
37
|
+
exports.resolveCoreVersion = resolveCoreVersion;
|
|
37
38
|
exports.collectVersionInfo = collectVersionInfo;
|
|
38
39
|
exports.runVersionCommand = runVersionCommand;
|
|
39
40
|
/**
|
|
@@ -158,7 +159,12 @@ function readPackageMeta(packageRoot) {
|
|
|
158
159
|
return { name: "unknown", version: "0.0.0" };
|
|
159
160
|
}
|
|
160
161
|
}
|
|
161
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* Resolve the `@ory/argus` core version from this module's own package.json.
|
|
164
|
+
* Exported so the install flow can surface the running CLI version — every
|
|
165
|
+
* `@ory/*` package releases in lockstep, so the core version is the release
|
|
166
|
+
* version of whatever plugin CLI is executing.
|
|
167
|
+
*/
|
|
162
168
|
function resolveCoreVersion() {
|
|
163
169
|
let dir = __dirname;
|
|
164
170
|
for (let i = 0; i < 5; i++) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/argus",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
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",
|