runline 0.15.0 → 0.15.1

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.
@@ -37,5 +37,13 @@ export declare class ExecutionEngine {
37
37
  constructor(registry: PluginRegistry, config: RunlineConfig);
38
38
  execute(code: string, options?: EngineOptions): Promise<ExecuteResult>;
39
39
  private invokeAction;
40
+ /**
41
+ * A miss on "salesforce.status" is often a plugin that registered
42
+ * its action name with the plugin prefix ("salesforce.status" inside
43
+ * plugin "salesforce"), making the real path
44
+ * "salesforce.salesforce.status". Detect exactly that shape and
45
+ * teach the caller — agents read error messages and self-correct.
46
+ */
47
+ private selfPrefixHint;
40
48
  private resolveConnection;
41
49
  }
@@ -158,7 +158,7 @@ export class ExecutionEngine {
158
158
  async invokeAction(path, args) {
159
159
  const resolved = this.registry.resolveAction(path);
160
160
  if (!resolved) {
161
- throw new Error(`Unknown action: ${path}`);
161
+ throw new Error(`Unknown action: ${path}${this.selfPrefixHint(path)}`);
162
162
  }
163
163
  const { plugin, action } = resolved;
164
164
  const connection = this.resolveConnection(plugin);
@@ -184,6 +184,25 @@ export class ExecutionEngine {
184
184
  }
185
185
  return action.execute(args, ctx);
186
186
  }
187
+ /**
188
+ * A miss on "salesforce.status" is often a plugin that registered
189
+ * its action name with the plugin prefix ("salesforce.status" inside
190
+ * plugin "salesforce"), making the real path
191
+ * "salesforce.salesforce.status". Detect exactly that shape and
192
+ * teach the caller — agents read error messages and self-correct.
193
+ */
194
+ selfPrefixHint(path) {
195
+ // A dot-less miss can still be the exact-name case: plugin "ping"
196
+ // registering action "ping" makes the callable path "ping.ping".
197
+ const dot = path.indexOf(".");
198
+ const pluginName = dot < 0 ? path : path.slice(0, dot);
199
+ const doubled = `${pluginName}.${path}`;
200
+ if (!this.registry.resolveAction(doubled))
201
+ return "";
202
+ return (`. Did you mean "${doubled}"? Plugin "${pluginName}" registered its ` +
203
+ `action name with the plugin prefix ("${path}"), so the full callable ` +
204
+ `path is "${doubled}".`);
205
+ }
187
206
  resolveConnection(plugin) {
188
207
  const conn = this.config.connections.find((c) => c.plugin === plugin.name);
189
208
  const base = conn ?? {
@@ -59,14 +59,39 @@ export function createPluginAPI(pluginId) {
59
59
  export function isPluginFunction(val) {
60
60
  return typeof val === "function";
61
61
  }
62
+ /**
63
+ * Action names are plugin-relative: plugin "salesforce" registering
64
+ * "salesforce.status" is callable as "salesforce.salesforce.status",
65
+ * which is almost never what the author meant. Warn at resolve time —
66
+ * the earliest point where both the final plugin name and the full
67
+ * action list are known (setName may be called after registerAction,
68
+ * and object exports never go through createPluginAPI).
69
+ */
70
+ function warnSelfPrefixedActions(plugin) {
71
+ for (const action of plugin.actions) {
72
+ const prefixed = action.name.startsWith(`${plugin.name}.`);
73
+ if (!prefixed && action.name !== plugin.name)
74
+ continue;
75
+ const advice = prefixed
76
+ ? `Rename the action to "${action.name.slice(plugin.name.length + 1)}".`
77
+ : "Choose an action name that does not repeat the plugin name.";
78
+ console.warn(`[runline] Plugin "${plugin.name}" registered action "${action.name}", which repeats the plugin name. ` +
79
+ `Action names are plugin-relative, so this action is callable as "${plugin.name}.${action.name}" — ` +
80
+ `not "${action.name}". ${advice}`);
81
+ }
82
+ }
62
83
  export function resolvePluginExport(exported, pluginId) {
63
84
  if (isPluginFunction(exported)) {
64
85
  const { api, resolve } = createPluginAPI(pluginId);
65
86
  exported(api);
66
- return resolve();
87
+ const plugin = resolve();
88
+ warnSelfPrefixedActions(plugin);
89
+ return plugin;
67
90
  }
68
91
  if (exported && typeof exported === "object" && "actions" in exported) {
69
- return exported;
92
+ const plugin = exported;
93
+ warnSelfPrefixedActions(plugin);
94
+ return plugin;
70
95
  }
71
96
  throw new Error(`Invalid plugin export from "${pluginId}": expected a function or { name, actions } object`);
72
97
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runline",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "description": "Code mode for agents — turn any API or command into a callable action",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",