@quilltap/plugin-utils 1.0.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.
@@ -0,0 +1,109 @@
1
+ import { PluginLogger, LogContext, LogLevel } from '@quilltap/plugin-types';
2
+ export { LogContext, LogLevel, PluginLogger, createConsoleLogger, createNoopLogger } from '@quilltap/plugin-types';
3
+
4
+ /**
5
+ * Plugin Logger Bridge
6
+ *
7
+ * Provides a logger factory for plugins that automatically bridges
8
+ * to Quilltap's core logging when running inside the host application,
9
+ * or falls back to console logging when running standalone.
10
+ *
11
+ * @module @quilltap/plugin-utils/logging/plugin-logger
12
+ */
13
+
14
+ /**
15
+ * Extended logger interface with child logger support
16
+ */
17
+ interface PluginLoggerWithChild extends PluginLogger {
18
+ /**
19
+ * Create a child logger with additional context
20
+ * @param additionalContext Context to merge with parent context
21
+ * @returns A new logger with combined context
22
+ */
23
+ child(additionalContext: LogContext): PluginLoggerWithChild;
24
+ }
25
+ /**
26
+ * Type for the global Quilltap logger bridge
27
+ * Stored on globalThis to work across different npm package copies
28
+ */
29
+ declare global {
30
+ var __quilltap_logger_factory: ((pluginName: string) => PluginLoggerWithChild) | undefined;
31
+ }
32
+ /**
33
+ * Inject the core logger factory from Quilltap host
34
+ *
35
+ * This is called by Quilltap core when loading plugins to bridge
36
+ * plugin logging into the host's logging system. Uses globalThis
37
+ * to ensure it works even when plugins have their own copy of
38
+ * plugin-utils in their node_modules.
39
+ *
40
+ * **Internal API - not for plugin use**
41
+ *
42
+ * @param factory A function that creates a child logger for a plugin
43
+ */
44
+ declare function __injectCoreLoggerFactory(factory: (pluginName: string) => PluginLoggerWithChild): void;
45
+ /**
46
+ * Clear the injected core logger factory
47
+ *
48
+ * Useful for testing or when unloading the plugin system.
49
+ *
50
+ * **Internal API - not for plugin use**
51
+ */
52
+ declare function __clearCoreLoggerFactory(): void;
53
+ /**
54
+ * Check if a core logger has been injected
55
+ *
56
+ * @returns True if running inside Quilltap with core logging available
57
+ */
58
+ declare function hasCoreLogger(): boolean;
59
+ /**
60
+ * Create a plugin logger that bridges to Quilltap core logging
61
+ *
62
+ * When running inside Quilltap:
63
+ * - Routes all logs to the core logger
64
+ * - Tags logs with `{ plugin: pluginName, module: 'plugin' }`
65
+ * - Logs appear in Quilltap's combined.log and console
66
+ *
67
+ * When running standalone:
68
+ * - Falls back to console logging with `[pluginName]` prefix
69
+ * - Respects the specified minimum log level
70
+ *
71
+ * @param pluginName - The plugin identifier (e.g., 'qtap-plugin-openai')
72
+ * @param minLevel - Minimum log level when running standalone (default: 'debug')
73
+ * @returns A logger instance
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * // In your plugin's provider.ts
78
+ * import { createPluginLogger } from '@quilltap/plugin-utils';
79
+ *
80
+ * const logger = createPluginLogger('qtap-plugin-my-provider');
81
+ *
82
+ * export class MyProvider implements LLMProvider {
83
+ * async sendMessage(params: LLMParams, apiKey: string): Promise<LLMResponse> {
84
+ * logger.debug('Sending message', { model: params.model });
85
+ *
86
+ * try {
87
+ * const response = await this.client.chat({...});
88
+ * logger.info('Received response', { tokens: response.usage?.total_tokens });
89
+ * return response;
90
+ * } catch (error) {
91
+ * logger.error('Failed to send message', { model: params.model }, error as Error);
92
+ * throw error;
93
+ * }
94
+ * }
95
+ * }
96
+ * ```
97
+ */
98
+ declare function createPluginLogger(pluginName: string, minLevel?: LogLevel): PluginLoggerWithChild;
99
+ /**
100
+ * Get the minimum log level from environment
101
+ *
102
+ * Checks for LOG_LEVEL or QUILTTAP_LOG_LEVEL environment variables.
103
+ * Useful for configuring standalone plugin logging.
104
+ *
105
+ * @returns The configured log level, or 'info' as default
106
+ */
107
+ declare function getLogLevelFromEnv(): LogLevel;
108
+
109
+ export { type PluginLoggerWithChild, __clearCoreLoggerFactory, __injectCoreLoggerFactory, createPluginLogger, getLogLevelFromEnv, hasCoreLogger };
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/logging/index.ts
21
+ var logging_exports = {};
22
+ __export(logging_exports, {
23
+ __clearCoreLoggerFactory: () => __clearCoreLoggerFactory,
24
+ __injectCoreLoggerFactory: () => __injectCoreLoggerFactory,
25
+ createConsoleLogger: () => import_plugin_types.createConsoleLogger,
26
+ createNoopLogger: () => import_plugin_types.createNoopLogger,
27
+ createPluginLogger: () => createPluginLogger,
28
+ getLogLevelFromEnv: () => getLogLevelFromEnv,
29
+ hasCoreLogger: () => hasCoreLogger
30
+ });
31
+ module.exports = __toCommonJS(logging_exports);
32
+
33
+ // src/logging/plugin-logger.ts
34
+ function getCoreLoggerFactory() {
35
+ return globalThis.__quilltap_logger_factory ?? null;
36
+ }
37
+ function __injectCoreLoggerFactory(factory) {
38
+ globalThis.__quilltap_logger_factory = factory;
39
+ }
40
+ function __clearCoreLoggerFactory() {
41
+ globalThis.__quilltap_logger_factory = void 0;
42
+ }
43
+ function hasCoreLogger() {
44
+ return getCoreLoggerFactory() !== null;
45
+ }
46
+ function createConsoleLoggerWithChild(prefix, minLevel = "debug", baseContext = {}) {
47
+ const levels = ["debug", "info", "warn", "error"];
48
+ const shouldLog = (level) => levels.indexOf(level) >= levels.indexOf(minLevel);
49
+ const formatContext = (context) => {
50
+ const merged = { ...baseContext, ...context };
51
+ const entries = Object.entries(merged).filter(([key]) => key !== "context").map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(" ");
52
+ return entries ? ` ${entries}` : "";
53
+ };
54
+ const logger = {
55
+ debug: (message, context) => {
56
+ if (shouldLog("debug")) {
57
+ console.debug(`[${prefix}] ${message}${formatContext(context)}`);
58
+ }
59
+ },
60
+ info: (message, context) => {
61
+ if (shouldLog("info")) {
62
+ console.info(`[${prefix}] ${message}${formatContext(context)}`);
63
+ }
64
+ },
65
+ warn: (message, context) => {
66
+ if (shouldLog("warn")) {
67
+ console.warn(`[${prefix}] ${message}${formatContext(context)}`);
68
+ }
69
+ },
70
+ error: (message, context, error) => {
71
+ if (shouldLog("error")) {
72
+ console.error(
73
+ `[${prefix}] ${message}${formatContext(context)}`,
74
+ error ? `
75
+ ${error.stack || error.message}` : ""
76
+ );
77
+ }
78
+ },
79
+ child: (additionalContext) => {
80
+ return createConsoleLoggerWithChild(prefix, minLevel, {
81
+ ...baseContext,
82
+ ...additionalContext
83
+ });
84
+ }
85
+ };
86
+ return logger;
87
+ }
88
+ function createPluginLogger(pluginName, minLevel = "debug") {
89
+ const coreFactory = getCoreLoggerFactory();
90
+ if (coreFactory) {
91
+ return coreFactory(pluginName);
92
+ }
93
+ return createConsoleLoggerWithChild(pluginName, minLevel);
94
+ }
95
+ function getLogLevelFromEnv() {
96
+ if (typeof process !== "undefined" && process.env) {
97
+ const envLevel = process.env.LOG_LEVEL || process.env.QUILTTAP_LOG_LEVEL;
98
+ if (envLevel && ["debug", "info", "warn", "error"].includes(envLevel)) {
99
+ return envLevel;
100
+ }
101
+ }
102
+ return "info";
103
+ }
104
+
105
+ // src/logging/index.ts
106
+ var import_plugin_types = require("@quilltap/plugin-types");
107
+ // Annotate the CommonJS export names for ESM import in node:
108
+ 0 && (module.exports = {
109
+ __clearCoreLoggerFactory,
110
+ __injectCoreLoggerFactory,
111
+ createConsoleLogger,
112
+ createNoopLogger,
113
+ createPluginLogger,
114
+ getLogLevelFromEnv,
115
+ hasCoreLogger
116
+ });
117
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logging/index.ts","../../src/logging/plugin-logger.ts"],"sourcesContent":["/**\n * Logging Utilities\n *\n * Exports the plugin logger bridge and related utilities.\n *\n * @module @quilltap/plugin-utils/logging\n */\n\nexport {\n createPluginLogger,\n hasCoreLogger,\n getLogLevelFromEnv,\n __injectCoreLoggerFactory,\n __clearCoreLoggerFactory,\n} from './plugin-logger';\n\nexport type { PluginLoggerWithChild } from './plugin-logger';\n\n// Re-export logger types from plugin-types\nexport type { PluginLogger, LogContext, LogLevel } from '@quilltap/plugin-types';\n\n// Re-export logger utilities from plugin-types for convenience\nexport { createConsoleLogger, createNoopLogger } from '@quilltap/plugin-types';\n","/**\n * Plugin Logger Bridge\n *\n * Provides a logger factory for plugins that automatically bridges\n * to Quilltap's core logging when running inside the host application,\n * or falls back to console logging when running standalone.\n *\n * @module @quilltap/plugin-utils/logging/plugin-logger\n */\n\nimport type { PluginLogger, LogContext, LogLevel } from '@quilltap/plugin-types';\n\n/**\n * Extended logger interface with child logger support\n */\nexport interface PluginLoggerWithChild extends PluginLogger {\n /**\n * Create a child logger with additional context\n * @param additionalContext Context to merge with parent context\n * @returns A new logger with combined context\n */\n child(additionalContext: LogContext): PluginLoggerWithChild;\n}\n\n/**\n * Type for the global Quilltap logger bridge\n * Stored on globalThis to work across different npm package copies\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __quilltap_logger_factory:\n | ((pluginName: string) => PluginLoggerWithChild)\n | undefined;\n}\n\n/**\n * Get the core logger factory from global namespace\n *\n * @returns The injected factory or null if not in Quilltap environment\n */\nfunction getCoreLoggerFactory(): ((pluginName: string) => PluginLoggerWithChild) | null {\n return globalThis.__quilltap_logger_factory ?? null;\n}\n\n/**\n * Inject the core logger factory from Quilltap host\n *\n * This is called by Quilltap core when loading plugins to bridge\n * plugin logging into the host's logging system. Uses globalThis\n * to ensure it works even when plugins have their own copy of\n * plugin-utils in their node_modules.\n *\n * **Internal API - not for plugin use**\n *\n * @param factory A function that creates a child logger for a plugin\n */\nexport function __injectCoreLoggerFactory(\n factory: (pluginName: string) => PluginLoggerWithChild\n): void {\n globalThis.__quilltap_logger_factory = factory;\n}\n\n/**\n * Clear the injected core logger factory\n *\n * Useful for testing or when unloading the plugin system.\n *\n * **Internal API - not for plugin use**\n */\nexport function __clearCoreLoggerFactory(): void {\n globalThis.__quilltap_logger_factory = undefined;\n}\n\n/**\n * Check if a core logger has been injected\n *\n * @returns True if running inside Quilltap with core logging available\n */\nexport function hasCoreLogger(): boolean {\n return getCoreLoggerFactory() !== null;\n}\n\n/**\n * Create a console logger with child support\n *\n * @param prefix Logger prefix\n * @param minLevel Minimum log level\n * @param baseContext Base context to include in all logs\n */\nfunction createConsoleLoggerWithChild(\n prefix: string,\n minLevel: LogLevel = 'debug',\n baseContext: LogContext = {}\n): PluginLoggerWithChild {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const shouldLog = (level: LogLevel): boolean =>\n levels.indexOf(level) >= levels.indexOf(minLevel);\n\n const formatContext = (context?: LogContext): string => {\n const merged = { ...baseContext, ...context };\n const entries = Object.entries(merged)\n .filter(([key]) => key !== 'context')\n .map(([key, value]) => `${key}=${JSON.stringify(value)}`)\n .join(' ');\n return entries ? ` ${entries}` : '';\n };\n\n const logger: PluginLoggerWithChild = {\n debug: (message: string, context?: LogContext): void => {\n if (shouldLog('debug')) {\n console.debug(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n info: (message: string, context?: LogContext): void => {\n if (shouldLog('info')) {\n console.info(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n warn: (message: string, context?: LogContext): void => {\n if (shouldLog('warn')) {\n console.warn(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n error: (message: string, context?: LogContext, error?: Error): void => {\n if (shouldLog('error')) {\n console.error(\n `[${prefix}] ${message}${formatContext(context)}`,\n error ? `\\n${error.stack || error.message}` : ''\n );\n }\n },\n\n child: (additionalContext: LogContext): PluginLoggerWithChild => {\n return createConsoleLoggerWithChild(prefix, minLevel, {\n ...baseContext,\n ...additionalContext,\n });\n },\n };\n\n return logger;\n}\n\n/**\n * Create a plugin logger that bridges to Quilltap core logging\n *\n * When running inside Quilltap:\n * - Routes all logs to the core logger\n * - Tags logs with `{ plugin: pluginName, module: 'plugin' }`\n * - Logs appear in Quilltap's combined.log and console\n *\n * When running standalone:\n * - Falls back to console logging with `[pluginName]` prefix\n * - Respects the specified minimum log level\n *\n * @param pluginName - The plugin identifier (e.g., 'qtap-plugin-openai')\n * @param minLevel - Minimum log level when running standalone (default: 'debug')\n * @returns A logger instance\n *\n * @example\n * ```typescript\n * // In your plugin's provider.ts\n * import { createPluginLogger } from '@quilltap/plugin-utils';\n *\n * const logger = createPluginLogger('qtap-plugin-my-provider');\n *\n * export class MyProvider implements LLMProvider {\n * async sendMessage(params: LLMParams, apiKey: string): Promise<LLMResponse> {\n * logger.debug('Sending message', { model: params.model });\n *\n * try {\n * const response = await this.client.chat({...});\n * logger.info('Received response', { tokens: response.usage?.total_tokens });\n * return response;\n * } catch (error) {\n * logger.error('Failed to send message', { model: params.model }, error as Error);\n * throw error;\n * }\n * }\n * }\n * ```\n */\nexport function createPluginLogger(\n pluginName: string,\n minLevel: LogLevel = 'debug'\n): PluginLoggerWithChild {\n // Check for core logger factory from global namespace\n const coreFactory = getCoreLoggerFactory();\n if (coreFactory) {\n return coreFactory(pluginName);\n }\n\n // Standalone mode: use enhanced console logger\n return createConsoleLoggerWithChild(pluginName, minLevel);\n}\n\n/**\n * Get the minimum log level from environment\n *\n * Checks for LOG_LEVEL or QUILTTAP_LOG_LEVEL environment variables.\n * Useful for configuring standalone plugin logging.\n *\n * @returns The configured log level, or 'info' as default\n */\nexport function getLogLevelFromEnv(): LogLevel {\n if (typeof process !== 'undefined' && process.env) {\n const envLevel = process.env.LOG_LEVEL || process.env.QUILTTAP_LOG_LEVEL;\n if (envLevel && ['debug', 'info', 'warn', 'error'].includes(envLevel)) {\n return envLevel as LogLevel;\n }\n }\n return 'info';\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACwCA,SAAS,uBAA+E;AACtF,SAAO,WAAW,6BAA6B;AACjD;AAcO,SAAS,0BACd,SACM;AACN,aAAW,4BAA4B;AACzC;AASO,SAAS,2BAAiC;AAC/C,aAAW,4BAA4B;AACzC;AAOO,SAAS,gBAAyB;AACvC,SAAO,qBAAqB,MAAM;AACpC;AASA,SAAS,6BACP,QACA,WAAqB,SACrB,cAA0B,CAAC,GACJ;AACvB,QAAM,SAAqB,CAAC,SAAS,QAAQ,QAAQ,OAAO;AAC5D,QAAM,YAAY,CAAC,UACjB,OAAO,QAAQ,KAAK,KAAK,OAAO,QAAQ,QAAQ;AAElD,QAAM,gBAAgB,CAAC,YAAiC;AACtD,UAAM,SAAS,EAAE,GAAG,aAAa,GAAG,QAAQ;AAC5C,UAAM,UAAU,OAAO,QAAQ,MAAM,EAClC,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,SAAS,EACnC,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,IAAI,KAAK,UAAU,KAAK,CAAC,EAAE,EACvD,KAAK,GAAG;AACX,WAAO,UAAU,IAAI,OAAO,KAAK;AAAA,EACnC;AAEA,QAAM,SAAgC;AAAA,IACpC,OAAO,CAAC,SAAiB,YAA+B;AACtD,UAAI,UAAU,OAAO,GAAG;AACtB,gBAAQ,MAAM,IAAI,MAAM,KAAK,OAAO,GAAG,cAAc,OAAO,CAAC,EAAE;AAAA,MACjE;AAAA,IACF;AAAA,IAEA,MAAM,CAAC,SAAiB,YAA+B;AACrD,UAAI,UAAU,MAAM,GAAG;AACrB,gBAAQ,KAAK,IAAI,MAAM,KAAK,OAAO,GAAG,cAAc,OAAO,CAAC,EAAE;AAAA,MAChE;AAAA,IACF;AAAA,IAEA,MAAM,CAAC,SAAiB,YAA+B;AACrD,UAAI,UAAU,MAAM,GAAG;AACrB,gBAAQ,KAAK,IAAI,MAAM,KAAK,OAAO,GAAG,cAAc,OAAO,CAAC,EAAE;AAAA,MAChE;AAAA,IACF;AAAA,IAEA,OAAO,CAAC,SAAiB,SAAsB,UAAwB;AACrE,UAAI,UAAU,OAAO,GAAG;AACtB,gBAAQ;AAAA,UACN,IAAI,MAAM,KAAK,OAAO,GAAG,cAAc,OAAO,CAAC;AAAA,UAC/C,QAAQ;AAAA,EAAK,MAAM,SAAS,MAAM,OAAO,KAAK;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAAA,IAEA,OAAO,CAAC,sBAAyD;AAC/D,aAAO,6BAA6B,QAAQ,UAAU;AAAA,QACpD,GAAG;AAAA,QACH,GAAG;AAAA,MACL,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAyCO,SAAS,mBACd,YACA,WAAqB,SACE;AAEvB,QAAM,cAAc,qBAAqB;AACzC,MAAI,aAAa;AACf,WAAO,YAAY,UAAU;AAAA,EAC/B;AAGA,SAAO,6BAA6B,YAAY,QAAQ;AAC1D;AAUO,SAAS,qBAA+B;AAC7C,MAAI,OAAO,YAAY,eAAe,QAAQ,KAAK;AACjD,UAAM,WAAW,QAAQ,IAAI,aAAa,QAAQ,IAAI;AACtD,QAAI,YAAY,CAAC,SAAS,QAAQ,QAAQ,OAAO,EAAE,SAAS,QAAQ,GAAG;AACrE,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;ADjMA,0BAAsD;","names":[]}
@@ -0,0 +1,84 @@
1
+ // src/logging/plugin-logger.ts
2
+ function getCoreLoggerFactory() {
3
+ return globalThis.__quilltap_logger_factory ?? null;
4
+ }
5
+ function __injectCoreLoggerFactory(factory) {
6
+ globalThis.__quilltap_logger_factory = factory;
7
+ }
8
+ function __clearCoreLoggerFactory() {
9
+ globalThis.__quilltap_logger_factory = void 0;
10
+ }
11
+ function hasCoreLogger() {
12
+ return getCoreLoggerFactory() !== null;
13
+ }
14
+ function createConsoleLoggerWithChild(prefix, minLevel = "debug", baseContext = {}) {
15
+ const levels = ["debug", "info", "warn", "error"];
16
+ const shouldLog = (level) => levels.indexOf(level) >= levels.indexOf(minLevel);
17
+ const formatContext = (context) => {
18
+ const merged = { ...baseContext, ...context };
19
+ const entries = Object.entries(merged).filter(([key]) => key !== "context").map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(" ");
20
+ return entries ? ` ${entries}` : "";
21
+ };
22
+ const logger = {
23
+ debug: (message, context) => {
24
+ if (shouldLog("debug")) {
25
+ console.debug(`[${prefix}] ${message}${formatContext(context)}`);
26
+ }
27
+ },
28
+ info: (message, context) => {
29
+ if (shouldLog("info")) {
30
+ console.info(`[${prefix}] ${message}${formatContext(context)}`);
31
+ }
32
+ },
33
+ warn: (message, context) => {
34
+ if (shouldLog("warn")) {
35
+ console.warn(`[${prefix}] ${message}${formatContext(context)}`);
36
+ }
37
+ },
38
+ error: (message, context, error) => {
39
+ if (shouldLog("error")) {
40
+ console.error(
41
+ `[${prefix}] ${message}${formatContext(context)}`,
42
+ error ? `
43
+ ${error.stack || error.message}` : ""
44
+ );
45
+ }
46
+ },
47
+ child: (additionalContext) => {
48
+ return createConsoleLoggerWithChild(prefix, minLevel, {
49
+ ...baseContext,
50
+ ...additionalContext
51
+ });
52
+ }
53
+ };
54
+ return logger;
55
+ }
56
+ function createPluginLogger(pluginName, minLevel = "debug") {
57
+ const coreFactory = getCoreLoggerFactory();
58
+ if (coreFactory) {
59
+ return coreFactory(pluginName);
60
+ }
61
+ return createConsoleLoggerWithChild(pluginName, minLevel);
62
+ }
63
+ function getLogLevelFromEnv() {
64
+ if (typeof process !== "undefined" && process.env) {
65
+ const envLevel = process.env.LOG_LEVEL || process.env.QUILTTAP_LOG_LEVEL;
66
+ if (envLevel && ["debug", "info", "warn", "error"].includes(envLevel)) {
67
+ return envLevel;
68
+ }
69
+ }
70
+ return "info";
71
+ }
72
+
73
+ // src/logging/index.ts
74
+ import { createConsoleLogger, createNoopLogger } from "@quilltap/plugin-types";
75
+ export {
76
+ __clearCoreLoggerFactory,
77
+ __injectCoreLoggerFactory,
78
+ createConsoleLogger,
79
+ createNoopLogger,
80
+ createPluginLogger,
81
+ getLogLevelFromEnv,
82
+ hasCoreLogger
83
+ };
84
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logging/plugin-logger.ts","../../src/logging/index.ts"],"sourcesContent":["/**\n * Plugin Logger Bridge\n *\n * Provides a logger factory for plugins that automatically bridges\n * to Quilltap's core logging when running inside the host application,\n * or falls back to console logging when running standalone.\n *\n * @module @quilltap/plugin-utils/logging/plugin-logger\n */\n\nimport type { PluginLogger, LogContext, LogLevel } from '@quilltap/plugin-types';\n\n/**\n * Extended logger interface with child logger support\n */\nexport interface PluginLoggerWithChild extends PluginLogger {\n /**\n * Create a child logger with additional context\n * @param additionalContext Context to merge with parent context\n * @returns A new logger with combined context\n */\n child(additionalContext: LogContext): PluginLoggerWithChild;\n}\n\n/**\n * Type for the global Quilltap logger bridge\n * Stored on globalThis to work across different npm package copies\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __quilltap_logger_factory:\n | ((pluginName: string) => PluginLoggerWithChild)\n | undefined;\n}\n\n/**\n * Get the core logger factory from global namespace\n *\n * @returns The injected factory or null if not in Quilltap environment\n */\nfunction getCoreLoggerFactory(): ((pluginName: string) => PluginLoggerWithChild) | null {\n return globalThis.__quilltap_logger_factory ?? null;\n}\n\n/**\n * Inject the core logger factory from Quilltap host\n *\n * This is called by Quilltap core when loading plugins to bridge\n * plugin logging into the host's logging system. Uses globalThis\n * to ensure it works even when plugins have their own copy of\n * plugin-utils in their node_modules.\n *\n * **Internal API - not for plugin use**\n *\n * @param factory A function that creates a child logger for a plugin\n */\nexport function __injectCoreLoggerFactory(\n factory: (pluginName: string) => PluginLoggerWithChild\n): void {\n globalThis.__quilltap_logger_factory = factory;\n}\n\n/**\n * Clear the injected core logger factory\n *\n * Useful for testing or when unloading the plugin system.\n *\n * **Internal API - not for plugin use**\n */\nexport function __clearCoreLoggerFactory(): void {\n globalThis.__quilltap_logger_factory = undefined;\n}\n\n/**\n * Check if a core logger has been injected\n *\n * @returns True if running inside Quilltap with core logging available\n */\nexport function hasCoreLogger(): boolean {\n return getCoreLoggerFactory() !== null;\n}\n\n/**\n * Create a console logger with child support\n *\n * @param prefix Logger prefix\n * @param minLevel Minimum log level\n * @param baseContext Base context to include in all logs\n */\nfunction createConsoleLoggerWithChild(\n prefix: string,\n minLevel: LogLevel = 'debug',\n baseContext: LogContext = {}\n): PluginLoggerWithChild {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const shouldLog = (level: LogLevel): boolean =>\n levels.indexOf(level) >= levels.indexOf(minLevel);\n\n const formatContext = (context?: LogContext): string => {\n const merged = { ...baseContext, ...context };\n const entries = Object.entries(merged)\n .filter(([key]) => key !== 'context')\n .map(([key, value]) => `${key}=${JSON.stringify(value)}`)\n .join(' ');\n return entries ? ` ${entries}` : '';\n };\n\n const logger: PluginLoggerWithChild = {\n debug: (message: string, context?: LogContext): void => {\n if (shouldLog('debug')) {\n console.debug(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n info: (message: string, context?: LogContext): void => {\n if (shouldLog('info')) {\n console.info(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n warn: (message: string, context?: LogContext): void => {\n if (shouldLog('warn')) {\n console.warn(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n error: (message: string, context?: LogContext, error?: Error): void => {\n if (shouldLog('error')) {\n console.error(\n `[${prefix}] ${message}${formatContext(context)}`,\n error ? `\\n${error.stack || error.message}` : ''\n );\n }\n },\n\n child: (additionalContext: LogContext): PluginLoggerWithChild => {\n return createConsoleLoggerWithChild(prefix, minLevel, {\n ...baseContext,\n ...additionalContext,\n });\n },\n };\n\n return logger;\n}\n\n/**\n * Create a plugin logger that bridges to Quilltap core logging\n *\n * When running inside Quilltap:\n * - Routes all logs to the core logger\n * - Tags logs with `{ plugin: pluginName, module: 'plugin' }`\n * - Logs appear in Quilltap's combined.log and console\n *\n * When running standalone:\n * - Falls back to console logging with `[pluginName]` prefix\n * - Respects the specified minimum log level\n *\n * @param pluginName - The plugin identifier (e.g., 'qtap-plugin-openai')\n * @param minLevel - Minimum log level when running standalone (default: 'debug')\n * @returns A logger instance\n *\n * @example\n * ```typescript\n * // In your plugin's provider.ts\n * import { createPluginLogger } from '@quilltap/plugin-utils';\n *\n * const logger = createPluginLogger('qtap-plugin-my-provider');\n *\n * export class MyProvider implements LLMProvider {\n * async sendMessage(params: LLMParams, apiKey: string): Promise<LLMResponse> {\n * logger.debug('Sending message', { model: params.model });\n *\n * try {\n * const response = await this.client.chat({...});\n * logger.info('Received response', { tokens: response.usage?.total_tokens });\n * return response;\n * } catch (error) {\n * logger.error('Failed to send message', { model: params.model }, error as Error);\n * throw error;\n * }\n * }\n * }\n * ```\n */\nexport function createPluginLogger(\n pluginName: string,\n minLevel: LogLevel = 'debug'\n): PluginLoggerWithChild {\n // Check for core logger factory from global namespace\n const coreFactory = getCoreLoggerFactory();\n if (coreFactory) {\n return coreFactory(pluginName);\n }\n\n // Standalone mode: use enhanced console logger\n return createConsoleLoggerWithChild(pluginName, minLevel);\n}\n\n/**\n * Get the minimum log level from environment\n *\n * Checks for LOG_LEVEL or QUILTTAP_LOG_LEVEL environment variables.\n * Useful for configuring standalone plugin logging.\n *\n * @returns The configured log level, or 'info' as default\n */\nexport function getLogLevelFromEnv(): LogLevel {\n if (typeof process !== 'undefined' && process.env) {\n const envLevel = process.env.LOG_LEVEL || process.env.QUILTTAP_LOG_LEVEL;\n if (envLevel && ['debug', 'info', 'warn', 'error'].includes(envLevel)) {\n return envLevel as LogLevel;\n }\n }\n return 'info';\n}\n","/**\n * Logging Utilities\n *\n * Exports the plugin logger bridge and related utilities.\n *\n * @module @quilltap/plugin-utils/logging\n */\n\nexport {\n createPluginLogger,\n hasCoreLogger,\n getLogLevelFromEnv,\n __injectCoreLoggerFactory,\n __clearCoreLoggerFactory,\n} from './plugin-logger';\n\nexport type { PluginLoggerWithChild } from './plugin-logger';\n\n// Re-export logger types from plugin-types\nexport type { PluginLogger, LogContext, LogLevel } from '@quilltap/plugin-types';\n\n// Re-export logger utilities from plugin-types for convenience\nexport { createConsoleLogger, createNoopLogger } from '@quilltap/plugin-types';\n"],"mappings":";AAwCA,SAAS,uBAA+E;AACtF,SAAO,WAAW,6BAA6B;AACjD;AAcO,SAAS,0BACd,SACM;AACN,aAAW,4BAA4B;AACzC;AASO,SAAS,2BAAiC;AAC/C,aAAW,4BAA4B;AACzC;AAOO,SAAS,gBAAyB;AACvC,SAAO,qBAAqB,MAAM;AACpC;AASA,SAAS,6BACP,QACA,WAAqB,SACrB,cAA0B,CAAC,GACJ;AACvB,QAAM,SAAqB,CAAC,SAAS,QAAQ,QAAQ,OAAO;AAC5D,QAAM,YAAY,CAAC,UACjB,OAAO,QAAQ,KAAK,KAAK,OAAO,QAAQ,QAAQ;AAElD,QAAM,gBAAgB,CAAC,YAAiC;AACtD,UAAM,SAAS,EAAE,GAAG,aAAa,GAAG,QAAQ;AAC5C,UAAM,UAAU,OAAO,QAAQ,MAAM,EAClC,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,SAAS,EACnC,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,IAAI,KAAK,UAAU,KAAK,CAAC,EAAE,EACvD,KAAK,GAAG;AACX,WAAO,UAAU,IAAI,OAAO,KAAK;AAAA,EACnC;AAEA,QAAM,SAAgC;AAAA,IACpC,OAAO,CAAC,SAAiB,YAA+B;AACtD,UAAI,UAAU,OAAO,GAAG;AACtB,gBAAQ,MAAM,IAAI,MAAM,KAAK,OAAO,GAAG,cAAc,OAAO,CAAC,EAAE;AAAA,MACjE;AAAA,IACF;AAAA,IAEA,MAAM,CAAC,SAAiB,YAA+B;AACrD,UAAI,UAAU,MAAM,GAAG;AACrB,gBAAQ,KAAK,IAAI,MAAM,KAAK,OAAO,GAAG,cAAc,OAAO,CAAC,EAAE;AAAA,MAChE;AAAA,IACF;AAAA,IAEA,MAAM,CAAC,SAAiB,YAA+B;AACrD,UAAI,UAAU,MAAM,GAAG;AACrB,gBAAQ,KAAK,IAAI,MAAM,KAAK,OAAO,GAAG,cAAc,OAAO,CAAC,EAAE;AAAA,MAChE;AAAA,IACF;AAAA,IAEA,OAAO,CAAC,SAAiB,SAAsB,UAAwB;AACrE,UAAI,UAAU,OAAO,GAAG;AACtB,gBAAQ;AAAA,UACN,IAAI,MAAM,KAAK,OAAO,GAAG,cAAc,OAAO,CAAC;AAAA,UAC/C,QAAQ;AAAA,EAAK,MAAM,SAAS,MAAM,OAAO,KAAK;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAAA,IAEA,OAAO,CAAC,sBAAyD;AAC/D,aAAO,6BAA6B,QAAQ,UAAU;AAAA,QACpD,GAAG;AAAA,QACH,GAAG;AAAA,MACL,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAyCO,SAAS,mBACd,YACA,WAAqB,SACE;AAEvB,QAAM,cAAc,qBAAqB;AACzC,MAAI,aAAa;AACf,WAAO,YAAY,UAAU;AAAA,EAC/B;AAGA,SAAO,6BAA6B,YAAY,QAAQ;AAC1D;AAUO,SAAS,qBAA+B;AAC7C,MAAI,OAAO,YAAY,eAAe,QAAQ,KAAK;AACjD,UAAM,WAAW,QAAQ,IAAI,aAAa,QAAQ,IAAI;AACtD,QAAI,YAAY,CAAC,SAAS,QAAQ,QAAQ,OAAO,EAAE,SAAS,QAAQ,GAAG;AACrE,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;ACjMA,SAAS,qBAAqB,wBAAwB;","names":[]}
@@ -0,0 +1,236 @@
1
+ import { ToolCallRequest, UniversalTool, AnthropicToolDefinition, GoogleToolDefinition } from '@quilltap/plugin-types';
2
+ export { AnthropicToolDefinition, GoogleToolDefinition, OpenAIToolDefinition, ToolCall, ToolCallRequest, ToolFormatOptions, ToolResult, UniversalTool } from '@quilltap/plugin-types';
3
+
4
+ /**
5
+ * Tool Call Parsers
6
+ *
7
+ * Provider-specific parsers for extracting tool calls from LLM responses.
8
+ * Each parser converts from a provider's native format to the standardized
9
+ * ToolCallRequest format.
10
+ *
11
+ * @module @quilltap/plugin-utils/tools/parsers
12
+ */
13
+
14
+ /**
15
+ * Supported tool call response formats
16
+ */
17
+ type ToolCallFormat = 'openai' | 'anthropic' | 'google' | 'auto';
18
+ /**
19
+ * Parse OpenAI format tool calls from LLM response
20
+ *
21
+ * Extracts tool calls from OpenAI/Grok API responses which return
22
+ * tool_calls in the message object.
23
+ *
24
+ * Expected response structures:
25
+ * - `response.tool_calls` (direct)
26
+ * - `response.choices[0].message.tool_calls` (nested)
27
+ *
28
+ * @param response - The raw response from provider API
29
+ * @returns Array of parsed tool call requests
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const response = await openai.chat.completions.create({...});
34
+ * const toolCalls = parseOpenAIToolCalls(response);
35
+ * // Returns: [{ name: 'search_web', arguments: { query: 'hello' } }]
36
+ * ```
37
+ */
38
+ declare function parseOpenAIToolCalls(response: unknown): ToolCallRequest[];
39
+ /**
40
+ * Parse Anthropic format tool calls from LLM response
41
+ *
42
+ * Extracts tool calls from Anthropic API responses which return
43
+ * tool_use blocks in the content array.
44
+ *
45
+ * Expected response structure:
46
+ * - `response.content` array with `{ type: 'tool_use', name, input }`
47
+ *
48
+ * @param response - The raw response from provider API
49
+ * @returns Array of parsed tool call requests
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const response = await anthropic.messages.create({...});
54
+ * const toolCalls = parseAnthropicToolCalls(response);
55
+ * // Returns: [{ name: 'search_web', arguments: { query: 'hello' } }]
56
+ * ```
57
+ */
58
+ declare function parseAnthropicToolCalls(response: unknown): ToolCallRequest[];
59
+ /**
60
+ * Parse Google Gemini format tool calls from LLM response
61
+ *
62
+ * Extracts tool calls from Google Gemini API responses which return
63
+ * functionCall objects in the parts array.
64
+ *
65
+ * Expected response structure:
66
+ * - `response.candidates[0].content.parts` array with `{ functionCall: { name, args } }`
67
+ *
68
+ * @param response - The raw response from provider API
69
+ * @returns Array of parsed tool call requests
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * const response = await gemini.generateContent({...});
74
+ * const toolCalls = parseGoogleToolCalls(response);
75
+ * // Returns: [{ name: 'search_web', arguments: { query: 'hello' } }]
76
+ * ```
77
+ */
78
+ declare function parseGoogleToolCalls(response: unknown): ToolCallRequest[];
79
+ /**
80
+ * Detect the format of a tool call response
81
+ *
82
+ * Analyzes the response structure to determine which provider format it uses.
83
+ *
84
+ * @param response - The raw response from a provider API
85
+ * @returns The detected format, or null if unrecognized
86
+ */
87
+ declare function detectToolCallFormat(response: unknown): ToolCallFormat | null;
88
+ /**
89
+ * Parse tool calls with auto-detection or explicit format
90
+ *
91
+ * A unified parser that can either auto-detect the response format
92
+ * or use a specified format. This is useful when you're not sure
93
+ * which provider's response you're handling.
94
+ *
95
+ * @param response - The raw response from a provider API
96
+ * @param format - The format to use: 'openai', 'anthropic', 'google', or 'auto'
97
+ * @returns Array of parsed tool call requests
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * // Auto-detect format
102
+ * const toolCalls = parseToolCalls(response, 'auto');
103
+ *
104
+ * // Or specify format explicitly
105
+ * const toolCalls = parseToolCalls(response, 'openai');
106
+ * ```
107
+ */
108
+ declare function parseToolCalls(response: unknown, format?: ToolCallFormat): ToolCallRequest[];
109
+ /**
110
+ * Check if a response contains tool calls
111
+ *
112
+ * Quick check to determine if a response has any tool calls
113
+ * without fully parsing them.
114
+ *
115
+ * @param response - The raw response from a provider API
116
+ * @returns True if the response contains tool calls
117
+ */
118
+ declare function hasToolCalls(response: unknown): boolean;
119
+
120
+ /**
121
+ * Tool Format Converters
122
+ *
123
+ * Utilities for converting between different provider tool formats.
124
+ * The universal format (OpenAI-style) serves as the baseline for all conversions.
125
+ *
126
+ * @module @quilltap/plugin-utils/tools/converters
127
+ */
128
+
129
+ /**
130
+ * Convert OpenAI/Universal format tool to Anthropic format
131
+ *
132
+ * Anthropic uses a tool_use format with:
133
+ * - name: string
134
+ * - description: string
135
+ * - input_schema: JSON schema object
136
+ *
137
+ * @param tool - Universal tool in OpenAI format
138
+ * @returns Tool formatted for Anthropic's tool_use
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * const anthropicTool = convertToAnthropicFormat(universalTool);
143
+ * // Returns: {
144
+ * // name: 'search_web',
145
+ * // description: 'Search the web',
146
+ * // input_schema: {
147
+ * // type: 'object',
148
+ * // properties: { query: { type: 'string' } },
149
+ * // required: ['query']
150
+ * // }
151
+ * // }
152
+ * ```
153
+ */
154
+ declare function convertToAnthropicFormat(tool: UniversalTool): AnthropicToolDefinition;
155
+ /**
156
+ * Convert OpenAI/Universal format tool to Google Gemini format
157
+ *
158
+ * Google uses a function calling format with:
159
+ * - name: string
160
+ * - description: string
161
+ * - parameters: JSON schema object
162
+ *
163
+ * @param tool - Universal tool in OpenAI format
164
+ * @returns Tool formatted for Google's functionCall
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * const googleTool = convertToGoogleFormat(universalTool);
169
+ * // Returns: {
170
+ * // name: 'search_web',
171
+ * // description: 'Search the web',
172
+ * // parameters: {
173
+ * // type: 'object',
174
+ * // properties: { query: { type: 'string' } },
175
+ * // required: ['query']
176
+ * // }
177
+ * // }
178
+ * ```
179
+ */
180
+ declare function convertToGoogleFormat(tool: UniversalTool): GoogleToolDefinition;
181
+ /**
182
+ * Convert Anthropic format tool to Universal/OpenAI format
183
+ *
184
+ * @param tool - Anthropic format tool
185
+ * @returns Tool in universal OpenAI format
186
+ */
187
+ declare function convertFromAnthropicFormat(tool: AnthropicToolDefinition): UniversalTool;
188
+ /**
189
+ * Convert Google format tool to Universal/OpenAI format
190
+ *
191
+ * @param tool - Google format tool
192
+ * @returns Tool in universal OpenAI format
193
+ */
194
+ declare function convertFromGoogleFormat(tool: GoogleToolDefinition): UniversalTool;
195
+ /**
196
+ * Apply prompt/description length limit to a tool
197
+ *
198
+ * Modifies a tool's description if it exceeds maxBytes, appending a warning
199
+ * that the description was truncated. This is useful for providers with
200
+ * strict token limits.
201
+ *
202
+ * @param tool - Tool object (any format) with a description property
203
+ * @param maxBytes - Maximum bytes allowed for description (including warning)
204
+ * @returns Modified tool with truncated description if needed
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * const limitedTool = applyDescriptionLimit(tool, 500);
209
+ * // If description > 500 bytes, truncates and adds warning
210
+ * ```
211
+ */
212
+ declare function applyDescriptionLimit<T extends {
213
+ description: string;
214
+ }>(tool: T, maxBytes: number): T;
215
+ /**
216
+ * Target format for tool conversion
217
+ */
218
+ type ToolConvertTarget = 'openai' | 'anthropic' | 'google';
219
+ /**
220
+ * Convert a universal tool to a specific provider format
221
+ *
222
+ * @param tool - Universal tool in OpenAI format
223
+ * @param target - Target provider format
224
+ * @returns Tool in the target format
225
+ */
226
+ declare function convertToolTo(tool: UniversalTool, target: ToolConvertTarget): UniversalTool | AnthropicToolDefinition | GoogleToolDefinition;
227
+ /**
228
+ * Convert multiple tools to a specific provider format
229
+ *
230
+ * @param tools - Array of universal tools
231
+ * @param target - Target provider format
232
+ * @returns Array of tools in the target format
233
+ */
234
+ declare function convertToolsTo(tools: UniversalTool[], target: ToolConvertTarget): Array<UniversalTool | AnthropicToolDefinition | GoogleToolDefinition>;
235
+
236
+ export { type ToolCallFormat, type ToolConvertTarget, applyDescriptionLimit, convertFromAnthropicFormat, convertFromGoogleFormat, convertToAnthropicFormat, convertToGoogleFormat, convertToolTo, convertToolsTo, detectToolCallFormat, hasToolCalls, parseAnthropicToolCalls, parseGoogleToolCalls, parseOpenAIToolCalls, parseToolCalls };