@ceraph/react-native-mcp 0.3.2 → 0.4.5
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 +335 -68
- package/dist/babel-plugin/index.cjs +1 -0
- package/dist/babel-plugin/index.js +1 -0
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +1 -47
- package/dist/index.d.ts +106 -1
- package/dist/index.js +2 -1651
- package/dist/shim/async-storage-ops.d.ts +26 -0
- package/dist/shim/async-storage-ops.js +1 -0
- package/dist/shim/boot.d.ts +9 -0
- package/dist/shim/boot.js +1 -141
- package/dist/shim/camera.js +1 -62
- package/dist/shim/command-poll.d.ts +18 -0
- package/dist/shim/command-poll.js +1 -0
- package/dist/shim/config.js +1 -56
- package/dist/shim/console-capture.d.ts +16 -0
- package/dist/shim/console-capture.js +1 -0
- package/dist/shim/deep-link.js +1 -25
- package/dist/shim/dev-guard.js +1 -3
- package/dist/shim/dev-host.d.ts +1 -0
- package/dist/shim/dev-host.js +1 -0
- package/dist/shim/error-handler.js +1 -66
- package/dist/shim/fetch-interceptor.js +1 -93
- package/dist/shim/index.d.ts +3 -0
- package/dist/shim/index.js +1 -6
- package/dist/shim/keep-awake.js +1 -118
- package/dist/shim/network-ownership.d.ts +4 -0
- package/dist/shim/network-ownership.js +1 -0
- package/dist/shim/optimistic-observer.d.ts +29 -0
- package/dist/shim/optimistic-observer.js +1 -0
- package/dist/shim/reload.js +1 -76
- package/dist/shim/reset.d.ts +30 -0
- package/dist/shim/reset.js +1 -0
- package/dist/shim/signal-capture.d.ts +8 -0
- package/dist/shim/signal-capture.js +1 -15
- package/dist/shim/signal-transport.d.ts +14 -1
- package/dist/shim/signal-transport.js +1 -43
- package/dist/shim/xhr-interceptor.d.ts +39 -0
- package/dist/shim/xhr-interceptor.js +1 -0
- package/package.json +41 -15
- package/dist/app-lifecycle.d.ts +0 -50
- package/dist/app-lifecycle.js +0 -487
- package/dist/camera-image-writer.d.ts +0 -43
- package/dist/camera-image-writer.js +0 -280
- package/dist/camera-registry-sync.d.ts +0 -18
- package/dist/camera-registry-sync.js +0 -117
- package/dist/device-autonomy.d.ts +0 -30
- package/dist/device-autonomy.js +0 -117
- package/dist/error-parser.d.ts +0 -51
- package/dist/error-parser.js +0 -275
- package/dist/expo-manager.d.ts +0 -62
- package/dist/expo-manager.js +0 -447
- package/dist/init/ast-camera.d.ts +0 -29
- package/dist/init/ast-camera.js +0 -267
- package/dist/init/ast-layout.d.ts +0 -15
- package/dist/init/ast-layout.js +0 -167
- package/dist/init/claude-hook-constants.d.ts +0 -9
- package/dist/init/claude-hook-constants.js +0 -91
- package/dist/init/lan-ip.d.ts +0 -11
- package/dist/init/lan-ip.js +0 -51
- package/dist/init/monorepo.d.ts +0 -13
- package/dist/init/monorepo.js +0 -185
- package/dist/init/oauth.d.ts +0 -52
- package/dist/init/oauth.js +0 -220
- package/dist/init/package-manager.d.ts +0 -11
- package/dist/init/package-manager.js +0 -60
- package/dist/init/prompt.d.ts +0 -12
- package/dist/init/prompt.js +0 -68
- package/dist/init/shell-profile.d.ts +0 -22
- package/dist/init/shell-profile.js +0 -85
- package/dist/init/steps.d.ts +0 -135
- package/dist/init/steps.js +0 -399
- package/dist/init/url-scheme.d.ts +0 -42
- package/dist/init/url-scheme.js +0 -187
- package/dist/init/walkthrough.d.ts +0 -76
- package/dist/init/walkthrough.js +0 -340
- package/dist/init.d.ts +0 -8
- package/dist/init.js +0 -395
- package/dist/iproxy-manager.d.ts +0 -32
- package/dist/iproxy-manager.js +0 -216
- package/dist/mac-caffeinate.d.ts +0 -10
- package/dist/mac-caffeinate.js +0 -56
- package/dist/permission-interceptor.d.ts +0 -29
- package/dist/permission-interceptor.js +0 -185
- package/dist/prebuild-detector.d.ts +0 -19
- package/dist/prebuild-detector.js +0 -174
- package/dist/preflight.d.ts +0 -34
- package/dist/preflight.js +0 -847
- package/dist/screen.d.ts +0 -184
- package/dist/screen.js +0 -931
- package/dist/signal-listener.d.ts +0 -27
- package/dist/signal-listener.js +0 -135
- package/dist/simulator-boot.d.ts +0 -52
- package/dist/simulator-boot.js +0 -227
- package/dist/target.d.ts +0 -48
- package/dist/target.js +0 -267
- package/dist/uninstall/cli-runner.d.ts +0 -32
- package/dist/uninstall/cli-runner.js +0 -223
- package/dist/uninstall/footprint.d.ts +0 -40
- package/dist/uninstall/footprint.js +0 -288
- package/dist/uninstall/mcp-tools.d.ts +0 -14
- package/dist/uninstall/mcp-tools.js +0 -175
- package/dist/uninstall/revert-auth.d.ts +0 -22
- package/dist/uninstall/revert-auth.js +0 -31
- package/dist/uninstall/revert-boot.d.ts +0 -24
- package/dist/uninstall/revert-boot.js +0 -242
- package/dist/uninstall/revert-camera.d.ts +0 -12
- package/dist/uninstall/revert-camera.js +0 -199
- package/dist/uninstall/revert-ceraph-dir.d.ts +0 -27
- package/dist/uninstall/revert-ceraph-dir.js +0 -38
- package/dist/uninstall/revert-claude-hooks.d.ts +0 -19
- package/dist/uninstall/revert-claude-hooks.js +0 -191
- package/dist/uninstall/revert-gitignore.d.ts +0 -17
- package/dist/uninstall/revert-gitignore.js +0 -43
- package/dist/uninstall/revert-mcp-clients.d.ts +0 -57
- package/dist/uninstall/revert-mcp-clients.js +0 -194
- package/dist/uninstall/revert-package.d.ts +0 -34
- package/dist/uninstall/revert-package.js +0 -98
- package/dist/uninstall/revert-scheme.d.ts +0 -36
- package/dist/uninstall/revert-scheme.js +0 -139
- package/dist/uninstall/revert-signal-host-env.d.ts +0 -31
- package/dist/uninstall/revert-signal-host-env.js +0 -61
- package/dist/uninstall/walkthrough.d.ts +0 -80
- package/dist/uninstall/walkthrough.js +0 -1244
- package/dist/utils/atomic-write.d.ts +0 -1
- package/dist/utils/atomic-write.js +0 -30
- package/dist/wait-for-device.d.ts +0 -68
- package/dist/wait-for-device.js +0 -368
- package/dist/wda-manager.d.ts +0 -38
- package/dist/wda-manager.js +0 -186
- package/dist/wda-simulator.d.ts +0 -28
- package/dist/wda-simulator.js +0 -257
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { RNManager } from "./expo-manager.js";
|
|
2
|
-
export interface SignalListenerOptions {
|
|
3
|
-
port?: number;
|
|
4
|
-
host?: string;
|
|
5
|
-
}
|
|
6
|
-
export interface SignalListenerStartResult {
|
|
7
|
-
ok: boolean;
|
|
8
|
-
port: number;
|
|
9
|
-
error?: string;
|
|
10
|
-
errorCode?: string;
|
|
11
|
-
}
|
|
12
|
-
export declare class SignalListener {
|
|
13
|
-
private rnManager;
|
|
14
|
-
private server;
|
|
15
|
-
private port;
|
|
16
|
-
private hostOverride;
|
|
17
|
-
constructor(rnManager: Pick<RNManager, "appendShimSignal">, opts?: SignalListenerOptions);
|
|
18
|
-
start(): Promise<SignalListenerStartResult>;
|
|
19
|
-
stop(): Promise<void>;
|
|
20
|
-
handleForTesting(payload: unknown): Promise<{
|
|
21
|
-
ok: boolean;
|
|
22
|
-
error?: string;
|
|
23
|
-
}>;
|
|
24
|
-
private handle;
|
|
25
|
-
private appendIfValid;
|
|
26
|
-
private readBody;
|
|
27
|
-
}
|
package/dist/signal-listener.js
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { createServer } from "node:http";
|
|
2
|
-
const DEFAULT_PORT = 8101;
|
|
3
|
-
const MAX_BODY_BYTES = 64 * 1024;
|
|
4
|
-
const KNOWN_KINDS = new Set([
|
|
5
|
-
"metro-error",
|
|
6
|
-
"js-error",
|
|
7
|
-
"unhandled-rejection",
|
|
8
|
-
"network-error",
|
|
9
|
-
"network-4xx",
|
|
10
|
-
]);
|
|
11
|
-
const DEFAULT_HOST = "127.0.0.1";
|
|
12
|
-
export class SignalListener {
|
|
13
|
-
rnManager;
|
|
14
|
-
server = null;
|
|
15
|
-
port;
|
|
16
|
-
hostOverride;
|
|
17
|
-
constructor(rnManager, opts = {}) {
|
|
18
|
-
this.rnManager = rnManager;
|
|
19
|
-
this.port = opts.port ?? DEFAULT_PORT;
|
|
20
|
-
this.hostOverride = opts.host ?? null;
|
|
21
|
-
}
|
|
22
|
-
async start() {
|
|
23
|
-
const host = this.hostOverride ?? process.env.CERAPH_SIGNAL_HOST ?? DEFAULT_HOST;
|
|
24
|
-
return new Promise((resolve) => {
|
|
25
|
-
const server = createServer((req, res) => this.handle(req, res));
|
|
26
|
-
server.once("error", (err) => {
|
|
27
|
-
resolve({
|
|
28
|
-
ok: false,
|
|
29
|
-
port: this.port,
|
|
30
|
-
error: err.message ?? err.code ?? "listen failed",
|
|
31
|
-
errorCode: err.code,
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
server.listen(this.port, host, () => {
|
|
35
|
-
this.server = server;
|
|
36
|
-
resolve({ ok: true, port: this.port });
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
async stop() {
|
|
41
|
-
const server = this.server;
|
|
42
|
-
if (!server)
|
|
43
|
-
return;
|
|
44
|
-
this.server = null;
|
|
45
|
-
await new Promise((resolve) => server.close(() => resolve()));
|
|
46
|
-
}
|
|
47
|
-
async handleForTesting(payload) {
|
|
48
|
-
return this.appendIfValid(payload);
|
|
49
|
-
}
|
|
50
|
-
async handle(req, res) {
|
|
51
|
-
if (req.method !== "POST" || req.url !== "/signal") {
|
|
52
|
-
res.statusCode = 404;
|
|
53
|
-
res.end();
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const body = await this.readBody(req).catch((err) => ({
|
|
57
|
-
tooLarge: false,
|
|
58
|
-
err: err,
|
|
59
|
-
}));
|
|
60
|
-
if ("err" in body) {
|
|
61
|
-
res.statusCode = 400;
|
|
62
|
-
res.end(JSON.stringify({ ok: false, error: body.err.message }));
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
if (body.tooLarge) {
|
|
66
|
-
res.statusCode = 413;
|
|
67
|
-
res.end(JSON.stringify({ ok: false, error: "payload too large" }));
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
let parsed;
|
|
71
|
-
try {
|
|
72
|
-
parsed = JSON.parse(body.text);
|
|
73
|
-
}
|
|
74
|
-
catch (err) {
|
|
75
|
-
res.statusCode = 400;
|
|
76
|
-
res.end(JSON.stringify({ ok: false, error: err.message }));
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const outcome = await this.appendIfValid(parsed);
|
|
80
|
-
if (!outcome.ok) {
|
|
81
|
-
res.statusCode = 400;
|
|
82
|
-
res.end(JSON.stringify(outcome));
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
res.statusCode = 204;
|
|
86
|
-
res.end();
|
|
87
|
-
}
|
|
88
|
-
async appendIfValid(payload) {
|
|
89
|
-
if (!payload || typeof payload !== "object") {
|
|
90
|
-
return { ok: false, error: "payload must be an object" };
|
|
91
|
-
}
|
|
92
|
-
const p = payload;
|
|
93
|
-
if (typeof p.message !== "string" || p.message.length === 0) {
|
|
94
|
-
return { ok: false, error: "message is required" };
|
|
95
|
-
}
|
|
96
|
-
if (typeof p.kind !== "string" || !KNOWN_KINDS.has(p.kind)) {
|
|
97
|
-
return { ok: false, error: `unknown kind: ${String(p.kind)}` };
|
|
98
|
-
}
|
|
99
|
-
const timestamp = typeof p.timestampMs === "number" && Number.isFinite(p.timestampMs)
|
|
100
|
-
? new Date(p.timestampMs).toISOString()
|
|
101
|
-
: new Date().toISOString();
|
|
102
|
-
await this.rnManager.appendShimSignal({
|
|
103
|
-
message: p.message,
|
|
104
|
-
stack: typeof p.stack === "string" ? p.stack : "",
|
|
105
|
-
timestamp,
|
|
106
|
-
kind: p.kind,
|
|
107
|
-
url: typeof p.url === "string" ? p.url : undefined,
|
|
108
|
-
status: typeof p.status === "number" ? p.status : undefined,
|
|
109
|
-
method: typeof p.method === "string" ? p.method : undefined,
|
|
110
|
-
durationMs: typeof p.durationMs === "number" ? p.durationMs : undefined,
|
|
111
|
-
});
|
|
112
|
-
return { ok: true };
|
|
113
|
-
}
|
|
114
|
-
readBody(req) {
|
|
115
|
-
return new Promise((resolve, reject) => {
|
|
116
|
-
let received = 0;
|
|
117
|
-
const chunks = [];
|
|
118
|
-
req.on("data", (chunk) => {
|
|
119
|
-
received += chunk.length;
|
|
120
|
-
if (received > MAX_BODY_BYTES) {
|
|
121
|
-
chunks.length = 0;
|
|
122
|
-
resolve({ text: "", tooLarge: true });
|
|
123
|
-
req.removeAllListeners("data");
|
|
124
|
-
req.removeAllListeners("end");
|
|
125
|
-
req.on("data", () => undefined);
|
|
126
|
-
req.on("end", () => undefined);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
chunks.push(chunk);
|
|
130
|
-
});
|
|
131
|
-
req.on("end", () => resolve({ text: Buffer.concat(chunks).toString("utf-8"), tooLarge: false }));
|
|
132
|
-
req.on("error", reject);
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
package/dist/simulator-boot.d.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
export interface BootSimulatorOpts {
|
|
2
|
-
udid?: string;
|
|
3
|
-
deviceType?: string;
|
|
4
|
-
bootTimeoutMs?: number;
|
|
5
|
-
}
|
|
6
|
-
export type BootSimulatorResult = {
|
|
7
|
-
udid: string;
|
|
8
|
-
deviceType: string;
|
|
9
|
-
alreadyBooted: boolean;
|
|
10
|
-
booted: true;
|
|
11
|
-
runtime: string;
|
|
12
|
-
durationMs: number;
|
|
13
|
-
};
|
|
14
|
-
export interface SimctlDeviceEntry {
|
|
15
|
-
udid: string;
|
|
16
|
-
name: string;
|
|
17
|
-
state: string;
|
|
18
|
-
runtime?: string;
|
|
19
|
-
isAvailable?: boolean;
|
|
20
|
-
}
|
|
21
|
-
export interface SimctlListResult {
|
|
22
|
-
devices: Record<string, SimctlDeviceEntry[]>;
|
|
23
|
-
}
|
|
24
|
-
export interface CommandResult {
|
|
25
|
-
code: number;
|
|
26
|
-
stdout: string;
|
|
27
|
-
stderr: string;
|
|
28
|
-
}
|
|
29
|
-
export interface BootSimulatorDeps {
|
|
30
|
-
runner: (cmd: string, args: string[], timeoutMs?: number) => Promise<CommandResult>;
|
|
31
|
-
}
|
|
32
|
-
export declare function runXcrun(cmd: string, args: string[], timeoutMs?: number): Promise<CommandResult>;
|
|
33
|
-
export declare const defaultBootSimulatorDeps: BootSimulatorDeps;
|
|
34
|
-
export declare function parseIosRuntimeVersion(runtimeKey: string): {
|
|
35
|
-
major: number;
|
|
36
|
-
minor: number;
|
|
37
|
-
label: string;
|
|
38
|
-
} | null;
|
|
39
|
-
export declare function pickBestSimulator(list: SimctlListResult, deviceType?: string): {
|
|
40
|
-
udid: string;
|
|
41
|
-
name: string;
|
|
42
|
-
runtimeLabel: string;
|
|
43
|
-
state: string;
|
|
44
|
-
} | null;
|
|
45
|
-
export declare function findSimulatorByUdid(list: SimctlListResult, udid: string): {
|
|
46
|
-
udid: string;
|
|
47
|
-
name: string;
|
|
48
|
-
runtimeLabel: string;
|
|
49
|
-
state: string;
|
|
50
|
-
} | null;
|
|
51
|
-
export declare function isAlreadyBootedError(stderr: string): boolean;
|
|
52
|
-
export declare function bootSimulator(opts?: BootSimulatorOpts, deps?: BootSimulatorDeps): Promise<BootSimulatorResult>;
|
package/dist/simulator-boot.js
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
export async function runXcrun(cmd, args, timeoutMs = 30_000) {
|
|
3
|
-
return new Promise((resolve) => {
|
|
4
|
-
let stdout = "";
|
|
5
|
-
let stderr = "";
|
|
6
|
-
let child;
|
|
7
|
-
try {
|
|
8
|
-
child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
9
|
-
}
|
|
10
|
-
catch (err) {
|
|
11
|
-
resolve({
|
|
12
|
-
code: 127,
|
|
13
|
-
stdout: "",
|
|
14
|
-
stderr: err instanceof Error ? err.message : String(err),
|
|
15
|
-
});
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
const timer = setTimeout(() => {
|
|
19
|
-
try {
|
|
20
|
-
child.kill("SIGKILL");
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
}
|
|
24
|
-
}, timeoutMs);
|
|
25
|
-
child.stdout?.on("data", (d) => {
|
|
26
|
-
stdout += d.toString();
|
|
27
|
-
});
|
|
28
|
-
child.stderr?.on("data", (d) => {
|
|
29
|
-
stderr += d.toString();
|
|
30
|
-
});
|
|
31
|
-
let settled = false;
|
|
32
|
-
child.on("error", (err) => {
|
|
33
|
-
if (settled)
|
|
34
|
-
return;
|
|
35
|
-
settled = true;
|
|
36
|
-
clearTimeout(timer);
|
|
37
|
-
resolve({
|
|
38
|
-
code: 127,
|
|
39
|
-
stdout,
|
|
40
|
-
stderr: stderr + (err.message ?? String(err)),
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
child.on("exit", (code) => {
|
|
44
|
-
if (settled)
|
|
45
|
-
return;
|
|
46
|
-
settled = true;
|
|
47
|
-
clearTimeout(timer);
|
|
48
|
-
resolve({ code: code ?? 1, stdout, stderr });
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
export const defaultBootSimulatorDeps = {
|
|
53
|
-
runner: runXcrun,
|
|
54
|
-
};
|
|
55
|
-
export function parseIosRuntimeVersion(runtimeKey) {
|
|
56
|
-
const lower = runtimeKey.toLowerCase();
|
|
57
|
-
if (!lower.includes("ios") && !lower.includes("iphone"))
|
|
58
|
-
return null;
|
|
59
|
-
const re = /ios[-\s]?(\d+)(?:[-.](\d+))?/i;
|
|
60
|
-
const m = re.exec(runtimeKey);
|
|
61
|
-
if (!m)
|
|
62
|
-
return null;
|
|
63
|
-
const major = Number(m[1]);
|
|
64
|
-
const minor = m[2] ? Number(m[2]) : 0;
|
|
65
|
-
if (!Number.isFinite(major))
|
|
66
|
-
return null;
|
|
67
|
-
return { major, minor, label: `iOS ${major}${m[2] ? "." + minor : ""}` };
|
|
68
|
-
}
|
|
69
|
-
const VARIANT_MODIFIERS = ["pro", "max", "plus", "mini", "se", "air"];
|
|
70
|
-
function parseModelNumber(name) {
|
|
71
|
-
const m = /\b(\d+)\b/.exec(name);
|
|
72
|
-
if (!m)
|
|
73
|
-
return 0;
|
|
74
|
-
const n = Number(m[1]);
|
|
75
|
-
return Number.isFinite(n) ? n : 0;
|
|
76
|
-
}
|
|
77
|
-
function countModifiers(name) {
|
|
78
|
-
const tokens = name.toLowerCase().split(/\s+/);
|
|
79
|
-
let count = 0;
|
|
80
|
-
for (const t of tokens) {
|
|
81
|
-
if (VARIANT_MODIFIERS.includes(t))
|
|
82
|
-
count++;
|
|
83
|
-
}
|
|
84
|
-
return count;
|
|
85
|
-
}
|
|
86
|
-
export function pickBestSimulator(list, deviceType) {
|
|
87
|
-
const wanted = (deviceType ?? "").trim().toLowerCase();
|
|
88
|
-
const candidates = [];
|
|
89
|
-
for (const [runtimeKey, devices] of Object.entries(list.devices ?? {})) {
|
|
90
|
-
const runtime = parseIosRuntimeVersion(runtimeKey);
|
|
91
|
-
if (!runtime)
|
|
92
|
-
continue;
|
|
93
|
-
if (!Array.isArray(devices))
|
|
94
|
-
continue;
|
|
95
|
-
for (const dev of devices) {
|
|
96
|
-
if (!dev.udid || !dev.name)
|
|
97
|
-
continue;
|
|
98
|
-
if (dev.isAvailable === false)
|
|
99
|
-
continue;
|
|
100
|
-
const lowerName = dev.name.toLowerCase();
|
|
101
|
-
if (wanted && !lowerName.includes(wanted))
|
|
102
|
-
continue;
|
|
103
|
-
candidates.push({
|
|
104
|
-
udid: dev.udid,
|
|
105
|
-
name: dev.name,
|
|
106
|
-
runtimeLabel: runtime.label,
|
|
107
|
-
state: dev.state ?? "Unknown",
|
|
108
|
-
major: runtime.major,
|
|
109
|
-
minor: runtime.minor,
|
|
110
|
-
modifiers: countModifiers(dev.name),
|
|
111
|
-
modelNumber: parseModelNumber(dev.name),
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
if (candidates.length === 0)
|
|
116
|
-
return null;
|
|
117
|
-
candidates.sort((a, b) => {
|
|
118
|
-
if (b.major !== a.major)
|
|
119
|
-
return b.major - a.major;
|
|
120
|
-
if (b.minor !== a.minor)
|
|
121
|
-
return b.minor - a.minor;
|
|
122
|
-
if (a.modifiers !== b.modifiers)
|
|
123
|
-
return a.modifiers - b.modifiers;
|
|
124
|
-
if (b.modelNumber !== a.modelNumber)
|
|
125
|
-
return b.modelNumber - a.modelNumber;
|
|
126
|
-
return a.name.localeCompare(b.name);
|
|
127
|
-
});
|
|
128
|
-
const top = candidates[0];
|
|
129
|
-
return {
|
|
130
|
-
udid: top.udid,
|
|
131
|
-
name: top.name,
|
|
132
|
-
runtimeLabel: top.runtimeLabel,
|
|
133
|
-
state: top.state,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
export function findSimulatorByUdid(list, udid) {
|
|
137
|
-
for (const [runtimeKey, devices] of Object.entries(list.devices ?? {})) {
|
|
138
|
-
const runtime = parseIosRuntimeVersion(runtimeKey);
|
|
139
|
-
if (!runtime)
|
|
140
|
-
continue;
|
|
141
|
-
if (!Array.isArray(devices))
|
|
142
|
-
continue;
|
|
143
|
-
for (const dev of devices) {
|
|
144
|
-
if (dev.udid !== udid)
|
|
145
|
-
continue;
|
|
146
|
-
return {
|
|
147
|
-
udid: dev.udid,
|
|
148
|
-
name: dev.name ?? udid,
|
|
149
|
-
runtimeLabel: runtime.label,
|
|
150
|
-
state: dev.state ?? "Unknown",
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
export function isAlreadyBootedError(stderr) {
|
|
157
|
-
const s = stderr.toLowerCase();
|
|
158
|
-
return (s.includes("unable to boot device in current state") &&
|
|
159
|
-
s.includes("booted"));
|
|
160
|
-
}
|
|
161
|
-
export async function bootSimulator(opts = {}, deps = defaultBootSimulatorDeps) {
|
|
162
|
-
const bootTimeoutMs = opts.bootTimeoutMs ?? 120_000;
|
|
163
|
-
const runner = deps.runner;
|
|
164
|
-
let chosen;
|
|
165
|
-
const listResult = await runner("xcrun", ["simctl", "list", "devices", "--json"], 15_000);
|
|
166
|
-
if (listResult.code === 127) {
|
|
167
|
-
throw new Error("`xcrun` not found on PATH. Install Xcode Command Line Tools " +
|
|
168
|
-
"(`xcode-select --install`) and try again.");
|
|
169
|
-
}
|
|
170
|
-
if (listResult.code !== 0) {
|
|
171
|
-
throw new Error(`xcrun simctl list devices failed (exit ${listResult.code}): ${listResult.stderr || listResult.stdout || "no output"}`);
|
|
172
|
-
}
|
|
173
|
-
let parsed;
|
|
174
|
-
try {
|
|
175
|
-
parsed = JSON.parse(listResult.stdout);
|
|
176
|
-
}
|
|
177
|
-
catch (err) {
|
|
178
|
-
throw new Error(`Failed to parse simctl device list as JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
179
|
-
}
|
|
180
|
-
if (opts.udid) {
|
|
181
|
-
const found = findSimulatorByUdid(parsed, opts.udid);
|
|
182
|
-
if (!found) {
|
|
183
|
-
throw new Error(`No iOS simulator with udid ${opts.udid} found in the simctl ` +
|
|
184
|
-
"listing. Run `xcrun simctl list devices` to see available udids.");
|
|
185
|
-
}
|
|
186
|
-
chosen = found;
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
const picked = pickBestSimulator(parsed, opts.deviceType);
|
|
190
|
-
if (!picked) {
|
|
191
|
-
throw new Error(opts.deviceType
|
|
192
|
-
? `No iOS simulators matching "${opts.deviceType}" are available. ` +
|
|
193
|
-
"Open Xcode → Settings → Platforms → install an iOS simulator " +
|
|
194
|
-
"runtime, or pick a different deviceType."
|
|
195
|
-
: "No iOS simulators are available on this Mac. Open Xcode → " +
|
|
196
|
-
"Settings → Platforms → install an iOS simulator runtime.");
|
|
197
|
-
}
|
|
198
|
-
chosen = picked;
|
|
199
|
-
}
|
|
200
|
-
if (chosen.state.toLowerCase() === "booted") {
|
|
201
|
-
return {
|
|
202
|
-
udid: chosen.udid,
|
|
203
|
-
deviceType: chosen.name,
|
|
204
|
-
alreadyBooted: true,
|
|
205
|
-
booted: true,
|
|
206
|
-
runtime: chosen.runtimeLabel,
|
|
207
|
-
durationMs: 0,
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
const start = Date.now();
|
|
211
|
-
const bootResult = await runner("xcrun", ["simctl", "boot", chosen.udid], 30_000);
|
|
212
|
-
if (bootResult.code !== 0 && !isAlreadyBootedError(bootResult.stderr)) {
|
|
213
|
-
throw new Error(`Failed to boot simulator ${chosen.name} (${chosen.udid}): ${bootResult.stderr || bootResult.stdout || "no output"}`);
|
|
214
|
-
}
|
|
215
|
-
const bootstatus = await runner("xcrun", ["simctl", "bootstatus", chosen.udid, "-b"], bootTimeoutMs + 5_000);
|
|
216
|
-
if (bootstatus.code !== 0) {
|
|
217
|
-
throw new Error(`simctl bootstatus failed for ${chosen.name} (${chosen.udid}): ${bootstatus.stderr || bootstatus.stdout || "no output"}`);
|
|
218
|
-
}
|
|
219
|
-
return {
|
|
220
|
-
udid: chosen.udid,
|
|
221
|
-
deviceType: chosen.name,
|
|
222
|
-
alreadyBooted: false,
|
|
223
|
-
booted: true,
|
|
224
|
-
runtime: chosen.runtimeLabel,
|
|
225
|
-
durationMs: Date.now() - start,
|
|
226
|
-
};
|
|
227
|
-
}
|
package/dist/target.d.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
export type Target = "device" | "simulator";
|
|
2
|
-
export type TargetPreference = Target | "auto";
|
|
3
|
-
export interface TargetInfo {
|
|
4
|
-
target: Target;
|
|
5
|
-
udid: string | null;
|
|
6
|
-
baseUrl: string;
|
|
7
|
-
wdaReady: boolean;
|
|
8
|
-
reason: "env-override-device" | "env-override-simulator" | "auto-prefer-device" | "auto-only-simulator" | "auto-no-target-found";
|
|
9
|
-
}
|
|
10
|
-
export interface BootedSimulator {
|
|
11
|
-
udid: string;
|
|
12
|
-
name: string;
|
|
13
|
-
runtime?: string;
|
|
14
|
-
}
|
|
15
|
-
export interface AppLifecycleLike {
|
|
16
|
-
getUdid(): Promise<string | null>;
|
|
17
|
-
}
|
|
18
|
-
export declare function preferenceFromEnv(raw: string | undefined): TargetPreference;
|
|
19
|
-
export declare function listBootedSimulators(): Promise<BootedSimulator[]>;
|
|
20
|
-
export declare function parseWdaPort(stdout: string): number | null;
|
|
21
|
-
export declare const DEFAULT_DEVICE_WDA_BASE_URL = "http://localhost:8100";
|
|
22
|
-
export declare function simulatorWdaBaseUrl(port: number): string;
|
|
23
|
-
export declare class TargetResolver {
|
|
24
|
-
private apps;
|
|
25
|
-
private readPreference;
|
|
26
|
-
private cached;
|
|
27
|
-
private cachedAt;
|
|
28
|
-
private readonly cacheTtlMs;
|
|
29
|
-
private inflight;
|
|
30
|
-
private stateVersion;
|
|
31
|
-
private simulatorWdaPort;
|
|
32
|
-
private simulatorWdaUdid;
|
|
33
|
-
constructor(opts?: {
|
|
34
|
-
apps?: AppLifecycleLike;
|
|
35
|
-
readPreference?: () => TargetPreference;
|
|
36
|
-
cacheTtlMs?: number;
|
|
37
|
-
});
|
|
38
|
-
setAppLifecycle(apps: AppLifecycleLike): void;
|
|
39
|
-
setSimulatorWdaPort(port: number, udid: string): void;
|
|
40
|
-
clearSimulatorWdaPort(): void;
|
|
41
|
-
getSimulatorWdaPort(): number | null;
|
|
42
|
-
getSimulatorWdaUdid(): string | null;
|
|
43
|
-
invalidate(): void;
|
|
44
|
-
getBaseUrl(): Promise<string>;
|
|
45
|
-
getTarget(): Promise<Target>;
|
|
46
|
-
resolve(): Promise<TargetInfo>;
|
|
47
|
-
reset(): void;
|
|
48
|
-
}
|