@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
@@ -0,0 +1,38 @@
1
+ import { cp, mkdtemp, rm, symlink } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { tmpdir } from "node:os";
4
+ const uniqueSkillNames = (skills)=>{
5
+ const seen = new Set();
6
+ const unique = [];
7
+ for (const skill of skills){
8
+ const normalized = skill.trim();
9
+ if (!(!normalized || seen.has(normalized))) {
10
+ seen.add(normalized);
11
+ unique.push(normalized);
12
+ }
13
+ }
14
+ return unique;
15
+ };
16
+ const createSkillOverlayDirectory = async (skillsRoot, activeSkillNames)=>{
17
+ const overlayRoot = await mkdtemp(join(tmpdir(), "wingman-skill-overlay-"));
18
+ for (const skillName of uniqueSkillNames(activeSkillNames)){
19
+ const source = join(skillsRoot, skillName);
20
+ const destination = join(overlayRoot, skillName);
21
+ try {
22
+ await symlink(source, destination, "win32" === process.platform ? "junction" : "dir");
23
+ } catch {
24
+ await cp(source, destination, {
25
+ recursive: true,
26
+ force: true
27
+ });
28
+ }
29
+ }
30
+ return overlayRoot;
31
+ };
32
+ const removeSkillOverlayDirectory = async (overlayPath)=>{
33
+ await rm(overlayPath, {
34
+ recursive: true,
35
+ force: true
36
+ });
37
+ };
38
+ export { createSkillOverlayDirectory, removeSkillOverlayDirectory };
@@ -117,6 +117,98 @@ const additional_messages_cjs_namespaceObject = require("../agent/middleware/add
117
117
  }).length;
118
118
  (0, external_vitest_namespaceObject.expect)(injectedCount).toBe(1);
119
119
  });
120
+ (0, external_vitest_namespaceObject.it)("refreshes injected node context on each invocation", async ()=>{
121
+ let connectedIds = [
122
+ "node-a"
123
+ ];
124
+ const middleware = (0, additional_messages_cjs_namespaceObject.additionalMessageMiddleware)({
125
+ nodeConnectedIdsProvider: ()=>connectedIds
126
+ });
127
+ const beforeAgent = "function" == typeof middleware.beforeAgent ? middleware.beforeAgent : middleware.beforeAgent?.hook;
128
+ if (!beforeAgent) throw new Error("beforeAgent hook not configured");
129
+ const first = await beforeAgent({
130
+ messages: [
131
+ new external_langchain_namespaceObject.HumanMessage("Hello")
132
+ ]
133
+ }, {});
134
+ const firstContent = first.messages[0]?.content ?? "";
135
+ (0, external_vitest_namespaceObject.expect)(firstContent).toContain("Connected node IDs: node-a");
136
+ connectedIds = [
137
+ "node-b"
138
+ ];
139
+ const second = await beforeAgent({
140
+ messages: first.messages
141
+ }, {});
142
+ const secondContent = second.messages[0]?.content ?? "";
143
+ (0, external_vitest_namespaceObject.expect)(secondContent).toContain("Connected node IDs: node-b");
144
+ (0, external_vitest_namespaceObject.expect)(secondContent).not.toContain("Connected node IDs: node-a");
145
+ });
146
+ (0, external_vitest_namespaceObject.it)("injects connected node IDs for node tool targeting when provided", async ()=>{
147
+ const middleware = (0, additional_messages_cjs_namespaceObject.additionalMessageMiddleware)({
148
+ nodeConnectedIdsProvider: ()=>[
149
+ "node-b",
150
+ "node-a",
151
+ "",
152
+ "node-b"
153
+ ]
154
+ });
155
+ const input = {
156
+ messages: [
157
+ new external_langchain_namespaceObject.HumanMessage("Hello")
158
+ ]
159
+ };
160
+ const beforeAgent = "function" == typeof middleware.beforeAgent ? middleware.beforeAgent : middleware.beforeAgent?.hook;
161
+ if (!beforeAgent) throw new Error("beforeAgent hook not configured");
162
+ const result = await beforeAgent(input, {});
163
+ const content = result.messages[0]?.content ?? "";
164
+ (0, external_vitest_namespaceObject.expect)(content).toContain("Connected Node Targets");
165
+ (0, external_vitest_namespaceObject.expect)(content).toContain("Connected node IDs: node-b, node-a");
166
+ (0, external_vitest_namespaceObject.expect)(content).toContain("target.nodeId or target.clientId");
167
+ });
168
+ (0, external_vitest_namespaceObject.it)("injects default node target clientId when present", async ()=>{
169
+ const middleware = (0, additional_messages_cjs_namespaceObject.additionalMessageMiddleware)({
170
+ nodeConnectedIdsProvider: ()=>[],
171
+ defaultNodeTargetClientId: "desktop-abc123"
172
+ });
173
+ const input = {
174
+ messages: [
175
+ new external_langchain_namespaceObject.HumanMessage("Hello")
176
+ ]
177
+ };
178
+ const beforeAgent = "function" == typeof middleware.beforeAgent ? middleware.beforeAgent : middleware.beforeAgent?.hook;
179
+ if (!beforeAgent) throw new Error("beforeAgent hook not configured");
180
+ const result = await beforeAgent(input, {});
181
+ const content = result.messages[0]?.content ?? "";
182
+ (0, external_vitest_namespaceObject.expect)(content).toContain("Connected node IDs: (none currently connected)");
183
+ (0, external_vitest_namespaceObject.expect)(content).toContain("Default node target clientId for this request: desktop-abc123");
184
+ });
185
+ (0, external_vitest_namespaceObject.it)("injects connected node metadata when provided", async ()=>{
186
+ const middleware = (0, additional_messages_cjs_namespaceObject.additionalMessageMiddleware)({
187
+ nodeConnectedTargetsProvider: ()=>[
188
+ {
189
+ nodeId: "node-a",
190
+ clientId: "desktop-a",
191
+ name: "Russell MacBook",
192
+ capabilities: [
193
+ "system.notify",
194
+ "system.run"
195
+ ]
196
+ }
197
+ ]
198
+ });
199
+ const input = {
200
+ messages: [
201
+ new external_langchain_namespaceObject.HumanMessage("Hello")
202
+ ]
203
+ };
204
+ const beforeAgent = "function" == typeof middleware.beforeAgent ? middleware.beforeAgent : middleware.beforeAgent?.hook;
205
+ if (!beforeAgent) throw new Error("beforeAgent hook not configured");
206
+ const result = await beforeAgent(input, {});
207
+ const content = result.messages[0]?.content ?? "";
208
+ (0, external_vitest_namespaceObject.expect)(content).toContain("Connected node metadata:");
209
+ (0, external_vitest_namespaceObject.expect)(content).toContain("node-a (clientId: desktop-a; name: Russell MacBook;");
210
+ (0, external_vitest_namespaceObject.expect)(content).toContain("capabilities: system.notify, system.run");
211
+ });
120
212
  });
121
213
  for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
122
214
  Object.defineProperty(exports, '__esModule', {
@@ -93,4 +93,96 @@ describe("additionalMessageMiddleware", ()=>{
93
93
  }).length;
94
94
  expect(injectedCount).toBe(1);
95
95
  });
96
+ it("refreshes injected node context on each invocation", async ()=>{
97
+ let connectedIds = [
98
+ "node-a"
99
+ ];
100
+ const middleware = additionalMessageMiddleware({
101
+ nodeConnectedIdsProvider: ()=>connectedIds
102
+ });
103
+ const beforeAgent = "function" == typeof middleware.beforeAgent ? middleware.beforeAgent : middleware.beforeAgent?.hook;
104
+ if (!beforeAgent) throw new Error("beforeAgent hook not configured");
105
+ const first = await beforeAgent({
106
+ messages: [
107
+ new HumanMessage("Hello")
108
+ ]
109
+ }, {});
110
+ const firstContent = first.messages[0]?.content ?? "";
111
+ expect(firstContent).toContain("Connected node IDs: node-a");
112
+ connectedIds = [
113
+ "node-b"
114
+ ];
115
+ const second = await beforeAgent({
116
+ messages: first.messages
117
+ }, {});
118
+ const secondContent = second.messages[0]?.content ?? "";
119
+ expect(secondContent).toContain("Connected node IDs: node-b");
120
+ expect(secondContent).not.toContain("Connected node IDs: node-a");
121
+ });
122
+ it("injects connected node IDs for node tool targeting when provided", async ()=>{
123
+ const middleware = additionalMessageMiddleware({
124
+ nodeConnectedIdsProvider: ()=>[
125
+ "node-b",
126
+ "node-a",
127
+ "",
128
+ "node-b"
129
+ ]
130
+ });
131
+ const input = {
132
+ messages: [
133
+ new HumanMessage("Hello")
134
+ ]
135
+ };
136
+ const beforeAgent = "function" == typeof middleware.beforeAgent ? middleware.beforeAgent : middleware.beforeAgent?.hook;
137
+ if (!beforeAgent) throw new Error("beforeAgent hook not configured");
138
+ const result = await beforeAgent(input, {});
139
+ const content = result.messages[0]?.content ?? "";
140
+ expect(content).toContain("Connected Node Targets");
141
+ expect(content).toContain("Connected node IDs: node-b, node-a");
142
+ expect(content).toContain("target.nodeId or target.clientId");
143
+ });
144
+ it("injects default node target clientId when present", async ()=>{
145
+ const middleware = additionalMessageMiddleware({
146
+ nodeConnectedIdsProvider: ()=>[],
147
+ defaultNodeTargetClientId: "desktop-abc123"
148
+ });
149
+ const input = {
150
+ messages: [
151
+ new HumanMessage("Hello")
152
+ ]
153
+ };
154
+ const beforeAgent = "function" == typeof middleware.beforeAgent ? middleware.beforeAgent : middleware.beforeAgent?.hook;
155
+ if (!beforeAgent) throw new Error("beforeAgent hook not configured");
156
+ const result = await beforeAgent(input, {});
157
+ const content = result.messages[0]?.content ?? "";
158
+ expect(content).toContain("Connected node IDs: (none currently connected)");
159
+ expect(content).toContain("Default node target clientId for this request: desktop-abc123");
160
+ });
161
+ it("injects connected node metadata when provided", async ()=>{
162
+ const middleware = additionalMessageMiddleware({
163
+ nodeConnectedTargetsProvider: ()=>[
164
+ {
165
+ nodeId: "node-a",
166
+ clientId: "desktop-a",
167
+ name: "Russell MacBook",
168
+ capabilities: [
169
+ "system.notify",
170
+ "system.run"
171
+ ]
172
+ }
173
+ ]
174
+ });
175
+ const input = {
176
+ messages: [
177
+ new HumanMessage("Hello")
178
+ ]
179
+ };
180
+ const beforeAgent = "function" == typeof middleware.beforeAgent ? middleware.beforeAgent : middleware.beforeAgent?.hook;
181
+ if (!beforeAgent) throw new Error("beforeAgent hook not configured");
182
+ const result = await beforeAgent(input, {});
183
+ const content = result.messages[0]?.content ?? "";
184
+ expect(content).toContain("Connected node metadata:");
185
+ expect(content).toContain("node-a (clientId: desktop-a; name: Russell MacBook;");
186
+ expect(content).toContain("capabilities: system.notify, system.run");
187
+ });
96
188
  });
@@ -75,9 +75,13 @@ const external_os_namespaceObject = require("os");
75
75
  outputMode: "auto"
76
76
  },
77
77
  skills: {
78
- provider: "github",
79
- repositoryOwner: "anthropics",
80
- repositoryName: "skills",
78
+ provider: "hybrid",
79
+ repositories: [
80
+ {
81
+ owner: "RussellCanfield",
82
+ name: "wingman-ai"
83
+ }
84
+ ],
81
85
  clawhubBaseUrl: "https://clawhub.ai",
82
86
  skillsDirectory: "skills",
83
87
  security: {
@@ -73,9 +73,13 @@ describe("CLI Config Loader", ()=>{
73
73
  outputMode: "auto"
74
74
  },
75
75
  skills: {
76
- provider: "github",
77
- repositoryOwner: "anthropics",
78
- repositoryName: "skills",
76
+ provider: "hybrid",
77
+ repositories: [
78
+ {
79
+ owner: "RussellCanfield",
80
+ name: "wingman-ai"
81
+ }
82
+ ],
79
83
  clawhubBaseUrl: "https://clawhub.ai",
80
84
  skillsDirectory: "skills",
81
85
  security: {
@@ -35,6 +35,12 @@ const init_cjs_namespaceObject = require("../cli/commands/init.cjs");
35
35
  const config = JSON.parse((0, external_node_fs_namespaceObject.readFileSync)(configPath, "utf-8"));
36
36
  (0, external_vitest_namespaceObject.expect)(config.defaultAgent).toBe("wingman");
37
37
  (0, external_vitest_namespaceObject.expect)(config.gateway.fsRoots).toContain(".");
38
+ (0, external_vitest_namespaceObject.expect)(config.browser?.defaultProfile).toBe("default");
39
+ (0, external_vitest_namespaceObject.expect)(config.browser?.profiles?.default).toBe(".wingman/browser-profiles/default");
40
+ (0, external_vitest_namespaceObject.expect)(config.browser?.extensions?.wingman).toBe(".wingman/browser-extensions/wingman");
41
+ (0, external_vitest_namespaceObject.expect)(config.browser?.defaultExtensions).toContain("wingman");
42
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)((0, external_node_path_namespaceObject.join)(workspace, ".wingman", "browser-profiles", "default"))).toBe(true);
43
+ (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)((0, external_node_path_namespaceObject.join)(workspace, ".wingman", "browser-extensions", "wingman", "manifest.json"))).toBe(true);
38
44
  const agentPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "wingman", "agent.json");
39
45
  (0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(agentPath)).toBe(true);
40
46
  const agent = JSON.parse((0, external_node_fs_namespaceObject.readFileSync)(agentPath, "utf-8"));
@@ -135,6 +141,54 @@ const init_cjs_namespaceObject = require("../cli/commands/init.cjs");
135
141
  "./existing",
136
142
  "."
137
143
  ]));
144
+ (0, external_vitest_namespaceObject.expect)(updated.browser?.defaultProfile).toBe("default");
145
+ (0, external_vitest_namespaceObject.expect)(updated.browser?.profiles?.default).toBe(".wingman/browser-profiles/default");
146
+ (0, external_vitest_namespaceObject.expect)(updated.browser?.extensions?.wingman).toBe(".wingman/browser-extensions/wingman");
147
+ (0, external_vitest_namespaceObject.expect)(updated.browser?.defaultExtensions).toContain("wingman");
148
+ });
149
+ (0, external_vitest_namespaceObject.it)("merges config without overriding existing browser profile defaults", async ()=>{
150
+ const configDir = (0, external_node_path_namespaceObject.join)(workspace, ".wingman");
151
+ const configPath = (0, external_node_path_namespaceObject.join)(configDir, "wingman.config.json");
152
+ (0, external_node_fs_namespaceObject.mkdirSync)(configDir, {
153
+ recursive: true
154
+ });
155
+ (0, external_node_fs_namespaceObject.writeFileSync)(configPath, JSON.stringify({
156
+ defaultAgent: "wingman",
157
+ browser: {
158
+ defaultProfile: "trading",
159
+ profiles: {
160
+ trading: ".wingman/browser-profiles/trading"
161
+ },
162
+ extensions: {
163
+ relay: ".wingman/browser-extensions/relay"
164
+ },
165
+ defaultExtensions: [
166
+ "relay"
167
+ ]
168
+ }
169
+ }, null, 2));
170
+ await (0, init_cjs_namespaceObject.executeInitCommand)({
171
+ subcommand: "",
172
+ args: [],
173
+ verbosity: "silent",
174
+ outputMode: "json",
175
+ options: {
176
+ merge: true,
177
+ only: "config"
178
+ },
179
+ agent: "wingman"
180
+ }, {
181
+ workspace
182
+ });
183
+ const updated = JSON.parse((0, external_node_fs_namespaceObject.readFileSync)(configPath, "utf-8"));
184
+ (0, external_vitest_namespaceObject.expect)(updated.browser?.defaultProfile).toBe("trading");
185
+ (0, external_vitest_namespaceObject.expect)(updated.browser?.profiles?.trading).toBe(".wingman/browser-profiles/trading");
186
+ (0, external_vitest_namespaceObject.expect)(updated.browser?.defaultExtensions).toEqual(external_vitest_namespaceObject.expect.arrayContaining([
187
+ "relay",
188
+ "wingman"
189
+ ]));
190
+ (0, external_vitest_namespaceObject.expect)(updated.browser?.extensions?.relay).toBe(".wingman/browser-extensions/relay");
191
+ (0, external_vitest_namespaceObject.expect)(updated.browser?.extensions?.wingman).toBe(".wingman/browser-extensions/wingman");
138
192
  });
139
193
  (0, external_vitest_namespaceObject.it)("sync mode copies bundled agents without creating config", async ()=>{
140
194
  await (0, init_cjs_namespaceObject.executeInitCommand)({
@@ -33,6 +33,12 @@ describe("CLI init", ()=>{
33
33
  const config = JSON.parse(readFileSync(configPath, "utf-8"));
34
34
  expect(config.defaultAgent).toBe("wingman");
35
35
  expect(config.gateway.fsRoots).toContain(".");
36
+ expect(config.browser?.defaultProfile).toBe("default");
37
+ expect(config.browser?.profiles?.default).toBe(".wingman/browser-profiles/default");
38
+ expect(config.browser?.extensions?.wingman).toBe(".wingman/browser-extensions/wingman");
39
+ expect(config.browser?.defaultExtensions).toContain("wingman");
40
+ expect(existsSync(join(workspace, ".wingman", "browser-profiles", "default"))).toBe(true);
41
+ expect(existsSync(join(workspace, ".wingman", "browser-extensions", "wingman", "manifest.json"))).toBe(true);
36
42
  const agentPath = join(workspace, ".wingman", "agents", "wingman", "agent.json");
37
43
  expect(existsSync(agentPath)).toBe(true);
38
44
  const agent = JSON.parse(readFileSync(agentPath, "utf-8"));
@@ -133,6 +139,54 @@ describe("CLI init", ()=>{
133
139
  "./existing",
134
140
  "."
135
141
  ]));
142
+ expect(updated.browser?.defaultProfile).toBe("default");
143
+ expect(updated.browser?.profiles?.default).toBe(".wingman/browser-profiles/default");
144
+ expect(updated.browser?.extensions?.wingman).toBe(".wingman/browser-extensions/wingman");
145
+ expect(updated.browser?.defaultExtensions).toContain("wingman");
146
+ });
147
+ it("merges config without overriding existing browser profile defaults", async ()=>{
148
+ const configDir = join(workspace, ".wingman");
149
+ const configPath = join(configDir, "wingman.config.json");
150
+ mkdirSync(configDir, {
151
+ recursive: true
152
+ });
153
+ writeFileSync(configPath, JSON.stringify({
154
+ defaultAgent: "wingman",
155
+ browser: {
156
+ defaultProfile: "trading",
157
+ profiles: {
158
+ trading: ".wingman/browser-profiles/trading"
159
+ },
160
+ extensions: {
161
+ relay: ".wingman/browser-extensions/relay"
162
+ },
163
+ defaultExtensions: [
164
+ "relay"
165
+ ]
166
+ }
167
+ }, null, 2));
168
+ await executeInitCommand({
169
+ subcommand: "",
170
+ args: [],
171
+ verbosity: "silent",
172
+ outputMode: "json",
173
+ options: {
174
+ merge: true,
175
+ only: "config"
176
+ },
177
+ agent: "wingman"
178
+ }, {
179
+ workspace
180
+ });
181
+ const updated = JSON.parse(readFileSync(configPath, "utf-8"));
182
+ expect(updated.browser?.defaultProfile).toBe("trading");
183
+ expect(updated.browser?.profiles?.trading).toBe(".wingman/browser-profiles/trading");
184
+ expect(updated.browser?.defaultExtensions).toEqual(expect.arrayContaining([
185
+ "relay",
186
+ "wingman"
187
+ ]));
188
+ expect(updated.browser?.extensions?.relay).toBe(".wingman/browser-extensions/relay");
189
+ expect(updated.browser?.extensions?.wingman).toBe(".wingman/browser-extensions/wingman");
136
190
  });
137
191
  it("sync mode copies bundled agents without creating config", async ()=>{
138
192
  await executeInitCommand({
@@ -18,6 +18,18 @@ const jsonSchema_cjs_namespaceObject = require("../cli/config/jsonSchema.cjs");
18
18
  const scannerArgsDefault = schema.properties.skills.properties.security.properties.scannerArgs.default;
19
19
  (0, external_vitest_namespaceObject.expect)(scannerArgsDefault).toContain("mcp-scan>=0.4,<0.5");
20
20
  });
21
+ (0, external_vitest_namespaceObject.it)("exposes hybrid defaults for skill sources", ()=>{
22
+ const schema = (0, jsonSchema_cjs_namespaceObject.buildWingmanConfigJsonSchema)();
23
+ const repositoriesDefault = schema.properties.skills.properties.repositories.default;
24
+ const providerDefault = schema.properties.skills.properties.provider.default;
25
+ (0, external_vitest_namespaceObject.expect)(providerDefault).toBe("hybrid");
26
+ (0, external_vitest_namespaceObject.expect)(repositoriesDefault).toEqual([
27
+ {
28
+ owner: "RussellCanfield",
29
+ name: "wingman-ai"
30
+ }
31
+ ]);
32
+ });
21
33
  });
22
34
  for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
23
35
  Object.defineProperty(exports, '__esModule', {
@@ -16,4 +16,16 @@ describe("wingman config json schema", ()=>{
16
16
  const scannerArgsDefault = schema.properties.skills.properties.security.properties.scannerArgs.default;
17
17
  expect(scannerArgsDefault).toContain("mcp-scan>=0.4,<0.5");
18
18
  });
19
+ it("exposes hybrid defaults for skill sources", ()=>{
20
+ const schema = buildWingmanConfigJsonSchema();
21
+ const repositoriesDefault = schema.properties.skills.properties.repositories.default;
22
+ const providerDefault = schema.properties.skills.properties.provider.default;
23
+ expect(providerDefault).toBe("hybrid");
24
+ expect(repositoriesDefault).toEqual([
25
+ {
26
+ owner: "RussellCanfield",
27
+ name: "wingman-ai"
28
+ }
29
+ ]);
30
+ });
19
31
  });