@lingjingai/scriptctl 0.20.0 → 0.22.0

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 (208) hide show
  1. package/dist/cli.js +105 -11
  2. package/dist/cli.js.map +1 -1
  3. package/dist/common.d.ts +1 -0
  4. package/dist/common.js.map +1 -1
  5. package/dist/domain/script/address.d.ts +30 -0
  6. package/dist/domain/script/address.js +141 -0
  7. package/dist/domain/script/address.js.map +1 -0
  8. package/dist/domain/script/lookups.d.ts +33 -0
  9. package/dist/domain/script/lookups.js +309 -0
  10. package/dist/domain/script/lookups.js.map +1 -0
  11. package/dist/domain/script/patch/apply.d.ts +4 -0
  12. package/dist/domain/script/patch/apply.js +38 -0
  13. package/dist/domain/script/patch/apply.js.map +1 -0
  14. package/dist/domain/script/patch/helpers.d.ts +11 -0
  15. package/dist/domain/script/patch/helpers.js +141 -0
  16. package/dist/domain/script/patch/helpers.js.map +1 -0
  17. package/dist/domain/script/patch/ops-action.d.ts +12 -0
  18. package/dist/domain/script/patch/ops-action.js +371 -0
  19. package/dist/domain/script/patch/ops-action.js.map +1 -0
  20. package/dist/domain/script/patch/ops-asset.d.ts +13 -0
  21. package/dist/domain/script/patch/ops-asset.js +363 -0
  22. package/dist/domain/script/patch/ops-asset.js.map +1 -0
  23. package/dist/domain/script/patch/ops-dialogue.d.ts +5 -0
  24. package/dist/domain/script/patch/ops-dialogue.js +233 -0
  25. package/dist/domain/script/patch/ops-dialogue.js.map +1 -0
  26. package/dist/domain/script/patch/ops-meta.d.ts +3 -0
  27. package/dist/domain/script/patch/ops-meta.js +47 -0
  28. package/dist/domain/script/patch/ops-meta.js.map +1 -0
  29. package/dist/domain/script/patch/ops-scene.d.ts +6 -0
  30. package/dist/domain/script/patch/ops-scene.js +345 -0
  31. package/dist/domain/script/patch/ops-scene.js.map +1 -0
  32. package/dist/domain/script/patch/ops-state.d.ts +8 -0
  33. package/dist/domain/script/patch/ops-state.js +146 -0
  34. package/dist/domain/script/patch/ops-state.js.map +1 -0
  35. package/dist/domain/script/patch/registry.d.ts +2 -0
  36. package/dist/domain/script/patch/registry.js +50 -0
  37. package/dist/domain/script/patch/registry.js.map +1 -0
  38. package/dist/domain/script/patch/types.d.ts +10 -0
  39. package/dist/domain/script/patch/types.js +2 -0
  40. package/dist/domain/script/patch/types.js.map +1 -0
  41. package/dist/domain/script/refs.d.ts +4 -0
  42. package/dist/domain/script/refs.js +152 -0
  43. package/dist/domain/script/refs.js.map +1 -0
  44. package/dist/domain/script/schema.d.ts +6 -0
  45. package/dist/domain/script/schema.js +53 -0
  46. package/dist/domain/script/schema.js.map +1 -0
  47. package/dist/domain/script/selector.d.ts +45 -0
  48. package/dist/domain/script/selector.js +187 -0
  49. package/dist/domain/script/selector.js.map +1 -0
  50. package/dist/domain/script/shared.d.ts +15 -0
  51. package/dist/domain/script/shared.js +37 -0
  52. package/dist/domain/script/shared.js.map +1 -0
  53. package/dist/domain/script/validate.d.ts +22 -0
  54. package/dist/domain/script/validate.js +844 -0
  55. package/dist/domain/script/validate.js.map +1 -0
  56. package/dist/help-text.js +33 -8
  57. package/dist/help-text.js.map +1 -1
  58. package/dist/infra/script-output-store.d.ts +46 -10
  59. package/dist/infra/script-output-store.js +83 -11
  60. package/dist/infra/script-output-store.js.map +1 -1
  61. package/dist/output.d.ts +1 -1
  62. package/dist/output.js +11 -1
  63. package/dist/output.js.map +1 -1
  64. package/dist/usecases/direct.js +1 -1
  65. package/dist/usecases/direct.js.map +1 -1
  66. package/dist/usecases/doctor.js +32 -22
  67. package/dist/usecases/doctor.js.map +1 -1
  68. package/dist/usecases/parse.js +10 -3
  69. package/dist/usecases/parse.js.map +1 -1
  70. package/dist/usecases/script/actions.d.ts +3 -0
  71. package/dist/usecases/script/actions.js +71 -0
  72. package/dist/usecases/script/actions.js.map +1 -0
  73. package/dist/usecases/script/actor.d.ts +3 -0
  74. package/dist/usecases/script/actor.js +27 -0
  75. package/dist/usecases/script/actor.js.map +1 -0
  76. package/dist/usecases/script/actors.d.ts +3 -0
  77. package/dist/usecases/script/actors.js +15 -0
  78. package/dist/usecases/script/actors.js.map +1 -0
  79. package/dist/usecases/script/add-actor.d.ts +3 -0
  80. package/dist/usecases/script/add-actor.js +13 -0
  81. package/dist/usecases/script/add-actor.js.map +1 -0
  82. package/dist/usecases/script/add-episode.d.ts +3 -0
  83. package/dist/usecases/script/add-episode.js +9 -0
  84. package/dist/usecases/script/add-episode.js.map +1 -0
  85. package/dist/usecases/script/add-location.d.ts +3 -0
  86. package/dist/usecases/script/add-location.js +11 -0
  87. package/dist/usecases/script/add-location.js.map +1 -0
  88. package/dist/usecases/script/add-prop.d.ts +3 -0
  89. package/dist/usecases/script/add-prop.js +11 -0
  90. package/dist/usecases/script/add-prop.js.map +1 -0
  91. package/dist/usecases/script/add-speaker.d.ts +3 -0
  92. package/dist/usecases/script/add-speaker.js +22 -0
  93. package/dist/usecases/script/add-speaker.js.map +1 -0
  94. package/dist/usecases/script/alias.d.ts +3 -0
  95. package/dist/usecases/script/alias.js +40 -0
  96. package/dist/usecases/script/alias.js.map +1 -0
  97. package/dist/usecases/script/assets.d.ts +3 -0
  98. package/dist/usecases/script/assets.js +21 -0
  99. package/dist/usecases/script/assets.js.map +1 -0
  100. package/dist/usecases/script/context.d.ts +3 -0
  101. package/dist/usecases/script/context.js +20 -0
  102. package/dist/usecases/script/context.js.map +1 -0
  103. package/dist/usecases/script/create.d.ts +3 -0
  104. package/dist/usecases/script/create.js +67 -0
  105. package/dist/usecases/script/create.js.map +1 -0
  106. package/dist/usecases/script/delete.d.ts +3 -0
  107. package/dist/usecases/script/delete.js +79 -0
  108. package/dist/usecases/script/delete.js.map +1 -0
  109. package/dist/usecases/script/describe.d.ts +3 -0
  110. package/dist/usecases/script/describe.js +31 -0
  111. package/dist/usecases/script/describe.js.map +1 -0
  112. package/dist/usecases/script/dialogue.d.ts +3 -0
  113. package/dist/usecases/script/dialogue.js +28 -0
  114. package/dist/usecases/script/dialogue.js.map +1 -0
  115. package/dist/usecases/script/do.d.ts +3 -0
  116. package/dist/usecases/script/do.js +130 -0
  117. package/dist/usecases/script/do.js.map +1 -0
  118. package/dist/usecases/script/episodes.d.ts +3 -0
  119. package/dist/usecases/script/episodes.js +36 -0
  120. package/dist/usecases/script/episodes.js.map +1 -0
  121. package/dist/usecases/script/export.d.ts +3 -0
  122. package/dist/usecases/script/export.js +117 -0
  123. package/dist/usecases/script/export.js.map +1 -0
  124. package/dist/usecases/script/insert.d.ts +3 -0
  125. package/dist/usecases/script/insert.js +87 -0
  126. package/dist/usecases/script/insert.js.map +1 -0
  127. package/dist/usecases/script/issues.d.ts +3 -0
  128. package/dist/usecases/script/issues.js +27 -0
  129. package/dist/usecases/script/issues.js.map +1 -0
  130. package/dist/usecases/script/lib.d.ts +77 -0
  131. package/dist/usecases/script/lib.js +849 -0
  132. package/dist/usecases/script/lib.js.map +1 -0
  133. package/dist/usecases/script/locations.d.ts +3 -0
  134. package/dist/usecases/script/locations.js +15 -0
  135. package/dist/usecases/script/locations.js.map +1 -0
  136. package/dist/usecases/script/merge.d.ts +3 -0
  137. package/dist/usecases/script/merge.js +43 -0
  138. package/dist/usecases/script/merge.js.map +1 -0
  139. package/dist/usecases/script/move.d.ts +3 -0
  140. package/dist/usecases/script/move.js +33 -0
  141. package/dist/usecases/script/move.js.map +1 -0
  142. package/dist/usecases/script/overlap.d.ts +3 -0
  143. package/dist/usecases/script/overlap.js +21 -0
  144. package/dist/usecases/script/overlap.js.map +1 -0
  145. package/dist/usecases/script/props.d.ts +3 -0
  146. package/dist/usecases/script/props.js +15 -0
  147. package/dist/usecases/script/props.js.map +1 -0
  148. package/dist/usecases/script/refs.d.ts +3 -0
  149. package/dist/usecases/script/refs.js +61 -0
  150. package/dist/usecases/script/refs.js.map +1 -0
  151. package/dist/usecases/script/rename.d.ts +3 -0
  152. package/dist/usecases/script/rename.js +17 -0
  153. package/dist/usecases/script/rename.js.map +1 -0
  154. package/dist/usecases/script/replace.d.ts +3 -0
  155. package/dist/usecases/script/replace.js +35 -0
  156. package/dist/usecases/script/replace.js.map +1 -0
  157. package/dist/usecases/script/role.d.ts +3 -0
  158. package/dist/usecases/script/role.js +17 -0
  159. package/dist/usecases/script/role.js.map +1 -0
  160. package/dist/usecases/script/scenes.d.ts +3 -0
  161. package/dist/usecases/script/scenes.js +45 -0
  162. package/dist/usecases/script/scenes.js.map +1 -0
  163. package/dist/usecases/script/script-patch.d.ts +3 -0
  164. package/dist/usecases/script/script-patch.js +187 -0
  165. package/dist/usecases/script/script-patch.js.map +1 -0
  166. package/dist/usecases/script/script-validate.d.ts +3 -0
  167. package/dist/usecases/script/script-validate.js +30 -0
  168. package/dist/usecases/script/script-validate.js.map +1 -0
  169. package/dist/usecases/script/session.d.ts +56 -0
  170. package/dist/usecases/script/session.js +568 -0
  171. package/dist/usecases/script/session.js.map +1 -0
  172. package/dist/usecases/script/speakers.d.ts +3 -0
  173. package/dist/usecases/script/speakers.js +28 -0
  174. package/dist/usecases/script/speakers.js.map +1 -0
  175. package/dist/usecases/script/split.d.ts +3 -0
  176. package/dist/usecases/script/split.js +37 -0
  177. package/dist/usecases/script/split.js.map +1 -0
  178. package/dist/usecases/script/state-add.d.ts +3 -0
  179. package/dist/usecases/script/state-add.js +23 -0
  180. package/dist/usecases/script/state-add.js.map +1 -0
  181. package/dist/usecases/script/state-change.d.ts +3 -0
  182. package/dist/usecases/script/state-change.js +24 -0
  183. package/dist/usecases/script/state-change.js.map +1 -0
  184. package/dist/usecases/script/state-delete.d.ts +3 -0
  185. package/dist/usecases/script/state-delete.js +23 -0
  186. package/dist/usecases/script/state-delete.js.map +1 -0
  187. package/dist/usecases/script/state-rename.d.ts +3 -0
  188. package/dist/usecases/script/state-rename.js +17 -0
  189. package/dist/usecases/script/state-rename.js.map +1 -0
  190. package/dist/usecases/script/summary.d.ts +3 -0
  191. package/dist/usecases/script/summary.js +45 -0
  192. package/dist/usecases/script/summary.js.map +1 -0
  193. package/dist/usecases/script/transition.d.ts +3 -0
  194. package/dist/usecases/script/transition.js +24 -0
  195. package/dist/usecases/script/transition.js.map +1 -0
  196. package/dist/usecases/script/type.d.ts +3 -0
  197. package/dist/usecases/script/type.js +24 -0
  198. package/dist/usecases/script/type.js.map +1 -0
  199. package/dist/usecases/script/worldview.d.ts +3 -0
  200. package/dist/usecases/script/worldview.js +18 -0
  201. package/dist/usecases/script/worldview.js.map +1 -0
  202. package/package.json +1 -1
  203. package/dist/domain/script-core.d.ts +0 -78
  204. package/dist/domain/script-core.js +0 -3026
  205. package/dist/domain/script-core.js.map +0 -1
  206. package/dist/usecases/script.d.ts +0 -67
  207. package/dist/usecases/script.js +0 -2348
  208. package/dist/usecases/script.js.map +0 -1
@@ -0,0 +1,363 @@
1
+ import { ROLE_TYPE_VALUES, } from "../../../common.js";
2
+ import { inferNonActorSpeakerKind, sceneContext, setSceneContext, } from "../../direct-core.js";
3
+ import { asList, isDict, isList, opErr, strOf } from "../shared.js";
4
+ import { assetKeys, normalizeStates, pluralForKind, pushAsset, } from "../lookups.js";
5
+ import { parseAssetTarget, } from "../address.js";
6
+ import { collectAssetRefs } from "../refs.js";
7
+ import { contextRefsForKind, resolveAssetByTarget, } from "./helpers.js";
8
+ export function op_actor_add(__ctx) {
9
+ const { script, op, sourceText, applied, kind } = __ctx;
10
+ const name = strOf(op["name"] ?? op["actor_name"]).trim();
11
+ // Reject non-human speakers as actors up front (validate would flag
12
+ // NONHUMAN_AS_ACTOR otherwise) — system/broadcast/prop voices belong in
13
+ // speakers[], not actors[].
14
+ if (name && inferNonActorSpeakerKind(name) !== "actor") {
15
+ throw opErr("SCRIPT OP BLOCKED: Non-human as actor", "Non-human speaker registered as actor.", {
16
+ required: ["a human/character name"], received: [name],
17
+ nextSteps: ["Register non-human voices with add-speaker (kind system/broadcast/prop/group/other)."],
18
+ op: kind, errorCode: "NONHUMAN_AS_ACTOR",
19
+ });
20
+ }
21
+ const entity = pushAsset(script, "actor", op);
22
+ const roleType = strOf(op["role_type"] ?? op["role"]).trim();
23
+ if (roleType) {
24
+ if (!ROLE_TYPE_VALUES.includes(roleType)) {
25
+ throw opErr("SCRIPT OP BLOCKED: Role type invalid", "Role type invalid.", {
26
+ required: ["role_type: 主角 or 配角"], received: [roleType],
27
+ nextSteps: ["Use a supported actor role_type, or omit to leave it unset."],
28
+ op: kind, errorCode: "ROLE_TYPE_INVALID",
29
+ });
30
+ }
31
+ entity["role_type"] = roleType;
32
+ }
33
+ // Only stamp aliases when there's at least one — keeps the entity shape
34
+ // identical to direct/parse-produced actors (which omit the key when empty).
35
+ const aliases = isList(op["aliases"]) ? op["aliases"].map((a) => strOf(a).trim()).filter(Boolean) : [];
36
+ if (aliases.length)
37
+ entity["aliases"] = aliases;
38
+ applied.push(kind);
39
+ }
40
+ export function op_location_add(__ctx) {
41
+ const { script, op, sourceText, applied, kind } = __ctx;
42
+ pushAsset(script, "location", op);
43
+ applied.push(kind);
44
+ }
45
+ export function op_prop_add(__ctx) {
46
+ const { script, op, sourceText, applied, kind } = __ctx;
47
+ pushAsset(script, "prop", op);
48
+ applied.push(kind);
49
+ }
50
+ export function op_asset_role_set(__ctx) {
51
+ const { script, op, sourceText, applied, kind } = __ctx;
52
+ const [assetKind, assetId] = parseAssetTarget(op["target"]);
53
+ if (assetKind !== "actor") {
54
+ throw opErr("SCRIPT OP BLOCKED: Role only on actors", "Role applies to actor only.", {
55
+ required: ["actor:<id>"],
56
+ received: [strOf(op["target"])],
57
+ nextSteps: ["Use an actor target."],
58
+ op: kind,
59
+ errorCode: "ROLE_TARGET_INVALID",
60
+ });
61
+ }
62
+ const asset = resolveAssetByTarget(script, "actor", assetId);
63
+ const roleType = strOf(op["role_type"] ?? op["role"]).trim();
64
+ if (!ROLE_TYPE_VALUES.includes(roleType)) {
65
+ throw opErr("SCRIPT OP BLOCKED: Role type invalid", "Role type invalid.", {
66
+ required: ["role_type: 主角 or 配角"],
67
+ received: [roleType || "<empty>"],
68
+ nextSteps: ["Use a supported actor role_type."],
69
+ op: kind,
70
+ errorCode: "ROLE_TYPE_INVALID",
71
+ });
72
+ }
73
+ asset["role_type"] = roleType;
74
+ applied.push(kind);
75
+ }
76
+ export function op_asset_rename(__ctx) {
77
+ const { script, op, sourceText, applied, kind } = __ctx;
78
+ const [assetKind, assetId] = parseAssetTarget(op["target"]);
79
+ const asset = resolveAssetByTarget(script, assetKind, assetId);
80
+ const newName = strOf(op["name"] ?? op["new_name"]).trim();
81
+ if (!newName) {
82
+ throw opErr("SCRIPT OP BLOCKED: Name empty", "Name empty.", {
83
+ required: ["name"],
84
+ received: [JSON.stringify(op)],
85
+ nextSteps: ["Provide a non-empty name."],
86
+ op: kind,
87
+ errorCode: "NAME_EMPTY",
88
+ });
89
+ }
90
+ const [, , nameKey] = assetKeys(assetKind);
91
+ asset[nameKey] = newName;
92
+ applied.push(kind);
93
+ }
94
+ export function op_asset_describe(__ctx) {
95
+ const { script, op, sourceText, applied, kind } = __ctx;
96
+ const [assetKind, assetId] = parseAssetTarget(op["target"]);
97
+ const asset = resolveAssetByTarget(script, assetKind, assetId);
98
+ const description = strOf(op["description"]).trim();
99
+ if (!description) {
100
+ throw opErr("SCRIPT OP BLOCKED: Description empty", "Description empty.", {
101
+ required: ["description"],
102
+ received: [JSON.stringify(op)],
103
+ nextSteps: ["Provide a concise grounded description."],
104
+ op: kind,
105
+ errorCode: "DESCRIPTION_EMPTY",
106
+ });
107
+ }
108
+ asset["description"] = description;
109
+ applied.push(kind);
110
+ }
111
+ export function op_asset_alias_set(__ctx) {
112
+ const { script, op, sourceText, applied, kind } = __ctx;
113
+ // Bulk replace. Rare; primarily for migration tools.
114
+ const [assetKind, assetId] = parseAssetTarget(op["target"]);
115
+ const asset = resolveAssetByTarget(script, assetKind, assetId);
116
+ const aliases = op["aliases"];
117
+ if (!isList(aliases) || aliases.some((it) => typeof it !== "string")) {
118
+ throw opErr("SCRIPT OP BLOCKED: Aliases invalid", "Aliases invalid.", {
119
+ required: ["aliases: array of strings"],
120
+ received: [JSON.stringify(op)],
121
+ nextSteps: ["Pass a string array."],
122
+ op: kind,
123
+ errorCode: "ALIASES_INVALID",
124
+ });
125
+ }
126
+ const seen = new Set();
127
+ const cleaned = [];
128
+ for (const item of aliases) {
129
+ const trimmed = item.trim();
130
+ if (trimmed && !seen.has(trimmed)) {
131
+ seen.add(trimmed);
132
+ cleaned.push(trimmed);
133
+ }
134
+ }
135
+ asset["aliases"] = cleaned;
136
+ applied.push(kind);
137
+ }
138
+ export function op_asset_alias_add(__ctx) {
139
+ const { script, op, sourceText, applied, kind } = __ctx;
140
+ const [assetKind, assetId] = parseAssetTarget(op["target"]);
141
+ const asset = resolveAssetByTarget(script, assetKind, assetId);
142
+ const rawAdd = op["alias"] ?? op["aliases"];
143
+ const toAdd = isList(rawAdd)
144
+ ? rawAdd.map((v) => strOf(v).trim()).filter((v) => v)
145
+ : strOf(rawAdd).trim() ? [strOf(rawAdd).trim()] : [];
146
+ if (toAdd.length === 0) {
147
+ throw opErr("SCRIPT OP BLOCKED: Alias empty", "Alias empty.", {
148
+ required: ["alias: non-empty string or array"],
149
+ received: [JSON.stringify(op)],
150
+ nextSteps: ["Pass --add <alias> at least once."],
151
+ op: kind,
152
+ errorCode: "ALIAS_EMPTY",
153
+ });
154
+ }
155
+ const existing = asList(asset["aliases"]).map((s) => strOf(s));
156
+ const seen = new Set(existing);
157
+ for (const alias of toAdd) {
158
+ if (!seen.has(alias)) {
159
+ existing.push(alias);
160
+ seen.add(alias);
161
+ }
162
+ }
163
+ asset["aliases"] = existing;
164
+ applied.push(kind);
165
+ }
166
+ export function op_asset_alias_remove(__ctx) {
167
+ const { script, op, sourceText, applied, kind } = __ctx;
168
+ const [assetKind, assetId] = parseAssetTarget(op["target"]);
169
+ const asset = resolveAssetByTarget(script, assetKind, assetId);
170
+ const rawRemove = op["alias"] ?? op["aliases"];
171
+ const toRemove = new Set(isList(rawRemove)
172
+ ? rawRemove.map((v) => strOf(v).trim()).filter((v) => v)
173
+ : strOf(rawRemove).trim() ? [strOf(rawRemove).trim()] : []);
174
+ if (toRemove.size === 0) {
175
+ throw opErr("SCRIPT OP BLOCKED: Alias empty", "Alias empty.", {
176
+ required: ["alias: non-empty string or array"],
177
+ received: [JSON.stringify(op)],
178
+ nextSteps: ["Pass --remove <alias> at least once."],
179
+ op: kind,
180
+ errorCode: "ALIAS_EMPTY",
181
+ });
182
+ }
183
+ asset["aliases"] = asList(asset["aliases"]).map((s) => strOf(s)).filter((a) => !toRemove.has(a));
184
+ applied.push(kind);
185
+ }
186
+ export function op_asset_states_set(__ctx) {
187
+ const { script, op, sourceText, applied, kind } = __ctx;
188
+ const [assetKind, assetId] = parseAssetTarget(op["target"]);
189
+ const asset = resolveAssetByTarget(script, assetKind, assetId);
190
+ asset["states"] = normalizeStates(script, op["states"], assetKind);
191
+ applied.push(kind);
192
+ }
193
+ export function op_asset_merge(__ctx) {
194
+ const { script, op, sourceText, applied, kind } = __ctx;
195
+ // Direction is explicit: `from` disappears, `into` keeps everything.
196
+ const fromRaw = op["from"] ?? op["source"];
197
+ const intoRaw = op["into"] ?? op["target"];
198
+ const [fromKind, fromId] = parseAssetTarget(fromRaw);
199
+ const [intoKind, intoId] = parseAssetTarget(intoRaw);
200
+ if (fromKind !== intoKind) {
201
+ throw opErr("SCRIPT OP BLOCKED: Merge kind mismatch", "Merge source and target must be the same kind.", {
202
+ required: ["same kind on both sides (actor/actor, location/location, prop/prop)"],
203
+ received: [`${fromKind}:${fromId} → ${intoKind}:${intoId}`],
204
+ nextSteps: ["Use a same-kind target."],
205
+ op: kind,
206
+ errorCode: "MERGE_KIND_MISMATCH",
207
+ });
208
+ }
209
+ const [key, idKey] = assetKeys(fromKind);
210
+ // Verify both exist.
211
+ resolveAssetByTarget(script, fromKind, fromId);
212
+ resolveAssetByTarget(script, intoKind, intoId);
213
+ // Pre-flight: refuse merge if any scene context already references BOTH
214
+ // from and into with conflicting state_ids. Silently picking one (the
215
+ // array-order-first) was a quiet data-loss bug — we'd rather make the
216
+ // user resolve the state divergence explicitly.
217
+ for (const ep of asList(script["episodes"])) {
218
+ for (const scene of asList(ep["scenes"])) {
219
+ const ctx = sceneContext(scene);
220
+ const refList = contextRefsForKind(ctx, fromKind);
221
+ const fromRef = refList.find((ref) => strOf(ref[idKey]) === fromId);
222
+ const intoRef = refList.find((ref) => strOf(ref[idKey]) === intoId);
223
+ if (fromRef && intoRef) {
224
+ const fromState = strOf(fromRef["state_id"]);
225
+ const intoState = strOf(intoRef["state_id"]);
226
+ if (fromState !== intoState) {
227
+ throw opErr("SCRIPT OP BLOCKED: Merge state conflict", "Merge has conflicting state_ids in a shared scene.", {
228
+ required: ["from and into agree on state_id in every scene where both appear"],
229
+ received: [`${ep["episode_id"]}/${scene["scene_id"]}: ${fromId}=${fromState || "null"}, ${intoId}=${intoState || "null"}`],
230
+ nextSteps: ["Resolve the state divergence (run `scriptctl context <scene> <kind>:<into-id> --state <state>`) then retry merge."],
231
+ op: kind,
232
+ errorCode: "MERGE_STATE_CONFLICT",
233
+ });
234
+ }
235
+ }
236
+ }
237
+ }
238
+ // Drop source from the asset list.
239
+ script[key] = asList(script[key]).filter((it) => strOf(it[idKey]) !== fromId);
240
+ // Rewrite refs: scene.context, action.actor_id (actor only), state_changes targets, transition targets, speaker.source_id.
241
+ for (const ep of asList(script["episodes"])) {
242
+ for (const scene of asList(ep["scenes"])) {
243
+ const ctx = sceneContext(scene);
244
+ const refList = contextRefsForKind(ctx, fromKind);
245
+ for (const ref of refList) {
246
+ if (strOf(ref[idKey]) === fromId)
247
+ ref[idKey] = intoId;
248
+ }
249
+ // After rewrite, dedupe (intoId may now appear twice). State_id was
250
+ // pre-validated to agree above, so first-seen wins is safe.
251
+ const seen = new Set();
252
+ ctx[pluralForKind(fromKind)] = refList.filter((ref) => {
253
+ const key2 = strOf(ref[idKey]);
254
+ if (seen.has(key2))
255
+ return false;
256
+ seen.add(key2);
257
+ return true;
258
+ });
259
+ setSceneContext(scene, ctx);
260
+ for (const action of asList(scene["actions"])) {
261
+ if (fromKind === "actor" && strOf(action["actor_id"]) === fromId)
262
+ action["actor_id"] = intoId;
263
+ for (const change of asList(action["state_changes"])) {
264
+ if (strOf(change["target_kind"]) === fromKind && strOf(change["target_id"]) === fromId) {
265
+ change["target_id"] = intoId;
266
+ }
267
+ }
268
+ const transition = action["transition_prompt"];
269
+ if (isDict(transition) && strOf(transition["target_kind"]) === fromKind && strOf(transition["target_id"]) === fromId) {
270
+ transition["target_id"] = intoId;
271
+ }
272
+ }
273
+ }
274
+ }
275
+ // Speakers anchored to the source actor get reattached to the target.
276
+ for (const sp of asList(script["speakers"])) {
277
+ if (strOf(sp["source_kind"]) === fromKind && strOf(sp["source_id"]) === fromId) {
278
+ sp["source_id"] = intoId;
279
+ }
280
+ }
281
+ applied.push(kind);
282
+ }
283
+ export function op_asset_delete(__ctx) {
284
+ const { script, op, sourceText, applied, kind } = __ctx;
285
+ // Refs are walked via collectAssetRefs. Default: refuse if any refs exist.
286
+ // strategy=replace → rewrite refs to a replacement asset, then delete.
287
+ // strategy=remove → rewrite refs to null/clean and delete (leaves dangling
288
+ // pointers cleaned by per-edge logic; validation will still surface anything
289
+ // still wrong).
290
+ const [assetKind, assetId] = parseAssetTarget(op["target"]);
291
+ resolveAssetByTarget(script, assetKind, assetId);
292
+ const refs = collectAssetRefs(script, assetKind, assetId);
293
+ const strategy = strOf(op["strategy"]).trim();
294
+ if (refs.length > 0 && !strategy) {
295
+ throw opErr("SCRIPT OP BLOCKED: Asset has refs", `Asset is referenced ${refs.length} time(s).`, {
296
+ required: ["--strategy replace|remove"],
297
+ received: [`refs=${refs.length}`],
298
+ nextSteps: ["Use --strategy replace --replacement <addr> to remap, or --strategy remove to scrub refs."],
299
+ op: kind,
300
+ errorCode: "ASSET_HAS_REFS",
301
+ });
302
+ }
303
+ if (strategy === "replace") {
304
+ const replacementRaw = op["replacement"] ?? op["into"];
305
+ const [replacementKind, replacementId] = parseAssetTarget(replacementRaw);
306
+ if (replacementKind !== assetKind) {
307
+ throw opErr("SCRIPT OP BLOCKED: Replacement kind mismatch", "Replacement must be same kind.", {
308
+ required: ["replacement same kind as target"],
309
+ received: [`${assetKind} vs ${replacementKind}`],
310
+ nextSteps: ["Pass a same-kind replacement."],
311
+ op: kind,
312
+ errorCode: "REPLACEMENT_KIND_MISMATCH",
313
+ });
314
+ }
315
+ resolveAssetByTarget(script, replacementKind, replacementId);
316
+ // Delegate ref rewrite to asset.merge logic by reusing the same traversal.
317
+ __ctx.runOps([
318
+ { op: "asset.merge", from: op["target"], into: replacementRaw },
319
+ ]);
320
+ applied.push(kind);
321
+ }
322
+ else if (strategy === "remove" || refs.length === 0) {
323
+ // Scrub refs (actor_id → undefined, state_changes/transition refs removed, scene.context entries removed, speakers' source_id cleared).
324
+ const [key, idKey] = assetKeys(assetKind);
325
+ script[key] = asList(script[key]).filter((it) => strOf(it[idKey]) !== assetId);
326
+ for (const ep of asList(script["episodes"])) {
327
+ for (const scene of asList(ep["scenes"])) {
328
+ const ctx = sceneContext(scene);
329
+ ctx[pluralForKind(assetKind)] = contextRefsForKind(ctx, assetKind).filter((ref) => strOf(ref[idKey]) !== assetId);
330
+ setSceneContext(scene, ctx);
331
+ for (const action of asList(scene["actions"])) {
332
+ if (assetKind === "actor" && strOf(action["actor_id"]) === assetId)
333
+ delete action["actor_id"];
334
+ const changes = asList(action["state_changes"]).filter((c) => !(strOf(c["target_kind"]) === assetKind && strOf(c["target_id"]) === assetId));
335
+ if (changes.length > 0)
336
+ action["state_changes"] = changes;
337
+ else
338
+ delete action["state_changes"];
339
+ const transition = action["transition_prompt"];
340
+ if (isDict(transition) && strOf(transition["target_kind"]) === assetKind && strOf(transition["target_id"]) === assetId) {
341
+ delete action["transition_prompt"];
342
+ }
343
+ }
344
+ }
345
+ }
346
+ for (const sp of asList(script["speakers"])) {
347
+ if (strOf(sp["source_kind"]) === assetKind && strOf(sp["source_id"]) === assetId) {
348
+ sp["source_id"] = null;
349
+ }
350
+ }
351
+ applied.push(kind);
352
+ }
353
+ else {
354
+ throw opErr("SCRIPT OP BLOCKED: Strategy invalid", "Strategy invalid.", {
355
+ required: ["strategy: replace or remove"],
356
+ received: [strategy],
357
+ nextSteps: ["Use replace (with --replacement) or remove."],
358
+ op: kind,
359
+ errorCode: "DELETE_STRATEGY_INVALID",
360
+ });
361
+ }
362
+ }
363
+ //# sourceMappingURL=ops-asset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ops-asset.js","sourceRoot":"","sources":["../../../../src/domain/script/patch/ops-asset.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { OpCtx } from "./types.js";
2
+ export declare function op_speaker_add(__ctx: OpCtx): void;
3
+ export declare function op_dialogue_speakers(__ctx: OpCtx): void;
4
+ export declare function op_dialogue_overlap(__ctx: OpCtx): void;
5
+ export declare function op_speaker_delete(__ctx: OpCtx): void;
@@ -0,0 +1,233 @@
1
+ import { SCRIPT_TARGET_KINDS, SPEAKER_SOURCE_KINDS, } from "../../../common.js";
2
+ import { asList, isDict, isList, opErr, strOf } from "../shared.js";
3
+ import { ensureRefId, findAction, } from "../lookups.js";
4
+ import { parseActionRef, } from "../address.js";
5
+ import { collectAssetRefs } from "../refs.js";
6
+ import { ensureSpeakerId, nextSpeakerId, speakerSourceActorId, } from "./helpers.js";
7
+ export function op_speaker_add(__ctx) {
8
+ const { script, op, sourceText, applied, kind } = __ctx;
9
+ const sourceKind = strOf(op["kind"] || op["source_kind"]).trim();
10
+ if (!SPEAKER_SOURCE_KINDS.has(sourceKind)) {
11
+ throw opErr("SCRIPT OP BLOCKED: Speaker kind invalid", "Speaker kind invalid.", { required: ["kind: actor/location/prop/system/broadcast/group/other"], received: [sourceKind || "<empty>"], nextSteps: ["Use a supported speaker kind."], op: kind, errorCode: "SPEAKER_KIND_INVALID" });
12
+ }
13
+ const name = strOf(op["name"] || op["display_name"]).trim();
14
+ if (!name) {
15
+ throw opErr("SCRIPT OP BLOCKED: Speaker name empty", "Speaker name empty.", { required: ["name"], received: [JSON.stringify(op)], nextSteps: ["Provide display name."], op: kind, errorCode: "SPEAKER_NAME_EMPTY" });
16
+ }
17
+ const sourceId = strOf(op["source_id"]).trim() || null;
18
+ if (SCRIPT_TARGET_KINDS.has(sourceKind)) {
19
+ if (!sourceId) {
20
+ throw opErr("SCRIPT OP BLOCKED: Speaker source missing", "Speaker source missing.", { required: ["source_id for actor/location/prop speakers"], received: ["<empty>"], nextSteps: ["Pass --source-id."], op: kind, errorCode: "SPEAKER_SOURCE_MISSING" });
21
+ }
22
+ ensureRefId(script, sourceKind, sourceId);
23
+ }
24
+ const speakerId = strOf(op["speaker_id"]).trim() || nextSpeakerId(script, sourceKind, sourceId);
25
+ if (asList(script["speakers"]).some((s) => strOf(s["speaker_id"]) === speakerId)) {
26
+ throw opErr("SCRIPT OP BLOCKED: Speaker id exists", "Speaker id exists.", { required: ["unused speaker_id"], received: [speakerId], nextSteps: ["Use another speaker_id."], op: kind, errorCode: "SPEAKER_ID_EXISTS" });
27
+ }
28
+ if (!isList(script["speakers"]))
29
+ script["speakers"] = [];
30
+ script["speakers"].push({
31
+ speaker_id: speakerId,
32
+ display_name: name,
33
+ source_kind: sourceKind,
34
+ source_id: sourceId,
35
+ voice_desc: strOf(op["voice_desc"]).trim(),
36
+ });
37
+ applied.push(kind);
38
+ }
39
+ export function op_dialogue_speakers(__ctx) {
40
+ const { script, op, sourceText, applied, kind } = __ctx;
41
+ const [epId, sceneId, actionIndex] = parseActionRef(op["at"]);
42
+ const delivery = strOf(op["delivery"] || "simultaneous").trim();
43
+ if (delivery !== "single" && delivery !== "simultaneous" && delivery !== "group") {
44
+ throw opErr("SCRIPT OP BLOCKED: Delivery invalid", "Delivery invalid.", { required: ["delivery: single, simultaneous, or group"], received: [delivery], nextSteps: ["Use a supported delivery."], op: kind, errorCode: "DELIVERY_INVALID" });
45
+ }
46
+ let rawSpeakers = op["speakers"] ?? op["speaker"] ?? [];
47
+ if (typeof rawSpeakers === "string")
48
+ rawSpeakers = [rawSpeakers];
49
+ if (!isList(rawSpeakers) || rawSpeakers.length === 0) {
50
+ throw opErr("SCRIPT OP BLOCKED: Speakers empty", "Speakers empty.", { required: ["speaker list"], received: [JSON.stringify(op)], nextSteps: ["Pass at least one --speaker."], op: kind, errorCode: "SPEAKERS_EMPTY" });
51
+ }
52
+ const speakerIds = rawSpeakers.map((sid) => ensureSpeakerId(script, strOf(sid)));
53
+ const action = findAction(script, epId, sceneId, actionIndex);
54
+ action["type"] = "dialogue";
55
+ action["delivery"] = delivery;
56
+ action["speakers"] = speakerIds.map((sid) => ({ speaker_id: sid }));
57
+ delete action["lines"];
58
+ const actorId = speakerIds.length === 1 ? speakerSourceActorId(script, speakerIds[0]) : null;
59
+ if (actorId) {
60
+ action["actor_id"] = actorId;
61
+ action["speaker_id"] = speakerIds[0];
62
+ }
63
+ else {
64
+ delete action["actor_id"];
65
+ delete action["speaker_id"];
66
+ }
67
+ applied.push(kind);
68
+ }
69
+ export function op_dialogue_overlap(__ctx) {
70
+ const { script, op, sourceText, applied, kind } = __ctx;
71
+ const [epId, sceneId, actionIndex] = parseActionRef(op["at"]);
72
+ let rawLines = op["lines"] ?? op["line"] ?? [];
73
+ if (typeof rawLines === "string")
74
+ rawLines = [rawLines];
75
+ if (!isList(rawLines) || rawLines.length === 0) {
76
+ throw opErr("SCRIPT OP BLOCKED: Lines empty", "Lines empty.", { required: ["line list"], received: [JSON.stringify(op)], nextSteps: ["Pass at least one --line speaker_id:content."], op: kind, errorCode: "LINES_EMPTY" });
77
+ }
78
+ const lines = [];
79
+ for (const line of rawLines) {
80
+ let speakerId;
81
+ let content;
82
+ if (isDict(line)) {
83
+ speakerId = ensureSpeakerId(script, strOf(line["speaker_id"]));
84
+ content = strOf(line["content"]).trim();
85
+ }
86
+ else {
87
+ const rawLine = strOf(line);
88
+ if (!rawLine.includes(":") && !rawLine.includes(":")) {
89
+ throw opErr("SCRIPT OP BLOCKED: Line invalid", "Line invalid.", { required: ["speaker_id:content"], received: [rawLine], nextSteps: ["Use --line spk_xxx:对白."], op: kind, errorCode: "LINE_INVALID" });
90
+ }
91
+ const m = /^([^::]+)[::](.*)$/.exec(rawLine);
92
+ if (!m) {
93
+ throw opErr("SCRIPT OP BLOCKED: Line invalid", "Line invalid.", { required: ["speaker_id:content"], received: [rawLine], nextSteps: ["Use --line spk_xxx:对白."], op: kind, errorCode: "LINE_INVALID" });
94
+ }
95
+ speakerId = ensureSpeakerId(script, m[1]);
96
+ content = m[2].trim();
97
+ }
98
+ if (!content) {
99
+ throw opErr("SCRIPT OP BLOCKED: Line content empty", "Line content empty.", { required: ["non-empty line content"], received: [JSON.stringify(line)], nextSteps: ["Provide line content."], op: kind, errorCode: "LINE_CONTENT_EMPTY" });
100
+ }
101
+ lines.push({ speaker_id: speakerId, content });
102
+ }
103
+ const action = findAction(script, epId, sceneId, actionIndex);
104
+ action["type"] = "dialogue";
105
+ action["delivery"] = "overlap";
106
+ action["lines"] = lines;
107
+ action["content"] = lines.map((l) => l["content"]).join(" / ");
108
+ delete action["speakers"];
109
+ delete action["actor_id"];
110
+ delete action["speaker_id"];
111
+ applied.push(kind);
112
+ }
113
+ export function op_speaker_delete(__ctx) {
114
+ const { script, op, sourceText, applied, kind } = __ctx;
115
+ // Mirror of asset.delete for speakers: refs scrub or replace via
116
+ // --strategy. Speaker refs live in action.speaker_id /
117
+ // action.speakers[] / action.lines[].speaker_id only — no scene
118
+ // context entries to handle.
119
+ const speakerId = strOf(op["target"] ?? op["speaker_id"]).trim();
120
+ if (!speakerId) {
121
+ throw opErr("SCRIPT OP BLOCKED: Speaker id missing", "Speaker id missing.", {
122
+ required: ["target speaker id"],
123
+ received: [JSON.stringify(op)],
124
+ nextSteps: ["Pass a spk_id."],
125
+ op: kind,
126
+ errorCode: "SPEAKER_ID_MISSING",
127
+ });
128
+ }
129
+ const refs = collectAssetRefs(script, "speaker", speakerId);
130
+ const strategy = strOf(op["strategy"]).trim();
131
+ if (refs.length > 0 && !strategy) {
132
+ throw opErr("SCRIPT OP BLOCKED: Speaker has refs", `Speaker is referenced ${refs.length} time(s).`, {
133
+ required: ["--strategy replace|remove"],
134
+ received: [`refs=${refs.length}`],
135
+ nextSteps: ["Use --strategy replace --replacement <spk_id> to remap, or --strategy remove to scrub refs."],
136
+ op: kind,
137
+ errorCode: "SPEAKER_HAS_REFS",
138
+ });
139
+ }
140
+ const replacement = strOf(op["replacement"]).trim();
141
+ if (strategy === "replace") {
142
+ if (!replacement) {
143
+ throw opErr("SCRIPT OP BLOCKED: Replacement missing", "Replacement missing.", {
144
+ required: ["replacement speaker id"],
145
+ received: ["<empty>"],
146
+ nextSteps: ["Pass --replacement <spk_id>."],
147
+ op: kind,
148
+ errorCode: "REPLACEMENT_MISSING",
149
+ });
150
+ }
151
+ // Verify replacement exists.
152
+ if (!asList(script["speakers"]).some((s) => strOf(s["speaker_id"]) === replacement)) {
153
+ throw opErr("SCRIPT OP BLOCKED: Replacement speaker not found", "Replacement speaker not found.", {
154
+ required: ["existing speaker id"],
155
+ received: [replacement],
156
+ nextSteps: ["Add the speaker first or use a different replacement."],
157
+ op: kind,
158
+ errorCode: "SPEAKER_NOT_FOUND",
159
+ });
160
+ }
161
+ for (const ep of asList(script["episodes"])) {
162
+ for (const scene of asList(ep["scenes"])) {
163
+ for (const action of asList(scene["actions"])) {
164
+ if (strOf(action["speaker_id"]) === speakerId)
165
+ action["speaker_id"] = replacement;
166
+ const speakers = asList(action["speakers"]);
167
+ for (const sp of speakers) {
168
+ if (strOf(sp["speaker_id"]) === speakerId)
169
+ sp["speaker_id"] = replacement;
170
+ }
171
+ // Dedupe action.speakers[] in case the action already referenced
172
+ // the replacement: an unconditional rewrite + no dedupe would
173
+ // leave [spk_replacement, spk_replacement], breaking overlap/
174
+ // simultaneous delivery counts downstream.
175
+ if (action["speakers"] !== undefined) {
176
+ const seen = new Set();
177
+ action["speakers"] = speakers.filter((s) => {
178
+ const id = strOf(s["speaker_id"]);
179
+ if (seen.has(id))
180
+ return false;
181
+ seen.add(id);
182
+ return true;
183
+ });
184
+ }
185
+ const lines = asList(action["lines"]);
186
+ for (const line of lines) {
187
+ if (strOf(line["speaker_id"]) === speakerId)
188
+ line["speaker_id"] = replacement;
189
+ }
190
+ // lines[] semantically represents distinct overlapping lines
191
+ // (a speaker can legitimately overlap with themselves), so we do
192
+ // NOT dedupe lines — preserve overlap-with-self if it existed.
193
+ }
194
+ }
195
+ }
196
+ }
197
+ else if (strategy === "remove" || refs.length === 0) {
198
+ for (const ep of asList(script["episodes"])) {
199
+ for (const scene of asList(ep["scenes"])) {
200
+ for (const action of asList(scene["actions"])) {
201
+ if (strOf(action["speaker_id"]) === speakerId)
202
+ delete action["speaker_id"];
203
+ const speakers = asList(action["speakers"]).filter((s) => strOf(s["speaker_id"]) !== speakerId);
204
+ if (action["speakers"] !== undefined) {
205
+ if (speakers.length > 0)
206
+ action["speakers"] = speakers;
207
+ else
208
+ delete action["speakers"];
209
+ }
210
+ const lines = asList(action["lines"]).filter((l) => strOf(l["speaker_id"]) !== speakerId);
211
+ if (action["lines"] !== undefined) {
212
+ if (lines.length > 0)
213
+ action["lines"] = lines;
214
+ else
215
+ delete action["lines"];
216
+ }
217
+ }
218
+ }
219
+ }
220
+ }
221
+ else {
222
+ throw opErr("SCRIPT OP BLOCKED: Strategy invalid", "Strategy invalid.", {
223
+ required: ["strategy: replace or remove"],
224
+ received: [strategy],
225
+ nextSteps: ["Use replace (with --replacement) or remove."],
226
+ op: kind,
227
+ errorCode: "DELETE_STRATEGY_INVALID",
228
+ });
229
+ }
230
+ script["speakers"] = asList(script["speakers"]).filter((s) => strOf(s["speaker_id"]) !== speakerId);
231
+ applied.push(kind);
232
+ }
233
+ //# sourceMappingURL=ops-dialogue.js.map