@smithers-orchestrator/gateway 0.16.9
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/LICENSE +21 -0
- package/openapi.yaml +6849 -0
- package/package.json +39 -0
- package/src/auth/scopes.ts +88 -0
- package/src/index.ts +2 -0
- package/src/rpc/index.ts +622 -0
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@smithers-orchestrator/gateway",
|
|
3
|
+
"version": "0.16.9",
|
|
4
|
+
"description": "Stable Smithers Gateway RPC contracts, auth scopes, and deployment metadata",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./src/index.ts",
|
|
10
|
+
"import": "./src/index.ts",
|
|
11
|
+
"default": "./src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"./rpc": {
|
|
14
|
+
"types": "./src/rpc/index.ts",
|
|
15
|
+
"import": "./src/rpc/index.ts",
|
|
16
|
+
"default": "./src/rpc/index.ts"
|
|
17
|
+
},
|
|
18
|
+
"./auth/scopes": {
|
|
19
|
+
"types": "./src/auth/scopes.ts",
|
|
20
|
+
"import": "./src/auth/scopes.ts",
|
|
21
|
+
"default": "./src/auth/scopes.ts"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"src/",
|
|
26
|
+
"openapi.yaml"
|
|
27
|
+
],
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/bun": "latest",
|
|
30
|
+
"typescript": "~5.9.3"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"generate:openapi": "bun run scripts/generate-openapi.ts",
|
|
34
|
+
"check:openapi": "bun run scripts/generate-openapi.ts --check",
|
|
35
|
+
"build": "bun run check:openapi && tsc -p tsconfig.json --noEmit",
|
|
36
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
37
|
+
"test": "bun test tests"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export const GATEWAY_SCOPE_VALUES = [
|
|
2
|
+
"run:read",
|
|
3
|
+
"run:write",
|
|
4
|
+
"run:admin",
|
|
5
|
+
"approval:submit",
|
|
6
|
+
"signal:submit",
|
|
7
|
+
"cron:read",
|
|
8
|
+
"cron:write",
|
|
9
|
+
"observability:read",
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
export type GatewayScope = (typeof GATEWAY_SCOPE_VALUES)[number];
|
|
13
|
+
|
|
14
|
+
export const GATEWAY_SCOPE_DESCRIPTIONS: Record<GatewayScope, string> = {
|
|
15
|
+
"run:read": "Read run state, summaries, event streams, node output, and node diffs.",
|
|
16
|
+
"run:write": "Launch, resume, and cancel runs.",
|
|
17
|
+
"run:admin": "Perform elevated run control such as hijack and rewind.",
|
|
18
|
+
"approval:submit": "Submit approval decisions.",
|
|
19
|
+
"signal:submit": "Submit workflow signals.",
|
|
20
|
+
"cron:read": "List cron schedules.",
|
|
21
|
+
"cron:write": "Create, delete, and trigger cron schedules.",
|
|
22
|
+
"observability:read": "Read DevTools and other observability streams.",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const RUN_SCOPE_ORDER: GatewayScope[] = ["run:read", "run:write", "run:admin"];
|
|
26
|
+
const CRON_SCOPE_ORDER: GatewayScope[] = ["cron:read", "cron:write"];
|
|
27
|
+
|
|
28
|
+
function normalizeScope(scope: string): string {
|
|
29
|
+
return scope.trim();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isGatewayScope(scope: string): scope is GatewayScope {
|
|
33
|
+
return (GATEWAY_SCOPE_VALUES as readonly string[]).includes(scope);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function gatewayScopeImplies(granted: GatewayScope, required: GatewayScope): boolean {
|
|
37
|
+
if (granted === required) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (granted.startsWith("run:") && required.startsWith("run:")) {
|
|
41
|
+
return RUN_SCOPE_ORDER.indexOf(granted) >= RUN_SCOPE_ORDER.indexOf(required);
|
|
42
|
+
}
|
|
43
|
+
if (granted.startsWith("cron:") && required.startsWith("cron:")) {
|
|
44
|
+
return CRON_SCOPE_ORDER.indexOf(granted) >= CRON_SCOPE_ORDER.indexOf(required);
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function legacyAccessImplies(scope: string, required: GatewayScope): boolean {
|
|
50
|
+
switch (scope) {
|
|
51
|
+
case "read":
|
|
52
|
+
return required === "run:read" || required === "cron:read" || required === "observability:read";
|
|
53
|
+
case "execute":
|
|
54
|
+
return required === "run:read" || required === "run:write" || required === "signal:submit" || required === "cron:read" || required === "cron:write";
|
|
55
|
+
case "approve":
|
|
56
|
+
return required === "approval:submit" || legacyAccessImplies("execute", required);
|
|
57
|
+
case "admin":
|
|
58
|
+
return true;
|
|
59
|
+
default:
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function hasGatewayScope(
|
|
65
|
+
grantedScopes: readonly string[],
|
|
66
|
+
requiredScope: GatewayScope,
|
|
67
|
+
methodName?: string,
|
|
68
|
+
): boolean {
|
|
69
|
+
const normalized = grantedScopes.map(normalizeScope).filter(Boolean);
|
|
70
|
+
if (normalized.includes("*")) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
for (const granted of normalized) {
|
|
74
|
+
if (methodName && granted === methodName) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
if (methodName && granted.endsWith(".*") && methodName.startsWith(granted.slice(0, -1))) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
if (isGatewayScope(granted) && gatewayScopeImplies(granted, requiredScope)) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (legacyAccessImplies(granted, requiredScope)) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
package/src/index.ts
ADDED