dist / toolsProvider.js
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.notificationConfigSchematics = void 0;
exports.toolsProvider = toolsProvider;
const sdk_1 = require("@lmstudio/sdk");
const zod_1 = require("zod");
const path = __importStar(require("path"));
/**
* Lazily load node-notifier. It is a CommonJS module; under the LM Studio
* runtime the resolved namespace exposes the notifier on `.default`, so we
* always reach `notify` via that path (see regression test #26 in the tools
* repo). The result is cached across tool calls.
*/
let cachedNotifier;
async function getNotifier() {
if (cachedNotifier) {
return cachedNotifier;
}
const mod = await Promise.resolve().then(() => __importStar(require("node-notifier")));
cachedNotifier = (mod && mod.default) || mod;
return cachedNotifier;
}
/**
* Parameters for `send_notification`, expressed as a Zod raw shape.
*
* `tool()` wraps this object in `z.object(shape)` internally, so it must be an
* object of zod schema instances (a ZodRawShape) — never a pre-built
* `z.object(...)` and never a raw JSON Schema. Only the simplest forms are used
* here (`z.string()` / `z.boolean().optional()` / `z.array(z.string())`);
* refinements like `.min()` / `.describe()` / `.int()` were avoided because they
* crashed the SDK's zod -> json-schema extraction in earlier builds.
*/
const sendNotificationParameters = {
title: zod_1.z.string(),
message: zod_1.z.string(),
subtitle: zod_1.z.string().optional(),
sound: zod_1.z.boolean().optional(),
icon: zod_1.z.array(zod_1.z.string()).optional(),
wait: zod_1.z.boolean().optional(),
timeout: zod_1.z.number().optional(),
actions: zod_1.z.string().optional(),
};
/**
* User-configurable global settings exposed in the LM Studio plugin UI.
* Registered via `context.withGlobalConfigSchematics()` in index.ts and read
* inside the tool implementation through `ctl.getGlobalPluginConfig()`. These
* provide sensible defaults that the model can still override per call.
*/
exports.notificationConfigSchematics = (0, sdk_1.createConfigSchematics)()
.field("sound", "boolean", { displayName: "Play a system sound" }, false)
.field("timeout", "numeric", { displayName: "Default wait timeout (seconds)", hint: "Applied when the model requests 'wait'.", min: 1, max: 300, step: 1, int: true }, 10)
.field("defaultIcon", "string", { displayName: "Default icon path", hint: "Optional absolute image path shown on notifications." }, "")
.build();
/**
* Tools provider that exposes the send_notification tool to LM Studio models.
*/
async function toolsProvider(ctl) {
// Read the user-configurable global defaults registered in index.ts.
const config = ctl.getGlobalPluginConfig(exports.notificationConfigSchematics);
const defaultSound = config.get("sound");
const defaultTimeout = config.get("timeout");
const defaultIcon = config.get("defaultIcon") || "";
return [
(0, sdk_1.tool)({
name: "send_notification",
description: "Send a native macOS system notification. The LLM should generate a short summary as 'title', " +
"a descriptive message body based on the conversation context as 'message', and set optional " +
"parameters such as sound, icon, wait, timeout, and actions. Leave subtitle empty unless it adds " +
"useful context.",
parameters: sendNotificationParameters,
implementation: async (args) => {
// Apply sensible defaults for optional fields not provided by the model.
const title = args.title ?? "";
const message = args.message ?? "";
const subtitle = args.subtitle ?? "";
const sound = args.sound === true || (args.sound === undefined && defaultSound);
const iconRaw = Array.isArray(args.icon) ? args.icon : [];
const wait = args.wait === true;
const timeout = (args.timeout != null && Number.isFinite(args.timeout) && args.timeout >= 1)
? args.timeout
: defaultTimeout;
const actions = args.actions ?? "";
// Resolve the first valid icon path, falling back to the configured default.
let notifierIcon;
const iconCandidates = iconRaw.length > 0 ? iconRaw : defaultIcon ? [defaultIcon] : [];
for (const candidate of iconCandidates) {
if (!candidate)
continue;
const abs = path.isAbsolute(candidate)
? candidate
: path.resolve(process.cwd(), candidate);
notifierIcon = abs;
break;
}
// Build the notify options object.
const notifyOptions = {
title,
message,
subtitle,
};
if (sound === true) {
notifyOptions.sound = true;
}
if (notifierIcon) {
notifyOptions.icon = notifierIcon;
}
// wait / timeout — timeout takes precedence per node-notifier docs.
if (wait === true) {
notifyOptions.wait = true;
if (timeout > 0) {
notifyOptions.timeout = timeout;
}
}
else if (timeout > 0) {
notifyOptions.timeout = timeout;
}
if (actions && actions.trim().length > 0) {
notifyOptions.actions = [actions];
}
try {
const notifier = await getNotifier();
return new Promise((resolve) => {
notifier.notify(notifyOptions, (err, response, metadata) => {
if (err) {
resolve({ success: false, error: err?.message ?? String(err) });
return;
}
resolve({
success: true,
response,
metadata,
title,
message,
});
});
});
}
catch (e) {
return {
success: false,
error: e instanceof Error ? e.message : String(e),
};
}
},
}),
];
}
//# sourceMappingURL=toolsProvider.js.mapdist / toolsProvider.js
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.notificationConfigSchematics = void 0;
exports.toolsProvider = toolsProvider;
const sdk_1 = require("@lmstudio/sdk");
const zod_1 = require("zod");
const path = __importStar(require("path"));
/**
* Lazily load node-notifier. It is a CommonJS module; under the LM Studio
* runtime the resolved namespace exposes the notifier on `.default`, so we
* always reach `notify` via that path (see regression test #26 in the tools
* repo). The result is cached across tool calls.
*/
let cachedNotifier;
async function getNotifier() {
if (cachedNotifier) {
return cachedNotifier;
}
const mod = await Promise.resolve().then(() => __importStar(require("node-notifier")));
cachedNotifier = (mod && mod.default) || mod;
return cachedNotifier;
}
/**
* Parameters for `send_notification`, expressed as a Zod raw shape.
*
* `tool()` wraps this object in `z.object(shape)` internally, so it must be an
* object of zod schema instances (a ZodRawShape) — never a pre-built
* `z.object(...)` and never a raw JSON Schema. Only the simplest forms are used
* here (`z.string()` / `z.boolean().optional()` / `z.array(z.string())`);
* refinements like `.min()` / `.describe()` / `.int()` were avoided because they
* crashed the SDK's zod -> json-schema extraction in earlier builds.
*/
const sendNotificationParameters = {
title: zod_1.z.string(),
message: zod_1.z.string(),
subtitle: zod_1.z.string().optional(),
sound: zod_1.z.boolean().optional(),
icon: zod_1.z.array(zod_1.z.string()).optional(),
wait: zod_1.z.boolean().optional(),
timeout: zod_1.z.number().optional(),
actions: zod_1.z.string().optional(),
};
/**
* User-configurable global settings exposed in the LM Studio plugin UI.
* Registered via `context.withGlobalConfigSchematics()` in index.ts and read
* inside the tool implementation through `ctl.getGlobalPluginConfig()`. These
* provide sensible defaults that the model can still override per call.
*/
exports.notificationConfigSchematics = (0, sdk_1.createConfigSchematics)()
.field("sound", "boolean", { displayName: "Play a system sound" }, false)
.field("timeout", "numeric", { displayName: "Default wait timeout (seconds)", hint: "Applied when the model requests 'wait'.", min: 1, max: 300, step: 1, int: true }, 10)
.field("defaultIcon", "string", { displayName: "Default icon path", hint: "Optional absolute image path shown on notifications." }, "")
.build();
/**
* Tools provider that exposes the send_notification tool to LM Studio models.
*/
async function toolsProvider(ctl) {
// Read the user-configurable global defaults registered in index.ts.
const config = ctl.getGlobalPluginConfig(exports.notificationConfigSchematics);
const defaultSound = config.get("sound");
const defaultTimeout = config.get("timeout");
const defaultIcon = config.get("defaultIcon") || "";
return [
(0, sdk_1.tool)({
name: "send_notification",
description: "Send a native macOS system notification. The LLM should generate a short summary as 'title', " +
"a descriptive message body based on the conversation context as 'message', and set optional " +
"parameters such as sound, icon, wait, timeout, and actions. Leave subtitle empty unless it adds " +
"useful context.",
parameters: sendNotificationParameters,
implementation: async (args) => {
// Apply sensible defaults for optional fields not provided by the model.
const title = args.title ?? "";
const message = args.message ?? "";
const subtitle = args.subtitle ?? "";
const sound = args.sound === true || (args.sound === undefined && defaultSound);
const iconRaw = Array.isArray(args.icon) ? args.icon : [];
const wait = args.wait === true;
const timeout = (args.timeout != null && Number.isFinite(args.timeout) && args.timeout >= 1)
? args.timeout
: defaultTimeout;
const actions = args.actions ?? "";
// Resolve the first valid icon path, falling back to the configured default.
let notifierIcon;
const iconCandidates = iconRaw.length > 0 ? iconRaw : defaultIcon ? [defaultIcon] : [];
for (const candidate of iconCandidates) {
if (!candidate)
continue;
const abs = path.isAbsolute(candidate)
? candidate
: path.resolve(process.cwd(), candidate);
notifierIcon = abs;
break;
}
// Build the notify options object.
const notifyOptions = {
title,
message,
subtitle,
};
if (sound === true) {
notifyOptions.sound = true;
}
if (notifierIcon) {
notifyOptions.icon = notifierIcon;
}
// wait / timeout — timeout takes precedence per node-notifier docs.
if (wait === true) {
notifyOptions.wait = true;
if (timeout > 0) {
notifyOptions.timeout = timeout;
}
}
else if (timeout > 0) {
notifyOptions.timeout = timeout;
}
if (actions && actions.trim().length > 0) {
notifyOptions.actions = [actions];
}
try {
const notifier = await getNotifier();
return new Promise((resolve) => {
notifier.notify(notifyOptions, (err, response, metadata) => {
if (err) {
resolve({ success: false, error: err?.message ?? String(err) });
return;
}
resolve({
success: true,
response,
metadata,
title,
message,
});
});
});
}
catch (e) {
return {
success: false,
error: e instanceof Error ? e.message : String(e),
};
}
},
}),
];
}
//# sourceMappingURL=toolsProvider.js.map