@platforma-sdk/workflow-tengo 1.6.3 → 1.7.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.
- package/dist/tengo/lib/assets.lib.tengo +26 -7
- package/dist/tengo/lib/exec.lib.tengo +44 -3
- package/dist/tengo/lib/exec.package.lib.tengo +24 -16
- package/dist/tengo/lib/exec.runenv.lib.tengo +1 -1
- package/dist/tengo/lib/file.internal.lib.tengo +101 -0
- package/dist/tengo/lib/file.lib.tengo +9 -45
- package/dist/tengo/lib/ll.lib.tengo +47 -2
- package/dist/tengo/lib/pframes.xsv.lib.tengo +20 -11
- package/dist/tengo/lib/smart.lib.tengo +7 -6
- package/dist/tengo/lib/tpl.lib.tengo +2 -2
- package/dist/tengo/lib/workdir.lib.tengo +77 -4
- package/dist/tengo/tpl/exec.exec.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/pframes.aggregate.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-multiple-pcolumns.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.import-xsv-map.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.map-pframe.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/package.json +5 -5
|
@@ -34,6 +34,7 @@ importSoftware := func(swName) {
|
|
|
34
34
|
sw := plapi.getSoftwareInfo(swName)
|
|
35
35
|
return ll.toStrict({
|
|
36
36
|
_type: "softwareDescriptor",
|
|
37
|
+
_id: sw.id,
|
|
37
38
|
|
|
38
39
|
resource: smart.resource(sw.id),
|
|
39
40
|
name: sw.name,
|
|
@@ -43,6 +44,21 @@ importSoftware := func(swName) {
|
|
|
43
44
|
})
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Checks if given item is a software info
|
|
49
|
+
*
|
|
50
|
+
* @param toCheck: any - item to check
|
|
51
|
+
*
|
|
52
|
+
* @return isSoftware: boolean - true for software info, loaded with importSoftware()
|
|
53
|
+
*/
|
|
54
|
+
isSoftwareInfo := func(toCheck) {
|
|
55
|
+
if !ll.isMap(toCheck) {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (ll.fromStrict(toCheck)._type) == "softwareDescriptor"
|
|
60
|
+
}
|
|
61
|
+
|
|
46
62
|
/**
|
|
47
63
|
* Returns asset description suitable for 'exec' operations.
|
|
48
64
|
* Asset is the way to put static files into working directory before execution of the command.
|
|
@@ -53,10 +69,11 @@ importSoftware := func(swName) {
|
|
|
53
69
|
* - "@company/package:some-oftware"
|
|
54
70
|
* - ":my-local-software"
|
|
55
71
|
*/
|
|
56
|
-
importAsset := func(
|
|
57
|
-
asset := plapi.getSoftwareInfo(
|
|
72
|
+
importAsset := func(assetName) {
|
|
73
|
+
asset := plapi.getSoftwareInfo(assetName)
|
|
58
74
|
return ll.toStrict({
|
|
59
75
|
_type: "assetDescriptor",
|
|
76
|
+
_id: asset.id,
|
|
60
77
|
|
|
61
78
|
resource: smart.resource(asset.id),
|
|
62
79
|
name: asset.name,
|
|
@@ -67,22 +84,24 @@ importAsset := func(swName) {
|
|
|
67
84
|
}
|
|
68
85
|
|
|
69
86
|
/**
|
|
70
|
-
* Checks if given item is
|
|
87
|
+
* Checks if given item is an asset info
|
|
71
88
|
*
|
|
72
89
|
* @param toCheck: any - item to check
|
|
73
90
|
*
|
|
74
|
-
* @return
|
|
91
|
+
* @return isAsset: boolean - true for asset info, loaded with importAsset()
|
|
75
92
|
*/
|
|
76
|
-
|
|
93
|
+
isAssetInfo := func(toCheck) {
|
|
77
94
|
if !ll.isMap(toCheck) {
|
|
78
95
|
return false
|
|
79
96
|
}
|
|
80
97
|
|
|
81
|
-
return (ll.fromStrict(toCheck)._type) == "
|
|
98
|
+
return (ll.fromStrict(toCheck)._type) == "assetDescriptor"
|
|
82
99
|
}
|
|
83
100
|
|
|
84
101
|
export ll.toStrict({
|
|
85
102
|
importTemplate: importTemplate,
|
|
86
103
|
importSoftware: importSoftware,
|
|
87
|
-
isSoftwareInfo: isSoftwareInfo
|
|
104
|
+
isSoftwareInfo: isSoftwareInfo,
|
|
105
|
+
importAsset: importAsset,
|
|
106
|
+
isAssetInfo: isAssetInfo
|
|
88
107
|
})
|
|
@@ -65,6 +65,11 @@ builder := func() {
|
|
|
65
65
|
// workdir processors
|
|
66
66
|
wdProcessors := {}
|
|
67
67
|
|
|
68
|
+
// assets to be extracted into workdir before execution
|
|
69
|
+
assetInfos := {}
|
|
70
|
+
assetDescriptors := {}
|
|
71
|
+
assetRules := {}
|
|
72
|
+
|
|
68
73
|
stdout := "stdout.txt"
|
|
69
74
|
stderr := "stderr.txt"
|
|
70
75
|
|
|
@@ -94,7 +99,7 @@ builder := func() {
|
|
|
94
99
|
software: func(sw) {
|
|
95
100
|
assertEntrypointNotSet()
|
|
96
101
|
ll.assert(assets.isSoftwareInfo(sw),
|
|
97
|
-
"exec.builder.software: <software> parameter is not a software info. Provide the value obtained from
|
|
102
|
+
"exec.builder.software: <software> parameter is not a software info. Provide the value obtained from assets.importSoftware() here"
|
|
98
103
|
)
|
|
99
104
|
|
|
100
105
|
soft = sw
|
|
@@ -247,7 +252,7 @@ builder := func() {
|
|
|
247
252
|
cache = days * times.hour * 24
|
|
248
253
|
return self
|
|
249
254
|
},
|
|
250
|
-
|
|
255
|
+
|
|
251
256
|
/**
|
|
252
257
|
* Sets the number of error lines to be captured.
|
|
253
258
|
*
|
|
@@ -262,6 +267,39 @@ builder := func() {
|
|
|
262
267
|
return self
|
|
263
268
|
},
|
|
264
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Adds files from given asset into working directory.
|
|
272
|
+
*
|
|
273
|
+
* @param asset: assetInfo - asset info loaded from assets.importAsset()
|
|
274
|
+
* @param destinationDir: string - path inside working directory for files to be extracted from archive.
|
|
275
|
+
* @param pathsInArchive: string[] - paths inside the archive to be extracted into destinationDir.
|
|
276
|
+
* Empty array means 'unpack full archive'.
|
|
277
|
+
*/
|
|
278
|
+
addAsset: func(asset, destinationDir, pathsInArchive) {
|
|
279
|
+
ll.assert(assets.isAssetInfo(asset),
|
|
280
|
+
"exec.builder.addAsset: <asset> parameter is not an asset info. Provide the value obtained from assets.importAsset() here")
|
|
281
|
+
|
|
282
|
+
validation.assertJsonSchema(destinationDir, "string", "exec.builder.addAsset: <destinationDir> is not a string. It must be a path inside working directory")
|
|
283
|
+
validation.assertJsonSchema(pathsInArchive, ["string"], "exec.builder.addAsset: <pathsInArchive> must contain list of strings with paths inside archive")
|
|
284
|
+
|
|
285
|
+
assetIdKey := ll.idToString(asset._id)
|
|
286
|
+
if is_undefined(assetRules[assetIdKey]) {
|
|
287
|
+
assetInfos[assetIdKey] = asset.resource
|
|
288
|
+
assetDescriptors[assetIdKey] = ll.fromStrict(asset.descriptor)
|
|
289
|
+
assetRules[assetIdKey] = {}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if len(pathsInArchive) == 0 || is_undefined(assetRules[assetIdKey][destinationDir]) {
|
|
293
|
+
assetRules[assetIdKey][destinationDir] = []
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (len(pathsInArchive) > 0) {
|
|
297
|
+
assetRules[assetIdKey][destinationDir] = append(assetRules[assetIdKey][destinationDir], pathsInArchive...)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return self
|
|
301
|
+
},
|
|
302
|
+
|
|
265
303
|
/**
|
|
266
304
|
* Adds a file to the working directory
|
|
267
305
|
*
|
|
@@ -474,7 +512,10 @@ builder := func() {
|
|
|
474
512
|
fileSetsToSave: fileSetsToSave,
|
|
475
513
|
fileSetsContentToSave: fileSetsContentToSave,
|
|
476
514
|
filesToStream: slices.fromSet(filesToStream),
|
|
477
|
-
wdProcessors: wdProcessors
|
|
515
|
+
wdProcessors: wdProcessors,
|
|
516
|
+
assets: smart.createMapResource(assetInfos),
|
|
517
|
+
assetDescriptors: assetDescriptors,
|
|
518
|
+
assetRules: assetRules
|
|
478
519
|
}
|
|
479
520
|
|
|
480
521
|
if !is_undefined(soft) {
|
|
@@ -24,6 +24,12 @@ binaryDescriptorScheme := {
|
|
|
24
24
|
runEnv: runEnvDescriptorScheme
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
assetDescriptorScheme := {
|
|
28
|
+
registry: "string",
|
|
29
|
+
package: "string",
|
|
30
|
+
url: "string"
|
|
31
|
+
}
|
|
32
|
+
|
|
27
33
|
localDescriptorScheme := {
|
|
28
34
|
hash: "string",
|
|
29
35
|
path: "string",
|
|
@@ -34,6 +40,7 @@ localDescriptorScheme := {
|
|
|
34
40
|
descriptorScheme := {
|
|
35
41
|
"isDev,omitempty": "bool",
|
|
36
42
|
"binary,omitempty": binaryDescriptorScheme,
|
|
43
|
+
"asset,omitempty": assetDescriptorScheme,
|
|
37
44
|
"local,omitempty": localDescriptorScheme
|
|
38
45
|
}
|
|
39
46
|
|
|
@@ -135,22 +142,21 @@ use := func(settings) {
|
|
|
135
142
|
return self
|
|
136
143
|
}
|
|
137
144
|
|
|
138
|
-
|
|
139
145
|
_RTYPE_PACKAGE_PUT_ASSET := { Name: "PackagePutAssetsToWD", Version: "1" }
|
|
140
146
|
_INPUT_PACKAGE := "package"
|
|
141
147
|
_INPUT_WD_IN := "workdirIn"
|
|
142
148
|
_OUTPUT_WD_OUT := "workdirOut"
|
|
143
149
|
|
|
144
150
|
/**
|
|
145
|
-
* Load
|
|
151
|
+
* Load static files from software package into working directory or value resources.
|
|
146
152
|
*
|
|
147
153
|
* @param pkg: package - installed package (usually from package.install(<archive>))
|
|
148
154
|
*
|
|
149
|
-
* @return loader:
|
|
155
|
+
* @return loader: pkgFileLoader - builder that provides requested files and content after load() call
|
|
150
156
|
*/
|
|
151
|
-
|
|
157
|
+
pkgFileLoader := func(pkg) {
|
|
152
158
|
ll.assert(smart.isReference(pkg),
|
|
153
|
-
"package.
|
|
159
|
+
"package.pkgFileLoader: <pkg> must be reference to installed package. It is usually obtained from package.install().package")
|
|
154
160
|
|
|
155
161
|
filePaths := []
|
|
156
162
|
fileContentPaths := []
|
|
@@ -159,14 +165,14 @@ assetsLoader := func(pkg) {
|
|
|
159
165
|
|
|
160
166
|
self = {
|
|
161
167
|
saveFile: func(path) {
|
|
162
|
-
validation.assertJsonSchema(path, "string", "package.
|
|
168
|
+
validation.assertJsonSchema(path, "string", "package.pkgFileLoader.saveFile: <path> must be string")
|
|
163
169
|
filePaths = append(filePaths, path)
|
|
164
170
|
|
|
165
171
|
return self
|
|
166
172
|
},
|
|
167
173
|
|
|
168
174
|
saveFiles: func(paths) {
|
|
169
|
-
validation.assertJsonSchema(paths, ["string"], "package.
|
|
175
|
+
validation.assertJsonSchema(paths, ["string"], "package.pkgFileLoader.saveFiles: <paths> must be array of strings")
|
|
170
176
|
for p in paths {
|
|
171
177
|
self.saveFile(p)
|
|
172
178
|
}
|
|
@@ -175,13 +181,13 @@ assetsLoader := func(pkg) {
|
|
|
175
181
|
},
|
|
176
182
|
|
|
177
183
|
saveFileContent: func(path) {
|
|
178
|
-
validation.assertJsonSchema(path, "string", "package.
|
|
184
|
+
validation.assertJsonSchema(path, "string", "package.pkgFileLoader.saveFileContent: <path> must be string")
|
|
179
185
|
fileContentPaths = append(fileContentPaths, path)
|
|
180
186
|
return self
|
|
181
187
|
},
|
|
182
188
|
|
|
183
189
|
saveFilesContent: func(paths) {
|
|
184
|
-
validation.assertJsonSchema(paths, ["string"], "package.
|
|
190
|
+
validation.assertJsonSchema(paths, ["string"], "package.pkgFileLoader.saveFilesContent: <paths> must be array of strings")
|
|
185
191
|
for p in paths {
|
|
186
192
|
self.saveFileContent(p)
|
|
187
193
|
}
|
|
@@ -252,8 +258,8 @@ assetsLoader := func(pkg) {
|
|
|
252
258
|
*
|
|
253
259
|
* @return file: singleAsset - object with reference to the file resource and progress report
|
|
254
260
|
*/
|
|
255
|
-
|
|
256
|
-
assets :=
|
|
261
|
+
loadPackageFile := func(pkg, assetPath) {
|
|
262
|
+
assets := pkgFileLoader(pkg).
|
|
257
263
|
saveFile(assetPath).
|
|
258
264
|
load()
|
|
259
265
|
|
|
@@ -271,8 +277,8 @@ loadAssetFile := func(pkg, assetPath) {
|
|
|
271
277
|
*
|
|
272
278
|
* @return content: singleAsset - object with reference to the asset content resource and progress report
|
|
273
279
|
*/
|
|
274
|
-
|
|
275
|
-
assets :=
|
|
280
|
+
loadPackageFileContent := func(pkg, assetPath) {
|
|
281
|
+
assets := pkgFileLoader(pkg).
|
|
276
282
|
saveFileContent(assetPath).
|
|
277
283
|
load()
|
|
278
284
|
|
|
@@ -285,6 +291,8 @@ loadAssetContent := func(pkg, assetPath) {
|
|
|
285
291
|
|
|
286
292
|
export ll.toStrict({
|
|
287
293
|
softwareInfoScheme : softwareInfoScheme,
|
|
294
|
+
descriptorScheme : descriptorScheme,
|
|
295
|
+
assetDescriptorScheme : assetDescriptorScheme,
|
|
288
296
|
binaryDescriptorScheme : binaryDescriptorScheme,
|
|
289
297
|
localDescriptorScheme : localDescriptorScheme,
|
|
290
298
|
runEnvDescriptorScheme : runEnvDescriptorScheme,
|
|
@@ -294,7 +302,7 @@ export ll.toStrict({
|
|
|
294
302
|
get : get,
|
|
295
303
|
install : install,
|
|
296
304
|
use : use,
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
305
|
+
pkgFileLoader : pkgFileLoader,
|
|
306
|
+
loadPackageFile : loadPackageFile,
|
|
307
|
+
loadPackageFileContent : loadPackageFileContent
|
|
300
308
|
})
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal functions to work with blobs.
|
|
3
|
+
*
|
|
4
|
+
* This file contains functions intended for use only within workflow tengo SDK.
|
|
5
|
+
* Their parameters list, results and other parts of the interface may change from
|
|
6
|
+
* vertion to version without prior notice.
|
|
7
|
+
*
|
|
8
|
+
* DO NOT USE them in workflows or templates directly.
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
13
|
+
smart := import("@platforma-sdk/workflow-tengo:smart")
|
|
14
|
+
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
15
|
+
|
|
16
|
+
json := import("json")
|
|
17
|
+
|
|
18
|
+
//
|
|
19
|
+
// Resource types:
|
|
20
|
+
//
|
|
21
|
+
|
|
22
|
+
RTYPE_BLOB_IMPORT_EXTERNAL := { Name: "BlobImportExternal", Version: "1" }
|
|
23
|
+
RTYPE_BLOB_IMPORT_INTERNAL := { Name: "BlobImportInternal", Version: "1" }
|
|
24
|
+
RTYPE_BLOB_UPLOAD := { Name: "BlobUpload", Version: "1" }
|
|
25
|
+
RTYPE_BLOB_DOWNLOAD := { Name: "BlobDownload", Version: "2" }
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create a task for importing blob from external service by URL
|
|
29
|
+
* Produces 2 fields:
|
|
30
|
+
* 'file' - reference to blob to appear once import gets finished.
|
|
31
|
+
* 'handle' - reference to resource with 'progress' API.
|
|
32
|
+
*/
|
|
33
|
+
createBlobImportExternal := func(settings) {
|
|
34
|
+
validation.assertJsonSchema(settings, {
|
|
35
|
+
"dataURL": "string",
|
|
36
|
+
"dataHeaders,omitempty": [["string"]],
|
|
37
|
+
"dataVersion,omitempty": "string"
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
importer := smart.structBuilder(RTYPE_BLOB_IMPORT_EXTERNAL, json.encode(settings)).lockAndBuild()
|
|
41
|
+
|
|
42
|
+
return ll.toStrict({
|
|
43
|
+
file: importer.getField("blob"),
|
|
44
|
+
handle: importer.getField("handle")
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create a task for importing blob from one of preconfigured storages.
|
|
50
|
+
* Produces 2 fields:
|
|
51
|
+
* 'file' - reference to blob to appear once import gets finished.
|
|
52
|
+
* 'handle' - reference to resource with 'progress' API.
|
|
53
|
+
*/
|
|
54
|
+
createBlobImportInternal := func(settings) {
|
|
55
|
+
validation.assertJsonSchema(settings, {
|
|
56
|
+
"path": "string",
|
|
57
|
+
"storageId": "string",
|
|
58
|
+
"maxSpeedBytes,omitempty": "number",
|
|
59
|
+
"dataVersion,omitempty": "string"
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
importer := smart.structBuilder(RTYPE_BLOB_IMPORT_INTERNAL, json.encode(settings)).lockAndBuild()
|
|
63
|
+
|
|
64
|
+
return ll.toStrict({
|
|
65
|
+
file: importer.getField("blob"),
|
|
66
|
+
handle: importer.getField("handle")
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create a task for getting a blob directly from the user via 'upload' procedure.
|
|
72
|
+
* Produces 2 fields:
|
|
73
|
+
* 'file' - reference to blob to appear once upload gets finished.
|
|
74
|
+
* 'handle' - reference to resource with 'progress' and 'upload' APIs.
|
|
75
|
+
*/
|
|
76
|
+
createBlobUpload := func(settings) {
|
|
77
|
+
validation.assertJsonSchema(settings, {
|
|
78
|
+
"modificationTime": "string",
|
|
79
|
+
"localPath": "string",
|
|
80
|
+
"pathSignature": "string",
|
|
81
|
+
"sizeBytes": "string"
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
uploader := smart.structBuilder(RTYPE_BLOB_UPLOAD, json.encode(settings)).lockAndBuild()
|
|
85
|
+
|
|
86
|
+
return ll.toStrict({
|
|
87
|
+
file: uploader.getField("blob"),
|
|
88
|
+
handle: uploader.getField("handle")
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export ll.toStrict({
|
|
93
|
+
RTYPE_BLOB_IMPORT_EXTERNAL : RTYPE_BLOB_IMPORT_EXTERNAL,
|
|
94
|
+
RTYPE_BLOB_IMPORT_INTERNAL : RTYPE_BLOB_IMPORT_INTERNAL,
|
|
95
|
+
RTYPE_BLOB_UPLOAD : RTYPE_BLOB_UPLOAD,
|
|
96
|
+
RTYPE_BLOB_DOWNLOAD : RTYPE_BLOB_DOWNLOAD,
|
|
97
|
+
|
|
98
|
+
createBlobImportExternal : createBlobImportExternal,
|
|
99
|
+
createBlobImportInternal : createBlobImportInternal,
|
|
100
|
+
createBlobUpload : createBlobUpload
|
|
101
|
+
})
|
|
@@ -14,49 +14,10 @@
|
|
|
14
14
|
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
15
15
|
smart := import("@platforma-sdk/workflow-tengo:smart")
|
|
16
16
|
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
17
|
+
blobs := import("@platforma-sdk/workflow-tengo:file.internal")
|
|
17
18
|
|
|
18
19
|
json := import("json")
|
|
19
20
|
|
|
20
|
-
//
|
|
21
|
-
// Constants:
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
_RTYPE_BLOB_IMPORT_INTERNAL := { Name: "BlobImportInternal", Version: "1" }
|
|
25
|
-
_RTYPE_BLOB_UPLOAD := { Name: "BlobUpload", Version: "1" }
|
|
26
|
-
_RTYPE_BLOB_DOWNLOAD := { Name: "BlobDownload", Version: "2" }
|
|
27
|
-
|
|
28
|
-
createBlobImportInternal := func(settings) {
|
|
29
|
-
validation.assertJsonSchema(settings, {
|
|
30
|
-
"path": "string",
|
|
31
|
-
"storageId": "string",
|
|
32
|
-
"maxSpeedBytes,omitempty": "number",
|
|
33
|
-
"dataVersion,omitempty": "string"
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
importer := smart.structBuilder(_RTYPE_BLOB_IMPORT_INTERNAL, json.encode(settings)).lockAndBuild()
|
|
37
|
-
|
|
38
|
-
return ll.toStrict({
|
|
39
|
-
file: importer.getField("blob"),
|
|
40
|
-
handle: importer.getField("handle")
|
|
41
|
-
})
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
createBlobUpload := func(settings) {
|
|
45
|
-
validation.assertJsonSchema(settings, {
|
|
46
|
-
"modificationTime": "string",
|
|
47
|
-
"localPath": "string",
|
|
48
|
-
"pathSignature": "string",
|
|
49
|
-
"sizeBytes": "string"
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
uploader := smart.structBuilder(_RTYPE_BLOB_UPLOAD, json.encode(settings)).lockAndBuild()
|
|
53
|
-
|
|
54
|
-
return ll.toStrict({
|
|
55
|
-
file: uploader.getField("blob"),
|
|
56
|
-
handle: uploader.getField("handle")
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
|
|
60
21
|
/**
|
|
61
22
|
* Imports an external file into the workflow, either from pre-configured storage,
|
|
62
23
|
* available from the pl instance, or from the client machine via upload.
|
|
@@ -82,11 +43,14 @@ importFile := func(importFileHandle) {
|
|
|
82
43
|
settings := json.decode(path)
|
|
83
44
|
|
|
84
45
|
if url.Scheme == "upload" {
|
|
85
|
-
ll.assert(url.Host == "upload", "
|
|
86
|
-
return createBlobUpload(settings)
|
|
46
|
+
ll.assert(url.Host == "upload", "wrong 'host' in import file handle. Expected: 'upload', found: '", url.Host, "'")
|
|
47
|
+
return blobs.createBlobUpload(settings)
|
|
87
48
|
} else if url.Scheme == "index" {
|
|
88
|
-
ll.assert(url.Host == "index", "
|
|
89
|
-
return createBlobImportInternal(settings)
|
|
49
|
+
ll.assert(url.Host == "index", "wrong 'host' in import file handle. Expected 'index', found: '", url.Host, "'")
|
|
50
|
+
return blobs.createBlobImportInternal(settings)
|
|
51
|
+
} else if (url.Scheme == "download") {
|
|
52
|
+
ll.assert(url.Host == "download", "wrong 'host' in import file handle. Expected: 'download', found: '", url.Host, "'")
|
|
53
|
+
return blobs.createBlobImportExternal(settings)
|
|
90
54
|
} else {
|
|
91
55
|
ll.panic("Unknown file import handle schema: ", url.Scheme)
|
|
92
56
|
}
|
|
@@ -97,7 +61,7 @@ importFile := func(importFileHandle) {
|
|
|
97
61
|
* downloaded from the UI side.
|
|
98
62
|
*/
|
|
99
63
|
exportFile := func(blob) {
|
|
100
|
-
builder := smart.structBuilder(
|
|
64
|
+
builder := smart.structBuilder(blobs.RTYPE_BLOB_DOWNLOAD)
|
|
101
65
|
|
|
102
66
|
builder.createInputField("blob").set(blob)
|
|
103
67
|
|
|
@@ -93,15 +93,34 @@ ensureNonStrict := func(r) {
|
|
|
93
93
|
* access to non-existing elements.
|
|
94
94
|
*
|
|
95
95
|
* @param m: map|strictMap
|
|
96
|
+
* @param recursive: boolean - convert also all child maps
|
|
96
97
|
*
|
|
97
98
|
* @return strict: strictMap
|
|
98
99
|
*/
|
|
99
|
-
toStrict := func(v) {
|
|
100
|
+
toStrict := func(v, ...recursive) {
|
|
101
|
+
if len(recursive) > 0 && recursive[0] {
|
|
102
|
+
if isMap(v) {
|
|
103
|
+
for k, vv in v {
|
|
104
|
+
v[k] = toStrict(vv)
|
|
105
|
+
}
|
|
106
|
+
} else if is_array(v) {
|
|
107
|
+
for i, vv in v {
|
|
108
|
+
if isMap(vv) {
|
|
109
|
+
v[i] = toStrict(vv)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if !isMap(v) {
|
|
115
|
+
return v // for recursive transformation, accept the top-level value to be of any type (i.e. array of maps)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
100
119
|
if isStrict(v) {
|
|
101
120
|
return v
|
|
102
121
|
}
|
|
103
122
|
|
|
104
|
-
if(!
|
|
123
|
+
if(!isMap(v)) {
|
|
105
124
|
panic("toStrict(<v>): <v> is not a map", v)
|
|
106
125
|
}
|
|
107
126
|
return plapi.mapToStrict(v)
|
|
@@ -114,6 +133,30 @@ fieldId := func(resourceId, fieldName) {
|
|
|
114
133
|
return plapi.newFieldID(resourceId, fieldName)
|
|
115
134
|
}
|
|
116
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Checks if <val> can be used as field ID
|
|
138
|
+
*/
|
|
139
|
+
isFieldId := func(val) {
|
|
140
|
+
if !isStrict(val) {
|
|
141
|
+
return false
|
|
142
|
+
}
|
|
143
|
+
v := fromStrict(val)
|
|
144
|
+
return v["ResourceID"] != undefined && v["Name"] != undefined
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Converts reference ID (resource ID or field ID) to stable string representation, suitable for use as map keys
|
|
149
|
+
*/
|
|
150
|
+
idToString := func(referenceID) {
|
|
151
|
+
if isFieldId(referenceID) {
|
|
152
|
+
return fmt.sprintf("0x%X/%s", referenceID.ResourceID, referenceID.Name)
|
|
153
|
+
}
|
|
154
|
+
if is_int(referenceID) {
|
|
155
|
+
return fmt.sprintf("0x%X", referenceID)
|
|
156
|
+
}
|
|
157
|
+
panic("ll.idToString: <referenceID> cannot be used as resource or field ID")
|
|
158
|
+
}
|
|
159
|
+
|
|
117
160
|
/**
|
|
118
161
|
* Returns current template renderer
|
|
119
162
|
*/
|
|
@@ -221,6 +264,8 @@ export toStrict({
|
|
|
221
264
|
ensureNonStrict: ensureNonStrict,
|
|
222
265
|
toStrict: toStrict,
|
|
223
266
|
fieldId: fieldId,
|
|
267
|
+
isFieldId: isFieldId,
|
|
268
|
+
idToString: idToString,
|
|
224
269
|
getCurrentTemplateRenderer: getCurrentTemplateRenderer,
|
|
225
270
|
isInitializing: isInitializing,
|
|
226
271
|
getExecutorsList: getExecutorsList,
|
|
@@ -64,8 +64,10 @@ getColumnSpec := func(axesSpec, col) {
|
|
|
64
64
|
* @param ops: object - additional options
|
|
65
65
|
* {
|
|
66
66
|
* dataOnly: boolean - set to true to completely skip creation of specs
|
|
67
|
+
* splitDataAndSpec: boolean - if true, resulting map will have nested structures,
|
|
68
|
+
* with "spec" + "data" fields in nested maps
|
|
67
69
|
* transpose: object - if specified, then the input table will be transposed
|
|
68
|
-
* {
|
|
70
|
+
* {
|
|
69
71
|
* separator: string - (optional) use separator instead of inferring from the file extension
|
|
70
72
|
* pAxisIdx: index - (optional) the index of the primary axis (column in the input xsv); default 0
|
|
71
73
|
* pAxisName: string - (optional) the name of the primary axis (column name in the input xsv); default undefined
|
|
@@ -86,7 +88,7 @@ importFile := func(xsvFile, xsvType, spec, ...ops) {
|
|
|
86
88
|
|
|
87
89
|
xsvFileName := "file." + xsvType
|
|
88
90
|
|
|
89
|
-
ops := renderOps({ dataOnly: false }, ops...)
|
|
91
|
+
ops := renderOps({ dataOnly: false, splitDataAndSpec: false }, ops...)
|
|
90
92
|
|
|
91
93
|
if ops.transpose != undefined {
|
|
92
94
|
|
|
@@ -99,7 +101,7 @@ importFile := func(xsvFile, xsvType, spec, ...ops) {
|
|
|
99
101
|
software(transposeXsvSw).
|
|
100
102
|
printErrStreamToStdout().
|
|
101
103
|
inLightQueue()
|
|
102
|
-
|
|
104
|
+
|
|
103
105
|
if (tops.separator != undefined) {
|
|
104
106
|
transposeXsv = transposeXsv.arg("-input-separator").arg(tops.separator)
|
|
105
107
|
}
|
|
@@ -119,15 +121,15 @@ importFile := func(xsvFile, xsvType, spec, ...ops) {
|
|
|
119
121
|
transposeXsv = transposeXsv.arg("-metric-columns-search").arg(tops.sAxisSearch)
|
|
120
122
|
}
|
|
121
123
|
if (tops.sAxisName != undefined) {
|
|
122
|
-
transposeXsv = transposeXsv.arg("-metric-label").arg(tops.sAxisName)
|
|
124
|
+
transposeXsv = transposeXsv.arg("-metric-label").arg(tops.sAxisName)
|
|
123
125
|
}
|
|
124
126
|
if (tops.valueName != undefined) {
|
|
125
|
-
transposeXsv = transposeXsv.arg("-value-label").arg(tops.valueName)
|
|
127
|
+
transposeXsv = transposeXsv.arg("-value-label").arg(tops.valueName)
|
|
126
128
|
}
|
|
127
129
|
if (tops.separatorOverride != undefined) {
|
|
128
|
-
transposeXsv = transposeXsv.arg("-separator").arg(tops.separatorOverride)
|
|
130
|
+
transposeXsv = transposeXsv.arg("-separator").arg(tops.separatorOverride)
|
|
129
131
|
}
|
|
130
|
-
|
|
132
|
+
|
|
131
133
|
transposeXsv = transposeXsv.
|
|
132
134
|
arg(xsvFileName).
|
|
133
135
|
arg(transposedFileName).
|
|
@@ -170,8 +172,15 @@ importFile := func(xsvFile, xsvType, spec, ...ops) {
|
|
|
170
172
|
for col in spec.columns {
|
|
171
173
|
id := util.xsvColumnId(col)
|
|
172
174
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
+
if ops.splitDataAndSpec {
|
|
176
|
+
result[id] = {
|
|
177
|
+
data: pf.getFutureInputField(id),
|
|
178
|
+
spec: getColumnSpec(axesSpec, col)
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
result[id + ".data"] = pf.getFutureInputField(id)
|
|
182
|
+
result[id + ".spec"] = getColumnSpec(axesSpec, col)
|
|
183
|
+
}
|
|
175
184
|
}
|
|
176
185
|
|
|
177
186
|
return result
|
|
@@ -192,7 +201,7 @@ importFile := func(xsvFile, xsvType, spec, ...ops) {
|
|
|
192
201
|
* additionalAxesSpec: AxisSpec[] - array of additional axes spec to prepend to
|
|
193
202
|
* each column spec
|
|
194
203
|
* transpose: object - if specified, then the input table will be transposed
|
|
195
|
-
* {
|
|
204
|
+
* {
|
|
196
205
|
* separator: string - (optional) use separator instead of inferring from the file extension
|
|
197
206
|
* pAxisIdx: index - (optional) the index of the primary axis (column in the input xsv); default 0
|
|
198
207
|
* pAxisName: string - (optional) the name of the primary axis (column name in the input xsv); default undefined
|
|
@@ -249,7 +258,7 @@ importFileMap := func(xsvMap, xsvType, spec, ...ops) {
|
|
|
249
258
|
result[id + ".spec"] = columnsSpec[id]
|
|
250
259
|
}
|
|
251
260
|
}
|
|
252
|
-
|
|
261
|
+
|
|
253
262
|
return result
|
|
254
263
|
}
|
|
255
264
|
|
|
@@ -91,11 +91,6 @@ createBlobMapResource := undefined
|
|
|
91
91
|
|
|
92
92
|
////////////////// helpers ///////////////////////////
|
|
93
93
|
|
|
94
|
-
// internal helper function
|
|
95
|
-
isFieldId := func(val) {
|
|
96
|
-
return ll.isStrict(val) && ll.fromStrict(val)["ResourceID"] != undefined
|
|
97
|
-
}
|
|
98
|
-
|
|
99
94
|
/**
|
|
100
95
|
* Returns true if given object is a smart.Resource
|
|
101
96
|
*/
|
|
@@ -561,6 +556,12 @@ field = func(f) {
|
|
|
561
556
|
self := undefined
|
|
562
557
|
strictSelf := undefined
|
|
563
558
|
|
|
559
|
+
id := ll.isFieldId(f) ? f : f.ID
|
|
560
|
+
|
|
561
|
+
if (is_undefined(id)) {
|
|
562
|
+
ll.panic("undefined id in smart.field. <f>="+f)
|
|
563
|
+
}
|
|
564
|
+
|
|
564
565
|
self = {
|
|
565
566
|
_type: "field",
|
|
566
567
|
|
|
@@ -571,7 +572,7 @@ field = func(f) {
|
|
|
571
572
|
* Name: string
|
|
572
573
|
*
|
|
573
574
|
*/
|
|
574
|
-
id:
|
|
575
|
+
id: id,
|
|
575
576
|
|
|
576
577
|
/**
|
|
577
578
|
* Field name
|
|
@@ -292,8 +292,8 @@ tpl := func() {
|
|
|
292
292
|
ll.assert(is_map(r), "expected map, got ", r)
|
|
293
293
|
|
|
294
294
|
for name, ref in r {
|
|
295
|
-
ll.assert(smart.isReference(ref), "expected reference, got ", ref)
|
|
296
|
-
ll.assert(is_undefined(newInputs[name]), "prepare name is already in use: ", name)
|
|
295
|
+
ll.assert(smart.isReference(ref), "expected reference, got %v", ref)
|
|
296
|
+
ll.assert(is_undefined(newInputs[name]), "prepare name is already in use: %s", name)
|
|
297
297
|
|
|
298
298
|
builder.add(name, ref)
|
|
299
299
|
}
|
|
@@ -15,6 +15,7 @@ path := import("@platforma-sdk/workflow-tengo:path")
|
|
|
15
15
|
enum := import("enum")
|
|
16
16
|
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
17
17
|
render := import("@platforma-sdk/workflow-tengo:render")
|
|
18
|
+
blobs := import("@platforma-sdk/workflow-tengo:file.internal")
|
|
18
19
|
|
|
19
20
|
create := func() {
|
|
20
21
|
wd := smart.ephemeralBuilder(constants.RTYPE_WORKDIR_CREATE).lockAndBuild()
|
|
@@ -23,8 +24,8 @@ create := func() {
|
|
|
23
24
|
|
|
24
25
|
_getFileFillRule := func(filePath, extension) {
|
|
25
26
|
return {
|
|
26
|
-
path: filePath,
|
|
27
27
|
type: "file",
|
|
28
|
+
path: filePath,
|
|
28
29
|
permissions: 0o600,
|
|
29
30
|
pathKey: filePath,
|
|
30
31
|
blobKey: filePath,
|
|
@@ -33,6 +34,26 @@ _getFileFillRule := func(filePath, extension) {
|
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
_getArchiveFillRule := func(dstDir, whatToExtract, archiveBlobKey) {
|
|
38
|
+
extractRules := []
|
|
39
|
+
for f in whatToExtract {
|
|
40
|
+
extractRules = append(extractRules, {
|
|
41
|
+
srcPath: f,
|
|
42
|
+
dstPath: f,
|
|
43
|
+
filePerms: 0o600,
|
|
44
|
+
dirPerms: 0o700
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
type: "archive",
|
|
50
|
+
path: dstDir,
|
|
51
|
+
blobKey: archiveBlobKey,
|
|
52
|
+
archiveType: "zip",
|
|
53
|
+
extractRules: extractRules
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
36
57
|
/**
|
|
37
58
|
* Generates a value fill rule for a given file path.
|
|
38
59
|
*
|
|
@@ -41,8 +62,8 @@ _getFileFillRule := func(filePath, extension) {
|
|
|
41
62
|
*/
|
|
42
63
|
_getValueFillRule := func(filePath) {
|
|
43
64
|
return {
|
|
44
|
-
path: filePath,
|
|
45
65
|
type: "value",
|
|
66
|
+
path: filePath,
|
|
46
67
|
permissions: 0o600,
|
|
47
68
|
pathKey: filePath,
|
|
48
69
|
valueKey: filePath
|
|
@@ -57,8 +78,8 @@ _getValueFillRule := func(filePath) {
|
|
|
57
78
|
*/
|
|
58
79
|
_getDirFillRule := func(dir) {
|
|
59
80
|
return {
|
|
60
|
-
path: dir,
|
|
61
81
|
type: "dir",
|
|
82
|
+
path: dir,
|
|
62
83
|
permissions: 0o700,
|
|
63
84
|
pathKey: ""
|
|
64
85
|
}
|
|
@@ -75,7 +96,9 @@ _fill := func(workdir) {
|
|
|
75
96
|
wd := smart.ephemeralBuilder(constants.RTYPE_WORKDIR_FILL)
|
|
76
97
|
wd.getField("workdirIn").set(workdir)
|
|
77
98
|
|
|
99
|
+
blobs := {}
|
|
78
100
|
files := {}
|
|
101
|
+
zipExtractions := {}
|
|
79
102
|
values := {}
|
|
80
103
|
dirs := {}
|
|
81
104
|
|
|
@@ -91,6 +114,49 @@ _fill := func(workdir) {
|
|
|
91
114
|
sets.add(dirs, newDirs...)
|
|
92
115
|
},
|
|
93
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Adds files from given zip archive into working directory.
|
|
119
|
+
*
|
|
120
|
+
* @param zipResource: smart.reference - reference to zip archive to be extracted.
|
|
121
|
+
* @param destinationDir: string - path inside working directory for files to be extracted from archive.
|
|
122
|
+
* Consider it as 'cd' before extraction. It makes all extracted files to be
|
|
123
|
+
* stored inside this directory.
|
|
124
|
+
* @param pathsInArchive: string[] - paths inside the archive to be extracted into destinationDir.
|
|
125
|
+
* Empty array means 'unpack full archive'.
|
|
126
|
+
* Full file paths are preserved during extraction: the file 'a/b/file.txt' would
|
|
127
|
+
* be extracted into '<destinationDir>/a/b/file.txt', NOT '<destinationDir>/file.txt',
|
|
128
|
+
*/
|
|
129
|
+
addFromZip: func(zipResource, destinationDir, pathsInArchive) {
|
|
130
|
+
validation.assertJsonSchema(zipResource, validation.reference, "workdir.builder.addFromZip: <zipResource> is not smart.reference")
|
|
131
|
+
validation.assertJsonSchema(destinationDir, "string", "workdir.builder.addFromZip: <destinationDir> is not a string. It must be a path inside working directory")
|
|
132
|
+
|
|
133
|
+
if (is_undefined(pathsInArchive)) {
|
|
134
|
+
pathsInArchive = []
|
|
135
|
+
}
|
|
136
|
+
validation.assertJsonSchema(pathsInArchive, ["string"], "workdir.builder.addFromZip: <pathsInArchive> must contain list of strings with paths inside archive")
|
|
137
|
+
|
|
138
|
+
blobKey := ll.idToString(zipResource.id)
|
|
139
|
+
|
|
140
|
+
if is_undefined(blobs[blobKey]) {
|
|
141
|
+
blobs[blobKey] = zipResource
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if is_undefined(zipExtractions[blobKey]) {
|
|
145
|
+
zipExtractions[blobKey] = {}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if is_undefined(zipExtractions[blobKey][destinationDir]) || len(pathsInArchive) == 0 {
|
|
149
|
+
zipExtractions[blobKey][destinationDir] = pathsInArchive
|
|
150
|
+
return self
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (len(pathsInArchive) > 0) {
|
|
154
|
+
zipExtractions[blobKey][destinationDir] = append(zipExtractions[blobKey][destinationDir], pathsInArchive...)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return self
|
|
158
|
+
},
|
|
159
|
+
|
|
94
160
|
/**
|
|
95
161
|
* Adds a file to the workdir.
|
|
96
162
|
*
|
|
@@ -102,6 +168,7 @@ _fill := func(workdir) {
|
|
|
102
168
|
validation.assertJsonSchema(fileResource, validation.reference)
|
|
103
169
|
|
|
104
170
|
files[fileName] = fileResource
|
|
171
|
+
blobs[fileName] = fileResource
|
|
105
172
|
self._addDirs(fileName)
|
|
106
173
|
|
|
107
174
|
return self
|
|
@@ -168,6 +235,12 @@ _fill := func(workdir) {
|
|
|
168
235
|
rules = append(rules, _getValueFillRule(fileName))
|
|
169
236
|
}
|
|
170
237
|
|
|
238
|
+
for blobKey, directories in zipExtractions {
|
|
239
|
+
for dstDir, whatToExtract in directories {
|
|
240
|
+
rules = append(rules, _getArchiveFillRule(dstDir, whatToExtract, blobKey))
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
171
244
|
rulesRes := smart.createValueResource(
|
|
172
245
|
constants.RTYPE_WORKDIR_FILL_RULES,
|
|
173
246
|
json.encode(rules)
|
|
@@ -175,7 +248,7 @@ _fill := func(workdir) {
|
|
|
175
248
|
self.getField("rules").set(rulesRes)
|
|
176
249
|
|
|
177
250
|
self.getField("dataIn").set(smart.createBinaryMapResource(values))
|
|
178
|
-
self.getField("blobsIn").set(smart.createBlobMapResource(
|
|
251
|
+
self.getField("blobsIn").set(smart.createBlobMapResource(blobs))
|
|
179
252
|
|
|
180
253
|
return self.lockAndBuild().outputs().workdirOut
|
|
181
254
|
}
|
|
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": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Tengo SDK library",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
"./dist/**/*"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@milaboratories/software-pframes-conv": "^1.6.
|
|
20
|
-
"@platforma-open/milaboratories.software-small-binaries": "^1.14.
|
|
19
|
+
"@milaboratories/software-pframes-conv": "^1.6.4",
|
|
20
|
+
"@platforma-open/milaboratories.software-small-binaries": "^1.14.5"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@platforma-sdk/tengo-builder": "^1.
|
|
23
|
+
"@platforma-sdk/tengo-builder": "^1.16.0"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"type-check": "pl-tengo check",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"test": "pl-tengo test",
|
|
29
29
|
"format": "/usr/bin/env emacs --quick --script ./format.el",
|
|
30
30
|
"format-stdin": "/usr/bin/env emacs --quick --script ./format-stdin.el",
|
|
31
|
-
"vscode-tags": "./create_tags.sh .tags false",
|
|
31
|
+
"vscode-tags": "cd ../../ && bash ./sdk/workflow-tengo/create_tags.sh .tags false",
|
|
32
32
|
"emacs-tags": "./create_tags.sh ../TAGS false -e",
|
|
33
33
|
"do-pack": "rm -f *.tgz && pnpm pack && mv *.tgz package.tgz"
|
|
34
34
|
}
|