@h-rig/browser-plugin 0.0.6-alpha.156

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # @h-rig/browser-plugin
@@ -0,0 +1,16 @@
1
+ import type { RuntimeBrowserContext, TaskBrowserConfig } from "@rig/contracts";
2
+ export declare const DEFAULT_BROWSER_ATTACH_URL = "http://127.0.0.1:9333";
3
+ export declare const DEFAULT_BROWSER_MODE = "persistent";
4
+ export declare const RUNTIME_BROWSER_HELPERS: {
5
+ readonly launch: "rig-browser-launch";
6
+ readonly check: "rig-browser-check";
7
+ readonly attachInfo: "rig-browser-attach-info";
8
+ readonly e2e: "rig-browser-e2e";
9
+ readonly resetProfile: "rig-browser-reset-profile";
10
+ };
11
+ export declare function resolveBrowserStateDir(projectRoot: string | undefined, configuredStateDir: string | undefined): string;
12
+ export declare function resolveTaskBrowserContext(browser: TaskBrowserConfig | undefined, options?: {
13
+ hostProjectRoot?: string;
14
+ runtimeId?: string;
15
+ }): RuntimeBrowserContext | undefined;
16
+ export declare function buildBrowserGuidanceLines(browser: RuntimeBrowserContext): string[];
@@ -0,0 +1,101 @@
1
+ // @bun
2
+ // packages/browser-plugin/src/browser-contract.ts
3
+ import { resolve } from "path";
4
+ var DEFAULT_BROWSER_ATTACH_URL = "http://127.0.0.1:9333";
5
+ var DEFAULT_BROWSER_MODE = "persistent";
6
+ var RUNTIME_BROWSER_HELPERS = {
7
+ launch: "rig-browser-launch",
8
+ check: "rig-browser-check",
9
+ attachInfo: "rig-browser-attach-info",
10
+ e2e: "rig-browser-e2e",
11
+ resetProfile: "rig-browser-reset-profile"
12
+ };
13
+ var BASE_REMOTE_DEBUGGING_PORT = 9222;
14
+ var REMOTE_DEBUGGING_PORT_SPREAD = 4000;
15
+ function hashString(input) {
16
+ let hash = 0;
17
+ for (let index = 0;index < input.length; index += 1) {
18
+ hash = (hash << 5) - hash + input.charCodeAt(index) | 0;
19
+ }
20
+ return Math.abs(hash);
21
+ }
22
+ function derivePortFromProfile(profileName) {
23
+ return BASE_REMOTE_DEBUGGING_PORT + hashString(profileName) % REMOTE_DEBUGGING_PORT_SPREAD;
24
+ }
25
+ function parseAttachUrl(attachUrl) {
26
+ try {
27
+ return new URL((attachUrl || DEFAULT_BROWSER_ATTACH_URL).trim() || DEFAULT_BROWSER_ATTACH_URL);
28
+ } catch {
29
+ return new URL(DEFAULT_BROWSER_ATTACH_URL);
30
+ }
31
+ }
32
+ function sanitizeRuntimeSuffix(runtimeId) {
33
+ return runtimeId.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 16) || "runtime";
34
+ }
35
+ function resolveBrowserStateDir(projectRoot, configuredStateDir) {
36
+ const trimmed = configuredStateDir?.trim() || ".tmp/rig-browser";
37
+ if (trimmed.startsWith("/")) {
38
+ return resolve(trimmed);
39
+ }
40
+ return resolve(projectRoot || process.cwd(), trimmed);
41
+ }
42
+ function resolveTaskBrowserContext(browser, options = {}) {
43
+ if (!browser?.required) {
44
+ return;
45
+ }
46
+ const defaultProfile = browser.profile?.trim() || "default";
47
+ const mode = browser.mode?.trim() || DEFAULT_BROWSER_MODE;
48
+ const defaultAttach = parseAttachUrl(browser.attach_url);
49
+ const shouldDeriveRuntimeProfile = Boolean(options.runtimeId?.trim()) && mode !== "shared";
50
+ const effectiveProfile = shouldDeriveRuntimeProfile ? `${defaultProfile}-${sanitizeRuntimeSuffix(options.runtimeId.trim())}` : defaultProfile;
51
+ const effectivePort = shouldDeriveRuntimeProfile ? derivePortFromProfile(effectiveProfile) : Number(defaultAttach.port || "80");
52
+ const effectiveAttach = new URL(defaultAttach.toString());
53
+ effectiveAttach.port = String(effectivePort);
54
+ return {
55
+ required: true,
56
+ preset: browser.preset?.trim() || "default",
57
+ mode,
58
+ stateDir: resolveBrowserStateDir(options.hostProjectRoot, browser.state_dir),
59
+ defaultProfile,
60
+ effectiveProfile,
61
+ defaultAttachUrl: defaultAttach.toString(),
62
+ effectiveAttachUrl: effectiveAttach.toString(),
63
+ ...browser.dev_command?.trim() ? { devCommand: browser.dev_command.trim() } : {},
64
+ ...browser.launch_command?.trim() ? { launchCommand: browser.launch_command.trim() } : {},
65
+ ...browser.check_command?.trim() ? { checkCommand: browser.check_command.trim() } : {},
66
+ ...browser.e2e_command?.trim() ? { e2eCommand: browser.e2e_command.trim() } : {},
67
+ launchHelper: RUNTIME_BROWSER_HELPERS.launch,
68
+ checkHelper: RUNTIME_BROWSER_HELPERS.check,
69
+ attachInfoHelper: RUNTIME_BROWSER_HELPERS.attachInfo,
70
+ e2eHelper: RUNTIME_BROWSER_HELPERS.e2e,
71
+ resetProfileHelper: RUNTIME_BROWSER_HELPERS.resetProfile
72
+ };
73
+ }
74
+ function buildBrowserGuidanceLines(browser) {
75
+ const lines = [
76
+ "This task requires Rig Browser.",
77
+ `Launch the browser: \`${browser.launchHelper}\`${browser.devCommand ? " or `rig-browser-launch --dev`" : ""}.`,
78
+ `Check the browser contract: \`${browser.checkHelper}\`.`,
79
+ `Show attach details: \`${browser.attachInfoHelper}\`.`,
80
+ `Attach Chrome DevTools MCP to ${browser.effectiveAttachUrl}.`,
81
+ `Preset: ${browser.preset}.`,
82
+ `Profile: ${browser.effectiveProfile}.`,
83
+ `State dir: ${browser.stateDir}.`,
84
+ `Reset the active profile with \`${browser.resetProfileHelper}\`.`
85
+ ];
86
+ if (browser.e2eCommand) {
87
+ lines.push(`Run app-owned browser e2e with \`${browser.e2eHelper}\`.`);
88
+ }
89
+ if (browser.defaultProfile !== browser.effectiveProfile) {
90
+ lines.push(`Base profile: ${browser.defaultProfile}. Runtime launches derive an isolated effective profile.`);
91
+ }
92
+ return lines;
93
+ }
94
+ export {
95
+ resolveTaskBrowserContext,
96
+ resolveBrowserStateDir,
97
+ buildBrowserGuidanceLines,
98
+ RUNTIME_BROWSER_HELPERS,
99
+ DEFAULT_BROWSER_MODE,
100
+ DEFAULT_BROWSER_ATTACH_URL
101
+ };
@@ -0,0 +1,4 @@
1
+ export * from "./browser-contract";
2
+ export { BROWSER_PLUGIN_NAME, browserPlugin, createBrowserPlugin, } from "./plugin";
3
+ export { browserContractService, svc } from "./service";
4
+ export { default } from "./plugin";
@@ -0,0 +1,167 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
+
18
+ // packages/browser-plugin/src/browser-contract.ts
19
+ import { resolve } from "path";
20
+ function hashString(input) {
21
+ let hash = 0;
22
+ for (let index = 0;index < input.length; index += 1) {
23
+ hash = (hash << 5) - hash + input.charCodeAt(index) | 0;
24
+ }
25
+ return Math.abs(hash);
26
+ }
27
+ function derivePortFromProfile(profileName) {
28
+ return BASE_REMOTE_DEBUGGING_PORT + hashString(profileName) % REMOTE_DEBUGGING_PORT_SPREAD;
29
+ }
30
+ function parseAttachUrl(attachUrl) {
31
+ try {
32
+ return new URL((attachUrl || DEFAULT_BROWSER_ATTACH_URL).trim() || DEFAULT_BROWSER_ATTACH_URL);
33
+ } catch {
34
+ return new URL(DEFAULT_BROWSER_ATTACH_URL);
35
+ }
36
+ }
37
+ function sanitizeRuntimeSuffix(runtimeId) {
38
+ return runtimeId.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 16) || "runtime";
39
+ }
40
+ function resolveBrowserStateDir(projectRoot, configuredStateDir) {
41
+ const trimmed = configuredStateDir?.trim() || ".tmp/rig-browser";
42
+ if (trimmed.startsWith("/")) {
43
+ return resolve(trimmed);
44
+ }
45
+ return resolve(projectRoot || process.cwd(), trimmed);
46
+ }
47
+ function resolveTaskBrowserContext(browser, options = {}) {
48
+ if (!browser?.required) {
49
+ return;
50
+ }
51
+ const defaultProfile = browser.profile?.trim() || "default";
52
+ const mode = browser.mode?.trim() || DEFAULT_BROWSER_MODE;
53
+ const defaultAttach = parseAttachUrl(browser.attach_url);
54
+ const shouldDeriveRuntimeProfile = Boolean(options.runtimeId?.trim()) && mode !== "shared";
55
+ const effectiveProfile = shouldDeriveRuntimeProfile ? `${defaultProfile}-${sanitizeRuntimeSuffix(options.runtimeId.trim())}` : defaultProfile;
56
+ const effectivePort = shouldDeriveRuntimeProfile ? derivePortFromProfile(effectiveProfile) : Number(defaultAttach.port || "80");
57
+ const effectiveAttach = new URL(defaultAttach.toString());
58
+ effectiveAttach.port = String(effectivePort);
59
+ return {
60
+ required: true,
61
+ preset: browser.preset?.trim() || "default",
62
+ mode,
63
+ stateDir: resolveBrowserStateDir(options.hostProjectRoot, browser.state_dir),
64
+ defaultProfile,
65
+ effectiveProfile,
66
+ defaultAttachUrl: defaultAttach.toString(),
67
+ effectiveAttachUrl: effectiveAttach.toString(),
68
+ ...browser.dev_command?.trim() ? { devCommand: browser.dev_command.trim() } : {},
69
+ ...browser.launch_command?.trim() ? { launchCommand: browser.launch_command.trim() } : {},
70
+ ...browser.check_command?.trim() ? { checkCommand: browser.check_command.trim() } : {},
71
+ ...browser.e2e_command?.trim() ? { e2eCommand: browser.e2e_command.trim() } : {},
72
+ launchHelper: RUNTIME_BROWSER_HELPERS.launch,
73
+ checkHelper: RUNTIME_BROWSER_HELPERS.check,
74
+ attachInfoHelper: RUNTIME_BROWSER_HELPERS.attachInfo,
75
+ e2eHelper: RUNTIME_BROWSER_HELPERS.e2e,
76
+ resetProfileHelper: RUNTIME_BROWSER_HELPERS.resetProfile
77
+ };
78
+ }
79
+ function buildBrowserGuidanceLines(browser) {
80
+ const lines = [
81
+ "This task requires Rig Browser.",
82
+ `Launch the browser: \`${browser.launchHelper}\`${browser.devCommand ? " or `rig-browser-launch --dev`" : ""}.`,
83
+ `Check the browser contract: \`${browser.checkHelper}\`.`,
84
+ `Show attach details: \`${browser.attachInfoHelper}\`.`,
85
+ `Attach Chrome DevTools MCP to ${browser.effectiveAttachUrl}.`,
86
+ `Preset: ${browser.preset}.`,
87
+ `Profile: ${browser.effectiveProfile}.`,
88
+ `State dir: ${browser.stateDir}.`,
89
+ `Reset the active profile with \`${browser.resetProfileHelper}\`.`
90
+ ];
91
+ if (browser.e2eCommand) {
92
+ lines.push(`Run app-owned browser e2e with \`${browser.e2eHelper}\`.`);
93
+ }
94
+ if (browser.defaultProfile !== browser.effectiveProfile) {
95
+ lines.push(`Base profile: ${browser.defaultProfile}. Runtime launches derive an isolated effective profile.`);
96
+ }
97
+ return lines;
98
+ }
99
+ var DEFAULT_BROWSER_ATTACH_URL = "http://127.0.0.1:9333", DEFAULT_BROWSER_MODE = "persistent", RUNTIME_BROWSER_HELPERS, BASE_REMOTE_DEBUGGING_PORT = 9222, REMOTE_DEBUGGING_PORT_SPREAD = 4000;
100
+ var init_browser_contract = __esm(() => {
101
+ RUNTIME_BROWSER_HELPERS = {
102
+ launch: "rig-browser-launch",
103
+ check: "rig-browser-check",
104
+ attachInfo: "rig-browser-attach-info",
105
+ e2e: "rig-browser-e2e",
106
+ resetProfile: "rig-browser-reset-profile"
107
+ };
108
+ });
109
+
110
+ // packages/browser-plugin/src/service.ts
111
+ var exports_service = {};
112
+ __export(exports_service, {
113
+ svc: () => svc,
114
+ browserContractService: () => browserContractService
115
+ });
116
+ var svc, browserContractService;
117
+ var init_service = __esm(() => {
118
+ init_browser_contract();
119
+ svc = {
120
+ resolveTaskBrowserContext,
121
+ buildBrowserGuidanceLines
122
+ };
123
+ browserContractService = svc;
124
+ });
125
+
126
+ // packages/browser-plugin/src/index.ts
127
+ init_browser_contract();
128
+
129
+ // packages/browser-plugin/src/plugin.ts
130
+ import { definePlugin } from "@rig/core/config";
131
+ import { BROWSER_CONTRACT_SERVICE_CAPABILITY_ID } from "@rig/contracts";
132
+ var BROWSER_PLUGIN_NAME = "@rig/browser-plugin";
133
+ var browserPlugin = definePlugin({
134
+ name: BROWSER_PLUGIN_NAME,
135
+ version: "0.0.0-alpha.1",
136
+ contributes: {
137
+ capabilities: [
138
+ {
139
+ id: BROWSER_CONTRACT_SERVICE_CAPABILITY_ID,
140
+ title: "Browser-required task contract",
141
+ description: "Resolve a task's browser config into the runtime-effective browser context and render task-info browser guidance.",
142
+ run: async () => (await Promise.resolve().then(() => (init_service(), exports_service))).svc
143
+ }
144
+ ]
145
+ }
146
+ });
147
+ function createBrowserPlugin() {
148
+ return browserPlugin;
149
+ }
150
+ var plugin_default = browserPlugin;
151
+
152
+ // packages/browser-plugin/src/index.ts
153
+ init_service();
154
+ export {
155
+ svc,
156
+ resolveTaskBrowserContext,
157
+ resolveBrowserStateDir,
158
+ plugin_default as default,
159
+ createBrowserPlugin,
160
+ buildBrowserGuidanceLines,
161
+ browserPlugin,
162
+ browserContractService,
163
+ RUNTIME_BROWSER_HELPERS,
164
+ DEFAULT_BROWSER_MODE,
165
+ DEFAULT_BROWSER_ATTACH_URL,
166
+ BROWSER_PLUGIN_NAME
167
+ };
@@ -0,0 +1,4 @@
1
+ export declare const BROWSER_PLUGIN_NAME = "@rig/browser-plugin";
2
+ export declare const browserPlugin: import("@rig/core").RigPlugin;
3
+ export declare function createBrowserPlugin(): import("@rig/core").RigPlugin;
4
+ export default browserPlugin;
@@ -0,0 +1,153 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
+
18
+ // packages/browser-plugin/src/browser-contract.ts
19
+ import { resolve } from "path";
20
+ function hashString(input) {
21
+ let hash = 0;
22
+ for (let index = 0;index < input.length; index += 1) {
23
+ hash = (hash << 5) - hash + input.charCodeAt(index) | 0;
24
+ }
25
+ return Math.abs(hash);
26
+ }
27
+ function derivePortFromProfile(profileName) {
28
+ return BASE_REMOTE_DEBUGGING_PORT + hashString(profileName) % REMOTE_DEBUGGING_PORT_SPREAD;
29
+ }
30
+ function parseAttachUrl(attachUrl) {
31
+ try {
32
+ return new URL((attachUrl || DEFAULT_BROWSER_ATTACH_URL).trim() || DEFAULT_BROWSER_ATTACH_URL);
33
+ } catch {
34
+ return new URL(DEFAULT_BROWSER_ATTACH_URL);
35
+ }
36
+ }
37
+ function sanitizeRuntimeSuffix(runtimeId) {
38
+ return runtimeId.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 16) || "runtime";
39
+ }
40
+ function resolveBrowserStateDir(projectRoot, configuredStateDir) {
41
+ const trimmed = configuredStateDir?.trim() || ".tmp/rig-browser";
42
+ if (trimmed.startsWith("/")) {
43
+ return resolve(trimmed);
44
+ }
45
+ return resolve(projectRoot || process.cwd(), trimmed);
46
+ }
47
+ function resolveTaskBrowserContext(browser, options = {}) {
48
+ if (!browser?.required) {
49
+ return;
50
+ }
51
+ const defaultProfile = browser.profile?.trim() || "default";
52
+ const mode = browser.mode?.trim() || DEFAULT_BROWSER_MODE;
53
+ const defaultAttach = parseAttachUrl(browser.attach_url);
54
+ const shouldDeriveRuntimeProfile = Boolean(options.runtimeId?.trim()) && mode !== "shared";
55
+ const effectiveProfile = shouldDeriveRuntimeProfile ? `${defaultProfile}-${sanitizeRuntimeSuffix(options.runtimeId.trim())}` : defaultProfile;
56
+ const effectivePort = shouldDeriveRuntimeProfile ? derivePortFromProfile(effectiveProfile) : Number(defaultAttach.port || "80");
57
+ const effectiveAttach = new URL(defaultAttach.toString());
58
+ effectiveAttach.port = String(effectivePort);
59
+ return {
60
+ required: true,
61
+ preset: browser.preset?.trim() || "default",
62
+ mode,
63
+ stateDir: resolveBrowserStateDir(options.hostProjectRoot, browser.state_dir),
64
+ defaultProfile,
65
+ effectiveProfile,
66
+ defaultAttachUrl: defaultAttach.toString(),
67
+ effectiveAttachUrl: effectiveAttach.toString(),
68
+ ...browser.dev_command?.trim() ? { devCommand: browser.dev_command.trim() } : {},
69
+ ...browser.launch_command?.trim() ? { launchCommand: browser.launch_command.trim() } : {},
70
+ ...browser.check_command?.trim() ? { checkCommand: browser.check_command.trim() } : {},
71
+ ...browser.e2e_command?.trim() ? { e2eCommand: browser.e2e_command.trim() } : {},
72
+ launchHelper: RUNTIME_BROWSER_HELPERS.launch,
73
+ checkHelper: RUNTIME_BROWSER_HELPERS.check,
74
+ attachInfoHelper: RUNTIME_BROWSER_HELPERS.attachInfo,
75
+ e2eHelper: RUNTIME_BROWSER_HELPERS.e2e,
76
+ resetProfileHelper: RUNTIME_BROWSER_HELPERS.resetProfile
77
+ };
78
+ }
79
+ function buildBrowserGuidanceLines(browser) {
80
+ const lines = [
81
+ "This task requires Rig Browser.",
82
+ `Launch the browser: \`${browser.launchHelper}\`${browser.devCommand ? " or `rig-browser-launch --dev`" : ""}.`,
83
+ `Check the browser contract: \`${browser.checkHelper}\`.`,
84
+ `Show attach details: \`${browser.attachInfoHelper}\`.`,
85
+ `Attach Chrome DevTools MCP to ${browser.effectiveAttachUrl}.`,
86
+ `Preset: ${browser.preset}.`,
87
+ `Profile: ${browser.effectiveProfile}.`,
88
+ `State dir: ${browser.stateDir}.`,
89
+ `Reset the active profile with \`${browser.resetProfileHelper}\`.`
90
+ ];
91
+ if (browser.e2eCommand) {
92
+ lines.push(`Run app-owned browser e2e with \`${browser.e2eHelper}\`.`);
93
+ }
94
+ if (browser.defaultProfile !== browser.effectiveProfile) {
95
+ lines.push(`Base profile: ${browser.defaultProfile}. Runtime launches derive an isolated effective profile.`);
96
+ }
97
+ return lines;
98
+ }
99
+ var DEFAULT_BROWSER_ATTACH_URL = "http://127.0.0.1:9333", DEFAULT_BROWSER_MODE = "persistent", RUNTIME_BROWSER_HELPERS, BASE_REMOTE_DEBUGGING_PORT = 9222, REMOTE_DEBUGGING_PORT_SPREAD = 4000;
100
+ var init_browser_contract = __esm(() => {
101
+ RUNTIME_BROWSER_HELPERS = {
102
+ launch: "rig-browser-launch",
103
+ check: "rig-browser-check",
104
+ attachInfo: "rig-browser-attach-info",
105
+ e2e: "rig-browser-e2e",
106
+ resetProfile: "rig-browser-reset-profile"
107
+ };
108
+ });
109
+
110
+ // packages/browser-plugin/src/service.ts
111
+ var exports_service = {};
112
+ __export(exports_service, {
113
+ svc: () => svc,
114
+ browserContractService: () => browserContractService
115
+ });
116
+ var svc, browserContractService;
117
+ var init_service = __esm(() => {
118
+ init_browser_contract();
119
+ svc = {
120
+ resolveTaskBrowserContext,
121
+ buildBrowserGuidanceLines
122
+ };
123
+ browserContractService = svc;
124
+ });
125
+
126
+ // packages/browser-plugin/src/plugin.ts
127
+ import { definePlugin } from "@rig/core/config";
128
+ import { BROWSER_CONTRACT_SERVICE_CAPABILITY_ID } from "@rig/contracts";
129
+ var BROWSER_PLUGIN_NAME = "@rig/browser-plugin";
130
+ var browserPlugin = definePlugin({
131
+ name: BROWSER_PLUGIN_NAME,
132
+ version: "0.0.0-alpha.1",
133
+ contributes: {
134
+ capabilities: [
135
+ {
136
+ id: BROWSER_CONTRACT_SERVICE_CAPABILITY_ID,
137
+ title: "Browser-required task contract",
138
+ description: "Resolve a task's browser config into the runtime-effective browser context and render task-info browser guidance.",
139
+ run: async () => (await Promise.resolve().then(() => (init_service(), exports_service))).svc
140
+ }
141
+ ]
142
+ }
143
+ });
144
+ function createBrowserPlugin() {
145
+ return browserPlugin;
146
+ }
147
+ var plugin_default = browserPlugin;
148
+ export {
149
+ plugin_default as default,
150
+ createBrowserPlugin,
151
+ browserPlugin,
152
+ BROWSER_PLUGIN_NAME
153
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * service.ts — the concrete browser-contract service the runtime port resolves
3
+ * and consumes.
4
+ *
5
+ * CONFIG-LIGHT: this module top-level-imports the browser-contract impl. It is
6
+ * loaded LAZILY by the capability `run()` in plugin.ts
7
+ * (`(await import("./service")).svc`), so merely evaluating rig.config.ts never
8
+ * drags the browser-contract impl into scope.
9
+ */
10
+ import type { BrowserContractService } from "@rig/runtime/control-plane/browser-contract-port";
11
+ /** The concrete browser-contract service the runtime port resolves and consumes. */
12
+ export declare const svc: BrowserContractService;
13
+ /** Back-compat alias. */
14
+ export declare const browserContractService: BrowserContractService;
@@ -0,0 +1,104 @@
1
+ // @bun
2
+ // packages/browser-plugin/src/browser-contract.ts
3
+ import { resolve } from "path";
4
+ var DEFAULT_BROWSER_ATTACH_URL = "http://127.0.0.1:9333";
5
+ var DEFAULT_BROWSER_MODE = "persistent";
6
+ var RUNTIME_BROWSER_HELPERS = {
7
+ launch: "rig-browser-launch",
8
+ check: "rig-browser-check",
9
+ attachInfo: "rig-browser-attach-info",
10
+ e2e: "rig-browser-e2e",
11
+ resetProfile: "rig-browser-reset-profile"
12
+ };
13
+ var BASE_REMOTE_DEBUGGING_PORT = 9222;
14
+ var REMOTE_DEBUGGING_PORT_SPREAD = 4000;
15
+ function hashString(input) {
16
+ let hash = 0;
17
+ for (let index = 0;index < input.length; index += 1) {
18
+ hash = (hash << 5) - hash + input.charCodeAt(index) | 0;
19
+ }
20
+ return Math.abs(hash);
21
+ }
22
+ function derivePortFromProfile(profileName) {
23
+ return BASE_REMOTE_DEBUGGING_PORT + hashString(profileName) % REMOTE_DEBUGGING_PORT_SPREAD;
24
+ }
25
+ function parseAttachUrl(attachUrl) {
26
+ try {
27
+ return new URL((attachUrl || DEFAULT_BROWSER_ATTACH_URL).trim() || DEFAULT_BROWSER_ATTACH_URL);
28
+ } catch {
29
+ return new URL(DEFAULT_BROWSER_ATTACH_URL);
30
+ }
31
+ }
32
+ function sanitizeRuntimeSuffix(runtimeId) {
33
+ return runtimeId.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 16) || "runtime";
34
+ }
35
+ function resolveBrowserStateDir(projectRoot, configuredStateDir) {
36
+ const trimmed = configuredStateDir?.trim() || ".tmp/rig-browser";
37
+ if (trimmed.startsWith("/")) {
38
+ return resolve(trimmed);
39
+ }
40
+ return resolve(projectRoot || process.cwd(), trimmed);
41
+ }
42
+ function resolveTaskBrowserContext(browser, options = {}) {
43
+ if (!browser?.required) {
44
+ return;
45
+ }
46
+ const defaultProfile = browser.profile?.trim() || "default";
47
+ const mode = browser.mode?.trim() || DEFAULT_BROWSER_MODE;
48
+ const defaultAttach = parseAttachUrl(browser.attach_url);
49
+ const shouldDeriveRuntimeProfile = Boolean(options.runtimeId?.trim()) && mode !== "shared";
50
+ const effectiveProfile = shouldDeriveRuntimeProfile ? `${defaultProfile}-${sanitizeRuntimeSuffix(options.runtimeId.trim())}` : defaultProfile;
51
+ const effectivePort = shouldDeriveRuntimeProfile ? derivePortFromProfile(effectiveProfile) : Number(defaultAttach.port || "80");
52
+ const effectiveAttach = new URL(defaultAttach.toString());
53
+ effectiveAttach.port = String(effectivePort);
54
+ return {
55
+ required: true,
56
+ preset: browser.preset?.trim() || "default",
57
+ mode,
58
+ stateDir: resolveBrowserStateDir(options.hostProjectRoot, browser.state_dir),
59
+ defaultProfile,
60
+ effectiveProfile,
61
+ defaultAttachUrl: defaultAttach.toString(),
62
+ effectiveAttachUrl: effectiveAttach.toString(),
63
+ ...browser.dev_command?.trim() ? { devCommand: browser.dev_command.trim() } : {},
64
+ ...browser.launch_command?.trim() ? { launchCommand: browser.launch_command.trim() } : {},
65
+ ...browser.check_command?.trim() ? { checkCommand: browser.check_command.trim() } : {},
66
+ ...browser.e2e_command?.trim() ? { e2eCommand: browser.e2e_command.trim() } : {},
67
+ launchHelper: RUNTIME_BROWSER_HELPERS.launch,
68
+ checkHelper: RUNTIME_BROWSER_HELPERS.check,
69
+ attachInfoHelper: RUNTIME_BROWSER_HELPERS.attachInfo,
70
+ e2eHelper: RUNTIME_BROWSER_HELPERS.e2e,
71
+ resetProfileHelper: RUNTIME_BROWSER_HELPERS.resetProfile
72
+ };
73
+ }
74
+ function buildBrowserGuidanceLines(browser) {
75
+ const lines = [
76
+ "This task requires Rig Browser.",
77
+ `Launch the browser: \`${browser.launchHelper}\`${browser.devCommand ? " or `rig-browser-launch --dev`" : ""}.`,
78
+ `Check the browser contract: \`${browser.checkHelper}\`.`,
79
+ `Show attach details: \`${browser.attachInfoHelper}\`.`,
80
+ `Attach Chrome DevTools MCP to ${browser.effectiveAttachUrl}.`,
81
+ `Preset: ${browser.preset}.`,
82
+ `Profile: ${browser.effectiveProfile}.`,
83
+ `State dir: ${browser.stateDir}.`,
84
+ `Reset the active profile with \`${browser.resetProfileHelper}\`.`
85
+ ];
86
+ if (browser.e2eCommand) {
87
+ lines.push(`Run app-owned browser e2e with \`${browser.e2eHelper}\`.`);
88
+ }
89
+ if (browser.defaultProfile !== browser.effectiveProfile) {
90
+ lines.push(`Base profile: ${browser.defaultProfile}. Runtime launches derive an isolated effective profile.`);
91
+ }
92
+ return lines;
93
+ }
94
+
95
+ // packages/browser-plugin/src/service.ts
96
+ var svc = {
97
+ resolveTaskBrowserContext,
98
+ buildBrowserGuidanceLines
99
+ };
100
+ var browserContractService = svc;
101
+ export {
102
+ svc,
103
+ browserContractService
104
+ };
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@h-rig/browser-plugin",
3
+ "version": "0.0.6-alpha.156",
4
+ "type": "module",
5
+ "description": "First-party browser-required task contract capability plugin for Rig.",
6
+ "license": "UNLICENSED",
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/src/plugin.d.ts",
14
+ "import": "./dist/src/plugin.js"
15
+ },
16
+ "./plugin": {
17
+ "types": "./dist/src/plugin.d.ts",
18
+ "import": "./dist/src/plugin.js"
19
+ },
20
+ "./index": {
21
+ "types": "./dist/src/index.d.ts",
22
+ "import": "./dist/src/index.js"
23
+ }
24
+ },
25
+ "engines": {
26
+ "bun": ">=1.3.11"
27
+ },
28
+ "main": "./dist/src/index.js",
29
+ "module": "./dist/src/index.js",
30
+ "types": "./dist/src/index.d.ts",
31
+ "dependencies": {
32
+ "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.156",
33
+ "@rig/core": "npm:@h-rig/core@0.0.6-alpha.156",
34
+ "@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.156"
35
+ }
36
+ }