@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,373 @@
1
+ #!/usr/bin/env node
2
+ import { Readable, Writable } from "node:stream";
3
+ import { Cli } from "@fungi.computer/lantern";
4
+ import { linearAuthInteraction } from "./linear-auth.js";
5
+ export { linearAuthInteraction } from "./linear-auth.js";
6
+ import { parseArgs } from "node:util";
7
+ import { localLoginProviders } from "./local-login.js";
8
+ import { createPrivateModels } from "./local-models.js";
9
+ import { createPrivateCredentialStore } from "./private-file-credential-store.js";
10
+ import { capturePrivateLocalConfigRoot } from "./private-local-config-root.js";
11
+ export { maximumCredentialStringLength as localAuthInputLimit } from "./private-file-credential-store.js";
12
+ /**
13
+ * A blank API key was entered. Its message is fixed by the type, never
14
+ * provider output or credential material, so it is safe to show verbatim
15
+ * wherever the login surfaces (including across the Local service).
16
+ */
17
+ export class LocalAuthRefusal extends Error {
18
+ constructor() {
19
+ super("No key entered");
20
+ this.name = "LocalAuthRefusal";
21
+ }
22
+ }
23
+ function usage() {
24
+ return "Usage: fungi-local-auth <login|status|logout> [--provider <id>] [--auth-type oauth|api_key] [--method <id>]";
25
+ }
26
+ function parseAuthType(value) {
27
+ if (value === undefined)
28
+ return undefined;
29
+ if (value === "oauth" || value === "api_key")
30
+ return value;
31
+ throw new Error("--auth-type must be oauth or api_key");
32
+ }
33
+ function writeMessage(output, interaction, message) {
34
+ if (interaction !== undefined)
35
+ interaction.notify({ type: "info", message });
36
+ else
37
+ output.write(`${message}\n`);
38
+ }
39
+ function providerLabel(provider) {
40
+ return provider.name === provider.id
41
+ ? provider.name
42
+ : `${provider.name} (${provider.id})`;
43
+ }
44
+ /**
45
+ * Interactive login methods for one provider, from the same registry the
46
+ * Whistle/TUI picker uses (`localLoginProviders`), so Pi's labels and which
47
+ * methods count as interactive are decided in exactly one place.
48
+ */
49
+ function authMethodOptions(provider) {
50
+ const [entry] = localLoginProviders([provider]);
51
+ if (entry === undefined)
52
+ return [];
53
+ return entry.methods.map((method) => ({
54
+ id: method.kind === "subscription"
55
+ ? "oauth"
56
+ : "api_key",
57
+ label: method.label,
58
+ }));
59
+ }
60
+ /** Whether `provider` declares `requested` at all, interactive or not. */
61
+ function declaresAuthType(provider, requested) {
62
+ return requested === "oauth"
63
+ ? provider.auth.oauth !== undefined
64
+ : provider.auth.apiKey !== undefined;
65
+ }
66
+ function terminalInteraction(input, output) {
67
+ const cli = new Cli({
68
+ brand: "fungi-local-auth",
69
+ input: input instanceof Readable ? input : Readable.from(input),
70
+ output: output instanceof Writable
71
+ ? output
72
+ : new Writable({
73
+ write(chunk, _encoding, callback) {
74
+ output.write(chunk);
75
+ callback();
76
+ },
77
+ }),
78
+ });
79
+ return linearAuthInteraction(cli.interactive, cli);
80
+ }
81
+ function interactionFor(signal, input, output, supplied, method,
82
+ // Known once `login()` resolves the provider, before Pi's own login()
83
+ // runs; stamped onto a forwarded `auth_url` event so linear-auth.ts can
84
+ // head it "Sign in to <provider>" instead of a bare "Sign in".
85
+ providerLabel) {
86
+ let methodOverride = method;
87
+ return {
88
+ signal,
89
+ prompt: async (prompt) => {
90
+ if (prompt.type === "select" && methodOverride !== undefined) {
91
+ const selected = prompt.options.find((option) => option.id === methodOverride);
92
+ if (selected === undefined)
93
+ throw new Error(`Pi auth did not offer method ${methodOverride}`);
94
+ methodOverride = undefined;
95
+ return selected.id;
96
+ }
97
+ signal.throwIfAborted();
98
+ const promptSignal = prompt.signal === undefined
99
+ ? signal
100
+ : AbortSignal.any([signal, prompt.signal]);
101
+ const answer = await (supplied ?? terminalInteraction(input, output)).prompt({
102
+ ...prompt,
103
+ signal: promptSignal,
104
+ });
105
+ // A blank key would be stored as a credential and report success while
106
+ // every model call then fails; refuse it where the key is read.
107
+ if (prompt.type === "secret" && answer.trim() === "")
108
+ throw new LocalAuthRefusal();
109
+ return answer;
110
+ },
111
+ notify: (event) => {
112
+ const augmented = providerLabel === undefined || event.type !== "auth_url"
113
+ ? event
114
+ : { ...event, providerLabel };
115
+ if (supplied === undefined)
116
+ terminalInteraction(input, output).notify(augmented);
117
+ else
118
+ supplied.notify(augmented);
119
+ },
120
+ };
121
+ }
122
+ async function chooseProvider(providers, requested, signal, input, output, supplied, message = "Choose a provider") {
123
+ if (requested !== undefined) {
124
+ const provider = providers.find((candidate) => candidate.id === requested);
125
+ if (provider === undefined)
126
+ throw new Error(`Unknown provider: ${requested}`);
127
+ return provider;
128
+ }
129
+ const id = await interactionFor(signal, input, output, supplied).prompt({
130
+ type: "select",
131
+ message,
132
+ options: providers.map((provider) => ({
133
+ id: provider.id,
134
+ label: providerLabel(provider),
135
+ })),
136
+ });
137
+ const provider = providers.find((candidate) => candidate.id === id);
138
+ if (provider === undefined)
139
+ throw new Error(`Unknown provider: ${id}`);
140
+ return provider;
141
+ }
142
+ async function chooseAuthType(provider, requested, signal, input, output, supplied) {
143
+ if (requested !== undefined) {
144
+ if (!declaresAuthType(provider, requested))
145
+ throw new Error(`${providerLabel(provider)} does not offer ${requested} login`);
146
+ return requested;
147
+ }
148
+ const options = authMethodOptions(provider);
149
+ if (options.length === 0) {
150
+ // No interactive method (e.g. an ambient-only API key): fall through to
151
+ // api_key so login() can explain why, the same as an explicit request.
152
+ if (provider.auth.apiKey !== undefined)
153
+ return "api_key";
154
+ throw new Error(`${providerLabel(provider)} has no login method`);
155
+ }
156
+ if (options.length === 1) {
157
+ const only = options[0];
158
+ if (only === undefined)
159
+ throw new Error("No login method is available");
160
+ return only.id;
161
+ }
162
+ const id = await interactionFor(signal, input, output, supplied).prompt({
163
+ type: "select",
164
+ message: `Choose login method for ${providerLabel(provider)}`,
165
+ options,
166
+ });
167
+ const selected = options.find((option) => option.id === id);
168
+ if (selected === undefined)
169
+ throw new Error(`Unknown login method: ${id}`);
170
+ return selected.id;
171
+ }
172
+ async function login(models, command, signal, input, output, host, supplied) {
173
+ const provider = await chooseProvider(models.getProviders(), command.provider, signal, input, output, supplied);
174
+ const authType = await chooseAuthType(provider, command.authType, signal, input, output, supplied);
175
+ if (authType === "api_key" && provider.auth.apiKey?.login === undefined) {
176
+ const message = `${providerLabel(provider)} uses ambient credentials; configure its environment or provider settings. No credential was changed.`;
177
+ writeMessage(output, supplied, message);
178
+ throw new Error(message);
179
+ }
180
+ const signInLabel = authType === "oauth" && provider.id === "openai"
181
+ ? "ChatGPT for Shiitake"
182
+ : authType === "oauth" && provider.id === "anthropic"
183
+ ? "Claude for Shiitake"
184
+ : provider.name;
185
+ const interaction = interactionFor(signal, input, output, supplied, command.method, signInLabel);
186
+ // Sign in with ChatGPT needs a stable installation UUID in its authorize
187
+ // URL (`ext_agent_host_id`) and shows `agentName` on OpenAI's consent page
188
+ // (Levi, 2026-10-16: Shiitake, Fungi's agent). Pass both at this one call
189
+ // site, the way Pi's own coding-agent does. Pi's device-ID option is
190
+ // synchronous, so resolve our (async, disk-backed) ID first and hand it a
191
+ // closure over the resolved value.
192
+ if (provider.id === "openai" && authType === "oauth") {
193
+ signal.throwIfAborted();
194
+ const deviceId = await host.getDeviceId();
195
+ signal.throwIfAborted();
196
+ const loginOptions = {
197
+ agentName: "Shiitake",
198
+ getDeviceId: () => deviceId,
199
+ };
200
+ await models.login(provider.id, authType, interaction, loginOptions);
201
+ }
202
+ else {
203
+ await models.login(provider.id, authType, interaction);
204
+ }
205
+ writeMessage(output, supplied, `${signInLabel} login complete.`);
206
+ }
207
+ const credentialLabel = { oauth: "subscription", api_key: "API key" };
208
+ /**
209
+ * Signed-in providers first, then one summary line; the full catalog only
210
+ * when one provider is asked for by name.
211
+ */
212
+ function statusLines(statuses, explicit) {
213
+ const label = (status) => status.providerName === status.providerId
214
+ ? status.providerName
215
+ : `${status.providerName} (${status.providerId})`;
216
+ const signedIn = statuses.flatMap((status) => status.state === "configured"
217
+ ? [`${label(status)}: signed in (${credentialLabel[status.type]})`]
218
+ : []);
219
+ if (explicit)
220
+ return [
221
+ ...signedIn,
222
+ ...statuses.flatMap((status) => status.state === "unconfigured"
223
+ ? [`${label(status)}: not signed in`]
224
+ : []),
225
+ ];
226
+ const others = statuses.length - signedIn.length;
227
+ return signedIn.length === 0
228
+ ? [
229
+ `Model providers: none signed in (${String(others)} available). Run fungi login.`,
230
+ ]
231
+ : [
232
+ ...signedIn,
233
+ `${String(others)} other providers not signed in. Run fungi login to add one.`,
234
+ ];
235
+ }
236
+ async function status(models, providerId, signal, output, interaction) {
237
+ const providers = models.getProviders();
238
+ const selected = providerId === undefined
239
+ ? providers
240
+ : providers.filter((provider) => provider.id === providerId);
241
+ if (selected.length === 0)
242
+ throw new Error(`Unknown provider: ${providerId ?? "(all providers)"}`);
243
+ const statuses = [];
244
+ for (const provider of selected) {
245
+ const checked = await models.checkAuth(provider.id, { signal });
246
+ statuses.push(checked === undefined
247
+ ? {
248
+ providerId: provider.id,
249
+ providerName: provider.name,
250
+ state: "unconfigured",
251
+ }
252
+ : {
253
+ providerId: provider.id,
254
+ providerName: provider.name,
255
+ state: "configured",
256
+ type: checked.type,
257
+ });
258
+ }
259
+ for (const line of statusLines(statuses, providerId !== undefined))
260
+ writeMessage(output, interaction, line);
261
+ return {
262
+ kind: "status",
263
+ providers: Object.freeze(statuses.map((providerStatus) => Object.freeze(providerStatus))),
264
+ };
265
+ }
266
+ async function signedInProviders(models, providers, signal) {
267
+ const signedIn = [];
268
+ for (const provider of providers)
269
+ if ((await models.checkAuth(provider.id, { signal })) !== undefined)
270
+ signedIn.push(provider);
271
+ return signedIn;
272
+ }
273
+ async function runCommand(command, models, signal, input, output, host, interaction) {
274
+ if (command.type === "status")
275
+ return status(models, command.provider, signal, output, interaction);
276
+ if (command.type === "login") {
277
+ await login(models, command, signal, input, output, host, interaction);
278
+ return { kind: "completed" };
279
+ }
280
+ const providers = models.getProviders();
281
+ const candidates = command.provider === undefined
282
+ ? await signedInProviders(models, providers, signal)
283
+ : providers;
284
+ if (candidates.length === 0) {
285
+ writeMessage(output, interaction, "You're not signed in to any model provider. Nothing to log out of.");
286
+ return { kind: "completed" };
287
+ }
288
+ const provider = await chooseProvider(candidates, command.provider, signal, input, output, interaction, "Log out of which provider?");
289
+ await models.logout(provider.id, { signal });
290
+ writeMessage(output, interaction, `${providerLabel(provider)} logout complete.`);
291
+ return { kind: "completed" };
292
+ }
293
+ function parseCommand(argv) {
294
+ const parsed = parseArgs({
295
+ args: [...argv],
296
+ allowPositionals: true,
297
+ strict: true,
298
+ options: {
299
+ help: { type: "boolean", short: "h" },
300
+ provider: { type: "string" },
301
+ "auth-type": { type: "string" },
302
+ method: { type: "string" },
303
+ },
304
+ });
305
+ const command = parsed.positionals[0] ?? "help";
306
+ if (parsed.values.help === true || command === "help")
307
+ return { type: "help" };
308
+ if (parsed.positionals.length !== 1)
309
+ throw new Error(usage());
310
+ const provider = parsed.values.provider;
311
+ const method = parsed.values.method;
312
+ const authType = parseAuthType(parsed.values["auth-type"]);
313
+ if (command === "login") {
314
+ const result = { type: "login" };
315
+ if (provider !== undefined)
316
+ result.provider = provider;
317
+ if (authType !== undefined)
318
+ result.authType = authType;
319
+ if (method !== undefined)
320
+ result.method = method === "device-code" ? "device_code" : method;
321
+ return result;
322
+ }
323
+ if (method !== undefined || authType !== undefined)
324
+ throw new Error("--auth-type and --method apply only to login");
325
+ if (command === "status" || command === "logout") {
326
+ const result = {
327
+ type: command,
328
+ };
329
+ if (provider !== undefined)
330
+ result.provider = provider;
331
+ return result;
332
+ }
333
+ throw new Error(usage());
334
+ }
335
+ export async function runLocalAuthCli(argv, options = {}) {
336
+ const output = options.output ?? process.stdout;
337
+ const command = parseCommand(argv);
338
+ if (command.type === "help") {
339
+ output.write(`${usage()}\n`);
340
+ return { kind: "completed" };
341
+ }
342
+ const credentialPath = options.credentialPath ?? capturePrivateLocalConfigRoot().credentialPath;
343
+ const controller = new AbortController();
344
+ const signals = options.signals ?? process;
345
+ const abort = () => {
346
+ controller.abort();
347
+ };
348
+ signals.once("SIGINT", abort);
349
+ signals.once("SIGTERM", abort);
350
+ const getDeviceId = options.getDeviceId ??
351
+ (() => createPrivateCredentialStore(credentialPath).getDeviceId());
352
+ try {
353
+ return await runCommand(command, options.models ?? createPrivateModels(credentialPath), options.interaction?.signal === undefined
354
+ ? controller.signal
355
+ : AbortSignal.any([controller.signal, options.interaction.signal]), options.input ?? process.stdin, output, { getDeviceId }, options.interaction);
356
+ }
357
+ finally {
358
+ signals.removeListener("SIGINT", abort);
359
+ signals.removeListener("SIGTERM", abort);
360
+ }
361
+ }
362
+ if (import.meta.main) {
363
+ const cli = new Cli({ brand: "fungi-local-auth" });
364
+ if (cli.interactive)
365
+ cli.welcome("Provider login for your local agents.");
366
+ runLocalAuthCli(process.argv.slice(2), {
367
+ interaction: linearAuthInteraction(cli.interactive, cli),
368
+ }).catch((cause) => {
369
+ // Provider messages can contain credentials; keep the diagnostic bounded.
370
+ process.stderr.write(`Local authentication command failed (${cause instanceof Error ? cause.name : "NonError"}).\n`);
371
+ process.exitCode = 1;
372
+ });
373
+ }
@@ -0,0 +1,18 @@
1
+ import type { FetchLike } from "@modelcontextprotocol/client";
2
+ export type FungiAccountCommand = Readonly<{
3
+ action: "status" | "teams" | "balance";
4
+ }> | Readonly<{
5
+ action: "use-team";
6
+ teamId: string;
7
+ }>;
8
+ export type FungiAccountCommandOptions = Readonly<{
9
+ json: boolean;
10
+ stdout: Readonly<{
11
+ write: (text: string) => void;
12
+ }>;
13
+ credentialPath?: string | undefined;
14
+ fetch?: FetchLike | undefined;
15
+ }>;
16
+ export declare const fungiAccountCommandHelp = "Usage: fungi account [--json]\n fungi account teams [--json]\n fungi account use-team <team-id>\n fungi account balance [--json]\n fungi account prepare-credential --provider anthropic|openai\n fungi account use-credential --provider anthropic|openai --from-file -\n\nRead the signed-in Fungi account without exposing its credentials. The plain\ncommand shows the Hub and the Team that pays for models; teams lists your\ncurrent memberships; use-team selects one of them (the prior Team is kept when\nyou are not a current member); balance reads that Team's credit when you may\nsee its billing.\n";
17
+ /** `fungi account`: non-secret reads over Local's one Fungi account owner. */
18
+ export declare function runFungiAccountCommand(command: FungiAccountCommand, options: FungiAccountCommandOptions): Promise<void>;
@@ -0,0 +1,95 @@
1
+ import { capturePrivateLocalConfigRoot } from "./private-local-config-root.js";
2
+ import { readFungiAccountStatus, readFungiAccountTeams, readFungiTeamBalance, switchFungiAccountTeam, } from "./private-fungi-account.js";
3
+ export const fungiAccountCommandHelp = `Usage: fungi account [--json]
4
+ fungi account teams [--json]
5
+ fungi account use-team <team-id>
6
+ fungi account balance [--json]
7
+ fungi account prepare-credential --provider anthropic|openai
8
+ fungi account use-credential --provider anthropic|openai --from-file -
9
+
10
+ Read the signed-in Fungi account without exposing its credentials. The plain
11
+ command shows the Hub and the Team that pays for models; teams lists your
12
+ current memberships; use-team selects one of them (the prior Team is kept when
13
+ you are not a current member); balance reads that Team's credit when you may
14
+ see its billing.
15
+ `;
16
+ function teamFields({ teamId, handle, name }) {
17
+ return { teamId, handle, name };
18
+ }
19
+ /** `fungi account`: non-secret reads over Local's one Fungi account owner. */
20
+ export async function runFungiAccountCommand(command, options) {
21
+ const owner = {
22
+ credentialPath: options.credentialPath ?? capturePrivateLocalConfigRoot().credentialPath,
23
+ };
24
+ if (options.fetch !== undefined)
25
+ owner.fetch = options.fetch;
26
+ const emit = (reply, text) => {
27
+ options.stdout.write(options.json
28
+ ? `${JSON.stringify({ schemaVersion: 1, ...reply })}\n`
29
+ : `${text}\n`);
30
+ };
31
+ if (command.action === "use-team") {
32
+ await switchFungiAccountTeam(command.teamId, owner);
33
+ const team = (await readFungiAccountStatus(owner))?.team;
34
+ emit({ team: team === undefined ? undefined : teamFields(team) }, `Fungi models now bill ${team?.name ?? command.teamId}.`);
35
+ return;
36
+ }
37
+ const status = await readFungiAccountStatus(owner);
38
+ if (status === undefined) {
39
+ emit({ signedIn: false }, "Not signed in to Fungi. Run fungi login --provider fungi.");
40
+ return;
41
+ }
42
+ if (command.action === "teams") {
43
+ const { teams, selectedTeamId } = await readFungiAccountTeams(owner);
44
+ emit({
45
+ teams: teams.map((team) => ({
46
+ ...teamFields(team),
47
+ selected: team.teamId === selectedTeamId,
48
+ })),
49
+ }, teams
50
+ .map((team) => `${team.teamId === selectedTeamId ? "*" : " "} ${team.name} (${team.handle}) ${team.teamId}`)
51
+ .join("\n") || "You are not a member of any Team.");
52
+ return;
53
+ }
54
+ if (command.action === "balance") {
55
+ const balance = await readFungiTeamBalance(owner);
56
+ emit(balanceReply(balance), balanceText(balance));
57
+ return;
58
+ }
59
+ const reply = {
60
+ signedIn: true,
61
+ hub: status.issuer,
62
+ accessExpiresAt: status.accessExpiresAt.toISOString(),
63
+ };
64
+ if (status.team !== undefined)
65
+ reply.team = teamFields(status.team);
66
+ if (status.teamCount !== undefined)
67
+ reply.teamCount = status.teamCount;
68
+ emit(reply, `Signed in to ${status.issuer}. ${status.team === undefined ? "No Team selected for Fungi models." : `Fungi models bill ${status.team.name} (${status.team.handle}).`}`);
69
+ }
70
+ function balanceReply(balance) {
71
+ if (balance.kind === "not-available")
72
+ return {
73
+ kind: balance.kind,
74
+ reason: balance.reason,
75
+ };
76
+ const { teamId, ...credit } = balance.credit;
77
+ return { kind: balance.kind, teamId, credit };
78
+ }
79
+ function dollars(money) {
80
+ const cents = BigInt(money.amount) / 10n ** BigInt(money.scale - 2);
81
+ const whole = cents / 100n;
82
+ const rest = (cents % 100n).toString().padStart(2, "0");
83
+ return `$${whole.toString()}.${rest}`;
84
+ }
85
+ function balanceText(balance) {
86
+ if (balance.kind === "balance")
87
+ return `Available ${dollars(balance.credit.available)} (purchased ${dollars(balance.credit.balance)}, reserved ${dollars(balance.credit.reserved)}${balance.credit.included === null ? "" : `, included ${dollars(balance.credit.included.remaining)} left`}).`;
88
+ if (balance.reason === "no-team")
89
+ return "No Team selected. Run fungi account use-team <team-id>.";
90
+ if (balance.reason === "not-a-member")
91
+ return "You are not a current member of the selected Team. Run fungi account teams.";
92
+ if (balance.reason === "sign-in-again")
93
+ return "This sign-in lacks billing permission. Run fungi login --provider fungi again.";
94
+ return "This Fungi Hub does not admit Local sign-ins for Team balance.";
95
+ }
@@ -0,0 +1,27 @@
1
+ export { createPrivateCredentialStore, resolveDefaultCredentialPath, } from "./private-file-credential-store.js";
2
+ export type { McpFormApprovalRequest, McpFormApprovalDecision, McpFormAnswerResult, } from "./private-mcp-form-contract.js";
3
+ export { McpFormApprovalRequestSchema, mcpFormApprovalContract, } from "./private-mcp-form-contract.js";
4
+ export type { McpFormApprovalRefusal, RequestMcpFormApproval, } from "./private-mcp-form-elicitation.js";
5
+ export type { PrivateCredentialStore } from "./private-file-credential-store.js";
6
+ export { createPrivateModels, registerPrivateBundledModelOAuthFlows, listPrivateModels, type PrivateLocalModelChoice, } from "./local-models.js";
7
+ export { resolveAgentStartupModel, type LocalStartupModel, type LocalStartupModelSource, } from "./private-local-startup-model.js";
8
+ export { createPrivateInvocationFilesPort } from "./private-invocation-files.js";
9
+ export { createPrivateInvocationComputer } from "./private-invocation-computer.js";
10
+ export { ensurePrivateLocalGitExclude } from "./private-local-git-exclude.js";
11
+ export { createPrivateWorkerdSandbox } from "./private-workerd-sandbox.js";
12
+ export { signInFungiAccount, launchPlatformBrowser, signOutFungiAccount, readFungiAccountStatus, switchFungiAccountTeam, freshFungiAccess, createFungiAccountOAuth, createFungiAccountCredentialStore, type FungiAccountTeam, type PrivateFungiAccountLoginOptions, type PrivateFungiAccountLogoutResult, } from "./private-fungi-account.js";
13
+ export { fungiAccountCommandHelp, runFungiAccountCommand, type FungiAccountCommand, } from "./fungi-account-command.js";
14
+ export type { PrivateFungiMcpConnectionOptions } from "./private-fungi-mcp-connection.js";
15
+ export { parseFungiMcpEndpoint } from "./private-fungi-mcp-endpoint.js";
16
+ export { readProjectFungiMcpSelection } from "./private-fungi-mcp-selection.js";
17
+ export { fungiMcpSelectionSchema, type PrivateFungiMcpSelection, } from "./private-fungi-mcp-selection-schema.js";
18
+ export { saveOysterMcpKey, OysterMcpKeyRejectedError, } from "./private-oyster-mcp-auth.js";
19
+ export { attachPrivateLocalComputer, detachPrivateLocalComputer, listPrivateLocalComputers, runPrivateLocalComputer, type PrivateLocalComputerAttachResult, type PrivateLocalComputerDetachResult, } from "./private-local-computer-attach.js";
20
+ export { openPrivateLocalRuntimeHost, type PrivateLocalRuntimeHost, type PrivateLocalRuntimeHostOptions, } from "./private-local-runtime-host.js";
21
+ export { openPrivateLocalShiitakeHost, openPrivateLocalShiitakeWorkerBatch, type PrivateLocalShiitakeHost, type PrivateLocalShiitakeHostOpener, type PrivateLocalShiitakeServer, type PrivateLocalShiitakeServerOpener, type PrivateLocalShiitakeWorkerBatch, type PrivateLocalShiitakeWorkerInput, type PrivateLocalShiitakeWorkerRoute, } from "./private-local-shiitake-host.js";
22
+ export { createPrivateLocalServiceManager, LocalAgentSelectionAmbiguous, LocalServiceUnreadableResponse, LocalSessionPathConflict, runPrivateLocalWorkerServer, type PrivateLocalServiceConnection, type PrivateLocalServiceManager, type PrivateLocalServiceRequestOptions, type PrivateLocalDiscoveryOptions, type PrivateLocalArchiveRequest, type PrivateLocalServiceSelection, type PrivateLocalServiceStatus, type ServiceDiagnosticsStatus, type ServiceDiagnosticsFailureCode, type PrivateLocalAgentChoice, type PrivateLocalSessionChoice, type PrivateLocalSessionDiscovery, type PrivateLocalSessionInspection, type PrivateLocalSessionProblem, type PrivateLocalWorkerServerIo, } from "./private-local-service.js";
23
+ export { createPrivateLocalAcpAgent, runPrivateLocalAcpStdio, type LocalAcpStdioOptions, } from "./private-local-acp-stdio.js";
24
+ export { listPrivateTeamComputers, startPrivateTeamComputer, } from "./private-team-computers.js";
25
+ export { runTrackerGitCredential } from "./tracker-git-credential.js";
26
+ export { activateSubscriptionCredential, CredentialActivationError, } from "./subscription-activation.js";
27
+ export { prepareSubscriptionIdentity } from "./subscription-identity.js";
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ export { createPrivateCredentialStore, resolveDefaultCredentialPath, } from "./private-file-credential-store.js";
2
+ export { McpFormApprovalRequestSchema, mcpFormApprovalContract, } from "./private-mcp-form-contract.js";
3
+ export { createPrivateModels, registerPrivateBundledModelOAuthFlows, listPrivateModels, } from "./local-models.js";
4
+ export { resolveAgentStartupModel, } from "./private-local-startup-model.js";
5
+ export { createPrivateInvocationFilesPort } from "./private-invocation-files.js";
6
+ export { createPrivateInvocationComputer } from "./private-invocation-computer.js";
7
+ export { ensurePrivateLocalGitExclude } from "./private-local-git-exclude.js";
8
+ export { createPrivateWorkerdSandbox } from "./private-workerd-sandbox.js";
9
+ export { signInFungiAccount, launchPlatformBrowser, signOutFungiAccount, readFungiAccountStatus, switchFungiAccountTeam, freshFungiAccess, createFungiAccountOAuth, createFungiAccountCredentialStore, } from "./private-fungi-account.js";
10
+ export { fungiAccountCommandHelp, runFungiAccountCommand, } from "./fungi-account-command.js";
11
+ export { parseFungiMcpEndpoint } from "./private-fungi-mcp-endpoint.js";
12
+ export { readProjectFungiMcpSelection } from "./private-fungi-mcp-selection.js";
13
+ export { fungiMcpSelectionSchema, } from "./private-fungi-mcp-selection-schema.js";
14
+ export { saveOysterMcpKey, OysterMcpKeyRejectedError, } from "./private-oyster-mcp-auth.js";
15
+ export { attachPrivateLocalComputer, detachPrivateLocalComputer, listPrivateLocalComputers, runPrivateLocalComputer, } from "./private-local-computer-attach.js";
16
+ export { openPrivateLocalRuntimeHost, } from "./private-local-runtime-host.js";
17
+ export { openPrivateLocalShiitakeHost, openPrivateLocalShiitakeWorkerBatch, } from "./private-local-shiitake-host.js";
18
+ export { createPrivateLocalServiceManager, LocalAgentSelectionAmbiguous, LocalServiceUnreadableResponse, LocalSessionPathConflict, runPrivateLocalWorkerServer, } from "./private-local-service.js";
19
+ export { createPrivateLocalAcpAgent, runPrivateLocalAcpStdio, } from "./private-local-acp-stdio.js";
20
+ export { listPrivateTeamComputers, startPrivateTeamComputer, } from "./private-team-computers.js";
21
+ export { runTrackerGitCredential } from "./tracker-git-credential.js";
22
+ export { activateSubscriptionCredential, CredentialActivationError, } from "./subscription-activation.js";
23
+ export { prepareSubscriptionIdentity } from "./subscription-identity.js";
@@ -0,0 +1,106 @@
1
+ {
2
+ "inputs": {
3
+ "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/lib/photon_rs.js": {
4
+ "bytes": 154888,
5
+ "imports": [],
6
+ "format": "esm"
7
+ },
8
+ "../../node_modules/.pnpm/@cf-wasm+internals@0.2.0/node_modules/@cf-wasm/internals/dist/polyfills/image-data.js": {
9
+ "bytes": 4651,
10
+ "imports": [],
11
+ "format": "esm"
12
+ },
13
+ "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/photon.js": {
14
+ "bytes": 6679,
15
+ "imports": [
16
+ {
17
+ "path": "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/lib/photon_rs.js",
18
+ "kind": "import-statement",
19
+ "original": "./lib/photon_rs.js"
20
+ },
21
+ {
22
+ "path": "../../node_modules/.pnpm/@cf-wasm+internals@0.2.0/node_modules/@cf-wasm/internals/dist/polyfills/image-data.js",
23
+ "kind": "import-statement",
24
+ "original": "@cf-wasm/internals/polyfills/image-data"
25
+ }
26
+ ],
27
+ "format": "esm"
28
+ },
29
+ "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/lib/photon_rs_bg.wasm.inline.js": {
30
+ "bytes": 2089508,
31
+ "imports": [],
32
+ "format": "esm"
33
+ },
34
+ "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/node.js": {
35
+ "bytes": 5540,
36
+ "imports": [
37
+ {
38
+ "path": "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/lib/photon_rs.js",
39
+ "kind": "import-statement",
40
+ "original": "./lib/photon_rs.js"
41
+ },
42
+ {
43
+ "path": "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/photon.js",
44
+ "kind": "import-statement",
45
+ "original": "./photon.js"
46
+ },
47
+ {
48
+ "path": "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/lib/photon_rs_bg.wasm.inline.js",
49
+ "kind": "import-statement",
50
+ "original": "./lib/photon_rs_bg.wasm.inline.js"
51
+ }
52
+ ],
53
+ "format": "esm"
54
+ },
55
+ "src/internal/local-image-fit-worker.mjs": {
56
+ "bytes": 3820,
57
+ "imports": [
58
+ {
59
+ "path": "node:worker_threads",
60
+ "kind": "import-statement",
61
+ "external": true
62
+ },
63
+ {
64
+ "path": "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/node.js",
65
+ "kind": "import-statement",
66
+ "original": "@cf-wasm/photon/node"
67
+ }
68
+ ],
69
+ "format": "esm"
70
+ }
71
+ },
72
+ "outputs": {
73
+ "dist/internal/local-image-fit-worker.mjs": {
74
+ "imports": [
75
+ {
76
+ "path": "node:worker_threads",
77
+ "kind": "import-statement",
78
+ "external": true
79
+ }
80
+ ],
81
+ "exports": [],
82
+ "entryPoint": "src/internal/local-image-fit-worker.mjs",
83
+ "inputs": {
84
+ "src/internal/local-image-fit-worker.mjs": {
85
+ "bytesInOutput": 2390
86
+ },
87
+ "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/lib/photon_rs.js": {
88
+ "bytesInOutput": 23750
89
+ },
90
+ "../../node_modules/.pnpm/@cf-wasm+internals@0.2.0/node_modules/@cf-wasm/internals/dist/polyfills/image-data.js": {
91
+ "bytesInOutput": 4431
92
+ },
93
+ "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/photon.js": {
94
+ "bytesInOutput": 1156
95
+ },
96
+ "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/lib/photon_rs_bg.wasm.inline.js": {
97
+ "bytesInOutput": 2089338
98
+ },
99
+ "../../node_modules/.pnpm/@cf-wasm+photon@0.4.0/node_modules/@cf-wasm/photon/dist/node.js": {
100
+ "bytesInOutput": 128
101
+ }
102
+ },
103
+ "bytes": 2122026
104
+ }
105
+ }
106
+ }