@fungi.computer/truffle 0.1.1 → 0.2.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.
Files changed (174) hide show
  1. package/README.md +282 -34
  2. package/dist/auth-cli.d.ts +10 -0
  3. package/dist/auth-cli.js +36 -14
  4. package/dist/fungi-account-command.d.ts +1 -1
  5. package/dist/fungi-account-command.js +6 -3
  6. package/dist/index.d.ts +10 -2
  7. package/dist/index.js +8 -2
  8. package/dist/internal/image-fit-metafile.json +106 -0
  9. package/dist/internal/local-image-fit-worker.mjs +960 -0
  10. package/dist/internal/private-workerd-sandbox-worker.mjs +11 -12
  11. package/dist/internal/sandbox-metafile.json +7 -7
  12. package/dist/local-archive-handoff.d.ts +12 -0
  13. package/dist/local-archive-handoff.js +21 -0
  14. package/dist/local-extensions.d.ts +58 -12
  15. package/dist/local-extensions.js +195 -55
  16. package/dist/local-image-fit.d.ts +30 -0
  17. package/dist/local-image-fit.js +105 -0
  18. package/dist/local-models.d.ts +4 -2
  19. package/dist/local-models.js +11 -6
  20. package/dist/local-session-archives.d.ts +8 -0
  21. package/dist/local-session-archives.js +152 -0
  22. package/dist/local-skill-source.d.ts +18 -3
  23. package/dist/local-skill-source.js +216 -140
  24. package/dist/log-event.d.ts +7 -0
  25. package/dist/log-event.js +8 -4
  26. package/dist/private-bounded-response.d.ts +2 -0
  27. package/dist/private-bounded-response.js +41 -0
  28. package/dist/private-file-credential-store.d.ts +28 -3
  29. package/dist/private-file-credential-store.js +57 -9
  30. package/dist/private-files-patch.d.ts +2 -1
  31. package/dist/private-files-patch.js +21 -3
  32. package/dist/private-fungi-account.d.ts +40 -23
  33. package/dist/private-fungi-account.js +201 -21
  34. package/dist/private-fungi-mcp-connection.d.ts +4 -0
  35. package/dist/private-fungi-mcp-connection.js +25 -6
  36. package/dist/private-fungi-mcp-selection-schema.d.ts +7 -0
  37. package/dist/private-fungi-mcp-selection-schema.js +15 -0
  38. package/dist/private-fungi-mcp-selection.d.ts +6 -6
  39. package/dist/private-fungi-mcp-selection.js +29 -16
  40. package/dist/private-fungi-model-api.js +4 -4
  41. package/dist/private-fungi-model-catalog.d.ts +10 -0
  42. package/dist/private-fungi-model-catalog.js +10 -2
  43. package/dist/private-fungi-model-provider.d.ts +1 -1
  44. package/dist/private-fungi-model-provider.js +6 -1
  45. package/dist/private-fungi-pending-logouts.d.ts +1 -1
  46. package/dist/private-fungi-pending-logouts.js +1 -2
  47. package/dist/private-fungi-service-logout.d.ts +44 -0
  48. package/dist/private-fungi-service-logout.js +59 -0
  49. package/dist/private-invocation-computer.d.ts +24 -3
  50. package/dist/private-invocation-computer.js +101 -62
  51. package/dist/private-invocation-files.d.ts +16 -1
  52. package/dist/private-invocation-files.js +154 -83
  53. package/dist/private-local-acp-stdio.js +3 -0
  54. package/dist/private-local-admission-gate.d.ts +14 -0
  55. package/dist/private-local-admission-gate.js +52 -0
  56. package/dist/private-local-agent-control.d.ts +101 -0
  57. package/dist/private-local-agent-control.js +30 -0
  58. package/dist/private-local-agent-default-model.d.ts +12 -0
  59. package/dist/private-local-agent-default-model.js +31 -0
  60. package/dist/private-local-auth-rpc.d.ts +67 -7
  61. package/dist/private-local-auth-rpc.js +140 -17
  62. package/dist/private-local-bwrap.d.ts +13 -0
  63. package/dist/private-local-bwrap.js +70 -0
  64. package/dist/private-local-client-version.d.ts +5 -0
  65. package/dist/private-local-client-version.js +43 -0
  66. package/dist/private-local-computer-attach.d.ts +3 -3
  67. package/dist/private-local-computer-attach.js +17 -3
  68. package/dist/private-local-computer-processes.d.ts +6 -3
  69. package/dist/private-local-computer-processes.js +55 -31
  70. package/dist/private-local-config-root.d.ts +21 -0
  71. package/dist/private-local-config-root.js +19 -0
  72. package/dist/private-local-containment.d.ts +51 -0
  73. package/dist/private-local-containment.js +161 -0
  74. package/dist/private-local-delegation.d.ts +10 -3
  75. package/dist/private-local-delegation.js +70 -15
  76. package/dist/private-local-effective-model.d.ts +9 -1
  77. package/dist/private-local-effective-model.js +3 -0
  78. package/dist/private-local-exec-environment.d.ts +16 -0
  79. package/dist/private-local-exec-environment.js +11 -0
  80. package/dist/private-local-linux-sandbox.d.ts +8 -0
  81. package/dist/private-local-linux-sandbox.js +67 -0
  82. package/dist/private-local-mail.d.ts +4 -2
  83. package/dist/private-local-mail.js +41 -33
  84. package/dist/private-local-mesh-enrollment.d.ts +34 -0
  85. package/dist/private-local-mesh-enrollment.js +188 -0
  86. package/dist/private-local-mesh-links.d.ts +21 -0
  87. package/dist/private-local-mesh-links.js +229 -0
  88. package/dist/private-local-mesh-record.d.ts +120 -0
  89. package/dist/private-local-mesh-record.js +55 -0
  90. package/dist/private-local-process-identity.d.ts +20 -0
  91. package/dist/private-local-process-identity.js +40 -0
  92. package/dist/private-local-runtime-host.d.ts +64 -13
  93. package/dist/private-local-runtime-host.js +361 -172
  94. package/dist/private-local-sandbox-policy.d.ts +21 -0
  95. package/dist/private-local-sandbox-policy.js +121 -0
  96. package/dist/private-local-sandbox.d.ts +37 -0
  97. package/dist/private-local-sandbox.js +114 -0
  98. package/dist/private-local-seatbelt.d.ts +24 -0
  99. package/dist/private-local-seatbelt.js +68 -0
  100. package/dist/private-local-service-contract.d.ts +91 -11
  101. package/dist/private-local-service-discovery.d.ts +129 -13
  102. package/dist/private-local-service-discovery.js +148 -47
  103. package/dist/private-local-service-lease.d.ts +16 -0
  104. package/dist/private-local-service-lease.js +105 -0
  105. package/dist/private-local-service-listener.d.ts +9 -2
  106. package/dist/private-local-service-listener.js +260 -64
  107. package/dist/private-local-service-log.d.ts +128 -0
  108. package/dist/private-local-service-log.js +321 -0
  109. package/dist/private-local-service-owner.d.ts +87 -1519
  110. package/dist/private-local-service-owner.js +1069 -71
  111. package/dist/private-local-service-process.js +3 -1
  112. package/dist/private-local-service-termination.d.ts +7 -0
  113. package/dist/private-local-service-termination.js +67 -0
  114. package/dist/private-local-service.d.ts +31 -2
  115. package/dist/private-local-service.js +402 -126
  116. package/dist/private-local-session-discovery.d.ts +52 -5
  117. package/dist/private-local-session-discovery.js +132 -11
  118. package/dist/private-local-shiitake-host.js +2 -2
  119. package/dist/private-local-startup-model.d.ts +51 -0
  120. package/dist/private-local-startup-model.js +86 -0
  121. package/dist/private-local-stop-all.d.ts +27 -0
  122. package/dist/private-local-stop-all.js +26 -0
  123. package/dist/private-local-ui-attachment.d.ts +19 -0
  124. package/dist/private-local-ui-attachment.js +71 -0
  125. package/dist/private-local-worker-server.d.ts +1 -0
  126. package/dist/private-local-worker-server.js +13 -5
  127. package/dist/private-mcp-form-contract.d.ts +186 -0
  128. package/dist/private-mcp-form-contract.js +53 -0
  129. package/dist/private-mcp-form-elicitation.d.ts +18 -0
  130. package/dist/private-mcp-form-elicitation.js +97 -0
  131. package/dist/private-mcp-form-session.d.ts +172 -0
  132. package/dist/private-mcp-form-session.js +148 -0
  133. package/dist/private-openai-claims.d.ts +30 -0
  134. package/dist/private-openai-claims.js +69 -0
  135. package/dist/private-oyster-mcp-auth.d.ts +13 -0
  136. package/dist/private-oyster-mcp-auth.js +57 -0
  137. package/dist/private-provider-rejection.d.ts +15 -0
  138. package/dist/private-provider-rejection.js +24 -0
  139. package/dist/private-sqlite-owner.d.ts +34 -2
  140. package/dist/private-sqlite-owner.js +103 -37
  141. package/dist/private-team-computers.d.ts +4 -4
  142. package/dist/private-team-computers.js +11 -2
  143. package/dist/seatbelt/safe-base.sbpl +79 -0
  144. package/dist/subscription-activation.d.ts +9 -0
  145. package/dist/subscription-activation.js +62 -0
  146. package/dist/subscription-identity.d.ts +23 -0
  147. package/dist/subscription-identity.js +124 -0
  148. package/dist/subscription-turn.d.ts +17 -0
  149. package/dist/subscription-turn.js +143 -0
  150. package/dist/team-apps.d.ts +2 -2
  151. package/dist/team-apps.js +7 -2
  152. package/native/bin/LICENSE.bubblewrap +502 -0
  153. package/native/bin/LICENSE.libcap +112 -0
  154. package/native/bin/LICENSE.sandbox-runtime +201 -0
  155. package/native/bin/SOURCE.bubblewrap.tar.xz +0 -0
  156. package/native/bin/bwrap-build-source/Dockerfile +48 -0
  157. package/native/bin/bwrap-build-source/README.md +96 -0
  158. package/native/bin/bwrap-build-source/build.mjs +52 -0
  159. package/native/bin/bwrap-build-source/linux-arm64.ini +14 -0
  160. package/native/bin/bwrap-build-source/pin.mjs +21 -0
  161. package/native/bin/bwrap-build-source/record.mjs +74 -0
  162. package/native/bin/bwrap-build-source/vendor/LICENSE.sandbox-runtime +201 -0
  163. package/native/bin/bwrap-build-source/vendor/README.md +23 -0
  164. package/native/bin/bwrap-build-source/vendor/seccomp-unix-block.c +280 -0
  165. package/native/bin/bwrap-build.json +44 -0
  166. package/native/bin/fungi-bwrap +0 -0
  167. package/native/bin/fungi-bwrap.sha256 +1 -0
  168. package/native/bin/fungi-seccomp.bpf +0 -0
  169. package/native/bin/fungi-seccomp.bpf.sha256 +1 -0
  170. package/package.json +44 -31
  171. package/dist/private-local-model-settings.d.ts +0 -5
  172. package/dist/private-local-model-settings.js +0 -31
  173. package/dist/private-local-subagents.d.ts +0 -27
  174. package/dist/private-local-subagents.js +0 -77
@@ -4,6 +4,7 @@ import { Client, StreamableHTTPClientTransport, UnauthorizedError, } from "@mode
4
4
  import { Mycelium } from "@fungi.computer/mycelium";
5
5
  import { z } from "zod";
6
6
  import { parseFungiMcpEndpoint } from "./private-fungi-mcp-endpoint.js";
7
+ import { registerMcpFormElicitation, McpFormApprovalError, } from "./private-mcp-form-elicitation.js";
7
8
  const localPackage = z
8
9
  .looseObject({ version: z.string().min(1) })
9
10
  .parse(createRequire(import.meta.url)("../package.json"));
@@ -31,8 +32,11 @@ export function rejectedFungiMcpAccess(response) {
31
32
  }
32
33
  export function fungiMcpReachableFetch(endpoint, fetchImpl = fetch) {
33
34
  return async (url, init) => {
35
+ const target = new URL(url instanceof Request ? url.url : String(url));
36
+ if (target.origin !== endpoint.origin)
37
+ throw new Error("Refusing an MCP request outside the selected origin");
34
38
  try {
35
- const response = await fetchImpl(url, init);
39
+ const response = await fetchImpl(url, { ...init, redirect: "error" });
36
40
  const authorization = new Headers(init?.headers).get("Authorization");
37
41
  if (response.status === 401 && authorization?.startsWith("Bearer "))
38
42
  rejectedAccess.set(response, authorization.slice(7));
@@ -55,9 +59,10 @@ export function createPrivateFungiMcpConnection(options) {
55
59
  const fetch = fungiMcpReachableFetch(endpoint, options.fetch);
56
60
  return {
57
61
  id: "fungi-mcp",
58
- acquire: async ({ signal }) => {
62
+ acquire: async ({ signal, session }) => {
59
63
  signal.throwIfAborted();
60
64
  const client = new Client(fungiMcpClientInfo);
65
+ const forms = registerMcpFormElicitation(client, options.requestApproval, session?.sessionId);
61
66
  const transportOptions = {
62
67
  authProvider: options.authProvider,
63
68
  fetch,
@@ -84,9 +89,18 @@ export function createPrivateFungiMcpConnection(options) {
84
89
  try {
85
90
  return z
86
91
  .json()
87
- .parse(await client.callTool({ name: "execute", arguments: input }, { signal: context.signal }));
92
+ .parse(await forms.run(context.signal, (signal) => client.callTool({ name: "execute", arguments: input }, { signal })));
88
93
  }
89
94
  catch (cause) {
95
+ if (cause instanceof McpFormApprovalError)
96
+ return {
97
+ isError: true,
98
+ content: [{ type: "text", text: cause.message }],
99
+ structuredContent: {
100
+ kind: cause.kind,
101
+ reason: cause.reason,
102
+ },
103
+ };
90
104
  // Report uncertainty as a failed MCP result so the caller
91
105
  // can distinguish it from a successful remote result.
92
106
  // Replaying arbitrary JavaScript here could duplicate work.
@@ -127,13 +141,18 @@ export function createPrivateFungiMcpConnection(options) {
127
141
  }),
128
142
  ],
129
143
  },
130
- release: () => client.close(),
144
+ release: () => {
145
+ forms.close();
146
+ return client.close();
147
+ },
131
148
  };
132
149
  }
133
150
  catch (error) {
151
+ forms.close();
134
152
  await client.close();
135
- if (error instanceof UnauthorizedError ||
136
- error instanceof FungiSignInRequiredError ||
153
+ if (error instanceof UnauthorizedError)
154
+ throw error;
155
+ if (error instanceof FungiSignInRequiredError ||
137
156
  z.object({ status: z.literal(401) }).safeParse(error).success)
138
157
  throw new Error(`Fungi MCP sign-in is missing, expired or revoked; run fungi login --fungi-mcp ${endpoint.href}`, { cause: error });
139
158
  throw error;
@@ -0,0 +1,7 @@
1
+ import { z } from "zod";
2
+ /** A project selects a target and auth kind; credential material stays with Local. */
3
+ export declare const fungiMcpSelectionSchema: z.ZodObject<{
4
+ endpoint: z.ZodPipe<z.ZodURL, z.ZodString>;
5
+ authentication: z.ZodOptional<z.ZodLiteral<"oyster-key">>;
6
+ }, z.core.$strict>;
7
+ export type PrivateFungiMcpSelection = z.infer<typeof fungiMcpSelectionSchema>;
@@ -0,0 +1,15 @@
1
+ import { z } from "zod";
2
+ import { parseFungiMcpEndpoint } from "./private-fungi-mcp-endpoint.js";
3
+ /** A project selects a target and auth kind; credential material stays with Local. */
4
+ export const fungiMcpSelectionSchema = z.strictObject({
5
+ endpoint: z.url().pipe(z.string().refine((value) => {
6
+ try {
7
+ parseFungiMcpEndpoint(value);
8
+ return true;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }, "Fungi MCP requires HTTPS (or HTTP on an exact loopback host) without credentials, query or fragment")),
14
+ authentication: z.literal("oyster-key").optional(),
15
+ });
@@ -1,10 +1,10 @@
1
- import { z } from "zod";
2
1
  import type { PrivateFungiMcpConnectionOptions } from "./private-fungi-mcp-connection.js";
3
- /** A Team MCP address selects a target; OAuth identity stays with Local. */
4
- export declare const fungiMcpSelectionSchema: z.ZodObject<{
5
- endpoint: z.ZodPipe<z.ZodURL, z.ZodString>;
6
- }, z.core.$strict>;
7
- export type PrivateFungiMcpSelection = z.infer<typeof fungiMcpSelectionSchema>;
2
+ import { type PrivateFungiMcpSelection } from "./private-fungi-mcp-selection-schema.js";
3
+ /** The service validates saved project selections with the RPC selection schema. */
4
+ export declare function readProjectFungiMcpSelection(root: string): Promise<{
5
+ endpoint: string;
6
+ authentication?: "oyster-key" | undefined;
7
+ } | undefined>;
8
8
  export declare function readFungiMcpSelection(selection: PrivateFungiMcpSelection, options?: {
9
9
  credentialPath?: string;
10
10
  }): PrivateFungiMcpConnectionOptions;
@@ -1,18 +1,27 @@
1
- import { z } from "zod";
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
2
3
  import { fungiMcpAccountAuth } from "./private-fungi-account.js";
3
- import { parseFungiMcpEndpoint } from "./private-fungi-mcp-endpoint.js";
4
- /** A Team MCP address selects a target; OAuth identity stays with Local. */
5
- export const fungiMcpSelectionSchema = z.strictObject({
6
- endpoint: z.url().pipe(z.string().refine((value) => {
7
- try {
8
- parseFungiMcpEndpoint(value);
9
- return true;
10
- }
11
- catch {
12
- return false;
13
- }
14
- }, "Fungi MCP requires HTTPS (or HTTP on an exact loopback host) without credentials, query or fragment")),
15
- });
4
+ import { oysterMcpKeyAuth } from "./private-oyster-mcp-auth.js";
5
+ import { fungiMcpSelectionSchema, } from "./private-fungi-mcp-selection-schema.js";
6
+ /** The service validates saved project selections with the RPC selection schema. */
7
+ export async function readProjectFungiMcpSelection(root) {
8
+ const path = join(root, ".fungi", "team-mcp.json");
9
+ let text;
10
+ try {
11
+ text = await readFile(path, "utf8");
12
+ }
13
+ catch (cause) {
14
+ if (cause instanceof Error && "code" in cause && cause.code === "ENOENT")
15
+ return undefined;
16
+ throw cause;
17
+ }
18
+ try {
19
+ return fungiMcpSelectionSchema.parse(JSON.parse(text));
20
+ }
21
+ catch (cause) {
22
+ throw new Error(`${path}: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
23
+ }
24
+ }
16
25
  export function readFungiMcpSelection(selection, options = {}) {
17
26
  const parsed = fungiMcpSelectionSchema.safeParse(selection);
18
27
  if (!parsed.success)
@@ -22,11 +31,15 @@ export function readFungiMcpSelection(selection, options = {}) {
22
31
  const endpoint = new URL(parsed.data.endpoint);
23
32
  return {
24
33
  endpoint,
25
- authProvider: fungiMcpAccountAuth(endpoint, options),
34
+ authentication: parsed.data.authentication,
35
+ authProvider: parsed.data.authentication === "oyster-key"
36
+ ? oysterMcpKeyAuth(endpoint, options)
37
+ : fungiMcpAccountAuth(endpoint, options),
26
38
  };
27
39
  }
28
40
  export function sameFungiMcpConnection(left, right) {
29
- return left?.endpoint.href === right.endpoint.href;
41
+ return (left?.endpoint.href === right.endpoint.href &&
42
+ left.authentication === right.authentication);
30
43
  }
31
44
  export class FungiMcpSelectionConflict extends Error {
32
45
  constructor() {
@@ -1,5 +1,6 @@
1
1
  import process from "node:process";
2
2
  import { fungiAccountFetch } from "./private-fungi-account.js";
3
+ import { capturePrivateLocalConfigRoot } from "./private-local-config-root.js";
3
4
  import { FungiSignInRequiredError } from "./private-fungi-auth-error.js";
4
5
  import { hasApi, } from "@earendil-works/pi-ai";
5
6
  import { stream, streamSimple, } from "@earendil-works/pi-ai/api/openai-completions";
@@ -55,15 +56,14 @@ function assertModelEndpoint(base, url) {
55
56
  throw new Error("Fungi refused a model endpoint outside the selected Team");
56
57
  }
57
58
  /** Access tokens can only travel to the selected Team's two inference endpoints. */
58
- export function fungiModelFetch(baseUrl, teamName, fetchImpl = globalThis.fetch, credentialPath) {
59
+ export function fungiModelFetch(baseUrl, teamName, fetchImpl = globalThis.fetch, credentialPath = capturePrivateLocalConfigRoot().credentialPath) {
59
60
  const base = parseFungiMcpEndpoint(baseUrl);
60
61
  if (!/^\/api\/v1\/teams\/[^/]+$/u.test(base.pathname))
61
62
  throw new Error("Fungi model requests require a selected Team");
62
63
  const accountOptions = {
63
64
  fetch: fetchImpl,
65
+ credentialPath,
64
66
  };
65
- if (credentialPath !== undefined)
66
- accountOptions.credentialPath = credentialPath;
67
67
  const accountFetch = fungiAccountFetch(base.origin, accountOptions);
68
68
  const transport = async (input, init) => {
69
69
  if (!fungiModelsEnabled())
@@ -97,7 +97,7 @@ export function fungiModelFetch(baseUrl, teamName, fetchImpl = globalThis.fetch,
97
97
  return Object.assign(transport, { preconnect: fetchImpl.preconnect });
98
98
  }
99
99
  /** Pi owns streaming; this adapter owns Fungi's destination, incident policy and refusals. */
100
- export function createFungiModelApi(resolveTeamName, credentialPath) {
100
+ export function createFungiModelApi(resolveTeamName, credentialPath = capturePrivateLocalConfigRoot().credentialPath) {
101
101
  const fetchFor = (baseUrl, fetchImpl = globalThis.fetch) => Object.assign(async (input, init) => {
102
102
  const teamName = await resolveTeamName(baseUrl);
103
103
  return fungiModelFetch(baseUrl, teamName, fetchImpl, credentialPath)(input, init);
@@ -1,4 +1,14 @@
1
1
  import type { Model } from "@earendil-works/pi-ai";
2
+ import { z } from "zod";
3
+ /** The thinking levels a Local model may offer. */
4
+ export declare const thinkingLevelSchema: z.ZodEnum<{
5
+ minimal: "minimal";
6
+ low: "low";
7
+ medium: "medium";
8
+ high: "high";
9
+ xhigh: "xhigh";
10
+ max: "max";
11
+ }>;
2
12
  export type FungiHostedModel = Model<"openai-completions"> & {
3
13
  default: boolean;
4
14
  };
@@ -1,5 +1,13 @@
1
1
  import { z } from "zod";
2
- const levels = z.enum(["minimal", "low", "medium", "high", "xhigh", "max"]);
2
+ /** The thinking levels a Local model may offer. */
3
+ export const thinkingLevelSchema = z.enum([
4
+ "minimal",
5
+ "low",
6
+ "medium",
7
+ "high",
8
+ "xhigh",
9
+ "max",
10
+ ]);
3
11
  const catalogSchema = z.object({
4
12
  object: z.literal("list"),
5
13
  data: z
@@ -9,7 +17,7 @@ const catalogSchema = z.object({
9
17
  name: z.string().min(1).max(256),
10
18
  context_window: z.number().int().positive(),
11
19
  max_output_tokens: z.number().int().positive(),
12
- reasoning_levels: z.array(levels).max(6),
20
+ reasoning_levels: z.array(thinkingLevelSchema).max(6),
13
21
  input: z.array(z.literal("text")).length(1),
14
22
  default: z.boolean(),
15
23
  cost: z.object({
@@ -1,6 +1,6 @@
1
1
  import { type FetchFunction } from "@earendil-works/pi-ai";
2
2
  /** The selected account Team owns the endpoint; the Hub owns the live model catalog. */
3
- export declare function createFungiModelProvider(options?: {
3
+ export declare function createFungiModelProvider(input?: {
4
4
  credentialPath?: string;
5
5
  fetch?: FetchFunction;
6
6
  }): import("@earendil-works/pi-ai").Provider<"openai-completions">;
@@ -2,9 +2,14 @@ import { z } from "zod";
2
2
  import { createProvider } from "@earendil-works/pi-ai";
3
3
  import { createFungiAccountOAuth, fungiAccountTeam, readFungiAccountStatus, } from "./private-fungi-account.js";
4
4
  import { fungiHostedModels } from "./private-fungi-model-catalog.js";
5
+ import { capturePrivateLocalConfigRoot } from "./private-local-config-root.js";
5
6
  import { assertFungiModelsEnabled, createFungiModelApi, fungiModelFetch, } from "./private-fungi-model-api.js";
6
7
  /** The selected account Team owns the endpoint; the Hub owns the live model catalog. */
7
- export function createFungiModelProvider(options = {}) {
8
+ export function createFungiModelProvider(input = {}) {
9
+ const options = {
10
+ ...input,
11
+ credentialPath: input.credentialPath ?? capturePrivateLocalConfigRoot().credentialPath,
12
+ };
8
13
  const oauth = createFungiAccountOAuth(options);
9
14
  return createProvider({
10
15
  id: "fungi",
@@ -26,7 +26,7 @@ export type LoginIdentity = z.output<typeof identitySchema>;
26
26
  /** Decode only routing metadata. These IDs never authorize a revocation. */
27
27
  export declare function loginIdentity(issuer: string, access: string): LoginIdentity | undefined;
28
28
  /** Durable logout intent, separate from the credential that logout destroys. */
29
- export declare function pendingLogouts(credentialPath?: string): {
29
+ export declare function pendingLogouts(credentialPath: string): {
30
30
  remember(identity: LoginIdentity, detaches: PendingDetach[]): Promise<void>;
31
31
  confirmed(identity: LoginIdentity, detach?: PendingDetach): Promise<void>;
32
32
  forAccount(identity: LoginIdentity): Promise<PendingLogout[]>;
@@ -5,7 +5,6 @@ import lockfile from "proper-lockfile";
5
5
  import writeFileAtomic from "write-file-atomic";
6
6
  import { z } from "zod";
7
7
  import { logEvent } from "./log-event.js";
8
- import { resolveDefaultCredentialPath } from "./private-file-credential-store.js";
9
8
  const identitySchema = z.strictObject({
10
9
  issuer: z.url().max(2048),
11
10
  userId: z.string().min(1).max(256),
@@ -43,7 +42,7 @@ function matches(left, right) {
43
42
  left.loginId === right.loginId);
44
43
  }
45
44
  /** Durable logout intent, separate from the credential that logout destroys. */
46
- export function pendingLogouts(credentialPath = resolveDefaultCredentialPath()) {
45
+ export function pendingLogouts(credentialPath) {
47
46
  const path = `${credentialPath}.pending-logouts.json`;
48
47
  async function update(change) {
49
48
  await mkdir(dirname(path), { recursive: true, mode: 0o700 });
@@ -0,0 +1,44 @@
1
+ import type { FetchLike } from "@modelcontextprotocol/client";
2
+ import { z } from "zod";
3
+ export declare const fungiAccountLogoutRequestSchema: z.ZodObject<{
4
+ credentialPath: z.ZodString;
5
+ endpoint: z.ZodOptional<z.ZodString>;
6
+ }, z.core.$strict>;
7
+ export type FungiAccountLogoutRequest = z.infer<typeof fungiAccountLogoutRequestSchema>;
8
+ export declare const fungiAccountLogoutResultSchema: z.ZodObject<{
9
+ kind: z.ZodEnum<{
10
+ "not-logged-in": "not-logged-in";
11
+ removed: "removed";
12
+ revoked: "revoked";
13
+ }>;
14
+ message: z.ZodString;
15
+ unconfirmedComputers: z.ZodArray<z.ZodObject<{
16
+ name: z.ZodString;
17
+ url: z.ZodString;
18
+ }, z.core.$strict>>;
19
+ }, z.core.$strict>;
20
+ /** A logout outcome: never a remote-revocation claim the issuer didn't confirm. */
21
+ export type FungiAccountLogoutResult = Readonly<{
22
+ kind: "not-logged-in" | "removed" | "revoked";
23
+ message: string;
24
+ unconfirmedComputers: readonly Readonly<{
25
+ name: string;
26
+ url: string;
27
+ }>[];
28
+ }>;
29
+ /**
30
+ * Route a logout to the running Local account owner. It never starts,
31
+ * upgrades, stops or replaces a service. With no service, the caller's own
32
+ * offline clear runs under the ordinary startup lock, so a service cannot
33
+ * start between proving its absence and the store mutation. A live owner
34
+ * that cannot be reached fails closed.
35
+ */
36
+ export declare function signOutFungiThroughLocalService(endpoint: string | URL | undefined, options: {
37
+ credentialPath: string;
38
+ directory: string;
39
+ fetch?: FetchLike;
40
+ clearOffline: (endpoint: string | URL | undefined, input: {
41
+ credentialPath: string;
42
+ fetch?: FetchLike;
43
+ }) => Promise<FungiAccountLogoutResult>;
44
+ }): Promise<FungiAccountLogoutResult>;
@@ -0,0 +1,59 @@
1
+ import { newWebSocketRpcSession } from "@fungi.computer/capnweb";
2
+ import { z } from "zod";
3
+ import { acquireServiceLock, readLiveServiceStatus, readServiceRegistration, serviceDirectory, serviceOwnerIsLive, } from "./private-local-service-discovery.js";
4
+ export const fungiAccountLogoutRequestSchema = z.strictObject({
5
+ credentialPath: z.string().min(1),
6
+ endpoint: z.string().optional(),
7
+ });
8
+ export const fungiAccountLogoutResultSchema = z.strictObject({
9
+ kind: z.enum(["not-logged-in", "removed", "revoked"]),
10
+ message: z.string(),
11
+ unconfirmedComputers: z.array(z.strictObject({ name: z.string(), url: z.string() })),
12
+ });
13
+ /**
14
+ * Route a logout to the running Local account owner. It never starts,
15
+ * upgrades, stops or replaces a service. With no service, the caller's own
16
+ * offline clear runs under the ordinary startup lock, so a service cannot
17
+ * start between proving its absence and the store mutation. A live owner
18
+ * that cannot be reached fails closed.
19
+ */
20
+ export async function signOutFungiThroughLocalService(endpoint, options) {
21
+ const { clearOffline, directory: requested, ...input } = options;
22
+ const directory = await serviceDirectory(requested);
23
+ const release = await acquireServiceLock(directory, "startup");
24
+ try {
25
+ const registration = await readServiceRegistration(directory);
26
+ const owned = await serviceOwnerIsLive(directory);
27
+ if (registration?.kind !== "current") {
28
+ if (owned)
29
+ throw new Error("The running Local account owner is unavailable");
30
+ return await clearOffline(endpoint, input);
31
+ }
32
+ const probe = await readLiveServiceStatus(registration.record);
33
+ if (probe.kind !== "running") {
34
+ if (owned || probe.kind === "unavailable")
35
+ throw new Error("The running Local account owner is unavailable");
36
+ return await clearOffline(endpoint, input);
37
+ }
38
+ const service = probe.service;
39
+ const url = new URL(`${service.baseUrl}/service/${service.serviceId}/auth`);
40
+ url.protocol = "ws:";
41
+ const socket = new WebSocket(url);
42
+ const remote = newWebSocketRpcSession(socket);
43
+ try {
44
+ const request = {
45
+ credentialPath: input.credentialPath,
46
+ };
47
+ if (endpoint !== undefined)
48
+ request.endpoint = endpoint.toString();
49
+ return fungiAccountLogoutResultSchema.parse(await remote.signOutFungi(request));
50
+ }
51
+ finally {
52
+ remote[Symbol.dispose]();
53
+ socket.close();
54
+ }
55
+ }
56
+ finally {
57
+ await release();
58
+ }
59
+ }
@@ -2,11 +2,21 @@ import * as ChildProcess from "effect/process/ChildProcess";
2
2
  import type { KnapsackComputer } from "@fungi.computer/knapsack";
3
3
  import { z } from "zod";
4
4
  import { type PrivateLocalProcessScope } from "./private-local-computer-processes.js";
5
+ import type { SandboxCommand } from "./private-local-containment.js";
6
+ /** The host admits each launch; undefined is its explicit unavailable fallback. */
7
+ export type LocalSandboxLaunch = (cwd: string, argv: readonly [string, ...string[]]) => SandboxCommand | undefined;
5
8
  declare const execOutput: z.ZodDiscriminatedUnion<[z.ZodObject<{
6
9
  exitCode: z.ZodNumber;
7
10
  stdout: z.ZodString;
8
11
  stderr: z.ZodString;
9
- truncated: z.ZodBoolean;
12
+ truncated: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodArray<z.ZodObject<{
13
+ stream: z.ZodEnum<{
14
+ stdout: "stdout";
15
+ stderr: "stderr";
16
+ }>;
17
+ keptBytes: z.ZodNumber;
18
+ totalBytes: z.ZodNumber;
19
+ }, z.core.$strict>>]>;
10
20
  status: z.ZodEnum<{
11
21
  failed: "failed";
12
22
  completed: "completed";
@@ -15,7 +25,14 @@ declare const execOutput: z.ZodDiscriminatedUnion<[z.ZodObject<{
15
25
  exitCode: z.ZodNumber;
16
26
  stdout: z.ZodString;
17
27
  stderr: z.ZodString;
18
- truncated: z.ZodBoolean;
28
+ truncated: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodArray<z.ZodObject<{
29
+ stream: z.ZodEnum<{
30
+ stdout: "stdout";
31
+ stderr: "stderr";
32
+ }>;
33
+ keptBytes: z.ZodNumber;
34
+ totalBytes: z.ZodNumber;
35
+ }, z.core.$strict>>]>;
19
36
  status: z.ZodLiteral<"deadline_exceeded">;
20
37
  timeoutMs: z.ZodNumber;
21
38
  elapsedMs: z.ZodNumber;
@@ -33,5 +50,9 @@ export declare function runPrivateInvocationProcess(processCommand: ChildProcess
33
50
  * Construct one private invocation-root Computer: bounded shell exec, plus the
34
51
  * background-process operations of `processes` when the host supplies a scope.
35
52
  */
36
- export declare function createPrivateInvocationComputer(invocationRoot: string, executionPath?: string, processes?: PrivateLocalProcessScope): Promise<KnapsackComputer>;
53
+ export declare function createPrivateInvocationComputer(invocationRoot: string, executionPath?: string, processes?: PrivateLocalProcessScope,
54
+ /** Selected host home; standalone callers capture it before any await. */
55
+ home?: string,
56
+ /** Native Local always supplies its live per-Agent admission callback. */
57
+ sandbox?: LocalSandboxLaunch): Promise<KnapsackComputer>;
37
58
  export {};