@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.
package/dist/index.js CHANGED
@@ -1895,6 +1895,286 @@ var screenshotPlugin = {
1895
1895
  }
1896
1896
  };
1897
1897
 
1898
+ // src/plugins/pluginBuilder.ts
1899
+ var buildPluginTool = {
1900
+ name: "build_plugin",
1901
+ description: `Build a Hustle plugin definition. Use this tool to construct a plugin based on user requirements.
1902
+
1903
+ ## Plugin Structure
1904
+
1905
+ A plugin consists of:
1906
+ - **name**: Unique identifier (lowercase, no spaces, e.g., "my-plugin")
1907
+ - **version**: Semantic version (e.g., "1.0.0")
1908
+ - **description**: What the plugin does
1909
+ - **tools**: Array of tool definitions the AI can call
1910
+ - **executorCode**: Object mapping tool names to JavaScript function code strings
1911
+
1912
+ ## Tool Definition Format
1913
+
1914
+ Each tool needs:
1915
+ - **name**: Unique tool name (alphanumeric + underscore, e.g., "get_weather")
1916
+ - **description**: Clear description for the AI to understand when to use it
1917
+ - **parameters**: JSON Schema object defining the arguments
1918
+
1919
+ Example tool:
1920
+ {
1921
+ "name": "get_weather",
1922
+ "description": "Get current weather for a city",
1923
+ "parameters": {
1924
+ "type": "object",
1925
+ "properties": {
1926
+ "city": { "type": "string", "description": "City name" },
1927
+ "units": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "Temperature units" }
1928
+ },
1929
+ "required": ["city"]
1930
+ }
1931
+ }
1932
+
1933
+ ## Executor Code Format
1934
+
1935
+ Executors are async JavaScript functions that receive args and return a result.
1936
+ Write them as arrow function strings that will be eval'd:
1937
+
1938
+ "async (args) => { const { city } = args; return { weather: 'sunny', city }; }"
1939
+
1940
+ The function receives args as Record<string, unknown> and should return the result.
1941
+ You can use fetch(), standard browser APIs, and async/await.
1942
+
1943
+ ## Lifecycle Hooks (Optional)
1944
+
1945
+ - **beforeRequestCode**: Modify messages before sending. Receives request object, must return modified request.
1946
+ Example: "async (req) => { req.messages = req.messages.map(m => ({...m, content: m.content.toUpperCase()})); return req; }"
1947
+
1948
+ - **afterResponseCode**: Process response after receiving. Receives response object.
1949
+ Example: "async (res) => { console.log('Response:', res.content); }"
1950
+
1951
+ - **onRegisterCode**: Called when plugin is registered.
1952
+ Example: "async () => { console.log('Plugin loaded!'); }"
1953
+
1954
+ - **onErrorCode**: Called on errors. Receives (error, context).
1955
+ Example: "async (error, ctx) => { console.error('Error in', ctx.phase, error); }"`,
1956
+ parameters: {
1957
+ type: "object",
1958
+ properties: {
1959
+ name: {
1960
+ type: "string",
1961
+ description: "Unique plugin identifier (lowercase, no spaces)"
1962
+ },
1963
+ version: {
1964
+ type: "string",
1965
+ description: 'Semantic version (e.g., "1.0.0")'
1966
+ },
1967
+ description: {
1968
+ type: "string",
1969
+ description: "What the plugin does"
1970
+ },
1971
+ tools: {
1972
+ type: "array",
1973
+ description: "Array of tool definitions",
1974
+ items: {
1975
+ type: "object",
1976
+ properties: {
1977
+ name: { type: "string", description: "Tool name" },
1978
+ description: { type: "string", description: "Tool description for AI" },
1979
+ parameters: { type: "object", description: "JSON Schema for arguments" }
1980
+ },
1981
+ required: ["name", "description", "parameters"]
1982
+ }
1983
+ },
1984
+ executorCode: {
1985
+ type: "object",
1986
+ description: "Object mapping tool names to executor function code strings"
1987
+ },
1988
+ beforeRequestCode: {
1989
+ type: "string",
1990
+ description: "Optional: Code for beforeRequest hook"
1991
+ },
1992
+ afterResponseCode: {
1993
+ type: "string",
1994
+ description: "Optional: Code for afterResponse hook"
1995
+ },
1996
+ onRegisterCode: {
1997
+ type: "string",
1998
+ description: "Optional: Code for onRegister hook"
1999
+ },
2000
+ onErrorCode: {
2001
+ type: "string",
2002
+ description: "Optional: Code for onError hook"
2003
+ }
2004
+ },
2005
+ required: ["name", "version", "description", "tools", "executorCode"]
2006
+ }
2007
+ };
2008
+ var savePluginTool = {
2009
+ name: "save_plugin",
2010
+ description: "Save a built plugin as a JSON file. Opens a download dialog for the user.",
2011
+ parameters: {
2012
+ type: "object",
2013
+ properties: {
2014
+ plugin: {
2015
+ type: "object",
2016
+ description: "The plugin object to save (from build_plugin result)"
2017
+ },
2018
+ filename: {
2019
+ type: "string",
2020
+ description: "Filename without extension (defaults to plugin name)"
2021
+ }
2022
+ },
2023
+ required: ["plugin"]
2024
+ }
2025
+ };
2026
+ var installPluginTool = {
2027
+ name: "install_plugin",
2028
+ description: "Install a built plugin to browser storage so it persists and can be used.",
2029
+ parameters: {
2030
+ type: "object",
2031
+ properties: {
2032
+ plugin: {
2033
+ type: "object",
2034
+ description: "The plugin object to install (from build_plugin result)"
2035
+ },
2036
+ enabled: {
2037
+ type: "boolean",
2038
+ description: "Whether to enable the plugin immediately (default: true)"
2039
+ }
2040
+ },
2041
+ required: ["plugin"]
2042
+ }
2043
+ };
2044
+ var buildPluginExecutor = async (args2) => {
2045
+ const {
2046
+ name,
2047
+ version,
2048
+ description,
2049
+ tools,
2050
+ executorCode,
2051
+ beforeRequestCode,
2052
+ afterResponseCode,
2053
+ onRegisterCode,
2054
+ onErrorCode
2055
+ } = args2;
2056
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) {
2057
+ return {
2058
+ success: false,
2059
+ error: "Plugin name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens"
2060
+ };
2061
+ }
2062
+ if (!/^\d+\.\d+\.\d+/.test(version)) {
2063
+ return {
2064
+ success: false,
2065
+ error: 'Version must be in semver format (e.g., "1.0.0")'
2066
+ };
2067
+ }
2068
+ for (const tool of tools) {
2069
+ if (!executorCode[tool.name]) {
2070
+ return {
2071
+ success: false,
2072
+ error: `Missing executor code for tool: ${tool.name}`
2073
+ };
2074
+ }
2075
+ }
2076
+ const storedPlugin = {
2077
+ name,
2078
+ version,
2079
+ description,
2080
+ tools: tools.map((tool) => ({
2081
+ ...tool,
2082
+ executorCode: executorCode[tool.name]
2083
+ })),
2084
+ enabled: true,
2085
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
2086
+ };
2087
+ if (beforeRequestCode || afterResponseCode || onRegisterCode || onErrorCode) {
2088
+ storedPlugin.hooksCode = {};
2089
+ if (beforeRequestCode) storedPlugin.hooksCode.beforeRequestCode = beforeRequestCode;
2090
+ if (afterResponseCode) storedPlugin.hooksCode.afterResponseCode = afterResponseCode;
2091
+ if (onRegisterCode) storedPlugin.hooksCode.onRegisterCode = onRegisterCode;
2092
+ if (onErrorCode) storedPlugin.hooksCode.onErrorCode = onErrorCode;
2093
+ }
2094
+ return {
2095
+ success: true,
2096
+ plugin: storedPlugin,
2097
+ 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.`
2098
+ };
2099
+ };
2100
+ var savePluginExecutor = async (args2) => {
2101
+ const { plugin, filename } = args2;
2102
+ if (typeof window === "undefined") {
2103
+ return { success: false, error: "Cannot save files in server environment" };
2104
+ }
2105
+ try {
2106
+ const json2 = JSON.stringify(plugin, null, 2);
2107
+ const blob = new Blob([json2], { type: "application/json" });
2108
+ const url = URL.createObjectURL(blob);
2109
+ const a = document.createElement("a");
2110
+ a.href = url;
2111
+ a.download = `${filename || plugin.name}.json`;
2112
+ document.body.appendChild(a);
2113
+ a.click();
2114
+ document.body.removeChild(a);
2115
+ URL.revokeObjectURL(url);
2116
+ return {
2117
+ success: true,
2118
+ message: `Plugin saved as ${filename || plugin.name}.json`
2119
+ };
2120
+ } catch (error2) {
2121
+ return {
2122
+ success: false,
2123
+ error: `Failed to save: ${error2 instanceof Error ? error2.message : "Unknown error"}`
2124
+ };
2125
+ }
2126
+ };
2127
+ var installPluginExecutor = async (args2) => {
2128
+ const { plugin, enabled = true } = args2;
2129
+ if (typeof window === "undefined") {
2130
+ return { success: false, error: "Cannot install plugins in server environment" };
2131
+ }
2132
+ try {
2133
+ const instanceId = window.__hustleInstanceId || "global-demo";
2134
+ const STORAGE_KEY = `hustle-plugins-${instanceId}`;
2135
+ const stored = localStorage.getItem(STORAGE_KEY);
2136
+ const plugins = stored ? JSON.parse(stored) : [];
2137
+ const existingIndex = plugins.findIndex((p) => p.name === plugin.name);
2138
+ const pluginToStore = {
2139
+ ...plugin,
2140
+ enabled,
2141
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
2142
+ };
2143
+ if (existingIndex >= 0) {
2144
+ plugins[existingIndex] = pluginToStore;
2145
+ } else {
2146
+ plugins.push(pluginToStore);
2147
+ }
2148
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(plugins));
2149
+ return {
2150
+ success: true,
2151
+ message: `Plugin "${plugin.name}" installed${enabled ? " and enabled" : ""}. Refresh the page or re-register plugins to activate.`,
2152
+ action: existingIndex >= 0 ? "updated" : "installed"
2153
+ };
2154
+ } catch (error2) {
2155
+ return {
2156
+ success: false,
2157
+ error: `Failed to install: ${error2 instanceof Error ? error2.message : "Unknown error"}`
2158
+ };
2159
+ }
2160
+ };
2161
+ var pluginBuilderPlugin = {
2162
+ name: "plugin-builder",
2163
+ version: "1.0.0",
2164
+ description: "Build custom plugins through conversation",
2165
+ tools: [buildPluginTool, savePluginTool, installPluginTool],
2166
+ executors: {
2167
+ build_plugin: buildPluginExecutor,
2168
+ save_plugin: savePluginExecutor,
2169
+ install_plugin: installPluginExecutor
2170
+ },
2171
+ hooks: {
2172
+ onRegister: () => {
2173
+ console.log("[Plugin Builder] Ready to help build custom plugins");
2174
+ }
2175
+ }
2176
+ };
2177
+
1898
2178
  // src/plugins/index.ts
1899
2179
  var availablePlugins = [
1900
2180
  {
@@ -1924,6 +2204,10 @@ var availablePlugins = [
1924
2204
  {
1925
2205
  ...screenshotPlugin,
1926
2206
  description: "Take screenshots of the current page"
2207
+ },
2208
+ {
2209
+ ...pluginBuilderPlugin,
2210
+ description: "Build custom plugins through conversation with AI"
1927
2211
  }
1928
2212
  ];
1929
2213
  function getAvailablePlugin(name) {