@fungi.computer/truffle 0.0.0-stage → 0.2.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.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +542 -2
  3. package/computerd-loopback.cjs +16 -0
  4. package/dist/auth-cli.d.ts +46 -0
  5. package/dist/auth-cli.js +373 -0
  6. package/dist/fungi-account-command.d.ts +18 -0
  7. package/dist/fungi-account-command.js +95 -0
  8. package/dist/index.d.ts +27 -0
  9. package/dist/index.js +23 -0
  10. package/dist/internal/image-fit-metafile.json +106 -0
  11. package/dist/internal/local-image-fit-worker.mjs +960 -0
  12. package/dist/internal/private-workerd-sandbox-worker.mjs +27487 -0
  13. package/dist/internal/sandbox-metafile.json +6726 -0
  14. package/dist/linear-auth.d.ts +4 -0
  15. package/dist/linear-auth.js +72 -0
  16. package/dist/local-archive-handoff.d.ts +12 -0
  17. package/dist/local-archive-handoff.js +21 -0
  18. package/dist/local-extensions.d.ts +143 -0
  19. package/dist/local-extensions.js +624 -0
  20. package/dist/local-image-fit.d.ts +30 -0
  21. package/dist/local-image-fit.js +105 -0
  22. package/dist/local-login.d.ts +55 -0
  23. package/dist/local-login.js +167 -0
  24. package/dist/local-models.d.ts +16 -0
  25. package/dist/local-models.js +82 -0
  26. package/dist/local-session-archives.d.ts +8 -0
  27. package/dist/local-session-archives.js +152 -0
  28. package/dist/local-skill-source.d.ts +21 -0
  29. package/dist/local-skill-source.js +433 -0
  30. package/dist/log-event.d.ts +9 -0
  31. package/dist/log-event.js +13 -0
  32. package/dist/private-bounded-response.d.ts +2 -0
  33. package/dist/private-bounded-response.js +41 -0
  34. package/dist/private-bun-shiitake-host.d.ts +11 -0
  35. package/dist/private-bun-shiitake-host.js +66 -0
  36. package/dist/private-file-credential-store.d.ts +58 -0
  37. package/dist/private-file-credential-store.js +331 -0
  38. package/dist/private-files-patch.d.ts +4 -0
  39. package/dist/private-files-patch.js +104 -0
  40. package/dist/private-fungi-account.d.ts +252 -0
  41. package/dist/private-fungi-account.js +1329 -0
  42. package/dist/private-fungi-auth-error.d.ts +5 -0
  43. package/dist/private-fungi-auth-error.js +7 -0
  44. package/dist/private-fungi-mcp-connection.d.ts +26 -0
  45. package/dist/private-fungi-mcp-connection.js +162 -0
  46. package/dist/private-fungi-mcp-endpoint.d.ts +2 -0
  47. package/dist/private-fungi-mcp-endpoint.js +15 -0
  48. package/dist/private-fungi-mcp-selection-schema.d.ts +7 -0
  49. package/dist/private-fungi-mcp-selection-schema.js +15 -0
  50. package/dist/private-fungi-mcp-selection.d.ts +14 -0
  51. package/dist/private-fungi-mcp-selection.js +49 -0
  52. package/dist/private-fungi-model-api.d.ts +6 -0
  53. package/dist/private-fungi-model-api.js +125 -0
  54. package/dist/private-fungi-model-catalog.d.ts +16 -0
  55. package/dist/private-fungi-model-catalog.js +86 -0
  56. package/dist/private-fungi-model-provider.d.ts +6 -0
  57. package/dist/private-fungi-model-provider.js +51 -0
  58. package/dist/private-fungi-pending-logouts.d.ts +34 -0
  59. package/dist/private-fungi-pending-logouts.js +135 -0
  60. package/dist/private-fungi-service-logout.d.ts +44 -0
  61. package/dist/private-fungi-service-logout.js +59 -0
  62. package/dist/private-invocation-computer.d.ts +58 -0
  63. package/dist/private-invocation-computer.js +498 -0
  64. package/dist/private-invocation-files.d.ts +30 -0
  65. package/dist/private-invocation-files.js +520 -0
  66. package/dist/private-local-acp-stdio.d.ts +17 -0
  67. package/dist/private-local-acp-stdio.js +145 -0
  68. package/dist/private-local-admission-gate.d.ts +14 -0
  69. package/dist/private-local-admission-gate.js +52 -0
  70. package/dist/private-local-agent-control.d.ts +101 -0
  71. package/dist/private-local-agent-control.js +30 -0
  72. package/dist/private-local-agent-default-model.d.ts +12 -0
  73. package/dist/private-local-agent-default-model.js +31 -0
  74. package/dist/private-local-auth-rpc.d.ts +129 -0
  75. package/dist/private-local-auth-rpc.js +396 -0
  76. package/dist/private-local-bwrap.d.ts +13 -0
  77. package/dist/private-local-bwrap.js +70 -0
  78. package/dist/private-local-client-version.d.ts +5 -0
  79. package/dist/private-local-client-version.js +43 -0
  80. package/dist/private-local-computer-attach.d.ts +49 -0
  81. package/dist/private-local-computer-attach.js +230 -0
  82. package/dist/private-local-computer-backend.d.ts +282 -0
  83. package/dist/private-local-computer-backend.js +402 -0
  84. package/dist/private-local-computer-processes.d.ts +202 -0
  85. package/dist/private-local-computer-processes.js +613 -0
  86. package/dist/private-local-config-root.d.ts +21 -0
  87. package/dist/private-local-config-root.js +19 -0
  88. package/dist/private-local-containment.d.ts +51 -0
  89. package/dist/private-local-containment.js +161 -0
  90. package/dist/private-local-default-model.d.ts +11 -0
  91. package/dist/private-local-default-model.js +45 -0
  92. package/dist/private-local-delegation.d.ts +36 -0
  93. package/dist/private-local-delegation.js +133 -0
  94. package/dist/private-local-effective-model.d.ts +27 -0
  95. package/dist/private-local-effective-model.js +17 -0
  96. package/dist/private-local-exec-environment.d.ts +16 -0
  97. package/dist/private-local-exec-environment.js +11 -0
  98. package/dist/private-local-exec-launch.d.ts +41 -0
  99. package/dist/private-local-exec-launch.js +51 -0
  100. package/dist/private-local-exec-path.d.ts +6 -0
  101. package/dist/private-local-exec-path.js +40 -0
  102. package/dist/private-local-git-exclude.d.ts +2 -0
  103. package/dist/private-local-git-exclude.js +33 -0
  104. package/dist/private-local-http-dispatch.d.ts +5 -0
  105. package/dist/private-local-http-dispatch.js +14 -0
  106. package/dist/private-local-linux-sandbox.d.ts +8 -0
  107. package/dist/private-local-linux-sandbox.js +67 -0
  108. package/dist/private-local-mail.d.ts +13 -0
  109. package/dist/private-local-mail.js +77 -0
  110. package/dist/private-local-mesh-enrollment.d.ts +34 -0
  111. package/dist/private-local-mesh-enrollment.js +188 -0
  112. package/dist/private-local-mesh-links.d.ts +21 -0
  113. package/dist/private-local-mesh-links.js +229 -0
  114. package/dist/private-local-mesh-record.d.ts +120 -0
  115. package/dist/private-local-mesh-record.js +55 -0
  116. package/dist/private-local-process-identity.d.ts +20 -0
  117. package/dist/private-local-process-identity.js +40 -0
  118. package/dist/private-local-runtime-host.d.ts +192 -0
  119. package/dist/private-local-runtime-host.js +1177 -0
  120. package/dist/private-local-sandbox-policy.d.ts +21 -0
  121. package/dist/private-local-sandbox-policy.js +121 -0
  122. package/dist/private-local-sandbox.d.ts +37 -0
  123. package/dist/private-local-sandbox.js +114 -0
  124. package/dist/private-local-seatbelt.d.ts +24 -0
  125. package/dist/private-local-seatbelt.js +68 -0
  126. package/dist/private-local-service-contract.d.ts +170 -0
  127. package/dist/private-local-service-contract.js +1 -0
  128. package/dist/private-local-service-discovery.d.ts +217 -0
  129. package/dist/private-local-service-discovery.js +322 -0
  130. package/dist/private-local-service-ids.d.ts +3 -0
  131. package/dist/private-local-service-ids.js +8 -0
  132. package/dist/private-local-service-lease.d.ts +16 -0
  133. package/dist/private-local-service-lease.js +105 -0
  134. package/dist/private-local-service-listener.d.ts +22 -0
  135. package/dist/private-local-service-listener.js +407 -0
  136. package/dist/private-local-service-log.d.ts +128 -0
  137. package/dist/private-local-service-log.js +321 -0
  138. package/dist/private-local-service-owner.d.ts +135 -0
  139. package/dist/private-local-service-owner.js +1369 -0
  140. package/dist/private-local-service-process.d.ts +1 -0
  141. package/dist/private-local-service-process.js +8 -0
  142. package/dist/private-local-service-termination.d.ts +7 -0
  143. package/dist/private-local-service-termination.js +67 -0
  144. package/dist/private-local-service.d.ts +46 -0
  145. package/dist/private-local-service.js +606 -0
  146. package/dist/private-local-session-discovery.d.ts +66 -0
  147. package/dist/private-local-session-discovery.js +278 -0
  148. package/dist/private-local-shiitake-host.d.ts +44 -0
  149. package/dist/private-local-shiitake-host.js +143 -0
  150. package/dist/private-local-startup-model.d.ts +51 -0
  151. package/dist/private-local-startup-model.js +86 -0
  152. package/dist/private-local-stop-all.d.ts +27 -0
  153. package/dist/private-local-stop-all.js +26 -0
  154. package/dist/private-local-ui-attachment.d.ts +19 -0
  155. package/dist/private-local-ui-attachment.js +71 -0
  156. package/dist/private-local-worker-server.d.ts +16 -0
  157. package/dist/private-local-worker-server.js +144 -0
  158. package/dist/private-mcp-form-contract.d.ts +186 -0
  159. package/dist/private-mcp-form-contract.js +53 -0
  160. package/dist/private-mcp-form-elicitation.d.ts +18 -0
  161. package/dist/private-mcp-form-elicitation.js +97 -0
  162. package/dist/private-mcp-form-session.d.ts +172 -0
  163. package/dist/private-mcp-form-session.js +148 -0
  164. package/dist/private-openai-claims.d.ts +30 -0
  165. package/dist/private-openai-claims.js +69 -0
  166. package/dist/private-oyster-mcp-auth.d.ts +13 -0
  167. package/dist/private-oyster-mcp-auth.js +57 -0
  168. package/dist/private-provider-rejection.d.ts +15 -0
  169. package/dist/private-provider-rejection.js +24 -0
  170. package/dist/private-sqlite-owner.d.ts +39 -0
  171. package/dist/private-sqlite-owner.js +169 -0
  172. package/dist/private-team-computers.d.ts +38 -0
  173. package/dist/private-team-computers.js +110 -0
  174. package/dist/private-workerd-sandbox.d.ts +3 -0
  175. package/dist/private-workerd-sandbox.js +347 -0
  176. package/dist/seatbelt/safe-base.sbpl +79 -0
  177. package/dist/subscription-activation.d.ts +9 -0
  178. package/dist/subscription-activation.js +62 -0
  179. package/dist/subscription-identity.d.ts +23 -0
  180. package/dist/subscription-identity.js +124 -0
  181. package/dist/subscription-turn.d.ts +17 -0
  182. package/dist/subscription-turn.js +143 -0
  183. package/dist/team-apps.d.ts +24 -0
  184. package/dist/team-apps.js +128 -0
  185. package/dist/tracker-git-credential.d.ts +15 -0
  186. package/dist/tracker-git-credential.js +87 -0
  187. package/native/bin/LICENSE.bubblewrap +502 -0
  188. package/native/bin/LICENSE.libcap +112 -0
  189. package/native/bin/LICENSE.sandbox-runtime +201 -0
  190. package/native/bin/SOURCE.bubblewrap.tar.xz +0 -0
  191. package/native/bin/build.json +4037 -0
  192. package/native/bin/bwrap-build-source/Dockerfile +48 -0
  193. package/native/bin/bwrap-build-source/README.md +96 -0
  194. package/native/bin/bwrap-build-source/build.mjs +52 -0
  195. package/native/bin/bwrap-build-source/linux-arm64.ini +14 -0
  196. package/native/bin/bwrap-build-source/pin.mjs +21 -0
  197. package/native/bin/bwrap-build-source/record.mjs +74 -0
  198. package/native/bin/bwrap-build-source/vendor/LICENSE.sandbox-runtime +201 -0
  199. package/native/bin/bwrap-build-source/vendor/README.md +23 -0
  200. package/native/bin/bwrap-build-source/vendor/seccomp-unix-block.c +280 -0
  201. package/native/bin/bwrap-build.json +44 -0
  202. package/native/bin/fungi-apply-patch +0 -0
  203. package/native/bin/fungi-bwrap +0 -0
  204. package/native/bin/fungi-bwrap.sha256 +1 -0
  205. package/native/bin/fungi-seccomp.bpf +0 -0
  206. package/native/bin/fungi-seccomp.bpf.sha256 +1 -0
  207. package/package.json +161 -3
@@ -0,0 +1,229 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
2
+ import { z } from "zod";
3
+ import { createMachineLinkPostChannel, MachineLinkPostUnavailable, machineLinkAudience, machineLinkDialUrl, } from "@fungi.computer/seam/machine-link-protocol";
4
+ import { importSeamSigner, signSeamRequest, seamHeader, unsignedPayload, } from "@fungi.computer/seam";
5
+ import { createLocalMachineEnrollmentAccount } from "./private-fungi-account.js";
6
+ import { logEvent } from "./log-event.js";
7
+ export const localMeshLinkKey = (record) => JSON.stringify([
8
+ record.authority.kind,
9
+ record.authority.origin,
10
+ record.scopeId,
11
+ record.deviceId,
12
+ ]);
13
+ /**
14
+ * Service-owned native I/O only. The account owns pins, Carrier owns retry
15
+ * custody, and the addressed runtime owns acceptance. No mail is queued here.
16
+ */
17
+ export function createLocalMeshLinks(options) {
18
+ const account = createLocalMachineEnrollmentAccount(options.credentialPath);
19
+ const links = new Map();
20
+ let stopping = false;
21
+ let refreshing;
22
+ const isStopping = () => stopping;
23
+ async function current(record) {
24
+ const saved = await account.read();
25
+ const { authority, ...pin } = record;
26
+ return saved.some((source) => source.kind === authority.kind &&
27
+ source.origin === authority.origin &&
28
+ source.enrollments.some((candidate) => JSON.stringify(candidate) === JSON.stringify(pin)));
29
+ }
30
+ async function isCurrent(record) {
31
+ const link = links.get(localMeshLinkKey(record));
32
+ return (link !== undefined &&
33
+ !link.stop.signal.aborted &&
34
+ (await current(link.record)));
35
+ }
36
+ async function connection(record, signal, publish) {
37
+ const url = machineLinkDialUrl(record.authority.origin, record.scopeId, record.deviceId);
38
+ const signer = await importSeamSigner(`${record.deviceId}:${record.privateKey}`);
39
+ const signature = await signSeamRequest(signer, machineLinkAudience(record.scopeId, record.deviceId), "GET", url.href, unsignedPayload);
40
+ signal.throwIfAborted();
41
+ if (!(await current(record)))
42
+ throw new MachineLinkPostUnavailable({ reason: "closed" });
43
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
44
+ const socket = new WebSocket(url, {
45
+ headers: {
46
+ [seamHeader]: signature,
47
+ },
48
+ });
49
+ const channel = createMachineLinkPostChannel({
50
+ limits: {
51
+ maxInFlight: 32,
52
+ maxFrameBytes: 512 * 1024,
53
+ acknowledgmentMs: 10_000,
54
+ },
55
+ send: (frame) => {
56
+ socket.send(frame);
57
+ },
58
+ receive: async (envelope) => (await current(record)) ? options.receive(record, envelope) : "refused",
59
+ });
60
+ let ping;
61
+ let pong;
62
+ let opening;
63
+ const abort = () => {
64
+ socket.close(1000, "service stopping");
65
+ };
66
+ try {
67
+ await new Promise((resolve, reject) => {
68
+ const fail = (cause) => {
69
+ reject(cause instanceof Error
70
+ ? cause
71
+ : new Error("Machine link failed", { cause }));
72
+ };
73
+ opening = setTimeout(() => {
74
+ fail(new Error("Machine link upgrade timed out"));
75
+ }, 10_000);
76
+ signal.addEventListener("abort", abort, { once: true });
77
+ socket.addEventListener("error", () => {
78
+ fail(new Error("Machine link native socket failed"));
79
+ });
80
+ socket.addEventListener("close", () => {
81
+ resolve();
82
+ }, { once: true });
83
+ socket.addEventListener("open", () => {
84
+ clearTimeout(opening);
85
+ publish(channel);
86
+ ping = setInterval(() => {
87
+ void current(record).then((allowed) => {
88
+ if (!allowed) {
89
+ fail(new MachineLinkPostUnavailable({ reason: "closed" }));
90
+ return;
91
+ }
92
+ try {
93
+ socket.send("ping");
94
+ }
95
+ catch (cause) {
96
+ reject(new Error("Machine link ping failed", { cause }));
97
+ return;
98
+ }
99
+ pong = setTimeout(() => {
100
+ fail(new Error("Machine link pong timed out"));
101
+ }, 10_000);
102
+ }, fail);
103
+ }, 30_000);
104
+ }, { once: true });
105
+ socket.addEventListener("message", (event) => {
106
+ if (event.data === "pong") {
107
+ clearTimeout(pong);
108
+ return;
109
+ }
110
+ const text = z.string().safeParse(event.data);
111
+ if (!text.success) {
112
+ fail(new Error("Machine link requires text frames"));
113
+ return;
114
+ }
115
+ // EventTarget cannot await a handler. Transfer its actual outcome to
116
+ // the connection's awaited lifetime, never a detached dropped catch.
117
+ void channel.receive(text.data).then(() => undefined, fail);
118
+ });
119
+ });
120
+ }
121
+ finally {
122
+ publish(undefined);
123
+ signal.removeEventListener("abort", abort);
124
+ clearTimeout(opening);
125
+ clearInterval(ping);
126
+ clearTimeout(pong);
127
+ channel.close();
128
+ socket.close(1000, "connection ended");
129
+ }
130
+ }
131
+ async function refresh() {
132
+ if (refreshing !== undefined) {
133
+ await refreshing;
134
+ await refresh();
135
+ return;
136
+ }
137
+ const task = (async () => {
138
+ if (isStopping())
139
+ return;
140
+ const saved = await account.read();
141
+ const enrolled = saved.flatMap(({ kind, origin, enrollments }) => enrollments.flatMap((record) => record.state === "enrolled"
142
+ ? [{ ...record, authority: { kind, origin } }]
143
+ : []));
144
+ for (const [key, link] of links)
145
+ if (!enrolled.some((record) => localMeshLinkKey(record) === key &&
146
+ JSON.stringify(record) === JSON.stringify(link.record))) {
147
+ link.stop.abort();
148
+ await link.done;
149
+ links.delete(key);
150
+ }
151
+ if (isStopping())
152
+ return;
153
+ for (const record of enrolled) {
154
+ const key = localMeshLinkKey(record);
155
+ if (links.has(key))
156
+ continue;
157
+ const stop = new AbortController();
158
+ const stopped = () => stop.signal.aborted;
159
+ const link = {
160
+ record,
161
+ stop,
162
+ channel: undefined,
163
+ done: (async () => {
164
+ while (!stopped()) {
165
+ try {
166
+ if (!(await current(record)))
167
+ return "stopped";
168
+ await connection(record, stop.signal, (channel) => {
169
+ link.channel = channel;
170
+ });
171
+ }
172
+ catch (cause) {
173
+ if (stopped())
174
+ return "stopped";
175
+ logEvent("mesh-link.connection", cause);
176
+ }
177
+ try {
178
+ await delay(1000, undefined, { signal: stop.signal });
179
+ }
180
+ catch (cause) {
181
+ if (stopped())
182
+ return "stopped";
183
+ throw cause;
184
+ }
185
+ }
186
+ return "stopped";
187
+ })(),
188
+ };
189
+ links.set(key, link);
190
+ }
191
+ })();
192
+ refreshing = task;
193
+ try {
194
+ await task;
195
+ }
196
+ finally {
197
+ refreshing = undefined;
198
+ }
199
+ }
200
+ return {
201
+ refresh,
202
+ isCurrent,
203
+ deliver: async (record, envelope) => {
204
+ const link = links.get(localMeshLinkKey(record));
205
+ const channel = link?.channel;
206
+ if (channel === undefined || !(await isCurrent(record))) {
207
+ link?.stop.abort();
208
+ throw new MachineLinkPostUnavailable({ reason: "closed" });
209
+ }
210
+ return await channel.deliver(envelope);
211
+ },
212
+ close: async () => {
213
+ stopping = true;
214
+ try {
215
+ await refreshing;
216
+ }
217
+ finally {
218
+ for (const link of links.values())
219
+ link.stop.abort();
220
+ try {
221
+ await Promise.all([...links.values()].map((link) => link.done));
222
+ }
223
+ finally {
224
+ links.clear();
225
+ }
226
+ }
227
+ },
228
+ };
229
+ }
@@ -0,0 +1,120 @@
1
+ import { z } from "zod";
2
+ export declare const machineBindingSchema: z.ZodObject<{
3
+ selection: z.ZodNumber;
4
+ agentId: z.ZodString;
5
+ }, z.core.$strict>;
6
+ /** Private credential-file state; only public key, label and names go to Hub. */
7
+ export declare const localMachineEnrollmentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
8
+ requestId: z.ZodString;
9
+ target: z.ZodDiscriminatedUnion<[z.ZodObject<{
10
+ kind: z.ZodLiteral<"oauth">;
11
+ teamId: z.ZodString;
12
+ }, z.core.$strict>, z.ZodObject<{
13
+ kind: z.ZodLiteral<"oyster">;
14
+ }, z.core.$strict>], "kind">;
15
+ invocationRoot: z.ZodString;
16
+ machineLabel: z.ZodString;
17
+ publicKey: z.ZodBase64;
18
+ privateKey: z.ZodBase64;
19
+ agents: z.ZodArray<z.ZodObject<{
20
+ localAgentId: z.ZodString;
21
+ databasePath: z.ZodString;
22
+ name: z.ZodString;
23
+ }, z.core.$strict>>;
24
+ state: z.ZodLiteral<"pending">;
25
+ }, z.core.$strict>, z.ZodObject<{
26
+ requestId: z.ZodString;
27
+ target: z.ZodDiscriminatedUnion<[z.ZodObject<{
28
+ kind: z.ZodLiteral<"oauth">;
29
+ teamId: z.ZodString;
30
+ }, z.core.$strict>, z.ZodObject<{
31
+ kind: z.ZodLiteral<"oyster">;
32
+ }, z.core.$strict>], "kind">;
33
+ invocationRoot: z.ZodString;
34
+ machineLabel: z.ZodString;
35
+ publicKey: z.ZodBase64;
36
+ privateKey: z.ZodBase64;
37
+ agents: z.ZodArray<z.ZodObject<{
38
+ localAgentId: z.ZodString;
39
+ databasePath: z.ZodString;
40
+ name: z.ZodString;
41
+ }, z.core.$strict>>;
42
+ state: z.ZodLiteral<"enrolled">;
43
+ scopeId: z.ZodString;
44
+ deviceId: z.ZodString;
45
+ bindings: z.ZodArray<z.ZodObject<{
46
+ selection: z.ZodNumber;
47
+ agentId: z.ZodString;
48
+ }, z.core.$strict>>;
49
+ }, z.core.$strict>], "state">;
50
+ export type LocalMachineEnrollment = z.infer<typeof localMachineEnrollmentSchema>;
51
+ export declare const machineEnrollmentAuthoritySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
52
+ kind: z.ZodLiteral<"oauth">;
53
+ origin: z.ZodURL;
54
+ }, z.core.$strict>, z.ZodObject<{
55
+ kind: z.ZodLiteral<"oyster">;
56
+ origin: z.ZodURL;
57
+ }, z.core.$strict>], "kind">;
58
+ export type MachineEnrollmentAuthority = z.infer<typeof machineEnrollmentAuthoritySchema>;
59
+ /**
60
+ * One authority's device enrollments, keyed in the credential file by origin:
61
+ * a standalone Oyster's, or a Fungi issuer's partition. Every enrollment in a
62
+ * record names the same authority kind. Never a Pi credential or a bearer.
63
+ */
64
+ export declare const machineEnrollmentRecordSchema: z.ZodObject<{
65
+ type: z.ZodLiteral<"machine_enrollment">;
66
+ origin: z.ZodURL;
67
+ enrollments: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
68
+ requestId: z.ZodString;
69
+ target: z.ZodDiscriminatedUnion<[z.ZodObject<{
70
+ kind: z.ZodLiteral<"oauth">;
71
+ teamId: z.ZodString;
72
+ }, z.core.$strict>, z.ZodObject<{
73
+ kind: z.ZodLiteral<"oyster">;
74
+ }, z.core.$strict>], "kind">;
75
+ invocationRoot: z.ZodString;
76
+ machineLabel: z.ZodString;
77
+ publicKey: z.ZodBase64;
78
+ privateKey: z.ZodBase64;
79
+ agents: z.ZodArray<z.ZodObject<{
80
+ localAgentId: z.ZodString;
81
+ databasePath: z.ZodString;
82
+ name: z.ZodString;
83
+ }, z.core.$strict>>;
84
+ state: z.ZodLiteral<"pending">;
85
+ }, z.core.$strict>, z.ZodObject<{
86
+ requestId: z.ZodString;
87
+ target: z.ZodDiscriminatedUnion<[z.ZodObject<{
88
+ kind: z.ZodLiteral<"oauth">;
89
+ teamId: z.ZodString;
90
+ }, z.core.$strict>, z.ZodObject<{
91
+ kind: z.ZodLiteral<"oyster">;
92
+ }, z.core.$strict>], "kind">;
93
+ invocationRoot: z.ZodString;
94
+ machineLabel: z.ZodString;
95
+ publicKey: z.ZodBase64;
96
+ privateKey: z.ZodBase64;
97
+ agents: z.ZodArray<z.ZodObject<{
98
+ localAgentId: z.ZodString;
99
+ databasePath: z.ZodString;
100
+ name: z.ZodString;
101
+ }, z.core.$strict>>;
102
+ state: z.ZodLiteral<"enrolled">;
103
+ scopeId: z.ZodString;
104
+ deviceId: z.ZodString;
105
+ bindings: z.ZodArray<z.ZodObject<{
106
+ selection: z.ZodNumber;
107
+ agentId: z.ZodString;
108
+ }, z.core.$strict>>;
109
+ }, z.core.$strict>], "state">>;
110
+ }, z.core.$strict>;
111
+ export type MachineEnrollmentRecord = z.infer<typeof machineEnrollmentRecordSchema>;
112
+ /** The authority kind a record's enrollments serve; none for an empty record. */
113
+ export declare function machineEnrollmentRecordKind(record: MachineEnrollmentRecord): MachineEnrollmentAuthority["kind"] | undefined;
114
+ /** The existing credential owner lends its one cross-process transaction. */
115
+ export type LocalMachineEnrollmentAccount = Readonly<{
116
+ read: () => Promise<(MachineEnrollmentAuthority & {
117
+ enrollments: LocalMachineEnrollment[];
118
+ })[]>;
119
+ update: (authority: MachineEnrollmentAuthority, edit: (current: readonly LocalMachineEnrollment[]) => LocalMachineEnrollment[]) => Promise<void>;
120
+ }>;
@@ -0,0 +1,55 @@
1
+ import { z } from "zod";
2
+ const id = z.string().min(1).max(256);
3
+ const selected = z.strictObject({
4
+ localAgentId: id,
5
+ databasePath: z.string().min(1).max(4096),
6
+ name: id,
7
+ });
8
+ export const machineBindingSchema = z.strictObject({
9
+ selection: z.number().int().min(0).max(255),
10
+ agentId: id,
11
+ });
12
+ const common = z.strictObject({
13
+ requestId: id,
14
+ target: z.discriminatedUnion("kind", [
15
+ z.strictObject({ kind: z.literal("oauth"), teamId: id }),
16
+ z.strictObject({ kind: z.literal("oyster") }),
17
+ ]),
18
+ invocationRoot: z.string().min(1).max(4096),
19
+ machineLabel: id,
20
+ publicKey: z.base64().length(44),
21
+ privateKey: z.base64().min(1).max(512),
22
+ agents: z.array(selected).max(256),
23
+ });
24
+ /** Private credential-file state; only public key, label and names go to Hub. */
25
+ export const localMachineEnrollmentSchema = z.discriminatedUnion("state", [
26
+ common.extend({ state: z.literal("pending") }),
27
+ common.extend({
28
+ state: z.literal("enrolled"),
29
+ scopeId: id,
30
+ deviceId: id,
31
+ bindings: z.array(machineBindingSchema).max(256),
32
+ }),
33
+ ]);
34
+ const origin = z.url().refine((value) => new URL(value).origin === value);
35
+ export const machineEnrollmentAuthoritySchema = z.discriminatedUnion("kind", [
36
+ z.strictObject({ kind: z.literal("oauth"), origin }),
37
+ z.strictObject({ kind: z.literal("oyster"), origin }),
38
+ ]);
39
+ /**
40
+ * One authority's device enrollments, keyed in the credential file by origin:
41
+ * a standalone Oyster's, or a Fungi issuer's partition. Every enrollment in a
42
+ * record names the same authority kind. Never a Pi credential or a bearer.
43
+ */
44
+ export const machineEnrollmentRecordSchema = z.strictObject({
45
+ type: z.literal("machine_enrollment"),
46
+ origin,
47
+ enrollments: z
48
+ .array(localMachineEnrollmentSchema)
49
+ .max(256)
50
+ .refine((records) => records.every((record) => record.target.kind === records[0]?.target.kind)),
51
+ });
52
+ /** The authority kind a record's enrollments serve; none for an empty record. */
53
+ export function machineEnrollmentRecordKind(record) {
54
+ return record.enrollments[0]?.target.kind;
55
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A process-start discriminator. `unverifiable` means no sufficiently precise
3
+ * reader is implemented on this platform.
4
+ */
5
+ export type ProcessStart = Readonly<{
6
+ kind: "running";
7
+ start: string;
8
+ }> | Readonly<{
9
+ kind: "gone";
10
+ }> | Readonly<{
11
+ kind: "unverifiable";
12
+ }>;
13
+ /**
14
+ * Linux reads proc(5)'s `starttime` (field 22 of `/proc/<pid>/stat`, in clock
15
+ * ticks since boot). macOS's ps lstart is only second-resolution and is not
16
+ * accepted as signal authority. No sub-second macOS reader is implemented.
17
+ */
18
+ export declare function readProcessStart(pid: number): Promise<ProcessStart>;
19
+ /** The owner's start discriminator; an unverifiable marker never permits signalling. */
20
+ export declare function readOwnProcessStart(): Promise<string>;
@@ -0,0 +1,40 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { z } from "zod";
3
+ const procStartSchema = z.string().regex(/^\d+$/u);
4
+ /**
5
+ * Linux reads proc(5)'s `starttime` (field 22 of `/proc/<pid>/stat`, in clock
6
+ * ticks since boot). macOS's ps lstart is only second-resolution and is not
7
+ * accepted as signal authority. No sub-second macOS reader is implemented.
8
+ */
9
+ export async function readProcessStart(pid) {
10
+ if (process.platform === "linux") {
11
+ let stat;
12
+ try {
13
+ stat = await readFile(`/proc/${String(pid)}/stat`, "utf8");
14
+ }
15
+ catch (cause) {
16
+ // The process may exit after procfs opened stat but before it was read.
17
+ if (cause instanceof Error &&
18
+ "code" in cause &&
19
+ (cause.code === "ENOENT" || cause.code === "ESRCH"))
20
+ return { kind: "gone" };
21
+ throw cause;
22
+ }
23
+ // Field 2 is the parenthesised command name, which may itself contain
24
+ // spaces or parentheses; the fields after its last `)` are plain.
25
+ const start = procStartSchema.safeParse(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[19]);
26
+ return start.success
27
+ ? { kind: "running", start: `linux:${start.data}` }
28
+ : { kind: "unverifiable" };
29
+ }
30
+ return { kind: "unverifiable" };
31
+ }
32
+ /** The owner's start discriminator; an unverifiable marker never permits signalling. */
33
+ export async function readOwnProcessStart() {
34
+ const own = await readProcessStart(process.pid);
35
+ if (own.kind === "unverifiable")
36
+ return "unverifiable";
37
+ if (own.kind === "gone")
38
+ throw new Error("The Fungi service cannot read its own process start time.");
39
+ return own.start;
40
+ }
@@ -0,0 +1,192 @@
1
+ import type { OutstandingDelegation } from "@fungi.computer/shiitake/fleet";
2
+ import { createMcpFormSession } from "./private-mcp-form-session.js";
3
+ import type { LocalUiAttachment } from "./private-local-ui-attachment.js";
4
+ import { type LocalAgentExtensions } from "./local-extensions.js";
5
+ import { type Models, type ThinkingLevel } from "@earendil-works/pi-ai";
6
+ import type { CapabilitySource } from "@fungi.computer/mycelium";
7
+ import type { BirdDogMail } from "@fungi.computer/bird-dog";
8
+ import type { Accepted, CommandSettlement } from "@fungi.computer/shiitake";
9
+ import { type ModelPreference } from "@fungi.computer/shiitake/model-preference";
10
+ import { type AgentControlDirectory } from "@fungi.computer/shiitake/agent-control";
11
+ import type { ExtensionRosterV0 } from "@fungi.computer/shiitake/extensions";
12
+ import { type InstructionSource, type ModelSource, Shiitake, type RuntimeHost, type ShiitakeAgent, type ShiitakeSession, type ShiitakeOptions } from "@fungi.computer/shiitake";
13
+ import { type PrivateLocalConfigRoot } from "./private-local-config-root.js";
14
+ import { type PrivateLocalEffectiveModel } from "./private-local-effective-model.js";
15
+ import { type PrivateLocalComputerProcesses } from "./private-local-computer-processes.js";
16
+ import { type LocalSandboxControls, type LocalSandboxStatus } from "./private-local-sandbox.js";
17
+ import { type NodeRuntimeHost, type NodeStoreLease } from "@fungi.computer/shiitake/node";
18
+ import { createPrivateInvocationFilesPort, type LocalSandboxWrites } from "./private-invocation-files.js";
19
+ import { type PrivateFungiMcpConnectionOptions } from "./private-fungi-mcp-connection.js";
20
+ import { type PrivateSqliteDatabase } from "./private-sqlite-owner.js";
21
+ import { type PrivateLocalMailRouting } from "./private-local-delegation.js";
22
+ import { type LocalAgentControlDirectory } from "./private-local-agent-control.js";
23
+ import { type SubagentSource } from "@fungi.computer/shiitake-extensions/subagents/host";
24
+ declare const localHostIds: Readonly<{
25
+ readonly store: import("@fungi.computer/cairn").IdCodec<"local-host", "store", 1>;
26
+ readonly "session-create-request": import("@fungi.computer/cairn").IdCodec<"local-host", "session-create-request", 1>;
27
+ }>;
28
+ export type LocalStoreId = ReturnType<typeof localHostIds.store.generate>;
29
+ export type LocalResources = Readonly<{
30
+ agent?: Pick<ShiitakeAgent, "close"> | undefined;
31
+ processes?: Pick<PrivateLocalComputerProcesses, "close"> | undefined;
32
+ sandbox?: Pick<LocalSandboxControls, "close"> | undefined;
33
+ runtime: Omit<NodeRuntimeHost, "scheduleWake">;
34
+ database?: Pick<PrivateSqliteDatabase, "close"> | undefined;
35
+ lease?: NodeStoreLease | undefined;
36
+ providerCacheKey?: string | undefined;
37
+ }>;
38
+ export type PrivateLocalTools = Readonly<{
39
+ bindSession: ReturnType<typeof createMcpFormSession>["bindSession"];
40
+ presentApproval: ReturnType<typeof createMcpFormSession>["presentApproval"];
41
+ configure: (installed: LocalAgentExtensions, fungiMcp: PrivateFungiMcpConnectionOptions | undefined, clientPath?: string) => Pick<ShiitakeOptions, "extensions" | "tools">;
42
+ port: Awaited<ReturnType<typeof createPrivateInvocationFilesPort>>;
43
+ tools: NonNullable<ShiitakeOptions["tools"]>;
44
+ extensions: ExtensionRosterV0;
45
+ /** Background Computer processes, scoped per Session; the host closes them. */
46
+ processes: PrivateLocalComputerProcesses;
47
+ }>;
48
+ /** Inputs for the one private local Agent opener. */
49
+ export type PrivateLocalRuntimeHostOptions = Readonly<{
50
+ /** Service-selected host configuration, independent of this Agent's Files root. */
51
+ configRoot?: PrivateLocalConfigRoot;
52
+ configuration?: (storeDirectory: string) => Promise<LocalRuntimeConfiguration>;
53
+ /** Invocation root captured before selecting a fresh local database. */
54
+ invocationRoot: string;
55
+ /** Existing Agent database to reopen instead of creating a fresh local store. */
56
+ databasePath?: string | undefined;
57
+ /** Stable Local store identity for replaying one Agent create request. */
58
+ createRequestId?: string | undefined;
59
+ /** Display name chosen when this Agent is created; the id names it otherwise. */
60
+ name?: string | undefined;
61
+ /** The Agent that created this one; recorded once with its identity. Absent when a human made it. */
62
+ createdBy?: string | undefined;
63
+ /** Immutable project-relative folder authority, inherited by child Agents. */
64
+ folder?: string | undefined;
65
+ /** Persist before constructing the Agent, which can recover durable work. */
66
+ requireSandbox?: boolean | undefined;
67
+ /** Explicit Agent model; otherwise the Agent's stored default, then the available login. */
68
+ providerId?: string | undefined;
69
+ modelId?: string | undefined;
70
+ reasoning?: ThinkingLevel | undefined;
71
+ /**
72
+ * Service-owned project mail routing. With `agentControl`, it gives this
73
+ * Agent follow-up delegation with authenticated replies and settles its own
74
+ * addressed work back to the sender.
75
+ */
76
+ mail?: PrivateLocalMailRouting | undefined;
77
+ /** Service-owned directory scoped to the captured project. */
78
+ agentControl?: LocalAgentControlDirectory | undefined;
79
+ /** Service-owned Team context for this store; never a caller-supplied alias. */
80
+ meshAdmission?: (localAgentId: string, recipientAgentId: string) => Promise<readonly {
81
+ agentId: string;
82
+ name: string;
83
+ }[] | undefined>;
84
+ /** Explicit hosted capability connection; local tools and the Agent store stay local. */
85
+ fungiMcp?: PrivateFungiMcpConnectionOptions | undefined;
86
+ }>;
87
+ /** Typed Session and awaited resource owner supplied directly to the local TUI. */
88
+ export type PrivateLocalRuntimeHost = Readonly<{
89
+ /** Native host-only form/record binding; never a Session or model command. */
90
+ presentApproval: PrivateLocalTools["presentApproval"];
91
+ prepareReload: (configuration: LocalRuntimeConfiguration) => ReturnType<Awaited<ReturnType<typeof Shiitake.make>>["prepareConfiguration"]>;
92
+ /** Stable Local directory identity of this authorized Agent. */
93
+ agentId: string;
94
+ /** The Agent's stored display name; its id for an Agent stored before names. */
95
+ name: string;
96
+ /** Canonical file path to pass explicitly to a later --db launch. */
97
+ databasePath: string;
98
+ session: ShiitakeSession;
99
+ /** Address a Session through this same Agent and resource owner. */
100
+ getSession: (sessionId: string, attachment?: LocalUiAttachment) => ShiitakeSession;
101
+ /** A selected Session keeps one client's command PATH while this host is live. */
102
+ bindSessionPath: (sessionId: string, path: string) => boolean;
103
+ /** Resolve this Session's effective model and nonsecret credential source. */
104
+ effectiveModel: (sessionId: string, signal: AbortSignal) => Promise<PrivateLocalEffectiveModel>;
105
+ /**
106
+ * Hide this Agent from lists, pickers and delegation, or restore it. Only the
107
+ * flag changes; the store, its Sessions and files stay.
108
+ */
109
+ setArchived: (archived: boolean) => void;
110
+ /** Persist the requirement before another affected launch is admitted. */
111
+ setRequireSandbox: (required: boolean) => void;
112
+ sandboxStatus: () => LocalSandboxStatus;
113
+ /** Set the persistent Agent default, thinking level included, without changing Session overrides. */
114
+ setModel: (model: ModelPreference, signal: AbortSignal) => Promise<void>;
115
+ /** Refuse unrunnable creation or send before claiming acceptance. */
116
+ assertModelReady: (sessionId: string, signal: AbortSignal) => Promise<void>;
117
+ listSessions: ShiitakeAgent["listSessions"];
118
+ createSession: ShiitakeAgent["createSession"];
119
+ deleteSession: ShiitakeAgent["deleteSession"];
120
+ /** List, resume and call this Agent's rostered extensions. */
121
+ extensions: ShiitakeAgent["extensions"];
122
+ /**
123
+ * Admit mail addressed to this Agent: new work that replies to its sender,
124
+ * or a reply authenticated against work this Agent sent. Anything else is
125
+ * refused.
126
+ */
127
+ acceptMail: (mail: BirdDogMail) => Promise<Accepted>;
128
+ /** Delegated work this Agent still waits on, as its target Session and command; undefined when none. */
129
+ pendingDelegation: () => Promise<OutstandingDelegation | undefined>;
130
+ /** Exact settled command facts for a delegating sender. */
131
+ readSettlement: (sessionId: string, commandId: string) => Promise<CommandSettlement | undefined>;
132
+ /**
133
+ * Whether this Agent holds nothing a later reopen could not resume: no
134
+ * running or waiting Session, Watchdog job, scheduled wake, reply still
135
+ * owed a delivery attempt or running background process. Work it delegated
136
+ * is owed by the child, whose reply mail reopens this Agent.
137
+ */
138
+ idle: () => Promise<boolean>;
139
+ close: () => Promise<void>;
140
+ }>;
141
+ export type LocalRuntimeConfiguration = Readonly<{
142
+ installed: LocalAgentExtensions;
143
+ fungiMcp: PrivateFungiMcpConnectionOptions | undefined;
144
+ clientPath: string;
145
+ }>;
146
+ /** Local-owned id for a new Session creation request. */
147
+ export declare function createPrivateLocalSessionRequestId(): import("@fungi.computer/cairn").CairnId<"local-host", "session-create-request", 1>;
148
+ export type PrivateLocalCreateStore = Readonly<{
149
+ agentId: LocalStoreId;
150
+ databasePath: string;
151
+ }>;
152
+ /** Resolve an Agent create request through its durable Local receipt. */
153
+ export declare function resolvePrivateLocalCreateStore(root: string, requestId: string): Promise<PrivateLocalCreateStore>;
154
+ /** Build the fixed source read by Shiitake once for each local run. */
155
+ export declare function createPrivateLocalInstructionSource(port: Awaited<ReturnType<typeof createPrivateInvocationFilesPort>>): InstructionSource;
156
+ export declare function createPrivateLocalModelSource(models: Models, providerId: string | undefined, modelId: string | undefined, providerCacheKey: string, selectedOwnerReasoning?: ThinkingLevel, defaults?: Readonly<{
157
+ agent?: () => ModelPreference | null;
158
+ }>): ModelSource & Readonly<{
159
+ /** The secret-free effective selection the footer and /model show. */
160
+ effective: (preference: ModelPreference | null, signal: AbortSignal) => Promise<PrivateLocalEffectiveModel>;
161
+ assertReady: (preference: ModelPreference | null, signal: AbortSignal) => Promise<void>;
162
+ }>;
163
+ /** Assemble the existing Files, Computer, and workerd owners once per host. */
164
+ export declare function createPrivateLocalTools(invocationRoot: string, fungiMcp?: PrivateFungiMcpConnectionOptions, agentControl?: AgentControlDirectory, sessionPath?: (sessionId: string) => string | undefined,
165
+ /** This Agent's store; its owner-enabled installed extensions join the roster. */
166
+ storeDirectory?: string,
167
+ /**
168
+ * This Agent's sub-agent record: its Session-bound Agent-control tools and
169
+ * the view's facts. Present when it can direct other Agents.
170
+ */
171
+ subagents?: Readonly<{
172
+ control: CapabilitySource;
173
+ source: SubagentSource;
174
+ }>, configuration?: LocalRuntimeConfiguration, configRoot?: PrivateLocalConfigRoot, sandbox?: LocalSandboxWrites): Promise<PrivateLocalTools>;
175
+ /** Event-loop adaptation for the one local Shiitake owner; it has no queue policy. */
176
+ export declare function createPrivateLocalRuntimeHost(): RuntimeHost;
177
+ /**
178
+ * Close the fixed local owner in dependency order without surrendering its
179
+ * sidecar before runtime drain and main-database close have succeeded.
180
+ *
181
+ * This source-local seam is intentionally not re-exported by the package; the
182
+ * focused tests use it to falsify injected failure ordering.
183
+ */
184
+ export declare function closePrivateLocalResources(resources: LocalResources, initiatingFailure?: Error): Promise<void>;
185
+ /**
186
+ * Open one private file-backed local Shiitake host and its fixed Session.
187
+ *
188
+ * The returned close owns agent, main database, and sidecar release in that
189
+ * order. It is the only lifecycle the current terminal caller needs to learn.
190
+ */
191
+ export declare function openPrivateLocalRuntimeHost(input: PrivateLocalRuntimeHostOptions, suppliedModels?: Models): Promise<PrivateLocalRuntimeHost>;
192
+ export {};