@remixmate/cli 0.9.72 → 0.9.74
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 +45 -21
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/argv.js +1 -51
- package/dist/auth/auth-lock.js +1 -100
- package/dist/auth/auto-login.js +7 -75
- package/dist/auth/commands.js +40 -290
- package/dist/auth/credential-store.js +1 -143
- package/dist/auth/device-flow-runner.js +13 -110
- package/dist/auth/device-flow.js +1 -132
- package/dist/auth/ensure.js +1 -62
- package/dist/auth/environment.js +1 -69
- package/dist/auth/pending-store.js +1 -45
- package/dist/auth/resolve.js +1 -44
- package/dist/billing.js +3 -76
- package/dist/capabilities.js +1 -75
- package/dist/cli.js +39 -211
- package/dist/diagnostics.js +2 -70
- package/dist/doctor.js +9 -188
- package/dist/errors.js +1 -33
- package/dist/exec.js +4 -47
- package/dist/handlers/gen-digital-human.js +33 -264
- package/dist/handlers/gen-image.js +9 -113
- package/dist/handlers/gen-post.js +15 -192
- package/dist/handlers/gen-video.js +7 -185
- package/dist/handlers/gen-voice.js +19 -133
- package/dist/handlers/index.js +1 -20
- package/dist/handlers/shared.js +1 -66
- package/dist/handlers/template-init.js +1 -21
- package/dist/http.js +3 -155
- package/dist/index.js +1 -8
- package/dist/install.js +40 -448
- package/dist/manifest.json +44 -3
- package/dist/progress.js +2 -15
- package/dist/project/commands.js +34 -156
- package/dist/project/host.js +1 -151
- package/dist/project/resolve.js +1 -65
- package/dist/project/store.js +2 -97
- package/dist/project/take.js +1 -106
- package/dist/project/trace.js +5 -278
- package/dist/registry.js +3 -70
- package/dist/runner.js +7 -225
- package/dist/skill-schema.js +1 -194
- package/dist/template/args.js +1 -69
- package/dist/template/check.js +2 -30
- package/dist/template/commands.js +34 -163
- package/dist/template/constants.js +1 -46
- package/dist/template/init.js +5 -133
- package/dist/template/list.js +1 -46
- package/dist/template/preview.js +7 -318
- package/dist/template/proc.js +1 -43
- package/dist/template/project.js +6 -161
- package/dist/template/publish.js +5 -185
- package/dist/template/render-client.js +2 -79
- package/dist/template/upgrade.js +4 -123
- package/dist/text.js +1 -15
- package/package.json +2 -35
- package/skills/doc-parse/scripts/extract_via_render.py +32 -32
- package/skills/export-jianying/scripts/gen_jianying_draft.py +188 -188
- package/skills/gen-script/SKILL.md +23 -1
- package/skills/gen-script/scripts/gen_script.py +644 -551
- package/skills/gen-script/skill.json +12 -0
- package/skills/gen-script/version.json +1 -1
- package/skills/prepare-video-assets/scripts/prepare_video_assets.py +21 -21
- package/skills/render-video/scripts/_chrome_vendor.py +17 -17
- package/skills/render-video/scripts/_digital_human_segments.py +34 -34
- package/skills/render-video/scripts/_video_probe.py +33 -33
- package/skills/render-video/scripts/_vod_polling.py +13 -13
- package/skills/render-video/scripts/remote_renderer_client.py +42 -42
- package/skills/render-video/scripts/render_video.py +639 -639
- package/skills/render-video/scripts/required_props.py +37 -37
- package/skills/render-video/scripts/upload_video.py +35 -35
- package/skills/render-video/version.json +1 -1
- package/skills/template-registry/SKILL.md +16 -0
- package/skills/template-registry/scripts/countdown_spec.py +92 -92
- package/skills/template-registry/scripts/doc_extract.py +40 -40
- package/skills/template-registry/scripts/list_templates.py +303 -191
- package/skills/template-registry/scripts/log_diagnostics.py +1 -1
- package/skills/template-registry/scripts/match_template.py +87 -87
- package/skills/template-registry/scripts/payload_hydrator.py +89 -89
- package/skills/template-registry/scripts/registry_loader.py +159 -159
- package/skills/template-registry/scripts/render_job_client.py +75 -75
- package/skills/template-registry/scripts/showcase_loader.py +44 -44
- package/skills/template-registry/skill.json +4 -1
- package/skills/template-registry/version.json +1 -1
- package/skills/template-registry/video_dsl/runtime/__init__.py +8 -8
- package/skills/template-registry/video_dsl/runtime/dsl_validator.py +329 -329
- package/skills/template-registry/video_dsl/runtime/narration_budget.py +36 -36
- package/skills/template-registry/video_dsl/runtime/payload_contract.py +7 -7
- package/skills/template-registry/video_dsl/runtime/prompt_enhancer.py +98 -98
- package/skills/template-registry/video_dsl/runtime/timeline_compiler.py +127 -127
- package/skills/template-registry/video_dsl/schema/video-dsl-v1alpha1.json +14 -1
- package/skills/video-parser/scripts/analyze_video.py +173 -173
- package/skills/video-parser/scripts/deconstruct_video.py +91 -91
- package/skills/video-parser/scripts/parse_via_render.py +21 -21
- package/skills/web-screenshot/scripts/_media_screenshot/__init__.py +4 -4
- package/skills/web-screenshot/scripts/_media_screenshot/bootstrap.py +7 -7
- package/skills/web-screenshot/scripts/_media_screenshot/browser.py +4 -4
- package/skills/web-screenshot/scripts/_media_screenshot/cli_args.py +4 -4
- package/skills/web-screenshot/scripts/_media_screenshot/js_loader.py +2 -2
- package/skills/web-screenshot/scripts/_media_screenshot/overlay.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/reader.py +40 -40
- package/skills/web-screenshot/scripts/_media_screenshot/recording.py +11 -11
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/__init__.py +8 -8
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/arrow.py +2 -2
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/caption.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/click.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/code_lines.py +5 -5
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/focus.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/highlight.py +3 -3
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/hover.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/redact.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/scroll.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/sequence.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/title_card.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/type.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/wait.py +1 -1
- package/skills/web-screenshot/scripts/_media_screenshot/scenes/zoom.py +2 -2
- package/skills/web-screenshot/scripts/_media_screenshot/screenshot.py +15 -15
- package/skills/web-screenshot/scripts/_media_screenshot/storyboard.py +2 -2
- package/skills/web-screenshot/scripts/_media_screenshot/template.py +11 -11
- package/skills/web-screenshot/scripts/_media_screenshot/trim.py +10 -10
- package/skills/web-screenshot/scripts/_media_screenshot/urlguard.py +22 -22
- package/skills/web-screenshot/scripts/_media_upload.py +33 -33
- package/skills/web-screenshot/scripts/read_page.py +15 -15
- package/skills/web-screenshot/scripts/record.py +26 -26
- package/skills/web-screenshot/scripts/screenshot.py +5 -5
- package/dist/argv.d.ts +0 -15
- package/dist/auth/auth-lock.d.ts +0 -26
- package/dist/auth/auto-login.d.ts +0 -20
- package/dist/auth/commands.d.ts +0 -8
- package/dist/auth/credential-store.d.ts +0 -60
- package/dist/auth/device-flow-runner.d.ts +0 -58
- package/dist/auth/device-flow.d.ts +0 -52
- package/dist/auth/ensure.d.ts +0 -55
- package/dist/auth/environment.d.ts +0 -38
- package/dist/auth/pending-store.d.ts +0 -33
- package/dist/auth/resolve.d.ts +0 -30
- package/dist/billing.d.ts +0 -44
- package/dist/capabilities.d.ts +0 -63
- package/dist/cli.d.ts +0 -13
- package/dist/diagnostics.d.ts +0 -24
- package/dist/doctor.d.ts +0 -22
- package/dist/errors.d.ts +0 -29
- package/dist/exec.d.ts +0 -18
- package/dist/handlers/gen-digital-human.d.ts +0 -17
- package/dist/handlers/gen-image.d.ts +0 -20
- package/dist/handlers/gen-post.d.ts +0 -22
- package/dist/handlers/gen-video.d.ts +0 -19
- package/dist/handlers/gen-voice.d.ts +0 -26
- package/dist/handlers/index.d.ts +0 -20
- package/dist/handlers/shared.d.ts +0 -26
- package/dist/handlers/template-init.d.ts +0 -10
- package/dist/http.d.ts +0 -72
- package/dist/index.d.ts +0 -9
- package/dist/install.d.ts +0 -71
- package/dist/progress.d.ts +0 -23
- package/dist/project/commands.d.ts +0 -19
- package/dist/project/host.d.ts +0 -91
- package/dist/project/resolve.d.ts +0 -43
- package/dist/project/store.d.ts +0 -31
- package/dist/project/take.d.ts +0 -50
- package/dist/project/trace.d.ts +0 -98
- package/dist/registry.d.ts +0 -30
- package/dist/runner.d.ts +0 -39
- package/dist/skill-schema.d.ts +0 -138
- package/dist/template/args.d.ts +0 -25
- package/dist/template/check.d.ts +0 -23
- package/dist/template/commands.d.ts +0 -25
- package/dist/template/constants.d.ts +0 -39
- package/dist/template/init.d.ts +0 -28
- package/dist/template/list.d.ts +0 -23
- package/dist/template/preview.d.ts +0 -60
- package/dist/template/proc.d.ts +0 -26
- package/dist/template/project.d.ts +0 -47
- package/dist/template/publish.d.ts +0 -43
- package/dist/template/render-client.d.ts +0 -39
- package/dist/template/upgrade.d.ts +0 -44
- package/dist/text.d.ts +0 -8
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
37
|
|
|
38
38
|
from __future__ import annotations
|
|
39
39
|
|
|
@@ -63,54 +63,54 @@ __all__ = [
|
|
|
63
63
|
"EXIT_BACKEND_UNREACHABLE",
|
|
64
64
|
]
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
|
|
67
|
+
|
|
68
68
|
EXIT_NOT_AUTHENTICATED = 4
|
|
69
69
|
EXIT_BACKEND_UNREACHABLE = 5
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
class RegistryAuthError(RuntimeError):
|
|
73
|
-
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
74
77
|
|
|
75
|
-
这两种情况以前被压成同一段「请确认 ab-api 可达…并已设置有效 PRIV_TOKEN」的
|
|
76
|
-
文案,用户无从判断该去登录还是该等后端恢复。
|
|
77
|
-
"""
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
class RegistryUnreachableError(RuntimeError):
|
|
81
|
-
|
|
81
|
+
pass
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
class TemplateStatusGatedError(RuntimeError):
|
|
85
|
-
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
86
91
|
|
|
87
|
-
与「id 不存在」分开的独立失败模式:前者要改 id,后者要开门控开关
|
|
88
|
-
(``ENABLE_BETA_TEMPLATES=1``)或把模板转正。压成同一个 ``None`` 返回值的后果见
|
|
89
|
-
``require_visible_template`` 的文档。
|
|
90
|
-
"""
|
|
91
92
|
|
|
92
|
-
# 默认缓存 TTL(秒)。HTTP 拉取后该时间内不再重新请求。
|
|
93
93
|
_DEFAULT_TTL_SECONDS = 300
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
_DEFAULT_HTTP_TIMEOUT = 5
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
_CACHE_DIR = os.environ.get(
|
|
100
100
|
"VIDEO_TEMPLATE_REGISTRY_CACHE_DIR",
|
|
101
101
|
os.path.join(tempfile.gettempdir(), "ab-template-registry"),
|
|
102
102
|
)
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
_DEFAULT_API_BASE_URL = "https://api.remixmate.ai/api"
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
def api_base_url() -> str:
|
|
110
|
-
"""ab-api base(不带尾斜杠)。registry 与其它挂在 ab-api 上的只读接口共用这一份解析。
|
|
111
110
|
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
114
|
return (
|
|
115
115
|
os.environ.get("MM_API_BASE_URL", "").strip()
|
|
116
116
|
or os.environ.get("MM_BACKEND_API_URL", "").strip()
|
|
@@ -119,28 +119,28 @@ def api_base_url() -> str:
|
|
|
119
119
|
|
|
120
120
|
|
|
121
121
|
def _default_registry_url() -> str:
|
|
122
|
-
"""未显式配置 VIDEO_TEMPLATE_REGISTRY_URL 时,推导默认 ab-api registry endpoint。
|
|
123
122
|
|
|
124
|
-
默认值内置在 CLI 自身(而非依赖 ab-agent 等宿主在 spawn 时注入),这样
|
|
125
|
-
``remixmate-cli`` 独立安装(codex / ``npm i -g``)也能开箱即用。
|
|
126
123
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
133
|
return f"{api_base_url()}/remotionTemplate/registry"
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
def _cache_path_for(url: str) -> str:
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
digest = hashlib.sha256(url.encode("utf-8")).hexdigest()[:16]
|
|
139
139
|
return os.path.join(_CACHE_DIR, f"registry-{digest}.json")
|
|
140
140
|
|
|
141
141
|
|
|
142
142
|
def _read_cached(cache_file: str) -> Optional[dict]:
|
|
143
|
-
|
|
143
|
+
|
|
144
144
|
if not os.path.exists(cache_file):
|
|
145
145
|
return None
|
|
146
146
|
try:
|
|
@@ -151,7 +151,7 @@ def _read_cached(cache_file: str) -> Optional[dict]:
|
|
|
151
151
|
|
|
152
152
|
|
|
153
153
|
def _write_cached(cache_file: str, payload: dict) -> None:
|
|
154
|
-
|
|
154
|
+
|
|
155
155
|
os.makedirs(os.path.dirname(cache_file), exist_ok=True)
|
|
156
156
|
tmp_fd, tmp_path = tempfile.mkstemp(dir=os.path.dirname(cache_file))
|
|
157
157
|
try:
|
|
@@ -166,31 +166,31 @@ def _write_cached(cache_file: str, payload: dict) -> None:
|
|
|
166
166
|
raise
|
|
167
167
|
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
178
|
|
|
179
179
|
|
|
180
180
|
def _resolve_priv_token(_url: str) -> str:
|
|
181
|
-
|
|
181
|
+
|
|
182
182
|
return os.environ.get("PRIV_TOKEN", "").strip()
|
|
183
183
|
|
|
184
184
|
|
|
185
185
|
def _fetch_http(url: str, timeout: int) -> dict:
|
|
186
|
-
"""执行 HTTP 请求,自动加 X-Priv-Token / If-None-Match 头。
|
|
187
186
|
|
|
188
|
-
支持两种 endpoint 形态:
|
|
189
|
-
- 老形态(@ab-templates/metadata 静态文件):GET 返回纯 registry JSON
|
|
190
|
-
- ab-api 形态:POST 返回 {code, msg, data: <registry>} 包装
|
|
191
187
|
|
|
192
|
-
|
|
193
|
-
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
194
|
cache_file = _cache_path_for(url)
|
|
195
195
|
cached = _read_cached(cache_file)
|
|
196
196
|
|
|
@@ -213,11 +213,11 @@ def _fetch_http(url: str, timeout: int) -> dict:
|
|
|
213
213
|
etag = resp.headers.get("ETag", "")
|
|
214
214
|
parsed = json.loads(raw)
|
|
215
215
|
|
|
216
|
-
|
|
216
|
+
|
|
217
217
|
if isinstance(parsed, dict) and "data" in parsed and "code" in parsed:
|
|
218
218
|
code = parsed.get("code")
|
|
219
219
|
if str(code) in ("401", "403"):
|
|
220
|
-
|
|
220
|
+
|
|
221
221
|
raise RegistryAuthError(str(parsed.get("msg") or f"code={code}"))
|
|
222
222
|
if code not in (0, "0"):
|
|
223
223
|
raise RuntimeError(
|
|
@@ -233,7 +233,7 @@ def _fetch_http(url: str, timeout: int) -> dict:
|
|
|
233
233
|
)
|
|
234
234
|
return data
|
|
235
235
|
except urllib.error.HTTPError as e:
|
|
236
|
-
|
|
236
|
+
|
|
237
237
|
if e.code == 304 and cached:
|
|
238
238
|
cached["fetchedAt"] = time.time()
|
|
239
239
|
_write_cached(cache_file, cached)
|
|
@@ -244,9 +244,9 @@ def _fetch_http(url: str, timeout: int) -> dict:
|
|
|
244
244
|
|
|
245
245
|
|
|
246
246
|
def _load_from_url(url: str, ttl: int, timeout: int) -> dict:
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
250
|
cache_file = _cache_path_for(url)
|
|
251
251
|
cached = _read_cached(cache_file)
|
|
252
252
|
now = time.time()
|
|
@@ -257,14 +257,14 @@ def _load_from_url(url: str, ttl: int, timeout: int) -> dict:
|
|
|
257
257
|
try:
|
|
258
258
|
return _fetch_http(url, timeout)
|
|
259
259
|
except RegistryAuthError as exc:
|
|
260
|
-
|
|
261
|
-
|
|
260
|
+
|
|
261
|
+
|
|
262
262
|
raise RegistryAuthError(
|
|
263
263
|
f"凭证无效或已过期({exc})。运行 remixmate login 重新授权,"
|
|
264
264
|
f"或设置有效的 PRIV_TOKEN。当前后端:{url}"
|
|
265
265
|
) from exc
|
|
266
266
|
except Exception as exc:
|
|
267
|
-
|
|
267
|
+
|
|
268
268
|
if cached:
|
|
269
269
|
print(
|
|
270
270
|
f"⚠️ registry HTTP 拉取失败,降级使用本地缓存: {exc}",
|
|
@@ -282,14 +282,14 @@ def load_registry_data(
|
|
|
282
282
|
ttl_seconds: int = _DEFAULT_TTL_SECONDS,
|
|
283
283
|
http_timeout: int = _DEFAULT_HTTP_TIMEOUT,
|
|
284
284
|
) -> dict:
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
293
|
debug = os.environ.get("REMIXMATE_DEBUG", "").strip() == "1"
|
|
294
294
|
url = os.environ.get("VIDEO_TEMPLATE_REGISTRY_URL", "").strip()
|
|
295
295
|
if url:
|
|
@@ -307,43 +307,43 @@ def load_registry_data(
|
|
|
307
307
|
|
|
308
308
|
|
|
309
309
|
def registry_page_urls() -> dict[str, str]:
|
|
310
|
-
"""Document-level ``pageUrls`` (templateId → web detail page), built by ab-api.
|
|
311
310
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
|
|
317
317
|
try:
|
|
318
318
|
raw = load_registry_data().get("pageUrls")
|
|
319
|
-
except Exception:
|
|
319
|
+
except Exception:
|
|
320
320
|
return {}
|
|
321
321
|
if not isinstance(raw, dict):
|
|
322
322
|
return {}
|
|
323
323
|
return {k: v for k, v in raw.items() if isinstance(k, str) and isinstance(v, str) and v}
|
|
324
324
|
|
|
325
325
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
|
|
340
340
|
|
|
341
341
|
_DEFAULT_STATUS = "stable"
|
|
342
342
|
_BETA_GATE_ENV = "ENABLE_BETA_TEMPLATES"
|
|
343
343
|
|
|
344
344
|
|
|
345
345
|
def _truthy(value: str) -> bool:
|
|
346
|
-
|
|
346
|
+
|
|
347
347
|
return value.strip().lower() in ("1", "true", "yes", "on")
|
|
348
348
|
|
|
349
349
|
|
|
@@ -359,12 +359,12 @@ def _resolve_status(tpl: dict) -> str:
|
|
|
359
359
|
|
|
360
360
|
|
|
361
361
|
def _allowed_statuses() -> set[str]:
|
|
362
|
-
"""Which template statuses the current process is allowed to see.
|
|
363
362
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
|
|
368
368
|
allowed = {"stable"}
|
|
369
369
|
if _truthy_env(_BETA_GATE_ENV):
|
|
370
370
|
allowed.add("beta")
|
|
@@ -373,12 +373,12 @@ def _allowed_statuses() -> set[str]:
|
|
|
373
373
|
|
|
374
374
|
@lru_cache(maxsize=4)
|
|
375
375
|
def _indexed_view(allowed_statuses_key: Optional[tuple[str, ...]]) -> dict[str, dict]:
|
|
376
|
-
"""Build (and memoize) ``{templateId: tpl}`` keyed by the allowed-status set.
|
|
377
376
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
|
|
382
382
|
raw = load_registry_data()
|
|
383
383
|
allow_filter = allowed_statuses_key is not None
|
|
384
384
|
allowed = set(allowed_statuses_key) if allowed_statuses_key else set()
|
|
@@ -399,36 +399,36 @@ def load_registry_indexed(
|
|
|
399
399
|
http_timeout: int = _DEFAULT_HTTP_TIMEOUT,
|
|
400
400
|
include_all_statuses: bool = False,
|
|
401
401
|
) -> dict[str, dict]:
|
|
402
|
-
"""Return registry as ``{templateId: template_dict}``.
|
|
403
402
|
|
|
404
|
-
Filters by the process-wide status gate unless ``include_all_statuses``
|
|
405
|
-
is set (e.g. render-video reading an already-chosen template). Templates
|
|
406
|
-
missing ``status`` are treated as ``stable`` for backwards compatibility.
|
|
407
403
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
412
|
if include_all_statuses:
|
|
413
413
|
return _indexed_view(None)
|
|
414
414
|
return _indexed_view(tuple(sorted(_allowed_statuses())))
|
|
415
415
|
|
|
416
416
|
|
|
417
417
|
def get_template(template_id: str, *, include_all_statuses: bool = False) -> Optional[dict]:
|
|
418
|
-
|
|
418
|
+
|
|
419
419
|
if not template_id:
|
|
420
420
|
return None
|
|
421
421
|
return load_registry_indexed(include_all_statuses=include_all_statuses).get(template_id)
|
|
422
422
|
|
|
423
423
|
|
|
424
424
|
def gated_status(template_id: str) -> Optional[str]:
|
|
425
|
-
"""模板存在、但被状态门控挡在可见集合外时返回它的真实 status;否则 None。
|
|
426
425
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
|
|
432
432
|
if not template_id:
|
|
433
433
|
return None
|
|
434
434
|
if get_template(template_id) is not None:
|
|
@@ -438,16 +438,16 @@ def gated_status(template_id: str) -> Optional[str]:
|
|
|
438
438
|
|
|
439
439
|
|
|
440
440
|
def require_visible_template(template_id: str) -> Optional[dict]:
|
|
441
|
-
"""与 ``get_template`` 相同,但模板**被状态门控挡住**时抛异常而不是返回 None。
|
|
442
441
|
|
|
443
|
-
为什么需要它:``get_template`` 返回 None 之后,调用方普遍的写法是「拿不到就用
|
|
444
|
-
默认值继续」—— 对"这个 id 不存在"是合理的容错,对"模板在线但本进程看不到"却是
|
|
445
|
-
灾难性的静默降级:模板的 llmHint / customPayloadSchema / capabilities 全部蒸发,
|
|
446
|
-
脚本按通用骨架生成、payload 契约整段跳过,最后烧掉渲染积分换一条废片,而日志里
|
|
447
|
-
一个字都没有。
|
|
448
442
|
|
|
449
|
-
|
|
450
|
-
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
|
|
451
451
|
tpl = get_template(template_id)
|
|
452
452
|
if tpl is not None:
|
|
453
453
|
return tpl
|
|
@@ -462,14 +462,14 @@ def require_visible_template(template_id: str) -> Optional[dict]:
|
|
|
462
462
|
|
|
463
463
|
|
|
464
464
|
def list_templates(*, include_all_statuses: bool = False) -> list[dict]:
|
|
465
|
-
|
|
465
|
+
|
|
466
466
|
return list(load_registry_indexed(include_all_statuses=include_all_statuses).values())
|
|
467
467
|
|
|
468
468
|
|
|
469
469
|
def invalidate_cache() -> None:
|
|
470
|
-
"""Drop the indexed-view memoization. Mostly for tests / hot reload.
|
|
471
470
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
|
|
475
475
|
_indexed_view.cache_clear()
|