@emblemvault/hustle-react 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1187,6 +1187,286 @@ var screenshotPlugin = {
1187
1187
  }
1188
1188
  };
1189
1189
 
1190
+ // src/plugins/pluginBuilder.ts
1191
+ var buildPluginTool = {
1192
+ name: "build_plugin",
1193
+ description: `Build a Hustle plugin definition. Use this tool to construct a plugin based on user requirements.
1194
+
1195
+ ## Plugin Structure
1196
+
1197
+ A plugin consists of:
1198
+ - **name**: Unique identifier (lowercase, no spaces, e.g., "my-plugin")
1199
+ - **version**: Semantic version (e.g., "1.0.0")
1200
+ - **description**: What the plugin does
1201
+ - **tools**: Array of tool definitions the AI can call
1202
+ - **executorCode**: Object mapping tool names to JavaScript function code strings
1203
+
1204
+ ## Tool Definition Format
1205
+
1206
+ Each tool needs:
1207
+ - **name**: Unique tool name (alphanumeric + underscore, e.g., "get_weather")
1208
+ - **description**: Clear description for the AI to understand when to use it
1209
+ - **parameters**: JSON Schema object defining the arguments
1210
+
1211
+ Example tool:
1212
+ {
1213
+ "name": "get_weather",
1214
+ "description": "Get current weather for a city",
1215
+ "parameters": {
1216
+ "type": "object",
1217
+ "properties": {
1218
+ "city": { "type": "string", "description": "City name" },
1219
+ "units": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "Temperature units" }
1220
+ },
1221
+ "required": ["city"]
1222
+ }
1223
+ }
1224
+
1225
+ ## Executor Code Format
1226
+
1227
+ Executors are async JavaScript functions that receive args and return a result.
1228
+ Write them as arrow function strings that will be eval'd:
1229
+
1230
+ "async (args) => { const { city } = args; return { weather: 'sunny', city }; }"
1231
+
1232
+ The function receives args as Record<string, unknown> and should return the result.
1233
+ You can use fetch(), standard browser APIs, and async/await.
1234
+
1235
+ ## Lifecycle Hooks (Optional)
1236
+
1237
+ - **beforeRequestCode**: Modify messages before sending. Receives request object, must return modified request.
1238
+ Example: "async (req) => { req.messages = req.messages.map(m => ({...m, content: m.content.toUpperCase()})); return req; }"
1239
+
1240
+ - **afterResponseCode**: Process response after receiving. Receives response object.
1241
+ Example: "async (res) => { console.log('Response:', res.content); }"
1242
+
1243
+ - **onRegisterCode**: Called when plugin is registered.
1244
+ Example: "async () => { console.log('Plugin loaded!'); }"
1245
+
1246
+ - **onErrorCode**: Called on errors. Receives (error, context).
1247
+ Example: "async (error, ctx) => { console.error('Error in', ctx.phase, error); }"`,
1248
+ parameters: {
1249
+ type: "object",
1250
+ properties: {
1251
+ name: {
1252
+ type: "string",
1253
+ description: "Unique plugin identifier (lowercase, no spaces)"
1254
+ },
1255
+ version: {
1256
+ type: "string",
1257
+ description: 'Semantic version (e.g., "1.0.0")'
1258
+ },
1259
+ description: {
1260
+ type: "string",
1261
+ description: "What the plugin does"
1262
+ },
1263
+ tools: {
1264
+ type: "array",
1265
+ description: "Array of tool definitions",
1266
+ items: {
1267
+ type: "object",
1268
+ properties: {
1269
+ name: { type: "string", description: "Tool name" },
1270
+ description: { type: "string", description: "Tool description for AI" },
1271
+ parameters: { type: "object", description: "JSON Schema for arguments" }
1272
+ },
1273
+ required: ["name", "description", "parameters"]
1274
+ }
1275
+ },
1276
+ executorCode: {
1277
+ type: "object",
1278
+ description: "Object mapping tool names to executor function code strings"
1279
+ },
1280
+ beforeRequestCode: {
1281
+ type: "string",
1282
+ description: "Optional: Code for beforeRequest hook"
1283
+ },
1284
+ afterResponseCode: {
1285
+ type: "string",
1286
+ description: "Optional: Code for afterResponse hook"
1287
+ },
1288
+ onRegisterCode: {
1289
+ type: "string",
1290
+ description: "Optional: Code for onRegister hook"
1291
+ },
1292
+ onErrorCode: {
1293
+ type: "string",
1294
+ description: "Optional: Code for onError hook"
1295
+ }
1296
+ },
1297
+ required: ["name", "version", "description", "tools", "executorCode"]
1298
+ }
1299
+ };
1300
+ var savePluginTool = {
1301
+ name: "save_plugin",
1302
+ description: "Save a built plugin as a JSON file. Opens a download dialog for the user.",
1303
+ parameters: {
1304
+ type: "object",
1305
+ properties: {
1306
+ plugin: {
1307
+ type: "object",
1308
+ description: "The plugin object to save (from build_plugin result)"
1309
+ },
1310
+ filename: {
1311
+ type: "string",
1312
+ description: "Filename without extension (defaults to plugin name)"
1313
+ }
1314
+ },
1315
+ required: ["plugin"]
1316
+ }
1317
+ };
1318
+ var installPluginTool = {
1319
+ name: "install_plugin",
1320
+ description: "Install a built plugin to browser storage so it persists and can be used.",
1321
+ parameters: {
1322
+ type: "object",
1323
+ properties: {
1324
+ plugin: {
1325
+ type: "object",
1326
+ description: "The plugin object to install (from build_plugin result)"
1327
+ },
1328
+ enabled: {
1329
+ type: "boolean",
1330
+ description: "Whether to enable the plugin immediately (default: true)"
1331
+ }
1332
+ },
1333
+ required: ["plugin"]
1334
+ }
1335
+ };
1336
+ var buildPluginExecutor = async (args2) => {
1337
+ const {
1338
+ name,
1339
+ version,
1340
+ description,
1341
+ tools,
1342
+ executorCode,
1343
+ beforeRequestCode,
1344
+ afterResponseCode,
1345
+ onRegisterCode,
1346
+ onErrorCode
1347
+ } = args2;
1348
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) {
1349
+ return {
1350
+ success: false,
1351
+ error: "Plugin name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens"
1352
+ };
1353
+ }
1354
+ if (!/^\d+\.\d+\.\d+/.test(version)) {
1355
+ return {
1356
+ success: false,
1357
+ error: 'Version must be in semver format (e.g., "1.0.0")'
1358
+ };
1359
+ }
1360
+ for (const tool of tools) {
1361
+ if (!executorCode[tool.name]) {
1362
+ return {
1363
+ success: false,
1364
+ error: `Missing executor code for tool: ${tool.name}`
1365
+ };
1366
+ }
1367
+ }
1368
+ const storedPlugin = {
1369
+ name,
1370
+ version,
1371
+ description,
1372
+ tools: tools.map((tool) => ({
1373
+ ...tool,
1374
+ executorCode: executorCode[tool.name]
1375
+ })),
1376
+ enabled: true,
1377
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
1378
+ };
1379
+ if (beforeRequestCode || afterResponseCode || onRegisterCode || onErrorCode) {
1380
+ storedPlugin.hooksCode = {};
1381
+ if (beforeRequestCode) storedPlugin.hooksCode.beforeRequestCode = beforeRequestCode;
1382
+ if (afterResponseCode) storedPlugin.hooksCode.afterResponseCode = afterResponseCode;
1383
+ if (onRegisterCode) storedPlugin.hooksCode.onRegisterCode = onRegisterCode;
1384
+ if (onErrorCode) storedPlugin.hooksCode.onErrorCode = onErrorCode;
1385
+ }
1386
+ return {
1387
+ success: true,
1388
+ plugin: storedPlugin,
1389
+ message: `Plugin "${name}" v${version} built successfully with ${tools.length} tool(s). Use save_plugin to download or install_plugin to add to browser storage.`
1390
+ };
1391
+ };
1392
+ var savePluginExecutor = async (args2) => {
1393
+ const { plugin, filename } = args2;
1394
+ if (typeof window === "undefined") {
1395
+ return { success: false, error: "Cannot save files in server environment" };
1396
+ }
1397
+ try {
1398
+ const json = JSON.stringify(plugin, null, 2);
1399
+ const blob = new Blob([json], { type: "application/json" });
1400
+ const url = URL.createObjectURL(blob);
1401
+ const a = document.createElement("a");
1402
+ a.href = url;
1403
+ a.download = `${filename || plugin.name}.json`;
1404
+ document.body.appendChild(a);
1405
+ a.click();
1406
+ document.body.removeChild(a);
1407
+ URL.revokeObjectURL(url);
1408
+ return {
1409
+ success: true,
1410
+ message: `Plugin saved as ${filename || plugin.name}.json`
1411
+ };
1412
+ } catch (error2) {
1413
+ return {
1414
+ success: false,
1415
+ error: `Failed to save: ${error2 instanceof Error ? error2.message : "Unknown error"}`
1416
+ };
1417
+ }
1418
+ };
1419
+ var installPluginExecutor = async (args2) => {
1420
+ const { plugin, enabled = true } = args2;
1421
+ if (typeof window === "undefined") {
1422
+ return { success: false, error: "Cannot install plugins in server environment" };
1423
+ }
1424
+ try {
1425
+ const instanceId = window.__hustleInstanceId || "global-demo";
1426
+ const STORAGE_KEY = `hustle-plugins-${instanceId}`;
1427
+ const stored = localStorage.getItem(STORAGE_KEY);
1428
+ const plugins = stored ? JSON.parse(stored) : [];
1429
+ const existingIndex = plugins.findIndex((p) => p.name === plugin.name);
1430
+ const pluginToStore = {
1431
+ ...plugin,
1432
+ enabled,
1433
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
1434
+ };
1435
+ if (existingIndex >= 0) {
1436
+ plugins[existingIndex] = pluginToStore;
1437
+ } else {
1438
+ plugins.push(pluginToStore);
1439
+ }
1440
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(plugins));
1441
+ return {
1442
+ success: true,
1443
+ message: `Plugin "${plugin.name}" installed${enabled ? " and enabled" : ""}. Refresh the page or re-register plugins to activate.`,
1444
+ action: existingIndex >= 0 ? "updated" : "installed"
1445
+ };
1446
+ } catch (error2) {
1447
+ return {
1448
+ success: false,
1449
+ error: `Failed to install: ${error2 instanceof Error ? error2.message : "Unknown error"}`
1450
+ };
1451
+ }
1452
+ };
1453
+ var pluginBuilderPlugin = {
1454
+ name: "plugin-builder",
1455
+ version: "1.0.0",
1456
+ description: "Build custom plugins through conversation",
1457
+ tools: [buildPluginTool, savePluginTool, installPluginTool],
1458
+ executors: {
1459
+ build_plugin: buildPluginExecutor,
1460
+ save_plugin: savePluginExecutor,
1461
+ install_plugin: installPluginExecutor
1462
+ },
1463
+ hooks: {
1464
+ onRegister: () => {
1465
+ console.log("[Plugin Builder] Ready to help build custom plugins");
1466
+ }
1467
+ }
1468
+ };
1469
+
1190
1470
  // src/plugins/index.ts
1191
1471
  var availablePlugins = [
1192
1472
  {
@@ -1216,6 +1496,10 @@ var availablePlugins = [
1216
1496
  {
1217
1497
  ...screenshotPlugin,
1218
1498
  description: "Take screenshots of the current page"
1499
+ },
1500
+ {
1501
+ ...pluginBuilderPlugin,
1502
+ description: "Build custom plugins through conversation with AI"
1219
1503
  }
1220
1504
  ];
1221
1505
  function getAvailablePlugin(name) {
@@ -1228,6 +1512,7 @@ exports.getAvailablePlugin = getAvailablePlugin;
1228
1512
  exports.jsExecutorPlugin = jsExecutorPlugin;
1229
1513
  exports.migrateFunPlugin = migrateFunPlugin;
1230
1514
  exports.piiProtectionPlugin = piiProtectionPlugin;
1515
+ exports.pluginBuilderPlugin = pluginBuilderPlugin;
1231
1516
  exports.predictionMarketPlugin = predictionMarketPlugin;
1232
1517
  exports.screenshotPlugin = screenshotPlugin;
1233
1518
  exports.userQuestionPlugin = userQuestionPlugin;