@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.
@@ -3439,6 +3439,286 @@ var screenshotPlugin = {
3439
3439
  }
3440
3440
  };
3441
3441
 
3442
+ // src/plugins/pluginBuilder.ts
3443
+ var buildPluginTool = {
3444
+ name: "build_plugin",
3445
+ description: `Build a Hustle plugin definition. Use this tool to construct a plugin based on user requirements.
3446
+
3447
+ ## Plugin Structure
3448
+
3449
+ A plugin consists of:
3450
+ - **name**: Unique identifier (lowercase, no spaces, e.g., "my-plugin")
3451
+ - **version**: Semantic version (e.g., "1.0.0")
3452
+ - **description**: What the plugin does
3453
+ - **tools**: Array of tool definitions the AI can call
3454
+ - **executorCode**: Object mapping tool names to JavaScript function code strings
3455
+
3456
+ ## Tool Definition Format
3457
+
3458
+ Each tool needs:
3459
+ - **name**: Unique tool name (alphanumeric + underscore, e.g., "get_weather")
3460
+ - **description**: Clear description for the AI to understand when to use it
3461
+ - **parameters**: JSON Schema object defining the arguments
3462
+
3463
+ Example tool:
3464
+ {
3465
+ "name": "get_weather",
3466
+ "description": "Get current weather for a city",
3467
+ "parameters": {
3468
+ "type": "object",
3469
+ "properties": {
3470
+ "city": { "type": "string", "description": "City name" },
3471
+ "units": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "Temperature units" }
3472
+ },
3473
+ "required": ["city"]
3474
+ }
3475
+ }
3476
+
3477
+ ## Executor Code Format
3478
+
3479
+ Executors are async JavaScript functions that receive args and return a result.
3480
+ Write them as arrow function strings that will be eval'd:
3481
+
3482
+ "async (args) => { const { city } = args; return { weather: 'sunny', city }; }"
3483
+
3484
+ The function receives args as Record<string, unknown> and should return the result.
3485
+ You can use fetch(), standard browser APIs, and async/await.
3486
+
3487
+ ## Lifecycle Hooks (Optional)
3488
+
3489
+ - **beforeRequestCode**: Modify messages before sending. Receives request object, must return modified request.
3490
+ Example: "async (req) => { req.messages = req.messages.map(m => ({...m, content: m.content.toUpperCase()})); return req; }"
3491
+
3492
+ - **afterResponseCode**: Process response after receiving. Receives response object.
3493
+ Example: "async (res) => { console.log('Response:', res.content); }"
3494
+
3495
+ - **onRegisterCode**: Called when plugin is registered.
3496
+ Example: "async () => { console.log('Plugin loaded!'); }"
3497
+
3498
+ - **onErrorCode**: Called on errors. Receives (error, context).
3499
+ Example: "async (error, ctx) => { console.error('Error in', ctx.phase, error); }"`,
3500
+ parameters: {
3501
+ type: "object",
3502
+ properties: {
3503
+ name: {
3504
+ type: "string",
3505
+ description: "Unique plugin identifier (lowercase, no spaces)"
3506
+ },
3507
+ version: {
3508
+ type: "string",
3509
+ description: 'Semantic version (e.g., "1.0.0")'
3510
+ },
3511
+ description: {
3512
+ type: "string",
3513
+ description: "What the plugin does"
3514
+ },
3515
+ tools: {
3516
+ type: "array",
3517
+ description: "Array of tool definitions",
3518
+ items: {
3519
+ type: "object",
3520
+ properties: {
3521
+ name: { type: "string", description: "Tool name" },
3522
+ description: { type: "string", description: "Tool description for AI" },
3523
+ parameters: { type: "object", description: "JSON Schema for arguments" }
3524
+ },
3525
+ required: ["name", "description", "parameters"]
3526
+ }
3527
+ },
3528
+ executorCode: {
3529
+ type: "object",
3530
+ description: "Object mapping tool names to executor function code strings"
3531
+ },
3532
+ beforeRequestCode: {
3533
+ type: "string",
3534
+ description: "Optional: Code for beforeRequest hook"
3535
+ },
3536
+ afterResponseCode: {
3537
+ type: "string",
3538
+ description: "Optional: Code for afterResponse hook"
3539
+ },
3540
+ onRegisterCode: {
3541
+ type: "string",
3542
+ description: "Optional: Code for onRegister hook"
3543
+ },
3544
+ onErrorCode: {
3545
+ type: "string",
3546
+ description: "Optional: Code for onError hook"
3547
+ }
3548
+ },
3549
+ required: ["name", "version", "description", "tools", "executorCode"]
3550
+ }
3551
+ };
3552
+ var savePluginTool = {
3553
+ name: "save_plugin",
3554
+ description: "Save a built plugin as a JSON file. Opens a download dialog for the user.",
3555
+ parameters: {
3556
+ type: "object",
3557
+ properties: {
3558
+ plugin: {
3559
+ type: "object",
3560
+ description: "The plugin object to save (from build_plugin result)"
3561
+ },
3562
+ filename: {
3563
+ type: "string",
3564
+ description: "Filename without extension (defaults to plugin name)"
3565
+ }
3566
+ },
3567
+ required: ["plugin"]
3568
+ }
3569
+ };
3570
+ var installPluginTool = {
3571
+ name: "install_plugin",
3572
+ description: "Install a built plugin to browser storage so it persists and can be used.",
3573
+ parameters: {
3574
+ type: "object",
3575
+ properties: {
3576
+ plugin: {
3577
+ type: "object",
3578
+ description: "The plugin object to install (from build_plugin result)"
3579
+ },
3580
+ enabled: {
3581
+ type: "boolean",
3582
+ description: "Whether to enable the plugin immediately (default: true)"
3583
+ }
3584
+ },
3585
+ required: ["plugin"]
3586
+ }
3587
+ };
3588
+ var buildPluginExecutor = async (args2) => {
3589
+ const {
3590
+ name,
3591
+ version,
3592
+ description,
3593
+ tools,
3594
+ executorCode,
3595
+ beforeRequestCode,
3596
+ afterResponseCode,
3597
+ onRegisterCode,
3598
+ onErrorCode
3599
+ } = args2;
3600
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) {
3601
+ return {
3602
+ success: false,
3603
+ error: "Plugin name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens"
3604
+ };
3605
+ }
3606
+ if (!/^\d+\.\d+\.\d+/.test(version)) {
3607
+ return {
3608
+ success: false,
3609
+ error: 'Version must be in semver format (e.g., "1.0.0")'
3610
+ };
3611
+ }
3612
+ for (const tool of tools) {
3613
+ if (!executorCode[tool.name]) {
3614
+ return {
3615
+ success: false,
3616
+ error: `Missing executor code for tool: ${tool.name}`
3617
+ };
3618
+ }
3619
+ }
3620
+ const storedPlugin = {
3621
+ name,
3622
+ version,
3623
+ description,
3624
+ tools: tools.map((tool) => ({
3625
+ ...tool,
3626
+ executorCode: executorCode[tool.name]
3627
+ })),
3628
+ enabled: true,
3629
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
3630
+ };
3631
+ if (beforeRequestCode || afterResponseCode || onRegisterCode || onErrorCode) {
3632
+ storedPlugin.hooksCode = {};
3633
+ if (beforeRequestCode) storedPlugin.hooksCode.beforeRequestCode = beforeRequestCode;
3634
+ if (afterResponseCode) storedPlugin.hooksCode.afterResponseCode = afterResponseCode;
3635
+ if (onRegisterCode) storedPlugin.hooksCode.onRegisterCode = onRegisterCode;
3636
+ if (onErrorCode) storedPlugin.hooksCode.onErrorCode = onErrorCode;
3637
+ }
3638
+ return {
3639
+ success: true,
3640
+ plugin: storedPlugin,
3641
+ 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.`
3642
+ };
3643
+ };
3644
+ var savePluginExecutor = async (args2) => {
3645
+ const { plugin, filename } = args2;
3646
+ if (typeof window === "undefined") {
3647
+ return { success: false, error: "Cannot save files in server environment" };
3648
+ }
3649
+ try {
3650
+ const json2 = JSON.stringify(plugin, null, 2);
3651
+ const blob = new Blob([json2], { type: "application/json" });
3652
+ const url = URL.createObjectURL(blob);
3653
+ const a = document.createElement("a");
3654
+ a.href = url;
3655
+ a.download = `${filename || plugin.name}.json`;
3656
+ document.body.appendChild(a);
3657
+ a.click();
3658
+ document.body.removeChild(a);
3659
+ URL.revokeObjectURL(url);
3660
+ return {
3661
+ success: true,
3662
+ message: `Plugin saved as ${filename || plugin.name}.json`
3663
+ };
3664
+ } catch (error2) {
3665
+ return {
3666
+ success: false,
3667
+ error: `Failed to save: ${error2 instanceof Error ? error2.message : "Unknown error"}`
3668
+ };
3669
+ }
3670
+ };
3671
+ var installPluginExecutor = async (args2) => {
3672
+ const { plugin, enabled = true } = args2;
3673
+ if (typeof window === "undefined") {
3674
+ return { success: false, error: "Cannot install plugins in server environment" };
3675
+ }
3676
+ try {
3677
+ const instanceId = window.__hustleInstanceId || "global-demo";
3678
+ const STORAGE_KEY = `hustle-plugins-${instanceId}`;
3679
+ const stored = localStorage.getItem(STORAGE_KEY);
3680
+ const plugins = stored ? JSON.parse(stored) : [];
3681
+ const existingIndex = plugins.findIndex((p) => p.name === plugin.name);
3682
+ const pluginToStore = {
3683
+ ...plugin,
3684
+ enabled,
3685
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
3686
+ };
3687
+ if (existingIndex >= 0) {
3688
+ plugins[existingIndex] = pluginToStore;
3689
+ } else {
3690
+ plugins.push(pluginToStore);
3691
+ }
3692
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(plugins));
3693
+ return {
3694
+ success: true,
3695
+ message: `Plugin "${plugin.name}" installed${enabled ? " and enabled" : ""}. Refresh the page or re-register plugins to activate.`,
3696
+ action: existingIndex >= 0 ? "updated" : "installed"
3697
+ };
3698
+ } catch (error2) {
3699
+ return {
3700
+ success: false,
3701
+ error: `Failed to install: ${error2 instanceof Error ? error2.message : "Unknown error"}`
3702
+ };
3703
+ }
3704
+ };
3705
+ var pluginBuilderPlugin = {
3706
+ name: "plugin-builder",
3707
+ version: "1.0.0",
3708
+ description: "Build custom plugins through conversation",
3709
+ tools: [buildPluginTool, savePluginTool, installPluginTool],
3710
+ executors: {
3711
+ build_plugin: buildPluginExecutor,
3712
+ save_plugin: savePluginExecutor,
3713
+ install_plugin: installPluginExecutor
3714
+ },
3715
+ hooks: {
3716
+ onRegister: () => {
3717
+ console.log("[Plugin Builder] Ready to help build custom plugins");
3718
+ }
3719
+ }
3720
+ };
3721
+
3442
3722
  // src/plugins/index.ts
3443
3723
  var availablePlugins = [
3444
3724
  {
@@ -3468,6 +3748,10 @@ var availablePlugins = [
3468
3748
  {
3469
3749
  ...screenshotPlugin,
3470
3750
  description: "Take screenshots of the current page"
3751
+ },
3752
+ {
3753
+ ...pluginBuilderPlugin,
3754
+ description: "Build custom plugins through conversation with AI"
3471
3755
  }
3472
3756
  ];
3473
3757
  function getAvailablePlugin(name) {