clavarium 0.2.0-next.8 → 0.2.0-next.9
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/npm-shrinkwrap.json +2 -2
- package/package.json +3 -2
- package/server/conversations.mjs +27 -2
- package/server/health.mjs +3 -0
- package/server/main.mjs +1 -0
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clavarium",
|
|
3
|
-
"version": "0.2.0-next.
|
|
3
|
+
"version": "0.2.0-next.9",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "clavarium",
|
|
9
|
-
"version": "0.2.0-next.
|
|
9
|
+
"version": "0.2.0-next.9",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clavarium",
|
|
3
|
-
"version": "0.2.0-next.
|
|
3
|
+
"version": "0.2.0-next.9",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A conversation is a VM with a terminal in it. Everything else is a folder.",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"prepare": "git config core.hooksPath hooks 2>/dev/null || true",
|
|
36
36
|
"check:hooks": "node tools/check-hooks.mjs",
|
|
37
37
|
"test:e2e": "node --test --test-concurrency=1 \"e2e/*.e2e.mjs\"",
|
|
38
|
-
"toolbox": "node deploy/parley-archive.mjs"
|
|
38
|
+
"toolbox": "node deploy/parley-archive.mjs",
|
|
39
|
+
"acceptance": "node acceptance/run.mjs"
|
|
39
40
|
},
|
|
40
41
|
"engines": {
|
|
41
42
|
"node": ">=22"
|
package/server/conversations.mjs
CHANGED
|
@@ -38,6 +38,8 @@ export const STARTING_MS = 180_000;
|
|
|
38
38
|
export const ANSWER_MS = 90_000;
|
|
39
39
|
/* What the folders of a machine just started are waited for, at most, before its tool starts. */
|
|
40
40
|
const MOUNTED_MS = 30_000;
|
|
41
|
+
/* How long a stop waits for folders being mounted (six folders took a minute on a real host). */
|
|
42
|
+
const CALM_MS = 180_000;
|
|
41
43
|
const POLL_MS = 2000;
|
|
42
44
|
/* The tool's screen not drawn this long after its terminal was opened: its
|
|
43
45
|
start is said to be over all the same (the page then opens it itself). */
|
|
@@ -79,6 +81,13 @@ export class Conversations {
|
|
|
79
81
|
when it was just built, restarted or its work moved; otherwise only
|
|
80
82
|
looked at, and put back if they came loose (health.mjs). */
|
|
81
83
|
mounted = async () => {};
|
|
84
|
+
/* Set by the server: what is being done to its machine's folders just
|
|
85
|
+
now (a look of health.mjs that may mount one again), or null. */
|
|
86
|
+
quiet = () => null;
|
|
87
|
+
/* Folders being mounted again after a start, by name: a stop waits for
|
|
88
|
+
them (#calm). Stopping a machine while the VM manager mounts a folder
|
|
89
|
+
in it was seen to hang the VM manager itself, for every machine. */
|
|
90
|
+
#mounting = new Map();
|
|
82
91
|
/* Set by the server: told when its machine was restarted or built again
|
|
83
92
|
by this server, in words for its chat, and whether the tool runs again
|
|
84
93
|
(its chat says it, and the tool is told to read it again: talk.mjs). */
|
|
@@ -426,7 +435,10 @@ export class Conversations {
|
|
|
426
435
|
const again = this.#mountAgain.delete(name);
|
|
427
436
|
if (again || stopped) {
|
|
428
437
|
this.#reach(name, 'folders'); tell();
|
|
429
|
-
|
|
438
|
+
const mounting = Promise.resolve().then(() => this.mounted(name, again)).catch(() => {});
|
|
439
|
+
this.#mounting.set(name, mounting);
|
|
440
|
+
void mounting.finally(() => { if (this.#mounting.get(name) === mounting) this.#mounting.delete(name); });
|
|
441
|
+
await Promise.race([mounting, new Promise((r) => { setTimeout(r, MOUNTED_MS).unref?.(); })]);
|
|
430
442
|
}
|
|
431
443
|
/* Woken from sleep: its tool is still there when its session is. */
|
|
432
444
|
if (asleep) {
|
|
@@ -766,6 +778,7 @@ export class Conversations {
|
|
|
766
778
|
async #cycle(name, c, between, restart) {
|
|
767
779
|
const step = (text) => { if (this.#store.get(name)) this.#store.patch(name, { step: text }); };
|
|
768
780
|
try {
|
|
781
|
+
await this.#calm(name, step);
|
|
769
782
|
this.drop(name);
|
|
770
783
|
let state = await this.#vms.state(name);
|
|
771
784
|
if (state !== 'Stopped') {
|
|
@@ -778,7 +791,7 @@ export class Conversations {
|
|
|
778
791
|
await this.#watched(name, 'stop', this.#vms.kill(name));
|
|
779
792
|
state = await this.#vms.state(name);
|
|
780
793
|
}
|
|
781
|
-
if (state !== 'Stopped') return `the machine did not stop (it is ${state
|
|
794
|
+
if (state !== 'Stopped') return `the machine did not stop (${state ? `it is ${state}` : 'its state could not be read'}).`;
|
|
782
795
|
}
|
|
783
796
|
const failed = await between();
|
|
784
797
|
if (!restart) return failed;
|
|
@@ -794,6 +807,18 @@ export class Conversations {
|
|
|
794
807
|
} catch { return 'it stopped on an unexpected error.'; }
|
|
795
808
|
}
|
|
796
809
|
|
|
810
|
+
/* Before its machine is stopped: its folders being mounted again are
|
|
811
|
+
left to finish, within a bound (a new look does not start meanwhile:
|
|
812
|
+
health.mjs leaves a machine that is not `ready` alone). */
|
|
813
|
+
async #calm(name, step) {
|
|
814
|
+
const pending = [this.#mounting.get(name), this.quiet(name)].filter(Boolean);
|
|
815
|
+
if (pending.length === 0) return;
|
|
816
|
+
step('Waiting for its folders to be mounted');
|
|
817
|
+
let timer;
|
|
818
|
+
await Promise.race([Promise.allSettled(pending), new Promise((r) => { timer = setTimeout(r, CALM_MS); timer.unref?.(); })]);
|
|
819
|
+
clearTimeout(timer);
|
|
820
|
+
}
|
|
821
|
+
|
|
797
822
|
/* `chat`: the same, as its chat says it. */
|
|
798
823
|
async #settle(name, said, chat, failed = false) {
|
|
799
824
|
if (!this.#store.get(name)) return;
|
package/server/health.mjs
CHANGED
|
@@ -120,6 +120,9 @@ export class Health {
|
|
|
120
120
|
of(name) { return this.#records.get(name) ?? null; }
|
|
121
121
|
forget(name) { this.#records.delete(name); }
|
|
122
122
|
|
|
123
|
+
/* The look under way (it may be mounting a folder again), or null. */
|
|
124
|
+
busy(name) { return this.#running.get(name) ?? null; }
|
|
125
|
+
|
|
123
126
|
/* Looks now. One look at a time per conversation: a second caller gets the same. */
|
|
124
127
|
check(name, { repair = true } = {}) {
|
|
125
128
|
const going = this.#running.get(name);
|
package/server/main.mjs
CHANGED
|
@@ -337,6 +337,7 @@ export async function startHome({ port, host, dataDir, mntDir = dataDir, gitRese
|
|
|
337
337
|
/* A machine just started: its folders are mounted again (after a build,
|
|
338
338
|
a restart, a move) or looked at and put back if they came loose (after
|
|
339
339
|
a wake), before its tool starts in them. */
|
|
340
|
+
all.quiet = (name) => health.busy(name);
|
|
340
341
|
all.mounted = async (name, again) => { if (again) await folders.remount(name).catch(() => {}); await health.check(name).catch(() => {}); };
|
|
341
342
|
/* What the VM manager says each machine has is what is recorded (a record
|
|
342
343
|
may say otherwise: set by hand, or by an earlier version). Not waited for. */
|