@fnndsc/brasa 0.55.3 → 0.56.1
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/buildinfo.json +1 -1
- package/dist/builtins/berth/berth.d.ts +18 -0
- package/dist/builtins/berth/berth.js +471 -0
- package/dist/builtins/berth/berth.js.map +1 -0
- package/dist/builtins/berth/berthClose.d.ts +23 -0
- package/dist/builtins/berth/berthClose.js +475 -0
- package/dist/builtins/berth/berthClose.js.map +1 -0
- package/dist/builtins/berth/berthCube.d.ts +238 -0
- package/dist/builtins/berth/berthCube.js +366 -0
- package/dist/builtins/berth/berthCube.js.map +1 -0
- package/dist/builtins/berth/berthDisk.d.ts +13 -0
- package/dist/builtins/berth/berthDisk.js +34 -0
- package/dist/builtins/berth/berthDisk.js.map +1 -0
- package/dist/builtins/berth/berthProcesses.d.ts +47 -0
- package/dist/builtins/berth/berthProcesses.js +204 -0
- package/dist/builtins/berth/berthProcesses.js.map +1 -0
- package/dist/builtins/berth/berthRecord.d.ts +210 -0
- package/dist/builtins/berth/berthRecord.js +130 -0
- package/dist/builtins/berth/berthRecord.js.map +1 -0
- package/dist/builtins/berth/berthState.d.ts +139 -0
- package/dist/builtins/berth/berthState.js +194 -0
- package/dist/builtins/berth/berthState.js.map +1 -0
- package/dist/builtins/berth/berthWords.d.ts +87 -0
- package/dist/builtins/berth/berthWords.js +78 -0
- package/dist/builtins/berth/berthWords.js.map +1 -0
- package/dist/builtins/helpSections.js +59 -1
- package/dist/builtins/helpSections.js.map +1 -1
- package/dist/chris/chrisCommands.js +6 -0
- package/dist/chris/chrisCommands.js.map +1 -1
- package/dist/chris/help.js +50 -0
- package/dist/chris/help.js.map +1 -1
- package/dist/notes.json +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What stands at a ChRIS path.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* const entry: RemoteEntry = { kind: 'file', size: 1024 };
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export interface RemoteEntry {
|
|
10
|
+
/** A file, or a folder (a link to a folder counts as one). */
|
|
11
|
+
readonly kind: 'file' | 'folder';
|
|
12
|
+
/** A file's size in bytes; 0 for a folder. */
|
|
13
|
+
readonly size: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* One file under a folder being anchored.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const file: RemoteFile = { name: 'anat/t1.nii', source: '/home/alice/uploads/s/anat/t1.nii', size: 1024 };
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export interface RemoteFile {
|
|
24
|
+
/** Its name relative to the folder, with `/` between parts. */
|
|
25
|
+
readonly name: string;
|
|
26
|
+
/** The ChRIS path its bytes are read from, links followed. */
|
|
27
|
+
readonly source: string;
|
|
28
|
+
/** Its size in bytes. */
|
|
29
|
+
readonly size: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* One file's bytes as ChRIS sent them: a stream, or a PACS file whole.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const bytes: RemoteBytes = { stream: Buffer.from('DICM') };
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export interface RemoteBytes {
|
|
40
|
+
/** The bytes: a stream to read, or the whole file. */
|
|
41
|
+
readonly stream: NodeJS.ReadableStream | Buffer;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The person's home in ChRIS, `/home/<user>`.
|
|
45
|
+
*
|
|
46
|
+
* @returns The home the session names; `/` when no session is installed.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* await berthHome_get(); // '/home/alice'
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function berthHome_get(): Promise<string>;
|
|
54
|
+
/**
|
|
55
|
+
* A typed path as an absolute ChRIS path: relative to the working directory,
|
|
56
|
+
* `~` for the home, and a `/proc/jobs/…/data` path through the job's link
|
|
57
|
+
* to the output it names, as `ls` and `download` enter it.
|
|
58
|
+
*
|
|
59
|
+
* @param typed - The path as typed.
|
|
60
|
+
* @returns The absolute path.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* await berthPath_resolve('~/uploads/weights'); // '/home/alice/uploads/weights'
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function berthPath_resolve(typed: string): Promise<string>;
|
|
68
|
+
/**
|
|
69
|
+
* What stands at a path: a file with its size, a folder, or nothing.
|
|
70
|
+
*
|
|
71
|
+
* @param target - An absolute ChRIS path.
|
|
72
|
+
* @returns The entry, or null when nothing of that name is in its folder.
|
|
73
|
+
* @throws {Error} When the folder holding it cannot be listed.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* await remoteEntry_read('/home/alice/uploads/t1.nii'); // { kind: 'file', size: 1024 }
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function remoteEntry_read(target: string): Promise<RemoteEntry | null>;
|
|
81
|
+
/**
|
|
82
|
+
* Every file under a ChRIS folder, with its size and the path to read it at.
|
|
83
|
+
*
|
|
84
|
+
* @param folder - An absolute ChRIS path (logical: the folder as the person sees it).
|
|
85
|
+
* @returns The files, by name relative to the folder; empty for an empty folder.
|
|
86
|
+
* @throws {Error} When a folder under it cannot be listed.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* await remoteTree_list('/home/alice/uploads/s'); // [{ name: 'anat/t1.nii', source: '/home/alice/uploads/s/anat/t1.nii', size: 1024 }]
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare function remoteTree_list(folder: string): Promise<RemoteFile[]>;
|
|
94
|
+
/**
|
|
95
|
+
* One ChRIS file as a file to read: its path with links followed.
|
|
96
|
+
*
|
|
97
|
+
* @param file - An absolute ChRIS path to a file.
|
|
98
|
+
* @param size - Its size in bytes.
|
|
99
|
+
* @returns The file, named by its last part.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* await remoteFile_locate('/home/alice/uploads/t1.nii', 1024); // { name: 't1.nii', source: '/home/alice/uploads/t1.nii', size: 1024 }
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export declare function remoteFile_locate(file: string, size: number): Promise<RemoteFile>;
|
|
107
|
+
/**
|
|
108
|
+
* A plugin instance's status, as CUBE reports it.
|
|
109
|
+
*
|
|
110
|
+
* @param instanceId - The plugin instance's id.
|
|
111
|
+
* @returns Its status (`finishedSuccessfully`, `started`, …).
|
|
112
|
+
* @throws {Error} When CUBE does not answer for it.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* await nodeStatus_get(41); // 'finishedSuccessfully'
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export declare function nodeStatus_get(instanceId: number): Promise<string>;
|
|
120
|
+
/**
|
|
121
|
+
* One ChRIS file's bytes, as the kernel reads them with the session's token.
|
|
122
|
+
*
|
|
123
|
+
* @param source - The file's path (physical).
|
|
124
|
+
* @returns Its bytes: a stream, or a PACS file whole as a Buffer.
|
|
125
|
+
* @throws {Error} When ChRIS does not give the bytes.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* const bytes: RemoteBytes = await remoteFile_open('/home/alice/uploads/t1.nii');
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export declare function remoteFile_open(source: string): Promise<RemoteBytes>;
|
|
133
|
+
/**
|
|
134
|
+
* What a plugin run or a feed's start answered: the plugin instance made, or
|
|
135
|
+
* CUBE's reason for making none.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* const made: AttachOutcome = { ok: true, instance: 57, feed: 5, pluginName: 'pl-dsdircopy' };
|
|
140
|
+
* const refused: AttachOutcome = { ok: false, reason: 'No matching plugins found' };
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export type AttachOutcome = AttachMade | AttachRefused;
|
|
144
|
+
/** A plugin instance made. */
|
|
145
|
+
export interface AttachMade {
|
|
146
|
+
/** The instance was made. */
|
|
147
|
+
readonly ok: true;
|
|
148
|
+
/** Its id. */
|
|
149
|
+
readonly instance: number;
|
|
150
|
+
/** The feed it is in; null when CUBE did not say. */
|
|
151
|
+
readonly feed: number | null;
|
|
152
|
+
/** The plugin's name, as CUBE gave it. */
|
|
153
|
+
readonly pluginName: string;
|
|
154
|
+
}
|
|
155
|
+
/** No plugin instance made, and why. */
|
|
156
|
+
export interface AttachRefused {
|
|
157
|
+
/** No instance was made. */
|
|
158
|
+
readonly ok: false;
|
|
159
|
+
/** CUBE's reason, as the kernel stacked it. */
|
|
160
|
+
readonly reason: string;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* What an upload of a folder's contents did.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* const sent: UploadOutcome = { files: 3, bytes: 1024, failed: 0 };
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
export interface UploadOutcome {
|
|
171
|
+
/** The files that reached ChRIS. */
|
|
172
|
+
readonly files: number;
|
|
173
|
+
/** Their bytes. */
|
|
174
|
+
readonly bytes: number;
|
|
175
|
+
/** The files that did not. */
|
|
176
|
+
readonly failed: number;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Uploads everything inside a local folder into a ChRIS folder: each entry
|
|
180
|
+
* keeps its name under the target, a folder with all it holds, hidden files
|
|
181
|
+
* too. It is the upload `upload` runs, with its progress, entry by entry, so
|
|
182
|
+
* the local folder's own name is not added to the target.
|
|
183
|
+
*
|
|
184
|
+
* @param local - The local folder.
|
|
185
|
+
* @param target - The ChRIS folder, absolute; the upload makes it.
|
|
186
|
+
* @returns What reached ChRIS and what did not.
|
|
187
|
+
* @throws {Error} When the upload cannot start (a local entry that cannot be read).
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```ts
|
|
191
|
+
* await folderContents_upload('/outgoing', '/home/alice/berth/0b6c…'); // { files: 3, bytes: 1024, failed: 0 }
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
export declare function folderContents_upload(local: string, target: string): Promise<UploadOutcome>;
|
|
195
|
+
/**
|
|
196
|
+
* Runs a registered plugin by name as a child of a plugin instance, as a
|
|
197
|
+
* line run in that node's folder does: the version CUBE lists first under
|
|
198
|
+
* that exact name.
|
|
199
|
+
*
|
|
200
|
+
* @param plugin - The plugin's name, `pl-dsdircopy`.
|
|
201
|
+
* @param parent - The plugin instance it runs after.
|
|
202
|
+
* @param parameters - Its parameters by name (`dir`), and `title` for the node.
|
|
203
|
+
* @returns The instance made, or CUBE's reason.
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* await pluginChild_run('pl-dsdircopy', 41, { dir: '/home/alice/berth/0b6c…', title: 'berth work' });
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
export declare function pluginChild_run(plugin: string, parent: number, parameters: Readonly<Record<string, string>>): Promise<AttachOutcome>;
|
|
211
|
+
/**
|
|
212
|
+
* Starts a new feed from `pl-dircopy` over some ChRIS paths: its `dir` is
|
|
213
|
+
* the paths joined by commas, as the fs plugin takes several, and its title
|
|
214
|
+
* names the feed, as CUBE names a feed after its root.
|
|
215
|
+
*
|
|
216
|
+
* @param paths - The ChRIS paths, absolute.
|
|
217
|
+
* @param name - The feed's name, the root node's title.
|
|
218
|
+
* @returns The root instance made with its feed, or CUBE's reason.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```ts
|
|
222
|
+
* await feedRoot_create(['/home/alice/uploads/weights', '/PUBLIC/atlas'], 'berth work');
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
export declare function feedRoot_create(paths: readonly string[], name: string): Promise<AttachOutcome>;
|
|
226
|
+
/**
|
|
227
|
+
* Names a feed.
|
|
228
|
+
*
|
|
229
|
+
* @param feed - The feed's id.
|
|
230
|
+
* @param name - Its name.
|
|
231
|
+
* @returns Null when CUBE took it; else CUBE's reason.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```ts
|
|
235
|
+
* await feedName_set(12, 'seg run'); // null
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
export declare function feedName_set(feed: number, name: string): Promise<string | null>;
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file What the `berth` verbs ask of ChRIS: a path resolved as the session
|
|
3
|
+
* resolves it, what stands there, every file beneath it with its size, a
|
|
4
|
+
* plugin instance's status and one file's bytes, for an anchor; and for a
|
|
5
|
+
* close, a local folder uploaded into the person's files, a ds plugin run as
|
|
6
|
+
* a child of a node, and a feed started from an fs plugin over some paths.
|
|
7
|
+
*
|
|
8
|
+
* Every read goes through the session's own kernel (salsa, with the
|
|
9
|
+
* session's token), so an anchor reads only what ChRIS lets that person
|
|
10
|
+
* read. A folder is walked through the session's dispatcher, its links
|
|
11
|
+
* followed to the folders they name (a node's output that is a link to a
|
|
12
|
+
* PACS series, an upload a feed was made from). A PACS file comes back whole,
|
|
13
|
+
* as a Buffer, where any other comes back as a stream; both are handed on.
|
|
14
|
+
*
|
|
15
|
+
* Key components:
|
|
16
|
+
* - RemoteEntry, RemoteFile, RemoteBytes: what the reads answer.
|
|
17
|
+
* - berthHome_get: the person's home in ChRIS.
|
|
18
|
+
* - berthPath_resolve: a typed path as an absolute ChRIS path.
|
|
19
|
+
* - remoteEntry_read: what stands at a path, a file or a folder.
|
|
20
|
+
* - remoteTree_list: every file under a folder, with its size and where to read it.
|
|
21
|
+
* - nodeStatus_get: a plugin instance's status, as CUBE reports it.
|
|
22
|
+
* - remoteFile_open: one file's bytes, a stream or a Buffer.
|
|
23
|
+
* - folderContents_upload: everything inside a local folder, into a ChRIS folder.
|
|
24
|
+
* - pluginChild_run: a registered plugin run by name as a child of a plugin instance.
|
|
25
|
+
* - feedRoot_create: a new feed whose root is `pl-dircopy` over some paths.
|
|
26
|
+
* - feedName_set: a feed named.
|
|
27
|
+
* - AttachOutcome, UploadOutcome: what those answer.
|
|
28
|
+
*
|
|
29
|
+
* Typical usage:
|
|
30
|
+
* ```ts
|
|
31
|
+
* const path: string = await berthPath_resolve('~/uploads/weights');
|
|
32
|
+
* const files: RemoteFile[] = await remoteTree_list(path);
|
|
33
|
+
* const bytes: RemoteBytes = await remoteFile_open(files[0].source);
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Dependencies: a connected ChRIS session (salsa); the session's dispatcher
|
|
37
|
+
* and path mapper; chili's folder upload, the one `upload` runs. Sizes are
|
|
38
|
+
* bytes. A run or a feed CUBE refuses answers with CUBE's reason, as the
|
|
39
|
+
* kernel stacked it.
|
|
40
|
+
*
|
|
41
|
+
* @module
|
|
42
|
+
*/
|
|
43
|
+
import { readdirSync } from 'node:fs';
|
|
44
|
+
import { join, posix } from 'node:path';
|
|
45
|
+
import { errorStack } from '@fnndsc/fond';
|
|
46
|
+
import { backendInstalled_get } from '../../core/backend.js';
|
|
47
|
+
import { error_stripDebugPrefix } from '../../core/errorText.js';
|
|
48
|
+
import { vfsDispatcher_get } from '../../core/filesystem.js';
|
|
49
|
+
import { path_resolve } from '../../core/paths.js';
|
|
50
|
+
import { sink_get } from '../../core/sink.js';
|
|
51
|
+
/** A `/proc/jobs` path at or under a job's `data` link: the link, and what follows it. */
|
|
52
|
+
const PROC_DATA_PATH = /^(\/proc\/jobs\/feed_\d+(?:\/[^/]+_\d+)+\/data)(\/.*)?$/;
|
|
53
|
+
/** Listing kinds that are folders to walk into. */
|
|
54
|
+
const FOLDER_KINDS = new Set(['dir', 'vfs']);
|
|
55
|
+
/**
|
|
56
|
+
* The person's home in ChRIS, `/home/<user>`.
|
|
57
|
+
*
|
|
58
|
+
* @returns The home the session names; `/` when no session is installed.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* await berthHome_get(); // '/home/alice'
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export async function berthHome_get() {
|
|
66
|
+
return (await backendInstalled_get()?.session.home_get()) ?? '/';
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A typed path as an absolute ChRIS path: relative to the working directory,
|
|
70
|
+
* `~` for the home, and a `/proc/jobs/…/data` path through the job's link
|
|
71
|
+
* to the output it names, as `ls` and `download` enter it.
|
|
72
|
+
*
|
|
73
|
+
* @param typed - The path as typed.
|
|
74
|
+
* @returns The absolute path.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* await berthPath_resolve('~/uploads/weights'); // '/home/alice/uploads/weights'
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export async function berthPath_resolve(typed) {
|
|
82
|
+
const resolved = await path_resolve(typed);
|
|
83
|
+
const link = resolved.match(PROC_DATA_PATH);
|
|
84
|
+
if (link === null)
|
|
85
|
+
return resolved;
|
|
86
|
+
const target = await vfsDispatcher_get().linkTarget_resolve(link[1]);
|
|
87
|
+
return target.ok ? `${target.value}${link[2] ?? ''}` : resolved;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The path CUBE stores a logical path at, links followed; the path as given
|
|
91
|
+
* when it maps to nothing else.
|
|
92
|
+
*
|
|
93
|
+
* @param logical - An absolute ChRIS path.
|
|
94
|
+
* @returns The physical path.
|
|
95
|
+
*/
|
|
96
|
+
async function physicalPath_get(logical) {
|
|
97
|
+
try {
|
|
98
|
+
const { logicalPath_resolve } = await import('@fnndsc/chili/utils');
|
|
99
|
+
const physical = await logicalPath_resolve(logical);
|
|
100
|
+
return physical.ok ? physical.value : logical;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return logical;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Lists one folder through the session's dispatcher.
|
|
108
|
+
*
|
|
109
|
+
* @param folder - The folder.
|
|
110
|
+
* @returns Its entries.
|
|
111
|
+
* @throws {Error} When it cannot be listed: a walk must not read an unlistable folder as an empty one.
|
|
112
|
+
*/
|
|
113
|
+
async function folder_list(folder) {
|
|
114
|
+
const listed = await vfsDispatcher_get().list(folder);
|
|
115
|
+
if (!listed.ok)
|
|
116
|
+
throw new Error(`cannot list ${folder}`);
|
|
117
|
+
return listed.value;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* What stands at a path: a file with its size, a folder, or nothing.
|
|
121
|
+
*
|
|
122
|
+
* @param target - An absolute ChRIS path.
|
|
123
|
+
* @returns The entry, or null when nothing of that name is in its folder.
|
|
124
|
+
* @throws {Error} When the folder holding it cannot be listed.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* await remoteEntry_read('/home/alice/uploads/t1.nii'); // { kind: 'file', size: 1024 }
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export async function remoteEntry_read(target) {
|
|
132
|
+
if (target === '/')
|
|
133
|
+
return { kind: 'folder', size: 0 };
|
|
134
|
+
const items = await folder_list(posix.dirname(target));
|
|
135
|
+
const found = items.find((item) => item.name === posix.basename(target));
|
|
136
|
+
if (found === undefined)
|
|
137
|
+
return null;
|
|
138
|
+
return found.type === 'file' ? { kind: 'file', size: found.size } : { kind: 'folder', size: 0 };
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Every file under a folder, walked depth first, links followed to the
|
|
142
|
+
* folders they name; a folder already walked is not walked again.
|
|
143
|
+
*
|
|
144
|
+
* @param folder - The folder as listed (a physical path).
|
|
145
|
+
* @param prefix - The relative name of the folder under the anchor ('' at its top).
|
|
146
|
+
* @param seen - Folders already walked.
|
|
147
|
+
* @returns The files.
|
|
148
|
+
* @throws {Error} When a folder under it cannot be listed.
|
|
149
|
+
*/
|
|
150
|
+
async function folderFiles_walk(folder, prefix, seen) {
|
|
151
|
+
if (seen.has(folder))
|
|
152
|
+
return [];
|
|
153
|
+
seen.add(folder);
|
|
154
|
+
const files = [];
|
|
155
|
+
for (const item of await folder_list(folder)) {
|
|
156
|
+
const name = prefix === '' ? item.name : `${prefix}/${item.name}`;
|
|
157
|
+
const here = posix.join(folder, item.name);
|
|
158
|
+
if (item.type === 'file') {
|
|
159
|
+
files.push({ name, source: here, size: item.size });
|
|
160
|
+
}
|
|
161
|
+
else if (FOLDER_KINDS.has(item.type)) {
|
|
162
|
+
files.push(...await folderFiles_walk(here, name, seen));
|
|
163
|
+
}
|
|
164
|
+
else if (item.type === 'link' && item.target !== undefined && item.target !== '') {
|
|
165
|
+
const target = item.target.startsWith('/') ? item.target : posix.join(folder, item.target);
|
|
166
|
+
files.push(...await folderFiles_walk(target, name, seen));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return files;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Every file under a ChRIS folder, with its size and the path to read it at.
|
|
173
|
+
*
|
|
174
|
+
* @param folder - An absolute ChRIS path (logical: the folder as the person sees it).
|
|
175
|
+
* @returns The files, by name relative to the folder; empty for an empty folder.
|
|
176
|
+
* @throws {Error} When a folder under it cannot be listed.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts
|
|
180
|
+
* await remoteTree_list('/home/alice/uploads/s'); // [{ name: 'anat/t1.nii', source: '/home/alice/uploads/s/anat/t1.nii', size: 1024 }]
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export async function remoteTree_list(folder) {
|
|
184
|
+
return folderFiles_walk(await physicalPath_get(folder), '', new Set());
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* One ChRIS file as a file to read: its path with links followed.
|
|
188
|
+
*
|
|
189
|
+
* @param file - An absolute ChRIS path to a file.
|
|
190
|
+
* @param size - Its size in bytes.
|
|
191
|
+
* @returns The file, named by its last part.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```ts
|
|
195
|
+
* await remoteFile_locate('/home/alice/uploads/t1.nii', 1024); // { name: 't1.nii', source: '/home/alice/uploads/t1.nii', size: 1024 }
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
export async function remoteFile_locate(file, size) {
|
|
199
|
+
return { name: posix.basename(file), source: await physicalPath_get(file), size };
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* A plugin instance's status, as CUBE reports it.
|
|
203
|
+
*
|
|
204
|
+
* @param instanceId - The plugin instance's id.
|
|
205
|
+
* @returns Its status (`finishedSuccessfully`, `started`, …).
|
|
206
|
+
* @throws {Error} When CUBE does not answer for it.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* await nodeStatus_get(41); // 'finishedSuccessfully'
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
export async function nodeStatus_get(instanceId) {
|
|
214
|
+
const { job_statusFetch } = await import('@fnndsc/salsa');
|
|
215
|
+
const status = await job_statusFetch(instanceId);
|
|
216
|
+
if (!status.ok)
|
|
217
|
+
throw new Error(`cannot read the status of plugin instance ${instanceId}`);
|
|
218
|
+
return status.value;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* One ChRIS file's bytes, as the kernel reads them with the session's token.
|
|
222
|
+
*
|
|
223
|
+
* @param source - The file's path (physical).
|
|
224
|
+
* @returns Its bytes: a stream, or a PACS file whole as a Buffer.
|
|
225
|
+
* @throws {Error} When ChRIS does not give the bytes.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```ts
|
|
229
|
+
* const bytes: RemoteBytes = await remoteFile_open('/home/alice/uploads/t1.nii');
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
export async function remoteFile_open(source) {
|
|
233
|
+
const { fileContent_getBinaryStream } = await import('@fnndsc/salsa');
|
|
234
|
+
const opened = await fileContent_getBinaryStream(source);
|
|
235
|
+
if (!opened.ok)
|
|
236
|
+
throw new Error(`cannot read ${source}`);
|
|
237
|
+
const stream = opened.value.stream;
|
|
238
|
+
if (Buffer.isBuffer(stream))
|
|
239
|
+
return { stream };
|
|
240
|
+
return { stream: stream };
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* CUBE's reason for a refusal: what the kernel stacked since a mark, taken off the stack.
|
|
244
|
+
*
|
|
245
|
+
* @param mark - The stack's mark before the call.
|
|
246
|
+
* @param fallback - The words when nothing was stacked.
|
|
247
|
+
* @returns The reason.
|
|
248
|
+
*/
|
|
249
|
+
function cubeReason_take(mark, fallback) {
|
|
250
|
+
const stacked = errorStack.checkpoint_drain(mark);
|
|
251
|
+
const words = stacked.map((message) => error_stripDebugPrefix(message.message).trim()).filter((message) => message !== '');
|
|
252
|
+
return words.length > 0 ? words.join('; ') : fallback;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Uploads everything inside a local folder into a ChRIS folder: each entry
|
|
256
|
+
* keeps its name under the target, a folder with all it holds, hidden files
|
|
257
|
+
* too. It is the upload `upload` runs, with its progress, entry by entry, so
|
|
258
|
+
* the local folder's own name is not added to the target.
|
|
259
|
+
*
|
|
260
|
+
* @param local - The local folder.
|
|
261
|
+
* @param target - The ChRIS folder, absolute; the upload makes it.
|
|
262
|
+
* @returns What reached ChRIS and what did not.
|
|
263
|
+
* @throws {Error} When the upload cannot start (a local entry that cannot be read).
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```ts
|
|
267
|
+
* await folderContents_upload('/outgoing', '/home/alice/berth/0b6c…'); // { files: 3, bytes: 1024, failed: 0 }
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
export async function folderContents_upload(local, target) {
|
|
271
|
+
const { files_uploadWithProgress } = await import('@fnndsc/chili/commands/fs/upload.js');
|
|
272
|
+
let files = 0;
|
|
273
|
+
let bytes = 0;
|
|
274
|
+
let failed = 0;
|
|
275
|
+
for (const name of readdirSync(local).sort()) {
|
|
276
|
+
const summary = await files_uploadWithProgress(join(local, name), target, {
|
|
277
|
+
expandLocalGlob: false,
|
|
278
|
+
force: true,
|
|
279
|
+
onNotice: (message, channel) => {
|
|
280
|
+
if (channel === 'err')
|
|
281
|
+
sink_get().err_write(`${message}\n`);
|
|
282
|
+
},
|
|
283
|
+
onProgress: (event) => sink_get().progress_write({ ...event, label: name }),
|
|
284
|
+
});
|
|
285
|
+
files += summary.transferredCount;
|
|
286
|
+
bytes += summary.transferSize;
|
|
287
|
+
failed += summary.failedCount;
|
|
288
|
+
}
|
|
289
|
+
const { listCache_get } = await import('@fnndsc/cumin');
|
|
290
|
+
listCache_get().cache_invalidateTree(target);
|
|
291
|
+
listCache_get().cache_invalidate(posix.dirname(target));
|
|
292
|
+
return { files, bytes, failed };
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Runs a registered plugin by name as a child of a plugin instance, as a
|
|
296
|
+
* line run in that node's folder does: the version CUBE lists first under
|
|
297
|
+
* that exact name.
|
|
298
|
+
*
|
|
299
|
+
* @param plugin - The plugin's name, `pl-dsdircopy`.
|
|
300
|
+
* @param parent - The plugin instance it runs after.
|
|
301
|
+
* @param parameters - Its parameters by name (`dir`), and `title` for the node.
|
|
302
|
+
* @returns The instance made, or CUBE's reason.
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```ts
|
|
306
|
+
* await pluginChild_run('pl-dsdircopy', 41, { dir: '/home/alice/berth/0b6c…', title: 'berth work' });
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
309
|
+
export async function pluginChild_run(plugin, parent, parameters) {
|
|
310
|
+
const { plugin_run } = await import('@fnndsc/salsa');
|
|
311
|
+
const mark = errorStack.checkpoint_mark();
|
|
312
|
+
const made = await plugin_run(`name_exact:${plugin}`, { ...parameters, previous_id: parent });
|
|
313
|
+
const instance = made?.['id'];
|
|
314
|
+
if (made === null || typeof instance !== 'number')
|
|
315
|
+
return { ok: false, reason: cubeReason_take(mark, `CUBE made no ${plugin} instance`) };
|
|
316
|
+
errorStack.checkpoint_drain(mark);
|
|
317
|
+
const feed = made['feed_id'];
|
|
318
|
+
const pluginName = made['plugin_name'];
|
|
319
|
+
return { ok: true, instance, feed: typeof feed === 'number' ? feed : null, pluginName: typeof pluginName === 'string' ? pluginName : plugin };
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Starts a new feed from `pl-dircopy` over some ChRIS paths: its `dir` is
|
|
323
|
+
* the paths joined by commas, as the fs plugin takes several, and its title
|
|
324
|
+
* names the feed, as CUBE names a feed after its root.
|
|
325
|
+
*
|
|
326
|
+
* @param paths - The ChRIS paths, absolute.
|
|
327
|
+
* @param name - The feed's name, the root node's title.
|
|
328
|
+
* @returns The root instance made with its feed, or CUBE's reason.
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* ```ts
|
|
332
|
+
* await feedRoot_create(['/home/alice/uploads/weights', '/PUBLIC/atlas'], 'berth work');
|
|
333
|
+
* ```
|
|
334
|
+
*/
|
|
335
|
+
export async function feedRoot_create(paths, name) {
|
|
336
|
+
const { feed_create } = await import('@fnndsc/salsa');
|
|
337
|
+
const mark = errorStack.checkpoint_mark();
|
|
338
|
+
const made = await feed_create([...paths], { title: name });
|
|
339
|
+
const instance = made?.pluginInstance.data.id;
|
|
340
|
+
if (made === null || typeof instance !== 'number')
|
|
341
|
+
return { ok: false, reason: cubeReason_take(mark, 'CUBE made no feed') };
|
|
342
|
+
errorStack.checkpoint_drain(mark);
|
|
343
|
+
return { ok: true, instance, feed: made.id, pluginName: 'pl-dircopy' };
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Names a feed.
|
|
347
|
+
*
|
|
348
|
+
* @param feed - The feed's id.
|
|
349
|
+
* @param name - Its name.
|
|
350
|
+
* @returns Null when CUBE took it; else CUBE's reason.
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```ts
|
|
354
|
+
* await feedName_set(12, 'seg run'); // null
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
export async function feedName_set(feed, name) {
|
|
358
|
+
const { feed_rename } = await import('@fnndsc/cumin');
|
|
359
|
+
const mark = errorStack.checkpoint_mark();
|
|
360
|
+
const renamed = await feed_rename(feed, name);
|
|
361
|
+
if (!renamed.ok)
|
|
362
|
+
return cubeReason_take(mark, `CUBE did not name feed ${feed}`);
|
|
363
|
+
errorStack.checkpoint_drain(mark);
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
//# sourceMappingURL=berthCube.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"berthCube.js","sourceRoot":"","sources":["../../../src/builtins/berth/berthCube.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bytes free on the filesystem holding a folder, for an unprivileged
|
|
3
|
+
* user: the blocks available to one, not those kept for root.
|
|
4
|
+
*
|
|
5
|
+
* @param folder - The folder.
|
|
6
|
+
* @returns Free bytes.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* freeBytes_get('/anchors'); // 85899345920
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export declare function freeBytes_get(folder: string): number;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The room a berth's disk has: the bytes free on the filesystem that
|
|
3
|
+
* holds a folder.
|
|
4
|
+
*
|
|
5
|
+
* Key components:
|
|
6
|
+
* - freeBytes_get: the bytes an unprivileged user may still write there.
|
|
7
|
+
*
|
|
8
|
+
* Typical usage:
|
|
9
|
+
* ```ts
|
|
10
|
+
* const room: number = freeBytes_get('/anchors');
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Dependencies: Node.js built-ins only (`statfs`).
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { statfsSync } from 'node:fs';
|
|
18
|
+
/**
|
|
19
|
+
* The bytes free on the filesystem holding a folder, for an unprivileged
|
|
20
|
+
* user: the blocks available to one, not those kept for root.
|
|
21
|
+
*
|
|
22
|
+
* @param folder - The folder.
|
|
23
|
+
* @returns Free bytes.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* freeBytes_get('/anchors'); // 85899345920
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function freeBytes_get(folder) {
|
|
31
|
+
const stats = statfsSync(folder);
|
|
32
|
+
return Number(stats.bavail) * Number(stats.bsize);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=berthDisk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"berthDisk.js","sourceRoot":"","sources":["../../../src/builtins/berth/berthDisk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,UAAU,EAAgB,MAAM,SAAS,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,MAAM,KAAK,GAAY,UAAU,CAAC,MAAM,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACpD,CAAC"}
|