@mandujs/core 0.45.0 → 0.46.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.
- package/package.json +2 -2
- package/src/brain/adapters/__tests__/resolver.test.ts +37 -21
- package/src/brain/adapters/base.ts +4 -3
- package/src/brain/adapters/index.ts +319 -333
- package/src/brain/adapters/openai-oauth.ts +3 -5
- package/src/brain/brain.ts +8 -11
- package/src/brain/consent.ts +1 -1
- package/src/brain/index.ts +4 -1
- package/src/config/mandu.ts +12 -14
- package/src/config/validate.ts +3 -10
- package/src/deploy/cache.ts +139 -0
- package/src/deploy/index.ts +62 -0
- package/src/deploy/inference/context.ts +173 -0
- package/src/deploy/inference/heuristic.ts +182 -0
- package/src/deploy/intent.ts +173 -0
- package/src/deploy/plan.ts +178 -0
- package/src/runtime/server.ts +30 -0
- package/src/brain/adapters/ollama.ts +0 -235
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mandujs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
4
4
|
"description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"./content/llms-txt": "./src/content/llms-txt.ts",
|
|
26
26
|
"./content/schema": "./src/content/schema.ts",
|
|
27
27
|
"./db": "./src/db/index.ts",
|
|
28
|
+
"./deploy": "./src/deploy/index.ts",
|
|
28
29
|
"./design": "./src/design/index.ts",
|
|
29
30
|
"./desktop": "./src/desktop/index.ts",
|
|
30
31
|
"./desktop/worker": "./src/desktop/worker.ts",
|
|
@@ -155,7 +156,6 @@
|
|
|
155
156
|
"fast-glob": "^3.3.2",
|
|
156
157
|
"glob": "^13.0.0",
|
|
157
158
|
"minimatch": "^10.1.1",
|
|
158
|
-
"ollama": "^0.6.3",
|
|
159
159
|
"zod": "^3.23.8"
|
|
160
160
|
}
|
|
161
161
|
}
|
|
@@ -4,10 +4,13 @@
|
|
|
4
4
|
* Priority order under `adapter: "auto"`:
|
|
5
5
|
* 1. openai-oauth when token present
|
|
6
6
|
* 2. anthropic-oauth when token present
|
|
7
|
-
* 3.
|
|
8
|
-
*
|
|
7
|
+
* 3. template otherwise — with `needsLogin: true` so interactive
|
|
8
|
+
* CLIs prompt `mandu brain login --provider=openai` instead of
|
|
9
|
+
* degrading silently.
|
|
9
10
|
*
|
|
10
|
-
* `telemetryOptOut: true` disables every cloud tier
|
|
11
|
+
* `telemetryOptOut: true` disables every cloud tier and clears
|
|
12
|
+
* `needsLogin` (the user opted out, so prompting login would be
|
|
13
|
+
* incorrect).
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
16
|
import { describe, it, expect } from "bun:test";
|
|
@@ -23,7 +26,7 @@ function anthropicToken(): StoredToken {
|
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
describe("resolveBrainAdapter — priority order", () => {
|
|
26
|
-
it("picks openai first when both cloud tokens
|
|
29
|
+
it("picks openai first when both cloud tokens are available", async () => {
|
|
27
30
|
const store = makeMemoryStore({
|
|
28
31
|
openai: openaiToken(),
|
|
29
32
|
anthropic: anthropicToken(),
|
|
@@ -31,11 +34,11 @@ describe("resolveBrainAdapter — priority order", () => {
|
|
|
31
34
|
const res = await resolveBrainAdapter({
|
|
32
35
|
adapter: "auto",
|
|
33
36
|
credentialStore: store,
|
|
34
|
-
probeOllama: async () => true,
|
|
35
37
|
probeChatGPTAuth: () => ({ authenticated: false, path: null }),
|
|
36
38
|
});
|
|
37
39
|
expect(res.resolved).toBe("openai");
|
|
38
40
|
expect(res.adapter.name).toBe("openai-oauth");
|
|
41
|
+
expect(res.needsLogin).toBe(false);
|
|
39
42
|
});
|
|
40
43
|
|
|
41
44
|
it("falls to anthropic when only anthropic token present", async () => {
|
|
@@ -43,35 +46,35 @@ describe("resolveBrainAdapter — priority order", () => {
|
|
|
43
46
|
const res = await resolveBrainAdapter({
|
|
44
47
|
adapter: "auto",
|
|
45
48
|
credentialStore: store,
|
|
46
|
-
probeOllama: async () => true,
|
|
47
49
|
probeChatGPTAuth: () => ({ authenticated: false, path: null }),
|
|
48
50
|
});
|
|
49
51
|
expect(res.resolved).toBe("anthropic");
|
|
50
52
|
expect(res.adapter.name).toBe("anthropic-oauth");
|
|
53
|
+
expect(res.needsLogin).toBe(false);
|
|
51
54
|
});
|
|
52
55
|
|
|
53
|
-
it("falls to
|
|
56
|
+
it("falls to template with needsLogin=true when no cloud tokens", async () => {
|
|
54
57
|
const store = makeMemoryStore();
|
|
55
58
|
const res = await resolveBrainAdapter({
|
|
56
59
|
adapter: "auto",
|
|
57
60
|
credentialStore: store,
|
|
58
|
-
probeOllama: async () => true,
|
|
59
61
|
probeChatGPTAuth: () => ({ authenticated: false, path: null }),
|
|
60
62
|
});
|
|
61
|
-
expect(res.resolved).toBe("
|
|
62
|
-
expect(res.adapter.name).toBe("
|
|
63
|
+
expect(res.resolved).toBe("template");
|
|
64
|
+
expect(res.adapter.name).toBe("noop");
|
|
65
|
+
expect(res.needsLogin).toBe(true);
|
|
66
|
+
expect(res.reason).toContain("mandu brain login");
|
|
63
67
|
});
|
|
64
68
|
|
|
65
|
-
it("
|
|
69
|
+
it("picks openai via ChatGPT session token when keychain is empty", async () => {
|
|
66
70
|
const store = makeMemoryStore();
|
|
67
71
|
const res = await resolveBrainAdapter({
|
|
68
72
|
adapter: "auto",
|
|
69
73
|
credentialStore: store,
|
|
70
|
-
|
|
71
|
-
probeChatGPTAuth: () => ({ authenticated: false, path: null }),
|
|
74
|
+
probeChatGPTAuth: () => ({ authenticated: true, path: "/tmp/auth.json" }),
|
|
72
75
|
});
|
|
73
|
-
expect(res.resolved).toBe("
|
|
74
|
-
expect(res.
|
|
76
|
+
expect(res.resolved).toBe("openai");
|
|
77
|
+
expect(res.needsLogin).toBe(false);
|
|
75
78
|
});
|
|
76
79
|
});
|
|
77
80
|
|
|
@@ -85,25 +88,26 @@ describe("resolveBrainAdapter — telemetryOptOut", () => {
|
|
|
85
88
|
adapter: "auto",
|
|
86
89
|
telemetryOptOut: true,
|
|
87
90
|
credentialStore: store,
|
|
88
|
-
probeOllama: async () => true,
|
|
89
91
|
});
|
|
90
|
-
expect(res.resolved).toBe("
|
|
92
|
+
expect(res.resolved).toBe("template");
|
|
93
|
+
expect(res.needsLogin).toBe(false);
|
|
94
|
+
expect(res.reason).toContain("telemetryOptOut");
|
|
91
95
|
});
|
|
92
96
|
|
|
93
|
-
it("falls to template
|
|
94
|
-
const store = makeMemoryStore(
|
|
97
|
+
it("falls to template without prompting login (needsLogin=false)", async () => {
|
|
98
|
+
const store = makeMemoryStore();
|
|
95
99
|
const res = await resolveBrainAdapter({
|
|
96
100
|
adapter: "auto",
|
|
97
101
|
telemetryOptOut: true,
|
|
98
102
|
credentialStore: store,
|
|
99
|
-
probeOllama: async () => false,
|
|
100
103
|
});
|
|
101
104
|
expect(res.resolved).toBe("template");
|
|
105
|
+
expect(res.needsLogin).toBe(false);
|
|
102
106
|
});
|
|
103
107
|
});
|
|
104
108
|
|
|
105
109
|
describe("resolveBrainAdapter — explicit pins degrade gracefully", () => {
|
|
106
|
-
it("explicit 'openai' without a token degrades to template
|
|
110
|
+
it("explicit 'openai' without a token degrades to template + needsLogin=true", async () => {
|
|
107
111
|
const store = makeMemoryStore();
|
|
108
112
|
const res = await resolveBrainAdapter({
|
|
109
113
|
adapter: "openai",
|
|
@@ -111,6 +115,7 @@ describe("resolveBrainAdapter — explicit pins degrade gracefully", () => {
|
|
|
111
115
|
probeChatGPTAuth: () => ({ authenticated: false, path: null }),
|
|
112
116
|
});
|
|
113
117
|
expect(res.resolved).toBe("template");
|
|
118
|
+
expect(res.needsLogin).toBe(true);
|
|
114
119
|
expect(res.reason).toContain("no token");
|
|
115
120
|
});
|
|
116
121
|
|
|
@@ -122,6 +127,17 @@ describe("resolveBrainAdapter — explicit pins degrade gracefully", () => {
|
|
|
122
127
|
credentialStore: store,
|
|
123
128
|
});
|
|
124
129
|
expect(res.resolved).toBe("template");
|
|
130
|
+
expect(res.needsLogin).toBe(false);
|
|
125
131
|
expect(res.reason).toContain("telemetryOptOut");
|
|
126
132
|
});
|
|
133
|
+
|
|
134
|
+
it("explicit 'template' is the only no-prompt zero-cost path", async () => {
|
|
135
|
+
const store = makeMemoryStore({ openai: openaiToken() });
|
|
136
|
+
const res = await resolveBrainAdapter({
|
|
137
|
+
adapter: "template",
|
|
138
|
+
credentialStore: store,
|
|
139
|
+
});
|
|
140
|
+
expect(res.resolved).toBe("template");
|
|
141
|
+
expect(res.needsLogin).toBe(false);
|
|
142
|
+
});
|
|
127
143
|
});
|
|
@@ -17,12 +17,13 @@ import type {
|
|
|
17
17
|
* Base LLM Adapter Interface
|
|
18
18
|
*
|
|
19
19
|
* Implementations:
|
|
20
|
-
* -
|
|
21
|
-
* -
|
|
20
|
+
* - OpenAIOAuthAdapter: OpenAI via ChatGPT session token / OAuth
|
|
21
|
+
* - AnthropicOAuthAdapter: Anthropic via Claude OAuth
|
|
22
|
+
* - NoopAdapter: template fallback (no LLM)
|
|
22
23
|
*/
|
|
23
24
|
export interface LLMAdapter {
|
|
24
25
|
/**
|
|
25
|
-
* Adapter name (e.g., "
|
|
26
|
+
* Adapter name (e.g., "openai", "anthropic", "template")
|
|
26
27
|
*/
|
|
27
28
|
readonly name: string;
|
|
28
29
|
|