@gitruck/cli 0.2.6 → 0.2.8
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/AGENT.md +2 -2
- package/README.md +145 -10
- package/contracts/README.md +1 -1
- package/contracts/handoff-contracts.json +2 -2
- package/dist/index.js +513 -26
- package/package.json +2 -2
- package/skills/gtrk-ai-drama/SKILL.md +209 -0
- package/skills/gtrk-matrix/SKILL.md +111 -0
- package/skills/gtrk-mg/SKILL.md +97 -0
- package/skills/gtrk-oralcut/SKILL.md +6 -0
- package/skills/gtrk-splitter/SKILL.md +32 -12
- package/skills/gtrk-splitter/references/example-visual-split.json +20 -20
- package/skills/gtrk-splitter/references/example-visual-split.md +11 -11
- package/skills/gtrk-splitter/references/field-schema.md +38 -10
- package/skills/gtrk-style-maker/SKILL.md +6 -0
- package/skills/gtrk-style-maker/references/seeds/seed-psych-humanities.md +1 -1
package/dist/index.js
CHANGED
|
@@ -4197,7 +4197,7 @@ var {
|
|
|
4197
4197
|
|
|
4198
4198
|
// src/index.ts
|
|
4199
4199
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
4200
|
-
import { join as
|
|
4200
|
+
import { join as join21 } from "node:path";
|
|
4201
4201
|
|
|
4202
4202
|
// src/lib/paths.ts
|
|
4203
4203
|
import { dirname, join } from "node:path";
|
|
@@ -4274,7 +4274,14 @@ var log = {
|
|
|
4274
4274
|
};
|
|
4275
4275
|
|
|
4276
4276
|
// src/commands/skills.ts
|
|
4277
|
-
var SKILL_NAMES = [
|
|
4277
|
+
var SKILL_NAMES = [
|
|
4278
|
+
"gtrk-oralcut",
|
|
4279
|
+
"gtrk-splitter",
|
|
4280
|
+
"gtrk-matrix",
|
|
4281
|
+
"gtrk-mg",
|
|
4282
|
+
"gtrk-ai-drama",
|
|
4283
|
+
"gtrk-style-maker"
|
|
4284
|
+
];
|
|
4278
4285
|
function installSkill(opts = {}) {
|
|
4279
4286
|
const destRoot = opts.dir ?? join2(homedir2(), ".claude", "skills");
|
|
4280
4287
|
let allOk = true;
|
|
@@ -4300,7 +4307,7 @@ function installSkill(opts = {}) {
|
|
|
4300
4307
|
}
|
|
4301
4308
|
function registerSkills(program2) {
|
|
4302
4309
|
const skills = program2.command("skills").description("管理 agent skill(安装到 Claude Code)");
|
|
4303
|
-
skills.command("install").description("把
|
|
4310
|
+
skills.command("install").description("把 gtrk 全家框架 skill 安装到 ~/.claude/skills(对标飞书 skills add)").option("--dir <dir>", "自定义 skills 目录(缺省 ~/.claude/skills)").action((opts) => {
|
|
4304
4311
|
installSkill({ dir: opts.dir });
|
|
4305
4312
|
});
|
|
4306
4313
|
}
|
|
@@ -4475,7 +4482,7 @@ import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:f
|
|
|
4475
4482
|
|
|
4476
4483
|
// src/lib/splitdoc.ts
|
|
4477
4484
|
var BASE_TRACKS = ["真人出镜", "口播继续", "旁白主导"];
|
|
4478
|
-
var LANES = ["A_ROLL", "
|
|
4485
|
+
var LANES = ["A_ROLL", "MG", "AI_DRAMA", "FILM_BROLL"];
|
|
4479
4486
|
var NARRATIVES = [
|
|
4480
4487
|
"mirror-hook",
|
|
4481
4488
|
"demolition",
|
|
@@ -4503,8 +4510,17 @@ var AUX_TYPES = [
|
|
|
4503
4510
|
"archive-caption",
|
|
4504
4511
|
"pause-card",
|
|
4505
4512
|
"data-annotation",
|
|
4506
|
-
"timeline-tag"
|
|
4513
|
+
"timeline-tag",
|
|
4514
|
+
"overlay"
|
|
4507
4515
|
];
|
|
4516
|
+
var LEGACY_LANE_ALIASES = { RRV_MG: "MG" };
|
|
4517
|
+
function normalizeLane(v) {
|
|
4518
|
+
if (typeof v !== "string")
|
|
4519
|
+
return;
|
|
4520
|
+
if (LANES.includes(v))
|
|
4521
|
+
return v;
|
|
4522
|
+
return LEGACY_LANE_ALIASES[v];
|
|
4523
|
+
}
|
|
4508
4524
|
function isNonEmptyStr(v) {
|
|
4509
4525
|
return typeof v === "string" && v.trim().length > 0;
|
|
4510
4526
|
}
|
|
@@ -4573,7 +4589,7 @@ function validateSplitDoc(doc, ctx) {
|
|
|
4573
4589
|
if (!enumOk(b.container_stage, vocab.container_stage))
|
|
4574
4590
|
errors.push(`${tag}:container_stage 非法(不在栏目词表内)`);
|
|
4575
4591
|
}
|
|
4576
|
-
if (!
|
|
4592
|
+
if (!normalizeLane(b.lane))
|
|
4577
4593
|
errors.push(`${tag}:lane 非法(四选一:${LANES.join(" | ")})`);
|
|
4578
4594
|
if (!enumOk(b.irreplaceability, IRREPLACEABILITY))
|
|
4579
4595
|
errors.push(`${tag}:irreplaceability 非法(四枚举之一)`);
|
|
@@ -4607,7 +4623,7 @@ function validateSplitDoc(doc, ctx) {
|
|
|
4607
4623
|
if (!Array.isArray(b.aux_layers))
|
|
4608
4624
|
errors.push(`${tag}:aux_layers 必须是数组`);
|
|
4609
4625
|
else
|
|
4610
|
-
b.aux_layers.forEach((a, ai) => validateAux(`${tag}.aux[${ai}]`, a, idIndex, errors));
|
|
4626
|
+
b.aux_layers.forEach((a, ai) => validateAux(`${tag}.aux[${ai}]`, a, idIndex, errors, warnings));
|
|
4611
4627
|
}
|
|
4612
4628
|
});
|
|
4613
4629
|
const sorted = [...ranges].sort((a, b) => a.from - b.from || a.to - b.to);
|
|
@@ -4628,9 +4644,12 @@ function validateHandoff(tag, b, errors, warnings) {
|
|
|
4628
4644
|
warnings.push(`${tag}:A_ROLL 不应带 handoff,已忽略`);
|
|
4629
4645
|
return;
|
|
4630
4646
|
}
|
|
4631
|
-
if (lane === "RRV_MG") {
|
|
4647
|
+
if (lane === "MG" || lane === "RRV_MG") {
|
|
4632
4648
|
if (!handoff || typeof handoff.duration_hint !== "number") {
|
|
4633
|
-
errors.push(`${tag}:
|
|
4649
|
+
errors.push(`${tag}:MG 的 handoff.duration_hint 必填(秒,数值)`);
|
|
4650
|
+
}
|
|
4651
|
+
if (handoff && handoff.category !== undefined && !isKnownCategory(handoff.category)) {
|
|
4652
|
+
warnings.push(`${tag}:handoff.category「${String(handoff.category)}」非已知品类(${MG_CATEGORIES.join("/")}),已透传但下游按 opaque 反推`);
|
|
4634
4653
|
}
|
|
4635
4654
|
return;
|
|
4636
4655
|
}
|
|
@@ -4642,16 +4661,25 @@ function validateHandoff(tag, b, errors, warnings) {
|
|
|
4642
4661
|
return;
|
|
4643
4662
|
}
|
|
4644
4663
|
}
|
|
4645
|
-
function validateAux(tag, raw, idIndex, errors) {
|
|
4664
|
+
function validateAux(tag, raw, idIndex, errors, warnings) {
|
|
4646
4665
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
4647
4666
|
errors.push(`${tag}:辅助层必须是对象`);
|
|
4648
4667
|
return;
|
|
4649
4668
|
}
|
|
4650
4669
|
const a = raw;
|
|
4651
4670
|
if (!enumOk(a.type, AUX_TYPES))
|
|
4652
|
-
errors.push(`${tag}:type
|
|
4671
|
+
errors.push(`${tag}:type 非法(八类之一)`);
|
|
4653
4672
|
if (!isNonEmptyStr(a.role))
|
|
4654
4673
|
errors.push(`${tag}:缺 role(职责)`);
|
|
4674
|
+
if (a.type === "overlay") {
|
|
4675
|
+
const handoff = a.handoff;
|
|
4676
|
+
if (!handoff || typeof handoff.duration_hint !== "number" || !(handoff.duration_hint > 0)) {
|
|
4677
|
+
errors.push(`${tag}:overlay aux 缺颗粒时长(handoff.duration_hint 必填且须为正数)`);
|
|
4678
|
+
}
|
|
4679
|
+
if (handoff && handoff.category !== undefined && !isKnownCategory(handoff.category)) {
|
|
4680
|
+
warnings.push(`${tag}:handoff.category「${String(handoff.category)}」非已知品类(${MG_CATEGORIES.join("/")}),已透传但下游按 opaque 反推`);
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4655
4683
|
const m = a.mount;
|
|
4656
4684
|
if (m === "same_beat")
|
|
4657
4685
|
return;
|
|
@@ -4675,6 +4703,20 @@ function validateAux(tag, raw, idIndex, errors) {
|
|
|
4675
4703
|
}
|
|
4676
4704
|
errors.push(`${tag}:mount 非法(应为 "same_beat" | {from,to} | {trigger})`);
|
|
4677
4705
|
}
|
|
4706
|
+
var MG_CATEGORIES = ["overlay", "fullscreen", "subtitle", "title"];
|
|
4707
|
+
var CATEGORY_EXPECTED_OPAQUE = {
|
|
4708
|
+
overlay: false,
|
|
4709
|
+
fullscreen: true,
|
|
4710
|
+
subtitle: false,
|
|
4711
|
+
title: true,
|
|
4712
|
+
"rrv-overlay": false,
|
|
4713
|
+
"mg-fullscreen": true,
|
|
4714
|
+
"explain-subtitle": false,
|
|
4715
|
+
"op-ed-title": true
|
|
4716
|
+
};
|
|
4717
|
+
function isKnownCategory(v) {
|
|
4718
|
+
return typeof v === "string" && Object.prototype.hasOwnProperty.call(CATEGORY_EXPECTED_OPAQUE, v);
|
|
4719
|
+
}
|
|
4678
4720
|
function r3(n) {
|
|
4679
4721
|
return Math.round(n * 1000) / 1000;
|
|
4680
4722
|
}
|
|
@@ -4698,7 +4740,7 @@ function buildLanding(doc, view, opts) {
|
|
|
4698
4740
|
...opts.sourceIndex ? { material_id: opts.sourceIndex.materialId } : {},
|
|
4699
4741
|
beats: []
|
|
4700
4742
|
};
|
|
4701
|
-
const dispatch = {
|
|
4743
|
+
const dispatch = { mg: [], film_broll: [], ai_drama: [] };
|
|
4702
4744
|
const skipped = [];
|
|
4703
4745
|
const shrunk = [];
|
|
4704
4746
|
const unhandledLanes = new Set;
|
|
@@ -4715,7 +4757,8 @@ function buildLanding(doc, view, opts) {
|
|
|
4715
4757
|
const track_st = Math.min(...instances.map((x) => x.track_st));
|
|
4716
4758
|
const track_ed = Math.max(...instances.map((x) => x.track_ed));
|
|
4717
4759
|
const isShrunk = droppedCount > 0;
|
|
4718
|
-
const
|
|
4760
|
+
const lane = normalizeLane(beat.lane) ?? beat.lane;
|
|
4761
|
+
const metaBeat = { id: beat.id, lane, span: beat.span, track_st, track_ed };
|
|
4719
4762
|
if (opts.sourceIndex) {
|
|
4720
4763
|
const from = opts.sourceIndex.utterances.get(beat.span.from);
|
|
4721
4764
|
const to = opts.sourceIndex.utterances.get(beat.span.to);
|
|
@@ -4731,7 +4774,9 @@ function buildLanding(doc, view, opts) {
|
|
|
4731
4774
|
metaBeat.container_stage = beat.container_stage;
|
|
4732
4775
|
if (beat.visual_task)
|
|
4733
4776
|
metaBeat.visual_task = beat.visual_task;
|
|
4734
|
-
if (
|
|
4777
|
+
if (lane === "MG" && typeof beat.handoff?.category === "string")
|
|
4778
|
+
metaBeat.category = beat.handoff.category;
|
|
4779
|
+
if (lane !== "A_ROLL" && beat.handoff)
|
|
4735
4780
|
metaBeat.handoff = beat.handoff;
|
|
4736
4781
|
split.beats.push(metaBeat);
|
|
4737
4782
|
if (isShrunk) {
|
|
@@ -4739,18 +4784,19 @@ function buildLanding(doc, view, opts) {
|
|
|
4739
4784
|
}
|
|
4740
4785
|
const h = beat.handoff ?? {};
|
|
4741
4786
|
const compositionId = `${opts.projectSlug}-${beat.id}`;
|
|
4742
|
-
if (
|
|
4743
|
-
dispatch.
|
|
4787
|
+
if (lane === "MG") {
|
|
4788
|
+
dispatch.mg.push({
|
|
4744
4789
|
beat: beat.id,
|
|
4745
4790
|
composition_id: compositionId,
|
|
4746
4791
|
duration: typeof h.duration_hint === "number" ? h.duration_hint : null,
|
|
4792
|
+
...h.category !== undefined ? { category: h.category } : {},
|
|
4747
4793
|
theme: h.theme,
|
|
4748
4794
|
bg: h.bg,
|
|
4749
4795
|
slug_hint: h.slug_hint,
|
|
4750
4796
|
track_st,
|
|
4751
4797
|
track_ed
|
|
4752
4798
|
});
|
|
4753
|
-
} else if (
|
|
4799
|
+
} else if (lane === "FILM_BROLL") {
|
|
4754
4800
|
dispatch.film_broll.push({
|
|
4755
4801
|
beat: beat.id,
|
|
4756
4802
|
queries: Array.isArray(h.queries) ? h.queries : [],
|
|
@@ -4760,10 +4806,69 @@ function buildLanding(doc, view, opts) {
|
|
|
4760
4806
|
track_st,
|
|
4761
4807
|
track_ed
|
|
4762
4808
|
});
|
|
4763
|
-
} else if (
|
|
4809
|
+
} else if (lane === "AI_DRAMA") {
|
|
4764
4810
|
dispatch.ai_drama.push({ beat: beat.id, ...h, track_st, track_ed });
|
|
4765
|
-
} else if (
|
|
4766
|
-
unhandledLanes.add(
|
|
4811
|
+
} else if (lane !== "A_ROLL") {
|
|
4812
|
+
unhandledLanes.add(lane);
|
|
4813
|
+
}
|
|
4814
|
+
let auxN = 0;
|
|
4815
|
+
for (const aux of beat.aux_layers ?? []) {
|
|
4816
|
+
if (aux.type !== "overlay")
|
|
4817
|
+
continue;
|
|
4818
|
+
auxN += 1;
|
|
4819
|
+
const auxTag = `${beat.id}-aux${auxN}`;
|
|
4820
|
+
const mount = aux.mount;
|
|
4821
|
+
let auxFromId;
|
|
4822
|
+
let auxToId;
|
|
4823
|
+
if (mount === "same_beat") {
|
|
4824
|
+
auxFromId = beat.span.from;
|
|
4825
|
+
auxToId = beat.span.to;
|
|
4826
|
+
} else if ("from" in mount && "to" in mount) {
|
|
4827
|
+
auxFromId = mount.from;
|
|
4828
|
+
auxToId = mount.to;
|
|
4829
|
+
} else {
|
|
4830
|
+
skipped.push({ beat: auxTag, reason: "overlay aux 使用 {trigger} 点挂载,一期不支持(二期补合成窗口)" });
|
|
4831
|
+
continue;
|
|
4832
|
+
}
|
|
4833
|
+
const auxFromIdx = idIndex.get(auxFromId);
|
|
4834
|
+
const auxToIdx = idIndex.get(auxToId);
|
|
4835
|
+
const auxSpanIds = opts.utteranceIds.slice(auxFromIdx, auxToIdx + 1);
|
|
4836
|
+
const auxInstances = auxSpanIds.flatMap((id) => byId.get(id) ?? []);
|
|
4837
|
+
if (auxInstances.length === 0) {
|
|
4838
|
+
skipped.push({ beat: auxTag, reason: "overlay aux 源区间 utterance 全被剪,未落轨" });
|
|
4839
|
+
continue;
|
|
4840
|
+
}
|
|
4841
|
+
const auxTrackSt = Math.min(...auxInstances.map((x) => x.track_st));
|
|
4842
|
+
const auxTrackEd = Math.max(...auxInstances.map((x) => x.track_ed));
|
|
4843
|
+
const auxCompositionId = `${opts.projectSlug}-${beat.id}-aux${auxN}`;
|
|
4844
|
+
const ah = aux.handoff;
|
|
4845
|
+
dispatch.mg.push({
|
|
4846
|
+
beat: beat.id,
|
|
4847
|
+
composition_id: auxCompositionId,
|
|
4848
|
+
duration: ah && typeof ah.duration_hint === "number" ? ah.duration_hint : null,
|
|
4849
|
+
category: "overlay",
|
|
4850
|
+
theme: ah?.theme,
|
|
4851
|
+
bg: ah?.bg,
|
|
4852
|
+
slug_hint: ah?.slug_hint,
|
|
4853
|
+
track_st: auxTrackSt,
|
|
4854
|
+
track_ed: auxTrackEd
|
|
4855
|
+
});
|
|
4856
|
+
const auxMetaBeat = {
|
|
4857
|
+
id: auxTag,
|
|
4858
|
+
lane: "MG",
|
|
4859
|
+
span: { from: auxFromId, to: auxToId },
|
|
4860
|
+
track_st: auxTrackSt,
|
|
4861
|
+
track_ed: auxTrackEd,
|
|
4862
|
+
category: "overlay"
|
|
4863
|
+
};
|
|
4864
|
+
if (opts.sourceIndex) {
|
|
4865
|
+
const sfrom = opts.sourceIndex.utterances.get(auxFromId);
|
|
4866
|
+
const sto = opts.sourceIndex.utterances.get(auxToId);
|
|
4867
|
+
if (sfrom && sto && sto.ed > sfrom.st) {
|
|
4868
|
+
auxMetaBeat.source_ranges = [{ st: r3(sfrom.st), ed: r3(sto.ed) }];
|
|
4869
|
+
}
|
|
4870
|
+
}
|
|
4871
|
+
split.beats.push(auxMetaBeat);
|
|
4767
4872
|
}
|
|
4768
4873
|
}
|
|
4769
4874
|
return { split, dispatch, skipped, shrunk, unhandledLanes: [...unhandledLanes] };
|
|
@@ -4810,8 +4915,8 @@ function renderSplitMarkdown(doc, landing, meta) {
|
|
|
4810
4915
|
L.push(`- \`${b.id}\` ${b.visual_task}`);
|
|
4811
4916
|
}
|
|
4812
4917
|
L.push("");
|
|
4813
|
-
L.push("##
|
|
4814
|
-
for (const r of landing.dispatch.
|
|
4918
|
+
L.push("## MG Queue");
|
|
4919
|
+
for (const r of landing.dispatch.mg) {
|
|
4815
4920
|
L.push(`- \`${r.beat}\` composition_id=\`${r.composition_id}\`${r.duration != null ? ` · ${r.duration}s` : ""}`);
|
|
4816
4921
|
}
|
|
4817
4922
|
L.push("");
|
|
@@ -4928,6 +5033,13 @@ function resolveColumnConfig(opts = {}) {
|
|
|
4928
5033
|
return { config, warnings };
|
|
4929
5034
|
}
|
|
4930
5035
|
var HANDOFF_REGISTRY = LANES;
|
|
5036
|
+
var LEGACY_HANDOFF_ALIASES = { RRV_MG: "MG" };
|
|
5037
|
+
function normalizeHandoffType(v) {
|
|
5038
|
+
return LEGACY_HANDOFF_ALIASES[v] ?? v;
|
|
5039
|
+
}
|
|
5040
|
+
function isRegisteredHandoff(v) {
|
|
5041
|
+
return HANDOFF_REGISTRY.includes(normalizeHandoffType(v));
|
|
5042
|
+
}
|
|
4931
5043
|
function normalizeEntries(list, kind, warnings) {
|
|
4932
5044
|
if (list === undefined)
|
|
4933
5045
|
return;
|
|
@@ -5002,7 +5114,7 @@ function producesNotices(style) {
|
|
|
5002
5114
|
if (e.routing === "none")
|
|
5003
5115
|
continue;
|
|
5004
5116
|
for (const v of producesValues(e)) {
|
|
5005
|
-
if (
|
|
5117
|
+
if (isRegisteredHandoff(v) || seen.has(v))
|
|
5006
5118
|
continue;
|
|
5007
5119
|
const near = HANDOFF_REGISTRY.find((r) => r.toLowerCase() === v.toLowerCase() || editDistance(r.toUpperCase(), v.toUpperCase()) <= 2);
|
|
5008
5120
|
if (near) {
|
|
@@ -6877,7 +6989,7 @@ async function runLand(splitdocPath, baseDir, gtrkPath, transcriptPath, opts) {
|
|
|
6877
6989
|
mdPath = join18(splitDir, "visual-split.md");
|
|
6878
6990
|
await writeFile6(mdPath, renderSplitMarkdown(doc, landing, { projectSlug, projectedAt }));
|
|
6879
6991
|
}
|
|
6880
|
-
log.ok(`落地完成:${landing.split.beats.length}/${doc.beats.length} beat 落轨` + `(
|
|
6992
|
+
log.ok(`落地完成:${landing.split.beats.length}/${doc.beats.length} beat 落轨` + `(MG ${landing.dispatch.mg.length} · FILM_BROLL ${landing.dispatch.film_broll.length} · AI_DRAMA ${landing.dispatch.ai_drama.length})`);
|
|
6881
6993
|
for (const s of landing.skipped)
|
|
6882
6994
|
log.warn(`跳过 ${s.beat}:${s.reason}`);
|
|
6883
6995
|
for (const s of landing.shrunk)
|
|
@@ -6895,7 +7007,7 @@ async function runLand(splitdocPath, baseDir, gtrkPath, transcriptPath, opts) {
|
|
|
6895
7007
|
projected_at: projectedAt,
|
|
6896
7008
|
beats: { total: doc.beats.length, landed: landing.split.beats.length, skipped: landing.skipped, shrunk: landing.shrunk },
|
|
6897
7009
|
queues: {
|
|
6898
|
-
|
|
7010
|
+
mg: landing.dispatch.mg.length,
|
|
6899
7011
|
film_broll: landing.dispatch.film_broll.length,
|
|
6900
7012
|
ai_drama: landing.dispatch.ai_drama.length
|
|
6901
7013
|
}
|
|
@@ -7685,12 +7797,386 @@ function slugify2(name) {
|
|
|
7685
7797
|
return s || "project";
|
|
7686
7798
|
}
|
|
7687
7799
|
|
|
7800
|
+
// src/commands/mg.ts
|
|
7801
|
+
import { resolve as resolve8, join as join20, dirname as dirname7, basename as basename10 } from "node:path";
|
|
7802
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
7803
|
+
import { readFile as readFile6, mkdir as mkdir7, copyFile } from "node:fs/promises";
|
|
7804
|
+
|
|
7805
|
+
// src/lib/mg-lint.ts
|
|
7806
|
+
var CDN_OK = [/lib\.baomitu\.com/i, /cdnjs\.cloudflare\.com/i, /unpkg\.com/i];
|
|
7807
|
+
var CDN_WARN = [/jsdelivr\.net/i];
|
|
7808
|
+
function rootTag(html) {
|
|
7809
|
+
const m = html.match(/<[a-zA-Z][^>]*\bdata-composition-id\s*=[^>]*>/);
|
|
7810
|
+
return m ? m[0] : null;
|
|
7811
|
+
}
|
|
7812
|
+
function attr(tag, name) {
|
|
7813
|
+
const m = tag.match(new RegExp(`\\b${name}\\s*=\\s*["']([^"']*)["']`, "i"));
|
|
7814
|
+
return m ? m[1] : undefined;
|
|
7815
|
+
}
|
|
7816
|
+
function deriveOpaque(rootTagStr) {
|
|
7817
|
+
if (!rootTagStr)
|
|
7818
|
+
return { opaque: false, declared: false };
|
|
7819
|
+
const style = attr(rootTagStr, "style") ?? "";
|
|
7820
|
+
const bg = style.match(/background(?:-color)?\s*:\s*([^;"']+)/i);
|
|
7821
|
+
if (!bg)
|
|
7822
|
+
return { opaque: false, declared: false };
|
|
7823
|
+
const val = bg[1].trim().toLowerCase();
|
|
7824
|
+
const transparent = val === "transparent" || val === "none" || /rgba\([^)]*,\s*0\s*\)/.test(val);
|
|
7825
|
+
return { opaque: !transparent, declared: true };
|
|
7826
|
+
}
|
|
7827
|
+
var CATEGORY_EXPECTED_OPAQUE2 = {
|
|
7828
|
+
overlay: false,
|
|
7829
|
+
fullscreen: true,
|
|
7830
|
+
subtitle: false,
|
|
7831
|
+
title: true,
|
|
7832
|
+
"rrv-overlay": false,
|
|
7833
|
+
"mg-fullscreen": true,
|
|
7834
|
+
"explain-subtitle": false,
|
|
7835
|
+
"op-ed-title": true
|
|
7836
|
+
};
|
|
7837
|
+
function lintParticle(html, opts = {}) {
|
|
7838
|
+
const v = [];
|
|
7839
|
+
const push = (law, fatal, msg) => v.push({ law, fatal, msg });
|
|
7840
|
+
if (!/<template[\s>]/i.test(html))
|
|
7841
|
+
push("1-template", true, "缺 <template> 包裹根元素(裸 div 整片渲染失败)");
|
|
7842
|
+
const root = rootTag(html);
|
|
7843
|
+
const cid = root ? attr(root, "data-composition-id") : undefined;
|
|
7844
|
+
if (!root || !cid)
|
|
7845
|
+
push("1-composition-id", true, "根元素缺 data-composition-id");
|
|
7846
|
+
if (root) {
|
|
7847
|
+
if (attr(root, "data-width") !== "1920")
|
|
7848
|
+
push("1-width", true, `根 data-width 应为 "1920"(实为 ${attr(root, "data-width") ?? "缺"})`);
|
|
7849
|
+
if (attr(root, "data-height") !== "1080")
|
|
7850
|
+
push("1-height", true, `根 data-height 应为 "1080"(实为 ${attr(root, "data-height") ?? "缺"})`);
|
|
7851
|
+
}
|
|
7852
|
+
if (!/gsap\.timeline\s*\(\s*\{[^}]*\bpaused\s*:\s*true\b[^}]*\}\s*\)/.test(html))
|
|
7853
|
+
push("2-paused", true, "缺 gsap.timeline({ paused: true })");
|
|
7854
|
+
const regMatch = html.match(/window\.__timelines\s*\[\s*(["']([^"']+)["']|[A-Za-z_$][\w$]*)\s*\]\s*=/);
|
|
7855
|
+
if (!regMatch)
|
|
7856
|
+
push("2-register", true, "缺 window.__timelines[<id>] = 注册");
|
|
7857
|
+
else {
|
|
7858
|
+
let regId = regMatch[2];
|
|
7859
|
+
if (regId === undefined) {
|
|
7860
|
+
const ident = regMatch[1];
|
|
7861
|
+
const vm = html.match(new RegExp(`(?:var|const|let)\\s+${ident}\\s*=\\s*["']([^"']+)["']`));
|
|
7862
|
+
regId = vm?.[1];
|
|
7863
|
+
if (!regId)
|
|
7864
|
+
push("2-register", true, `__timelines[${ident}] 的 ${ident} 未见字面串赋值,无法静态判定注册 id`);
|
|
7865
|
+
}
|
|
7866
|
+
if (cid && regId !== undefined && regId !== cid)
|
|
7867
|
+
push("2-id-match", true, `__timelines 注册 id "${regId}" 与 data-composition-id "${cid}" 不一致`);
|
|
7868
|
+
}
|
|
7869
|
+
if (/Math\.random\s*\(/.test(html))
|
|
7870
|
+
push("3-random", true, "含 Math.random()(破 StaticGuard,逐帧不确定)");
|
|
7871
|
+
if (/Date\.now\s*\(/.test(html))
|
|
7872
|
+
push("3-date-now", true, "含 Date.now()");
|
|
7873
|
+
if (/new\s+Date\s*\(\s*\)/.test(html))
|
|
7874
|
+
push("3-new-date", true, "含无参 new Date()");
|
|
7875
|
+
for (const m of html.matchAll(/<script\b[^>]*\bsrc\s*=\s*["']([^"']+)["']/gi)) {
|
|
7876
|
+
const src = m[1];
|
|
7877
|
+
if (!/^https?:\/\//i.test(src))
|
|
7878
|
+
push("4-script-rel", true, `<script src> 非 http(s) 绝对 url:${src}`);
|
|
7879
|
+
else if (CDN_WARN.some((r) => r.test(src)))
|
|
7880
|
+
push("5-cdn-jsdelivr", false, `CDN 用 jsdelivr(国内渲染机不稳,建议 lib.baomitu.com):${src}`);
|
|
7881
|
+
else if (!CDN_OK.some((r) => r.test(src)))
|
|
7882
|
+
push("5-cdn-unknown", false, `CDN 不在已知可达白名单(渲染机可能拉不到):${src}`);
|
|
7883
|
+
}
|
|
7884
|
+
for (const [re, tag] of [
|
|
7885
|
+
[/<img\b[^>]*\bsrc\s*=\s*["'](?!https?:|data:)[^"']+["']/gi, "<img src>"],
|
|
7886
|
+
[/<link\b[^>]*\bhref\s*=\s*["'](?!https?:|data:)[^"']+["']/gi, "<link href>"],
|
|
7887
|
+
[/<use\b[^>]*\b(?:xlink:)?href\s*=\s*["'](?!#|https?:|data:)[^"']+["']/gi, "<use href>"],
|
|
7888
|
+
[/\burl\(\s*["']?(?!https?:|data:|#)[^)"']+["']?\s*\)/gi, "css url()"]
|
|
7889
|
+
]) {
|
|
7890
|
+
if (re.test(html))
|
|
7891
|
+
push("4-rel-asset", true, `含相对外链 ${tag}(违反自包含,渲染机读不到)`);
|
|
7892
|
+
}
|
|
7893
|
+
const { opaque, declared } = deriveOpaque(root);
|
|
7894
|
+
if (root && !declared)
|
|
7895
|
+
push("4-bg-explicit", false, "根未显式声明 background(透明与否应明确;缺省按透明叠加 opaque=false 处理)");
|
|
7896
|
+
if (/var\(\s*--/.test(html))
|
|
7897
|
+
push("6-css-var", true, "含 CSS var(--...)(Hyperframes 不解析→整片全黑,须字面值)");
|
|
7898
|
+
const effectiveCid = opts.compositionId ?? cid;
|
|
7899
|
+
if (opts.dispatchIds && effectiveCid && !opts.dispatchIds.includes(effectiveCid))
|
|
7900
|
+
push("x-dispatch", false, `composition_id "${effectiveCid}" 不在 dispatch.mg 派单中`);
|
|
7901
|
+
if (opts.category && opts.category in CATEGORY_EXPECTED_OPAQUE2) {
|
|
7902
|
+
const expect = CATEGORY_EXPECTED_OPAQUE2[opts.category];
|
|
7903
|
+
if (expect !== opaque)
|
|
7904
|
+
push("x-category-opaque", false, `category「${opts.category}」期望${expect ? "不透明满屏" : "透明叠加"},但颗粒 HTML 反推为${opaque ? "不透明满屏" : "透明叠加"}(以 HTML 为准落 clip.opaque=${opaque})`);
|
|
7905
|
+
}
|
|
7906
|
+
return { ok: !v.some((x) => x.fatal), violations: v, opaque, compositionId: cid };
|
|
7907
|
+
}
|
|
7908
|
+
|
|
7909
|
+
// src/lib/mg-lay.ts
|
|
7910
|
+
var MG_MATERIAL_PREFIX = "mg-";
|
|
7911
|
+
var LEGACY_MATERIAL_PREFIX = "rrv-";
|
|
7912
|
+
var isOwnMaterialId = (id) => id.startsWith(MG_MATERIAL_PREFIX) || id.startsWith(LEGACY_MATERIAL_PREFIX);
|
|
7913
|
+
var r34 = (n) => Math.round(n * 1000) / 1000;
|
|
7914
|
+
function layTracksOf(prev) {
|
|
7915
|
+
return Array.isArray(prev?.lay_tracks) ? prev.lay_tracks.filter((x) => typeof x === "number") : [];
|
|
7916
|
+
}
|
|
7917
|
+
function layMgTracks(opts) {
|
|
7918
|
+
const { gtrk, items, generatedAt } = opts;
|
|
7919
|
+
const beatTracks = [...gtrk.beat_track ?? []];
|
|
7920
|
+
const materials = [...gtrk.materials ?? []];
|
|
7921
|
+
const structMeta = { ...gtrk.struct_meta ?? {} };
|
|
7922
|
+
const prevMg = structMeta.mg;
|
|
7923
|
+
const prevRrv = structMeta.rrv;
|
|
7924
|
+
const prevIndices = new Set([...layTracksOf(prevMg), ...layTracksOf(prevRrv)]);
|
|
7925
|
+
const removedTracks = beatTracks.filter((t) => typeof t.track_index === "number" && prevIndices.has(t.track_index));
|
|
7926
|
+
const keptTracks = beatTracks.filter((t) => !(typeof t.track_index === "number" && prevIndices.has(t.track_index)));
|
|
7927
|
+
const removedMaterialIds = new Set;
|
|
7928
|
+
for (const t of removedTracks) {
|
|
7929
|
+
for (const c3 of t.track_timeline ?? []) {
|
|
7930
|
+
const hm = c3.html_material;
|
|
7931
|
+
if (typeof hm === "string" && isOwnMaterialId(hm))
|
|
7932
|
+
removedMaterialIds.add(hm);
|
|
7933
|
+
}
|
|
7934
|
+
}
|
|
7935
|
+
const keptMaterials = materials.filter((m) => !(typeof m.id === "string" && removedMaterialIds.has(m.id)));
|
|
7936
|
+
const newMaterials = [];
|
|
7937
|
+
const clips = [];
|
|
7938
|
+
const metaBeats = [];
|
|
7939
|
+
const allIndices = [
|
|
7940
|
+
...keptTracks,
|
|
7941
|
+
...gtrk.video_track ?? [],
|
|
7942
|
+
...gtrk.audio_track ?? []
|
|
7943
|
+
].map((t) => typeof t.track_index === "number" ? t.track_index : 0);
|
|
7944
|
+
const newIndex = Math.max(9, ...allIndices) + 1;
|
|
7945
|
+
for (const it of items) {
|
|
7946
|
+
if (!(it.duration > 0)) {
|
|
7947
|
+
metaBeats.push({ ...toMetaBeat(it), laid: null });
|
|
7948
|
+
continue;
|
|
7949
|
+
}
|
|
7950
|
+
const materialId = `${MG_MATERIAL_PREFIX}${it.composition_id}`;
|
|
7951
|
+
newMaterials.push({ id: materialId, path: it.html_rel });
|
|
7952
|
+
clips.push({
|
|
7953
|
+
clip_id: it.composition_id,
|
|
7954
|
+
material: it.composition_id,
|
|
7955
|
+
html_material: materialId,
|
|
7956
|
+
opaque: it.opaque,
|
|
7957
|
+
track_st: it.track_st,
|
|
7958
|
+
duration: it.duration
|
|
7959
|
+
});
|
|
7960
|
+
metaBeats.push({ ...toMetaBeat(it), laid: { track_index: newIndex } });
|
|
7961
|
+
}
|
|
7962
|
+
const createdTracks = clips.length > 0 ? [
|
|
7963
|
+
{
|
|
7964
|
+
track_index: newIndex,
|
|
7965
|
+
track_timeline: clips.sort((a, b) => a.track_st - b.track_st)
|
|
7966
|
+
}
|
|
7967
|
+
] : [];
|
|
7968
|
+
const mg = {
|
|
7969
|
+
contract_version: "v1",
|
|
7970
|
+
generated_at: generatedAt,
|
|
7971
|
+
lay_tracks: createdTracks.map((t) => t.track_index),
|
|
7972
|
+
beats: metaBeats
|
|
7973
|
+
};
|
|
7974
|
+
const nextStructMeta = { ...structMeta, mg };
|
|
7975
|
+
delete nextStructMeta.rrv;
|
|
7976
|
+
const next = {
|
|
7977
|
+
...gtrk,
|
|
7978
|
+
materials: [...keptMaterials, ...newMaterials],
|
|
7979
|
+
beat_track: [...keptTracks, ...createdTracks],
|
|
7980
|
+
struct_meta: nextStructMeta
|
|
7981
|
+
};
|
|
7982
|
+
return {
|
|
7983
|
+
next,
|
|
7984
|
+
summary: { laidTrack: createdTracks[0]?.track_index ?? null, laidParticles: clips.length },
|
|
7985
|
+
mg
|
|
7986
|
+
};
|
|
7987
|
+
}
|
|
7988
|
+
function toMetaBeat(it) {
|
|
7989
|
+
return {
|
|
7990
|
+
beat: it.beat,
|
|
7991
|
+
composition_id: it.composition_id,
|
|
7992
|
+
track_st: it.track_st,
|
|
7993
|
+
track_ed: r34(it.track_ed),
|
|
7994
|
+
duration: it.duration,
|
|
7995
|
+
html_path: it.html_rel,
|
|
7996
|
+
...it.category ? { category: it.category } : {}
|
|
7997
|
+
};
|
|
7998
|
+
}
|
|
7999
|
+
|
|
8000
|
+
// src/commands/mg.ts
|
|
8001
|
+
var MG_ASSET_DIR = "assets/mg";
|
|
8002
|
+
var MG_SRC_DIRS = ["mg", "rrv"];
|
|
8003
|
+
function registerMg(program2) {
|
|
8004
|
+
program2.command("mg [words...]").alias("rrv").description("MG 颗粒铺轨:无 positional=消费 dispatch.mg 铺 html-particle;`mg lint <file>`=单文件 lint;`mg status`=看板").option("--project <dir>", "oralcut 产物目录(定位 split/dispatch.json 与工程)").option("--dispatch <path>", "显式指定 dispatch.json(非标准布局兜底)").option("--only <beat>", "只跑单 beat").option("--lint-only", "只 lint 校验,不铺轨不写回").option("--json", "机读模式:人读日志转 stderr,stdout 只输出结果 JSON").action(async (words, opts) => {
|
|
8005
|
+
if (process.argv[2] === "rrv")
|
|
8006
|
+
log.warn("`gtrk rrv` 已更名为 `gtrk mg`(去品牌化),别名仍可用但建议改用 `gtrk mg`。");
|
|
8007
|
+
await runMg(words ?? [], opts);
|
|
8008
|
+
});
|
|
8009
|
+
}
|
|
8010
|
+
async function runMg(words, opts) {
|
|
8011
|
+
if (opts.json)
|
|
8012
|
+
routeLogsToStderr();
|
|
8013
|
+
const sub = words[0];
|
|
8014
|
+
if (sub === "lint")
|
|
8015
|
+
return runLint(words.slice(1), opts);
|
|
8016
|
+
if (sub === "status")
|
|
8017
|
+
return runStatus(opts);
|
|
8018
|
+
if (sub)
|
|
8019
|
+
throw new Error(`未知子命令「${sub}」——铺轨:gtrk mg --project <dir>;lint:gtrk mg lint <file>;看板:gtrk mg status`);
|
|
8020
|
+
return runLay(opts);
|
|
8021
|
+
}
|
|
8022
|
+
function resolveDispatch(opts) {
|
|
8023
|
+
if (opts.dispatch) {
|
|
8024
|
+
const dispatchPath = resolve8(opts.dispatch);
|
|
8025
|
+
return { dispatchPath, baseDir: dirname7(dirname7(dispatchPath)) };
|
|
8026
|
+
}
|
|
8027
|
+
if (opts.project) {
|
|
8028
|
+
const baseDir = resolve8(opts.project);
|
|
8029
|
+
return { dispatchPath: join20(baseDir, "split", "dispatch.json"), baseDir };
|
|
8030
|
+
}
|
|
8031
|
+
throw new Error("需 --project <目录> 或显式 --dispatch <path>");
|
|
8032
|
+
}
|
|
8033
|
+
function locateGtrk2(baseDir) {
|
|
8034
|
+
return [join20(baseDir, "gtrk", "project.gtrk"), join20(baseDir, "project.gtrk")].find((p) => existsSync16(p));
|
|
8035
|
+
}
|
|
8036
|
+
function locateSrcHtml(baseDir, compositionId) {
|
|
8037
|
+
for (const d of MG_SRC_DIRS) {
|
|
8038
|
+
const p = join20(baseDir, d, `${compositionId}.html`);
|
|
8039
|
+
if (existsSync16(p))
|
|
8040
|
+
return p;
|
|
8041
|
+
}
|
|
8042
|
+
return;
|
|
8043
|
+
}
|
|
8044
|
+
async function readMgQueue(dispatchPath) {
|
|
8045
|
+
if (!existsSync16(dispatchPath))
|
|
8046
|
+
throw new Error(`找不到派单清单:${dispatchPath}(先跑 gtrk split 落地派单)`);
|
|
8047
|
+
const dispatch = JSON.parse(await readFile6(dispatchPath, "utf8"));
|
|
8048
|
+
const queue = dispatch.mg ?? dispatch.rrv_mg;
|
|
8049
|
+
return Array.isArray(queue) ? queue : [];
|
|
8050
|
+
}
|
|
8051
|
+
async function runLay(opts) {
|
|
8052
|
+
const { dispatchPath, baseDir } = resolveDispatch(opts);
|
|
8053
|
+
let queue = await readMgQueue(dispatchPath);
|
|
8054
|
+
if (opts.only)
|
|
8055
|
+
queue = queue.filter((q) => q.beat === opts.only);
|
|
8056
|
+
log.step(`▶ MG 颗粒铺轨:${queue.length} 个 beat…`);
|
|
8057
|
+
const dispatchIds = queue.map((q) => q.composition_id);
|
|
8058
|
+
const items = [];
|
|
8059
|
+
const srcByComp = new Map;
|
|
8060
|
+
const skipped = [];
|
|
8061
|
+
for (const q of queue) {
|
|
8062
|
+
const srcPath = locateSrcHtml(baseDir, q.composition_id);
|
|
8063
|
+
if (!srcPath) {
|
|
8064
|
+
skipped.push({ beat: q.beat, reason: "缺颗粒 HTML(未产出)" });
|
|
8065
|
+
log.warn(`${q.beat}:缺 ${join20(baseDir, MG_SRC_DIRS[0], `${q.composition_id}.html`)},跳过`);
|
|
8066
|
+
continue;
|
|
8067
|
+
}
|
|
8068
|
+
const html = await readFile6(srcPath, "utf8");
|
|
8069
|
+
const category = typeof q.category === "string" ? q.category : undefined;
|
|
8070
|
+
const lint = lintParticle(html, { compositionId: q.composition_id, dispatchIds, category });
|
|
8071
|
+
for (const vv of lint.violations)
|
|
8072
|
+
(vv.fatal ? log.warn : log.info)(`${q.beat} lint ${vv.fatal ? "✗" : "·"} ${vv.law}: ${vv.msg}`);
|
|
8073
|
+
if (!lint.ok) {
|
|
8074
|
+
skipped.push({ beat: q.beat, reason: "lint 未过" });
|
|
8075
|
+
log.warn(`${q.beat}:lint 未过,跳过`);
|
|
8076
|
+
continue;
|
|
8077
|
+
}
|
|
8078
|
+
if (typeof q.duration !== "number" || !(q.duration > 0)) {
|
|
8079
|
+
skipped.push({ beat: q.beat, reason: "duration 非正数" });
|
|
8080
|
+
continue;
|
|
8081
|
+
}
|
|
8082
|
+
srcByComp.set(q.composition_id, srcPath);
|
|
8083
|
+
items.push({
|
|
8084
|
+
beat: q.beat,
|
|
8085
|
+
composition_id: q.composition_id,
|
|
8086
|
+
track_st: q.track_st,
|
|
8087
|
+
track_ed: q.track_ed,
|
|
8088
|
+
duration: q.duration,
|
|
8089
|
+
opaque: lint.opaque,
|
|
8090
|
+
html_rel: `${MG_ASSET_DIR}/${q.composition_id}.html`,
|
|
8091
|
+
...category ? { category } : {}
|
|
8092
|
+
});
|
|
8093
|
+
}
|
|
8094
|
+
if (opts.lintOnly) {
|
|
8095
|
+
log.ok(`lint-only:${items.length}/${queue.length} 通过,${skipped.length} 跳过(不铺轨)`);
|
|
8096
|
+
return done(opts, { ok: skipped.length === 0, mode: "lay", lintOnly: true, passed: items.length, skipped });
|
|
8097
|
+
}
|
|
8098
|
+
const gtrkPath = locateGtrk2(baseDir);
|
|
8099
|
+
if (!gtrkPath) {
|
|
8100
|
+
log.warn(`未找到工程文件(${join20(baseDir, "gtrk", "project.gtrk")}),跳过铺轨——lint 已完成`);
|
|
8101
|
+
return done(opts, { ok: true, mode: "lay", laid: 0, skipped, note: "工程缺失,未铺轨" });
|
|
8102
|
+
}
|
|
8103
|
+
const { gtrk, mtimeMs } = readGtrk(gtrkPath);
|
|
8104
|
+
assertGtrkV1(gtrk);
|
|
8105
|
+
const gtrkDir = dirname7(gtrkPath);
|
|
8106
|
+
await mkdir7(join20(gtrkDir, ...MG_ASSET_DIR.split("/")), { recursive: true });
|
|
8107
|
+
for (const it of items) {
|
|
8108
|
+
await copyFile(srcByComp.get(it.composition_id), join20(gtrkDir, ...it.html_rel.split("/")));
|
|
8109
|
+
}
|
|
8110
|
+
const { next, summary } = layMgTracks({ gtrk, items, generatedAt: new Date().toISOString() });
|
|
8111
|
+
writeGtrkAtomic(gtrkPath, next, mtimeMs);
|
|
8112
|
+
log.ok(`铺轨完成:${summary.laidParticles} 颗粒 → beat_track ${summary.laidTrack ?? "-"}` + `${skipped.length ? `(${skipped.length} beat 跳过)` : ""}`);
|
|
8113
|
+
log.info("opencut 打开工程即见 MG overlay 轨(预览需 add-particle-project-folder-preview 上线);出片时客户端云渲。");
|
|
8114
|
+
return done(opts, {
|
|
8115
|
+
ok: true,
|
|
8116
|
+
mode: "lay",
|
|
8117
|
+
laid: summary.laidParticles,
|
|
8118
|
+
laidTrack: summary.laidTrack,
|
|
8119
|
+
skipped
|
|
8120
|
+
});
|
|
8121
|
+
}
|
|
8122
|
+
async function runLint(args, opts) {
|
|
8123
|
+
const file = args[0];
|
|
8124
|
+
if (!file)
|
|
8125
|
+
throw new Error("用法:gtrk mg lint <particle.html> [--dispatch <path>]");
|
|
8126
|
+
const html = await readFile6(resolve8(file), "utf8");
|
|
8127
|
+
let dispatchIds;
|
|
8128
|
+
if (opts.dispatch && existsSync16(resolve8(opts.dispatch))) {
|
|
8129
|
+
dispatchIds = (await readMgQueue(resolve8(opts.dispatch))).map((q) => q.composition_id);
|
|
8130
|
+
}
|
|
8131
|
+
const lint = lintParticle(html, { dispatchIds });
|
|
8132
|
+
for (const vv of lint.violations)
|
|
8133
|
+
(vv.fatal ? log.err : log.warn)(`${vv.fatal ? "✗" : "·"} ${vv.law}: ${vv.msg}`);
|
|
8134
|
+
if (lint.ok)
|
|
8135
|
+
log.ok(`lint 通过(${basename10(file)};opaque=${lint.opaque})`);
|
|
8136
|
+
else
|
|
8137
|
+
log.err(`lint 未过(${lint.violations.filter((v) => v.fatal).length} 项致命)`);
|
|
8138
|
+
const result = { mode: "lint", ...lint, ok: lint.ok };
|
|
8139
|
+
if (opts.json)
|
|
8140
|
+
console.log(JSON.stringify(result));
|
|
8141
|
+
if (!lint.ok)
|
|
8142
|
+
process.exitCode = 1;
|
|
8143
|
+
return result;
|
|
8144
|
+
}
|
|
8145
|
+
async function runStatus(opts) {
|
|
8146
|
+
const { dispatchPath, baseDir } = resolveDispatch(opts);
|
|
8147
|
+
const queue = await readMgQueue(dispatchPath);
|
|
8148
|
+
const gtrkPath = locateGtrk2(baseDir);
|
|
8149
|
+
let laidIds = new Set;
|
|
8150
|
+
if (gtrkPath) {
|
|
8151
|
+
const { gtrk } = readGtrk(gtrkPath);
|
|
8152
|
+
const structMeta = gtrk.struct_meta;
|
|
8153
|
+
const meta = structMeta?.mg ?? structMeta?.rrv;
|
|
8154
|
+
laidIds = new Set((meta?.beats ?? []).filter((b) => b.laid).map((b) => b.composition_id));
|
|
8155
|
+
}
|
|
8156
|
+
const rows = queue.map((q) => {
|
|
8157
|
+
const authored2 = locateSrcHtml(baseDir, q.composition_id) !== undefined;
|
|
8158
|
+
const laid2 = laidIds.has(q.composition_id);
|
|
8159
|
+
return { beat: q.beat, composition_id: q.composition_id, authored: authored2, laid: laid2, state: laid2 ? "已铺" : authored2 ? "已产未铺" : "缺 HTML" };
|
|
8160
|
+
});
|
|
8161
|
+
const authored = rows.filter((r) => r.authored).length;
|
|
8162
|
+
const laid = rows.filter((r) => r.laid).length;
|
|
8163
|
+
log.step(`▶ MG 看板:${queue.length} beat · ${authored} 已产 · ${laid} 已铺`);
|
|
8164
|
+
for (const r of rows)
|
|
8165
|
+
log.info(`${r.beat}(${r.composition_id})→ ${r.state}`);
|
|
8166
|
+
return done(opts, { ok: true, mode: "status", total: queue.length, authored, laid, rows });
|
|
8167
|
+
}
|
|
8168
|
+
function done(opts, result) {
|
|
8169
|
+
if (opts.json)
|
|
8170
|
+
console.log(JSON.stringify(result));
|
|
8171
|
+
return result;
|
|
8172
|
+
}
|
|
8173
|
+
|
|
7688
8174
|
// src/index.ts
|
|
7689
8175
|
try {
|
|
7690
8176
|
process.loadEnvFile?.();
|
|
7691
8177
|
} catch {}
|
|
7692
8178
|
migrateLegacyHome();
|
|
7693
|
-
var { version } = JSON.parse(readFileSync5(
|
|
8179
|
+
var { version } = JSON.parse(readFileSync5(join21(packageRoot(), "package.json"), "utf8"));
|
|
7694
8180
|
var program2 = new Command;
|
|
7695
8181
|
program2.name("gtrk").description("同合云成片流水线 CLI —— agent 驱动云端任务、产物拉回本地、三方工程文件(客户端/剪映/PR)互通").version(version);
|
|
7696
8182
|
registerInstall(program2);
|
|
@@ -7703,6 +8189,7 @@ registerUpgrade(program2);
|
|
|
7703
8189
|
registerRender(program2);
|
|
7704
8190
|
registerSplit(program2);
|
|
7705
8191
|
registerMatrix(program2);
|
|
8192
|
+
registerMg(program2);
|
|
7706
8193
|
program2.parseAsync(process.argv).catch((e) => {
|
|
7707
8194
|
console.error(`
|
|
7708
8195
|
❌ ${e instanceof Error ? e.message : String(e)}`);
|