@fnndsc/brasa 0.55.3 → 0.56.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/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 +467 -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 +32 -0
- package/dist/builtins/berth/berthProcesses.js +181 -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 +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One of the person's processes.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* const running: BerthProcess = { pid: 412, command: 'python train.py', open: ['/outgoing/model.pt'] };
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export interface BerthProcess {
|
|
10
|
+
/** Its process id. */
|
|
11
|
+
readonly pid: number;
|
|
12
|
+
/** Its command line, the words joined by spaces. */
|
|
13
|
+
readonly command: string;
|
|
14
|
+
/** The files it holds open under the folder asked about, by path. */
|
|
15
|
+
readonly open: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The person's processes still running in the berth, the session's own left
|
|
19
|
+
* out, each with the files it holds open under a folder.
|
|
20
|
+
*
|
|
21
|
+
* @param folder - The folder whose open files are said (the outgoing place).
|
|
22
|
+
* @param procRoot - The `/proc` folder; the system's by default.
|
|
23
|
+
* @param self - This process's id; the running process by default.
|
|
24
|
+
* @param uid - The person's user id; this process's by default.
|
|
25
|
+
* @returns The processes, by pid; empty when `/proc` is not there.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* processesOwn_list('/outgoing'); // [{ pid: 412, command: 'python train.py', open: ['/outgoing/model.pt'] }]
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function processesOwn_list(folder: string, procRoot?: string, self?: number, uid?: number): BerthProcess[];
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The person's processes still running in a berth, read from `/proc`,
|
|
3
|
+
* with the files each holds open under a folder.
|
|
4
|
+
*
|
|
5
|
+
* Inside a berth every process is the person's, so the processes that
|
|
6
|
+
* matter to a close are all of them but the session's own: this process,
|
|
7
|
+
* its parents up to PID 1, the terminals it runs for the console (a child
|
|
8
|
+
* whose environment carries `CHELL_TERMINAL` or `CHELL_CONSOLE`, as calypso
|
|
9
|
+
* starts them), and a shell such a terminal starts directly (`!`). Whatever
|
|
10
|
+
* runs under that shell is the person's work and is listed. A process of
|
|
11
|
+
* another user, or with no command line (a kernel thread, a zombie), is not
|
|
12
|
+
* listed.
|
|
13
|
+
*
|
|
14
|
+
* Key components:
|
|
15
|
+
* - BerthProcess: one process, its command and its open files under the folder.
|
|
16
|
+
* - processesOwn_list: the person's processes, the session's own left out.
|
|
17
|
+
*
|
|
18
|
+
* Typical usage:
|
|
19
|
+
* ```ts
|
|
20
|
+
* const running: BerthProcess[] = processesOwn_list('/outgoing');
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Dependencies: Node.js built-ins only; a Linux `/proc` (a berth's). Where
|
|
24
|
+
* `/proc` is not there, no process is listed.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import { existsSync, readdirSync, readFileSync, readlinkSync, statSync } from 'node:fs';
|
|
29
|
+
import { join } from 'node:path';
|
|
30
|
+
/** The environment marks of a console terminal calypso starts. */
|
|
31
|
+
const TERMINAL_MARKS = ['CHELL_TERMINAL=', 'CHELL_CONSOLE='];
|
|
32
|
+
/** The commands that are a shell a console terminal opens. */
|
|
33
|
+
const SHELLS = new Set(['sh', 'bash', 'zsh', 'dash', 'ash', 'fish', 'ksh', 'tcsh', 'csh']);
|
|
34
|
+
/**
|
|
35
|
+
* Reads a file of `/proc`, or nothing when the process is gone or the file cannot be read.
|
|
36
|
+
*
|
|
37
|
+
* @param path - The file.
|
|
38
|
+
* @returns Its text, or null.
|
|
39
|
+
*/
|
|
40
|
+
function procFile_read(path) {
|
|
41
|
+
try {
|
|
42
|
+
return readFileSync(path, 'latin1');
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Reads one process from `/proc/<pid>`.
|
|
50
|
+
*
|
|
51
|
+
* @param procRoot - The `/proc` folder.
|
|
52
|
+
* @param pid - The process.
|
|
53
|
+
* @returns The entry, or null when it is gone or unreadable.
|
|
54
|
+
*/
|
|
55
|
+
function procEntry_read(procRoot, pid) {
|
|
56
|
+
const stat = procFile_read(join(procRoot, String(pid), 'stat'));
|
|
57
|
+
if (stat === null)
|
|
58
|
+
return null;
|
|
59
|
+
// `comm` sits in parentheses and may hold spaces and parentheses itself:
|
|
60
|
+
// the fields after it start after the last `)`.
|
|
61
|
+
const open = stat.indexOf('(');
|
|
62
|
+
const close = stat.lastIndexOf(')');
|
|
63
|
+
if (open === -1 || close < open)
|
|
64
|
+
return null;
|
|
65
|
+
const fields = stat.slice(close + 2).split(' ');
|
|
66
|
+
const ppid = Number(fields[1]);
|
|
67
|
+
const cmdline = procFile_read(join(procRoot, String(pid), 'cmdline')) ?? '';
|
|
68
|
+
const environ = procFile_read(join(procRoot, String(pid), 'environ')) ?? '';
|
|
69
|
+
return {
|
|
70
|
+
pid,
|
|
71
|
+
ppid: Number.isFinite(ppid) ? ppid : 0,
|
|
72
|
+
name: stat.slice(open + 1, close),
|
|
73
|
+
argv: cmdline.split('\0').filter((word) => word !== ''),
|
|
74
|
+
terminal: environ.split('\0').some((pair) => TERMINAL_MARKS.some((mark) => pair.startsWith(mark))),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Every process of one user in `/proc`.
|
|
79
|
+
*
|
|
80
|
+
* @param procRoot - The `/proc` folder.
|
|
81
|
+
* @param uid - The user's id.
|
|
82
|
+
* @returns The processes, by pid.
|
|
83
|
+
*/
|
|
84
|
+
function procEntries_read(procRoot, uid) {
|
|
85
|
+
const entries = new Map();
|
|
86
|
+
if (!existsSync(procRoot))
|
|
87
|
+
return entries;
|
|
88
|
+
for (const name of readdirSync(procRoot)) {
|
|
89
|
+
if (!/^\d+$/.test(name))
|
|
90
|
+
continue;
|
|
91
|
+
const owner = statSync(join(procRoot, name), { throwIfNoEntry: false })?.uid;
|
|
92
|
+
if (owner !== uid)
|
|
93
|
+
continue;
|
|
94
|
+
const entry = procEntry_read(procRoot, Number(name));
|
|
95
|
+
if (entry !== null)
|
|
96
|
+
entries.set(entry.pid, entry);
|
|
97
|
+
}
|
|
98
|
+
return entries;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The session's own processes: this one, its parents up to PID 1, the
|
|
102
|
+
* console terminals it runs, and the shells those terminals start.
|
|
103
|
+
*
|
|
104
|
+
* @param entries - The user's processes.
|
|
105
|
+
* @param procRoot - The `/proc` folder, for parents of another user.
|
|
106
|
+
* @param self - This process's id.
|
|
107
|
+
* @returns Their ids.
|
|
108
|
+
*/
|
|
109
|
+
function sessionPids_find(entries, procRoot, self) {
|
|
110
|
+
const session = new Set([self]);
|
|
111
|
+
let parent = entries.get(self)?.ppid ?? procEntry_read(procRoot, self)?.ppid ?? 0;
|
|
112
|
+
while (parent > 0 && !session.has(parent)) {
|
|
113
|
+
session.add(parent);
|
|
114
|
+
parent = entries.get(parent)?.ppid ?? procEntry_read(procRoot, parent)?.ppid ?? 0;
|
|
115
|
+
}
|
|
116
|
+
const terminals = [...entries.values()]
|
|
117
|
+
.filter((entry) => entry.ppid === self && entry.terminal)
|
|
118
|
+
.map((entry) => entry.pid);
|
|
119
|
+
for (const terminal of terminals)
|
|
120
|
+
session.add(terminal);
|
|
121
|
+
for (const entry of entries.values()) {
|
|
122
|
+
if (terminals.includes(entry.ppid) && SHELLS.has(entry.name))
|
|
123
|
+
session.add(entry.pid);
|
|
124
|
+
}
|
|
125
|
+
return session;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The files a process holds open under a folder.
|
|
129
|
+
*
|
|
130
|
+
* @param procRoot - The `/proc` folder.
|
|
131
|
+
* @param pid - The process.
|
|
132
|
+
* @param folder - The folder, absolute.
|
|
133
|
+
* @returns Their paths, sorted; none when its descriptors cannot be read.
|
|
134
|
+
*/
|
|
135
|
+
function openFiles_list(procRoot, pid, folder) {
|
|
136
|
+
const fdDir = join(procRoot, String(pid), 'fd');
|
|
137
|
+
const prefix = folder.endsWith('/') ? folder : `${folder}/`;
|
|
138
|
+
let descriptors;
|
|
139
|
+
try {
|
|
140
|
+
descriptors = readdirSync(fdDir);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
const open = new Set();
|
|
146
|
+
for (const descriptor of descriptors) {
|
|
147
|
+
try {
|
|
148
|
+
const target = readlinkSync(join(fdDir, descriptor));
|
|
149
|
+
if (target.startsWith(prefix))
|
|
150
|
+
open.add(target.replace(/ \(deleted\)$/, ''));
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// A descriptor closed between the listing and the read holds nothing.
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return [...open].sort();
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The person's processes still running in the berth, the session's own left
|
|
160
|
+
* out, each with the files it holds open under a folder.
|
|
161
|
+
*
|
|
162
|
+
* @param folder - The folder whose open files are said (the outgoing place).
|
|
163
|
+
* @param procRoot - The `/proc` folder; the system's by default.
|
|
164
|
+
* @param self - This process's id; the running process by default.
|
|
165
|
+
* @param uid - The person's user id; this process's by default.
|
|
166
|
+
* @returns The processes, by pid; empty when `/proc` is not there.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* processesOwn_list('/outgoing'); // [{ pid: 412, command: 'python train.py', open: ['/outgoing/model.pt'] }]
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export function processesOwn_list(folder, procRoot = '/proc', self = process.pid, uid = process.getuid?.() ?? 0) {
|
|
174
|
+
const entries = procEntries_read(procRoot, uid);
|
|
175
|
+
const session = sessionPids_find(entries, procRoot, self);
|
|
176
|
+
return [...entries.values()]
|
|
177
|
+
.filter((entry) => !session.has(entry.pid) && entry.argv.length > 0)
|
|
178
|
+
.sort((a, b) => a.pid - b.pid)
|
|
179
|
+
.map((entry) => ({ pid: entry.pid, command: entry.argv.join(' '), open: openFiles_list(procRoot, entry.pid, folder) }));
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=berthProcesses.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"berthProcesses.js","sourceRoot":"","sources":["../../../src/builtins/berth/berthProcesses.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { AnchoredFile, BerthAnchor } from './berthState.js';
|
|
2
|
+
/** The record's name, in the outgoing folder. */
|
|
3
|
+
export declare const BERTH_RECORD_FILE: string;
|
|
4
|
+
/**
|
|
5
|
+
* A file whose size differs from the size anchored.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const resized: ResizedFile = { name: 'model.pt', anchored: 5, atClose: 6 };
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export interface ResizedFile {
|
|
13
|
+
/** Its name relative to the anchor. */
|
|
14
|
+
readonly name: string;
|
|
15
|
+
/** Its size when anchored, in bytes. */
|
|
16
|
+
readonly anchored: number;
|
|
17
|
+
/** Its size in ChRIS at the close, in bytes. */
|
|
18
|
+
readonly atClose: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* What differs between an anchor's files and what ChRIS holds at the close.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const changes: AnchorChanges = { added: ['extra.pt'], removed: [], resized: [], unreadable: null };
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export interface AnchorChanges {
|
|
29
|
+
/** Files ChRIS holds that were not anchored. */
|
|
30
|
+
readonly added: readonly string[];
|
|
31
|
+
/** Files anchored that ChRIS does not hold at the close. */
|
|
32
|
+
readonly removed: readonly string[];
|
|
33
|
+
/** Files whose size differs. */
|
|
34
|
+
readonly resized: readonly ResizedFile[];
|
|
35
|
+
/** Why ChRIS could not be read for the comparison; null when it was read. */
|
|
36
|
+
readonly unreadable: string | null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* One anchor as the record holds it: the anchor, and what changed since.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const anchor: RecordAnchor = { name: 'weights', path: '/home/alice/uploads/weights', node: null, feed: null, files: [], anchoredAt: '…', changes: null };
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export interface RecordAnchor extends BerthAnchor {
|
|
47
|
+
/** What differs in ChRIS since the anchor; null for an anchor under a node, which does not change. */
|
|
48
|
+
readonly changes: AnchorChanges | null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Where the work's node goes.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const child: BerthPlacement = { kind: 'child', by: 'anchors', parent: 41, parentName: 'pl-fshack_41', feed: 5, anchor: null };
|
|
56
|
+
* const fresh: BerthPlacement = { kind: 'newFeed', by: '--newfeed', name: 'berth work', inputs: ['/home/alice/uploads/weights'] };
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export type BerthPlacement = PlacementChild | PlacementNewFeed;
|
|
60
|
+
/** What decided a placement: the anchors themselves, or the flag given. */
|
|
61
|
+
export type PlacementCause = 'anchors' | '--infeed' | '--newfeed';
|
|
62
|
+
/** The work as a child of a node of the person's feeds. */
|
|
63
|
+
export interface PlacementChild {
|
|
64
|
+
/** A child of a node. */
|
|
65
|
+
readonly kind: 'child';
|
|
66
|
+
/** What decided it. */
|
|
67
|
+
readonly by: PlacementCause;
|
|
68
|
+
/** The parent plugin instance. */
|
|
69
|
+
readonly parent: number;
|
|
70
|
+
/** The parent's folder name, `pl-fshack_41`. */
|
|
71
|
+
readonly parentName: string;
|
|
72
|
+
/** The parent's feed. */
|
|
73
|
+
readonly feed: number | null;
|
|
74
|
+
/** The anchor `--infeed` named; null when the anchors decided. */
|
|
75
|
+
readonly anchor: string | null;
|
|
76
|
+
}
|
|
77
|
+
/** The work in a new feed. */
|
|
78
|
+
export interface PlacementNewFeed {
|
|
79
|
+
/** A new feed. */
|
|
80
|
+
readonly kind: 'newFeed';
|
|
81
|
+
/** What decided it. */
|
|
82
|
+
readonly by: PlacementCause;
|
|
83
|
+
/** The feed's name. */
|
|
84
|
+
readonly name: string;
|
|
85
|
+
/** The ChRIS paths its `pl-dircopy` root copies; empty when the root is the work itself. */
|
|
86
|
+
readonly inputs: readonly string[];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The node made, once the close attached the work.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* const attached: BerthAttachment = { feed: 12, node: 58, plugin: 'pl-dsdircopy', root: 57 };
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export interface BerthAttachment {
|
|
97
|
+
/** The feed the node is in; null when CUBE did not say. */
|
|
98
|
+
readonly feed: number | null;
|
|
99
|
+
/** The work's plugin instance. */
|
|
100
|
+
readonly node: number;
|
|
101
|
+
/** Its plugin. */
|
|
102
|
+
readonly plugin: string;
|
|
103
|
+
/** The new feed's root, when the close made one under the work; else null. */
|
|
104
|
+
readonly root: number | null;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The source the work was made from: the git repository holding the folder the session runs in.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* const source: BerthSource = { repository: '/home/alice/src/seg', commit: '3f2a…', dirty: false };
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export interface BerthSource {
|
|
115
|
+
/** The repository's top folder. */
|
|
116
|
+
readonly repository: string;
|
|
117
|
+
/** The commit checked out. */
|
|
118
|
+
readonly commit: string;
|
|
119
|
+
/** Whether the working tree differs from the commit. */
|
|
120
|
+
readonly dirty: boolean;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* `berth.json`.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* const record: BerthRecord = {
|
|
128
|
+
* version: 1, who: 'alice', session: 'berth-alice', image: null, source: null, closedAt: '2026-11-14T10:00:00.000Z',
|
|
129
|
+
* title: 'berth work', anchors: [], outgoing: '/outgoing', folder: '/home/alice/berth/0b6c…',
|
|
130
|
+
* placement: { kind: 'newFeed', by: 'anchors', name: 'berth work', inputs: [] }, attached: null,
|
|
131
|
+
* };
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export interface BerthRecord {
|
|
135
|
+
/** The record's format version. */
|
|
136
|
+
readonly version: number;
|
|
137
|
+
/** The person, by CUBE user name. */
|
|
138
|
+
readonly who: string;
|
|
139
|
+
/** The berth session: the berth's host name. */
|
|
140
|
+
readonly session: string;
|
|
141
|
+
/** The berth's image, as the berth names it; null when it does not. */
|
|
142
|
+
readonly image: string | null;
|
|
143
|
+
/** The source commit; null when the session's folder is in no git repository. */
|
|
144
|
+
readonly source: BerthSource | null;
|
|
145
|
+
/** When the close ran. */
|
|
146
|
+
readonly closedAt: string;
|
|
147
|
+
/** The work node's title. */
|
|
148
|
+
readonly title: string;
|
|
149
|
+
/** Every anchor, with what changed since. */
|
|
150
|
+
readonly anchors: readonly RecordAnchor[];
|
|
151
|
+
/** The outgoing folder returned. */
|
|
152
|
+
readonly outgoing: string;
|
|
153
|
+
/** The person's CUBE files folder it was uploaded to. */
|
|
154
|
+
readonly folder: string;
|
|
155
|
+
/** Where the node goes. */
|
|
156
|
+
readonly placement: BerthPlacement;
|
|
157
|
+
/** The node made; null until the close attached it. */
|
|
158
|
+
readonly attached: BerthAttachment | null;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* What differs between the files anchored and the files ChRIS holds at the close, by name and size.
|
|
162
|
+
*
|
|
163
|
+
* @param anchored - The files the anchor downloaded.
|
|
164
|
+
* @param atClose - The files ChRIS holds at the anchor's path at the close.
|
|
165
|
+
* @returns The files added, removed and resized, each sorted by name.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```ts
|
|
169
|
+
* anchorChanges_compare([{ name: 'a', size: 1 }, { name: 'b', size: 2 }], [{ name: 'b', size: 3 }, { name: 'c', size: 1 }]);
|
|
170
|
+
* // { added: ['c'], removed: ['a'], resized: [{ name: 'b', anchored: 2, atClose: 3 }], unreadable: null }
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export declare function anchorChanges_compare(anchored: readonly AnchoredFile[], atClose: readonly AnchoredFile[]): AnchorChanges;
|
|
174
|
+
/**
|
|
175
|
+
* Whether a comparison found anything different, or could not be made.
|
|
176
|
+
*
|
|
177
|
+
* @param changes - The comparison.
|
|
178
|
+
* @returns True when a file was added, removed or resized, or ChRIS could not be read.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* anchorChanges_any({ added: [], removed: [], resized: [], unreadable: null }); // false
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
export declare function anchorChanges_any(changes: AnchorChanges): boolean;
|
|
186
|
+
/**
|
|
187
|
+
* The git commit a folder is at, when it is in a repository.
|
|
188
|
+
*
|
|
189
|
+
* @param folder - The folder the session runs in.
|
|
190
|
+
* @returns The repository, its commit and whether its tree differs; null outside a repository, or one with no commit.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```ts
|
|
194
|
+
* berthSource_read('/home/alice/src/seg'); // { repository: '/home/alice/src/seg', commit: '3f2a…', dirty: false }
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
export declare function berthSource_read(folder: string): BerthSource | null;
|
|
198
|
+
/**
|
|
199
|
+
* Writes the record into a folder as `berth.json`.
|
|
200
|
+
*
|
|
201
|
+
* @param folder - The outgoing folder.
|
|
202
|
+
* @param record - The record.
|
|
203
|
+
* @returns The file written.
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* berthRecord_write('/outgoing', record); // '/outgoing/berth.json'
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
export declare function berthRecord_write(folder: string, record: BerthRecord): string;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `berth.json`, the record a close writes into the outgoing folder and
|
|
3
|
+
* returns with the work: who, every anchor with its ChRIS path, node, feed,
|
|
4
|
+
* what was downloaded and what changed since, the berth session, the source
|
|
5
|
+
* commit, the image, the time, and where the node goes.
|
|
6
|
+
*
|
|
7
|
+
* The record travels in the data, so a copy or a download of the node keeps
|
|
8
|
+
* it. It is written before the upload with the placement the close decided,
|
|
9
|
+
* and written again in the berth once the node is made, with the node's
|
|
10
|
+
* feed and id.
|
|
11
|
+
*
|
|
12
|
+
* Key components:
|
|
13
|
+
* - BERTH_RECORD_FILE: the record's name, `berth.json`.
|
|
14
|
+
* - BerthRecord, RecordAnchor, AnchorChanges, BerthPlacement, BerthAttachment, BerthSource: its shape.
|
|
15
|
+
* - anchorChanges_compare: what differs between the files anchored and the files ChRIS holds at the close.
|
|
16
|
+
* - anchorChanges_any: whether anything differs.
|
|
17
|
+
* - berthSource_read: the git commit of a folder, when it is in a repository.
|
|
18
|
+
* - berthRecord_write: writes the record into a folder.
|
|
19
|
+
*
|
|
20
|
+
* Typical usage:
|
|
21
|
+
* ```ts
|
|
22
|
+
* const changes: AnchorChanges = anchorChanges_compare(anchor.files, atClose);
|
|
23
|
+
* berthRecord_write('/outgoing', record);
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Dependencies: Node.js built-ins; `git` on the path for the source commit
|
|
27
|
+
* (none when it is absent). Times are ISO 8601 strings, sizes bytes.
|
|
28
|
+
*
|
|
29
|
+
* @module
|
|
30
|
+
*/
|
|
31
|
+
import { execFileSync } from 'node:child_process';
|
|
32
|
+
import { writeFileSync } from 'node:fs';
|
|
33
|
+
import { join } from 'node:path';
|
|
34
|
+
/** The record's name, in the outgoing folder. */
|
|
35
|
+
export const BERTH_RECORD_FILE = 'berth.json';
|
|
36
|
+
/** The record's format version. */
|
|
37
|
+
const BERTH_RECORD_VERSION = 1;
|
|
38
|
+
/** How long `git` may take to say the commit, in milliseconds. */
|
|
39
|
+
const GIT_TIMEOUT_MS = 5_000;
|
|
40
|
+
/**
|
|
41
|
+
* What differs between the files anchored and the files ChRIS holds at the close, by name and size.
|
|
42
|
+
*
|
|
43
|
+
* @param anchored - The files the anchor downloaded.
|
|
44
|
+
* @param atClose - The files ChRIS holds at the anchor's path at the close.
|
|
45
|
+
* @returns The files added, removed and resized, each sorted by name.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* anchorChanges_compare([{ name: 'a', size: 1 }, { name: 'b', size: 2 }], [{ name: 'b', size: 3 }, { name: 'c', size: 1 }]);
|
|
50
|
+
* // { added: ['c'], removed: ['a'], resized: [{ name: 'b', anchored: 2, atClose: 3 }], unreadable: null }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function anchorChanges_compare(anchored, atClose) {
|
|
54
|
+
const before = new Map(anchored.map((file) => [file.name, file.size]));
|
|
55
|
+
const after = new Map(atClose.map((file) => [file.name, file.size]));
|
|
56
|
+
const byName = (a, b) => a.localeCompare(b);
|
|
57
|
+
const added = [...after.keys()].filter((name) => !before.has(name)).sort(byName);
|
|
58
|
+
const removed = [...before.keys()].filter((name) => !after.has(name)).sort(byName);
|
|
59
|
+
const resized = [...before.entries()]
|
|
60
|
+
.filter(([name, size]) => after.has(name) && after.get(name) !== size)
|
|
61
|
+
.map(([name, size]) => ({ name, anchored: size, atClose: after.get(name) ?? 0 }))
|
|
62
|
+
.sort((a, b) => byName(a.name, b.name));
|
|
63
|
+
return { added, removed, resized, unreadable: null };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Whether a comparison found anything different, or could not be made.
|
|
67
|
+
*
|
|
68
|
+
* @param changes - The comparison.
|
|
69
|
+
* @returns True when a file was added, removed or resized, or ChRIS could not be read.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* anchorChanges_any({ added: [], removed: [], resized: [], unreadable: null }); // false
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function anchorChanges_any(changes) {
|
|
77
|
+
return changes.added.length + changes.removed.length + changes.resized.length > 0 || changes.unreadable !== null;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Runs `git` in a folder.
|
|
81
|
+
*
|
|
82
|
+
* @param folder - The folder.
|
|
83
|
+
* @param args - The words after `git`.
|
|
84
|
+
* @returns What it printed, trimmed; null when it failed or is not there.
|
|
85
|
+
*/
|
|
86
|
+
function git_ask(folder, args) {
|
|
87
|
+
try {
|
|
88
|
+
return execFileSync('git', ['-C', folder, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: GIT_TIMEOUT_MS }).trim();
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The git commit a folder is at, when it is in a repository.
|
|
96
|
+
*
|
|
97
|
+
* @param folder - The folder the session runs in.
|
|
98
|
+
* @returns The repository, its commit and whether its tree differs; null outside a repository, or one with no commit.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* berthSource_read('/home/alice/src/seg'); // { repository: '/home/alice/src/seg', commit: '3f2a…', dirty: false }
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export function berthSource_read(folder) {
|
|
106
|
+
const repository = git_ask(folder, ['rev-parse', '--show-toplevel']);
|
|
107
|
+
const commit = git_ask(folder, ['rev-parse', 'HEAD']);
|
|
108
|
+
if (repository === null || commit === null || commit === '')
|
|
109
|
+
return null;
|
|
110
|
+
const status = git_ask(folder, ['status', '--porcelain']);
|
|
111
|
+
return { repository, commit, dirty: status !== null && status !== '' };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Writes the record into a folder as `berth.json`.
|
|
115
|
+
*
|
|
116
|
+
* @param folder - The outgoing folder.
|
|
117
|
+
* @param record - The record.
|
|
118
|
+
* @returns The file written.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* berthRecord_write('/outgoing', record); // '/outgoing/berth.json'
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export function berthRecord_write(folder, record) {
|
|
126
|
+
const file = join(folder, BERTH_RECORD_FILE);
|
|
127
|
+
writeFileSync(file, `${JSON.stringify({ ...record, version: BERTH_RECORD_VERSION }, null, 2)}\n`, { mode: 0o644 });
|
|
128
|
+
return file;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=berthRecord.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"berthRecord.js","sourceRoot":"","sources":["../../../src/builtins/berth/berthRecord.ts"],"names":[],"mappings":"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"}
|