@pablo2410/core-server 0.1.1 → 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.
package/dist/auth.d.ts ADDED
@@ -0,0 +1,172 @@
1
+ import * as express from 'express';
2
+ import { Request, CookieOptions } from 'express';
3
+
4
+ /** Payload stored inside the session JWT. */
5
+ interface SessionPayload {
6
+ openId: string;
7
+ appId: string;
8
+ name: string;
9
+ }
10
+ /** Minimal portal user shape returned by the portal API. */
11
+ interface PortalUser {
12
+ id: number;
13
+ openId: string;
14
+ name: string | null;
15
+ email: string | null;
16
+ role: string;
17
+ companyId?: number;
18
+ siteId?: number;
19
+ }
20
+ interface ExchangeTokenResponse {
21
+ accessToken: string;
22
+ tokenType: string;
23
+ expiresIn: number;
24
+ refreshToken?: string;
25
+ scope: string;
26
+ idToken: string;
27
+ }
28
+ interface GetUserInfoResponse {
29
+ openId: string;
30
+ projectId: string;
31
+ name: string;
32
+ email?: string | null;
33
+ platform?: string | null;
34
+ loginMethod?: string | null;
35
+ }
36
+ interface GetUserInfoWithJwtResponse {
37
+ openId: string;
38
+ projectId: string;
39
+ name: string;
40
+ email?: string | null;
41
+ platform?: string | null;
42
+ loginMethod?: string | null;
43
+ }
44
+ /** Environment variables required by the auth SDK. */
45
+ interface AuthEnv {
46
+ /** Manus app ID (VITE_APP_ID) */
47
+ appId: string;
48
+ /** JWT secret for signing local sessions (JWT_SECRET) */
49
+ jwtSecret: string;
50
+ /** Shared portal JWT secret for cross-subdomain SSO (PORTAL_JWT_SECRET) */
51
+ portalJwtSecret: string;
52
+ /** Cookie domain for cross-subdomain sharing, e.g. ".oplytics.digital" */
53
+ cookieDomain: string;
54
+ /** Manus OAuth server URL */
55
+ oAuthServerUrl: string;
56
+ /** Owner's openId for auto-admin assignment */
57
+ ownerOpenId: string;
58
+ /** Portal API base URL for user enrichment */
59
+ portalApiUrl: string;
60
+ /** Portal API key sent as X-Service-Key header */
61
+ portalApiKey: string;
62
+ }
63
+ /**
64
+ * Minimal user shape that the auth SDK works with.
65
+ * Subdomains extend this with their own fields.
66
+ */
67
+ interface BaseUser {
68
+ id: number;
69
+ openId: string;
70
+ name: string | null;
71
+ email: string | null;
72
+ role: string;
73
+ enterpriseId: number | null;
74
+ portalUserId: number | null;
75
+ }
76
+ /**
77
+ * Minimal upsert payload for creating/updating users.
78
+ * Subdomains can extend with additional fields.
79
+ */
80
+ interface UpsertUserPayload {
81
+ openId: string;
82
+ name?: string | null;
83
+ email?: string | null;
84
+ loginMethod?: string | null;
85
+ role?: string;
86
+ lastSignedIn?: Date;
87
+ enterpriseId?: number;
88
+ portalUserId?: number;
89
+ }
90
+ /**
91
+ * Database adapter that each subdomain must provide.
92
+ * This decouples the auth SDK from any specific ORM or schema.
93
+ */
94
+ interface DbAdapter<TUser extends BaseUser = BaseUser> {
95
+ /** Find a user by their Manus openId. Returns null/undefined if not found. */
96
+ getUserByOpenId(openId: string): Promise<TUser | null | undefined>;
97
+ /** Create or update a user record. */
98
+ upsertUser(payload: UpsertUserPayload): Promise<void>;
99
+ /**
100
+ * Update specific fields on an existing user by their local DB id.
101
+ * Used for enterprise backfill after portal enrichment.
102
+ */
103
+ updateUserFields(userId: number, fields: Partial<Pick<BaseUser, "enterpriseId" | "portalUserId">>): Promise<void>;
104
+ }
105
+ /**
106
+ * Maps a portal role string (e.g. "platform_admin", "enterprise_admin")
107
+ * to the subdomain's local role enum value.
108
+ */
109
+ type RoleMapper = (portalRole: string) => string;
110
+ /** Configuration object passed to createSubdomainAuth. */
111
+ interface SubdomainAuthConfig<TUser extends BaseUser = BaseUser> {
112
+ /** Environment variables for auth. */
113
+ env: AuthEnv;
114
+ /** Database adapter for user CRUD. */
115
+ db: DbAdapter<TUser>;
116
+ /** Maps portal roles to local roles. */
117
+ mapRole: RoleMapper;
118
+ /** Cookie name (defaults to "app_session_id"). */
119
+ cookieName?: string;
120
+ /** Session expiry in ms (defaults to 1 year). */
121
+ sessionExpiryMs?: number;
122
+ /** Service name for logging (e.g. "SQDCP", "OEE Manager"). */
123
+ serviceName?: string;
124
+ }
125
+ /** The auth SDK instance returned by createSubdomainAuth. */
126
+ interface SubdomainAuth<TUser extends BaseUser = BaseUser> {
127
+ /** Verify a session cookie value. Returns payload or null. */
128
+ verifySession(cookieValue: string | undefined | null): Promise<SessionPayload | null>;
129
+ /** Create a signed session JWT for a given openId. */
130
+ createSessionToken(openId: string, options?: {
131
+ expiresInMs?: number;
132
+ name?: string;
133
+ }): Promise<string>;
134
+ /**
135
+ * Full authentication flow: verify cookie → find/create user → enrich
136
+ * from portal → backfill enterprise. Returns the local user record.
137
+ */
138
+ authenticateRequest(req: Request): Promise<TUser>;
139
+ /** Exchange an OAuth authorization code for tokens. */
140
+ exchangeCodeForToken(code: string, state: string): Promise<ExchangeTokenResponse>;
141
+ /** Get user info from Manus OAuth using an access token. */
142
+ getUserInfo(accessToken: string): Promise<GetUserInfoResponse>;
143
+ /** Get user info from Manus OAuth using a JWT. */
144
+ getUserInfoWithJwt(jwtToken: string): Promise<GetUserInfoWithJwtResponse>;
145
+ /** Fetch a user from the portal API by openId. */
146
+ getPortalUser(openId: string): Promise<PortalUser | null>;
147
+ /** Get cookie options for setting session cookies. */
148
+ getSessionCookieOptions(req: Request): Pick<CookieOptions, "domain" | "httpOnly" | "path" | "sameSite" | "secure">;
149
+ /** Validate a returnTo URL to prevent open-redirect attacks. */
150
+ isValidReturnUrl(url: string): boolean;
151
+ /** Register the /api/oauth/callback route on an Express app. */
152
+ registerOAuthRoutes(app: express.Express): void;
153
+ }
154
+
155
+ /**
156
+ * createSubdomainAuth — shared SSO auth factory for Oplytics.digital subdomains.
157
+ *
158
+ * Encapsulates:
159
+ * - JWT session verification (PORTAL_JWT_SECRET first, fallback to JWT_SECRET)
160
+ * - Cookie domain handling via COOKIE_DOMAIN env var
161
+ * - Portal API user enrichment with X-Service-Key header
162
+ * - Auto-create user from JWT payload if not in local DB
163
+ * - Enterprise backfill from portal API
164
+ * - Manus OAuth code exchange and user info
165
+ * - OAuth callback route registration
166
+ *
167
+ * Based on SQDCP's battle-tested sdk.ts + cookies.ts.
168
+ */
169
+
170
+ declare function createSubdomainAuth<TUser extends BaseUser = BaseUser>(config: SubdomainAuthConfig<TUser>): SubdomainAuth<TUser>;
171
+
172
+ export { type AuthEnv, type BaseUser, type DbAdapter, type ExchangeTokenResponse, type GetUserInfoResponse, type GetUserInfoWithJwtResponse, type PortalUser, type RoleMapper, type SessionPayload, type SubdomainAuth, type SubdomainAuthConfig, type UpsertUserPayload, createSubdomainAuth };
package/dist/auth.js ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ createSubdomainAuth
3
+ } from "./chunk-BSM4MWQQ.js";
4
+ export {
5
+ createSubdomainAuth
6
+ };
7
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,397 @@
1
+ // src/auth/createSubdomainAuth.ts
2
+ var DEFAULT_COOKIE_NAME = "app_session_id";
3
+ var ONE_YEAR_MS = 1e3 * 60 * 60 * 24 * 365;
4
+ var AXIOS_TIMEOUT_MS = 3e4;
5
+ var EXCHANGE_TOKEN_PATH = "/webdev.v1.WebDevAuthPublicService/ExchangeToken";
6
+ var GET_USER_INFO_PATH = "/webdev.v1.WebDevAuthPublicService/GetUserInfo";
7
+ var GET_USER_INFO_WITH_JWT_PATH = "/webdev.v1.WebDevAuthPublicService/GetUserInfoWithJwt";
8
+ var isNonEmptyString = (value) => typeof value === "string" && value.length > 0;
9
+ function deriveLoginMethod(platforms, fallback) {
10
+ if (fallback && fallback.length > 0) return fallback;
11
+ if (!Array.isArray(platforms) || platforms.length === 0) return null;
12
+ const set = new Set(
13
+ platforms.filter((p) => typeof p === "string")
14
+ );
15
+ if (set.has("REGISTERED_PLATFORM_EMAIL")) return "email";
16
+ if (set.has("REGISTERED_PLATFORM_GOOGLE")) return "google";
17
+ if (set.has("REGISTERED_PLATFORM_APPLE")) return "apple";
18
+ if (set.has("REGISTERED_PLATFORM_MICROSOFT") || set.has("REGISTERED_PLATFORM_AZURE"))
19
+ return "microsoft";
20
+ if (set.has("REGISTERED_PLATFORM_GITHUB")) return "github";
21
+ const first = Array.from(set)[0];
22
+ return first ? first.toLowerCase() : null;
23
+ }
24
+ function createSubdomainAuth(config) {
25
+ const {
26
+ env,
27
+ db,
28
+ mapRole,
29
+ cookieName = DEFAULT_COOKIE_NAME,
30
+ sessionExpiryMs = ONE_YEAR_MS,
31
+ serviceName = "Subdomain"
32
+ } = config;
33
+ const tag = `[${serviceName} Auth]`;
34
+ let _jose = null;
35
+ async function getJose() {
36
+ if (!_jose) _jose = await import("jose");
37
+ return _jose;
38
+ }
39
+ let _cookie = null;
40
+ async function getCookie() {
41
+ if (!_cookie) _cookie = await import("cookie");
42
+ return _cookie;
43
+ }
44
+ let _axios = null;
45
+ async function getAxios() {
46
+ if (!_axios) {
47
+ const mod = await import("axios");
48
+ _axios = mod.default;
49
+ }
50
+ return _axios;
51
+ }
52
+ let oauthClient = null;
53
+ async function getOAuthClient() {
54
+ if (oauthClient) return oauthClient;
55
+ const axios = await getAxios();
56
+ oauthClient = axios.create({
57
+ baseURL: env.oAuthServerUrl,
58
+ timeout: AXIOS_TIMEOUT_MS
59
+ });
60
+ return oauthClient;
61
+ }
62
+ let portalClient = null;
63
+ function getPortalClient() {
64
+ if (portalClient) return portalClient;
65
+ if (!env.portalApiUrl) return null;
66
+ return null;
67
+ }
68
+ async function getPortalClientAsync() {
69
+ if (portalClient) return portalClient;
70
+ if (!env.portalApiUrl) return null;
71
+ const axios = await getAxios();
72
+ portalClient = axios.create({
73
+ baseURL: env.portalApiUrl,
74
+ timeout: 1e4,
75
+ headers: {
76
+ "Content-Type": "application/json",
77
+ ...env.portalApiKey ? { "X-Service-Key": env.portalApiKey } : {}
78
+ }
79
+ });
80
+ return portalClient;
81
+ }
82
+ function getPortalSecret() {
83
+ const secret = env.portalJwtSecret;
84
+ if (!secret) throw new Error("PORTAL_JWT_SECRET is not configured");
85
+ return new TextEncoder().encode(secret);
86
+ }
87
+ function getSessionSecret() {
88
+ const secret = env.portalJwtSecret || env.jwtSecret;
89
+ return new TextEncoder().encode(secret);
90
+ }
91
+ function getCookieDomain() {
92
+ return env.cookieDomain || void 0;
93
+ }
94
+ function isSecureRequest(req) {
95
+ if (req.protocol === "https") return true;
96
+ const forwardedProto = req.headers["x-forwarded-proto"];
97
+ if (!forwardedProto) return false;
98
+ const protoList = Array.isArray(forwardedProto) ? forwardedProto : forwardedProto.split(",");
99
+ return protoList.some((p) => p.trim().toLowerCase() === "https");
100
+ }
101
+ function getSessionCookieOptions(req) {
102
+ const secure = isSecureRequest(req);
103
+ const domain = getCookieDomain();
104
+ return {
105
+ domain,
106
+ httpOnly: true,
107
+ path: "/",
108
+ sameSite: "lax",
109
+ secure
110
+ };
111
+ }
112
+ function isValidReturnUrl(url) {
113
+ if (url.startsWith("/") && !url.startsWith("//")) return true;
114
+ try {
115
+ const parsed = new URL(url);
116
+ if (parsed.protocol !== "https:") return false;
117
+ return parsed.hostname === "oplytics.digital" || parsed.hostname.endsWith(".oplytics.digital");
118
+ } catch {
119
+ return false;
120
+ }
121
+ }
122
+ async function signSession(payload, options = {}) {
123
+ const jose = await getJose();
124
+ const issuedAt = Date.now();
125
+ const expiresInMs = options.expiresInMs ?? sessionExpiryMs;
126
+ const expirationSeconds = Math.floor((issuedAt + expiresInMs) / 1e3);
127
+ const secretKey = getSessionSecret();
128
+ return new jose.SignJWT({
129
+ openId: payload.openId,
130
+ appId: payload.appId,
131
+ name: payload.name
132
+ }).setProtectedHeader({ alg: "HS256", typ: "JWT" }).setExpirationTime(expirationSeconds).sign(secretKey);
133
+ }
134
+ async function createSessionToken(openId, options = {}) {
135
+ return signSession(
136
+ { openId, appId: env.appId, name: options.name || "" },
137
+ options
138
+ );
139
+ }
140
+ async function verifySession(cookieValue) {
141
+ if (!cookieValue) {
142
+ console.warn(`${tag} Missing session cookie`);
143
+ return null;
144
+ }
145
+ const jose = await getJose();
146
+ if (env.portalJwtSecret) {
147
+ try {
148
+ const portalKey = getPortalSecret();
149
+ const { payload } = await jose.jwtVerify(cookieValue, portalKey, {
150
+ algorithms: ["HS256"]
151
+ });
152
+ const { openId, appId, name } = payload;
153
+ if (!isNonEmptyString(openId) || !isNonEmptyString(appId) || !isNonEmptyString(name)) {
154
+ console.warn(`${tag} Portal JWT payload missing required fields`);
155
+ return null;
156
+ }
157
+ return { openId, appId, name };
158
+ } catch {
159
+ }
160
+ }
161
+ try {
162
+ const secretKey = getSessionSecret();
163
+ const { payload } = await jose.jwtVerify(cookieValue, secretKey, {
164
+ algorithms: ["HS256"]
165
+ });
166
+ const { openId, appId, name } = payload;
167
+ if (!isNonEmptyString(openId) || !isNonEmptyString(appId) || !isNonEmptyString(name)) {
168
+ console.warn(`${tag} Session payload missing required fields`);
169
+ return null;
170
+ }
171
+ return { openId, appId, name };
172
+ } catch (error) {
173
+ console.warn(
174
+ `${tag} Session verification failed:`,
175
+ error.message
176
+ );
177
+ return null;
178
+ }
179
+ }
180
+ async function getPortalUser(openId) {
181
+ try {
182
+ const client = await getPortalClientAsync();
183
+ if (!client) return null;
184
+ const { data } = await client.get(
185
+ `/users/by-openid/${openId}`
186
+ );
187
+ return data ?? null;
188
+ } catch (err) {
189
+ const status = err?.response?.status;
190
+ if (status === 401) {
191
+ console.error(
192
+ `${tag} Portal API key revoked (401) \u2014 enrichment disabled`
193
+ );
194
+ } else if (status === 404) {
195
+ } else {
196
+ console.warn(
197
+ `${tag} Portal user lookup failed:`,
198
+ err.message
199
+ );
200
+ }
201
+ return null;
202
+ }
203
+ }
204
+ async function exchangeCodeForToken(code, state) {
205
+ const client = await getOAuthClient();
206
+ const redirectUri = atob(state);
207
+ const { data } = await client.post(
208
+ EXCHANGE_TOKEN_PATH,
209
+ {
210
+ clientId: env.appId,
211
+ grantType: "authorization_code",
212
+ code,
213
+ redirectUri
214
+ }
215
+ );
216
+ return data;
217
+ }
218
+ async function getUserInfo(accessToken) {
219
+ const client = await getOAuthClient();
220
+ const { data } = await client.post(
221
+ GET_USER_INFO_PATH,
222
+ { accessToken }
223
+ );
224
+ const loginMethod = deriveLoginMethod(
225
+ data?.platforms,
226
+ data?.platform ?? data.platform ?? null
227
+ );
228
+ return { ...data, platform: loginMethod, loginMethod };
229
+ }
230
+ async function getUserInfoWithJwt(jwtToken) {
231
+ const client = await getOAuthClient();
232
+ const { data } = await client.post(
233
+ GET_USER_INFO_WITH_JWT_PATH,
234
+ { jwtToken, projectId: env.appId }
235
+ );
236
+ const loginMethod = deriveLoginMethod(
237
+ data?.platforms,
238
+ data?.platform ?? data.platform ?? null
239
+ );
240
+ return { ...data, platform: loginMethod, loginMethod };
241
+ }
242
+ async function authenticateRequest(req) {
243
+ const cookie = await getCookie();
244
+ const cookies = req.headers.cookie ? new Map(Object.entries(cookie.parse(req.headers.cookie))) : /* @__PURE__ */ new Map();
245
+ const sessionCookie = cookies.get(cookieName);
246
+ const session = await verifySession(sessionCookie);
247
+ if (!session) {
248
+ throw Object.assign(new Error("Invalid session cookie"), {
249
+ statusCode: 403
250
+ });
251
+ }
252
+ const sessionUserId = session.openId;
253
+ const signedInAt = /* @__PURE__ */ new Date();
254
+ let user = await db.getUserByOpenId(sessionUserId) ?? null;
255
+ if (!user) {
256
+ try {
257
+ const portalUser = await getPortalUser(sessionUserId);
258
+ if (portalUser) {
259
+ console.log(
260
+ `${tag} Cross-subdomain SSO: creating user from portal (${portalUser.email || sessionUserId})`
261
+ );
262
+ await db.upsertUser({
263
+ openId: sessionUserId,
264
+ name: portalUser.name || session.name || null,
265
+ email: portalUser.email ?? null,
266
+ loginMethod: "portal-sso",
267
+ role: mapRole(portalUser.role),
268
+ lastSignedIn: signedInAt,
269
+ ...portalUser.companyId ? {
270
+ enterpriseId: portalUser.companyId,
271
+ portalUserId: portalUser.id
272
+ } : {}
273
+ });
274
+ user = await db.getUserByOpenId(sessionUserId) ?? null;
275
+ } else {
276
+ console.log(
277
+ `${tag} Cross-subdomain SSO: creating user from JWT payload (${sessionUserId})`
278
+ );
279
+ await db.upsertUser({
280
+ openId: sessionUserId,
281
+ name: session.name || null,
282
+ loginMethod: "portal-sso",
283
+ lastSignedIn: signedInAt
284
+ });
285
+ user = await db.getUserByOpenId(sessionUserId) ?? null;
286
+ }
287
+ } catch (error) {
288
+ try {
289
+ const userInfo = await getUserInfoWithJwt(sessionCookie ?? "");
290
+ await db.upsertUser({
291
+ openId: userInfo.openId,
292
+ name: userInfo.name || null,
293
+ email: userInfo.email ?? null,
294
+ loginMethod: userInfo.loginMethod ?? userInfo.platform ?? null,
295
+ lastSignedIn: signedInAt
296
+ });
297
+ user = await db.getUserByOpenId(userInfo.openId) ?? null;
298
+ } catch (oauthError) {
299
+ console.error(
300
+ `${tag} All user sync methods failed:`,
301
+ error,
302
+ oauthError
303
+ );
304
+ throw Object.assign(new Error("Failed to sync user info"), {
305
+ statusCode: 403
306
+ });
307
+ }
308
+ }
309
+ }
310
+ if (!user) {
311
+ throw Object.assign(new Error("User not found"), { statusCode: 403 });
312
+ }
313
+ if (user.enterpriseId === null) {
314
+ try {
315
+ const portalUser = await getPortalUser(user.openId);
316
+ if (portalUser?.companyId) {
317
+ console.log(
318
+ `${tag} Backfilling enterpriseId=${portalUser.companyId} for user ${user.id}`
319
+ );
320
+ await db.updateUserFields(user.id, {
321
+ enterpriseId: portalUser.companyId,
322
+ ...portalUser.id ? { portalUserId: portalUser.id } : {}
323
+ });
324
+ user = await db.getUserByOpenId(user.openId) ?? user;
325
+ }
326
+ } catch (backfillError) {
327
+ console.warn(
328
+ `${tag} Failed to backfill enterpriseId for user ${user.id}:`,
329
+ backfillError
330
+ );
331
+ }
332
+ }
333
+ await db.upsertUser({
334
+ openId: user.openId,
335
+ lastSignedIn: signedInAt
336
+ });
337
+ return user;
338
+ }
339
+ function registerOAuthRoutes(app) {
340
+ app.get(
341
+ "/api/oauth/callback",
342
+ async (req, res) => {
343
+ const code = typeof req.query.code === "string" ? req.query.code : void 0;
344
+ const state = typeof req.query.state === "string" ? req.query.state : void 0;
345
+ if (!code || !state) {
346
+ res.status(400).json({ error: "code and state are required" });
347
+ return;
348
+ }
349
+ try {
350
+ const tokenResponse = await exchangeCodeForToken(code, state);
351
+ const userInfo = await getUserInfo(tokenResponse.accessToken);
352
+ if (!userInfo.openId) {
353
+ res.status(400).json({ error: "openId missing from user info" });
354
+ return;
355
+ }
356
+ await db.upsertUser({
357
+ openId: userInfo.openId,
358
+ name: userInfo.name || null,
359
+ email: userInfo.email ?? null,
360
+ loginMethod: userInfo.loginMethod ?? userInfo.platform ?? null,
361
+ lastSignedIn: /* @__PURE__ */ new Date()
362
+ });
363
+ const sessionToken = await createSessionToken(userInfo.openId, {
364
+ name: userInfo.name || "",
365
+ expiresInMs: sessionExpiryMs
366
+ });
367
+ const cookieOptions = getSessionCookieOptions(req);
368
+ res.cookie(cookieName, sessionToken, {
369
+ ...cookieOptions,
370
+ maxAge: sessionExpiryMs
371
+ });
372
+ res.redirect(302, "/");
373
+ } catch (error) {
374
+ console.error(`${tag} OAuth callback failed`, error);
375
+ res.status(500).json({ error: "OAuth callback failed" });
376
+ }
377
+ }
378
+ );
379
+ }
380
+ return {
381
+ verifySession,
382
+ createSessionToken,
383
+ authenticateRequest,
384
+ exchangeCodeForToken,
385
+ getUserInfo,
386
+ getUserInfoWithJwt,
387
+ getPortalUser,
388
+ getSessionCookieOptions,
389
+ isValidReturnUrl,
390
+ registerOAuthRoutes
391
+ };
392
+ }
393
+
394
+ export {
395
+ createSubdomainAuth
396
+ };
397
+ //# sourceMappingURL=chunk-BSM4MWQQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth/createSubdomainAuth.ts"],"sourcesContent":["/**\n * createSubdomainAuth — shared SSO auth factory for Oplytics.digital subdomains.\n *\n * Encapsulates:\n * - JWT session verification (PORTAL_JWT_SECRET first, fallback to JWT_SECRET)\n * - Cookie domain handling via COOKIE_DOMAIN env var\n * - Portal API user enrichment with X-Service-Key header\n * - Auto-create user from JWT payload if not in local DB\n * - Enterprise backfill from portal API\n * - Manus OAuth code exchange and user info\n * - OAuth callback route registration\n *\n * Based on SQDCP's battle-tested sdk.ts + cookies.ts.\n */\n\nimport type { Request, CookieOptions } from \"express\";\nimport type {\n AuthEnv,\n BaseUser,\n DbAdapter,\n ExchangeTokenResponse,\n GetUserInfoResponse,\n GetUserInfoWithJwtResponse,\n PortalUser,\n RoleMapper,\n SessionPayload,\n SubdomainAuth,\n SubdomainAuthConfig,\n} from \"./types\";\n\n// ─── Constants ──────────────────────────────────────────────────────\n\nconst DEFAULT_COOKIE_NAME = \"app_session_id\";\nconst ONE_YEAR_MS = 1000 * 60 * 60 * 24 * 365;\nconst AXIOS_TIMEOUT_MS = 30_000;\n\nconst EXCHANGE_TOKEN_PATH =\n \"/webdev.v1.WebDevAuthPublicService/ExchangeToken\";\nconst GET_USER_INFO_PATH =\n \"/webdev.v1.WebDevAuthPublicService/GetUserInfo\";\nconst GET_USER_INFO_WITH_JWT_PATH =\n \"/webdev.v1.WebDevAuthPublicService/GetUserInfoWithJwt\";\n\n// ─── Helpers ────────────────────────────────────────────────────────\n\nconst isNonEmptyString = (value: unknown): value is string =>\n typeof value === \"string\" && value.length > 0;\n\n/**\n * Derive the login method from the Manus platforms array.\n */\nfunction deriveLoginMethod(\n platforms: unknown,\n fallback: string | null | undefined\n): string | null {\n if (fallback && fallback.length > 0) return fallback;\n if (!Array.isArray(platforms) || platforms.length === 0) return null;\n const set = new Set<string>(\n platforms.filter((p): p is string => typeof p === \"string\")\n );\n if (set.has(\"REGISTERED_PLATFORM_EMAIL\")) return \"email\";\n if (set.has(\"REGISTERED_PLATFORM_GOOGLE\")) return \"google\";\n if (set.has(\"REGISTERED_PLATFORM_APPLE\")) return \"apple\";\n if (\n set.has(\"REGISTERED_PLATFORM_MICROSOFT\") ||\n set.has(\"REGISTERED_PLATFORM_AZURE\")\n )\n return \"microsoft\";\n if (set.has(\"REGISTERED_PLATFORM_GITHUB\")) return \"github\";\n const first = Array.from(set)[0];\n return first ? first.toLowerCase() : null;\n}\n\n// ─── Factory ────────────────────────────────────────────────────────\n\nexport function createSubdomainAuth<TUser extends BaseUser = BaseUser>(\n config: SubdomainAuthConfig<TUser>\n): SubdomainAuth<TUser> {\n const {\n env,\n db,\n mapRole,\n cookieName = DEFAULT_COOKIE_NAME,\n sessionExpiryMs = ONE_YEAR_MS,\n serviceName = \"Subdomain\",\n } = config;\n\n const tag = `[${serviceName} Auth]`;\n\n // ── Lazy-loaded dependencies (jose, cookie, axios) ──────────────\n // We use dynamic imports so consumers don't need to install these\n // as peer deps if they only use other core-server features.\n\n let _jose: typeof import(\"jose\") | null = null;\n async function getJose() {\n if (!_jose) _jose = await import(\"jose\");\n return _jose;\n }\n\n let _cookie: typeof import(\"cookie\") | null = null;\n async function getCookie() {\n if (!_cookie) _cookie = await import(\"cookie\");\n return _cookie;\n }\n\n let _axios: typeof import(\"axios\").default | null = null;\n async function getAxios() {\n if (!_axios) {\n const mod = await import(\"axios\");\n _axios = mod.default;\n }\n return _axios;\n }\n\n // ── OAuth HTTP client ───────────────────────────────────────────\n\n let oauthClient: import(\"axios\").AxiosInstance | null = null;\n async function getOAuthClient() {\n if (oauthClient) return oauthClient;\n const axios = await getAxios();\n oauthClient = axios.create({\n baseURL: env.oAuthServerUrl,\n timeout: AXIOS_TIMEOUT_MS,\n });\n return oauthClient;\n }\n\n // ── Portal API client ──────────────────────────────────────────\n\n let portalClient: import(\"axios\").AxiosInstance | null = null;\n function getPortalClient() {\n if (portalClient) return portalClient;\n // Synchronous check — we only create if we have the URL\n if (!env.portalApiUrl) return null;\n // We need axios synchronously here, but it should already be loaded\n // by the time portal client is needed (after authenticateRequest calls getAxios)\n return null; // Will be created lazily in async context\n }\n\n async function getPortalClientAsync() {\n if (portalClient) return portalClient;\n if (!env.portalApiUrl) return null;\n const axios = await getAxios();\n portalClient = axios.create({\n baseURL: env.portalApiUrl,\n timeout: 10_000,\n headers: {\n \"Content-Type\": \"application/json\",\n ...(env.portalApiKey ? { \"X-Service-Key\": env.portalApiKey } : {}),\n },\n });\n return portalClient;\n }\n\n // ── JWT Secrets ────────────────────────────────────────────────\n\n function getPortalSecret() {\n const secret = env.portalJwtSecret;\n if (!secret) throw new Error(\"PORTAL_JWT_SECRET is not configured\");\n return new TextEncoder().encode(secret);\n }\n\n function getSessionSecret() {\n // Prefer portalJwtSecret for signing so cookies work cross-subdomain\n const secret = env.portalJwtSecret || env.jwtSecret;\n return new TextEncoder().encode(secret);\n }\n\n // ── Cookie Helpers ─────────────────────────────────────────────\n\n function getCookieDomain(): string | undefined {\n return env.cookieDomain || undefined;\n }\n\n function isSecureRequest(req: Request): boolean {\n if (req.protocol === \"https\") return true;\n const forwardedProto = req.headers[\"x-forwarded-proto\"];\n if (!forwardedProto) return false;\n const protoList = Array.isArray(forwardedProto)\n ? forwardedProto\n : forwardedProto.split(\",\");\n return protoList.some((p) => p.trim().toLowerCase() === \"https\");\n }\n\n function getSessionCookieOptions(\n req: Request\n ): Pick<\n CookieOptions,\n \"domain\" | \"httpOnly\" | \"path\" | \"sameSite\" | \"secure\"\n > {\n const secure = isSecureRequest(req);\n const domain = getCookieDomain();\n return {\n domain,\n httpOnly: true,\n path: \"/\",\n sameSite: \"lax\",\n secure,\n };\n }\n\n function isValidReturnUrl(url: string): boolean {\n if (url.startsWith(\"/\") && !url.startsWith(\"//\")) return true;\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"https:\") return false;\n return (\n parsed.hostname === \"oplytics.digital\" ||\n parsed.hostname.endsWith(\".oplytics.digital\")\n );\n } catch {\n return false;\n }\n }\n\n // ── Session Token ──────────────────────────────────────────────\n\n async function signSession(\n payload: SessionPayload,\n options: { expiresInMs?: number } = {}\n ): Promise<string> {\n const jose = await getJose();\n const issuedAt = Date.now();\n const expiresInMs = options.expiresInMs ?? sessionExpiryMs;\n const expirationSeconds = Math.floor((issuedAt + expiresInMs) / 1000);\n const secretKey = getSessionSecret();\n\n return new jose.SignJWT({\n openId: payload.openId,\n appId: payload.appId,\n name: payload.name,\n })\n .setProtectedHeader({ alg: \"HS256\", typ: \"JWT\" })\n .setExpirationTime(expirationSeconds)\n .sign(secretKey);\n }\n\n async function createSessionToken(\n openId: string,\n options: { expiresInMs?: number; name?: string } = {}\n ): Promise<string> {\n return signSession(\n { openId, appId: env.appId, name: options.name || \"\" },\n options\n );\n }\n\n // ── Session Verification ───────────────────────────────────────\n\n async function verifySession(\n cookieValue: string | undefined | null\n ): Promise<SessionPayload | null> {\n if (!cookieValue) {\n console.warn(`${tag} Missing session cookie`);\n return null;\n }\n\n const jose = await getJose();\n\n // Try PORTAL_JWT_SECRET first (cross-subdomain SSO)\n if (env.portalJwtSecret) {\n try {\n const portalKey = getPortalSecret();\n const { payload } = await jose.jwtVerify(cookieValue, portalKey, {\n algorithms: [\"HS256\"],\n });\n const { openId, appId, name } = payload as Record<string, unknown>;\n if (\n !isNonEmptyString(openId) ||\n !isNonEmptyString(appId) ||\n !isNonEmptyString(name)\n ) {\n console.warn(`${tag} Portal JWT payload missing required fields`);\n return null;\n }\n return { openId, appId, name };\n } catch {\n // Portal secret didn't work — fall through to local secret\n }\n }\n\n // Fallback: try local JWT_SECRET\n try {\n const secretKey = getSessionSecret();\n const { payload } = await jose.jwtVerify(cookieValue, secretKey, {\n algorithms: [\"HS256\"],\n });\n const { openId, appId, name } = payload as Record<string, unknown>;\n if (\n !isNonEmptyString(openId) ||\n !isNonEmptyString(appId) ||\n !isNonEmptyString(name)\n ) {\n console.warn(`${tag} Session payload missing required fields`);\n return null;\n }\n return { openId, appId, name };\n } catch (error) {\n console.warn(\n `${tag} Session verification failed:`,\n (error as Error).message\n );\n return null;\n }\n }\n\n // ── Portal API ─────────────────────────────────────────────────\n\n async function getPortalUser(openId: string): Promise<PortalUser | null> {\n try {\n const client = await getPortalClientAsync();\n if (!client) return null;\n const { data } = await client.get<PortalUser>(\n `/users/by-openid/${openId}`\n );\n return data ?? null;\n } catch (err: unknown) {\n const status = (err as any)?.response?.status;\n if (status === 401) {\n console.error(\n `${tag} Portal API key revoked (401) — enrichment disabled`\n );\n } else if (status === 404) {\n // User not found in portal — normal for first-time users\n } else {\n console.warn(\n `${tag} Portal user lookup failed:`,\n (err as Error).message\n );\n }\n return null;\n }\n }\n\n // ── OAuth ──────────────────────────────────────────────────────\n\n async function exchangeCodeForToken(\n code: string,\n state: string\n ): Promise<ExchangeTokenResponse> {\n const client = await getOAuthClient();\n const redirectUri = atob(state);\n const { data } = await client.post<ExchangeTokenResponse>(\n EXCHANGE_TOKEN_PATH,\n {\n clientId: env.appId,\n grantType: \"authorization_code\",\n code,\n redirectUri,\n }\n );\n return data;\n }\n\n async function getUserInfo(\n accessToken: string\n ): Promise<GetUserInfoResponse> {\n const client = await getOAuthClient();\n const { data } = await client.post<GetUserInfoResponse>(\n GET_USER_INFO_PATH,\n { accessToken }\n );\n const loginMethod = deriveLoginMethod(\n (data as any)?.platforms,\n (data as any)?.platform ?? data.platform ?? null\n );\n return { ...(data as any), platform: loginMethod, loginMethod };\n }\n\n async function getUserInfoWithJwt(\n jwtToken: string\n ): Promise<GetUserInfoWithJwtResponse> {\n const client = await getOAuthClient();\n const { data } = await client.post<GetUserInfoWithJwtResponse>(\n GET_USER_INFO_WITH_JWT_PATH,\n { jwtToken, projectId: env.appId }\n );\n const loginMethod = deriveLoginMethod(\n (data as any)?.platforms,\n (data as any)?.platform ?? data.platform ?? null\n );\n return { ...(data as any), platform: loginMethod, loginMethod };\n }\n\n // ── Authenticate Request ───────────────────────────────────────\n\n async function authenticateRequest(req: Request): Promise<TUser> {\n const cookie = await getCookie();\n const cookies = req.headers.cookie\n ? new Map(Object.entries(cookie.parse(req.headers.cookie)))\n : new Map<string, string>();\n\n const sessionCookie = cookies.get(cookieName);\n const session = await verifySession(sessionCookie);\n\n if (!session) {\n throw Object.assign(new Error(\"Invalid session cookie\"), {\n statusCode: 403,\n });\n }\n\n const sessionUserId = session.openId;\n const signedInAt = new Date();\n let user = (await db.getUserByOpenId(sessionUserId)) ?? null;\n\n // ── Auto-create user from verified JWT ──────────────────────\n if (!user) {\n try {\n // Try portal API first for richer user info\n const portalUser = await getPortalUser(sessionUserId);\n\n if (portalUser) {\n console.log(\n `${tag} Cross-subdomain SSO: creating user from portal (${portalUser.email || sessionUserId})`\n );\n await db.upsertUser({\n openId: sessionUserId,\n name: portalUser.name || session.name || null,\n email: portalUser.email ?? null,\n loginMethod: \"portal-sso\",\n role: mapRole(portalUser.role),\n lastSignedIn: signedInAt,\n ...(portalUser.companyId\n ? {\n enterpriseId: portalUser.companyId,\n portalUserId: portalUser.id,\n }\n : {}),\n });\n user = (await db.getUserByOpenId(sessionUserId)) ?? null;\n } else {\n // Portal unavailable — create from JWT payload\n console.log(\n `${tag} Cross-subdomain SSO: creating user from JWT payload (${sessionUserId})`\n );\n await db.upsertUser({\n openId: sessionUserId,\n name: session.name || null,\n loginMethod: \"portal-sso\",\n lastSignedIn: signedInAt,\n });\n user = (await db.getUserByOpenId(sessionUserId)) ?? null;\n }\n } catch (error) {\n // Last resort: try Manus OAuth JWT\n try {\n const userInfo = await getUserInfoWithJwt(sessionCookie ?? \"\");\n await db.upsertUser({\n openId: userInfo.openId,\n name: userInfo.name || null,\n email: userInfo.email ?? null,\n loginMethod: userInfo.loginMethod ?? userInfo.platform ?? null,\n lastSignedIn: signedInAt,\n });\n user = (await db.getUserByOpenId(userInfo.openId)) ?? null;\n } catch (oauthError) {\n console.error(\n `${tag} All user sync methods failed:`,\n error,\n oauthError\n );\n throw Object.assign(new Error(\"Failed to sync user info\"), {\n statusCode: 403,\n });\n }\n }\n }\n\n if (!user) {\n throw Object.assign(new Error(\"User not found\"), { statusCode: 403 });\n }\n\n // ── Enterprise backfill ─────────────────────────────────────\n if (user.enterpriseId === null) {\n try {\n const portalUser = await getPortalUser(user.openId);\n if (portalUser?.companyId) {\n console.log(\n `${tag} Backfilling enterpriseId=${portalUser.companyId} for user ${user.id}`\n );\n await db.updateUserFields(user.id, {\n enterpriseId: portalUser.companyId,\n ...(portalUser.id ? { portalUserId: portalUser.id } : {}),\n });\n user = (await db.getUserByOpenId(user.openId)) ?? user;\n }\n } catch (backfillError) {\n console.warn(\n `${tag} Failed to backfill enterpriseId for user ${user.id}:`,\n backfillError\n );\n // Non-fatal — user can still proceed\n }\n }\n\n // Touch lastSignedIn\n await db.upsertUser({\n openId: user.openId,\n lastSignedIn: signedInAt,\n });\n\n return user;\n }\n\n // ── OAuth Route Registration ───────────────────────────────────\n\n function registerOAuthRoutes(app: import(\"express\").Express) {\n app.get(\n \"/api/oauth/callback\",\n async (req: Request, res: import(\"express\").Response) => {\n const code =\n typeof req.query.code === \"string\" ? req.query.code : undefined;\n const state =\n typeof req.query.state === \"string\" ? req.query.state : undefined;\n\n if (!code || !state) {\n res.status(400).json({ error: \"code and state are required\" });\n return;\n }\n\n try {\n const tokenResponse = await exchangeCodeForToken(code, state);\n const userInfo = await getUserInfo(tokenResponse.accessToken);\n\n if (!userInfo.openId) {\n res.status(400).json({ error: \"openId missing from user info\" });\n return;\n }\n\n await db.upsertUser({\n openId: userInfo.openId,\n name: userInfo.name || null,\n email: userInfo.email ?? null,\n loginMethod: userInfo.loginMethod ?? userInfo.platform ?? null,\n lastSignedIn: new Date(),\n });\n\n const sessionToken = await createSessionToken(userInfo.openId, {\n name: userInfo.name || \"\",\n expiresInMs: sessionExpiryMs,\n });\n\n const cookieOptions = getSessionCookieOptions(req);\n res.cookie(cookieName, sessionToken, {\n ...cookieOptions,\n maxAge: sessionExpiryMs,\n });\n\n res.redirect(302, \"/\");\n } catch (error) {\n console.error(`${tag} OAuth callback failed`, error);\n res.status(500).json({ error: \"OAuth callback failed\" });\n }\n }\n );\n }\n\n // ── Return public API ──────────────────────────────────────────\n\n return {\n verifySession,\n createSessionToken,\n authenticateRequest,\n exchangeCodeForToken,\n getUserInfo,\n getUserInfoWithJwt,\n getPortalUser,\n getSessionCookieOptions,\n isValidReturnUrl,\n registerOAuthRoutes,\n };\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ export { AuthEnv, BaseUser, DbAdapter, ExchangeTokenResponse, GetUserInfoResponse, GetUserInfoWithJwtResponse, PortalUser, RoleMapper, SessionPayload, SubdomainAuth, SubdomainAuthConfig, UpsertUserPayload, createSubdomainAuth } from './auth.js';
2
+ import 'express';
3
+
1
4
  /**
2
5
  * Minimal environment interface required by core-server utilities.
3
6
  * Each subdomain provides its own ENV object that satisfies this shape.
package/dist/index.js CHANGED
@@ -1,3 +1,7 @@
1
+ import {
2
+ createSubdomainAuth
3
+ } from "./chunk-BSM4MWQQ.js";
4
+
1
5
  // src/dataApi.ts
2
6
  function createDataApiClient(env) {
3
7
  return async function callDataApi(apiId, options = {}) {
@@ -462,6 +466,7 @@ export {
462
466
  createLLMClient,
463
467
  createMapClient,
464
468
  createNotificationClient,
469
+ createSubdomainAuth,
465
470
  createTranscriptionClient
466
471
  };
467
472
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dataApi.ts","../src/llm.ts","../src/imageGeneration.ts","../src/map.ts","../src/notification.ts","../src/voiceTranscription.ts"],"sourcesContent":["/**\n * Data API helper — calls the Manus built-in Data API Hub.\n *\n * Quick example:\n * const callDataApi = createDataApiClient(ENV);\n * await callDataApi(\"Youtube/search\", {\n * query: { gl: \"US\", hl: \"en\", q: \"manus\" },\n * });\n */\nimport type { CoreEnv } from \"./types\";\n\nexport type DataApiCallOptions = {\n query?: Record<string, unknown>;\n body?: Record<string, unknown>;\n pathParams?: Record<string, unknown>;\n formData?: Record<string, unknown>;\n};\n\nexport function createDataApiClient(env: CoreEnv) {\n return async function callDataApi(\n apiId: string,\n options: DataApiCallOptions = {}\n ): Promise<unknown> {\n if (!env.forgeApiUrl) {\n throw new Error(\"BUILT_IN_FORGE_API_URL is not configured\");\n }\n if (!env.forgeApiKey) {\n throw new Error(\"BUILT_IN_FORGE_API_KEY is not configured\");\n }\n\n const baseUrl = env.forgeApiUrl.endsWith(\"/\")\n ? env.forgeApiUrl\n : `${env.forgeApiUrl}/`;\n const fullUrl = new URL(\n \"webdevtoken.v1.WebDevService/CallApi\",\n baseUrl\n ).toString();\n\n const response = await fetch(fullUrl, {\n method: \"POST\",\n headers: {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n \"connect-protocol-version\": \"1\",\n authorization: `Bearer ${env.forgeApiKey}`,\n },\n body: JSON.stringify({\n apiId,\n query: options.query,\n body: options.body,\n path_params: options.pathParams,\n multipart_form_data: options.formData,\n }),\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `Data API request failed (${response.status} ${response.statusText})${detail ? `: ${detail}` : \"\"}`\n );\n }\n\n const payload = await response.json().catch(() => ({}));\n if (payload && typeof payload === \"object\" && \"jsonData\" in payload) {\n try {\n return JSON.parse(\n (payload as Record<string, string>).jsonData ?? \"{}\"\n );\n } catch {\n return (payload as Record<string, unknown>).jsonData;\n }\n }\n return payload;\n };\n}\n","/**\n * LLM helper — calls the Manus built-in LLM API (OpenAI-compatible).\n *\n * Example:\n * const invokeLLM = createLLMClient(ENV);\n * const result = await invokeLLM({\n * messages: [\n * { role: \"system\", content: \"You are a helpful assistant.\" },\n * { role: \"user\", content: \"Hello, world!\" },\n * ],\n * });\n */\nimport type { CoreEnv } from \"./types\";\n\nexport type Role = \"system\" | \"user\" | \"assistant\" | \"tool\" | \"function\";\n\nexport type TextContent = {\n type: \"text\";\n text: string;\n};\n\nexport type ImageContent = {\n type: \"image_url\";\n image_url: {\n url: string;\n detail?: \"auto\" | \"low\" | \"high\";\n };\n};\n\nexport type FileContent = {\n type: \"file_url\";\n file_url: {\n url: string;\n mime_type?:\n | \"audio/mpeg\"\n | \"audio/wav\"\n | \"application/pdf\"\n | \"audio/mp4\"\n | \"video/mp4\";\n };\n};\n\nexport type MessageContent = string | TextContent | ImageContent | FileContent;\n\nexport type Message = {\n role: Role;\n name?: string;\n tool_call_id?: string;\n content: MessageContent | MessageContent[];\n};\n\nexport type Tool = {\n type: \"function\";\n function: {\n name: string;\n description?: string;\n parameters?: Record<string, unknown>;\n };\n};\n\ntype ToolChoiceExplicit = {\n type: \"function\";\n function: { name: string };\n};\n\nexport type ToolChoice =\n | \"none\"\n | \"auto\"\n | \"required\"\n | { name: string }\n | ToolChoiceExplicit;\n\ntype JsonSchema = {\n name: string;\n schema: Record<string, unknown>;\n strict?: boolean;\n};\n\nexport type ResponseFormat =\n | { type: \"text\" }\n | { type: \"json_object\" }\n | { type: \"json_schema\"; json_schema: JsonSchema };\n\nexport type OutputSchema = {\n name: string;\n schema: Record<string, unknown>;\n strict?: boolean;\n};\n\nexport type InvokeParams = {\n messages: Message[];\n tools?: Tool[];\n toolChoice?: ToolChoice;\n tool_choice?: ToolChoice;\n outputSchema?: OutputSchema;\n output_schema?: OutputSchema;\n responseFormat?: ResponseFormat;\n response_format?: ResponseFormat;\n};\n\nexport type InvokeResult = {\n id: string;\n object: string;\n created: number;\n model: string;\n choices: Array<{\n index: number;\n message: {\n role: string;\n content: string | null;\n tool_calls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>;\n };\n finish_reason: string;\n }>;\n usage?: {\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n };\n};\n\nconst ensureArray = (\n value: MessageContent | MessageContent[]\n): MessageContent[] => (Array.isArray(value) ? value : [value]);\n\nconst normalizeContentPart = (\n part: MessageContent\n): TextContent | ImageContent | FileContent => {\n if (typeof part === \"string\") {\n return { type: \"text\", text: part };\n }\n if (part.type === \"text\") return part;\n if (part.type === \"image_url\") return part;\n if (part.type === \"file_url\") return part;\n throw new Error(\"Unsupported message content part\");\n};\n\nconst normalizeMessage = (message: Message) => {\n const { role, name, tool_call_id } = message;\n if (role === \"tool\" || role === \"function\") {\n const content = ensureArray(message.content)\n .map((part) => (typeof part === \"string\" ? part : JSON.stringify(part)))\n .join(\"\\n\");\n return { role, name, tool_call_id, content };\n }\n const contentParts = ensureArray(message.content).map(normalizeContentPart);\n if (contentParts.length === 1 && contentParts[0].type === \"text\") {\n return { role, name, content: contentParts[0].text };\n }\n return { role, name, content: contentParts };\n};\n\nconst normalizeToolChoice = (\n toolChoice: ToolChoice | undefined,\n tools: Tool[] | undefined\n): \"none\" | \"auto\" | ToolChoiceExplicit | undefined => {\n if (!toolChoice) return undefined;\n if (toolChoice === \"none\" || toolChoice === \"auto\") return toolChoice;\n if (toolChoice === \"required\") {\n if (!tools || tools.length === 0) {\n throw new Error(\n \"tool_choice 'required' was provided but no tools were configured\"\n );\n }\n if (tools.length > 1) {\n throw new Error(\n \"tool_choice 'required' needs a single tool or specify the tool name explicitly\"\n );\n }\n return { type: \"function\", function: { name: tools[0].function.name } };\n }\n if (\"name\" in toolChoice) {\n return { type: \"function\", function: { name: toolChoice.name } };\n }\n return toolChoice;\n};\n\nconst normalizeResponseFormat = ({\n responseFormat,\n response_format,\n outputSchema,\n output_schema,\n}: {\n responseFormat?: ResponseFormat;\n response_format?: ResponseFormat;\n outputSchema?: OutputSchema;\n output_schema?: OutputSchema;\n}):\n | { type: \"json_schema\"; json_schema: JsonSchema }\n | { type: \"text\" }\n | { type: \"json_object\" }\n | undefined => {\n const explicitFormat = responseFormat || response_format;\n if (explicitFormat) {\n if (\n explicitFormat.type === \"json_schema\" &&\n !explicitFormat.json_schema?.schema\n ) {\n throw new Error(\n \"responseFormat json_schema requires a defined schema object\"\n );\n }\n return explicitFormat;\n }\n const schema = outputSchema || output_schema;\n if (!schema) return undefined;\n if (!schema.name || !schema.schema) {\n throw new Error(\"outputSchema requires both name and schema\");\n }\n return {\n type: \"json_schema\",\n json_schema: {\n name: schema.name,\n schema: schema.schema,\n ...(typeof schema.strict === \"boolean\" ? { strict: schema.strict } : {}),\n },\n };\n};\n\nexport function createLLMClient(env: CoreEnv) {\n const resolveApiUrl = () =>\n env.forgeApiUrl && env.forgeApiUrl.trim().length > 0\n ? `${env.forgeApiUrl.replace(/\\/$/, \"\")}/v1/chat/completions`\n : \"https://forge.manus.im/v1/chat/completions\";\n\n const assertApiKey = () => {\n if (!env.forgeApiKey) {\n throw new Error(\"OPENAI_API_KEY is not configured\");\n }\n };\n\n return async function invokeLLM(\n params: InvokeParams\n ): Promise<InvokeResult> {\n assertApiKey();\n\n const {\n messages,\n tools,\n toolChoice,\n tool_choice,\n outputSchema,\n output_schema,\n responseFormat,\n response_format,\n } = params;\n\n const payload: Record<string, unknown> = {\n model: \"gemini-2.5-flash\",\n messages: messages.map(normalizeMessage),\n };\n\n if (tools && tools.length > 0) {\n payload.tools = tools;\n }\n\n const normalizedToolChoice = normalizeToolChoice(\n toolChoice || tool_choice,\n tools\n );\n if (normalizedToolChoice) {\n payload.tool_choice = normalizedToolChoice;\n }\n\n payload.max_tokens = 32768;\n payload.thinking = { budget_tokens: 128 };\n\n const normalizedResponseFormat = normalizeResponseFormat({\n responseFormat,\n response_format,\n outputSchema,\n output_schema,\n });\n if (normalizedResponseFormat) {\n payload.response_format = normalizedResponseFormat;\n }\n\n const response = await fetch(resolveApiUrl(), {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${env.forgeApiKey}`,\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `LLM invoke failed: ${response.status} ${response.statusText} – ${errorText}`\n );\n }\n\n return (await response.json()) as InvokeResult;\n };\n}\n","/**\n * Image generation helper using internal ImageService.\n *\n * Example:\n * import { storagePut } from \"../storage\";\n * const generateImage = createImageGenerator(ENV, storagePut);\n * const { url } = await generateImage({ prompt: \"A serene landscape\" });\n */\nimport type { CoreEnv, StoragePutFn } from \"./types\";\n\nexport type GenerateImageOptions = {\n prompt: string;\n originalImages?: Array<{\n url?: string;\n b64Json?: string;\n mimeType?: string;\n }>;\n};\n\nexport type GenerateImageResponse = {\n url?: string;\n};\n\nexport function createImageGenerator(env: CoreEnv, storagePut: StoragePutFn) {\n return async function generateImage(\n options: GenerateImageOptions\n ): Promise<GenerateImageResponse> {\n if (!env.forgeApiUrl) {\n throw new Error(\"BUILT_IN_FORGE_API_URL is not configured\");\n }\n if (!env.forgeApiKey) {\n throw new Error(\"BUILT_IN_FORGE_API_KEY is not configured\");\n }\n\n const baseUrl = env.forgeApiUrl.endsWith(\"/\")\n ? env.forgeApiUrl\n : `${env.forgeApiUrl}/`;\n const fullUrl = new URL(\n \"images.v1.ImageService/GenerateImage\",\n baseUrl\n ).toString();\n\n const response = await fetch(fullUrl, {\n method: \"POST\",\n headers: {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n \"connect-protocol-version\": \"1\",\n authorization: `Bearer ${env.forgeApiKey}`,\n },\n body: JSON.stringify({\n prompt: options.prompt,\n original_images: options.originalImages || [],\n }),\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `Image generation request failed (${response.status} ${response.statusText})${detail ? `: ${detail}` : \"\"}`\n );\n }\n\n const result = (await response.json()) as {\n image: {\n b64Json: string;\n mimeType: string;\n };\n };\n\n const base64Data = result.image.b64Json;\n const buffer = Buffer.from(base64Data, \"base64\");\n\n const { url } = await storagePut(\n `generated/${Date.now()}.png`,\n buffer,\n result.image.mimeType\n );\n\n return { url };\n };\n}\n","/**\n * Google Maps API Integration for Manus WebDev Templates.\n *\n * Main function: createMapClient(env) returns makeRequest<T>(endpoint, params)\n * All credentials are automatically injected. Array parameters use | as separator.\n *\n * Example:\n * const makeRequest = createMapClient(ENV);\n * const result = await makeRequest<GeocodeResult>(\n * \"/maps/api/geocode/json\",\n * { address: \"1600 Amphitheatre Parkway\" }\n * );\n */\nimport type { CoreEnv } from \"./types\";\n\nexport type MapRequestParams = Record<\n string,\n string | number | boolean | string[] | undefined\n>;\n\nexport function createMapClient(env: CoreEnv) {\n return async function makeRequest<T = unknown>(\n endpoint: string,\n params: MapRequestParams = {}\n ): Promise<T> {\n if (!env.forgeApiUrl) {\n throw new Error(\"BUILT_IN_FORGE_API_URL is not configured\");\n }\n if (!env.forgeApiKey) {\n throw new Error(\"BUILT_IN_FORGE_API_KEY is not configured\");\n }\n\n const baseUrl = env.forgeApiUrl.endsWith(\"/\")\n ? env.forgeApiUrl\n : `${env.forgeApiUrl}/`;\n const proxyUrl = new URL(\n \"webdevtoken.v1.WebDevService/GoogleMapsProxy\",\n baseUrl\n ).toString();\n\n const processedParams: Record<string, string> = {};\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n processedParams[key] = value.join(\"|\");\n } else {\n processedParams[key] = String(value);\n }\n }\n\n const response = await fetch(proxyUrl, {\n method: \"POST\",\n headers: {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n \"connect-protocol-version\": \"1\",\n authorization: `Bearer ${env.forgeApiKey}`,\n },\n body: JSON.stringify({\n endpoint,\n params: processedParams,\n }),\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `Maps API request failed (${response.status} ${response.statusText})${detail ? `: ${detail}` : \"\"}`\n );\n }\n\n const wrapper = (await response.json()) as { jsonData?: string };\n if (wrapper.jsonData) {\n try {\n return JSON.parse(wrapper.jsonData) as T;\n } catch {\n return wrapper.jsonData as unknown as T;\n }\n }\n return wrapper as unknown as T;\n };\n}\n","/**\n * Owner notification helper — pushes operational updates to the Manus project owner.\n *\n * Example:\n * const notifyOwner = createNotificationClient(ENV);\n * await notifyOwner({ title: \"New submission\", content: \"User X submitted form Y\" });\n */\nimport type { CoreEnv } from \"./types\";\n\nexport type NotificationPayload = {\n title: string;\n content: string;\n};\n\nconst TITLE_MAX_LENGTH = 1200;\nconst CONTENT_MAX_LENGTH = 20000;\n\nexport function createNotificationClient(env: CoreEnv) {\n return async function notifyOwner(\n payload: NotificationPayload\n ): Promise<boolean> {\n if (!env.forgeApiUrl) {\n throw new Error(\"BUILT_IN_FORGE_API_URL is not configured\");\n }\n if (!env.forgeApiKey) {\n throw new Error(\"BUILT_IN_FORGE_API_KEY is not configured\");\n }\n\n const title =\n payload.title.length > TITLE_MAX_LENGTH\n ? payload.title.slice(0, TITLE_MAX_LENGTH)\n : payload.title;\n const content =\n payload.content.length > CONTENT_MAX_LENGTH\n ? payload.content.slice(0, CONTENT_MAX_LENGTH)\n : payload.content;\n\n const baseUrl = env.forgeApiUrl.endsWith(\"/\")\n ? env.forgeApiUrl\n : `${env.forgeApiUrl}/`;\n const fullUrl = new URL(\n \"webdevtoken.v1.WebDevService/NotifyOwner\",\n baseUrl\n ).toString();\n\n try {\n const response = await fetch(fullUrl, {\n method: \"POST\",\n headers: {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n \"connect-protocol-version\": \"1\",\n authorization: `Bearer ${env.forgeApiKey}`,\n },\n body: JSON.stringify({ title, content }),\n });\n\n if (!response.ok) {\n console.error(\n `Notification failed: ${response.status} ${response.statusText}`\n );\n return false;\n }\n return true;\n } catch (error) {\n console.error(\"Notification error:\", error);\n return false;\n }\n };\n}\n","/**\n * Voice transcription helper using internal Speech-to-Text service (Whisper API).\n *\n * Example:\n * const transcribeAudio = createTranscriptionClient(ENV);\n * const result = await transcribeAudio({\n * audioUrl: \"https://storage.example.com/audio/recording.mp3\",\n * language: \"en\",\n * });\n */\nimport type { CoreEnv } from \"./types\";\n\nexport type TranscribeOptions = {\n audioUrl: string;\n language?: string;\n prompt?: string;\n};\n\nexport type WhisperSegment = {\n id: number;\n seek: number;\n start: number;\n end: number;\n text: string;\n tokens: number[];\n temperature: number;\n avg_logprob: number;\n compression_ratio: number;\n no_speech_prob: number;\n};\n\nexport type WhisperResponse = {\n task: string;\n language: string;\n duration: number;\n text: string;\n segments: WhisperSegment[];\n};\n\nexport type TranscriptionError = {\n error: string;\n code: string;\n details: string;\n};\n\nexport type TranscriptionResult = WhisperResponse | TranscriptionError;\n\nconst SUPPORTED_MIME_TYPES = [\n \"audio/webm\",\n \"audio/mp3\",\n \"audio/mpeg\",\n \"audio/wav\",\n \"audio/wave\",\n \"audio/ogg\",\n \"audio/m4a\",\n \"audio/mp4\",\n] as const;\n\nfunction getFileExtension(mimeType: string): string {\n const mimeToExt: Record<string, string> = {\n \"audio/webm\": \"webm\",\n \"audio/mp3\": \"mp3\",\n \"audio/mpeg\": \"mp3\",\n \"audio/wav\": \"wav\",\n \"audio/wave\": \"wav\",\n \"audio/ogg\": \"ogg\",\n \"audio/m4a\": \"m4a\",\n \"audio/mp4\": \"m4a\",\n };\n return mimeToExt[mimeType] || \"audio\";\n}\n\nfunction getLanguageName(langCode: string): string {\n const langMap: Record<string, string> = {\n en: \"English\",\n es: \"Spanish\",\n fr: \"French\",\n de: \"German\",\n it: \"Italian\",\n pt: \"Portuguese\",\n ru: \"Russian\",\n ja: \"Japanese\",\n ko: \"Korean\",\n zh: \"Chinese\",\n ar: \"Arabic\",\n hi: \"Hindi\",\n nl: \"Dutch\",\n pl: \"Polish\",\n tr: \"Turkish\",\n sv: \"Swedish\",\n da: \"Danish\",\n no: \"Norwegian\",\n fi: \"Finnish\",\n };\n return langMap[langCode] || langCode;\n}\n\nexport function createTranscriptionClient(env: CoreEnv) {\n return async function transcribeAudio(\n options: TranscribeOptions\n ): Promise<TranscriptionResult> {\n try {\n if (!env.forgeApiUrl) {\n throw new Error(\"BUILT_IN_FORGE_API_URL is not configured\");\n }\n if (!env.forgeApiKey) {\n throw new Error(\"BUILT_IN_FORGE_API_KEY is not configured\");\n }\n\n // Step 1: Fetch the audio file to determine MIME type and validate\n const audioResponse = await fetch(options.audioUrl);\n if (!audioResponse.ok) {\n return {\n error: \"Failed to fetch audio file\",\n code: \"FETCH_FAILED\",\n details: `HTTP ${audioResponse.status} ${audioResponse.statusText}`,\n };\n }\n\n const contentType =\n audioResponse.headers.get(\"content-type\") || \"audio/webm\";\n const mimeType = contentType.split(\";\")[0].trim();\n\n if (\n !SUPPORTED_MIME_TYPES.includes(\n mimeType as (typeof SUPPORTED_MIME_TYPES)[number]\n )\n ) {\n return {\n error: \"Unsupported audio format\",\n code: \"UNSUPPORTED_FORMAT\",\n details: `MIME type '${mimeType}' is not supported. Supported: ${SUPPORTED_MIME_TYPES.join(\", \")}`,\n };\n }\n\n // Step 2: Read audio buffer and check size\n const audioBuffer = await audioResponse.arrayBuffer();\n const sizeMB = audioBuffer.byteLength / (1024 * 1024);\n if (sizeMB > 16) {\n return {\n error: \"Audio file exceeds maximum size limit\",\n code: \"FILE_TOO_LARGE\",\n details: `File size is ${sizeMB.toFixed(2)}MB, maximum allowed is 16MB`,\n };\n }\n\n // Step 3: Create FormData for multipart upload to Whisper API\n const formData = new FormData();\n const filename = `audio.${getFileExtension(mimeType)}`;\n const audioBlob = new Blob([new Uint8Array(audioBuffer)], {\n type: mimeType,\n });\n formData.append(\"file\", audioBlob, filename);\n formData.append(\"model\", \"whisper-1\");\n formData.append(\"response_format\", \"verbose_json\");\n\n const prompt =\n options.prompt ||\n (options.language\n ? `Transcribe the user's voice to text, the user's working language is ${getLanguageName(options.language)}`\n : \"Transcribe the user's voice to text\");\n formData.append(\"prompt\", prompt);\n\n // Step 4: Call the transcription service\n const baseUrl = env.forgeApiUrl.endsWith(\"/\")\n ? env.forgeApiUrl\n : `${env.forgeApiUrl}/`;\n const fullUrl = new URL(\"v1/audio/transcriptions\", baseUrl).toString();\n\n const response = await fetch(fullUrl, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${env.forgeApiKey}`,\n \"Accept-Encoding\": \"identity\",\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"\");\n return {\n error: \"Transcription service request failed\",\n code: \"TRANSCRIPTION_FAILED\",\n details: `${response.status} ${response.statusText}${errorText ? `: ${errorText}` : \"\"}`,\n };\n }\n\n // Step 5: Parse and return the transcription result\n const whisperResponse = (await response.json()) as WhisperResponse;\n\n if (\n !whisperResponse.text ||\n typeof whisperResponse.text !== \"string\"\n ) {\n return {\n error: \"Invalid transcription response\",\n code: \"SERVICE_ERROR\",\n details:\n \"Transcription service returned an invalid response format\",\n };\n }\n\n return whisperResponse;\n } catch (error) {\n return {\n error: \"Voice transcription failed\",\n code: \"SERVICE_ERROR\",\n details:\n error instanceof Error\n ? error.message\n : \"An unexpected error occurred\",\n };\n }\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/dataApi.ts","../src/llm.ts","../src/imageGeneration.ts","../src/map.ts","../src/notification.ts","../src/voiceTranscription.ts"],"sourcesContent":["/**\n * Data API helper — calls the Manus built-in Data API Hub.\n *\n * Quick example:\n * const callDataApi = createDataApiClient(ENV);\n * await callDataApi(\"Youtube/search\", {\n * query: { gl: \"US\", hl: \"en\", q: \"manus\" },\n * });\n */\nimport type { CoreEnv } from \"./types\";\n\nexport type DataApiCallOptions = {\n query?: Record<string, unknown>;\n body?: Record<string, unknown>;\n pathParams?: Record<string, unknown>;\n formData?: Record<string, unknown>;\n};\n\nexport function createDataApiClient(env: CoreEnv) {\n return async function callDataApi(\n apiId: string,\n options: DataApiCallOptions = {}\n ): Promise<unknown> {\n if (!env.forgeApiUrl) {\n throw new Error(\"BUILT_IN_FORGE_API_URL is not configured\");\n }\n if (!env.forgeApiKey) {\n throw new Error(\"BUILT_IN_FORGE_API_KEY is not configured\");\n }\n\n const baseUrl = env.forgeApiUrl.endsWith(\"/\")\n ? env.forgeApiUrl\n : `${env.forgeApiUrl}/`;\n const fullUrl = new URL(\n \"webdevtoken.v1.WebDevService/CallApi\",\n baseUrl\n ).toString();\n\n const response = await fetch(fullUrl, {\n method: \"POST\",\n headers: {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n \"connect-protocol-version\": \"1\",\n authorization: `Bearer ${env.forgeApiKey}`,\n },\n body: JSON.stringify({\n apiId,\n query: options.query,\n body: options.body,\n path_params: options.pathParams,\n multipart_form_data: options.formData,\n }),\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `Data API request failed (${response.status} ${response.statusText})${detail ? `: ${detail}` : \"\"}`\n );\n }\n\n const payload = await response.json().catch(() => ({}));\n if (payload && typeof payload === \"object\" && \"jsonData\" in payload) {\n try {\n return JSON.parse(\n (payload as Record<string, string>).jsonData ?? \"{}\"\n );\n } catch {\n return (payload as Record<string, unknown>).jsonData;\n }\n }\n return payload;\n };\n}\n","/**\n * LLM helper — calls the Manus built-in LLM API (OpenAI-compatible).\n *\n * Example:\n * const invokeLLM = createLLMClient(ENV);\n * const result = await invokeLLM({\n * messages: [\n * { role: \"system\", content: \"You are a helpful assistant.\" },\n * { role: \"user\", content: \"Hello, world!\" },\n * ],\n * });\n */\nimport type { CoreEnv } from \"./types\";\n\nexport type Role = \"system\" | \"user\" | \"assistant\" | \"tool\" | \"function\";\n\nexport type TextContent = {\n type: \"text\";\n text: string;\n};\n\nexport type ImageContent = {\n type: \"image_url\";\n image_url: {\n url: string;\n detail?: \"auto\" | \"low\" | \"high\";\n };\n};\n\nexport type FileContent = {\n type: \"file_url\";\n file_url: {\n url: string;\n mime_type?:\n | \"audio/mpeg\"\n | \"audio/wav\"\n | \"application/pdf\"\n | \"audio/mp4\"\n | \"video/mp4\";\n };\n};\n\nexport type MessageContent = string | TextContent | ImageContent | FileContent;\n\nexport type Message = {\n role: Role;\n name?: string;\n tool_call_id?: string;\n content: MessageContent | MessageContent[];\n};\n\nexport type Tool = {\n type: \"function\";\n function: {\n name: string;\n description?: string;\n parameters?: Record<string, unknown>;\n };\n};\n\ntype ToolChoiceExplicit = {\n type: \"function\";\n function: { name: string };\n};\n\nexport type ToolChoice =\n | \"none\"\n | \"auto\"\n | \"required\"\n | { name: string }\n | ToolChoiceExplicit;\n\ntype JsonSchema = {\n name: string;\n schema: Record<string, unknown>;\n strict?: boolean;\n};\n\nexport type ResponseFormat =\n | { type: \"text\" }\n | { type: \"json_object\" }\n | { type: \"json_schema\"; json_schema: JsonSchema };\n\nexport type OutputSchema = {\n name: string;\n schema: Record<string, unknown>;\n strict?: boolean;\n};\n\nexport type InvokeParams = {\n messages: Message[];\n tools?: Tool[];\n toolChoice?: ToolChoice;\n tool_choice?: ToolChoice;\n outputSchema?: OutputSchema;\n output_schema?: OutputSchema;\n responseFormat?: ResponseFormat;\n response_format?: ResponseFormat;\n};\n\nexport type InvokeResult = {\n id: string;\n object: string;\n created: number;\n model: string;\n choices: Array<{\n index: number;\n message: {\n role: string;\n content: string | null;\n tool_calls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>;\n };\n finish_reason: string;\n }>;\n usage?: {\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n };\n};\n\nconst ensureArray = (\n value: MessageContent | MessageContent[]\n): MessageContent[] => (Array.isArray(value) ? value : [value]);\n\nconst normalizeContentPart = (\n part: MessageContent\n): TextContent | ImageContent | FileContent => {\n if (typeof part === \"string\") {\n return { type: \"text\", text: part };\n }\n if (part.type === \"text\") return part;\n if (part.type === \"image_url\") return part;\n if (part.type === \"file_url\") return part;\n throw new Error(\"Unsupported message content part\");\n};\n\nconst normalizeMessage = (message: Message) => {\n const { role, name, tool_call_id } = message;\n if (role === \"tool\" || role === \"function\") {\n const content = ensureArray(message.content)\n .map((part) => (typeof part === \"string\" ? part : JSON.stringify(part)))\n .join(\"\\n\");\n return { role, name, tool_call_id, content };\n }\n const contentParts = ensureArray(message.content).map(normalizeContentPart);\n if (contentParts.length === 1 && contentParts[0].type === \"text\") {\n return { role, name, content: contentParts[0].text };\n }\n return { role, name, content: contentParts };\n};\n\nconst normalizeToolChoice = (\n toolChoice: ToolChoice | undefined,\n tools: Tool[] | undefined\n): \"none\" | \"auto\" | ToolChoiceExplicit | undefined => {\n if (!toolChoice) return undefined;\n if (toolChoice === \"none\" || toolChoice === \"auto\") return toolChoice;\n if (toolChoice === \"required\") {\n if (!tools || tools.length === 0) {\n throw new Error(\n \"tool_choice 'required' was provided but no tools were configured\"\n );\n }\n if (tools.length > 1) {\n throw new Error(\n \"tool_choice 'required' needs a single tool or specify the tool name explicitly\"\n );\n }\n return { type: \"function\", function: { name: tools[0].function.name } };\n }\n if (\"name\" in toolChoice) {\n return { type: \"function\", function: { name: toolChoice.name } };\n }\n return toolChoice;\n};\n\nconst normalizeResponseFormat = ({\n responseFormat,\n response_format,\n outputSchema,\n output_schema,\n}: {\n responseFormat?: ResponseFormat;\n response_format?: ResponseFormat;\n outputSchema?: OutputSchema;\n output_schema?: OutputSchema;\n}):\n | { type: \"json_schema\"; json_schema: JsonSchema }\n | { type: \"text\" }\n | { type: \"json_object\" }\n | undefined => {\n const explicitFormat = responseFormat || response_format;\n if (explicitFormat) {\n if (\n explicitFormat.type === \"json_schema\" &&\n !explicitFormat.json_schema?.schema\n ) {\n throw new Error(\n \"responseFormat json_schema requires a defined schema object\"\n );\n }\n return explicitFormat;\n }\n const schema = outputSchema || output_schema;\n if (!schema) return undefined;\n if (!schema.name || !schema.schema) {\n throw new Error(\"outputSchema requires both name and schema\");\n }\n return {\n type: \"json_schema\",\n json_schema: {\n name: schema.name,\n schema: schema.schema,\n ...(typeof schema.strict === \"boolean\" ? { strict: schema.strict } : {}),\n },\n };\n};\n\nexport function createLLMClient(env: CoreEnv) {\n const resolveApiUrl = () =>\n env.forgeApiUrl && env.forgeApiUrl.trim().length > 0\n ? `${env.forgeApiUrl.replace(/\\/$/, \"\")}/v1/chat/completions`\n : \"https://forge.manus.im/v1/chat/completions\";\n\n const assertApiKey = () => {\n if (!env.forgeApiKey) {\n throw new Error(\"OPENAI_API_KEY is not configured\");\n }\n };\n\n return async function invokeLLM(\n params: InvokeParams\n ): Promise<InvokeResult> {\n assertApiKey();\n\n const {\n messages,\n tools,\n toolChoice,\n tool_choice,\n outputSchema,\n output_schema,\n responseFormat,\n response_format,\n } = params;\n\n const payload: Record<string, unknown> = {\n model: \"gemini-2.5-flash\",\n messages: messages.map(normalizeMessage),\n };\n\n if (tools && tools.length > 0) {\n payload.tools = tools;\n }\n\n const normalizedToolChoice = normalizeToolChoice(\n toolChoice || tool_choice,\n tools\n );\n if (normalizedToolChoice) {\n payload.tool_choice = normalizedToolChoice;\n }\n\n payload.max_tokens = 32768;\n payload.thinking = { budget_tokens: 128 };\n\n const normalizedResponseFormat = normalizeResponseFormat({\n responseFormat,\n response_format,\n outputSchema,\n output_schema,\n });\n if (normalizedResponseFormat) {\n payload.response_format = normalizedResponseFormat;\n }\n\n const response = await fetch(resolveApiUrl(), {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${env.forgeApiKey}`,\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `LLM invoke failed: ${response.status} ${response.statusText} – ${errorText}`\n );\n }\n\n return (await response.json()) as InvokeResult;\n };\n}\n","/**\n * Image generation helper using internal ImageService.\n *\n * Example:\n * import { storagePut } from \"../storage\";\n * const generateImage = createImageGenerator(ENV, storagePut);\n * const { url } = await generateImage({ prompt: \"A serene landscape\" });\n */\nimport type { CoreEnv, StoragePutFn } from \"./types\";\n\nexport type GenerateImageOptions = {\n prompt: string;\n originalImages?: Array<{\n url?: string;\n b64Json?: string;\n mimeType?: string;\n }>;\n};\n\nexport type GenerateImageResponse = {\n url?: string;\n};\n\nexport function createImageGenerator(env: CoreEnv, storagePut: StoragePutFn) {\n return async function generateImage(\n options: GenerateImageOptions\n ): Promise<GenerateImageResponse> {\n if (!env.forgeApiUrl) {\n throw new Error(\"BUILT_IN_FORGE_API_URL is not configured\");\n }\n if (!env.forgeApiKey) {\n throw new Error(\"BUILT_IN_FORGE_API_KEY is not configured\");\n }\n\n const baseUrl = env.forgeApiUrl.endsWith(\"/\")\n ? env.forgeApiUrl\n : `${env.forgeApiUrl}/`;\n const fullUrl = new URL(\n \"images.v1.ImageService/GenerateImage\",\n baseUrl\n ).toString();\n\n const response = await fetch(fullUrl, {\n method: \"POST\",\n headers: {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n \"connect-protocol-version\": \"1\",\n authorization: `Bearer ${env.forgeApiKey}`,\n },\n body: JSON.stringify({\n prompt: options.prompt,\n original_images: options.originalImages || [],\n }),\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `Image generation request failed (${response.status} ${response.statusText})${detail ? `: ${detail}` : \"\"}`\n );\n }\n\n const result = (await response.json()) as {\n image: {\n b64Json: string;\n mimeType: string;\n };\n };\n\n const base64Data = result.image.b64Json;\n const buffer = Buffer.from(base64Data, \"base64\");\n\n const { url } = await storagePut(\n `generated/${Date.now()}.png`,\n buffer,\n result.image.mimeType\n );\n\n return { url };\n };\n}\n","/**\n * Google Maps API Integration for Manus WebDev Templates.\n *\n * Main function: createMapClient(env) returns makeRequest<T>(endpoint, params)\n * All credentials are automatically injected. Array parameters use | as separator.\n *\n * Example:\n * const makeRequest = createMapClient(ENV);\n * const result = await makeRequest<GeocodeResult>(\n * \"/maps/api/geocode/json\",\n * { address: \"1600 Amphitheatre Parkway\" }\n * );\n */\nimport type { CoreEnv } from \"./types\";\n\nexport type MapRequestParams = Record<\n string,\n string | number | boolean | string[] | undefined\n>;\n\nexport function createMapClient(env: CoreEnv) {\n return async function makeRequest<T = unknown>(\n endpoint: string,\n params: MapRequestParams = {}\n ): Promise<T> {\n if (!env.forgeApiUrl) {\n throw new Error(\"BUILT_IN_FORGE_API_URL is not configured\");\n }\n if (!env.forgeApiKey) {\n throw new Error(\"BUILT_IN_FORGE_API_KEY is not configured\");\n }\n\n const baseUrl = env.forgeApiUrl.endsWith(\"/\")\n ? env.forgeApiUrl\n : `${env.forgeApiUrl}/`;\n const proxyUrl = new URL(\n \"webdevtoken.v1.WebDevService/GoogleMapsProxy\",\n baseUrl\n ).toString();\n\n const processedParams: Record<string, string> = {};\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n processedParams[key] = value.join(\"|\");\n } else {\n processedParams[key] = String(value);\n }\n }\n\n const response = await fetch(proxyUrl, {\n method: \"POST\",\n headers: {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n \"connect-protocol-version\": \"1\",\n authorization: `Bearer ${env.forgeApiKey}`,\n },\n body: JSON.stringify({\n endpoint,\n params: processedParams,\n }),\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `Maps API request failed (${response.status} ${response.statusText})${detail ? `: ${detail}` : \"\"}`\n );\n }\n\n const wrapper = (await response.json()) as { jsonData?: string };\n if (wrapper.jsonData) {\n try {\n return JSON.parse(wrapper.jsonData) as T;\n } catch {\n return wrapper.jsonData as unknown as T;\n }\n }\n return wrapper as unknown as T;\n };\n}\n","/**\n * Owner notification helper — pushes operational updates to the Manus project owner.\n *\n * Example:\n * const notifyOwner = createNotificationClient(ENV);\n * await notifyOwner({ title: \"New submission\", content: \"User X submitted form Y\" });\n */\nimport type { CoreEnv } from \"./types\";\n\nexport type NotificationPayload = {\n title: string;\n content: string;\n};\n\nconst TITLE_MAX_LENGTH = 1200;\nconst CONTENT_MAX_LENGTH = 20000;\n\nexport function createNotificationClient(env: CoreEnv) {\n return async function notifyOwner(\n payload: NotificationPayload\n ): Promise<boolean> {\n if (!env.forgeApiUrl) {\n throw new Error(\"BUILT_IN_FORGE_API_URL is not configured\");\n }\n if (!env.forgeApiKey) {\n throw new Error(\"BUILT_IN_FORGE_API_KEY is not configured\");\n }\n\n const title =\n payload.title.length > TITLE_MAX_LENGTH\n ? payload.title.slice(0, TITLE_MAX_LENGTH)\n : payload.title;\n const content =\n payload.content.length > CONTENT_MAX_LENGTH\n ? payload.content.slice(0, CONTENT_MAX_LENGTH)\n : payload.content;\n\n const baseUrl = env.forgeApiUrl.endsWith(\"/\")\n ? env.forgeApiUrl\n : `${env.forgeApiUrl}/`;\n const fullUrl = new URL(\n \"webdevtoken.v1.WebDevService/NotifyOwner\",\n baseUrl\n ).toString();\n\n try {\n const response = await fetch(fullUrl, {\n method: \"POST\",\n headers: {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n \"connect-protocol-version\": \"1\",\n authorization: `Bearer ${env.forgeApiKey}`,\n },\n body: JSON.stringify({ title, content }),\n });\n\n if (!response.ok) {\n console.error(\n `Notification failed: ${response.status} ${response.statusText}`\n );\n return false;\n }\n return true;\n } catch (error) {\n console.error(\"Notification error:\", error);\n return false;\n }\n };\n}\n","/**\n * Voice transcription helper using internal Speech-to-Text service (Whisper API).\n *\n * Example:\n * const transcribeAudio = createTranscriptionClient(ENV);\n * const result = await transcribeAudio({\n * audioUrl: \"https://storage.example.com/audio/recording.mp3\",\n * language: \"en\",\n * });\n */\nimport type { CoreEnv } from \"./types\";\n\nexport type TranscribeOptions = {\n audioUrl: string;\n language?: string;\n prompt?: string;\n};\n\nexport type WhisperSegment = {\n id: number;\n seek: number;\n start: number;\n end: number;\n text: string;\n tokens: number[];\n temperature: number;\n avg_logprob: number;\n compression_ratio: number;\n no_speech_prob: number;\n};\n\nexport type WhisperResponse = {\n task: string;\n language: string;\n duration: number;\n text: string;\n segments: WhisperSegment[];\n};\n\nexport type TranscriptionError = {\n error: string;\n code: string;\n details: string;\n};\n\nexport type TranscriptionResult = WhisperResponse | TranscriptionError;\n\nconst SUPPORTED_MIME_TYPES = [\n \"audio/webm\",\n \"audio/mp3\",\n \"audio/mpeg\",\n \"audio/wav\",\n \"audio/wave\",\n \"audio/ogg\",\n \"audio/m4a\",\n \"audio/mp4\",\n] as const;\n\nfunction getFileExtension(mimeType: string): string {\n const mimeToExt: Record<string, string> = {\n \"audio/webm\": \"webm\",\n \"audio/mp3\": \"mp3\",\n \"audio/mpeg\": \"mp3\",\n \"audio/wav\": \"wav\",\n \"audio/wave\": \"wav\",\n \"audio/ogg\": \"ogg\",\n \"audio/m4a\": \"m4a\",\n \"audio/mp4\": \"m4a\",\n };\n return mimeToExt[mimeType] || \"audio\";\n}\n\nfunction getLanguageName(langCode: string): string {\n const langMap: Record<string, string> = {\n en: \"English\",\n es: \"Spanish\",\n fr: \"French\",\n de: \"German\",\n it: \"Italian\",\n pt: \"Portuguese\",\n ru: \"Russian\",\n ja: \"Japanese\",\n ko: \"Korean\",\n zh: \"Chinese\",\n ar: \"Arabic\",\n hi: \"Hindi\",\n nl: \"Dutch\",\n pl: \"Polish\",\n tr: \"Turkish\",\n sv: \"Swedish\",\n da: \"Danish\",\n no: \"Norwegian\",\n fi: \"Finnish\",\n };\n return langMap[langCode] || langCode;\n}\n\nexport function createTranscriptionClient(env: CoreEnv) {\n return async function transcribeAudio(\n options: TranscribeOptions\n ): Promise<TranscriptionResult> {\n try {\n if (!env.forgeApiUrl) {\n throw new Error(\"BUILT_IN_FORGE_API_URL is not configured\");\n }\n if (!env.forgeApiKey) {\n throw new Error(\"BUILT_IN_FORGE_API_KEY is not configured\");\n }\n\n // Step 1: Fetch the audio file to determine MIME type and validate\n const audioResponse = await fetch(options.audioUrl);\n if (!audioResponse.ok) {\n return {\n error: \"Failed to fetch audio file\",\n code: \"FETCH_FAILED\",\n details: `HTTP ${audioResponse.status} ${audioResponse.statusText}`,\n };\n }\n\n const contentType =\n audioResponse.headers.get(\"content-type\") || \"audio/webm\";\n const mimeType = contentType.split(\";\")[0].trim();\n\n if (\n !SUPPORTED_MIME_TYPES.includes(\n mimeType as (typeof SUPPORTED_MIME_TYPES)[number]\n )\n ) {\n return {\n error: \"Unsupported audio format\",\n code: \"UNSUPPORTED_FORMAT\",\n details: `MIME type '${mimeType}' is not supported. Supported: ${SUPPORTED_MIME_TYPES.join(\", \")}`,\n };\n }\n\n // Step 2: Read audio buffer and check size\n const audioBuffer = await audioResponse.arrayBuffer();\n const sizeMB = audioBuffer.byteLength / (1024 * 1024);\n if (sizeMB > 16) {\n return {\n error: \"Audio file exceeds maximum size limit\",\n code: \"FILE_TOO_LARGE\",\n details: `File size is ${sizeMB.toFixed(2)}MB, maximum allowed is 16MB`,\n };\n }\n\n // Step 3: Create FormData for multipart upload to Whisper API\n const formData = new FormData();\n const filename = `audio.${getFileExtension(mimeType)}`;\n const audioBlob = new Blob([new Uint8Array(audioBuffer)], {\n type: mimeType,\n });\n formData.append(\"file\", audioBlob, filename);\n formData.append(\"model\", \"whisper-1\");\n formData.append(\"response_format\", \"verbose_json\");\n\n const prompt =\n options.prompt ||\n (options.language\n ? `Transcribe the user's voice to text, the user's working language is ${getLanguageName(options.language)}`\n : \"Transcribe the user's voice to text\");\n formData.append(\"prompt\", prompt);\n\n // Step 4: Call the transcription service\n const baseUrl = env.forgeApiUrl.endsWith(\"/\")\n ? env.forgeApiUrl\n : `${env.forgeApiUrl}/`;\n const fullUrl = new URL(\"v1/audio/transcriptions\", baseUrl).toString();\n\n const response = await fetch(fullUrl, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${env.forgeApiKey}`,\n \"Accept-Encoding\": \"identity\",\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"\");\n return {\n error: \"Transcription service request failed\",\n code: \"TRANSCRIPTION_FAILED\",\n details: `${response.status} ${response.statusText}${errorText ? `: ${errorText}` : \"\"}`,\n };\n }\n\n // Step 5: Parse and return the transcription result\n const whisperResponse = (await response.json()) as WhisperResponse;\n\n if (\n !whisperResponse.text ||\n typeof whisperResponse.text !== \"string\"\n ) {\n return {\n error: \"Invalid transcription response\",\n code: \"SERVICE_ERROR\",\n details:\n \"Transcription service returned an invalid response format\",\n };\n }\n\n return whisperResponse;\n } catch (error) {\n return {\n error: \"Voice transcription failed\",\n code: \"SERVICE_ERROR\",\n details:\n error instanceof Error\n ? error.message\n : \"An unexpected error occurred\",\n };\n }\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "@pablo2410/core-server",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Shared server utilities for Oplytics.digital subdomains",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {
8
8
  "types": "./dist/index.d.ts",
9
9
  "import": "./dist/index.js"
10
+ },
11
+ "./auth": {
12
+ "types": "./dist/auth.d.ts",
13
+ "import": "./dist/auth.js"
10
14
  }
11
15
  },
12
16
  "files": [
@@ -26,12 +30,27 @@
26
30
  "access": "public"
27
31
  },
28
32
  "devDependencies": {
33
+ "@types/express": "^5.0.0",
29
34
  "@types/node": "^25.5.0",
35
+ "axios": "^1.7.0",
36
+ "cookie": "^1.0.0",
37
+ "express": "^4.21.0",
38
+ "jose": "^6.0.0",
30
39
  "tsup": "^8.0.0",
31
40
  "typescript": "^5.6.0",
32
41
  "vitest": "^3.0.0"
33
42
  },
34
43
  "peerDependencies": {
35
- "@trpc/server": ">=11.0.0"
44
+ "@trpc/server": ">=11.0.0",
45
+ "express": ">=4.0.0",
46
+ "jose": ">=5.0.0",
47
+ "cookie": ">=0.6.0",
48
+ "axios": ">=1.0.0"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "express": { "optional": true },
52
+ "jose": { "optional": true },
53
+ "cookie": { "optional": true },
54
+ "axios": { "optional": true }
36
55
  }
37
56
  }