@kvell007/embed-labs-local-bridge 0.1.0-alpha.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.
@@ -0,0 +1,64 @@
1
+ import net from "node:net";
2
+ import { commandExists, runCommand, tailLines } from "../process.js";
3
+ export async function probeTcp(host, port, timeoutMs) {
4
+ const evidence = await probeTcpWithEvidence(host, port, timeoutMs, new Date().toISOString());
5
+ return evidence.reachable;
6
+ }
7
+ export async function probeTcpWithEvidence(host, port, timeoutMs, observedAt) {
8
+ return new Promise((resolve) => {
9
+ const startedAt = Date.now();
10
+ const socket = new net.Socket();
11
+ let settled = false;
12
+ const done = (value, error) => {
13
+ if (settled) {
14
+ return;
15
+ }
16
+ settled = true;
17
+ socket.destroy();
18
+ resolve({
19
+ transport: "tcp",
20
+ host,
21
+ port,
22
+ reachable: value,
23
+ duration_ms: Date.now() - startedAt,
24
+ observed_at: observedAt,
25
+ error_code: error?.code,
26
+ error_message: error?.message
27
+ });
28
+ };
29
+ socket.setTimeout(timeoutMs);
30
+ socket.once("connect", () => done(true));
31
+ socket.once("timeout", () => done(false, Object.assign(new Error(`Timed out after ${timeoutMs}ms.`), { code: "timeout" })));
32
+ socket.once("error", (error) => done(false, error));
33
+ socket.connect(port, host);
34
+ });
35
+ }
36
+ export async function runBoundedSsh(host, user, command, timeoutSeconds = 15) {
37
+ const sshExists = await commandExists("ssh");
38
+ if (!sshExists) {
39
+ return {
40
+ ok: false,
41
+ error: {
42
+ code: "ssh_not_found",
43
+ message: "ssh binary was not found on PATH.",
44
+ remediation: "Install OpenSSH client or add ssh to PATH."
45
+ }
46
+ };
47
+ }
48
+ const result = await runCommand("ssh", [
49
+ "-o", "BatchMode=yes",
50
+ "-o", "UserKnownHostsFile=/dev/null",
51
+ "-o", "StrictHostKeyChecking=no",
52
+ "-o", `ConnectTimeout=${Math.max(1, Math.min(timeoutSeconds, 30))}`,
53
+ `${user}@${host}`,
54
+ command
55
+ ], timeoutSeconds * 1000 + 1000);
56
+ return {
57
+ ok: result.exit_code === 0,
58
+ exit_code: result.exit_code,
59
+ stdout: result.stdout,
60
+ stderr: result.stderr,
61
+ output_tail: tailLines(`${result.stdout}\n${result.stderr}`)
62
+ };
63
+ }
64
+ //# sourceMappingURL=network.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/adapters/network.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ErrorEnvelope, SerialPortInfo } from "@embed-labs/protocol";
2
+ export declare function listSerialPorts(warnings?: ErrorEnvelope[]): Promise<SerialPortInfo[]>;
3
+ export declare function captureSerial(path: string, baudRate?: number, durationSeconds?: number): Promise<unknown>;
@@ -0,0 +1,133 @@
1
+ import { spawn } from "node:child_process";
2
+ import { readdir, stat } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { runCommand, stripControl, tailLines } from "../process.js";
5
+ export async function listSerialPorts(warnings = []) {
6
+ if (process.platform === "win32") {
7
+ const result = await runCommand("powershell.exe", [
8
+ "-NoProfile",
9
+ "-Command",
10
+ "Get-CimInstance Win32_SerialPort | Select-Object DeviceID,Name,PNPDeviceID | ConvertTo-Json -Compress"
11
+ ], 5000);
12
+ if (result.exit_code !== 0) {
13
+ warnings.push({ code: "serial_scan_failed", message: result.stderr || "Windows serial scan failed" });
14
+ return [];
15
+ }
16
+ try {
17
+ const parsed = JSON.parse(result.stdout || "[]");
18
+ const rows = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];
19
+ return rows.map((row) => {
20
+ const item = row;
21
+ return {
22
+ path: String(item.DeviceID ?? ""),
23
+ display_name: String(item.Name ?? item.DeviceID ?? ""),
24
+ serial_number: String(item.PNPDeviceID ?? ""),
25
+ available: Boolean(item.DeviceID)
26
+ };
27
+ }).filter((item) => item.path);
28
+ }
29
+ catch {
30
+ warnings.push({ code: "serial_scan_parse_failed", message: "Windows serial scan returned invalid JSON" });
31
+ return [];
32
+ }
33
+ }
34
+ const root = "/dev";
35
+ let entries = [];
36
+ try {
37
+ entries = await readdir(root);
38
+ }
39
+ catch (error) {
40
+ warnings.push({ code: "serial_scan_failed", message: String(error) });
41
+ return [];
42
+ }
43
+ const patterns = process.platform === "darwin"
44
+ ? [/^cu\./, /^tty\./]
45
+ : [/^ttyUSB/, /^ttyACM/, /^ttyAMA/, /^ttyS/, /^serial/];
46
+ return entries
47
+ .filter((entry) => patterns.some((pattern) => pattern.test(entry)))
48
+ .map((entry) => ({
49
+ path: join(root, entry),
50
+ display_name: entry,
51
+ available: true
52
+ }));
53
+ }
54
+ export async function captureSerial(path, baudRate = 115200, durationSeconds = 5) {
55
+ if (process.platform === "win32") {
56
+ return {
57
+ ok: false,
58
+ error: {
59
+ code: "serial_capture_not_implemented",
60
+ message: "Serial capture without an external serial library is not implemented on Windows yet.",
61
+ remediation: "Use serial list for detection; install the future native bridge helper for Windows capture."
62
+ }
63
+ };
64
+ }
65
+ try {
66
+ await stat(path);
67
+ }
68
+ catch {
69
+ return {
70
+ ok: false,
71
+ error: {
72
+ code: "serial_port_not_found",
73
+ message: `Serial port ${path} does not exist.`,
74
+ retryable: true
75
+ }
76
+ };
77
+ }
78
+ const sttyArgs = process.platform === "darwin"
79
+ ? ["-f", path, String(baudRate), "raw", "-echo"]
80
+ : ["-F", path, String(baudRate), "raw", "-echo"];
81
+ const stty = await runCommand("stty", sttyArgs, 3000);
82
+ if (stty.exit_code !== 0) {
83
+ return {
84
+ ok: false,
85
+ error: {
86
+ code: "serial_config_failed",
87
+ message: stty.stderr || `Failed to configure ${path}.`,
88
+ retryable: true
89
+ }
90
+ };
91
+ }
92
+ return new Promise((resolve) => {
93
+ const child = spawn("cat", [path], { stdio: ["ignore", "pipe", "pipe"] });
94
+ let stdout = "";
95
+ let stderr = "";
96
+ const timer = setTimeout(() => {
97
+ child.kill("SIGTERM");
98
+ setTimeout(() => child.kill("SIGKILL"), 1000).unref();
99
+ }, Math.max(1, Math.min(durationSeconds, 60)) * 1000);
100
+ child.stdout.on("data", (chunk) => {
101
+ stdout += chunk.toString("utf8");
102
+ });
103
+ child.stderr.on("data", (chunk) => {
104
+ stderr += chunk.toString("utf8");
105
+ });
106
+ child.on("error", (error) => {
107
+ clearTimeout(timer);
108
+ resolve({
109
+ ok: false,
110
+ error: {
111
+ code: "serial_capture_failed",
112
+ message: error.message,
113
+ retryable: true
114
+ }
115
+ });
116
+ });
117
+ child.on("close", (code) => {
118
+ clearTimeout(timer);
119
+ const cleanOutput = stripControl(stdout);
120
+ const cleanError = stripControl(stderr);
121
+ resolve({
122
+ ok: code === 0 || code === null,
123
+ path,
124
+ baud_rate: baudRate,
125
+ duration_seconds: durationSeconds,
126
+ stdout: cleanOutput,
127
+ stderr: cleanError,
128
+ output_tail: tailLines(`${cleanOutput}\n${cleanError}`)
129
+ });
130
+ });
131
+ });
132
+ }
133
+ //# sourceMappingURL=serial.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serial.js","sourceRoot":"","sources":["../../src/adapters/serial.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function findUf2Volumes(): Promise<string[]>;
@@ -0,0 +1,53 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ const RP2350_UF2_VOLUME_NAMES = ["RPI-RP2", "RP2350", "PICOBOOT"];
4
+ export async function findUf2Volumes() {
5
+ const candidates = [];
6
+ if (process.platform === "darwin") {
7
+ candidates.push("/Volumes");
8
+ }
9
+ else if (process.platform === "linux") {
10
+ candidates.push("/media", "/run/media", `/media/${process.env.USER ?? ""}`, `/run/media/${process.env.USER ?? ""}`);
11
+ }
12
+ else if (process.platform === "win32") {
13
+ for (let code = 65; code <= 90; code += 1) {
14
+ candidates.push(`${String.fromCharCode(code)}:\\`);
15
+ }
16
+ }
17
+ const found = [];
18
+ for (const root of candidates) {
19
+ try {
20
+ const rootStat = await stat(root);
21
+ if (!rootStat.isDirectory()) {
22
+ continue;
23
+ }
24
+ if (process.platform === "win32") {
25
+ if (await hasUf2Marker(root)) {
26
+ found.push(root);
27
+ }
28
+ continue;
29
+ }
30
+ const entries = await readdir(root);
31
+ for (const entry of entries) {
32
+ const path = join(root, entry);
33
+ if (RP2350_UF2_VOLUME_NAMES.some((name) => entry.toUpperCase().includes(name)) || await hasUf2Marker(path)) {
34
+ found.push(path);
35
+ }
36
+ }
37
+ }
38
+ catch {
39
+ // Ignore unavailable mount roots.
40
+ }
41
+ }
42
+ return [...new Set(found)];
43
+ }
44
+ async function hasUf2Marker(path) {
45
+ try {
46
+ await stat(join(path, "INFO_UF2.TXT"));
47
+ return true;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ //# sourceMappingURL=uf2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uf2.js","sourceRoot":"","sources":["../../src/adapters/uf2.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,uBAAuB,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;AAElE,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9B,CAAC;SAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACxC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,EAAE,UAAU,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,cAAc,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;IACtH,CAAC;SAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACxC,KAAK,IAAI,IAAI,GAAG,EAAE,EAAE,IAAI,IAAI,EAAE,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;YAC1C,UAAU,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;gBAC5B,SAAS;YACX,CAAC;YACD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;gBACjC,IAAI,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC7B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnB,CAAC;gBACD,SAAS;YACX,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;YACpC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBAC/B,IAAI,uBAAuB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC3G,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnB,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;QACpC,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAAY;IACtC,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ErrorEnvelope, UsbDeviceInfo } from "@embed-labs/protocol";
2
+ export declare function scanUsbDevices(warnings?: ErrorEnvelope[]): Promise<UsbDeviceInfo[]>;
3
+ export declare function isRockchipUsb(item: UsbDeviceInfo): boolean;
@@ -0,0 +1,102 @@
1
+ import { commandExists, runCommand } from "../process.js";
2
+ export async function scanUsbDevices(warnings = []) {
3
+ if (process.platform === "darwin") {
4
+ const result = await runCommand("system_profiler", ["SPUSBDataType", "-json"], 12_000);
5
+ if (result.exit_code !== 0) {
6
+ warnings.push({ code: "usb_scan_failed", message: result.stderr || "macOS USB scan failed" });
7
+ return [];
8
+ }
9
+ try {
10
+ const parsed = JSON.parse(result.stdout);
11
+ return flattenMacUsb(parsed);
12
+ }
13
+ catch {
14
+ warnings.push({ code: "usb_scan_parse_failed", message: "macOS USB scan returned invalid JSON" });
15
+ return [];
16
+ }
17
+ }
18
+ if (process.platform === "linux" && await commandExists("lsusb")) {
19
+ const result = await runCommand("lsusb", [], 5000);
20
+ if (result.exit_code !== 0) {
21
+ warnings.push({ code: "usb_scan_failed", message: result.stderr || "lsusb failed" });
22
+ return [];
23
+ }
24
+ return result.stdout.split(/\r?\n/).filter(Boolean).map((line) => {
25
+ const match = line.match(/ID\s+([0-9a-fA-F]{4}):([0-9a-fA-F]{4})\s+(.+)/);
26
+ return {
27
+ vendor_id: match?.[1],
28
+ product_id: match?.[2],
29
+ product_name: match?.[3] ?? line,
30
+ raw: { line }
31
+ };
32
+ });
33
+ }
34
+ if (process.platform === "win32") {
35
+ const result = await runCommand("powershell.exe", [
36
+ "-NoProfile",
37
+ "-Command",
38
+ "Get-PnpDevice -PresentOnly | Where-Object {$_.InstanceId -like 'USB*'} | Select-Object FriendlyName,InstanceId | ConvertTo-Json -Compress"
39
+ ], 8000);
40
+ if (result.exit_code !== 0) {
41
+ warnings.push({ code: "usb_scan_failed", message: result.stderr || "Windows USB scan failed" });
42
+ return [];
43
+ }
44
+ try {
45
+ const parsed = JSON.parse(result.stdout || "[]");
46
+ const rows = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];
47
+ return rows.map((row) => {
48
+ const item = row;
49
+ return {
50
+ product_name: String(item.FriendlyName ?? ""),
51
+ location_id: String(item.InstanceId ?? ""),
52
+ raw: item
53
+ };
54
+ }).filter((item) => item.product_name || item.location_id);
55
+ }
56
+ catch {
57
+ warnings.push({ code: "usb_scan_parse_failed", message: "Windows USB scan returned invalid JSON" });
58
+ return [];
59
+ }
60
+ }
61
+ warnings.push({ code: "usb_scan_unsupported", message: `USB scan is not implemented for ${process.platform}` });
62
+ return [];
63
+ }
64
+ export function isRockchipUsb(item) {
65
+ const text = `${item.vendor_id ?? ""} ${item.product_id ?? ""} ${item.product_name ?? ""}`.toLowerCase();
66
+ return text.includes("2207") || text.includes("rockchip") || text.includes("maskrom") || text.includes("loader");
67
+ }
68
+ function flattenMacUsb(parsed) {
69
+ const roots = Array.isArray(parsed.SPUSBDataType) ? parsed.SPUSBDataType : [];
70
+ const result = [];
71
+ const visit = (node) => {
72
+ const name = String(node._name ?? node.name ?? "");
73
+ if (name) {
74
+ result.push({
75
+ product_name: name,
76
+ vendor_id: normalizeHex(node.vendor_id),
77
+ product_id: normalizeHex(node.product_id),
78
+ serial_number: typeof node.serial_num === "string" ? node.serial_num : undefined,
79
+ location_id: typeof node.location_id === "string" ? node.location_id : undefined,
80
+ raw: node
81
+ });
82
+ }
83
+ const children = node._items;
84
+ if (Array.isArray(children)) {
85
+ for (const child of children) {
86
+ if (child && typeof child === "object") {
87
+ visit(child);
88
+ }
89
+ }
90
+ }
91
+ };
92
+ roots.forEach(visit);
93
+ return result;
94
+ }
95
+ function normalizeHex(value) {
96
+ if (typeof value !== "string") {
97
+ return undefined;
98
+ }
99
+ const match = value.match(/0x([0-9a-fA-F]+)/);
100
+ return match?.[1]?.toLowerCase() ?? value.toLowerCase();
101
+ }
102
+ //# sourceMappingURL=usb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usb.js","sourceRoot":"","sources":["../../src/adapters/usb.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { BoardDeployRequest, BoardDeployResult, DeviceProbeRequest, DeviceProbeResult, DeviceScanResult, ErrorEnvelope } from "@embed-labs/protocol";
2
+ export { buildFlashPlan, executeRp2350Uf2Flash } from "./adapters/flash.js";
3
+ export { scanDebugTools } from "./adapters/debug.js";
4
+ export { captureSerial, listSerialPorts } from "./adapters/serial.js";
5
+ export { runBoundedSsh } from "./adapters/network.js";
6
+ export { scanUsbDevices } from "./adapters/usb.js";
7
+ export declare function scanHardware(): Promise<DeviceScanResult>;
8
+ export declare function probeDevices(request: DeviceProbeRequest): Promise<DeviceProbeResult>;
9
+ export declare function deployTaishanPiApplication(request: BoardDeployRequest): Promise<BoardDeployResult | {
10
+ ok: false;
11
+ error: ErrorEnvelope;
12
+ }>;
13
+ export declare function validateTaishanPiDeployRequest(input: unknown): {
14
+ request: BoardDeployRequest;
15
+ } | {
16
+ error: string;
17
+ };