Advertise AquaSynth live session schemas
This commit is contained in:
parent
a87e5aa4e0
commit
91c7c61832
4 changed files with 132 additions and 30 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
"schema": "gamecult.eve.provider_advertisement.v1",
|
"schema": "gamecult.eve.provider_advertisement.v1",
|
||||||
"providerId": "aquasynth.service",
|
"providerId": "aquasynth.service",
|
||||||
"title": "AquaSynth Service",
|
"title": "AquaSynth Service",
|
||||||
"description": "AquaSynth accepts .aqua DSL and Weksa utterance handoffs, compiles Faust-native instruments, returns live instrument handles, and publishes controllable synth state through CultMesh/Eve.",
|
"description": "AquaSynth accepts .aqua DSL and Weksa utterance handoffs, compiles Faust-native instruments, owns live streaming patch sessions, and publishes controllable synth state through CultMesh/Eve.",
|
||||||
"mode": "fixture-read-only",
|
"mode": "fixture-read-only",
|
||||||
"serviceVerse": {
|
"serviceVerse": {
|
||||||
"id": "verse.aquasynth.service.v1",
|
"id": "verse.aquasynth.service.v1",
|
||||||
|
|
@ -13,8 +13,10 @@
|
||||||
"patch graph semantics",
|
"patch graph semantics",
|
||||||
"Faust source emission",
|
"Faust source emission",
|
||||||
"native Faust compilation",
|
"native Faust compilation",
|
||||||
"compiled instrument handles",
|
"compiled instrument sessions",
|
||||||
"live control sessions",
|
"live streaming patch sessions",
|
||||||
|
"live session control state",
|
||||||
|
"live session lifecycle receipts",
|
||||||
"sample/render receipts",
|
"sample/render receipts",
|
||||||
"learned utterance embeddings and synth-driver controls"
|
"learned utterance embeddings and synth-driver controls"
|
||||||
],
|
],
|
||||||
|
|
@ -48,10 +50,18 @@
|
||||||
"id": "aquasynth.instrument",
|
"id": "aquasynth.instrument",
|
||||||
"kind": "instrument",
|
"kind": "instrument",
|
||||||
"cultMeshKey": "aquasynth.instrument/sessions",
|
"cultMeshKey": "aquasynth.instrument/sessions",
|
||||||
"authority": "AquaSynth native host and control-session owner",
|
"authority": "AquaSynth native host and live-session owner",
|
||||||
"schemas": [
|
"schemas": [
|
||||||
"aquasynth.instrument_handle.v0",
|
"aquasynth.compiled_instrument_session.v1",
|
||||||
"aquasynth.control_session.v0"
|
"aquasynth.live_instrument_session.v1",
|
||||||
|
"aquasynth.command.instrument_open.v1",
|
||||||
|
"aquasynth.command.instrument_control.v1",
|
||||||
|
"aquasynth.command.instrument_block.v1",
|
||||||
|
"aquasynth.command.instrument_close.v1",
|
||||||
|
"aquasynth.instrument_open_receipt.v1",
|
||||||
|
"aquasynth.instrument_control_receipt.v1",
|
||||||
|
"aquasynth.instrument_block_receipt.v1",
|
||||||
|
"aquasynth.instrument_close_receipt.v1"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -97,10 +107,14 @@
|
||||||
"family": "instrument",
|
"family": "instrument",
|
||||||
"namespace": "aquasynth.instrument.*",
|
"namespace": "aquasynth.instrument.*",
|
||||||
"witnesses": [
|
"witnesses": [
|
||||||
"aquasynth.instrument_handle.v0",
|
"aquasynth.compiled_instrument_session.v1",
|
||||||
"aquasynth.control_session.v0"
|
"aquasynth.live_instrument_session.v1",
|
||||||
|
"aquasynth.instrument_open_receipt.v1",
|
||||||
|
"aquasynth.instrument_control_receipt.v1",
|
||||||
|
"aquasynth.instrument_block_receipt.v1",
|
||||||
|
"aquasynth.instrument_close_receipt.v1"
|
||||||
],
|
],
|
||||||
"durableShape": ".aquasynth/sessions/{sessionId}.cc"
|
"durableShape": ".aquasynth/sessions/{sessionId}.cc and .aquasynth/live-sessions/{sessionId}.cc"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"family": "render",
|
"family": "render",
|
||||||
|
|
@ -189,16 +203,58 @@
|
||||||
"aquasynth.compiled_instrument.v0"
|
"aquasynth.compiled_instrument.v0"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "aquasynth.command.instrument.open",
|
||||||
|
"owner": "AquaSynth",
|
||||||
|
"acceptedByFixture": false,
|
||||||
|
"allowedInputs": [
|
||||||
|
".aqua DSL",
|
||||||
|
"block size",
|
||||||
|
"initial semantic control values"
|
||||||
|
],
|
||||||
|
"emits": [
|
||||||
|
"aquasynth.compiled_instrument_session.v1",
|
||||||
|
"aquasynth.live_instrument_session.v1",
|
||||||
|
"aquasynth.instrument_open_receipt.v1"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "aquasynth.command.instrument.control",
|
"id": "aquasynth.command.instrument.control",
|
||||||
"owner": "AquaSynth",
|
"owner": "AquaSynth",
|
||||||
"acceptedByFixture": false,
|
"acceptedByFixture": false,
|
||||||
"allowedInputs": [
|
"allowedInputs": [
|
||||||
"instrument handle",
|
"live session id",
|
||||||
"semantic control values"
|
"semantic control values"
|
||||||
],
|
],
|
||||||
"emits": [
|
"emits": [
|
||||||
"aquasynth.control_session.v0"
|
"aquasynth.live_instrument_session.v1",
|
||||||
|
"aquasynth.instrument_control_receipt.v1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "aquasynth.command.instrument.block",
|
||||||
|
"owner": "AquaSynth",
|
||||||
|
"acceptedByFixture": false,
|
||||||
|
"allowedInputs": [
|
||||||
|
"live session id",
|
||||||
|
"frame count",
|
||||||
|
"optional control values"
|
||||||
|
],
|
||||||
|
"emits": [
|
||||||
|
"aquasynth.live_instrument_session.v1",
|
||||||
|
"aquasynth.instrument_block_receipt.v1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "aquasynth.command.instrument.close",
|
||||||
|
"owner": "AquaSynth",
|
||||||
|
"acceptedByFixture": false,
|
||||||
|
"allowedInputs": [
|
||||||
|
"live session id"
|
||||||
|
],
|
||||||
|
"emits": [
|
||||||
|
"aquasynth.live_instrument_session.v1",
|
||||||
|
"aquasynth.instrument_close_receipt.v1"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -206,9 +262,10 @@
|
||||||
"owner": "AquaSynth",
|
"owner": "AquaSynth",
|
||||||
"acceptedByFixture": false,
|
"acceptedByFixture": false,
|
||||||
"allowedInputs": [
|
"allowedInputs": [
|
||||||
"instrument handle",
|
".aqua DSL",
|
||||||
"duration",
|
"duration",
|
||||||
"sample format"
|
"sample format",
|
||||||
|
"optional semantic control values"
|
||||||
],
|
],
|
||||||
"emits": [
|
"emits": [
|
||||||
"aquasynth.render_sample.v0"
|
"aquasynth.render_sample.v0"
|
||||||
|
|
@ -232,9 +289,9 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"loweringRules": [
|
"loweringRules": [
|
||||||
"AquaSynth owns learned embeddings, synth controls, Faust compilation, native binary lifetime, instrument handles, and sampled outputs.",
|
"AquaSynth owns learned embeddings, synth controls, Faust compilation, native binary lifetime, compiled instrument sessions, live streaming patch sessions, and sampled outputs.",
|
||||||
"Weksa owns writing-to-intent and intent-to-utterance lowering.",
|
"Weksa owns writing-to-intent and intent-to-utterance lowering.",
|
||||||
"Faust source and native binaries are emitted artifacts with typed receipts, not the semantic patch owner.",
|
"Faust source and native binaries are emitted artifacts with typed receipts, not the semantic patch owner.",
|
||||||
"Eve surfaces control and inspect sessions through CultMesh command documents; they do not own audio state."
|
"Eve surfaces control and inspect sessions through CultMesh command documents and retained live-session state; they do not own audio state."
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,12 @@ Outputs:
|
||||||
|
|
||||||
- `aquasynth.patch_graph.v0`
|
- `aquasynth.patch_graph.v0`
|
||||||
- `aquasynth.faust_source.v0`
|
- `aquasynth.faust_source.v0`
|
||||||
- `aquasynth.compiled_instrument.v0`
|
- `aquasynth.compiled_instrument_session.v1`
|
||||||
- `aquasynth.instrument_handle.v0`
|
- `aquasynth.live_instrument_session.v1`
|
||||||
- `aquasynth.control_session.v0`
|
- `aquasynth.instrument_open_receipt.v1`
|
||||||
|
- `aquasynth.instrument_control_receipt.v1`
|
||||||
|
- `aquasynth.instrument_block_receipt.v1`
|
||||||
|
- `aquasynth.instrument_close_receipt.v1`
|
||||||
- `aquasynth.render_sample.v0`
|
- `aquasynth.render_sample.v0`
|
||||||
- Eve/CultUI surfaces for patch graphs, compile products, live controls, and
|
- Eve/CultUI surfaces for patch graphs, compile products, live controls, and
|
||||||
speech-training/render status
|
speech-training/render status
|
||||||
|
|
|
||||||
|
|
@ -285,4 +285,5 @@
|
||||||
{"timestamp":"2026-06-15T03:04:00+01:00","topic":"aquasynth-daemon-live-session-control","claim":"AquaSynth live daemon control now belongs to AquaSynthDaemonService: instrument.open compiles once and keeps a native streaming patch instance, instrument.control updates session controls, and instrument.block processes fixed blocks through the same live session. AquaSynthCultNetDaemon only observes typed command documents and publishes typed receipts; it is not a second synth/session owner.","evidence":"src/AquaSynth.Faust/AquaSynthDaemonService.cs; src/AquaSynth.Faust/AquaSynthCultNetDaemon.cs; tools/AquaSynthDaemon/Program.cs; tests/AquaSynth.Dsl.Tests/AquaSynthDaemonServiceTests.cs; focused AquaSynthDaemonServiceTests passed 5/5"}
|
{"timestamp":"2026-06-15T03:04:00+01:00","topic":"aquasynth-daemon-live-session-control","claim":"AquaSynth live daemon control now belongs to AquaSynthDaemonService: instrument.open compiles once and keeps a native streaming patch instance, instrument.control updates session controls, and instrument.block processes fixed blocks through the same live session. AquaSynthCultNetDaemon only observes typed command documents and publishes typed receipts; it is not a second synth/session owner.","evidence":"src/AquaSynth.Faust/AquaSynthDaemonService.cs; src/AquaSynth.Faust/AquaSynthCultNetDaemon.cs; tools/AquaSynthDaemon/Program.cs; tests/AquaSynth.Dsl.Tests/AquaSynthDaemonServiceTests.cs; focused AquaSynthDaemonServiceTests passed 5/5"}
|
||||||
{"timestamp":"2026-06-15T03:31:00+01:00","topic":"aquasynth-daemon-live-session-lifecycle","claim":"Live AquaSynth daemon sessions now have an explicit release path. instrument.close is owned by AquaSynthDaemonService, removes and disposes the native streaming patch instance, writes a close receipt, leaves the compiled patch witness reusable, and makes subsequent instrument.block calls for that live session fail with session_not_found.","evidence":"src/AquaSynth.Faust/AquaSynthDaemonService.cs; src/AquaSynth.Faust/AquaSynthCultNetDaemon.cs; tools/AquaSynthDaemon/Program.cs; tests/AquaSynth.Dsl.Tests/AquaSynthDaemonServiceTests.cs"}
|
{"timestamp":"2026-06-15T03:31:00+01:00","topic":"aquasynth-daemon-live-session-lifecycle","claim":"Live AquaSynth daemon sessions now have an explicit release path. instrument.close is owned by AquaSynthDaemonService, removes and disposes the native streaming patch instance, writes a close receipt, leaves the compiled patch witness reusable, and makes subsequent instrument.block calls for that live session fail with session_not_found.","evidence":"src/AquaSynth.Faust/AquaSynthDaemonService.cs; src/AquaSynth.Faust/AquaSynthCultNetDaemon.cs; tools/AquaSynthDaemon/Program.cs; tests/AquaSynth.Dsl.Tests/AquaSynthDaemonServiceTests.cs"}
|
||||||
{"timestamp":"2026-06-15T03:55:00+01:00","topic":"aquasynth-daemon-live-session-state","claim":"Live AquaSynth daemon sessions now publish retained aquasynth.live_instrument_session.v1 state under .aquasynth/live-sessions/{sessionId}.cc. AquaSynthDaemonService rewrites it on open/control/block/close with status, current controls, block size, next sequence, and last command/receipt ids, so clients can inspect current session truth without replaying event receipts.","evidence":"src/AquaSynth.Faust/AquaSynthDaemonService.cs; src/AquaSynth.Faust/AquaSynthCultNetDaemon.cs; tests/AquaSynth.Dsl.Tests/AquaSynthDaemonServiceTests.cs; docs/verse-service-contract.md; focused AquaSynthDaemonServiceTests passed 5/5"}
|
{"timestamp":"2026-06-15T03:55:00+01:00","topic":"aquasynth-daemon-live-session-state","claim":"Live AquaSynth daemon sessions now publish retained aquasynth.live_instrument_session.v1 state under .aquasynth/live-sessions/{sessionId}.cc. AquaSynthDaemonService rewrites it on open/control/block/close with status, current controls, block size, next sequence, and last command/receipt ids, so clients can inspect current session truth without replaying event receipts.","evidence":"src/AquaSynth.Faust/AquaSynthDaemonService.cs; src/AquaSynth.Faust/AquaSynthCultNetDaemon.cs; tests/AquaSynth.Dsl.Tests/AquaSynthDaemonServiceTests.cs; docs/verse-service-contract.md; focused AquaSynthDaemonServiceTests passed 5/5"}
|
||||||
|
{"timestamp":"2026-06-15T04:16:00+01:00","topic":"aquasynth-provider-advertisement-live-session-schemas","claim":"The static Eve provider advertisement now exposes the actual live daemon session command/state schemas instead of the old abstract instrument_handle/control_session pair. The generator owns the fixture, and the generated JSON advertises compiled sessions, retained live session state, open/control/block/close commands, and matching receipts.","evidence":"tools/export-provider-advertisement.mjs; docs/fixtures/aquasynth-provider-advertisement.json; docs/verse-service-contract.md"}
|
||||||
{"timestamp":"2026-06-15T02:35:00+01:00","topic":"greater-daemon-composition-rule","claim":"The daemonic swarm docs now state the greater-daemon composition rule: smaller daemon capabilities compose upward through typed command/receipt surfaces, while larger workflow daemons own orchestration and aggregate receipts without absorbing lower-daemon truth, provenance, rejections, or failures.","evidence":"docs/gamecult-daemonic-swarm.md; docs/daemonic-swarm-prior-work.md"}
|
{"timestamp":"2026-06-15T02:35:00+01:00","topic":"greater-daemon-composition-rule","claim":"The daemonic swarm docs now state the greater-daemon composition rule: smaller daemon capabilities compose upward through typed command/receipt surfaces, while larger workflow daemons own orchestration and aggregate receipts without absorbing lower-daemon truth, provenance, rejections, or failures.","evidence":"docs/gamecult-daemonic-swarm.md; docs/daemonic-swarm-prior-work.md"}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ const document = {
|
||||||
providerId: "aquasynth.service",
|
providerId: "aquasynth.service",
|
||||||
title: "AquaSynth Service",
|
title: "AquaSynth Service",
|
||||||
description:
|
description:
|
||||||
"AquaSynth accepts .aqua DSL and Weksa utterance handoffs, compiles Faust-native instruments, returns live instrument handles, and publishes controllable synth state through CultMesh/Eve.",
|
"AquaSynth accepts .aqua DSL and Weksa utterance handoffs, compiles Faust-native instruments, owns live streaming patch sessions, and publishes controllable synth state through CultMesh/Eve.",
|
||||||
mode: "fixture-read-only",
|
mode: "fixture-read-only",
|
||||||
serviceVerse: {
|
serviceVerse: {
|
||||||
id: "verse.aquasynth.service.v1",
|
id: "verse.aquasynth.service.v1",
|
||||||
|
|
@ -18,8 +18,10 @@ const document = {
|
||||||
"patch graph semantics",
|
"patch graph semantics",
|
||||||
"Faust source emission",
|
"Faust source emission",
|
||||||
"native Faust compilation",
|
"native Faust compilation",
|
||||||
"compiled instrument handles",
|
"compiled instrument sessions",
|
||||||
"live control sessions",
|
"live streaming patch sessions",
|
||||||
|
"live session control state",
|
||||||
|
"live session lifecycle receipts",
|
||||||
"sample/render receipts",
|
"sample/render receipts",
|
||||||
"learned utterance embeddings and synth-driver controls",
|
"learned utterance embeddings and synth-driver controls",
|
||||||
],
|
],
|
||||||
|
|
@ -44,8 +46,19 @@ const document = {
|
||||||
id: "aquasynth.instrument",
|
id: "aquasynth.instrument",
|
||||||
kind: "instrument",
|
kind: "instrument",
|
||||||
cultMeshKey: "aquasynth.instrument/sessions",
|
cultMeshKey: "aquasynth.instrument/sessions",
|
||||||
authority: "AquaSynth native host and control-session owner",
|
authority: "AquaSynth native host and live-session owner",
|
||||||
schemas: ["aquasynth.instrument_handle.v0", "aquasynth.control_session.v0"],
|
schemas: [
|
||||||
|
"aquasynth.compiled_instrument_session.v1",
|
||||||
|
"aquasynth.live_instrument_session.v1",
|
||||||
|
"aquasynth.command.instrument_open.v1",
|
||||||
|
"aquasynth.command.instrument_control.v1",
|
||||||
|
"aquasynth.command.instrument_block.v1",
|
||||||
|
"aquasynth.command.instrument_close.v1",
|
||||||
|
"aquasynth.instrument_open_receipt.v1",
|
||||||
|
"aquasynth.instrument_control_receipt.v1",
|
||||||
|
"aquasynth.instrument_block_receipt.v1",
|
||||||
|
"aquasynth.instrument_close_receipt.v1",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "aquasynth.speech",
|
id: "aquasynth.speech",
|
||||||
|
|
@ -78,8 +91,15 @@ const document = {
|
||||||
{
|
{
|
||||||
family: "instrument",
|
family: "instrument",
|
||||||
namespace: "aquasynth.instrument.*",
|
namespace: "aquasynth.instrument.*",
|
||||||
witnesses: ["aquasynth.instrument_handle.v0", "aquasynth.control_session.v0"],
|
witnesses: [
|
||||||
durableShape: ".aquasynth/sessions/{sessionId}.cc",
|
"aquasynth.compiled_instrument_session.v1",
|
||||||
|
"aquasynth.live_instrument_session.v1",
|
||||||
|
"aquasynth.instrument_open_receipt.v1",
|
||||||
|
"aquasynth.instrument_control_receipt.v1",
|
||||||
|
"aquasynth.instrument_block_receipt.v1",
|
||||||
|
"aquasynth.instrument_close_receipt.v1",
|
||||||
|
],
|
||||||
|
durableShape: ".aquasynth/sessions/{sessionId}.cc and .aquasynth/live-sessions/{sessionId}.cc",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
family: "render",
|
family: "render",
|
||||||
|
|
@ -138,18 +158,39 @@ const document = {
|
||||||
allowedInputs: [".aqua DSL", "compile target", "host architecture"],
|
allowedInputs: [".aqua DSL", "compile target", "host architecture"],
|
||||||
emits: ["aquasynth.patch_graph.v0", "aquasynth.faust_source.v0", "aquasynth.compiled_instrument.v0"],
|
emits: ["aquasynth.patch_graph.v0", "aquasynth.faust_source.v0", "aquasynth.compiled_instrument.v0"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "aquasynth.command.instrument.open",
|
||||||
|
owner: "AquaSynth",
|
||||||
|
acceptedByFixture: false,
|
||||||
|
allowedInputs: [".aqua DSL", "block size", "initial semantic control values"],
|
||||||
|
emits: ["aquasynth.compiled_instrument_session.v1", "aquasynth.live_instrument_session.v1", "aquasynth.instrument_open_receipt.v1"],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "aquasynth.command.instrument.control",
|
id: "aquasynth.command.instrument.control",
|
||||||
owner: "AquaSynth",
|
owner: "AquaSynth",
|
||||||
acceptedByFixture: false,
|
acceptedByFixture: false,
|
||||||
allowedInputs: ["instrument handle", "semantic control values"],
|
allowedInputs: ["live session id", "semantic control values"],
|
||||||
emits: ["aquasynth.control_session.v0"],
|
emits: ["aquasynth.live_instrument_session.v1", "aquasynth.instrument_control_receipt.v1"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "aquasynth.command.instrument.block",
|
||||||
|
owner: "AquaSynth",
|
||||||
|
acceptedByFixture: false,
|
||||||
|
allowedInputs: ["live session id", "frame count", "optional control values"],
|
||||||
|
emits: ["aquasynth.live_instrument_session.v1", "aquasynth.instrument_block_receipt.v1"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "aquasynth.command.instrument.close",
|
||||||
|
owner: "AquaSynth",
|
||||||
|
acceptedByFixture: false,
|
||||||
|
allowedInputs: ["live session id"],
|
||||||
|
emits: ["aquasynth.live_instrument_session.v1", "aquasynth.instrument_close_receipt.v1"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "aquasynth.command.instrument.sample",
|
id: "aquasynth.command.instrument.sample",
|
||||||
owner: "AquaSynth",
|
owner: "AquaSynth",
|
||||||
acceptedByFixture: false,
|
acceptedByFixture: false,
|
||||||
allowedInputs: ["instrument handle", "duration", "sample format"],
|
allowedInputs: [".aqua DSL", "duration", "sample format", "optional semantic control values"],
|
||||||
emits: ["aquasynth.render_sample.v0"],
|
emits: ["aquasynth.render_sample.v0"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -162,10 +203,10 @@ const document = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
loweringRules: [
|
loweringRules: [
|
||||||
"AquaSynth owns learned embeddings, synth controls, Faust compilation, native binary lifetime, instrument handles, and sampled outputs.",
|
"AquaSynth owns learned embeddings, synth controls, Faust compilation, native binary lifetime, compiled instrument sessions, live streaming patch sessions, and sampled outputs.",
|
||||||
"Weksa owns writing-to-intent and intent-to-utterance lowering.",
|
"Weksa owns writing-to-intent and intent-to-utterance lowering.",
|
||||||
"Faust source and native binaries are emitted artifacts with typed receipts, not the semantic patch owner.",
|
"Faust source and native binaries are emitted artifacts with typed receipts, not the semantic patch owner.",
|
||||||
"Eve surfaces control and inspect sessions through CultMesh command documents; they do not own audio state.",
|
"Eve surfaces control and inspect sessions through CultMesh command documents and retained live-session state; they do not own audio state.",
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue