@platforma-sdk/workflow-tengo 5.5.1 → 5.5.3
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/dist/tengo/lib/exec.R.internal.lib.tengo +93 -24
- package/dist/tengo/lib/exec.R.lib.tengo +12 -10
- package/dist/tengo/lib/exec.runcmd.lib.tengo +36 -10
- package/dist/tengo/tpl/annotations.compute.plj.gz +0 -0
- package/dist/tengo/tpl/anonymize.deanonymize.plj.gz +0 -0
- package/dist/tengo/tpl/anonymize.impl.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.create_workdir_from_storage.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.download_blob.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.run_hello_world.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.run_hello_world_py.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.upload_blob.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.upload_template.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.workdir_type_name_extractor.plj.gz +0 -0
- package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.conda.create-env.plj.gz +0 -0
- package/dist/tengo/tpl/exec.delayed-compute-allocation.impl.plj.gz +0 -0
- package/dist/tengo/tpl/exec.exec-impl.plj.gz +0 -0
- package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
- package/dist/tengo/tpl/exec.load-software.impl.plj.gz +0 -0
- package/dist/tengo/tpl/exec.monetization_info.plj.gz +0 -0
- package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.venv-bin-path.plj.gz +0 -0
- package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-parquet-pcolumn-chunk.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.parse-to-json.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
- package/dist/tengo/tpl/pt.import-dir.plj.gz +0 -0
- package/dist/tengo/tpl/pt.workflow-run.plj.gz +0 -0
- package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
- package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
- package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
- package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
- package/package.json +3 -3
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
2
|
-
feats := import("@platforma-sdk/workflow-tengo:feats")
|
|
3
2
|
|
|
4
3
|
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
5
|
-
runcmd := import("@platforma-sdk/workflow-tengo:exec.runcmd")
|
|
6
4
|
path := import("@platforma-sdk/workflow-tengo:path")
|
|
5
|
+
smart := import("@platforma-sdk/workflow-tengo:smart")
|
|
6
|
+
|
|
7
|
+
runcmd := import("@platforma-sdk/workflow-tengo:exec.runcmd")
|
|
7
8
|
desc := import("@platforma-sdk/workflow-tengo:exec.descriptor")
|
|
8
|
-
constants := import("@platforma-sdk/workflow-tengo:exec.constants")
|
|
9
|
-
pkg := import("@platforma-sdk/workflow-tengo:exec.package")
|
|
10
9
|
|
|
11
10
|
rVenvInputSchema := {
|
|
12
11
|
operationMode: "string",
|
|
@@ -14,37 +13,107 @@ rVenvInputSchema := {
|
|
|
14
13
|
dependencies: {"any": "string"},
|
|
15
14
|
software: validation.reference,
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
rRunEnvDescriptor: desc.runEnvScheme,
|
|
18
17
|
R: validation.reference
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
ARG_VAR_R := "R" // abs path to 'R' run environment runtime
|
|
21
|
+
ARG_VAR_R_VENV := "rVenv" // abs path to 'R' portable virtual environment, restored on runner side
|
|
22
|
+
|
|
23
|
+
RENV_ROOT_DIR_NAME := "renv-root" // directory inside R run environment package, where RENV_PATHS_ROOT is stored.
|
|
24
|
+
|
|
25
|
+
_expr := func(varName) {
|
|
26
|
+
return "{" + varName + "}"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
runRPlan := func(refsPrefix, rPkgRef, rRunEnvDescriptor, rVenvRef) {
|
|
35
|
+
validation.assertType(refsPrefix, "string", "exec.R.runRPlan: <refsPrefix> is expected to be string with prefix for all default references attached to R-based command")
|
|
36
|
+
validation.assertType(rPkgRef, validation.reference, "exec.R.runRPlan: <rPkgRef> is expected to be reference to R run environment package")
|
|
37
|
+
validation.assertType(rRunEnvDescriptor, desc.runEnvScheme, "exec.R.runRPlan: <rRunEnvDescriptor> is expected to be run environment descriptor")
|
|
38
|
+
validation.assert(rRunEnvDescriptor.type == "R", "exec.R.runRPlan: <rRunEnvDescriptor> is expected to be R run environment descriptor")
|
|
39
|
+
if (!is_undefined(rVenvRef)) {
|
|
40
|
+
validation.assertType(rVenvRef, validation.reference, "exec.R.runRPlan: <rVenvRef> is expected to be reference to R virtual environment")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
plan := runcmd.createRunCommandPlan()
|
|
24
44
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
cpu(pkg.DEFAULT_ALLOCATION_CPU).
|
|
28
|
-
mem(pkg.DEFAULT_ALLOCATION_RAM).
|
|
29
|
-
ref("r-run-environment", rPackage).
|
|
30
|
-
substitutionRule(rule, "r-run-environment").
|
|
31
|
-
addPathTyped(t, path.join("{R}", rDescriptor.binDir)).
|
|
32
|
-
envTyped("RHOME", t, "{R}").
|
|
33
|
-
envTyped("R_HOME_DIR", t, "{R}").
|
|
34
|
-
envTyped("RENV_PATHS_ROOT", t, path.join("{R}", "renv-root")).
|
|
35
|
-
envTyped("RENV_PATHS_BINARY", t, path.join("{R}", "renv-root", "binaries")).
|
|
36
|
-
envTyped("RENV_PATHS_SOURCE", t, path.join("{R}", "renv-root", "sources")).
|
|
37
|
-
envTyped("RENV_PATHS_CACHE", t, path.join("{R}", "renv-root", "cache"))
|
|
45
|
+
rRefKey := refsPrefix + "/R-runtime"
|
|
46
|
+
rEnvRefKey := refsPrefix + "/R-virtual-env"
|
|
38
47
|
|
|
48
|
+
rBinDir := path.join(_expr(ARG_VAR_R), rRunEnvDescriptor.binDir)
|
|
49
|
+
renvRootArg := path.join(_expr(ARG_VAR_R), RENV_ROOT_DIR_NAME)
|
|
39
50
|
|
|
51
|
+
plan.
|
|
52
|
+
refVar(ARG_VAR_R, rRefKey, rPkgRef).
|
|
53
|
+
addPathExpression(rBinDir).
|
|
54
|
+
envExpression("RHOME", _expr(ARG_VAR_R)). // RScript uses this variable instead of R_HOME_DIR or R_HOME
|
|
55
|
+
envExpression("R_HOME_DIR", _expr(ARG_VAR_R)). // R starter sh script uses this to correctly set R_HOME and many others
|
|
56
|
+
envExpression("RENV_PATHS_ROOT", renvRootArg).
|
|
57
|
+
envExpression("RENV_PATHS_BINARY", path.join(renvRootArg, "binaries")).
|
|
58
|
+
envExpression("RENV_PATHS_SOURCE", path.join(renvRootArg, "sources")).
|
|
59
|
+
envExpression("RENV_PATHS_CACHE", path.join(renvRootArg, "cache"))
|
|
40
60
|
|
|
61
|
+
if (!is_undefined(rVenvRef)) {
|
|
62
|
+
plan.
|
|
63
|
+
refVar(ARG_VAR_R_VENV, rEnvRefKey, rVenvRef).
|
|
64
|
+
envExpression("RENV_PATHS_RENV", path.join(_expr(ARG_VAR_R_VENV), "r-venv")).
|
|
65
|
+
envExpression("RENV_PATHS_LOCKFILE", path.join(_expr(ARG_VAR_R_VENV), "renv.lock"))
|
|
66
|
+
}
|
|
41
67
|
|
|
42
|
-
builder.envTyped("RENV_CONFIG_REPOS_OVERRIDE", t, path.join("{R}", "packages"))
|
|
43
68
|
|
|
44
|
-
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
plan.envExpression("RENV_CONFIG_REPOS_OVERRIDE", path.join("{R}", "packages"))
|
|
72
|
+
|
|
73
|
+
return plan
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
activateRenvInWD := func(wd, queue, computeResources, plan) {
|
|
77
|
+
validation.assertType(wd, validation.reference, "exec.R.activateRenvInWD: <wd> must be a reference to workdir")
|
|
78
|
+
validation.assertType(queue, "string", "exec.R.activateRenvInWD: <queue> must be a string")
|
|
79
|
+
validation.assertType(computeResources,
|
|
80
|
+
["or",
|
|
81
|
+
validation.reference,
|
|
82
|
+
{ "cpu": "number", "ram": ["or", "number", "string"] }
|
|
83
|
+
],
|
|
84
|
+
"exec.R.activateRenvInWD: <computeResources> must be a reference to allocation, or a map with cpu and ram"
|
|
85
|
+
)
|
|
86
|
+
validation.assertType(plan, runcmd.RUN_CMD_PLAN_STATE_OR_OBJECT_SCHEMA, "exec.R.activateRenvInWD: <plan> must be a run command plan or state")
|
|
87
|
+
|
|
88
|
+
activateRenv := runcmd.builder(wd, plan).
|
|
89
|
+
cmd("R").
|
|
90
|
+
inQueue(queue).
|
|
91
|
+
resetArgs().
|
|
92
|
+
arg("--no-echo").
|
|
93
|
+
arg("--no-restore").
|
|
94
|
+
arg("-e").arg("renv::activate()")
|
|
95
|
+
|
|
96
|
+
if (smart.isReference(computeResources)) {
|
|
97
|
+
activateRenv.allocation(computeResources)
|
|
98
|
+
} else {
|
|
99
|
+
activateRenv.
|
|
100
|
+
cpu(computeResources.cpu).
|
|
101
|
+
mem(computeResources.ram)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return activateRenv.build().workdir
|
|
45
105
|
}
|
|
46
106
|
|
|
47
107
|
export ll.toStrict({
|
|
108
|
+
|
|
109
|
+
ARG_VAR_R : ARG_VAR_R,
|
|
110
|
+
ARG_VAR_R_VENV : ARG_VAR_R_VENV,
|
|
111
|
+
RENV_ROOT_DIR_NAME : RENV_ROOT_DIR_NAME,
|
|
112
|
+
|
|
113
|
+
|
|
48
114
|
rVenvInputSchema : rVenvInputSchema,
|
|
49
|
-
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
runRPlan : runRPlan,
|
|
118
|
+
activateRenvInWD : activateRenvInWD
|
|
50
119
|
})
|
|
@@ -19,7 +19,7 @@ rVenvBuilder := func() {
|
|
|
19
19
|
|
|
20
20
|
opMode := "remote"
|
|
21
21
|
toolset := "renv"
|
|
22
|
-
|
|
22
|
+
rRunEnvDescriptor := undefined
|
|
23
23
|
softwareRef := undefined
|
|
24
24
|
dependencies := undefined
|
|
25
25
|
cache := 0
|
|
@@ -47,9 +47,9 @@ rVenvBuilder := func() {
|
|
|
47
47
|
|
|
48
48
|
runEnvDescriptor: func(descriptor) {
|
|
49
49
|
validation.assertType(descriptor, desc.runEnvScheme, "exec.R.runEnvDescriptor: <descriptor> must be run environment specification")
|
|
50
|
-
ll.assert(descriptor.type == "R", "exec.R.runEnvDescriptor:
|
|
50
|
+
ll.assert(descriptor.type == "R", "exec.R.runEnvDescriptor: incorrect run environment type. Expected 'R', got %q", descriptor.type)
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
rRunEnvDescriptor = ll.fromStrict(descriptor)
|
|
53
53
|
return self
|
|
54
54
|
},
|
|
55
55
|
|
|
@@ -138,20 +138,19 @@ rVenvBuilder := func() {
|
|
|
138
138
|
},
|
|
139
139
|
|
|
140
140
|
build: func() {
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
archiveRef := pkg.get(rRunEnvDescriptor.registry, rRunEnvDescriptor.package).archive()
|
|
142
|
+
rPkgRef := pkg.install(archiveRef).package()
|
|
143
143
|
|
|
144
144
|
rVenvInputs := {
|
|
145
145
|
operationMode: opMode,
|
|
146
146
|
toolset: toolset,
|
|
147
147
|
dependencies: dependencies,
|
|
148
148
|
software: softwareRef,
|
|
149
|
-
|
|
150
|
-
R:
|
|
149
|
+
rRunEnvDescriptor: rRunEnvDescriptor,
|
|
150
|
+
R: rPkgRef
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
validation.assertType(rVenvInputs, internal.rVenvInputSchema, "incomplete R runenv configuration")
|
|
154
|
-
|
|
155
154
|
tpl := render.create(createRenvTpl, rVenvInputs)
|
|
156
155
|
|
|
157
156
|
self := undefined
|
|
@@ -171,7 +170,8 @@ rVenvBuilder := func() {
|
|
|
171
170
|
|
|
172
171
|
|
|
173
172
|
R: func() {
|
|
174
|
-
|
|
173
|
+
|
|
174
|
+
return rPkgRef
|
|
175
175
|
}
|
|
176
176
|
}))
|
|
177
177
|
|
|
@@ -184,5 +184,7 @@ rVenvBuilder := func() {
|
|
|
184
184
|
|
|
185
185
|
|
|
186
186
|
export ll.toStrict({
|
|
187
|
-
rVenvBuilder : rVenvBuilder
|
|
187
|
+
rVenvBuilder : rVenvBuilder,
|
|
188
|
+
runRPlan : internal.runRPlan,
|
|
189
|
+
activateRenvInWD : internal.activateRenvInWD
|
|
188
190
|
})
|
|
@@ -44,13 +44,27 @@ RUN_CMD_PLAN_OPTIONS_SCHEMA := {
|
|
|
44
44
|
"dockerEntrypoint,?": ["string"]
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
RUN_CMD_PLAN_STATE_SCHEMA := {
|
|
48
48
|
"refs": RUN_CMD_PLAN_REFS_SCHEMA,
|
|
49
49
|
"options": RUN_CMD_PLAN_OPTIONS_SCHEMA
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
_isRunCommandPlanObject := func(obj) {
|
|
53
|
+
return ll.isStrict(obj) && maps.containsKey(obj, "_type") && obj["_type"] == "runcmd.plan"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_validateIsRunCommandPlanObject := func(obj, keyPath, stack) {
|
|
57
|
+
if !_isRunCommandPlanObject(obj) {
|
|
58
|
+
return validation.fail(keyPath, "item %q is not a run command plan object", obj)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return validation.success
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
RUN_CMD_PLAN_STATE_OR_OBJECT_SCHEMA := [ "or", RUN_CMD_PLAN_STATE_SCHEMA, _validateIsRunCommandPlanObject ]
|
|
65
|
+
|
|
52
66
|
_initStateFromPlan := func(state, initState, context) {
|
|
53
|
-
validation.assertType(initState,
|
|
67
|
+
validation.assertType(initState, RUN_CMD_PLAN_STATE_SCHEMA, "invalid initial state for " + context)
|
|
54
68
|
|
|
55
69
|
initOptions := initState.options
|
|
56
70
|
state.options.cmd = maps.clone(initOptions.cmd)
|
|
@@ -119,6 +133,8 @@ _createAbstractRunCommandPlan := func(state, returnSelfFn) {
|
|
|
119
133
|
self := undefined
|
|
120
134
|
|
|
121
135
|
self = ll.toStrict({
|
|
136
|
+
_type: "runcmd.plan",
|
|
137
|
+
|
|
122
138
|
|
|
123
139
|
|
|
124
140
|
|
|
@@ -492,6 +508,9 @@ builder := func(workdir, ...initialState) {
|
|
|
492
508
|
|
|
493
509
|
if len(initialState) == 1 {
|
|
494
510
|
initState := initialState[0]
|
|
511
|
+
if (_isRunCommandPlanObject(initState)) {
|
|
512
|
+
initState = initState.state()
|
|
513
|
+
}
|
|
495
514
|
_initStateFromPlan(state, initState, "run command builder")
|
|
496
515
|
} else {
|
|
497
516
|
ll.assert(len(initialState) == 0, "builder accepts at most two arguments")
|
|
@@ -642,10 +661,13 @@ builder := func(workdir, ...initialState) {
|
|
|
642
661
|
|
|
643
662
|
|
|
644
663
|
|
|
645
|
-
|
|
646
|
-
|
|
664
|
+
hasAllocation := !is_undefined(allocation)
|
|
665
|
+
hasCpuRamRequest := !is_undefined(cpu) || !is_undefined(ram)
|
|
666
|
+
|
|
667
|
+
ll.assert(!(hasAllocation && hasCpuRamRequest), // none specified is OK: default queue limits would be applied.
|
|
668
|
+
"runcmd.builder.build: allocation and CPU/RAM request are mutually exclusive for the same command run")
|
|
647
669
|
|
|
648
|
-
ll.assert(!is_undefined(queue), "queue
|
|
670
|
+
ll.assert(!is_undefined(queue), "runcmd.builder.build: queue is required for any command run")
|
|
649
671
|
|
|
650
672
|
useBatch := limits.useBatch(queue)
|
|
651
673
|
|
|
@@ -728,13 +750,17 @@ builder := func(workdir, ...initialState) {
|
|
|
728
750
|
}
|
|
729
751
|
|
|
730
752
|
export ll.toStrict({
|
|
753
|
+
|
|
754
|
+
ARG_SCHEMA: ARG_SCHEMA,
|
|
755
|
+
RUN_CMD_PLAN_REFS_SCHEMA: RUN_CMD_PLAN_REFS_SCHEMA,
|
|
756
|
+
RUN_CMD_PLAN_OPTIONS_SCHEMA: RUN_CMD_PLAN_OPTIONS_SCHEMA,
|
|
757
|
+
RUN_CMD_PLAN_STATE_SCHEMA: RUN_CMD_PLAN_STATE_SCHEMA,
|
|
758
|
+
RUN_CMD_PLAN_STATE_OR_OBJECT_SCHEMA: RUN_CMD_PLAN_STATE_OR_OBJECT_SCHEMA,
|
|
759
|
+
|
|
760
|
+
|
|
731
761
|
builder: builder,
|
|
732
762
|
simpleArg: simpleArg,
|
|
733
763
|
variableArg: variableArg,
|
|
734
764
|
expressionArg: expressionArg,
|
|
735
|
-
createRunCommandPlan: createRunCommandPlan
|
|
736
|
-
ARG_SCHEMA: ARG_SCHEMA,
|
|
737
|
-
RUN_CMD_PLAN_REFS_SCHEMA: RUN_CMD_PLAN_REFS_SCHEMA,
|
|
738
|
-
RUN_CMD_PLAN_OPTIONS_SCHEMA: RUN_CMD_PLAN_OPTIONS_SCHEMA,
|
|
739
|
-
RUN_CMD_PLAN_SCHEMA: RUN_CMD_PLAN_SCHEMA
|
|
765
|
+
createRunCommandPlan: createRunCommandPlan
|
|
740
766
|
})
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platforma-sdk/workflow-tengo",
|
|
3
|
-
"version": "5.5.
|
|
3
|
+
"version": "5.5.3",
|
|
4
4
|
"description": "Tengo SDK library",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@milaboratories/software-pframes-conv": "2.2.9",
|
|
20
20
|
"@platforma-open/milaboratories.software-small-binaries": "^1.15.23",
|
|
21
|
-
"@platforma-open/milaboratories.software-
|
|
22
|
-
"@platforma-open/milaboratories.software-
|
|
21
|
+
"@platforma-open/milaboratories.software-ptabler": "1.13.1",
|
|
22
|
+
"@platforma-open/milaboratories.software-ptexter": "1.2.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@platforma-sdk/tengo-builder": "2.3.3"
|