@lingjingai/scriptctl 0.36.4 → 0.37.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.
Files changed (43) hide show
  1. package/changes/0.36.5.md +13 -0
  2. package/changes/0.37.0.md +17 -0
  3. package/changes/0.37.1.md +13 -0
  4. package/changes/0.37.2.md +13 -0
  5. package/dist/cli.js +2 -0
  6. package/dist/cli.js.map +1 -1
  7. package/dist/domain/ingest/assembler.js +14 -6
  8. package/dist/domain/ingest/assembler.js.map +1 -1
  9. package/dist/domain/ingest/asset-state-md-parser.d.ts +2 -0
  10. package/dist/domain/ingest/asset-state-md-parser.js +133 -0
  11. package/dist/domain/ingest/asset-state-md-parser.js.map +1 -0
  12. package/dist/domain/ingest/serial-context.d.ts +95 -0
  13. package/dist/domain/ingest/serial-context.js +219 -0
  14. package/dist/domain/ingest/serial-context.js.map +1 -0
  15. package/dist/domain/ingest/state-reconcile.d.ts +1 -0
  16. package/dist/domain/ingest/state-reconcile.js +2 -0
  17. package/dist/domain/ingest/state-reconcile.js.map +1 -1
  18. package/dist/help-text.js +1 -1
  19. package/dist/llm/config.js +9 -3
  20. package/dist/llm/config.js.map +1 -1
  21. package/dist/llm/registry.d.ts +1 -0
  22. package/dist/llm/registry.js +4 -0
  23. package/dist/llm/registry.js.map +1 -1
  24. package/dist/llm/tasks/ingest/episode-assets.d.ts +8 -0
  25. package/dist/llm/tasks/ingest/episode-assets.js +46 -0
  26. package/dist/llm/tasks/ingest/episode-assets.js.map +1 -0
  27. package/dist/llm/types.d.ts +1 -1
  28. package/dist/llm/types.js.map +1 -1
  29. package/dist/usecases/ingest/pipeline.js +40 -46
  30. package/dist/usecases/ingest/pipeline.js.map +1 -1
  31. package/dist/usecases/ingest/serial-govern.d.ts +23 -0
  32. package/dist/usecases/ingest/serial-govern.js +169 -0
  33. package/dist/usecases/ingest/serial-govern.js.map +1 -0
  34. package/dist/usecases/ingest/status.js +17 -1
  35. package/dist/usecases/ingest/status.js.map +1 -1
  36. package/dist/usecases/ingest/view-command.js +15 -0
  37. package/dist/usecases/ingest/view-command.js.map +1 -1
  38. package/dist/usecases/ingest/view-server.d.ts +16 -0
  39. package/dist/usecases/ingest/view-server.js +220 -0
  40. package/dist/usecases/ingest/view-server.js.map +1 -0
  41. package/package.json +1 -1
  42. package/skills/scriptctl/SKILL.md +1 -1
  43. package/skills/scriptctl/references/ingest-workflow.md +4 -2
@@ -0,0 +1,220 @@
1
+ // `scriptctl view --watch` — a live workspace monitor. Starts a tiny stdlib HTTP
2
+ // server (no deps) that a self-refreshing HTML dashboard polls: ingest progress
3
+ // (deriveIngestStatus), the artifact tree (pass files, per-episode serial context
4
+ // + update md, provisional + final script), and any single file's content. The
5
+ // dashboard auto-refreshes so a running `scriptctl ingest` is watched live and the
6
+ // per-episode context/decision md stream in as episodes finish. Runs until Ctrl-C.
7
+ import * as http from "node:http";
8
+ import * as fs from "node:fs";
9
+ import * as path from "node:path";
10
+ import { spawn } from "node:child_process";
11
+ import { EXIT_OK } from "../../common.js";
12
+ import { deriveIngestStatus } from "./status.js";
13
+ const strOf = (v) => (v === null || v === undefined ? "" : String(v));
14
+ // Curated artifact list for the sidebar, grouped and ordered for reviewing an
15
+ // ingest run. Only files that exist are returned.
16
+ export function listArtifacts(root) {
17
+ const meta = (rel) => {
18
+ try {
19
+ const s = fs.statSync(path.join(root, rel));
20
+ return { rel, size: s.size, mtime: s.mtimeMs };
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ };
26
+ const dirFiles = (dir, filter) => {
27
+ let names = [];
28
+ try {
29
+ names = fs.readdirSync(path.join(root, dir));
30
+ }
31
+ catch {
32
+ return [];
33
+ }
34
+ return names.filter(filter).sort().map((n) => meta(`${dir}/${n}`)).filter((m) => !!m);
35
+ };
36
+ const only = (arr) => arr.filter((m) => !!m);
37
+ return {
38
+ 结果: only([meta("script.json"), meta("validation.json"), meta("review.html")]),
39
+ 上下文: dirFiles("serial", (n) => n.startsWith("context_") && n.endsWith(".md")),
40
+ 每集更新: dirFiles("serial", (n) => n.startsWith("ep_") && n.endsWith(".md")),
41
+ 中间产物: only([meta("script.initial.json"), meta("provisional_assets.json"), meta("notes.json"), meta("manifest.json")]),
42
+ 前段: dirFiles("pass2", (n) => n.endsWith(".md")),
43
+ };
44
+ }
45
+ // Read a workspace file, guarding against path traversal outside the root.
46
+ export function readSafe(root, rel) {
47
+ const abs = path.resolve(root, rel);
48
+ if (abs !== root && !abs.startsWith(root + path.sep))
49
+ return { ok: false };
50
+ try {
51
+ return { ok: true, text: fs.readFileSync(abs, "utf-8") };
52
+ }
53
+ catch {
54
+ return { ok: false };
55
+ }
56
+ }
57
+ function send(res, code, type, body) {
58
+ res.writeHead(code, { "Content-Type": type, "Cache-Control": "no-store" });
59
+ res.end(body);
60
+ }
61
+ export async function runViewServer(opts) {
62
+ const root = path.resolve(strOf(opts["workspace_path"] || "workspace"));
63
+ const port = Number(opts["port"]) || 0; // 0 → OS-assigned ephemeral (no conflicts)
64
+ const server = http.createServer((req, res) => {
65
+ const u = new URL(req.url ?? "/", "http://localhost");
66
+ if (u.pathname === "/")
67
+ return send(res, 200, "text/html; charset=utf-8", DASHBOARD_HTML);
68
+ if (u.pathname === "/api/state") {
69
+ const status = deriveIngestStatus(root);
70
+ return send(res, 200, "application/json", JSON.stringify({ root, status, groups: listArtifacts(root) }));
71
+ }
72
+ if (u.pathname === "/api/file") {
73
+ const rel = u.searchParams.get("path") ?? "";
74
+ const r = readSafe(root, rel);
75
+ return r.ok ? send(res, 200, "text/plain; charset=utf-8", r.text) : send(res, 404, "text/plain", "not found");
76
+ }
77
+ send(res, 404, "text/plain", "not found");
78
+ });
79
+ const actualPort = await new Promise((resolve, reject) => {
80
+ server.once("error", reject);
81
+ server.listen(port, "127.0.0.1", () => resolve(server.address().port));
82
+ });
83
+ const url = `http://127.0.0.1:${actualPort}/`;
84
+ process.stdout.write(`ingest 监听面板: ${url}\n工作区: ${root}\nCtrl-C 退出。\n`);
85
+ if (process.platform === "darwin") {
86
+ try {
87
+ spawn("open", [url], { detached: true, stdio: "ignore" }).unref();
88
+ }
89
+ catch { /* best-effort */ }
90
+ }
91
+ // Block until interrupted; resolve cleanly so the CLI exits 0 on Ctrl-C.
92
+ await new Promise((resolve) => { process.once("SIGINT", () => { server.close(); resolve(); }); process.once("SIGTERM", () => { server.close(); resolve(); }); });
93
+ return [{ result: [url] }, EXIT_OK];
94
+ }
95
+ const DASHBOARD_HTML = `<!doctype html><html lang="zh"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
96
+ <title>scriptctl ingest 监听</title>
97
+ <style>
98
+ :root{--bg:#f6f7f9;--panel:#ffffff;--panel2:#f0f2f5;--line:#e3e6ea;--fg:#1f2430;--dim:#6b7280;--acc:#2563eb;--ok:#16a34a;--warn:#d97706;--err:#dc2626}
99
+ *{box-sizing:border-box}body{margin:0;font:13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;background:var(--bg);color:var(--fg);height:100vh;display:grid;grid-template-rows:auto auto 1fr}
100
+ header{padding:12px 16px;background:var(--panel);border-bottom:1px solid var(--line);display:flex;gap:16px;align-items:center;flex-wrap:wrap}
101
+ header .title{font-weight:700;font-size:15px}
102
+ .now{font-size:14px}.now b{color:var(--acc)}
103
+ .state{padding:3px 12px;border-radius:20px;font-weight:600;font-size:12px}
104
+ .state.ready{background:#e7f6ee;color:var(--ok)}.state.incomplete{background:#e8f0fe;color:var(--acc)}.state.failed{background:#fde8e8;color:var(--err)}.state.empty{background:var(--panel2);color:var(--dim)}
105
+ .dim{color:var(--dim)}.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
106
+ /* overall bar + stage stepper */
107
+ .progress{background:var(--panel);border-bottom:1px solid var(--line);padding:12px 16px}
108
+ .ovwrap{display:flex;align-items:center;gap:10px;margin-bottom:12px}
109
+ .ovtrack{flex:1;height:10px;background:var(--panel2);border-radius:6px;overflow:hidden}
110
+ .ovtrack i{display:block;height:100%;background:linear-gradient(90deg,#3b82f6,#2563eb);border-radius:6px;transition:width .4s}
111
+ .ovpct{font-weight:700;min-width:44px;text-align:right}
112
+ .stages{display:flex;align-items:stretch;gap:0}
113
+ .stage{flex:1;padding:8px 12px;border:1px solid var(--line);background:var(--panel);border-radius:8px;margin-right:8px}
114
+ .stage:last-child{margin-right:0}
115
+ .stage-h{display:flex;align-items:center;gap:7px;font-weight:600}
116
+ .stage .dot{width:9px;height:9px;border-radius:50%;background:#cbd2dc;flex:none}
117
+ .stage .cnt{margin-left:auto;font-size:12px;color:var(--dim)}
118
+ .stage .track{height:6px;background:var(--panel2);border-radius:4px;margin-top:8px;overflow:hidden}
119
+ .stage .track i{display:block;height:100%;width:0;border-radius:4px;transition:width .4s}
120
+ .stage.wait{opacity:.7}
121
+ .stage.done{border-color:#bfe6cd}.stage.done .dot{background:var(--ok)}.stage.done .track i{background:var(--ok)}.stage.done .stage-h b{color:var(--ok)}
122
+ .stage.run{border-color:#bcd3fb;box-shadow:0 0 0 2px #e8f0fe}.stage.run .dot{background:var(--acc);animation:pulse 1.2s infinite}.stage.run .track i{background:var(--acc)}
123
+ .stage.fail{border-color:#f3bcbc}.stage.fail .dot{background:var(--err)}.stage.fail .track i{background:var(--err)}
124
+ @keyframes pulse{50%{opacity:.35}}
125
+ main{display:grid;grid-template-columns:280px 1fr;min-height:0}
126
+ aside{background:var(--panel);border-right:1px solid var(--line);overflow:auto;padding:8px}
127
+ .grp{margin:8px 4px 2px;color:var(--dim);font-size:11px;font-weight:700;letter-spacing:.04em}
128
+ .f{padding:4px 8px;border-radius:6px;cursor:pointer;display:flex;justify-content:space-between;gap:8px;white-space:nowrap;overflow:hidden}
129
+ .f:hover{background:var(--panel2)}.f.sel{background:#e8f0fe;color:var(--acc);font-weight:600}
130
+ .f .sz{color:var(--dim);font-size:11px}
131
+ section{overflow:auto;background:var(--panel)}
132
+ .toolbar{position:sticky;top:0;background:var(--panel2);border-bottom:1px solid var(--line);padding:6px 12px;display:flex;gap:14px;align-items:center;font-size:12px}
133
+ label{cursor:pointer;user-select:none}.empty{color:var(--dim);padding:34px;text-align:center}
134
+ #view{padding:14px 18px;white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12.5px}
135
+ #view h1,#view h2{margin:14px 0 4px}#view h1{color:var(--acc);font-size:15px}#view h2{color:var(--warn);font-size:13px}
136
+ #view .b{color:var(--dim)}
137
+ </style></head><body>
138
+ <header>
139
+ <span class="title">scriptctl ingest</span>
140
+ <span id="stateBadge" class="state empty">—</span>
141
+ <span class="now">当前:<b id="curState">—</b></span>
142
+ <span class="dim mono" id="root" style="margin-left:auto"></span>
143
+ <span class="dim" id="tick"></span>
144
+ </header>
145
+ <div class="progress">
146
+ <div class="ovwrap"><span class="dim">总进度</span><div class="ovtrack"><i id="ovBar"></i></div><span class="ovpct" id="ovPct">0%</span></div>
147
+ <div class="stages" id="stages"></div>
148
+ </div>
149
+ <main>
150
+ <aside id="side"></aside>
151
+ <section>
152
+ <div class="toolbar">
153
+ <span id="cur" class="mono dim">未选择文件</span>
154
+ <label style="margin-left:auto"><input type="checkbox" id="follow" checked> 跟随最新集</label>
155
+ </div>
156
+ <div id="view"><div class="empty">左侧选择文件,或等待 ingest 产出…</div></div>
157
+ </section>
158
+ </main>
159
+ <script>
160
+ var NAMES={normalize:'规范化',assemble:'装配',serial:'串行解析',govern:'治理成稿',transcribe:'转写',roster:'角色合并',correct:'校正',speaker:'发声源'};
161
+ var cur=null, follow=true, lastLatest=null;
162
+ function $(s){return document.querySelector(s)}
163
+ $('#follow').onchange=function(e){follow=e.target.checked};
164
+ function esc(s){return s.replace(/[&<>]/g,function(c){return{'&':'&amp;','<':'&lt;','>':'&gt;'}[c]})}
165
+ function renderContent(rel,text){
166
+ if(rel.endsWith('.json')){try{text=JSON.stringify(JSON.parse(text),null,2)}catch(e){}$('#view').textContent=text;return}
167
+ if(rel.endsWith('.md')){
168
+ $('#view').innerHTML=text.split('\\n').map(function(l){
169
+ if(l.indexOf('## ')===0)return '<h2>'+esc(l.slice(3))+'</h2>';
170
+ if(l.indexOf('# ')===0)return '<h1>'+esc(l.slice(2))+'</h1>';
171
+ if(l.indexOf(' ')===0||l.indexOf('- ')===0)return '<span class="b">'+esc(l)+'</span>';
172
+ return esc(l)||'&nbsp;';
173
+ }).join('\\n');return}
174
+ $('#view').textContent=text;
175
+ }
176
+ function loadFile(rel){
177
+ cur=rel;$('#cur').textContent=rel;
178
+ var fs=document.querySelectorAll('.f');for(var i=0;i<fs.length;i++)fs[i].classList.toggle('sel',fs[i].dataset.rel===rel);
179
+ fetch('/api/file?path='+encodeURIComponent(rel)).then(function(r){return r.text()}).then(function(t){renderContent(rel,t)}).catch(function(){});
180
+ }
181
+ function frac(p){return p.unit&&p.unit.total?p.unit.done/p.unit.total:(p.done?1:0)}
182
+ function poll(){
183
+ fetch('/api/state').then(function(r){return r.json()}).then(function(d){
184
+ var s=d.status,passes=s.passes||[];
185
+ $('#root').textContent=d.root;
186
+ $('#stateBadge').className='state '+s.state;
187
+ $('#stateBadge').textContent={ready:'已完成',incomplete:'进行中',failed:'失败',empty:'空'}[s.state]||s.state;
188
+ // overall progress = mean of per-stage fractions
189
+ var sum=0;passes.forEach(function(p){sum+=frac(p)});
190
+ var pct=passes.length?Math.round(100*sum/passes.length):0;
191
+ $('#ovBar').style.width=pct+'%';$('#ovPct').textContent=pct+'%';
192
+ // current stage
193
+ var curP=null;for(var i=0;i<passes.length;i++){if(!passes[i].done){curP=passes[i];break}}
194
+ $('#curState').textContent=s.state==='ready'?'已完成 ✓':(curP?(NAMES[curP.name]||curP.name)+(curP.unit?' · '+curP.unit.done+'/'+curP.unit.total:''):'—');
195
+ // staged stepper with per-stage bars
196
+ $('#stages').innerHTML=passes.map(function(p){
197
+ var st=p.done?'done':(p.unit&&p.unit.failed?'fail':(p.unit&&p.unit.done>0||p===curP?'run':'wait'));
198
+ var cnt=p.unit?(p.unit.done+'/'+p.unit.total+(p.unit.failed?' ✗'+p.unit.failed:'')):(p.done?'✓':'—');
199
+ return '<div class="stage '+st+'"><div class="stage-h"><span class="dot"></span><b>'+(NAMES[p.name]||p.name)+'</b><span class="cnt">'+cnt+'</span></div><div class="track"><i style="width:'+Math.round(frac(p)*100)+'%"></i></div></div>';
200
+ }).join('');
201
+ // sidebar
202
+ var html='',latest=null;
203
+ Object.keys(d.groups).forEach(function(grp){
204
+ var files=d.groups[grp];if(!files.length)return;html+='<div class="grp">'+grp+'</div>';
205
+ files.forEach(function(f){
206
+ html+='<div class="f" data-rel="'+f.rel+'"><span>'+f.rel.split('/').pop()+'</span><span class="sz">'+(f.size>1024?(f.size/1024).toFixed(0)+'k':f.size)+'</span></div>';
207
+ if(grp==='每集更新'||grp==='上下文')latest=f.rel;
208
+ });
209
+ });
210
+ $('#side').innerHTML=html;
211
+ var els=document.querySelectorAll('.f');
212
+ for(var j=0;j<els.length;j++){(function(el){el.onclick=function(){follow=false;$('#follow').checked=false;loadFile(el.dataset.rel)};if(el.dataset.rel===cur)el.classList.add('sel')})(els[j])}
213
+ $('#tick').textContent='● '+new Date().toLocaleTimeString();
214
+ if(follow&&latest&&latest!==lastLatest){lastLatest=latest;loadFile(latest)}
215
+ else if(cur){loadFile(cur)}
216
+ }).catch(function(){$('#tick').textContent='● 离线'});
217
+ }
218
+ poll();setInterval(poll,1500);
219
+ </script></body></html>`;
220
+ //# sourceMappingURL=view-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-server.js","sourceRoot":"","sources":["../../../src/usecases/ingest/view-server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingjingai/scriptctl",
3
- "version": "0.36.4",
3
+ "version": "0.37.2",
4
4
  "description": "剧本阶段统一 CLI:直转外部素材到 script.json,以及当前最终剧本的读取、校验、原子编辑与批量 patch 精修。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,7 +2,7 @@
2
2
  name: scriptctl
3
3
  description: "剧本 script.json 的读写 + 入库 CLI。转剧本:ingest(txt/md/docx 或视频,自动分流,产出 workspace/script.json)→ view 自查 → publish 入库。读:集/场/action/资产/状态/校验/时间戳/出场统计。写:台词/类型/归属/scene-ref 场景造型/内联发声源/importance/资产。选择器批量精修(读写共享 --in/谓词)、动词脚本 do(异构批量一事务)、结构调整(插/删/移/分/合)。ingest status 从工作区真实文件报进度。沙箱里默认读写项目 DB;本地电脑默认 --script-path 直接读写本地 script.json。剧本阶段做任何剧本相关读写、以及洗稿/对标/转绘里操作两个 script.json 之前都应加载。正文由你用原子能力写入。"
4
4
  version: "0.19.0"
5
- scriptctl_version: "0.36.4"
5
+ scriptctl_version: "0.36.5"
6
6
  author: "official"
7
7
  ---
8
8
 
@@ -28,8 +28,8 @@ scriptctl ingest --source-path uploads/正片/
28
28
  - `--force`:**同源**重跑,清掉产物重来(**不能换源**)。
29
29
 
30
30
  管线(了解即可,产物都在 `workspace/`):
31
- - **文本**:pass1 切块 → pass2 归一化(廉价模型出 markdown,确定性解析,**逐块 checkpoint**)→ pass3 合并 → 组装 `script.initial.json` pass5 govern(资产 recall/refine 合并/canonical/裁剪)→ `script.json`(v3 校验)。
32
- - **视频**:pass1 Gemini 转录 → pass2 解析 → pass3 跨集花名册归并 + 状态归并(**贵,`roster.json`/`context.merged.md` 会复用**)→ pass4 二次画面校正 → pass5 纯剧情发声源审计 → pass6 apply + 汇入同一 govern 收尾。时间戳挂在 `extend.timestamp`。
31
+ - **文本**:pass1 切块 → pass2 归一化(廉价模型出 markdown,确定性解析,**逐块 checkpoint**)→ pass3 合并 → 组装 `body.json`(正文骨架;台词/动作**逐字保真、不改写**)→ **串行 govern**(逐集:模型带累积上下文,从正文自己认人物 / importance(featured=出图/background=龙套) / 提造型(每个=一张造型图,带描述) / 做身份合并;`serial/context_NNN.md`=喂模型的完整上下文、`serial/ep_NNN.md`=模型每集决策,**逐集 checkpoint、重跑续**)→ `script.json`(v3 校验)。
32
+ - **视频**:pass1 Gemini 转录 → pass2 解析 → pass3 跨集花名册归并 + 状态归并(**贵,`roster.json`/`context.merged.md` 会复用**)→ pass4 二次画面校正 → pass5 纯剧情发声源审计 → pass6 apply + 汇入 govern 收尾。时间戳挂在 `extend.timestamp`。
33
33
 
34
34
  ## 二、看进度 / 自查
35
35
 
@@ -37,6 +37,7 @@ scriptctl ingest --source-path uploads/正片/
37
37
  scriptctl ingest status # 人读:kind / state / 各 pass 进度(transcribe 40/57 (2 failed) ...)
38
38
  scriptctl ingest status --json # 机器:.status = {kind,state,phase,passes[],units,validation,errors[]}
39
39
  scriptctl view # 生成 workspace/review.html(视频版可点击定位到画面)
40
+ scriptctl view --watch [--port N]# 实时监听面板:分阶段进度条 + 逐集 context/决策 + 产物树,自动刷新(跑中也能开)。🔴 仅本地用,**沙箱内不要用**(起阻塞服务+开浏览器,会挂住;沙箱看进度用 `ingest status`)
40
41
  scriptctl summary --script-path workspace/script.json # 抽完当普通剧本读
41
42
  ```
42
43
 
@@ -49,6 +50,7 @@ scriptctl summary --script-path workspace/script.json # 抽完当普通剧本
49
50
  | 报错 | 含义 | 怎么办 |
50
51
  |---|---|---|
51
52
  | `INGEST_NORMALIZE_INCOMPLETE`(文本)<br>`INGEST_TRANSCRIBE_/CORRECT_/SPEAKER_INCOMPLETE`(视频) | 部分单元失败,留下 `passN/*.error.json` sidecar | **直接重跑 `scriptctl ingest`(同 source、同 workspace)**——从 checkpoint 续,只补失败单元。跑完 `ingest status` 看还剩几个,多跑几次到收敛 |
53
+ | `INGEST_SERIAL_FAILED`(文本串行 govern) | 某集串行解析连试几次仍失败(模型抖动) | **直接重跑**,从该集续——前面各集已缓存不重跑 |
52
54
  | `INGEST_SOURCE_MISMATCH` | 工作区是别的 source 建的 | 换一个新的 `--workspace-path`,**别 `--force` 硬覆盖** |
53
55
  | `INGEST_VALIDATION_FAILED` | 最终 v3 校验没过 | 看 `workspace/validation.json` 的具体报错,用读写动词修 `--script-path workspace/script.json` 再 `publish` |
54
56
  | 持续 401/403/404 / 模型路由错 | 网关/密钥/模型环境问题(不是抖动,重跑也不好) | 如实告诉用户,别反复空跑、别编造成功。视频需 `GEMINI_API_KEY` |