@datalayer/agent-runtimes 1.5.35 → 1.5.37
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-embed/datalayer-app-main.js +69 -69
- package/lib/apps/apps/sessionRules.d.ts +81 -0
- package/lib/apps/apps/sessionRules.js +122 -0
- package/lib/apps/plugins/app-rules/AppRulesCard.d.ts +14 -1
- package/lib/apps/plugins/app-rules/AppRulesCard.js +56 -5
- package/lib/apps/plugins/app-rules/index.d.ts +2 -1
- package/lib/apps/plugins/app-rules/index.js +4 -3
- package/lib/specs/policies.d.ts +16 -0
- package/lib/specs/policies.js +228 -0
- package/lib/types/agentspecs.d.ts +43 -0
- package/package.json +1 -1
- package/scripts/codegen/generate_agents.py +1 -0
- package/scripts/codegen/generate_policies.py +209 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
# Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Generate Python and TypeScript code from the YAML policy specifications.
|
|
7
|
+
|
|
8
|
+
One YAML per policy under ``agentspecs/policies`` (SHIELD.md SH-50, §6.7):
|
|
9
|
+
the Shield's policy document under the catalogue's fields. The document's
|
|
10
|
+
schema is the ``agent-shield`` package's, carried by agentspecs verbatim
|
|
11
|
+
(``policies/shield-policy.schema.json``); each document is validated against
|
|
12
|
+
it here before it is written out.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
python generate_policies.py \\
|
|
16
|
+
--specs-dir agentspecs/agentspecs/policies \\
|
|
17
|
+
--python-output agent_runtimes/specs/policies.py \\
|
|
18
|
+
--typescript-output src/specs/policies.ts
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import argparse
|
|
22
|
+
import json
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
import jsonschema
|
|
27
|
+
import yaml
|
|
28
|
+
from versioning import ensure_spec_version, version_suffix
|
|
29
|
+
|
|
30
|
+
SHIELD_SCHEMA = "shield-policy.schema.json"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def load_policy_specs(specs_dir: Path) -> list[dict[str, Any]]:
|
|
34
|
+
"""Every policy spec of a directory, each named for its id, its document valid."""
|
|
35
|
+
validator = jsonschema.Draft202012Validator(
|
|
36
|
+
json.loads((specs_dir / SHIELD_SCHEMA).read_text())
|
|
37
|
+
)
|
|
38
|
+
specs = []
|
|
39
|
+
for yaml_file in sorted(specs_dir.glob("*.yaml")):
|
|
40
|
+
spec = yaml.safe_load(yaml_file.read_text()) or {}
|
|
41
|
+
ensure_spec_version(spec)
|
|
42
|
+
if spec["id"] != yaml_file.stem:
|
|
43
|
+
raise ValueError(
|
|
44
|
+
f"{yaml_file.name}: the file is named for {yaml_file.stem!r}, the spec says {spec['id']!r}"
|
|
45
|
+
)
|
|
46
|
+
problems = sorted(
|
|
47
|
+
validator.iter_errors(spec.get("policy")),
|
|
48
|
+
key=lambda e: list(e.absolute_path),
|
|
49
|
+
)
|
|
50
|
+
if problems:
|
|
51
|
+
where = (
|
|
52
|
+
".".join(str(part) for part in problems[0].absolute_path)
|
|
53
|
+
or "the document"
|
|
54
|
+
)
|
|
55
|
+
raise ValueError(
|
|
56
|
+
f"{yaml_file.name}: policy: {where}: {problems[0].message}"
|
|
57
|
+
)
|
|
58
|
+
specs.append(spec)
|
|
59
|
+
return specs
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _const(spec: dict[str, Any]) -> str:
|
|
63
|
+
return (
|
|
64
|
+
spec["id"].replace("-", "_").upper()
|
|
65
|
+
+ "_POLICY"
|
|
66
|
+
+ version_suffix(spec["version"])
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _s(value: Any) -> str:
|
|
71
|
+
return json.dumps(" ".join(str(value or "").split()), ensure_ascii=False)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
75
|
+
lines = [
|
|
76
|
+
"# Copyright (c) 2025-2026 Datalayer, Inc.",
|
|
77
|
+
"# Distributed under the terms of the Modified BSD License.",
|
|
78
|
+
'"""',
|
|
79
|
+
"Policy Catalog.",
|
|
80
|
+
"",
|
|
81
|
+
"The Shield's policy documents the platform ships or shows (SHIELD.md SH-50):",
|
|
82
|
+
"each a catalogue entry whose ``policy`` is a document of ``agent_shield.policy``.",
|
|
83
|
+
"",
|
|
84
|
+
"This file is AUTO-GENERATED from YAML specifications.",
|
|
85
|
+
"DO NOT EDIT MANUALLY - run 'make specs' to regenerate.",
|
|
86
|
+
'"""',
|
|
87
|
+
"",
|
|
88
|
+
"from typing import Any, Dict, List, Optional",
|
|
89
|
+
"",
|
|
90
|
+
"from agent_runtimes.types import PolicySpec",
|
|
91
|
+
]
|
|
92
|
+
for spec in specs:
|
|
93
|
+
lines += [
|
|
94
|
+
"",
|
|
95
|
+
f"{_const(spec)} = PolicySpec(",
|
|
96
|
+
f" id={_s(spec['id'])},",
|
|
97
|
+
f" version={_s(spec['version'])},",
|
|
98
|
+
f" name={_s(spec['name'])},",
|
|
99
|
+
f" description={_s(spec.get('description'))},",
|
|
100
|
+
f" enabled={bool(spec.get('enabled', True))},",
|
|
101
|
+
f" tags={json.dumps(list(spec.get('tags') or []))},",
|
|
102
|
+
f" policy={json.dumps(spec['policy'], ensure_ascii=False, indent=8).replace('true', 'True').replace('false', 'False').replace('null', 'None')},",
|
|
103
|
+
")",
|
|
104
|
+
]
|
|
105
|
+
lines += [
|
|
106
|
+
"",
|
|
107
|
+
"",
|
|
108
|
+
"POLICY_CATALOGUE: Dict[str, PolicySpec] = {",
|
|
109
|
+
*[f" {_s(spec['id'])}: {_const(spec)}," for spec in specs],
|
|
110
|
+
"}",
|
|
111
|
+
"",
|
|
112
|
+
"",
|
|
113
|
+
"def get_policy(policy_id: str) -> Optional[PolicySpec]:",
|
|
114
|
+
' """A policy by id, or None."""',
|
|
115
|
+
" return POLICY_CATALOGUE.get(policy_id)",
|
|
116
|
+
"",
|
|
117
|
+
"",
|
|
118
|
+
"def list_policies() -> List[PolicySpec]:",
|
|
119
|
+
' """Every policy of the catalogue."""',
|
|
120
|
+
" return list(POLICY_CATALOGUE.values())",
|
|
121
|
+
"",
|
|
122
|
+
"",
|
|
123
|
+
"def policy_documents() -> Dict[str, Dict[str, Any]]:",
|
|
124
|
+
' """Each policy\'s Shield document, by id."""',
|
|
125
|
+
" return {key: dict(spec.policy) for key, spec in POLICY_CATALOGUE.items()}",
|
|
126
|
+
"",
|
|
127
|
+
]
|
|
128
|
+
return "\n".join(lines)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _t(value: Any) -> str:
|
|
132
|
+
return json.dumps(" ".join(str(value or "").split()), ensure_ascii=False)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _ts_value(value: Any, indent: int = 2) -> str:
|
|
136
|
+
text = json.dumps(value, ensure_ascii=False, indent=2)
|
|
137
|
+
pad = " " * indent
|
|
138
|
+
return "\n".join(
|
|
139
|
+
pad + line if i else line for i, line in enumerate(text.splitlines())
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
144
|
+
lines = [
|
|
145
|
+
"/*",
|
|
146
|
+
" * Copyright (c) 2025-2026 Datalayer, Inc.",
|
|
147
|
+
" * Distributed under the terms of the Modified BSD License.",
|
|
148
|
+
" */",
|
|
149
|
+
"",
|
|
150
|
+
"/**",
|
|
151
|
+
" * Policy Catalog.",
|
|
152
|
+
" *",
|
|
153
|
+
" * The Shield's policy documents the platform ships or shows (SHIELD.md SH-50).",
|
|
154
|
+
" *",
|
|
155
|
+
" * This file is AUTO-GENERATED from YAML specifications.",
|
|
156
|
+
" * DO NOT EDIT MANUALLY - run 'make specs' to regenerate.",
|
|
157
|
+
" */",
|
|
158
|
+
"",
|
|
159
|
+
"import type { PolicySpec } from '../types/agentspecs';",
|
|
160
|
+
]
|
|
161
|
+
for spec in specs:
|
|
162
|
+
lines += [
|
|
163
|
+
"",
|
|
164
|
+
f"export const {_const(spec)}: PolicySpec = {{",
|
|
165
|
+
f" id: {_t(spec['id'])},",
|
|
166
|
+
f" version: {_t(spec['version'])},",
|
|
167
|
+
f" name: {_t(spec['name'])},",
|
|
168
|
+
f" description: {_t(spec.get('description'))},",
|
|
169
|
+
f" enabled: {'true' if spec.get('enabled', True) else 'false'},",
|
|
170
|
+
f" tags: {json.dumps(list(spec.get('tags') or []))},",
|
|
171
|
+
f" policy: {_ts_value(spec['policy'])},",
|
|
172
|
+
"};",
|
|
173
|
+
]
|
|
174
|
+
lines += [
|
|
175
|
+
"",
|
|
176
|
+
"export const POLICY_CATALOGUE: Record<string, PolicySpec> = {",
|
|
177
|
+
*[f" {_t(spec['id'])}: {_const(spec)}," for spec in specs],
|
|
178
|
+
"};",
|
|
179
|
+
"",
|
|
180
|
+
"/** A policy by id. */",
|
|
181
|
+
"export function getPolicy(policyId: string): PolicySpec | undefined {",
|
|
182
|
+
" return POLICY_CATALOGUE[policyId];",
|
|
183
|
+
"}",
|
|
184
|
+
"",
|
|
185
|
+
"/** Every policy of the catalogue. */",
|
|
186
|
+
"export function listPolicySpecs(): PolicySpec[] {",
|
|
187
|
+
" return Object.values(POLICY_CATALOGUE);",
|
|
188
|
+
"}",
|
|
189
|
+
"",
|
|
190
|
+
]
|
|
191
|
+
return "\n".join(lines)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def main() -> None:
|
|
195
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
196
|
+
parser.add_argument("--specs-dir", type=Path, required=True)
|
|
197
|
+
parser.add_argument("--python-output", type=Path, required=True)
|
|
198
|
+
parser.add_argument("--typescript-output", type=Path, required=True)
|
|
199
|
+
args = parser.parse_args()
|
|
200
|
+
specs = load_policy_specs(args.specs_dir)
|
|
201
|
+
args.python_output.write_text(generate_python_code(specs))
|
|
202
|
+
args.typescript_output.write_text(generate_typescript_code(specs))
|
|
203
|
+
print(
|
|
204
|
+
f"Generated {len(specs)} policies: {args.python_output}, {args.typescript_output}"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
if __name__ == "__main__":
|
|
209
|
+
main()
|