@wingman-ai/gateway 0.4.0 → 0.4.2

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.
Files changed (104) hide show
  1. package/README.md +29 -111
  2. package/dist/agent/config/agentConfig.cjs +2 -0
  3. package/dist/agent/config/agentConfig.d.ts +6 -0
  4. package/dist/agent/config/agentConfig.js +2 -0
  5. package/dist/agent/config/agentLoader.cjs +21 -18
  6. package/dist/agent/config/agentLoader.js +22 -19
  7. package/dist/agent/config/toolRegistry.cjs +19 -0
  8. package/dist/agent/config/toolRegistry.d.ts +4 -0
  9. package/dist/agent/config/toolRegistry.js +17 -1
  10. package/dist/agent/middleware/additional-messages.cjs +115 -11
  11. package/dist/agent/middleware/additional-messages.d.ts +9 -0
  12. package/dist/agent/middleware/additional-messages.js +115 -11
  13. package/dist/agent/tests/agentLoader.test.cjs +45 -0
  14. package/dist/agent/tests/agentLoader.test.js +45 -0
  15. package/dist/agent/tests/toolRegistry.test.cjs +2 -0
  16. package/dist/agent/tests/toolRegistry.test.js +2 -0
  17. package/dist/agent/tools/node_invoke.cjs +146 -0
  18. package/dist/agent/tools/node_invoke.d.ts +86 -0
  19. package/dist/agent/tools/node_invoke.js +109 -0
  20. package/dist/cli/commands/gateway.cjs +1 -1
  21. package/dist/cli/commands/gateway.js +1 -1
  22. package/dist/cli/commands/init.cjs +135 -1
  23. package/dist/cli/commands/init.js +136 -2
  24. package/dist/cli/commands/skill.cjs +7 -3
  25. package/dist/cli/commands/skill.js +7 -3
  26. package/dist/cli/config/loader.cjs +7 -3
  27. package/dist/cli/config/loader.js +7 -3
  28. package/dist/cli/config/schema.cjs +27 -9
  29. package/dist/cli/config/schema.d.ts +18 -4
  30. package/dist/cli/config/schema.js +23 -8
  31. package/dist/cli/core/agentInvoker.cjs +70 -14
  32. package/dist/cli/core/agentInvoker.d.ts +10 -0
  33. package/dist/cli/core/agentInvoker.js +70 -14
  34. package/dist/cli/services/skillRepository.cjs +155 -69
  35. package/dist/cli/services/skillRepository.d.ts +7 -2
  36. package/dist/cli/services/skillRepository.js +155 -69
  37. package/dist/cli/services/skillService.cjs +93 -26
  38. package/dist/cli/services/skillService.d.ts +7 -0
  39. package/dist/cli/services/skillService.js +96 -29
  40. package/dist/cli/types/skill.d.ts +8 -3
  41. package/dist/gateway/http/nodes.cjs +247 -0
  42. package/dist/gateway/http/nodes.d.ts +20 -0
  43. package/dist/gateway/http/nodes.js +210 -0
  44. package/dist/gateway/node.cjs +10 -1
  45. package/dist/gateway/node.d.ts +10 -1
  46. package/dist/gateway/node.js +10 -1
  47. package/dist/gateway/server.cjs +414 -27
  48. package/dist/gateway/server.d.ts +34 -0
  49. package/dist/gateway/server.js +408 -27
  50. package/dist/gateway/types.d.ts +6 -1
  51. package/dist/gateway/validation.cjs +2 -0
  52. package/dist/gateway/validation.d.ts +4 -0
  53. package/dist/gateway/validation.js +2 -0
  54. package/dist/skills/activation.cjs +92 -0
  55. package/dist/skills/activation.d.ts +12 -0
  56. package/dist/skills/activation.js +58 -0
  57. package/dist/skills/bin-requirements.cjs +63 -0
  58. package/dist/skills/bin-requirements.d.ts +3 -0
  59. package/dist/skills/bin-requirements.js +26 -0
  60. package/dist/skills/metadata.cjs +141 -0
  61. package/dist/skills/metadata.d.ts +29 -0
  62. package/dist/skills/metadata.js +104 -0
  63. package/dist/skills/overlay.cjs +75 -0
  64. package/dist/skills/overlay.d.ts +2 -0
  65. package/dist/skills/overlay.js +38 -0
  66. package/dist/tests/additionalMessageMiddleware.test.cjs +92 -0
  67. package/dist/tests/additionalMessageMiddleware.test.js +92 -0
  68. package/dist/tests/cli-config-loader.test.cjs +7 -3
  69. package/dist/tests/cli-config-loader.test.js +7 -3
  70. package/dist/tests/cli-init.test.cjs +54 -0
  71. package/dist/tests/cli-init.test.js +54 -0
  72. package/dist/tests/config-json-schema.test.cjs +12 -0
  73. package/dist/tests/config-json-schema.test.js +12 -0
  74. package/dist/tests/gateway-http-security.test.cjs +277 -0
  75. package/dist/tests/gateway-http-security.test.d.ts +1 -0
  76. package/dist/tests/gateway-http-security.test.js +271 -0
  77. package/dist/tests/gateway-node-mode.test.cjs +174 -0
  78. package/dist/tests/gateway-node-mode.test.d.ts +1 -0
  79. package/dist/tests/gateway-node-mode.test.js +168 -0
  80. package/dist/tests/gateway-origin-policy.test.cjs +60 -0
  81. package/dist/tests/gateway-origin-policy.test.d.ts +1 -0
  82. package/dist/tests/gateway-origin-policy.test.js +54 -0
  83. package/dist/tests/gateway.test.cjs +1 -0
  84. package/dist/tests/gateway.test.js +1 -0
  85. package/dist/tests/node-tools.test.cjs +77 -0
  86. package/dist/tests/node-tools.test.d.ts +1 -0
  87. package/dist/tests/node-tools.test.js +71 -0
  88. package/dist/tests/nodes-api.test.cjs +86 -0
  89. package/dist/tests/nodes-api.test.d.ts +1 -0
  90. package/dist/tests/nodes-api.test.js +80 -0
  91. package/dist/tests/skill-activation.test.cjs +86 -0
  92. package/dist/tests/skill-activation.test.d.ts +1 -0
  93. package/dist/tests/skill-activation.test.js +80 -0
  94. package/dist/tests/skill-metadata.test.cjs +119 -0
  95. package/dist/tests/skill-metadata.test.d.ts +1 -0
  96. package/dist/tests/skill-metadata.test.js +113 -0
  97. package/dist/tests/skill-repository.test.cjs +363 -0
  98. package/dist/tests/skill-repository.test.js +363 -0
  99. package/dist/webui/assets/{index-DHbfLOUR.js → index-BMekSELC.js} +106 -106
  100. package/dist/webui/index.html +1 -1
  101. package/package.json +4 -4
  102. package/skills/gog/SKILL.md +1 -1
  103. package/skills/weather/SKILL.md +1 -1
  104. package/skills/ui-registry/SKILL.md +0 -35
@@ -2,6 +2,7 @@ import { isAbsolute, relative } from "node:path";
2
2
  import { HumanMessage, MIDDLEWARE_BRAND } from "langchain";
3
3
  import { getConfidentialityNotice } from "../utils.js";
4
4
  import { loadUiRegistry, resolveUiRegistryPath, summarizeUiRegistry } from "../uiRegistry.js";
5
+ const INJECTION_SOURCE = "additional-message-middleware";
5
6
  const normalizeRelativePath = (value)=>value.replace(/\\/g, "/");
6
7
  const toSafeRelativePath = (workspaceRoot, targetPath)=>{
7
8
  if (!workspaceRoot) return null;
@@ -28,12 +29,108 @@ const buildWorkingDirectoryMessage = (context)=>{
28
29
  if (!context.workspaceRoot) return null;
29
30
  return "** Working Directory **\n- Treat `./` as the current working directory for file and tool operations in this session.\n- Use relative paths such as `./test.md`; do not prepend the working directory absolute path.";
30
31
  };
32
+ const resolveConnectedNodeIds = async (context)=>{
33
+ if (context.nodeConnectedTargetsProvider) try {
34
+ const rawTargets = await context.nodeConnectedTargetsProvider();
35
+ if (!Array.isArray(rawTargets)) return [];
36
+ const seen = new Set();
37
+ const normalized = [];
38
+ for (const target of rawTargets){
39
+ if (!target || "object" != typeof target) continue;
40
+ const nodeId = target.nodeId?.trim();
41
+ if (!(!nodeId || seen.has(nodeId))) {
42
+ seen.add(nodeId);
43
+ normalized.push(nodeId);
44
+ }
45
+ }
46
+ return normalized;
47
+ } catch {
48
+ return [];
49
+ }
50
+ if (!context.nodeConnectedIdsProvider) return [];
51
+ try {
52
+ const rawIds = await context.nodeConnectedIdsProvider();
53
+ if (!Array.isArray(rawIds)) return [];
54
+ const seen = new Set();
55
+ const normalized = [];
56
+ for (const value of rawIds){
57
+ if ("string" != typeof value) continue;
58
+ const trimmed = value.trim();
59
+ if (!(!trimmed || seen.has(trimmed))) {
60
+ seen.add(trimmed);
61
+ normalized.push(trimmed);
62
+ }
63
+ }
64
+ return normalized;
65
+ } catch {
66
+ return [];
67
+ }
68
+ };
69
+ const resolveConnectedNodeTargets = async (context)=>{
70
+ if (context.nodeConnectedTargetsProvider) try {
71
+ const rawTargets = await context.nodeConnectedTargetsProvider();
72
+ if (!Array.isArray(rawTargets)) return [];
73
+ const seen = new Set();
74
+ const normalized = [];
75
+ for (const target of rawTargets){
76
+ if (!target || "object" != typeof target) continue;
77
+ const typedTarget = target;
78
+ const nodeId = typedTarget.nodeId?.trim();
79
+ if (!nodeId || seen.has(nodeId)) continue;
80
+ seen.add(nodeId);
81
+ const rawCapabilities = typedTarget.capabilities;
82
+ const capabilities = Array.isArray(rawCapabilities) ? rawCapabilities.filter((value)=>"string" == typeof value).map((value)=>value.trim()).filter(Boolean) : [];
83
+ normalized.push({
84
+ nodeId,
85
+ clientId: typedTarget.clientId?.trim() || void 0,
86
+ name: typedTarget.name?.trim() || void 0,
87
+ capabilities: capabilities.length > 0 ? capabilities : void 0
88
+ });
89
+ }
90
+ return normalized;
91
+ } catch {
92
+ return [];
93
+ }
94
+ const nodeIds = await resolveConnectedNodeIds(context);
95
+ return nodeIds.map((nodeId)=>({
96
+ nodeId
97
+ }));
98
+ };
99
+ const buildNodeTargetsMessage = async (context)=>{
100
+ const connectedNodeTargets = await resolveConnectedNodeTargets(context);
101
+ const connectedNodeIds = connectedNodeTargets.map((target)=>target.nodeId);
102
+ const defaultClientId = context.defaultNodeTargetClientId?.trim();
103
+ if (0 === connectedNodeIds.length && !defaultClientId) return null;
104
+ const lines = [
105
+ "** Connected Node Targets **"
106
+ ];
107
+ if (connectedNodeIds.length > 0) lines.push(`- Connected node IDs: ${connectedNodeIds.join(", ")}`);
108
+ else lines.push("- Connected node IDs: (none currently connected)");
109
+ const withMetadata = connectedNodeTargets.filter((target)=>Boolean(target.clientId) || Boolean(target.name) || target.capabilities && target.capabilities.length > 0);
110
+ if (withMetadata.length > 0) {
111
+ lines.push("- Connected node metadata:");
112
+ for (const target of withMetadata.slice(0, 8)){
113
+ const details = [];
114
+ if (target.clientId) details.push(`clientId: ${target.clientId}`);
115
+ if (target.name) details.push(`name: ${target.name}`);
116
+ if (target.capabilities && target.capabilities.length > 0) {
117
+ const preview = target.capabilities.slice(0, 6).join(", ");
118
+ const remaining = target.capabilities.length - 6;
119
+ details.push(remaining > 0 ? `capabilities: ${preview} (+${remaining} more)` : `capabilities: ${preview}`);
120
+ }
121
+ if (details.length > 0) lines.push(` - ${target.nodeId} (${details.join("; ")})`);
122
+ }
123
+ if (withMetadata.length > 8) lines.push(` - ... ${withMetadata.length - 8} more connected node(s)`);
124
+ }
125
+ if (defaultClientId) lines.push(`- Default node target clientId for this request: ${defaultClientId}`);
126
+ lines.push("- For node_notify/node_run, set target.nodeId or target.clientId when the user specifies a device.");
127
+ return lines.join("\n");
128
+ };
31
129
  const additionalMessageMiddleware = (context = {})=>({
32
- name: "additional-message-middleware",
130
+ name: INJECTION_SOURCE,
33
131
  [MIDDLEWARE_BRAND]: true,
34
132
  beforeAgent: async (input)=>{
35
- const alreadyInjected = input.messages.some((message)=>message?.additional_kwargs?.source === "additional-message-middleware");
36
- if (alreadyInjected) return input;
133
+ const messagesWithoutInjected = input.messages.filter((message)=>message?.additional_kwargs?.source !== INJECTION_SOURCE);
37
134
  const lines = [
38
135
  getConfidentialityNotice(),
39
136
  `** Current Date Time (UTC): ${new Date().toISOString()} **`
@@ -42,6 +139,8 @@ const additionalMessageMiddleware = (context = {})=>({
42
139
  if (outputLocation) lines.push(outputLocation);
43
140
  const workingDirectory = buildWorkingDirectoryMessage(context);
44
141
  if (workingDirectory) lines.push(workingDirectory);
142
+ const nodeTargets = await buildNodeTargetsMessage(context);
143
+ if (nodeTargets) lines.push(nodeTargets);
45
144
  lines.push("** Long-term memory **\n- Use /memories/ for durable notes across threads.\n- Store stable preferences, project context, decisions, and research notes.\n- Avoid transient logs; keep entries concise and organized.\n- Suggested paths: /memories/preferences.md, /memories/projects/<name>/context.md, /memories/projects/<name>/decisions.md");
46
145
  if (false === context.dynamicUiEnabled) lines.push("** Dynamic UI **\n- Dynamic UI rendering is disabled for this gateway.\n- Respond with plain text and avoid calling UI presentation tools.");
47
146
  else {
@@ -54,14 +153,19 @@ const additionalMessageMiddleware = (context = {})=>({
54
153
  lines.push("** Dynamic UI Registry **\n" + summaryLines + "\n- Use ui_registry_get for schema details, then ui_present with textFallback.");
55
154
  }
56
155
  }
57
- input.messages.unshift(new HumanMessage({
58
- content: lines.join("\n\n"),
59
- additional_kwargs: {
60
- ui_hidden: true,
61
- source: "additional-message-middleware"
62
- }
63
- }));
64
- return input;
156
+ return {
157
+ ...input,
158
+ messages: [
159
+ new HumanMessage({
160
+ content: lines.join("\n\n"),
161
+ additional_kwargs: {
162
+ ui_hidden: true,
163
+ source: INJECTION_SOURCE
164
+ }
165
+ }),
166
+ ...messagesWithoutInjected
167
+ ]
168
+ };
65
169
  }
66
170
  });
67
171
  export { additionalMessageMiddleware };
@@ -197,6 +197,51 @@ Markdown agent`;
197
197
  });
198
198
  });
199
199
  (0, external_vitest_namespaceObject.describe)("loadAgent", ()=>{
200
+ (0, external_vitest_namespaceObject.it)("should always inject node tools for top-level agents", async ()=>{
201
+ const agentDir = (0, external_node_path_namespaceObject.join)(TEST_CONFIG_DIR, "agents", "node-enabled-agent");
202
+ (0, external_node_fs_namespaceObject.mkdirSync)(agentDir, {
203
+ recursive: true
204
+ });
205
+ const config = {
206
+ name: "node-enabled-agent",
207
+ description: "Agent with default node tools",
208
+ systemPrompt: "You are node capable"
209
+ };
210
+ (0, external_node_fs_namespaceObject.writeFileSync)((0, external_node_path_namespaceObject.join)(agentDir, "agent.json"), JSON.stringify(config));
211
+ const loader = new agentLoader_cjs_namespaceObject.AgentLoader(TEST_CONFIG_DIR);
212
+ const agent = await loader.loadAgent("node-enabled-agent");
213
+ const toolNames = (agent?.tools || []).map((tool)=>tool.name);
214
+ (0, external_vitest_namespaceObject.expect)(toolNames).toContain("node_notify");
215
+ (0, external_vitest_namespaceObject.expect)(toolNames).toContain("node_run");
216
+ });
217
+ (0, external_vitest_namespaceObject.it)("should always inject and deduplicate node tools for subagents", async ()=>{
218
+ const agentDir = (0, external_node_path_namespaceObject.join)(TEST_CONFIG_DIR, "agents", "node-subagent-parent");
219
+ (0, external_node_fs_namespaceObject.mkdirSync)(agentDir, {
220
+ recursive: true
221
+ });
222
+ const config = {
223
+ name: "node-subagent-parent",
224
+ description: "Parent agent",
225
+ systemPrompt: "You are the parent",
226
+ subAgents: [
227
+ {
228
+ name: "node-subagent",
229
+ description: "Subagent",
230
+ systemPrompt: "You are a subagent",
231
+ tools: [
232
+ "node_run"
233
+ ]
234
+ }
235
+ ]
236
+ };
237
+ (0, external_node_fs_namespaceObject.writeFileSync)((0, external_node_path_namespaceObject.join)(agentDir, "agent.json"), JSON.stringify(config));
238
+ const loader = new agentLoader_cjs_namespaceObject.AgentLoader(TEST_CONFIG_DIR);
239
+ const agent = await loader.loadAgent("node-subagent-parent");
240
+ const sub = agent?.subagents?.[0];
241
+ const toolNames = (sub?.tools || []).map((tool)=>tool.name);
242
+ (0, external_vitest_namespaceObject.expect)(toolNames).toContain("node_notify");
243
+ (0, external_vitest_namespaceObject.expect)(toolNames.filter((name)=>"node_run" === name)).toHaveLength(1);
244
+ });
200
245
  (0, external_vitest_namespaceObject.it)("should hydrate subagent tools for runtime use", async ()=>{
201
246
  const agentDir = (0, external_node_path_namespaceObject.join)(TEST_CONFIG_DIR, "agents", "parent-agent");
202
247
  (0, external_node_fs_namespaceObject.mkdirSync)(agentDir, {
@@ -195,6 +195,51 @@ Markdown agent`;
195
195
  });
196
196
  });
197
197
  describe("loadAgent", ()=>{
198
+ it("should always inject node tools for top-level agents", async ()=>{
199
+ const agentDir = join(TEST_CONFIG_DIR, "agents", "node-enabled-agent");
200
+ mkdirSync(agentDir, {
201
+ recursive: true
202
+ });
203
+ const config = {
204
+ name: "node-enabled-agent",
205
+ description: "Agent with default node tools",
206
+ systemPrompt: "You are node capable"
207
+ };
208
+ writeFileSync(join(agentDir, "agent.json"), JSON.stringify(config));
209
+ const loader = new AgentLoader(TEST_CONFIG_DIR);
210
+ const agent = await loader.loadAgent("node-enabled-agent");
211
+ const toolNames = (agent?.tools || []).map((tool)=>tool.name);
212
+ expect(toolNames).toContain("node_notify");
213
+ expect(toolNames).toContain("node_run");
214
+ });
215
+ it("should always inject and deduplicate node tools for subagents", async ()=>{
216
+ const agentDir = join(TEST_CONFIG_DIR, "agents", "node-subagent-parent");
217
+ mkdirSync(agentDir, {
218
+ recursive: true
219
+ });
220
+ const config = {
221
+ name: "node-subagent-parent",
222
+ description: "Parent agent",
223
+ systemPrompt: "You are the parent",
224
+ subAgents: [
225
+ {
226
+ name: "node-subagent",
227
+ description: "Subagent",
228
+ systemPrompt: "You are a subagent",
229
+ tools: [
230
+ "node_run"
231
+ ]
232
+ }
233
+ ]
234
+ };
235
+ writeFileSync(join(agentDir, "agent.json"), JSON.stringify(config));
236
+ const loader = new AgentLoader(TEST_CONFIG_DIR);
237
+ const agent = await loader.loadAgent("node-subagent-parent");
238
+ const sub = agent?.subagents?.[0];
239
+ const toolNames = (sub?.tools || []).map((tool)=>tool.name);
240
+ expect(toolNames).toContain("node_notify");
241
+ expect(toolNames.filter((name)=>"node_run" === name)).toHaveLength(1);
242
+ });
198
243
  it("should hydrate subagent tools for runtime use", async ()=>{
199
244
  const agentDir = join(TEST_CONFIG_DIR, "agents", "parent-agent");
200
245
  mkdirSync(agentDir, {
@@ -124,6 +124,8 @@ const terminal_session_manager_cjs_namespaceObject = require("../tools/terminal_
124
124
  "browser_control",
125
125
  "command_execute",
126
126
  "background_terminal",
127
+ "node_notify",
128
+ "node_run",
127
129
  "think",
128
130
  "code_search",
129
131
  "git_status",
@@ -122,6 +122,8 @@ describe("Tool Registry", ()=>{
122
122
  "browser_control",
123
123
  "command_execute",
124
124
  "background_terminal",
125
+ "node_notify",
126
+ "node_run",
125
127
  "think",
126
128
  "code_search",
127
129
  "git_status",
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ createNodeNotifyTool: ()=>createNodeNotifyTool,
28
+ createNodeRunTool: ()=>createNodeRunTool
29
+ });
30
+ const external_langchain_namespaceObject = require("langchain");
31
+ const external_zod_namespaceObject = require("zod");
32
+ const DEFAULT_NODE_TIMEOUT_MS = 30000;
33
+ const TargetSchema = external_zod_namespaceObject.object({
34
+ nodeId: external_zod_namespaceObject.string().min(1).optional(),
35
+ clientId: external_zod_namespaceObject.string().min(1).optional()
36
+ }).optional();
37
+ function resolveTarget(target, defaultTargetClientId) {
38
+ const targetNodeId = target?.nodeId?.trim();
39
+ if (targetNodeId) return {
40
+ targetNodeId
41
+ };
42
+ const targetClientId = target?.clientId?.trim() || defaultTargetClientId?.trim();
43
+ if (targetClientId) return {
44
+ targetClientId
45
+ };
46
+ return {};
47
+ }
48
+ function missingInvokerResult(toolName) {
49
+ return {
50
+ ok: false,
51
+ error: `${toolName} is only available when invoked through Wingman Gateway.`
52
+ };
53
+ }
54
+ const createNodeNotifyTool = (options = {})=>{
55
+ const { nodeInvoker, defaultTargetClientId } = options;
56
+ return (0, external_langchain_namespaceObject.tool)(async ({ body, title, target, timeoutMs })=>{
57
+ if (!nodeInvoker) return missingInvokerResult("node_notify");
58
+ try {
59
+ const { targetNodeId, targetClientId } = resolveTarget(target, defaultTargetClientId);
60
+ const result = await nodeInvoker({
61
+ tool: "system.notify",
62
+ args: {
63
+ title: title?.trim() || "Wingman",
64
+ body: body.trim()
65
+ },
66
+ timeoutMs: timeoutMs ?? DEFAULT_NODE_TIMEOUT_MS,
67
+ targetNodeId,
68
+ targetClientId,
69
+ capability: "system.notify"
70
+ });
71
+ const payload = result.payload && "object" == typeof result.payload ? result.payload : null;
72
+ return {
73
+ ok: true,
74
+ nodeId: result.nodeId,
75
+ delivered: payload?.delivered === true,
76
+ payload: result.payload
77
+ };
78
+ } catch (error) {
79
+ return {
80
+ ok: false,
81
+ error: error instanceof Error ? error.message : String(error)
82
+ };
83
+ }
84
+ }, {
85
+ name: "node_notify",
86
+ description: "Send a user-visible notification on an approved connected node device via system.notify.",
87
+ schema: external_zod_namespaceObject.object({
88
+ body: external_zod_namespaceObject.string().min(1).describe("Notification body text shown on the node device"),
89
+ title: external_zod_namespaceObject.string().min(1).optional().describe("Optional notification title"),
90
+ target: TargetSchema.describe("Optional target selector. Use nodeId for a specific node or clientId for a paired device."),
91
+ timeoutMs: external_zod_namespaceObject.number().int().min(1000).max(120000).optional().describe("Optional timeout for node execution in milliseconds")
92
+ })
93
+ });
94
+ };
95
+ const createNodeRunTool = (options = {})=>{
96
+ const { nodeInvoker, defaultTargetClientId } = options;
97
+ return (0, external_langchain_namespaceObject.tool)(async ({ command, args, target, timeoutMs })=>{
98
+ if (!nodeInvoker) return missingInvokerResult("node_run");
99
+ try {
100
+ const { targetNodeId, targetClientId } = resolveTarget(target, defaultTargetClientId);
101
+ const result = await nodeInvoker({
102
+ tool: "system.run",
103
+ args: {
104
+ command: command.trim(),
105
+ args: Array.isArray(args) ? args : []
106
+ },
107
+ timeoutMs: timeoutMs ?? DEFAULT_NODE_TIMEOUT_MS,
108
+ targetNodeId,
109
+ targetClientId,
110
+ capability: "system.run"
111
+ });
112
+ const payload = result.payload && "object" == typeof result.payload ? result.payload : null;
113
+ return {
114
+ ok: true,
115
+ nodeId: result.nodeId,
116
+ exitCode: "number" == typeof payload?.exitCode ? payload.exitCode : void 0,
117
+ stdout: "string" == typeof payload?.stdout ? payload.stdout : "",
118
+ stderr: "string" == typeof payload?.stderr ? payload.stderr : "",
119
+ payload: result.payload
120
+ };
121
+ } catch (error) {
122
+ return {
123
+ ok: false,
124
+ error: error instanceof Error ? error.message : String(error)
125
+ };
126
+ }
127
+ }, {
128
+ name: "node_run",
129
+ description: "Run a command on an approved connected node device via system.run and return exitCode/stdout/stderr.",
130
+ schema: external_zod_namespaceObject.object({
131
+ command: external_zod_namespaceObject.string().min(1).describe("Executable command path/name to run on the node device"),
132
+ args: external_zod_namespaceObject.array(external_zod_namespaceObject.string()).optional().describe("Optional command arguments"),
133
+ target: TargetSchema.describe("Optional target selector. Use nodeId for a specific node or clientId for a paired device."),
134
+ timeoutMs: external_zod_namespaceObject.number().int().min(1000).max(120000).optional().describe("Optional timeout for node execution in milliseconds")
135
+ })
136
+ });
137
+ };
138
+ exports.createNodeNotifyTool = __webpack_exports__.createNodeNotifyTool;
139
+ exports.createNodeRunTool = __webpack_exports__.createNodeRunTool;
140
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
141
+ "createNodeNotifyTool",
142
+ "createNodeRunTool"
143
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
144
+ Object.defineProperty(exports, '__esModule', {
145
+ value: true
146
+ });
@@ -0,0 +1,86 @@
1
+ import * as z from "zod";
2
+ export type NodeInvokeRequest = {
3
+ tool: "system.notify" | "system.run";
4
+ args?: Record<string, unknown>;
5
+ timeoutMs?: number;
6
+ targetNodeId?: string;
7
+ targetClientId?: string;
8
+ capability?: string;
9
+ };
10
+ export type NodeInvokeResult = {
11
+ nodeId: string;
12
+ payload: unknown;
13
+ };
14
+ export type NodeInvoker = (request: NodeInvokeRequest) => Promise<NodeInvokeResult>;
15
+ export interface NodeToolOptions {
16
+ nodeInvoker?: NodeInvoker;
17
+ defaultTargetClientId?: string;
18
+ }
19
+ export declare const createNodeNotifyTool: (options?: NodeToolOptions) => import("langchain").DynamicStructuredTool<z.ZodObject<{
20
+ body: z.ZodString;
21
+ title: z.ZodOptional<z.ZodString>;
22
+ target: z.ZodOptional<z.ZodObject<{
23
+ nodeId: z.ZodOptional<z.ZodString>;
24
+ clientId: z.ZodOptional<z.ZodString>;
25
+ }, z.core.$strip>>;
26
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
27
+ }, z.core.$strip>, {
28
+ body: string;
29
+ title?: string;
30
+ target?: {
31
+ nodeId?: string;
32
+ clientId?: string;
33
+ };
34
+ timeoutMs?: number;
35
+ }, {
36
+ body: string;
37
+ title?: string | undefined;
38
+ target?: {
39
+ nodeId?: string | undefined;
40
+ clientId?: string | undefined;
41
+ } | undefined;
42
+ timeoutMs?: number | undefined;
43
+ }, {
44
+ ok: boolean;
45
+ error: string;
46
+ } | {
47
+ ok: boolean;
48
+ nodeId: string;
49
+ delivered: boolean;
50
+ payload: unknown;
51
+ }, "node_notify">;
52
+ export declare const createNodeRunTool: (options?: NodeToolOptions) => import("langchain").DynamicStructuredTool<z.ZodObject<{
53
+ command: z.ZodString;
54
+ args: z.ZodOptional<z.ZodArray<z.ZodString>>;
55
+ target: z.ZodOptional<z.ZodObject<{
56
+ nodeId: z.ZodOptional<z.ZodString>;
57
+ clientId: z.ZodOptional<z.ZodString>;
58
+ }, z.core.$strip>>;
59
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
60
+ }, z.core.$strip>, {
61
+ command: string;
62
+ args?: string[];
63
+ target?: {
64
+ nodeId?: string;
65
+ clientId?: string;
66
+ };
67
+ timeoutMs?: number;
68
+ }, {
69
+ command: string;
70
+ args?: string[] | undefined;
71
+ target?: {
72
+ nodeId?: string | undefined;
73
+ clientId?: string | undefined;
74
+ } | undefined;
75
+ timeoutMs?: number | undefined;
76
+ }, {
77
+ ok: boolean;
78
+ error: string;
79
+ } | {
80
+ ok: boolean;
81
+ nodeId: string;
82
+ exitCode: number | undefined;
83
+ stdout: string;
84
+ stderr: string;
85
+ payload: unknown;
86
+ }, "node_run">;
@@ -0,0 +1,109 @@
1
+ import { tool } from "langchain";
2
+ import { array, number, object, string } from "zod";
3
+ const DEFAULT_NODE_TIMEOUT_MS = 30000;
4
+ const TargetSchema = object({
5
+ nodeId: string().min(1).optional(),
6
+ clientId: string().min(1).optional()
7
+ }).optional();
8
+ function resolveTarget(target, defaultTargetClientId) {
9
+ const targetNodeId = target?.nodeId?.trim();
10
+ if (targetNodeId) return {
11
+ targetNodeId
12
+ };
13
+ const targetClientId = target?.clientId?.trim() || defaultTargetClientId?.trim();
14
+ if (targetClientId) return {
15
+ targetClientId
16
+ };
17
+ return {};
18
+ }
19
+ function missingInvokerResult(toolName) {
20
+ return {
21
+ ok: false,
22
+ error: `${toolName} is only available when invoked through Wingman Gateway.`
23
+ };
24
+ }
25
+ const createNodeNotifyTool = (options = {})=>{
26
+ const { nodeInvoker, defaultTargetClientId } = options;
27
+ return tool(async ({ body, title, target, timeoutMs })=>{
28
+ if (!nodeInvoker) return missingInvokerResult("node_notify");
29
+ try {
30
+ const { targetNodeId, targetClientId } = resolveTarget(target, defaultTargetClientId);
31
+ const result = await nodeInvoker({
32
+ tool: "system.notify",
33
+ args: {
34
+ title: title?.trim() || "Wingman",
35
+ body: body.trim()
36
+ },
37
+ timeoutMs: timeoutMs ?? DEFAULT_NODE_TIMEOUT_MS,
38
+ targetNodeId,
39
+ targetClientId,
40
+ capability: "system.notify"
41
+ });
42
+ const payload = result.payload && "object" == typeof result.payload ? result.payload : null;
43
+ return {
44
+ ok: true,
45
+ nodeId: result.nodeId,
46
+ delivered: payload?.delivered === true,
47
+ payload: result.payload
48
+ };
49
+ } catch (error) {
50
+ return {
51
+ ok: false,
52
+ error: error instanceof Error ? error.message : String(error)
53
+ };
54
+ }
55
+ }, {
56
+ name: "node_notify",
57
+ description: "Send a user-visible notification on an approved connected node device via system.notify.",
58
+ schema: object({
59
+ body: string().min(1).describe("Notification body text shown on the node device"),
60
+ title: string().min(1).optional().describe("Optional notification title"),
61
+ target: TargetSchema.describe("Optional target selector. Use nodeId for a specific node or clientId for a paired device."),
62
+ timeoutMs: number().int().min(1000).max(120000).optional().describe("Optional timeout for node execution in milliseconds")
63
+ })
64
+ });
65
+ };
66
+ const createNodeRunTool = (options = {})=>{
67
+ const { nodeInvoker, defaultTargetClientId } = options;
68
+ return tool(async ({ command, args, target, timeoutMs })=>{
69
+ if (!nodeInvoker) return missingInvokerResult("node_run");
70
+ try {
71
+ const { targetNodeId, targetClientId } = resolveTarget(target, defaultTargetClientId);
72
+ const result = await nodeInvoker({
73
+ tool: "system.run",
74
+ args: {
75
+ command: command.trim(),
76
+ args: Array.isArray(args) ? args : []
77
+ },
78
+ timeoutMs: timeoutMs ?? DEFAULT_NODE_TIMEOUT_MS,
79
+ targetNodeId,
80
+ targetClientId,
81
+ capability: "system.run"
82
+ });
83
+ const payload = result.payload && "object" == typeof result.payload ? result.payload : null;
84
+ return {
85
+ ok: true,
86
+ nodeId: result.nodeId,
87
+ exitCode: "number" == typeof payload?.exitCode ? payload.exitCode : void 0,
88
+ stdout: "string" == typeof payload?.stdout ? payload.stdout : "",
89
+ stderr: "string" == typeof payload?.stderr ? payload.stderr : "",
90
+ payload: result.payload
91
+ };
92
+ } catch (error) {
93
+ return {
94
+ ok: false,
95
+ error: error instanceof Error ? error.message : String(error)
96
+ };
97
+ }
98
+ }, {
99
+ name: "node_run",
100
+ description: "Run a command on an approved connected node device via system.run and return exitCode/stdout/stderr.",
101
+ schema: object({
102
+ command: string().min(1).describe("Executable command path/name to run on the node device"),
103
+ args: array(string()).optional().describe("Optional command arguments"),
104
+ target: TargetSchema.describe("Optional target selector. Use nodeId for a specific node or clientId for a paired device."),
105
+ timeoutMs: number().int().min(1000).max(120000).optional().describe("Optional timeout for node execution in milliseconds")
106
+ })
107
+ });
108
+ };
109
+ export { createNodeNotifyTool, createNodeRunTool };
@@ -600,7 +600,7 @@ Examples:
600
600
  Deployment:
601
601
  Local: Run on localhost or LAN
602
602
  Tailscale: Accessible over Tailscale network
603
- Cloudflare: Deploy to Cloudflare Workers (see cloudflare/README.md)
603
+ Cloudflare: Deploy to Cloudflare Workers (see apps/cloudflare/README.md)
604
604
  `);
605
605
  }
606
606
  exports.executeGatewayCommand = __webpack_exports__.executeGatewayCommand;
@@ -572,7 +572,7 @@ Examples:
572
572
  Deployment:
573
573
  Local: Run on localhost or LAN
574
574
  Tailscale: Accessible over Tailscale network
575
- Cloudflare: Deploy to Cloudflare Workers (see cloudflare/README.md)
575
+ Cloudflare: Deploy to Cloudflare Workers (see apps/cloudflare/README.md)
576
576
  `);
577
577
  }
578
578
  export { executeGatewayCommand };