agent tool and toolbox
- created AiTool and AiToolbox for representing tools in the API - add googleapis dependency - integrate Google Search tool as first agent tool - created IAiEnvironment to communicate AI environment vars around the platform
This commit is contained in:
parent
819654e20a
commit
f8dbb2e08a
@ -94,6 +94,12 @@ export default {
|
|||||||
sameSite: yamlConfig.session?.cookie?.sameSite || false,
|
sameSite: yamlConfig.session?.cookie?.sameSite || false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
google: {
|
||||||
|
cse: {
|
||||||
|
apiKey: yamlConfig.google.cse.apiKey,
|
||||||
|
engineId: yamlConfig.google.cse.engineId,
|
||||||
|
},
|
||||||
|
},
|
||||||
mongodb: {
|
mongodb: {
|
||||||
host: yamlConfig.mongodb?.host || "localhost",
|
host: yamlConfig.mongodb?.host || "localhost",
|
||||||
database: yamlConfig.mongodb?.database || "",
|
database: yamlConfig.mongodb?.database || "",
|
||||||
|
|||||||
@ -2,6 +2,16 @@
|
|||||||
// Copyright (C) 2026 Robert Colbert <rob.colbert@openplatform.us>
|
// Copyright (C) 2026 Robert Colbert <rob.colbert@openplatform.us>
|
||||||
// All Rights Reserved
|
// All Rights Reserved
|
||||||
|
|
||||||
|
import env from "./config/env.js";
|
||||||
|
const aiEnv: IAiEnvironment = {
|
||||||
|
NODE_ENV: env.NODE_ENV || "develop",
|
||||||
|
google: {
|
||||||
|
cse: {
|
||||||
|
apiKey: env.google.cse.apiKey,
|
||||||
|
engineId: env.google.cse.engineId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
import "./lib/db.js";
|
import "./lib/db.js";
|
||||||
@ -30,7 +40,7 @@ import {
|
|||||||
* App Logic
|
* App Logic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createAiApi, type IAiLogger } from "@gadget/ai";
|
import { createAiApi, IAiEnvironment, type IAiLogger } from "@gadget/ai";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IUser,
|
IUser,
|
||||||
@ -551,6 +561,7 @@ class DtpWebCli extends DtpProcess {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const api = createAiApi(
|
const api = createAiApi(
|
||||||
|
aiEnv,
|
||||||
{
|
{
|
||||||
_id: provider._id,
|
_id: provider._id,
|
||||||
name: provider.name,
|
name: provider.name,
|
||||||
|
|||||||
@ -37,7 +37,7 @@ async function readJsonFile<T>(filePath: string): Promise<T> {
|
|||||||
|
|
||||||
/* eslint-disable no-process-env */
|
/* eslint-disable no-process-env */
|
||||||
export default {
|
export default {
|
||||||
NODE_ENV: process.env.NODE_ENV,
|
NODE_ENV: process.env.NODE_ENV || "develop",
|
||||||
timezone: yamlConfig.timezone || "America/New_York",
|
timezone: yamlConfig.timezone || "America/New_York",
|
||||||
installDir: INSTALL_DIR,
|
installDir: INSTALL_DIR,
|
||||||
pkg: await readJsonFile<typeof PackageJson>(
|
pkg: await readJsonFile<typeof PackageJson>(
|
||||||
@ -47,6 +47,12 @@ export default {
|
|||||||
baseUrl: yamlConfig.platform.baseUrl,
|
baseUrl: yamlConfig.platform.baseUrl,
|
||||||
gadgetKey: yamlConfig.platform.gadgetKey,
|
gadgetKey: yamlConfig.platform.gadgetKey,
|
||||||
},
|
},
|
||||||
|
google: {
|
||||||
|
cse: {
|
||||||
|
apiKey: yamlConfig.google?.cse?.apiKey,
|
||||||
|
engineId: yamlConfig.google?.cse?.engineId,
|
||||||
|
},
|
||||||
|
},
|
||||||
log: {
|
log: {
|
||||||
console: {
|
console: {
|
||||||
enabled: yamlConfig.logging?.console?.enabled === true,
|
enabled: yamlConfig.logging?.console?.enabled === true,
|
||||||
|
|||||||
@ -2,8 +2,10 @@
|
|||||||
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
// Licensed under the Apache License, Version 2.0
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
import { IAiProvider as DbAiProvider } from "@gadget/api";
|
import env from "../config/env.ts";
|
||||||
import { GadgetService } from "../lib/service.ts";
|
|
||||||
|
import { IAiProvider as DbAiProvider, GadgetId } from "@gadget/api";
|
||||||
|
import { GadgetService } from "../lib/service.js";
|
||||||
import {
|
import {
|
||||||
type IAiChatOptions,
|
type IAiChatOptions,
|
||||||
type IAiChatResponse,
|
type IAiChatResponse,
|
||||||
@ -13,8 +15,8 @@ import {
|
|||||||
type IAiProvider as AiProviderConfig,
|
type IAiProvider as AiProviderConfig,
|
||||||
type IAiResponseStreamFn,
|
type IAiResponseStreamFn,
|
||||||
createAiApi,
|
createAiApi,
|
||||||
|
IAiEnvironment,
|
||||||
} from "@gadget/ai";
|
} from "@gadget/ai";
|
||||||
import { GadgetId } from "../../../packages/api/dist/lib/gadget-id.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drone-specific model config that accepts the database provider type.
|
* Drone-specific model config that accepts the database provider type.
|
||||||
@ -126,7 +128,16 @@ class AiService extends GadgetService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getApi(provider: AiProviderConfig) {
|
getApi(provider: AiProviderConfig) {
|
||||||
return createAiApi(provider, this.log);
|
const aiEnv: IAiEnvironment = {
|
||||||
|
NODE_ENV: env.NODE_ENV,
|
||||||
|
google: {
|
||||||
|
cse: {
|
||||||
|
apiKey: env.google.cse.apiKey,
|
||||||
|
engineId: env.google.cse.engineId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return createAiApi(aiEnv, provider, this.log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,10 +15,17 @@
|
|||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"dev": "tsc --watch"
|
"dev": "tsc --watch"
|
||||||
},
|
},
|
||||||
"keywords": ["gadget", "ai", "ollama", "openai", "abstraction"],
|
"keywords": [
|
||||||
|
"gadget",
|
||||||
|
"ai",
|
||||||
|
"agent",
|
||||||
|
"ollama",
|
||||||
|
"openai"
|
||||||
|
],
|
||||||
"author": "Rob Colbert",
|
"author": "Rob Colbert",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"googleapis": "^171.4.0",
|
||||||
"numeral": "^2.0.6",
|
"numeral": "^2.0.6",
|
||||||
"ollama": "^0.6.3",
|
"ollama": "^0.6.3",
|
||||||
"openai": "^6.34.0"
|
"openai": "^6.34.0"
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
// Licensed under the Apache License, Version 2.0
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
import { IAiEnvironment } from "./config/env.ts";
|
||||||
|
import { AiTool } from "./tools/tool.ts";
|
||||||
|
|
||||||
export type AiSdkType = "ollama" | "openai";
|
export type AiSdkType = "ollama" | "openai";
|
||||||
|
|
||||||
export interface IAiProvider {
|
export interface IAiProvider {
|
||||||
@ -63,6 +66,7 @@ export interface IAiChatOptions {
|
|||||||
systemPrompt?: string;
|
systemPrompt?: string;
|
||||||
userPrompt?: string;
|
userPrompt?: string;
|
||||||
context?: IContextChatMessage[];
|
context?: IContextChatMessage[];
|
||||||
|
tools?: AiTool[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IToolCall {
|
export interface IToolCall {
|
||||||
@ -133,10 +137,12 @@ export interface IAiModelProbeResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export abstract class AiApi {
|
export abstract class AiApi {
|
||||||
|
protected env: IAiEnvironment;
|
||||||
protected provider: IAiProvider;
|
protected provider: IAiProvider;
|
||||||
protected log: IAiLogger;
|
protected log: IAiLogger;
|
||||||
|
|
||||||
constructor(provider: IAiProvider, logger?: IAiLogger) {
|
constructor(env: IAiEnvironment, provider: IAiProvider, logger?: IAiLogger) {
|
||||||
|
this.env = env;
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.log = logger ?? defaultLogger();
|
this.log = logger ?? defaultLogger();
|
||||||
}
|
}
|
||||||
|
|||||||
12
packages/ai/src/config/env.ts
Normal file
12
packages/ai/src/config/env.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
export interface IAiEnvironment {
|
||||||
|
NODE_ENV: string;
|
||||||
|
google: {
|
||||||
|
cse: {
|
||||||
|
apiKey: string | undefined;
|
||||||
|
engineId: string | undefined;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
// Licensed under the Apache License, Version 2.0
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
export { IAiEnvironment } from "./config/env.ts";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type AiSdkType,
|
type AiSdkType,
|
||||||
type IAiProvider,
|
type IAiProvider,
|
||||||
@ -29,13 +31,18 @@ import { OpenAiApi } from "./openai.js";
|
|||||||
import type { IAiProvider } from "./api.js";
|
import type { IAiProvider } from "./api.js";
|
||||||
import type { IAiLogger } from "./api.js";
|
import type { IAiLogger } from "./api.js";
|
||||||
import type { AiApi } from "./api.js";
|
import type { AiApi } from "./api.js";
|
||||||
|
import { IAiEnvironment } from "./config/env.ts";
|
||||||
|
|
||||||
export function createAiApi(provider: IAiProvider, logger?: IAiLogger): AiApi {
|
export function createAiApi(
|
||||||
|
env: IAiEnvironment,
|
||||||
|
provider: IAiProvider,
|
||||||
|
logger?: IAiLogger,
|
||||||
|
): AiApi {
|
||||||
switch (provider.sdk) {
|
switch (provider.sdk) {
|
||||||
case "ollama":
|
case "ollama":
|
||||||
return new OllamaAiApi(provider, logger);
|
return new OllamaAiApi(env, provider, logger);
|
||||||
case "openai":
|
case "openai":
|
||||||
return new OpenAiApi(provider, logger);
|
return new OpenAiApi(env, provider, logger);
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown AI SDK: ${(provider as IAiProvider).sdk}`);
|
throw new Error(`Unknown AI SDK: ${(provider as IAiProvider).sdk}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,12 +19,13 @@ import {
|
|||||||
IAiProvider,
|
IAiProvider,
|
||||||
IAiResponseStreamFn,
|
IAiResponseStreamFn,
|
||||||
} from "./api.js";
|
} from "./api.js";
|
||||||
|
import { IAiEnvironment } from "./config/env.ts";
|
||||||
|
|
||||||
export class OllamaAiApi extends AiApi {
|
export class OllamaAiApi extends AiApi {
|
||||||
protected client: Ollama;
|
protected client: Ollama;
|
||||||
|
|
||||||
constructor(provider: IAiProvider, logger?: IAiLogger) {
|
constructor(env: IAiEnvironment, provider: IAiProvider, logger?: IAiLogger) {
|
||||||
super(provider, logger);
|
super(env, provider, logger);
|
||||||
this.client = new Ollama({
|
this.client = new Ollama({
|
||||||
host: this.provider.baseUrl,
|
host: this.provider.baseUrl,
|
||||||
headers: { Authorization: `Bearer ${this.provider.apiKey}` },
|
headers: { Authorization: `Bearer ${this.provider.apiKey}` },
|
||||||
@ -105,7 +106,8 @@ export class OllamaAiApi extends AiApi {
|
|||||||
!!modelInfo?.["clip"],
|
!!modelInfo?.["clip"],
|
||||||
hasEmbedding: capabilities.includes("embeddings"),
|
hasEmbedding: capabilities.includes("embeddings"),
|
||||||
hasThinking: capabilities.includes("reasoning"),
|
hasThinking: capabilities.includes("reasoning"),
|
||||||
isInstructTuned: modelId.toLowerCase().includes("instruct") ||
|
isInstructTuned:
|
||||||
|
modelId.toLowerCase().includes("instruct") ||
|
||||||
modelId.toLowerCase().includes("chat") ||
|
modelId.toLowerCase().includes("chat") ||
|
||||||
modelId.toLowerCase().includes("-it"),
|
modelId.toLowerCase().includes("-it"),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -17,6 +17,11 @@ import {
|
|||||||
IAiProvider,
|
IAiProvider,
|
||||||
IAiResponseStreamFn,
|
IAiResponseStreamFn,
|
||||||
} from "./api.js";
|
} from "./api.js";
|
||||||
|
import {
|
||||||
|
ChatCompletionFunctionTool,
|
||||||
|
ChatCompletionTool,
|
||||||
|
} from "openai/resources";
|
||||||
|
import { IAiEnvironment } from "./config/env.ts";
|
||||||
|
|
||||||
interface GabAiCapabilities {
|
interface GabAiCapabilities {
|
||||||
text?: boolean;
|
text?: boolean;
|
||||||
@ -50,8 +55,8 @@ interface OpenAIModelInfo {
|
|||||||
export class OpenAiApi extends AiApi {
|
export class OpenAiApi extends AiApi {
|
||||||
protected client: OpenAI;
|
protected client: OpenAI;
|
||||||
|
|
||||||
constructor(provider: IAiProvider, logger?: IAiLogger) {
|
constructor(env: IAiEnvironment, provider: IAiProvider, logger?: IAiLogger) {
|
||||||
super(provider, logger);
|
super(env, provider, logger);
|
||||||
this.client = new OpenAI({
|
this.client = new OpenAI({
|
||||||
baseURL: provider.baseUrl,
|
baseURL: provider.baseUrl,
|
||||||
apiKey: provider.apiKey,
|
apiKey: provider.apiKey,
|
||||||
@ -127,12 +132,15 @@ export class OpenAiApi extends AiApi {
|
|||||||
return {
|
return {
|
||||||
capabilities: {
|
capabilities: {
|
||||||
canCallTools: modelId.toLowerCase().includes("gpt"),
|
canCallTools: modelId.toLowerCase().includes("gpt"),
|
||||||
hasVision: modelId.toLowerCase().includes("vision") ||
|
hasVision:
|
||||||
|
modelId.toLowerCase().includes("vision") ||
|
||||||
modelId.toLowerCase().includes("4o") ||
|
modelId.toLowerCase().includes("4o") ||
|
||||||
modelId.toLowerCase().includes("image"),
|
modelId.toLowerCase().includes("image"),
|
||||||
hasEmbedding: modelId.toLowerCase().includes("embedding") ||
|
hasEmbedding:
|
||||||
|
modelId.toLowerCase().includes("embedding") ||
|
||||||
modelId.toLowerCase().includes("embed"),
|
modelId.toLowerCase().includes("embed"),
|
||||||
hasThinking: modelId.toLowerCase().includes("o1") ||
|
hasThinking:
|
||||||
|
modelId.toLowerCase().includes("o1") ||
|
||||||
modelId.toLowerCase().includes("o3") ||
|
modelId.toLowerCase().includes("o3") ||
|
||||||
modelId.toLowerCase().includes("reasoning"),
|
modelId.toLowerCase().includes("reasoning"),
|
||||||
isInstructTuned: true,
|
isInstructTuned: true,
|
||||||
@ -242,9 +250,24 @@ export class OpenAiApi extends AiApi {
|
|||||||
messages.push({ role: "user" as const, content: options.userPrompt });
|
messages.push({ role: "user" as const, content: options.userPrompt });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tools: ChatCompletionTool[] = options.tools
|
||||||
|
? options.tools.map((tool) => {
|
||||||
|
const openaiTool: ChatCompletionFunctionTool = {
|
||||||
|
type: tool.definition.type,
|
||||||
|
function: {
|
||||||
|
name: tool.definition.function.name,
|
||||||
|
description: tool.definition.function.description,
|
||||||
|
parameters: tool.definition.function.parameters,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return openaiTool;
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
const response = await this.client.chat.completions.create({
|
const response = await this.client.chat.completions.create({
|
||||||
model: model.modelId,
|
model: model.modelId,
|
||||||
messages,
|
messages,
|
||||||
|
tools,
|
||||||
stream: false,
|
stream: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
76
packages/ai/src/toolbox.ts
Normal file
76
packages/ai/src/toolbox.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
import { IAiEnvironment } from "./config/env.ts";
|
||||||
|
import { AiTool } from "./tools/tool.ts";
|
||||||
|
|
||||||
|
export type ToolMap = Map<string, AiTool>;
|
||||||
|
export type ToolSet = Set<AiTool>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No. I don't want to create an "MCP" server. I just want an in-process
|
||||||
|
* toolbox that the agents can use in their daily work. That's not too much to
|
||||||
|
* ask, dammit.
|
||||||
|
*/
|
||||||
|
export class AiToolbox {
|
||||||
|
private _env: IAiEnvironment;
|
||||||
|
get env(): IAiEnvironment {
|
||||||
|
return this._env;
|
||||||
|
}
|
||||||
|
|
||||||
|
private tools: ToolMap = new Map<string, AiTool>();
|
||||||
|
private modeSets: Map<string, ToolSet> = new Map<string, Set<AiTool>>();
|
||||||
|
|
||||||
|
constructor(env: IAiEnvironment) {
|
||||||
|
this._env = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an AiTool instance for use by the platform. If no ChatSessionMode
|
||||||
|
* modes are specified, you are registering a system tool - such as the chat
|
||||||
|
* session auto-naming tool - which are not called by agents (they are called)
|
||||||
|
* by the platform itself, deterministically.
|
||||||
|
* @param tool the tool being registered for use by the platform
|
||||||
|
* @param modes the optional name(s) of the mode for which the tool is being
|
||||||
|
* registered
|
||||||
|
*/
|
||||||
|
register(tool: AiTool, modes?: string[]): void {
|
||||||
|
if (this.tools.has(tool.name)) {
|
||||||
|
throw new Error(`tool already registered: ${tool.name}`);
|
||||||
|
}
|
||||||
|
this.tools.set(tool.name, tool);
|
||||||
|
|
||||||
|
if (!modes) {
|
||||||
|
return; // system tools aren't listed in the modes for agent use
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const mode of modes) {
|
||||||
|
let set = this.modeSets.get(mode);
|
||||||
|
if (!set) {
|
||||||
|
set = new Set<AiTool>();
|
||||||
|
this.modeSets.set(mode, set);
|
||||||
|
}
|
||||||
|
set.add(tool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a tool instance from the toolbox by name, ignoring mode(s). This
|
||||||
|
* is how the system fetches system tools for use.
|
||||||
|
* @param name the name of the tool to be retrieved
|
||||||
|
* @returns the tool, or undefined if the tool is not registered
|
||||||
|
*/
|
||||||
|
getTool(name: string): AiTool | undefined {
|
||||||
|
return this.tools.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the set of tools registered for use in a given ChatSessionMode.
|
||||||
|
* @param mode the ChatSessionMode for which a set of tools is being requested
|
||||||
|
* @returns the set of tools, or undefined if there is not set for the mode
|
||||||
|
* @todo the mode parameter should be the ChatSessionMode enum
|
||||||
|
*/
|
||||||
|
getModeSet(mode: string): ToolSet | undefined {
|
||||||
|
return this.modeSets.get(mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
273
packages/ai/src/tools/search/google.ts
Normal file
273
packages/ai/src/tools/search/google.ts
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
import { IAiLogger } from "../../api.ts";
|
||||||
|
import { formatError } from "../tool-error.ts";
|
||||||
|
import { AiTool, IToolArguments, IToolDefinition } from "../tool.ts";
|
||||||
|
|
||||||
|
import { google } from "googleapis";
|
||||||
|
// import SearchService, { SearchServiceError } from "../../services/search.js";
|
||||||
|
|
||||||
|
export interface ISearchResult {
|
||||||
|
title: string;
|
||||||
|
link: string;
|
||||||
|
snippet: string;
|
||||||
|
image?: string;
|
||||||
|
position?: number;
|
||||||
|
displayLink?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISearchOptions {
|
||||||
|
num?: number;
|
||||||
|
siteSearch?: string;
|
||||||
|
dateRestrict?: string;
|
||||||
|
fileType?: string;
|
||||||
|
safe?: "active" | "off";
|
||||||
|
sort?: "relevance" | "date";
|
||||||
|
start?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GoogleSearchTool extends AiTool {
|
||||||
|
get name(): string {
|
||||||
|
return "search_google";
|
||||||
|
}
|
||||||
|
|
||||||
|
get category(): string {
|
||||||
|
return "search";
|
||||||
|
}
|
||||||
|
|
||||||
|
public definition: IToolDefinition = {
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: this.name,
|
||||||
|
description:
|
||||||
|
"Perform a Google search for relevant information on the web.",
|
||||||
|
parameters: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
query: {
|
||||||
|
type: "string",
|
||||||
|
description: "The search query string.",
|
||||||
|
},
|
||||||
|
num_results: {
|
||||||
|
type: "number",
|
||||||
|
description:
|
||||||
|
"Number of search results to return (default: 10, max: 10).",
|
||||||
|
},
|
||||||
|
siteSearch: {
|
||||||
|
type: "string",
|
||||||
|
description:
|
||||||
|
"Optional site to restrict the search to (e.g. github.com).",
|
||||||
|
},
|
||||||
|
dateRestrict: {
|
||||||
|
type: "string",
|
||||||
|
description:
|
||||||
|
"Restricts results to documents based on a date range. Examples: d1 (last day), d7 (last week), d30 (last month), d365 (last year).",
|
||||||
|
},
|
||||||
|
fileType: {
|
||||||
|
type: "string",
|
||||||
|
description:
|
||||||
|
"Restricts results to files of a specified extension. Examples: pdf, doc, xls, ppt.",
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
type: "string",
|
||||||
|
description:
|
||||||
|
"Sort order for results. Values: 'relevance' (default) or 'date'.",
|
||||||
|
enum: ["relevance", "date"],
|
||||||
|
},
|
||||||
|
start: {
|
||||||
|
type: "number",
|
||||||
|
description:
|
||||||
|
"The index of the first result to return (for pagination). Default: 1.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["query"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
public async execute(
|
||||||
|
args: IToolArguments,
|
||||||
|
logger: IAiLogger,
|
||||||
|
): Promise<string> {
|
||||||
|
const { query } = args;
|
||||||
|
|
||||||
|
if (!query || typeof query !== "string" || query.trim().length === 0) {
|
||||||
|
return formatError({
|
||||||
|
code: "MISSING_PARAMETER",
|
||||||
|
message: "The 'query' parameter is required.",
|
||||||
|
parameter: "query",
|
||||||
|
expected: "A non-empty string containing the search query.",
|
||||||
|
example: 'search_google(query: "latest AI news")',
|
||||||
|
recoveryHint:
|
||||||
|
"Provide a 'query' parameter with your search terms and try again.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("performing Google search for user", { args });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
num_results = 10,
|
||||||
|
siteSearch,
|
||||||
|
dateRestrict,
|
||||||
|
fileType,
|
||||||
|
sort,
|
||||||
|
start,
|
||||||
|
} = args;
|
||||||
|
|
||||||
|
const results = await this.search(query, {
|
||||||
|
num: Math.min(num_results as number, 10),
|
||||||
|
siteSearch: siteSearch as string | undefined,
|
||||||
|
dateRestrict: dateRestrict as string | undefined,
|
||||||
|
fileType: fileType as string | undefined,
|
||||||
|
safe: "active",
|
||||||
|
sort: sort as "relevance" | "date" | undefined,
|
||||||
|
start: start as number | undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.debug("Google search results", { results });
|
||||||
|
|
||||||
|
let content = "";
|
||||||
|
if (results && results.length) {
|
||||||
|
content += `Here are some relevant search results I found:\n\n`;
|
||||||
|
for (const result of results) {
|
||||||
|
const title = JSON.stringify(result.title || "").slice(1, -1);
|
||||||
|
const link = JSON.stringify(result.link || "").slice(1, -1);
|
||||||
|
const snippet = JSON.stringify(result.snippet || "").slice(1, -1);
|
||||||
|
const displayLink = result.displayLink
|
||||||
|
? JSON.stringify(result.displayLink).slice(1, -1)
|
||||||
|
: "";
|
||||||
|
|
||||||
|
content += `Title: ${title}\n`;
|
||||||
|
content += `Link: ${link}\n`;
|
||||||
|
if (displayLink) {
|
||||||
|
content += `Source: ${displayLink}\n`;
|
||||||
|
}
|
||||||
|
content += `Snippet: ${snippet}\n\n`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
content += "No relevant search results found.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
} catch (error: any) {
|
||||||
|
// Generic error handling
|
||||||
|
return formatError({
|
||||||
|
code: "OPERATION_FAILED",
|
||||||
|
message: `Failed to perform search: ${error.message}`,
|
||||||
|
recoveryHint: "Please try again or check your search query.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async search(
|
||||||
|
query: string,
|
||||||
|
options: ISearchOptions,
|
||||||
|
): Promise<ISearchResult[]> {
|
||||||
|
const customSearch = google.customsearch({
|
||||||
|
version: "v1",
|
||||||
|
auth: this.toolbox.env.google.cse.apiKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
const params: any = {
|
||||||
|
q: query,
|
||||||
|
cx: this.toolbox.env.google.cse.engineId,
|
||||||
|
num: options.num || 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.siteSearch) {
|
||||||
|
params.siteSearch = options.siteSearch;
|
||||||
|
}
|
||||||
|
if (options.dateRestrict) {
|
||||||
|
params.dateRestrict = options.dateRestrict;
|
||||||
|
}
|
||||||
|
if (options.fileType) {
|
||||||
|
params.fileType = options.fileType;
|
||||||
|
}
|
||||||
|
if (options.safe) {
|
||||||
|
params.safe = options.safe;
|
||||||
|
}
|
||||||
|
if (options.sort) {
|
||||||
|
params.sort = options.sort;
|
||||||
|
}
|
||||||
|
if (options.start) {
|
||||||
|
params.start = options.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await customSearch.cse.list(params);
|
||||||
|
const results: ISearchResult[] = [];
|
||||||
|
|
||||||
|
if (response.data.items) {
|
||||||
|
response.data.items.forEach((item: any, index: number) => {
|
||||||
|
const result: ISearchResult = {
|
||||||
|
title: item.title || "",
|
||||||
|
link: item.link || "",
|
||||||
|
snippet: item.snippet || "",
|
||||||
|
position: item.position || index + 1,
|
||||||
|
displayLink: item.displayLink || "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extract thumbnail if available
|
||||||
|
if (item.pagemap?.cse_thumbnail?.[0]?.src) {
|
||||||
|
result.image = item.pagemap.cse_thumbnail[0].src;
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse Google CSE API errors and provide meaningful messages
|
||||||
|
*/
|
||||||
|
private parseCseError(error: any): string {
|
||||||
|
// Handle Google API error structure
|
||||||
|
if (error.response?.data?.error) {
|
||||||
|
const apiError = error.response.data.error;
|
||||||
|
const statusCode = error.response.status;
|
||||||
|
|
||||||
|
switch (statusCode) {
|
||||||
|
case 401:
|
||||||
|
formatError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: `401 Unauthorized - Invalid API key. ${apiError.message || ""}`,
|
||||||
|
});
|
||||||
|
case 403:
|
||||||
|
return formatError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: `403 Forbidden - ${apiError.message || "Access denied"}. Check your Engine ID and API key permissions.`,
|
||||||
|
});
|
||||||
|
case 429:
|
||||||
|
const retryAfter = error.response.headers?.["retry-after"];
|
||||||
|
return formatError({
|
||||||
|
code: "RATE_LIMIT_EXCEEDED",
|
||||||
|
message: `429 Too Many Requests - Rate limit exceeded. ${retryAfter ? `Retry after: ${retryAfter} seconds.` : ""} ${apiError.message || ""}`,
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatError({
|
||||||
|
code: "TOOL_EXECUTION_FAILED",
|
||||||
|
message: `HTTP ${statusCode} - ${apiError.message || "Unknown error"}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle network errors
|
||||||
|
if (error.code === "ENOTFOUND") {
|
||||||
|
return formatError({
|
||||||
|
code: "NETWORK_ERROR",
|
||||||
|
message: `Network error (code: ${error.code}) - Unable to reach Google API`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic error
|
||||||
|
return formatError({
|
||||||
|
code: error.code || "UNKNOWN_ERROR",
|
||||||
|
message: error.message || "An unknown error occurred",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
50
packages/ai/src/tools/tool-error.ts
Normal file
50
packages/ai/src/tools/tool-error.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
export type ToolErrorCode =
|
||||||
|
| "MISSING_PARAMETER"
|
||||||
|
| "INVALID_PARAMETER"
|
||||||
|
| "NOT_FOUND"
|
||||||
|
| "PERMISSION_DENIED"
|
||||||
|
| "OPERATION_FAILED"
|
||||||
|
| "OPERATION_NOT_ALLOWED"
|
||||||
|
| "VALIDATION_ERROR"
|
||||||
|
| "RATE_LIMITED"
|
||||||
|
| "LIMIT_EXCEEDED"
|
||||||
|
| "INVALID_CRON_SPEC"
|
||||||
|
| "INVALID_OPERATION"
|
||||||
|
| "TIMEOUT"
|
||||||
|
| "INVALID_TOOL_ARGUMENTS"
|
||||||
|
| "SUBAGENT_FAILED"
|
||||||
|
| "TOOL_EXECUTION_FAILED"
|
||||||
|
| "UNAUTHORIZED"
|
||||||
|
| "FORBIDDEN"
|
||||||
|
| "RATE_LIMIT_EXCEEDED"
|
||||||
|
| "NETWORK_ERROR"
|
||||||
|
| "SECURITY_VIOLATION";
|
||||||
|
|
||||||
|
export interface IToolError {
|
||||||
|
code: ToolErrorCode;
|
||||||
|
message: string;
|
||||||
|
parameter?: string;
|
||||||
|
expected?: string;
|
||||||
|
example?: string;
|
||||||
|
recoveryHint?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatError(error: IToolError): string {
|
||||||
|
const components: string[] = [`TOOL ERROR: ${error.code}`, error.message];
|
||||||
|
if (error.parameter) {
|
||||||
|
components.push(`PARAMETER: ${error.parameter}`);
|
||||||
|
}
|
||||||
|
if (error.expected) {
|
||||||
|
components.push(`EXPECTED: ${error.expected}`);
|
||||||
|
}
|
||||||
|
if (error.example) {
|
||||||
|
components.push(`EXAMPLE: ${error.example}`);
|
||||||
|
}
|
||||||
|
if (error.recoveryHint) {
|
||||||
|
components.push(`RECOVERY HINT: ${error.recoveryHint}`);
|
||||||
|
}
|
||||||
|
return components.join("\n");
|
||||||
|
}
|
||||||
35
packages/ai/src/tools/tool.ts
Normal file
35
packages/ai/src/tools/tool.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
|
||||||
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
|
import { IAiLogger } from "../api.ts";
|
||||||
|
import { AiToolbox } from "../toolbox.ts";
|
||||||
|
|
||||||
|
export interface IToolArguments {
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IToolDefinition {
|
||||||
|
type: "function";
|
||||||
|
function: {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
parameters: IToolArguments;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class AiTool {
|
||||||
|
protected _toolbox: AiToolbox;
|
||||||
|
get toolbox(): AiToolbox {
|
||||||
|
return this._toolbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(toolbox: AiToolbox) {
|
||||||
|
this._toolbox = toolbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract get name(): string;
|
||||||
|
abstract get category(): string;
|
||||||
|
abstract get definition(): IToolDefinition;
|
||||||
|
|
||||||
|
abstract execute(args: IToolArguments, logger: IAiLogger): Promise<string>;
|
||||||
|
}
|
||||||
@ -30,6 +30,12 @@ export interface GadgetCodeConfig {
|
|||||||
sameSite?: boolean | "lax" | "strict" | "none";
|
sameSite?: boolean | "lax" | "strict" | "none";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
google: {
|
||||||
|
cse: {
|
||||||
|
apiKey: string;
|
||||||
|
engineId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
mongodb: {
|
mongodb: {
|
||||||
host: string;
|
host: string;
|
||||||
database: string;
|
database: string;
|
||||||
@ -120,6 +126,12 @@ export interface GadgetDroneConfig {
|
|||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
gadgetKey: string;
|
gadgetKey: string;
|
||||||
};
|
};
|
||||||
|
google?: {
|
||||||
|
cse?: {
|
||||||
|
apiKey: string;
|
||||||
|
engineId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
logging?: {
|
logging?: {
|
||||||
console?: {
|
console?: {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
|
|||||||
158
pnpm-lock.yaml
158
pnpm-lock.yaml
@ -315,6 +315,9 @@ importers:
|
|||||||
|
|
||||||
packages/ai:
|
packages/ai:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
googleapis:
|
||||||
|
specifier: ^171.4.0
|
||||||
|
version: 171.4.0
|
||||||
numeral:
|
numeral:
|
||||||
specifier: ^2.0.6
|
specifier: ^2.0.6
|
||||||
version: 2.0.6
|
version: 2.0.6
|
||||||
@ -1502,6 +1505,10 @@ packages:
|
|||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
agent-base@7.1.4:
|
||||||
|
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
||||||
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
ansi-regex@5.0.1:
|
ansi-regex@5.0.1:
|
||||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -1594,6 +1601,9 @@ packages:
|
|||||||
bidi-js@1.0.3:
|
bidi-js@1.0.3:
|
||||||
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
|
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
|
||||||
|
|
||||||
|
bignumber.js@9.3.1:
|
||||||
|
resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
|
||||||
|
|
||||||
binary-extensions@2.3.0:
|
binary-extensions@2.3.0:
|
||||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -1832,6 +1842,10 @@ packages:
|
|||||||
csstype@3.2.3:
|
csstype@3.2.3:
|
||||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||||
|
|
||||||
|
data-uri-to-buffer@4.0.1:
|
||||||
|
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
||||||
|
engines: {node: '>= 12'}
|
||||||
|
|
||||||
data-urls@7.0.0:
|
data-urls@7.0.0:
|
||||||
resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
|
resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
|
||||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
@ -2047,6 +2061,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
|
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
|
||||||
engines: {node: '>= 18'}
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
|
extend@3.0.2:
|
||||||
|
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
||||||
|
|
||||||
fast-glob@3.3.3:
|
fast-glob@3.3.3:
|
||||||
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
|
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
|
||||||
engines: {node: '>=8.6.0'}
|
engines: {node: '>=8.6.0'}
|
||||||
@ -2082,6 +2099,10 @@ packages:
|
|||||||
picomatch:
|
picomatch:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
fetch-blob@3.2.0:
|
||||||
|
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
||||||
|
engines: {node: ^12.20 || >= 14.13}
|
||||||
|
|
||||||
fflate@0.6.10:
|
fflate@0.6.10:
|
||||||
resolution: {integrity: sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==}
|
resolution: {integrity: sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==}
|
||||||
|
|
||||||
@ -2113,6 +2134,10 @@ packages:
|
|||||||
debug:
|
debug:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
formdata-polyfill@4.0.10:
|
||||||
|
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||||
|
engines: {node: '>=12.20.0'}
|
||||||
|
|
||||||
forwarded@0.2.0:
|
forwarded@0.2.0:
|
||||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@ -2147,6 +2172,14 @@ packages:
|
|||||||
function-bind@1.1.2:
|
function-bind@1.1.2:
|
||||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
|
gaxios@7.1.4:
|
||||||
|
resolution: {integrity: sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
gcp-metadata@8.1.2:
|
||||||
|
resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
geoip-lite@1.4.10:
|
geoip-lite@1.4.10:
|
||||||
resolution: {integrity: sha512-4N69uhpS3KFd97m00wiFEefwa+L+HT5xZbzPhwu+sDawStg6UN/dPwWtUfkQuZkGIY1Cj7wDVp80IsqNtGMi2w==}
|
resolution: {integrity: sha512-4N69uhpS3KFd97m00wiFEefwa+L+HT5xZbzPhwu+sDawStg6UN/dPwWtUfkQuZkGIY1Cj7wDVp80IsqNtGMi2w==}
|
||||||
engines: {node: '>=10.3.0'}
|
engines: {node: '>=10.3.0'}
|
||||||
@ -2189,6 +2222,22 @@ packages:
|
|||||||
glsl-noise@0.0.0:
|
glsl-noise@0.0.0:
|
||||||
resolution: {integrity: sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==}
|
resolution: {integrity: sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==}
|
||||||
|
|
||||||
|
google-auth-library@10.6.2:
|
||||||
|
resolution: {integrity: sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
google-logging-utils@1.1.3:
|
||||||
|
resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
googleapis-common@8.0.1:
|
||||||
|
resolution: {integrity: sha512-eCzNACUXPb1PW5l0ULTzMHaL/ltPRADoPgjBlT8jWsTbxkCp6siv+qKJ/1ldaybCthGwsYFYallF7u9AkU4L+A==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
googleapis@171.4.0:
|
||||||
|
resolution: {integrity: sha512-xybFL2SmmUgIifgsbsRQYRdNrSAYwxWZDmkZTGjUIaRnX5jPqR8el/cEvo6rCqh7iaZx6MfEPS/lrDgZ0bymkg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
gopd@1.2.0:
|
gopd@1.2.0:
|
||||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -2235,6 +2284,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==}
|
resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==}
|
||||||
engines: {node: '>=8.0.0'}
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
|
https-proxy-agent@7.0.6:
|
||||||
|
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
|
||||||
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
iconv-lite@0.4.24:
|
iconv-lite@0.4.24:
|
||||||
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -2381,6 +2434,9 @@ packages:
|
|||||||
canvas:
|
canvas:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
json-bigint@1.0.0:
|
||||||
|
resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
|
||||||
|
|
||||||
jsonfile@3.0.1:
|
jsonfile@3.0.1:
|
||||||
resolution: {integrity: sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==}
|
resolution: {integrity: sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==}
|
||||||
|
|
||||||
@ -2732,6 +2788,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
|
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
node-domexception@1.0.0:
|
||||||
|
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||||
|
engines: {node: '>=10.5.0'}
|
||||||
|
deprecated: Use your platform's native DOMException instead
|
||||||
|
|
||||||
|
node-fetch@3.3.2:
|
||||||
|
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
||||||
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
|
||||||
node-gyp-build-optional-packages@5.2.2:
|
node-gyp-build-optional-packages@5.2.2:
|
||||||
resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==}
|
resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@ -3477,6 +3542,9 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
browserslist: '>= 4.21.0'
|
browserslist: '>= 4.21.0'
|
||||||
|
|
||||||
|
url-template@2.0.8:
|
||||||
|
resolution: {integrity: sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==}
|
||||||
|
|
||||||
use-sync-external-store@1.6.0:
|
use-sync-external-store@1.6.0:
|
||||||
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
|
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -3597,6 +3665,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
|
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
web-streams-polyfill@3.3.3:
|
||||||
|
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
webgl-constants@1.1.1:
|
webgl-constants@1.1.1:
|
||||||
resolution: {integrity: sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==}
|
resolution: {integrity: sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==}
|
||||||
|
|
||||||
@ -4635,6 +4707,8 @@ snapshots:
|
|||||||
|
|
||||||
acorn@7.4.1: {}
|
acorn@7.4.1: {}
|
||||||
|
|
||||||
|
agent-base@7.1.4: {}
|
||||||
|
|
||||||
ansi-regex@5.0.1: {}
|
ansi-regex@5.0.1: {}
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
@ -4707,6 +4781,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
require-from-string: 2.0.2
|
require-from-string: 2.0.2
|
||||||
|
|
||||||
|
bignumber.js@9.3.1: {}
|
||||||
|
|
||||||
binary-extensions@2.3.0: {}
|
binary-extensions@2.3.0: {}
|
||||||
|
|
||||||
block-stream2@2.1.0:
|
block-stream2@2.1.0:
|
||||||
@ -5005,6 +5081,8 @@ snapshots:
|
|||||||
|
|
||||||
csstype@3.2.3: {}
|
csstype@3.2.3: {}
|
||||||
|
|
||||||
|
data-uri-to-buffer@4.0.1: {}
|
||||||
|
|
||||||
data-urls@7.0.0:
|
data-urls@7.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-mimetype: 5.0.0
|
whatwg-mimetype: 5.0.0
|
||||||
@ -5276,6 +5354,8 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
extend@3.0.2: {}
|
||||||
|
|
||||||
fast-glob@3.3.3:
|
fast-glob@3.3.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nodelib/fs.stat': 2.0.5
|
'@nodelib/fs.stat': 2.0.5
|
||||||
@ -5317,6 +5397,11 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
picomatch: 4.0.4
|
picomatch: 4.0.4
|
||||||
|
|
||||||
|
fetch-blob@3.2.0:
|
||||||
|
dependencies:
|
||||||
|
node-domexception: 1.0.0
|
||||||
|
web-streams-polyfill: 3.3.3
|
||||||
|
|
||||||
fflate@0.6.10: {}
|
fflate@0.6.10: {}
|
||||||
|
|
||||||
fflate@0.8.2: {}
|
fflate@0.8.2: {}
|
||||||
@ -5352,6 +5437,10 @@ snapshots:
|
|||||||
|
|
||||||
follow-redirects@1.16.0: {}
|
follow-redirects@1.16.0: {}
|
||||||
|
|
||||||
|
formdata-polyfill@4.0.10:
|
||||||
|
dependencies:
|
||||||
|
fetch-blob: 3.2.0
|
||||||
|
|
||||||
forwarded@0.2.0: {}
|
forwarded@0.2.0: {}
|
||||||
|
|
||||||
fraction.js@5.3.4: {}
|
fraction.js@5.3.4: {}
|
||||||
@ -5376,6 +5465,22 @@ snapshots:
|
|||||||
|
|
||||||
function-bind@1.1.2: {}
|
function-bind@1.1.2: {}
|
||||||
|
|
||||||
|
gaxios@7.1.4:
|
||||||
|
dependencies:
|
||||||
|
extend: 3.0.2
|
||||||
|
https-proxy-agent: 7.0.6
|
||||||
|
node-fetch: 3.3.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
gcp-metadata@8.1.2:
|
||||||
|
dependencies:
|
||||||
|
gaxios: 7.1.4
|
||||||
|
google-logging-utils: 1.1.3
|
||||||
|
json-bigint: 1.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
geoip-lite@1.4.10:
|
geoip-lite@1.4.10:
|
||||||
dependencies:
|
dependencies:
|
||||||
async: 2.6.4
|
async: 2.6.4
|
||||||
@ -5438,6 +5543,36 @@ snapshots:
|
|||||||
|
|
||||||
glsl-noise@0.0.0: {}
|
glsl-noise@0.0.0: {}
|
||||||
|
|
||||||
|
google-auth-library@10.6.2:
|
||||||
|
dependencies:
|
||||||
|
base64-js: 1.5.1
|
||||||
|
ecdsa-sig-formatter: 1.0.11
|
||||||
|
gaxios: 7.1.4
|
||||||
|
gcp-metadata: 8.1.2
|
||||||
|
google-logging-utils: 1.1.3
|
||||||
|
jws: 4.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
google-logging-utils@1.1.3: {}
|
||||||
|
|
||||||
|
googleapis-common@8.0.1:
|
||||||
|
dependencies:
|
||||||
|
extend: 3.0.2
|
||||||
|
gaxios: 7.1.4
|
||||||
|
google-auth-library: 10.6.2
|
||||||
|
qs: 6.15.1
|
||||||
|
url-template: 2.0.8
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
googleapis@171.4.0:
|
||||||
|
dependencies:
|
||||||
|
google-auth-library: 10.6.2
|
||||||
|
googleapis-common: 8.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
gopd@1.2.0: {}
|
gopd@1.2.0: {}
|
||||||
|
|
||||||
graceful-fs@4.2.11: {}
|
graceful-fs@4.2.11: {}
|
||||||
@ -5488,6 +5623,13 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
|
|
||||||
|
https-proxy-agent@7.0.6:
|
||||||
|
dependencies:
|
||||||
|
agent-base: 7.1.4
|
||||||
|
debug: 4.4.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
iconv-lite@0.4.24:
|
iconv-lite@0.4.24:
|
||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer: 2.1.2
|
safer-buffer: 2.1.2
|
||||||
@ -5637,6 +5779,10 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@noble/hashes'
|
- '@noble/hashes'
|
||||||
|
|
||||||
|
json-bigint@1.0.0:
|
||||||
|
dependencies:
|
||||||
|
bignumber.js: 9.3.1
|
||||||
|
|
||||||
jsonfile@3.0.1:
|
jsonfile@3.0.1:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
@ -5956,6 +6102,14 @@ snapshots:
|
|||||||
|
|
||||||
negotiator@1.0.0: {}
|
negotiator@1.0.0: {}
|
||||||
|
|
||||||
|
node-domexception@1.0.0: {}
|
||||||
|
|
||||||
|
node-fetch@3.3.2:
|
||||||
|
dependencies:
|
||||||
|
data-uri-to-buffer: 4.0.1
|
||||||
|
fetch-blob: 3.2.0
|
||||||
|
formdata-polyfill: 4.0.10
|
||||||
|
|
||||||
node-gyp-build-optional-packages@5.2.2:
|
node-gyp-build-optional-packages@5.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
detect-libc: 2.1.2
|
detect-libc: 2.1.2
|
||||||
@ -6738,6 +6892,8 @@ snapshots:
|
|||||||
escalade: 3.2.0
|
escalade: 3.2.0
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
|
|
||||||
|
url-template@2.0.8: {}
|
||||||
|
|
||||||
use-sync-external-store@1.6.0(react@19.2.5):
|
use-sync-external-store@1.6.0(react@19.2.5):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.2.5
|
react: 19.2.5
|
||||||
@ -6803,6 +6959,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
xml-name-validator: 5.0.0
|
xml-name-validator: 5.0.0
|
||||||
|
|
||||||
|
web-streams-polyfill@3.3.3: {}
|
||||||
|
|
||||||
webgl-constants@1.1.1: {}
|
webgl-constants@1.1.1: {}
|
||||||
|
|
||||||
webgl-sdf-generator@1.1.1: {}
|
webgl-sdf-generator@1.1.1: {}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user