@mandujs/core 0.41.0 → 0.41.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/core",
3
- "version": "0.41.0",
3
+ "version": "0.41.2",
4
4
  "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -32,6 +32,7 @@ describe("resolveBrainAdapter — priority order", () => {
32
32
  adapter: "auto",
33
33
  credentialStore: store,
34
34
  probeOllama: async () => true,
35
+ probeChatGPTAuth: () => ({ authenticated: false, path: null }),
35
36
  });
36
37
  expect(res.resolved).toBe("openai");
37
38
  expect(res.adapter.name).toBe("openai-oauth");
@@ -43,6 +44,7 @@ describe("resolveBrainAdapter — priority order", () => {
43
44
  adapter: "auto",
44
45
  credentialStore: store,
45
46
  probeOllama: async () => true,
47
+ probeChatGPTAuth: () => ({ authenticated: false, path: null }),
46
48
  });
47
49
  expect(res.resolved).toBe("anthropic");
48
50
  expect(res.adapter.name).toBe("anthropic-oauth");
@@ -54,6 +56,7 @@ describe("resolveBrainAdapter — priority order", () => {
54
56
  adapter: "auto",
55
57
  credentialStore: store,
56
58
  probeOllama: async () => true,
59
+ probeChatGPTAuth: () => ({ authenticated: false, path: null }),
57
60
  });
58
61
  expect(res.resolved).toBe("ollama");
59
62
  expect(res.adapter.name).toBe("ollama");
@@ -65,6 +68,7 @@ describe("resolveBrainAdapter — priority order", () => {
65
68
  adapter: "auto",
66
69
  credentialStore: store,
67
70
  probeOllama: async () => false,
71
+ probeChatGPTAuth: () => ({ authenticated: false, path: null }),
68
72
  });
69
73
  expect(res.resolved).toBe("template");
70
74
  expect(res.adapter.name).toBe("noop");
@@ -104,6 +108,7 @@ describe("resolveBrainAdapter — explicit pins degrade gracefully", () => {
104
108
  const res = await resolveBrainAdapter({
105
109
  adapter: "openai",
106
110
  credentialStore: store,
111
+ probeChatGPTAuth: () => ({ authenticated: false, path: null }),
107
112
  });
108
113
  expect(res.resolved).toBe("template");
109
114
  expect(res.reason).toContain("no token");
@@ -34,6 +34,7 @@ import {
34
34
  createOpenAIOAuthAdapter,
35
35
  type OpenAIOAuthAdapterOptions,
36
36
  } from "./openai-oauth";
37
+ import { ChatGPTAuth } from "./chatgpt-auth";
37
38
  import {
38
39
  AnthropicOAuthAdapter,
39
40
  createAnthropicOAuthAdapter,
@@ -82,6 +83,13 @@ export interface BrainAdapterConfig {
82
83
  * default consults `credentialStore.load(provider)`.
83
84
  */
84
85
  probeToken?: (provider: "openai" | "anthropic") => Promise<StoredToken | null>;
86
+ /**
87
+ * Override the ChatGPT session-token probe. Default: instantiate
88
+ * `new ChatGPTAuth()` and check its on-disk auth.json. Tests inject
89
+ * a stub returning `false` so the developer's real `~/.codex/auth.json`
90
+ * doesn't leak into unit-test expectations.
91
+ */
92
+ probeChatGPTAuth?: () => { authenticated: boolean; path: string | null };
85
93
  }
86
94
 
87
95
  /**
@@ -122,6 +130,13 @@ export async function resolveBrainAdapter(
122
130
  config.probeOllama ??
123
131
  (async (ollama: OllamaAdapter) => ollama.isServerRunning());
124
132
 
133
+ const probeChatGPTAuth =
134
+ config.probeChatGPTAuth ??
135
+ (() => {
136
+ const c = new ChatGPTAuth();
137
+ return { authenticated: c.isAuthenticated(), path: c.locateAuthFile() };
138
+ });
139
+
125
140
  // Explicit template — skip every other check.
126
141
  if (requested === "template") {
127
142
  return {
@@ -145,14 +160,17 @@ export async function resolveBrainAdapter(
145
160
  "adapter: 'openai' requested but telemetryOptOut=true — forcing template",
146
161
  };
147
162
  }
148
- const token = await probeToken("openai");
149
- if (!token) {
163
+ // Primary: ChatGPT session token (written by `@openai/codex login`).
164
+ const cg = probeChatGPTAuth();
165
+ const hasChatGPT = cg.authenticated;
166
+ const token = hasChatGPT ? null : await probeToken("openai");
167
+ if (!hasChatGPT && !token) {
150
168
  return {
151
169
  adapter: new NoopAdapter(),
152
170
  resolved: "template",
153
171
  requested,
154
172
  reason:
155
- "adapter: 'openai' requested but no token in keychain — run `mandu brain login --provider=openai`",
173
+ "adapter: 'openai' requested but no token found — run `mandu brain login --provider=openai`",
156
174
  };
157
175
  }
158
176
  return {
@@ -164,7 +182,9 @@ export async function resolveBrainAdapter(
164
182
  }),
165
183
  resolved: "openai",
166
184
  requested,
167
- reason: "Explicit adapter: 'openai' + token present",
185
+ reason: hasChatGPT
186
+ ? "Explicit adapter: 'openai' + ChatGPT session token present"
187
+ : "Explicit adapter: 'openai' + keychain token present",
168
188
  };
169
189
  }
170
190
 
@@ -217,6 +237,21 @@ export async function resolveBrainAdapter(
217
237
  // Auto — try cloud providers first (when allowed), then ollama,
218
238
  // then template.
219
239
  if (!telemetryOptOut) {
240
+ // Primary: ChatGPT session token (managed by `@openai/codex`).
241
+ const cg2 = probeChatGPTAuth();
242
+ if (cg2.authenticated) {
243
+ return {
244
+ adapter: createOpenAIOAuthAdapter({
245
+ ...(config.openaiOptions ?? {}),
246
+ model: config.openai?.model ?? config.openaiOptions?.model,
247
+ credentialStore: store,
248
+ projectRoot,
249
+ }),
250
+ resolved: "openai",
251
+ requested,
252
+ reason: `auto: ChatGPT session token at ${cg2.path ?? "(unknown)"}`,
253
+ };
254
+ }
220
255
  const openaiToken = await probeToken("openai");
221
256
  if (openaiToken) {
222
257
  return {
package/src/index.ts CHANGED
@@ -1,3 +1,26 @@
1
+ /**
2
+ * Version marker — consumers (notably `@mandujs/mcp`) use this to detect
3
+ * that a stale nested copy of `@mandujs/core` was resolved and produce
4
+ * a helpful error instead of a cryptic "X is not a function". See #236.
5
+ *
6
+ * Read directly from the package's own `package.json` at module load
7
+ * time so the value can NEVER drift from the real published version.
8
+ * Best-effort — falls back to "unknown" if the file cannot be read
9
+ * (e.g. when the bundler strips `package.json` from the tree; we never
10
+ * strip it but keep this defensive).
11
+ */
12
+ export const __MANDU_CORE_VERSION__: string = (() => {
13
+ try {
14
+ // `import.meta.dir` → `<install>/src`; package.json is one level up.
15
+ const pkgPath = `${import.meta.dir}/../package.json`;
16
+ const raw = require("node:fs").readFileSync(pkgPath, "utf-8") as string;
17
+ const parsed = JSON.parse(raw) as { version?: string };
18
+ return typeof parsed.version === "string" ? parsed.version : "unknown";
19
+ } catch {
20
+ return "unknown";
21
+ }
22
+ })();
23
+
1
24
  export * from "./spec";
2
25
  export * from "./runtime";
3
26
  export * from "./generator";