@mengine/medeo-tool 1.2.1-alpha.0 → 1.2.1-alpha.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/README.md +71 -8
- package/dist/entity-contract-DycLxdQ5.d.mts +220 -0
- package/dist/index.d.mts +224 -21
- package/dist/index.mjs +1326 -885
- package/dist/index.mjs.map +1 -1
- package/dist/sandbox-api.d.mts +467 -715
- package/dist/script-session-CHyIUBkO.mjs +1841 -0
- package/dist/script-session-CHyIUBkO.mjs.map +1 -0
- package/dist/worker-entry.d.mts +6 -3
- package/dist/worker-entry.mjs +308 -8
- package/dist/worker-entry.mjs.map +1 -1
- package/package.json +4 -3
- package/dist/script-session-DdPA4tTf.mjs +0 -447
- package/dist/script-session-DdPA4tTf.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# `@mengine/medeo-tool`
|
|
2
2
|
|
|
3
|
-
Node tool surface for editing
|
|
4
|
-
edit sandbox.
|
|
3
|
+
Node tool surface for editing the authoritative Medeo Entity/Relation graph
|
|
4
|
+
through the deterministic edit sandbox. Timeline editing is entity editing,
|
|
5
|
+
not a legacy document mutation followed by optional entity registration.
|
|
5
6
|
|
|
6
7
|
The package owns the complete tool path:
|
|
7
8
|
|
|
8
9
|
- compact snapshot projection;
|
|
9
10
|
- trusted worker execution of an agent-authored JavaScript edit script;
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
11
|
+
- entity-command `ChangePlan` caching;
|
|
12
|
+
- complete Entity/Relation snapshot reads and revision-CAS commits;
|
|
13
|
+
- per-document serialization, document cache, and shutdown.
|
|
13
14
|
|
|
14
15
|
A host supplies only environment facts: Mengine HTTP origin, optional request
|
|
15
16
|
identity headers, `fetch`, and a stable agent peer id.
|
|
@@ -27,7 +28,8 @@ const snapshot = await medeo.handle({ op: 'snapshot', doc_id: docId });
|
|
|
27
28
|
const run = await medeo.handle({
|
|
28
29
|
op: 'run-edit-script',
|
|
29
30
|
doc_id: docId,
|
|
30
|
-
script: '
|
|
31
|
+
script: 'edit.updateClip({ clipEntityId: inputs.clip_id, payload: { volume: -6 } })',
|
|
32
|
+
inputs: { clip_id: existingClipEntityId },
|
|
31
33
|
});
|
|
32
34
|
if (!run.ok || run.op !== 'run-edit-script') throw new Error('edit script failed');
|
|
33
35
|
const commit = await medeo.handle({
|
|
@@ -36,9 +38,70 @@ const commit = await medeo.handle({
|
|
|
36
38
|
plan_id: run.plan_id,
|
|
37
39
|
});
|
|
38
40
|
|
|
41
|
+
// Generation facts are recalled by the host and passed into the sandbox.
|
|
42
|
+
// This example is text-to-image: there is no invented input-media entity.
|
|
43
|
+
const entityRun = await medeo.handle({
|
|
44
|
+
op: 'run-edit-script',
|
|
45
|
+
doc_id: docId,
|
|
46
|
+
inputs: { output_asset_id: 'asset-from-host', track_entity_id: existingTrackEntityId },
|
|
47
|
+
script: `
|
|
48
|
+
if (typeof inputs.output_asset_id !== 'string' || typeof inputs.track_entity_id !== 'string') {
|
|
49
|
+
throw new Error('missing recalled generation facts');
|
|
50
|
+
}
|
|
51
|
+
const asset = entities.importAsset({ asset_id: inputs.output_asset_id });
|
|
52
|
+
const output = entities.create({
|
|
53
|
+
entity_kind: 'image',
|
|
54
|
+
payload: { extent: { kind: 'unbounded', start: 0 }, sampling: 'constant', coordinateSpace: 'ms' },
|
|
55
|
+
});
|
|
56
|
+
relations.link({ relation_kind: 'physical-asset', endpoint_0_entity_id: output, endpoint_1_entity_id: asset });
|
|
57
|
+
edit.insertClip({
|
|
58
|
+
trackEntityId: inputs.track_entity_id,
|
|
59
|
+
contentEntityId: output,
|
|
60
|
+
sourceRange: { start: 0, end: 1 },
|
|
61
|
+
duration: { mode: 'fixed', value: 5000 },
|
|
62
|
+
targetRange: { start: 0, end: 5000 },
|
|
63
|
+
});
|
|
64
|
+
`,
|
|
65
|
+
});
|
|
66
|
+
|
|
39
67
|
await medeo.close();
|
|
40
68
|
```
|
|
41
69
|
|
|
42
70
|
The sandbox has no network, storage, clock, or generation access. Materialize
|
|
43
|
-
speech/media side effects in the host first and pass stable facts
|
|
44
|
-
`inputs`.
|
|
71
|
+
or recall speech/media side effects in the host first and pass stable facts
|
|
72
|
+
through `inputs`. There is no automatic Asset→Entity projection: the model
|
|
73
|
+
selects and imports assets, creates only known typed Entities, and links them
|
|
74
|
+
explicitly. Assets and media Entities are intentionally not one-to-one.
|
|
75
|
+
|
|
76
|
+
The production tool exposes native Clip/Marker editing plus visual placement,
|
|
77
|
+
voiceover, caption, and BGM helpers through its generated sandbox interface.
|
|
78
|
+
They preserve structural and anchor relations in the same plan. Timeline targets
|
|
79
|
+
are Entity IDs, never raw asset IDs or URLs. Asset import, media creation, factual `generated` relations, and Clip
|
|
80
|
+
insertion belong in the **same entity plan**. `generated` retains output at
|
|
81
|
+
endpoint 0 and input at endpoint 1, while lookup can use either endpoint.
|
|
82
|
+
|
|
83
|
+
Entities own their facts: SequenceMarker owns source/target ranges, duration,
|
|
84
|
+
and time remapping; Clip owns volume; Track owns role/visibility. Cross-entity
|
|
85
|
+
references are Relations. There is no separate orientation or timing profile.
|
|
86
|
+
|
|
87
|
+
The server validates and commits the graph and its read-only Loro projection in
|
|
88
|
+
one database transaction. `deleted_entity_ids` and `deleted_relation_ids` make
|
|
89
|
+
deletion explicit; dropped rows without deletion intent are rejected. After
|
|
90
|
+
cutover, raw Loro `/updates` cannot mutate the document. Legacy standalone
|
|
91
|
+
sandbox exports remain library compatibility APIs, not a production-tool mode.
|
|
92
|
+
|
|
93
|
+
Existing nonempty legacy documents require a separate version-CAS migration
|
|
94
|
+
whose projection preserves existing editing facts. `snapshot` identifies that
|
|
95
|
+
requirement and the media IDs needing factual metadata. Call
|
|
96
|
+
`{op:'migrate-legacy',doc_id,asset_facts}` explicitly, then take a fresh snapshot
|
|
97
|
+
before editing. The package reads the canonical document and current Loro
|
|
98
|
+
version itself; it does not accept a caller-supplied snapshot or version, use a
|
|
99
|
+
stale cached document after a failed pull, or combine migration with a new edit.
|
|
100
|
+
Repeating migration on an entity timeline is read-only. An uncertain submission
|
|
101
|
+
must be inspected and retried, never reported as committed.
|
|
102
|
+
|
|
103
|
+
The compatibility reader covers the existing four lanes: Image/Video, Voice,
|
|
104
|
+
Caption, and background Audio. Coordinates are whole milliseconds for this
|
|
105
|
+
reader. It preserves linear visual remapping (`{kind:'linear',rate:2}`), including
|
|
106
|
+
image display speed, and explicit anchor chains. Nonlinear remapping and multiple
|
|
107
|
+
visual overlay tracks are not existing editor features and remain unsupported.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
//#region src/entity/entity-contract.d.ts
|
|
2
|
+
type JsonPrimitive = string | number | boolean | null;
|
|
3
|
+
type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
|
|
4
|
+
interface JsonObject {
|
|
5
|
+
[key: string]: JsonValue;
|
|
6
|
+
}
|
|
7
|
+
type KnownEntityKind = 'axvideo' | 'timeline' | 'track' | 'clip' | 'asset' | 'video' | 'audio' | 'voice' | 'image' | 'sequence-marker' | 'viewport' | 'audio-script' | 'phonetic-script' | 'caption';
|
|
8
|
+
type KnownRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline' | 'physical-asset' | 'generated' | 'phonetic-script-provenance' | 'caption-provenance' | 'caption-alignment' | 'clip-anchor' | 'audio-script-render';
|
|
9
|
+
type AuthorableRelationKind = Exclude<KnownRelationKind, 'generated'>;
|
|
10
|
+
interface BoundedNativeSequencePayload extends JsonObject {
|
|
11
|
+
/** Factual coordinates from recalled media metadata; never invent an end/duration. */
|
|
12
|
+
extent: {
|
|
13
|
+
kind: 'bounded';
|
|
14
|
+
start: number;
|
|
15
|
+
end: number;
|
|
16
|
+
};
|
|
17
|
+
sampling: 'native';
|
|
18
|
+
coordinateSpace: JsonValue;
|
|
19
|
+
}
|
|
20
|
+
interface UnboundedConstantSequencePayload extends JsonObject {
|
|
21
|
+
extent: {
|
|
22
|
+
kind: 'unbounded';
|
|
23
|
+
start: number;
|
|
24
|
+
};
|
|
25
|
+
sampling: 'constant';
|
|
26
|
+
coordinateSpace: JsonValue;
|
|
27
|
+
}
|
|
28
|
+
interface BoundedDerivedSequencePayload extends JsonObject {
|
|
29
|
+
extent: {
|
|
30
|
+
kind: 'bounded';
|
|
31
|
+
start: number;
|
|
32
|
+
end: number;
|
|
33
|
+
};
|
|
34
|
+
sampling: 'derived';
|
|
35
|
+
coordinateSpace: JsonValue;
|
|
36
|
+
}
|
|
37
|
+
type ScriptTextSegment = JsonObject & {
|
|
38
|
+
segmentId: string;
|
|
39
|
+
text: string;
|
|
40
|
+
language?: string;
|
|
41
|
+
};
|
|
42
|
+
interface EntityPayloadByKind {
|
|
43
|
+
axvideo: BoundedDerivedSequencePayload;
|
|
44
|
+
timeline: JsonObject;
|
|
45
|
+
track: JsonObject & {
|
|
46
|
+
hidden?: boolean;
|
|
47
|
+
role?: string;
|
|
48
|
+
};
|
|
49
|
+
clip: JsonObject;
|
|
50
|
+
/** Asset-owned metadata. Peer media associations belong in physical-asset Relations. */
|
|
51
|
+
asset: JsonObject;
|
|
52
|
+
video: BoundedNativeSequencePayload;
|
|
53
|
+
audio: BoundedNativeSequencePayload;
|
|
54
|
+
voice: BoundedNativeSequencePayload;
|
|
55
|
+
image: UnboundedConstantSequencePayload;
|
|
56
|
+
'sequence-marker': JsonObject & {
|
|
57
|
+
sourceRange: {
|
|
58
|
+
start: number;
|
|
59
|
+
end: number;
|
|
60
|
+
};
|
|
61
|
+
targetRange?: {
|
|
62
|
+
start: number;
|
|
63
|
+
end: number;
|
|
64
|
+
};
|
|
65
|
+
duration: {
|
|
66
|
+
mode: 'from-source';
|
|
67
|
+
} | {
|
|
68
|
+
mode: 'fixed';
|
|
69
|
+
value: number;
|
|
70
|
+
};
|
|
71
|
+
timeRemapping?: JsonValue;
|
|
72
|
+
anchorOffset?: number;
|
|
73
|
+
durationPolicy?: 'timeline';
|
|
74
|
+
};
|
|
75
|
+
viewport: JsonObject;
|
|
76
|
+
'audio-script': JsonObject & {
|
|
77
|
+
segments: ScriptTextSegment[];
|
|
78
|
+
};
|
|
79
|
+
'phonetic-script': JsonObject & {
|
|
80
|
+
segments: ScriptTextSegment[];
|
|
81
|
+
};
|
|
82
|
+
caption: BoundedNativeSequencePayload;
|
|
83
|
+
}
|
|
84
|
+
interface SandboxEntity<K extends KnownEntityKind = KnownEntityKind> {
|
|
85
|
+
entity_id: string;
|
|
86
|
+
entity_kind: K;
|
|
87
|
+
payload: EntityPayloadByKind[K];
|
|
88
|
+
}
|
|
89
|
+
interface SandboxRelation {
|
|
90
|
+
relation_id: string;
|
|
91
|
+
relation_kind: KnownRelationKind;
|
|
92
|
+
endpoint_0_entity_id: string;
|
|
93
|
+
endpoint_1_entity_id: string;
|
|
94
|
+
metadata: JsonObject;
|
|
95
|
+
trace: JsonObject;
|
|
96
|
+
}
|
|
97
|
+
interface EntityStoreSnapshot {
|
|
98
|
+
revision: number;
|
|
99
|
+
entities: SandboxEntity[];
|
|
100
|
+
relations: SandboxRelation[];
|
|
101
|
+
}
|
|
102
|
+
type CreateEntityInput = { [K in KnownEntityKind]: {
|
|
103
|
+
entity_id?: string;
|
|
104
|
+
entity_kind: K;
|
|
105
|
+
payload: EntityPayloadByKind[K];
|
|
106
|
+
} }[KnownEntityKind];
|
|
107
|
+
interface UpdateEntityInput {
|
|
108
|
+
entity_id: string;
|
|
109
|
+
payload: JsonObject;
|
|
110
|
+
}
|
|
111
|
+
interface DeleteEntityInput {
|
|
112
|
+
entity_id: string;
|
|
113
|
+
}
|
|
114
|
+
interface ImportAssetInput {
|
|
115
|
+
asset_id: string;
|
|
116
|
+
entity_id?: string;
|
|
117
|
+
payload?: JsonObject;
|
|
118
|
+
}
|
|
119
|
+
type EmptyRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline';
|
|
120
|
+
interface LinkRelationBase {
|
|
121
|
+
relation_id?: string;
|
|
122
|
+
endpoint_0_entity_id: string;
|
|
123
|
+
endpoint_1_entity_id: string;
|
|
124
|
+
trace?: JsonObject;
|
|
125
|
+
}
|
|
126
|
+
type LinkRelationInput = (LinkRelationBase & {
|
|
127
|
+
relation_kind: EmptyRelationKind;
|
|
128
|
+
metadata?: {
|
|
129
|
+
[key: string]: never;
|
|
130
|
+
};
|
|
131
|
+
}) | (LinkRelationBase & {
|
|
132
|
+
relation_kind: 'physical-asset';
|
|
133
|
+
metadata?: JsonObject;
|
|
134
|
+
}) | (LinkRelationBase & {
|
|
135
|
+
relation_kind: 'phonetic-script-provenance' | 'caption-provenance';
|
|
136
|
+
metadata: JsonObject & {
|
|
137
|
+
segmentAlignment: JsonValue;
|
|
138
|
+
};
|
|
139
|
+
}) | (LinkRelationBase & {
|
|
140
|
+
relation_kind: 'caption-alignment';
|
|
141
|
+
metadata: JsonObject & {
|
|
142
|
+
alignment: JsonValue;
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
interface LinkGeneratedRelationInput {
|
|
146
|
+
relation_id?: string;
|
|
147
|
+
output_entity_id: string;
|
|
148
|
+
input_entity_id: string;
|
|
149
|
+
trace?: JsonObject;
|
|
150
|
+
}
|
|
151
|
+
interface LinkClipAnchorRelationInput {
|
|
152
|
+
relation_id?: string;
|
|
153
|
+
child_clip_entity_id: string;
|
|
154
|
+
host_clip_entity_id: string;
|
|
155
|
+
trace?: JsonObject;
|
|
156
|
+
}
|
|
157
|
+
interface LinkAudioScriptRenderRelationInput {
|
|
158
|
+
relation_id?: string;
|
|
159
|
+
output_entity_id: string;
|
|
160
|
+
script_entity_id: string;
|
|
161
|
+
trace?: JsonObject;
|
|
162
|
+
}
|
|
163
|
+
interface UnlinkRelationInput {
|
|
164
|
+
relation_id: string;
|
|
165
|
+
}
|
|
166
|
+
type EntityCommand = {
|
|
167
|
+
kind: 'create-entity';
|
|
168
|
+
entity: SandboxEntity;
|
|
169
|
+
} | {
|
|
170
|
+
kind: 'update-entity';
|
|
171
|
+
entity_id: string;
|
|
172
|
+
payload: JsonObject;
|
|
173
|
+
} | {
|
|
174
|
+
kind: 'delete-entity';
|
|
175
|
+
entity_id: string;
|
|
176
|
+
} | {
|
|
177
|
+
kind: 'link-relation';
|
|
178
|
+
relation: SandboxRelation;
|
|
179
|
+
} | {
|
|
180
|
+
kind: 'unlink-relation';
|
|
181
|
+
relation_id: string;
|
|
182
|
+
};
|
|
183
|
+
interface EntityPlanState {
|
|
184
|
+
base_revision: number;
|
|
185
|
+
commands: readonly EntityCommand[];
|
|
186
|
+
rows: EntityStoreSnapshot;
|
|
187
|
+
deleted_entity_ids: readonly string[];
|
|
188
|
+
deleted_relation_ids: readonly string[];
|
|
189
|
+
}
|
|
190
|
+
interface EntityFacade {
|
|
191
|
+
list(): SandboxEntity[];
|
|
192
|
+
get(entityId: string): SandboxEntity | null;
|
|
193
|
+
/** Return every explicitly imported Asset entity for a Memota asset id. */
|
|
194
|
+
findByAssetId(assetId: string): SandboxEntity<'asset'>[];
|
|
195
|
+
create(input: CreateEntityInput): string;
|
|
196
|
+
/** Replace one Entity's owned payload without changing its identity or kind. */
|
|
197
|
+
update(input: UpdateEntityInput): void;
|
|
198
|
+
/** Delete an Entity only after all of its incident Relations have been explicitly unlinked. */
|
|
199
|
+
delete(input: DeleteEntityInput): void;
|
|
200
|
+
/** Import one physical asset without implying a one-to-one media Entity mapping. */
|
|
201
|
+
importAsset(input: ImportAssetInput): string;
|
|
202
|
+
}
|
|
203
|
+
interface RelationFacade {
|
|
204
|
+
list(): SandboxRelation[];
|
|
205
|
+
/** Incident lookup is endpoint-agnostic; persisted endpoint positions stay unchanged. */
|
|
206
|
+
of(entityId: string, relationKind?: KnownRelationKind): SandboxRelation[];
|
|
207
|
+
/** For physical-asset use sequence media as endpoint 0 and Asset as endpoint 1. */
|
|
208
|
+
link(input: LinkRelationInput): string;
|
|
209
|
+
/** Author ordered generated(output,input); generic link() deliberately rejects this kind. */
|
|
210
|
+
linkGenerated(input: LinkGeneratedRelationInput): string;
|
|
211
|
+
/** Author ordered clip-anchor(child,host) without positional endpoint ambiguity. */
|
|
212
|
+
linkClipAnchor(input: LinkClipAnchorRelationInput): string;
|
|
213
|
+
/** Author ordered audio-script-render(output,script) without positional endpoint ambiguity. */
|
|
214
|
+
linkAudioScriptRender(input: LinkAudioScriptRenderRelationInput): string;
|
|
215
|
+
/** Remove a Relation by identity; endpoint replacement is an explicit unlink plus link. */
|
|
216
|
+
unlink(input: UnlinkRelationInput): void;
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
219
|
+
export { SandboxEntity as _, EntityFacade as a, UpdateEntityInput as b, ImportAssetInput as c, JsonValue as d, KnownEntityKind as f, RelationFacade as g, LinkRelationInput as h, EntityCommand as i, JsonObject as l, LinkGeneratedRelationInput as m, CreateEntityInput as n, EntityPlanState as o, KnownRelationKind as p, DeleteEntityInput as r, EntityStoreSnapshot as s, AuthorableRelationKind as t, JsonPrimitive as u, SandboxRelation as v, UnlinkRelationInput as y };
|
|
220
|
+
//# sourceMappingURL=entity-contract-DycLxdQ5.d.mts.map
|