@fungi.computer/computer 0.2.2 → 0.3.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.
@@ -10,6 +10,8 @@
10
10
  // container behind. Any other host builds its own target natively through
11
11
  // rustup. One immutable artifact per pin, wrapper and target is cached under
12
12
  // ${XDG_CACHE_HOME:-~/.cache}/fungi/native-patch and restored on later calls.
13
+ // Turbo's strict environment lends only tool homes, so under Turbo the cache
14
+ // follows HOME; a caller that wants a private cache sets a private HOME.
13
15
  import { spawn } from "node:child_process";
14
16
  import { homedir } from "node:os";
15
17
  import { resolve } from "node:path";
@@ -50,7 +50,7 @@ async function digest(path) {
50
50
  .digest("hex");
51
51
  }
52
52
 
53
- /** One immutable artifact per Codex pin/target, shared by every checkout and updater. */
53
+ /** One immutable artifact per Codex pin/target/wrapper, shared by every checkout and updater. */
54
54
  export async function ensureNativePatchArtifact({
55
55
  cacheRoot,
56
56
  pin: inputPin,
@@ -62,7 +62,7 @@ export async function ensureNativePatchArtifact({
62
62
  const pin = pinSchema.parse(inputPin);
63
63
  const target = targetSchema.parse(inputTarget);
64
64
  const wrapperSha256 = await digest(wrapper);
65
- const key = join(cacheRoot, pin.version, target);
65
+ const key = join(cacheRoot, pin.version, target, wrapperSha256);
66
66
  const artifact = join(key, "artifact");
67
67
  await mkdir(key, { recursive: true, mode: 0o700 });
68
68
  async function published() {
@@ -82,7 +82,7 @@ export async function ensureNativePatchArtifact({
82
82
  receipt.wrapperSha256 !== wrapperSha256
83
83
  )
84
84
  throw new Error(
85
- `Cached patch artifact does not match this pin or wrapper: ${artifact}. Use a new pin before rebuilding.`,
85
+ `Cached patch artifact does not match this pin or wrapper: ${artifact}. The artifact was not used or rebuilt.`,
86
86
  );
87
87
  if (
88
88
  (await digest(join(artifact, "fungi-apply-patch"))) !==
@@ -114,7 +114,11 @@ const nativePathFlags = [
114
114
  .map((flag) => JSON.stringify(flag))
115
115
  .join(" ");
116
116
  ensureNativeHostToolchain({ toolchain: codexPatchPin.rustToolchain });
117
- const { command: cargoCommand, args: cargoArgs } = selectCargoInvocation({
117
+ const {
118
+ command: cargoCommand,
119
+ args: cargoArgs,
120
+ env: cargoEnv,
121
+ } = selectCargoInvocation({
118
122
  toolchain: codexPatchPin.rustToolchain,
119
123
  cargoArgs: [
120
124
  crossMac ? "zigbuild" : "build",
@@ -139,6 +143,7 @@ const cargo = spawnSync(cargoCommand, cargoArgs, {
139
143
  maxBuffer: 64 * 1024 * 1024,
140
144
  env: {
141
145
  ...process.env,
146
+ ...cargoEnv,
142
147
  CARGO_TARGET_DIR: resolve(scratch, "target"),
143
148
  CARGO_ENCODED_RUSTFLAGS: [
144
149
  `--remap-path-prefix=${scratch}=/build`,
@@ -242,6 +242,20 @@ async fn run() -> Result<(), Box<dyn std::error::Error>> {
242
242
  }
243
243
  }
244
244
  }
245
+ // Inspection uses the same upstream parser and admission as application.
246
+ // The host uses these NUL-delimited paths to enforce protected mounts
247
+ // before offering an approval and again immediately before dispatch.
248
+ if std::env::args().nth(1).as_deref() == Some("--inspect") {
249
+ use std::io::Write;
250
+ let mut output = io::stdout().lock();
251
+ let mut paths: Vec<_> = paths.into_iter().collect();
252
+ paths.sort();
253
+ for path in paths {
254
+ output.write_all(path.as_os_str().as_encoded_bytes())?;
255
+ output.write_all(&[0])?;
256
+ }
257
+ return Ok(());
258
+ }
245
259
  let cwd = PathUri::from_abs_path(&AbsolutePathBuf::try_from(PathBuf::from(&root))?);
246
260
  let mode = ApplyPatchFileUpdateMode::PreserveLineEndings;
247
261
  let diagnostic_hunks = parsed.hunks.clone();
@@ -4,16 +4,36 @@
4
4
  // image's builder stage) installs Rust through rustup. A non-rustup `cargo`
5
5
  // earlier on `PATH` (for example Homebrew's) rejects the `+toolchain`
6
6
  // directive, so the build asks rustup's ordinary interface for the pinned
7
- // toolchain's `cargo` explicitly.
7
+ // toolchain's `cargo` explicitly. Cargo itself still resolves `rustc` from
8
+ // `PATH`, so a Homebrew `rustc` listed first would compile with the wrong
9
+ // compiler; the invocation pins `RUSTC` to the toolchain's own compiler too.
8
10
  import { spawnSync } from "node:child_process";
9
11
 
10
12
  /**
11
- * Builds the `{ command, args }` cargo invocation for the pinned toolchain.
13
+ * Builds the `{ command, args, env }` cargo invocation for the pinned
14
+ * toolchain. `env` carries `RUSTC` and must be merged into the child's
15
+ * environment.
12
16
  *
13
17
  * @param {{ toolchain: string, cargoArgs: string[] }} options
14
18
  */
15
19
  export function selectCargoInvocation({ toolchain, cargoArgs }) {
16
- return { command: "rustup", args: ["run", toolchain, "cargo", ...cargoArgs] };
20
+ const which = spawnSync(
21
+ "rustup",
22
+ ["which", "--toolchain", toolchain, "rustc"],
23
+ {
24
+ encoding: "utf8",
25
+ },
26
+ );
27
+ if (which.error) throw which.error;
28
+ if (which.status !== 0)
29
+ throw new Error(
30
+ `rustup could not locate the Rust ${toolchain} toolchain's rustc: ${which.stderr.trim()}`,
31
+ );
32
+ return {
33
+ command: "rustup",
34
+ args: ["run", toolchain, "cargo", ...cargoArgs],
35
+ env: { RUSTC: which.stdout.trim() },
36
+ };
17
37
  }
18
38
 
19
39
  /**
@@ -8,9 +8,6 @@ export declare const computerActionSchema: z.ZodEnum<{
8
8
  mkdir: "mkdir";
9
9
  backing: "backing";
10
10
  bash: "bash";
11
- exposePort: "exposePort";
12
- listPorts: "listPorts";
13
- unexposePort: "unexposePort";
14
11
  "files.read": "files.read";
15
12
  "files.readBytes": "files.readBytes";
16
13
  "files.write": "files.write";
@@ -25,6 +22,9 @@ export declare const computerActionSchema: z.ZodEnum<{
25
22
  processStart: "processStart";
26
23
  processList: "processList";
27
24
  processKill: "processKill";
25
+ exposePort: "exposePort";
26
+ unexposePort: "unexposePort";
27
+ listPorts: "listPorts";
28
28
  }>;
29
29
  export type ComputerAction = z.output<typeof computerActionSchema>;
30
30
  /** Opaque host identities; the fleet does not derive authority from their text. */
@@ -43,9 +43,6 @@ export declare const computerGrantSchema: z.ZodObject<{
43
43
  mkdir: "mkdir";
44
44
  backing: "backing";
45
45
  bash: "bash";
46
- exposePort: "exposePort";
47
- listPorts: "listPorts";
48
- unexposePort: "unexposePort";
49
46
  "files.read": "files.read";
50
47
  "files.readBytes": "files.readBytes";
51
48
  "files.write": "files.write";
@@ -60,6 +57,9 @@ export declare const computerGrantSchema: z.ZodObject<{
60
57
  processStart: "processStart";
61
58
  processList: "processList";
62
59
  processKill: "processKill";
60
+ exposePort: "exposePort";
61
+ unexposePort: "unexposePort";
62
+ listPorts: "listPorts";
63
63
  }>>>;
64
64
  expiresAt: z.ZodNumber;
65
65
  egress: z.ZodOptional<z.ZodJSONSchema>;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { createLocalBackendReplayStore } from "./local-backend-replay.js";
2
- import { Workspace, WorkspaceProxy, WorkspaceServiceProxy, type WorkspaceStubHost } from "@fungi.computer/cloudflare-computer";
2
+ import { Workspace, WorkspaceProxy, WorkspaceServiceProxy, type WorkspaceStub } from "@fungi.computer/cloudflare-computer";
3
3
  import { type WorkerShellBackendOptions } from "@fungi.computer/cloudflare-computer/backends/worker-shell";
4
4
  import { type IWorkspaceContainerAPI } from "@fungi.computer/cloudflare-computer/backends/container";
5
5
  import { DurableObject } from "cloudflare:workers";
@@ -10,8 +10,13 @@ import { createWorkspaceFilesPort } from "./files.js";
10
10
  import { OnBoxVmContainerHost, type OnBoxVmEnv, type OnBoxVmPlacement } from "./on-box-vm.js";
11
11
  import { type AttachedBackend, type AttachedBackendView, type BackendNeed, type BackendRoute, type ContainerRuntimeStart, type HostedBacking } from "./computer-backends.js";
12
12
  import { LocalBackendConnections } from "./local-backend.js";
13
+ /** Files needs only the native Workspace lease and its atomic replacement door. */
14
+ export type ComputerFilesRpc = Readonly<{
15
+ __getWorkspaceStub(): Promise<WorkspaceStub>;
16
+ replaceFileIfUnchanged(input: ComputerFileReplacement): Promise<ComputerFileReplacementResult>;
17
+ }>;
13
18
  /** The exact RPC surface of one Computer Durable Object. */
14
- export type ComputerRpc = WorkspaceStubHost & Readonly<{
19
+ export type ComputerRpc = ComputerFilesRpc & Readonly<{
15
20
  getCapabilities: ComputerDO["getCapabilities"];
16
21
  call: (capabilityId: ComputerValue, input: ComputerValue) => Promise<ComputerValue>;
17
22
  readFile: (input: ComputerFileReadInput) => Promise<ReadableStream<Uint8Array>>;
@@ -31,7 +36,6 @@ export type ComputerRpc = WorkspaceStubHost & Readonly<{
31
36
  port: number;
32
37
  removed: boolean;
33
38
  }>;
34
- replaceFileIfUnchanged: (input: ComputerFileReplacement) => Promise<ComputerFileReplacementResult>;
35
39
  }>;
36
40
  /** The Cloudflare binding projection needed to select one named Computer. */
37
41
  export type ComputerNamespace = Readonly<{
@@ -120,7 +124,7 @@ declare class ContainerComputer extends ContainerComputerBase {
120
124
  */
121
125
  protected readyWorkspace(): Promise<Workspace>;
122
126
  /** `getWorkspace(stub)` reaches the current generation through this door. */
123
- __getWorkspaceStub(): Promise<import("@fungi.computer/cloudflare-computer").WorkspaceStub>;
127
+ __getWorkspaceStub(): Promise<WorkspaceStub>;
124
128
  /**
125
129
  * Backend doors. An attached machine dials in on its public connect path.
126
130
  * A container backend's `/ws` upgrade and egress: the platform container's
@@ -130,7 +134,7 @@ declare class ContainerComputer extends ContainerComputerBase {
130
134
  fetch(request: Request): Promise<Response>;
131
135
  }
132
136
  /** One upstream Cloudflare Workspace that advertises and runs its capabilities. */
133
- export declare class ComputerDO extends ContainerComputer {
137
+ export declare class ComputerDO extends ContainerComputer implements ComputerFilesRpc {
134
138
  #private;
135
139
  constructor(ctx: DurableObjectState, env: ComputerEnv);
136
140
  /** Host-admitted settings write; egress reads the current durable value. */
@@ -294,7 +298,7 @@ export { computerContainerSleepAfterMs } from "./container-policy.js";
294
298
  export * from "./computer-backends.js";
295
299
  export { LocalBackendOfflineError, localBackendAudience, localBackendDialPath, verifyLocalBackendDial, } from "./local-backend.js";
296
300
  /** Keep each native Workspace lease inside the operation that uses it. */
297
- export declare function createComputerFilesPort(computer: ComputerRpc): FilesPort;
301
+ export declare function createComputerFilesPort(computer: ComputerFilesRpc | Rpc.Provider<ComputerFilesRpc>): FilesPort;
298
302
  export { createWorkspaceFilesPort, type WorkspaceFilesOwner } from "./files.js";
299
303
  export { ComputerOperationConflictError, ComputerOperationLimitError, ComputerRequestInvalidError, } from "./operation-record.js";
300
304
  export type { ContainerComputer };
@@ -14544,6 +14544,21 @@ function makeIsGreaterThanOrEqualTo(options) {
14544
14544
  });
14545
14545
  };
14546
14546
  }
14547
+ function makeIsLessThanOrEqualTo(options) {
14548
+ const lte = isLessThanOrEqualTo(options.order);
14549
+ const formatter = options.formatter ?? format;
14550
+ return (maximum, annotations) => {
14551
+ return makeFilter2((input) => lte(input, maximum), {
14552
+ expected: `a value less than or equal to ${formatter(maximum)}`,
14553
+ arbitraryConstraint: {
14554
+ order: options.order,
14555
+ maximum
14556
+ },
14557
+ ...options.annotate?.(maximum),
14558
+ ...annotations
14559
+ });
14560
+ };
14561
+ }
14547
14562
  function makeIsBetween(deriveOptions) {
14548
14563
  const greaterThanOrEqualTo = isGreaterThanOrEqualTo(deriveOptions.order);
14549
14564
  const greaterThan = isGreaterThan(deriveOptions.order);
@@ -14611,6 +14626,23 @@ var isGreaterThanOrEqualTo2 = /* @__PURE__ */ makeIsGreaterThanOrEqualTo({
14611
14626
  })
14612
14627
  })
14613
14628
  });
14629
+ var isLessThanOrEqualTo2 = /* @__PURE__ */ makeIsLessThanOrEqualTo({
14630
+ order: Number2,
14631
+ annotate: (maximum) => ({
14632
+ representation: {
14633
+ id: "effect/schema/isLessThanOrEqualTo",
14634
+ payload: {
14635
+ maximum: encodeNumberPayload(maximum)
14636
+ }
14637
+ },
14638
+ toJsonSchema: () => ({
14639
+ maximum
14640
+ }),
14641
+ toCode: () => ({
14642
+ runtime: `Schema.isLessThanOrEqualTo(${format(maximum)})`
14643
+ })
14644
+ })
14645
+ });
14614
14646
  var isBetween = /* @__PURE__ */ makeIsBetween({
14615
14647
  order: Number2,
14616
14648
  annotate: (options) => {
@@ -16806,14 +16838,14 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
16806
16838
  return `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
16807
16839
  };
16808
16840
  doc.write(`const input = payload.value;`);
16809
- const ids = /* @__PURE__ */ Object.create(null);
16841
+ const ids2 = /* @__PURE__ */ Object.create(null);
16810
16842
  let counter = 0;
16811
16843
  for (const key of normalized.keys) {
16812
- ids[key] = `key_${counter++}`;
16844
+ ids2[key] = `key_${counter++}`;
16813
16845
  }
16814
16846
  doc.write(`const newResult = {};`);
16815
16847
  for (const key of normalized.keys) {
16816
- const id = ids[key];
16848
+ const id = ids2[key];
16817
16849
  const k = esc(key);
16818
16850
  const schema2 = shape[key];
16819
16851
  const isOptionalIn = schema2?._zod?.optin === "optional";
@@ -21625,6 +21657,204 @@ var publicRouteConfigurationSchema = Struct({
21625
21657
  // src/build/candidate-cli.ts
21626
21658
  import { readFile as readFile6 } from "node:fs/promises";
21627
21659
 
21660
+ // ../carrier/dist/internal/post-contract.js
21661
+ var CursorResultSchema = Struct({
21662
+ status: Literals(["accepted", "duplicate"]),
21663
+ cursor: Int.check(isGreaterThanOrEqualTo2(0))
21664
+ });
21665
+ var PostDeliveryResultSchema = Union2([
21666
+ Literals(["accepted", "duplicate", "refused"]),
21667
+ CursorResultSchema,
21668
+ Struct({
21669
+ status: Literals(["accepted", "duplicate", "refused"]),
21670
+ receipt: Json2
21671
+ })
21672
+ ]);
21673
+ var PostError = class extends TaggedError2("PostError") {
21674
+ constructor(message, reason) {
21675
+ super(reason === void 0 ? { message } : { message, reason });
21676
+ }
21677
+ };
21678
+
21679
+ // ../carrier/dist/post.js
21680
+ var Key = String4.check(isMinLength(1), isMaxLength(256));
21681
+ var JobPayload = Struct({ postKey: Key });
21682
+ var PageLimit = Int.check(isGreaterThanOrEqualTo2(1), isLessThanOrEqualTo2(64));
21683
+ var maxRouteDeadlineMs = 7 * 24 * 60 * 60 * 1e3;
21684
+ var ids = defineIdFamily({
21685
+ family: "post",
21686
+ version: 1,
21687
+ kinds: { work: { prefix: "post_work" } }
21688
+ });
21689
+ var PostEnvelopeSchema = Struct({
21690
+ key: Key,
21691
+ kind: Key,
21692
+ address: Unknown2,
21693
+ event: Unknown2
21694
+ });
21695
+ var PostRowSchema = fromJsonString2(Union2([
21696
+ Struct({
21697
+ state: Literal2("pending"),
21698
+ envelope: PostEnvelopeSchema,
21699
+ attempts: Int.check(isGreaterThanOrEqualTo2(0)),
21700
+ nextAttemptAt: Number5.check(isGreaterThanOrEqualTo2(0)),
21701
+ deadlineAt: optional2(Number5.check(isGreaterThanOrEqualTo2(0))),
21702
+ jobId: optional2(Key),
21703
+ delivery: optional2(PostEnvelopeSchema),
21704
+ nextKey: optional2(Key)
21705
+ }),
21706
+ Struct({
21707
+ state: Literal2("delivered"),
21708
+ receipt: optional2(Json2),
21709
+ nextKey: optional2(Key)
21710
+ }),
21711
+ Struct({
21712
+ state: Literal2("dead"),
21713
+ envelope: PostEnvelopeSchema,
21714
+ attempts: Int.check(isGreaterThanOrEqualTo2(0)),
21715
+ reason: Literals(["refused", "exhausted", "expired"]),
21716
+ deadlineAt: optional2(Number5.check(isGreaterThanOrEqualTo2(0))),
21717
+ receipt: optional2(Json2),
21718
+ delivery: optional2(PostEnvelopeSchema),
21719
+ nextKey: optional2(Key)
21720
+ })
21721
+ ]));
21722
+ var StoredRow = Struct({ key: String4, value: String4 });
21723
+ var DueRow = Struct({
21724
+ due: NullOr(Number5.check(isGreaterThanOrEqualTo2(0)))
21725
+ });
21726
+
21727
+ // ../bird-dog/dist/effect.js
21728
+ var Identifier = String4.check(isMinLength(1), isMaxLength(256));
21729
+ var BIRD_DOG_MAIL_CONTENT_MAX_BYTES = 256 * 1024;
21730
+ var MailContent = String4.check(makeFilter2((content) => new TextEncoder().encode(content).byteLength <= BIRD_DOG_MAIL_CONTENT_MAX_BYTES, {
21731
+ message: `Mail content must be at most ${String(BIRD_DOG_MAIL_CONTENT_MAX_BYTES)} UTF-8 bytes`
21732
+ }));
21733
+ var BirdDogAgentReferenceSchema = Struct({
21734
+ kind: Literal2("agent"),
21735
+ agentId: Identifier,
21736
+ sessionId: Identifier
21737
+ });
21738
+ var BirdDogAgentRecipientReferenceSchema = Struct({
21739
+ kind: Literal2("agent"),
21740
+ agentId: Identifier,
21741
+ sessionId: optionalKey2(Identifier)
21742
+ });
21743
+ var BirdDogSystemIdentitySchema = Struct({
21744
+ kind: Literal2("system"),
21745
+ systemId: Literal2("hub")
21746
+ });
21747
+ var BIRD_DOG_DIAGNOSTIC_MAX_LENGTH = 200;
21748
+ var SettledBirdDogReplySchema = Struct({
21749
+ commandId: Identifier,
21750
+ outcome: Literals([
21751
+ "completed",
21752
+ "failed",
21753
+ "interrupted",
21754
+ "cancelled",
21755
+ "outcome_unknown"
21756
+ ]),
21757
+ diagnostic: optionalKey2(String4.check(isMinLength(1), isMaxLength(BIRD_DOG_DIAGNOSTIC_MAX_LENGTH)))
21758
+ });
21759
+ var BirdDogUndeliverableReasonSchema = Literals([
21760
+ "recipient_offline",
21761
+ "recipient_left"
21762
+ ]);
21763
+ var UndeliverableBirdDogReplySchema = Struct({
21764
+ outcome: Literal2("undeliverable"),
21765
+ reason: BirdDogUndeliverableReasonSchema
21766
+ });
21767
+ var AcceptedBirdDogReplySchema = Struct({
21768
+ outcome: Literal2("accepted"),
21769
+ commandId: Identifier
21770
+ });
21771
+ var BirdDogReplySchema = Union2([
21772
+ SettledBirdDogReplySchema,
21773
+ UndeliverableBirdDogReplySchema,
21774
+ AcceptedBirdDogReplySchema
21775
+ ]);
21776
+ var BirdDogAgentReplySchema = Union2([
21777
+ SettledBirdDogReplySchema,
21778
+ AcceptedBirdDogReplySchema,
21779
+ Struct({
21780
+ outcome: Literal2("undeliverable"),
21781
+ reason: Literal2("recipient_left")
21782
+ })
21783
+ ]);
21784
+ var REDACTED = "[redacted]";
21785
+ var schemeCredential = /\b(bearer|basic)\s+[^\s"',;]+/giu;
21786
+ var namedCredential = /\b((?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|secret|password|authorization)["']?\s*[:=]\s*["']?)(?!(?:bearer|basic)\s)[^\s"',;&]+/giu;
21787
+ var opaqueToken = /[A-Za-z0-9][\w-]{23,}/gu;
21788
+ function redactCredentials(text) {
21789
+ return text.replace(schemeCredential, `$1 ${REDACTED}`).replace(namedCredential, `$1${REDACTED}`).replace(opaqueToken, (token) => /\d/u.test(token) && /[A-Za-z]/u.test(token) ? REDACTED : token);
21790
+ }
21791
+ function boundedDiagnostic(text) {
21792
+ const line = redactCredentials(text.replace(/[\p{Cc}\p{Zl}\p{Zp}\s]+/gu, " ").trim()).slice(0, BIRD_DOG_DIAGNOSTIC_MAX_LENGTH).trimEnd();
21793
+ return line.length === 0 ? void 0 : line;
21794
+ }
21795
+ var BirdDogMailFields = {
21796
+ key: Identifier,
21797
+ to: BirdDogAgentRecipientReferenceSchema,
21798
+ replyTo: ArraySchema(BirdDogAgentReferenceSchema),
21799
+ content: MailContent,
21800
+ createdAt: Number5.check(isGreaterThanOrEqualTo2(0)),
21801
+ /** The sender's opaque host metadata; BirdDog never reads it. */
21802
+ meta: optionalKey2(Json2)
21803
+ };
21804
+ var BirdDogMailSchema = Union2([
21805
+ Struct({
21806
+ ...BirdDogMailFields,
21807
+ from: BirdDogAgentReferenceSchema,
21808
+ /** Settlement or acceptance facts, present only on a reply. */
21809
+ reply: optionalKey2(BirdDogAgentReplySchema)
21810
+ }),
21811
+ Struct({
21812
+ ...BirdDogMailFields,
21813
+ from: BirdDogSystemIdentitySchema,
21814
+ /** Only the Hub may author this offline disposition. */
21815
+ reply: Struct({
21816
+ outcome: Literal2("undeliverable"),
21817
+ reason: Literal2("recipient_offline")
21818
+ })
21819
+ })
21820
+ ]);
21821
+ var BirdDogAgentWorkSchema = Struct({
21822
+ jobId: Identifier,
21823
+ sessionId: Identifier,
21824
+ commandFactId: Identifier,
21825
+ recovery: optional2(Struct({
21826
+ maxRecoveries: Int.check(isGreaterThanOrEqualTo2(0))
21827
+ }))
21828
+ });
21829
+ var BirdDogAgentJobPayloadSchema = Struct({
21830
+ work: Struct({
21831
+ sessionId: Identifier,
21832
+ commandFactId: Identifier
21833
+ }),
21834
+ mail: optional2(BirdDogMailSchema)
21835
+ });
21836
+ var BirdDogAgentJobInputSchema = Struct({
21837
+ work: BirdDogAgentWorkSchema,
21838
+ mail: optional2(BirdDogMailSchema)
21839
+ });
21840
+
21841
+ // src/build/candidate-diagnostic.ts
21842
+ function candidateFailureDiagnostic(cause) {
21843
+ const messages = [];
21844
+ let current = cause;
21845
+ for (let depth = 0; depth < 3; depth += 1) {
21846
+ if (!(current instanceof Error)) {
21847
+ if (depth === 0) messages.push("Non-error thrown");
21848
+ break;
21849
+ }
21850
+ messages.push(
21851
+ `${depth === 0 ? "" : "Caused by: "}${current.message || current.name}`
21852
+ );
21853
+ current = current.cause;
21854
+ }
21855
+ return boundedDiagnostic(messages.join("; ")) ?? "Build failed";
21856
+ }
21857
+
21628
21858
  // ../puffball/dist/assets.js
21629
21859
  var positiveInteger = Number5.pipe(check(isInt()), check(isGreaterThan2(0)), check(makeFilter2(Number.isSafeInteger)));
21630
21860
  var assetBuildPolicySchema = Struct({
@@ -36087,10 +36317,14 @@ async function buildTeamCandidate(input) {
36087
36317
  var uploadConcurrency = 4;
36088
36318
  var uploadAttempts = 3;
36089
36319
  function reportFailure2(cause) {
36320
+ const diagnostic = candidateFailureDiagnostic(cause);
36321
+ process.stderr.write(`Team App source/build failed: ${diagnostic}
36322
+ `);
36090
36323
  process.stderr.write(
36091
36324
  `${JSON.stringify({
36092
36325
  source: "oyster-candidate-builder",
36093
- failureKind: cause instanceof Error ? "Error" : "NonErrorThrown"
36326
+ failureKind: cause instanceof Error ? "Error" : "NonErrorThrown",
36327
+ diagnostic
36094
36328
  })}
36095
36329
  `
36096
36330
  );
@@ -36199,7 +36433,6 @@ try {
36199
36433
  );
36200
36434
  } catch (cause) {
36201
36435
  reportFailure2(cause);
36202
- process.stderr.write("Team App source/build failed\n");
36203
36436
  process.exitCode = 1;
36204
36437
  }
36205
36438
  /*! Bundled license information:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/computer",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "description": "Cloudflare Computer host for one durable Fungi Computer",
6
6
  "type": "module",
@@ -35,6 +35,10 @@
35
35
  "types": "./dist/fleet/wire.d.ts",
36
36
  "import": "./dist/fleet/wire.js"
37
37
  },
38
+ "./fleet/inventory-wire": {
39
+ "types": "./dist/fleet/inventory-wire.d.ts",
40
+ "import": "./dist/fleet/inventory-wire.js"
41
+ },
38
42
  "./fleet": {
39
43
  "types": "./dist/fleet/index.d.ts",
40
44
  "import": "./dist/fleet/index.js"
@@ -55,11 +59,11 @@
55
59
  "@fungi.computer/cairn": "0.1.0",
56
60
  "@fungi.computer/capnweb": "0.12.0",
57
61
  "@fungi.computer/cloudflare-computer": "0.2.1-fungi.2",
58
- "@fungi.computer/cordyceps": "0.3.1",
59
- "@fungi.computer/knapsack": "0.2.1",
60
- "@fungi.computer/seam": "0.1.0",
62
+ "@fungi.computer/cordyceps": "0.4.0",
63
+ "@fungi.computer/knapsack": "0.3.1",
64
+ "@fungi.computer/seam": "0.2.0",
61
65
  "@fungi.computer/watchdog": "0.1.0",
62
- "@fungi.computer/computer-contract": "0.0.2",
66
+ "@fungi.computer/computer-contract": "0.0.4",
63
67
  "effect": "4.0.2",
64
68
  "just-bash": "3.4.2",
65
69
  "zod": "4.4.3"