geminimock 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/dist/auth/credential-store.d.ts +17 -0
  4. package/dist/auth/credential-store.js +44 -0
  5. package/dist/auth/credential-store.js.map +1 -0
  6. package/dist/auth/oauth-flow.d.ts +26 -0
  7. package/dist/auth/oauth-flow.js +87 -0
  8. package/dist/auth/oauth-flow.js.map +1 -0
  9. package/dist/auth/oauth-service.d.ts +18 -0
  10. package/dist/auth/oauth-service.js +287 -0
  11. package/dist/auth/oauth-service.js.map +1 -0
  12. package/dist/cli.d.ts +2 -0
  13. package/dist/cli.js +76 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/config/env.d.ts +11 -0
  16. package/dist/config/env.js +28 -0
  17. package/dist/config/env.js.map +1 -0
  18. package/dist/gemini/code-assist-client.d.ts +17 -0
  19. package/dist/gemini/code-assist-client.js +179 -0
  20. package/dist/gemini/code-assist-client.js.map +1 -0
  21. package/dist/gemini/model-catalog-service.d.ts +8 -0
  22. package/dist/gemini/model-catalog-service.js +31 -0
  23. package/dist/gemini/model-catalog-service.js.map +1 -0
  24. package/dist/gemini/types.d.ts +82 -0
  25. package/dist/gemini/types.js +2 -0
  26. package/dist/gemini/types.js.map +1 -0
  27. package/dist/openai/chat-service.d.ts +12 -0
  28. package/dist/openai/chat-service.js +82 -0
  29. package/dist/openai/chat-service.js.map +1 -0
  30. package/dist/openai/mapper.d.ts +7 -0
  31. package/dist/openai/mapper.js +86 -0
  32. package/dist/openai/mapper.js.map +1 -0
  33. package/dist/openai/model-resolver.d.ts +2 -0
  34. package/dist/openai/model-resolver.js +52 -0
  35. package/dist/openai/model-resolver.js.map +1 -0
  36. package/dist/openai/models-response.d.ts +10 -0
  37. package/dist/openai/models-response.js +12 -0
  38. package/dist/openai/models-response.js.map +1 -0
  39. package/dist/openai/types.d.ts +56 -0
  40. package/dist/openai/types.js +19 -0
  41. package/dist/openai/types.js.map +1 -0
  42. package/dist/server/app.d.ts +17 -0
  43. package/dist/server/app.js +91 -0
  44. package/dist/server/app.js.map +1 -0
  45. package/package.json +45 -0
@@ -0,0 +1,86 @@
1
+ function normalizeMessageContent(content) {
2
+ if (typeof content === "string") {
3
+ return content;
4
+ }
5
+ return content.map((item) => item.text).join("\n");
6
+ }
7
+ function toVertexRole(role) {
8
+ if (role === "assistant") {
9
+ return "model";
10
+ }
11
+ return "user";
12
+ }
13
+ function normalizeStop(stop) {
14
+ if (!stop) {
15
+ return undefined;
16
+ }
17
+ if (typeof stop === "string") {
18
+ return [stop];
19
+ }
20
+ return stop;
21
+ }
22
+ export function mapOpenAIToCodeAssist(request, context) {
23
+ const systemMessages = [];
24
+ const contents = [];
25
+ for (const message of request.messages) {
26
+ const text = normalizeMessageContent(message.content);
27
+ if (message.role === "system") {
28
+ systemMessages.push(text);
29
+ continue;
30
+ }
31
+ contents.push({
32
+ role: toVertexRole(message.role),
33
+ parts: [{ text }]
34
+ });
35
+ }
36
+ return {
37
+ model: request.model ?? context.defaultModel ?? "gemini-2.5-pro",
38
+ project: context.projectId,
39
+ request: {
40
+ contents,
41
+ systemInstruction: systemMessages.length > 0
42
+ ? {
43
+ role: "user",
44
+ parts: [{ text: systemMessages.join("\n") }]
45
+ }
46
+ : undefined,
47
+ generationConfig: {
48
+ temperature: request.temperature,
49
+ topP: request.top_p,
50
+ maxOutputTokens: request.max_tokens,
51
+ stopSequences: normalizeStop(request.stop)
52
+ }
53
+ }
54
+ };
55
+ }
56
+ function extractCandidateText(response) {
57
+ const parts = response.response.candidates?.[0]?.content?.parts ?? [];
58
+ const text = parts.map((part) => part.text ?? "").join("");
59
+ return text;
60
+ }
61
+ export function mapCodeAssistToOpenAI(response, model) {
62
+ const text = extractCandidateText(response);
63
+ const usage = response.response.usageMetadata;
64
+ return {
65
+ id: response.traceId ?? `chatcmpl-${Date.now()}`,
66
+ object: "chat.completion",
67
+ created: Math.floor(Date.now() / 1000),
68
+ model,
69
+ choices: [
70
+ {
71
+ index: 0,
72
+ finish_reason: response.response.candidates?.[0]?.finishReason ?? "stop",
73
+ message: {
74
+ role: "assistant",
75
+ content: text
76
+ }
77
+ }
78
+ ],
79
+ usage: {
80
+ prompt_tokens: usage?.promptTokenCount ?? 0,
81
+ completion_tokens: usage?.candidatesTokenCount ?? 0,
82
+ total_tokens: usage?.totalTokenCount ?? 0
83
+ }
84
+ };
85
+ }
86
+ //# sourceMappingURL=mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapper.js","sourceRoot":"","sources":["../../src/openai/mapper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function resolveRequestedModel(requestedModel: string, availableModels: string[]): string;
2
+ export declare function normalizeModelList(rawModels: string[]): string[];
@@ -0,0 +1,52 @@
1
+ const ALIAS_MAP = {
2
+ "gemini-3-flash": "gemini-3-flash-preview",
3
+ "gemini-3-pro": "gemini-3-pro-preview",
4
+ "gemini-3.1-pro": "gemini-3.1-pro-preview"
5
+ };
6
+ function normalizeList(models) {
7
+ return new Set(models.map((model) => model.trim()).filter((model) => model.length > 0));
8
+ }
9
+ function stripVertexSuffix(model) {
10
+ return model.endsWith("_vertex") ? model.slice(0, -7) : model;
11
+ }
12
+ export function resolveRequestedModel(requestedModel, availableModels) {
13
+ const requested = requestedModel.trim();
14
+ if (!requested) {
15
+ return requestedModel;
16
+ }
17
+ const normalized = normalizeList(availableModels);
18
+ if (normalized.has(requested)) {
19
+ return requested;
20
+ }
21
+ const directAlias = ALIAS_MAP[requested];
22
+ if (directAlias && normalized.has(directAlias)) {
23
+ return directAlias;
24
+ }
25
+ const previewCandidate = `${requested}-preview`;
26
+ if (normalized.has(previewCandidate)) {
27
+ return previewCandidate;
28
+ }
29
+ const requestedWithoutPreview = requested.endsWith("-preview")
30
+ ? requested.slice(0, -8)
31
+ : requested;
32
+ if (normalized.has(requestedWithoutPreview)) {
33
+ return requestedWithoutPreview;
34
+ }
35
+ const requestedNoVertex = stripVertexSuffix(requested);
36
+ if (normalized.has(requestedNoVertex)) {
37
+ return requestedNoVertex;
38
+ }
39
+ return requested;
40
+ }
41
+ export function normalizeModelList(rawModels) {
42
+ const unique = new Set();
43
+ for (const model of rawModels) {
44
+ const trimmed = model.trim();
45
+ if (!trimmed) {
46
+ continue;
47
+ }
48
+ unique.add(stripVertexSuffix(trimmed));
49
+ }
50
+ return [...unique].sort((a, b) => a.localeCompare(b));
51
+ }
52
+ //# sourceMappingURL=model-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-resolver.js","sourceRoot":"","sources":["../../src/openai/model-resolver.ts"],"names":[],"mappings":"AAAA,MAAM,SAAS,GAA2B;IACxC,gBAAgB,EAAE,wBAAwB;IAC1C,cAAc,EAAE,sBAAsB;IACtC,gBAAgB,EAAE,wBAAwB;CAC3C,CAAC;AAEF,SAAS,aAAa,CAAC,MAAgB;IACrC,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;AAC1F,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa;IACtC,OAAO,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,cAAsB,EAAE,eAAyB;IACrF,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,MAAM,UAAU,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;IAClD,IAAI,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,WAAW,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,WAAW,IAAI,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/C,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,MAAM,gBAAgB,GAAG,GAAG,SAAS,UAAU,CAAC;IAChD,IAAI,UAAU,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACrC,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,MAAM,uBAAuB,GAAG,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC;QAC5D,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxB,CAAC,CAAC,SAAS,CAAC;IACd,IAAI,UAAU,CAAC,GAAG,CAAC,uBAAuB,CAAC,EAAE,CAAC;QAC5C,OAAO,uBAAuB,CAAC;IACjC,CAAC;IAED,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACvD,IAAI,UAAU,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACtC,OAAO,iBAAiB,CAAC;IAC3B,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,SAAmB;IACpD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,SAAS;QACX,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,10 @@
1
+ export type OpenAIModelListResponse = {
2
+ object: "list";
3
+ data: Array<{
4
+ id: string;
5
+ object: "model";
6
+ created: number;
7
+ owned_by: string;
8
+ }>;
9
+ };
10
+ export declare function toOpenAIModelList(models: string[]): OpenAIModelListResponse;
@@ -0,0 +1,12 @@
1
+ export function toOpenAIModelList(models) {
2
+ return {
3
+ object: "list",
4
+ data: models.map((model) => ({
5
+ id: model,
6
+ object: "model",
7
+ created: 0,
8
+ owned_by: "google-code-assist"
9
+ }))
10
+ };
11
+ }
12
+ //# sourceMappingURL=models-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models-response.js","sourceRoot":"","sources":["../../src/openai/models-response.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,iBAAiB,CAAC,MAAgB;IAChD,OAAO;QACL,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC3B,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,CAAC;YACV,QAAQ,EAAE,oBAAoB;SAC/B,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+ export declare const openAIChatCompletionRequestSchema: z.ZodObject<{
3
+ model: z.ZodOptional<z.ZodString>;
4
+ messages: z.ZodArray<z.ZodObject<{
5
+ role: z.ZodEnum<{
6
+ user: "user";
7
+ system: "system";
8
+ assistant: "assistant";
9
+ developer: "developer";
10
+ tool: "tool";
11
+ }>;
12
+ content: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodObject<{
13
+ type: z.ZodLiteral<"text">;
14
+ text: z.ZodString;
15
+ }, z.core.$strip>>]>;
16
+ }, z.core.$strip>>;
17
+ stream: z.ZodOptional<z.ZodBoolean>;
18
+ temperature: z.ZodOptional<z.ZodNumber>;
19
+ top_p: z.ZodOptional<z.ZodNumber>;
20
+ max_tokens: z.ZodOptional<z.ZodNumber>;
21
+ stop: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
22
+ }, z.core.$strip>;
23
+ export type OpenAIChatCompletionRequest = z.infer<typeof openAIChatCompletionRequestSchema>;
24
+ export type OpenAIChatCompletionResponse = {
25
+ id: string;
26
+ object: "chat.completion";
27
+ created: number;
28
+ model: string;
29
+ choices: Array<{
30
+ index: number;
31
+ finish_reason: string;
32
+ message: {
33
+ role: "assistant";
34
+ content: string;
35
+ };
36
+ }>;
37
+ usage: {
38
+ prompt_tokens: number;
39
+ completion_tokens: number;
40
+ total_tokens: number;
41
+ };
42
+ };
43
+ export type OpenAIChatCompletionChunk = {
44
+ id: string;
45
+ object: "chat.completion.chunk";
46
+ created: number;
47
+ model: string;
48
+ choices: Array<{
49
+ index: number;
50
+ finish_reason: string | null;
51
+ delta: {
52
+ role?: "assistant";
53
+ content?: string;
54
+ };
55
+ }>;
56
+ };
@@ -0,0 +1,19 @@
1
+ import { z } from "zod";
2
+ const contentItemSchema = z.object({
3
+ type: z.literal("text"),
4
+ text: z.string()
5
+ });
6
+ const messageSchema = z.object({
7
+ role: z.enum(["system", "user", "assistant", "developer", "tool"]),
8
+ content: z.union([z.string(), z.array(contentItemSchema)])
9
+ });
10
+ export const openAIChatCompletionRequestSchema = z.object({
11
+ model: z.string().optional(),
12
+ messages: z.array(messageSchema).min(1),
13
+ stream: z.boolean().optional(),
14
+ temperature: z.number().optional(),
15
+ top_p: z.number().optional(),
16
+ max_tokens: z.number().int().optional(),
17
+ stop: z.union([z.string(), z.array(z.string())]).optional()
18
+ });
19
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/openai/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IACvB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;CACjB,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAClE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;CAC3D,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC,MAAM,CAAC;IACxD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC9B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACvC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CAC5D,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import Fastify from "fastify";
2
+ import { OAuthService } from "../auth/oauth-service.js";
3
+ import { type AppEnv } from "../config/env.js";
4
+ import { ModelCatalogService } from "../gemini/model-catalog-service.js";
5
+ import { type OpenAIChatCompletionRequest, type OpenAIChatCompletionResponse } from "../openai/types.js";
6
+ export type ChatService = {
7
+ generate(request: OpenAIChatCompletionRequest): Promise<OpenAIChatCompletionResponse>;
8
+ stream?(request: OpenAIChatCompletionRequest): AsyncGenerator<unknown>;
9
+ };
10
+ export type AppDependencies = {
11
+ env: AppEnv;
12
+ oauthService: OAuthService;
13
+ chatService: ChatService;
14
+ modelCatalogService: ModelCatalogService;
15
+ };
16
+ export declare function createDefaultDependencies(env?: AppEnv): Promise<AppDependencies>;
17
+ export declare function createApp(input?: Partial<AppDependencies>): Promise<Fastify.FastifyInstance<import("node:http").Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>, import("node:http").IncomingMessage, import("node:http").ServerResponse<import("node:http").IncomingMessage>, Fastify.FastifyBaseLogger, Fastify.FastifyTypeProviderDefault>>;
@@ -0,0 +1,91 @@
1
+ import Fastify from "fastify";
2
+ import cors from "@fastify/cors";
3
+ import { ZodError } from "zod";
4
+ import { createCredentialStore } from "../auth/credential-store.js";
5
+ import { OAuthService } from "../auth/oauth-service.js";
6
+ import { loadEnv } from "../config/env.js";
7
+ import { CodeAssistClient } from "../gemini/code-assist-client.js";
8
+ import { ModelCatalogService } from "../gemini/model-catalog-service.js";
9
+ import { OpenAIChatService } from "../openai/chat-service.js";
10
+ import { toOpenAIModelList } from "../openai/models-response.js";
11
+ import { openAIChatCompletionRequestSchema } from "../openai/types.js";
12
+ export async function createDefaultDependencies(env = loadEnv()) {
13
+ const store = createCredentialStore(env.oauthPath, env.oauthFallbackPath);
14
+ const oauthService = new OAuthService(store);
15
+ const codeAssistClient = new CodeAssistClient(env, () => oauthService.getAccessToken());
16
+ const modelCatalogService = new ModelCatalogService(codeAssistClient);
17
+ const chatService = new OpenAIChatService(env.defaultModel, codeAssistClient, modelCatalogService);
18
+ return { env, oauthService, chatService, modelCatalogService };
19
+ }
20
+ export async function createApp(input = {}) {
21
+ const env = input.env ?? loadEnv();
22
+ const defaults = await createDefaultDependencies(env);
23
+ const deps = {
24
+ env,
25
+ oauthService: input.oauthService ?? defaults.oauthService,
26
+ chatService: input.chatService ?? defaults.chatService,
27
+ modelCatalogService: input.modelCatalogService ?? defaults.modelCatalogService
28
+ };
29
+ const app = Fastify({ logger: false });
30
+ await app.register(cors, { origin: true });
31
+ app.get("/health", async () => ({ ok: true }));
32
+ app.get("/v1/models", async (request, reply) => {
33
+ try {
34
+ const models = await deps.modelCatalogService.listModels();
35
+ return toOpenAIModelList(models);
36
+ }
37
+ catch (error) {
38
+ const message = error instanceof Error ? error.message : String(error);
39
+ reply.code(500);
40
+ return { error: { message } };
41
+ }
42
+ });
43
+ app.post("/v1/chat/completions", async (request, reply) => {
44
+ try {
45
+ const body = openAIChatCompletionRequestSchema.parse(request.body);
46
+ if (body.stream) {
47
+ if (!deps.chatService.stream) {
48
+ reply.code(400);
49
+ return { error: { message: "stream is not supported" } };
50
+ }
51
+ reply.hijack();
52
+ reply.raw.writeHead(200, {
53
+ "Content-Type": "text/event-stream",
54
+ "Cache-Control": "no-cache",
55
+ Connection: "keep-alive"
56
+ });
57
+ for await (const chunk of deps.chatService.stream(body)) {
58
+ reply.raw.write(`data: ${JSON.stringify(chunk)}\n\n`);
59
+ }
60
+ reply.raw.write("data: [DONE]\n\n");
61
+ reply.raw.end();
62
+ return;
63
+ }
64
+ return deps.chatService.generate(body);
65
+ }
66
+ catch (error) {
67
+ if (error instanceof ZodError) {
68
+ reply.code(400);
69
+ return {
70
+ error: {
71
+ message: error.issues.map((issue) => issue.message).join(", ")
72
+ }
73
+ };
74
+ }
75
+ reply.code(500);
76
+ const message = error instanceof Error ? error.message : String(error);
77
+ return { error: { message } };
78
+ }
79
+ });
80
+ app.get("/v1/auth/status", async () => {
81
+ try {
82
+ await deps.oauthService.getAccessToken();
83
+ return { authenticated: true };
84
+ }
85
+ catch {
86
+ return { authenticated: false };
87
+ }
88
+ });
89
+ return app;
90
+ }
91
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/server/app.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "geminimock",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "geminimock": "dist/cli.js"
7
+ },
8
+ "files": [
9
+ "dist",
10
+ "README.md"
11
+ ],
12
+ "main": "dist/cli.js",
13
+ "engines": {
14
+ "node": ">=18.17"
15
+ },
16
+ "scripts": {
17
+ "dev": "bun --watch src/cli.ts serve",
18
+ "start": "bun src/cli.ts serve",
19
+ "auth:login": "bun src/cli.ts auth login",
20
+ "auth:logout": "bun src/cli.ts auth logout",
21
+ "models:list": "bun src/cli.ts models list",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "lint": "eslint .",
25
+ "typecheck": "tsc --noEmit",
26
+ "build": "tsc -p tsconfig.build.json && node scripts/prepare-cli-bin.mjs",
27
+ "prepack": "npm run build"
28
+ },
29
+ "dependencies": {
30
+ "@fastify/cors": "^11.2.0",
31
+ "fastify": "^5.7.4",
32
+ "google-auth-library": "^10.6.1",
33
+ "zod": "^4.3.6"
34
+ },
35
+ "devDependencies": {
36
+ "@types/bun": "latest",
37
+ "@types/node": "^25.3.2",
38
+ "@typescript-eslint/eslint-plugin": "^8.56.1",
39
+ "@typescript-eslint/parser": "^8.56.1",
40
+ "eslint": "^10.0.2",
41
+ "eslint-config-prettier": "^10.1.8",
42
+ "typescript": "^5.9.3",
43
+ "vitest": "^4.0.18"
44
+ }
45
+ }