@frockbot/plugin-fly-sprite 0.3.3 → 0.3.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frockbot/plugin-fly-sprite",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -25,16 +25,16 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@cordisjs/plugin-webui": "0.8.2",
28
- "@frockbot/computer-core": "0.3.3",
29
- "@frockbot/computer-host-protocol": "0.3.3",
30
- "@frockbot/computer-host-runtime": "0.3.3",
31
- "@frockbot/kernel-contracts": "0.3.3",
32
- "@frockbot/plugin-computer": "0.3.3",
28
+ "@frockbot/computer-core": "0.3.5",
29
+ "@frockbot/computer-host-protocol": "0.3.5",
30
+ "@frockbot/computer-host-runtime": "0.3.5",
31
+ "@frockbot/kernel-contracts": "0.3.5",
32
+ "@frockbot/plugin-computer": "0.3.5",
33
33
  "cordis": "4.0.0-rc.8"
34
34
  },
35
35
  "devDependencies": {
36
- "@frockbot/plugin-testkit": "0.3.3",
37
- "@frockbot/workspace-store": "0.3.3",
36
+ "@frockbot/plugin-testkit": "0.3.5",
37
+ "@frockbot/workspace-store": "0.3.5",
38
38
  "@types/bun": "1.4.0",
39
39
  "@types/node": "26.2.0",
40
40
  "typescript": "5.9.3"
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { describe, expect, test } from "bun:test";
4
4
  import type { ComputerConnectionProgressV1 } from "@frockbot/computer-core";
5
+ import { COMPUTER_UNCONFIGURED_MESSAGE_V1 } from "@frockbot/computer-core";
5
6
  import { verifyPluginPackage } from "@frockbot/plugin-testkit";
6
7
  import { DESKTOP_GUI_LEASE_KEY } from "@frockbot/computer-host-runtime";
7
8
  import manifest from "../frockbot.json" with { type: "json" };
@@ -316,8 +317,10 @@ describe("Fly Sprite computer", () => {
316
317
  test("an unconfigured Computer refuses rather than pretending", async () => {
317
318
  const computer = new FlySpriteComputer({ spriteName: "frockbot-test" });
318
319
  expect(computer.configured).toBe(false);
320
+ // The refusal is addressed to the User, not to whoever deploys the host:
321
+ // it names no environment variable and no settings screen.
319
322
  await expect(computer.bot("general").ensure()).rejects.toThrow(
320
- "Set SPRITES_TOKEN",
323
+ COMPUTER_UNCONFIGURED_MESSAGE_V1,
321
324
  );
322
325
  });
323
326
 
package/src/computer.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import {
3
+ COMPUTER_UNCONFIGURED_MESSAGE_V1,
3
4
  ComputerError,
4
5
  decodeComputerDoctorReportV1,
5
6
  type ComputerConnectionOptionsV1,
@@ -1181,7 +1182,7 @@ export class FlySpriteComputer {
1181
1182
  if (!this.host) {
1182
1183
  throw new ComputerError(
1183
1184
  "provider-unavailable",
1184
- "Set SPRITES_TOKEN to attach a Fly Sprite computer",
1185
+ COMPUTER_UNCONFIGURED_MESSAGE_V1,
1185
1186
  );
1186
1187
  }
1187
1188
  let held = this.surfaces.get(layout.key);
@@ -11,8 +11,10 @@ import {
11
11
  type ComputerHostOpenFrameV1,
12
12
  type ComputerHostProvisioningV1,
13
13
  } from "@frockbot/computer-host-protocol";
14
+ import { COMPUTER_COLD_PROVISION_EXPECTATION_MS } from "@frockbot/plugin-computer/protocol";
14
15
  import {
15
16
  ComputerHostClient,
17
+ COMPUTER_HOST_OPEN_TIMEOUT_MS,
16
18
  type ComputerHostFetcherV1,
17
19
  } from "./host-client.ts";
18
20
 
@@ -566,6 +568,26 @@ describe("ComputerHostClient failures", () => {
566
568
  );
567
569
  });
568
570
 
571
+ test("open outlasts the cold provision the product advertises", async () => {
572
+ // A first-ever wake that takes exactly as long as the Computer card
573
+ // promises must not abort on our own clock: on the shared 120 s default it
574
+ // reached the User as `provider-unavailable`, which reads as a broken
575
+ // Computer rather than one that is still coming up.
576
+ expect(COMPUTER_HOST_OPEN_TIMEOUT_MS).toBeGreaterThan(
577
+ COMPUTER_COLD_PROVISION_EXPECTATION_MS,
578
+ );
579
+ });
580
+
581
+ test("a deadline the client set says the Computer may still be starting", async () => {
582
+ const error = await client(hanging())
583
+ .exec({ script: "sleep 600", timeoutMs: 5 }, { timeoutMs: 5 })
584
+ .catch((thrown: unknown) => thrown);
585
+ // Addressed to whoever reads it in the app, with the next move in it.
586
+ expect((error as ComputerError).message).toBe(
587
+ "The Computer did not answer in time. It may still be starting up; try again in a moment.",
588
+ );
589
+ }, 10_000);
590
+
569
591
  test("the client's own deadline expiring is provider-unavailable", async () => {
570
592
  const error = await client(hanging())
571
593
  .exec({ script: "sleep 600", timeoutMs: 5 }, { timeoutMs: 5 })
@@ -83,6 +83,18 @@ export const COMPUTER_HOST_TIMEOUT_GRACE_MS = 5_000;
83
83
  /** The deadline for a call that declares none of its own. */
84
84
  export const COMPUTER_HOST_DEFAULT_TIMEOUT_MS = 120_000;
85
85
 
86
+ /**
87
+ * The deadline for `open`, which is the one call that may provision.
88
+ *
89
+ * It has to outlast the slowest thing the product tells the User to expect:
90
+ * the Computer card advertises "This usually takes 2-3 minutes" for a cold
91
+ * provision. On the shared 120 s default a first-ever wake that took exactly
92
+ * as long as the UI promised aborted on our own clock and reached the User as
93
+ * `provider-unavailable` — "the Computer is broken" — rather than as a
94
+ * Computer that was still coming up.
95
+ */
96
+ export const COMPUTER_HOST_OPEN_TIMEOUT_MS = 300_000;
97
+
86
98
  /** The deadline for a best-effort cancel, which must never outlive its caller. */
87
99
  const CANCEL_TIMEOUT_MS = 5_000;
88
100
 
@@ -336,15 +348,21 @@ export class ComputerHostClient {
336
348
  async open(
337
349
  options?: ComputerHostOpenOptionsV1,
338
350
  ): Promise<ComputerHostOpenResultV1> {
351
+ // A caller's own deadline still wins; absent one, `open` gets the cold
352
+ // provision's, not the shared default.
353
+ const openOptions: ComputerHostOpenOptionsV1 = {
354
+ ...options,
355
+ timeoutMs: options?.timeoutMs ?? COMPUTER_HOST_OPEN_TIMEOUT_MS,
356
+ };
339
357
  if (!options?.onProgress) {
340
358
  return this.json(
341
359
  { kind: "open" },
342
360
  decodeComputerHostOpenResultV1,
343
- options,
361
+ openOptions,
344
362
  );
345
363
  }
346
- const effectId = this.effectIdFor(options);
347
- const lease = this.lease(COMPUTER_HOST_DEFAULT_TIMEOUT_MS, options);
364
+ const effectId = this.effectIdFor(openOptions);
365
+ const lease = this.lease(COMPUTER_HOST_OPEN_TIMEOUT_MS, openOptions);
348
366
  try {
349
367
  const response = await this.send(
350
368
  { kind: "open", stream: true },
@@ -379,7 +397,7 @@ export class ComputerHostClient {
379
397
  return await this.json(
380
398
  { kind: "open" },
381
399
  decodeComputerHostOpenResultV1,
382
- options,
400
+ openOptions,
383
401
  );
384
402
  } finally {
385
403
  lease.release();
@@ -900,9 +918,12 @@ export class ComputerHostClient {
900
918
  );
901
919
  }
902
920
  if (lease.timedOut) {
921
+ // Written for whoever reads it in the app. A deadline this call set is
922
+ // not a broken Computer: most often the Computer is still starting, and
923
+ // "try again" is exactly the right next move.
903
924
  return new ComputerError(
904
925
  "provider-unavailable",
905
- "The Computer host did not answer within the effect's deadline",
926
+ "The Computer did not answer in time. It may still be starting up; try again in a moment.",
906
927
  true,
907
928
  { cause: error },
908
929
  );
package/src/host.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import type { Entry } from "@cordisjs/plugin-webui";
2
- import { ComputerError } from "@frockbot/computer-core";
2
+ import {
3
+ COMPUTER_UNCONFIGURED_MESSAGE_V1,
4
+ ComputerError,
5
+ } from "@frockbot/computer-core";
3
6
  import type { ComputerState } from "@frockbot/plugin-computer/shared";
4
7
  import { computerUpdateLabelV1 } from "@frockbot/plugin-computer/protocol";
5
8
  import {
@@ -50,7 +53,7 @@ class FlySpriteHostController {
50
53
  configured: computer.configured,
51
54
  message: computer.configured
52
55
  ? "Persistent Fly Sprite computer"
53
- : "Set SPRITES_TOKEN to attach a computer",
56
+ : COMPUTER_UNCONFIGURED_MESSAGE_V1,
54
57
  });
55
58
  const data: ComputerState = {
56
59
  ...this.machine,