@wingman-ai/gateway 0.3.0 → 0.3.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.
Files changed (120) hide show
  1. package/README.md +8 -0
  2. package/dist/agent/config/agentConfig.cjs +12 -0
  3. package/dist/agent/config/agentConfig.d.ts +22 -0
  4. package/dist/agent/config/agentConfig.js +10 -1
  5. package/dist/agent/config/agentLoader.cjs +9 -0
  6. package/dist/agent/config/agentLoader.js +9 -0
  7. package/dist/agent/config/toolRegistry.cjs +17 -0
  8. package/dist/agent/config/toolRegistry.d.ts +15 -0
  9. package/dist/agent/config/toolRegistry.js +17 -0
  10. package/dist/agent/tests/agentConfig.test.cjs +6 -1
  11. package/dist/agent/tests/agentConfig.test.js +6 -1
  12. package/dist/agent/tests/browserControlHelpers.test.cjs +35 -0
  13. package/dist/agent/tests/browserControlHelpers.test.d.ts +1 -0
  14. package/dist/agent/tests/browserControlHelpers.test.js +29 -0
  15. package/dist/agent/tests/browserControlTool.test.cjs +2117 -0
  16. package/dist/agent/tests/browserControlTool.test.d.ts +1 -0
  17. package/dist/agent/tests/browserControlTool.test.js +2111 -0
  18. package/dist/agent/tests/toolRegistry.test.cjs +6 -0
  19. package/dist/agent/tests/toolRegistry.test.js +6 -0
  20. package/dist/agent/tools/browser_control.cjs +1282 -0
  21. package/dist/agent/tools/browser_control.d.ts +478 -0
  22. package/dist/agent/tools/browser_control.js +1242 -0
  23. package/dist/cli/commands/agent.cjs +16 -2
  24. package/dist/cli/commands/agent.js +16 -2
  25. package/dist/cli/commands/browser.cjs +603 -0
  26. package/dist/cli/commands/browser.d.ts +13 -0
  27. package/dist/cli/commands/browser.js +566 -0
  28. package/dist/cli/commands/gateway.cjs +18 -7
  29. package/dist/cli/commands/gateway.d.ts +5 -1
  30. package/dist/cli/commands/gateway.js +18 -7
  31. package/dist/cli/commands/init.cjs +134 -45
  32. package/dist/cli/commands/init.js +134 -45
  33. package/dist/cli/commands/skill.cjs +3 -2
  34. package/dist/cli/commands/skill.js +3 -2
  35. package/dist/cli/config/loader.cjs +15 -0
  36. package/dist/cli/config/loader.js +15 -0
  37. package/dist/cli/config/schema.cjs +51 -2
  38. package/dist/cli/config/schema.d.ts +49 -0
  39. package/dist/cli/config/schema.js +44 -1
  40. package/dist/cli/core/workspace.cjs +89 -0
  41. package/dist/cli/core/workspace.d.ts +1 -0
  42. package/dist/cli/core/workspace.js +55 -0
  43. package/dist/cli/index.cjs +53 -5
  44. package/dist/cli/index.js +53 -5
  45. package/dist/cli/types/browser.cjs +18 -0
  46. package/dist/cli/types/browser.d.ts +9 -0
  47. package/dist/cli/types/browser.js +0 -0
  48. package/dist/gateway/browserRelayServer.cjs +338 -0
  49. package/dist/gateway/browserRelayServer.d.ts +38 -0
  50. package/dist/gateway/browserRelayServer.js +301 -0
  51. package/dist/gateway/http/agents.cjs +22 -0
  52. package/dist/gateway/http/agents.js +22 -0
  53. package/dist/gateway/http/fs.cjs +57 -0
  54. package/dist/gateway/http/fs.js +58 -1
  55. package/dist/gateway/server.cjs +43 -6
  56. package/dist/gateway/server.d.ts +4 -1
  57. package/dist/gateway/server.js +36 -5
  58. package/dist/gateway/transport/websocket.cjs +45 -10
  59. package/dist/gateway/transport/websocket.d.ts +1 -0
  60. package/dist/gateway/transport/websocket.js +41 -9
  61. package/dist/gateway/types.d.ts +4 -0
  62. package/dist/tests/agents-api.test.cjs +52 -0
  63. package/dist/tests/agents-api.test.js +53 -1
  64. package/dist/tests/browser-command.test.cjs +264 -0
  65. package/dist/tests/browser-command.test.d.ts +1 -0
  66. package/dist/tests/browser-command.test.js +258 -0
  67. package/dist/tests/browser-relay-server.test.cjs +20 -0
  68. package/dist/tests/browser-relay-server.test.d.ts +1 -0
  69. package/dist/tests/browser-relay-server.test.js +14 -0
  70. package/dist/tests/cli-config-loader.test.cjs +43 -0
  71. package/dist/tests/cli-config-loader.test.js +43 -0
  72. package/dist/tests/cli-init.test.cjs +25 -2
  73. package/dist/tests/cli-init.test.js +25 -2
  74. package/dist/tests/cli-workspace-root.test.cjs +114 -0
  75. package/dist/tests/cli-workspace-root.test.d.ts +1 -0
  76. package/dist/tests/cli-workspace-root.test.js +108 -0
  77. package/dist/tests/fs-api.test.cjs +138 -0
  78. package/dist/tests/fs-api.test.d.ts +1 -0
  79. package/dist/tests/fs-api.test.js +132 -0
  80. package/dist/tests/gateway-command-workspace.test.cjs +150 -0
  81. package/dist/tests/gateway-command-workspace.test.d.ts +1 -0
  82. package/dist/tests/gateway-command-workspace.test.js +144 -0
  83. package/dist/tests/gateway-request-execution-overrides.test.cjs +42 -0
  84. package/dist/tests/gateway-request-execution-overrides.test.d.ts +1 -0
  85. package/dist/tests/gateway-request-execution-overrides.test.js +36 -0
  86. package/dist/tests/gateway.test.cjs +31 -0
  87. package/dist/tests/gateway.test.js +31 -0
  88. package/dist/tests/websocket-transport.test.cjs +31 -0
  89. package/dist/tests/websocket-transport.test.d.ts +1 -0
  90. package/dist/tests/websocket-transport.test.js +25 -0
  91. package/dist/webui/assets/index-BW9nM0J2.css +11 -0
  92. package/dist/webui/assets/{index-0nUBsUUq.js → index-C8-oboEC.js} +107 -107
  93. package/dist/webui/index.html +2 -2
  94. package/extensions/wingman-browser-extension/README.md +27 -0
  95. package/extensions/wingman-browser-extension/background.js +416 -0
  96. package/extensions/wingman-browser-extension/manifest.json +19 -0
  97. package/extensions/wingman-browser-extension/options.html +156 -0
  98. package/extensions/wingman-browser-extension/options.js +106 -0
  99. package/package.json +8 -6
  100. package/{.wingman → templates}/agents/README.md +2 -1
  101. package/{.wingman → templates}/agents/coding/agent.md +0 -1
  102. package/{.wingman → templates}/agents/coding-v2/agent.md +0 -1
  103. package/{.wingman → templates}/agents/game-dev/agent.md +8 -1
  104. package/{.wingman → templates}/agents/game-dev/art-generation.md +1 -0
  105. package/{.wingman → templates}/agents/main/agent.md +5 -0
  106. package/{.wingman → templates}/agents/researcher/agent.md +9 -0
  107. package/{.wingman → templates}/agents/stock-trader/agent.md +1 -0
  108. package/dist/webui/assets/index-kk7OrD-G.css +0 -11
  109. /package/{.wingman → templates}/agents/coding-v2/implementor.md +0 -0
  110. /package/{.wingman → templates}/agents/game-dev/asset-refinement.md +0 -0
  111. /package/{.wingman → templates}/agents/game-dev/planning-idea.md +0 -0
  112. /package/{.wingman → templates}/agents/game-dev/ui-specialist.md +0 -0
  113. /package/{.wingman → templates}/agents/stock-trader/chain-curator.md +0 -0
  114. /package/{.wingman → templates}/agents/stock-trader/goal-translator.md +0 -0
  115. /package/{.wingman → templates}/agents/stock-trader/guardrails-veto.md +0 -0
  116. /package/{.wingman → templates}/agents/stock-trader/path-planner.md +0 -0
  117. /package/{.wingman → templates}/agents/stock-trader/regime-analyst.md +0 -0
  118. /package/{.wingman → templates}/agents/stock-trader/risk.md +0 -0
  119. /package/{.wingman → templates}/agents/stock-trader/selection.md +0 -0
  120. /package/{.wingman → templates}/agents/stock-trader/strategy-composer.md +0 -0
@@ -0,0 +1,603 @@
1
+ "use strict";
2
+ const __rslib_import_meta_url__ = /*#__PURE__*/ function() {
3
+ return "u" < typeof document ? new (require('url'.replace('', ''))).URL('file:' + __filename).href : document.currentScript && document.currentScript.src || new URL('main.js', document.baseURI).href;
4
+ }();
5
+ var __webpack_require__ = {};
6
+ (()=>{
7
+ __webpack_require__.d = (exports1, definition)=>{
8
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
9
+ enumerable: true,
10
+ get: definition[key]
11
+ });
12
+ };
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
16
+ })();
17
+ (()=>{
18
+ __webpack_require__.r = (exports1)=>{
19
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
20
+ value: 'Module'
21
+ });
22
+ Object.defineProperty(exports1, '__esModule', {
23
+ value: true
24
+ });
25
+ };
26
+ })();
27
+ var __webpack_exports__ = {};
28
+ __webpack_require__.r(__webpack_exports__);
29
+ __webpack_require__.d(__webpack_exports__, {
30
+ executeBrowserCommand: ()=>executeBrowserCommand
31
+ });
32
+ const external_node_fs_namespaceObject = require("node:fs");
33
+ const external_node_child_process_namespaceObject = require("node:child_process");
34
+ const external_node_crypto_namespaceObject = require("node:crypto");
35
+ const external_node_path_namespaceObject = require("node:path");
36
+ const external_node_url_namespaceObject = require("node:url");
37
+ const external_logger_cjs_namespaceObject = require("../../logger.cjs");
38
+ const outputManager_cjs_namespaceObject = require("../core/outputManager.cjs");
39
+ const DEFAULT_CONFIG_DIR = ".wingman";
40
+ const DEFAULT_PROFILES_DIR = ".wingman/browser-profiles";
41
+ const DEFAULT_EXTENSIONS_DIR = ".wingman/browser-extensions";
42
+ const DEFAULT_BUNDLED_EXTENSION_ID = "wingman";
43
+ const BUNDLED_EXTENSION_RELATIVE_PATH = "../../../extensions/wingman-browser-extension";
44
+ const PROFILE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
45
+ async function executeBrowserCommand(args, options = {}) {
46
+ const outputManager = new outputManager_cjs_namespaceObject.OutputManager(args.outputMode);
47
+ try {
48
+ switch(args.subcommand){
49
+ case "profile":
50
+ await executeBrowserProfileCommand(args, outputManager, options);
51
+ break;
52
+ case "extension":
53
+ await executeBrowserExtensionCommand(args, outputManager, options);
54
+ break;
55
+ case "":
56
+ case "help":
57
+ case "--help":
58
+ case "-h":
59
+ showBrowserHelp(outputManager);
60
+ break;
61
+ default:
62
+ throw new Error(`Unknown subcommand: ${args.subcommand}. Run 'wingman browser help' for usage.`);
63
+ }
64
+ } catch (error) {
65
+ const message = error instanceof Error ? error.message : String(error);
66
+ const logFile = (0, external_logger_cjs_namespaceObject.getLogFilePath)();
67
+ (0, external_logger_cjs_namespaceObject.createLogger)().error("Browser command failed", {
68
+ error: message
69
+ });
70
+ if ("interactive" === outputManager.getMode()) {
71
+ console.error(`\nError: ${message}`);
72
+ console.error(`Logs: ${logFile}`);
73
+ process.exit(1);
74
+ } else {
75
+ outputManager.emitAgentError(error);
76
+ process.exit(1);
77
+ }
78
+ }
79
+ }
80
+ async function executeBrowserProfileCommand(args, outputManager, options) {
81
+ const action = args.args[0] || "";
82
+ switch(action){
83
+ case "init":
84
+ await handleProfileInit(args, outputManager, options);
85
+ break;
86
+ case "open":
87
+ await handleProfileOpen(args, outputManager, options);
88
+ break;
89
+ case "":
90
+ case "help":
91
+ case "--help":
92
+ case "-h":
93
+ showBrowserProfileHelp(outputManager);
94
+ break;
95
+ default:
96
+ throw new Error(`Unknown browser profile subcommand: ${action}. Run 'wingman browser profile help' for usage.`);
97
+ }
98
+ }
99
+ async function executeBrowserExtensionCommand(args, outputManager, options) {
100
+ const action = args.args[0] || "";
101
+ switch(action){
102
+ case "install":
103
+ await handleExtensionInstall(args, outputManager, options);
104
+ break;
105
+ case "path":
106
+ await handleExtensionPath(args, outputManager, options);
107
+ break;
108
+ case "list":
109
+ await handleExtensionList(args, outputManager, options);
110
+ break;
111
+ case "pair":
112
+ await handleExtensionPair(args, outputManager, options);
113
+ break;
114
+ case "":
115
+ case "help":
116
+ case "--help":
117
+ case "-h":
118
+ showBrowserExtensionHelp(outputManager);
119
+ break;
120
+ default:
121
+ throw new Error(`Unknown browser extension subcommand: ${action}. Run 'wingman browser extension help' for usage.`);
122
+ }
123
+ }
124
+ async function handleProfileInit(args, outputManager, options) {
125
+ const profileArg = getPositionalArg(args.args, 1);
126
+ if (!profileArg) throw new Error("Profile ID required. Usage: wingman browser profile init <profile-id>");
127
+ const profileId = validateProfileId(profileArg);
128
+ const workspace = options.workspace || process.cwd();
129
+ const configDir = options.configDir || DEFAULT_CONFIG_DIR;
130
+ const configRoot = (0, external_node_path_namespaceObject.join)(workspace, configDir);
131
+ const configPath = (0, external_node_path_namespaceObject.join)(configRoot, "wingman.config.json");
132
+ const config = readConfigObject(configPath);
133
+ const browserConfig = readObject(config.browser);
134
+ const profiles = readStringRecord(browserConfig.profiles);
135
+ const force = getBooleanOption(args.options, "force");
136
+ const profilesDirOption = getStringOption(args.options, "profiles-dir") || getStringOption(args.options, "profilesDir");
137
+ if (profilesDirOption) browserConfig.profilesDir = profilesDirOption;
138
+ const baseProfilesDir = resolveProfilesDir(browserConfig.profilesDir);
139
+ const profilePathOption = getStringOption(args.options, "path");
140
+ const profilePath = profilePathOption ? profilePathOption : normalizePathForConfig((0, external_node_path_namespaceObject.join)(baseProfilesDir, profileId));
141
+ const existingProfilePath = profiles[profileId];
142
+ if (existingProfilePath && existingProfilePath !== profilePath && !force) throw new Error(`Browser profile "${profileId}" already exists at ${existingProfilePath}. Use --force to overwrite.`);
143
+ profiles[profileId] = profilePath;
144
+ browserConfig.profiles = profiles;
145
+ browserConfig.profilesDir = baseProfilesDir;
146
+ const requestedDefault = getBooleanOption(args.options, "default");
147
+ const shouldSetDefault = requestedDefault || "string" != typeof browserConfig.defaultProfile;
148
+ if (shouldSetDefault) browserConfig.defaultProfile = profileId;
149
+ config.browser = browserConfig;
150
+ const absoluteProfilePath = resolveProfilePath(workspace, profilePath);
151
+ (0, external_node_fs_namespaceObject.mkdirSync)(absoluteProfilePath, {
152
+ recursive: true
153
+ });
154
+ (0, external_node_fs_namespaceObject.mkdirSync)(configRoot, {
155
+ recursive: true
156
+ });
157
+ (0, external_node_fs_namespaceObject.writeFileSync)(configPath, JSON.stringify(config, null, 2));
158
+ writeLine(outputManager, `Initialized browser profile "${profileId}".`);
159
+ writeLine(outputManager, `Profile directory: ${absoluteProfilePath}`);
160
+ if (shouldSetDefault) writeLine(outputManager, `Default browser profile: ${profileId}`);
161
+ writeLine(outputManager, `Saved config: ${configPath}`);
162
+ }
163
+ async function handleProfileOpen(args, outputManager, options) {
164
+ const workspace = options.workspace || process.cwd();
165
+ const configDir = options.configDir || DEFAULT_CONFIG_DIR;
166
+ const configRoot = (0, external_node_path_namespaceObject.join)(workspace, configDir);
167
+ const configPath = (0, external_node_path_namespaceObject.join)(configRoot, "wingman.config.json");
168
+ const config = readConfigObject(configPath);
169
+ const browserConfig = readObject(config.browser);
170
+ const requestedProfile = getPositionalArg(args.args, 1);
171
+ const defaultProfile = "string" == typeof browserConfig.defaultProfile ? browserConfig.defaultProfile.trim() : "";
172
+ const resolvedProfileId = validateProfileId(requestedProfile || defaultProfile || "");
173
+ const profiles = readStringRecord(browserConfig.profiles);
174
+ const profilePath = profiles[resolvedProfileId] || normalizePathForConfig((0, external_node_path_namespaceObject.join)(resolveProfilesDir(browserConfig.profilesDir), resolvedProfileId));
175
+ const absoluteProfilePath = resolveProfilePath(workspace, profilePath);
176
+ (0, external_node_fs_namespaceObject.mkdirSync)(absoluteProfilePath, {
177
+ recursive: true
178
+ });
179
+ const url = getStringOption(args.options, "url") || getStringOption(args.options, "target-url") || "https://example.com";
180
+ const headless = getBooleanOption(args.options, "headless");
181
+ const explicitExecutablePath = getStringOption(args.options, "executable-path") || getStringOption(args.options, "executablePath");
182
+ const resolveExecutable = options.resolveExecutablePath || resolveChromeExecutablePath;
183
+ const executablePath = resolveExecutable(explicitExecutablePath);
184
+ const defaultExtensionIds = readStringArray(browserConfig.defaultExtensions);
185
+ const extensionArgs = resolveExtensionArgs(workspace, browserConfig, defaultExtensionIds);
186
+ const chromeArgs = [
187
+ `--user-data-dir=${absoluteProfilePath}`,
188
+ "--no-default-browser-check",
189
+ "--no-first-run",
190
+ "--disable-background-networking",
191
+ "--disable-sync",
192
+ "--mute-audio",
193
+ ...extensionArgs
194
+ ];
195
+ if (headless) chromeArgs.push("--headless=new");
196
+ chromeArgs.push(url);
197
+ const spawnProcess = options.spawnProcess || external_node_child_process_namespaceObject.spawn;
198
+ const chromeProcess = spawnProcess(executablePath, chromeArgs, {
199
+ detached: true,
200
+ stdio: "ignore"
201
+ });
202
+ chromeProcess.unref();
203
+ writeLine(outputManager, `Opened profile "${resolvedProfileId}" at ${url} using ${absoluteProfilePath}`);
204
+ if (defaultExtensionIds.length > 0) writeLine(outputManager, `Loaded default extension(s): ${defaultExtensionIds.join(", ")}`);
205
+ }
206
+ async function handleExtensionInstall(args, outputManager, options) {
207
+ const extensionArg = getPositionalArg(args.args, 1);
208
+ const extensionIdOption = getStringOption(args.options, "id");
209
+ const extensionPathOption = getStringOption(args.options, "path");
210
+ const sourcePathOption = getStringOption(args.options, "source") || getStringOption(args.options, "from");
211
+ const selectedId = extensionArg || extensionIdOption;
212
+ const shouldInstallBundled = !selectedId && !extensionPathOption && !sourcePathOption;
213
+ if (!selectedId && !shouldInstallBundled) throw new Error("Extension ID required when using --source/--path. Usage: wingman browser extension install [extension-id] [options]");
214
+ const extensionId = validateProfileId(selectedId || DEFAULT_BUNDLED_EXTENSION_ID);
215
+ const workspace = options.workspace || process.cwd();
216
+ const configDir = options.configDir || DEFAULT_CONFIG_DIR;
217
+ const configRoot = (0, external_node_path_namespaceObject.join)(workspace, configDir);
218
+ const configPath = (0, external_node_path_namespaceObject.join)(configRoot, "wingman.config.json");
219
+ const config = readConfigObject(configPath);
220
+ const browserConfig = readObject(config.browser);
221
+ const force = getBooleanOption(args.options, "force");
222
+ const extensions = readStringRecord(browserConfig.extensions);
223
+ const extensionsDirOption = getStringOption(args.options, "extensions-dir") || getStringOption(args.options, "extensionsDir");
224
+ if (extensionsDirOption) browserConfig.extensionsDir = extensionsDirOption;
225
+ const extensionsDir = resolveExtensionsDir(browserConfig.extensionsDir);
226
+ const extensionPath = extensionPathOption ? extensionPathOption : normalizePathForConfig((0, external_node_path_namespaceObject.join)(extensionsDir, extensionId));
227
+ const absoluteExtensionPath = resolveProfilePath(workspace, extensionPath);
228
+ const sourcePath = sourcePathOption || (shouldInstallBundled ? resolveBundledExtensionSourcePath() : void 0);
229
+ if (sourcePath) {
230
+ const absoluteSourcePath = resolveProfilePath(workspace, sourcePath);
231
+ if (!(0, external_node_fs_namespaceObject.existsSync)(absoluteSourcePath)) throw new Error(`Extension source does not exist: ${absoluteSourcePath}`);
232
+ if (!(0, external_node_fs_namespaceObject.statSync)(absoluteSourcePath).isDirectory()) throw new Error(`Extension source must be a directory: ${absoluteSourcePath}`);
233
+ if ((0, external_node_fs_namespaceObject.existsSync)(absoluteExtensionPath) && !force) throw new Error(`Extension target already exists at ${absoluteExtensionPath}. Use --force to overwrite.`);
234
+ (0, external_node_fs_namespaceObject.cpSync)(absoluteSourcePath, absoluteExtensionPath, {
235
+ recursive: true,
236
+ force
237
+ });
238
+ }
239
+ if (!(0, external_node_fs_namespaceObject.existsSync)(absoluteExtensionPath)) throw new Error(`Extension path does not exist: ${absoluteExtensionPath}. Provide --source or --path to an unpacked extension directory.`);
240
+ if (!(0, external_node_fs_namespaceObject.statSync)(absoluteExtensionPath).isDirectory()) throw new Error(`Extension path must be a directory: ${absoluteExtensionPath}.`);
241
+ const manifestPath = (0, external_node_path_namespaceObject.join)(absoluteExtensionPath, "manifest.json");
242
+ if (!(0, external_node_fs_namespaceObject.existsSync)(manifestPath)) throw new Error(`manifest.json not found in extension directory: ${absoluteExtensionPath}.`);
243
+ const existingExtensionPath = extensions[extensionId];
244
+ if (existingExtensionPath && existingExtensionPath !== extensionPath && !force) throw new Error(`Extension "${extensionId}" already exists at ${existingExtensionPath}. Use --force to overwrite.`);
245
+ extensions[extensionId] = extensionPath;
246
+ browserConfig.extensions = extensions;
247
+ browserConfig.extensionsDir = extensionsDir;
248
+ const setAsDefault = getBooleanOption(args.options, "default");
249
+ if (setAsDefault) {
250
+ const defaults = new Set(readStringArray(browserConfig.defaultExtensions));
251
+ defaults.add(extensionId);
252
+ browserConfig.defaultExtensions = Array.from(defaults);
253
+ }
254
+ config.browser = browserConfig;
255
+ (0, external_node_fs_namespaceObject.mkdirSync)(configRoot, {
256
+ recursive: true
257
+ });
258
+ (0, external_node_fs_namespaceObject.writeFileSync)(configPath, JSON.stringify(config, null, 2));
259
+ writeLine(outputManager, `Registered extension "${extensionId}".`);
260
+ writeLine(outputManager, `Extension directory: ${absoluteExtensionPath}`);
261
+ if (shouldInstallBundled) writeLine(outputManager, `Installed bundled Wingman extension as "${extensionId}".`);
262
+ if (setAsDefault) writeLine(outputManager, `Added "${extensionId}" to browser.defaultExtensions`);
263
+ writeLine(outputManager, `Saved config: ${configPath}`);
264
+ }
265
+ async function handleExtensionPath(args, outputManager, options) {
266
+ const extensionArg = getPositionalArg(args.args, 1);
267
+ if (!extensionArg) throw new Error("Extension ID required. Usage: wingman browser extension path <extension-id>");
268
+ const extensionId = validateProfileId(extensionArg);
269
+ const workspace = options.workspace || process.cwd();
270
+ const configDir = options.configDir || DEFAULT_CONFIG_DIR;
271
+ const configRoot = (0, external_node_path_namespaceObject.join)(workspace, configDir);
272
+ const configPath = (0, external_node_path_namespaceObject.join)(configRoot, "wingman.config.json");
273
+ const config = readConfigObject(configPath);
274
+ const browserConfig = readObject(config.browser);
275
+ const extensions = readStringRecord(browserConfig.extensions);
276
+ const configuredPath = extensions[extensionId];
277
+ if (!configuredPath) throw new Error(`Extension "${extensionId}" is not configured. Run wingman browser extension install ${extensionId} --path <dir> first.`);
278
+ const absoluteExtensionPath = resolveProfilePath(workspace, configuredPath);
279
+ writeLine(outputManager, absoluteExtensionPath);
280
+ }
281
+ async function handleExtensionList(args, outputManager, options) {
282
+ const workspace = options.workspace || process.cwd();
283
+ const configDir = options.configDir || DEFAULT_CONFIG_DIR;
284
+ const configRoot = (0, external_node_path_namespaceObject.join)(workspace, configDir);
285
+ const configPath = (0, external_node_path_namespaceObject.join)(configRoot, "wingman.config.json");
286
+ const config = readConfigObject(configPath);
287
+ const browserConfig = readObject(config.browser);
288
+ const extensions = readStringRecord(browserConfig.extensions);
289
+ const defaults = new Set(readStringArray(browserConfig.defaultExtensions));
290
+ const entries = Object.entries(extensions);
291
+ if (0 === entries.length) return void writeLine(outputManager, "No browser extensions configured.");
292
+ for (const [extensionId, extensionPath] of entries){
293
+ const marker = defaults.has(extensionId) ? " (default)" : "";
294
+ writeLine(outputManager, `${extensionId}${marker}: ${extensionPath}`);
295
+ }
296
+ }
297
+ async function handleExtensionPair(args, outputManager, options) {
298
+ const workspace = options.workspace || process.cwd();
299
+ const configDir = options.configDir || DEFAULT_CONFIG_DIR;
300
+ const configRoot = (0, external_node_path_namespaceObject.join)(workspace, configDir);
301
+ const configPath = (0, external_node_path_namespaceObject.join)(configRoot, "wingman.config.json");
302
+ const config = readConfigObject(configPath);
303
+ const browserConfig = readObject(config.browser);
304
+ const relayConfig = readObject(browserConfig.relay);
305
+ const relayHost = getStringOption(args.options, "host") || "127.0.0.1";
306
+ if (![
307
+ "127.0.0.1",
308
+ "localhost",
309
+ "::1"
310
+ ].includes(relayHost)) throw new Error(`Relay host must be loopback (127.0.0.1, localhost, ::1). Received "${relayHost}".`);
311
+ const relayPort = getNumberOption(args.options, "port") || ("number" == typeof relayConfig.port ? relayConfig.port : 18792);
312
+ if (!Number.isInteger(relayPort) || relayPort < 1 || relayPort > 65535) throw new Error("Relay port must be an integer between 1 and 65535.");
313
+ const configuredToken = getStringOption(args.options, "token");
314
+ const token = configuredToken || createRelayToken();
315
+ if (token.length < 16) throw new Error("Relay token must be at least 16 characters.");
316
+ relayConfig.enabled = true;
317
+ relayConfig.host = relayHost;
318
+ relayConfig.port = relayPort;
319
+ relayConfig.requireAuth = true;
320
+ relayConfig.authToken = token;
321
+ if ("number" != typeof relayConfig.maxMessageBytes) relayConfig.maxMessageBytes = 262144;
322
+ browserConfig.relay = relayConfig;
323
+ const extensionsDir = resolveExtensionsDir(browserConfig.extensionsDir);
324
+ const extensions = readStringRecord(browserConfig.extensions);
325
+ if (!extensions[DEFAULT_BUNDLED_EXTENSION_ID]) {
326
+ const extensionPath = normalizePathForConfig((0, external_node_path_namespaceObject.join)(extensionsDir, DEFAULT_BUNDLED_EXTENSION_ID));
327
+ const absoluteExtensionPath = resolveProfilePath(workspace, extensionPath);
328
+ const bundledSourcePath = resolveBundledExtensionSourcePath();
329
+ (0, external_node_fs_namespaceObject.mkdirSync)(absoluteExtensionPath, {
330
+ recursive: true
331
+ });
332
+ (0, external_node_fs_namespaceObject.cpSync)(bundledSourcePath, absoluteExtensionPath, {
333
+ recursive: true,
334
+ force: true
335
+ });
336
+ extensions[DEFAULT_BUNDLED_EXTENSION_ID] = extensionPath;
337
+ }
338
+ browserConfig.extensions = extensions;
339
+ browserConfig.extensionsDir = extensionsDir;
340
+ const defaults = new Set(readStringArray(browserConfig.defaultExtensions));
341
+ defaults.add(DEFAULT_BUNDLED_EXTENSION_ID);
342
+ browserConfig.defaultExtensions = Array.from(defaults);
343
+ config.browser = browserConfig;
344
+ (0, external_node_fs_namespaceObject.mkdirSync)(configRoot, {
345
+ recursive: true
346
+ });
347
+ (0, external_node_fs_namespaceObject.writeFileSync)(configPath, JSON.stringify(config, null, 2));
348
+ writeLine(outputManager, "Configured secure browser relay pairing.");
349
+ writeLine(outputManager, `Relay host: ${relayHost}`);
350
+ writeLine(outputManager, `Relay port: ${relayPort}`);
351
+ writeLine(outputManager, `Relay token: ${token}`);
352
+ writeLine(outputManager, `Saved config: ${configPath}`);
353
+ writeLine(outputManager, "Next: open the extension options page and set the same relay token.");
354
+ }
355
+ function showBrowserHelp(outputManager) {
356
+ if ("interactive" === outputManager.getMode()) return void console.log(`
357
+ Wingman Browser Tools
358
+
359
+ Usage:
360
+ wingman browser profile init <profile-id> [options]
361
+ wingman browser profile open [profile-id] [options]
362
+ wingman browser extension install [extension-id] [options]
363
+ wingman browser extension pair [options]
364
+ wingman browser extension path <extension-id>
365
+ wingman browser extension list
366
+ wingman browser extension help
367
+ wingman browser profile help
368
+ wingman browser help
369
+
370
+ Examples:
371
+ wingman browser profile init trading
372
+ wingman browser profile open trading --url https://robinhood.com/login
373
+ wingman browser extension install --default
374
+ wingman browser extension pair
375
+ wingman browser extension install relay --source ./my-extension --default
376
+ wingman browser extension path relay
377
+ wingman browser profile init shopping --default
378
+ wingman browser profile init work --path .wingman/profiles/work
379
+
380
+ Options:
381
+ --workspace <dir> Workspace root (defaults to nearest ancestor with .wingman/)
382
+ --path <dir> Path for profile/extension (relative to workspace or absolute)
383
+ --profiles-dir <dir> Base profile directory used when --path is omitted
384
+ --extensions-dir <dir> Base extension directory used when --path is omitted
385
+ --source <dir> Source directory to copy from during extension install
386
+ --url <url> Target URL for profile open
387
+ --headless Open profile in headless mode
388
+ --default Set as browser.defaultProfile or add to browser.defaultExtensions
389
+ --force Overwrite existing profile/extension mapping
390
+ --host <host> Relay host for extension pairing (must be loopback)
391
+ --port <port> Relay port for extension pairing
392
+ --token <token> Explicit relay token for extension pairing
393
+ `);
394
+ outputManager.emitLog("info", "Browser help requested");
395
+ }
396
+ function showBrowserProfileHelp(outputManager) {
397
+ if ("interactive" === outputManager.getMode()) return void console.log(`
398
+ Wingman Browser Profile Manager
399
+
400
+ Usage:
401
+ wingman browser profile init <profile-id> [options]
402
+ wingman browser profile open [profile-id] [options]
403
+ wingman browser profile help
404
+
405
+ Examples:
406
+ wingman browser profile init trading
407
+ wingman browser profile init work --path .wingman/profiles/work --default
408
+ wingman browser profile open trading --url https://robinhood.com/login
409
+ `);
410
+ outputManager.emitLog("info", "Browser profile help requested");
411
+ }
412
+ function showBrowserExtensionHelp(outputManager) {
413
+ if ("interactive" === outputManager.getMode()) return void console.log(`
414
+ Wingman Browser Extension Manager
415
+
416
+ Usage:
417
+ wingman browser extension install [extension-id] [options]
418
+ wingman browser extension pair [options]
419
+ wingman browser extension path <extension-id>
420
+ wingman browser extension list
421
+ wingman browser extension help
422
+
423
+ Examples:
424
+ wingman browser extension install --default
425
+ wingman browser extension pair
426
+ wingman browser extension install relay --path .wingman/browser-extensions/relay
427
+ wingman browser extension install relay --source ./relay-extension --default
428
+ wingman browser extension path relay
429
+ wingman browser extension list
430
+ `);
431
+ outputManager.emitLog("info", "Browser extension help requested");
432
+ }
433
+ function readConfigObject(configPath) {
434
+ if (!(0, external_node_fs_namespaceObject.existsSync)(configPath)) return {};
435
+ const raw = (0, external_node_fs_namespaceObject.readFileSync)(configPath, "utf-8");
436
+ let parsed;
437
+ try {
438
+ parsed = JSON.parse(raw);
439
+ } catch {
440
+ throw new Error("Existing wingman.config.json is invalid JSON. Fix the file or run wingman init --force.");
441
+ }
442
+ if (!isObject(parsed)) throw new Error("Existing wingman.config.json must be a JSON object.");
443
+ return {
444
+ ...parsed
445
+ };
446
+ }
447
+ function resolveProfilesDir(rawProfilesDir) {
448
+ if ("string" == typeof rawProfilesDir && rawProfilesDir.trim()) return rawProfilesDir.trim();
449
+ return DEFAULT_PROFILES_DIR;
450
+ }
451
+ function resolveExtensionsDir(rawExtensionsDir) {
452
+ if ("string" == typeof rawExtensionsDir && rawExtensionsDir.trim()) return rawExtensionsDir.trim();
453
+ return DEFAULT_EXTENSIONS_DIR;
454
+ }
455
+ function validateProfileId(value) {
456
+ const profileId = value.trim();
457
+ if (!profileId) throw new Error("Profile ID cannot be empty.");
458
+ if (!PROFILE_ID_PATTERN.test(profileId)) throw new Error(`Invalid profile ID "${value}". Use letters, numbers, dot, underscore, or dash.`);
459
+ return profileId;
460
+ }
461
+ function isObject(value) {
462
+ return Boolean(value) && "object" == typeof value && !Array.isArray(value);
463
+ }
464
+ function readObject(value) {
465
+ if (!isObject(value)) return {};
466
+ return {
467
+ ...value
468
+ };
469
+ }
470
+ function readStringRecord(value) {
471
+ if (!isObject(value)) return {};
472
+ const result = {};
473
+ for (const [key, innerValue] of Object.entries(value))if (key.trim() && "string" == typeof innerValue) result[key] = innerValue;
474
+ return result;
475
+ }
476
+ function readStringArray(value) {
477
+ if (!Array.isArray(value)) return [];
478
+ return value.filter((item)=>"string" == typeof item).map((item)=>item.trim()).filter(Boolean);
479
+ }
480
+ function getStringOption(options, key) {
481
+ const value = options[key];
482
+ if ("string" == typeof value && value.trim()) return value.trim();
483
+ }
484
+ function getNumberOption(options, key) {
485
+ const value = options[key];
486
+ if ("number" == typeof value && Number.isFinite(value)) return value;
487
+ if ("string" == typeof value) {
488
+ const parsed = Number.parseInt(value.trim(), 10);
489
+ if (Number.isFinite(parsed)) return parsed;
490
+ }
491
+ }
492
+ function getBooleanOption(options, key) {
493
+ const value = options[key];
494
+ if ("boolean" == typeof value) return value;
495
+ if ("string" == typeof value) {
496
+ const normalized = value.trim().toLowerCase();
497
+ if ([
498
+ "true",
499
+ "1",
500
+ "yes",
501
+ "y",
502
+ "on"
503
+ ].includes(normalized)) return true;
504
+ [
505
+ "false",
506
+ "0",
507
+ "no",
508
+ "n",
509
+ "off"
510
+ ].includes(normalized);
511
+ }
512
+ return false;
513
+ }
514
+ function getPositionalArg(args, index) {
515
+ const value = args[index];
516
+ if ("string" != typeof value) return;
517
+ const trimmed = value.trim();
518
+ if (!trimmed || trimmed.startsWith("--")) return;
519
+ return trimmed;
520
+ }
521
+ function normalizePathForConfig(pathValue) {
522
+ if ((0, external_node_path_namespaceObject.isAbsolute)(pathValue)) return pathValue;
523
+ return pathValue.split("\\").join("/");
524
+ }
525
+ function resolveProfilePath(workspace, profilePath) {
526
+ if ((0, external_node_path_namespaceObject.isAbsolute)(profilePath)) return profilePath;
527
+ return (0, external_node_path_namespaceObject.resolve)(workspace, profilePath);
528
+ }
529
+ function resolveBundledExtensionSourcePath() {
530
+ const bundledPath = (0, external_node_path_namespaceObject.resolve)((0, external_node_url_namespaceObject.fileURLToPath)(new URL(BUNDLED_EXTENSION_RELATIVE_PATH, __rslib_import_meta_url__)));
531
+ const bundledManifest = (0, external_node_path_namespaceObject.join)(bundledPath, "manifest.json");
532
+ if ((0, external_node_fs_namespaceObject.existsSync)(bundledManifest)) return bundledPath;
533
+ throw new Error("Bundled Wingman extension assets were not found. Reinstall Wingman or provide --source <dir>.");
534
+ }
535
+ function createRelayToken() {
536
+ return (0, external_node_crypto_namespaceObject.randomBytes)(24).toString("base64url");
537
+ }
538
+ function resolveExtensionArgs(workspace, browserConfig, extensionIds) {
539
+ if (0 === extensionIds.length) return [
540
+ "--disable-extensions"
541
+ ];
542
+ const extensions = readStringRecord(browserConfig.extensions);
543
+ const dirs = extensionIds.map((extensionId)=>{
544
+ const configuredPath = extensions[extensionId];
545
+ if (!configuredPath) throw new Error(`Extension "${extensionId}" is not configured in browser.extensions.`);
546
+ const absolutePath = resolveProfilePath(workspace, configuredPath);
547
+ if (!(0, external_node_fs_namespaceObject.existsSync)(absolutePath)) throw new Error(`Configured extension path does not exist: ${absolutePath}.`);
548
+ return absolutePath;
549
+ });
550
+ const joined = dirs.join(",");
551
+ return [
552
+ `--disable-extensions-except=${joined}`,
553
+ `--load-extension=${joined}`
554
+ ];
555
+ }
556
+ function getChromeCandidates() {
557
+ const candidates = [
558
+ process.env.WINGMAN_CHROME_EXECUTABLE,
559
+ "google-chrome",
560
+ "chromium-browser",
561
+ "chromium"
562
+ ].filter((candidate)=>Boolean(candidate?.trim()));
563
+ if ("darwin" === process.platform) candidates.push("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "/Applications/Chromium.app/Contents/MacOS/Chromium");
564
+ if ("linux" === process.platform) candidates.push("/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium-browser", "/usr/bin/chromium");
565
+ if ("win32" === process.platform) candidates.push("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe");
566
+ return candidates;
567
+ }
568
+ function resolveBinaryFromPath(binaryName) {
569
+ const locator = "win32" === process.platform ? "where" : "which";
570
+ const result = (0, external_node_child_process_namespaceObject.spawnSync)(locator, [
571
+ binaryName
572
+ ], {
573
+ encoding: "utf-8"
574
+ });
575
+ if (0 !== result.status || !result.stdout) return null;
576
+ const firstLine = result.stdout.split(/\r?\n/).map((line)=>line.trim()).find(Boolean);
577
+ return firstLine || null;
578
+ }
579
+ function resolveChromeExecutablePath(explicitPath) {
580
+ const candidatePool = explicitPath?.trim() ? [
581
+ explicitPath.trim()
582
+ ] : getChromeCandidates();
583
+ for (const candidate of candidatePool)if (candidate) {
584
+ if ((0, external_node_path_namespaceObject.isAbsolute)(candidate) && (0, external_node_fs_namespaceObject.existsSync)(candidate)) return candidate;
585
+ if (!(0, external_node_path_namespaceObject.isAbsolute)(candidate)) {
586
+ const fromPath = resolveBinaryFromPath(candidate);
587
+ if (fromPath) return fromPath;
588
+ }
589
+ }
590
+ if (explicitPath?.trim()) throw new Error(`Chrome executable not found at "${explicitPath}". Provide a valid executable path.`);
591
+ throw new Error("No Chrome/Chromium executable found. Install Chrome/Chromium or set WINGMAN_CHROME_EXECUTABLE.");
592
+ }
593
+ function writeLine(outputManager, message) {
594
+ if ("interactive" === outputManager.getMode()) console.log(message);
595
+ else outputManager.emitLog("info", message);
596
+ }
597
+ exports.executeBrowserCommand = __webpack_exports__.executeBrowserCommand;
598
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
599
+ "executeBrowserCommand"
600
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
601
+ Object.defineProperty(exports, '__esModule', {
602
+ value: true
603
+ });
@@ -0,0 +1,13 @@
1
+ import { type SpawnOptions } from "node:child_process";
2
+ import type { BrowserCommandArgs } from "../types/browser.js";
3
+ type BrowserSpawn = (command: string, args: readonly string[], options: SpawnOptions) => {
4
+ unref: () => void;
5
+ };
6
+ export interface BrowserCommandOptions {
7
+ workspace?: string;
8
+ configDir?: string;
9
+ spawnProcess?: BrowserSpawn;
10
+ resolveExecutablePath?: (explicitPath?: string) => string;
11
+ }
12
+ export declare function executeBrowserCommand(args: BrowserCommandArgs, options?: BrowserCommandOptions): Promise<void>;
13
+ export {};