@deeeed/metamask-harness 0.80.0 → 0.81.0
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/CHANGELOG.md +26 -0
- package/adapters/manifest.json +14 -30
- package/adapters/terminal/launch.mjs +17 -13
- package/adapters/terminal/lib/mainnet-guard.mjs +9 -0
- package/adapters/terminal/lib/readiness.mjs +20 -12
- package/adapters/terminal/policy.mjs +35 -0
- package/adapters/terminal/verify.mjs +10 -3
- package/adapters/terminal/wallet-host.mjs +43 -225
- package/dist/adapters/core/actions.js +4 -1
- package/dist/adapters/extension/actions.js +6 -2
- package/dist/adapters/extension/network-observer.js +13 -0
- package/dist/adapters/extension/run.js +112 -5
- package/dist/adapters/mobile/actions.js +4 -1
- package/dist/adapters/mobile/network-observer.js +68 -0
- package/dist/adapters/mobile/performance-observer.js +1 -3
- package/dist/adapters/mobile/run.js +32 -9
- package/dist/adapters/perps-order-inputs.js +36 -0
- package/dist/adapters/proof-failure.js +11 -0
- package/dist/adapters/terminal/actions.js +4 -1
- package/dist/adapters/terminal/doctor-checks.js +2 -0
- package/dist/adapters/terminal/launch.js +3 -0
- package/dist/adapters/terminal/overlay.js +8 -1
- package/dist/adapters/terminal/run.js +8 -3
- package/dist/adapters/terminal/surface.js +2 -0
- package/dist/adapters.js +20 -4
- package/dist/cli.js +31 -6
- package/dist/commands/check.js +30 -6
- package/dist/commands/completion-candidates.js +5 -5
- package/dist/commands/self-test.js +4 -3
- package/dist/engine.js +57 -0
- package/dist/funding-execution-context.js +81 -0
- package/dist/mm-harness-cli.js +3 -3
- package/dist/performance-observation.js +1 -3
- package/dist/recipe-assistance-context.js +6 -7
- package/dist/runner.js +2 -1
- package/docs/CONTRIBUTING.md +4 -2
- package/docs/RECIPES.md +31 -3
- package/library/actions/core/perps/_controller.mjs +24 -28
- package/library/actions/core/perps/_rewards.mjs +186 -0
- package/library/actions/core/perps/close_positions.mjs +1 -1
- package/library/actions/core/perps/place_order.mjs +167 -45
- package/library/actions/core/perps/quote_fees.mjs +7 -4
- package/library/actions/core/wallet/list_accounts.mjs +1 -1
- package/library/actions/extension/perps/close_visible_position.mjs +5 -0
- package/library/actions/extension/perps/edit_margin.mjs +4 -0
- package/library/actions/extension/perps/mutation-receipt.mjs +95 -3
- package/library/actions/extension/perps/perps.mjs +320 -3
- package/library/actions/extension/perps/place_order.mjs +3 -1
- package/library/actions/extension/perps/update_position_tpsl.mjs +4 -0
- package/library/actions/extension/platform/cdp.mjs +71 -3
- package/library/actions/mobile/perps/perps.mjs +216 -11
- package/library/actions/mobile/perps/place_order.mjs +3 -1
- package/library/actions/shared/perps/order-policy.mjs +177 -0
- package/library/actions/{core/wallet/_fixture-accounts.mjs → shared/wallet/fixture-accounts.mjs} +18 -3
- package/library/actions/terminal/app/launch.mjs +3 -2
- package/library/actions/terminal/platform/hud.mjs +2 -1
- package/library/actions/terminal/platform/page.mjs +2 -1
- package/library/actions/terminal/wallet/_signature-log.mjs +40 -161
- package/library/actions/terminal/wallet/assert_signatures.mjs +6 -12
- package/library/actions/terminal/wallet/read_signatures.mjs +10 -9
- package/library/manifests/core.action-manifest.json +6 -2
- package/library/manifests/extension.action-manifest.json +4 -0
- package/library/manifests/mobile.action-manifest.json +12 -0
- package/package.json +4 -4
- package/site/assets/help-recipes.json +1 -0
- package/adapters/terminal/lib/origin.mjs +0 -30
- package/adapters/terminal/lib/page-script.mjs +0 -246
- package/adapters/terminal/lib/strict-wallet.mjs +0 -138
- package/dist/commands/call.js +0 -671
- package/dist/commands/list-executables.js +0 -245
- package/dist/commands/manifest.js +0 -477
- package/dist/commands/run-engine.js +0 -1301
- package/dist/commands/run.js +0 -730
- package/dist/discovery.js +0 -15
- package/dist/metamask-action-validation.js +0 -68
- package/dist/network-observation.js +0 -290
package/dist/discovery.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { assessRecipe } from "@farmslot/recipe-cli";
|
|
2
|
-
import {
|
|
3
|
-
loadRecipeLibraries
|
|
4
|
-
} from "@farmslot/recipe-runner";
|
|
5
|
-
import { resolveActionManifest } from "./manifest.js";
|
|
6
|
-
async function runnableLibraryRecipes(adapter, sources) {
|
|
7
|
-
const resolution = await loadRecipeLibraries(sources, { adapter });
|
|
8
|
-
const { manifest } = await resolveActionManifest(adapter, void 0, sources);
|
|
9
|
-
return [...resolution.recipes.values()].filter(
|
|
10
|
-
(recipe) => assessRecipe({ resolution, manifest }, recipe).length === 0
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
export {
|
|
14
|
-
runnableLibraryRecipes
|
|
15
|
-
};
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { adapterRegistry } from "./adapters/registry.js";
|
|
2
|
-
import { getAdapterSurface } from "./adapters/surface.js";
|
|
3
|
-
function isRecord(value) {
|
|
4
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5
|
-
}
|
|
6
|
-
function resolveMetaMaskParameterValue(value, params) {
|
|
7
|
-
if (typeof value === "string") {
|
|
8
|
-
const exact = /^\{\{params\.([A-Za-z0-9_.-]+)\}\}$/u.exec(value);
|
|
9
|
-
if (!exact) return value;
|
|
10
|
-
let current = params;
|
|
11
|
-
for (const segment of exact[1].split(".")) {
|
|
12
|
-
if (!isRecord(current) || !Object.hasOwn(current, segment)) return value;
|
|
13
|
-
current = current[segment];
|
|
14
|
-
}
|
|
15
|
-
return current;
|
|
16
|
-
}
|
|
17
|
-
if (Array.isArray(value)) return value.map((entry) => resolveMetaMaskParameterValue(entry, params));
|
|
18
|
-
if (!isRecord(value)) return value;
|
|
19
|
-
return Object.fromEntries(
|
|
20
|
-
Object.entries(value).map(([key, entry]) => [key, resolveMetaMaskParameterValue(entry, params)])
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
function validateMetaMaskActionInputs(recipe, adapter, params) {
|
|
24
|
-
if (!isRecord(recipe) || !isRecord(recipe.workflow)) return [];
|
|
25
|
-
const nodes = isRecord(recipe.workflow.nodes) ? recipe.workflow.nodes : void 0;
|
|
26
|
-
if (!nodes) return [];
|
|
27
|
-
const surface = adapter && adapterRegistry.has(adapter) ? getAdapterSurface(adapter) : void 0;
|
|
28
|
-
const findings = [];
|
|
29
|
-
for (const [nodeId, rawNode] of Object.entries(nodes)) {
|
|
30
|
-
const node = params ? resolveMetaMaskParameterValue(rawNode, params) : rawNode;
|
|
31
|
-
if (!isRecord(node)) continue;
|
|
32
|
-
const action = node.action;
|
|
33
|
-
findings.push(...surface?.actions.inputFindings?.(nodeId, node) ?? []);
|
|
34
|
-
const state = typeof node.state === "string" ? node.state.toLowerCase() : void 0;
|
|
35
|
-
const createsOrder = action === "metamask.perps.place_order" || !surface?.actions.ensureChecksOnly && (action === "metamask.perps.ensure_positions" || action === "metamask.perps.ensure_orders") && (state === "open" || state === "present");
|
|
36
|
-
if (!createsOrder) continue;
|
|
37
|
-
const requireOneOf = (names, message) => {
|
|
38
|
-
const present = names.some((name) => {
|
|
39
|
-
const value = node[name];
|
|
40
|
-
return value !== void 0 && value !== null && value !== "";
|
|
41
|
-
});
|
|
42
|
-
if (present) return;
|
|
43
|
-
findings.push({
|
|
44
|
-
severity: "error",
|
|
45
|
-
code: "recipe.missing_param",
|
|
46
|
-
path: `workflow.nodes.${nodeId}.${names[0]}`,
|
|
47
|
-
message
|
|
48
|
-
});
|
|
49
|
-
};
|
|
50
|
-
requireOneOf(
|
|
51
|
-
["market", "symbol"],
|
|
52
|
-
`${action} requires market=<symbol> (or symbol=<symbol>) before it may create an order`
|
|
53
|
-
);
|
|
54
|
-
requireOneOf(
|
|
55
|
-
["side"],
|
|
56
|
-
`${action} requires side=long or side=short before it may create an order`
|
|
57
|
-
);
|
|
58
|
-
requireOneOf(
|
|
59
|
-
["amount", "notional"],
|
|
60
|
-
`${action} requires amount=<usd> or notional=<usd> before it may create an order`
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
return findings;
|
|
64
|
-
}
|
|
65
|
-
export {
|
|
66
|
-
resolveMetaMaskParameterValue,
|
|
67
|
-
validateMetaMaskActionInputs
|
|
68
|
-
};
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import brokerModule from "@farmslot/recipe-runner/cdp-broker";
|
|
3
|
-
import configModule from "@farmslot/adapter-rn/bridge-runtime/lib/config.cjs";
|
|
4
|
-
import {
|
|
5
|
-
createExtensionNetworkObserver
|
|
6
|
-
} from "./adapters/extension/network-observer.js";
|
|
7
|
-
import { getAdapterSurface } from "./adapters/surface.js";
|
|
8
|
-
import {
|
|
9
|
-
indexArtifactManifest,
|
|
10
|
-
writeContainedArtifact
|
|
11
|
-
} from "@farmslot/recipe-cli/harness";
|
|
12
|
-
const AUTO_CAPTURE_ID = "run-network";
|
|
13
|
-
const AUTO_ARTIFACT_PATH = "network/run-summary.json";
|
|
14
|
-
const { brokerSocketPath, createBrokerClient } = brokerModule;
|
|
15
|
-
const { resolvePort } = configModule;
|
|
16
|
-
const sessions = /* @__PURE__ */ new Map();
|
|
17
|
-
async function startRunNetworkObservation(adapter, target, artifactsDir, env, ports = {}) {
|
|
18
|
-
const network = getAdapterSurface(adapter).observation?.network;
|
|
19
|
-
if (!network) return void 0;
|
|
20
|
-
const runtimeEnv = {
|
|
21
|
-
...env,
|
|
22
|
-
...ports.cdpPort ? { CDP_PORT: ports.cdpPort, RECIPE_CDP_PORT: ports.cdpPort } : {},
|
|
23
|
-
...ports.watcherPort ? { WATCHER_PORT: ports.watcherPort } : {}
|
|
24
|
-
};
|
|
25
|
-
const key = path.resolve(artifactsDir);
|
|
26
|
-
const session = {
|
|
27
|
-
artifactsDir: key,
|
|
28
|
-
autoStarted: false,
|
|
29
|
-
autoStartedAt: Date.now(),
|
|
30
|
-
nodeEvents: []
|
|
31
|
-
};
|
|
32
|
-
sessions.set(key, session);
|
|
33
|
-
try {
|
|
34
|
-
session.backend = await network.backend(target, runtimeEnv, key);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
session.setupError = boundedError(error);
|
|
37
|
-
}
|
|
38
|
-
if (runtimeEnv.MM_HARNESS_AUTO_NETWORK_CAPTURE !== "0") {
|
|
39
|
-
session.autoStartedAt = Date.now();
|
|
40
|
-
if (session.backend) {
|
|
41
|
-
try {
|
|
42
|
-
await session.backend.start({
|
|
43
|
-
id: AUTO_CAPTURE_ID,
|
|
44
|
-
bodyJsonFields: ["type"],
|
|
45
|
-
maxDurationMs: 60 * 60 * 1e3,
|
|
46
|
-
maxRequests: 1e4,
|
|
47
|
-
methods: [],
|
|
48
|
-
urlIncludes: []
|
|
49
|
-
});
|
|
50
|
-
session.autoStarted = true;
|
|
51
|
-
} catch (error) {
|
|
52
|
-
session.setupError = boundedError(error);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return {
|
|
57
|
-
onActionEvent(event) {
|
|
58
|
-
session.nodeEvents.push({ ...event, epochMs: Date.now() });
|
|
59
|
-
},
|
|
60
|
-
async finalize(artifactManifestPath) {
|
|
61
|
-
try {
|
|
62
|
-
if (runtimeEnv.MM_HARNESS_AUTO_NETWORK_CAPTURE !== "0") {
|
|
63
|
-
const summary = await automaticSummary(session);
|
|
64
|
-
await writeSummary(key, AUTO_ARTIFACT_PATH, summary);
|
|
65
|
-
if (artifactManifestPath) {
|
|
66
|
-
await indexArtifact(
|
|
67
|
-
artifactManifestPath,
|
|
68
|
-
AUTO_ARTIFACT_PATH,
|
|
69
|
-
"Automatic network observation"
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
} finally {
|
|
74
|
-
sessions.delete(key);
|
|
75
|
-
await session.backend?.close();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
async function handleRunNetworkAction(platform, action, node, context) {
|
|
81
|
-
if (!getAdapterSurface(platform).observation?.network?.actions) return null;
|
|
82
|
-
if (action === "app.network_assert") {
|
|
83
|
-
const assertion = await import("../library/actions/shared/app/network-assert.mjs");
|
|
84
|
-
return {
|
|
85
|
-
output: await assertion.assertNetwork({ action, node, context })
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
if (action !== "app.network_capture") return null;
|
|
89
|
-
const phase = String(node.phase ?? "").toLowerCase();
|
|
90
|
-
const id = String(node.id ?? "").trim();
|
|
91
|
-
if (!["start", "end"].includes(phase) || !id) {
|
|
92
|
-
throw new Error(
|
|
93
|
-
"app.network_capture requires phase=start|end and a non-empty id."
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
const session = sessions.get(path.resolve(context.artifactsDir));
|
|
97
|
-
if (!session?.backend) {
|
|
98
|
-
throw new Error(
|
|
99
|
-
`Extension network observation is unavailable: ${session?.setupError ?? "run observer was not started"}.`
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
if (phase === "start") {
|
|
103
|
-
const result = await session.backend.start(captureParams(id, node));
|
|
104
|
-
return {
|
|
105
|
-
output: {
|
|
106
|
-
action,
|
|
107
|
-
phase,
|
|
108
|
-
...asRecord(result)
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
const artifactPath = String(
|
|
113
|
-
node.artifact_path ?? `network/${id}-summary.json`
|
|
114
|
-
);
|
|
115
|
-
const summary = await session.backend.end(id);
|
|
116
|
-
await writeSummary(context.artifactsDir, artifactPath, summary);
|
|
117
|
-
return {
|
|
118
|
-
output: {
|
|
119
|
-
action,
|
|
120
|
-
phase,
|
|
121
|
-
...summary
|
|
122
|
-
},
|
|
123
|
-
artifacts: [
|
|
124
|
-
{
|
|
125
|
-
path: artifactPath,
|
|
126
|
-
type: "report",
|
|
127
|
-
nodeId: context.nodeId
|
|
128
|
-
}
|
|
129
|
-
]
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
async function createMobileNetworkBackend(target, env) {
|
|
133
|
-
const { client } = await connectMobileBroker(target, env);
|
|
134
|
-
return {
|
|
135
|
-
start(params) {
|
|
136
|
-
return client.control("capture-start", params, 1e4);
|
|
137
|
-
},
|
|
138
|
-
async end(id) {
|
|
139
|
-
return asRecord(await client.control("capture-end", { id }, 1e4));
|
|
140
|
-
},
|
|
141
|
-
async close() {
|
|
142
|
-
client.close();
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
async function connectMobileBroker(target, env) {
|
|
147
|
-
const runtimeDir = env.RECIPE_RUNTIME_DIR ? path.resolve(target, env.RECIPE_RUNTIME_DIR) : path.join(target, "temp", "recipe", "runtime");
|
|
148
|
-
const socketPath = brokerSocketPath(
|
|
149
|
-
runtimeDir,
|
|
150
|
-
resolvePort(env, target)
|
|
151
|
-
);
|
|
152
|
-
const discoveryClient = await createBrokerClient(socketPath, "", 1e4);
|
|
153
|
-
let targets;
|
|
154
|
-
try {
|
|
155
|
-
targets = await discoveryClient.control(
|
|
156
|
-
"resolve-targets",
|
|
157
|
-
{ nameIncludes: mobileTargetPin(env) },
|
|
158
|
-
1e4
|
|
159
|
-
);
|
|
160
|
-
} finally {
|
|
161
|
-
discoveryClient.close();
|
|
162
|
-
}
|
|
163
|
-
const deviceId = selectMobileBrokerTarget(targets, env);
|
|
164
|
-
const client = await createBrokerClient(socketPath, deviceId, 1e4);
|
|
165
|
-
return { client, deviceId };
|
|
166
|
-
}
|
|
167
|
-
function selectMobileBrokerTarget(value, env) {
|
|
168
|
-
const targets = Array.isArray(value) ? value.map(asRecord).filter((target) => target.deviceId) : [];
|
|
169
|
-
const pin = mobileTargetPin(env);
|
|
170
|
-
const candidates = pin ? targets.filter(
|
|
171
|
-
(target) => String(target.name ?? "").toLowerCase().includes(pin.toLowerCase())
|
|
172
|
-
) : targets;
|
|
173
|
-
if (candidates.length !== 1) {
|
|
174
|
-
throw new Error(
|
|
175
|
-
`Mobile network observation requires one broker target; found ${candidates.length}.`
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
return String(candidates[0].deviceId);
|
|
179
|
-
}
|
|
180
|
-
function mobileTargetPin(env) {
|
|
181
|
-
const platform = String(
|
|
182
|
-
env.RECIPE_RN_EXPLICIT_PLATFORM ?? env.RECIPE_HARNESS_PLATFORM ?? ""
|
|
183
|
-
).toLowerCase();
|
|
184
|
-
return String(
|
|
185
|
-
platform === "android" ? env.ANDROID_TARGET_DEVICE_NAME ?? env.ANDROID_DEVICE ?? "" : platform === "ios" ? env.IOS_SIMULATOR ?? "" : env.IOS_SIMULATOR ?? env.ANDROID_TARGET_DEVICE_NAME ?? env.ANDROID_DEVICE ?? ""
|
|
186
|
-
).trim();
|
|
187
|
-
}
|
|
188
|
-
async function automaticSummary(session) {
|
|
189
|
-
let summary;
|
|
190
|
-
if (session.autoStarted && session.backend) {
|
|
191
|
-
try {
|
|
192
|
-
summary = await session.backend.end(AUTO_CAPTURE_ID);
|
|
193
|
-
} catch (error) {
|
|
194
|
-
summary = unavailableSummary(session.autoStartedAt, boundedError(error));
|
|
195
|
-
}
|
|
196
|
-
} else {
|
|
197
|
-
summary = unavailableSummary(
|
|
198
|
-
session.autoStartedAt,
|
|
199
|
-
session.setupError ?? "Network observer did not start."
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
const startedAt = Number(summary.startedAtEpochMs ?? session.autoStartedAt);
|
|
203
|
-
return {
|
|
204
|
-
...summary,
|
|
205
|
-
nodeEvents: session.nodeEvents.map((event) => ({
|
|
206
|
-
action: event.action,
|
|
207
|
-
elapsedMs: Math.max(0, event.epochMs - startedAt),
|
|
208
|
-
nodeId: event.nodeId,
|
|
209
|
-
status: event.status
|
|
210
|
-
}))
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
function unavailableSummary(startedAtEpochMs, reason) {
|
|
214
|
-
return {
|
|
215
|
-
schemaVersion: 1,
|
|
216
|
-
id: AUTO_CAPTURE_ID,
|
|
217
|
-
status: "unavailable",
|
|
218
|
-
startedAtEpochMs,
|
|
219
|
-
endedAtEpochMs: Date.now(),
|
|
220
|
-
maxDurationMs: 60 * 60 * 1e3,
|
|
221
|
-
reconnects: 0,
|
|
222
|
-
droppedRequests: 0,
|
|
223
|
-
unavailableReasons: [reason],
|
|
224
|
-
coverageGapReasons: [],
|
|
225
|
-
uninspectableBodyRequests: 0,
|
|
226
|
-
projectedBodyFields: ["type"],
|
|
227
|
-
totalRequests: 0,
|
|
228
|
-
requestsByMethod: {},
|
|
229
|
-
requestsByHost: {},
|
|
230
|
-
requestsByType: {},
|
|
231
|
-
requests: []
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
function captureParams(id, node) {
|
|
235
|
-
return {
|
|
236
|
-
id,
|
|
237
|
-
urlIncludes: node.url_includes ?? [],
|
|
238
|
-
methods: node.methods ?? [],
|
|
239
|
-
bodyJsonFields: node.body_json_fields ?? [],
|
|
240
|
-
maxRequests: node.max_requests,
|
|
241
|
-
maxDurationMs: node.max_duration_ms
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
async function writeSummary(artifactsDir, relativePath, summary) {
|
|
245
|
-
await writeContainedArtifact(
|
|
246
|
-
artifactsDir,
|
|
247
|
-
relativePath,
|
|
248
|
-
`${JSON.stringify(summary, null, 2)}
|
|
249
|
-
`,
|
|
250
|
-
"Network artifact"
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
async function indexArtifact(artifactManifestPath, relativePath, label) {
|
|
254
|
-
await indexArtifactManifest(
|
|
255
|
-
artifactManifestPath,
|
|
256
|
-
[
|
|
257
|
-
{
|
|
258
|
-
path: relativePath,
|
|
259
|
-
type: "report",
|
|
260
|
-
label,
|
|
261
|
-
category: "diagnostic"
|
|
262
|
-
}
|
|
263
|
-
]
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
async function createExtensionNetworkBackend(env, artifactsDir) {
|
|
267
|
-
return createExtensionNetworkObserver(extensionCdpPort(env), artifactsDir);
|
|
268
|
-
}
|
|
269
|
-
function extensionCdpPort(env) {
|
|
270
|
-
const port = Number(
|
|
271
|
-
env.CDP_PORT ?? env.RECIPE_CDP_PORT ?? process.env.CDP_PORT
|
|
272
|
-
);
|
|
273
|
-
if (!Number.isInteger(port) || port <= 0) {
|
|
274
|
-
throw new Error("Extension network observation requires CDP_PORT.");
|
|
275
|
-
}
|
|
276
|
-
return port;
|
|
277
|
-
}
|
|
278
|
-
function boundedError(error) {
|
|
279
|
-
return String(error instanceof Error ? error.message : error).slice(0, 256);
|
|
280
|
-
}
|
|
281
|
-
function asRecord(value) {
|
|
282
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
283
|
-
}
|
|
284
|
-
export {
|
|
285
|
-
connectMobileBroker,
|
|
286
|
-
createExtensionNetworkBackend,
|
|
287
|
-
createMobileNetworkBackend,
|
|
288
|
-
handleRunNetworkAction,
|
|
289
|
-
startRunNetworkObservation
|
|
290
|
-
};
|