@frockbot/plugin-computer 0.1.0 → 0.1.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/package.json +12 -12
- package/src/client/application.test.ts +35 -5
- package/src/client/application.ts +21 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frockbot/plugin-computer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -29,21 +29,21 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@cordisjs/client": "0.8.2",
|
|
32
|
-
"@frockbot/client-core": "0.1.
|
|
33
|
-
"@frockbot/client-ui": "0.1.
|
|
34
|
-
"@frockbot/computer-core": "0.1.
|
|
35
|
-
"@frockbot/computer-host-runtime": "0.1.
|
|
36
|
-
"@frockbot/kernel-agent-loop": "0.1.
|
|
37
|
-
"@frockbot/kernel-contracts": "0.1.
|
|
38
|
-
"@frockbot/plugin-prompt": "0.1.
|
|
39
|
-
"@frockbot/plugin-shell": "0.1.
|
|
40
|
-
"@frockbot/plugin-tools": "0.1.
|
|
32
|
+
"@frockbot/client-core": "0.1.1",
|
|
33
|
+
"@frockbot/client-ui": "0.1.1",
|
|
34
|
+
"@frockbot/computer-core": "0.1.1",
|
|
35
|
+
"@frockbot/computer-host-runtime": "0.1.1",
|
|
36
|
+
"@frockbot/kernel-agent-loop": "0.1.1",
|
|
37
|
+
"@frockbot/kernel-contracts": "0.1.1",
|
|
38
|
+
"@frockbot/plugin-prompt": "0.1.1",
|
|
39
|
+
"@frockbot/plugin-shell": "0.1.1",
|
|
40
|
+
"@frockbot/plugin-tools": "0.1.1",
|
|
41
41
|
"cordis": "4.0.0-rc.8",
|
|
42
42
|
"vue": "3.5.41"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@frockbot/plugin-models": "0.1.
|
|
46
|
-
"@frockbot/plugin-testkit": "0.1.
|
|
45
|
+
"@frockbot/plugin-models": "0.1.1",
|
|
46
|
+
"@frockbot/plugin-testkit": "0.1.1",
|
|
47
47
|
"@types/bun": "1.4.0",
|
|
48
48
|
"@types/node": "26.2.0",
|
|
49
49
|
"@vitejs/plugin-vue": "6.0.8",
|
|
@@ -70,6 +70,7 @@ function mountHostedProvider() {
|
|
|
70
70
|
const calls: Array<[string, string | undefined, string | undefined]> = [];
|
|
71
71
|
const runtime = new FakeRuntime();
|
|
72
72
|
let phase: Phase = "idle";
|
|
73
|
+
let hostUpdating = false;
|
|
73
74
|
let controlHeld = false;
|
|
74
75
|
let renewFails = false;
|
|
75
76
|
let state: { value: ComputerState } | undefined;
|
|
@@ -85,7 +86,9 @@ function mountHostedProvider() {
|
|
|
85
86
|
type:
|
|
86
87
|
"connect" | "takeControl" | "releaseControl" | "refreshViewer";
|
|
87
88
|
};
|
|
88
|
-
if (command.type === "connect")
|
|
89
|
+
if (command.type === "connect") {
|
|
90
|
+
phase = hostUpdating ? "updating" : "ready";
|
|
91
|
+
}
|
|
89
92
|
if (command.type === "takeControl") {
|
|
90
93
|
phase = "human-control";
|
|
91
94
|
controlHeld = true;
|
|
@@ -122,7 +125,9 @@ function mountHostedProvider() {
|
|
|
122
125
|
: phase === "updating"
|
|
123
126
|
? "Updating the Computer runtime"
|
|
124
127
|
: "Computer ready",
|
|
125
|
-
...(phase === "idle" ||
|
|
128
|
+
...(phase === "idle" ||
|
|
129
|
+
phase === "disconnected" ||
|
|
130
|
+
phase === "updating"
|
|
126
131
|
? {}
|
|
127
132
|
: {
|
|
128
133
|
viewerSession: {
|
|
@@ -174,6 +179,10 @@ function mountHostedProvider() {
|
|
|
174
179
|
},
|
|
175
180
|
setUpdating() {
|
|
176
181
|
phase = "updating";
|
|
182
|
+
hostUpdating = true;
|
|
183
|
+
},
|
|
184
|
+
setReady() {
|
|
185
|
+
hostUpdating = false;
|
|
177
186
|
},
|
|
178
187
|
dispose() {
|
|
179
188
|
if (Array.isArray(disposers)) {
|
|
@@ -235,7 +244,7 @@ describe("hosted Computer provider", () => {
|
|
|
235
244
|
mounted.dispose();
|
|
236
245
|
});
|
|
237
246
|
|
|
238
|
-
test("an updating strip click
|
|
247
|
+
test("an updating strip click rejoins the update and lands on ready when it finishes", async () => {
|
|
239
248
|
const mounted = mountHostedProvider();
|
|
240
249
|
await flush();
|
|
241
250
|
mounted.setUpdating();
|
|
@@ -246,14 +255,35 @@ describe("hosted Computer provider", () => {
|
|
|
246
255
|
message: "Updating the Computer runtime",
|
|
247
256
|
expanded: false,
|
|
248
257
|
});
|
|
258
|
+
// A collapsed strip never asks the host anything while it updates.
|
|
259
|
+
expect(postedTypes(mounted.calls)).toEqual([]);
|
|
249
260
|
|
|
261
|
+
// Opening rejoins: the host still reports the update, so the phase holds
|
|
262
|
+
// and the progress view is what expands.
|
|
250
263
|
await mounted.state.openViewer();
|
|
251
|
-
|
|
252
264
|
expect(mounted.state).toMatchObject({
|
|
253
265
|
phase: "updating",
|
|
254
266
|
expanded: true,
|
|
267
|
+
viewerUrl: undefined,
|
|
255
268
|
});
|
|
256
|
-
expect(postedTypes(mounted.calls)).toEqual([]);
|
|
269
|
+
expect(postedTypes(mounted.calls)).toEqual(["connect"]);
|
|
270
|
+
|
|
271
|
+
// Every poll while open asks again, so the durable `updating` record is
|
|
272
|
+
// not the last word once the host has finished.
|
|
273
|
+
mounted.setReady();
|
|
274
|
+
mounted.runtime.tick(PROJECTION_POLL_INTERVAL_MS);
|
|
275
|
+
await flush();
|
|
276
|
+
expect(postedTypes(mounted.calls)).toEqual(["connect", "connect"]);
|
|
277
|
+
expect(mounted.state).toMatchObject({
|
|
278
|
+
phase: "ready",
|
|
279
|
+
expanded: true,
|
|
280
|
+
viewerUrl: "https://viewer.invalid/secret#view_only=1",
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Once ready, polls go back to reading only.
|
|
284
|
+
mounted.runtime.tick(PROJECTION_POLL_INTERVAL_MS);
|
|
285
|
+
await flush();
|
|
286
|
+
expect(postedTypes(mounted.calls)).toEqual(["connect", "connect"]);
|
|
257
287
|
mounted.dispose();
|
|
258
288
|
});
|
|
259
289
|
|
|
@@ -163,6 +163,16 @@ export function createComputerClientPlugin(
|
|
|
163
163
|
projectionPoll = runtime.setInterval(() => {
|
|
164
164
|
const selectedBotId = shell.value.activeBotId;
|
|
165
165
|
if (!selectedBotId || !runtime.isVisible()) return;
|
|
166
|
+
// The `updating` record is written when the host refused a connect
|
|
167
|
+
// mid-update; it only changes when someone connects again. While the
|
|
168
|
+
// User has the Computer open, rejoin the update each poll so the
|
|
169
|
+
// viewer arrives the moment the host finishes (P4).
|
|
170
|
+
if (machine.phase === "updating" && machine.expanded) {
|
|
171
|
+
void execute("connect").catch(() => {
|
|
172
|
+
// A refusal keeps the updating phase; the next poll asks again.
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
166
176
|
void load(selectedBotId).catch((error) =>
|
|
167
177
|
apply({ type: "failed", message: errorMessage(error) }),
|
|
168
178
|
);
|
|
@@ -234,9 +244,18 @@ export function createComputerClientPlugin(
|
|
|
234
244
|
|
|
235
245
|
async function openViewer(): Promise<void> {
|
|
236
246
|
if (machine.expanded) return;
|
|
237
|
-
|
|
247
|
+
// Idle wakes. Updating rejoins: the host either hands back the viewer
|
|
248
|
+
// because the update has finished, or refuses again with its phase.
|
|
249
|
+
const wake = machine.phase === "idle" || machine.phase === "updating";
|
|
238
250
|
apply({ type: "viewer-expanded" });
|
|
239
|
-
if (wake)
|
|
251
|
+
if (!wake) return;
|
|
252
|
+
if (machine.phase === "updating") {
|
|
253
|
+
await execute("connect").catch(() => {
|
|
254
|
+
// Still updating; the projection poll keeps rejoining.
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
await connect("connect-requested");
|
|
240
259
|
}
|
|
241
260
|
|
|
242
261
|
async function closeViewer(): Promise<void> {
|